jumoku 0.2.0 → 0.2.1

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.
Files changed (97) hide show
  1. data/Gemfile +0 -1
  2. data/lib/jumoku.rb +2 -3
  3. data/lib/jumoku/builders/extended.rb +15 -23
  4. data/lib/jumoku/builders/raw_directed_tree.rb +15 -0
  5. data/lib/jumoku/builders/raw_undirected_tree.rb +15 -0
  6. data/lib/jumoku/builders/shared.rb +2 -5
  7. data/lib/jumoku/support/ruby_compatibility.rb +19 -0
  8. data/lib/jumoku/version.rb +1 -1
  9. data/spec/arborescence_spec.rb +14 -0
  10. data/spec/behaviors/core_tree.rb +281 -0
  11. data/spec/behaviors/extended.rb +530 -0
  12. data/spec/raw_directed_tree_spec.rb +14 -0
  13. data/spec/raw_undirected_tree_spec.rb +9 -310
  14. data/spec/spec_helper.rb +2 -0
  15. data/spec/tree_spec.rb +8 -535
  16. metadata +21 -86
  17. data/lib/jumoku/tree_api.rb +0 -27
  18. data/vendor/git/plexus/CREDITS.md +0 -31
  19. data/vendor/git/plexus/Gemfile +0 -3
  20. data/vendor/git/plexus/Gemfile.lock +0 -28
  21. data/vendor/git/plexus/LICENSE +0 -37
  22. data/vendor/git/plexus/README.md +0 -208
  23. data/vendor/git/plexus/Rakefile +0 -25
  24. data/vendor/git/plexus/TODO.md +0 -20
  25. data/vendor/git/plexus/VERSION +0 -1
  26. data/vendor/git/plexus/examples/graph_self.rb +0 -56
  27. data/vendor/git/plexus/examples/module_graph.jpg +0 -0
  28. data/vendor/git/plexus/examples/module_graph.rb +0 -14
  29. data/vendor/git/plexus/examples/self_graph.jpg +0 -0
  30. data/vendor/git/plexus/examples/visualize.jpg +0 -0
  31. data/vendor/git/plexus/examples/visualize.rb +0 -10
  32. data/vendor/git/plexus/lib/plexus.rb +0 -90
  33. data/vendor/git/plexus/lib/plexus/adjacency_graph.rb +0 -224
  34. data/vendor/git/plexus/lib/plexus/arc.rb +0 -59
  35. data/vendor/git/plexus/lib/plexus/arc_number.rb +0 -52
  36. data/vendor/git/plexus/lib/plexus/biconnected.rb +0 -84
  37. data/vendor/git/plexus/lib/plexus/chinese_postman.rb +0 -91
  38. data/vendor/git/plexus/lib/plexus/classes/graph_classes.rb +0 -28
  39. data/vendor/git/plexus/lib/plexus/common.rb +0 -63
  40. data/vendor/git/plexus/lib/plexus/comparability.rb +0 -63
  41. data/vendor/git/plexus/lib/plexus/directed_graph.rb +0 -78
  42. data/vendor/git/plexus/lib/plexus/directed_graph/algorithms.rb +0 -95
  43. data/vendor/git/plexus/lib/plexus/directed_graph/distance.rb +0 -167
  44. data/vendor/git/plexus/lib/plexus/dot.rb +0 -94
  45. data/vendor/git/plexus/lib/plexus/edge.rb +0 -36
  46. data/vendor/git/plexus/lib/plexus/ext.rb +0 -79
  47. data/vendor/git/plexus/lib/plexus/graph.rb +0 -626
  48. data/vendor/git/plexus/lib/plexus/graph_api.rb +0 -35
  49. data/vendor/git/plexus/lib/plexus/labels.rb +0 -113
  50. data/vendor/git/plexus/lib/plexus/maximum_flow.rb +0 -77
  51. data/vendor/git/plexus/lib/plexus/ruby_compatibility.rb +0 -17
  52. data/vendor/git/plexus/lib/plexus/search.rb +0 -510
  53. data/vendor/git/plexus/lib/plexus/strong_components.rb +0 -93
  54. data/vendor/git/plexus/lib/plexus/support/support.rb +0 -9
  55. data/vendor/git/plexus/lib/plexus/undirected_graph.rb +0 -56
  56. data/vendor/git/plexus/lib/plexus/undirected_graph/algorithms.rb +0 -90
  57. data/vendor/git/plexus/lib/plexus/version.rb +0 -6
  58. data/vendor/git/plexus/plexus.gemspec +0 -24
  59. data/vendor/git/plexus/spec/biconnected_spec.rb +0 -27
  60. data/vendor/git/plexus/spec/chinese_postman_spec.rb +0 -27
  61. data/vendor/git/plexus/spec/community_spec.rb +0 -44
  62. data/vendor/git/plexus/spec/complement_spec.rb +0 -27
  63. data/vendor/git/plexus/spec/digraph_distance_spec.rb +0 -121
  64. data/vendor/git/plexus/spec/digraph_spec.rb +0 -339
  65. data/vendor/git/plexus/spec/dot_spec.rb +0 -48
  66. data/vendor/git/plexus/spec/edge_spec.rb +0 -158
  67. data/vendor/git/plexus/spec/inspection_spec.rb +0 -38
  68. data/vendor/git/plexus/spec/multi_edge_spec.rb +0 -32
  69. data/vendor/git/plexus/spec/neighborhood_spec.rb +0 -36
  70. data/vendor/git/plexus/spec/properties_spec.rb +0 -146
  71. data/vendor/git/plexus/spec/search_spec.rb +0 -227
  72. data/vendor/git/plexus/spec/spec.opts +0 -4
  73. data/vendor/git/plexus/spec/spec_helper.rb +0 -59
  74. data/vendor/git/plexus/spec/strong_components_spec.rb +0 -61
  75. data/vendor/git/plexus/spec/triangulated_spec.rb +0 -125
  76. data/vendor/git/plexus/spec/undirected_graph_spec.rb +0 -220
  77. data/vendor/git/plexus/vendor/priority-queue/CHANGELOG +0 -33
  78. data/vendor/git/plexus/vendor/priority-queue/Makefile +0 -140
  79. data/vendor/git/plexus/vendor/priority-queue/README +0 -133
  80. data/vendor/git/plexus/vendor/priority-queue/benchmark/dijkstra.rb +0 -171
  81. data/vendor/git/plexus/vendor/priority-queue/compare_comments.rb +0 -49
  82. data/vendor/git/plexus/vendor/priority-queue/doc/c-vs-rb.png +0 -0
  83. data/vendor/git/plexus/vendor/priority-queue/doc/compare_big.gp +0 -14
  84. data/vendor/git/plexus/vendor/priority-queue/doc/compare_big.png +0 -0
  85. data/vendor/git/plexus/vendor/priority-queue/doc/compare_small.gp +0 -15
  86. data/vendor/git/plexus/vendor/priority-queue/doc/compare_small.png +0 -0
  87. data/vendor/git/plexus/vendor/priority-queue/doc/results.csv +0 -37
  88. data/vendor/git/plexus/vendor/priority-queue/ext/priority_queue/CPriorityQueue/extconf.rb +0 -2
  89. data/vendor/git/plexus/vendor/priority-queue/ext/priority_queue/CPriorityQueue/priority_queue.c +0 -947
  90. data/vendor/git/plexus/vendor/priority-queue/lib/priority_queue.rb +0 -14
  91. data/vendor/git/plexus/vendor/priority-queue/lib/priority_queue/c_priority_queue.rb +0 -1
  92. data/vendor/git/plexus/vendor/priority-queue/lib/priority_queue/poor_priority_queue.rb +0 -46
  93. data/vendor/git/plexus/vendor/priority-queue/lib/priority_queue/ruby_priority_queue.rb +0 -526
  94. data/vendor/git/plexus/vendor/priority-queue/priority_queue.so +0 -0
  95. data/vendor/git/plexus/vendor/priority-queue/setup.rb +0 -1551
  96. data/vendor/git/plexus/vendor/priority-queue/test/priority_queue_test.rb +0 -371
  97. data/vendor/git/plexus/vendor/rdot.rb +0 -360
