jumoku 0.2.0 → 0.2.1

Sign up to get free protection for your applications and to get access to all the features.
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