golang.org/x/tools/gopls@v0.15.3/internal/template/parse_test.go (about) 1 // Copyright 2021 The Go Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 package template 6 7 import ( 8 "strings" 9 "testing" 10 ) 11 12 type datum struct { 13 buf string 14 cnt int 15 syms []string // the symbols in the parse of buf 16 } 17 18 var tmpl = []datum{{` 19 {{if (foo .X.Y)}}{{$A := "hi"}}{{.Z $A}}{{else}} 20 {{$A.X 12}} 21 {{foo (.X.Y) 23 ($A.Zü)}} 22 {{end}}`, 1, []string{"{7,3,foo,Function,false}", "{12,1,X,Method,false}", 23 "{14,1,Y,Method,false}", "{21,2,$A,Variable,true}", "{26,2,,String,false}", 24 "{35,1,Z,Method,false}", "{38,2,$A,Variable,false}", 25 "{53,2,$A,Variable,false}", "{56,1,X,Method,false}", "{57,2,,Number,false}", 26 "{64,3,foo,Function,false}", "{70,1,X,Method,false}", 27 "{72,1,Y,Method,false}", "{75,2,,Number,false}", "{80,2,$A,Variable,false}", 28 "{83,2,Zü,Method,false}", "{94,3,,Constant,false}"}}, 29 30 {`{{define "zzz"}}{{.}}{{end}} 31 {{template "zzz"}}`, 2, []string{"{10,3,zzz,Namespace,true}", "{18,1,dot,Variable,false}", 32 "{41,3,zzz,Package,false}"}}, 33 34 {`{{block "aaa" foo}}b{{end}}`, 2, []string{"{9,3,aaa,Namespace,true}", 35 "{9,3,aaa,Package,false}", "{14,3,foo,Function,false}", "{19,1,,Constant,false}"}}, 36 {"", 0, nil}, 37 } 38 39 func TestSymbols(t *testing.T) { 40 for i, x := range tmpl { 41 got := parseBuffer([]byte(x.buf)) 42 if got.ParseErr != nil { 43 t.Errorf("error:%v", got.ParseErr) 44 continue 45 } 46 if len(got.named) != x.cnt { 47 t.Errorf("%d: got %d, expected %d", i, len(got.named), x.cnt) 48 } 49 for n, s := range got.symbols { 50 if s.String() != x.syms[n] { 51 t.Errorf("%d: got %s, expected %s", i, s.String(), x.syms[n]) 52 } 53 } 54 } 55 } 56 57 func TestWordAt(t *testing.T) { 58 want := []string{"", "", "$A", "$A", "", "", "", "", "", "", 59 "", "", "", "if", "if", "", "$A", "$A", "", "", 60 "B", "", "", "end", "end", "end", "", "", ""} 61 p := parseBuffer([]byte("{{$A := .}}{{if $A}}B{{end}}")) 62 for i := 0; i < len(p.buf); i++ { 63 got := findWordAt(p, i) 64 if got != want[i] { 65 t.Errorf("for %d, got %q, wanted %q", i, got, want[i]) 66 } 67 } 68 } 69 70 func TestNLS(t *testing.T) { 71 buf := `{{if (foÜx .X.Y)}}{{$A := "hi"}}{{.Z $A}}{{else}} 72 {{$A.X 12}} 73 {{foo (.X.Y) 23 ($A.Z)}} 74 {{end}} 75 ` 76 p := parseBuffer([]byte(buf)) 77 if p.ParseErr != nil { 78 t.Fatal(p.ParseErr) 79 } 80 // line 0 doesn't have a \n in front of it 81 for i := 1; i < len(p.nls)-1; i++ { 82 if buf[p.nls[i]] != '\n' { 83 t.Errorf("line %d got %c", i, buf[p.nls[i]]) 84 } 85 } 86 // fake line at end of file 87 if p.nls[len(p.nls)-1] != len(buf) { 88 t.Errorf("got %d expected %d", p.nls[len(p.nls)-1], len(buf)) 89 } 90 } 91 92 func TestLineCol(t *testing.T) { 93 buf := `{{if (foÜx .X.Y)}}{{$A := "hi"}}{{.Z $A}}{{else}} 94 {{$A.X 12}} 95 {{foo (.X.Y) 23 ($A.Z)}} 96 {{end}}` 97 if false { 98 t.Error(buf) 99 } 100 for n, cx := range tmpl { 101 buf := cx.buf 102 p := parseBuffer([]byte(buf)) 103 if p.ParseErr != nil { 104 t.Fatal(p.ParseErr) 105 } 106 type loc struct { 107 offset int 108 l, c uint32 109 } 110 saved := []loc{} 111 // forwards 112 var lastl, lastc uint32 113 for offset := range buf { 114 l, c := p.LineCol(offset) 115 saved = append(saved, loc{offset, l, c}) 116 if l > lastl { 117 lastl = l 118 if c != 0 { 119 t.Errorf("line %d, got %d instead of 0", l, c) 120 } 121 } 122 if c > lastc { 123 lastc = c 124 } 125 } 126 lines := strings.Split(buf, "\n") 127 mxlen := -1 128 for _, l := range lines { 129 if len(l) > mxlen { 130 mxlen = len(l) 131 } 132 } 133 if int(lastl) != len(lines)-1 && int(lastc) != mxlen { 134 // lastl is 0 if there is only 1 line(?) 135 t.Errorf("expected %d, %d, got %d, %d for case %d", len(lines)-1, mxlen, lastl, lastc, n) 136 } 137 // backwards 138 for j := len(saved) - 1; j >= 0; j-- { 139 s := saved[j] 140 xl, xc := p.LineCol(s.offset) 141 if xl != s.l || xc != s.c { 142 t.Errorf("at offset %d(%d), got (%d,%d), expected (%d,%d)", s.offset, j, xl, xc, s.l, s.c) 143 } 144 } 145 } 146 } 147 148 func TestLineColNL(t *testing.T) { 149 buf := "\n\n\n\n\n" 150 p := parseBuffer([]byte(buf)) 151 if p.ParseErr != nil { 152 t.Fatal(p.ParseErr) 153 } 154 for i := 0; i < len(buf); i++ { 155 l, c := p.LineCol(i) 156 if c != 0 || int(l) != i+1 { 157 t.Errorf("got (%d,%d), expected (%d,0)", l, c, i) 158 } 159 } 160 } 161 162 func TestPos(t *testing.T) { 163 buf := ` 164 {{if (foÜx .X.Y)}}{{$A := "hi"}}{{.Z $A}}{{else}} 165 {{$A.X 12}} 166 {{foo (.X.Y) 23 ($A.Z)}} 167 {{end}}` 168 p := parseBuffer([]byte(buf)) 169 if p.ParseErr != nil { 170 t.Fatal(p.ParseErr) 171 } 172 for pos, r := range buf { 173 if r == '\n' { 174 continue 175 } 176 x := p.Position(pos) 177 n := p.FromPosition(x) 178 if n != pos { 179 // once it's wrong, it will be wrong forever 180 t.Fatalf("at pos %d (rune %c) got %d {%#v]", pos, r, n, x) 181 } 182 183 } 184 } 185 func TestLen(t *testing.T) { 186 data := []struct { 187 cnt int 188 v string 189 }{{1, "a"}, {1, "膈"}, {4, "😆🥸"}, {7, "3😀4567"}} 190 p := &Parsed{nonASCII: true} 191 for _, d := range data { 192 got := p.utf16len([]byte(d.v)) 193 if got != d.cnt { 194 t.Errorf("%v, got %d wanted %d", d, got, d.cnt) 195 } 196 } 197 } 198 199 func TestUtf16(t *testing.T) { 200 buf := ` 201 {{if (foÜx .X.Y)}}😀{{$A := "hi"}}{{.Z $A}}{{else}} 202 {{$A.X 12}} 203 {{foo (.X.Y) 23 ($A.Z)}} 204 {{end}}` 205 p := parseBuffer([]byte(buf)) 206 if p.nonASCII == false { 207 t.Error("expected nonASCII to be true") 208 } 209 } 210 211 type ttest struct { 212 tmpl string 213 tokCnt int 214 elidedCnt int8 215 } 216 217 func TestQuotes(t *testing.T) { 218 tsts := []ttest{ 219 {"{{- /*comment*/ -}}", 1, 0}, 220 {"{{/*`\ncomment\n`*/}}", 1, 0}, 221 //{"{{foo\nbar}}\n", 1, 0}, // this action spanning lines parses in 1.16 222 {"{{\"{{foo}}{{\"}}", 1, 0}, 223 {"{{\n{{- when}}", 1, 1}, // corrected 224 {"{{{{if .}}xx{{\n{{end}}", 2, 2}, // corrected 225 } 226 for _, s := range tsts { 227 p := parseBuffer([]byte(s.tmpl)) 228 if len(p.tokens) != s.tokCnt { 229 t.Errorf("%q: got %d tokens, expected %d", s, len(p.tokens), s.tokCnt) 230 } 231 if p.ParseErr != nil { 232 t.Errorf("%q: %v", string(p.buf), p.ParseErr) 233 } 234 if len(p.elided) != int(s.elidedCnt) { 235 t.Errorf("%q: elided %d, expected %d", s, len(p.elided), s.elidedCnt) 236 } 237 } 238 }