diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-28 13:14:23 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-28 13:14:23 +0000 |
commit | 73df946d56c74384511a194dd01dbe099584fd1a (patch) | |
tree | fd0bcea490dd81327ddfbb31e215439672c9a068 /src/cmd/pack/pack_test.go | |
parent | Initial commit. (diff) | |
download | golang-1.16-upstream.tar.xz golang-1.16-upstream.zip |
Adding upstream version 1.16.10.upstream/1.16.10upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'src/cmd/pack/pack_test.go')
-rw-r--r-- | src/cmd/pack/pack_test.go | 490 |
1 files changed, 490 insertions, 0 deletions
diff --git a/src/cmd/pack/pack_test.go b/src/cmd/pack/pack_test.go new file mode 100644 index 0000000..118376f --- /dev/null +++ b/src/cmd/pack/pack_test.go @@ -0,0 +1,490 @@ +// Copyright 2014 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package main + +import ( + "bufio" + "bytes" + "cmd/internal/archive" + "fmt" + "internal/testenv" + "io" + "io/fs" + "os" + "os/exec" + "path/filepath" + "testing" + "time" +) + +// tmpDir creates a temporary directory and returns its name. +func tmpDir(t *testing.T) string { + name, err := os.MkdirTemp("", "pack") + if err != nil { + t.Fatal(err) + } + return name +} + +// testCreate creates an archive in the specified directory. +func testCreate(t *testing.T, dir string) { + name := filepath.Join(dir, "pack.a") + ar := openArchive(name, os.O_RDWR|os.O_CREATE, nil) + // Add an entry by hand. + ar.addFile(helloFile.Reset()) + ar.a.File().Close() + // Now check it. + ar = openArchive(name, os.O_RDONLY, []string{helloFile.name}) + var buf bytes.Buffer + stdout = &buf + verbose = true + defer func() { + stdout = os.Stdout + verbose = false + }() + ar.scan(ar.printContents) + ar.a.File().Close() + result := buf.String() + // Expect verbose output plus file contents. + expect := fmt.Sprintf("%s\n%s", helloFile.name, helloFile.contents) + if result != expect { + t.Fatalf("expected %q got %q", expect, result) + } +} + +// Test that we can create an archive, write to it, and get the same contents back. +// Tests the rv and then the pv command on a new archive. +func TestCreate(t *testing.T) { + dir := tmpDir(t) + defer os.RemoveAll(dir) + testCreate(t, dir) +} + +// Test that we can create an archive twice with the same name (Issue 8369). +func TestCreateTwice(t *testing.T) { + dir := tmpDir(t) + defer os.RemoveAll(dir) + testCreate(t, dir) + testCreate(t, dir) +} + +// Test that we can create an archive, put some files in it, and get back a correct listing. +// Tests the tv command. +func TestTableOfContents(t *testing.T) { + dir := tmpDir(t) + defer os.RemoveAll(dir) + name := filepath.Join(dir, "pack.a") + ar := openArchive(name, os.O_RDWR|os.O_CREATE, nil) + + // Add some entries by hand. + ar.addFile(helloFile.Reset()) + ar.addFile(goodbyeFile.Reset()) + ar.a.File().Close() + + // Now print it. + var buf bytes.Buffer + stdout = &buf + verbose = true + defer func() { + stdout = os.Stdout + verbose = false + }() + ar = openArchive(name, os.O_RDONLY, nil) + ar.scan(ar.tableOfContents) + ar.a.File().Close() + result := buf.String() + // Expect verbose listing. + expect := fmt.Sprintf("%s\n%s\n", helloFile.Entry(), goodbyeFile.Entry()) + if result != expect { + t.Fatalf("expected %q got %q", expect, result) + } + + // Do it again without verbose. + verbose = false + buf.Reset() + ar = openArchive(name, os.O_RDONLY, nil) + ar.scan(ar.tableOfContents) + ar.a.File().Close() + result = buf.String() + // Expect non-verbose listing. + expect = fmt.Sprintf("%s\n%s\n", helloFile.name, goodbyeFile.name) + if result != expect { + t.Fatalf("expected %q got %q", expect, result) + } + + // Do it again with file list arguments. + verbose = false + buf.Reset() + ar = openArchive(name, os.O_RDONLY, []string{helloFile.name}) + ar.scan(ar.tableOfContents) + ar.a.File().Close() + result = buf.String() + // Expect only helloFile. + expect = fmt.Sprintf("%s\n", helloFile.name) + if result != expect { + t.Fatalf("expected %q got %q", expect, result) + } +} + +// Test that we can create an archive, put some files in it, and get back a file. +// Tests the x command. +func TestExtract(t *testing.T) { + dir := tmpDir(t) + defer os.RemoveAll(dir) + name := filepath.Join(dir, "pack.a") + ar := openArchive(name, os.O_RDWR|os.O_CREATE, nil) + // Add some entries by hand. + ar.addFile(helloFile.Reset()) + ar.addFile(goodbyeFile.Reset()) + ar.a.File().Close() + // Now extract one file. We chdir to the directory of the archive for simplicity. + pwd, err := os.Getwd() + if err != nil { + t.Fatal("os.Getwd: ", err) + } + err = os.Chdir(dir) + if err != nil { + t.Fatal("os.Chdir: ", err) + } + defer func() { + err := os.Chdir(pwd) + if err != nil { + t.Fatal("os.Chdir: ", err) + } + }() + ar = openArchive(name, os.O_RDONLY, []string{goodbyeFile.name}) + ar.scan(ar.extractContents) + ar.a.File().Close() + data, err := os.ReadFile(goodbyeFile.name) + if err != nil { + t.Fatal(err) + } + // Expect contents of file. + result := string(data) + expect := goodbyeFile.contents + if result != expect { + t.Fatalf("expected %q got %q", expect, result) + } +} + +// Test that pack-created archives can be understood by the tools. +func TestHello(t *testing.T) { + testenv.MustHaveGoBuild(t) + + dir := tmpDir(t) + defer os.RemoveAll(dir) + hello := filepath.Join(dir, "hello.go") + prog := ` + package main + func main() { + println("hello world") + } + ` + err := os.WriteFile(hello, []byte(prog), 0666) + if err != nil { + t.Fatal(err) + } + + run := func(args ...string) string { + return doRun(t, dir, args...) + } + + goBin := testenv.GoToolPath(t) + run(goBin, "build", "cmd/pack") // writes pack binary to dir + run(goBin, "tool", "compile", "hello.go") + run("./pack", "grc", "hello.a", "hello.o") + run(goBin, "tool", "link", "-o", "a.out", "hello.a") + out := run("./a.out") + if out != "hello world\n" { + t.Fatalf("incorrect output: %q, want %q", out, "hello world\n") + } +} + +// Test that pack works with very long lines in PKGDEF. +func TestLargeDefs(t *testing.T) { + if testing.Short() { + t.Skip("skipping in -short mode") + } + testenv.MustHaveGoBuild(t) + + dir := tmpDir(t) + defer os.RemoveAll(dir) + large := filepath.Join(dir, "large.go") + f, err := os.Create(large) + if err != nil { + t.Fatal(err) + } + b := bufio.NewWriter(f) + + printf := func(format string, args ...interface{}) { + _, err := fmt.Fprintf(b, format, args...) + if err != nil { + t.Fatalf("Writing to %s: %v", large, err) + } + } + + printf("package large\n\ntype T struct {\n") + for i := 0; i < 1000; i++ { + printf("f%d int `tag:\"", i) + for j := 0; j < 100; j++ { + printf("t%d=%d,", j, j) + } + printf("\"`\n") + } + printf("}\n") + if err = b.Flush(); err != nil { + t.Fatal(err) + } + if err = f.Close(); err != nil { + t.Fatal(err) + } + + main := filepath.Join(dir, "main.go") + prog := ` + package main + import "large" + var V large.T + func main() { + println("ok") + } + ` + err = os.WriteFile(main, []byte(prog), 0666) + if err != nil { + t.Fatal(err) + } + + run := func(args ...string) string { + return doRun(t, dir, args...) + } + + goBin := testenv.GoToolPath(t) + run(goBin, "build", "cmd/pack") // writes pack binary to dir + run(goBin, "tool", "compile", "large.go") + run("./pack", "grc", "large.a", "large.o") + run(goBin, "tool", "compile", "-I", ".", "main.go") + run(goBin, "tool", "link", "-L", ".", "-o", "a.out", "main.o") + out := run("./a.out") + if out != "ok\n" { + t.Fatalf("incorrect output: %q, want %q", out, "ok\n") + } +} + +// Test that "\n!\n" inside export data doesn't result in a truncated +// package definition when creating a .a archive from a .o Go object. +func TestIssue21703(t *testing.T) { + testenv.MustHaveGoBuild(t) + + dir := tmpDir(t) + defer os.RemoveAll(dir) + + const aSrc = `package a; const X = "\n!\n"` + err := os.WriteFile(filepath.Join(dir, "a.go"), []byte(aSrc), 0666) + if err != nil { + t.Fatal(err) + } + + const bSrc = `package b; import _ "a"` + err = os.WriteFile(filepath.Join(dir, "b.go"), []byte(bSrc), 0666) + if err != nil { + t.Fatal(err) + } + + run := func(args ...string) string { + return doRun(t, dir, args...) + } + + goBin := testenv.GoToolPath(t) + run(goBin, "build", "cmd/pack") // writes pack binary to dir + run(goBin, "tool", "compile", "a.go") + run("./pack", "c", "a.a", "a.o") + run(goBin, "tool", "compile", "-I", ".", "b.go") +} + +// Test the "c" command can "see through" the archive generated by the compiler. +// This is peculiar. (See issue #43271) +func TestCreateWithCompilerObj(t *testing.T) { + testenv.MustHaveGoBuild(t) + + dir := tmpDir(t) + defer os.RemoveAll(dir) + src := filepath.Join(dir, "p.go") + prog := "package p; var X = 42\n" + err := os.WriteFile(src, []byte(prog), 0666) + if err != nil { + t.Fatal(err) + } + + run := func(args ...string) string { + return doRun(t, dir, args...) + } + + goBin := testenv.GoToolPath(t) + run(goBin, "build", "cmd/pack") // writes pack binary to dir + run(goBin, "tool", "compile", "-pack", "-o", "p.a", "p.go") + run("./pack", "c", "packed.a", "p.a") + fi, err := os.Stat(filepath.Join(dir, "p.a")) + if err != nil { + t.Fatalf("stat p.a failed: %v", err) + } + fi2, err := os.Stat(filepath.Join(dir, "packed.a")) + if err != nil { + t.Fatalf("stat packed.a failed: %v", err) + } + // For compiler-generated object file, the "c" command is + // expected to get (essentially) the same file back, instead + // of packing it into a new archive with a single entry. + if want, got := fi.Size(), fi2.Size(); want != got { + t.Errorf("packed file with different size: want %d, got %d", want, got) + } + + // Test -linkobj flag as well. + run(goBin, "tool", "compile", "-linkobj", "p2.a", "-o", "p.x", "p.go") + run("./pack", "c", "packed2.a", "p2.a") + fi, err = os.Stat(filepath.Join(dir, "p2.a")) + if err != nil { + t.Fatalf("stat p2.a failed: %v", err) + } + fi2, err = os.Stat(filepath.Join(dir, "packed2.a")) + if err != nil { + t.Fatalf("stat packed2.a failed: %v", err) + } + if want, got := fi.Size(), fi2.Size(); want != got { + t.Errorf("packed file with different size: want %d, got %d", want, got) + } + + run("./pack", "c", "packed3.a", "p.x") + fi, err = os.Stat(filepath.Join(dir, "p.x")) + if err != nil { + t.Fatalf("stat p.x failed: %v", err) + } + fi2, err = os.Stat(filepath.Join(dir, "packed3.a")) + if err != nil { + t.Fatalf("stat packed3.a failed: %v", err) + } + if want, got := fi.Size(), fi2.Size(); want != got { + t.Errorf("packed file with different size: want %d, got %d", want, got) + } +} + +// Test the "r" command creates the output file if it does not exist. +func TestRWithNonexistentFile(t *testing.T) { + testenv.MustHaveGoBuild(t) + + dir := tmpDir(t) + defer os.RemoveAll(dir) + src := filepath.Join(dir, "p.go") + prog := "package p; var X = 42\n" + err := os.WriteFile(src, []byte(prog), 0666) + if err != nil { + t.Fatal(err) + } + + run := func(args ...string) string { + return doRun(t, dir, args...) + } + + goBin := testenv.GoToolPath(t) + run(goBin, "build", "cmd/pack") // writes pack binary to dir + run(goBin, "tool", "compile", "-o", "p.o", "p.go") + run("./pack", "r", "p.a", "p.o") // should succeed +} + +// doRun runs a program in a directory and returns the output. +func doRun(t *testing.T, dir string, args ...string) string { + cmd := exec.Command(args[0], args[1:]...) + cmd.Dir = dir + out, err := cmd.CombinedOutput() + if err != nil { + t.Fatalf("%v: %v\n%s", args, err, string(out)) + } + return string(out) +} + +// Fake implementation of files. + +var helloFile = &FakeFile{ + name: "hello", + contents: "hello world", // 11 bytes, an odd number. + mode: 0644, +} + +var goodbyeFile = &FakeFile{ + name: "goodbye", + contents: "Sayonara, Jim", // 13 bytes, another odd number. + mode: 0644, +} + +// FakeFile implements FileLike and also fs.FileInfo. +type FakeFile struct { + name string + contents string + mode fs.FileMode + offset int +} + +// Reset prepares a FakeFile for reuse. +func (f *FakeFile) Reset() *FakeFile { + f.offset = 0 + return f +} + +// FileLike methods. + +func (f *FakeFile) Name() string { + // A bit of a cheat: we only have a basename, so that's also ok for FileInfo. + return f.name +} + +func (f *FakeFile) Stat() (fs.FileInfo, error) { + return f, nil +} + +func (f *FakeFile) Read(p []byte) (int, error) { + if f.offset >= len(f.contents) { + return 0, io.EOF + } + n := copy(p, f.contents[f.offset:]) + f.offset += n + return n, nil +} + +func (f *FakeFile) Close() error { + return nil +} + +// fs.FileInfo methods. + +func (f *FakeFile) Size() int64 { + return int64(len(f.contents)) +} + +func (f *FakeFile) Mode() fs.FileMode { + return f.mode +} + +func (f *FakeFile) ModTime() time.Time { + return time.Time{} +} + +func (f *FakeFile) IsDir() bool { + return false +} + +func (f *FakeFile) Sys() interface{} { + return nil +} + +// Special helpers. + +func (f *FakeFile) Entry() *archive.Entry { + return &archive.Entry{ + Name: f.name, + Mtime: 0, // Defined to be zero. + Uid: 0, // Ditto. + Gid: 0, // Ditto. + Mode: f.mode, + Data: archive.Data{Size: int64(len(f.contents))}, + } +} |