rgl 0.2.3 → 0.3.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.
@@ -0,0 +1,30 @@
1
+ # -*- ruby -*-
2
+
3
+ begin
4
+ require 'rgl/dot'
5
+ require 'rgl/implicit'
6
+ rescue Exception
7
+ nil
8
+ end
9
+
10
+ # Example usage:
11
+ #
12
+ # rake -R/home/hd/src/rgl/rakelib -f /usr/lib/ruby/gems/1.8/gems/rails-1.0.0/Rakefile dep_graph
13
+ desc "Generate dependency graph of rake tasks"
14
+ task :dep_graph do |task|
15
+ this_task = task.name
16
+ dep = RGL::ImplicitGraph.new { |g|
17
+ # vertices of the graph are all defined tasks without this task
18
+ g.vertex_iterator do |b|
19
+ Rake::Task.tasks.each do |t|
20
+ b.call(t) unless t.name == this_task
21
+ end
22
+ end
23
+ # neighbors of task t are its prerequisites
24
+ g.adjacent_iterator { |t, b| t.prerequisites.each(&b) }
25
+ g.directed = true
26
+ }
27
+
28
+ dep.write_to_graphic_file('png', this_task)
29
+ puts "Wrote dependency graph to #{this_task}.png."
30
+ end
@@ -1,11 +1,9 @@
1
1
  require 'test/unit'
2
2
 
3
+ require 'rgl/traversal'
3
4
  require 'rgl/connected_components'
4
5
  require 'rgl/adjacency'
5
-
6
- class Array
7
- alias add push
8
- end
6
+ require 'test_helper'
9
7
 
10
8
  include RGL
11
9
 
@@ -0,0 +1,61 @@
1
+ $LOAD_PATH << "../lib"
2
+ require 'test/unit'
3
+ require 'rgl/adjacency'
4
+ require 'test_helper'
5
+
6
+ include RGL
7
+
8
+ class TestCycles < Test::Unit::TestCase
9
+
10
+ def setup
11
+ @dg = DirectedAdjacencyGraph.new(Array)
12
+ edges = [[1,2],[2,2],[2,3],[3,4],[4,5],[5,1],[6,4],[6,6],[1,4],[7,7],[7,7]]
13
+ edges.each do |(src,target)|
14
+ @dg.add_edge(src, target)
15
+ end
16
+
17
+ @ug = AdjacencyGraph.new(Array)
18
+ @ug.add_edges(*edges)
19
+ end
20
+
21
+ # Helper for testing for different permutations of a cycle
22
+ def contains_cycle?(cycles,cycle)
23
+ cycle.size.times do |i|
24
+ return true if cycles.include?(cycle)
25
+ cycle = cycle[1..-1] + [cycle[0]]
26
+ end
27
+ end
28
+
29
+ def test_cycles
30
+ d_cycles = @dg.cycles
31
+ assert_equal 6, d_cycles.size
32
+ assert d_cycles.include?([6])
33
+ assert d_cycles.include?([7])
34
+ assert d_cycles.include?([2])
35
+ assert contains_cycle?(d_cycles, [1,4,5])
36
+ assert contains_cycle?(d_cycles, [1,2,3,4,5])
37
+
38
+ assert_equal 5, DirectedAdjacencyGraph.new(Set, @dg).cycles.size
39
+
40
+ u_cycles = AdjacencyGraph.new(Set, @dg).cycles.sort
41
+
42
+ assert u_cycles.include?([2])
43
+ assert u_cycles.include?([6])
44
+ assert u_cycles.include?([7])
45
+ assert contains_cycle?(u_cycles, [1,2,3,4,5])
46
+ assert contains_cycle?(u_cycles, [1,5,4,3,2])
47
+ assert contains_cycle?(u_cycles, [1,4,3,2])
48
+ assert contains_cycle?(u_cycles, [1,4,5])
49
+ assert contains_cycle?(u_cycles, [1,5,4])
50
+ assert contains_cycle?(u_cycles, [1,5])
51
+ assert contains_cycle?(u_cycles, [1,2])
52
+ assert contains_cycle?(u_cycles, [1,2,3,4])
53
+ assert contains_cycle?(u_cycles, [2,3])
54
+ assert contains_cycle?(u_cycles, [1,4])
55
+ assert contains_cycle?(u_cycles, [3,4])
56
+ assert contains_cycle?(u_cycles, [4,5])
57
+ assert contains_cycle?(u_cycles, [4,6])
58
+ assert_equal 16, u_cycles.size
59
+ end
60
+
61
+ end
@@ -11,56 +11,56 @@ class TestDirectedGraph < Test::Unit::TestCase
11
11
  @dg.add_edge(src, target)
12
12
  end
13
13
  end
14
-
14
+
15
15
  def test_empty_graph
16
- dg = DirectedAdjacencyGraph.new
17
- assert dg.empty?
16
+ dg = DirectedAdjacencyGraph.new
17
+ assert dg.empty?
18
18
  assert dg.directed?
19
- assert(!dg.has_edge?(2,1))
20
- assert(!dg.has_vertex?(3))
21
- # Non existend vertex result in a Name Error because each_key is
22
- # called for nil
23
- assert_raises(NoVertexError) {dg.out_degree(3)}
24
- assert_equal([],dg.vertices)
25
- assert_equal(0,dg.size)
26
- assert_equal(0,dg.num_vertices)
27
- assert_equal(0,dg.num_edges)
28
- assert_equal(DirectedEdge,dg.edge_class)
29
- assert([].eql?(dg.edges))
19
+ assert(!dg.has_edge?(2,1))
20
+ assert(!dg.has_vertex?(3))
21
+ # Non existend vertex result in a Name Error because each_key is
22
+ # called for nil
23
+ assert_raises(NoVertexError) {dg.out_degree(3)}
24
+ assert_equal([],dg.vertices)
25
+ assert_equal(0,dg.size)
26
+ assert_equal(0,dg.num_vertices)
27
+ assert_equal(0,dg.num_edges)
28
+ assert_equal(DirectedEdge,dg.edge_class)
29
+ assert([].eql?(dg.edges))
30
30
  end
31
-
31
+
32
32
  def test_add
33
- dg = DirectedAdjacencyGraph.new
34
- dg.add_edge(1,2)
35
- assert(!dg.empty?)
36
- assert(dg.has_edge?(1,2))
37
- assert(!dg.has_edge?(2,1))
38
- assert(dg.has_vertex?(1) && dg.has_vertex?(2))
39
- assert(!dg.has_vertex?(3))
40
-
41
- assert_equal([1,2],dg.vertices.sort)
42
- assert([DirectedEdge.new(1,2)].eql?(dg.edges))
43
- assert_equal("(1-2)",dg.edges.to_s)
44
-
45
- assert_equal([2],dg.adjacent_vertices(1))
46
- assert_equal([],dg.adjacent_vertices(2))
47
-
48
- assert_equal(1,dg.out_degree(1))
49
- assert_equal(0,dg.out_degree(2))
33
+ dg = DirectedAdjacencyGraph.new
34
+ dg.add_edge(1,2)
35
+ assert(!dg.empty?)
36
+ assert(dg.has_edge?(1,2))
37
+ assert(!dg.has_edge?(2,1))
38
+ assert(dg.has_vertex?(1) && dg.has_vertex?(2))
39
+ assert(!dg.has_vertex?(3))
40
+
41
+ assert_equal([1,2],dg.vertices.sort)
42
+ assert([DirectedEdge.new(1,2)].eql?(dg.edges))
43
+ assert_equal("(1-2)",dg.edges.to_s)
44
+
45
+ assert_equal([2],dg.adjacent_vertices(1))
46
+ assert_equal([],dg.adjacent_vertices(2))
47
+
48
+ assert_equal(1,dg.out_degree(1))
49
+ assert_equal(0,dg.out_degree(2))
50
50
  end
