1require 'rubygems/test_case'
2require 'rubygems/dependency_resolver'
3
4class TestGemDependencyResolver < Gem::TestCase
5
6  def make_dep(name, *req)
7    Gem::Dependency.new(name, *req)
8  end
9
10  def set(*specs)
11    StaticSet.new(specs)
12  end
13
14  def assert_set(expected, actual)
15    exp = expected.sort_by { |s| s.full_name }
16    act = actual.map { |a| a.spec }.sort_by { |s| s.full_name }
17
18    assert_equal exp, act
19  end
20
21  def test_no_overlap_specificly
22    a = util_spec "a", '1'
23    b = util_spec "b", "1"
24
25    ad = make_dep "a", "= 1"
26    bd = make_dep "b", "= 1"
27
28    deps = [ad, bd]
29
30    s = set(a, b)
31
32    res = Gem::DependencyResolver.new(deps, s)
33
34    assert_set [a, b], res.resolve
35  end
36
37  def test_pulls_in_dependencies
38    a = util_spec "a", '1'
39    b = util_spec "b", "1", "c" => "= 1"
40    c = util_spec "c", "1"
41
42    ad = make_dep "a", "= 1"
43    bd = make_dep "b", "= 1"
44
45    deps = [ad, bd]
46
47    s = set(a, b, c)
48
49    res = Gem::DependencyResolver.new(deps, s)
50
51    assert_set [a, b, c], res.resolve
52  end
53
54  def test_picks_highest_version
55    a1 = util_spec "a", '1'
56    a2 = util_spec "a", '2'
57
58    s = set(a1, a2)
59
60    ad = make_dep "a"
61
62    res = Gem::DependencyResolver.new([ad], s)
63
64    assert_set [a2], res.resolve
65  end
66
67  def test_only_returns_spec_once
68    a1 = util_spec "a", "1", "c" => "= 1"
69    b1 = util_spec "b", "1", "c" => "= 1"
70
71    c1 = util_spec "c", "1"
72
73    ad = make_dep "a"
74    bd = make_dep "b"
75
76    s = set(a1, b1, c1)
77
78    res = Gem::DependencyResolver.new([ad, bd], s)
79
80    assert_set [a1, b1, c1], res.resolve
81  end
82
83  def test_picks_lower_version_when_needed
84    a1 = util_spec "a", "1", "c" => ">= 1"
85    b1 = util_spec "b", "1", "c" => "= 1"
86
87    c1 = util_spec "c", "1"
88    c2 = util_spec "c", "2"
89
90    ad = make_dep "a"
91    bd = make_dep "b"
92
93    s = set(a1, b1, c1, c2)
94
95    res = Gem::DependencyResolver.new([ad, bd], s)
96
97    assert_set [a1, b1, c1], res.resolve
98
99    cons = res.conflicts
100
101    assert_equal 1, cons.size
102    con = cons.first
103
104    assert_equal "c (= 1)", con.dependency.to_s
105    assert_equal "c-2", con.activated.full_name
106  end
107
108  def test_conflict_resolution_only_effects_correct_spec
109    a1 = util_spec "a", "1", "c" => ">= 1"
110    b1 = util_spec "b", "1", "d" => ">= 1"
111
112    d3 = util_spec "d", "3", "c" => "= 1"
113    d4 = util_spec "d", "4", "c" => "= 1"
114
115    c1 = util_spec "c", "1"
116    c2 = util_spec "c", "2"
117
118    ad = make_dep "a"
119    bd = make_dep "b"
120
121    s = set(a1, b1, d3, d4, c1, c2)
122
123    res = Gem::DependencyResolver.new([ad, bd], s)
124
125    assert_set [a1, b1, c1, d4], res.resolve
126
127    cons = res.conflicts
128
129    assert_equal 1, cons.size
130    con = cons.first
131
132    assert_equal "c (= 1)", con.dependency.to_s
133    assert_equal "c-2", con.activated.full_name
134  end
135
136  def test_raises_dependency_error
137    a1 = util_spec "a", "1", "c" => "= 1"
138    b1 = util_spec "b", "1", "c" => "= 2"
139
140    c1 = util_spec "c", "1"
141    c2 = util_spec "c", "2"
142
143    ad = make_dep "a"
144    bd = make_dep "b"
145
146    s = set(a1, b1, c1, c2)
147
148    r = Gem::DependencyResolver.new([ad, bd], s)
149
150    e = assert_raises Gem::DependencyResolutionError do
151      r.resolve
152    end
153
154    assert_equal "unable to resolve conflicting dependencies 'c (= 2)' and 'c (= 1)'", e.message
155
156    deps = [make_dep("c", "= 2"), make_dep("c", "= 1")]
157    assert_equal deps, e.conflicting_dependencies
158
159    con = e.conflict
160
161    act = con.activated
162    assert_equal "c-1", act.spec.full_name
163
164    parent = act.parent
165    assert_equal "a-1", parent.spec.full_name
166
167    act = con.requester
168    assert_equal "b-1", act.spec.full_name
169  end
170
171  def test_raises_when_a_gem_is_missing
172    ad = make_dep "a"
173
174    r = Gem::DependencyResolver.new([ad], set)
175
176    e = assert_raises Gem::UnsatisfiableDepedencyError do
177      r.resolve
178    end
179
180    assert_equal "unable to find any gem matching dependency 'a (>= 0)'", e.message
181
182    assert_equal "a (>= 0)", e.dependency.to_s
183  end
184
185  def test_raises_when_a_gem_version_is_missing
186    a1 = util_spec "a", "1"
187
188    ad = make_dep "a", "= 3"
189
190    r = Gem::DependencyResolver.new([ad], set(a1))
191
192    e = assert_raises Gem::UnsatisfiableDepedencyError do
193      r.resolve
194    end
195
196    assert_equal "a (= 3)", e.dependency.to_s
197  end
198
199  def test_raises_when_possibles_are_exhausted
200    a1 = util_spec "a", "1", "c" => ">= 2"
201    b1 = util_spec "b", "1", "c" => "= 1"
202
203    c1 = util_spec "c", "1"
204    c2 = util_spec "c", "2"
205    c3 = util_spec "c", "3"
206
207    s = set(a1, b1, c1, c2, c3)
208
209    ad = make_dep "a"
210    bd = make_dep "b"
211
212    r = Gem::DependencyResolver.new([ad, bd], s)
213
214    e = assert_raises Gem::ImpossibleDependenciesError do
215      r.resolve
216    end
217
218    assert_equal "detected 1 conflict with dependency 'c (>= 2)'", e.message
219
220    assert_equal "c (>= 2)", e.dependency.to_s
221
222    s, con = e.conflicts[0]
223    assert_equal "c-3", s.full_name
224    assert_equal "c (= 1)", con.dependency.to_s
225    assert_equal "b-1", con.requester.full_name
226  end
227
228  def test_keeps_resolving_after_seeing_satisfied_dep
229    a1 = util_spec "a", "1", "b" => "= 1", "c" => "= 1"
230    b1 = util_spec "b", "1"
231    c1 = util_spec "c", "1"
232
233    ad = make_dep "a"
234    bd = make_dep "b"
235
236    s = set(a1, b1, c1)
237
238    r = Gem::DependencyResolver.new([ad, bd], s)
239
240    assert_set [a1, b1, c1], r.resolve
241  end
242
243  def test_common_rack_activation_scenario
244    rack100 = util_spec "rack", "1.0.0"
245    rack101 = util_spec "rack", "1.0.1"
246
247    lib1 =    util_spec "lib", "1", "rack" => ">= 1.0.1"
248
249    rails =   util_spec "rails", "3", "actionpack" => "= 3"
250    ap =      util_spec "actionpack", "3", "rack" => ">= 1.0.0"
251
252    d1 = make_dep "rails"
253    d2 = make_dep "lib"
254
255    s = set(lib1, rails, ap, rack100, rack101)
256
257    r = Gem::DependencyResolver.new([d1, d2], s)
258
259    assert_set [rails, ap, rack101, lib1], r.resolve
260
261    # check it with the deps reverse too
262
263    r = Gem::DependencyResolver.new([d2, d1], s)
264
265    assert_set [lib1, rack101, rails, ap], r.resolve
266  end
267
268  def test_backtracks_to_the_first_conflict
269    a1 = util_spec "a", "1"
270    a2 = util_spec "a", "2"
271    a3 = util_spec "a", "3"
272    a4 = util_spec "a", "4"
273
274    d1 = make_dep "a"
275    d2 = make_dep "a", ">= 2"
276    d3 = make_dep "a", "= 1"
277
278    s = set(a1, a2, a3, a4)
279
280    r = Gem::DependencyResolver.new([d1, d2, d3], s)
281
282    assert_raises Gem::ImpossibleDependenciesError do
283      r.resolve
284    end
285  end
286
287  # actionmailer 2.3.4
288  # activemerchant 1.5.0
289  # activesupport 2.3.5, 2.3.4
290  # Activemerchant needs activesupport >= 2.3.2. When you require activemerchant, it will activate the latest version that meets that requirement which is 2.3.5. Actionmailer on the other hand needs activesupport = 2.3.4. When rubygems tries to activate activesupport 2.3.4, it will raise an error.
291
292
293  def test_simple_activesupport_problem
294    sup1  = util_spec "activesupport", "2.3.4"
295    sup2  = util_spec "activesupport", "2.3.5"
296
297    merch = util_spec "activemerchant", "1.5.0", "activesupport" => ">= 2.3.2"
298    mail =  util_spec "actionmailer", "2.3.4", "activesupport" => "= 2.3.4"
299
300    s = set(mail, merch, sup1, sup2)
301
302    d1 = make_dep "activemerchant"
303    d2 = make_dep "actionmailer"
304
305    r = Gem::DependencyResolver.new([d1, d2], s)
306
307    assert_set [merch, mail, sup1], r.resolve
308  end
309
310  def test_second_level_backout
311    b1 = new_spec "b", "1", { "c" => ">= 1" }, "lib/b.rb"
312    b2 = new_spec "b", "2", { "c" => ">= 2" }, "lib/b.rb"
313    c1 = new_spec "c", "1"
314    c2 = new_spec "c", "2"
315    d1 = new_spec "d", "1", { "c" => "< 2" },  "lib/d.rb"
316    d2 = new_spec "d", "2", { "c" => "< 2" },  "lib/d.rb"
317
318    s = set(b1, b2, c1, c2, d1, d2)
319
320    p1 = make_dep "b", "> 0"
321    p2 = make_dep "d", "> 0"
322
323    r = Gem::DependencyResolver.new([p1, p2], s)
324
325    assert_set [b1, c1, d2], r.resolve
326  end
327end
328