michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: Components.utils.import("resource://gre/modules/Dict.jsm"); michael@0: michael@0: /** michael@0: * Test that a few basic get, set, has and del operations work. michael@0: */ michael@0: function test_get_set_has_del() { michael@0: let dict = new Dict({foo: "bar"}); michael@0: dict.set("baz", 200); michael@0: do_check_eq(dict.get("foo"), "bar"); michael@0: do_check_eq(dict.get("baz"), 200); michael@0: do_check_true(dict.has("foo")); michael@0: do_check_true(dict.has("baz")); michael@0: // Now delete the entries michael@0: do_check_true(dict.del("foo")); michael@0: do_check_true(dict.del("baz")); michael@0: do_check_false(dict.has("foo")); michael@0: do_check_false(dict.has("baz")); michael@0: // and make sure del returns false michael@0: do_check_false(dict.del("foo")); michael@0: do_check_false(dict.del("baz")); michael@0: } michael@0: michael@0: /** michael@0: * Test that the second parameter of get (default value) works. michael@0: */ michael@0: function test_get_default() { michael@0: let dict = new Dict(); michael@0: do_check_true(dict.get("foo") === undefined); michael@0: do_check_eq(dict.get("foo", "bar"), "bar"); michael@0: } michael@0: michael@0: /** michael@0: * Test that there are no collisions with builtins. michael@0: */ michael@0: function test_collisions_with_builtins() { michael@0: let dict = new Dict(); michael@0: // First check that a new dictionary doesn't already have builtins. michael@0: do_check_false(dict.has("toString")); michael@0: do_check_false(dict.has("watch")); michael@0: do_check_false(dict.has("__proto__")); michael@0: michael@0: // Add elements in an attempt to collide with builtins. michael@0: dict.set("toString", "toString"); michael@0: dict.set("watch", "watch"); michael@0: // This is a little evil. We set __proto__ to an object to try to make it look michael@0: // up the prototype chain. michael@0: dict.set("__proto__", {prototest: "prototest"}); michael@0: michael@0: // Now check that the entries exist. michael@0: do_check_true(dict.has("toString")); michael@0: do_check_true(dict.has("watch")); michael@0: do_check_true(dict.has("__proto__")); michael@0: // ...and that we aren't looking up the prototype chain. michael@0: do_check_false(dict.has("prototest")); michael@0: } michael@0: michael@0: /** michael@0: * Test that the "count" property works as expected. michael@0: */ michael@0: function test_count() { michael@0: let dict = new Dict({foo: "bar"}); michael@0: do_check_eq(dict.count, 1); michael@0: dict.set("baz", "quux"); michael@0: do_check_eq(dict.count, 2); michael@0: // This shouldn't change the count michael@0: dict.set("baz", "quux2"); michael@0: do_check_eq(dict.count, 2); michael@0: michael@0: do_check_true(dict.del("baz")); michael@0: do_check_eq(dict.count, 1); michael@0: // This shouldn't change the count either michael@0: do_check_false(dict.del("not")); michael@0: do_check_eq(dict.count, 1); michael@0: do_check_true(dict.del("foo")); michael@0: do_check_eq(dict.count, 0); michael@0: } michael@0: michael@0: /** michael@0: * Test that the copy function works as expected. michael@0: */ michael@0: function test_copy() { michael@0: let obj = {}; michael@0: let dict1 = new Dict({foo: "bar", baz: obj}); michael@0: let dict2 = dict1.copy(); michael@0: do_check_eq(dict2.get("foo"), "bar"); michael@0: do_check_eq(dict2.get("baz"), obj); michael@0: // Make sure the two update independent of each other. michael@0: dict1.del("foo"); michael@0: do_check_false(dict1.has("foo")); michael@0: do_check_true(dict2.has("foo")); michael@0: dict2.set("test", 400); michael@0: do_check_true(dict2.has("test")); michael@0: do_check_false(dict1.has("test")); michael@0: michael@0: // Check that the copy is shallow and not deep. michael@0: dict1.get("baz").prop = "proptest"; michael@0: do_check_eq(dict2.get("baz").prop, "proptest"); michael@0: } michael@0: michael@0: // This is used by both test_listers and test_iterators. michael@0: function _check_lists(keys, values, items) { michael@0: do_check_eq(keys.length, 2); michael@0: do_check_true(keys.indexOf("x") != -1); michael@0: do_check_true(keys.indexOf("y") != -1); michael@0: michael@0: do_check_eq(values.length, 2); michael@0: do_check_true(values.indexOf("a") != -1); michael@0: do_check_true(values.indexOf("b") != -1); michael@0: michael@0: // This is a little more tricky -- we need to check that one of the two michael@0: // entries is ["x", "a"] and the other is ["y", "b"]. michael@0: do_check_eq(items.length, 2); michael@0: do_check_eq(items[0].length, 2); michael@0: do_check_eq(items[1].length, 2); michael@0: let ix = (items[0][0] == "x") ? 0 : 1; michael@0: let iy = (ix == 0) ? 1 : 0; michael@0: do_check_eq(items[ix][0], "x"); michael@0: do_check_eq(items[ix][1], "a"); michael@0: do_check_eq(items[iy][0], "y"); michael@0: do_check_eq(items[iy][1], "b"); michael@0: } michael@0: michael@0: /** michael@0: * Test the list functions. michael@0: */ michael@0: function test_listers() { michael@0: let dict = new Dict({"x": "a", "y": "b"}); michael@0: let keys = dict.listkeys(); michael@0: let values = dict.listvalues(); michael@0: let items = dict.listitems(); michael@0: _check_lists(keys, values, items); michael@0: } michael@0: michael@0: /** michael@0: * Test the iterator functions. michael@0: */ michael@0: function test_iterators() { michael@0: let dict = new Dict({"x": "a", "y": "b"}); michael@0: // Convert the generators to lists michael@0: let keys = [x for (x in dict.keys)]; michael@0: let values = [x for (x in dict.values)]; michael@0: let items = [x for (x in dict.items)]; michael@0: _check_lists(keys, values, items); michael@0: } michael@0: michael@0: /** michael@0: * Test that setting a property throws an exception in strict mode. michael@0: */ michael@0: function test_set_property_strict() { michael@0: "use strict"; michael@0: var dict = new Dict(); michael@0: var thrown = false; michael@0: try { michael@0: dict.foo = "bar"; michael@0: } michael@0: catch (ex) { michael@0: thrown = true; michael@0: } michael@0: do_check_true(thrown); michael@0: } michael@0: michael@0: /** michael@0: * Test that setting a property has no effect in non-strict mode. michael@0: */ michael@0: function test_set_property_non_strict() { michael@0: let dict = new Dict(); michael@0: dict.foo = "bar"; michael@0: do_check_false("foo" in dict); michael@0: let realget = dict.get; michael@0: dict.get = "baz"; michael@0: do_check_eq(dict.get, realget); michael@0: } michael@0: michael@0: /** michael@0: * Tests setting a property by a lazy getter. michael@0: */ michael@0: function test_set_property_lazy_getter() { michael@0: let thunkCalled = false; michael@0: michael@0: let setThunk = function(dict) { michael@0: thunkCalled = false; michael@0: dict.setAsLazyGetter("foo", function() { michael@0: thunkCalled = true; michael@0: return "bar"; michael@0: }); michael@0: }; michael@0: michael@0: let (dict = new Dict()) { michael@0: setThunk(dict); michael@0: michael@0: // Test that checking for the key existence does not invoke michael@0: // the getter function. michael@0: do_check_true(dict.has("foo")); michael@0: do_check_false(thunkCalled); michael@0: do_check_true(dict.isLazyGetter("foo")); michael@0: michael@0: // Calling get the first time should invoke the getter function michael@0: // and unmark the key as a lazy getter. michael@0: do_check_eq(dict.get("foo"), "bar"); michael@0: do_check_true(thunkCalled); michael@0: do_check_false(dict.isLazyGetter("foo")); michael@0: michael@0: // Calling get again should not invoke the getter function michael@0: thunkCalled = false; michael@0: do_check_eq(dict.get("foo"), "bar"); michael@0: do_check_false(thunkCalled); michael@0: do_check_false(dict.isLazyGetter("foo")); michael@0: } michael@0: michael@0: // Test that listvalues works for lazy keys. michael@0: let (dict = new Dict()) { michael@0: setThunk(dict); michael@0: do_check_true(dict.isLazyGetter("foo")); michael@0: michael@0: let (listvalues = dict.listvalues()) { michael@0: do_check_false(dict.isLazyGetter("foo")); michael@0: do_check_true(thunkCalled); michael@0: do_check_true(listvalues.length, 1); michael@0: do_check_eq(listvalues[0], "bar"); michael@0: } michael@0: michael@0: thunkCalled = false; michael@0: michael@0: // Retrieving the list again shouldn't invoke our getter. michael@0: let (listvalues = dict.listvalues()) { michael@0: do_check_false(dict.isLazyGetter("foo")); michael@0: do_check_false(thunkCalled); michael@0: do_check_true(listvalues.length, 1); michael@0: do_check_eq(listvalues[0], "bar"); michael@0: } michael@0: } michael@0: michael@0: // Test that the values iterator also works as expected. michael@0: let (dict = new Dict()) { michael@0: setThunk(dict); michael@0: let values = dict.values; michael@0: michael@0: // Our getter shouldn't be called before the iterator reaches it. michael@0: do_check_true(dict.isLazyGetter("foo")); michael@0: do_check_false(thunkCalled); michael@0: do_check_eq(values.next(), "bar"); michael@0: do_check_true(thunkCalled); michael@0: michael@0: thunkCalled = false; michael@0: do_check_false(dict.isLazyGetter("foo")); michael@0: do_check_eq(dict.get("foo"), "bar"); michael@0: do_check_false(thunkCalled); michael@0: } michael@0: } michael@0: michael@0: // This is used by both test_construct_dict_from_json_string and test_serialize_dict_to_json_string michael@0: function _sort_comp_arr(arr1,arr2){ michael@0: arr1.sort(); michael@0: arr2.sort(); michael@0: do_check_eq(arr1.toString(),arr2.toString()); michael@0: } michael@0: michael@0: /** michael@0: * Tests constructing a dictionary from a JSON string. michael@0: */ michael@0: function test_construct_dict_from_json_string() { michael@0: let d1 = new Dict({a:1, b:2, c:"foobar"}); michael@0: let d2 = new Dict(JSON.stringify(({a:1, b:2, c:"foobar"}))); michael@0: _sort_comp_arr(d1.listkeys(),d2.listkeys()); michael@0: do_check_eq(d1.get("a"), d2.get("a")); michael@0: do_check_eq(d1.get("b"), d2.get("b")); michael@0: do_check_eq(d1.get("c"), d2.get("c")); michael@0: } michael@0: michael@0: /** michael@0: * Tests serializing a dictionary to a JSON string. michael@0: */ michael@0: function test_serialize_dict_to_json_string() { michael@0: let d1 = new Dict({a:1, b:2, c:"foobar"}); michael@0: let d2 = new Dict(d1.toJSON()); michael@0: _sort_comp_arr(d1.listkeys(),d2.listkeys()); michael@0: do_check_eq(d1.get("a"), d2.get("a")); michael@0: do_check_eq(d1.get("b"), d2.get("b")); michael@0: do_check_eq(d1.get("c"), d2.get("c")); michael@0: } michael@0: michael@0: var tests = [ michael@0: test_get_set_has_del, michael@0: test_get_default, michael@0: test_collisions_with_builtins, michael@0: test_count, michael@0: test_copy, michael@0: test_listers, michael@0: test_iterators, michael@0: test_set_property_strict, michael@0: test_set_property_non_strict, michael@0: test_set_property_lazy_getter, michael@0: test_construct_dict_from_json_string, michael@0: test_serialize_dict_to_json_string michael@0: ]; michael@0: michael@0: function run_test() { michael@0: for (let [, test] in Iterator(tests)) michael@0: test(); michael@0: }