Module: OrigenTesters::Generator::FlowControlAPI

Included in:
Doc::Generator::Flow, IGXLBasedTester::Base::Flow, SmartestBasedTester::Base::Flow
Defined in:
lib/origen_testers/generator/flow_control_api.rb

Defined Under Namespace

Modules: Interface

Constant Summary collapse

FLOW_METHODS =

Flow control methods related to flow context

[
  # Methods in arrays are aliases, the primary name is the first one
  [:if_enable, :if_enabled, :enable, :enabled],
  [:unless_enable, :unless_enabled],
  [:if_job, :if_jobs],
  [:unless_job, :unless_jobs]
]
RELATION_METHODS =

Flow control methods related to a relationship with another test

[
  # Methods in arrays are aliases, the primary name is the first one
  :if_ran,
  :unless_ran,
  [:if_failed, :unless_passed],
  [:if_passed, :unless_failed],
  [:if_any_passed, :unless_all_failed],
  [:if_all_passed, :unless_any_failed],
  [:if_any_failed, :unless_all_passed],
  [:if_all_failed, :unless_any_passed]
]

Instance Method Summary collapse

Instance Method Details

#apply_current_context!(line) ⇒ Object

:nodoc:



460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
# File 'lib/origen_testers/generator/flow_control_api.rb', line 460

def apply_current_context!(line) # :nodoc:
  if @if_enable_block
    if line.enable && line.enable != @if_enable_block
      fail "Cannot apply enable word '#{@if_enable_block}' to '#{line.parameter}', it already has '#{line.enable}'"
    else
      line.enable = @if_enable_block
    end
  end
  if @unless_enable_block
    line.unless_enable = @unless_enable_block
  end
  line.if_job = @if_job_block if @if_job_block
  line.unless_job = @unless_job_block if @unless_job_block
  line
end

#apply_relationshipsObject

:nodoc:



277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
# File 'lib/origen_testers/generator/flow_control_api.rb', line 277

def apply_relationships # :nodoc:
  if @relationships
    @relationships.each do |rel|
      t = find_by_id(rel[:target_id])
      fail "Test not found with ID: #{rel[:target_id]}, referenced in flow: #{filename}" unless t
      t.id = rel[:target_id]
      confirm_valid_context(t, rel[:dependent])
      case rel[:type]
      # The first cases here contain J750 logic, these should be replaced
      # with the call method style used for the later cases when time permits.
      when :failed
        if rel[:dependent].respond_to?(:run_if_failed)
          rel[:dependent].run_if_failed(rel[:target_id])
        else
          t.continue_on_fail
          flag = t.set_flag_on_fail
          rel[:dependent].flag_true = flag
        end
      when :passed
        if rel[:dependent].respond_to?(:run_if_passed)
          rel[:dependent].run_if_passed(rel[:target_id])
        else
          t.continue_on_fail
          flag = t.set_flag_on_pass
          rel[:dependent].flag_true = flag
        end
      when :if_ran, :unless_ran
        if rel[:type] == :if_ran
          if rel[:dependent].respond_to?(:run_if_ran)
            rel[:dependent].run_if_ran(rel[:target_id])
          else
            # t.continue_on_fail
            flag = t.set_flag_on_ran
            rel[:dependent].flag_true = flag
          end
        else
          if rel[:dependent].respond_to?(:run_unless_ran)
            rel[:dependent].run_unless_ran(rel[:target_id])
          else
            # t.continue_on_fail
            flag = t.set_flag_on_ran
            rel[:dependent].flag_clear = flag
          end
        end
      when :any_passed
        rel[:dependent].run_if_any_passed(t)
      when :all_passed
        rel[:dependent].run_if_all_passed(t)
      when :any_failed
        rel[:dependent].run_if_any_failed(t)
      when :all_failed
        rel[:dependent].run_if_all_failed(t)
      else
        fail 'Unknown relationship type!'
      end
    end
    @relationships = nil
  end
end

#at_run_startObject Also known as: reset_globals

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.



356
357
358
359
# File 'lib/origen_testers/generator/flow_control_api.rb', line 356

