Skip to content
This repository was archived by the owner on Sep 11, 2020. It is now read-only.

Add revision implementation #139

Merged
merged 59 commits into from
Feb 6, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
59 commits
Select commit Hold shift + click to select a range
31b7705
internal/revision: add scanner
Nov 24, 2016
c00a50d
internal/revision : add at symbol to scanner
Nov 25, 2016
2ccdc85
internal/revision : setup base parser
Nov 25, 2016
a393fc3
internal/revision : add several symbols
Nov 25, 2016
f449b4c
internal/revision : add reference name parser
Nov 27, 2016
a83eac6
internal/revision : define reference delimiters
Nov 27, 2016
11d0107
internal/revision : refactor reference parser
Nov 28, 2016
6684996
internal/revision : revision suffix parser
Nov 28, 2016
c5b8a84
internal/revision : add ref type
Nov 29, 2016
f3d886f
internal/revision : add rev suffix types
Nov 29, 2016
0dba6ac
internal/revision : add emark symbol to scanner
Nov 29, 2016
d449781
internal/revision : revision brace suffix parser
Nov 30, 2016
9e16613
internal/revision : @ suffix parser reflog
Dec 1, 2016
aa20b79
internal/revision : @ suffix parser checkout
Dec 5, 2016
a09b68a
internal/revision : fix wrong errors
Dec 5, 2016
be7c905
internal/revision : @ suffix parser upstream and push
Dec 6, 2016
af65745
internal/revision : rewrite
Dec 6, 2016
30a8cc9
internal/revision : missing unscan
Dec 7, 2016
a64c47b
internal/revision : replace suffixer with revisioner
Dec 7, 2016
64d5862
internal/revision : add general parse function
Dec 7, 2016
20d44a5
internal/revision : rename functions
Dec 7, 2016
6a090ac
internal/revision : solve @ alone as HEAD reference
Dec 7, 2016
c488270
internal/revision : remove "suffix" word
Dec 8, 2016
d9d9275
internal/revision : rename function to simplify
Dec 8, 2016
25c07c9
internal/revision : ":" suffix parser first step
Dec 8, 2016
c609385
internal/revision : fix comment
Dec 8, 2016
194afa3
internal/revision : fix parse
Dec 8, 2016
0c89a4e
internal/revision : update function names
Dec 8, 2016
610b55e
internal/revision : add test
Dec 8, 2016
db2976a
internal/revision : ":" suffix parser, add error
Dec 10, 2016
bb3617a
internal/revision : ":" suffix parser, add path parser
Dec 10, 2016
2895a42
internal/revision : remove code never reached
Dec 10, 2016
c7d2807
internal/revision : ":" suffix parser, add stage path parser
Dec 10, 2016
748a636
internal/revision : add scanner test
Dec 10, 2016
08de3d7
internal/revision : "@" suffix parser, add date parser
Dec 16, 2016
4166492
internal/revision : remove useless statement
Dec 18, 2016
a651b89
internal/revision : add validateFullRevision
Dec 18, 2016
0dc3817
internal/revision : update validateFullRevision
Dec 18, 2016
e287f17
internal/revision : add colonStagePath
Dec 18, 2016
58f53b1
internal/revision : remove wrong constraint
Dec 18, 2016
50f4344
internal/revision : update validateFullRevision
Dec 18, 2016
db6d3ac
internal/revision : use golang regexp
Dec 18, 2016
891cbdd
internal/revision : various changes
Dec 19, 2016
b17936b
internal/revision : add NewParserFromString
Dec 28, 2016
c55cfa4
internal/revision : fix caret depth
Dec 28, 2016
22ae690
repository : start to add ResolveRevision
Dec 29, 2016
7669f6e
internal/revision : use nil
Jan 2, 2017
c2d2cfb
repository : update ResolveRevision
Jan 2, 2017
f237d13
repository : update ResolveRevision
Jan 3, 2017
80d5dc0
repository : update ResolveRevision
Jan 4, 2017
4990f7a
internal/revision : remove empty line
Jan 9, 2017
4c3a027
internal/revision : update comments
Jan 10, 2017
2abdeb4
internal/revision : use a slice instead of string
Jan 10, 2017
c1e3972
internal/revision : several changes
Jan 11, 2017
22a6adc
internal/revision : update comment
Jan 11, 2017
be0d388
internal/revision : remove useless unscan
Jan 14, 2017
6090ea9
internal/revision : rewrite scanner
Jan 20, 2017
5851ff0
repository : fix tests
Jan 31, 2017
31d8ab2
internal/revision : refactor scanner
Feb 5, 2017
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
622 changes: 622 additions & 0 deletions internal/revision/parser.go