data/Gemfile CHANGED
@@ -1,4 +1,3 @@
1
1
  source "http://rubygems.org"
2
2
 
3
3
  gemspec
4
- gem 'plexus', :path => 'vendor/git/plexus'
@@ -1,6 +1,5 @@
1
1
  libpath = File.expand_path(File.dirname(__FILE__))
2
2
  $:.unshift File.dirname(__FILE__) unless $:.include?(File.dirname(__FILE__)) || $:.include?(libpath)
3
- $: << libpath + '/../vendor/git/plexus/lib'
4
3
 
5
4
  require 'pathname'
6
5
  require 'pp'
@@ -24,8 +23,7 @@ require 'plexus'
24
23
  # (from the Facets library), both turning nodes into versatile handlers.
25
24
  #
26
25
  module Jumoku
27
- # API, core implementations
28
- autoload :TreeAPI, 'jumoku/tree_api'
26
+ # core implementations
29
27
  autoload :Shared, 'jumoku/builders/shared'
30
28
  autoload :Extended, 'jumoku/builders/extended'
31
29
 
@@ -46,6 +44,7 @@ module Jumoku
46
44
  autoload :Arborescence, 'jumoku/classes/tree_classes'
47
45
 
48
46
  # support
47
+ require 'jumoku/support/ruby_compatibility'
49
48
  require 'jumoku/support/support'
