github.com/munnerz/test-infra@v0.0.0-20190108210205-ce3d181dc989/prow/pod-utils/clone/clone_test.go (about)

     1  /*
     2  Copyright 2018 The Kubernetes Authors.
     3  
     4  Licensed under the Apache License, Version 2.0 (the "License");
     5  you may not use this file except in compliance with the License.
     6  You may obtain a copy of the License at
     7  
     8      http://www.apache.org/licenses/LICENSE-2.0
     9  
    10  Unless required by applicable law or agreed to in writing, software
    11  distributed under the License is distributed on an "AS IS" BASIS,
    12  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13  See the License for the specific language governing permissions and
    14  limitations under the License.
    15  */
    16  
    17  package clone
    18  
    19  import (
    20  	"io/ioutil"
    21  	"os"
    22  	"os/exec"
    23  	"reflect"
    24  	"testing"
    25  
    26  	"k8s.io/apimachinery/pkg/util/diff"
    27  	"k8s.io/test-infra/prow/kube"
    28  )
    29  
    30  func TestPathForRefs(t *testing.T) {
    31  	var testCases = []struct {
    32  		name     string
    33  		refs     kube.Refs
    34  		expected string
    35  	}{
    36  		{
    37  			name: "literal override",
    38  			refs: kube.Refs{
    39  				PathAlias: "alias",
    40  			},
    41  			expected: "base/src/alias",
    42  		},
    43  		{
    44  			name: "default generated",
    45  			refs: kube.Refs{
    46  				Org:  "org",
    47  				Repo: "repo",
    48  			},
    49  			expected: "base/src/github.com/org/repo",
    50  		},
    51  	}
    52  
    53  	for _, testCase := range testCases {
    54  		if actual, expected := PathForRefs("base", testCase.refs), testCase.expected; actual != expected {
    55  			t.Errorf("%s: expected path %q, got %q", testCase.name, expected, actual)
    56  		}
    57  	}
    58  }
    59  
    60  func TestCommandsForRefs(t *testing.T) {
    61  	fakeTimestamp := 100200300
    62  	var testCases = []struct {
    63  		name                                       string
    64  		refs                                       kube.Refs
    65  		dir, gitUserName, gitUserEmail, cookiePath string
    66  		env                                        []string
    67  		expectedBase                               []cloneCommand
    68  		expectedPull                               []cloneCommand
    69  	}{
    70  		{
    71  			name: "simplest case, minimal refs",
    72  			refs: kube.Refs{
    73  				Org:     "org",
    74  				Repo:    "repo",
    75  				BaseRef: "master",
    76  			},
    77  			dir: "/go",
    78  			expectedBase: []cloneCommand{
    79  				{dir: "/", command: "mkdir", args: []string{"-p", "/go/src/github.com/org/repo"}},
    80  				{dir: "/go/src/github.com/org/repo", command: "git", args: []string{"init"}},
    81  				{dir: "/go/src/github.com/org/repo", command: "git", args: []string{"fetch", "https://github.com/org/repo.git", "--tags", "--prune"}},
    82  				{dir: "/go/src/github.com/org/repo", command: "git", args: []string{"fetch", "https://github.com/org/repo.git", "master"}},
    83  				{dir: "/go/src/github.com/org/repo", command: "git", args: []string{"checkout", "FETCH_HEAD"}},
    84  				{dir: "/go/src/github.com/org/repo", command: "git", args: []string{"branch", "--force", "master", "FETCH_HEAD"}},
    85  				{dir: "/go/src/github.com/org/repo", command: "git", args: []string{"checkout", "master"}},
    86  			},
    87  			expectedPull: []cloneCommand{
    88  				{dir: "/go/src/github.com/org/repo", command: "git", args: []string{"submodule", "update", "--init", "--recursive"}},
    89  			},
    90  		},
    91  		{
    92  			name: "minimal refs with git user name",
    93  			refs: kube.Refs{
    94  				Org:     "org",
    95  				Repo:    "repo",
    96  				BaseRef: "master",
    97  			},
    98  			gitUserName: "user",
    99  			dir:         "/go",
   100  			expectedBase: []cloneCommand{
   101  				{dir: "/", command: "mkdir", args: []string{"-p", "/go/src/github.com/org/repo"}},
   102  				{dir: "/go/src/github.com/org/repo", command: "git", args: []string{"init"}},
   103  				{dir: "/go/src/github.com/org/repo", command: "git", args: []string{"config", "user.name", "user"}},
   104  				{dir: "/go/src/github.com/org/repo", command: "git", args: []string{"fetch", "https://github.com/org/repo.git", "--tags", "--prune"}},
   105  				{dir: "/go/src/github.com/org/repo", command: "git", args: []string{"fetch", "https://github.com/org/repo.git", "master"}},
   106  				{dir: "/go/src/github.com/org/repo", command: "git", args: []string{"checkout", "FETCH_HEAD"}},
   107  				{dir: "/go/src/github.com/org/repo", command: "git", args: []string{"branch", "--force", "master", "FETCH_HEAD"}},
   108  				{dir: "/go/src/github.com/org/repo", command: "git", args: []string{"checkout", "master"}},
   109  			},
   110  			expectedPull: []cloneCommand{
   111  				{dir: "/go/src/github.com/org/repo", command: "git", args: []string{"submodule", "update", "--init", "--recursive"}},
   112  			},
   113  		},
   114  		{
   115  			name: "minimal refs with git user email",
   116  			refs: kube.Refs{
   117  				Org:     "org",
   118  				Repo:    "repo",
   119  				BaseRef: "master",
   120  			},
   121  			gitUserEmail: "user@go.com",
   122  			dir:          "/go",
   123  			expectedBase: []cloneCommand{
   124  				{dir: "/", command: "mkdir", args: []string{"-p", "/go/src/github.com/org/repo"}},
   125  				{dir: "/go/src/github.com/org/repo", command: "git", args: []string{"init"}},
   126  				{dir: "/go/src/github.com/org/repo", command: "git", args: []string{"config", "user.email", "user@go.com"}},
   127  				{dir: "/go/src/github.com/org/repo", command: "git", args: []string{"fetch", "https://github.com/org/repo.git", "--tags", "--prune"}},
   128  				{dir: "/go/src/github.com/org/repo", command: "git", args: []string{"fetch", "https://github.com/org/repo.git", "master"}},
   129  				{dir: "/go/src/github.com/org/repo", command: "git", args: []string{"checkout", "FETCH_HEAD"}},
   130  				{dir: "/go/src/github.com/org/repo", command: "git", args: []string{"branch", "--force", "master", "FETCH_HEAD"}},
   131  				{dir: "/go/src/github.com/org/repo", command: "git", args: []string{"checkout", "master"}},
   132  			},
   133  			expectedPull: []cloneCommand{
   134  				{dir: "/go/src/github.com/org/repo", command: "git", args: []string{"submodule", "update", "--init", "--recursive"}},
   135  			},
   136  		},
   137  		{
   138  			name: "minimal refs with http cookie file",
   139  			refs: kube.Refs{
   140  				Org:     "org",
   141  				Repo:    "repo",
   142  				BaseRef: "master",
   143  			},
   144  			cookiePath: "/cookie.txt",
   145  			dir:        "/go",
   146  			expectedBase: []cloneCommand{
   147  				{dir: "/", command: "mkdir", args: []string{"-p", "/go/src/github.com/org/repo"}},
   148  				{dir: "/go/src/github.com/org/repo", command: "git", args: []string{"init"}},
   149  				{dir: "/go/src/github.com/org/repo", command: "git", args: []string{"config", "http.cookiefile", "/cookie.txt"}},
   150  				{dir: "/go/src/github.com/org/repo", command: "git", args: []string{"fetch", "https://github.com/org/repo.git", "--tags", "--prune"}},
   151  				{dir: "/go/src/github.com/org/repo", command: "git", args: []string{"fetch", "https://github.com/org/repo.git", "master"}},
   152  				{dir: "/go/src/github.com/org/repo", command: "git", args: []string{"checkout", "FETCH_HEAD"}},
   153  				{dir: "/go/src/github.com/org/repo", command: "git", args: []string{"branch", "--force", "master", "FETCH_HEAD"}},
   154  				{dir: "/go/src/github.com/org/repo", command: "git", args: []string{"checkout", "master"}},
   155  			},
   156  			expectedPull: []cloneCommand{
   157  				{dir: "/go/src/github.com/org/repo", command: "git", args: []string{"submodule", "update", "--init", "--recursive"}},
   158  			},
   159  		},
   160  		{
   161  			name: "minimal refs with no submodules",
   162  			refs: kube.Refs{
   163  				Org:            "org",
   164  				Repo:           "repo",
   165  				BaseRef:        "master",
   166  				SkipSubmodules: true,
   167  			},
   168  			dir: "/go",
   169  			expectedBase: []cloneCommand{
   170  				{dir: "/", command: "mkdir", args: []string{"-p", "/go/src/github.com/org/repo"}},
   171  				{dir: "/go/src/github.com/org/repo", command: "git", args: []string{"init"}},
   172  				{dir: "/go/src/github.com/org/repo", command: "git", args: []string{"fetch", "https://github.com/org/repo.git", "--tags", "--prune"}},
   173  				{dir: "/go/src/github.com/org/repo", command: "git", args: []string{"fetch", "https://github.com/org/repo.git", "master"}},
   174  				{dir: "/go/src/github.com/org/repo", command: "git", args: []string{"checkout", "FETCH_HEAD"}},
   175  				{dir: "/go/src/github.com/org/repo", command: "git", args: []string{"branch", "--force", "master", "FETCH_HEAD"}},
   176  				{dir: "/go/src/github.com/org/repo", command: "git", args: []string{"checkout", "master"}},
   177  			},
   178  			expectedPull: nil,
   179  		},
   180  		{
   181  			name: "refs with clone URI override",
   182  			refs: kube.Refs{
   183  				Org:      "org",
   184  				Repo:     "repo",
   185  				BaseRef:  "master",
   186  				CloneURI: "internet.com",
   187  			},
   188  			dir: "/go",
   189  			expectedBase: []cloneCommand{
   190  				{dir: "/", command: "mkdir", args: []string{"-p", "/go/src/github.com/org/repo"}},
   191  				{dir: "/go/src/github.com/org/repo", command: "git", args: []string{"init"}},
   192  				{dir: "/go/src/github.com/org/repo", command: "git", args: []string{"fetch", "internet.com", "--tags", "--prune"}},
   193  				{dir: "/go/src/github.com/org/repo", command: "git", args: []string{"fetch", "internet.com", "master"}},
   194  				{dir: "/go/src/github.com/org/repo", command: "git", args: []string{"checkout", "FETCH_HEAD"}},
   195  				{dir: "/go/src/github.com/org/repo", command: "git", args: []string{"branch", "--force", "master", "FETCH_HEAD"}},
   196  				{dir: "/go/src/github.com/org/repo", command: "git", args: []string{"checkout", "master"}},
   197  			},
   198  			expectedPull: []cloneCommand{
   199  				{dir: "/go/src/github.com/org/repo", command: "git", args: []string{"submodule", "update", "--init", "--recursive"}},
   200  			},
   201  		},
   202  		{
   203  			name: "refs with path alias",
   204  			refs: kube.Refs{
   205  				Org:       "org",
   206  				Repo:      "repo",
   207  				BaseRef:   "master",
   208  				PathAlias: "my/favorite/dir",
   209  			},
   210  			dir: "/go",
   211  			expectedBase: []cloneCommand{
   212  				{dir: "/", command: "mkdir", args: []string{"-p", "/go/src/my/favorite/dir"}},
   213  				{dir: "/go/src/my/favorite/dir", command: "git", args: []string{"init"}},
   214  				{dir: "/go/src/my/favorite/dir", command: "git", args: []string{"fetch", "https://github.com/org/repo.git", "--tags", "--prune"}},
   215  				{dir: "/go/src/my/favorite/dir", command: "git", args: []string{"fetch", "https://github.com/org/repo.git", "master"}},
   216  				{dir: "/go/src/my/favorite/dir", command: "git", args: []string{"checkout", "FETCH_HEAD"}},
   217  				{dir: "/go/src/my/favorite/dir", command: "git", args: []string{"branch", "--force", "master", "FETCH_HEAD"}},
   218  				{dir: "/go/src/my/favorite/dir", command: "git", args: []string{"checkout", "master"}},
   219  			},
   220  			expectedPull: []cloneCommand{
   221  				{dir: "/go/src/my/favorite/dir", command: "git", args: []string{"submodule", "update", "--init", "--recursive"}},
   222  			},
   223  		},
   224  		{
   225  			name: "refs with specific base sha",
   226  			refs: kube.Refs{
   227  				Org:     "org",
   228  				Repo:    "repo",
   229  				BaseRef: "master",
   230  				BaseSHA: "abcdef",
   231  			},
   232  			dir: "/go",
   233  			expectedBase: []cloneCommand{
   234  				{dir: "/", command: "mkdir", args: []string{"-p", "/go/src/github.com/org/repo"}},
   235  				{dir: "/go/src/github.com/org/repo", command: "git", args: []string{"init"}},
   236  				{dir: "/go/src/github.com/org/repo", command: "git", args: []string{"fetch", "https://github.com/org/repo.git", "--tags", "--prune"}},
   237  				{dir: "/go/src/github.com/org/repo", command: "git", args: []string{"fetch", "https://github.com/org/repo.git", "master"}},
   238  				{dir: "/go/src/github.com/org/repo", command: "git", args: []string{"checkout", "abcdef"}},
   239  				{dir: "/go/src/github.com/org/repo", command: "git", args: []string{"branch", "--force", "master", "abcdef"}},
   240  				{dir: "/go/src/github.com/org/repo", command: "git", args: []string{"checkout", "master"}},
   241  			},
   242  			expectedPull: []cloneCommand{
   243  				{dir: "/go/src/github.com/org/repo", command: "git", args: []string{"submodule", "update", "--init", "--recursive"}},
   244  			},
   245  		},
   246  		{
   247  			name: "refs with simple pr ref",
   248  			refs: kube.Refs{
   249  				Org:     "org",
   250  				Repo:    "repo",
   251  				BaseRef: "master",
   252  				Pulls: []kube.Pull{
   253  					{Number: 1},
   254  				},
   255  			},
   256  			dir: "/go",
   257  			expectedBase: []cloneCommand{
   258  				{dir: "/", command: "mkdir", args: []string{"-p", "/go/src/github.com/org/repo"}},
   259  				{dir: "/go/src/github.com/org/repo", command: "git", args: []string{"init"}},
   260  				{dir: "/go/src/github.com/org/repo", command: "git", args: []string{"fetch", "https://github.com/org/repo.git", "--tags", "--prune"}},
   261  				{dir: "/go/src/github.com/org/repo", command: "git", args: []string{"fetch", "https://github.com/org/repo.git", "master"}},
   262  				{dir: "/go/src/github.com/org/repo", command: "git", args: []string{"checkout", "FETCH_HEAD"}},
   263  				{dir: "/go/src/github.com/org/repo", command: "git", args: []string{"branch", "--force", "master", "FETCH_HEAD"}},
   264  				{dir: "/go/src/github.com/org/repo", command: "git", args: []string{"checkout", "master"}},
   265  			},
   266  			expectedPull: []cloneCommand{
   267  				{dir: "/go/src/github.com/org/repo", command: "git", args: []string{"fetch", "https://github.com/org/repo.git", "pull/1/head"}},
   268  				{dir: "/go/src/github.com/org/repo", command: "git", args: []string{"merge", "--no-ff", "FETCH_HEAD"}, env: gitTimestampEnvs(fakeTimestamp + 1)},
   269  				{dir: "/go/src/github.com/org/repo", command: "git", args: []string{"submodule", "update", "--init", "--recursive"}},
   270  			},
   271  		},
   272  		{
   273  			name: "refs with pr ref override",
   274  			refs: kube.Refs{
   275  				Org:     "org",
   276  				Repo:    "repo",
   277  				BaseRef: "master",
   278  				Pulls: []kube.Pull{
   279  					{Number: 1, Ref: "pull-me"},
   280  				},
   281  			},
   282  			dir: "/go",
   283  			expectedBase: []cloneCommand{
   284  				{dir: "/", command: "mkdir", args: []string{"-p", "/go/src/github.com/org/repo"}},
   285  				{dir: "/go/src/github.com/org/repo", command: "git", args: []string{"init"}},
   286  				{dir: "/go/src/github.com/org/repo", command: "git", args: []string{"fetch", "https://github.com/org/repo.git", "--tags", "--prune"}},
   287  				{dir: "/go/src/github.com/org/repo", command: "git", args: []string{"fetch", "https://github.com/org/repo.git", "master"}},
   288  				{dir: "/go/src/github.com/org/repo", command: "git", args: []string{"checkout", "FETCH_HEAD"}},
   289  				{dir: "/go/src/github.com/org/repo", command: "git", args: []string{"branch", "--force", "master", "FETCH_HEAD"}},
   290  				{dir: "/go/src/github.com/org/repo", command: "git", args: []string{"checkout", "master"}},
   291  			},
   292  			expectedPull: []cloneCommand{
   293  				{dir: "/go/src/github.com/org/repo", command: "git", args: []string{"fetch", "https://github.com/org/repo.git", "pull-me"}},
   294  				{dir: "/go/src/github.com/org/repo", command: "git", args: []string{"merge", "--no-ff", "FETCH_HEAD"}, env: gitTimestampEnvs(fakeTimestamp + 1)},
   295  				{dir: "/go/src/github.com/org/repo", command: "git", args: []string{"submodule", "update", "--init", "--recursive"}},
   296  			},
   297  		},
   298  		{
   299  			name: "refs with pr ref with specific sha",
   300  			refs: kube.Refs{
   301  				Org:     "org",
   302  				Repo:    "repo",
   303  				BaseRef: "master",
   304  				Pulls: []kube.Pull{
   305  					{Number: 1, SHA: "abcdef"},
   306  				},
   307  			},
   308  			dir: "/go",
   309  			expectedBase: []cloneCommand{
   310  				{dir: "/", command: "mkdir", args: []string{"-p", "/go/src/github.com/org/repo"}},
   311  				{dir: "/go/src/github.com/org/repo", command: "git", args: []string{"init"}},
   312  				{dir: "/go/src/github.com/org/repo", command: "git", args: []string{"fetch", "https://github.com/org/repo.git", "--tags", "--prune"}},
   313  				{dir: "/go/src/github.com/org/repo", command: "git", args: []string{"fetch", "https://github.com/org/repo.git", "master"}},
   314  				{dir: "/go/src/github.com/org/repo", command: "git", args: []string{"checkout", "FETCH_HEAD"}},
   315  				{dir: "/go/src/github.com/org/repo", command: "git", args: []string{"branch", "--force", "master", "FETCH_HEAD"}},
   316  				{dir: "/go/src/github.com/org/repo", command: "git", args: []string{"checkout", "master"}},
   317  			},
   318  			expectedPull: []cloneCommand{
   319  				{dir: "/go/src/github.com/org/repo", command: "git", args: []string{"fetch", "https://github.com/org/repo.git", "pull/1/head"}},
   320  				{dir: "/go/src/github.com/org/repo", command: "git", args: []string{"merge", "--no-ff", "abcdef"}, env: gitTimestampEnvs(fakeTimestamp + 1)},
   321  				{dir: "/go/src/github.com/org/repo", command: "git", args: []string{"submodule", "update", "--init", "--recursive"}},
   322  			},
   323  		},
   324  		{
   325  			name: "refs with multiple simple pr refs",
   326  			refs: kube.Refs{
   327  				Org:     "org",
   328  				Repo:    "repo",
   329  				BaseRef: "master",
   330  				Pulls: []kube.Pull{
   331  					{Number: 1},
   332  					{Number: 2},
   333  				},
   334  			},
   335  			dir: "/go",
   336  			expectedBase: []cloneCommand{
   337  				{dir: "/", command: "mkdir", args: []string{"-p", "/go/src/github.com/org/repo"}},
   338  				{dir: "/go/src/github.com/org/repo", command: "git", args: []string{"init"}},
   339  				{dir: "/go/src/github.com/org/repo", command: "git", args: []string{"fetch", "https://github.com/org/repo.git", "--tags", "--prune"}},
   340  				{dir: "/go/src/github.com/org/repo", command: "git", args: []string{"fetch", "https://github.com/org/repo.git", "master"}},
   341  				{dir: "/go/src/github.com/org/repo", command: "git", args: []string{"checkout", "FETCH_HEAD"}},
   342  				{dir: "/go/src/github.com/org/repo", command: "git", args: []string{"branch", "--force", "master", "FETCH_HEAD"}},
   343  				{dir: "/go/src/github.com/org/repo", command: "git", args: []string{"checkout", "master"}},
   344  			},
   345  			expectedPull: []cloneCommand{
   346  				{dir: "/go/src/github.com/org/repo", command: "git", args: []string{"fetch", "https://github.com/org/repo.git", "pull/1/head"}},
   347  				{dir: "/go/src/github.com/org/repo", command: "git", args: []string{"merge", "--no-ff", "FETCH_HEAD"}, env: gitTimestampEnvs(fakeTimestamp + 1)},
   348  				{dir: "/go/src/github.com/org/repo", command: "git", args: []string{"fetch", "https://github.com/org/repo.git", "pull/2/head"}},
   349  				{dir: "/go/src/github.com/org/repo", command: "git", args: []string{"merge", "--no-ff", "FETCH_HEAD"}, env: gitTimestampEnvs(fakeTimestamp + 2)},
   350  				{dir: "/go/src/github.com/org/repo", command: "git", args: []string{"submodule", "update", "--init", "--recursive"}},
   351  			},
   352  		},
   353  	}
   354  
   355  	for _, testCase := range testCases {
   356  		g := gitCtxForRefs(testCase.refs, testCase.dir, testCase.env)
   357  		actualBase := g.commandsForBaseRef(testCase.refs, testCase.gitUserName, testCase.gitUserEmail, testCase.cookiePath)
   358  		if !reflect.DeepEqual(actualBase, testCase.expectedBase) {
   359  			t.Errorf("%s: generated incorrect commands: %v", testCase.name, diff.ObjectGoPrintDiff(testCase.expectedBase, actualBase))
   360  		}
   361  		actualPull := g.commandsForPullRefs(testCase.refs, fakeTimestamp)
   362  		if !reflect.DeepEqual(actualPull, testCase.expectedPull) {
   363  			t.Errorf("%s: generated incorrect commands: %v", testCase.name, diff.ObjectGoPrintDiff(testCase.expectedPull, actualPull))
   364  		}
   365  	}
   366  }
   367  
   368  func TestGitHeadTimestamp(t *testing.T) {
   369  	fakeTimestamp := 987654321
   370  	fakeGitDir, err := makeFakeGitRepo(fakeTimestamp)
   371  	if err != nil {
   372  		t.Errorf("error creating fake git dir: %v", err)
   373  	}
   374  	defer func() {
   375  		if err := os.RemoveAll(fakeGitDir); err != nil {
   376  			t.Errorf("error cleaning up fake git dir: %v", err)
   377  		}
   378  	}()
   379  
   380  	var testCases = []struct {
   381  		name        string
   382  		dir         string
   383  		noPath      bool
   384  		expected    int
   385  		expectError bool
   386  	}{
   387  		{
   388  			name:        "root - no git",
   389  			dir:         "/",
   390  			expected:    0,
   391  			expectError: true,
   392  		},
   393  		{
   394  			name:        "fake git repo",
   395  			dir:         fakeGitDir,
   396  			expected:    fakeTimestamp,
   397  			expectError: false,
   398  		},
   399  		{
   400  			name:        "fake git repo but no git binary",
   401  			dir:         fakeGitDir,
   402  			noPath:      true,
   403  			expected:    0,
   404  			expectError: true,
   405  		},
   406  	}
   407  	origCwd, err := os.Getwd()
   408  	if err != nil {
   409  		t.Errorf("failed getting cwd: %v", err)
   410  	}
   411  	origPath := os.Getenv("PATH")
   412  	for _, testCase := range testCases {
   413  		t.Run(testCase.name, func(t *testing.T) {
   414  			if err := os.Chdir(testCase.dir); err != nil {
   415  				t.Errorf("%s: failed to chdir to %s: %v", testCase.name, testCase.dir, err)
   416  			}
   417  			if testCase.noPath {
   418  				if err := os.Unsetenv("PATH"); err != nil {
   419  					t.Errorf("%s: failed to unset PATH: %v", testCase.name, err)
   420  				}
   421  			}
   422  			g := gitCtx{
   423  				cloneDir: testCase.dir,
   424  			}
   425  			timestamp, err := g.gitHeadTimestamp()
   426  			if timestamp != testCase.expected {
   427  				t.Errorf("%s: timestamp %d does not match expected timestamp %d", testCase.name, timestamp, testCase.expected)
   428  			}
   429  			if (err == nil && testCase.expectError) || (err != nil && !testCase.expectError) {
   430  				t.Errorf("%s: expect error is %v but received error %v", testCase.name, testCase.expectError, err)
   431  			}
   432  			if err := os.Chdir(origCwd); err != nil {
   433  				t.Errorf("%s: failed to chdir to original cwd %s: %v", testCase.name, origCwd, err)
   434  			}
   435  			if testCase.noPath {
   436  				if err := os.Setenv("PATH", origPath); err != nil {
   437  					t.Errorf("%s: failed to set PATH to original: %v", testCase.name, err)
   438  				}
   439  			}
   440  
   441  		})
   442  	}
   443  }
   444  
   445  // makeFakeGitRepo creates a fake git repo with a constant digest and timestamp.
   446  func makeFakeGitRepo(fakeTimestamp int) (string, error) {
   447  	fakeGitDir, err := ioutil.TempDir("", "fakegit")
   448  	if err != nil {
   449  		return "", err
   450  	}
   451  	cmds := [][]string{
   452  		{"git", "init"},
   453  		{"git", "config", "user.email", "test@test.test"},
   454  		{"git", "config", "user.name", "test test"},
   455  		{"touch", "a_file"},
   456  		{"git", "add", "a_file"},
   457  		{"git", "commit", "-m", "adding a_file"},
   458  	}
   459  	for _, cmd := range cmds {
   460  		c := exec.Command(cmd[0], cmd[1:]...)
   461  		c.Dir = fakeGitDir
   462  		c.Env = append(os.Environ(), gitTimestampEnvs(fakeTimestamp)...)
   463  		if err := c.Run(); err != nil {
   464  			return fakeGitDir, err
   465  		}
   466  	}
   467  	return fakeGitDir, nil
   468  }