Large diffs are not rendered by default.

395 changes: 395 additions & 0 deletions internal/revision/parser_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,395 @@
package revision

import (
"bytes"
"regexp"
"time"

. "gopkg.in/check.v1"
)

type ParserSuite struct{}

var _ = Suite(&ParserSuite{})

func (s *ParserSuite) TestErrInvalidRevision(c *C) {
e := ErrInvalidRevision{"test"}

c.Assert(e.Error(), Equals, "Revision invalid : test")
}

func (s *ParserSuite) TestNewParserFromString(c *C) {
p := NewParserFromString("test")

c.Assert(p, FitsTypeOf, &Parser{})
}

func (s *ParserSuite) TestScan(c *C) {
parser := NewParser(bytes.NewBufferString("Hello world !"))

expected := []struct {
t token
s string
}{
{
word,
"Hello",
},
{
space,
" ",
},
{
word,
"world",
},
{
space,
" ",
},
{
emark,
"!",
},
}

for i := 0; ; {
tok, str, err := parser.scan()

if tok == eof {
return
}

c.Assert(err, Equals, nil)
c.Assert(str, Equals, expected[i].s)
c.Assert(tok, Equals, expected[i].t)

i++
}
}

func (s *ParserSuite) TestUnscan(c *C) {
parser := NewParser(bytes.NewBufferString("Hello world !"))

tok, str, err := parser.scan()

c.Assert(err, Equals, nil)
c.Assert(str, Equals, "Hello")
c.Assert(tok, Equals, word)

parser.unscan()

tok, str, err = parser.scan()

c.Assert(err, Equals, nil)
c.Assert(str, Equals, "Hello")
c.Assert(tok, Equals, word)
}

func (s *ParserSuite) TestParseWithValidExpression(c *C) {
tim, _ := time.Parse("2006-01-02T15:04:05Z", "2016-12-16T21:42:47Z")

datas := map[string]Revisioner{
"@": []Revisioner{Ref("HEAD")},
"@~3": []Revisioner{
Ref("HEAD"),
TildePath{3},
},
"@{2016-12-16T21:42:47Z}": []Revisioner{AtDate{tim}},
"@{1}": []Revisioner{AtReflog{1}},
"@{-1}": []Revisioner{AtCheckout{1}},
"master@{upstream}": []Revisioner{
Ref("master"),
AtUpstream{},
},
"@{upstream}": []Revisioner{
AtUpstream{},
},
"@{u}": []Revisioner{
AtUpstream{},
},
"master@{push}": []Revisioner{
Ref("master"),
AtPush{},
},
"master@{2016-12-16T21:42:47Z}": []Revisioner{
Ref("master"),
AtDate{tim},
},
"HEAD^": []Revisioner{
Ref("HEAD"),
CaretPath{1},
},
"master~3": []Revisioner{
Ref("master"),
TildePath{3},
},
"v0.99.8^{commit}": []Revisioner{
Ref("v0.99.8"),
CaretType{"commit"},
},
"v0.99.8^{}": []Revisioner{
Ref("v0.99.8"),
CaretType{"tag"},
},
"HEAD^{/fix nasty bug}": []Revisioner{
Ref("HEAD"),
CaretReg{regexp.MustCompile("fix nasty bug"), false},
},
":/fix nasty bug": []Revisioner{
ColonReg{regexp.MustCompile("fix nasty bug"), false},
},
"HEAD:README": []Revisioner{
Ref("HEAD"),
ColonPath{"README"},
},
":README": []Revisioner{
ColonPath{"README"},
},
"master:./README": []Revisioner{
Ref("master"),
ColonPath{"./README"},
},
"master^1~:./README": []Revisioner{
Ref("master"),
CaretPath{1},
TildePath{1},
ColonPath{"./README"},
},
":0:README": []Revisioner{
ColonStagePath{"README", 0},
},
":3:README": []Revisioner{
ColonStagePath{"README", 3},
},
"master~1^{/update}~5~^^1": []Revisioner{
Ref("master"),
TildePath{1},
CaretReg{regexp.MustCompile("update"), false},
TildePath{5},
TildePath{1},
CaretPath{1},
CaretPath{1},
},
}

for d, expected := range datas {
parser := NewParser(bytes.NewBufferString(d))

result, err := parser.Parse()

c.Assert(err, Equals, nil)
c.Assert(result, DeepEquals, expected)
}
}

