Skip to content

Commit

Permalink
Simplify service test pattern
Browse files Browse the repository at this point in the history
* Make a simple test lifecycle registry, instead of creating empty
testactivity

* Remove use of `runBlockingTest`: according to
Kotlin/kotlinx.coroutines#1222, if the test
results in coroutines being finished on other threads, it's possible to
receive "This job has not yet completed" exceptions, even though the
jobs were properly terminated. Since we don't need the delay-skipping
properties of `runBlockingTest`, I think it's OK to use `runBlocking`.
  • Loading branch information
jblebrun committed Mar 3, 2020
1 parent b5578ea commit 56b5dc0
Show file tree
Hide file tree
Showing 5 changed files with 48 additions and 97 deletions.
12 changes: 11 additions & 1 deletion javatests/arcs/android/host/BUILD
Expand Up @@ -9,6 +9,16 @@ licenses(["notice"])

package(default_visibility = ["//visibility:public"])

kt_android_library(
name = "test_app",
testonly = 1,
srcs = ["TestActivity.kt"],
manifest = ":AndroidManifest.xml",
deps = [
"//third_party/java/androidx/appcompat",
],
)

arcs_kt_android_test_suite(
name = "host",
srcs = glob(["*Test.kt"]),
Expand All @@ -17,6 +27,7 @@ arcs_kt_android_test_suite(
deps = [
":schemas",
":services",
":test_app",
"//java/arcs/android/crdt",
"//java/arcs/android/host",
"//java/arcs/android/sdk/host",
Expand All @@ -42,7 +53,6 @@ arcs_kt_android_test_suite(
"//java/arcs/sdk/android/storage",
"//java/arcs/sdk/android/storage/service",
"//java/arcs/sdk/android/storage/service/testutil",
"//javatests/arcs/android/storage/handle:test_app",
"//javatests/arcs/core/allocator:allocator-test-util",
"//third_party/android/androidx_test/core",
"//third_party/android/androidx_test/ext/junit",
Expand Down
108 changes: 37 additions & 71 deletions javatests/arcs/android/storage/handle/AndroidHandleManagerTest.kt
Expand Up @@ -2,6 +2,8 @@ package arcs.android.storage.handle

import android.app.Application
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.LifecycleOwner
import androidx.lifecycle.LifecycleRegistry
import androidx.test.core.app.ActivityScenario
import androidx.test.core.app.ApplicationProvider
import androidx.test.ext.junit.runners.AndroidJUnit4
Expand All @@ -27,10 +29,7 @@ import arcs.sdk.android.storage.service.DefaultConnectionFactory
import arcs.sdk.android.storage.service.testutil.TestBindingDelegate
import com.google.common.truth.Truth.assertThat
import com.nhaarman.mockitokotlin2.mock
import kotlinx.coroutines.launch
import kotlinx.coroutines.runBlocking
import kotlinx.coroutines.test.TestCoroutineScope
import kotlinx.coroutines.test.runBlockingTest
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
Expand All @@ -39,11 +38,16 @@ import org.mockito.Mockito.verify

@Suppress("EXPERIMENTAL_API_USAGE")
@RunWith(AndroidJUnit4::class)
class AndroidHandleManagerTest {
class AndroidHandleManagerTest : LifecycleOwner {
private lateinit var lifecycle: LifecycleRegistry
override fun getLifecycle() = lifecycle

private lateinit var app: Application

private val backingKey = RamDiskStorageKey("entities")

private lateinit var handleManager: HandleManager

val entity1 = RawEntity(
"entity1",
singletons = mapOf(
Expand Down Expand Up @@ -93,61 +97,35 @@ class AndroidHandleManagerTest {
@Before
fun setUp() {
RamDisk.clear()
lifecycle = LifecycleRegistry(this).apply {
setCurrentState(Lifecycle.State.CREATED)
setCurrentState(Lifecycle.State.STARTED)
setCurrentState(Lifecycle.State.RESUMED)
}
app = ApplicationProvider.getApplicationContext()
app.setTheme(R.style.Theme_AppCompat);

// Initialize WorkManager for instrumentation tests.
WorkManagerTestInitHelper.initializeTestWorkManager(app)
}

fun handleManagerTest(
block: suspend TestCoroutineScope.(HandleManager) -> Unit
) = runBlockingTest {
val scenario = ActivityScenario.launch(TestActivity::class.java)

scenario.moveToState(Lifecycle.State.STARTED)

val activityJob = launch {
scenario.onActivity { activity ->
val hf = AndroidHandleManager(
lifecycle = activity.lifecycle,
context = activity,
connectionFactory = DefaultConnectionFactory(activity, TestBindingDelegate(app)),
coroutineContext = coroutineContext
)
runBlocking {
this@runBlockingTest.block(hf)
}
scenario.close()
}
}

activityJob.join()
}

@Test
fun testCreateSingletonHandle() = handleManagerTest { hm ->
val singletonHandle = hm.singletonHandle(singletonKey, schema)
singletonHandle.store(entity1)

// Now read back from a different handle
val readbackHandle = hm.singletonHandle(singletonKey, schema)
val readBack = readbackHandle.fetch()
assertThat(readBack).isEqualTo(entity1)
handleManager = AndroidHandleManager(
lifecycle = lifecycle,
context = app,
connectionFactory = DefaultConnectionFactory(app, TestBindingDelegate(app))
)
}

@Test
fun testDereferencingFromSingletonEntity() = handleManagerTest { hm ->
val singleton1Handle = hm.singletonHandle(singletonKey, schema)
val singleton1Handle2 = hm.singletonHandle(singletonKey, schema)
fun singleton_dereferenceEntity() = runBlocking {
val singleton1Handle = handleManager.singletonHandle(singletonKey, schema)
val singleton1Handle2 = handleManager.singletonHandle(singletonKey, schema)
singleton1Handle.store(entity1)

// Create a second handle for the second entity, so we can store it.
val singleton2Handle = hm.singletonHandle(
val singleton2Handle = handleManager.singletonHandle(
ReferenceModeStorageKey(backingKey, RamDiskStorageKey("entity2")),
schema
)
val singleton2Handle2 = hm.singletonHandle(
val singleton2Handle2 = handleManager.singletonHandle(
ReferenceModeStorageKey(backingKey, RamDiskStorageKey("entity2")),
schema
)
Expand All @@ -171,40 +149,28 @@ class AndroidHandleManagerTest {
}

@Test
fun testCreateSetHandle() = handleManagerTest { hm ->
val setHandle = hm.setHandle(setKey, schema)
setHandle.store(entity1)
setHandle.store(entity2)

// Now read back from a different handle
val secondHandle = hm.setHandle(setKey, schema)
val readBack = secondHandle.fetchAll()
assertThat(readBack).containsExactly(entity1, entity2)
}

@Test
fun testDereferencingFromSetHandleEntity() = handleManagerTest { hm ->
val setHandle = hm.setHandle(setKey, schema)
fun set_dereferenceEntity () = runBlocking {
val setHandle = handleManager.setHandle(setKey, schema)
setHandle.store(entity1)
setHandle.store(entity2)

val secondHandle = hm.setHandle(setKey, schema)
val secondHandle = handleManager.setHandle(setKey, schema)
secondHandle.fetchAll().also { assertThat(it).hasSize(2) }.forEach { entity ->
val expectedBestFriend = if (entity.id == "entity1") entity2 else entity1
val actualBestFriend = (entity.singletons["best_friend"] as Reference)
.dereference()
.dereference(coroutineContext)
assertThat(actualBestFriend).isEqualTo(expectedBestFriend)
}
}

private fun testMapForKey(key: StorageKey) = VersionMap(key.toKeyString() to 1)

@Test
fun testSetHandleOnUpdate() = handleManagerTest { hm ->
fun set_onHandleUpdate() = runBlocking<Unit> {
val testCallback1 = mock<SetCallbacks<RawEntity>>()
val testCallback2 = mock<SetCallbacks<RawEntity>>()
val firstHandle = hm.setHandle(setKey, schema, testCallback1)
val secondHandle = hm.setHandle(setKey, schema, testCallback2)
val firstHandle = handleManager.setHandle(setKey, schema, testCallback1)
val secondHandle = handleManager.setHandle(setKey, schema, testCallback2)

val expectedAdd = CrdtSet.Operation.Add(
setKey.toKeyString(),
Expand All @@ -226,11 +192,11 @@ class AndroidHandleManagerTest {
}

@Test
fun testSingletonHandleOnUpdate() = handleManagerTest { hm ->
fun singleton_OnHandleUpdate() = runBlocking<Unit> {
val testCallback1 = mock<SingletonCallbacks<RawEntity>>()
val testCallback2 = mock<SingletonCallbacks<RawEntity>>()
val firstHandle = hm.singletonHandle(singletonKey, schema, testCallback1)
val secondHandle = hm.singletonHandle(singletonKey, schema, testCallback2)
val firstHandle = handleManager.singletonHandle(singletonKey, schema, testCallback1)
val secondHandle = handleManager.singletonHandle(singletonKey, schema, testCallback2)
secondHandle.store(entity1)
val expectedAdd = CrdtSingleton.Operation.Update(
singletonKey.toKeyString(),
Expand All @@ -250,18 +216,18 @@ class AndroidHandleManagerTest {
}

@Test
fun testSetSyncOnRegister() = handleManagerTest { hm ->
fun set_syncOnRegister() = runBlocking<Unit> {
val testCallback = mock<SetCallbacks<RawEntity>>()
val firstHandle = hm.setHandle(setKey, schema, testCallback)
val firstHandle = handleManager.setHandle(setKey, schema, testCallback)
verify(testCallback, times(1)).onSync(firstHandle)
firstHandle.fetchAll()
verify(testCallback, times(1)).onSync(firstHandle)
}

@Test
fun testSingletonSyncOnRegister() = handleManagerTest { hm ->
fun singleton_syncOnRegister() = runBlocking<Unit> {
val testCallback = mock<SingletonCallbacks<RawEntity>>()
val firstHandle = hm.singletonHandle(setKey, schema, testCallback)
val firstHandle = handleManager.singletonHandle(setKey, schema, testCallback)
verify(testCallback, times(1)).onSync(firstHandle)
firstHandle.fetch()
verify(testCallback, times(1)).onSync(firstHandle)
Expand Down
6 changes: 0 additions & 6 deletions javatests/arcs/android/storage/handle/AndroidManifest.xml
Expand Up @@ -16,12 +16,6 @@

<application>
<service android:name="arcs.sdk.android.service.StorageService" android:exported="false"/>
<activity android:name="arcs.android.storage.handle.TestActivity" >
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
</activity>
</application>

</manifest>
14 changes: 0 additions & 14 deletions javatests/arcs/android/storage/handle/BUILD
Expand Up @@ -2,30 +2,18 @@ load(
"//third_party/java/arcs/build_defs:build_defs.bzl",
"arcs_kt_android_test_suite",
)
load("//tools/build_defs/kotlin:rules.bzl", "kt_android_library")

licenses(["notice"])

package(default_visibility = ["//visibility:public"])

kt_android_library(
name = "test_app",
testonly = 1,
srcs = ["TestActivity.kt"],
manifest = ":AndroidManifest.xml",
deps = [
"//third_party/java/androidx/appcompat",
],
)

arcs_kt_android_test_suite(
name = "handle",
size = "medium",
srcs = glob(["*Test.kt"]),
manifest = "AndroidManifest.xml",
package = "arcs.android.storage.handle",
deps = [
":test_app",
"//java/arcs/android/crdt",
"//java/arcs/android/storage",
"//java/arcs/android/storage/handle",
Expand All @@ -45,14 +33,12 @@ arcs_kt_android_test_suite(
"//java/arcs/sdk/android/storage/service/testutil",
"//third_party/android/androidx_test/core",
"//third_party/android/androidx_test/ext/junit",
"//third_party/java/androidx/appcompat",
"//third_party/java/androidx/work:testing",
"//third_party/java/junit:junit-android",
"//third_party/java/mockito:mockito-android",
"//third_party/java/robolectric",
"//third_party/java/truth:truth-android",
"//third_party/kotlin/kotlinx_coroutines",
"//third_party/kotlin/kotlinx_coroutines:kotlinx_coroutines_test",
"//third_party/kotlin/mockito_kotlin:mockito_kotlin-android",
],
)
5 changes: 0 additions & 5 deletions javatests/arcs/android/storage/handle/TestActivity.kt

This file was deleted.

0 comments on commit 56b5dc0

Please sign in to comment.