def at_run_start
  @@existing_ids = nil
  @@labels = nil
end

#conditionally_deactivated?(options) ⇒ Boolean

Returns:

  • (Boolean)


244
245
246
247
# File 'lib/origen_testers/generator/flow_control_api.rb', line 244

def conditionally_deactivated?(options)
  (options.key?(:if) && !options[:if]) ||
    (options.key?(:unless) && options[:unless])
end

#confirm_valid_context(test, dependent) ⇒ Object

:nodoc:



337
338
339
340
341
# File 'lib/origen_testers/generator/flow_control_api.rb', line 337

def confirm_valid_context(test, dependent) # :nodoc:
  # TODO:  Add some validation checks here, for example make sure the dependent
  #        executes in the same job(s) as the test, otherwise the dependent will
  #        never be hit and will cause a validation error.
end

#context_changed?(options = {}) ⇒ Boolean

Returns true if the test context generated from the supplied options + existing context wrappers is different from that which was applied to the previous test.

Returns:

  • (Boolean)


28
29
30
# File 'lib/origen_testers/generator/flow_control_api.rb', line 28

def context_changed?(options = {})
  current_context[:hash_code] != summarize_context(options)[:hash_code]
end

#current_contextObject

Returns a hash representing the current context, that is the context that was applied to the last test.

The hash contains two items:

  • :context contains a hash that summarises the flow control options that have been used, for example it may contain something like: :if_enable => “data_collection”

  • :hash_code returns a hash-code for the values contained in the :context arrary. Any two equivalent contexts will have the same :hash_code, therefore this can be used to easily check the equivalence of any two contexts.



53
54
55
# File 'lib/origen_testers/generator/flow_control_api.rb', line 53

def current_context
  @current_context ||= save_context
end

#extract_flow_control_options!(options) ⇒ Object



486
487
488
489
490
491
492
493
494
# File 'lib/origen_testers/generator/flow_control_api.rb', line 486

def extract_flow_control_options!(options)
  opts = {}
  FLOW_METHODS.flatten.each do |o|
    if options.key?(o)
      opts[o] = options.delete(o)
    end
  end
  opts
end

#extract_relation_options!(options) ⇒ Object

Removes any flow relationship options from given hash and returns them in a new hash



478
479
480
481
482
483
484
# File 'lib/origen_testers/generator/flow_control_api.rb', line 478

def extract_relation_options!(options) # :nodoc:
  opts = {}
  RELATION_METHODS.flatten.each do |o|
    opts[o] = options.delete(o)
  end
  opts
end

#finalize(options = {}) ⇒ Object

If a class that includes this module has a finalize method it must call apply_relationships



272
273
274
275
# File 'lib/origen_testers/generator/flow_control_api.rb', line 272

def finalize(options = {}) # :nodoc:
  apply_relationships
  reset_globals
end

#find_by_id(id, options = {}) ⇒ Object

:nodoc:



249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
# File 'lib/origen_testers/generator/flow_control_api.rb', line 249

def find_by_id(id, options = {}) # :nodoc:
  options = {
    search_other_flows: true
  }.merge(options)
  # Look within the current flow for a match first
  t = identity_map[id.to_sym]
  return t if t
  # If no match then look across other flow modules for a match
  # This currently returns the first match, should it raise an error on multiple?
  if options[:search_other_flows]
    Origen.interface.flow_generators.any? do |flow|
      t = flow.find_by_id(id, search_other_flows: false)
    end
  end
  t
end

#generate_unique_label(id = nil) ⇒ Object



496
497
498
499
500
501
502
503
504
505
506
# File 'lib/origen_testers/generator/flow_control_api.rb', line 496

def generate_unique_label(id = nil)
  id = 'label' if !id || id == ''
  label = "#{Origen.interface.app_identifier}_#{id}"
  label.gsub!(' ', '_')
  label.upcase!
  @@labels ||= {}
  @@labels[Origen.tester.class] ||= {}
  @@labels[Origen.tester.class][label] ||= 0
  @@labels[Origen.tester.class][label] += 1
  "#{label}_#{@@labels[Origen.tester.class][label]}"
