Source file src/go/build/read_test.go

     1  // Copyright 2012 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 build
     6  
     7  import (
     8  	"fmt"
     9  	"go/token"
    10  	"io"
    11  	"strings"
    12  	"testing"
    13  )
    14  
    15  const quote = "`"
    16  
    17  type readTest struct {
    18  	// Test input contains ℙ where readGoInfo should stop.
    19  	in  string
    20  	err string
    21  }
    22  
    23  var readGoInfoTests = []readTest{
    24  	{
    25  		`package p`,
    26  		"",
    27  	},
    28  	{
    29  		`package p; import "x"`,
    30  		"",
    31  	},
    32  	{
    33  		`package p; import . "x"`,
    34  		"",
    35  	},
    36  	{
    37  		`package p; import "x";ℙvar x = 1`,
    38  		"",
    39  	},
    40  	{
    41  		`package p
    42  
    43  		// comment
    44  
    45  		import "x"
    46  		import _ "x"
    47  		import a "x"
    48  
    49  		/* comment */
    50  
    51  		import (
    52  			"x" /* comment */
    53  			_ "x"
    54  			a "x" // comment
    55  			` + quote + `x` + quote + `
    56  			_ /*comment*/ ` + quote + `x` + quote + `
    57  			a ` + quote + `x` + quote + `
    58  		)
    59  		import (
    60  		)
    61  		import ()
    62  		import()import()import()
    63  		import();import();import()
    64  
    65  		ℙvar x = 1
    66  		`,
    67  		"",
    68  	},
    69  	{
    70  		"\ufeff𝔻" + `package p; import "x";ℙvar x = 1`,
    71  		"",
    72  	},
    73  }
    74  
    75  var readCommentsTests = []readTest{
    76  	{
    77  		`ℙpackage p`,
    78  		"",
    79  	},
    80  	{
    81  		`ℙpackage p; import "x"`,
    82  		"",
    83  	},
    84  	{
    85  		`ℙpackage p; import . "x"`,
    86  		"",
    87  	},
    88  	{
    89  		"\ufeff𝔻" + `ℙpackage p; import . "x"`,
    90  		"",
    91  	},
    92  	{
    93  		`// foo
    94  
    95  		/* bar */
    96  
    97  		/* quux */ // baz
    98  
    99  		/*/ zot */
   100  
   101  		// asdf
   102  		ℙHello, world`,
   103  		"",
   104  	},
   105  	{
   106  		"\ufeff𝔻" + `// foo
   107  
   108  		/* bar */
   109  
   110  		/* quux */ // baz
   111  
   112  		/*/ zot */
   113  
   114  		// asdf
   115  		ℙHello, world`,
   116  		"",
   117  	},
   118  }
   119  
   120  func testRead(t *testing.T, tests []readTest, read func(io.Reader) ([]byte, error)) {
   121  	for i, tt := range tests {
   122  		beforeP, afterP, _ := strings.Cut(tt.in, "ℙ")
   123  		in := beforeP + afterP
   124  		testOut := beforeP
   125  
   126  		if beforeD, afterD, ok := strings.Cut(beforeP, "𝔻"); ok {
   127  			in = beforeD + afterD + afterP
   128  			testOut = afterD
   129  		}
   130  
   131  		r := strings.NewReader(in)
   132  		buf, err := read(r)
   133  		if err != nil {
   134  			if tt.err == "" {
   135  				t.Errorf("#%d: err=%q, expected success (%q)", i, err, string(buf))
   136  			} else if !strings.Contains(err.Error(), tt.err) {
   137  				t.Errorf("#%d: err=%q, expected %q", i, err, tt.err)
   138  			}
   139  			continue
   140  		}
   141  		if tt.err != "" {
   142  			t.Errorf("#%d: success, expected %q", i, tt.err)
   143  			continue
   144  		}
   145  
   146  		out := string(buf)
   147  		if out != testOut {
   148  			t.Errorf("#%d: wrong output:\nhave %q\nwant %q\n", i, out, testOut)
   149  		}
   150  	}
   151  }
   152  
   153  func TestReadGoInfo(t *testing.T) {
   154  	testRead(t, readGoInfoTests, func(r io.Reader) ([]byte, error) {
   155  		var info fileInfo
   156  		err := readGoInfo(r, &info)
   157  		return info.header, err
   158  	})
   159  }
   160  
   161  func TestReadComments(t *testing.T) {
   162  	testRead(t, readCommentsTests, readComments)
   163  }
   164  
   165  var readFailuresTests = []readTest{
   166  	{
   167  		`package`,
   168  		"syntax error",
   169  	},
   170  	{
   171  		"package p\n\x00\nimport `math`\n",
   172  		"unexpected NUL in input",
   173  	},
   174  	{
   175  		`package p; import`,
   176  		"syntax error",
   177  	},
   178  	{
   179  		`package p; import "`,
   180  		"syntax error",
   181  	},
   182  	{
   183  		"package p; import ` \n\n",
   184  		"syntax error",
   185  	},
   186  	{
   187  		`package p; import "x`,
   188  		"syntax error",
   189  	},
   190  	{
   191  		`package p; import _`,
   192  		"syntax error",
   193  	},
   194  	{
   195  		`package p; import _ "`,
   196  		"syntax error",
   197  	},
   198  	{
   199  		`package p; import _ "x`,
   200  		"syntax error",
   201  	},
   202  	{
   203  		`package p; import .`,
   204  		"syntax error",
   205  	},
   206  	{
   207  		`package p; import . "`,
   208  		"syntax error",
   209  	},
   210  	{
   211  		`package p; import . "x`,
   212  		"syntax error",
   213  	},
   214  	{
   215  		`package p; import (`,
   216  		"syntax error",
   217  	},
   218  	{
   219  		`package p; import ("`,
   220  		"syntax error",
   221  	},
   222  	{
   223  		`package p; import ("x`,
   224  		"syntax error",
   225  	},
   226  	{
   227  		`package p; import ("x"`,
   228  		"syntax error",
   229  	},
   230  }
   231  
   232  func TestReadFailuresIgnored(t *testing.T) {
   233  	// Syntax errors should not be reported (false arg to readImports).
   234  	// Instead, entire file should be the output and no error.
   235  	// Convert tests not to return syntax errors.
   236  	tests := make([]readTest, len(readFailuresTests))
   237  	copy(tests, readFailuresTests)
   238  	for i := range tests {
   239  		tt := &tests[i]
   240  		if !strings.Contains(tt.err, "NUL") {
   241  			tt.err = ""
   242  		}
   243  	}
   244  	testRead(t, tests, func(r io.Reader) ([]byte, error) {
   245  		var info fileInfo
   246  		err := readGoInfo(r, &info)
   247  		return info.header, err
   248  	})
   249  }
   250  
   251  var readEmbedTests = []struct {
   252  	in, out string
   253  }{
   254  	{
   255  		"package p\n",
   256  		"",
   257  	},
   258  	{
   259  		"package p\nimport \"embed\"\nvar i int\n//go:embed x y z\nvar files embed.FS",
   260  		`test:4:12:x
   261  		 test:4:14:y
   262  		 test:4:16:z`,
   263  	},
   264  	{
   265  		"package p\nimport \"embed\"\nvar i int\n//go:embed x \"\\x79\" `z`\nvar files embed.FS",
   266  		`test:4:12:x
   267  		 test:4:14:y
   268  		 test:4:21:z`,
   269  	},
   270  	{
   271  		"package p\nimport \"embed\"\nvar i int\n//go:embed x y\n//go:embed z\nvar files embed.FS",
   272  		`test:4:12:x
   273  		 test:4:14:y
   274  		 test:5:12:z`,
   275  	},
   276  	{
   277  		"package p\nimport \"embed\"\nvar i int\n\t //go:embed x y\n\t //go:embed z\n\t var files embed.FS",
   278  		`test:4:14:x
   279  		 test:4:16:y
   280  		 test:5:14:z`,
   281  	},
   282  	{
   283  		"package p\nimport \"embed\"\n//go:embed x y z\nvar files embed.FS",
   284  		`test:3:12:x
   285  		 test:3:14:y
   286  		 test:3:16:z`,
   287  	},
   288  	{
   289  		"\ufeffpackage p\nimport \"embed\"\n//go:embed x y z\nvar files embed.FS",
   290  		`test:3:12:x
   291  		 test:3:14:y
   292  		 test:3:16:z`,
   293  	},
   294  	{
   295  		"package p\nimport \"embed\"\nvar s = \"/*\"\n//go:embed x\nvar files embed.FS",
   296  		`test:4:12:x`,
   297  	},
   298  	{
   299  		`package p
   300  		 import "embed"
   301  		 var s = "\"\\\\"
   302  		 //go:embed x
   303  		 var files embed.FS`,
   304  		`test:4:15:x`,
   305  	},
   306  	{
   307  		"package p\nimport \"embed\"\nvar s = `/*`\n//go:embed x\nvar files embed.FS",
   308  		`test:4:12:x`,
   309  	},
   310  	{
   311  		"package p\nimport \"embed\"\nvar s = z/ *y\n//go:embed pointer\nvar pointer embed.FS",
   312  		"test:4:12:pointer",
   313  	},
   314  	{
   315  		"package p\n//go:embed x y z\n", // no import, no scan
   316  		"",
   317  	},
   318  	{
   319  		"package p\n//go:embed x y z\nvar files embed.FS", // no import, no scan
   320  		"",
   321  	},
   322  	{
   323  		"\ufeffpackage p\n//go:embed x y z\nvar files embed.FS", // no import, no scan
   324  		"",
   325  	},
   326  }
   327  
   328  func TestReadEmbed(t *testing.T) {
   329  	fset := token.NewFileSet()
   330  	for i, tt := range readEmbedTests {
   331  		info := fileInfo{
   332  			name: "test",
   333  			fset: fset,
   334  		}
   335  		err := readGoInfo(strings.NewReader(tt.in), &info)
   336  		if err != nil {
   337  			t.Errorf("#%d: %v", i, err)
   338  			continue
   339  		}
   340  		b := &strings.Builder{}
   341  		sep := ""
   342  		for _, emb := range info.embeds {
   343  			fmt.Fprintf(b, "%s%v:%s", sep, emb.pos, emb.pattern)
   344  			sep = "\n"
   345  		}
   346  		got := b.String()
   347  		want := strings.Join(strings.Fields(tt.out), "\n")
   348  		if got != want {
   349  			t.Errorf("#%d: embeds:\n%s\nwant:\n%s", i, got, want)
   350  		}
   351  	}
   352  }
   353  

View as plain text