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