func (s *ParserSuite) TestParseWithUnValidExpression(c *C) {
datas := map[string]error{
"..": &ErrInvalidRevision{`must not start with "."`},
"master^1master": &ErrInvalidRevision{`reference must be defined once at the beginning`},
"master^1@{2016-12-16T21:42:47Z}": &ErrInvalidRevision{`"@" statement is not valid, could be : <refname>@{<ISO-8601 date>}, @{<ISO-8601 date>}`},
"master^1@{1}": &ErrInvalidRevision{`"@" statement is not valid, could be : <refname>@{<n>}, @{<n>}`},
"master@{-1}": &ErrInvalidRevision{`"@" statement is not valid, could be : @{-<n>}`},
"master^1@{upstream}": &ErrInvalidRevision{`"@" statement is not valid, could be : <refname>@{upstream}, @{upstream}, <refname>@{u}, @{u}`},
"master^1@{u}": &ErrInvalidRevision{`"@" statement is not valid, could be : <refname>@{upstream}, @{upstream}, <refname>@{u}, @{u}`},
"master^1@{push}": &ErrInvalidRevision{`"@" statement is not valid, could be : <refname>@{push}, @{push}`},
"^1": &ErrInvalidRevision{`"~" or "^" statement must have a reference defined at the beginning`},
"^{/test}": &ErrInvalidRevision{`"~" or "^" statement must have a reference defined at the beginning`},
"~1": &ErrInvalidRevision{`"~" or "^" statement must have a reference defined at the beginning`},
"master:/test": &ErrInvalidRevision{`":" statement is not valid, could be : :/<regexp>`},
"master:0:README": &ErrInvalidRevision{`":" statement is not valid, could be : :<n>:<path>`},
}

for s, e := range datas {
parser := NewParser(bytes.NewBufferString(s))
_, err := parser.Parse()
c.Assert(err, DeepEquals, e)
}
}

func (s *ParserSuite) TestParseAtWithValidExpression(c *C) {
tim, _ := time.Parse("2006-01-02T15:04:05Z", "2016-12-16T21:42:47Z")

datas := map[string]Revisioner{
"": Ref("HEAD"),
"{1}": AtReflog{1},
"{-1}": AtCheckout{1},
"{push}": AtPush{},
"{upstream}": AtUpstream{},
"{u}": AtUpstream{},
"{2016-12-16T21:42:47Z}": AtDate{tim},
}

for d, expected := range datas {
parser := NewParser(bytes.NewBufferString(d))

result, err := parser.parseAt()

c.Assert(err, Equals, nil)
c.Assert(result, DeepEquals, expected)
}
}

func (s *ParserSuite) TestParseAtWithUnValidExpression(c *C) {
datas := map[string]error{
"{test}": &ErrInvalidRevision{`wrong date "test" must fit ISO-8601 format : 2006-01-02T15:04:05Z`},
"{-1": &ErrInvalidRevision{`missing "}" in @{-n} structure`},
}

for s, e := range datas {
parser := NewParser(bytes.NewBufferString(s))

_, err := parser.parseAt()

c.Assert(err, DeepEquals, e)
}
}

func (s *ParserSuite) TestParseCaretWithValidExpression(c *C) {
datas := map[string]Revisioner{
"": CaretPath{1},
"2": CaretPath{2},
"{}": CaretType{"tag"},
"{commit}": CaretType{"commit"},
"{tree}": CaretType{"tree"},
"{blob}": CaretType{"blob"},
"{tag}": CaretType{"tag"},
"{object}": CaretType{"object"},
"{/hello world !}": CaretReg{regexp.MustCompile("hello world !"), false},
"{/!-hello world !}": CaretReg{regexp.MustCompile("hello world !"), true},
"{/!! hello world !}": CaretReg{regexp.MustCompile("! hello world !"), false},
}

for d, expected := range datas {
parser := NewParser(bytes.NewBufferString(d))

result, err := parser.parseCaret()

c.Assert(err, Equals, nil)
c.Assert(result, DeepEquals, expected)
}
}

