Skip to content

Commit b03038d

Browse files
authored
dataconnect: add tests for user-defined enum support (#7173)
1 parent 0ebb4ab commit b03038d

File tree

12 files changed

+2329
-0
lines changed

12 files changed

+2329
-0
lines changed

firebase-dataconnect/connectors/connectors.gradle.kts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,7 @@ dependencies {
7373
testImplementation(libs.kotest.assertions)
7474
testImplementation(libs.kotest.property)
7575
testImplementation(libs.kotlin.coroutines.test)
76+
testImplementation(libs.kotlinx.serialization.json)
7677
testImplementation(libs.mockk)
7778
testImplementation(libs.robolectric)
7879

firebase-dataconnect/connectors/src/androidTest/kotlin/com/google/firebase/dataconnect/connectors/demo/EnumIntegrationTest.kt

Lines changed: 546 additions & 0 deletions
Large diffs are not rendered by default.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,168 @@
1+
/*
2+
* Copyright 2025 Google LLC
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
@file:OptIn(ExperimentalKotest::class)
18+
19+
package com.google.firebase.dataconnect.connectors.demo
20+
21+
import com.google.firebase.dataconnect.testutil.property.arbitrary.distinctPair
22+
import io.kotest.assertions.withClue
23+
import io.kotest.common.ExperimentalKotest
24+
import io.kotest.matchers.shouldBe
25+
import io.kotest.matchers.shouldNotBe
26+
import io.kotest.matchers.types.shouldBeSameInstanceAs
27+
import io.kotest.matchers.types.shouldNotBeSameInstanceAs
28+
import io.kotest.property.Arb
29+
import io.kotest.property.PropTestConfig
30+
import io.kotest.property.arbitrary.enum
31+
import io.kotest.property.arbitrary.of
32+
import io.kotest.property.assume
33+
import io.kotest.property.checkAll
34+
import kotlinx.coroutines.test.runTest
35+
import org.junit.Test
36+
37+
@Suppress("ReplaceCallWithBinaryOperator")
38+
class EnumValueKnownUnitTest {
39+
40+
@Test
41+
fun `constructor() should set properties to corresponding arguments`() = runTest {
42+
checkAll(propTestConfig, Arb.enum<Food>()) { enum ->
43+
val enumValue = EnumValue.Known(enum)
44+
enumValue.value shouldBeSameInstanceAs enum
45+
}
46+
}
47+
48+
@Test
49+
fun `stringValue property should be the name of the enum`() = runTest {
50+
checkAll(propTestConfig, Arb.enum<Food>()) { enum ->
51+
val enumValue = EnumValue.Known(enum)
52+
enumValue.stringValue shouldBe enum.name
53+
}
54+
}
55+
56+
@Test
57+
fun `equals() should return true when invoked with itself`() = runTest {
58+
checkAll(propTestConfig, Arb.enum<Food>()) { enum ->
59+
val enumValue = EnumValue.Known(enum)
60+
enumValue.equals(enumValue) shouldBe true
61+
}
62+
}
63+
64+
@Test
65+
fun `equals() should return true when invoked with a distinct, but equal, instance`() = runTest {
66+
checkAll(propTestConfig, Arb.enum<Food>()) { enum ->
67+
val enumValue1 = EnumValue.Known(enum)
68+
val enumValue2 = EnumValue.Known(enum)
69+
enumValue1.equals(enumValue2) shouldBe true
70+
}
71+
}
72+
73+
@Test
74+
fun `equals() should return false when invoked with null`() = runTest {
75+
checkAll(propTestConfig, Arb.enum<Food>()) { enum ->
76+
val enumValue = EnumValue.Known(enum)
77+
enumValue.equals(null) shouldBe false
78+
}
79+
}
80+
81+
@Test
82+
fun `equals() should return false when invoked with a different type`() = runTest {
83+
val others = Arb.of("foo", 42, java.time.LocalDate.now())
84+
checkAll(propTestConfig, Arb.enum<Food>(), others) { enum, other ->
85+
val enumValue = EnumValue.Known(enum)
86+
enumValue.equals(other) shouldBe false
87+
}
88+
}
89+
90+
@Test
91+
fun `equals() should return false when the enum differs`() = runTest {
92+
checkAll(propTestConfig, Arb.enum<Food>().distinctPair()) { (enum1, enum2) ->
93+
val enumValue1 = EnumValue.Known(enum1)
94+
val enumValue2 = EnumValue.Known(enum2)
95+
enumValue1.equals(enumValue2) shouldBe false
96+
}
97+
}
98+
99+
@Test
100+
fun `hashCode() should return the same value when invoked repeatedly`() = runTest {
101+
checkAll(propTestConfig, Arb.enum<Food>()) { enum ->
102+
val enumValue = EnumValue.Known(enum)
103+
val hashCode = enumValue.hashCode()
104+
repeat(5) { withClue("iteration=$it") { enumValue.hashCode() shouldBe hashCode } }
105+
}
106+
}
107+
108+
@Test
109+
fun `hashCode() should return the same value when invoked on equal, but distinct, objects`() =
110+
runTest {
111+
checkAll(propTestConfig, Arb.enum<Food>()) { enum ->
112+
val enumValue1 = EnumValue.Known(enum)
113+
val enumValue2 = EnumValue.Known(enum)
114+
enumValue1.hashCode() shouldBe enumValue2.hashCode()
115+
}
116+
}
117+
118+
@Test
119+
fun `hashCode() should return different values for different enum values`() = runTest {
120+
checkAll(propTestConfig, Arb.enum<Food>().distinctPair()) { (enum1, enum2) ->
121+
assume(enum1.hashCode() != enum2.hashCode())
122+
val enumValue1 = EnumValue.Known(enum1)
123+
val enumValue2 = EnumValue.Known(enum2)
124+
enumValue1.hashCode() shouldNotBe enumValue2.hashCode()
125+
}
126+
}
127+
128+
@Test
129+
fun `toString() should return a string conforming to what is expected`() = runTest {
130+
checkAll(propTestConfig, Arb.enum<Food>()) { enum ->
131+
val enumValue = EnumValue.Known(enum)
132+
enumValue.toString() shouldBe "Known(${enum.name})"
133+
}
134+
}
135+
136+
@Test
137+
fun `copy() with no arguments should return an equal, but distinct, instance`() = runTest {
138+
checkAll(propTestConfig, Arb.enum<Food>()) { enum ->
139+
val enumValue = EnumValue.Known(enum)
140+
val enumValueCopy = enumValue.copy()
141+
enumValue shouldBe enumValueCopy
142+
enumValue shouldNotBeSameInstanceAs enumValueCopy
143+
}
144+
}
145+
146+
@Test
147+
fun `copy() with all arguments should return a new instance with the given arguments`() =
148+
runTest {
149+
checkAll(propTestConfig, Arb.enum<Food>().distinctPair()) { (enum1, enum2) ->
150+
val enumValue1 = EnumValue.Known(enum1)
151+
val enumValue2 = enumValue1.copy(enum2)
152+
enumValue2 shouldBe EnumValue.Known(enum2)
153+
}
154+
}
155+
156+
@Suppress("unused")
157+
private enum class Food {
158+
Burrito,
159+
Cake,
160+
Pizza,
161+
Shawarma,
162+
Sushi,
163+
}
164+
165+
private companion object {
166+
val propTestConfig = PropTestConfig(iterations = 50)
167+
}
168+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
/*
2+
* Copyright 2025 Google LLC
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
@file:OptIn(ExperimentalKotest::class, ExperimentalSerializationApi::class)
17+
18+
package com.google.firebase.dataconnect.connectors.demo
19+
20+
import com.google.firebase.dataconnect.testutil.property.arbitrary.dataConnect
21+
import io.kotest.common.ExperimentalKotest
22+
import io.kotest.matchers.shouldBe
23+
import io.kotest.property.Arb
24+
import io.kotest.property.EdgeConfig
25+
import io.kotest.property.PropTestConfig
26+
import io.kotest.property.arbitrary.enum
27+
import io.kotest.property.arbitrary.map
28+
import io.kotest.property.checkAll
29+
import kotlinx.coroutines.test.runTest
30+
import kotlinx.serialization.ExperimentalSerializationApi
31+
import kotlinx.serialization.KSerializer
32+
import kotlinx.serialization.json.Json
33+
import kotlinx.serialization.json.JsonPrimitive
34+
import kotlinx.serialization.json.jsonPrimitive
35+
import org.junit.Test
36+
37+
class EnumValueSerializerUnitTest {
38+
39+
@Test
40+
fun `serialize() should produce the expected serialized string for _known_ values`() = runTest {
41+
checkAll(propTestConfig, Arb.knownEnumValue()) { knownEnumValue: EnumValue.Known<Dog> ->
42+
val encodedValue = Json.encodeToJsonElement(Dog.serializer, knownEnumValue)
43+
encodedValue.jsonPrimitive.content shouldBe knownEnumValue.value.name
44+
}
45+
}
46+
47+
@Test
48+
fun `serialize() should produce the expected serialized string for _unknown_ values`() = runTest {
49+
checkAll(propTestConfig, Arb.unknownEnumValue()) { unknownEnumValue: EnumValue.Unknown ->
50+
val encodedValue = Json.encodeToJsonElement(Dog.serializer, unknownEnumValue)
51+
encodedValue.jsonPrimitive.content shouldBe unknownEnumValue.stringValue
52+
}
53+
}
54+
55+
@Test
56+
fun `deserialize() should produce the expected EnumValue object for _known_ values`() = runTest {
57+
checkAll(propTestConfig, Arb.knownEnumValue()) { knownEnumValue: EnumValue.Known<Dog> ->
58+
val encodedValue = JsonPrimitive(knownEnumValue.value.name)
59+
val decodedEnumValue = Json.decodeFromJsonElement(Dog.serializer, encodedValue)
60+
decodedEnumValue shouldBe knownEnumValue
61+
}
62+
}
63+
64+
@Test
65+
fun `deserialize() should produce the expected EnumValue object for _unknown_ values`() =
66+
runTest {
67+
checkAll(propTestConfig, Arb.unknownEnumValue()) { unknownEnumValue: EnumValue.Unknown ->
68+
val encodedValue = JsonPrimitive(unknownEnumValue.stringValue)
69+
val decodedEnumValue = Json.decodeFromJsonElement(Dog.serializer, encodedValue)
70+
decodedEnumValue shouldBe unknownEnumValue
71+
}
72+
}
73+
74+
@Suppress("unused")
75+
enum class Dog {
76+
Boxer,
77+
Bulldog,
78+
Dachshund,
79+
Labrador,
80+
Poodle;
81+
82+
companion object {
83+
val serializer: KSerializer<EnumValue<Dog>> = EnumValueSerializer(Dog.entries)
84+
}
85+
}
86+
87+
private companion object {
88+
val propTestConfig =
89+
PropTestConfig(
90+
iterations = 500,
91+
edgeConfig = EdgeConfig(edgecasesGenerationProbability = 0.2)
92+
)
93+
94+
fun Arb.Companion.unknownEnumValue(
95+
stringValue: Arb<String> = Arb.dataConnect.string()
96+
): Arb<EnumValue.Unknown> = stringValue.map { EnumValue.Unknown(it) }
97+
98+
fun Arb.Companion.knownEnumValue(
99+
enumValue: Arb<Dog> = Arb.enum<Dog>()
100+
): Arb<EnumValue.Known<Dog>> = enumValue.map { EnumValue.Known(it) }
101+
}
102+
}

0 commit comments

Comments
 (0)