github.com/btwiuse/jiri@v0.0.0-20191125065820-53353bcfef54/project/project_test.go (about)

     1  // Copyright 2015 The Vanadium 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 project_test
     6  
     7  import (
     8  	"bytes"
     9  	"fmt"
    10  	"io/ioutil"
    11  	"net/http"
    12  	"net/http/httptest"
    13  	"os"
    14  	"path/filepath"
    15  	"reflect"
    16  	"sort"
    17  	"strconv"
    18  	"strings"
    19  	"testing"
    20  
    21  	"github.com/btwiuse/jiri"
    22  	"github.com/btwiuse/jiri/cipd"
    23  	"github.com/btwiuse/jiri/gitutil"
    24  	"github.com/btwiuse/jiri/jiritest"
    25  	"github.com/btwiuse/jiri/jiritest/xtest"
    26  	"github.com/btwiuse/jiri/project"
    27  )
    28  
    29  func dirExists(dirname string) error {
    30  	fileInfo, err := os.Stat(dirname)
    31  	if err != nil {
    32  		return err
    33  	}
    34  	if !fileInfo.IsDir() {
    35  		return os.ErrNotExist
    36  	}
    37  	return nil
    38  }
    39  
    40  func fileExists(dirname string) error {
    41  	fileInfo, err := os.Stat(dirname)
    42  	if err != nil {
    43  		return err
    44  	}
    45  	if fileInfo.IsDir() {
    46  		return os.ErrNotExist
    47  	}
    48  	return nil
    49  }
    50  
    51  func checkReadme(t *testing.T, jirix *jiri.X, p project.Project, message string) {
    52  	if _, err := os.Stat(p.Path); err != nil {
    53  		t.Fatalf("%v", err)
    54  	}
    55  	readmeFile := filepath.Join(p.Path, "README")
    56  	data, err := ioutil.ReadFile(readmeFile)
    57  	if err != nil {
    58  		t.Fatalf("ReadFile(%v) failed: %v", readmeFile, err)
    59  	}
    60  	if got, want := data, []byte(message); bytes.Compare(got, want) != 0 {
    61  		t.Fatalf("unexpected content in project %v:\ngot\n%s\nwant\n%s\n", p.Name, got, want)
    62  	}
    63  }
    64  
    65  func checkJiriRevFiles(t *testing.T, jirix *jiri.X, p project.Project) {
    66  	fake, cleanup := jiritest.NewFakeJiriRoot(t)
    67  	defer cleanup()
    68  
    69  	g := gitutil.New(fake.X, gitutil.RootDirOpt(p.Path))
    70  
    71  	file := filepath.Join(p.Path, ".git", "JIRI_HEAD")
    72  	data, err := ioutil.ReadFile(file)
    73  	if err != nil {
    74  		t.Fatalf("ReadFile(%v) failed: %s", file, err)
    75  	}
    76  	headFileContents := string(data)
    77  	headFileCommit, err := g.CurrentRevisionForRef(headFileContents)
    78  	if err != nil {
    79  		t.Fatalf("CurrentRevisionForRef failed: %s", err)
    80  	}
    81  
    82  	projectRevision := p.Revision
    83  	if projectRevision == "" {
    84  		if p.RemoteBranch == "" {
    85  			projectRevision = "origin/master"
    86  		} else {
    87  			projectRevision = "origin/" + p.RemoteBranch
    88  		}
    89  	}
    90  	revisionCommit, err := g.CurrentRevisionForRef(projectRevision)
    91  	if err != nil {
    92  		t.Fatalf("CurrentRevisionForRef failed: %s", err)
    93  	}
    94  
    95  	if revisionCommit != headFileCommit {
    96  		t.Fatalf("JIRI_HEAD contains %s (%s) expected %s (%s)", headFileContents, headFileCommit, projectRevision, revisionCommit)
    97  	}
    98  	file = filepath.Join(p.Path, ".git", "JIRI_LAST_BASE")
    99  	data, err = ioutil.ReadFile(file)
   100  	if err != nil {
   101  		t.Fatalf("ReadFile(%v) failed: %s", file, err)
   102  	}
   103  	if rev, err := g.CurrentRevision(); err != nil {
   104  		t.Fatalf("CurrentRevision() failed: %s", err)
   105  	} else if rev != string(data) {
   106  		t.Fatalf("JIRI_LAST_BASE contains %s expected %s", string(data), rev)
   107  	}
   108  }
   109  
   110  func commitFile(t *testing.T, jirix *jiri.X, dir, file, msg string) {
   111  	cwd, err := os.Getwd()
   112  	if err != nil {
   113  		t.Fatal(err)
   114  	}
   115  	defer os.Chdir(cwd)
   116  	if err := os.Chdir(dir); err != nil {
   117  		t.Fatal(err)
   118  	}
   119  	if err := gitutil.New(jirix, gitutil.UserNameOpt("John Doe"), gitutil.UserEmailOpt("john.doe@example.com")).CommitFile(file, msg); err != nil {
   120  		t.Fatal(err)
   121  	}
   122  }
   123  
   124  func projectName(i int) string {
   125  	return fmt.Sprintf("project-%d", i)
   126  }
   127  
   128  func writeUncommitedFile(t *testing.T, jirix *jiri.X, projectDir, fileName, message string) string {
   129  	path, perm := filepath.Join(projectDir, fileName), os.FileMode(0644)
   130  	if err := ioutil.WriteFile(path, []byte(message), perm); err != nil {
   131  		t.Fatalf("WriteFile(%v, %v) failed: %v", path, perm, err)
   132  	}
   133  	return path
   134  }
   135  func writeFile(t *testing.T, jirix *jiri.X, projectDir, fileName, message string) {
   136  	path := writeUncommitedFile(t, jirix, projectDir, fileName, message)
   137  	commitFile(t, jirix, projectDir, path, "creating "+fileName)
   138  }
   139  
   140  func writeReadme(t *testing.T, jirix *jiri.X, projectDir, message string) {
   141  	writeFile(t, jirix, projectDir, "README", message)
   142  }
   143  
   144  func checkProjectsMatchPaths(t *testing.T, gotProjects project.Projects, wantProjectPaths []string) {
   145  	gotProjectPaths := []string{}
   146  	for _, p := range gotProjects {
   147  		gotProjectPaths = append(gotProjectPaths, p.Path)
   148  	}
   149  	sort.Strings(gotProjectPaths)
   150  	sort.Strings(wantProjectPaths)
   151  	if !reflect.DeepEqual(gotProjectPaths, wantProjectPaths) {
   152  		t.Errorf("project paths got %v, want %v", gotProjectPaths, wantProjectPaths)
   153  	}
   154  }
   155  
   156  // TestLocalProjects tests the behavior of the LocalProjects method with
   157  // different ScanModes.
   158  func TestLocalProjects(t *testing.T) {
   159  	jirix, cleanup := xtest.NewX(t)
   160  	defer cleanup()
   161  
   162  	// Create some projects.
   163  	numProjects, projectPaths := 3, []string{}
   164  	for i := 0; i < numProjects; i++ {
   165  		name := projectName(i)
   166  		path := filepath.Join(jirix.Root, name)
   167  		if err := os.MkdirAll(path, 0755); err != nil {
   168  			t.Fatal(err)
   169  		}
   170  
   171  		// Initialize empty git repository.  The commit is necessary, otherwise
   172  		// "git rev-parse master" fails.
   173  		git := gitutil.New(jirix, gitutil.UserNameOpt("John Doe"), gitutil.UserEmailOpt("john.doe@example.com"), gitutil.RootDirOpt(path))
   174  		if err := git.Init(path); err != nil {
   175  			t.Fatal(err)
   176  		}
   177  		if err := git.Commit(); err != nil {
   178  			t.Fatal(err)
   179  		}
   180  
   181  		// Write project metadata.
   182  		p := project.Project{
   183  			Path: path,
   184  			Name: name,
   185  		}
   186  		if err := project.InternalWriteMetadata(jirix, p, path); err != nil {
   187  			t.Fatalf("writeMetadata %v %v) failed: %v\n", p, path, err)
   188  		}
   189  		projectPaths = append(projectPaths, path)
   190  	}
   191  
   192  	// Create a latest update snapshot but only tell it about the first project.
   193  	manifest := project.Manifest{
   194  		Version: project.ManifestVersion,
   195  		Projects: []project.Project{
   196  			{
   197  				Name: projectName(0),
   198  				Path: projectPaths[0],
   199  			},
   200  		},
   201  	}
   202  	if err := os.MkdirAll(jirix.UpdateHistoryDir(), 0755); err != nil {
   203  		t.Fatalf("MkdirAll(%v) failed: %v", jirix.UpdateHistoryDir(), err)
   204  	}
   205  	if err := manifest.ToFile(jirix, jirix.UpdateHistoryLatestLink()); err != nil {
   206  		t.Fatalf("manifest.ToFile(%v) failed: %v", jirix.UpdateHistoryLatestLink(), err)
   207  	}
   208  
   209  	// LocalProjects with scanMode = FastScan should only find the first
   210  	// project.
   211  	foundProjects, err := project.LocalProjects(jirix, project.FastScan)
   212  	if err != nil {
   213  		t.Fatalf("LocalProjects(%v) failed: %v", project.FastScan, err)
   214  	}
   215  	checkProjectsMatchPaths(t, foundProjects, projectPaths[:1])
   216  
   217  	// LocalProjects with scanMode = FullScan should find all projects.
   218  	foundProjects, err = project.LocalProjects(jirix, project.FullScan)
   219  	if err != nil {
   220  		t.Fatalf("LocalProjects(%v) failed: %v", project.FastScan, err)
   221  	}
   222  	checkProjectsMatchPaths(t, foundProjects, projectPaths[:])
   223  
   224  	// Check that deleting a project forces LocalProjects to run a full scan,
   225  	// even if FastScan is specified.
   226  	if err := os.RemoveAll(projectPaths[0]); err != nil {
   227  		t.Fatalf("RemoveAll(%s) failed: %s", projectPaths[0], err)
   228  	}
   229  	foundProjects, err = project.LocalProjects(jirix, project.FastScan)
   230  	if err != nil {
   231  		t.Fatalf("LocalProjects(%v) failed: %v", project.FastScan, err)
   232  	}
   233  	checkProjectsMatchPaths(t, foundProjects, projectPaths[1:])
   234  }
   235  
   236  // setupUniverse creates a fake jiri root with 3 remote projects.  Each project
   237  // has a README with text "initial readme".
   238  func setupUniverse(t *testing.T) ([]project.Project, *jiritest.FakeJiriRoot, func()) {
   239  	fake, cleanup := jiritest.NewFakeJiriRoot(t)
   240  	success := false
   241  	defer func() {
   242  		if !success {
   243  			cleanup()
   244  		}
   245  	}()
   246  
   247  	// Create some projects and add them to the remote manifest.
   248  	numProjects := 7
   249  	localProjects := []project.Project{}
   250  	for i := 0; i < numProjects; i++ {
   251  		name := projectName(i)
   252  		path := fmt.Sprintf("path-%d", i)
   253  		if err := fake.CreateRemoteProject(name); err != nil {
   254  			t.Fatal(err)
   255  		}
   256  		p := project.Project{
   257  			Name:   name,
   258  			Path:   filepath.Join(fake.X.Root, path),
   259  			Remote: fake.Projects[name],
   260  		}
   261  		localProjects = append(localProjects, p)
   262  	}
   263  	localProjects[2].HistoryDepth = 1
   264  	localProjects[3].Path = filepath.Join(localProjects[2].Path, "path-3")
   265  	localProjects[4].Path = filepath.Join(localProjects[3].Path, "path-4")
   266  	localProjects[5].Path = filepath.Join(localProjects[2].Path, "path-5")
   267  	localProjects[6].Path = filepath.Join(localProjects[0].Path, "path-6")
   268  	for _, p := range localProjects {
   269  		if err := fake.AddProject(p); err != nil {
   270  			t.Fatal(err)
   271  		}
   272  	}
   273  
   274  	// Create initial commit in each repo.
   275  	for _, remoteProjectDir := range fake.Projects {
   276  		writeReadme(t, fake.X, remoteProjectDir, "initial readme")
   277  	}
   278  	writeFile(t, fake.X, fake.Projects[localProjects[2].Name], ".gitignore", "path-3/\npath-5/\n")
   279  	writeFile(t, fake.X, fake.Projects[localProjects[0].Name], ".gitignore", "path-6/\n")
   280  	writeFile(t, fake.X, fake.Projects[localProjects[3].Name], ".gitignore", "path-4/\n")
   281  
   282  	success = true
   283  	return localProjects, fake, cleanup
   284  }
   285  
   286  // TestUpdateUniverseSimple tests that UpdateUniverse will pull remote projects
   287  // locally, and that jiri metadata is ignored in the repos.
   288  func TestUpdateUniverseSimple(t *testing.T) {
   289  	localProjects, fake, cleanup := setupUniverse(t)
   290  	defer cleanup()
   291  
   292  	// Check that calling UpdateUniverse() creates local copies of the remote
   293  	// repositories.
   294  	if err := fake.UpdateUniverse(false); err != nil {
   295  		t.Fatal(err)
   296  	}
   297  	for _, p := range localProjects {
   298  		if err := dirExists(p.Path); err != nil {
   299  			t.Fatalf("expected project to exist at path %q but none found", p.Path)
   300  		}
   301  		if branches, _, err := gitutil.New(fake.X, gitutil.RootDirOpt(p.Path)).GetBranches(); err != nil {
   302  			t.Fatal(err)
   303  		} else if len(branches) != 0 {
   304  			t.Fatalf("expected project %s(%s) to contain no branches but it contains %s", p.Name, p.Path, branches)
   305  		}
   306  		checkReadme(t, fake.X, p, "initial readme")
   307  		checkJiriRevFiles(t, fake.X, p)
   308  	}
   309  }
   310  
   311  func TestUpdateUniverseWhenLocalTracksLocal(t *testing.T) {
   312  	localProjects, fake, cleanup := setupUniverse(t)
   313  	defer cleanup()
   314  
   315  	// Check that calling UpdateUniverse() creates local copies of the remote
   316  	// repositories, and that jiri metadata is ignored by git.
   317  	if err := fake.UpdateUniverse(false); err != nil {
   318  		t.Fatal(err)
   319  	}
   320  
   321  	gitLocal := gitutil.New(fake.X, gitutil.RootDirOpt(localProjects[1].Path))
   322  	gitLocal.CreateBranchWithUpstream("A", "origin/master")
   323  	gitLocal.CreateBranch("B")
   324  	gitLocal.SetUpstream("B", "A")
   325  	writeFile(t, fake.X, fake.Projects[localProjects[1].Name], "file1", "file1")
   326  	gitRemote := gitutil.New(fake.X, gitutil.RootDirOpt(fake.Projects[localProjects[1].Name]))
   327  	remoteRev, _ := gitRemote.CurrentRevision()
   328  	if err := project.UpdateUniverse(fake.X, false, false, false, false, true /*rebase-all*/, true /*run-hooks*/, true /*run-packages*/, project.DefaultHookTimeout, project.DefaultPackageTimeout); err != nil {
   329  		t.Fatal(err)
   330  	}
   331  	projects, err := project.LocalProjects(fake.X, project.FastScan)
   332  	if err != nil {
   333  		t.Fatal(err)
   334  	}
   335  	states, err := project.GetProjectStates(fake.X, projects, false)
   336  	if err != nil {
   337  		t.Fatal(err)
   338  	}
   339  	state := states[localProjects[1].Key()]
   340  	for _, b := range state.Branches {
   341  		if b.Revision != remoteRev {
   342  			t.Fatalf("Branch %q should have rev %q, instead it has %q", b.Name, remoteRev, b.Revision)
   343  		}
   344  	}
   345  }
   346  
   347  func TestUpdateUniverseWhenLocalTracksEachOther(t *testing.T) {
   348  	localProjects, fake, cleanup := setupUniverse(t)
   349  	defer cleanup()
   350  
   351  	// Check that calling UpdateUniverse() creates local copies of the remote
   352  	// repositories, and that jiri metadata is ignored by git.
   353  	if err := fake.UpdateUniverse(false); err != nil {
   354  		t.Fatal(err)
   355  	}
   356  
   357  	gitLocal := gitutil.New(fake.X, gitutil.RootDirOpt(localProjects[1].Path))
   358  	gitLocal.CreateBranch("A")
   359  	gitLocal.CreateBranch("B")
   360  	gitLocal.SetUpstream("B", "A")
   361  	gitLocal.SetUpstream("A", "B")
   362  
   363  	gitRemote := gitutil.New(fake.X, gitutil.RootDirOpt(fake.Projects[localProjects[1].Name]))
   364  	oldRemoteRev, _ := gitRemote.CurrentRevision()
   365  	writeFile(t, fake.X, fake.Projects[localProjects[1].Name], "file1", "file1")
   366  	remoteRev, _ := gitRemote.CurrentRevision()
   367  
   368  	if err := project.UpdateUniverse(fake.X, false, false, false, false, true /*rebase-all*/, true /*run-hooks*/, true /*run-packages*/, project.DefaultHookTimeout, project.DefaultPackageTimeout); err != nil {
   369  		t.Fatal(err)
   370  	}
   371  	projects, err := project.LocalProjects(fake.X, project.FastScan)
   372  	if err != nil {
   373  		t.Fatal(err)
   374  	}
   375  	states, err := project.GetProjectStates(fake.X, projects, false)
   376  	if err != nil {
   377  		t.Fatal(err)
   378  	}
   379  	state := states[localProjects[1].Key()]
   380  	for _, b := range state.Branches {
   381  		expectedRev := oldRemoteRev
   382  		if b.Name == "" {
   383  			expectedRev = remoteRev
   384  		}
   385  		if b.Revision != expectedRev {
   386  			t.Fatalf("Branch %q should have rev %q, instead it has %q", b.Name, expectedRev, b.Revision)
   387  		}
   388  	}
   389  }
   390  
   391  // TestOldMetaDirIsMovedOnUpdate tests that old metadir os moved to new
   392  // location on update and projects are updated properly
   393  func TestOldMetaDirIsMovedOnUpdate(t *testing.T) {
   394  	localProjects, fake, cleanup := setupUniverse(t)
   395  	defer cleanup()
   396  
   397  	if err := fake.UpdateUniverse(false); err != nil {
   398  		t.Fatal(err)
   399  	}
   400  	for i, p := range localProjects {
   401  		oldPath := filepath.Join(p.Path, jiri.OldProjectMetaDir)
   402  		newPath := filepath.Join(p.Path, jiri.ProjectMetaDir)
   403  
   404  		// move new path to old path to replicate old structure
   405  		if err := os.Rename(newPath, oldPath); err != nil {
   406  			t.Fatal(err)
   407  		}
   408  		if i != 1 {
   409  			writeReadme(t, fake.X, fake.Projects[p.Name], "new readme")
   410  		}
   411  	}
   412  	if err := fake.UpdateUniverse(false); err != nil {
   413  		t.Fatal(err)
   414  	}
   415  	for i, p := range localProjects {
   416  		newPath := filepath.Join(p.Path, jiri.ProjectMetaDir)
   417  		if err := dirExists(newPath); err != nil {
   418  			t.Fatalf("expected metadata to exist at path %q but none found", newPath)
   419  		}
   420  		// Check all projects are at latest
   421  		if i != 1 {
   422  			checkReadme(t, fake.X, p, "new readme")
   423  		} else {
   424  			checkReadme(t, fake.X, p, "initial readme")
   425  		}
   426  	}
   427  }
   428  
   429  // TestUpdateUniverseWithCache checks that UpdateUniverse can clone and pull
   430  // from a cache.
   431  func TestUpdateUniverseWithCache(t *testing.T) {
   432  	localProjects, fake, cleanup := setupUniverse(t)
   433  	defer cleanup()
   434  
   435  	// Create cache directory
   436  	cacheDir, err := ioutil.TempDir("", "cache")
   437  	if err != nil {
   438  		t.Fatalf("TempDir() failed: %v", err)
   439  	}
   440  	if err := os.MkdirAll(cacheDir, os.FileMode(0700)); err != nil {
   441  		t.Fatal(err)
   442  	}
   443  	defer func() {
   444  		if err := os.RemoveAll(cacheDir); err != nil {
   445  			t.Fatalf("RemoveAll(%q) failed: %v", cacheDir, err)
   446  		}
   447  	}()
   448  	fake.X.Cache = cacheDir
   449  
   450  	if err := fake.UpdateUniverse(false); err != nil {
   451  		t.Fatal(err)
   452  	}
   453  	for _, p := range localProjects {
   454  		// Check that local clone was referenced from cache
   455  		err := fileExists(p.Path + "/.git/objects/info/alternates")
   456  		if p.HistoryDepth == 0 {
   457  			if err != nil {
   458  				t.Fatalf("expected %v to exist, but not found", p.Path+"/.git/objects/info/alternates")
   459  			}
   460  		} else if err == nil {
   461  			t.Fatalf("expected %v to not exist, but found", p.Path+"/.git/objects/info/alternates")
   462  		}
   463  		checkReadme(t, fake.X, p, "initial readme")
   464  		checkJiriRevFiles(t, fake.X, p)
   465  	}
   466  
   467  	// Commit to master branch of a project 1.
   468  	writeReadme(t, fake.X, fake.Projects[localProjects[1].Name], "master commit")
   469  
   470  	gitRemote := gitutil.New(fake.X, gitutil.RootDirOpt(localProjects[1].Remote))
   471  	remoteRev, err := gitRemote.CurrentRevision()
   472  	if err != nil {
   473  		t.Fatal(err)
   474  	}
   475  
   476  	// Update the manifest with our new HEAD position.
   477  	fake.AddProjectOverride(localProjects[1].Name, localProjects[1].Remote, remoteRev)
   478  	if err := fake.UpdateUniverse(false); err != nil {
   479  		t.Fatal(err)
   480  	}
   481  
   482  	checkReadme(t, fake.X, localProjects[1], "master commit")
   483  	checkJiriRevFiles(t, fake.X, localProjects[1])
   484  
   485  	// Check that cache was updated
   486  	cacheDirPath, err := localProjects[1].CacheDirPath(fake.X)
   487  	if err != nil {
   488  		t.Fatal(err)
   489  	}
   490  	gCache := gitutil.New(fake.X, gitutil.RootDirOpt(cacheDirPath))
   491  	cacheRev, err := gCache.CurrentRevision()
   492  	if err != nil {
   493  		t.Fatal(err)
   494  	}
   495  	if cacheRev != remoteRev {
   496  		t.Fatalf("Cache revision(%v) not equal to local revision(%v)", cacheRev, remoteRev)
   497  	}
   498  }
   499  
   500  func TestProjectUpdateWhenNoUpdate(t *testing.T) {
   501  	localProjects, fake, cleanup := setupUniverse(t)
   502  	defer cleanup()
   503  	if err := fake.UpdateUniverse(false); err != nil {
   504  		t.Fatal(err)
   505  	}
   506  
   507  	lc := project.LocalConfig{NoUpdate: true}
   508  	project.WriteLocalConfig(fake.X, localProjects[1], lc)
   509  	// Commit to master branch of a project 1.
   510  	writeReadme(t, fake.X, fake.Projects[localProjects[1].Name], "master commit")
   511  	gitRemote := gitutil.New(fake.X, gitutil.RootDirOpt(fake.Projects[localProjects[1].Name]))
   512  	remoteRev, _ := gitRemote.CurrentRevision()
   513  	if err := fake.UpdateUniverse(false); err != nil {
   514  		t.Fatal(err)
   515  	}
   516  
   517  	gitLocal := gitutil.New(fake.X, gitutil.RootDirOpt(localProjects[1].Path))
   518  	localRev, _ := gitLocal.CurrentRevision()
   519  
   520  	if remoteRev == localRev {
   521  		t.Fatal("local project should not be updated")
   522  	}
   523  }
   524  
   525  func TestRecursiveImport(t *testing.T) {
   526  	localProjects, fake, cleanup := setupUniverse(t)
   527  	defer cleanup()
   528  
   529  	manifest, err := fake.ReadRemoteManifest()
   530  	if err != nil {
   531  		t.Fatal(err)
   532  	}
   533  
   534  	// Remove last project from manifest
   535  	lastProject := manifest.Projects[len(manifest.Projects)-1]
   536  	manifest.Projects = manifest.Projects[:len(manifest.Projects)-1]
   537  	remoteManifestStr := "remotemanifest"
   538  	if err := fake.CreateRemoteProject(remoteManifestStr); err != nil {
   539  		t.Fatal(err)
   540  	}
   541  	// Fix last projet rev
   542  	lastPRev, _ := gitutil.New(fake.X, gitutil.RootDirOpt(fake.Projects[lastProject.Name])).CurrentRevision()
   543  	lastProject.Revision = lastPRev
   544  	remoteManifest := &project.Manifest{
   545  		Projects: []project.Project{lastProject, project.Project{
   546  			Name:   remoteManifestStr,
   547  			Path:   remoteManifestStr,
   548  			Remote: fake.Projects[remoteManifestStr],
   549  		}},
   550  	}
   551  	remoteManifestFile := filepath.Join(fake.Projects[remoteManifestStr], "manifest")
   552  	if err := remoteManifest.ToFile(fake.X, remoteManifestFile); err != nil {
   553  		t.Fatal(err)
   554  	}
   555  	commitFile(t, fake.X, fake.Projects[remoteManifestStr], "manifest", "1")
   556  	rev, _ := gitutil.New(fake.X, gitutil.RootDirOpt(fake.Projects[remoteManifestStr])).CurrentRevision()
   557  
   558  	// unpin last project in next commit
   559  	remoteManifest.Projects[0].Revision = ""
   560  	if err := remoteManifest.ToFile(fake.X, remoteManifestFile); err != nil {
   561  		t.Fatal(err)
   562  	}
   563  	commitFile(t, fake.X, fake.Projects[remoteManifestStr], "manifest", "2")
   564  	writeFile(t, fake.X, fake.Projects[lastProject.Name], "file1", "file1")
   565  	manifest.Imports = []project.Import{project.Import{
   566  		Name:     remoteManifestStr,
   567  		Remote:   fake.Projects[remoteManifestStr],
   568  		Manifest: "manifest",
   569  		Revision: rev,
   570  	}}
   571  	fake.WriteRemoteManifest(manifest)
   572  	if err := fake.UpdateUniverse(false); err != nil {
   573  		t.Fatal(err)
   574  	}
   575  
   576  	// check all local projects
   577  	for _, p := range localProjects {
   578  		if err := dirExists(p.Path); err != nil {
   579  			t.Fatalf("expected project to exist at path %q but none found", p.Path)
   580  		}
   581  		checkReadme(t, fake.X, p, "initial readme")
   582  	}
   583  
   584  	// check that remotemanifest is at correct revision
   585  	remoteManifestPath := filepath.Join(fake.X.Root, remoteManifestStr)
   586  	if err := dirExists(remoteManifestPath); err != nil {
   587  		t.Fatalf("expected project to exist at path %q but none found", remoteManifestPath)
   588  	}
   589  	currentRev, _ := gitutil.New(fake.X, gitutil.RootDirOpt(remoteManifestPath)).CurrentRevision()
   590  	if currentRev != rev {
   591  		t.Fatalf("For project remotemanifest expected rev to be %q got %q", rev, currentRev)
   592  	}
   593  	// check last project revision
   594  	currentRev, _ = gitutil.New(fake.X, gitutil.RootDirOpt(filepath.Join(fake.X.Root, lastProject.Path))).CurrentRevision()
   595  	if currentRev != lastPRev {
   596  		t.Fatalf("For project %q expected rev to be %q got %q", lastProject.Name, lastPRev, currentRev)
   597  	}
   598  
   599  	//unpin import
   600  	manifest.Imports[0].Revision = ""
   601  	fake.WriteRemoteManifest(manifest)
   602  	if err := fake.UpdateUniverse(false); err != nil {
   603  		t.Fatal(err)
   604  	}
   605  
   606  	//check that projects advances
   607  	currentRev, _ = gitutil.New(fake.X, gitutil.RootDirOpt(remoteManifestPath)).CurrentRevision()
   608  	if currentRev == rev {
   609  		t.Fatalf("For project remotemanifest expected rev to NOT be %q", rev)
   610  	}
   611  	// check last project revision
   612  	currentRev, _ = gitutil.New(fake.X, gitutil.RootDirOpt(filepath.Join(fake.X.Root, lastProject.Path))).CurrentRevision()
   613  	if currentRev == lastPRev {
   614  		t.Fatalf("For project %q expected rev to NOT be %q", lastProject.Name, lastPRev)
   615  	}
   616  }
   617  
   618  func TestLoadManifestFileRecursiveImport(t *testing.T) {
   619  	_, fake, cleanup := setupUniverse(t)
   620  	defer cleanup()
   621  
   622  	manifest, err := fake.ReadRemoteManifest()
   623  	if err != nil {
   624  		t.Fatal(err)
   625  	}
   626  
   627  	// Remove last project from manifest
   628  	lastProject := manifest.Projects[len(manifest.Projects)-1]
   629  	manifest.Projects = manifest.Projects[:len(manifest.Projects)-1]
   630  	remoteManifestStr := "remotemanifest"
   631  	if err := fake.CreateRemoteProject(remoteManifestStr); err != nil {
   632  		t.Fatal(err)
   633  	}
   634  
   635  	remoteManifest := &project.Manifest{
   636  		Projects: []project.Project{lastProject, project.Project{
   637  			Name:   remoteManifestStr,
   638  			Path:   remoteManifestStr,
   639  			Remote: fake.Projects[remoteManifestStr],
   640  		}},
   641  	}
   642  	remoteManifestFile := filepath.Join(fake.Projects[remoteManifestStr], "manifest")
   643  	if err := remoteManifest.ToFile(fake.X, remoteManifestFile); err != nil {
   644  		t.Fatal(err)
   645  	}
   646  	commitFile(t, fake.X, fake.Projects[remoteManifestStr], "manifest", "1")
   647  	rev, _ := gitutil.New(fake.X, gitutil.RootDirOpt(fake.Projects[remoteManifestStr])).CurrentRevision()
   648  
   649  	manifest.Imports = []project.Import{project.Import{
   650  		Name:     remoteManifestStr,
   651  		Remote:   fake.Projects[remoteManifestStr],
   652  		Manifest: "manifest",
   653  		Revision: rev,
   654  	}}
   655  	fake.WriteRemoteManifest(manifest)
   656  	if err := fake.UpdateUniverse(false); err != nil {
   657  		t.Fatal(err)
   658  	}
   659  
   660  	// Write arbitrary revision
   661  	manifest.Imports[0].Revision = "AB"
   662  	fake.WriteRemoteManifest(manifest)
   663  
   664  	// local fetch on manifest project
   665  	gitLocal := gitutil.New(fake.X, gitutil.RootDirOpt(filepath.Join(fake.X.Root, jiritest.ManifestProjectPath)))
   666  	if err := gitLocal.Fetch("origin"); err != nil {
   667  		t.Fatal(err)
   668  	}
   669  	localProjects, err := project.LocalProjects(fake.X, project.FastScan)
   670  	if err != nil {
   671  		t.Fatal(err)
   672  	}
   673  	if _, _, _, err := project.LoadManifestFile(fake.X, fake.X.JiriManifestFile(), localProjects, false); err != nil {
   674  		t.Fatal(err)
   675  	}
   676  }
   677  
   678  func TestRecursiveImportWithLocalImport(t *testing.T) {
   679  	_, fake, cleanup := setupUniverse(t)
   680  	defer cleanup()
   681  
   682  	manifest, err := fake.ReadRemoteManifest()
   683  	if err != nil {
   684  		t.Fatal(err)
   685  	}
   686  
   687  	// Remove last project from manifest
   688  	lastProject := manifest.Projects[len(manifest.Projects)-1]
   689  	manifest.Projects = manifest.Projects[:len(manifest.Projects)-1]
   690  	remoteManifestStr := "remotemanifest"
   691  	if err := fake.CreateRemoteProject(remoteManifestStr); err != nil {
   692  		t.Fatal(err)
   693  	}
   694  	// Fix last project rev
   695  	lastPRev, _ := gitutil.New(fake.X, gitutil.RootDirOpt(fake.Projects[lastProject.Name])).CurrentRevision()
   696  	lastProject.Revision = lastPRev
   697  	remoteManifest := &project.Manifest{
   698  		Projects: []project.Project{lastProject, project.Project{
   699  			Name:   remoteManifestStr,
   700  			Path:   remoteManifestStr,
   701  			Remote: fake.Projects[remoteManifestStr],
   702  		}},
   703  	}
   704  	remoteManifestFile := filepath.Join(fake.Projects[remoteManifestStr], "manifest")
   705  	if err := remoteManifest.ToFile(fake.X, remoteManifestFile); err != nil {
   706  		t.Fatal(err)
   707  	}
   708  	commitFile(t, fake.X, fake.Projects[remoteManifestStr], "manifest", "1")
   709  	rev, _ := gitutil.New(fake.X, gitutil.RootDirOpt(fake.Projects[remoteManifestStr])).CurrentRevision()
   710  	manifest.Imports = []project.Import{project.Import{
   711  		Name:     remoteManifestStr,
   712  		Remote:   fake.Projects[remoteManifestStr],
   713  		Manifest: "manifest",
   714  		Revision: rev,
   715  	}}
   716  
   717  	// unpin last project in next commit
   718  	remoteManifest.Projects[0].Revision = ""
   719  	if err := remoteManifest.ToFile(fake.X, remoteManifestFile); err != nil {
   720  		t.Fatal(err)
   721  	}
   722  	commitFile(t, fake.X, fake.Projects[remoteManifestStr], "manifest", "2")
   723  	// get latest revision
   724  	rev, _ = gitutil.New(fake.X, gitutil.RootDirOpt(fake.Projects[remoteManifestStr])).CurrentRevision()
   725  	writeFile(t, fake.X, fake.Projects[lastProject.Name], "file1", "file1")
   726  	// Get latest last project revision
   727  	lastPRev, _ = gitutil.New(fake.X, gitutil.RootDirOpt(fake.Projects[lastProject.Name])).CurrentRevision()
   728  	fake.WriteRemoteManifest(manifest)
   729  	if err := fake.UpdateUniverse(false); err != nil {
   730  		t.Fatal(err)
   731  	}
   732  
   733  	// make local change in top level manifest and unpin remote manifest
   734  	manifest.Imports[0].Revision = ""
   735  	if err := manifest.ToFile(fake.X, filepath.Join(fake.X.Root, jiritest.ManifestProjectPath, jiritest.ManifestFileName)); err != nil {
   736  		t.Fatal(err)
   737  	}
   738  	if err := project.UpdateUniverse(fake.X, false, true /* localManifest */, false, false, false, false, false, project.DefaultHookTimeout, project.DefaultPackageTimeout); err != nil {
   739  		t.Fatal(err)
   740  	}
   741  
   742  	remoteManifestPath := filepath.Join(fake.X.Root, remoteManifestStr)
   743  	currentRev, _ := gitutil.New(fake.X, gitutil.RootDirOpt(remoteManifestPath)).CurrentRevision()
   744  	if currentRev != rev {
   745  		t.Fatalf("For project remotemanifest expected rev to be %q got %q", rev, currentRev)
   746  	}
   747  	// check last project revision
   748  	currentRev, _ = gitutil.New(fake.X, gitutil.RootDirOpt(filepath.Join(fake.X.Root, lastProject.Path))).CurrentRevision()
   749  	if currentRev != lastPRev {
   750  		t.Fatalf("For project %q expected rev to be %q got %q", lastProject.Name, lastPRev, currentRev)
   751  	}
   752  }
   753  
   754  func TestRecursiveImportWhenOriginalManifestIsImportedAgain(t *testing.T) {
   755  	_, fake, cleanup := setupUniverse(t)
   756  	defer cleanup()
   757  
   758  	manifest, err := fake.ReadRemoteManifest()
   759  	if err != nil {
   760  		t.Fatal(err)
   761  	}
   762  
   763  	// Remove last project from manifest and add it to local import
   764  	lastProject := manifest.Projects[len(manifest.Projects)-1]
   765  	manifest.Projects = manifest.Projects[:len(manifest.Projects)-1]
   766  	manifest.LocalImports = []project.LocalImport{project.LocalImport{
   767  		File: "localmanifest",
   768  	}}
   769  	localManifest := project.Manifest{
   770  		Projects: []project.Project{lastProject},
   771  	}
   772  	localManifestFile := filepath.Join(fake.Projects[jiritest.ManifestProjectName], "localmanifest")
   773  	if err := localManifest.ToFile(fake.X, localManifestFile); err != nil {
   774  		t.Fatal(err)
   775  	}
   776  	commitFile(t, fake.X, fake.Projects[jiritest.ManifestProjectName], "localmanifest", "1")
   777  
   778  	remoteManifestStr := "remotemanifest"
   779  	if err := fake.CreateRemoteProject(remoteManifestStr); err != nil {
   780  		t.Fatal(err)
   781  	}
   782  	manifest.Imports = []project.Import{project.Import{
   783  		Name:     remoteManifestStr,
   784  		Remote:   fake.Projects[remoteManifestStr],
   785  		Manifest: "manifest",
   786  	}}
   787  	fake.WriteRemoteManifest(manifest)
   788  
   789  	// Fix last project rev
   790  	remoteManifest := &project.Manifest{
   791  		Projects: []project.Project{project.Project{
   792  			Name:   remoteManifestStr,
   793  			Path:   remoteManifestStr,
   794  			Remote: fake.Projects[remoteManifestStr],
   795  		}},
   796  		Imports: []project.Import{project.Import{
   797  			Name:     jiritest.ManifestProjectName,
   798  			Remote:   fake.Projects[jiritest.ManifestProjectName],
   799  			Manifest: "localmanifest",
   800  		}},
   801  	}
   802  	remoteManifestFile := filepath.Join(fake.Projects[remoteManifestStr], "manifest")
   803  	if err := remoteManifest.ToFile(fake.X, remoteManifestFile); err != nil {
   804  		t.Fatal(err)
   805  	}
   806  	commitFile(t, fake.X, fake.Projects[remoteManifestStr], "manifest", "1")
   807  
   808  	if err := fake.UpdateUniverse(false); err != nil {
   809  		t.Fatal(err)
   810  	}
   811  	//pin last project and don't commit
   812  	lastPRev, _ := gitutil.New(fake.X, gitutil.RootDirOpt(fake.Projects[lastProject.Name])).CurrentRevision()
   813  	localManifest.Projects[0].Revision = lastPRev
   814  	if err := localManifest.ToFile(fake.X, filepath.Join(fake.X.Root, jiritest.ManifestProjectPath, "localmanifest")); err != nil {
   815  		t.Fatal(err)
   816  	}
   817  
   818  	// Add new commit to last project
   819  	writeFile(t, fake.X, fake.Projects[lastProject.Name], "file1", "file1")
   820  	if err := project.UpdateUniverse(fake.X, false, true /* localManifest */, false, false, false, false, false, project.DefaultHookTimeout, project.DefaultPackageTimeout); err != nil {
   821  		t.Fatal(err)
   822  	}
   823  	// check last project revision
   824  	currentRev, _ := gitutil.New(fake.X, gitutil.RootDirOpt(filepath.Join(fake.X.Root, lastProject.Path))).CurrentRevision()
   825  	if currentRev != lastPRev {
   826  		t.Fatalf("For project %q expected rev to be %q got %q", lastProject.Name, lastPRev, currentRev)
   827  	}
   828  }
   829  
   830  func TestProjectUpdateWhenIgnore(t *testing.T) {
   831  	localProjects, fake, cleanup := setupUniverse(t)
   832  	defer cleanup()
   833  	if err := fake.UpdateUniverse(false); err != nil {
   834  		t.Fatal(err)
   835  	}
   836  
   837  	lc := project.LocalConfig{Ignore: true}
   838  	project.WriteLocalConfig(fake.X, localProjects[1], lc)
   839  	// Commit to master branch of a project 1.
   840  	writeReadme(t, fake.X, fake.Projects[localProjects[1].Name], "master commit")
   841  	gitRemote := gitutil.New(fake.X, gitutil.RootDirOpt(fake.Projects[localProjects[1].Name]))
   842  	remoteRev, _ := gitRemote.CurrentRevision()
   843  	if err := fake.UpdateUniverse(false); err != nil {
   844  		t.Fatal(err)
   845  	}
   846  
   847  	gitLocal := gitutil.New(fake.X, gitutil.RootDirOpt(localProjects[1].Path))
   848  	localRev, _ := gitLocal.CurrentRevision()
   849  
   850  	if remoteRev == localRev {
   851  		t.Fatal("local project should not be updated")
   852  	}
   853  }
   854  
   855  func TestLocalProjectWithConfig(t *testing.T) {
   856  	localProjects, fake, cleanup := setupUniverse(t)
   857  	defer cleanup()
   858  	if err := fake.UpdateUniverse(false); err != nil {
   859  		t.Fatal(err)
   860  	}
   861  	project.WriteUpdateHistorySnapshot(fake.X, "", nil, nil, false)
   862  
   863  	lc := project.LocalConfig{Ignore: true}
   864  	project.WriteLocalConfig(fake.X, localProjects[1], lc)
   865  	scanModes := []project.ScanMode{project.FullScan, project.FastScan}
   866  	for _, scanMode := range scanModes {
   867  		newLocalProjects, err := project.LocalProjects(fake.X, scanMode)
   868  		if err != nil {
   869  			t.Fatal(err)
   870  		}
   871  		for k, p := range newLocalProjects {
   872  			expectedIgnore := k == localProjects[1].Key()
   873  			if p.LocalConfig.Ignore != expectedIgnore {
   874  				t.Errorf("local config ignore: got %t, want %t", p.LocalConfig.Ignore, expectedIgnore)
   875  			}
   876  
   877  			if p.LocalConfig.NoUpdate != false {
   878  				t.Errorf("local config no-update: got %t, want %t", p.LocalConfig.NoUpdate, false)
   879  			}
   880  
   881  			if p.LocalConfig.NoRebase != false {
   882  				t.Errorf("local config no-rebase: got %t, want %t", p.LocalConfig.NoUpdate, false)
   883  			}
   884  		}
   885  	}
   886  }
   887  
   888  func TestProjectUpdateWhenNoRebase(t *testing.T) {
   889  	localProjects, fake, cleanup := setupUniverse(t)
   890  	defer cleanup()
   891  	if err := fake.UpdateUniverse(false); err != nil {
   892  		t.Fatal(err)
   893  	}
   894  
   895  	lc := project.LocalConfig{NoRebase: true}
   896  	project.WriteLocalConfig(fake.X, localProjects[1], lc)
   897  	// Commit to master branch of a project 1.
   898  	writeReadme(t, fake.X, fake.Projects[localProjects[1].Name], "master commit")
   899  	gitRemote := gitutil.New(fake.X, gitutil.RootDirOpt(fake.Projects[localProjects[1].Name]))
   900  	remoteRev, _ := gitRemote.CurrentRevision()
   901  	if err := fake.UpdateUniverse(false); err != nil {
   902  		t.Fatal(err)
   903  	}
   904  
   905  	gitLocal := gitutil.New(fake.X, gitutil.RootDirOpt(localProjects[1].Path))
   906  	localRev, _ := gitLocal.CurrentRevision()
   907  
   908  	if remoteRev != localRev {
   909  		t.Fatal("local project should be updated")
   910  	}
   911  }
   912  
   913  func TestBranchUpdateWhenNoRebase(t *testing.T) {
   914  	localProjects, fake, cleanup := setupUniverse(t)
   915  	defer cleanup()
   916  
   917  	if err := fake.UpdateUniverse(false); err != nil {
   918  		t.Fatal(err)
   919  	}
   920  	gitLocal := gitutil.New(fake.X, gitutil.RootDirOpt(localProjects[1].Path))
   921  	gitLocal.CheckoutBranch("master")
   922  
   923  	lc := project.LocalConfig{NoRebase: true}
   924  	project.WriteLocalConfig(fake.X, localProjects[1], lc)
   925  	// Commit to master branch of a project 1.
   926  	writeReadme(t, fake.X, fake.Projects[localProjects[1].Name], "master commit")
   927  	gitRemote := gitutil.New(fake.X, gitutil.RootDirOpt(fake.Projects[localProjects[1].Name]))
   928  	remoteRev, _ := gitRemote.CurrentRevision()
   929  	if err := fake.UpdateUniverse(false); err != nil {
   930  		t.Fatal(err)
   931  	}
   932  
   933  	localRev, _ := gitLocal.CurrentRevision()
   934  
   935  	if remoteRev == localRev {
   936  		t.Fatal("local branch master should not be updated")
   937  	}
   938  }
   939  
   940  // TestHookLoadSimple tests that manifest is loaded correctly
   941  // with correct project path in hook
   942  func TestHookLoadSimple(t *testing.T) {
   943  	p, fake, cleanup := setupUniverse(t)
   944  	defer cleanup()
   945  	err := fake.AddHook(project.Hook{Name: "hook1",
   946  		Action:      "action.sh",
   947  		ProjectName: p[0].Name})
   948  
   949  	if err != nil {
   950  		t.Fatal(err)
   951  	}
   952  	err = fake.UpdateUniverse(false)
   953  	if err == nil {
   954  		t.Fatal("run hook should throw error as there is no action.sh script")
   955  	}
   956  }
   957  
   958  // TestRunHookFlag tests that hook is not executed when flag is false
   959  func TestRunHookFlag(t *testing.T) {
   960  	p, fake, cleanup := setupUniverse(t)
   961  	defer cleanup()
   962  	err := fake.AddHook(project.Hook{Name: "hook1",
   963  		Action:      "action.sh",
   964  		ProjectName: p[0].Name})
   965  
   966  	if err != nil {
   967  		t.Fatal(err)
   968  	}
   969  	if err := project.UpdateUniverse(fake.X, false, false, true /*rebaseTracked*/, false, false, false /*run-hooks*/, false /*run-packages*/, project.DefaultHookTimeout, project.DefaultPackageTimeout); err != nil {
   970  		t.Fatal(err)
   971  	}
   972  }
   973  
   974  // TestHookLoadError tests that manifest load
   975  // throws error for invalid hook
   976  func TestHookLoadError(t *testing.T) {
   977  	_, fake, cleanup := setupUniverse(t)
   978  	defer cleanup()
   979  	err := fake.AddHook(project.Hook{Name: "hook1",
   980  		Action:      "action",
   981  		ProjectName: "non-existant"})
   982  
   983  	if err != nil {
   984  		t.Fatal(err)
   985  	}
   986  	err = fake.UpdateUniverse(false)
   987  	if err == nil {
   988  		t.Fatal("Update universe should throw error for the hook")
   989  	}
   990  	if !strings.Contains(err.Error(), "invalid hook") {
   991  		t.Fatal(err)
   992  	}
   993  }
   994  
   995  // TestUpdateUniverseWithRevision checks that UpdateUniverse will pull remote
   996  // projects at the specified revision.
   997  func TestUpdateUniverseWithRevision(t *testing.T) {
   998  	localProjects, fake, cleanup := setupUniverse(t)
   999  	defer cleanup()
  1000  
  1001  	// Set project 1's revision in the manifest to the current revision.
  1002  	g := gitutil.New(fake.X, gitutil.RootDirOpt(fake.Projects[localProjects[1].Name]))
  1003  	rev, err := g.CurrentRevision()
  1004  	if err != nil {
  1005  		t.Fatal(err)
  1006  	}
  1007  	m, err := fake.ReadRemoteManifest()
  1008  	if err != nil {
  1009  		t.Fatal(err)
  1010  	}
  1011  	projects := []project.Project{}
  1012  	for _, p := range m.Projects {
  1013  		if p.Name == localProjects[1].Name {
  1014  			p.Revision = rev
  1015  		}
  1016  		projects = append(projects, p)
  1017  	}
  1018  	m.Projects = projects
  1019  	if err := fake.WriteRemoteManifest(m); err != nil {
  1020  		t.Fatal(err)
  1021  	}
  1022  	// Update README in all projects.
  1023  	for _, remoteProjectDir := range fake.Projects {
  1024  		writeReadme(t, fake.X, remoteProjectDir, "new revision")
  1025  	}
  1026  	// Check that calling UpdateUniverse() updates all projects except for
  1027  	// project 1.
  1028  	if err := fake.UpdateUniverse(false); err != nil {
  1029  		t.Fatal(err)
  1030  	}
  1031  	for i, p := range localProjects {
  1032  		if i == 1 {
  1033  			checkReadme(t, fake.X, p, "initial readme")
  1034  		} else {
  1035  			checkReadme(t, fake.X, p, "new revision")
  1036  		}
  1037  	}
  1038  }
  1039  
  1040  // TestUpdateUniverseWithBadRevision checks that UpdateUniverse
  1041  // will not leave bad state behind.
  1042  //func TestUpdateUniverseWithBadRevision(t *testing.T) {
  1043  //	localProjects, fake, cleanup := setupUniverse(t)
  1044  //	defer cleanup()
  1045  //
  1046  //	m, err := fake.ReadRemoteManifest()
  1047  //	if err != nil {
  1048  //		t.Fatal(err)
  1049  //	}
  1050  //	projects := []project.Project{}
  1051  //	for _, p := range m.Projects {
  1052  //		if p.Name == localProjects[1].Name {
  1053  //			p.Revision = "badrev"
  1054  //		}
  1055  //		projects = append(projects, p)
  1056  //	}
  1057  //	m.Projects = projects
  1058  //	if err := fake.WriteRemoteManifest(m); err != nil {
  1059  //		t.Fatal(err)
  1060  //	}
  1061  //
  1062  //	if err := fake.UpdateUniverse(false); err == nil {
  1063  //		t.Fatal("should have thrown error")
  1064  //	}
  1065  //
  1066  //	if err := dirExists(localProjects[1].Path); err == nil {
  1067  //		t.Fatalf("expected project %q at path %q not to exist but it did", localProjects[1].Name, localProjects[1].Path)
  1068  //	}
  1069  //
  1070  //}
  1071  
  1072  func commitChanges(t *testing.T, jirix *jiri.X, dir string) {
  1073  	scm := gitutil.New(jirix, gitutil.UserNameOpt("John Doe"), gitutil.UserEmailOpt("john.doe@example.com"), gitutil.RootDirOpt(dir))
  1074  	if err := scm.AddUpdatedFiles(); err != nil {
  1075  		t.Fatal(err)
  1076  	}
  1077  	if err := scm.Commit(); err != nil {
  1078  		t.Fatal(err)
  1079  	}
  1080  }
  1081  
  1082  // TestSubDirToNestedProj checks that UpdateUniverse will correctly update when
  1083  // nested folder is converted to nested project
  1084  func TestSubDirToNestedProj(t *testing.T) {
  1085  	localProjects, fake, cleanup := setupUniverse(t)
  1086  	defer cleanup()
  1087  
  1088  	folderName := "nested_folder"
  1089  	nestedFolderPath := filepath.Join(fake.Projects[localProjects[1].Name], folderName)
  1090  	os.MkdirAll(nestedFolderPath, os.FileMode(0755))
  1091  	writeReadme(t, fake.X, nestedFolderPath, "nested folder")
  1092  
  1093  	if err := fake.UpdateUniverse(false); err != nil {
  1094  		t.Fatal(err)
  1095  	}
  1096  	os.RemoveAll(nestedFolderPath)
  1097  	commitChanges(t, fake.X, fake.Projects[localProjects[1].Name])
  1098  
  1099  	// Create nested project
  1100  	if err := fake.CreateRemoteProject(folderName); err != nil {
  1101  		t.Fatal(err)
  1102  	}
  1103  	writeReadme(t, fake.X, fake.Projects[folderName], "nested folder")
  1104  	p := project.Project{
  1105  		Name:   folderName,
  1106  		Path:   filepath.Join(localProjects[1].Path, folderName),
  1107  		Remote: fake.Projects[folderName],
  1108  	}
  1109  	if err := fake.AddProject(p); err != nil {
  1110  		t.Fatal(err)
  1111  	}
  1112  
  1113  	if err := fake.UpdateUniverse(false); err != nil {
  1114  		t.Fatal(err)
  1115  	}
  1116  	checkReadme(t, fake.X, p, "nested folder")
  1117  }
  1118  
  1119  // TestMoveNestedProjects checks that UpdateUniverse will correctly move nested projects
  1120  func TestMoveNestedProjects(t *testing.T) {
  1121  	localProjects, fake, cleanup := setupUniverse(t)
  1122  	defer cleanup()
  1123  
  1124  	folderName := "nested_proj"
  1125  	// Create nested project
  1126  	if err := fake.CreateRemoteProject(folderName); err != nil {
  1127  		t.Fatal(err)
  1128  	}
  1129  	writeReadme(t, fake.X, fake.Projects[folderName], "nested folder")
  1130  	p := project.Project{
  1131  		Name:   folderName,
  1132  		Path:   filepath.Join(localProjects[1].Path, folderName),
  1133  		Remote: fake.Projects[folderName],
  1134  	}
  1135  	if err := fake.AddProject(p); err != nil {
  1136  		t.Fatal(err)
  1137  	}
  1138  
  1139  	if err := fake.UpdateUniverse(false); err != nil {
  1140  		t.Fatal(err)
  1141  	}
  1142  
  1143  	oldProjectPath := localProjects[1].Path
  1144  	localProjects[1].Path = filepath.Join(fake.X.Root, "new-project-path")
  1145  	p.Path = filepath.Join(localProjects[1].Path, folderName)
  1146  	m, err := fake.ReadRemoteManifest()
  1147  	if err != nil {
  1148  		t.Fatal(err)
  1149  	}
  1150  	projects := []project.Project{}
  1151  	for _, proj := range m.Projects {
  1152  		if proj.Name == localProjects[1].Name {
  1153  			proj.Path = localProjects[1].Path
  1154  		}
  1155  		if proj.Name == p.Name {
  1156  			proj.Path = p.Path
  1157  		}
  1158  		projects = append(projects, proj)
  1159  	}
  1160  	m.Projects = projects
  1161  	if err := fake.WriteRemoteManifest(m); err != nil {
  1162  		t.Fatal(err)
  1163  	}
  1164  
  1165  	if err := fake.UpdateUniverse(false); err != nil {
  1166  		t.Fatal(err)
  1167  	}
  1168  	checkReadme(t, fake.X, localProjects[1], "initial readme")
  1169  	checkReadme(t, fake.X, p, "nested folder")
  1170  	if err := dirExists(oldProjectPath); err == nil {
  1171  		t.Fatalf("expected project %q at path %q not to exist but it did", localProjects[1].Name, oldProjectPath)
  1172  	}
  1173  }
  1174  
  1175  // TestUpdateUniverseWithUncommitted checks that uncommitted files are not droped
  1176  // by UpdateUniverse(). This ensures that the "git reset --hard" mechanism used
  1177  // for pointing the master branch to a fixed revision does not lose work in
  1178  // progress.
  1179  func TestUpdateUniverseWithUncommitted(t *testing.T) {
  1180  	localProjects, fake, cleanup := setupUniverse(t)
  1181  	defer cleanup()
  1182  	if err := fake.UpdateUniverse(false); err != nil {
  1183  		t.Fatal(err)
  1184  	}
  1185  
  1186  	// Create an uncommitted file in project 1.
  1187  	file, perm, want := filepath.Join(localProjects[1].Path, "uncommitted_file"), os.FileMode(0644), []byte("uncommitted work")
  1188  	if err := ioutil.WriteFile(file, want, perm); err != nil {
  1189  		t.Fatalf("WriteFile(%v, %v) failed: %v", file, err, perm)
  1190  	}
  1191  	if err := fake.UpdateUniverse(false); err != nil {
  1192  		t.Fatal(err)
  1193  	}
  1194  	got, err := ioutil.ReadFile(file)
  1195  	if err != nil {
  1196  		t.Fatalf("%v", err)
  1197  	}
  1198  	if bytes.Compare(got, want) != 0 {
  1199  		t.Fatalf("unexpected content %v:\ngot\n%s\nwant\n%s\n", localProjects[1], got, want)
  1200  	}
  1201  }
  1202  
  1203  // TestUpdateUniverseMovedProject checks that UpdateUniverse can move a
  1204  // project.
  1205  func TestUpdateUniverseMovedProject(t *testing.T) {
  1206  	localProjects, fake, cleanup := setupUniverse(t)
  1207  	defer cleanup()
  1208  	if err := fake.UpdateUniverse(false); err != nil {
  1209  		t.Fatal(err)
  1210  	}
  1211  
  1212  	// Update the local path at which project 1 is located.
  1213  	m, err := fake.ReadRemoteManifest()
  1214  	if err != nil {
  1215  		t.Fatal(err)
  1216  	}
  1217  	oldProjectPath := localProjects[1].Path
  1218  	localProjects[1].Path = filepath.Join(fake.X.Root, "new-project-path")
  1219  	projects := []project.Project{}
  1220  	for _, p := range m.Projects {
  1221  		if p.Name == localProjects[1].Name {
  1222  			p.Path = localProjects[1].Path
  1223  		}
  1224  		projects = append(projects, p)
  1225  	}
  1226  	m.Projects = projects
  1227  	if err := fake.WriteRemoteManifest(m); err != nil {
  1228  		t.Fatal(err)
  1229  	}
  1230  	// Check that UpdateUniverse() moves the local copy of the project 1.
  1231  	if err := fake.UpdateUniverse(false); err != nil {
  1232  		t.Fatal(err)
  1233  	}
  1234  	if err := dirExists(oldProjectPath); err == nil {
  1235  		t.Fatalf("expected project %q at path %q not to exist but it did", localProjects[1].Name, oldProjectPath)
  1236  	}
  1237  	if err := dirExists(localProjects[2].Path); err != nil {
  1238  		t.Fatalf("expected project %q at path %q to exist but it did not", localProjects[1].Name, localProjects[1].Path)
  1239  	}
  1240  	checkReadme(t, fake.X, localProjects[1], "initial readme")
  1241  }
  1242  
  1243  // TestUpdateUniverseChangeRemote checks that UpdateUniverse can change remote
  1244  // of a project.
  1245  func TestUpdateUniverseChangeRemote(t *testing.T) {
  1246  	localProjects, fake, cleanup := setupUniverse(t)
  1247  	defer cleanup()
  1248  	if err := fake.UpdateUniverse(false); err != nil {
  1249  		t.Fatal(err)
  1250  	}
  1251  
  1252  	changedRemoteDir := fake.Projects[localProjects[1].Name] + "-remote-changed"
  1253  	if err := os.Rename(fake.Projects[localProjects[1].Name], changedRemoteDir); err != nil {
  1254  		t.Fatal(err)
  1255  	}
  1256  
  1257  	writeReadme(t, fake.X, changedRemoteDir, "new commit")
  1258  
  1259  	// Update the local path at which project 1 is located.
  1260  	m, err := fake.ReadRemoteManifest()
  1261  	if err != nil {
  1262  		t.Fatal(err)
  1263  	}
  1264  	projects := []project.Project{}
  1265  	for _, p := range m.Projects {
  1266  		if p.Name == localProjects[1].Name {
  1267  			p.Remote = changedRemoteDir
  1268  		}
  1269  		projects = append(projects, p)
  1270  	}
  1271  	m.Projects = projects
  1272  	if err := fake.WriteRemoteManifest(m); err != nil {
  1273  		t.Fatal(err)
  1274  	}
  1275  	// Check that UpdateUniverse() moves the local copy of the project 1.
  1276  	if err := fake.UpdateUniverse(false); err != nil {
  1277  		t.Fatal(err)
  1278  	}
  1279  	checkReadme(t, fake.X, localProjects[1], "new commit")
  1280  }
  1281  
  1282  func TestIgnoredProjectsNotMoved(t *testing.T) {
  1283  	localProjects, fake, cleanup := setupUniverse(t)
  1284  	defer cleanup()
  1285  	if err := fake.UpdateUniverse(false); err != nil {
  1286  		t.Fatal(err)
  1287  	}
  1288  
  1289  	// Update the local path at which project 1 is located.
  1290  	m, err := fake.ReadRemoteManifest()
  1291  	if err != nil {
  1292  		t.Fatal(err)
  1293  	}
  1294  	lc := project.LocalConfig{Ignore: true}
  1295  	project.WriteLocalConfig(fake.X, localProjects[1], lc)
  1296  	oldProjectPath := localProjects[1].Path
  1297  	localProjects[1].Path = filepath.Join(fake.X.Root, "new-project-path")
  1298  	projects := []project.Project{}
  1299  	for _, p := range m.Projects {
  1300  		if p.Name == localProjects[1].Name {
  1301  			p.Path = localProjects[1].Path
  1302  		}
  1303  		projects = append(projects, p)
  1304  	}
  1305  	m.Projects = projects
  1306  	if err := fake.WriteRemoteManifest(m); err != nil {
  1307  		t.Fatal(err)
  1308  	}
  1309  
  1310  	// Check that UpdateUniverse() does not move the local copy of the project 1.
  1311  	if err := fake.UpdateUniverse(false); err != nil {
  1312  		t.Fatal(err)
  1313  	}
  1314  	if err := dirExists(oldProjectPath); err != nil {
  1315  		t.Fatalf("expected project %q at path %q to exist but it did not: %s", localProjects[1].Name, oldProjectPath, err)
  1316  	}
  1317  	if err := dirExists(localProjects[2].Path); err != nil {
  1318  		t.Fatalf("expected project %q at path %q to not exist but it did", localProjects[1].Name, localProjects[1].Path)
  1319  	}
  1320  }
  1321  
  1322  // TestUpdateUniverseRenamedProject checks that UpdateUniverse can update
  1323  // renamed project.
  1324  func TestUpdateUniverseRenamedProject(t *testing.T) {
  1325  	localProjects, fake, cleanup := setupUniverse(t)
  1326  	defer cleanup()
  1327  	if err := fake.UpdateUniverse(false); err != nil {
  1328  		t.Fatal(err)
  1329  	}
  1330  
  1331  	m, err := fake.ReadRemoteManifest()
  1332  	if err != nil {
  1333  		t.Fatal(err)
  1334  	}
  1335  	oldProjectName := localProjects[1].Name
  1336  	localProjects[1].Name = localProjects[1].Name + "new"
  1337  	projects := []project.Project{}
  1338  	for _, p := range m.Projects {
  1339  		if p.Name == oldProjectName {
  1340  			p.Name = localProjects[1].Name
  1341  		}
  1342  		projects = append(projects, p)
  1343  	}
  1344  	m.Projects = projects
  1345  	if err := fake.WriteRemoteManifest(m); err != nil {
  1346  		t.Fatal(err)
  1347  	}
  1348  
  1349  	if err := fake.UpdateUniverse(false); err != nil {
  1350  		t.Fatal(err)
  1351  	}
  1352  	newLocalProjects, err := project.LocalProjects(fake.X, project.FullScan)
  1353  	if err != nil {
  1354  		t.Fatal(err)
  1355  	}
  1356  	projectFound := false
  1357  	for _, p := range newLocalProjects {
  1358  		if p.Name == localProjects[1].Name {
  1359  			projectFound = true
  1360  		}
  1361  	}
  1362  	if !projectFound {
  1363  		t.Fatalf("Project with updated name(%v) not found", localProjects[1].Name)
  1364  	}
  1365  }
  1366  
  1367  // testUpdateUniverseDeletedProject checks that UpdateUniverse will delete a
  1368  // project if gc=true.
  1369  func testUpdateUniverseDeletedProject(t *testing.T, testDirtyProjectDelete, testProjectWithBranch bool) {
  1370  	localProjects, fake, cleanup := setupUniverse(t)
  1371  	defer cleanup()
  1372  	if err := fake.UpdateUniverse(false); err != nil {
  1373  		t.Fatal(err)
  1374  	}
  1375  
  1376  	// Delete project 1.
  1377  	m, err := fake.ReadRemoteManifest()
  1378  	if err != nil {
  1379  		t.Fatal(err)
  1380  	}
  1381  	projects := []project.Project{}
  1382  	if testDirtyProjectDelete {
  1383  		writeUncommitedFile(t, fake.X, localProjects[4].Path, "extra", "")
  1384  	} else if testProjectWithBranch {
  1385  		// Create and checkout master.
  1386  		git := gitutil.New(fake.X, gitutil.RootDirOpt(localProjects[4].Path))
  1387  		if err := git.CreateAndCheckoutBranch("master"); err != nil {
  1388  			t.Fatal(err)
  1389  		}
  1390  	}
  1391  	for _, p := range m.Projects {
  1392  		skip := false
  1393  		for i := 1; i <= 5; i++ {
  1394  			if p.Name == localProjects[i].Name {
  1395  				skip = true
  1396  			}
  1397  		}
  1398  		if skip {
  1399  			continue
  1400  		}
  1401  		projects = append(projects, p)
  1402  	}
  1403  	m.Projects = projects
  1404  	if err := fake.WriteRemoteManifest(m); err != nil {
  1405  		t.Fatal(err)
  1406  	}
  1407  	// Check that UpdateUniverse() with gc=false does not delete the local copy
  1408  	// of the project.
  1409  	if err := fake.UpdateUniverse(false); err != nil {
  1410  		t.Fatal(err)
  1411  	}
  1412  	for i := 1; i <= 5; i++ {
  1413  		if err := dirExists(localProjects[i].Path); err != nil {
  1414  			t.Fatalf("expected project %q at path %q to exist but it did not", localProjects[i].Name, localProjects[i].Path)
  1415  		}
  1416  		checkReadme(t, fake.X, localProjects[i], "initial readme")
  1417  	}
  1418  	// Check that UpdateUniverse() with gc=true does delete the local copy of
  1419  	// the project.
  1420  	if err := fake.UpdateUniverse(true); err != nil {
  1421  		t.Fatal(err)
  1422  	}
  1423  	for i := 1; i <= 5; i++ {
  1424  		err := dirExists(localProjects[i].Path)
  1425  		if (testProjectWithBranch || testDirtyProjectDelete) && i >= 2 && i <= 4 {
  1426  			if err != nil {
  1427  				t.Fatalf("expected project %q at path %q to exist but it did not", localProjects[i].Name, localProjects[i].Path)
  1428  			}
  1429  		} else if err == nil {
  1430  			t.Fatalf("expected project %q at path %q not to exist but it did", localProjects[i].Name, localProjects[i].Path)
  1431  		}
  1432  	}
  1433  }
  1434  
  1435  func TestUpdateUniverseDeletedProject(t *testing.T) {
  1436  	testUpdateUniverseDeletedProject(t, false, false)
  1437  	testUpdateUniverseDeletedProject(t, true, false)
  1438  	testUpdateUniverseDeletedProject(t, false, true)
  1439  }
  1440  
  1441  func TestIgnoredProjectsNotDeleted(t *testing.T) {
  1442  	localProjects, fake, cleanup := setupUniverse(t)
  1443  	defer cleanup()
  1444  	if err := fake.UpdateUniverse(false); err != nil {
  1445  		t.Fatal(err)
  1446  	}
  1447  
  1448  	// Delete project 1.
  1449  	m, err := fake.ReadRemoteManifest()
  1450  	if err != nil {
  1451  		t.Fatal(err)
  1452  	}
  1453  	projects := []project.Project{}
  1454  	for _, p := range m.Projects {
  1455  		if p.Name == localProjects[1].Name {
  1456  			continue
  1457  		}
  1458  		projects = append(projects, p)
  1459  	}
  1460  	m.Projects = projects
  1461  	if err := fake.WriteRemoteManifest(m); err != nil {
  1462  		t.Fatal(err)
  1463  	}
  1464  	lc := project.LocalConfig{Ignore: true}
  1465  	project.WriteLocalConfig(fake.X, localProjects[1], lc)
  1466  	if err := fake.UpdateUniverse(true); err != nil {
  1467  		t.Fatal(err)
  1468  	}
  1469  	if err := dirExists(localProjects[1].Path); err != nil {
  1470  		t.Fatalf("expected project %q at path %q to exist but it did not: %s", localProjects[1].Name, localProjects[1].Path, err)
  1471  	}
  1472  }
  1473  
  1474  // TestUpdateUniverseNewProjectSamePath checks that UpdateUniverse can handle a
  1475  // new project with the same path as a deleted project, but a different path.
  1476  func TestUpdateUniverseNewProjectSamePath(t *testing.T) {
  1477  	localProjects, fake, cleanup := setupUniverse(t)
  1478  	defer cleanup()
  1479  	if err := fake.UpdateUniverse(false); err != nil {
  1480  		t.Fatal(err)
  1481  	}
  1482  
  1483  	// Delete a project 1 and create a new one with a different name but the
  1484  	// same path.
  1485  	m, err := fake.ReadRemoteManifest()
  1486  	if err != nil {
  1487  		t.Fatal(err)
  1488  	}
  1489  	newProjectName := "new-project-name"
  1490  	projects := []project.Project{}
  1491  	for _, p := range m.Projects {
  1492  		if p.Path == localProjects[1].Path {
  1493  			p.Name = newProjectName
  1494  		}
  1495  		projects = append(projects, p)
  1496  	}
  1497  	localProjects[1].Name = newProjectName
  1498  	m.Projects = projects
  1499  	if err := fake.WriteRemoteManifest(m); err != nil {
  1500  		t.Fatal(err)
  1501  	}
  1502  	// Check that UpdateUniverse() does not fail.
  1503  	if err := fake.UpdateUniverse(true); err != nil {
  1504  		t.Fatal(err)
  1505  	}
  1506  }
  1507  
  1508  // TestUpdateUniverseRemoteBranch checks that UpdateUniverse can pull from a
  1509  // non-master remote branch.
  1510  func TestUpdateUniverseRemoteBranch(t *testing.T) {
  1511  	localProjects, fake, cleanup := setupUniverse(t)
  1512  	defer cleanup()
  1513  	if err := fake.UpdateUniverse(false); err != nil {
  1514  		t.Fatal(err)
  1515  	}
  1516  
  1517  	// Commit to master branch of a project 1.
  1518  	writeReadme(t, fake.X, fake.Projects[localProjects[1].Name], "master commit")
  1519  	// Create and checkout a new branch of project 1 and make a new commit.
  1520  	git := gitutil.New(fake.X, gitutil.RootDirOpt(fake.Projects[localProjects[1].Name]))
  1521  	if err := git.CreateAndCheckoutBranch("non-master"); err != nil {
  1522  		t.Fatal(err)
  1523  	}
  1524  	writeReadme(t, fake.X, fake.Projects[localProjects[1].Name], "non-master commit")
  1525  	// Point the manifest to the new non-master branch.
  1526  	m, err := fake.ReadRemoteManifest()
  1527  	if err != nil {
  1528  		t.Fatal(err)
  1529  	}
  1530  	projects := []project.Project{}
  1531  	for _, p := range m.Projects {
  1532  		if p.Name == localProjects[1].Name {
  1533  			p.RemoteBranch = "non-master"
  1534  		}
  1535  		projects = append(projects, p)
  1536  	}
  1537  	m.Projects = projects
  1538  	if err := fake.WriteRemoteManifest(m); err != nil {
  1539  		t.Fatal(err)
  1540  	}
  1541  	// Check that UpdateUniverse pulls the commit from the non-master branch.
  1542  	if err := fake.UpdateUniverse(false); err != nil {
  1543  		t.Fatal(err)
  1544  	}
  1545  	checkReadme(t, fake.X, localProjects[1], "non-master commit")
  1546  }
  1547  
  1548  // TestUpdateWhenRemoteChangesRebased checks that UpdateUniverse can pull from a
  1549  // non-master remote branch if the local changes were rebased somewhere else(gerrit)
  1550  // before being pushed to remote
  1551  func TestUpdateWhenRemoteChangesRebased(t *testing.T) {
  1552  	localProjects, fake, cleanup := setupUniverse(t)
  1553  	defer cleanup()
  1554  	if err := fake.UpdateUniverse(false); err != nil {
  1555  		t.Fatal(err)
  1556  	}
  1557  
  1558  	gitRemote := gitutil.New(fake.X, gitutil.UserNameOpt("John Doe"), gitutil.UserEmailOpt("john.doe@example.com"), gitutil.RootDirOpt(fake.Projects[localProjects[1].Name]))
  1559  	if err := gitRemote.CreateAndCheckoutBranch("non-master"); err != nil {
  1560  		t.Fatal(err)
  1561  	}
  1562  
  1563  	gitLocal := gitutil.New(fake.X, gitutil.UserNameOpt("John Doe"), gitutil.UserEmailOpt("john.doe@example.com"), gitutil.RootDirOpt(localProjects[1].Path))
  1564  	if err := gitLocal.Fetch("origin", gitutil.PruneOpt(true)); err != nil {
  1565  		t.Fatal(err)
  1566  	}
  1567  
  1568  	// checkout branch in local repo
  1569  	if err := gitLocal.CheckoutBranch("non-master"); err != nil {
  1570  		t.Fatal(err)
  1571  	}
  1572  
  1573  	// Create commits in remote repo
  1574  	writeReadme(t, fake.X, fake.Projects[localProjects[1].Name], "non-master commit")
  1575  	writeFile(t, fake.X, fake.Projects[localProjects[1].Name], "file1", "file1")
  1576  	file1CommitRev, _ := gitRemote.CurrentRevision()
  1577  	writeFile(t, fake.X, fake.Projects[localProjects[1].Name], "file2", "file2")
  1578  	file2CommitRev, _ := gitRemote.CurrentRevision()
  1579  
  1580  	if err := gitLocal.Fetch("origin", gitutil.PruneOpt(true)); err != nil {
  1581  		t.Fatal(err)
  1582  	}
  1583  
  1584  	// Cherry pick creation of file1, so that it acts like been rebased on remote repo
  1585  	// As there is a commit creating README on remote not in local repo
  1586  	if err := gitLocal.CherryPick(file1CommitRev); err != nil {
  1587  		t.Fatal(err)
  1588  	}
  1589  
  1590  	if err := project.UpdateUniverse(fake.X, false, false, true /*rebaseTracked*/, false, false, true /*run-hooks*/, true /*run-packages*/, project.DefaultHookTimeout, project.DefaultPackageTimeout); err != nil {
  1591  		t.Fatal(err)
  1592  	}
  1593  
  1594  	// It rebased properly and pulled latest changes
  1595  	localRev, _ := gitLocal.CurrentRevision()
  1596  	if file2CommitRev != localRev {
  1597  		t.Fatalf("Current commit is %v, it should be %v\n", localRev, file2CommitRev)
  1598  	}
  1599  }
  1600  
  1601  func TestUpdateWhenConflictMerge(t *testing.T) {
  1602  	localProjects, fake, cleanup := setupUniverse(t)
  1603  	defer cleanup()
  1604  	if err := fake.UpdateUniverse(false); err != nil {
  1605  		t.Fatal(err)
  1606  	}
  1607  
  1608  	gitRemote := gitutil.New(fake.X, gitutil.UserNameOpt("John Doe"), gitutil.UserEmailOpt("john.doe@example.com"), gitutil.RootDirOpt(fake.Projects[localProjects[1].Name]))
  1609  	if err := gitRemote.CreateAndCheckoutBranch("non-master"); err != nil {
  1610  		t.Fatal(err)
  1611  	}
  1612  
  1613  	gitLocal := gitutil.New(fake.X, gitutil.UserNameOpt("John Doe"), gitutil.UserEmailOpt("john.doe@example.com"), gitutil.RootDirOpt(localProjects[1].Path))
  1614  	if err := gitLocal.Fetch("origin", gitutil.PruneOpt(true)); err != nil {
  1615  		t.Fatal(err)
  1616  	}
  1617  
  1618  	// checkout branch in local repo
  1619  	if err := gitLocal.CheckoutBranch("non-master"); err != nil {
  1620  		t.Fatal(err)
  1621  	}
  1622  
  1623  	// Create commits in remote repo
  1624  	writeReadme(t, fake.X, fake.Projects[localProjects[1].Name], "non-master commit")
  1625  	writeFile(t, fake.X, fake.Projects[localProjects[1].Name], "file1", "file1")
  1626  	file1CommitRev, _ := gitRemote.CurrentRevision()
  1627  	writeFile(t, fake.X, fake.Projects[localProjects[1].Name], "file2", "file2")
  1628  
  1629  	if err := gitLocal.Fetch("origin", gitutil.PruneOpt(true)); err != nil {
  1630  		t.Fatal(err)
  1631  	}
  1632  
  1633  	// Cherry pick creation of file1, so that it acts like been rebased on remote repo
  1634  	// This would act like conflicting merge
  1635  	if err := gitLocal.CherryPick(file1CommitRev); err != nil {
  1636  		t.Fatal(err)
  1637  	}
  1638  	rev, _ := gitLocal.CurrentRevision()
  1639  
  1640  	if err := fake.UpdateUniverse(false); err != nil {
  1641  		t.Fatal(err)
  1642  	}
  1643  
  1644  	localRev, _ := gitLocal.CurrentRevision()
  1645  	if rev != localRev {
  1646  		t.Fatalf("Current commit is %v, it should be %v\n", localRev, rev)
  1647  	}
  1648  	checkJiriRevFiles(t, fake.X, localProjects[1])
  1649  }
  1650  
  1651  func TestTagNotContainedInBranch(t *testing.T) {
  1652  	localProjects, fake, cleanup := setupUniverse(t)
  1653  	defer cleanup()
  1654  	if err := fake.UpdateUniverse(false); err != nil {
  1655  		t.Fatal(err)
  1656  	}
  1657  
  1658  	gitRemote := gitutil.New(fake.X, gitutil.UserNameOpt("John Doe"), gitutil.UserEmailOpt("john.doe@example.com"), gitutil.RootDirOpt(fake.Projects[localProjects[1].Name]))
  1659  	if err := gitRemote.CreateAndCheckoutBranch("non-master"); err != nil {
  1660  		t.Fatal(err)
  1661  	}
  1662  
  1663  	// Create commits in remote repo
  1664  	writeReadme(t, fake.X, fake.Projects[localProjects[1].Name], "non-master commit")
  1665  	writeFile(t, fake.X, fake.Projects[localProjects[1].Name], "file1", "file1")
  1666  	file1CommitRev, _ := gitRemote.CurrentRevision()
  1667  	if err := gitRemote.CreateLightweightTag("testtag"); err != nil {
  1668  		t.Fatalf("Creating tag: %s", err)
  1669  
  1670  	}
  1671  	if err := gitRemote.CheckoutBranch("master"); err != nil {
  1672  		t.Fatal(err)
  1673  	}
  1674  	if err := gitRemote.DeleteBranch("non-master", gitutil.ForceOpt(true)); err != nil {
  1675  		t.Fatal(err)
  1676  	}
  1677  
  1678  	m, err := fake.ReadRemoteManifest()
  1679  	if err != nil {
  1680  		t.Fatal(err)
  1681  	}
  1682  	projects := []project.Project{}
  1683  	for _, p := range m.Projects {
  1684  		if p.Name == localProjects[1].Name {
  1685  			p.Revision = "testtag"
  1686  		}
  1687  		projects = append(projects, p)
  1688  	}
  1689  	m.Projects = projects
  1690  	if err := fake.WriteRemoteManifest(m); err != nil {
  1691  		t.Fatal(err)
  1692  	}
  1693  
  1694  	if err := fake.UpdateUniverse(false); err != nil {
  1695  		t.Fatal(err)
  1696  	}
  1697  
  1698  	gitLocal := gitutil.New(fake.X, gitutil.RootDirOpt(localProjects[1].Path))
  1699  	// It rebased properly and pulled latest changes
  1700  	localRev, _ := gitLocal.CurrentRevision()
  1701  	if file1CommitRev != localRev {
  1702  		t.Fatalf("Current commit is %v, it should be %v\n", localRev, file1CommitRev)
  1703  	}
  1704  }
  1705  
  1706  // TestCheckoutSnapshotUrl tests checking out snapshot functionality from a url
  1707  func TestCheckoutSnapshotUrl(t *testing.T) {
  1708  	testCheckoutSnapshot(t, true)
  1709  }
  1710  
  1711  // TestCheckoutSnapshotFileSystem tests checking out snapshot functionality from filesystem
  1712  func TestCheckoutSnapshotFileSystem(t *testing.T) {
  1713  	testCheckoutSnapshot(t, false)
  1714  }
  1715  
  1716  func testCheckoutSnapshot(t *testing.T, testURL bool) {
  1717  	localProjects, fake, cleanup := setupUniverse(t)
  1718  	defer cleanup()
  1719  	var oldCommitRevs []string
  1720  	var latestCommitRevs []string
  1721  
  1722  	for i, localProject := range localProjects {
  1723  		gitRemote := gitutil.New(fake.X, gitutil.RootDirOpt(fake.Projects[localProject.Name]))
  1724  		writeFile(t, fake.X, fake.Projects[localProject.Name], "file1"+strconv.Itoa(i), "file1"+strconv.Itoa(i))
  1725  		file1CommitRev, _ := gitRemote.CurrentRevision()
  1726  		oldCommitRevs = append(oldCommitRevs, file1CommitRev)
  1727  		writeFile(t, fake.X, fake.Projects[localProject.Name], "file2"+strconv.Itoa(i), "file2"+strconv.Itoa(i))
  1728  		file2CommitRev, _ := gitRemote.CurrentRevision()
  1729  		latestCommitRevs = append(latestCommitRevs, file2CommitRev)
  1730  	}
  1731  
  1732  	if err := fake.UpdateUniverse(false); err != nil {
  1733  		t.Fatal(err)
  1734  	}
  1735  
  1736  	for i, localProject := range localProjects {
  1737  		gitLocal := gitutil.New(fake.X, gitutil.UserNameOpt("John Doe"), gitutil.UserEmailOpt("john.doe@example.com"), gitutil.RootDirOpt(localProject.Path))
  1738  		rev, _ := gitLocal.CurrentRevision()
  1739  		if rev != latestCommitRevs[i] {
  1740  			t.Fatalf("Current commit for project %q is %v, it should be %v\n", localProject.Name, rev, latestCommitRevs[i])
  1741  		}
  1742  
  1743  		// Test case when local repo in on a branch
  1744  		if i == 1 {
  1745  			if err := gitLocal.CheckoutBranch("master"); err != nil {
  1746  				t.Fatal(err)
  1747  			}
  1748  		}
  1749  	}
  1750  	dir, err := ioutil.TempDir("", "snap")
  1751  	if err != nil {
  1752  		t.Fatal(err)
  1753  	}
  1754  	defer os.RemoveAll(dir)
  1755  	manifest := &project.Manifest{Version: project.ManifestVersion}
  1756  	for _, localProject := range localProjects {
  1757  		manifest.Projects = append(manifest.Projects, localProject)
  1758  	}
  1759  	manifest.Projects[0].Revision = oldCommitRevs[0]
  1760  	manifest.Projects[1].Revision = oldCommitRevs[1]
  1761  
  1762  	// Test case when snapshot specifies latest revision
  1763  	manifest.Projects[2].Revision = latestCommitRevs[2]
  1764  
  1765  	manifest.Projects[3].Revision = oldCommitRevs[3]
  1766  	manifest.Projects[4].Revision = latestCommitRevs[4]
  1767  	manifest.Projects[5].Revision = oldCommitRevs[5]
  1768  	manifest.Projects[6].Revision = latestCommitRevs[6]
  1769  	snapshotFile := filepath.Join(dir, "snapshot")
  1770  	manifest.ToFile(fake.X, snapshotFile)
  1771  	if testURL {
  1772  		snapBytes, err := ioutil.ReadFile(snapshotFile)
  1773  		if err != nil {
  1774  			t.Fatal(err)
  1775  		}
  1776  		server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
  1777  			w.Header().Set("Content-Type", "html/text")
  1778  			fmt.Fprintln(w, string(snapBytes[:]))
  1779  		}))
  1780  		defer server.Close()
  1781  
  1782  		project.CheckoutSnapshot(fake.X, server.URL, false, true /*run-hooks*/, true /*run-packages*/, project.DefaultHookTimeout, project.DefaultPackageTimeout)
  1783  	} else {
  1784  		project.CheckoutSnapshot(fake.X, snapshotFile, false, true /*run-hooks*/, true /*run-packages*/, project.DefaultHookTimeout, project.DefaultPackageTimeout)
  1785  	}
  1786  	sort.Sort(project.ProjectsByPath(localProjects))
  1787  	for i, localProject := range localProjects {
  1788  		gitLocal := gitutil.New(fake.X, gitutil.RootDirOpt(localProject.Path))
  1789  		rev, _ := gitLocal.CurrentRevision()
  1790  		expectedRev := manifest.Projects[i].Revision
  1791  		if rev != expectedRev {
  1792  			t.Fatalf("Current commit for project %q is %v, it should be %v\n", localProject.Name, rev, expectedRev)
  1793  		}
  1794  	}
  1795  }
  1796  
  1797  func testLocalBranchesAreUpdated(t *testing.T, shouldLocalBeOnABranch, rebaseAll bool) {
  1798  	localProjects, fake, cleanup := setupUniverse(t)
  1799  	defer cleanup()
  1800  	if err := fake.UpdateUniverse(false); err != nil {
  1801  		t.Fatal(err)
  1802  	}
  1803  
  1804  	gitRemote := gitutil.New(fake.X, gitutil.UserNameOpt("John Doe"), gitutil.UserEmailOpt("john.doe@example.com"), gitutil.RootDirOpt(fake.Projects[localProjects[1].Name]))
  1805  	if err := gitRemote.CreateAndCheckoutBranch("non-master"); err != nil {
  1806  		t.Fatal(err)
  1807  	}
  1808  
  1809  	// This will fetch non-master to local
  1810  	if err := fake.UpdateUniverse(false); err != nil {
  1811  		t.Fatal(err)
  1812  	}
  1813  
  1814  	writeReadme(t, fake.X, fake.Projects[localProjects[1].Name], "non-master commit")
  1815  
  1816  	if err := gitRemote.CheckoutBranch("master"); err != nil {
  1817  		t.Fatal(err)
  1818  	}
  1819  	writeReadme(t, fake.X, fake.Projects[localProjects[1].Name], "master commit")
  1820  
  1821  	gitLocal := gitutil.New(fake.X, gitutil.UserNameOpt("John Doe"), gitutil.UserEmailOpt("john.doe@example.com"), gitutil.RootDirOpt(localProjects[1].Path))
  1822  
  1823  	// This will create a local branch non-master
  1824  	if err := gitLocal.CheckoutBranch("non-master"); err != nil {
  1825  		t.Fatal(err)
  1826  	}
  1827  
  1828  	// Go back to detached HEAD
  1829  	if !shouldLocalBeOnABranch {
  1830  		if err := gitLocal.CheckoutBranch("HEAD", gitutil.DetachOpt(true)); err != nil {
  1831  			t.Fatal(err)
  1832  		}
  1833  	}
  1834  
  1835  	if err := project.UpdateUniverse(fake.X, false, false, false, false, rebaseAll, true /*run-hooks*/, true /*run-packages*/, project.DefaultHookTimeout, project.DefaultPackageTimeout); err != nil {
  1836  		t.Fatal(err)
  1837  	}
  1838  
  1839  	if shouldLocalBeOnABranch && gitLocal.IsOnBranch() == false {
  1840  		t.Fatalf("local repo should be on the branch after update")
  1841  	} else if !shouldLocalBeOnABranch && gitLocal.IsOnBranch() == true {
  1842  		t.Fatalf("local repo should be on detached head after update")
  1843  	}
  1844  
  1845  	projects, err := project.LocalProjects(fake.X, project.FastScan)
  1846  	if err != nil {
  1847  		t.Fatal(err)
  1848  	}
  1849  
  1850  	states, err := project.GetProjectStates(fake.X, projects, false)
  1851  	if err != nil {
  1852  		t.Fatal(err)
  1853  	}
  1854  
  1855  	state := states[localProjects[1].Key()]
  1856  	if shouldLocalBeOnABranch && state.CurrentBranch.Name != "non-master" {
  1857  		t.Fatalf("local repo should be on branch(non-master) it is on %q", state.CurrentBranch.Name)
  1858  	}
  1859  
  1860  	if rebaseAll {
  1861  		for _, branch := range state.Branches {
  1862  			if branch.Tracking != nil {
  1863  				if branch.Revision != branch.Tracking.Revision {
  1864  					t.Fatalf("branch %q has different revision then remote branch %q", branch.Name, branch.Tracking.Name)
  1865  				}
  1866  			}
  1867  		}
  1868  	} else {
  1869  		for _, branch := range state.Branches {
  1870  			if branch.Tracking != nil {
  1871  				if branch.Name == state.CurrentBranch.Name {
  1872  					if branch.Revision != branch.Tracking.Revision {
  1873  						t.Fatalf("branch %q has different revision then remote branch %q", branch.Name, branch.Tracking.Name)
  1874  					}
  1875  				} else if branch.Revision == branch.Tracking.Revision {
  1876  					t.Fatalf("branch %q should have different revision then remote branch %q", branch.Name, branch.Tracking.Name)
  1877  				}
  1878  			}
  1879  		}
  1880  	}
  1881  }
  1882  
  1883  // TestLocalBranchesAreUpdatedWhenOnHead test that all the local branches are
  1884  // updated on jiri update when local repo is on detached head
  1885  func TestLocalBranchesAreUpdatedWhenOnHead(t *testing.T) {
  1886  	testLocalBranchesAreUpdated(t, false, true)
  1887  }
  1888  
  1889  // TestLocalBranchesAreUpdatedWhenOnBranch test that all the local branches are
  1890  // updated on jiri update when local repo is on a branch
  1891  func TestLocalBranchesAreUpdatedWhenOnBranch(t *testing.T) {
  1892  	testLocalBranchesAreUpdated(t, true, true)
  1893  }
  1894  
  1895  // TestLocalBranchesNotUpdatedWhenOnHead test that all the local branches are not
  1896  // updated on jiri update when local repo is on detached head and rebaseAll is false
  1897  func TestLocalBranchesNotUpdatedWhenOnHead(t *testing.T) {
  1898  	testLocalBranchesAreUpdated(t, false, false)
  1899  }
  1900  
  1901  // TestLocalBranchesAreUpdatedWhenOnBranch test that all the local branches are not
  1902  // updated on jiri update when local repo is on a branch and rebaseAll is false
  1903  func TestLocalBranchesNotUpdatedWhenOnBranch(t *testing.T) {
  1904  	testLocalBranchesAreUpdated(t, true, false)
  1905  }
  1906  
  1907  func TestFileImportCycle(t *testing.T) {
  1908  	jirix, cleanup := xtest.NewX(t)
  1909  	defer cleanup()
  1910  
  1911  	// Set up the cycle .jiri_manifest -> A -> B -> A
  1912  	jiriManifest := project.Manifest{
  1913  		LocalImports: []project.LocalImport{
  1914  			{File: "A"},
  1915  		},
  1916  	}
  1917  	manifestA := project.Manifest{
  1918  		LocalImports: []project.LocalImport{
  1919  			{File: "B"},
  1920  		},
  1921  	}
  1922  	manifestB := project.Manifest{
  1923  		LocalImports: []project.LocalImport{
  1924  			{File: "A"},
  1925  		},
  1926  	}
  1927  	if err := jiriManifest.ToFile(jirix, jirix.JiriManifestFile()); err != nil {
  1928  		t.Fatal(err)
  1929  	}
  1930  	if err := manifestA.ToFile(jirix, filepath.Join(jirix.Root, "A")); err != nil {
  1931  		t.Fatal(err)
  1932  	}
  1933  	if err := manifestB.ToFile(jirix, filepath.Join(jirix.Root, "B")); err != nil {
  1934  		t.Fatal(err)
  1935  	}
  1936  
  1937  	// The update should complain about the cycle.
  1938  	err := project.UpdateUniverse(jirix, false, false, false, false, false, true /*run-hooks*/, true /*run-packages*/, project.DefaultHookTimeout, project.DefaultPackageTimeout)
  1939  	if got, want := fmt.Sprint(err), "import cycle detected in local manifest files"; !strings.Contains(got, want) {
  1940  		t.Errorf("got error %v, want substr %v", got, want)
  1941  	}
  1942  }
  1943  
  1944  func TestRemoteImportCycle(t *testing.T) {
  1945  	fake, cleanup := jiritest.NewFakeJiriRoot(t)
  1946  	defer cleanup()
  1947  
  1948  	// Set up two remote manifest projects, remote1 and remote1.
  1949  	if err := fake.CreateRemoteProject("remote1"); err != nil {
  1950  		t.Fatal(err)
  1951  	}
  1952  	if err := fake.CreateRemoteProject("remote2"); err != nil {
  1953  		t.Fatal(err)
  1954  	}
  1955  	remote1 := fake.Projects["remote1"]
  1956  	remote2 := fake.Projects["remote2"]
  1957  
  1958  	fileA, fileB := filepath.Join(remote1, "A"), filepath.Join(remote2, "B")
  1959  
  1960  	// Set up the cycle .jiri_manifest -> remote1+A -> remote2+B -> remote1+A
  1961  	jiriManifest := project.Manifest{
  1962  		Imports: []project.Import{
  1963  			{Manifest: "A", Name: "n1", Remote: remote1},
  1964  		},
  1965  	}
  1966  	manifestA := project.Manifest{
  1967  		Imports: []project.Import{
  1968  			{Manifest: "B", Name: "n2", Remote: remote2},
  1969  		},
  1970  	}
  1971  	manifestB := project.Manifest{
  1972  		Imports: []project.Import{
  1973  			{Manifest: "A", Name: "n3", Remote: remote1},
  1974  		},
  1975  	}
  1976  	if err := jiriManifest.ToFile(fake.X, fake.X.JiriManifestFile()); err != nil {
  1977  		t.Fatal(err)
  1978  	}
  1979  	if err := manifestA.ToFile(fake.X, fileA); err != nil {
  1980  		t.Fatal(err)
  1981  	}
  1982  	if err := manifestB.ToFile(fake.X, fileB); err != nil {
  1983  		t.Fatal(err)
  1984  	}
  1985  	commitFile(t, fake.X, remote1, fileA, "commit A")
  1986  	commitFile(t, fake.X, remote2, fileB, "commit B")
  1987  
  1988  	// The update should complain about the cycle.
  1989  	err := project.UpdateUniverse(fake.X, false, false, false, false, false, true /*run-hooks*/, true /*run-packages*/, project.DefaultHookTimeout, project.DefaultPackageTimeout)
  1990  	if got, want := fmt.Sprint(err), "import cycle detected in remote manifest imports"; !strings.Contains(got, want) {
  1991  		t.Errorf("got error %v, want substr %v", got, want)
  1992  	}
  1993  }
  1994  
  1995  func TestFileAndRemoteImportCycle(t *testing.T) {
  1996  	fake, cleanup := jiritest.NewFakeJiriRoot(t)
  1997  	defer cleanup()
  1998  
  1999  	// Set up two remote manifest projects, remote1 and remote2.
  2000  	// Set up two remote manifest projects, remote1 and remote1.
  2001  	if err := fake.CreateRemoteProject("remote1"); err != nil {
  2002  		t.Fatal(err)
  2003  	}
  2004  	if err := fake.CreateRemoteProject("remote2"); err != nil {
  2005  		t.Fatal(err)
  2006  	}
  2007  	remote1 := fake.Projects["remote1"]
  2008  	remote2 := fake.Projects["remote2"]
  2009  	fileA, fileD := filepath.Join(remote1, "A"), filepath.Join(remote1, "D")
  2010  	fileB, fileC := filepath.Join(remote2, "B"), filepath.Join(remote2, "C")
  2011  
  2012  	// Set up the cycle .jiri_manifest -> remote1+A -> remote2+B -> C -> remote1+D -> A
  2013  	jiriManifest := project.Manifest{
  2014  		Imports: []project.Import{
  2015  			{Manifest: "A", Root: "r1", Name: "n1", Remote: remote1},
  2016  		},
  2017  	}
  2018  	manifestA := project.Manifest{
  2019  		Imports: []project.Import{
  2020  			{Manifest: "B", Root: "r2", Name: "n2", Remote: remote2},
  2021  		},
  2022  	}
  2023  	manifestB := project.Manifest{
  2024  		LocalImports: []project.LocalImport{
  2025  			{File: "C"},
  2026  		},
  2027  	}
  2028  	manifestC := project.Manifest{
  2029  		Imports: []project.Import{
  2030  			{Manifest: "D", Root: "r3", Name: "n3", Remote: remote1},
  2031  		},
  2032  	}
  2033  	manifestD := project.Manifest{
  2034  		LocalImports: []project.LocalImport{
  2035  			{File: "A"},
  2036  		},
  2037  	}
  2038  	if err := jiriManifest.ToFile(fake.X, fake.X.JiriManifestFile()); err != nil {
  2039  		t.Fatal(err)
  2040  	}
  2041  	if err := manifestA.ToFile(fake.X, fileA); err != nil {
  2042  		t.Fatal(err)
  2043  	}
  2044  	if err := manifestB.ToFile(fake.X, fileB); err != nil {
  2045  		t.Fatal(err)
  2046  	}
  2047  	if err := manifestC.ToFile(fake.X, fileC); err != nil {
  2048  		t.Fatal(err)
  2049  	}
  2050  	if err := manifestD.ToFile(fake.X, fileD); err != nil {
  2051  		t.Fatal(err)
  2052  	}
  2053  	commitFile(t, fake.X, remote1, fileA, "commit A")
  2054  	commitFile(t, fake.X, remote2, fileB, "commit B")
  2055  	commitFile(t, fake.X, remote2, fileC, "commit C")
  2056  	commitFile(t, fake.X, remote1, fileD, "commit D")
  2057  
  2058  	// The update should complain about the cycle.
  2059  	err := project.UpdateUniverse(fake.X, false, false, false, false, false, true /*run-hooks*/, true /*run-packages*/, project.DefaultHookTimeout, project.DefaultPackageTimeout)
  2060  	if got, want := fmt.Sprint(err), "import cycle detected"; !strings.Contains(got, want) {
  2061  		t.Errorf("got error %v, want substr %v", got, want)
  2062  	}
  2063  }
  2064  
  2065  func TestManifestToFromBytes(t *testing.T) {
  2066  	tests := []struct {
  2067  		Manifest project.Manifest
  2068  		XML      string
  2069  	}{
  2070  		{
  2071  			project.Manifest{},
  2072  			`<manifest>
  2073  </manifest>
  2074  `,
  2075  		},
  2076  		{
  2077  			project.Manifest{
  2078  				Imports: []project.Import{
  2079  					{
  2080  						Manifest:     "manifest1",
  2081  						Name:         "remoteimport1",
  2082  						Remote:       "remote1",
  2083  						Revision:     "HEAD",
  2084  						RemoteBranch: "master",
  2085  					},
  2086  					{
  2087  						Manifest:     "manifest2",
  2088  						Name:         "remoteimport2",
  2089  						Remote:       "remote2",
  2090  						Revision:     "HEAD",
  2091  						RemoteBranch: "branch2",
  2092  					},
  2093  					{
  2094  						Manifest:     "manifest3",
  2095  						Name:         "remoteimport3",
  2096  						Remote:       "remote3",
  2097  						Revision:     "rev3",
  2098  						RemoteBranch: "branch3",
  2099  					},
  2100  				},
  2101  				LocalImports: []project.LocalImport{
  2102  					{File: "fileimport"},
  2103  				},
  2104  				Projects: []project.Project{
  2105  					{
  2106  						Name:         "project1",
  2107  						Path:         "path1",
  2108  						Remote:       "remote1",
  2109  						RemoteBranch: "master",
  2110  						Revision:     "HEAD",
  2111  						GerritHost:   "https://test-review.googlesource.com",
  2112  						GitHooks:     "path/to/githooks",
  2113  					},
  2114  					{
  2115  						Name:         "project2",
  2116  						Path:         "path2",
  2117  						Remote:       "remote2",
  2118  						RemoteBranch: "branch2",
  2119  						Revision:     "rev2",
  2120  					},
  2121  				},
  2122  				Hooks: []project.Hook{
  2123  					{
  2124  						Name:        "testhook",
  2125  						ProjectName: "project1",
  2126  						Action:      "action.sh",
  2127  					},
  2128  				},
  2129  			},
  2130  			`<manifest>
  2131    <imports>
  2132      <import manifest="manifest1" name="remoteimport1" remote="remote1"/>
  2133      <import manifest="manifest2" name="remoteimport2" remote="remote2" remotebranch="branch2"/>
  2134      <import manifest="manifest3" name="remoteimport3" remote="remote3" revision="rev3" remotebranch="branch3"/>
  2135      <localimport file="fileimport"/>
  2136    </imports>
  2137    <projects>
  2138      <project name="project1" path="path1" remote="remote1" gerrithost="https://test-review.googlesource.com" githooks="path/to/githooks"/>
  2139      <project name="project2" path="path2" remote="remote2" remotebranch="branch2" revision="rev2"/>
  2140    </projects>
  2141    <hooks>
  2142      <hook name="testhook" action="action.sh" project="project1"/>
  2143    </hooks>
  2144  </manifest>
  2145  `,
  2146  		},
  2147  	}
  2148  	for _, test := range tests {
  2149  		gotBytes, err := test.Manifest.ToBytes()
  2150  		if err != nil {
  2151  			t.Errorf("%+v ToBytes failed: %v", test.Manifest, err)
  2152  		}
  2153  		if got, want := string(gotBytes), test.XML; got != want {
  2154  			t.Errorf("%+v ToBytes GOT\n%v\nWANT\n%v", test.Manifest, got, want)
  2155  		}
  2156  		manifest, err := project.ManifestFromBytes([]byte(test.XML))
  2157  		if err != nil {
  2158  			t.Errorf("%+v FromBytes failed: %v", test.Manifest, err)
  2159  		}
  2160  		if got, want := manifest, &test.Manifest; !reflect.DeepEqual(got, want) {
  2161  			t.Errorf("%+v FromBytes GOT\n%#v\nWANT\n%#v", test.Manifest, got, want)
  2162  		}
  2163  	}
  2164  }
  2165  
  2166  func TestProjectToFromFile(t *testing.T) {
  2167  	jirix, cleanup := xtest.NewX(t)
  2168  	defer cleanup()
  2169  
  2170  	tests := []struct {
  2171  		Project project.Project
  2172  		XML     string
  2173  	}{
  2174  		{
  2175  			// Default fields are dropped when marshaled, and added when unmarshaled.
  2176  			project.Project{
  2177  				Name:         "project1",
  2178  				Path:         filepath.Join(jirix.Root, "path1"),
  2179  				Remote:       "remote1",
  2180  				RemoteBranch: "master",
  2181  				Revision:     "HEAD",
  2182  			},
  2183  			`<project name="project1" path="path1" remote="remote1"/>
  2184  `,
  2185  		},
  2186  		{
  2187  			project.Project{
  2188  				Name:         "project2",
  2189  				Path:         filepath.Join(jirix.Root, "path2"),
  2190  				GitHooks:     filepath.Join(jirix.Root, "git-hooks"),
  2191  				Remote:       "remote2",
  2192  				RemoteBranch: "branch2",
  2193  				Revision:     "rev2",
  2194  			},
  2195  			`<project name="project2" path="path2" remote="remote2" remotebranch="branch2" revision="rev2" githooks="git-hooks"/>
  2196  `,
  2197  		},
  2198  	}
  2199  	for index, test := range tests {
  2200  		filename := filepath.Join(jirix.Root, fmt.Sprintf("test-%d", index))
  2201  		if err := test.Project.ToFile(jirix, filename); err != nil {
  2202  			t.Errorf("%+v ToFile failed: %v", test.Project, err)
  2203  		}
  2204  		gotBytes, err := ioutil.ReadFile(filename)
  2205  		if err != nil {
  2206  			t.Errorf("%+v ReadFile failed: %v", test.Project, err)
  2207  		}
  2208  		if got, want := string(gotBytes), test.XML; got != want {
  2209  			t.Errorf("%+v ToFile GOT\n%v\nWANT\n%v", test.Project, got, want)
  2210  		}
  2211  		project, err := project.ProjectFromFile(jirix, filename)
  2212  		if err != nil {
  2213  			t.Errorf("%+v FromFile failed: %v", test.Project, err)
  2214  		}
  2215  		if got, want := project, &test.Project; !reflect.DeepEqual(got, want) {
  2216  			t.Errorf("%+v FromFile got %#v, want %#v", test.Project, got, want)
  2217  		}
  2218  	}
  2219  }
  2220  
  2221  func TestMarshalAndUnmarshalLockEntries(t *testing.T) {
  2222  
  2223  	projectLock0 := project.ProjectLock{"https://dart.googlesource.com/web_socket_channel.git", "dart", "1.0.9"}
  2224  	pkgLock0 := project.PackageLock{
  2225  		PackageName: "fuchsia/go/mac-amd64",
  2226  		VersionTag:  "git_revision:b8bd7d94a2ae6c80ab8b6ed5900d3eeba8a777c3",
  2227  		InstanceID:  "3c33b55c1a75b900536c91181805bb8668857341",
  2228  	}
  2229  
  2230  	testProjectLocks0 := project.ProjectLocks{
  2231  		projectLock0.Key(): projectLock0,
  2232  	}
  2233  	testPkgLocks0 := project.PackageLocks{
  2234  		pkgLock0.Key(): pkgLock0,
  2235  	}
  2236  
  2237  	jsonData, err := project.MarshalLockEntries(testProjectLocks0, testPkgLocks0)
  2238  
  2239  	if err != nil {
  2240  		t.Errorf("marshalling lockfile failed due to error: %v", err)
  2241  	}
  2242  
  2243  	projectLocks, pkgLocks, err := project.UnmarshalLockEntries(jsonData)
  2244  	if err != nil {
  2245  		t.Errorf("unmarshalling lockfile failed due to error: %v", err)
  2246  	}
  2247  
  2248  	if !reflect.DeepEqual(projectLocks, testProjectLocks0) {
  2249  		t.Errorf("unmarshalled project locks do not match test data, expecting %v, got %v", testProjectLocks0, projectLocks)
  2250  	}
  2251  
  2252  	if !reflect.DeepEqual(pkgLocks, testPkgLocks0) {
  2253  		t.Errorf("unmarshalled locks do not match test data, expecting %v, got %v", testPkgLocks0, pkgLocks)
  2254  	}
  2255  
  2256  	jsonData = []byte(`
  2257  [
  2258  	{
  2259  		"repository_url": "https://dart.googlesource.com/web_socket_channel.git",
  2260  		"name": "dart",
  2261  		"revision": "1.0.9"
  2262  	},
  2263  	{
  2264  		"repository_url": "https://dart.googlesource.com/web_socket_channel.git",
  2265  		"name": "dart",
  2266  		"revision": "1.1.0"
  2267  	}
  2268  ]`)
  2269  
  2270  	if _, _, err := project.UnmarshalLockEntries(jsonData); err == nil {
  2271  		t.Errorf("unmarshalling lockfile with conflicting data should fail but it did not happen")
  2272  	} else {
  2273  		if !strings.Contains(err.Error(), "has more than 1") {
  2274  			t.Errorf("unmarshalling lockfile with conflicting data failed due to unrelated error: %v", err)
  2275  		}
  2276  	}
  2277  
  2278  }
  2279  
  2280  func TestGetPath(t *testing.T) {
  2281  	testPkgs := []project.Package{
  2282  		project.Package{Name: "test0", Version: "version", Path: "A/test0"},
  2283  		project.Package{Name: "test1/${platform}", Version: "version", Path: ""},
  2284  		project.Package{Name: "test2/${os}-${arch}", Version: "version", Path: ""},
  2285  		project.Package{Name: "test3/${platform=linux-armv6l}", Version: "version", Path: ""},
  2286  	}
  2287  	testResults := []string{
  2288  		"A/test0",
  2289  		"prebuilt/test1/",
  2290  		"prebuilt/test2/",
  2291  		"prebuilt",
  2292  	}
  2293  
  2294  	for i, v := range testPkgs {
  2295  		defaultPath, err := v.GetPath()
  2296  		if err != nil {
  2297  			t.Errorf("TestGetPath failed due to error: %v", err)
  2298  		}
  2299  		if strings.HasSuffix(testResults[i], "/") {
  2300  			testResults[i] += cipd.CipdPlatform.String()
  2301  		}
  2302  		if testResults[i] != defaultPath {
  2303  			t.Errorf("expecting %q, got %q", testResults[i], defaultPath)
  2304  		}
  2305  	}
  2306  }
  2307  
  2308  func TestWritePackageFlags(t *testing.T) {
  2309  	jirix, cleanup := xtest.NewX(t)
  2310  	defer cleanup()
  2311  
  2312  	testPkgsWAS := []project.Package{
  2313  		project.Package{Name: "test0", Version: "version", Flag: "flagfile0|internal = true|internal = false"},
  2314  		project.Package{Name: "test1", Version: "version", Flag: "flagfile1|{\"internal\" = true}|{\"internal\" = false}"},
  2315  	}
  2316  	testPkgsWoAS := []project.Package{
  2317  		project.Package{Name: "test2", Version: "version", Flag: "flagfile2|internal = true|internal = false"},
  2318  		project.Package{Name: "test3", Version: "version", Flag: "flagfile3|{\"internal\" = true}|{\"internal\" = false}"},
  2319  	}
  2320  	expected := map[string]string{
  2321  		"flagfile0": "internal = true",
  2322  		"flagfile1": "{\"internal\" = true}",
  2323  		"flagfile2": "internal = false",
  2324  		"flagfile3": "{\"internal\" = false}",
  2325  	}
  2326  
  2327  	testPkgs := make(project.Packages)
  2328  	testPkgsWA := make(project.Packages)
  2329  	for _, v := range testPkgsWAS {
  2330  		testPkgs[v.Key()] = v
  2331  		testPkgsWA[v.Key()] = v
  2332  	}
  2333  	for _, v := range testPkgsWoAS {
  2334  		testPkgs[v.Key()] = v
  2335  	}
  2336  
  2337  	if err := project.WritePackageFlags(jirix, testPkgs, testPkgsWA); err != nil {
  2338  		t.Errorf("WritePackageFlags failed due to error: %v", err)
  2339  	}
  2340  
  2341  	for k, v := range expected {
  2342  		data, err := ioutil.ReadFile(filepath.Join(jirix.Root, k))
  2343  		if err != nil {
  2344  			t.Errorf("read flag file %q failed due error: %v", k, err)
  2345  		}
  2346  		if string(data) != v {
  2347  			t.Errorf("expecting flag %q from file %q, got %q", v, k, string(data))
  2348  		}
  2349  	}
  2350  }
  2351  
  2352  func TestWriteProjectFlags(t *testing.T) {
  2353  	jirix, cleanup := xtest.NewX(t)
  2354  	defer cleanup()
  2355  
  2356  	testProjsList := []project.Project{
  2357  		project.Project{Name: "test0", Revision: "version", Flag: "flagfile0|internal = true|internal = false"},
  2358  		project.Project{Name: "test1", Revision: "version", Flag: ""},
  2359  	}
  2360  	expected := map[string]string{
  2361  		"flagfile0": "internal = true",
  2362  	}
  2363  
  2364  	testProjs := make(project.Projects)
  2365  	for _, v := range testProjsList {
  2366  		testProjs[v.Key()] = v
  2367  	}
  2368  
  2369  	if err := project.WriteProjectFlags(jirix, testProjs); err != nil {
  2370  		t.Errorf("WritePackageFlags failed due to error: %v", err)
  2371  	}
  2372  
  2373  	for k, v := range expected {
  2374  		data, err := ioutil.ReadFile(filepath.Join(jirix.Root, k))
  2375  		if err != nil {
  2376  			t.Errorf("read flag file %q failed due error: %v", k, err)
  2377  		}
  2378  		if string(data) != v {
  2379  			t.Errorf("expecting flag %q from file %q, got %q", v, k, string(data))
  2380  		}
  2381  	}
  2382  }
  2383  
  2384  func TestPackageVersionTemplate(t *testing.T) {
  2385  	jirix, cleanup := xtest.NewX(t)
  2386  	defer cleanup()
  2387  
  2388  	testProjs := []project.Project{
  2389  		project.Project{
  2390  			Name:       "testTP0",
  2391  			Path:       "testTP0",
  2392  			Remote:     "https://example.com/testTP0",
  2393  			Revision:   "acf8ae59e121b3922bc8ac979323a531da418cc5",
  2394  			GerritHost: "https://example.com",
  2395  		},
  2396  		project.Project{
  2397  			Name:       "testTP1",
  2398  			Path:       "testTP1",
  2399  			Remote:     "https://example.com/testTP1",
  2400  			Revision:   "1e4aee79b472a6939ac26811c5dee8a132c29fef",
  2401  			GerritHost: "https://example.com",
  2402  		},
  2403  	}
  2404  
  2405  	testPkgs := []project.Package{
  2406  		project.Package{
  2407  			Name:    "test0",
  2408  			Version: "git_revision:{{(index .Projects \"testTP0\").Revision}}",
  2409  			Path:    "test0",
  2410  		},
  2411  		project.Package{
  2412  			Name:    "test1",
  2413  			Version: "git_revision:{{(index .Projects \"testTP1\").Revision}}",
  2414  			Path:    "test0",
  2415  		},
  2416  	}
  2417  
  2418  	projects := make(project.Projects)
  2419  	pkgs := make(project.Packages)
  2420  	verificationMap := make(map[string]project.Project)
  2421  	for i, v := range testPkgs {
  2422  		projects[testProjs[i].Key()] = testProjs[i]
  2423  		pkgs[v.Key()] = v
  2424  		verificationMap[v.Name] = testProjs[i]
  2425  	}
  2426  
  2427  	pkgs, err := project.ResolveImplicitPackageVersions(jirix, projects, pkgs)
  2428  	if err != nil {
  2429  		t.Errorf("ResolveImplicitPackageVersions failed due to error: %v", err)
  2430  	}
  2431  	for _, v := range pkgs {
  2432  		if proj, ok := verificationMap[v.Name]; ok {
  2433  			if proj.Revision != v.Version[len("git_revision:"):] {
  2434  				t.Errorf("expecting \"git_revision:%s\", got %q", proj.Revision, v.Version)
  2435  			}
  2436  		} else {
  2437  			t.Errorf("unexpected package %+v", v)
  2438  		}
  2439  	}
  2440  }
  2441  
  2442  func TestOptionalProjectsAndPackages(t *testing.T) {
  2443  	fake, cleanup := jiritest.NewFakeJiriRoot(t)
  2444  	defer cleanup()
  2445  
  2446  	// Set up projects and packages with explict attributes
  2447  	numProjects := 3
  2448  	numOptionalProjects := 2
  2449  	localProjects := []project.Project{}
  2450  
  2451  	createRemoteProj := func(i int, attributes string) {
  2452  		name := projectName(i)
  2453  		path := fmt.Sprintf("path-%d", i)
  2454  		if err := fake.CreateRemoteProject(name); err != nil {
  2455  			t.Errorf("failed to create remote project due to error: %v", err)
  2456  		}
  2457  		p := project.Project{
  2458  			Name:       name,
  2459  			Path:       filepath.Join(fake.X.Root, path),
  2460  			Remote:     fake.Projects[name],
  2461  			Attributes: attributes,
  2462  		}
  2463  		localProjects = append(localProjects, p)
  2464  		if err := fake.AddProject(p); err != nil {
  2465  			t.Errorf("failed to add a project to manifest due to error: %v", err)
  2466  		}
  2467  	}
  2468  
  2469  	for i := 0; i < numProjects; i++ {
  2470  		createRemoteProj(i, "")
  2471  	}
  2472  
  2473  	for i := numProjects; i < numProjects+numOptionalProjects; i++ {
  2474  		createRemoteProj(i, "optional,debug")
  2475  	}
  2476  
  2477  	// Create initial commit in each repo.
  2478  	for _, remoteProjectDir := range fake.Projects {
  2479  		writeReadme(t, fake.X, remoteProjectDir, "initial readme")
  2480  	}
  2481  	pkg0 := project.Package{
  2482  		Name:       "gn/gn/${platform}",
  2483  		Path:       "path-pkg0",
  2484  		Version:    "git_revision:bdb0fd02324b120cacde634a9235405061c8ea06",
  2485  		Attributes: "debug,testing",
  2486  	}
  2487  	pkg1 := project.Package{
  2488  		Name:       "fuchsia/tools/jiri/${platform}",
  2489  		Path:       "path-pkg1",
  2490  		Version:    "git_revision:05715c8fbbdb952ab38e50533a1b653445e74b40",
  2491  		Attributes: "",
  2492  	}
  2493  
  2494  	fake.AddPackage(pkg0)
  2495  	fake.AddPackage(pkg1)
  2496  
  2497  	pathExists := func(projPath string) bool {
  2498  		if _, err := os.Stat(projPath); err != nil {
  2499  			if os.IsNotExist(err) {
  2500  				return false
  2501  			}
  2502  			t.Errorf("failed to access path due to error: %v", err)
  2503  		}
  2504  		return true
  2505  	}
  2506  	assertExist := func(localPath string) {
  2507  		if !pathExists(localPath) {
  2508  			t.Errorf("expecting path %q exists, but it does not", localPath)
  2509  		}
  2510  	}
  2511  	assertNotExist := func(localPath string) {
  2512  		if pathExists(localPath) {
  2513  			t.Errorf("expecting path %q does not exist, but it does", localPath)
  2514  		}
  2515  	}
  2516  
  2517  	// Try default mode
  2518  	fake.X.FetchingAttrs = ""
  2519  	fake.UpdateUniverse(true)
  2520  	// The optional projects should not be fetched
  2521  	for i := 0; i < numProjects; i++ {
  2522  		assertExist(localProjects[i].Path)
  2523  	}
  2524  	for i := numProjects; i < numOptionalProjects+numProjects; i++ {
  2525  		assertNotExist(localProjects[i].Path)
  2526  	}
  2527  	assertNotExist(filepath.Join(fake.X.Root, pkg0.Path))
  2528  	assertExist(filepath.Join(fake.X.Root, pkg1.Path))
  2529  
  2530  	// Try setting attributes to "optional, testing"
  2531  	fake.X.FetchingAttrs = "optional, testing"
  2532  	fake.UpdateUniverse(true)
  2533  	for i := 0; i < numProjects; i++ {
  2534  		assertExist(localProjects[i].Path)
  2535  	}
  2536  	for i := numProjects; i < numOptionalProjects+numProjects; i++ {
  2537  		assertExist(localProjects[i].Path)
  2538  	}
  2539  	assertExist(filepath.Join(fake.X.Root, pkg0.Path))
  2540  	assertExist(filepath.Join(fake.X.Root, pkg1.Path))
  2541  
  2542  	// Reset optional attributes
  2543  	fake.X.FetchingAttrs = "nonexist"
  2544  	fake.UpdateUniverse(true)
  2545  	for i := 0; i < numProjects; i++ {
  2546  		assertExist(localProjects[i].Path)
  2547  	}
  2548  	for i := numProjects; i < numOptionalProjects+numProjects; i++ {
  2549  		assertNotExist(localProjects[i].Path)
  2550  	}
  2551  	assertNotExist(filepath.Join(fake.X.Root, pkg0.Path))
  2552  	assertExist(filepath.Join(fake.X.Root, pkg1.Path))
  2553  }
  2554  
  2555  func TestMultiplePackageVersions(t *testing.T) {
  2556  	fake, cleanup := jiritest.NewFakeJiriRoot(t)
  2557  	defer cleanup()
  2558  
  2559  	pkg0 := project.Package{
  2560  		Name:    "fuchsia/tools/jiri/${platform}",
  2561  		Path:    "path-pkg0",
  2562  		Version: "git_revision:9904764ed228c7fb87bfb252762952b502d1e360",
  2563  	}
  2564  	pkg1 := project.Package{
  2565  		Name:    "fuchsia/tools/jiri/${platform}",
  2566  		Path:    "path-pkg1",
  2567  		Version: "git_revision:05715c8fbbdb952ab38e50533a1b653445e74b40",
  2568  	}
  2569  
  2570  	fake.AddPackage(pkg0)
  2571  	fake.AddPackage(pkg1)
  2572  
  2573  	pathExists := func(projPath string) bool {
  2574  		if _, err := os.Stat(projPath); err != nil {
  2575  			if os.IsNotExist(err) {
  2576  				return false
  2577  			}
  2578  			t.Errorf("failed to access path due to error: %v", err)
  2579  		}
  2580  		return true
  2581  	}
  2582  	assertExist := func(localPath string) {
  2583  		if !pathExists(localPath) {
  2584  			t.Errorf("expecting path %q exists, but it does not", localPath)
  2585  		}
  2586  	}
  2587  
  2588  	fake.UpdateUniverse(true)
  2589  
  2590  	assertExist(filepath.Join(fake.X.Root, pkg0.Path))
  2591  	assertExist(filepath.Join(fake.X.Root, pkg1.Path))
  2592  }
  2593  
  2594  func TestOverrideImport(t *testing.T) {
  2595  	_, fake, cleanup := setupUniverse(t)
  2596  	defer cleanup()
  2597  
  2598  	// override the import
  2599  	hashes, ok := fake.ProjectHashes[jiritest.ManifestProjectName]
  2600  	if !ok || len(hashes) < 2 {
  2601  		t.Errorf("failed to retrieve git hashes of manifest project")
  2602  	}
  2603  	testHash := hashes[len(hashes)-2]
  2604  	fake.AddImportOverride(jiritest.ManifestProjectName, fake.Projects[jiritest.ManifestProjectName], testHash, jiritest.ManifestFileName)
  2605  	if err := fake.UpdateUniverse(false); err != nil {
  2606  		t.Errorf("Update universe failed due to error: %v", err)
  2607  	}
  2608  	scm := gitutil.New(fake.X, gitutil.RootDirOpt(filepath.Join(fake.X.Root, jiritest.ManifestProjectPath)))
  2609  	currentHash, err := scm.CurrentRevision()
  2610  	if err != nil {
  2611  		t.Errorf("failed to get current hash due to error: %v", err)
  2612  	}
  2613  	if currentHash != testHash {
  2614  		t.Errorf("expected git hash %q, got %q", testHash, currentHash)
  2615  	}
  2616  }
  2617  
  2618  func TestOverrideProject(t *testing.T) {
  2619  	localProjects, fake, cleanup := setupUniverse(t)
  2620  	defer cleanup()
  2621  
  2622  	// override localProjects 1
  2623  	hashes, ok := fake.ProjectHashes[localProjects[0].Name]
  2624  	if !ok || len(hashes) == 0 {
  2625  		t.Errorf("fail to retrieve git hashes of test project 0")
  2626  	}
  2627  	testHash := hashes[0]
  2628  	fake.AddProjectOverride(localProjects[0].Name, localProjects[0].Remote, testHash)
  2629  
  2630  	fake.UpdateUniverse(true)
  2631  	scm := gitutil.New(fake.X, gitutil.RootDirOpt(localProjects[0].Path))
  2632  	currentHash, err := scm.CurrentRevision()
  2633  	if err != nil {
  2634  		t.Errorf("failed to get current hash due to error: %v", err)
  2635  	}
  2636  	if currentHash != testHash {
  2637  		t.Errorf("expected git hash %q, got %q", testHash, currentHash)
  2638  	}
  2639  }
  2640  
  2641  func TestHostnameAllowed(t *testing.T) {
  2642  	tests := map[string]bool{
  2643  		"*.google.com,fuchsia.google.com":       true,
  2644  		"*.google.com,fuchsia.dev.google.com":   true,
  2645  		"google.com,google.com":                 true,
  2646  		"*google.com,fuchsiagoogle.com":         true,
  2647  		"google.com,fuchsiagoogle.com":          false,
  2648  		"google.com,oogle.com":                  false,
  2649  		"fuchsia-internal,fuchsia-internal":     true,
  2650  		"fuchsia-internal,fuchsia":              false,
  2651  		",":                                     true,
  2652  		"*google*.com,github.com/btwiuse": false,
  2653  	}
  2654  	for k, v := range tests {
  2655  		test := strings.Split(k, ",")
  2656  		if len(test) != 2 {
  2657  			t.Errorf("expecting a single ',' in %q", k)
  2658  		}
  2659  		ret := project.HostnameAllowed(test[0], test[1])
  2660  		if v != ret {
  2661  			t.Errorf("expecting %v, got %v from test %q", v, ret, k)
  2662  		}
  2663  	}
  2664  }
  2665  
  2666  func TestCheckProjectsHostnames(t *testing.T) {
  2667  	allowList := []string{
  2668  		"*.googlesource.com",
  2669  		"fuchsia-internal",
  2670  	}
  2671  	allowListIllegal := []string{
  2672  		"*.google*.com",
  2673  		"fuchsia-internal",
  2674  	}
  2675  	testProjectListsTrue := []project.Project{
  2676  		{
  2677  			Name:   "project1",
  2678  			Remote: "https://github.com/btwiuse/project1",
  2679  		},
  2680  		{
  2681  			Name:   "project2",
  2682  			Remote: "https://chromium.googlesource.com/project2",
  2683  		},
  2684  		{
  2685  			Name:   "project4",
  2686  			Remote: "sso://fuchsia-internal/project4",
  2687  		},
  2688  	}
  2689  	testProjectListsFalse := []project.Project{
  2690  		{
  2691  			Name:   "project1",
  2692  			Remote: "https://github.com/btwiuse/project1",
  2693  		},
  2694  		{
  2695  			Name:   "project2",
  2696  			Remote: "https://chromium.googlesource.com/project2",
  2697  		},
  2698  		{
  2699  			Name:   "project3",
  2700  			Remote: "https://test.github.com/project3",
  2701  		},
  2702  		{
  2703  			Name:   "project4",
  2704  			Remote: "sso://fuchsia-internal/project4",
  2705  		},
  2706  	}
  2707  
  2708  	mapTrue := make(project.Projects)
  2709  	for _, item := range testProjectListsTrue {
  2710  		mapTrue[item.Key()] = item
  2711  	}
  2712  	if err := project.CheckProjectsHostnames(mapTrue, allowList); err != nil {
  2713  		t.Errorf("expecting nil from CheckProjectsHostnames, but got: %v", err)
  2714  	}
  2715  
  2716  	mapFalse := make(project.Projects)
  2717  	for _, item := range testProjectListsFalse {
  2718  		mapFalse[item.Key()] = item
  2719  	}
  2720  	if err := project.CheckProjectsHostnames(mapFalse, allowList); err == nil {
  2721  		t.Errorf("expecting error from CheckProjectsHostnames, but got nil")
  2722  	}
  2723  
  2724  	if err := project.CheckProjectsHostnames(mapTrue, allowListIllegal); err == nil {
  2725  		t.Errorf("expecting error from CheckProjectsHostnames, but got nil")
  2726  	}
  2727  }