Class: ATP::Processors::Condition

Inherits:
ATP::Processor show all
Defined in:
lib/atp/processors/condition.rb

Overview

This optimizes the condition nodes such that any adjacent flow nodes that have the same condition, will be grouped together under a single condition wrapper.

For example this AST:

(flow
  (group "g1"
    (test
      (name "test1"))
    (flow-flag "bitmap" true
      (test
        (name "test2"))))
  (flow-flag "bitmap" true
    (group "g1"
      (flow-flag "x" true
        (test
          (name "test3")))
      (flow-flag "y" true
        (flow-flag "x" true
          (test
            (name "test4")))))))

Will be optimized to this:

(flow
  (group "g1"
    (test
      (name "test1"))
    (flow-flag "bitmap" true
      (test
        (name "test2"))
      (flow-flag "x" true
        (test
          (name "test3"))
        (flow-flag "y" true
          (test
            (name "test4")))))))

Constant Summary collapse

CONDITION_NODES =
[:flow_flag, :test_result, :test_executed, :group, :job]

Instance Method Summary collapse

Methods inherited from ATP::Processor

#handler_missing, #n, #n0, #n1, #run

Instance Method Details

#combine(conditions, node) ⇒ Object



210
211
212
213
214
215
216
217
# File 'lib/atp/processors/condition.rb', line 210

def combine(conditions, node)
  if conditions && !conditions.empty?
    conditions.reverse_each do |n|
      node = n.updated(nil, n.children + (node.is_a?(Array) ? node : [node]))
    end
  end
  node
end

#condition?(node) ⇒ Boolean

Returns:

  • (Boolean)


120
121
122
# File 'lib/atp/processors/condition.rb', line 120

def condition?(node)
  node.is_a?(ATP::AST::Node) && CONDITION_NODES.include?(node.type)
end

#condition_to_be_removed?(node) ⇒ Boolean

Returns:

  • (Boolean)


106
107
108
# File 'lib/atp/processors/condition.rb', line 106

def condition_to_be_removed?(node)
  remove_condition.any? { |c| equal_conditions?(c, node) }
end

#equal_conditions?(node1, node2) ⇒ Boolean

Returns:

  • (Boolean)


110
111
112
113
114
115
116
117
118
# File 'lib/atp/processors/condition.rb', line 110

def equal_conditions?(node1, node2)
  if node1.type == node2.type
    if node1.type == :group
      node1.to_a.take(1) == node2.to_a.take(1)
    else
      node1.to_a.take(2) == node2.to_a.take(2)
    end
  end
end

#extract_common_embedded_conditions(nodes) ⇒ Object



179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
# File 'lib/atp/processors/condition.rb', line 179

def extract_common_embedded_conditions(nodes)
  nodes = [nodes] unless nodes.is_a?(Array)
  result = []
  cond_a = nil
  test_a = nil
  ConditionExtractor.new.run(nodes).each do |cond_b, test_b|
    if cond_a
      common = cond_a & cond_b
      if common.empty?
        result << combine(cond_a, extract_common_embedded_conditions(test_a))
        cond_a = cond_b
        test_a = test_b
      else
        a = combine(cond_a - common, test_a)
        b = combine(cond_b - common, test_b)
        cond_a = common
        test_a = [a, b].flatten
      end
    else
      cond_a = cond_b
      test_a = test_b
    end
  end
  if nodes == [test_a]
    nodes
  else
    result << combine(cond_a, extract_common_embedded_conditions(test_a))
    result.flatten
  end
end

#has_condition?(condition, node) ⇒ Boolean

Returns true if the given node contains the given condition within its immediate children

Returns:

  • (Boolean)


98
99
100
101
102
103
104
# File 'lib/atp/processors/condition.rb', line 98

def has_condition?(condition, node)
  ([node] + node.children.to_a).any? do |n|
    if n.is_a?(ATP::AST::Node)
      equal_conditions?(condition, n)
    end
  end
end

#on_boolean_condition(node) ⇒ Object Also known as: on_flow_flag, on_test_result, on_test_executed, on_job



64
65
66
67
68
69
70
71
72
73
74
75
76
# File 'lib/atp/processors/condition.rb', line 64

def on_boolean_condition(node)
  children = node.children.dup
  name = children.shift
  state = children.shift
  remove_condition << node
  children = optimize_siblings(n(:temp, children))
  remove_condition.pop
  if condition_to_be_removed?(node)
    process_all(children)
  else
    node.updated(nil, [name, state] + process_all(children))
  end
end

#on_condition(node) ⇒ Object Also known as: on_group



82
83
84
85
86
87
88
89
90
91
92
93
# File 'lib/atp/processors/condition.rb', line 82

def on_condition(node)
  children = node.children.dup
  name = children.shift
  remove_condition << node
  children = optimize_siblings(n(:temp, children))
  remove_condition.pop
  if condition_to_be_removed?(node)
    process_all(children)
  else
    node.updated(nil, [name] + process_all(children))
  end
end

#on_flow(node) ⇒ Object



124
125
126
127
128
129
130
131
132
# File 'lib/atp/processors/condition.rb', line 124

def on_flow(node)
  # The extract_common_embedded_conditions method can probably do the whole job,
  # but it might get a little complicated with regards to optimizing adjacent groups,
  # so have left the original logic to have the first crack and deal with the groups
  # for now.
  nodes = optimize_siblings(node)
  nodes = extract_common_embedded_conditions(nodes)
  node.updated(nil, nodes)
end

#on_members(node) ⇒ Object



134
135
136
# File 'lib/atp/processors/condition.rb', line 134

def on_members(node)
  node.updated(nil, extract_common_embedded_conditions(optimize_siblings(node)))
end

#optimize_siblings(top_node) ⇒ Object



138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
# File 'lib/atp/processors/condition.rb', line 138

def optimize_siblings(top_node)
  children = []
  unprocessed_children = []
  current = nil
  last = top_node.children.size - 1
  top_node.to_a.each_with_index do |node, i|
    # If a condition has been identified in a previous node
    if current
      process_nodes = false
      # If this node has the current condition, then buffer it for later processing
      # and continue to the next node
      if has_condition?(current, node)
        unprocessed_children << node
        node = nil
      else
        process_nodes = true
      end
      if process_nodes || i == last
        remove_condition << current
        current_children = current.children + [process_all(unprocessed_children)].flatten
        unprocessed_children = []
        remove_condition.pop
        children << process(current.updated(nil, current_children))
        if node && (!condition?(node) || i == last)
          current = nil
          children << process(node)
        else
          current = node
        end
      end
    else
      if condition?(node) && i != last
        current = node
      else
        children << process(node)
      end
    end
  end
  children.flatten
end

#process(node) ⇒ Object



45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
# File 'lib/atp/processors/condition.rb', line 45

def process(node)
  # Bit of a hack - To get all of the nested conditions optimized away it is necessary
  # to execute this recursively a few times. This guard ensures that the recursion is
  # only performed on the top-level and not on every process operation.
  if @top_level_called
    super
  else
    @top_level_called = true
    ast1 = nil
    ast2 = node
    while ast1 != ast2
      ast1 = super(ast2)
      ast2 = super(ast1)
    end
    @top_level_called = false
    ast1
  end
end

#remove_conditionObject



219
220
221
# File 'lib/atp/processors/condition.rb', line 219

def remove_condition
  @remove_condition ||= []
end