51
-
51
+
52
52
  def test_edges
53
53
  assert_equal(4, @dg.edges.length)
54
54
  assert_equal([1,2,2,3], @dg.edges.map {|l| l.source}.sort)
55
55
  assert_equal([2,2,3,4], @dg.edges.map {|l| l.target}.sort)
56
56
  assert_equal("(1-2)(2-3)(2-4)(3-2)", @dg.edges.map {|l| l.to_s}.sort.join)
57
- # assert_equal([0,1,2,3], @dg.edges.map {|l| l.info}.sort)
57
+ # assert_equal([0,1,2,3], @dg.edges.map {|l| l.info}.sort)
58
58
  end
59
-
59
+
60
60
  def test_vertices
61
61
  assert_equal([1,2,3,4], @dg.vertices.sort)
62
62
  end
63
-
63
+
64
64
  def test_edges_from_to?
65
65
  assert @dg.has_edge?(1,2)
66
66
  assert @dg.has_edge?(2,3)
@@ -71,40 +71,40 @@ class TestDirectedGraph < Test::Unit::TestCase
71
71
  assert !@dg.has_edge?(4,1)
72
72
  assert !@dg.has_edge?(4,2)
73
73
  end
74
-
74
+
75
75
  def test_remove_edges
76
- @dg.remove_edge 1,2
77
- assert !@dg.has_edge?(1,2)
78
- @dg.remove_edge 1,2
79
- assert !@dg.has_edge?(1,2)
80
- @dg.remove_vertex 3
81
- assert !@dg.has_vertex?(3)
82
- assert !@dg.has_edge?(2,3)
83
- assert_equal("(2-4)",@dg.to_s)
76
+ @dg.remove_edge 1,2
77
+ assert !@dg.has_edge?(1,2)
78
+ @dg.remove_edge 1,2
79
+ assert !@dg.has_edge?(1,2)
80
+ @dg.remove_vertex 3
81
+ assert !@dg.has_vertex?(3)
82
+ assert !@dg.has_edge?(2,3)
83
+ assert_equal("(2-4)",@dg.to_s)
84
84
  end
85
-
85
+
86
86
  def test_add_vertices
87
- dg = DirectedAdjacencyGraph.new
88
- dg.add_vertices 1,3,2,4
89
- assert_equal dg.vertices.sort, [1,2,3,4]
90
-
91
- dg.remove_vertices 1,3
92
- assert_equal dg.vertices.sort, [2,4]
87
+ dg = DirectedAdjacencyGraph.new
88
+ dg.add_vertices 1,3,2,4
89
+ assert_equal dg.vertices.sort, [1,2,3,4]
90
+
91
+ dg.remove_vertices 1,3
92
+ assert_equal dg.vertices.sort, [2,4]
93
93
  end
94
-
94
+
95
95
  def test_creating_from_array
96
- dg = DirectedAdjacencyGraph[1, 2, 3, 4]
97
- assert_equal(dg.vertices.sort, [1,2,3,4])
98
- assert_equal(dg.edges.to_s, "(1-2)(3-4)")
96
+ dg = DirectedAdjacencyGraph[1, 2, 3, 4]
97
+ assert_equal(dg.vertices.sort, [1,2,3,4])
98
+ assert_equal(dg.edges.to_s, "(1-2)(3-4)")
99
99
  end
100
-
100
+
101
101
  def test_reverse
102
102
  reverted = @dg.reverse
103
103
  @dg.each_edge do |u,v|
104
104
  assert(reverted.has_edge?(v,u))
105
105
  end
106
106
  end
107
-
107
+
108
108
  def test_reverse
109
109
  # Add isolated vertex
110
110
  @dg.add_vertex(42)
@@ -113,8 +113,13 @@ class TestDirectedGraph < Test::Unit::TestCase
113
113
  @dg.each_edge do |u,v|
114
114
  assert(reverted.has_edge?(v,u))
115
115
  end
116
-
116
+
117
117
  assert(reverted.has_vertex?(42),
118
118
  "Reverted graph should contain isolated Vertex 42")
119
119
  end
120
+
121
+ def test_to_undirected
122
+ undirected = @dg.to_undirected
123
+ assert_equal undirected.edges.sort.to_s, "(1=2)(2=3)(2=4)"
124
+ end
120
125
  end
