Skip to content

Commit 712db3a

Browse files
committed
ProtoStructEncoderUnitTest.kt: use arb
1 parent 39d683c commit 712db3a

File tree

2 files changed

+114
-116
lines changed

2 files changed

+114
-116
lines changed

firebase-dataconnect/src/main/kotlin/com/google/firebase/dataconnect/util/ProtoUtil.kt

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -248,11 +248,13 @@ internal object ProtoUtil {
248248

249249
fun Value.toAny(): Any? = valueToAnyMutualRecursion.anyValueFromValue(this)
250250

251-
fun List<Any?>.toValueProto(): Value {
251+
fun <T> List<T>.toValueProto(): Value {
252252
val key = "y8czq9rh75"
253253
return mapOf(key to this).toStructProto().getFieldsOrThrow(key)
254254
}
255255

256+
fun <T> List<T>.toListValueProto(): ListValue = toValueProto().listValue
257+
256258
fun Map<String, Any?>.toValueProto(): Value =
257259
Value.newBuilder().setStructValue(toStructProto()).build()
258260

firebase-dataconnect/src/test/kotlin/com/google/firebase/dataconnect/ProtoStructEncoderUnitTest.kt

Lines changed: 111 additions & 115 deletions
Original file line numberDiff line numberDiff line change
@@ -13,47 +13,57 @@
1313
* See the License for the specific language governing permissions and
1414
* limitations under the License.
1515
*/
16-
17-
@file:OptIn(ExperimentalSerializationApi::class)
18-
1916
package com.google.firebase.dataconnect
2017

2118
import com.google.firebase.dataconnect.testutil.shouldBe
2219
import com.google.firebase.dataconnect.testutil.shouldBeDefaultInstance
2320
import com.google.firebase.dataconnect.testutil.shouldContainWithNonAbuttingText
2421
import com.google.firebase.dataconnect.util.ProtoUtil.buildStructProto
2522
import com.google.firebase.dataconnect.util.ProtoUtil.encodeToStruct
23+
import com.google.firebase.dataconnect.util.ProtoUtil.toListValueProto
2624
import com.google.protobuf.Struct
2725
import io.kotest.assertions.throwables.shouldThrow
28-
import kotlinx.serialization.ExperimentalSerializationApi
26+
import io.kotest.property.Arb
27+
import io.kotest.property.arbitrary.arbitrary
28+
import io.kotest.property.arbitrary.boolean
29+
import io.kotest.property.arbitrary.double
30+
import io.kotest.property.arbitrary.int
31+
import io.kotest.property.arbitrary.list
32+
import io.kotest.property.arbitrary.next
33+
import io.kotest.property.arbitrary.orNull
34+
import io.kotest.property.arbitrary.string
35+
import io.kotest.property.checkAll
36+
import kotlinx.coroutines.test.runTest
2937
import kotlinx.serialization.Serializable
3038
import org.junit.Test
3139

3240
class ProtoStructEncoderUnitTest {
3341

3442
@Test
3543
fun `encodeToStruct() should throw if a NUMBER_VALUE is produced`() {
36-
val exception = shouldThrow<IllegalArgumentException> { encodeToStruct(42) }
44+
val intValue: Int = Arb.int().next()
45+
val exception = shouldThrow<IllegalArgumentException> { encodeToStruct(intValue) }
3746
exception.message shouldContainWithNonAbuttingText "NUMBER_VALUE"
3847
}
3948

4049
@Test
4150
fun `encodeToStruct() should throw if a BOOL_VALUE is produced`() {
42-
val exception = shouldThrow<IllegalArgumentException> { encodeToStruct(true) }
51+
val booleanValue: Boolean = Arb.boolean().next()
52+
val exception = shouldThrow<IllegalArgumentException> { encodeToStruct(booleanValue) }
4353
exception.message shouldContainWithNonAbuttingText "BOOL_VALUE"
4454
}
4555

4656
@Test
4757
fun `encodeToStruct() should throw if a STRING_VALUE is produced`() {
48-
val exception =
49-
shouldThrow<IllegalArgumentException> { encodeToStruct("arbitrary string value") }
58+
val stringValue: String = Arb.string().next()
59+
val exception = shouldThrow<IllegalArgumentException> { encodeToStruct(stringValue) }
5060
exception.message shouldContainWithNonAbuttingText "STRING_VALUE"
5161
}
5262

5363
@Test
5464
fun `encodeToStruct() should throw if a LIST_VALUE is produced`() {
55-
val exception =
56-
shouldThrow<IllegalArgumentException> { encodeToStruct(listOf("element1", "element2")) }
65+
val listValue: List<String> = Arb.list(Arb.string(5..10), 1..10).next()
66+
val exception = shouldThrow<IllegalArgumentException> { encodeToStruct(listValue) }
5767
exception.message shouldContainWithNonAbuttingText "LIST_VALUE"
5868
}
5969

@@ -70,109 +80,79 @@ class ProtoStructEncoderUnitTest {
7080
}
7181

7282
@Test
73-
fun `encodeToStruct() should encode an class with all primitive types`() {
83+
fun `encodeToStruct() should encode an class with all primitive types`() = runTest {
7484
@Serializable
7585
data class TestData(
7686
val iv: Int,
7787
val dv: Double,
78-
val bvt: Boolean,
79-
val bvf: Boolean,
88+
val bv: Boolean,
8089
val sv: String,
81-
val nsvn: String?,
82-
val nsvnn: String?
90+
val svn: String?,
8391
)
84-
val encodedStruct =
85-
encodeToStruct(
86-
TestData(
87-
iv = 42,
88-
dv = 1234.5,
89-
bvt = true,
90-
bvf = false,
91-
sv = "blah blah",
92-
nsvn = null,
93-
nsvnn = "I'm not null"
94-
)
95-
)
96-
97-
encodedStruct.shouldBe(
98-
buildStructProto {
99-
put("iv", 42.0)
100-
put("dv", 1234.5)
101-
put("bvt", true)
102-
put("bvf", false)
103-
put("sv", "blah blah")
104-
putNull("nsvn")
105-
put("nsvnn", "I'm not null")
92+
val arb: Arb<Pair<TestData, Struct>> = arbitrary {
93+
val iv = Arb.int().bind()
94+
val dv = Arb.double().bind()
95+
val bv = Arb.boolean().bind()
96+
val sv = Arb.string().bind()
97+
val svn = Arb.string().orNull(0.33).bind()
98+
99+
val testData = TestData(iv = iv, dv = dv, bv = bv, sv = sv, svn = svn)
100+
101+
val struct = buildStructProto {
102+
put("iv", iv)
103+
put("dv", dv)
104+
put("bv", bv)
105+
put("sv", sv)
106+
if (svn === null) {
107+
putNull("svn")
108+
} else {
109+
put("svn", svn)
110+
}
106111
}
107-
)
112+
113+
Pair(testData, struct)
114+
}
115+
116+
checkAll(arb) { (testData, expectedStruct) ->
117+
val encodedStruct = encodeToStruct(testData)
118+
encodedStruct shouldBe expectedStruct
119+
}
108120
}
109121

110122
@Test
111-
fun `encodeToStruct() should encode lists with all primitive types`() {
123+
fun `encodeToStruct() should encode lists with all primitive types`() = runTest {
112124
@Serializable
113125
data class TestData(
114126
val iv: List<Int>,
115127
val dv: List<Double>,
116128
val bv: List<Boolean>,
117129
val sv: List<String>,
118-
val nsv: List<String?>
130+
val svn: List<String?>
119131
)
120-
val encodedStruct =
121-
encodeToStruct(
122-
TestData(
123-
iv = listOf(42, 43),
124-
dv = listOf(1234.5, 5678.9),
125-
bv = listOf(true, false, false, true),
126-
sv = listOf("abcde", "fghij"),
127-
nsv = listOf("klmno", null, "pqrst", null)
128-
)
129-
)
130-
131-
encodedStruct.shouldBe(
132-
buildStructProto {
133-
putList("iv") {
134-
add(42.0)
135-
add(43.0)
136-
}
137-
putList("dv") {
138-
add(1234.5)
139-
add(5678.9)
140-
}
141-
putList("bv") {
142-
add(true)
143-
add(false)
144-
add(false)
145-
add(true)
146-
}
147-
putList("sv") {
148-
add("abcde")
149-
add("fghij")
150-
}
151-
putList("nsv") {
152-
add("klmno")
153-
addNull()
154-
add("pqrst")
155-
addNull()
156-
}
132+
val arb: Arb<Pair<TestData, Struct>> = arbitrary {
133+
val iv = Arb.list(Arb.int()).bind()
134+
val dv = Arb.list(Arb.double()).bind()
135+
val bv = Arb.list(Arb.boolean()).bind()
136+
val sv = Arb.list(Arb.string()).bind()
137+
val svn = Arb.list(Arb.string().orNull(0.33)).bind()
138+
139+
val testData = TestData(iv = iv, dv = dv, bv = bv, sv = sv, svn = svn)
140+
141+
val struct = buildStructProto {
142+
put("iv", iv.map { it.toDouble() }.toListValueProto())
143+
put("dv", dv.toListValueProto())
144+
put("bv", bv.toListValueProto())
145+
put("sv", sv.toListValueProto())
146+
put("svn", svn.toListValueProto())
157147
}
158-
)
159-
}
160148

161-
@Test
162-
fun `encodeToStruct() should support nested composite types`() {
163-
@Serializable data class TestData3(val s: String)
164-
@Serializable data class TestData2(val data3: TestData3, val data3N: TestData3?)
165-
@Serializable data class TestData1(val data2: TestData2)
166-
val encodedStruct = encodeToStruct(TestData1(TestData2(TestData3("zzzz"), null)))
167-
168-
encodedStruct.shouldBe(
169-
buildStructProto {
170-
putStruct("data2") {
171-
putNull("data3N")
172-
putStruct("data3") { put("s", "zzzz") }
173-
}
174-
}
175-
)
149+
Pair(testData, struct)
150+
}
151+
152+
checkAll(arb) { (testData, expectedStruct) ->
153+
val encodedStruct = encodeToStruct(testData)
154+
encodedStruct shouldBe expectedStruct
155+
}
176156
}
177157

178158
@Test
@@ -194,29 +174,45 @@ class ProtoStructEncoderUnitTest {
194174
}
195175

196176
@Test
197-
fun `encodeToStruct() should support OptionalVariable Value when T is not nullable`() {
198-
@Serializable data class TestData(val s: OptionalVariable<String>)
199-
200-
val encodedStruct = encodeToStruct(TestData(OptionalVariable.Value("Hello")))
201-
202-
encodedStruct shouldBe buildStructProto { put("s", "Hello") }
203-
}
204-
205-
@Test
206-
fun `encodeToStruct() should support OptionalVariable Value when T is nullable but not null`() {
207-
@Serializable data class TestData(val s: OptionalVariable<String?>)
208-
209-
val encodedStruct = encodeToStruct(TestData(OptionalVariable.Value("World")))
210-
211-
encodedStruct shouldBe buildStructProto { put("s", "World") }
177+
fun `encodeToStruct() should support OptionalVariable Value when T is not nullable`() = runTest {
178+
@Serializable
179+
data class TestData(val s: OptionalVariable<String>, val i: OptionalVariable<Int>)
180+
val arb = arbitrary {
181+
val s = OptionalVariable.Value(Arb.string().bind())
182+
val i = OptionalVariable.Value(Arb.int().bind())
183+
TestData(s, i)
184+
}
185+
186+
checkAll(arb) { testData ->
187+
val encodedStruct = encodeToStruct(testData)
188+
val expected = buildStructProto {
189+
put("s", testData.s.valueOrThrow())
190+
put("i", testData.i.valueOrThrow())
191+
}
192+
encodedStruct shouldBe expected
193+
}
212194
}
213195

214196
@Test
215-
fun `encodeToStruct() should support OptionalVariable Value when T is nullable and null`() {
216-
@Serializable data class TestData(val s: OptionalVariable<String?>)
217-
218-
val encodedStruct = encodeToStruct(TestData(OptionalVariable.Value(null)))
219-
220-
encodedStruct shouldBe buildStructProto { putNull("s") }
197+
fun `encodeToStruct() should support OptionalVariable Value when T is nullable`() = runTest {
198+
@Serializable
199+
data class TestData(
200+
val s: OptionalVariable<String?>,
201+
val i: OptionalVariable<Int?>,
202+
)
203+
val arb = arbitrary {
204+
val s = OptionalVariable.Value(Arb.string().orNull(0.33).bind())
205+
val i = OptionalVariable.Value(Arb.int().orNull(0.33).bind())
206+
TestData(s, i)
207+
}
208+
209+
checkAll(arb) { testData ->
210+
val encodedStruct = encodeToStruct(testData)
211+
val expected = buildStructProto {
212+
put("s", testData.s.valueOrThrow())
213+
put("i", testData.i.valueOrThrow())
214+
}
215+
encodedStruct shouldBe expected
216+
}
221217
}
222218
}

0 commit comments

Comments
 (0)