atp 0.8.0 → 1.0.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/commands.rb +5 -7
- data/config/version.rb +2 -2
- data/lib/atp.rb +45 -14
- data/lib/atp/ast/node.rb +18 -9
- data/lib/atp/flow.rb +558 -151
- data/lib/atp/flow_api.rb +26 -0
- data/lib/atp/formatters/basic.rb +3 -1
- data/lib/atp/processor.rb +0 -16
- data/lib/atp/processors/add_ids.rb +1 -0
- data/lib/atp/processors/adjacent_if_combiner.rb +97 -0
- data/lib/atp/processors/append_to.rb +27 -0
- data/lib/atp/processors/apply_post_group_actions.rb +50 -0
- data/lib/atp/processors/condition.rb +38 -37
- data/lib/atp/processors/continue_implementer.rb +35 -0
- data/lib/atp/processors/else_remover.rb +31 -0
- data/lib/atp/processors/empty_branch_remover.rb +17 -0
- data/lib/atp/processors/extract_set_flags.rb +18 -0
- data/lib/atp/processors/flag_optimizer.rb +214 -0
- data/lib/atp/processors/flattener.rb +58 -0
- data/lib/atp/processors/flow_id.rb +10 -4
- data/lib/atp/processors/on_pass_fail_remover.rb +39 -0
- data/lib/atp/processors/one_flag_per_test.rb +79 -0
- data/lib/atp/processors/pre_cleaner.rb +13 -8
- data/lib/atp/processors/redundant_condition_remover.rb +28 -0
- data/lib/atp/processors/relationship.rb +91 -53
- data/lib/atp/runner.rb +41 -31
- data/lib/atp/validator.rb +19 -0
- data/lib/atp/validators/duplicate_ids.rb +2 -2
- data/lib/atp/validators/jobs.rb +12 -10
- data/lib/atp/validators/missing_ids.rb +14 -8
- metadata +15 -5
- data/lib/atp/ast/builder.rb +0 -397
- data/lib/atp/ast/factories.rb +0 -17
- data/lib/atp/processors/post_cleaner.rb +0 -43
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 898bb2e160b6d893026e211eba9cf858387c33b5
|
4
|
+
data.tar.gz: f6e78515801ad71eed4358f3c5373df08a32e115
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: ef51a2e7bdcd532882e9266db0f5be8537e9079dd6f6c370b4ccff34e989737539f7fa7123cdbadf7d388540f29570946a49bde56d4b3f63b5bb740753acf92e
|
7
|
+
data.tar.gz: c35f0f652e10edf1634b8ed839c4d77efbbfed048b80529d73c61ff08b7987ce35355e10f73ab4e9fa5b18abca593ad4ee89b1fcbca715019e0a648e743969c9
|
data/config/commands.rb
CHANGED
@@ -20,13 +20,10 @@ aliases ={
|
|
20
20
|
# Now branch to the specific task code
|
21
21
|
case @command
|
22
22
|
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
require "commands/my_command" # Would load file lib/commands/my_command.rb
|
28
|
-
# You must always exit upon successfully capturing a command to prevent
|
29
|
-
# control flowing back to Origen
|
23
|
+
when "tags"
|
24
|
+
Dir.chdir Origen.root do
|
25
|
+
system "ripper-tags --recursive lib"
|
26
|
+
end
|
30
27
|
exit 0
|
31
28
|
|
32
29
|
# Example of how to make a command to run unit tests, this simply invokes RSpec on
|
@@ -70,6 +67,7 @@ else
|
|
70
67
|
# before handing control back to Origen. Un-comment the example below to get started.
|
71
68
|
@application_commands = <<-EOT
|
72
69
|
specs Run the specs (tests), -c will enable coverage
|
70
|
+
tags Generate ctags for this app
|
73
71
|
EOT
|
74
72
|
# examples Run the examples (tests), -c will enable coverage
|
75
73
|
|
data/config/version.rb
CHANGED
data/lib/atp.rb
CHANGED
@@ -9,12 +9,39 @@ module ATP
|
|
9
9
|
autoload :Runner, 'atp/runner'
|
10
10
|
autoload :Formatter, 'atp/formatter'
|
11
11
|
autoload :Parser, 'atp/parser'
|
12
|
+
autoload :FlowAPI, 'atp/flow_api'
|
12
13
|
|
13
14
|
module AST
|
14
15
|
autoload :Node, 'atp/ast/node'
|
15
|
-
autoload :Builder, 'atp/ast/builder'
|
16
|
-
autoload :Factories, 'atp/ast/factories'
|
17
16
|
autoload :Extractor, 'atp/ast/extractor'
|
17
|
+
|
18
|
+
# This is a shim to help backwards compatibility with ATP v0
|
19
|
+
module Builder
|
20
|
+
class LazyObject < ::BasicObject
|
21
|
+
def initialize(&callable)
|
22
|
+
@callable = callable
|
23
|
+
end
|
24
|
+
|
25
|
+
def __target_object__
|
26
|
+
@__target_object__ ||= @callable.call
|
27
|
+
end
|
28
|
+
|
29
|
+
def method_missing(method_name, *args, &block)
|
30
|
+
__target_object__.send(method_name, *args, &block)
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
# Some trickery to lazy load this to fire a deprecation warning if an app references it
|
35
|
+
CONDITION_KEYS ||= LazyObject.new do
|
36
|
+
Origen.log.deprecate 'ATP::AST::Builder::CONDITION_KEYS is frozen and is no longer maintained, consider switching to ATP::Flow::CONDITION_KEYS.keys for similar functionality'
|
37
|
+
[:if_enabled, :enabled, :enable_flag, :enable, :if_enable, :unless_enabled, :not_enabled,
|
38
|
+
:disabled, :disable, :unless_enable, :if_failed, :unless_passed, :failed, :if_passed,
|
39
|
+
:unless_failed, :passed, :if_ran, :if_executed, :unless_ran, :unless_executed, :job,
|
40
|
+
:jobs, :if_job, :if_jobs, :unless_job, :unless_jobs, :if_any_failed, :unless_all_passed,
|
41
|
+
:if_all_failed, :unless_any_passed, :if_any_passed, :unless_all_failed, :if_all_passed,
|
42
|
+
:unless_any_failed, :if_flag, :unless_flag]
|
43
|
+
end
|
44
|
+
end
|
18
45
|
end
|
19
46
|
|
20
47
|
# Processors actually modify the AST to clean and optimize the user input
|
@@ -23,11 +50,22 @@ module ATP
|
|
23
50
|
autoload :Condition, 'atp/processors/condition'
|
24
51
|
autoload :Relationship, 'atp/processors/relationship'
|
25
52
|
autoload :PreCleaner, 'atp/processors/pre_cleaner'
|
26
|
-
autoload :PostCleaner, 'atp/processors/post_cleaner'
|
27
53
|
autoload :Marshal, 'atp/processors/marshal'
|
28
54
|
autoload :AddIDs, 'atp/processors/add_ids'
|
29
55
|
autoload :AddSetResult, 'atp/processors/add_set_result'
|
30
56
|
autoload :FlowID, 'atp/processors/flow_id'
|
57
|
+
autoload :EmptyBranchRemover, 'atp/processors/empty_branch_remover'
|
58
|
+
autoload :AppendTo, 'atp/processors/append_to'
|
59
|
+
autoload :Flattener, 'atp/processors/flattener'
|
60
|
+
autoload :RedundantConditionRemover, 'atp/processors/redundant_condition_remover'
|
61
|
+
autoload :ElseRemover, 'atp/processors/else_remover'
|
62
|
+
autoload :OnPassFailRemover, 'atp/processors/on_pass_fail_remover'
|
63
|
+
autoload :ApplyPostGroupActions, 'atp/processors/apply_post_group_actions'
|
64
|
+
autoload :OneFlagPerTest, 'atp/processors/one_flag_per_test'
|
65
|
+
autoload :FlagOptimizer, 'atp/processors/flag_optimizer'
|
66
|
+
autoload :AdjacentIfCombiner, 'atp/processors/adjacent_if_combiner'
|
67
|
+
autoload :ContinueImplementer, 'atp/processors/continue_implementer'
|
68
|
+
autoload :ExtractSetFlags, 'atp/processors/extract_set_flags'
|
31
69
|
end
|
32
70
|
|
33
71
|
# Summarizers extract summary data from the given AST
|
@@ -48,18 +86,11 @@ module ATP
|
|
48
86
|
module Formatters
|
49
87
|
autoload :Basic, 'atp/formatters/basic'
|
50
88
|
autoload :Datalog, 'atp/formatters/datalog'
|
51
|
-
autoload :Graph, 'atp/formatters/graph'
|
52
|
-
end
|
53
|
-
|
54
|
-
def self.or(*args)
|
55
|
-
OR.new(*args)
|
56
|
-
end
|
57
|
-
|
58
|
-
def self.and(*args)
|
59
|
-
AND.new(*args)
|
60
89
|
end
|
61
90
|
|
62
|
-
|
63
|
-
|
91
|
+
# Maintains a unique ID counter to ensure that all nodes get a unique ID
|
92
|
+
def self.next_id
|
93
|
+
@next_id ||= 0
|
94
|
+
@next_id += 1
|
64
95
|
end
|
65
96
|
end
|
data/lib/atp/ast/node.rb
CHANGED
@@ -2,9 +2,8 @@ require 'ast'
|
|
2
2
|
module ATP
|
3
3
|
module AST
|
4
4
|
class Node < ::AST::Node
|
5
|
-
include Factories
|
6
|
-
|
7
5
|
attr_reader :file, :line_number, :description
|
6
|
+
attr_accessor :id
|
8
7
|
|
9
8
|
def initialize(type, children = [], properties = {})
|
10
9
|
# Always use strings instead of symbols in the AST, makes serializing
|
@@ -21,6 +20,11 @@ module ATP
|
|
21
20
|
end
|
22
21
|
end
|
23
22
|
|
23
|
+
# Returns true if the node carries source file data, retrieve it via the source method
|
24
|
+
def has_source?
|
25
|
+
!!file
|
26
|
+
end
|
27
|
+
|
24
28
|
# Create a new node from the given S-expression (a string)
|
25
29
|
def self.from_sexp(sexp)
|
26
30
|
@parser ||= Parser.new
|
@@ -29,14 +33,14 @@ module ATP
|
|
29
33
|
|
30
34
|
# Adds an empty node of the given type to the children unless another
|
31
35
|
# node of the same type is already present
|
32
|
-
def ensure_node_present(type, child_nodes
|
36
|
+
def ensure_node_present(type, *child_nodes)
|
33
37
|
if children.any? { |n| n.type == type }
|
34
38
|
self
|
35
39
|
else
|
36
|
-
if child_nodes
|
37
|
-
node =
|
40
|
+
if !child_nodes.empty?
|
41
|
+
node = updated(type, child_nodes)
|
38
42
|
else
|
39
|
-
node =
|
43
|
+
node = updated(type, [])
|
40
44
|
end
|
41
45
|
updated(nil, children + [node])
|
42
46
|
end
|
@@ -68,15 +72,20 @@ module ATP
|
|
68
72
|
updated(nil, children - nodes)
|
69
73
|
end
|
70
74
|
|
71
|
-
# Returns the first child node of the given type that is found
|
72
|
-
def find(
|
73
|
-
children.find { |c| c.try(:type)
|
75
|
+
# Returns the first child node of the given type(s) that is found
|
76
|
+
def find(*types)
|
77
|
+
children.find { |c| types.include?(c.try(:type)) }
|
74
78
|
end
|
75
79
|
|
76
80
|
# Returns an array containing all child nodes of the given type(s)
|
77
81
|
def find_all(*types)
|
78
82
|
children.select { |c| types.include?(c.try(:type)) }
|
79
83
|
end
|
84
|
+
|
85
|
+
# Returns an array containing all flags which are set within the given node
|
86
|
+
def set_flags
|
87
|
+
Processors::ExtractSetFlags.new.run(self)
|
88
|
+
end
|
80
89
|
end
|
81
90
|
end
|
82
91
|
end
|
data/lib/atp/flow.rb
CHANGED
@@ -3,41 +3,179 @@ module ATP
|
|
3
3
|
# with an abstract test program
|
4
4
|
class Flow
|
5
5
|
attr_reader :program, :name
|
6
|
-
|
7
|
-
|
8
|
-
|
6
|
+
|
7
|
+
attr_accessor :source_file, :source_line_number, :description
|
8
|
+
|
9
|
+
CONDITION_KEYS = {
|
10
|
+
if_enabled: :if_enabled,
|
11
|
+
if_enable: :if_enabled,
|
12
|
+
enabled: :if_enabled,
|
13
|
+
enable_flag: :if_enabled,
|
14
|
+
enable: :if_enabled,
|
15
|
+
|
16
|
+
unless_enabled: :unless_enabled,
|
17
|
+
not_enabled: :unless_enabled,
|
18
|
+
disabled: :unless_enabled,
|
19
|
+
disable: :unless_enabled,
|
20
|
+
unless_enable: :unless_enabled,
|
21
|
+
|
22
|
+
if_failed: :if_failed,
|
23
|
+
unless_passed: :if_failed,
|
24
|
+
failed: :if_failed,
|
25
|
+
|
26
|
+
if_passed: :if_passed,
|
27
|
+
unless_failed: :if_passed,
|
28
|
+
passed: :if_passed,
|
29
|
+
|
30
|
+
if_any_failed: :if_any_failed,
|
31
|
+
unless_all_passed: :if_any_failed,
|
32
|
+
|
33
|
+
if_all_failed: :if_all_failed,
|
34
|
+
unless_any_passed: :if_all_failed,
|
35
|
+
|
36
|
+
if_any_passed: :if_any_passed,
|
37
|
+
unless_all_failed: :if_any_passed,
|
38
|
+
|
39
|
+
if_all_passed: :if_all_passed,
|
40
|
+
unless_any_failed: :if_all_passed,
|
41
|
+
|
42
|
+
if_ran: :if_ran,
|
43
|
+
if_executed: :if_ran,
|
44
|
+
|
45
|
+
unless_ran: :unless_ran,
|
46
|
+
unless_executed: :unless_ran,
|
47
|
+
|
48
|
+
job: :if_job,
|
49
|
+
jobs: :if_job,
|
50
|
+
if_job: :if_job,
|
51
|
+
if_jobs: :if_job,
|
52
|
+
|
53
|
+
unless_job: :unless_job,
|
54
|
+
unless_jobs: :unless_job,
|
55
|
+
|
56
|
+
if_flag: :if_flag,
|
57
|
+
|
58
|
+
unless_flag: :unless_flag,
|
59
|
+
|
60
|
+
group: :group
|
61
|
+
}
|
62
|
+
|
63
|
+
CONDITION_NODE_TYPES = CONDITION_KEYS.values.uniq
|
9
64
|
|
10
65
|
def initialize(program, name = nil, options = {})
|
11
66
|
name, options = nil, name if name.is_a?(Hash)
|
12
67
|
extract_meta!(options)
|
13
68
|
@program = program
|
14
69
|
@name = name
|
15
|
-
@
|
70
|
+
@pipeline = [n1(:flow, n1(:name, name))]
|
16
71
|
end
|
17
72
|
|
18
73
|
# @api private
|
19
74
|
def marshal_dump
|
20
|
-
[@name, @program, Processors::Marshal.new.process(
|
75
|
+
[@name, @program, Processors::Marshal.new.process(raw)]
|
21
76
|
end
|
22
77
|
|
23
78
|
# @api private
|
24
79
|
def marshal_load(array)
|
25
|
-
@name, @program,
|
80
|
+
@name, @program, raw = array
|
81
|
+
@pipeline = [raw]
|
82
|
+
end
|
83
|
+
|
84
|
+
# Returns the raw AST
|
85
|
+
def raw
|
86
|
+
n = nil
|
87
|
+
@pipeline.reverse_each do |node|
|
88
|
+
if n
|
89
|
+
n = node.updated(nil, node.children + [n])
|
90
|
+
else
|
91
|
+
n = node
|
92
|
+
end
|
93
|
+
end
|
94
|
+
n
|
26
95
|
end
|
27
96
|
|
28
97
|
# Returns a processed/optimized AST, this is the one that should be
|
29
98
|
# used to build and represent the given test flow
|
30
|
-
def ast
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
99
|
+
def ast(options = {})
|
100
|
+
options = {
|
101
|
+
apply_relationships: true,
|
102
|
+
# Supply a unique ID to append to all IDs
|
103
|
+
unique_id: nil,
|
104
|
+
# Set to :smt, or :igxl
|
105
|
+
optimization: :runner,
|
106
|
+
# These options are not intended for application use, but provide the ability to
|
107
|
+
# turn off certain processors during test cases
|
108
|
+
add_ids: true,
|
109
|
+
optimize_flags: true,
|
110
|
+
one_flag_per_test: true,
|
111
|
+
implement_continue: true
|
112
|
+
}.merge(options)
|
113
|
+
###############################################################################
|
114
|
+
## Common pre-processing and validation
|
115
|
+
###############################################################################
|
116
|
+
ast = Processors::PreCleaner.new.run(raw)
|
117
|
+
Validators::DuplicateIDs.new(self).run(ast)
|
118
|
+
Validators::MissingIDs.new(self).run(ast)
|
119
|
+
Validators::Jobs.new(self).run(ast)
|
120
|
+
# Ensure everything has an ID, this helps later if condition nodes need to be generated
|
121
|
+
ast = Processors::AddIDs.new.run(ast) if options[:add_ids]
|
122
|
+
ast = Processors::FlowID.new.run(ast, options[:unique_id]) if options[:unique_id]
|
123
|
+
|
124
|
+
###############################################################################
|
125
|
+
## Optimization for a C-like flow target, e.g. V93K
|
126
|
+
###############################################################################
|
127
|
+
if options[:optimization] == :smt || options[:optimization] == :runner
|
128
|
+
# This applies all the relationships by setting flags in the referenced test and
|
129
|
+
# changing all if_passed/failed type nodes to if_flag type nodes
|
130
|
+
ast = Processors::Relationship.new.run(ast) if options[:apply_relationships]
|
131
|
+
ast = Processors::Condition.new.run(ast)
|
132
|
+
unless options[:optimization] == :runner
|
133
|
+
ast = Processors::ContinueImplementer.new.run(ast) if options[:implement_continue]
|
134
|
+
end
|
135
|
+
ast = Processors::FlagOptimizer.new.run(ast) if options[:optimize_flags]
|
136
|
+
ast = Processors::AdjacentIfCombiner.new.run(ast)
|
137
|
+
|
138
|
+
###############################################################################
|
139
|
+
## Optimization for a row-based target, e.g. UltraFLEX
|
140
|
+
###############################################################################
|
141
|
+
elsif options[:optimization] == :igxl
|
142
|
+
# Un-nest everything embedded in else nodes
|
143
|
+
ast = Processors::ElseRemover.new.run(ast)
|
144
|
+
# Un-nest everything embedded in on_pass/fail nodes except for binning and
|
145
|
+
# flag setting
|
146
|
+
ast = Processors::OnPassFailRemover.new.run(ast)
|
147
|
+
# This applies all the relationships by setting flags in the referenced test and
|
148
|
+
# changing all if_passed/failed type nodes to if_flag type nodes
|
149
|
+
ast = Processors::Relationship.new.run(ast) if options[:apply_relationships]
|
150
|
+
ast = Processors::Condition.new.run(ast)
|
151
|
+
ast = Processors::ApplyPostGroupActions.new.run(ast)
|
152
|
+
ast = Processors::OneFlagPerTest.new.run(ast) if options[:one_flag_per_test]
|
153
|
+
ast = Processors::RedundantConditionRemover.new.run(ast)
|
154
|
+
|
155
|
+
###############################################################################
|
156
|
+
## Not currently used, more of a test case
|
157
|
+
###############################################################################
|
158
|
+
elsif options[:optimization] == :flat
|
159
|
+
# Un-nest everything embedded in else nodes
|
160
|
+
ast = Processors::ElseRemover.new.run(ast)
|
161
|
+
# Un-nest everything embedded in on_pass/fail nodes except for binning and
|
162
|
+
# flag setting
|
163
|
+
ast = Processors::OnPassFailRemover.new.run(ast)
|
164
|
+
ast = Processors::Condition.new.run(ast)
|
165
|
+
ast = Processors::Flattener.new.run(ast)
|
166
|
+
|
167
|
+
###############################################################################
|
168
|
+
## Default Optimization
|
169
|
+
###############################################################################
|
170
|
+
else
|
171
|
+
ast = Processors::Condition.new.run(ast)
|
172
|
+
end
|
173
|
+
|
174
|
+
###############################################################################
|
175
|
+
## Common cleanup
|
176
|
+
###############################################################################
|
177
|
+
# Removes any empty on_pass and on_fail branches
|
178
|
+
ast = Processors::EmptyBranchRemover.new.run(ast)
|
41
179
|
ast
|
42
180
|
end
|
43
181
|
|
@@ -46,7 +184,7 @@ module ATP
|
|
46
184
|
def volatile(*flags)
|
47
185
|
options = flags.pop if flags.last.is_a?(Hash)
|
48
186
|
flags = flags.flatten
|
49
|
-
@
|
187
|
+
@pipeline[0] = add_volatile_flags(@pipeline[0], flags)
|
50
188
|
end
|
51
189
|
|
52
190
|
# Group all tests generated within the given block
|
@@ -57,10 +195,15 @@ module ATP
|
|
57
195
|
# flow.test ...
|
58
196
|
# end
|
59
197
|
def group(name, options = {})
|
60
|
-
open_groups.push([])
|
61
|
-
yield
|
62
198
|
extract_meta!(options)
|
63
|
-
|
199
|
+
apply_conditions(options) do
|
200
|
+
children = [n1(:name, name)]
|
201
|
+
children << id(options[:id]) if options[:id]
|
202
|
+
children << on_fail(options[:on_fail]) if options[:on_fail]
|
203
|
+
children << on_pass(options[:on_pass]) if options[:on_pass]
|
204
|
+
g = n(:group, children)
|
205
|
+
append_to(g) { yield }
|
206
|
+
end
|
64
207
|
end
|
65
208
|
|
66
209
|
# Add a test line to the flow
|
@@ -74,8 +217,7 @@ module ATP
|
|
74
217
|
# @option options [Hash] :conditions What conditions must be met to execute the test
|
75
218
|
def test(instance, options = {})
|
76
219
|
extract_meta!(options)
|
77
|
-
|
78
|
-
t = apply_open_conditions(options) do |options|
|
220
|
+
apply_conditions(options) do
|
79
221
|
# Allows any continue, bin, or soft bin argument passed in at the options top-level to be assumed
|
80
222
|
# to be the action to take if the test fails
|
81
223
|
if b = options.delete(:bin)
|
@@ -100,93 +242,176 @@ module ATP
|
|
100
242
|
end
|
101
243
|
if f = options.delete(:flag_pass)
|
102
244
|
options[:on_pass] ||= {}
|
103
|
-
options[:on_pass][:
|
245
|
+
options[:on_pass][:set_flag] = f
|
104
246
|
end
|
105
247
|
if f = options.delete(:flag_fail)
|
106
248
|
options[:on_fail] ||= {}
|
107
|
-
options[:on_fail][:
|
249
|
+
options[:on_fail][:set_flag] = f
|
250
|
+
end
|
251
|
+
|
252
|
+
children = [n1(:object, instance)]
|
253
|
+
|
254
|
+
name = (options[:name] || options[:tname] || options[:test_name])
|
255
|
+
unless name
|
256
|
+
[:name, :tname, :test_name].each do |m|
|
257
|
+
name ||= instance.respond_to?(m) ? instance.send(m) : nil
|
258
|
+
end
|
259
|
+
end
|
260
|
+
children << n1(:name, name) if name
|
261
|
+
|
262
|
+
num = (options[:number] || options[:num] || options[:tnum] || options[:test_number])
|
263
|
+
unless num
|
264
|
+
[:number, :num, :tnum, :test_number].each do |m|
|
265
|
+
num ||= instance.respond_to?(m) ? instance.send(m) : nil
|
266
|
+
end
|
267
|
+
end
|
268
|
+
children << number(num) if num
|
269
|
+
|
270
|
+
children << id(options[:id]) if options[:id]
|
271
|
+
|
272
|
+
if levels = options[:level] || options[:levels]
|
273
|
+
levels = [levels] unless levels.is_a?(Array)
|
274
|
+
levels.each do |l|
|
275
|
+
children << level(l[:name], l[:value], l[:unit] || l[:units])
|
276
|
+
end
|
277
|
+
end
|
278
|
+
|
279
|
+
if lims = options[:limit] || options[:limits]
|
280
|
+
lims = [lims] unless lims.is_a?(Array)
|
281
|
+
lims.each do |l|
|
282
|
+
if l.is_a?(Hash)
|
283
|
+
children << limit(l[:value], l[:rule], l[:unit] || l[:units])
|
284
|
+
end
|
285
|
+
end
|
286
|
+
end
|
287
|
+
|
288
|
+
if pins = options[:pin] || options[:pins]
|
289
|
+
pins = [pins] unless pins.is_a?(Array)
|
290
|
+
pins.each do |p|
|
291
|
+
if p.is_a?(Hash)
|
292
|
+
children << pin(p[:name])
|
293
|
+
else
|
294
|
+
children << pin(p)
|
295
|
+
end
|
296
|
+
end
|
297
|
+
end
|
298
|
+
|
299
|
+
if pats = options[:pattern] || options[:patterns]
|
300
|
+
pats = [pats] unless pats.is_a?(Array)
|
301
|
+
pats.each do |p|
|
302
|
+
if p.is_a?(Hash)
|
303
|
+
children << pattern(p[:name], p[:path])
|
304
|
+
else
|
305
|
+
children << pattern(p)
|
306
|
+
end
|
307
|
+
end
|
108
308
|
end
|
109
|
-
|
309
|
+
|
310
|
+
if options[:meta]
|
311
|
+
attrs = []
|
312
|
+
options[:meta].each { |k, v| attrs << attribute(k, v) }
|
313
|
+
children << n(:meta, attrs)
|
314
|
+
end
|
315
|
+
|
316
|
+
if subs = options[:sub_test] || options[:sub_tests]
|
317
|
+
subs = [subs] unless subs.is_a?(Array)
|
318
|
+
subs.each do |s|
|
319
|
+
children << s.updated(:sub_test, nil)
|
320
|
+
end
|
321
|
+
end
|
322
|
+
|
323
|
+
children << on_fail(options[:on_fail]) if options[:on_fail]
|
324
|
+
children << on_pass(options[:on_pass]) if options[:on_pass]
|
325
|
+
|
326
|
+
save_conditions
|
327
|
+
n(:test, children)
|
110
328
|
end
|
111
|
-
append(t) unless r
|
112
|
-
t
|
113
329
|
end
|
114
330
|
|
115
331
|
# Equivalent to calling test, but returns a sub_test node instead of adding it to the flow.
|
116
|
-
# It will also ignore any condition nodes that would normally wrap the equivalent flow.test call.
|
117
332
|
#
|
118
333
|
# This is a helper to create sub_tests for inclusion in a top-level test node.
|
119
334
|
def sub_test(instance, options = {})
|
120
|
-
|
121
|
-
|
122
|
-
test(instance, options)
|
335
|
+
temp = append_to(n0(:temp)) { test(instance, options) }
|
336
|
+
temp.children.first.updated(:sub_test, nil)
|
123
337
|
end
|
124
338
|
|
125
339
|
def bin(number, options = {})
|
340
|
+
if number.is_a?(Hash)
|
341
|
+
fail 'The bin number must be passed as the first argument'
|
342
|
+
end
|
343
|
+
options[:bin_description] ||= options.delete(:description)
|
126
344
|
extract_meta!(options)
|
127
|
-
|
128
|
-
|
345
|
+
apply_conditions(options) do
|
346
|
+
options[:type] ||= :fail
|
129
347
|
options[:bin] = number
|
130
348
|
options[:softbin] ||= options[:soft_bin] || options[:sbin]
|
131
|
-
|
349
|
+
set_result(options[:type], options)
|
132
350
|
end
|
133
|
-
|
351
|
+
end
|
352
|
+
|
353
|
+
def pass(number, options = {})
|
354
|
+
if number.is_a?(Hash)
|
355
|
+
fail 'The bin number must be passed as the first argument'
|
356
|
+
end
|
357
|
+
options[:type] = :pass
|
358
|
+
bin(number, options)
|
134
359
|
end
|
135
360
|
|
136
361
|
def cz(instance, cz_setup, options = {})
|
137
362
|
extract_meta!(options)
|
138
|
-
|
139
|
-
|
140
|
-
options
|
141
|
-
builder.cz(cz_setup, test(instance, options.merge(dont_apply_conditions: true)), conditions: conditions)
|
363
|
+
apply_conditions(options) do
|
364
|
+
node = n1(:cz, cz_setup)
|
365
|
+
append_to(node) { test(instance, options) }
|
142
366
|
end
|
143
|
-
append(t)
|
144
367
|
end
|
145
368
|
alias_method :characterize, :cz
|
146
369
|
|
147
370
|
# Append a log message line to the flow
|
148
371
|
def log(message, options = {})
|
149
372
|
extract_meta!(options)
|
150
|
-
|
151
|
-
|
373
|
+
apply_conditions(options) do
|
374
|
+
n1(:log, message.to_s)
|
152
375
|
end
|
153
|
-
append(t)
|
154
376
|
end
|
155
377
|
|
156
378
|
# Enable a flow control variable
|
157
379
|
def enable(var, options = {})
|
158
380
|
extract_meta!(options)
|
159
|
-
|
160
|
-
|
381
|
+
apply_conditions(options) do
|
382
|
+
n1(:enable, var)
|
161
383
|
end
|
162
|
-
append(t)
|
163
384
|
end
|
164
385
|
|
165
386
|
# Disable a flow control variable
|
166
387
|
def disable(var, options = {})
|
167
388
|
extract_meta!(options)
|
168
|
-
|
169
|
-
|
389
|
+
apply_conditions(options) do
|
390
|
+
n1(:disable, var)
|
391
|
+
end
|
392
|
+
end
|
393
|
+
|
394
|
+
def set_flag(flag, options = {})
|
395
|
+
extract_meta!(options)
|
396
|
+
apply_conditions(options) do
|
397
|
+
set_flag_node(flag)
|
170
398
|
end
|
171
|
-
append(t)
|
172
399
|
end
|
173
400
|
|
174
401
|
# Insert explicitly rendered content in to the flow
|
175
402
|
def render(str, options = {})
|
176
403
|
extract_meta!(options)
|
177
|
-
|
178
|
-
|
404
|
+
apply_conditions(options) do
|
405
|
+
n1(:render, str)
|
179
406
|
end
|
180
|
-
append(t)
|
181
407
|
end
|
182
408
|
|
183
|
-
def
|
409
|
+
def continue(options = {})
|
184
410
|
extract_meta!(options)
|
185
|
-
|
186
|
-
|
187
|
-
|
411
|
+
apply_conditions(options) do
|
412
|
+
n0(:continue)
|
413
|
+
end
|
188
414
|
end
|
189
|
-
alias_method :with_conditions, :with_condition
|
190
415
|
|
191
416
|
# Execute the given flow in the console
|
192
417
|
def run(options = {})
|
@@ -197,139 +422,321 @@ module ATP
|
|
197
422
|
# Returns true if the test context generated from the supplied options + existing condition
|
198
423
|
# wrappers, is different from that which was applied to the previous test.
|
199
424
|
def context_changed?(options)
|
200
|
-
|
201
|
-
|
202
|
-
!context_equal?(a, b)
|
425
|
+
options[:_dont_delete_conditions_] = true
|
426
|
+
last_conditions != clean_conditions(open_conditions + [extract_conditions(options)])
|
203
427
|
end
|
204
428
|
|
205
|
-
|
206
|
-
|
429
|
+
# Define handlers for all of the flow control block methods, unless a custom one has already
|
430
|
+
# been defined above
|
431
|
+
CONDITION_KEYS.keys.each do |method|
|
432
|
+
define_method method do |*flags, &block|
|
433
|
+
if flags.last.is_a?(Hash)
|
434
|
+
options = flags.pop
|
435
|
+
else
|
436
|
+
options = {}
|
437
|
+
end
|
438
|
+
flags = flags.first if flags.size == 1
|
439
|
+
# Legacy option provided by OrigenTesters that permits override of a block enable method by passing
|
440
|
+
# an :or option with a true value
|
441
|
+
if (CONDITION_KEYS[method] == :if_enabled || CONDITION_KEYS[method] || :unless_enabled) && options[:or]
|
442
|
+
block.call
|
443
|
+
else
|
444
|
+
flow_control_method(CONDITION_KEYS[method], flags, options, &block)
|
445
|
+
end
|
446
|
+
end unless method_defined?(method)
|
207
447
|
end
|
208
448
|
|
209
|
-
def
|
210
|
-
|
211
|
-
a = clean_condition(a[:conditions])
|
212
|
-
b = clean_condition(b[:conditions])
|
213
|
-
if a.keys.sort == b.keys.sort
|
214
|
-
a.all? do |key, value|
|
215
|
-
value.flatten.uniq.sort == b[key].flatten.uniq.sort
|
216
|
-
end
|
217
|
-
end
|
218
|
-
end
|
449
|
+
def inspect
|
450
|
+
"<ATP::Flow:#{object_id} #{name}>"
|
219
451
|
end
|
220
452
|
|
221
453
|
private
|
222
454
|
|
223
|
-
def
|
224
|
-
|
225
|
-
|
226
|
-
|
227
|
-
|
228
|
-
|
229
|
-
|
230
|
-
|
455
|
+
def flow_control_method(name, flag, options = {}, &block)
|
456
|
+
extract_meta!(options)
|
457
|
+
if flag.is_a?(Array)
|
458
|
+
if name == :if_passed
|
459
|
+
fail 'if_passed only accepts one ID, use if_any_passed or if_all_passed for multiple IDs'
|
460
|
+
end
|
461
|
+
if name == :if_failed
|
462
|
+
fail 'if_failed only accepts one ID, use if_any_failed or if_all_failed for multiple IDs'
|
463
|
+
end
|
464
|
+
end
|
465
|
+
apply_conditions(options) do
|
466
|
+
if block
|
467
|
+
node = n1(name, flag)
|
468
|
+
open_conditions << [name, flag]
|
469
|
+
node = append_to(node) { block.call }
|
470
|
+
open_conditions.pop
|
471
|
+
else
|
472
|
+
unless options[:then] || options[:else]
|
473
|
+
fail "You must supply a :then or :else option when calling #{name} like this!"
|
474
|
+
end
|
475
|
+
node = n1(name, flag)
|
476
|
+
if options[:then]
|
477
|
+
node = append_to(node) { options[:then].call }
|
478
|
+
end
|
479
|
+
if options[:else]
|
480
|
+
e = n0(:else)
|
481
|
+
e = append_to(e) { options[:else].call }
|
482
|
+
node = node.updated(nil, node.children + [e])
|
483
|
+
end
|
484
|
+
end
|
485
|
+
node
|
231
486
|
end
|
232
|
-
c
|
233
487
|
end
|
234
488
|
|
235
|
-
def
|
236
|
-
|
237
|
-
|
489
|
+
def apply_conditions(options, node = nil)
|
490
|
+
# Applying the current context, means to append to the same node as the last time, this
|
491
|
+
# means that the next node will pick up the exact same condition context as the previous one
|
492
|
+
if options[:context] == :current
|
493
|
+
node = yield
|
494
|
+
found = false
|
495
|
+
@pipeline = @pipeline.map do |parent|
|
496
|
+
p = Processors::AppendTo.new
|
497
|
+
n = p.run(parent, node, @last_append.id)
|
498
|
+
found ||= p.succeeded?
|
499
|
+
n
|
500
|
+
end
|
501
|
+
unless found
|
502
|
+
fail 'The request to apply the current context has failed, this is likely a bug in the ATP plugin'
|
503
|
+
end
|
504
|
+
node
|
238
505
|
else
|
239
|
-
|
506
|
+
conditions = extract_conditions(options)
|
507
|
+
open_conditions << conditions
|
508
|
+
node = yield
|
509
|
+
open_conditions.pop
|
510
|
+
|
511
|
+
update_last_append = !condition_node?(node)
|
512
|
+
|
513
|
+
conditions.each do |key, value|
|
514
|
+
if key == :group
|
515
|
+
node = n2(key, n1(:name, value.to_s), node)
|
516
|
+
else
|
517
|
+
node = n2(key, value, node)
|
518
|
+
end
|
519
|
+
if update_last_append
|
520
|
+
@last_append = node
|
521
|
+
update_last_append = false
|
522
|
+
end
|
523
|
+
end
|
524
|
+
|
525
|
+
append(node)
|
526
|
+
node
|
240
527
|
end
|
241
528
|
end
|
242
529
|
|
243
|
-
def
|
244
|
-
|
245
|
-
|
246
|
-
|
247
|
-
|
248
|
-
|
249
|
-
|
250
|
-
|
251
|
-
|
252
|
-
|
253
|
-
|
254
|
-
|
255
|
-
|
256
|
-
|
257
|
-
|
258
|
-
|
259
|
-
|
260
|
-
|
261
|
-
|
262
|
-
|
263
|
-
|
264
|
-
|
265
|
-
|
266
|
-
|
267
|
-
when :unless_job, :unless_jobs
|
268
|
-
:unless_job
|
269
|
-
else
|
270
|
-
fail "Unknown test condition attribute - #{key}"
|
530
|
+
def save_conditions
|
531
|
+
@last_conditions = clean_conditions(open_conditions.dup)
|
532
|
+
end
|
533
|
+
|
534
|
+
def last_conditions
|
535
|
+
@last_conditions || {}
|
536
|
+
end
|
537
|
+
|
538
|
+
def open_conditions
|
539
|
+
@open_conditions ||= []
|
540
|
+
end
|
541
|
+
|
542
|
+
def clean_conditions(conditions)
|
543
|
+
result = {}.with_indifferent_access
|
544
|
+
conditions.each do |cond|
|
545
|
+
if cond.is_a?(Array)
|
546
|
+
if cond.size != 2
|
547
|
+
fail 'Something has gone wrong in ATP!'
|
548
|
+
else
|
549
|
+
result[cond[0]] = cond[1].to_s if cond[1]
|
550
|
+
end
|
551
|
+
else
|
552
|
+
cond.each { |k, v| result[k] = v.to_s if v }
|
553
|
+
end
|
271
554
|
end
|
555
|
+
result
|
272
556
|
end
|
273
557
|
|
274
|
-
def
|
275
|
-
|
276
|
-
|
277
|
-
|
278
|
-
|
558
|
+
def extract_conditions(options)
|
559
|
+
conditions = {}
|
560
|
+
delete_from_options = !options.delete(:_dont_delete_conditions_)
|
561
|
+
options.each do |key, value|
|
562
|
+
if CONDITION_KEYS[key]
|
563
|
+
options.delete(key) if delete_from_options
|
564
|
+
key = CONDITION_KEYS[key]
|
565
|
+
if conditions[key] && value
|
566
|
+
fail "Multiple values assigned to flow condition #{key}" unless conditions[key] == value
|
567
|
+
else
|
568
|
+
conditions[key] = value if value
|
569
|
+
end
|
279
570
|
end
|
280
571
|
end
|
281
|
-
|
572
|
+
conditions
|
573
|
+
end
|
574
|
+
|
575
|
+
def append(node)
|
576
|
+
@last_append = @pipeline.last unless condition_node?(node)
|
577
|
+
n = @pipeline.pop
|
578
|
+
@pipeline << n.updated(nil, n.children + [node])
|
579
|
+
@pipeline.last
|
580
|
+
end
|
581
|
+
|
582
|
+
# Append all nodes generated within the given block to the given node
|
583
|
+
# instead of the top-level flow node
|
584
|
+
def append_to(node)
|
585
|
+
@pipeline << node
|
586
|
+
yield
|
587
|
+
@pipeline.pop
|
588
|
+
end
|
589
|
+
|
590
|
+
def condition_node?(node)
|
591
|
+
!!CONDITION_KEYS[node.type]
|
592
|
+
end
|
593
|
+
|
594
|
+
def extract_meta!(options)
|
595
|
+
self.source_file = options.delete(:source_file)
|
596
|
+
self.source_line_number = options.delete(:source_line_number)
|
597
|
+
self.description = options.delete(:description)
|
282
598
|
end
|
283
599
|
|
284
|
-
def
|
285
|
-
|
600
|
+
def id(name)
|
601
|
+
n1(:id, name)
|
286
602
|
end
|
287
603
|
|
288
|
-
def
|
289
|
-
if options
|
290
|
-
|
604
|
+
def on_fail(options = {})
|
605
|
+
if options.is_a?(Proc)
|
606
|
+
node = n0(:on_fail)
|
607
|
+
append_to(node) { options.call }
|
291
608
|
else
|
292
|
-
|
293
|
-
|
609
|
+
children = []
|
610
|
+
if options[:bin] || options[:softbin]
|
611
|
+
fail_opts = { bin: options[:bin], softbin: options[:softbin] }
|
612
|
+
fail_opts[:bin_description] = options[:bin_description] if options[:bin_description]
|
613
|
+
fail_opts[:softbin_description] = options[:softbin_description] if options[:softbin_description]
|
614
|
+
children << set_result(:fail, fail_opts)
|
294
615
|
end
|
295
|
-
|
296
|
-
|
297
|
-
unless options[:context] == :current
|
298
|
-
unless options[:dont_apply_conditions]
|
299
|
-
open_conditions.each do |conditions|
|
300
|
-
t = builder.apply_conditions(t, conditions)
|
301
|
-
end
|
302
|
-
end
|
616
|
+
if options[:set_run_flag] || options[:set_flag]
|
617
|
+
children << set_flag_node(options[:set_run_flag] || options[:set_flag])
|
303
618
|
end
|
304
|
-
|
619
|
+
children << n0(:continue) if options[:continue]
|
620
|
+
children << n1(:render, options[:render]) if options[:render]
|
621
|
+
n(:on_fail, children)
|
305
622
|
end
|
306
623
|
end
|
307
624
|
|
308
|
-
def
|
309
|
-
|
310
|
-
|
311
|
-
|
625
|
+
def on_pass(options = {})
|
626
|
+
if options.is_a?(Proc)
|
627
|
+
node = n0(:on_pass)
|
628
|
+
append_to(node) { options.call }
|
629
|
+
else
|
630
|
+
children = []
|
631
|
+
if options[:bin] || options[:softbin]
|
632
|
+
pass_opts = { bin: options[:bin], softbin: options[:softbin] }
|
633
|
+
pass_opts[:bin_description] = options[:bin_description] if options[:bin_description]
|
634
|
+
pass_opts[:softbin_description] = options[:softbin_description] if options[:softbin_description]
|
635
|
+
children << set_result(:pass, pass_opts)
|
636
|
+
end
|
637
|
+
if options[:set_run_flag] || options[:set_flag]
|
638
|
+
children << set_flag_node(options[:set_run_flag] || options[:set_flag])
|
639
|
+
end
|
640
|
+
children << n0(:continue) if options[:continue]
|
641
|
+
children << n1(:render, options[:render]) if options[:render]
|
642
|
+
n(:on_pass, children)
|
643
|
+
end
|
312
644
|
end
|
313
645
|
|
314
|
-
|
315
|
-
|
316
|
-
|
646
|
+
def pattern(name, path = nil)
|
647
|
+
if path
|
648
|
+
n2(:pattern, name, path)
|
649
|
+
else
|
650
|
+
n1(:pattern, name)
|
651
|
+
end
|
317
652
|
end
|
318
653
|
|
319
|
-
def
|
320
|
-
|
654
|
+
def attribute(name, value)
|
655
|
+
n2(:attribute, name, value)
|
321
656
|
end
|
322
657
|
|
323
|
-
def
|
324
|
-
|
658
|
+
def level(name, value, units = nil)
|
659
|
+
if units
|
660
|
+
n(:level, [name, value, units])
|
661
|
+
else
|
662
|
+
n2(:level, name, value)
|
663
|
+
end
|
325
664
|
end
|
326
665
|
|
327
|
-
def
|
328
|
-
if
|
329
|
-
|
666
|
+
def limit(value, rule, units = nil)
|
667
|
+
if units
|
668
|
+
n(:limit, [value, rule, units])
|
669
|
+
else
|
670
|
+
n2(:limit, value, rule)
|
671
|
+
end
|
672
|
+
end
|
673
|
+
|
674
|
+
def pin(name)
|
675
|
+
n1(:pin, name)
|
676
|
+
end
|
677
|
+
|
678
|
+
def set_result(type, options = {})
|
679
|
+
children = []
|
680
|
+
children << type
|
681
|
+
if options[:bin] && options[:bin_description]
|
682
|
+
children << n2(:bin, options[:bin], options[:bin_description])
|
330
683
|
else
|
331
|
-
|
684
|
+
children << n1(:bin, options[:bin]) if options[:bin]
|
332
685
|
end
|
686
|
+
if options[:softbin] && options[:softbin_description]
|
687
|
+
children << n2(:softbin, options[:softbin], options[:softbin_description])
|
688
|
+
else
|
689
|
+
children << n1(:softbin, options[:softbin]) if options[:softbin]
|
690
|
+
end
|
691
|
+
n(:set_result, children)
|
692
|
+
end
|
693
|
+
|
694
|
+
def number(val)
|
695
|
+
n1(:number, val.to_i)
|
696
|
+
end
|
697
|
+
|
698
|
+
def set_flag_node(flag)
|
699
|
+
n1(:set_flag, flag)
|
700
|
+
end
|
701
|
+
|
702
|
+
# Ensures the flow ast has a volatile node, then adds the
|
703
|
+
# given flags to it
|
704
|
+
def add_volatile_flags(node, flags)
|
705
|
+
name, *nodes = *node
|
706
|
+
if nodes[0] && nodes[0].type == :volatile
|
707
|
+
v = nodes.shift
|
708
|
+
else
|
709
|
+
v = n0(:volatile)
|
710
|
+
end
|
711
|
+
existing = v.children.map { |f| f.type == :flag ? f.value : nil }.compact
|
712
|
+
new = []
|
713
|
+
flags.each do |flag|
|
714
|
+
new << n1(:flag, flag) unless existing.include?(flag)
|
715
|
+
end
|
716
|
+
v = v.updated(nil, v.children + new)
|
717
|
+
node.updated(nil, [name, v] + nodes)
|
718
|
+
end
|
719
|
+
|
720
|
+
def n(type, children, options = {})
|
721
|
+
options[:file] ||= options.delete(:source_file) || source_file
|
722
|
+
options[:line_number] ||= options.delete(:source_line_number) || source_line_number
|
723
|
+
options[:description] ||= options.delete(:description) || description
|
724
|
+
# Guarantee that each node has a unique meta-ID, in case we need to ever search
|
725
|
+
# for it
|
726
|
+
options[:id] = ATP.next_id
|
727
|
+
ATP::AST::Node.new(type, children, options)
|
728
|
+
end
|
729
|
+
|
730
|
+
def n0(type, options = {})
|
731
|
+
n(type, [], options)
|
732
|
+
end
|
733
|
+
|
734
|
+
def n1(type, arg, options = {})
|
735
|
+
n(type, [arg], options)
|
736
|
+
end
|
737
|
+
|
738
|
+
def n2(type, arg1, arg2, options = {})
|
739
|
+
n(type, [arg1, arg2], options)
|
333
740
|
end
|
334
741
|
end
|
335
742
|
end
|