test_ids 0.8.2 → 1.2.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
- SHA1:
3
- metadata.gz: 131ee6beb6b36186d39111f96c2ba0c06d9a71fd
4
- data.tar.gz: 5d4fc2e812a336213246939bf1f23b61739a35c8
2
+ SHA256:
3
+ metadata.gz: c45579faaf12aa3964acd4caa219e9b3443c366d2186870a5ea86381376dbeae
4
+ data.tar.gz: 1cb178416379a578fa91c472ce7e4d22521a428bfc40d400ea52127b10dd1c8e
5
5
  SHA512:
6
- metadata.gz: 91110e14f7c4ff5f63fdb3011abca0e3ecaf774423a1002a6a5db73ce3bdf0fd33bc31cdde8e6d3887cf4c1e5dd37c5dd8fc0112fc7ca1c096320e9bcae50530
7
- data.tar.gz: fed24675c4d9754fce08d539b43039a7bda4c838c5cd5daa378d94fbcff05151a314b70a20185c514b4a15012371e5aab9fe5d9b3b0324acefe20bddbf89d37e
6
+ metadata.gz: cc9951b84d9f7754d3225461157b6cd7579e6745c22ec8de0bb60ab91bc51d9f48ffdecf2c136329506fde707df173ba8e238233190e4236b62793beef92c06c
7
+ data.tar.gz: a04f02f1207e4599a3c0cb5d986b4e28741206afaf64e530b9d47dd86849b2c4a322e1a6c35e45284d727731d2bd592b4a5f4306b9eade6ccde0087062e01272
@@ -33,6 +33,8 @@ when "examples", "test"
33
33
  # Program generator integration test
34
34
  ARGV = %w(program/prb1.rb -t default -e default -r approved)
35
35
  load "#{Origen.top}/lib/origen/commands/program.rb"
