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 	run("go", "build", "cmd/pack") // writes pack binary to dir
    222 	run("go", "tool", "compile", "hello.go")
    223 	run("./pack", "grc", "hello.a", "hello.o")
    224 	run("go", "tool", "link", "-o", "a.out", "hello.a")
    225 	out := run("./a.out")
    226 	if out != "hello world\n" {
    227 		t.Fatalf("incorrect output: %q, want %q", out, "hello world\n")
    228 	}
    229 }
    230 
    231 // Test that pack works with very long lines in PKGDEF.
    232 func TestLargeDefs(t *testing.T) {
    233 	testenv.MustHaveGoBuild(t)
    234 
    235 	dir := tmpDir(t)
    236 	defer os.RemoveAll(dir)
    237 	large := filepath.Join(dir, "large.go")
    238 	f, err := os.Create(large)
    239 	if err != nil {
    240 		t.Fatal(err)
    241 	}
    242 	b := bufio.NewWriter(f)
    243 
    244 	printf := func(format string, args ...interface{}) {
    245 		_, err := fmt.Fprintf(b, format, args...)
    246 		if err != nil {
    247 			t.Fatalf("Writing to %s: %v", large, err)
    248 		}
    249 	}
    250 
    251 	printf("package large\n\ntype T struct {\n")
    252 	for i := 0; i < 1000; i++ {
    253 		printf("f%d int `tag:\"", i)
    254 		for j := 0; j < 100; j++ {
    255 			printf("t%d=%d,", j, j)
    256 		}
    257 		printf("\"`\n")
    258 	}
    259 	printf("}\n")
    260 	if err = b.Flush(); err != nil {
    261 		t.Fatal(err)
    262 	}
    263 	if err = f.Close(); err != nil {
    264 		t.Fatal(err)
    265 	}
    266 
    267 	main := filepath.Join(dir, "main.go")
    268 	prog := `
    269 		package main
    270 		import "large"
    271 		var V large.T
    272 		func main() {
    273 			println("ok")
    274 		}
    275 	`
    276 	err = ioutil.WriteFile(main, []byte(prog), 0666)
    277 	if err != nil {
    278 		t.Fatal(err)
    279 	}
    280 
    281 	run := func(args ...string) string {
    282 		return doRun(t, dir, args...)
    283 	}
    284 
    285 	run("go", "build", "cmd/pack") // writes pack binary to dir
    286 	run("go", "tool", "compile", "large.go")
    287 	run("./pack", "grc", "large.a", "large.o")
    288 	run("go", "tool", "compile", "-I", ".", "main.go")
    289 	run("go", "tool", "link", "-L", ".", "-o", "a.out", "main.o")
    290 	out := run("./a.out")
    291 	if out != "ok\n" {
    292 		t.Fatalf("incorrect output: %q, want %q", out, "ok\n")
    293 	}
    294 }
    295 
    296 // doRun runs a program in a directory and returns the output.
    297 func doRun(t *testing.T, dir string, args ...string) string {
    298 	cmd := exec.Command(args[0], args[1:]...)
    299 	cmd.Dir = dir
    300 	out, err := cmd.CombinedOutput()
    301 	if err != nil {
    302 		t.Fatalf("%v: %v\n%s", args, err, string(out))
    303 	}
    304 	return string(out)
    305 }
    306 
    307 // Fake implementation of files.
    308 
    309 var helloFile = &FakeFile{
    310 	name:     "hello",
    311 	contents: "hello world", // 11 bytes, an odd number.
    312 	mode:     0644,
    313 }
    314 
    315 var goodbyeFile = &FakeFile{
    316 	name:     "goodbye",
    317 	contents: "Sayonara, Jim", // 13 bytes, another odd number.
    318 	mode:     0644,
    319 }
    320 
    321 // FakeFile implements FileLike and also os.FileInfo.
    322 type FakeFile struct {
    323 	name     string
    324 	contents string
    325 	mode     os.FileMode
    326 	offset   int
    327 }
    328 
    329 // Reset prepares a FakeFile for reuse.
    330 func (f *FakeFile) Reset() *FakeFile {
    331 	f.offset = 0
    332 	return f
    333 }
    334 
    335 // FileLike methods.
    336 
    337 func (f *FakeFile) Name() string {
    338 	// A bit of a cheat: we only have a basename, so that's also ok for FileInfo.
    339 	return f.name
    340 }
    341 
    342 func (f *FakeFile) Stat() (os.FileInfo, error) {
    343 	return f, nil
    344 }
    345 
    346 func (f *FakeFile) Read(p []byte) (int, error) {
    347 	if f.offset >= len(f.contents) {
    348 		return 0, io.EOF
    349 	}
    350 	n := copy(p, f.contents[f.offset:])
    351 	f.offset += n
    352 	return n, nil
    353 }
    354 
    355 func (f *FakeFile) Close() error {
    356 	return nil
    357 }
    358 
    359 // os.FileInfo methods.
    360 
    361 func (f *FakeFile) Size() int64 {
    362 	return int64(len(f.contents))
    363 }
    364 
    365 func (f *FakeFile) Mode() os.FileMode {
    366 	return f.mode
    367 }
    368 
    369 func (f *FakeFile) ModTime() time.Time {
    370 	return time.Time{}
    371 }
    372 
    373 func (f *FakeFile) IsDir() bool {
    374 	return false
    375 }
    376 
    377 func (f *FakeFile) Sys() interface{} {
    378 	return nil
    379 }
    380 
    381 // Special helpers.
    382 
    383 func (f *FakeFile) Entry() *Entry {
    384 	return &Entry{
    385 		name:  f.name,
    386 		mtime: 0, // Defined to be zero.
    387 		uid:   0, // Ditto.
    388 		gid:   0, // Ditto.
    389 		mode:  f.mode,
    390 		size:  int64(len(f.contents)),
    391 	}
    392 }
    393