atp 0.7.0 → 0.8.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 8dec9f1b4fc0f21e7cc484fce7787483e2e93b78
4
- data.tar.gz: be379e4724e0eeac24edc94fe47b8db1b6e24b4a
3
+ metadata.gz: f75bbab3b302da47d1dee34b1d3ee5ff9e19fbf8
4
+ data.tar.gz: bdd796b0790974ab59638e3eb1c3a6d0978838e4
5
5
  SHA512:
6
- metadata.gz: da277917432ec7ddd4bc4ddcb3aca7734dec9a0e034f1e756c7012c218907ef5c7e70c32e3de2de917d8a2b98735cdd67e33b26650de0834555f7a21b15678f9
7
- data.tar.gz: 3fa65d6a651d94617f71f93c05d39aedc290c551e0f553a1d2b4b49330afd08bce9e1952315449206b14f87cbce0abe1bd53f84c3da1dbbbc8ea01fb84cd27d8
6
+ metadata.gz: c75c86487c1fa3db9cd8ec84b754850b4cb308c6cd5bd6c2fbc96342bf300d433154e66787b66bd896b484677f6c367102b641f9bdd5166f2b8be1a62a054767
7
+ data.tar.gz: eb3547a04a6b3ceea52ac58c831d5d320a1c818ffd161195207f858b0b30b592853ab94edeae371684221def67a0ee7075ee8c8ca7bb6e6af7890469b58e59b5
@@ -1,6 +1,6 @@
1
1
  module ATP
2
2
  MAJOR = 0
3
- MINOR = 7
3
+ MINOR = 8
4
4
  BUGFIX = 0
5
5
  DEV = nil
6
6
 
data/lib/atp.rb CHANGED
@@ -21,7 +21,6 @@ module ATP
21
21
  # and to implement the flow control API
22
22
  module Processors
23
23
  autoload :Condition, 'atp/processors/condition'
24
- autoload :ConditionExtractor, 'atp/processors/condition_extractor'
25
24
  autoload :Relationship, 'atp/processors/relationship'
26
25
  autoload :PreCleaner, 'atp/processors/pre_cleaner'
27
26
  autoload :PostCleaner, 'atp/processors/post_cleaner'
@@ -14,6 +14,24 @@ module ATP
14
14
  n(:flow, name(str))
15
15
  end
16
16
 
17
+ # Ensures the given flow ast has a volatile node, then adds the
18
+ # given flags to it
19
+ def add_volatile_flags(flow, flags)
20
+ name, *nodes = *flow
21
+ if nodes[0] && nodes[0].type == :volatile
22
+ v = nodes.shift
23
+ else
24
+ v = n0(:volatile)
25
+ end
26
+ existing = v.children.map { |f| f.type == :flag ? f.value : nil }.compact
27
+ new = []
28
+ flags.each do |flag|
29
+ new << n(:flag, flag) unless existing.include?(flag)
30
+ end
31
+ v = v.updated(nil, v.children + new)
32
+ flow.updated(nil, [name, v] + nodes)
33
+ end
34
+
17
35
  def name(str)
18
36
  n(:name, str.to_s)
19
37
  end
@@ -220,6 +238,55 @@ module ATP
220
238
 
221
239
  children << id(options[:id].to_s.downcase.to_sym) if options[:id]
222
240
 
