Home | History | Annotate | Download | only in pack
      1 // Copyright 2014 The Go Authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style
      3 // license that can be found in the LICENSE file.
      4 
      5 package main
      6 
      7 import (
      8 	"bufio"
      9 	"bytes"
     10 	"fmt"
     11 	"internal/testenv"
     12 	"io"
     13 	"io/ioutil"
     14 	"os"
     15 	"os/exec"
     16 	"path/filepath"
     17 	"testing"
     18 	"time"
     19 	"unicode/utf8"
     20 )
     21 
     22 func TestExactly16Bytes(t *testing.T) {
     23 	var tests = []string{
     24 		"",
     25 		"a",
     26 		"",
     27 		"1234567890123456",
     28 		"12345678901234567890",
     29 		"12345678901234567890",
     30 		"12345678901234567890",
     31 		"12345678901234567890",
     32 		"12345678901234567890",
     33 		"12345678901234567890",
     34 	}
     35 	for _, str := range tests {
     36 		got := exactly16Bytes(str)
     37 		if len(got) != 16 {
     38 			t.Errorf("exactly16Bytes(%q) is %q, length %d", str, got, len(got))
     39 		}
     40 		// Make sure it is full runes.
     41 		for _, c := range got {
     42 			if c == utf8.RuneError {
     43 				t.Errorf("exactly16Bytes(%q) is %q, has partial rune", str, got)
     44 			}
     45 		}
     46 	}
     47 }
     48 
     49 // tmpDir creates a temporary directory and returns its name.
     50 func tmpDir(t *testing.T) string {
     51 	name, err := ioutil.TempDir("", "pack")
     52 	if err != nil {
     53 		t.Fatal(err)
     54 	}
     55 	return name
     56 }
     57 
     58 // testCreate creates an archive in the specified directory.
     59 func testCreate(t *testing.T, dir string) {
     60 	name := filepath.Join(dir, "pack.a")
     61 	ar := archive(name, os.O_RDWR, nil)
     62 	// Add an entry by hand.
     63 	ar.addFile(helloFile.Reset())
     64 	ar.fd.Close()
     65 	// Now check it.
     66 	ar = archive(name, os.O_RDONLY, []string{helloFile.name})
     67 	var buf bytes.Buffer
     68 	stdout = &buf
     69 	verbose = true
     70 	defer func() {
     71 		stdout = os.Stdout
     72 		verbose = false
     73 	}()
     74 	ar.scan(ar.printContents)
     75 	ar.fd.Close()
     76 	result := buf.String()
     77 	// Expect verbose output plus file contents.
     78 	expect := fmt.Sprintf("%s\n%s", helloFile.name, helloFile.contents)
     79 	if result != expect {
     80 		t.Fatalf("expected %q got %q", expect, result)
     81 	}
     82 }
     83 
     84 // Test that we can create an archive, write to it, and get the same contents back.
     85 // Tests the rv and then the pv command on a new archive.
     86 func TestCreate(t *testing.T) {
     87 	dir := tmpDir(t)
     88 	defer os.RemoveAll(dir)
     89 	testCreate(t, dir)
     90 }
     91 
     92 // Test that we can create an archive twice with the same name (Issue 8369).
     93 func TestCreateTwice(t *testing.T) {
     94 	dir := tmpDir(t)
     95 	defer os.RemoveAll(dir)
     96 	testCreate(t, dir)
     97 	testCreate(t, dir)
     98 }
     99 
    100 // Test that we can create an archive, put some files in it, and get back a correct listing.
    101 // Tests the tv command.
    102 func TestTableOfContents(t *testing.T) {
    103 	dir := tmpDir(t)
    104 	defer os.RemoveAll(dir)
    105 	name := filepath.Join(dir, "pack.a")
    106 	ar := archive(name, os.O_RDWR, nil)
    107 
    108 	// Add some entries by hand.
    109 	ar.addFile(helloFile.Reset())
    110 	ar.addFile(goodbyeFile.Reset())
    111 	ar.fd.Close()
    112 
    113 	// Now print it.
    114 	ar = archive(name, os.O_RDONLY, nil)
    115 	var buf bytes.Buffer
    116 	stdout = &buf
    117 	verbose = true
    118 	defer func() {
    119 		stdout = os.Stdout
    120 		verbose = false
    121 	}()
    122 	ar.scan(ar.tableOfContents)
    123 	ar.fd.Close()
    124 	result := buf.String()
    125 	// Expect verbose listing.
    126 	expect := fmt.Sprintf("%s\n%s\n", helloFile.Entry(), goodbyeFile.Entry())
    127 	if result != expect {
    128 		t.Fatalf("expected %q got %q", expect, result)
    129 	}
    130 
    131 	// Do it again without verbose.
    132 	verbose = false
    133 	buf.Reset()
    134 	ar = archive(name, os.O_RDONLY, nil)
    135 	ar.scan(ar.tableOfContents)
    136 	ar.fd.Close()
    137 	result = buf.String()
    138 	// Expect non-verbose listing.
    139 	expect = fmt.Sprintf("%s\n%s\n", helloFile.name, goodbyeFile.name)
    140 	if result != expect {
    141 		t.Fatalf("expected %q got %q", expect, result)
    142 	}
    143 
    144 	// Do it again with file list arguments.
    145 	verbose = false
    146 	buf.Reset()
    147 	ar = archive(name, os.O_RDONLY, []string{helloFile.name})
    148 	ar.scan(ar.tableOfContents)
    149 	ar.fd.Close()
    150 	result = buf.String()
    151 	// Expect only helloFile.
    152 	expect = fmt.Sprintf("%s\n", helloFile.name)
    153 	if result != expect {
    154 		t.Fatalf("expected %q got %q", expect, result)
    155 	}
    156 }
    157 
    158 // Test that we can create an archive, put some files in it, and get back a file.
    159 // Tests the x command.
    160 func TestExtract(t *testing.T) {
    161 	dir := tmpDir(t)
    162 	defer os.RemoveAll(dir)
    163 	name := filepath.Join(dir, "pack.a")
    164 	ar := archive(name, os.O_RDWR, nil)
    165 	// Add some entries by hand.
    166 	ar.addFile(helloFile.Reset())
    167 	ar.addFile(goodbyeFile.Reset())
    168 	ar.fd.Close()
    169 	// Now extract one file. We chdir to the directory of the archive for simplicity.
    170 	pwd, err := os.Getwd()
    171 	if err != nil {
    172 		t.Fatal("os.Getwd: ", err)
    173 	}
    174 	err = os.Chdir(dir)
    175 	if err != nil {
    176 		t.Fatal("os.Chdir: ", err)
    177 	}
    178 	defer func() {
    179 		err := os.Chdir(pwd)
    180 		if err != nil {
    181 			t.Fatal("os.Chdir: ", err)
    182 		}
    183 	}()
    184 	ar = archive(name, os.O_RDONLY, []string{goodbyeFile.name})
    185 	ar.scan(ar.extractContents)
    186 	ar.fd.Close()
    187 	data, err := ioutil.ReadFile(goodbyeFile.name)
    188 	if err != nil {
    189 		t.Fatal(err)
    190 	}
    191 	// Expect contents of file.
    192 	result := string(data)
    193 	expect := goodbyeFile.contents
    194 	if result != expect {
    195 		t.Fatalf("expected %q got %q", expect, result)
    196 	}
    197 }
    198 
    199 // Test that pack-created archives can be understood by the tools.
    200 func TestHello(t *testing.T) {
    201 	testenv.MustHaveGoBuild(t)
    202 
    203 	dir := tmpDir(t)
    204 	defer os.RemoveAll(dir)
    205 	hello := filepath.Join(dir, "hello.go")
    206 	prog := `
    207 		package main
    208 		func main() {
    209 			println("hello world")
    210 		}
    211 	`
    212 	err := ioutil.WriteFile(hello, []byte(prog), 0666)
    213 	if err != nil {
    214 		t.Fatal(err)
    215 	}
    216 
    217 	run := func(args ...string) string {
    218 		return doRun(t, dir, args...)
    219 	}
    220 
    221 	goBin := testenv.GoToolPath(t)
    222 	run(goBin, "build", "cmd/pack") // writes pack binary to dir
    223 	run(goBin, "tool", "compile", "hello.go")
    224 	run("./pack", "grc", "hello.a", "hello.o")
    225 	run(goBin, "tool", "link", "-o", "a.out", "hello.a")
    226 	out := run("./a.out")
    227 	if out != "hello world\n" {
    228 		t.Fatalf("incorrect output: %q, want %q", out, "hello world\n")
    229 	}
    230 }
    231 
    232 // Test that pack works with very long lines in PKGDEF.
    233 func TestLargeDefs(t *testing.T) {
    234 	testenv.MustHaveGoBuild(t)
    235 
    236 	dir := tmpDir(t)
    237 	defer os.RemoveAll(dir)
    238 	large := filepath.Join(dir, "large.go")
    239 	f, err := os.Create(large)
    240 	if err != nil {
    241 		t.Fatal(err)
    242 	}
    243 	b := bufio.NewWriter(f)
    244 
    245 	printf := func(format string, args ...interface{}) {
    246 		_, err := fmt.Fprintf(b, format, args...)
    247 		if err != nil {
    248 			t.Fatalf("Writing to %s: %v", large, err)
    249 		}
    250 	}
    251 
    252 	printf("package large\n\ntype T struct {\n")
    253 	for i := 0; i < 1000; i++ {
    254 		printf("f%d int `tag:\"", i)
    255 		for j := 0; j < 100; j++ {
    256 			printf("t%d=%d,", j, j)
    257 		}
    258 		printf("\"`\n")
    259 	}
    260 	printf("}\n")
    261 	if err = b.Flush(); err != nil {
    262 		t.Fatal(err)
    263 	}
    264 	if err = f.Close(); err != nil {
    265 		t.Fatal(err)
    266 	}
    267 
    268 	main := filepath.Join(dir, "main.go")
    269 	prog := `
    270 		package main
    271 		import "large"
    272 		var V large.T
    273 		func main() {
    274 			println("ok")
    275 		}
    276 	`
    277 	err = ioutil.WriteFile(main, []byte(prog), 0666)
    278 	if err != nil {
    279 		t.Fatal(err)
    280 	}
    281 
    282 	run := func(args ...string) string {
    283 		return doRun(t, dir, args...)
    284 	}
    285 
    286 	goBin := testenv.GoToolPath(t)
    287 	run(goBin, "build", "cmd/pack") // writes pack binary to dir
    288 	run(goBin, "tool", "compile", "large.go")
    289 	run("./pack", "grc", "large.a", "large.o")
    290 	run(goBin, "tool", "compile", "-I", ".", "main.go")
    291 	run(goBin, "tool", "link", "-L", ".", "-o", "a.out", "main.o")
    292 	out := run("./a.out")
    293 	if out != "ok\n" {
    294 		t.Fatalf("incorrect output: %q, want %q", out, "ok\n")
    295 	}
    296 }
    297 
    298 // Test that "\n!\n" inside export data doesn't result in a truncated
    299 // package definition when creating a .a archive from a .o Go object.
    300 func TestIssue21703(t *testing.T) {
    301 	testenv.MustHaveGoBuild(t)
    302 
    303 	dir := tmpDir(t)
    304 	defer os.RemoveAll(dir)
    305 
    306 	const aSrc = `package a; const X = "\n!\n"`
    307 	err := ioutil.WriteFile(filepath.Join(dir, "a.go"), []byte(aSrc), 0666)
    308 	if err != nil {
    309 		t.Fatal(err)
    310 	}
    311 
    312 	const bSrc = `package b; import _ "a"`
    313 	err = ioutil.WriteFile(filepath.Join(dir, "b.go"), []byte(bSrc), 0666)
    314 	if err != nil {
    315 		t.Fatal(err)
    316 	}
    317 
    318 	run := func(args ...string) string {
    319 		return doRun(t, dir, args...)
    320 	}
    321 
    322 	goBin := testenv.GoToolPath(t)
    323 	run(goBin, "build", "cmd/pack") // writes pack binary to dir
    324 	run(goBin, "tool", "compile", "a.go")
    325 	run("./pack", "c", "a.a", "a.o")
    326 	run(goBin, "tool", "compile", "-I", ".", "b.go")
    327 }
    328 
    329 // doRun runs a program in a directory and returns the output.
    330 func doRun(t *testing.T, dir string, args ...string) string {
    331 	cmd := exec.Command(args[0], args[1:]...)
    332 	cmd.Dir = dir
    333 	out, err := cmd.CombinedOutput()
    334 	if err != nil {
    335 		t.Fatalf("%v: %v\n%s", args, err, string(out))
    336 	}
    337 	return string(out)
    338 }
    339 
    340 // Fake implementation of files.
    341 
    342 var helloFile = &FakeFile{
    343 	name:     "hello",
    344 	contents: "hello world", // 11 bytes, an odd number.
    345 	mode:     0644,
    346 }
    347 
    348 var goodbyeFile = &FakeFile{
    349 	name:     "goodbye",
    350 	contents: "Sayonara, Jim", // 13 bytes, another odd number.
    351 	mode:     0644,
    352 }
    353 
    354 // FakeFile implements FileLike and also os.FileInfo.
    355 type FakeFile struct {
    356 	name     string
    357 	contents string
    358 	mode     os.FileMode
    359 	offset   int
    360 }
    361 
    362 // Reset prepares a FakeFile for reuse.
    363 func (f *FakeFile) Reset() *FakeFile {
    364 	f.offset = 0
    365 	return f
    366 }
    367 
    368 // FileLike methods.
    369 
    370 func (f *FakeFile) Name() string {
    371 	// A bit of a cheat: we only have a basename, so that's also ok for FileInfo.
    372 	return f.name
    373 }
    374 
    375 func (f *FakeFile) Stat() (os.FileInfo, error) {
    376 	return f, nil
    377 }
    378 
    379 func (f *FakeFile) Read(p []byte) (int, error) {
    380 	if f.offset >= len(f.contents) {
    381 		return 0, io.EOF
    382 	}
    383 	n := copy(p, f.contents[f.offset:])
    384 	f.offset += n
    385 	return n, nil
    386 }
    387 
    388 func (f *FakeFile) Close() error {
    389 	return nil
    390 }
    391 
    392 // os.FileInfo methods.
    393 
    394 func (f *FakeFile) Size() int64 {
    395 	return int64(len(f.contents))
    396 }
    397 
    398 func (f *FakeFile) Mode() os.FileMode {
    399 	return f.mode
    400 }
    401 
    402 func (f *FakeFile) ModTime() time.Time {
    403 	return time.Time{}
    404 }
    405 
    406 func (f *FakeFile) IsDir() bool {
    407 	return false
    408 }
    409 
    410 func (f *FakeFile) Sys() interface{} {
    411 	return nil
    412 }
    413 
    414 // Special helpers.
    415 
    416 func (f *FakeFile) Entry() *Entry {
    417 	return &Entry{
    418 		name:  f.name,
    419 		mtime: 0, // Defined to be zero.
    420 		uid:   0, // Ditto.
    421 		gid:   0, // Ditto.
    422 		mode:  f.mode,
    423 		size:  int64(len(f.contents)),
    424 	}
    425 }
    426