|
13 | 13 | */ |
14 | 14 |
|
15 | 15 | import com.google.j2objc.annotations.AutoreleasePool; |
| 16 | +import com.google.protobuf.ByteString; |
16 | 17 | import com.google.protobuf.Descriptors.Descriptor; |
17 | 18 | import com.google.protobuf.Descriptors.FieldDescriptor; |
18 | 19 | import com.google.protobuf.Descriptors.FieldDescriptor.Type; |
|
23 | 24 | import java.util.ArrayList; |
24 | 25 | import java.util.List; |
25 | 26 | import java.util.Map; |
| 27 | +import protos.FakeScalarBytesMap; |
| 28 | +import protos.FakeScalarBytesMapFieldEntry; |
| 29 | +import protos.FakeScalarEnumMap; |
| 30 | +import protos.FakeScalarEnumMapFieldEntry; |
| 31 | +import protos.FakeScalarMsgMap; |
| 32 | +import protos.FakeScalarMsgMapFieldEntry; |
| 33 | +import protos.FakeStringStringMap; |
| 34 | +import protos.FakeStringStringMapFieldEntry; |
26 | 35 | import protos.MapMsg; |
27 | 36 | import protos.MapMsgOrBuilder; |
28 | 37 | import protos.MapValue; |
29 | | - |
30 | | -/** |
31 | | - * Tests for correct serialization and deserialization of map fields. |
32 | | - */ |
| 38 | +import protos.RandomMessage; |
| 39 | +import protos.RealScalarBytesMap; |
| 40 | +import protos.RealScalarEnumMap; |
| 41 | +import protos.RealScalarMsgMap; |
| 42 | +import protos.RealStringBytesMap; |
| 43 | +import protos.RealStringStringMap; |
| 44 | + |
| 45 | +/** Tests for correct serialization and deserialization of map fields. */ |
33 | 46 | public class MapsTest extends ProtobufTest { |
34 | 47 |
|
35 | 48 | @AutoreleasePool |
@@ -272,6 +285,119 @@ public void testEquals() throws Exception { |
272 | 285 | assertEquals(msg1.hashCode(), msg2.hashCode()); |
273 | 286 | } |
274 | 287 |
|
| 288 | + public void testStringStringRepeatedFieldsToMapConversions() throws Exception { |
| 289 | + // According to https://protobuf.dev/programming-guides/proto3/#backwards repeated fields are |
| 290 | + // converted to maps by using the first field as the key and the second field as the value. |
| 291 | + // This also verifies that we can parse maps with missing fields by using default values. |
| 292 | + FakeStringStringMap fakeMap = |
| 293 | + FakeStringStringMap.newBuilder() |
| 294 | + .addMapField(FakeStringStringMapFieldEntry.newBuilder().setKey("duck").build()) |
| 295 | + .addMapField(FakeStringStringMapFieldEntry.newBuilder().setValue("quack").build()) |
| 296 | + .addMapField( |
| 297 | + FakeStringStringMapFieldEntry.newBuilder().setKey("cat").setValue("meow").build()) |
| 298 | + .build(); |
| 299 | + byte[] bytes = fakeMap.toByteArray(); |
| 300 | + RealStringStringMap realMap = RealStringStringMap.parseFrom(bytes); |
| 301 | + assertEquals("", realMap.getMapFieldOrThrow("duck")); |
| 302 | + assertEquals("quack", realMap.getMapFieldOrThrow("")); |
| 303 | + assertEquals("meow", realMap.getMapFieldOrThrow("cat")); |
| 304 | + } |
| 305 | + |
| 306 | + public void testScalarBytesRepeatedFieldsToMapConversions() throws Exception { |
| 307 | + // According to https://protobuf.dev/programming-guides/proto3/#backwards repeated fields are |
| 308 | + // converted to maps by using the first field as the key and the second field as the value. |
| 309 | + // This also verifies that we can parse maps with missing fields by using default values. |
| 310 | + FakeScalarBytesMap fakeScalarBytesMap = |
| 311 | + FakeScalarBytesMap.newBuilder() |
| 312 | + .addMapField(FakeScalarBytesMapFieldEntry.newBuilder().setKey(42).build()) |
| 313 | + .addMapField( |
| 314 | + FakeScalarBytesMapFieldEntry.newBuilder().setValue(ByteString.EMPTY).build()) |
| 315 | + .build(); |
| 316 | + byte[] bytes = fakeScalarBytesMap.toByteArray(); |
| 317 | + RealScalarBytesMap realScalarBytesMap = RealScalarBytesMap.parseFrom(bytes); |
| 318 | + assertEquals(ByteString.EMPTY, realScalarBytesMap.getMapFieldOrThrow(42)); |
| 319 | + assertEquals(ByteString.EMPTY, realScalarBytesMap.getMapFieldOrThrow(0)); |
| 320 | + } |
| 321 | + |
| 322 | + public void testScalarMsgRepeatedFieldsToMapConversions() throws Exception { |
| 323 | + // According to https://protobuf.dev/programming-guides/proto3/#backwards repeated fields are |
| 324 | + // converted to maps by using the first field as the key and the second field as the value. |
| 325 | + // This also verifies that we can parse maps with missing fields by using default values. |
| 326 | + FakeScalarMsgMap fakeScalarMsgMap = |
| 327 | + FakeScalarMsgMap.newBuilder() |
| 328 | + .addMapField(FakeScalarMsgMapFieldEntry.newBuilder().setKey(777).build()) |
| 329 | + .addMapField( |
| 330 | + FakeScalarMsgMapFieldEntry.newBuilder() |
| 331 | + .setValue(RandomMessage.getDefaultInstance()) |
| 332 | + .build()) |
| 333 | + .build(); |
| 334 | + byte[] bytes = fakeScalarMsgMap.toByteArray(); |
| 335 | + RealScalarMsgMap realScalarMsgMap = RealScalarMsgMap.parseFrom(bytes); |
| 336 | + assertEquals(RandomMessage.getDefaultInstance(), realScalarMsgMap.getMapFieldOrThrow(777)); |
| 337 | + assertEquals(RandomMessage.getDefaultInstance(), realScalarMsgMap.getMapFieldOrThrow(0)); |
| 338 | + } |
| 339 | + |
| 340 | + public void testScalarEnumRepeatedFieldsToMapConversions() throws Exception { |
| 341 | + // According to https://protobuf.dev/programming-guides/proto3/#backwards repeated fields are |
| 342 | + // converted to maps by using the first field as the key and the second field as the value. |
| 343 | + // This also verifies that we can parse maps with missing fields by using default values. |
| 344 | + // Note that the default value for an enum is the first enum value. |
| 345 | + FakeScalarEnumMap fakeScalarEnumMap = |
| 346 | + FakeScalarEnumMap.newBuilder() |
| 347 | + .addMapField(FakeScalarEnumMapFieldEntry.newBuilder().setKey(123).build()) |
| 348 | + .addMapField( |
| 349 | + FakeScalarEnumMapFieldEntry.newBuilder() |
| 350 | + .setKey(456) |
| 351 | + .setValue(MapMsg.Color.YELLOW) |
| 352 | + .build()) |
| 353 | + .build(); |
| 354 | + byte[] bytes = fakeScalarEnumMap.toByteArray(); |
| 355 | + RealScalarEnumMap realScalarEnumMap = RealScalarEnumMap.parseFrom(bytes); |
| 356 | + assertEquals(MapMsg.Color.GREEN, realScalarEnumMap.getMapFieldOrThrow(123)); |
| 357 | + assertEquals(MapMsg.Color.YELLOW, realScalarEnumMap.getMapFieldOrThrow(456)); |
| 358 | + } |
| 359 | + |
| 360 | + public void testBadValueType() throws Exception { |
| 361 | + // Verifies that maps fail to parse if the value type is not the expected type. |
| 362 | + FakeScalarBytesMap fakeMap = |
| 363 | + FakeScalarBytesMap.newBuilder() |
| 364 | + .addMapField( |
| 365 | + FakeScalarBytesMapFieldEntry.newBuilder() |
| 366 | + .setKey(7) |
| 367 | + .setValue(ByteString.copyFromUtf8("hello")) |
| 368 | + .build()) |
| 369 | + .build(); |
| 370 | + byte[] bytes = fakeMap.toByteArray(); |
| 371 | + try { |
| 372 | + RealScalarMsgMap realMap = RealScalarMsgMap.parseFrom(bytes); |
| 373 | + fail("Expected exception instead of map: " + realMap); |
| 374 | + } catch (Exception e) { |
| 375 | + // Expected. |
| 376 | + } |
| 377 | + } |
| 378 | + |
| 379 | + // This test is disabled because the java runtime doesn't throw an exception when the key type is |
| 380 | + // not the expected type. Instead, it has undefined behavior with regards to what map you actually |
| 381 | + // get. The j2objc runtime throws an exception. |
| 382 | + public void disabledTestBadKeyType() throws Exception { |
| 383 | + // Verifies that maps fail to parse if the key type is not the expected type. |
| 384 | + FakeScalarBytesMap fakeMap = |
| 385 | + FakeScalarBytesMap.newBuilder() |
| 386 | + .addMapField( |
| 387 | + FakeScalarBytesMapFieldEntry.newBuilder() |
| 388 | + .setKey(7) |
| 389 | + .setValue(ByteString.copyFromUtf8("hello")) |
| 390 | + .build()) |
| 391 | + .build(); |
| 392 | + byte[] bytes = fakeMap.toByteArray(); |
| 393 | + try { |
| 394 | + RealStringBytesMap realMap = RealStringBytesMap.parseFrom(bytes); |
| 395 | + fail("Expected exception instead of map: " + realMap); |
| 396 | + } catch (Exception e) { |
| 397 | + // Expected. |
| 398 | + } |
| 399 | + } |
| 400 | + |
275 | 401 | public void testToString() throws Exception { |
276 | 402 | String result = getFilledMessage().toString(); |
277 | 403 | assertTrue(result.contains("int_int")); |
|
0 commit comments