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.
@@ -2,12 +2,11 @@ require 'spec_helper'
2
2
 
3
3
  describe Solve::Solver::Serializer do
4
4
  it "deserializes a serialized solver to an equivalent solver" do
5
-
6
5
  graph = Solve::Graph.new
7
6
 
8
- graph.artifacts("A", "1.0.0").depends("B", "1.0.0")
9
- graph.artifacts("B", "1.0.0").depends("C", "1.0.0")
10
- graph.artifacts("C", "1.0.0")
7
+ graph.artifact("A", "1.0.0").depends("B", "1.0.0")
8
+ graph.artifact("B", "1.0.0").depends("C", "1.0.0")
9
+ graph.artifact("C", "1.0.0")
11
10
 
12
11
  demands = [["A", "1.0.0"]]
13
12
 
@@ -1,302 +1,158 @@
1
1
  require 'spec_helper'
2
2
 
3
3
  describe Solve::Solver do
4
- let(:graph) { double('graph') }
4
+ let(:graph) { double(Solve::Graph) }
5
+ let(:demands) { [["mysql"], ["nginx"]] }
6
+ subject(:solver) { described_class.new(graph, demands) }
5
7
 
6
- describe "ClassMethods" do
7
- subject { Solve::Solver }
8
-
9
- describe "::new" do
10
- let(:demand_array) { ["nginx", "ntp"] }
11
-
12
- it "adds a demand for each element in the array" do
13
- obj = subject.new(graph, demand_array)
14
-
15
- obj.demands.should have(2).items
16
- end
17
-
18
- context "when demand_array is an array of array" do
19
- let(:demand_array) { [["nginx", "= 1.2.3"], ["ntp", "= 1.0.0"]] }
20
-
21
- it "creates a new demand with the name and constraint of each element in the array" do
22
- obj = subject.new(graph, demand_array)
23
-
24
- obj.demands[0].name.should eql("nginx")
25
- obj.demands[0].constraint.to_s.should eql("= 1.2.3")
26
- obj.demands[1].name.should eql("ntp")
27
- obj.demands[1].constraint.to_s.should eql("= 1.0.0")
28
- end
29
- end
30
-
31
- context "when demand_array is an array of strings" do
32
- let(:demand_array) { ["nginx", "ntp"] }
33
-
34
- it "creates a new demand with the name and a default constraint of each element in the array" do
35
- obj = subject.new(graph, demand_array)
36
-
37
- obj.demands[0].name.should eql("nginx")
38
- obj.demands[0].constraint.to_s.should eql(">= 0.0.0")
39
- obj.demands[1].name.should eql("ntp")
40
- obj.demands[1].constraint.to_s.should eql(">= 0.0.0")
41
- end
42
- end
8
+ it "has a list of demands as ruby literals" do
9
+ solver.demands_array.should == demands
10
+ end
43
11
 
44
- context "when demand_array is a mix between an array of arrays and an array of strings" do
45
- let(:demand_array) { [["nginx", "= 1.2.3"], "ntp"] }
12
+ it "has a list of demands as model objects" do
13
+ expected = [
14
+ Solve::Demand.new(solver, "mysql"),
15
+ Solve::Demand.new(solver, "nginx")
16
+ ]
17
+ solver.demands.should == expected
18
+ end
46
19
 
47
- it "creates a new demand with the name and default constraint or constraint of each element in the array" do
48
- obj = subject.new(graph, demand_array)
20
+ it "has a graph" do
21
+ solver.graph.should == graph
22
+ end
49
23
 
50
- obj.demands[0].name.should eql("nginx")
51
- obj.demands[0].constraint.to_s.should eql("= 1.2.3")
52
- obj.demands[1].name.should eql("ntp")
53
- obj.demands[1].constraint.to_s.should eql(">= 0.0.0")
54
- end
55
- end
24
+ describe "when the constraints are solvable" do
25
+ let(:graph) do
26
+ graph = Solve::Graph.new
27
+ graph.artifact("A", "1.0.0")
28
+ graph.artifact("B", "1.0.0").depends("A")
29
+ graph
56
30
  end
57
31
 
58
- describe "::demand_key" do
59
- let(:demand) { Solve::Demand.new(double('solver'), "nginx", "= 1.2.3") }
32
+ let(:demands) { [["A"], ["B"]] }
60
33
 
