doubleshot 0.2.0-java → 0.3.0-java

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.
Files changed (47) hide show
  1. data/Doubleshot +16 -6
  2. data/README-OLD.textile +216 -0
  3. data/README.textile +38 -182
  4. data/lib/doubleshot.rb +100 -39
  5. data/lib/doubleshot/commands/gem.rb +15 -12
  6. data/lib/doubleshot/commands/test.rb +38 -5
  7. data/lib/doubleshot/configuration.rb +2 -2
  8. data/lib/doubleshot/dependencies/dependency.rb +1 -1
  9. data/lib/doubleshot/dependencies/gem_dependency.rb +2 -10
  10. data/lib/doubleshot/dependencies/jar_dependency.rb +12 -2
  11. data/lib/doubleshot/lockfile.rb +9 -6
  12. data/lib/doubleshot/pom.rb +15 -2
  13. data/lib/doubleshot/resolver.rb +1 -0
  14. data/lib/doubleshot/resolver/gem_resolver.rb +45 -0
  15. data/lib/doubleshot/resolver/gem_resolver/artifact.rb +146 -0
  16. data/lib/doubleshot/resolver/gem_resolver/demand.rb +57 -0
  17. data/lib/doubleshot/resolver/gem_resolver/dependency.rb +57 -0
  18. data/lib/doubleshot/resolver/gem_resolver/errors.rb +37 -0
  19. data/lib/doubleshot/resolver/gem_resolver/gem_source.rb +58 -0
  20. data/lib/doubleshot/resolver/gem_resolver/graph.rb +200 -0
  21. data/lib/doubleshot/resolver/gem_resolver/solver.rb +279 -0
  22. data/lib/doubleshot/resolver/gem_resolver/solver/constraint_row.rb +29 -0
  23. data/lib/doubleshot/resolver/gem_resolver/solver/constraint_table.rb +35 -0
  24. data/lib/doubleshot/resolver/gem_resolver/solver/variable_row.rb +47 -0
  25. data/lib/doubleshot/resolver/gem_resolver/solver/variable_table.rb +59 -0
  26. data/lib/doubleshot/resolver/gem_resolver/source.rb +36 -0
  27. data/lib/doubleshot/resolver/jar_resolver.rb +1 -3
  28. data/lib/ruby/gem/requirement.rb +9 -0
  29. data/target/doubleshot.jar +0 -0
  30. data/test/compiler_spec.rb +31 -3
  31. data/test/configuration_spec.rb +11 -3
  32. data/test/dependencies/gem_dependency_spec.rb +3 -17
  33. data/test/dependencies/jar_dependency_spec.rb +20 -0
  34. data/test/helper.rb +3 -1
  35. data/test/helpers/stub_source.rb +120 -0
  36. data/test/lockfile_spec.rb +9 -17
  37. data/test/pom_spec.rb +31 -1
  38. data/test/resolver/gem_resolver/artifact_spec.rb +106 -0
  39. data/test/resolver/gem_resolver/demand_spec.rb +70 -0
  40. data/test/resolver/gem_resolver/dependency_spec.rb +33 -0
  41. data/test/resolver/gem_resolver/gem_source_spec.rb +28 -0
  42. data/test/resolver/gem_resolver/graph_spec.rb +239 -0
  43. data/test/resolver/gem_resolver/solver_spec.rb +449 -0
  44. data/test/resolver/gem_resolver/source_spec.rb +18 -0
  45. data/test/resolver/gem_resolver_spec.rb +102 -0
  46. metadata +35 -73
  47. data/lib/doubleshot/jar.rb +0 -62
