1 // Protocol Buffers - Google's data interchange format 2 // Copyright 2008 Google Inc. All rights reserved. 3 // https://developers.google.com/protocol-buffers/ 4 // 5 // Redistribution and use in source and binary forms, with or without 6 // modification, are permitted provided that the following conditions are 7 // met: 8 // 9 // * Redistributions of source code must retain the above copyright 10 // notice, this list of conditions and the following disclaimer. 11 // * Redistributions in binary form must reproduce the above 12 // copyright notice, this list of conditions and the following disclaimer 13 // in the documentation and/or other materials provided with the 14 // distribution. 15 // * Neither the name of Google Inc. nor the names of its 16 // contributors may be used to endorse or promote products derived from 17 // this software without specific prior written permission. 18 // 19 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 20 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 21 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 22 // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 23 // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 24 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 25 // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 26 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 27 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 28 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 29 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 31 package com.google.protobuf; 32 33 import com.google.protobuf.Descriptors.Descriptor; 34 import com.google.protobuf.Descriptors.EnumDescriptor; 35 import com.google.protobuf.Descriptors.EnumValueDescriptor; 36 import com.google.protobuf.Descriptors.FieldDescriptor; 37 import map_test.MapTestProto.TestMap; 38 import map_test.MapTestProto.TestMap.MessageValue; 39 import map_test.MapTestProto.TestOnChangeEventPropagation; 40 import junit.framework.TestCase; 41 42 import java.util.ArrayList; 43 import java.util.Arrays; 44 import java.util.HashMap; 45 import java.util.List; 46 import java.util.Map; 47 48 /** 49 * Unit tests for map fields. 50 */ 51 public class MapTest extends TestCase { setMapValues(TestMap.Builder builder)52 private void setMapValues(TestMap.Builder builder) { 53 builder.getMutableInt32ToInt32Field().put(1, 11); 54 builder.getMutableInt32ToInt32Field().put(2, 22); 55 builder.getMutableInt32ToInt32Field().put(3, 33); 56 57 builder.getMutableInt32ToStringField().put(1, "11"); 58 builder.getMutableInt32ToStringField().put(2, "22"); 59 builder.getMutableInt32ToStringField().put(3, "33"); 60 61 builder.getMutableInt32ToBytesField().put(1, TestUtil.toBytes("11")); 62 builder.getMutableInt32ToBytesField().put(2, TestUtil.toBytes("22")); 63 builder.getMutableInt32ToBytesField().put(3, TestUtil.toBytes("33")); 64 65 builder.getMutableInt32ToEnumField().put(1, TestMap.EnumValue.FOO); 66 builder.getMutableInt32ToEnumField().put(2, TestMap.EnumValue.BAR); 67 builder.getMutableInt32ToEnumField().put(3, TestMap.EnumValue.BAZ); 68 69 builder.getMutableInt32ToMessageField().put( 70 1, MessageValue.newBuilder().setValue(11).build()); 71 builder.getMutableInt32ToMessageField().put( 72 2, MessageValue.newBuilder().setValue(22).build()); 73 builder.getMutableInt32ToMessageField().put( 74 3, MessageValue.newBuilder().setValue(33).build()); 75 76 builder.getMutableStringToInt32Field().put("1", 11); 77 builder.getMutableStringToInt32Field().put("2", 22); 78 builder.getMutableStringToInt32Field().put("3", 33); 79 } 80 copyMapValues(TestMap source, TestMap.Builder destination)81 private void copyMapValues(TestMap source, TestMap.Builder destination) { 82 destination 83 .putAllInt32ToInt32Field(source.getInt32ToInt32Field()) 84 .putAllInt32ToStringField(source.getInt32ToStringField()) 85 .putAllInt32ToBytesField(source.getInt32ToBytesField()) 86 .putAllInt32ToEnumField(source.getInt32ToEnumField()) 87 .putAllInt32ToMessageField(source.getInt32ToMessageField()) 88 .putAllStringToInt32Field(source.getStringToInt32Field()); 89 } 90 assertMapValuesSet(TestMap message)91 private void assertMapValuesSet(TestMap message) { 92 assertEquals(3, message.getInt32ToInt32Field().size()); 93 assertEquals(11, message.getInt32ToInt32Field().get(1).intValue()); 94 assertEquals(22, message.getInt32ToInt32Field().get(2).intValue()); 95 assertEquals(33, message.getInt32ToInt32Field().get(3).intValue()); 96 97 assertEquals(3, message.getInt32ToStringField().size()); 98 assertEquals("11", message.getInt32ToStringField().get(1)); 99 assertEquals("22", message.getInt32ToStringField().get(2)); 100 assertEquals("33", message.getInt32ToStringField().get(3)); 101 102 assertEquals(3, message.getInt32ToBytesField().size()); 103 assertEquals(TestUtil.toBytes("11"), message.getInt32ToBytesField().get(1)); 104 assertEquals(TestUtil.toBytes("22"), message.getInt32ToBytesField().get(2)); 105 assertEquals(TestUtil.toBytes("33"), message.getInt32ToBytesField().get(3)); 106 107 assertEquals(3, message.getInt32ToEnumField().size()); 108 assertEquals(TestMap.EnumValue.FOO, message.getInt32ToEnumField().get(1)); 109 assertEquals(TestMap.EnumValue.BAR, message.getInt32ToEnumField().get(2)); 110 assertEquals(TestMap.EnumValue.BAZ, message.getInt32ToEnumField().get(3)); 111 112 assertEquals(3, message.getInt32ToMessageField().size()); 113 assertEquals(11, message.getInt32ToMessageField().get(1).getValue()); 114 assertEquals(22, message.getInt32ToMessageField().get(2).getValue()); 115 assertEquals(33, message.getInt32ToMessageField().get(3).getValue()); 116 117 assertEquals(3, message.getStringToInt32Field().size()); 118 assertEquals(11, message.getStringToInt32Field().get("1").intValue()); 119 assertEquals(22, message.getStringToInt32Field().get("2").intValue()); 120 assertEquals(33, message.getStringToInt32Field().get("3").intValue()); 121 } 122 updateMapValues(TestMap.Builder builder)123 private void updateMapValues(TestMap.Builder builder) { 124 builder.getMutableInt32ToInt32Field().put(1, 111); 125 builder.getMutableInt32ToInt32Field().remove(2); 126 builder.getMutableInt32ToInt32Field().put(4, 44); 127 128 builder.getMutableInt32ToStringField().put(1, "111"); 129 builder.getMutableInt32ToStringField().remove(2); 130 builder.getMutableInt32ToStringField().put(4, "44"); 131 132 builder.getMutableInt32ToBytesField().put(1, TestUtil.toBytes("111")); 133 builder.getMutableInt32ToBytesField().remove(2); 134 builder.getMutableInt32ToBytesField().put(4, TestUtil.toBytes("44")); 135 136 builder.getMutableInt32ToEnumField().put(1, TestMap.EnumValue.BAR); 137 builder.getMutableInt32ToEnumField().remove(2); 138 builder.getMutableInt32ToEnumField().put(4, TestMap.EnumValue.QUX); 139 140 builder.getMutableInt32ToMessageField().put( 141 1, MessageValue.newBuilder().setValue(111).build()); 142 builder.getMutableInt32ToMessageField().remove(2); 143 builder.getMutableInt32ToMessageField().put( 144 4, MessageValue.newBuilder().setValue(44).build()); 145 146 builder.getMutableStringToInt32Field().put("1", 111); 147 builder.getMutableStringToInt32Field().remove("2"); 148 builder.getMutableStringToInt32Field().put("4", 44); 149 } 150 assertMapValuesUpdated(TestMap message)151 private void assertMapValuesUpdated(TestMap message) { 152 assertEquals(3, message.getInt32ToInt32Field().size()); 153 assertEquals(111, message.getInt32ToInt32Field().get(1).intValue()); 154 assertEquals(33, message.getInt32ToInt32Field().get(3).intValue()); 155 assertEquals(44, message.getInt32ToInt32Field().get(4).intValue()); 156 157 assertEquals(3, message.getInt32ToStringField().size()); 158 assertEquals("111", message.getInt32ToStringField().get(1)); 159 assertEquals("33", message.getInt32ToStringField().get(3)); 160 assertEquals("44", message.getInt32ToStringField().get(4)); 161 162 assertEquals(3, message.getInt32ToBytesField().size()); 163 assertEquals(TestUtil.toBytes("111"), message.getInt32ToBytesField().get(1)); 164 assertEquals(TestUtil.toBytes("33"), message.getInt32ToBytesField().get(3)); 165 assertEquals(TestUtil.toBytes("44"), message.getInt32ToBytesField().get(4)); 166 167 assertEquals(3, message.getInt32ToEnumField().size()); 168 assertEquals(TestMap.EnumValue.BAR, message.getInt32ToEnumField().get(1)); 169 assertEquals(TestMap.EnumValue.BAZ, message.getInt32ToEnumField().get(3)); 170 assertEquals(TestMap.EnumValue.QUX, message.getInt32ToEnumField().get(4)); 171 172 assertEquals(3, message.getInt32ToMessageField().size()); 173 assertEquals(111, message.getInt32ToMessageField().get(1).getValue()); 174 assertEquals(33, message.getInt32ToMessageField().get(3).getValue()); 175 assertEquals(44, message.getInt32ToMessageField().get(4).getValue()); 176 177 assertEquals(3, message.getStringToInt32Field().size()); 178 assertEquals(111, message.getStringToInt32Field().get("1").intValue()); 179 assertEquals(33, message.getStringToInt32Field().get("3").intValue()); 180 assertEquals(44, message.getStringToInt32Field().get("4").intValue()); 181 } 182 assertMapValuesCleared(TestMap message)183 private void assertMapValuesCleared(TestMap message) { 184 assertEquals(0, message.getInt32ToInt32Field().size()); 185 assertEquals(0, message.getInt32ToStringField().size()); 186 assertEquals(0, message.getInt32ToBytesField().size()); 187 assertEquals(0, message.getInt32ToEnumField().size()); 188 assertEquals(0, message.getInt32ToMessageField().size()); 189 assertEquals(0, message.getStringToInt32Field().size()); 190 } 191 testMutableMapLifecycle()192 public void testMutableMapLifecycle() { 193 TestMap.Builder builder = TestMap.newBuilder(); 194 Map<Integer, Integer> intMap = builder.getMutableInt32ToInt32Field(); 195 intMap.put(1, 2); 196 assertEquals(newMap(1, 2), builder.build().getInt32ToInt32Field()); 197 try { 198 intMap.put(2, 3); 199 fail(); 200 } catch (UnsupportedOperationException e) { 201 // expected 202 } 203 assertEquals(newMap(1, 2), builder.getInt32ToInt32Field()); 204 builder.getMutableInt32ToInt32Field().put(2, 3); 205 assertEquals(newMap(1, 2, 2, 3), builder.getInt32ToInt32Field()); 206 207 Map<Integer, TestMap.EnumValue> enumMap = builder.getMutableInt32ToEnumField(); 208 enumMap.put(1, TestMap.EnumValue.BAR); 209 assertEquals(newMap(1, TestMap.EnumValue.BAR), builder.build().getInt32ToEnumField()); 210 try { 211 enumMap.put(2, TestMap.EnumValue.FOO); 212 fail(); 213 } catch (UnsupportedOperationException e) { 214 // expected 215 } 216 assertEquals(newMap(1, TestMap.EnumValue.BAR), builder.getInt32ToEnumField()); 217 builder.getMutableInt32ToEnumField().put(2, TestMap.EnumValue.FOO); 218 assertEquals( 219 newMap(1, TestMap.EnumValue.BAR, 2, TestMap.EnumValue.FOO), 220 builder.getInt32ToEnumField()); 221 222 Map<Integer, String> stringMap = builder.getMutableInt32ToStringField(); 223 stringMap.put(1, "1"); 224 assertEquals(newMap(1, "1"), builder.build().getInt32ToStringField()); 225 try { 226 stringMap.put(2, "2"); 227 fail(); 228 } catch (UnsupportedOperationException e) { 229 // expected 230 } 231 assertEquals(newMap(1, "1"), builder.getInt32ToStringField()); 232 builder.getMutableInt32ToStringField().put(2, "2"); 233 assertEquals( 234 newMap(1, "1", 2, "2"), 235 builder.getInt32ToStringField()); 236 237 Map<Integer, TestMap.MessageValue> messageMap = builder.getMutableInt32ToMessageField(); 238 messageMap.put(1, TestMap.MessageValue.getDefaultInstance()); 239 assertEquals(newMap(1, TestMap.MessageValue.getDefaultInstance()), 240 builder.build().getInt32ToMessageField()); 241 try { 242 messageMap.put(2, TestMap.MessageValue.getDefaultInstance()); 243 fail(); 244 } catch (UnsupportedOperationException e) { 245 // expected 246 } 247 assertEquals(newMap(1, TestMap.MessageValue.getDefaultInstance()), 248 builder.getInt32ToMessageField()); 249 builder.getMutableInt32ToMessageField().put(2, TestMap.MessageValue.getDefaultInstance()); 250 assertEquals( 251 newMap(1, TestMap.MessageValue.getDefaultInstance(), 252 2, TestMap.MessageValue.getDefaultInstance()), 253 builder.getInt32ToMessageField()); 254 } 255 testMutableMapLifecycle_collections()256 public void testMutableMapLifecycle_collections() { 257 TestMap.Builder builder = TestMap.newBuilder(); 258 Map<Integer, Integer> intMap = builder.getMutableInt32ToInt32Field(); 259 intMap.put(1, 2); 260 assertEquals(newMap(1, 2), builder.build().getInt32ToInt32Field()); 261 try { 262 intMap.remove(2); 263 fail(); 264 } catch (UnsupportedOperationException e) { 265 // expected 266 } 267 try { 268 intMap.entrySet().remove(new Object()); 269 fail(); 270 } catch (UnsupportedOperationException e) { 271 // expected 272 } 273 try { 274 intMap.entrySet().iterator().remove(); 275 fail(); 276 } catch (UnsupportedOperationException e) { 277 // expected 278 } 279 try { 280 intMap.keySet().remove(new Object()); 281 fail(); 282 } catch (UnsupportedOperationException e) { 283 // expected 284 } 285 try { 286 intMap.values().remove(new Object()); 287 fail(); 288 } catch (UnsupportedOperationException e) { 289 // expected 290 } 291 try { 292 intMap.values().iterator().remove(); 293 fail(); 294 } catch (UnsupportedOperationException e) { 295 // expected 296 } 297 assertEquals(newMap(1, 2), intMap); 298 assertEquals(newMap(1, 2), builder.getInt32ToInt32Field()); 299 assertEquals(newMap(1, 2), builder.build().getInt32ToInt32Field()); 300 } 301 testGettersAndSetters()302 public void testGettersAndSetters() throws Exception { 303 TestMap.Builder builder = TestMap.newBuilder(); 304 TestMap message = builder.build(); 305 assertMapValuesCleared(message); 306 307 builder = message.toBuilder(); 308 setMapValues(builder); 309 message = builder.build(); 310 assertMapValuesSet(message); 311 312 builder = message.toBuilder(); 313 updateMapValues(builder); 314 message = builder.build(); 315 assertMapValuesUpdated(message); 316 317 builder = message.toBuilder(); 318 builder.clear(); 319 message = builder.build(); 320 assertMapValuesCleared(message); 321 } 322 testPutAll()323 public void testPutAll() throws Exception { 324 TestMap.Builder sourceBuilder = TestMap.newBuilder(); 325 setMapValues(sourceBuilder); 326 TestMap source = sourceBuilder.build(); 327 328 TestMap.Builder destination = TestMap.newBuilder(); 329 copyMapValues(source, destination); 330 assertMapValuesSet(destination.build()); 331 } 332 testPutAllForUnknownEnumValues()333 public void testPutAllForUnknownEnumValues() throws Exception { 334 TestMap.Builder sourceBuilder = TestMap.newBuilder(); 335 sourceBuilder.getMutableInt32ToEnumFieldValue().put(0, 0); 336 sourceBuilder.getMutableInt32ToEnumFieldValue().put(1, 1); 337 sourceBuilder.getMutableInt32ToEnumFieldValue().put(2, 1000); // unknown value. 338 TestMap source = sourceBuilder.build(); 339 340 TestMap.Builder destinationBuilder = TestMap.newBuilder(); 341 destinationBuilder.putAllInt32ToEnumFieldValue(source.getInt32ToEnumFieldValue()); 342 TestMap destination = destinationBuilder.build(); 343 344 assertEquals(0, destination.getInt32ToEnumFieldValue().get(0).intValue()); 345 assertEquals(1, destination.getInt32ToEnumFieldValue().get(1).intValue()); 346 assertEquals(1000, destination.getInt32ToEnumFieldValue().get(2).intValue()); 347 } 348 testSerializeAndParse()349 public void testSerializeAndParse() throws Exception { 350 TestMap.Builder builder = TestMap.newBuilder(); 351 setMapValues(builder); 352 TestMap message = builder.build(); 353 assertEquals(message.getSerializedSize(), message.toByteString().size()); 354 message = TestMap.parser().parseFrom(message.toByteString()); 355 assertMapValuesSet(message); 356 357 builder = message.toBuilder(); 358 updateMapValues(builder); 359 message = builder.build(); 360 assertEquals(message.getSerializedSize(), message.toByteString().size()); 361 message = TestMap.parser().parseFrom(message.toByteString()); 362 assertMapValuesUpdated(message); 363 364 builder = message.toBuilder(); 365 builder.clear(); 366 message = builder.build(); 367 assertEquals(message.getSerializedSize(), message.toByteString().size()); 368 message = TestMap.parser().parseFrom(message.toByteString()); 369 assertMapValuesCleared(message); 370 } 371 testMergeFrom()372 public void testMergeFrom() throws Exception { 373 TestMap.Builder builder = TestMap.newBuilder(); 374 setMapValues(builder); 375 TestMap message = builder.build(); 376 377 TestMap.Builder other = TestMap.newBuilder(); 378 other.mergeFrom(message); 379 assertMapValuesSet(other.build()); 380 } 381 testEqualsAndHashCode()382 public void testEqualsAndHashCode() throws Exception { 383 // Test that generated equals() and hashCode() will disregard the order 384 // of map entries when comparing/hashing map fields. 385 386 // We can't control the order of elements in a HashMap. The best we can do 387 // here is to add elements in different order. 388 TestMap.Builder b1 = TestMap.newBuilder(); 389 b1.getMutableInt32ToInt32Field().put(1, 2); 390 b1.getMutableInt32ToInt32Field().put(3, 4); 391 b1.getMutableInt32ToInt32Field().put(5, 6); 392 TestMap m1 = b1.build(); 393 394 TestMap.Builder b2 = TestMap.newBuilder(); 395 b2.getMutableInt32ToInt32Field().put(5, 6); 396 b2.getMutableInt32ToInt32Field().put(1, 2); 397 b2.getMutableInt32ToInt32Field().put(3, 4); 398 TestMap m2 = b2.build(); 399 400 assertEquals(m1, m2); 401 assertEquals(m1.hashCode(), m2.hashCode()); 402 403 // Make sure we did compare map fields. 404 b2.getMutableInt32ToInt32Field().put(1, 0); 405 m2 = b2.build(); 406 assertFalse(m1.equals(m2)); 407 // Don't check m1.hashCode() != m2.hashCode() because it's not guaranteed 408 // to be different. 409 410 // Regression test for b/18549190: if a map is a subset of the other map, 411 // equals() should return false. 412 b2.getMutableInt32ToInt32Field().remove(1); 413 m2 = b2.build(); 414 assertFalse(m1.equals(m2)); 415 assertFalse(m2.equals(m1)); 416 } 417 testNestedBuilderOnChangeEventPropagation()418 public void testNestedBuilderOnChangeEventPropagation() { 419 TestOnChangeEventPropagation.Builder parent = 420 TestOnChangeEventPropagation.newBuilder(); 421 parent.getOptionalMessageBuilder().getMutableInt32ToInt32Field().put(1, 2); 422 TestOnChangeEventPropagation message = parent.build(); 423 assertEquals(2, message.getOptionalMessage().getInt32ToInt32Field().get(1).intValue()); 424 425 // Make a change using nested builder. 426 parent.getOptionalMessageBuilder().getMutableInt32ToInt32Field().put(1, 3); 427 428 // Should be able to observe the change. 429 message = parent.build(); 430 assertEquals(3, message.getOptionalMessage().getInt32ToInt32Field().get(1).intValue()); 431 432 // Make another change using mergeFrom() 433 TestMap.Builder other = TestMap.newBuilder(); 434 other.getMutableInt32ToInt32Field().put(1, 4); 435 parent.getOptionalMessageBuilder().mergeFrom(other.build()); 436 437 // Should be able to observe the change. 438 message = parent.build(); 439 assertEquals(4, message.getOptionalMessage().getInt32ToInt32Field().get(1).intValue()); 440 441 // Make yet another change by clearing the nested builder. 442 parent.getOptionalMessageBuilder().clear(); 443 444 // Should be able to observe the change. 445 message = parent.build(); 446 assertEquals(0, message.getOptionalMessage().getInt32ToInt32Field().size()); 447 } 448 testNestedBuilderOnChangeEventPropagationReflection()449 public void testNestedBuilderOnChangeEventPropagationReflection() { 450 FieldDescriptor intMapField = f("int32_to_int32_field"); 451 // Create an outer message builder with nested builder. 452 TestOnChangeEventPropagation.Builder parentBuilder = 453 TestOnChangeEventPropagation.newBuilder(); 454 TestMap.Builder testMapBuilder = parentBuilder.getOptionalMessageBuilder(); 455 456 // Create a map entry message. 457 TestMap.Builder entryBuilder = TestMap.newBuilder(); 458 entryBuilder.getMutableInt32ToInt32Field().put(1, 1); 459 460 // Put the entry into the nested builder. 461 testMapBuilder.addRepeatedField( 462 intMapField, entryBuilder.getRepeatedField(intMapField, 0)); 463 464 // Should be able to observe the change. 465 TestOnChangeEventPropagation message = parentBuilder.build(); 466 assertEquals(1, message.getOptionalMessage().getInt32ToInt32Field().size()); 467 468 // Change the entry value. 469 entryBuilder.getMutableInt32ToInt32Field().put(1, 4); 470 testMapBuilder = parentBuilder.getOptionalMessageBuilder(); 471 testMapBuilder.setRepeatedField( 472 intMapField, 0, entryBuilder.getRepeatedField(intMapField, 0)); 473 474 // Should be able to observe the change. 475 message = parentBuilder.build(); 476 assertEquals(4, 477 message.getOptionalMessage().getInt32ToInt32Field().get(1).intValue()); 478 479 // Clear the nested builder. 480 testMapBuilder = parentBuilder.getOptionalMessageBuilder(); 481 testMapBuilder.clearField(intMapField); 482 483 // Should be able to observe the change. 484 message = parentBuilder.build(); 485 assertEquals(0, message.getOptionalMessage().getInt32ToInt32Field().size()); 486 } 487 488 // The following methods are used to test reflection API. 489 f(String name)490 private static FieldDescriptor f(String name) { 491 return TestMap.getDescriptor().findFieldByName(name); 492 } 493 getFieldValue(Message mapEntry, String name)494 private static Object getFieldValue(Message mapEntry, String name) { 495 FieldDescriptor field = mapEntry.getDescriptorForType().findFieldByName(name); 496 return mapEntry.getField(field); 497 } 498 setFieldValue( Message.Builder mapEntry, String name, Object value)499 private static Message.Builder setFieldValue( 500 Message.Builder mapEntry, String name, Object value) { 501 FieldDescriptor field = mapEntry.getDescriptorForType().findFieldByName(name); 502 mapEntry.setField(field, value); 503 return mapEntry; 504 } 505 assertHasMapValues(Message message, String name, Map<?, ?> values)506 private static void assertHasMapValues(Message message, String name, Map<?, ?> values) { 507 FieldDescriptor field = f(name); 508 for (Object entry : (List<?>) message.getField(field)) { 509 Message mapEntry = (Message) entry; 510 Object key = getFieldValue(mapEntry, "key"); 511 Object value = getFieldValue(mapEntry, "value"); 512 assertTrue(values.containsKey(key)); 513 assertEquals(value, values.get(key)); 514 } 515 assertEquals(values.size(), message.getRepeatedFieldCount(field)); 516 for (int i = 0; i < message.getRepeatedFieldCount(field); i++) { 517 Message mapEntry = (Message) message.getRepeatedField(field, i); 518 Object key = getFieldValue(mapEntry, "key"); 519 Object value = getFieldValue(mapEntry, "value"); 520 assertTrue(values.containsKey(key)); 521 assertEquals(value, values.get(key)); 522 } 523 } 524 525 private static <KeyType, ValueType> newMapEntry(Message.Builder builder, String name, KeyType key, ValueType value)526 Message newMapEntry(Message.Builder builder, String name, KeyType key, ValueType value) { 527 FieldDescriptor field = builder.getDescriptorForType().findFieldByName(name); 528 Message.Builder entryBuilder = builder.newBuilderForField(field); 529 FieldDescriptor keyField = entryBuilder.getDescriptorForType().findFieldByName("key"); 530 FieldDescriptor valueField = entryBuilder.getDescriptorForType().findFieldByName("value"); 531 entryBuilder.setField(keyField, key); 532 entryBuilder.setField(valueField, value); 533 return entryBuilder.build(); 534 } 535 setMapValues(Message.Builder builder, String name, Map<?, ?> values)536 private static void setMapValues(Message.Builder builder, String name, Map<?, ?> values) { 537 List<Message> entryList = new ArrayList<Message>(); 538 for (Map.Entry<?, ?> entry : values.entrySet()) { 539 entryList.add(newMapEntry(builder, name, entry.getKey(), entry.getValue())); 540 } 541 FieldDescriptor field = builder.getDescriptorForType().findFieldByName(name); 542 builder.setField(field, entryList); 543 } 544 545 private static <KeyType, ValueType> mapForValues( KeyType key1, ValueType value1, KeyType key2, ValueType value2)546 Map<KeyType, ValueType> mapForValues( 547 KeyType key1, ValueType value1, KeyType key2, ValueType value2) { 548 Map<KeyType, ValueType> map = new HashMap<KeyType, ValueType>(); 549 map.put(key1, value1); 550 map.put(key2, value2); 551 return map; 552 } 553 testReflectionApi()554 public void testReflectionApi() throws Exception { 555 // In reflection API, map fields are just repeated message fields. 556 TestMap.Builder builder = TestMap.newBuilder(); 557 builder.getMutableInt32ToInt32Field().put(1, 2); 558 builder.getMutableInt32ToInt32Field().put(3, 4); 559 builder.getMutableInt32ToMessageField().put( 560 11, MessageValue.newBuilder().setValue(22).build()); 561 builder.getMutableInt32ToMessageField().put( 562 33, MessageValue.newBuilder().setValue(44).build()); 563 TestMap message = builder.build(); 564 565 // Test getField(), getRepeatedFieldCount(), getRepeatedField(). 566 assertHasMapValues(message, "int32_to_int32_field", 567 mapForValues(1, 2, 3, 4)); 568 assertHasMapValues(message, "int32_to_message_field", 569 mapForValues( 570 11, MessageValue.newBuilder().setValue(22).build(), 571 33, MessageValue.newBuilder().setValue(44).build())); 572 573 // Test clearField() 574 builder.clearField(f("int32_to_int32_field")); 575 builder.clearField(f("int32_to_message_field")); 576 message = builder.build(); 577 assertEquals(0, message.getInt32ToInt32Field().size()); 578 assertEquals(0, message.getInt32ToMessageField().size()); 579 580 // Test setField() 581 setMapValues(builder, "int32_to_int32_field", 582 mapForValues(11, 22, 33, 44)); 583 setMapValues(builder, "int32_to_message_field", 584 mapForValues( 585 111, MessageValue.newBuilder().setValue(222).build(), 586 333, MessageValue.newBuilder().setValue(444).build())); 587 message = builder.build(); 588 assertEquals(22, message.getInt32ToInt32Field().get(11).intValue()); 589 assertEquals(44, message.getInt32ToInt32Field().get(33).intValue()); 590 assertEquals(222, message.getInt32ToMessageField().get(111).getValue()); 591 assertEquals(444, message.getInt32ToMessageField().get(333).getValue()); 592 593 // Test addRepeatedField 594 builder.addRepeatedField(f("int32_to_int32_field"), 595 newMapEntry(builder, "int32_to_int32_field", 55, 66)); 596 builder.addRepeatedField(f("int32_to_message_field"), 597 newMapEntry(builder, "int32_to_message_field", 555, 598 MessageValue.newBuilder().setValue(666).build())); 599 message = builder.build(); 600 assertEquals(66, message.getInt32ToInt32Field().get(55).intValue()); 601 assertEquals(666, message.getInt32ToMessageField().get(555).getValue()); 602 603 // Test addRepeatedField (overriding existing values) 604 builder.addRepeatedField(f("int32_to_int32_field"), 605 newMapEntry(builder, "int32_to_int32_field", 55, 55)); 606 builder.addRepeatedField(f("int32_to_message_field"), 607 newMapEntry(builder, "int32_to_message_field", 555, 608 MessageValue.newBuilder().setValue(555).build())); 609 message = builder.build(); 610 assertEquals(55, message.getInt32ToInt32Field().get(55).intValue()); 611 assertEquals(555, message.getInt32ToMessageField().get(555).getValue()); 612 613 // Test setRepeatedField 614 for (int i = 0; i < builder.getRepeatedFieldCount(f("int32_to_int32_field")); i++) { 615 Message mapEntry = (Message) builder.getRepeatedField(f("int32_to_int32_field"), i); 616 int oldKey = ((Integer) getFieldValue(mapEntry, "key")).intValue(); 617 int oldValue = ((Integer) getFieldValue(mapEntry, "value")).intValue(); 618 // Swap key with value for each entry. 619 Message.Builder mapEntryBuilder = mapEntry.toBuilder(); 620 setFieldValue(mapEntryBuilder, "key", oldValue); 621 setFieldValue(mapEntryBuilder, "value", oldKey); 622 builder.setRepeatedField(f("int32_to_int32_field"), i, mapEntryBuilder.build()); 623 } 624 message = builder.build(); 625 assertEquals(11, message.getInt32ToInt32Field().get(22).intValue()); 626 assertEquals(33, message.getInt32ToInt32Field().get(44).intValue()); 627 assertEquals(55, message.getInt32ToInt32Field().get(55).intValue()); 628 } 629 testTextFormat()630 public void testTextFormat() throws Exception { 631 TestMap.Builder builder = TestMap.newBuilder(); 632 setMapValues(builder); 633 TestMap message = builder.build(); 634 635 String textData = TextFormat.printToString(message); 636 637 builder = TestMap.newBuilder(); 638 TextFormat.merge(textData, builder); 639 message = builder.build(); 640 641 assertMapValuesSet(message); 642 } 643 testDynamicMessage()644 public void testDynamicMessage() throws Exception { 645 TestMap.Builder builder = TestMap.newBuilder(); 646 setMapValues(builder); 647 TestMap message = builder.build(); 648 649 Message dynamicDefaultInstance = 650 DynamicMessage.getDefaultInstance(TestMap.getDescriptor()); 651 Message dynamicMessage = dynamicDefaultInstance 652 .newBuilderForType().mergeFrom(message.toByteString()).build(); 653 654 assertEquals(message, dynamicMessage); 655 assertEquals(message.hashCode(), dynamicMessage.hashCode()); 656 } 657 testReflectionEqualsAndHashCode()658 public void testReflectionEqualsAndHashCode() throws Exception { 659 // Test that generated equals() and hashCode() will disregard the order 660 // of map entries when comparing/hashing map fields. 661 662 // We use DynamicMessage to test reflection based equals()/hashCode(). 663 Message dynamicDefaultInstance = 664 DynamicMessage.getDefaultInstance(TestMap.getDescriptor()); 665 FieldDescriptor field = f("int32_to_int32_field"); 666 667 Message.Builder b1 = dynamicDefaultInstance.newBuilderForType(); 668 b1.addRepeatedField(field, newMapEntry(b1, "int32_to_int32_field", 1, 2)); 669 b1.addRepeatedField(field, newMapEntry(b1, "int32_to_int32_field", 3, 4)); 670 b1.addRepeatedField(field, newMapEntry(b1, "int32_to_int32_field", 5, 6)); 671 Message m1 = b1.build(); 672 673 Message.Builder b2 = dynamicDefaultInstance.newBuilderForType(); 674 b2.addRepeatedField(field, newMapEntry(b2, "int32_to_int32_field", 5, 6)); 675 b2.addRepeatedField(field, newMapEntry(b2, "int32_to_int32_field", 1, 2)); 676 b2.addRepeatedField(field, newMapEntry(b2, "int32_to_int32_field", 3, 4)); 677 Message m2 = b2.build(); 678 679 assertEquals(m1, m2); 680 assertEquals(m1.hashCode(), m2.hashCode()); 681 682 // Make sure we did compare map fields. 683 b2.setRepeatedField(field, 0, newMapEntry(b1, "int32_to_int32_field", 0, 0)); 684 m2 = b2.build(); 685 assertFalse(m1.equals(m2)); 686 // Don't check m1.hashCode() != m2.hashCode() because it's not guaranteed 687 // to be different. 688 } 689 testUnknownEnumValues()690 public void testUnknownEnumValues() throws Exception { 691 TestMap.Builder builder = TestMap.newBuilder(); 692 builder.getMutableInt32ToEnumFieldValue().put(0, 0); 693 builder.getMutableInt32ToEnumFieldValue().put(1, 1); 694 builder.getMutableInt32ToEnumFieldValue().put(2, 1000); // unknown value. 695 TestMap message = builder.build(); 696 697 assertEquals(TestMap.EnumValue.FOO, 698 message.getInt32ToEnumField().get(0)); 699 assertEquals(TestMap.EnumValue.BAR, 700 message.getInt32ToEnumField().get(1)); 701 assertEquals(TestMap.EnumValue.UNRECOGNIZED, 702 message.getInt32ToEnumField().get(2)); 703 assertEquals(1000, message.getInt32ToEnumFieldValue().get(2).intValue()); 704 705 // Unknown enum values should be preserved after: 706 // 1. Serialization and parsing. 707 // 2. toBuild(). 708 // 3. mergeFrom(). 709 message = TestMap.parseFrom(message.toByteString()); 710 assertEquals(1000, message.getInt32ToEnumFieldValue().get(2).intValue()); 711 builder = message.toBuilder(); 712 assertEquals(1000, builder.getInt32ToEnumFieldValue().get(2).intValue()); 713 builder = TestMap.newBuilder().mergeFrom(message); 714 assertEquals(1000, builder.getInt32ToEnumFieldValue().get(2).intValue()); 715 716 // hashCode()/equals() should take unknown enum values into account. 717 builder.getMutableInt32ToEnumFieldValue().put(2, 1001); 718 TestMap message2 = builder.build(); 719 assertFalse(message.hashCode() == message2.hashCode()); 720 assertFalse(message.equals(message2)); 721 // Unknown values will be converted to UNRECOGNIZED so the resulted enum map 722 // should be the same. 723 assertTrue(message.getInt32ToEnumField().equals(message2.getInt32ToEnumField())); 724 } 725 testUnknownEnumValuesInReflectionApi()726 public void testUnknownEnumValuesInReflectionApi() throws Exception { 727 Descriptor descriptor = TestMap.getDescriptor(); 728 EnumDescriptor enumDescriptor = TestMap.EnumValue.getDescriptor(); 729 FieldDescriptor field = descriptor.findFieldByName("int32_to_enum_field"); 730 731 Map<Integer, Integer> data = new HashMap<Integer, Integer>(); 732 data.put(0, 0); 733 data.put(1, 1); 734 data.put(2, 1000); // unknown value. 735 736 TestMap.Builder builder = TestMap.newBuilder(); 737 for (Map.Entry<Integer, Integer> entry : data.entrySet()) { 738 builder.getMutableInt32ToEnumFieldValue().put(entry.getKey(), entry.getValue()); 739 } 740 741 // Try to read unknown enum values using reflection API. 742 for (int i = 0; i < builder.getRepeatedFieldCount(field); i++) { 743 Message mapEntry = (Message) builder.getRepeatedField(field, i); 744 int key = ((Integer) getFieldValue(mapEntry, "key")).intValue(); 745 int value = ((EnumValueDescriptor) getFieldValue(mapEntry, "value")).getNumber(); 746 assertEquals(data.get(key).intValue(), value); 747 Message.Builder mapEntryBuilder = mapEntry.toBuilder(); 748 // Increase the value by 1. 749 setFieldValue(mapEntryBuilder, "value", 750 enumDescriptor.findValueByNumberCreatingIfUnknown(value + 1)); 751 builder.setRepeatedField(field, i, mapEntryBuilder.build()); 752 } 753 754 // Verify that enum values have been successfully updated. 755 TestMap message = builder.build(); 756 for (Map.Entry<Integer, Integer> entry : message.getInt32ToEnumFieldValue().entrySet()) { 757 assertEquals(data.get(entry.getKey()) + 1, entry.getValue().intValue()); 758 } 759 } 760 testIterationOrder()761 public void testIterationOrder() throws Exception { 762 TestMap.Builder builder = TestMap.newBuilder(); 763 setMapValues(builder); 764 TestMap message = builder.build(); 765 766 assertEquals(Arrays.asList("1", "2", "3"), 767 new ArrayList<String>(message.getStringToInt32Field().keySet())); 768 } 769 newMap(K key1, V value1)770 private static <K, V> Map<K, V> newMap(K key1, V value1) { 771 Map<K, V> map = new HashMap<K, V>(); 772 map.put(key1, value1); 773 return map; 774 } 775 newMap(K key1, V value1, K key2, V value2)776 private static <K, V> Map<K, V> newMap(K key1, V value1, K key2, V value2) { 777 Map<K, V> map = new HashMap<K, V>(); 778 map.put(key1, value1); 779 map.put(key2, value2); 780 return map; 781 } 782 } 783