github.com/golang/dep@v0.5.4/context_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 dep
     6  
     7  import (
     8  	"fmt"
     9  	"io/ioutil"
    10  	"log"
    11  	"os"
    12  	"path/filepath"
    13  	"runtime"
    14  	"strings"
    15  	"testing"
    16  	"unicode"
    17  
    18  	"github.com/golang/dep/internal/test"
    19  )
    20  
    21  func discardLogger() *log.Logger {
    22  	return log.New(ioutil.Discard, "", 0)
    23  }
    24  
    25  func TestCtx_ProjectImport(t *testing.T) {
    26  	h := test.NewHelper(t)
    27  	defer h.Cleanup()
    28  
    29  	h.TempDir("src")
    30  
    31  	h.Setenv("GOPATH", h.Path("."))
    32  	depCtx := &Ctx{GOPATH: h.Path(".")}
    33  
    34  	importPaths := []string{
    35  		"github.com/pkg/errors",
    36  		"my/silly/thing",
    37  	}
    38  
    39  	for _, want := range importPaths {
    40  		fullpath := filepath.Join(depCtx.GOPATH, "src", want)
    41  		h.TempDir(filepath.Join("src", want))
    42  		got, err := depCtx.ImportForAbs(fullpath)
    43  		if err != nil {
    44  			t.Fatal(err)
    45  		}
    46  		if got != want {
    47  			t.Fatalf("expected %s, got %s", want, got)
    48  		}
    49  	}
    50  
    51  	// test where it should return an error when directly within $GOPATH/src
    52  	got, err := depCtx.ImportForAbs(filepath.Join(depCtx.GOPATH, "src"))
    53  	if err == nil || !strings.Contains(err.Error(), "GOPATH/src") {
    54  		t.Fatalf("should have gotten an error for use directly in GOPATH/src, but got %s", got)
    55  	}
    56  
    57  	// test where it should return an error
    58  	got, err = depCtx.ImportForAbs("tra/la/la/la")
    59  	if err == nil {
    60  		t.Fatalf("should have gotten an error but did not for tra/la/la/la: %s", got)
    61  	}
    62  }
    63  
    64  func TestAbsoluteProjectRoot(t *testing.T) {
    65  	h := test.NewHelper(t)
    66  	defer h.Cleanup()
    67  
    68  	h.TempDir("src")
    69  	h.Setenv("GOPATH", h.Path("."))
    70  	depCtx := &Ctx{GOPATH: h.Path(".")}
    71  
    72  	importPaths := map[string]bool{
    73  		"github.com/pkg/errors": true,
    74  		"my/silly/thing":        false,
    75  	}
    76  
    77  	for i, create := range importPaths {
    78  		if create {
    79  			h.TempDir(filepath.Join("src", i))
    80  		}
    81  	}
    82  
    83  	for i, ok := range importPaths {
    84  		got, err := depCtx.AbsForImport(i)
    85  		if ok {
    86  			h.Must(err)
    87  			want := h.Path(filepath.Join("src", i))
    88  			if got != want {
    89  				t.Fatalf("expected %s, got %q", want, got)
    90  			}
    91  			continue
    92  		}
    93  
    94  		if err == nil {
    95  			t.Fatalf("expected %s to fail", i)
    96  		}
    97  	}
    98  
    99  	// test that a file fails
   100  	h.TempFile("src/thing/thing.go", "hello world")
   101  	_, err := depCtx.AbsForImport("thing/thing.go")
   102  	if err == nil {
   103  		t.Fatal("error should not be nil for a file found")
   104  	}
   105  }
   106  
   107  func TestLoadProject(t *testing.T) {
   108  	h := test.NewHelper(t)
   109  	defer h.Cleanup()
   110  
   111  	h.TempDir(filepath.Join("src", "test1", "sub"))
   112  	h.TempFile(filepath.Join("src", "test1", ManifestName), "")
   113  	h.TempFile(filepath.Join("src", "test1", LockName), `memo = "cdafe8641b28cd16fe025df278b0a49b9416859345d8b6ba0ace0272b74925ee"`)
   114  	h.TempDir(filepath.Join("src", "test2", "sub"))
   115  	h.TempFile(filepath.Join("src", "test2", ManifestName), "")
   116  
   117  	var testcases = []struct {
   118  		name string
   119  		lock bool
   120  		wd   string
   121  	}{
   122  		{"direct", true, filepath.Join("src", "test1")},
   123  		{"ascending", true, filepath.Join("src", "test1", "sub")},
   124  		{"without lock", false, filepath.Join("src", "test2")},
   125  		{"ascending without lock", false, filepath.Join("src", "test2", "sub")},
   126  	}
   127  
   128  	for _, tc := range testcases {
   129  		t.Run(tc.name, func(t *testing.T) {
   130  			ctx := &Ctx{
   131  				Out: discardLogger(),
   132  				Err: discardLogger(),
   133  			}
   134  
   135  			err := ctx.SetPaths(h.Path(tc.wd), h.Path("."))
   136  			if err != nil {
   137  				t.Fatalf("%+v", err)
   138  			}
   139  
   140  			p, err := ctx.LoadProject()
   141  			switch {
   142  			case err != nil:
   143  				t.Fatalf("%s: LoadProject failed: %+v", tc.wd, err)
   144  			case p.Manifest == nil:
   145  				t.Fatalf("%s: Manifest file didn't load", tc.wd)
   146  			case tc.lock && p.Lock == nil:
   147  				t.Fatalf("%s: Lock file didn't load", tc.wd)
   148  			case !tc.lock && p.Lock != nil:
   149  				t.Fatalf("%s: Non-existent Lock file loaded", tc.wd)
   150  			}
   151  		})
   152  	}
   153  }
   154  
   155  func TestExplicitRootProject(t *testing.T) {
   156  	h := test.NewHelper(t)
   157  	defer h.Cleanup()
   158  
   159  	h.TempDir(filepath.Join("src", "test1", "sub"))
   160  	h.TempFile(filepath.Join("src", "test1", ManifestName), "")
   161  	h.TempFile(filepath.Join("src", "test1", LockName), `memo = "cdafe8641b28cd16fe025df278b0a49b9416859345d8b6ba0ace0272b74925ee"`)
   162  	h.TempDir(filepath.Join("src", "test2", "sub"))
   163  	h.TempFile(filepath.Join("src", "test2", ManifestName), "")
   164  	h.Setenv("DEP_PROJECT_ROOT", "github.com/user/module")
   165  
   166  	type tcase struct {
   167  		name string
   168  		lock bool
   169  		wd   string
   170  	}
   171  	var testcases = []tcase{
   172  		{"direct", true, filepath.Join("src", "test1")},
   173  		{"ascending", true, filepath.Join("src", "test1", "sub")},
   174  		{"without lock", false, filepath.Join("src", "test2")},
   175  		{"ascending without lock", false, filepath.Join("src", "test2", "sub")},
   176  	}
   177  
   178  	tf := func(withGOPATH bool, tc tcase, t *testing.T) func(t *testing.T) {
   179  		return func(t *testing.T) {
   180  			ctx := &Ctx{
   181  				Out: discardLogger(),
   182  				Err: discardLogger(),
   183  			}
   184  
   185  			var err error
   186  			if withGOPATH {
   187  				err = ctx.SetPaths(h.Path(tc.wd), h.Path("."))
   188  			} else {
   189  				err = ctx.SetPaths(h.Path(tc.wd))
   190  			}
   191  			if err != nil {
   192  				t.Fatalf("%+v", err)
   193  			}
   194  			ctx.ExplicitRoot = "github.com/user/module"
   195  
   196  			p, err := ctx.LoadProject()
   197  			switch {
   198  			case err != nil:
   199  				t.Fatalf("%s: LoadProject failed: %+v", tc.wd, err)
   200  			case p.Manifest == nil:
   201  				t.Fatalf("%s: Manifest file didn't load", tc.wd)
   202  			case tc.lock && p.Lock == nil:
   203  				t.Fatalf("%s: Lock file didn't load", tc.wd)
   204  			case !tc.lock && p.Lock != nil:
   205  				t.Fatalf("%s: Non-existent Lock file loaded", tc.wd)
   206  			}
   207  		}
   208  	}
   209  
   210  	for _, tc := range testcases {
   211  		t.Run(tc.name, func(t *testing.T) {
   212  			t.Run("within-GOPATH", tf(true, tc, t))
   213  			t.Run("outside-GOPATH", tf(false, tc, t))
   214  		})
   215  	}
   216  }
   217  
   218  func TestLoadProjectNotFoundErrors(t *testing.T) {
   219  	tg := test.NewHelper(t)
   220  	defer tg.Cleanup()
   221  
   222  	tg.TempDir("src")
   223  	tg.TempDir("src/test1")
   224  	tg.TempDir("src/test1/sub")
   225  	tg.Setenv("GOPATH", tg.Path("."))
   226  
   227  	var testcases = []struct {
   228  		lock  bool
   229  		start string
   230  		path  string
   231  	}{
   232  		{true, filepath.Join("src", "test1"), ""},        //direct
   233  		{true, filepath.Join("src", "test1", "sub"), ""}, //ascending
   234  	}
   235  
   236  	for _, testcase := range testcases {
   237  		ctx := &Ctx{GOPATHs: []string{tg.Path(".")}, WorkingDir: tg.Path(testcase.start)}
   238  
   239  		_, err := ctx.LoadProject()
   240  		if err == nil {
   241  			t.Errorf("%s: should have returned 'No Manifest Found' error", testcase.start)
   242  		}
   243  	}
   244  }
   245  
   246  func TestLoadProjectManifestParseError(t *testing.T) {
   247  	tg := test.NewHelper(t)
   248  	defer tg.Cleanup()
   249  
   250  	tg.TempDir("src")
   251  	tg.TempDir("src/test1")
   252  	tg.TempFile(filepath.Join("src/test1", ManifestName), `[[constraint]]`)
   253  	tg.TempFile(filepath.Join("src/test1", LockName), `memo = "cdafe8641b28cd16fe025df278b0a49b9416859345d8b6ba0ace0272b74925ee"\n\n[[projects]]`)
   254  	tg.Setenv("GOPATH", tg.Path("."))
   255  
   256  	path := filepath.Join("src", "test1")
   257  	tg.Cd(tg.Path(path))
   258  
   259  	wd, err := os.Getwd()
   260  	if err != nil {
   261  		t.Fatal("failed to get working directory", err)
   262  	}
   263  
   264  	ctx := &Ctx{
   265  		GOPATH:     tg.Path("."),
   266  		WorkingDir: wd,
   267  		Out:        discardLogger(),
   268  		Err:        discardLogger(),
   269  	}
   270  
   271  	_, err = ctx.LoadProject()
   272  	if err == nil {
   273  		t.Fatal("should have returned 'Manifest Syntax' error")
   274  	}
   275  }
   276  
   277  func TestLoadProjectLockParseError(t *testing.T) {
   278  	tg := test.NewHelper(t)
   279  	defer tg.Cleanup()
   280  
   281  	tg.TempDir("src")
   282  	tg.TempDir("src/test1")
   283  	tg.TempFile(filepath.Join("src/test1", ManifestName), `[[constraint]]`)
   284  	tg.TempFile(filepath.Join("src/test1", LockName), `memo = "cdafe8641b28cd16fe025df278b0a49b9416859345d8b6ba0ace0272b74925ee"\n\n[[projects]]`)
   285  	tg.Setenv("GOPATH", tg.Path("."))
   286  
   287  	path := filepath.Join("src", "test1")
   288  	tg.Cd(tg.Path(path))
   289  
   290  	wd, err := os.Getwd()
   291  	if err != nil {
   292  		t.Fatal("failed to get working directory", err)
   293  	}
   294  
   295  	ctx := &Ctx{
   296  		GOPATH:     tg.Path("."),
   297  		WorkingDir: wd,
   298  		Out:        discardLogger(),
   299  		Err:        discardLogger(),
   300  	}
   301  
   302  	_, err = ctx.LoadProject()
   303  	if err == nil {
   304  		t.Fatal("should have returned 'Lock Syntax' error")
   305  	}
   306  }
   307  
   308  func TestLoadProjectNoSrcDir(t *testing.T) {
   309  	tg := test.NewHelper(t)
   310  	defer tg.Cleanup()
   311  
   312  	tg.TempDir("test1")
   313  	tg.TempFile(filepath.Join("test1", ManifestName), `[[constraint]]`)
   314  	tg.TempFile(filepath.Join("test1", LockName), `memo = "cdafe8641b28cd16fe025df278b0a49b9416859345d8b6ba0ace0272b74925ee"\n\n[[projects]]`)
   315  	tg.Setenv("GOPATH", tg.Path("."))
   316  
   317  	ctx := &Ctx{GOPATH: tg.Path(".")}
   318  	path := filepath.Join("test1")
   319  	tg.Cd(tg.Path(path))
   320  
   321  	f, _ := os.OpenFile(filepath.Join(ctx.GOPATH, "src", "test1", LockName), os.O_WRONLY, os.ModePerm)
   322  	defer f.Close()
   323  
   324  	_, err := ctx.LoadProject()
   325  	if err == nil {
   326  		t.Fatal("should have returned 'Split Absolute Root' error (no 'src' dir present)")
   327  	}
   328  }
   329  
   330  func TestLoadProjectGopkgFilenames(t *testing.T) {
   331  	// We are trying to skip this test on file systems which are case-sensiive. We could
   332  	// have used `fs.IsCaseSensitiveFilesystem` for this check. However, the code we are
   333  	// testing also relies on `fs.IsCaseSensitiveFilesystem`. So a bug in
   334  	// `fs.IsCaseSensitiveFilesystem` could prevent this test from being run. This is the
   335  	// only scenario where we prefer the OS heuristic over doing the actual work of
   336  	// validating filesystem case sensitivity via `fs.IsCaseSensitiveFilesystem`.
   337  	if runtime.GOOS != "windows" && runtime.GOOS != "darwin" {
   338  		t.Skip("skip this test on non-Windows, non-macOS")
   339  	}
   340  
   341  	// Here we test that a manifest filename with incorrect case throws an error. Similar
   342  	// error will also be thrown for the lock file as well which has been tested in
   343  	// `project_test.go#TestCheckGopkgFilenames`. So not repeating here.
   344  
   345  	h := test.NewHelper(t)
   346  	defer h.Cleanup()
   347  
   348  	invalidMfName := strings.ToLower(ManifestName)
   349  
   350  	wd := filepath.Join("src", "test")
   351  	h.TempFile(filepath.Join(wd, invalidMfName), "")
   352  
   353  	ctx := &Ctx{
   354  		Out: discardLogger(),
   355  		Err: discardLogger(),
   356  	}
   357  
   358  	err := ctx.SetPaths(h.Path(wd), h.Path("."))
   359  	if err != nil {
   360  		t.Fatalf("%+v", err)
   361  	}
   362  
   363  	_, err = ctx.LoadProject()
   364  
   365  	if err == nil {
   366  		t.Fatal("should have returned 'Manifest Filename' error")
   367  	}
   368  
   369  	expectedErrMsg := fmt.Sprintf(
   370  		"manifest filename %q does not match %q",
   371  		invalidMfName, ManifestName,
   372  	)
   373  
   374  	if err.Error() != expectedErrMsg {
   375  		t.Fatalf("unexpected error: %+v", err)
   376  	}
   377  }
   378  
   379  // TestCaseInsensitive is test for Windows. This should work even though set
   380  // difference letter cases in GOPATH.
   381  func TestCaseInsensitiveGOPATH(t *testing.T) {
   382  	if runtime.GOOS != "windows" {
   383  		t.Skip("skip this test on non-Windows")
   384  	}
   385  
   386  	h := test.NewHelper(t)
   387  	defer h.Cleanup()
   388  
   389  	h.TempDir("src")
   390  	h.TempDir("src/test1")
   391  	h.TempFile(filepath.Join("src/test1", ManifestName), `
   392  	[[constraint]]
   393  		name = "github.com/foo/bar"
   394  		branch = "master"`)
   395  
   396  	// Shuffle letter case
   397  	rs := []rune(strings.ToLower(h.Path(".")))
   398  	for i, r := range rs {
   399  		if unicode.IsLower(r) {
   400  			rs[i] = unicode.ToUpper(r)
   401  		} else {
   402  			rs[i] = unicode.ToLower(r)
   403  		}
   404  	}
   405  	gopath := string(rs)
   406  	h.Setenv("GOPATH", gopath)
   407  	wd := h.Path("src/test1")
   408  
   409  	depCtx := &Ctx{}
   410  	if err := depCtx.SetPaths(wd, gopath); err != nil {
   411  		t.Fatal(err)
   412  	}
   413  	if _, err := depCtx.LoadProject(); err != nil {
   414  		t.Fatal(err)
   415  	}
   416  
   417  	ip := "github.com/pkg/errors"
   418  	fullpath := filepath.Join(depCtx.GOPATH, "src", ip)
   419  	h.TempDir(filepath.Join("src", ip))
   420  	pr, err := depCtx.ImportForAbs(fullpath)
   421  	if err != nil {
   422  		t.Fatal(err)
   423  	}
   424  	if pr != ip {
   425  		t.Fatalf("expected %s, got %s", ip, pr)
   426  	}
   427  }
   428  
   429  func TestDetectProjectGOPATH(t *testing.T) {
   430  	h := test.NewHelper(t)
   431  	defer h.Cleanup()
   432  
   433  	h.TempDir(filepath.Join("sym", "symlink"))
   434  	h.TempDir(filepath.Join("go", "src", "sym", "path"))
   435  	h.TempDir(filepath.Join("go", "src", "real", "path"))
   436  	h.TempDir(filepath.Join("go-two", "src", "real", "path"))
   437  	h.TempDir(filepath.Join("go-two", "src", "sym"))
   438  
   439  	ctx := &Ctx{
   440  		GOPATHs: []string{h.Path("go"), h.Path("go-two")},
   441  	}
   442  
   443  	testcases := []struct {
   444  		name         string
   445  		root         string
   446  		resolvedRoot string
   447  		GOPATH       string
   448  		expectErr    bool
   449  	}{
   450  		{
   451  			name:         "project-with-no-AbsRoot",
   452  			root:         "",
   453  			resolvedRoot: filepath.Join(ctx.GOPATHs[0], "src", "real", "path"),
   454  			expectErr:    true,
   455  		},
   456  		{
   457  			name:         "project-with-no-ResolvedAbsRoot",
   458  			root:         filepath.Join(ctx.GOPATHs[0], "src", "real", "path"),
   459  			resolvedRoot: "",
   460  			expectErr:    true,
   461  		},
   462  		{
   463  			name:         "AbsRoot-is-not-within-any-GOPATH",
   464  			root:         filepath.Join(h.Path("."), "src", "real", "path"),
   465  			resolvedRoot: filepath.Join(h.Path("."), "src", "real", "path"),
   466  			expectErr:    true,
   467  		},
   468  		{
   469  			name:         "neither-AbsRoot-nor-ResolvedAbsRoot-are-in-any-GOPATH",
   470  			root:         filepath.Join(h.Path("."), "src", "sym", "path"),
   471  			resolvedRoot: filepath.Join(h.Path("."), "src", "real", "path"),
   472  			expectErr:    true,
   473  		},
   474  		{
   475  			name:         "both-AbsRoot-and-ResolvedAbsRoot-are-in-the-same-GOPATH",
   476  			root:         filepath.Join(ctx.GOPATHs[0], "src", "sym", "path"),
   477  			resolvedRoot: filepath.Join(ctx.GOPATHs[0], "src", "real", "path"),
   478  			expectErr:    true,
   479  		},
   480  		{
   481  			name:         "AbsRoot-and-ResolvedAbsRoot-are-each-within-a-different-GOPATH",
   482  			root:         filepath.Join(ctx.GOPATHs[0], "src", "sym", "path"),
   483  			resolvedRoot: filepath.Join(ctx.GOPATHs[1], "src", "real", "path"),
   484  			expectErr:    true,
   485  		},
   486  		{
   487  			name:         "AbsRoot-is-not-a-symlink",
   488  			root:         filepath.Join(ctx.GOPATHs[0], "src", "real", "path"),
   489  			resolvedRoot: filepath.Join(ctx.GOPATHs[0], "src", "real", "path"),
   490  			GOPATH:       ctx.GOPATHs[0],
   491  		},
   492  		{
   493  			name:         "AbsRoot-is-a-symlink-to-ResolvedAbsRoot",
   494  			root:         filepath.Join(h.Path("."), "sym", "symlink"),
   495  			resolvedRoot: filepath.Join(ctx.GOPATHs[0], "src", "real", "path"),
   496  			GOPATH:       ctx.GOPATHs[0],
   497  		},
   498  	}
   499  
   500  	for _, tc := range testcases {
   501  		t.Run(tc.name, func(t *testing.T) {
   502  			project := &Project{
   503  				AbsRoot:         tc.root,
   504  				ResolvedAbsRoot: tc.resolvedRoot,
   505  			}
   506  
   507  			GOPATH, err := ctx.DetectProjectGOPATH(project)
   508  			if !tc.expectErr && err != nil {
   509  				t.Fatalf("%+v", err)
   510  			} else if tc.expectErr && err == nil {
   511  				t.Fatalf("expected an error, got nil and gopath %s", GOPATH)
   512  			}
   513  			if GOPATH != tc.GOPATH {
   514  				t.Errorf("expected GOPATH %s, got %s", tc.GOPATH, GOPATH)
   515  			}
   516  		})
   517  	}
   518  }
   519  
   520  func TestDetectGOPATH(t *testing.T) {
   521  	th := test.NewHelper(t)
   522  	defer th.Cleanup()
   523  
   524  	th.TempDir(filepath.Join("code", "src", "github.com", "username", "package"))
   525  	th.TempDir(filepath.Join("go", "src", "github.com", "username", "package"))
   526  	th.TempDir(filepath.Join("gotwo", "src", "github.com", "username", "package"))
   527  	th.TempDir(filepath.Join("gothree", "sep", "src", "github.com", "username", "package"))
   528  
   529  	sep := string(os.PathSeparator)
   530  
   531  	ctx := &Ctx{GOPATHs: []string{
   532  		th.Path("go"),
   533  		th.Path("gotwo"),
   534  		th.Path("gothree") + sep + sep + "sep",
   535  	}}
   536  
   537  	testcases := []struct {
   538  		GOPATH string
   539  		path   string
   540  		err    bool
   541  	}{
   542  		{th.Path("go"), th.Path(filepath.Join("go", "src", "github.com", "username", "package")), false},
   543  		{th.Path("go"), th.Path(filepath.Join("go", "src", "github.com", "username", "package")), false},
   544  		{th.Path("gotwo"), th.Path(filepath.Join("gotwo", "src", "github.com", "username", "package")), false},
   545  		{th.Path(filepath.Join("gothree", "sep")),
   546  			th.Path(filepath.Join("gothree", "sep", "src", "github.com", "username", "package")), false},
   547  		{"", th.Path(filepath.Join("code", "src", "github.com", "username", "package")), true},
   548  	}
   549  
   550  	for _, tc := range testcases {
   551  		GOPATH, err := ctx.detectGOPATH(tc.path)
   552  		if tc.err && err == nil {
   553  			t.Error("expected error but got none")
   554  		}
   555  		if GOPATH != tc.GOPATH {
   556  			t.Errorf("expected GOPATH to be %s, got %s", tc.GOPATH, GOPATH)
   557  		}
   558  	}
   559  }
   560  
   561  func TestDepCachedir(t *testing.T) {
   562  	h := test.NewHelper(t)
   563  	defer h.Cleanup()
   564  
   565  	h.TempDir("cache")
   566  	// Create the directory for default cachedir location.
   567  	h.TempDir(filepath.Join("go", "pkg", "dep"))
   568  
   569  	testCachedir := h.Path("cache")
   570  	gopath := h.Path("go")
   571  	discardLgr := discardLogger()
   572  
   573  	cases := []struct {
   574  		cachedir     string
   575  		wantCachedir string
   576  	}{
   577  		// If `Cachedir` is not set in the context, it should use `$GOPATH/pkg/dep`.
   578  		{cachedir: "", wantCachedir: h.Path(filepath.Join("go", "pkg", "dep"))},
   579  		// If `Cachedir` is set in the context, it should use that.
   580  		{cachedir: testCachedir, wantCachedir: testCachedir},
   581  	}
   582  
   583  	for _, c := range cases {
   584  		ctx := &Ctx{
   585  			GOPATH:   gopath,
   586  			Cachedir: c.cachedir,
   587  			Out:      discardLgr,
   588  			Err:      discardLgr,
   589  		}
   590  		sm, err := ctx.SourceManager()
   591  		h.Must(err)
   592  		defer sm.Release()
   593  
   594  		if sm.Cachedir() != c.wantCachedir {
   595  			t.Errorf("expected cachedir to be %s, got %s", c.wantCachedir, sm.Cachedir())
   596  		}
   597  	}
   598  }