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 5package build 6 7import ( 8 "fmt" 9 "go/token" 10 "io" 11 "strings" 12 "testing" 13) 14 15const quote = "`" 16 17type readTest struct { 18 // Test input contains ℙ where readGoInfo should stop. 19 in string 20 err string 21} 22 23var 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 75var 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 120func testRead(t *testing.T, tests []readTest, read func(io.Reader) ([]byte, error)) { 121 for i, tt := range tests { 122 var in, testOut string 123 j := strings.Index(tt.in, "ℙ") 124 if j < 0 { 125 in = tt.in 126 testOut = tt.in 127 } else { 128 in = tt.in[:j] + tt.in[j+len("ℙ"):] 129 testOut = tt.in[:j] 130 } 131 d := strings.Index(tt.in, "") 132 if d >= 0 { 133 in = in[:d] + in[d+len(""):] 134 testOut = testOut[d+len(""):] 135 } 136 r := strings.NewReader(in) 137 buf, err := read(r) 138 if err != nil { 139 if tt.err == "" { 140 t.Errorf("#%d: err=%q, expected success (%q)", i, err, string(buf)) 141 } else if !strings.Contains(err.Error(), tt.err) { 142 t.Errorf("#%d: err=%q, expected %q", i, err, tt.err) 143 } 144 continue 145 } 146 if tt.err != "" { 147 t.Errorf("#%d: success, expected %q", i, tt.err) 148 continue 149 } 150 151 out := string(buf) 152 if out != testOut { 153 t.Errorf("#%d: wrong output:\nhave %q\nwant %q\n", i, out, testOut) 154 } 155 } 156} 157 158func TestReadGoInfo(t *testing.T) { 159 testRead(t, readGoInfoTests, func(r io.Reader) ([]byte, error) { 160 var info fileInfo 161 err := readGoInfo(r, &info) 162 return info.header, err 163 }) 164} 165 166func TestReadComments(t *testing.T) { 167 testRead(t, readCommentsTests, readComments) 168} 169 170var readFailuresTests = []readTest{ 171 { 172 `package`, 173 "syntax error", 174 }, 175 { 176 "package p\n\x00\nimport `math`\n", 177 "unexpected NUL in input", 178 }, 179 { 180 `package p; import`, 181 "syntax error", 182 }, 183 { 184 `package p; import "`, 185 "syntax error", 186 }, 187 { 188 "package p; import ` \n\n", 189 "syntax error", 190 }, 191 { 192 `package p; import "x`, 193 "syntax error", 194 }, 195 { 196 `package p; import _`, 197 "syntax error", 198 }, 199 { 200 `package p; import _ "`, 201 "syntax error", 202 }, 203 { 204 `package p; import _ "x`, 205 "syntax error", 206 }, 207 { 208 `package p; import .`, 209 "syntax error", 210 }, 211 { 212 `package p; import . "`, 213 "syntax error", 214 }, 215 { 216 `package p; import . "x`, 217 "syntax error", 218 }, 219 { 220 `package p; import (`, 221 "syntax error", 222 }, 223 { 224 `package p; import ("`, 225 "syntax error", 226 }, 227 { 228 `package p; import ("x`, 229 "syntax error", 230 }, 231 { 232 `package p; import ("x"`, 233 "syntax error", 234 }, 235} 236 237func TestReadFailuresIgnored(t *testing.T) { 238 // Syntax errors should not be reported (false arg to readImports). 239 // Instead, entire file should be the output and no error. 240 // Convert tests not to return syntax errors. 241 tests := make([]readTest, len(readFailuresTests)) 242 copy(tests, readFailuresTests) 243 for i := range tests { 244 tt := &tests[i] 245 if !strings.Contains(tt.err, "NUL") { 246 tt.err = "" 247 } 248 } 249 testRead(t, tests, func(r io.Reader) ([]byte, error) { 250 var info fileInfo 251 err := readGoInfo(r, &info) 252 return info.header, err 253 }) 254} 255 256var readEmbedTests = []struct { 257 in, out string 258}{ 259 { 260 "package p\n", 261 "", 262 }, 263 { 264 "package p\nimport \"embed\"\nvar i int\n//go:embed x y z\nvar files embed.FS", 265 `test:4:12:x 266 test:4:14:y 267 test:4:16:z`, 268 }, 269 { 270 "package p\nimport \"embed\"\nvar i int\n//go:embed x \"\\x79\" `z`\nvar files embed.FS", 271 `test:4:12:x 272 test:4:14:y 273 test:4:21:z`, 274 }, 275 { 276 "package p\nimport \"embed\"\nvar i int\n//go:embed x y\n//go:embed z\nvar files embed.FS", 277 `test:4:12:x 278 test:4:14:y 279 test:5:12:z`, 280 }, 281 { 282 "package p\nimport \"embed\"\nvar i int\n\t //go:embed x y\n\t //go:embed z\n\t var files embed.FS", 283 `test:4:14:x 284 test:4:16:y 285 test:5:14:z`, 286 }, 287 { 288 "package p\nimport \"embed\"\n//go:embed x y z\nvar files embed.FS", 289 `test:3:12:x 290 test:3:14:y 291 test:3:16:z`, 292 }, 293 { 294 "\ufeffpackage p\nimport \"embed\"\n//go:embed x y z\nvar files embed.FS", 295 `test:3:12:x 296 test:3:14:y 297 test:3:16:z`, 298 }, 299 { 300 "package p\nimport \"embed\"\nvar s = \"/*\"\n//go:embed x\nvar files embed.FS", 301 `test:4:12:x`, 302 }, 303 { 304 `package p 305 import "embed" 306 var s = "\"\\\\" 307 //go:embed x 308 var files embed.FS`, 309 `test:4:15:x`, 310 }, 311 { 312 "package p\nimport \"embed\"\nvar s = `/*`\n//go:embed x\nvar files embed.FS", 313 `test:4:12:x`, 314 }, 315 { 316 "package p\nimport \"embed\"\nvar s = z/ *y\n//go:embed pointer\nvar pointer embed.FS", 317 "test:4:12:pointer", 318 }, 319 { 320 "package p\n//go:embed x y z\n", // no import, no scan 321 "", 322 }, 323 { 324 "package p\n//go:embed x y z\nvar files embed.FS", // no import, no scan 325 "", 326 }, 327 { 328 "\ufeffpackage p\n//go:embed x y z\nvar files embed.FS", // no import, no scan 329 "", 330 }, 331} 332 333func TestReadEmbed(t *testing.T) { 334 fset := token.NewFileSet() 335 for i, tt := range readEmbedTests { 336 info := fileInfo{ 337 name: "test", 338 fset: fset, 339 } 340 err := readGoInfo(strings.NewReader(tt.in), &info) 341 if err != nil { 342 t.Errorf("#%d: %v", i, err) 343 continue 344 } 345 b := &strings.Builder{} 346 sep := "" 347 for _, emb := range info.embeds { 348 fmt.Fprintf(b, "%s%v:%s", sep, emb.pos, emb.pattern) 349 sep = "\n" 350 } 351 got := b.String() 352 want := strings.Join(strings.Fields(tt.out), "\n") 353 if got != want { 354 t.Errorf("#%d: embeds:\n%s\nwant:\n%s", i, got, want) 355 } 356 } 357} 358