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,56 @@
|
|
1
|
+
module OrigenTesters::ATP
|
2
|
+
module FlowAPI
|
3
|
+
def atp=(atp)
|
4
|
+
@atp = atp
|
5
|
+
end
|
6
|
+
|
7
|
+
def atp
|
8
|
+
@atp
|
9
|
+
end
|
10
|
+
|
11
|
+
([:test, :bin, :pass, :continue, :cz, :log, :sub_test, :volatile, :set_flag, :set, :enable, :disable, :render,
|
12
|
+
:context_changed?, :ids, :describe_bin, :describe_softbin, :describe_soft_bin, :loop] +
|
13
|
+
OrigenTesters::ATP::Flow::CONDITION_KEYS.keys + OrigenTesters::ATP::Flow::RELATIONAL_OPERATORS).each do |method|
|
14
|
+
define_method method do |*args, &block|
|
15
|
+
options = args.pop if args.last.is_a?(Hash)
|
16
|
+
options ||= {}
|
17
|
+
add_meta!(options) if respond_to?(:add_meta!, true)
|
18
|
+
add_description!(options) if respond_to?(:add_description!, true)
|
19
|
+
args << options
|
20
|
+
atp.send(method, *args, &block)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
alias_method :logprint, :log
|
25
|
+
|
26
|
+
def lo_limit(value, options)
|
27
|
+
{
|
28
|
+
value: value,
|
29
|
+
rule: options[:rule] || :gte,
|
30
|
+
units: options[:units],
|
31
|
+
selector: options[:selector] || options[:test_mode]
|
32
|
+
}
|
33
|
+
end
|
34
|
+
|
35
|
+
def hi_limit(value, options)
|
36
|
+
{
|
37
|
+
value: value,
|
38
|
+
rule: options[:rule] || :lte,
|
39
|
+
units: options[:units],
|
40
|
+
selector: options[:selector] || options[:test_mode]
|
41
|
+
}
|
42
|
+
end
|
43
|
+
|
44
|
+
def limit(value, options)
|
45
|
+
unless options[:rule]
|
46
|
+
fail 'You must supply option :rule (e.g. rule: :gt) when calling the limit helper'
|
47
|
+
end
|
48
|
+
{
|
49
|
+
value: value,
|
50
|
+
rule: options[:rule] || :lt,
|
51
|
+
units: options[:units],
|
52
|
+
selector: options[:selector] || options[:test_mode]
|
53
|
+
}
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
module OrigenTesters::ATP
|
2
|
+
class Formatter < Processor
|
3
|
+
def format(node, options = {})
|
4
|
+
process(node)
|
5
|
+
end
|
6
|
+
|
7
|
+
def run_and_format(node, options = {})
|
8
|
+
ast = Runner.new.run(node, options)
|
9
|
+
format(ast, options)
|
10
|
+
end
|
11
|
+
|
12
|
+
def self.format(node, options = {})
|
13
|
+
new.format(node, options)
|
14
|
+
end
|
15
|
+
|
16
|
+
def self.run_and_format(node, options = {})
|
17
|
+
ast = Runner.new.run(node, options)
|
18
|
+
format(ast, options)
|
19
|
+
end
|
20
|
+
|
21
|
+
def self.run(*args)
|
22
|
+
run_and_format(*args)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
module OrigenTesters::ATP
|
2
|
+
module Formatters
|
3
|
+
# Returns the executed flow as a string of test names. This
|
4
|
+
# is mainly intended to be used for testing the runner.
|
5
|
+
class Basic < Formatter
|
6
|
+
def format(node, options = {})
|
7
|
+
@output = ''
|
8
|
+
process(node)
|
9
|
+
@output
|
10
|
+
end
|
11
|
+
|
12
|
+
def on_test(node)
|
13
|
+
if node.find(:name)
|
14
|
+
@output += node.find(:name).value
|
15
|
+
else
|
16
|
+
obj = node.find(:object).value
|
17
|
+
obj = obj['Test'] unless obj.is_a?(String)
|
18
|
+
@output += obj
|
19
|
+
end
|
20
|
+
@output += ' F' if node.find(:failed)
|
21
|
+
@output += "\n"
|
22
|
+
end
|
23
|
+
|
24
|
+
def on_set_result(node)
|
25
|
+
@output += node.to_a[0].upcase
|
26
|
+
@output += " #{node.find(:bin).value}" if node.find(:bin)
|
27
|
+
@output += " #{node.find(:softbin).value}" if node.find(:softbin)
|
28
|
+
@output += "\n"
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,65 @@
|
|
1
|
+
require 'colored'
|
2
|
+
module OrigenTesters::ATP
|
3
|
+
module Formatters
|
4
|
+
# Outputs the given AST to something resembling an ATE datalog,
|
5
|
+
# this can optionally be rendered to a file or the console (the default).
|
6
|
+
class Datalog < Formatter
|
7
|
+
def on_flow(node)
|
8
|
+
str = 'Number'.ljust(15)
|
9
|
+
str += 'Result'.ljust(9)
|
10
|
+
str += 'Name'.ljust(55)
|
11
|
+
str += 'Pattern'.ljust(55)
|
12
|
+
str += 'ID'
|
13
|
+
puts str
|
14
|
+
process_all(node.children)
|
15
|
+
end
|
16
|
+
|
17
|
+
def on_test(node)
|
18
|
+
str = "#{node.find(:number).try(:value)}".ljust(15)
|
19
|
+
if node.find(:failed)
|
20
|
+
str += 'FAIL'.ljust(9).red
|
21
|
+
else
|
22
|
+
str += 'PASS'.ljust(9)
|
23
|
+
end
|
24
|
+
if n = node.find(:name)
|
25
|
+
name = n.value
|
26
|
+
else
|
27
|
+
name = node.find(:object).value['Test']
|
28
|
+
end
|
29
|
+
str += "#{name}".ljust(55)
|
30
|
+
str += "#{node.find(:object).value['Pattern']}".ljust(55)
|
31
|
+
str += "#{node.find(:id).value}"
|
32
|
+
puts str
|
33
|
+
end
|
34
|
+
|
35
|
+
def on_render(node)
|
36
|
+
puts '************ Directly rendered flow snippet ************'
|
37
|
+
puts node.value
|
38
|
+
puts '********************************************************'
|
39
|
+
end
|
40
|
+
|
41
|
+
def on_log(node)
|
42
|
+
puts "// #{node.value}"
|
43
|
+
end
|
44
|
+
|
45
|
+
def on_set_result(node)
|
46
|
+
bin = node.find(:bin).try(:value)
|
47
|
+
sbin = node.find(:softbin).try(:value)
|
48
|
+
desc = node.find(:description).try(:value)
|
49
|
+
|
50
|
+
if node.to_a[0] == 'pass'
|
51
|
+
str = " PASS #{bin} #{sbin}"
|
52
|
+
color = :green
|
53
|
+
else
|
54
|
+
str = " FAIL #{bin} #{sbin}"
|
55
|
+
color = :red
|
56
|
+
end
|
57
|
+
str += " (#{desc})" if desc
|
58
|
+
|
59
|
+
puts '---------------------------------------------------------------------------------------------------------------------------------------------------------'
|
60
|
+
puts str.send(color)
|
61
|
+
puts '---------------------------------------------------------------------------------------------------------------------------------------------------------'
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
require 'sexpistol'
|
2
|
+
module OrigenTesters::ATP
|
3
|
+
class Parser < Sexpistol
|
4
|
+
def initialize
|
5
|
+
self.ruby_keyword_literals = true
|
6
|
+
end
|
7
|
+
|
8
|
+
def string_to_ast(string)
|
9
|
+
to_sexp(parse_string(string))
|
10
|
+
end
|
11
|
+
|
12
|
+
def to_sexp(ast_array)
|
13
|
+
children = ast_array.map do |item|
|
14
|
+
if item.is_a?(Array)
|
15
|
+
to_sexp(item)
|
16
|
+
else
|
17
|
+
item
|
18
|
+
end
|
19
|
+
end
|
20
|
+
type = children.shift
|
21
|
+
return type if type.is_a?(OrigenTesters::ATP::AST::Node)
|
22
|
+
type = type.to_s.gsub('-', '_').to_sym
|
23
|
+
OrigenTesters::ATP::AST::Node.new(type, children)
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,73 @@
|
|
1
|
+
require 'ast'
|
2
|
+
module OrigenTesters::ATP
|
3
|
+
# The base processor, this provides a default handler for
|
4
|
+
# all node types and will not make any changes to the AST,
|
5
|
+
# i.e. an equivalent AST will be returned by the process method.
|
6
|
+
#
|
7
|
+
# Child classes of this should be used to implement additional
|
8
|
+
# processors to modify or otherwise work with the AST.
|
9
|
+
#
|
10
|
+
# @see http://www.rubydoc.info/gems/ast/2.0.0/AST/Processor
|
11
|
+
class Processor
|
12
|
+
include ::AST::Processor::Mixin
|
13
|
+
|
14
|
+
def run(node)
|
15
|
+
process(node)
|
16
|
+
end
|
17
|
+
|
18
|
+
def process(node)
|
19
|
+
if node.respond_to?(:to_ast)
|
20
|
+
super(node)
|
21
|
+
else
|
22
|
+
node
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
# Some of our processors remove a wrapping node from the AST, returning
|
27
|
+
# a node of type :inline containing the children which should be inlined.
|
28
|
+
# Here we override the default version of this method to deal with handlers
|
29
|
+
# that return an inline node in place of a regular node.
|
30
|
+
def process_all(nodes)
|
31
|
+
results = []
|
32
|
+
nodes.to_a.each do |node|
|
33
|
+
n = process(node)
|
34
|
+
if n.respond_to?(:type) && n.type == :inline
|
35
|
+
results += n.children
|
36
|
+
else
|
37
|
+
results << n unless n.respond_to?(:type) && n.type == :remove
|
38
|
+
end
|
39
|
+
end
|
40
|
+
results
|
41
|
+
end
|
42
|
+
|
43
|
+
def handler_missing(node)
|
44
|
+
node.updated(nil, process_all(node.children))
|
45
|
+
end
|
46
|
+
|
47
|
+
def extract_volatiles(flow)
|
48
|
+
@volatiles = {}
|
49
|
+
if v = flow.find(:volatile)
|
50
|
+
@volatiles[:flags] = Array(v.find_all(:flag)).map(&:value)
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
def volatile_flags
|
55
|
+
unless @volatiles
|
56
|
+
fail 'You must first call extract_volatiles(node) from your on_flow hander method'
|
57
|
+
end
|
58
|
+
@volatiles[:flags] || []
|
59
|
+
end
|
60
|
+
|
61
|
+
# Returns true if the given flag name has been marked as volatile
|
62
|
+
def volatile?(flag)
|
63
|
+
result = volatile_flags.any? { |f| clean_flag(f) == clean_flag(flag) }
|
64
|
+
result
|
65
|
+
end
|
66
|
+
|
67
|
+
def clean_flag(flag)
|
68
|
+
flag = flag.dup.to_s
|
69
|
+
flag[0] = '' if flag[0] == '$'
|
70
|
+
flag.downcase
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
@@ -0,0 +1,45 @@
|
|
1
|
+
module OrigenTesters::ATP
|
2
|
+
module Processors
|
3
|
+
# Assigns an ID to all test nodes that don't have one
|
4
|
+
class AddIDs < Processor
|
5
|
+
def run(node)
|
6
|
+
@i = 0
|
7
|
+
@existing_ids = []
|
8
|
+
@add_ids = false
|
9
|
+
# First collect all existing IDs, this is required to make sure
|
10
|
+
# that a generated ID does not clash with an existing one
|
11
|
+
process(node)
|
12
|
+
# Now run again to fill in the blanks
|
13
|
+
@add_ids = true
|
14
|
+
process(node)
|
15
|
+
end
|
16
|
+
|
17
|
+
def on_test(node)
|
18
|
+
if @add_ids
|
19
|
+
node = node.ensure_node_present(:id)
|
20
|
+
node.updated(nil, process_all(node))
|
21
|
+
else
|
22
|
+
if id = node.find(:id)
|
23
|
+
@existing_ids << id.value
|
24
|
+
end
|
25
|
+
process_all(node)
|
26
|
+
end
|
27
|
+
end
|
28
|
+
alias_method :on_group, :on_test
|
29
|
+
|
30
|
+
def on_id(node)
|
31
|
+
if @add_ids
|
32
|
+
unless node.value
|
33
|
+
node.updated(nil, [next_id])
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
def next_id
|
39
|
+
@i += 1
|
40
|
+
@i += 1 while @existing_ids.include?("t#{@i}")
|
41
|
+
"t#{@i}"
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
module OrigenTesters::ATP
|
2
|
+
module Processors
|
3
|
+
# Makes sure every test node has an on_fail/set_result node,
|
4
|
+
class AddSetResult < Processor
|
5
|
+
def run(node)
|
6
|
+
process(node)
|
7
|
+
end
|
8
|
+
|
9
|
+
def on_test(node)
|
10
|
+
node = node.ensure_node_present(:on_fail)
|
11
|
+
node.updated(nil, process_all(node))
|
12
|
+
end
|
13
|
+
|
14
|
+
def on_on_fail(node)
|
15
|
+
unless node.find(:continue)
|
16
|
+
node = node.ensure_node_present(:set_result, 'fail')
|
17
|
+
end
|
18
|
+
node.updated(nil, process_all(node))
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,100 @@
|
|
1
|
+
module OrigenTesters::ATP
|
2
|
+
module Processors
|
3
|
+
# This combines adjacent if flag nodes where the flag is in the opposite state
|
4
|
+
#
|
5
|
+
# s(:flow,
|
6
|
+
# s(:name, "prb1"),
|
7
|
+
# s(:if_flag, "SOME_FLAG",
|
8
|
+
# s(:test,
|
9
|
+
# s(:name, "test1"))),
|
10
|
+
# s(:unless_flag, "SOME_FLAG",
|
11
|
+
# s(:test,
|
12
|
+
# s(:name, "test2"))))
|
13
|
+
#
|
14
|
+
# s(:flow,
|
15
|
+
# s(:name, "prb1"),
|
16
|
+
# s(:if_flag, "SOME_FLAG",
|
17
|
+
# s(:test,
|
18
|
+
# s(:name, "test1"))),
|
19
|
+
# s(:else,
|
20
|
+
# s(:test,
|
21
|
+
# s(:name, "test2"))))
|
22
|
+
#
|
23
|
+
# See here for an example of the kind of flow level effect it has:
|
24
|
+
# https://github.com/Origen-SDK/origen_testers/issues/43
|
25
|
+
class AdjacentIfCombiner < OrigenTesters::ATP::Processor
|
26
|
+
class SetRunFlagFinder < OrigenTesters::ATP::Processor
|
27
|
+
def contains?(node, flag_name)
|
28
|
+
@result = false
|
29
|
+
@flag_name = flag_name
|
30
|
+
process_all(node)
|
31
|
+
@result
|
32
|
+
end
|
33
|
+
|
34
|
+
def on_set_flag(node)
|
35
|
+
if node.to_a[0] == @flag_name
|
36
|
+
@result = true
|
37
|
+
end
|
38
|
+
end
|
39
|
+
alias_method :on_enable, :on_set_flag
|
40
|
+
alias_method :on_disable, :on_set_flag
|
41
|
+
end
|
42
|
+
|
43
|
+
def on_flow(node)
|
44
|
+
extract_volatiles(node)
|
45
|
+
name, *nodes = *node
|
46
|
+
node.updated(nil, [name] + optimize(process_all(nodes)))
|
47
|
+
end
|
48
|
+
|
49
|
+
def on_named_collection(node)
|
50
|
+
name, *nodes = *node
|
51
|
+
node.updated(nil, [name] + optimize(process_all(nodes)))
|
52
|
+
end
|
53
|
+
alias_method :on_group, :on_named_collection
|
54
|
+
alias_method :on_sub_flow, :on_named_collection
|
55
|
+
|
56
|
+
def on_unnamed_collection(node)
|
57
|
+
node.updated(nil, optimize(process_all(node.children)))
|
58
|
+
end
|
59
|
+
alias_method :on_on_fail, :on_unnamed_collection
|
60
|
+
alias_method :on_on_pass, :on_unnamed_collection
|
61
|
+
|
62
|
+
def optimize(nodes)
|
63
|
+
results = []
|
64
|
+
node1 = nil
|
65
|
+
nodes.each do |node2|
|
66
|
+
if node1
|
67
|
+
if opposite_flag_states?(node1, node2) && safe_to_combine?(node1, node2)
|
68
|
+
results << combine(node1, node2)
|
69
|
+
node1 = nil
|
70
|
+
else
|
71
|
+
results << node1
|
72
|
+
node1 = node2
|
73
|
+
end
|
74
|
+
else
|
75
|
+
node1 = node2
|
76
|
+
end
|
77
|
+
end
|
78
|
+
results << node1 if node1
|
79
|
+
results
|
80
|
+
end
|
81
|
+
|
82
|
+
def combine(node1, node2)
|
83
|
+
node1.updated(nil, process_all(node1.children) + [node2.updated(:else, process_all(node2.to_a[1..-1]))])
|
84
|
+
end
|
85
|
+
|
86
|
+
def opposite_flag_states?(node1, node2)
|
87
|
+
((node1.type == :if_flag && node2.type == :unless_flag) || (node1.type == :unless_flag && node2.type == :if_flag) ||
|
88
|
+
(node1.type == :if_enabled && node2.type == :unless_enabled) || (node1.type == :unless_enabled && node2.type == :if_enabled)) &&
|
89
|
+
node1.to_a[0] == node2.to_a[0]
|
90
|
+
end
|
91
|
+
|
92
|
+
def safe_to_combine?(node1, node2)
|
93
|
+
# Nodes won't be collapsed if node1 touches the shared run flag, i.e. if there is any chance
|
94
|
+
# that by the time it would naturally execute node2, the flag could have been changed by node1
|
95
|
+
(!volatile?(node1.to_a[0]) || (volatile?(node1.to_a[0]) && !node1.contains?(:test))) &&
|
96
|
+
!SetRunFlagFinder.new.contains?(node1, node1.to_a[0])
|
97
|
+
end
|
98
|
+
end
|
99
|
+
end
|
100
|
+
end
|