1
2
3
4
5 package main
6
7 import (
8 "bytes"
9 "fmt"
10 "go/parser"
11 "go/token"
12 "html/template"
13 "strings"
14 "testing"
15 )
16
17 func TestImports(t *testing.T) {
18 t.Run("importName", func(t *testing.T) {
19 cases := []struct {
20 src string
21 ident string
22 }{
23 {`"syscall"`, "syscall"},
24 {`. "foobar"`, "."},
25 {`"go/ast"`, "ast"},
26 {`moo "go/format"`, "moo"},
27 {`. "go/token"`, "."},
28 {`"golang.org/x/sys/unix"`, "unix"},
29 {`nix "golang.org/x/sys/unix"`, "nix"},
30 {`_ "golang.org/x/sys/unix"`, "_"},
31 }
32
33 for _, c := range cases {
34 pkgSrc := fmt.Sprintf("package main\nimport %s", c.src)
35
36 f, err := parser.ParseFile(token.NewFileSet(), "", pkgSrc, parser.ImportsOnly)
37 if err != nil {
38 t.Error(err)
39 continue
40 }
41 if len(f.Imports) != 1 {
42 t.Errorf("Got %d imports, expected 1", len(f.Imports))
43 continue
44 }
45
46 got, err := importName(f.Imports[0])
47 if err != nil {
48 t.Fatal(err)
49 }
50 if got != c.ident {
51 t.Errorf("Got %q, expected %q", got, c.ident)
52 }
53 }
54 })
55
56 t.Run("filterImports", func(t *testing.T) {
57 cases := []struct{ before, after string }{
58 {`package test
59
60 import (
61 "foo"
62 "bar"
63 )`,
64 "package test\n"},
65 {`package test
66
67 import (
68 "foo"
69 "bar"
70 )
71
72 func useFoo() { foo.Usage() }`,
73 `package test
74
75 import (
76 "foo"
77 )
78
79 func useFoo() { foo.Usage() }
80 `},
81 }
82 for _, c := range cases {
83 got, err := filterImports([]byte(c.before))
84 if err != nil {
85 t.Error(err)
86 }
87
88 if string(got) != c.after {
89 t.Errorf("Got:\n%s\nExpected:\n%s\n", got, c.after)
90 }
91 }
92 })
93 }
94
95 func TestMerge(t *testing.T) {
96
97 inTmpl := template.Must(template.New("input").Parse(`
98 // Package comments
99
100 // build directives for arch{{.}}
101
102 //go:build goos && arch{{.}}
103
104 package main
105
106 /*
107 #include <stdint.h>
108 #include <stddef.h>
109 int utimes(uintptr_t, uintptr_t);
110 int utimensat(int, uintptr_t, uintptr_t, int);
111 */
112 import "C"
113
114 // The imports
115 import (
116 "commonDep"
117 "uniqueDep{{.}}"
118 )
119
120 // Vars
121 var (
122 commonVar = commonDep.Use("common")
123
124 uniqueVar{{.}} = "unique{{.}}"
125 )
126
127 // Common free standing comment
128
129 // Common comment
130 const COMMON_INDEPENDENT = 1234
131 const UNIQUE_INDEPENDENT_{{.}} = "UNIQUE_INDEPENDENT_{{.}}"
132
133 // Group comment
134 const (
135 COMMON_GROUP = "COMMON_GROUP"
136 UNIQUE_GROUP_{{.}} = "UNIQUE_GROUP_{{.}}"
137 )
138
139 // Group2 comment
140 const (
141 UNIQUE_GROUP21_{{.}} = "UNIQUE_GROUP21_{{.}}"
142 UNIQUE_GROUP22_{{.}} = "UNIQUE_GROUP22_{{.}}"
143 )
144
145 // Group3 comment
146 const (
147 sub1Common1 = 11
148 sub1Unique2{{.}} = 12
149 sub1Common3_LONG = 13
150
151 sub2Unique1{{.}} = 21
152 sub2Common2 = 22
153 sub2Common3 = 23
154 sub2Unique4{{.}} = 24
155 )
156
157 type commonInt int
158
159 type uniqueInt{{.}} int
160
161 func commonF() string {
162 return commonDep.Use("common")
163 }
164
165 func uniqueF() string {
166 C.utimes(0, 0)
167 return uniqueDep{{.}}.Use("{{.}}")
168 }
169
170 // Group4 comment
171 const (
172 sub3Common1 = 31
173 sub3Unique2{{.}} = 32
174 sub3Unique3{{.}} = 33
175 sub3Common4 = 34
176
177 sub4Common1, sub4Unique2{{.}} = 41, 42
178 sub4Unique3{{.}}, sub4Common4 = 43, 44
179 )
180 `))
181
182
183 outTmpl := template.Must(template.New("output").Parse(`// Package comments
184
185 // build directives for arch{{.}}
186
187 //go:build goos && arch{{.}}
188
189 package main
190
191 /*
192 #include <stdint.h>
193 #include <stddef.h>
194 int utimes(uintptr_t, uintptr_t);
195 int utimensat(int, uintptr_t, uintptr_t, int);
196 */
197 import "C"
198
199 // The imports
200 import (
201 "commonDep"
202 "uniqueDep{{.}}"
203 )
204
205 // Vars
206 var (
207 commonVar = commonDep.Use("common")
208
209 uniqueVar{{.}} = "unique{{.}}"
210 )
211
212 const UNIQUE_INDEPENDENT_{{.}} = "UNIQUE_INDEPENDENT_{{.}}"
213
214 // Group comment
215 const (
216 UNIQUE_GROUP_{{.}} = "UNIQUE_GROUP_{{.}}"
217 )
218
219 // Group2 comment
220 const (
221 UNIQUE_GROUP21_{{.}} = "UNIQUE_GROUP21_{{.}}"
222 UNIQUE_GROUP22_{{.}} = "UNIQUE_GROUP22_{{.}}"
223 )
224
225 // Group3 comment
226 const (
227 sub1Unique2{{.}} = 12
228
229 sub2Unique1{{.}} = 21
230 sub2Unique4{{.}} = 24
231 )
232
233 type uniqueInt{{.}} int
234
235 func uniqueF() string {
236 C.utimes(0, 0)
237 return uniqueDep{{.}}.Use("{{.}}")
238 }
239
240 // Group4 comment
241 const (
242 sub3Unique2{{.}} = 32
243 sub3Unique3{{.}} = 33
244
245 sub4Common1, sub4Unique2{{.}} = 41, 42
246 sub4Unique3{{.}}, sub4Common4 = 43, 44
247 )
248 `))
249
250 const mergedFile = `// Package comments
251
252 package main
253
254 // The imports
255 import (
256 "commonDep"
257 )
258
259 // Common free standing comment
260
261 // Common comment
262 const COMMON_INDEPENDENT = 1234
263
264 // Group comment
265 const (
266 COMMON_GROUP = "COMMON_GROUP"
267 )
268
269 // Group3 comment
270 const (
271 sub1Common1 = 11
272 sub1Common3_LONG = 13
273
274 sub2Common2 = 22
275 sub2Common3 = 23
276 )
277
278 type commonInt int
279
280 func commonF() string {
281 return commonDep.Use("common")
282 }
283
284 // Group4 comment
285 const (
286 sub3Common1 = 31
287 sub3Common4 = 34
288 )
289 `
290
291
292 var inFiles, outFiles []srcFile
293 for _, arch := range strings.Fields("A B C D") {
294 buf := new(bytes.Buffer)
295 err := inTmpl.Execute(buf, arch)
296 if err != nil {
297 t.Fatal(err)
298 }
299 inFiles = append(inFiles, srcFile{"file" + arch, buf.Bytes()})
300
301 buf = new(bytes.Buffer)
302 err = outTmpl.Execute(buf, arch)
303 if err != nil {
304 t.Fatal(err)
305 }
306 outFiles = append(outFiles, srcFile{"file" + arch, buf.Bytes()})
307 }
308
309 t.Run("getCodeSet", func(t *testing.T) {
310 got, err := getCodeSet(inFiles[0].src)
311 if err != nil {
312 t.Fatal(err)
313 }
314
315 expectedElems := []codeElem{
316 {token.COMMENT, "Package comments\n"},
317 {token.COMMENT, "build directives for archA\n"},
318 {token.COMMENT, "go:build goos && archA\n"},
319 {token.CONST, `COMMON_INDEPENDENT = 1234`},
320 {token.CONST, `UNIQUE_INDEPENDENT_A = "UNIQUE_INDEPENDENT_A"`},
321 {token.CONST, `COMMON_GROUP = "COMMON_GROUP"`},
322 {token.CONST, `UNIQUE_GROUP_A = "UNIQUE_GROUP_A"`},
323 {token.CONST, `UNIQUE_GROUP21_A = "UNIQUE_GROUP21_A"`},
324 {token.CONST, `UNIQUE_GROUP22_A = "UNIQUE_GROUP22_A"`},
325 {token.CONST, `sub1Common1 = 11`},
326 {token.CONST, `sub1Unique2A = 12`},
327 {token.CONST, `sub1Common3_LONG = 13`},
328 {token.CONST, `sub2Unique1A = 21`},
329 {token.CONST, `sub2Common2 = 22`},
330 {token.CONST, `sub2Common3 = 23`},
331 {token.CONST, `sub2Unique4A = 24`},
332 {token.CONST, `sub3Common1 = 31`},
333 {token.CONST, `sub3Unique2A = 32`},
334 {token.CONST, `sub3Unique3A = 33`},
335 {token.CONST, `sub3Common4 = 34`},
336 {token.CONST, `sub4Common1, sub4Unique2A = 41, 42`},
337 {token.CONST, `sub4Unique3A, sub4Common4 = 43, 44`},
338 {token.TYPE, `commonInt int`},
339 {token.TYPE, `uniqueIntA int`},
340 {token.FUNC, `func commonF() string {
341 return commonDep.Use("common")
342 }`},
343 {token.FUNC, `func uniqueF() string {
344 C.utimes(0, 0)
345 return uniqueDepA.Use("A")
346 }`},
347 }
348 expected := newCodeSet()
349 for _, d := range expectedElems {
350 expected.add(d)
351 }
352
353 if len(got.set) != len(expected.set) {
354 t.Errorf("Got %d codeElems, expected %d", len(got.set), len(expected.set))
355 }
356 for expElem := range expected.set {
357 if !got.has(expElem) {
358 t.Errorf("Didn't get expected codeElem %#v", expElem)
359 }
360 }
361 for gotElem := range got.set {
362 if !expected.has(gotElem) {
363 t.Errorf("Got unexpected codeElem %#v", gotElem)
364 }
365 }
366 })
367
368 t.Run("getCommonSet", func(t *testing.T) {
369 got, err := getCommonSet(inFiles)
370 if err != nil {
371 t.Fatal(err)
372 }
373
374 expected := newCodeSet()
375 expected.add(codeElem{token.COMMENT, "Package comments\n"})
376 expected.add(codeElem{token.CONST, `COMMON_INDEPENDENT = 1234`})
377 expected.add(codeElem{token.CONST, `COMMON_GROUP = "COMMON_GROUP"`})
378 expected.add(codeElem{token.CONST, `sub1Common1 = 11`})
379 expected.add(codeElem{token.CONST, `sub1Common3_LONG = 13`})
380 expected.add(codeElem{token.CONST, `sub2Common2 = 22`})
381 expected.add(codeElem{token.CONST, `sub2Common3 = 23`})
382 expected.add(codeElem{token.CONST, `sub3Common1 = 31`})
383 expected.add(codeElem{token.CONST, `sub3Common4 = 34`})
384 expected.add(codeElem{token.TYPE, `commonInt int`})
385 expected.add(codeElem{token.FUNC, `func commonF() string {
386 return commonDep.Use("common")
387 }`})
388
389 if len(got.set) != len(expected.set) {
390 t.Errorf("Got %d codeElems, expected %d", len(got.set), len(expected.set))
391 }
392 for expElem := range expected.set {
393 if !got.has(expElem) {
394 t.Errorf("Didn't get expected codeElem %#v", expElem)
395 }
396 }
397 for gotElem := range got.set {
398 if !expected.has(gotElem) {
399 t.Errorf("Got unexpected codeElem %#v", gotElem)
400 }
401 }
402 })
403
404 t.Run("filter(keepCommon)", func(t *testing.T) {
405 commonSet, err := getCommonSet(inFiles)
406 if err != nil {
407 t.Fatal(err)
408 }
409
410 got, err := filter(inFiles[0].src, commonSet.keepCommon)
411 if err != nil {
412 t.Fatal(err)
413 }
414
415 expected := []byte(mergedFile)
416
417 if !bytes.Equal(got, expected) {
418 t.Errorf("Got:\n%s\nExpected:\n%s", addLineNr(got), addLineNr(expected))
419 diffLines(t, got, expected)
420 }
421 })
422
423 t.Run("filter(keepArchSpecific)", func(t *testing.T) {
424 commonSet, err := getCommonSet(inFiles)
425 if err != nil {
426 t.Fatal(err)
427 }
428
429 for i := range inFiles {
430 got, err := filter(inFiles[i].src, commonSet.keepArchSpecific)
431 if err != nil {
432 t.Fatal(err)
433 }
434
435 expected := outFiles[i].src
436
437 if !bytes.Equal(got, expected) {
438 t.Errorf("Got:\n%s\nExpected:\n%s", addLineNr(got), addLineNr(expected))
439 diffLines(t, got, expected)
440 }
441 }
442 })
443 }
444
445 func TestMergedName(t *testing.T) {
446 t.Run("getValidGOOS", func(t *testing.T) {
447 testcases := []struct {
448 filename, goos string
449 ok bool
450 }{
451 {"zerrors_aix.go", "aix", true},
452 {"zerrors_darwin.go", "darwin", true},
453 {"zerrors_dragonfly.go", "dragonfly", true},
454 {"zerrors_freebsd.go", "freebsd", true},
455 {"zerrors_linux.go", "linux", true},
456 {"zerrors_netbsd.go", "netbsd", true},
457 {"zerrors_openbsd.go", "openbsd", true},
458 {"zerrors_solaris.go", "solaris", true},
459 {"zerrors_multics.go", "", false},
460 }
461 for _, tc := range testcases {
462 goos, ok := getValidGOOS(tc.filename)
463 if goos != tc.goos {
464 t.Errorf("got GOOS %q, expected %q", goos, tc.goos)
465 }
466 if ok != tc.ok {
467 t.Errorf("got ok %v, expected %v", ok, tc.ok)
468 }
469 }
470 })
471 }
472
473
474
475 func diffLines(t *testing.T, got, expected []byte) {
476 t.Helper()
477
478 gotLines := bytes.Split(got, []byte{'\n'})
479 expLines := bytes.Split(expected, []byte{'\n'})
480
481 i := 0
482 for i < len(gotLines) && i < len(expLines) {
483 if !bytes.Equal(gotLines[i], expLines[i]) {
484 t.Errorf("Line %d: Got:\n%q\nExpected:\n%q", i+1, gotLines[i], expLines[i])
485 return
486 }
487 i++
488 }
489
490 if i < len(gotLines) && i >= len(expLines) {
491 t.Errorf("Line %d: got %q, expected EOF", i+1, gotLines[i])
492 }
493 if i >= len(gotLines) && i < len(expLines) {
494 t.Errorf("Line %d: got EOF, expected %q", i+1, gotLines[i])
495 }
496 }
497
498 func addLineNr(src []byte) []byte {
499 lines := bytes.Split(src, []byte("\n"))
500 for i, line := range lines {
501 lines[i] = []byte(fmt.Sprintf("%d: %s", i+1, line))
502 }
503 return bytes.Join(lines, []byte("\n"))
504 }
505
View as plain text