pairwise_psych 0.2.3

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: f3235ee705e199c82b95973eecb55348db8f8092
4
+ data.tar.gz: 0f69362bfa95968b1c87bccc462cef0168d792df
5
+ SHA512:
6
+ metadata.gz: 1dd0513ecc088ec79b1dcd49d14513f582d77a2549659c3e091ff2b674d04389e6b01943527e70fc318c80407f660ed21b52a86a6b67a790ae6419a8d46ece72
7
+ data.tar.gz: af9493453403096987d4f4b0837fe3f672f0a6d34b7743c9dcf5b3409a16eb881912211e10b14a095d2f4b375ad0871d48a1ae124b49071faeb8fb989382b12b
data/README.md ADDED
@@ -0,0 +1,34 @@
1
+ Pairwise
2
+ -------
3
+
4
+ Pairwise is a Ruby based tool for selecting a smaller number of test input combinations (using pairwise generation)
5
+ rather than exhaustively testing all possible permutations.
6
+
7
+ Created by Joseph Wilks, updated by Ali King for newer Rubies
8
+
9
+ How to use Pairwise: http://josephwilk.github.com/pairwise/
10
+
11
+ Newer Rubies and Syck vs Psych
12
+ -----------
13
+ Syck and Psych are YAML serialization libraries. Historically Ruby used syck, now Psych is the default. There are a
14
+ couple of differences between the two, which are better described here :- http://devblog.arnebrasseur.net/2014-02-yaml-syck-vs-psych
15
+
16
+ This fork from the original pairwise just removes a directive to use syck and adds some tests for multibyte characters
17
+ which may have been interpreted differently by the different libraries.
18
+
19
+ Also adds unicode-display_width gem to help with formatting - multibyte characters are tricksy.
20
+
21
+ Tested on ruby 1.8.7-p374 and 2.0.0-p353 as a sampling, but this should be future-proof.
22
+
23
+
24
+ Running tests
25
+ ------------
26
+ <pre><code>rake</code></pre
27
+
28
+
29
+
30
+ Copyright
31
+ --------
32
+
33
+ See LICENSE for details.
34
+
data/bin/pairwise ADDED
@@ -0,0 +1,7 @@
1
+ #!/usr/bin/env ruby
2
+ $:.unshift(File.dirname(__FILE__) + '/../lib') unless $:.include?(File.dirname(__FILE__) + '/../lib')
3
+
4
+ require 'pairwise'
5
+ require 'pairwise/cli'
6
+
7
+ Pairwise::Cli.execute(ARGV)
@@ -0,0 +1,92 @@
1
+ require 'optparse'
2
+
3
+ module Pairwise
4
+ class Cli
5
+ BUILTIN_FORMATS = {
6
+ 'cucumber' => [Pairwise::Formatter::Cucumber,
7
+ 'Tables for Cucumber'],
8
+ 'csv' => [Pairwise::Formatter::Csv,
9
+ 'Comma seperated values']}
10
+
11
+ max = BUILTIN_FORMATS.keys.map{|s| s.length}.max
12
+ FORMAT_HELP = (BUILTIN_FORMATS.keys.sort.map do |key|
13
+ " #{key}#{' ' * (max - key.length)} : #{BUILTIN_FORMATS[key][1]}"
14
+ end)
15
+
16
+ class << self
17
+ def execute(args)
18
+ new(args).execute!
19
+ end
20
+ end
21
+
22
+ def initialize(args, out = STDOUT)
23
+ @args, @out = args, out
24
+ @options = defaults
25
+ end
26
+
27
+ def parse!
28
+ @args.extend(::OptionParser::Arguable)
29
+ @args.options do |opts|
30
+ opts.banner = ["Usage: pairwise [options] FILE.[yml|csv]", "",
31
+ "Example:",
32
+ "pairwise data/inputs.yml", "", "",
33
+ ].join("\n")
34
+ opts.on("-k", "--keep-wild-cards",
35
+ "Don't automatically replace any wild-cards which appear",
36
+ "in the pairwise data") do
37
+ @options[:keep_wild_cards] = true
38
+ end
39
+ opts.on('-f FORMAT', '--format FORMAT',
40
+ "How to format pairwise data (Default: cucumber). Available formats:",
41
+ *FORMAT_HELP) do |format|
42
+ @options[:format] = format
43
+ end
44
+ opts.on_tail("--version", "Show version.") do
45
+ @out.puts Pairwise::VERSION
46
+ Kernel.exit(0)
47
+ end
48
+ opts.on_tail("-h", "--help", "You're looking at it.") do
49
+ exit_with_help
50
+ end
51
+ end.parse!
52
+
53
+ @filename_with_path = @args[0] unless @args.empty?
54
+ end
55
+
56
+ def execute!
57
+ parse!
58
+ validate_options!
59
+
60
+ if inputs = InputFile.load(@filename_with_path)
61
+ builder = Pairwise::IPO.new(inputs.data, @options)
62
+
63
+ formatter.display(builder.build, inputs.labels)
64
+ else
65
+ puts "Error: '#{@filename_with_path}' does not contain the right structure for me to generate the pairwise set!"
66
+ end
67
+ end
68
+
69
+ private
70
+ def defaults
71
+ { :keep_wild_cards => false,
72
+ :format => 'cucumber' }
73
+ end
74
+
75
+ def validate_options!
76
+ exit_with_help if @filename_with_path.nil? || @filename_with_path.empty?
77
+ raise Errno::ENOENT, @filename_with_path unless File.exist?(@filename_with_path)
78
+ exit_with_help unless File.file?(@filename_with_path)
79
+ end
80
+
81
+ def exit_with_help
82
+ @out.puts @args.options.help
83
+ Kernel.exit(0)
84
+ end
85
+
86
+ def formatter
87
+ format = BUILTIN_FORMATS[@options[:format]][0]
88
+ format.new(@out)
89
+ end
90
+
91
+ end
92
+ end
@@ -0,0 +1,17 @@
1
+ module Pairwise
2
+ module Formatter
3
+ class Csv
4
+
5
+ def initialize(out)
6
+ @out = out
7
+ end
8
+
9
+ def display(test_data, input_labels)
10
+ @out.puts input_labels.join(',')
11
+ test_data.each do |data|
12
+ @out.puts data.join(',')
13
+ end
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,52 @@
1
+ require 'unicode/display_width'
2
+
3
+ module Pairwise
4
+ module Formatter
5
+ class Cucumber
6
+
7
+ def initialize(out)
8
+ @out = out
9
+ @max = {}
10
+ end
11
+
12
+ def display(test_data, input_labels)
13
+ @test_data = label_wild_cards(test_data, input_labels)
14
+ @input_labels = input_labels
15
+
16
+ @out.print "|"
17
+ @input_labels.each_with_index do |label, column|
18
+ @out.print padded_string(label, column) + "|"
19
+ end
20
+ @out.puts
21
+
22
+ @test_data.each do |data|
23
+ @out.print "|"
24
+ data.each_with_index do |datum, column|
25
+ @out.print padded_string(datum, column) + "|"
26
+ end
27
+ @out.puts
28
+ end
29
+ end
30
+
31
+ private
32
+ def label_wild_cards(test_data, labels)
33
+ test_data.map do |data|
34
+ data.enum_for(:each_with_index).map do |datum, column|
35
+ datum == IPO::WILD_CARD ? "any_value_of_#{labels[column]}" : datum
36
+ end
37
+ end
38
+ end
39
+
40
+ def padded_string(string, column)
41
+ string = string.to_s unless string.is_a? String
42
+ padding_length = max_line_length(column) - string.display_width
43
+ " #{string} " + (" " * padding_length)
44
+ end
45
+
46
+ def max_line_length(column)
47
+ @max[column] ||= ([@input_labels[column].to_s.length] + @test_data.map{|data| data[column].to_s.length}).max
48
+ end
49
+
50
+ end
51
+ end
52
+ end
@@ -0,0 +1 @@
1
+ %w[cucumber csv].each {|file| require "pairwise/formatter/#{file}"}
@@ -0,0 +1,25 @@
1
+ module Pairwise
2
+ class InputData
3
+
4
+ def initialize(inputs)
5
+ @inputs = inputs.is_a?(Hash) ? hash_inputs_to_list(inputs) : inputs
6
+ end
7
+
8
+ def data
9
+ @data ||= @inputs.map {|input| input.values[0]}
10
+ end
11
+
12
+ def labels
13
+ @labels ||= @inputs.map{|input| input.keys}.flatten
14
+ end
15
+
16
+ private
17
+
18
+ def hash_inputs_to_list(inputs_hash)
19
+ inputs_hash.sort.map do |key, value|
20
+ value = [value] unless value.is_a?(Array)
21
+ {key => value}
22
+ end
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,62 @@
1
+ module Pairwise
2
+ class InputFile
3
+
4
+ class << self
5
+ def load(filename)
6
+ inputs = self.new(filename).load_and_parse
7
+ InputData.new(inputs) if valid?(inputs)
8
+ end
9
+
10
+ def valid?(inputs)
11
+ inputs && (inputs.is_a?(Array) || inputs.is_a?(Hash))
12
+ end
13
+ end
14
+
15
+ def initialize(filename)
16
+ @filename = filename
17
+ self.extend(input_file_module)
18
+ end
19
+
20
+ private
21
+
22
+ def input_file_module
23
+ type = @filename[/\.(.+)$/, 1]
24
+ raise "Cannot determine file type for: #{@filename}" unless type
25
+ case type.downcase
26
+ when 'yaml', 'yml' then Yaml
27
+ else
28
+ Pairwise.const_get(type.capitalize) rescue raise "Unsupported file type: #{type}"
29
+ end
30
+ end
31
+
32
+ end
33
+
34
+ module Yaml
35
+ def load_and_parse
36
+ require 'yaml'
37
+ begin
38
+ inputs = YAML.load_file(@filename)
39
+ rescue
40
+ nil
41
+ end
42
+ end
43
+ end
44
+
45
+ module Csv
46
+ def load_and_parse
47
+ require 'csv'
48
+
49
+ csv_data = CSV.read @filename
50
+ headers = csv_data.shift.map {|head| head.to_s.strip }
51
+ string_data = csv_data.map {|row| row.map {|cell| cell.to_s.strip } }
52
+
53
+ inputs = Hash.new {|h,k| h[k] = []}
54
+
55
+ string_data.each do |row|
56
+ row.each_with_index { |value, index| inputs[headers[index]] << value }
57
+ end
58
+
59
+ inputs
60
+ end
61
+ end
62
+ 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
@@ -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,72 @@
1
+ module Pairwise
2
+ class PairCollection < Array
3
+
4
+ def initialize(input_parameter_values, input_value_lists, input_parameter_index)
5
+ pairs = generate_pairs_between(input_parameter_values, input_value_lists, input_parameter_index)
6
+ @combination_history = []
7
+ super(pairs)
8
+ end
9
+
10
+ def remove_pairs_covered_by!(extended_input_list)
11
+ self.reject!{|pair| pair.covered_by?(extended_input_list)}
12
+ end
13
+
14
+ def input_combination_that_covers_most_pairs(input_combination, input_values_for_growth)
15
+ candidates = input_values_for_growth.map{|value| input_combination + [value]}
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)}
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
+
28
+ def to_a
29
+ self.map{|list| list.to_a}
30
+ end
31
+
32
+ private
33
+
34
+ def generate_pairs_between(input_parameter_values, input_value_lists, input_parameter_index)
35
+ pairs = []
36
+ input_parameter_values.each do |input_value_a|
37
+ input_value_lists.each_with_index do |input_list, input_value_b_index|
38
+ input_list.each do |input_value_b|
39
+ pairs << TestPair.new(input_parameter_index, input_value_b_index, input_value_a, input_value_b)
40
+ end
41
+ end
42
+ end
43
+ pairs
44
+ end
45
+
46
+ def pairs_covered_count(input_combination)
47
+ self.reduce(0) do |covered_count, pair|
48
+ covered_count += 1 if pair.covered_by?(input_combination)
49
+ covered_count
50
+ end
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
+
71
+ end
72
+ end
@@ -0,0 +1,48 @@
1
+ require 'forwardable'
2
+
3
+ module Pairwise
4
+ class TestPair
5
+ extend Forwardable
6
+
7
+ def_delegators :@pair, :+, :inspect, :to_a, :==
8
+
9
+ def initialize(p1_position, p2_position, p1, p2)
10
+ @p1, @p2 = p1, p2
11
+ @p1_position, @p2_position = p1_position, p2_position
12
+ @pair = [@p1, @p2]
13
+ end
14
+
15
+ def covered_by?(test_pair)
16
+ debugger unless test_pair
17
+ test_pair[@p1_position] == @p1 &&
18
+ test_pair[@p2_position] == @p2
19
+ end
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
32
+ def create_input_list
33
+ new_input_list = Array.new(@p1_position){IPO::WILD_CARD}
34
+
35
+ new_input_list[@p1_position] = @p1
36
+ new_input_list[@p2_position] = @p2
37
+ new_input_list
38
+ end
39
+
40
+ def find_wild_card_index(input_combinations)
41
+ wild_card_list = input_combinations.map do |input_combination|
42
+ input_combination[@p2_position] == IPO::WILD_CARD && input_combination[@p1_position] == @p1
43
+ end
44
+ wild_card_list.rindex(true)
45
+ end
46
+
47
+ end
48
+ end
data/lib/pairwise.rb ADDED
@@ -0,0 +1,40 @@
1
+ $:.unshift(File.dirname(__FILE__)) unless
2
+ $:.include?(File.dirname(__FILE__)) || $:.include?(File.expand_path(File.dirname(__FILE__)))
3
+
4
+ require 'pairwise/test_pair'
5
+ require 'pairwise/pair_collection'
6
+ require 'pairwise/ipo'
7
+ require 'pairwise/ipo/horizontal'
8
+ require 'pairwise/ipo/vertical'
9
+ require 'pairwise/formatter'
10
+ require 'pairwise/input_data'
11
+ require 'pairwise/input_file'
12
+ require 'pairwise/cli'
13
+
14
+ require 'yaml'
15
+
16
+
17
+ module Pairwise
18
+ class InvalidInputData < Exception; end
19
+
20
+ VERSION = '0.2.2'
21
+
22
+ class << self
23
+ def combinations(*inputs)
24
+ raise InvalidInputData, "Minimum of 2 inputs are required to generate pairwise test set" unless valid?(inputs)
25
+ Pairwise::IPO.new(inputs).build
26
+ end
27
+
28
+ private
29
+ def valid?(inputs)
30
+ array_of_arrays?(inputs) &&
31
+ inputs.length >= 2 &&
32
+ !inputs[0].empty? && !inputs[1].empty?
33
+ end
34
+
35
+ def array_of_arrays?(data)
36
+ data.reject{|datum| datum.kind_of?(Array)}.empty?
37
+ end
38
+
39
+ end
40
+ end
data/lib/version.rb ADDED
@@ -0,0 +1,3 @@
1
+ module Pairwise
2
+ VERSION = '0.2.3'
3
+ end
@@ -0,0 +1,68 @@
1
+ require 'spec_helper'
2
+
3
+ module Pairwise
4
+ describe Cli do
5
+
6
+ before(:each) do
7
+ Kernel.stub!(:exit).and_return(nil)
8
+ end
9
+
10
+ def output_stream
11
+ @output_stream ||= StringIO.new
12
+ end
13
+
14
+ def options
15
+ #TODO: push options out to an object and avoid hacking at private instance vars
16
+ @cli.instance_variable_get("@options")
17
+ end
18
+
19
+ def prepare_args(args)
20
+ args.is_a?(Array) ? args : args.split(' ')
21
+ end
22
+
23
+ def after_parsing(args)
24
+ @cli = Pairwise::Cli.new(prepare_args(args), output_stream)
25
+ @cli.parse!
26
+ yield
27
+ end
28
+
29
+ context '--keep-wild-cards' do
30
+ it "displays wild cards in output result" do
31
+ after_parsing('--keep-wild-cards') do
32
+ options[:keep_wild_cards].should == true
33
+ end
34
+ end
35
+ end
36
+
37
+ context '-f FORMAT or --format FORMAT' do
38
+ it "defaults to the cucumber format for output" do
39
+ after_parsing('') do
40
+ options[:format].should == 'cucumber'
41
+ end
42
+ end
43
+
44
+ it "overides the cucumber format when passed a specific format" do
45
+ after_parsing('--format csv') do
46
+ options[:format].should == 'csv'
47
+ end
48
+ end
49
+ end
50
+
51
+ context "--help" do
52
+ it "displays usage" do
53
+ after_parsing('--help') do
54
+ output_stream.string.should =~ /Usage: pairwise \[options\] FILE\.\[yml|csv\]/
55
+ end
56
+ end
57
+ end
58
+
59
+ context '--version' do
60
+ it "displays Pairwise's version" do
61
+ after_parsing('--version') do
62
+ output_stream.string.should =~ /#{Pairwise::VERSION}/
63
+ end
64
+ end
65
+ end
66
+
67
+ end
68
+ 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
@@ -0,0 +1,7 @@
1
+ require 'spec_helper'
2
+
3
+ module Pairwise
4
+ describe IPO do
5
+
6
+ end
7
+ end
@@ -0,0 +1,15 @@
1
+ require 'spec_helper'
2
+
3
+ module Pairwise
4
+ describe PairCollection do
5
+ describe '#input_combination_that_covers_most_pairs' do
6
+ it "should do find the combination that covers most pairs" do
7
+ pair_collection = PairCollection.new([:A2, :B2], [[:A2, :B2], [:B2, :A1]], 1)
8
+
9
+ combination = pair_collection.input_combination_that_covers_most_pairs([:A2, :B2], [:C2, :A1])
10
+
11
+ combination.should == [:A2, :B2, :C2]
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,123 @@
1
+ require 'spec_helper'
2
+
3
+ describe Pairwise do
4
+ before(:each) do
5
+ Kernel.stub!(:rand).and_return(0)
6
+ end
7
+
8
+ context "with invalid inputs" do
9
+ it "should be invalid when running with no input" do
10
+ lambda{ Pairwise.combinations([]) }.should raise_error(Pairwise::InvalidInputData)
11
+ end
12
+
13
+ it "should be invalid when running with only 1 input" do
14
+ lambda{ Pairwise.combinations([:A1, :A2])}.should raise_error(Pairwise::InvalidInputData)
15
+ end
16
+
17
+ it "should be invalid when running with a single list input" do
18
+ lambda{ Pairwise.combinations([:A1, :A2])}.should raise_error(Pairwise::InvalidInputData)
19
+ end
20
+ end
21
+
22
+ context "with valid inputs" do
23
+ context "which are equal lengths" do
24
+ it "should generate pairs for 2 parameters of 1 value" do
25
+ data = [[:A1], [:B1]]
26
+
27
+ Pairwise.combinations(*data).should == [[:A1, :B1]]
28
+ end
29
+
30
+ it "should generate all pairs for 2 parameters of 2 values" do
31
+ data = [[:A1, :A2], [:B1, :B2]]
32
+
33
+ Pairwise.combinations(*data).should == [[:A1, :B1], [:A1, :B2], [:A2, :B1], [:A2, :B2]]
34
+ end
35
+
36
+ it "should generate all pairs for 3 parameters of 1 value" do
37
+ data = [[:A1], [:B1], [:C1]]
38
+
39
+ Pairwise.combinations(*data).should == [[:A1, :B1, :C1]]
40
+ end
41
+
42
+ it "should generate pairs for three paramters" do
43
+ data = [[:A1, :A2],
44
+ [:B1, :B2],
45
+ [:C1 , :C2]]
46
+
47
+ Pairwise.combinations(*data).should == [[:A1, :B1, :C1],
48
+ [:A1, :B2, :C2],
49
+ [:A2, :B1, :C2],
50
+ [:A2, :B2, :C1]]
51
+ end
52
+ end
53
+
54
+ context "which are unequal lengths" do
55
+ it "should generate all pairs for 3 parameters of 1,1,2 values" do
56
+ data = [[:A1], [:B1], [:C1, :C2]]
57
+
58
+ Pairwise.combinations(*data).should == [[:A1, :B1, :C1],
59
+ [:A1, :B1, :C2]]
60
+ end
61
+
62
+ it "should generate all pairs for 3 parameters of 1,1,3 values" do
63
+ data = [[:A1], [:B1], [:C1, :C2, :C3]]
64
+
65
+ Pairwise.combinations(*data).should == [[:A1, :B1, :C1],
66
+ [:A1, :B1, :C2],
67
+ [:A1, :B1, :C3]]
68
+ end
69
+
70
+ it "should generate all pairs for 3 parameters of 1,2,3 values" do
71
+ data = [[:A1], [:B1, :B2], [:C1, :C2, :C3]]
72
+
73
+ Pairwise.combinations(*data).should == [[:A1, :B1, :C1], [:A1, :B2, :C3], [:A1, :B2, :C1], [:A1, :B1, :C2], [:A1, :B2, :C2], [:A1, :B1, :C3]]
74
+ end
75
+
76
+ it "should generate all pairs for 3 parameters of 2,1,2 values" do
77
+ data = [[:A1, :A2], [:B1], [:C1, :C2]]
78
+
79
+ Pairwise.combinations(*data).should == [[:A1, :B1, :C1],
80
+ [:A2, :B1, :C2],
81
+ [:A2, :B1, :C1],
82
+ [:A1, :B1, :C2]]
83
+
84
+
85
+ #:A1, :B1, :C1
86
+ #:A1, :B1, :C2
87
+ #:A2, :B1, :C1
88
+ #:A2,any_value_of_B, :C2
89
+ end
90
+
91
+ it "should generate all pairs for 4 parameters of 2,1,2,2 values" do
92
+ data = [[:A1, :A2], [:B1], [:C1, :C2], [:D1, :D2]]
93
+
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]]
95
+ end
96
+
97
+ it "should generate pairs for three parameters" do
98
+ data = [[:A1, :A2],
99
+ [:B1, :B2],
100
+ [:C1 , :C2 , :C3 ]]
101
+
102
+ Pairwise.combinations(*data).should == [[:A1, :B1, :C1], [:A1, :B2, :C3], [:A2, :B1, :C3], [:A2, :B2, :C2], [:A2, :B2, :C1], [:A1, :B1, :C2]]
103
+ end
104
+
105
+ describe "replacing wildcards which could have more than one option" do
106
+ it "should generate pairs for 2 parameters of 3,2,3 values" do
107
+ Pairwise.combinations([:A1, :A2, :A3],
108
+ [:B1, :B2],
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]]
118
+ end
119
+ end
120
+ end
121
+
122
+ end
123
+ end
@@ -0,0 +1,8 @@
1
+ require 'rubygems'
2
+ require 'bundler'
3
+ Bundler.require(:test)
4
+
5
+ SimpleCov.start
6
+ SimpleCov.command_name 'unit_tests'
7
+
8
+ require File.dirname(__FILE__) + '/../lib/pairwise'
metadata ADDED
@@ -0,0 +1,111 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: pairwise_psych
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.2.3
5
+ platform: ruby
6
+ authors:
7
+ - Joseph Wilk
8
+ - Ali King
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2016-06-04 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: unicode-display_width
16
+ requirement: !ruby/object:Gem::Requirement
17
+ requirements:
18
+ - - '>='
19
+ - !ruby/object:Gem::Version
20
+ version: '0'
21
+ type: :runtime
22
+ prerelease: false
23
+ version_requirements: !ruby/object:Gem::Requirement
24
+ requirements:
25
+ - - '>='
26
+ - !ruby/object:Gem::Version
27
+ version: '0'
28
+ - !ruby/object:Gem::Dependency
29
+ name: rspec
30
+ requirement: !ruby/object:Gem::Requirement
31
+ requirements:
32
+ - - '>='
33
+ - !ruby/object:Gem::Version
34
+ version: '0'
35
+ type: :development
36
+ prerelease: false
37
+ version_requirements: !ruby/object:Gem::Requirement
38
+ requirements:
39
+ - - '>='
40
+ - !ruby/object:Gem::Version
41
+ version: '0'
42
+ - !ruby/object:Gem::Dependency
43
+ name: cucumber
44
+ requirement: !ruby/object:Gem::Requirement
45
+ requirements:
46
+ - - '>='
47
+ - !ruby/object:Gem::Version
48
+ version: '0'
49
+ type: :development
50
+ prerelease: false
51
+ version_requirements: !ruby/object:Gem::Requirement
52
+ requirements:
53
+ - - '>='
54
+ - !ruby/object:Gem::Version
55
+ version: '0'
56
+ description: |-
57
+ A tool for selecting a smaller number of test combinations (using pairwise generation) rather than exhaustively testing all possible permutations.
58
+ To read about why, go here: http://www.pairwise.org/
59
+ This variation uses the newer psych yaml engine
60
+ email: ali@animoto.com
61
+ executables:
62
+ - pairwise
63
+ extensions: []
64
+ extra_rdoc_files: []
65
+ files:
66
+ - README.md
67
+ - bin/pairwise
68
+ - lib/pairwise.rb
69
+ - lib/pairwise/cli.rb
70
+ - lib/pairwise/formatter.rb
71
+ - lib/pairwise/formatter/csv.rb
72
+ - lib/pairwise/formatter/cucumber.rb
73
+ - lib/pairwise/input_data.rb
74
+ - lib/pairwise/input_file.rb
75
+ - lib/pairwise/ipo.rb
76
+ - lib/pairwise/ipo/horizontal.rb
77
+ - lib/pairwise/ipo/vertical.rb
78
+ - lib/pairwise/pair_collection.rb
79
+ - lib/pairwise/test_pair.rb
80
+ - lib/version.rb
81
+ - spec/pairwise/cli_spec.rb
82
+ - spec/pairwise/ipo/horizontal_spec.rb
83
+ - spec/pairwise/ipo_spec.rb
84
+ - spec/pairwise/pair_collection_spec.rb
85
+ - spec/pairwise_spec.rb
86
+ - spec/spec_helper.rb
87
+ homepage: https://aliking.github.io/pairwise/
88
+ licenses: []
89
+ metadata: {}
90
+ post_install_message:
91
+ rdoc_options: []
92
+ require_paths:
93
+ - lib
94
+ required_ruby_version: !ruby/object:Gem::Requirement
95
+ requirements:
96
+ - - '>='
97
+ - !ruby/object:Gem::Version
98
+ version: '0'
99
+ required_rubygems_version: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - '>='
102
+ - !ruby/object:Gem::Version
103
+ version: '0'
104
+ requirements: []
105
+ rubyforge_project:
106
+ rubygems_version: 2.5.2
107
+ signing_key:
108
+ specification_version: 4
109
+ summary: Turn large test data combinations into smaller ones
110
+ test_files: []
111
+ has_rdoc: