...

Source file src/archive/tar/writer_test.go

Documentation: archive/tar

		 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  	"encoding/hex"
		10  	"errors"
		11  	"io"
		12  	"os"
		13  	"path"
		14  	"reflect"
		15  	"sort"
		16  	"strings"
		17  	"testing"
		18  	"testing/iotest"
		19  	"time"
		20  )
		21  
		22  func bytediff(a, b []byte) string {
		23  	const (
		24  		uniqueA	= "-	"
		25  		uniqueB	= "+	"
		26  		identity = "	 "
		27  	)
		28  	var ss []string
		29  	sa := strings.Split(strings.TrimSpace(hex.Dump(a)), "\n")
		30  	sb := strings.Split(strings.TrimSpace(hex.Dump(b)), "\n")
		31  	for len(sa) > 0 && len(sb) > 0 {
		32  		if sa[0] == sb[0] {
		33  			ss = append(ss, identity+sa[0])
		34  		} else {
		35  			ss = append(ss, uniqueA+sa[0])
		36  			ss = append(ss, uniqueB+sb[0])
		37  		}
		38  		sa, sb = sa[1:], sb[1:]
		39  	}
		40  	for len(sa) > 0 {
		41  		ss = append(ss, uniqueA+sa[0])
		42  		sa = sa[1:]
		43  	}
		44  	for len(sb) > 0 {
		45  		ss = append(ss, uniqueB+sb[0])
		46  		sb = sb[1:]
		47  	}
		48  	return strings.Join(ss, "\n")
		49  }
		50  
		51  func TestWriter(t *testing.T) {
		52  	type (
		53  		testHeader struct { // WriteHeader(hdr) == wantErr
		54  			hdr		 Header
		55  			wantErr error
		56  		}
		57  		testWrite struct { // Write(str) == (wantCnt, wantErr)
		58  			str		 string
		59  			wantCnt int
		60  			wantErr error
		61  		}
		62  		testReadFrom struct { // ReadFrom(testFile{ops}) == (wantCnt, wantErr)
		63  			ops		 fileOps
		64  			wantCnt int64
		65  			wantErr error
		66  		}
		67  		testClose struct { // Close() == wantErr
		68  			wantErr error
		69  		}
		70  		testFnc interface{} // testHeader | testWrite | testReadFrom | testClose
		71  	)
		72  
		73  	vectors := []struct {
		74  		file	string // Optional filename of expected output
		75  		tests []testFnc
		76  	}{{
		77  		// The writer test file was produced with this command:
		78  		// tar (GNU tar) 1.26
		79  		//	 ln -s small.txt link.txt
		80  		//	 tar -b 1 --format=ustar -c -f writer.tar small.txt small2.txt link.txt
		81  		file: "testdata/writer.tar",
		82  		tests: []testFnc{
		83  			testHeader{Header{
		84  				Typeflag: TypeReg,
		85  				Name:		 "small.txt",
		86  				Size:		 5,
		87  				Mode:		 0640,
		88  				Uid:			73025,
		89  				Gid:			5000,
		90  				Uname:		"dsymonds",
		91  				Gname:		"eng",
		92  				ModTime:	time.Unix(1246508266, 0),
		93  			}, nil},
		94  			testWrite{"Kilts", 5, nil},
		95  
		96  			testHeader{Header{
		97  				Typeflag: TypeReg,
		98  				Name:		 "small2.txt",
		99  				Size:		 11,
	 100  				Mode:		 0640,
	 101  				Uid:			73025,
	 102  				Uname:		"dsymonds",
	 103  				Gname:		"eng",
	 104  				Gid:			5000,
	 105  				ModTime:	time.Unix(1245217492, 0),
	 106  			}, nil},
	 107  			testWrite{"Google.com\n", 11, nil},
	 108  
	 109  			testHeader{Header{
	 110  				Typeflag: TypeSymlink,
	 111  				Name:		 "link.txt",
	 112  				Linkname: "small.txt",
	 113  				Mode:		 0777,
	 114  				Uid:			1000,
	 115  				Gid:			1000,
	 116  				Uname:		"strings",
	 117  				Gname:		"strings",
	 118  				ModTime:	time.Unix(1314603082, 0),
	 119  			}, nil},
	 120  			testWrite{"", 0, nil},
	 121  
	 122  			testClose{nil},
	 123  		},
	 124  	}, {
	 125  		// The truncated test file was produced using these commands:
	 126  		//	 dd if=/dev/zero bs=1048576 count=16384 > /tmp/16gig.txt
	 127  		//	 tar -b 1 -c -f- /tmp/16gig.txt | dd bs=512 count=8 > writer-big.tar
	 128  		file: "testdata/writer-big.tar",
	 129  		tests: []testFnc{
	 130  			testHeader{Header{
	 131  				Typeflag: TypeReg,
	 132  				Name:		 "tmp/16gig.txt",
	 133  				Size:		 16 << 30,
	 134  				Mode:		 0640,
	 135  				Uid:			73025,
	 136  				Gid:			5000,
	 137  				Uname:		"dsymonds",
	 138  				Gname:		"eng",
	 139  				ModTime:	time.Unix(1254699560, 0),
	 140  				Format:	 FormatGNU,
	 141  			}, nil},
	 142  		},
	 143  	}, {
	 144  		// This truncated file was produced using this library.
	 145  		// It was verified to work with GNU tar 1.27.1 and BSD tar 3.1.2.
	 146  		//	dd if=/dev/zero bs=1G count=16 >> writer-big-long.tar
	 147  		//	gnutar -xvf writer-big-long.tar
	 148  		//	bsdtar -xvf writer-big-long.tar
	 149  		//
	 150  		// This file is in PAX format.
	 151  		file: "testdata/writer-big-long.tar",
	 152  		tests: []testFnc{
	 153  			testHeader{Header{
	 154  				Typeflag: TypeReg,
	 155  				Name:		 strings.Repeat("longname/", 15) + "16gig.txt",
	 156  				Size:		 16 << 30,
	 157  				Mode:		 0644,
	 158  				Uid:			1000,
	 159  				Gid:			1000,
	 160  				Uname:		"guillaume",
	 161  				Gname:		"guillaume",
	 162  				ModTime:	time.Unix(1399583047, 0),
	 163  			}, nil},
	 164  		},
	 165  	}, {
	 166  		// This file was produced using GNU tar v1.17.
	 167  		//	gnutar -b 4 --format=ustar (longname/)*15 + file.txt
	 168  		file: "testdata/ustar.tar",
	 169  		tests: []testFnc{
	 170  			testHeader{Header{
	 171  				Typeflag: TypeReg,
	 172  				Name:		 strings.Repeat("longname/", 15) + "file.txt",
	 173  				Size:		 6,
	 174  				Mode:		 0644,
	 175  				Uid:			501,
	 176  				Gid:			20,
	 177  				Uname:		"shane",
	 178  				Gname:		"staff",
	 179  				ModTime:	time.Unix(1360135598, 0),
	 180  			}, nil},
	 181  			testWrite{"hello\n", 6, nil},
	 182  			testClose{nil},
	 183  		},
	 184  	}, {
	 185  		// This file was produced using GNU tar v1.26:
	 186  		//	echo "Slartibartfast" > file.txt
	 187  		//	ln file.txt hard.txt
	 188  		//	tar -b 1 --format=ustar -c -f hardlink.tar file.txt hard.txt
	 189  		file: "testdata/hardlink.tar",
	 190  		tests: []testFnc{
	 191  			testHeader{Header{
	 192  				Typeflag: TypeReg,
	 193  				Name:		 "file.txt",
	 194  				Size:		 15,
	 195  				Mode:		 0644,
	 196  				Uid:			1000,
	 197  				Gid:			100,
	 198  				Uname:		"vbatts",
	 199  				Gname:		"users",
	 200  				ModTime:	time.Unix(1425484303, 0),
	 201  			}, nil},
	 202  			testWrite{"Slartibartfast\n", 15, nil},
	 203  
	 204  			testHeader{Header{
	 205  				Typeflag: TypeLink,
	 206  				Name:		 "hard.txt",
	 207  				Linkname: "file.txt",
	 208  				Mode:		 0644,
	 209  				Uid:			1000,
	 210  				Gid:			100,
	 211  				Uname:		"vbatts",
	 212  				Gname:		"users",
	 213  				ModTime:	time.Unix(1425484303, 0),
	 214  			}, nil},
	 215  			testWrite{"", 0, nil},
	 216  
	 217  			testClose{nil},
	 218  		},
	 219  	}, {
	 220  		tests: []testFnc{
	 221  			testHeader{Header{
	 222  				Typeflag: TypeReg,
	 223  				Name:		 "bad-null.txt",
	 224  				Xattrs:	 map[string]string{"null\x00null\x00": "fizzbuzz"},
	 225  			}, headerError{}},
	 226  		},
	 227  	}, {
	 228  		tests: []testFnc{
	 229  			testHeader{Header{
	 230  				Typeflag: TypeReg,
	 231  				Name:		 "null\x00.txt",
	 232  			}, headerError{}},
	 233  		},
	 234  	}, {
	 235  		file: "testdata/pax-records.tar",
	 236  		tests: []testFnc{
	 237  			testHeader{Header{
	 238  				Typeflag: TypeReg,
	 239  				Name:		 "file",
	 240  				Uname:		strings.Repeat("long", 10),
	 241  				PAXRecords: map[string]string{
	 242  					"path":					 "FILE", // Should be ignored
	 243  					"GNU.sparse.map": "0,0",	// Should be ignored
	 244  					"comment":				"Hello, 世界",
	 245  					"GOLANG.pkg":		 "tar",
	 246  				},
	 247  			}, nil},
	 248  			testClose{nil},
	 249  		},
	 250  	}, {
	 251  		// Craft a theoretically valid PAX archive with global headers.
	 252  		// The GNU and BSD tar tools do not parse these the same way.
	 253  		//
	 254  		// BSD tar v3.1.2 parses and ignores all global headers;
	 255  		// the behavior is verified by researching the source code.
	 256  		//
	 257  		//	$ bsdtar -tvf pax-global-records.tar
	 258  		//	----------	0 0			0					 0 Dec 31	1969 file1
	 259  		//	----------	0 0			0					 0 Dec 31	1969 file2
	 260  		//	----------	0 0			0					 0 Dec 31	1969 file3
	 261  		//	----------	0 0			0					 0 May 13	2014 file4
	 262  		//
	 263  		// GNU tar v1.27.1 applies global headers to subsequent records,
	 264  		// but does not do the following properly:
	 265  		//	* It does not treat an empty record as deletion.
	 266  		//	* It does not use subsequent global headers to update previous ones.
	 267  		//
	 268  		//	$ gnutar -tvf pax-global-records.tar
	 269  		//	---------- 0/0							 0 2017-07-13 19:40 global1
	 270  		//	---------- 0/0							 0 2017-07-13 19:40 file2
	 271  		//	gnutar: Substituting `.' for empty member name
	 272  		//	---------- 0/0							 0 1969-12-31 16:00
	 273  		//	gnutar: Substituting `.' for empty member name
	 274  		//	---------- 0/0							 0 2014-05-13 09:53
	 275  		//
	 276  		// According to the PAX specification, this should have been the result:
	 277  		//	---------- 0/0							 0 2017-07-13 19:40 global1
	 278  		//	---------- 0/0							 0 2017-07-13 19:40 file2
	 279  		//	---------- 0/0							 0 2017-07-13 19:40 file3
	 280  		//	---------- 0/0							 0 2014-05-13 09:53 file4
	 281  		file: "testdata/pax-global-records.tar",
	 282  		tests: []testFnc{
	 283  			testHeader{Header{
	 284  				Typeflag:	 TypeXGlobalHeader,
	 285  				PAXRecords: map[string]string{"path": "global1", "mtime": "1500000000.0"},
	 286  			}, nil},
	 287  			testHeader{Header{
	 288  				Typeflag: TypeReg, Name: "file1",
	 289  			}, nil},
	 290  			testHeader{Header{
	 291  				Typeflag:	 TypeReg,
	 292  				Name:			 "file2",
	 293  				PAXRecords: map[string]string{"path": "file2"},
	 294  			}, nil},
	 295  			testHeader{Header{
	 296  				Typeflag:	 TypeXGlobalHeader,
	 297  				PAXRecords: map[string]string{"path": ""}, // Should delete "path", but keep "mtime"
	 298  			}, nil},
	 299  			testHeader{Header{
	 300  				Typeflag: TypeReg, Name: "file3",
	 301  			}, nil},
	 302  			testHeader{Header{
	 303  				Typeflag:	 TypeReg,
	 304  				Name:			 "file4",
	 305  				ModTime:		time.Unix(1400000000, 0),
	 306  				PAXRecords: map[string]string{"mtime": "1400000000"},
	 307  			}, nil},
	 308  			testClose{nil},
	 309  		},
	 310  	}, {
	 311  		file: "testdata/gnu-utf8.tar",
	 312  		tests: []testFnc{
	 313  			testHeader{Header{
	 314  				Typeflag: TypeReg,
	 315  				Name:		 "☺☻☹☺☻☹☺☻☹☺☻☹☺☻☹☺☻☹☺☻☹☺☻☹☺☻☹☺☻☹☺☻☹☺☻☹☺☻☹☺☻☹☺☻☹☺☻☹☺☻☹☺☻☹",
	 316  				Mode:		 0644,
	 317  				Uid:			1000, Gid: 1000,
	 318  				Uname:	 "☺",
	 319  				Gname:	 "⚹",
	 320  				ModTime: time.Unix(0, 0),
	 321  				Format:	FormatGNU,
	 322  			}, nil},
	 323  			testClose{nil},
	 324  		},
	 325  	}, {
	 326  		file: "testdata/gnu-not-utf8.tar",
	 327  		tests: []testFnc{
	 328  			testHeader{Header{
	 329  				Typeflag: TypeReg,
	 330  				Name:		 "hi\x80\x81\x82\x83bye",
	 331  				Mode:		 0644,
	 332  				Uid:			1000,
	 333  				Gid:			1000,
	 334  				Uname:		"rawr",
	 335  				Gname:		"dsnet",
	 336  				ModTime:	time.Unix(0, 0),
	 337  				Format:	 FormatGNU,
	 338  			}, nil},
	 339  			testClose{nil},
	 340  		},
	 341  		// TODO(dsnet): Re-enable this test when adding sparse support.
	 342  		// See https://golang.org/issue/22735
	 343  		/*
	 344  			}, {
	 345  				file: "testdata/gnu-nil-sparse-data.tar",
	 346  				tests: []testFnc{
	 347  					testHeader{Header{
	 348  						Typeflag:		TypeGNUSparse,
	 349  						Name:				"sparse.db",
	 350  						Size:				1000,
	 351  						SparseHoles: []sparseEntry{{Offset: 1000, Length: 0}},
	 352  					}, nil},
	 353  					testWrite{strings.Repeat("0123456789", 100), 1000, nil},
	 354  					testClose{},
	 355  				},
	 356  			}, {
	 357  				file: "testdata/gnu-nil-sparse-hole.tar",
	 358  				tests: []testFnc{
	 359  					testHeader{Header{
	 360  						Typeflag:		TypeGNUSparse,
	 361  						Name:				"sparse.db",
	 362  						Size:				1000,
	 363  						SparseHoles: []sparseEntry{{Offset: 0, Length: 1000}},
	 364  					}, nil},
	 365  					testWrite{strings.Repeat("\x00", 1000), 1000, nil},
	 366  					testClose{},
	 367  				},
	 368  			}, {
	 369  				file: "testdata/pax-nil-sparse-data.tar",
	 370  				tests: []testFnc{
	 371  					testHeader{Header{
	 372  						Typeflag:		TypeReg,
	 373  						Name:				"sparse.db",
	 374  						Size:				1000,
	 375  						SparseHoles: []sparseEntry{{Offset: 1000, Length: 0}},
	 376  					}, nil},
	 377  					testWrite{strings.Repeat("0123456789", 100), 1000, nil},
	 378  					testClose{},
	 379  				},
	 380  			}, {
	 381  				file: "testdata/pax-nil-sparse-hole.tar",
	 382  				tests: []testFnc{
	 383  					testHeader{Header{
	 384  						Typeflag:		TypeReg,
	 385  						Name:				"sparse.db",
	 386  						Size:				1000,
	 387  						SparseHoles: []sparseEntry{{Offset: 0, Length: 1000}},
	 388  					}, nil},
	 389  					testWrite{strings.Repeat("\x00", 1000), 1000, nil},
	 390  					testClose{},
	 391  				},
	 392  			}, {
	 393  				file: "testdata/gnu-sparse-big.tar",
	 394  				tests: []testFnc{
	 395  					testHeader{Header{
	 396  						Typeflag: TypeGNUSparse,
	 397  						Name:		 "gnu-sparse",
	 398  						Size:		 6e10,
	 399  						SparseHoles: []sparseEntry{
	 400  							{Offset: 0e10, Length: 1e10 - 100},
	 401  							{Offset: 1e10, Length: 1e10 - 100},
	 402  							{Offset: 2e10, Length: 1e10 - 100},
	 403  							{Offset: 3e10, Length: 1e10 - 100},
	 404  							{Offset: 4e10, Length: 1e10 - 100},
	 405  							{Offset: 5e10, Length: 1e10 - 100},
	 406  						},
	 407  					}, nil},
	 408  					testReadFrom{fileOps{
	 409  						int64(1e10 - blockSize),
	 410  						strings.Repeat("\x00", blockSize-100) + strings.Repeat("0123456789", 10),
	 411  						int64(1e10 - blockSize),
	 412  						strings.Repeat("\x00", blockSize-100) + strings.Repeat("0123456789", 10),
	 413  						int64(1e10 - blockSize),
	 414  						strings.Repeat("\x00", blockSize-100) + strings.Repeat("0123456789", 10),
	 415  						int64(1e10 - blockSize),
	 416  						strings.Repeat("\x00", blockSize-100) + strings.Repeat("0123456789", 10),
	 417  						int64(1e10 - blockSize),
	 418  						strings.Repeat("\x00", blockSize-100) + strings.Repeat("0123456789", 10),
	 419  						int64(1e10 - blockSize),
	 420  						strings.Repeat("\x00", blockSize-100) + strings.Repeat("0123456789", 10),
	 421  					}, 6e10, nil},
	 422  					testClose{nil},
	 423  				},
	 424  			}, {
	 425  				file: "testdata/pax-sparse-big.tar",
	 426  				tests: []testFnc{
	 427  					testHeader{Header{
	 428  						Typeflag: TypeReg,
	 429  						Name:		 "pax-sparse",
	 430  						Size:		 6e10,
	 431  						SparseHoles: []sparseEntry{
	 432  							{Offset: 0e10, Length: 1e10 - 100},
	 433  							{Offset: 1e10, Length: 1e10 - 100},
	 434  							{Offset: 2e10, Length: 1e10 - 100},
	 435  							{Offset: 3e10, Length: 1e10 - 100},
	 436  							{Offset: 4e10, Length: 1e10 - 100},
	 437  							{Offset: 5e10, Length: 1e10 - 100},
	 438  						},
	 439  					}, nil},
	 440  					testReadFrom{fileOps{
	 441  						int64(1e10 - blockSize),
	 442  						strings.Repeat("\x00", blockSize-100) + strings.Repeat("0123456789", 10),
	 443  						int64(1e10 - blockSize),
	 444  						strings.Repeat("\x00", blockSize-100) + strings.Repeat("0123456789", 10),
	 445  						int64(1e10 - blockSize),
	 446  						strings.Repeat("\x00", blockSize-100) + strings.Repeat("0123456789", 10),
	 447  						int64(1e10 - blockSize),
	 448  						strings.Repeat("\x00", blockSize-100) + strings.Repeat("0123456789", 10),
	 449  						int64(1e10 - blockSize),
	 450  						strings.Repeat("\x00", blockSize-100) + strings.Repeat("0123456789", 10),
	 451  						int64(1e10 - blockSize),
	 452  						strings.Repeat("\x00", blockSize-100) + strings.Repeat("0123456789", 10),
	 453  					}, 6e10, nil},
	 454  					testClose{nil},
	 455  				},
	 456  		*/
	 457  	}, {
	 458  		file: "testdata/trailing-slash.tar",
	 459  		tests: []testFnc{
	 460  			testHeader{Header{Name: strings.Repeat("123456789/", 30)}, nil},
	 461  			testClose{nil},
	 462  		},
	 463  	}, {
	 464  		// Automatically promote zero value of Typeflag depending on the name.
	 465  		file: "testdata/file-and-dir.tar",
	 466  		tests: []testFnc{
	 467  			testHeader{Header{Name: "small.txt", Size: 5}, nil},
	 468  			testWrite{"Kilts", 5, nil},
	 469  			testHeader{Header{Name: "dir/"}, nil},
	 470  			testClose{nil},
	 471  		},
	 472  	}}
	 473  
	 474  	equalError := func(x, y error) bool {
	 475  		_, ok1 := x.(headerError)
	 476  		_, ok2 := y.(headerError)
	 477  		if ok1 || ok2 {
	 478  			return ok1 && ok2
	 479  		}
	 480  		return x == y
	 481  	}
	 482  	for _, v := range vectors {
	 483  		t.Run(path.Base(v.file), func(t *testing.T) {
	 484  			const maxSize = 10 << 10 // 10KiB
	 485  			buf := new(bytes.Buffer)
	 486  			tw := NewWriter(iotest.TruncateWriter(buf, maxSize))
	 487  
	 488  			for i, tf := range v.tests {
	 489  				switch tf := tf.(type) {
	 490  				case testHeader:
	 491  					err := tw.WriteHeader(&tf.hdr)
	 492  					if !equalError(err, tf.wantErr) {
	 493  						t.Fatalf("test %d, WriteHeader() = %v, want %v", i, err, tf.wantErr)
	 494  					}
	 495  				case testWrite:
	 496  					got, err := tw.Write([]byte(tf.str))
	 497  					if got != tf.wantCnt || !equalError(err, tf.wantErr) {
	 498  						t.Fatalf("test %d, Write() = (%d, %v), want (%d, %v)", i, got, err, tf.wantCnt, tf.wantErr)
	 499  					}
	 500  				case testReadFrom:
	 501  					f := &testFile{ops: tf.ops}
	 502  					got, err := tw.readFrom(f)
	 503  					if _, ok := err.(testError); ok {
	 504  						t.Errorf("test %d, ReadFrom(): %v", i, err)
	 505  					} else if got != tf.wantCnt || !equalError(err, tf.wantErr) {
	 506  						t.Errorf("test %d, ReadFrom() = (%d, %v), want (%d, %v)", i, got, err, tf.wantCnt, tf.wantErr)
	 507  					}
	 508  					if len(f.ops) > 0 {
	 509  						t.Errorf("test %d, expected %d more operations", i, len(f.ops))
	 510  					}
	 511  				case testClose:
	 512  					err := tw.Close()
	 513  					if !equalError(err, tf.wantErr) {
	 514  						t.Fatalf("test %d, Close() = %v, want %v", i, err, tf.wantErr)
	 515  					}
	 516  				default:
	 517  					t.Fatalf("test %d, unknown test operation: %T", i, tf)
	 518  				}
	 519  			}
	 520  
	 521  			if v.file != "" {
	 522  				want, err := os.ReadFile(v.file)
	 523  				if err != nil {
	 524  					t.Fatalf("ReadFile() = %v, want nil", err)
	 525  				}
	 526  				got := buf.Bytes()
	 527  				if !bytes.Equal(want, got) {
	 528  					t.Fatalf("incorrect result: (-got +want)\n%v", bytediff(got, want))
	 529  				}
	 530  			}
	 531  		})
	 532  	}
	 533  }
	 534  
	 535  func TestPax(t *testing.T) {
	 536  	// Create an archive with a large name
	 537  	fileinfo, err := os.Stat("testdata/small.txt")
	 538  	if err != nil {
	 539  		t.Fatal(err)
	 540  	}
	 541  	hdr, err := FileInfoHeader(fileinfo, "")
	 542  	if err != nil {
	 543  		t.Fatalf("os.Stat: %v", err)
	 544  	}
	 545  	// Force a PAX long name to be written
	 546  	longName := strings.Repeat("ab", 100)
	 547  	contents := strings.Repeat(" ", int(hdr.Size))
	 548  	hdr.Name = longName
	 549  	var buf bytes.Buffer
	 550  	writer := NewWriter(&buf)
	 551  	if err := writer.WriteHeader(hdr); err != nil {
	 552  		t.Fatal(err)
	 553  	}
	 554  	if _, err = writer.Write([]byte(contents)); err != nil {
	 555  		t.Fatal(err)
	 556  	}
	 557  	if err := writer.Close(); err != nil {
	 558  		t.Fatal(err)
	 559  	}
	 560  	// Simple test to make sure PAX extensions are in effect
	 561  	if !bytes.Contains(buf.Bytes(), []byte("PaxHeaders.0")) {
	 562  		t.Fatal("Expected at least one PAX header to be written.")
	 563  	}
	 564  	// Test that we can get a long name back out of the archive.
	 565  	reader := NewReader(&buf)
	 566  	hdr, err = reader.Next()
	 567  	if err != nil {
	 568  		t.Fatal(err)
	 569  	}
	 570  	if hdr.Name != longName {
	 571  		t.Fatal("Couldn't recover long file name")
	 572  	}
	 573  }
	 574  
	 575  func TestPaxSymlink(t *testing.T) {
	 576  	// Create an archive with a large linkname
	 577  	fileinfo, err := os.Stat("testdata/small.txt")
	 578  	if err != nil {
	 579  		t.Fatal(err)
	 580  	}
	 581  	hdr, err := FileInfoHeader(fileinfo, "")
	 582  	hdr.Typeflag = TypeSymlink
	 583  	if err != nil {
	 584  		t.Fatalf("os.Stat:1 %v", err)
	 585  	}
	 586  	// Force a PAX long linkname to be written
	 587  	longLinkname := strings.Repeat("1234567890/1234567890", 10)
	 588  	hdr.Linkname = longLinkname
	 589  
	 590  	hdr.Size = 0
	 591  	var buf bytes.Buffer
	 592  	writer := NewWriter(&buf)
	 593  	if err := writer.WriteHeader(hdr); err != nil {
	 594  		t.Fatal(err)
	 595  	}
	 596  	if err := writer.Close(); err != nil {
	 597  		t.Fatal(err)
	 598  	}
	 599  	// Simple test to make sure PAX extensions are in effect
	 600  	if !bytes.Contains(buf.Bytes(), []byte("PaxHeaders.0")) {
	 601  		t.Fatal("Expected at least one PAX header to be written.")
	 602  	}
	 603  	// Test that we can get a long name back out of the archive.
	 604  	reader := NewReader(&buf)
	 605  	hdr, err = reader.Next()
	 606  	if err != nil {
	 607  		t.Fatal(err)
	 608  	}
	 609  	if hdr.Linkname != longLinkname {
	 610  		t.Fatal("Couldn't recover long link name")
	 611  	}
	 612  }
	 613  
	 614  func TestPaxNonAscii(t *testing.T) {
	 615  	// Create an archive with non ascii. These should trigger a pax header
	 616  	// because pax headers have a defined utf-8 encoding.
	 617  	fileinfo, err := os.Stat("testdata/small.txt")
	 618  	if err != nil {
	 619  		t.Fatal(err)
	 620  	}
	 621  
	 622  	hdr, err := FileInfoHeader(fileinfo, "")
	 623  	if err != nil {
	 624  		t.Fatalf("os.Stat:1 %v", err)
	 625  	}
	 626  
	 627  	// some sample data
	 628  	chineseFilename := "文件名"
	 629  	chineseGroupname := "組"
	 630  	chineseUsername := "用戶名"
	 631  
	 632  	hdr.Name = chineseFilename
	 633  	hdr.Gname = chineseGroupname
	 634  	hdr.Uname = chineseUsername
	 635  
	 636  	contents := strings.Repeat(" ", int(hdr.Size))
	 637  
	 638  	var buf bytes.Buffer
	 639  	writer := NewWriter(&buf)
	 640  	if err := writer.WriteHeader(hdr); err != nil {
	 641  		t.Fatal(err)
	 642  	}
	 643  	if _, err = writer.Write([]byte(contents)); err != nil {
	 644  		t.Fatal(err)
	 645  	}
	 646  	if err := writer.Close(); err != nil {
	 647  		t.Fatal(err)
	 648  	}
	 649  	// Simple test to make sure PAX extensions are in effect
	 650  	if !bytes.Contains(buf.Bytes(), []byte("PaxHeaders.0")) {
	 651  		t.Fatal("Expected at least one PAX header to be written.")
	 652  	}
	 653  	// Test that we can get a long name back out of the archive.
	 654  	reader := NewReader(&buf)
	 655  	hdr, err = reader.Next()
	 656  	if err != nil {
	 657  		t.Fatal(err)
	 658  	}
	 659  	if hdr.Name != chineseFilename {
	 660  		t.Fatal("Couldn't recover unicode name")
	 661  	}
	 662  	if hdr.Gname != chineseGroupname {
	 663  		t.Fatal("Couldn't recover unicode group")
	 664  	}
	 665  	if hdr.Uname != chineseUsername {
	 666  		t.Fatal("Couldn't recover unicode user")
	 667  	}
	 668  }
	 669  
	 670  func TestPaxXattrs(t *testing.T) {
	 671  	xattrs := map[string]string{
	 672  		"user.key": "value",
	 673  	}
	 674  
	 675  	// Create an archive with an xattr
	 676  	fileinfo, err := os.Stat("testdata/small.txt")
	 677  	if err != nil {
	 678  		t.Fatal(err)
	 679  	}
	 680  	hdr, err := FileInfoHeader(fileinfo, "")
	 681  	if err != nil {
	 682  		t.Fatalf("os.Stat: %v", err)
	 683  	}
	 684  	contents := "Kilts"
	 685  	hdr.Xattrs = xattrs
	 686  	var buf bytes.Buffer
	 687  	writer := NewWriter(&buf)
	 688  	if err := writer.WriteHeader(hdr); err != nil {
	 689  		t.Fatal(err)
	 690  	}
	 691  	if _, err = writer.Write([]byte(contents)); err != nil {
	 692  		t.Fatal(err)
	 693  	}
	 694  	if err := writer.Close(); err != nil {
	 695  		t.Fatal(err)
	 696  	}
	 697  	// Test that we can get the xattrs back out of the archive.
	 698  	reader := NewReader(&buf)
	 699  	hdr, err = reader.Next()
	 700  	if err != nil {
	 701  		t.Fatal(err)
	 702  	}
	 703  	if !reflect.DeepEqual(hdr.Xattrs, xattrs) {
	 704  		t.Fatalf("xattrs did not survive round trip: got %+v, want %+v",
	 705  			hdr.Xattrs, xattrs)
	 706  	}
	 707  }
	 708  
	 709  func TestPaxHeadersSorted(t *testing.T) {
	 710  	fileinfo, err := os.Stat("testdata/small.txt")
	 711  	if err != nil {
	 712  		t.Fatal(err)
	 713  	}
	 714  	hdr, err := FileInfoHeader(fileinfo, "")
	 715  	if err != nil {
	 716  		t.Fatalf("os.Stat: %v", err)
	 717  	}
	 718  	contents := strings.Repeat(" ", int(hdr.Size))
	 719  
	 720  	hdr.Xattrs = map[string]string{
	 721  		"foo": "foo",
	 722  		"bar": "bar",
	 723  		"baz": "baz",
	 724  		"qux": "qux",
	 725  	}
	 726  
	 727  	var buf bytes.Buffer
	 728  	writer := NewWriter(&buf)
	 729  	if err := writer.WriteHeader(hdr); err != nil {
	 730  		t.Fatal(err)
	 731  	}
	 732  	if _, err = writer.Write([]byte(contents)); err != nil {
	 733  		t.Fatal(err)
	 734  	}
	 735  	if err := writer.Close(); err != nil {
	 736  		t.Fatal(err)
	 737  	}
	 738  	// Simple test to make sure PAX extensions are in effect
	 739  	if !bytes.Contains(buf.Bytes(), []byte("PaxHeaders.0")) {
	 740  		t.Fatal("Expected at least one PAX header to be written.")
	 741  	}
	 742  
	 743  	// xattr bar should always appear before others
	 744  	indices := []int{
	 745  		bytes.Index(buf.Bytes(), []byte("bar=bar")),
	 746  		bytes.Index(buf.Bytes(), []byte("baz=baz")),
	 747  		bytes.Index(buf.Bytes(), []byte("foo=foo")),
	 748  		bytes.Index(buf.Bytes(), []byte("qux=qux")),
	 749  	}
	 750  	if !sort.IntsAreSorted(indices) {
	 751  		t.Fatal("PAX headers are not sorted")
	 752  	}
	 753  }
	 754  
	 755  func TestUSTARLongName(t *testing.T) {
	 756  	// Create an archive with a path that failed to split with USTAR extension in previous versions.
	 757  	fileinfo, err := os.Stat("testdata/small.txt")
	 758  	if err != nil {
	 759  		t.Fatal(err)
	 760  	}
	 761  	hdr, err := FileInfoHeader(fileinfo, "")
	 762  	hdr.Typeflag = TypeDir
	 763  	if err != nil {
	 764  		t.Fatalf("os.Stat:1 %v", err)
	 765  	}
	 766  	// Force a PAX long name to be written. The name was taken from a practical example
	 767  	// that fails and replaced ever char through numbers to anonymize the sample.
	 768  	longName := "/0000_0000000/00000-000000000/0000_0000000/00000-0000000000000/0000_0000000/00000-0000000-00000000/0000_0000000/00000000/0000_0000000/000/0000_0000000/00000000v00/0000_0000000/000000/0000_0000000/0000000/0000_0000000/00000y-00/0000/0000/00000000/0x000000/"
	 769  	hdr.Name = longName
	 770  
	 771  	hdr.Size = 0
	 772  	var buf bytes.Buffer
	 773  	writer := NewWriter(&buf)
	 774  	if err := writer.WriteHeader(hdr); err != nil {
	 775  		t.Fatal(err)
	 776  	}
	 777  	if err := writer.Close(); err != nil {
	 778  		t.Fatal(err)
	 779  	}
	 780  	// Test that we can get a long name back out of the archive.
	 781  	reader := NewReader(&buf)
	 782  	hdr, err = reader.Next()
	 783  	if err != nil {
	 784  		t.Fatal(err)
	 785  	}
	 786  	if hdr.Name != longName {
	 787  		t.Fatal("Couldn't recover long name")
	 788  	}
	 789  }
	 790  
	 791  func TestValidTypeflagWithPAXHeader(t *testing.T) {
	 792  	var buffer bytes.Buffer
	 793  	tw := NewWriter(&buffer)
	 794  
	 795  	fileName := strings.Repeat("ab", 100)
	 796  
	 797  	hdr := &Header{
	 798  		Name:		 fileName,
	 799  		Size:		 4,
	 800  		Typeflag: 0,
	 801  	}
	 802  	if err := tw.WriteHeader(hdr); err != nil {
	 803  		t.Fatalf("Failed to write header: %s", err)
	 804  	}
	 805  	if _, err := tw.Write([]byte("fooo")); err != nil {
	 806  		t.Fatalf("Failed to write the file's data: %s", err)
	 807  	}
	 808  	tw.Close()
	 809  
	 810  	tr := NewReader(&buffer)
	 811  
	 812  	for {
	 813  		header, err := tr.Next()
	 814  		if err == io.EOF {
	 815  			break
	 816  		}
	 817  		if err != nil {
	 818  			t.Fatalf("Failed to read header: %s", err)
	 819  		}
	 820  		if header.Typeflag != TypeReg {
	 821  			t.Fatalf("Typeflag should've been %d, found %d", TypeReg, header.Typeflag)
	 822  		}
	 823  	}
	 824  }
	 825  
	 826  // failOnceWriter fails exactly once and then always reports success.
	 827  type failOnceWriter bool
	 828  
	 829  func (w *failOnceWriter) Write(b []byte) (int, error) {
	 830  	if !*w {
	 831  		return 0, io.ErrShortWrite
	 832  	}
	 833  	*w = true
	 834  	return len(b), nil
	 835  }
	 836  
	 837  func TestWriterErrors(t *testing.T) {
	 838  	t.Run("HeaderOnly", func(t *testing.T) {
	 839  		tw := NewWriter(new(bytes.Buffer))
	 840  		hdr := &Header{Name: "dir/", Typeflag: TypeDir}
	 841  		if err := tw.WriteHeader(hdr); err != nil {
	 842  			t.Fatalf("WriteHeader() = %v, want nil", err)
	 843  		}
	 844  		if _, err := tw.Write([]byte{0x00}); err != ErrWriteTooLong {
	 845  			t.Fatalf("Write() = %v, want %v", err, ErrWriteTooLong)
	 846  		}
	 847  	})
	 848  
	 849  	t.Run("NegativeSize", func(t *testing.T) {
	 850  		tw := NewWriter(new(bytes.Buffer))
	 851  		hdr := &Header{Name: "small.txt", Size: -1}
	 852  		if err := tw.WriteHeader(hdr); err == nil {
	 853  			t.Fatalf("WriteHeader() = nil, want non-nil error")
	 854  		}
	 855  	})
	 856  
	 857  	t.Run("BeforeHeader", func(t *testing.T) {
	 858  		tw := NewWriter(new(bytes.Buffer))
	 859  		if _, err := tw.Write([]byte("Kilts")); err != ErrWriteTooLong {
	 860  			t.Fatalf("Write() = %v, want %v", err, ErrWriteTooLong)
	 861  		}
	 862  	})
	 863  
	 864  	t.Run("AfterClose", func(t *testing.T) {
	 865  		tw := NewWriter(new(bytes.Buffer))
	 866  		hdr := &Header{Name: "small.txt"}
	 867  		if err := tw.WriteHeader(hdr); err != nil {
	 868  			t.Fatalf("WriteHeader() = %v, want nil", err)
	 869  		}
	 870  		if err := tw.Close(); err != nil {
	 871  			t.Fatalf("Close() = %v, want nil", err)
	 872  		}
	 873  		if _, err := tw.Write([]byte("Kilts")); err != ErrWriteAfterClose {
	 874  			t.Fatalf("Write() = %v, want %v", err, ErrWriteAfterClose)
	 875  		}
	 876  		if err := tw.Flush(); err != ErrWriteAfterClose {
	 877  			t.Fatalf("Flush() = %v, want %v", err, ErrWriteAfterClose)
	 878  		}
	 879  		if err := tw.Close(); err != nil {
	 880  			t.Fatalf("Close() = %v, want nil", err)
	 881  		}
	 882  	})
	 883  
	 884  	t.Run("PrematureFlush", func(t *testing.T) {
	 885  		tw := NewWriter(new(bytes.Buffer))
	 886  		hdr := &Header{Name: "small.txt", Size: 5}
	 887  		if err := tw.WriteHeader(hdr); err != nil {
	 888  			t.Fatalf("WriteHeader() = %v, want nil", err)
	 889  		}
	 890  		if err := tw.Flush(); err == nil {
	 891  			t.Fatalf("Flush() = %v, want non-nil error", err)
	 892  		}
	 893  	})
	 894  
	 895  	t.Run("PrematureClose", func(t *testing.T) {
	 896  		tw := NewWriter(new(bytes.Buffer))
	 897  		hdr := &Header{Name: "small.txt", Size: 5}
	 898  		if err := tw.WriteHeader(hdr); err != nil {
	 899  			t.Fatalf("WriteHeader() = %v, want nil", err)
	 900  		}
	 901  		if err := tw.Close(); err == nil {
	 902  			t.Fatalf("Close() = %v, want non-nil error", err)
	 903  		}
	 904  	})
	 905  
	 906  	t.Run("Persistence", func(t *testing.T) {
	 907  		tw := NewWriter(new(failOnceWriter))
	 908  		if err := tw.WriteHeader(&Header{}); err != io.ErrShortWrite {
	 909  			t.Fatalf("WriteHeader() = %v, want %v", err, io.ErrShortWrite)
	 910  		}
	 911  		if err := tw.WriteHeader(&Header{Name: "small.txt"}); err == nil {
	 912  			t.Errorf("WriteHeader() = got %v, want non-nil error", err)
	 913  		}
	 914  		if _, err := tw.Write(nil); err == nil {
	 915  			t.Errorf("Write() = %v, want non-nil error", err)
	 916  		}
	 917  		if err := tw.Flush(); err == nil {
	 918  			t.Errorf("Flush() = %v, want non-nil error", err)
	 919  		}
	 920  		if err := tw.Close(); err == nil {
	 921  			t.Errorf("Close() = %v, want non-nil error", err)
	 922  		}
	 923  	})
	 924  }
	 925  
	 926  func TestSplitUSTARPath(t *testing.T) {
	 927  	sr := strings.Repeat
	 928  
	 929  	vectors := []struct {
	 930  		input	string // Input path
	 931  		prefix string // Expected output prefix
	 932  		suffix string // Expected output suffix
	 933  		ok		 bool	 // Split success?
	 934  	}{
	 935  		{"", "", "", false},
	 936  		{"abc", "", "", false},
	 937  		{"用戶名", "", "", false},
	 938  		{sr("a", nameSize), "", "", false},
	 939  		{sr("a", nameSize) + "/", "", "", false},
	 940  		{sr("a", nameSize) + "/a", sr("a", nameSize), "a", true},
	 941  		{sr("a", prefixSize) + "/", "", "", false},
	 942  		{sr("a", prefixSize) + "/a", sr("a", prefixSize), "a", true},
	 943  		{sr("a", nameSize+1), "", "", false},
	 944  		{sr("/", nameSize+1), sr("/", nameSize-1), "/", true},
	 945  		{sr("a", prefixSize) + "/" + sr("b", nameSize),
	 946  			sr("a", prefixSize), sr("b", nameSize), true},
	 947  		{sr("a", prefixSize) + "//" + sr("b", nameSize), "", "", false},
	 948  		{sr("a/", nameSize), sr("a/", 77) + "a", sr("a/", 22), true},
	 949  	}
	 950  
	 951  	for _, v := range vectors {
	 952  		prefix, suffix, ok := splitUSTARPath(v.input)
	 953  		if prefix != v.prefix || suffix != v.suffix || ok != v.ok {
	 954  			t.Errorf("splitUSTARPath(%q):\ngot	(%q, %q, %v)\nwant (%q, %q, %v)",
	 955  				v.input, prefix, suffix, ok, v.prefix, v.suffix, v.ok)
	 956  		}
	 957  	}
	 958  }
	 959  
	 960  // TestIssue12594 tests that the Writer does not attempt to populate the prefix
	 961  // field when encoding a header in the GNU format. The prefix field is valid
	 962  // in USTAR and PAX, but not GNU.
	 963  func TestIssue12594(t *testing.T) {
	 964  	names := []string{
	 965  		"0/1/2/3/4/5/6/7/8/9/10/11/12/13/14/15/16/17/18/19/20/21/22/23/24/25/26/27/28/29/30/file.txt",
	 966  		"0/1/2/3/4/5/6/7/8/9/10/11/12/13/14/15/16/17/18/19/20/21/22/23/24/25/26/27/28/29/30/31/32/33/file.txt",
	 967  		"0/1/2/3/4/5/6/7/8/9/10/11/12/13/14/15/16/17/18/19/20/21/22/23/24/25/26/27/28/29/30/31/32/333/file.txt",
	 968  		"0/1/2/3/4/5/6/7/8/9/10/11/12/13/14/15/16/17/18/19/20/21/22/23/24/25/26/27/28/29/30/31/32/33/34/35/36/37/38/39/40/file.txt",
	 969  		"0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000/file.txt",
	 970  		"/home/support/.openoffice.org/3/user/uno_packages/cache/registry/com.sun.star.comp.deployment.executable.PackageRegistryBackend",
	 971  	}
	 972  
	 973  	for i, name := range names {
	 974  		var b bytes.Buffer
	 975  
	 976  		tw := NewWriter(&b)
	 977  		if err := tw.WriteHeader(&Header{
	 978  			Name: name,
	 979  			Uid:	1 << 25, // Prevent USTAR format
	 980  		}); err != nil {
	 981  			t.Errorf("test %d, unexpected WriteHeader error: %v", i, err)
	 982  		}
	 983  		if err := tw.Close(); err != nil {
	 984  			t.Errorf("test %d, unexpected Close error: %v", i, err)
	 985  		}
	 986  
	 987  		// The prefix field should never appear in the GNU format.
	 988  		var blk block
	 989  		copy(blk[:], b.Bytes())
	 990  		prefix := string(blk.USTAR().Prefix())
	 991  		if i := strings.IndexByte(prefix, 0); i >= 0 {
	 992  			prefix = prefix[:i] // Truncate at the NUL terminator
	 993  		}
	 994  		if blk.GetFormat() == FormatGNU && len(prefix) > 0 && strings.HasPrefix(name, prefix) {
	 995  			t.Errorf("test %d, found prefix in GNU format: %s", i, prefix)
	 996  		}
	 997  
	 998  		tr := NewReader(&b)
	 999  		hdr, err := tr.Next()
	1000  		if err != nil {
	1001  			t.Errorf("test %d, unexpected Next error: %v", i, err)
	1002  		}
	1003  		if hdr.Name != name {
	1004  			t.Errorf("test %d, hdr.Name = %s, want %s", i, hdr.Name, name)
	1005  		}
	1006  	}
	1007  }
	1008  
	1009  // testNonEmptyWriter wraps an io.Writer and ensures that
	1010  // Write is never called with an empty buffer.
	1011  type testNonEmptyWriter struct{ io.Writer }
	1012  
	1013  func (w testNonEmptyWriter) Write(b []byte) (int, error) {
	1014  	if len(b) == 0 {
	1015  		return 0, errors.New("unexpected empty Write call")
	1016  	}
	1017  	return w.Writer.Write(b)
	1018  }
	1019  
	1020  func TestFileWriter(t *testing.T) {
	1021  	type (
	1022  		testWrite struct { // Write(str) == (wantCnt, wantErr)
	1023  			str		 string
	1024  			wantCnt int
	1025  			wantErr error
	1026  		}
	1027  		testReadFrom struct { // ReadFrom(testFile{ops}) == (wantCnt, wantErr)
	1028  			ops		 fileOps
	1029  			wantCnt int64
	1030  			wantErr error
	1031  		}
	1032  		testRemaining struct { // LogicalRemaining() == wantLCnt, PhysicalRemaining() == wantPCnt
	1033  			wantLCnt int64
	1034  			wantPCnt int64
	1035  		}
	1036  		testFnc interface{} // testWrite | testReadFrom | testRemaining
	1037  	)
	1038  
	1039  	type (
	1040  		makeReg struct {
	1041  			size		int64
	1042  			wantStr string
	1043  		}
	1044  		makeSparse struct {
	1045  			makeReg makeReg
	1046  			sph		 sparseHoles
	1047  			size		int64
	1048  		}
	1049  		fileMaker interface{} // makeReg | makeSparse
	1050  	)
	1051  
	1052  	vectors := []struct {
	1053  		maker fileMaker
	1054  		tests []testFnc
	1055  	}{{
	1056  		maker: makeReg{0, ""},
	1057  		tests: []testFnc{
	1058  			testRemaining{0, 0},
	1059  			testWrite{"", 0, nil},
	1060  			testWrite{"a", 0, ErrWriteTooLong},
	1061  			testReadFrom{fileOps{""}, 0, nil},
	1062  			testReadFrom{fileOps{"a"}, 0, ErrWriteTooLong},
	1063  			testRemaining{0, 0},
	1064  		},
	1065  	}, {
	1066  		maker: makeReg{1, "a"},
	1067  		tests: []testFnc{
	1068  			testRemaining{1, 1},
	1069  			testWrite{"", 0, nil},
	1070  			testWrite{"a", 1, nil},
	1071  			testWrite{"bcde", 0, ErrWriteTooLong},
	1072  			testWrite{"", 0, nil},
	1073  			testReadFrom{fileOps{""}, 0, nil},
	1074  			testReadFrom{fileOps{"a"}, 0, ErrWriteTooLong},
	1075  			testRemaining{0, 0},
	1076  		},
	1077  	}, {
	1078  		maker: makeReg{5, "hello"},
	1079  		tests: []testFnc{
	1080  			testRemaining{5, 5},
	1081  			testWrite{"hello", 5, nil},
	1082  			testRemaining{0, 0},
	1083  		},
	1084  	}, {
	1085  		maker: makeReg{5, "\x00\x00\x00\x00\x00"},
	1086  		tests: []testFnc{
	1087  			testRemaining{5, 5},
	1088  			testReadFrom{fileOps{"\x00\x00\x00\x00\x00"}, 5, nil},
	1089  			testRemaining{0, 0},
	1090  		},
	1091  	}, {
	1092  		maker: makeReg{5, "\x00\x00\x00\x00\x00"},
	1093  		tests: []testFnc{
	1094  			testRemaining{5, 5},
	1095  			testReadFrom{fileOps{"\x00\x00\x00\x00\x00extra"}, 5, ErrWriteTooLong},
	1096  			testRemaining{0, 0},
	1097  		},
	1098  	}, {
	1099  		maker: makeReg{5, "abc\x00\x00"},
	1100  		tests: []testFnc{
	1101  			testRemaining{5, 5},
	1102  			testWrite{"abc", 3, nil},
	1103  			testRemaining{2, 2},
	1104  			testReadFrom{fileOps{"\x00\x00"}, 2, nil},
	1105  			testRemaining{0, 0},
	1106  		},
	1107  	}, {
	1108  		maker: makeReg{5, "\x00\x00abc"},
	1109  		tests: []testFnc{
	1110  			testRemaining{5, 5},
	1111  			testWrite{"\x00\x00", 2, nil},
	1112  			testRemaining{3, 3},
	1113  			testWrite{"abc", 3, nil},
	1114  			testReadFrom{fileOps{"z"}, 0, ErrWriteTooLong},
	1115  			testWrite{"z", 0, ErrWriteTooLong},
	1116  			testRemaining{0, 0},
	1117  		},
	1118  	}, {
	1119  		maker: makeSparse{makeReg{5, "abcde"}, sparseHoles{{2, 3}}, 8},
	1120  		tests: []testFnc{
	1121  			testRemaining{8, 5},
	1122  			testWrite{"ab\x00\x00\x00cde", 8, nil},
	1123  			testWrite{"a", 0, ErrWriteTooLong},
	1124  			testRemaining{0, 0},
	1125  		},
	1126  	}, {
	1127  		maker: makeSparse{makeReg{5, "abcde"}, sparseHoles{{2, 3}}, 8},
	1128  		tests: []testFnc{
	1129  			testWrite{"ab\x00\x00\x00cdez", 8, ErrWriteTooLong},
	1130  			testRemaining{0, 0},
	1131  		},
	1132  	}, {
	1133  		maker: makeSparse{makeReg{5, "abcde"}, sparseHoles{{2, 3}}, 8},
	1134  		tests: []testFnc{
	1135  			testWrite{"ab\x00", 3, nil},
	1136  			testRemaining{5, 3},
	1137  			testWrite{"\x00\x00cde", 5, nil},
	1138  			testWrite{"a", 0, ErrWriteTooLong},
	1139  			testRemaining{0, 0},
	1140  		},
	1141  	}, {
	1142  		maker: makeSparse{makeReg{5, "abcde"}, sparseHoles{{2, 3}}, 8},
	1143  		tests: []testFnc{
	1144  			testWrite{"ab", 2, nil},
	1145  			testRemaining{6, 3},
	1146  			testReadFrom{fileOps{int64(3), "cde"}, 6, nil},
	1147  			testRemaining{0, 0},
	1148  		},
	1149  	}, {
	1150  		maker: makeSparse{makeReg{5, "abcde"}, sparseHoles{{2, 3}}, 8},
	1151  		tests: []testFnc{
	1152  			testReadFrom{fileOps{"ab", int64(3), "cde"}, 8, nil},
	1153  			testRemaining{0, 0},
	1154  		},
	1155  	}, {
	1156  		maker: makeSparse{makeReg{5, "abcde"}, sparseHoles{{2, 3}}, 8},
	1157  		tests: []testFnc{
	1158  			testReadFrom{fileOps{"ab", int64(3), "cdeX"}, 8, ErrWriteTooLong},
	1159  			testRemaining{0, 0},
	1160  		},
	1161  	}, {
	1162  		maker: makeSparse{makeReg{4, "abcd"}, sparseHoles{{2, 3}}, 8},
	1163  		tests: []testFnc{
	1164  			testReadFrom{fileOps{"ab", int64(3), "cd"}, 7, io.ErrUnexpectedEOF},
	1165  			testRemaining{1, 0},
	1166  		},
	1167  	}, {
	1168  		maker: makeSparse{makeReg{4, "abcd"}, sparseHoles{{2, 3}}, 8},
	1169  		tests: []testFnc{
	1170  			testReadFrom{fileOps{"ab", int64(3), "cde"}, 7, errMissData},
	1171  			testRemaining{1, 0},
	1172  		},
	1173  	}, {
	1174  		maker: makeSparse{makeReg{6, "abcde"}, sparseHoles{{2, 3}}, 8},
	1175  		tests: []testFnc{
	1176  			testReadFrom{fileOps{"ab", int64(3), "cde"}, 8, errUnrefData},
	1177  			testRemaining{0, 1},
	1178  		},
	1179  	}, {
	1180  		maker: makeSparse{makeReg{4, "abcd"}, sparseHoles{{2, 3}}, 8},
	1181  		tests: []testFnc{
	1182  			testWrite{"ab", 2, nil},
	1183  			testRemaining{6, 2},
	1184  			testWrite{"\x00\x00\x00", 3, nil},
	1185  			testRemaining{3, 2},
	1186  			testWrite{"cde", 2, errMissData},
	1187  			testRemaining{1, 0},
	1188  		},
	1189  	}, {
	1190  		maker: makeSparse{makeReg{6, "abcde"}, sparseHoles{{2, 3}}, 8},
	1191  		tests: []testFnc{
	1192  			testWrite{"ab", 2, nil},
	1193  			testRemaining{6, 4},
	1194  			testWrite{"\x00\x00\x00", 3, nil},
	1195  			testRemaining{3, 4},
	1196  			testWrite{"cde", 3, errUnrefData},
	1197  			testRemaining{0, 1},
	1198  		},
	1199  	}, {
	1200  		maker: makeSparse{makeReg{3, "abc"}, sparseHoles{{0, 2}, {5, 2}}, 7},
	1201  		tests: []testFnc{
	1202  			testRemaining{7, 3},
	1203  			testWrite{"\x00\x00abc\x00\x00", 7, nil},
	1204  			testRemaining{0, 0},
	1205  		},
	1206  	}, {
	1207  		maker: makeSparse{makeReg{3, "abc"}, sparseHoles{{0, 2}, {5, 2}}, 7},
	1208  		tests: []testFnc{
	1209  			testRemaining{7, 3},
	1210  			testReadFrom{fileOps{int64(2), "abc", int64(1), "\x00"}, 7, nil},
	1211  			testRemaining{0, 0},
	1212  		},
	1213  	}, {
	1214  		maker: makeSparse{makeReg{3, ""}, sparseHoles{{0, 2}, {5, 2}}, 7},
	1215  		tests: []testFnc{
	1216  			testWrite{"abcdefg", 0, errWriteHole},
	1217  		},
	1218  	}, {
	1219  		maker: makeSparse{makeReg{3, "abc"}, sparseHoles{{0, 2}, {5, 2}}, 7},
	1220  		tests: []testFnc{
	1221  			testWrite{"\x00\x00abcde", 5, errWriteHole},
	1222  		},
	1223  	}, {
	1224  		maker: makeSparse{makeReg{3, "abc"}, sparseHoles{{0, 2}, {5, 2}}, 7},
	1225  		tests: []testFnc{
	1226  			testWrite{"\x00\x00abc\x00\x00z", 7, ErrWriteTooLong},
	1227  			testRemaining{0, 0},
	1228  		},
	1229  	}, {
	1230  		maker: makeSparse{makeReg{3, "abc"}, sparseHoles{{0, 2}, {5, 2}}, 7},
	1231  		tests: []testFnc{
	1232  			testWrite{"\x00\x00", 2, nil},
	1233  			testRemaining{5, 3},
	1234  			testWrite{"abc", 3, nil},
	1235  			testRemaining{2, 0},
	1236  			testWrite{"\x00\x00", 2, nil},
	1237  			testRemaining{0, 0},
	1238  		},
	1239  	}, {
	1240  		maker: makeSparse{makeReg{2, "ab"}, sparseHoles{{0, 2}, {5, 2}}, 7},
	1241  		tests: []testFnc{
	1242  			testWrite{"\x00\x00", 2, nil},
	1243  			testWrite{"abc", 2, errMissData},
	1244  			testWrite{"\x00\x00", 0, errMissData},
	1245  		},
	1246  	}, {
	1247  		maker: makeSparse{makeReg{4, "abc"}, sparseHoles{{0, 2}, {5, 2}}, 7},
	1248  		tests: []testFnc{
	1249  			testWrite{"\x00\x00", 2, nil},
	1250  			testWrite{"abc", 3, nil},
	1251  			testWrite{"\x00\x00", 2, errUnrefData},
	1252  		},
	1253  	}}
	1254  
	1255  	for i, v := range vectors {
	1256  		var wantStr string
	1257  		bb := new(bytes.Buffer)
	1258  		w := testNonEmptyWriter{bb}
	1259  		var fw fileWriter
	1260  		switch maker := v.maker.(type) {
	1261  		case makeReg:
	1262  			fw = &regFileWriter{w, maker.size}
	1263  			wantStr = maker.wantStr
	1264  		case makeSparse:
	1265  			if !validateSparseEntries(maker.sph, maker.size) {
	1266  				t.Fatalf("invalid sparse map: %v", maker.sph)
	1267  			}
	1268  			spd := invertSparseEntries(maker.sph, maker.size)
	1269  			fw = &regFileWriter{w, maker.makeReg.size}
	1270  			fw = &sparseFileWriter{fw, spd, 0}
	1271  			wantStr = maker.makeReg.wantStr
	1272  		default:
	1273  			t.Fatalf("test %d, unknown make operation: %T", i, maker)
	1274  		}
	1275  
	1276  		for j, tf := range v.tests {
	1277  			switch tf := tf.(type) {
	1278  			case testWrite:
	1279  				got, err := fw.Write([]byte(tf.str))
	1280  				if got != tf.wantCnt || err != tf.wantErr {
	1281  					t.Errorf("test %d.%d, Write(%s):\ngot	(%d, %v)\nwant (%d, %v)", i, j, tf.str, got, err, tf.wantCnt, tf.wantErr)
	1282  				}
	1283  			case testReadFrom:
	1284  				f := &testFile{ops: tf.ops}
	1285  				got, err := fw.ReadFrom(f)
	1286  				if _, ok := err.(testError); ok {
	1287  					t.Errorf("test %d.%d, ReadFrom(): %v", i, j, err)
	1288  				} else if got != tf.wantCnt || err != tf.wantErr {
	1289  					t.Errorf("test %d.%d, ReadFrom() = (%d, %v), want (%d, %v)", i, j, got, err, tf.wantCnt, tf.wantErr)
	1290  				}
	1291  				if len(f.ops) > 0 {
	1292  					t.Errorf("test %d.%d, expected %d more operations", i, j, len(f.ops))
	1293  				}
	1294  			case testRemaining:
	1295  				if got := fw.LogicalRemaining(); got != tf.wantLCnt {
	1296  					t.Errorf("test %d.%d, LogicalRemaining() = %d, want %d", i, j, got, tf.wantLCnt)
	1297  				}
	1298  				if got := fw.PhysicalRemaining(); got != tf.wantPCnt {
	1299  					t.Errorf("test %d.%d, PhysicalRemaining() = %d, want %d", i, j, got, tf.wantPCnt)
	1300  				}
	1301  			default:
	1302  				t.Fatalf("test %d.%d, unknown test operation: %T", i, j, tf)
	1303  			}
	1304  		}
	1305  
	1306  		if got := bb.String(); got != wantStr {
	1307  			t.Fatalf("test %d, String() = %q, want %q", i, got, wantStr)
	1308  		}
	1309  	}
	1310  }
	1311  

View as plain text