test_ids 0.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 +7 -0
- data/config/application.rb +103 -0
- data/config/boot.rb +24 -0
- data/config/commands.rb +71 -0
- data/config/version.rb +8 -0
- data/lib/tasks/test_ids.rake +6 -0
- data/lib/test_ids/allocator.rb +359 -0
- data/lib/test_ids/bin_array.rb +117 -0
- data/lib/test_ids/configuration.rb +106 -0
- data/lib/test_ids/git.rb +116 -0
- data/lib/test_ids/origen_testers/flow.rb +14 -0
- data/lib/test_ids.rb +76 -0
- data/lib/test_ids_dev/dut.rb +7 -0
- data/lib/test_ids_dev/interface.rb +26 -0
- data/program/prb1.rb +6 -0
- data/templates/web/index.md.erb +11 -0
- data/templates/web/layouts/_basic.html.erb +15 -0
- data/templates/web/partials/_navbar.html.erb +21 -0
- data/templates/web/release_notes.md.erb +5 -0
- metadata +103 -0
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
|
data/config/commands.rb
ADDED
@@ -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,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
|
data/lib/test_ids/git.rb
ADDED
@@ -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,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,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>
|
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: []
|