diff options
Diffstat (limited to 'src/cmd/pack/pack_test.go')
-rw-r--r-- | src/cmd/pack/pack_test.go | 402 |
1 files changed, 402 insertions, 0 deletions
diff --git a/src/cmd/pack/pack_test.go b/src/cmd/pack/pack_test.go new file mode 100644 index 000000000..486242603 --- /dev/null +++ b/src/cmd/pack/pack_test.go @@ -0,0 +1,402 @@ +// 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" + "io" + "io/ioutil" + "os" + "os/exec" + "path/filepath" + "regexp" + "runtime" + "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 +} + +// 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) + 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, 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) { + if runtime.GOOS == "nacl" { + t.Skip("skipping on nacl") + } + + 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) + } + + char := findChar(t, dir) + + run := func(args ...string) string { + return doRun(t, dir, args...) + } + + run("go", "build", "cmd/pack") // writes pack binary to dir + run("go", "tool", char+"g", "hello.go") + run("./pack", "grc", "hello.a", "hello."+char) + run("go", "tool", char+"l", "-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 runtime.GOOS == "nacl" { + t.Skip("skipping on nacl") + } + + 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 < 10000; 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) + } + + char := findChar(t, dir) + + run := func(args ...string) string { + return doRun(t, dir, args...) + } + + run("go", "build", "cmd/pack") // writes pack binary to dir + run("go", "tool", char+"g", "large.go") + run("./pack", "grc", "large.a", "large."+char) + run("go", "tool", char+"g", "-I", ".", "main.go") + run("go", "tool", char+"l", "-L", ".", "-o", "a.out", "main."+char) + out := run("./a.out") + if out != "ok\n" { + t.Fatalf("incorrect output: %q, want %q", out, "ok\n") + } +} + +// 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) +} + +// findChar returns the architecture character for the go command. +func findChar(t *testing.T, dir string) string { + out := doRun(t, dir, "go", "env") + re, err := regexp.Compile(`\s*GOCHAR=['"]?(\w)['"]?`) + if err != nil { + t.Fatal(err) + } + fields := re.FindStringSubmatch(out) + if fields == nil { + t.Fatal("cannot find GOCHAR in 'go env' output:\n", out) + } + return fields[1] +} + +// 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)), + } +} |