259 lines
6 KiB
Go
259 lines
6 KiB
Go
package deps
|
|
|
|
import (
|
|
"reflect"
|
|
"testing"
|
|
)
|
|
|
|
func TestParseRequirement(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
req, err := ParseRequirement("lua5.1 (>= 5.1.5) | luajit")
|
|
if err != nil {
|
|
t.Fatalf("ParseRequirement() error = %v", err)
|
|
}
|
|
|
|
if len(req.Alternatives) != 2 {
|
|
t.Fatalf("expected 2 alternatives, got %d", len(req.Alternatives))
|
|
}
|
|
|
|
first := req.Alternatives[0]
|
|
if first.Name != "lua5.1" || first.Op != OpGreaterOrEqual || first.Version != "5.1.5" {
|
|
t.Fatalf("unexpected first alt: %#v", first)
|
|
}
|
|
second := req.Alternatives[1]
|
|
if second.Name != "luajit" || second.Op != OpAny {
|
|
t.Fatalf("unexpected second alt: %#v", second)
|
|
}
|
|
}
|
|
|
|
func TestParseRequirementsInvalid(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
invalid := []string{
|
|
"",
|
|
" ",
|
|
"bad dep",
|
|
"foo (>> 1)",
|
|
"foo >=",
|
|
"foo |",
|
|
"| foo",
|
|
}
|
|
|
|
for _, raw := range invalid {
|
|
if _, err := ParseRequirement(raw); err == nil {
|
|
t.Fatalf("expected parse error for %q", raw)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestCompareVersions(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
cases := []struct {
|
|
a, b string
|
|
want int
|
|
}{
|
|
{"1.0", "1.0", 0},
|
|
{"1.0", "1.0.1", -1},
|
|
{"2.0", "1.9", 1},
|
|
{"1.02", "1.2", 0},
|
|
{"1.0~rc1", "1.0", -1},
|
|
{"1:1.0-1", "1.9.9", 1},
|
|
{"2:1.0-1", "1:9.0-9", 1},
|
|
{"1.0-2", "1.0-10", -1},
|
|
{"1.0+dfsg1", "1.0+dfsg2", -1},
|
|
{"1.0a", "1.0", 1},
|
|
}
|
|
|
|
for _, tc := range cases {
|
|
got := CompareVersions(tc.a, tc.b)
|
|
if sign(got) != sign(tc.want) {
|
|
t.Fatalf("CompareVersions(%q, %q): want sign %d, got %d", tc.a, tc.b, sign(tc.want), sign(got))
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestConstraintMatchesVersion(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
cases := []struct {
|
|
rawDep string
|
|
version string
|
|
want bool
|
|
}{
|
|
{"foo", "1.0", true},
|
|
{"foo=1.0", "1.0", true},
|
|
{"foo=1.0", "1.1", false},
|
|
{"foo>=1.0", "1.2", true},
|
|
{"foo>=1.0", "0.9", false},
|
|
{"foo<2.0", "1.9", true},
|
|
{"foo<2.0", "2.0", false},
|
|
{"foo (<= 2.1)", "2.1", true},
|
|
}
|
|
|
|
for _, tc := range cases {
|
|
req, err := ParseRequirement(tc.rawDep)
|
|
if err != nil {
|
|
t.Fatalf("ParseRequirement(%q) error = %v", tc.rawDep, err)
|
|
}
|
|
got := req.Alternatives[0].MatchesVersion(tc.version)
|
|
if got != tc.want {
|
|
t.Fatalf("MatchesVersion(%q, %q): want %v, got %v", tc.rawDep, tc.version, tc.want, got)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestParseRequirements(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
input := []string{"foo", "bar>=1.2", "baz | qux"}
|
|
got, err := ParseRequirements(input)
|
|
if err != nil {
|
|
t.Fatalf("ParseRequirements() error = %v", err)
|
|
}
|
|
if len(got) != 3 {
|
|
t.Fatalf("expected 3 requirements, got %d", len(got))
|
|
}
|
|
|
|
names := []string{got[0].Alternatives[0].Name, got[1].Alternatives[0].Name, got[2].Alternatives[0].Name}
|
|
if want := []string{"foo", "bar", "baz"}; !reflect.DeepEqual(names, want) {
|
|
t.Fatalf("unexpected parsed names:\nwant: %#v\ngot: %#v", want, names)
|
|
}
|
|
}
|
|
|
|
// Additional comprehensive tests for version comparison
|
|
func TestCompareVersionsEdgeCases(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
cases := []struct {
|
|
a, b string
|
|
want int
|
|
desc string
|
|
}{
|
|
// Debian epoch tests
|
|
{"1:1.0", "1.0", 1, "epoch vs no epoch"},
|
|
{"2:1.0", "1:2.0", 1, "different epochs"},
|
|
{"0:1.0", "1.0", 0, "zero epoch explicit vs implicit"},
|
|
|
|
// Tilde precedence tests
|
|
{"1.0~rc1", "1.0~rc2", -1, "tilde rc versions"},
|
|
{"1.0~rc1", "1.0", -1, "tilde vs final"},
|
|
{"1.0~alpha", "1.0~dev", -1, "tilde alpha vs dev"},
|
|
|
|
// Complex version parts
|
|
{"1.2.3-4", "1.2.3-4ubuntu1", -1, "ubuntu suffix"},
|
|
{"1.2.3+dfsg1", "1.2.3+dfsg2", -1, "dfsg suffix"},
|
|
{"1.2.3-1+b1", "1.2.3-1", 1, "binNMU suffix"},
|
|
|
|
// Numeric vs alphanumeric
|
|
{"1.0a", "1.0", 1, "alpha suffix"},
|
|
{"1.0beta", "1.0", 1, "beta suffix"},
|
|
{"1.0rc1", "1.0", 1, "rc suffix"},
|
|
|
|
// Leading zeros
|
|
{"1.02", "1.002", 0, "leading zeros"},
|
|
{"1.010", "1.2", 1, "leading zeros comparison"},
|
|
|
|
// Empty parts
|
|
{"1.0-", "1.0", 0, "empty release"},
|
|
{"1.0-0", "1.0", 0, "zero release"},
|
|
}
|
|
|
|
for _, tc := range cases {
|
|
t.Run(tc.desc, func(t *testing.T) {
|
|
got := CompareVersions(tc.a, tc.b)
|
|
if sign(got) != sign(tc.want) {
|
|
t.Errorf("CompareVersions(%q, %q): want sign %d, got %d", tc.a, tc.b, sign(tc.want), sign(got))
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestParseRequirementComplex(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
cases := []struct {
|
|
raw string
|
|
expect Requirement
|
|
desc string
|
|
}{
|
|
{
|
|
"pkg (>= 1.2.3) | otherpkg (= 2.0)",
|
|
Requirement{
|
|
Raw: "pkg (>= 1.2.3) | otherpkg (= 2.0)",
|
|
Alternatives: []Constraint{
|
|
{Name: "pkg", Op: OpGreaterOrEqual, Version: "1.2.3"},
|
|
{Name: "otherpkg", Op: OpEqual, Version: "2.0"},
|
|
},
|
|
},
|
|
"debian style with alternatives",
|
|
},
|
|
{
|
|
"complex-name+suffix_1.0",
|
|
Requirement{
|
|
Raw: "complex-name+suffix_1.0",
|
|
Alternatives: []Constraint{
|
|
{Name: "complex-name+suffix_1.0", Op: OpAny, Version: ""},
|
|
},
|
|
},
|
|
"complex package name",
|
|
},
|
|
}
|
|
|
|
for _, tc := range cases {
|
|
t.Run(tc.desc, func(t *testing.T) {
|
|
got, err := ParseRequirement(tc.raw)
|
|
if err != nil {
|
|
t.Fatalf("ParseRequirement(%q) error = %v", tc.raw, err)
|
|
}
|
|
if !reflect.DeepEqual(got, tc.expect) {
|
|
t.Errorf("ParseRequirement(%q):\nwant: %#v\ngot: %#v", tc.raw, tc.expect, got)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
// Property-based test for version comparison transitivity
|
|
func TestCompareVersionsTransitivity(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
versions := []string{
|
|
"1.0", "1.0~rc1", "1.0~dev", "1.1", "2.0",
|
|
"1:1.0", "1:0.9", "1.0-1", "1.0-2",
|
|
}
|
|
|
|
for i, a := range versions {
|
|
for j, b := range versions {
|
|
for k, c := range versions {
|
|
if i == j || j == k || i == k {
|
|
continue
|
|
}
|
|
|
|
ab := CompareVersions(a, b)
|
|
bc := CompareVersions(b, c)
|
|
ac := CompareVersions(a, c)
|
|
|
|
// If a > b and b > c, then a > c
|
|
if ab > 0 && bc > 0 && ac <= 0 {
|
|
t.Errorf("Transitivity violation: %s > %s and %s > %s but %s <= %s", a, b, b, c, a, c)
|
|
}
|
|
// If a == b and b == c, then a == c
|
|
if ab == 0 && bc == 0 && ac != 0 {
|
|
t.Errorf("Transitivity violation: %s == %s and %s == %s but %s != %s", a, b, b, c, a, c)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
func sign(v int) int {
|
|
switch {
|
|
case v < 0:
|
|
return -1
|
|
case v > 0:
|
|
return 1
|
|
default:
|
|
return 0
|
|
}
|
|
}
|