test_ids 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: e75b85981fb273db9e57af616ed8077d13f0c6d8
4
+ data.tar.gz: 1a1c23efe34764a6db9def529358430eeb7d8e29
5
+ SHA512:
6
+ metadata.gz: 444daec69e397bf06c6f0bd5982d956c1caf30b2bf2be07b0a37d4ec492a472285657775e8098194d63ccd767a2194ca5fbfc635ca39a885ffcf7589f41ebbfa
7
+ data.tar.gz: b98facddf7e176d90ddebb503221356c563102f63e2d5a48048be6a2118b5acf7a9349f7ac87d581421d98ce5c95d2b006a67a45c3fd23281ffa6cd1c7f38e63
@@ -0,0 +1,103 @@
1
+ require 'origen'
2
+ class TestIdsApplication < Origen::Application
3
+
4
+ # See http://origen-sdk.org/origen/api/Origen/Application/Configuration.html
5
+ # for a full list of the configuration options available
6
+
7
+ # These attributes should never be changed, the duplication here will be resolved in future
8
+ # by condensing these attributes that do similar things
9
+ self.name = "test_ids"
10
+ self.namespace = "TestIds"
11
+ config.name = "test_ids"
12
+ config.initials = "TestIds"
13
+ # Change this to point to the revision control repository for this plugin
14
+ config.rc_url = "ssh://git@github.com:Origen-SDK/test_ids.git"
15
+ config.release_externally = true
16
+
17
+ # To enable deployment of your documentation to a web server (via the 'origen web'
18
+ # command) fill in these attributes.
19
+ config.web_directory = "git@github.com:Origen-SDK/Origen-SDK.github.io.git/test_ids"
20
+ config.web_domain = "http://origen-sdk.org/test_ids"
21
+
22
+ # When false Origen will be less strict about checking for some common coding errors,
23
+ # it is recommended that you leave this to true for better feedback and easier debug.
24
+ # This will be the default setting in Origen v3.
25
+ config.strict_errors = true
26
+
27
+ # See: http://origen-sdk.org/origen/latest/guides/utilities/lint/
28
+ config.lint_test = {
29
+ # Require the lint tests to pass before allowing a release to proceed
30
+ run_on_tag: true,
31
+ # Auto correct violations where possible whenever 'origen lint' is run
32
+ auto_correct: true,
33
+ # Limit the testing for large legacy applications
34
+ #level: :easy,
35
+ # Run on these directories/files by default
36
+ #files: ["lib", "config/application.rb"],
37
+ }
38
+
39
+ config.semantically_version = true
40
+
41
+ # An example of how to set application specific LSF parameters
42
+ #config.lsf.project = "msg.te"
43
+
44
+ # An example of how to specify a prefix to add to all generated patterns
45
+ #config.pattern_prefix = "nvm"
46
+
47
+ # An example of how to add header comments to all generated patterns
48
+ #config.pattern_header do
49
+ # cc "This is a pattern created by the example origen application"
50
+ #end
51
+
52
+ # By default all generated output will end up in ./output.
53
+ # Here you can specify an alternative directory entirely, or make it dynamic such that
54
+ # the output ends up in a setup specific directory.
55
+ #config.output_directory do
56
+ # "#{Origen.root}/output/#{$dut.class}"
57
+ #end
58
+
59
+ # Similarly for the reference files, generally you want to setup the reference directory
60
+ # structure to mirror that of your output directory structure.
61
+ #config.reference_directory do
62
+ # "#{Origen.root}/.ref/#{$dut.class}"
63
+ #end
64
+
65
+ # This will automatically deploy your documentation after every tag
66
+ def after_release_email(tag, note, type, selector, options)
67
+ command = "origen web compile --remote --api"
68
+ Dir.chdir Origen.root do
69
+ system command
70
+ end
71
+ end
72
+
73
+ # Ensure that all tests pass before allowing a release to continue
74
+ def validate_release
75
+ if !system("origen specs") || !system("origen examples")
76
+ puts "Sorry but you can't release with failing tests, please fix them and try again."
77
+ exit 1
78
+ else
79
+ puts "All tests passing, proceeding with release process!"
80
+ end
81
+ end
82
+
83
+ # To enabled source-less pattern generation create a class (for example PatternDispatcher)
84
+ # to generate the pattern. This should return false if the requested pattern has been
85
+ # dispatched, otherwise Origen will proceed with looking up a pattern source as normal.
86
+ #def before_pattern_lookup(requested_pattern)
87
+ # PatternDispatcher.new.dispatch_or_return(requested_pattern)
88
+ #end
89
+
90
+ # If you use pattern iterators you may come across the case where you request a pattern
91
+ # like this:
92
+ # origen g example_pat_b0.atp
93
+ #
94
+ # However it cannot be found by Origen since the pattern name is actually example_pat_bx.atp
95
+ # In the case where the pattern cannot be found Origen will pass the name to this translator
96
+ # if it exists, and here you can make any substitutions to help Origen find the file you
97
+ # want. In this example any instances of _b\d, where \d means a number, are replaced by
98
+ # _bx.
99
+ #config.pattern_name_translator do |name|
100
+ # name.gsub(/_b\d/, "_bx")
101
+ #end
102
+
103
+ end
data/config/boot.rb ADDED
@@ -0,0 +1,24 @@
1
+ # This file is used to boot your plugin when it is running in standalone mode
2
+ # from its own workspace - i.e. when the plugin is being developed.
3
+ #
4
+ # It will not be loaded when the plugin is imported by a 3rd party app - in that
5
+ # case only lib/test_ids.rb is loaded.
6
+ #
7
+ # Therefore this file can be used to load anything extra that you need to boot
8
+ # the development environment for this app. For example, this is typically used
9
+ # to load some additional test classes to use your plugin APIs so that they can
10
+ # be tested and/or interacted with in the console.
11
+ require "test_ids"
12
+
13
+ module TestIdsDev
14
+ # Example of how to explicitly require a file
15
+ # require "test_ids_dev/my_file"
16
+
17
+ # Load all files in the lib/test_ids_dev directory.
18
+ # Note that there is no problem from requiring a file twice (Ruby will ignore
19
+ # the second require), so if you have a file that must be required first, then
20
+ # explicitly require it up above and then let this take care of the rest.
21
+ Dir.glob("#{File.dirname(__FILE__)}/../lib/test_ids_dev/**/*.rb").sort.each do |file|
22
+ require file
23
+ end
24
+ end
@@ -0,0 +1,71 @@
1
+ # This file should be used to extend the origen with application specific commands
2
+
3
+ # Map any command aliases here, for example to allow 'origen ex' to refer to a
4
+ # command called execute you would add a reference as shown below:
5
+ aliases ={
6
+ # "ex" => "execute",
7
+ }
8
+
9
+ # The requested command is passed in here as @command, this checks it against
10
+ # the above alias table and should not be removed.
11
+ @command = aliases[@command] || @command
12
+
13
+ # Now branch to the specific task code
14
+ case @command
15
+
16
+ # Here is an example of how to implement a command, the logic can go straight
17
+ # in here or you can require an external file if preferred.
18
+ when "my_command"
19
+ puts "Doing something..."
20
+ #require "commands/my_command" # Would load file lib/commands/my_command.rb
21
+ # You must always exit upon successfully capturing a command to prevent
22
+ # control flowing back to Origen
23
+ exit 0
24
+
25
+ # Example of how to make a command to run unit tests, this simply invokes RSpec on
26
+ # the spec directory
27
+ when "specs"
28
+ require "rspec"
29
+ exit RSpec::Core::Runner.run(['spec'])
30
+
31
+ # Example of how to make a command to run diff-based tests
32
+ when "examples", "test"
33
+ Origen.load_application
34
+ status = 0
35
+
36
+ # Program generator integration test
37
+ ARGV = %w(program/prb1.rb -t default -e default -r approved)
38
+ load "#{Origen.top}/lib/origen/commands/program.rb"
39
+
40
+ if Origen.app.stats.changed_files == 0 &&
41
+ Origen.app.stats.new_files == 0 &&
42
+ Origen.app.stats.changed_patterns == 0 &&
43
+ Origen.app.stats.new_patterns == 0
44
+
45
+ Origen.app.stats.report_pass
46
+ else
47
+ Origen.app.stats.report_fail
48
+ status = 1
49
+ end
50
+ puts
51
+ if @command == "test"
52
+ Origen.app.unload_target!
53
+ require "rspec"
54
+ result = RSpec::Core::Runner.run(['spec'])
55
+ status = status == 1 ? 1 : result
56
+ end
57
+ exit status # Exit with a 1 on the event of a failure per std unix result codes
58
+
59
+ # Always leave an else clause to allow control to fall back through to the
60
+ # Origen command handler.
61
+ else
62
+ # You probably want to also add the your commands to the help shown via
63
+ # origen -h, you can do this be assigning the required text to @application_commands
64
+ # before handing control back to Origen. Un-comment the example below to get started.
65
+ @application_commands = <<-EOT
66
+ specs Run the specs (tests), -c will enable coverage
67
+ examples Run the examples (tests), -c will enable coverage
68
+ test Run both specs and examples, -c will enable coverage
69
+ EOT
70
+
71
+ end
data/config/version.rb ADDED
@@ -0,0 +1,8 @@
1
+ module TestIds
2
+ MAJOR = 0
3
+ MINOR = 2
4
+ BUGFIX = 0
5
+ DEV = nil
6
+
7
+ VERSION = [MAJOR, MINOR, BUGFIX].join(".") + (DEV ? ".pre#{DEV}" : '')
8
+ end
@@ -0,0 +1,6 @@
1
+ # You can define any Rake tasks to support your application here (or in any file
2
+ # ending in .rake in this directory).
3
+ #
4
+ # Rake (Ruby Make) is very useful for creating build scripts, see this short video
5
+ # for a quick introduction:
6
+ # http://railscasts.com/episodes/66-custom-rake-tasks
@@ -0,0 +1,359 @@
1
+ module TestIds
2
+ class Allocator
3
+ include Origen::Callbacks
4
+ attr_reader :config
5
+
6
+ def initialize
7
+ @@allocators ||= 0
8
+ @@allocators += 1
9
+ if @@allocators > 1 && !TestIds.send(:testing?)
10
+ fail 'TestIds::Allocators is a singleton, there can be only one'
11
+ end
12
+ end
13
+
14
+ # Main method to inject generated bin and test numbers, the given
15
+ # options instance is modified accordingly
16
+ def allocate(instance, options)
17
+ @changes_made = true
18
+ clean(options)
19
+ @callbacks = []
20
+ name = extract_test_name(instance, options)
21
+ name = "#{name}_#{options[:index]}" if options[:index]
22
+ store['tests'][name] ||= {}
23
+ t = store['tests'][name]
24
+ # If the user has supplied any of these, that number should be used
25
+ # and reserved so that it is not automatically generated later
26
+ if options[:bin]
27
+ t['bin'] = options[:bin]
28
+ store['manually_assigned']['bin'][options[:bin].to_s] = true
29
+ # Regenerate the bin if the original allocation has since been applied
30
+ # manually elsewhere
31
+ elsif store['manually_assigned']['bin'][t['bin'].to_s]
32
+ t['bin'] = nil
33
+ # Also regenerate these as they could be a function of the bin
34
+ t['softbin'] = nil if config.softbins.function?
35
+ t['number'] = nil if config.numbers.function?
36
+ end
37
+ if options[:softbin]
38
+ t['softbin'] = options[:softbin]
39
+ store['manually_assigned']['softbin'][options[:softbin].to_s] = true
40
+ elsif store['manually_assigned']['softbin'][t['softbin'].to_s]
41
+ t['softbin'] = nil
42
+ # Also regenerate the number as it could be a function of the softbin
43
+ t['number'] = nil if config.numbers.function?
44
+ end
45
+ if options[:number]
46
+ t['number'] = options[:number]
47
+ store['manually_assigned']['number'][options[:number].to_s] = true
48
+ elsif store['manually_assigned']['number'][t['number'].to_s]
49
+ t['number'] = nil
50
+ end
51
+ # Otherwise generate the missing ones
52
+ t['bin'] ||= allocate_bin
53
+ t['softbin'] ||= allocate_softbin(t['bin'])
54
+ t['number'] ||= allocate_number(t['bin'], t['softbin'])
55
+ # Record that there has been a reference to the final numbers
56
+ time = Time.now.to_f
57
+ store['references']['bin'][t['bin'].to_s] = time if t['bin']
58
+ store['references']['softbin'][t['softbin'].to_s] = time if t['softbin']
59
+ store['references']['number'][t['number'].to_s] = time if t['number']
60
+ # Update the supplied options hash that will be forwarded to the
61
+ # program generator
62
+ options[:bin] = t['bin']
63
+ options[:softbin] = t['softbin']
64
+ options[:number] = t['number']
65
+ options
66
+ end
67
+
68
+ def config
69
+ TestIds.config
70
+ end
71
+
72
+ def store
73
+ @store ||= begin
74
+ s = JSON.load(File.read(file)) if file && File.exist?(file)
75
+ if s
76
+ @last_bin = s['pointers']['bin']
77
+ @last_softbin = s['pointers']['softbin']
78
+ @last_number = s['pointers']['number']
79
+ s
80
+ else
81
+ { 'tests' => {},
82
+ 'manually_assigned' => { 'bin' => {}, 'softbin' => {}, 'number' => {} },
83
+ 'pointers' => { 'bin' => nil, 'softbin' => nil, 'number' => nil },
84
+ 'references' => { 'bin' => {}, 'softbin' => {}, 'number' => {} }
85
+ }
86
+ end
87
+ end
88
+ end
89
+
90
+ # Saves the current allocator state to the repository
91
+ def save
92
+ if file
93
+ p = Pathname.new(file)
94
+ FileUtils.mkdir_p(p.dirname)
95
+ File.open(p, 'w') { |f| f.puts JSON.pretty_generate(store) }
96
+ end
97
+ end
98
+
99
+ def on_origen_shutdown
100
+ unless TestIds.send(:testing?)
101
+ if config.repo && @changes_made && config.on_completion != :discard
102
+ save
103
+ git.publish if publish?
104
+ end
105
+ end
106
+ end
107
+
108
+ # Returns a path to the file that will be used to store the allocated bins/numbers.
109
+ # If config.repo has not been set it returns nil.
110
+ def file
111
+ if config.repo
112
+ @file ||= begin
113
+ if git?
114
+ dir = "#{Origen.app.imports_directory}/test_ids/#{Pathname.new(config.repo).basename}"
115
+ FileUtils.mkdir_p(dir)
116
+ "#{dir}/store.json"
117
+ else
118
+ config.repo
119
+ end
120
+ end
121
+ end
122
+ end
123
+
124
+ def git
125
+ @git ||= Git.new(local: Pathname.new(file).dirname, remote: config.repo, no_pull: publish?)
126
+ end
127
+
128
+ def prepare
129
+ if git?
130
+ git # Pulls the latest repo
131
+ git.get_lock if publish?
132
+ end
133
+ end
134
+
135
+ private
136
+
137
+ def publish?
138
+ git? && config.on_completion == :publish
139
+ end
140
+
141
+ def git?
142
+ if config.repo
143
+ !!(config.repo =~ /git/i)
144
+ end
145
+ end
146
+
147
+ # Returns the next available bin in the pool, if they have all been given out
148
+ # the one that hasn't been used for the longest time will be given out
149
+ def allocate_bin
150
+ return nil if config.bins.empty?
151
+ if store['pointers']['bin'] == 'done'
152
+ reclaim_bin
153
+ else
154
+ b = config.bins.include.next(@last_bin)
155
+ @last_bin = nil
156
+ while b && (store['manually_assigned']['bin'][b.to_s] || config.bins.exclude.include?(b))
157
+ b = config.bins.include.next
158
+ end
159
+ # When no bin is returned it means we have used them all, all future generation
160
+ # now switches to reclaim mode
161
+ if b
162
+ store['pointers']['bin'] = b
163
+ else
164
+ store['pointers']['bin'] = 'done'
165
+ reclaim_bin
166
+ end
167
+ end
168
+ end
169
+
170
+ def reclaim_bin
171
+ store['references']['bin'] = store['references']['bin'].sort_by { |k, v| v }.to_h
172
+ store['references']['bin'].first[0].to_i
173
+ end
174
+
175
+ def allocate_softbin(bin)
176
+ return nil if config.softbins.empty?
177
+ if config.softbins.algorithm
178
+ algo = config.softbins.algorithm.to_s.downcase
179
+ if algo.to_s =~ /^[b\dx]+$/
180
+ number = algo.to_s
181
+ bin = bin.to_s
182
+ if number =~ /(b+)/
183
+ max_bin_size = Regexp.last_match(1).size
184
+ if bin.size > max_bin_size
185
+ fail "Bin number (#{bin}) overflows the test number algorithm (#{algo})"
186
+ end
187
+ number = number.sub(/b+/, bin.rjust(max_bin_size, '0'))
188
+ end
189
+ if number =~ /(x+)/
190
+ max_counter_size = Regexp.last_match(1).size
191
+ refs = store['references']['softbin']
192
+ i = 0
193
+ possible = []
194
+ proposal = number.sub(/x+/, i.to_s.rjust(max_counter_size, '0'))
195
+ possible << proposal
196
+ while refs[proposal] && i.to_s.size <= max_counter_size
197
+ i += 1
198
+ proposal = number.sub(/x+/, i.to_s.rjust(max_counter_size, '0'))
199
+ possible << proposal
200
+ end
201
+ # Overflowed, need to go search for the oldest duplicate now
202
+ if i.to_s.size > max_counter_size
203
+ i = 0
204
+ # Not the most efficient search algorithm, but this should be hit very rarely
205
+ # and even then only to generate the bin the first time around
206
+ p = refs.sort_by { |bin, last_used| last_used }.find do |bin, last_used|
207
+ possible.include?(bin)
208
+ end
209
+ proposal = p[0]
210
+ end
211
+ number = proposal
212
+ end
213
+ else
214
+ fail "Unknown softbin algorithm: #{algo}"
215
+ end
216
+ number.to_i
217
+ elsif callback = config.softbins.callback
218
+ callback.call(bin)
219
+ else
220
+ if store['pointers']['softbin'] == 'done'
221
+ reclaim_softbin
222
+ else
223
+ b = config.softbins.include.next(@last_softbin)
224
+ @last_softbin = nil
225
+ while b && (store['manually_assigned']['softbin'][b.to_s] || config.softbins.exclude.include?(b))
226
+ b = config.softbins.include.next
227
+ end
228
+ # When no softbin is returned it means we have used them all, all future generation
229
+ # now switches to reclaim mode
230
+ if b
231
+ store['pointers']['softbin'] = b
232
+ else
233
+ store['pointers']['softbin'] = 'done'
234
+ reclaim_softbin
235
+ end
236
+ end
237
+ end
238
+ end
239
+
240
+ def reclaim_softbin
241
+ store['references']['softbin'] = store['references']['softbin'].sort_by { |k, v| v }.to_h
242
+ store['references']['softbin'].first[0].to_i
243
+ end
244
+
245
+ def allocate_number(bin, softbin)
246
+ return nil if config.numbers.empty?
247
+ if config.numbers.algorithm
248
+ algo = config.numbers.algorithm.to_s.downcase
249
+ if algo.to_s =~ /^[bs\dx]+$/
250
+ number = algo.to_s
251
+ bin = bin.to_s
252
+ if number =~ /(b+)/
253
+ max_bin_size = Regexp.last_match(1).size
254
+ if bin.size > max_bin_size
255
+ fail "Bin number (#{bin}) overflows the test number algorithm (#{algo})"
256
+ end
257
+ number = number.sub(/b+/, bin.rjust(max_bin_size, '0'))
258
+ end
259
+ softbin = softbin.to_s
260
+ if number =~ /(s+)/
261
+ max_softbin_size = Regexp.last_match(1).size
262
+ if softbin.size > max_softbin_size
263
+ fail "Softbin number (#{softbin}) overflows the test number algorithm (#{algo})"
264
+ end
265
+ number = number.sub(/s+/, softbin.rjust(max_bin_size, '0'))
266
+ end
267
+ if number =~ /(x+)/
268
+ max_counter_size = Regexp.last_match(1).size
269
+ refs = store['references']['number']
270
+ i = 0
271
+ possible = []
272
+ proposal = number.sub(/x+/, i.to_s.rjust(max_counter_size, '0'))
273
+ possible << proposal
274
+ while refs[proposal] && i.to_s.size <= max_counter_size
275
+ i += 1
276
+ proposal = number.sub(/x+/, i.to_s.rjust(max_counter_size, '0'))
277
+ possible << proposal
278
+ end
279
+ # Overflowed, need to go search for the oldest duplicate now
280
+ if i.to_s.size > max_counter_size
281
+ i = 0
282
+ # Not the most efficient search algorithm, but this should be hit very rarely
283
+ # and even then only to generate the bin the first time around
284
+ p = refs.sort_by { |bin, last_used| last_used }.find do |bin, last_used|
285
+ possible.include?(bin)
286
+ end
287
+ proposal = p[0]
288
+ end
289
+ number = proposal
290
+ end
291
+ number.to_i
292
+ else
293
+ fail "Unknown test number algorithm: #{algo}"
294
+ end
295
+ elsif callback = config.numbers.callback
296
+ callback.call(bin, softbin)
297
+ else
298
+ if store['pointers']['number'] == 'done'
299
+ reclaim_number
300
+ else
301
+ b = config.numbers.include.next(@last_number)
302
+ @last_number = nil
303
+ while b && (store['manually_assigned']['number'][b.to_s] || config.numbers.exclude.include?(b))
304
+ b = config.numbers.include.next
305
+ end
306
+ # When no number is returned it means we have used them all, all future generation
307
+ # now switches to reclaim mode
308
+ if b
309
+ store['pointers']['number'] = b
310
+ else
311
+ store['pointers']['number'] = 'done'
312
+ reclaim_number
313
+ end
314
+ end
315
+ end
316
+ end
317
+
318
+ def reclaim_number
319
+ store['references']['number'] = store['references']['number'].sort_by { |k, v| v }.to_h
320
+ store['references']['number'].first[0].to_i
321
+ end
322
+
323
+ def extract_test_name(instance, options)
324
+ name = options[:name]
325
+ unless name
326
+ if instance.is_a?(String)
327
+ name = instance
328
+ elsif instance.is_a?(Symbol)
329
+ name = instance.to_s
330
+ elsif instance.is_a?(Hash)
331
+ h = instance.with_indifferent_access
332
+ name = h[:name] || h[:tname] || h[:testname] || h[:test_name]
333
+ elsif instance.respond_to?(:name)
334
+ name = instance.name
335
+ else
336
+ fail "Could not get the test name from #{instance}"
337
+ end
338
+ end
339
+ name.to_s.downcase
340
+ end
341
+
342
+ # Cleans the given options hash by consolidating any bin/test numbers
343
+ # to the following option keys:
344
+ #
345
+ # * :bin
346
+ # * :softbin
347
+ # * :number
348
+ # * :name
349
+ # * :index
350
+ def clean(options)
351
+ options[:softbin] ||= options.delete(:sbin) || options.delete(:soft_bin)
352
+ options[:number] ||= options.delete(:test_number) || options.delete(:tnum) ||
353
+ options.delete(:testnumber)
354
+ options[:name] ||= options.delete(:tname) || options.delete(:testname) ||
355
+ options.delete(:test_name)
356
+ options[:index] ||= options.delete(:ix)
357
+ end
358
+ end
359
+ end
@@ -0,0 +1,117 @@
1
+ module TestIds
2
+ class BinArray
3
+ def initialize
4
+ @store = []
5
+ end
6
+
7
+ def <<(val)
8
+ @store += Array(val)
9
+ @store = @store.sort do |a, b|
10
+ a = a.min if a.is_a?(Range)
11
+ b = b.min if b.is_a?(Range)
12
+ a <=> b
13
+ end
14
+ nil
15
+ end
16
+
17
+ def empty?
18
+ @store.empty?
19
+ end
20
+
21
+ def freeze
22
+ @store.freeze
23
+ end
24
+
25
+ # Returns true if the array contains the given bin number
26
+ def include?(bin)
27
+ @store.any? do |v|
28
+ v == bin || (v.is_a?(Range) && bin >= v.min && bin <= v.max)
29
+ end
30
+ end
31
+
32
+ # Returns the next bin in the array, starting from the first and remembering the last bin
33
+ # when called the next time.
34
+ # A bin can optionally be supplied in which case the internal pointer will be reset and the
35
+ # next bin that occurs after the given number will be returned.
36
+ def next(after = nil)
37
+ if after
38
+ # Need to work out the pointer here as it is probably out of sync with the
39
+ # last value now
40
+ @pointer = nil
41
+ i = 0
42
+ until @pointer
43
+ v = @store[i]
44
+ if v
45
+ if after == v || (v.is_a?(Range) && after >= v.min && after <= v.max)
46
+ @pointer = i
47
+ @next = after
48
+ elsif after < min_val(v)
49
+ @pointer = previous_pointer(i)
50
+ @next = min_val(v) - 1
51
+ end
52
+ else
53
+ # Gone past the end of the array
54
+ @pointer = @store.size - 1
55
+ @next = min_val(@store[0]) - 1
56
+ end
57
+ i += 1
58
+ end
59
+ end
60
+ if @next
61
+ @pointer ||= 0
62
+ if @store[@pointer].is_a?(Range) && @next != @store[@pointer].max
63
+ @next += 1
64
+ else
65
+ @pointer += 1
66
+ # Return nil when we get to the end of the array
67
+ if @pointer == @store.size
68
+ @pointer -= 1
69
+ return nil
70
+ end
71
+ @next = @store[@pointer]
72
+ @next = @next.min if @next.is_a?(Range)
73
+ end
74
+ else
75
+ v = @store.first
76
+ if v.is_a?(Range)
77
+ @next = v.min
78
+ else
79
+ @next = v
80
+ end
81
+ end
82
+ @next
83
+ end
84
+
85
+ def min
86
+ v = @store.first
87
+ if v.is_a?(Range)
88
+ v.min
89
+ else
90
+ v
91
+ end
92
+ end
93
+
94
+ def max
95
+ v = @store.last
96
+ if v.is_a?(Range)
97
+ v.max
98
+ else
99
+ v
100
+ end
101
+ end
102
+
103
+ private
104
+
105
+ def previous_pointer(i)
106
+ i == 0 ? @store.size - 1 : i - 1
107
+ end
108
+
109
+ def min_val(v)
110
+ v.is_a?(Range) ? v.min : v
111
+ end
112
+
113
+ def max_val(v)
114
+ v.is_a?(Range) ? v.max : v
115
+ end
116
+ end
117
+ end
@@ -0,0 +1,106 @@
1
+ module TestIds
2
+ class Configuration
3
+ class Item
4
+ attr_accessor :include, :exclude, :algorithm
5
+
6
+ def initialize
7
+ @include = BinArray.new
8
+ @exclude = BinArray.new
9
+ end
10
+
11
+ def callback(&block)
12
+ if block_given?
13
+ @callback = block
14
+ else
15
+ @callback
16
+ end
17
+ end
18
+
19
+ def empty?
20
+ include.empty? && exclude.empty? && !algorithm && !callback
21
+ end
22
+
23
+ def function?
24
+ !!algorithm || !!callback
25
+ end
26
+
27
+ def freeze
28
+ @include.freeze
29
+ @exclude.freeze
30
+ super
31
+ end
32
+ end
33
+
34
+ attr_accessor :repo
35
+ attr_reader :on_completion
36
+
37
+ def on_completion
38
+ @on_completion || :publish
39
+ end
40
+
41
+ def on_completion=(val)
42
+ unless %w(publish save discard).include?(val.to_s)
43
+ fail 'on_completion must be set to one of: :publish, :save, :discard'
44
+ end
45
+ @on_completion = val.to_sym
46
+ end
47
+
48
+ def bins
49
+ @bins ||= Item.new
50
+ end
51
+
52
+ def softbins
53
+ @softbins ||= Item.new
54
+ if block_given?
55
+ @softbins.callback(&block)
56
+ end
57
+ @softbins
58
+ end
59
+
60
+ # An alias for config.softbins.algorithm=
61
+ def softbins=(val)
62
+ softbins.algorithm = val
63
+ end
64
+
65
+ def numbers(&block)
66
+ @numbers ||= Item.new
67
+ if block_given?
68
+ @numbers.callback(&block)
69
+ end
70
+ @numbers
71
+ end
72
+
73
+ # An alias for config.numbers.algorithm=
74
+ def numbers=(val)
75
+ numbers.algorithm = val
76
+ end
77
+
78
+ def validate!
79
+ unless validated?
80
+ if bins.algorithm
81
+ fail 'The TestIds bins configuration cannot be set to an algorithm, only a range set by bins.include and bins.exclude is permitted'
82
+ end
83
+ if bins.callback
84
+ fail 'The TestIds bins configuration cannot be set by a callback, only a range set by bins.include and bins.exclude is permitted'
85
+ end
86
+ @validated = true
87
+ freeze
88
+ end
89
+ end
90
+
91
+ def empty?
92
+ bins.empty? && softbins.empty? && numbers.empty?
93
+ end
94
+
95
+ def validated?
96
+ @validated
97
+ end
98
+
99
+ def freeze
100
+ bins.freeze
101
+ softbins.freeze
102
+ numbers.freeze
103
+ super
104
+ end
105
+ end
106
+ end
@@ -0,0 +1,116 @@
1
+ require 'json'
2
+ require 'git'
3
+ module TestIds
4
+ # The Git driver is responsible for committing and fetching the
5
+ # store from the central Git repository.
6
+ #
7
+ # All operations are automatically pushed immediately to the central repository
8
+ # and a lock will be taken out whenever a program generation operation is done in
9
+ # production mode to prevent the need to merge with other users.
10
+ #
11
+ # An instance of this class is instantiated as TestIds.git
12
+ class Git
13
+ attr_reader :repo, :local
14
+
15
+ def initialize(options)
16
+ unless File.exist?("#{options[:local]}/.git")
17
+ FileUtils.rm_rf(options[:local]) if File.exist?(options[:local])
18
+ FileUtils.mkdir_p(options[:local])
19
+ Dir.chdir options[:local] do
20
+ `git clone #{options[:remote]} .`
21
+ if !File.exist?('store.json') || !File.exist?('lock.json')
22
+ # Should really try to use the Git driver for this
23
+ exec 'touch store.json lock.json'
24
+ exec 'git add store.json lock.json'
25
+ exec 'git commit -m "Initial commit"'
26
+ exec 'git push'
27
+ end
28
+ end
29
+ end
30
+ @local = options[:local]
31
+ @repo = ::Git.open(options[:local])
32
+ @repo.reset_hard
33
+ @repo.pull unless options[:no_pull]
34
+ end
35
+
36
+ def exec(cmd)
37
+ r = system(cmd)
38
+ unless r
39
+ fail "Something went wrong running command: #{cmd}"
40
+ end
41
+ end
42
+
43
+ def publish
44
+ write('store.json')
45
+ release_lock
46
+ repo.commit('Publishing latest store')
47
+ repo.push('origin')
48
+ end
49
+
50
+ # Writes the data to the given file and pushes to the remote repo
51
+ def write(path, data = nil)
52
+ f = File.join(local, path)
53
+ File.write(f, data) if data
54
+ repo.add(f)
55
+ end
56
+
57
+ def get_lock
58
+ until available_to_lock?
59
+ puts "Waiting for lock, currently locked by #{lock_user} (the lock will expire in less than #{lock_minutes_remaining} #{'minute'.pluralize(lock_minutes_remaining)} if not released before that)"
60
+ sleep 5
61
+ end
62
+ data = {
63
+ 'user' => User.current.name,
64
+ 'expires' => (Time.now + minutes(5)).to_f
65
+ }
66
+ write('lock.json', JSON.pretty_generate(data))
67
+ repo.commit('Obtaining lock')
68
+ repo.push('origin')
69
+ end
70
+
71
+ def release_lock
72
+ data = {
73
+ 'user' => nil,
74
+ 'expires' => nil
75
+ }
76
+ write('lock.json', JSON.pretty_generate(data))
77
+ end
78
+
79
+ def with_lock
80
+ get_lock
81
+ yield
82
+ ensure
83
+ release_lock
84
+ end
85
+
86
+ def available_to_lock?
87
+ repo.pull
88
+ if lock_content && lock_user && lock_user != User.current.name
89
+ Time.now.to_f > lock_expires
90
+ else
91
+ true
92
+ end
93
+ end
94
+
95
+ def lock_minutes_remaining
96
+ ((lock_expires - Time.now.to_f) / 60).ceil
97
+ end
98
+
99
+ def lock_expires
100
+ lock_content['expires']
101
+ end
102
+
103
+ def lock_user
104
+ lock_content['user']
105
+ end
106
+
107
+ def lock_content
108
+ f = File.join(local, 'lock.json')
109
+ JSON.load(File.read(f)) if File.exist?(f)
110
+ end
111
+
112
+ def minutes(number)
113
+ number * 60
114
+ end
115
+ end
116
+ end
@@ -0,0 +1,14 @@
1
+ require 'origen_testers/flow'
2
+ module OrigenTesters
3
+ module Flow
4
+ # Override the flow.test method to inject our generated bin and
5
+ # test numbers
6
+ alias_method :_orig_test, :test
7
+ def test(instance, options = {})
8
+ unless TestIds.config.empty?
9
+ TestIds.allocator.allocate(instance, options)
10
+ end
11
+ _orig_test(instance, options)
12
+ end
13
+ end
14
+ end
data/lib/test_ids.rb ADDED
@@ -0,0 +1,76 @@
1
+ require 'origen'
2
+ require_relative '../config/application.rb'
3
+ require 'origen_testers'
4
+
5
+ module TestIds
6
+ # THIS FILE SHOULD ONLY BE USED TO LOAD RUNTIME DEPENDENCIES
7
+ # If this plugin has any development dependencies (e.g. dummy DUT or other models that are only used
8
+ # for testing), then these should be loaded from config/boot.rb
9
+
10
+ # Example of how to explicitly require a file
11
+ # require "test_ids/my_file"
12
+
13
+ # Load all files in the lib/test_ids directory.
14
+ # Note that there is no problem from requiring a file twice (Ruby will ignore
15
+ # the second require), so if you have a file that must be required first, then
16
+ # explicitly require it up above and then let this take care of the rest.
17
+ Dir.glob("#{File.dirname(__FILE__)}/test_ids/**/*.rb").sort.each do |file|
18
+ require file
19
+ end
20
+
21
+ class <<self
22
+ def store
23
+ unless @configuration
24
+ fail 'The test ID generator has to be configured before you can start using it'
25
+ end
26
+ @store ||= Store.new
27
+ end
28
+
29
+ def allocator
30
+ unless @configuration
31
+ fail 'The test ID generator has to be configured before you can start using it'
32
+ end
33
+ @allocator ||= Allocator.new
34
+ end
35
+
36
+ def configuration
37
+ if block_given?
38
+ configure do |config|
39
+ yield config
40
+ end
41
+ else
42
+ @configuration ||
43
+ fail('You have to create the configuration first before you can access it')
44
+ end
45
+ end
46
+ alias_method :config, :configuration
47
+
48
+ def configure
49
+ if @configuration
50
+ fail "You can't modify an existing test IDs configuration"
51
+ end
52
+ @configuration = Configuration.new
53
+ yield @configuration
54
+ @configuration.validate!
55
+ allocator.prepare
56
+ end
57
+
58
+ private
59
+
60
+ # For testing, clears all instances including the configuration
61
+ def reset
62
+ @git = nil
63
+ @store = nil
64
+ @allocator = nil
65
+ @configuration = nil
66
+ end
67
+
68
+ def testing=(val)
69
+ @testing = val
70
+ end
71
+
72
+ def testing?
73
+ !!@testing
74
+ end
75
+ end
76
+ end
@@ -0,0 +1,7 @@
1
+ module TestIdsDev
2
+ class DUT
3
+ include Origen::TopLevel
4
+
5
+ attr_accessor :test_ids
6
+ end
7
+ end
@@ -0,0 +1,26 @@
1
+ module TestIdsDev
2
+ class Interface
3
+ include OrigenTesters::ProgramGenerators
4
+
5
+ def initialize(options = {})
6
+ case dut.test_ids
7
+ when 1
8
+ TestIds.configure do |config|
9
+ # Example of testing remote repo
10
+ # config.repo = 'ssh://git@sw-stash.freescale.net/~r49409/test_ids_repo.git'
11
+ config.bins.include << 3
12
+ config.bins.include << (10..20)
13
+ config.bins.exclude << 15
14
+ config.softbins = :bbbxx
15
+ config.numbers do |bin, softbin|
16
+ softbin * 100
17
+ end
18
+ end
19
+ end
20
+ end
21
+
22
+ def func(name, options = {})
23
+ flow.test(name, options)
24
+ end
25
+ end
26
+ end
data/program/prb1.rb ADDED
@@ -0,0 +1,6 @@
1
+ Flow.create do
2
+
3
+ func :t1
4
+ func :t2
5
+ func :t3
6
+ end
@@ -0,0 +1,11 @@
1
+ % render "layouts/basic.html" do
2
+
3
+ %# HTML tags can be embedded in mark down files if you want to do specific custom
4
+ %# formatting like this, but in most cases that is not required.
5
+ <h1><%= Origen.app.namespace %> <span style="font-size: 14px">(<%= Origen.app.version %>)</span></h1>
6
+
7
+ % lines = File.readlines("#{Origen.root}/README.md")
8
+ % lines.shift # Lose the heading, we have our own here
9
+ <%= lines.join('') %>
10
+
11
+ % end
@@ -0,0 +1,15 @@
1
+ ---
2
+ title: <%= options[:title] || Origen.config.name %>
3
+ ---
4
+ <%= render "partials/navbar.html", tab: options[:tab] %>
5
+
6
+ <div class="row">
7
+ %# The markdown attribute is important if you are going to include content written
8
+ %# in markdown, without this is will be included verbatim
9
+ <div class="span12" markdown="1">
10
+ <%= yield %>
11
+
12
+ <%= disqus_comments %>
13
+
14
+ </div>
15
+ </div>
@@ -0,0 +1,21 @@
1
+ <nav class="navbar navbar-inverse navbar-fixed-top">
2
+ <div class="container">
3
+ <div class="navbar-header">
4
+ <button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#navbar" aria-expanded="false" aria-controls="navbar">
5
+ <span class="sr-only">Toggle navigation</span>
6
+ <span class="icon-bar"></span>
7
+ <span class="icon-bar"></span>
8
+ <span class="icon-bar"></span>
9
+ </button>
10
+ <a class="navbar-brand" href="<%= path "/" %>">Home</a>
11
+ </div>
12
+ <div id="navbar" class="collapse navbar-collapse">
13
+ <ul class="nav navbar-nav">
14
+ <li class="<%= options[:tab] == :api ? 'active' : '' %>"><a href="<%= path "/api/" %>">API</a></li>
15
+ <li><a href="https://github.com/Origen-SDK/test_ids">Github</a></li>
16
+ <li class="<%= options[:tab] == :release ? 'active' : '' %>"><a href="<%= path "/release_notes" %>">Release Notes</a></li>
17
+ </ul>
18
+ <%= import "origen/web/logo.html" %>
19
+ </div><!--/.nav-collapse -->
20
+ </div>
21
+ </nav>
@@ -0,0 +1,5 @@
1
+ % render "layouts/basic.html", tab: :release do
2
+
3
+ <%= render "#{Origen.root}/doc/history" %>
4
+
5
+ % end
metadata ADDED
@@ -0,0 +1,103 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: test_ids
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.2.0
5
+ platform: ruby
6
+ authors:
7
+ - Stephen McGinty
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2016-09-06 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: origen
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: 0.7.25
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: 0.7.25
27
+ - !ruby/object:Gem::Dependency
28
+ name: origen_testers
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: git
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ description:
56
+ email:
57
+ - stephen.mcginty@nxp.com
58
+ executables: []
59
+ extensions: []
60
+ extra_rdoc_files: []
61
+ files:
62
+ - config/application.rb
63
+ - config/boot.rb
64
+ - config/commands.rb
65
+ - config/version.rb
66
+ - lib/tasks/test_ids.rake
67
+ - lib/test_ids.rb
68
+ - lib/test_ids/allocator.rb
69
+ - lib/test_ids/bin_array.rb
70
+ - lib/test_ids/configuration.rb
71
+ - lib/test_ids/git.rb
72
+ - lib/test_ids/origen_testers/flow.rb
73
+ - lib/test_ids_dev/dut.rb
74
+ - lib/test_ids_dev/interface.rb
75
+ - program/prb1.rb
76
+ - templates/web/index.md.erb
77
+ - templates/web/layouts/_basic.html.erb
78
+ - templates/web/partials/_navbar.html.erb
79
+ - templates/web/release_notes.md.erb
80
+ homepage:
81
+ licenses: []
82
+ metadata: {}
83
+ post_install_message:
84
+ rdoc_options: []
85
+ require_paths:
86
+ - lib
87
+ required_ruby_version: !ruby/object:Gem::Requirement
88
+ requirements:
89
+ - - ">="
90
+ - !ruby/object:Gem::Version
91
+ version: '2'
92
+ required_rubygems_version: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - ">="
95
+ - !ruby/object:Gem::Version
96
+ version: 1.8.11
97
+ requirements: []
98
+ rubyforge_project:
99
+ rubygems_version: 2.2.2
100
+ signing_key:
101
+ specification_version: 4
102
+ summary: Origen plugin to assign and track test program bins and test numbers
103
+ test_files: []