1Code.require_file("test_helper.exs", __DIR__)
2
3defmodule VersionTest do
4  use ExUnit.Case, async: true
5
6  doctest Version
7
8  alias Version.Parser
9
10  test "compare/2 with valid versions" do
11    assert Version.compare("1.0.1", "1.0.0") == :gt
12    assert Version.compare("1.1.0", "1.0.1") == :gt
13    assert Version.compare("2.1.1", "1.2.2") == :gt
14    assert Version.compare("1.0.0", "1.0.0-dev") == :gt
15    assert Version.compare("1.2.3-dev", "0.1.2") == :gt
16    assert Version.compare("1.0.0-a.b", "1.0.0-a") == :gt
17    assert Version.compare("1.0.0-b", "1.0.0-a.b") == :gt
18    assert Version.compare("1.0.0-a", "1.0.0-0") == :gt
19    assert Version.compare("1.0.0-a.b", "1.0.0-a.a") == :gt
20
21    assert Version.compare("1.0.0", "1.0.1") == :lt
22    assert Version.compare("1.0.1", "1.1.0") == :lt
23    assert Version.compare("1.2.2", "2.1.1") == :lt
24    assert Version.compare("1.0.0-dev", "1.0.0") == :lt
25    assert Version.compare("0.1.2", "1.2.3-dev") == :lt
26    assert Version.compare("1.0.0-a", "1.0.0-a.b") == :lt
27    assert Version.compare("1.0.0-a.b", "1.0.0-b") == :lt
28    assert Version.compare("1.0.0-0", "1.0.0-a") == :lt
29    assert Version.compare("1.0.0-a.a", "1.0.0-a.b") == :lt
30
31    assert Version.compare("1.0.0", "1.0.0") == :eq
32    assert Version.compare("1.0.0-dev", "1.0.0-dev") == :eq
33    assert Version.compare("1.0.0-a", "1.0.0-a") == :eq
34    assert Version.compare("1.5.0-rc.0", "1.5.0-rc0") == :lt
35  end
36
37  test "compare/2 with invalid versions" do
38    assert_raise Version.InvalidVersionError, fn ->
39      Version.compare("1.0", "1.0.0")
40    end
41
42    assert_raise Version.InvalidVersionError, fn ->
43      Version.compare("1.0.0-dev", "1.0")
44    end
45
46    assert_raise Version.InvalidVersionError, fn ->
47      Version.compare("foo", "1.0.0-a")
48    end
49  end
50
51  test "lexes specifications properly" do
52    assert Parser.lexer("== > >= < <= ~>") |> Enum.reverse() == [:==, :>, :>=, :<, :<=, :~>]
53    assert Parser.lexer("2.3.0") |> Enum.reverse() == [:==, "2.3.0"]
54    assert Parser.lexer(">>=") |> Enum.reverse() == [:>, :>=]
55    assert Parser.lexer(">2.4.0") |> Enum.reverse() == [:>, "2.4.0"]
56    assert Parser.lexer("> 2.4.0") |> Enum.reverse() == [:>, "2.4.0"]
57    assert Parser.lexer("    >     2.4.0") |> Enum.reverse() == [:>, "2.4.0"]
58    assert Parser.lexer(" or 2.1.0") |> Enum.reverse() == [:or, :==, "2.1.0"]
59    assert Parser.lexer(" and 2.1.0") |> Enum.reverse() == [:and, :==, "2.1.0"]
60
61    assert Parser.lexer(">= 2.0.0 and < 2.1.0") |> Enum.reverse() ==
62             [:>=, "2.0.0", :and, :<, "2.1.0"]
63
64    assert Parser.lexer(">= 2.0.0 or < 2.1.0") |> Enum.reverse() ==
65             [:>=, "2.0.0", :or, :<, "2.1.0"]
66  end
67
68  test "parse/1" do
69    assert {:ok, %Version{major: 1, minor: 2, patch: 3}} = Version.parse("1.2.3")
70    assert {:ok, %Version{major: 1, minor: 4, patch: 5}} = Version.parse("1.4.5+ignore")
71    assert {:ok, %Version{major: 0, minor: 0, patch: 1}} = Version.parse("0.0.1+sha.0702245")
72
73    assert {:ok, %Version{major: 1, minor: 4, patch: 5, pre: ["6-g3318bd5"]}} =
74             Version.parse("1.4.5-6-g3318bd5")
75
76    assert {:ok, %Version{major: 1, minor: 4, patch: 5, pre: [6, 7, "eight"]}} =
77             Version.parse("1.4.5-6.7.eight")
78
79    assert {:ok, %Version{major: 1, minor: 4, patch: 5, pre: ["6-g3318bd5"]}} =
80             Version.parse("1.4.5-6-g3318bd5+ignore")
81
82    assert Version.parse("foobar") == :error
83    assert Version.parse("2") == :error
84    assert Version.parse("2.") == :error
85    assert Version.parse("2.3") == :error
86    assert Version.parse("2.3.") == :error
87    assert Version.parse("2.3.0-") == :error
88    assert Version.parse("2.3.0+") == :error
89    assert Version.parse("2.3.0.") == :error
90    assert Version.parse("2.3.0.4") == :error
91    assert Version.parse("2.3.-rc.1") == :error
92    assert Version.parse("2.3.+rc.1") == :error
93    assert Version.parse("2.3.0-01") == :error
94    assert Version.parse("2.3.00-1") == :error
95    assert Version.parse("2.3.00") == :error
96    assert Version.parse("2.03.0") == :error
97    assert Version.parse("02.3.0") == :error
98    assert Version.parse("0. 0.0") == :error
99    assert Version.parse("0.1.0-&&pre") == :error
100  end
101
102  test "Kernel.to_string/1" do
103    assert Version.parse!("1.0.0") |> to_string == "1.0.0"
104    assert Version.parse!("1.0.0-dev") |> to_string == "1.0.0-dev"
105    assert Version.parse!("1.0.0+lol") |> to_string == "1.0.0+lol"
106    assert Version.parse!("1.0.0-dev+lol") |> to_string == "1.0.0-dev+lol"
107    assert Version.parse!("1.0.0-0") |> to_string == "1.0.0-0"
108    assert Version.parse!("1.0.0-rc.0") |> to_string == "1.0.0-rc.0"
109    assert %Version{major: 1, minor: 0, patch: 0} |> to_string() == "1.0.0"
110  end
111
112  test "match?/2 with invalid versions" do
113    assert_raise Version.InvalidVersionError, fn ->
114      Version.match?("foo", "2.3.0")
115    end
116
117    assert_raise Version.InvalidVersionError, fn ->
118      Version.match?("2.3", "2.3.0")
119    end
120
121    assert_raise Version.InvalidRequirementError, fn ->
122      Version.match?("2.3.0", "foo")
123    end
124
125    assert_raise Version.InvalidRequirementError, fn ->
126      Version.match?("2.3.0", "2.3")
127    end
128  end
129
130  test "==" do
131    assert Version.match?("2.3.0", "2.3.0")
132    refute Version.match?("2.4.0", "2.3.0")
133
134    assert Version.match?("2.3.0", "== 2.3.0")
135    refute Version.match?("2.4.0", "== 2.3.0")
136
137    assert Version.match?("1.0.0", "1.0.0")
138    assert Version.match?("1.0.0", "1.0.0")
139
140    assert Version.match?("1.2.3-alpha", "1.2.3-alpha")
141
142    assert Version.match?("0.9.3", "== 0.9.3+dev")
143
144    {:ok, vsn} = Version.parse("2.3.0")
145    assert Version.match?(vsn, "2.3.0")
146  end
147
148  test "!=" do
149    ExUnit.CaptureIO.capture_io(:stderr, fn ->
150      assert Version.match?("2.4.0", "!2.3.0")
151      refute Version.match?("2.3.0", "!2.3.0")
152
153      assert Version.match?("2.4.0", "!= 2.3.0")
154      refute Version.match?("2.3.0", "!= 2.3.0")
155    end)
156  end
157
158  test ">" do
159    assert Version.match?("2.4.0", "> 2.3.0")
160    refute Version.match?("2.2.0", "> 2.3.0")
161    refute Version.match?("2.3.0", "> 2.3.0")
162
163    assert Version.match?("1.2.3", "> 1.2.3-alpha")
164    assert Version.match?("1.2.3-alpha.1", "> 1.2.3-alpha")
165    assert Version.match?("1.2.3-alpha.beta.sigma", "> 1.2.3-alpha.beta")
166    refute Version.match?("1.2.3-alpha.10", "< 1.2.3-alpha.1")
167    refute Version.match?("0.10.2-dev", "> 0.10.2")
168
169    refute Version.match?("1.5.0-rc.0", "> 1.5.0-rc0")
170    assert Version.match?("1.5.0-rc0", "> 1.5.0-rc.0")
171  end
172
173  test ">=" do
174    assert Version.match?("2.4.0", ">= 2.3.0")
175    refute Version.match?("2.2.0", ">= 2.3.0")
176    assert Version.match?("2.3.0", ">= 2.3.0")
177
178    assert Version.match?("2.0.0", ">= 1.0.0")
179    assert Version.match?("1.0.0", ">= 1.0.0")
180
181    refute Version.match?("1.5.0-rc.0", ">= 1.5.0-rc0")
182    assert Version.match?("1.5.0-rc0", ">= 1.5.0-rc.0")
183  end
184
185  test "<" do
186    assert Version.match?("2.2.0", "< 2.3.0")
187    refute Version.match?("2.4.0", "< 2.3.0")
188    refute Version.match?("2.3.0", "< 2.3.0")
189
190    assert Version.match?("0.10.2-dev", "< 0.10.2")
191
192    refute Version.match?("1.0.0", "< 1.0.0-dev")
193    refute Version.match?("1.2.3-dev", "< 0.1.2")
194  end
195
196  test "<=" do
197    assert Version.match?("2.2.0", "<= 2.3.0")
198    refute Version.match?("2.4.0", "<= 2.3.0")
199    assert Version.match?("2.3.0", "<= 2.3.0")
200  end
201
202  describe "~>" do
203    test "regular cases" do
204      assert Version.match?("3.0.0", "~> 3.0")
205      assert Version.match?("3.2.0", "~> 3.0")
206      refute Version.match?("4.0.0", "~> 3.0")
207      refute Version.match?("4.4.0", "~> 3.0")
208
209      assert Version.match?("3.0.2", "~> 3.0.0")
210      assert Version.match?("3.0.0", "~> 3.0.0")
211      refute Version.match?("3.1.0", "~> 3.0.0")
212      refute Version.match?("3.4.0", "~> 3.0.0")
213
214      assert Version.match?("3.6.0", "~> 3.5")
215      assert Version.match?("3.5.0", "~> 3.5")
216      refute Version.match?("4.0.0", "~> 3.5")
217      refute Version.match?("5.0.0", "~> 3.5")
218
219      assert Version.match?("3.5.2", "~> 3.5.0")
220      assert Version.match?("3.5.4", "~> 3.5.0")
221      refute Version.match?("3.6.0", "~> 3.5.0")
222      refute Version.match?("3.6.3", "~> 3.5.0")
223
224      assert Version.match?("0.9.3", "~> 0.9.3-dev")
225      refute Version.match?("0.10.0", "~> 0.9.3-dev")
226
227      refute Version.match?("0.3.0-dev", "~> 0.2.0")
228
229      assert Version.match?("1.11.0-dev", "~> 1.11-dev")
230      assert Version.match?("1.11.0", "~> 1.11-dev")
231      assert Version.match?("1.12.0", "~> 1.11-dev")
232      refute Version.match?("1.10.0", "~> 1.11-dev")
233      refute Version.match?("2.0.0", "~> 1.11-dev")
234
235      refute Version.match?("1.5.0-rc.0", "~> 1.5.0-rc0")
236      assert Version.match?("1.5.0-rc0", "~> 1.5.0-rc.0")
237
238      assert_raise Version.InvalidRequirementError, fn ->
239        Version.match?("3.0.0", "~> 3")
240      end
241    end
242
243    test "~> will never include pre-release versions of its upper bound" do
244      refute Version.match?("2.2.0-dev", "~> 2.1.0")
245      refute Version.match?("2.2.0-dev", "~> 2.1.0", allow_pre: false)
246      refute Version.match?("2.2.0-dev", "~> 2.1.0-dev")
247      refute Version.match?("2.2.0-dev", "~> 2.1.0-dev", allow_pre: false)
248    end
249  end
250
251  test "allow_pre" do
252    assert Version.match?("1.1.0", "~> 1.0", allow_pre: true)
253    assert Version.match?("1.1.0", "~> 1.0", allow_pre: false)
254    assert Version.match?("1.1.0-beta", "~> 1.0", allow_pre: true)
255    refute Version.match?("1.1.0-beta", "~> 1.0", allow_pre: false)
256    assert Version.match?("1.0.1-beta", "~> 1.0.0-beta", allow_pre: false)
257
258    assert Version.match?("1.1.0", ">= 1.0.0", allow_pre: true)
259    assert Version.match?("1.1.0", ">= 1.0.0", allow_pre: false)
260    assert Version.match?("1.1.0-beta", ">= 1.0.0", allow_pre: true)
261    refute Version.match?("1.1.0-beta", ">= 1.0.0", allow_pre: false)
262    assert Version.match?("1.1.0-beta", ">= 1.0.0-beta", allow_pre: false)
263  end
264
265  test "and" do
266    assert Version.match?("0.9.3", "> 0.9.0 and < 0.10.0")
267    refute Version.match?("0.10.2", "> 0.9.0 and < 0.10.0")
268  end
269
270  test "or" do
271    assert Version.match?("0.9.1", "0.9.1 or 0.9.3 or 0.9.5")
272    assert Version.match?("0.9.3", "0.9.1 or 0.9.3 or 0.9.5")
273    assert Version.match?("0.9.5", "0.9.1 or 0.9.3 or 0.9.5")
274    refute Version.match?("0.9.6", "0.9.1 or 0.9.3 or 0.9.5")
275  end
276
277  test "and/or" do
278    req = "< 0.2.0 and >= 0.1.0 or >= 0.7.0"
279    assert Version.match?("0.1.0", req)
280    assert Version.match?("0.1.5", req)
281    refute Version.match?("0.3.0", req)
282    refute Version.match?("0.6.0", req)
283    assert Version.match?("0.7.0", req)
284    assert Version.match?("0.7.5", req)
285
286    req = ">= 0.7.0 or < 0.2.0 and >= 0.1.0"
287    assert Version.match?("0.1.0", req)
288    assert Version.match?("0.1.5", req)
289    refute Version.match?("0.3.0", req)
290    refute Version.match?("0.6.0", req)
291    assert Version.match?("0.7.0", req)
292    assert Version.match?("0.7.5", req)
293
294    req = "< 0.2.0 and >= 0.1.0 or < 0.8.0 and >= 0.7.0"
295    assert Version.match?("0.1.0", req)
296    assert Version.match?("0.1.5", req)
297    refute Version.match?("0.3.0", req)
298    refute Version.match?("0.6.0", req)
299    assert Version.match?("0.7.0", req)
300    assert Version.match?("0.7.5", req)
301
302    req = "== 0.2.0 or >= 0.3.0 and < 0.4.0 or == 0.7.0"
303    assert Version.match?("0.2.0", req)
304    refute Version.match?("0.2.5", req)
305    assert Version.match?("0.3.0", req)
306    assert Version.match?("0.3.5", req)
307    refute Version.match?("0.4.0", req)
308    assert Version.match?("0.7.0", req)
309  end
310
311  test "compile_requirement/1" do
312    {:ok, req} = Version.parse_requirement("1.2.3")
313    assert req == Version.compile_requirement(req)
314
315    assert_raise(FunctionClauseError, fn ->
316      Version.compile_requirement("~> 1.2.3")
317    end)
318  end
319
320  test "compile requirement" do
321    {:ok, req} = Version.parse_requirement("1.2.3")
322    req = Version.compile_requirement(req)
323
324    assert Version.match?("1.2.3", req)
325    refute Version.match?("1.2.4", req)
326
327    assert Version.parse_requirement("1 . 2 . 3") == :error
328    assert Version.parse_requirement("== >= 1.2.3") == :error
329    assert Version.parse_requirement("1.2.3 and or 4.5.6") == :error
330    assert Version.parse_requirement(">= 1") == :error
331    assert Version.parse_requirement("1.2.3 >=") == :error
332  end
333end
334