Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
godotengine
GitHub Repository: godotengine/godot
Path: blob/master/tests/core/variant/test_dictionary.h
10278 views
1
/**************************************************************************/
2
/* test_dictionary.h */
3
/**************************************************************************/
4
/* This file is part of: */
5
/* GODOT ENGINE */
6
/* https://godotengine.org */
7
/**************************************************************************/
8
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
9
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
10
/* */
11
/* Permission is hereby granted, free of charge, to any person obtaining */
12
/* a copy of this software and associated documentation files (the */
13
/* "Software"), to deal in the Software without restriction, including */
14
/* without limitation the rights to use, copy, modify, merge, publish, */
15
/* distribute, sublicense, and/or sell copies of the Software, and to */
16
/* permit persons to whom the Software is furnished to do so, subject to */
17
/* the following conditions: */
18
/* */
19
/* The above copyright notice and this permission notice shall be */
20
/* included in all copies or substantial portions of the Software. */
21
/* */
22
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
23
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
24
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
25
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
26
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
27
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
28
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
29
/**************************************************************************/
30
31
#pragma once
32
33
#include "core/variant/typed_dictionary.h"
34
#include "tests/test_macros.h"
35
36
namespace TestDictionary {
37
TEST_CASE("[Dictionary] Assignment using bracket notation ([])") {
38
Dictionary map;
39
map["Hello"] = 0;
40
CHECK(int(map["Hello"]) == 0);
41
map["Hello"] = 3;
42
CHECK(int(map["Hello"]) == 3);
43
map["World!"] = 4;
44
CHECK(int(map["World!"]) == 4);
45
46
map[StringName("HelloName")] = 6;
47
CHECK(int(map[StringName("HelloName")]) == 6);
48
CHECK(int(map.find_key(6).get_type()) == Variant::STRING_NAME);
49
map[StringName("HelloName")] = 7;
50
CHECK(int(map[StringName("HelloName")]) == 7);
51
52
// Test String and StringName are equivalent.
53
map[StringName("Hello")] = 8;
54
CHECK(int(map["Hello"]) == 8);
55
map["Hello"] = 9;
56
CHECK(int(map[StringName("Hello")]) == 9);
57
58
// Test non-string keys, since keys can be of any Variant type.
59
map[12345] = -5;
60
CHECK(int(map[12345]) == -5);
61
map[false] = 128;
62
CHECK(int(map[false]) == 128);
63
map[Vector2(10, 20)] = 30;
64
CHECK(int(map[Vector2(10, 20)]) == 30);
65
map[0] = 400;
66
CHECK(int(map[0]) == 400);
67
// Check that assigning 0 doesn't overwrite the value for `false`.
68
CHECK(int(map[false]) == 128);
69
70
// Ensure read-only maps aren't modified by non-existing keys.
71
const int length = map.size();
72
map.make_read_only();
73
CHECK(int(map["This key does not exist"].get_type()) == Variant::NIL);
74
CHECK(map.size() == length);
75
}
76
77
TEST_CASE("[Dictionary] List init") {
78
Dictionary dict{
79
{ 0, "int" },
80
{ "packed_string_array", PackedStringArray({ "array", "of", "values" }) },
81
{ "key", Dictionary({ { "nested", 200 } }) },
82
{ Vector2(), "v2" },
83
};
84
CHECK(dict.size() == 4);
85
CHECK(dict[0] == "int");
86
CHECK(PackedStringArray(dict["packed_string_array"])[2] == "values");
87
CHECK(Dictionary(dict["key"])["nested"] == Variant(200));
88
CHECK(dict[Vector2()] == "v2");
89
90
TypedDictionary<double, double> tdict{
91
{ 0.0, 1.0 },
92
{ 5.0, 2.0 },
93
};
94
CHECK_EQ(tdict[0.0], Variant(1.0));
95
CHECK_EQ(tdict[5.0], Variant(2.0));
96
}
97
98
TEST_CASE("[Dictionary] get_key_list()") {
99
Dictionary map;
100
LocalVector<Variant> keys;
101
keys = map.get_key_list();
102
CHECK(keys.is_empty());
103
map[1] = 3;
104
keys = map.get_key_list();
105
CHECK(keys.size() == 1);
106
CHECK(int(keys[0]) == 1);
107
map[2] = 4;
108
keys = map.get_key_list();
109
CHECK(keys.size() == 2);
110
}
111
112
TEST_CASE("[Dictionary] get_key_at_index()") {
113
Dictionary map;
114
map[4] = 3;
115
Variant val = map.get_key_at_index(0);
116
CHECK(int(val) == 4);
117
map[3] = 1;
118
val = map.get_key_at_index(0);
119
CHECK(int(val) == 4);
120
val = map.get_key_at_index(1);
121
CHECK(int(val) == 3);
122
}
123
124
TEST_CASE("[Dictionary] getptr()") {
125
Dictionary map;
126
map[1] = 3;
127
Variant *key = map.getptr(1);
128
CHECK(int(*key) == 3);
129
key = map.getptr(2);
130
CHECK(key == nullptr);
131
}
132
133
TEST_CASE("[Dictionary] get_valid()") {
134
Dictionary map;
135
map[1] = 3;
136
Variant val = map.get_valid(1);
137
CHECK(int(val) == 3);
138
}
139
TEST_CASE("[Dictionary] get()") {
140
Dictionary map;
141
map[1] = 3;
142
Variant val = map.get(1, -1);
143
CHECK(int(val) == 3);
144
}
145
146
TEST_CASE("[Dictionary] size(), empty() and clear()") {
147
Dictionary map;
148
CHECK(map.size() == 0);
149
CHECK(map.is_empty());
150
map[1] = 3;
151
CHECK(map.size() == 1);
152
CHECK(!map.is_empty());
153
map.clear();
154
CHECK(map.size() == 0);
155
CHECK(map.is_empty());
156
}
157
158
TEST_CASE("[Dictionary] has() and has_all()") {
159
Dictionary map;
160
CHECK(map.has(1) == false);
161
map[1] = 3;
162
CHECK(map.has(1));
163
Array keys;
164
keys.push_back(1);
165
CHECK(map.has_all(keys));
166
keys.push_back(2);
167
CHECK(map.has_all(keys) == false);
168
}
169
170
TEST_CASE("[Dictionary] keys() and values()") {
171
Dictionary map;
172
Array keys = map.keys();
173
Array values = map.values();
174
CHECK(keys.is_empty());
175
CHECK(values.is_empty());
176
map[1] = 3;
177
keys = map.keys();
178
values = map.values();
179
CHECK(int(keys[0]) == 1);
180
CHECK(int(values[0]) == 3);
181
}
182
183
TEST_CASE("[Dictionary] Duplicate dictionary") {
184
// d = {1: {1: 1}, {2: 2}: [2], [3]: 3}
185
Dictionary k2 = { { 2, 2 } };
186
Array k3 = { 3 };
187
Dictionary d = {
188
{ 1, Dictionary({ { 1, 1 } }) },
189
{ k2, Array({ 2 }) },
190
{ k3, 3 }
191
};
192
193
// Deep copy
194
Dictionary deep_d = d.duplicate(true);
195
CHECK_MESSAGE(deep_d.id() != d.id(), "Should create a new dictionary");
196
CHECK_MESSAGE(Dictionary(deep_d[1]).id() != Dictionary(d[1]).id(), "Should clone nested dictionary");
197
CHECK_MESSAGE(Array(deep_d[k2]).id() != Array(d[k2]).id(), "Should clone nested array");
198
CHECK_EQ(deep_d, d);
199
deep_d[0] = 0;
200
CHECK_NE(deep_d, d);
201
deep_d.erase(0);
202
Dictionary(deep_d[1]).operator[](0) = 0;
203
CHECK_NE(deep_d, d);
204
Dictionary(deep_d[1]).erase(0);
205
CHECK_EQ(deep_d, d);
206
// Keys should also be copied
207
k2[0] = 0;
208
CHECK_NE(deep_d, d);
209
k2.erase(0);
210
CHECK_EQ(deep_d, d);
211
k3.push_back(0);
212
CHECK_NE(deep_d, d);
213
k3.pop_back();
214
CHECK_EQ(deep_d, d);
215
216
// Shallow copy
217
Dictionary shallow_d = d.duplicate(false);
218
CHECK_MESSAGE(shallow_d.id() != d.id(), "Should create a new array");
219
CHECK_MESSAGE(Dictionary(shallow_d[1]).id() == Dictionary(d[1]).id(), "Should keep nested dictionary");
220
CHECK_MESSAGE(Array(shallow_d[k2]).id() == Array(d[k2]).id(), "Should keep nested array");
221
CHECK_EQ(shallow_d, d);
222
shallow_d[0] = 0;
223
CHECK_NE(shallow_d, d);
224
shallow_d.erase(0);
225
#if 0 // TODO: recursion in dict key currently is buggy
226
// Keys should also be shallowed
227
k2[0] = 0;
228
CHECK_EQ(shallow_d, d);
229
k2.erase(0);
230
k3.push_back(0);
231
CHECK_EQ(shallow_d, d);
232
#endif
233
}
234
235
TEST_CASE("[Dictionary] Duplicate recursive dictionary") {
236
// Self recursive
237
Dictionary d;
238
d[1] = d;
239
240
Dictionary d_shallow = d.duplicate(false);
241
CHECK_EQ(d, d_shallow);
242
243
// Deep copy of recursive dictionary endup with recursion limit and return
244
// an invalid result (multiple nested dictionaries), the point is we should
245
// not end up with a segfault and an error log should be printed
246
ERR_PRINT_OFF;
247
d.duplicate(true);
248
ERR_PRINT_ON;
249
250
// Nested recursive
251
Dictionary d1;
252
Dictionary d2;
253
d1[2] = d2;
254
d2[1] = d1;
255
256
Dictionary d1_shallow = d1.duplicate(false);
257
CHECK_EQ(d1, d1_shallow);
258
259
// Same deep copy issue as above
260
ERR_PRINT_OFF;
261
d1.duplicate(true);
262
ERR_PRINT_ON;
263
264
// Break the recursivity otherwise Dictionary teardown will leak memory
265
d.clear();
266
d1.clear();
267
d2.clear();
268
}
269
270
#if 0 // TODO: duplicate recursion in dict key is currently buggy
271
TEST_CASE("[Dictionary] Duplicate recursive dictionary on keys") {
272
// Self recursive
273
Dictionary d;
274
d[d] = d;
275
276
Dictionary d_shallow = d.duplicate(false);
277
CHECK_EQ(d, d_shallow);
278
279
// Deep copy of recursive dictionary endup with recursion limit and return
280
// an invalid result (multiple nested dictionaries), the point is we should
281
// not end up with a segfault and an error log should be printed
282
ERR_PRINT_OFF;
283
d.duplicate(true);
284
ERR_PRINT_ON;
285
286
// Nested recursive
287
Dictionary d1;
288
Dictionary d2;
289
d1[d2] = d2;
290
d2[d1] = d1;
291
292
Dictionary d1_shallow = d1.duplicate(false);
293
CHECK_EQ(d1, d1_shallow);
294
295
// Same deep copy issue as above
296
ERR_PRINT_OFF;
297
d1.duplicate(true);
298
ERR_PRINT_ON;
299
300
// Break the recursivity otherwise Dictionary teardown will leak memory
301
d.clear();
302
d1.clear();
303
d2.clear();
304
}
305
#endif
306
307
TEST_CASE("[Dictionary] Hash dictionary") {
308
// d = {1: {1: 1}, {2: 2}: [2], [3]: 3}
309
Dictionary k2 = { { 2, 2 } };
310
Array k3 = { 3 };
311
Dictionary d = {
312
{ 1, Dictionary({ { 1, 1 } }) },
313
{ k2, Array({ 2 }) },
314
{ k3, 3 }
315
};
316
uint32_t original_hash = d.hash();
317
318
// Modify dict change the hash
319
d[0] = 0;
320
CHECK_NE(d.hash(), original_hash);
321
d.erase(0);
322
CHECK_EQ(d.hash(), original_hash);
323
324
// Modify nested item change the hash
325
Dictionary(d[1]).operator[](0) = 0;
326
CHECK_NE(d.hash(), original_hash);
327
Dictionary(d[1]).erase(0);
328
Array(d[k2]).push_back(0);
329
CHECK_NE(d.hash(), original_hash);
330
Array(d[k2]).pop_back();
331
332
// Modify a key change the hash
333
k2[0] = 0;
334
CHECK_NE(d.hash(), original_hash);
335
k2.erase(0);
336
CHECK_EQ(d.hash(), original_hash);
337
k3.push_back(0);
338
CHECK_NE(d.hash(), original_hash);
339
k3.pop_back();
340
CHECK_EQ(d.hash(), original_hash);
341
342
// Duplication doesn't change the hash
343
Dictionary d2 = d.duplicate(true);
344
CHECK_EQ(d2.hash(), original_hash);
345
}
346
347
TEST_CASE("[Dictionary] Hash recursive dictionary") {
348
Dictionary d;
349
d[1] = d;
350
351
// Hash should reach recursion limit, we just make sure this doesn't blow up
352
ERR_PRINT_OFF;
353
d.hash();
354
ERR_PRINT_ON;
355
356
// Break the recursivity otherwise Dictionary teardown will leak memory
357
d.clear();
358
}
359
360
#if 0 // TODO: recursion in dict key is currently buggy
361
TEST_CASE("[Dictionary] Hash recursive dictionary on keys") {
362
Dictionary d;
363
d[d] = 1;
364
365
// Hash should reach recursion limit, we just make sure this doesn't blow up
366
ERR_PRINT_OFF;
367
d.hash();
368
ERR_PRINT_ON;
369
370
// Break the recursivity otherwise Dictionary teardown will leak memory
371
d.clear();
372
}
373
#endif
374
375
TEST_CASE("[Dictionary] Empty comparison") {
376
Dictionary d1;
377
Dictionary d2;
378
379
// test both operator== and operator!=
380
CHECK_EQ(d1, d2);
381
CHECK_FALSE(d1 != d2);
382
}
383
384
TEST_CASE("[Dictionary] Flat comparison") {
385
Dictionary d1 = { { 1, 1 } };
386
Dictionary d2 = { { 1, 1 } };
387
Dictionary other_d = { { 2, 1 } };
388
389
// test both operator== and operator!=
390
CHECK_EQ(d1, d1); // compare self
391
CHECK_FALSE(d1 != d1);
392
CHECK_EQ(d1, d2); // different equivalent arrays
393
CHECK_FALSE(d1 != d2);
394
CHECK_NE(d1, other_d); // different arrays with different content
395
CHECK_FALSE(d1 == other_d);
396
}
397
398
TEST_CASE("[Dictionary] Nested dictionary comparison") {
399
// d1 = {1: {2: {3: 4}}}
400
Dictionary d1 = { { 1, Dictionary({ { 2, Dictionary({ { 3, 4 } }) } }) } };
401
402
Dictionary d2 = d1.duplicate(true);
403
404
// other_d = {1: {2: {3: 0}}}
405
Dictionary other_d = { { 1, Dictionary({ { 2, Dictionary({ { 3, 0 } }) } }) } };
406
407
// test both operator== and operator!=
408
CHECK_EQ(d1, d1); // compare self
409
CHECK_FALSE(d1 != d1);
410
CHECK_EQ(d1, d2); // different equivalent arrays
411
CHECK_FALSE(d1 != d2);
412
CHECK_NE(d1, other_d); // different arrays with different content
413
CHECK_FALSE(d1 == other_d);
414
}
415
416
TEST_CASE("[Dictionary] Nested array comparison") {
417
// d1 = {1: [2, 3]}
418
Dictionary d1 = { { 1, { 2, 3 } } };
419
420
Dictionary d2 = d1.duplicate(true);
421
422
// other_d = {1: [2, 0]}
423
Dictionary other_d = { { 1, { 2, 0 } } };
424
425
// test both operator== and operator!=
426
CHECK_EQ(d1, d1); // compare self
427
CHECK_FALSE(d1 != d1);
428
CHECK_EQ(d1, d2); // different equivalent arrays
429
CHECK_FALSE(d1 != d2);
430
CHECK_NE(d1, other_d); // different arrays with different content
431
CHECK_FALSE(d1 == other_d);
432
}
433
434
TEST_CASE("[Dictionary] Recursive comparison") {
435
Dictionary d1;
436
d1[1] = d1;
437
438
Dictionary d2;
439
d2[1] = d2;
440
441
// Comparison should reach recursion limit
442
ERR_PRINT_OFF;
443
CHECK_EQ(d1, d2);
444
CHECK_FALSE(d1 != d2);
445
ERR_PRINT_ON;
446
447
d1[2] = 2;
448
d2[2] = 2;
449
450
// Comparison should reach recursion limit
451
ERR_PRINT_OFF;
452
CHECK_EQ(d1, d2);
453
CHECK_FALSE(d1 != d2);
454
ERR_PRINT_ON;
455
456
d1[3] = 3;
457
d2[3] = 0;
458
459
// Comparison should reach recursion limit
460
ERR_PRINT_OFF;
461
CHECK_NE(d1, d2);
462
CHECK_FALSE(d1 == d2);
463
ERR_PRINT_ON;
464
465
// Break the recursivity otherwise Dictionary teardown will leak memory
466
d1.clear();
467
d2.clear();
468
}
469
470
#if 0 // TODO: recursion in dict key is currently buggy
471
TEST_CASE("[Dictionary] Recursive comparison on keys") {
472
Dictionary d1;
473
// Hash computation should reach recursion limit
474
ERR_PRINT_OFF;
475
d1[d1] = 1;
476
ERR_PRINT_ON;
477
478
Dictionary d2;
479
// Hash computation should reach recursion limit
480
ERR_PRINT_OFF;
481
d2[d2] = 1;
482
ERR_PRINT_ON;
483
484
// Comparison should reach recursion limit
485
ERR_PRINT_OFF;
486
CHECK_EQ(d1, d2);
487
CHECK_FALSE(d1 != d2);
488
ERR_PRINT_ON;
489
490
d1[2] = 2;
491
d2[2] = 2;
492
493
// Comparison should reach recursion limit
494
ERR_PRINT_OFF;
495
CHECK_EQ(d1, d2);
496
CHECK_FALSE(d1 != d2);
497
ERR_PRINT_ON;
498
499
d1[3] = 3;
500
d2[3] = 0;
501
502
// Comparison should reach recursion limit
503
ERR_PRINT_OFF;
504
CHECK_NE(d1, d2);
505
CHECK_FALSE(d1 == d2);
506
ERR_PRINT_ON;
507
508
// Break the recursivity otherwise Dictionary teardown will leak memory
509
d1.clear();
510
d2.clear();
511
}
512
#endif
513
514
TEST_CASE("[Dictionary] Recursive self comparison") {
515
Dictionary d1;
516
Dictionary d2;
517
d1[1] = d2;
518
d2[1] = d1;
519
520
CHECK_EQ(d1, d1);
521
CHECK_FALSE(d1 != d1);
522
523
// Break the recursivity otherwise Dictionary teardown will leak memory
524
d1.clear();
525
d2.clear();
526
}
527
528
TEST_CASE("[Dictionary] Order and find") {
529
Dictionary d;
530
d[4] = "four";
531
d[8] = "eight";
532
d[12] = "twelve";
533
d["4"] = "four";
534
535
Array keys = { 4, 8, 12, "4" };
536
537
CHECK_EQ(d.keys(), keys);
538
CHECK_EQ(d.find_key("four"), Variant(4));
539
CHECK_EQ(d.find_key("does not exist"), Variant());
540
}
541
542
TEST_CASE("[Dictionary] Typed copying") {
543
TypedDictionary<int, int> d1;
544
d1[0] = 1;
545
546
TypedDictionary<double, double> d2;
547
d2[0] = 1.0;
548
549
Dictionary d3 = d1;
550
TypedDictionary<int, int> d4 = d3;
551
552
Dictionary d5 = d2;
553
TypedDictionary<int, int> d6 = d5;
554
555
d3[0] = 2;
556
d4[0] = 3;
557
558
// Same typed TypedDictionary should be shared.
559
CHECK_EQ(d1[0], Variant(3));
560
CHECK_EQ(d3[0], Variant(3));
561
CHECK_EQ(d4[0], Variant(3));
562
563
d5[0] = 2.0;
564
d6[0] = 3.0;
565
566
// Different typed TypedDictionary should not be shared.
567
CHECK_EQ(d2[0], Variant(2.0));
568
CHECK_EQ(d5[0], Variant(2.0));
569
CHECK_EQ(d6[0], Variant(3.0));
570
571
d1.clear();
572
d2.clear();
573
d3.clear();
574
d4.clear();
575
d5.clear();
576
d6.clear();
577
}
578
579
TEST_CASE("[Dictionary] Iteration") {
580
Dictionary a1 = { { 1, 2 }, { 3, 4 }, { 5, 6 } };
581
Dictionary a2 = { { 1, 2 }, { 3, 4 }, { 5, 6 } };
582
583
int idx = 0;
584
585
for (const KeyValue<Variant, Variant> &kv : (const Dictionary &)a1) {
586
CHECK_EQ(int(a2[kv.key]), int(kv.value));
587
idx++;
588
}
589
590
CHECK_EQ(idx, a1.size());
591
592
a1.clear();
593
a2.clear();
594
}
595
596
TEST_CASE("[Dictionary] Object value init") {
597
Object *a = memnew(Object);
598
Object *b = memnew(Object);
599
TypedDictionary<double, Object *> tdict = {
600
{ 0.0, a },
601
{ 5.0, b },
602
};
603
CHECK_EQ(tdict[0.0], Variant(a));
604
CHECK_EQ(tdict[5.0], Variant(b));
605
memdelete(a);
606
memdelete(b);
607
}
608
609
TEST_CASE("[Dictionary] RefCounted value init") {
610
Ref<RefCounted> a = memnew(RefCounted);
611
Ref<RefCounted> b = memnew(RefCounted);
612
TypedDictionary<double, Ref<RefCounted>> tdict = {
613
{ 0.0, a },
614
{ 5.0, b },
615
};
616
CHECK_EQ(tdict[0.0], Variant(a));
617
CHECK_EQ(tdict[5.0], Variant(b));
618
}
619
620
} // namespace TestDictionary
621
622