pairwise_psych 0.2.3

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 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: