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  }