61
- it "returns a symbol containing the name and constraint of the demand" do
62
- subject.demand_key(demand).should eql(:'nginx-= 1.2.3')
63
- end
34
+ it "gives the solution as a Hash" do
35
+ solver.resolve.should == {"A"=>"1.0.0", "B"=>"1.0.0"}
64
36
  end
65
37
 
66
- describe "::satisfy_all" do
67
- let(:ver_one) { Solve::Version.new("3.1.1") }
68
- let(:ver_two) { Solve::Version.new("3.1.2") }
69
-
70
- let(:constraints) do
71
- [
72
- Solve::Constraint.new("> 3.0.0"),
73
- Solve::Constraint.new("<= 3.1.2")
74
- ]
75
- end
76
-
77
- let(:versions) do
78
- [
79
- Solve::Version.new("0.0.1"),
80
- Solve::Version.new("0.1.0"),
81
- Solve::Version.new("1.0.0"),
82
- Solve::Version.new("2.0.0"),
83
- Solve::Version.new("3.0.0"),
84
- ver_one,
85
- ver_two,
86
- Solve::Version.new("4.1.0")
87
- ].shuffle
88
- end
89
-
90
- it "returns all of the versions which satisfy all of the given constraints" do
91
- solution = subject.satisfy_all(constraints, versions)
92
-
93
- solution.should have(2).items
94
- solution.should include(ver_one)
95
- solution.should include(ver_two)
96
- end
97
-
98
- context "given multiple duplicate versions" do
99
- let(:versions) do
100
- [
101
- ver_one,
102
- ver_one,
103
- ver_one
104
- ]
105
- end
106
-
107
- it "does not return duplicate satisfied versions" do
108
- solution = subject.satisfy_all(constraints, versions)
109
-
110
- solution.should have(1).item
111
- end
112
- end
38
+ it "gives the solution in sorted form" do
39
+ solver.resolve(sorted: true).should == [["A", "1.0.0"], ["B", "1.0.0"]]
113
40
  end
41
+ end
114
42
 
115
- describe "::satisfy_best" do
116
- let(:versions) do
117
- [
118
- Solve::Version.new("0.0.1"),
119
- Solve::Version.new("0.1.0"),
120
- Solve::Version.new("1.0.0"),
121
- Solve::Version.new("2.0.0"),
122
- Solve::Version.new("3.0.0"),
123
- Solve::Version.new("3.1.1"),
124
- Solve::Version.new("3.1.2"),
125
- Solve::Version.new("4.1.0")
126
- ].shuffle
43
+ describe "when the constraints are not solvable" do
44
+ let(:error) do
45
+ begin
46
+ solver.resolve
47
+ rescue => e
48
+ e
49
+ else
50
+ raise "Expected resolve to cause an error"
127
51
  end
52
+ end
128
53
 
129
- it "returns the best possible match for the given constraints" do
130
- subject.satisfy_best([">= 1.0.0", "< 4.1.0"], versions).to_s.should eql("3.1.2")
54
+ context "and dep-selector identifies missing artifacts" do
55
+ let(:graph) do
56
+ graph = Solve::Graph.new
57
+ graph.artifact("A", "1.0.0")
58
+ graph
131
59
  end
132
60
 
133
- context "given no version matches a constraint" do
134
- let(:versions) do
135
- [
136
- Solve::Version.new("4.1.0")
137
- ]
138
- end
61
+ let(:demands) { [ ["Z"] ] }
139
62
 
140
- it "raises a NoSolutionError error" do
141
- lambda {
142
- subject.satisfy_best(">= 5.0.0", versions)
143
- }.should raise_error(Solve::Errors::NoSolutionError)
144
- end
63
+ it "raises an error detailing the missing artifacts" do
64
+ error.to_s.should include("Missing artifacts: Z")
145
65
  end
146
66
  end
147
- end
148
-
149
- subject { Solve::Solver.new(graph) }
150
-
151
- describe "#resolve" do
152
- let(:graph) { Solve::Graph.new }
153
-
154
- subject { Solve::Solver.new(graph) }
155
-
156
- before(:each) do
157
- graph.artifacts("nginx", "1.0.0")
158
- subject.demands("nginx", "= 1.0.0")
159
- end
160
-
161
- it "returns a solution in the form of a Hash" do
162
- subject.resolve.should be_a(Hash)
163
- end
164
- end
165
-
166
- describe "#demands" do
167
- context "given a name and constraint argument" do
168
- let(:name) { "nginx" }
169
- let(:constraint) { "~> 0.101.5" }
170
-
171
- context "given the artifact of the given name and constraint does not exist" do
172
- it "returns a Solve::Demand" do
173
- subject.demands(name, constraint).should be_a(Solve::Demand)
174
- end
175
-
176
- it "the artifact has the given name" do
177
- subject.demands(name, constraint).name.should eql(name)
178
- end
179
-
180
- it "the artifact has the given constraint" do
181
- subject.demands(name, constraint).constraint.to_s.should eql(constraint)
182
- end
183
67
 
