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