func (s *ParserSuite) TestParseCaretWithUnValidExpression(c *C) {
datas := map[string]error{
"3": &ErrInvalidRevision{`"3" found must be 0, 1 or 2 after "^"`},
"{test}": &ErrInvalidRevision{`"test" is not a valid revision suffix brace component`},
"{/!test}": &ErrInvalidRevision{`revision suffix brace component sequences starting with "/!" others than those defined are reserved`},
"{/test**}": &ErrInvalidRevision{"revision suffix brace component, error parsing regexp: invalid nested repetition operator: `**`"},
}

for s, e := range datas {
parser := NewParser(bytes.NewBufferString(s))

_, err := parser.parseCaret()

c.Assert(err, DeepEquals, e)
}
}

func (s *ParserSuite) TestParseTildeWithValidExpression(c *C) {
datas := map[string]Revisioner{
"3": TildePath{3},
"1": TildePath{1},
"": TildePath{1},
}

for d, expected := range datas {
parser := NewParser(bytes.NewBufferString(d))

result, err := parser.parseTilde()

c.Assert(err, Equals, nil)
c.Assert(result, DeepEquals, expected)
}
}

func (s *ParserSuite) TestParseColonWithValidExpression(c *C) {
datas := map[string]Revisioner{
"/hello world !": ColonReg{regexp.MustCompile("hello world !"), false},
"/!-hello world !": ColonReg{regexp.MustCompile("hello world !"), true},
"/!! hello world !": ColonReg{regexp.MustCompile("! hello world !"), false},
"../parser.go": ColonPath{"../parser.go"},
"./parser.go": ColonPath{"./parser.go"},
"parser.go": ColonPath{"parser.go"},
"0:parser.go": ColonStagePath{"parser.go", 0},
"1:parser.go": ColonStagePath{"parser.go", 1},
"2:parser.go": ColonStagePath{"parser.go", 2},
"3:parser.go": ColonStagePath{"parser.go", 3},
}

for d, expected := range datas {
parser := NewParser(bytes.NewBufferString(d))

result, err := parser.parseColon()

c.Assert(err, Equals, nil)
c.Assert(result, DeepEquals, expected)
}
}

func (s *ParserSuite) TestParseColonWithUnValidExpression(c *C) {
datas := map[string]error{
"/!test": &ErrInvalidRevision{`revision suffix brace component sequences starting with "/!" others than those defined are reserved`},
"/*": &ErrInvalidRevision{"revision suffix brace component, error parsing regexp: missing argument to repetition operator: `*`"},
}

for s, e := range datas {
parser := NewParser(bytes.NewBufferString(s))

_, err := parser.parseColon()

c.Assert(err, DeepEquals, e)
}
}

func (s *ParserSuite) TestParseRefWithValidName(c *C) {
datas := []string{
"lock",
"master",
"v1.0.0",
"refs/stash",
"refs/tags/v1.0.0",
"refs/heads/master",
"refs/remotes/test",
"refs/remotes/origin/HEAD",
"refs/remotes/origin/master",
}

for _, d := range datas {
parser := NewParser(bytes.NewBufferString(d))

result, err := parser.parseRef()

c.Assert(err, Equals, nil)
c.Assert(result, Equals, Ref(d))
}
}

func (s *ParserSuite) TestParseRefWithUnvalidName(c *C) {
datas := map[string]error{
".master": &ErrInvalidRevision{`must not start with "."`},
"/master": &ErrInvalidRevision{`must not start with "/"`},
"master/": &ErrInvalidRevision{`must not end with "/"`},
"master.": &ErrInvalidRevision{`must not end with "."`},
"refs/remotes/.origin/HEAD": &ErrInvalidRevision{`must not contains "/."`},
"test..test": &ErrInvalidRevision{`must not contains ".."`},
"test..": &ErrInvalidRevision{`must not contains ".."`},
"test test": &ErrInvalidRevision{`must not contains " "`},
"test*test": &ErrInvalidRevision{`must not contains "*"`},
"test?test": &ErrInvalidRevision{`must not contains "?"`},
"test\\test": &ErrInvalidRevision{`must not contains "\"`},
"test[test": &ErrInvalidRevision{`must not contains "["`},
"te//st": &ErrInvalidRevision{`must not contains consecutively "/"`},
"refs/remotes/test.lock/HEAD": &ErrInvalidRevision{`cannot end with .lock`},
"test.lock": &ErrInvalidRevision{`cannot end with .lock`},
}

for s, e := range datas {
parser := NewParser(bytes.NewBufferString(s))

_, err := parser.parseRef()

c.Assert(err, DeepEquals, e)
}
}
Loading