end

#identity_mapObject

:nodoc:



266
267
268
# File 'lib/origen_testers/generator/flow_control_api.rb', line 266

def identity_map # :nodoc:
  @identity_map ||= {}
end

#if_all_failed(test_id, options = {}) ⇒ Object Also known as: unless_any_passed

All tests generated within the given block will only run if the given test id has failed ON ALL SITES earlier in the flow



232
233
234
235
236
237
238
239
240
241
# File 'lib/origen_testers/generator/flow_control_api.rb', line 232

def if_all_failed(test_id, options = {})
  test_id = Origen.interface.filter_id(test_id, options)
  return if conditionally_deactivated?(options)
  if @if_all_failed_block
    fail 'Sorry but nesting of if_all_failed is not currently supported!'
  end
  @if_all_failed_block = test_id
  yield
  @if_all_failed_block = nil
end

#if_all_passed(test_id, options = {}) ⇒ Object Also known as: unless_any_failed

All tests generated within the given block will only run if the given test id has passed ON ALL SITES earlier in the flow



204
205
206
207
208
209
210
211
212
213
# File 'lib/origen_testers/generator/flow_control_api.rb', line 204

def if_all_passed(test_id, options = {})
  test_id = Origen.interface.filter_id(test_id, options)
  return if conditionally_deactivated?(options)
  if @if_all_passed_block
    fail 'Sorry but nesting of if_all_passed is not currently supported!'
  end
  @if_all_passed_block = test_id
  yield
  @if_all_passed_block = nil
end

#if_any_failed(test_id, options = {}) ⇒ Object Also known as: unless_all_passed

All tests generated within the given block will only run if the given test id has failed ON ANY SITE earlier in the flow



218
219
220
221
222
223
224
225
226
227
# File 'lib/origen_testers/generator/flow_control_api.rb', line 218

def if_any_failed(test_id, options = {})
  test_id = Origen.interface.filter_id(test_id, options)
  return if conditionally_deactivated?(options)
  if @if_any_failed_block
    fail 'Sorry but nesting of if_any_failed is not currently supported!'
  end
  @if_any_failed_block = test_id
  yield
  @if_any_failed_block = nil
end

#if_any_passed(test_id, options = {}) ⇒ Object Also known as: unless_all_failed

All tests generated within the given block will only run if the given test id has passed ON ANY SITE earlier in the flow



190
191
192
193
194
195
196
197
198
199
# File 'lib/origen_testers/generator/flow_control_api.rb', line 190

def if_any_passed(test_id, options = {})
  test_id = Origen.interface.filter_id(test_id, options)
  return if conditionally_deactivated?(options)
  if @if_any_passed_block
    fail 'Sorry but nesting of if_any_passed is not currently supported!'
  end
  @if_any_passed_block = test_id
  yield
  @if_any_passed_block = nil
end

#if_enable(word, options = {}) {|word| ... } ⇒ Object Also known as: if_enabled

All tests generated within the given block will be assigned the given enable word.

If a test encountered within the block already has another enable word assigned to it then an error will be raised.

Yields:

  • (word)


97
98
99
100
101
# File 'lib/origen_testers/generator/flow_control_api.rb', line 97

def if_enable(word, options = {})
  @if_enable_block = word
  yield word
  @if_enable_block = nil
end

#if_failed(test_id, options = {}) ⇒ Object Also known as: unless_passed

All tests generated within the given block will only run if the given test id has failed earlier in the flow



160
161
162
163
164
165
166
167
168
169
170
# File 'lib/origen_testers/generator/flow_control_api.rb', line 160

def if_failed(test_id, options = {})
  test_id = Origen.interface.filter_id(test_id, options)
  return if options.key?(:if) && !options[:if]
  return if options.key?(:unless) && options[:unless]
  if @if_failed_block
    fail 'Sorry but nesting of if_failed is not currently supported!'
  end
  @if_failed_block = test_id
  yield
  @if_failed_block = nil
end

#if_job(*jobs) ⇒ Object Also known as: if_jobs

All tests generated within the given block will be enabled only for the given jobs.



