origen_testers 0.21.0 → 0.30.0

Sign up to get free protection for your applications and to get access to all the features.
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,58 @@
1
+ module OrigenTesters::ATP
2
+ module Processors
3
+ # Gives every node their own individual wrapping of condition nodes. No attempt is made
4
+ # to identify or remove duplicate conditions in the wrapping, that will be done later by
5
+ # the RedundantConditionRemover.
6
+ class Flattener < Processor
7
+ def run(node)
8
+ @results = [[]]
9
+ @conditions = []
10
+ process(node)
11
+ node.updated(:flow, results)
12
+ end
13
+
14
+ def on_flow(node)
15
+ process_all(node.children)
16
+ end
17
+
18
+ # Handles the top-level flow nodes
19
+ def on_volatile(node)
20
+ results << node
21
+ end
22
+ alias_method :on_name, :on_volatile
23
+ alias_method :on_id, :on_volatile
24
+
25
+ def on_group(node)
26
+ @results << []
27
+ process_all(node.children)
28
+ nodes = @results.pop
29
+ results << node.updated(nil, nodes)
30
+ end
31
+
32
+ def on_condition_node(node)
33
+ flag, *nodes = *node
34
+ @conditions << node.updated(node.type, [flag])
35
+ process_all(nodes)
36
+ @conditions.pop
37
+ end
38
+ OrigenTesters::ATP::Flow::CONDITION_NODE_TYPES.each do |type|
39
+ alias_method "on_#{type}", :on_condition_node unless method_defined?("on_#{type}")
40
+ end
41
+
42
+ def handler_missing(node)
43
+ results << wrap_with_current_conditions(node)
44
+ end
45
+
46
+ def wrap_with_current_conditions(node)
47
+ @conditions.reverse_each do |condition|
48
+ node = condition.updated(nil, condition.children + [node])
49
+ end
50
+ node
51
+ end
52
+
53
+ def results
54
+ @results.last
55
+ end
56
+ end
57
+ end
58
+ end
@@ -0,0 +1,46 @@
1
+ module OrigenTesters::ATP
2
+ module Processors
3
+ # Adds the flow ID to all ids and label names
4
+ class FlowID < Processor
5
+ attr_reader :id
6
+
7
+ def run(node, id)
8
+ @id = id
9
+ process(node)
10
+ end
11
+
12
+ def on_id(node)
13
+ if node.value =~ /^extern/
14
+ node
15
+ else
16
+ node.updated(nil, ["#{node.value}_#{id}"])
17
+ end
18
+ end
19
+
20
+ def on_if_failed(node)
21
+ tid, *nodes = *node
22
+ if tid.is_a?(Array)
23
+ tid = tid.map do |tid|
24
+ if tid =~ /^extern/
25
+ tid
26
+ else
27
+ "#{tid}_#{id}"
28
+ end
29
+ end
30
+ else
31
+ if tid !~ /^extern/
32
+ tid = "#{tid}_#{id}"
33
+ end
34
+ end
35
+ node.updated(nil, [tid] + process_all(nodes))
36
+ end
37
+ alias_method :on_if_any_failed, :on_if_failed
38
+ alias_method :on_if_all_failed, :on_if_failed
39
+ alias_method :on_if_passed, :on_if_failed
40
+ alias_method :on_if_any_passed, :on_if_failed
41
+ alias_method :on_if_all_passed, :on_if_failed
42
+ alias_method :on_if_ran, :on_if_failed
43
+ alias_method :on_unless_ran, :on_if_failed
44
+ end
45
+ end
46
+ end
@@ -0,0 +1,33 @@
1
+ module OrigenTesters::ATP
2
+ module Processors
3
+ # Makes the AST safe for Marshaling
4
+ class Marshal < Processor
5
+ def on_object(node)
6
+ o = node.value
7
+ if o.is_a?(String)
8
+ meta = { 'Test' => o }
9
+ elsif o.is_a?(Hash)
10
+ meta = o
11
+ elsif o.respond_to?(:to_meta) && o.to_meta && !o.to_meta.empty?
12
+ meta = o.to_meta
13
+ else
14
+ meta = {}
15
+ end
16
+ # The test suite / test instance name
17
+ meta['Test'] ||= o.try(:name)
18
+ meta['Pattern'] ||= o.try(:pattern)
19
+ # The test name column on IG-XL, or the name of a specific instance of a test which shares a common
20
+ # 'Test' name with other tests
21
+ meta['Test Name'] ||= o.try(:test_name) || o.try(:_test_name) || o.try('TestName') || meta['Test']
22
+ # The name of the primary test that is logged by the test instance / test method, if it logs more
23
+ # than one then this is represented by sub_test nodes
24
+ meta['Sub Test Name'] ||= o.try(:sub_test_name) || o.try('SubTestName') || meta['Test']
25
+ node.updated(nil, [meta])
26
+ end
27
+
28
+ def on_render(node)
29
+ node.updated(nil, [node.value.to_s])
30
+ end
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,39 @@
1
+ module OrigenTesters::ATP
2
+ module Processors
3
+ # Removes most things from embedded on_pass/fail nodes and converts them to the equivalent
4
+ # on_passed/failed condition at the same level as the parent node
5
+ class OnPassFailRemover < Processor
6
+ def run(node)
7
+ process(node)
8
+ end
9
+
10
+ def on_test(node)
11
+ on_pass = node.find(:on_pass)
12
+ on_fail = node.find(:on_fail)
13
+ if on_pass || on_fail
14
+ id = node.find(:id)
15
+ unless id
16
+ fail 'Something has gone wrong, all nodes should have IDs by this point'
17
+ end
18
+ id = id.value
19
+ nodes = [node]
20
+ if on_fail && contains_anything_interesting?(on_fail)
21
+ nodes << node.updated(:if_failed, [id] + on_fail.children)
22
+ nodes[0] = nodes[0].remove(on_fail)
23
+ end
24
+ if on_pass && contains_anything_interesting?(on_pass)
25
+ nodes << node.updated(:if_passed, [id] + on_pass.children)
26
+ nodes[0] = nodes[0].remove(on_pass)
27
+ end
28
+ node.updated(:inline, nodes)
29
+ else
30
+ node.updated(nil, process_all(node.children))
31
+ end
32
+ end
33
+
34
+ def contains_anything_interesting?(node)
35
+ node.children.any? { |n| n.type != :set_result && n.type != :continue && n.type != :set_flag }
36
+ end
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,79 @@
1
+ module OrigenTesters::ATP
2
+ module Processors
3
+ # Ensures that all test nodes only ever set a flag once
4
+ class OneFlagPerTest < Processor
5
+ def run(node)
6
+ @build_table = true
7
+ @pass_table = {}
8
+ @fail_table = {}
9
+ process(node)
10
+ @counters = {}
11
+ @pass_table.each { |f, v| @counters[f] = 0 }
12
+ @fail_table.each { |f, v| @counters[f] = 0 }
13
+ @build_table = false
14
+ process(node)
15
+ end
16
+
17
+ def on_test(node)
18
+ on_pass = node.find(:on_pass)
19
+ on_fail = node.find(:on_fail)
20
+ if @build_table
21
+ if on_fail
22
+ on_fail.find_all(:set_flag).each do |n|
23
+ @fail_table[n.to_a[0]] ||= 0
24
+ @fail_table[n.to_a[0]] += 1
25
+ end
26
+ end
27
+ if on_pass
28
+ on_pass.find_all(:set_flag).each do |n|
29
+ @pass_table[n.to_a[0]] ||= 0
30
+ @pass_table[n.to_a[0]] += 1
31
+ end
32
+ end
33
+ else
34
+ to_be_set = {}
35
+ if on_fail
36
+ node = node.remove(on_fail)
37
+ on_fail.find_all(:set_flag).each do |set_flag|
38
+ old_flag = set_flag.to_a[0]
39
+ if @fail_table[old_flag] > 1
40
+ on_fail = on_fail.remove(set_flag)
41
+ new_flag = "#{old_flag}_#{@counters[old_flag]}"
42
+ @counters[old_flag] += 1
43
+ to_be_set[old_flag] = new_flag
44
+ c = set_flag.children.dup
45
+ c[0] = new_flag
46
+ set_flag = set_flag.updated(nil, c)
47
+ on_fail = on_fail.updated(nil, on_fail.children + [set_flag])
48
+ end
49
+ end
50
+ node = node.updated(nil, node.children + [on_fail])
51
+ end
52
+ if on_pass
53
+ node = node.remove(on_pass)
54
+ on_pass.find_all(:set_flag).each do |set_flag|
55
+ old_flag = set_flag.to_a[0]
56
+ if @pass_table[old_flag] > 1
57
+ on_pass = on_pass.remove(set_flag)
58
+ new_flag = "#{old_flag}_#{@counters[old_flag]}"
59
+ @counters[old_flag] += 1
60
+ to_be_set[old_flag] = new_flag
61
+ c = set_flag.children.dup
62
+ c[0] = new_flag
63
+ set_flag = set_flag.updated(nil, c)
64
+ on_pass = on_pass.updated(nil, on_pass.children + [set_flag])
65
+ end
66
+ end
67
+ node = node.updated(nil, node.children + [on_pass])
68
+ end
69
+ if to_be_set.empty?
70
+ node
71
+ else
72
+ nodes = to_be_set.map { |old, new| node.updated(:if_flag, [new, node.updated(:set_flag, [old, 'auto_generated'])]) }
73
+ node.updated(:inline, [node] + nodes)
74
+ end
75
+ end
76
+ end
77
+ end
78
+ end
79
+ end
@@ -0,0 +1,65 @@
1
+ module OrigenTesters::ATP
2
+ module Processors
3
+ # Modifies the AST by performing some basic clean up, mainly to sanitize
4
+ # user input. For example it will ensure that all IDs and references are underscored
5
+ # and lower cased.
6
+ class PreCleaner < Processor
7
+ def initialize
8
+ @group_ids = []
9
+ end
10
+
11
+ # Make all IDs lower cased symbols
12
+ def on_id(node)
13
+ id = node.to_a[0]
14
+ node.updated(nil, [clean(id)])
15
+ end
16
+
17
+ # Make all ID references use the lower case symbols
18
+ def on_if_failed(node)
19
+ id, *children = *node
20
+ node.updated(nil, [clean(id)] + process_all(children))
21
+ end
22
+ alias_method :on_if_passed, :on_if_failed
23
+ alias_method :on_if_any_failed, :on_if_failed
24
+ alias_method :on_if_all_failed, :on_if_failed
25
+ alias_method :on_if_any_passed, :on_if_failed
26
+ alias_method :on_if_all_passed, :on_if_failed
27
+ alias_method :on_if_ran, :on_if_failed
28
+ alias_method :on_unless_ran, :on_if_failed
29
+
30
+ def on_group(node)
31
+ if id = node.children.find { |n| n.type == :id }
32
+ @group_ids << process(id).value
33
+ else
34
+ @group_ids << nil
35
+ end
36
+ group = node.updated(nil, process_all(node.children))
37
+ @group_ids.pop
38
+ group
39
+ end
40
+ alias_method :on_sub_flow, :on_group
41
+
42
+ def on_test(node)
43
+ # Remove IDs nodes from test nodes if they refer to the ID of a parent group
44
+ if @group_ids.last
45
+ children = node.children.reject do |n|
46
+ if n.type == :id
47
+ @group_ids.last == process(n).value
48
+ end
49
+ end
50
+ else
51
+ children = node.children
52
+ end
53
+ node.updated(nil, process_all(children))
54
+ end
55
+
56
+ def clean(id)
57
+ if id.is_a?(Array)
58
+ id.map { |i| clean(i) }
59
+ else
60
+ id.to_s.symbolize.to_s
61
+ end
62
+ end
63
+ end
64
+ end
65
+ end
@@ -0,0 +1,28 @@
1
+ module OrigenTesters::ATP
2
+ module Processors
3
+ # Removes any conditions nodes that are nested within other condition
4
+ # nodes that specify the same condition
5
+ class RedundantConditionRemover < Processor
6
+ def run(node)
7
+ @conditions = []
8
+ process(node)
9
+ end
10
+
11
+ def on_condition_node(node)
12
+ sig = [node.type, node.to_a[0]]
13
+ if @conditions.include?(sig)
14
+ flag, *nodes = *node
15
+ node.updated(:inline, process_all(nodes))
16
+ else
17
+ @conditions << sig
18
+ node = node.updated(nil, process_all(node.children))
19
+ @conditions.pop
20
+ node
21
+ end
22
+ end
23
+ OrigenTesters::ATP::Flow::CONDITION_NODE_TYPES.each do |type|
24
+ alias_method "on_#{type}", :on_condition_node unless method_defined?("on_#{type}")
25
+ end
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,199 @@
1
+ module OrigenTesters::ATP
2
+ module Processors
3
+ # This processor will apply the relationships between tests, e.g. if testB should only
4
+ # execute if testA passes, then this processor will update the AST to make testA set
5
+ # a flag on pass, and then update testB to only run if that flag is set.
6
+ class Relationship < Processor
7
+ # Returns a hash containing the IDs of all tests that have dependents
8
+ attr_reader :test_results
9
+
10
+ # Extracts all test-result nodes from the given AST
11
+ class ExtractTestResults < Processor
12
+ attr_reader :results
13
+
14
+ def on_if_failed(node)
15
+ ids, *children = *node
16
+ unless ids.is_a?(Array)
17
+ ids = [ids]
18
+ end
19
+ ids.each do |id|
20
+ results[id] ||= {}
21
+ results[id][:failed] = true
22
+ end
23
+ process_all(children)
24
+ end
25
+ alias_method :on_if_any_failed, :on_if_failed
26
+ alias_method :on_if_all_failed, :on_if_failed
27
+
28
+ def on_if_passed(node)
29
+ ids, *children = *node
30
+ unless ids.is_a?(Array)
31
+ ids = [ids]
32
+ end
33
+ ids.each do |id|
34
+ results[id] ||= {}
35
+ results[id][:passed] = true
36
+ end
37
+ process_all(children)
38
+ end
39
+ alias_method :on_if_any_passed, :on_if_passed
40
+ alias_method :on_if_all_passed, :on_if_passed
41
+
42
+ def on_if_ran(node)
43
+ id, *children = *node
44
+ results[id] ||= {}
45
+ results[id][:ran] = true
46
+ process_all(children)
47
+ end
48
+ alias_method :on_unless_ran, :on_if_ran
49
+
50
+ def results
51
+ @results ||= {}.with_indifferent_access
52
+ end
53
+ end
54
+
55
+ def run(node)
56
+ t = ExtractTestResults.new
57
+ t.process(node)
58
+ @test_results = t.results || {}
59
+ process(node)
60
+ end
61
+
62
+ def add_pass_flag(id, node)
63
+ node = node.ensure_node_present(:on_pass)
64
+ node = node.ensure_node_present(:on_fail)
65
+ node.updated(nil, node.children.map do |n|
66
+ if n.type == :on_pass
67
+ n = n.add node.updated(:set_flag, ["#{id}_PASSED", :auto_generated])
68
+ elsif n.type == :on_fail
69
+ delayed = n.find(:delayed)
70
+ if delayed && delayed.to_a[0]
71
+ n
72
+ else
73
+ n.ensure_node_present(:continue)
74
+ end
75
+ else
76
+ n
77
+ end
78
+ end)
79
+ end
80
+
81
+ def add_fail_flag(id, node)
82
+ node = node.ensure_node_present(:on_fail)
83
+ node.updated(nil, node.children.map do |n|
84
+ if n.type == :on_fail
85
+ n = n.add node.updated(:set_flag, ["#{id}_FAILED", :auto_generated])
86
+ delayed = n.find(:delayed)
87
+ if delayed && delayed.to_a[0]
88
+ n
89
+ else
90
+ n.ensure_node_present(:continue)
91
+ end
92
+ else
93
+ n
94
+ end
95
+ end)
96
+ end
97
+
98
+ def add_ran_flags(id, node)
99
+ set_flag = node.updated(:set_flag, ["#{id}_RAN", :auto_generated])
100
+ # For a group, set a flag immediately upon entry to the group to signal that
101
+ # it ran to later tests, this is better than doing it immediately after the group
102
+ # in case it was bypassed
103
+ if node.type == :group || node.type == :sub_flow
104
+ nodes = node.to_a.dup
105
+ pre_nodes = []
106
+ while [:name, :id, :path].include?(nodes.first.try(:type))
107
+ pre_nodes << nodes.shift
108
+ end
109
+ node.updated(nil, pre_nodes + [set_flag] + nodes)
110
+
111
+ # For a test, set a flag immediately after the referenced test has executed
112
+ # but don't change its pass/fail handling
113
+ elsif node.type == :test
114
+ node.updated(:inline, [node, set_flag])
115
+ else
116
+ fail "Don't know how to add ran flag to #{node.type}"
117
+ end
118
+ end
119
+
120
+ # Set flags depending on the result on tests which have dependents later
121
+ # in the flow
122
+ def on_test(node)
123
+ node = node.updated(nil, process_all(node.children))
124
+ nid = id(node)
125
+ # If this test has a dependent
126
+ if test_results[nid]
127
+ node = add_pass_flag(nid, node) if test_results[nid][:passed]
128
+ node = add_fail_flag(nid, node) if test_results[nid][:failed]
129
+ node = add_ran_flags(nid, node) if test_results[nid][:ran]
130
+ end
131
+ node
132
+ end
133
+ alias_method :on_group, :on_test
134
+ alias_method :on_sub_flow, :on_test
135
+
136
+ def on_if_failed(node)
137
+ id, *children = *node
138
+ node.updated(:if_flag, [id_to_flag(id, 'FAILED')] + process_all(children))
139
+ end
140
+ alias_method :on_if_any_failed, :on_if_failed
141
+
142
+ def on_if_all_failed(node)
143
+ ids, *children = *node
144
+ ids.reverse_each.with_index do |id, i|
145
+ if i == 0
146
+ node = node.updated(:if_flag, [id_to_flag(id, 'FAILED')] + process_all(children))
147
+ else
148
+ node = node.updated(:if_flag, [id_to_flag(id, 'FAILED'), node])
149
+ end
150
+ end
151
+ node
152
+ end
153
+
154
+ def on_if_passed(node)
155
+ id, *children = *node
156
+ node.updated(:if_flag, [id_to_flag(id, 'PASSED')] + process_all(children))
157
+ end
158
+ alias_method :on_if_any_passed, :on_if_passed
159
+
160
+ def on_if_all_passed(node)
161
+ ids, *children = *node
162
+ ids.reverse_each.with_index do |id, i|
163
+ if i == 0
164
+ node = node.updated(:if_flag, [id_to_flag(id, 'PASSED')] + process_all(children))
165
+ else
166
+ node = node.updated(:if_flag, [id_to_flag(id, 'PASSED'), node])
167
+ end
168
+ end
169
+ node
170
+ end
171
+
172
+ def on_if_ran(node)
173
+ id, *children = *node
174
+ node.updated(:if_flag, [id_to_flag(id, 'RAN')] + process_all(children))
175
+ end
176
+
177
+ def on_unless_ran(node)
178
+ id, *children = *node
179
+ node.updated(:unless_flag, [id_to_flag(id, 'RAN')] + process_all(children))
180
+ end
181
+
182
+ # Returns the ID of the give test node (if any), caller is responsible
183
+ # for only passing test nodes
184
+ def id(node)
185
+ if n = node.children.find { |c| c.type == :id }
186
+ n.children.first
187
+ end
188
+ end
189
+
190
+ def id_to_flag(id, type)
191
+ if id.is_a?(Array)
192
+ id.map { |i| "#{i}_#{type}" }
193
+ else
194
+ "#{id}_#{type}"
195
+ end
196
+ end
197
+ end
198
+ end
199
+ end