|  | // 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") | 
|  | } | 
|  | } | 
|  |  | 
|  | // 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)), | 
|  | } | 
|  | } |