50
49
  require 'jumoku/ext/ext'
51
50
  end
@@ -1,5 +1,5 @@
1
1
  module Jumoku
2
- # This module provides methods extending the core TreeAPI. Most of those
2
+ # This module provides methods extending the core implementation. Most of those
3
3
  # methods are cloning the receiver tree and perform their operation on the
4
4
  # duplicated object. Some other are helpers adding reflexivity to trees.
5
5
  # There are also a few in-place methods.
@@ -163,11 +163,16 @@ module Jumoku
163
163
  #
164
164
  # The process relies on {RawTreeBuilder#remove_node! remove_node!}.
165
165
  #
166
- # @param [#each] *a an Enumerable nodes set
166
+ # @param [#each] *nodes an Enumerable nodes set; may contain Range
167
167
  # @return [Tree] `self`
168
168
  #
169
- def remove_nodes!(*a)
170
- a.flatten.each { |v| remove_node! v }
169
+ # @example
170
+ #
171
+ # remove_nodes! 1, 3..5
172
+ #
173
+ def remove_nodes!(*nodes)
174
+ nodes = nodes.to_a.map { |i| i.is_a?(Range) ? i.to_a : i }.flatten
175
+ nodes.each { |v| remove_node! v }
171
176
  self
172
177
  end
173
178
  alias delete_nodes! remove_nodes!
@@ -265,18 +270,11 @@ module Jumoku
265
270
  # @return [Boolean]
266
271
  #
267
272
  def branches?(*maybe_branches)
268
- list = maybe_branches.create_branches_list(_branch_type)
269
- all = true
270
-
271
- # Branch objects are really Edge objects within Plexus, therefore
272
- # cannot rely on #eql? to compare those structures and must drop
273
- # down to the attributes.
274
- list.each do |e| # Jumoku::Branch structs
275
- all = branches.any? do |b| # Plexus::Edge structs
276
- (b[:source] == e[:source]) and (b[:target] == e[:target])
273
+ maybe_branches.create_branches_list(_branch_type).all? do |maybe_branch|
274
+ branches.any? do |branch|
275
+ maybe_branch == branch
277
276
  end
278
277
  end
279
- all
280
278
  end
281
279
  alias has_branches? branches?
282
280
 
@@ -292,17 +290,11 @@ module Jumoku
292
290
  #
293
291
  def branches_among?(*maybe_branches)
294
292
  list = maybe_branches.create_branches_list(_branch_type)
295
- all = true
296
-
297
- # Branch objects are really Edge objects within Plexus, therefore
298
- # cannot rely on #eql? to compare those structures and must drop
299
- # down to the attributes.
300
- branches.each do |e| # Plexus::Edge structs
301
- all = list.any? do |b| # Jumoku::Branch structs
302
- (b[:source] == e[:source]) and (b[:target] == e[:target])
293
+ branches.all? do |branch|
294
+ list.any? do |maybe_branch|
295
+ branch == maybe_branch
303
296
  end
304
297
  end
305
- all
306
298
  end
307
299
  alias has_branches_among? branches_among?
308
300
 
@@ -22,6 +22,21 @@ module Jumoku
22
22
  #
23
23
  def initialize(*params)
24
24
  super(*params) # Delegates to Plexus.
25
+ class << self; self; end.module_eval do
26
+ alias has_branch? has_arc?
27
+ end
28
+ end
29
+
30
+ # Checks whether the tree is *really* a valid tree, that is if the
31
+ # following conditions are fulfilled:
32
+ #
33
+ # * directed
34
+ # * acyclic
35
+ # * connected
36
+ #
37
+ # @return [Boolean]
38
+ def valid?
39
+ super and directed?
25
40
  end
