blob: 1cbff7b300c817b25c6fe1ee435e0138ded92a6d [file] [log] [blame]
Bo Yangcc8ca5b2016-09-19 13:45:07 -07001// 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
31goog.require('goog.testing.asserts');
32goog.require('goog.userAgent');
Joshua Habermanaf62fde2016-09-20 13:56:18 -070033
34// CommonJS-LoadFromFile: testbinary_pb proto.jspb.test
Bo Yangcc8ca5b2016-09-19 13:45:07 -070035goog.require('proto.jspb.test.MapValueEnum');
36goog.require('proto.jspb.test.MapValueMessage');
Bo Yangcc8ca5b2016-09-19 13:45:07 -070037goog.require('proto.jspb.test.TestMapFields');
Adam Cozzetted1af0292018-05-25 14:12:43 -070038goog.require('proto.jspb.test.TestMapFieldsOptionalKeys');
Rafi Kamal4f02f052019-08-22 16:14:22 -070039goog.require('proto.jspb.test.TestMapFieldsOptionalValues');
Adam Cozzetted1af0292018-05-25 14:12:43 -070040goog.require('proto.jspb.test.MapEntryOptionalKeysStringKey');
41goog.require('proto.jspb.test.MapEntryOptionalKeysInt32Key');
42goog.require('proto.jspb.test.MapEntryOptionalKeysInt64Key');
43goog.require('proto.jspb.test.MapEntryOptionalKeysBoolKey');
Rafi Kamal4f02f052019-08-22 16:14:22 -070044goog.require('proto.jspb.test.MapEntryOptionalValuesStringValue');
45goog.require('proto.jspb.test.MapEntryOptionalValuesInt32Value');
46goog.require('proto.jspb.test.MapEntryOptionalValuesInt64Value');
47goog.require('proto.jspb.test.MapEntryOptionalValuesBoolValue');
48goog.require('proto.jspb.test.MapEntryOptionalValuesDoubleValue');
49goog.require('proto.jspb.test.MapEntryOptionalValuesEnumValue');
50goog.require('proto.jspb.test.MapEntryOptionalValuesMessageValue');
Joshua Habermanaf62fde2016-09-20 13:56:18 -070051
52// CommonJS-LoadFromFile: test_pb proto.jspb.test
53goog.require('proto.jspb.test.MapValueMessageNoBinary');
Bo Yangcc8ca5b2016-09-19 13:45:07 -070054goog.require('proto.jspb.test.TestMapFieldsNoBinary');
55
56/**
57 * Helper: check that the given map has exactly this set of (sorted) entries.
58 * @param {!jspb.Map} map
59 * @param {!Array<!Array<?>>} entries
60 */
61function checkMapEquals(map, entries) {
62 var arr = map.toArray();
63 assertEquals(arr.length, entries.length);
64 for (var i = 0; i < arr.length; i++) {
Rafi Kamal4f02f052019-08-22 16:14:22 -070065 if (Array.isArray(arr[i])) {
66 assertTrue(Array.isArray(entries[i]));
67 assertArrayEquals(arr[i], entries[i]);
68 } else {
69 assertElementsEquals(arr[i], entries[i]);
70 }
Bo Yangcc8ca5b2016-09-19 13:45:07 -070071 }
72}
73
74/**
75 * Converts an ES6 iterator to an array.
76 * @template T
77 * @param {!Iterator<T>} iter an iterator
78 * @return {!Array<T>}
79 */
80function toArray(iter) {
81 var arr = [];
82 while (true) {
83 var val = iter.next();
84 if (val.done) {
85 break;
86 }
87 arr.push(val.value);
88 }
89 return arr;
90}
91
92
93/**
94 * Helper: generate test methods for this TestMapFields class.
95 * @param {?} msgInfo
96 * @param {?} submessageCtor
Adam Cozzetted1af0292018-05-25 14:12:43 -070097 * @param {string} suffix
Bo Yangcc8ca5b2016-09-19 13:45:07 -070098 */
99function makeTests(msgInfo, submessageCtor, suffix) {
100 /**
101 * Helper: fill all maps on a TestMapFields.
102 * @param {?} msg
103 */
104 var fillMapFields = function(msg) {
105 msg.getMapStringStringMap().set('asdf', 'jkl;').set('key 2', 'hello world');
106 msg.getMapStringInt32Map().set('a', 1).set('b', -2);
107 msg.getMapStringInt64Map().set('c', 0x100000000).set('d', 0x200000000);
108 msg.getMapStringBoolMap().set('e', true).set('f', false);
109 msg.getMapStringDoubleMap().set('g', 3.14159).set('h', 2.71828);
110 msg.getMapStringEnumMap()
111 .set('i', proto.jspb.test.MapValueEnum.MAP_VALUE_BAR)
112 .set('j', proto.jspb.test.MapValueEnum.MAP_VALUE_BAZ);
113 msg.getMapStringMsgMap()
114 .set('k', new submessageCtor())
115 .set('l', new submessageCtor());
116 msg.getMapStringMsgMap().get('k').setFoo(42);
117 msg.getMapStringMsgMap().get('l').setFoo(84);
118 msg.getMapInt32StringMap().set(-1, 'a').set(42, 'b');
119 msg.getMapInt64StringMap().set(0x123456789abc, 'c').set(0xcba987654321, 'd');
120 msg.getMapBoolStringMap().set(false, 'e').set(true, 'f');
121 };
122
123 /**
124 * Helper: check all maps on a TestMapFields.
125 * @param {?} msg
126 */
127 var checkMapFields = function(msg) {
128 checkMapEquals(msg.getMapStringStringMap(), [
129 ['asdf', 'jkl;'],
130 ['key 2', 'hello world']
131 ]);
132 checkMapEquals(msg.getMapStringInt32Map(), [
133 ['a', 1],
134 ['b', -2]
135 ]);
136 checkMapEquals(msg.getMapStringInt64Map(), [
137 ['c', 0x100000000],
138 ['d', 0x200000000]
139 ]);
140 checkMapEquals(msg.getMapStringBoolMap(), [
141 ['e', true],
142 ['f', false]
143 ]);
144 checkMapEquals(msg.getMapStringDoubleMap(), [
145 ['g', 3.14159],
146 ['h', 2.71828]
147 ]);
148 checkMapEquals(msg.getMapStringEnumMap(), [
149 ['i', proto.jspb.test.MapValueEnum.MAP_VALUE_BAR],
150 ['j', proto.jspb.test.MapValueEnum.MAP_VALUE_BAZ]
151 ]);
152 checkMapEquals(msg.getMapInt32StringMap(), [
153 [-1, 'a'],
154 [42, 'b']
155 ]);
156 checkMapEquals(msg.getMapInt64StringMap(), [
157 [0x123456789abc, 'c'],
158 [0xcba987654321, 'd']
159 ]);
160 checkMapEquals(msg.getMapBoolStringMap(), [
161 [false, 'e'],
162 [true, 'f']
163 ]);
164
165 assertEquals(msg.getMapStringMsgMap().getLength(), 2);
166 assertEquals(msg.getMapStringMsgMap().get('k').getFoo(), 42);
167 assertEquals(msg.getMapStringMsgMap().get('l').getFoo(), 84);
168
169 var entries = toArray(msg.getMapStringMsgMap().entries());
170 assertEquals(entries.length, 2);
171 entries.forEach(function(entry) {
172 var key = entry[0];
173 var val = entry[1];
174 assert(val === msg.getMapStringMsgMap().get(key));
175 });
176
177 msg.getMapStringMsgMap().forEach(function(val, key) {
178 assert(val === msg.getMapStringMsgMap().get(key));
179 });
180 };
181
182 it('testMapStringStringField' + suffix, function() {
183 var msg = new msgInfo.constructor();
184 assertEquals(msg.getMapStringStringMap().getLength(), 0);
185 assertEquals(msg.getMapStringInt32Map().getLength(), 0);
186 assertEquals(msg.getMapStringInt64Map().getLength(), 0);
187 assertEquals(msg.getMapStringBoolMap().getLength(), 0);
188 assertEquals(msg.getMapStringDoubleMap().getLength(), 0);
189 assertEquals(msg.getMapStringEnumMap().getLength(), 0);
190 assertEquals(msg.getMapStringMsgMap().getLength(), 0);
191
192 // Re-create to clear out any internally-cached wrappers, etc.
193 msg = new msgInfo.constructor();
194 var m = msg.getMapStringStringMap();
195 assertEquals(m.has('asdf'), false);
196 assertEquals(m.get('asdf'), undefined);
197 m.set('asdf', 'hello world');
198 assertEquals(m.has('asdf'), true);
199 assertEquals(m.get('asdf'), 'hello world');
200 m.set('jkl;', 'key 2');
201 assertEquals(m.has('jkl;'), true);
202 assertEquals(m.get('jkl;'), 'key 2');
203 assertEquals(m.getLength(), 2);
204 var it = m.entries();
205 assertElementsEquals(it.next().value, ['asdf', 'hello world']);
206 assertElementsEquals(it.next().value, ['jkl;', 'key 2']);
207 assertEquals(it.next().done, true);
208 checkMapEquals(m, [
209 ['asdf', 'hello world'],
210 ['jkl;', 'key 2']
211 ]);
212 m.del('jkl;');
213 assertEquals(m.has('jkl;'), false);
214 assertEquals(m.get('jkl;'), undefined);
215 assertEquals(m.getLength(), 1);
216 it = m.keys();
217 assertEquals(it.next().value, 'asdf');
218 assertEquals(it.next().done, true);
219 it = m.values();
220 assertEquals(it.next().value, 'hello world');
221 assertEquals(it.next().done, true);
222
223 var count = 0;
224 m.forEach(function(value, key, map) {
225 assertEquals(map, m);
226 assertEquals(key, 'asdf');
227 assertEquals(value, 'hello world');
228 count++;
229 });
230 assertEquals(count, 1);
231
232 m.clear();
233 assertEquals(m.getLength(), 0);
234 });
235
236
237 /**
238 * Tests operations on maps with all key and value types.
239 */
240 it('testAllMapTypes' + suffix, function() {
241 var msg = new msgInfo.constructor();
242 fillMapFields(msg);
243 checkMapFields(msg);
244 });
245
246
247 if (msgInfo.deserializeBinary) {
248 /**
249 * Tests serialization and deserialization in binary format.
250 */
251 it('testBinaryFormat' + suffix, function() {
252 if (goog.userAgent.IE && !goog.userAgent.isDocumentModeOrHigher(10)) {
253 // IE8/9 currently doesn't support binary format because they lack
254 // TypedArray.
255 return;
256 }
257
258 // Check that the format is correct.
259 var msg = new msgInfo.constructor();
260 msg.getMapStringStringMap().set('A', 'a');
261 var serialized = msg.serializeBinary();
262 var expectedSerialized = [
263 0x0a, 0x6, // field 1 (map_string_string), delimited, length 6
264 0x0a, 0x1, // field 1 in submessage (key), delimited, length 1
265 0x41, // ASCII 'A'
266 0x12, 0x1, // field 2 in submessage (value), delimited, length 1
267 0x61 // ASCII 'a'
268 ];
269 assertEquals(serialized.length, expectedSerialized.length);
270 for (var i = 0; i < serialized.length; i++) {
271 assertEquals(serialized[i], expectedSerialized[i]);
272 }
273
274 // Check that all map fields successfully round-trip.
275 msg = new msgInfo.constructor();
276 fillMapFields(msg);
277 serialized = msg.serializeBinary();
278 var decoded = msgInfo.deserializeBinary(serialized);
279 checkMapFields(decoded);
280 });
Rafi Kamal4f02f052019-08-22 16:14:22 -0700281
Adam Cozzetted1af0292018-05-25 14:12:43 -0700282 /**
Rafi Kamal4f02f052019-08-22 16:14:22 -0700283 * Tests deserialization of undefined map keys go to default values in
284 * binary format.
Adam Cozzetted1af0292018-05-25 14:12:43 -0700285 */
286 it('testMapDeserializationForUndefinedKeys', function() {
287 var testMessageOptionalKeys = new proto.jspb.test.TestMapFieldsOptionalKeys();
288 var mapEntryStringKey = new proto.jspb.test.MapEntryOptionalKeysStringKey();
289 mapEntryStringKey.setValue("a");
290 testMessageOptionalKeys.setMapStringString(mapEntryStringKey);
291 var mapEntryInt32Key = new proto.jspb.test.MapEntryOptionalKeysInt32Key();
292 mapEntryInt32Key.setValue("b");
293 testMessageOptionalKeys.setMapInt32String(mapEntryInt32Key);
294 var mapEntryInt64Key = new proto.jspb.test.MapEntryOptionalKeysInt64Key();
295 mapEntryInt64Key.setValue("c");
296 testMessageOptionalKeys.setMapInt64String(mapEntryInt64Key);
297 var mapEntryBoolKey = new proto.jspb.test.MapEntryOptionalKeysBoolKey();
298 mapEntryBoolKey.setValue("d");
299 testMessageOptionalKeys.setMapBoolString(mapEntryBoolKey);
300 var deserializedMessage = msgInfo.deserializeBinary(
301 testMessageOptionalKeys.serializeBinary()
302 );
303 checkMapEquals(deserializedMessage.getMapStringStringMap(), [
304 ['', 'a']
305 ]);
306 checkMapEquals(deserializedMessage.getMapInt32StringMap(), [
307 [0, 'b']
308 ]);
309 checkMapEquals(deserializedMessage.getMapInt64StringMap(), [
310 [0, 'c']
311 ]);
312 checkMapEquals(deserializedMessage.getMapBoolStringMap(), [
313 [false, 'd']
314 ]);
315 });
Rafi Kamal4f02f052019-08-22 16:14:22 -0700316
317 /**
318 * Tests deserialization of undefined map values go to default values in
319 * binary format.
320 */
321 it('testMapDeserializationForUndefinedValues', function() {
322 var testMessageOptionalValues =
323 new proto.jspb.test.TestMapFieldsOptionalValues();
324 var mapEntryStringValue =
325 new proto.jspb.test.MapEntryOptionalValuesStringValue();
326 mapEntryStringValue.setKey("a");
327 testMessageOptionalValues.setMapStringString(mapEntryStringValue);
328 var mapEntryInt32Value =
329 new proto.jspb.test.MapEntryOptionalValuesInt32Value();
330 mapEntryInt32Value.setKey("b");
331 testMessageOptionalValues.setMapStringInt32(mapEntryInt32Value);
332 var mapEntryInt64Value =
333 new proto.jspb.test.MapEntryOptionalValuesInt64Value();
334 mapEntryInt64Value.setKey("c");
335 testMessageOptionalValues.setMapStringInt64(mapEntryInt64Value);
336 var mapEntryBoolValue =
337 new proto.jspb.test.MapEntryOptionalValuesBoolValue();
338 mapEntryBoolValue.setKey("d");
339 testMessageOptionalValues.setMapStringBool(mapEntryBoolValue);
340 var mapEntryDoubleValue =
341 new proto.jspb.test.MapEntryOptionalValuesDoubleValue();
342 mapEntryDoubleValue.setKey("e");
343 testMessageOptionalValues.setMapStringDouble(mapEntryDoubleValue);
344 var mapEntryEnumValue =
345 new proto.jspb.test.MapEntryOptionalValuesEnumValue();
346 mapEntryEnumValue.setKey("f");
347 testMessageOptionalValues.setMapStringEnum(mapEntryEnumValue);
348 var mapEntryMessageValue =
349 new proto.jspb.test.MapEntryOptionalValuesMessageValue();
350 mapEntryMessageValue.setKey("g");
351 testMessageOptionalValues.setMapStringMsg(mapEntryMessageValue);
352 var deserializedMessage = msgInfo.deserializeBinary(
353 testMessageOptionalValues.serializeBinary()
354 );
355 checkMapEquals(deserializedMessage.getMapStringStringMap(), [
356 ['a', '']
357 ]);
358 checkMapEquals(deserializedMessage.getMapStringInt32Map(), [
359 ['b', 0]
360 ]);
361 checkMapEquals(deserializedMessage.getMapStringInt64Map(), [
362 ['c', 0]
363 ]);
364 checkMapEquals(deserializedMessage.getMapStringBoolMap(), [
365 ['d', false]
366 ]);
367 checkMapEquals(deserializedMessage.getMapStringDoubleMap(), [
368 ['e', 0.0]
369 ]);
370 checkMapEquals(deserializedMessage.getMapStringEnumMap(), [
371 ['f', 0]
372 ]);
373 checkMapEquals(deserializedMessage.getMapStringMsgMap(), [
374 ['g', []]
375 ]);
376 });
Bo Yangcc8ca5b2016-09-19 13:45:07 -0700377 }
378
Adam Cozzette5a76e632016-11-17 16:48:38 -0800379
Bo Yangcc8ca5b2016-09-19 13:45:07 -0700380 /**
381 * Exercises the lazy map<->underlying array sync.
382 */
383 it('testLazyMapSync' + suffix, function() {
384 // Start with a JSPB array containing a few map entries.
385 var entries = [
386 ['a', 'entry 1'],
387 ['c', 'entry 2'],
388 ['b', 'entry 3']
389 ];
390 var msg = new msgInfo.constructor([entries]);
391 assertEquals(entries.length, 3);
392 assertEquals(entries[0][0], 'a');
393 assertEquals(entries[1][0], 'c');
394 assertEquals(entries[2][0], 'b');
395 msg.getMapStringStringMap().del('a');
396 assertEquals(entries.length, 3); // not yet sync'd
397 msg.toArray(); // force a sync
398 assertEquals(entries.length, 2);
399 assertEquals(entries[0][0], 'b'); // now in sorted order
400 assertEquals(entries[1][0], 'c');
401
402 var a = msg.toArray();
403 assertEquals(a[0], entries); // retains original reference
404 });
Paul Yang7f3e2372017-01-31 09:17:32 -0800405
406 /**
407 * Returns IteratorIterables for entries(), keys() and values().
408 */
409 it('testIteratorIterables' + suffix, function() {
410 var msg = new msgInfo.constructor();
411 var m = msg.getMapStringStringMap();
412 m.set('key1', 'value1');
413 m.set('key2', 'value2');
414 var entryIterator = m.entries();
415 assertElementsEquals(entryIterator.next().value, ['key1', 'value1']);
416 assertElementsEquals(entryIterator.next().value, ['key2', 'value2']);
417 assertEquals(entryIterator.next().done, true);
418
Adam Cozzette0894e072018-11-09 11:28:22 -0800419 try {
Paul Yang7f3e2372017-01-31 09:17:32 -0800420 var entryIterable = m.entries()[Symbol.iterator]();
421 assertElementsEquals(entryIterable.next().value, ['key1', 'value1']);
422 assertElementsEquals(entryIterable.next().value, ['key2', 'value2']);
423 assertEquals(entryIterable.next().done, true);
Adam Cozzette0894e072018-11-09 11:28:22 -0800424 } catch (err) {
425 // jspb.Map.ArrayIteratorIterable_.prototype[Symbol.iterator] may be
426 // undefined in some environment.
427 if (err.name != 'TypeError' && err.name != 'ReferenceError') {
428 throw err;
429 }
Paul Yang7f3e2372017-01-31 09:17:32 -0800430 }
431
432 var keyIterator = m.keys();
433 assertEquals(keyIterator.next().value, 'key1');
434 assertEquals(keyIterator.next().value, 'key2');
435 assertEquals(keyIterator.next().done, true);
436
Adam Cozzette0894e072018-11-09 11:28:22 -0800437 try {
Paul Yang7f3e2372017-01-31 09:17:32 -0800438 var keyIterable = m.keys()[Symbol.iterator]();
439 assertEquals(keyIterable.next().value, 'key1');
440 assertEquals(keyIterable.next().value, 'key2');
441 assertEquals(keyIterable.next().done, true);
Adam Cozzette0894e072018-11-09 11:28:22 -0800442 } catch (err) {
443 // jspb.Map.ArrayIteratorIterable_.prototype[Symbol.iterator] may be
444 // undefined in some environment.
445 if (err.name != 'TypeError' && err.name != 'ReferenceError') {
446 throw err;
447 }
Paul Yang7f3e2372017-01-31 09:17:32 -0800448 }
449 var valueIterator = m.values();
450 assertEquals(valueIterator.next().value, 'value1');
451 assertEquals(valueIterator.next().value, 'value2');
452 assertEquals(valueIterator.next().done, true);
453
Adam Cozzette0894e072018-11-09 11:28:22 -0800454 try {
Paul Yang7f3e2372017-01-31 09:17:32 -0800455 var valueIterable = m.values()[Symbol.iterator]();
456 assertEquals(valueIterable.next().value, 'value1');
457 assertEquals(valueIterable.next().value, 'value2');
458 assertEquals(valueIterable.next().done, true);
Adam Cozzette0894e072018-11-09 11:28:22 -0800459 } catch (err) {
460 // jspb.Map.ArrayIteratorIterable_.prototype[Symbol.iterator] may be
461 // undefined in some environment.
462 if (err.name != 'TypeError' && err.name != 'ReferenceError') {
463 throw err;
464 }
Paul Yang7f3e2372017-01-31 09:17:32 -0800465 }
466 });
Bo Yangcc8ca5b2016-09-19 13:45:07 -0700467}
468
469describe('mapsTest', function() {
Adam Cozzette5a76e632016-11-17 16:48:38 -0800470 makeTests(
471 {
472 constructor: proto.jspb.test.TestMapFields,
473 deserializeBinary: proto.jspb.test.TestMapFields.deserializeBinary
474 },
475 proto.jspb.test.MapValueMessage, '_Binary');
476 makeTests(
477 {
478 constructor: proto.jspb.test.TestMapFieldsNoBinary,
479 deserializeBinary: null
480 },
481 proto.jspb.test.MapValueMessageNoBinary, '_NoBinary');
Bo Yangcc8ca5b2016-09-19 13:45:07 -0700482});