solve 0.8.2 → 1.0.0.rc1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.gitignore +2 -0
- data/.travis.yml +4 -0
- data/README.md +8 -11
- data/lib/solve.rb +3 -8
- data/lib/solve/artifact.rb +44 -80
- data/lib/solve/constraint.rb +62 -46
- data/lib/solve/demand.rb +6 -21
- data/lib/solve/dependency.rb +10 -22
- data/lib/solve/errors.rb +43 -17
- data/lib/solve/gem_version.rb +1 -1
- data/lib/solve/graph.rb +43 -123
- data/lib/solve/solver.rb +134 -262
- data/lib/solve/solver/serializer.rb +1 -1
- data/solve.gemspec +3 -1
- data/spec/acceptance/benchmark.rb +45 -0
- data/spec/acceptance/large_graph_no_solution.rb +18730 -0
- data/spec/acceptance/opscode_ci_graph.rb +18600 -0
- data/spec/acceptance/solutions_spec.rb +117 -76
- data/spec/spec_helper.rb +3 -0
- data/spec/unit/solve/artifact_spec.rb +49 -64
- data/spec/unit/solve/demand_spec.rb +19 -56
- data/spec/unit/solve/dependency_spec.rb +7 -46
- data/spec/unit/solve/graph_spec.rb +72 -209
- data/spec/unit/solve/solver/serializer_spec.rb +3 -4
- data/spec/unit/solve/solver_spec.rb +103 -247
- metadata +43 -22
- data/.ruby-version +0 -1
- data/lib/solve/solver/constraint_row.rb +0 -25
- data/lib/solve/solver/constraint_table.rb +0 -31
- data/lib/solve/solver/variable_row.rb +0 -43
- data/lib/solve/solver/variable_table.rb +0 -55
- data/lib/solve/tracers.rb +0 -50
- data/lib/solve/tracers/human_readable.rb +0 -67
- data/lib/solve/tracers/silent.rb +0 -17
- data/lib/solve/version.rb +0 -140
- data/spec/unit/solve/constraint_spec.rb +0 -708
- data/spec/unit/solve/version_spec.rb +0 -355
@@ -4,27 +4,25 @@ describe Solve::Demand do
|
|
4
4
|
let(:solver) { double('solver') }
|
5
5
|
let(:name) { "league" }
|
6
6
|
|
7
|
-
describe "
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
subject.new(solver, name, "= 0.0.1").constraint.to_s.should eql("= 0.0.1")
|
13
|
-
end
|
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
|
14
12
|
|
15
|
-
|
16
|
-
|
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)
|
17
16
|
|
18
|
-
|
19
|
-
|
17
|
+
expect(demand.constraint).to eq(constraint)
|
18
|
+
end
|
20
19
|
|
21
|
-
|
22
|
-
|
23
|
-
|
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)
|
24
23
|
|
25
|
-
|
26
|
-
|
27
|
-
end
|
24
|
+
expect(demand.constraint.operator).to eq(">=")
|
25
|
+
expect(demand.constraint.version.to_s).to eq("0.0.0")
|
28
26
|
end
|
29
27
|
end
|
30
28
|
end
|
@@ -32,59 +30,24 @@ describe Solve::Demand do
|
|
32
30
|
let(:constraint) { "~> 1.0.0" }
|
33
31
|
subject { Solve::Demand.new(solver, name, constraint) }
|
34
32
|
|
35
|
-
describe "#delete" do
|
36
|
-
context "given the demand is related to a solver" do
|
37
|
-
subject { Solve::Demand.new(solver, name, constraint) }
|
38
|
-
|
39
|
-
before(:each) do
|
40
|
-
solver.should_receive(:remove_demand).with(subject).and_return(subject)
|
41
|
-
end
|
42
|
-
|
43
|
-
it "notifies the solver that the demand should be removed" do
|
44
|
-
subject.delete
|
45
|
-
end
|
46
|
-
|
47
|
-
it "sets the solver attribute to nil" do
|
48
|
-
subject.delete
|
49
|
-
|
50
|
-
subject.solver.should be_nil
|
51
|
-
end
|
52
|
-
|
53
|
-
it "returns the instance of demand deleted from the solver" do
|
54
|
-
subject.delete.should eql(subject)
|
55
|
-
end
|
56
|
-
end
|
57
|
-
|
58
|
-
context "given the demand is not the member of a solver" do
|
59
|
-
subject { Solve::Demand.new(nil, name, constraint) }
|
60
|
-
|
61
|
-
it "returns nil" do
|
62
|
-
subject.delete.should be_nil
|
63
|
-
end
|
64
|
-
end
|
65
|
-
end
|
66
|
-
|
67
33
|
describe "equality" do
|
68
34
|
it "returns true when other is a Solve::Demand with the same name and constriant" do
|
69
35
|
other = Solve::Demand.new(solver, name, constraint)
|
70
|
-
|
71
|
-
subject.should eql(other)
|
36
|
+
expect(subject).to eq(other)
|
72
37
|
end
|
73
38
|
|
74
39
|
it "returns false when other isn't a Solve::Demand" do
|
75
|
-
subject.
|
40
|
+
expect(subject).to_not eq("chicken")
|
76
41
|
end
|
77
42
|
|
78
43
|
it "returns false when other is a Solve::Demand with the same name but a different constraint" do
|
79
44
|
other = Solve::Demand.new(solver, name, "< 3.4.5")
|
80
|
-
|
81
|
-
subject.should_not eql(other)
|
45
|
+
expect(subject).to_not eq(other)
|
82
46
|
end
|
83
47
|
|
84
48
|
it "returns false when other is a Solve::Demand with the same constraint but a different name" do
|
85
49
|
other = Solve::Demand.new(solver, "chicken", constraint)
|
86
|
-
|
87
|
-
subject.should_not eql(other)
|
50
|
+
expect(subject).to_not eq(other)
|
88
51
|
end
|
89
52
|
end
|
90
53
|
end
|
@@ -1,18 +1,12 @@
|
|
1
1
|
require 'spec_helper'
|
2
2
|
|
3
3
|
describe Solve::Dependency do
|
4
|
-
describe "
|
5
|
-
|
4
|
+
describe "#initialize" do
|
5
|
+
it "uses a default of >= 0.0.0" do
|
6
|
+
dep = Solve::Dependency.new(double("artifact"), "ntp")
|
6
7
|
|
7
|
-
|
8
|
-
|
9
|
-
it "uses a default of >= 0.0.0" do
|
10
|
-
dep = subject.new(double('artifact'), "ntp")
|
11
|
-
|
12
|
-
dep.constraint.operator.should eql(">=")
|
13
|
-
dep.constraint.version.to_s.should eql("0.0.0")
|
14
|
-
end
|
15
|
-
end
|
8
|
+
expect(dep.constraint.operator).to eq(">=")
|
9
|
+
expect(dep.constraint.version.to_s).to eq("0.0.0")
|
16
10
|
end
|
17
11
|
end
|
18
12
|
|
@@ -22,43 +16,10 @@ describe Solve::Dependency do
|
|
22
16
|
|
23
17
|
subject { Solve::Dependency.new(artifact, name, constraint) }
|
24
18
|
|
25
|
-
describe "
|
26
|
-
context "given the dependency is a member of an artifact" do
|
27
|
-
subject { Solve::Dependency.new(artifact, name, constraint) }
|
28
|
-
|
29
|
-
before(:each) do
|
30
|
-
artifact.should_receive(:remove_dependency).with(subject).and_return(subject)
|
31
|
-
end
|
32
|
-
|
33
|
-
it "notifies the artifact that the dependency should be removed" do
|
34
|
-
subject.delete
|
35
|
-
end
|
36
|
-
|
37
|
-
it "sets the artifact attribute to nil" do
|
38
|
-
subject.delete
|
39
|
-
|
40
|
-
subject.artifact.should be_nil
|
41
|
-
end
|
42
|
-
|
43
|
-
it "returns the instance of dependency deleted from the artifact" do
|
44
|
-
subject.delete.should eql(subject)
|
45
|
-
end
|
46
|
-
end
|
47
|
-
|
48
|
-
context "given the dependency is not the member of an artifact" do
|
49
|
-
subject { Solve::Dependency.new(nil, name, constraint) }
|
50
|
-
|
51
|
-
it "returns nil" do
|
52
|
-
subject.delete.should be_nil
|
53
|
-
end
|
54
|
-
end
|
55
|
-
end
|
56
|
-
|
57
|
-
describe "#eql?" do
|
19
|
+
describe "#==" do
|
58
20
|
it "returns true if the other object is an instance of Solve::Dependency with the same constraint and artifact" do
|
59
21
|
other = Solve::Dependency.new(artifact, name, constraint)
|
60
|
-
|
61
|
-
subject.should eql(other)
|
22
|
+
expect(subject).to eq(other)
|
62
23
|
end
|
63
24
|
end
|
64
25
|
end
|
@@ -1,267 +1,130 @@
|
|
1
|
-
require
|
1
|
+
require "spec_helper"
|
2
2
|
|
3
3
|
describe Solve::Graph do
|
4
|
-
describe "
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
context "given a Solve::Artifact" do
|
9
|
-
let(:artifact) { Solve::Artifact.new(double('graph'), "nginx", "1.2.3") }
|
10
|
-
|
11
|
-
it "delegates to ::artifact_key with the name and version of the artifact" do
|
12
|
-
subject.should_receive(:artifact_key).with(artifact.name, artifact.version)
|
13
|
-
|
14
|
-
subject.key_for(artifact)
|
15
|
-
end
|
16
|
-
end
|
17
|
-
|
18
|
-
context "given a Solve::Dependency" do
|
19
|
-
let(:demand) { Solve::Dependency.new(double('artifact'), "ntp", "= 2.3.4") }
|
20
|
-
|
21
|
-
it "delegates to ::dependency_key with the name and constraint of the dependency" do
|
22
|
-
subject.should_receive(:dependency_key).with(demand.name, anything)
|
23
|
-
|
24
|
-
subject.key_for(demand)
|
25
|
-
end
|
26
|
-
end
|
27
|
-
|
28
|
-
context "given an unknown object" do
|
29
|
-
it "raises an ArgumentError" do
|
30
|
-
lambda {
|
31
|
-
subject.key_for("hello")
|
32
|
-
}.should raise_error(ArgumentError)
|
33
|
-
end
|
34
|
-
end
|
35
|
-
end
|
36
|
-
|
37
|
-
describe "::artifact_key" do
|
38
|
-
it "returns a symbol containing the name and version of the artifact" do
|
39
|
-
subject.artifact_key("nginx", "1.2.3").should eql(:'nginx-1.2.3')
|
40
|
-
end
|
4
|
+
describe "#artifact?" do
|
5
|
+
it "returns true if the given Solve::Artifact is a member of the collection" do
|
6
|
+
subject.artifact("nginx", "1.0.0")
|
7
|
+
expect(subject).to have_artifact("nginx", "1.0.0")
|
41
8
|
end
|
42
9
|
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
end
|
10
|
+
it "returns false if the given Solve::Artifact is not a member of the collection" do
|
11
|
+
expect(subject).to_not have_artifact("apache", "1.0.0")
|
12
|
+
expect(subject).to_not have_artifact("nginx", "11.4.4")
|
47
13
|
end
|
48
14
|
end
|
49
15
|
|
50
|
-
|
51
|
-
|
52
|
-
describe "#artifacts" do
|
53
|
-
context "given a name and version argument" do
|
54
|
-
let(:name) { "nginx" }
|
55
|
-
let(:version) { "0.101.5" }
|
56
|
-
|
57
|
-
context "given the artifact of the given name and version does not exist" do
|
58
|
-
it "returns a Solve::Artifact" do
|
59
|
-
subject.artifacts(name, version).should be_a(Solve::Artifact)
|
60
|
-
end
|
61
|
-
|
62
|
-
it "the artifact has the given name" do
|
63
|
-
subject.artifacts(name, version).name.should eql(name)
|
64
|
-
end
|
65
|
-
|
66
|
-
it "the artifact has the given version" do
|
67
|
-
subject.artifacts(name, version).version.to_s.should eql(version)
|
68
|
-
end
|
69
|
-
|
70
|
-
it "adds an artifact to the artifacts collection" do
|
71
|
-
subject.artifacts(name, version)
|
16
|
+
describe "#find" do
|
17
|
+
before { subject.artifact("nginx", "1.0.0") }
|
72
18
|
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
it "the artifact added matches the given name" do
|
77
|
-
subject.artifacts(name, version)
|
78
|
-
|
79
|
-
subject.artifacts[0].name.should eql(name)
|
80
|
-
end
|
81
|
-
|
82
|
-
it "the artifact added matches the given version" do
|
83
|
-
subject.artifacts(name, version)
|
19
|
+
it "returns an instance of artifact of the matching name and version" do
|
20
|
+
artifact = subject.find("nginx", "1.0.0")
|
84
21
|
|
85
|
-
|
86
|
-
|
87
|
-
|
22
|
+
expect(artifact).to be_a(Solve::Artifact)
|
23
|
+
expect(artifact.name).to eq("nginx")
|
24
|
+
expect(artifact.version.to_s).to eq("1.0.0")
|
88
25
|
end
|
89
26
|
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
it "returns an empty array if no artifacts have been accessed" do
|
96
|
-
subject.artifacts.should have(0).items
|
97
|
-
end
|
27
|
+
it "returns nil when the artifact does not exist" do
|
28
|
+
expect(subject.find("notthere", "1.0.0")).to be_nil
|
29
|
+
end
|
30
|
+
end
|
98
31
|
|
99
|
-
|
100
|
-
|
32
|
+
describe "#artifact" do
|
33
|
+
let(:name) { "nginx" }
|
34
|
+
let(:version) { "1.0.0" }
|
101
35
|
|
102
|
-
|
36
|
+
context "given the artifact of the given name and version does not exist" do
|
37
|
+
it "returns a Solve::Artifact" do
|
38
|
+
expect(subject.artifact(name, version)).to be_a(Solve::Artifact)
|
103
39
|
end
|
104
|
-
end
|
105
40
|
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
subject.artifacts(1, 2, 3)
|
110
|
-
}.should raise_error(ArgumentError, "Unexpected number of arguments. You gave: 3. Expected: 0 or 2.")
|
41
|
+
it "the artifact has the given name" do
|
42
|
+
artifact = subject.artifact(name, version)
|
43
|
+
expect(artifact.name).to eq(name)
|
111
44
|
end
|
112
45
|
|
113
|
-
it "
|
114
|
-
|
115
|
-
|
116
|
-
}.should raise_error(ArgumentError, "Unexpected number of arguments. You gave: 1. Expected: 0 or 2.")
|
46
|
+
it "the artifact has the given version" do
|
47
|
+
artifact = subject.artifact(name, version)
|
48
|
+
expect(artifact.version.to_s).to eq(version)
|
117
49
|
end
|
118
50
|
|
119
|
-
it "
|
120
|
-
|
121
|
-
|
122
|
-
}.should raise_error(ArgumentError, 'A name and version must be specified. You gave: ["nginx", nil].')
|
51
|
+
it "adds an artifact to the artifacts collection" do
|
52
|
+
subject.artifact(name, version)
|
53
|
+
expect(subject).to have_artifact(name, version)
|
123
54
|
end
|
124
55
|
end
|
125
56
|
end
|
126
57
|
|
127
|
-
describe "#
|
128
|
-
|
129
|
-
subject.artifacts(
|
58
|
+
describe "#artifacts" do
|
59
|
+
it "returns an array" do
|
60
|
+
expect(subject.artifacts).to be_a(Array)
|
130
61
|
end
|
131
62
|
|
132
|
-
it "returns an
|
133
|
-
|
134
|
-
|
135
|
-
artifact.should be_a(Solve::Artifact)
|
136
|
-
artifact.name.should eql("nginx")
|
137
|
-
artifact.version.to_s.should eql("1.0.0")
|
63
|
+
it "returns an empty array if no artifacts have been accessed" do
|
64
|
+
expect(subject.artifacts).to be_empty
|
138
65
|
end
|
139
66
|
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
end
|
67
|
+
it "returns an array containing an artifact if one was accessed" do
|
68
|
+
subject.artifact("nginx", "1.0.0")
|
69
|
+
expect(subject.artifacts.size).to eq(1)
|
144
70
|
end
|
145
71
|
end
|
146
72
|
|
147
73
|
describe "#versions" do
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
double('artifact', name: 'nginx', version: Solve::Version.new('4.0.0')),
|
154
|
-
double('artifact', name: 'nginx', version: Solve::Version.new('5.0.0')),
|
155
|
-
double('artifact', name: 'mysql', version: Solve::Version.new('4.0.0'))
|
156
|
-
]
|
157
|
-
end
|
74
|
+
before do
|
75
|
+
subject.artifact('nginx', '1.0.0')
|
76
|
+
subject.artifact('nginx', '2.0.0')
|
77
|
+
subject.artifact('nginx', '3.0.0')
|
78
|
+
subject.artifact('nginx', '4.0.0')
|
158
79
|
|
159
|
-
|
160
|
-
subject.stub(:artifacts).and_return(artifacts)
|
80
|
+
subject.artifact('other', '1.0.0')
|
161
81
|
end
|
162
82
|
|
163
83
|
it "returns all the artifacts matching the given name" do
|
164
|
-
subject.versions("nginx").
|
165
|
-
end
|
166
|
-
|
167
|
-
context "given an optional constraint value" do
|
168
|
-
it "returns only the artifacts matching the given constraint value and name" do
|
169
|
-
subject.versions("nginx", ">= 4.0.0").should have(2).items
|
170
|
-
end
|
171
|
-
end
|
172
|
-
end
|
173
|
-
|
174
|
-
describe "#add_artifact" do
|
175
|
-
let(:artifact) { Solve::Artifact.new(double('graph'), "nginx", "1.0.0") }
|
176
|
-
|
177
|
-
it "adds a Solve::Artifact to the collection of artifacts" do
|
178
|
-
subject.add_artifact(artifact)
|
179
|
-
|
180
|
-
subject.should have_artifact(artifact.name, artifact.version)
|
181
|
-
subject.artifacts.should have(1).item
|
182
|
-
end
|
183
|
-
|
184
|
-
it "should not add the same artifact twice to the collection" do
|
185
|
-
subject.add_artifact(artifact)
|
186
|
-
subject.add_artifact(artifact)
|
187
|
-
|
188
|
-
subject.artifacts.should have(1).item
|
189
|
-
end
|
190
|
-
end
|
191
|
-
|
192
|
-
describe "#remove_artifact" do
|
193
|
-
let(:artifact) { Solve::Artifact.new(double('graph'), "nginx", "1.0.0") }
|
194
|
-
|
195
|
-
context "given the artifact is a member of the collection" do
|
196
|
-
before(:each) { subject.add_artifact(artifact) }
|
197
|
-
|
198
|
-
it "removes the Solve::Artifact from the collection of artifacts" do
|
199
|
-
subject.remove_artifact(artifact)
|
200
|
-
|
201
|
-
subject.artifacts.should have(0).items
|
202
|
-
end
|
203
|
-
|
204
|
-
it "returns the removed Solve::Artifact" do
|
205
|
-
subject.remove_artifact(artifact).should eql(artifact)
|
206
|
-
end
|
84
|
+
expect(subject.versions("nginx").size).to eq(4)
|
207
85
|
end
|
208
86
|
|
209
|
-
|
210
|
-
|
211
|
-
|
212
|
-
|
87
|
+
it "does not satisfy constraints if it is the default" do
|
88
|
+
constraint = Semverse::Constraint.new(Semverse::DEFAULT_CONSTRAINT.to_s)
|
89
|
+
expect(constraint).to_not receive(:satisfies?)
|
90
|
+
subject.versions("nginx")
|
213
91
|
end
|
214
|
-
end
|
215
92
|
|
216
|
-
|
217
|
-
|
218
|
-
|
219
|
-
it "returns true if the given Solve::Artifact is a member of the collection" do
|
220
|
-
subject.add_artifact(artifact)
|
221
|
-
|
222
|
-
subject.has_artifact?(artifact.name, artifact.version).should be_true
|
223
|
-
end
|
224
|
-
|
225
|
-
it "returns false if the given Solve::Artifact is not a member of the collection" do
|
226
|
-
subject.has_artifact?(artifact.name, artifact.version).should be_false
|
93
|
+
it "returns only matching constraints if one is given" do
|
94
|
+
expect(subject.versions("nginx", ">= 3.0.0").size).to eq(2)
|
227
95
|
end
|
228
96
|
end
|
229
97
|
|
230
|
-
describe "
|
231
|
-
|
98
|
+
describe "==" do
|
99
|
+
def make_graph
|
232
100
|
graph = Solve::Graph.new
|
233
|
-
graph.
|
234
|
-
graph.
|
101
|
+
graph.artifact("A" ,"1.0.0").depends("B", "1.0.0")
|
102
|
+
graph.artifact("A" ,"2.0.0").depends("C", "1.0.0")
|
235
103
|
graph
|
236
104
|
end
|
237
105
|
|
106
|
+
subject { make_graph }
|
107
|
+
|
238
108
|
it "returns false if other isn't a Solve::Graph" do
|
239
|
-
subject.
|
109
|
+
expect(subject).to_not eq("chicken")
|
240
110
|
end
|
241
111
|
|
242
|
-
it "returns true if other is
|
243
|
-
|
244
|
-
other.artifacts("A", "1.0.0").depends("B", "1.0.0")
|
245
|
-
other.artifacts("A", "2.0.0").depends("C", "1.0.0")
|
246
|
-
|
247
|
-
subject.should eql(other)
|
112
|
+
it "returns true if the other is the same" do
|
113
|
+
expect(subject).to eq(make_graph)
|
248
114
|
end
|
249
115
|
|
250
|
-
it "returns false if the other
|
251
|
-
other =
|
252
|
-
other.
|
253
|
-
other.artifacts("A", "2.0.0")
|
116
|
+
it "returns false if the other has the same artifacts but different dependencies" do
|
117
|
+
other = make_graph
|
118
|
+
other.artifact("A", "1.0.0").depends("D", "1.0.0")
|
254
119
|
|
255
|
-
subject.
|
120
|
+
expect(subject).to_not eq(other)
|
256
121
|
end
|
257
122
|
|
258
|
-
it "returns false if the other
|
259
|
-
other =
|
260
|
-
other.
|
261
|
-
other.artifacts("A", "2.0.0").depends("C", "1.0.0")
|
262
|
-
other.artifacts("B", "1.0.0")
|
123
|
+
it "returns false if the other has the same dependencies but different artifacts" do
|
124
|
+
other = make_graph
|
125
|
+
other.artifact("E", "1.0.0")
|
263
126
|
|
264
|
-
subject.
|
127
|
+
expect(subject).to_not eq(other)
|
265
128
|
end
|
266
129
|
end
|
267
130
|
end
|