summaryrefslogtreecommitdiffstats
path: root/vendor/github.com/hashicorp/hcl/hcl/parser/parser_test.go
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/github.com/hashicorp/hcl/hcl/parser/parser_test.go')
-rw-r--r--vendor/github.com/hashicorp/hcl/hcl/parser/parser_test.go566
1 files changed, 566 insertions, 0 deletions
diff --git a/vendor/github.com/hashicorp/hcl/hcl/parser/parser_test.go b/vendor/github.com/hashicorp/hcl/hcl/parser/parser_test.go
new file mode 100644
index 000000000..575865838
--- /dev/null
+++ b/vendor/github.com/hashicorp/hcl/hcl/parser/parser_test.go
@@ -0,0 +1,566 @@
+package parser
+
+import (
+ "fmt"
+ "io/ioutil"
+ "path/filepath"
+ "reflect"
+ "runtime"
+ "strings"
+ "testing"
+
+ "github.com/hashicorp/hcl/hcl/ast"
+ "github.com/hashicorp/hcl/hcl/token"
+)
+
+func TestType(t *testing.T) {
+ var literals = []struct {
+ typ token.Type
+ src string
+ }{
+ {token.STRING, `foo = "foo"`},
+ {token.NUMBER, `foo = 123`},
+ {token.NUMBER, `foo = -29`},
+ {token.FLOAT, `foo = 123.12`},
+ {token.FLOAT, `foo = -123.12`},
+ {token.BOOL, `foo = true`},
+ {token.HEREDOC, "foo = <<EOF\nHello\nWorld\nEOF"},
+ }
+
+ for _, l := range literals {
+ p := newParser([]byte(l.src))
+ item, err := p.objectItem()
+ if err != nil {
+ t.Error(err)
+ }
+
+ lit, ok := item.Val.(*ast.LiteralType)
+ if !ok {
+ t.Errorf("node should be of type LiteralType, got: %T", item.Val)
+ }
+
+ if lit.Token.Type != l.typ {
+ t.Errorf("want: %s, got: %s", l.typ, lit.Token.Type)
+ }
+ }
+}
+
+func TestListType(t *testing.T) {
+ var literals = []struct {
+ src string
+ tokens []token.Type
+ }{
+ {
+ `foo = ["123", 123]`,
+ []token.Type{token.STRING, token.NUMBER},
+ },
+ {
+ `foo = [123, "123",]`,
+ []token.Type{token.NUMBER, token.STRING},
+ },
+ {
+ `foo = [false]`,
+ []token.Type{token.BOOL},
+ },
+ {
+ `foo = []`,
+ []token.Type{},
+ },
+ {
+ `foo = [1,
+"string",
+<<EOF
+heredoc contents
+EOF
+]`,
+ []token.Type{token.NUMBER, token.STRING, token.HEREDOC},
+ },
+ }
+
+ for _, l := range literals {
+ p := newParser([]byte(l.src))
+ item, err := p.objectItem()
+ if err != nil {
+ t.Error(err)
+ }
+
+ list, ok := item.Val.(*ast.ListType)
+ if !ok {
+ t.Errorf("node should be of type LiteralType, got: %T", item.Val)
+ }
+
+ tokens := []token.Type{}
+ for _, li := range list.List {
+ if tp, ok := li.(*ast.LiteralType); ok {
+ tokens = append(tokens, tp.Token.Type)
+ }
+ }
+
+ equals(t, l.tokens, tokens)
+ }
+}
+
+func TestListOfMaps(t *testing.T) {
+ src := `foo = [
+ {key = "bar"},
+ {key = "baz", key2 = "qux"},
+ ]`
+ p := newParser([]byte(src))
+
+ file, err := p.Parse()
+ if err != nil {
+ t.Fatalf("err: %s", err)
+ }
+
+ // Here we make all sorts of assumptions about the input structure w/ type
+ // assertions. The intent is only for this to be a "smoke test" ensuring
+ // parsing actually performed its duty - giving this test something a bit
+ // more robust than _just_ "no error occurred".
+ expected := []string{`"bar"`, `"baz"`, `"qux"`}
+ actual := make([]string, 0, 3)
+ ol := file.Node.(*ast.ObjectList)
+ objItem := ol.Items[0]
+ list := objItem.Val.(*ast.ListType)
+ for _, node := range list.List {
+ obj := node.(*ast.ObjectType)
+ for _, item := range obj.List.Items {
+ val := item.Val.(*ast.LiteralType)
+ actual = append(actual, val.Token.Text)
+ }
+
+ }
+ if !reflect.DeepEqual(expected, actual) {
+ t.Fatalf("Expected: %#v, got %#v", expected, actual)
+ }
+}
+
+func TestListOfMaps_requiresComma(t *testing.T) {
+ src := `foo = [
+ {key = "bar"}
+ {key = "baz"}
+ ]`
+ p := newParser([]byte(src))
+
+ _, err := p.Parse()
+ if err == nil {
+ t.Fatalf("Expected error, got none!")
+ }
+
+ expected := "error parsing list, expected comma or list end"
+ if !strings.Contains(err.Error(), expected) {
+ t.Fatalf("Expected err:\n %s\nTo contain:\n %s\n", err, expected)
+ }
+}
+
+func TestListType_leadComment(t *testing.T) {
+ var literals = []struct {
+ src string
+ comment []string
+ }{
+ {
+ `foo = [
+ 1,
+ # bar
+ 2,
+ 3,
+ ]`,
+ []string{"", "# bar", ""},
+ },
+ }
+
+ for _, l := range literals {
+ p := newParser([]byte(l.src))
+ item, err := p.objectItem()
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ list, ok := item.Val.(*ast.ListType)
+ if !ok {
+ t.Fatalf("node should be of type LiteralType, got: %T", item.Val)
+ }
+
+ if len(list.List) != len(l.comment) {
+ t.Fatalf("bad: %d", len(list.List))
+ }
+
+ for i, li := range list.List {
+ lt := li.(*ast.LiteralType)
+ comment := l.comment[i]
+
+ if (lt.LeadComment == nil) != (comment == "") {
+ t.Fatalf("bad: %#v", lt)
+ }
+
+ if comment == "" {
+ continue
+ }
+
+ actual := lt.LeadComment.List[0].Text
+ if actual != comment {
+ t.Fatalf("bad: %q %q", actual, comment)
+ }
+ }
+ }
+}
+
+func TestListType_lineComment(t *testing.T) {
+ var literals = []struct {
+ src string
+ comment []string
+ }{
+ {
+ `foo = [
+ 1,
+ 2, # bar
+ 3,
+ ]`,
+ []string{"", "# bar", ""},
+ },
+ }
+
+ for _, l := range literals {
+ p := newParser([]byte(l.src))
+ item, err := p.objectItem()
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ list, ok := item.Val.(*ast.ListType)
+ if !ok {
+ t.Fatalf("node should be of type LiteralType, got: %T", item.Val)
+ }
+
+ if len(list.List) != len(l.comment) {
+ t.Fatalf("bad: %d", len(list.List))
+ }
+
+ for i, li := range list.List {
+ lt := li.(*ast.LiteralType)
+ comment := l.comment[i]
+
+ if (lt.LineComment == nil) != (comment == "") {
+ t.Fatalf("bad: %s", lt)
+ }
+
+ if comment == "" {
+ continue
+ }
+
+ actual := lt.LineComment.List[0].Text
+ if actual != comment {
+ t.Fatalf("bad: %q %q", actual, comment)
+ }
+ }
+ }
+}
+
+func TestObjectType(t *testing.T) {
+ var literals = []struct {
+ src string
+ nodeType []ast.Node
+ itemLen int
+ }{
+ {
+ `foo = {}`,
+ nil,
+ 0,
+ },
+ {
+ `foo = {
+ bar = "fatih"
+ }`,
+ []ast.Node{&ast.LiteralType{}},
+ 1,
+ },
+ {
+ `foo = {
+ bar = "fatih"
+ baz = ["arslan"]
+ }`,
+ []ast.Node{
+ &ast.LiteralType{},
+ &ast.ListType{},
+ },
+ 2,
+ },
+ {
+ `foo = {
+ bar {}
+ }`,
+ []ast.Node{
+ &ast.ObjectType{},
+ },
+ 1,
+ },
+ {
+ `foo {
+ bar {}
+ foo = true
+ }`,
+ []ast.Node{
+ &ast.ObjectType{},
+ &ast.LiteralType{},
+ },
+ 2,
+ },
+ }
+
+ for _, l := range literals {
+ t.Logf("Source: %s", l.src)
+
+ p := newParser([]byte(l.src))
+ // p.enableTrace = true
+ item, err := p.objectItem()
+ if err != nil {
+ t.Error(err)
+ continue
+ }
+
+ // we know that the ObjectKey name is foo for all cases, what matters
+ // is the object
+ obj, ok := item.Val.(*ast.ObjectType)
+ if !ok {
+ t.Errorf("node should be of type LiteralType, got: %T", item.Val)
+ continue
+ }
+
+ // check if the total length of items are correct
+ equals(t, l.itemLen, len(obj.List.Items))
+
+ // check if the types are correct
+ for i, item := range obj.List.Items {
+ equals(t, reflect.TypeOf(l.nodeType[i]), reflect.TypeOf(item.Val))
+ }
+ }
+}
+
+func TestObjectKey(t *testing.T) {
+ keys := []struct {
+ exp []token.Type
+ src string
+ }{
+ {[]token.Type{token.IDENT}, `foo {}`},
+ {[]token.Type{token.IDENT}, `foo = {}`},
+ {[]token.Type{token.IDENT}, `foo = bar`},
+ {[]token.Type{token.IDENT}, `foo = 123`},
+ {[]token.Type{token.IDENT}, `foo = "${var.bar}`},
+ {[]token.Type{token.STRING}, `"foo" {}`},
+ {[]token.Type{token.STRING}, `"foo" = {}`},
+ {[]token.Type{token.STRING}, `"foo" = "${var.bar}`},
+ {[]token.Type{token.IDENT, token.IDENT}, `foo bar {}`},
+ {[]token.Type{token.IDENT, token.STRING}, `foo "bar" {}`},
+ {[]token.Type{token.STRING, token.IDENT}, `"foo" bar {}`},
+ {[]token.Type{token.IDENT, token.IDENT, token.IDENT}, `foo bar baz {}`},
+ }
+
+ for _, k := range keys {
+ p := newParser([]byte(k.src))
+ keys, err := p.objectKey()
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ tokens := []token.Type{}
+ for _, o := range keys {
+ tokens = append(tokens, o.Token.Type)
+ }
+
+ equals(t, k.exp, tokens)
+ }
+
+ errKeys := []struct {
+ src string
+ }{
+ {`foo 12 {}`},
+ {`foo bar = {}`},
+ {`foo []`},
+ {`12 {}`},
+ }
+
+ for _, k := range errKeys {
+ p := newParser([]byte(k.src))
+ _, err := p.objectKey()
+ if err == nil {
+ t.Errorf("case '%s' should give an error", k.src)
+ }
+ }
+}
+
+func TestCommentGroup(t *testing.T) {
+ var cases = []struct {
+ src string
+ groups int
+ }{
+ {"# Hello\n# World", 1},
+ }
+
+ for _, tc := range cases {
+ t.Run(tc.src, func(t *testing.T) {
+ p := newParser([]byte(tc.src))
+ file, err := p.Parse()
+ if err != nil {
+ t.Fatalf("parse error: %s", err)
+ }
+
+ if len(file.Comments) != tc.groups {
+ t.Fatalf("bad: %#v", file.Comments)
+ }
+ })
+ }
+}
+
+// Official HCL tests
+func TestParse(t *testing.T) {
+ cases := []struct {
+ Name string
+ Err bool
+ }{
+ {
+ "assign_colon.hcl",
+ true,
+ },
+ {
+ "comment.hcl",
+ false,
+ },
+ {
+ "comment_lastline.hcl",
+ false,
+ },
+ {
+ "comment_single.hcl",
+ false,
+ },
+ {
+ "empty.hcl",
+ false,
+ },
+ {
+ "list_comma.hcl",
+ false,
+ },
+ {
+ "multiple.hcl",
+ false,
+ },
+ {
+ "object_list_comma.hcl",
+ false,
+ },
+ {
+ "structure.hcl",
+ false,
+ },
+ {
+ "structure_basic.hcl",
+ false,
+ },
+ {
+ "structure_empty.hcl",
+ false,
+ },
+ {
+ "complex.hcl",
+ false,
+ },
+ {
+ "types.hcl",
+ false,
+ },
+ {
+ "array_comment.hcl",
+ false,
+ },
+ {
+ "array_comment_2.hcl",
+ true,
+ },
+ {
+ "missing_braces.hcl",
+ true,
+ },
+ {
+ "unterminated_object.hcl",
+ true,
+ },
+ {
+ "unterminated_object_2.hcl",
+ true,
+ },
+ {
+ "key_without_value.hcl",
+ true,
+ },
+ {
+ "object_key_without_value.hcl",
+ true,
+ },
+ {
+ "object_key_assign_without_value.hcl",
+ true,
+ },
+ {
+ "object_key_assign_without_value2.hcl",
+ true,
+ },
+ {
+ "object_key_assign_without_value3.hcl",
+ true,
+ },
+ {
+ "git_crypt.hcl",
+ true,
+ },
+ }
+
+ const fixtureDir = "./test-fixtures"
+
+ for _, tc := range cases {
+ t.Run(tc.Name, func(t *testing.T) {
+ d, err := ioutil.ReadFile(filepath.Join(fixtureDir, tc.Name))
+ if err != nil {
+ t.Fatalf("err: %s", err)
+ }
+
+ v, err := Parse(d)
+ if (err != nil) != tc.Err {
+ t.Fatalf("Input: %s\n\nError: %s\n\nAST: %#v", tc.Name, err, v)
+ }
+ })
+ }
+}
+
+func TestParse_inline(t *testing.T) {
+ cases := []struct {
+ Value string
+ Err bool
+ }{
+ {"t t e{{}}", true},
+ {"o{{}}", true},
+ {"t t e d N{{}}", true},
+ {"t t e d{{}}", true},
+ {"N{}N{{}}", true},
+ {"v\nN{{}}", true},
+ {"v=/\n[,", true},
+ {"v=10kb", true},
+ {"v=/foo", true},
+ }
+
+ for _, tc := range cases {
+ t.Logf("Testing: %q", tc.Value)
+ ast, err := Parse([]byte(tc.Value))
+ if (err != nil) != tc.Err {
+ t.Fatalf("Input: %q\n\nError: %s\n\nAST: %#v", tc.Value, err, ast)
+ }
+ }
+}
+
+// equals fails the test if exp is not equal to act.
+func equals(tb testing.TB, exp, act interface{}) {
+ if !reflect.DeepEqual(exp, act) {
+ _, file, line, _ := runtime.Caller(1)
+ fmt.Printf("\033[31m%s:%d:\n\n\texp: %#v\n\n\tgot: %#v\033[39m\n\n", filepath.Base(file), line, exp, act)
+ tb.FailNow()
+ }
+}