github.com/miolini/go@v0.0.0-20160405192216-fca68c8cb408/src/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  	"math"
    13  	"os"
    14  	"reflect"
    15  	"sort"
    16  	"strings"
    17  	"testing"
    18  	"testing/iotest"
    19  	"time"
    20  )
    21  
    22  type writerTestEntry struct {
    23  	header   *Header
    24  	contents string
    25  }
    26  
    27  type writerTest struct {
    28  	file    string // filename of expected output
    29  	entries []*writerTestEntry
    30  }
    31  
    32  var writerTests = []*writerTest{
    33  	// The writer test file was produced with this command:
    34  	// tar (GNU tar) 1.26
    35  	//   ln -s small.txt link.txt
    36  	//   tar -b 1 --format=ustar -c -f writer.tar small.txt small2.txt link.txt
    37  	{
    38  		file: "testdata/writer.tar",
    39  		entries: []*writerTestEntry{
    40  			{
    41  				header: &Header{
    42  					Name:     "small.txt",
    43  					Mode:     0640,
    44  					Uid:      73025,
    45  					Gid:      5000,
    46  					Size:     5,
    47  					ModTime:  time.Unix(1246508266, 0),
    48  					Typeflag: '0',
    49  					Uname:    "dsymonds",
    50  					Gname:    "eng",
    51  				},
    52  				contents: "Kilts",
    53  			},
    54  			{
    55  				header: &Header{
    56  					Name:     "small2.txt",
    57  					Mode:     0640,
    58  					Uid:      73025,
    59  					Gid:      5000,
    60  					Size:     11,
    61  					ModTime:  time.Unix(1245217492, 0),
    62  					Typeflag: '0',
    63  					Uname:    "dsymonds",
    64  					Gname:    "eng",
    65  				},
    66  				contents: "Google.com\n",
    67  			},
    68  			{
    69  				header: &Header{
    70  					Name:     "link.txt",
    71  					Mode:     0777,
    72  					Uid:      1000,
    73  					Gid:      1000,
    74  					Size:     0,
    75  					ModTime:  time.Unix(1314603082, 0),
    76  					Typeflag: '2',
    77  					Linkname: "small.txt",
    78  					Uname:    "strings",
    79  					Gname:    "strings",
    80  				},
    81  				// no contents
    82  			},
    83  		},
    84  	},
    85  	// The truncated test file was produced using these commands:
    86  	//   dd if=/dev/zero bs=1048576 count=16384 > /tmp/16gig.txt
    87  	//   tar -b 1 -c -f- /tmp/16gig.txt | dd bs=512 count=8 > writer-big.tar
    88  	{
    89  		file: "testdata/writer-big.tar",
    90  		entries: []*writerTestEntry{
    91  			{
    92  				header: &Header{
    93  					Name:     "tmp/16gig.txt",
    94  					Mode:     0640,
    95  					Uid:      73025,
    96  					Gid:      5000,
    97  					Size:     16 << 30,
    98  					ModTime:  time.Unix(1254699560, 0),
    99  					Typeflag: '0',
   100  					Uname:    "dsymonds",
   101  					Gname:    "eng",
   102  				},
   103  				// fake contents
   104  				contents: strings.Repeat("\x00", 4<<10),
   105  			},
   106  		},
   107  	},
   108  	// The truncated test file was produced using these commands:
   109  	//   dd if=/dev/zero bs=1048576 count=16384 > (longname/)*15 /16gig.txt
   110  	//   tar -b 1 -c -f- (longname/)*15 /16gig.txt | dd bs=512 count=8 > writer-big-long.tar
   111  	{
   112  		file: "testdata/writer-big-long.tar",
   113  		entries: []*writerTestEntry{
   114  			{
   115  				header: &Header{
   116  					Name:     strings.Repeat("longname/", 15) + "16gig.txt",
   117  					Mode:     0644,
   118  					Uid:      1000,
   119  					Gid:      1000,
   120  					Size:     16 << 30,
   121  					ModTime:  time.Unix(1399583047, 0),
   122  					Typeflag: '0',
   123  					Uname:    "guillaume",
   124  					Gname:    "guillaume",
   125  				},
   126  				// fake contents
   127  				contents: strings.Repeat("\x00", 4<<10),
   128  			},
   129  		},
   130  	},
   131  	// This file was produced using gnu tar 1.17
   132  	// gnutar  -b 4 --format=ustar (longname/)*15 + file.txt
   133  	{
   134  		file: "testdata/ustar.tar",
   135  		entries: []*writerTestEntry{
   136  			{
   137  				header: &Header{
   138  					Name:     strings.Repeat("longname/", 15) + "file.txt",
   139  					Mode:     0644,
   140  					Uid:      0765,
   141  					Gid:      024,
   142  					Size:     06,
   143  					ModTime:  time.Unix(1360135598, 0),
   144  					Typeflag: '0',
   145  					Uname:    "shane",
   146  					Gname:    "staff",
   147  				},
   148  				contents: "hello\n",
   149  			},
   150  		},
   151  	},
   152  	// This file was produced using gnu tar 1.26
   153  	// echo "Slartibartfast" > file.txt
   154  	// ln file.txt hard.txt
   155  	// tar -b 1 --format=ustar -c -f hardlink.tar file.txt hard.txt
   156  	{
   157  		file: "testdata/hardlink.tar",
   158  		entries: []*writerTestEntry{
   159  			{
   160  				header: &Header{
   161  					Name:     "file.txt",
   162  					Mode:     0644,
   163  					Uid:      1000,
   164  					Gid:      100,
   165  					Size:     15,
   166  					ModTime:  time.Unix(1425484303, 0),
   167  					Typeflag: '0',
   168  					Uname:    "vbatts",
   169  					Gname:    "users",
   170  				},
   171  				contents: "Slartibartfast\n",
   172  			},
   173  			{
   174  				header: &Header{
   175  					Name:     "hard.txt",
   176  					Mode:     0644,
   177  					Uid:      1000,
   178  					Gid:      100,
   179  					Size:     0,
   180  					ModTime:  time.Unix(1425484303, 0),
   181  					Typeflag: '1',
   182  					Linkname: "file.txt",
   183  					Uname:    "vbatts",
   184  					Gname:    "users",
   185  				},
   186  				// no contents
   187  			},
   188  		},
   189  	},
   190  }
   191  
   192  // Render byte array in a two-character hexadecimal string, spaced for easy visual inspection.
   193  func bytestr(offset int, b []byte) string {
   194  	const rowLen = 32
   195  	s := fmt.Sprintf("%04x ", offset)
   196  	for _, ch := range b {
   197  		switch {
   198  		case '0' <= ch && ch <= '9', 'A' <= ch && ch <= 'Z', 'a' <= ch && ch <= 'z':
   199  			s += fmt.Sprintf("  %c", ch)
   200  		default:
   201  			s += fmt.Sprintf(" %02x", ch)
   202  		}
   203  	}
   204  	return s
   205  }
   206  
   207  // Render a pseudo-diff between two blocks of bytes.
   208  func bytediff(a []byte, b []byte) string {
   209  	const rowLen = 32
   210  	s := fmt.Sprintf("(%d bytes vs. %d bytes)\n", len(a), len(b))
   211  	for offset := 0; len(a)+len(b) > 0; offset += rowLen {
   212  		na, nb := rowLen, rowLen
   213  		if na > len(a) {
   214  			na = len(a)
   215  		}
   216  		if nb > len(b) {
   217  			nb = len(b)
   218  		}
   219  		sa := bytestr(offset, a[0:na])
   220  		sb := bytestr(offset, b[0:nb])
   221  		if sa != sb {
   222  			s += fmt.Sprintf("-%v\n+%v\n", sa, sb)
   223  		}
   224  		a = a[na:]
   225  		b = b[nb:]
   226  	}
   227  	return s
   228  }
   229  
   230  func TestWriter(t *testing.T) {
   231  testLoop:
   232  	for i, test := range writerTests {
   233  		expected, err := ioutil.ReadFile(test.file)
   234  		if err != nil {
   235  			t.Errorf("test %d: Unexpected error: %v", i, err)
   236  			continue
   237  		}
   238  
   239  		buf := new(bytes.Buffer)
   240  		tw := NewWriter(iotest.TruncateWriter(buf, 4<<10)) // only catch the first 4 KB
   241  		big := false
   242  		for j, entry := range test.entries {
   243  			big = big || entry.header.Size > 1<<10
   244  			if err := tw.WriteHeader(entry.header); err != nil {
   245  				t.Errorf("test %d, entry %d: Failed writing header: %v", i, j, err)
   246  				continue testLoop
   247  			}
   248  			if _, err := io.WriteString(tw, entry.contents); err != nil {
   249  				t.Errorf("test %d, entry %d: Failed writing contents: %v", i, j, err)
   250  				continue testLoop
   251  			}
   252  		}
   253  		// Only interested in Close failures for the small tests.
   254  		if err := tw.Close(); err != nil && !big {
   255  			t.Errorf("test %d: Failed closing archive: %v", i, err)
   256  			continue testLoop
   257  		}
   258  
   259  		actual := buf.Bytes()
   260  		if !bytes.Equal(expected, actual) {
   261  			t.Errorf("test %d: Incorrect result: (-=expected, +=actual)\n%v",
   262  				i, bytediff(expected, actual))
   263  		}
   264  		if testing.Short() { // The second test is expensive.
   265  			break
   266  		}
   267  	}
   268  }
   269  
   270  func TestPax(t *testing.T) {
   271  	// Create an archive with a large name
   272  	fileinfo, err := os.Stat("testdata/small.txt")
   273  	if err != nil {
   274  		t.Fatal(err)
   275  	}
   276  	hdr, err := FileInfoHeader(fileinfo, "")
   277  	if err != nil {
   278  		t.Fatalf("os.Stat: %v", err)
   279  	}
   280  	// Force a PAX long name to be written
   281  	longName := strings.Repeat("ab", 100)
   282  	contents := strings.Repeat(" ", int(hdr.Size))
   283  	hdr.Name = longName
   284  	var buf bytes.Buffer
   285  	writer := NewWriter(&buf)
   286  	if err := writer.WriteHeader(hdr); err != nil {
   287  		t.Fatal(err)
   288  	}
   289  	if _, err = writer.Write([]byte(contents)); err != nil {
   290  		t.Fatal(err)
   291  	}
   292  	if err := writer.Close(); err != nil {
   293  		t.Fatal(err)
   294  	}
   295  	// Simple test to make sure PAX extensions are in effect
   296  	if !bytes.Contains(buf.Bytes(), []byte("PaxHeaders.0")) {
   297  		t.Fatal("Expected at least one PAX header to be written.")
   298  	}
   299  	// Test that we can get a long name back out of the archive.
   300  	reader := NewReader(&buf)
   301  	hdr, err = reader.Next()
   302  	if err != nil {
   303  		t.Fatal(err)
   304  	}
   305  	if hdr.Name != longName {
   306  		t.Fatal("Couldn't recover long file name")
   307  	}
   308  }
   309  
   310  func TestPaxSymlink(t *testing.T) {
   311  	// Create an archive with a large linkname
   312  	fileinfo, err := os.Stat("testdata/small.txt")
   313  	if err != nil {
   314  		t.Fatal(err)
   315  	}
   316  	hdr, err := FileInfoHeader(fileinfo, "")
   317  	hdr.Typeflag = TypeSymlink
   318  	if err != nil {
   319  		t.Fatalf("os.Stat:1 %v", err)
   320  	}
   321  	// Force a PAX long linkname to be written
   322  	longLinkname := strings.Repeat("1234567890/1234567890", 10)
   323  	hdr.Linkname = longLinkname
   324  
   325  	hdr.Size = 0
   326  	var buf bytes.Buffer
   327  	writer := NewWriter(&buf)
   328  	if err := writer.WriteHeader(hdr); err != nil {
   329  		t.Fatal(err)
   330  	}
   331  	if err := writer.Close(); err != nil {
   332  		t.Fatal(err)
   333  	}
   334  	// Simple test to make sure PAX extensions are in effect
   335  	if !bytes.Contains(buf.Bytes(), []byte("PaxHeaders.0")) {
   336  		t.Fatal("Expected at least one PAX header to be written.")
   337  	}
   338  	// Test that we can get a long name back out of the archive.
   339  	reader := NewReader(&buf)
   340  	hdr, err = reader.Next()
   341  	if err != nil {
   342  		t.Fatal(err)
   343  	}
   344  	if hdr.Linkname != longLinkname {
   345  		t.Fatal("Couldn't recover long link name")
   346  	}
   347  }
   348  
   349  func TestPaxNonAscii(t *testing.T) {
   350  	// Create an archive with non ascii. These should trigger a pax header
   351  	// because pax headers have a defined utf-8 encoding.
   352  	fileinfo, err := os.Stat("testdata/small.txt")
   353  	if err != nil {
   354  		t.Fatal(err)
   355  	}
   356  
   357  	hdr, err := FileInfoHeader(fileinfo, "")
   358  	if err != nil {
   359  		t.Fatalf("os.Stat:1 %v", err)
   360  	}
   361  
   362  	// some sample data
   363  	chineseFilename := "文件名"
   364  	chineseGroupname := "組"
   365  	chineseUsername := "用戶名"
   366  
   367  	hdr.Name = chineseFilename
   368  	hdr.Gname = chineseGroupname
   369  	hdr.Uname = chineseUsername
   370  
   371  	contents := strings.Repeat(" ", int(hdr.Size))
   372  
   373  	var buf bytes.Buffer
   374  	writer := NewWriter(&buf)
   375  	if err := writer.WriteHeader(hdr); err != nil {
   376  		t.Fatal(err)
   377  	}
   378  	if _, err = writer.Write([]byte(contents)); err != nil {
   379  		t.Fatal(err)
   380  	}
   381  	if err := writer.Close(); err != nil {
   382  		t.Fatal(err)
   383  	}
   384  	// Simple test to make sure PAX extensions are in effect
   385  	if !bytes.Contains(buf.Bytes(), []byte("PaxHeaders.0")) {
   386  		t.Fatal("Expected at least one PAX header to be written.")
   387  	}
   388  	// Test that we can get a long name back out of the archive.
   389  	reader := NewReader(&buf)
   390  	hdr, err = reader.Next()
   391  	if err != nil {
   392  		t.Fatal(err)
   393  	}
   394  	if hdr.Name != chineseFilename {
   395  		t.Fatal("Couldn't recover unicode name")
   396  	}
   397  	if hdr.Gname != chineseGroupname {
   398  		t.Fatal("Couldn't recover unicode group")
   399  	}
   400  	if hdr.Uname != chineseUsername {
   401  		t.Fatal("Couldn't recover unicode user")
   402  	}
   403  }
   404  
   405  func TestPaxXattrs(t *testing.T) {
   406  	xattrs := map[string]string{
   407  		"user.key": "value",
   408  	}
   409  
   410  	// Create an archive with an xattr
   411  	fileinfo, err := os.Stat("testdata/small.txt")
   412  	if err != nil {
   413  		t.Fatal(err)
   414  	}
   415  	hdr, err := FileInfoHeader(fileinfo, "")
   416  	if err != nil {
   417  		t.Fatalf("os.Stat: %v", err)
   418  	}
   419  	contents := "Kilts"
   420  	hdr.Xattrs = xattrs
   421  	var buf bytes.Buffer
   422  	writer := NewWriter(&buf)
   423  	if err := writer.WriteHeader(hdr); err != nil {
   424  		t.Fatal(err)
   425  	}
   426  	if _, err = writer.Write([]byte(contents)); err != nil {
   427  		t.Fatal(err)
   428  	}
   429  	if err := writer.Close(); err != nil {
   430  		t.Fatal(err)
   431  	}
   432  	// Test that we can get the xattrs back out of the archive.
   433  	reader := NewReader(&buf)
   434  	hdr, err = reader.Next()
   435  	if err != nil {
   436  		t.Fatal(err)
   437  	}
   438  	if !reflect.DeepEqual(hdr.Xattrs, xattrs) {
   439  		t.Fatalf("xattrs did not survive round trip: got %+v, want %+v",
   440  			hdr.Xattrs, xattrs)
   441  	}
   442  }
   443  
   444  func TestPaxHeadersSorted(t *testing.T) {
   445  	fileinfo, err := os.Stat("testdata/small.txt")
   446  	if err != nil {
   447  		t.Fatal(err)
   448  	}
   449  	hdr, err := FileInfoHeader(fileinfo, "")
   450  	if err != nil {
   451  		t.Fatalf("os.Stat: %v", err)
   452  	}
   453  	contents := strings.Repeat(" ", int(hdr.Size))
   454  
   455  	hdr.Xattrs = map[string]string{
   456  		"foo": "foo",
   457  		"bar": "bar",
   458  		"baz": "baz",
   459  		"qux": "qux",
   460  	}
   461  
   462  	var buf bytes.Buffer
   463  	writer := NewWriter(&buf)
   464  	if err := writer.WriteHeader(hdr); err != nil {
   465  		t.Fatal(err)
   466  	}
   467  	if _, err = writer.Write([]byte(contents)); err != nil {
   468  		t.Fatal(err)
   469  	}
   470  	if err := writer.Close(); err != nil {
   471  		t.Fatal(err)
   472  	}
   473  	// Simple test to make sure PAX extensions are in effect
   474  	if !bytes.Contains(buf.Bytes(), []byte("PaxHeaders.0")) {
   475  		t.Fatal("Expected at least one PAX header to be written.")
   476  	}
   477  
   478  	// xattr bar should always appear before others
   479  	indices := []int{
   480  		bytes.Index(buf.Bytes(), []byte("bar=bar")),
   481  		bytes.Index(buf.Bytes(), []byte("baz=baz")),
   482  		bytes.Index(buf.Bytes(), []byte("foo=foo")),
   483  		bytes.Index(buf.Bytes(), []byte("qux=qux")),
   484  	}
   485  	if !sort.IntsAreSorted(indices) {
   486  		t.Fatal("PAX headers are not sorted")
   487  	}
   488  }
   489  
   490  func TestUSTARLongName(t *testing.T) {
   491  	// Create an archive with a path that failed to split with USTAR extension in previous versions.
   492  	fileinfo, err := os.Stat("testdata/small.txt")
   493  	if err != nil {
   494  		t.Fatal(err)
   495  	}
   496  	hdr, err := FileInfoHeader(fileinfo, "")
   497  	hdr.Typeflag = TypeDir
   498  	if err != nil {
   499  		t.Fatalf("os.Stat:1 %v", err)
   500  	}
   501  	// Force a PAX long name to be written. The name was taken from a practical example
   502  	// that fails and replaced ever char through numbers to anonymize the sample.
   503  	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/"
   504  	hdr.Name = longName
   505  
   506  	hdr.Size = 0
   507  	var buf bytes.Buffer
   508  	writer := NewWriter(&buf)
   509  	if err := writer.WriteHeader(hdr); err != nil {
   510  		t.Fatal(err)
   511  	}
   512  	if err := writer.Close(); err != nil {
   513  		t.Fatal(err)
   514  	}
   515  	// Test that we can get a long name back out of the archive.
   516  	reader := NewReader(&buf)
   517  	hdr, err = reader.Next()
   518  	if err != nil {
   519  		t.Fatal(err)
   520  	}
   521  	if hdr.Name != longName {
   522  		t.Fatal("Couldn't recover long name")
   523  	}
   524  }
   525  
   526  func TestValidTypeflagWithPAXHeader(t *testing.T) {
   527  	var buffer bytes.Buffer
   528  	tw := NewWriter(&buffer)
   529  
   530  	fileName := strings.Repeat("ab", 100)
   531  
   532  	hdr := &Header{
   533  		Name:     fileName,
   534  		Size:     4,
   535  		Typeflag: 0,
   536  	}
   537  	if err := tw.WriteHeader(hdr); err != nil {
   538  		t.Fatalf("Failed to write header: %s", err)
   539  	}
   540  	if _, err := tw.Write([]byte("fooo")); err != nil {
   541  		t.Fatalf("Failed to write the file's data: %s", err)
   542  	}
   543  	tw.Close()
   544  
   545  	tr := NewReader(&buffer)
   546  
   547  	for {
   548  		header, err := tr.Next()
   549  		if err == io.EOF {
   550  			break
   551  		}
   552  		if err != nil {
   553  			t.Fatalf("Failed to read header: %s", err)
   554  		}
   555  		if header.Typeflag != 0 {
   556  			t.Fatalf("Typeflag should've been 0, found %d", header.Typeflag)
   557  		}
   558  	}
   559  }
   560  
   561  func TestWriteAfterClose(t *testing.T) {
   562  	var buffer bytes.Buffer
   563  	tw := NewWriter(&buffer)
   564  
   565  	hdr := &Header{
   566  		Name: "small.txt",
   567  		Size: 5,
   568  	}
   569  	if err := tw.WriteHeader(hdr); err != nil {
   570  		t.Fatalf("Failed to write header: %s", err)
   571  	}
   572  	tw.Close()
   573  	if _, err := tw.Write([]byte("Kilts")); err != ErrWriteAfterClose {
   574  		t.Fatalf("Write: got %v; want ErrWriteAfterClose", err)
   575  	}
   576  }
   577  
   578  func TestSplitUSTARPath(t *testing.T) {
   579  	var sr = strings.Repeat
   580  
   581  	var vectors = []struct {
   582  		input  string // Input path
   583  		prefix string // Expected output prefix
   584  		suffix string // Expected output suffix
   585  		ok     bool   // Split success?
   586  	}{
   587  		{"", "", "", false},
   588  		{"abc", "", "", false},
   589  		{"用戶名", "", "", false},
   590  		{sr("a", fileNameSize), "", "", false},
   591  		{sr("a", fileNameSize) + "/", "", "", false},
   592  		{sr("a", fileNameSize) + "/a", sr("a", fileNameSize), "a", true},
   593  		{sr("a", fileNamePrefixSize) + "/", "", "", false},
   594  		{sr("a", fileNamePrefixSize) + "/a", sr("a", fileNamePrefixSize), "a", true},
   595  		{sr("a", fileNameSize+1), "", "", false},
   596  		{sr("/", fileNameSize+1), sr("/", fileNameSize-1), "/", true},
   597  		{sr("a", fileNamePrefixSize) + "/" + sr("b", fileNameSize),
   598  			sr("a", fileNamePrefixSize), sr("b", fileNameSize), true},
   599  		{sr("a", fileNamePrefixSize) + "//" + sr("b", fileNameSize), "", "", false},
   600  		{sr("a/", fileNameSize), sr("a/", 77) + "a", sr("a/", 22), true},
   601  	}
   602  
   603  	for _, v := range vectors {
   604  		prefix, suffix, ok := splitUSTARPath(v.input)
   605  		if prefix != v.prefix || suffix != v.suffix || ok != v.ok {
   606  			t.Errorf("splitUSTARPath(%q):\ngot  (%q, %q, %v)\nwant (%q, %q, %v)",
   607  				v.input, prefix, suffix, ok, v.prefix, v.suffix, v.ok)
   608  		}
   609  	}
   610  }
   611  
   612  func TestFormatPAXRecord(t *testing.T) {
   613  	var medName = strings.Repeat("CD", 50)
   614  	var longName = strings.Repeat("AB", 100)
   615  
   616  	var vectors = []struct {
   617  		inputKey string
   618  		inputVal string
   619  		output   string
   620  	}{
   621  		{"k", "v", "6 k=v\n"},
   622  		{"path", "/etc/hosts", "19 path=/etc/hosts\n"},
   623  		{"path", longName, "210 path=" + longName + "\n"},
   624  		{"path", medName, "110 path=" + medName + "\n"},
   625  		{"foo", "ba", "9 foo=ba\n"},
   626  		{"foo", "bar", "11 foo=bar\n"},
   627  		{"foo", "b=\nar=\n==\x00", "18 foo=b=\nar=\n==\x00\n"},
   628  		{"foo", "hello9 foo=ba\nworld", "27 foo=hello9 foo=ba\nworld\n"},
   629  		{"☺☻☹", "日a本b語ç", "27 ☺☻☹=日a本b語ç\n"},
   630  		{"\x00hello", "\x00world", "17 \x00hello=\x00world\n"},
   631  	}
   632  
   633  	for _, v := range vectors {
   634  		output := formatPAXRecord(v.inputKey, v.inputVal)
   635  		if output != v.output {
   636  			t.Errorf("formatPAXRecord(%q, %q): got %q, want %q",
   637  				v.inputKey, v.inputVal, output, v.output)
   638  		}
   639  	}
   640  }
   641  
   642  func TestFitsInBase256(t *testing.T) {
   643  	var vectors = []struct {
   644  		input int64
   645  		width int
   646  		ok    bool
   647  	}{
   648  		{+1, 8, true},
   649  		{0, 8, true},
   650  		{-1, 8, true},
   651  		{1 << 56, 8, false},
   652  		{(1 << 56) - 1, 8, true},
   653  		{-1 << 56, 8, true},
   654  		{(-1 << 56) - 1, 8, false},
   655  		{121654, 8, true},
   656  		{-9849849, 8, true},
   657  		{math.MaxInt64, 9, true},
   658  		{0, 9, true},
   659  		{math.MinInt64, 9, true},
   660  		{math.MaxInt64, 12, true},
   661  		{0, 12, true},
   662  		{math.MinInt64, 12, true},
   663  	}
   664  
   665  	for _, v := range vectors {
   666  		ok := fitsInBase256(v.width, v.input)
   667  		if ok != v.ok {
   668  			t.Errorf("checkNumeric(%d, %d): got %v, want %v", v.input, v.width, ok, v.ok)
   669  		}
   670  	}
   671  }
   672  
   673  func TestFormatNumeric(t *testing.T) {
   674  	var vectors = []struct {
   675  		input  int64
   676  		output string
   677  		ok     bool
   678  	}{
   679  		// Test base-256 (binary) encoded values.
   680  		{-1, "\xff", true},
   681  		{-1, "\xff\xff", true},
   682  		{-1, "\xff\xff\xff", true},
   683  		{(1 << 0), "0", false},
   684  		{(1 << 8) - 1, "\x80\xff", true},
   685  		{(1 << 8), "0\x00", false},
   686  		{(1 << 16) - 1, "\x80\xff\xff", true},
   687  		{(1 << 16), "00\x00", false},
   688  		{-1 * (1 << 0), "\xff", true},
   689  		{-1*(1<<0) - 1, "0", false},
   690  		{-1 * (1 << 8), "\xff\x00", true},
   691  		{-1*(1<<8) - 1, "0\x00", false},
   692  		{-1 * (1 << 16), "\xff\x00\x00", true},
   693  		{-1*(1<<16) - 1, "00\x00", false},
   694  		{537795476381659745, "0000000\x00", false},
   695  		{537795476381659745, "\x80\x00\x00\x00\x07\x76\xa2\x22\xeb\x8a\x72\x61", true},
   696  		{-615126028225187231, "0000000\x00", false},
   697  		{-615126028225187231, "\xff\xff\xff\xff\xf7\x76\xa2\x22\xeb\x8a\x72\x61", true},
   698  		{math.MaxInt64, "0000000\x00", false},
   699  		{math.MaxInt64, "\x80\x00\x00\x00\x7f\xff\xff\xff\xff\xff\xff\xff", true},
   700  		{math.MinInt64, "0000000\x00", false},
   701  		{math.MinInt64, "\xff\xff\xff\xff\x80\x00\x00\x00\x00\x00\x00\x00", true},
   702  		{math.MaxInt64, "\x80\x7f\xff\xff\xff\xff\xff\xff\xff", true},
   703  		{math.MinInt64, "\xff\x80\x00\x00\x00\x00\x00\x00\x00", true},
   704  	}
   705  
   706  	for _, v := range vectors {
   707  		var f formatter
   708  		output := make([]byte, len(v.output))
   709  		f.formatNumeric(output, v.input)
   710  		ok := (f.err == nil)
   711  		if ok != v.ok {
   712  			if v.ok {
   713  				t.Errorf("formatNumeric(%d): got formatting failure, want success", v.input)
   714  			} else {
   715  				t.Errorf("formatNumeric(%d): got formatting success, want failure", v.input)
   716  			}
   717  		}
   718  		if string(output) != v.output {
   719  			t.Errorf("formatNumeric(%d): got %q, want %q", v.input, output, v.output)
   720  		}
   721  	}
   722  }