26
41
 
27
42
  private
@@ -26,6 +26,21 @@ module Jumoku
26
26
  #
27
27
  def initialize(*params)
28
28
  super(*params) # Delegates to Plexus.
29
+ class << self; self; end.module_eval do
30
+ alias has_branch? has_edge?
31
+ end
32
+ end
33
+
34
+ # Checks whether the tree is *really* a valid tree, that is if the
35
+ # following conditions are fulfilled:
36
+ #
37
+ # * undirected
38
+ # * acyclic
39
+ # * connected
40
+ #
41
+ # @return [Boolean]
42
+ def valid?
43
+ super && !directed?
29
44
  end
30
45
 
31
46
  private
@@ -1,15 +1,12 @@
1
1
  module Jumoku
2
2
  # This module provides the basic routines needed to implement the specialized
3
- # builders: {UndirectedTreeBuilder} and {DirectedTreeBuilder}. Those
4
- # implementations are under the control of the {TreeAPI}.
3
+ # builders: {UndirectedTreeBuilder} and {DirectedTreeBuilder}.
5
4
  #
6
5
  module Shared
7
6
  def self.included(base)
8
7
  base.class_eval do
9
8
  # Late aliasing as it references methods provided by Plexus modules.
10
9
  alias has_node? has_vertex?
11
- alias has_branch? has_edge?
12
- include TreeAPI
13
10
  end
14
11
  end
15
12
 
@@ -176,7 +173,7 @@ module Jumoku
176
173
  #
177
174
  # @return [Boolean]
178
175
  def valid?
179
- acyclic? and connected? and not directed?
176
+ acyclic? and connected?
180
177
  end
181
178
 
182
179
  # Is the node a terminal node?
@@ -0,0 +1,19 @@
1
+ module Jukomu
2
+ if RUBY_VERSION < "1.9"
3
+ def ruby_18
4
+ yield
5
+ end
6
+
7
+ def ruby_19
8
+ false
9
+ end
10
+ else
11
+ def ruby_18
12
+ false
13
+ end
14
+
15
+ def ruby_19
16
+ yield
17
+ end
18
+ end
19
+ end
@@ -1,6 +1,6 @@
1
1
  module Jumoku
2
2
  MAJOR = 0
3
3
  MINOR = 2
4
- PATCH = 0
4
+ PATCH = 1
5
5
  VERSION = [MAJOR, MINOR, PATCH].join('.')
6
6
  end
