summaryrefslogtreecommitdiffstats
path: root/src/cmd/pack/pack_test.go
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-28 13:14:23 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-28 13:14:23 +0000
commit73df946d56c74384511a194dd01dbe099584fd1a (patch)
treefd0bcea490dd81327ddfbb31e215439672c9a068 /src/cmd/pack/pack_test.go
parentInitial commit. (diff)
downloadgolang-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.go490
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))},
+ }
+}