diff --git a/vendor/github.com/blang/semver/.gx/lastpubver b/vendor/github.com/blang/semver/.gx/lastpubver new file mode 100644 index 0000000000..073ce1eff6 --- /dev/null +++ b/vendor/github.com/blang/semver/.gx/lastpubver @@ -0,0 +1 @@ +3.4.0: QmZTgGMg34JKEvF1hjr7wwYESvFhg9Khv2WFibDAi5dhno diff --git a/vendor/github.com/blang/semver/README.md b/vendor/github.com/blang/semver/README.md index 5171c5c557..4399639e23 100644 --- a/vendor/github.com/blang/semver/README.md +++ b/vendor/github.com/blang/semver/README.md @@ -40,10 +40,52 @@ Features - Comparator-like comparisons - Compare Helper Methods - InPlace manipulation +- Ranges `>=1.0.0 <2.0.0 || >=3.0.0 !3.0.1-beta.1` - Sortable (implements sort.Interface) - database/sql compatible (sql.Scanner/Valuer) - encoding/json compatible (json.Marshaler/Unmarshaler) +Ranges +------ + +A `Range` is a set of conditions which specify which versions satisfy the range. + +A condition is composed of an operator and a version. The supported operators are: + +- `<1.0.0` Less than `1.0.0` +- `<=1.0.0` Less than or equal to `1.0.0` +- `>1.0.0` Greater than `1.0.0` +- `>=1.0.0` Greater than or equal to `1.0.0` +- `1.0.0`, `=1.0.0`, `==1.0.0` Equal to `1.0.0` +- `!1.0.0`, `!=1.0.0` Not equal to `1.0.0`. Excludes version `1.0.0`. + +A `Range` can link multiple `Ranges` separated by space: + +Ranges can be linked by logical AND: + + - `>1.0.0 <2.0.0` would match between both ranges, so `1.1.1` and `1.8.7` but not `1.0.0` or `2.0.0` + - `>1.0.0 <3.0.0 !2.0.3-beta.2` would match every version between `1.0.0` and `3.0.0` except `2.0.3-beta.2` + +Ranges can also be linked by logical OR: + + - `<2.0.0 || >=3.0.0` would match `1.x.x` and `3.x.x` but not `2.x.x` + +AND has a higher precedence than OR. It's not possible to use brackets. + +Ranges can be combined by both AND and OR + + - `>1.0.0 <2.0.0 || >3.0.0 !4.2.1` would match `1.2.3`, `1.9.9`, `3.1.1`, but not `4.2.1`, `2.1.1` + +Range usage: + +``` +v, err := semver.Parse("1.2.3") +range, err := semver.ParseRange(">1.0.0 <2.0.0 || >=3.0.0") +if range(v) { + //valid +} + +``` Example ----- @@ -103,23 +145,30 @@ if err != nil { } ``` + Benchmarks ----- - BenchmarkParseSimple 5000000 328 ns/op 49 B/op 1 allocs/op - BenchmarkParseComplex 1000000 2105 ns/op 263 B/op 7 allocs/op - BenchmarkParseAverage 1000000 1301 ns/op 168 B/op 4 allocs/op - BenchmarkStringSimple 10000000 130 ns/op 5 B/op 1 allocs/op - BenchmarkStringLarger 5000000 280 ns/op 32 B/op 2 allocs/op - BenchmarkStringComplex 3000000 512 ns/op 80 B/op 3 allocs/op - BenchmarkStringAverage 5000000 387 ns/op 47 B/op 2 allocs/op - BenchmarkValidateSimple 500000000 7.92 ns/op 0 B/op 0 allocs/op - BenchmarkValidateComplex 2000000 923 ns/op 0 B/op 0 allocs/op - BenchmarkValidateAverage 5000000 452 ns/op 0 B/op 0 allocs/op - BenchmarkCompareSimple 100000000 11.2 ns/op 0 B/op 0 allocs/op - BenchmarkCompareComplex 50000000 40.9 ns/op 0 B/op 0 allocs/op - BenchmarkCompareAverage 50000000 43.8 ns/op 0 B/op 0 allocs/op - BenchmarkSort 5000000 436 ns/op 259 B/op 2 allocs/op + BenchmarkParseSimple-4 5000000 390 ns/op 48 B/op 1 allocs/op + BenchmarkParseComplex-4 1000000 1813 ns/op 256 B/op 7 allocs/op + BenchmarkParseAverage-4 1000000 1171 ns/op 163 B/op 4 allocs/op + BenchmarkStringSimple-4 20000000 119 ns/op 16 B/op 1 allocs/op + BenchmarkStringLarger-4 10000000 206 ns/op 32 B/op 2 allocs/op + BenchmarkStringComplex-4 5000000 324 ns/op 80 B/op 3 allocs/op + BenchmarkStringAverage-4 5000000 273 ns/op 53 B/op 2 allocs/op + BenchmarkValidateSimple-4 200000000 9.33 ns/op 0 B/op 0 allocs/op + BenchmarkValidateComplex-4 3000000 469 ns/op 0 B/op 0 allocs/op + BenchmarkValidateAverage-4 5000000 256 ns/op 0 B/op 0 allocs/op + BenchmarkCompareSimple-4 100000000 11.8 ns/op 0 B/op 0 allocs/op + BenchmarkCompareComplex-4 50000000 30.8 ns/op 0 B/op 0 allocs/op + BenchmarkCompareAverage-4 30000000 41.5 ns/op 0 B/op 0 allocs/op + BenchmarkSort-4 3000000 419 ns/op 256 B/op 2 allocs/op + BenchmarkRangeParseSimple-4 2000000 850 ns/op 192 B/op 5 allocs/op + BenchmarkRangeParseAverage-4 1000000 1677 ns/op 400 B/op 10 allocs/op + BenchmarkRangeParseComplex-4 300000 5214 ns/op 1440 B/op 30 allocs/op + BenchmarkRangeMatchSimple-4 50000000 25.6 ns/op 0 B/op 0 allocs/op + BenchmarkRangeMatchAverage-4 30000000 56.4 ns/op 0 B/op 0 allocs/op + BenchmarkRangeMatchComplex-4 10000000 153 ns/op 0 B/op 0 allocs/op See benchmark cases at [semver_test.go](semver_test.go) diff --git a/vendor/github.com/blang/semver/json_test.go b/vendor/github.com/blang/semver/json_test.go index 039117da99..c635dea16f 100644 --- a/vendor/github.com/blang/semver/json_test.go +++ b/vendor/github.com/blang/semver/json_test.go @@ -42,4 +42,8 @@ func TestJSONUnmarshal(t *testing.T) { if err := json.Unmarshal([]byte(badVersionString), &v); err == nil { t.Fatal("expected JSON unmarshal error, got nil") } + + if err := json.Unmarshal([]byte("3.1"), &v); err == nil { + t.Fatal("expected JSON unmarshal error, got nil") + } } diff --git a/vendor/github.com/blang/semver/package.json b/vendor/github.com/blang/semver/package.json new file mode 100644 index 0000000000..568be8d943 --- /dev/null +++ b/vendor/github.com/blang/semver/package.json @@ -0,0 +1,17 @@ +{ + "author": "blang", + "bugs": { + "URL": "https://github.com/blang/semver/issues", + "url": "https://github.com/blang/semver/issues" + }, + "gx": { + "dvcsimport": "github.com/blang/semver" + }, + "gxVersion": "0.10.0", + "language": "go", + "license": "MIT", + "name": "semver", + "releaseCmd": "git commit -a -m \"gx publish $VERSION\"", + "version": "3.4.0" +} + diff --git a/vendor/github.com/blang/semver/range.go b/vendor/github.com/blang/semver/range.go new file mode 100644 index 0000000000..fca406d479 --- /dev/null +++ b/vendor/github.com/blang/semver/range.go @@ -0,0 +1,416 @@ +package semver + +import ( + "fmt" + "strconv" + "strings" + "unicode" +) + +type wildcardType int + +const ( + noneWildcard wildcardType = iota + majorWildcard wildcardType = 1 + minorWildcard wildcardType = 2 + patchWildcard wildcardType = 3 +) + +func wildcardTypefromInt(i int) wildcardType { + switch i { + case 1: + return majorWildcard + case 2: + return minorWildcard + case 3: + return patchWildcard + default: + return noneWildcard + } +} + +type comparator func(Version, Version) bool + +var ( + compEQ comparator = func(v1 Version, v2 Version) bool { + return v1.Compare(v2) == 0 + } + compNE = func(v1 Version, v2 Version) bool { + return v1.Compare(v2) != 0 + } + compGT = func(v1 Version, v2 Version) bool { + return v1.Compare(v2) == 1 + } + compGE = func(v1 Version, v2 Version) bool { + return v1.Compare(v2) >= 0 + } + compLT = func(v1 Version, v2 Version) bool { + return v1.Compare(v2) == -1 + } + compLE = func(v1 Version, v2 Version) bool { + return v1.Compare(v2) <= 0 + } +) + +type versionRange struct { + v Version + c comparator +} + +// rangeFunc creates a Range from the given versionRange. +func (vr *versionRange) rangeFunc() Range { + return Range(func(v Version) bool { + return vr.c(v, vr.v) + }) +} + +// Range represents a range of versions. +// A Range can be used to check if a Version satisfies it: +// +// range, err := semver.ParseRange(">1.0.0 <2.0.0") +// range(semver.MustParse("1.1.1") // returns true +type Range func(Version) bool + +// OR combines the existing Range with another Range using logical OR. +func (rf Range) OR(f Range) Range { + return Range(func(v Version) bool { + return rf(v) || f(v) + }) +} + +// AND combines the existing Range with another Range using logical AND. +func (rf Range) AND(f Range) Range { + return Range(func(v Version) bool { + return rf(v) && f(v) + }) +} + +// ParseRange parses a range and returns a Range. +// If the range could not be parsed an error is returned. +// +// Valid ranges are: +// - "<1.0.0" +// - "<=1.0.0" +// - ">1.0.0" +// - ">=1.0.0" +// - "1.0.0", "=1.0.0", "==1.0.0" +// - "!1.0.0", "!=1.0.0" +// +// A Range can consist of multiple ranges separated by space: +// Ranges can be linked by logical AND: +// - ">1.0.0 <2.0.0" would match between both ranges, so "1.1.1" and "1.8.7" but not "1.0.0" or "2.0.0" +// - ">1.0.0 <3.0.0 !2.0.3-beta.2" would match every version between 1.0.0 and 3.0.0 except 2.0.3-beta.2 +// +// Ranges can also be linked by logical OR: +// - "<2.0.0 || >=3.0.0" would match "1.x.x" and "3.x.x" but not "2.x.x" +// +// AND has a higher precedence than OR. It's not possible to use brackets. +// +// Ranges can be combined by both AND and OR +// +// - `>1.0.0 <2.0.0 || >3.0.0 !4.2.1` would match `1.2.3`, `1.9.9`, `3.1.1`, but not `4.2.1`, `2.1.1` +func ParseRange(s string) (Range, error) { + parts := splitAndTrim(s) + orParts, err := splitORParts(parts) + if err != nil { + return nil, err + } + expandedParts, err := expandWildcardVersion(orParts) + if err != nil { + return nil, err + } + var orFn Range + for _, p := range expandedParts { + var andFn Range + for _, ap := range p { + opStr, vStr, err := splitComparatorVersion(ap) + if err != nil { + return nil, err + } + vr, err := buildVersionRange(opStr, vStr) + if err != nil { + return nil, fmt.Errorf("Could not parse Range %q: %s", ap, err) + } + rf := vr.rangeFunc() + + // Set function + if andFn == nil { + andFn = rf + } else { // Combine with existing function + andFn = andFn.AND(rf) + } + } + if orFn == nil { + orFn = andFn + } else { + orFn = orFn.OR(andFn) + } + + } + return orFn, nil +} + +// splitORParts splits the already cleaned parts by '||'. +// Checks for invalid positions of the operator and returns an +// error if found. +func splitORParts(parts []string) ([][]string, error) { + var ORparts [][]string + last := 0 + for i, p := range parts { + if p == "||" { + if i == 0 { + return nil, fmt.Errorf("First element in range is '||'") + } + ORparts = append(ORparts, parts[last:i]) + last = i + 1 + } + } + if last == len(parts) { + return nil, fmt.Errorf("Last element in range is '||'") + } + ORparts = append(ORparts, parts[last:]) + return ORparts, nil +} + +// buildVersionRange takes a slice of 2: operator and version +// and builds a versionRange, otherwise an error. +func buildVersionRange(opStr, vStr string) (*versionRange, error) { + c := parseComparator(opStr) + if c == nil { + return nil, fmt.Errorf("Could not parse comparator %q in %q", opStr, strings.Join([]string{opStr, vStr}, "")) + } + v, err := Parse(vStr) + if err != nil { + return nil, fmt.Errorf("Could not parse version %q in %q: %s", vStr, strings.Join([]string{opStr, vStr}, ""), err) + } + + return &versionRange{ + v: v, + c: c, + }, nil + +} + +// inArray checks if a byte is contained in an array of bytes +func inArray(s byte, list []byte) bool { + for _, el := range list { + if el == s { + return true + } + } + return false +} + +// splitAndTrim splits a range string by spaces and cleans whitespaces +func splitAndTrim(s string) (result []string) { + last := 0 + var lastChar byte + excludeFromSplit := []byte{'>', '<', '='} + for i := 0; i < len(s); i++ { + if s[i] == ' ' && !inArray(lastChar, excludeFromSplit) { + if last < i-1 { + result = append(result, s[last:i]) + } + last = i + 1 + } else if s[i] != ' ' { + lastChar = s[i] + } + } + if last < len(s)-1 { + result = append(result, s[last:]) + } + + for i, v := range result { + result[i] = strings.Replace(v, " ", "", -1) + } + + // parts := strings.Split(s, " ") + // for _, x := range parts { + // if s := strings.TrimSpace(x); len(s) != 0 { + // result = append(result, s) + // } + // } + return +} + +// splitComparatorVersion splits the comparator from the version. +// Input must be free of leading or trailing spaces. +func splitComparatorVersion(s string) (string, string, error) { + i := strings.IndexFunc(s, unicode.IsDigit) + if i == -1 { + return "", "", fmt.Errorf("Could not get version from string: %q", s) + } + return strings.TrimSpace(s[0:i]), s[i:], nil +} + +// getWildcardType will return the type of wildcard that the +// passed version contains +func getWildcardType(vStr string) wildcardType { + parts := strings.Split(vStr, ".") + nparts := len(parts) + wildcard := parts[nparts-1] + + possibleWildcardType := wildcardTypefromInt(nparts) + if wildcard == "x" { + return possibleWildcardType + } + + return noneWildcard +} + +// createVersionFromWildcard will convert a wildcard version +// into a regular version, replacing 'x's with '0's, handling +// special cases like '1.x.x' and '1.x' +func createVersionFromWildcard(vStr string) string { + // handle 1.x.x + vStr2 := strings.Replace(vStr, ".x.x", ".x", 1) + vStr2 = strings.Replace(vStr2, ".x", ".0", 1) + parts := strings.Split(vStr2, ".") + + // handle 1.x + if len(parts) == 2 { + return vStr2 + ".0" + } + + return vStr2 +} + +// incrementMajorVersion will increment the major version +// of the passed version +func incrementMajorVersion(vStr string) (string, error) { + parts := strings.Split(vStr, ".") + i, err := strconv.Atoi(parts[0]) + if err != nil { + return "", err + } + parts[0] = strconv.Itoa(i + 1) + + return strings.Join(parts, "."), nil +} + +// incrementMajorVersion will increment the minor version +// of the passed version +func incrementMinorVersion(vStr string) (string, error) { + parts := strings.Split(vStr, ".") + i, err := strconv.Atoi(parts[1]) + if err != nil { + return "", err + } + parts[1] = strconv.Itoa(i + 1) + + return strings.Join(parts, "."), nil +} + +// expandWildcardVersion will expand wildcards inside versions +// following these rules: +// +// * when dealing with patch wildcards: +// >= 1.2.x will become >= 1.2.0 +// <= 1.2.x will become < 1.3.0 +// > 1.2.x will become >= 1.3.0 +// < 1.2.x will become < 1.2.0 +// != 1.2.x will become < 1.2.0 >= 1.3.0 +// +// * when dealing with minor wildcards: +// >= 1.x will become >= 1.0.0 +// <= 1.x will become < 2.0.0 +// > 1.x will become >= 2.0.0 +// < 1.0 will become < 1.0.0 +// != 1.x will become < 1.0.0 >= 2.0.0 +// +// * when dealing with wildcards without +// version operator: +// 1.2.x will become >= 1.2.0 < 1.3.0 +// 1.x will become >= 1.0.0 < 2.0.0 +func expandWildcardVersion(parts [][]string) ([][]string, error) { + var expandedParts [][]string + for _, p := range parts { + var newParts []string + for _, ap := range p { + if strings.Index(ap, "x") != -1 { + opStr, vStr, err := splitComparatorVersion(ap) + if err != nil { + return nil, err + } + + versionWildcardType := getWildcardType(vStr) + flatVersion := createVersionFromWildcard(vStr) + + var resultOperator string + var shouldIncrementVersion bool + switch opStr { + case ">": + resultOperator = ">=" + shouldIncrementVersion = true + case ">=": + resultOperator = ">=" + case "<": + resultOperator = "<" + case "<=": + resultOperator = "<" + shouldIncrementVersion = true + case "", "=", "==": + newParts = append(newParts, ">="+flatVersion) + resultOperator = "<" + shouldIncrementVersion = true + case "!=", "!": + newParts = append(newParts, "<"+flatVersion) + resultOperator = ">=" + shouldIncrementVersion = true + } + + var resultVersion string + if shouldIncrementVersion { + switch versionWildcardType { + case patchWildcard: + resultVersion, _ = incrementMinorVersion(flatVersion) + case minorWildcard: + resultVersion, _ = incrementMajorVersion(flatVersion) + } + } else { + resultVersion = flatVersion + } + + ap = resultOperator + resultVersion + } + newParts = append(newParts, ap) + } + expandedParts = append(expandedParts, newParts) + } + + return expandedParts, nil +} + +func parseComparator(s string) comparator { + switch s { + case "==": + fallthrough + case "": + fallthrough + case "=": + return compEQ + case ">": + return compGT + case ">=": + return compGE + case "<": + return compLT + case "<=": + return compLE + case "!": + fallthrough + case "!=": + return compNE + } + + return nil +} + +// MustParseRange is like ParseRange but panics if the range cannot be parsed. +func MustParseRange(s string) Range { + r, err := ParseRange(s) + if err != nil { + panic(`semver: ParseRange(` + s + `): ` + err.Error()) + } + return r +} diff --git a/vendor/github.com/blang/semver/range_test.go b/vendor/github.com/blang/semver/range_test.go new file mode 100644 index 0000000000..83ee9c7878 --- /dev/null +++ b/vendor/github.com/blang/semver/range_test.go @@ -0,0 +1,581 @@ +package semver + +import ( + "reflect" + "strings" + "testing" +) + +type wildcardTypeTest struct { + input string + wildcardType wildcardType +} + +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 ", []string{">=1.2.3", "<=1.2.3"}}, // Spaces between operator and version + {"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 TestGetWildcardType(t *testing.T) { + wildcardTypeTests := []wildcardTypeTest{ + {"x", majorWildcard}, + {"1.x", minorWildcard}, + {"1.2.x", patchWildcard}, + {"fo.o.b.ar", noneWildcard}, + } + + for _, tc := range wildcardTypeTests { + o := getWildcardType(tc.input) + if o != tc.wildcardType { + t.Errorf("Invalid for case: %q: Expected %q, got: %q", tc.input, tc.wildcardType, o) + } + } +} + +func TestCreateVersionFromWildcard(t *testing.T) { + tests := []struct { + i string + s string + }{ + {"1.2.x", "1.2.0"}, + {"1.x", "1.0.0"}, + } + + for _, tc := range tests { + p := createVersionFromWildcard(tc.i) + if p != tc.s { + t.Errorf("Invalid for case %q: Expected %q, got: %q", tc.i, tc.s, p) + } + } +} + +func TestIncrementMajorVersion(t *testing.T) { + tests := []struct { + i string + s string + }{ + {"1.2.3", "2.2.3"}, + {"1.2", "2.2"}, + {"foo.bar", ""}, + } + + for _, tc := range tests { + p, _ := incrementMajorVersion(tc.i) + if p != tc.s { + t.Errorf("Invalid for case %q: Expected %q, got: %q", tc.i, tc.s, p) + } + } +} + +func TestIncrementMinorVersion(t *testing.T) { + tests := []struct { + i string + s string + }{ + {"1.2.3", "1.3.3"}, + {"1.2", "1.3"}, + {"foo.bar", ""}, + } + + for _, tc := range tests { + p, _ := incrementMinorVersion(tc.i) + if p != tc.s { + t.Errorf("Invalid for case %q: Expected %q, got: %q", tc.i, tc.s, p) + } + } +} + +func TestExpandWildcardVersion(t *testing.T) { + tests := []struct { + i [][]string + o [][]string + }{ + {[][]string{[]string{"foox"}}, nil}, + {[][]string{[]string{">=1.2.x"}}, [][]string{[]string{">=1.2.0"}}}, + {[][]string{[]string{"<=1.2.x"}}, [][]string{[]string{"<1.3.0"}}}, + {[][]string{[]string{">1.2.x"}}, [][]string{[]string{">=1.3.0"}}}, + {[][]string{[]string{"<1.2.x"}}, [][]string{[]string{"<1.2.0"}}}, + {[][]string{[]string{"!=1.2.x"}}, [][]string{[]string{"<1.2.0", ">=1.3.0"}}}, + {[][]string{[]string{">=1.x"}}, [][]string{[]string{">=1.0.0"}}}, + {[][]string{[]string{"<=1.x"}}, [][]string{[]string{"<2.0.0"}}}, + {[][]string{[]string{">1.x"}}, [][]string{[]string{">=2.0.0"}}}, + {[][]string{[]string{"<1.x"}}, [][]string{[]string{"<1.0.0"}}}, + {[][]string{[]string{"!=1.x"}}, [][]string{[]string{"<1.0.0", ">=2.0.0"}}}, + {[][]string{[]string{"1.2.x"}}, [][]string{[]string{">=1.2.0", "<1.3.0"}}}, + {[][]string{[]string{"1.x"}}, [][]string{[]string{">=1.0.0", "<2.0.0"}}}, + } + + for _, tc := range tests { + o, _ := expandWildcardVersion(tc.i) + 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}, + {"fo.ob.ar.x", 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}, + }}, + // Wildcard expressions + {">1.x", []tv{ + {"0.1.9", false}, + {"1.2.6", false}, + {"1.9.0", false}, + {"2.0.0", true}, + }}, + {">1.2.x", []tv{ + {"1.1.9", false}, + {"1.2.6", false}, + {"1.3.0", true}, + }}, + // 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.x || >=2.0.x <2.2.x", []tv{ + {"0.9.2", false}, + {"1.2.2", true}, + {"2.0.0", true}, + {"2.1.8", true}, + {"2.2.0", false}, + }}, + {">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) + } +} diff --git a/vendor/github.com/blang/semver/semver.go b/vendor/github.com/blang/semver/semver.go index bbf85ce972..8ee0842e6a 100644 --- a/vendor/github.com/blang/semver/semver.go +++ b/vendor/github.com/blang/semver/semver.go @@ -200,6 +200,29 @@ func Make(s string) (Version, error) { return Parse(s) } +// ParseTolerant allows for certain version specifications that do not strictly adhere to semver +// specs to be parsed by this library. It does so by normalizing versions before passing them to +// Parse(). It currently trims spaces, removes a "v" prefix, and adds a 0 patch number to versions +// with only major and minor components specified +func ParseTolerant(s string) (Version, error) { + s = strings.TrimSpace(s) + s = strings.TrimPrefix(s, "v") + + // Split into major.minor.(patch+pr+meta) + parts := strings.SplitN(s, ".", 3) + if len(parts) < 3 { + if strings.ContainsAny(parts[len(parts)-1], "+-") { + return Version{}, errors.New("Short version cannot contain PreRelease/Build meta data") + } + for len(parts) < 3 { + parts = append(parts, "0") + } + s = strings.Join(parts, ".") + } + + return Parse(s) +} + // Parse parses version string and returns a validated Version or error func Parse(s string) (Version, error) { if len(s) == 0 { diff --git a/vendor/github.com/blang/semver/semver_test.go b/vendor/github.com/blang/semver/semver_test.go index e56ebce057..b3e1fd43c7 100644 --- a/vendor/github.com/blang/semver/semver_test.go +++ b/vendor/github.com/blang/semver/semver_test.go @@ -30,6 +30,13 @@ var formatTests = []formatTest{ {Version{1, 2, 3, []PRVersion{prstr("alpha"), prstr("b-eta")}, nil}, "1.2.3-alpha.b-eta"}, } +var tolerantFormatTests = []formatTest{ + {Version{1, 2, 3, nil, nil}, "v1.2.3"}, + {Version{1, 2, 3, nil, nil}, " 1.2.3 "}, + {Version{1, 2, 0, nil, nil}, "1.2"}, + {Version{1, 0, 0, nil, nil}, "1"}, +} + func TestStringer(t *testing.T) { for _, test := range formatTests { if res := test.v.String(); res != test.result { @@ -50,6 +57,18 @@ func TestParse(t *testing.T) { } } +func TestParseTolerant(t *testing.T) { + for _, test := range tolerantFormatTests { + if v, err := ParseTolerant(test.result); err != nil { + t.Errorf("Error parsing %q: %q", test.result, err) + } else if comp := v.Compare(test.v); comp != 0 { + t.Errorf("Parsing, expected %q but got %q, comp: %d ", test.v, v, comp) + } else if err := v.Validate(); err != nil { + t.Errorf("Error validating parsed version %q: %q", test.v, err) + } + } +} + func TestMustParse(t *testing.T) { _ = MustParse("32.2.1-alpha") } @@ -184,6 +203,19 @@ func TestWrongFormat(t *testing.T) { } } +var wrongTolerantFormatTests = []wrongformatTest{ + {nil, "1.0+abc"}, + {nil, "1.0-rc.1"}, +} + +func TestWrongTolerantFormat(t *testing.T) { + for _, test := range wrongTolerantFormatTests { + if res, err := ParseTolerant(test.str); err == nil { + t.Errorf("Parsing wrong format version %q, expected error but got %q", test.str, res) + } + } +} + func TestCompareHelper(t *testing.T) { v := Version{1, 0, 0, []PRVersion{prstr("alpha")}, nil} v1 := Version{1, 0, 0, nil, nil} @@ -319,6 +351,15 @@ func BenchmarkParseAverage(b *testing.B) { } } +func BenchmarkParseTolerantAverage(b *testing.B) { + l := len(tolerantFormatTests) + b.ReportAllocs() + b.ResetTimer() + for n := 0; n < b.N; n++ { + ParseTolerant(tolerantFormatTests[n%l].result) + } +} + func BenchmarkStringSimple(b *testing.B) { const VERSION = "0.0.1" v, _ := Parse(VERSION)