dither 0.1.5 → 0.2.0.rc3
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 +4 -4
- data/Gemfile.lock +6 -1
- data/README.md +3 -0
- data/Rakefile +4 -0
- data/dither.gemspec +4 -0
- data/ext/dither/README.md +2 -0
- data/ext/dither/base_constraint_handler.h +36 -0
- data/ext/dither/combinations.h +127 -0
- data/ext/dither/dither.cc +47 -0
- data/ext/dither/dither.h +31 -0
- data/ext/dither/dither_types.h +32 -0
- data/ext/dither/extconf.rb +4 -0
- data/ext/dither/ipog.cc +451 -0
- data/ext/dither/ipog.h +128 -0
- data/ext/dither/simple_constraint_handler.cc +119 -0
- data/ext/dither/simple_constraint_handler.h +38 -0
- data/lib/dither/api.rb +20 -0
- data/lib/dither/version.rb +1 -1
- data/lib/dither.rb +64 -13
- data/spec/dither/dither_spec.rb +27 -96
- metadata +47 -12
- data/lib/dither/ipog.rb +0 -58
- data/lib/dither/ipog_helper.rb +0 -161
- data/lib/dither/mipog.rb +0 -85
- data/lib/dither/param.rb +0 -19
- data/lib/dither/test_case.rb +0 -80
- data/lib/dither/unbound_param.rb +0 -17
@@ -0,0 +1,38 @@
|
|
1
|
+
/*
|
2
|
+
*
|
3
|
+
* Copyright (C) 2015 Jason Gowan
|
4
|
+
* All rights reserved.
|
5
|
+
*
|
6
|
+
* This software may be modified and distributed under the terms
|
7
|
+
* of the BSD license. See the LICENSE file for details.
|
8
|
+
*/
|
9
|
+
|
10
|
+
#ifndef SIMPLE_CONSTRAINT_HANDLER_H_
|
11
|
+
#define SIMPLE_CONSTRAINT_HANDLER_H_
|
12
|
+
|
13
|
+
#include <vector>
|
14
|
+
#include <utility>
|
15
|
+
#include <algorithm>
|
16
|
+
#include "dither_types.h"
|
17
|
+
#include "base_constraint_handler.h"
|
18
|
+
|
19
|
+
namespace dither {
|
20
|
+
|
21
|
+
class SimpleConstraintHandler : public BaseConstraintHandler {
|
22
|
+
protected:
|
23
|
+
std::vector<std::vector<std::pair<std::size_t, dval>>> constraints;
|
24
|
+
std::vector<dval> params;
|
25
|
+
|
26
|
+
inline bool violate_constraint(const dtest_case& test_case, const std::vector<std::pair<std::size_t, dval>>& constraint);
|
27
|
+
inline bool violate_constraint(const std::vector<param>& test_case, const std::vector<std::pair<std::size_t, dval>>& constraint);
|
28
|
+
|
29
|
+
public:
|
30
|
+
SimpleConstraintHandler(std::vector<dval>& ranges, std::vector<std::vector<dval>>& pconstraints);
|
31
|
+
bool violate_constraints(const dtest_case &test_case);
|
32
|
+
bool violate_constraints(const std::vector<param> &test_case);
|
33
|
+
bool ground(dtest_case &test_case);
|
34
|
+
};
|
35
|
+
}
|
36
|
+
|
37
|
+
#endif // SIMPLE_CONSTRAINT_HANDLER_H_
|
38
|
+
|
data/lib/dither/api.rb
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
|
2
|
+
require 'ffi'
|
3
|
+
|
4
|
+
# Interface to the c++ api.
|
5
|
+
module Dither
|
6
|
+
module API
|
7
|
+
extend FFI::Library
|
8
|
+
ffi_lib %w[lib/dither.so lib/dither.dll]
|
9
|
+
|
10
|
+
attach_function :dither_ipog_new, [:int], :pointer
|
11
|
+
attach_function :dither_ipog_add_parameter_int, [:pointer, :int, :pointer, :int], :void
|
12
|
+
attach_function :dither_ipog_run, [:pointer], :void
|
13
|
+
attach_function :dither_ipog_size, [:pointer], :int
|
14
|
+
attach_function :dither_ipog_display_raw_solution, [:pointer], :void
|
15
|
+
attach_function :dither_ipog_fill, [:pointer, :pointer], :void
|
16
|
+
attach_function :dither_ipog_add_constraint, [:pointer, :pointer, :int], :void
|
17
|
+
attach_function :dither_ipog_add_previously_tested, [:pointer, :pointer, :int], :void
|
18
|
+
# attach_function :dither_ipog_delete, [:pointer], :void
|
19
|
+
end
|
20
|
+
end
|
data/lib/dither/version.rb
CHANGED
data/lib/dither.rb
CHANGED
@@ -12,18 +12,73 @@ module Dither
|
|
12
12
|
# deprecated
|
13
13
|
def self.all_pairs(params, t = 2, opts = {})
|
14
14
|
opts[:t] = t
|
15
|
-
|
15
|
+
ipog(params, opts)
|
16
16
|
end
|
17
17
|
|
18
18
|
def self.ipog(params, opts = {})
|
19
19
|
opts = DEFUALT_OPTS.dup.merge(opts)
|
20
|
-
|
21
|
-
|
20
|
+
t = opts[:t] || 2
|
21
|
+
if t < 2
|
22
|
+
raise Dither::Error,'t must be >= 2'
|
23
|
+
end
|
24
|
+
raise Dither::Error, 'param length must be > 1' if params.any? { |a| a.size <= 1 }
|
25
|
+
if t > params.size
|
26
|
+
raise Dither::Error, 't must be <= params.length'
|
27
|
+
end
|
22
28
|
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
29
|
+
pointer = Dither::API.dither_ipog_new(t)
|
30
|
+
c_params = (0..params.max { |a| a.size }.size).to_a
|
31
|
+
c_int_params = FFI::MemoryPointer.new(:int, c_params.size)
|
32
|
+
c_int_params.write_array_of_int(c_params)
|
33
|
+
|
34
|
+
params.each_with_index do |param, i|
|
35
|
+
Dither::API.dither_ipog_add_parameter_int(pointer, i, c_int_params, param.size)
|
36
|
+
end
|
37
|
+
|
38
|
+
if opts[:constraints]
|
39
|
+
constraint_scratch = FFI::MemoryPointer.new(:int, params.size)
|
40
|
+
opts[:constraints].each do |constraint|
|
41
|
+
arr = Array.new(params.size, -1)
|
42
|
+
constraint.each do |k, v|
|
43
|
+
if k >= params.size
|
44
|
+
raise Dither::Error, "Invalid constraint #{k} > #{params.size}"
|
45
|
+
end
|
46
|
+
if v >= params[k].size
|
47
|
+
raise Dither::Error, "Invalid constraint #{k} > #{params[k].size}"
|
48
|
+
|
49
|
+
end
|
50
|
+
arr[k] = v
|
51
|
+
end
|
52
|
+
constraint_scratch.write_array_of_int(arr)
|
53
|
+
Dither::API.dither_ipog_add_constraint(pointer, constraint_scratch, params.size)
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
if opts[:previously_tested]
|
58
|
+
tested_scratch = FFI::MemoryPointer.new(:int, params.size)
|
59
|
+
opts[:previously_tested].each do |test_case|
|
60
|
+
if test_case.size != params.size
|
61
|
+
raise Dither::Error
|
62
|
+
end
|
63
|
+
arr = Array.new(params.size)
|
64
|
+
(0...params.size).each do |i|
|
65
|
+
arr[i] = params[i].find(test_case[i]).first
|
66
|
+
end
|
67
|
+
tested_scratch.write_array_of_int(arr)
|
68
|
+
Dither::API.dither_ipog_add_previously_tested(pointer, tested_scratch, params.size)
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
Dither::API.dither_ipog_run(pointer)
|
73
|
+
result_size = Dither::API.dither_ipog_size(pointer)
|
74
|
+
solution = FFI::MemoryPointer.new(:int, params.size * result_size)
|
75
|
+
Dither::API.dither_ipog_fill(pointer, solution)
|
76
|
+
|
77
|
+
results = solution.read_array_of_int(params.size * result_size)
|
78
|
+
.enum_for(:each_slice, params.size)
|
79
|
+
.map do |test_case|
|
80
|
+
test_case.zip(params).map { |a, b| b[a] }
|
81
|
+
end
|
27
82
|
end
|
28
83
|
|
29
84
|
def self.aetg(params, opts = {})
|
@@ -34,12 +89,6 @@ module Dither
|
|
34
89
|
class << self; alias_method :ateg, :aetg end
|
35
90
|
end # Dither
|
36
91
|
|
37
|
-
require 'dither/param'
|
38
|
-
require 'dither/unbound_param'
|
39
|
-
require 'dither/test_case'
|
40
|
-
require 'dither/ipog_helper'
|
41
|
-
require 'dither/ipog'
|
42
|
-
require 'dither/mipog'
|
43
92
|
require 'dither/chinese_postman_problem'
|
44
93
|
require 'dither/aetg'
|
45
94
|
require 'dither/aetg_pairwise'
|
@@ -51,4 +100,6 @@ if RUBY_PLATFORM =~ /java/
|
|
51
100
|
require 'dither-0.1.3.jar'
|
52
101
|
|
53
102
|
require 'dither/java_ext/dither'
|
103
|
+
else
|
104
|
+
require 'dither/api'
|
54
105
|
end
|
data/spec/dither/dither_spec.rb
CHANGED
@@ -2,10 +2,6 @@ require File.expand_path('../../spec_helper.rb', __FILE__)
|
|
2
2
|
|
3
3
|
describe Dither do
|
4
4
|
|
5
|
-
it 'mipog does not support constraints' do
|
6
|
-
expect { Dither.mipog([[1,1],[1,2]], 2, :constraints => []) }.to raise_error(Dither::Error, 'mipog does not support constraints')
|
7
|
-
end
|
8
|
-
|
9
5
|
it 't must be >= 2' do
|
10
6
|
expect { Dither.ipog([], :t => 0) }.to raise_error(Dither::Error, 't must be >= 2')
|
11
7
|
end
|
@@ -20,63 +16,15 @@ describe Dither do
|
|
20
16
|
|
21
17
|
it 'can compute 2-way ipog using symbols' do
|
22
18
|
params = [[:a, :b, :c], [:d, :e, :f], [:h, :i]]
|
23
|
-
expect(Dither.
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
end
|
33
|
-
|
34
|
-
it 'can compute 3-way mipog' do
|
35
|
-
params = [(0...2).to_a, (0...2).to_a, (0..3).to_a]
|
36
|
-
expect(Dither.mipog(params, 3)).to eq([[0, 0, 0],
|
37
|
-
[1, 0, 0],
|
38
|
-
[0, 1, 0],
|
39
|
-
[1, 1, 0],
|
40
|
-
[0, 0, 1],
|
41
|
-
[1, 0, 1],
|
42
|
-
[0, 1, 1],
|
43
|
-
[1, 1, 1],
|
44
|
-
[0, 0, 2],
|
45
|
-
[1, 0, 2],
|
46
|
-
[0, 1, 2],
|
47
|
-
[1, 1, 2],
|
48
|
-
[0, 0, 3],
|
49
|
-
[1, 0, 3],
|
50
|
-
[0, 1, 3],
|
51
|
-
[1, 1, 3],
|
52
|
-
])
|
53
|
-
end
|
54
|
-
|
55
|
-
it 'can compute 2-way mipog using symbols' do
|
56
|
-
params = [[:a, :b, :c], [:d, :e, :f], [:h, :i]]
|
57
|
-
expect(Dither.mipog(params).to_set).to eq([[:a, :d, :h],
|
58
|
-
[:a, :e, :i],
|
59
|
-
[:a, :f, :h],
|
60
|
-
[:b, :d, :i],
|
61
|
-
[:b, :e, :h],
|
62
|
-
[:b, :f, :i],
|
63
|
-
[:c, :d, :h],
|
64
|
-
[:c, :e, :i],
|
65
|
-
[:c, :f, :h]].to_set)
|
66
|
-
end
|
67
|
-
|
68
|
-
it 'can compute 2-way mipog' do
|
69
|
-
params = [(0...2).to_a, (0..3).to_a]
|
70
|
-
expect(Dither.mipog(params)).to eq([
|
71
|
-
[0, 0],
|
72
|
-
[1, 0],
|
73
|
-
[0, 1],
|
74
|
-
[1, 1],
|
75
|
-
[0, 2],
|
76
|
-
[1, 2],
|
77
|
-
[0, 3],
|
78
|
-
[1, 3],
|
79
|
-
])
|
19
|
+
expect(Dither.all_pairs(params)).to eq([[:c, :f, :h],
|
20
|
+
[:b, :f, :i],
|
21
|
+
[:a, :f, :h],
|
22
|
+
[:c, :e, :i],
|
23
|
+
[:b, :e, :h],
|
24
|
+
[:a, :e, :i],
|
25
|
+
[:c, :d, :h],
|
26
|
+
[:b, :d, :i],
|
27
|
+
[:a, :d, :h]])
|
80
28
|
end
|
81
29
|
|
82
30
|
it 'can compute 2-way ipog' do
|
@@ -90,7 +38,7 @@ describe Dither do
|
|
90
38
|
[1, 2],
|
91
39
|
[0, 3],
|
92
40
|
[1, 3],
|
93
|
-
|
41
|
+
].reverse)
|
94
42
|
end
|
95
43
|
|
96
44
|
it 'can compute 3-way ipog' do
|
@@ -116,54 +64,37 @@ describe Dither do
|
|
116
64
|
|
117
65
|
it 'can compute 3-way ipog with constraints' do
|
118
66
|
params = [(0...2).to_a, (0...2).to_a, (0..3).to_a]
|
119
|
-
|
67
|
+
results = Dither.ipog(params, :t => 3,
|
120
68
|
:constraints => [
|
121
69
|
{0 => 0,
|
122
70
|
2 => 2},
|
123
71
|
{0 => 0,
|
124
72
|
1 => 1,
|
125
73
|
2 => 0}],
|
126
|
-
:previously_tested => [[0, 0, 0]]
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
[0, 1, 3],
|
138
|
-
[1, 1, 3],
|
139
|
-
].to_set)
|
74
|
+
:previously_tested => [[0, 0, 0]]
|
75
|
+
)
|
76
|
+
results.each do |result|
|
77
|
+
expect(result[0] == 0 && result[1] == 1 && result[2] == 0).to be false
|
78
|
+
end
|
79
|
+
results.each do |result|
|
80
|
+
expect(result[0] == 0 && result[1] == 2).to be false
|
81
|
+
end
|
82
|
+
results.each do |result|
|
83
|
+
expect(result[0] == 0 && result[1] == 0 && result[2] == 0).to be false
|
84
|
+
end
|
140
85
|
end
|
141
86
|
|
142
87
|
it 'another 3-way ipog with constraints' do
|
143
88
|
params = [(0...2).to_a, (0...2).to_a, (0...2).to_a, (0..3).to_a]
|
144
|
-
|
89
|
+
results = Dither.ipog(params, :t => 3,
|
145
90
|
:constraints => [
|
146
91
|
{0 => 0,
|
147
92
|
1 => 1,
|
148
93
|
2 => 0}
|
149
|
-
])
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
[1, 0, 0, 1],
|
154
|
-
[1, 1, 0, 1],
|
155
|
-
[0, 0, 1, 1],
|
156
|
-
[1, 1, 1, 1],
|
157
|
-
[0, 0, 0, 2],
|
158
|
-
[1, 1, 0, 2],
|
159
|
-
[1, 0, 1, 2],
|
160
|
-
[0, 1, 1, 2],
|
161
|
-
[0, 0, 0, 3],
|
162
|
-
[1, 1, 0, 3],
|
163
|
-
[1, 0, 1, 3],
|
164
|
-
[0, 1, 1, 3],
|
165
|
-
[0, 0, 0, 1],
|
166
|
-
[0, 1, 1, 1]].to_set)
|
94
|
+
])
|
95
|
+
results.each do |result|
|
96
|
+
expect(result[0] == 0 && result[1] == 1 && result[2] == 0).to be false
|
97
|
+
end
|
167
98
|
end
|
168
99
|
|
169
100
|
it 'can run 2-way aetg' do
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: dither
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.2.0.rc3
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Jason Gowan
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2015-
|
11
|
+
date: 2015-12-08 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rspec
|
@@ -38,6 +38,20 @@ dependencies:
|
|
38
38
|
- - "~>"
|
39
39
|
- !ruby/object:Gem::Version
|
40
40
|
version: 0.9.2
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: rake-compiler
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - ">="
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '0'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - ">="
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '0'
|
41
55
|
- !ruby/object:Gem::Dependency
|
42
56
|
name: coveralls
|
43
57
|
requirement: !ruby/object:Gem::Requirement
|
@@ -52,11 +66,26 @@ dependencies:
|
|
52
66
|
- - ">="
|
53
67
|
- !ruby/object:Gem::Version
|
54
68
|
version: '0'
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: ffi
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - "~>"
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '1.0'
|
76
|
+
type: :runtime
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - "~>"
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: '1.0'
|
55
83
|
description: Efficient test generation strategies
|
56
84
|
email:
|
57
85
|
- gowanjason@gmail.com
|
58
86
|
executables: []
|
59
|
-
extensions:
|
87
|
+
extensions:
|
88
|
+
- ext/dither/extconf.rb
|
60
89
|
extra_rdoc_files: []
|
61
90
|
files:
|
62
91
|
- ".gitignore"
|
@@ -68,18 +97,24 @@ files:
|
|
68
97
|
- README.md
|
69
98
|
- Rakefile
|
70
99
|
- dither.gemspec
|
100
|
+
- ext/dither/README.md
|
101
|
+
- ext/dither/base_constraint_handler.h
|
102
|
+
- ext/dither/combinations.h
|
103
|
+
- ext/dither/dither.cc
|
104
|
+
- ext/dither/dither.h
|
105
|
+
- ext/dither/dither_types.h
|
106
|
+
- ext/dither/extconf.rb
|
107
|
+
- ext/dither/ipog.cc
|
108
|
+
- ext/dither/ipog.h
|
109
|
+
- ext/dither/simple_constraint_handler.cc
|
110
|
+
- ext/dither/simple_constraint_handler.h
|
71
111
|
- lib/dither.rb
|
72
112
|
- lib/dither/aetg.rb
|
73
113
|
- lib/dither/aetg_pairwise.rb
|
114
|
+
- lib/dither/api.rb
|
74
115
|
- lib/dither/chinese_postman_problem.rb
|
75
116
|
- lib/dither/graph.rb
|
76
|
-
- lib/dither/ipog.rb
|
77
|
-
- lib/dither/ipog_helper.rb
|
78
117
|
- lib/dither/java_ext/dither.rb
|
79
|
-
- lib/dither/mipog.rb
|
80
|
-
- lib/dither/param.rb
|
81
|
-
- lib/dither/test_case.rb
|
82
|
-
- lib/dither/unbound_param.rb
|
83
118
|
- lib/dither/version.rb
|
84
119
|
- spec/dither/chinese_postman_problem_spec.rb
|
85
120
|
- spec/dither/dither_spec.rb
|
@@ -100,12 +135,12 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
100
135
|
version: '0'
|
101
136
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
102
137
|
requirements:
|
103
|
-
- - "
|
138
|
+
- - ">"
|
104
139
|
- !ruby/object:Gem::Version
|
105
|
-
version:
|
140
|
+
version: 1.3.1
|
106
141
|
requirements: []
|
107
142
|
rubyforge_project: dither
|
108
|
-
rubygems_version: 2.
|
143
|
+
rubygems_version: 2.4.5.1
|
109
144
|
signing_key:
|
110
145
|
specification_version: 4
|
111
146
|
summary: Collection of test generation strategies
|
data/lib/dither/ipog.rb
DELETED
@@ -1,58 +0,0 @@
|
|
1
|
-
# coding: utf-8
|
2
|
-
|
3
|
-
module Dither
|
4
|
-
class IPOG
|
5
|
-
include IPOGHelper
|
6
|
-
|
7
|
-
def run
|
8
|
-
# add into test set a test for each combination of values
|
9
|
-
# of the first t parameter
|
10
|
-
test_set = comb
|
11
|
-
|
12
|
-
(t...params.length).each do |i|
|
13
|
-
# let pi
|
14
|
-
# be the set of t-way combinations of values involving
|
15
|
-
# parameter Pi and t -1 parameters among the first i – 1
|
16
|
-
# parameters
|
17
|
-
pi = comb_i(i)
|
18
|
-
|
19
|
-
# horizontal extension for parameter i
|
20
|
-
test_set.each do |test_case|
|
21
|
-
cover = maximize_coverage(i, test_case, pi)
|
22
|
-
|
23
|
-
if cover.nil?
|
24
|
-
test_set.delete(test_case)
|
25
|
-
else
|
26
|
-
pi -= cover
|
27
|
-
end
|
28
|
-
end
|
29
|
-
|
30
|
-
# vertical extension for parameter i
|
31
|
-
pi.each do |a|
|
32
|
-
if test_set.any? { |b| a.subset?(b) }
|
33
|
-
pi.delete(a)
|
34
|
-
else
|
35
|
-
|
36
|
-
test_case = nil
|
37
|
-
test_set.each do |b|
|
38
|
-
test_case = b.merge_without_conflict(i, a) do |a|
|
39
|
-
violates_constraints?(a)
|
40
|
-
end
|
41
|
-
break unless test_case.nil?
|
42
|
-
end
|
43
|
-
|
44
|
-
if test_case.nil?
|
45
|
-
test_set << a.create_unbound(i)
|
46
|
-
end
|
47
|
-
pi.delete(a)
|
48
|
-
end
|
49
|
-
end
|
50
|
-
end
|
51
|
-
|
52
|
-
@test_set = test_set.map { |a| fill_unbound(a) }
|
53
|
-
.delete_if(&:nil?)
|
54
|
-
.to_a
|
55
|
-
@test_set
|
56
|
-
end
|
57
|
-
end # IPOG
|
58
|
-
end # Dither
|
data/lib/dither/ipog_helper.rb
DELETED
@@ -1,161 +0,0 @@
|
|
1
|
-
# coding: utf-8
|
2
|
-
|
3
|
-
module Dither
|
4
|
-
module IPOGHelper
|
5
|
-
attr_reader :params, :t, :constraints, :test_set, :orig_params, :unbound_param_pool, :tested
|
6
|
-
private :params, :t, :constraints, :test_set, :orig_params, :unbound_param_pool, :tested
|
7
|
-
|
8
|
-
def initialize(params, opts = {})
|
9
|
-
init_params(params, (opts[:previously_tested] || []))
|
10
|
-
@t = opts[:t]
|
11
|
-
unless opts[:constraints].nil?
|
12
|
-
@constraints = opts[:constraints].map(&:to_a)
|
13
|
-
.map { |a| a.map { |b| @params[@map_to_orig_index.key(b[0])][b[1]] } }
|
14
|
-
.map(&:to_set)
|
15
|
-
end
|
16
|
-
|
17
|
-
raise Dither::Error, 't must be >= 2' if opts[:t] < 2
|
18
|
-
raise Dither::Error, 't must be <= params.length' if opts[:t] > params.length
|
19
|
-
params.each do |param|
|
20
|
-
raise Dither::Error, 'param length must be > 1' if param.length < 2
|
21
|
-
end
|
22
|
-
end
|
23
|
-
|
24
|
-
def init_params(user_params, previously_tested)
|
25
|
-
tmp = []
|
26
|
-
@input_params = user_params
|
27
|
-
user_params.each_with_index { |e, i| tmp << [i, e] }
|
28
|
-
@orig_params = tmp.sort_by { |a| a[1].length }
|
29
|
-
.reverse!
|
30
|
-
|
31
|
-
orig_param_map = {}
|
32
|
-
@map_to_orig_index = {}
|
33
|
-
@orig_params.each_with_index do |e, i|
|
34
|
-
@map_to_orig_index[i] = e[0]
|
35
|
-
orig_param_map[e[0]] = {}
|
36
|
-
end
|
37
|
-
|
38
|
-
@params = []
|
39
|
-
@unbound_param_pool = []
|
40
|
-
orig_params.each_with_index do |e, i|
|
41
|
-
@params << (0...e[1].length).map do |j|
|
42
|
-
local_param = Param.new(i, j)
|
43
|
-
orig_param_map[e[0]][e[1][j]] = local_param
|
44
|
-
local_param
|
45
|
-
end
|
46
|
-
@unbound_param_pool << UnboundParam.new(i)
|
47
|
-
end
|
48
|
-
|
49
|
-
@tested = []
|
50
|
-
previously_tested.each do |a|
|
51
|
-
local_params = []
|
52
|
-
a.each_with_index do |e, i|
|
53
|
-
local_params << orig_param_map[i][e]
|
54
|
-
end
|
55
|
-
@tested << TestCase.create(params, unbound_param_pool, local_params)
|
56
|
-
end
|
57
|
-
|
58
|
-
params
|
59
|
-
end
|
60
|
-
|
61
|
-
# return nil if unable to satisfy constraints
|
62
|
-
def maximize_coverage(i, test_case, pi)
|
63
|
-
current_max = 0
|
64
|
-
current_max_j = 0
|
65
|
-
current_matches = []
|
66
|
-
|
67
|
-
(0...params[i].length).each do |j|
|
68
|
-
current_param = params[i][j]
|
69
|
-
test_case << current_param
|
70
|
-
unless violates_constraints?(test_case)
|
71
|
-
matches = pi.select { |a| a.subset?(test_case) }
|
72
|
-
count = matches.count
|
73
|
-
|
74
|
-
if count > current_max
|
75
|
-
current_max = count
|
76
|
-
current_max_j = j
|
77
|
-
current_matches = matches
|
78
|
-
end
|
79
|
-
end
|
80
|
-
test_case.delete(current_param)
|
81
|
-
end
|
82
|
-
|
83
|
-
return nil if violates_constraints?(test_case)
|
84
|
-
test_case << params[i][current_max_j]
|
85
|
-
|
86
|
-
current_matches
|
87
|
-
end
|
88
|
-
|
89
|
-
def violates_constraints?(params)
|
90
|
-
return false if constraints.nil?
|
91
|
-
constraints.any? { |b| b.subset?(params) }
|
92
|
-
end
|
93
|
-
|
94
|
-
private
|
95
|
-
|
96
|
-
def comb
|
97
|
-
ranges = (0...t).to_a.inject([]) do |a, i|
|
98
|
-
a << (0...params[i].length).map { |j| params[i][j] }
|
99
|
-
end
|
100
|
-
|
101
|
-
products = ranges[1..-1].inject(ranges[0]) do |a, b|
|
102
|
-
a = a.product(b)
|
103
|
-
end
|
104
|
-
|
105
|
-
result = products.map(&:flatten)
|
106
|
-
.map { |a| TestCase.create(params, unbound_param_pool, a) }
|
107
|
-
result.reject { |a| tested?(a) }
|
108
|
-
end
|
109
|
-
|
110
|
-
def comb_i(param_i)
|
111
|
-
values = (0...param_i).to_a.combination((t-1)).to_a
|
112
|
-
values.each do |a|
|
113
|
-
a << param_i
|
114
|
-
end
|
115
|
-
result = []
|
116
|
-
values.each do |a|
|
117
|
-
result += a[1..-1]
|
118
|
-
.inject((0...params[a[0]].length).map { |b| params[a[0]][b] }) { |p, i| p.product((0...params[i].length).to_a.map { |c| params[i][c] }) }
|
119
|
-
.map(&:flatten)
|
120
|
-
.map { |b| TestCase.create(params, unbound_param_pool, b) }
|
121
|
-
end
|
122
|
-
result.reject { |a| tested?(a) }.to_set
|
123
|
-
end
|
124
|
-
|
125
|
-
def tested?(test_case)
|
126
|
-
@tested.any? { |a| test_case.subset?(a) }
|
127
|
-
end
|
128
|
-
|
129
|
-
def fill_unbound(data)
|
130
|
-
arr = Array.new(params.length)
|
131
|
-
data.each do |param|
|
132
|
-
unless param.unbound?
|
133
|
-
i = @map_to_orig_index[param.i]
|
134
|
-
arr[i] = @input_params[i][param.j]
|
135
|
-
end
|
136
|
-
end
|
137
|
-
|
138
|
-
arr.each_with_index do |e, i|
|
139
|
-
next unless e.nil?
|
140
|
-
|
141
|
-
orig_param = @input_params[i]
|
142
|
-
(0...orig_param.length).each do |j|
|
143
|
-
data << params[@map_to_orig_index.key(i)][j]
|
144
|
-
if violates_constraints?(data)
|
145
|
-
data.delete(params[@map_to_orig_index.key(i)][j])
|
146
|
-
next
|
147
|
-
else
|
148
|
-
arr[i] = orig_param[j]
|
149
|
-
break
|
150
|
-
end
|
151
|
-
end
|
152
|
-
return nil if arr[i].nil?
|
153
|
-
end
|
154
|
-
|
155
|
-
return nil if violates_constraints?(data)
|
156
|
-
return nil if tested?(data)
|
157
|
-
|
158
|
-
arr
|
159
|
-
end
|
160
|
-
end # IPOGHelper
|
161
|
-
end # Dither
|