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  }