github.com/varialus/godfly@v0.0.0-20130904042352-1934f9f095ab/src/pkg/archive/tar/writer_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 "fmt" 10 "io" 11 "io/ioutil" 12 "os" 13 "strings" 14 "testing" 15 "testing/iotest" 16 "time" 17 ) 18 19 type writerTestEntry struct { 20 header *Header 21 contents string 22 } 23 24 type writerTest struct { 25 file string // filename of expected output 26 entries []*writerTestEntry 27 } 28 29 var writerTests = []*writerTest{ 30 // The writer test file was produced with this command: 31 // tar (GNU tar) 1.26 32 // ln -s small.txt link.txt 33 // tar -b 1 --format=ustar -c -f writer.tar small.txt small2.txt link.txt 34 { 35 file: "testdata/writer.tar", 36 entries: []*writerTestEntry{ 37 { 38 header: &Header{ 39 Name: "small.txt", 40 Mode: 0640, 41 Uid: 73025, 42 Gid: 5000, 43 Size: 5, 44 ModTime: time.Unix(1246508266, 0), 45 Typeflag: '0', 46 Uname: "dsymonds", 47 Gname: "eng", 48 }, 49 contents: "Kilts", 50 }, 51 { 52 header: &Header{ 53 Name: "small2.txt", 54 Mode: 0640, 55 Uid: 73025, 56 Gid: 5000, 57 Size: 11, 58 ModTime: time.Unix(1245217492, 0), 59 Typeflag: '0', 60 Uname: "dsymonds", 61 Gname: "eng", 62 }, 63 contents: "Google.com\n", 64 }, 65 { 66 header: &Header{ 67 Name: "link.txt", 68 Mode: 0777, 69 Uid: 1000, 70 Gid: 1000, 71 Size: 0, 72 ModTime: time.Unix(1314603082, 0), 73 Typeflag: '2', 74 Linkname: "small.txt", 75 Uname: "strings", 76 Gname: "strings", 77 }, 78 // no contents 79 }, 80 }, 81 }, 82 // The truncated test file was produced using these commands: 83 // dd if=/dev/zero bs=1048576 count=16384 > /tmp/16gig.txt 84 // tar -b 1 -c -f- /tmp/16gig.txt | dd bs=512 count=8 > writer-big.tar 85 { 86 file: "testdata/writer-big.tar", 87 entries: []*writerTestEntry{ 88 { 89 header: &Header{ 90 Name: "tmp/16gig.txt", 91 Mode: 0640, 92 Uid: 73025, 93 Gid: 5000, 94 Size: 16 << 30, 95 ModTime: time.Unix(1254699560, 0), 96 Typeflag: '0', 97 Uname: "dsymonds", 98 Gname: "eng", 99 }, 100 // fake contents 101 contents: strings.Repeat("\x00", 4<<10), 102 }, 103 }, 104 }, 105 // This file was produced using gnu tar 1.17 106 // gnutar -b 4 --format=ustar (longname/)*15 + file.txt 107 { 108 file: "testdata/ustar.tar", 109 entries: []*writerTestEntry{ 110 { 111 header: &Header{ 112 Name: strings.Repeat("longname/", 15) + "file.txt", 113 Mode: 0644, 114 Uid: 0765, 115 Gid: 024, 116 Size: 06, 117 ModTime: time.Unix(1360135598, 0), 118 Typeflag: '0', 119 Uname: "shane", 120 Gname: "staff", 121 }, 122 contents: "hello\n", 123 }, 124 }, 125 }, 126 } 127 128 // Render byte array in a two-character hexadecimal string, spaced for easy visual inspection. 129 func bytestr(offset int, b []byte) string { 130 const rowLen = 32 131 s := fmt.Sprintf("%04x ", offset) 132 for _, ch := range b { 133 switch { 134 case '0' <= ch && ch <= '9', 'A' <= ch && ch <= 'Z', 'a' <= ch && ch <= 'z': 135 s += fmt.Sprintf(" %c", ch) 136 default: 137 s += fmt.Sprintf(" %02x", ch) 138 } 139 } 140 return s 141 } 142 143 // Render a pseudo-diff between two blocks of bytes. 144 func bytediff(a []byte, b []byte) string { 145 const rowLen = 32 146 s := fmt.Sprintf("(%d bytes vs. %d bytes)\n", len(a), len(b)) 147 for offset := 0; len(a)+len(b) > 0; offset += rowLen { 148 na, nb := rowLen, rowLen 149 if na > len(a) { 150 na = len(a) 151 } 152 if nb > len(b) { 153 nb = len(b) 154 } 155 sa := bytestr(offset, a[0:na]) 156 sb := bytestr(offset, b[0:nb]) 157 if sa != sb { 158 s += fmt.Sprintf("-%v\n+%v\n", sa, sb) 159 } 160 a = a[na:] 161 b = b[nb:] 162 } 163 return s 164 } 165 166 func TestWriter(t *testing.T) { 167 testLoop: 168 for i, test := range writerTests { 169 expected, err := ioutil.ReadFile(test.file) 170 if err != nil { 171 t.Errorf("test %d: Unexpected error: %v", i, err) 172 continue 173 } 174 175 buf := new(bytes.Buffer) 176 tw := NewWriter(iotest.TruncateWriter(buf, 4<<10)) // only catch the first 4 KB 177 big := false 178 for j, entry := range test.entries { 179 big = big || entry.header.Size > 1<<10 180 if err := tw.WriteHeader(entry.header); err != nil { 181 t.Errorf("test %d, entry %d: Failed writing header: %v", i, j, err) 182 continue testLoop 183 } 184 if _, err := io.WriteString(tw, entry.contents); err != nil { 185 t.Errorf("test %d, entry %d: Failed writing contents: %v", i, j, err) 186 continue testLoop 187 } 188 } 189 // Only interested in Close failures for the small tests. 190 if err := tw.Close(); err != nil && !big { 191 t.Errorf("test %d: Failed closing archive: %v", i, err) 192 continue testLoop 193 } 194 195 actual := buf.Bytes() 196 if !bytes.Equal(expected, actual) { 197 t.Errorf("test %d: Incorrect result: (-=expected, +=actual)\n%v", 198 i, bytediff(expected, actual)) 199 } 200 if testing.Short() { // The second test is expensive. 201 break 202 } 203 } 204 } 205 206 func TestPax(t *testing.T) { 207 // Create an archive with a large name 208 fileinfo, err := os.Stat("testdata/small.txt") 209 if err != nil { 210 t.Fatal(err) 211 } 212 hdr, err := FileInfoHeader(fileinfo, "") 213 if err != nil { 214 t.Fatalf("os.Stat: %v", err) 215 } 216 // Force a PAX long name to be written 217 longName := strings.Repeat("ab", 100) 218 contents := strings.Repeat(" ", int(hdr.Size)) 219 hdr.Name = longName 220 var buf bytes.Buffer 221 writer := NewWriter(&buf) 222 if err := writer.WriteHeader(hdr); err != nil { 223 t.Fatal(err) 224 } 225 if _, err = writer.Write([]byte(contents)); err != nil { 226 t.Fatal(err) 227 } 228 if err := writer.Close(); err != nil { 229 t.Fatal(err) 230 } 231 // Simple test to make sure PAX extensions are in effect 232 if !bytes.Contains(buf.Bytes(), []byte("PaxHeaders.")) { 233 t.Fatal("Expected at least one PAX header to be written.") 234 } 235 // Test that we can get a long name back out of the archive. 236 reader := NewReader(&buf) 237 hdr, err = reader.Next() 238 if err != nil { 239 t.Fatal(err) 240 } 241 if hdr.Name != longName { 242 t.Fatal("Couldn't recover long file name") 243 } 244 } 245 246 func TestPaxSymlink(t *testing.T) { 247 // Create an archive with a large linkname 248 fileinfo, err := os.Stat("testdata/small.txt") 249 if err != nil { 250 t.Fatal(err) 251 } 252 hdr, err := FileInfoHeader(fileinfo, "") 253 hdr.Typeflag = TypeSymlink 254 if err != nil { 255 t.Fatalf("os.Stat:1 %v", err) 256 } 257 // Force a PAX long linkname to be written 258 longLinkname := strings.Repeat("1234567890/1234567890", 10) 259 hdr.Linkname = longLinkname 260 261 hdr.Size = 0 262 var buf bytes.Buffer 263 writer := NewWriter(&buf) 264 if err := writer.WriteHeader(hdr); err != nil { 265 t.Fatal(err) 266 } 267 if err := writer.Close(); err != nil { 268 t.Fatal(err) 269 } 270 // Simple test to make sure PAX extensions are in effect 271 if !bytes.Contains(buf.Bytes(), []byte("PaxHeaders.")) { 272 t.Fatal("Expected at least one PAX header to be written.") 273 } 274 // Test that we can get a long name back out of the archive. 275 reader := NewReader(&buf) 276 hdr, err = reader.Next() 277 if err != nil { 278 t.Fatal(err) 279 } 280 if hdr.Linkname != longLinkname { 281 t.Fatal("Couldn't recover long link name") 282 } 283 } 284 285 func TestPaxNonAscii(t *testing.T) { 286 // Create an archive with non ascii. These should trigger a pax header 287 // because pax headers have a defined utf-8 encoding. 288 fileinfo, err := os.Stat("testdata/small.txt") 289 if err != nil { 290 t.Fatal(err) 291 } 292 293 hdr, err := FileInfoHeader(fileinfo, "") 294 if err != nil { 295 t.Fatalf("os.Stat:1 %v", err) 296 } 297 298 // some sample data 299 chineseFilename := "文件名" 300 chineseGroupname := "組" 301 chineseUsername := "用戶名" 302 303 hdr.Name = chineseFilename 304 hdr.Gname = chineseGroupname 305 hdr.Uname = chineseUsername 306 307 contents := strings.Repeat(" ", int(hdr.Size)) 308 309 var buf bytes.Buffer 310 writer := NewWriter(&buf) 311 if err := writer.WriteHeader(hdr); err != nil { 312 t.Fatal(err) 313 } 314 if _, err = writer.Write([]byte(contents)); err != nil { 315 t.Fatal(err) 316 } 317 if err := writer.Close(); err != nil { 318 t.Fatal(err) 319 } 320 // Simple test to make sure PAX extensions are in effect 321 if !bytes.Contains(buf.Bytes(), []byte("PaxHeaders.")) { 322 t.Fatal("Expected at least one PAX header to be written.") 323 } 324 // Test that we can get a long name back out of the archive. 325 reader := NewReader(&buf) 326 hdr, err = reader.Next() 327 if err != nil { 328 t.Fatal(err) 329 } 330 if hdr.Name != chineseFilename { 331 t.Fatal("Couldn't recover unicode name") 332 } 333 if hdr.Gname != chineseGroupname { 334 t.Fatal("Couldn't recover unicode group") 335 } 336 if hdr.Uname != chineseUsername { 337 t.Fatal("Couldn't recover unicode user") 338 } 339 } 340 341 func TestPAXHeader(t *testing.T) { 342 medName := strings.Repeat("CD", 50) 343 longName := strings.Repeat("AB", 100) 344 paxTests := [][2]string{ 345 {paxPath + "=/etc/hosts", "19 path=/etc/hosts\n"}, 346 {"a=b", "6 a=b\n"}, // Single digit length 347 {"a=names", "11 a=names\n"}, // Test case involving carries 348 {paxPath + "=" + longName, fmt.Sprintf("210 path=%s\n", longName)}, 349 {paxPath + "=" + medName, fmt.Sprintf("110 path=%s\n", medName)}} 350 351 for _, test := range paxTests { 352 key, expected := test[0], test[1] 353 if result := paxHeader(key); result != expected { 354 t.Fatalf("paxHeader: got %s, expected %s", result, expected) 355 } 356 } 357 }