origen_testers 0.45.3 → 0.48.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 +5 -5
- data/config/application.rb +1 -0
- data/config/version.rb +2 -2
- data/lib/origen_testers.rb +1 -0
- data/lib/origen_testers/atp/flow.rb +4 -0
- data/lib/origen_testers/charz.rb +421 -0
- data/lib/origen_testers/charz/profile.rb +120 -0
- data/lib/origen_testers/charz/routine.rb +38 -0
- data/lib/origen_testers/charz/routines/search_routine.rb +42 -0
- data/lib/origen_testers/charz/routines/shmoo_routine.rb +62 -0
- data/lib/origen_testers/charz/session.rb +100 -0
- data/lib/origen_testers/igxl_based_tester/base.rb +14 -0
- data/lib/origen_testers/origen_ext/generator/flow.rb +1 -0
- data/lib/origen_testers/smartest_based_tester/base/flow.rb +1 -1
- data/lib/origen_testers/smartest_based_tester/base/limits_file.rb +7 -3
- data/lib/origen_testers/smartest_based_tester/base/pattern_master.rb +2 -2
- data/lib/origen_testers/smartest_based_tester/base/test_method.rb +7 -1
- data/lib/origen_testers/smartest_based_tester/base/test_methods/base_tml.rb +18 -7
- data/lib/origen_testers/smartest_based_tester/base/test_methods/dc_tml.rb +1 -1
- data/lib/origen_testers/smartest_based_tester/base/variables_file.rb +16 -12
- data/lib/origen_testers/smartest_based_tester/v93k/templates/vars.tf.erb +10 -6
- data/lib/origen_testers/test/interface.rb +80 -0
- data/pattern/tester_overlay_no_start.rb +41 -0
- data/program/charz.rb +48 -0
- data/program/prb1.rb +1 -1
- data/templates/origen_guides/pattern/ultraflex.md.erb +20 -0
- data/templates/origen_guides/program/charz.md.erb +221 -0
- metadata +43 -5
@@ -0,0 +1,120 @@
|
|
1
|
+
module OrigenTesters
|
2
|
+
module Charz
|
3
|
+
# A Charz Profile
|
4
|
+
# Used to store characterization routines as well as flow control, conditional execution, and test placement meta data
|
5
|
+
class Profile
|
6
|
+
# @!attribute id
|
7
|
+
# @return [Symbol] the id of the current profile, used as a key in OrigenTesters::Charz#charz_profiles hash
|
8
|
+
# @!attribute name
|
9
|
+
# @return [Symbol] the value used (if the user decides) to generate the name of the created charz test. defaults to the value of @id
|
10
|
+
# @!attribute placement
|
11
|
+
# @return [Symbol] placement of the to be created charz tests, defaults to inline, accepts :eof as well. Other placements can be used as well if @valid_placements is altered
|
12
|
+
# @!attribute on_result
|
13
|
+
# @return [Symbol] indicates if the resulting charz tests are depending on the point tests result, valid values include :on_fail, and :on_pass
|
14
|
+
# @!attribute enables
|
15
|
+
# @return [Symbol, String, Array, Hash] enable gates to be wrapped around the resulting charz tests
|
16
|
+
# @!attribute flags
|
17
|
+
# @return [Symbol, String, Array, Hash] flag gates to be wrapped around the resulting charz tests
|
18
|
+
# @!attribute routines
|
19
|
+
# @return [Array] list of charz routines to be called under this profile
|
20
|
+
# @!attribute charz_only
|
21
|
+
# @return [Boolean] indicates if the point tests should or shouldn't be added to the flow
|
22
|
+
attr_accessor :id, :name, :placement, :on_result, :enables, :flags, :routines, :charz_only
|
23
|
+
|
24
|
+
def initialize(id, options, &block)
|
25
|
+
@id = id
|
26
|
+
@id = @id.symbolize unless id.is_a? Symbol
|
27
|
+
options.each { |k, v| instance_variable_set("@#{k}", v) }
|
28
|
+
(block.arity < 1 ? (instance_eval(&block)) : block.call(self)) if block_given?
|
29
|
+
@name ||= id
|
30
|
+
@placement ||= :inline
|
31
|
+
@defined_routines = options.delete(:defined_routines)
|
32
|
+
attrs_ok?
|
33
|
+
end
|
34
|
+
|
35
|
+
def attrs_ok?
|
36
|
+
return if @quality_check == false
|
37
|
+
|
38
|
+
unknown_routines = @routines - @defined_routines
|
39
|
+
unless unknown_routines.empty?
|
40
|
+
Origen.log.error "Profile #{id}: unknown routines: #{unknown_routines}"
|
41
|
+
fail
|
42
|
+
end
|
43
|
+
|
44
|
+
@valid_placements ||= [:inline, :eof]
|
45
|
+
unless @valid_placements.include? @placement
|
46
|
+
Origen.log.error "Profile #{id}: invalid placement value, must be one of: #{@valid_placements}"
|
47
|
+
fail
|
48
|
+
end
|
49
|
+
|
50
|
+
if @on_result
|
51
|
+
@valid_on_results ||= [:on_fail, :fail, :failed, :on_pass, :pass, :passed]
|
52
|
+
unless @valid_on_results.include?(@on_result)
|
53
|
+
Origen.log.error "Profile #{id}: invalid on_result value, must be one of: #{@valid_on_results}"
|
54
|
+
fail
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
if @charz_only && @on_result
|
59
|
+
Origen.log.error "Profile #{id}: @charz_only is set, but @on_result (#{@on_result}) requires the parent test to exist in the flow"
|
60
|
+
fail
|
61
|
+
end
|
62
|
+
|
63
|
+
unless @gate_checks == false
|
64
|
+
gate_check(@enables, :enables) if @enables
|
65
|
+
gate_check(@flags, :flags) if @flags
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
def gate_check(gates, gate_type)
|
70
|
+
case gates
|
71
|
+
when Symbol, String
|
72
|
+
return
|
73
|
+
when Array
|
74
|
+
unknown_gates = gates.reject { |gate| [String, Symbol].include? gate.class }
|
75
|
+
if unknown_gates.empty?
|
76
|
+
return
|
77
|
+
else
|
78
|
+
Origen.log.error "Profile #{id}: Unknown #{gate_type} type(s) in #{gate_type} array."
|
79
|
+
Origen.log.error "Arrays must contain Strings and/or Symbols, but #{unknown_gates.map(&:class).uniq } were found in #{gates}"
|
80
|
+
fail
|
81
|
+
end
|
82
|
+
when Hash
|
83
|
+
gates.each do |gate, gated_routines|
|
84
|
+
if gate.is_a? Hash
|
85
|
+
Origen.log.error "Profile #{id}: #{gate_type} Hash keys cannot be of type Hash, but only Symbol, String, or Array"
|
86
|
+
fail
|
87
|
+
end
|
88
|
+
gate_check(gate, gate_type)
|
89
|
+
gated_routines = [gated_routines] unless gated_routines.is_a? Array
|
90
|
+
unknown_routines = gated_routines - @defined_routines
|
91
|
+
unless unknown_routines.empty?
|
92
|
+
Origen.log.error "Profile #{id}: unknown routines found in @#{gate_type}[#{gate.is_a?(Symbol) ? ':' : ''}#{gate}]: #{unknown_routines}"
|
93
|
+
fail
|
94
|
+
end
|
95
|
+
end
|
96
|
+
else
|
97
|
+
Origen.log.error "Profile #{id}: Unknown #{gate_type} type: #{gates.class}. #{gate_type} must be of type Symbol, String, Array, or Hash"
|
98
|
+
fail
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
def method_missing(m, *args, &block)
|
103
|
+
ivar = "@#{m.to_s.gsub('=', '')}"
|
104
|
+
ivar_sym = ":#{ivar}"
|
105
|
+
if m.to_s =~ /=$/
|
106
|
+
define_singleton_method(m) do |val|
|
107
|
+
instance_variable_set(ivar, val)
|
108
|
+
end
|
109
|
+
elsif instance_variables.include? ivar_sym
|
110
|
+
instance_variable_get(ivar)
|
111
|
+
else
|
112
|
+
define_singleton_method(m) do
|
113
|
+
instance_variable_get(ivar)
|
114
|
+
end
|
115
|
+
end
|
116
|
+
send(m, *args, &block)
|
117
|
+
end
|
118
|
+
end
|
119
|
+
end
|
120
|
+
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
module OrigenTesters
|
2
|
+
module Charz
|
3
|
+
# A Generic charz routine
|
4
|
+
# Used to store characterization test specific meta data, values of which are used by the user to determine test parameter values
|
5
|
+
class Routine
|
6
|
+
# @!attribute id
|
7
|
+
# @return [Symbol] charz routine symbol, used as a key in OrigenTesters::Charz#charz_routines
|
8
|
+
# @!attribute name
|
9
|
+
# @return [Symbol] the value used (if the user decides) to generate the name of the created charz test. defaults to the value of @id
|
10
|
+
attr_accessor :id, :name
|
11
|
+
|
12
|
+
def initialize(id, options = {}, &block)
|
13
|
+
@id = id
|
14
|
+
@id = @id.symbolize unless id.is_a? Symbol
|
15
|
+
options.each { |k, v| instance_variable_set("@#{k}", v) }
|
16
|
+
(block.arity < 1 ? (instance_eval(&block)) : block.call(self)) if block_given?
|
17
|
+
@name ||= @id
|
18
|
+
end
|
19
|
+
|
20
|
+
def method_missing(m, *args, &block)
|
21
|
+
ivar = "@#{m.to_s.gsub('=', '')}"
|
22
|
+
ivar_sym = ":#{ivar}"
|
23
|
+
if m.to_s =~ /=$/
|
24
|
+
define_singleton_method(m) do |val|
|
25
|
+
instance_variable_set(ivar, val)
|
26
|
+
end
|
27
|
+
elsif instance_variables.include? ivar_sym
|
28
|
+
instance_variable_get(ivar)
|
29
|
+
else
|
30
|
+
define_singleton_method(m) do
|
31
|
+
instance_variable_get(ivar)
|
32
|
+
end
|
33
|
+
end
|
34
|
+
send(m, *args, &block)
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
module OrigenTesters
|
2
|
+
module Charz
|
3
|
+
# a 1D search routine
|
4
|
+
class SearchRoutine < Routine
|
5
|
+
# @!attribute start
|
6
|
+
# @return [Numeric] search start value
|
7
|
+
# @!attribute stop
|
8
|
+
# @return [Numeric] search stop value
|
9
|
+
# @!attribute res
|
10
|
+
# @return [Numeric] search resolution
|
11
|
+
# @!attribute spec
|
12
|
+
# @return [Numeric] spec parameter to be searched
|
13
|
+
attr_accessor :start, :stop, :res, :spec
|
14
|
+
|
15
|
+
# Runs the same initialization as Routine
|
16
|
+
# performs some rudimentary quality checks, which can be disabled by setting @quality_check = false
|
17
|
+
def initialize(id, options = {}, &block)
|
18
|
+
super
|
19
|
+
attrs_ok?
|
20
|
+
end
|
21
|
+
|
22
|
+
def attrs_ok?
|
23
|
+
return if @quality_check == false
|
24
|
+
|
25
|
+
@required_attrs ||= [:start, :stop, :res, :spec]
|
26
|
+
attrs = @required_attrs.map { |attr| instance_variable_get("@#{attr}") }
|
27
|
+
if attrs.compact.size != @required_attrs.size
|
28
|
+
Origen.log.error "SearchRoutine #{@id}: unspecified attributes, each of #{@required_attrs} must have a value"
|
29
|
+
fail
|
30
|
+
end
|
31
|
+
|
32
|
+
return if @attr_value_check == false
|
33
|
+
if [@start, @stop, @res].all? { |attr| attr.is_a? Numeric }
|
34
|
+
unless @res <= (@start - @stop).abs
|
35
|
+
Origen.log.error "SearchRoutine #{@id}: Search resolution (#{@res}) is larger than the search range: #{(@start - @stop).abs}"
|
36
|
+
fail
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
@@ -0,0 +1,62 @@
|
|
1
|
+
module OrigenTesters
|
2
|
+
module Charz
|
3
|
+
# A 2D search or "Shmoo" routine
|
4
|
+
class ShmooRoutine < Routine
|
5
|
+
# @!attribute x_start
|
6
|
+
# @return [Numeric] the starting search value for the x dimension's spec search
|
7
|
+
# @!attribute x_stop
|
8
|
+
# @return [Numeric] the stopping search value for the x dimension's spec search
|
9
|
+
# @!attribute x_res
|
10
|
+
# @return the search resolution value for the x dimension's spec search
|
11
|
+
# @!attribute x_spec
|
12
|
+
# @return [Symbol, String] the spec parameter of interest for the x dimension
|
13
|
+
attr_accessor :x_start, :x_stop, :x_res, :x_spec
|
14
|
+
# @!attribute y_start
|
15
|
+
# @return [Numeric] the starting search value for the x dimension's spec search
|
16
|
+
# @!attribute y_stop
|
17
|
+
# @return [Numeric] the stopping search value for the x dimension's spec search
|
18
|
+
# @!attribute y_res
|
19
|
+
# @return the search resolution value for the x dimension's spec search
|
20
|
+
# @!attribute y_spec
|
21
|
+
# @return [Symbol, String] the spec parameter of interest for the x dimension
|
22
|
+
attr_accessor :y_start, :y_stop, :y_res, :y_spec
|
23
|
+
|
24
|
+
def initialize(id, options = {}, &block)
|
25
|
+
super
|
26
|
+
attrs_ok?
|
27
|
+
end
|
28
|
+
|
29
|
+
def attrs_ok?
|
30
|
+
return if @quality_check == false
|
31
|
+
|
32
|
+
@required_attrs ||= [:x_start, :x_stop, :x_res, :x_spec, :y_start, :y_stop, :y_res, :y_spec]
|
33
|
+
attrs = @required_attrs.map { |attr| instance_variable_get("@#{attr}") }
|
34
|
+
if attrs.compact.size != @required_attrs.size
|
35
|
+
Origen.log.error "ShmooRoutine #{@id}: unspecified attributes, each of #{@required_attrs} must have a value"
|
36
|
+
fail
|
37
|
+
end
|
38
|
+
|
39
|
+
return if @attr_value_check == false
|
40
|
+
|
41
|
+
# not sure if I want this check, if so need to scope out if step count is common
|
42
|
+
|
43
|
+
# if [@x_start, @x_stop, @x_res].all? { |attr| attr.is_a? Numeric }
|
44
|
+
# unless @x_res <= (@x_start - @x_stop).abs
|
45
|
+
# Origen.log.error "ShmooRoutine #{@id}: Search x_resolution (#{@x_res} is larger than the search x_range (#{@x_start - @x_stop).abs})"
|
46
|
+
# fail
|
47
|
+
# end
|
48
|
+
# end
|
49
|
+
# if [@y_start, @y_stop, @y_res].all? { |attr| attr.is_a? Numeric }
|
50
|
+
# unless @y_res <= (@y_start - @y_stop).abs
|
51
|
+
# Origen.log.error "ShmooRoutine #{@id}: Search y_resolution (#{@y_res} is larger than the search y_range (#{@y_start - @y_stop).abs})"
|
52
|
+
# fail
|
53
|
+
# end
|
54
|
+
# end
|
55
|
+
unless @x_spec != @y_spec
|
56
|
+
Origen.log.error "ShmooRoutine #{@id}: Search x_spec is identical to y_spec"
|
57
|
+
fail
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
@@ -0,0 +1,100 @@
|
|
1
|
+
module OrigenTesters
|
2
|
+
module Charz
|
3
|
+
# A charz session
|
4
|
+
# contains the final combination of charz object (routines/profiles) and user options to determine how and what charz tests should be created
|
5
|
+
# the session should be checked in your interface to determine the current status and can be queried to make charz generation decisions
|
6
|
+
class Session < Profile
|
7
|
+
# @!attribute defaults
|
8
|
+
# @return [Hash] list of values to instantiate the inherited attributes from Profile with if not altered by the session update
|
9
|
+
attr_accessor :defaults
|
10
|
+
|
11
|
+
def initialize(options = {})
|
12
|
+
@id = :current_charz_session
|
13
|
+
@active = false
|
14
|
+
@valid = false
|
15
|
+
if options[:defaults]
|
16
|
+
@defaults = options[:defaults]
|
17
|
+
else
|
18
|
+
@defaults = {
|
19
|
+
placement: :inline,
|
20
|
+
on_result: nil,
|
21
|
+
enables: nil,
|
22
|
+
flags: nil,
|
23
|
+
name: 'charz',
|
24
|
+
charz_only: false
|
25
|
+
}
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
# Pauses the current session's activity while maintaining everthing else about the sessions state
|
30
|
+
def pause
|
31
|
+
@active = false
|
32
|
+
end
|
33
|
+
|
34
|
+
# Resume activity, if the session is valid
|
35
|
+
def resume
|
36
|
+
if @valid
|
37
|
+
@active = true
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
# Takes a Routine or Profile, and queries it to setup the session's attributes
|
42
|
+
# the attributes values can be set from 3 different sources, in order of priority (first is most important):
|
43
|
+
# - options
|
44
|
+
# - charz object
|
45
|
+
# - defaults
|
46
|
+
#
|
47
|
+
# If the resulting session is invalid, @valid will turn false. Otherwise, the session becomes active
|
48
|
+
def update(charz_obj, options)
|
49
|
+
@valid = false
|
50
|
+
if charz_obj.nil?
|
51
|
+
@active = false
|
52
|
+
@valid = false
|
53
|
+
return @valid
|
54
|
+
end
|
55
|
+
@defined_routines = options.delete(:defined_routines)
|
56
|
+
assign_by_priority(:placement, charz_obj, options)
|
57
|
+
assign_by_priority(:on_result, charz_obj, options)
|
58
|
+
assign_by_priority(:enables, charz_obj, options)
|
59
|
+
assign_by_priority(:flags, charz_obj, options)
|
60
|
+
assign_by_priority(:routines, charz_obj, options)
|
61
|
+
assign_by_priority(:name, charz_obj, options)
|
62
|
+
assign_by_priority(:charz_only, charz_obj, options)
|
63
|
+
attrs_ok?
|
64
|
+
massage_gates
|
65
|
+
@active = true
|
66
|
+
@valid = true
|
67
|
+
end
|
68
|
+
|
69
|
+
private
|
70
|
+
|
71
|
+
# convert hash gates to set convert their routines to type array if not already
|
72
|
+
def massage_gates
|
73
|
+
if @enables.is_a?(Hash)
|
74
|
+
@enables = {}.tap do |new_h|
|
75
|
+
@enables.each { |gates, routines| new_h[gates] = [routines].flatten }
|
76
|
+
end
|
77
|
+
end
|
78
|
+
if @flags.is_a?(Hash)
|
79
|
+
@flags = {}.tap do |new_h|
|
80
|
+
@flags.each { |gates, routines| new_h[gates] = [routines].flatten }
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
# see initialize
|
86
|
+
def assign_by_priority(ivar, charz_obj, options)
|
87
|
+
if options.keys.include?(ivar)
|
88
|
+
instance_variable_set("@#{ivar}", options[ivar])
|
89
|
+
elsif charz_obj.send(ivar)
|
90
|
+
instance_variable_set("@#{ivar}", charz_obj.send(ivar))
|
91
|
+
elsif @defaults.keys.include?(ivar)
|
92
|
+
instance_variable_set("@#{ivar}", @defaults[ivar])
|
93
|
+
else
|
94
|
+
Origen.log.error "Charz Session: No value could be determined for #{ivar}"
|
95
|
+
fail
|
96
|
+
end
|
97
|
+
end
|
98
|
+
end
|
99
|
+
end
|
100
|
+
end
|
@@ -1041,6 +1041,20 @@ module OrigenTesters
|
|
1041
1041
|
# stage = sub_name
|
1042
1042
|
end # subroutine_overlay
|
1043
1043
|
|
1044
|
+
# Disable the automatic addition of the digsrc start command at the beginning of the pattern
|
1045
|
+
#
|
1046
|
+
# @example
|
1047
|
+
# tester.digsrc_skip_start :pin_or_group_name
|
1048
|
+
#
|
1049
|
+
def digsrc_skip_start(pin_or_group)
|
1050
|
+
pin_or_group = dut.pin(pin_or_group).name
|
1051
|
+
if @overlay_history[pin_or_group].nil?
|
1052
|
+
@overlay_history[pin_or_group] = { count: 0, is_digsrc: true, start_applied: true }
|
1053
|
+
else
|
1054
|
+
Origen.log.warn 'tester.digsrc_skip_start must be called before any overlay actions on the pin'
|
1055
|
+
end
|
1056
|
+
end
|
1057
|
+
|
1044
1058
|
# Perform digsrc overlay (called by tester.cycle)
|
1045
1059
|
def digsrc_overlay(options = {})
|
1046
1060
|
options[:overlay] = { change_data: true }.merge(options[:overlay])
|
@@ -138,6 +138,7 @@ module Origen
|
|
138
138
|
end
|
139
139
|
Origen.interface.startup(options) if Origen.interface.respond_to?(:startup)
|
140
140
|
interface.instance_eval(&block)
|
141
|
+
Origen.interface.generate_eof_charz_tests if Origen.interface.respond_to?(:generate_eof_charz_tests)
|
141
142
|
Origen.interface.shutdown(options) if Origen.interface.respond_to?(:shutdown)
|
142
143
|
interface.at_flow_end if interface.respond_to?(:at_flow_end)
|
143
144
|
Origen.app.listeners_for(:on_flow_end).each do |listener|
|
@@ -18,7 +18,7 @@ module OrigenTesters
|
|
18
18
|
# Returns an array containing all runtime variables which get set by the flow
|
19
19
|
attr_reader :set_runtime_variables
|
20
20
|
|
21
|
-
attr_accessor :add_flow_enable, :flow_name, :flow_bypass, :flow_description
|
21
|
+
attr_accessor :add_flow_enable, :flow_name, :flow_bypass, :flow_description, :subdirectory
|
22
22
|
|
23
23
|
def self.generate_flag_name(flag)
|
24
24
|
case flag[0]
|
@@ -119,6 +119,7 @@ module OrigenTesters
|
|
119
119
|
o[:test_name] = extract_test_name(node, o)
|
120
120
|
o[:test_number] = extract_test_number(node, o)
|
121
121
|
o[:limits] = extract_limits(node, o)
|
122
|
+
o[:test_text] = node.find(:test_text).try(:value)
|
122
123
|
if on_fail = node.find(:on_fail)
|
123
124
|
if set_result = on_fail.find(:set_result)
|
124
125
|
if bin = set_result.find(:bin)
|
@@ -252,9 +253,12 @@ module OrigenTesters
|
|
252
253
|
# "Test Number"
|
253
254
|
l << f(options[:test_number])
|
254
255
|
# "Test Text"
|
255
|
-
|
256
|
-
|
257
|
-
|
256
|
+
if options[:test_text]
|
257
|
+
l << f(options[:test_text])
|
258
|
+
else
|
259
|
+
names = ["#{options[:suite_name]}", "#{options[:test_name]}"]
|
260
|
+
l << f(names.uniq.join('.'))
|
261
|
+
end
|
258
262
|
if test_modes.empty?
|
259
263
|
# "Low Limit"
|
260
264
|
l << f((options[:limits][nil] || {})[:lsl])
|
@@ -6,7 +6,7 @@ module OrigenTesters
|
|
6
6
|
include OrigenTesters::Generator
|
7
7
|
|
8
8
|
attr_reader :flow, :paths
|
9
|
-
attr_accessor :filename, :id
|
9
|
+
attr_accessor :filename, :id, :subdirectory
|
10
10
|
|
11
11
|
def initialize(flow = nil)
|
12
12
|
@flow = flow
|
@@ -18,7 +18,7 @@ module OrigenTesters
|
|
18
18
|
end
|
19
19
|
|
20
20
|
def subdirectory
|
21
|
-
'vectors'
|
21
|
+
@subdirectory ||= 'vectors'
|
22
22
|
end
|
23
23
|
|
24
24
|
def paths
|