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