IPOAlgorithm 0.0.1

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: ab7632425eddb21f2030c19dfaca5f7f83957d62
4
+ data.tar.gz: 242b2c3342e31338d7a4797cf22767f6d6a684f2
5
+ SHA512:
6
+ metadata.gz: b3d92e4fe19edda9ef2137f717c4cf3dd000aaadb27c75a0ee4d952569bd8ef98239c63954fd26efd33378ecb3150711ae37147b3706f06a5c23b14e272ad0a7
7
+ data.tar.gz: 2a672ea546523b5e2e622c47e3bcda38cc1171e4f3d601a5f39cfb52b175098bd464bc0f1ad83c1218b50f96dfc97c70fc0e31c557b3d346c2f983419ab85c59
@@ -0,0 +1,21 @@
1
+ require_relative '../lib/IPOAlgorithm.rb'
2
+
3
+ #
4
+ #Exception handling for the arguments passed to the algorithm
5
+ #
6
+
7
+ #The algorithm raises an error if the number of arguments is not 2
8
+ raise ArgumentError, "The IPO algorithm needs at least 2 arguments and a maximum of 4." unless 2 <= ARGV.size && ARGV.size <=4
9
+
10
+ #The first argument must be an integer indicating the number of factors/parameters
11
+ raise ArgumentError, "The first parameter must be an Integer i s.t. 2 <= i <= 26 indicating the number of factors." unless (no_parameters = Integer(ARGV.first)) && no_parameters >= 2 && no_parameters < 27
12
+
13
+ #The second argument must be an array that is matched against a regular expression to see if it is of the correct form
14
+ raise ArgumentError, "The second parameter must be an array of form [val1, val2, ... valn] where each vali indicates the number of levels for factor i and n is the number of factors already specified as the first parameter to the algorithm." unless /\[(\d+,)+\d+\]/.match ARGV[1]
15
+
16
+ #The 'levels' array will contain the number of values (levels) for each parameter
17
+ levels = eval ARGV[1]
18
+ raise ArgumentError, "The second parameter must be an array of form [val1, val2, ... valn] where each vali indicates the number of levels for factor i and n is the number of factors already specified as the first parameter to the algorithm." if levels.size != no_parameters
19
+
20
+ puts IPOAlgorithm.new(no_parameters, levels,ARGV[2],ARGV[3]).output_table
21
+
@@ -0,0 +1,393 @@
1
+
2
+ require 'terminal-table'
3
+ require 'csv'
4
+
5
+ class IPOAlgorithm
6
+
7
+ attr_reader :no_parameters
8
+ attr_reader :levels
9
+ attr_reader :parameters
10
+ attr_reader :runs
11
+ attr_reader :actual_parameters
12
+ attr_reader :infeasible_pairs
13
+ attr_reader :output_table
14
+ def initialize(no_parameters,levels,csv=nil,infeasible_csv=nil)
15
+ #
16
+ #preliminary checks of the arguments passed to the algorithm and initialization of the '@no_parameters' instance_variable
17
+ #
18
+ raise ArgumentError, "The number of parameters passed as an argument must be an Integer." unless no_parameters.is_a? Integer
19
+ @no_parameters = no_parameters
20
+ raise ArgumentError, "The IPOAlgorithm can process at most 26 parameters (at least 2 parameters must be provided)" if @no_parameters >26 || @no_parameters < 2
21
+ raise ArgumentError, "The number of parameters doesn't match the size of the array argument representing the number of levels for each parameter." unless levels.size == @no_parameters
22
+ raise ArgumentError, "The number of levels for each argument must be an Integer." unless levels.all? {|l| l.is_a? Integer}
23
+
24
+
25
+ #initialization of the remaining instance_variables
26
+ @levels = levels
27
+ @runs = Array.new
28
+ @parameters = Array.new
29
+ @infeasible_pairs = Array.new
30
+
31
+ #to differentiate values of different factors, each factor is named with uppercase letters in the range [A-Z]; thus the number of factors is limited to a maximum of 26 factors
32
+ 0.upto(@no_parameters-1) do |i|
33
+ @parameters << Parameter.new((65 + i).chr, @levels[i])
34
+ end
35
+
36
+ #
37
+ #reads the parameters and relative values from the csv file passed as an argument and checks if the number of factors and relative levels correspond to the specified values
38
+ #if a .csv file containing the infeasible pairs is passed parses this file in order to check if the specified levels are valid and represents these values by means of the format used by the internal logic of the algorithm
39
+ unless csv.nil?
40
+ @actual_parameters = Array.new
41
+ #parses the .csv file containing the factors and relative levels and saves the values in a tabular format
42
+ read_parameters_from_csv(csv)
43
+ #performs checks for inconsistencies
44
+ raise ArgumentError, "The number of factors in the CSV file doesn't correspond to the specified value" unless @actual_parameters.size == @parameters.size
45
+ @actual_parameters.each_with_index {|t,i| raise ArgumentError, "The number of levels doesn't correspond to the specified values" unless t.size - 1 == @levels[i]}
46
+ #in case an additional file for the infeasible pairs is passed parses this file
47
+ unless infeasible_csv.nil?
48
+ #reads the infeasible pairs and stores them in the '@infeasible_pairs' instance variable
49
+ read_infeasible_pairs(infeasible_csv)
50
+
51
+ @infeasible_pairs.each_with_index do |p,i|
52
+ #checks if the levels belonging to the pair exist and saves the 'a1', 'b3', ... internal representation using temporary variables
53
+ temp1 = find_value p.first
54
+ temp2 = find_value p.last
55
+
56
+ #raises ArgumentError if the levels weren't specified in the factors' .csv file
57
+ raise ArgumentError,"#{p.first} level specified in the #{infeasible_csv} file doesn't belong to any parameter in the #{csv} file" if temp1.nil?
58
+ raise ArgumentError,"#{p.last} level specified in the #{infeasible_csv} file doesn't belong to any parameter in the #{csv} file" if temp2.nil?
59
+ #overwrites the levels using the internal representation
60
+ @infeasible_pairs[i] = [temp1,temp2]
61
+ #orders the levels in alphabetically ascending order
62
+ @infeasible_pairs[i].sort_by {|p| p}
63
+
64
+ end
65
+ end
66
+ end
67
+
68
+ #generate all pairs of the first two parameters F1 and F2 and add them to the runs
69
+
70
+ @parameters[0].values.each do |value1|
71
+ @parameters[1].values.each do |value2|
72
+ @runs << (Run.new.elements << value1 << value2)
73
+ end
74
+ end
75
+
76
+ #removes the infeasible pairs
77
+ @runs -= @infeasible_pairs
78
+
79
+
80
+ #possible termination if there are 2 factors
81
+ return nil if no_parameters == 2
82
+
83
+ #given that there are more than 2 factors there's need to repeat the next steps for all the remaining factors
84
+ 2.upto(no_parameters-1) do |current_F|
85
+ #replace all runs with an extended version containing an appropriate value of the current parameter; keep track of the uncovered pairs
86
+ temp = horizontal_growth(current_F)
87
+ @runs = temp[:runs]
88
+ uncovered_pairs = temp[:uncovered_pairs]
89
+
90
+ #if there aren't any uncovered pairs end the current step else apply the vertical growth procedure
91
+ @runs += vertical_growth(uncovered_pairs,current_F) unless uncovered_pairs.empty?
92
+ end
93
+
94
+ #displays the runs obtained by the algorithm in a generic form and, if a .csv file was provided, with real values
95
+ format_output_without_CSV if csv.nil?
96
+ format_output_with_CSV unless csv.nil?
97
+ self
98
+ end
99
+
100
+ private
101
+
102
+ def horizontal_growth(f)
103
+
104
+ #calculate all the pairs formed by the values of already processed factors and those of the current factor
105
+ ap = Array.new
106
+ 0.upto(f-1) {|i| ap += pairs(i,f)}
107
+
108
+ #removes the infeasible pairs
109
+
110
+ ap -= @infeasible_pairs
111
+
112
+ #calculate the minimum between the number of tests already obtained and the number of levels of the current factor
113
+ c = [@parameters[f].no_levels, @runs.size].min
114
+
115
+ #the runs already obtained are extended with values of the current factor; the pairs contained by the runs thus extended, are removed from the ap (all pairs) set; given the fact that values of the factor can give rise to infeasible pairs the original IPO algorithm is thus modified:
116
+ #for each run an attempt is made to extend the run with a different level of the factor under observation; the first level which hasn't been already used and that doesn't form infeasible pairs with the values contained by the run is added to the run; the 'loop_counter' variable serves as a flag to indicate if such a value is available; if it reaches 2 then it means that all the values have been examined and there isn't such a level
117
+ #an already used level is used to extend the run; if all levels give rise to unfeasible pairs an error is raised indicating that the input to the algorithm cannot produce valid runs
118
+
119
+ #saves the already used levels in order to minimize the number of tests
120
+ already_used_level = []
121
+ tPrime = Array.new
122
+ 0.upto(c-1) do |i|
123
+ #the evaluation of levels of the current factor under observation starts at a specific index in accordance with the original IPO Algorithm, so that a minimal set of runs is obtained
124
+ j=i
125
+ loop_counter=0
126
+ loop do
127
+ #attempt to extend the run with the next level of the factor
128
+ temp = ext(@runs[i].dup,@parameters[f].values[j])
129
+ #if it doesn't form infeasible pairs and it hasn't been already used the run is extended with this level
130
+ if (!pairs_for_run(temp).any? {|t| @infeasible_pairs.include? t} && !already_used_level.include?(j))
131
+ tPrime << temp
132
+ already_used_level << j
133
+ break
134
+ end
135
+ #the parsing of the levels of the factor restarts with the first level in a circular fashion; the loop_counter flag is increased
136
+ if j==@parameters[f].no_levels-1
137
+ j=0
138
+ loop_counter+=1
139
+ else
140
+ #increases the index that points at the next level of the factor
141
+ j+=1
142
+ end
143
+ #if the domain of the factor has been parsed and no valid level was encountered stop the search
144
+ break if loop_counter == 2
145
+ end
146
+ #search an already used level to extend the run in a similar fashion to the steps commented above; this time raise an error if no such level is found meaning that there is no valid run that respects the contraints of infeasible pairs
147
+ j=i
148
+ loop_counter_2 = 0
149
+ if loop_counter==2
150
+ loop do
151
+ temp = ext(@runs[i].dup,@parameters[f].values[j])
152
+ if (!pairs_for_run(temp).any? {|t| @infeasible_pairs.include? t})
153
+ tPrime << temp
154
+ break
155
+ end
156
+ if j==@parameters[f].no_levels-1
157
+ j=0
158
+ loop_counter_2+=1
159
+ else
160
+ j+=1
161
+ end
162
+ loop_counter_2
163
+ raise ArgumentError,"The input provided doesn't allow the creation of valid runs(raised in horizontal_growth)" if loop_counter_2 == 2
164
+ end
165
+ end
166
+
167
+ #remove the covered pairs by the run produced
168
+ ap -= pairs_for_run(tPrime[i])
169
+ end
170
+
171
+ #if the minimum is the preexistent number of runs than the method return the extended runs
172
+ return {runs: tPrime, uncovered_pairs: ap} if c == @runs.size
173
+
174
+ #
175
+ #otherwise extend the remaining runs by values of the current factor under observation that cover the maximum number of pairs in ap
176
+ #
177
+
178
+ valuePrime = nil
179
+ apPrime = []
180
+
181
+ #scan every run already produced
182
+ c.upto(@runs.size-1) do |j|
183
+ apSecond = []
184
+ #for every value of the current factor under observation
185
+ @parameters[f].values.each_with_index do |value,index|
186
+ #extend the run with the value
187
+ tempa = ext(@runs[j].dup,value)
188
+ #check if any infeasible pairs have been obtained
189
+ unless pairs_for_run(tempa).any? {|p| @infeasible_pairs.include? p}
190
+ #calculate the pairs contained by the run thus obtained
191
+ apSecond = pairs_for_run(tempa)
192
+ #find the value that adds the maximum pairwise coverage
193
+ if apSecond.find_all {|e| ap.include? e}.size > apPrime.find_all {|e| ap.include? e}.size
194
+ apPrime = apSecond
195
+ valuePrime = value
196
+ end
197
+ end
198
+ raise ArgumentError,"The input provided doesn't allow the creation of valid runs(raised in horizontal_growth)" if apSecond.nil? && index == @parameters[f].no_levels-1
199
+ end
200
+ #extend the run with the value found in the previous step
201
+ tPrime << ext(@runs[j].dup,valuePrime)
202
+ #remove the pairs covered by the newly extended run
203
+ ap -= apPrime
204
+ end
205
+ {runs: tPrime, uncovered_pairs: ap}
206
+ end
207
+
208
+ #
209
+ #permits to extend a run with a value
210
+ #
211
+ def ext(run, val)
212
+ run << val
213
+ end
214
+
215
+ #
216
+ #given the indexes for two parameters produces the cartesian product of their respective values
217
+ #
218
+ def pairs(f1, f2)
219
+ temp = Array.new
220
+ @parameters[f1].values.each do |value1|
221
+ @parameters[f2].values.each do |value2|
222
+ temp << (Run.new.elements << value1 << value2)
223
+ end
224
+ end
225
+ temp
226
+ end
227
+
228
+ #
229
+ #produces all the pairs of values given a run
230
+ #
231
+ def pairs_for_run(run)
232
+ temp = Array.new
233
+ 0.upto(run.size-2) do |index1|
234
+ (index1+1).upto(run.size-1) do |index2|
235
+ temp << ([] << run[index1] << run[index2])
236
+ end
237
+ end
238
+ temp
239
+ end
240
+
241
+ #
242
+ #Vertical Growth procedure to produce additional runs that cover the remaining uncovered pairs
243
+ #
244
+ def vertical_growth(uncovered_pairs,current_F)
245
+ current_F = current_F
246
+ tPrime = []
247
+ #scan every run
248
+ uncovered_pairs.each do |u_pair|
249
+ #calculate the index of the factor whose value doesn't form any pair with the factor under observation within the preexistent runs
250
+ uncovered_F = u_pair[0][0].ord - 97
251
+ #use the flag to indicate if the pair has been covered
252
+ flag = false
253
+ #scan every preexistent run
254
+ @runs.each do |run|
255
+ #if the run contains a placeholder to indicate the absence of a value where a value of the factor with index uncovered_F should be, overwrite the placeholder with the uncovered value
256
+ temp = run.dup
257
+ temp[uncovered_F] = u_pair[0]
258
+ if run[uncovered_F].nil? && !pairs_for_run(temp).any? {|p| @infeasible_pairs.include? p}
259
+ run[uncovered_F] = u_pair[0]
260
+ #indicate that the pair is covered and break the loop
261
+ flag = true
262
+ break
263
+ end
264
+ end
265
+
266
+ #if no placeholder was found create a new run
267
+ if !flag
268
+ temp = []
269
+ 0.upto(current_F-1) do |j|
270
+ #use placeholders for values belonging to factors different from the one whose value isn't covered (uncovered_F)
271
+ if j != uncovered_F
272
+ temp << nil
273
+ #save the value needing coverage
274
+ else
275
+ temp << u_pair[0]
276
+ end
277
+ end
278
+ #append the second value of the uncovered pair, that belongs to the factor under observation (current_F)
279
+ temp << u_pair[1]
280
+ #add the run obtained to the set of runs
281
+ tPrime << temp
282
+ end
283
+ end
284
+ #
285
+ #replace each placeholder with a value of the corresponding factor selected in random fashion but respecting the constraints on infeasible pairs
286
+ #
287
+ #for each run obtained in the previous step
288
+ tPrime.each do |run|
289
+ #for each level of the run
290
+ run.each_with_index do |val,index|
291
+ #if the value is nil
292
+ if run[index].nil?
293
+ #create a copy of the run
294
+ temp = run.dup
295
+ #generate a random index in the range that goes from zero to the number of levels of the factor - 1
296
+ i = rand(0...@parameters[index].no_levels)
297
+ #store this index
298
+ j = i
299
+ loop do
300
+ #check if the level at index j is a valid one in the sense that doesn't form infeasible pairs
301
+ temp[index] = @parameters[index].values[j]
302
+ #if it's valid stop the search
303
+ break unless pairs_for_run(temp).any? {|p| @infeasible_pairs.include? p}
304
+ #tries next level
305
+ j+=1
306
+ #if it reaches the last level of the factor restart from the first level in a circular fashion
307
+ j = 0 if j == @parameters[index].no_levels
308
+ #if no level is valid raise an exception
309
+ raise ArgumentError,"The input provided doesn't allow the creation of valid runs(raised in vertical_growth)" if j == i
310
+ end
311
+ #save the level thus obtained
312
+ run[index] = temp[index]
313
+ end
314
+ end
315
+ end
316
+ #return the runs obtained by the Vertical_Growth method
317
+ tPrime
318
+ end
319
+
320
+ #
321
+ #formats the runs obtained by the algorithm in a table with an appropriate structure
322
+ #
323
+ def format_output_without_CSV
324
+ headings = [] << "Run"
325
+ rows = []
326
+ @parameters.each_with_index {|p,i| headings << "F#{i+1}"}
327
+ @runs.each_with_index {|r,i| temp = ["#{i+1}"]; temp += r; rows << temp}
328
+ @output_table = Terminal::Table.new :title => "IPO Algorithm tests output", :headings => headings, :rows => rows
329
+ end
330
+
331
+ #
332
+ #formats the runs obtained by the algorithm substituting real values passed by means of the .csv file
333
+ #
334
+
335
+ def format_output_with_CSV()
336
+ headings = [] << "Run"
337
+ rows = []
338
+ @actual_parameters.each {|t| headings << t[0]}
339
+ @runs.each_with_index do |run,i|
340
+ temp = ["#{i+1}"];
341
+ run.each_with_index do |r,i|
342
+ temp << @actual_parameters[i][Integer(run[i][1])]
343
+ end
344
+ rows << temp
345
+ end
346
+ @output_table = Terminal::Table.new :title => "IPO Algorithm tests output", :headings => headings, :rows => rows
347
+ end
348
+
349
+ #
350
+ #the procedure reads the .csv file and produces a tabular representation
351
+ #
352
+ def read_parameters_from_csv(csv)
353
+ begin
354
+ temp = CSV.read(csv)
355
+ rescue Exception
356
+ raise ArgumentError, "It wasn't possible to read from #{csv} file."
357
+ end
358
+ temp.each_with_index do |row,index|
359
+ @actual_parameters[index] = row[0].split(';')
360
+ end
361
+ end
362
+
363
+ #
364
+ #reads the infeasible pairs from the .csv file
365
+ #
366
+ def read_infeasible_pairs(infeasible_csv)
367
+ begin
368
+ temp = CSV.read(infeasible_csv)
369
+ rescue Exception
370
+ raise ArgumentError, "It wasn't possible to read from #{infeasible_csv} file."
371
+ end
372
+ temp.each_with_index do |row,index|
373
+ @infeasible_pairs[index] = row[0].split(';')
374
+ end
375
+ raise ArgumentError,"Every line of the .csv file containing infeasible pairs must contain exactly 2 elements" unless @infeasible_pairs.all? {|pair| pair.size == 2}
376
+ end
377
+
378
+ #permits to retrieve a level from the tabular representation of the .csv file of factors; if it exists the internal representation of the level is returned, consisting of a lowercase letter from a to z and an index
379
+ def find_value(val)
380
+ temp = nil
381
+ @actual_parameters.each_with_index do |row,index|
382
+ row.each_with_index do |element, index1|
383
+ if (element.downcase.delete(' ') == val.downcase.delete(' ') && index1 != 0)
384
+ return (index + 97).chr + index1.to_s
385
+ end
386
+ end
387
+ end
388
+ return nil
389
+ end
390
+ end
391
+
392
+ require_relative 'IPOAlgorithm/Parameter'
393
+ require_relative 'IPOAlgorithm/Run'
@@ -0,0 +1,12 @@
1
+ #the class 'Parameter' models a Factor/Parameter
2
+ class IPOAlgorithm::Parameter
3
+ attr_reader :name, :no_levels, :values
4
+
5
+ def initialize(name, levels)
6
+ @values = Array.new
7
+ @name = name
8
+ @no_levels = levels
9
+ #each value is represented using the lowercase version of the factor's name and an index subscript starting from 1 up to the number of levels of the factor
10
+ 1.upto(levels) {|i| values << name.downcase + i.to_s}
11
+ end
12
+ end
@@ -0,0 +1,8 @@
1
+ class IPOAlgorithm::Run
2
+ attr_accessor :elements
3
+
4
+ def initialize
5
+ @elements = Array.new
6
+ end
7
+
8
+ end
@@ -0,0 +1,4 @@
1
+ Hardware;Dell Dimension Series;Apple iMac;Apple MacBook Pro;;
2
+ OS;Windows Server 2008: Web Edition;Windows Server 2008: R2 standard edition;Windows 7 Enterprise;OS 10.6;OS 10.7
3
+ Browser;Internet Explorer 9;Internet Explorer;Chrome;Safari 5.1.6;Firefox
4
+ AnotherFactor;f1;f2;;;
@@ -0,0 +1,20 @@
1
+ Dell Dimension Series;OS 10.6
2
+ Dell Dimension Series;OS 10.7
3
+ Apple iMac;Windows Server 2008: Web Edition
4
+ Apple iMac;Windows Server 2008: R2 standard edition
5
+ Apple iMac;Windows 7 Enterprise
6
+ Apple MacBook Pro;Windows Server 2008: Web Edition
7
+ Apple MacBook Pro;Windows Server 2008: R2 standard edition
8
+ Apple MacBook Pro;Windows 7 Enterprise
9
+ Dell Dimension Series;Safari 5.1.6
10
+ Apple iMac;Internet Explorer 9
11
+ Apple iMac;Internet Explorer
12
+ Apple MacBook Pro;Internet Explorer 9
13
+ Apple MacBook Pro;Internet Explorer
14
+ OS 10.6;Internet Explorer 9
15
+ OS 10.6;Internet Explorer
16
+ OS 10.7;Internet Explorer 9
17
+ OS 10.7;Internet Explorer
18
+ Windows Server 2008: Web Edition;Safari 5.1.6
19
+ Windows Server 2008: R2 standard edition;Safari 5.1.6
20
+ Windows 7 Enterprise;Safari 5.1.6
@@ -0,0 +1,6 @@
1
+ Dell Dimension Series;OS 10.6
2
+ Apple iMac;Windows 7 Enterprise
3
+ OS 10.6;Internet Explorer 9
4
+ Windows 7 Enterprise;Safari 5.1.6
5
+ Ubuntu 16.04 LTS;Internet Explorer 9
6
+ Ubuntu 16.04 LTS;Safari 5.1.6
@@ -0,0 +1,4 @@
1
+ Dell Dimension Series;OS 10.6
2
+ Apple iMac;Windows 7 Enterprise
3
+ OS 10.6;Internet Explorer 9
4
+ Windows 7 Enterprise;Safari 5.1.6
@@ -0,0 +1,20 @@
1
+ Dell Dimension Series;OS 10.6
2
+ Dell Dimension Series;OS 10.7
3
+ Apple iMac;Windows Server 2008: Web Edition
4
+ Apple iMac;Windows Server 2008: R2 standard edition
5
+ Apple iMac;Windows 7 Enterprise
6
+ Apple MacBook Pro;Windows Server 2008: Web Edition
7
+ Apple MacBook Pro;Windows Server 2008: R2 standard edition
8
+ Apple MacBook Pro;Windows 7 Enterprise
9
+ Dell Dimension Series;Safari 5.1.6
10
+ Apple iMac;Internet Explorer 9
11
+ Apple iMac;
12
+ Apple MacBook Pro;Internet Explorer 9
13
+ Apple MacBook Pro;Internet Explorer
14
+ OS 10.6;Internet Explorer 9
15
+ OS 10.6;Internet Explorer
16
+ OS 10.7;Internet Explorer 9
17
+ OS 10.7;Internet Explorer
18
+ Windows Server 2008: Web Edition;Safari 5.1.6
19
+ Windows Server 2008: R2 standard edition;Safari 5.1.6
20
+ Windows 7 Enterprise;Safari 5.1.6
@@ -0,0 +1,20 @@
1
+ Dell Dimension Series;OS 10.6
2
+ Dell Dimension Series;OS 10.7
3
+ Apple iMac;Windows Server 2008: Web Edition
4
+ Apple iMac;Windows Server 2008: R2 standard edition
5
+ Apple iMac;Windows 7 Enterprise
6
+ Apple MacBook Pro;Windows Server 2008: Web Edition
7
+ Apple MacBook Pro;Windows Server 2008: R2 standard edition
8
+ Apple MacBook Pro;Windows 10 Enterprise
9
+ Dell Dimension Series;Safari 5.1.6
10
+ Apple iMac;Internet Explorer 9
11
+ Apple iMac;Internet Explorer
12
+ Apple MacBook Pro;Internet Explorer 9
13
+ Apple MacBook Pro;Internet Explorer
14
+ OS 10.6;Internet Explorer 9
15
+ OS 10.6;Internet Explorer
16
+ OS 10.7;Internet Explorer 9
17
+ OS 10.7;Internet Explorer
18
+ Windows Server 2008: Web Edition;Safari 5.1.6
19
+ Windows Server 2008: R2 standard edition;Safari 5.1.6
20
+ Windows 7 Enterprise;Safari 5.1.6
@@ -0,0 +1,3 @@
1
+ Hardware;Dell Dimension Series;Apple iMac;Apple MacBook Pro;;
2
+ OS;Windows Server 2008: Web Edition;Windows Server 2008: R2 standard edition;Windows 7 Enterprise;OS 10.6;OS 10.7
3
+ Browser;Internet Explorer 9;Internet Explorer;Chrome;Safari 5.1.6;Firefox
@@ -0,0 +1,3 @@
1
+ Hardware;Dell Dimension Series;Apple iMac
2
+ OS;Windows 7 Enterprise;OS 10.6;Ubuntu 16.04 LTS
3
+ Browser;Internet Explorer 9;Safari 5.1.6
@@ -0,0 +1,3 @@
1
+ Hardware;Dell Dimension Series;Apple iMac
2
+ OS;Windows 7 Enterprise;OS 10.6
3
+ Browser;Internet Explorer 9;Safari 5.1.6
@@ -0,0 +1,120 @@
1
+ require 'test/unit'
2
+ require_relative '../lib/IPOAlgorithm.rb'
3
+
4
+ #tests the response of the algorithm with only the first 2 arguments provided
5
+ class TestIpoWithoutCSV < Test::Unit::TestCase
6
+
7
+ #it specifies the maximum number of parameters to be tested
8
+ def setup
9
+ @no_tests = 10
10
+ end
11
+
12
+ #tests if the algorithm spots invalid inputs
13
+ def test_invalid_number_of_parameters
14
+ assert_raise(ArgumentError.new("The number of parameters passed as an argument must be an Integer.")) {IPOAlgorithm.new(3.5,[3,5,5])}
15
+ assert_raise(ArgumentError.new("The IPOAlgorithm can process at most 26 parameters (at least 2 parameters must be provided)")) {IPOAlgorithm.new(27,[3,5,5])}
16
+ assert_raise(ArgumentError.new("The IPOAlgorithm can process at most 26 parameters (at least 2 parameters must be provided)")) {IPOAlgorithm.new(1,[3,5,5])}
17
+ end
18
+
19
+ #tests if the algorithm recognizes when the specified number of parameters doesn't correspond to the size of the array representing the levels for each parameter
20
+ def test_different_no_parameters
21
+ assert_raise(ArgumentError.new("The number of parameters doesn't match the size of the array argument representing the number of levels for each parameter.")) {IPOAlgorithm.new(3,[3,5,5,4])}
22
+ end
23
+
24
+ #tests if the algorithm recognizes when the specified number of levels for a parameter isn't a valid value
25
+ def test_invalid_levels
26
+ assert_raise(ArgumentError.new("The number of levels for each argument must be an Integer.")) {IPOAlgorithm.new(3,[3,5,"a"])}
27
+ end
28
+
29
+ #test if all the pairs have been covered; to do so the Kernel#eval method is used to create objects of IPO class dynamically and to test the pairwise coverage
30
+ def test_all_pairs_covered
31
+ puts "\n"
32
+ 2.upto(@no_tests) do |no_factors|
33
+ levels="["
34
+ 1.upto(no_factors) {|factor| levels += rand(2...10 ).to_s + ","}
35
+ levels[levels.size-1] = "]"
36
+ declaration = "@ipo_#{no_factors}_factors = IPOAlgorithm.new(#{no_factors}, #{levels})"
37
+ puts declaration + " ----->I'm testing #{no_factors} factors"
38
+ eval declaration
39
+ eval "assert_not_nil @ipo_#{no_factors}_factors"
40
+ eval "assert_equal @ipo_#{no_factors}_factors.class, IPOAlgorithm"
41
+ eval "assert all_pairs_covered @ipo_#{no_factors}_factors"
42
+
43
+ end
44
+ end
45
+
46
+ #method that given an IPO object outputs a boolean value indicating the pairwise coverage
47
+ def all_pairs_covered(ipo)
48
+ all_pairs = []
49
+ 0.upto(ipo.no_parameters-2) do |index1|
50
+ (index1+1).upto(ipo.no_parameters-1) do |index2|
51
+ all_pairs += ipo.instance_eval {pairs(index1,index2)}
52
+ end
53
+ end
54
+ ipo.runs.each {|r| all_pairs -= ipo.instance_eval{pairs_for_run(r)}}
55
+ (all_pairs-ipo.infeasible_pairs).empty?
56
+ end
57
+ end
58
+
59
+ #tests the algorithm response with the first two mandatory arguments and the third optional one containing the path to the .csv file with the actual values of the parameters; the pairwise coverage isn't tested because it is the same as for the TestIpoWithoutCSV tests.
60
+ class TestIpoWithCSV < Test::Unit::TestCase
61
+
62
+ #tests if the .csv file has been correctly read and the tabular representation created
63
+ def setup
64
+ @path = File.expand_path File.dirname(__FILE__)
65
+ end
66
+ def test_table_created
67
+ @ipo = IPOAlgorithm.new(3,[3,5,5],"#{@path}/input_file.csv")
68
+ assert_not_nil @ipo.actual_parameters
69
+ end
70
+
71
+ #tests if the algorithm spots invalid .csv files
72
+ def test_invalid_file
73
+ assert_raise(ArgumentError.new("It wasn't possible to read from #{@path}/invalid_file.csv file.")) {IPOAlgorithm.new(3,[3,5,5],"#{@path}/invalid_file.csv")}
74
+ end
75
+
76
+ #test if the algorithm recognizes a discrepancy between the values of the first two arguments and the values contained within the .csv file
77
+ def test_incorrect_csv
78
+ assert_raise(ArgumentError.new("The number of factors in the CSV file doesn't correspond to the specified value")) {IPOAlgorithm.new(3,[3,5,5],"#{@path}/incorrect_file.csv")}
79
+ assert_raise(ArgumentError.new("The number of levels doesn't correspond to the specified values")) {IPOAlgorithm.new(3,[3,4,5],"#{@path}/input_file.csv")}
80
+ end
81
+
82
+ end
83
+
84
+ #tests the algorithm response when all four arguments are provided
85
+ class TestIpoWithCSVandInfeasiblePairs < Test::Unit::TestCase
86
+ def setup
87
+ @path = File.expand_path File.dirname(__FILE__)
88
+ end
89
+
90
+ #tests if the algorithm recognizes invalid files for the infeasible pairs
91
+ def test_invalid_file
92
+ assert_raise(ArgumentError.new("It wasn't possible to read from #{@path}/invalid_infeasible_file.csv file.")) {IPOAlgorithm.new(3,[3,5,5],"#{@path}/input_file.csv","#{@path}/invalid_infeasible_file.csv")}
93
+ end
94
+
95
+ #tests if the algorithm spots rows of the .csv file containing infeasible pairs with less or more than 2 values
96
+ def test_not_all_lines_are_pairs
97
+ assert_raise(ArgumentError.new("Every line of the .csv file containing infeasible pairs must contain exactly 2 elements")) {IPOAlgorithm.new(3,[3,5,5],"#{@path}/input_file.csv","#{@path}/infeasible_pairs_not_all_pairs.csv")}
98
+ end
99
+
100
+ #tests if the algorithm recognizes values specified by the .csv file containing infeasible pairs that have not been declared by the .csv file containing the actual values of the paramenters
101
+ def test_unspecified_level
102
+ assert_raise(ArgumentError.new("Windows 10 Enterprise level specified in the #{@path}/infeasible_pairs_unspecified_level.csv file doesn't belong to any parameter in the #{@path}/input_file.csv file")) {IPOAlgorithm.new(3,[3,5,5],"#{@path}/input_file.csv","#{@path}/infeasible_pairs_unspecified_level.csv")}
103
+ end
104
+
105
+ #tests if the algorithm spots infeasible pairs for which it is impossible to obtain valid runs; more precisely this impossibility is spotted within the Vertical Growth step of the algorithm.
106
+ def test_impossible_combination_VG
107
+ assert_raise(ArgumentError.new("The input provided doesn't allow the creation of valid runs(raised in vertical_growth)")) {IPOAlgorithm.new(3,[2,2,2],"#{@path}/input_file_impossible_combination_VG.csv","#{@path}/infeasible_pairs_impossible_combination_VG.csv")}
108
+ end
109
+
110
+ #tests if the algorithm spots infeasible pairs for which it is impossible to obtain valid runs; more precisely this impossibility is spotted within the Horizontal Growth step of the algorithm.
111
+ def test_impossible_combination_HG
112
+ assert_raise(ArgumentError.new("The input provided doesn't allow the creation of valid runs(raised in horizontal_growth)")) {IPOAlgorithm.new(3,[2,3,2],"#{@path}/input_file_impossible_combination_HG.csv","#{@path}/infeasible_pairs_impossible_combination_HG.csv")}
113
+ end
114
+
115
+ #test if indeed no infeasible pair has been covered
116
+ def test_no_infeasible_pair_covered
117
+ assert TestIpoWithoutCSV.new(5).all_pairs_covered(IPOAlgorithm.new(3,[3,5,5],"#{@path}/input_file.csv","#{@path}/infeasible_pairs.csv"))
118
+ end
119
+ end
120
+
metadata ADDED
@@ -0,0 +1,73 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: IPOAlgorithm
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - Mihai Soldan
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2016-07-27 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: terminal-table
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - '='
18
+ - !ruby/object:Gem::Version
19
+ version: 1.6.0
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - '='
25
+ - !ruby/object:Gem::Version
26
+ version: 1.6.0
27
+ description: IPO (In-Parameter-Order) procedure for the generation of mixed level
28
+ covering arrays with constraints of infeasible pairs.
29
+ email: catalinmihai.soldan@studenti.unicam.it
30
+ executables: []
31
+ extensions: []
32
+ extra_rdoc_files: []
33
+ files:
34
+ - bin/IPOAlgorithm.rb
35
+ - lib/IPOAlgorithm.rb
36
+ - lib/IPOAlgorithm/Parameter.rb
37
+ - lib/IPOAlgorithm/Run.rb
38
+ - test/incorrect_file.csv
39
+ - test/infeasible_pairs.csv
40
+ - test/infeasible_pairs_impossible_combination_HG.csv
41
+ - test/infeasible_pairs_impossible_combination_VG.csv
42
+ - test/infeasible_pairs_not_all_pairs.csv
43
+ - test/infeasible_pairs_unspecified_level.csv
44
+ - test/input_file.csv
45
+ - test/input_file_impossible_combination_HG.csv
46
+ - test/input_file_impossible_combination_VG.csv
47
+ - test/test_IPOAlgorithm.rb
48
+ homepage: http://rubygems.org/gems/IPOAlgorithm
49
+ licenses:
50
+ - GNU GPLv3
51
+ metadata: {}
52
+ post_install_message:
53
+ rdoc_options: []
54
+ require_paths:
55
+ - lib
56
+ required_ruby_version: !ruby/object:Gem::Requirement
57
+ requirements:
58
+ - - ">="
59
+ - !ruby/object:Gem::Version
60
+ version: '0'
61
+ required_rubygems_version: !ruby/object:Gem::Requirement
62
+ requirements:
63
+ - - ">="
64
+ - !ruby/object:Gem::Version
65
+ version: '0'
66
+ requirements: []
67
+ rubyforge_project:
68
+ rubygems_version: 2.4.5.1
69
+ signing_key:
70
+ specification_version: 4
71
+ summary: IPO procedure
72
+ test_files:
73
+ - test/test_IPOAlgorithm.rb