241
+ if levels = options[:level] || options[:levels]
242
+ levels = [levels] unless levels.is_a?(Array)
243
+ levels.each do |l|
244
+ children << level(l[:name], l[:value], l[:unit] || l[:units])
245
+ end
246
+ end
247
+
248
+ if lims = options[:limit] || options[:limits]
249
+ lims = [lims] unless lims.is_a?(Array)
250
+ lims.each do |l|
251
+ children << limit(l[:value], l[:rule], l[:unit] || l[:units])
252
+ end
253
+ end
254
+
255
+ if pins = options[:pin] || options[:pins]
256
+ pins = [pins] unless pins.is_a?(Array)
257
+ pins.each do |p|
258
+ if p.is_a?(Hash)
259
+ children << pin(p[:name])
260
+ else
261
+ children << pin(p)
262
+ end
263
+ end
264
+ end
265
+
266
+ if pats = options[:pattern] || options[:patterns]
267
+ pats = [pats] unless pats.is_a?(Array)
268
+ pats.each do |p|
269
+ if p.is_a?(Hash)
270
+ children << pattern(p[:name], p[:path])
271
+ else
272
+ children << pattern(p)
273
+ end
274
+ end
275
+ end
276
+
277
+ if options[:meta]
278
+ attrs = []
279
+ options[:meta].each { |k, v| attrs << attribute(k, v) }
280
+ children << n(:meta, *attrs)
281
+ end
282
+
283
+ if subs = options[:sub_test] || options[:sub_tests]
284
+ subs = [subs] unless subs.is_a?(Array)
285
+ subs.each do |s|
286
+ children << s.updated(:sub_test, nil)
287
+ end
288
+ end
289
+
223
290
  children << on_fail(options[:on_fail]) if options[:on_fail]
224
291
  children << on_pass(options[:on_pass]) if options[:on_pass]
225
292
 
@@ -232,6 +299,38 @@ module ATP
232
299
  end
233
300
  end
234
301
 
302
+ def pattern(name, path = nil)
303
+ if path
304
+ n(:pattern, name, path)
305
+ else
306
+ n(:pattern, name)
307
+ end
308
+ end
309
+
310
+ def attribute(name, value)
311
+ n(:attribute, name, value)
312
+ end
313
+
314
+ def level(name, value, units = nil)
315
+ if units
316
+ n(:level, name, value, units)
317
+ else
318
+ n(:level, name, value)
319
+ end
320
+ end
321
+
322
+ def limit(value, rule, units = nil)
323
+ if units
324
+ n(:limit, value, rule, units)
325
+ else
326
+ n(:limit, value, rule)
327
+ end
328
+ end
329
+
330
+ def pin(name)
331
+ n(:pin, name)
332
+ end
333
+
235
334
  def on_fail(options = {})
236
335
  children = []
237
336
  if options[:bin] || options[:softbin]
@@ -41,6 +41,14 @@ module ATP
41
41
  ast
42
42
  end
43
43
 
44
+ # Indicate the that given flags should be considered volatile (can change at any time), which will
45
+ # prevent them from been touched by the optimization algorithms
46
+ def volatile(*flags)
47
+ options = flags.pop if flags.last.is_a?(Hash)
48
+ flags = flags.flatten
49
+ @raw = builder.add_volatile_flags(@raw, flags)
50
+ end
51
+
44
52
  # Group all tests generated within the given block
45
53
  #
46
54
  # @example
@@ -104,6 +112,16 @@ module ATP
104
112
  t
105
113
  end
106
114
 
115
+ # 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
+ #
118
+ # This is a helper to create sub_tests for inclusion in a top-level test node.
119
+ def sub_test(instance, options = {})
120
+ options[:return] = true
121
+ options[:ignore_all_conditions] = true
122
+ test(instance, options)
123
+ end
124
+
107
125
  def bin(number, options = {})
108
126
  extract_meta!(options)
109
127
  t = apply_open_conditions(options) do |options|
@@ -268,19 +286,23 @@ module ATP
268
286
  end
269
287
 
270
288
  def apply_open_conditions(options)
271
- if options[:context] == :current
272
- options[:conditions] = builder.context[:conditions]
273
- end
274
- builder.new_context
275
- t = yield(options)
276
- unless options[:context] == :current
277
- unless options[:dont_apply_conditions]
278
- open_conditions.each do |conditions|
279
- t = builder.apply_conditions(t, conditions)
289
+ if options[:ignore_all_conditions]
290
+ yield(options)
291
+ else
292
+ if options[:context] == :current
293
+ options[:conditions] = builder.context[:conditions]
294
+ end
295
+ builder.new_context
296
+ t = yield(options)
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
280
302
  end
281
303
  end
304
+ t
282
305
  end
283
- t
284
306
  end
285
307
 
286
308
  def extract_meta!(options)
@@ -23,6 +23,23 @@ module ATP
23
23
  end
