rdg 0.0.2 → 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.rubocop.yml +3 -0
- data/.ruby-version +1 -0
- data/.travis.yml +2 -2
- data/Gemfile +1 -1
- data/RELEASES.md +10 -0
- data/TODO.md +68 -0
- data/bin/ast +5 -4
- data/bin/cfg +5 -4
- data/lib/rdg/analysis/analyser.rb +30 -0
- data/lib/rdg/analysis/composite.rb +23 -0
- data/lib/rdg/analysis/context.rb +17 -0
- data/lib/rdg/analysis/equivalences.rb +25 -0
- data/lib/rdg/analysis/propagater.rb +51 -0
- data/lib/rdg/analysis/registry.rb +35 -0
- data/lib/rdg/cfg.rb +12 -41
- data/lib/rdg/control/begin.rb +6 -4
- data/lib/rdg/control/break.rb +19 -0
- data/lib/rdg/control/case.rb +25 -0
- data/lib/rdg/control/{while.rb → conditional_loop.rb} +8 -7
- data/lib/rdg/control/def.rb +21 -7
- data/lib/rdg/control/ensure.rb +30 -0
- data/lib/rdg/control/for.rb +29 -0
- data/lib/rdg/control/handler.rb +25 -0
- data/lib/rdg/control/if.rb +7 -6
- data/lib/rdg/control/jump.rb +33 -0
- data/lib/rdg/control/jump_to_start.rb +11 -0
- data/lib/rdg/control/next.rb +9 -0
- data/lib/rdg/control/none.rb +5 -3
- data/lib/rdg/control/redo.rb +9 -0
- data/lib/rdg/control/rescue.rb +31 -0
- data/lib/rdg/control/rescue_body.rb +29 -0
- data/lib/rdg/control/retry.rb +13 -0
- data/lib/rdg/control/return.rb +5 -6
- data/lib/rdg/control/when.rb +27 -0
- data/lib/rdg/graph/bidirected_adjacency_graph.rb +19 -0
- data/lib/rdg/graph/rgl/allow_duplicates.rb +45 -0
- data/lib/rdg/tree/ast.rb +15 -4
- data/lib/rdg/version.rb +1 -1
- data/rdg.gemspec +3 -2
- data/spec/integration/cfg/conditionals/case_spec.rb +66 -0
- data/spec/integration/cfg/{if_spec.rb → conditionals/if_spec.rb} +24 -9
- data/spec/integration/cfg/conditionals/unless_spec.rb +53 -0
- data/spec/integration/cfg/exceptions_spec.rb +131 -0
- data/spec/integration/cfg/loops/loop_control_spec.rb +90 -0
- data/spec/integration/cfg/loops/loop_spec.rb +70 -0
- data/spec/integration/cfg/sequence_spec.rb +7 -1
- data/spec/support/doubles/fake_ast.rb +15 -0
- data/spec/support/matchers/flow_between_matcher.rb +1 -1
- data/spec/unit/analysis/composite_spec.rb +47 -0
- data/spec/unit/analysis/equivalences_spec.rb +29 -0
- data/spec/unit/{control/analyser_spec.rb → analysis/propagater_spec.rb} +23 -12
- data/spec/unit/analysis/registry_spec.rb +61 -0
- data/spec/unit/control/begin_spec.rb +3 -6
- data/spec/unit/control/break_spec.rb +26 -0
- data/spec/unit/control/case_spec.rb +66 -0
- data/spec/unit/control/conditional_loop_spec.rb +22 -0
- data/spec/unit/control/ensure_spec.rb +33 -0
- data/spec/unit/control/for_spec.rb +26 -0
- data/spec/unit/control/handler_spec.rb +27 -0
- data/spec/unit/control/if_spec.rb +26 -18
- data/spec/unit/control/jump_spec.rb +43 -0
- data/spec/unit/control/jump_to_start_spec.rb +22 -0
- data/spec/unit/control/rescue_body_spec.rb +26 -0
- data/spec/unit/control/rescue_spec.rb +62 -0
- data/spec/unit/control/return_spec.rb +1 -10
- data/spec/unit/control/when_spec.rb +59 -0
- data/spec/unit/graph/bidirected_adjacency_graph_spec.rb +49 -0
- metadata +91 -18
- data/lib/rdg/control/analyser.rb +0 -44
- data/spec/integration/cfg/methods_spec.rb +0 -39
- data/spec/unit/control/def_spec.rb +0 -28
- data/spec/unit/control/while_spec.rb +0 -25
@@ -0,0 +1,45 @@
|
|
1
|
+
require "rgl/dot"
|
2
|
+
|
3
|
+
# This module provides a workaround for RGL issue #15:
|
4
|
+
# https://github.com/monora/rgl/issues/15
|
5
|
+
#
|
6
|
+
# Once the fix appears in an RGL release, this module
|
7
|
+
# and any includes of this module can be safely removed.
|
8
|
+
|
9
|
+
# rubocop:disable all
|
10
|
+
module RDG
|
11
|
+
module RGL
|
12
|
+
module AllowDuplicates
|
13
|
+
def to_dot_graph(params = {})
|
14
|
+
params['name'] ||= self.class.name.gsub(/:/, '_')
|
15
|
+
fontsize = params['fontsize'] ? params['fontsize'] : '8'
|
16
|
+
graph = (directed? ? ::RGL::DOT::Digraph : ::RGL::DOT::Graph).new(params)
|
17
|
+
edge_class = directed? ? ::RGL::DOT::DirectedEdge : ::RGL::DOT::Edge
|
18
|
+
|
19
|
+
each_vertex do |v|
|
20
|
+
graph << ::RGL::DOT::Node.new(
|
21
|
+
'name' => v.object_id,
|
22
|
+
'fontsize' => fontsize,
|
23
|
+
'label' => vertex_label(v)
|
24
|
+
)
|
25
|
+
end
|
26
|
+
|
27
|
+
each_edge do |u, v|
|
28
|
+
graph << edge_class.new(
|
29
|
+
'from' => u.object_id,
|
30
|
+
'to' => v.object_id,
|
31
|
+
'fontsize' => fontsize
|
32
|
+
)
|
33
|
+
end
|
34
|
+
|
35
|
+
graph
|
36
|
+
end
|
37
|
+
|
38
|
+
# Returns a label for vertex v. Default is v.to_s
|
39
|
+
def vertex_label(v)
|
40
|
+
v.to_s
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
# rubocop:enable all
|
data/lib/rdg/tree/ast.rb
CHANGED
@@ -1,6 +1,5 @@
|
|
1
|
-
require "rgl/adjacency"
|
2
|
-
require "rgl/dot"
|
3
1
|
require "parser/current"
|
2
|
+
require_relative "../graph/bidirected_adjacency_graph"
|
4
3
|
require_relative "rgl/pre_order_iterator"
|
5
4
|
require_relative "rgl/post_order_iterator"
|
6
5
|
|
@@ -16,7 +15,7 @@ module RDG
|
|
16
15
|
end
|
17
16
|
|
18
17
|
def initialize(ast)
|
19
|
-
@graph = ::
|
18
|
+
@graph = Graph::BidirectedAdjacencyGraph.new
|
20
19
|
import(ast)
|
21
20
|
end
|
22
21
|
|
@@ -75,7 +74,19 @@ module RDG
|
|
75
74
|
end
|
76
75
|
|
77
76
|
def children
|
78
|
-
@graph.
|
77
|
+
@graph.each_successor(self).to_a
|
78
|
+
end
|
79
|
+
|
80
|
+
def parent
|
81
|
+
@graph.each_predecessor(self).first
|
82
|
+
end
|
83
|
+
|
84
|
+
def ancestors
|
85
|
+
parent.nil? ? [] : parent.ancestors.unshift(parent)
|
86
|
+
end
|
87
|
+
|
88
|
+
def siblings
|
89
|
+
parent.nil? ? [] : parent.children
|
79
90
|
end
|
80
91
|
|
81
92
|
def ==(other)
|
data/lib/rdg/version.rb
CHANGED
data/rdg.gemspec
CHANGED
@@ -18,10 +18,11 @@ Gem::Specification.new do |spec|
|
|
18
18
|
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
19
19
|
spec.require_paths = ["lib"]
|
20
20
|
|
21
|
-
spec.add_runtime_dependency "parser", "~> 2.2.0
|
21
|
+
spec.add_runtime_dependency "parser", "~> 2.2.2.0"
|
22
22
|
spec.add_runtime_dependency "rgl", "~> 0.5.0"
|
23
|
+
spec.add_runtime_dependency "require_all", "~> 1.3.2"
|
23
24
|
|
24
|
-
spec.add_development_dependency "bundler", "~> 1.
|
25
|
+
spec.add_development_dependency "bundler", "~> 1.9.0"
|
25
26
|
spec.add_development_dependency "rake", "~> 10.4.2"
|
26
27
|
spec.add_development_dependency "rspec", "~> 3.2.0"
|
27
28
|
spec.add_development_dependency "codeclimate-test-reporter", "~> 0.4.6"
|
@@ -0,0 +1,66 @@
|
|
1
|
+
require "rdg/cfg"
|
2
|
+
|
3
|
+
module RDG
|
4
|
+
describe CFG do
|
5
|
+
context "for simple case expressions" do
|
6
|
+
it "should show control flowing to expression, to test and then through and around action" do
|
7
|
+
cfg = CFG.from_source("a = 1; case b; when 2; c = 1; end; z = 1")
|
8
|
+
|
9
|
+
expect(cfg).to contain("a = 1", "b", "2", "c = 1", "z = 1")
|
10
|
+
|
11
|
+
expect(cfg).to flow_between("a = 1", "b")
|
12
|
+
expect(cfg).to flow_between("b", "2")
|
13
|
+
expect(cfg).to flow_between("2", "z = 1")
|
14
|
+
expect(cfg).to flow_between("2", "c = 1")
|
15
|
+
expect(cfg).to flow_between("c = 1", "z = 1")
|
16
|
+
end
|
17
|
+
|
18
|
+
it "should show control flowing through longer action" do
|
19
|
+
cfg = CFG.from_source("a = 1; case b; when 2; c = 1; d = 1; e = 1; end; z = 1")
|
20
|
+
|
21
|
+
expect(cfg).to contain("a = 1", "b", "2", "c = 1", "d = 1", "e = 1", "z = 1")
|
22
|
+
|
23
|
+
expect(cfg).to flow_between("a = 1", "b")
|
24
|
+
expect(cfg).to flow_between("b", "2")
|
25
|
+
expect(cfg).to flow_between("2", "z = 1")
|
26
|
+
expect(cfg).to flow_between("2", "c = 1")
|
27
|
+
expect(cfg).to flow_between("c = 1", "d = 1")
|
28
|
+
expect(cfg).to flow_between("d = 1", "e = 1")
|
29
|
+
expect(cfg).to flow_between("e = 1", "z = 1")
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
context "for case expressions with several actions" do
|
34
|
+
it "should show control flowing from test to action and to alternative" do
|
35
|
+
cfg = CFG.from_source("a = 1; case b; when 2; c = 1; else; d = 1; end; z = 1")
|
36
|
+
|
37
|
+
expect(cfg).to contain("a = 1", "b", "2", "c = 1", "d = 1", "z = 1")
|
38
|
+
|
39
|
+
expect(cfg).to flow_between("a = 1", "b")
|
40
|
+
expect(cfg).to flow_between("b", "2")
|
41
|
+
expect(cfg).to flow_between("2", "c = 1")
|
42
|
+
expect(cfg).to flow_between("2", "d = 1")
|
43
|
+
expect(cfg).to flow_between("c = 1", "z = 1")
|
44
|
+
expect(cfg).to flow_between("d = 1", "z = 1")
|
45
|
+
end
|
46
|
+
|
47
|
+
it "should show control flowing from test to action and to next test" do
|
48
|
+
cfg = CFG.from_source("a = 1;" \
|
49
|
+
"case b; when 2; c = 1; when 3; d = 1; else; e = 1; end;" \
|
50
|
+
"z = 1")
|
51
|
+
|
52
|
+
expect(cfg).to contain("a = 1", "b", "2", "c = 1", "3", "d = 1", "e = 1", "z = 1")
|
53
|
+
|
54
|
+
expect(cfg).to flow_between("a = 1", "b")
|
55
|
+
expect(cfg).to flow_between("b", "2")
|
56
|
+
expect(cfg).to flow_between("2", "c = 1")
|
57
|
+
expect(cfg).to flow_between("2", "3")
|
58
|
+
expect(cfg).to flow_between("3", "d = 1")
|
59
|
+
expect(cfg).to flow_between("3", "e = 1")
|
60
|
+
expect(cfg).to flow_between("c = 1", "z = 1")
|
61
|
+
expect(cfg).to flow_between("d = 1", "z = 1")
|
62
|
+
expect(cfg).to flow_between("e = 1", "z = 1")
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
@@ -3,21 +3,13 @@ require "rdg/cfg"
|
|
3
3
|
module RDG
|
4
4
|
describe CFG do
|
5
5
|
context "for simple if expressions" do
|
6
|
-
it "should show control flowing from predicate
|
6
|
+
it "should show control flowing from predicate through and around consequence" do
|
7
7
|
cfg = CFG.from_source("a = 1; if b == 1 then; c = 1; end; z = 1")
|
8
8
|
|
9
9
|
expect(cfg).to contain("a = 1", "b == 1", "c = 1", "z = 1")
|
10
10
|
|
11
11
|
expect(cfg).to flow_between("a = 1", "b == 1")
|
12
12
|
expect(cfg).to flow_between("b == 1", "z = 1")
|
13
|
-
end
|
14
|
-
|
15
|
-
it "should show control flowing into predicate and through consequence" do
|
16
|
-
cfg = CFG.from_source("a = 1; if b == 1 then; c = 1; end; z = 1")
|
17
|
-
|
18
|
-
expect(cfg).to contain("a = 1", "b == 1", "c = 1", "z = 1")
|
19
|
-
|
20
|
-
expect(cfg).to flow_between("a = 1", "b == 1")
|
21
13
|
expect(cfg).to flow_between("b == 1", "c = 1")
|
22
14
|
expect(cfg).to flow_between("c = 1", "z = 1")
|
23
15
|
end
|
@@ -33,6 +25,17 @@ module RDG
|
|
33
25
|
expect(cfg).to flow_between("d = 1", "e = 1")
|
34
26
|
expect(cfg).to flow_between("e = 1", "z = 1")
|
35
27
|
end
|
28
|
+
|
29
|
+
it "should show control flowing through and around predicate of modifier if" do
|
30
|
+
cfg = CFG.from_source("a = 1; c = 1 if b == 1; z = 1")
|
31
|
+
|
32
|
+
expect(cfg).to contain("a = 1", "b == 1", "c = 1", "z = 1")
|
33
|
+
|
34
|
+
expect(cfg).to flow_between("a = 1", "b == 1")
|
35
|
+
expect(cfg).to flow_between("b == 1", "z = 1")
|
36
|
+
expect(cfg).to flow_between("b == 1", "c = 1")
|
37
|
+
expect(cfg).to flow_between("c = 1", "z = 1")
|
38
|
+
end
|
36
39
|
end
|
37
40
|
|
38
41
|
context "for if expressions with several consequences" do
|
@@ -64,6 +67,18 @@ module RDG
|
|
64
67
|
expect(cfg).to flow_between("d = 1", "z = 1")
|
65
68
|
expect(cfg).to flow_between("e = 1", "z = 1")
|
66
69
|
end
|
70
|
+
|
71
|
+
it "should show control flowing from predicate to both consequences of ternary" do
|
72
|
+
cfg = CFG.from_source("a = 1; b == 1 ? c = 1 : d = 1; z = 1")
|
73
|
+
|
74
|
+
expect(cfg).to contain("a = 1", "b == 1", "c = 1", "d = 1", "z = 1")
|
75
|
+
|
76
|
+
expect(cfg).to flow_between("a = 1", "b == 1")
|
77
|
+
expect(cfg).to flow_between("b == 1", "c = 1")
|
78
|
+
expect(cfg).to flow_between("b == 1", "d = 1")
|
79
|
+
expect(cfg).to flow_between("c = 1", "z = 1")
|
80
|
+
expect(cfg).to flow_between("d = 1", "z = 1")
|
81
|
+
end
|
67
82
|
end
|
68
83
|
end
|
69
84
|
end
|
@@ -0,0 +1,53 @@
|
|
1
|
+
require "rdg/cfg"
|
2
|
+
|
3
|
+
module RDG
|
4
|
+
describe CFG do
|
5
|
+
context "for unless expressions" do
|
6
|
+
it "should show control flowing from predicate through and around consequence" do
|
7
|
+
cfg = CFG.from_source("a = 1; unless b == 1 then; c = 1; end; z = 1")
|
8
|
+
|
9
|
+
expect(cfg).to contain("a = 1", "b == 1", "c = 1", "z = 1")
|
10
|
+
|
11
|
+
expect(cfg).to flow_between("a = 1", "b == 1")
|
12
|
+
expect(cfg).to flow_between("b == 1", "z = 1")
|
13
|
+
expect(cfg).to flow_between("b == 1", "c = 1")
|
14
|
+
expect(cfg).to flow_between("c = 1", "z = 1")
|
15
|
+
end
|
16
|
+
|
17
|
+
it "should show control flowing into predicate and through longer consequence" do
|
18
|
+
cfg = CFG.from_source("a = 1; unless b == 1 then; c = 1; d = 1; e = 1; end; z = 1")
|
19
|
+
|
20
|
+
expect(cfg).to contain("a = 1", "b == 1", "c = 1", "d = 1", "e = 1", "z = 1")
|
21
|
+
|
22
|
+
expect(cfg).to flow_between("a = 1", "b == 1")
|
23
|
+
expect(cfg).to flow_between("b == 1", "c = 1")
|
24
|
+
expect(cfg).to flow_between("c = 1", "d = 1")
|
25
|
+
expect(cfg).to flow_between("d = 1", "e = 1")
|
26
|
+
expect(cfg).to flow_between("e = 1", "z = 1")
|
27
|
+
end
|
28
|
+
|
29
|
+
it "should show control flowing through and around predicate of modifier unless" do
|
30
|
+
cfg = CFG.from_source("a = 1; c = 1 unless b == 1; z = 1")
|
31
|
+
|
32
|
+
expect(cfg).to contain("a = 1", "b == 1", "c = 1", "z = 1")
|
33
|
+
|
34
|
+
expect(cfg).to flow_between("a = 1", "b == 1")
|
35
|
+
expect(cfg).to flow_between("b == 1", "z = 1")
|
36
|
+
expect(cfg).to flow_between("b == 1", "c = 1")
|
37
|
+
expect(cfg).to flow_between("c = 1", "z = 1")
|
38
|
+
end
|
39
|
+
|
40
|
+
it "should show control flowing from predicate to (and out of) all consequences" do
|
41
|
+
cfg = CFG.from_source("a = 1; unless b == 1 then; c = 1; else; d = 1; end; z = 1")
|
42
|
+
|
43
|
+
expect(cfg).to contain("a = 1", "b == 1", "c = 1", "d = 1", "z = 1")
|
44
|
+
|
45
|
+
expect(cfg).to flow_between("a = 1", "b == 1")
|
46
|
+
expect(cfg).to flow_between("b == 1", "c = 1")
|
47
|
+
expect(cfg).to flow_between("b == 1", "d = 1")
|
48
|
+
expect(cfg).to flow_between("c = 1", "z = 1")
|
49
|
+
expect(cfg).to flow_between("d = 1", "z = 1")
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
@@ -0,0 +1,131 @@
|
|
1
|
+
require "rdg/cfg"
|
2
|
+
|
3
|
+
module RDG
|
4
|
+
describe CFG do
|
5
|
+
["begin"].each do |block_type|
|
6
|
+
context "for a #{block_type} block with exception handlers" do
|
7
|
+
it "should show control flow from every child to the start of every handler" do
|
8
|
+
cfg = CFG.from_source(
|
9
|
+
"#{block_type}; a = 1; b = 1; rescue Err; y = 1; rescue; z = 1; end"
|
10
|
+
)
|
11
|
+
|
12
|
+
expect(cfg).to contain("a = 1", "b = 1", "y = 1", "z = 1")
|
13
|
+
|
14
|
+
expect(cfg).to flow_between("a = 1", "y = 1")
|
15
|
+
expect(cfg).to flow_between("b = 1", "y = 1")
|
16
|
+
expect(cfg).to flow_between("a = 1", "z = 1")
|
17
|
+
expect(cfg).to flow_between("b = 1", "z = 1")
|
18
|
+
end
|
19
|
+
|
20
|
+
it "should show control flowing into the first child" do
|
21
|
+
cfg = CFG.from_source(
|
22
|
+
"a = 1; #{block_type}; b = 1; rescue; z = 1; end"
|
23
|
+
)
|
24
|
+
|
25
|
+
expect(cfg).to contain("a = 1", "b = 1", "z = 1")
|
26
|
+
|
27
|
+
expect(cfg).to flow_between("a = 1", "b = 1")
|
28
|
+
end
|
29
|
+
|
30
|
+
it "should show control flowing out of the last child and the handlers to successor" do
|
31
|
+
cfg = CFG.from_source(
|
32
|
+
"#{block_type}; a = 1; b = 1; rescue Err; w = 1; x = 1; rescue; y = 1; end; z = 1"
|
33
|
+
)
|
34
|
+
|
35
|
+
expect(cfg).to contain("a = 1", "b = 1", "w = 1", "x = 1", "y = 1", "z = 1")
|
36
|
+
|
37
|
+
expect(cfg).to flow_between("b = 1", "z = 1")
|
38
|
+
expect(cfg).to flow_between("x = 1", "z = 1")
|
39
|
+
expect(cfg).to flow_between("y = 1", "z = 1")
|
40
|
+
end
|
41
|
+
|
42
|
+
it "should show control flowing out from out of last child through an else" do
|
43
|
+
cfg = CFG.from_source(
|
44
|
+
"#{block_type}; a = 1; b = 1; rescue Err; w = 1; x = 1; else; y = 1; end; z = 1"
|
45
|
+
)
|
46
|
+
|
47
|
+
expect(cfg).to contain("a = 1", "b = 1", "x = 1", "w = 1", "y = 1", "z = 1")
|
48
|
+
|
49
|
+
expect(cfg).to flow_between("b = 1", "y = 1")
|
50
|
+
expect(cfg).to flow_between("y = 1", "z = 1")
|
51
|
+
|
52
|
+
expect(cfg).not_to flow_between("b = 1", "z = 1")
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
context "for a #{block_type} with an ensure" do
|
57
|
+
it "should show control flow from every child to the start of an ensure" do
|
58
|
+
cfg = CFG.from_source(
|
59
|
+
"#{block_type}; a = 1; b = 1; ensure; z = 1; end; final = 1"
|
60
|
+
)
|
61
|
+
|
62
|
+
expect(cfg).to contain("a = 1", "b = 1", "z = 1", "final = 1")
|
63
|
+
|
64
|
+
expect(cfg).to flow_between("a = 1", "z = 1")
|
65
|
+
expect(cfg).to flow_between("b = 1", "z = 1")
|
66
|
+
|
67
|
+
expect(cfg).to flow_between("z = 1", "final = 1")
|
68
|
+
expect(cfg).not_to flow_between("b = 1", "final = 1")
|
69
|
+
end
|
70
|
+
|
71
|
+
it "should show control flow from every child incl. rescues to the start of an ensure" do
|
72
|
+
cfg = CFG.from_source(
|
73
|
+
"#{block_type}; a = 1; b = 1;" \
|
74
|
+
"rescue Err; v = 1;" \
|
75
|
+
"rescue; w = 1; y = 1;" \
|
76
|
+
"ensure; z = 1; end;" \
|
77
|
+
"final = 1"
|
78
|
+
)
|
79
|
+
|
80
|
+
expect(cfg).to contain("a = 1", "b = 1", "v = 1", "w = 1", "y = 1", "z = 1", "final = 1")
|
81
|
+
|
82
|
+
expect(cfg).to flow_between("a = 1", "z = 1")
|
83
|
+
expect(cfg).to flow_between("b = 1", "z = 1")
|
84
|
+
expect(cfg).to flow_between("w = 1", "z = 1")
|
85
|
+
expect(cfg).to flow_between("y = 1", "z = 1")
|
86
|
+
|
87
|
+
expect(cfg).to flow_between("z = 1", "final = 1")
|
88
|
+
expect(cfg).not_to flow_between("b = 1", "final = 1")
|
89
|
+
expect(cfg).not_to flow_between("v = 1", "final = 1")
|
90
|
+
expect(cfg).not_to flow_between("y = 1", "final = 1")
|
91
|
+
end
|
92
|
+
|
93
|
+
it "should show control flow from last child to ensure via else" do
|
94
|
+
cfg = CFG.from_source(
|
95
|
+
"#{block_type}; a = 1; b = 1;" \
|
96
|
+
"rescue; v = 1;" \
|
97
|
+
"else; w = 1; y = 1;" \
|
98
|
+
"ensure; z = 1; end;" \
|
99
|
+
"final = 1"
|
100
|
+
)
|
101
|
+
|
102
|
+
expect(cfg).to contain("a = 1", "b = 1", "v = 1", "w = 1", "y = 1", "z = 1", "final = 1")
|
103
|
+
|
104
|
+
expect(cfg).to flow_between("b = 1", "w = 1")
|
105
|
+
expect(cfg).to flow_between("y = 1", "z = 1")
|
106
|
+
expect(cfg).to flow_between("z = 1", "final = 1")
|
107
|
+
|
108
|
+
# b = 1 could raise, not be rescued and flow directly to ensure
|
109
|
+
expect(cfg).to flow_between("b = 1", "z = 1")
|
110
|
+
expect(cfg).not_to flow_between("b = 1", "final = 1")
|
111
|
+
end
|
112
|
+
end
|
113
|
+
|
114
|
+
context "retrying" do
|
115
|
+
it "should return control to the start of the #{block_type}" do
|
116
|
+
cfg = CFG.from_source(
|
117
|
+
"#{block_type}; a = 1; rescue; retry; end; z = 1"
|
118
|
+
)
|
119
|
+
|
120
|
+
expect(cfg).to contain("a = 1", "retry", "z = 1")
|
121
|
+
|
122
|
+
expect(cfg).to flow_between("a = 1", "z = 1")
|
123
|
+
expect(cfg).to flow_between("a = 1", "retry")
|
124
|
+
expect(cfg).to flow_between("retry", "a = 1")
|
125
|
+
|
126
|
+
expect(cfg).not_to flow_between("retry", "z = 1")
|
127
|
+
end
|
128
|
+
end
|
129
|
+
end
|
130
|
+
end
|
131
|
+
end
|
@@ -0,0 +1,90 @@
|
|
1
|
+
require "rdg/cfg"
|
2
|
+
|
3
|
+
module RDG
|
4
|
+
describe CFG do
|
5
|
+
context "for simple loop control expressions" do
|
6
|
+
it "should show control flowing from break directly to successor" do
|
7
|
+
cfg = CFG.from_source("a = 1; while true do; a += 1; break; b += 1; end; z = 1")
|
8
|
+
|
9
|
+
expect(cfg).to contain("a = 1", "true", "a += 1", "break", "b += 1", "z = 1")
|
10
|
+
|
11
|
+
expect(cfg).to flow_between("a = 1", "true")
|
12
|
+
expect(cfg).to flow_between("true", "a += 1")
|
13
|
+
expect(cfg).to flow_between("true", "z = 1")
|
14
|
+
expect(cfg).to flow_between("a += 1", "break")
|
15
|
+
expect(cfg).to flow_between("break", "z = 1")
|
16
|
+
expect(cfg).to flow_between("b += 1", "true")
|
17
|
+
|
18
|
+
expect(cfg).not_to flow_between("break", "a += 1")
|
19
|
+
end
|
20
|
+
|
21
|
+
it "should show control flowing from nested break only to successor" do
|
22
|
+
cfg = CFG.from_source("a = 1; while true do; a += 1; break if false; b += 1; end; z = 1")
|
23
|
+
|
24
|
+
expect(cfg).to contain("a = 1", "true", "a += 1", "break", "false", "b += 1", "z = 1")
|
25
|
+
|
26
|
+
expect(cfg).to flow_between("a = 1", "true")
|
27
|
+
expect(cfg).to flow_between("true", "a += 1")
|
28
|
+
expect(cfg).to flow_between("true", "z = 1")
|
29
|
+
expect(cfg).to flow_between("a += 1", "false")
|
30
|
+
expect(cfg).to flow_between("false", "break")
|
31
|
+
expect(cfg).to flow_between("false", "b += 1")
|
32
|
+
expect(cfg).to flow_between("break", "z = 1")
|
33
|
+
expect(cfg).to flow_between("b += 1", "true")
|
34
|
+
|
35
|
+
expect(cfg).not_to flow_between("break", "a += 1")
|
36
|
+
end
|
37
|
+
|
38
|
+
%w(next redo).each do |kind|
|
39
|
+
it "should show control flowing from #{kind} directly to test" do
|
40
|
+
cfg = CFG.from_source("a = 1; while true do; a += 1; #{kind}; b += 1; end; z = 1")
|
41
|
+
|
42
|
+
expect(cfg).to contain("a = 1", "true", "a += 1", "#{kind}", "b += 1", "z = 1")
|
43
|
+
|
44
|
+
expect(cfg).to flow_between("a = 1", "true")
|
45
|
+
expect(cfg).to flow_between("true", "a += 1")
|
46
|
+
expect(cfg).to flow_between("true", "z = 1")
|
47
|
+
expect(cfg).to flow_between("a += 1", "#{kind}")
|
48
|
+
expect(cfg).to flow_between("#{kind}", "true")
|
49
|
+
expect(cfg).to flow_between("b += 1", "true")
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
context "for nested loop control expressions" do
|
55
|
+
it "should show control flowing from break to inner loop's successor" do
|
56
|
+
cfg = CFG.from_source("while false do;" \
|
57
|
+
"a = 1; while true do; a += 1; break; b += 1; end; z = 1;" \
|
58
|
+
"end;" \
|
59
|
+
"final = 1")
|
60
|
+
|
61
|
+
expect(cfg).to flow_between("break", "z = 1")
|
62
|
+
expect(cfg).not_to flow_between("break", "final = 1")
|
63
|
+
end
|
64
|
+
|
65
|
+
%w(next redo).each do |kind|
|
66
|
+
it "should show control flowing from #{kind} to inner loop's test" do
|
67
|
+
cfg = CFG.from_source("while false do;" \
|
68
|
+
"a = 1; while true do; a += 1; #{kind}; b += 1; end; z = 1;" \
|
69
|
+
"end;" \
|
70
|
+
"final = 1")
|
71
|
+
|
72
|
+
expect(cfg).to flow_between("#{kind}", "true")
|
73
|
+
expect(cfg).not_to flow_between("#{kind}", "false")
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
context "for complex loop test" do
|
79
|
+
%w(next redo).each do |kind|
|
80
|
+
it "should show control flow from #{kind} to children of inner loop's test" do
|
81
|
+
cfg = CFG.from_source("a = 1;" \
|
82
|
+
"while 42 ? :foo : 'bar' do; a += 1; #{kind}; b += 1; end;" \
|
83
|
+
"z = 1;")
|
84
|
+
|
85
|
+
expect(cfg).to flow_between("#{kind}", "42")
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|
90
|
+
end
|