github.com/xushiwei/go@v0.0.0-20130601165731-2b9d83f45bc9/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 147 func TestReader(t *testing.T) { 148 testLoop: 149 for i, test := range untarTests { 150 f, err := os.Open(test.file) 151 if err != nil { 152 t.Errorf("test %d: Unexpected error: %v", i, err) 153 continue 154 } 155 tr := NewReader(f) 156 for j, header := range test.headers { 157 hdr, err := tr.Next() 158 if err != nil || hdr == nil { 159 t.Errorf("test %d, entry %d: Didn't get entry: %v", i, j, err) 160 f.Close() 161 continue testLoop 162 } 163 if *hdr != *header { 164 t.Errorf("test %d, entry %d: Incorrect header:\nhave %+v\nwant %+v", 165 i, j, *hdr, *header) 166 } 167 } 168 hdr, err := tr.Next() 169 if err == io.EOF { 170 continue testLoop 171 } 172 if hdr != nil || err != nil { 173 t.Errorf("test %d: Unexpected entry or error: hdr=%v err=%v", i, hdr, err) 174 } 175 f.Close() 176 } 177 } 178 179 func TestPartialRead(t *testing.T) { 180 f, err := os.Open("testdata/gnu.tar") 181 if err != nil { 182 t.Fatalf("Unexpected error: %v", err) 183 } 184 defer f.Close() 185 186 tr := NewReader(f) 187 188 // Read the first four bytes; Next() should skip the last byte. 189 hdr, err := tr.Next() 190 if err != nil || hdr == nil { 191 t.Fatalf("Didn't get first file: %v", err) 192 } 193 buf := make([]byte, 4) 194 if _, err := io.ReadFull(tr, buf); err != nil { 195 t.Fatalf("Unexpected error: %v", err) 196 } 197 if expected := []byte("Kilt"); !bytes.Equal(buf, expected) { 198 t.Errorf("Contents = %v, want %v", buf, expected) 199 } 200 201 // Second file 202 hdr, err = tr.Next() 203 if err != nil || hdr == nil { 204 t.Fatalf("Didn't get second file: %v", err) 205 } 206 buf = make([]byte, 6) 207 if _, err := io.ReadFull(tr, buf); err != nil { 208 t.Fatalf("Unexpected error: %v", err) 209 } 210 if expected := []byte("Google"); !bytes.Equal(buf, expected) { 211 t.Errorf("Contents = %v, want %v", buf, expected) 212 } 213 } 214 215 func TestIncrementalRead(t *testing.T) { 216 test := gnuTarTest 217 f, err := os.Open(test.file) 218 if err != nil { 219 t.Fatalf("Unexpected error: %v", err) 220 } 221 defer f.Close() 222 223 tr := NewReader(f) 224 225 headers := test.headers 226 cksums := test.cksums 227 nread := 0 228 229 // loop over all files 230 for ; ; nread++ { 231 hdr, err := tr.Next() 232 if hdr == nil || err == io.EOF { 233 break 234 } 235 236 // check the header 237 if *hdr != *headers[nread] { 238 t.Errorf("Incorrect header:\nhave %+v\nwant %+v", 239 *hdr, headers[nread]) 240 } 241 242 // read file contents in little chunks EOF, 243 // checksumming all the way 244 h := md5.New() 245 rdbuf := make([]uint8, 8) 246 for { 247 nr, err := tr.Read(rdbuf) 248 if err == io.EOF { 249 break 250 } 251 if err != nil { 252 t.Errorf("Read: unexpected error %v\n", err) 253 break 254 } 255 h.Write(rdbuf[0:nr]) 256 } 257 // verify checksum 258 have := fmt.Sprintf("%x", h.Sum(nil)) 259 want := cksums[nread] 260 if want != have { 261 t.Errorf("Bad checksum on file %s:\nhave %+v\nwant %+v", hdr.Name, have, want) 262 } 263 } 264 if nread != len(headers) { 265 t.Errorf("Didn't process all files\nexpected: %d\nprocessed %d\n", len(headers), nread) 266 } 267 } 268 269 func TestNonSeekable(t *testing.T) { 270 test := gnuTarTest 271 f, err := os.Open(test.file) 272 if err != nil { 273 t.Fatalf("Unexpected error: %v", err) 274 } 275 defer f.Close() 276 277 type readerOnly struct { 278 io.Reader 279 } 280 tr := NewReader(readerOnly{f}) 281 nread := 0 282 283 for ; ; nread++ { 284 _, err := tr.Next() 285 if err == io.EOF { 286 break 287 } 288 if err != nil { 289 t.Fatalf("Unexpected error: %v", err) 290 } 291 } 292 293 if nread != len(test.headers) { 294 t.Errorf("Didn't process all files\nexpected: %d\nprocessed %d\n", len(test.headers), nread) 295 } 296 } 297 298 func TestParsePAXHeader(t *testing.T) { 299 paxTests := [][3]string{ 300 {"a", "a=name", "10 a=name\n"}, // Test case involving multiple acceptable lengths 301 {"a", "a=name", "9 a=name\n"}, // Test case involving multiple acceptable length 302 {"mtime", "mtime=1350244992.023960108", "30 mtime=1350244992.023960108\n"}} 303 for _, test := range paxTests { 304 key, expected, raw := test[0], test[1], test[2] 305 reader := bytes.NewBuffer([]byte(raw)) 306 headers, err := parsePAX(reader) 307 if err != nil { 308 t.Errorf("Couldn't parse correctly formatted headers: %v", err) 309 continue 310 } 311 if strings.EqualFold(headers[key], expected) { 312 t.Errorf("mtime header incorrectly parsed: got %s, wanted %s", headers[key], expected) 313 continue 314 } 315 trailer := make([]byte, 100) 316 n, err := reader.Read(trailer) 317 if err != io.EOF || n != 0 { 318 t.Error("Buffer wasn't consumed") 319 } 320 } 321 badHeader := bytes.NewBuffer([]byte("3 somelongkey=")) 322 if _, err := parsePAX(badHeader); err != ErrHeader { 323 t.Fatal("Unexpected success when parsing bad header") 324 } 325 } 326 327 func TestParsePAXTime(t *testing.T) { 328 // Some valid PAX time values 329 timestamps := map[string]time.Time{ 330 "1350244992.023960108": time.Unix(1350244992, 23960108), // The commoon case 331 "1350244992.02396010": time.Unix(1350244992, 23960100), // Lower precision value 332 "1350244992.0239601089": time.Unix(1350244992, 23960108), // Higher precision value 333 "1350244992": time.Unix(1350244992, 0), // Low precision value 334 } 335 for input, expected := range timestamps { 336 ts, err := parsePAXTime(input) 337 if err != nil { 338 t.Fatal(err) 339 } 340 if !ts.Equal(expected) { 341 t.Fatalf("Time parsing failure %s %s", ts, expected) 342 } 343 } 344 } 345 346 func TestMergePAX(t *testing.T) { 347 hdr := new(Header) 348 // Test a string, integer, and time based value. 349 headers := map[string]string{ 350 "path": "a/b/c", 351 "uid": "1000", 352 "mtime": "1350244992.023960108", 353 } 354 err := mergePAX(hdr, headers) 355 if err != nil { 356 t.Fatal(err) 357 } 358 want := &Header{ 359 Name: "a/b/c", 360 Uid: 1000, 361 ModTime: time.Unix(1350244992, 23960108), 362 } 363 if !reflect.DeepEqual(hdr, want) { 364 t.Errorf("incorrect merge: got %+v, want %+v", hdr, want) 365 } 366 }