blob: e496f446bdc1b429dec62ca096ec7a98336820af [file] [log] [blame]
Austin Schuh40c16522018-10-28 20:27:54 -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');
33
34// CommonJS-LoadFromFile: testbinary_pb proto.jspb.test
35goog.require('proto.jspb.test.MapValueEnum');
36goog.require('proto.jspb.test.MapValueMessage');
37goog.require('proto.jspb.test.TestMapFields');
38goog.require('proto.jspb.test.TestMapFieldsOptionalKeys');
39goog.require('proto.jspb.test.MapEntryOptionalKeysStringKey');
40goog.require('proto.jspb.test.MapEntryOptionalKeysInt32Key');
41goog.require('proto.jspb.test.MapEntryOptionalKeysInt64Key');
42goog.require('proto.jspb.test.MapEntryOptionalKeysBoolKey');
43
44// CommonJS-LoadFromFile: test_pb proto.jspb.test
45goog.require('proto.jspb.test.MapValueMessageNoBinary');
46goog.require('proto.jspb.test.TestMapFieldsNoBinary');
47
48/**
49 * Helper: check that the given map has exactly this set of (sorted) entries.
50 * @param {!jspb.Map} map
51 * @param {!Array<!Array<?>>} entries
52 */
53function checkMapEquals(map, entries) {
54 var arr = map.toArray();
55 assertEquals(arr.length, entries.length);
56 for (var i = 0; i < arr.length; i++) {
57 assertElementsEquals(arr[i], entries[i]);
58 }
59}
60
61/**
62 * Converts an ES6 iterator to an array.
63 * @template T
64 * @param {!Iterator<T>} iter an iterator
65 * @return {!Array<T>}
66 */
67function toArray(iter) {
68 var arr = [];
69 while (true) {
70 var val = iter.next();
71 if (val.done) {
72 break;
73 }
74 arr.push(val.value);
75 }
76 return arr;
77}
78
79
80/**
81 * Helper: generate test methods for this TestMapFields class.
82 * @param {?} msgInfo
83 * @param {?} submessageCtor
84 * @param {string} suffix
85 */
86function makeTests(msgInfo, submessageCtor, suffix) {
87 /**
88 * Helper: fill all maps on a TestMapFields.
89 * @param {?} msg
90 */
91 var fillMapFields = function(msg) {
92 msg.getMapStringStringMap().set('asdf', 'jkl;').set('key 2', 'hello world');
93 msg.getMapStringInt32Map().set('a', 1).set('b', -2);
94 msg.getMapStringInt64Map().set('c', 0x100000000).set('d', 0x200000000);
95 msg.getMapStringBoolMap().set('e', true).set('f', false);
96 msg.getMapStringDoubleMap().set('g', 3.14159).set('h', 2.71828);
97 msg.getMapStringEnumMap()
98 .set('i', proto.jspb.test.MapValueEnum.MAP_VALUE_BAR)
99 .set('j', proto.jspb.test.MapValueEnum.MAP_VALUE_BAZ);
100 msg.getMapStringMsgMap()
101 .set('k', new submessageCtor())
102 .set('l', new submessageCtor());
103 msg.getMapStringMsgMap().get('k').setFoo(42);
104 msg.getMapStringMsgMap().get('l').setFoo(84);
105 msg.getMapInt32StringMap().set(-1, 'a').set(42, 'b');
106 msg.getMapInt64StringMap().set(0x123456789abc, 'c').set(0xcba987654321, 'd');
107 msg.getMapBoolStringMap().set(false, 'e').set(true, 'f');
108 };
109
110 /**
111 * Helper: check all maps on a TestMapFields.
112 * @param {?} msg
113 */
114 var checkMapFields = function(msg) {
115 checkMapEquals(msg.getMapStringStringMap(), [
116 ['asdf', 'jkl;'],
117 ['key 2', 'hello world']
118 ]);
119 checkMapEquals(msg.getMapStringInt32Map(), [
120 ['a', 1],
121 ['b', -2]
122 ]);
123 checkMapEquals(msg.getMapStringInt64Map(), [
124 ['c', 0x100000000],
125 ['d', 0x200000000]
126 ]);
127 checkMapEquals(msg.getMapStringBoolMap(), [
128 ['e', true],
129 ['f', false]
130 ]);
131 checkMapEquals(msg.getMapStringDoubleMap(), [
132 ['g', 3.14159],
133 ['h', 2.71828]
134 ]);
135 checkMapEquals(msg.getMapStringEnumMap(), [
136 ['i', proto.jspb.test.MapValueEnum.MAP_VALUE_BAR],
137 ['j', proto.jspb.test.MapValueEnum.MAP_VALUE_BAZ]
138 ]);
139 checkMapEquals(msg.getMapInt32StringMap(), [
140 [-1, 'a'],
141 [42, 'b']
142 ]);
143 checkMapEquals(msg.getMapInt64StringMap(), [
144 [0x123456789abc, 'c'],
145 [0xcba987654321, 'd']
146 ]);
147 checkMapEquals(msg.getMapBoolStringMap(), [
148 [false, 'e'],
149 [true, 'f']
150 ]);
151
152 assertEquals(msg.getMapStringMsgMap().getLength(), 2);
153 assertEquals(msg.getMapStringMsgMap().get('k').getFoo(), 42);
154 assertEquals(msg.getMapStringMsgMap().get('l').getFoo(), 84);
155
156 var entries = toArray(msg.getMapStringMsgMap().entries());
157 assertEquals(entries.length, 2);
158 entries.forEach(function(entry) {
159 var key = entry[0];
160 var val = entry[1];
161 assert(val === msg.getMapStringMsgMap().get(key));
162 });
163
164 msg.getMapStringMsgMap().forEach(function(val, key) {
165 assert(val === msg.getMapStringMsgMap().get(key));
166 });
167 };
168
169 it('testMapStringStringField' + suffix, function() {
170 var msg = new msgInfo.constructor();
171 assertEquals(msg.getMapStringStringMap().getLength(), 0);
172 assertEquals(msg.getMapStringInt32Map().getLength(), 0);
173 assertEquals(msg.getMapStringInt64Map().getLength(), 0);
174 assertEquals(msg.getMapStringBoolMap().getLength(), 0);
175 assertEquals(msg.getMapStringDoubleMap().getLength(), 0);
176 assertEquals(msg.getMapStringEnumMap().getLength(), 0);
177 assertEquals(msg.getMapStringMsgMap().getLength(), 0);
178
179 // Re-create to clear out any internally-cached wrappers, etc.
180 msg = new msgInfo.constructor();
181 var m = msg.getMapStringStringMap();
182 assertEquals(m.has('asdf'), false);
183 assertEquals(m.get('asdf'), undefined);
184 m.set('asdf', 'hello world');
185 assertEquals(m.has('asdf'), true);
186 assertEquals(m.get('asdf'), 'hello world');
187 m.set('jkl;', 'key 2');
188 assertEquals(m.has('jkl;'), true);
189 assertEquals(m.get('jkl;'), 'key 2');
190 assertEquals(m.getLength(), 2);
191 var it = m.entries();
192 assertElementsEquals(it.next().value, ['asdf', 'hello world']);
193 assertElementsEquals(it.next().value, ['jkl;', 'key 2']);
194 assertEquals(it.next().done, true);
195 checkMapEquals(m, [
196 ['asdf', 'hello world'],
197 ['jkl;', 'key 2']
198 ]);
199 m.del('jkl;');
200 assertEquals(m.has('jkl;'), false);
201 assertEquals(m.get('jkl;'), undefined);
202 assertEquals(m.getLength(), 1);
203 it = m.keys();
204 assertEquals(it.next().value, 'asdf');
205 assertEquals(it.next().done, true);
206 it = m.values();
207 assertEquals(it.next().value, 'hello world');
208 assertEquals(it.next().done, true);
209
210 var count = 0;
211 m.forEach(function(value, key, map) {
212 assertEquals(map, m);
213 assertEquals(key, 'asdf');
214 assertEquals(value, 'hello world');
215 count++;
216 });
217 assertEquals(count, 1);
218
219 m.clear();
220 assertEquals(m.getLength(), 0);
221 });
222
223
224 /**
225 * Tests operations on maps with all key and value types.
226 */
227 it('testAllMapTypes' + suffix, function() {
228 var msg = new msgInfo.constructor();
229 fillMapFields(msg);
230 checkMapFields(msg);
231 });
232
233
234 if (msgInfo.deserializeBinary) {
235 /**
236 * Tests serialization and deserialization in binary format.
237 */
238 it('testBinaryFormat' + suffix, function() {
239 if (goog.userAgent.IE && !goog.userAgent.isDocumentModeOrHigher(10)) {
240 // IE8/9 currently doesn't support binary format because they lack
241 // TypedArray.
242 return;
243 }
244
245 // Check that the format is correct.
246 var msg = new msgInfo.constructor();
247 msg.getMapStringStringMap().set('A', 'a');
248 var serialized = msg.serializeBinary();
249 var expectedSerialized = [
250 0x0a, 0x6, // field 1 (map_string_string), delimited, length 6
251 0x0a, 0x1, // field 1 in submessage (key), delimited, length 1
252 0x41, // ASCII 'A'
253 0x12, 0x1, // field 2 in submessage (value), delimited, length 1
254 0x61 // ASCII 'a'
255 ];
256 assertEquals(serialized.length, expectedSerialized.length);
257 for (var i = 0; i < serialized.length; i++) {
258 assertEquals(serialized[i], expectedSerialized[i]);
259 }
260
261 // Check that all map fields successfully round-trip.
262 msg = new msgInfo.constructor();
263 fillMapFields(msg);
264 serialized = msg.serializeBinary();
265 var decoded = msgInfo.deserializeBinary(serialized);
266 checkMapFields(decoded);
267 });
268 /**
269 * Tests deserialization of undefined map keys go to default values in binary format.
270 */
271 it('testMapDeserializationForUndefinedKeys', function() {
272 var testMessageOptionalKeys = new proto.jspb.test.TestMapFieldsOptionalKeys();
273 var mapEntryStringKey = new proto.jspb.test.MapEntryOptionalKeysStringKey();
274 mapEntryStringKey.setValue("a");
275 testMessageOptionalKeys.setMapStringString(mapEntryStringKey);
276 var mapEntryInt32Key = new proto.jspb.test.MapEntryOptionalKeysInt32Key();
277 mapEntryInt32Key.setValue("b");
278 testMessageOptionalKeys.setMapInt32String(mapEntryInt32Key);
279 var mapEntryInt64Key = new proto.jspb.test.MapEntryOptionalKeysInt64Key();
280 mapEntryInt64Key.setValue("c");
281 testMessageOptionalKeys.setMapInt64String(mapEntryInt64Key);
282 var mapEntryBoolKey = new proto.jspb.test.MapEntryOptionalKeysBoolKey();
283 mapEntryBoolKey.setValue("d");
284 testMessageOptionalKeys.setMapBoolString(mapEntryBoolKey);
285 var deserializedMessage = msgInfo.deserializeBinary(
286 testMessageOptionalKeys.serializeBinary()
287 );
288 checkMapEquals(deserializedMessage.getMapStringStringMap(), [
289 ['', 'a']
290 ]);
291 checkMapEquals(deserializedMessage.getMapInt32StringMap(), [
292 [0, 'b']
293 ]);
294 checkMapEquals(deserializedMessage.getMapInt64StringMap(), [
295 [0, 'c']
296 ]);
297 checkMapEquals(deserializedMessage.getMapBoolStringMap(), [
298 [false, 'd']
299 ]);
300 });
301 }
302
303
304 /**
305 * Exercises the lazy map<->underlying array sync.
306 */
307 it('testLazyMapSync' + suffix, function() {
308 // Start with a JSPB array containing a few map entries.
309 var entries = [
310 ['a', 'entry 1'],
311 ['c', 'entry 2'],
312 ['b', 'entry 3']
313 ];
314 var msg = new msgInfo.constructor([entries]);
315 assertEquals(entries.length, 3);
316 assertEquals(entries[0][0], 'a');
317 assertEquals(entries[1][0], 'c');
318 assertEquals(entries[2][0], 'b');
319 msg.getMapStringStringMap().del('a');
320 assertEquals(entries.length, 3); // not yet sync'd
321 msg.toArray(); // force a sync
322 assertEquals(entries.length, 2);
323 assertEquals(entries[0][0], 'b'); // now in sorted order
324 assertEquals(entries[1][0], 'c');
325
326 var a = msg.toArray();
327 assertEquals(a[0], entries); // retains original reference
328 });
329
330 /**
331 * Returns IteratorIterables for entries(), keys() and values().
332 */
333 it('testIteratorIterables' + suffix, function() {
334 var msg = new msgInfo.constructor();
335 var m = msg.getMapStringStringMap();
336 m.set('key1', 'value1');
337 m.set('key2', 'value2');
338 var entryIterator = m.entries();
339 assertElementsEquals(entryIterator.next().value, ['key1', 'value1']);
340 assertElementsEquals(entryIterator.next().value, ['key2', 'value2']);
341 assertEquals(entryIterator.next().done, true);
342
343 if (typeof(Symbol) != 'undefined') {
344 var entryIterable = m.entries()[Symbol.iterator]();
345 assertElementsEquals(entryIterable.next().value, ['key1', 'value1']);
346 assertElementsEquals(entryIterable.next().value, ['key2', 'value2']);
347 assertEquals(entryIterable.next().done, true);
348 }
349
350 var keyIterator = m.keys();
351 assertEquals(keyIterator.next().value, 'key1');
352 assertEquals(keyIterator.next().value, 'key2');
353 assertEquals(keyIterator.next().done, true);
354
355 if (typeof(Symbol) != 'undefined') {
356 var keyIterable = m.keys()[Symbol.iterator]();
357 assertEquals(keyIterable.next().value, 'key1');
358 assertEquals(keyIterable.next().value, 'key2');
359 assertEquals(keyIterable.next().done, true);
360 }
361 var valueIterator = m.values();
362 assertEquals(valueIterator.next().value, 'value1');
363 assertEquals(valueIterator.next().value, 'value2');
364 assertEquals(valueIterator.next().done, true);
365
366 if (typeof(Symbol) != 'undefined') {
367 var valueIterable = m.values()[Symbol.iterator]();
368 assertEquals(valueIterable.next().value, 'value1');
369 assertEquals(valueIterable.next().value, 'value2');
370 assertEquals(valueIterable.next().done, true);
371 }
372 });
373}
374
375describe('mapsTest', function() {
376 makeTests(
377 {
378 constructor: proto.jspb.test.TestMapFields,
379 deserializeBinary: proto.jspb.test.TestMapFields.deserializeBinary
380 },
381 proto.jspb.test.MapValueMessage, '_Binary');
382 makeTests(
383 {
384 constructor: proto.jspb.test.TestMapFieldsNoBinary,
385 deserializeBinary: null
386 },
387 proto.jspb.test.MapValueMessageNoBinary, '_NoBinary');
388});