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 map_lite_test.MapForProto2TestProto.TestMap;
34 import map_lite_test.MapForProto2TestProto.TestMap.MessageValue;
35 import map_lite_test.MapForProto2TestProto.TestUnknownEnumValue;
36 
37 import junit.framework.TestCase;
38 
39 import java.util.ArrayList;
40 import java.util.Arrays;
41 import java.util.HashMap;
42 import java.util.Map;
43 
44 /**
45  * Unit tests for map fields.
46  */
47 public class MapForProto2LiteTest extends TestCase {
setMapValues(TestMap.Builder builder)48   private void setMapValues(TestMap.Builder builder) {
49     builder.getMutableInt32ToInt32Field().put(1, 11);
50     builder.getMutableInt32ToInt32Field().put(2, 22);
51     builder.getMutableInt32ToInt32Field().put(3, 33);
52 
53     builder.getMutableInt32ToStringField().put(1, "11");
54     builder.getMutableInt32ToStringField().put(2, "22");
55     builder.getMutableInt32ToStringField().put(3, "33");
56 
57     builder.getMutableInt32ToBytesField().put(1, TestUtil.toBytes("11"));
58     builder.getMutableInt32ToBytesField().put(2, TestUtil.toBytes("22"));
59     builder.getMutableInt32ToBytesField().put(3, TestUtil.toBytes("33"));
60 
61     builder.getMutableInt32ToEnumField().put(1, TestMap.EnumValue.FOO);
62     builder.getMutableInt32ToEnumField().put(2, TestMap.EnumValue.BAR);
63     builder.getMutableInt32ToEnumField().put(3, TestMap.EnumValue.BAZ);
64 
65     builder.getMutableInt32ToMessageField().put(
66         1, MessageValue.newBuilder().setValue(11).build());
67     builder.getMutableInt32ToMessageField().put(
68         2, MessageValue.newBuilder().setValue(22).build());
69     builder.getMutableInt32ToMessageField().put(
70         3, MessageValue.newBuilder().setValue(33).build());
71 
72     builder.getMutableStringToInt32Field().put("1", 11);
73     builder.getMutableStringToInt32Field().put("2", 22);
74     builder.getMutableStringToInt32Field().put("3", 33);
75   }
76 
copyMapValues(TestMap source, TestMap.Builder destination)77   private void copyMapValues(TestMap source, TestMap.Builder destination) {
78     destination
79         .putAllInt32ToInt32Field(source.getInt32ToInt32Field())
80         .putAllInt32ToStringField(source.getInt32ToStringField())
81         .putAllInt32ToBytesField(source.getInt32ToBytesField())
82         .putAllInt32ToEnumField(source.getInt32ToEnumField())
83         .putAllInt32ToMessageField(source.getInt32ToMessageField())
84         .putAllStringToInt32Field(source.getStringToInt32Field());
85   }
86 
assertMapValuesSet(TestMap message)87   private void assertMapValuesSet(TestMap message) {
88     assertEquals(3, message.getInt32ToInt32Field().size());
89     assertEquals(11, message.getInt32ToInt32Field().get(1).intValue());
90     assertEquals(22, message.getInt32ToInt32Field().get(2).intValue());
91     assertEquals(33, message.getInt32ToInt32Field().get(3).intValue());
92 
93     assertEquals(3, message.getInt32ToStringField().size());
94     assertEquals("11", message.getInt32ToStringField().get(1));
95     assertEquals("22", message.getInt32ToStringField().get(2));
96     assertEquals("33", message.getInt32ToStringField().get(3));
97 
98     assertEquals(3, message.getInt32ToBytesField().size());
99     assertEquals(TestUtil.toBytes("11"), message.getInt32ToBytesField().get(1));
100     assertEquals(TestUtil.toBytes("22"), message.getInt32ToBytesField().get(2));
101     assertEquals(TestUtil.toBytes("33"), message.getInt32ToBytesField().get(3));
102 
103     assertEquals(3, message.getInt32ToEnumField().size());
104     assertEquals(TestMap.EnumValue.FOO, message.getInt32ToEnumField().get(1));
105     assertEquals(TestMap.EnumValue.BAR, message.getInt32ToEnumField().get(2));
106     assertEquals(TestMap.EnumValue.BAZ, message.getInt32ToEnumField().get(3));
107 
108     assertEquals(3, message.getInt32ToMessageField().size());
109     assertEquals(11, message.getInt32ToMessageField().get(1).getValue());
110     assertEquals(22, message.getInt32ToMessageField().get(2).getValue());
111     assertEquals(33, message.getInt32ToMessageField().get(3).getValue());
112 
113     assertEquals(3, message.getStringToInt32Field().size());
114     assertEquals(11, message.getStringToInt32Field().get("1").intValue());
115     assertEquals(22, message.getStringToInt32Field().get("2").intValue());
116     assertEquals(33, message.getStringToInt32Field().get("3").intValue());
117   }
118 
updateMapValues(TestMap.Builder builder)119   private void updateMapValues(TestMap.Builder builder) {
120     builder.getMutableInt32ToInt32Field().put(1, 111);
121     builder.getMutableInt32ToInt32Field().remove(2);
122     builder.getMutableInt32ToInt32Field().put(4, 44);
123 
124     builder.getMutableInt32ToStringField().put(1, "111");
125     builder.getMutableInt32ToStringField().remove(2);
126     builder.getMutableInt32ToStringField().put(4, "44");
127 
128     builder.getMutableInt32ToBytesField().put(1, TestUtil.toBytes("111"));
129     builder.getMutableInt32ToBytesField().remove(2);
130     builder.getMutableInt32ToBytesField().put(4, TestUtil.toBytes("44"));
131 
132     builder.getMutableInt32ToEnumField().put(1, TestMap.EnumValue.BAR);
133     builder.getMutableInt32ToEnumField().remove(2);
134     builder.getMutableInt32ToEnumField().put(4, TestMap.EnumValue.QUX);
135 
136     builder.getMutableInt32ToMessageField().put(
137         1, MessageValue.newBuilder().setValue(111).build());
138     builder.getMutableInt32ToMessageField().remove(2);
139     builder.getMutableInt32ToMessageField().put(
140         4, MessageValue.newBuilder().setValue(44).build());
141 
142     builder.getMutableStringToInt32Field().put("1", 111);
143     builder.getMutableStringToInt32Field().remove("2");
144     builder.getMutableStringToInt32Field().put("4", 44);
145   }
146 
assertMapValuesUpdated(TestMap message)147   private void assertMapValuesUpdated(TestMap message) {
148     assertEquals(3, message.getInt32ToInt32Field().size());
149     assertEquals(111, message.getInt32ToInt32Field().get(1).intValue());
150     assertEquals(33, message.getInt32ToInt32Field().get(3).intValue());
151     assertEquals(44, message.getInt32ToInt32Field().get(4).intValue());
152 
153     assertEquals(3, message.getInt32ToStringField().size());
154     assertEquals("111", message.getInt32ToStringField().get(1));
155     assertEquals("33", message.getInt32ToStringField().get(3));
156     assertEquals("44", message.getInt32ToStringField().get(4));
157 
158     assertEquals(3, message.getInt32ToBytesField().size());
159     assertEquals(TestUtil.toBytes("111"), message.getInt32ToBytesField().get(1));
160     assertEquals(TestUtil.toBytes("33"), message.getInt32ToBytesField().get(3));
161     assertEquals(TestUtil.toBytes("44"), message.getInt32ToBytesField().get(4));
162 
163     assertEquals(3, message.getInt32ToEnumField().size());
164     assertEquals(TestMap.EnumValue.BAR, message.getInt32ToEnumField().get(1));
165     assertEquals(TestMap.EnumValue.BAZ, message.getInt32ToEnumField().get(3));
166     assertEquals(TestMap.EnumValue.QUX, message.getInt32ToEnumField().get(4));
167 
168     assertEquals(3, message.getInt32ToMessageField().size());
169     assertEquals(111, message.getInt32ToMessageField().get(1).getValue());
170     assertEquals(33, message.getInt32ToMessageField().get(3).getValue());
171     assertEquals(44, message.getInt32ToMessageField().get(4).getValue());
172 
173     assertEquals(3, message.getStringToInt32Field().size());
174     assertEquals(111, message.getStringToInt32Field().get("1").intValue());
175     assertEquals(33, message.getStringToInt32Field().get("3").intValue());
176     assertEquals(44, message.getStringToInt32Field().get("4").intValue());
177   }
178 
assertMapValuesCleared(TestMap message)179   private void assertMapValuesCleared(TestMap message) {
180     assertEquals(0, message.getInt32ToInt32Field().size());
181     assertEquals(0, message.getInt32ToStringField().size());
182     assertEquals(0, message.getInt32ToBytesField().size());
183     assertEquals(0, message.getInt32ToEnumField().size());
184     assertEquals(0, message.getInt32ToMessageField().size());
185     assertEquals(0, message.getStringToInt32Field().size());
186   }
187 
testSanityCopyOnWrite()188   public void testSanityCopyOnWrite() throws InvalidProtocolBufferException {
189     // Since builders are implemented as a thin wrapper around a message
190     // instance, we attempt to verify that we can't cause the builder to modify
191     // a produced message.
192 
193     TestMap.Builder builder = TestMap.newBuilder();
194     TestMap message = builder.build();
195     Map<Integer, Integer> intMap = builder.getMutableInt32ToInt32Field();
196     intMap.put(1, 2);
197     assertTrue(message.getInt32ToInt32Field().isEmpty());
198     message = builder.build();
199     try {
200       intMap.put(2, 3);
201       fail();
202     } catch (UnsupportedOperationException e) {
203       // expected
204     }
205     assertEquals(newMap(1, 2), message.getInt32ToInt32Field());
206     assertEquals(newMap(1, 2), builder.getInt32ToInt32Field());
207     builder.getMutableInt32ToInt32Field().put(2, 3);
208     assertEquals(newMap(1, 2), message.getInt32ToInt32Field());
209     assertEquals(newMap(1, 2, 2, 3), builder.getInt32ToInt32Field());
210   }
211 
testMutableMapLifecycle()212   public void testMutableMapLifecycle() {
213     TestMap.Builder builder = TestMap.newBuilder();
214     Map<Integer, Integer> intMap = builder.getMutableInt32ToInt32Field();
215     intMap.put(1, 2);
216     assertEquals(newMap(1, 2), builder.build().getInt32ToInt32Field());
217     try {
218       intMap.put(2, 3);
219       fail();
220     } catch (UnsupportedOperationException e) {
221       // expected
222     }
223     assertEquals(newMap(1, 2), builder.getInt32ToInt32Field());
224     builder.getMutableInt32ToInt32Field().put(2, 3);
225     assertEquals(newMap(1, 2, 2, 3), builder.getInt32ToInt32Field());
226 
227     Map<Integer, TestMap.EnumValue> enumMap = builder.getMutableInt32ToEnumField();
228     enumMap.put(1, TestMap.EnumValue.BAR);
229     assertEquals(newMap(1, TestMap.EnumValue.BAR), builder.build().getInt32ToEnumField());
230     try {
231       enumMap.put(2, TestMap.EnumValue.FOO);
232       fail();
233     } catch (UnsupportedOperationException e) {
234       // expected
235     }
236     assertEquals(newMap(1, TestMap.EnumValue.BAR), builder.getInt32ToEnumField());
237     builder.getMutableInt32ToEnumField().put(2, TestMap.EnumValue.FOO);
238     assertEquals(
239         newMap(1, TestMap.EnumValue.BAR, 2, TestMap.EnumValue.FOO),
240         builder.getInt32ToEnumField());
241 
242     Map<Integer, String> stringMap = builder.getMutableInt32ToStringField();
243     stringMap.put(1, "1");
244     assertEquals(newMap(1, "1"), builder.build().getInt32ToStringField());
245     try {
246       stringMap.put(2, "2");
247       fail();
248     } catch (UnsupportedOperationException e) {
249       // expected
250     }
251     assertEquals(newMap(1, "1"), builder.getInt32ToStringField());
252     builder.getMutableInt32ToStringField().put(2, "2");
253     assertEquals(
254         newMap(1, "1", 2, "2"),
255         builder.getInt32ToStringField());
256 
257     Map<Integer, TestMap.MessageValue> messageMap = builder.getMutableInt32ToMessageField();
258     messageMap.put(1, TestMap.MessageValue.getDefaultInstance());
259     assertEquals(newMap(1, TestMap.MessageValue.getDefaultInstance()),
260         builder.build().getInt32ToMessageField());
261     try {
262       messageMap.put(2, TestMap.MessageValue.getDefaultInstance());
263       fail();
264     } catch (UnsupportedOperationException e) {
265       // expected
266     }
267     assertEquals(newMap(1, TestMap.MessageValue.getDefaultInstance()),
268         builder.getInt32ToMessageField());
269     builder.getMutableInt32ToMessageField().put(2, TestMap.MessageValue.getDefaultInstance());
270     assertEquals(
271         newMap(1, TestMap.MessageValue.getDefaultInstance(),
272             2, TestMap.MessageValue.getDefaultInstance()),
273         builder.getInt32ToMessageField());
274   }
275 
testMutableMapLifecycle_collections()276   public void testMutableMapLifecycle_collections() {
277     TestMap.Builder builder = TestMap.newBuilder();
278     Map<Integer, Integer> intMap = builder.getMutableInt32ToInt32Field();
279     intMap.put(1, 2);
280     assertEquals(newMap(1, 2), builder.build().getInt32ToInt32Field());
281     try {
282       intMap.remove(2);
283       fail();
284     } catch (UnsupportedOperationException e) {
285       // expected
286     }
287     try {
288       intMap.entrySet().remove(new Object());
289       fail();
290     } catch (UnsupportedOperationException e) {
291       // expected
292     }
293     try {
294       intMap.entrySet().iterator().remove();
295       fail();
296     } catch (UnsupportedOperationException e) {
297       // expected
298     }
299     try {
300       intMap.keySet().remove(new Object());
301       fail();
302     } catch (UnsupportedOperationException e) {
303       // expected
304     }
305     try {
306       intMap.values().remove(new Object());
307       fail();
308     } catch (UnsupportedOperationException e) {
309       // expected
310     }
311     try {
312       intMap.values().iterator().remove();
313       fail();
314     } catch (UnsupportedOperationException e) {
315       // expected
316     }
317     assertEquals(newMap(1, 2), intMap);
318     assertEquals(newMap(1, 2), builder.getInt32ToInt32Field());
319     assertEquals(newMap(1, 2), builder.build().getInt32ToInt32Field());
320   }
321 
testGettersAndSetters()322   public void testGettersAndSetters() throws Exception {
323     TestMap.Builder builder = TestMap.newBuilder();
324     TestMap message = builder.build();
325     assertMapValuesCleared(message);
326 
327     builder = message.toBuilder();
328     setMapValues(builder);
329     message = builder.build();
330     assertMapValuesSet(message);
331 
332     builder = message.toBuilder();
333     updateMapValues(builder);
334     message = builder.build();
335     assertMapValuesUpdated(message);
336 
337     builder = message.toBuilder();
338     builder.clear();
339     message = builder.build();
340     assertMapValuesCleared(message);
341   }
342 
testPutAll()343   public void testPutAll() throws Exception {
344     TestMap.Builder sourceBuilder = TestMap.newBuilder();
345     setMapValues(sourceBuilder);
346     TestMap source = sourceBuilder.build();
347 
348     TestMap.Builder destination = TestMap.newBuilder();
349     copyMapValues(source, destination);
350     assertMapValuesSet(destination.build());
351   }
352 
testSerializeAndParse()353   public void testSerializeAndParse() throws Exception {
354     TestMap.Builder builder = TestMap.newBuilder();
355     setMapValues(builder);
356     TestMap message = builder.build();
357     assertEquals(message.getSerializedSize(), message.toByteString().size());
358     message = TestMap.parser().parseFrom(message.toByteString());
359     assertMapValuesSet(message);
360 
361     builder = message.toBuilder();
362     updateMapValues(builder);
363     message = builder.build();
364     assertEquals(message.getSerializedSize(), message.toByteString().size());
365     message = TestMap.parser().parseFrom(message.toByteString());
366     assertMapValuesUpdated(message);
367 
368     builder = message.toBuilder();
369     builder.clear();
370     message = builder.build();
371     assertEquals(message.getSerializedSize(), message.toByteString().size());
372     message = TestMap.parser().parseFrom(message.toByteString());
373     assertMapValuesCleared(message);
374   }
375 
testMergeFrom()376   public void testMergeFrom() throws Exception {
377     TestMap.Builder builder = TestMap.newBuilder();
378     setMapValues(builder);
379     TestMap message = builder.build();
380 
381     TestMap.Builder other = TestMap.newBuilder();
382     other.mergeFrom(message);
383     assertMapValuesSet(other.build());
384   }
385 
testEqualsAndHashCode()386   public void testEqualsAndHashCode() throws Exception {
387     // Test that generated equals() and hashCode() will disregard the order
388     // of map entries when comparing/hashing map fields.
389 
390     // We can't control the order of elements in a HashMap. The best we can do
391     // here is to add elements in different order.
392     TestMap.Builder b1 = TestMap.newBuilder();
393     b1.getMutableInt32ToInt32Field().put(1, 2);
394     b1.getMutableInt32ToInt32Field().put(3, 4);
395     b1.getMutableInt32ToInt32Field().put(5, 6);
396     TestMap m1 = b1.build();
397 
398     TestMap.Builder b2 = TestMap.newBuilder();
399     b2.getMutableInt32ToInt32Field().put(5, 6);
400     b2.getMutableInt32ToInt32Field().put(1, 2);
401     b2.getMutableInt32ToInt32Field().put(3, 4);
402     TestMap m2 = b2.build();
403 
404     assertEquals(m1, m2);
405     assertEquals(m1.hashCode(), m2.hashCode());
406 
407     // Make sure we did compare map fields.
408     b2.getMutableInt32ToInt32Field().put(1, 0);
409     m2 = b2.build();
410     assertFalse(m1.equals(m2));
411     // Don't check m1.hashCode() != m2.hashCode() because it's not guaranteed
412     // to be different.
413   }
414 
testUnknownEnumValues()415   public void testUnknownEnumValues() throws Exception {
416     TestUnknownEnumValue.Builder builder =
417         TestUnknownEnumValue.newBuilder();
418     builder.getMutableInt32ToInt32Field().put(1, 1);
419     builder.getMutableInt32ToInt32Field().put(2, 54321);
420     ByteString data = builder.build().toByteString();
421 
422     TestMap message = TestMap.parseFrom(data);
423     // Entries with unknown enum values will be stored into UnknownFieldSet so
424     // there is only one entry in the map.
425     assertEquals(1, message.getInt32ToEnumField().size());
426     assertEquals(TestMap.EnumValue.BAR, message.getInt32ToEnumField().get(1));
427     // Serializing and parsing should preserve the unknown entry.
428     data = message.toByteString();
429     TestUnknownEnumValue messageWithUnknownEnums =
430         TestUnknownEnumValue.parseFrom(data);
431     assertEquals(2, messageWithUnknownEnums.getInt32ToInt32Field().size());
432     assertEquals(1, messageWithUnknownEnums.getInt32ToInt32Field().get(1).intValue());
433     assertEquals(54321, messageWithUnknownEnums.getInt32ToInt32Field().get(2).intValue());
434   }
435 
436 
testIterationOrder()437   public void testIterationOrder() throws Exception {
438     TestMap.Builder builder = TestMap.newBuilder();
439     setMapValues(builder);
440     TestMap message = builder.build();
441 
442     assertEquals(Arrays.asList("1", "2", "3"),
443         new ArrayList<String>(message.getStringToInt32Field().keySet()));
444   }
445 
newMap(K key1, V value1)446   private static <K, V> Map<K, V> newMap(K key1, V value1) {
447     Map<K, V> map = new HashMap<K, V>();
448     map.put(key1, value1);
449     return map;
450   }
451 
newMap(K key1, V value1, K key2, V value2)452   private static <K, V> Map<K, V> newMap(K key1, V value1, K key2, V value2) {
453     Map<K, V> map = new HashMap<K, V>();
454     map.put(key1, value1);
455     map.put(key2, value2);
456     return map;
457   }
458 }
459