seafoam 0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (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