Skip to content

Commit 0416a9c

Browse files
committed
pb: Add copy tests
1 parent dd433ce commit 0416a9c

File tree

1 file changed

+161
-0
lines changed
  • protobuf/protobuf-core/src/commonTest/kotlin/kotlinx/rpc/protobuf/test

1 file changed

+161
-0
lines changed
Lines changed: 161 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,161 @@
1+
/*
2+
* Copyright 2023-2025 JetBrains s.r.o and contributors. Use of this source code is governed by the Apache 2.0 license.
3+
*/
4+
5+
package kotlinx.rpc.protobuf.test
6+
7+
import invoke
8+
import kotlinx.rpc.protobuf.test.invoke
9+
import test.submsg.Other
10+
import test.submsg.invoke
11+
import kotlin.collections.iterator
12+
import kotlin.test.Test
13+
import kotlin.test.assertEquals
14+
import kotlin.test.assertNull
15+
import kotlin.test.assertTrue
16+
17+
class CopyTest {
18+
19+
@Test
20+
fun `test list copy - should be real copy`() {
21+
val userList = mutableListOf(1,2,3)
22+
val original = Repeated {
23+
listInt32 = userList
24+
}
25+
val copy = original.copy()
26+
userList.add(4)
27+
28+
assertEquals(listOf(1,2,3,4), original.listInt32)
29+
assertEquals(listOf(1,2,3), copy.listInt32)
30+
}
31+
32+
@Test
33+
fun `copy primitives and bytes - equality and independence`() {
34+
val bytesSrc = byteArrayOf(1, 2, 3)
35+
val msg = AllPrimitives {
36+
int32 = 42
37+
int64 = 123L
38+
uint32 = 7u
39+
uint64 = 9uL
40+
sint32 = -5
41+
sint64 = -7L
42+
fixed32 = 11u
43+
fixed64 = 13uL
44+
sfixed32 = -17
45+
sfixed64 = -19L
46+
bool = true
47+
float = 2.5f
48+
double = 3.5
49+
string = "hello"
50+
bytes = bytesSrc
51+
}
52+
53+
val copy = msg.copy()
54+
55+
// verify all fields are copied
56+
assertEquals(msg, copy)
57+
58+
// bytes are copied
59+
assertEquals(byteArrayOf(1, 2, 3).toList(), msg.bytes?.toList())
60+
assertEquals(byteArrayOf(1, 2, 3).toList(), copy.bytes?.toList())
61+
}
62+
63+
@Test
64+
fun `copy repeated messages - deep copy of list and elements`() {
65+
val orig = Repeated {
66+
listMessage = listOf(
67+
Repeated.Other { a = 1 },
68+
Repeated.Other { a = 2 }
69+
)
70+
}
71+
val cp = orig.copy()
72+
73+
// Lists are distinct instances
74+
assertTrue(orig.listMessage !== cp.listMessage)
75+
// Elements are deep-copied
76+
for (i in orig.listMessage.indices) {
77+
assertEquals(orig.listMessage[i].a, cp.listMessage[i].a)
78+
assertTrue(orig.listMessage[i] !== cp.listMessage[i])
79+
}
80+
81+
// Modify copy via copy { } and ensure original unaffected
82+
val mutated = orig.copy {
83+
listMessage = listMessage + Repeated.Other { a = 3 }
84+
}
85+
assertEquals(listOf(1, 2), orig.listMessage.map { it.a })
86+
assertEquals(listOf(1, 2, 3), mutated.listMessage.map { it.a })
87+
}
88+
89+
@Test
90+
fun `copy maps - deep copy keys and values`() {
91+
val original = TestMap {
92+
primitives = mapOf("a" to 10L, "b" to 20L)
93+
messages = mapOf(1 to PresenceCheck { RequiredPresence = 1 }, 2 to PresenceCheck { RequiredPresence = 2 })
94+
}
95+
96+
val copy = original.copy()
97+
98+
// verify maps are copied
99+
assertEquals(mapOf("a" to 10L, "b" to 20L), original.primitives)
100+
assertEquals(mapOf("a" to 10L, "b" to 20L), copy.primitives)
101+
102+
// deep copy for message map values
103+
for ((k, v) in original.messages) {
104+
val cv = copy.messages.getValue(k)
105+
assertEquals(v.RequiredPresence, cv.RequiredPresence)
106+
assertTrue(v !== cv)
107+
}
108+
109+
// copy { } change and ensure isolation
110+
val mutated = original.copy { primitives = primitives + ("z" to 90L) }
111+
assertEquals(setOf("a","b"), original.primitives.keys)
112+
assertEquals(setOf("a","b","z"), mutated.primitives.keys)
113+
}
114+
115+
@Test
116+
fun `copy oneof - preserve active case and allow mutation in lambda`() {
117+
val o1 = OneOfMsg.Companion { field = OneOfMsg.Field.Sint(7) }
118+
val c1 = o1.copy()
119+
assertEquals(o1.field, c1.field)
120+
121+
// mutate with copy-lambda (switch case)
122+
val c2 = o1.copy { field = OneOfMsg.Field.Other(Other.Companion { arg1 = "x" }) }
123+
// original unaffected
124+
assertEquals(OneOfMsg.Field.Sint(7), o1.field)
125+
// new case set
126+
assertTrue(c2.field is OneOfMsg.Field.Other)
127+
}
128+
129+
@Test
130+
fun `copy nested sub-message - deep copy and lambda modification`() {
131+
val equals = Equals.Companion {
132+
str1 = "root"
133+
bytes1 = byteArrayOf(1, 2, 3)
134+
someEnum2 = Equals.SomeEnum.VALUE1
135+
nested = Equals.Nested { content = "leaf" }
136+
}
137+
val copy = equals.copy()
138+
// deep copy of nested
139+
assertTrue(equals.nested !== copy.nested)
140+
assertEquals(equals.nested.content, copy.nested.content)
141+
142+
// modify nested in copy-lambda
143+
val mutated = equals.copy {
144+
nested = Equals.Nested { content = "mut" }
145+
}
146+
assertEquals("leaf", equals.nested.content)
147+
assertEquals("mut", mutated.nested.content)
148+
}
149+
150+
@Test
151+
fun `copy preserves presence and required fields`() {
152+
val p = PresenceCheck { RequiredPresence = 1 }
153+
val cp = p.copy()
154+
assertEquals(1, cp.RequiredPresence)
155+
assertNull(cp.OptionalPresence)
156+
157+
val cp2 = p.copy { OptionalPresence = 5f }
158+
assertEquals(1, cp2.RequiredPresence)
159+
assertEquals(5f, cp2.OptionalPresence)
160+
}
161+
}

0 commit comments

Comments
 (0)