Skip to content

Commit b5578ea

Browse files
authored
Add tests for dereferencing references to the HandleManagerTest(s) (#4816)
* Add dereferencing tests to the android handle manager test. * Add dereferencing tests to core HandleManager.
1 parent a695797 commit b5578ea

File tree

7 files changed

+240
-141
lines changed

7 files changed

+240
-141
lines changed

java/arcs/core/storage/handle/RawEntityDereferencer.kt

+7-4
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ class RawEntityDereferencer(
4343
override suspend fun dereference(
4444
reference: Reference,
4545
coroutineContext: CoroutineContext
46-
): RawEntity? = withContext(coroutineContext) {
46+
): RawEntity? {
4747
log.debug { "De-referencing $reference" }
4848

4949
val storageKey = reference.storageKey.childKeyWithComponent(reference.id)
@@ -70,10 +70,13 @@ class RawEntityDereferencer(
7070
true
7171
}
7272
)
73-
launch { store.onProxyMessage(ProxyMessage.SyncRequest(token)) }
7473

75-
// Only return the item if we've actually managed to pull it out of the database.
76-
deferred.await().takeIf { it matches schema }?.copy(id = reference.id)
74+
return withContext(coroutineContext) {
75+
launch { store.onProxyMessage(ProxyMessage.SyncRequest(token)) }
76+
77+
// Only return the item if we've actually managed to pull it out of the database.
78+
deferred.await().takeIf { it matches schema }?.copy(id = reference.id)
79+
}
7780
}
7881
}
7982

java/arcs/sdk/android/storage/BUILD

+1
Original file line numberDiff line numberDiff line change
@@ -21,5 +21,6 @@ kt_android_library(
2121
"//third_party/java/androidx/annotation",
2222
"//third_party/java/androidx/lifecycle",
2323
"//third_party/kotlin/kotlinx_coroutines",
24+
"//third_party/kotlin/kotlinx_coroutines:kotlinx_coroutines_android",
2425
],
2526
)

java/arcs/sdk/android/storage/ServiceStore.kt

