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.
Files changed (43) hide show
  1. checksums.yaml +4 -4
  2. data/config/version.rb +1 -2
  3. data/lib/origen_testers.rb +2 -1
  4. data/lib/origen_testers/atp.rb +95 -0
  5. data/lib/origen_testers/atp/ast/extractor.rb +26 -0
  6. data/lib/origen_testers/atp/ast/node.rb +147 -0
  7. data/lib/origen_testers/atp/flow.rb +920 -0
  8. data/lib/origen_testers/atp/flow_api.rb +56 -0
  9. data/lib/origen_testers/atp/formatter.rb +25 -0
  10. data/lib/origen_testers/atp/formatters/basic.rb +32 -0
  11. data/lib/origen_testers/atp/formatters/datalog.rb +65 -0
  12. data/lib/origen_testers/atp/parser.rb +26 -0
  13. data/lib/origen_testers/atp/processor.rb +73 -0
  14. data/lib/origen_testers/atp/processors/add_ids.rb +45 -0
  15. data/lib/origen_testers/atp/processors/add_set_result.rb +22 -0
  16. data/lib/origen_testers/atp/processors/adjacent_if_combiner.rb +100 -0
  17. data/lib/origen_testers/atp/processors/append_to.rb +27 -0
  18. data/lib/origen_testers/atp/processors/apply_post_group_actions.rb +50 -0
  19. data/lib/origen_testers/atp/processors/condition.rb +179 -0
  20. data/lib/origen_testers/atp/processors/continue_implementer.rb +35 -0
  21. data/lib/origen_testers/atp/processors/else_remover.rb +31 -0
  22. data/lib/origen_testers/atp/processors/empty_branch_remover.rb +17 -0
  23. data/lib/origen_testers/atp/processors/extract_set_flags.rb +18 -0
  24. data/lib/origen_testers/atp/processors/flag_optimizer.rb +234 -0
  25. data/lib/origen_testers/atp/processors/flattener.rb +58 -0
  26. data/lib/origen_testers/atp/processors/flow_id.rb +46 -0
  27. data/lib/origen_testers/atp/processors/marshal.rb +33 -0
  28. data/lib/origen_testers/atp/processors/on_pass_fail_remover.rb +39 -0
  29. data/lib/origen_testers/atp/processors/one_flag_per_test.rb +79 -0
  30. data/lib/origen_testers/atp/processors/pre_cleaner.rb +65 -0
  31. data/lib/origen_testers/atp/processors/redundant_condition_remover.rb +28 -0
  32. data/lib/origen_testers/atp/processors/relationship.rb +199 -0
  33. data/lib/origen_testers/atp/processors/sub_flow_remover.rb +10 -0
  34. data/lib/origen_testers/atp/program.rb +48 -0
  35. data/lib/origen_testers/atp/runner.rb +234 -0
  36. data/lib/origen_testers/atp/validator.rb +53 -0
  37. data/lib/origen_testers/atp/validators/condition.rb +4 -0
  38. data/lib/origen_testers/atp/validators/duplicate_ids.rb +32 -0
  39. data/lib/origen_testers/atp/validators/flags.rb +59 -0
  40. data/lib/origen_testers/atp/validators/jobs.rb +55 -0
  41. data/lib/origen_testers/atp/validators/missing_ids.rb +63 -0
  42. data/lib/origen_testers/atp_deprecation.rb +2 -0
  43. 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