origen_testers 0.21.0 → 0.30.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/config/version.rb +1 -2
- data/lib/origen_testers.rb +2 -1
- data/lib/origen_testers/atp.rb +95 -0
- data/lib/origen_testers/atp/ast/extractor.rb +26 -0
- data/lib/origen_testers/atp/ast/node.rb +147 -0
- data/lib/origen_testers/atp/flow.rb +920 -0
- data/lib/origen_testers/atp/flow_api.rb +56 -0
- data/lib/origen_testers/atp/formatter.rb +25 -0
- data/lib/origen_testers/atp/formatters/basic.rb +32 -0
- data/lib/origen_testers/atp/formatters/datalog.rb +65 -0
- data/lib/origen_testers/atp/parser.rb +26 -0
- data/lib/origen_testers/atp/processor.rb +73 -0
- data/lib/origen_testers/atp/processors/add_ids.rb +45 -0
- data/lib/origen_testers/atp/processors/add_set_result.rb +22 -0
- data/lib/origen_testers/atp/processors/adjacent_if_combiner.rb +100 -0
- data/lib/origen_testers/atp/processors/append_to.rb +27 -0
- data/lib/origen_testers/atp/processors/apply_post_group_actions.rb +50 -0
- data/lib/origen_testers/atp/processors/condition.rb +179 -0
- data/lib/origen_testers/atp/processors/continue_implementer.rb +35 -0
- data/lib/origen_testers/atp/processors/else_remover.rb +31 -0
- data/lib/origen_testers/atp/processors/empty_branch_remover.rb +17 -0
- data/lib/origen_testers/atp/processors/extract_set_flags.rb +18 -0
- data/lib/origen_testers/atp/processors/flag_optimizer.rb +234 -0
- data/lib/origen_testers/atp/processors/flattener.rb +58 -0
- data/lib/origen_testers/atp/processors/flow_id.rb +46 -0
- data/lib/origen_testers/atp/processors/marshal.rb +33 -0
- data/lib/origen_testers/atp/processors/on_pass_fail_remover.rb +39 -0
- data/lib/origen_testers/atp/processors/one_flag_per_test.rb +79 -0
- data/lib/origen_testers/atp/processors/pre_cleaner.rb +65 -0
- data/lib/origen_testers/atp/processors/redundant_condition_remover.rb +28 -0
- data/lib/origen_testers/atp/processors/relationship.rb +199 -0
- data/lib/origen_testers/atp/processors/sub_flow_remover.rb +10 -0
- data/lib/origen_testers/atp/program.rb +48 -0
- data/lib/origen_testers/atp/runner.rb +234 -0
- data/lib/origen_testers/atp/validator.rb +53 -0
- data/lib/origen_testers/atp/validators/condition.rb +4 -0
- data/lib/origen_testers/atp/validators/duplicate_ids.rb +32 -0
- data/lib/origen_testers/atp/validators/flags.rb +59 -0
- data/lib/origen_testers/atp/validators/jobs.rb +55 -0
- data/lib/origen_testers/atp/validators/missing_ids.rb +63 -0
- data/lib/origen_testers/atp_deprecation.rb +2 -0
- metadata +62 -15
@@ -0,0 +1,27 @@
|
|
1
|
+
module OrigenTesters::ATP
|
2
|
+
module Processors
|
3
|
+
# Appends the given node to the node with the given ID, if it exists
|
4
|
+
# somewhere within the given parent node
|
5
|
+
class AppendTo < Processor
|
6
|
+
def run(parent, node, id, options = {})
|
7
|
+
@to_be_appended = node
|
8
|
+
@id_of_to_be_appended_to = id
|
9
|
+
@found = false
|
10
|
+
process(parent)
|
11
|
+
end
|
12
|
+
|
13
|
+
def succeeded?
|
14
|
+
@found
|
15
|
+
end
|
16
|
+
|
17
|
+
def handler_missing(node)
|
18
|
+
if node.id == @id_of_to_be_appended_to
|
19
|
+
@found = true
|
20
|
+
node.updated(nil, node.children + [@to_be_appended])
|
21
|
+
else
|
22
|
+
node.updated(nil, process_all(node.children))
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,50 @@
|
|
1
|
+
module OrigenTesters::ATP
|
2
|
+
module Processors
|
3
|
+
# This removes on_pass/fail operations from groups and applies them to all
|
4
|
+
# contained tests
|
5
|
+
class ApplyPostGroupActions < Processor
|
6
|
+
def run(node)
|
7
|
+
@on_pass = []
|
8
|
+
@on_fail = []
|
9
|
+
process(node)
|
10
|
+
end
|
11
|
+
|
12
|
+
def on_group(node)
|
13
|
+
on_pass = node.find(:on_pass)
|
14
|
+
on_fail = node.find(:on_fail)
|
15
|
+
@on_pass << on_pass
|
16
|
+
@on_fail << on_fail
|
17
|
+
node = node.remove(on_pass) if on_pass
|
18
|
+
node = node.remove(on_fail) if on_fail
|
19
|
+
node = node.updated(nil, process_all(node.children))
|
20
|
+
@on_fail.pop
|
21
|
+
@on_pass.pop
|
22
|
+
node
|
23
|
+
end
|
24
|
+
|
25
|
+
def on_test(node)
|
26
|
+
node = node.ensure_node_present(:on_pass) if @on_pass.any? { |n| n }
|
27
|
+
node = node.ensure_node_present(:on_fail) if @on_fail.any? { |n| n }
|
28
|
+
node.updated(nil, process_all(node.children))
|
29
|
+
end
|
30
|
+
|
31
|
+
def on_on_pass(node)
|
32
|
+
@on_pass.each do |on_pass|
|
33
|
+
if on_pass
|
34
|
+
node = node.updated(nil, node.children + process_all(on_pass.children))
|
35
|
+
end
|
36
|
+
end
|
37
|
+
node
|
38
|
+
end
|
39
|
+
|
40
|
+
def on_on_fail(node)
|
41
|
+
@on_fail.each do |on_fail|
|
42
|
+
if on_fail
|
43
|
+
node = node.updated(nil, node.children + process_all(on_fail.children))
|
44
|
+
end
|
45
|
+
end
|
46
|
+
node
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
@@ -0,0 +1,179 @@
|
|
1
|
+
module OrigenTesters::ATP
|
2
|
+
module Processors
|
3
|
+
# This optimizes the condition nodes such that any adjacent flow nodes that
|
4
|
+
# have the same condition, will be grouped together under a single condition
|
5
|
+
# wrapper.
|
6
|
+
#
|
7
|
+
# For example this AST:
|
8
|
+
#
|
9
|
+
# (flow
|
10
|
+
# (group
|
11
|
+
# (name "g1")
|
12
|
+
# (test
|
13
|
+
# (name "test1"))
|
14
|
+
# (flow-flag "bitmap" true
|
15
|
+
# (test
|
16
|
+
# (name "test2"))))
|
17
|
+
# (flow-flag "bitmap" true
|
18
|
+
# (group
|
19
|
+
# (name "g1")
|
20
|
+
# (flow-flag "x" true
|
21
|
+
# (test
|
22
|
+
# (name "test3")))
|
23
|
+
# (flow-flag "y" true
|
24
|
+
# (flow-flag "x" true
|
25
|
+
# (test
|
26
|
+
# (name "test4")))))))
|
27
|
+
#
|
28
|
+
# Will be optimized to this:
|
29
|
+
#
|
30
|
+
# (flow
|
31
|
+
# (group
|
32
|
+
# (name "g1")
|
33
|
+
# (test
|
34
|
+
# (name "test1"))
|
35
|
+
# (flow-flag "bitmap" true
|
36
|
+
# (test
|
37
|
+
# (name "test2"))
|
38
|
+
# (flow-flag "x" true
|
39
|
+
# (test
|
40
|
+
# (name "test3"))
|
41
|
+
# (flow-flag "y" true
|
42
|
+
# (test
|
43
|
+
# (name "test4")))))))
|
44
|
+
#
|
45
|
+
class Condition < Processor
|
46
|
+
def on_flow(node)
|
47
|
+
extract_volatiles(node)
|
48
|
+
node.updated(nil, optimize(process_all(node.children)))
|
49
|
+
end
|
50
|
+
|
51
|
+
def on_sub_flow(node)
|
52
|
+
node.updated(nil, optimize(process_all(node.children)))
|
53
|
+
end
|
54
|
+
|
55
|
+
def on_group(node)
|
56
|
+
name, *nodes = *node
|
57
|
+
if conditions_to_remove.any? { |c| node.type == c.type && c.to_a == [name] }
|
58
|
+
conditions_to_remove << node.updated(nil, [name])
|
59
|
+
result = node.updated(:inline, optimize(process_all(nodes)))
|
60
|
+
conditions_to_remove.pop
|
61
|
+
else
|
62
|
+
conditions_to_remove << node.updated(nil, [name])
|
63
|
+
result = node.updated(nil, [name] + optimize(process_all(nodes)))
|
64
|
+
conditions_to_remove.pop
|
65
|
+
end
|
66
|
+
result
|
67
|
+
end
|
68
|
+
|
69
|
+
def on_condition_node(node)
|
70
|
+
flag, *nodes = *node
|
71
|
+
if conditions_to_remove.any? { |c| node.type == c.type && c.to_a == [flag] }
|
72
|
+
if volatile?(flag)
|
73
|
+
result = node.updated(:inline, optimize(process_all(nodes)))
|
74
|
+
else
|
75
|
+
# This ensures any duplicate conditions matching the current one get removed
|
76
|
+
conditions_to_remove << node.updated(nil, [flag])
|
77
|
+
result = node.updated(:inline, optimize(process_all(nodes)))
|
78
|
+
conditions_to_remove.pop
|
79
|
+
end
|
80
|
+
else
|
81
|
+
if volatile?(flag)
|
82
|
+
result = node.updated(nil, [flag] + optimize(process_all(nodes)))
|
83
|
+
else
|
84
|
+
conditions_to_remove << node.updated(nil, [flag])
|
85
|
+
result = node.updated(nil, [flag] + optimize(process_all(nodes)))
|
86
|
+
conditions_to_remove.pop
|
87
|
+
end
|
88
|
+
end
|
89
|
+
result
|
90
|
+
end
|
91
|
+
OrigenTesters::ATP::Flow::CONDITION_NODE_TYPES.each do |type|
|
92
|
+
alias_method "on_#{type}", :on_condition_node unless method_defined?("on_#{type}")
|
93
|
+
end
|
94
|
+
|
95
|
+
def optimize(nodes)
|
96
|
+
results = []
|
97
|
+
node1 = nil
|
98
|
+
nodes.each do |node2|
|
99
|
+
if node1
|
100
|
+
if can_be_combined?(node1, node2)
|
101
|
+
node1 = process(combine(node1, node2))
|
102
|
+
else
|
103
|
+
results << node1
|
104
|
+
node1 = node2
|
105
|
+
end
|
106
|
+
else
|
107
|
+
node1 = node2
|
108
|
+
end
|
109
|
+
end
|
110
|
+
results << node1 if node1
|
111
|
+
results
|
112
|
+
end
|
113
|
+
|
114
|
+
def can_be_combined?(node1, node2)
|
115
|
+
if condition_node?(node1) && condition_node?(node2)
|
116
|
+
!(conditions(node1) & conditions(node2)).empty?
|
117
|
+
else
|
118
|
+
false
|
119
|
+
end
|
120
|
+
end
|
121
|
+
|
122
|
+
def condition_node?(node)
|
123
|
+
# [:flow_flag, :run_flag, :test_result, :group, :job, :test_executed].include?(node.type)
|
124
|
+
node.respond_to?(:type) && OrigenTesters::ATP::Flow::CONDITION_KEYS[node.type]
|
125
|
+
end
|
126
|
+
|
127
|
+
def combine(node1, node2)
|
128
|
+
common = conditions(node1) & conditions(node2)
|
129
|
+
common.each { |condition| conditions_to_remove << condition }
|
130
|
+
node1 = process(node1)
|
131
|
+
node1 = [node1] unless node1.is_a?(Array)
|
132
|
+
node2 = process(node2)
|
133
|
+
node2 = [node2] unless node2.is_a?(Array)
|
134
|
+
common.size.times { conditions_to_remove.pop }
|
135
|
+
|
136
|
+
node = nil
|
137
|
+
common.reverse_each do |condition|
|
138
|
+
if node
|
139
|
+
node = condition.updated(nil, condition.children + [node])
|
140
|
+
else
|
141
|
+
node = condition.updated(nil, condition.children + node1 + node2)
|
142
|
+
end
|
143
|
+
end
|
144
|
+
node
|
145
|
+
end
|
146
|
+
|
147
|
+
def conditions(node)
|
148
|
+
result = []
|
149
|
+
# if [:flow_flag, :run_flag].include?(node.type)
|
150
|
+
if [:if_enabled, :unless_enabled, :if_flag, :unless_flag].include?(node.type)
|
151
|
+
flag, *children = *node
|
152
|
+
unless volatile?(flag)
|
153
|
+
result << node.updated(nil, [flag])
|
154
|
+
end
|
155
|
+
result += conditions(children.first) if children.first && children.size == 1
|
156
|
+
# elsif [:test_result, :job, :test_executed].include?(node.type)
|
157
|
+
elsif node.type == :group
|
158
|
+
name, *children = *node
|
159
|
+
# Sometimes a group can have an ID
|
160
|
+
if children.first.try(:type) == :id
|
161
|
+
result << node.updated(nil, [name, children.shift])
|
162
|
+
else
|
163
|
+
result << node.updated(nil, [name])
|
164
|
+
end
|
165
|
+
result += conditions(children.first) if children.first && children.size == 1
|
166
|
+
elsif OrigenTesters::ATP::Flow::CONDITION_NODE_TYPES.include?(node.type)
|
167
|
+
flag, *children = *node
|
168
|
+
result << node.updated(nil, [flag])
|
169
|
+
result += conditions(children.first) if children.first && children.size == 1
|
170
|
+
end
|
171
|
+
result
|
172
|
+
end
|
173
|
+
|
174
|
+
def conditions_to_remove
|
175
|
+
@conditions_to_remove ||= []
|
176
|
+
end
|
177
|
+
end
|
178
|
+
end
|
179
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
module OrigenTesters::ATP
|
2
|
+
module Processors
|
3
|
+
# Implements continue on a fail branch for V93K by removing any bin nodes that are
|
4
|
+
# siblings of continue nodes. The continue nodes are also removed in the process since
|
5
|
+
# they have now served their function.
|
6
|
+
class ContinueImplementer < OrigenTesters::ATP::Processor
|
7
|
+
# Delete any on-fail child if it's 'empty'
|
8
|
+
def on_on_fail(node)
|
9
|
+
if cont = node.find(:continue) || @continue
|
10
|
+
node = node.updated(nil, node.children - [cont] - node.find_all(:set_result))
|
11
|
+
end
|
12
|
+
node.updated(nil, process_all(node.children))
|
13
|
+
end
|
14
|
+
|
15
|
+
def on_group(node)
|
16
|
+
f = node.find(:on_fail)
|
17
|
+
if f && f.find(:continue)
|
18
|
+
with_continue do
|
19
|
+
node = node.updated(nil, process_all(node.children))
|
20
|
+
end
|
21
|
+
node
|
22
|
+
else
|
23
|
+
node.updated(nil, process_all(node.children))
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
def with_continue
|
28
|
+
orig = @continue
|
29
|
+
@continue = true
|
30
|
+
yield
|
31
|
+
@continue = orig
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
module OrigenTesters::ATP
|
2
|
+
module Processors
|
3
|
+
# Removes embedded else nodes and converts them to the equivalent inverse condition
|
4
|
+
# node at the same level as the parent node
|
5
|
+
class ElseRemover < Processor
|
6
|
+
def run(node)
|
7
|
+
process(node)
|
8
|
+
end
|
9
|
+
|
10
|
+
def on_condition_node(node)
|
11
|
+
if e = node.find(:else)
|
12
|
+
n1 = node.remove(e)
|
13
|
+
if node.type.to_s =~ /if_/
|
14
|
+
type = node.type.to_s.sub('if_', 'unless_').to_sym
|
15
|
+
elsif node.type.to_s =~ /unless_/
|
16
|
+
type = node.type.to_s.sub('unless_', 'if_').to_sym
|
17
|
+
else
|
18
|
+
fail "Don't know how to inverse: #{node.type}"
|
19
|
+
end
|
20
|
+
n2 = e.updated(type, [n1.to_a[0]] + e.children)
|
21
|
+
node.updated(:inline, [n1, n2])
|
22
|
+
else
|
23
|
+
node.updated(nil, process_all(node.children))
|
24
|
+
end
|
25
|
+
end
|
26
|
+
OrigenTesters::ATP::Flow::CONDITION_NODE_TYPES.each do |type|
|
27
|
+
alias_method "on_#{type}", :on_condition_node unless method_defined?("on_#{type}")
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
module OrigenTesters::ATP
|
2
|
+
module Processors
|
3
|
+
# Removes any empty on_pass and on_fail branches
|
4
|
+
class EmptyBranchRemover < Processor
|
5
|
+
# Delete any on-fail child if it's 'empty'
|
6
|
+
def on_test(node)
|
7
|
+
if on_pass = node.find(:on_pass)
|
8
|
+
node = node.remove(on_pass) if on_pass.children.empty?
|
9
|
+
end
|
10
|
+
if on_fail = node.find(:on_fail)
|
11
|
+
node = node.remove(on_fail) if on_fail.children.empty?
|
12
|
+
end
|
13
|
+
node = node.updated(nil, process_all(node.children))
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
module OrigenTesters::ATP
|
2
|
+
module Processors
|
3
|
+
# Extracts all flags which are set within the given flow, returning
|
4
|
+
# them in an array
|
5
|
+
class ExtractSetFlags < OrigenTesters::ATP::Processor
|
6
|
+
def run(nodes)
|
7
|
+
@results = []
|
8
|
+
process_all(nodes)
|
9
|
+
@results.uniq
|
10
|
+
end
|
11
|
+
|
12
|
+
def on_set_flag(node)
|
13
|
+
flag = node.value
|
14
|
+
@results << flag
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,234 @@
|
|
1
|
+
module OrigenTesters::ATP
|
2
|
+
module Processors
|
3
|
+
# This processor eliminates the use of run flags between adjacent tests:
|
4
|
+
#
|
5
|
+
# s(:flow,
|
6
|
+
# s(:name, "prb1"),
|
7
|
+
# s(:test,
|
8
|
+
# s(:name, "test1"),
|
9
|
+
# s(:id, "t1"),
|
10
|
+
# s(:on_fail,
|
11
|
+
# s(:set_flag, "t1_FAILED", "auto_generated"),
|
12
|
+
# s(:continue))),
|
13
|
+
# s(:if_flag, "t1_FAILED",
|
14
|
+
# s(:test,
|
15
|
+
# s(:name, "test2"))))
|
16
|
+
#
|
17
|
+
#
|
18
|
+
# s(:flow,
|
19
|
+
# s(:name, "prb1"),
|
20
|
+
# s(:test,
|
21
|
+
# s(:name, "test1"),
|
22
|
+
# s(:id, "t1"),
|
23
|
+
# s(:on_fail,
|
24
|
+
# s(:test,
|
25
|
+
# s(:name, "test2")))))
|
26
|
+
#
|
27
|
+
class FlagOptimizer < Processor
|
28
|
+
attr_reader :run_flag_table, :optimize_when_continue
|
29
|
+
|
30
|
+
class ExtractRunFlagTable < Processor
|
31
|
+
# Hash table of run_flag name with number of times used
|
32
|
+
attr_reader :run_flag_table
|
33
|
+
|
34
|
+
# Reset hash table
|
35
|
+
def initialize
|
36
|
+
@run_flag_table = {}.with_indifferent_access
|
37
|
+
end
|
38
|
+
|
39
|
+
# For run_flag nodes, increment # of occurrences for specified flag
|
40
|
+
def on_if_flag(node)
|
41
|
+
children = node.children.dup
|
42
|
+
names = children.shift
|
43
|
+
state = node.type == :if_flag
|
44
|
+
Array(names).each do |name|
|
45
|
+
if @run_flag_table[name.to_sym].nil?
|
46
|
+
@run_flag_table[name.to_sym] = 1
|
47
|
+
else
|
48
|
+
@run_flag_table[name.to_sym] += 1
|
49
|
+
end
|
50
|
+
end
|
51
|
+
process_all(node.children)
|
52
|
+
end
|
53
|
+
alias_method :on_unless_flag, :on_if_flag
|
54
|
+
end
|
55
|
+
|
56
|
+
def run(node, options = {})
|
57
|
+
options = {
|
58
|
+
optimize_when_continue: true
|
59
|
+
}.merge(options)
|
60
|
+
@optimize_when_continue = options[:optimize_when_continue]
|
61
|
+
# Pre-process the AST for # of occurrences of each run-flag used
|
62
|
+
t = ExtractRunFlagTable.new
|
63
|
+
t.process(node)
|
64
|
+
@run_flag_table = t.run_flag_table
|
65
|
+
extract_volatiles(node)
|
66
|
+
process(node)
|
67
|
+
end
|
68
|
+
|
69
|
+
def on_named_collection(node)
|
70
|
+
name, *nodes = *node
|
71
|
+
node.updated(nil, [name] + optimize(process_all(nodes)))
|
72
|
+
end
|
73
|
+
alias_method :on_flow, :on_named_collection
|
74
|
+
alias_method :on_group, :on_named_collection
|
75
|
+
alias_method :on_unless_flag, :on_named_collection
|
76
|
+
alias_method :on_sub_flow, :on_named_collection
|
77
|
+
|
78
|
+
def on_unnamed_collection(node)
|
79
|
+
node.updated(nil, optimize(process_all(node.children)))
|
80
|
+
end
|
81
|
+
alias_method :on_else, :on_unnamed_collection
|
82
|
+
|
83
|
+
def on_whenever(node)
|
84
|
+
name, *nodes = *node
|
85
|
+
node.updated(nil, [name] + optimize(process_all(nodes)))
|
86
|
+
end
|
87
|
+
alias_method :on_whenever_all, :on_whenever
|
88
|
+
alias_method :on_whenever_any, :on_whenever
|
89
|
+
|
90
|
+
def on_if_flag(node)
|
91
|
+
name, *nodes = *node
|
92
|
+
# Remove this node and return its children if required
|
93
|
+
if if_run_flag_to_remove.last == node.to_a[0]
|
94
|
+
node.updated(:inline, optimize(process_all(node.to_a[1..-1])))
|
95
|
+
else
|
96
|
+
node.updated(nil, [name] + optimize(process_all(nodes)))
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
def on_on_fail(node)
|
101
|
+
if to_inline = nodes_to_inline_on_pass_or_fail.last
|
102
|
+
# If this node sets the flag that gates the node to be inlined
|
103
|
+
set_flag = node.find(:set_flag)
|
104
|
+
if set_flag && gated_by_set?(set_flag.to_a[0], to_inline)
|
105
|
+
# Remove the sub-node that sets the flag if there are no further references to it
|
106
|
+
|
107
|
+
if @run_flag_table[set_flag.to_a[0]] == 1 || !@run_flag_table[set_flag.to_a[0]]
|
108
|
+
node = node.updated(nil, node.children - [set_flag])
|
109
|
+
end
|
110
|
+
|
111
|
+
# And append the content of the node to be in_lined at the end of this on pass/fail node
|
112
|
+
append = reorder_nested_run_flags(set_flag.to_a[0], to_inline).to_a[1..-1]
|
113
|
+
|
114
|
+
# Belt and braces approach to make sure this node to be inlined does
|
115
|
+
# not get picked up anywhere else
|
116
|
+
nodes_to_inline_on_pass_or_fail.pop
|
117
|
+
nodes_to_inline_on_pass_or_fail << nil
|
118
|
+
end
|
119
|
+
end
|
120
|
+
node.updated(nil, optimize(process_all(node.children + Array(append))))
|
121
|
+
end
|
122
|
+
alias_method :on_on_pass, :on_on_fail
|
123
|
+
|
124
|
+
def optimize(nodes)
|
125
|
+
results = []
|
126
|
+
node1 = nil
|
127
|
+
nodes.each do |node2|
|
128
|
+
if node1
|
129
|
+
if can_be_combined?(node1, node2)
|
130
|
+
node1 = combine(node1, node2)
|
131
|
+
else
|
132
|
+
results << node1
|
133
|
+
node1 = node2
|
134
|
+
end
|
135
|
+
else
|
136
|
+
node1 = node2
|
137
|
+
end
|
138
|
+
end
|
139
|
+
results << node1 if node1
|
140
|
+
results
|
141
|
+
end
|
142
|
+
|
143
|
+
def can_be_combined?(node1, node2)
|
144
|
+
if (node1.type == :test || node1.type == :sub_flow) && (node2.type == :if_flag || node2.type == :unless_flag) &&
|
145
|
+
# Don't optimize tests which are marked as continue if told not to
|
146
|
+
!(node1.find(:on_fail) && node1.find(:on_fail).find(:continue) && !optimize_when_continue)
|
147
|
+
|
148
|
+
if node1.find_all(:on_fail, :on_pass).any? do |node|
|
149
|
+
if n = node.find(:set_flag)
|
150
|
+
# Inline instead of setting a flag if...
|
151
|
+
gated_by_set?(n.to_a[0], node2) && # The flag set by node1 is gating node2
|
152
|
+
n.to_a[1] == 'auto_generated' && # The flag has been generated and not specified by the user
|
153
|
+
n.to_a[0] !~ /_RAN$/ && # And don't compress RAN flags because they can be set by both on_fail and on_pass
|
154
|
+
!volatile?(n.to_a[0]) # And make sure the flag has not been marked as volatile
|
155
|
+
end
|
156
|
+
end
|
157
|
+
return true
|
158
|
+
end
|
159
|
+
end
|
160
|
+
false
|
161
|
+
end
|
162
|
+
|
163
|
+
def combine(node1, node2)
|
164
|
+
nodes_to_inline_on_pass_or_fail << node2 # .updated(nil, process_all(node2.children))
|
165
|
+
node1 = node1.updated(nil, process_all(node1.children))
|
166
|
+
nodes_to_inline_on_pass_or_fail.pop
|
167
|
+
node1
|
168
|
+
end
|
169
|
+
|
170
|
+
# node will always be an if_flag or unless_flag type node, guaranteed by the caller
|
171
|
+
#
|
172
|
+
# Returns true if flag matches the one supplied
|
173
|
+
#
|
174
|
+
# s(:if_flag, flag,
|
175
|
+
# s(:test, ...
|
176
|
+
#
|
177
|
+
# Also returns true if flag matches the one supplied, but it is nested within other flag conditions:
|
178
|
+
#
|
179
|
+
# s(:unless_flag, other_flag,
|
180
|
+
# s(:if_flag, other_flag2,
|
181
|
+
# s(:if_flag, flag,
|
182
|
+
# s(:test, ...
|
183
|
+
def gated_by_set?(flag, node)
|
184
|
+
(flag == node.to_a[0] && node.type == :if_flag) ||
|
185
|
+
(node.to_a.size == 2 && (node.to_a.last.type == :if_flag || node.to_a.last.type == :unless_flag) && gated_by_set?(flag, node.to_a.last))
|
186
|
+
end
|
187
|
+
|
188
|
+
# Returns the node with the run_flag clauses re-ordered to have the given flag of interest at the top.
|
189
|
+
#
|
190
|
+
# The caller guarantees the run_flag clause containing the given flag is present.
|
191
|
+
#
|
192
|
+
# For example, given this node:
|
193
|
+
#
|
194
|
+
# s(:unless_flag, "flag1",
|
195
|
+
# s(:if_flag, "ot_BEA7F3B_FAILED",
|
196
|
+
# s(:test,
|
197
|
+
# s(:object, <TestSuite: inner_test1_BEA7F3B>),
|
198
|
+
# s(:name, "inner_test1_BEA7F3B"),
|
199
|
+
# s(:number, 0),
|
200
|
+
# s(:id, "it1_BEA7F3B"),
|
201
|
+
# s(:on_fail,
|
202
|
+
# s(:render, "multi_bin;")))))
|
203
|
+
#
|
204
|
+
# Then this node would be returned when the flag of interest is ot_BEA7F3B_FAILED:
|
205
|
+
#
|
206
|
+
# s(:if_flag, "ot_BEA7F3B_FAILED",
|
207
|
+
# s(:unless_flag, "flag1",
|
208
|
+
# s(:test,
|
209
|
+
# s(:object, <TestSuite: inner_test1_BEA7F3B>),
|
210
|
+
# s(:name, "inner_test1_BEA7F3B"),
|
211
|
+
# s(:number, 0),
|
212
|
+
# s(:id, "it1_BEA7F3B"),
|
213
|
+
# s(:on_fail,
|
214
|
+
# s(:render, "multi_bin;")))))
|
215
|
+
def reorder_nested_run_flags(flag, node)
|
216
|
+
# If the run_flag we care about is already at the top, just return node
|
217
|
+
unless node.to_a[0] == flag && node.type == :if_flag
|
218
|
+
if_run_flag_to_remove << flag
|
219
|
+
node = node.updated(:if_flag, [flag] + [process(node)])
|
220
|
+
if_run_flag_to_remove.pop
|
221
|
+
end
|
222
|
+
node
|
223
|
+
end
|
224
|
+
|
225
|
+
def if_run_flag_to_remove
|
226
|
+
@if_run_flag_to_remove ||= []
|
227
|
+
end
|
228
|
+
|
229
|
+
def nodes_to_inline_on_pass_or_fail
|
230
|
+
@nodes_to_inline_on_pass_or_fail ||= []
|
231
|
+
end
|
232
|
+
end
|
233
|
+
end
|
234
|
+
end
|