Source file
src/cmd/pack/pack_test.go
1
2
3
4
5 package main
6
7 import (
8 "bufio"
9 "bytes"
10 "cmd/internal/archive"
11 "fmt"
12 "internal/testenv"
13 "io"
14 "io/fs"
15 "os"
16 "path/filepath"
17 "runtime"
18 "strings"
19 "testing"
20 "time"
21 )
22
23
24
25 func TestMain(m *testing.M) {
26 if os.Getenv("GO_PACKTEST_IS_PACK") != "" {
27 main()
28 os.Exit(0)
29 }
30
31 os.Setenv("GO_PACKTEST_IS_PACK", "1")
32 os.Exit(m.Run())
33 }
34
35
36 func packPath(t testing.TB) string {
37 return testenv.Executable(t)
38 }
39
40
41 func testCreate(t *testing.T, dir string) {
42 name := filepath.Join(dir, "pack.a")
43 ar := openArchive(name, os.O_RDWR|os.O_CREATE, nil)
44
45 ar.addFile(helloFile.Reset())
46 ar.a.File().Close()
47
48 ar = openArchive(name, os.O_RDONLY, []string{helloFile.name})
49 var buf strings.Builder
50 stdout = &buf
51 verbose = true
52 defer func() {
53 stdout = os.Stdout
54 verbose = false
55 }()
56 ar.scan(ar.printContents)
57 ar.a.File().Close()
58 result := buf.String()
59
60 expect := fmt.Sprintf("%s\n%s", helloFile.name, helloFile.contents)
61 if result != expect {
62 t.Fatalf("expected %q got %q", expect, result)
63 }
64 }
65
66
67
68 func TestCreate(t *testing.T) {
69 dir := t.TempDir()
70 testCreate(t, dir)
71 }
72
73
74 func TestCreateTwice(t *testing.T) {
75 dir := t.TempDir()
76 testCreate(t, dir)
77 testCreate(t, dir)
78 }
79
80
81
82 func TestTableOfContents(t *testing.T) {
83 dir := t.TempDir()
84 name := filepath.Join(dir, "pack.a")
85 ar := openArchive(name, os.O_RDWR|os.O_CREATE, nil)
86
87
88 ar.addFile(helloFile.Reset())
89 ar.addFile(goodbyeFile.Reset())
90 ar.a.File().Close()
91
92
93 var buf strings.Builder
94 stdout = &buf
95 verbose = true
96 defer func() {
97 stdout = os.Stdout
98 verbose = false
99 }()
100 ar = openArchive(name, os.O_RDONLY, nil)
101 ar.scan(ar.tableOfContents)
102 ar.a.File().Close()
103 result := buf.String()
104
105 expect := fmt.Sprintf("%s\n%s\n", helloFile.Entry(), goodbyeFile.Entry())
106 if result != expect {
107 t.Fatalf("expected %q got %q", expect, result)
108 }
109
110
111 verbose = false
112 buf.Reset()
113 ar = openArchive(name, os.O_RDONLY, nil)
114 ar.scan(ar.tableOfContents)
115 ar.a.File().Close()
116 result = buf.String()
117
118 expect = fmt.Sprintf("%s\n%s\n", helloFile.name, goodbyeFile.name)
119 if result != expect {
120 t.Fatalf("expected %q got %q", expect, result)
121 }
122
123
124 verbose = false
125 buf.Reset()
126 ar = openArchive(name, os.O_RDONLY, []string{helloFile.name})
127 ar.scan(ar.tableOfContents)
128 ar.a.File().Close()
129 result = buf.String()
130
131 expect = fmt.Sprintf("%s\n", helloFile.name)
132 if result != expect {
133 t.Fatalf("expected %q got %q", expect, result)
134 }
135 }
136
137
138
139 func TestExtract(t *testing.T) {
140 dir := t.TempDir()
141 name := filepath.Join(dir, "pack.a")
142 ar := openArchive(name, os.O_RDWR|os.O_CREATE, nil)
143
144 ar.addFile(helloFile.Reset())
145 ar.addFile(goodbyeFile.Reset())
146 ar.a.File().Close()
147
148 t.Chdir(dir)
149 ar = openArchive(name, os.O_RDONLY, []string{goodbyeFile.name})
150 ar.scan(ar.extractContents)
151 ar.a.File().Close()
152 data, err := os.ReadFile(goodbyeFile.name)
153 if err != nil {
154 t.Fatal(err)
155 }
156
157 result := string(data)
158 expect := goodbyeFile.contents
159 if result != expect {
160 t.Fatalf("expected %q got %q", expect, result)
161 }
162 }
163
164
165 func TestHello(t *testing.T) {
166 testenv.MustHaveGoBuild(t)
167
168
169 testenv.MustInternalLink(t, testenv.NoSpecialBuildTypes)
170
171 dir := t.TempDir()
172 hello := filepath.Join(dir, "hello.go")
173 prog := `
174 package main
175 func main() {
176 println("hello world")
177 }
178 `
179 err := os.WriteFile(hello, []byte(prog), 0666)
180 if err != nil {
181 t.Fatal(err)
182 }
183
184 run := func(args ...string) string {
185 return doRun(t, dir, args...)
186 }
187
188 importcfgfile := filepath.Join(dir, "hello.importcfg")
189 testenv.WriteImportcfg(t, importcfgfile, nil, hello)
190
191 goBin := testenv.GoToolPath(t)
192 run(goBin, "tool", "compile", "-importcfg="+importcfgfile, "-p=main", "hello.go")
193 run(packPath(t), "grc", "hello.a", "hello.o")
194 run(goBin, "tool", "link", "-importcfg="+importcfgfile, "-o", "a.out", "hello.a")
195 out := run("./a.out")
196 if out != "hello world\n" {
197 t.Fatalf("incorrect output: %q, want %q", out, "hello world\n")
198 }
199 }
200
201
202 func TestLargeDefs(t *testing.T) {
203 if testing.Short() {
204 t.Skip("skipping in -short mode")
205 }
206 testenv.MustHaveGoBuild(t)
207
208 dir := t.TempDir()
209 large := filepath.Join(dir, "large.go")
210 f, err := os.Create(large)
211 if err != nil {
212 t.Fatal(err)
213 }
214 b := bufio.NewWriter(f)
215
216 printf := func(format string, args ...any) {
217 _, err := fmt.Fprintf(b, format, args...)
218 if err != nil {
219 t.Fatalf("Writing to %s: %v", large, err)
220 }
221 }
222
223 printf("package large\n\ntype T struct {\n")
224 for i := 0; i < 1000; i++ {
225 printf("f%d int `tag:\"", i)
226 for j := 0; j < 100; j++ {
227 printf("t%d=%d,", j, j)
228 }
229 printf("\"`\n")
230 }
231 printf("}\n")
232 if err = b.Flush(); err != nil {
233 t.Fatal(err)
234 }
235 if err = f.Close(); err != nil {
236 t.Fatal(err)
237 }
238
239 main := filepath.Join(dir, "main.go")
240 prog := `
241 package main
242 import "large"
243 var V large.T
244 func main() {
245 println("ok")
246 }
247 `
248 err = os.WriteFile(main, []byte(prog), 0666)
249 if err != nil {
250 t.Fatal(err)
251 }
252
253 run := func(args ...string) string {
254 return doRun(t, dir, args...)
255 }
256
257 importcfgfile := filepath.Join(dir, "hello.importcfg")
258 testenv.WriteImportcfg(t, importcfgfile, nil)
259
260 goBin := testenv.GoToolPath(t)
261 run(goBin, "tool", "compile", "-importcfg="+importcfgfile, "-p=large", "large.go")
262 run(packPath(t), "grc", "large.a", "large.o")
263 testenv.WriteImportcfg(t, importcfgfile, map[string]string{"large": filepath.Join(dir, "large.o")}, "runtime")
264 run(goBin, "tool", "compile", "-importcfg="+importcfgfile, "-p=main", "main.go")
265 run(goBin, "tool", "link", "-importcfg="+importcfgfile, "-L", ".", "-o", "a.out", "main.o")
266 out := run("./a.out")
267 if out != "ok\n" {
268 t.Fatalf("incorrect output: %q, want %q", out, "ok\n")
269 }
270 }
271
272
273
274 func TestIssue21703(t *testing.T) {
275 testenv.MustHaveGoBuild(t)
276
277 dir := t.TempDir()
278
279 const aSrc = `package a; const X = "\n!\n"`
280 err := os.WriteFile(filepath.Join(dir, "a.go"), []byte(aSrc), 0666)
281 if err != nil {
282 t.Fatal(err)
283 }
284
285 const bSrc = `package b; import _ "a"`
286 err = os.WriteFile(filepath.Join(dir, "b.go"), []byte(bSrc), 0666)
287 if err != nil {
288 t.Fatal(err)
289 }
290
291 run := func(args ...string) string {
292 return doRun(t, dir, args...)
293 }
294
295 goBin := testenv.GoToolPath(t)
296 run(goBin, "tool", "compile", "-p=a", "a.go")
297 run(packPath(t), "c", "a.a", "a.o")
298 run(goBin, "tool", "compile", "-p=b", "-I", ".", "b.go")
299 }
300
301
302
303 func TestCreateWithCompilerObj(t *testing.T) {
304 testenv.MustHaveGoBuild(t)
305
306 dir := t.TempDir()
307 src := filepath.Join(dir, "p.go")
308 prog := "package p; var X = 42\n"
309 err := os.WriteFile(src, []byte(prog), 0666)
310 if err != nil {
311 t.Fatal(err)
312 }
313
314 run := func(args ...string) string {
315 return doRun(t, dir, args...)
316 }
317
318 goBin := testenv.GoToolPath(t)
319 run(goBin, "tool", "compile", "-pack", "-p=p", "-o", "p.a", "p.go")
320 run(packPath(t), "c", "packed.a", "p.a")
321 fi, err := os.Stat(filepath.Join(dir, "p.a"))
322 if err != nil {
323 t.Fatalf("stat p.a failed: %v", err)
324 }
325 fi2, err := os.Stat(filepath.Join(dir, "packed.a"))
326 if err != nil {
327 t.Fatalf("stat packed.a failed: %v", err)
328 }
329
330
331
332 if want, got := fi.Size(), fi2.Size(); want != got {
333 t.Errorf("packed file with different size: want %d, got %d", want, got)
334 }
335
336
337 run(goBin, "tool", "compile", "-p=p", "-linkobj", "p2.a", "-o", "p.x", "p.go")
338 run(packPath(t), "c", "packed2.a", "p2.a")
339 fi, err = os.Stat(filepath.Join(dir, "p2.a"))
340 if err != nil {
341 t.Fatalf("stat p2.a failed: %v", err)
342 }
343 fi2, err = os.Stat(filepath.Join(dir, "packed2.a"))
344 if err != nil {
345 t.Fatalf("stat packed2.a failed: %v", err)
346 }
347 if want, got := fi.Size(), fi2.Size(); want != got {
348 t.Errorf("packed file with different size: want %d, got %d", want, got)
349 }
350
351 run(packPath(t), "c", "packed3.a", "p.x")
352 fi, err = os.Stat(filepath.Join(dir, "p.x"))
353 if err != nil {
354 t.Fatalf("stat p.x failed: %v", err)
355 }
356 fi2, err = os.Stat(filepath.Join(dir, "packed3.a"))
357 if err != nil {
358 t.Fatalf("stat packed3.a failed: %v", err)
359 }
360 if want, got := fi.Size(), fi2.Size(); want != got {
361 t.Errorf("packed file with different size: want %d, got %d", want, got)
362 }
363 }
364
365
366 func TestRWithNonexistentFile(t *testing.T) {
367 testenv.MustHaveGoBuild(t)
368
369 dir := t.TempDir()
370 src := filepath.Join(dir, "p.go")
371 prog := "package p; var X = 42\n"
372 err := os.WriteFile(src, []byte(prog), 0666)
373 if err != nil {
374 t.Fatal(err)
375 }
376
377 run := func(args ...string) string {
378 return doRun(t, dir, args...)
379 }
380
381 goBin := testenv.GoToolPath(t)
382 run(goBin, "tool", "compile", "-p=p", "-o", "p.o", "p.go")
383 run(packPath(t), "r", "p.a", "p.o")
384 }
385
386 func TestOutputPathSanitization(t *testing.T) {
387 dir := t.TempDir()
388
389
390
391 const validPathName = "longpathname"
392 if err := os.WriteFile(dir+"/"+validPathName, make([]byte, 8), 0o666); err != nil {
393 t.Fatal(err)
394 }
395 doRun(t, dir, packPath(t), "grc", "pack.a", validPathName)
396
397
398 b, err := os.ReadFile(dir + "/pack.a")
399 if err != nil {
400 t.Fatal(err)
401 }
402 idx := bytes.Index(b, []byte(validPathName))
403 if idx < 0 {
404 t.Fatalf("%v not found in pack.a", validPathName)
405 }
406 copy(b[idx:], "out/")
407 os.WriteFile(dir+"/evil.a", b, 0o666)
408
409
410 os.Mkdir(dir+"/out", 0o777)
411
412 cmd := testenv.Command(t, packPath(t), "x", "evil.a")
413 cmd.Dir = dir
414 _, err = cmd.CombinedOutput()
415 if err == nil {
416 t.Errorf("pack x evil.a: unexpected success")
417 }
418
419 ents, err := os.ReadDir(dir + "/out")
420 if err != nil {
421 t.Error(err)
422 }
423 for _, e := range ents {
424 t.Errorf("unexpected file in /out: %q", e.Name())
425 }
426
427 }
428
429
430 func doRun(t *testing.T, dir string, args ...string) string {
431 cmd := testenv.Command(t, args[0], args[1:]...)
432 cmd.Dir = dir
433 out, err := cmd.CombinedOutput()
434 if err != nil {
435 if t.Name() == "TestHello" && runtime.GOOS == "android" && runtime.GOARCH == "arm64" {
436 testenv.SkipFlaky(t, 58806)
437 }
438 t.Fatalf("%v: %v\n%s", args, err, string(out))
439 }
440 return string(out)
441 }
442
443
444
445 var helloFile = &FakeFile{
446 name: "hello",
447 contents: "hello world",
448 mode: 0644,
449 }
450
451 var goodbyeFile = &FakeFile{
452 name: "goodbye",
453 contents: "Sayonara, Jim",
454 mode: 0644,
455 }
456
457
458 type FakeFile struct {
459 name string
460 contents string
461 mode fs.FileMode
462 offset int
463 }
464
465
466 func (f *FakeFile) Reset() *FakeFile {
467 f.offset = 0
468 return f
469 }
470
471
472
473 func (f *FakeFile) Name() string {
474
475 return f.name
476 }
477
478 func (f *FakeFile) Stat() (fs.FileInfo, error) {
479 return f, nil
480 }
481
482 func (f *FakeFile) Read(p []byte) (int, error) {
483 if f.offset >= len(f.contents) {
484 return 0, io.EOF
485 }
486 n := copy(p, f.contents[f.offset:])
487 f.offset += n
488 return n, nil
489 }
490
491 func (f *FakeFile) Close() error {
492 return nil
493 }
494
495
496
497 func (f *FakeFile) Size() int64 {
498 return int64(len(f.contents))
499 }
500
501 func (f *FakeFile) Mode() fs.FileMode {
502 return f.mode
503 }
504
505 func (f *FakeFile) ModTime() time.Time {
506 return time.Time{}
507 }
508
509 func (f *FakeFile) IsDir() bool {
510 return false
511 }
512
513 func (f *FakeFile) Sys() any {
514 return nil
515 }
516
517 func (f *FakeFile) String() string {
518 return fs.FormatFileInfo(f)
519 }
520
521
522
523 func (f *FakeFile) Entry() *archive.Entry {
524 return &archive.Entry{
525 Name: f.name,
526 Mtime: 0,
527 Uid: 0,
528 Gid: 0,
529 Mode: f.mode,
530 Data: archive.Data{Size: int64(len(f.contents))},
531 }
532 }
533
View as plain text