+3-7
Original file line numberDiff line numberDiff line change
@@ -103,9 +103,7 @@ class ServiceStore<Data : CrdtData, Op : CrdtOperation, ConsumerData>(
103103
@Suppress("UNCHECKED_CAST")
104104
override suspend fun getLocalData(): Data {
105105
val service = checkNotNull(storageService)
106-
val channel = ParcelableProxyMessageChannel(
107-
coroutineContext
108-
)
106+
val channel = ParcelableProxyMessageChannel(coroutineContext)
109107
service.getLocalData(channel)
110108
val flow = channel.asFlow()
111109
val modelUpdate =
@@ -140,12 +138,10 @@ class ServiceStore<Data : CrdtData, Op : CrdtOperation, ConsumerData>(
140138
"Connection to StorageService is already alive."
141139
}
142140
val connection = connectionFactory(options, crdtType)
141+
// Need to initiate the connection on the main thread.
143142
val service = connection.connectAsync().await()
144143

145-
val messageChannel =
146-
ParcelableProxyMessageChannel(
147-
coroutineContext
148-
)
144+
val messageChannel = ParcelableProxyMessageChannel(coroutineContext)
149145
serviceCallbackToken = withContext(coroutineContext) {
150146
service.registerCallback(messageChannel)
151147
}

javatests/arcs/android/storage/handle/AndroidHandleManagerTest.kt

+136-94
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ import arcs.core.data.Schema
1515
import arcs.core.data.SchemaFields
1616
import arcs.core.data.SchemaName
1717
import arcs.core.data.util.toReferencable
18+
import arcs.core.storage.Reference
1819
import arcs.core.storage.StorageKey
1920
import arcs.core.storage.driver.RamDisk
2021
import arcs.core.storage.driver.RamDiskStorageKey
@@ -36,30 +37,33 @@ import org.junit.runner.RunWith
3637
import org.mockito.Mockito.times
3738
import org.mockito.Mockito.verify
3839

39-
4040
@Suppress("EXPERIMENTAL_API_USAGE")
4141
@RunWith(AndroidJUnit4::class)
4242
class AndroidHandleManagerTest {
4343
private lateinit var app: Application
4444

45+
private val backingKey = RamDiskStorageKey("entities")
46+
4547
val entity1 = RawEntity(
4648
"entity1",
47-
singletons=mapOf(
49+
singletons = mapOf(
4850
"name" to "Jason".toReferencable(),
4951
"age" to 21.toReferencable(),
50-
"is_cool" to false.toReferencable()
52+
"is_cool" to false.toReferencable(),
53+
"best_friend" to Reference("entity2", backingKey, null)
5154
),
52-
collections=emptyMap()
55+
collections = emptyMap()
5356
)
5457

5558
val entity2 = RawEntity(
5659
"entity2",
57-
singletons=mapOf(
60+
singletons = mapOf(
5861
"name" to "Jason".toReferencable(),
5962
"age" to 22.toReferencable(),
60-
"is_cool" to true.toReferencable()
63+
"is_cool" to true.toReferencable(),
64+
"best_friend" to Reference("entity1", backingKey, null)
6165
),
62-
collections=emptyMap()
66+
collections = emptyMap()
6367
)
6468

6569
private val schema = Schema(
@@ -68,20 +72,21 @@ class AndroidHandleManagerTest {
6872
singletons = mapOf(
6973
"name" to FieldType.Text,
7074
"age" to FieldType.Number,
71-
"is_cool" to FieldType.Boolean
75+
"is_cool" to FieldType.Boolean,
76+
"best_friend" to FieldType.EntityRef("1234acf")
7277
),
7378
collections = emptyMap()
7479
),
7580
"1234acf"
7681
)
7782

7883
private val singletonKey = ReferenceModeStorageKey(
79-
backingKey = RamDiskStorageKey("single-back"),
84+
backingKey = backingKey,
8085
storageKey = RamDiskStorageKey("single-ent")
8186
)
8287

8388
private val setKey = ReferenceModeStorageKey(
84-
backingKey = RamDiskStorageKey("set-back"),
89+
backingKey = backingKey,
8590
storageKey = RamDiskStorageKey("set-ent")
8691
)
8792

@@ -121,107 +126,144 @@ class AndroidHandleManagerTest {
121126
}
122127

123128
@Test
124-
fun testCreateSingletonHandle() = runBlockingTest {
125-
handleManagerTest { hm ->
126-
val singletonHandle = hm.singletonHandle(singletonKey, schema)
127-
singletonHandle.store(entity1)
128-
129-
// Now read back from a different handle
130-
val readbackHandle = hm.singletonHandle(singletonKey, schema)
131-
val readBack = readbackHandle.fetch()
132-
assertThat(readBack).isEqualTo(entity1)
133-
}
129+
fun testCreateSingletonHandle() = handleManagerTest { hm ->
130+
val singletonHandle = hm.singletonHandle(singletonKey, schema)
131+
singletonHandle.store(entity1)
132+
133+
// Now read back from a different handle
134+
val readbackHandle = hm.singletonHandle(singletonKey, schema)
135+
val readBack = readbackHandle.fetch()
136+
assertThat(readBack).isEqualTo(entity1)
137+
}
138+
139+
@Test
140+
fun testDereferencingFromSingletonEntity() = handleManagerTest { hm ->
141+
val singleton1Handle = hm.singletonHandle(singletonKey, schema)
142+
val singleton1Handle2 = hm.singletonHandle(singletonKey, schema)
143+
singleton1Handle.store(entity1)
144+
145+
// Create a second handle for the second entity, so we can store it.
146+
val singleton2Handle = hm.singletonHandle(
147+
ReferenceModeStorageKey(backingKey, RamDiskStorageKey("entity2")),
148+
schema
149+
)
150+
val singleton2Handle2 = hm.singletonHandle(
151+
ReferenceModeStorageKey(backingKey, RamDiskStorageKey("entity2")),
152+
schema
153+
)
154+
singleton2Handle.store(entity2)
155+
156+
// Now read back entity1, and dereference its best_friend.
157+
val dereferencedEntity2 =
158+
(singleton1Handle2.fetch()!!.singletons["best_friend"] as Reference)
159+
.also {
160+
// Check that it's alive
161+
assertThat(it.isAlive(coroutineContext)).isTrue()
162+
}
163+
.dereference(coroutineContext)
164+
assertThat(dereferencedEntity2).isEqualTo(entity2)
165+
166+
// Do the same for entity2's best_friend
167+
val dereferencedEntity1 =
168+
(singleton2Handle2.fetch()!!.singletons["best_friend"] as Reference)
169+
.dereference(coroutineContext)
170+
assertThat(dereferencedEntity1).isEqualTo(entity1)
134171
}
135172

136173
@Test
137-
fun testCreateSetHandle() = runBlockingTest {
138-
handleManagerTest { hm ->
139-
val setHandle = hm.setHandle(setKey, schema)
140-
setHandle.store(entity1)
141-
setHandle.store(entity2)
142-
143-
// Now read back from a different handle
144-
val secondHandle = hm.setHandle(setKey, schema)
145-
val readBack = secondHandle.fetchAll()
146-
assertThat(readBack).containsExactly(entity1, entity2)
174+
fun testCreateSetHandle() = handleManagerTest { hm ->
175+
val setHandle = hm.setHandle(setKey, schema)
176+
setHandle.store(entity1)
177+
setHandle.store(entity2)
178+
179+
// Now read back from a different handle
180+
val secondHandle = hm.setHandle(setKey, schema)
181+
val readBack = secondHandle.fetchAll()
182+
assertThat(readBack).containsExactly(entity1, entity2)
183+
}
184+
185+
@Test
186+
fun testDereferencingFromSetHandleEntity() = handleManagerTest { hm ->
187+
val setHandle = hm.setHandle(setKey, schema)
188+
setHandle.store(entity1)
189+
setHandle.store(entity2)
190+
191+
val secondHandle = hm.setHandle(setKey, schema)
192+
secondHandle.fetchAll().also { assertThat(it).hasSize(2) }.forEach { entity ->
193+
val expectedBestFriend = if (entity.id == "entity1") entity2 else entity1
194+
val actualBestFriend = (entity.singletons["best_friend"] as Reference)
195+
.dereference()
196+
assertThat(actualBestFriend).isEqualTo(expectedBestFriend)
147197
}
148198
}
149199

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

152202
@Test
153-
fun testSetHandleOnUpdate() = runBlockingTest {
154-
handleManagerTest { hm ->
155-
val testCallback1 = mock<SetCallbacks<RawEntity>>()
156-
val testCallback2 = mock<SetCallbacks<RawEntity>>()
157-
val firstHandle = hm.setHandle(setKey, schema, testCallback1)
158-
val secondHandle = hm.setHandle(setKey, schema, testCallback2)
159-
160-
val expectedAdd = CrdtSet.Operation.Add(
161-
setKey.toKeyString(),
162-
testMapForKey(setKey),
163-
entity1
164-
)
165-
secondHandle.store(entity1)
166-
verify(testCallback1, times(1)).onUpdate(firstHandle, expectedAdd)
167-
verify(testCallback2, times(1)).onUpdate(secondHandle, expectedAdd)
168-
169-
firstHandle.remove(entity1)
170-
val expectedRemove = CrdtSet.Operation.Remove(
171-
setKey.toKeyString(),
172-
testMapForKey(setKey),
173-
entity1
174-
)
175-
verify(testCallback1, times(1)).onUpdate(firstHandle, expectedRemove)
176-
verify(testCallback2, times(1)).onUpdate(secondHandle, expectedRemove)
177-
}
203+
fun testSetHandleOnUpdate() = handleManagerTest { hm ->
204+
val testCallback1 = mock<SetCallbacks<RawEntity>>()
205+
val testCallback2 = mock<SetCallbacks<RawEntity>>()
206+
val firstHandle = hm.setHandle(setKey, schema, testCallback1)
207+
val secondHandle = hm.setHandle(setKey, schema, testCallback2)
208+
209+
val expectedAdd = CrdtSet.Operation.Add(
210+
setKey.toKeyString(),
211+
testMapForKey(setKey),
212+
entity1
213+
)
214+
secondHandle.store(entity1)
215+
verify(testCallback1, times(1)).onUpdate(firstHandle, expectedAdd)
216+
verify(testCallback2, times(1)).onUpdate(secondHandle, expectedAdd)
217+
218+
firstHandle.remove(entity1)
219+
val expectedRemove = CrdtSet.Operation.Remove(
220+
setKey.toKeyString(),
221+
testMapForKey(setKey),
222+
entity1
223+
)
224+
verify(testCallback1, times(1)).onUpdate(firstHandle, expectedRemove)
225+
verify(testCallback2, times(1)).onUpdate(secondHandle, expectedRemove)
178226
}
179227

180228
@Test
181-
fun testSingletonHandleOnUpdate() = runBlockingTest {
182-
handleManagerTest { hm ->
183-
val testCallback1 = mock<SingletonCallbacks<RawEntity>>()
184-
val testCallback2 = mock<SingletonCallbacks<RawEntity>>()
185-
val firstHandle = hm.singletonHandle(singletonKey, schema, testCallback1)
186-
val secondHandle = hm.singletonHandle(singletonKey, schema, testCallback2)
187-
secondHandle.store(entity1)
188-
val expectedAdd = CrdtSingleton.Operation.Update(
189-
singletonKey.toKeyString(),
190-
testMapForKey(singletonKey),
191-
entity1
192-
)
193-
verify(testCallback1, times(1)).onUpdate(firstHandle, expectedAdd)
194-
verify(testCallback2, times(1)).onUpdate(secondHandle, expectedAdd)
195-
firstHandle.clear()
196-
197-
val expectedRemove = CrdtSingleton.Operation.Clear<RawEntity>(
198-
singletonKey.toKeyString(),
199-
testMapForKey(singletonKey)
200-
)
201-
verify(testCallback1, times(1)).onUpdate(firstHandle, expectedRemove)
202-
verify(testCallback2, times(1)).onUpdate(secondHandle, expectedRemove)
203-
}
229+
fun testSingletonHandleOnUpdate() = handleManagerTest { hm ->
230+
val testCallback1 = mock<SingletonCallbacks<RawEntity>>()
231+
val testCallback2 = mock<SingletonCallbacks<RawEntity>>()
232+
val firstHandle = hm.singletonHandle(singletonKey, schema, testCallback1)
233+
val secondHandle = hm.singletonHandle(singletonKey, schema, testCallback2)
234+
secondHandle.store(entity1)
235+
val expectedAdd = CrdtSingleton.Operation.Update(
236+
singletonKey.toKeyString(),
237+
testMapForKey(singletonKey),
238+
entity1
239+
)
240+
verify(testCallback1, times(1)).onUpdate(firstHandle, expectedAdd)
241+
verify(testCallback2, times(1)).onUpdate(secondHandle, expectedAdd)
242+
firstHandle.clear()
243+
244+
val expectedRemove = CrdtSingleton.Operation.Clear<RawEntity>(
245+
singletonKey.toKeyString(),
246+
testMapForKey(singletonKey)
247+
)
248+
verify(testCallback1, times(1)).onUpdate(firstHandle, expectedRemove)
249+
verify(testCallback2, times(1)).onUpdate(secondHandle, expectedRemove)
204250
}
205251

206252
@Test
207-
fun testSetSyncOnRegister() = runBlockingTest {
208-
handleManagerTest { hm ->
209-
val testCallback = mock<SetCallbacks<RawEntity>>()
210-
val firstHandle = hm.setHandle(setKey, schema, testCallback)
211-
verify(testCallback, times(1)).onSync(firstHandle)
212-
firstHandle.fetchAll()
213-
verify(testCallback, times(1)).onSync(firstHandle)
214-
}
253+
fun testSetSyncOnRegister() = handleManagerTest { hm ->
254+
val testCallback = mock<SetCallbacks<RawEntity>>()
255+
val firstHandle = hm.setHandle(setKey, schema, testCallback)
256+
verify(testCallback, times(1)).onSync(firstHandle)
257+
firstHandle.fetchAll()
258+
verify(testCallback, times(1)).onSync(firstHandle)
215259
}
216260

217261
@Test
218-
fun testSingletonSyncOnRegister() = runBlockingTest {
219-
handleManagerTest { hm ->
220-
val testCallback = mock<SingletonCallbacks<RawEntity>>()
221-
val firstHandle = hm.singletonHandle(setKey, schema, testCallback)
222-
verify(testCallback, times(1)).onSync(firstHandle)
223-
firstHandle.fetch()
224-
verify(testCallback, times(1)).onSync(firstHandle)
225-
}
262+
fun testSingletonSyncOnRegister() = handleManagerTest { hm ->
263+
val testCallback = mock<SingletonCallbacks<RawEntity>>()
264+
val firstHandle = hm.singletonHandle(setKey, schema, testCallback)
265+
verify(testCallback, times(1)).onSync(firstHandle)
266+
firstHandle.fetch()
267+
verify(testCallback, times(1)).onSync(firstHandle)
226268
}
227269
}

javatests/arcs/android/storage/handle/BUILD

+1
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ kt_android_library(
2020

2121
arcs_kt_android_test_suite(
2222
name = "handle",
23+
size = "medium",
2324
srcs = glob(["*Test.kt"]),
2425
manifest = "AndroidManifest.xml",
2526
package = "arcs.android.storage.handle",

0 commit comments

Comments
 (0)