184
- it "adds an artifact to the demands collection" do
185
- subject.demands(name, constraint)
186
-
187
- subject.demands.should have(1).item
188
- end
189
-
190
- it "the artifact added matches the given name" do
191
- subject.demands(name, constraint)
192
-
193
- subject.demands[0].name.should eql(name)
194
- end
195
-
196
- it "the artifact added matches the given constraint" do
197
- subject.demands(name, constraint)
198
-
199
- subject.demands[0].constraint.to_s.should eql(constraint)
200
- end
68
+ context "and dep-selector identifies constraints that exclude all known versions" do
69
+ let(:graph) do
70
+ graph = Solve::Graph.new
71
+ graph.artifact("A", "1.0.0")
72
+ graph
201
73
  end
202
- end
203
74
 
204
- context "given only a name argument" do
205
- it "returns a demand with a match all version constraint (>= 0.0.0)" do
206
- subject.demands("nginx").constraint.to_s.should eql(">= 0.0.0")
207
- end
208
- end
75
+ let(:demands) { [ ["A", "> 1.0.0"] ] }
209
76
 
210
- context "given no arguments" do
211
- it "returns an array" do
212
- subject.demands.should be_a(Array)
77
+ it "raises an error detailing the missing artifacts" do
78
+ error.to_s.should include("Required artifacts do not exist at the desired version")
79
+ error.to_s.should include("Constraints that match no available version: (A > 1.0.0)")
213
80
  end
81
+ end
214
82
 
215
- it "returns an empty array if no demands have been accessed" do
216
- subject.demands.should have(0).items
83
+ context "and dep-selector identifies dependency conflicts" do
84
+ let(:graph) do
85
+ graph = Solve::Graph.new
86
+ graph.artifact("A", "1.0.0").depends("B").depends("C")
87
+ graph.artifact("B", "1.0.0").depends("D", "= 1.0.0")
88
+ graph.artifact("C", "1.0.0").depends("D", "= 2.0.0")
89
+ graph.artifact("D", "1.0.0")
90
+ graph.artifact("D", "2.0.0")
91
+ graph
217
92
  end
218
93
 
219
- it "returns an array containing a demand if one was accessed" do
220
- subject.demands("nginx", "~> 0.101.5")
94
+ let(:demands) { [ [ "A" ] ] }
221
95
 
222
- subject.demands.should have(1).item
96
+ it "raises an error detailing the missing artifacts" do
97
+ error.to_s.should include("Demand that cannot be met: (A >= 0.0.0)")
98
+ error.to_s.should include("Artifacts for which there are conflicting dependencies: D = 1.0.0 -> []")
223
99
  end
224
100
  end
225
101
 
226
- context "given an unexpected number of arguments" do
227
- it "raises an ArgumentError if more than two are provided" do
228
- lambda {
229
- subject.demands(1, 2, 3)
230
- }.should raise_error(ArgumentError, "Unexpected number of arguments. You gave: 3. Expected: 2 or less.")
231
- end
102
+ context "and dep-selector times out looking for a solution" do
103
+ let(:selector) { double(DepSelector::Selector) }
232
104
 
233
- it "raises an ArgumentError if a name argument of nil is provided" do
234
- lambda {
235
- subject.demands(nil)
236
- }.should raise_error(ArgumentError, "A name must be specified. You gave: [nil].")
105
+ before do
106
+ graph.stub(:artifacts).and_return([])
107
+ DepSelector::Selector.stub(:new).and_return(selector)
108
+ selector.stub(:find_solution).and_raise(DepSelector::Exceptions::TimeBoundExceeded)
237
109
  end
238
110
 
239
- it "raises an ArgumentError if a name and constraint argument are provided but name is nil" do
240
- lambda {
241
- subject.demands(nil, "= 1.0.0")
242
- }.should raise_error(ArgumentError, 'A name must be specified. You gave: [nil, "= 1.0.0"].')
111
+ it "raises an error explaining no solution could be found" do
112
+ error.to_s.should include("The dependency constraints could not be solved in the time allotted.")
243
113
  end