24
24
  end
25
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
38
+ end
39
+ end
40
+ results
41
+ end
42
+
26
43
  def handler_missing(node)
27
44
  node.updated(nil, process_all(node.children))
28
45
  end
@@ -42,5 +59,31 @@ module ATP
42
59
  def n2(type, arg1, arg2)
43
60
  n(type, [arg1, arg2])
44
61
  end
62
+
63
+ def extract_volatiles(flow)
64
+ @volatiles = {}
65
+ if v = flow.find(:volatile)
66
+ @volatiles[:flags] = Array(v.find_all(:flag)).map(&:value)
67
+ end
68
+ end
69
+
70
+ def volatile_flags
71
+ unless @volatiles
72
+ fail 'You must first call extract_volatiles(node) from your on_flow hander method'
73
+ end
74
+ @volatiles[:flags] || []
75
+ end
76
+
77
+ # Returns true if the given flag name has been marked as volatile
78
+ def volatile?(flag)
79
+ result = volatile_flags.any? { |f| clean_flag(f) == clean_flag(flag) }
80
+ result
81
+ end
82
+
83
+ def clean_flag(flag)
84
+ flag = flag.dup.to_s
85
+ flag[0] = '' if flag[0] == '$'
86
+ flag.downcase
87
+ end
45
88
  end
46
89
  end
@@ -43,134 +43,131 @@ module ATP
43
43
  # (name "test4")))))))
44
44
  #
45
45
  class Condition < Processor
46
- CONDITION_NODES = [:flow_flag, :test_result, :test_executed, :group, :job]
47
-
48
- def process(node)
49
- # Bit of a hack - To get all of the nested conditions optimized away it is necessary
50
- # to execute this recursively a few times. This guard ensures that the recursion is
51
- # only performed on the top-level and not on every process operation.
52
- if @top_level_called
53
- super
54
- else
55
- @top_level_called = true
56
- ast1 = nil
57
- ast2 = node
58
- while ast1 != ast2
59
- ast1 = super(ast2)
60
- ast2 = super(ast1)
61
- end
62
- @top_level_called = false
63
- ast1
64
- end
46
+ def on_flow(node)
47
+ extract_volatiles(node)
48
+ node.updated(nil, optimize(process_all(node.children)))
65
49
  end
66
50
 
67
- def on_boolean_condition(node)
68
- children = node.children.dup
69
- name = children.shift
70
- state = children.shift
71
- remove_condition << node
72
- children = extract_common_embedded_conditions(n(:temp, children))
73
- remove_condition.pop
74
- if condition_to_be_removed?(node)
75
- process_all(children)
51
+ def on_flow_flag(node)
52
+ flag, state, *nodes = *node
53
+ if conditions_to_remove.any? { |c| node.type == c.type && c.to_a == [flag, state] }
54
+ if volatile?(flag)
55
+ result = n(:inline, optimize(process_all(nodes)))
56
+ else
57
+ # This ensures any duplicate conditions matching the current one get removed
58
+ conditions_to_remove << node.updated(nil, [flag, state])
59
+ result = n(:inline, optimize(process_all(nodes)))
60
+ conditions_to_remove.pop
61
+ end
76
62
  else
77
- node.updated(nil, [name, state] + process_all(children))
63
+ if volatile?(flag)
64
+ result = node.updated(nil, [flag, state] + optimize(process_all(nodes)))
65
+ else
66
+ conditions_to_remove << node.updated(nil, [flag, state])
67
+ result = node.updated(nil, [flag, state] + optimize(process_all(nodes)))
68
+ conditions_to_remove.pop
69
+ end
78
70
  end
71
+ result
79
72
  end
80
- alias_method :on_flow_flag, :on_boolean_condition
81
- alias_method :on_test_result, :on_boolean_condition
82
- alias_method :on_test_executed, :on_boolean_condition
83
- alias_method :on_job, :on_boolean_condition
73
+ alias_method :on_test_result, :on_flow_flag
74
+ alias_method :on_job, :on_flow_flag
75
+ alias_method :on_run_flag, :on_flow_flag
76
+ alias_method :on_test_executed, :on_flow_flag
84
77
 
