seafoam 0.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (147) hide show
  1. checksums.yaml +7 -0
  2. data/.github/probots.yml +2 -0
  3. data/.github/workflows/rubocop.yml +10 -0
  4. data/.github/workflows/specs.yml +19 -0
  5. data/.gitignore +7 -0
  6. data/.rubocop.yml +34 -0
  7. data/.ruby-version +1 -0
  8. data/.seafoam/config +1 -0
  9. data/CODE_OF_CONDUCT.md +128 -0
  10. data/CONTRIBUTING.md +5 -0
  11. data/Gemfile +2 -0
  12. data/LICENSE.md +7 -0
  13. data/README.md +298 -0
  14. data/bin/bgv2isabelle +53 -0
  15. data/bin/bgv2json +42 -0
  16. data/bin/seafoam +24 -0
  17. data/docs/annotators.md +43 -0
  18. data/docs/bgv.md +284 -0
  19. data/docs/getting-graphs.md +47 -0
  20. data/examples/Fib.java +24 -0
  21. data/examples/MatMult.java +39 -0
  22. data/examples/fib-java.bgv +0 -0
  23. data/examples/fib-js.bgv +0 -0
  24. data/examples/fib-ruby.bgv +0 -0
  25. data/examples/fib.js +15 -0
  26. data/examples/fib.rb +15 -0
  27. data/examples/identity.bgv +0 -0
  28. data/examples/identity.rb +13 -0
  29. data/examples/java/Irreducible.j +35 -0
  30. data/examples/java/IrreducibleDecompiled.java +21 -0
  31. data/examples/java/JavaExamples.java +418 -0
  32. data/examples/java/exampleArithOperator.bgv +0 -0
  33. data/examples/java/exampleArithOperator.cfg +925 -0
  34. data/examples/java/exampleArrayAllocation.bgv +0 -0
  35. data/examples/java/exampleArrayAllocation.cfg +5268 -0
  36. data/examples/java/exampleArrayRead.bgv +0 -0
  37. data/examples/java/exampleArrayRead.cfg +2263 -0
  38. data/examples/java/exampleArrayWrite.bgv +0 -0
  39. data/examples/java/exampleArrayWrite.cfg +2315 -0
  40. data/examples/java/exampleCatch.bgv +0 -0
  41. data/examples/java/exampleCatch.cfg +4150 -0
  42. data/examples/java/exampleCompareOperator.bgv +0 -0
  43. data/examples/java/exampleCompareOperator.cfg +1109 -0
  44. data/examples/java/exampleDoubleSynchronized.bgv +0 -0
  45. data/examples/java/exampleDoubleSynchronized.cfg +26497 -0
  46. data/examples/java/exampleExactArith.bgv +0 -0
  47. data/examples/java/exampleExactArith.cfg +1888 -0
  48. data/examples/java/exampleFieldRead.bgv +0 -0
  49. data/examples/java/exampleFieldRead.cfg +1228 -0
  50. data/examples/java/exampleFieldWrite.bgv +0 -0
  51. data/examples/java/exampleFieldWrite.cfg +1102 -0
  52. data/examples/java/exampleFor.bgv +0 -0
  53. data/examples/java/exampleFor.cfg +3936 -0
  54. data/examples/java/exampleFullEscape.bgv +0 -0
  55. data/examples/java/exampleFullEscape.cfg +5893 -0
  56. data/examples/java/exampleIf.bgv +0 -0
  57. data/examples/java/exampleIf.cfg +2462 -0
  58. data/examples/java/exampleIfNeverTaken.bgv +0 -0
  59. data/examples/java/exampleIfNeverTaken.cfg +2476 -0
  60. data/examples/java/exampleInstanceOfManyImpls.bgv +0 -0
  61. data/examples/java/exampleInstanceOfManyImpls.cfg +6391 -0
  62. data/examples/java/exampleInstanceOfOneImpl.bgv +0 -0
  63. data/examples/java/exampleInstanceOfOneImpl.cfg +2604 -0
  64. data/examples/java/exampleIntSwitch.bgv +0 -0
  65. data/examples/java/exampleIntSwitch.cfg +3121 -0
  66. data/examples/java/exampleInterfaceCallManyImpls.bgv +0 -0
  67. data/examples/java/exampleInterfaceCallManyImpls.cfg +1358 -0
  68. data/examples/java/exampleInterfaceCallOneImpl.bgv +0 -0
  69. data/examples/java/exampleInterfaceCallOneImpl.cfg +3859 -0
  70. data/examples/java/exampleLocalInstanceOf.bgv +0 -0
  71. data/examples/java/exampleLocalInstanceOf.cfg +5276 -0
  72. data/examples/java/exampleLocalSynchronized.bgv +0 -0
  73. data/examples/java/exampleLocalSynchronized.cfg +1364 -0
  74. data/examples/java/exampleLocalVariables.bgv +0 -0
  75. data/examples/java/exampleLocalVariables.cfg +1195 -0
  76. data/examples/java/exampleLocalVariablesState.bgv +0 -0
  77. data/examples/java/exampleLocalVariablesState.cfg +1673 -0
  78. data/examples/java/exampleNestedWhile.bgv +0 -0
  79. data/examples/java/exampleNestedWhile.cfg +15499 -0
  80. data/examples/java/exampleNestedWhileBreak.bgv +0 -0
  81. data/examples/java/exampleNestedWhileBreak.cfg +11162 -0
  82. data/examples/java/exampleNoEscape.bgv +0 -0
  83. data/examples/java/exampleNoEscape.cfg +974 -0
  84. data/examples/java/exampleObjectAllocation.bgv +0 -0
  85. data/examples/java/exampleObjectAllocation.cfg +5287 -0
  86. data/examples/java/examplePartialEscape.bgv +0 -0
  87. data/examples/java/examplePartialEscape.cfg +7042 -0
  88. data/examples/java/examplePhi.bgv +0 -0
  89. data/examples/java/examplePhi.cfg +3227 -0
  90. data/examples/java/exampleReducible.bgv +0 -0
  91. data/examples/java/exampleReducible.cfg +5578 -0
  92. data/examples/java/exampleSimpleCall.bgv +0 -0
  93. data/examples/java/exampleSimpleCall.cfg +1435 -0
  94. data/examples/java/exampleStamp.bgv +0 -0
  95. data/examples/java/exampleStamp.cfg +913 -0
  96. data/examples/java/exampleStaticCall.bgv +0 -0
  97. data/examples/java/exampleStaticCall.cfg +1154 -0
  98. data/examples/java/exampleStringSwitch.bgv +0 -0
  99. data/examples/java/exampleStringSwitch.cfg +15377 -0
  100. data/examples/java/exampleSynchronized.bgv +0 -0
  101. data/examples/java/exampleSynchronized.cfg +26027 -0
  102. data/examples/java/exampleThrow.bgv +0 -0
  103. data/examples/java/exampleThrow.cfg +780 -0
  104. data/examples/java/exampleThrowCatch.bgv +0 -0
  105. data/examples/java/exampleThrowCatch.cfg +744 -0
  106. data/examples/java/exampleUnsafeRead.bgv +0 -0
  107. data/examples/java/exampleUnsafeRead.cfg +912 -0
  108. data/examples/java/exampleUnsafeWrite.bgv +0 -0
  109. data/examples/java/exampleUnsafeWrite.cfg +962 -0
  110. data/examples/java/exampleWhile.bgv +0 -0
  111. data/examples/java/exampleWhile.cfg +3936 -0
  112. data/examples/java/exampleWhileBreak.bgv +0 -0
  113. data/examples/java/exampleWhileBreak.cfg +5963 -0
  114. data/examples/matmult-java.bgv +0 -0
  115. data/examples/matmult-ruby.bgv +0 -0
  116. data/examples/matmult.rb +29 -0
  117. data/examples/overflow.bgv +0 -0
  118. data/examples/overflow.rb +13 -0
  119. data/lib/seafoam.rb +13 -0
  120. data/lib/seafoam/annotators.rb +54 -0
  121. data/lib/seafoam/annotators/fallback.rb +27 -0
  122. data/lib/seafoam/annotators/graal.rb +376 -0
  123. data/lib/seafoam/bgv/bgv_parser.rb +602 -0
  124. data/lib/seafoam/binary/binary_reader.rb +21 -0
  125. data/lib/seafoam/binary/io_binary_reader.rb +88 -0
  126. data/lib/seafoam/colors.rb +18 -0
  127. data/lib/seafoam/commands.rb +447 -0
  128. data/lib/seafoam/config.rb +34 -0
  129. data/lib/seafoam/graph.rb +91 -0
  130. data/lib/seafoam/graphviz_writer.rb +213 -0
  131. data/lib/seafoam/spotlight.rb +28 -0
  132. data/lib/seafoam/version.rb +5 -0
  133. data/seafoam.gemspec +20 -0
  134. data/spec/seafoam/annotators/fallback_spec.rb +69 -0
  135. data/spec/seafoam/annotators/graal_spec.rb +96 -0
  136. data/spec/seafoam/annotators_spec.rb +61 -0
  137. data/spec/seafoam/bgv/bgv_parser_spec.rb +144 -0
  138. data/spec/seafoam/bgv/fixtures/not.bgv +1 -0
  139. data/spec/seafoam/bgv/fixtures/unsupported.bgv +1 -0
  140. data/spec/seafoam/binary/io_binary_reader_spec.rb +176 -0
  141. data/spec/seafoam/command_spec.rb +252 -0
  142. data/spec/seafoam/graph_spec.rb +172 -0
  143. data/spec/seafoam/graphviz_writer_spec.rb +63 -0
  144. data/spec/seafoam/spec_helpers.rb +30 -0
  145. data/spec/seafoam/spotlight_spec.rb +38 -0
  146. data/tools/render-all +36 -0
  147. metadata +238 -0