113
114
115
116
117
118
# File 'lib/origen_testers/generator/flow_control_api.rb', line 113

def if_job(*jobs)
  jobs = jobs.flatten
  @if_job_block = @if_job_block ? @if_job_block + jobs : jobs
  yield
  @if_job_block = nil
end

#if_passed(test_id, options = {}) ⇒ Object Also known as: unless_failed

All tests generated within the given block will only run if the given test id has passed earlier in the flow



175
176
177
178
179
180
181
182
183
184
185
# File 'lib/origen_testers/generator/flow_control_api.rb', line 175

def if_passed(test_id, options = {})
  test_id = Origen.interface.filter_id(test_id, options)
  return if options.key?(:if) && !options[:if]
  return if options.key?(:unless) && options[:unless]
  if @if_passed_block
    fail 'Sorry but nesting of if_passed is not currently supported!'
  end
  @if_passed_block = test_id
  yield
  @if_passed_block = nil
end

#if_ran(test_id, options = {}) ⇒ Object

All tests generated within the given block will only run if the given test id has also run earlier in the flow



132
133
134
135
136
137
138
139
140
141
142
# File 'lib/origen_testers/generator/flow_control_api.rb', line 132

def if_ran(test_id, options = {})
  test_id = Origen.interface.filter_id(test_id, options)
  return if options.key?(:if) && !options[:if]
  return if options.key?(:unless) && options[:unless]
  if @if_ran_block
    fail 'Sorry but nesting of if_ran is not currently supported!'
  end
  @if_ran_block = test_id
  yield
  @if_ran_block = nil
end

#record_id(test, options = {}) ⇒ Object



343
344
345
346
347
348
349
350
351
352
353
# File 'lib/origen_testers/generator/flow_control_api.rb', line 343

def record_id(test, options = {})
  if options[:id]
    @@existing_ids ||= []
    if @@existing_ids.include?(options[:id].to_sym)
      fail "The ID '#{test.id}' is not unique, it has already been assigned!"
    else
      @@existing_ids << options[:id].to_sym
    end
    identity_map[options[:id].to_sym] = test
  end
end

#replace_context_with_current(options) ⇒ Object

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Removes any context options from the given options hash and merges in the current context



61
62
63
64
65
66
67
# File 'lib/origen_testers/generator/flow_control_api.rb', line 61

def replace_context_with_current(options)
  options = options.merge({})
  [FLOW_METHODS, RELATION_METHODS].flatten.each do |m|
    options.delete(m)
  end
  options.merge(current_context[:context])
end

#replace_relationship_dependent(old_node, new_node) ⇒ Object



362
363
364
365
366
# File 'lib/origen_testers/generator/flow_control_api.rb', line 362

def replace_relationship_dependent(old_node, new_node)
  @relationships.each do |rel|
    rel[:dependent] = new_node if rel[:dependent] == old_node
  end
end

#save_context(options = {}) ⇒ Object

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.



33
34
35
36
37
38
39
40
41
# File 'lib/origen_testers/generator/flow_control_api.rb', line 33

def save_context(options = {})
  # If the test has requested to use the current context...
  if options[:context] == :current
    replace_context_with_current(options)
  else
    @current_context = summarize_context(options)
    options.merge(@current_context[:context])
  end
end

#summarize_context(options = {}) ⇒ Object

Returns a hash like that returned by current_context based on the given set of options + existing context wrappers.



71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
# File 'lib/origen_testers/generator/flow_control_api.rb', line 71

def summarize_context(options = {})
  code = []
  context = {}
  (FLOW_METHODS + RELATION_METHODS).each do |m|
    primary = m.is_a?(Array) ? m.first : m
    val = false
    [m].flatten.each do |m|
      if options[m]
        val = options[m]
      elsif instance_variable_get("@#{m}_block")
        val = instance_variable_get("@#{m}_block")
      end
    end
    if val
      code << primary
      code << val
      context[primary] = val
    end
  end
  { hash_code: code.flatten.hash, context: context }
end

#track_relationships(test_options = {}) ⇒ Object

