github.com/varialus/godfly@v0.0.0-20130904042352-1934f9f095ab/src/pkg/archive/tar/reader_test.go (about) 1 // Copyright 2009 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 tar 6 7 import ( 8 "bytes" 9 "crypto/md5" 10 "fmt" 11 "io" 12 "os" 13 "reflect" 14 "strings" 15 "testing" 16 "time" 17 ) 18 19 type untarTest struct { 20 file string 21 headers []*Header 22 cksums []string 23 } 24 25 var gnuTarTest = &untarTest{ 26 file: "testdata/gnu.tar", 27 headers: []*Header{ 28 { 29 Name: "small.txt", 30 Mode: 0640, 31 Uid: 73025, 32 Gid: 5000, 33 Size: 5, 34 ModTime: time.Unix(1244428340, 0), 35 Typeflag: '0', 36 Uname: "dsymonds", 37 Gname: "eng", 38 }, 39 { 40 Name: "small2.txt", 41 Mode: 0640, 42 Uid: 73025, 43 Gid: 5000, 44 Size: 11, 45 ModTime: time.Unix(1244436044, 0), 46 Typeflag: '0', 47 Uname: "dsymonds", 48 Gname: "eng", 49 }, 50 }, 51 cksums: []string{ 52 "e38b27eaccb4391bdec553a7f3ae6b2f", 53 "c65bd2e50a56a2138bf1716f2fd56fe9", 54 }, 55 } 56 57 var untarTests = []*untarTest{ 58 gnuTarTest, 59 { 60 file: "testdata/star.tar", 61 headers: []*Header{ 62 { 63 Name: "small.txt", 64 Mode: 0640, 65 Uid: 73025, 66 Gid: 5000, 67 Size: 5, 68 ModTime: time.Unix(1244592783, 0), 69 Typeflag: '0', 70 Uname: "dsymonds", 71 Gname: "eng", 72 AccessTime: time.Unix(1244592783, 0), 73 ChangeTime: time.Unix(1244592783, 0), 74 }, 75 { 76 Name: "small2.txt", 77 Mode: 0640, 78 Uid: 73025, 79 Gid: 5000, 80 Size: 11, 81 ModTime: time.Unix(1244592783, 0), 82 Typeflag: '0', 83 Uname: "dsymonds", 84 Gname: "eng", 85 AccessTime: time.Unix(1244592783, 0), 86 ChangeTime: time.Unix(1244592783, 0), 87 }, 88 }, 89 }, 90 { 91 file: "testdata/v7.tar", 92 headers: []*Header{ 93 { 94 Name: "small.txt", 95 Mode: 0444, 96 Uid: 73025, 97 Gid: 5000, 98 Size: 5, 99 ModTime: time.Unix(1244593104, 0), 100 Typeflag: '\x00', 101 }, 102 { 103 Name: "small2.txt", 104 Mode: 0444, 105 Uid: 73025, 106 Gid: 5000, 107 Size: 11, 108 ModTime: time.Unix(1244593104, 0), 109 Typeflag: '\x00', 110 }, 111 }, 112 }, 113 { 114 file: "testdata/pax.tar", 115 headers: []*Header{ 116 { 117 Name: "a/123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100", 118 Mode: 0664, 119 Uid: 1000, 120 Gid: 1000, 121 Uname: "shane", 122 Gname: "shane", 123 Size: 7, 124 ModTime: time.Unix(1350244992, 23960108), 125 ChangeTime: time.Unix(1350244992, 23960108), 126 AccessTime: time.Unix(1350244992, 23960108), 127 Typeflag: TypeReg, 128 }, 129 { 130 Name: "a/b", 131 Mode: 0777, 132 Uid: 1000, 133 Gid: 1000, 134 Uname: "shane", 135 Gname: "shane", 136 Size: 0, 137 ModTime: time.Unix(1350266320, 910238425), 138 ChangeTime: time.Unix(1350266320, 910238425), 139 AccessTime: time.Unix(1350266320, 910238425), 140 Typeflag: TypeSymlink, 141 Linkname: "123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100", 142 }, 143 }, 144 }, 145 { 146 file: "testdata/nil-uid.tar", // golang.org/issue/5290 147 headers: []*Header{ 148 { 149 Name: "P1050238.JPG.log", 150 Mode: 0664, 151 Uid: 0, 152 Gid: 0, 153 Size: 14, 154 ModTime: time.Unix(1365454838, 0), 155 Typeflag: TypeReg, 156 Linkname: "", 157 Uname: "eyefi", 158 Gname: "eyefi", 159 Devmajor: 0, 160 Devminor: 0, 161 }, 162 }, 163 }, 164 } 165 166 func TestReader(t *testing.T) { 167 testLoop: 168 for i, test := range untarTests { 169 f, err := os.Open(test.file) 170 if err != nil { 171 t.Errorf("test %d: Unexpected error: %v", i, err) 172 continue 173 } 174 defer f.Close() 175 tr := NewReader(f) 176 for j, header := range test.headers { 177 hdr, err := tr.Next() 178 if err != nil || hdr == nil { 179 t.Errorf("test %d, entry %d: Didn't get entry: %v", i, j, err) 180 f.Close() 181 continue testLoop 182 } 183 if *hdr != *header { 184 t.Errorf("test %d, entry %d: Incorrect header:\nhave %+v\nwant %+v", 185 i, j, *hdr, *header) 186 } 187 } 188 hdr, err := tr.Next() 189 if err == io.EOF { 190 continue testLoop 191 } 192 if hdr != nil || err != nil { 193 t.Errorf("test %d: Unexpected entry or error: hdr=%v err=%v", i, hdr, err) 194 } 195 } 196 } 197 198 func TestPartialRead(t *testing.T) { 199 f, err := os.Open("testdata/gnu.tar") 200 if err != nil { 201 t.Fatalf("Unexpected error: %v", err) 202 } 203 defer f.Close() 204 205 tr := NewReader(f) 206 207 // Read the first four bytes; Next() should skip the last byte. 208 hdr, err := tr.Next() 209 if err != nil || hdr == nil { 210 t.Fatalf("Didn't get first file: %v", err) 211 } 212 buf := make([]byte, 4) 213 if _, err := io.ReadFull(tr, buf); err != nil { 214 t.Fatalf("Unexpected error: %v", err) 215 } 216 if expected := []byte("Kilt"); !bytes.Equal(buf, expected) { 217 t.Errorf("Contents = %v, want %v", buf, expected) 218 } 219 220 // Second file 221 hdr, err = tr.Next() 222 if err != nil || hdr == nil { 223 t.Fatalf("Didn't get second file: %v", err) 224 } 225 buf = make([]byte, 6) 226 if _, err := io.ReadFull(tr, buf); err != nil { 227 t.Fatalf("Unexpected error: %v", err) 228 } 229 if expected := []byte("Google"); !bytes.Equal(buf, expected) { 230 t.Errorf("Contents = %v, want %v", buf, expected) 231 } 232 } 233 234 func TestIncrementalRead(t *testing.T) { 235 test := gnuTarTest 236 f, err := os.Open(test.file) 237 if err != nil { 238 t.Fatalf("Unexpected error: %v", err) 239 } 240 defer f.Close() 241 242 tr := NewReader(f) 243 244 headers := test.headers 245 cksums := test.cksums 246 nread := 0 247 248 // loop over all files 249 for ; ; nread++ { 250 hdr, err := tr.Next() 251 if hdr == nil || err == io.EOF { 252 break 253 } 254 255 // check the header 256 if *hdr != *headers[nread] { 257 t.Errorf("Incorrect header:\nhave %+v\nwant %+v", 258 *hdr, headers[nread]) 259 } 260 261 // read file contents in little chunks EOF, 262 // checksumming all the way 263 h := md5.New() 264 rdbuf := make([]uint8, 8) 265 for { 266 nr, err := tr.Read(rdbuf) 267 if err == io.EOF { 268 break 269 } 270 if err != nil { 271 t.Errorf("Read: unexpected error %v\n", err) 272 break 273 } 274 h.Write(rdbuf[0:nr]) 275 } 276 // verify checksum 277 have := fmt.Sprintf("%x", h.Sum(nil)) 278 want := cksums[nread] 279 if want != have { 280 t.Errorf("Bad checksum on file %s:\nhave %+v\nwant %+v", hdr.Name, have, want) 281 } 282 } 283 if nread != len(headers) { 284 t.Errorf("Didn't process all files\nexpected: %d\nprocessed %d\n", len(headers), nread) 285 } 286 } 287 288 func TestNonSeekable(t *testing.T) { 289 test := gnuTarTest 290 f, err := os.Open(test.file) 291 if err != nil { 292 t.Fatalf("Unexpected error: %v", err) 293 } 294 defer f.Close() 295 296 type readerOnly struct { 297 io.Reader 298 } 299 tr := NewReader(readerOnly{f}) 300 nread := 0 301 302 for ; ; nread++ { 303 _, err := tr.Next() 304 if err == io.EOF { 305 break 306 } 307 if err != nil { 308 t.Fatalf("Unexpected error: %v", err) 309 } 310 } 311 312 if nread != len(test.headers) { 313 t.Errorf("Didn't process all files\nexpected: %d\nprocessed %d\n", len(test.headers), nread) 314 } 315 } 316 317 func TestParsePAXHeader(t *testing.T) { 318 paxTests := [][3]string{ 319 {"a", "a=name", "10 a=name\n"}, // Test case involving multiple acceptable lengths 320 {"a", "a=name", "9 a=name\n"}, // Test case involving multiple acceptable length 321 {"mtime", "mtime=1350244992.023960108", "30 mtime=1350244992.023960108\n"}} 322 for _, test := range paxTests { 323 key, expected, raw := test[0], test[1], test[2] 324 reader := bytes.NewBuffer([]byte(raw)) 325 headers, err := parsePAX(reader) 326 if err != nil { 327 t.Errorf("Couldn't parse correctly formatted headers: %v", err) 328 continue 329 } 330 if strings.EqualFold(headers[key], expected) { 331 t.Errorf("mtime header incorrectly parsed: got %s, wanted %s", headers[key], expected) 332 continue 333 } 334 trailer := make([]byte, 100) 335 n, err := reader.Read(trailer) 336 if err != io.EOF || n != 0 { 337 t.Error("Buffer wasn't consumed") 338 } 339 } 340 badHeader := bytes.NewBuffer([]byte("3 somelongkey=")) 341 if _, err := parsePAX(badHeader); err != ErrHeader { 342 t.Fatal("Unexpected success when parsing bad header") 343 } 344 } 345 346 func TestParsePAXTime(t *testing.T) { 347 // Some valid PAX time values 348 timestamps := map[string]time.Time{ 349 "1350244992.023960108": time.Unix(1350244992, 23960108), // The commoon case 350 "1350244992.02396010": time.Unix(1350244992, 23960100), // Lower precision value 351 "1350244992.0239601089": time.Unix(1350244992, 23960108), // Higher precision value 352 "1350244992": time.Unix(1350244992, 0), // Low precision value 353 } 354 for input, expected := range timestamps { 355 ts, err := parsePAXTime(input) 356 if err != nil { 357 t.Fatal(err) 358 } 359 if !ts.Equal(expected) { 360 t.Fatalf("Time parsing failure %s %s", ts, expected) 361 } 362 } 363 } 364 365 func TestMergePAX(t *testing.T) { 366 hdr := new(Header) 367 // Test a string, integer, and time based value. 368 headers := map[string]string{ 369 "path": "a/b/c", 370 "uid": "1000", 371 "mtime": "1350244992.023960108", 372 } 373 err := mergePAX(hdr, headers) 374 if err != nil { 375 t.Fatal(err) 376 } 377 want := &Header{ 378 Name: "a/b/c", 379 Uid: 1000, 380 ModTime: time.Unix(1350244992, 23960108), 381 } 382 if !reflect.DeepEqual(hdr, want) { 383 t.Errorf("incorrect merge: got %+v, want %+v", hdr, want) 384 } 385 }