rubytree 0.8.3 → 0.9.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/API-CHANGES.rdoc +51 -20
- data/Gemfile +5 -1
- data/Gemfile.lock +14 -13
- data/History.rdoc +110 -42
- data/LICENSE.md +37 -0
- data/README.md +276 -0
- data/Rakefile +53 -28
- data/TAGS +218 -0
- data/TODO.org +114 -42
- data/examples/example_basic.rb +53 -0
- data/lib/tree.rb +357 -529
- data/lib/tree/binarytree.rb +76 -36
- data/lib/tree/utils/camel_case_method_handler.rb +78 -0
- data/lib/tree/utils/json_converter.rb +125 -0
- data/lib/tree/utils/metrics_methods.rb +172 -0
- data/lib/tree/utils/tree_merge_handler.rb +118 -0
- data/lib/tree/utils/utils.rb +41 -0
- data/lib/tree/version.rb +3 -3
- data/test/test_binarytree.rb +46 -1
- data/test/test_subclassed_node.rb +70 -0
- data/test/test_thread_and_fiber.rb +88 -0
- data/test/test_tree.rb +209 -35
- metadata +85 -82
- data/.dir-locals.el +0 -5
- data/COPYING.rdoc +0 -32
- data/README.rdoc +0 -219
@@ -0,0 +1,118 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
#
|
3
|
+
# tree_merge_handler.rb
|
4
|
+
#
|
5
|
+
# Author: Anupam Sengupta
|
6
|
+
# Time-stamp: <2013-12-31 21:52:59 anupam>
|
7
|
+
#
|
8
|
+
# Copyright (C) 2013 Anupam Sengupta (anupamsg@gmail.com)
|
9
|
+
#
|
10
|
+
# All rights reserved.
|
11
|
+
#
|
12
|
+
# Redistribution and use in source and binary forms, with or without modification,
|
13
|
+
# are permitted provided that the following conditions are met:
|
14
|
+
#
|
15
|
+
# - Redistributions of source code must retain the above copyright notice, this
|
16
|
+
# list of conditions and the following disclaimer.
|
17
|
+
#
|
18
|
+
# - Redistributions in binary form must reproduce the above copyright notice, this
|
19
|
+
# list of conditions and the following disclaimer in the documentation and/or
|
20
|
+
# other materials provided with the distribution.
|
21
|
+
#
|
22
|
+
# - Neither the name of the organization nor the names of its contributors may
|
23
|
+
# be used to endorse or promote products derived from this software without
|
24
|
+
# specific prior written permission.
|
25
|
+
#
|
26
|
+
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
27
|
+
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
28
|
+
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
29
|
+
# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
|
30
|
+
# ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
31
|
+
# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
32
|
+
# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
|
33
|
+
# ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
34
|
+
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
35
|
+
# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
36
|
+
#
|
37
|
+
|
38
|
+
# Provides utility methods to merge two {Tree::TreeNode} based trees.
|
39
|
+
# @since 0.9.0
|
40
|
+
module Tree::Utils::TreeMergeHandler
|
41
|
+
|
42
|
+
# @!group Merging Trees
|
43
|
+
|
44
|
+
# Merge two trees that share the same root node and returns <em>a new tree</em>.
|
45
|
+
#
|
46
|
+
# The new tree contains the contents of the merge between _other_tree_ and
|
47
|
+
# self. Duplicate nodes (coming from _other_tree_) will *NOT* be overwritten
|
48
|
+
# in self.
|
49
|
+
#
|
50
|
+
# @author Darren Oakley (https://github.com/dazoakley)
|
51
|
+
#
|
52
|
+
# @param [Tree::TreeNode] other_tree The other tree to merge with.
|
53
|
+
# @return [Tree::TreeNode] the resulting tree following the merge.
|
54
|
+
#
|
55
|
+
# @raise [TypeError] This exception is raised if _other_tree_ is not a {Tree::TreeNode}.
|
56
|
+
# @raise [ArgumentError] This exception is raised if _other_tree_ does not have the same root node as self.
|
57
|
+
def merge(other_tree)
|
58
|
+
check_merge_prerequisites(other_tree)
|
59
|
+
new_tree = merge_trees( self.root.dup, other_tree.root )
|
60
|
+
end
|
61
|
+
|
62
|
+
# Merge in another tree (that shares the same root node) into +this+ tree.
|
63
|
+
# Duplicate nodes (coming from _other_tree_) will NOT be overwritten in
|
64
|
+
# self.
|
65
|
+
#
|
66
|
+
# @author Darren Oakley (https://github.com/dazoakley)
|
67
|
+
#
|
68
|
+
# @param [Tree::TreeNode] other_tree The other tree to merge with.
|
69
|
+
#
|
70
|
+
# @raise [TypeError] This exception is raised if _other_tree_ is not a {Tree::TreeNode}.
|
71
|
+
# @raise [ArgumentError] This exception is raised if _other_tree_ does not have the same root node as self.
|
72
|
+
def merge!(other_tree)
|
73
|
+
check_merge_prerequisites( other_tree )
|
74
|
+
merge_trees( self.root, other_tree.root )
|
75
|
+
end
|
76
|
+
|
77
|
+
private
|
78
|
+
|
79
|
+
# Utility function to check that the conditions for a tree merge are met.
|
80
|
+
#
|
81
|
+
# @author Darren Oakley (https://github.com/dazoakley)
|
82
|
+
#
|
83
|
+
# @see #merge
|
84
|
+
# @see #merge!
|
85
|
+
def check_merge_prerequisites(other_tree)
|
86
|
+
unless other_tree.is_a?(Tree::TreeNode)
|
87
|
+
raise TypeError, 'You can only merge in another instance of Tree::TreeNode'
|
88
|
+
end
|
89
|
+
|
90
|
+
unless self.root.name == other_tree.root.name
|
91
|
+
raise ArgumentError, 'Unable to merge trees as they do not share the same root'
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
# Utility function to recursivley merge two subtrees.
|
96
|
+
#
|
97
|
+
# @author Darren Oakley (https://github.com/dazoakley)
|
98
|
+
#
|
99
|
+
# @param [Tree::TreeNode] tree1 The target tree to merge into.
|
100
|
+
# @param [Tree::TreeNode] tree2 The donor tree (that will be merged into target).
|
101
|
+
# @raise [Tree::TreeNode] The merged tree.
|
102
|
+
def merge_trees(tree1, tree2)
|
103
|
+
names1 = tree1.has_children? ? tree1.children.map { |child| child.name } : []
|
104
|
+
names2 = tree2.has_children? ? tree2.children.map { |child| child.name } : []
|
105
|
+
|
106
|
+
names_to_merge = names2 - names1
|
107
|
+
names_to_merge.each do |name|
|
108
|
+
tree1 << tree2[name].detached_subtree_copy
|
109
|
+
end
|
110
|
+
|
111
|
+
tree1.children.each do |child|
|
112
|
+
merge_trees( child, tree2[child.name] ) unless tree2[child.name].nil?
|
113
|
+
end
|
114
|
+
|
115
|
+
return tree1
|
116
|
+
end
|
117
|
+
|
118
|
+
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
# utils.rb - This file is part of the RubyTree package.
|
2
|
+
#
|
3
|
+
# = utils.rb - Provides utility functions and mixins for RubyTree.
|
4
|
+
#
|
5
|
+
# Author:: Anupam Sengupta (anupamsg@gmail.com)
|
6
|
+
#
|
7
|
+
# Time-stamp: <2012-08-25 22:01:17 anupam>
|
8
|
+
#
|
9
|
+
# Copyright (C) 2012 Anupam Sengupta <anupamsg@gmail.com>
|
10
|
+
#
|
11
|
+
# All rights reserved.
|
12
|
+
#
|
13
|
+
# Redistribution and use in source and binary forms, with or without modification,
|
14
|
+
# are permitted provided that the following conditions are met:
|
15
|
+
#
|
16
|
+
# - Redistributions of source code must retain the above copyright notice, this
|
17
|
+
# list of conditions and the following disclaimer.
|
18
|
+
#
|
19
|
+
# - Redistributions in binary form must reproduce the above copyright notice, this
|
20
|
+
# list of conditions and the following disclaimer in the documentation and/or
|
21
|
+
# other materials provided with the distribution.
|
22
|
+
#
|
23
|
+
# - Neither the name of the organization nor the names of its contributors may
|
24
|
+
# be used to endorse or promote products derived from this software without
|
25
|
+
# specific prior written permission.
|
26
|
+
#
|
27
|
+
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
28
|
+
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
29
|
+
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
30
|
+
# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
|
31
|
+
# ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
32
|
+
# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
33
|
+
# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
|
34
|
+
# ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
35
|
+
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
36
|
+
# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
37
|
+
|
38
|
+
# Provides utilities and mixin modules for RubyTree.
|
39
|
+
module Tree::Utils
|
40
|
+
# Empty module. Being used as a namespace.
|
41
|
+
end
|
data/lib/tree/version.rb
CHANGED
@@ -1,11 +1,10 @@
|
|
1
|
-
#--
|
2
1
|
# version.rb - This file is part of the RubyTree package.
|
3
2
|
#
|
4
3
|
# This file provides the version number for Rubytree.
|
5
4
|
#
|
6
5
|
# Author:: Anupam Sengupta (anupamsg@gmail.com)
|
7
6
|
#
|
8
|
-
# Copyright (c) 2012 Anupam Sengupta
|
7
|
+
# Copyright (c) 2012, 2013 Anupam Sengupta
|
9
8
|
#
|
10
9
|
# All rights reserved.
|
11
10
|
#
|
@@ -35,7 +34,8 @@
|
|
35
34
|
# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
36
35
|
#
|
37
36
|
|
37
|
+
#
|
38
38
|
module Tree
|
39
39
|
# Rubytree Package Version
|
40
|
-
VERSION = '0.
|
40
|
+
VERSION = '0.9.0'
|
41
41
|
end
|
data/test/test_binarytree.rb
CHANGED
@@ -3,7 +3,7 @@
|
|
3
3
|
# test_binarytree.rb - This file is part of the RubyTree package.
|
4
4
|
#
|
5
5
|
#
|
6
|
-
# Copyright (c) 2006, 2007, 2008, 2009, 2010, 2012 Anupam Sengupta
|
6
|
+
# Copyright (c) 2006, 2007, 2008, 2009, 2010, 2012, 2013 Anupam Sengupta
|
7
7
|
#
|
8
8
|
# All rights reserved.
|
9
9
|
#
|
@@ -86,6 +86,50 @@ module TestTree
|
|
86
86
|
end
|
87
87
|
end
|
88
88
|
|
89
|
+
# Test the inordered_each method.
|
90
|
+
def test_inordered_each
|
91
|
+
a = Tree::BinaryTreeNode.new("a")
|
92
|
+
b = Tree::BinaryTreeNode.new("b")
|
93
|
+
c = Tree::BinaryTreeNode.new("c")
|
94
|
+
d = Tree::BinaryTreeNode.new("d")
|
95
|
+
e = Tree::BinaryTreeNode.new("e")
|
96
|
+
f = Tree::BinaryTreeNode.new("f")
|
97
|
+
g = Tree::BinaryTreeNode.new("g")
|
98
|
+
h = Tree::BinaryTreeNode.new("h")
|
99
|
+
i = Tree::BinaryTreeNode.new("i")
|
100
|
+
|
101
|
+
# Create the following Tree
|
102
|
+
# f <-- level 0 (Root)
|
103
|
+
# / \
|
104
|
+
# b g <-- level 1
|
105
|
+
# / \ \
|
106
|
+
# a d i <-- level 2
|
107
|
+
# / \ /
|
108
|
+
# c e h <-- level 3
|
109
|
+
f << b << a
|
110
|
+
f << g
|
111
|
+
b << d << c
|
112
|
+
d << e
|
113
|
+
g.right_child = i # This needs to be explicit
|
114
|
+
i << h
|
115
|
+
|
116
|
+
# The expected order of response
|
117
|
+
expected_array = [a, b, c, d, e, f, g, h, i]
|
118
|
+
|
119
|
+
result_array = []
|
120
|
+
result = f.inordered_each { |node| result_array << node.detached_copy}
|
121
|
+
|
122
|
+
assert_equal(result, f) # each should return the original object
|
123
|
+
|
124
|
+
expected_array.each_index do |i|
|
125
|
+
# Match only the names.
|
126
|
+
assert_equal(expected_array[i].name, result_array[i].name)
|
127
|
+
end
|
128
|
+
|
129
|
+
assert_equal(Enumerator, f.inordered_each.class) if defined?(Enumerator.class )# Without a block
|
130
|
+
assert_equal(Enumerable::Enumerator, f.inordered_each.class) if defined?(Enumerable::Enumerator.class )# Without a block
|
131
|
+
end
|
132
|
+
|
89
133
|
# Test the left_child method.
|
90
134
|
def test_left_child
|
91
135
|
@root << @left_child1
|
@@ -203,6 +247,7 @@ module TestTree
|
|
203
247
|
|
204
248
|
assert_warn(DeprecatedMethodWarning) {@root.leftChild = @left_child2}
|
205
249
|
assert_warn(DeprecatedMethodWarning) {@root.rightChild = @right_child2}
|
250
|
+
assert_raise(NoMethodError) {@root.to_snake_case("ABCD")} # Make sure the right method is visible
|
206
251
|
|
207
252
|
end
|
208
253
|
|
@@ -0,0 +1,70 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
# test_subclassed_node.rb - This file is part of the RubyTree package.
|
4
|
+
#
|
5
|
+
# Copyright (c) 2012 Anupam Sengupta
|
6
|
+
#
|
7
|
+
# All rights reserved.
|
8
|
+
#
|
9
|
+
# Redistribution and use in source and binary forms, with or without modification,
|
10
|
+
# are permitted provided that the following conditions are met:
|
11
|
+
#
|
12
|
+
# - Redistributions of source code must retain the above copyright notice, this
|
13
|
+
# list of conditions and the following disclaimer.
|
14
|
+
#
|
15
|
+
# - Redistributions in binary form must reproduce the above copyright notice, this
|
16
|
+
# list of conditions and the following disclaimer in the documentation and/or
|
17
|
+
# other materials provided with the distribution.
|
18
|
+
#
|
19
|
+
# - Neither the name of the organization nor the names of its contributors may
|
20
|
+
# be used to endorse or promote products derived from this software without
|
21
|
+
# specific prior written permission.
|
22
|
+
#
|
23
|
+
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
24
|
+
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
25
|
+
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
26
|
+
# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
|
27
|
+
# ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
28
|
+
# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
29
|
+
# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
|
30
|
+
# ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
31
|
+
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
32
|
+
# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
33
|
+
#
|
34
|
+
|
35
|
+
require 'test/unit'
|
36
|
+
require 'json'
|
37
|
+
require 'tree'
|
38
|
+
|
39
|
+
module TestTree
|
40
|
+
|
41
|
+
# Test class for the Tree node.
|
42
|
+
class TestSubclassedTreeNode < Test::Unit::TestCase
|
43
|
+
|
44
|
+
# A subclassed node to test various inheritance related features.
|
45
|
+
class MyNode < Tree::TreeNode
|
46
|
+
# A dummy method to test the camelCasedMethod resolution
|
47
|
+
def my_dummy_method
|
48
|
+
"Hello"
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
def test_subclassed_camelcase_methods
|
53
|
+
root = MyNode.new("Root")
|
54
|
+
|
55
|
+
assert_equal("Hello", root.my_dummy_method)
|
56
|
+
|
57
|
+
# We should get a warning as we are invoking the camelCase version of the dummy method.
|
58
|
+
assert_warn(DeprecatedMethodWarning) { root.send('MyDummyMethod') }
|
59
|
+
|
60
|
+
# Test if the structured_warnings can be disabled to call the CamelCa
|
61
|
+
DeprecatedMethodWarning.disable do
|
62
|
+
assert_equal("Hello", root.myDummyMethod)
|
63
|
+
end
|
64
|
+
|
65
|
+
end
|
66
|
+
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
__END__
|
@@ -0,0 +1,88 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
# test_thread_and_fiber.rb - This file is part of the RubyTree package.
|
4
|
+
#
|
5
|
+
# Copyright (c) 2012, 2013 Anupam Sengupta
|
6
|
+
#
|
7
|
+
# All rights reserved.
|
8
|
+
#
|
9
|
+
# Redistribution and use in source and binary forms, with or without modification,
|
10
|
+
# are permitted provided that the following conditions are met:
|
11
|
+
#
|
12
|
+
# - Redistributions of source code must retain the above copyright notice, this
|
13
|
+
# list of conditions and the following disclaimer.
|
14
|
+
#
|
15
|
+
# - Redistributions in binary form must reproduce the above copyright notice, this
|
16
|
+
# list of conditions and the following disclaimer in the documentation and/or
|
17
|
+
# other materials provided with the distribution.
|
18
|
+
#
|
19
|
+
# - Neither the name of the organization nor the names of its contributors may
|
20
|
+
# be used to endorse or promote products derived from this software without
|
21
|
+
# specific prior written permission.
|
22
|
+
#
|
23
|
+
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
24
|
+
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
25
|
+
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
26
|
+
# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
|
27
|
+
# ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
28
|
+
# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
29
|
+
# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
|
30
|
+
# ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
31
|
+
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
32
|
+
# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
33
|
+
#
|
34
|
+
|
35
|
+
require 'test/unit'
|
36
|
+
require 'json'
|
37
|
+
require 'tree'
|
38
|
+
|
39
|
+
module TestTree
|
40
|
+
# Test class for the Tree node.
|
41
|
+
class TestFiberAndThreadOnNode < Test::Unit::TestCase
|
42
|
+
|
43
|
+
# Test long and unbalanced trees
|
44
|
+
def create_long_depth_trees(depth=100)
|
45
|
+
tree = Tree::TreeNode.new("/")
|
46
|
+
current = tree
|
47
|
+
depth.times do |i|
|
48
|
+
new_node = Tree::TreeNode.new("#{i}")
|
49
|
+
current << new_node
|
50
|
+
current = new_node
|
51
|
+
end
|
52
|
+
|
53
|
+
tree.each { |n| nil }
|
54
|
+
tree
|
55
|
+
end
|
56
|
+
|
57
|
+
# Test the recursive methods with a fiber. The stack usage is causing
|
58
|
+
# failure for very large depths on unbalanced nodes.
|
59
|
+
def test_fiber_for_recursion
|
60
|
+
return unless defined?(Fiber.class) # Fibers exist only from Ruby 1.9 onwards.
|
61
|
+
assert_nothing_thrown do
|
62
|
+
Fiber.new do
|
63
|
+
depth = 1000 # Use a reasonably large depth, which would trip a recursive stack
|
64
|
+
root = create_long_depth_trees(depth)
|
65
|
+
assert_equal(depth+1, root.size)
|
66
|
+
end.resume
|
67
|
+
end
|
68
|
+
|
69
|
+
end # test_fiber
|
70
|
+
|
71
|
+
# Test the recursive methods with a thread. The stack usage is causing
|
72
|
+
# failure for very large depths on unbalanced nodes.
|
73
|
+
def test_thread_for_recursion
|
74
|
+
assert_nothing_thrown do
|
75
|
+
depth = 1000 # Use a reasonably large depth, which would trip a recursive stack
|
76
|
+
Thread.abort_on_exception = true
|
77
|
+
Thread.new do
|
78
|
+
root = create_long_depth_trees(depth)
|
79
|
+
assert_equal(depth+1, root.size)
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
end # test_thread
|
84
|
+
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
__END__
|
data/test/test_tree.rb
CHANGED
@@ -2,7 +2,7 @@
|
|
2
2
|
|
3
3
|
# test_tree.rb - This file is part of the RubyTree package.
|
4
4
|
#
|
5
|
-
# Copyright (c) 2006, 2007, 2008, 2009, 2010, 2011, 2012 Anupam Sengupta
|
5
|
+
# Copyright (c) 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 Anupam Sengupta
|
6
6
|
#
|
7
7
|
# All rights reserved.
|
8
8
|
#
|
@@ -145,7 +145,6 @@ module TestTree
|
|
145
145
|
first_node = Tree::TreeNode.new(1)
|
146
146
|
second_node = Tree::TreeNode.new(2)
|
147
147
|
|
148
|
-
|
149
148
|
assert_equal(first_node <=> nil, +1)
|
150
149
|
assert_equal(first_node <=> second_node, -1)
|
151
150
|
|
@@ -164,6 +163,24 @@ module TestTree
|
|
164
163
|
StandardWarning.enable
|
165
164
|
end
|
166
165
|
|
166
|
+
# Test the inclusion of Comparable
|
167
|
+
def test_is_comparable
|
168
|
+
nodeA = Tree::TreeNode.new("NodeA", "Some Content")
|
169
|
+
nodeB = Tree::TreeNode.new("NodeB", "Some Content")
|
170
|
+
nodeC = Tree::TreeNode.new("NodeC", "Some Content")
|
171
|
+
|
172
|
+
# Check if the nodes compare correctly
|
173
|
+
assert(nodeA < nodeB, "Node A is lexically 'less than' node B")
|
174
|
+
assert(nodeA <= nodeB, "Node A is lexically 'less than' node B")
|
175
|
+
assert(nodeB > nodeA, "Node B is lexically 'greater than' node A")
|
176
|
+
assert(nodeB >= nodeA, "Node B is lexically 'greater than' node A")
|
177
|
+
|
178
|
+
assert(!(nodeA == nodeB), "Node A and Node B are not equal")
|
179
|
+
assert(nodeB.between?(nodeA, nodeC), "Node B is lexically between node A and node C")
|
180
|
+
|
181
|
+
|
182
|
+
end
|
183
|
+
|
167
184
|
# Test the to_s method. This is probably a little fragile right now.
|
168
185
|
def test_to_s
|
169
186
|
a_node = Tree::TreeNode.new("A Node", "Some Content")
|
@@ -250,14 +267,16 @@ module TestTree
|
|
250
267
|
|
251
268
|
# Lets first collect the siblings in an array.
|
252
269
|
siblings = []
|
253
|
-
@child1.siblings { |sibling| siblings << sibling}
|
270
|
+
result = @child1.siblings { |sibling| siblings << sibling}
|
254
271
|
|
272
|
+
assert_equal(@child1, result)
|
255
273
|
assert_equal(2, siblings.length, "Should have two siblings")
|
256
274
|
assert(siblings.include?(@child2), "Should have 2nd child as sibling")
|
257
275
|
assert(siblings.include?(@child3), "Should have 3rd child as sibling")
|
258
276
|
|
259
277
|
siblings.clear
|
260
278
|
siblings = @child1.siblings
|
279
|
+
assert_equal(Array, siblings.class)
|
261
280
|
assert_equal(2, siblings.length, "Should have two siblings")
|
262
281
|
|
263
282
|
siblings.clear
|
@@ -267,6 +286,7 @@ module TestTree
|
|
267
286
|
siblings.clear
|
268
287
|
siblings = @root.siblings
|
269
288
|
assert_equal(0, siblings.length, "Root should not have any siblings")
|
289
|
+
|
270
290
|
end
|
271
291
|
|
272
292
|
# Test the is_only_child? method.
|
@@ -465,21 +485,26 @@ module TestTree
|
|
465
485
|
assert_equal(0, child.node_height, "The subtree at #{child.name} should have a height of 0")
|
466
486
|
end
|
467
487
|
|
468
|
-
|
469
|
-
|
470
|
-
|
471
|
-
|
488
|
+
result_array = @root.children
|
489
|
+
|
490
|
+
assert_equal(3, result_array.length, "Should have three direct children")
|
491
|
+
assert(!result_array.include?(@root), "Should not have root")
|
492
|
+
assert_equal(result_array[0], @child1, "Should have child 1")
|
493
|
+
assert_equal(result_array[1], @child2, "Should have child 2")
|
494
|
+
assert_equal(result_array[2], @child3, "Should have child 3")
|
495
|
+
assert(!result_array.include?(@child4), "Should not have child 4")
|
496
|
+
|
497
|
+
# Lets try the block version of the method.
|
498
|
+
result_array.clear
|
499
|
+
result = @root.children {|child| result_array << child}
|
500
|
+
assert_equal(@root, result)
|
501
|
+
result_array.length
|
502
|
+
assert_equal(3, result_array.length, "Should have three children")
|
503
|
+
assert_equal(result_array[0], @child1, "Should have child 1")
|
504
|
+
assert_equal(result_array[1], @child2, "Should have child 2")
|
505
|
+
assert_equal(result_array[2], @child3, "Should have child 3")
|
506
|
+
assert(!result_array.include?(@child4), "Should not have child 4")
|
472
507
|
|
473
|
-
assert_equal(3, children.length, "Should have three direct children")
|
474
|
-
assert(!children.include?(@root), "Should not have root")
|
475
|
-
assert(children.include?(@child1), "Should have child 1")
|
476
|
-
assert(children.include?(@child2), "Should have child 2")
|
477
|
-
assert(children.include?(@child3), "Should have child 3")
|
478
|
-
assert(!children.include?(@child4), "Should not have child 4")
|
479
|
-
|
480
|
-
children.clear
|
481
|
-
children = @root.children
|
482
|
-
assert_equal(3, children.length, "Should have three children")
|
483
508
|
end
|
484
509
|
|
485
510
|
# Test the first_child method.
|
@@ -547,15 +572,27 @@ module TestTree
|
|
547
572
|
def test_each_leaf
|
548
573
|
setup_test_tree
|
549
574
|
|
550
|
-
|
551
|
-
@root.each_leaf { |node|
|
575
|
+
result_array = []
|
576
|
+
result = @root.each_leaf { |node| result_array << node }
|
577
|
+
assert_equal(@root, result)
|
578
|
+
assert_equal(3, result_array.length, "Should have THREE LEAF NODES")
|
579
|
+
assert(!result_array.include?(@root), "Should not have root")
|
580
|
+
assert(result_array.include?(@child1), "Should have child 1")
|
581
|
+
assert(result_array.include?(@child2), "Should have child 2")
|
582
|
+
assert(!result_array.include?(@child3), "Should not have child 3")
|
583
|
+
assert(result_array.include?(@child4), "Should have child 4")
|
584
|
+
|
585
|
+
# Now lets try without the block
|
586
|
+
result_array.clear
|
587
|
+
result_array = @root.each_leaf
|
588
|
+
assert_equal(Array, result_array.class)
|
589
|
+
assert_equal(3, result_array.length, "Should have THREE LEAF NODES")
|
590
|
+
assert(!result_array.include?(@root), "Should not have root")
|
591
|
+
assert(result_array.include?(@child1), "Should have child 1")
|
592
|
+
assert(result_array.include?(@child2), "Should have child 2")
|
593
|
+
assert(!result_array.include?(@child3), "Should not have child 3")
|
594
|
+
assert(result_array.include?(@child4), "Should have child 4")
|
552
595
|
|
553
|
-
assert_equal(3, nodes.length, "Should have THREE LEAF NODES")
|
554
|
-
assert(!nodes.include?(@root), "Should not have root")
|
555
|
-
assert(nodes.include?(@child1), "Should have child 1")
|
556
|
-
assert(nodes.include?(@child2), "Should have child 2")
|
557
|
-
assert(!nodes.include?(@child3), "Should not have child 3")
|
558
|
-
assert(nodes.include?(@child4), "Should have child 4")
|
559
596
|
end
|
560
597
|
|
561
598
|
# Test the parent method.
|
@@ -795,11 +832,15 @@ module TestTree
|
|
795
832
|
|
796
833
|
# Create the response
|
797
834
|
result_array = Array.new
|
798
|
-
j.breadth_each { |node| result_array << node.detached_copy }
|
835
|
+
result = j.breadth_each { |node| result_array << node.detached_copy }
|
799
836
|
|
837
|
+
assert_equal(j, result)
|
800
838
|
expected_array.each_index do |i|
|
801
839
|
assert_equal(expected_array[i].name, result_array[i].name) # Match only the names.
|
802
840
|
end
|
841
|
+
|
842
|
+
assert_equal(Enumerator, j.breadth_each.class) if defined?(Enumerator.class )# Without a block
|
843
|
+
assert_equal(Enumerable::Enumerator, j.breadth_each.class) if defined?(Enumerable::Enumerator.class )# Without a block
|
803
844
|
end
|
804
845
|
|
805
846
|
# Test the preordered_each method.
|
@@ -828,12 +869,56 @@ module TestTree
|
|
828
869
|
j << k << z
|
829
870
|
|
830
871
|
result_array = []
|
831
|
-
j.preordered_each { |node| result_array << node.detached_copy}
|
872
|
+
result = j.preordered_each { |node| result_array << node.detached_copy}
|
873
|
+
|
874
|
+
assert_equal(j, result) # Each returns the invocation target
|
832
875
|
|
833
876
|
expected_array.each_index do |i|
|
834
877
|
# Match only the names.
|
835
878
|
assert_equal(expected_array[i].name, result_array[i].name)
|
836
879
|
end
|
880
|
+
|
881
|
+
assert_equal(Enumerator, j.preordered_each.class) if defined?(Enumerator.class )# Without a block
|
882
|
+
assert_equal(Enumerable::Enumerator, j.preordered_each.class) if defined?(Enumerable::Enumerator.class )# Without a block
|
883
|
+
end
|
884
|
+
|
885
|
+
# Test the postordered_each method.
|
886
|
+
def test_postordered_each
|
887
|
+
j = Tree::TreeNode.new("j")
|
888
|
+
f = Tree::TreeNode.new("f")
|
889
|
+
k = Tree::TreeNode.new("k")
|
890
|
+
a = Tree::TreeNode.new("a")
|
891
|
+
d = Tree::TreeNode.new("d")
|
892
|
+
h = Tree::TreeNode.new("h")
|
893
|
+
z = Tree::TreeNode.new("z")
|
894
|
+
|
895
|
+
# The expected order of response
|
896
|
+
expected_array = [d, a, h, f, z, k, j]
|
897
|
+
|
898
|
+
# Create the following Tree
|
899
|
+
# j <-- level 0 (Root)
|
900
|
+
# / \
|
901
|
+
# f k <-- level 1
|
902
|
+
# / \ \
|
903
|
+
# a h z <-- level 2
|
904
|
+
# \
|
905
|
+
# d <-- level 3
|
906
|
+
j << f << a << d
|
907
|
+
f << h
|
908
|
+
j << k << z
|
909
|
+
|
910
|
+
result_array = []
|
911
|
+
result = j.postordered_each { |node| result_array << node.detached_copy}
|
912
|
+
|
913
|
+
assert_equal(j, result) # Each returns the invocation target
|
914
|
+
|
915
|
+
expected_array.each_index do |i|
|
916
|
+
# Match only the names.
|
917
|
+
assert_equal(expected_array[i].name, result_array[i].name)
|
918
|
+
end
|
919
|
+
|
920
|
+
assert_equal(Enumerator, j.postordered_each.class) if defined?(Enumerator.class )# Without a block
|
921
|
+
assert_equal(Enumerable::Enumerator, j.postordered_each.class) if defined?(Enumerable::Enumerator.class )# Without a block
|
837
922
|
end
|
838
923
|
|
839
924
|
# test the detached_copy method.
|
@@ -1021,7 +1106,7 @@ module TestTree
|
|
1021
1106
|
]
|
1022
1107
|
}.to_json
|
1023
1108
|
|
1024
|
-
tree = JSON.parse(tree_as_json)
|
1109
|
+
tree = JSON.parse(tree_as_json, :create_additions => true)
|
1025
1110
|
|
1026
1111
|
assert_equal(@root.name, tree.root.name, "Root should be returned")
|
1027
1112
|
assert_equal(@child1.name, tree[0].name, "Child 1 should be returned")
|
@@ -1037,7 +1122,7 @@ module TestTree
|
|
1037
1122
|
|
1038
1123
|
j = root_node.to_json
|
1039
1124
|
|
1040
|
-
k = JSON.parse(j)
|
1125
|
+
k = JSON.parse(j, :create_additions => true)
|
1041
1126
|
|
1042
1127
|
assert_equal(k.name, root_node.name, "Root should be returned")
|
1043
1128
|
assert_equal(k[0].name, root_node[0].name, "Child 1 should be returned")
|
@@ -1050,10 +1135,10 @@ module TestTree
|
|
1050
1135
|
setup_test_tree
|
1051
1136
|
|
1052
1137
|
meth_names_to_test = %w{isRoot? isLeaf? hasContent?
|
1053
|
-
hasChildren?
|
1138
|
+
hasChildren? firstChild lastChild
|
1054
1139
|
firstSibling isFirstSibling? lastSibling isLastSibling?
|
1055
1140
|
isOnlyChild? nextSibling previousSibling nodeHeight nodeDepth
|
1056
|
-
|
1141
|
+
removeFromParent! removeAll! freezeTree! }
|
1057
1142
|
|
1058
1143
|
require 'structured_warnings'
|
1059
1144
|
|
@@ -1065,7 +1150,7 @@ module TestTree
|
|
1065
1150
|
assert_warn(DeprecatedMethodWarning) {@root.send(meth_name)}
|
1066
1151
|
end
|
1067
1152
|
|
1068
|
-
|
1153
|
+
# Special Case for printTree to avoid putting stuff on the STDOUT during the unit test.
|
1069
1154
|
begin
|
1070
1155
|
require 'stringio'
|
1071
1156
|
$stdout = StringIO.new
|
@@ -1115,10 +1200,8 @@ module TestTree
|
|
1115
1200
|
assert_raise(ArgumentError) {root << root}
|
1116
1201
|
|
1117
1202
|
# And now a scenario where the node addition is done down the hierarchy
|
1118
|
-
# @todo This scenario is not yet fixed.
|
1119
1203
|
child = Tree::TreeNode.new("child")
|
1120
|
-
root << child << root
|
1121
|
-
# puts root # This will throw a stack trace
|
1204
|
+
assert_raise(RuntimeError) { root << child << root }
|
1122
1205
|
end
|
1123
1206
|
|
1124
1207
|
# Test whether the tree_leaf method works correctly
|
@@ -1132,6 +1215,97 @@ module TestTree
|
|
1132
1215
|
|
1133
1216
|
end
|
1134
1217
|
|
1218
|
+
# Test if node names are really unique in the whole tree
|
1219
|
+
def test_unique_node_names
|
1220
|
+
setup_test_tree
|
1221
|
+
|
1222
|
+
assert_raise(RuntimeError) { @root << @child1 }
|
1223
|
+
assert_raise(RuntimeError) { @root.first_child << @child2 }
|
1224
|
+
end
|
1225
|
+
|
1226
|
+
# Setup function to build some extra trees to play with.
|
1227
|
+
def setup_other_test_tree
|
1228
|
+
# Build up another tree
|
1229
|
+
#
|
1230
|
+
# ROOT
|
1231
|
+
# |
|
1232
|
+
# |-- Child1
|
1233
|
+
# | |
|
1234
|
+
# | |-- Child1a
|
1235
|
+
# | |-- Child1b
|
1236
|
+
# |
|
1237
|
+
# |-- Child3
|
1238
|
+
# |
|
1239
|
+
# |-- Child3a -- Child3a1
|
1240
|
+
#
|
1241
|
+
@other_tree = @root.detached_copy
|
1242
|
+
@other_tree << @child1.detached_copy
|
1243
|
+
@other_tree["Child1"] << Tree::TreeNode.new("Child1a", "GrandChild Node 1a")
|
1244
|
+
@other_tree["Child1"] << Tree::TreeNode.new("Child1b", "GrandChild Node 1b")
|
1245
|
+
@other_tree << @child3.detached_copy
|
1246
|
+
@other_tree["Child3"] << Tree::TreeNode.new("Child3a", "GrandChild Node 3a")
|
1247
|
+
@other_tree["Child3"]["Child3a"] << Tree::TreeNode.new("Child3a1", "GreatGrandChild Node 3a1")
|
1248
|
+
|
1249
|
+
# And another (different) one so we can test exceptions...
|
1250
|
+
@other_tree2 = Tree::TreeNode.new("ROOTIE", "A different root")
|
1251
|
+
@other_tree2 << Tree::TreeNode.new("new_child1", "New Child 1")
|
1252
|
+
end
|
1253
|
+
|
1254
|
+
# Test tree merging.
|
1255
|
+
def test_merge
|
1256
|
+
setup_test_tree
|
1257
|
+
setup_other_test_tree
|
1258
|
+
|
1259
|
+
merged_tree = @root.merge(@other_tree)
|
1260
|
+
|
1261
|
+
# puts "\n\ntest_merge:\n\n"
|
1262
|
+
# @root.print_tree
|
1263
|
+
# puts "\n"
|
1264
|
+
# @other_tree.print_tree
|
1265
|
+
# puts "\n"
|
1266
|
+
# merged_tree.print_tree
|
1267
|
+
|
1268
|
+
assert( @root["Child1"]["Child1a"].nil?, ".merge() has altered self." )
|
1269
|
+
assert( @root["Child1"]["Child1b"].nil?, ".merge() has altered self." )
|
1270
|
+
assert( @root["Child3"]["Child3a"].nil?, ".merge() has altered self." )
|
1271
|
+
assert( merged_tree.is_a?(Tree::TreeNode) )
|
1272
|
+
assert( !merged_tree["Child1"]["Child1a"].nil?, ".merge() has not included ['Child1']['Child1a'] from other_tree." )
|
1273
|
+
assert( !merged_tree["Child1"]["Child1b"].nil?, ".merge() has not included ['Child1']['Child1b'] from other_tree." )
|
1274
|
+
assert( !merged_tree["Child3"]["Child3a"].nil?, ".merge() has not included ['Child3']['Child3a'] from other_tree." )
|
1275
|
+
assert( !merged_tree["Child2"].nil?, ".merge() has not included ['Child2'] from self." )
|
1276
|
+
assert( !merged_tree["Child3"]["Child3a"]["Child3a1"].nil?, ".merge() has not included ['Child3']['Child3a']['Child3a1'] from other_tree." )
|
1277
|
+
assert( !merged_tree["Child3"]["Child4"].nil?, ".merge() has not included ['Child3']['Child4'] from self." )
|
1278
|
+
|
1279
|
+
assert_raise(ArgumentError) { @root.merge(@other_tree2) }
|
1280
|
+
assert_raise(TypeError) { @root.merge('ROOT') }
|
1281
|
+
end
|
1282
|
+
|
1283
|
+
# Test tree merging.
|
1284
|
+
def test_merge_bang
|
1285
|
+
setup_test_tree
|
1286
|
+
setup_other_test_tree
|
1287
|
+
|
1288
|
+
# puts "\n\ntest_merge_bang:\n\n"
|
1289
|
+
# @root.print_tree
|
1290
|
+
# puts "\n"
|
1291
|
+
# @other_tree.print_tree
|
1292
|
+
|
1293
|
+
@root.merge!(@other_tree)
|
1294
|
+
|
1295
|
+
# puts "\n"
|
1296
|
+
# @root.print_tree
|
1297
|
+
|
1298
|
+
assert( !@root["Child1"]["Child1a"].nil?, ".merge() has not included ['Child1']['Child1a'] from other_tree." )
|
1299
|
+
assert( !@root["Child1"]["Child1b"].nil?, ".merge() has not included ['Child1']['Child1b'] from other_tree." )
|
1300
|
+
assert( !@root["Child3"]["Child3a"].nil?, ".merge() has not included ['Child3']['Child3a'] from other_tree." )
|
1301
|
+
assert( !@root["Child2"].nil?, ".merge() has not included ['Child2'] from self." )
|
1302
|
+
assert( !@root["Child3"]["Child3a"]["Child3a1"].nil?, ".merge() has not included ['Child3']['Child3a']['Child3a1'] from other_tree." )
|
1303
|
+
assert( !@root["Child3"]["Child4"].nil?, ".merge() has not included ['Child3']['Child4'] from self." )
|
1304
|
+
|
1305
|
+
assert_raise(ArgumentError) { @root.merge!(@other_tree2) }
|
1306
|
+
assert_raise(TypeError) { @root.merge!('ROOT') }
|
1307
|
+
end
|
1308
|
+
|
1135
1309
|
end
|
1136
1310
|
end
|
1137
1311
|
|