1 /*
2  * Copyright 2017-2023 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
3  */
4 
5 package kotlinx.serialization.protobuf.conformance
6 
7 import com.google.protobuf_test_messages.proto3.*
8 import io.kotlintest.properties.*
9 import kotlinx.serialization.*
10 import kotlinx.serialization.protobuf.*
11 import kotlin.test.*
12 
13 @Serializable
14 data class KTestMessagesProto3Map(
15     @ProtoNumber(56) val mapInt32Int32: Map<Int, Int> = emptyMap(),
16     @ProtoNumber(57) val mapInt64Int64: Map<Long, Long> = emptyMap(),
17     @ProtoNumber(58) val mapUint32Uint32: Map<UInt, UInt> = emptyMap(),
18     @ProtoNumber(59) val mapUint64Uint64: Map<ULong, ULong> = emptyMap(),
19     @ProtoNumber(60) val mapSint32Sint32: Map<Int, Int> = emptyMap(),
20     @ProtoNumber(61) val mapSint64Sint64: Map<Long, Long> = emptyMap(),
21     @ProtoNumber(62) val mapFixed32Fixed32: Map<Int, Int> = emptyMap(),
22     @ProtoNumber(63) val mapFixed64Fixed64: Map<Long, Long> = emptyMap(),
23     @ProtoNumber(64) val mapSfixed32Sfixed32: Map<Int, Int> = emptyMap(),
24     @ProtoNumber(65) val mapSfixed64Sfixed64: Map<Long, Long> = emptyMap(),
25     @ProtoNumber(66) val mapInt32Float: Map<Int, Float> = emptyMap(),
26     @ProtoNumber(67) val mapInt32Double: Map<Int, Double> = emptyMap(),
27     @ProtoNumber(68) val mapBoolBool: Map<Boolean, Boolean> = emptyMap(),
28     @ProtoNumber(69) val mapStringString: Map<String, String> = emptyMap(),
29     @ProtoNumber(70) val mapStringBytes: Map<String, ByteArray> = emptyMap(),
30     @ProtoNumber(71) val mapStringNestedMessage: Map<String, KTestMessagesProto3Message.KNestedMessage> = emptyMap(),
31     @ProtoNumber(72) val mapStringForeignMessage: Map<String, KForeignMessage> = emptyMap(),
32     @ProtoNumber(73) val mapStringNestedEnum: Map<String, KTestMessagesProto3Enum.KNestedEnum> = emptyMap(),
33     @ProtoNumber(74) val mapStringForeignEnum: Map<String, KForeignEnum> = emptyMap(),
34 )
35 
36 class Proto3MapTest {
37     @Test
defaultnull38     fun default() {
39         val message = KTestMessagesProto3Map(
40             mapInt32Int32 = Gen.map(Gen.int(), Gen.int()).generate(),
41             mapInt64Int64 = Gen.map(Gen.long(), Gen.long()).generate(),
42             mapUint32Uint32 = Gen.map(Gen.int().map { it.toUInt() }, Gen.int().map { it.toUInt() }).generate(),
43             mapUint64Uint64 = Gen.map(Gen.int().map { it.toULong() }, Gen.int().map { it.toULong() }).generate(),
44             mapInt32Float = Gen.map(Gen.int(), Gen.float()).generate(),
45             mapInt32Double = Gen.map(Gen.int(), Gen.double()).generate(),
46             mapBoolBool = Gen.map(Gen.bool(), Gen.bool()).generate(),
47             mapStringString = Gen.map(Gen.string(), Gen.string()).generate(),
48             mapStringBytes = Gen.map(Gen.string(), Gen.string().map { it.toByteArray() }).generate(),
49             mapStringNestedMessage = mapOf(
50                 "asd_1" to KTestMessagesProto3Message.KNestedMessage(
51                     1,
52                     null
53                 ),
54                 "asi_#" to KTestMessagesProto3Message.KNestedMessage(
55                     2,
56                     KTestMessagesProto3Message(
57                         KTestMessagesProto3Message.KNestedMessage(3, null),
58                     )
59                 )
60             ),
61             mapStringForeignMessage = mapOf(
62                 "" to KForeignMessage(1),
63                 "-2" to KForeignMessage(-12),
64             ),
65             mapStringNestedEnum = Gen.map(
66                 Gen.string(), Gen.oneOf(
67                     KTestMessagesProto3Enum.KNestedEnum.entries,
68                 )
69             ).generate(),
70             mapStringForeignEnum = Gen.map(
71                 Gen.string(), Gen.oneOf(
72                     KForeignEnum.entries,
73                 )
74             ).generate(),
75         )
76 
77         val bytes = ProtoBuf.encodeToByteArray(message)
78         val restored = TestMessagesProto3.TestAllTypesProto3.parseFrom(bytes)
79 
80 
81         assertEquals(message.mapInt32Int32, restored.mapInt32Int32Map)
82         assertEquals(message.mapInt64Int64, restored.mapInt64Int64Map)
83         assertEquals(
84             message.mapUint32Uint32,
85             restored.mapUint32Uint32Map.map { it.key.toUInt() to it.value.toUInt() }.toMap()
86         )
87         assertEquals(
88             message.mapUint64Uint64,
89             restored.mapUint64Uint64Map.map { it.key.toULong() to it.value.toULong() }.toMap()
90         )
91         assertEquals(message.mapInt32Float, restored.mapInt32FloatMap)
92         assertEquals(message.mapInt32Double, restored.mapInt32DoubleMap)
93         assertEquals(message.mapBoolBool, restored.mapBoolBoolMap)
94         assertEquals(message.mapStringString, restored.mapStringStringMap)
95         assertContentEquals(
96             message.mapStringBytes.mapValues { it.value.toString(Charsets.UTF_32) }.entries.toList(),
97             restored.mapStringBytesMap.mapValues { it.value.toByteArray().toString(Charsets.UTF_32) }.entries.toList()
98         )
99         assertEquals(
100             message.mapStringNestedMessage.mapValues { it.value.toProto() },
101             restored.mapStringNestedMessageMap
102         )
103         assertEquals(
104             message.mapStringForeignMessage.mapValues { it.value.toProto() },
105             restored.mapStringForeignMessageMap
106         )
107         assertEquals(
108             message.mapStringNestedEnum.mapValues { it.value.name },
109             restored.mapStringNestedEnumMap.mapValues { it.value.name },
110         )
111         assertEquals(
112             message.mapStringForeignEnum.mapValues { it.value.name },
113             restored.mapStringForeignEnumMap.mapValues { it.value.name }
114         )
115 
116         val restoredMessage = ProtoBuf.decodeFromByteArray<KTestMessagesProto3Map>(restored.toByteArray())
117         assertEquals(message.copy(mapStringBytes = mapOf()), restoredMessage.copy(mapStringBytes = mapOf()))
118     }
119 
120     @Test
121     @Ignore
122     // Issue: https://github.com/Kotlin/kotlinx.serialization/issues/2417
signedAndFixednull123     fun signedAndFixed() {
124         val message = KTestMessagesProto3Map(
125             mapSint32Sint32 = Gen.map(Gen.int(), Gen.int()).generate(),
126             mapSint64Sint64 = Gen.map(Gen.long(), Gen.long()).generate(),
127             mapFixed32Fixed32 = Gen.map(Gen.int(), Gen.int()).generate(),
128             mapFixed64Fixed64 = Gen.map(Gen.long(), Gen.long()).generate(),
129             mapSfixed32Sfixed32 = Gen.map(Gen.int(), Gen.int()).generate(),
130             mapSfixed64Sfixed64 = Gen.map(Gen.long(), Gen.long()).generate(),
131         )
132 
133         val bytes = ProtoBuf.encodeToByteArray(message)
134         val restored = TestMessagesProto3.TestAllTypesProto3.parseFrom(bytes)
135 
136 
137         assertContentEquals(message.mapSint32Sint32.entries.toList(), restored.mapSint32Sint32Map.entries.toList())
138         assertContentEquals(message.mapSint64Sint64.entries.toList(), restored.mapSint64Sint64Map.entries.toList())
139         assertContentEquals(message.mapFixed32Fixed32.entries.toList(), restored.mapFixed32Fixed32Map.entries.toList())
140         assertContentEquals(message.mapFixed64Fixed64.entries.toList(), restored.mapFixed64Fixed64Map.entries.toList())
141         assertContentEquals(
142             message.mapSfixed32Sfixed32.entries.toList(),
143             restored.mapSfixed32Sfixed32Map.entries.toList()
144         )
145         assertContentEquals(
146             message.mapSfixed64Sfixed64.entries.toList(),
147             restored.mapSfixed64Sfixed64Map.entries.toList()
148         )
149 
150 
151         val restoredMessage = ProtoBuf.decodeFromByteArray<KTestMessagesProto3Map>(restored.toByteArray())
152         assertEquals(message, restoredMessage)
153     }
154 }
155