244
114
  end
245
- end
246
115
 
247
- describe "#add_demand" do
248
- let(:demand) { Solve::Demand.new(double('graph'), 'ntp') }
116
+ context "and dep-selector times out looking for dependency conflicts" do
117
+ let(:selector) { double(DepSelector::Selector) }
249
118
 
250
- it "adds a Solve::Artifact to the collection of artifacts" do
251
- subject.add_demand(demand)
252
-
253
- subject.should have_demand(demand)
254
- subject.demands.should have(1).item
255
- end
256
-
257
- it "should not add the same demand twice to the collection" do
258
- subject.add_demand(demand)
259
- subject.add_demand(demand)
260
-
261
- subject.demands.should have(1).item
262
- end
263
- end
264
-
265
- describe "#remove_demand" do
266
- let(:demand) { Solve::Demand.new(double('graph'), 'ntp') }
267
-
268
- context "given the demand is a member of the collection" do
269
- before(:each) { subject.add_demand(demand) }
270
-
271
- it "removes the Solve::Artifact from the collection of demands" do
272
- subject.remove_demand(demand)
273
-
274
- subject.demands.should have(0).items
119
+ before do
120
+ graph.stub(:artifacts).and_return([])
121
+ DepSelector::Selector.stub(:new).and_return(selector)
122
+ selector.stub(:find_solution).and_raise(DepSelector::Exceptions::TimeBoundExceededNoSolution)
275
123
  end
276
124
 
277
- it "returns the removed Solve::Artifact" do
278
- subject.remove_demand(demand).should eql(demand)
125
+ it "raises a NoSolutionCauseUnknown error to indicate that no debug info was generated" do
126
+ error.should be_a_kind_of(Solve::Errors::NoSolutionCauseUnknown)
279
127
  end
280
- end
281
128
 
282
- context "given the demand is not a member of the collection" do
283
- it "should return nil" do
284
- subject.remove_demand(demand).should be_nil
129
+ it "raises an error explaining that no solution exists but the cause could not be determined" do
130
+ error.to_s.should include("There is a dependency conflict, but the solver could not determine the precise cause in the time allotted.")
285
131
  end
286
132
  end
287
133
  end
288
134
 
289
- describe "#has_demand?" do
290
- let(:demand) { Solve::Demand.new(double('graph'), 'ntp') }
135
+ describe "finding unsatisfiable demands" do
136
+ it "partitions demands into satisfiable and not satisfiable"
137
+ end
291
138
 
292
- it "returns true if the given Solve::Artifact is a member of the collection" do
293
- subject.add_demand(demand)
139
+ describe "supporting Serializer interface" do
140
+ let(:serializer) { Solve::Solver::Serializer.new }
294
141
 
295
- subject.has_demand?(demand).should be_true
142
+ before do
143
+ graph.stub(:artifacts).and_return([])
296
144
  end
297
145
 
298
- it "returns false if the given Solve::Artifact is not a member of the collection" do
299
- subject.has_demand?(demand).should be_false
146
+ it "implements the required interface" do
147
+ json_string = serializer.serialize(solver)
148
+ problem_data = JSON.parse(json_string)
149
+ expected_demands = [
150
+ {"name" => "mysql", "constraint" => ">= 0.0.0"},
151
+ {"name" => "nginx", "constraint" => ">= 0.0.0"}
152
+ ]
153
+
154
+ problem_data["demands"].should =~ expected_demands
300
155
  end
301
156
  end
302
157
  end
158
+
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: solve
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.8.2
4
+ version: 1.0.0.rc1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Jamie Winsor
@@ -10,8 +10,36 @@ authors:
10
10
  autorequire:
11
11
  bindir: bin
12
12
  cert_chain: []
13
- date: 2013-11-29 00:00:00.000000000 Z
14
- dependencies: []
13
+ date: 2014-04-07 00:00:00.000000000 Z
14
+ dependencies:
15
+ - !ruby/object:Gem::Dependency
16
+ name: semverse
17
+ requirement: !ruby/object:Gem::Requirement
18
+ requirements:
19
+ - - "~>"
20
+ - !ruby/object:Gem::Version
21
+ version: '1.1'
22
+ type: :runtime
23
+ prerelease: false
24
+ version_requirements: !ruby/object:Gem::Requirement
25
+ requirements:
26
+ - - "~>"
27
+ - !ruby/object:Gem::Version
28
+ version: '1.1'
29
+ - !ruby/object:Gem::Dependency
30
+ name: dep_selector
31
+ requirement: !ruby/object:Gem::Requirement
32
+ requirements:
33
+ - - "~>"
34
+ - !ruby/object:Gem::Version
35
+ version: 1.0.0.alpha
36
+ type: :runtime
37
+ prerelease: false
38
+ version_requirements: !ruby/object:Gem::Requirement
39
+ requirements:
40
+ - - "~>"
41
+ - !ruby/object:Gem::Version
42
+ version: 1.0.0.alpha
15
43
  description: A Ruby version constraint solver
