blob: 6ee1b47010284925ac11b7405392d0fc82e3cbf9 [file] [log] [blame]
// Copyright 2011 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package parse
import (
"reflect"
"testing"
)
type lexTest struct {
name string
input string
items []item
}
var (
tEOF = item{itemEOF, ""}
tLeft = item{itemLeftDelim, "{{"}
tRight = item{itemRightDelim, "}}"}
tRange = item{itemRange, "range"}
tPipe = item{itemPipe, "|"}
tFor = item{itemIdentifier, "for"}
tQuote = item{itemString, `"abc \n\t\" "`}
raw = "`" + `abc\n\t\" ` + "`"
tRawQuote = item{itemRawString, raw}
)
var lexTests = []lexTest{
{"empty", "", []item{tEOF}},
{"spaces", " \t\n", []item{{itemText, " \t\n"}, tEOF}},
{"text", `now is the time`, []item{{itemText, "now is the time"}, tEOF}},
{"text with comment", "hello-{{/* this is a comment */}}-world", []item{
{itemText, "hello-"},
{itemText, "-world"},
tEOF,
}},
{"punctuation", "{{,@%}}", []item{
tLeft,
{itemChar, ","},
{itemChar, "@"},
{itemChar, "%"},
tRight,
tEOF,
}},
{"empty action", `{{}}`, []item{tLeft, tRight, tEOF}},
{"for", `{{for }}`, []item{tLeft, tFor, tRight, tEOF}},
{"quote", `{{"abc \n\t\" "}}`, []item{tLeft, tQuote, tRight, tEOF}},
{"raw quote", "{{" + raw + "}}", []item{tLeft, tRawQuote, tRight, tEOF}},
{"numbers", "{{1 02 0x14 -7.2i 1e3 +1.2e-4 4.2i 1+2i}}", []item{
tLeft,
{itemNumber, "1"},
{itemNumber, "02"},
{itemNumber, "0x14"},
{itemNumber, "-7.2i"},
{itemNumber, "1e3"},
{itemNumber, "+1.2e-4"},
{itemNumber, "4.2i"},
{itemComplex, "1+2i"},
tRight,
tEOF,
}},
{"characters", `{{'a' '\n' '\'' '\\' '\u00FF' '\xFF' '本'}}`, []item{
tLeft,
{itemCharConstant, `'a'`},
{itemCharConstant, `'\n'`},
{itemCharConstant, `'\''`},
{itemCharConstant, `'\\'`},
{itemCharConstant, `'\u00FF'`},
{itemCharConstant, `'\xFF'`},
{itemCharConstant, `'本'`},
tRight,
tEOF,
}},
{"bools", "{{true false}}", []item{
tLeft,
{itemBool, "true"},
{itemBool, "false"},
tRight,
tEOF,
}},
{"dot", "{{.}}", []item{
tLeft,
{itemDot, "."},
tRight,
tEOF,
}},
{"dots", "{{.x . .2 .x.y}}", []item{
tLeft,
{itemField, ".x"},
{itemDot, "."},
{itemNumber, ".2"},
{itemField, ".x.y"},
tRight,
tEOF,
}},
{"keywords", "{{range if else end with}}", []item{
tLeft,
{itemRange, "range"},
{itemIf, "if"},
{itemElse, "else"},
{itemEnd, "end"},
{itemWith, "with"},
tRight,
tEOF,
}},
{"variables", "{{$c := printf $ $hello $23 $ $var.Field .Method}}", []item{
tLeft,
{itemVariable, "$c"},
{itemColonEquals, ":="},
{itemIdentifier, "printf"},
{itemVariable, "$"},
{itemVariable, "$hello"},
{itemVariable, "$23"},
{itemVariable, "$"},
{itemVariable, "$var.Field"},
{itemField, ".Method"},
tRight,
tEOF,
}},
{"pipeline", `intro {{echo hi 1.2 |noargs|args 1 "hi"}} outro`, []item{
{itemText, "intro "},
tLeft,
{itemIdentifier, "echo"},
{itemIdentifier, "hi"},
{itemNumber, "1.2"},
tPipe,
{itemIdentifier, "noargs"},
tPipe,
{itemIdentifier, "args"},
{itemNumber, "1"},
{itemString, `"hi"`},
tRight,
{itemText, " outro"},
tEOF,
}},
{"declaration", "{{$v := 3}}", []item{
tLeft,
{itemVariable, "$v"},
{itemColonEquals, ":="},
{itemNumber, "3"},
tRight,
tEOF,
}},
{"2 declarations", "{{$v , $w := 3}}", []item{
tLeft,
{itemVariable, "$v"},
{itemChar, ","},
{itemVariable, "$w"},
{itemColonEquals, ":="},
{itemNumber, "3"},
tRight,
tEOF,
}},
// errors
{"badchar", "#{{\x01}}", []item{
{itemText, "#"},
tLeft,
{itemError, "unrecognized character in action: U+0001"},
}},
{"unclosed action", "{{\n}}", []item{
tLeft,
{itemError, "unclosed action"},
}},
{"EOF in action", "{{range", []item{
tLeft,
tRange,
{itemError, "unclosed action"},
}},
{"unclosed quote", "{{\"\n\"}}", []item{
tLeft,
{itemError, "unterminated quoted string"},
}},
{"unclosed raw quote", "{{`xx\n`}}", []item{
tLeft,
{itemError, "unterminated raw quoted string"},
}},
{"unclosed char constant", "{{'\n}}", []item{
tLeft,
{itemError, "unterminated character constant"},
}},
{"bad number", "{{3k}}", []item{
tLeft,
{itemError, `bad number syntax: "3k"`},
}},
// Fixed bugs
// Many elements in an action blew the lookahead until
// we made lexInsideAction not loop.
{"long pipeline deadlock", "{{|||||}}", []item{
tLeft,
tPipe,
tPipe,
tPipe,
tPipe,
tPipe,
tRight,
tEOF,
}},
}
// collect gathers the emitted items into a slice.
func collect(t *lexTest, left, right string) (items []item) {
l := lex(t.name, t.input, left, right)
for {
item := l.nextItem()
items = append(items, item)
if item.typ == itemEOF || item.typ == itemError {
break
}
}
return
}
func TestLex(t *testing.T) {
for _, test := range lexTests {
items := collect(&test, "", "")
if !reflect.DeepEqual(items, test.items) {
t.Errorf("%s: got\n\t%v\nexpected\n\t%v", test.name, items, test.items)
}
}
}
// Some easy cases from above, but with delimiters $$ and @@
var lexDelimTests = []lexTest{
{"punctuation", "$$,@%{{}}@@", []item{
tLeftDelim,
{itemChar, ","},
{itemChar, "@"},
{itemChar, "%"},
{itemChar, "{"},
{itemChar, "{"},
{itemChar, "}"},
{itemChar, "}"},
tRightDelim,
tEOF,
}},
{"empty action", `$$@@`, []item{tLeftDelim, tRightDelim, tEOF}},
{"for", `$$for @@`, []item{tLeftDelim, tFor, tRightDelim, tEOF}},
{"quote", `$$"abc \n\t\" "@@`, []item{tLeftDelim, tQuote, tRightDelim, tEOF}},
{"raw quote", "$$" + raw + "@@", []item{tLeftDelim, tRawQuote, tRightDelim, tEOF}},
}
var (
tLeftDelim = item{itemLeftDelim, "$$"}
tRightDelim = item{itemRightDelim, "@@"}
)
func TestDelims(t *testing.T) {
for _, test := range lexDelimTests {
items := collect(&test, "$$", "@@")
if !reflect.DeepEqual(items, test.items) {
t.Errorf("%s: got\n\t%v\nexpected\n\t%v", test.name, items, test.items)
}
}
}