pairwise 0.1.8 → 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.
- data/README.md +1 -1
- data/lib/pairwise.rb +5 -4
- data/lib/pairwise/cli.rb +1 -1
- data/lib/pairwise/formatter/cucumber.rb +1 -1
- data/lib/pairwise/ipo.rb +63 -0
- data/lib/pairwise/ipo/horizontal.rb +27 -0
- data/lib/pairwise/ipo/vertical.rb +15 -0
- data/lib/pairwise/pair_collection.rb +31 -2
- data/lib/pairwise/test_pair.rb +15 -8
- data/spec/pairwise/ipo/horizontal_spec.rb +42 -0
- data/spec/pairwise/ipo_spec.rb +7 -0
- data/spec/pairwise_spec.rb +12 -26
- data/spec/spec_helper.rb +4 -3
- metadata +23 -10
- data/lib/pairwise/builder.rb +0 -108
- data/spec/pairwise/builder_spec.rb +0 -33
data/README.md
CHANGED
@@ -4,7 +4,7 @@ Pairwise
|
|
4
4
|
[](https://codeclimate.com/github/josephwilk/pairwise)
|
5
5
|
[](http://travis-ci.org/josephwilk/pairwise)
|
6
6
|
|
7
|
-
|
7
|
+
How to use Pairwise: http://josephwilk.github.com/pairwise/
|
8
8
|
|
9
9
|
Running tests
|
10
10
|
------------
|
data/lib/pairwise.rb
CHANGED
@@ -3,7 +3,9 @@ $:.unshift(File.dirname(__FILE__)) unless
|
|
3
3
|
|
4
4
|
require 'pairwise/test_pair'
|
5
5
|
require 'pairwise/pair_collection'
|
6
|
-
require 'pairwise/
|
6
|
+
require 'pairwise/ipo'
|
7
|
+
require 'pairwise/ipo/horizontal'
|
8
|
+
require 'pairwise/ipo/vertical'
|
7
9
|
require 'pairwise/formatter'
|
8
10
|
require 'pairwise/input_data'
|
9
11
|
require 'pairwise/input_file'
|
@@ -17,13 +19,12 @@ end
|
|
17
19
|
module Pairwise
|
18
20
|
class InvalidInputData < Exception; end
|
19
21
|
|
20
|
-
|
21
|
-
VERSION = [version[:major], version[:minor], version[:patch], version[:build]].compact.join('.')
|
22
|
+
VERSION = '0.2.0'
|
22
23
|
|
23
24
|
class << self
|
24
25
|
def combinations(*inputs)
|
25
26
|
raise InvalidInputData, "Minimum of 2 inputs are required to generate pairwise test set" unless valid?(inputs)
|
26
|
-
Pairwise::
|
27
|
+
Pairwise::IPO.new(inputs).build
|
27
28
|
end
|
28
29
|
|
29
30
|
private
|
data/lib/pairwise/cli.rb
CHANGED
@@ -58,7 +58,7 @@ module Pairwise
|
|
58
58
|
validate_options!
|
59
59
|
|
60
60
|
if inputs = InputFile.load(@filename_with_path)
|
61
|
-
builder = Pairwise::
|
61
|
+
builder = Pairwise::IPO.new(inputs.data, @options)
|
62
62
|
|
63
63
|
formatter.display(builder.build, inputs.labels)
|
64
64
|
else
|
@@ -30,7 +30,7 @@ module Pairwise
|
|
30
30
|
def label_wild_cards(test_data, labels)
|
31
31
|
test_data.map do |data|
|
32
32
|
data.enum_for(:each_with_index).map do |datum, column|
|
33
|
-
datum ==
|
33
|
+
datum == IPO::WILD_CARD ? "any_value_of_#{labels[column]}" : datum
|
34
34
|
end
|
35
35
|
end
|
36
36
|
end
|
data/lib/pairwise/ipo.rb
ADDED
@@ -0,0 +1,63 @@
|
|
1
|
+
# A pairwise implementation using the in-parameter-order (IPO) strategy.
|
2
|
+
# Based on: http://ranger.uta.edu/~ylei/paper/ipo-tse.pdf
|
3
|
+
module Pairwise
|
4
|
+
class IPO
|
5
|
+
|
6
|
+
WILD_CARD = 'wild_card'
|
7
|
+
|
8
|
+
def initialize(inputs, options = {})
|
9
|
+
@list_of_input_values = inputs
|
10
|
+
@options = options
|
11
|
+
end
|
12
|
+
|
13
|
+
def build
|
14
|
+
input_combinations = PairCollection.new(@list_of_input_values[0], [@list_of_input_values[1]], 0)
|
15
|
+
@list_of_input_values.size > 2 ? in_parameter_order_generation(input_combinations) : input_combinations.to_a
|
16
|
+
end
|
17
|
+
|
18
|
+
private
|
19
|
+
|
20
|
+
def in_parameter_order_generation(input_combinations)
|
21
|
+
@list_of_input_values[2..-1].each_with_index do |input_values, index|
|
22
|
+
index += 2
|
23
|
+
previously_grown_input_values = @list_of_input_values[0..(index-1)]
|
24
|
+
|
25
|
+
input_combinations, uncovered_pairs = IPO::Horizontal.growth(input_combinations, input_values, previously_grown_input_values)
|
26
|
+
input_combinations = IPO::Vertical.growth(input_combinations, uncovered_pairs)
|
27
|
+
end
|
28
|
+
input_combinations = replace_wild_cards(input_combinations) unless @options[:keep_wild_cards]
|
29
|
+
input_combinations
|
30
|
+
end
|
31
|
+
|
32
|
+
def replace_wild_cards(input_combinations)
|
33
|
+
map_wild_cards(input_combinations) do |_, index|
|
34
|
+
if @list_of_input_values[index].length == 1
|
35
|
+
@list_of_input_values[index][0]
|
36
|
+
else
|
37
|
+
pick_random_value(@list_of_input_values[index])
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
def map_wild_cards(input_combinations)
|
43
|
+
input_combinations.map do |input_combination|
|
44
|
+
input_combination.enum_for(:each_with_index).map do |input_value, index|
|
45
|
+
input_value == WILD_CARD ? yield(index, index) : input_value
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
def map_each_input_value(input_combinations, &block)
|
51
|
+
input_combinations.map do |input_combination|
|
52
|
+
input_combination.enum_for(:each_with_index).map do |input_value, index|
|
53
|
+
yield input_value, index
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
def pick_random_value(input_combination)
|
59
|
+
input_combination[Kernel.rand(input_combination.size)]
|
60
|
+
end
|
61
|
+
|
62
|
+
end
|
63
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
module Pairwise
|
2
|
+
class IPO
|
3
|
+
|
4
|
+
class Horizontal
|
5
|
+
class << self
|
6
|
+
|
7
|
+
def growth(input_combinations, input_values_for_growth, previously_grown_input_values)
|
8
|
+
uncovered_pairs = PairCollection.new(input_values_for_growth, previously_grown_input_values, previously_grown_input_values.size)
|
9
|
+
input_combinations, uncovered_pairs = grow_input_combinations_and_remove_covered_pairs(input_combinations, input_values_for_growth, uncovered_pairs)
|
10
|
+
[input_combinations, uncovered_pairs]
|
11
|
+
end
|
12
|
+
|
13
|
+
private
|
14
|
+
|
15
|
+
def grow_input_combinations_and_remove_covered_pairs(input_combinations, input_values_for_growth, uncovered_pairs)
|
16
|
+
input_combinations = input_combinations.enum_for(:each_with_index).map do |input_combination, input_index|
|
17
|
+
extended_input_combination = uncovered_pairs.input_combination_that_covers_most_pairs(input_combination, input_values_for_growth)
|
18
|
+
uncovered_pairs.remove_pairs_covered_by!(extended_input_combination)
|
19
|
+
extended_input_combination
|
20
|
+
end
|
21
|
+
[input_combinations, uncovered_pairs]
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
module Pairwise
|
2
|
+
class IPO
|
3
|
+
class Vertical
|
4
|
+
|
5
|
+
def self.growth(input_combinations, uncovered_pairs)
|
6
|
+
new_input_combinations = uncovered_pairs.reduce([]) do |new_input_combinations, uncovered_pair|
|
7
|
+
new_input_combinations = uncovered_pair.replace_wild_card(new_input_combinations)
|
8
|
+
end
|
9
|
+
|
10
|
+
input_combinations + new_input_combinations
|
11
|
+
end
|
12
|
+
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
@@ -3,6 +3,7 @@ module Pairwise
|
|
3
3
|
|
4
4
|
def initialize(input_parameter_values, input_value_lists, input_parameter_index)
|
5
5
|
pairs = generate_pairs_between(input_parameter_values, input_value_lists, input_parameter_index)
|
6
|
+
@combination_history = []
|
6
7
|
super(pairs)
|
7
8
|
end
|
8
9
|
|
@@ -12,9 +13,18 @@ module Pairwise
|
|
12
13
|
|
13
14
|
def input_combination_that_covers_most_pairs(input_combination, input_values_for_growth)
|
14
15
|
candidates = input_values_for_growth.map{|value| input_combination + [value]}
|
15
|
-
candidates.max {|combination_1, combination_2| pairs_covered_count(combination_1) <=> pairs_covered_count(combination_2)}
|
16
|
-
|
16
|
+
max_combination = candidates.max {|combination_1, combination_2| pairs_covered_count(combination_1) <=> pairs_covered_count(combination_2)}
|
17
|
+
possible_max_combinations = candidates.select{|combination| pairs_covered_count(max_combination) == pairs_covered_count(combination)}
|
17
18
|
|
19
|
+
winner = if possible_max_combinations.size > 1 && !@combination_history.empty?
|
20
|
+
find_most_different_combination(possible_max_combinations)
|
21
|
+
else
|
22
|
+
possible_max_combinations[0]
|
23
|
+
end
|
24
|
+
@combination_history << winner
|
25
|
+
winner
|
26
|
+
end
|
27
|
+
|
18
28
|
def to_a
|
19
29
|
self.map{|list| list.to_a}
|
20
30
|
end
|
@@ -39,5 +49,24 @@ module Pairwise
|
|
39
49
|
covered_count
|
40
50
|
end
|
41
51
|
end
|
52
|
+
|
53
|
+
def find_most_different_combination(options)
|
54
|
+
scores = options.map do |option|
|
55
|
+
score(option)
|
56
|
+
end.flatten
|
57
|
+
|
58
|
+
_, winner_index = *scores.each_with_index.max
|
59
|
+
options[winner_index]
|
60
|
+
end
|
61
|
+
|
62
|
+
def score(option)
|
63
|
+
#O(n^2)
|
64
|
+
@combination_history.map do |previous_combination|
|
65
|
+
option.each_with_index.inject(0) do |difference_score, (value, index)|
|
66
|
+
value != previous_combination[index] ? difference_score : difference_score += 1
|
67
|
+
end
|
68
|
+
end.max
|
69
|
+
end
|
70
|
+
|
42
71
|
end
|
43
72
|
end
|
data/lib/pairwise/test_pair.rb
CHANGED
@@ -13,26 +13,33 @@ module Pairwise
|
|
13
13
|
end
|
14
14
|
|
15
15
|
def covered_by?(test_pair)
|
16
|
+
debugger unless test_pair
|
16
17
|
test_pair[@p1_position] == @p1 &&
|
17
18
|
test_pair[@p2_position] == @p2
|
18
19
|
end
|
19
20
|
|
21
|
+
def replace_wild_card(new_input_combinations)
|
22
|
+
#TODO: Decided if we should replace all matches or single matches?
|
23
|
+
if wild_card_index = find_wild_card_index(new_input_combinations)
|
24
|
+
new_input_combinations[wild_card_index][@p2_position] = @p2
|
25
|
+
else
|
26
|
+
new_input_combinations << create_input_list
|
27
|
+
end
|
28
|
+
new_input_combinations
|
29
|
+
end
|
30
|
+
|
31
|
+
private
|
20
32
|
def create_input_list
|
21
|
-
new_input_list = Array.new(@p1_position){
|
33
|
+
new_input_list = Array.new(@p1_position){IPO::WILD_CARD}
|
22
34
|
|
23
35
|
new_input_list[@p1_position] = @p1
|
24
36
|
new_input_list[@p2_position] = @p2
|
25
37
|
new_input_list
|
26
38
|
end
|
27
39
|
|
28
|
-
def
|
29
|
-
input_list[@p2_position] = @p2
|
30
|
-
input_list
|
31
|
-
end
|
32
|
-
|
33
|
-
def replaceable_wild_card?(input_combinations)
|
40
|
+
def find_wild_card_index(input_combinations)
|
34
41
|
wild_card_list = input_combinations.map do |input_combination|
|
35
|
-
input_combination[@p2_position] ==
|
42
|
+
input_combination[@p2_position] == IPO::WILD_CARD && input_combination[@p1_position] == @p1
|
36
43
|
end
|
37
44
|
wild_card_list.rindex(true)
|
38
45
|
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
module Pairwise
|
4
|
+
class IPO
|
5
|
+
|
6
|
+
describe Horizontal do
|
7
|
+
describe ".growth" do
|
8
|
+
context "when the input_combinations size is smaller than the input values for growth size" do
|
9
|
+
it "should extenhd with C's inputs" do
|
10
|
+
input_combinations = [[:A1, :B1], [:A1, :B2]]
|
11
|
+
data = [[:A1, :A2], [:C1, :C2, :C3 ]]
|
12
|
+
|
13
|
+
test_set, _ = Horizontal.growth(input_combinations, data[1], data[0..1])
|
14
|
+
|
15
|
+
test_set.should == [[:A1, :B1, :C1], [:A1, :B2, :C3]]
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
context "when the input_combinations size is larger than the input values for growth size" do
|
20
|
+
before(:each) do
|
21
|
+
@test_pairs = [[:A1, :B1], [:A1, :B2], [:A2, :B1], [:A2, :B2]]
|
22
|
+
@data = [[:A1, :A2], [:B1, :B2], [:C1, :C2, :C3 ]]
|
23
|
+
end
|
24
|
+
|
25
|
+
it "should return pairs extended with C's inputs" do
|
26
|
+
test_set, _ = Horizontal.growth(@test_pairs, @data[2], @data[0..1])
|
27
|
+
|
28
|
+
test_set.should == [[:A1, :B1, :C1], [:A1, :B2, :C3], [:A2, :B1, :C3], [:A2, :B2, :C2]]
|
29
|
+
end
|
30
|
+
|
31
|
+
it "should return all the uncovered pairs" do
|
32
|
+
_, pi = Horizontal.growth(@test_pairs, @data[2], @data[0..1])
|
33
|
+
|
34
|
+
pi.to_a.should == [[:C1, :A2], [:C1, :B2], [:C2, :A1], [:C2, :B1]]
|
35
|
+
end
|
36
|
+
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
end
|
42
|
+
end
|
data/spec/pairwise_spec.rb
CHANGED
@@ -70,12 +70,7 @@ describe Pairwise do
|
|
70
70
|
it "should generate all pairs for 3 parameters of 1,2,3 values" do
|
71
71
|
data = [[:A1], [:B1, :B2], [:C1, :C2, :C3]]
|
72
72
|
|
73
|
-
Pairwise.combinations(*data).should == [[:A1, :B1, :C1],
|
74
|
-
[:A1, :B2, :C2],
|
75
|
-
[:A1, :B2, :C1],
|
76
|
-
[:A1, :B1, :C2],
|
77
|
-
[:A1, :B1, :C3],
|
78
|
-
[:A1, :B2, :C3]]
|
73
|
+
Pairwise.combinations(*data).should == [[:A1, :B1, :C1], [:A1, :B2, :C3], [:A1, :B2, :C1], [:A1, :B1, :C2], [:A1, :B2, :C2], [:A1, :B1, :C3]]
|
79
74
|
end
|
80
75
|
|
81
76
|
it "should generate all pairs for 3 parameters of 2,1,2 values" do
|
@@ -96,11 +91,7 @@ describe Pairwise do
|
|
96
91
|
it "should generate all pairs for 4 parameters of 2,1,2,2 values" do
|
97
92
|
data = [[:A1, :A2], [:B1], [:C1, :C2], [:D1, :D2]]
|
98
93
|
|
99
|
-
Pairwise.combinations(*data).should == [[:A1, :B1, :C1, :D1],
|
100
|
-
[:A2, :B1, :C2, :D2],
|
101
|
-
[:A2, :B1, :C1, :D1],
|
102
|
-
[:A1, :B1, :C2, :D1],
|
103
|
-
[:A1, :B1, :C1, :D2]]
|
94
|
+
Pairwise.combinations(*data).should == [[:A1, :B1, :C1, :D1], [:A2, :B1, :C2, :D2], [:A2, :B1, :C1, :D2], [:A1, :B1, :C2, :D2], [:A2, :B1, :C2, :D1]]
|
104
95
|
end
|
105
96
|
|
106
97
|
it "should generate pairs for three parameters" do
|
@@ -108,27 +99,22 @@ describe Pairwise do
|
|
108
99
|
[:B1, :B2],
|
109
100
|
[:C1 , :C2 , :C3 ]]
|
110
101
|
|
111
|
-
Pairwise.combinations(*data).should == [[:A1, :B1, :C1],
|
112
|
-
[:A1, :B2, :C2],
|
113
|
-
[:A2, :B1, :C3],
|
114
|
-
[:A2, :B2, :C1],
|
115
|
-
[:A2, :B1, :C2],
|
116
|
-
[:A1, :B2, :C3]]
|
102
|
+
Pairwise.combinations(*data).should == [[:A1, :B1, :C1], [:A1, :B2, :C3], [:A2, :B1, :C3], [:A2, :B2, :C2], [:A2, :B2, :C1], [:A1, :B1, :C2]]
|
117
103
|
end
|
118
104
|
|
119
105
|
describe "replacing wildcards which could have more than one option" do
|
120
106
|
it "should generate pairs for 2 parameters of 3,2,3 values" do
|
121
107
|
Pairwise.combinations([:A1, :A2, :A3],
|
122
108
|
[:B1, :B2],
|
123
|
-
[:C1, :C2, :C3]).should == [[:A1, :B1, :C1],
|
124
|
-
[:A1, :B2, :
|
125
|
-
[:A2, :B1, :C3],
|
126
|
-
[:A2, :B2, :
|
127
|
-
[:A3, :B1, :C2],
|
128
|
-
[:A3, :B2, :
|
129
|
-
[:
|
130
|
-
[:
|
131
|
-
[:
|
109
|
+
[:C1, :C2, :C3]).should == [[:A1, :B1, :C1],
|
110
|
+
[:A1, :B2, :C3],
|
111
|
+
[:A2, :B1, :C3],
|
112
|
+
[:A2, :B2, :C2],
|
113
|
+
[:A3, :B1, :C2],
|
114
|
+
[:A3, :B2, :C1],
|
115
|
+
[:A2, :B1, :C1],
|
116
|
+
[:A1, :B1, :C2],
|
117
|
+
[:A3, :B1, :C3]]
|
132
118
|
end
|
133
119
|
end
|
134
120
|
end
|
data/spec/spec_helper.rb
CHANGED
@@ -1,7 +1,8 @@
|
|
1
1
|
require 'rubygems'
|
2
|
-
require '
|
2
|
+
require 'bundler'
|
3
|
+
Bundler.require(:test)
|
3
4
|
|
4
|
-
require 'simplecov'
|
5
5
|
SimpleCov.start
|
6
|
+
SimpleCov.command_name 'unit_tests'
|
6
7
|
|
7
|
-
require File.dirname(__FILE__) + '/../lib/pairwise'
|
8
|
+
require File.dirname(__FILE__) + '/../lib/pairwise'
|
metadata
CHANGED
@@ -1,8 +1,12 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: pairwise
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
prerelease:
|
5
|
-
|
4
|
+
prerelease: false
|
5
|
+
segments:
|
6
|
+
- 0
|
7
|
+
- 2
|
8
|
+
- 0
|
9
|
+
version: 0.2.0
|
6
10
|
platform: ruby
|
7
11
|
authors:
|
8
12
|
- Joseph Wilk
|
@@ -10,16 +14,18 @@ autorequire:
|
|
10
14
|
bindir: bin
|
11
15
|
cert_chain: []
|
12
16
|
|
13
|
-
date: 2012-
|
17
|
+
date: 2012-09-04 00:00:00 +01:00
|
18
|
+
default_executable:
|
14
19
|
dependencies:
|
15
20
|
- !ruby/object:Gem::Dependency
|
16
21
|
name: rspec
|
17
22
|
prerelease: false
|
18
23
|
requirement: &id001 !ruby/object:Gem::Requirement
|
19
|
-
none: false
|
20
24
|
requirements:
|
21
25
|
- - ">="
|
22
26
|
- !ruby/object:Gem::Version
|
27
|
+
segments:
|
28
|
+
- 0
|
23
29
|
version: "0"
|
24
30
|
type: :development
|
25
31
|
version_requirements: *id001
|
@@ -27,10 +33,11 @@ dependencies:
|
|
27
33
|
name: cucumber
|
28
34
|
prerelease: false
|
29
35
|
requirement: &id002 !ruby/object:Gem::Requirement
|
30
|
-
none: false
|
31
36
|
requirements:
|
32
37
|
- - ">="
|
33
38
|
- !ruby/object:Gem::Version
|
39
|
+
segments:
|
40
|
+
- 0
|
34
41
|
version: "0"
|
35
42
|
type: :development
|
36
43
|
version_requirements: *id002
|
@@ -44,21 +51,25 @@ extra_rdoc_files: []
|
|
44
51
|
|
45
52
|
files:
|
46
53
|
- README.md
|
47
|
-
- lib/pairwise/builder.rb
|
48
54
|
- lib/pairwise/cli.rb
|
49
55
|
- lib/pairwise/formatter/csv.rb
|
50
56
|
- lib/pairwise/formatter/cucumber.rb
|
51
57
|
- lib/pairwise/formatter.rb
|
52
58
|
- lib/pairwise/input_data.rb
|
53
59
|
- lib/pairwise/input_file.rb
|
60
|
+
- lib/pairwise/ipo/horizontal.rb
|
61
|
+
- lib/pairwise/ipo/vertical.rb
|
62
|
+
- lib/pairwise/ipo.rb
|
54
63
|
- lib/pairwise/pair_collection.rb
|
55
64
|
- lib/pairwise/test_pair.rb
|
56
65
|
- lib/pairwise.rb
|
57
|
-
- spec/pairwise/builder_spec.rb
|
58
66
|
- spec/pairwise/cli_spec.rb
|
67
|
+
- spec/pairwise/ipo/horizontal_spec.rb
|
68
|
+
- spec/pairwise/ipo_spec.rb
|
59
69
|
- spec/pairwise/pair_collection_spec.rb
|
60
70
|
- spec/pairwise_spec.rb
|
61
71
|
- spec/spec_helper.rb
|
72
|
+
has_rdoc: true
|
62
73
|
homepage: http://wiki.github.com/josephwilk/pairwise
|
63
74
|
licenses: []
|
64
75
|
|
@@ -68,21 +79,23 @@ rdoc_options: []
|
|
68
79
|
require_paths:
|
69
80
|
- lib
|
70
81
|
required_ruby_version: !ruby/object:Gem::Requirement
|
71
|
-
none: false
|
72
82
|
requirements:
|
73
83
|
- - ">="
|
74
84
|
- !ruby/object:Gem::Version
|
85
|
+
segments:
|
86
|
+
- 0
|
75
87
|
version: "0"
|
76
88
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
77
|
-
none: false
|
78
89
|
requirements:
|
79
90
|
- - ">="
|
80
91
|
- !ruby/object:Gem::Version
|
92
|
+
segments:
|
93
|
+
- 0
|
81
94
|
version: "0"
|
82
95
|
requirements: []
|
83
96
|
|
84
97
|
rubyforge_project:
|
85
|
-
rubygems_version: 1.
|
98
|
+
rubygems_version: 1.3.6
|
86
99
|
signing_key:
|
87
100
|
specification_version: 3
|
88
101
|
summary: Turn large test data combinations into smaller ones
|
data/lib/pairwise/builder.rb
DELETED
@@ -1,108 +0,0 @@
|
|
1
|
-
# A pairwise implementation using the in-parameter-order (IPO) strategy.
|
2
|
-
# Based on: http://ranger.uta.edu/~ylei/paper/ipo-tse.pdf
|
3
|
-
module Pairwise
|
4
|
-
class Builder
|
5
|
-
|
6
|
-
WILD_CARD = 'wild_card'
|
7
|
-
|
8
|
-
def initialize(inputs, options = {})
|
9
|
-
@list_of_input_values = inputs
|
10
|
-
@options = options
|
11
|
-
end
|
12
|
-
|
13
|
-
def build
|
14
|
-
input_combinations = PairCollection.new(@list_of_input_values[0], [@list_of_input_values[1]], 0)
|
15
|
-
@list_of_input_values.size > 2 ? in_parameter_order_generation(input_combinations) : input_combinations.to_a
|
16
|
-
end
|
17
|
-
|
18
|
-
private
|
19
|
-
|
20
|
-
def in_parameter_order_generation(input_combinations)
|
21
|
-
@list_of_input_values[2..-1].each_with_index do |input_values, i|
|
22
|
-
i += 2
|
23
|
-
input_combinations, uncovered_pairs = horizontal_growth(input_combinations, input_values, @list_of_input_values[0..(i-1)])
|
24
|
-
input_combinations = vertical_growth(input_combinations, uncovered_pairs)
|
25
|
-
end
|
26
|
-
input_combinations = replace_redundant_wild_cards(input_combinations)
|
27
|
-
input_combinations = replace_wild_cards(input_combinations) unless @options[:keep_wild_cards]
|
28
|
-
input_combinations
|
29
|
-
end
|
30
|
-
|
31
|
-
def horizontal_growth(input_combinations, input_values_for_growth, previously_grown_input_values)
|
32
|
-
uncovered_pairs = PairCollection.new(input_values_for_growth, previously_grown_input_values, previously_grown_input_values.size)
|
33
|
-
|
34
|
-
if input_combinations.size <= input_values_for_growth.size
|
35
|
-
input_combinations, uncovered_pairs = grow_input_combinations_and_remove_covered_pairs(input_combinations, input_values_for_growth, uncovered_pairs)
|
36
|
-
else
|
37
|
-
range_to_grow = 0...input_values_for_growth.size
|
38
|
-
input_combinations[range_to_grow], uncovered_pairs = grow_input_combinations_and_remove_covered_pairs(input_combinations[range_to_grow], input_values_for_growth, uncovered_pairs)
|
39
|
-
|
40
|
-
range_to_grow = input_values_for_growth.size..-1
|
41
|
-
input_combinations[range_to_grow] = input_combinations[range_to_grow].map do |input_combination|
|
42
|
-
extended_input_combination = uncovered_pairs.input_combination_that_covers_most_pairs(input_combination, input_values_for_growth)
|
43
|
-
uncovered_pairs.remove_pairs_covered_by!(extended_input_combination)
|
44
|
-
extended_input_combination
|
45
|
-
end
|
46
|
-
end
|
47
|
-
|
48
|
-
[input_combinations, uncovered_pairs]
|
49
|
-
end
|
50
|
-
|
51
|
-
def grow_input_combinations_and_remove_covered_pairs(input_combinations, input_values_for_growth, uncovered_pairs)
|
52
|
-
input_combinations = input_combinations.enum_for(:each_with_index).map do |input_combination, input_index|
|
53
|
-
extended_input_combination = input_combination + [input_values_for_growth[input_index]]
|
54
|
-
uncovered_pairs.remove_pairs_covered_by!(extended_input_combination)
|
55
|
-
extended_input_combination
|
56
|
-
end
|
57
|
-
[input_combinations, uncovered_pairs]
|
58
|
-
end
|
59
|
-
|
60
|
-
def vertical_growth(input_combinations, uncovered_pairs)
|
61
|
-
new_input_combinations = []
|
62
|
-
|
63
|
-
uncovered_pairs.each do |uncovered_pair|
|
64
|
-
#TODO: Decided if we should replace all matches or single matches?
|
65
|
-
if test_position = uncovered_pair.replaceable_wild_card?(new_input_combinations)
|
66
|
-
new_input_combinations[test_position] = uncovered_pair.replace_wild_card(new_input_combinations[test_position])
|
67
|
-
else
|
68
|
-
new_input_combinations << uncovered_pair.create_input_list
|
69
|
-
end
|
70
|
-
end
|
71
|
-
|
72
|
-
input_combinations + new_input_combinations
|
73
|
-
end
|
74
|
-
|
75
|
-
def replace_redundant_wild_cards(input_combinations)
|
76
|
-
map_each_input_value(input_combinations) do |input_value, index|
|
77
|
-
if input_value == WILD_CARD && @list_of_input_values[index].length == 1
|
78
|
-
@list_of_input_values[index][0]
|
79
|
-
else
|
80
|
-
input_value
|
81
|
-
end
|
82
|
-
end
|
83
|
-
end
|
84
|
-
|
85
|
-
def replace_wild_cards(input_combinations)
|
86
|
-
map_each_input_value(input_combinations) do |input_value, index|
|
87
|
-
if input_value == WILD_CARD
|
88
|
-
pick_random_value(@list_of_input_values[index])
|
89
|
-
else
|
90
|
-
input_value
|
91
|
-
end
|
92
|
-
end
|
93
|
-
end
|
94
|
-
|
95
|
-
def map_each_input_value(input_combinations)
|
96
|
-
input_combinations.map do |input_combination|
|
97
|
-
input_combination.enum_for(:each_with_index).map do |input_value, index|
|
98
|
-
yield input_value, index
|
99
|
-
end
|
100
|
-
end
|
101
|
-
end
|
102
|
-
|
103
|
-
def pick_random_value(input_combination)
|
104
|
-
input_combination[Kernel.rand(input_combination.size)]
|
105
|
-
end
|
106
|
-
|
107
|
-
end
|
108
|
-
end
|
@@ -1,33 +0,0 @@
|
|
1
|
-
require 'spec_helper'
|
2
|
-
|
3
|
-
module Pairwise
|
4
|
-
describe Builder do
|
5
|
-
|
6
|
-
describe 'ipo horizontal growth' do
|
7
|
-
before(:each) do
|
8
|
-
@test_pairs = [[:A1, :B1], [:A1, :B2], [:A2, :B1], [:A2, :B2]]
|
9
|
-
@data = [[:A1, :A2],[:B1, :B2],[:C1 , :C2 , :C3 ]]
|
10
|
-
|
11
|
-
@builder = Builder.new(@test_pairs)
|
12
|
-
end
|
13
|
-
|
14
|
-
it "should return pairs extended with C's inputs" do
|
15
|
-
test_set, _ = @builder.send(:horizontal_growth, @test_pairs, @data[2], @data[0..1])
|
16
|
-
|
17
|
-
test_set.should == [[:A1, :B1, :C1],
|
18
|
-
[:A1, :B2, :C2],
|
19
|
-
[:A2, :B1, :C3],
|
20
|
-
[:A2, :B2, :C1]]
|
21
|
-
end
|
22
|
-
|
23
|
-
it "should return all the uncovered pairs" do
|
24
|
-
_, pi = @builder.send(:horizontal_growth, @test_pairs, @data[2], @data[0..1])
|
25
|
-
|
26
|
-
# We are getting the uncovered pairs in reverse
|
27
|
-
#pi.should == [[:A2, :C2],[:A1, :C3],[:B1, :C2],[:B2, :C3]]
|
28
|
-
# Cheat and check we get the list in reverse
|
29
|
-
pi.to_a.should == [[:C2, :A2], [:C2, :B1], [:C3, :A1], [:C3, :B2]]
|
30
|
-
end
|
31
|
-
end
|
32
|
-
end
|
33
|
-
end
|