16
44
  email:
17
45
  - jamie@vialstudios.com
@@ -21,9 +49,8 @@ executables: []
21
49
  extensions: []
22
50
  extra_rdoc_files: []
23
51
  files:
24
- - .gitignore
25
- - .ruby-version
26
- - .travis.yml
52
+ - ".gitignore"
53
+ - ".travis.yml"
27
54
  - Gemfile
28
55
  - Guardfile
29
56
  - LICENSE
@@ -38,26 +65,19 @@ files:
38
65
  - lib/solve/gem_version.rb
39
66
  - lib/solve/graph.rb
40
67
  - lib/solve/solver.rb
41
- - lib/solve/solver/constraint_row.rb
42
- - lib/solve/solver/constraint_table.rb
43
68
  - lib/solve/solver/serializer.rb
44
- - lib/solve/solver/variable_row.rb
45
- - lib/solve/solver/variable_table.rb
46
- - lib/solve/tracers.rb
47
- - lib/solve/tracers/human_readable.rb
48
- - lib/solve/tracers/silent.rb
49
- - lib/solve/version.rb
50
69
  - solve.gemspec
70
+ - spec/acceptance/benchmark.rb
71
+ - spec/acceptance/large_graph_no_solution.rb
72
+ - spec/acceptance/opscode_ci_graph.rb
51
73
  - spec/acceptance/solutions_spec.rb
52
74
  - spec/spec_helper.rb
53
75
  - spec/unit/solve/artifact_spec.rb
54
- - spec/unit/solve/constraint_spec.rb
55
76
  - spec/unit/solve/demand_spec.rb
56
77
  - spec/unit/solve/dependency_spec.rb
57
78
  - spec/unit/solve/graph_spec.rb
58
79
  - spec/unit/solve/solver/serializer_spec.rb
59
80
  - spec/unit/solve/solver_spec.rb
60
- - spec/unit/solve/version_spec.rb
61
81
  - spec/unit/solve_spec.rb
62
82
  homepage: https://github.com/berkshelf/solve
63
83
  licenses:
@@ -69,30 +89,31 @@ require_paths:
69
89
  - lib
70
90
  required_ruby_version: !ruby/object:Gem::Requirement
71
91
  requirements:
72
- - - '>='
92
+ - - ">="
73
93
  - !ruby/object:Gem::Version
74
94
  version: 1.9.1
75
95
  required_rubygems_version: !ruby/object:Gem::Requirement
76
96
  requirements:
77
- - - '>='
97
+ - - ">"
78
98
  - !ruby/object:Gem::Version
79
- version: '0'
99
+ version: 1.3.1
80
100
  requirements: []
81
101
  rubyforge_project:
82
- rubygems_version: 2.0.7
102
+ rubygems_version: 2.2.2
83
103
  signing_key:
84
104
  specification_version: 4
85
105
  summary: A Ruby version constraint solver implementing Semantic Versioning 2.0.0-rc.1
86
106
  test_files:
107
+ - spec/acceptance/benchmark.rb
108
+ - spec/acceptance/large_graph_no_solution.rb
109
+ - spec/acceptance/opscode_ci_graph.rb
87
110
  - spec/acceptance/solutions_spec.rb
88
111
  - spec/spec_helper.rb
89
112
  - spec/unit/solve/artifact_spec.rb
90
- - spec/unit/solve/constraint_spec.rb
91
113
  - spec/unit/solve/demand_spec.rb
92
114
  - spec/unit/solve/dependency_spec.rb
93
115
  - spec/unit/solve/graph_spec.rb
94
116
  - spec/unit/solve/solver/serializer_spec.rb
95
117
  - spec/unit/solve/solver_spec.rb
96
- - spec/unit/solve/version_spec.rb
97
118
  - spec/unit/solve_spec.rb
98
119
  has_rdoc: