├── .travis.yml ├── doc.go ├── .gitignore ├── stability_test.go ├── sort.go ├── LICENSE ├── constraint_test.go ├── constraint.go ├── sort_test.go ├── stability.go ├── README.md ├── normalize_test.go ├── normalize.go ├── compare.go ├── group_test.go ├── group.go └── compare_test.go /.travis.yml: -------------------------------------------------------------------------------- 1 | language: go 2 | -------------------------------------------------------------------------------- /doc.go: -------------------------------------------------------------------------------- 1 | /* 2 | Version normalizer and comparison library for go, heavy based on PHP 3 | version_compare function and Version comparsion libs from Composer 4 | (https://github.com/composer/composer) PHP project 5 | */ 6 | package version -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Compiled Object files, Static and Dynamic libs (Shared Objects) 2 | *.o 3 | *.a 4 | *.so 5 | 6 | # Folders 7 | _obj 8 | _test 9 | 10 | # Architecture specific extensions/prefixes 11 | *.[568vq] 12 | [568vq].out 13 | 14 | *.cgo1.go 15 | *.cgo2.c 16 | _cgo_defun.c 17 | _cgo_gotypes.go 18 | _cgo_export.* 19 | 20 | _testmain.go 21 | 22 | *.exe 23 | -------------------------------------------------------------------------------- /stability_test.go: -------------------------------------------------------------------------------- 1 | package version 2 | 3 | import ( 4 | "testing" 5 | ) 6 | 7 | var stabilityValues = map[string]int{ 8 | "1.0": Stable, 9 | "1.0-dev": Development, 10 | "1.0-alpha": Alpha, 11 | "1.0b1": Beta, 12 | "1.0rc1": RC, 13 | } 14 | 15 | func TestGetStability(t *testing.T) { 16 | for in, out := range stabilityValues { 17 | if x := GetStability(in); x != out { 18 | t.Errorf("FAIL: GetStability(%v) = %v: want %v", in, x, out) 19 | } 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /sort.go: -------------------------------------------------------------------------------- 1 | package version 2 | 3 | import ( 4 | "sort" 5 | ) 6 | 7 | // Sorts a string slice of version number strings using version.CompareSimple() 8 | // 9 | // Example: 10 | // version.Sort([]string{"1.10-dev", "1.0rc1", "1.0", "1.0-dev"}) 11 | // Returns []string{"1.0-dev", "1.0rc1", "1.0", "1.10-dev"} 12 | // 13 | func Sort(versionStrings []string) { 14 | versions := versionSlice(versionStrings) 15 | sort.Sort(versions) 16 | } 17 | 18 | type versionSlice []string 19 | 20 | func (s versionSlice) Len() int { 21 | return len(s) 22 | } 23 | 24 | func (s versionSlice) Less(i, j int) bool { 25 | cmp := CompareSimple(Normalize(s[i]), Normalize(s[j])) 26 | if cmp == 0 { 27 | return s[i] < s[j] 28 | } 29 | return cmp < 0 30 | } 31 | 32 | func (s versionSlice) Swap(i, j int) { 33 | tmp := s[j] 34 | s[j] = s[i] 35 | s[i] = tmp 36 | } 37 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2013 Máximo Cuadros 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is furnished 8 | to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in all 11 | copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | THE SOFTWARE. 20 | -------------------------------------------------------------------------------- /constraint_test.go: -------------------------------------------------------------------------------- 1 | package version 2 | 3 | import ( 4 | "testing" 5 | ) 6 | 7 | func TestGetOperator(t *testing.T) { 8 | constraint := NewConstrain("=", "1.0.0") 9 | out := "=" 10 | 11 | if x := constraint.GetOperator(); x != "=" { 12 | t.Errorf("FAIL: GetOperator() = {%s}: want {%s}", x, out) 13 | } 14 | } 15 | 16 | func TestGetVersion(t *testing.T) { 17 | constraint := NewConstrain("=", "1.0.0") 18 | out := "1.0.0" 19 | 20 | if x := constraint.GetVersion(); x != "1.0.0" { 21 | t.Errorf("FAIL: GetVersion() = {%s}: want {%s}", x, out) 22 | } 23 | } 24 | 25 | func TestString(t *testing.T) { 26 | constraint := NewConstrain("=", "1.0.0") 27 | out := "= 1.0.0" 28 | 29 | if x := constraint.String(); x != out { 30 | t.Errorf("FAIL: String() = {%s}: want {%s}", x, out) 31 | } 32 | } 33 | 34 | func TestMatchSuccess(t *testing.T) { 35 | constraint := NewConstrain("=", "1.0.0") 36 | out := true 37 | 38 | if x := constraint.Match("1.0"); x != out { 39 | t.Errorf("FAIL: Match() = {%v}: want {%v}", x, out) 40 | } 41 | } 42 | 43 | func TestMatchFail(t *testing.T) { 44 | constraint := NewConstrain("=", "1.0.0") 45 | out := false 46 | 47 | if x := constraint.Match("2.0"); x != out { 48 | t.Errorf("FAIL: Match() = {%v}: want {%v}", x, out) 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /constraint.go: -------------------------------------------------------------------------------- 1 | package version 2 | 3 | import ( 4 | "strings" 5 | ) 6 | 7 | type Constraint struct { 8 | operator string 9 | version string 10 | } 11 | 12 | // NewConstrain returns a new Constrain and sets operator and version to compare 13 | func NewConstrain(operator, version string) *Constraint { 14 | constraint := new(Constraint) 15 | constraint.SetOperator(operator) 16 | constraint.SetVersion(version) 17 | 18 | return constraint 19 | } 20 | 21 | // Sets operator to compare 22 | func (self *Constraint) SetOperator(operator string) { 23 | self.operator = operator 24 | } 25 | 26 | // GetOperator gets operator to compare 27 | func (self *Constraint) GetOperator() string { 28 | return self.operator 29 | } 30 | 31 | // Sets version to compare 32 | func (self *Constraint) SetVersion(version string) { 33 | self.version = version 34 | } 35 | 36 | // GetVersion gets version to compare 37 | func (self *Constraint) GetVersion() string { 38 | return self.version 39 | } 40 | 41 | // Match a given version againts the constraint 42 | func (self *Constraint) Match(version string) bool { 43 | return Compare(version, self.version, self.operator) 44 | } 45 | 46 | // String returns a string representation 47 | func (self *Constraint) String() string { 48 | return strings.Trim(self.operator+" "+self.version, " ") 49 | } 50 | -------------------------------------------------------------------------------- /sort_test.go: -------------------------------------------------------------------------------- 1 | package version 2 | 3 | import ( 4 | "reflect" 5 | "testing" 6 | ) 7 | 8 | func TestSort(t *testing.T) { 9 | testcases := []struct { 10 | input []string 11 | output []string 12 | }{ 13 | { 14 | input: []string{ 15 | "Package-0.4.tar.gz", 16 | "Package-0.1.tar.gz", 17 | "Package-0.10.1.tar.gz", 18 | "Package-0.10.tar.gz", 19 | "Package-0.2.tar.gz", 20 | "Package-0.3.1.tar.gz", 21 | "Package-0.3.2.tar.gz", 22 | "Package-0.3.tar.gz", 23 | }, 24 | output: []string{ 25 | "Package-0.1.tar.gz", 26 | "Package-0.2.tar.gz", 27 | "Package-0.3.tar.gz", 28 | "Package-0.3.1.tar.gz", 29 | "Package-0.3.2.tar.gz", 30 | "Package-0.4.tar.gz", 31 | "Package-0.10.tar.gz", 32 | "Package-0.10.1.tar.gz", 33 | }, 34 | }, 35 | { 36 | input: []string{ 37 | "1.0-dev", 38 | "1.0a1", 39 | "1.0b1", 40 | "1.0RC1", 41 | "1.0rc1", 42 | "1.0", 43 | "1.0pl1", 44 | "1.1-dev", 45 | "1.2", 46 | "1.10", 47 | }, 48 | output: []string{ 49 | "1.0pl1", 50 | "1.0-dev", 51 | "1.0a1", 52 | "1.0b1", 53 | "1.0RC1", 54 | "1.0rc1", 55 | "1.0", 56 | "1.1-dev", 57 | "1.2", 58 | "1.10", 59 | }, 60 | }, 61 | { 62 | input: []string{ 63 | "v1.0", 64 | "1.0.1", 65 | "dev-master", 66 | }, 67 | output: []string{ 68 | "v1.0", 69 | "1.0.1", 70 | "dev-master", 71 | }, 72 | }, 73 | } 74 | 75 | for _, testcase := range testcases { 76 | Sort(testcase.input) 77 | if !reflect.DeepEqual(testcase.input, testcase.output) { 78 | t.Errorf("Expected output %+v did not match actual %+v", testcase.output, testcase.input) 79 | } 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /stability.go: -------------------------------------------------------------------------------- 1 | package version 2 | 3 | import ( 4 | "regexp" 5 | "strings" 6 | ) 7 | 8 | const ( 9 | Development = iota 10 | Alpha 11 | Beta 12 | RC 13 | Stable 14 | ) 15 | 16 | func expandStability(stability string) string { 17 | stability = strings.ToLower(stability) 18 | 19 | switch stability { 20 | case "a": 21 | return "alpha" 22 | case "b": 23 | return "beta" 24 | case "p": 25 | return "patch" 26 | case "pl": 27 | return "patch" 28 | case "rc": 29 | return "RC" 30 | } 31 | 32 | return stability 33 | } 34 | 35 | func parseStability(version string) string { 36 | version = regexp.MustCompile(`(?i)#.+$`).ReplaceAllString(version, " ") 37 | version = strings.ToLower(version) 38 | 39 | if strings.HasPrefix(version, "dev-") || strings.HasSuffix(version, "-dev") { 40 | return "dev" 41 | } 42 | 43 | result := RegFind(`(?i)^v?(\d{1,3})(\.\d+)?(\.\d+)?(\.\d+)?`+modifierRegex+`$`, version) 44 | if result != nil { 45 | if len(result) > 3 { 46 | return "dev" 47 | } 48 | } 49 | 50 | if result[1] != "" { 51 | if "beta" == result[1] || "b" == result[1] { 52 | return "beta" 53 | } 54 | if "alpha" == result[1] || "a" == result[1] { 55 | return "alpha" 56 | } 57 | if "rc" == result[1] { 58 | return "RC" 59 | } 60 | } 61 | 62 | return "stable" 63 | } 64 | 65 | func GetStability(version string) int { 66 | result := RegFind(`(?i)(stable|RC|beta|alpha|dev)`, Normalize(version)) 67 | if len(result) == 0 { 68 | return Stable 69 | } 70 | 71 | switch result[1] { 72 | case "dev": 73 | return Development 74 | case "alpha": 75 | return Alpha 76 | case "beta": 77 | return Beta 78 | case "RC": 79 | return RC 80 | } 81 | 82 | return Stable 83 | } 84 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | go-version [![Build Status](https://travis-ci.org/mcuadros/go-version.svg?branch=master)](https://travis-ci.org/mcuadros/go-version) [![GoDoc](https://godoc.org/github.com/mcuadros/go-version?status.svg)](http://godoc.org/github.com/mcuadros/go-version) 2 | ============================== 3 | 4 | Version normalizer and comparison library for go, heavy based on PHP version_compare function and Version comparsion libs from [Composer](https://github.com/composer/composer) PHP project 5 | 6 | Installation 7 | ------------ 8 | 9 | The recommended way to install go-version 10 | 11 | ``` 12 | go get github.com/mcuadros/go-version 13 | ``` 14 | 15 | Examples 16 | -------- 17 | 18 | How import the package 19 | 20 | ```go 21 | import ( 22 | "github.com/mcuadros/go-version" 23 | ) 24 | ``` 25 | 26 | `version.Normalize()`: Normalizes a version string to be able to perform comparisons on it 27 | 28 | ```go 29 | version.Normalize("10.4.13-b") 30 | //Returns: 10.4.13.0-beta 31 | ``` 32 | 33 | 34 | `version.CompareSimple()`: Compares two normalizated version number strings 35 | 36 | ```go 37 | version.CompareSimple("1.2", "1.0.1") 38 | //Returns: 1 39 | 40 | version.CompareSimple("1.0rc1", "1.0") 41 | //Returns: -1 42 | ``` 43 | 44 | 45 | `version.Compare()`: Compares two normalizated version number strings, for a particular relationship 46 | 47 | ```go 48 | version.Compare("1.0-dev", "1.0", "<") 49 | //Returns: true 50 | 51 | version.Compare("1.0rc1", "1.0", ">=") 52 | //Returns: false 53 | 54 | version.Compare("2.3.4", "v3.1.2", "<") 55 | //Returns: true 56 | ``` 57 | 58 | `version.ConstrainGroup.Match()`: Match a given version againts a group of constrains, read about constraint string format at [Composer documentation](http://getcomposer.org/doc/01-basic-usage.md#package-versions) 59 | 60 | ```go 61 | c := version.NewConstrainGroupFromString(">2.0,<=3.0") 62 | c.Match("2.5.0beta") 63 | //Returns: true 64 | 65 | c := version.NewConstrainGroupFromString("~1.2.3") 66 | c.Match("1.2.3.5") 67 | //Returns: true 68 | ``` 69 | 70 | `version.Sort()`: Sorts a string slice of version number strings using version.CompareSimple() 71 | 72 | ```go 73 | version.Sort([]string{"1.10-dev", "1.0rc1", "1.0", "1.0-dev"}) 74 | //Returns []string{"1.0-dev", "1.0rc1", "1.0", "1.10-dev"} 75 | ``` 76 | 77 | License 78 | ------- 79 | 80 | MIT, see [LICENSE](LICENSE) 81 | -------------------------------------------------------------------------------- /normalize_test.go: -------------------------------------------------------------------------------- 1 | package version 2 | 3 | import ( 4 | "testing" 5 | ) 6 | 7 | var versions = map[string]string{ 8 | "1-stable": "1.0.0.0", 9 | "1.0.0": "1.0.0.0", 10 | "1.2.3.4": "1.2.3.4", 11 | "1.0.0RC1dev": "1.0.0.0-RC1-dev", 12 | "1.0.0-rC15-dev": "1.0.0.0-RC15-dev", 13 | "1.0.0.RC.15-dev": "1.0.0.0-RC15-dev", 14 | "1.0.0-rc1": "1.0.0.0-RC1", 15 | "1.0.0.pl3-dev": "1.0.0.0-patch3-dev", 16 | "1.0-dev": "1.0.0.0-dev", 17 | "0": "0.0.0.0", 18 | "10.4.13-beta": "10.4.13.0-beta", 19 | "10.4.13-b": "10.4.13.0-beta", 20 | "10.4.13-b5": "10.4.13.0-beta5", 21 | "v1.0.0": "1.0.0.0", 22 | "v20100102": "20100102", 23 | "2010.01": "2010-01", 24 | "2010.01.02": "2010-01-02", 25 | "2010-01-02": "2010-01-02", 26 | "2010-01-02.5": "2010-01-02-5", 27 | "20100102-203040": "20100102-203040", 28 | "20100102203040-10": "20100102203040-10", 29 | "20100102-203040-p1": "20100102-203040-patch1", 30 | "dev-master": "9999999-dev", 31 | "dev-trunk": "9999999-dev", 32 | "1.x-dev": "1.9999999.9999999.9999999-dev", 33 | "dev-feature-foo": "dev-feature-foo", 34 | "DEV-FOOBAR": "dev-FOOBAR", 35 | "dev-feature/foo": "dev-feature/foo", 36 | "dev-master as 1.0.0": "9999999-dev", 37 | } 38 | 39 | func TestNormalize(t *testing.T) { 40 | for in, out := range versions { 41 | if x := Normalize(in); x != out { 42 | t.Errorf("FAIL: Normalize(%v) = %v: want %v", in, x, out) 43 | } 44 | } 45 | } 46 | 47 | var branches = map[string]string{ 48 | "v1.x": "1.9999999.9999999.9999999-dev", 49 | "v1.*": "1.9999999.9999999.9999999-dev", 50 | "v1.0": "1.0.9999999.9999999-dev", 51 | "2.0": "2.0.9999999.9999999-dev", 52 | "v1.0.x": "1.0.9999999.9999999-dev", 53 | "v1.0.3.*": "1.0.3.9999999-dev", 54 | "v2.4.0": "2.4.0.9999999-dev", 55 | "2.4.4": "2.4.4.9999999-dev", 56 | "master": "9999999-dev", 57 | "trunk": "9999999-dev", 58 | "feature-a": "dev-feature-a", 59 | "FOOBAR": "dev-FOOBAR", 60 | } 61 | 62 | func TestNormalizeBranch(t *testing.T) { 63 | for in, out := range branches { 64 | if x := normalizeBranch(in); x != out { 65 | t.Errorf("FAIL: normalizeBranch(%v) = %v: want %v", in, x, out) 66 | } 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /normalize.go: -------------------------------------------------------------------------------- 1 | package version 2 | 3 | import ( 4 | "regexp" 5 | "strings" 6 | ) 7 | 8 | var modifierRegex = `[._-]?(?:(stable|beta|b|RC|alpha|a|patch|pl|p)(?:[.-]?(\d+))?)?([.-]?dev)?` 9 | 10 | var regexpMasterLikeBranches = regexp.MustCompile(`^(?:dev-)?(?:master|trunk|default)$`) 11 | var regexpBranchNormalize = regexp.MustCompile(`(?i)^v?(\d+)(\.(?:\d+|[x*]))?(\.(?:\d+|[x*]))?(\.(?:\d+|[x*]))?$`) 12 | 13 | // Normalizes a version string to be able to perform comparisons on it 14 | // 15 | // Example: 16 | // version.Normalize("10.4.13-b") 17 | // Returns: 10.4.13.0-beta 18 | // 19 | func Normalize(version string) string { 20 | 21 | // ignore aliases and just assume the alias is required instead of the source 22 | result := RegFind(`^([^,\s]+) +as +([^,\s]+)$`, version) 23 | if result != nil { 24 | version = result[1] 25 | } 26 | 27 | // match master-like branches 28 | if regexpMasterLikeBranches.MatchString(strings.ToLower(version)) { 29 | return "9999999-dev" 30 | } 31 | 32 | if strings.HasPrefix(strings.ToLower(version), "dev-") { 33 | return "dev-" + version[4:len(version)] 34 | } 35 | 36 | index := 0 37 | 38 | // match classical versioning 39 | result = RegFind(`(?i)^v?(\d{1,3})(\.\d+)?(\.\d+)?(\.\d+)?`+modifierRegex+`$`, version) 40 | if result != nil { 41 | version = "" 42 | for _, val := range result[1:5] { 43 | if val != "" { 44 | version = version + val 45 | } else { 46 | version = version + ".0" 47 | } 48 | } 49 | 50 | index = 5 51 | } else { 52 | // match date-based versioning 53 | result = RegFind(`(?i)^v?(\d{4}(?:[.:-]?\d{2}){1,6}(?:[.:-]?\d{1,3})?)`+modifierRegex+`$`, version) 54 | if result != nil { 55 | version = regexp.MustCompile(`\D`).ReplaceAllString(result[1], "-") 56 | index = 2 57 | } 58 | } 59 | 60 | if index != 0 { 61 | if result[index] != "" { 62 | if result[index] == "stable" { 63 | return version 64 | } 65 | 66 | version = version + "-" + expandStability(result[index]) 67 | if result[index+1] != "" { 68 | version = version + result[index+1] 69 | } 70 | } 71 | 72 | if result[index+2] != "" { 73 | version = version + "-dev" 74 | } 75 | 76 | return version 77 | } 78 | 79 | result = RegFind(`(?i)(.*?)[.-]?dev$`, version) 80 | if result != nil { 81 | return normalizeBranch(result[1]) 82 | } 83 | 84 | return version 85 | } 86 | 87 | func normalizeBranch(name string) string { 88 | name = strings.Trim(name, " ") 89 | 90 | if name == "master" || name == "trunk" || name == "default" { 91 | return Normalize(name) 92 | } 93 | 94 | replace := strings.NewReplacer("*", "9999999", "x", "9999999") 95 | 96 | matched := regexpBranchNormalize.FindAllStringSubmatch(name, -1) 97 | if matched != nil { 98 | name = "" 99 | for _, val := range matched[0][1:5] { 100 | if val != "" { 101 | name = name + replace.Replace(val) 102 | } else { 103 | name = name + ".9999999" 104 | } 105 | } 106 | 107 | return name + "-dev" 108 | 109 | } 110 | 111 | if strings.HasSuffix(strings.ToLower(name), "-dev") { 112 | return name 113 | } 114 | 115 | return "dev-" + name 116 | } 117 | -------------------------------------------------------------------------------- /compare.go: -------------------------------------------------------------------------------- 1 | package version 2 | 3 | import ( 4 | "regexp" 5 | "strconv" 6 | "strings" 7 | ) 8 | 9 | var regexpSigns = regexp.MustCompile(`[_\-+]`) 10 | var regexpDotBeforeDigit = regexp.MustCompile(`([^.\d]+)`) 11 | var regexpMultipleDots = regexp.MustCompile(`\.{2,}`) 12 | 13 | var specialForms = map[string]int{ 14 | "SNAPSHOT": -7, 15 | "snapshot": -7, 16 | "dev": -6, 17 | "alpha": -5, 18 | "a": -5, 19 | "beta": -4, 20 | "b": -4, 21 | "RC": -3, 22 | "rc": -3, 23 | "#": -2, 24 | "p": 1, 25 | "pl": 1, 26 | } 27 | 28 | var unknownForm int = -7 29 | 30 | // Compare compares two version number strings, for a particular relationship 31 | // 32 | // Usage 33 | // version.Compare("2.3.4", "v3.1.2", "<") 34 | // Returns: true 35 | // 36 | // version.Compare("1.0rc1", "1.0", ">=") 37 | // Returns: false 38 | func Compare(version1, version2, operator string) bool { 39 | version1N := Normalize(version1) 40 | version2N := Normalize(version2) 41 | 42 | return CompareNormalized(version1N, version2N, operator) 43 | } 44 | 45 | // CompareNormalized compares two normalizated version number strings, for a particular relationship 46 | // 47 | // The function first replaces _, - and + with a dot . in the version strings 48 | // and also inserts dots . before and after any non number so that for example 49 | // '4.3.2RC1' becomes '4.3.2.RC.1'. 50 | // 51 | // Then it splits the results like if you were using Split(version, '.'). 52 | // Then it compares the parts starting from left to right. If a part contains 53 | // special version strings these are handled in the following order: any string 54 | // not found in this list: 55 | // < dev < alpha = a < beta = b < RC = rc < # < pl = p. 56 | // 57 | // Usage 58 | // version.CompareNormalized("1.0-dev", "1.0", "<") 59 | // Returns: true 60 | // 61 | // version.CompareNormalized("1.0rc1", "1.0", ">=") 62 | // Returns: false 63 | // 64 | // version.CompareNormalized("1.0", "1.0b1", "ge") 65 | // Returns: true 66 | func CompareNormalized(version1, version2, operator string) bool { 67 | compare := CompareSimple(version1, version2) 68 | 69 | switch { 70 | case operator == ">" || operator == "gt": 71 | return compare > 0 72 | case operator == ">=" || operator == "ge": 73 | return compare >= 0 74 | case operator == "<=" || operator == "le": 75 | return compare <= 0 76 | case operator == "==" || operator == "=" || operator == "eq": 77 | return compare == 0 78 | case operator == "<>" || operator == "!=" || operator == "ne": 79 | return compare != 0 80 | case operator == "" || operator == "<" || operator == "lt": 81 | return compare < 0 82 | } 83 | 84 | return false 85 | } 86 | 87 | // CompareSimple compares two normalizated version number strings 88 | // 89 | // Just the same of CompareVersion but return a int result, 0 if both version 90 | // are equal, 1 if the right side is bigger and -1 if the right side is lower 91 | // 92 | // Usage 93 | // version.CompareSimple("1.2", "1.0.1") 94 | // Returns: 1 95 | // 96 | // version.CompareSimple("1.0rc1", "1.0") 97 | // Returns: -1 98 | func CompareSimple(version1, version2 string) int { 99 | var x, r, l int = 0, 0, 0 100 | 101 | v1, v2 := prepVersion(version1), prepVersion(version2) 102 | len1, len2 := len(v1), len(v2) 103 | 104 | if len1 > len2 { 105 | x = len1 106 | } else { 107 | x = len2 108 | } 109 | 110 | for i := 0; i < x; i++ { 111 | if i < len1 && i < len2 { 112 | if v1[i] == v2[i] { 113 | continue 114 | } 115 | } 116 | 117 | r = 0 118 | if i < len1 { 119 | r = numVersion(v1[i]) 120 | } 121 | 122 | l = 0 123 | if i < len2 { 124 | l = numVersion(v2[i]) 125 | } 126 | 127 | if r < l { 128 | return -1 129 | } else if r > l { 130 | return 1 131 | } 132 | } 133 | 134 | return 0 135 | } 136 | 137 | func prepVersion(version string) []string { 138 | if len(version) == 0 { 139 | return []string{""} 140 | } 141 | 142 | version = regexpSigns.ReplaceAllString(version, ".") 143 | version = regexpDotBeforeDigit.ReplaceAllString(version, ".$1.") 144 | version = regexpMultipleDots.ReplaceAllString(version, ".") 145 | 146 | return strings.Split(version, ".") 147 | } 148 | 149 | func numVersion(value string) int { 150 | if value == "" { 151 | return 0 152 | } 153 | 154 | if number, err := strconv.Atoi(value); err == nil { 155 | return number 156 | } 157 | 158 | if special, ok := specialForms[value]; ok { 159 | return special 160 | } 161 | 162 | return unknownForm 163 | } 164 | 165 | func ValidSimpleVersionFormat(value string) bool { 166 | normalized := Normalize(value) 167 | for _, component := range prepVersion(normalized) { 168 | if numVersion(component) == unknownForm { 169 | return false 170 | } 171 | } 172 | return true 173 | } 174 | -------------------------------------------------------------------------------- /group_test.go: -------------------------------------------------------------------------------- 1 | package version 2 | 3 | import ( 4 | "strings" 5 | "testing" 6 | ) 7 | 8 | var anyConstraints = map[string][]*Constraint{ 9 | "*": []*Constraint{}, 10 | "*.*": []*Constraint{}, 11 | "*.x.*": []*Constraint{}, 12 | "x.x.x.*": []*Constraint{}, 13 | } 14 | 15 | func TestParseAnyConstraints(t *testing.T) { 16 | for in, out := range anyConstraints { 17 | constraint := NewConstrainGroupFromString(in) 18 | if x := constraint.GetConstraints(); len(x) != 0 { 19 | t.Errorf("FAIL: fromString(%v) = {%s}: want {%s}", in, x, out) 20 | } 21 | } 22 | } 23 | 24 | var simpleConstraints = map[string][]*Constraint{ 25 | "<>1.0.0": []*Constraint{{"<>", "1.0.0.0"}}, 26 | "!=1.0.0": []*Constraint{{"!=", "1.0.0.0"}}, 27 | ">1.0.0": []*Constraint{{">", "1.0.0.0"}}, 28 | "<1.2.3.4": []*Constraint{{"<", "1.2.3.4-dev"}}, 29 | "<=1.2.3": []*Constraint{{"<=", "1.2.3.0"}}, 30 | ">=1.2.3": []*Constraint{{">=", "1.2.3.0"}}, 31 | "=1.2.3": []*Constraint{{"=", "1.2.3.0"}}, 32 | "==1.2.3": []*Constraint{{"==", "1.2.3.0"}}, 33 | "1.2.3": []*Constraint{{"=", "1.2.3.0"}}, 34 | "=1.0": []*Constraint{{"=", "1.0.0.0"}}, 35 | "1.2.3b5": []*Constraint{{"=", "1.2.3.0-beta5"}}, 36 | ">= 1.2.3": []*Constraint{{">=", "1.2.3.0"}}, 37 | ">=dev-master": []*Constraint{{">=", "9999999-dev"}}, 38 | "dev-master": []*Constraint{{"=", "9999999-dev"}}, 39 | "dev-feature-a": []*Constraint{{"=", "dev-feature-a"}}, 40 | "dev-some-fix": []*Constraint{{"=", "dev-some-fix"}}, 41 | "dev-CAPS": []*Constraint{{"=", "dev-CAPS"}}, 42 | "dev-master as 1.0.0": []*Constraint{{"=", "9999999-dev"}}, 43 | "<1.2.3.4-stable": []*Constraint{{"<", "1.2.3.4"}}, 44 | 45 | "<=3.0@dev": []*Constraint{{"<=", "3.0.0.0"}}, 46 | "1.0@dev": []*Constraint{{"=", "1.0.0.0"}}, //IgnoresStabilityFlag 47 | "1.0.x-dev#abcd123": []*Constraint{{"=", "1.0.9999999.9999999-dev"}}, //IgnoresReferenceOnDevVersion 48 | "1.0.x-dev#trunk/@123": []*Constraint{{"=", "1.0.9999999.9999999-dev"}}, //IgnoresReferenceOnDevVersion 49 | //"1.0#abcd123": []string{"=", "1.0.0.0"}, //FailsOnBadReference 50 | //"1.0#trunk/@123": []string{"=", "1.0.0.0"}, //FailsOnBadReference 51 | } 52 | 53 | func TestParseConstraints(t *testing.T) { 54 | for in, out := range simpleConstraints { 55 | constraint := NewConstrainGroupFromString(in) 56 | if x := constraint.GetConstraints(); x[0].String() != out[0].String() { 57 | t.Errorf("FAIL: parseConstraints(%v) = {%s}: want {%s}", in, x, out) 58 | } 59 | } 60 | } 61 | 62 | var wildcardConstraints = map[string][]*Constraint{ 63 | "2.*": []*Constraint{{">", "1.9999999.9999999.9999999"}, {"<", "2.9999999.9999999.9999999"}}, 64 | "20.*": []*Constraint{{">", "19.9999999.9999999.9999999"}, {"<", "20.9999999.9999999.9999999"}}, 65 | "2.0.*": []*Constraint{{">", "1.9999999.9999999.9999999"}, {"<", "2.0.9999999.9999999"}}, 66 | "2.2.x": []*Constraint{{">", "2.1.9999999.9999999"}, {"<", "2.2.9999999.9999999"}}, 67 | "2.10.x": []*Constraint{{">", "2.9.9999999.9999999"}, {"<", "2.10.9999999.9999999"}}, 68 | "2.1.3.*": []*Constraint{{">", "2.1.2.9999999"}, {"<", "2.1.3.9999999"}}, 69 | "0.*": []*Constraint{{"<", "0.9999999.9999999.9999999"}}, 70 | } 71 | 72 | func TestParseConstraintsWildcardConstraints(t *testing.T) { 73 | for in, out := range wildcardConstraints { 74 | constraint := NewConstrainGroupFromString(in) 75 | if x := constraint.GetConstraints(); x[0].String() != out[0].String() && x[1].String() != out[1].String() { 76 | t.Errorf("FAIL: parseConstraints(%v) = {%s}: want {%s}", in, x, out) 77 | } 78 | } 79 | } 80 | 81 | var tildeConstraints = map[string][]*Constraint{ 82 | "~1": []*Constraint{{">=", "1.0.0.0"}, {"<", "2.0.0.0-dev"}}, 83 | "~1.2": []*Constraint{{">=", "1.2.0.0"}, {"<", "2.0.0.0-dev"}}, 84 | "~1.2.3": []*Constraint{{">=", "1.2.3.0"}, {"<", "1.3.0.0-dev"}}, 85 | "~1.2.3.4": []*Constraint{{">=", "1.2.3.4"}, {"<", "1.2.4.0-dev"}}, 86 | "~1.2-beta": []*Constraint{{">=", "1.2.0.0-beta"}, {"<", "2.0.0.0-dev"}}, 87 | "~1.2-b2": []*Constraint{{">=", "1.2.0.0-beta2"}, {"<", "2.0.0.0-dev"}}, 88 | "~1.2-BETA2": []*Constraint{{">=", "1.2.0.0-beta2"}, {"<", "2.0.0.0-dev"}}, 89 | "~1.2.2-dev": []*Constraint{{">=", "1.2.2.0-dev"}, {"<", "1.3.0.0-dev"}}, 90 | } 91 | 92 | func TestParseConstraintsTildeConstraints(t *testing.T) { 93 | for in, out := range tildeConstraints { 94 | constraint := NewConstrainGroupFromString(in) 95 | if x := constraint.GetConstraints(); x[0].String() != out[0].String() && x[1].String() != out[1].String() { 96 | t.Errorf("FAIL: parseConstraints(%v) = {%s}: want {%s}", in, x, out) 97 | } 98 | } 99 | } 100 | 101 | var multiConstraints = map[string][]*Constraint{ 102 | ">2.0,<=3.0": []*Constraint{{">", "2.0.0.0"}, {"<=", "3.0.0.0"}}, 103 | ">2.0@stable,<=3.0@dev": []*Constraint{{">", "2.0.0.0"}, {"<=", "3.0.0.0-dev"}}, 104 | } 105 | 106 | func TestParseConstraintsMultiConstraints(t *testing.T) { 107 | for in, out := range multiConstraints { 108 | constraint := NewConstrainGroupFromString(in) 109 | if x := constraint.GetConstraints(); x[0].String() != out[0].String() && x[1].String() != out[1].String() { 110 | t.Errorf("FAIL: parseConstraints(%v) = {%s}: want {%s}", in, x, out) 111 | } 112 | } 113 | } 114 | 115 | var miscConstraints = map[string]bool{ 116 | "*|1.0": true, 117 | ">2.0,<=3.0|2.5.0beta": true, 118 | ">2.0,<=3.0|3.5.0beta": false, 119 | ">=2.2.3,<2.4-dev|2.3.3": true, 120 | "~1.2.3|1.2.3.5": true, 121 | "~1.2.3|1.4.3.5": false, 122 | "2.0.*|2.0.5": true, 123 | "2.0.*|2.1.5": false, 124 | "2.0.5|2.0.5": true, 125 | "2.0.5|2.0.9": false, 126 | ">2.0.5|2.0.9": true, 127 | "<2.0.5|2.0.9": false, 128 | ">=dev-master|dev-master": true, 129 | } 130 | 131 | func TestMatch(t *testing.T) { 132 | for in, out := range miscConstraints { 133 | tmp := strings.Split(in, "|") 134 | 135 | constraint := NewConstrainGroupFromString(tmp[0]) 136 | if x := constraint.Match(tmp[1]); x != out { 137 | t.Errorf("FAIL: Match(%v) = {%v}: want {%v}", in, x, out) 138 | } 139 | } 140 | } 141 | 142 | func TestAddConstraint(t *testing.T) { 143 | group := NewConstrainGroup() 144 | group.AddConstraint(NewConstrain("=", "1.0.0")) 145 | 146 | constraints := group.GetConstraints() 147 | if x := constraints[0].GetOperator(); x != "=" { 148 | t.Errorf("FAIL: AddConstraintOperator() = {%s}: want {%s}", x, "=") 149 | } 150 | 151 | if x := constraints[0].GetVersion(); x != "1.0.0" { 152 | t.Errorf("FAIL: AddConstraintVersion() = {%s}: want {%s}", x, "1.0.0") 153 | } 154 | } 155 | -------------------------------------------------------------------------------- /group.go: -------------------------------------------------------------------------------- 1 | package version 2 | 3 | import ( 4 | "regexp" 5 | "strconv" 6 | "strings" 7 | "sync" 8 | ) 9 | 10 | type ConstraintGroup struct { 11 | constraints []*Constraint 12 | } 13 | 14 | // NewConstrainGroup returns a new NewConstrainGroup 15 | func NewConstrainGroup() *ConstraintGroup { 16 | group := new(ConstraintGroup) 17 | 18 | return group 19 | } 20 | 21 | // NewConstrainGroupFromString returns a new NewConstrainGroup and create the constraints based on a string 22 | // 23 | // Version constraints can be specified in a few different ways: 24 | // 25 | // Exact version: You can specify the exact version of a package, for 26 | // example 1.0.2. 27 | // 28 | // Range: By using comparison operators you can specify ranges of valid versions. 29 | // Valid operators are >, >=, <, <=, !=. An example range would be >=1.0. You can 30 | // define multiple ranges, separated by a comma: >=1.0,<2.0. 31 | // 32 | // Wildcard: You can specify a pattern with a * wildcard. 1.0.* is the equivalent 33 | // of >=1.0,<1.1. 34 | // 35 | // Next Significant Release (Tilde Operator): The ~ operator is best explained by 36 | // example: ~1.2 is equivalent to >=1.2,<2.0, while ~1.2.3 is equivalent to 37 | // >=1.2.3,<1.3. As you can see it is mostly useful for projects respecting 38 | // semantic versioning. A common usage would be to mark the minimum minor 39 | // version you depend on, like ~1.2 (which allows anything up to, but not 40 | // including, 2.0). Since in theory there should be no backwards compatibility 41 | // breaks until 2.0, that works well. Another way of looking at it is that 42 | // using ~ specifies a minimum version, but allows the last digit specified 43 | // to go up. 44 | // 45 | // By default only stable releases are taken into consideration. If you would like 46 | // to also get RC, beta, alpha or dev versions of your dependencies you can do so 47 | // using stability flags. To change that for all packages instead of doing per 48 | // dependency you can also use the minimum-stability setting. 49 | // 50 | // From: http://getcomposer.org/doc/01-basic-usage.md#package-versions 51 | func NewConstrainGroupFromString(name string) *ConstraintGroup { 52 | group := new(ConstraintGroup) 53 | group.fromString(name) 54 | 55 | return group 56 | } 57 | 58 | // AddConstraint adds a Contraint to the group 59 | func (self *ConstraintGroup) AddConstraint(constraint ...*Constraint) { 60 | if self.constraints == nil { 61 | self.constraints = make([]*Constraint, 0) 62 | } 63 | 64 | self.constraints = append(self.constraints, constraint...) 65 | } 66 | 67 | // GetConstraints returns all the constraints 68 | func (self *ConstraintGroup) GetConstraints() []*Constraint { 69 | return self.constraints 70 | } 71 | 72 | // Match a given version againts the group 73 | // 74 | // Usage 75 | // c := version.NewConstrainGroupFromString(">2.0,<=3.0") 76 | // c.Match("2.5.0beta") 77 | // Returns: true 78 | // 79 | // c := version.NewConstrainGroupFromString("~1.2.3") 80 | // c.Match("1.2.3.5") 81 | // Returns: true 82 | func (self *ConstraintGroup) Match(version string) bool { 83 | for _, constraint := range self.constraints { 84 | if constraint.Match(version) == false { 85 | return false 86 | } 87 | } 88 | 89 | return true 90 | } 91 | 92 | func (self *ConstraintGroup) fromString(constraint string) bool { 93 | result := RegFind(`(?i)^([^,\s]*?)@(stable|RC|beta|alpha|dev)$`, constraint) 94 | if result != nil { 95 | constraint = result[1] 96 | if constraint == "" { 97 | constraint = "*" 98 | } 99 | } 100 | 101 | result = RegFind(`(?i)^(dev-[^,\s@]+?|[^,\s@]+?\.x-dev)#.+$`, constraint) 102 | if result != nil { 103 | if result[1] != "" { 104 | constraint = result[1] 105 | } 106 | } 107 | 108 | constraints := RegSplit(`\s*,\s*`, strings.Trim(constraint, " ")) 109 | 110 | if len(constraints) > 1 { 111 | for _, part := range constraints { 112 | self.AddConstraint(self.parseConstraint(part)...) 113 | } 114 | 115 | return true 116 | } 117 | 118 | self.AddConstraint(self.parseConstraint(constraints[0])...) 119 | 120 | return true 121 | } 122 | 123 | func (self *ConstraintGroup) parseConstraint(constraint string) []*Constraint { 124 | 125 | stabilityModifier := "" 126 | 127 | result := RegFind(`(?i)^([^,\s]+?)@(stable|RC|beta|alpha|dev)$`, constraint) 128 | if result != nil { 129 | constraint = result[1] 130 | if result[2] != "stable" { 131 | stabilityModifier = result[2] 132 | } 133 | } 134 | 135 | result = RegFind(`^[x*](\.[x*])*$`, constraint) 136 | if result != nil { 137 | return make([]*Constraint, 0) 138 | } 139 | 140 | highVersion := "" 141 | lowVersion := "" 142 | 143 | result = RegFind(`(?i)^~(\d+)(?:\.(\d+))?(?:\.(\d+))?(?:\.(\d+))?`+modifierRegex+`?$`, constraint) 144 | if result != nil { 145 | if len(result) > 4 && result[4] != "" { 146 | last, _ := strconv.Atoi(result[3]) 147 | highVersion = result[1] + "." + result[2] + "." + strconv.Itoa(last+1) + ".0-dev" 148 | lowVersion = result[1] + "." + result[2] + "." + result[3] + "." + result[4] 149 | } else if len(result) > 3 && result[3] != "" { 150 | last, _ := strconv.Atoi(result[2]) 151 | highVersion = result[1] + "." + strconv.Itoa(last+1) + ".0.0-dev" 152 | lowVersion = result[1] + "." + result[2] + "." + result[3] + ".0" 153 | } else { 154 | last, _ := strconv.Atoi(result[1]) 155 | highVersion = strconv.Itoa(last+1) + ".0.0.0-dev" 156 | if len(result) > 2 && result[2] != "" { 157 | lowVersion = result[1] + "." + result[2] + ".0.0" 158 | } else { 159 | lowVersion = result[1] + ".0.0.0" 160 | } 161 | } 162 | 163 | if len(result) > 5 && result[5] != "" { 164 | lowVersion = lowVersion + "-" + expandStability(result[5]) 165 | 166 | } 167 | 168 | if len(result) > 6 && result[6] != "" { 169 | lowVersion = lowVersion + result[6] 170 | } 171 | 172 | if len(result) > 7 && result[7] != "" { 173 | lowVersion = lowVersion + "-dev" 174 | } 175 | 176 | return []*Constraint{ 177 | {">=", lowVersion}, 178 | {"<", highVersion}, 179 | } 180 | } 181 | 182 | result = RegFind(`^(\d+)(?:\.(\d+))?(?:\.(\d+))?\.[x*]$`, constraint) 183 | if result != nil { 184 | if len(result) > 3 && result[3] != "" { 185 | highVersion = result[1] + "." + result[2] + "." + result[3] + ".9999999" 186 | if result[3] == "0" { 187 | last, _ := strconv.Atoi(result[2]) 188 | lowVersion = result[1] + "." + strconv.Itoa(last-1) + ".9999999.9999999" 189 | } else { 190 | last, _ := strconv.Atoi(result[3]) 191 | lowVersion = result[1] + "." + result[2] + "." + strconv.Itoa(last-1) + ".9999999" 192 | } 193 | 194 | } else if len(result) > 2 && result[2] != "" { 195 | highVersion = result[1] + "." + result[2] + ".9999999.9999999" 196 | if result[2] == "0" { 197 | last, _ := strconv.Atoi(result[1]) 198 | lowVersion = strconv.Itoa(last-1) + ".9999999.9999999.9999999" 199 | } else { 200 | last, _ := strconv.Atoi(result[2]) 201 | lowVersion = result[1] + "." + strconv.Itoa(last-1) + ".9999999.9999999" 202 | } 203 | 204 | } else { 205 | highVersion = result[1] + ".9999999.9999999.9999999" 206 | if result[1] == "0" { 207 | return []*Constraint{{"<", highVersion}} 208 | } else { 209 | last, _ := strconv.Atoi(result[1]) 210 | lowVersion = strconv.Itoa(last-1) + ".9999999.9999999.9999999" 211 | } 212 | } 213 | 214 | return []*Constraint{ 215 | {">", lowVersion}, 216 | {"<", highVersion}, 217 | } 218 | } 219 | 220 | // match operators constraints 221 | result = RegFind(`^(<>|!=|>=?|<=?|==?)?\s*(.*)`, constraint) 222 | if result != nil { 223 | version := Normalize(result[2]) 224 | 225 | if stabilityModifier != "" && parseStability(version) == "stable" { 226 | version = version + "-" + stabilityModifier 227 | } else if result[1] == "<" { 228 | match := RegFind(`(?i)-stable$`, result[2]) 229 | if match == nil { 230 | version = version + "-dev" 231 | } 232 | } 233 | 234 | if len(result) > 1 && result[1] != "" { 235 | return []*Constraint{{result[1], version}} 236 | } else { 237 | return []*Constraint{{"=", version}} 238 | 239 | } 240 | } 241 | 242 | return []*Constraint{{constraint, stabilityModifier}} 243 | } 244 | 245 | // PCRegMap : PreCompiled Regex Map 246 | type PCRegMap struct { 247 | sync.RWMutex 248 | m map[string]*regexp.Regexp 249 | } 250 | 251 | // MustCompile : to replace regexp.MustCompile in RegFind. 252 | func (p *PCRegMap) MustCompile(pattern string) *regexp.Regexp { 253 | p.RLock() 254 | ret, exist := p.m[pattern] 255 | p.RUnlock() 256 | if exist { 257 | return ret 258 | } 259 | ret = regexp.MustCompile(pattern) 260 | p.Lock() 261 | p.m[pattern] = ret 262 | p.Unlock() 263 | return ret 264 | } 265 | 266 | var ( 267 | regexpCache *PCRegMap 268 | ) 269 | 270 | func init() { 271 | regexpCache = new(PCRegMap) 272 | regexpCache.m = make(map[string]*regexp.Regexp) 273 | } 274 | 275 | func RegFind(pattern, subject string) []string { 276 | reg := regexpCache.MustCompile(pattern) 277 | matched := reg.FindAllStringSubmatch(subject, -1) 278 | 279 | if matched != nil { 280 | return matched[0] 281 | } 282 | 283 | return nil 284 | } 285 | 286 | func RegSplit(pattern, subject string) []string { 287 | reg := regexp.MustCompile(pattern) 288 | indexes := reg.FindAllStringIndex(subject, -1) 289 | 290 | laststart := 0 291 | result := make([]string, len(indexes)+1) 292 | 293 | for i, element := range indexes { 294 | result[i] = subject[laststart:element[0]] 295 | laststart = element[1] 296 | } 297 | 298 | result[len(indexes)] = subject[laststart:len(subject)] 299 | return result 300 | } 301 | -------------------------------------------------------------------------------- /compare_test.go: -------------------------------------------------------------------------------- 1 | package version 2 | 3 | import ( 4 | "strings" 5 | "testing" 6 | ) 7 | 8 | var prepVersionValues = map[string]string{ 9 | "1-stable": "1.stable.", 10 | "1....0.0": "1.0.0", 11 | "1-2_3-4": "1.2.3.4", 12 | "1-2_3-..4-beta": "1.2.3.4.beta.", 13 | } 14 | 15 | func TestPrepVersion(t *testing.T) { 16 | for in, out := range prepVersionValues { 17 | if x := prepVersion(in); strings.Join(x, ".") != out { 18 | t.Errorf("FAIL: Normalize(%v) = %v: want %v", in, x, out) 19 | } 20 | } 21 | } 22 | 23 | var numVersionValues = map[string]int{ 24 | "foo": -7, 25 | "10": 10, 26 | "12212": 12212, 27 | "": 0, 28 | "dev": -6, 29 | "alpha": -5, 30 | "a": -5, 31 | "beta": -4, 32 | "b": -4, 33 | "RC": -3, 34 | "rc": -3, 35 | "#": -2, 36 | "p": 1, 37 | "pl": 1, 38 | } 39 | 40 | func TestNumVersion(t *testing.T) { 41 | for in, out := range numVersionValues { 42 | if x := numVersion(in); x != out { 43 | t.Errorf("FAIL: Normalize(%v) = %v: want %v", in, x, out) 44 | } 45 | } 46 | } 47 | 48 | var compareVersionSimpleValues = map[string]int{ 49 | "1|2": -1, 50 | "10|2": 1, 51 | "1.0|1.1": -1, 52 | "1.2|1.0.1": 1, 53 | "1.0-snapshot|1.0-SNAPSHOT": 0, 54 | "1.0-snapshot|1.0dev": -1, 55 | "1.0-snapshot|1.0a1": -1, 56 | "1.0-snapshot|1.0b1": -1, 57 | "1.0-snapshot|1.0RC1": -1, 58 | "1.0-snapshot|1.0rc1": -1, 59 | "1.0-snapshot|1.0": -1, 60 | "1.0-snapshot|1.0pl1": -1, 61 | "1.0-dev|1.0-dev": 0, 62 | "1.0-dev|1.0a1": -1, 63 | "1.0-dev|1.0b1": -1, 64 | "1.0-dev|1.0RC1": -1, 65 | "1.0-dev|1.0rc1": -1, 66 | "1.0-dev|1.0": -1, 67 | "1.0-dev|1.0pl1": -1, 68 | "1.0a1|1.0-dev": 1, 69 | "1.0a1|1.0a1": 0, 70 | "1.0a1|1.0b1": -1, 71 | "1.0a1|1.0RC1": -1, 72 | "1.0a1|1.0rc1": -1, 73 | "1.0a1|1.0": -1, 74 | "1.0a1|1.0pl1": -1, 75 | "1.0b1|1.0-dev": 1, 76 | "1.0b1|1.0a1": 1, 77 | "1.0b1|1.0b1": 0, 78 | "1.0b1|1.0RC1": -1, 79 | "1.0b1|1.0rc1": -1, 80 | "1.0b1|1.0": -1, 81 | "1.0b1|1.0pl1": -1, 82 | "1.0RC1|1.0-dev": 1, 83 | "1.0RC1|1.0a1": 1, 84 | "1.0RC1|1.0b1": 1, 85 | "1.0RC1|1.0RC1": 0, 86 | "1.0RC1|1.0rc1": 0, 87 | "1.0RC1|1.0": -1, 88 | "1.0RC1|1.0pl1": -1, 89 | "1.0rc1|1.0-dev": 1, 90 | "1.0rc1|1.0a1": 1, 91 | "1.0rc1|1.0b1": 1, 92 | "1.0rc1|1.0RC1": 0, 93 | "1.0rc1|1.0rc1": 0, 94 | "1.0rc1|1.0": -1, 95 | "1.0rc1|1.0pl1": -1, 96 | "1.0|1.0-dev": 1, 97 | "1.0|1.0a1": 1, 98 | "1.0|1.0b1": 1, 99 | "1.0|1.0RC1": 1, 100 | "1.0|1.0rc1": 1, 101 | "1.0|1.0": 0, 102 | "1.0|1.0pl1": -1, 103 | "1.0pl1|1.0-dev": 1, 104 | "1.0pl1|1.0a1": 1, 105 | "1.0pl1|1.0b1": 1, 106 | "1.0pl1|1.0RC1": 1, 107 | "1.0pl1|1.0rc1": 1, 108 | "1.0pl1|1.0": 1, 109 | "1.0pl1|1.0pl1": 0, 110 | } 111 | 112 | func TestCompareVersionSimple(t *testing.T) { 113 | for in, out := range compareVersionSimpleValues { 114 | v := strings.Split(in, "|") 115 | if x := CompareSimple(v[0], v[1]); x != out { 116 | t.Errorf("FAIL: CompareVersionSimple(%v) = %v: want %v", in, x, out) 117 | } 118 | } 119 | } 120 | 121 | var compareNormalizedVersionValues = map[string]bool{ 122 | "1.0-snapshot eq 1.0-SNAPSHOT": true, 123 | "1.0-snapshot == 1.0-SNAPSHOT": true, 124 | "1.0-snapshot lt 1.0-snapshot": false, 125 | "1.0-snapshot < 1.0-snapshot": false, 126 | "1.0-snapshot le 1.0-snapshot": true, 127 | "1.0-snapshot <= 1.0-snapshot": true, 128 | "1.0-snapshot gt 1.0-snapshot": false, 129 | "1.0-snapshot > 1.0-snapshot": false, 130 | "1.0-snapshot ge 1.0-snapshot": true, 131 | "1.0-snapshot >= 1.0-snapshot": true, 132 | "1.0-snapshot eq 1.0-snapshot": true, 133 | "1.0-snapshot = 1.0-snapshot": true, 134 | "1.0-snapshot == 1.0-snapshot": true, 135 | "1.0-snapshot ne 1.0-snapshot": false, 136 | "1.0-snapshot <> 1.0-snapshot": false, 137 | "1.0-snapshot != 1.0-snapshot": false, 138 | "1.0-snapshot lt 1.0-dev": true, 139 | "1.0-snapshot < 1.0-dev": true, 140 | "1.0-snapshot le 1.0-dev": true, 141 | "1.0-snapshot <= 1.0-dev": true, 142 | "1.0-snapshot gt 1.0-dev": false, 143 | "1.0-snapshot > 1.0-dev": false, 144 | "1.0-snapshot ge 1.0-dev": false, 145 | "1.0-snapshot >= 1.0-dev": false, 146 | "1.0-snapshot eq 1.0-dev": false, 147 | "1.0-snapshot = 1.0-dev": false, 148 | "1.0-snapshot == 1.0-dev": false, 149 | "1.0-snapshot ne 1.0-dev": true, 150 | "1.0-snapshot <> 1.0-dev": true, 151 | "1.0-snapshot != 1.0-dev": true, 152 | "1.0-snapshot lt 1.0a1": true, 153 | "1.0-snapshot < 1.0a1": true, 154 | "1.0-snapshot le 1.0a1": true, 155 | "1.0-snapshot <= 1.0a1": true, 156 | "1.0-snapshot gt 1.0a1": false, 157 | "1.0-snapshot > 1.0a1": false, 158 | "1.0-snapshot ge 1.0a1": false, 159 | "1.0-snapshot >= 1.0a1": false, 160 | "1.0-snapshot eq 1.0a1": false, 161 | "1.0-snapshot = 1.0a1": false, 162 | "1.0-snapshot == 1.0a1": false, 163 | "1.0-snapshot ne 1.0a1": true, 164 | "1.0-snapshot <> 1.0a1": true, 165 | "1.0-snapshot != 1.0a1": true, 166 | "1.0-snapshot lt 1.0b1": true, 167 | "1.0-snapshot < 1.0b1": true, 168 | "1.0-snapshot le 1.0b1": true, 169 | "1.0-snapshot <= 1.0b1": true, 170 | "1.0-snapshot gt 1.0b1": false, 171 | "1.0-snapshot > 1.0b1": false, 172 | "1.0-snapshot ge 1.0b1": false, 173 | "1.0-snapshot >= 1.0b1": false, 174 | "1.0-snapshot eq 1.0b1": false, 175 | "1.0-snapshot = 1.0b1": false, 176 | "1.0-snapshot == 1.0b1": false, 177 | "1.0-snapshot ne 1.0b1": true, 178 | "1.0-snapshot <> 1.0b1": true, 179 | "1.0-snapshot != 1.0b1": true, 180 | "1.0-snapshot lt 1.0RC1": true, 181 | "1.0-snapshot < 1.0RC1": true, 182 | "1.0-snapshot le 1.0RC1": true, 183 | "1.0-snapshot <= 1.0RC1": true, 184 | "1.0-snapshot gt 1.0RC1": false, 185 | "1.0-snapshot > 1.0RC1": false, 186 | "1.0-snapshot ge 1.0RC1": false, 187 | "1.0-snapshot >= 1.0RC1": false, 188 | "1.0-snapshot eq 1.0RC1": false, 189 | "1.0-snapshot = 1.0RC1": false, 190 | "1.0-snapshot == 1.0RC1": false, 191 | "1.0-snapshot ne 1.0RC1": true, 192 | "1.0-snapshot <> 1.0RC1": true, 193 | "1.0-snapshot != 1.0RC1": true, 194 | "1.0-snapshot lt 1.0rc1": true, 195 | "1.0-snapshot < 1.0rc1": true, 196 | "1.0-snapshot le 1.0rc1": true, 197 | "1.0-snapshot <= 1.0rc1": true, 198 | "1.0-snapshot gt 1.0rc1": false, 199 | "1.0-snapshot > 1.0rc1": false, 200 | "1.0-snapshot ge 1.0rc1": false, 201 | "1.0-snapshot >= 1.0rc1": false, 202 | "1.0-snapshot eq 1.0rc1": false, 203 | "1.0-snapshot = 1.0rc1": false, 204 | "1.0-snapshot == 1.0rc1": false, 205 | "1.0-snapshot ne 1.0rc1": true, 206 | "1.0-snapshot <> 1.0rc1": true, 207 | "1.0-snapshot != 1.0rc1": true, 208 | "1.0-snapshot lt 1.0": true, 209 | "1.0-snapshot < 1.0": true, 210 | "1.0-snapshot le 1.0": true, 211 | "1.0-snapshot <= 1.0": true, 212 | "1.0-snapshot gt 1.0": false, 213 | "1.0-snapshot > 1.0": false, 214 | "1.0-snapshot ge 1.0": false, 215 | "1.0-snapshot >= 1.0": false, 216 | "1.0-snapshot eq 1.0": false, 217 | "1.0-snapshot = 1.0": false, 218 | "1.0-snapshot == 1.0": false, 219 | "1.0-snapshot ne 1.0": true, 220 | "1.0-snapshot <> 1.0": true, 221 | "1.0-snapshot != 1.0": true, 222 | "1.0-snapshot lt 1.0pl1": true, 223 | "1.0-snapshot < 1.0pl1": true, 224 | "1.0-snapshot le 1.0pl1": true, 225 | "1.0-snapshot <= 1.0pl1": true, 226 | "1.0-snapshot gt 1.0pl1": false, 227 | "1.0-snapshot > 1.0pl1": false, 228 | "1.0-snapshot ge 1.0pl1": false, 229 | "1.0-snapshot >= 1.0pl1": false, 230 | "1.0-snapshot eq 1.0pl1": false, 231 | "1.0-snapshot = 1.0pl1": false, 232 | "1.0-snapshot == 1.0pl1": false, 233 | "1.0-snapshot ne 1.0pl1": true, 234 | "1.0-snapshot <> 1.0pl1": true, 235 | "1.0-snapshot != 1.0pl1": true, 236 | "1.0-dev lt 1.0-dev": false, 237 | "1.0-dev < 1.0-dev": false, 238 | "1.0-dev le 1.0-dev": true, 239 | "1.0-dev <= 1.0-dev": true, 240 | "1.0-dev gt 1.0-dev": false, 241 | "1.0-dev > 1.0-dev": false, 242 | "1.0-dev ge 1.0-dev": true, 243 | "1.0-dev >= 1.0-dev": true, 244 | "1.0-dev eq 1.0-dev": true, 245 | "1.0-dev = 1.0-dev": true, 246 | "1.0-dev == 1.0-dev": true, 247 | "1.0-dev ne 1.0-dev": false, 248 | "1.0-dev <> 1.0-dev": false, 249 | "1.0-dev != 1.0-dev": false, 250 | "1.0-dev lt 1.0a1": true, 251 | "1.0-dev < 1.0a1": true, 252 | "1.0-dev le 1.0a1": true, 253 | "1.0-dev <= 1.0a1": true, 254 | "1.0-dev gt 1.0a1": false, 255 | "1.0-dev > 1.0a1": false, 256 | "1.0-dev ge 1.0a1": false, 257 | "1.0-dev >= 1.0a1": false, 258 | "1.0-dev eq 1.0a1": false, 259 | "1.0-dev = 1.0a1": false, 260 | "1.0-dev == 1.0a1": false, 261 | "1.0-dev ne 1.0a1": true, 262 | "1.0-dev <> 1.0a1": true, 263 | "1.0-dev != 1.0a1": true, 264 | "1.0-dev lt 1.0b1": true, 265 | "1.0-dev < 1.0b1": true, 266 | "1.0-dev le 1.0b1": true, 267 | "1.0-dev <= 1.0b1": true, 268 | "1.0-dev gt 1.0b1": false, 269 | "1.0-dev > 1.0b1": false, 270 | "1.0-dev ge 1.0b1": false, 271 | "1.0-dev >= 1.0b1": false, 272 | "1.0-dev eq 1.0b1": false, 273 | "1.0-dev = 1.0b1": false, 274 | "1.0-dev == 1.0b1": false, 275 | "1.0-dev ne 1.0b1": true, 276 | "1.0-dev <> 1.0b1": true, 277 | "1.0-dev != 1.0b1": true, 278 | "1.0-dev lt 1.0RC1": true, 279 | "1.0-dev < 1.0RC1": true, 280 | "1.0-dev le 1.0RC1": true, 281 | "1.0-dev <= 1.0RC1": true, 282 | "1.0-dev gt 1.0RC1": false, 283 | "1.0-dev > 1.0RC1": false, 284 | "1.0-dev ge 1.0RC1": false, 285 | "1.0-dev >= 1.0RC1": false, 286 | "1.0-dev eq 1.0RC1": false, 287 | "1.0-dev = 1.0RC1": false, 288 | "1.0-dev == 1.0RC1": false, 289 | "1.0-dev ne 1.0RC1": true, 290 | "1.0-dev <> 1.0RC1": true, 291 | "1.0-dev != 1.0RC1": true, 292 | "1.0-dev lt 1.0rc1": true, 293 | "1.0-dev < 1.0rc1": true, 294 | "1.0-dev le 1.0rc1": true, 295 | "1.0-dev <= 1.0rc1": true, 296 | "1.0-dev gt 1.0rc1": false, 297 | "1.0-dev > 1.0rc1": false, 298 | "1.0-dev ge 1.0rc1": false, 299 | "1.0-dev >= 1.0rc1": false, 300 | "1.0-dev eq 1.0rc1": false, 301 | "1.0-dev = 1.0rc1": false, 302 | "1.0-dev == 1.0rc1": false, 303 | "1.0-dev ne 1.0rc1": true, 304 | "1.0-dev <> 1.0rc1": true, 305 | "1.0-dev != 1.0rc1": true, 306 | "1.0-dev lt 1.0": true, 307 | "1.0-dev < 1.0": true, 308 | "1.0-dev le 1.0": true, 309 | "1.0-dev <= 1.0": true, 310 | "1.0-dev gt 1.0": false, 311 | "1.0-dev > 1.0": false, 312 | "1.0-dev ge 1.0": false, 313 | "1.0-dev >= 1.0": false, 314 | "1.0-dev eq 1.0": false, 315 | "1.0-dev = 1.0": false, 316 | "1.0-dev == 1.0": false, 317 | "1.0-dev ne 1.0": true, 318 | "1.0-dev <> 1.0": true, 319 | "1.0-dev != 1.0": true, 320 | "1.0-dev lt 1.0pl1": true, 321 | "1.0-dev < 1.0pl1": true, 322 | "1.0-dev le 1.0pl1": true, 323 | "1.0-dev <= 1.0pl1": true, 324 | "1.0-dev gt 1.0pl1": false, 325 | "1.0-dev > 1.0pl1": false, 326 | "1.0-dev ge 1.0pl1": false, 327 | "1.0-dev >= 1.0pl1": false, 328 | "1.0-dev eq 1.0pl1": false, 329 | "1.0-dev = 1.0pl1": false, 330 | "1.0-dev == 1.0pl1": false, 331 | "1.0-dev ne 1.0pl1": true, 332 | "1.0-dev <> 1.0pl1": true, 333 | "1.0-dev != 1.0pl1": true, 334 | "1.0a1 lt 1.0-dev": false, 335 | "1.0a1 < 1.0-dev": false, 336 | "1.0a1 le 1.0-dev": false, 337 | "1.0a1 <= 1.0-dev": false, 338 | "1.0a1 gt 1.0-dev": true, 339 | "1.0a1 > 1.0-dev": true, 340 | "1.0a1 ge 1.0-dev": true, 341 | "1.0a1 >= 1.0-dev": true, 342 | "1.0a1 eq 1.0-dev": false, 343 | "1.0a1 = 1.0-dev": false, 344 | "1.0a1 == 1.0-dev": false, 345 | "1.0a1 ne 1.0-dev": true, 346 | "1.0a1 <> 1.0-dev": true, 347 | "1.0a1 != 1.0-dev": true, 348 | "1.0a1 lt 1.0a1": false, 349 | "1.0a1 < 1.0a1": false, 350 | "1.0a1 le 1.0a1": true, 351 | "1.0a1 <= 1.0a1": true, 352 | "1.0a1 gt 1.0a1": false, 353 | "1.0a1 > 1.0a1": false, 354 | "1.0a1 ge 1.0a1": true, 355 | "1.0a1 >= 1.0a1": true, 356 | "1.0a1 eq 1.0a1": true, 357 | "1.0a1 = 1.0a1": true, 358 | "1.0a1 == 1.0a1": true, 359 | "1.0a1 ne 1.0a1": false, 360 | "1.0a1 <> 1.0a1": false, 361 | "1.0a1 != 1.0a1": false, 362 | "1.0a1 lt 1.0b1": true, 363 | "1.0a1 < 1.0b1": true, 364 | "1.0a1 le 1.0b1": true, 365 | "1.0a1 <= 1.0b1": true, 366 | "1.0a1 gt 1.0b1": false, 367 | "1.0a1 > 1.0b1": false, 368 | "1.0a1 ge 1.0b1": false, 369 | "1.0a1 >= 1.0b1": false, 370 | "1.0a1 eq 1.0b1": false, 371 | "1.0a1 = 1.0b1": false, 372 | "1.0a1 == 1.0b1": false, 373 | "1.0a1 ne 1.0b1": true, 374 | "1.0a1 <> 1.0b1": true, 375 | "1.0a1 != 1.0b1": true, 376 | "1.0a1 lt 1.0RC1": true, 377 | "1.0a1 < 1.0RC1": true, 378 | "1.0a1 le 1.0RC1": true, 379 | "1.0a1 <= 1.0RC1": true, 380 | "1.0a1 gt 1.0RC1": false, 381 | "1.0a1 > 1.0RC1": false, 382 | "1.0a1 ge 1.0RC1": false, 383 | "1.0a1 >= 1.0RC1": false, 384 | "1.0a1 eq 1.0RC1": false, 385 | "1.0a1 = 1.0RC1": false, 386 | "1.0a1 == 1.0RC1": false, 387 | "1.0a1 ne 1.0RC1": true, 388 | "1.0a1 <> 1.0RC1": true, 389 | "1.0a1 != 1.0RC1": true, 390 | "1.0a1 lt 1.0rc1": true, 391 | "1.0a1 < 1.0rc1": true, 392 | "1.0a1 le 1.0rc1": true, 393 | "1.0a1 <= 1.0rc1": true, 394 | "1.0a1 gt 1.0rc1": false, 395 | "1.0a1 > 1.0rc1": false, 396 | "1.0a1 ge 1.0rc1": false, 397 | "1.0a1 >= 1.0rc1": false, 398 | "1.0a1 eq 1.0rc1": false, 399 | "1.0a1 = 1.0rc1": false, 400 | "1.0a1 == 1.0rc1": false, 401 | "1.0a1 ne 1.0rc1": true, 402 | "1.0a1 <> 1.0rc1": true, 403 | "1.0a1 != 1.0rc1": true, 404 | "1.0a1 lt 1.0": true, 405 | "1.0a1 < 1.0": true, 406 | "1.0a1 le 1.0": true, 407 | "1.0a1 <= 1.0": true, 408 | "1.0a1 gt 1.0": false, 409 | "1.0a1 > 1.0": false, 410 | "1.0a1 ge 1.0": false, 411 | "1.0a1 >= 1.0": false, 412 | "1.0a1 eq 1.0": false, 413 | "1.0a1 = 1.0": false, 414 | "1.0a1 == 1.0": false, 415 | "1.0a1 ne 1.0": true, 416 | "1.0a1 <> 1.0": true, 417 | "1.0a1 != 1.0": true, 418 | "1.0a1 lt 1.0pl1": true, 419 | "1.0a1 < 1.0pl1": true, 420 | "1.0a1 le 1.0pl1": true, 421 | "1.0a1 <= 1.0pl1": true, 422 | "1.0a1 gt 1.0pl1": false, 423 | "1.0a1 > 1.0pl1": false, 424 | "1.0a1 ge 1.0pl1": false, 425 | "1.0a1 >= 1.0pl1": false, 426 | "1.0a1 eq 1.0pl1": false, 427 | "1.0a1 = 1.0pl1": false, 428 | "1.0a1 == 1.0pl1": false, 429 | "1.0a1 ne 1.0pl1": true, 430 | "1.0a1 <> 1.0pl1": true, 431 | "1.0a1 != 1.0pl1": true, 432 | "1.0b1 lt 1.0-dev": false, 433 | "1.0b1 < 1.0-dev": false, 434 | "1.0b1 le 1.0-dev": false, 435 | "1.0b1 <= 1.0-dev": false, 436 | "1.0b1 gt 1.0-dev": true, 437 | "1.0b1 > 1.0-dev": true, 438 | "1.0b1 ge 1.0-dev": true, 439 | "1.0b1 >= 1.0-dev": true, 440 | "1.0b1 eq 1.0-dev": false, 441 | "1.0b1 = 1.0-dev": false, 442 | "1.0b1 == 1.0-dev": false, 443 | "1.0b1 ne 1.0-dev": true, 444 | "1.0b1 <> 1.0-dev": true, 445 | "1.0b1 != 1.0-dev": true, 446 | "1.0b1 lt 1.0a1": false, 447 | "1.0b1 < 1.0a1": false, 448 | "1.0b1 le 1.0a1": false, 449 | "1.0b1 <= 1.0a1": false, 450 | "1.0b1 gt 1.0a1": true, 451 | "1.0b1 > 1.0a1": true, 452 | "1.0b1 ge 1.0a1": true, 453 | "1.0b1 >= 1.0a1": true, 454 | "1.0b1 eq 1.0a1": false, 455 | "1.0b1 = 1.0a1": false, 456 | "1.0b1 == 1.0a1": false, 457 | "1.0b1 ne 1.0a1": true, 458 | "1.0b1 <> 1.0a1": true, 459 | "1.0b1 != 1.0a1": true, 460 | "1.0b1 lt 1.0b1": false, 461 | "1.0b1 < 1.0b1": false, 462 | "1.0b1 le 1.0b1": true, 463 | "1.0b1 <= 1.0b1": true, 464 | "1.0b1 gt 1.0b1": false, 465 | "1.0b1 > 1.0b1": false, 466 | "1.0b1 ge 1.0b1": true, 467 | "1.0b1 >= 1.0b1": true, 468 | "1.0b1 eq 1.0b1": true, 469 | "1.0b1 = 1.0b1": true, 470 | "1.0b1 == 1.0b1": true, 471 | "1.0b1 ne 1.0b1": false, 472 | "1.0b1 <> 1.0b1": false, 473 | "1.0b1 != 1.0b1": false, 474 | "1.0b1 lt 1.0RC1": true, 475 | "1.0b1 < 1.0RC1": true, 476 | "1.0b1 le 1.0RC1": true, 477 | "1.0b1 <= 1.0RC1": true, 478 | "1.0b1 gt 1.0RC1": false, 479 | "1.0b1 > 1.0RC1": false, 480 | "1.0b1 ge 1.0RC1": false, 481 | "1.0b1 >= 1.0RC1": false, 482 | "1.0b1 eq 1.0RC1": false, 483 | "1.0b1 = 1.0RC1": false, 484 | "1.0b1 == 1.0RC1": false, 485 | "1.0b1 ne 1.0RC1": true, 486 | "1.0b1 <> 1.0RC1": true, 487 | "1.0b1 != 1.0RC1": true, 488 | "1.0b1 lt 1.0rc1": true, 489 | "1.0b1 < 1.0rc1": true, 490 | "1.0b1 le 1.0rc1": true, 491 | "1.0b1 <= 1.0rc1": true, 492 | "1.0b1 gt 1.0rc1": false, 493 | "1.0b1 > 1.0rc1": false, 494 | "1.0b1 ge 1.0rc1": false, 495 | "1.0b1 >= 1.0rc1": false, 496 | "1.0b1 eq 1.0rc1": false, 497 | "1.0b1 = 1.0rc1": false, 498 | "1.0b1 == 1.0rc1": false, 499 | "1.0b1 ne 1.0rc1": true, 500 | "1.0b1 <> 1.0rc1": true, 501 | "1.0b1 != 1.0rc1": true, 502 | "1.0b1 lt 1.0": true, 503 | "1.0b1 < 1.0": true, 504 | "1.0b1 le 1.0": true, 505 | "1.0b1 <= 1.0": true, 506 | "1.0b1 gt 1.0": false, 507 | "1.0b1 > 1.0": false, 508 | "1.0b1 ge 1.0": false, 509 | "1.0b1 >= 1.0": false, 510 | "1.0b1 eq 1.0": false, 511 | "1.0b1 = 1.0": false, 512 | "1.0b1 == 1.0": false, 513 | "1.0b1 ne 1.0": true, 514 | "1.0b1 <> 1.0": true, 515 | "1.0b1 != 1.0": true, 516 | "1.0b1 lt 1.0pl1": true, 517 | "1.0b1 < 1.0pl1": true, 518 | "1.0b1 le 1.0pl1": true, 519 | "1.0b1 <= 1.0pl1": true, 520 | "1.0b1 gt 1.0pl1": false, 521 | "1.0b1 > 1.0pl1": false, 522 | "1.0b1 ge 1.0pl1": false, 523 | "1.0b1 >= 1.0pl1": false, 524 | "1.0b1 eq 1.0pl1": false, 525 | "1.0b1 = 1.0pl1": false, 526 | "1.0b1 == 1.0pl1": false, 527 | "1.0b1 ne 1.0pl1": true, 528 | "1.0b1 <> 1.0pl1": true, 529 | "1.0b1 != 1.0pl1": true, 530 | "1.0RC1 lt 1.0-dev": false, 531 | "1.0RC1 < 1.0-dev": false, 532 | "1.0RC1 le 1.0-dev": false, 533 | "1.0RC1 <= 1.0-dev": false, 534 | "1.0RC1 gt 1.0-dev": true, 535 | "1.0RC1 > 1.0-dev": true, 536 | "1.0RC1 ge 1.0-dev": true, 537 | "1.0RC1 >= 1.0-dev": true, 538 | "1.0RC1 eq 1.0-dev": false, 539 | "1.0RC1 = 1.0-dev": false, 540 | "1.0RC1 == 1.0-dev": false, 541 | "1.0RC1 ne 1.0-dev": true, 542 | "1.0RC1 <> 1.0-dev": true, 543 | "1.0RC1 != 1.0-dev": true, 544 | "1.0RC1 lt 1.0a1": false, 545 | "1.0RC1 < 1.0a1": false, 546 | "1.0RC1 le 1.0a1": false, 547 | "1.0RC1 <= 1.0a1": false, 548 | "1.0RC1 gt 1.0a1": true, 549 | "1.0RC1 > 1.0a1": true, 550 | "1.0RC1 ge 1.0a1": true, 551 | "1.0RC1 >= 1.0a1": true, 552 | "1.0RC1 eq 1.0a1": false, 553 | "1.0RC1 = 1.0a1": false, 554 | "1.0RC1 == 1.0a1": false, 555 | "1.0RC1 ne 1.0a1": true, 556 | "1.0RC1 <> 1.0a1": true, 557 | "1.0RC1 != 1.0a1": true, 558 | "1.0RC1 lt 1.0b1": false, 559 | "1.0RC1 < 1.0b1": false, 560 | "1.0RC1 le 1.0b1": false, 561 | "1.0RC1 <= 1.0b1": false, 562 | "1.0RC1 gt 1.0b1": true, 563 | "1.0RC1 > 1.0b1": true, 564 | "1.0RC1 ge 1.0b1": true, 565 | "1.0RC1 >= 1.0b1": true, 566 | "1.0RC1 eq 1.0b1": false, 567 | "1.0RC1 = 1.0b1": false, 568 | "1.0RC1 == 1.0b1": false, 569 | "1.0RC1 ne 1.0b1": true, 570 | "1.0RC1 <> 1.0b1": true, 571 | "1.0RC1 != 1.0b1": true, 572 | "1.0RC1 lt 1.0RC1": false, 573 | "1.0RC1 < 1.0RC1": false, 574 | "1.0RC1 le 1.0RC1": true, 575 | "1.0RC1 <= 1.0RC1": true, 576 | "1.0RC1 gt 1.0RC1": false, 577 | "1.0RC1 > 1.0RC1": false, 578 | "1.0RC1 ge 1.0RC1": true, 579 | "1.0RC1 >= 1.0RC1": true, 580 | "1.0RC1 eq 1.0RC1": true, 581 | "1.0RC1 = 1.0RC1": true, 582 | "1.0RC1 == 1.0RC1": true, 583 | "1.0RC1 ne 1.0RC1": false, 584 | "1.0RC1 <> 1.0RC1": false, 585 | "1.0RC1 != 1.0RC1": false, 586 | "1.0RC1 lt 1.0rc1": false, 587 | "1.0RC1 < 1.0rc1": false, 588 | "1.0RC1 le 1.0rc1": true, 589 | "1.0RC1 <= 1.0rc1": true, 590 | "1.0RC1 gt 1.0rc1": false, 591 | "1.0RC1 > 1.0rc1": false, 592 | "1.0RC1 ge 1.0rc1": true, 593 | "1.0RC1 >= 1.0rc1": true, 594 | "1.0RC1 eq 1.0rc1": true, 595 | "1.0RC1 = 1.0rc1": true, 596 | "1.0RC1 == 1.0rc1": true, 597 | "1.0RC1 ne 1.0rc1": false, 598 | "1.0RC1 <> 1.0rc1": false, 599 | "1.0RC1 != 1.0rc1": false, 600 | "1.0RC1 lt 1.0": true, 601 | "1.0RC1 < 1.0": true, 602 | "1.0RC1 le 1.0": true, 603 | "1.0RC1 <= 1.0": true, 604 | "1.0RC1 gt 1.0": false, 605 | "1.0RC1 > 1.0": false, 606 | "1.0RC1 ge 1.0": false, 607 | "1.0RC1 >= 1.0": false, 608 | "1.0RC1 eq 1.0": false, 609 | "1.0RC1 = 1.0": false, 610 | "1.0RC1 == 1.0": false, 611 | "1.0RC1 ne 1.0": true, 612 | "1.0RC1 <> 1.0": true, 613 | "1.0RC1 != 1.0": true, 614 | "1.0RC1 lt 1.0pl1": true, 615 | "1.0RC1 < 1.0pl1": true, 616 | "1.0RC1 le 1.0pl1": true, 617 | "1.0RC1 <= 1.0pl1": true, 618 | "1.0RC1 gt 1.0pl1": false, 619 | "1.0RC1 > 1.0pl1": false, 620 | "1.0RC1 ge 1.0pl1": false, 621 | "1.0RC1 >= 1.0pl1": false, 622 | "1.0RC1 eq 1.0pl1": false, 623 | "1.0RC1 = 1.0pl1": false, 624 | "1.0RC1 == 1.0pl1": false, 625 | "1.0RC1 ne 1.0pl1": true, 626 | "1.0RC1 <> 1.0pl1": true, 627 | "1.0RC1 != 1.0pl1": true, 628 | "1.0rc1 lt 1.0-dev": false, 629 | "1.0rc1 < 1.0-dev": false, 630 | "1.0rc1 le 1.0-dev": false, 631 | "1.0rc1 <= 1.0-dev": false, 632 | "1.0rc1 gt 1.0-dev": true, 633 | "1.0rc1 > 1.0-dev": true, 634 | "1.0rc1 ge 1.0-dev": true, 635 | "1.0rc1 >= 1.0-dev": true, 636 | "1.0rc1 eq 1.0-dev": false, 637 | "1.0rc1 = 1.0-dev": false, 638 | "1.0rc1 == 1.0-dev": false, 639 | "1.0rc1 ne 1.0-dev": true, 640 | "1.0rc1 <> 1.0-dev": true, 641 | "1.0rc1 != 1.0-dev": true, 642 | "1.0rc1 lt 1.0a1": false, 643 | "1.0rc1 < 1.0a1": false, 644 | "1.0rc1 le 1.0a1": false, 645 | "1.0rc1 <= 1.0a1": false, 646 | "1.0rc1 gt 1.0a1": true, 647 | "1.0rc1 > 1.0a1": true, 648 | "1.0rc1 ge 1.0a1": true, 649 | "1.0rc1 >= 1.0a1": true, 650 | "1.0rc1 eq 1.0a1": false, 651 | "1.0rc1 = 1.0a1": false, 652 | "1.0rc1 == 1.0a1": false, 653 | "1.0rc1 ne 1.0a1": true, 654 | "1.0rc1 <> 1.0a1": true, 655 | "1.0rc1 != 1.0a1": true, 656 | "1.0rc1 lt 1.0b1": false, 657 | "1.0rc1 < 1.0b1": false, 658 | "1.0rc1 le 1.0b1": false, 659 | "1.0rc1 <= 1.0b1": false, 660 | "1.0rc1 gt 1.0b1": true, 661 | "1.0rc1 > 1.0b1": true, 662 | "1.0rc1 ge 1.0b1": true, 663 | "1.0rc1 >= 1.0b1": true, 664 | "1.0rc1 eq 1.0b1": false, 665 | "1.0rc1 = 1.0b1": false, 666 | "1.0rc1 == 1.0b1": false, 667 | "1.0rc1 ne 1.0b1": true, 668 | "1.0rc1 <> 1.0b1": true, 669 | "1.0rc1 != 1.0b1": true, 670 | "1.0rc1 lt 1.0RC1": false, 671 | "1.0rc1 < 1.0RC1": false, 672 | "1.0rc1 le 1.0RC1": true, 673 | "1.0rc1 <= 1.0RC1": true, 674 | "1.0rc1 gt 1.0RC1": false, 675 | "1.0rc1 > 1.0RC1": false, 676 | "1.0rc1 ge 1.0RC1": true, 677 | "1.0rc1 >= 1.0RC1": true, 678 | "1.0rc1 eq 1.0RC1": true, 679 | "1.0rc1 = 1.0RC1": true, 680 | "1.0rc1 == 1.0RC1": true, 681 | "1.0rc1 ne 1.0RC1": false, 682 | "1.0rc1 <> 1.0RC1": false, 683 | "1.0rc1 != 1.0RC1": false, 684 | "1.0rc1 lt 1.0rc1": false, 685 | "1.0rc1 < 1.0rc1": false, 686 | "1.0rc1 le 1.0rc1": true, 687 | "1.0rc1 <= 1.0rc1": true, 688 | "1.0rc1 gt 1.0rc1": false, 689 | "1.0rc1 > 1.0rc1": false, 690 | "1.0rc1 ge 1.0rc1": true, 691 | "1.0rc1 >= 1.0rc1": true, 692 | "1.0rc1 eq 1.0rc1": true, 693 | "1.0rc1 = 1.0rc1": true, 694 | "1.0rc1 == 1.0rc1": true, 695 | "1.0rc1 ne 1.0rc1": false, 696 | "1.0rc1 <> 1.0rc1": false, 697 | "1.0rc1 != 1.0rc1": false, 698 | "1.0rc1 lt 1.0": true, 699 | "1.0rc1 < 1.0": true, 700 | "1.0rc1 le 1.0": true, 701 | "1.0rc1 <= 1.0": true, 702 | "1.0rc1 gt 1.0": false, 703 | "1.0rc1 > 1.0": false, 704 | "1.0rc1 ge 1.0": false, 705 | "1.0rc1 >= 1.0": false, 706 | "1.0rc1 eq 1.0": false, 707 | "1.0rc1 = 1.0": false, 708 | "1.0rc1 == 1.0": false, 709 | "1.0rc1 ne 1.0": true, 710 | "1.0rc1 <> 1.0": true, 711 | "1.0rc1 != 1.0": true, 712 | "1.0rc1 lt 1.0pl1": true, 713 | "1.0rc1 < 1.0pl1": true, 714 | "1.0rc1 le 1.0pl1": true, 715 | "1.0rc1 <= 1.0pl1": true, 716 | "1.0rc1 gt 1.0pl1": false, 717 | "1.0rc1 > 1.0pl1": false, 718 | "1.0rc1 ge 1.0pl1": false, 719 | "1.0rc1 >= 1.0pl1": false, 720 | "1.0rc1 eq 1.0pl1": false, 721 | "1.0rc1 = 1.0pl1": false, 722 | "1.0rc1 == 1.0pl1": false, 723 | "1.0rc1 ne 1.0pl1": true, 724 | "1.0rc1 <> 1.0pl1": true, 725 | "1.0rc1 != 1.0pl1": true, 726 | "1.0 lt 1.0-dev": false, 727 | "1.0 < 1.0-dev": false, 728 | "1.0 le 1.0-dev": false, 729 | "1.0 <= 1.0-dev": false, 730 | "1.0 gt 1.0-dev": true, 731 | "1.0 > 1.0-dev": true, 732 | "1.0 ge 1.0-dev": true, 733 | "1.0 >= 1.0-dev": true, 734 | "1.0 eq 1.0-dev": false, 735 | "1.0 = 1.0-dev": false, 736 | "1.0 == 1.0-dev": false, 737 | "1.0 ne 1.0-dev": true, 738 | "1.0 <> 1.0-dev": true, 739 | "1.0 != 1.0-dev": true, 740 | "1.0 lt 1.0a1": false, 741 | "1.0 < 1.0a1": false, 742 | "1.0 le 1.0a1": false, 743 | "1.0 <= 1.0a1": false, 744 | "1.0 gt 1.0a1": true, 745 | "1.0 > 1.0a1": true, 746 | "1.0 ge 1.0a1": true, 747 | "1.0 >= 1.0a1": true, 748 | "1.0 eq 1.0a1": false, 749 | "1.0 = 1.0a1": false, 750 | "1.0 == 1.0a1": false, 751 | "1.0 ne 1.0a1": true, 752 | "1.0 <> 1.0a1": true, 753 | "1.0 != 1.0a1": true, 754 | "1.0 lt 1.0b1": false, 755 | "1.0 < 1.0b1": false, 756 | "1.0 le 1.0b1": false, 757 | "1.0 <= 1.0b1": false, 758 | "1.0 gt 1.0b1": true, 759 | "1.0 > 1.0b1": true, 760 | "1.0 ge 1.0b1": true, 761 | "1.0 >= 1.0b1": true, 762 | "1.0 eq 1.0b1": false, 763 | "1.0 = 1.0b1": false, 764 | "1.0 == 1.0b1": false, 765 | "1.0 ne 1.0b1": true, 766 | "1.0 <> 1.0b1": true, 767 | "1.0 != 1.0b1": true, 768 | "1.0 lt 1.0RC1": false, 769 | "1.0 < 1.0RC1": false, 770 | "1.0 le 1.0RC1": false, 771 | "1.0 <= 1.0RC1": false, 772 | "1.0 gt 1.0RC1": true, 773 | "1.0 > 1.0RC1": true, 774 | "1.0 ge 1.0RC1": true, 775 | "1.0 >= 1.0RC1": true, 776 | "1.0 eq 1.0RC1": false, 777 | "1.0 = 1.0RC1": false, 778 | "1.0 == 1.0RC1": false, 779 | "1.0 ne 1.0RC1": true, 780 | "1.0 <> 1.0RC1": true, 781 | "1.0 != 1.0RC1": true, 782 | "1.0 lt 1.0rc1": false, 783 | "1.0 < 1.0rc1": false, 784 | "1.0 le 1.0rc1": false, 785 | "1.0 <= 1.0rc1": false, 786 | "1.0 gt 1.0rc1": true, 787 | "1.0 > 1.0rc1": true, 788 | "1.0 ge 1.0rc1": true, 789 | "1.0 >= 1.0rc1": true, 790 | "1.0 eq 1.0rc1": false, 791 | "1.0 = 1.0rc1": false, 792 | "1.0 == 1.0rc1": false, 793 | "1.0 ne 1.0rc1": true, 794 | "1.0 <> 1.0rc1": true, 795 | "1.0 != 1.0rc1": true, 796 | "1.0 lt 1.0": false, 797 | "1.0 < 1.0": false, 798 | "1.0 le 1.0": true, 799 | "1.0 <= 1.0": true, 800 | "1.0 gt 1.0": false, 801 | "1.0 > 1.0": false, 802 | "1.0 ge 1.0": true, 803 | "1.0 >= 1.0": true, 804 | "1.0 eq 1.0": true, 805 | "1.0 = 1.0": true, 806 | "1.0 == 1.0": true, 807 | "1.0 ne 1.0": false, 808 | "1.0 <> 1.0": false, 809 | "1.0 != 1.0": false, 810 | "1.0 lt 1.0pl1": true, 811 | "1.0 < 1.0pl1": true, 812 | "1.0 le 1.0pl1": true, 813 | "1.0 <= 1.0pl1": true, 814 | "1.0 gt 1.0pl1": false, 815 | "1.0 > 1.0pl1": false, 816 | "1.0 ge 1.0pl1": false, 817 | "1.0 >= 1.0pl1": false, 818 | "1.0 eq 1.0pl1": false, 819 | "1.0 = 1.0pl1": false, 820 | "1.0 == 1.0pl1": false, 821 | "1.0 ne 1.0pl1": true, 822 | "1.0 <> 1.0pl1": true, 823 | "1.0 != 1.0pl1": true, 824 | "1.0pl1 lt 1.0-dev": false, 825 | "1.0pl1 < 1.0-dev": false, 826 | "1.0pl1 le 1.0-dev": false, 827 | "1.0pl1 <= 1.0-dev": false, 828 | "1.0pl1 gt 1.0-dev": true, 829 | "1.0pl1 > 1.0-dev": true, 830 | "1.0pl1 ge 1.0-dev": true, 831 | "1.0pl1 >= 1.0-dev": true, 832 | "1.0pl1 eq 1.0-dev": false, 833 | "1.0pl1 = 1.0-dev": false, 834 | "1.0pl1 == 1.0-dev": false, 835 | "1.0pl1 ne 1.0-dev": true, 836 | "1.0pl1 <> 1.0-dev": true, 837 | "1.0pl1 != 1.0-dev": true, 838 | "1.0pl1 lt 1.0a1": false, 839 | "1.0pl1 < 1.0a1": false, 840 | "1.0pl1 le 1.0a1": false, 841 | "1.0pl1 <= 1.0a1": false, 842 | "1.0pl1 gt 1.0a1": true, 843 | "1.0pl1 > 1.0a1": true, 844 | "1.0pl1 ge 1.0a1": true, 845 | "1.0pl1 >= 1.0a1": true, 846 | "1.0pl1 eq 1.0a1": false, 847 | "1.0pl1 = 1.0a1": false, 848 | "1.0pl1 == 1.0a1": false, 849 | "1.0pl1 ne 1.0a1": true, 850 | "1.0pl1 <> 1.0a1": true, 851 | "1.0pl1 != 1.0a1": true, 852 | "1.0pl1 lt 1.0b1": false, 853 | "1.0pl1 < 1.0b1": false, 854 | "1.0pl1 le 1.0b1": false, 855 | "1.0pl1 <= 1.0b1": false, 856 | "1.0pl1 gt 1.0b1": true, 857 | "1.0pl1 > 1.0b1": true, 858 | "1.0pl1 ge 1.0b1": true, 859 | "1.0pl1 >= 1.0b1": true, 860 | "1.0pl1 eq 1.0b1": false, 861 | "1.0pl1 = 1.0b1": false, 862 | "1.0pl1 == 1.0b1": false, 863 | "1.0pl1 ne 1.0b1": true, 864 | "1.0pl1 <> 1.0b1": true, 865 | "1.0pl1 != 1.0b1": true, 866 | "1.0pl1 lt 1.0RC1": false, 867 | "1.0pl1 < 1.0RC1": false, 868 | "1.0pl1 le 1.0RC1": false, 869 | "1.0pl1 <= 1.0RC1": false, 870 | "1.0pl1 gt 1.0RC1": true, 871 | "1.0pl1 > 1.0RC1": true, 872 | "1.0pl1 ge 1.0RC1": true, 873 | "1.0pl1 >= 1.0RC1": true, 874 | "1.0pl1 eq 1.0RC1": false, 875 | "1.0pl1 = 1.0RC1": false, 876 | "1.0pl1 == 1.0RC1": false, 877 | "1.0pl1 ne 1.0RC1": true, 878 | "1.0pl1 <> 1.0RC1": true, 879 | "1.0pl1 != 1.0RC1": true, 880 | "1.0pl1 lt 1.0rc1": false, 881 | "1.0pl1 < 1.0rc1": false, 882 | "1.0pl1 le 1.0rc1": false, 883 | "1.0pl1 <= 1.0rc1": false, 884 | "1.0pl1 gt 1.0rc1": true, 885 | "1.0pl1 > 1.0rc1": true, 886 | "1.0pl1 ge 1.0rc1": true, 887 | "1.0pl1 >= 1.0rc1": true, 888 | "1.0pl1 eq 1.0rc1": false, 889 | "1.0pl1 = 1.0rc1": false, 890 | "1.0pl1 == 1.0rc1": false, 891 | "1.0pl1 ne 1.0rc1": true, 892 | "1.0pl1 <> 1.0rc1": true, 893 | "1.0pl1 != 1.0rc1": true, 894 | "1.0pl1 lt 1.0": false, 895 | "1.0pl1 < 1.0": false, 896 | "1.0pl1 le 1.0": false, 897 | "1.0pl1 <= 1.0": false, 898 | "1.0pl1 gt 1.0": true, 899 | "1.0pl1 > 1.0": true, 900 | "1.0pl1 ge 1.0": true, 901 | "1.0pl1 >= 1.0": true, 902 | "1.0pl1 eq 1.0": false, 903 | "1.0pl1 = 1.0": false, 904 | "1.0pl1 == 1.0": false, 905 | "1.0pl1 ne 1.0": true, 906 | "1.0pl1 <> 1.0": true, 907 | "1.0pl1 != 1.0": true, 908 | "1.0pl1 lt 1.0pl1": false, 909 | "1.0pl1 < 1.0pl1": false, 910 | "1.0pl1 le 1.0pl1": true, 911 | "1.0pl1 <= 1.0pl1": true, 912 | "1.0pl1 gt 1.0pl1": false, 913 | "1.0pl1 > 1.0pl1": false, 914 | "1.0pl1 ge 1.0pl1": true, 915 | "1.0pl1 >= 1.0pl1": true, 916 | "1.0pl1 eq 1.0pl1": true, 917 | "1.0pl1 = 1.0pl1": true, 918 | "1.0pl1 == 1.0pl1": true, 919 | "1.0pl1 ne 1.0pl1": false, 920 | "1.0pl1 <> 1.0pl1": false, 921 | "1.0pl1 != 1.0pl1": false, 922 | "2.2.3.0 < 2.4.0.0-dev": true, 923 | //Test unnormalized strings 924 | "2.3.4 < v3.1.2": false, 925 | "dev-master = 9999999-dev": false, 926 | } 927 | 928 | func TestCompareNormalized(t *testing.T) { 929 | for in, out := range compareNormalizedVersionValues { 930 | v := strings.Split(in, " ") 931 | if x := CompareNormalized(v[0], v[2], v[1]); x != out { 932 | t.Errorf("FAIL: CompareNormalized(%v) = %v: want %v", in, x, out) 933 | } 934 | } 935 | } 936 | 937 | var compareVersionValues = map[string]bool{ 938 | "2.3.4 < v3.1.2": true, 939 | "dev-master = 9999999-dev": true, 940 | } 941 | 942 | func TestCompare(t *testing.T) { 943 | for in, out := range compareVersionValues { 944 | v := strings.Split(in, " ") 945 | if x := Compare(v[0], v[2], v[1]); x != out { 946 | t.Errorf("FAIL: Compare(%v) = %v: want %v", in, x, out) 947 | } 948 | } 949 | } 950 | 951 | var simpleVersionFormatValues = map[string]bool{ 952 | "9999999-dev": true, 953 | "1.2.3": true, 954 | "abcdef": false, 955 | "1.abcdef": false, 956 | } 957 | 958 | func TestValidSimpleVersionFormat(t *testing.T) { 959 | for in, out := range simpleVersionFormatValues { 960 | if x := ValidSimpleVersionFormat(in); x != out { 961 | t.Errorf("FAIL: ValidSimpleVersionFormat(%v) = %v: want %v", in, x, out) 962 | } 963 | } 964 | } 965 | --------------------------------------------------------------------------------