@@ -0,0 +1,70 @@
1
+ #!/usr/bin/env jruby
2
+
3
+ # encoding: utf-8
4
+
5
+ require_relative "../../helper"
6
+
7
+ describe Doubleshot::Resolver::GemResolver::Demand do
8
+ before do
9
+ @solver = MiniTest::Mock.new
10
+ end
11
+
12
+ describe "ClassMethods" do
13
+ describe "::new" do
14
+ it "accepts a string for the constraint parameter" do
15
+ Doubleshot::Resolver::GemResolver::Demand.new(@solver, "listen", "= 0.0.1")
16
+ .constraint.to_s.must_equal "= 0.0.1"
17
+ end
18
+
19
+ it "accepts a Gem::Requirement for the constraint parameter" do
20
+ constraint = Gem::Requirement.new("= 0.0.1")
21
+
22
+ Doubleshot::Resolver::GemResolver::Demand.new(@solver, "listen", constraint)
23
+ .constraint.must_equal constraint
24
+ end
25
+
26
+ describe "when no value for 'constraint' is given" do
27
+ it "uses a default of >= 0" do
28
+ Doubleshot::Resolver::GemResolver::Demand.new(@solver, "listen")
29
+ .constraint.to_s.must_equal ">= 0"
30
+ end
31
+ end
32
+ end
33
+ end
34
+
35
+ describe "#delete" do
36
+ describe "given the demand is not the member of a solver" do
37
+ it "returns nil" do
38
+ Doubleshot::Resolver::GemResolver::Demand.new(nil, "listen", "~> 1.0.0")
39
+ .delete.must_be_nil
40
+ end
41
+ end
42
+ end
43
+
44
+ describe "equality" do
45
+ before do
46
+ @demand = Doubleshot::Resolver::GemResolver::Demand.new(@solver, "listen", "1.0")
47
+ end
48
+
49
+ it "returns true when other is a Doubleshot::Resolver::GemResolver::Demand with the same name and constriant" do
50
+ other_demand = Doubleshot::Resolver::GemResolver::Demand.new(@solver, "listen", "1.0")
51
+ @demand.must_equal other_demand
52
+ end
53
+
54
+ it "returns false when other isn't a Solve::Demand" do
55
+ @demand.wont_equal "chicken"
56
+ end
57
+
58
+ it "returns false when other is a Solve::Demand with the same name but a different constraint" do
59
+ other_demand = Doubleshot::Resolver::GemResolver::Demand.new(@solver, "listen", "< 3.4.5")
60
+
61
+ @demand.wont_equal other_demand
62
+ end
63
+
64
+ it "returns false when other is a Solve::Demand with the same constraint but a different name" do
65
+ other_demand = Doubleshot::Resolver::GemResolver::Demand.new(@solver, "chicken", "1.0")
66
+
67
+ @demand.wont_equal other_demand
68
+ end
69
+ end
70
+ end
@@ -0,0 +1,33 @@
1
+ #!/usr/bin/env jruby
2
+
3
+ # encoding: utf-8
4
+
5
+ require_relative "../../helper"
6
+
7
+ describe Doubleshot::Resolver::GemResolver::Dependency do
8
+ before do
9
+ @dependency = Doubleshot::Resolver::GemResolver::Dependency.new(
10
+ Doubleshot::Resolver::GemResolver::Artifact.new(nil, "something_using_ntp", "1.0"),
11
+ "ntp")
12
+ end
13
+
14
+ describe "ClassMethods" do
15
+ describe "::new" do
16
+ describe "when no value for 'constraint' is given" do
17
+ it "uses a default of >= 0" do
18
+ @dependency.constraint.to_s.must_equal ">= 0"
19
+ end
20
+ end
21
+ end
22
+ end
23
+
24
+ describe "#eql?" do
25
+ it "returns true if the other object is an instance of Solve::Dependency with the same constraint and artifact" do
26
+ other = Doubleshot::Resolver::GemResolver::Dependency.new(
27
+ Doubleshot::Resolver::GemResolver::Artifact.new(nil, "something_using_ntp", "1.0"),
28
+ "ntp")
29
+
30
+ @dependency.must_equal other
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,28 @@
1
+ #!/usr/bin/env jruby
2
+
3
+ # encoding: utf-8
4
+
5
+ require_relative "../../helper"
6
+
7
+ describe Doubleshot::Resolver::GemResolver::Source do
8
+
9
+ before do
10
+ @source = Doubleshot::Resolver::GemResolver::Source.new Doubleshot::Resolver::GemResolver::DEFAULT_REPOSITORY
11
+ end
12
+
13
+ describe "versions" do
14
+ it "must return a list of available versions for a gem name" do
15
+ versions = @source.versions("rack")
16
+ versions.size.must_be :>, 10
17
+ versions.must_include Gem::Version.new "1.2.0"
18
+ end
19
+ end
20
+
21
+ describe "spec" do
22
+ it "must return a gemspec for a given gem name and version" do
23
+ gemspec = @source.spec "rack", "1.2.0"
24
+ gemspec.name.must_equal "rack"
25
+ gemspec.version.to_s.must_equal "1.2.0"
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,239 @@
1
+ #!/usr/bin/env jruby
2
+
3
+ # encoding: utf-8
4
+
5
+ require_relative "../../helper"
6
+
7
+ describe Doubleshot::Resolver::GemResolver::Graph do
8
+ before do
9
+ @graph = Doubleshot::Resolver::GemResolver::Graph.new
10
+ end
11
+
12
+ describe "ClassMethods" do
13
+ describe "::artifact_key" do
14
+ it "returns a symbol containing the name and version of the artifact" do
15
+ Doubleshot::Resolver::GemResolver::Graph::artifact_key("nginx", "1.2.3").must_equal :'nginx-1.2.3'
16
+ end
17
+ end
18
+
19
+ describe "::dependency_key" do
20
+ it "returns a symbol containing the name and constraint of the dependency" do
21
+ Doubleshot::Resolver::GemResolver::Graph::dependency_key("ntp", "= 2.3.4").must_equal :'ntp-= 2.3.4'
22
+ end
23
+ end
24
+ end
25
+
26
+ describe "#artifacts" do
27
+ describe "given a name and version argument" do
28
+ before do
29
+ @name = "nginx"
30
+ @version = "0.101.5"
31
+ end
32
+
33
+ describe "given the artifact of the given name and version does not exist" do
34
+ it "returns a Doubleshot::Resolver::GemResolver::Artifact" do
35
+ @graph.artifacts(@name, @version).must_be_kind_of Doubleshot::Resolver::GemResolver::Artifact
36
+ end
37
+
38
+ it "the artifact has the given name" do
39
+ @graph.artifacts(@name, @version).name.must_equal @name
40
+ end
41
+
42
+ it "the artifact has the given version" do
43
+ @graph.artifacts(@name, @version).version.to_s.must_equal @version
44
+ end
45
+
46
+ it "adds an artifact to the artifacts collection" do
47
+ @graph.artifacts(@name, @version)
48
+
49
+ @graph.artifacts.size.must_equal 1
50
+ end
51
+
52
+ it "the artifact added matches the given name" do
53
+ @graph.artifacts(@name, @version)
54
+
55
+ @graph.artifacts[0].name.must_equal @name
56
+ end
57
+
58
+ it "the artifact added matches the given version" do
59
+ @graph.artifacts(@name, @version)
60
+
61
+ @graph.artifacts[0].version.to_s.must_equal @version
62
+ end
63
+ end
64
+ end
65
+
66
+ describe "given no arguments" do
67
+ it "returns an array" do
68
+ @graph.artifacts.must_be_kind_of Array
69
+ end
70
+
71
+ it "returns an empty array if no artifacts have been accessed" do
72
+ @graph.artifacts.size.must_equal 0
73
+ end
74
+
75
+ it "returns an array containing an artifact if one was accessed" do
76
+ @graph.artifacts("nginx", "0.101.5")
77
+
78
+ @graph.artifacts.size.must_equal 1
79
+ end
80
+ end
81
+
82
+ describe "given an unexpected number of arguments" do
83
+ it "raises an ArgumentError if more than two are provided" do
84
+ -> { @graph.artifacts(1, 2, 3) }.must_raise ArgumentError, "Unexpected number of arguments. You gave: 3. Expected: 0 or 2."
85
+ end
86
+
87
+ it "raises an ArgumentError if one argument is provided" do
88
+ -> { @graph.artifacts(nil) }.must_raise ArgumentError, "Unexpected number of arguments. You gave: 1. Expected: 0 or 2."
89
+ end
90
+
91
+ it "raises an ArgumentError if one of the arguments provided is nil" do
92
+ -> { @graph.artifacts("nginx", nil) }.must_raise ArgumentError, 'A name and version must be specified. You gave: ["nginx", nil].'
93
+ end
94
+ end
95
+ end
96
+
97
+ describe "#get_artifact" do
98
+ before do
99
+ @graph.artifacts("nginx", "1.0.0")
100
+ end
101
+
102
+ it "returns an instance of artifact of the matching name and version" do
103
+ artifact = @graph.get_artifact("nginx", "1.0.0")
104
+
105
+ artifact.must_be_kind_of Doubleshot::Resolver::GemResolver::Artifact
106
+ artifact.name.must_equal "nginx"
107
+ artifact.version.to_s.must_equal "1.0.0"
108
+ end
109
+
110
+ describe "when an artifact of the given name is not in the collection of artifacts" do
111
+ it "returns nil" do
112
+ @graph.get_artifact("nothere", "1.0.0").must_be_nil
113
+ end
114
+ end
115
+ end
116
+
117
+ describe "#versions" do
118
+ before do
119
+ @graph.artifacts("nginx", "1.0.0")
120
+ @graph.artifacts("nginx", "2.0.0")
121
+ @graph.artifacts("nginx", "3.0.0")
122
+ @graph.artifacts("nginx", "4.0.0")
123
+ @graph.artifacts("nginx", "5.0.0")
124
+ @graph.artifacts("nginx", "4.0.0")
125
+ end
126
+
127
+ it "returns all the artifacts matching the given name" do
128
+ @graph.versions("nginx").size.must_equal 5
129
+ end
130
+
131
+ describe "given an optional constraint value" do
132
+ it "returns only the artifacts matching the given constraint value and name" do
133
+ @graph.versions("nginx", ">= 4.0.0").size.must_equal 2
134
+ end
135
+ end
136
+ end
137
+
138
+ describe "#add_artifact" do
139
+ before do
140
+ @artifact = Doubleshot::Resolver::GemResolver::Artifact.new(@graph, "nginx", "1.0.0")
141
+ end
142
+
143
+ it "adds a Doubleshot::Resolver::GemResolver::Artifact to the collection of artifacts" do
144
+ @graph.add_artifact @artifact
145
+
146
+ @graph.artifacts.must_include @artifact
147
+ @graph.artifacts.size.must_equal 1
148
+ end
149
+
150
+ it "should not add the same artifact twice to the collection" do
151
+ @graph.add_artifact @artifact
152
+ @graph.add_artifact @artifact
153
+
154
+ @graph.artifacts.size.must_equal 1
155
+ end
156
+ end
157
+
158
+ describe "#remove_artifact" do
159
+ before do
160
+ @artifact = Doubleshot::Resolver::GemResolver::Artifact.new(@graph, "nginx", "1.0.0")
161
+ end
162
+
163
+ describe "given the artifact is a member of the collection" do
164
+ before do
165
+ @graph.add_artifact @artifact
166
+ end
167
+
168
+ it "removes the Solve::Artifact from the collection of artifacts" do
169
+ @graph.remove_artifact(@artifact)
170
+
171
+ @graph.artifacts.size.must_equal 0
172
+ end
173
+
174
+ it "returns the removed Solve::Artifact" do
175
+ @graph.remove_artifact(@artifact).must_equal @artifact
176
+ end
177
+ end
178
+
179
+ describe "given the artifact is not a member of the collection" do
180
+ it "should return nil" do
181
+ @graph.remove_artifact(@artifact).must_be_nil
182
+ end
183
+ end
184
+ end
185
+
186
+ describe "#has_artifact?" do
187
+ before do
188
+ @artifact = Doubleshot::Resolver::GemResolver::Artifact.new(@graph, "nginx", "1.0.0")
189
+ end
190
+
191
+ it "returns true if the given Solve::Artifact is a member of the collection" do
192
+ @graph.add_artifact @artifact
193
+
194
+ @graph.has_artifact?(@artifact.name, @artifact.version).must_equal true
195
+ end
196
+
197
+ it "returns false if the given Solve::Artifact is not a member of the collection" do
198
+ @graph.has_artifact?(@artifact.name, @artifact.version).must_equal false
199
+ end
200
+ end
201
+
202
+ describe "eql?" do
203
+ before do
204
+ @graph = Doubleshot::Resolver::GemResolver::Graph.new
205
+ @graph.artifacts("A", "1.0.0").depends("B", "1.0.0")
206
+ @graph.artifacts("A", "2.0.0").depends("C", "1.0.0")
207
+ @graph
208
+ end
209
+
210
+ it "returns false if other isn't a Solve::Graph" do
211
+ @graph.wont_equal "chicken"
212
+ end
213
+
214
+ it "returns true if other is a Solve::Graph with the same artifacts and dependencies" do
215
+ other = Doubleshot::Resolver::GemResolver::Graph.new
216
+ other.artifacts("A", "1.0.0").depends("B", "1.0.0")
217
+ other.artifacts("A", "2.0.0").depends("C", "1.0.0")
218
+
219
+ @graph.must_equal other
220
+ end
221
+
222
+ it "returns false if the other is a Solve::Graph with the same artifacts but different dependencies" do
223
+ other = Doubleshot::Resolver::GemResolver::Graph.new
224
+ other.artifacts("A", "1.0.0")
225
+ other.artifacts("A", "2.0.0")
226
+
227
+ @graph.wont_equal other
228
+ end
229
+
230
+ it "returns false if the other is a Solve::Graph with the same dependencies but different artifacts" do
231
+ other = Doubleshot::Resolver::GemResolver::Graph.new
232
+ other.artifacts("A", "1.0.0").depends("B", "1.0.0")
233
+ other.artifacts("A", "2.0.0").depends("C", "1.0.0")
234
+ other.artifacts("B", "1.0.0")
235
+
236
+ @graph.wont_equal other
237
+ end
238
+ end
239
+ end
@@ -0,0 +1,449 @@
1
+ #!/usr/bin/env jruby
2
+
3
+ # encoding: utf-8
4
+
5
+ require_relative "../../helper"
6
+
7
+ describe Doubleshot::Resolver::GemResolver::Solver do
8
+ before do
9
+ @graph = Doubleshot::Resolver::GemResolver::Graph.new
10
+ @solver = Doubleshot::Resolver::GemResolver::Solver.new(@graph)
11
+ end
12
+
13
+ describe "ClassMethods" do
14
+ describe "::new" do
15
+
16
+ it "adds a demand for each element in the array" do
17
+ solver = Doubleshot::Resolver::GemResolver::Solver.new(@graph, ["nginx", "ntp"])
18
+ solver.demands.size.must_equal 2
19
+ end
20
+
21
+ describe "when demand_array is an array of array" do
22
+ it "creates a new demand with the name and constraint of each element in the array" do
23
+ solver = Doubleshot::Resolver::GemResolver::Solver.new(@graph, [["nginx", "= 1.2.3"], ["ntp", "= 1.0.0"]])
24
+
25
+ solver.demands[0].name.must_equal "nginx"
26
+ solver.demands[0].constraint.to_s.must_equal "= 1.2.3"
27
+ solver.demands[1].name.must_equal "ntp"
28
+ solver.demands[1].constraint.to_s.must_equal "= 1.0.0"
29
+ end
30
+ end
31
+
32
+ describe "when demand_array is an array of strings" do
33
+ it "creates a new demand with the name and a default constraint of each element in the array" do
34
+ solver = Doubleshot::Resolver::GemResolver::Solver.new(@graph, ["nginx", "ntp"])
35
+
36
+ solver.demands[0].name.must_equal "nginx"
37
+ solver.demands[0].constraint.to_s.must_equal ">= 0"
38
+ solver.demands[1].name.must_equal "ntp"
39
+ solver.demands[1].constraint.to_s.must_equal ">= 0"
40
+ end
41
+ end
42
+
43
+ describe "when demand_array is a mix between an array of arrays and an array of strings" do
44
+ let(:demand_array) { [["nginx", "= 1.2.3"], "ntp"] }
45
+
46
+ it "creates a new demand with the name and default constraint or constraint of each element in the array" do
47
+ solver = Doubleshot::Resolver::GemResolver::Solver.new(@graph, [["nginx", "= 1.2.3"], "ntp"])
48
+
49
+ solver.demands[0].name.must_equal "nginx"
50
+ solver.demands[0].constraint.to_s.must_equal "= 1.2.3"
51
+ solver.demands[1].name.must_equal "ntp"
52
+ solver.demands[1].constraint.to_s.must_equal ">= 0"
53
+ end
54
+ end
55
+ end
56
+
57
+ describe "::demand_key" do
58
+ it "returns a symbol containing the name and constraint of the demand" do
59
+ demand = Doubleshot::Resolver::GemResolver::Demand.new(MiniTest::Mock.new, "nginx", "= 1.2.3")
60
+ Doubleshot::Resolver::GemResolver::Solver::demand_key(demand).must_equal :'nginx-= 1.2.3'
61
+ end
62
+ end
63
+
64
+ describe "::satisfy_all" do
65
+ before do
66
+ @version_1 = Gem::Version.new("3.1.1")
67
+ @version_2 = Gem::Version.new("3.1.2")
68
+
69
+ @constraints = [
70
+ Gem::Requirement.new("> 3.0.0"),
71
+ Gem::Requirement.new("<= 3.1.2")
72
+ ]
73
+
74
+ @versions = [
75
+ Gem::Version.new("0.0.1"),
76
+ Gem::Version.new("0.1.0"),
77
+ Gem::Version.new("1.0.0"),
78
+ Gem::Version.new("2.0.0"),
79
+ Gem::Version.new("3.0.0"),
80
+ @version_1,
81
+ @version_2,
82
+ Gem::Version.new("4.1.0")
83
+ ].shuffle
84
+ end
85
+
86
+ it "returns all of the versions which satisfy all of the given constraints" do
87
+ solution = Doubleshot::Resolver::GemResolver::Solver::satisfy_all(@constraints, @versions)
88
+
89
+ solution.size.must_equal 2
90
+ solution.must_include @version_1
91
+ solution.must_include @version_2
92
+ end
93
+
94
+ it "does not return duplicate satisfied versions given multiple duplicate versions" do
95
+ solution = Doubleshot::Resolver::GemResolver::Solver::satisfy_all(@constraints, [@version_1, @version_1, @version_1])
96
+
97
+ solution.size.must_equal 1
98
+ solution.must_include @version_1
99
+ end
100
+ end
101
+
102
+ describe "::satisfy_best" do
103
+ before do
104
+ @versions = [
105
+ Gem::Version.new("0.0.1"),
106
+ Gem::Version.new("0.1.0"),
107
+ Gem::Version.new("1.0.0"),
108
+ Gem::Version.new("2.0.0"),
109
+ Gem::Version.new("3.0.0"),
110
+ Gem::Version.new("3.1.1"),
111
+ Gem::Version.new("3.1.2"),
112
+ Gem::Version.new("4.1.0")
113
+ ].shuffle
114
+ end
115
+
116
+ it "returns the best possible match for the given constraints" do
117
+ Doubleshot::Resolver::GemResolver::Solver::satisfy_best([">= 1.0.0", "< 4.1.0"], @versions)
118
+ .to_s.must_equal "3.1.2"
119
+ end
120
+
121
+ it "raises a NoSolutionError error given no version matches a constraint" do
122
+ -> {
123
+ Doubleshot::Resolver::GemResolver::Solver::satisfy_best(">= 5.0.0", [Gem::Version.new("4.1.0")])
124
+ }.must_raise Doubleshot::Resolver::GemResolver::Errors::NoSolutionError
125
+ end
126
+ end
127
+ end
128
+
129
+ describe "#resolve" do
130
+ before do
131
+ @graph.artifacts("nginx", "1.0.0")
132
+ @solver.demands("nginx", "= 1.0.0")
133
+ end
134
+
135
+ it "returns a solution in the form of a Hash" do
136
+ @solver.resolve.must_be_kind_of Hash
137
+ end
138
+ end
139
+
140
+ describe "#demands" do
141
+ describe "given a name and constraint argument" do
142
+ before do
143
+ @name = "nginx"
144
+ @constraint = "~> 0.101.5"
145
+ @demand = @solver.demands(@name, @constraint)
146
+ end
147
+
148
+ describe "given the artifact of the given name and constraint does not exist" do
149
+ it "returns a Solve::Demand" do
150
+ @demand.must_be_kind_of Doubleshot::Resolver::GemResolver::Demand
151
+ end
152
+
153
+ it "the artifact has the given name" do
154
+ @demand.name.must_equal @name
155
+ end
156
+
157
+ it "the artifact has the given constraint" do
158
+ @demand.constraint.to_s.must_equal @constraint
159
+ end
160
+
161
+ it "adds an artifact to the demands collection" do
162
+ @solver.demands.size.must_equal 1
163
+ end
164
+
165
+ it "the artifact added matches the given name" do
166
+ @solver.demands[0].name.must_equal @name
167
+ end
168
+
169
+ it "the artifact added matches the given constraint" do
170
+ @solver.demands[0].constraint.to_s.must_equal @constraint
171
+ end
172
+ end
173
+ end
174
+
175
+ describe "given only a name argument" do
176
+ it "returns a demand with a match all version constraint (>= 0)" do
177
+ @solver.demands("nginx").constraint.to_s.must_equal ">= 0"
178
+ end
179
+ end
180
+
181
+ describe "given no arguments" do
182
+ it "returns an array" do
183
+ @solver.demands.must_be_kind_of Array
184
+ end
185
+
186
+ it "returns an empty array if no demands have been accessed" do
187
+ @solver.demands.size.must_equal 0
188
+ end
189
+
190
+ it "returns an array containing a demand if one was accessed" do
191
+ @solver.demands("nginx", "~> 0.101.5")
192
+ @solver.demands.size.must_equal 1
193
+ end
194
+ end
195
+
196
+ describe "given an unexpected number of arguments" do
197
+ it "raises an ArgumentError if more than two are provided" do
198
+ -> {
199
+ @solver.demands(1, 2, 3)
200
+ }.must_raise ArgumentError, "Unexpected number of arguments. You gave: 3. Expected: 2 or less."
201
+ end
202
+
203
+ it "raises an ArgumentError if a name argument of nil is provided" do
204
+ -> {
205
+ @solver.demands(nil)
206
+ }.must_raise ArgumentError, "A name must be specified. You gave: [nil]."
207
+ end
208
+
209
+ it "raises an ArgumentError if a name and constraint argument are provided but name is nil" do
210
+ -> {
211
+ @solver.demands(nil, "= 1.0.0")
212
+ }.must_raise ArgumentError, 'A name must be specified. You gave: [nil, "= 1.0.0"].'
213
+ end
214
+ end
215
+ end
216
+
217
+ describe "#add_demand" do
218
+ before do
219
+ @demand = Doubleshot::Resolver::GemResolver::Demand.new(MiniTest::Mock, "ntp")
220
+ end
221
+
222
+ it "adds a Solve::Artifact to the collection of artifacts" do
223
+ @solver.add_demand @demand
224
+
225
+ @solver.demands.must_include @demand
226
+ @solver.demands.size.must_equal 1
227
+ end
228
+
229
+ it "should not add the same demand twice to the collection" do
230
+ @solver.add_demand @demand
231
+ @solver.add_demand @demand
232
+
233
+ @solver.demands.must_include @demand
234
+ @solver.demands.size.must_equal 1
235
+ end
236
+ end
237
+
238
+ describe "#remove_demand" do
239
+ before do
240
+ @demand = Doubleshot::Resolver::GemResolver::Demand.new(MiniTest::Mock, "ntp")
241
+ end
242
+
243
+ describe "given the demand is a member of the collection" do
244
+ before do
245
+ @solver.add_demand @demand
246
+ end
247
+
248
+ it "removes the Solve::Artifact from the collection of demands" do
249
+ @solver.remove_demand @demand
250
+ @solver.demands.size.must_equal 0
251
+ end
252
+
253
+ it "returns the removed Solve::Artifact" do
254
+ @solver.remove_demand(@demand).must_equal @demand
255
+ end
256
+ end
257
+
258
+ it "should return nil given the demand is not a member of the collection" do
259
+ @solver.remove_demand(@demand).must_be_nil
260
+ end
261
+ end
262
+
263
+ describe "#has_demand?" do
264
+ before do
265
+ @demand = Doubleshot::Resolver::GemResolver::Demand.new(MiniTest::Mock, "ntp")
266
+ end
267
+
268
+ it "returns true if the given Solve::Artifact is a member of the collection" do
269
+ @solver.add_demand @demand
270
+
271
+ @solver.has_demand?(@demand).must_equal true
272
+ end
273
+
274
+ it "returns false if the given Solve::Artifact is not a member of the collection" do
275
+ @solver.has_demand?(@demand).must_equal false
276
+ end
277
+ end
278
+
279
+ describe "solutions" do
280
+ it "chooses the correct artifact for the demands" do
281
+ @graph.artifacts("mysql", "2.0.0")
282
+ @graph.artifacts("mysql", "1.2.0")
283
+ @graph.artifacts("nginx", "1.0.0").depends("mysql", "= 1.2.0")
284
+
285
+ result = Doubleshot::Resolver::GemResolver::Solver.new(@graph, [['nginx', '= 1.0.0'], ['mysql']]).resolve
286
+
287
+ result.must_equal({"nginx" => "1.0.0", "mysql" => "1.2.0"})
288
+ end
289
+
290
+ it "chooses the best artifact for the demands" do
291
+ @graph.artifacts("mysql", "2.0.0")
292
+ @graph.artifacts("mysql", "1.2.0")
293
+ @graph.artifacts("nginx", "1.0.0").depends("mysql", ">= 1.2.0")
294
+
295
+ result = Doubleshot::Resolver::GemResolver::Solver.new(@graph, [['nginx', '= 1.0.0'], ['mysql']]).resolve
296
+
297
+ result.must_equal({"nginx" => "1.0.0", "mysql" => "2.0.0"})
298
+ end
299
+
300
+ it "raises NoSolutionError when a solution cannot be found" do
301
+ @graph.artifacts("mysql", "1.2.0")
302
+
303
+ -> {
304
+ Doubleshot::Resolver::GemResolver::Solver.new(@graph, ['mysql', '>= 2.0.0']).resolve
305
+ }.must_raise Doubleshot::Resolver::GemResolver::Errors::NoSolutionError
306
+ end
307
+
308
+ it "find the correct solution when backtracking in variables introduced via demands" do
309
+ @graph.artifacts("D", "1.2.0")
310
+ @graph.artifacts("D", "1.3.0")
311
+ @graph.artifacts("D", "1.4.0")
312
+ @graph.artifacts("D", "2.0.0")
313
+ @graph.artifacts("D", "2.1.0")
314
+
315
+ @graph.artifacts("C", "2.0.0").depends("D", "= 1.2.0")
316
+ @graph.artifacts("C", "2.1.0").depends("D", ">= 2.1.0")
317
+ @graph.artifacts("C", "2.2.0").depends("D", "> 2.0.0")
318
+
319
+ @graph.artifacts("B", "1.0.0").depends("D", "= 1.0.0")
320
+ @graph.artifacts("B", "1.1.0").depends("D", "= 1.0.0")
321
+ @graph.artifacts("B", "2.0.0").depends("D", ">= 1.3.0")
322
+ @graph.artifacts("B", "2.1.0").depends("D", ">= 2.0.0")
323
+
324
+ @graph.artifacts("A", "1.0.0").depends("B", "> 2.0.0")
325
+ @graph.artifacts("A", "1.0.0").depends("C", "= 2.1.0")
326
+ @graph.artifacts("A", "1.0.1").depends("B", "> 1.0.0")
327
+ @graph.artifacts("A", "1.0.1").depends("C", "= 2.1.0")
328
+ @graph.artifacts("A", "1.0.2").depends("B", "> 1.0.0")
329
+ @graph.artifacts("A", "1.0.2").depends("C", "= 2.0.0")
330
+
331
+ result = Doubleshot::Resolver::GemResolver::Solver.new(@graph, [['A', '~> 1.0.0'], ['D', ">= 2.0.0"]]).resolve
332
+
333
+ result.must_equal({"A" => "1.0.1",
334
+ "B" => "2.1.0",
335
+ "C" => "2.1.0",
336
+ "D" => "2.1.0"})
337
+ end
338
+
339
+ it "must correctly resolve when one resolution exists but it is not the latest" do
340
+ skip "pending: https://github.com/reset/solve/pull/7"
341
+
342
+ @graph.artifacts("get-the-old-one", "1.0")
343
+ .depends("locked-mid-1", ">= 0")
344
+ .depends("locked-mid-2", ">= 0")
345
+ @graph.artifacts("get-the-old-one", "0.5")
346
+
347
+ @graph.artifacts("locked-mid-1", "2.0").depends("old-bottom", "= 2.0")
348
+ @graph.artifacts("locked-mid-1", "1.3").depends("old-bottom", "= 0.5")
349
+ @graph.artifacts("locked-mid-1", "1.0")
350
+
351
+ @graph.artifacts("locked-mid-2", "2.0").depends("old-bottom", "= 2.1")
352
+ @graph.artifacts("locked-mid-2", "1.4").depends("old-bottom", "= 0.5")
353
+ @graph.artifacts("locked-mid-2", "1.0")
354
+
355
+ @graph.artifacts("old-bottom", "2.1")
356
+ @graph.artifacts("old-bottom", "2.0")
357
+ @graph.artifacts("old-bottom", "1.0")
358
+ @graph.artifacts("old-bottom", "0.5")
359
+
360
+ Doubleshot::Resolver::GemResolver::Solver.new(@graph, ["get-the-old-one"]).resolve.must_equal(
361
+ {
362
+ "get-the-old-one" => "1.0",
363
+ "locked-mid-1" => "1.3",
364
+ "locked-mid-2" => "1.4",
365
+ "old-bottom" => "0.5"
366
+ })
367
+ end
368
+
369
+ it "finds the correct solution when there is a circular dependency" do
370
+ @graph.artifacts("A", "1.0.0").depends("B", "1.0.0")
371
+ @graph.artifacts("B", "1.0.0").depends("C", "1.0.0")
372
+ @graph.artifacts("C", "1.0.0").depends("A", "1.0.0")
373
+
374
+ result = Doubleshot::Resolver::GemResolver::Solver.new(@graph, [["A", "1.0.0"]]).resolve
375
+
376
+ result.must_equal({"A" => "1.0.0",
377
+ "B" => "1.0.0",
378
+ "C" => "1.0.0"})
379
+ end
380
+
381
+ it "finds the correct solution when there is a p shaped dependency chain" do
382
+ @graph.artifacts("A", "1.0.0").depends("B", "1.0.0")
383
+ @graph.artifacts("B", "1.0.0").depends("C", "1.0.0")
384
+ @graph.artifacts("C", "1.0.0").depends("B", "1.0.0")
385
+
386
+ result = Doubleshot::Resolver::GemResolver::Solver.new(@graph, [["A", "1.0.0"]]).resolve
387
+
388
+ result.must_equal({"A" => "1.0.0",
389
+ "B" => "1.0.0",
390
+ "C" => "1.0.0"})
391
+ end
392
+
393
+ it "finds the correct solution when there is a diamond shaped dependency" do
394
+ @graph.artifacts("A", "1.0.0")
395
+ .depends("B", "1.0.0")
396
+ .depends("C", "1.0.0")
397
+ @graph.artifacts("B", "1.0.0")
398
+ .depends("D", "1.0.0")
399
+ @graph.artifacts("C", "1.0.0")
400
+ .depends("D", "1.0.0")
401
+ @graph.artifacts("D", "1.0.0")
402
+
403
+ result = Doubleshot::Resolver::GemResolver::Solver.new(@graph, [["A", "1.0.0"]]).resolve
404
+
405
+ result.must_equal({"A" => "1.0.0",
406
+ "B" => "1.0.0",
407
+ "C" => "1.0.0",
408
+ "D" => "1.0.0"})
409
+ end
410
+
411
+ it "gives an empty solution when there are no demands" do
412
+ result = Doubleshot::Resolver::GemResolver::Solver.new(@graph, []).resolve
413
+ result.must_equal({})
414
+ end
415
+
416
+ it "tries all combinations until it finds a solution" do
417
+ @graph.artifacts("A", "1.0.0").depends("B", "~> 1.0.0")
418
+ @graph.artifacts("A", "1.0.1").depends("B", "~> 1.0.0")
419
+ @graph.artifacts("A", "1.0.2").depends("B", "~> 1.0.0")
420
+
421
+ @graph.artifacts("B", "1.0.0").depends("C", "~> 1.0.0")
422
+ @graph.artifacts("B", "1.0.1").depends("C", "~> 1.0.0")
423
+ @graph.artifacts("B", "1.0.2").depends("C", "~> 1.0.0")
424
+
425
+ @graph.artifacts("C", "1.0.0").depends("D", "1.0.0")
426
+ @graph.artifacts("C", "1.0.1").depends("D", "1.0.0")
427
+ @graph.artifacts("C", "1.0.2").depends("D", "1.0.0")
428
+
429
+ # ensure we can't find a solution in the above
430
+ @graph.artifacts("D", "1.0.0").depends("A", "< 0.0.0")
431
+
432
+ # Add a solution to the graph that should be reached only after
433
+ # all of the others have been tried
434
+ # it must be circular to ensure that no other branch can find it
435
+ @graph.artifacts("A", "0.0.0").depends("B", "0.0.0")
436
+ @graph.artifacts("B", "0.0.0").depends("C", "0.0.0")
437
+ @graph.artifacts("C", "0.0.0").depends("D", "0.0.0")
438
+ @graph.artifacts("D", "0.0.0").depends("A", "0.0.0")
439
+
440
+ result = Doubleshot::Resolver::GemResolver::Solver.new(@graph, [["A"]]).resolve
441
+
442
+ result.must_equal({ "A" => "0.0.0",
443
+ "B" => "0.0.0",
444
+ "C" => "0.0.0",
445
+ "D" => "0.0.0"})
446
+
447
+ end
448
+ end
449
+ end