Browse Source

Full working set, missing some tests

Benedikt Lang 12 years ago
parent
commit
a76e16a4c7
2 changed files with 221 additions and 59 deletions
  1. +172
    -50
      semver.go
  2. +49
    -9
      semver_test.go

+ 172
- 50
semver.go View File

@ -3,7 +3,9 @@ package semver
import (
"bytes"
"errors"
"fmt"
"strconv"
"strings"
)
var SEMVER_SPEC_VERSION = Version{
@ -13,23 +15,23 @@ var SEMVER_SPEC_VERSION = Version{
}
type Version struct {
Major int
Minor int
Patch int
Pre []PRVersion
Major uint64
Minor uint64
Patch uint64
Pre []*PRVersion
Build []string //No Precendence
}
func (v Version) String() string {
func (v *Version) String() string {
var buf bytes.Buffer
var DOT = []byte(".")
var HYPHEN = []byte("-")
var PLUS = []byte("+")
buf.WriteString(strconv.Itoa(v.Major))
buf.WriteString(strconv.FormatUint(v.Major, 10))
buf.Write(DOT)
buf.WriteString(strconv.Itoa(v.Minor))
buf.WriteString(strconv.FormatUint(v.Minor, 10))
buf.Write(DOT)
buf.WriteString(strconv.Itoa(v.Patch))
buf.WriteString(strconv.FormatUint(v.Patch, 10))
if len(v.Pre) > 0 {
buf.Write(HYPHEN)
for i, pre := range v.Pre {
@ -51,7 +53,7 @@ func (v Version) String() string {
return buf.String()
}
func (v *Version) compare(o *Version) int {
func (v *Version) Compare(o *Version) int {
if v.Major != o.Major {
if v.Major > o.Major {
return 1
@ -74,74 +76,194 @@ func (v *Version) compare(o *Version) int {
}
}
// Quick comparison if a version has no prerelease versions
if len(v.Pre) == 0 && len(o.Pre) == 0 {
return 0
} else if len(v.Pre) == 0 && len(o.Pre) > 0 {
return -1
} else if len(v.Pre) > 0 && len(o.Pre) == 0 {
return 1
} else if len(v.Pre) > 0 && len(o.Pre) == 0 {
return -1
} else {
//Deep PreRelease Version comparison
return -2 //TODO: Not yet implemented
i := 0
for ; i < len(v.Pre) && i < len(o.Pre); i++ {
if comp := v.Pre[i].Compare(o.Pre[i]); comp == 0 {
continue
} else if comp == 1 {
return 1
} else {
return -1
}
}
}
// If all pr versions are the equal but one has further prversion, this one greater
if i == len(v.Pre) && i == len(o.Pre) {
return 0
} else if i == len(v.Pre) && i < len(o.Pre) {
return -1
} else {
return 1
}
}
}
func Parse(s string) (*Version, error) {
return nil, errors.New("Not implemented yet")
}
if len(s) == 0 {
return nil, errors.New("Version string empty")
}
// PreRelease Version
type PRVersion interface {
compare(*PRVersion) int
String() string //fmt.Stringer
IsNumeric() bool
}
parts := strings.SplitN(s, ".", 3)
if len(parts) != 3 {
return nil, errors.New("No Major.Minor.Patch elements found")
}
// Alphabetical PreRelease Version
type AlphaPRVersion struct {
Version string
}
if !containsOnly(parts[0], NUMBERS) {
return nil, fmt.Errorf("Invalid character(s) found in major number %q", parts[0])
}
major, err := strconv.ParseUint(parts[0], 10, 64)
if err != nil {
return nil, err
}
func (v *AlphaPRVersion) IsNumeric() bool {
return false
}
if !containsOnly(parts[1], NUMBERS) {
return nil, fmt.Errorf("Invalid character(s) found in minor number %q", parts[1])
}
minor, err := strconv.ParseUint(parts[1], 10, 64)
if err != nil {
return nil, err
}
func (v *AlphaPRVersion) compare(o *AlphaPRVersion) int {
if v.Version == o.Version {
return 0
} else if v.Version > o.Version {
return 1
preIndex := strings.Index(parts[2], "-")
buildIndex := strings.Index(parts[2], "+")
var subVersionIndex int
if preIndex != -1 && buildIndex == -1 {
subVersionIndex = preIndex
} else if preIndex == -1 && buildIndex != -1 {
subVersionIndex = buildIndex
} else if preIndex == -1 && buildIndex == -1 {
subVersionIndex = len(parts[2])
} else {
return -1
// if there is no actual preIndex but a hyphen inside the build meta data
if buildIndex < preIndex {
subVersionIndex = buildIndex
preIndex = -1 // Build meta data before preIndex found implicates there are no prerelease versions
} else {
subVersionIndex = preIndex
}
}
}
func (v AlphaPRVersion) String() string {
return v.Version
if !containsOnly(parts[2][:subVersionIndex], NUMBERS) {
return nil, fmt.Errorf("Invalid character(s) found in patch number %q", parts[2][:subVersionIndex])
}
patch, err := strconv.ParseUint(parts[2][:subVersionIndex], 10, 64)
if err != nil {
return nil, err
}
v := &Version{}
v.Major = major
v.Minor = minor
v.Patch = patch
// There are PreRelease versions
if preIndex != -1 {
var preRels string
if buildIndex != -1 {
preRels = parts[2][subVersionIndex+1 : buildIndex]
} else {
preRels = parts[2][subVersionIndex+1:]
}
prparts := strings.Split(preRels, ".")
for _, prstr := range prparts {
parsedPR, err := NewPRVersion(prstr)
if err != nil {
return nil, err
}
v.Pre = append(v.Pre, parsedPR)
}
}
// There is build meta data
if buildIndex != -1 {
buildStr := parts[2][buildIndex+1:]
buildParts := strings.Split(buildStr, ".")
for _, str := range buildParts {
if !containsOnly(str, ALPHAS+NUMBERS) {
return nil, fmt.Errorf("Invalid character(s) found in build meta data %q", str)
}
v.Build = append(v.Build, str)
}
}
return v, nil
}
// Numeric PreRelease Version
type NumPRVersion struct {
Version int
// PreRelease Version
type PRVersion struct {
VersionStr string
VersionNum uint64
IsNum bool
}
func (v *NumPRVersion) compare(o *NumPRVersion) int {
if v.Version == o.Version {
return 0
} else if v.Version > o.Version {
return 1
func NewPRVersion(s string) (*PRVersion, error) {
v := &PRVersion{}
if containsOnly(s, NUMBERS) {
num, err := strconv.ParseUint(s, 10, 64)
if err != nil {
return nil, err
}
v.VersionNum = num
v.IsNum = true
} else if containsOnly(s, ALPHAS+NUMBERS) {
v.VersionStr = s
v.IsNum = false
} else {
return nil, fmt.Errorf("Invalid character(s) found in prerelease %q", s)
}
return v, nil
}
func (v *PRVersion) IsNumeric() bool {
return v.IsNum
}
func (v *PRVersion) Compare(o *PRVersion) int {
if v.IsNum && !o.IsNum {
return -1
} else if !v.IsNum && o.IsNum {
return 1
} else if v.IsNum && o.IsNum {
if v.VersionNum == o.VersionNum {
return 0
} else if v.VersionNum > o.VersionNum {
return 1
} else {
return -1
}
} else { // both are Alphas
if v.VersionStr == o.VersionStr {
return 0
} else if v.VersionStr > o.VersionStr {
return 1
} else {
return -1
}
}
}
func (v NumPRVersion) String() string {
return strconv.Itoa(v.Version)
func (v *PRVersion) String() string {
if v.IsNum {
return strconv.FormatUint(v.VersionNum, 10)
}
return v.VersionStr
}
func (v *NumPRVersion) IsNumeric() bool {
return true
const NUMBERS = "0123456789"
const ALPHAS = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ-"
func containsOnly(s string, set string) bool {
return strings.IndexFunc(s, func(r rune) bool {
return !strings.ContainsRune(set, r)
}) == -1
}

+ 49
- 9
semver_test.go View File

@ -1,31 +1,55 @@
package semver
//TODO: Test incorrect version formats
import (
"testing"
)
func TestParse(t *testing.T) {
}
type stringerTest struct {
type formatTest struct {
v Version
result string
}
var stringerTests = []stringerTest{
var formatTests = []formatTest{
{Version{1, 2, 3, nil, nil}, "1.2.3"},
{Version{0, 0, 1, nil, nil}, "0.0.1"},
{Version{0, 0, 1, []*PRVersion{prstr("alpha"), prstr("preview")}, []string{"123", "456"}}, "0.0.1-alpha.preview+123.456"},
{Version{1, 2, 3, []*PRVersion{prstr("alpha"), prnum(1)}, []string{"123", "456"}}, "1.2.3-alpha.1+123.456"},
{Version{1, 2, 3, []*PRVersion{prstr("alpha"), prnum(1)}, nil}, "1.2.3-alpha.1"},
{Version{1, 2, 3, nil, []string{"123", "456"}}, "1.2.3+123.456"},
// Prereleases and build metadata hyphens
{Version{1, 2, 3, []*PRVersion{prstr("alpha"), prstr("b-eta")}, []string{"123", "b-uild"}}, "1.2.3-alpha.b-eta+123.b-uild"},
{Version{1, 2, 3, nil, []string{"123", "b-uild"}}, "1.2.3+123.b-uild"},
{Version{1, 2, 3, []*PRVersion{prstr("alpha"), prstr("b-eta")}, nil}, "1.2.3-alpha.b-eta"},
}
func prstr(s string) *PRVersion {
return &PRVersion{s, 0, false}
}
func prnum(i uint64) *PRVersion {
return &PRVersion{"", i, true}
}
func TestStringer(t *testing.T) {
for _, test := range stringerTests {
for _, test := range formatTests {
if res := test.v.String(); res != test.result {
t.Errorf("Stringer, expected %q but got %q", test.result, res)
}
}
}
func TestParse(t *testing.T) {
for _, test := range formatTests {
if v, err := Parse(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)
}
}
}
type compareTest struct {
v1 Version
v2 Version
@ -43,15 +67,31 @@ var compareTests = []compareTest{
{Version{2, 2, 4, nil, nil}, Version{1, 2, 4, nil, nil}, 1},
{Version{1, 3, 3, nil, nil}, Version{1, 2, 3, nil, nil}, 1},
{Version{1, 2, 4, nil, nil}, Version{1, 2, 3, nil, nil}, 1},
// Spec Examples #11
{Version{1, 0, 0, nil, nil}, Version{2, 0, 0, nil, nil}, -1},
{Version{2, 0, 0, nil, nil}, Version{2, 1, 0, nil, nil}, -1},
{Version{2, 1, 0, nil, nil}, Version{2, 1, 1, nil, nil}, -1},
{Version{1, 0, 0, []*PRVersion{prstr("alpha")}, nil}, Version{1, 0, 0, []*PRVersion{prstr("alpha"), prnum(1)}, nil}, -1},
{Version{1, 0, 0, []*PRVersion{prstr("alpha"), prnum(1)}, nil}, Version{1, 0, 0, []*PRVersion{prstr("alpha"), prstr("beta")}, nil}, -1},
{Version{1, 0, 0, []*PRVersion{prstr("alpha"), prstr("beta")}, nil}, Version{1, 0, 0, []*PRVersion{prstr("beta")}, nil}, -1},
{Version{1, 0, 0, []*PRVersion{prstr("beta")}, nil}, Version{1, 0, 0, []*PRVersion{prstr("beta"), prnum(2)}, nil}, -1},
{Version{1, 0, 0, []*PRVersion{prstr("beta"), prnum(2)}, nil}, Version{1, 0, 0, []*PRVersion{prstr("beta"), prnum(11)}, nil}, -1},
{Version{1, 0, 0, []*PRVersion{prstr("beta"), prnum(11)}, nil}, Version{1, 0, 0, []*PRVersion{prstr("rc"), prnum(1)}, nil}, -1},
{Version{1, 0, 0, []*PRVersion{prstr("rc"), prnum(1)}, nil}, Version{1, 0, 0, nil, nil}, -1},
// Ignore Build metadata
{Version{1, 0, 0, nil, []string{"1", "2", "3"}}, Version{1, 0, 0, nil, nil}, 0},
}
func TestCompare(t *testing.T) {
for _, test := range compareTests {
if res := test.v1.compare(&test.v2); res != test.result {
if res := test.v1.Compare(&test.v2); res != test.result {
t.Errorf("Comparing %q : %q, expected %d but got %d", test.v1, test.v2, test.result, res)
}
//Test counterpart
if res := test.v2.compare(&test.v1); res != -test.result {
if res := test.v2.Compare(&test.v1); res != -test.result {
t.Errorf("Comparing %q : %q, expected %d but got %d", test.v2, test.v1, -test.result, res)
}
}


Loading…
Cancel
Save