solve 0.2.1
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.
- data/.gitignore +17 -0
- data/.rbenv-version +1 -0
- data/Gemfile +3 -0
- data/Guardfile +17 -0
- data/LICENSE +201 -0
- data/README.md +43 -0
- data/Thorfile +31 -0
- data/lib/solve.rb +47 -0
- data/lib/solve/artifact.rb +104 -0
- data/lib/solve/constraint.rb +73 -0
- data/lib/solve/core_ext.rb +3 -0
- data/lib/solve/core_ext/kernel.rb +33 -0
- data/lib/solve/demand.rb +38 -0
- data/lib/solve/dependency.rb +40 -0
- data/lib/solve/errors.rb +31 -0
- data/lib/solve/gem_version.rb +3 -0
- data/lib/solve/graph.rb +152 -0
- data/lib/solve/version.rb +47 -0
- data/solve.gemspec +32 -0
- data/spec/acceptance/solutions_spec.rb +25 -0
- data/spec/spec_helper.rb +22 -0
- data/spec/unit/solve/artifact_spec.rb +128 -0
- data/spec/unit/solve/constraint_spec.rb +201 -0
- data/spec/unit/solve/demand_spec.rb +57 -0
- data/spec/unit/solve/dependency_spec.rb +49 -0
- data/spec/unit/solve/graph_spec.rb +273 -0
- data/spec/unit/solve/version_spec.rb +11 -0
- data/spec/unit/solve_spec.rb +19 -0
- metadata +278 -0
@@ -0,0 +1,25 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe "Solutions" do
|
4
|
+
it "chooses the best artifact for the demands" do
|
5
|
+
graph = Solve::Graph.new
|
6
|
+
graph.artifacts("mysql", "2.0.0")
|
7
|
+
graph.artifacts("mysql", "1.2.0")
|
8
|
+
graph.artifacts("nginx", "1.0.0").depends("mysql", "= 1.2.0")
|
9
|
+
graph.demands('nginx')
|
10
|
+
graph.demands('mysql')
|
11
|
+
|
12
|
+
Solve.it(graph).should eql("nginx" => "1.0.0", "mysql" => "1.2.0")
|
13
|
+
end
|
14
|
+
|
15
|
+
it "raises NoSolutionError when a solution cannot be found" do
|
16
|
+
graph = Solve::Graph.new
|
17
|
+
graph.artifacts("mysql", "1.2.0")
|
18
|
+
|
19
|
+
graph.demands("mysql", ">= 2.0.0")
|
20
|
+
|
21
|
+
lambda {
|
22
|
+
Solve.it!(graph)
|
23
|
+
}.should raise_error(Solve::NoSolutionError)
|
24
|
+
end
|
25
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,22 @@
|
|
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.treat_symbols_as_metadata_keys_with_true_values = true
|
15
|
+
config.filter_run focus: true
|
16
|
+
config.run_all_when_everything_filtered = true
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
Spork.each_run do
|
21
|
+
require 'solve'
|
22
|
+
end
|
@@ -0,0 +1,128 @@
|
|
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 "#dependencies" do
|
17
|
+
context "given a name and constraint argument" do
|
18
|
+
let(:name) { "nginx" }
|
19
|
+
let(:constraint) { "~> 0.101.5" }
|
20
|
+
|
21
|
+
context "given the dependency of the given name and constraint does not exist" do
|
22
|
+
it "returns a Solve::Artifact" do
|
23
|
+
subject.dependencies(name, constraint).should be_a(Solve::Dependency)
|
24
|
+
end
|
25
|
+
|
26
|
+
it "the dependency has the given name" do
|
27
|
+
subject.dependencies(name, constraint).name.should eql(name)
|
28
|
+
end
|
29
|
+
|
30
|
+
it "the dependency has the given constraint" do
|
31
|
+
subject.dependencies(name, constraint).constraint.to_s.should eql(constraint)
|
32
|
+
end
|
33
|
+
|
34
|
+
it "adds an dependency to the dependency collection" do
|
35
|
+
subject.dependencies(name, constraint)
|
36
|
+
|
37
|
+
subject.dependencies.should have(1).item
|
38
|
+
end
|
39
|
+
|
40
|
+
it "the dependency added matches the given name" do
|
41
|
+
subject.dependencies(name, constraint)
|
42
|
+
|
43
|
+
subject.dependencies[0].name.should eql(name)
|
44
|
+
end
|
45
|
+
|
46
|
+
it "the dependency added matches the given constraint" do
|
47
|
+
subject.dependencies(name, constraint)
|
48
|
+
|
49
|
+
subject.dependencies[0].constraint.to_s.should eql(constraint)
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
context "given no arguments" do
|
55
|
+
it "returns an array" do
|
56
|
+
subject.dependencies.should be_a(Array)
|
57
|
+
end
|
58
|
+
|
59
|
+
it "returns an empty array if no dependencies have been accessed" do
|
60
|
+
subject.dependencies.should have(0).items
|
61
|
+
end
|
62
|
+
|
63
|
+
it "returns an array containing an dependency if one was accessed" do
|
64
|
+
subject.dependencies("nginx", "~> 0.101.5")
|
65
|
+
|
66
|
+
subject.dependencies.should have(1).item
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
context "given only a name argument" do
|
71
|
+
it "returns an array containing a match all constraint (>= 0.0.0)" do
|
72
|
+
subject.dependencies("nginx").constraint.to_s.should eql(">= 0.0.0")
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
context "given an unexpected number of arguments" do
|
77
|
+
it "raises an ArgumentError if more than two are provided" do
|
78
|
+
lambda {
|
79
|
+
subject.dependencies(1, 2, 3)
|
80
|
+
}.should raise_error(ArgumentError, "Unexpected number of arguments. You gave: 3. Expected: 2 or less.")
|
81
|
+
end
|
82
|
+
|
83
|
+
it "raises an ArgumentError if a name argument is provided but it is nil" do
|
84
|
+
lambda {
|
85
|
+
subject.dependencies(nil)
|
86
|
+
}.should raise_error(ArgumentError, "A name must be specified. You gave: [nil].")
|
87
|
+
end
|
88
|
+
|
89
|
+
it "raises an ArgumentError if a name and constraint argument are provided but the name is nil" do
|
90
|
+
lambda {
|
91
|
+
subject.dependencies(nil, "= 1.0.0")
|
92
|
+
}.should raise_error(ArgumentError, 'A name must be specified. You gave: [nil, "= 1.0.0"].')
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
describe "#delete" do
|
98
|
+
context "given the artifact is a member of a graph" do
|
99
|
+
subject { Solve::Artifact.new(graph, name, version) }
|
100
|
+
|
101
|
+
before(:each) do
|
102
|
+
graph.should_receive(:remove_artifact).with(subject).and_return(subject)
|
103
|
+
end
|
104
|
+
|
105
|
+
it "notifies the graph that the artifact should be removed" do
|
106
|
+
subject.delete
|
107
|
+
end
|
108
|
+
|
109
|
+
it "sets the graph attribute to nil" do
|
110
|
+
subject.delete
|
111
|
+
|
112
|
+
subject.graph.should be_nil
|
113
|
+
end
|
114
|
+
|
115
|
+
it "returns the instance of artifact deleted from the graph" do
|
116
|
+
subject.delete.should eql(subject)
|
117
|
+
end
|
118
|
+
end
|
119
|
+
|
120
|
+
context "given the artifact is not the member of a graph" do
|
121
|
+
subject { Solve::Artifact.new(nil, name, version) }
|
122
|
+
|
123
|
+
it "returns nil" do
|
124
|
+
subject.delete.should be_nil
|
125
|
+
end
|
126
|
+
end
|
127
|
+
end
|
128
|
+
end
|
@@ -0,0 +1,201 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Solve::Constraint do
|
4
|
+
let(:valid_string) { ">= 0.0.0" }
|
5
|
+
let(:invalid_string) { "x23u7089213.*" }
|
6
|
+
|
7
|
+
describe "ClassMethods" do
|
8
|
+
subject { Solve::Constraint }
|
9
|
+
|
10
|
+
describe "#parse" do
|
11
|
+
it "returns an array containing two items" do
|
12
|
+
subject.parse(valid_string).should have(2).items
|
13
|
+
end
|
14
|
+
|
15
|
+
it "returns the operator at index 0" do
|
16
|
+
subject.parse(valid_string)[0].should eql(">=")
|
17
|
+
end
|
18
|
+
|
19
|
+
it "returns the version string at index 1" do
|
20
|
+
subject.parse(valid_string)[1].should eql("0.0.0")
|
21
|
+
end
|
22
|
+
|
23
|
+
context "given a string that does not match the Constraint REGEXP" do
|
24
|
+
it "returns nil" do
|
25
|
+
subject.parse(invalid_string).should be_nil
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
context "given a string that does not contain an operator" do
|
30
|
+
it "returns a constraint constraint with a default operator (=)" do
|
31
|
+
subject.parse("1.0.0")[0].should eql("=")
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
describe "#initialize" do
|
37
|
+
it "returns a new instance of Constraint" do
|
38
|
+
subject.new(valid_string).should be_a(Solve::Constraint)
|
39
|
+
end
|
40
|
+
|
41
|
+
it "assigns the parsed operator to the operator attribute" do
|
42
|
+
subject.new(valid_string).operator.should eql(">=")
|
43
|
+
end
|
44
|
+
|
45
|
+
it "assigns the parsed version string as an instance of Version to the version attribute" do
|
46
|
+
result = subject.new(valid_string)
|
47
|
+
|
48
|
+
result.version.should be_a(Solve::Version)
|
49
|
+
result.version.to_s.should eql("0.0.0")
|
50
|
+
end
|
51
|
+
|
52
|
+
context "given a string that does not match the Constraint REGEXP" do
|
53
|
+
it "raises an InvalidConstraintFormat error" do
|
54
|
+
lambda {
|
55
|
+
subject.new(invalid_string)
|
56
|
+
}.should raise_error(Solve::InvalidConstraintFormat)
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
context "given a nil value" do
|
61
|
+
it "raises an InvalidConstraintFormat error" do
|
62
|
+
lambda {
|
63
|
+
subject.new(nil)
|
64
|
+
}.should raise_error(Solve::InvalidConstraintFormat)
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
describe "#satisfies?" do
|
71
|
+
subject { Solve::Constraint.new("= 1.0.0") }
|
72
|
+
|
73
|
+
it "accepts a String for version" do
|
74
|
+
subject.satisfies?("1.0.0").should be_true
|
75
|
+
end
|
76
|
+
|
77
|
+
it "accepts a Version for version" do
|
78
|
+
subject.satisfies?(Solve::Version.new("1.0.0")).should be_true
|
79
|
+
end
|
80
|
+
|
81
|
+
context "strictly greater than (>)" do
|
82
|
+
subject { Solve::Constraint.new("> 1.0.0") }
|
83
|
+
|
84
|
+
it "returns true if the given version would satisfy the constraint" do
|
85
|
+
subject.satisfies?("2.0.0").should be_true
|
86
|
+
end
|
87
|
+
|
88
|
+
it "returns false if the given version would not satisfy the constraint" do
|
89
|
+
subject.satisfies?("1.0.0").should be_false
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
context "strictly less than (<)" do
|
94
|
+
subject { Solve::Constraint.new("< 1.0.0") }
|
95
|
+
|
96
|
+
it "returns true if the given version would satisfy the constraint" do
|
97
|
+
subject.satisfies?("0.1.0").should be_true
|
98
|
+
end
|
99
|
+
|
100
|
+
it "returns false if the given version would not satisfy the constraint" do
|
101
|
+
subject.satisfies?("1.0.0").should be_false
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
105
|
+
context "strictly equal to (=)" do
|
106
|
+
subject { Solve::Constraint.new("= 1.0.0") }
|
107
|
+
|
108
|
+
it "returns true if the given version would satisfy the constraint" do
|
109
|
+
subject.satisfies?("1.0.0").should be_true
|
110
|
+
end
|
111
|
+
|
112
|
+
it "returns false if the given version would not satisfy the constraint" do
|
113
|
+
subject.satisfies?("1.0.1").should be_false
|
114
|
+
end
|
115
|
+
end
|
116
|
+
|
117
|
+
context "greater than or equal to (>=)" do
|
118
|
+
subject { Solve::Constraint.new(">= 1.0.0") }
|
119
|
+
|
120
|
+
it "returns true if the given version is greater than the version constraint" do
|
121
|
+
subject.satisfies?("2.0.0").should be_true
|
122
|
+
end
|
123
|
+
|
124
|
+
it "returns true if the given version is equal to the version constraint" do
|
125
|
+
subject.satisfies?("1.0.0").should be_true
|
126
|
+
end
|
127
|
+
|
128
|
+
it "returns false if the given version is less than the version constraint" do
|
129
|
+
subject.satisfies?("0.9.0").should be_false
|
130
|
+
end
|
131
|
+
end
|
132
|
+
|
133
|
+
context "greater than or equal to (<=)" do
|
134
|
+
subject { Solve::Constraint.new("<= 1.0.0") }
|
135
|
+
|
136
|
+
it "returns true if the given version is less than the version constraint" do
|
137
|
+
subject.satisfies?("0.9.0").should be_true
|
138
|
+
end
|
139
|
+
|
140
|
+
it "returns true if the given version is equal to the version constraint" do
|
141
|
+
subject.satisfies?("1.0.0").should be_true
|
142
|
+
end
|
143
|
+
|
144
|
+
it "returns false if the given version is less than the version constraint" do
|
145
|
+
subject.satisfies?("1.0.1").should be_false
|
146
|
+
end
|
147
|
+
end
|
148
|
+
|
149
|
+
context "greater than or equal to (~>)" do
|
150
|
+
subject { Solve::Constraint.new("~> 1.0.0") }
|
151
|
+
|
152
|
+
it "returns true if the given version is equal to the version constraint" do
|
153
|
+
subject.satisfies?("1.0.0").should be_true
|
154
|
+
end
|
155
|
+
|
156
|
+
context "when the last value in the constraint is for patch" do
|
157
|
+
subject { Solve::Constraint.new("~> 1.0.1") }
|
158
|
+
|
159
|
+
it "returns true if the patch level is greater than the constraint's" do
|
160
|
+
subject.satisfies?("1.0.2").should be_true
|
161
|
+
end
|
162
|
+
|
163
|
+
it "returns true if the patch level is equal to the constraint's" do
|
164
|
+
subject.satisfies?("1.0.1").should be_true
|
165
|
+
end
|
166
|
+
|
167
|
+
it "returns false if the patch level is less than the constraint's" do
|
168
|
+
subject.satisfies?("1.0.0").should be_false
|
169
|
+
end
|
170
|
+
|
171
|
+
it "returns false if the given version is less than the constraint's" do
|
172
|
+
subject.satisfies?("0.9.0").should be_false
|
173
|
+
end
|
174
|
+
end
|
175
|
+
|
176
|
+
context "when the last value in the constraint is for minor" do
|
177
|
+
subject { Solve::Constraint.new("~> 1.1") }
|
178
|
+
|
179
|
+
it "returns true if the minor level is greater than the constraint's" do
|
180
|
+
subject.satisfies?("1.2").should be_true
|
181
|
+
end
|
182
|
+
|
183
|
+
it "returns true if the minor level is equal to the constraint's" do
|
184
|
+
subject.satisfies?("1.1").should be_true
|
185
|
+
end
|
186
|
+
|
187
|
+
it "returns true if a patch level is set but the minor level is equal to or greater than the constraint's" do
|
188
|
+
subject.satisfies?("1.2.8").should be_true
|
189
|
+
end
|
190
|
+
|
191
|
+
it "returns false if the patch level is less than the constraint's" do
|
192
|
+
subject.satisfies?("1.0.1").should be_false
|
193
|
+
end
|
194
|
+
|
195
|
+
it "returns false if the major level is greater than the constraint's" do
|
196
|
+
subject.satisfies?("2.0.0").should be_false
|
197
|
+
end
|
198
|
+
end
|
199
|
+
end
|
200
|
+
end
|
201
|
+
end
|
@@ -0,0 +1,57 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Solve::Demand do
|
4
|
+
let(:graph) { double('graph') }
|
5
|
+
let(:name) { "league" }
|
6
|
+
|
7
|
+
describe "ClassMethods" do
|
8
|
+
subject { Solve::Demand }
|
9
|
+
|
10
|
+
describe "::initialize" do
|
11
|
+
it "accepts a string for the constraint parameter" do
|
12
|
+
subject.new(graph, name, "= 0.0.1").constraint.to_s.should eql("= 0.0.1")
|
13
|
+
end
|
14
|
+
|
15
|
+
it "accepts a Solve::Constraint for the constraint parameter" do
|
16
|
+
constraint = Solve::Constraint.new("= 0.0.1")
|
17
|
+
|
18
|
+
subject.new(graph, name, constraint).constraint.should eql(constraint)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
let(:constraint) { "~> 1.0.0" }
|
24
|
+
subject { Solve::Demand.new(graph, name, constraint) }
|
25
|
+
|
26
|
+
describe "#delete" do
|
27
|
+
context "given the artifact is a member of a graph" do
|
28
|
+
subject { Solve::Demand.new(graph, name, constraint) }
|
29
|
+
|
30
|
+
before(:each) do
|
31
|
+
graph.should_receive(:remove_demand).with(subject).and_return(subject)
|
32
|
+
end
|
33
|
+
|
34
|
+
it "notifies the graph that the artifact should be removed" do
|
35
|
+
subject.delete
|
36
|
+
end
|
37
|
+
|
38
|
+
it "sets the graph attribute to nil" do
|
39
|
+
subject.delete
|
40
|
+
|
41
|
+
subject.graph.should be_nil
|
42
|
+
end
|
43
|
+
|
44
|
+
it "returns the instance of artifact deleted from the graph" do
|
45
|
+
subject.delete.should eql(subject)
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
context "given the artifact is not the member of a graph" do
|
50
|
+
subject { Solve::Demand.new(nil, name, constraint) }
|
51
|
+
|
52
|
+
it "returns nil" do
|
53
|
+
subject.delete.should be_nil
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
@@ -0,0 +1,49 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Solve::Dependency do
|
4
|
+
let(:artifact) { double('artifact') }
|
5
|
+
let(:name) { 'nginx' }
|
6
|
+
let(:constraint) { "~> 0.0.1" }
|
7
|
+
|
8
|
+
subject { Solve::Dependency.new(artifact, name, constraint) }
|
9
|
+
|
10
|
+
describe "#delete" do
|
11
|
+
context "given the dependency is a member of an artifact" do
|
12
|
+
subject { Solve::Dependency.new(artifact, name, constraint) }
|
13
|
+
|
14
|
+
before(:each) do
|
15
|
+
artifact.should_receive(:remove_dependency).with(subject).and_return(subject)
|
16
|
+
end
|
17
|
+
|
18
|
+
it "notifies the artifact that the dependency should be removed" do
|
19
|
+
subject.delete
|
20
|
+
end
|
21
|
+
|
22
|
+
it "sets the artifact attribute to nil" do
|
23
|
+
subject.delete
|
24
|
+
|
25
|
+
subject.artifact.should be_nil
|
26
|
+
end
|
27
|
+
|
28
|
+
it "returns the instance of dependency deleted from the artifact" do
|
29
|
+
subject.delete.should eql(subject)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
context "given the dependency is not the member of an artifact" do
|
34
|
+
subject { Solve::Dependency.new(nil, name, constraint) }
|
35
|
+
|
36
|
+
it "returns nil" do
|
37
|
+
subject.delete.should be_nil
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
describe "#eql?" do
|
43
|
+
it "returns true if the other object is an instance of Solve::Dependency with the same constraint and artifact" do
|
44
|
+
other = Solve::Dependency.new(artifact, name, constraint)
|
45
|
+
|
46
|
+
subject.should eql(other)
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|