github.com/golang/dep@v0.5.4/gps/verify/digest_test.go (about)

     1  // Copyright 2017 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 verify
     6  
     7  import (
     8  	"bytes"
     9  	"io"
    10  	"os"
    11  	"path/filepath"
    12  	"testing"
    13  )
    14  
    15  // crossBuffer is a test io.Reader that emits a few canned responses.
    16  type crossBuffer struct {
    17  	readCount  int
    18  	iterations []string
    19  }
    20  
    21  func (cb *crossBuffer) Read(buf []byte) (int, error) {
    22  	if cb.readCount == len(cb.iterations) {
    23  		return 0, io.EOF
    24  	}
    25  	cb.readCount++
    26  	return copy(buf, cb.iterations[cb.readCount-1]), nil
    27  }
    28  
    29  func streamThruLineEndingReader(t *testing.T, iterations []string) []byte {
    30  	dst := new(bytes.Buffer)
    31  	n, err := io.Copy(dst, newLineEndingReader(&crossBuffer{iterations: iterations}))
    32  	if got, want := err, error(nil); got != want {
    33  		t.Errorf("(GOT): %v; (WNT): %v", got, want)
    34  	}
    35  	if got, want := n, int64(dst.Len()); got != want {
    36  		t.Errorf("(GOT): %v; (WNT): %v", got, want)
    37  	}
    38  	return dst.Bytes()
    39  }
    40  
    41  func TestLineEndingReader(t *testing.T) {
    42  	testCases := []struct {
    43  		input  []string
    44  		output string
    45  	}{
    46  		{[]string{"\r"}, "\r"},
    47  		{[]string{"\r\n"}, "\n"},
    48  		{[]string{"now is the time\r\n"}, "now is the time\n"},
    49  		{[]string{"now is the time\r\n(trailing data)"}, "now is the time\n(trailing data)"},
    50  		{[]string{"now is the time\n"}, "now is the time\n"},
    51  		{[]string{"now is the time\r"}, "now is the time\r"},     // trailing CR ought to convey
    52  		{[]string{"\rnow is the time"}, "\rnow is the time"},     // CR not followed by LF ought to convey
    53  		{[]string{"\rnow is the time\r"}, "\rnow is the time\r"}, // CR not followed by LF ought to convey
    54  
    55  		// no line splits
    56  		{[]string{"first", "second", "third"}, "firstsecondthird"},
    57  
    58  		// 1->2 and 2->3 both break across a CRLF
    59  		{[]string{"first\r", "\nsecond\r", "\nthird"}, "first\nsecond\nthird"},
    60  
    61  		// 1->2 breaks across CRLF and 2->3 does not
    62  		{[]string{"first\r", "\nsecond", "third"}, "first\nsecondthird"},
    63  
    64  		// 1->2 breaks across CRLF and 2 ends in CR but 3 does not begin LF
    65  		{[]string{"first\r", "\nsecond\r", "third"}, "first\nsecond\rthird"},
    66  
    67  		// 1 ends in CR but 2 does not begin LF, and 2->3 breaks across CRLF
    68  		{[]string{"first\r", "second\r", "\nthird"}, "first\rsecond\nthird"},
    69  
    70  		// 1 ends in CR but 2 does not begin LF, and 2->3 does not break across CRLF
    71  		{[]string{"first\r", "second\r", "\nthird"}, "first\rsecond\nthird"},
    72  
    73  		// 1->2 and 2->3 both break across a CRLF, but 3->4 does not
    74  		{[]string{"first\r", "\nsecond\r", "\nthird\r", "fourth"}, "first\nsecond\nthird\rfourth"},
    75  		{[]string{"first\r", "\nsecond\r", "\nthird\n", "fourth"}, "first\nsecond\nthird\nfourth"},
    76  
    77  		{[]string{"this is the result\r\nfrom the first read\r", "\nthis is the result\r\nfrom the second read\r"},
    78  			"this is the result\nfrom the first read\nthis is the result\nfrom the second read\r"},
    79  		{[]string{"now is the time\r\nfor all good engineers\r\nto improve their test coverage!\r\n"},
    80  			"now is the time\nfor all good engineers\nto improve their test coverage!\n"},
    81  		{[]string{"now is the time\r\nfor all good engineers\r", "\nto improve their test coverage!\r\n"},
    82  			"now is the time\nfor all good engineers\nto improve their test coverage!\n"},
    83  	}
    84  
    85  	for _, testCase := range testCases {
    86  		got := streamThruLineEndingReader(t, testCase.input)
    87  		if want := []byte(testCase.output); !bytes.Equal(got, want) {
    88  			t.Errorf("Input: %#v; (GOT): %#q; (WNT): %#q", testCase.input, got, want)
    89  		}
    90  	}
    91  }
    92  
    93  ////////////////////////////////////////
    94  
    95  func getTestdataVerifyRoot(t *testing.T) string {
    96  	cwd, err := os.Getwd()
    97  	if err != nil {
    98  		t.Fatal(err)
    99  	}
   100  	return filepath.Join(filepath.Dir(cwd), "_testdata/digest")
   101  }
   102  
   103  func TestDigestFromDirectoryBailsUnlessDirectory(t *testing.T) {
   104  	prefix := getTestdataVerifyRoot(t)
   105  	relativePathname := "launchpad.net/match"
   106  	_, err := DigestFromDirectory(filepath.Join(prefix, relativePathname))
   107  	if got, want := err, error(nil); got != want {
   108  		t.Errorf("\n(GOT): %v; (WNT): %v", got, want)
   109  	}
   110  }
   111  
   112  func TestDigestFromDirectory(t *testing.T) {
   113  	relativePathname := "launchpad.net/match"
   114  	want := []byte{0x7e, 0x10, 0x6, 0x2f, 0x8, 0x3, 0x3c, 0x76, 0xae, 0xbc, 0xa4, 0xc9, 0xec, 0x73, 0x67, 0x15, 0x70, 0x2b, 0x0, 0x89, 0x27, 0xbb, 0x61, 0x9d, 0xc7, 0xc3, 0x39, 0x46, 0x3, 0x91, 0xb7, 0x3b}
   115  
   116  	// NOTE: Create the hash using both an absolute and a relative pathname to
   117  	// ensure hash ignores prefix.
   118  
   119  	t.Run("AbsolutePrefix", func(t *testing.T) {
   120  		t.Parallel()
   121  		prefix := getTestdataVerifyRoot(t)
   122  		got, err := DigestFromDirectory(filepath.Join(prefix, relativePathname))
   123  		if err != nil {
   124  			t.Fatal(err)
   125  		}
   126  		if !bytes.Equal(got.Digest, want) {
   127  			t.Errorf("\n(GOT):\n\t%#v\n(WNT):\n\t%#v", got, want)
   128  		}
   129  	})
   130  
   131  	t.Run("RelativePrefix", func(t *testing.T) {
   132  		t.Parallel()
   133  		prefix := "../_testdata/digest"
   134  		got, err := DigestFromDirectory(filepath.Join(prefix, relativePathname))
   135  		if err != nil {
   136  			t.Fatal(err)
   137  		}
   138  		if !bytes.Equal(got.Digest, want) {
   139  			t.Errorf("\n(GOT):\n\t%#v\n(WNT):\n\t%#v", got, want)
   140  		}
   141  	})
   142  }
   143  
   144  func TestVerifyDepTree(t *testing.T) {
   145  	vendorRoot := getTestdataVerifyRoot(t)
   146  
   147  	wantSums := map[string][]byte{
   148  		"github.com/alice/match":       {0x7e, 0x10, 0x6, 0x2f, 0x8, 0x3, 0x3c, 0x76, 0xae, 0xbc, 0xa4, 0xc9, 0xec, 0x73, 0x67, 0x15, 0x70, 0x2b, 0x0, 0x89, 0x27, 0xbb, 0x61, 0x9d, 0xc7, 0xc3, 0x39, 0x46, 0x3, 0x91, 0xb7, 0x3b},
   149  		"github.com/alice/mismatch":    []byte("some non-matching digest"),
   150  		"github.com/bob/emptyDigest":   nil, // empty hash result
   151  		"github.com/bob/match":         {0x7e, 0x10, 0x6, 0x2f, 0x8, 0x3, 0x3c, 0x76, 0xae, 0xbc, 0xa4, 0xc9, 0xec, 0x73, 0x67, 0x15, 0x70, 0x2b, 0x0, 0x89, 0x27, 0xbb, 0x61, 0x9d, 0xc7, 0xc3, 0x39, 0x46, 0x3, 0x91, 0xb7, 0x3b},
   152  		"github.com/charlie/notInTree": nil, // not in tree result ought to superseede empty digest result
   153  		// matching result at seldom found directory level
   154  		"launchpad.net/match": {0x7e, 0x10, 0x6, 0x2f, 0x8, 0x3, 0x3c, 0x76, 0xae, 0xbc, 0xa4, 0xc9, 0xec, 0x73, 0x67, 0x15, 0x70, 0x2b, 0x0, 0x89, 0x27, 0xbb, 0x61, 0x9d, 0xc7, 0xc3, 0x39, 0x46, 0x3, 0x91, 0xb7, 0x3b},
   155  	}
   156  
   157  	checkStatus := func(t *testing.T, status map[string]VendorStatus, key string, want VendorStatus) {
   158  		t.Helper()
   159  		got, ok := status[key]
   160  		if !ok {
   161  			t.Errorf("Want key: %q", key)
   162  			return
   163  		}
   164  		if got != want {
   165  			t.Errorf("Key: %q; (GOT): %v; (WNT): %v", key, got, want)
   166  		}
   167  	}
   168  
   169  	t.Run("normal", func(t *testing.T) {
   170  		t.Parallel()
   171  		wantDigests := make(map[string]VersionedDigest)
   172  		for k, v := range wantSums {
   173  			wantDigests[k] = VersionedDigest{
   174  				HashVersion: HashVersion,
   175  				Digest:      v,
   176  			}
   177  		}
   178  
   179  		status, err := CheckDepTree(vendorRoot, wantDigests)
   180  		if err != nil {
   181  			t.Fatal(err)
   182  		}
   183  
   184  		if got, want := len(status), 7; got != want {
   185  			t.Errorf("Unexpected result count from VerifyDepTree:\n\t(GOT): %v\n\t(WNT): %v", got, want)
   186  		}
   187  
   188  		checkStatus(t, status, "github.com/alice/match", NoMismatch)
   189  		checkStatus(t, status, "github.com/alice/mismatch", DigestMismatchInLock)
   190  		checkStatus(t, status, "github.com/alice/notInLock", NotInLock)
   191  		checkStatus(t, status, "github.com/bob/match", NoMismatch)
   192  		checkStatus(t, status, "github.com/bob/emptyDigest", EmptyDigestInLock)
   193  		checkStatus(t, status, "github.com/charlie/notInTree", NotInTree)
   194  		checkStatus(t, status, "launchpad.net/match", NoMismatch)
   195  
   196  		if t.Failed() {
   197  			for k, want := range wantSums {
   198  				got, err := DigestFromDirectory(filepath.Join(vendorRoot, k))
   199  				if err != nil {
   200  					t.Error(err)
   201  				}
   202  				if !bytes.Equal(got.Digest, want) {
   203  					t.Errorf("Digest mismatch for %q\n(GOT):\n\t%#v\n(WNT):\n\t%#v", k, got, want)
   204  				}
   205  			}
   206  		}
   207  
   208  	})
   209  
   210  	t.Run("hashv-mismatch", func(t *testing.T) {
   211  		t.Parallel()
   212  		wantDigests := make(map[string]VersionedDigest)
   213  		for k, v := range wantSums {
   214  			wantDigests[k] = VersionedDigest{
   215  				HashVersion: HashVersion + 1,
   216  				Digest:      v,
   217  			}
   218  		}
   219  
   220  		status, err := CheckDepTree(vendorRoot, wantDigests)
   221  		if err != nil {
   222  			t.Fatal(err)
   223  		}
   224  
   225  		if got, want := len(status), 7; got != want {
   226  			t.Errorf("Unexpected result count from VerifyDepTree:\n\t(GOT): %v\n\t(WNT): %v", got, want)
   227  		}
   228  
   229  		checkStatus(t, status, "github.com/alice/match", HashVersionMismatch)
   230  		checkStatus(t, status, "github.com/alice/mismatch", HashVersionMismatch)
   231  		checkStatus(t, status, "github.com/alice/notInLock", NotInLock)
   232  		checkStatus(t, status, "github.com/bob/match", HashVersionMismatch)
   233  		checkStatus(t, status, "github.com/bob/emptyDigest", HashVersionMismatch)
   234  		checkStatus(t, status, "github.com/charlie/notInTree", NotInTree)
   235  		checkStatus(t, status, "launchpad.net/match", HashVersionMismatch)
   236  	})
   237  
   238  	t.Run("Non-existent directory", func(t *testing.T) {
   239  		t.Parallel()
   240  		wantDigests := make(map[string]VersionedDigest)
   241  		for k, v := range wantSums {
   242  			wantDigests[k] = VersionedDigest{
   243  				HashVersion: HashVersion + 1,
   244  				Digest:      v,
   245  			}
   246  		}
   247  
   248  		status, err := CheckDepTree("fooVendorRoot", wantDigests)
   249  		if err != nil {
   250  			t.Fatal(err)
   251  		}
   252  
   253  		if got, want := len(status), 6; got != want {
   254  			t.Errorf("Unexpected result count from VerifyDepTree:\n\t(GOT): %v\n\t(WNT): %v", got, want)
   255  		}
   256  
   257  		checkStatus(t, status, "github.com/alice/match", NotInTree)
   258  		checkStatus(t, status, "github.com/alice/mismatch", NotInTree)
   259  		checkStatus(t, status, "github.com/bob/match", NotInTree)
   260  		checkStatus(t, status, "github.com/bob/emptyDigest", NotInTree)
   261  		checkStatus(t, status, "github.com/charlie/notInTree", NotInTree)
   262  		checkStatus(t, status, "launchpad.net/match", NotInTree)
   263  
   264  	})
   265  }
   266  
   267  func TestParseVersionedDigest(t *testing.T) {
   268  	t.Run("Parse valid VersionedDigest", func(t *testing.T) {
   269  		t.Parallel()
   270  		input := "1:60861e762bdbe39c4c7bf292c291329b731c9925388fd41125888f5c1c595feb"
   271  		vd, err := ParseVersionedDigest(input)
   272  		if err != nil {
   273  			t.Fatal()
   274  		}
   275  
   276  		expectedHash := "60861e762bdbe39c4c7bf292c291329b731c9925388fd41125888f5c1c595feb"
   277  		if got, want := vd.Digest, expectedHash; bytes.Equal(got, []byte(expectedHash)) {
   278  			t.Errorf("Unexpected result from ParseVersionedDigest:\n\t(GOT): %s\n\t(WNT): %s", got, want)
   279  		}
   280  
   281  		if got, want := vd.String(), input; got != want {
   282  			t.Errorf("Unexpected result from ParseVersionedDigest String:\n\t(GOT): %s\n\t(WNT): %s", got, want)
   283  		}
   284  	})
   285  
   286  	t.Run("Parse VersionedDigest with invalid format", func(t *testing.T) {
   287  		t.Parallel()
   288  		input := "1abc"
   289  		_, err := ParseVersionedDigest(input)
   290  		if err == nil {
   291  			t.Error("expected error for invalid VersionedDigest format")
   292  		}
   293  	})
   294  
   295  	t.Run("Parse VersionedDigest with invalid hex string", func(t *testing.T) {
   296  		t.Parallel()
   297  		input := "1:60861g762bdbe39c4c7bf292c291329b731c9925388fd41125888f5c1c595feb"
   298  		_, err := ParseVersionedDigest(input)
   299  		if err == nil {
   300  			t.Error("expected error VersionedDigest with invalid hex string")
   301  		}
   302  	})
   303  
   304  	t.Run("Parse VersionedDigest with invalid hash version", func(t *testing.T) {
   305  		t.Parallel()
   306  		input := "a:60861e762bdbe39c4c7bf292c291329b731c9925388fd41125888f5c1c595feb"
   307  		_, err := ParseVersionedDigest(input)
   308  		if err == nil {
   309  			t.Error("expected error VersionedDigest with invalid hash version")
   310  		}
   311  	})
   312  }
   313  
   314  func BenchmarkDigestFromDirectory(b *testing.B) {
   315  	b.Skip("Eliding benchmark of user's Go source directory")
   316  
   317  	prefix := filepath.Join(os.Getenv("GOPATH"), "src")
   318  
   319  	for i := 0; i < b.N; i++ {
   320  		_, err := DigestFromDirectory(prefix)
   321  		if err != nil {
   322  			b.Fatal(err)
   323  		}
   324  	}
   325  }
   326  
   327  func BenchmarkVerifyDepTree(b *testing.B) {
   328  	b.Skip("Eliding benchmark of user's Go source directory")
   329  
   330  	prefix := filepath.Join(os.Getenv("GOPATH"), "src")
   331  
   332  	for i := 0; i < b.N; i++ {
   333  		_, err := CheckDepTree(prefix, nil)
   334  		if err != nil {
   335  			b.Fatal(err)
   336  		}
   337  	}
   338  }