85
78
  def on_group(node)
86
- children = node.children.dup
87
- name = children.shift
88
- remove_condition << node
89
- children = extract_common_embedded_conditions(n(:temp, children))
90
- remove_condition.pop
91
- if condition_to_be_removed?(node)
92
- process_all(children)
79
+ name, *nodes = *node
80
+ if conditions_to_remove.any? { |c| node.type == c.type && c.to_a == [name] }
81
+ conditions_to_remove << node.updated(nil, [name])
82
+ result = n(:inline, optimize(process_all(nodes)))
83
+ conditions_to_remove.pop
93
84
  else
94
- node.updated(nil, [name] + process_all(children))
85
+ conditions_to_remove << node.updated(nil, [name])
86
+ result = node.updated(nil, [name] + optimize(process_all(nodes)))
87
+ conditions_to_remove.pop
95
88
  end
89
+ result
96
90
  end
97
91
 
98
- # Returns true if the given node contains the given condition within
99
- # its immediate children
100
- def has_condition?(condition, node)
101
- ([node] + node.children.to_a).any? do |n|
102
- if n.is_a?(ATP::AST::Node)
103
- equal_conditions?(condition, n)
92
+ def optimize(nodes)
93
+ results = []
94
+ node1 = nil
95
+ nodes.each do |node2|
96
+ if node1
97
+ if can_be_combined?(node1, node2)
98
+ node1 = process(combine(node1, node2))
99
+ else
100
+ results << node1
101
+ node1 = node2
102
+ end
103
+ else
104
+ node1 = node2
104
105
  end
105
106
  end
107
+ results << node1 if node1
108
+ results
106
109
  end
107
110
 
108
- def condition_to_be_removed?(node)
109
- remove_condition.any? { |c| equal_conditions?(c, node) }
110
- end
111
-
112
- def equal_conditions?(node1, node2)
113
- if node1.type == node2.type
114
- if node1.type == :group
115
- node1.to_a.take(1) == node2.to_a.take(1)
116
- else
117
- node1.to_a.take(2) == node2.to_a.take(2)
118
- end
111
+ def can_be_combined?(node1, node2)
112
+ if condition_node?(node1) && condition_node?(node2)
113
+ !(conditions(node1) & conditions(node2)).empty?
114
+ else
115
+ false
119
116
  end
120
117
  end
121
118
 
122
- def condition?(node)
123
- node.is_a?(ATP::AST::Node) && CONDITION_NODES.include?(node.type)
119
+ def condition_node?(node)
120
+ node.respond_to?(:type) &&
121
+ [:flow_flag, :run_flag, :test_result, :group, :job, :test_executed].include?(node.type)
124
122
  end
125
123
 
126
- def on_flow(node)
127
- name, *nodes = *node
128
- nodes = extract_common_embedded_conditions(nodes)
129
- node.updated(nil, [name] + nodes)
130
- end
124
+ def combine(node1, node2)
125
+ common = conditions(node1) & conditions(node2)
126
+ common.each { |condition| conditions_to_remove << condition }
127
+ node1 = process(node1)
128
+ node1 = [node1] unless node1.is_a?(Array)
129
+ node2 = process(node2)
130
+ node2 = [node2] unless node2.is_a?(Array)
131
+ common.size.times { conditions_to_remove.pop }
131
132
 
132
- def extract_common_embedded_conditions(nodes)
133
- nodes = [nodes] unless nodes.is_a?(Array)
134
- result = []
135
- cond_a = nil
136
- test_a = nil
137
- ConditionExtractor.new.run(nodes).each do |cond_b, test_b|
138
- if cond_a
139
- common = cond_a & cond_b
140
- if common.empty?
141
- result << combine(cond_a, extract_common_embedded_conditions(test_a))
142
- cond_a = cond_b
143
- test_a = test_b
144
- else
145
- a = combine(cond_a - common, test_a)
146
- b = combine(cond_b - common, test_b)
147
- cond_a = common
148
- test_a = [a, b].flatten
149
- end
133
+ node = nil
134
+ common.reverse_each do |condition|
135
+ if node
136
+ node = condition.updated(nil, condition.children + [node])
150
137
  else
