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,10 @@
1
+ module OrigenTesters::ATP
2
+ module Processors
3
+ # Removes all :sub_flow nodes
4
+ class SubFlowRemover < Processor
5
+ def on_sub_flow(node)
6
+ node.updated(:remove, nil)
7
+ end
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,48 @@
1
+ module OrigenTesters::ATP
2
+ # Program is the top-level container for a collection of test flows
3
+ class Program
4
+ # Load a program from a previously saved file
5
+ def self.load(file)
6
+ p = nil
7
+ File.open(file) do |f|
8
+ p = Marshal.load(f)
9
+ end
10
+ p
11
+ end
12
+
13
+ def flow(name, options = {})
14
+ flows[name] ||= Flow.new(self, name, options)
15
+ end
16
+
17
+ def flows
18
+ @flows ||= {}.with_indifferent_access
19
+ # To rescue previously created programs which have been loaded
20
+ unless @flows.is_a?(ActiveSupport::HashWithIndifferentAccess)
21
+ @flows = @flows.with_indifferent_access
22
+ end
23
+ @flows
24
+ end
25
+
26
+ # Save the program to a file
27
+ def save(file)
28
+ File.open(file, 'w') do |f|
29
+ Marshal.dump(self, f)
30
+ end
31
+ end
32
+
33
+ def respond_to?(*args)
34
+ flows.key?(args.first) || super
35
+ end
36
+
37
+ def method_missing(method, *args, &block) # :nodoc:
38
+ if f = flows[method]
39
+ define_singleton_method method do
40
+ f
41
+ end
42
+ f
43
+ else
44
+ super
45
+ end
46
+ end
47
+ end
48
+ end
@@ -0,0 +1,234 @@
1
+ module OrigenTesters::ATP
2
+ # This class is responsible for executing the given test flow based on a given
3
+ # set of runtime conditions.
4
+ # A subset of the input AST will be returned containing only the nodes that would
5
+ # be hit when the flow is executed under the given conditions.
6
+ class Runner < Processor
7
+ def run(node, options = {})
8
+ options = {
9
+ evaluate_enables: true,
10
+ evaluate_flags: true,
11
+ evaluate_set_result: true
12
+ }.merge(options)
13
+ @options = options
14
+ @completed = false
15
+ @groups = []
16
+ @groups_on_fail = []
17
+ @groups_on_pass = []
18
+ node = Processors::AddIDs.new.run(node)
19
+ node = Processors::AddSetResult.new.run(node)
20
+ process(node)
21
+ end
22
+
23
+ def on_flow(node)
24
+ c = open_container do
25
+ process_all(node.children)
26
+ end
27
+ node.updated(nil, c)
28
+ end
29
+
30
+ def on_name(node)
31
+ container << node
32
+ end
33
+
34
+ def on_if_flag(node)
35
+ if @options[:evaluate_flags]
36
+ flag, *nodes = *node
37
+ flag = [flag].flatten
38
+ enabled = node.type == :if_flag
39
+ active = flag.any? { |f| set_flags.include?(f) }
40
+ if (enabled && active) || (!enabled && !active)
41
+ process_all(nodes)
42
+ end
43
+ else
44
+ c = open_container do
45
+ process_all(node.children)
46
+ end
47
+ container << node.updated(nil, node.children.take(1) + c)
48
+ end
49
+ end
50
+ alias_method :on_unless_flag, :on_if_flag
51
+
52
+ def on_if_enabled(node)
53
+ if @options[:evaluate_enables]
54
+ flag, *nodes = *node
55
+ flag = [flag].flatten
56
+ enabled = node.type == :if_enabled
57
+ active = flag.any? { |f| set_enables.include?(f) }
58
+ if (enabled && active) || (!enabled && !active)
59
+ process_all(nodes)
60
+ end
61
+ else
62
+ c = open_container do
63
+ process_all(node.children)
64
+ end
65
+ container << node.updated(nil, node.children.take(1) + c)
66
+ end
67
+ end
68
+ alias_method :on_unless_enabled, :on_if_enabled
69
+
70
+ def on_if_failed(node)
71
+ id, *nodes = *node
72
+ if failed_test_ids.include?(id)
73
+ process_all(nodes)
74
+ end
75
+ end
76
+
77
+ def on_if_passed(node)
78
+ id, *nodes = *node
79
+ unless failed_test_ids.include?(id)
80
+ process_all(nodes)
81
+ end
82
+ end
83
+
84
+ def on_test(node)
85
+ if id = node.find(:id)
86
+ id = id.to_a[0]
87
+ if failed_test_ids.include?(id)
88
+ node = node.add(node.updated(:failed, []))
89
+ failed = true
90
+ if n_on_fail = node.find(:on_fail)
91
+ node = node.remove(n_on_fail)
92
+ end
93
+ end
94
+ end
95
+ unless failed
96
+ if n_on_pass = node.find(:on_pass)
97
+ node = node.remove(n_on_pass)
98
+ end
99
+ end
100
+
101
+ unless completed?
102
+ container << node
103
+ process_all(n_on_fail) if n_on_fail
104
+ process_all(n_on_pass) if n_on_pass
105
+ end
106
+
107
+ if failed
108
+ # Give indication to the parent group that at least one test within it failed
109
+ if @groups.last
110
+ @groups.pop
111
+ @groups << false
112
+ end
113
+ if n = node.find(:on_fail)
114
+ # If it has been set by a parent group, don't clear it
115
+ orig = @continue
116
+ @continue ||= !!n.find(:continue)
117
+ process_all(n)
118
+ @continue = orig
119
+ end
120
+ else
121
+ if n = node.find(:on_pass)
122
+ process_all(n)
123
+ end
124
+ end
125
+ end
126
+
127
+ def on_group(node)
128
+ on_fail = node.find(:on_fail)
129
+ on_pass = node.find(:on_pass)
130
+ c = open_container do
131
+ @groups << true # This will be set to false by any tests that fail within the group
132
+ @groups_on_fail << on_fail
133
+ @groups_on_pass << on_pass
134
+ if on_fail
135
+ orig = @continue
136
+ @continue = !!on_fail.find(:continue)
137
+ process_all(node.children - [on_fail, on_pass])
138
+ @continue = orig
139
+ else
140
+ process_all(node.children - [on_fail, on_pass])
141
+ end
142
+ if !@groups.pop # If failed
143
+ if on_fail
144
+ @continue = !!on_fail.find(:continue)
145
+ process_all(on_fail)
146
+ @continue = false
147
+ end
148
+ else
149
+ if on_pass
150
+ process_all(on_pass)
151
+ end
152
+ end
153
+ @groups_on_fail.pop
154
+ @groups_on_pass.pop
155
+ end
156
+ container << node.updated(nil, c)
157
+ end
158
+
159
+ def on_set_result(node)
160
+ unless @continue
161
+ container << node unless completed?
162
+ @completed = true if @options[:evaluate_set_result]
163
+ end
164
+ end
165
+
166
+ def on_set_flag(node)
167
+ set_flags << node.to_a[0]
168
+ end
169
+
170
+ def on_enable(node)
171
+ set_enables << node.value unless set_enables.include?(node.value)
172
+ end
173
+
174
+ def on_disable(node)
175
+ set_enables.delete(node.value)
176
+ end
177
+
178
+ def on_log(node)
179
+ container << node unless completed?
180
+ end
181
+ alias_method :on_render, :on_log
182
+
183
+ def on_if_job(node)
184
+ jobs, *nodes = *node
185
+ jobs = clean_job(jobs)
186
+ state = node.type == :if_job
187
+ unless job
188
+ fail 'Flow contains JOB-based conditions and no current JOB has been given!'
189
+ end
190
+ if state
191
+ process_all(node) if jobs.include?(job)
192
+ else
193
+ process_all(node) unless jobs.include?(job)
194
+ end
195
+ end
196
+ alias_method :on_unless_job, :on_if_job
197
+
198
+ def clean_job(job)
199
+ [job].flatten.map { |j| j.to_s.upcase }
200
+ end
201
+
202
+ def job
203
+ @options[:job].to_s.upcase if @options[:job]
204
+ end
205
+
206
+ def failed_test_ids
207
+ @failed_test_ids ||= [@options[:failed_test_id] || @options[:failed_test_ids]].flatten.compact
208
+ end
209
+
210
+ def set_flags
211
+ @set_flags ||= []
212
+ end
213
+
214
+ # Returns an array of enabled flow flags
215
+ def set_enables
216
+ @set_enables ||= [@options[:enable] || @options[:enables]].flatten.compact
217
+ end
218
+
219
+ def completed?
220
+ @completed
221
+ end
222
+
223
+ def open_container(c = [])
224
+ @containers ||= []
225
+ @containers << c
226
+ yield
227
+ @containers.pop
228
+ end
229
+
230
+ def container
231
+ @containers.last
232
+ end
233
+ end
234
+ end
@@ -0,0 +1,53 @@
1
+ require 'ast'
2
+ module OrigenTesters::ATP
3
+ class Validator < Processor
4
+ attr_reader :flow
5
+
6
+ def self.testing=(value)
7
+ @testing = value
8
+ end
9
+
10
+ def self.testing
11
+ @testing
12
+ end
13
+
14
+ def initialize(flow)
15
+ @flow = flow
16
+ end
17
+
18
+ def process(node)
19
+ if @top_level_called
20
+ super
21
+ else
22
+ @top_level_called = true
23
+ setup
24
+ super(node)
25
+ unless @testing
26
+ exit 1 if on_completion
27
+ end
28
+ end
29
+ end
30
+
31
+ # For test purposes, returns true if validation failed rather
32
+ # than exiting the process
33
+ def test_process(node)
34
+ @testing = true
35
+ process(node)
36
+ on_completion
37
+ end
38
+
39
+ def setup
40
+ end
41
+
42
+ def error(message)
43
+ # This is a hack to make the specs pass, for some reason RSpec
44
+ # seems to be swallowing the Origen log output after the first
45
+ # test that generates an error
46
+ if Validator.testing
47
+ puts message
48
+ else
49
+ Origen.log.error(message)
50
+ end
51
+ end
52
+ end
53
+ end
@@ -0,0 +1,4 @@
1
+ module OrigenTesters::ATP
2
+ module Validators
3
+ end
4
+ end
@@ -0,0 +1,32 @@
1
+ module OrigenTesters::ATP
2
+ module Validators
3
+ class DuplicateIDs < Validator
4
+ def on_completion
5
+ if @duplicate_ids
6
+ @duplicate_ids.each do |id, nodes|
7
+ error "Test ID #{id} is defined more than once in flow #{flow.name}:"
8
+ nodes.each do |node|
9
+ error " #{node.source}"
10
+ end
11
+ end
12
+ true
13
+ end
14
+ end
15
+
16
+ def on_id(node)
17
+ @existing_ids ||= {}
18
+ id = node.value
19
+ if @existing_ids[id]
20
+ @duplicate_ids ||= {}
21
+ if @duplicate_ids[id]
22
+ @duplicate_ids[id] << node
23
+ else
24
+ @duplicate_ids[id] = [@existing_ids[id], node]
25
+ end
26
+ else
27
+ @existing_ids[id] = node
28
+ end
29
+ end
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,59 @@
1
+ module OrigenTesters::ATP
2
+ module Validators
3
+ class Flags < Validator
4
+ def setup
5
+ @open_if_nodes = []
6
+ @open_unless_nodes = []
7
+ @conflicting = []
8
+ end
9
+
10
+ def on_completion
11
+ failed = false
12
+ unless @conflicting.empty?
13
+ error 'if_flag and unless_flag conditions cannot be nested and refer to the same flag unless it is declared as volatile'
14
+ error "The following conflicts were found in flow #{flow.name}:"
15
+ @conflicting.each do |a, b|
16
+ a_condition = a.to_a[1] ? 'if_job: ' : 'unless_job:'
17
+ b_condition = b.to_a[1] ? 'if_job: ' : 'unless_job:'
18
+ error " #{a.type}(#{a.to_a[0]}) #{a.source}"
19
+ error " #{b.type}(#{b.to_a[0]}) #{b.source}"
20
+ error ''
21
+ end
22
+ failed = true
23
+ end
24
+ failed
25
+ end
26
+
27
+ def on_flow(node)
28
+ extract_volatiles(node)
29
+ process_all(node.children)
30
+ end
31
+
32
+ def on_if_flag(node)
33
+ if volatile?(node.to_a[0])
34
+ process_all(node.children)
35
+ else
36
+ if n = @open_unless_nodes.find { |n| n.to_a[0] == node.to_a[0] }
37
+ @conflicting << [n, node]
38
+ end
39
+ @open_if_nodes << node
40
+ process_all(node.children)
41
+ @open_if_nodes.pop
42
+ end
43
+ end
44
+
45
+ def on_unless_flag(node)
46
+ if volatile?(node.to_a[0])
47
+ process_all(node.children)
48
+ else
49
+ if n = @open_if_nodes.find { |n| n.to_a[0] == node.to_a[0] }
50
+ @conflicting << [n, node]
51
+ end
52
+ @open_unless_nodes << node
53
+ process_all(node.children)
54
+ @open_unless_nodes.pop
55
+ end
56
+ end
57
+ end
58
+ end
59
+ end