rdg 0.0.2 → 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- 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,33 @@
|
|
1
|
+
require "rdg/control/ensure"
|
2
|
+
|
3
|
+
module RDG
|
4
|
+
module Control
|
5
|
+
describe Ensure do
|
6
|
+
let(:registry) { spy('registry') }
|
7
|
+
let(:context) { Analysis::Context.new(spy('graph'), spy('equivalences'), registry) }
|
8
|
+
|
9
|
+
let(:main_ast) { FakeAst.new(:if) }
|
10
|
+
let(:ensure_ast) { FakeAst.new(:send) }
|
11
|
+
let(:ast) { FakeAst.new(:rescue, children: [main_ast, ensure_ast]) }
|
12
|
+
subject { Ensure.new(ast, context) }
|
13
|
+
|
14
|
+
it "should have control flow start at the main block" do
|
15
|
+
expect(subject.start_node).to eq(main_ast)
|
16
|
+
end
|
17
|
+
|
18
|
+
it "should have control flow end at the ensure block" do
|
19
|
+
expect(subject.end_nodes).to eq([ensure_ast])
|
20
|
+
end
|
21
|
+
|
22
|
+
it "should have control flow from main block to ensure block" do
|
23
|
+
expect(subject.internal_flow_edges).to eq([[main_ast, ensure_ast]])
|
24
|
+
end
|
25
|
+
|
26
|
+
it "should prepend Handler so that edges back to main_ast are created for ensure block" do
|
27
|
+
subject.analyse
|
28
|
+
|
29
|
+
expect(registry).to have_received(:prepend_for).with(ensure_ast, Handler)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
require "rdg/control/for"
|
2
|
+
|
3
|
+
module RDG
|
4
|
+
module Control
|
5
|
+
describe For do
|
6
|
+
let(:ast) { FakeAst.new(:for, children: [:iterator, :iterable, :body]) }
|
7
|
+
subject { For.new(ast) }
|
8
|
+
|
9
|
+
it "should have control flow start at the iterable object" do
|
10
|
+
expect(subject.start_node).to eq(:iterable)
|
11
|
+
end
|
12
|
+
|
13
|
+
it "should have control flow end at the iterable object" do
|
14
|
+
expect(subject.end_nodes).to eq(%i(iterable))
|
15
|
+
end
|
16
|
+
|
17
|
+
it "should have control flow edges between iterable and body, and vice-versa" do
|
18
|
+
expect(subject.internal_flow_edges).to eq([%i(iterable body), %i(body iterable)])
|
19
|
+
end
|
20
|
+
|
21
|
+
it "should have control flow nodes only for iterable and body, and not for iterator" do
|
22
|
+
expect(subject.nodes).to eq(%i(iterable body))
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
require "rdg/control/handler"
|
2
|
+
|
3
|
+
module RDG
|
4
|
+
module Control
|
5
|
+
describe Handler do
|
6
|
+
let(:graph) { spy("graph") }
|
7
|
+
let(:equivalences) { spy("equivalences") }
|
8
|
+
let(:context) { Analysis::Context.new(graph, equivalences) }
|
9
|
+
|
10
|
+
let(:block_ast) { FakeAst.new(:rescue, children: [:rescuable]) }
|
11
|
+
let(:ast) { FakeAst.new(:resbody, ancestors: [block_ast]) }
|
12
|
+
|
13
|
+
subject { Handler.new(ast, context) }
|
14
|
+
|
15
|
+
it "should add edges from main to handler" do
|
16
|
+
allow(equivalences).to receive(:all).with(:rescuable).and_return(
|
17
|
+
%i(first_rescuable second_rescuable)
|
18
|
+
)
|
19
|
+
|
20
|
+
subject.analyse
|
21
|
+
|
22
|
+
expect(graph).to have_received(:add_edge).with(:first_rescuable, ast)
|
23
|
+
expect(graph).to have_received(:add_edge).with(:second_rescuable, ast)
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -4,14 +4,11 @@ module RDG
|
|
4
4
|
module Control
|
5
5
|
describe If do
|
6
6
|
context "without any alternatives" do
|
7
|
-
|
8
|
-
|
9
|
-
allow(ast).to receive(:children) { [:predicate, :consequence, :""] }
|
10
|
-
If.new(ast, nil, nil)
|
11
|
-
end
|
7
|
+
let(:ast) { FakeAst.new(:if, children: [:predicate, :consequence, :""]) }
|
8
|
+
subject { If.new(ast) }
|
12
9
|
|
13
10
|
it "should have control flow start at the predicate" do
|
14
|
-
expect(subject.
|
11
|
+
expect(subject.start_node).to eq(:predicate)
|
15
12
|
end
|
16
13
|
|
17
14
|
it "should have control flow end at the predicate and the consequence" do
|
@@ -23,15 +20,29 @@ module RDG
|
|
23
20
|
end
|
24
21
|
end
|
25
22
|
|
26
|
-
context "
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
23
|
+
context "without any consequence (i.e., the `parser` gem's representation of unless)" do
|
24
|
+
let(:ast) { FakeAst.new(:if, children: [:predicate, :"", :alternative]) }
|
25
|
+
subject { If.new(ast) }
|
26
|
+
|
27
|
+
it "should have control flow start at the predicate" do
|
28
|
+
expect(subject.start_node).to eq(:predicate)
|
29
|
+
end
|
30
|
+
|
31
|
+
it "should have control flow end at the predicate and the alternative" do
|
32
|
+
expect(subject.end_nodes).to eq([:predicate, :alternative])
|
31
33
|
end
|
32
34
|
|
35
|
+
it "should have control flow edge between predicate and alternative" do
|
36
|
+
expect(subject.internal_flow_edges).to eq([[:predicate, :alternative]])
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
context "with one alternative" do
|
41
|
+
let(:ast) { FakeAst.new(:if, children: [:predicate, :consequence, :alternative]) }
|
42
|
+
subject { If.new(ast) }
|
43
|
+
|
33
44
|
it "should have control flow start at the predicate" do
|
34
|
-
expect(subject.
|
45
|
+
expect(subject.start_node).to eq(:predicate)
|
35
46
|
end
|
36
47
|
|
37
48
|
it "should have control flow end at the consequences" do
|
@@ -47,14 +58,11 @@ module RDG
|
|
47
58
|
end
|
48
59
|
|
49
60
|
context "with several alternatives" do
|
50
|
-
|
51
|
-
|
52
|
-
allow(ast).to receive(:children) { [:predicate, :consequence, :a1, :a2, :a3] }
|
53
|
-
If.new(ast, nil, nil)
|
54
|
-
end
|
61
|
+
let(:ast) { FakeAst.new(:if, children: [:predicate, :consequence, :a1, :a2, :a3]) }
|
62
|
+
subject { If.new(ast) }
|
55
63
|
|
56
64
|
it "should have control flow start at the predicate" do
|
57
|
-
expect(subject.
|
65
|
+
expect(subject.start_node).to eq(:predicate)
|
58
66
|
end
|
59
67
|
|
60
68
|
it "should have control flow end at the consequences" do
|
@@ -0,0 +1,43 @@
|
|
1
|
+
require "rdg/control/jump"
|
2
|
+
|
3
|
+
module RDG
|
4
|
+
module Control
|
5
|
+
describe Jump do
|
6
|
+
let(:graph) { spy("graph") }
|
7
|
+
let(:block_ast) { FakeAst.new(:while) }
|
8
|
+
let(:ast) { FakeAst.new(:thing, ancestors: [block_ast]) }
|
9
|
+
|
10
|
+
subject do
|
11
|
+
class DummyJump < Jump
|
12
|
+
def new_successors
|
13
|
+
%i(first_new_successor second_new_successor)
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
DummyJump.new(ast, graph)
|
18
|
+
end
|
19
|
+
|
20
|
+
it "should remove any edges to existing successors" do
|
21
|
+
allow(graph).to receive(:each_successor).and_yield(:successor)
|
22
|
+
subject.analyse
|
23
|
+
|
24
|
+
expect(graph).to have_received(:remove_edge).with(ast, :successor)
|
25
|
+
end
|
26
|
+
|
27
|
+
it "should add an edge from jump to each new successor" do
|
28
|
+
subject.analyse
|
29
|
+
|
30
|
+
expect(graph).to have_received(:add_edge).with(ast, :first_new_successor)
|
31
|
+
expect(graph).to have_received(:add_edge).with(ast, :second_new_successor)
|
32
|
+
end
|
33
|
+
|
34
|
+
it "should do nothing when not contained within a block" do
|
35
|
+
ast.ancestors = []
|
36
|
+
subject.analyse
|
37
|
+
|
38
|
+
expect(graph).not_to have_received(:add_edge)
|
39
|
+
expect(graph).not_to have_received(:remove_edge)
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
require "rdg/control/jump_to_start"
|
2
|
+
|
3
|
+
module RDG
|
4
|
+
module Control
|
5
|
+
describe JumpToStart do
|
6
|
+
let(:graph) { spy("graph") }
|
7
|
+
let(:equivalences) { spy("equivalences") }
|
8
|
+
let(:context) { Analysis::Context.new(graph, equivalences) }
|
9
|
+
|
10
|
+
let(:block_ast) { FakeAst.new(:while, children: [:jumpable]) }
|
11
|
+
let(:ast) { FakeAst.new(:thing, ancestors: [block_ast]) }
|
12
|
+
|
13
|
+
subject { JumpToStart.new(ast, context) }
|
14
|
+
|
15
|
+
it "should use equivalents of block's first child as new successors" do
|
16
|
+
allow(equivalences).to receive(:first).with(:jumpable).and_return(:new_successor)
|
17
|
+
|
18
|
+
expect(subject.new_successors).to eq([:new_successor])
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
require "rdg/control/rescue_body"
|
2
|
+
|
3
|
+
module RDG
|
4
|
+
module Control
|
5
|
+
describe RescueBody do
|
6
|
+
let(:ast) { FakeAst.new(:resbody, children: [:ts, :e, 1, 2, 3]) }
|
7
|
+
subject { RescueBody.new(ast) }
|
8
|
+
|
9
|
+
it "should have control flow start at the first child" do
|
10
|
+
expect(subject.start_node).to eq(1)
|
11
|
+
end
|
12
|
+
|
13
|
+
it "should have control flow end at the last child" do
|
14
|
+
expect(subject.end_nodes).to eq([3])
|
15
|
+
end
|
16
|
+
|
17
|
+
it "should have control flow edges between each pair of children" do
|
18
|
+
expect(subject.internal_flow_edges).to eq([[1, 2], [2, 3]])
|
19
|
+
end
|
20
|
+
|
21
|
+
it "should have control flow nodes only for statements, and not for ex types or variable" do
|
22
|
+
expect(subject.nodes).to eq([1, 2, 3])
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,62 @@
|
|
1
|
+
require "rdg/control/rescue"
|
2
|
+
|
3
|
+
module RDG
|
4
|
+
module Control
|
5
|
+
describe Rescue do
|
6
|
+
let(:registry) { spy('registry') }
|
7
|
+
let(:context) { Analysis::Context.new(spy('graph'), spy('equivalences'), registry) }
|
8
|
+
|
9
|
+
let(:main_ast) { FakeAst.new(:if) }
|
10
|
+
let(:handler1) { FakeAst.new(:send) }
|
11
|
+
let(:handler2) { FakeAst.new(:raise) }
|
12
|
+
|
13
|
+
context "rescue without an else" do
|
14
|
+
let(:ast) { FakeAst.new(:rescue, children: [main_ast, handler1, handler2, :""]) }
|
15
|
+
subject { Rescue.new(ast, context) }
|
16
|
+
|
17
|
+
it "should have control flow start at the main block" do
|
18
|
+
expect(subject.start_node).to eq(main_ast)
|
19
|
+
end
|
20
|
+
|
21
|
+
it "should have control flow end at the main block and each fallback" do
|
22
|
+
expect(subject.end_nodes).to eq([main_ast, handler1, handler2])
|
23
|
+
end
|
24
|
+
|
25
|
+
it "should have no control flow edges (yet)" do
|
26
|
+
expect(subject.internal_flow_edges).to eq([])
|
27
|
+
end
|
28
|
+
|
29
|
+
it "should prepend Handler so that edges back to main_ast are created for each handler" do
|
30
|
+
subject.analyse
|
31
|
+
|
32
|
+
expect(registry).to have_received(:prepend_for).with(handler1, Handler)
|
33
|
+
expect(registry).to have_received(:prepend_for).with(handler2, Handler)
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
context "rescue with an else" do
|
38
|
+
let(:ast) { FakeAst.new(:rescue, children: [main_ast, handler1, handler2, :else]) }
|
39
|
+
subject { Rescue.new(ast, context) }
|
40
|
+
|
41
|
+
it "should have control flow start at the main block" do
|
42
|
+
expect(subject.start_node).to eq(main_ast)
|
43
|
+
end
|
44
|
+
|
45
|
+
it "should have control flow end at each fallback and the else" do
|
46
|
+
expect(subject.end_nodes).to eq([handler1, handler2, :else])
|
47
|
+
end
|
48
|
+
|
49
|
+
it "should have control flow from the main block to the else and nothing more (yet)" do
|
50
|
+
expect(subject.internal_flow_edges).to eq([[main_ast, :else]])
|
51
|
+
end
|
52
|
+
|
53
|
+
it "should prepend Handler so that edges back to main_ast are created for each handler" do
|
54
|
+
subject.analyse
|
55
|
+
|
56
|
+
expect(registry).to have_received(:prepend_for).with(handler1, Handler)
|
57
|
+
expect(registry).to have_received(:prepend_for).with(handler2, Handler)
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
@@ -4,8 +4,7 @@ module RDG
|
|
4
4
|
module Control
|
5
5
|
describe Return do
|
6
6
|
let(:graph) { spy("graph") }
|
7
|
-
|
8
|
-
subject { Return.new(:return, graph, state) }
|
7
|
+
subject { Return.new(:return, graph) }
|
9
8
|
|
10
9
|
it "should remove existing edges out of return node" do
|
11
10
|
allow(graph).to receive(:each_successor).and_yield(:successor)
|
@@ -14,14 +13,6 @@ module RDG
|
|
14
13
|
|
15
14
|
expect(graph).to have_received(:remove_edge).with(:return, :successor)
|
16
15
|
end
|
17
|
-
|
18
|
-
it "should do nothing if there is no current method" do
|
19
|
-
state.delete(:current_method)
|
20
|
-
subject.analyse
|
21
|
-
|
22
|
-
expect(graph).not_to have_received(:add_edge)
|
23
|
-
expect(graph).not_to have_received(:remove_edge)
|
24
|
-
end
|
25
16
|
end
|
26
17
|
end
|
27
18
|
end
|
@@ -0,0 +1,59 @@
|
|
1
|
+
require "rdg/control/when"
|
2
|
+
|
3
|
+
module RDG
|
4
|
+
module Control
|
5
|
+
describe When do
|
6
|
+
let(:ast) { FakeAst.new(:when, children: [:test, :action]) }
|
7
|
+
let(:graph) { spy("graph") }
|
8
|
+
subject { When.new(ast, graph) }
|
9
|
+
|
10
|
+
context "two successors" do
|
11
|
+
before(:each) do
|
12
|
+
allow(graph).to receive(:each_successor).with(ast) { [:next_when, :successor] }
|
13
|
+
end
|
14
|
+
|
15
|
+
it "should have control flow start at the expression" do
|
16
|
+
expect(subject.start_node).to eq(:test)
|
17
|
+
end
|
18
|
+
|
19
|
+
it "should have control flow edge between test and action" do
|
20
|
+
expect(subject.internal_flow_edges).to eq([[:test, :action]])
|
21
|
+
end
|
22
|
+
|
23
|
+
it "should propagate outgoing flow from test to next when" do
|
24
|
+
subject.propogate_outgoing_flow
|
25
|
+
expect(graph).to have_received(:add_edge).with(:test, :next_when)
|
26
|
+
end
|
27
|
+
|
28
|
+
it "should propagate outgoing flow from action to successor" do
|
29
|
+
subject.propogate_outgoing_flow
|
30
|
+
expect(graph).to have_received(:add_edge).with(:action, :successor)
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
context "one successor" do
|
35
|
+
before(:each) do
|
36
|
+
allow(graph).to receive(:each_successor).with(ast) { [:successor] }
|
37
|
+
end
|
38
|
+
|
39
|
+
it "should have control flow start at the expression" do
|
40
|
+
expect(subject.start_node).to eq(:test)
|
41
|
+
end
|
42
|
+
|
43
|
+
it "should have control flow edge between test and action" do
|
44
|
+
expect(subject.internal_flow_edges).to eq([[:test, :action]])
|
45
|
+
end
|
46
|
+
|
47
|
+
it "should propagate outgoing flow from test to successor" do
|
48
|
+
subject.propogate_outgoing_flow
|
49
|
+
expect(graph).to have_received(:add_edge).with(:test, :successor)
|
50
|
+
end
|
51
|
+
|
52
|
+
it "should propagate outgoing flow from action to successor" do
|
53
|
+
subject.propogate_outgoing_flow
|
54
|
+
expect(graph).to have_received(:add_edge).with(:action, :successor)
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
@@ -0,0 +1,49 @@
|
|
1
|
+
require "rdg/graph/bidirected_adjacency_graph"
|
2
|
+
|
3
|
+
module RDG
|
4
|
+
module Graph
|
5
|
+
describe BidirectedAdjacencyGraph do
|
6
|
+
subject do
|
7
|
+
BidirectedAdjacencyGraph[
|
8
|
+
:f, :b, # F
|
9
|
+
:b, :a, # / \
|
10
|
+
:b, :d, # B G
|
11
|
+
:d, :c, # / \ / \
|
12
|
+
:d, :e, # A D I
|
13
|
+
:f, :g, # / \ /
|
14
|
+
:g, :i, # C E H
|
15
|
+
:i, :h, #
|
16
|
+
:g, :d # NB: edges are directed downwards
|
17
|
+
]
|
18
|
+
end
|
19
|
+
|
20
|
+
context "successors" do
|
21
|
+
it "should yield a list of successors" do
|
22
|
+
expect { |block| subject.each_successor(:b, &block) }.to yield_successive_args(:a, :d)
|
23
|
+
end
|
24
|
+
|
25
|
+
it "should not yield for a leaf" do
|
26
|
+
expect { |block| subject.each_successor(:c, &block) }.not_to yield_control
|
27
|
+
end
|
28
|
+
|
29
|
+
it "should not yield for an unknown node" do
|
30
|
+
expect { |block| subject.each_successor(:unknown, &block) }.not_to yield_control
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
context "predecessors" do
|
35
|
+
it "should yield a list of predecessors" do
|
36
|
+
expect { |block| subject.each_predecessor(:d, &block) }.to yield_successive_args(:b, :g)
|
37
|
+
end
|
38
|
+
|
39
|
+
it "should not yield for a root" do
|
40
|
+
expect { |block| subject.each_predecessor(:f, &block) }.not_to yield_control
|
41
|
+
end
|
42
|
+
|
43
|
+
it "should not yield for an unknown node" do
|
44
|
+
expect { |block| subject.each_predecessor(:unknown, &block) }.not_to yield_control
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|