Skip to content

Commit

Permalink
Add tests for nesting.
Browse files Browse the repository at this point in the history
Signed-off-by: Kenneth J. Shackleton <[email protected]>
  • Loading branch information
kennethshackleton committed Jul 18, 2021
1 parent 02866d2 commit 10d6ae2
Show file tree
Hide file tree
Showing 4 changed files with 77 additions and 12 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@

package com.bloomberg.selekt.android

import android.content.ContentValues
import com.bloomberg.selekt.Experimental
import com.bloomberg.selekt.SQLiteJournalMode
import com.bloomberg.selekt.SQLiteTransactionMode
Expand All @@ -27,14 +28,26 @@ import kotlinx.coroutines.cancel
import kotlinx.coroutines.test.TestCoroutineDispatcher
import kotlinx.coroutines.test.TestCoroutineScope
import kotlinx.coroutines.test.runBlockingTest
import kotlinx.coroutines.withContext
import org.junit.After
import org.junit.Rule
import org.junit.Test
import org.junit.rules.DisableOnDebug
import org.junit.rules.Timeout
import org.junit.runner.RunWith
import org.robolectric.RobolectricTestRunner
import java.io.File
import java.util.concurrent.TimeUnit
import kotlin.test.assertEquals
import kotlin.test.assertFalse
import kotlin.test.assertTrue

@OptIn(Experimental::class, ExperimentalCoroutinesApi::class)
@RunWith(RobolectricTestRunner::class)
internal class SQLiteDatabaseWithTransactionTest {
@get:Rule
val timeoutRule = DisableOnDebug(Timeout(10L, TimeUnit.SECONDS))

private val dispatcher = TestCoroutineDispatcher()
private val scope = TestCoroutineScope(dispatcher)

Expand All @@ -54,6 +67,13 @@ internal class SQLiteDatabaseWithTransactionTest {
}
}

@Test
fun withTransactionDefault(): Unit = scope.runBlockingTest {
database.withTransaction(dispatcher = dispatcher) {
assertTrue(isTransactionOpenedByCurrentThread)
}
}

@Test
fun withTransactionExclusively(): Unit = scope.runBlockingTest {
database.withTransaction(SQLiteTransactionMode.EXCLUSIVE, dispatcher) {
Expand Down Expand Up @@ -87,4 +107,55 @@ internal class SQLiteDatabaseWithTransactionTest {
delayTransaction(50L)
}
}

@Test
fun nestedTransaction(): Unit = scope.runBlockingTest {
database.withTransaction(dispatcher = dispatcher) {
withTransaction(dispatcher = dispatcher) { }
}
}

@Test
fun delayNestedTransaction(): Unit = scope.runBlockingTest {
database.withTransaction(dispatcher = dispatcher) {
withTransaction(dispatcher = dispatcher) {
delayTransaction()
}
assertTrue(isTransactionOpenedByCurrentThread)
}
}

@Test
fun withTransactionInsert(): Unit = scope.runBlockingTest {
withContext(dispatcher) {
database.exec("CREATE TABLE Foo (bar TEXT)")
withContext(dispatcher) {
database.withTransaction(dispatcher = dispatcher) {
insert("Foo", ContentValues().apply { put("bar", "xyz") }, ConflictAlgorithm.FAIL)
}
}
database.query("SELECT * FROM foo", emptyArray()).use {
assertTrue(it.moveToNext())
assertEquals("xyz", it.getString(0))
}
}
}

@Test
fun nestedWithTransactionInsert(): Unit = scope.runBlockingTest {
withContext(dispatcher) {
database.exec("CREATE TABLE Foo (bar TEXT)")
withContext(dispatcher) {
database.withTransaction(dispatcher = dispatcher) {
withTransaction(dispatcher = dispatcher) {
insert("Foo", ContentValues().apply { put("bar", "xyz") }, ConflictAlgorithm.FAIL)
}
}
}
database.query("SELECT * FROM foo", emptyArray()).use {
assertTrue(it.moveToNext())
assertEquals("xyz", it.getString(0))
}
}
}
}
7 changes: 5 additions & 2 deletions Lib/src/main/kotlin/com/bloomberg/selekt/Databases.kt
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@ import java.io.InputStream
import java.io.OutputStream
import java.lang.StringBuilder
import javax.annotation.concurrent.ThreadSafe
import kotlin.coroutines.EmptyCoroutineContext
import kotlin.coroutines.coroutineContext
import kotlin.jvm.Throws

private val EMPTY_ARRAY = emptyArray<Any?>()
Expand Down Expand Up @@ -355,8 +357,9 @@ class SQLDatabase constructor(
transactionMode: SQLiteTransactionMode = SQLiteTransactionMode.EXCLUSIVE,
dispatcher: CoroutineDispatcher = Dispatchers.IO,
block: suspend D.() -> T
): T = withPledge {
withContext(dispatcher + session.threadLocalContextElement()) {
): T = pledge {
val threadLocalContext = coroutineContext[ThreadLocalSession] ?: session.threadLocalContextElement()
withContext(dispatcher + threadLocalContext) {
when (transactionMode) {
SQLiteTransactionMode.EXCLUSIVE -> session.get().beginExclusiveTransaction()
SQLiteTransactionMode.IMMEDIATE -> session.get().beginImmediateTransaction()
Expand Down
2 changes: 1 addition & 1 deletion Lib/src/main/kotlin/com/bloomberg/selekt/Sessions.kt
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ internal class ThreadLocalSession(
override fun initialValue() = SQLSession(pool)
}

internal fun threadLocalContextElement() = threadLocal.asContextElement(SQLSession(pool))
internal fun threadLocalContextElement() = threadLocal.asContextElement()

internal fun get(): SQLSession = threadLocal.get()

Expand Down
9 changes: 0 additions & 9 deletions Lib/src/main/kotlin/com/bloomberg/selekt/SharedCloseable.kt
Original file line number Diff line number Diff line change
Expand Up @@ -40,15 +40,6 @@ abstract class SharedCloseable : Closeable {
}
}

internal suspend fun <T> withPledge(block: suspend () -> T): T {
retain()
try {
return block()
} finally {
release()
}
}

internal inline fun <T> pledge(block: () -> T): T {
retain()
try {
Expand Down

0 comments on commit 10d6ae2

Please sign in to comment.