151
- cond_a = cond_b
152
- test_a = test_b
138
+ node = condition.updated(nil, condition.children + node1 + node2)
153
139
  end
154
140
  end
155
- if nodes == [test_a]
156
- nodes
157
- else
158
- result << combine(cond_a, extract_common_embedded_conditions(test_a))
159
- result.flatten
160
- end
141
+ node
161
142
  end
162
143
 
163
- def combine(conditions, node)
164
- if conditions && !conditions.empty?
165
- conditions.reverse_each do |n|
166
- node = n.updated(nil, n.children + (node.is_a?(Array) ? node : [node]))
144
+ def conditions(node)
145
+ result = []
146
+ if [:flow_flag, :run_flag].include?(node.type)
147
+ flag, state, *children = *node
148
+ unless volatile?(flag)
149
+ result << node.updated(nil, [flag, state])
167
150
  end
151
+ result += conditions(children.first) if children.first
152
+ elsif [:test_result, :job, :test_executed].include?(node.type)
153
+ flag, state, *children = *node
154
+ result << node.updated(nil, [flag, state])
155
+ result += conditions(children.first) if children.first
156
+ elsif node.type == :group
157
+ name, *children = *node
158
+ # Sometimes a group can have an ID
159
+ if children.first.try(:type) == :id
160
+ result << node.updated(nil, [name, children.shift])
161
+ else
162
+ result << node.updated(nil, [name])
163
+ end
164
+ result += conditions(children.first) if children.first
168
165
  end
169
- node
166
+ result
170
167
  end
171
168
 
172
- def remove_condition
173
- @remove_condition ||= []
169
+ def conditions_to_remove
170
+ @conditions_to_remove ||= []
174
171
  end
175
172
  end
176
173
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: atp
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.7.0
4
+ version: 0.8.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Stephen McGinty
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2017-09-19 00:00:00.000000000 Z
11
+ date: 2017-10-02 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: origen
@@ -77,7 +77,6 @@ files:
77
77
  - lib/atp/processors/add_ids.rb
78
78
  - lib/atp/processors/add_set_result.rb
79
79
  - lib/atp/processors/condition.rb
80
- - lib/atp/processors/condition_extractor.rb
81
80
  - lib/atp/processors/flow_id.rb
82
81
  - lib/atp/processors/marshal.rb
83
82
  - lib/atp/processors/post_cleaner.rb
@@ -1,46 +0,0 @@
1
- module ATP
2
- module Processors
3
- class ConditionExtractor < Processor
4
- attr_reader :results, :conditions
5
-
6
- def run(nodes)
7
- @results = []
8
- @conditions = []
9
- process_all(nodes)
10
- @results
11
- end
12
-
13
- def on_boolean_condition(node)
14
- children = node.children.dup
15
- name = children.shift
16
- state = children.shift
17
- conditions << node.updated(nil, [name, state])
18
- process_all(children)
19
- conditions.pop
20
- end
21
- alias_method :on_flow_flag, :on_boolean_condition
22
- alias_method :on_test_result, :on_boolean_condition
23
- alias_method :on_test_executed, :on_boolean_condition
24
- alias_method :on_job, :on_boolean_condition
25
- alias_method :on_run_flag, :on_boolean_condition
26
-
27
- def on_group(node)
28
- sig = node.children.select { |n| [:id, :name, :on_fail, :on_pass].include?(n.try(:type)) }
29
- children = node.children.dup
30
- conditions << node.updated(nil, sig)
31
- process_all(children - sig)
32
- conditions.pop
33
- end
34
-
35
- def on_test(node)
36
- results << [conditions.uniq, node]
37
- end
38
- alias_method :on_log, :on_test
39
- alias_method :on_enable_flow_flag, :on_test
40
- alias_method :on_disable_flow_flag, :on_test
41
- alias_method :on_cz, :on_test
42
- alias_method :on_set_result, :on_test
43
- alias_method :on_render, :on_test
44
- end
45
- end
46
- end