solve 4.0.0 → 4.0.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +5 -5
- data/lib/solve/constraint.rb +1 -1
- data/lib/solve/gecode_solver.rb +1 -1
- data/lib/solve/ruby_solver.rb +1 -1
- data/lib/solve/version.rb +1 -1
- metadata +5 -42
- data/.gitignore +0 -20
- data/.travis.yml +0 -25
- data/Gemfile +0 -36
- data/Guardfile +0 -17
- data/NoGecode.gemfile +0 -4
- data/README.md +0 -85
- data/Rakefile +0 -11
- data/Thorfile +0 -36
- data/solve.gemspec +0 -26
- data/spec/acceptance/benchmark.rb +0 -59
- data/spec/acceptance/large_graph_no_solution.rb +0 -18730
- data/spec/acceptance/opscode_ci_graph.rb +0 -18600
- data/spec/acceptance/ruby_solver_solutions_spec.rb +0 -306
- data/spec/acceptance/solutions_spec.rb +0 -316
- data/spec/spec_helper.rb +0 -32
- data/spec/unit/solve/artifact_spec.rb +0 -137
- data/spec/unit/solve/demand_spec.rb +0 -53
- data/spec/unit/solve/dependency_spec.rb +0 -25
- data/spec/unit/solve/gecode_solver_spec.rb +0 -205
- data/spec/unit/solve/graph_spec.rb +0 -130
- data/spec/unit/solve/ruby_solver_spec.rb +0 -165
- data/spec/unit/solve/solver/serializer_spec.rb +0 -33
- data/spec/unit/solve_spec.rb +0 -19
data/spec/spec_helper.rb
DELETED
@@ -1,32 +0,0 @@
|
|
1
|
-
require "rubygems"
|
2
|
-
require "bundler"
|
3
|
-
require "spork"
|
4
|
-
|
5
|
-
Spork.prefork do
|
6
|
-
require "rspec"
|
7
|
-
|
8
|
-
APP_ROOT = File.expand_path("../../", __FILE__)
|
9
|
-
|
10
|
-
Dir[File.join(APP_ROOT, "spec/support/**/*.rb")].each { |f| require f }
|
11
|
-
|
12
|
-
RSpec.configure do |config|
|
13
|
-
config.mock_with :rspec
|
14
|
-
config.filter_run focus: true
|
15
|
-
config.run_all_when_everything_filtered = true
|
16
|
-
|
17
|
-
# Run specs in a random order
|
18
|
-
config.order = :random
|
19
|
-
end
|
20
|
-
end
|
21
|
-
|
22
|
-
Spork.each_run do
|
23
|
-
require "solve"
|
24
|
-
end
|
25
|
-
|
26
|
-
# Useful for debugging the solver - simply add `ui: TestUI` to a resolver call
|
27
|
-
class TestUI
|
28
|
-
def self.say(message = "")
|
29
|
-
$stdout.print(message + "\n")
|
30
|
-
$stdout.flush
|
31
|
-
end
|
32
|
-
end
|
@@ -1,137 +0,0 @@
|
|
1
|
-
require "spec_helper"
|
2
|
-
|
3
|
-
describe Solve::Artifact do
|
4
|
-
let(:graph) do
|
5
|
-
package = double("package")
|
6
|
-
package_version = double("package_version")
|
7
|
-
package_version.stub(:dependencies).and_return([])
|
8
|
-
package.stub(:add_version).and_return(package_version)
|
9
|
-
double("graph", dep_graph: double("dep_graph", package: package))
|
10
|
-
end
|
11
|
-
|
12
|
-
let(:name) { "league" }
|
13
|
-
let(:version) { "1.0.0" }
|
14
|
-
subject { Solve::Artifact.new(graph, name, version) }
|
15
|
-
|
16
|
-
describe "equality" do
|
17
|
-
context "given an artifact with the same name and version" do
|
18
|
-
let(:one) { Solve::Artifact.new(graph, "riot", "1.0.0") }
|
19
|
-
let(:two) { Solve::Artifact.new(graph, "riot", "1.0.0") }
|
20
|
-
|
21
|
-
it "is equal" do
|
22
|
-
expect(one).to eq(two)
|
23
|
-
end
|
24
|
-
end
|
25
|
-
|
26
|
-
context "given an artifact with the same name but different version" do
|
27
|
-
let(:one) { Solve::Artifact.new(graph, "riot", "1.0.0") }
|
28
|
-
let(:two) { Solve::Artifact.new(graph, "riot", "2.0.0") }
|
29
|
-
|
30
|
-
it "is not equal" do
|
31
|
-
expect(one).to_not eq(two)
|
32
|
-
end
|
33
|
-
end
|
34
|
-
|
35
|
-
context "given an artifact with the same version but different name" do
|
36
|
-
let(:one) { Solve::Artifact.new(graph, "riot", "1.0.0") }
|
37
|
-
let(:two) { Solve::Artifact.new(graph, "league", "1.0.0") }
|
38
|
-
|
39
|
-
it "is not equal" do
|
40
|
-
expect(one).to_not eq(two)
|
41
|
-
end
|
42
|
-
end
|
43
|
-
end
|
44
|
-
|
45
|
-
describe "sorting" do
|
46
|
-
let(:one) { Solve::Artifact.new(graph, "riot", "1.0.0") }
|
47
|
-
let(:two) { Solve::Artifact.new(graph, "riot", "2.0.0") }
|
48
|
-
let(:three) { Solve::Artifact.new(graph, "riot", "3.0.0") }
|
49
|
-
|
50
|
-
let(:artifacts) do
|
51
|
-
[
|
52
|
-
one,
|
53
|
-
two,
|
54
|
-
three,
|
55
|
-
].shuffle
|
56
|
-
end
|
57
|
-
|
58
|
-
it "orders artifacts by their version number" do
|
59
|
-
sorted = artifacts.sort
|
60
|
-
|
61
|
-
expect(sorted[0]).to eq(one)
|
62
|
-
expect(sorted[1]).to eq(two)
|
63
|
-
expect(sorted[2]).to eq(three)
|
64
|
-
end
|
65
|
-
end
|
66
|
-
|
67
|
-
describe "#dependency?" do
|
68
|
-
before { subject.depends("nginx", "1.0.0") }
|
69
|
-
|
70
|
-
it "returns false when the dependency does not exist" do
|
71
|
-
expect(subject).to have_dependency("nginx", "1.0.0")
|
72
|
-
end
|
73
|
-
|
74
|
-
it "returns true when the dependendency exists" do
|
75
|
-
expect(subject).to_not have_dependency("apache2", "2.0.0")
|
76
|
-
end
|
77
|
-
end
|
78
|
-
|
79
|
-
describe "#dependency" do
|
80
|
-
before { subject.depends("nginx", "~> 1.2.3") }
|
81
|
-
|
82
|
-
it "returns an instance of Solve::Dependency matching the given name and constraint" do
|
83
|
-
dependency = subject.dependency("nginx", "~> 1.2.3")
|
84
|
-
|
85
|
-
expect(dependency).to be_a(Solve::Dependency)
|
86
|
-
expect(dependency.name).to eq("nginx")
|
87
|
-
expect(dependency.constraint.to_s).to eq("~> 1.2.3")
|
88
|
-
end
|
89
|
-
end
|
90
|
-
|
91
|
-
describe "#dependencies" do
|
92
|
-
it "returns an array" do
|
93
|
-
expect(subject.dependencies).to be_a(Array)
|
94
|
-
end
|
95
|
-
|
96
|
-
it "returns an empty array if no dependencies have been accessed" do
|
97
|
-
expect(subject.dependencies).to be_empty
|
98
|
-
end
|
99
|
-
|
100
|
-
it "returns all dependencies" do
|
101
|
-
subject.depends("nginx", "1.0.0")
|
102
|
-
subject.depends("nginx", "~> 2.0.0")
|
103
|
-
|
104
|
-
expect(subject.dependencies.size).to eq(2)
|
105
|
-
end
|
106
|
-
end
|
107
|
-
|
108
|
-
describe "#depends" do
|
109
|
-
context "given a name and constraint argument" do
|
110
|
-
let(:name) { "nginx" }
|
111
|
-
let(:constraint) { "~> 1.0.0" }
|
112
|
-
|
113
|
-
context "given the dependency of the given name and constraint does not exist" do
|
114
|
-
it "returns a Solve::Artifact" do
|
115
|
-
expect(subject.depends(name, constraint)).to be_a(Solve::Artifact)
|
116
|
-
end
|
117
|
-
|
118
|
-
it "adds a dependency with the given name and constraint to the list of dependencies" do
|
119
|
-
subject.depends(name, constraint)
|
120
|
-
|
121
|
-
expect(subject.dependencies.size).to eq(1)
|
122
|
-
expect(subject.dependencies.first.name).to eq(name)
|
123
|
-
expect(subject.dependencies.first.constraint.to_s).to eq(constraint)
|
124
|
-
end
|
125
|
-
end
|
126
|
-
end
|
127
|
-
|
128
|
-
context "given only a name argument" do
|
129
|
-
it "adds a dependency with a all constraint (>= 0.0.0)" do
|
130
|
-
subject.depends("nginx")
|
131
|
-
|
132
|
-
expect(subject.dependencies.size).to eq(1)
|
133
|
-
expect(subject.dependencies.first.constraint.to_s).to eq(">= 0.0.0")
|
134
|
-
end
|
135
|
-
end
|
136
|
-
end
|
137
|
-
end
|
@@ -1,53 +0,0 @@
|
|
1
|
-
require "spec_helper"
|
2
|
-
|
3
|
-
describe Solve::Demand do
|
4
|
-
let(:solver) { double("solver") }
|
5
|
-
let(:name) { "league" }
|
6
|
-
|
7
|
-
describe "#initialize" do
|
8
|
-
it "accepts a string for the constraint parameter" do
|
9
|
-
demand = Solve::Demand.new(solver, name, "= 0.0.1")
|
10
|
-
expect(demand.constraint.to_s).to eq("= 0.0.1")
|
11
|
-
end
|
12
|
-
|
13
|
-
it "accepts a Semverse::Constraint for the constraint parameter" do
|
14
|
-
constraint = Semverse::Constraint.new("= 0.0.1")
|
15
|
-
demand = Solve::Demand.new(solver, name, constraint)
|
16
|
-
|
17
|
-
expect(demand.constraint).to eq(constraint)
|
18
|
-
end
|
19
|
-
|
20
|
-
context "when no value for 'constraint' is given" do
|
21
|
-
it "uses a default of >= 0.0.0" do
|
22
|
-
demand = Solve::Demand.new(solver, name)
|
23
|
-
|
24
|
-
expect(demand.constraint.operator).to eq(">=")
|
25
|
-
expect(demand.constraint.version.to_s).to eq("0.0.0")
|
26
|
-
end
|
27
|
-
end
|
28
|
-
end
|
29
|
-
|
30
|
-
let(:constraint) { "~> 1.0.0" }
|
31
|
-
subject { Solve::Demand.new(solver, name, constraint) }
|
32
|
-
|
33
|
-
describe "equality" do
|
34
|
-
it "returns true when other is a Solve::Demand with the same name and constriant" do
|
35
|
-
other = Solve::Demand.new(solver, name, constraint)
|
36
|
-
expect(subject).to eq(other)
|
37
|
-
end
|
38
|
-
|
39
|
-
it "returns false when other isn't a Solve::Demand" do
|
40
|
-
expect(subject).to_not eq("chicken")
|
41
|
-
end
|
42
|
-
|
43
|
-
it "returns false when other is a Solve::Demand with the same name but a different constraint" do
|
44
|
-
other = Solve::Demand.new(solver, name, "< 3.4.5")
|
45
|
-
expect(subject).to_not eq(other)
|
46
|
-
end
|
47
|
-
|
48
|
-
it "returns false when other is a Solve::Demand with the same constraint but a different name" do
|
49
|
-
other = Solve::Demand.new(solver, "chicken", constraint)
|
50
|
-
expect(subject).to_not eq(other)
|
51
|
-
end
|
52
|
-
end
|
53
|
-
end
|
@@ -1,25 +0,0 @@
|
|
1
|
-
require "spec_helper"
|
2
|
-
|
3
|
-
describe Solve::Dependency do
|
4
|
-
describe "#initialize" do
|
5
|
-
it "uses a default of >= 0.0.0" do
|
6
|
-
dep = Solve::Dependency.new(double("artifact"), "ntp")
|
7
|
-
|
8
|
-
expect(dep.constraint.operator).to eq(">=")
|
9
|
-
expect(dep.constraint.version.to_s).to eq("0.0.0")
|
10
|
-
end
|
11
|
-
end
|
12
|
-
|
13
|
-
let(:artifact) { double("artifact") }
|
14
|
-
let(:name) { "nginx" }
|
15
|
-
let(:constraint) { "~> 0.0.1" }
|
16
|
-
|
17
|
-
subject { Solve::Dependency.new(artifact, name, constraint) }
|
18
|
-
|
19
|
-
describe "#==" do
|
20
|
-
it "returns true if the other object is an instance of Solve::Dependency with the same constraint and artifact" do
|
21
|
-
other = Solve::Dependency.new(artifact, name, constraint)
|
22
|
-
expect(subject).to eq(other)
|
23
|
-
end
|
24
|
-
end
|
25
|
-
end
|
@@ -1,205 +0,0 @@
|
|
1
|
-
require "spec_helper"
|
2
|
-
|
3
|
-
require "solve/gecode_solver"
|
4
|
-
|
5
|
-
describe Solve::GecodeSolver, :gecode do
|
6
|
-
|
7
|
-
before(:all) do
|
8
|
-
described_class.activate
|
9
|
-
end
|
10
|
-
|
11
|
-
describe "ClassMethods" do
|
12
|
-
describe "::timeout" do
|
13
|
-
subject { described_class.timeout }
|
14
|
-
|
15
|
-
it "returns 30,000 by default" do
|
16
|
-
expect(subject).to eql(30_000)
|
17
|
-
end
|
18
|
-
|
19
|
-
context "when the SOLVE_TIMEOUT env variable is set" do
|
20
|
-
before { ENV.stub(:[]).with("SOLVE_TIMEOUT") { "30" } }
|
21
|
-
|
22
|
-
it "returns the value multiplied by a thousand" do
|
23
|
-
expect(subject).to eql(30_000)
|
24
|
-
end
|
25
|
-
end
|
26
|
-
end
|
27
|
-
|
28
|
-
describe "::activate" do
|
29
|
-
|
30
|
-
context "when dep_selector is not installed" do
|
31
|
-
|
32
|
-
it "raises EngineNotAvailable" do
|
33
|
-
exception = LoadError.new("cannot load such file -- dep_selector")
|
34
|
-
allow(described_class).to receive(:require).with("dep_selector").and_raise(exception)
|
35
|
-
expect { described_class.activate }.to raise_error(Solve::Errors::EngineNotAvailable)
|
36
|
-
end
|
37
|
-
|
38
|
-
end
|
39
|
-
|
40
|
-
context "when dep_selector is installed" do
|
41
|
-
|
42
|
-
it "requires dep_selector" do
|
43
|
-
expect(described_class).to receive(:require).with("dep_selector").and_call_original
|
44
|
-
described_class.activate
|
45
|
-
end
|
46
|
-
end
|
47
|
-
|
48
|
-
end
|
49
|
-
end
|
50
|
-
|
51
|
-
let(:graph) { double(Solve::Graph) }
|
52
|
-
let(:demands) { [["mysql"], ["nginx"]] }
|
53
|
-
subject(:solver) { described_class.new(graph, demands) }
|
54
|
-
|
55
|
-
it "has a list of demands as ruby literals" do
|
56
|
-
solver.demands_array.should == demands
|
57
|
-
end
|
58
|
-
|
59
|
-
it "has a list of demands as model objects" do
|
60
|
-
expected = [
|
61
|
-
Solve::Demand.new(solver, "mysql"),
|
62
|
-
Solve::Demand.new(solver, "nginx"),
|
63
|
-
]
|
64
|
-
solver.demands.should == expected
|
65
|
-
end
|
66
|
-
|
67
|
-
it "has a graph" do
|
68
|
-
solver.graph.should == graph
|
69
|
-
end
|
70
|
-
|
71
|
-
describe "when the constraints are solvable" do
|
72
|
-
let(:graph) do
|
73
|
-
graph = Solve::Graph.new
|
74
|
-
graph.artifact("A", "1.0.0")
|
75
|
-
graph.artifact("B", "1.0.0").depends("A")
|
76
|
-
graph
|
77
|
-
end
|
78
|
-
|
79
|
-
let(:demands) { [["A"], ["B"]] }
|
80
|
-
|
81
|
-
it "gives the solution as a Hash" do
|
82
|
-
solver.resolve.should == { "A" => "1.0.0", "B" => "1.0.0" }
|
83
|
-
end
|
84
|
-
|
85
|
-
it "gives the solution in sorted form" do
|
86
|
-
solver.resolve(sorted: true).should == [["A", "1.0.0"], ["B", "1.0.0"]]
|
87
|
-
end
|
88
|
-
end
|
89
|
-
|
90
|
-
describe "when the constraints are not solvable" do
|
91
|
-
let(:error) do
|
92
|
-
begin
|
93
|
-
solver.resolve
|
94
|
-
rescue => e
|
95
|
-
e
|
96
|
-
else
|
97
|
-
raise "Expected resolve to cause an error"
|
98
|
-
end
|
99
|
-
end
|
100
|
-
|
101
|
-
context "and dep-selector identifies missing artifacts" do
|
102
|
-
let(:graph) do
|
103
|
-
graph = Solve::Graph.new
|
104
|
-
graph.artifact("A", "1.0.0")
|
105
|
-
graph
|
106
|
-
end
|
107
|
-
|
108
|
-
let(:demands) { [ ["Z"] ] }
|
109
|
-
|
110
|
-
it "raises an error detailing the missing artifacts" do
|
111
|
-
error.to_s.should include("Missing artifacts: Z")
|
112
|
-
end
|
113
|
-
end
|
114
|
-
|
115
|
-
context "and dep-selector identifies constraints that exclude all known versions" do
|
116
|
-
let(:graph) do
|
117
|
-
graph = Solve::Graph.new
|
118
|
-
graph.artifact("A", "1.0.0")
|
119
|
-
graph
|
120
|
-
end
|
121
|
-
|
122
|
-
let(:demands) { [ ["A", "> 1.0.0"] ] }
|
123
|
-
|
124
|
-
it "raises an error detailing the missing artifacts" do
|
125
|
-
error.to_s.should include("Required artifacts do not exist at the desired version")
|
126
|
-
error.to_s.should include("Constraints that match no available version: (A > 1.0.0)")
|
127
|
-
end
|
128
|
-
end
|
129
|
-
|
130
|
-
context "and dep-selector identifies dependency conflicts" do
|
131
|
-
let(:graph) do
|
132
|
-
graph = Solve::Graph.new
|
133
|
-
graph.artifact("A", "1.0.0").depends("B").depends("C")
|
134
|
-
graph.artifact("B", "1.0.0").depends("D", "= 1.0.0")
|
135
|
-
graph.artifact("C", "1.0.0").depends("D", "= 2.0.0")
|
136
|
-
graph.artifact("D", "1.0.0")
|
137
|
-
graph.artifact("D", "2.0.0")
|
138
|
-
graph
|
139
|
-
end
|
140
|
-
|
141
|
-
let(:demands) { [ [ "A" ] ] }
|
142
|
-
|
143
|
-
it "raises an error detailing the missing artifacts" do
|
144
|
-
error.to_s.should include("Demand that cannot be met: (A >= 0.0.0)")
|
145
|
-
error.to_s.should include("Artifacts for which there are conflicting dependencies: D = 1.0.0 -> []")
|
146
|
-
end
|
147
|
-
end
|
148
|
-
|
149
|
-
context "and dep-selector times out looking for a solution" do
|
150
|
-
let(:selector) { double(DepSelector::Selector) }
|
151
|
-
|
152
|
-
before do
|
153
|
-
graph.stub(:artifacts).and_return([])
|
154
|
-
DepSelector::Selector.stub(:new).and_return(selector)
|
155
|
-
selector.stub(:find_solution).and_raise(DepSelector::Exceptions::TimeBoundExceeded)
|
156
|
-
end
|
157
|
-
|
158
|
-
it "raises an error explaining no solution could be found" do
|
159
|
-
error.to_s.should include("The dependency constraints could not be solved in the time allotted.")
|
160
|
-
end
|
161
|
-
end
|
162
|
-
|
163
|
-
context "and dep-selector times out looking for dependency conflicts" do
|
164
|
-
let(:selector) { double(DepSelector::Selector) }
|
165
|
-
|
166
|
-
before do
|
167
|
-
graph.stub(:artifacts).and_return([])
|
168
|
-
DepSelector::Selector.stub(:new).and_return(selector)
|
169
|
-
selector.stub(:find_solution).and_raise(DepSelector::Exceptions::TimeBoundExceededNoSolution)
|
170
|
-
end
|
171
|
-
|
172
|
-
it "raises a NoSolutionCauseUnknown error to indicate that no debug info was generated" do
|
173
|
-
error.should be_a_kind_of(Solve::Errors::NoSolutionCauseUnknown)
|
174
|
-
end
|
175
|
-
|
176
|
-
it "raises an error explaining that no solution exists but the cause could not be determined" do
|
177
|
-
error.to_s.should include("There is a dependency conflict, but the solver could not determine the precise cause in the time allotted.")
|
178
|
-
end
|
179
|
-
end
|
180
|
-
end
|
181
|
-
|
182
|
-
describe "finding unsatisfiable demands" do
|
183
|
-
it "partitions demands into satisfiable and not satisfiable"
|
184
|
-
end
|
185
|
-
|
186
|
-
describe "supporting Serializer interface" do
|
187
|
-
let(:serializer) { Solve::Solver::Serializer.new }
|
188
|
-
|
189
|
-
before do
|
190
|
-
graph.stub(:artifacts).and_return([])
|
191
|
-
end
|
192
|
-
|
193
|
-
it "implements the required interface" do
|
194
|
-
problem = Solve::Problem.from_solver(solver)
|
195
|
-
json_string = serializer.serialize(problem)
|
196
|
-
problem_data = JSON.parse(json_string)
|
197
|
-
expected_demands = [
|
198
|
-
{ "name" => "mysql", "constraint" => ">= 0.0.0" },
|
199
|
-
{ "name" => "nginx", "constraint" => ">= 0.0.0" },
|
200
|
-
]
|
201
|
-
|
202
|
-
problem_data["demands"].should =~ expected_demands
|
203
|
-
end
|
204
|
-
end
|
205
|
-
end
|