2 # Copyright 2014 The Chromium Authors. All rights reserved.
3 # Use of this source code is governed by a BSD-style license that can be
4 # found in the LICENSE file.
12 class ParseGperfTest(unittest
.TestCase
):
13 def testMalformedKey(self
):
14 """Tests exception is thrown at bad format."""
15 infile1
= [ '%%', '', '%%' ]
16 self
.assertRaises(make_dafsa
.InputError
, make_dafsa
.parse_gperf
, infile1
)
18 infile2
= [ '%%', 'apa,1', '%%' ]
19 self
.assertRaises(make_dafsa
.InputError
, make_dafsa
.parse_gperf
, infile2
)
21 infile3
= [ '%%', 'apa, 1', '%%' ]
22 self
.assertRaises(make_dafsa
.InputError
, make_dafsa
.parse_gperf
, infile3
)
24 def testBadValues(self
):
25 """Tests exception is thrown when value is out of range."""
26 infile1
= [ '%%', 'a, -1', '%%' ]
27 self
.assertRaises(make_dafsa
.InputError
, make_dafsa
.parse_gperf
, infile1
)
29 infile2
= [ '%%', 'a, x', '%%' ]
30 self
.assertRaises(make_dafsa
.InputError
, make_dafsa
.parse_gperf
, infile2
)
32 infile3
= [ '%%', 'a, 3', '%%' ]
33 self
.assertRaises(make_dafsa
.InputError
, make_dafsa
.parse_gperf
, infile3
)
35 infile4
= [ '%%', 'a, 6', '%%' ]
36 self
.assertRaises(make_dafsa
.InputError
, make_dafsa
.parse_gperf
, infile4
)
38 infile5
= [ '%%', 'a, 12', '%%' ]
39 self
.assertRaises(make_dafsa
.InputError
, make_dafsa
.parse_gperf
, infile5
)
42 """Tests legal values are accepted."""
43 infile1
= [ '%%', 'a, 0', '%%' ]
45 self
.assertEqual(make_dafsa
.parse_gperf(infile1
), words1
)
47 infile2
= [ '%%', 'a, 1', '%%' ]
49 self
.assertEqual(make_dafsa
.parse_gperf(infile2
), words2
)
51 infile3
= [ '%%', 'a, 2', '%%' ]
53 self
.assertEqual(make_dafsa
.parse_gperf(infile3
), words3
)
55 infile4
= [ '%%', 'a, 4', '%%' ]
57 self
.assertEqual(make_dafsa
.parse_gperf(infile4
), words4
)
59 def testOneWord(self
):
60 """Tests a single key can be parsed."""
61 infile
= [ '%%', 'apa, 1', '%%' ]
63 self
.assertEqual(make_dafsa
.parse_gperf(infile
), words
)
65 def testTwoWords(self
):
66 """Tests a sequence of keys can be parsed."""
67 infile
= [ '%%', 'apa, 1', 'bepa.com, 2', '%%' ]
68 words
= [ 'apa1', 'bepa.com2' ]
69 self
.assertEqual(make_dafsa
.parse_gperf(infile
), words
)
72 class ToDafsaTest(unittest
.TestCase
):
73 def testEmptyInput(self
):
74 """Tests exception is thrown at empty input."""
76 self
.assertRaises(make_dafsa
.InputError
, make_dafsa
.to_dafsa
, words
)
78 def testNonASCII(self
):
79 """Tests exception is thrown if illegal characters are used."""
80 words1
= ( chr(0x1F) + 'a1', )
81 self
.assertRaises(make_dafsa
.InputError
, make_dafsa
.to_dafsa
, words1
)
83 words2
= ( 'a' + chr(0x1F) + '1', )
84 self
.assertRaises(make_dafsa
.InputError
, make_dafsa
.to_dafsa
, words2
)
86 words3
= ( chr(0x80) + 'a1', )
87 self
.assertRaises(make_dafsa
.InputError
, make_dafsa
.to_dafsa
, words3
)
89 words4
= ( 'a' + chr(0x80) + '1', )
90 self
.assertRaises(make_dafsa
.InputError
, make_dafsa
.to_dafsa
, words4
)
93 """Tests a DAFSA can be created from a single character domain name."""
95 node2
= ( chr(0), [ None ] )
96 node1
= ( 'a', [ node2
] )
98 self
.assertEqual(make_dafsa
.to_dafsa(words
), source
)
101 """Tests a DAFSA can be created from a multi character domain name."""
103 node3
= ( chr(0), [ None ] )
104 node2
= ( 'b', [ node3
] )
105 node1
= ( 'a', [ node2
] )
107 self
.assertEqual(make_dafsa
.to_dafsa(words
), source
)
110 """Tests a DAFSA can be created from a sequence of domain names."""
111 words
= [ 'a0', 'b1' ]
112 node4
= ( chr(1), [ None ] )
113 node3
= ( 'b', [ node4
] )
114 node2
= ( chr(0), [ None ] )
115 node1
= ( 'a', [ node2
] )
116 source
= [ node1
, node3
]
117 self
.assertEqual(make_dafsa
.to_dafsa(words
), source
)
120 class ToWordsTest(unittest
.TestCase
):
122 """Tests the sink is exapnded to a list with an empty string."""
125 self
.assertEqual(make_dafsa
.to_words(node1
), words
)
127 def testSingleNode(self
):
128 """Tests a single node is expanded to a list with the label string."""
132 node1
= ( 'ab', [ None ] )
134 self
.assertEqual(make_dafsa
.to_words(node1
), words
)
137 """Tests a sequence of nodes are preoperly expanded."""
139 # 'ab' -> 'cd' => [ 'abcd' ]
141 node2
= ( 'cd', [ None ] )
142 node1
= ( 'ab', [ node2
] )
144 self
.assertEqual(make_dafsa
.to_words(node1
), words
)
146 def testInnerTerminator(self
):
147 """Tests a sequence with an inner terminator is expanded to two strings."""
153 node2
= ( 'b', [ None ] )
154 node1
= ( 'a', [ node2
, None ] )
155 words
= [ 'ab', 'a' ]
156 self
.assertEqual(make_dafsa
.to_words(node1
), words
)
158 def testDiamond(self
):
159 """Tests a diamond can be expanded to a word list."""
167 node4
= ( 'gh', [ None ] )
168 node3
= ( 'ef', [ node4
] )
169 node2
= ( 'cd', [ node4
] )
170 node1
= ( 'ab', [ node2
, node3
] )
171 words
= [ 'abcdgh', 'abefgh' ]
172 self
.assertEqual(make_dafsa
.to_words(node1
), words
)
175 class JoinLabelsTest(unittest
.TestCase
):
177 """Tests a single label passes unchanged."""
181 node1
= ( 'a', [ None ] )
183 self
.assertEqual(make_dafsa
.join_labels(source
), source
)
185 def testInnerTerminator(self
):
186 """Tests a sequence with an inner terminator passes unchanged."""
188 # 'a' -> 'b' 'a' -> 'b'
192 node2
= ( 'b', [ None ] )
193 node1
= ( 'a', [ node2
, None ] )
195 self
.assertEqual(make_dafsa
.join_labels(source
), source
)
197 def testLabels(self
):
198 """Tests a sequence of labels can be joined."""
202 node2
= ( 'b', [ None ] )
203 node1
= ( 'a', [ node2
] )
205 node3
= ( 'ab', [ None ] )
207 self
.assertEqual(make_dafsa
.join_labels(source1
), source2
)
209 def testCompositeLabels(self
):
210 """Tests a sequence of multi character labels can be joined."""
212 # 'ab' -> 'cd' => 'abcd'
214 node2
= ( 'cd', [ None ] )
215 node1
= ( 'ab', [ node2
] )
217 node3
= ( 'abcd', [ None ] )
219 self
.assertEqual(make_dafsa
.join_labels(source1
), source2
)
221 def testAtomicTrie(self
):
222 """Tests a trie formed DAFSA with atomic labels passes unchanged."""
230 node3
= ( 'c', [ None ] )
231 node2
= ( 'b', [ None ] )
232 node1
= ( 'a', [ node2
, node3
] )
234 self
.assertEqual(make_dafsa
.join_labels(source
), source
)
236 def testReverseAtomicTrie(self
):
237 """Tests a reverse trie formed DAFSA with atomic labels passes unchanged."""
245 node3
= ( 'c', [ None ] )
246 node2
= ( 'b', [ node3
] )
247 node1
= ( 'a', [ node3
] )
248 source
= [ node1
, node2
]
249 self
.assertEqual(make_dafsa
.join_labels(source
), source
)
251 def testChainedTrie(self
):
252 """Tests a trie formed DAFSA with chained labels can be joined."""
260 node6
= ( 'f', [ None ] )
261 node5
= ( 'e', [ node6
] )
262 node4
= ( 'd', [ None ] )
263 node3
= ( 'c', [ node4
] )
264 node2
= ( 'b', [ node3
, node5
] )
265 node1
= ( 'a', [ node2
] )
267 node9
= ( 'ef', [ None ] )
268 node8
= ( 'cd', [ None ] )
269 node7
= ( 'ab', [ node8
, node9
] )
271 self
.assertEqual(make_dafsa
.join_labels(source1
), source2
)
273 def testReverseChainedTrie(self
):
274 """Tests a reverse trie formed DAFSA with chained labels can be joined."""
282 node6
= ( 'f', [ None ] )
283 node5
= ( 'e', [ node6
] )
284 node4
= ( 'd', [ node5
] )
285 node3
= ( 'c', [ node4
] )
286 node2
= ( 'b', [ node5
] )
287 node1
= ( 'a', [ node2
] )
288 source1
= [ node1
, node3
]
289 node9
= ( 'ef', [ None ] )
290 node8
= ( 'cd', [ node9
] )
291 node7
= ( 'ab', [ node9
] )
292 source2
= [ node7
, node8
]
293 self
.assertEqual(make_dafsa
.join_labels(source1
), source2
)
296 class JoinSuffixesTest(unittest
.TestCase
):
297 def testSingleLabel(self
):
298 """Tests a single label passes unchanged."""
302 node1
= ( 'a', [ None ] )
304 self
.assertEqual(make_dafsa
.join_suffixes(source
), source
)
306 def testInnerTerminator(self
):
307 """Tests a sequence with an inner terminator passes unchanged."""
309 # 'a' -> 'b' 'a' -> 'b'
313 node2
= ( 'b', [ None ] )
314 node1
= ( 'a', [ node2
, None ] )
316 self
.assertEqual(make_dafsa
.join_suffixes(source
), source
)
318 def testDistinctTrie(self
):
319 """Tests a trie formed DAFSA with distinct labels passes unchanged."""
327 node3
= ( 'c', [ None ] )
328 node2
= ( 'b', [ None ] )
329 node1
= ( 'a', [ node2
, node3
] )
331 self
.assertEqual(make_dafsa
.join_suffixes(source
), source
)
333 def testReverseDistinctTrie(self
):
334 """Tests a reverse trie formed DAFSA with distinct labels passes unchanged.
343 node3
= ( 'c', [ None ] )
344 node2
= ( 'b', [ node3
] )
345 node1
= ( 'a', [ node3
] )
346 source
= [ node1
, node2
]
347 self
.assertEqual(make_dafsa
.join_suffixes(source
), source
)
349 def testJoinTwoHeads(self
):
350 """Tests two heads can be joined even if there is something else between."""
358 # The picture above should shows that the new version should have just one
359 # instance of the node with label 'a'.
361 node3
= ( 'a', [ None ] )
362 node2
= ( 'b', [ None ] )
363 node1
= ( 'a', [ None ] )
364 source1
= [ node1
, node2
, node3
]
365 source2
= make_dafsa
.join_suffixes(source1
)
367 # Both versions should expand to the same content.
368 self
.assertEqual(source1
, source2
)
369 # But the new version should have just one instance of 'a'.
370 self
.assertIs(source2
[0], source2
[2])
372 def testJoinTails(self
):
373 """Tests tails can be joined."""
381 node4
= ( 'c', [ None ] )
382 node3
= ( 'b', [ node4
] )
383 node2
= ( 'c', [ None ] )
384 node1
= ( 'a', [ node2
] )
385 source1
= [ node1
, node3
]
386 source2
= make_dafsa
.join_suffixes(source1
)
388 # Both versions should expand to the same content.
389 self
.assertEqual(source1
, source2
)
390 # But the new version should have just one tail.
391 self
.assertIs(source2
[0][1][0], source2
[1][1][0])
393 def testMakeRecursiveTrie(self
):
394 """Tests recursive suffix join."""
396 # 'a' -> 'e' -> 'g' 'a'
400 # 'b' -> 'e' -> 'g' 'b' \
404 # 'c' -> 'f' -> 'g' 'c' /
408 # 'd' -> 'f' -> 'g' 'd'
410 node7
= ( 'g', [ None ] )
411 node6
= ( 'f', [ node7
] )
412 node5
= ( 'e', [ node7
] )
413 node4
= ( 'd', [ node6
] )
414 node3
= ( 'c', [ node6
] )
415 node2
= ( 'b', [ node5
] )
416 node1
= ( 'a', [ node5
] )
417 source1
= [ node1
, node2
, node3
, node4
]
418 source2
= make_dafsa
.join_suffixes(source1
)
420 # Both versions should expand to the same content.
421 self
.assertEqual(source1
, source2
)
422 # But the new version should have just one 'e'.
423 self
.assertIs(source2
[0][1][0], source2
[1][1][0])
425 self
.assertIs(source2
[2][1][0], source2
[3][1][0])
427 self
.assertIs(source2
[0][1][0][1][0], source2
[2][1][0][1][0])
429 def testMakeDiamond(self
):
430 """Test we can join suffixes of a trie."""
438 node5
= ( 'd', [ None ] )
439 node4
= ( 'c', [ node5
] )
440 node3
= ( 'd', [ None ] )
441 node2
= ( 'b', [ node3
] )
442 node1
= ( 'a', [ node2
, node4
] )
444 source2
= make_dafsa
.join_suffixes(source1
)
446 # Both versions should expand to the same content.
447 self
.assertEqual(source1
, source2
)
448 # But the new version should have just one 'd'.
449 self
.assertIs(source2
[0][1][0][1][0], source2
[0][1][1][1][0])
451 def testJoinOneChild(self
):
452 """Tests that we can join some children but not all."""
466 node6
= ( 'e', [ None ] )
467 node5
= ( 'c', [ None ] )
468 node4
= ( 'b', [ node5
, node6
] )
469 node3
= ( 'd', [ None ] )
470 node2
= ( 'c', [ None ] )
471 node1
= ( 'a', [ node2
, node3
] )
472 source1
= [ node1
, node4
]
473 source2
= make_dafsa
.join_suffixes(source1
)
475 # Both versions should expand to the same content.
476 self
.assertEqual(source1
, source2
)
477 # But the new version should have just one 'c'.
478 self
.assertIs(source2
[0][1][0], source2
[1][1][0])
481 class ReverseTest(unittest
.TestCase
):
482 def testAtomicLabel(self
):
483 """Tests an atomic label passes unchanged."""
487 node1
= ( 'a', [ None ] )
489 self
.assertEqual(make_dafsa
.reverse(source
), source
)
492 """Tests that labels are reversed."""
496 node1
= ( 'ab', [ None ] )
498 node2
= ( 'ba', [ None ] )
500 self
.assertEqual(make_dafsa
.reverse(source1
), source2
)
503 """Tests that edges are reversed."""
505 # 'a' -> 'b' => 'b' -> 'a'
507 node2
= ( 'b', [ None ] )
508 node1
= ( 'a', [ node2
] )
510 node4
= ( 'a', [ None ] )
511 node3
= ( 'b', [ node4
] )
513 self
.assertEqual(make_dafsa
.reverse(source1
), source2
)
515 def testInnerTerminator(self
):
516 """Tests a sequence with an inner terminator can be reversed."""
518 # 'a' -> 'b' 'b' -> 'a'
522 node2
= ( 'b', [ None ] )
523 node1
= ( 'a', [ node2
, None ] )
525 node4
= ( 'a', [ None ] )
526 node3
= ( 'b', [ node4
] )
527 source2
= [ node3
, node4
]
528 self
.assertEqual(make_dafsa
.reverse(source1
), source2
)
530 def testAtomicTrie(self
):
531 """Tests a trie formed DAFSA can be reversed."""
539 node3
= ( 'c', [ None ] )
540 node2
= ( 'b', [ None ] )
541 node1
= ( 'a', [ node2
, node3
] )
543 node6
= ( 'a', [ None ] )
544 node5
= ( 'c', [ node6
] )
545 node4
= ( 'b', [ node6
] )
546 source2
= [ node4
, node5
]
547 self
.assertEqual(make_dafsa
.reverse(source1
), source2
)
549 def testReverseAtomicTrie(self
):
550 """Tests a reverse trie formed DAFSA can be reversed."""
558 node3
= ( 'c', [ None ] )
559 node2
= ( 'b', [ node3
] )
560 node1
= ( 'a', [ node3
] )
561 source1
= [ node1
, node2
]
562 node6
= ( 'b', [ None ] )
563 node5
= ( 'a', [ None ] )
564 node4
= ( 'c', [ node5
, node6
] )
566 self
.assertEqual(make_dafsa
.reverse(source1
), source2
)
568 def testDiamond(self
):
569 """Tests we can reverse both edges and nodes in a diamond."""
573 # 'ab' 'gh' => 'hg' 'ba'
577 node4
= ( 'gh', [ None ] )
578 node3
= ( 'ef', [ node4
] )
579 node2
= ( 'cd', [ node4
] )
580 node1
= ( 'ab', [ node2
, node3
] )
582 node8
= ( 'ba', [ None ] )
583 node7
= ( 'fe', [ node8
] )
584 node6
= ( 'dc', [ node8
] )
585 node5
= ( 'hg', [ node6
, node7
] )
587 self
.assertEqual(make_dafsa
.reverse(source1
), source2
)
590 class TopSortTest(unittest
.TestCase
):
592 """Tests a DAFSA with one node can be sorted."""
596 node1
= ( 'a', [ None ] )
599 self
.assertEqual(make_dafsa
.top_sort(source
), nodes
)
601 def testDiamond(self
):
602 """Tests nodes in a diamond can be sorted."""
610 node4
= ( 'd', [ None ] )
611 node3
= ( 'c', [ node4
] )
612 node2
= ( 'b', [ node4
] )
613 node1
= ( 'a', [ node2
, node3
] )
615 nodes
= make_dafsa
.top_sort(source
)
616 self
.assertLess(nodes
.index(node1
), nodes
.index(node2
))
617 self
.assertLess(nodes
.index(node2
), nodes
.index(node4
))
618 self
.assertLess(nodes
.index(node3
), nodes
.index(node4
))
621 class EncodePrefixTest(unittest
.TestCase
):
623 """Tests to encode a single character prefix."""
626 self
.assertEqual(make_dafsa
.encode_prefix(label
), bytes
)
629 """Tests to encode a multi character prefix."""
631 bytes
= [ ord('b'), ord('a') ]
632 self
.assertEqual(make_dafsa
.encode_prefix(label
), bytes
)
635 class EncodeLabelTest(unittest
.TestCase
):
637 """Tests to encode a single character label."""
639 bytes
= [ ord('a') + 0x80 ]
640 self
.assertEqual(make_dafsa
.encode_label(label
), bytes
)
643 """Tests to encode a multi character label."""
645 bytes
= [ ord('b') + 0x80, ord('a') ]
646 self
.assertEqual(make_dafsa
.encode_label(label
), bytes
)
649 class EncodeLinksTest(unittest
.TestCase
):
650 def testEndLabel(self
):
651 """Tests to encode link to the sink."""
656 self
.assertEqual(make_dafsa
.encode_links(children
, offsets
, bytes
),
659 def testOneByteOffset(self
):
660 """Tests to encode a single one byte offset."""
661 node
= ( '', [ None ] )
663 offsets
= { id(node
) : 2 }
666 self
.assertEqual(make_dafsa
.encode_links(children
, offsets
, bytes
),
669 def testOneByteOffsets(self
):
670 """Tests to encode a sequence of one byte offsets."""
671 node1
= ( '', [ None ] )
672 node2
= ( '', [ None ] )
673 children
= [ node1
, node2
]
674 offsets
= { id(node1
) : 2, id(node2
) : 1 }
677 self
.assertEqual(make_dafsa
.encode_links(children
, offsets
, bytes
),
680 def testTwoBytesOffset(self
):
681 """Tests to encode a single two byte offset."""
682 node
= ( '', [ None ] )
684 offsets
= { id(node
) : 2 }
687 self
.assertEqual(make_dafsa
.encode_links(children
, offsets
, bytes
),
690 def testTwoBytesOffsets(self
):
691 """Tests to encode a sequence of two byte offsets."""
692 node1
= ( '', [ None ] )
693 node2
= ( '', [ None ] )
694 node3
= ( '', [ None ] )
695 children
= [ node1
, node2
, node3
]
696 offsets
= { id(node1
) : 1002, id(node2
) : 2, id(node3
) : 2002 }
698 output
= [ 232, 195, 232, 67, 241, 67 ]
699 self
.assertEqual(make_dafsa
.encode_links(children
, offsets
, bytes
),
702 def testThreeBytesOffset(self
):
703 """Tests to encode a single three byte offset."""
704 node
= ( '', [ None ] )
706 offsets
= { id(node
) : 2 }
708 output
= [ 166, 134, 225 ]
709 self
.assertEqual(make_dafsa
.encode_links(children
, offsets
, bytes
),
712 def testThreeBytesOffsets(self
):
713 """Tests to encode a sequence of three byte offsets."""
714 node1
= ( '', [ None ] )
715 node2
= ( '', [ None ] )
716 node3
= ( '', [ None ] )
717 children
= [ node1
, node2
, node3
]
718 offsets
= { id(node1
) : 100002, id(node2
) : 2, id(node3
) : 200002 }
720 output
= [ 160, 134, 225, 160, 134, 97, 172, 134, 97 ]
721 self
.assertEqual(make_dafsa
.encode_links(children
, offsets
, bytes
),
724 def testOneTwoThreeBytesOffsets(self
):
725 """Tests to encode offsets of different sizes."""
726 node1
= ( '', [ None ] )
727 node2
= ( '', [ None ] )
728 node3
= ( '', [ None ] )
729 children
= [ node1
, node2
, node3
]
730 offsets
= { id(node1
) : 10003, id(node2
) : 10002, id(node3
) : 100002 }
732 output
= [ 129, 143, 95, 97, 74, 13, 99 ]
733 self
.assertEqual(make_dafsa
.encode_links(children
, offsets
, bytes
),
737 class ExamplesTest(unittest
.TestCase
):
738 def testExample1(self
):
739 """Tests Example 1 from make_dafsa.py."""
740 infile
= [ '%%', 'aa, 1', 'a, 2', '%%' ]
741 bytes
= [ 0x81, 0xE1, 0x02, 0x81, 0x82, 0x61, 0x81 ]
742 outfile
= make_dafsa
.to_cxx(bytes
)
743 self
.assertEqual(make_dafsa
.words_to_cxx(make_dafsa
.parse_gperf(infile
)),
746 def testExample2(self
):
747 """Tests Example 2 from make_dafsa.py."""
748 infile
= [ '%%', 'aa, 1', 'bbb, 2', 'baa, 1', '%%' ]
749 bytes
= [ 0x02, 0x83, 0xE2, 0x02, 0x83, 0x61, 0x61, 0x81, 0x62, 0x62,
751 outfile
= make_dafsa
.to_cxx(bytes
)
752 self
.assertEqual(make_dafsa
.words_to_cxx(make_dafsa
.parse_gperf(infile
)),
756 if __name__
== '__main__':