Skip to content

Commit a695797

Browse files
authored
Create RawEntityDereferencerTest, storage Reference-> CrdtEntity.Reference (PolymerLabs#4812)
* Create RawEntityDereferencerTest, make storage Reference implement CrdtEntity.Reference. Also: Create ParcelableReference. * Add dep. Also, apparently read/writeBoolean is Q-only. * Just write null if there is no version map. * Just write null if there is no version map.
1 parent ba7a107 commit a695797

File tree

12 files changed

+459
-19
lines changed

12 files changed

+459
-19
lines changed

java/arcs/android/crdt/BUILD

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,8 @@ kt_android_library(
2121
"//java/arcs/core/crdt",
2222
"//java/arcs/core/data:rawentity",
2323
"//java/arcs/core/data/util:data-util",
24+
"//java/arcs/core/storage:reference",
25+
"//java/arcs/core/storage:storage_key",
2426
"//third_party/java/jsr305_annotations",
2527
],
2628
)

java/arcs/android/crdt/ParcelableReferencable.kt

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,11 +13,12 @@ package arcs.android.crdt
1313

1414
import android.os.Parcel
1515
import android.os.Parcelable
16+
import arcs.android.crdt.ParcelableReferencable.Type
1617
import arcs.core.common.Referencable
1718
import arcs.core.crdt.CrdtEntity
1819
import arcs.core.data.RawEntity
1920
import arcs.core.data.util.ReferencablePrimitive
20-
import java.lang.IllegalArgumentException
21+
import arcs.core.storage.Reference
2122
import javax.annotation.OverridingMethodsMustInvokeSuper
2223

2324
/**
@@ -34,6 +35,7 @@ interface ParcelableReferencable : Parcelable {
3435
// TODO: Add other ParcelableReferencable subclasses.
3536
RawEntity(ParcelableRawEntity.CREATOR),
3637
CrdtEntityReferenceImpl(ParcelableCrdtEntity.ReferenceImpl),
38+
StorageReferenceImpl(ParcelableReference.CREATOR),
3739
Primitive(ParcelableReferencablePrimitive.CREATOR);
3840

3941
override fun writeToParcel(parcel: Parcel, flags: Int) {
@@ -56,6 +58,7 @@ interface ParcelableReferencable : Parcelable {
5658
// TODO: Add other ParcelableReferencable subclasses.
5759
is ParcelableRawEntity -> Type.RawEntity
5860
is ParcelableCrdtEntity.ReferenceImpl -> Type.CrdtEntityReferenceImpl
61+
is ParcelableReference -> Type.StorageReferenceImpl
5962
is ParcelableReferencablePrimitive -> Type.Primitive
6063
else -> throw IllegalArgumentException(
6164
"Unsupported Referencable type: ${this.javaClass}"
@@ -71,6 +74,7 @@ interface ParcelableReferencable : Parcelable {
7174
operator fun invoke(actual: Referencable): ParcelableReferencable = when (actual) {
7275
// TODO: Add other ParcelableReferencable subclasses.
7376
is RawEntity -> ParcelableRawEntity(actual)
77+
is Reference -> ParcelableReference(actual)
7478
is CrdtEntity.ReferenceImpl -> ParcelableCrdtEntity.ReferenceImpl(actual)
7579
is ReferencablePrimitive<*> -> ParcelableReferencablePrimitive(actual)
7680
else ->
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
/*
2+
* Copyright 2020 Google LLC.
3+
*
4+
* This code may only be used under the BSD style license found at
5+
* http://polymer.github.io/LICENSE.txt
6+
*
7+
* Code distributed by Google as part of this project is also subject to an additional IP rights
8+
* grant found at
9+
* http://polymer.github.io/PATENTS.txt
10+
*/
11+
12+
package arcs.android.crdt
13+
14+
import android.os.Parcel
15+
import android.os.Parcelable
16+
import arcs.android.util.writeProto
17+
import arcs.core.storage.Reference
18+
import arcs.core.storage.StorageKeyParser
19+
20+
/** Parcelable version of [Reference]. */
21+
data class ParcelableReference(override val actual: Reference) : ParcelableReferencable {
22+
override fun writeToParcel(parcel: Parcel, flags: Int) {
23+
super.writeToParcel(parcel, flags)
24+
parcel.writeString(actual.id)
25+
parcel.writeString(actual.storageKey.toString())
26+
actual.version?.let {
27+
parcel.writeProto(it.toProto())
28+
} ?: {
29+
parcel.writeTypedObject(null, flags)
30+
}()
31+
}
32+
33+
override fun describeContents(): Int = 0
34+
35+
/* Don't use this directly, instead use ParcelableReferencable. */
36+
internal companion object CREATOR : Parcelable.Creator<ParcelableReference> {
37+
override fun createFromParcel(parcel: Parcel): ParcelableReference {
38+
val id = requireNotNull(parcel.readString()) {
39+
"Required id not found in parcel for ParcelableReference"
40+
}
41+
val storageKeyString = requireNotNull(parcel.readString()) {
42+
"Required storageKey not found in parcel for ParcelableReference"
43+
}
44+
val versionMap = parcel.readVersionMap()?.takeIf { it.isNotEmpty() }
45+
46+
return ParcelableReference(
47+
Reference(id, StorageKeyParser.parse(storageKeyString), versionMap)
48+
)
49+
}
50+
51+
override fun newArray(size: Int): Array<ParcelableReference?> = arrayOfNulls(size)
52+
}
53+
}
54+
55+
/** Writes the [Reference] to the receiving [Parcel]. */
56+
fun Parcel.writeReference(reference: Reference, flags: Int) =
57+
writeTypedObject(ParcelableReference(reference), flags)