@@ -0,0 +1,14 @@
1
+ require 'spec_helper'
2
+
3
+ describe Arborescence do
4
+ subject { Arborescence.new }
5
+ let(:tree) { subject }
6
+ let(:tree_type) { subject.class }
7
+ let(:branch_type) { subject.send :_branch_type }
8
+
9
+ it_should_behave_like "a legacy tree"
10
+ it "should be a directed graph" do
11
+ tree.class.ancestors.should include RawDirectedTreeBuilder
12
+ end
13
+ it_should_behave_like "a tree with extended features"
14
+ end
@@ -0,0 +1,281 @@
1
+ require 'spec_helper'
2
+
3
+ shared_examples_for "a legacy tree" do
4
+ describe "#new" do
5
+ it "should create a valid tree graph" do
6
+ tree.should be_valid
7
+ tree.nodes.should be_empty
8
+ end
9
+ end
10
+
11
+ describe "#add_node!" do
12
+ describe "an empty tree" do
13
+ it "should grow up as a valid tree when adding its first node" do
14
+ tree.nodes.size.should == 0
15
+
16
+ tree.add_node! 1 # adding a raw string as the first node
17
+ tree.nodes.size.should == 1
18
+ tree.nodes.should == [1]
19
+ end
20
+ end
21
+
22
+ describe "a tree with only one node" do
23
+ before :each do
24
+ tree.add_node! 1
25
+ end
26
+
27
+ it "should raise an error when trying to add a new, disconnected node" do
28
+ # again, I'll use childX for the sake of this specs, as it is easy to
29
+ # remember, but the node is not really a "child" of the previous node,
30
+ # it is just connected
31
+ lambda { tree.add_node! 2 }.should raise_error
32
+ end
33
+
34
+ it "should grow up as a valid tree when adding new, connected nodes" do
35
+ lambda { tree.add_node! 2, 1 }.should_not raise_error
36
+
37
+ tree.add_node! 3, 1
38
+ tree.add_node! 1, 4
39
+ tree.add_node! branch_type.new(3, 5)
40
+
41
+ tree.should be_valid
42
+ tree.nodes.size.should == 5
43
+ tree.nodes.sort.should == [1,2,3,4,5]
44
+ end
45
+
46
+ it "should raise an error when trying to form a cycle" do
47
+ tree.add_node! 2, 1
48
+ tree.add_node! 3, 1
49
+ tree.add_node! 4, 2
50
+
51
+ lambda { tree.add_node! 4, 1 }.should raise_error ForbiddenCycle
52
+ end
53
+ end
54
+ end
55
+
56
+ describe "#add_branch!" do
57
+ describe "an empty tree" do
58
+ it "should create a branch and return the updated tree" do
59
+ tree.add_branch!(:one, :two).should be_a tree_type
60
+ tree.should be_valid
61
+ tree.nodes.should == [:one, :two]
62
+ end
63
+ end
64
+
65
+ describe "a tree containing a single node" do
66
+ before :each do
67
+ tree.add_node! 1
68
+ end
69
+
70
+ it "should not allow disconnected branch creation" do
71
+ lambda { tree.add_branch! 10, 11 }.should raise_error RawTreeError
72
+ end
73
+
74
+ it "should grow up as a valid tree when populated with connected branches" do
75
+ tree.nodes.size.should == 1
76
+
77
+ tree.add_branch! 1, 2
78
+ tree.nodes.should_not be_empty
79
+ tree.nodes.size.should == 2
80
+ tree.nodes.sort.should == [1, 2]
81
+
82
+ tree.add_branch! 2, 3
83
+ tree.nodes.size.should == 3
84
+ tree.nodes.sort.should == [1, 2, 3]
85
+
86
+ tree.add_branch! 3, 4
87
+ tree.add_branch! 2, 5
88
+ lambda { tree.add_branch! 5, 5 }.should raise_error ForbiddenCycle
89
+ lambda { tree.add_branch! 1, 5 }.should raise_error ForbiddenCycle
90
+ tree.add_branch! 5, 6
91
+ tree.nodes.size.should == 6
92
+ tree.nodes.sort.should == [1, 2, 3, 4, 5, 6]
93
+ tree.should be_valid
94
+ end
95
+ end
96
+ end
97
+
98
+ describe "#remove_node!" do
99
+ describe "an empty tree" do
100
+ it "should not allow to remove a node since there's none" do
101
+ lambda { tree.remove_node! "undefinedNode" }.should raise_error UndefinedNode
102
+ end
103
+ end
104
+
105
+ describe "a tree containing a single node" do
106
+ before :each do
107
+ tree.add_node! :last
108
+ end
109
+
110
+ it "should allow for last node deletion" do
111
+ lambda { tree.remove_node! :last }.should_not raise_error
112
+ tree.nodes.should be_empty
113
+ tree.should be_valid
114
+ end
115
+ end
116
+
117
+ describe "a tree containing one branch" do
118
+ before :each do
119
+ tree.add_node! 1
120
+ tree.add_node! 2, 1
121
+ end
122
+
123
+ it "should allow to remove both nodes in any order" do
124
+ tree.remove_node! 1
125
+ tree.should be_valid
126
+ tree.nodes.sort.should == [2]
127
+ tree.should be_valid
128
+ end
129
+ end
130
+
131
+ describe "a tree containing several branches" do
132
+ before :each do
133
+ # TODO: DRY this snippet
134
+ # i stands for internal, t for terminal
135
+ tree.add_branch! :i1, :i2
136
+ tree.add_branch! :i2, :i3
137
+ tree.add_branch! :i3, :i4
138
+ tree.add_branch! :i3, :i5
139
+ tree.add_branch! :i4, :t6
140
+ tree.add_branch! :i5, :t7
141
+ tree.add_branch! :i1, :t8
142
+ end
143
+
144
+ it "should allow deletion of its terminal nodes but not of its internal nodes" do
145
+ tree.nodes.size.should == 8
146
+ lambda { tree.remove_node! :t8 }.should_not raise_error
147
+ tree.nodes.size.should == 7
148
+ tree.nodes.should_not include :t8
149
+ lambda { tree.remove_node! :i3 }.should raise_error UndefinedNode
150
+ tree.should be_valid
151
+ end
152
+
153
+ it "should remain a valid tree after any terminal node was removed" do
154
+ tree.remove_node! :t6
155
+ tree.has_branch?(:i4, :t6).should_not be_true
156
+ tree.should be_valid
157
+ end
158
+ end
159
+ end
160
+
161
+ describe "#remove_branch!" do
162
+ describe "an empty tree" do
163
+ it "should not allow for branch deletion" do
164
+ lambda { tree.remove_branch! :null, :none }.should raise_error RawTreeError
165
+ end
166
+ end
167
+
168
+ describe "an tree containing one node" do
169
+ before :each do
170
+ tree.add_node! 1
171
+ end
172
+
173
+ it "should not allow for branch deletion" do
174
+ lambda { tree.remove_branch! 1, :none }.should raise_error RawTreeError
175
+ end
176
+ end
177
+
178
+ describe "a tree containing several branches" do
179
+ before :each do
180
+ # TODO: DRY this snippet
181
+ # i stands for internal, t for terminal
182
+ tree.add_branch! :i1, :i2
183
+ tree.add_branch! :i2, :i3
184
+ tree.add_branch! :i3, :i4
185
+ tree.add_branch! :i3, :i5
186
+ tree.add_branch! :i4, :t6
187
+ tree.add_branch! :i5, :t7
188
+ tree.add_branch! :i1, :t8
189
+ end
190
+
191
+ it "should allow for terminal branch deletion (triggers terminal node deletion)" do
192
+ tree.nodes.size.should == 8
193
+ lambda { tree.remove_branch! :i1, :i2 }.should raise_error RawTreeError
194
+ tree.nodes.size.should == 8
195
+ lambda { tree.remove_branch! :i1, :t8 }.should_not raise_error RawTreeError
196
+ tree.nodes.should_not include :t8
197
+ tree.has_branch?(:i1, :t8).should be_false
198
+ tree.should be_valid
199
+
200
+ tree.remove_branch! branch_type.new(:i5, :t7)
201
+ tree.has_branch?(:i5, :t7).should be_false
202
+ tree.should be_valid
203
+ end
204
+ end
205
+ end
206
+
207
+ describe "#nodes" do
208
+ describe "an empty tree" do
209
+ it "should not have any node" do
210
+ tree.nodes.should be_empty
211
+ end
212
+ end
213
+
214
+ describe "a tree containing several nodes" do
215
+ it "should be aware of its nodes" do
216
+ tree.add_node! :solo
217
+ tree.nodes.should == [:solo]
218
+
219
+ tree.add_branch! 2, :solo
220
+ tree.add_branch! 2, 3
221
+ tree.nodes.should == [:solo, 2, 3]
222
+ tree.should be_valid
223
+ end
224
+ end
225
+ end
226
+
227
+ describe "#terminal_nodes" do
228
+ describe "an empty tree" do
229
+ it "should have no terminal nodes" do
230
+ tree.terminal_nodes.should be_empty
231
+ end
232
+ end
233
+
234
+ describe "a tree containing one node" do
235
+ it "should have one terminal node" do
236
+ tree.add_node! 1
237
+ tree.terminal_nodes.should == [1]
238
+ end
239
+ end
240
+
241
+ describe "a tree with several branches" do
242
+ before :each do
243
+ tree.add_node! 1
244
+ tree.add_node! 2, 1
245
+ tree.add_node! 3, 2
246
+ end
247
+
248
+ it "should have a least two terminal nodes" do
249
+ tree.terminal_nodes.size.should >= 2
250
+
251
+ tree.add_node! 4, 3
252
+ tree.add_node! 5, 3
253
+ tree.terminal_nodes.sort.should == [1, 4, 5]
254
+ end
255
+ end
256
+ end
257
+
258
+ describe "#branches" do
259
+ describe "an empty tree" do
260
+ it "should not have any branch" do
261
+ tree.branches.should be_empty
262
+ end
263
+ end
264
+
265
+ describe "a tree containing one node" do
266
+ it "should not have any branch" do
267
+ tree.add_node! :solo
268
+ tree.branches.should be_empty
269
+ end
270
+ end
271
+
272
+ describe "a tree containing one branch" do
273
+ it "should have one branch only" do
274
+ tree.add_node! :one
275
+ tree.add_node! :two, :one
276
+ tree.branches.size.should == 1
277
+ tree.should be_valid
278
+ end
279
+ end
280
+ end
281
+ end