@@ -0,0 +1,252 @@
1
+ require 'stringio'
2
+ require 'tempfile'
3
+
4
+ require 'seafoam'
5
+
6
+ require 'rspec'
7
+
8
+ describe Seafoam::Commands do
9
+ before :all do
10
+ @fib_java = File.expand_path('../../examples/fib-java.bgv', __dir__)
11
+ end
12
+
13
+ before :each do
14
+ @out = StringIO.new
15
+ @commands = Seafoam::Commands.new(@out, {})
16
+ end
17
+
18
+ describe '#info' do
19
+ it 'prints format and version' do
20
+ @commands.send :info, @fib_java
21
+ lines = @out.string.lines.map(&:rstrip)
22
+ expect(lines.first).to eq 'BGV 6.1'
23
+ end
24
+
25
+ it 'does not work on a graph' do
26
+ expect { @commands.send :info, "#{@fib_java}:0" }.to raise_error(ArgumentError)
27
+ end
28
+ end
29
+
30
+ describe '#list' do
31
+ it 'prints graphs' do
32
+ @commands.send :list, @fib_java
33
+ lines = @out.string.lines.map(&:rstrip)
34
+ expect(lines.take(5)).to eq [
35
+ "#{@fib_java}:0 2:Fib.fib(int)/After phase org.graalvm.compiler.java.GraphBuilderPhase",
36
+ "#{@fib_java}:1 2:Fib.fib(int)/After phase org.graalvm.compiler.phases.PhaseSuite",
37
+ "#{@fib_java}:2 2:Fib.fib(int)/After phase org.graalvm.compiler.phases.common.DeadCodeEliminationPhase",
38
+ "#{@fib_java}:3 2:Fib.fib(int)/After parsing",
39
+ "#{@fib_java}:4 2:Fib.fib(int)/After phase org.graalvm.compiler.phases.common.CanonicalizerPhase"
40
+ ]
41
+ expect(lines.drop(lines.length - 5)).to eq [
42
+ "#{@fib_java}:46 2:Fib.fib(int)/After phase org.graalvm.compiler.phases.common.PropagateDeoptimizeProbabilityPhase",
43
+ "#{@fib_java}:47 2:Fib.fib(int)/After phase org.graalvm.compiler.phases.common.InsertMembarsPhase",
44
+ "#{@fib_java}:48 2:Fib.fib(int)/After phase org.graalvm.compiler.phases.schedule.SchedulePhase",
45
+ "#{@fib_java}:49 2:Fib.fib(int)/After phase org.graalvm.compiler.core.phases.LowTier",
46
+ "#{@fib_java}:50 2:Fib.fib(int)/After low tier"
47
+ ]
48
+ end
49
+
50
+ it 'does not work on a graph' do
51
+ expect { @commands.send :list, "#{@fib_java}:0" }.to raise_error(ArgumentError)
52
+ end
53
+ end
54
+
55
+ describe '#search' do
56
+ it 'finds terms in files' do
57
+ @commands.send :search, @fib_java, 'MethodCallTarget'
58
+ lines = @out.string.lines.map(&:rstrip)
59
+ expect(lines.take(5)).to eq [
60
+ "#{@fib_java}:0:12 ...class\":\"org.graalvm.compiler.nodes.java.MethodCallTargetNode\",\"name_template\":\"\",\"inputs\":[{\"dir...",
61
+ "#{@fib_java}:0:17 ...class\":\"org.graalvm.compiler.nodes.java.MethodCallTargetNode\",\"name_template\":\"\",\"inputs\":[{\"dir...",
62
+ "#{@fib_java}:1:12 ...class\":\"org.graalvm.compiler.nodes.java.MethodCallTargetNode\",\"name_template\":\"\",\"inputs\":[{\"dir...",
63
+ "#{@fib_java}:1:17 ...class\":\"org.graalvm.compiler.nodes.java.MethodCallTargetNode\",\"name_template\":\"\",\"inputs\":[{\"dir...",
64
+ "#{@fib_java}:2:12 ...class\":\"org.graalvm.compiler.nodes.java.MethodCallTargetNode\",\"name_template\":\"\",\"inputs\":[{\"dir..."
65
+ ]
66
+ end
67
+
68
+ it 'finds terms in graphs' do
69
+ @commands.send :search, "#{@fib_java}:0", 'MethodCallTarget'
70
+ lines = @out.string.lines.map(&:rstrip)
71
+ expect(lines).to eq [
72
+ "#{@fib_java}:0:12 ...class\":\"org.graalvm.compiler.nodes.java.MethodCallTargetNode\",\"name_template\":\"\",\"inputs\":[{\"dir...",
73
+ "#{@fib_java}:0:17 ...class\":\"org.graalvm.compiler.nodes.java.MethodCallTargetNode\",\"name_template\":\"\",\"inputs\":[{\"dir..."
74
+ ]
75
+ end
76
+
77
+ it 'is case-insensitive' do
78
+ @commands.send :search, @fib_java, 'methodcalltarget'
79
+ lines = @out.string.lines.map(&:rstrip)
80
+ expect(lines.take(5)).to eq [
81
+ "#{@fib_java}:0:12 ...class\":\"org.graalvm.compiler.nodes.java.MethodCallTargetNode\",\"name_template\":\"\",\"inputs\":[{\"dir...",
82
+ "#{@fib_java}:0:17 ...class\":\"org.graalvm.compiler.nodes.java.MethodCallTargetNode\",\"name_template\":\"\",\"inputs\":[{\"dir...",
83
+ "#{@fib_java}:1:12 ...class\":\"org.graalvm.compiler.nodes.java.MethodCallTargetNode\",\"name_template\":\"\",\"inputs\":[{\"dir...",
84
+ "#{@fib_java}:1:17 ...class\":\"org.graalvm.compiler.nodes.java.MethodCallTargetNode\",\"name_template\":\"\",\"inputs\":[{\"dir...",
85
+ "#{@fib_java}:2:12 ...class\":\"org.graalvm.compiler.nodes.java.MethodCallTargetNode\",\"name_template\":\"\",\"inputs\":[{\"dir..."
86
+ ]
87
+ end
88
+
89
+ it 'does not work on a node' do
90
+ expect { @commands.send :search, "#{@fib_java}:0:0" }.to raise_error(ArgumentError)
91
+ end
92
+ end
93
+
94
+ describe '#edges' do
95
+ it 'prints the number of edges and nodes for a graph' do
96
+ @commands.send :edges, "#{@fib_java}:0"
97
+ lines = @out.string.lines.map(&:rstrip)
98
+ expect(lines.first).to eq '22 nodes, 30 edges'
99
+ end
100
+
101
+ it 'prints the edges for a node' do
102
+ @commands.send :edges, "#{@fib_java}:0:13"
103
+ lines = @out.string.lines.map(&:rstrip)
104
+ expect(lines).to eq [
105
+ 'Input:',
106
+ ' 13 (Call Fib.fib) <-() 6 (Begin)',
107
+ ' 13 (Call Fib.fib) <-() 14 (FrameState Fib#fib Fib.java:20)',
108
+ ' 13 (Call Fib.fib) <-() 12 (MethodCallTarget)',
109
+ 'Output:',
110
+ ' 13 (Call Fib.fib) ->() 18 (Call Fib.fib)',
111
+ ' 13 (Call Fib.fib) ->(values) 14 (FrameState Fib#fib Fib.java:20)',
112
+ ' 13 (Call Fib.fib) ->(values) 19 (FrameState Fib#fib Fib.java:20)',
113
+ ' 13 (Call Fib.fib) ->(x) 20 (+)'
114
+ ]
115
+ end
116
+
117
+ it 'prints details for an edge' do
118
+ @commands.send :edges, "#{@fib_java}:0:13-18"
119
+ lines = @out.string.lines.map(&:rstrip)
120
+ expect(lines.first).to eq '13 (Call Fib.fib) ->() 18 (Call Fib.fib)'
121
+ end
122
+
123
+ it 'does not work on a file' do
124
+ expect { @commands.send :edges, @fib_java }.to raise_error(ArgumentError)
125
+ end
126
+ end
127
+
128
+ describe '#props' do
129
+ it 'prints properties for a file' do
130
+ @commands.send :props, @fib_java.to_s
131
+ expect(@out.string.gsub(/\n\n/, "\n")).to eq "{\n}\n"
132
+ end
133
+
134
+ it 'prints properties for a graph' do
135
+ @commands.send :props, "#{@fib_java}:0"
136
+ lines = @out.string.lines.map(&:rstrip)
137
+ expect(lines[3]).to include '"name": "2:Fib.fib(int)"'
138
+ end
139
+
140
+ it 'prints properties for a node' do
141
+ @commands.send :props, "#{@fib_java}:0:13"
142
+ lines = @out.string.lines.map(&:rstrip)
143
+ expect(lines[3]).to include '"declaring_class": "Fib"'
144
+ end
145
+
146
+ it 'prints properties for an edge' do
147
+ @commands.send :props, "#{@fib_java}:0:13-18"
148
+ lines = @out.string.lines.map(&:rstrip)
149
+ expect(lines[2]).to include '"name": "next"'
150
+ end
151
+ end
152
+
153
+ describe '#render' do
154
+ it 'does not work on a file' do
155
+ expect { @commands.send :render, @fib_java }.to raise_error(ArgumentError)
156
+ end
157
+
158
+ it 'can render all BGV files to dot' do
159
+ Seafoam::SpecHelpers::SAMPLE_BGV.each do |file|
160
+ @commands.send :render, "#{file}:7", '--out', 'out.dot'
161
+ end
162
+ end
163
+
164
+ it 'supports -o out.pdf' do
165
+ @commands.send :render, "#{@fib_java}:0", '--out', 'out.pdf'
166
+ expect(`file out.pdf`).to start_with 'out.pdf: PDF document'
167
+ end
168
+
169
+ it 'supports -o out.svg' do
170
+ @commands.send :render, "#{@fib_java}:0", '--out', 'out.svg'
171
+ expect(`file out.svg`).to start_with 'out.svg: SVG Scalable Vector Graphics image'
172
+ end
173
+
174
+ it 'supports -o out.png' do
175
+ @commands.send :render, "#{@fib_java}:0", '--out', 'out.png'
176
+ expect(`file out.png`).to start_with 'out.png: PNG image data'
177
+ end
178
+
179
+ it 'supports -o out.dot' do
180
+ @commands.send :render, "#{@fib_java}:0", '--out', 'out.dot'
181
+ expect(`file out.dot`).to start_with 'out.dot: ASCII text'
182
+ end
183
+
184
+ it 'supports spotlighting nodes' do
185
+ @commands.send :render, "#{@fib_java}:0", '--spotlight', '13'
186
+ end
187
+
188
+ it 'does not work on a node' do
189
+ expect { @commands.send :render, "#{@fib_java}:0:13" }.to raise_error(ArgumentError)
190
+ end
191
+ end
192
+
193
+ describe '#debug' do
194
+ it 'does not work with a graph' do
195
+ expect { @commands.send :debug, "#{@fib_java}:0" }.to raise_error(ArgumentError)
196
+ end
197
+ end
198
+
199
+ describe '#help' do
200
+ it 'does not take any arguments' do
201
+ expect { @commands.send :help, 'foo' }.to raise_error(ArgumentError)
202
+ end
203
+ end
204
+
205
+ describe '#version' do
206
+ it 'does not take any arguments' do
207
+ expect { @commands.send :help, 'foo' }.to raise_error(ArgumentError)
208
+ end
209
+ end
210
+
211
+ describe '#parse_name' do
212
+ it 'parses file.bgv' do
213
+ file, graph, node, edge = @commands.send(:parse_name, 'file.bgv')
214
+ expect(file).to eq 'file.bgv'
215
+ expect(graph).to be_nil
216
+ expect(node).to be_nil
217
+ expect(edge).to be_nil
218
+ end
219
+
220
+ it 'parses file.bgv:14' do
221
+ file, graph, node, edge = @commands.send(:parse_name, 'file.bgv:14')
222
+ expect(file).to eq 'file.bgv'
223
+ expect(graph).to eq 14
224
+ expect(node).to be_nil
225
+ expect(edge).to be_nil
226
+ end
227
+
228
+ it 'parses file.bgv:14:12' do
229
+ file, graph, node, edge = @commands.send(:parse_name, 'file.bgv:14:12')
230
+ expect(file).to eq 'file.bgv'
231
+ expect(graph).to eq 14
232
+ expect(node).to eq 12
233
+ expect(edge).to be_nil
234
+ end
235
+
236
+ it 'parses file.bgv:14:12-81' do
237
+ file, graph, node, edge = @commands.send(:parse_name, 'file.bgv:14:12-81')
238
+ expect(file).to eq 'file.bgv'
239
+ expect(graph).to eq 14
240
+ expect(node).to eq 12
241
+ expect(edge).to eq 81
242
+ end
243
+
244
+ it 'parses a realistic knarly BGV file name' do
245
+ file, graph, node, edge = @commands.send(:parse_name, '../graal_dumps/2019.11.03.19.35.03.828/TruffleHotSpotCompilation-13320[while_loop_at__Users_chrisseaton_src_github.com_Shopify_truffleruby-shopify_src_main_ruby_truffleruby_core_kernel.rb:360<OSR>].bgv:14:12-81')
246
+ expect(file).to eq '../graal_dumps/2019.11.03.19.35.03.828/TruffleHotSpotCompilation-13320[while_loop_at__Users_chrisseaton_src_github.com_Shopify_truffleruby-shopify_src_main_ruby_truffleruby_core_kernel.rb:360<OSR>].bgv'
247
+ expect(graph).to eq 14
248
+ expect(node).to eq 12
249
+ expect(edge).to eq 81
250
+ end
251
+ end
252
+ end
@@ -0,0 +1,172 @@
1
+ require 'seafoam'
2
+
3
+ require 'rspec'
4
+
5
+ describe Seafoam::Graph do
6
+ describe '#initialize' do
7
+ it 'set properties' do
8
+ graph = Seafoam::Graph.new(a: 12)
9
+ expect(graph.props[:a]).to eq 12
10
+ end
11
+ end
12
+
13
+ describe '#create_node' do
14
+ it 'adds a node to the graph' do
15
+ graph = Seafoam::Graph.new
16
+ node = graph.create_node(14)
17
+ expect(graph.nodes.values).to include node
18
+ end
19
+
20
+ it 'set properties' do
21
+ graph = Seafoam::Graph.new
22
+ node = graph.create_node(14, a: 12)
23
+ expect(node.props[:a]).to eq 12
24
+ end
25
+
26
+ it 'returns the node' do
27
+ graph = Seafoam::Graph.new
28
+ node = graph.create_node(14)
29
+ expect(node.id).to eq 14
30
+ end
31
+ end
32
+
33
+ describe '#create_edge' do
34
+ it 'adds an edge to the graph' do
35
+ graph = Seafoam::Graph.new
36
+ from = graph.create_node(14)
37
+ to = graph.create_node(2)
38
+ edge = graph.create_edge(from, to)
39
+ expect(graph.edges).to include edge
40
+ end
41
+
42
+ it 'set properties' do
43
+ graph = Seafoam::Graph.new
44
+ from = graph.create_node(14)
45
+ to = graph.create_node(2)
46
+ edge = graph.create_edge(from, to, a: 12)
47
+ expect(edge.props[:a]).to eq 12
48
+ end
49
+
50
+ it 'returns the edge' do
51
+ graph = Seafoam::Graph.new
52
+ from = graph.create_node(14)
53
+ to = graph.create_node(2)
54
+ edge = graph.create_edge(from, to)
55
+ expect(edge.from).to eq from
56
+ expect(edge.to).to eq to
57
+ end
58
+ end
59
+ end
60
+
61
+ describe Seafoam::Node do
62
+ describe '#inputs' do
63
+ it 'returns only input edges' do
64
+ graph = Seafoam::Graph.new
65
+ node_a = graph.create_node(1)
66
+ node_b = graph.create_node(2)
67
+ node_c = graph.create_node(3)
68
+ node_d = graph.create_node(4)
69
+ in_edge = graph.create_edge(node_a, node_b)
70
+ out_edge = graph.create_edge(node_b, node_c)
71
+ unrelated_edge = graph.create_edge(node_c, node_d)
72
+ expect(node_b.inputs).to include in_edge
73
+ expect(node_b.inputs).to_not include out_edge
74
+ expect(node_b.inputs).to_not include unrelated_edge
75
+ end
76
+ end
77
+
78
+ describe '#outputs' do
79
+ it 'returns only output edges' do
80
+ graph = Seafoam::Graph.new
81
+ node_a = graph.create_node(1)
82
+ node_b = graph.create_node(2)
83
+ node_c = graph.create_node(3)
84
+ node_d = graph.create_node(4)
85
+ in_edge = graph.create_edge(node_a, node_b)
86
+ out_edge = graph.create_edge(node_b, node_c)
87
+ unrelated_edge = graph.create_edge(node_c, node_d)
88
+ expect(node_b.outputs).to_not include in_edge
89
+ expect(node_b.outputs).to include out_edge
90
+ expect(node_b.outputs).to_not include unrelated_edge
91
+ end
92
+ end
93
+
94
+ describe '#edges' do
95
+ it 'returns both input and output edges' do
96
+ graph = Seafoam::Graph.new
97
+ node_a = graph.create_node(1)
98
+ node_b = graph.create_node(2)
99
+ node_c = graph.create_node(3)
100
+ node_d = graph.create_node(4)
101
+ in_edge = graph.create_edge(node_a, node_b)
102
+ out_edge = graph.create_edge(node_b, node_c)
103
+ unrelated_edge = graph.create_edge(node_c, node_d)
104
+ expect(node_b.edges).to include in_edge
105
+ expect(node_b.edges).to include out_edge
106
+ expect(node_b.edges).to_not include unrelated_edge
107
+ end
108
+ end
109
+
110
+ describe '#adjacent' do
111
+ it 'returns nodes from both input and output edges' do
112
+ graph = Seafoam::Graph.new
113
+ node_a = graph.create_node(1)
114
+ node_b = graph.create_node(2)
115
+ node_c = graph.create_node(3)
116
+ node_d = graph.create_node(4)
117
+ graph.create_edge node_a, node_b
118
+ graph.create_edge node_b, node_c
119
+ graph.create_edge node_c, node_d
120
+ expect(node_b.adjacent).to include node_a
121
+ expect(node_b.adjacent).to include node_c
122
+ expect(node_b.adjacent).to_not include node_d
123
+ end
124
+ end
125
+
126
+ describe '#id_and_label' do
127
+ it 'returns an id and label if there is one' do
128
+ node = Seafoam::Node.new(14, label: 'foo')
129
+ expect(node.id_and_label).to eq '14 (foo)'
130
+ end
131
+
132
+ it 'returns just an id if there is no label' do
133
+ node = Seafoam::Node.new(14)
134
+ expect(node.id_and_label).to eq '14'
135
+ end
136
+ end
137
+ end
138
+
139
+ describe Seafoam::Edge do
140
+ describe '#from' do
141
+ it 'returns the from node' do
142
+ graph = Seafoam::Graph.new
143
+ node_a = graph.create_node(1)
144
+ node_b = graph.create_node(2)
145
+ edge = graph.create_edge(node_a, node_b)
146
+ expect(edge.from).to eq node_a
147
+ end
148
+ end
149
+
150
+ describe '#to' do
151
+ it 'returns the to node' do
152
+ graph = Seafoam::Graph.new
153
+ node_a = graph.create_node(1)
154
+ node_b = graph.create_node(2)
155
+ edge = graph.create_edge(node_a, node_b)
156
+ expect(edge.to).to eq node_b
157
+ end
158
+ end
159
+
160
+ describe '#nodes' do
161
+ it 'returns both the from and to node' do
162
+ graph = Seafoam::Graph.new
163
+ node_a = graph.create_node(1)
164
+ node_b = graph.create_node(2)
165
+ node_c = graph.create_node(3)
166
+ edge = graph.create_edge(node_a, node_b)
167
+ expect(edge.nodes).to include node_a
168
+ expect(edge.nodes).to include node_b
169
+ expect(edge.nodes).to_not include node_c
170
+ end
171
+ end
172
+ end
@@ -0,0 +1,63 @@
1
+ require 'stringio'
2
+
3
+ require 'seafoam'
4
+
5
+ require 'rspec'
6
+
7
+ require_relative 'spec_helpers'
8
+
9
+ describe Seafoam::GraphvizWriter do
10
+ describe '#write_graph' do
11
+ before :all do
12
+ file = File.expand_path('../../examples/fib-java.bgv', __dir__)
13
+ parser = Seafoam::BGV::BGVParser.new(File.new(file))
14
+ parser.read_file_header
15
+ parser.read_graph_preheader
16
+ parser.read_graph_header
17
+ @fib_java_graph = parser.read_graph
18
+ end
19
+
20
+ before :each do
21
+ @stream = StringIO.new
22
+ @writer = Seafoam::GraphvizWriter.new(@stream)
23
+ end
24
+
25
+ it 'writes the header' do
26
+ @writer.write_graph @fib_java_graph
27
+ expect(@stream.string).to start_with 'digraph G {'
28
+ end
29
+
30
+ it 'writes all graphs' do
31
+ Seafoam::SpecHelpers::SAMPLE_BGV.each do |file|
32
+ parser = Seafoam::BGV::BGVParser.new(File.new(file))
33
+ parser.read_file_header
34
+ parser.skip_document_props
35
+ parser.skip_document_props
36
+ loop do
37
+ index, = parser.read_graph_preheader
38
+ break unless index
39
+
40
+ parser.read_graph_header
41
+ @writer.write_graph parser.read_graph
42
+ end
43
+ end
44
+ end
45
+ end
46
+
47
+ describe '#quote' do
48
+ it 'adds quotes' do
49
+ writer = Seafoam::GraphvizWriter.new(nil)
50
+ expect(writer.send(:quote, 'foo')).to eq '"foo"'
51
+ end
52
+
53
+ it 'escapes an existing quote' do
54
+ writer = Seafoam::GraphvizWriter.new(nil)
55
+ expect(writer.send(:quote, 'foo"bar')).to eq '"foo\"bar"'
56
+ end
57
+
58
+ it 'escapes an existing escape' do
59
+ writer = Seafoam::GraphvizWriter.new(nil)
60
+ expect(writer.send(:quote, 'foo\bar')).to eq '"foo\\bar"'
61
+ end
62
+ end
63
+ end