// Copyright 2007 The Closure Library Authors. All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS-IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. goog.provide('goog.structs.TrieTest'); goog.setTestOnly('goog.structs.TrieTest'); goog.require('goog.object'); goog.require('goog.structs'); goog.require('goog.structs.Trie'); goog.require('goog.testing.jsunit'); function makeTrie() { var trie = new goog.structs.Trie(); trie.add('hello', 1); trie.add('hi', 'howdy'); trie.add('', 'an empty string key'); trie.add('empty value', ''); trie.add('zero', 0); trie.add('object', {}); trie.add('null', null); trie.add('hello, world', 2); trie.add('world', {}); return trie; } function checkTrie(trie) { assertEquals('get, should be 1', trie.get('hello'), 1); assertEquals('get, should be "howdy"', trie.get('hi'), 'howdy'); assertEquals( 'get, should be "an empty string key"', trie.get(''), 'an empty string key'); assertEquals('get, should be ""', trie.get('empty value'), ''); assertEquals('get, should be ""', typeof trie.get('empty value'), 'string'); assertEquals('get, should be an object', typeof trie.get('object'), 'object'); assertEquals('get, should be 0', trie.get('zero'), 0); assertEquals('get "null", should be null', trie.get('null'), null); assertEquals('get, should be 2', trie.get('hello, world'), 2); assertEquals('get, should be an object', typeof trie.get('world'), 'object'); } function testTrieFormation() { var t = makeTrie(); checkTrie(t); } function testFailureOfMultipleAdds() { var t = new goog.structs.Trie(); t.add('hello', 'testing'); assertThrows('Error should be thrown when same key added twice.', function() { t.add('hello', 'test'); }); t = new goog.structs.Trie(); t.add('null', null); assertThrows('Error should be thrown when same key added twice.', function() { t.add('null', 'hi!'); }); t = new goog.structs.Trie(); t.add('null', 'blah'); assertThrows('Error should be thrown when same key added twice.', function() { t.add('null', null); }); } function testTrieClone() { var trieOne = makeTrie(); var trieTwo = new goog.structs.Trie(trieOne); checkTrie(trieTwo); } function testTrieFromObject() { var someObject = { 'hello': 1, 'hi': 'howdy', '': 'an empty string key', 'empty value': '', 'object': {}, 'zero': 0, 'null': null, 'hello, world': 2, 'world': {} }; var trie = new goog.structs.Trie(someObject); checkTrie(trie); } function testTrieGetValues() { var trie = makeTrie(); var values = trie.getValues(); assertTrue( 'getValues, should contain "howdy"', goog.object.contains(values, 'howdy')); assertTrue('getValues, should contain 1', goog.object.contains(values, 1)); assertTrue('getValues, should contain 0', goog.object.contains(values, 0)); assertTrue('getValues, should contain ""', goog.object.contains(values, '')); assertTrue( 'getValues, should contain null', goog.object.contains(values, null)); assertEquals( 'goog.structs.getCount(getValues()) should be 9', goog.structs.getCount(values), 9); } function testTrieGetKeys() { var trie = makeTrie(); var keys = trie.getKeys(); assertTrue( 'getKeys, should contain "hello"', goog.object.contains(keys, 'hello')); assertTrue( 'getKeys, should contain "empty value"', goog.object.contains(keys, 'empty value')); assertTrue('getKeys, should contain ""', goog.object.contains(keys, '')); assertTrue( 'getKeys, should contain "zero"', goog.object.contains(keys, 'zero')); assertEquals( 'goog.structs.getCount(getKeys()) should be 9', goog.structs.getCount(keys), 9); } function testTrieCount() { var trieOne = makeTrie(); var trieTwo = new goog.structs.Trie(); assertEquals('count, should be 9', trieOne.getCount(), 9); assertEquals('count, should be 0', trieTwo.getCount(), 0); } function testRemoveKeyFromTrie() { var trie = new goog.structs.Trie(); trie.add('key1', 'value1'); trie.add('key2', 'value2'); trie.add('ke', 'value3'); trie.add('zero', 0); trie.remove('key2'); assertEquals('get "key1", should be "value1"', trie.get('key1'), 'value1'); assertUndefined('get "key2", should be undefined', trie.get('key2')); trie.remove('zero'); assertUndefined('get "zero", should be undefined', trie.get('zero')); trie.remove('ke'); assertUndefined('get "ke", should be undefined', trie.get('ke')); assertEquals('get "key1", should be "value1"', trie.get('key1'), 'value1'); trie.add('a', 'value4'); assertTrue( 'testing internal structure, a should be a child', 'a' in trie.childNodes_); trie.remove('a'); assertFalse( 'testing internal structure, a should no longer be a child', 'a' in trie.childNodes_); trie.add('xyza', 'value'); trie.remove('xyza', 'value'); assertFalse('Should not have "x"', 'x' in trie.childNodes_); trie.add('xyza', null); assertTrue('Should have "x"', 'x' in trie.childNodes_); trie.remove('xyza'); assertFalse('Should not have "x"', 'x' in trie.childNodes_); trie.add('xyza', 'value'); trie.add('xb', 'value'); trie.remove('xyza'); assertTrue('get "x" should be defined', 'x' in trie.childNodes_); assertFalse( 'get "y" should be undefined', 'y' in trie.childNodes_['x'].childNodes_); trie.add('akey', 'value1'); trie.add('akey1', 'value2'); trie.remove('akey1'); assertEquals('get "akey", should be "value1"', 'value1', trie.get('akey')); assertUndefined('get "akey1", should be undefined', trie.get('akey1')); } function testRemoveKeyFromTrieWithNulls() { var trie = new goog.structs.Trie(); trie.add('key1', null); trie.add('key2', 'value2'); trie.add('ke', 'value3'); trie.add('zero', 0); trie.remove('key2'); assertEquals('get "key1", should be null', trie.get('key1'), null); assertUndefined('get "key2", should be undefined', trie.get('key2')); trie.remove('zero'); assertUndefined('get "zero", should be undefined', trie.get('zero')); trie.remove('ke'); assertUndefined('get "ke", should be undefined', trie.get('ke')); assertEquals('get "key1", should be null', trie.get('key1'), null); trie.add('a', 'value4'); assertTrue( 'testing internal structure, a should be a child', 'a' in trie.childNodes_); trie.remove('a'); assertFalse( 'testing internal structure, a should no longer be a child', 'a' in trie.childNodes_); trie.add('xyza', null); trie.add('xb', 'value'); trie.remove('xyza'); assertTrue('Should have "x"', 'x' in trie.childNodes_); assertFalse('Should not have "y"', 'y' in trie.childNodes_['x'].childNodes_); } function testRemoveKeyException() { var trie = new goog.structs.Trie(); trie.add('abcdefg', 'value'); trie.add('abcz', 'value'); trie.add('abc', 'value'); assertThrows( 'Remove should throw an error on removal of non-existent key', function() { trie.remove('abcdefge'); }); } function testTrieIsEmpty() { var trieOne = new goog.structs.Trie(); var trieTwo = makeTrie(); assertTrue('isEmpty, should be empty', trieOne.isEmpty()); assertFalse('isEmpty, should not be empty', trieTwo.isEmpty()); trieOne.add('', 1); assertFalse('isEmpty, should not be empty', trieTwo.isEmpty()); trieOne.remove(''); assertTrue('isEmpty, should be empty', trieOne.isEmpty()); trieOne.add('', 1); trieOne.add('a', 1); trieOne.remove('a'); assertFalse('isEmpty, should not be empty', trieOne.isEmpty()); trieOne.remove(''); assertTrue('isEmpty, should be empty', trieOne.isEmpty()); trieOne.add('', 1); trieOne.add('a', 1); trieOne.remove(''); assertFalse('isEmpty, should not be empty', trieOne.isEmpty()); trieOne.remove('a'); assertTrue('isEmpty, should be empty', trieOne.isEmpty()); } function testTrieClear() { var trie = new goog.structs.Trie(); trie.add('key1', 'value1'); trie.add('key2', 'value2'); trie.add('key3', null); trie.clear(); assertUndefined('get key1, should be undefined', trie.get('key1')); assertUndefined('get key2, should be undefined', trie.get('key2')); assertUndefined('get key3, should be undefined', trie.get('key3')); } function testTrieContainsKey() { var trie = makeTrie(); assertTrue('containsKey, should contain "hello"', trie.containsKey('hello')); assertTrue('containsKey, should contain "hi"', trie.containsKey('hi')); assertTrue('containsKey, should contain ""', trie.containsKey('')); assertTrue( 'containsKey, should contain "empty value"', trie.containsKey('empty value')); assertTrue( 'containsKey, should contain "object"', trie.containsKey('object')); assertTrue('containsKey, should contain "zero"', trie.containsKey('zero')); assertTrue('containsKey, should contain "null"', trie.containsKey('null')); assertFalse( 'containsKey, should not contain "blah"', trie.containsKey('blah')); trie.remove(''); trie.remove('hi'); trie.remove('zero'); trie.remove('null'); assertFalse( 'containsKey, should not contain "zero"', trie.containsKey('zero')); assertFalse('containsKey, should not contain ""', trie.containsKey('')); assertFalse('containsKey, should not contain "hi"', trie.containsKey('hi')); assertFalse( 'containsKey, should not contain "null"', trie.containsKey('null')); } function testTrieContainsPrefix() { // Empty trie. var trie = new goog.structs.Trie(); assertFalse('containsPrefix, should not contain ""', trie.containsPrefix('')); assertFalse( 'containsPrefix, should not contain "any"', trie.containsPrefix('any')); trie.add('key', 'value'); assertTrue('containsPrefix, should contain ""', trie.containsPrefix('')); // Non-empty trie. trie = makeTrie(); assertTrue('containsPrefix, should contain ""', trie.containsPrefix('')); assertFalse( 'containsPrefix, should not contain "blah"', trie.containsPrefix('blah')); assertTrue('containsPrefix, should contain "h"', trie.containsPrefix('h')); assertTrue( 'containsPrefix, should contain "hello"', trie.containsPrefix('hello')); assertTrue( 'containsPrefix, should contain "hello, world"', trie.containsPrefix('hello, world')); assertFalse( 'containsPrefix, should not contain "hello, world!"', trie.containsPrefix('hello, world!')); assertTrue('containsPrefix, should contain "nu"', trie.containsPrefix('nu')); assertTrue( 'containsPrefix, should contain "null"', trie.containsPrefix('null')); assertTrue( 'containsPrefix, should contain "empty value"', trie.containsPrefix('empty value')); // Remove nodes. trie.remove(''); assertTrue('containsPrefix, should contain ""', trie.containsPrefix('')); trie.remove('hi'); assertTrue('containsPrefix, should contain "h"', trie.containsPrefix('h')); assertFalse( 'containsPrefix, should not contain "hi"', trie.containsPrefix('hi')); trie.remove('hello'); trie.remove('hello, world'); assertFalse( 'containsPrefix, should not contain "h"', trie.containsPrefix('h')); assertFalse( 'containsPrefix, should not contain "hello"', trie.containsPrefix('hello')); // Remove all nodes. trie.remove('empty value'); trie.remove('zero'); trie.remove('object'); trie.remove('null'); trie.remove('world'); assertFalse('containsPrefix, should not contain ""', trie.containsPrefix('')); assertFalse( 'containsPrefix, should not contain "h"', trie.containsPrefix('h')); assertFalse( 'containsPrefix, should not contain "hi"', trie.containsPrefix('hi')); // Add some new nodes. trie.add('hi', 'value'); trie.add('null', 'value'); assertTrue('containsPrefix, should contain ""', trie.containsPrefix('')); assertTrue('containsPrefix, should contain "h"', trie.containsPrefix('h')); assertTrue('containsPrefix, should contain "hi"', trie.containsPrefix('hi')); assertFalse( 'containsPrefix, should not contain "hello"', trie.containsPrefix('hello')); assertFalse( 'containsPrefix, should not contain "zero"', trie.containsPrefix('zero')); // Clear the trie. trie.clear(); assertFalse('containsPrefix, should not contain ""', trie.containsPrefix('')); assertFalse( 'containsPrefix, should not contain "h"', trie.containsPrefix('h')); assertFalse( 'containsPrefix, should not contain "hi"', trie.containsPrefix('hi')); } function testTrieContainsValue() { var trie = makeTrie(); assertTrue( 'containsValue, should be true, should contain 1', trie.containsValue(1)); assertTrue( 'containsValue, should be true, should contain "howdy"', trie.containsValue('howdy')); assertTrue( 'containsValue, should be true, should contain ""', trie.containsValue('')); assertTrue( 'containsValue, should be true, should contain 0', trie.containsValue(0)); assertTrue( 'containsValue, should be true, should contain null', trie.containsValue(null)); assertTrue( 'containsValue, should be true, should ' + 'contain "an empty string key"', trie.containsValue('an empty string key')); assertFalse( 'containsValue, should be false, should not contain "blah"', trie.containsValue('blah')); trie.remove('empty value'); trie.remove('zero'); assertFalse( 'containsValue, should be false, should not contain 0', trie.containsValue(0)); assertFalse( 'containsValue, should be false, should not contain ""', trie.containsValue('')); } function testTrieHandlingOfEmptyStrings() { var trie = new goog.structs.Trie(); assertEquals('get, should be undefined', trie.get(''), undefined); assertFalse('containsValue, should be false', trie.containsValue('')); assertFalse('containsKey, should be false', trie.containsKey('')); trie.add('', 'test'); trie.add('test2', ''); assertTrue('containsValue, should be true', trie.containsValue('')); assertTrue('containsKey, should be true', trie.containsKey('')); assertEquals('get, should be "test"', trie.get(''), 'test'); assertEquals('get, should be ""', trie.get('test2'), ''); trie.remove(''); trie.remove('test2'); assertEquals('get, should be undefined', trie.get(''), undefined); assertFalse('containsValue, should be false', trie.containsValue('')); assertFalse('containsKey, should be false', trie.containsKey('')); } function testPrefixOptionOnGetKeys() { var trie = new goog.structs.Trie(); trie.add('abcdefg', 'one'); trie.add('abcdefghijk', 'two'); trie.add('abcde', 'three'); trie.add('abcq', null); trie.add('abc', 'four'); trie.add('xyz', 'five'); assertEquals('getKeys, should be 1', trie.getKeys('xy').length, 1); assertEquals('getKeys, should be 1', trie.getKeys('xyz').length, 1); assertEquals('getKeys, should be 1', trie.getKeys('x').length, 1); assertEquals('getKeys, should be 4', trie.getKeys('abc').length, 5); assertEquals('getKeys, should be 2', trie.getKeys('abcdef').length, 2); assertEquals('getKeys, should be 0', trie.getKeys('abcdefgi').length, 0); } function testGetKeyAndPrefixes() { var trie = makeTrie(); // Note: trie has one of its keys as '' assertEquals( 'getKeyAndPrefixes, should be 2', 2, goog.object.getCount(trie.getKeyAndPrefixes('world'))); assertEquals( 'getKeyAndPrefixes, should be 2', 2, goog.object.getCount(trie.getKeyAndPrefixes('hello'))); assertEquals( 'getKeyAndPrefixes, should be 2', 2, goog.object.getCount(trie.getKeyAndPrefixes('hello,'))); assertEquals( 'getKeyAndPrefixes, should be 3', 3, goog.object.getCount(trie.getKeyAndPrefixes('hello, world'))); assertEquals( 'getKeyAndPrefixes, should be 1', 1, goog.object.getCount(trie.getKeyAndPrefixes('hell'))); } function testGetKeyAndPrefixesStartIndex() { var trie = new goog.structs.Trie(); trie.add('abcdefg', 'one'); trie.add('bcdefg', 'two'); trie.add('abcdefghijk', 'three'); trie.add('abcde', 'four'); trie.add('abcq', null); trie.add('q', null); trie.add('abc', 'five'); trie.add('xyz', 'six'); assertEquals( 'getKeyAndPrefixes, should be 3', 3, goog.object.getCount(trie.getKeyAndPrefixes('abcdefg', 0))); assertEquals( 'getKeyAndPrefixes, should be 1', 1, goog.object.getCount(trie.getKeyAndPrefixes('abcdefg', 1))); assertEquals( 'getKeyAndPrefixes, should be 1', 1, goog.object.getCount(trie.getKeyAndPrefixes('abcq', 3))); assertEquals( 'getKeyAndPrefixes, should be 0', 0, goog.object.getCount(trie.getKeyAndPrefixes('abcd', 3))); }