java/arcs/core/crdt/extension/ConversionExtensions.kt

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,10 @@ import arcs.core.data.util.ReferencablePrimitive
1919
import arcs.core.util.Base64
2020

2121
/** Converts the [RawEntity] into a [CrdtEntity.Data] model, at the given version. */
22-
fun RawEntity.toCrdtEntityData(versionMap: VersionMap): CrdtEntity.Data =
23-
CrdtEntity.Data(versionMap.copy(), this) { CrdtEntity.ReferenceImpl(it.id) }
22+
fun RawEntity.toCrdtEntityData(
23+
versionMap: VersionMap,
24+
referenceBuilder: (Referencable) -> CrdtEntity.Reference = { CrdtEntity.ReferenceImpl(it.id) }
25+
): CrdtEntity.Data = CrdtEntity.Data(versionMap.copy(), this, referenceBuilder)
2426

2527
private fun Any?.toReferencable(): Referencable {
2628
requireNotNull(this) { "Cannot create a referencable from a null value." }

java/arcs/core/data/Reference.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ import kotlinx.coroutines.Dispatchers
2727
*
2828
* Developers can check the liveness of a [Reference] using either [isAlive] or [isDead].
2929
*/
30-
interface Reference<T : Referencable> {
30+
interface Reference<T : Referencable> : arcs.core.crdt.CrdtEntity.Reference {
3131
/**
3232
* Fetches the actual [Entity] value being referenced from storage.
3333
*

java/arcs/core/storage/DirectStore.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -92,7 +92,7 @@ class DirectStore<Data : CrdtData, Op : CrdtOperation, T> /* internal */ constru
9292
return when (message) {
9393
is ProxyMessage.SyncRequest -> {
9494
callbacks.value[message.id]?.invoke(
95-
ProxyMessage.ModelUpdate(localModel.data, message.id)
95+
ProxyMessage.ModelUpdate(getLocalData(), message.id)
9696
)
9797
true
9898
}

java/arcs/core/storage/ReferenceModeStore.kt

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -566,7 +566,10 @@ class ReferenceModeStore private constructor(
566566
entity,
567567
VersionMap(crdtKey to maxVersion),
568568
fieldVersionProvider
569-
) { CrdtEntity.ReferenceImpl(it.id) }
569+
) {
570+
if (it is Reference) it
571+
else CrdtEntity.Reference.buildReference(it)
572+
}
570573
}
571574

572575
companion object {

java/arcs/core/storage/driver/Database.kt

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -271,7 +271,11 @@ class DatabaseDriver<Data : Any>(
271271
)?.also {
272272
dataAndVersion = when (it) {
273273
is DatabaseData.Entity ->
274-
it.rawEntity.toCrdtEntityData(it.versionMap)
274+
it.rawEntity.toCrdtEntityData(it.versionMap) { refable ->
275+
// Use the storage reference if it is one.
276+
if (refable is Reference) refable
277+
else CrdtEntity.Reference.buildReference(refable)
278+
}
275279
is DatabaseData.Singleton ->
276280
it.reference.toCrdtSingletonData(it.versionMap)
277281
is DatabaseData.Collection ->
@@ -361,7 +365,10 @@ class DatabaseDriver<Data : Any>(
361365
val actualData = when (data) {
362366
is DatabaseData.Singleton -> data.reference.toCrdtSingletonData(data.versionMap)
363367
is DatabaseData.Collection -> data.values.toCrdtSetData(data.versionMap)
364-
is DatabaseData.Entity -> data.rawEntity.toCrdtEntityData(data.versionMap)
368+
is DatabaseData.Entity -> data.rawEntity.toCrdtEntityData(data.versionMap) {
369+
if (it is Reference) it
370+
else CrdtEntity.Reference.buildReference(it)
371+
}
365372
} as Data
366373

367374
// Stash it locally.

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

Lines changed: 11 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -73,17 +73,18 @@ class RawEntityDereferencer(
7373
launch { store.onProxyMessage(ProxyMessage.SyncRequest(token)) }
7474

7575
// Only return the item if we've actually managed to pull it out of the database.
76-
deferred.await().takeIf { it matches schema }
76+
deferred.await().takeIf { it matches schema }?.copy(id = reference.id)
7777
}
78+
}
7879

79-
private infix fun RawEntity.matches(schema: Schema): Boolean {
80-
// Only allow empty to match if the Schema is also empty.
81-
// TODO: Is this a correct assumption?
82-
if (singletons.isEmpty() && collections.isEmpty())
83-
return schema.fields.singletons.isEmpty() && schema.fields.collections.isEmpty()
80+
/* internal */
81+
infix fun RawEntity.matches(schema: Schema): Boolean {
82+
// Only allow empty to match if the Schema is also empty.
83+
// TODO: Is this a correct assumption?
84+
if (singletons.isEmpty() && collections.isEmpty())
85+
return schema.fields.singletons.isEmpty() && schema.fields.collections.isEmpty()
8486

85-
// Return true if any of the RawEntity's fields are part of the Schema.
86-
return (singletons.isEmpty() || singletons.keys.any { it in schema.fields.singletons }) &&
87-
(collections.isEmpty() || collections.keys.any { it in schema.fields.collections })
88-
}
87+
// Return true if any of the RawEntity's fields are part of the Schema.
88+
return (singletons.isEmpty() || singletons.keys.any { it in schema.fields.singletons }) &&
89+
(collections.isEmpty() || collections.keys.any { it in schema.fields.collections })
8990
}
Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
1+
/*
2+
* Copyright 2020 Google LLC.
3+
*
4+
* This code may only be used under the BSD style license found at
5+
* http://polymer.github.io/LICENSE.txt
6+
*
7+
* Code distributed by Google as part of this project is also subject to an additional IP rights
8+
* grant found at
9+
* http://polymer.github.io/PATENTS.txt
10+
*/
11+
12+
package arcs.android.storage
13+
14+
import android.os.Parcel
15+
import androidx.test.ext.junit.runners.AndroidJUnit4
16+
import arcs.android.crdt.ParcelableRawEntity
17+
import arcs.android.crdt.readReferencable
18+
import arcs.android.crdt.writeReference
19+
import arcs.core.crdt.VersionMap
20+
import arcs.core.data.RawEntity
21+
import arcs.core.storage.Reference
22+
import arcs.core.storage.driver.RamDiskStorageKey
23+
import com.google.common.truth.Truth.assertThat
24+
import org.junit.Before
25+
import org.junit.Test
26+
import org.junit.runner.RunWith
27+
28+
@RunWith(AndroidJUnit4::class)
29+
class ParcelableReferenceTest {
30+
@Before
31+
fun setUp() {
32+
RamDiskStorageKey.registerParser()
33+
}
34+
35+
@Test
36+
fun parcelableRoundtrip_works_withNullVersionMap() {
37+
val expected = Reference("myId", RamDiskStorageKey("backingKey"), null)
38+
39+
// Create a parcel and populate it with a ParcelableOperations object.
40+
val marshalled = with(Parcel.obtain()) {
41+
writeReference(expected, 0)
42+
marshall()
43+
}
44+
45+
// Now unmarshall the parcel, so we can verify the contents.
46+
val unmarshalled = with(Parcel.obtain()) {
47+
unmarshall(marshalled, 0, marshalled.size)
48+
setDataPosition(0)
49+
readReferencable()
50+
}
51+
assertThat(unmarshalled).isEqualTo(expected)
52+
}
53+
54+
@Test
55+
fun parcelableRoundtrip_works_withNonNullVersionMap() {
56+
val expected = Reference(
57+
"myId",
58+
RamDiskStorageKey("backingKey"),
59+
VersionMap("foo" to 1)
60+
)
61+
62+
// Create a parcel and populate it with a ParcelableOperations object.
63+
val marshalled = with(Parcel.obtain()) {
64+
writeReference(expected, 0)
65+
marshall()
66+
}
67+
68+
// Now unmarshall the parcel, so we can verify the contents.
69+
val unmarshalled = with(Parcel.obtain()) {
70+
unmarshall(marshalled, 0, marshalled.size)
71+
setDataPosition(0)
72+
readReferencable()
73+
}
74+
assertThat(unmarshalled).isEqualTo(expected)
75+
}
76+
77+
@Test
78+
fun parcelableRoundtripWorks_whenReference_isPartOfRawEntity() {
79+
val expectedReference = Reference(
80+
"myId",
81+
RamDiskStorageKey("backingKey"),
82+
VersionMap("foo" to 1)
83+
)
84+
val expected = RawEntity(
85+
"myId",
86+
singletons = mapOf("foo" to expectedReference),
87+
collections = emptyMap()
88+
)
89+
90+
// Create a parcel and populate it with a ParcelableOperations object.
91+
val marshalled = with(Parcel.obtain()) {
92+
writeTypedObject(ParcelableRawEntity(expected), 0)
93+
marshall()
94+
}
95+
96+
// Now unmarshall the parcel, so we can verify the contents.
97+
val unmarshalled = with(Parcel.obtain()) {
98+
unmarshall(marshalled, 0, marshalled.size)
99+
setDataPosition(0)
100+
readReferencable()
101+
}
102+
assertThat(unmarshalled).isEqualTo(expected)
103+
}
104+
}

javatests/arcs/core/storage/driver/DatabaseDriverTest.kt

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -95,7 +95,12 @@ class DatabaseDriverTest {
9595
calledWithVersion = version
9696
}
9797

98-
assertThat(calledWithData).isEqualTo(entity.toCrdtEntityData(VersionMap()))
98+
assertThat(calledWithData).isEqualTo(
99+
entity.toCrdtEntityData(VersionMap()) {
100+
if (it is Reference) it
101+
else buildReference(it)
102+
}
103+
)
99104
assertThat(calledWithVersion).isEqualTo(1)
100105
}
101106

0 commit comments

Comments
 (0)