Example #1
0
// TestCLSync checks the operation of the "jiri cl sync" command.
func TestCLSync(t *testing.T) {
	fake, _, _, _, cleanup := setupTest(t, true)
	defer cleanup()

	// Create some dependent CLs.
	if err := newCL(fake.X, []string{"feature1"}); err != nil {
		t.Fatalf("%v", err)
	}
	if err := newCL(fake.X, []string{"feature2"}); err != nil {
		t.Fatalf("%v", err)
	}

	// Add the "test" file to the master.
	if err := gitutil.New(fake.X.NewSeq()).CheckoutBranch("master"); err != nil {
		t.Fatalf("%v", err)
	}
	commitFiles(t, fake.X, []string{"test"})

	// Sync the dependent CLs.
	if err := gitutil.New(fake.X.NewSeq()).CheckoutBranch("feature2"); err != nil {
		t.Fatalf("%v", err)
	}
	if err := syncCL(fake.X); err != nil {
		t.Fatalf("%v", err)
	}

	// Check that the "test" file exists in the dependent CLs.
	for _, branch := range []string{"feature1", "feature2"} {
		if err := gitutil.New(fake.X.NewSeq()).CheckoutBranch(branch); err != nil {
			t.Fatalf("%v", err)
		}
		assertFilesExist(t, fake.X, []string{"test"})
	}
}
Example #2
0
// TestCreateReviewBranch checks that the temporary review branch is
// created correctly.
func TestCreateReviewBranch(t *testing.T) {
	fake, _, _, _, cleanup := setupTest(t, true)
	defer cleanup()
	branch := "my-branch"
	if err := gitutil.New(fake.X.NewSeq()).CreateAndCheckoutBranch(branch); err != nil {
		t.Fatalf("%v", err)
	}
	files := []string{"file1", "file2", "file3"}
	commitFiles(t, fake.X, files)
	review, err := newReview(fake.X, project.Project{}, gerrit.CLOpts{})
	if err != nil {
		t.Fatalf("%v", err)
	}
	if expected, got := branch+"-REVIEW", review.reviewBranch; expected != got {
		t.Fatalf("Unexpected review branch name: expected %v, got %v", expected, got)
	}
	commitMessage := "squashed commit"
	if err := review.createReviewBranch(commitMessage); err != nil {
		t.Fatalf("%v", err)
	}
	// Verify that the branch exists.
	if !gitutil.New(fake.X.NewSeq()).BranchExists(review.reviewBranch) {
		t.Fatalf("review branch not found")
	}
	if err := gitutil.New(fake.X.NewSeq()).CheckoutBranch(review.reviewBranch); err != nil {
		t.Fatalf("%v", err)
	}
	assertCommitCount(t, fake.X, review.reviewBranch, "master", 1)
	assertFilesCommitted(t, fake.X, files)
}
Example #3
0
// createRepoFromOrigin creates a Git repo tracking origin/master.
func createRepoFromOrigin(t *testing.T, jirix *jiri.X, subpath string, originPath string) string {
	repoPath := createRepo(t, jirix, subpath)
	chdir(t, jirix, repoPath)
	if err := gitutil.New(jirix.NewSeq()).AddRemote("origin", originPath); err != nil {
		t.Fatalf("%v", err)
	}
	if err := gitutil.New(jirix.NewSeq()).Pull("origin", "master"); err != nil {
		t.Fatalf("%v", err)
	}
	return repoPath
}
Example #4
0
// EnableRemoteManifestPush enables pushes to the remote manifest
// repository.
func (fake FakeJiriRoot) EnableRemoteManifestPush() error {
	dir := gitutil.RootDirOpt(filepath.Join(fake.remote, manifestProjectPath))
	if !gitutil.New(fake.X.NewSeq(), dir).BranchExists("non-master") {
		if err := gitutil.New(fake.X.NewSeq(), dir).CreateBranch("non-master"); err != nil {
			return err
		}
	}
	if err := gitutil.New(fake.X.NewSeq(), dir).CheckoutBranch("non-master"); err != nil {
		return err
	}
	return nil
}
Example #5
0
// CreateRemoteProject creates a new remote project.
func (fake FakeJiriRoot) CreateRemoteProject(name string) error {
	projectDir := filepath.Join(fake.remote, name)
	if err := fake.X.NewSeq().MkdirAll(projectDir, os.FileMode(0700)).Done(); err != nil {
		return err
	}
	if err := gitutil.New(fake.X.NewSeq()).Init(projectDir); err != nil {
		return err
	}
	if err := gitutil.New(fake.X.NewSeq(), gitutil.RootDirOpt(projectDir)).CommitWithMessage("initial commit"); err != nil {
		return err
	}
	fake.Projects[name] = projectDir
	return nil
}
Example #6
0
// defaultCommitMessage creates the default commit message from the
// list of commits on the branch.
func (review *review) defaultCommitMessage() (string, error) {
	commitMessages := ""
	var err error
	if commitMessageBodyFlag != "" {
		msg, tmpErr := ioutil.ReadFile(commitMessageBodyFlag)
		commitMessages = string(msg)
		err = tmpErr
	} else {
		commitMessages, err = gitutil.New(review.jirix.NewSeq()).CommitMessages(review.CLOpts.Branch, review.reviewBranch)
	}
	if err != nil {
		return "", err
	}
	// Strip "MultiPart ..." from the commit messages.
	strippedMessages := multiPartRE.ReplaceAllLiteralString(commitMessages, "")
	// Strip "Change-Id: ..." from the commit messages.
	strippedMessages = changeIDRE.ReplaceAllLiteralString(strippedMessages, "")
	// Add comment markers (#) to every line.
	commentedMessages := "# " + strings.Replace(strippedMessages, "\n", "\n# ", -1)
	message := defaultMessageHeader + commentedMessages
	if multipart := review.readMultiPart(); multipart != "" {
		message = message + "\n" + multipart + "\n"
	}
	return message, nil
}
Example #7
0
// DisableRemoteManifestPush disables pushes to the remote manifest
// repository.
func (fake FakeJiriRoot) DisableRemoteManifestPush() error {
	dir := gitutil.RootDirOpt(filepath.Join(fake.remote, manifestProjectPath))
	if err := gitutil.New(fake.X.NewSeq(), dir).CheckoutBranch("master"); err != nil {
		return err
	}
	return nil
}
Example #8
0
// commitAndPushChanges commits changes identified by the given manifest file
// and label to the containing repository and pushes these changes to the
// remote repository.
func commitAndPushChanges(jirix *jiri.X, snapshotDir, snapshotFile, label string) (e error) {
	cwd, err := os.Getwd()
	if err != nil {
		return err
	}
	defer collect.Error(func() error { return jirix.NewSeq().Chdir(cwd).Done() }, &e)
	if err := jirix.NewSeq().Chdir(snapshotDir).Done(); err != nil {
		return err
	}
	relativeSnapshotPath := strings.TrimPrefix(snapshotFile, snapshotDir+string(os.PathSeparator))
	git := gitutil.New(jirix.NewSeq())
	// Pull from master so we are up-to-date.
	if err := git.Pull("origin", "master"); err != nil {
		return err
	}
	if err := git.Add(relativeSnapshotPath); err != nil {
		return err
	}
	if err := git.Add(label); err != nil {
		return err
	}
	name := strings.TrimPrefix(snapshotFile, snapshotDir)
	if err := git.CommitNoVerify(fmt.Sprintf("adding snapshot %q for label %q", name, label)); err != nil {
		return err
	}
	if err := git.Push("origin", "master", gitutil.VerifyOpt(false)); err != nil {
		return err
	}
	return nil
}
Example #9
0
func getCommitMessageFileName(jirix *jiri.X, branch string) (string, error) {
	topLevel, err := gitutil.New(jirix.NewSeq()).TopLevel()
	if err != nil {
		return "", err
	}
	return filepath.Join(topLevel, jiri.ProjectMetaDir, branch, commitMessageFileName), nil
}
Example #10
0
// checkDependents makes sure that all CLs in the sequence of
// dependent CLs leading to (but not including) the current branch
// have been exported to Gerrit.
func checkDependents(jirix *jiri.X) (e error) {
	originalBranch, err := gitutil.New(jirix.NewSeq()).CurrentBranchName()
	if err != nil {
		return err
	}
	branches, err := getDependentCLs(jirix, originalBranch)
	if err != nil {
		return err
	}
	for i := 1; i < len(branches); i++ {
		file, err := getCommitMessageFileName(jirix, branches[i])
		if err != nil {
			return err
		}
		if _, err := jirix.NewSeq().Stat(file); err != nil {
			if !runutil.IsNotExist(err) {
				return err
			}
			return fmt.Errorf(`Failed to export the branch %q to Gerrit because its ancestor %q has not been exported to Gerrit yet.
The following steps are needed before the operation can be retried:
$ git checkout %v
$ jiri cl mail
$ git checkout %v
# retry the original command
`, originalBranch, branches[i], branches[i], originalBranch)
		}
	}

	return nil
}
Example #11
0
// assertFilesNotCommitted asserts that the files exist and are *not*
// committed in the current branch.
func assertFilesNotCommitted(t *testing.T, jirix *jiri.X, files []string) {
	assertFilesExist(t, jirix, files)
	for _, file := range files {
		if gitutil.New(jirix.NewSeq()).IsFileCommitted(file) {
			t.Fatalf("expected file %v not to be committed but it is", file)
		}
	}
}
Example #12
0
// assertFilesPushedToRef asserts that the given files have been
// pushed to the given remote repository reference.
func assertFilesPushedToRef(t *testing.T, jirix *jiri.X, repoPath, gerritPath, pushedRef string, files []string) {
	chdir(t, jirix, gerritPath)
	assertCommitCount(t, jirix, pushedRef, "master", 1)
	if err := gitutil.New(jirix.NewSeq()).CheckoutBranch(pushedRef); err != nil {
		t.Fatalf("%v", err)
	}
	assertFilesCommitted(t, jirix, files)
	chdir(t, jirix, repoPath)
}
Example #13
0
// assertStashSize asserts that the stash size matches the expected
// size.
func assertStashSize(t *testing.T, jirix *jiri.X, want int) {
	got, err := gitutil.New(jirix.NewSeq()).StashSize()
	if err != nil {
		t.Fatalf("%v", err)
	}
	if got != want {
		t.Fatalf("unxpected stash size: got %v, want %v", got, want)
	}
}
Example #14
0
// assertCommitCount asserts that the commit count between two
// branches matches the expectedCount.
func assertCommitCount(t *testing.T, jirix *jiri.X, branch, baseBranch string, expectedCount int) {
	got, err := gitutil.New(jirix.NewSeq()).CountCommits(branch, baseBranch)
	if err != nil {
		t.Fatalf("%v", err)
	}
	if want := 1; got != want {
		t.Fatalf("unexpected number of commits: got %v, want %v", got, want)
	}
}
Example #15
0
// commitFile commits a file with the specified content into a branch
func commitFile(t *testing.T, jirix *jiri.X, filename string, content string) {
	s := jirix.NewSeq()
	if err := s.WriteFile(filename, []byte(content), 0644).Done(); err != nil {
		t.Fatalf("%v", err)
	}
	commitMessage := "Commit " + filename
	if err := gitutil.New(jirix.NewSeq()).CommitFile(filename, commitMessage); err != nil {
		t.Fatalf("%v", err)
	}
}
Example #16
0
// ensureChangeID makes sure that the last commit contains a Change-Id, and
// returns an error if it does not.
func (review *review) ensureChangeID() error {
	latestCommitMessage, err := gitutil.New(review.jirix.NewSeq()).LatestCommitMessage()
	if err != nil {
		return err
	}
	changeID := changeIDRE.FindString(latestCommitMessage)
	if changeID == "" {
		return noChangeIDError(struct{}{})
	}
	return nil
}
Example #17
0
// TestCleanupDirty checks that cleanup is a no-op if the branch to be
// cleaned up has unmerged changes.
func TestCleanupDirty(t *testing.T) {
	fake, _, _, _, cleanup := setupTest(t, true)
	defer cleanup()
	branch := "my-branch"
	if err := gitutil.New(fake.X.NewSeq()).CreateAndCheckoutBranch(branch); err != nil {
		t.Fatalf("%v", err)
	}
	files := []string{"file1", "file2"}
	commitFiles(t, fake.X, files)
	if err := gitutil.New(fake.X.NewSeq()).CheckoutBranch("master"); err != nil {
		t.Fatalf("%v", err)
	}
	if err := cleanupCL(fake.X, []string{branch}); err == nil {
		t.Fatalf("cleanup did not fail when it should")
	}
	if err := gitutil.New(fake.X.NewSeq()).CheckoutBranch(branch); err != nil {
		t.Fatalf("%v", err)
	}
	assertFilesCommitted(t, fake.X, files)
}
Example #18
0
// TestCleanupClean checks that cleanup succeeds if the branch to be
// cleaned up has been merged with the master.
func TestCleanupClean(t *testing.T) {
	fake, repoPath, originPath, _, cleanup := setupTest(t, true)
	defer cleanup()
	branch := "my-branch"
	if err := gitutil.New(fake.X.NewSeq()).CreateAndCheckoutBranch(branch); err != nil {
		t.Fatalf("%v", err)
	}
	commitFiles(t, fake.X, []string{"file1", "file2"})
	if err := gitutil.New(fake.X.NewSeq()).CheckoutBranch("master"); err != nil {
		t.Fatalf("%v", err)
	}
	chdir(t, fake.X, originPath)
	commitFiles(t, fake.X, []string{"file1", "file2"})
	chdir(t, fake.X, repoPath)
	if err := cleanupCL(fake.X, []string{branch}); err != nil {
		t.Fatalf("cleanup() failed: %v", err)
	}
	if gitutil.New(fake.X.NewSeq()).BranchExists(branch) {
		t.Fatalf("cleanup failed to remove the feature branch")
	}
}
Example #19
0
// submit mocks a Gerrit review submit by pushing the Gerrit remote to origin.
// Actually origin pulls from Gerrit since origin isn't actually a bare git repo.
// Some of our tests actually rely on accessing .git in origin, so it must be non-bare.
func submit(t *testing.T, jirix *jiri.X, originPath string, gerritPath string, review *review) {
	cwd, err := os.Getwd()
	if err != nil {
		t.Fatalf("Getwd() failed: %v", err)
	}
	chdir(t, jirix, originPath)
	expectedRef := gerrit.Reference(review.CLOpts)
	if err := gitutil.New(jirix.NewSeq()).Pull(gerritPath, expectedRef); err != nil {
		t.Fatalf("Pull gerrit to origin failed: %v", err)
	}
	chdir(t, jirix, cwd)
}
Example #20
0
func (fake FakeJiriRoot) writeManifest(manifest *project.Manifest, dir, path string) error {
	git := gitutil.New(fake.X.NewSeq(), gitutil.RootDirOpt(dir))
	if err := manifest.ToFile(fake.X, path); err != nil {
		return err
	}
	if err := git.Add(path); err != nil {
		return err
	}
	if err := git.Commit(); err != nil {
		return err
	}
	return nil
}
Example #21
0
func commitFile(t *testing.T, jirix *jiri.X, dir, file, msg string) {
	cwd, err := os.Getwd()
	if err != nil {
		t.Fatal(err)
	}
	defer jirix.NewSeq().Chdir(cwd)
	if err := jirix.NewSeq().Chdir(dir).Done(); err != nil {
		t.Fatal(err)
	}
	if err := gitutil.New(jirix.NewSeq()).CommitFile(file, msg); err != nil {
		t.Fatal(err)
	}
}
Example #22
0
func newCL(jirix *jiri.X, args []string) error {
	git := gitutil.New(jirix.NewSeq())
	topLevel, err := git.TopLevel()
	if err != nil {
		return err
	}
	originalBranch, err := git.CurrentBranchName()
	if err != nil {
		return err
	}

	// Create a new branch using the current branch.
	newBranch := args[0]
	if err := git.CreateAndCheckoutBranch(newBranch); err != nil {
		return err
	}

	// Register a cleanup handler in case of subsequent errors.
	cleanup := true
	defer func() {
		if cleanup {
			git.CheckoutBranch(originalBranch, gitutil.ForceOpt(true))
			git.DeleteBranch(newBranch, gitutil.ForceOpt(true))
		}
	}()

	s := jirix.NewSeq()
	// Record the dependent CLs for the new branch. The dependent CLs
	// are recorded in a <dependencyPathFileName> file as a
	// newline-separated list of branch names.
	branches, err := getDependentCLs(jirix, originalBranch)
	if err != nil {
		return err
	}
	branches = append(branches, originalBranch)
	newMetadataDir := filepath.Join(topLevel, jiri.ProjectMetaDir, newBranch)
	if err := s.MkdirAll(newMetadataDir, os.FileMode(0755)).Done(); err != nil {
		return err
	}
	file, err := getDependencyPathFileName(jirix, newBranch)
	if err != nil {
		return err
	}
	if err := s.WriteFile(file, []byte(strings.Join(branches, "\n")), os.FileMode(0644)).Done(); err != nil {
		return err
	}

	cleanup = false
	return nil
}
Example #23
0
// runCLMail is a wrapper that sets up and runs a review instance across
// multiple projects.
func runCLMail(jirix *jiri.X, _ []string) error {
	mp, err := initForMultiPart(jirix)
	if err != nil {
		return err
	}
	if mp.clean {
		if err := mp.cleanMultiPartMetadata(jirix); err != nil {
			return err
		}
		return nil
	}
	if mp.current {
		return runCLMailCurrent(jirix, []string{})
	}
	// multipart mode
	if err := mp.writeMultiPartMetadata(jirix); err != nil {
		mp.cleanMultiPartMetadata(jirix)
		return err
	}
	if err := runCLMailCurrent(jirix, []string{}); err != nil {
		return err
	}
	git := gitutil.New(jirix.NewSeq())
	branch, err := git.CurrentBranchName()
	if err != nil {
		return err
	}
	initialMessage, err := strippedGerritCommitMessage(jirix, branch)
	if err != nil {
		return err
	}
	s := jirix.NewSeq()
	tmp, err := s.TempFile("", branch+"-")
	if err != nil {
		return err
	}
	defer func() {
		tmp.Close()
		os.Remove(tmp.Name())
	}()
	if _, err := io.WriteString(tmp, initialMessage); err != nil {
		return err
	}
	// Use Capture to make sure that all output from the subcommands is
	// sent to stdout/stderr.
	flags := clMailMultiFlags()
	flags = append(flags, "--commit-message-body-file="+tmp.Name())
	return s.Capture(jirix.Stdout(), jirix.Stderr()).Last("jiri", mp.commandline(mp.currentKey, flags)...)
}
Example #24
0
// createTestRepos sets up three local repositories: origin, gerrit,
// and the main test repository which pulls from origin and can push
// to gerrit.
func createTestRepos(t *testing.T, jirix *jiri.X) (string, string, string) {
	// Create origin.
	originPath := createRepo(t, jirix, "origin")
	chdir(t, jirix, originPath)
	if err := gitutil.New(jirix.NewSeq()).CommitWithMessage("initial commit"); err != nil {
		t.Fatalf("%v", err)
	}
	// Create test repo.
	repoPath := createRepoFromOrigin(t, jirix, "test", originPath)
	// Add Gerrit remote.
	gerritPath := createRepoFromOrigin(t, jirix, "gerrit", originPath)
	// Switch back to test repo.
	chdir(t, jirix, repoPath)
	return repoPath, originPath, gerritPath
}
Example #25
0
// cleanup cleans up after the review.
func (review *review) cleanup(stashed bool) error {
	git := gitutil.New(review.jirix.NewSeq())
	if err := git.CheckoutBranch(review.CLOpts.Branch); err != nil {
		return err
	}
	if git.BranchExists(review.reviewBranch) {
		if err := git.DeleteBranch(review.reviewBranch, gitutil.ForceOpt(true)); err != nil {
			return err
		}
	}
	if stashed {
		if err := git.StashPop(); err != nil {
			return err
		}
	}
	return nil
}
Example #26
0
// createRepo creates a new repository with the given prefix.
func createRepo(t *testing.T, jirix *jiri.X, prefix string) string {
	s := jirix.NewSeq()
	repoPath, err := s.TempDir(jirix.Root, "repo-"+prefix)
	if err != nil {
		t.Fatalf("TempDir() failed: %v", err)
	}
	if err := os.Chmod(repoPath, 0777); err != nil {
		t.Fatalf("Chmod(%v) failed: %v", repoPath, err)
	}
	if err := gitutil.New(jirix.NewSeq()).Init(repoPath); err != nil {
		t.Fatalf("%v", err)
	}
	if err := s.MkdirAll(filepath.Join(repoPath, jiri.ProjectMetaDir), os.FileMode(0755)).Done(); err != nil {
		t.Fatalf("%v", err)
	}
	return repoPath
}
Example #27
0
func writeReadme(t *testing.T, jirix *jiri.X, projectDir, message string) {
	s := jirix.NewSeq()
	path, perm := filepath.Join(projectDir, "README"), os.FileMode(0644)
	if err := s.WriteFile(path, []byte(message), perm).Done(); err != nil {
		t.Fatalf("%v", err)
	}
	cwd, err := os.Getwd()
	if err != nil {
		t.Fatalf("%v", err)
	}
	defer jirix.NewSeq().Chdir(cwd)
	if err := s.Chdir(projectDir).Done(); err != nil {
		t.Fatalf("%v", err)
	}
	if err := gitutil.New(jirix.NewSeq()).CommitFile(path, "creating README"); err != nil {
		t.Fatalf("%v", err)
	}
}
Example #28
0
func setProjectState(jirix *jiri.X, state *ProjectState, checkDirty bool, ch chan<- error) {
	var err error
	switch state.Project.Protocol {
	case "git":
		scm := gitutil.New(jirix.NewSeq(), gitutil.RootDirOpt(state.Project.Path))
		var branches []string
		branches, state.CurrentBranch, err = scm.GetBranches()
		if err != nil {
			ch <- err
			return
		}
		for _, branch := range branches {
			file := filepath.Join(state.Project.Path, jiri.ProjectMetaDir, branch, ".gerrit_commit_message")
			hasFile := true
			if _, err := jirix.NewSeq().Stat(file); err != nil {
				if !runutil.IsNotExist(err) {
					ch <- err
					return
				}
				hasFile = false
			}
			state.Branches = append(state.Branches, BranchState{
				Name:             branch,
				HasGerritMessage: hasFile,
			})
		}
		if checkDirty {
			state.HasUncommitted, err = scm.HasUncommittedChanges()
			if err != nil {
				ch <- err
				return
			}
			state.HasUntracked, err = scm.HasUntrackedFiles()
			if err != nil {
				ch <- err
				return
			}
		}
	default:
		ch <- UnsupportedProtocolErr(state.Project.Protocol)
		return
	}
	ch <- nil
}
Example #29
0
// TestUpdateUniverseWithRevision checks that UpdateUniverse will pull remote
// projects at the specified revision.
func TestUpdateUniverseWithRevision(t *testing.T) {
	localProjects, fake, cleanup := setupUniverse(t)
	defer cleanup()
	s := fake.X.NewSeq()

	// Set project 1's revision in the manifest to the current revision.
	git := gitutil.New(s, gitutil.RootDirOpt(fake.Projects[localProjects[1].Name]))
	rev, err := git.CurrentRevision()
	if err != nil {
		t.Fatal(err)
	}
	m, err := fake.ReadRemoteManifest()
	if err != nil {
		t.Fatal(err)
	}
	projects := []project.Project{}
	for _, p := range m.Projects {
		if p.Name == localProjects[1].Name {
			p.Revision = rev
		}
		projects = append(projects, p)
	}
	m.Projects = projects
	if err := fake.WriteRemoteManifest(m); err != nil {
		t.Fatal(err)
	}
	// Update README in all projects.
	for _, remoteProjectDir := range fake.Projects {
		writeReadme(t, fake.X, remoteProjectDir, "new revision")
	}
	// Check that calling UpdateUniverse() updates all projects except for
	// project 1.
	if err := fake.UpdateUniverse(false); err != nil {
		t.Fatal(err)
	}
	for i, p := range localProjects {
		if i == 1 {
			checkReadme(t, fake.X, p, "initial readme")
		} else {
			checkReadme(t, fake.X, p, "new revision")
		}
	}
}
Example #30
0
// TestRunInSubdirectory checks that the command will succeed when run from
// within a subdirectory of a branch that does not exist on master branch, and
// will return the user to the subdirectory after completion.
func TestRunInSubdirectory(t *testing.T) {
	fake, repoPath, _, gerritPath, cleanup := setupTest(t, true)
	defer cleanup()
	s := fake.X.NewSeq()
	branch := "my-branch"
	if err := gitutil.New(fake.X.NewSeq()).CreateAndCheckoutBranch(branch); err != nil {
		t.Fatalf("%v", err)
	}
	subdir := "sub/directory"
	subdirPerms := os.FileMode(0744)
	if err := s.MkdirAll(subdir, subdirPerms).Done(); err != nil {
		t.Fatalf("MkdirAll(%v, %v) failed: %v", subdir, subdirPerms, err)
	}
	files := []string{path.Join(subdir, "file1")}
	commitFiles(t, fake.X, files)
	chdir(t, fake.X, subdir)
	review, err := newReview(fake.X, project.Project{}, gerrit.CLOpts{Remote: gerritPath})
	if err != nil {
		t.Fatalf("%v", err)
	}
	setTopicFlag = false
	if err := review.run(); err != nil {
		t.Fatalf("run() failed: %v", err)
	}
	path := path.Join(repoPath, subdir)
	want, err := filepath.EvalSymlinks(path)
	if err != nil {
		t.Fatalf("EvalSymlinks(%v) failed: %v", path, err)
	}
	cwd, err := os.Getwd()
	if err != nil {
		t.Fatalf("%v", err)
	}
	got, err := filepath.EvalSymlinks(cwd)
	if err != nil {
		t.Fatalf("EvalSymlinks(%v) failed: %v", cwd, err)
	}
	if got != want {
		t.Fatalf("unexpected working directory: got %v, want %v", got, want)
	}
	expectedRef := gerrit.Reference(review.CLOpts)
	assertFilesPushedToRef(t, fake.X, repoPath, gerritPath, expectedRef, files)
}