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