Path: blob/master/tests/core/variant/test_dictionary.h
10278 views
/**************************************************************************/1/* test_dictionary.h */2/**************************************************************************/3/* This file is part of: */4/* GODOT ENGINE */5/* https://godotengine.org */6/**************************************************************************/7/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */8/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */9/* */10/* Permission is hereby granted, free of charge, to any person obtaining */11/* a copy of this software and associated documentation files (the */12/* "Software"), to deal in the Software without restriction, including */13/* without limitation the rights to use, copy, modify, merge, publish, */14/* distribute, sublicense, and/or sell copies of the Software, and to */15/* permit persons to whom the Software is furnished to do so, subject to */16/* the following conditions: */17/* */18/* The above copyright notice and this permission notice shall be */19/* included in all copies or substantial portions of the Software. */20/* */21/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */22/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */23/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */24/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */25/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */26/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */27/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */28/**************************************************************************/2930#pragma once3132#include "core/variant/typed_dictionary.h"33#include "tests/test_macros.h"3435namespace TestDictionary {36TEST_CASE("[Dictionary] Assignment using bracket notation ([])") {37Dictionary map;38map["Hello"] = 0;39CHECK(int(map["Hello"]) == 0);40map["Hello"] = 3;41CHECK(int(map["Hello"]) == 3);42map["World!"] = 4;43CHECK(int(map["World!"]) == 4);4445map[StringName("HelloName")] = 6;46CHECK(int(map[StringName("HelloName")]) == 6);47CHECK(int(map.find_key(6).get_type()) == Variant::STRING_NAME);48map[StringName("HelloName")] = 7;49CHECK(int(map[StringName("HelloName")]) == 7);5051// Test String and StringName are equivalent.52map[StringName("Hello")] = 8;53CHECK(int(map["Hello"]) == 8);54map["Hello"] = 9;55CHECK(int(map[StringName("Hello")]) == 9);5657// Test non-string keys, since keys can be of any Variant type.58map[12345] = -5;59CHECK(int(map[12345]) == -5);60map[false] = 128;61CHECK(int(map[false]) == 128);62map[Vector2(10, 20)] = 30;63CHECK(int(map[Vector2(10, 20)]) == 30);64map[0] = 400;65CHECK(int(map[0]) == 400);66// Check that assigning 0 doesn't overwrite the value for `false`.67CHECK(int(map[false]) == 128);6869// Ensure read-only maps aren't modified by non-existing keys.70const int length = map.size();71map.make_read_only();72CHECK(int(map["This key does not exist"].get_type()) == Variant::NIL);73CHECK(map.size() == length);74}7576TEST_CASE("[Dictionary] List init") {77Dictionary dict{78{ 0, "int" },79{ "packed_string_array", PackedStringArray({ "array", "of", "values" }) },80{ "key", Dictionary({ { "nested", 200 } }) },81{ Vector2(), "v2" },82};83CHECK(dict.size() == 4);84CHECK(dict[0] == "int");85CHECK(PackedStringArray(dict["packed_string_array"])[2] == "values");86CHECK(Dictionary(dict["key"])["nested"] == Variant(200));87CHECK(dict[Vector2()] == "v2");8889TypedDictionary<double, double> tdict{90{ 0.0, 1.0 },91{ 5.0, 2.0 },92};93CHECK_EQ(tdict[0.0], Variant(1.0));94CHECK_EQ(tdict[5.0], Variant(2.0));95}9697TEST_CASE("[Dictionary] get_key_list()") {98Dictionary map;99LocalVector<Variant> keys;100keys = map.get_key_list();101CHECK(keys.is_empty());102map[1] = 3;103keys = map.get_key_list();104CHECK(keys.size() == 1);105CHECK(int(keys[0]) == 1);106map[2] = 4;107keys = map.get_key_list();108CHECK(keys.size() == 2);109}110111TEST_CASE("[Dictionary] get_key_at_index()") {112Dictionary map;113map[4] = 3;114Variant val = map.get_key_at_index(0);115CHECK(int(val) == 4);116map[3] = 1;117val = map.get_key_at_index(0);118CHECK(int(val) == 4);119val = map.get_key_at_index(1);120CHECK(int(val) == 3);121}122123TEST_CASE("[Dictionary] getptr()") {124Dictionary map;125map[1] = 3;126Variant *key = map.getptr(1);127CHECK(int(*key) == 3);128key = map.getptr(2);129CHECK(key == nullptr);130}131132TEST_CASE("[Dictionary] get_valid()") {133Dictionary map;134map[1] = 3;135Variant val = map.get_valid(1);136CHECK(int(val) == 3);137}138TEST_CASE("[Dictionary] get()") {139Dictionary map;140map[1] = 3;141Variant val = map.get(1, -1);142CHECK(int(val) == 3);143}144145TEST_CASE("[Dictionary] size(), empty() and clear()") {146Dictionary map;147CHECK(map.size() == 0);148CHECK(map.is_empty());149map[1] = 3;150CHECK(map.size() == 1);151CHECK(!map.is_empty());152map.clear();153CHECK(map.size() == 0);154CHECK(map.is_empty());155}156157TEST_CASE("[Dictionary] has() and has_all()") {158Dictionary map;159CHECK(map.has(1) == false);160map[1] = 3;161CHECK(map.has(1));162Array keys;163keys.push_back(1);164CHECK(map.has_all(keys));165keys.push_back(2);166CHECK(map.has_all(keys) == false);167}168169TEST_CASE("[Dictionary] keys() and values()") {170Dictionary map;171Array keys = map.keys();172Array values = map.values();173CHECK(keys.is_empty());174CHECK(values.is_empty());175map[1] = 3;176keys = map.keys();177values = map.values();178CHECK(int(keys[0]) == 1);179CHECK(int(values[0]) == 3);180}181182TEST_CASE("[Dictionary] Duplicate dictionary") {183// d = {1: {1: 1}, {2: 2}: [2], [3]: 3}184Dictionary k2 = { { 2, 2 } };185Array k3 = { 3 };186Dictionary d = {187{ 1, Dictionary({ { 1, 1 } }) },188{ k2, Array({ 2 }) },189{ k3, 3 }190};191192// Deep copy193Dictionary deep_d = d.duplicate(true);194CHECK_MESSAGE(deep_d.id() != d.id(), "Should create a new dictionary");195CHECK_MESSAGE(Dictionary(deep_d[1]).id() != Dictionary(d[1]).id(), "Should clone nested dictionary");196CHECK_MESSAGE(Array(deep_d[k2]).id() != Array(d[k2]).id(), "Should clone nested array");197CHECK_EQ(deep_d, d);198deep_d[0] = 0;199CHECK_NE(deep_d, d);200deep_d.erase(0);201Dictionary(deep_d[1]).operator[](0) = 0;202CHECK_NE(deep_d, d);203Dictionary(deep_d[1]).erase(0);204CHECK_EQ(deep_d, d);205// Keys should also be copied206k2[0] = 0;207CHECK_NE(deep_d, d);208k2.erase(0);209CHECK_EQ(deep_d, d);210k3.push_back(0);211CHECK_NE(deep_d, d);212k3.pop_back();213CHECK_EQ(deep_d, d);214215// Shallow copy216Dictionary shallow_d = d.duplicate(false);217CHECK_MESSAGE(shallow_d.id() != d.id(), "Should create a new array");218CHECK_MESSAGE(Dictionary(shallow_d[1]).id() == Dictionary(d[1]).id(), "Should keep nested dictionary");219CHECK_MESSAGE(Array(shallow_d[k2]).id() == Array(d[k2]).id(), "Should keep nested array");220CHECK_EQ(shallow_d, d);221shallow_d[0] = 0;222CHECK_NE(shallow_d, d);223shallow_d.erase(0);224#if 0 // TODO: recursion in dict key currently is buggy225// Keys should also be shallowed226k2[0] = 0;227CHECK_EQ(shallow_d, d);228k2.erase(0);229k3.push_back(0);230CHECK_EQ(shallow_d, d);231#endif232}233234TEST_CASE("[Dictionary] Duplicate recursive dictionary") {235// Self recursive236Dictionary d;237d[1] = d;238239Dictionary d_shallow = d.duplicate(false);240CHECK_EQ(d, d_shallow);241242// Deep copy of recursive dictionary endup with recursion limit and return243// an invalid result (multiple nested dictionaries), the point is we should244// not end up with a segfault and an error log should be printed245ERR_PRINT_OFF;246d.duplicate(true);247ERR_PRINT_ON;248249// Nested recursive250Dictionary d1;251Dictionary d2;252d1[2] = d2;253d2[1] = d1;254255Dictionary d1_shallow = d1.duplicate(false);256CHECK_EQ(d1, d1_shallow);257258// Same deep copy issue as above259ERR_PRINT_OFF;260d1.duplicate(true);261ERR_PRINT_ON;262263// Break the recursivity otherwise Dictionary teardown will leak memory264d.clear();265d1.clear();266d2.clear();267}268269#if 0 // TODO: duplicate recursion in dict key is currently buggy270TEST_CASE("[Dictionary] Duplicate recursive dictionary on keys") {271// Self recursive272Dictionary d;273d[d] = d;274275Dictionary d_shallow = d.duplicate(false);276CHECK_EQ(d, d_shallow);277278// Deep copy of recursive dictionary endup with recursion limit and return279// an invalid result (multiple nested dictionaries), the point is we should280// not end up with a segfault and an error log should be printed281ERR_PRINT_OFF;282d.duplicate(true);283ERR_PRINT_ON;284285// Nested recursive286Dictionary d1;287Dictionary d2;288d1[d2] = d2;289d2[d1] = d1;290291Dictionary d1_shallow = d1.duplicate(false);292CHECK_EQ(d1, d1_shallow);293294// Same deep copy issue as above295ERR_PRINT_OFF;296d1.duplicate(true);297ERR_PRINT_ON;298299// Break the recursivity otherwise Dictionary teardown will leak memory300d.clear();301d1.clear();302d2.clear();303}304#endif305306TEST_CASE("[Dictionary] Hash dictionary") {307// d = {1: {1: 1}, {2: 2}: [2], [3]: 3}308Dictionary k2 = { { 2, 2 } };309Array k3 = { 3 };310Dictionary d = {311{ 1, Dictionary({ { 1, 1 } }) },312{ k2, Array({ 2 }) },313{ k3, 3 }314};315uint32_t original_hash = d.hash();316317// Modify dict change the hash318d[0] = 0;319CHECK_NE(d.hash(), original_hash);320d.erase(0);321CHECK_EQ(d.hash(), original_hash);322323// Modify nested item change the hash324Dictionary(d[1]).operator[](0) = 0;325CHECK_NE(d.hash(), original_hash);326Dictionary(d[1]).erase(0);327Array(d[k2]).push_back(0);328CHECK_NE(d.hash(), original_hash);329Array(d[k2]).pop_back();330331// Modify a key change the hash332k2[0] = 0;333CHECK_NE(d.hash(), original_hash);334k2.erase(0);335CHECK_EQ(d.hash(), original_hash);336k3.push_back(0);337CHECK_NE(d.hash(), original_hash);338k3.pop_back();339CHECK_EQ(d.hash(), original_hash);340341// Duplication doesn't change the hash342Dictionary d2 = d.duplicate(true);343CHECK_EQ(d2.hash(), original_hash);344}345346TEST_CASE("[Dictionary] Hash recursive dictionary") {347Dictionary d;348d[1] = d;349350// Hash should reach recursion limit, we just make sure this doesn't blow up351ERR_PRINT_OFF;352d.hash();353ERR_PRINT_ON;354355// Break the recursivity otherwise Dictionary teardown will leak memory356d.clear();357}358359#if 0 // TODO: recursion in dict key is currently buggy360TEST_CASE("[Dictionary] Hash recursive dictionary on keys") {361Dictionary d;362d[d] = 1;363364// Hash should reach recursion limit, we just make sure this doesn't blow up365ERR_PRINT_OFF;366d.hash();367ERR_PRINT_ON;368369// Break the recursivity otherwise Dictionary teardown will leak memory370d.clear();371}372#endif373374TEST_CASE("[Dictionary] Empty comparison") {375Dictionary d1;376Dictionary d2;377378// test both operator== and operator!=379CHECK_EQ(d1, d2);380CHECK_FALSE(d1 != d2);381}382383TEST_CASE("[Dictionary] Flat comparison") {384Dictionary d1 = { { 1, 1 } };385Dictionary d2 = { { 1, 1 } };386Dictionary other_d = { { 2, 1 } };387388// test both operator== and operator!=389CHECK_EQ(d1, d1); // compare self390CHECK_FALSE(d1 != d1);391CHECK_EQ(d1, d2); // different equivalent arrays392CHECK_FALSE(d1 != d2);393CHECK_NE(d1, other_d); // different arrays with different content394CHECK_FALSE(d1 == other_d);395}396397TEST_CASE("[Dictionary] Nested dictionary comparison") {398// d1 = {1: {2: {3: 4}}}399Dictionary d1 = { { 1, Dictionary({ { 2, Dictionary({ { 3, 4 } }) } }) } };400401Dictionary d2 = d1.duplicate(true);402403// other_d = {1: {2: {3: 0}}}404Dictionary other_d = { { 1, Dictionary({ { 2, Dictionary({ { 3, 0 } }) } }) } };405406// test both operator== and operator!=407CHECK_EQ(d1, d1); // compare self408CHECK_FALSE(d1 != d1);409CHECK_EQ(d1, d2); // different equivalent arrays410CHECK_FALSE(d1 != d2);411CHECK_NE(d1, other_d); // different arrays with different content412CHECK_FALSE(d1 == other_d);413}414415TEST_CASE("[Dictionary] Nested array comparison") {416// d1 = {1: [2, 3]}417Dictionary d1 = { { 1, { 2, 3 } } };418419Dictionary d2 = d1.duplicate(true);420421// other_d = {1: [2, 0]}422Dictionary other_d = { { 1, { 2, 0 } } };423424// test both operator== and operator!=425CHECK_EQ(d1, d1); // compare self426CHECK_FALSE(d1 != d1);427CHECK_EQ(d1, d2); // different equivalent arrays428CHECK_FALSE(d1 != d2);429CHECK_NE(d1, other_d); // different arrays with different content430CHECK_FALSE(d1 == other_d);431}432433TEST_CASE("[Dictionary] Recursive comparison") {434Dictionary d1;435d1[1] = d1;436437Dictionary d2;438d2[1] = d2;439440// Comparison should reach recursion limit441ERR_PRINT_OFF;442CHECK_EQ(d1, d2);443CHECK_FALSE(d1 != d2);444ERR_PRINT_ON;445446d1[2] = 2;447d2[2] = 2;448449// Comparison should reach recursion limit450ERR_PRINT_OFF;451CHECK_EQ(d1, d2);452CHECK_FALSE(d1 != d2);453ERR_PRINT_ON;454455d1[3] = 3;456d2[3] = 0;457458// Comparison should reach recursion limit459ERR_PRINT_OFF;460CHECK_NE(d1, d2);461CHECK_FALSE(d1 == d2);462ERR_PRINT_ON;463464// Break the recursivity otherwise Dictionary teardown will leak memory465d1.clear();466d2.clear();467}468469#if 0 // TODO: recursion in dict key is currently buggy470TEST_CASE("[Dictionary] Recursive comparison on keys") {471Dictionary d1;472// Hash computation should reach recursion limit473ERR_PRINT_OFF;474d1[d1] = 1;475ERR_PRINT_ON;476477Dictionary d2;478// Hash computation should reach recursion limit479ERR_PRINT_OFF;480d2[d2] = 1;481ERR_PRINT_ON;482483// Comparison should reach recursion limit484ERR_PRINT_OFF;485CHECK_EQ(d1, d2);486CHECK_FALSE(d1 != d2);487ERR_PRINT_ON;488489d1[2] = 2;490d2[2] = 2;491492// Comparison should reach recursion limit493ERR_PRINT_OFF;494CHECK_EQ(d1, d2);495CHECK_FALSE(d1 != d2);496ERR_PRINT_ON;497498d1[3] = 3;499d2[3] = 0;500501// Comparison should reach recursion limit502ERR_PRINT_OFF;503CHECK_NE(d1, d2);504CHECK_FALSE(d1 == d2);505ERR_PRINT_ON;506507// Break the recursivity otherwise Dictionary teardown will leak memory508d1.clear();509d2.clear();510}511#endif512513TEST_CASE("[Dictionary] Recursive self comparison") {514Dictionary d1;515Dictionary d2;516d1[1] = d2;517d2[1] = d1;518519CHECK_EQ(d1, d1);520CHECK_FALSE(d1 != d1);521522// Break the recursivity otherwise Dictionary teardown will leak memory523d1.clear();524d2.clear();525}526527TEST_CASE("[Dictionary] Order and find") {528Dictionary d;529d[4] = "four";530d[8] = "eight";531d[12] = "twelve";532d["4"] = "four";533534Array keys = { 4, 8, 12, "4" };535536CHECK_EQ(d.keys(), keys);537CHECK_EQ(d.find_key("four"), Variant(4));538CHECK_EQ(d.find_key("does not exist"), Variant());539}540541TEST_CASE("[Dictionary] Typed copying") {542TypedDictionary<int, int> d1;543d1[0] = 1;544545TypedDictionary<double, double> d2;546d2[0] = 1.0;547548Dictionary d3 = d1;549TypedDictionary<int, int> d4 = d3;550551Dictionary d5 = d2;552TypedDictionary<int, int> d6 = d5;553554d3[0] = 2;555d4[0] = 3;556557// Same typed TypedDictionary should be shared.558CHECK_EQ(d1[0], Variant(3));559CHECK_EQ(d3[0], Variant(3));560CHECK_EQ(d4[0], Variant(3));561562d5[0] = 2.0;563d6[0] = 3.0;564565// Different typed TypedDictionary should not be shared.566CHECK_EQ(d2[0], Variant(2.0));567CHECK_EQ(d5[0], Variant(2.0));568CHECK_EQ(d6[0], Variant(3.0));569570d1.clear();571d2.clear();572d3.clear();573d4.clear();574d5.clear();575d6.clear();576}577578TEST_CASE("[Dictionary] Iteration") {579Dictionary a1 = { { 1, 2 }, { 3, 4 }, { 5, 6 } };580Dictionary a2 = { { 1, 2 }, { 3, 4 }, { 5, 6 } };581582int idx = 0;583584for (const KeyValue<Variant, Variant> &kv : (const Dictionary &)a1) {585CHECK_EQ(int(a2[kv.key]), int(kv.value));586idx++;587}588589CHECK_EQ(idx, a1.size());590591a1.clear();592a2.clear();593}594595TEST_CASE("[Dictionary] Object value init") {596Object *a = memnew(Object);597Object *b = memnew(Object);598TypedDictionary<double, Object *> tdict = {599{ 0.0, a },600{ 5.0, b },601};602CHECK_EQ(tdict[0.0], Variant(a));603CHECK_EQ(tdict[5.0], Variant(b));604memdelete(a);605memdelete(b);606}607608TEST_CASE("[Dictionary] RefCounted value init") {609Ref<RefCounted> a = memnew(RefCounted);610Ref<RefCounted> b = memnew(RefCounted);611TypedDictionary<double, Ref<RefCounted>> tdict = {612{ 0.0, a },613{ 5.0, b },614};615CHECK_EQ(tdict[0.0], Variant(a));616CHECK_EQ(tdict[5.0], Variant(b));617}618619} // namespace TestDictionary620621622