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,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
|