123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459 |
- package semver
- import (
- "reflect"
- "strings"
- "testing"
- )
- type comparatorTest struct {
- input string
- comparator func(comparator) bool
- }
- func TestParseComparator(t *testing.T) {
- compatorTests := []comparatorTest{
- {">", testGT},
- {">=", testGE},
- {"<", testLT},
- {"<=", testLE},
- {"", testEQ},
- {"=", testEQ},
- {"==", testEQ},
- {"!=", testNE},
- {"!", testNE},
- {"-", nil},
- {"<==", nil},
- {"<<", nil},
- {">>", nil},
- }
- for _, tc := range compatorTests {
- if c := parseComparator(tc.input); c == nil {
- if tc.comparator != nil {
- t.Errorf("Comparator nil for case %q\n", tc.input)
- }
- } else if !tc.comparator(c) {
- t.Errorf("Invalid comparator for case %q\n", tc.input)
- }
- }
- }
- var (
- v1 = MustParse("1.2.2")
- v2 = MustParse("1.2.3")
- v3 = MustParse("1.2.4")
- )
- func testEQ(f comparator) bool {
- return f(v1, v1) && !f(v1, v2)
- }
- func testNE(f comparator) bool {
- return !f(v1, v1) && f(v1, v2)
- }
- func testGT(f comparator) bool {
- return f(v2, v1) && f(v3, v2) && !f(v1, v2) && !f(v1, v1)
- }
- func testGE(f comparator) bool {
- return f(v2, v1) && f(v3, v2) && !f(v1, v2)
- }
- func testLT(f comparator) bool {
- return f(v1, v2) && f(v2, v3) && !f(v2, v1) && !f(v1, v1)
- }
- func testLE(f comparator) bool {
- return f(v1, v2) && f(v2, v3) && !f(v2, v1)
- }
- func TestSplitAndTrim(t *testing.T) {
- tests := []struct {
- i string
- s []string
- }{
- {"1.2.3 1.2.3", []string{"1.2.3", "1.2.3"}},
- {" 1.2.3 1.2.3 ", []string{"1.2.3", "1.2.3"}}, // Spaces
- {"1.2.3 || >=1.2.3 <1.2.3", []string{"1.2.3", "||", ">=1.2.3", "<1.2.3"}},
- {" 1.2.3 || >=1.2.3 <1.2.3 ", []string{"1.2.3", "||", ">=1.2.3", "<1.2.3"}},
- }
- for _, tc := range tests {
- p := splitAndTrim(tc.i)
- if !reflect.DeepEqual(p, tc.s) {
- t.Errorf("Invalid for case %q: Expected %q, got: %q", tc.i, tc.s, p)
- }
- }
- }
- func TestSplitComparatorVersion(t *testing.T) {
- tests := []struct {
- i string
- p []string
- }{
- {">1.2.3", []string{">", "1.2.3"}},
- {">=1.2.3", []string{">=", "1.2.3"}},
- {"<1.2.3", []string{"<", "1.2.3"}},
- {"<=1.2.3", []string{"<=", "1.2.3"}},
- {"1.2.3", []string{"", "1.2.3"}},
- {"=1.2.3", []string{"=", "1.2.3"}},
- {"==1.2.3", []string{"==", "1.2.3"}},
- {"!=1.2.3", []string{"!=", "1.2.3"}},
- {"!1.2.3", []string{"!", "1.2.3"}},
- {"error", nil},
- }
- for _, tc := range tests {
- if op, v, err := splitComparatorVersion(tc.i); err != nil {
- if tc.p != nil {
- t.Errorf("Invalid for case %q: Expected %q, got error %q", tc.i, tc.p, err)
- }
- } else if op != tc.p[0] {
- t.Errorf("Invalid operator for case %q: Expected %q, got: %q", tc.i, tc.p[0], op)
- } else if v != tc.p[1] {
- t.Errorf("Invalid version for case %q: Expected %q, got: %q", tc.i, tc.p[1], v)
- }
- }
- }
- func TestBuildVersionRange(t *testing.T) {
- tests := []struct {
- opStr string
- vStr string
- c func(comparator) bool
- v string
- }{
- {">", "1.2.3", testGT, "1.2.3"},
- {">=", "1.2.3", testGE, "1.2.3"},
- {"<", "1.2.3", testLT, "1.2.3"},
- {"<=", "1.2.3", testLE, "1.2.3"},
- {"", "1.2.3", testEQ, "1.2.3"},
- {"=", "1.2.3", testEQ, "1.2.3"},
- {"==", "1.2.3", testEQ, "1.2.3"},
- {"!=", "1.2.3", testNE, "1.2.3"},
- {"!", "1.2.3", testNE, "1.2.3"},
- {">>", "1.2.3", nil, ""}, // Invalid comparator
- {"=", "invalid", nil, ""}, // Invalid version
- }
- for _, tc := range tests {
- if r, err := buildVersionRange(tc.opStr, tc.vStr); err != nil {
- if tc.c != nil {
- t.Errorf("Invalid for case %q: Expected %q, got error %q", strings.Join([]string{tc.opStr, tc.vStr}, ""), tc.v, err)
- }
- } else if r == nil {
- t.Errorf("Invalid for case %q: got nil", strings.Join([]string{tc.opStr, tc.vStr}, ""))
- } else {
- // test version
- if tv := MustParse(tc.v); !r.v.EQ(tv) {
- t.Errorf("Invalid for case %q: Expected version %q, got: %q", strings.Join([]string{tc.opStr, tc.vStr}, ""), tv, r.v)
- }
- // test comparator
- if r.c == nil {
- t.Errorf("Invalid for case %q: got nil comparator", strings.Join([]string{tc.opStr, tc.vStr}, ""))
- continue
- }
- if !tc.c(r.c) {
- t.Errorf("Invalid comparator for case %q\n", strings.Join([]string{tc.opStr, tc.vStr}, ""))
- }
- }
- }
- }
- func TestSplitORParts(t *testing.T) {
- tests := []struct {
- i []string
- o [][]string
- }{
- {[]string{">1.2.3", "||", "<1.2.3", "||", "=1.2.3"}, [][]string{
- []string{">1.2.3"},
- []string{"<1.2.3"},
- []string{"=1.2.3"},
- }},
- {[]string{">1.2.3", "<1.2.3", "||", "=1.2.3"}, [][]string{
- []string{">1.2.3", "<1.2.3"},
- []string{"=1.2.3"},
- }},
- {[]string{">1.2.3", "||"}, nil},
- {[]string{"||", ">1.2.3"}, nil},
- }
- for _, tc := range tests {
- o, err := splitORParts(tc.i)
- if err != nil && tc.o != nil {
- t.Errorf("Unexpected error for case %q: %s", tc.i, err)
- }
- if !reflect.DeepEqual(tc.o, o) {
- t.Errorf("Invalid for case %q: Expected %q, got: %q", tc.i, tc.o, o)
- }
- }
- }
- func TestVersionRangeToRange(t *testing.T) {
- vr := versionRange{
- v: MustParse("1.2.3"),
- c: compLT,
- }
- rf := vr.rangeFunc()
- if !rf(MustParse("1.2.2")) || rf(MustParse("1.2.3")) {
- t.Errorf("Invalid conversion to range func")
- }
- }
- func TestRangeAND(t *testing.T) {
- v := MustParse("1.2.2")
- v1 := MustParse("1.2.1")
- v2 := MustParse("1.2.3")
- rf1 := Range(func(v Version) bool {
- return v.GT(v1)
- })
- rf2 := Range(func(v Version) bool {
- return v.LT(v2)
- })
- rf := rf1.AND(rf2)
- if rf(v1) {
- t.Errorf("Invalid rangefunc, accepted: %s", v1)
- }
- if rf(v2) {
- t.Errorf("Invalid rangefunc, accepted: %s", v2)
- }
- if !rf(v) {
- t.Errorf("Invalid rangefunc, did not accept: %s", v)
- }
- }
- func TestRangeOR(t *testing.T) {
- tests := []struct {
- v Version
- b bool
- }{
- {MustParse("1.2.0"), true},
- {MustParse("1.2.2"), false},
- {MustParse("1.2.4"), true},
- }
- v1 := MustParse("1.2.1")
- v2 := MustParse("1.2.3")
- rf1 := Range(func(v Version) bool {
- return v.LT(v1)
- })
- rf2 := Range(func(v Version) bool {
- return v.GT(v2)
- })
- rf := rf1.OR(rf2)
- for _, tc := range tests {
- if r := rf(tc.v); r != tc.b {
- t.Errorf("Invalid for case %q: Expected %t, got %t", tc.v, tc.b, r)
- }
- }
- }
- func TestParseRange(t *testing.T) {
- type tv struct {
- v string
- b bool
- }
- tests := []struct {
- i string
- t []tv
- }{
- // Simple expressions
- {">1.2.3", []tv{
- {"1.2.2", false},
- {"1.2.3", false},
- {"1.2.4", true},
- }},
- {">=1.2.3", []tv{
- {"1.2.3", true},
- {"1.2.4", true},
- {"1.2.2", false},
- }},
- {"<1.2.3", []tv{
- {"1.2.2", true},
- {"1.2.3", false},
- {"1.2.4", false},
- }},
- {"<=1.2.3", []tv{
- {"1.2.2", true},
- {"1.2.3", true},
- {"1.2.4", false},
- }},
- {"1.2.3", []tv{
- {"1.2.2", false},
- {"1.2.3", true},
- {"1.2.4", false},
- }},
- {"=1.2.3", []tv{
- {"1.2.2", false},
- {"1.2.3", true},
- {"1.2.4", false},
- }},
- {"==1.2.3", []tv{
- {"1.2.2", false},
- {"1.2.3", true},
- {"1.2.4", false},
- }},
- {"!=1.2.3", []tv{
- {"1.2.2", true},
- {"1.2.3", false},
- {"1.2.4", true},
- }},
- {"!1.2.3", []tv{
- {"1.2.2", true},
- {"1.2.3", false},
- {"1.2.4", true},
- }},
- // Simple Expression errors
- {">>1.2.3", nil},
- {"!1.2.3", nil},
- {"1.0", nil},
- {"string", nil},
- {"", nil},
- // AND Expressions
- {">1.2.2 <1.2.4", []tv{
- {"1.2.2", false},
- {"1.2.3", true},
- {"1.2.4", false},
- }},
- {"<1.2.2 <1.2.4", []tv{
- {"1.2.1", true},
- {"1.2.2", false},
- {"1.2.3", false},
- {"1.2.4", false},
- }},
- {">1.2.2 <1.2.5 !=1.2.4", []tv{
- {"1.2.2", false},
- {"1.2.3", true},
- {"1.2.4", false},
- {"1.2.5", false},
- }},
- {">1.2.2 <1.2.5 !1.2.4", []tv{
- {"1.2.2", false},
- {"1.2.3", true},
- {"1.2.4", false},
- {"1.2.5", false},
- }},
- // OR Expressions
- {">1.2.2 || <1.2.4", []tv{
- {"1.2.2", true},
- {"1.2.3", true},
- {"1.2.4", true},
- }},
- {"<1.2.2 || >1.2.4", []tv{
- {"1.2.2", false},
- {"1.2.3", false},
- {"1.2.4", false},
- }},
- // Combined Expressions
- {">1.2.2 <1.2.4 || >=2.0.0", []tv{
- {"1.2.2", false},
- {"1.2.3", true},
- {"1.2.4", false},
- {"2.0.0", true},
- {"2.0.1", true},
- }},
- {">1.2.2 <1.2.4 || >=2.0.0 <3.0.0", []tv{
- {"1.2.2", false},
- {"1.2.3", true},
- {"1.2.4", false},
- {"2.0.0", true},
- {"2.0.1", true},
- {"2.9.9", true},
- {"3.0.0", false},
- }},
- }
- for _, tc := range tests {
- r, err := ParseRange(tc.i)
- if err != nil && tc.t != nil {
- t.Errorf("Error parsing range %q: %s", tc.i, err)
- continue
- }
- for _, tvc := range tc.t {
- v := MustParse(tvc.v)
- if res := r(v); res != tvc.b {
- t.Errorf("Invalid for case %q matching %q: Expected %t, got: %t", tc.i, tvc.v, tvc.b, res)
- }
- }
- }
- }
- func TestMustParseRange(t *testing.T) {
- testCase := ">1.2.2 <1.2.4 || >=2.0.0 <3.0.0"
- r := MustParseRange(testCase)
- if !r(MustParse("1.2.3")) {
- t.Errorf("Unexpected range behavior on MustParseRange")
- }
- }
- func TestMustParseRange_panic(t *testing.T) {
- defer func() {
- if recover() == nil {
- t.Errorf("Should have panicked")
- }
- }()
- _ = MustParseRange("invalid version")
- }
- func BenchmarkRangeParseSimple(b *testing.B) {
- const VERSION = ">1.0.0"
- b.ReportAllocs()
- b.ResetTimer()
- for n := 0; n < b.N; n++ {
- ParseRange(VERSION)
- }
- }
- func BenchmarkRangeParseAverage(b *testing.B) {
- const VERSION = ">=1.0.0 <2.0.0"
- b.ReportAllocs()
- b.ResetTimer()
- for n := 0; n < b.N; n++ {
- ParseRange(VERSION)
- }
- }
- func BenchmarkRangeParseComplex(b *testing.B) {
- const VERSION = ">=1.0.0 <2.0.0 || >=3.0.1 <4.0.0 !=3.0.3 || >=5.0.0"
- b.ReportAllocs()
- b.ResetTimer()
- for n := 0; n < b.N; n++ {
- ParseRange(VERSION)
- }
- }
- func BenchmarkRangeMatchSimple(b *testing.B) {
- const VERSION = ">1.0.0"
- r, _ := ParseRange(VERSION)
- v := MustParse("2.0.0")
- b.ReportAllocs()
- b.ResetTimer()
- for n := 0; n < b.N; n++ {
- r(v)
- }
- }
- func BenchmarkRangeMatchAverage(b *testing.B) {
- const VERSION = ">=1.0.0 <2.0.0"
- r, _ := ParseRange(VERSION)
- v := MustParse("1.2.3")
- b.ReportAllocs()
- b.ResetTimer()
- for n := 0; n < b.N; n++ {
- r(v)
- }
- }
- func BenchmarkRangeMatchComplex(b *testing.B) {
- const VERSION = ">=1.0.0 <2.0.0 || >=3.0.1 <4.0.0 !=3.0.3 || >=5.0.0"
- r, _ := ParseRange(VERSION)
- v := MustParse("5.0.1")
- b.ReportAllocs()
- b.ResetTimer()
- for n := 0; n < b.N; n++ {
- r(v)
- }
- }
|