| // Copyright 2021 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 template | 
 |  | 
 | import ( | 
 | 	"strings" | 
 | 	"testing" | 
 | ) | 
 |  | 
 | type datum struct { | 
 | 	buf  string | 
 | 	cnt  int | 
 | 	syms []string // the symbols in the parse of buf | 
 | } | 
 |  | 
 | var tmpl = []datum{{` | 
 | {{if (foo .X.Y)}}{{$A := "hi"}}{{.Z $A}}{{else}} | 
 | {{$A.X 12}} | 
 | {{foo (.X.Y) 23 ($A.Zü)}} | 
 | {{end}}`, 1, []string{"{7,3,foo,Function,false}", "{12,1,X,Method,false}", | 
 | 	"{14,1,Y,Method,false}", "{21,2,$A,Variable,true}", "{26,2,,String,false}", | 
 | 	"{35,1,Z,Method,false}", "{38,2,$A,Variable,false}", | 
 | 	"{53,2,$A,Variable,false}", "{56,1,X,Method,false}", "{57,2,,Number,false}", | 
 | 	"{64,3,foo,Function,false}", "{70,1,X,Method,false}", | 
 | 	"{72,1,Y,Method,false}", "{75,2,,Number,false}", "{80,2,$A,Variable,false}", | 
 | 	"{83,2,Zü,Method,false}", "{94,3,,Constant,false}"}}, | 
 |  | 
 | 	{`{{define "zzz"}}{{.}}{{end}} | 
 | {{template "zzz"}}`, 2, []string{"{10,3,zzz,Namespace,true}", "{18,1,dot,Variable,false}", | 
 | 		"{41,3,zzz,Package,false}"}}, | 
 |  | 
 | 	{`{{block "aaa" foo}}b{{end}}`, 2, []string{"{9,3,aaa,Namespace,true}", | 
 | 		"{9,3,aaa,Package,false}", "{14,3,foo,Function,false}", "{19,1,,Constant,false}"}}, | 
 | 	{"", 0, nil}, | 
 | } | 
 |  | 
 | func TestSymbols(t *testing.T) { | 
 | 	for i, x := range tmpl { | 
 | 		got := parseBuffer([]byte(x.buf)) | 
 | 		if got.ParseErr != nil { | 
 | 			t.Errorf("error:%v", got.ParseErr) | 
 | 			continue | 
 | 		} | 
 | 		if len(got.named) != x.cnt { | 
 | 			t.Errorf("%d: got %d, expected %d", i, len(got.named), x.cnt) | 
 | 		} | 
 | 		for n, s := range got.symbols { | 
 | 			if s.String() != x.syms[n] { | 
 | 				t.Errorf("%d: got %s, expected %s", i, s.String(), x.syms[n]) | 
 | 			} | 
 | 		} | 
 | 	} | 
 | } | 
 |  | 
 | func TestWordAt(t *testing.T) { | 
 | 	want := []string{"", "", "$A", "$A", "", "", "", "", "", "", | 
 | 		"", "", "", "if", "if", "", "$A", "$A", "", "", | 
 | 		"B", "", "", "end", "end", "end", "", "", ""} | 
 | 	p := parseBuffer([]byte("{{$A := .}}{{if $A}}B{{end}}")) | 
 | 	for i := 0; i < len(p.buf); i++ { | 
 | 		got := findWordAt(p, i) | 
 | 		if got != want[i] { | 
 | 			t.Errorf("for %d, got %q, wanted %q", i, got, want[i]) | 
 | 		} | 
 | 	} | 
 | } | 
 |  | 
 | func TestNLS(t *testing.T) { | 
 | 	buf := `{{if (foÜx .X.Y)}}{{$A := "hi"}}{{.Z $A}}{{else}} | 
 | 	{{$A.X 12}} | 
 | 	{{foo (.X.Y) 23 ($A.Z)}} | 
 | 	{{end}} | 
 | 	` | 
 | 	p := parseBuffer([]byte(buf)) | 
 | 	if p.ParseErr != nil { | 
 | 		t.Fatal(p.ParseErr) | 
 | 	} | 
 | 	// line 0 doesn't have a \n in front of it | 
 | 	for i := 1; i < len(p.nls)-1; i++ { | 
 | 		if buf[p.nls[i]] != '\n' { | 
 | 			t.Errorf("line %d got %c", i, buf[p.nls[i]]) | 
 | 		} | 
 | 	} | 
 | 	// fake line at end of file | 
 | 	if p.nls[len(p.nls)-1] != len(buf) { | 
 | 		t.Errorf("got %d expected %d", p.nls[len(p.nls)-1], len(buf)) | 
 | 	} | 
 | } | 
 |  | 
 | func TestLineCol(t *testing.T) { | 
 | 	buf := `{{if (foÜx .X.Y)}}{{$A := "hi"}}{{.Z $A}}{{else}} | 
 | 	{{$A.X 12}} | 
 | 	{{foo (.X.Y) 23 ($A.Z)}} | 
 | 	{{end}}` | 
 | 	if false { | 
 | 		t.Error(buf) | 
 | 	} | 
 | 	for n, cx := range tmpl { | 
 | 		buf := cx.buf | 
 | 		p := parseBuffer([]byte(buf)) | 
 | 		if p.ParseErr != nil { | 
 | 			t.Fatal(p.ParseErr) | 
 | 		} | 
 | 		type loc struct { | 
 | 			offset int | 
 | 			l, c   uint32 | 
 | 		} | 
 | 		saved := []loc{} | 
 | 		// forwards | 
 | 		var lastl, lastc uint32 | 
 | 		for offset := range buf { | 
 | 			l, c := p.LineCol(offset) | 
 | 			saved = append(saved, loc{offset, l, c}) | 
 | 			if l > lastl { | 
 | 				lastl = l | 
 | 				if c != 0 { | 
 | 					t.Errorf("line %d, got %d instead of 0", l, c) | 
 | 				} | 
 | 			} | 
 | 			if c > lastc { | 
 | 				lastc = c | 
 | 			} | 
 | 		} | 
 | 		lines := strings.Split(buf, "\n") | 
 | 		mxlen := -1 | 
 | 		for _, l := range lines { | 
 | 			if len(l) > mxlen { | 
 | 				mxlen = len(l) | 
 | 			} | 
 | 		} | 
 | 		if int(lastl) != len(lines)-1 && int(lastc) != mxlen { | 
 | 			// lastl is 0 if there is only 1 line(?) | 
 | 			t.Errorf("expected %d, %d, got %d, %d for case %d", len(lines)-1, mxlen, lastl, lastc, n) | 
 | 		} | 
 | 		// backwards | 
 | 		for j := len(saved) - 1; j >= 0; j-- { | 
 | 			s := saved[j] | 
 | 			xl, xc := p.LineCol(s.offset) | 
 | 			if xl != s.l || xc != s.c { | 
 | 				t.Errorf("at offset %d(%d), got (%d,%d), expected (%d,%d)", s.offset, j, xl, xc, s.l, s.c) | 
 | 			} | 
 | 		} | 
 | 	} | 
 | } | 
 |  | 
 | func TestLineColNL(t *testing.T) { | 
 | 	buf := "\n\n\n\n\n" | 
 | 	p := parseBuffer([]byte(buf)) | 
 | 	if p.ParseErr != nil { | 
 | 		t.Fatal(p.ParseErr) | 
 | 	} | 
 | 	for i := 0; i < len(buf); i++ { | 
 | 		l, c := p.LineCol(i) | 
 | 		if c != 0 || int(l) != i+1 { | 
 | 			t.Errorf("got (%d,%d), expected (%d,0)", l, c, i) | 
 | 		} | 
 | 	} | 
 | } | 
 |  | 
 | func TestPos(t *testing.T) { | 
 | 	buf := ` | 
 | 	{{if (foÜx .X.Y)}}{{$A := "hi"}}{{.Z $A}}{{else}} | 
 | 	{{$A.X 12}} | 
 | 	{{foo (.X.Y) 23 ($A.Z)}} | 
 | 	{{end}}` | 
 | 	p := parseBuffer([]byte(buf)) | 
 | 	if p.ParseErr != nil { | 
 | 		t.Fatal(p.ParseErr) | 
 | 	} | 
 | 	for pos, r := range buf { | 
 | 		if r == '\n' { | 
 | 			continue | 
 | 		} | 
 | 		x := p.Position(pos) | 
 | 		n := p.FromPosition(x) | 
 | 		if n != pos { | 
 | 			// once it's wrong, it will be wrong forever | 
 | 			t.Fatalf("at pos %d (rune %c) got %d {%#v]", pos, r, n, x) | 
 | 		} | 
 |  | 
 | 	} | 
 | } | 
 | func TestLen(t *testing.T) { | 
 | 	data := []struct { | 
 | 		cnt int | 
 | 		v   string | 
 | 	}{{1, "a"}, {1, "膈"}, {4, "😆🥸"}, {7, "3😀4567"}} | 
 | 	p := &Parsed{nonASCII: true} | 
 | 	for _, d := range data { | 
 | 		got := p.utf16len([]byte(d.v)) | 
 | 		if got != d.cnt { | 
 | 			t.Errorf("%v, got %d wanted %d", d, got, d.cnt) | 
 | 		} | 
 | 	} | 
 | } | 
 |  | 
 | func TestUtf16(t *testing.T) { | 
 | 	buf := ` | 
 | 	{{if (foÜx .X.Y)}}😀{{$A := "hi"}}{{.Z $A}}{{else}} | 
 | 	{{$A.X 12}} | 
 | 	{{foo (.X.Y) 23 ($A.Z)}} | 
 | 	{{end}}` | 
 | 	p := parseBuffer([]byte(buf)) | 
 | 	if p.nonASCII == false { | 
 | 		t.Error("expected nonASCII to be true") | 
 | 	} | 
 | } | 
 |  | 
 | type ttest struct { | 
 | 	tmpl      string | 
 | 	tokCnt    int | 
 | 	elidedCnt int8 | 
 | } | 
 |  | 
 | func TestQuotes(t *testing.T) { | 
 | 	tsts := []ttest{ | 
 | 		{"{{- /*comment*/ -}}", 1, 0}, | 
 | 		{"{{/*`\ncomment\n`*/}}", 1, 0}, | 
 | 		//{"{{foo\nbar}}\n", 1, 0}, // this action spanning lines parses in 1.16 | 
 | 		{"{{\"{{foo}}{{\"}}", 1, 0}, | 
 | 		{"{{\n{{- when}}", 1, 1},          // corrected | 
 | 		{"{{{{if .}}xx{{\n{{end}}", 2, 2}, // corrected | 
 | 	} | 
 | 	for _, s := range tsts { | 
 | 		p := parseBuffer([]byte(s.tmpl)) | 
 | 		if len(p.tokens) != s.tokCnt { | 
 | 			t.Errorf("%q: got %d tokens, expected %d", s, len(p.tokens), s.tokCnt) | 
 | 		} | 
 | 		if p.ParseErr != nil { | 
 | 			t.Errorf("%q: %v", string(p.buf), p.ParseErr) | 
 | 		} | 
 | 		if len(p.elided) != int(s.elidedCnt) { | 
 | 			t.Errorf("%q: elided %d, expected %d", s, len(p.elided), s.elidedCnt) | 
 | 		} | 
 | 	} | 
 | } |