forked from evolve75/RubyTree
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathtest_tree.rb
executable file
·1731 lines (1407 loc) · 64.3 KB
/
test_tree.rb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
#!/usr/bin/env ruby
# test_tree.rb - This file is part of the RubyTree package.
#
# Copyright (c) 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2017 Anupam Sengupta
#
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without modification,
# are permitted provided that the following conditions are met:
#
# - Redistributions of source code must retain the above copyright notice, this
# list of conditions and the following disclaimer.
#
# - Redistributions in binary form must reproduce the above copyright notice, this
# list of conditions and the following disclaimer in the documentation and/or
# other materials provided with the distribution.
#
# - Neither the name of the organization nor the names of its contributors may
# be used to endorse or promote products derived from this software without
# specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
# ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
# ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#
require 'test/unit'
# require 'structured_warnings'
require 'json'
require_relative '../lib/tree/tree_deps'
module TestTree
# Test class for the Tree node.
# noinspection RubyTooManyInstanceVariablesInspection
class TestTreeNode < Test::Unit::TestCase
Person = Struct::new(:First, :last) # A simple structure to use as the content for the nodes.
# Create this structure for the tests
#
# +----------+
# | ROOT |
# +-+--------+
# |
# | +---------------+
# +----+ CHILD1 |
# | +---------------+
# |
# | +---------------+
# +----+ CHILD2 |
# | +---------------+
# |
# | +---------------+ +------------------+
# +----+ CHILD3 +---+ CHILD4 |
# +---------------+ +------------------+
#
# Some basic setup to create the nodes for the test tree.
def setup
@root = Tree::TreeNode.new('ROOT', 'Root Node')
@child1 = Tree::TreeNode.new('Child1', 'Child Node 1')
@child2 = Tree::TreeNode.new('Child2', 'Child Node 2')
@child3 = Tree::TreeNode.new('Child3', 'Child Node 3')
@child4 = Tree::TreeNode.new('Child4', 'Grand Child 1')
@child5 = Tree::TreeNode.new('Child5', 'Child Node 4')
end
# Create the actual test tree.
def setup_test_tree
@root << @child1
@root << @child2
@root << @child3 << @child4
end
# Tear down the entire structure
def teardown
@root = nil
end
# Test for presence of the VERSION constant
def test_has_version_number
assert_not_nil(Tree::VERSION)
end
# This test is for the root alone - without any children being linked
def test_root_setup
assert_not_nil(@root , 'Root cannot be nil')
assert_nil(@root.parent , 'Parent of root node should be nil')
assert_not_nil(@root.name , 'Name should not be nil')
assert_equal('ROOT', @root.name, "Name should be 'ROOT'")
assert_equal('Root Node', @root.content, "Content should be 'Root Node'")
assert(@root.is_root? , 'Should identify as root')
assert([email protected]_children? , 'Cannot have any children')
assert(@root.has_content? , 'This root should have content')
assert_equal(1 , @root.size, 'Number of nodes should be one')
assert_equal(0, @root.siblings.length, 'This root does not have any children')
assert_equal(0, @root.in_degree, 'Root should have an in-degree of 0')
assert_equal(0, @root.node_height, "Root's height before adding any children is 0")
assert_raise(ArgumentError) { Tree::TreeNode.new(nil) }
end
# This test is for the state after the children are linked to the root.
def test_root
setup_test_tree
# TODO: Should probably change this logic. Root's root should
# return nil so that the possibility of a recursive error does not exist
# at all.
assert_same(@root , @root.root, "Root's root is self")
assert_same(@root , @child1.root, 'Root should be ROOT')
assert_same(@root , @child4.root, 'Root should be ROOT')
assert_equal(2 , @root.node_height, "Root's height after adding the children should be 2")
end
def test_from_hash
# A
# / | \
# B C D
# / \ /
# E F G
# / \
# H I
hash = {[:A, 'Root content'] => {
B: {
E: {},
F: {
H: {},
[:I, 'Leaf content'] => {}
}
},
C: {},
D: {
G: {}
}
}
}
tree = Tree::TreeNode.from_hash(hash)
assert_same(Tree::TreeNode, tree.class)
assert_same(tree.name, :A)
assert_equal(true, tree.is_root?)
assert_equal(false, tree.is_leaf?)
assert_equal(9, tree.size)
assert_equal('Root content', tree.content)
assert_equal(3, tree.children.count) # B, C, D
leaf_with_content = tree[:B][:F][:I]
assert_equal('Leaf content', leaf_with_content.content)
assert_equal(true, leaf_with_content.is_leaf?)
leaf_without_content = tree[:C]
assert_equal(true, leaf_without_content.is_leaf?)
interior_node = tree[:B][:F]
assert_equal(false, interior_node.is_leaf?)
assert_equal(2, interior_node.children.count)
# Can't make a node without a name
assert_raise (ArgumentError) { Tree::TreeNode.from_hash({}) }
# Can't have multiple roots
assert_raise (ArgumentError) { Tree::TreeNode.from_hash({A: {}, B: {}}) }
end
def test_from_hash_with_nils
# A
# / | \
# B C D
# / \ /
# E F G
# / \
# H I
hash = {[:A, 'Root content'] => {
B: {
E: nil,
F: {
H: nil,
[:I, 'Leaf content'] => nil
}
},
C: nil,
D: {
G: nil
}
}
}
tree = Tree::TreeNode.from_hash(hash)
assert_same(Tree::TreeNode, tree.class)
assert_same(:A, tree.name)
assert_equal(true, tree.is_root?)
assert_equal(false, tree.is_leaf?)
assert_equal(9, tree.size)
assert_equal('Root content', tree.content)
assert_equal(3, tree.children.count) # B, C, D
leaf_with_content = tree[:B][:F][:I]
assert_equal('Leaf content', leaf_with_content.content)
assert_equal(true, leaf_with_content.is_leaf?)
leaf_without_content = tree[:C]
assert_equal(true, leaf_without_content.is_leaf?)
interior_node = tree[:B][:F]
assert_equal(false, interior_node.is_leaf?)
assert_equal(2, interior_node.children.count)
end
def test_add_from_hash
tree = Tree::TreeNode.new(:A)
# Doesn't blow up when added an empty hash
hash = {}
assert_equal([], tree.add_from_hash(hash))
# Okay, now try a real hash
hash = {B: {C: {D: nil}, E: {}, F: {}}, [:G, 'G content'] => {}}
# A
# / \
# B G
# /|\
# C E F
# |
# D
added_children = tree.add_from_hash(hash)
assert_equal(Array, added_children.class)
assert_equal(2, added_children.count)
assert_equal(7, tree.size)
assert_equal('G content', tree[:G].content)
assert_equal(true, tree[:G].is_leaf?)
assert_equal(5, tree[:B].size)
assert_equal(3, tree[:B].children.count)
assert_raise (ArgumentError) { tree.add_from_hash([]) }
assert_raise (ArgumentError) { tree.add_from_hash('not a hash') }
assert_raise (ArgumentError) { tree.add_from_hash({X: 'Not a hash or nil'}) }
end
# Test exporting to ruby Hash
def test_to_h
a = Tree::TreeNode.new(:A)
b = Tree::TreeNode.new(:B)
c = Tree::TreeNode.new(:C)
# d = Tree::TreeNode.new(:D)
e = Tree::TreeNode.new(:E)
f = Tree::TreeNode.new(:F)
g = Tree::TreeNode.new(:G)
# A
# / \
# B C
# | / \
# E F G
a << b
a << c
c << f
c << g
b << e
exported = a.to_h
expected = {A: {B: {E: {}}, C: {F: {}, G: {}}}}
assert_equal(expected, exported)
end
# Test that from_hash and to_h are symmetric
def test_to_h_from_hash_symmetry
# A
# / \
# B C
# /|\ \
# E F G H
# |\ |
# I J K
input = {A: {B: {E: {I: {}, J: {}}, F: {}, G: {}}, C: {H: {K: {}}}}}
node = Tree::TreeNode.from_hash(input)
assert_equal(input, node.to_h)
end
# Test the presence of content in the nodes.
def test_has_content_eh
a_node = Tree::TreeNode.new('A Node')
assert_nil(a_node.content , 'The node should not have content')
assert(!a_node.has_content? , 'The node should not have content')
a_node.content = 'Something'
assert_not_nil(a_node.content, 'The node should now have content')
assert(a_node.has_content?, 'The node should now have content')
end
# Test the equivalence of size and length methods.
def test_length_is_size
setup_test_tree
assert_equal(@root.size, @root.length, 'Length and size methods should return the same result')
end
# Test the <=> operator.
def test_spaceship
# require 'structured_warnings'
StructuredWarnings::StandardWarning.disable # Disable the warnings for using integers as node names
first_node = Tree::TreeNode.new(1)
second_node = Tree::TreeNode.new(2)
assert_nil(first_node <=> nil)
assert_equal(-1, first_node <=> second_node)
second_node = Tree::TreeNode.new(1)
assert_equal(0, first_node <=> second_node)
first_node = Tree::TreeNode.new('ABC')
second_node = Tree::TreeNode.new('XYZ')
assert_nil(first_node <=> nil)
assert_equal(-1, first_node <=> second_node)
second_node = Tree::TreeNode.new('ABC')
assert_equal(0, first_node <=> second_node)
StructuredWarnings::StandardWarning.enable
end
# Test the inclusion of Comparable
def test_is_comparable
node_a = Tree::TreeNode.new('NodeA', 'Some Content')
node_b = Tree::TreeNode.new('NodeB', 'Some Content')
node_c = Tree::TreeNode.new('NodeC', 'Some Content')
# Check if the nodes compare correctly
assert(node_a < node_b, "Node A is lexically 'less than' node B")
assert(node_a <= node_b, "Node A is lexically 'less than' node B")
assert(node_b > node_a, "Node B is lexically 'greater than' node A")
assert(node_b >= node_a, "Node B is lexically 'greater than' node A")
assert(!(node_a == node_b), 'Node A and Node B are not equal')
assert(node_b.between?(node_a, node_c), 'Node B is lexically between node A and node C')
end
# Test the to_s method. This is probably a little fragile right now.
def test_to_s
a_node = Tree::TreeNode.new('A Node', 'Some Content')
expected_string = 'Node Name: A Node Content: Some Content Parent: <None> Children: 0 Total Nodes: 1'
assert_equal(expected_string, a_node.to_s, 'The string representation should be same')
# Now test with a symbol as a key.
a_node = Tree::TreeNode.new(:Node_Name, 'Some Content')
expected_string = 'Node Name: Node_Name Content: Some Content Parent: <None> Children: 0 Total Nodes: 1'
assert_equal(expected_string, a_node.to_s, 'The string representation should be same')
# Now test with a symbol as a key and another symbol as the content.
a_node = Tree::TreeNode.new(:Node_Name, :Content)
expected_string = 'Node Name: Node_Name Content: Content Parent: <None> Children: 0 Total Nodes: 1'
assert_equal(expected_string, a_node.to_s, 'The string representation should be same')
# Now test with a symbol as a key, and a hash as the content.
a_hash = {a_key: 'Some Value'}
a_node = Tree::TreeNode.new(:Node_Name, a_hash)
expected_string = "Node Name: Node_Name Content: #{a_hash} Parent: <None> Children: 0 Total Nodes: 1"
assert_equal(expected_string, a_node.to_s, 'The string representation should be same')
# Lets now add a child to the previous node, and test the to_s for the child
child_node = Tree::TreeNode.new(:Child_node, 'Child Node')
a_node << child_node
expected_string = 'Node Name: Child_node Content: Child Node Parent: Node_Name Children: 0 Total Nodes: 1'
assert_equal(expected_string, child_node.to_s, 'The string representation should be same')
end
# Test the first_sibling method.
def test_first_sibling
setup_test_tree
# TODO: Need to fix the first_sibling method to return nil for nodes with no siblings.
assert_same(@root, @root.first_sibling, "Root's first sibling is itself")
assert_same(@child1, @child1.first_sibling, "Child1's first sibling is itself")
assert_same(@child1, @child2.first_sibling, "Child2's first sibling should be child1")
assert_same(@child1, @child3.first_sibling, "Child3's first sibling should be child1")
assert_same(@child4, @child4.first_sibling, "Child4's first sibling should be itself")
assert_not_same(@child1, @child4.first_sibling, "Child4's first sibling is itself")
end
# Test the is_first_sibling? method.
def test_is_first_sibling_eh
setup_test_tree
assert(@root.is_first_sibling?, "Root's first sibling is itself")
assert( @child1.is_first_sibling?, "Child1's first sibling is itself")
assert([email protected]_first_sibling?, 'Child2 is not the first sibling')
assert([email protected]_first_sibling?, 'Child3 is not the first sibling')
assert( @child4.is_first_sibling?, "Child4's first sibling is itself")
end
# Test the is_last_sibling? method.
def test_is_last_sibling_eh
setup_test_tree
assert(@root.is_last_sibling?, "Root's last sibling is itself")
assert([email protected]_last_sibling?, 'Child1 is not the last sibling')
assert([email protected]_last_sibling?, 'Child2 is not the last sibling')
assert( @child3.is_last_sibling?, "Child3's last sibling is itself")
assert( @child4.is_last_sibling?, "Child4's last sibling is itself")
end
# Test the last_sibling method.
def test_last_sibling
setup_test_tree
assert_same(@root, @root.last_sibling, "Root's last sibling is itself")
assert_same(@child3, @child1.last_sibling, "Child1's last sibling should be child3")
assert_same(@child3, @child2.last_sibling, "Child2's last sibling should be child3")
assert_same(@child3, @child3.last_sibling, "Child3's last sibling should be itself")
assert_same(@child4, @child4.last_sibling, "Child4's last sibling should be itself")
assert_not_same(@child3, @child4.last_sibling, "Child4's last sibling is itself")
end
# Test the siblings method, which is essentially an iterator.
def test_siblings
setup_test_tree
# Lets first collect the siblings in an array.
siblings = []
result = @child1.siblings { |sibling| siblings << sibling}
assert_equal(@child1, result)
assert_equal(2, siblings.length, 'Should have two siblings')
assert(siblings.include?(@child2), 'Should have 2nd child as sibling')
assert(siblings.include?(@child3), 'Should have 3rd child as sibling')
siblings.clear
siblings = @child1.siblings
assert_equal(Array, siblings.class)
assert_equal(2, siblings.length, 'Should have two siblings')
siblings.clear
@child4.siblings {|sibling| siblings << sibling}
assert(siblings.empty?, 'Should not have any siblings')
siblings.clear
siblings = @root.siblings
assert_equal(0, siblings.length, 'Root should not have any siblings')
end
# Test the is_only_child? method.
def test_is_only_child_eh
setup_test_tree
assert(@root.is_only_child? , 'Root is an only child')
assert([email protected]_only_child?, 'Child1 is not the only child')
assert([email protected]_only_child?, 'Child2 is not the only child')
assert([email protected]_only_child?, 'Child3 is not the only child')
assert(@child4.is_only_child?, 'Child4 is an only child')
end
# Test the next_sibling method.
def test_next_sibling
setup_test_tree
assert_nil(@root.next_sibling, 'Root does not have any next sibling')
assert_equal(@child2, @child1.next_sibling, "Child1's next sibling is Child2")
assert_equal(@child3, @child2.next_sibling, "Child2's next sibling is Child3")
assert_nil(@child3.next_sibling, 'Child3 does not have a next sibling')
assert_nil(@child4.next_sibling, 'Child4 does not have a next sibling')
end
# Test the previous_sibling method.
def test_previous_sibling
setup_test_tree
assert_nil(@root.previous_sibling, 'Root does not have any previous sibling')
assert_nil(@child1.previous_sibling, 'Child1 does not have previous sibling')
assert_equal(@child1, @child2.previous_sibling, "Child2's previous sibling is Child1")
assert_equal(@child2, @child3.previous_sibling, "Child3's previous sibling is Child2")
assert_nil(@child4.previous_sibling, 'Child4 does not have a previous sibling')
end
# Test the add method.
def test_add
assert([email protected]_children?, 'Should not have any children')
assert_equal(1, @root.size, 'Should have 1 node (the root)')
@root.add(@child1)
@root << @child2
assert(@root.has_children?, 'Should have children')
assert_equal(3, @root.size, 'Should have three nodes')
@root << @child3 << @child4
assert_equal(5, @root.size, 'Should have five nodes')
assert_equal(2, @child3.size, 'Should have two nodes')
# Test the addition of a nil node.
assert_raise(ArgumentError) { @root.add(nil) }
end
# Test the addition of a duplicate node (duplicate being defined as a node with the same name).
def test_add_duplicate
# We need to allow duplicate nodes which are not *siblings*.
# Example (see https://github.com/evolve75/RubyTree/issues/24):
#
# * root
# |---+ one
# | +---> deep
# +---+ two
# +---> deep
#
# In this case, the two 'deep' nodes should not be considered duplicates
root = Tree::TreeNode.new('root')
one = Tree::TreeNode.new('one')
two = Tree::TreeNode.new('two')
three= Tree::TreeNode.new('three')
deep = Tree::TreeNode.new('deep')
root << one << deep
# The same child cannot be added under any circumstance
assert_raise(RuntimeError) { root.add(Tree::TreeNode.new(one.name)) }
assert_raise(RuntimeError) { root.add(one) }
begin
root << two << deep
rescue RuntimeError => e
fail("Error! The RuntimeError #{e} should not have been thrown. The same node can be added to different branches.")
end
assert_raise(ArgumentError) {root << three << three }
root.remove_all! # Because the first child 'three' would have been added.
begin
three_dup = Tree::TreeNode.new('three')
root << three << three_dup
rescue RuntimeError => e
fail("Error! The RuntimeError #{e} should not have been thrown. The same node name can be used in the branch.")
end
end
# Test Addition at a specific position
def test_add_at_specific_position
assert([email protected]_children?, 'Should not have any children')
assert_equal(1, @root.size, 'Should have 1 node (the root)')
@root.add(@child1) # First Child added at position 0
# Validate that children = [@child1]
assert_equal(@child1, @root[0])
@root << @child2 # Second child appended at position 1.
# Validate that children = [@child1, @child2]
assert_equal(@child1, @root[0])
assert_equal(@child2, @root[1])
assert_equal(2, @root.children.size, 'Should have two child nodes')
@root.add(@child3, 1) # Third child inserted at position 1 (before @child2)
# Validate that children = [@child1, @child3, @child2]
assert_equal(@child1, @root[0])
assert_equal(@child3, @root[1])
assert_equal(@child2, @root[2])
assert_equal(3, @root.children.size, 'Should have three child nodes')
@root.add(@child4, @root.children.size) # Fourth child inserted at the end (equivalent to plain #add(child4)
# Validate that children = [@child1, @child3, @child2, @child4]
assert_equal(@child1, @root[0])
assert_equal(@child3, @root[1])
assert_equal(@child2, @root[2])
assert_equal(@child4, @root[3])
assert_equal(4, @root.children.size, 'Should have four child nodes')
# Now, a negative test. We are preventing addition to a position that does not exist.
assert_raise(RuntimeError) {
@root.add(@child5, @root.children.size + 1) # Fifth child inserted beyond the last position that is valid (at 5th pos).
}
# Validate that we still have children = [@child1, @child3, @child2, @child4]
assert_equal(@child1, @root[0])
assert_equal(@child3, @root[1])
assert_equal(@child2, @root[2])
assert_equal(@child4, @root[3])
assert_nil(@root[4])
assert_equal(4, @root.children.size, 'Should have four child nodes')
# Another negative test. Lets attempt to add from the end at a position that is not available
assert_raise(RuntimeError) {
@root.add(@child5, -(@root.children.size+2)) # Fifth child inserted beyond the first position that is valid; i.e. at -6
}
assert_nil(@root[-5])
assert_equal(@child1, @root[-4])
assert_equal(@child3, @root[-3])
assert_equal(@child2, @root[-2])
assert_equal(@child4, @root[-1])
assert_equal(4, @root.children.size, 'Should have four child nodes')
# Lets correctly add the fifth child from the end to effectively prepend the node.
@root.add(@child5, -(@root.children.size+1)) # Fifth child inserted beyond the first position; i.e. at -5
assert_nil(@root[-6])
assert_equal(@child5, @root[-5])
assert_equal(@child1, @root[-4])
assert_equal(@child3, @root[-3])
assert_equal(@child2, @root[-2])
assert_equal(@child4, @root[-1])
assert_equal(5, @root.children.size, 'Should have five child nodes')
end
# Test the replace! and replace_with! methods
def test_replace_bang
@root << @child1
@root << @child2
@root << @child3
assert_equal(4, @root.size, 'Should have four nodes')
assert(@root.children.include?(@child1), 'Should parent child1')
assert(@root.children.include?(@child2), 'Should parent child2')
assert(@root.children.include?(@child3), 'Should parent child3')
assert([email protected]?(@child4), 'Should not parent child4')
@root.replace!(@child2, @child4)
# Also test replacing with a node of the same name
@root.replace! @child4, @child4.detached_copy
assert_equal(4, @root.size, 'Should have three nodes')
assert(@root.children.include?(@child1), 'Should parent child1')
assert([email protected]?(@child2), 'Should not parent child2')
assert(@root.children.include?(@child3), 'Should parent child3')
assert(@root.children.include?(@child4), 'Should parent child4')
assert_equal(1, @root.children.find_index(@child4), 'Should add child4 to index 1')
end
def test_replace_with
@root << @child1
@root << @child2
assert_equal(3, @root.size, 'Should have three nodes')
assert(@root.children.include?(@child1), 'Should parent child1')
assert(@root.children.include?(@child2), 'Should parent child2')
assert([email protected]?(@child3), 'Should not parent child3')
@child2.replace_with @child3
assert_equal(3, @root.size, 'Should have three nodes')
assert(@root.children.include?(@child1), 'Should parent child1')
assert([email protected]?(@child2), 'Should not parent child2')
assert(@root.children.include?(@child3), 'Should parent child3')
end
# Test the remove! and remove_all! methods.
def test_remove_bang
@root << @child1
@root << @child2
assert(@root.has_children?, 'Should have children')
assert_equal(3, @root.size, 'Should have three nodes')
@root.remove!(@child1)
assert_equal(2, @root.size, 'Should have two nodes')
@root.remove!(@child2)
assert([email protected]_children?, 'Should have no children')
assert_equal(1, @root.size, 'Should have one node')
@root << @child1
@root << @child2
assert(@root.has_children?, 'Should have children')
assert_equal(3, @root.size, 'Should have three nodes')
@root.remove_all!
assert([email protected]_children?, 'Should have no children')
assert_equal(1, @root.size, 'Should have one node')
# Some negative testing
@root.remove!(nil)
assert([email protected]_children?, 'Should have no children')
assert_equal(1, @root.size, 'Should have one node')
end
# Test the remove_all! method.
def test_remove_all_bang
setup_test_tree
assert(@root.has_children?, 'Should have children')
@root.remove_all!
assert([email protected]_children?, 'Should have no children')
assert_equal(1, @root.size, 'Should have one node')
end
# Test the remove_from_parent! method.
def test_remove_from_parent_bang
setup_test_tree
assert(@root.has_children?, 'Should have children')
assert([email protected]_leaf?, 'Root is not a leaf here')
child1 = @root[0]
assert_not_nil(child1, 'Child 1 should exist')
assert_same(@root, child1.root, "Child 1's root should be ROOT")
assert(@root.include?(child1), 'root should have child1')
child1.remove_from_parent!
assert_same(child1, child1.root, "Child 1's root should be self")
assert([email protected]?(child1), 'root should not have child1')
child1.remove_from_parent!
assert_same(child1, child1.root, "Child 1's root should still be self")
end
# Test the children method.
def test_children
setup_test_tree
assert(@root.has_children?, 'Should have children')
assert_equal(5, @root.size, 'Should have five nodes')
assert(@child3.has_children?, 'Should have children')
assert([email protected]_leaf?, 'Should not be a leaf')
assert_equal(1, @child3.node_height, 'The subtree at Child 3 should have a height of 1')
[@child1, @child2, @child4].each { |child|
assert_equal(0, child.node_height, "The subtree at #{child.name} should have a height of 0")
}
result_array = @root.children
assert_equal(3, result_array.length, 'Should have three direct children')
assert(!result_array.include?(@root), 'Should not have root')
assert_equal(result_array[0], @child1, 'Should have child 1')
assert_equal(result_array[1], @child2, 'Should have child 2')
assert_equal(result_array[2], @child3, 'Should have child 3')
assert(!result_array.include?(@child4), 'Should not have child 4')
# Lets try the block version of the method.
result_array.clear
result = @root.children {|child| result_array << child}
assert_equal(@root, result)
result_array.length
assert_equal(3, result_array.length, 'Should have three children')
assert_equal(result_array[0], @child1, 'Should have child 1')
assert_equal(result_array[1], @child2, 'Should have child 2')
assert_equal(result_array[2], @child3, 'Should have child 3')
assert(!result_array.include?(@child4), 'Should not have child 4')
end
# Test the first_child method.
def test_first_child
setup_test_tree
assert_equal(@child1, @root.first_child, "Root's first child is Child1")
assert_nil(@child1.first_child, 'Child1 does not have any children')
assert_equal(@child4, @child3.first_child, "Child3's first child is Child4")
end
# Test the last_child method.
def test_last_child
setup_test_tree
assert_equal(@child3, @root.last_child, "Root's last child is Child3")
assert_nil(@child1.last_child, 'Child1 does not have any children')
assert_equal(@child4, @child3.last_child, "Child3's last child is Child4")
end
# Test the find method.
def test_find
setup_test_tree
found_node = @root.find { |node| node == @child2}
assert_same(@child2, found_node, 'The node should be Child 2')
found_node = @root.find { |node| node == @child4}
assert_same(@child4, found_node, 'The node should be Child 4')
found_node = @root.find { |node| node.name == 'Child4' }
assert_same(@child4, found_node, 'The node should be Child 4')
found_node = @root.find { |node| node.name == 'NOT PRESENT' }
assert_nil(found_node, 'The node should not be found')
end
# Test the parentage method.
def test_parentage
setup_test_tree
assert_nil(@root.parentage, 'Root does not have any parentage')
assert_equal([@root], @child1.parentage, 'Child1 has Root as its parent')
assert_equal([@child3, @root], @child4.parentage, 'Child4 has Child3 and Root as ancestors')
end
# Test the each method.
def test_each
setup_test_tree
assert(@root.has_children?, 'Should have children')
assert_equal(5, @root.size, 'Should have five nodes')
assert(@child3.has_children?, 'Should have children')
nodes = []
@root.each { |node| nodes << node }
assert_equal(5, nodes.length, 'Should have FIVE NODES')
assert(nodes.include?(@root), 'Should have root')
assert(nodes.include?(@child1), 'Should have child 1')
assert(nodes.include?(@child2), 'Should have child 2')
assert(nodes.include?(@child3), 'Should have child 3')
assert(nodes.include?(@child4), 'Should have child 4')
end
# Test the each_leaf method.
def test_each_leaf
setup_test_tree
result_array = []
result = @root.each_leaf { |node| result_array << node }
assert_equal(@root, result)
assert_equal(3, result_array.length, 'Should have THREE LEAF NODES')
assert(!result_array.include?(@root), 'Should not have root')
assert(result_array.include?(@child1), 'Should have child 1')
assert(result_array.include?(@child2), 'Should have child 2')
assert(!result_array.include?(@child3), 'Should not have child 3')
assert(result_array.include?(@child4), 'Should have child 4')
# Now lets try without the block
result_array.clear
result_array = @root.each_leaf
assert_equal(Array, result_array.class)
assert_equal(3, result_array.length, 'Should have THREE LEAF NODES')
assert(!result_array.include?(@root), 'Should not have root')
assert(result_array.include?(@child1), 'Should have child 1')
assert(result_array.include?(@child2), 'Should have child 2')
assert(!result_array.include?(@child3), 'Should not have child 3')
assert(result_array.include?(@child4), 'Should have child 4')
end
# Test the parent method.
def test_parent
setup_test_tree
assert_nil(@root.parent, "Root's parent should be nil")
assert_equal(@root, @child1.parent, 'Parent should be root')
assert_equal(@root, @child3.parent, 'Parent should be root')
assert_equal(@child3, @child4.parent, 'Parent should be child3')
assert_equal(@root, @child4.parent.parent, 'Parent should be root')
end
# Test the [] method.
def test_indexed_access
setup_test_tree
assert_equal(@child1, @root[0], 'Should be the first child')
assert_equal(@child4, @root[2][0], 'Should be the grandchild')
assert_nil(@root['TEST'], 'Should be nil')
assert_nil(@root[99], 'Should be nil')
assert_raise(ArgumentError) { @root[nil] }
end
# Test the print_tree method.
def test_print_tree
setup_test_tree
#puts
#@root.print_tree
end
# Tests the binary dumping mechanism with an Object content node
def test_marshal_dump
# Setup Test Data
test_root = Tree::TreeNode.new('ROOT', 'Root Node')
test_content = {'KEY1' => 'Value1', 'KEY2' => 'Value2'}
test_child = Tree::TreeNode.new('Child', test_content)
test_content2 = %w(AValue1 AValue2 AValue3)
test_grand_child = Tree::TreeNode.new('Grand Child 1', test_content2)
test_root << test_child << test_grand_child
# Perform the test operation
data = Marshal.dump(test_root) # Marshal
new_root = Marshal.load(data) # And unmarshal
# Test the root node
assert_equal(test_root.name, new_root.name, 'Must identify as ROOT')
assert_equal(test_root.content, new_root.content, "Must have root's content")
assert(new_root.is_root?, 'Must be the ROOT node')
assert(new_root.has_children?, 'Must have a child node')
# Test the child node
new_child = new_root[test_child.name]
assert_equal(test_child.name, new_child.name, 'Must have child 1')
assert(new_child.has_content?, 'Child must have content')
assert(new_child.is_only_child?, 'Child must be the only child')
new_child_content = new_child.content
assert_equal(Hash, new_child_content.class, "Class of child's content should be a hash")
assert_equal(test_child.content.size, new_child_content.size, 'The content should have same size')
# Test the grand-child node
new_grand_child = new_child[test_grand_child.name]
assert_equal(test_grand_child.name, new_grand_child.name, 'Must have grand child')
assert(new_grand_child.has_content?, 'Grand-child must have content')
assert(new_grand_child.is_only_child?, 'Grand-child must be the only child')
new_grand_child_content = new_grand_child.content
assert_equal(Array, new_grand_child_content.class, "Class of grand-child's content should be an Array")
assert_equal(test_grand_child.content.size, new_grand_child_content.size, 'The content should have same size')
end
# marshal_load and marshal_dump are symmetric methods
# This alias is for satisfying ZenTest
alias test_marshal_load test_marshal_dump
# Test the collect method from the mixed-in Enumerable functionality.
def test_collect
setup_test_tree
collect_array = @root.collect do |node|
node.content = 'abc'
node
end
collect_array.each {|node| assert_equal('abc', node.content, "Should be 'abc'")}
end
# Test freezing the tree
def test_freeze_tree_bang
setup_test_tree
@root.content = 'ABC'
assert_equal('ABC', @root.content, "Content should be 'ABC'")
@root.freeze_tree!
# Note: The error raised here depends on the Ruby version.
# For Ruby > 1.9, RuntimeError is raised
# For Ruby ~ 1.8, TypeError is raised
assert_raise(RuntimeError, TypeError) {@root.content = '123'
}
assert_raise(RuntimeError, TypeError) {@root[0].content = '123'
}
end
# Test whether the content is accessible
def test_content
person = Person::new('John', 'Doe')
@root.content = person
assert_same(person, @root.content, 'Content should be the same')
end
# Test the depth computation algorithm. Note that this is an incorrect computation and actually returns height+1
# instead of depth. This method has been deprecated in this release and may be removed in the future.
def test_depth
begin
# require 'structured_warnings'
assert_warn(StructuredWarnings::DeprecatedMethodWarning) { do_deprecated_depth }
rescue LoadError
# Since the structured_warnings package is not present, we revert to good old Kernel#warn behavior.
do_deprecated_depth
end
end
# Run the assertions for the deprecated depth method.
def do_deprecated_depth
assert_equal(1, @root.depth, "A single node's depth is 1")
@root << @child1
assert_equal(2, @root.depth, 'This should be of depth 2')
@root << @child2
assert_equal(2, @root.depth, 'This should be of depth 2')
@child2 << @child3
assert_equal(3, @root.depth, 'This should be of depth 3')
assert_equal(2, @child2.depth, 'This should be of depth 2')
@child3 << @child4
assert_equal(4, @root.depth, 'This should be of depth 4')
end
# Test the height computation algorithm
def test_node_height
assert_equal(0, @root.node_height, "A single node's height is 0")
@root << @child1
assert_equal(1, @root.node_height, 'This should be of height 1')
assert_equal(0, @child1.node_height, 'This should be of height 0')
@root << @child2
assert_equal(1, @root.node_height, 'This should be of height 1')
assert_equal(0, @child2.node_height, 'This should be of height 0')