rgl 0.3.1 → 0.4.0
Sign up to get free protection for your applications and to get access to all the features.
- data/ChangeLog +75 -1
- data/Rakefile +18 -3
- data/install.rb +0 -0
- data/lib/rgl/adjacency.rb +2 -1
- data/lib/rgl/base.rb +1 -1
- data/lib/rgl/condensation.rb +47 -0
- data/lib/rgl/dot.rb +13 -13
- data/lib/rgl/rdot.rb +28 -28
- data/lib/rgl/transitiv_closure.rb +2 -46
- data/lib/rgl/transitivity.rb +179 -0
- data/tests/TestGraph.rb +36 -20
- data/tests/TestRdot.rb +237 -189
- data/tests/TestTransitivity.rb +129 -0
- metadata +138 -127
- data/tests/TestTransitiveClosure.rb +0 -26
@@ -0,0 +1,179 @@
|
|
1
|
+
require 'enumerator'
|
2
|
+
|
3
|
+
require 'rgl/adjacency'
|
4
|
+
require 'rgl/base'
|
5
|
+
require 'rgl/condensation'
|
6
|
+
|
7
|
+
module RGL
|
8
|
+
module Graph
|
9
|
+
# Returns an RGL::DirectedAdjacencyGraph which is the transitive closure of
|
10
|
+
# this graph. Meaning, for each path u -> ... -> v in this graph, the path
|
11
|
+
# is copied and the edge u -> v is added. This method supports working with
|
12
|
+
# cyclic graphs by ensuring that edges are created between every pair of
|
13
|
+
# vertices in the cycle, including self-referencing edges.
|
14
|
+
#
|
15
|
+
# This method should run in O(|V||E|) time, where |V| and |E| are the number
|
16
|
+
# of vertices and edges respectively.
|
17
|
+
#
|
18
|
+
# Raises RGL::NotDirectedError if run on an undirected graph.
|
19
|
+
def transitive_closure
|
20
|
+
raise NotDirectedError,
|
21
|
+
"transitive_closure only supported for directed graphs" unless directed?
|
22
|
+
|
23
|
+
# Compute a condensation graph in order to hide cycles.
|
24
|
+
cg = condensation_graph
|
25
|
+
|
26
|
+
# Use a depth first search to calculate the transitive closure over the
|
27
|
+
# condensation graph. This ensures that as we traverse up the graph we
|
28
|
+
# know the transitive closure of each subgraph rooted at each node
|
29
|
+
# starting at the leaves. Subsequent root nodes which consume these
|
30
|
+
# subgraphs by way of the nodes' immediate successors can then immediately
|
31
|
+
# add edges to the roots of the subgraphs and to every successor of those
|
32
|
+
# roots.
|
33
|
+
tc_cg = DirectedAdjacencyGraph.new
|
34
|
+
cg.depth_first_search do |v|
|
35
|
+
# For each vertex v, w, and x where the edges v -> w and w -> x exist in
|
36
|
+
# the source graph, add edges v -> w and v -> x to the target graph.
|
37
|
+
cg.each_adjacent(v) do |w|
|
38
|
+
tc_cg.add_edge(v, w)
|
39
|
+
tc_cg.each_adjacent(w) do |x|
|
40
|
+
tc_cg.add_edge(v, x)
|
41
|
+
end
|
42
|
+
end
|
43
|
+
# Ensure that a vertex with no in or out edges is added to the graph.
|
44
|
+
tc_cg.add_vertex(v)
|
45
|
+
end
|
46
|
+
|
47
|
+
# Expand the condensed transitive closure.
|
48
|
+
#
|
49
|
+
# For each trivial strongly connected component in the condensed graph,
|
50
|
+
# add the single node it contains to the new graph and add edges for each
|
51
|
+
# edge the node begins in the original graph.
|
52
|
+
# For each NON-trivial strongly connected component in the condensed
|
53
|
+
# graph, add each node it contains to the new graph and add edges to
|
54
|
+
# every node in the strongly connected component, including self
|
55
|
+
# referential edges. Then for each edge of the original graph from any
|
56
|
+
# of the contained nodes, add edges from each of the contained nodes to
|
57
|
+
# all the edge targets.
|
58
|
+
g = DirectedAdjacencyGraph.new
|
59
|
+
tc_cg.each_vertex do |scc|
|
60
|
+
scc.each do |v|
|
61
|
+
# Add edges between all members of non-trivial strongly connected
|
62
|
+
# components (size > 1) and ensure that self referential edges are
|
63
|
+
# added when necessary for trivial strongly connected components.
|
64
|
+
if scc.size > 1 || has_edge?(v, v) then
|
65
|
+
scc.each do |w|
|
66
|
+
g.add_edge(v, w)
|
67
|
+
end
|
68
|
+
end
|
69
|
+
# Ensure that a vertex with no in or out edges is added to the graph.
|
70
|
+
g.add_vertex(v)
|
71
|
+
end
|
72
|
+
# Add an edge from every member of a strongly connected component to
|
73
|
+
# every member of each strongly connected component to which the former
|
74
|
+
# points.
|
75
|
+
tc_cg.each_adjacent(scc) do |scc2|
|
76
|
+
scc.each do |v|
|
77
|
+
scc2.each do |w|
|
78
|
+
g.add_edge(v, w)
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
# Finally, the transitive closure...
|
85
|
+
g
|
86
|
+
end
|
87
|
+
|
88
|
+
# Returns an RGL::DirectedAdjacencyGraph which is the transitive reduction
|
89
|
+
# of this graph. Meaning, that each edge u -> v is omitted if path
|
90
|
+
# u -> ... -> v exists. This method supports working with cyclic graphs;
|
91
|
+
# however, cycles are arbitrarily simplified which may lead to variant,
|
92
|
+
# although equally valid, results on equivalent graphs.
|
93
|
+
#
|
94
|
+
# This method should run in O(|V||E|) time, where |V| and |E| are the number
|
95
|
+
# of vertices and edges respectively.
|
96
|
+
#
|
97
|
+
# Raises RGL::NotDirectedError if run on an undirected graph.
|
98
|
+
def transitive_reduction
|
99
|
+
raise NotDirectedError,
|
100
|
+
"transitive_reduction only supported for directed graphs" unless directed?
|
101
|
+
|
102
|
+
# Compute a condensation graph in order to hide cycles.
|
103
|
+
cg = condensation_graph
|
104
|
+
|
105
|
+
# Use a depth first search to compute the transitive reduction over the
|
106
|
+
# condensed graph. This is similar to the computation of the transitive
|
107
|
+
# closure over the graph in that for any node of the graph all nodes
|
108
|
+
# reachable from the node are tracked. Using a depth first search ensures
|
109
|
+
# that all nodes reachable from a target node are known when considering
|
110
|
+
# whether or not to add an edge pointing to that target.
|
111
|
+
tr_cg = DirectedAdjacencyGraph.new
|
112
|
+
paths_from = {}
|
113
|
+
cg.depth_first_search do |v|
|
114
|
+
paths_from[v] = Set.new
|
115
|
+
cg.each_adjacent(v) do |w|
|
116
|
+
# Only add the edge v -> w if there is no other edge v -> x such that
|
117
|
+
# w is reachable from x. Make sure to completely skip the case where
|
118
|
+
# x == w.
|
119
|
+
unless Enumerable::Enumerator.new(cg, :each_adjacent, v).any? do |x|
|
120
|
+
x != w && paths_from[x].include?(w)
|
121
|
+
end then
|
122
|
+
tr_cg.add_edge(v, w)
|
123
|
+
|
124
|
+
# For each vertex v, track all nodes reachable from v by adding node
|
125
|
+
# w to the list as well as all the nodes readable from w.
|
126
|
+
paths_from[v] << w
|
127
|
+
paths_from[v].merge(paths_from[w])
|
128
|
+
end
|
129
|
+
end
|
130
|
+
# Ensure that a vertex with no in or out edges is added to the graph.
|
131
|
+
tr_cg.add_vertex(v)
|
132
|
+
end
|
133
|
+
|
134
|
+
# Expand the condensed transitive reduction.
|
135
|
+
#
|
136
|
+
# For each trivial strongly connected component in the condensed graph,
|
137
|
+
# add the single node it contains to the new graph and add edges for each
|
138
|
+
# edge the node begins in the original graph.
|
139
|
+
# For each NON-trivial strongly connected component in the condensed
|
140
|
+
# graph, add each node it contains to the new graph and add arbitrary
|
141
|
+
# edges between the nodes to form a simple cycle. Then for each strongly
|
142
|
+
# connected component adjacent to the current one, find and add the first
|
143
|
+
# edge which exists in the original graph, starts in the first strongly
|
144
|
+
# connected component, and ends in the second strongly connected
|
145
|
+
# component.
|
146
|
+
g = DirectedAdjacencyGraph.new
|
147
|
+
tr_cg.each_vertex do |scc|
|
148
|
+
# Make a cycle of the contents of non-trivial strongly connected
|
149
|
+
# components.
|
150
|
+
scc_arr = scc.to_a
|
151
|
+
if scc.size > 1 || has_edge?(scc_arr.first, scc_arr.first) then
|
152
|
+
0.upto(scc_arr.size - 2) do |idx|
|
153
|
+
g.add_edge(scc_arr[idx], scc_arr[idx + 1])
|
154
|
+
end
|
155
|
+
g.add_edge(scc_arr.last, scc_arr.first)
|
156
|
+
end
|
157
|
+
|
158
|
+
# Choose a single edge between the members of two different strongly
|
159
|
+
# connected component to add to the graph.
|
160
|
+
edges = Enumerable::Enumerator.new(self, :each_edge)
|
161
|
+
tr_cg.each_adjacent(scc) do |scc2|
|
162
|
+
g.add_edge(
|
163
|
+
*edges.find do |v, w|
|
164
|
+
scc.member?(v) && scc2.member?(w)
|
165
|
+
end
|
166
|
+
)
|
167
|
+
end
|
168
|
+
|
169
|
+
# Ensure that a vertex with no in or out edges is added to the graph.
|
170
|
+
scc.each do |v|
|
171
|
+
g.add_vertex(v)
|
172
|
+
end
|
173
|
+
end
|
174
|
+
|
175
|
+
# Finally, the transitive reduction...
|
176
|
+
g
|
177
|
+
end
|
178
|
+
end # module Graph
|
179
|
+
end # module RGL
|
data/tests/TestGraph.rb
CHANGED
@@ -11,40 +11,56 @@ class TestGraph < Test::Unit::TestCase
|
|
11
11
|
end
|
12
12
|
|
13
13
|
def setup
|
14
|
-
@
|
14
|
+
@dg1 = DirectedAdjacencyGraph.new
|
15
15
|
@edges = [[1,2],[2,3],[2,4],[4,5],[1,6],[6,4]]
|
16
|
-
@edges.each do |(src,target)|
|
17
|
-
@
|
16
|
+
@edges.each do |(src,target)|
|
17
|
+
@dg1.add_edge(src, target)
|
18
|
+
end
|
19
|
+
@loan_vertices = [7, 8, 9]
|
20
|
+
@loan_vertices.each do |vertex|
|
21
|
+
@dg1.add_vertex(vertex)
|
22
|
+
end
|
23
|
+
|
24
|
+
@dg2 = DirectedAdjacencyGraph[*@edges.flatten]
|
25
|
+
@loan_vertices.each do |vertex|
|
26
|
+
@dg2.add_vertex(vertex)
|
18
27
|
end
|
19
28
|
|
20
29
|
@ug = AdjacencyGraph.new(Array)
|
21
30
|
@ug.add_edges(*@edges)
|
31
|
+
@ug.add_vertices(*@loan_vertices)
|
22
32
|
end
|
23
33
|
|
24
34
|
def test_equality
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
@
|
34
|
-
|
35
|
+
assert_equal @dg1, @dg1
|
36
|
+
assert_equal @dg1, @dg1.dup
|
37
|
+
assert_equal @ug, @ug.dup
|
38
|
+
assert_not_equal @ug, @dg1
|
39
|
+
assert_not_equal @dg1, @ug
|
40
|
+
assert_not_equal @dg1, 42
|
41
|
+
assert_equal @dg1, @dg2
|
42
|
+
@dg1.add_vertex 42
|
43
|
+
assert_not_equal @dg1, @dg2
|
44
|
+
end
|
45
|
+
|
46
|
+
def test_to_adjacency
|
47
|
+
assert_equal @dg1, @dg1.to_adjacency
|
48
|
+
assert_equal @ug, @ug.to_adjacency
|
35
49
|
end
|
36
50
|
|
37
51
|
def test_merge
|
38
|
-
merge = DirectedAdjacencyGraph.new(Array, @
|
39
|
-
|
40
|
-
merge
|
41
|
-
|
52
|
+
merge = DirectedAdjacencyGraph.new(Array, @dg1, @ug)
|
53
|
+
assert_equal merge.num_edges, 12
|
54
|
+
assert_equal merge.num_vertices, 9
|
55
|
+
merge = DirectedAdjacencyGraph.new(Set, @dg1, @dg1)
|
56
|
+
assert_equal merge.num_edges, 6
|
57
|
+
assert_equal merge.num_vertices, 9
|
42
58
|
end
|
43
59
|
|
44
60
|
def test_set_edgelist_class
|
45
|
-
edges = @
|
46
|
-
@
|
47
|
-
assert_equal edges, @
|
61
|
+
edges = @dg1.edges
|
62
|
+
@dg1.edgelist_class=Array
|
63
|
+
assert_equal edges, @dg1.edges
|
48
64
|
end
|
49
65
|
|
50
66
|
def test_not_implemented
|
data/tests/TestRdot.rb
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
require 'test/unit'
|
2
2
|
require 'rgl/rdot'
|
3
3
|
|
4
|
-
include
|
4
|
+
include RGL
|
5
5
|
|
6
6
|
# Add some helper methods to TestCase
|
7
7
|
class Test::Unit::TestCase
|
@@ -18,476 +18,500 @@ class Test::Unit::TestCase
|
|
18
18
|
|
19
19
|
end
|
20
20
|
|
21
|
-
# Tests for
|
21
|
+
# Tests for DOT::Port
|
22
22
|
class TestDotPort < Test::Unit::TestCase
|
23
23
|
def test_name
|
24
|
-
port =
|
24
|
+
port = DOT::Port.new()
|
25
25
|
assert_equal('', port.to_s)
|
26
26
|
|
27
|
-
port =
|
27
|
+
port = DOT::Port.new('test_name')
|
28
28
|
assert_equal('<test_name>', port.to_s)
|
29
29
|
end
|
30
30
|
|
31
31
|
def test_label
|
32
|
-
port =
|
32
|
+
port = DOT::Port.new(nil, 'test_label')
|
33
33
|
assert_equal('test_label', port.to_s)
|
34
34
|
end
|
35
35
|
|
36
36
|
def test_name_and_label
|
37
|
-
port =
|
37
|
+
port = DOT::Port.new('test_name', 'test_label')
|
38
38
|
assert_equal('<test_name> test_label', port.to_s)
|
39
39
|
end
|
40
40
|
|
41
41
|
def test_nested_ports
|
42
|
-
port =
|
42
|
+
port = DOT::Port.new([DOT::Port.new(nil, 'a'), DOT::Port.new(nil, 'b')])
|
43
43
|
assert_equal('{a | b}', port.to_s)
|
44
44
|
end
|
45
45
|
|
46
46
|
def test_name_label_and_nested_ports
|
47
|
-
port =
|
48
|
-
port.ports = [
|
47
|
+
port = DOT::Port.new('test_name', 'test_label')
|
48
|
+
port.ports = [DOT::Port.new(nil, 'a'), DOT::Port.new(nil, 'b')]
|
49
49
|
assert_equal('{a | b}', port.to_s)
|
50
50
|
end
|
51
51
|
end
|
52
52
|
|
53
|
-
# Tests for
|
53
|
+
# Tests for DOT::Node
|
54
54
|
class TestDotNode < Test::Unit::TestCase
|
55
55
|
|
56
56
|
def test_no_name
|
57
|
-
node =
|
57
|
+
node = DOT::Node.new()
|
58
58
|
dot = node.to_s
|
59
59
|
assert_nil(dot)
|
60
60
|
end
|
61
61
|
|
62
62
|
# bug 16125
|
63
63
|
def test_1prop_0comma
|
64
|
-
node =
|
64
|
+
node = DOT::Node.new({"label"=>"the_label"})
|
65
65
|
dot = node.to_s
|
66
66
|
assert_no_match(dot, /,/)
|
67
67
|
end
|
68
68
|
|
69
69
|
def test_2prop_1comma
|
70
|
-
node =
|
70
|
+
node = DOT::Node.new({"label"=>"the_label", "shape"=>"ellipse"})
|
71
71
|
dot = node.to_s
|
72
72
|
assert_match(dot, /\[[^,]*,[^,]*\]/)
|
73
73
|
end
|
74
74
|
|
75
75
|
def test_name_without_label
|
76
|
-
node =
|
76
|
+
node = DOT::Node.new({"name"=>"test_name"})
|
77
77
|
dot = node.to_s
|
78
78
|
assert_no_match(dot, /label/)
|
79
79
|
end
|
80
80
|
|
81
81
|
def test_no_label
|
82
|
-
node =
|
82
|
+
node = DOT::Node.new({"shape"=>"ellipse"})
|
83
83
|
dot = node.to_s
|
84
84
|
assert_no_match(dot, /label/)
|
85
85
|
end
|
86
86
|
|
87
87
|
def test_Mrecord_no_label_no_ports
|
88
|
-
node =
|
88
|
+
node = DOT::Node.new({"name" => "test_name", "shape"=>"Mrecord"})
|
89
89
|
dot = node.to_s
|
90
90
|
assert_match(dot, /shape\s*=\s*Mrecord/)
|
91
91
|
assert_no_match(dot, /label/)
|
92
92
|
end
|
93
93
|
|
94
94
|
def test_Mrecord_label_no_ports
|
95
|
-
node =
|
95
|
+
node = DOT::Node.new({"name" => "test_name", "label" => "test_label", "shape"=>"Mrecord"})
|
96
96
|
dot = node.to_s
|
97
97
|
assert_match(dot, /shape\s*=\s*Mrecord/)
|
98
98
|
assert_match(dot, /label\s*=\s*test_label/)
|
99
99
|
end
|
100
100
|
|
101
101
|
def test_Mrecord_label_with_ports
|
102
|
-
node =
|
103
|
-
node.ports <<
|
104
|
-
node.ports <<
|
102
|
+
node = DOT::Node.new({"name" => "test_name", "label" => "test_label", "shape"=>"Mrecord"})
|
103
|
+
node.ports << DOT::Port.new(nil, "a")
|
104
|
+
node.ports << DOT::Port.new(nil, "b")
|
105
105
|
dot = node.to_s
|
106
106
|
assert_match(dot, /shape\s*=\s*Mrecord/)
|
107
107
|
assert_match(dot, /label\s*=\s*"a\s*|\s*b"/)
|
108
108
|
end
|
109
109
|
|
110
110
|
def test_Mrecord_no_label_with_ports
|
111
|
-
node =
|
112
|
-
node.ports <<
|
113
|
-
node.ports <<
|
111
|
+
node = DOT::Node.new({"name" => "test_name", "shape"=>"Mrecord"})
|
112
|
+
node.ports << DOT::Port.new(nil, "a")
|
113
|
+
node.ports << DOT::Port.new(nil, "b")
|
114
114
|
dot = node.to_s
|
115
115
|
assert_match(dot, /shape\s*=\s*Mrecord/)
|
116
116
|
assert_match(dot, /label\s*=\s*"a\s*|\s*b"/)
|
117
117
|
end
|
118
118
|
|
119
119
|
def test_record_no_label_no_ports
|
120
|
-
node =
|
120
|
+
node = DOT::Node.new({"name" => "test_name", "shape"=>"record"})
|
121
121
|
dot = node.to_s
|
122
122
|
assert_match(dot, /shape\s*=\s*record/)
|
123
123
|
assert_no_match(dot, /label/)
|
124
124
|
end
|
125
125
|
|
126
126
|
def test_record_label_no_ports
|
127
|
-
node =
|
127
|
+
node = DOT::Node.new({"name" => "test_name", "label" => "test_label", "shape"=>"record"})
|
128
128
|
dot = node.to_s
|
129
129
|
assert_match(dot, /shape\s*=\s*record/)
|
130
130
|
assert_match(dot, /label\s*=\s*test_label/)
|
131
131
|
end
|
132
132
|
|
133
133
|
def test_record_label_with_ports
|
134
|
-
node =
|
135
|
-
node.ports <<
|
136
|
-
node.ports <<
|
134
|
+
node = DOT::Node.new({"name" => "test_name", "label" => "test_label", "shape"=>"record"})
|
135
|
+
node.ports << DOT::Port.new(nil, "a")
|
136
|
+
node.ports << DOT::Port.new(nil, "b")
|
137
137
|
dot = node.to_s
|
138
138
|
assert_match(dot, /shape\s*=\s*record/)
|
139
139
|
assert_match(dot, /label\s*=\s*"a\s*|\s*b"/)
|
140
140
|
end
|
141
141
|
|
142
142
|
def test_record_no_label_with_ports
|
143
|
-
node =
|
144
|
-
node.ports <<
|
145
|
-
node.ports <<
|
143
|
+
node = DOT::Node.new({"name" => "test_name", "shape"=>"record"})
|
144
|
+
node.ports << DOT::Port.new(nil, "a")
|
145
|
+
node.ports << DOT::Port.new(nil, "b")
|
146
146
|
dot = node.to_s
|
147
147
|
assert_match(dot, /shape\s*=\s*record/)
|
148
148
|
assert_match(dot, /label\s*=\s*"a\s*|\s*b"/)
|
149
149
|
end
|
150
150
|
|
151
151
|
def test_no_shape_no_label_no_ports
|
152
|
-
node =
|
153
|
-
node.ports <<
|
154
|
-
node.ports <<
|
152
|
+
node = DOT::Node.new({"name" => "test_name"})
|
153
|
+
node.ports << DOT::Port.new(nil, "a")
|
154
|
+
node.ports << DOT::Port.new(nil, "b")
|
155
155
|
dot = node.to_s
|
156
156
|
assert_no_match(dot, /shape\s*=\s/)
|
157
157
|
assert_no_match(dot, /label\s*=\s*/)
|
158
158
|
end
|
159
159
|
|
160
160
|
def test_no_shape_no_label_with_ports
|
161
|
-
node =
|
162
|
-
node.ports <<
|
163
|
-
node.ports <<
|
161
|
+
node = DOT::Node.new({"name" => "test_name"})
|
162
|
+
node.ports << DOT::Port.new(nil, "a")
|
163
|
+
node.ports << DOT::Port.new(nil, "b")
|
164
164
|
dot = node.to_s
|
165
165
|
assert_no_match(dot, /shape\s*=\s*record/)
|
166
166
|
assert_no_match(dot, /label\s*=\s*/)
|
167
167
|
end
|
168
168
|
|
169
169
|
def test_name_quoting
|
170
|
-
node =
|
170
|
+
node = DOT::Node.new({"name" => "Name with spaces"})
|
171
171
|
dot = node.to_s
|
172
172
|
assert_match(dot, /^"Name with spaces"$/)
|
173
173
|
|
174
|
-
node =
|
174
|
+
node = DOT::Node.new({"name" => "Name with \"quotes\""})
|
175
175
|
dot = node.to_s
|
176
176
|
assert_match(dot, /^"Name with \\"quotes\\""$/)
|
177
177
|
|
178
|
-
node =
|
178
|
+
node = DOT::Node.new({"name" => "Name with \\backslashes\\"})
|
179
179
|
dot = node.to_s
|
180
180
|
assert_match(dot, /^"Name with \\\\backslashes\\\\"$/)
|
181
181
|
|
182
|
-
node =
|
182
|
+
node = DOT::Node.new({"name" => "Name with\nembedded\nnewlines"})
|
183
|
+
dot = node.to_s
|
184
|
+
assert_match(dot, /\A.*"Name with\nembedded\nnewlines".*\Z/m)
|
185
|
+
|
186
|
+
node = DOT::Node.new({"name" => "Name_with_trailing_newline\n"})
|
187
|
+
dot = node.to_s
|
188
|
+
assert_match(dot, /\A.*"Name_with_trailing_newline\n".*\Z/m)
|
189
|
+
|
190
|
+
node = DOT::Node.new({"name" => "123.456"})
|
183
191
|
dot = node.to_s
|
184
192
|
assert_match(dot, /^123.456$/)
|
185
193
|
|
186
|
-
node =
|
194
|
+
node = DOT::Node.new({"name" => ".456"})
|
187
195
|
dot = node.to_s
|
188
196
|
assert_match(dot, /^.456$/)
|
189
197
|
|
190
|
-
node =
|
198
|
+
node = DOT::Node.new({"name" => "-.456"})
|
191
199
|
dot = node.to_s
|
192
200
|
assert_match(dot, /^-.456$/)
|
193
201
|
|
194
|
-
node =
|
202
|
+
node = DOT::Node.new({"name" => "-456"})
|
195
203
|
dot = node.to_s
|
196
204
|
assert_match(dot, /^-456$/)
|
197
205
|
|
198
|
-
node =
|
206
|
+
node = DOT::Node.new({"name" => "-123.456"})
|
199
207
|
dot = node.to_s
|
200
208
|
assert_match(dot, /^-123.456$/)
|
201
209
|
|
202
|
-
node =
|
210
|
+
node = DOT::Node.new({"name" => "<html><head><title>test</title></head>\n<body>text</body></html>"})
|
203
211
|
dot = node.to_s
|
204
212
|
assert_match(dot, /^<html><head><title>test<\/title><\/head>\n<body>text<\/body><\/html>$/)
|
205
213
|
end
|
206
214
|
|
207
215
|
def test_label_quoting
|
208
|
-
node =
|
216
|
+
node = DOT::Node.new({"name" => "test_name", "label" => "Label with spaces"})
|
209
217
|
dot = node.to_s
|
210
218
|
assert_match(dot, /label\s*=\s*"Label with spaces"/)
|
211
219
|
|
212
|
-
node =
|
220
|
+
node = DOT::Node.new({"name" => "test_name", "label" => "Label with \"quotes\""})
|
213
221
|
dot = node.to_s
|
214
222
|
assert_match(dot, /label\s*=\s*"Label with \\"quotes\\""/)
|
215
223
|
|
216
|
-
node =
|
224
|
+
node = DOT::Node.new({"name" => "test_name", "label" => "Label with \\backslashes\\"})
|
217
225
|
dot = node.to_s
|
218
226
|
assert_match(dot, /label\s*=\s*"Label with \\\\backslashes\\\\"/)
|
219
227
|
|
220
|
-
node =
|
228
|
+
node = DOT::Node.new({"name" => "test_name", "label" => "Label with\nembedded\nnewlines"})
|
229
|
+
dot = node.to_s
|
230
|
+
assert_match(dot, /label\s*=\s*"Label with\\nembedded\\nnewlines"/)
|
231
|
+
|
232
|
+
node = DOT::Node.new({"name" => "test_name", "label" => "Label_with_a_trailing_newline\n"})
|
221
233
|
dot = node.to_s
|
222
|
-
assert_match(dot, /label\s*=\s*"
|
234
|
+
assert_match(dot, /label\s*=\s*"Label_with_a_trailing_newline\\n"/)
|
223
235
|
|
224
|
-
node =
|
236
|
+
node = DOT::Node.new({"name" => "test_name", "label" => "Left justified label\\l"})
|
225
237
|
dot = node.to_s
|
226
238
|
assert_match(dot, /label\s*=\s*"Left justified label\\l"/)
|
227
239
|
|
228
|
-
node =
|
240
|
+
node = DOT::Node.new({"name" => "test_name", "label" => "Right justified label\\r"})
|
229
241
|
dot = node.to_s
|
230
242
|
assert_match(dot, /label\s*=\s*"Right justified label\\r"/)
|
231
243
|
|
232
|
-
node =
|
244
|
+
node = DOT::Node.new({"name" => "test_name", "label" => "123.456"})
|
233
245
|
dot = node.to_s
|
234
246
|
assert_match(dot, /label\s*=\s*123.456/)
|
235
247
|
|
236
|
-
node =
|
248
|
+
node = DOT::Node.new({"name" => "test_name", "label" => ".456"})
|
237
249
|
dot = node.to_s
|
238
250
|
assert_match(dot, /label\s*=\s*.456/)
|
239
251
|
|
240
|
-
node =
|
252
|
+
node = DOT::Node.new({"name" => "test_name", "label" => "-.456"})
|
241
253
|
dot = node.to_s
|
242
254
|
assert_match(dot, /label\s*=\s*-.456/)
|
243
255
|
|
244
|
-
node =
|
256
|
+
node = DOT::Node.new({"name" => "test_name", "label" => "-456"})
|
245
257
|
dot = node.to_s
|
246
258
|
assert_match(dot, /label\s*=\s*-456/)
|
247
259
|
|
248
|
-
node =
|
260
|
+
node = DOT::Node.new({"name" => "test_name", "label" => "-123.456"})
|
249
261
|
dot = node.to_s
|
250
262
|
assert_match(dot, /label\s*=\s*-123.456/)
|
251
263
|
|
252
|
-
node =
|
264
|
+
node = DOT::Node.new({"name" => "test_name", "label" => "<html><head><title>test</title></head>\n<body>text</body></html>"})
|
253
265
|
dot = node.to_s
|
254
266
|
assert_match(dot, /label\s*=\s*<html><head><title>test<\/title><\/head>\n<body>text<\/body><\/html>/)
|
255
267
|
end
|
256
268
|
|
257
269
|
def test_option_quoting
|
258
|
-
node =
|
270
|
+
node = DOT::Node.new({"name" => "test_name", "comment" => "Comment with spaces"})
|
259
271
|
dot = node.to_s
|
260
272
|
assert_match(dot, /comment\s*=\s*"Comment with spaces"/)
|
261
273
|
|
262
|
-
node =
|
274
|
+
node = DOT::Node.new({"name" => "test_name", "comment" => "Comment with \"quotes\""})
|
263
275
|
dot = node.to_s
|
264
276
|
assert_match(dot, /comment\s*=\s*"Comment with \\"quotes\\""/)
|
265
277
|
|
266
|
-
node =
|
278
|
+
node = DOT::Node.new({"name" => "test_name", "comment" => "Comment with \\backslashes\\"})
|
267
279
|
dot = node.to_s
|
268
280
|
assert_match(dot, /comment\s*=\s*"Comment with \\\\backslashes\\\\"/)
|
269
281
|
|
270
|
-
node =
|
282
|
+
node = DOT::Node.new({"name" => "test_name", "comment" => "123.456"})
|
271
283
|
dot = node.to_s
|
272
284
|
assert_match(dot, /comment\s*=\s*123.456/)
|
273
285
|
|
274
|
-
node =
|
286
|
+
node = DOT::Node.new({"name" => "test_name", "comment" => ".456"})
|
275
287
|
dot = node.to_s
|
276
288
|
assert_match(dot, /comment\s*=\s*.456/)
|
277
289
|
|
278
|
-
node =
|
290
|
+
node = DOT::Node.new({"name" => "test_name", "comment" => "-.456"})
|
279
291
|
dot = node.to_s
|
280
292
|
assert_match(dot, /comment\s*=\s*-.456/)
|
281
293
|
|
282
|
-
node =
|
294
|
+
node = DOT::Node.new({"name" => "test_name", "comment" => "-456"})
|
283
295
|
dot = node.to_s
|
284
296
|
assert_match(dot, /comment\s*=\s*-456/)
|
285
297
|
|
286
|
-
node =
|
298
|
+
node = DOT::Node.new({"name" => "test_name", "comment" => "-123.456"})
|
287
299
|
dot = node.to_s
|
288
300
|
assert_match(dot, /comment\s*=\s*-123.456/)
|
289
301
|
|
290
|
-
node =
|
302
|
+
node = DOT::Node.new({"name" => "test_name", "comment" => "<html><head><title>test</title></head>\n<body>text</body></html>"})
|
291
303
|
dot = node.to_s
|
292
304
|
assert_match(dot, /comment\s*=\s*<html><head><title>test<\/title><\/head>\n<body>text<\/body><\/html>/)
|
293
305
|
end
|
294
306
|
end
|
295
307
|
|
296
|
-
# Tests for
|
308
|
+
# Tests for DOT::Edge
|
297
309
|
class TestDotEdge < Test::Unit::TestCase
|
298
310
|
|
299
311
|
def test_0prop
|
300
|
-
edge =
|
312
|
+
edge = DOT::Edge.new({'from' => 'a', 'to' => 'b'})
|
301
313
|
dot = edge.to_s
|
302
314
|
assert_equal('a -- b', dot)
|
303
315
|
end
|
304
316
|
|
305
317
|
def test_1prop_0comma
|
306
|
-
edge =
|
318
|
+
edge = DOT::Edge.new({"label"=>"the_label"})
|
307
319
|
dot = edge.to_s
|
308
320
|
assert_no_match(dot, /,/)
|
309
321
|
end
|
310
322
|
|
311
323
|
def test_2prop_1comma
|
312
|
-
edge =
|
324
|
+
edge = DOT::Edge.new({"label"=>"the_label", "weight"=>"2"})
|
313
325
|
dot = edge.to_s
|
314
326
|
assert_match(dot, /\[[^,]*,[^,]*\]/)
|
315
327
|
end
|
316
328
|
|
317
329
|
def test_no_label
|
318
|
-
edge =
|
330
|
+
edge = DOT::Edge.new({"weight"=>"2"})
|
319
331
|
dot = edge.to_s
|
320
332
|
assert_no_match(dot, /label/)
|
321
333
|
end
|
322
334
|
end
|
323
335
|
|
324
|
-
# Tests for
|
336
|
+
# Tests for DOT::DirectedEdge
|
325
337
|
class TestDotDirectedEdge < Test::Unit::TestCase
|
326
338
|
|
327
339
|
def test_0prop
|
328
|
-
edge =
|
340
|
+
edge = DOT::DirectedEdge.new({'from' => 'a', 'to' => 'b'})
|
329
341
|
dot = edge.to_s
|
330
342
|
assert_equal('a -> b', dot)
|
331
343
|
end
|
332
344
|
|
333
345
|
def test_1prop_0comma
|
334
|
-
edge =
|
346
|
+
edge = DOT::DirectedEdge.new({"label"=>"the_label"})
|
335
347
|
dot = edge.to_s
|
336
348
|
assert_no_match(dot, /,/)
|
337
349
|
end
|
338
350
|
|
339
351
|
def test_2prop_1comma
|
340
|
-
edge =
|
352
|
+
edge = DOT::DirectedEdge.new({"label"=>"the_label", "weight"=>"2"})
|
341
353
|
dot = edge.to_s
|
342
354
|
assert_match(dot, /\[[^,]*,[^,]*\]/)
|
343
355
|
end
|
344
356
|
|
345
357
|
def test_no_label
|
346
|
-
edge =
|
358
|
+
edge = DOT::DirectedEdge.new({"weight"=>"2"})
|
347
359
|
dot = edge.to_s
|
348
360
|
assert_no_match(dot, /label/)
|
349
361
|
end
|
350
362
|
end
|
351
363
|
|
352
|
-
# Tests for
|
364
|
+
# Tests for DOT::Graph
|
353
365
|
class TestDotGraph < Test::Unit::TestCase
|
354
366
|
def test_graph_statement
|
355
|
-
graph =
|
367
|
+
graph = DOT::Graph.new()
|
356
368
|
dot = graph.to_s
|
357
369
|
assert_match(dot, /^\s*graph /)
|
358
370
|
end
|
359
371
|
|
360
372
|
def test_name_quoting
|
361
|
-
node =
|
373
|
+
node = DOT::Graph.new({"name" => "Name with spaces"})
|
362
374
|
dot = node.to_s
|
363
375
|
assert_match(dot, /^graph "Name with spaces" \{$/)
|
364
376
|
|
365
|
-
node =
|
377
|
+
node = DOT::Graph.new({"name" => "Name with \"quotes\""})
|
366
378
|
dot = node.to_s
|
367
379
|
assert_match(dot, /^graph "Name with \\"quotes\\"" \{$/)
|
368
380
|
|
369
|
-
node =
|
381
|
+
node = DOT::Graph.new({"name" => "Name with \\backslashes\\"})
|
370
382
|
dot = node.to_s
|
371
383
|
assert_match(dot, /^graph "Name with \\\\backslashes\\\\" \{$/)
|
372
384
|
|
373
|
-
node =
|
385
|
+
node = DOT::Graph.new({"name" => "Name with\nembedded\nnewlines"})
|
386
|
+
dot = node.to_s
|
387
|
+
assert_match(dot, /\A.*"Name with\nembedded\nnewlines".*\Z/m)
|
388
|
+
|
389
|
+
node = DOT::Graph.new({"name" => "Name_with_trailing_newline\n"})
|
390
|
+
dot = node.to_s
|
391
|
+
assert_match(dot, /\A.*"Name_with_trailing_newline\n".*\Z/m)
|
392
|
+
|
393
|
+
node = DOT::Graph.new({"name" => "123.456"})
|
374
394
|
dot = node.to_s
|
375
395
|
assert_match(dot, /^graph 123.456 \{$/)
|
376
396
|
|
377
|
-
node =
|
397
|
+
node = DOT::Graph.new({"name" => ".456"})
|
378
398
|
dot = node.to_s
|
379
399
|
assert_match(dot, /^graph .456 \{$/)
|
380
400
|
|
381
|
-
node =
|
401
|
+
node = DOT::Graph.new({"name" => "-.456"})
|
382
402
|
dot = node.to_s
|
383
403
|
assert_match(dot, /^graph -.456 \{$/)
|
384
404
|
|
385
|
-
node =
|
405
|
+
node = DOT::Graph.new({"name" => "-456"})
|
386
406
|
dot = node.to_s
|
387
407
|
assert_match(dot, /^graph -456 \{$/)
|
388
408
|
|
389
|
-
node =
|
409
|
+
node = DOT::Graph.new({"name" => "-123.456"})
|
390
410
|
dot = node.to_s
|
391
411
|
assert_match(dot, /^graph -123.456 \{$/)
|
392
412
|
|
393
|
-
node =
|
413
|
+
node = DOT::Graph.new({"name" => "<html><head><title>test</title></head>\n<body>text</body></html>"})
|
394
414
|
dot = node.to_s
|
395
415
|
assert_match(dot, /^graph <html><head><title>test<\/title><\/head>\n<body>text<\/body><\/html> \{$/)
|
396
416
|
end
|
397
417
|
|
398
418
|
def test_label_quoting
|
399
|
-
node =
|
419
|
+
node = DOT::Graph.new({"name" => "test_name", "label" => "Label with spaces"})
|
400
420
|
dot = node.to_s
|
401
421
|
assert_match(dot, /label\s*=\s*"Label with spaces"/)
|
402
422
|
|
403
|
-
node =
|
423
|
+
node = DOT::Graph.new({"name" => "test_name", "label" => "Label with \"quotes\""})
|
404
424
|
dot = node.to_s
|
405
425
|
assert_match(dot, /label\s*=\s*"Label with \\"quotes\\""/)
|
406
426
|
|
407
|
-
node =
|
427
|
+
node = DOT::Graph.new({"name" => "test_name", "label" => "Label with \\backslashes\\"})
|
408
428
|
dot = node.to_s
|
409
429
|
assert_match(dot, /label\s*=\s*"Label with \\\\backslashes\\\\"/)
|
410
430
|
|
411
|
-
node =
|
431
|
+
node = DOT::Graph.new({"name" => "test_name", "label" => "Label with\nembedded\nnewlines"})
|
432
|
+
dot = node.to_s
|
433
|
+
assert_match(dot, /label\s*=\s*"Label with\\nembedded\\nnewlines"/)
|
434
|
+
|
435
|
+
node = DOT::Graph.new({"name" => "test_name", "label" => "Label_with_a_trailing_newline\n"})
|
412
436
|
dot = node.to_s
|
413
|
-
assert_match(dot, /label\s*=\s*"
|
437
|
+
assert_match(dot, /label\s*=\s*"Label_with_a_trailing_newline\\n"/)
|
414
438
|
|
415
|
-
node =
|
439
|
+
node = DOT::Graph.new({"name" => "test_name", "label" => "Left justified label\\l"})
|
416
440
|
dot = node.to_s
|
417
441
|
assert_match(dot, /label\s*=\s*"Left justified label\\l"/)
|
418
442
|
|
419
|
-
node =
|
443
|
+
node = DOT::Graph.new({"name" => "test_name", "label" => "Right justified label\\r"})
|
420
444
|
dot = node.to_s
|
421
445
|
assert_match(dot, /label\s*=\s*"Right justified label\\r"/)
|
422
446
|
|
423
|
-
node =
|
447
|
+
node = DOT::Graph.new({"name" => "test_name", "label" => "123.456"})
|
424
448
|
dot = node.to_s
|
425
449
|
assert_match(dot, /label\s*=\s*123.456/)
|
426
450
|
|
427
|
-
node =
|
451
|
+
node = DOT::Graph.new({"name" => "test_name", "label" => ".456"})
|
428
452
|
dot = node.to_s
|
429
453
|
assert_match(dot, /label\s*=\s*.456/)
|
430
454
|
|
431
|
-
node =
|
455
|
+
node = DOT::Graph.new({"name" => "test_name", "label" => "-.456"})
|
432
456
|
dot = node.to_s
|
433
457
|
assert_match(dot, /label\s*=\s*-.456/)
|
434
458
|
|
435
|
-
node =
|
459
|
+
node = DOT::Graph.new({"name" => "test_name", "label" => "-456"})
|
436
460
|
dot = node.to_s
|
437
461
|
assert_match(dot, /label\s*=\s*-456/)
|
438
462
|
|
439
|
-
node =
|
463
|
+
node = DOT::Graph.new({"name" => "test_name", "label" => "-123.456"})
|
440
464
|
dot = node.to_s
|
441
465
|
assert_match(dot, /label\s*=\s*-123.456/)
|
442
466
|
|
443
|
-
node =
|
467
|
+
node = DOT::Graph.new({"name" => "test_name", "label" => "<html><head><title>test</title></head>\n<body>text</body></html>"})
|
444
468
|
dot = node.to_s
|
445
469
|
assert_match(dot, /label\s*=\s*<html><head><title>test<\/title><\/head>\n<body>text<\/body><\/html>/)
|
446
470
|
end
|
447
471
|
|
448
472
|
def test_option_quoting
|
449
|
-
node =
|
473
|
+
node = DOT::Graph.new({"name" => "test_name", "comment" => "Comment with spaces"})
|
450
474
|
dot = node.to_s
|
451
475
|
assert_match(dot, /comment\s*=\s*"Comment with spaces"/)
|
452
476
|
|
453
|
-
node =
|
477
|
+
node = DOT::Graph.new({"name" => "test_name", "comment" => "Comment with \"quotes\""})
|
454
478
|
dot = node.to_s
|
455
479
|
assert_match(dot, /comment\s*=\s*"Comment with \\"quotes\\""/)
|
456
480
|
|
457
|
-
node =
|
481
|
+
node = DOT::Graph.new({"name" => "test_name", "comment" => "Comment with \\backslashes\\"})
|
458
482
|
dot = node.to_s
|
459
483
|
assert_match(dot, /comment\s*=\s*"Comment with \\\\backslashes\\\\"/)
|
460
484
|
|
461
|
-
node =
|
485
|
+
node = DOT::Graph.new({"name" => "test_name", "comment" => "123.456"})
|
462
486
|
dot = node.to_s
|
463
487
|
assert_match(dot, /comment\s*=\s*123.456/)
|
464
488
|
|
465
|
-
node =
|
489
|
+
node = DOT::Graph.new({"name" => "test_name", "comment" => ".456"})
|
466
490
|
dot = node.to_s
|
467
491
|
assert_match(dot, /comment\s*=\s*.456/)
|
468
492
|
|
469
|
-
node =
|
493
|
+
node = DOT::Graph.new({"name" => "test_name", "comment" => "-.456"})
|
470
494
|
dot = node.to_s
|
471
495
|
assert_match(dot, /comment\s*=\s*-.456/)
|
472
496
|
|
473
|
-
node =
|
497
|
+
node = DOT::Graph.new({"name" => "test_name", "comment" => "-456"})
|
474
498
|
dot = node.to_s
|
475
499
|
assert_match(dot, /comment\s*=\s*-456/)
|
476
500
|
|
477
|
-
node =
|
501
|
+
node = DOT::Graph.new({"name" => "test_name", "comment" => "-123.456"})
|
478
502
|
dot = node.to_s
|
479
503
|
assert_match(dot, /comment\s*=\s*-123.456/)
|
480
504
|
|
481
|
-
node =
|
505
|
+
node = DOT::Graph.new({"name" => "test_name", "comment" => "<html><head><title>test</title></head>\n<body>text</body></html>"})
|
482
506
|
dot = node.to_s
|
483
507
|
assert_match(dot, /comment\s*=\s*<html><head><title>test<\/title><\/head>\n<body>text<\/body><\/html>/)
|
484
508
|
end
|
485
509
|
|
486
510
|
def test_element_containment
|
487
|
-
node1 =
|
488
|
-
node2 =
|
511
|
+
node1 = DOT::Node.new('name' => 'test_node1')
|
512
|
+
node2 = DOT::Node.new('name' => 'test_node2')
|
489
513
|
|
490
|
-
graph =
|
514
|
+
graph = DOT::Graph.new('name' => 'test_graph')
|
491
515
|
assert_nil(graph.pop)
|
492
516
|
assert_equal(graph, graph.push(node1))
|
493
517
|
assert_equal(graph, graph << node2)
|
@@ -498,151 +522,163 @@ class TestDotGraph < Test::Unit::TestCase
|
|
498
522
|
assert_equal(node1, graph.pop)
|
499
523
|
assert_nil(graph.pop)
|
500
524
|
|
501
|
-
graph =
|
525
|
+
graph = DOT::Graph.new('name' => 'test_graph', 'elements' => [node1])
|
502
526
|
assert_equal(node1, graph.pop)
|
503
527
|
assert_nil(graph.pop)
|
504
528
|
end
|
505
529
|
end
|
506
530
|
|
507
|
-
# Tests for
|
531
|
+
# Tests for DOT::Digraph
|
508
532
|
class TestDotDigraph < Test::Unit::TestCase
|
509
533
|
def test_digraph_statement
|
510
|
-
digraph =
|
534
|
+
digraph = DOT::Digraph.new()
|
511
535
|
dot = digraph.to_s
|
512
536
|
assert_match(dot, /^\s*digraph /)
|
513
537
|
end
|
514
538
|
|
515
539
|
def test_name_quoting
|
516
|
-
node =
|
540
|
+
node = DOT::Digraph.new({"name" => "Name with spaces"})
|
517
541
|
dot = node.to_s
|
518
542
|
assert_match(dot, /^digraph "Name with spaces" \{$/)
|
519
543
|
|
520
|
-
node =
|
544
|
+
node = DOT::Digraph.new({"name" => "Name with \"quotes\""})
|
521
545
|
dot = node.to_s
|
522
546
|
assert_match(dot, /^digraph "Name with \\"quotes\\"" \{$/)
|
523
547
|
|
524
|
-
node =
|
548
|
+
node = DOT::Digraph.new({"name" => "Name with \\backslashes\\"})
|
525
549
|
dot = node.to_s
|
526
550
|
assert_match(dot, /^digraph "Name with \\\\backslashes\\\\" \{$/)
|
527
551
|
|
528
|
-
node =
|
552
|
+
node = DOT::Digraph.new({"name" => "Name with\nembedded\nnewlines"})
|
553
|
+
dot = node.to_s
|
554
|
+
assert_match(dot, /\A.*"Name with\nembedded\nnewlines".*\Z/m)
|
555
|
+
|
556
|
+
node = DOT::Digraph.new({"name" => "Name_with_trailing_newline\n"})
|
557
|
+
dot = node.to_s
|
558
|
+
assert_match(dot, /\A.*"Name_with_trailing_newline\n".*\Z/m)
|
559
|
+
|
560
|
+
node = DOT::Digraph.new({"name" => "123.456"})
|
529
561
|
dot = node.to_s
|
530
562
|
assert_match(dot, /^digraph 123.456 \{$/)
|
531
563
|
|
532
|
-
node =
|
564
|
+
node = DOT::Digraph.new({"name" => ".456"})
|
533
565
|
dot = node.to_s
|
534
566
|
assert_match(dot, /^digraph .456 \{$/)
|
535
567
|
|
536
|
-
node =
|
568
|
+
node = DOT::Digraph.new({"name" => "-.456"})
|
537
569
|
dot = node.to_s
|
538
570
|
assert_match(dot, /^digraph -.456 \{$/)
|
539
571
|
|
540
|
-
node =
|
572
|
+
node = DOT::Digraph.new({"name" => "-456"})
|
541
573
|
dot = node.to_s
|
542
574
|
assert_match(dot, /^digraph -456 \{$/)
|
543
575
|
|
544
|
-
node =
|
576
|
+
node = DOT::Digraph.new({"name" => "-123.456"})
|
545
577
|
dot = node.to_s
|
546
578
|
assert_match(dot, /^digraph -123.456 \{$/)
|
547
579
|
|
548
|
-
node =
|
580
|
+
node = DOT::Digraph.new({"name" => "<html><head><title>test</title></head>\n<body>text</body></html>"})
|
549
581
|
dot = node.to_s
|
550
582
|
assert_match(dot, /^digraph <html><head><title>test<\/title><\/head>\n<body>text<\/body><\/html> \{$/)
|
551
583
|
end
|
552
584
|
|
553
585
|
def test_label_quoting
|
554
|
-
node =
|
586
|
+
node = DOT::Digraph.new({"name" => "test_name", "label" => "Label with spaces"})
|
555
587
|
dot = node.to_s
|
556
588
|
assert_match(dot, /label\s*=\s*"Label with spaces"/)
|
557
589
|
|
558
|
-
node =
|
590
|
+
node = DOT::Digraph.new({"name" => "test_name", "label" => "Label with \"quotes\""})
|
559
591
|
dot = node.to_s
|
560
592
|
assert_match(dot, /label\s*=\s*"Label with \\"quotes\\""/)
|
561
593
|
|
562
|
-
node =
|
594
|
+
node = DOT::Digraph.new({"name" => "test_name", "label" => "Label with \\backslashes\\"})
|
563
595
|
dot = node.to_s
|
564
596
|
assert_match(dot, /label\s*=\s*"Label with \\\\backslashes\\\\"/)
|
565
597
|
|
566
|
-
node =
|
598
|
+
node = DOT::Digraph.new({"name" => "test_name", "label" => "Label with\nembedded\nnewlines"})
|
599
|
+
dot = node.to_s
|
600
|
+
assert_match(dot, /label\s*=\s*"Label with\\nembedded\\nnewlines"/)
|
601
|
+
|
602
|
+
node = DOT::Digraph.new({"name" => "test_name", "label" => "Label_with_a_trailing_newline\n"})
|
567
603
|
dot = node.to_s
|
568
|
-
assert_match(dot, /label\s*=\s*"
|
604
|
+
assert_match(dot, /label\s*=\s*"Label_with_a_trailing_newline\\n"/)
|
569
605
|
|
570
|
-
node =
|
606
|
+
node = DOT::Digraph.new({"name" => "test_name", "label" => "Left justified label\\l"})
|
571
607
|
dot = node.to_s
|
572
608
|
assert_match(dot, /label\s*=\s*"Left justified label\\l"/)
|
573
609
|
|
574
|
-
node =
|
610
|
+
node = DOT::Digraph.new({"name" => "test_name", "label" => "Right justified label\\r"})
|
575
611
|
dot = node.to_s
|
576
612
|
assert_match(dot, /label\s*=\s*"Right justified label\\r"/)
|
577
613
|
|
578
|
-
node =
|
614
|
+
node = DOT::Digraph.new({"name" => "test_name", "label" => "123.456"})
|
579
615
|
dot = node.to_s
|
580
616
|
assert_match(dot, /label\s*=\s*123.456/)
|
581
617
|
|
582
|
-
node =
|
618
|
+
node = DOT::Digraph.new({"name" => "test_name", "label" => ".456"})
|
583
619
|
dot = node.to_s
|
584
620
|
assert_match(dot, /label\s*=\s*.456/)
|
585
621
|
|
586
|
-
node =
|
622
|
+
node = DOT::Digraph.new({"name" => "test_name", "label" => "-.456"})
|
587
623
|
dot = node.to_s
|
588
624
|
assert_match(dot, /label\s*=\s*-.456/)
|
589
625
|
|
590
|
-
node =
|
626
|
+
node = DOT::Digraph.new({"name" => "test_name", "label" => "-456"})
|
591
627
|
dot = node.to_s
|
592
628
|
assert_match(dot, /label\s*=\s*-456/)
|
593
629
|
|
594
|
-
node =
|
630
|
+
node = DOT::Digraph.new({"name" => "test_name", "label" => "-123.456"})
|
595
631
|
dot = node.to_s
|
596
632
|
assert_match(dot, /label\s*=\s*-123.456/)
|
597
633
|
|
598
|
-
node =
|
634
|
+
node = DOT::Digraph.new({"name" => "test_name", "label" => "<html><head><title>test</title></head>\n<body>text</body></html>"})
|
599
635
|
dot = node.to_s
|
600
636
|
assert_match(dot, /label\s*=\s*<html><head><title>test<\/title><\/head>\n<body>text<\/body><\/html>/)
|
601
637
|
end
|
602
638
|
|
603
639
|
def test_option_quoting
|
604
|
-
node =
|
640
|
+
node = DOT::Digraph.new({"name" => "test_name", "comment" => "Comment with spaces"})
|
605
641
|
dot = node.to_s
|
606
642
|
assert_match(dot, /comment\s*=\s*"Comment with spaces"/)
|
607
643
|
|
608
|
-
node =
|
644
|
+
node = DOT::Digraph.new({"name" => "test_name", "comment" => "Comment with \"quotes\""})
|
609
645
|
dot = node.to_s
|
610
646
|
assert_match(dot, /comment\s*=\s*"Comment with \\"quotes\\""/)
|
611
647
|
|
612
|
-
node =
|
648
|
+
node = DOT::Digraph.new({"name" => "test_name", "comment" => "Comment with \\backslashes\\"})
|
613
649
|
dot = node.to_s
|
614
650
|
assert_match(dot, /comment\s*=\s*"Comment with \\\\backslashes\\\\"/)
|
615
651
|
|
616
|
-
node =
|
652
|
+
node = DOT::Digraph.new({"name" => "test_name", "comment" => "123.456"})
|
617
653
|
dot = node.to_s
|
618
654
|
assert_match(dot, /comment\s*=\s*123.456/)
|
619
655
|
|
620
|
-
node =
|
656
|
+
node = DOT::Digraph.new({"name" => "test_name", "comment" => ".456"})
|
621
657
|
dot = node.to_s
|
622
658
|
assert_match(dot, /comment\s*=\s*.456/)
|
623
659
|
|
624
|
-
node =
|
660
|
+
node = DOT::Digraph.new({"name" => "test_name", "comment" => "-.456"})
|
625
661
|
dot = node.to_s
|
626
662
|
assert_match(dot, /comment\s*=\s*-.456/)
|
627
663
|
|
628
|
-
node =
|
664
|
+
node = DOT::Digraph.new({"name" => "test_name", "comment" => "-456"})
|
629
665
|
dot = node.to_s
|
630
666
|
assert_match(dot, /comment\s*=\s*-456/)
|
631
667
|
|
632
|
-
node =
|
668
|
+
node = DOT::Digraph.new({"name" => "test_name", "comment" => "-123.456"})
|
633
669
|
dot = node.to_s
|
634
670
|
assert_match(dot, /comment\s*=\s*-123.456/)
|
635
671
|
|
636
|
-
node =
|
672
|
+
node = DOT::Digraph.new({"name" => "test_name", "comment" => "<html><head><title>test</title></head>\n<body>text</body></html>"})
|
637
673
|
dot = node.to_s
|
638
674
|
assert_match(dot, /comment\s*=\s*<html><head><title>test<\/title><\/head>\n<body>text<\/body><\/html>/)
|
639
675
|
end
|
640
676
|
|
641
677
|
def test_element_containment
|
642
|
-
node1 =
|
643
|
-
node2 =
|
678
|
+
node1 = DOT::Node.new('name' => 'test_node1')
|
679
|
+
node2 = DOT::Node.new('name' => 'test_node2')
|
644
680
|
|
645
|
-
graph =
|
681
|
+
graph = DOT::Digraph.new('name' => 'test_graph')
|
646
682
|
assert_nil(graph.pop)
|
647
683
|
assert_equal(graph, graph.push(node1))
|
648
684
|
assert_equal(graph, graph << node2)
|
@@ -653,151 +689,163 @@ class TestDotDigraph < Test::Unit::TestCase
|
|
653
689
|
assert_equal(node1, graph.pop)
|
654
690
|
assert_nil(graph.pop)
|
655
691
|
|
656
|
-
graph =
|
692
|
+
graph = DOT::Digraph.new('name' => 'test_graph', 'elements' => [node1])
|
657
693
|
assert_equal(node1, graph.pop)
|
658
694
|
assert_nil(graph.pop)
|
659
695
|
end
|
660
696
|
end
|
661
697
|
|
662
|
-
# Tests for
|
698
|
+
# Tests for DOT::Subgraph
|
663
699
|
class TestDotSubgraph < Test::Unit::TestCase
|
664
700
|
def test_subgraph_statement
|
665
|
-
subgraph =
|
701
|
+
subgraph = DOT::Subgraph.new()
|
666
702
|
dot = subgraph.to_s
|
667
703
|
assert_match(dot, /^\s*subgraph /)
|
668
704
|
end
|
669
705
|
|
670
706
|
def test_name_quoting
|
671
|
-
node =
|
707
|
+
node = DOT::Subgraph.new({"name" => "Name with spaces"})
|
672
708
|
dot = node.to_s
|
673
709
|
assert_match(dot, /^subgraph "Name with spaces" \{$/)
|
674
710
|
|
675
|
-
node =
|
711
|
+
node = DOT::Subgraph.new({"name" => "Name with \"quotes\""})
|
676
712
|
dot = node.to_s
|
677
713
|
assert_match(dot, /^subgraph "Name with \\"quotes\\"" \{$/)
|
678
714
|
|
679
|
-
node =
|
715
|
+
node = DOT::Subgraph.new({"name" => "Name with \\backslashes\\"})
|
680
716
|
dot = node.to_s
|
681
717
|
assert_match(dot, /^subgraph "Name with \\\\backslashes\\\\" \{$/)
|
682
718
|
|
683
|
-
node =
|
719
|
+
node = DOT::Subgraph.new({"name" => "Name with\nembedded\nnewlines"})
|
720
|
+
dot = node.to_s
|
721
|
+
assert_match(dot, /\A.*"Name with\nembedded\nnewlines".*\Z/m)
|
722
|
+
|
723
|
+
node = DOT::Subgraph.new({"name" => "Name_with_trailing_newline\n"})
|
724
|
+
dot = node.to_s
|
725
|
+
assert_match(dot, /\A.*"Name_with_trailing_newline\n".*\Z/m)
|
726
|
+
|
727
|
+
node = DOT::Subgraph.new({"name" => "123.456"})
|
684
728
|
dot = node.to_s
|
685
729
|
assert_match(dot, /^subgraph 123.456 \{$/)
|
686
730
|
|
687
|
-
node =
|
731
|
+
node = DOT::Subgraph.new({"name" => ".456"})
|
688
732
|
dot = node.to_s
|
689
733
|
assert_match(dot, /^subgraph .456 \{$/)
|
690
734
|
|
691
|
-
node =
|
735
|
+
node = DOT::Subgraph.new({"name" => "-.456"})
|
692
736
|
dot = node.to_s
|
693
737
|
assert_match(dot, /^subgraph -.456 \{$/)
|
694
738
|
|
695
|
-
node =
|
739
|
+
node = DOT::Subgraph.new({"name" => "-456"})
|
696
740
|
dot = node.to_s
|
697
741
|
assert_match(dot, /^subgraph -456 \{$/)
|
698
742
|
|
699
|
-
node =
|
743
|
+
node = DOT::Subgraph.new({"name" => "-123.456"})
|
700
744
|
dot = node.to_s
|
701
745
|
assert_match(dot, /^subgraph -123.456 \{$/)
|
702
746
|
|
703
|
-
node =
|
747
|
+
node = DOT::Subgraph.new({"name" => "<html><head><title>test</title></head>\n<body>text</body></html>"})
|
704
748
|
dot = node.to_s
|
705
749
|
assert_match(dot, /^subgraph <html><head><title>test<\/title><\/head>\n<body>text<\/body><\/html> \{$/)
|
706
750
|
end
|
707
751
|
|
708
752
|
def test_label_quoting
|
709
|
-
node =
|
753
|
+
node = DOT::Subgraph.new({"name" => "test_name", "label" => "Label with spaces"})
|
710
754
|
dot = node.to_s
|
711
755
|
assert_match(dot, /label\s*=\s*"Label with spaces"/)
|
712
756
|
|
713
|
-
node =
|
757
|
+
node = DOT::Subgraph.new({"name" => "test_name", "label" => "Label with \"quotes\""})
|
714
758
|
dot = node.to_s
|
715
759
|
assert_match(dot, /label\s*=\s*"Label with \\"quotes\\""/)
|
716
760
|
|
717
|
-
node =
|
761
|
+
node = DOT::Subgraph.new({"name" => "test_name", "label" => "Label with \\backslashes\\"})
|
718
762
|
dot = node.to_s
|
719
763
|
assert_match(dot, /label\s*=\s*"Label with \\\\backslashes\\\\"/)
|
720
764
|
|
721
|
-
node =
|
765
|
+
node = DOT::Subgraph.new({"name" => "test_name", "label" => "Label with\nembedded\nnewlines"})
|
766
|
+
dot = node.to_s
|
767
|
+
assert_match(dot, /label\s*=\s*"Label with\\nembedded\\nnewlines"/)
|
768
|
+
|
769
|
+
node = DOT::Subgraph.new({"name" => "test_name", "label" => "Label_with_a_trailing_newline\n"})
|
722
770
|
dot = node.to_s
|
723
|
-
assert_match(dot, /label\s*=\s*"
|
771
|
+
assert_match(dot, /label\s*=\s*"Label_with_a_trailing_newline\\n"/)
|
724
772
|
|
725
|
-
node =
|
773
|
+
node = DOT::Subgraph.new({"name" => "test_name", "label" => "Left justified label\\l"})
|
726
774
|
dot = node.to_s
|
727
775
|
assert_match(dot, /label\s*=\s*"Left justified label\\l"/)
|
728
776
|
|
729
|
-
node =
|
777
|
+
node = DOT::Subgraph.new({"name" => "test_name", "label" => "Right justified label\\r"})
|
730
778
|
dot = node.to_s
|
731
779
|
assert_match(dot, /label\s*=\s*"Right justified label\\r"/)
|
732
780
|
|
733
|
-
node =
|
781
|
+
node = DOT::Subgraph.new({"name" => "test_name", "label" => "123.456"})
|
734
782
|
dot = node.to_s
|
735
783
|
assert_match(dot, /label\s*=\s*123.456/)
|
736
784
|
|
737
|
-
node =
|
785
|
+
node = DOT::Subgraph.new({"name" => "test_name", "label" => ".456"})
|
738
786
|
dot = node.to_s
|
739
787
|
assert_match(dot, /label\s*=\s*.456/)
|
740
788
|
|
741
|
-
node =
|
789
|
+
node = DOT::Subgraph.new({"name" => "test_name", "label" => "-.456"})
|
742
790
|
dot = node.to_s
|
743
791
|
assert_match(dot, /label\s*=\s*-.456/)
|
744
792
|
|
745
|
-
node =
|
793
|
+
node = DOT::Subgraph.new({"name" => "test_name", "label" => "-456"})
|
746
794
|
dot = node.to_s
|
747
795
|
assert_match(dot, /label\s*=\s*-456/)
|
748
796
|
|
749
|
-
node =
|
797
|
+
node = DOT::Subgraph.new({"name" => "test_name", "label" => "-123.456"})
|
750
798
|
dot = node.to_s
|
751
799
|
assert_match(dot, /label\s*=\s*-123.456/)
|
752
800
|
|
753
|
-
node =
|
801
|
+
node = DOT::Subgraph.new({"name" => "test_name", "label" => "<html><head><title>test</title></head>\n<body>text</body></html>"})
|
754
802
|
dot = node.to_s
|
755
803
|
assert_match(dot, /label\s*=\s*<html><head><title>test<\/title><\/head>\n<body>text<\/body><\/html>/)
|
756
804
|
end
|
757
805
|
|
758
806
|
def test_option_quoting
|
759
|
-
node =
|
807
|
+
node = DOT::Subgraph.new({"name" => "test_name", "comment" => "Comment with spaces"})
|
760
808
|
dot = node.to_s
|
761
809
|
assert_match(dot, /comment\s*=\s*"Comment with spaces"/)
|
762
810
|
|
763
|
-
node =
|
811
|
+
node = DOT::Subgraph.new({"name" => "test_name", "comment" => "Comment with \"quotes\""})
|
764
812
|
dot = node.to_s
|
765
813
|
assert_match(dot, /comment\s*=\s*"Comment with \\"quotes\\""/)
|
766
814
|
|
767
|
-
node =
|
815
|
+
node = DOT::Subgraph.new({"name" => "test_name", "comment" => "Comment with \\backslashes\\"})
|
768
816
|
dot = node.to_s
|
769
817
|
assert_match(dot, /comment\s*=\s*"Comment with \\\\backslashes\\\\"/)
|
770
818
|
|
771
|
-
node =
|
819
|
+
node = DOT::Subgraph.new({"name" => "test_name", "comment" => "123.456"})
|
772
820
|
dot = node.to_s
|
773
821
|
assert_match(dot, /comment\s*=\s*123.456/)
|
774
822
|
|
775
|
-
node =
|
823
|
+
node = DOT::Subgraph.new({"name" => "test_name", "comment" => ".456"})
|
776
824
|
dot = node.to_s
|
777
825
|
assert_match(dot, /comment\s*=\s*.456/)
|
778
826
|
|
779
|
-
node =
|
827
|
+
node = DOT::Subgraph.new({"name" => "test_name", "comment" => "-.456"})
|
780
828
|
dot = node.to_s
|
781
829
|
assert_match(dot, /comment\s*=\s*-.456/)
|
782
830
|
|
783
|
-
node =
|
831
|
+
node = DOT::Subgraph.new({"name" => "test_name", "comment" => "-456"})
|
784
832
|
dot = node.to_s
|
785
833
|
assert_match(dot, /comment\s*=\s*-456/)
|
786
834
|
|
787
|
-
node =
|
835
|
+
node = DOT::Subgraph.new({"name" => "test_name", "comment" => "-123.456"})
|
788
836
|
dot = node.to_s
|
789
837
|
assert_match(dot, /comment\s*=\s*-123.456/)
|
790
838
|
|
791
|
-
node =
|
839
|
+
node = DOT::Subgraph.new({"name" => "test_name", "comment" => "<html><head><title>test</title></head>\n<body>text</body></html>"})
|
792
840
|
dot = node.to_s
|
793
841
|
assert_match(dot, /comment\s*=\s*<html><head><title>test<\/title><\/head>\n<body>text<\/body><\/html>/)
|
794
842
|
end
|
795
843
|
|
796
844
|
def test_element_containment
|
797
|
-
node1 =
|
798
|
-
node2 =
|
845
|
+
node1 = DOT::Node.new('name' => 'test_node1')
|
846
|
+
node2 = DOT::Node.new('name' => 'test_node2')
|
799
847
|
|
800
|
-
graph =
|
848
|
+
graph = DOT::Subgraph.new('name' => 'test_graph')
|
801
849
|
assert_nil(graph.pop)
|
802
850
|
assert_equal(graph, graph.push(node1))
|
803
851
|
assert_equal(graph, graph << node2)
|
@@ -808,7 +856,7 @@ class TestDotSubgraph < Test::Unit::TestCase
|
|
808
856
|
assert_equal(node1, graph.pop)
|
809
857
|
assert_nil(graph.pop)
|
810
858
|
|
811
|
-
graph =
|
859
|
+
graph = DOT::Subgraph.new('name' => 'test_graph', 'elements' => [node1])
|
812
860
|
assert_equal(node1, graph.pop)
|
813
861
|
assert_nil(graph.pop)
|
814
862
|
end
|