atp 0.7.0 → 0.8.0

Sign up to get free protection for your applications and to get access to all the features.
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