// 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" "fmt" "internal/testenv" "io" "io/ioutil" "os" "os/exec" "path/filepath" "testing" "time" "unicode/utf8" ) func TestExactly16Bytes(t *testing.T) { var tests = []string{ "", "a", "日本語", "1234567890123456", "12345678901234567890", "1234567890123本語4567890", "12345678901234日本語567890", "123456789012345日本語67890", "1234567890123456日本語7890", "1234567890123456日本語7日本語890", } for _, str := range tests { got := exactly16Bytes(str) if len(got) != 16 { t.Errorf("exactly16Bytes(%q) is %q, length %d", str, got, len(got)) } // Make sure it is full runes. for _, c := range got { if c == utf8.RuneError { t.Errorf("exactly16Bytes(%q) is %q, has partial rune", str, got) } } } } // tmpDir creates a temporary directory and returns its name. func tmpDir(t *testing.T) string { name, err := ioutil.TempDir("", "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 := archive(name, os.O_RDWR, nil) // Add an entry by hand. ar.addFile(helloFile.Reset()) ar.fd.Close() // Now check it. ar = archive(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.fd.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 := archive(name, os.O_RDWR, nil) // Add some entries by hand. ar.addFile(helloFile.Reset()) ar.addFile(goodbyeFile.Reset()) ar.fd.Close() // Now print it. ar = archive(name, os.O_RDONLY, nil) var buf bytes.Buffer stdout = &buf verbose = true defer func() { stdout = os.Stdout verbose = false }() ar.scan(ar.tableOfContents) ar.fd.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 = archive(name, os.O_RDONLY, nil) ar.scan(ar.tableOfContents) ar.fd.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 = archive(name, os.O_RDONLY, []string{helloFile.name}) ar.scan(ar.tableOfContents) ar.fd.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 := archive(name, os.O_RDWR, nil) // Add some entries by hand. ar.addFile(helloFile.Reset()) ar.addFile(goodbyeFile.Reset()) ar.fd.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 = archive(name, os.O_RDONLY, []string{goodbyeFile.name}) ar.scan(ar.extractContents) ar.fd.Close() data, err := ioutil.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 := ioutil.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) { 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 = ioutil.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 := ioutil.WriteFile(filepath.Join(dir, "a.go"), []byte(aSrc), 0666) if err != nil { t.Fatal(err) } const bSrc = `package b; import _ "a"` err = ioutil.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") } // 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 os.FileInfo. type FakeFile struct { name string contents string mode os.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() (os.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 } // os.FileInfo methods. func (f *FakeFile) Size() int64 { return int64(len(f.contents)) } func (f *FakeFile) Mode() os.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() *Entry { return &Entry{ name: f.name, mtime: 0, // Defined to be zero. uid: 0, // Ditto. gid: 0, // Ditto. mode: f.mode, size: int64(len(f.contents)), } }