As generation of render and imports is not linear its possible that the test being referenced does not exist in the collection yet. Therefore the required relationship will be recorded for now and applied later upon closing the generator at which point the complete final collection will be available.

Note - as of v2.0.1.dev64 the above is no longer true - imports are generated linearly. Therefore parent test should always already exist and it is possible that this relationship handling could be cleaned up considerably.

However we should keep it around for now as it may come in useful when other tester platforms are supported in the future.



379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
# File 'lib/origen_testers/generator/flow_control_api.rb', line 379

def track_relationships(test_options = {}) # :nodoc:
  [:id, RELATION_METHODS].flatten.each do |id|
    if test_options[id]
      test_options[id] = Origen.interface.filter_id(test_options[id], test_options)
    end
  end
  options = extract_relation_options!(test_options)
  current_test = yield test_options
  record_id(current_test, test_options)
  @relationships ||= []
  target_id = options[:if_failed] || options[:unless_passed] || @if_failed_block
  if target_id
    @relationships << {
      type:      :failed,
      target_id: target_id,
      dependent: current_test
    }
  end
  target_id = options[:if_passed] || options[:unless_failed] || @if_passed_block
  if target_id
    @relationships << {
      type:      :passed,
      target_id: target_id,
      dependent: current_test
    }
  end
  target_id = options.delete(:if_ran) || @if_ran_block
  if target_id
    @relationships << {
      type:      :if_ran,
      target_id: target_id,
      dependent: current_test
    }
  end
  target_id = options.delete(:unless_ran) || @unless_ran_block
  if target_id
    @relationships << {
      type:      :unless_ran,
      target_id: target_id,
      dependent: current_test
    }
  end
  target_id = options[:if_any_passed] || options[:unless_all_failed] || @if_any_passed_block
  if target_id
    @relationships << {
      type:      :any_passed,
      target_id: target_id,
      dependent: current_test
    }
  end
  target_id = options[:if_all_passed] || options[:unless_any_failed] || @if_all_passed_block
  if target_id
    @relationships << {
      type:      :all_passed,
      target_id: target_id,
      dependent: current_test
    }
  end
  target_id = options[:if_any_failed] || options[:unless_all_passed] || @if_any_failed_block
  if target_id
    @relationships << {
      type:      :any_failed,
      target_id: target_id,
      dependent: current_test
    }
  end
  target_id = options[:if_all_failed] || options[:unless_any_passed] || @if_all_failed_block
  if target_id
    @relationships << {
      type:      :all_failed,
      target_id: target_id,
      dependent: current_test
    }
  end
  if test_options[:context] == :current  # Context has already been applied
    current_test
  else
    apply_current_context!(current_test)
  end
end

#unless_enable(word, options = {}) {|word| ... } ⇒ Object Also known as: unless_enabled

All tests generated will not run unless the given enable word is asserted.

Yields:

  • (word)


105
106
107
108
109
# File 'lib/origen_testers/generator/flow_control_api.rb', line 105

def unless_enable(word, options = {})
  @unless_enable_block = word unless options[:or]
  yield word
  @unless_enable_block = nil
end

#unless_job(*jobs) ⇒ Object Also known as: unless_jobs

All tests generated within the given block will be enabled only for the given jobs.



122
123
124
125
126
127
# File 'lib/origen_testers/generator/flow_control_api.rb', line 122

def unless_job(*jobs)
  jobs = jobs.flatten
  @unless_job_block = @unless_job_block ? @unless_job_block + jobs : jobs
  yield
  @unless_job_block = nil
end

#unless_ran(test_id, options = {}) ⇒ Object

All tests generated within the given block will only run if the given test id has not run earlier in the flow



146
147
148
149
150
151
152
153
154
155
156
# File 'lib/origen_testers/generator/flow_control_api.rb', line 146

def unless_ran(test_id, options = {})
  test_id = Origen.interface.filter_id(test_id, options)
  return if options.key?(:if) && !options[:if]
  return if options.key?(:unless) && options[:unless]
  if @unless_ran_block
    fail 'Sorry but nesting of unless_ran is not currently supported!'
  end
  @unless_ran_block = test_id
  yield
  @unless_ran_block = nil
end