test_ids 0.8.2 → 1.2.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
- 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