@@ -0,0 +1,18 @@
1
+ require 'test/unit'
2
+ require 'rgl/dot'
3
+
4
+ class TestDot < Test::Unit::TestCase
5
+
6
+ def test_to_dot_graph
7
+ graph = RGL::DirectedAdjacencyGraph[1,2]
8
+ dot = graph.to_dot_graph.to_s
9
+ assert_match(dot, /\{[^}]*\}/) # {...}
10
+ assert_match(dot, /1\s*\[/) # node 1
11
+ assert_match(dot, /2\s*\[/) # node 2
12
+ assert_match(dot, /1\s*->\s*2/) # edge
13
+ end
14
+
15
+ def assert_match(dot, pattern)
16
+ assert(!(dot =~ pattern).nil?, "#{dot} doesn't match #{pattern}")
17
+ end
18
+ end
@@ -4,30 +4,31 @@ require 'rgl/base'
4
4
  include RGL::Edge
5
5
 
6
6
  class TestEdge < Test::Unit::TestCase
7
-
7
+
8
8
  def test_directed_edge
9
- assert_raises(ArgumentError) {DirectedEdge.new}
10
- e = DirectedEdge.new 1,2
11
- assert_equal(1,e.source)
12
- assert_equal(2,e.target)
13
- assert_equal([1,2],e.to_a)
14
- assert_equal("(1-2)",e.to_s)
15
- assert_equal([1,2],[e[0],e[1]])
16
- assert(DirectedEdge.new(1,2).eql?(DirectedEdge.new(1,2)))
17
- assert(!DirectedEdge.new(1,2).eql?(DirectedEdge.new(1,3)))
18
- assert(!DirectedEdge.new(2,1).eql?(DirectedEdge.new(1,2)))
9
+ assert_raises(ArgumentError) {DirectedEdge.new}
10
+ e = DirectedEdge.new 1,2
11
+ assert_equal(1,e.source)
12
+ assert_equal(2,e.target)
13
+ assert_equal([1,2],e.to_a)
14
+ assert_equal("(1-2)",e.to_s)
15
+ assert_equal("(2-1)",e.reverse.to_s)
16
+ assert_equal([1,2],[e[0],e[1]])
17
+ assert(DirectedEdge[1,2].eql?(DirectedEdge.new(1,2)))
18
+ assert(!DirectedEdge[1,2].eql?(DirectedEdge.new(1,3)))
19
+ assert(!DirectedEdge[2,1].eql?(DirectedEdge.new(1,2)))
19
20
  end
20
-
21
+
21
22
  def test_undirected_edge
22
- assert_raises(ArgumentError) {UnDirectedEdge.new}
23
- e = UnDirectedEdge.new 1,2
24
- assert_equal(1,e.source)
25
- assert_equal(2,e.target)
26
- assert_equal([1,2],e.to_a)
27
- assert_equal("(1=2)",e.to_s)
28
- assert(UnDirectedEdge.new(1,2).eql?(UnDirectedEdge.new(2,1)))
29
- assert(!UnDirectedEdge.new(1,3).eql?(UnDirectedEdge.new(2,1)))
30
- assert_equal(UnDirectedEdge.new(1,2).hash,UnDirectedEdge.new(1,2).hash)
23
+ assert_raises(ArgumentError) {UnDirectedEdge.new}
24
+ e = UnDirectedEdge.new 1,2
25
+ assert_equal(1,e.source)
26
+ assert_equal(2,e.target)
27
+ assert_equal([1,2],e.to_a)
28
+ assert_equal("(1=2)",e.to_s)
29
+ assert(UnDirectedEdge.new(1,2).eql?(UnDirectedEdge.new(2,1)))
30
+ assert(!UnDirectedEdge.new(1,3).eql?(UnDirectedEdge.new(2,1)))
31
+ assert_equal(UnDirectedEdge.new(1,2).hash,UnDirectedEdge.new(1,2).hash)
31
32
  end
32
-
33
+
33
34
  end
@@ -0,0 +1,55 @@
1
+ require 'test/unit'
2
+ require 'rgl/adjacency'
3
+ require 'test_helper'
4
+
5
+ include RGL
6
+
7
+ class TestGraph < Test::Unit::TestCase
8
+
9
+ class NotImplementedGraph
10
+ include Graph
11
+ end
12
+
13
+ def setup
14
+ @dg = DirectedAdjacencyGraph.new
15
+ @edges = [[1,2],[2,3],[2,4],[4,5],[1,6],[6,4]]
16
+ @edges.each do |(src,target)|
17
+ @dg.add_edge(src, target)
18
+ end
19
+
20
+ @ug = AdjacencyGraph.new(Array)
21
+ @ug.add_edges(*@edges)
22
+ end
23
+
24
+ def test_equality
25
+ assert @dg == @dg
26
+ assert @dg == @dg.dup
27
+ assert @ug == @ug.dup
28
+ assert @ug != @dg
29
+ assert @dg != @ug
30
+ assert @dg != 42
31
+ dup = DirectedAdjacencyGraph[*@edges.flatten]
32
+ assert @dg == dup
33
+ @dg.add_vertex 42
34
+ assert @dg != dup
35
+ end
36
+
37
+ def test_merge
38
+ merge = DirectedAdjacencyGraph.new(Array, @dg, @ug)
39
+ assert merge.num_edges == 12
40
+ merge = DirectedAdjacencyGraph.new(Set, @dg, @dg)
41
+ assert merge.num_edges == 6
42
+ end
43
+
44
+ def test_set_edgelist_class
45
+ edges = @dg.edges
46
+ @dg.edgelist_class=Array
47
+ assert_equal edges, @dg.edges
48
+ end
49
+
50
+ def test_not_implemented
51
+ graph = NotImplementedGraph.new
52
+ assert_raise(NotImplementedError) { graph.each_vertex }
53
+ assert_raise(NotImplementedError) { graph.each_adjacent(nil) }
54
+ end
55
+ end
@@ -19,7 +19,7 @@ class TestGraphXML < Test::Unit::TestCase
19
19
  end
20
20
 
21
21
  def test_graphxml
22
- @dg = DirectedAdjacencyGraph.from_graphxml(@stream).to_s
22
+ @dg = DirectedAdjacencyGraph.new.from_graphxml(@stream).to_s
23
23
  assert_equal("(n0-n1)(n0-n2)(n0-n9)(n3-n4)(n4-n5)(n5-n7)(n8-n0)(n8-n3)(n8-n4)(n8-n5)(n8-n6)",@dg.to_s)
24
24
  end
25
25
 
@@ -35,7 +35,7 @@ class TestGraphXML < Test::Unit::TestCase
35
35
  File.open(NORTH_DIR + name + '.graphml') {
36
36
  |file|
37
37
  print '.'; $stdout.flush
38
- graph = (directed == 'true' ? DirectedAdjacencyGraph : AdjacencyGraph).from_graphxml(file)
38
+ graph = (directed == 'true' ? DirectedAdjacencyGraph : AdjacencyGraph).new.from_graphxml(file)
39
39
  #graph.write_to_graphic_file
40
40
  assert_equal(nnodes,graph.num_vertices)
41
41
  assert_equal(nedges,graph.num_edges)
@@ -0,0 +1,815 @@
1
+ require 'test/unit'
2
+ require 'rgl/rdot'
3
+
4
+ include DOT
5
+
6
+ # Add some helper methods to TestCase
7
+ class Test::Unit::TestCase
8
+
9
+ # assert string matches regular expression
10
+ def assert_match(dot, pattern)
11
+ assert(!(dot =~ pattern).nil?, "#{dot} doesn't match #{pattern}")
12
+ end
13
+
14
+ # assert string doesn't match regular expression
15
+ def assert_no_match(dot, pattern)
16
+ assert((dot =~ pattern).nil?, "#{dot} shouldn't match #{pattern}")
17
+ end
18
+
19
+ end
20
+
21
+ # Tests for DOTPort
22
+ class TestDotPort < Test::Unit::TestCase
23
+ def test_name
24
+ port = DOTPort.new()
25
+ assert_equal('', port.to_s)
26
+
27
+ port = DOTPort.new('test_name')
28
+ assert_equal('<test_name>', port.to_s)
29
+ end
30
+
31
+ def test_label
32
+ port = DOTPort.new(nil, 'test_label')
33
+ assert_equal('test_label', port.to_s)
34
+ end
35
+
36
+ def test_name_and_label
37
+ port = DOTPort.new('test_name', 'test_label')
38
+ assert_equal('<test_name> test_label', port.to_s)
39
+ end
40
+
41
+ def test_nested_ports
42
+ port = DOTPort.new([DOTPort.new(nil, 'a'), DOTPort.new(nil, 'b')])
43
+ assert_equal('{a | b}', port.to_s)
44
+ end
45
+
46
+ def test_name_label_and_nested_ports
47
+ port = DOTPort.new('test_name', 'test_label')
48
+ port.ports = [DOTPort.new(nil, 'a'), DOTPort.new(nil, 'b')]
49
+ assert_equal('{a | b}', port.to_s)
50
+ end
51
+ end
52
+
53
+ # Tests for DOTNode
54
+ class TestDotNode < Test::Unit::TestCase
55
+
56
+ def test_no_name
57
+ node = DOTNode.new()
58
+ dot = node.to_s
59
+ assert_nil(dot)
60
+ end
61
+
62
+ # bug 16125
63
+ def test_1prop_0comma
64
+ node = DOTNode.new({"label"=>"the_label"})
65
+ dot = node.to_s
66
+ assert_no_match(dot, /,/)
67
+ end
68
+
69
+ def test_2prop_1comma
70
+ node = DOTNode.new({"label"=>"the_label", "shape"=>"ellipse"})
71
+ dot = node.to_s
72
+ assert_match(dot, /\[[^,]*,[^,]*\]/)
73
+ end
74
+
75
+ def test_name_without_label
76
+ node = DOTNode.new({"name"=>"test_name"})
77
+ dot = node.to_s
78
+ assert_no_match(dot, /label/)
79
+ end
80
+
81
+ def test_no_label
82
+ node = DOTNode.new({"shape"=>"ellipse"})
83
+ dot = node.to_s
84
+ assert_no_match(dot, /label/)
85
+ end
86
+
87
+ def test_Mrecord_no_label_no_ports
88
+ node = DOTNode.new({"name" => "test_name", "shape"=>"Mrecord"})
89
+ dot = node.to_s
90
+ assert_match(dot, /shape\s*=\s*Mrecord/)
91
+ assert_no_match(dot, /label/)
92
+ end
93
+
94
+ def test_Mrecord_label_no_ports
95
+ node = DOTNode.new({"name" => "test_name", "label" => "test_label", "shape"=>"Mrecord"})
96
+ dot = node.to_s
97
+ assert_match(dot, /shape\s*=\s*Mrecord/)
98
+ assert_match(dot, /label\s*=\s*test_label/)
99
+ end
100
+
101
+ def test_Mrecord_label_with_ports
102
+ node = DOTNode.new({"name" => "test_name", "label" => "test_label", "shape"=>"Mrecord"})
103
+ node.ports << DOTPort.new(nil, "a")
104
+ node.ports << DOTPort.new(nil, "b")
105
+ dot = node.to_s
106
+ assert_match(dot, /shape\s*=\s*Mrecord/)
107
+ assert_match(dot, /label\s*=\s*"a\s*|\s*b"/)
108
+ end
109
+
110
+ def test_Mrecord_no_label_with_ports
111
+ node = DOTNode.new({"name" => "test_name", "shape"=>"Mrecord"})
112
+ node.ports << DOTPort.new(nil, "a")
113
+ node.ports << DOTPort.new(nil, "b")
114
+ dot = node.to_s
115
+ assert_match(dot, /shape\s*=\s*Mrecord/)
116
+ assert_match(dot, /label\s*=\s*"a\s*|\s*b"/)
117
+ end
118
+
119
+ def test_record_no_label_no_ports
120
+ node = DOTNode.new({"name" => "test_name", "shape"=>"record"})
121
+ dot = node.to_s
122
+ assert_match(dot, /shape\s*=\s*record/)
123
+ assert_no_match(dot, /label/)
124
+ end
125
+
126
+ def test_record_label_no_ports
127
+ node = DOTNode.new({"name" => "test_name", "label" => "test_label", "shape"=>"record"})
128
+ dot = node.to_s
129
+ assert_match(dot, /shape\s*=\s*record/)
130
+ assert_match(dot, /label\s*=\s*test_label/)
131
+ end
132
+
133
+ def test_record_label_with_ports
134
+ node = DOTNode.new({"name" => "test_name", "label" => "test_label", "shape"=>"record"})
135
+ node.ports << DOTPort.new(nil, "a")
136
+ node.ports << DOTPort.new(nil, "b")
137
+ dot = node.to_s
138
+ assert_match(dot, /shape\s*=\s*record/)
139
+ assert_match(dot, /label\s*=\s*"a\s*|\s*b"/)
140
+ end
141
+
142
+ def test_record_no_label_with_ports
143
+ node = DOTNode.new({"name" => "test_name", "shape"=>"record"})
144
+ node.ports << DOTPort.new(nil, "a")
145
+ node.ports << DOTPort.new(nil, "b")
146
+ dot = node.to_s
147
+ assert_match(dot, /shape\s*=\s*record/)
148
+ assert_match(dot, /label\s*=\s*"a\s*|\s*b"/)
149
+ end
150
+
151
+ def test_no_shape_no_label_no_ports
152
+ node = DOTNode.new({"name" => "test_name"})
153
+ node.ports << DOTPort.new(nil, "a")
154
+ node.ports << DOTPort.new(nil, "b")
155
+ dot = node.to_s
156
+ assert_no_match(dot, /shape\s*=\s/)
157
+ assert_no_match(dot, /label\s*=\s*/)
158
+ end
159
+
160
+ def test_no_shape_no_label_with_ports
161
+ node = DOTNode.new({"name" => "test_name"})
162
+ node.ports << DOTPort.new(nil, "a")
163
+ node.ports << DOTPort.new(nil, "b")
164
+ dot = node.to_s
165
+ assert_no_match(dot, /shape\s*=\s*record/)
166
+ assert_no_match(dot, /label\s*=\s*/)
167
+ end
168
+
169
+ def test_name_quoting
170
+ node = DOTNode.new({"name" => "Name with spaces"})
171
+ dot = node.to_s
172
+ assert_match(dot, /^"Name with spaces"$/)
173
+
174
+ node = DOTNode.new({"name" => "Name with \"quotes\""})
175
+ dot = node.to_s
176
+ assert_match(dot, /^"Name with \\"quotes\\""$/)
177
+
178
+ node = DOTNode.new({"name" => "Name with \\backslashes\\"})
179
+ dot = node.to_s
180
+ assert_match(dot, /^"Name with \\\\backslashes\\\\"$/)
181
+
182
+ node = DOTNode.new({"name" => "123.456"})
183
+ dot = node.to_s
184
+ assert_match(dot, /^123.456$/)
185
+
186
+ node = DOTNode.new({"name" => ".456"})
187
+ dot = node.to_s
188
+ assert_match(dot, /^.456$/)
189
+
190
+ node = DOTNode.new({"name" => "-.456"})
191
+ dot = node.to_s
192
+ assert_match(dot, /^-.456$/)
193
+
194
+ node = DOTNode.new({"name" => "-456"})
195
+ dot = node.to_s
196
+ assert_match(dot, /^-456$/)
197
+
198
+ node = DOTNode.new({"name" => "-123.456"})
199
+ dot = node.to_s
200
+ assert_match(dot, /^-123.456$/)
201
+
202
+ node = DOTNode.new({"name" => "<html><head><title>test</title></head>\n<body>text</body></html>"})
203
+ dot = node.to_s
204
+ assert_match(dot, /^<html><head><title>test<\/title><\/head>\n<body>text<\/body><\/html>$/)
205
+ end
206
+
207
+ def test_label_quoting
208
+ node = DOTNode.new({"name" => "test_name", "label" => "Label with spaces"})
209
+ dot = node.to_s
210
+ assert_match(dot, /label\s*=\s*"Label with spaces"/)
211
+
212
+ node = DOTNode.new({"name" => "test_name", "label" => "Label with \"quotes\""})
213
+ dot = node.to_s
214
+ assert_match(dot, /label\s*=\s*"Label with \\"quotes\\""/)
215
+
216
+ node = DOTNode.new({"name" => "test_name", "label" => "Label with \\backslashes\\"})
217
+ dot = node.to_s
218
+ assert_match(dot, /label\s*=\s*"Label with \\\\backslashes\\\\"/)
219
+
220
+ node = DOTNode.new({"name" => "test_name", "label" => "Label with\nembedded newline"})
221
+ dot = node.to_s
222
+ assert_match(dot, /label\s*=\s*"Label with\\nembedded newline"/)
223
+
224
+ node = DOTNode.new({"name" => "test_name", "label" => "Left justified label\\l"})
225
+ dot = node.to_s
226
+ assert_match(dot, /label\s*=\s*"Left justified label\\l"/)
227
+
228
+ node = DOTNode.new({"name" => "test_name", "label" => "Right justified label\\r"})
229
+ dot = node.to_s
230
+ assert_match(dot, /label\s*=\s*"Right justified label\\r"/)
231
+
232
+ node = DOTNode.new({"name" => "test_name", "label" => "123.456"})
233
+ dot = node.to_s
234
+ assert_match(dot, /label\s*=\s*123.456/)
235
+
236
+ node = DOTNode.new({"name" => "test_name", "label" => ".456"})
237
+ dot = node.to_s
238
+ assert_match(dot, /label\s*=\s*.456/)
239
+
240
+ node = DOTNode.new({"name" => "test_name", "label" => "-.456"})
241
+ dot = node.to_s
242
+ assert_match(dot, /label\s*=\s*-.456/)
243
+
244
+ node = DOTNode.new({"name" => "test_name", "label" => "-456"})
245
+ dot = node.to_s
246
+ assert_match(dot, /label\s*=\s*-456/)
247
+
248
+ node = DOTNode.new({"name" => "test_name", "label" => "-123.456"})
249
+ dot = node.to_s
250
+ assert_match(dot, /label\s*=\s*-123.456/)
251
+
252
+ node = DOTNode.new({"name" => "test_name", "label" => "<html><head><title>test</title></head>\n<body>text</body></html>"})
253
+ dot = node.to_s
254
+ assert_match(dot, /label\s*=\s*<html><head><title>test<\/title><\/head>\n<body>text<\/body><\/html>/)
255
+ end
256
+
257
+ def test_option_quoting
258
+ node = DOTNode.new({"name" => "test_name", "comment" => "Comment with spaces"})
259
+ dot = node.to_s
260
+ assert_match(dot, /comment\s*=\s*"Comment with spaces"/)
261
+
262
+ node = DOTNode.new({"name" => "test_name", "comment" => "Comment with \"quotes\""})
263
+ dot = node.to_s
264
+ assert_match(dot, /comment\s*=\s*"Comment with \\"quotes\\""/)
265
+
266
+ node = DOTNode.new({"name" => "test_name", "comment" => "Comment with \\backslashes\\"})
267
+ dot = node.to_s
268
+ assert_match(dot, /comment\s*=\s*"Comment with \\\\backslashes\\\\"/)
269
+
270
+ node = DOTNode.new({"name" => "test_name", "comment" => "123.456"})
271
+ dot = node.to_s
272
+ assert_match(dot, /comment\s*=\s*123.456/)
273
+
274
+ node = DOTNode.new({"name" => "test_name", "comment" => ".456"})
275
+ dot = node.to_s
276
+ assert_match(dot, /comment\s*=\s*.456/)
277
+
278
+ node = DOTNode.new({"name" => "test_name", "comment" => "-.456"})
279
+ dot = node.to_s
280
+ assert_match(dot, /comment\s*=\s*-.456/)
281
+
282
+ node = DOTNode.new({"name" => "test_name", "comment" => "-456"})
283
+ dot = node.to_s
284
+ assert_match(dot, /comment\s*=\s*-456/)
285
+
286
+ node = DOTNode.new({"name" => "test_name", "comment" => "-123.456"})
287
+ dot = node.to_s
288
+ assert_match(dot, /comment\s*=\s*-123.456/)
289
+
290
+ node = DOTNode.new({"name" => "test_name", "comment" => "<html><head><title>test</title></head>\n<body>text</body></html>"})
291
+ dot = node.to_s
292
+ assert_match(dot, /comment\s*=\s*<html><head><title>test<\/title><\/head>\n<body>text<\/body><\/html>/)
293
+ end
294
+ end
295
+
296
+ # Tests for DOTEdge
297
+ class TestDotEdge < Test::Unit::TestCase
298
+
299
+ def test_0prop
300
+ edge = DOTEdge.new({'from' => 'a', 'to' => 'b'})
301
+ dot = edge.to_s
302
+ assert_equal('a -- b', dot)
303
+ end
304
+
305
+ def test_1prop_0comma
306
+ edge = DOTEdge.new({"label"=>"the_label"})
307
+ dot = edge.to_s
308
+ assert_no_match(dot, /,/)
309
+ end
310
+
311
+ def test_2prop_1comma
312
+ edge = DOTEdge.new({"label"=>"the_label", "weight"=>"2"})
313
+ dot = edge.to_s
314
+ assert_match(dot, /\[[^,]*,[^,]*\]/)
315
+ end
316
+
317
+ def test_no_label
318
+ edge = DOTEdge.new({"weight"=>"2"})
319
+ dot = edge.to_s
320
+ assert_no_match(dot, /label/)
321
+ end
322
+ end
323
+
324
+ # Tests for DOTDirectedEdge
325
+ class TestDotDirectedEdge < Test::Unit::TestCase
326
+
327
+ def test_0prop
328
+ edge = DOTDirectedEdge.new({'from' => 'a', 'to' => 'b'})
329
+ dot = edge.to_s
330
+ assert_equal('a -> b', dot)
331
+ end
332
+
333
+ def test_1prop_0comma
334
+ edge = DOTDirectedEdge.new({"label"=>"the_label"})
335
+ dot = edge.to_s
336
+ assert_no_match(dot, /,/)
337
+ end
338
+
339
+ def test_2prop_1comma
340
+ edge = DOTDirectedEdge.new({"label"=>"the_label", "weight"=>"2"})
341
+ dot = edge.to_s
342
+ assert_match(dot, /\[[^,]*,[^,]*\]/)
343
+ end
344
+
345
+ def test_no_label
346
+ edge = DOTDirectedEdge.new({"weight"=>"2"})
347
+ dot = edge.to_s
348
+ assert_no_match(dot, /label/)
349
+ end
350
+ end
351
+
352
+ # Tests for DOTGraph
353
+ class TestDotGraph < Test::Unit::TestCase
354
+ def test_graph_statement
355
+ graph = DOTGraph.new()
356
+ dot = graph.to_s
357
+ assert_match(dot, /^\s*graph /)
358
+ end
359
+
360
+ def test_name_quoting
361
+ node = DOTGraph.new({"name" => "Name with spaces"})
362
+ dot = node.to_s
363
+ assert_match(dot, /^graph "Name with spaces" \{$/)
364
+
365
+ node = DOTGraph.new({"name" => "Name with \"quotes\""})
366
+ dot = node.to_s
367
+ assert_match(dot, /^graph "Name with \\"quotes\\"" \{$/)
368
+
369
+ node = DOTGraph.new({"name" => "Name with \\backslashes\\"})
370
+ dot = node.to_s
371
+ assert_match(dot, /^graph "Name with \\\\backslashes\\\\" \{$/)
372
+
373
+ node = DOTGraph.new({"name" => "123.456"})
374
+ dot = node.to_s
375
+ assert_match(dot, /^graph 123.456 \{$/)
376
+
377
+ node = DOTGraph.new({"name" => ".456"})
378
+ dot = node.to_s
379
+ assert_match(dot, /^graph .456 \{$/)
380
+
381
+ node = DOTGraph.new({"name" => "-.456"})
382
+ dot = node.to_s
383
+ assert_match(dot, /^graph -.456 \{$/)
384
+
385
+ node = DOTGraph.new({"name" => "-456"})
386
+ dot = node.to_s
387
+ assert_match(dot, /^graph -456 \{$/)
388
+
389
+ node = DOTGraph.new({"name" => "-123.456"})
390
+ dot = node.to_s
391
+ assert_match(dot, /^graph -123.456 \{$/)
392
+
393
+ node = DOTGraph.new({"name" => "<html><head><title>test</title></head>\n<body>text</body></html>"})
394
+ dot = node.to_s
395
+ assert_match(dot, /^graph <html><head><title>test<\/title><\/head>\n<body>text<\/body><\/html> \{$/)
396
+ end
397
+
398
+ def test_label_quoting
399
+ node = DOTGraph.new({"name" => "test_name", "label" => "Label with spaces"})
400
+ dot = node.to_s
401
+ assert_match(dot, /label\s*=\s*"Label with spaces"/)
402
+
403
+ node = DOTGraph.new({"name" => "test_name", "label" => "Label with \"quotes\""})
404
+ dot = node.to_s
405
+ assert_match(dot, /label\s*=\s*"Label with \\"quotes\\""/)
406
+
407
+ node = DOTGraph.new({"name" => "test_name", "label" => "Label with \\backslashes\\"})
408
+ dot = node.to_s
409
+ assert_match(dot, /label\s*=\s*"Label with \\\\backslashes\\\\"/)
410
+
411
+ node = DOTGraph.new({"name" => "test_name", "label" => "Label with\nembedded newline"})
412
+ dot = node.to_s
413
+ assert_match(dot, /label\s*=\s*"Label with\\nembedded newline"/)
414
+
415
+ node = DOTGraph.new({"name" => "test_name", "label" => "Left justified label\\l"})
416
+ dot = node.to_s
417
+ assert_match(dot, /label\s*=\s*"Left justified label\\l"/)
418
+
419
+ node = DOTGraph.new({"name" => "test_name", "label" => "Right justified label\\r"})
420
+ dot = node.to_s
421
+ assert_match(dot, /label\s*=\s*"Right justified label\\r"/)
422
+
423
+ node = DOTGraph.new({"name" => "test_name", "label" => "123.456"})
424
+ dot = node.to_s
425
+ assert_match(dot, /label\s*=\s*123.456/)
426
+
427
+ node = DOTGraph.new({"name" => "test_name", "label" => ".456"})
428
+ dot = node.to_s
429
+ assert_match(dot, /label\s*=\s*.456/)
430
+
431
+ node = DOTGraph.new({"name" => "test_name", "label" => "-.456"})
432
+ dot = node.to_s
433
+ assert_match(dot, /label\s*=\s*-.456/)
434
+
435
+ node = DOTGraph.new({"name" => "test_name", "label" => "-456"})
436
+ dot = node.to_s
437
+ assert_match(dot, /label\s*=\s*-456/)
438
+
439
+ node = DOTGraph.new({"name" => "test_name", "label" => "-123.456"})
440
+ dot = node.to_s
441
+ assert_match(dot, /label\s*=\s*-123.456/)
442
+
443
+ node = DOTGraph.new({"name" => "test_name", "label" => "<html><head><title>test</title></head>\n<body>text</body></html>"})
444
+ dot = node.to_s
445
+ assert_match(dot, /label\s*=\s*<html><head><title>test<\/title><\/head>\n<body>text<\/body><\/html>/)
446
+ end
447
+
448
+ def test_option_quoting
449
+ node = DOTGraph.new({"name" => "test_name", "comment" => "Comment with spaces"})
450
+ dot = node.to_s
451
+ assert_match(dot, /comment\s*=\s*"Comment with spaces"/)
452
+
453
+ node = DOTGraph.new({"name" => "test_name", "comment" => "Comment with \"quotes\""})
454
+ dot = node.to_s
455
+ assert_match(dot, /comment\s*=\s*"Comment with \\"quotes\\""/)
456
+
457
+ node = DOTGraph.new({"name" => "test_name", "comment" => "Comment with \\backslashes\\"})
458
+ dot = node.to_s
459
+ assert_match(dot, /comment\s*=\s*"Comment with \\\\backslashes\\\\"/)
460
+
461
+ node = DOTGraph.new({"name" => "test_name", "comment" => "123.456"})
462
+ dot = node.to_s
463
+ assert_match(dot, /comment\s*=\s*123.456/)
464
+
465
+ node = DOTGraph.new({"name" => "test_name", "comment" => ".456"})
466
+ dot = node.to_s
467
+ assert_match(dot, /comment\s*=\s*.456/)
468
+
469
+ node = DOTGraph.new({"name" => "test_name", "comment" => "-.456"})
470
+ dot = node.to_s
471
+ assert_match(dot, /comment\s*=\s*-.456/)
472
+
473
+ node = DOTGraph.new({"name" => "test_name", "comment" => "-456"})
474
+ dot = node.to_s
475
+ assert_match(dot, /comment\s*=\s*-456/)
476
+
477
+ node = DOTGraph.new({"name" => "test_name", "comment" => "-123.456"})
478
+ dot = node.to_s
479
+ assert_match(dot, /comment\s*=\s*-123.456/)
480
+
481
+ node = DOTGraph.new({"name" => "test_name", "comment" => "<html><head><title>test</title></head>\n<body>text</body></html>"})
482
+ dot = node.to_s
483
+ assert_match(dot, /comment\s*=\s*<html><head><title>test<\/title><\/head>\n<body>text<\/body><\/html>/)
484
+ end
485
+
486
+ def test_element_containment
487
+ node1 = DOTNode.new('name' => 'test_node1')
488
+ node2 = DOTNode.new('name' => 'test_node2')
489
+
490
+ graph = DOTGraph.new('name' => 'test_graph')
491
+ assert_nil(graph.pop)
492
+ assert_equal(graph, graph.push(node1))
493
+ assert_equal(graph, graph << node2)
494
+ graph.each_element do |element|
495
+ assert([node1, node2].include?(element))
496
+ end
497
+ assert_equal(node2, graph.pop)
498
+ assert_equal(node1, graph.pop)
499
+ assert_nil(graph.pop)
500
+
501
+ graph = DOTGraph.new('name' => 'test_graph', 'elements' => [node1])
502
+ assert_equal(node1, graph.pop)
503
+ assert_nil(graph.pop)
504
+ end
505
+ end
506
+
507
+ # Tests for DOTDigraph
508
+ class TestDotDigraph < Test::Unit::TestCase
509
+ def test_digraph_statement
510
+ digraph = DOTDigraph.new()
511
+ dot = digraph.to_s
512
+ assert_match(dot, /^\s*digraph /)
513
+ end
514
+
515
+ def test_name_quoting
516
+ node = DOTDigraph.new({"name" => "Name with spaces"})
517
+ dot = node.to_s
518
+ assert_match(dot, /^digraph "Name with spaces" \{$/)
519
+
520
+ node = DOTDigraph.new({"name" => "Name with \"quotes\""})
521
+ dot = node.to_s
522
+ assert_match(dot, /^digraph "Name with \\"quotes\\"" \{$/)
523
+
524
+ node = DOTDigraph.new({"name" => "Name with \\backslashes\\"})
525
+ dot = node.to_s
526
+ assert_match(dot, /^digraph "Name with \\\\backslashes\\\\" \{$/)
527
+
528
+ node = DOTDigraph.new({"name" => "123.456"})
529
+ dot = node.to_s
530
+ assert_match(dot, /^digraph 123.456 \{$/)
531
+
532
+ node = DOTDigraph.new({"name" => ".456"})
533
+ dot = node.to_s
534
+ assert_match(dot, /^digraph .456 \{$/)
535
+
536
+ node = DOTDigraph.new({"name" => "-.456"})
537
+ dot = node.to_s
538
+ assert_match(dot, /^digraph -.456 \{$/)
539
+
540
+ node = DOTDigraph.new({"name" => "-456"})
541
+ dot = node.to_s
542
+ assert_match(dot, /^digraph -456 \{$/)
543
+
544
+ node = DOTDigraph.new({"name" => "-123.456"})
545
+ dot = node.to_s
546
+ assert_match(dot, /^digraph -123.456 \{$/)
547
+
548
+ node = DOTDigraph.new({"name" => "<html><head><title>test</title></head>\n<body>text</body></html>"})
549
+ dot = node.to_s
550
+ assert_match(dot, /^digraph <html><head><title>test<\/title><\/head>\n<body>text<\/body><\/html> \{$/)
551
+ end
552
+
553
+ def test_label_quoting
554
+ node = DOTDigraph.new({"name" => "test_name", "label" => "Label with spaces"})
555
+ dot = node.to_s
556
+ assert_match(dot, /label\s*=\s*"Label with spaces"/)
557
+
558
+ node = DOTDigraph.new({"name" => "test_name", "label" => "Label with \"quotes\""})
559
+ dot = node.to_s
560
+ assert_match(dot, /label\s*=\s*"Label with \\"quotes\\""/)
561
+
562
+ node = DOTDigraph.new({"name" => "test_name", "label" => "Label with \\backslashes\\"})
563
+ dot = node.to_s
564
+ assert_match(dot, /label\s*=\s*"Label with \\\\backslashes\\\\"/)
565
+
566
+ node = DOTDigraph.new({"name" => "test_name", "label" => "Label with\nembedded newline"})
567
+ dot = node.to_s
568
+ assert_match(dot, /label\s*=\s*"Label with\\nembedded newline"/)
569
+
570
+ node = DOTDigraph.new({"name" => "test_name", "label" => "Left justified label\\l"})
571
+ dot = node.to_s
572
+ assert_match(dot, /label\s*=\s*"Left justified label\\l"/)
573
+
574
+ node = DOTDigraph.new({"name" => "test_name", "label" => "Right justified label\\r"})
575
+ dot = node.to_s
576
+ assert_match(dot, /label\s*=\s*"Right justified label\\r"/)
577
+
578
+ node = DOTDigraph.new({"name" => "test_name", "label" => "123.456"})
579
+ dot = node.to_s
580
+ assert_match(dot, /label\s*=\s*123.456/)
581
+
582
+ node = DOTDigraph.new({"name" => "test_name", "label" => ".456"})
583
+ dot = node.to_s
584
+ assert_match(dot, /label\s*=\s*.456/)
585
+
586
+ node = DOTDigraph.new({"name" => "test_name", "label" => "-.456"})
587
+ dot = node.to_s
588
+ assert_match(dot, /label\s*=\s*-.456/)
589
+
590
+ node = DOTDigraph.new({"name" => "test_name", "label" => "-456"})
591
+ dot = node.to_s
592
+ assert_match(dot, /label\s*=\s*-456/)
593
+
594
+ node = DOTDigraph.new({"name" => "test_name", "label" => "-123.456"})
595
+ dot = node.to_s
596
+ assert_match(dot, /label\s*=\s*-123.456/)
597
+
598
+ node = DOTDigraph.new({"name" => "test_name", "label" => "<html><head><title>test</title></head>\n<body>text</body></html>"})
599
+ dot = node.to_s
600
+ assert_match(dot, /label\s*=\s*<html><head><title>test<\/title><\/head>\n<body>text<\/body><\/html>/)
601
+ end
602
+
603
+ def test_option_quoting
604
+ node = DOTDigraph.new({"name" => "test_name", "comment" => "Comment with spaces"})
605
+ dot = node.to_s
606
+ assert_match(dot, /comment\s*=\s*"Comment with spaces"/)
607
+
608
+ node = DOTDigraph.new({"name" => "test_name", "comment" => "Comment with \"quotes\""})
609
+ dot = node.to_s
610
+ assert_match(dot, /comment\s*=\s*"Comment with \\"quotes\\""/)
611
+
612
+ node = DOTDigraph.new({"name" => "test_name", "comment" => "Comment with \\backslashes\\"})
613
+ dot = node.to_s
614
+ assert_match(dot, /comment\s*=\s*"Comment with \\\\backslashes\\\\"/)
615
+
616
+ node = DOTDigraph.new({"name" => "test_name", "comment" => "123.456"})
617
+ dot = node.to_s
618
+ assert_match(dot, /comment\s*=\s*123.456/)
619
+
620
+ node = DOTDigraph.new({"name" => "test_name", "comment" => ".456"})
621
+ dot = node.to_s
622
+ assert_match(dot, /comment\s*=\s*.456/)
623
+
624
+ node = DOTDigraph.new({"name" => "test_name", "comment" => "-.456"})
625
+ dot = node.to_s
626
+ assert_match(dot, /comment\s*=\s*-.456/)
627
+
628
+ node = DOTDigraph.new({"name" => "test_name", "comment" => "-456"})
629
+ dot = node.to_s
630
+ assert_match(dot, /comment\s*=\s*-456/)
631
+
632
+ node = DOTDigraph.new({"name" => "test_name", "comment" => "-123.456"})
633
+ dot = node.to_s
634
+ assert_match(dot, /comment\s*=\s*-123.456/)
635
+
636
+ node = DOTDigraph.new({"name" => "test_name", "comment" => "<html><head><title>test</title></head>\n<body>text</body></html>"})
637
+ dot = node.to_s
638
+ assert_match(dot, /comment\s*=\s*<html><head><title>test<\/title><\/head>\n<body>text<\/body><\/html>/)
639
+ end
640
+
641
+ def test_element_containment
642
+ node1 = DOTNode.new('name' => 'test_node1')
643
+ node2 = DOTNode.new('name' => 'test_node2')
644
+
645
+ graph = DOTDigraph.new('name' => 'test_graph')
646
+ assert_nil(graph.pop)
647
+ assert_equal(graph, graph.push(node1))
648
+ assert_equal(graph, graph << node2)
649
+ graph.each_element do |element|
650
+ assert([node1, node2].include?(element))
651
+ end
652
+ assert_equal(node2, graph.pop)
653
+ assert_equal(node1, graph.pop)
654
+ assert_nil(graph.pop)
655
+
656
+ graph = DOTDigraph.new('name' => 'test_graph', 'elements' => [node1])
657
+ assert_equal(node1, graph.pop)
658
+ assert_nil(graph.pop)
659
+ end
660
+ end
661
+
662
+ # Tests for DOTSubgraph
663
+ class TestDotSubgraph < Test::Unit::TestCase
664
+ def test_subgraph_statement
665
+ subgraph = DOTSubgraph.new()
666
+ dot = subgraph.to_s
667
+ assert_match(dot, /^\s*subgraph /)
668
+ end
669
+
670
+ def test_name_quoting
671
+ node = DOTSubgraph.new({"name" => "Name with spaces"})
672
+ dot = node.to_s
673
+ assert_match(dot, /^subgraph "Name with spaces" \{$/)
674
+
675
+ node = DOTSubgraph.new({"name" => "Name with \"quotes\""})
676
+ dot = node.to_s
677
+ assert_match(dot, /^subgraph "Name with \\"quotes\\"" \{$/)
678
+
679
+ node = DOTSubgraph.new({"name" => "Name with \\backslashes\\"})
680
+ dot = node.to_s
681
+ assert_match(dot, /^subgraph "Name with \\\\backslashes\\\\" \{$/)
682
+
683
+ node = DOTSubgraph.new({"name" => "123.456"})
684
+ dot = node.to_s
685
+ assert_match(dot, /^subgraph 123.456 \{$/)
686
+
687
+ node = DOTSubgraph.new({"name" => ".456"})
688
+ dot = node.to_s
689
+ assert_match(dot, /^subgraph .456 \{$/)
690
+
691
+ node = DOTSubgraph.new({"name" => "-.456"})
692
+ dot = node.to_s
693
+ assert_match(dot, /^subgraph -.456 \{$/)
694
+
695
+ node = DOTSubgraph.new({"name" => "-456"})
696
+ dot = node.to_s
697
+ assert_match(dot, /^subgraph -456 \{$/)
698
+
699
+ node = DOTSubgraph.new({"name" => "-123.456"})
700
+ dot = node.to_s
701
+ assert_match(dot, /^subgraph -123.456 \{$/)
702
+
703
+ node = DOTSubgraph.new({"name" => "<html><head><title>test</title></head>\n<body>text</body></html>"})
704
+ dot = node.to_s
705
+ assert_match(dot, /^subgraph <html><head><title>test<\/title><\/head>\n<body>text<\/body><\/html> \{$/)
706
+ end
707
+
708
+ def test_label_quoting
709
+ node = DOTSubgraph.new({"name" => "test_name", "label" => "Label with spaces"})
710
+ dot = node.to_s
711
+ assert_match(dot, /label\s*=\s*"Label with spaces"/)
712
+
713
+ node = DOTSubgraph.new({"name" => "test_name", "label" => "Label with \"quotes\""})
714
+ dot = node.to_s
715
+ assert_match(dot, /label\s*=\s*"Label with \\"quotes\\""/)
716
+
717
+ node = DOTSubgraph.new({"name" => "test_name", "label" => "Label with \\backslashes\\"})
718
+ dot = node.to_s
719
+ assert_match(dot, /label\s*=\s*"Label with \\\\backslashes\\\\"/)
720
+
721
+ node = DOTSubgraph.new({"name" => "test_name", "label" => "Label with\nembedded newline"})
722
+ dot = node.to_s
723
+ assert_match(dot, /label\s*=\s*"Label with\\nembedded newline"/)
724
+
725
+ node = DOTSubgraph.new({"name" => "test_name", "label" => "Left justified label\\l"})
726
+ dot = node.to_s
727
+ assert_match(dot, /label\s*=\s*"Left justified label\\l"/)
728
+
729
+ node = DOTSubgraph.new({"name" => "test_name", "label" => "Right justified label\\r"})
730
+ dot = node.to_s
731
+ assert_match(dot, /label\s*=\s*"Right justified label\\r"/)
732
+
733
+ node = DOTSubgraph.new({"name" => "test_name", "label" => "123.456"})
734
+ dot = node.to_s
735
+ assert_match(dot, /label\s*=\s*123.456/)
736
+
737
+ node = DOTSubgraph.new({"name" => "test_name", "label" => ".456"})
738
+ dot = node.to_s
739
+ assert_match(dot, /label\s*=\s*.456/)
740
+
741
+ node = DOTSubgraph.new({"name" => "test_name", "label" => "-.456"})
742
+ dot = node.to_s
743
+ assert_match(dot, /label\s*=\s*-.456/)
744
+
745
+ node = DOTSubgraph.new({"name" => "test_name", "label" => "-456"})
746
+ dot = node.to_s
747
+ assert_match(dot, /label\s*=\s*-456/)
748
+
749
+ node = DOTSubgraph.new({"name" => "test_name", "label" => "-123.456"})
750
+ dot = node.to_s
751
+ assert_match(dot, /label\s*=\s*-123.456/)
752
+
753
+ node = DOTSubgraph.new({"name" => "test_name", "label" => "<html><head><title>test</title></head>\n<body>text</body></html>"})
754
+ dot = node.to_s
755
+ assert_match(dot, /label\s*=\s*<html><head><title>test<\/title><\/head>\n<body>text<\/body><\/html>/)
756
+ end
757
+
758
+ def test_option_quoting
759
+ node = DOTSubgraph.new({"name" => "test_name", "comment" => "Comment with spaces"})
760
+ dot = node.to_s
761
+ assert_match(dot, /comment\s*=\s*"Comment with spaces"/)
762
+
763
+ node = DOTSubgraph.new({"name" => "test_name", "comment" => "Comment with \"quotes\""})
764
+ dot = node.to_s
765
+ assert_match(dot, /comment\s*=\s*"Comment with \\"quotes\\""/)
766
+
767
+ node = DOTSubgraph.new({"name" => "test_name", "comment" => "Comment with \\backslashes\\"})
768
+ dot = node.to_s
769
+ assert_match(dot, /comment\s*=\s*"Comment with \\\\backslashes\\\\"/)
770
+
771
+ node = DOTSubgraph.new({"name" => "test_name", "comment" => "123.456"})
772
+ dot = node.to_s
773
+ assert_match(dot, /comment\s*=\s*123.456/)
774
+
775
+ node = DOTSubgraph.new({"name" => "test_name", "comment" => ".456"})
776
+ dot = node.to_s
777
+ assert_match(dot, /comment\s*=\s*.456/)
778
+
779
+ node = DOTSubgraph.new({"name" => "test_name", "comment" => "-.456"})
780
+ dot = node.to_s
781
+ assert_match(dot, /comment\s*=\s*-.456/)
782
+
783
+ node = DOTSubgraph.new({"name" => "test_name", "comment" => "-456"})
784
+ dot = node.to_s
785
+ assert_match(dot, /comment\s*=\s*-456/)
786
+
787
+ node = DOTSubgraph.new({"name" => "test_name", "comment" => "-123.456"})
788
+ dot = node.to_s
789
+ assert_match(dot, /comment\s*=\s*-123.456/)
790
+
791
+ node = DOTSubgraph.new({"name" => "test_name", "comment" => "<html><head><title>test</title></head>\n<body>text</body></html>"})
792
+ dot = node.to_s
793
+ assert_match(dot, /comment\s*=\s*<html><head><title>test<\/title><\/head>\n<body>text<\/body><\/html>/)
794
+ end
795
+
796
+ def test_element_containment
797
+ node1 = DOTNode.new('name' => 'test_node1')
798
+ node2 = DOTNode.new('name' => 'test_node2')
799
+
800
+ graph = DOTSubgraph.new('name' => 'test_graph')
801
+ assert_nil(graph.pop)
802
+ assert_equal(graph, graph.push(node1))
803
+ assert_equal(graph, graph << node2)
804
+ graph.each_element do |element|
805
+ assert([node1, node2].include?(element))
806
+ end
807
+ assert_equal(node2, graph.pop)
808
+ assert_equal(node1, graph.pop)
809
+ assert_nil(graph.pop)
810
+
811
+ graph = DOTSubgraph.new('name' => 'test_graph', 'elements' => [node1])
812
+ assert_equal(node1, graph.pop)
813
+ assert_nil(graph.pop)
814
+ end
815
+ end