36
+ ARGV = %W(program/prb1.rb -t dut2 -o #{Origen.root}/output/dut2 -e default -r approved/dut2)
37
+ load "#{Origen.top}/lib/origen/commands/program.rb"
36
38
 
37
39
  if Origen.app.stats.changed_files == 0 &&
38
40
  Origen.app.stats.new_files == 0 &&
@@ -1,8 +1,7 @@
1
1
  module TestIds
2
- MAJOR = 0
3
- MINOR = 8
4
- BUGFIX = 2
2
+ MAJOR = 1
3
+ MINOR = 2
4
+ BUGFIX = 0
5
5
  DEV = nil
6
-
7
6
  VERSION = [MAJOR, MINOR, BUGFIX].join(".") + (DEV ? ".pre#{DEV}" : '')
8
7
  end
@@ -24,12 +24,58 @@ module TestIds
24
24
  # returned will be the same as would be injected into flow.test.
25
25
  def allocate(instance, options = {})
26
26
  opts = options.dup
27
+ inject_flow_id(opts)
27
28
  current_configuration.allocator.allocate(instance, opts)
28
29
  { bin: opts[:bin], bin_size: opts[:bin_size], softbin: opts[:softbin], softbin_size: opts[:softbin_size],
29
30
  number: opts[:number], number_size: opts[:number_size]
30
31
  }
31
32
  end
32
33
 
34
+ # Similar to allocate, but allocates a test number only, i.e. no bin or softbin
35
+ def allocate_number(instance, options = {})
36
+ opts = options.dup
37
+ opts[:bin] = :none
38
+ opts[:softbin] = :none
39
+ inject_flow_id(opts)
40
+ current_configuration.allocator.allocate(instance, opts)
41
+ {
42
+ number: opts[:number], number_size: opts[:number_size]
43
+ }
44
+ end
45
+
46
+ # Similar to allocate, but allocates a softbin number only, i.e. no bin or test number
47
+ def allocate_softbin(instance, options = {})
48
+ opts = options.dup
49
+ opts[:bin] = :none
50
+ opts[:number] = :none
51
+ inject_flow_id(opts)
52
+ current_configuration.allocator.allocate(instance, opts)
53
+ {
54
+ softbin: opts[:softbin], softbin_size: opts[:softbin_size]
55
+ }
56
+ end
57
+ alias_method :allocate_soft_bin, :allocate_softbin
58
+
59
+ # Similar to allocate, but allocates a bin number only, i.e. no softbin or test number
60
+ def allocate_bin(instance, options = {})
61
+ opts = options.dup
62
+ opts[:softbin] = :none
63
+ opts[:number] = :none
64
+ inject_flow_id(opts)
65
+ current_configuration.allocator.allocate(instance, opts)
66
+ {
67
+ softbin: opts[:bin], softbin_size: opts[:bin_size]
68
+ }
69
+ end
70
+
71
+ # @api private
72
+ def inject_flow_id(options)
73
+ if Origen.interface_loaded?
74
+ flow = Origen.interface.flow
75
+ options[:test_ids_flow_id] = flow.try(:top_level).try(:id) || flow.id
76
+ end
77
+ end
78
+
33
79
  # Load an existing allocator, which will be loaded with a configuration based on what has
34
80
  # been serialized into the database if present, otherwise it will have an empty configuration.
35
81
  # Returns nil if the given database can not be found.
@@ -47,9 +93,11 @@ module TestIds
47
93
  configuration(@configuration_id)
48
94
  end
49
95
 
50
- def configuration(id)
96
+ def configuration(id, fail_on_missing = true)
51
97
  return @configuration[id] if @configuration && @configuration[id]
52
- fail('You have to create the configuration first before you can access it')
98
+ if fail_on_missing
99
+ fail('You have to create the configuration first before you can access it')
100
+ end
53
101
  end
54
102
  alias_method :config, :configuration
55
103
 
@@ -73,26 +121,51 @@ module TestIds
73
121
  initialize_git
74
122
  end
75
123
 
76
- ## Can be called in place of TestIDs.configure to change the configuration from
77
- ## the one that was originally supplied.
78
- ## It is expected that this is mainly useful for testing purposes only.
79
- # def reconfigure(id = nil, options = {}, &block)
80
- # id, options = nil, id if id.is_a?(Hash)
124
+ # Switch the current configuration to the given ID
125
+ def config=(id)
126
+ unless @configuration[id]
127
+ fail "The TestIds configuration '#{id}' has not been defined yet!"
128
+ end
129
+ @configuration_id = id
130
+ end
81
131
 
82
- # @configuration_id = id || options[:id] || :not_specified
132
+ # Return an Array of configuration IDs
133
+ def configs
134
+ @configuration.ids
135
+ end
83
136
 
84
- # @configuration ||= {}
137
+ def bin_config=(id)
138
+ @bin_config = id
139
+ end
85
140
 
86
- # old = @configuration[@configuration_id]
87
- # new = Configuration.new(@configuration_id)
88
- # new.instance_variable_set('@allocator', old.allocator)
89
- # new.allocator.instance_variable_set('@config', new)
90
- # @configuration[@configuration_id] = new
141
+ def bin_config
142
+ @bin_config ? configuration(@bin_config, false) : current_configuration
143
+ end
91
144
 
92
- # yield new
145
+ def softbin_config=(id)
146
+ @softbin_config = id
147
+ end
93
148
 
94
- # new.validate!
95
- # end
149
+ def softbin_config
150
+ @softbin_config ? configuration(@softbin_config, false) : current_configuration
151
+ end
152
+
153
+ def number_config=(id)
154
+ @number_config = id
155
+ end
156
+
157
+ def number_config
158
+ @number_config ? configuration(@number_config, false) : current_configuration
159
+ end
160
+
161
+ # Temporarily switches the current configuration to the given ID for the
162
+ # duration of the given block, then switches it back to what it was
163
+ def with_config(id)
164
+ orig = @configuration_id
165
+ @configuration_id = id
166
+ yield
167
+ @configuration_id = orig
168
+ end
96
169
 
97
170
  def configured?
98
171
  !!@configuration_id
@@ -205,6 +278,9 @@ module TestIds
205
278
 
206
279
  def clear_configuration_id
207
280
  @configuration_id = nil
281
+ @bin_config = nil
282
+ @softbin_config = nil
283
+ @number_config = nil
208
284
  end
209
285
 
210
286
  def testing=(val)
@@ -8,202 +8,182 @@ module TestIds
8
8
  class Allocator
9
9
  STORE_FORMAT_REVISION = 2
10
10
 
11
- attr_reader :config
12
-
13
11
  def initialize(configuration)
14
12
  @config = configuration
15
13
  end
16
14
 
15
+ def config(type = nil)
16
+ if type
17
+ type = type.to_s
18
+ type.chop! if type[-1] == 's'
19
+ TestIds.send("#{type}_config") || @config
20
+ else
21
+ @config
22
+ end
23
+ end
24
+
17
25
  # Allocates a softbin number from the range specified in the test flow
18
26
  # It also keeps a track of the last softbin assigned out from a particular range
19
27
  # and uses that to increment the pointers accordingly.
20
28
  # If a numeric number is passed to the softbin, it uses that number.
21
29
  # The configuration for the TestId plugin needs to pass in the bin number and the options from the test flow
22
30
  # For this method to work as intended.
23
- def next_in_range(range, options)
24
- range_item(range, options)
31
+ # This will handle the following range inputs:
32
+ # - Range, Ex: 0..10
33
+ # - Array, Ex: [0..10, 20..30]
34
+ def next_in_range(range_definition, options)
35
+ if range_definition.is_a?(Range)
36
+ range = range_definition.to_a
37
+ elsif range_definition.is_a?(Array)
38
+ range = []
39
+ range_definition.each do |range_element|
40
+ range += range_element.is_a?(Integer) ? [range_element] : range_element.step(options[:size]).to_a
41
+ end
42
+ if range.uniq.size != range.size
43
+ Origen.log.error "Duplicate or overlapping range has been detected in configuration: \'#{TestIds.current_configuration.id}\'."
44
+ fail
45
+ end
46
+ end
47
+ range_item(range, range_definition, options)
25
48
  end
26
49
 
27
- def range_item(range, options)
28
- orig_options = options.dup
29
- # Now Check if the database (JSON file) exists.
30
- # If file exists, load the database and create the alias for ['pointer']['ranges']
31
- if file && File.exist?(file)
32
- lines = File.readlines(file)
33
- # Remove any header comment lines since these are not valid JSON
34
- lines.shift while lines.first =~ /^\/\// && !lines.empty?
35
- s = JSON.load(lines.join("\n"))
36
- rangehash = s['pointers']['ranges']
37
- rangehash = Hash[rangehash.map { |k, v| [k.to_sym, v] }]
50
+ def range_item(range, range_definition, options)
51
+ # This is the actual fix, it should now not be dependent on the json file being read in, instead the store pointers
52
+ # will be utilized to get the correct number assigned from the range.
53
+ if store['pointers']['ranges'].nil?
54
+ rangehash = {}
38
55
  else
39
- # Create an alias for the databse that stores the pointers per range
40
- rangehash = store['pointers']['ranges'] ||= {}
56
+ rangehash = store['pointers']['ranges']
57
+ rangehash = Hash[rangehash.map { |k, v| [k.to_sym, v] }]
41
58
  end
59
+ orig_options = options.dup
42
60
  # Check the database to see if the passed in range has already been included in the database hash
43
- if rangehash.key?(:"#{range}")
61
+ if rangehash.key?(:"#{range_definition}")
44
62
  # Read out the database hash to see what the last_softbin given out was for that range.
45
63
  # This hash is updated whenever a new softbin is assigned, so it should have the updated values for each range.
46
- previous_assigned_value = rangehash[:"#{range}"].to_i
64
+ previous_assigned_value = rangehash[:"#{range_definition}"].to_i
47
65
  # Now calculate the new pointer.
48
- @pointer = previous_assigned_value - range.min
66
+ @pointer = range.index(previous_assigned_value) + 1
49
67
  # Check if the last_softbin given out is the same as the range[@pointer],
50
68
  # if so increment pointer by softbin size, default size is 1, config.softbins.size is configurable.
51
69
  # from example above, pointer was calculated as 1,range[1] is 10101 and is same as last_softbin, so pointer is incremented
52
70
  # and new value is assigned to the softbin.
53
- if previous_assigned_value == range.to_a[@pointer]
71
+ if previous_assigned_value == range[@pointer]
54
72
  @pointer += options[:size]
55
- assigned_value = range.to_a[@pointer]
73
+ assigned_value = range[@pointer]
56
74
  else
57
75
  # Because of the pointer calculations above, I don't think it will ever reach here, has not in my test cases so far!
58
- assigned_value = range.to_a[@pointer]
76
+ assigned_value = range[@pointer]
59
77
  end
60
78
  # Now update the database pointers to point to the lastest assigned softbin for a given range.
61
- rangehash.merge!(:"#{range}" => "#{range.to_a[@pointer]}")
79
+ rangehash.merge!(:"#{range_definition}" => "#{range[@pointer]}")
62
80
  else
63
81
  # This is the case for a brand new range that has not been passed before
64
82
  # We start from the first value as the assigned softbin and update the database to reflect.
65
83
  @pointer = 0
66
- rangehash.merge!(:"#{range}" => "#{range.to_a[@pointer]}")
67
- assigned_value = range.to_a[@pointer]
84
+ rangehash.merge!(:"#{range_definition}" => "#{range[@pointer]}")
85
+ assigned_value = range[@pointer]
68
86
  end
69
- unless !assigned_value.nil? && assigned_value.between?(range.min, range.max)
87
+ unless !assigned_value.nil? && range.include?(assigned_value)
70
88
  Origen.log.error 'Assigned value not in range'
71
89
  fail
72
90
  end
91
+ # Since the assigned value for this test has now changed, update store to contain the newly assigned value
92
+ # so that when the json file is written, it contains the latest assigned value name.
93
+ store['pointers']['ranges'] = rangehash
73
94
  assigned_value
74
95
  end
75
96
 
76
- # Main method to inject generated bin and test numbers, the given
77
- # options instance is modified accordingly
78
- def allocate(instance, options)
79
- orig_options = options.dup
80
- clean(options)
81
- @callbacks = []
82
- name = extract_test_name(instance, options)
83
- name = "#{name}_#{options[:index]}" if options[:index]
84
-
85
- # First work out the test ID to be used for each of the numbers, and how many numbers
86
- # should be reserved
87
- if (options[:bin].is_a?(Symbol) || options[:bin].is_a?(String)) && options[:bin] != :none
88
- bin_id = options[:bin].to_s
97
+ # Returns an array containing :bin, :softbin, :number in the order that they should be calculated in order to fulfil
98
+ # the requirements of the current configuration and the given options.
99
+ # If an item is not required (e.g. if set to :none in the options), then it will not be present in the array.
100
+ def allocation_order(options)
101
+ items = []
102
+ items_required = 0
103
+ if allocation_required?(:bin, options) ||
104
+ (allocation_required?(:softbin, options) && config(:softbin).softbins.needs?(:bin)) ||
105
+ (allocation_required?(:number, options) && config(:number).numbers.needs?(:bin))
106
+ items_required += 1
89
107
  else
90
- bin_id = name
108
+ bin_done = true
91
109
  end
92
- if (options[:softbin].is_a?(Symbol) || options[:softbin].is_a?(String)) && options[:softbin] != :none
93
- softbin_id = options[:softbin].to_s
110
+ if allocation_required?(:softbin, options) ||
111
+ (allocation_required?(:bin, options) && config(:bin).bins.needs?(:softbin)) ||
112
+ (allocation_required?(:number, options) && config(:number).numbers.needs?(:softbin))
113
+ items_required += 1
94
114
  else
95
- softbin_id = name
115
+ softbin_done = true
96
116
  end
97
- if (options[:number].is_a?(Symbol) || options[:number].is_a?(String)) && options[:number] != :none
98
- number_id = options[:number].to_s
117
+ if allocation_required?(:number, options) ||
118
+ (allocation_required?(:bin, options) && config(:bin).bins.needs?(:number)) ||
119
+ (allocation_required?(:softbin, options) && config(:softbin).softbins.needs?(:number))
120
+ items_required += 1
99
121
  else
100
- number_id = name
101
- end
102
-
103
- bin_size = options[:bin_size] || config.bins.size
104
- softbin_size = options[:softbin_size] || config.softbins.size
105
- number_size = options[:number_size] || config.numbers.size
106
-
107
- bin = store['assigned']['bins'][bin_id] ||= {}
108
- softbin = store['assigned']['softbins'][softbin_id] ||= {}
109
- number = store['assigned']['numbers'][number_id] ||= {}
110
-
111
- # If the user has supplied any of these, that number should be used
112
- # and reserved so that it is not automatically generated later
113
- if options[:bin] && options[:bin].is_a?(Numeric)
114
- bin['number'] = options[:bin]
115
- bin['size'] = bin_size
116
- store['manually_assigned']['bins'][options[:bin].to_s] = true
117
- # Regenerate the bin if the original allocation has since been applied
118
- # manually elsewhere
119
- elsif store['manually_assigned']['bins'][bin['number'].to_s]
120
- bin['number'] = nil
121
- bin['size'] = nil
122
- # Also regenerate these as they could be a function of the bin
123
- if config.softbins.function?
124
- softbin['number'] = nil
125
- softbin['size'] = nil
126
- end
127
- if config.numbers.function?
128
- number['number'] = nil
129
- number['size'] = nil
130
- end
131
- end
132
- if options[:softbin] && options[:softbin].is_a?(Numeric)
133
- softbin['number'] = options[:softbin]
134
- softbin['size'] = softbin_size
135
- store['manually_assigned']['softbins'][options[:softbin].to_s] = true
136
- elsif store['manually_assigned']['softbins'][softbin['number'].to_s]
137
- softbin['number'] = nil
138
- softbin['size'] = nil
139
- # Also regenerate the number as it could be a function of the softbin
140
- if config.numbers.function?
141
- number['number'] = nil
142
- number['size'] = nil
143
- end
144
- end
145
- if options[:number] && options[:number].is_a?(Numeric)
146
- number['number'] = options[:number]
147
- number['size'] = number_size
148
- store['manually_assigned']['numbers'][options[:number].to_s] = true
149
- elsif store['manually_assigned']['numbers'][number['number'].to_s]
150
- number['number'] = nil
151
- number['size'] = nil
152
- # Also regenerate the softbin as it could be a function of the number
153
- if config.softbins.function?
154
- softbin['number'] = nil
155
- softbin['size'] = nil
122
+ number_done = true
123
+ end
124
+ items_required.times do |i|
125
+ if !bin_done && (!config(:bin).bins.needs?(:softbin) || softbin_done) && (!config(:bin).bins.needs?(:number) || number_done)
126
+ items << :bin
127
+ bin_done = true
128
+ elsif !softbin_done && (!config(:softbin).softbins.needs?(:bin) || bin_done) && (!config(:softbin).softbins.needs?(:number) || number_done)
129
+ items << :softbin
130
+ softbin_done = true
131
+ elsif !number_done && (!config(:number).numbers.needs?(:bin) || bin_done) && (!config(:number).numbers.needs?(:softbin) || softbin_done)
132
+ items << :number
133
+ number_done = true
134
+ else
135
+ fail "Couldn't work out whether to generate next on iteration #{i} of #{items_required}, already picked: #{items}"
156
136
  end
157
137
  end
138
+ items
139
+ end
158
140
 
159
- # Otherwise generate the missing ones
160
- bin['number'] ||= allocate_bin(options.merge(size: bin_size))
161
- bin['size'] ||= bin_size
162
- # If the softbin is based on the test number, then need to calculate the
163
- # test number first.
164
- # Also do the number first if the softbin is a callback and the number is not.
165
- if (config.softbins.algorithm && config.softbins.algorithm.to_s =~ /n/) ||
166
- (config.softbins.callback && !config.numbers.function?)
167
- number['number'] ||= allocate_number(options.merge(bin: bin['number'], size: number_size))
168
- number['size'] ||= number_size
169
- softbin['number'] ||= allocate_softbin(options.merge(bin: bin['number'], number: number['number'], size: softbin_size))
170
- softbin['size'] ||= softbin_size
171
- else
172
- softbin['number'] ||= allocate_softbin(options.merge(bin: bin['number'], size: softbin_size))
173
- softbin['size'] ||= softbin_size
174
- number['number'] ||= allocate_number(options.merge(bin: bin['number'], softbin: softbin['number'], size: number_size))
175
- number['size'] ||= number_size
176
- end
141
+ # Main method to inject generated bin and test numbers, the given
142
+ # options instance is modified accordingly
143
+ def allocate(instance, options)
144
+ orig_options = options.dup
145
+ clean(options)
146
+ name = extract_test_name(instance, options)
177
147
 
178
- # Record that there has been a reference to the final numbers
179
- time = Time.now.to_f
180
- bin_size.times do |i|
181
- store['references']['bins'][(bin['number'] + i).to_s] = time if bin['number'] && options[:bin] != :none
182
- end
183
- softbin_size.times do |i|
184
- store['references']['softbins'][(softbin['number'] + i).to_s] = time if softbin['number'] && options[:softbin] != :none
185
- end
186
- number_size.times do |i|
187
- store['references']['numbers'][(number['number'] + i).to_s] = time if number['number'] && options[:number] != :none
188
- end
148
+ nones = []
189
149
 
190
- # Update the supplied options hash that will be forwarded to the program generator
191
- unless options.delete(:bin) == :none
192
- options[:bin] = bin['number']
193
- options[:bin_size] = bin['size']
150
+ # Record any :nones that are present for later
151
+ [:bin, :softbin, :number].each do |type|
152
+ nones << type if options[type] == :none
153
+ config(type).allocator.instance_variable_set('@needs_regenerated', {})
194
154
  end
195
- unless options.delete(:softbin) == :none
196
- options[:softbin] = softbin['number']
197
- options[:softbin_size] = softbin['size']
155
+
156
+ allocation_order(options).each do |type|
157
+ config(type).allocator.send(:_allocate, type, name, options)
198
158
  end
199
- unless options.delete(:number) == :none
200
- options[:number] = number['number']
201
- options[:number_size] = number['size']
159
+
160
+ # Turn any :nones into nils in the returned options
161
+ nones.each do |type|
162
+ options[type] = nil
163
+ options["#{type}_size"] = nil
202
164
  end
203
165
 
204
166
  options
205
167
  end
206
168
 
169
+ # Merge the given other store into the current one, it is assumed that both are formatted
170
+ # from the same (latest) revision
171
+ def merge_store(other_store)
172
+ store['pointers'] = store['pointers'].merge(other_store['pointers'])
173
+ @last_bin = store['pointers']['bins']
174
+ @last_softbin = store['pointers']['softbins']
175
+ @last_number = store['pointers']['numbers']
176
+ store['assigned']['bins'] = store['assigned']['bins'].merge(other_store['assigned']['bins'])
177
+ store['assigned']['softbins'] = store['assigned']['softbins'].merge(other_store['assigned']['softbins'])
178
+ store['assigned']['numbers'] = store['assigned']['numbers'].merge(other_store['assigned']['numbers'])
179
+ store['manually_assigned']['bins'] = store['manually_assigned']['bins'].merge(other_store['manually_assigned']['bins'])
180
+ store['manually_assigned']['softbins'] = store['manually_assigned']['softbins'].merge(other_store['manually_assigned']['softbins'])
181
+ store['manually_assigned']['numbers'] = store['manually_assigned']['numbers'].merge(other_store['manually_assigned']['numbers'])
182
+ store['references']['bins'] = store['references']['bins'].merge(other_store['references']['bins'])
183
+ store['references']['softbins'] = store['references']['softbins'].merge(other_store['references']['softbins'])
184
+ store['references']['numbers'] = store['references']['numbers'].merge(other_store['references']['numbers'])
185
+ end
186
+
207
187
  def store
208
188
  @store ||= begin
209
189
  if file && File.exist?(file)
@@ -267,7 +247,7 @@ module TestIds
267
247
  { 'bins' => 'bins', 'softbins' => 'softbins', 'numbers' => 'test_numbers' }.each do |type, name|
268
248
  if !config.send(type).function? && store['pointers'][type] == 'done'
269
249
  Origen.log.info "Checking for missing #{name}..."
270
- recovered = add_missing_references(config.send(type), store['references'][type])
250
+ recovered = add_missing_references(config.send, store['references'][type])
271
251
  if recovered == 0
272
252
  Origen.log.info " All #{name} are already available."
273
253
  else
@@ -396,6 +376,72 @@ module TestIds
396
376
 
397
377
  private
398
378
 
379
+ def _allocate(type, name, options)
380
+ type_plural = "#{type}s"
381
+ conf = config.send(type_plural)
382
+
383
+ # First work out the test ID to be used for each of the numbers, and how many numbers
384
+ # should be reserved
385
+ if (options[type].is_a?(Symbol) || options[type].is_a?(String)) && options[type] != :none
386
+ id = options[type].to_s
387
+ else
388
+ id = name
389
+ end
390
+ id = "#{id}_#{options[:index]}" if options[:index]
391
+ id = "#{id}_#{options[:test_ids_flow_id]}" if config.unique_by_flow?
392
+
393
+ val = store['assigned'][type_plural][id] ||= {}
394
+
395
+ if options[type].is_a?(Integer)
396
+ unless val['number'] == options[type]
397
+ store['manually_assigned']["#{type}s"][options[type].to_s] = true
398
+ val['number'] = options[type]
399
+ end
400
+ else
401
+ # Will be set if an upstream dependent type has been marked for regeneration by the code below
402
+ if @needs_regenerated[type]
403
+ val['number'] = nil
404
+ val['size'] = nil
405
+ # Regenerate the number if the original allocation has since been applied manually elsewhere
406
+ elsif store['manually_assigned'][type_plural][val['number'].to_s]
407
+ val['number'] = nil
408
+ val['size'] = nil
409
+ # Also regenerate these as they could be a function of the number we just invalidated
410
+ ([:bin, :softbin, :number] - [type]).each do |t|
411
+ if config.send("#{t}s").needs?(type)
412
+ @needs_regenerated[t] = true
413
+ end
414
+ end
415
+ end
416
+ end
417
+
418
+ if size = options["#{type}_size".to_sym]
419
+ val['size'] = size
420
+ end
421
+
422
+ # Generate the missing ones
423
+ val['size'] ||= conf.size
424
+ val['number'] ||= allocate_item(type, options.merge(size: val['size']))
425
+
426
+ # Record that there has been a reference to the final numbers
427
+ time = Time.now.to_f
428
+ val['size'].times do |i|
429
+ store['references'][type_plural][(val['number'] + i).to_s] = time if val['number'] && options[type] != :none
430
+ end
431
+
432
+ # Update the supplied options hash that will be forwarded to the program generator
433
+ options[type] = val['number']
434
+ options["#{type}_size".to_sym] = val['size']
435
+ end
436
+
437
+ def allocation_required?(type, options)
438
+ if options[type] == :none
439
+ false
440
+ else
441
+ !config(type).send("#{type}s").empty?
442
+ end
443
+ end
444
+
399
445
  def remove_invalid_references(config_item, references, manually_assigned)
400
446
  removed = 0
401
447
  references.each do |num, time|
@@ -435,161 +481,34 @@ module TestIds
435
481
  recovered
436
482
  end
437
483
 
438
- # Returns the next available bin in the pool, if they have all been given out
439
- # the one that hasn't been used for the longest time will be given out
440
- def allocate_bin(options)
441
- # Not sure if this is the right way. IMO the following are true:
442
- # 1. config.bins will have a callback only when ranges are specified.
443
- # 2. If config.bins is empty but config.bins is not a callback, return nil to maintain functionality as before.
444
- return nil if config.bins.empty? && !config.bins.callback
445
- if store['pointers']['bins'] == 'done'
446
- reclaim_bin(options)
447
- elsif callback = config.bins.callback
448
- callback.call(options)
449
- else
450
- b = config.bins.include.next(after: @last_bin, size: options[:size])
451
- @last_bin = nil
452
- while b && (store['manually_assigned']['bins'][b.to_s] || config.bins.exclude.include?(b))
453
- b = config.bins.include.next(size: options[:size])
454
- end
455
- # When no bin is returned it means we have used them all, all future generation
456
- # now switches to reclaim mode
457
- if b
458
- store['pointers']['bins'] = b
459
- else
460
- store['pointers']['bins'] = 'done'
461
- reclaim_bin(options)
462
- end
463
- end
464
- end
465
-
466
- def reclaim_bin(options)
467
- store['references']['bins'] = store['references']['bins'].sort_by { |k, v| v }.to_h
468
- if options[:size] == 1
469
- store['references']['bins'].first[0].to_i
470
- else
471
- reclaim(store['references']['bins'], options)
472
- end
473
- end
474
-
475
- def allocate_softbin(options)
476
- bin = options[:bin]
477
- num = options[:number]
478
- return nil if config.softbins.empty?
479
- if config.softbins.algorithm
480
- algo = config.softbins.algorithm.to_s.downcase
481
- if algo.to_s =~ /^[b\dxn]+$/
484
+ def allocate_item(type, options)
485
+ type_plural = "#{type}s"
486
+ conf = config.send(type_plural)
487
+ if conf.algorithm
488
+ algo = conf.algorithm.to_s.downcase
489
+ if algo.to_s =~ /^[bsn\dx]+$/
482
490
  number = algo.to_s
483
- bin = bin.to_s
484
- if number =~ /(b+)/
485
- max_bin_size = Regexp.last_match(1).size
486
- if bin.size > max_bin_size
487
- fail "Bin number (#{bin}) overflows the softbin number algorithm (#{algo})"
488
- end
489
- number = number.sub(/b+/, bin.rjust(max_bin_size, '0'))
490
- end
491
- if number =~ /(n+)/
492
- num = num.to_s
493
- max_num_size = Regexp.last_match(1).size
494
- if num.size > max_num_size
495
- fail "Test number (#{num}) overflows the softbin number algorithm (#{algo})"
496
- end
497
- number = number.sub(/n+/, num.rjust(max_num_size, '0'))
498
- end
499
- if number =~ /(x+)/
500
- max_counter_size = Regexp.last_match(1).size
501
- refs = store['references']['softbins']
502
- i = 0
503
- possible = []
504
- proposal = number.sub(/x+/, i.to_s.rjust(max_counter_size, '0'))
505
- possible << proposal
506
- while refs[proposal] && i.to_s.size <= max_counter_size
507
- i += 1
508
- proposal = number.sub(/x+/, i.to_s.rjust(max_counter_size, '0'))
509
- possible << proposal
510
- end
511
- # Overflowed, need to go search for the oldest duplicate now
512
- if i.to_s.size > max_counter_size
513
- i = 0
514
- # Not the most efficient search algorithm, but this should be hit very rarely
515
- # and even then only to generate the bin the first time around
516
- p = refs.sort_by { |bin, last_used| last_used }.find do |bin, last_used|
517
- possible.include?(bin)
491
+ ([:bin, :softbin, :number] - [type]).each do |t|
492
+ if number =~ /(#{t.to_s[0]}+)/
493
+ max_size = Regexp.last_match(1).size
494
+ num = options[t].to_s
495
+ if num.size > max_size
496
+ fail "The allocated number, #{num}, overflows the #{t} field in the #{type} algorithm - #{algo}"
518
497
  end
519
- proposal = p[0]
498
+ number = number.sub(/#{t.to_s[0]}+/, num.rjust(max_size, '0'))
520
499
  end
521
- number = proposal
522
500
  end
523
- else
524
- fail "Unknown softbin algorithm: #{algo}"
525
- end
526
- number.to_i
527
- elsif callback = config.softbins.callback
528
- callback.call(bin, options)
529
- else
530
- if store['pointers']['softbins'] == 'done'
531
- reclaim_softbin(options)
532
- else
533
- b = config.softbins.include.next(after: @last_softbin, size: options[:size])
534
- @last_softbin = nil
535
- while b && (store['manually_assigned']['softbins'][b.to_s] || config.softbins.exclude.include?(b))
536
- b = config.softbins.include.next(size: options[:size])
537
- end
538
- # When no softbin is returned it means we have used them all, all future generation
539
- # now switches to reclaim mode
540
- if b
541
- store['pointers']['softbins'] = b
542
- else
543
- store['pointers']['softbins'] = 'done'
544
- reclaim_softbin(options)
545
- end
546
- end
547
- end
548
- end
549
-
550
- def reclaim_softbin(options)
551
- store['references']['softbins'] = store['references']['softbins'].sort_by { |k, v| v }.to_h
552
- if options[:size] == 1
553
- store['references']['softbins'].first[0].to_i
554
- else
555
- reclaim(store['references']['softbins'], options)
556
- end
557
- end
558
501
 
559
- def allocate_number(options)
560
- bin = options[:bin]
561
- softbin = options[:softbin]
562
- return nil if config.numbers.empty?
563
- if config.numbers.algorithm
564
- algo = config.numbers.algorithm.to_s.downcase
565
- if algo.to_s =~ /^[bs\dx]+$/
566
- number = algo.to_s
567
- bin = bin.to_s
568
- if number =~ /(b+)/
569
- max_bin_size = Regexp.last_match(1).size
570
- if bin.size > max_bin_size
571
- fail "Bin number (#{bin}) overflows the test number algorithm (#{algo})"
572
- end
573
- number = number.sub(/b+/, bin.rjust(max_bin_size, '0'))
574
- end
575
- softbin = softbin.to_s
576
- if number =~ /(s+)/
577
- max_softbin_size = Regexp.last_match(1).size
578
- if softbin.size > max_softbin_size
579
- fail "Softbin number (#{softbin}) overflows the test number algorithm (#{algo})"
580
- end
581
- number = number.sub(/s+/, softbin.rjust(max_softbin_size, '0'))
582
- end
583
502
  if number =~ /(x+)/
584
503
  max_counter_size = Regexp.last_match(1).size
585
- refs = store['references']['numbers']
504
+ refs = store['references'][type_plural]
586
505
  i = 0
587
506
  possible = []
588
- proposal = number.sub(/x+/, i.to_s.rjust(max_counter_size, '0'))
507
+ proposal = number.sub(/x+/, i.to_s.rjust(max_counter_size, '0')).to_i.to_s
589
508
  possible << proposal
590
509
  while refs[proposal] && i.to_s.size <= max_counter_size
591
510
  i += 1
592
- proposal = number.sub(/x+/, i.to_s.rjust(max_counter_size, '0'))
511
+ proposal = number.sub(/x+/, i.to_s.rjust(max_counter_size, '0')).to_i.to_s
593
512
  possible << proposal
594
513
  end
595
514
  # Overflowed, need to go search for the oldest duplicate now
@@ -604,39 +523,42 @@ module TestIds
604
523
  end
605
524
  number = proposal
606
525
  end
607
- number.to_i
608
526
  else
609
- fail "Unknown test number algorithm: #{algo}"
527
+ fail "Illegal algorithm: #{algo}"
610
528
  end
611
- elsif callback = config.numbers.callback
612
- callback.call(bin, softbin, options)
529
+ number.to_i
530
+ elsif callback = conf.callback
531
+ callback.call(options)
613
532
  else
614
- if store['pointers']['numbers'] == 'done'
615
- reclaim_number(options)
533
+ if store['pointers'][type_plural] == 'done'
534
+ reclaim_item(type, options)
616
535
  else
617
- b = config.numbers.include.next(after: @last_number, size: options[:size])
618
- @last_number = nil
619
- while b && (store['manually_assigned']['numbers'][b.to_s] || config.numbers.exclude.include?(b))
620
- b = config.numbers.include.next(size: options[:size])
536
+ b = conf.include.next(after: instance_variable_get("@last_#{type}"), size: options[:size])
537
+ instance_variable_set("@last_#{type}", nil)
538
+ while b && (store['manually_assigned'][type_plural][b.to_s] || conf.exclude.include?(b))
539
+ b = conf.include.next(size: options[:size])
621
540
  end
622
541
  # When no number is returned it means we have used them all, all future generation
623
542
  # now switches to reclaim mode
624
543
  if b
625
- store['pointers']['numbers'] = b
544
+ store['pointers'][type_plural] = b + (options[:size] || 1) - 1
545
+ b
626
546
  else
627
- store['pointers']['numbers'] = 'done'
628
- reclaim_number(options)
547
+ store['pointers'][type_plural] = 'done'
548
+ reclaim_item(type, options)
629
549
  end
630
550
  end
631
551
  end
632
552
  end
633
553
 
634
- def reclaim_number(options)
635
- store['references']['numbers'] = store['references']['numbers'].sort_by { |k, v| v }.to_h
554
+ def reclaim_item(type, options)
555
+ type_plural = "#{type}s"
556
+ store['references'][type_plural] = store['references'][type_plural].sort_by { |k, v| v }.to_h
636
557
  if options[:size] == 1
637
- store['references']['numbers'].first[0].to_i
558
+ v = store['references'][type_plural].first
559
+ v[0].to_i if v
638
560
  else
639
- reclaim(store['references']['numbers'], options)
561
+ reclaim(store['references'][type_plural], options)
640
562
  end
641
563
  end
642
564
 
@@ -1,22 +1,29 @@
1
1
  module TestIds
2
2
  class Configuration
3
3
  class Item
4
- attr_accessor :include, :exclude, :algorithm, :size
4
+ attr_accessor :include, :exclude, :algorithm, :size, :needs
5
5
 
6
6
  def initialize
7
7
  @include = BinArray.new
8
8
  @exclude = BinArray.new
9
+ @needs = []
9
10
  @size = 1
10
11
  end
11
12
 
12
- def callback(&block)
13
+ def callback(options = {}, &block)
13
14
  if block_given?
15
+ @needs += Array(options[:needs])
14
16
  @callback = block
15
17
  else
16
18
  @callback
17
19
  end
18
20
  end
19
21
 
22
+ def needs?(type)
23
+ !!(!empty? && function? && (needs.include?(type) ||
24
+ (algorithm && (algorithm.to_s =~ /#{type.to_s[0]}/i))))
25
+ end
26
+
20
27
  def empty?
21
28
  include.empty? && exclude.empty? && !algorithm && !callback
22
29
  end
@@ -36,6 +43,7 @@ module TestIds
36
43
  def freeze
37
44
  @include.freeze
38
45
  @exclude.freeze
46
+ @needs.freeze
39
47
  super
40
48
  end
41
49
 
@@ -88,18 +96,23 @@ module TestIds
88
96
  @id
89
97
  end
90
98
 
91
- def bins(&block)
99
+ def bins(options = {}, &block)
92
100
  @bins ||= Item.new
93
101
  if block_given?
94
- @bins.callback(&block)
102
+ @bins.callback(options, &block)
95
103
  end
96
104
  @bins
97
105
  end
98
106
 
99
- def softbins(&block)
107
+ # An alias for config.bins.algorithm=
108
+ def bins=(val)
109
+ bins.algorithm = val
110
+ end
111
+
112
+ def softbins(options = {}, &block)
100
113
  @softbins ||= Item.new
101
114
  if block_given?
102
- @softbins.callback(&block)
115
+ @softbins.callback(options, &block)
103
116
  end
104
117
  @softbins
105
118
  end
@@ -109,10 +122,10 @@ module TestIds
109
122
  softbins.algorithm = val
110
123
  end
111
124
 
112
- def numbers(&block)
125
+ def numbers(options = {}, &block)
113
126
  @numbers ||= Item.new
114
127
  if block_given?
115
- @numbers.callback(&block)
128
+ @numbers.callback(options, &block)
116
129
  end
117
130
  @numbers
118
131
  end
@@ -123,18 +136,23 @@ module TestIds
123
136
  end
124
137
 
125
138
  def send_to_ate=(val)
126
- @send_to_ate = val
139
+ @send_to_ate = !!val
127
140
  end
128
141
 
129
- def send_to_ate
130
- @send_to_ate
142
+ def send_to_ate?
143
+ defined?(@send_to_ate) ? @send_to_ate : true
144
+ end
145
+
146
+ def unique_by_flow=(val)
147
+ @unique_by_flow = !!val
148
+ end
149
+
150
+ def unique_by_flow?
151
+ @unique_by_flow || false
131
152
  end
132
153
 
133
154
  def validate!
134
155
  unless validated?
135
- if bins.algorithm
136
- fail 'The TestIds bins configuration cannot be set to an algorithm, only a range set by bins.include and bins.exclude is permitted'
137
- end
138
156
  @validated = true
139
157
  freeze
140
158
  end
@@ -7,13 +7,16 @@ module OrigenTesters
7
7
  # test numbers
8
8
  alias_method :_orig_test, :test
9
9
  def test(instance, options = {})
10
- if TestIds.configured? && options[:test_ids] != :notrack
11
- TestIds.current_configuration.allocator.allocate(instance, options)
12
- end
13
10
  if TestIds.configured?
14
- if TestIds.current_configuration.send_to_ate == false
15
- BIN_OPTS.each do |opt|
16
- options.delete(opt)
11
+ unless options[:test_ids] == :notrack
12
+ options[:test_ids_flow_id] = try(:top_level).try(:id) || id
13
+
14
+ TestIds.current_configuration.allocator.allocate(instance, options)
15
+
16
+ unless TestIds.current_configuration.send_to_ate?
17
+ BIN_OPTS.each do |opt|
18
+ options.delete(opt)
19
+ end
17
20
  end
18
21
  end
19
22
  end
@@ -5,17 +5,23 @@ module TestIdsDev
5
5
  def initialize(options = {})
6
6
  case dut.test_ids
7
7
  when 1
8
- TestIds.configure do |config|
8
+ TestIds.configure id: :cfg1 do |config|
9
9
  # Example of testing remote repo
10
10
  # config.repo = 'ssh://git@sw-stash.freescale.net/~r49409/test_ids_repo.git'
11
11
  config.bins.include << 3
12
12
  config.bins.include << (10..20)
13
13
  config.bins.exclude << 15
14
14
  config.softbins = :bbbxx
15
- config.numbers do |bin, softbin|
16
- softbin * 100
15
+ config.numbers needs: :softbin do |options|
16
+ options[:softbin] * 100
17
17
  end
18
18
  end
19
+
20
+ when 2
21
+ TestIds.configure id: :cfg2 do |config|
22
+ config.bins.include << (5..16)
23
+ config.softbins = :bbxxx
24
+ end
19
25
  end
20
26
  end
21
27
 
@@ -1,7 +1,14 @@
1
1
  Flow.create do
2
-
3
- func :t1
4
- func :t2
5
- func :t3
6
- func :t3, bin: :none, sbin: :none
2
+ if dut.test_ids == 2
3
+ func :t1, bin: 11
4
+ func :t2, bin: 11
5
+ func :t3, bin: 11
6
+ func :t4, bin: 11
7
+ func :t5, bin: 11
8
+ else
9
+ func :t1
10
+ func :t2
11
+ func :t3
12
+ func :t3, bin: :none, sbin: :none
13
+ end
7
14
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: test_ids
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.8.2
4
+ version: 1.2.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: 2018-05-16 00:00:00.000000000 Z
11
+ date: 2020-11-24 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: origen
@@ -16,14 +16,14 @@ dependencies:
16
16
  requirements:
17
17
  - - ">="
18
18
  - !ruby/object:Gem::Version
19
- version: 0.7.40
19
+ version: 0.57.0
20
20
  type: :runtime
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
24
  - - ">="
25
25
  - !ruby/object:Gem::Version
26
- version: 0.7.40
26
+ version: 0.57.0
27
27
  - !ruby/object:Gem::Dependency
28
28
  name: origen_testers
29
29
  requirement: !ruby/object:Gem::Requirement
@@ -100,8 +100,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
100
100
  - !ruby/object:Gem::Version
101
101
  version: 1.8.11
102
102
  requirements: []
103
- rubyforge_project:
104
- rubygems_version: 2.6.7
103
+ rubygems_version: 3.0.3
105
104
  signing_key:
106
105
  specification_version: 4
107
106
  summary: Origen plugin to assign and track test program bins and test numbers