// 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) }
// 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, ctx *tool.Context, originPath string, gerritPath string, review *review) { cwd, err := os.Getwd() if err != nil { t.Fatalf("Getwd() failed: %v", err) } chdir(t, ctx, originPath) expectedRef := gerrit.Reference(review.CLOpts) if err := ctx.Git().Pull(gerritPath, expectedRef); err != nil { t.Fatalf("Pull gerrit to origin failed: %v", err) } chdir(t, ctx, cwd) }
// 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) }
// TestEndToEnd checks the end-to-end functionality of the review tool. func TestEndToEnd(t *testing.T) { fake, repoPath, _, gerritPath, 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{Remote: gerritPath}) if err != nil { t.Fatalf("%v", err) } setTopicFlag = false if err := review.run(); err != nil { t.Fatalf("run() failed: %v", err) } expectedRef := gerrit.Reference(review.CLOpts) assertFilesPushedToRef(t, fake.X, repoPath, gerritPath, expectedRef, files) }
// TestEndToEnd checks the end-to-end functionality of the review tool. func TestEndToEnd(t *testing.T) { ctx, cwd, root, repoPath, _, gerritPath := setupTest(t, true) defer teardownTest(t, ctx, cwd, root) branch := "my-branch" if err := ctx.Git().CreateAndCheckoutBranch(branch); err != nil { t.Fatalf("%v", err) } files := []string{"file1", "file2", "file3"} commitFiles(t, ctx, files) review, err := newReview(ctx, 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) } expectedRef := gerrit.Reference(review.CLOpts) assertFilesPushedToRef(t, ctx, repoPath, gerritPath, expectedRef, files) }
// TestParallelDev checks "jiri cl mail" behavior when parallel development has // been submitted upstream. func TestParallelDev(t *testing.T) { fake, repoPath, originPath, gerritAPath, cleanup := setupTest(t, true) defer cleanup() gerritBPath := createRepoFromOrigin(t, fake.X, "gerritB", originPath) chdir(t, fake.X, repoPath) // Create parallel branches with: // * non-conflicting changes in different files // * conflicting changes in a file createCLWithFiles(t, fake.X, "feature1-A", "A") if err := gitutil.New(fake.X.NewSeq()).CheckoutBranch("master"); err != nil { t.Fatalf("%v", err) } createCLWithFiles(t, fake.X, "feature1-B", "B") commitFile(t, fake.X, "A", "Don't tread on me.") reviewB, err := newReview(fake.X, project.Project{}, gerrit.CLOpts{Remote: gerritBPath}) if err != nil { t.Fatalf("%v", err) } setTopicFlag = false if err := reviewB.run(); err != nil { t.Fatalf("run() failed: %v", err) } // Submit B and verify A doesn't revert it. submit(t, fake.X, originPath, gerritBPath, reviewB) // Assert files pushed to origin. chdir(t, fake.X, originPath) assertFilesExist(t, fake.X, []string{"A", "B"}) chdir(t, fake.X, repoPath) if err := gitutil.New(fake.X.NewSeq()).CheckoutBranch("feature1-A"); err != nil { t.Fatalf("%v", err) } reviewA, err := newReview(fake.X, project.Project{}, gerrit.CLOpts{Remote: gerritAPath}) if err == nil { t.Fatalf("creating a review did not fail when it should") } // Assert state restored after failed review. assertFileContent(t, fake.X, "A", "This is file A") assertFilesDoNotExist(t, fake.X, []string{"B"}) // Manual conflict resolution. if err := gitutil.New(fake.X.NewSeq()).Merge("master", gitutil.ResetOnFailureOpt(false)); err == nil { t.Fatalf("merge applied cleanly when it shouldn't") } assertFilesNotCommitted(t, fake.X, []string{"A", "B"}) assertFileContent(t, fake.X, "B", "This is file B") if err := fake.X.NewSeq().WriteFile("A", []byte("This is file A. Don't tread on me."), 0644).Done(); err != nil { t.Fatalf("%v", err) } if err := gitutil.New(fake.X.NewSeq()).Add("A"); err != nil { t.Fatalf("%v", err) } if err := gitutil.New(fake.X.NewSeq()).Add("B"); err != nil { t.Fatalf("%v", err) } if err := gitutil.New(fake.X.NewSeq()).CommitWithMessage("Conflict resolution"); err != nil { t.Fatalf("%v", err) } // Retry review. reviewA, err = newReview(fake.X, project.Project{}, gerrit.CLOpts{Remote: gerritAPath}) if err != nil { t.Fatalf("review failed: %v", err) } if err := reviewA.run(); err != nil { t.Fatalf("run() failed: %v", err) } chdir(t, fake.X, gerritAPath) expectedRef := gerrit.Reference(reviewA.CLOpts) if err := gitutil.New(fake.X.NewSeq()).CheckoutBranch(expectedRef); err != nil { t.Fatalf("%v", err) } assertFilesExist(t, fake.X, []string{"B"}) }
// TestDependentClsWithEditDelete exercises a previously observed failure case // where if a CL edits a file and a dependent CL deletes it, jiri cl mail after // the deletion failed with unrecoverable merge errors. func TestDependentClsWithEditDelete(t *testing.T) { fake, repoPath, originPath, gerritPath, cleanup := setupTest(t, true) defer cleanup() chdir(t, fake.X, originPath) commitFiles(t, fake.X, []string{"A", "B"}) chdir(t, fake.X, repoPath) if err := syncCL(fake.X); err != nil { t.Fatalf("%v", err) } assertFilesExist(t, fake.X, []string{"A", "B"}) createCLWithFiles(t, fake.X, "editme", "C") if err := fake.X.NewSeq().WriteFile("B", []byte("Will I dream?"), 0644).Done(); err != nil { t.Fatalf("%v", err) } if err := gitutil.New(fake.X.NewSeq()).Add("B"); err != nil { t.Fatalf("%v", err) } if err := gitutil.New(fake.X.NewSeq()).CommitWithMessage("editing stuff"); err != nil { t.Fatalf("git commit failed: %v", err) } review, err := newReview(fake.X, project.Project{}, gerrit.CLOpts{ Remote: gerritPath, Reviewers: parseEmails("run1"), // See hack note about TestLabelsInCommitMessage }) if err != nil { t.Fatalf("%v", err) } setTopicFlag = false if err := review.run(); err != nil { t.Fatalf("run() failed: %v", err) } if err := newCL(fake.X, []string{"deleteme"}); err != nil { t.Fatalf("%v", err) } if err := gitutil.New(fake.X.NewSeq()).Remove("B", "C"); err != nil { t.Fatalf("git rm B C failed: %v", err) } if err := gitutil.New(fake.X.NewSeq()).CommitWithMessage("deleting stuff"); err != nil { t.Fatalf("git commit failed: %v", err) } review, err = newReview(fake.X, project.Project{}, gerrit.CLOpts{ Remote: gerritPath, Reviewers: parseEmails("run2"), }) if err != nil { t.Fatalf("%v", err) } if err := review.run(); err != nil { t.Fatalf("run() failed: %v", err) } chdir(t, fake.X, gerritPath) expectedRef := gerrit.Reference(review.CLOpts) if err := gitutil.New(fake.X.NewSeq()).CheckoutBranch(expectedRef); err != nil { t.Fatalf("%v", err) } assertFilesExist(t, fake.X, []string{"A"}) assertFilesDoNotExist(t, fake.X, []string{"B", "C"}) }
// TestLabelsInCommitMessage checks the labels are correctly processed // for the commit message. // // HACK ALERT: This test runs the review.run() function multiple // times. The function ends up pushing a commit to a fake "gerrit" // repository created by the setupTest() function. For the real gerrit // repository, it is possible to push to the refs/for/change reference // multiple times, because it is a special reference that "maps" // incoming commits to CL branches based on the commit message // Change-Id. The fake "gerrit" repository does not implement this // logic and thus the same reference cannot be pushed to multiple // times. To overcome this obstacle, the test takes advantage of the // fact that the reference name is a function of the reviewers and // uses different reviewers for different review runs. func TestLabelsInCommitMessage(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) } // Test setting -presubmit=none and autosubmit. files := []string{"file1", "file2", "file3"} commitFiles(t, fake.X, files) review, err := newReview(fake.X, project.Project{}, gerrit.CLOpts{ Autosubmit: true, Presubmit: gerrit.PresubmitTestTypeNone, Remote: gerritPath, Reviewers: parseEmails("run1"), }) if err != nil { t.Fatalf("%v", err) } setTopicFlag = false if err := review.run(); err != nil { t.Fatalf("%v", err) } expectedRef := gerrit.Reference(review.CLOpts) assertFilesPushedToRef(t, fake.X, repoPath, gerritPath, expectedRef, files) // The last three lines of the gerrit commit message file should be: // AutoSubmit // PresubmitTest: none // Change-Id: ... file, err := getCommitMessageFileName(review.jirix, review.CLOpts.Branch) if err != nil { t.Fatalf("%v", err) } bytes, err := s.ReadFile(file) if err != nil { t.Fatalf("%v\n", err) } content := string(bytes) lines := strings.Split(content, "\n") // Make sure the Change-Id line is the last line. if got := lines[len(lines)-1]; !strings.HasPrefix(got, "Change-Id") { t.Fatalf("no Change-Id line found: %s", got) } // Make sure the "AutoSubmit" label exists. if autosubmitLabelRE.FindString(content) == "" { t.Fatalf("AutoSubmit label doesn't exist in the commit message: %s", content) } // Make sure the "PresubmitTest" label exists. if presubmitTestLabelRE.FindString(content) == "" { t.Fatalf("PresubmitTest label doesn't exist in the commit message: %s", content) } // Test setting -presubmit=all but keep autosubmit=true. review, err = newReview(fake.X, project.Project{}, gerrit.CLOpts{ Autosubmit: true, Remote: gerritPath, Reviewers: parseEmails("run2"), }) if err != nil { t.Fatalf("%v", err) } if err := review.run(); err != nil { t.Fatalf("%v", err) } expectedRef = gerrit.Reference(review.CLOpts) assertFilesPushedToRef(t, fake.X, repoPath, gerritPath, expectedRef, files) bytes, err = s.ReadFile(file) if err != nil { t.Fatalf("%v\n", err) } content = string(bytes) // Make sure there is no PresubmitTest=none any more. match := presubmitTestLabelRE.FindString(content) if match != "" { t.Fatalf("want no presubmit label line, got: %s", match) } // Make sure the "AutoSubmit" label still exists. if autosubmitLabelRE.FindString(content) == "" { t.Fatalf("AutoSubmit label doesn't exist in the commit message: %s", content) } // Test setting autosubmit=false. review, err = newReview(fake.X, project.Project{}, gerrit.CLOpts{ Remote: gerritPath, Reviewers: parseEmails("run3"), }) if err != nil { t.Fatalf("%v", err) } if err := review.run(); err != nil { t.Fatalf("%v", err) } expectedRef = gerrit.Reference(review.CLOpts) assertFilesPushedToRef(t, fake.X, repoPath, gerritPath, expectedRef, files) bytes, err = s.ReadFile(file) if err != nil { t.Fatalf("%v\n", err) } content = string(bytes) // Make sure there is no AutoSubmit label any more. match = autosubmitLabelRE.FindString(content) if match != "" { t.Fatalf("want no AutoSubmit label line, got: %s", match) } }
// TestSendReview checks the various options for sending a review. func TestSendReview(t *testing.T) { fake, repoPath, _, gerritPath, 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"} commitFiles(t, fake.X, files) { // Test with draft = false, no reviewiers, and no ccs. review, err := newReview(fake.X, project.Project{}, gerrit.CLOpts{Remote: gerritPath}) if err != nil { t.Fatalf("%v", err) } if err := review.send(); err != nil { t.Fatalf("failed to send a review: %v", err) } expectedRef := gerrit.Reference(review.CLOpts) assertFilesPushedToRef(t, fake.X, repoPath, gerritPath, expectedRef, files) } { // Test with draft = true, no reviewers, and no ccs. review, err := newReview(fake.X, project.Project{}, gerrit.CLOpts{ Draft: true, Remote: gerritPath, }) if err != nil { t.Fatalf("%v", err) } if err := review.send(); err != nil { t.Fatalf("failed to send a review: %v", err) } expectedRef := gerrit.Reference(review.CLOpts) assertFilesPushedToRef(t, fake.X, repoPath, gerritPath, expectedRef, files) } { // Test with draft = false, reviewers, and no ccs. review, err := newReview(fake.X, project.Project{}, gerrit.CLOpts{ Remote: gerritPath, Reviewers: parseEmails("reviewer1,[email protected]"), }) if err != nil { t.Fatalf("%v", err) } if err := review.send(); err != nil { t.Fatalf("failed to send a review: %v", err) } expectedRef := gerrit.Reference(review.CLOpts) assertFilesPushedToRef(t, fake.X, repoPath, gerritPath, expectedRef, files) } { // Test with draft = true, reviewers, and ccs. review, err := newReview(fake.X, project.Project{}, gerrit.CLOpts{ Ccs: parseEmails("[email protected],cc2"), Draft: true, Remote: gerritPath, Reviewers: parseEmails("[email protected],reviewer4"), }) if err != nil { t.Fatalf("%v", err) } if err := review.send(); err != nil { t.Fatalf("failed to send a review: %v", err) } expectedRef := gerrit.Reference(review.CLOpts) assertFilesPushedToRef(t, fake.X, repoPath, gerritPath, expectedRef, files) } }
// TestDependentClsWithEditDelete exercises a previously observed failure case // where if a CL edits a file and a dependent CL deletes it, jiri cl mail after // the deletion failed with unrecoverable merge errors. func TestDependentClsWithEditDelete(t *testing.T) { ctx, cwd, root, repoPath, originPath, gerritPath := setupTest(t, true) defer teardownTest(t, ctx, cwd, root) chdir(t, ctx, originPath) commitFiles(t, ctx, []string{"A", "B"}) chdir(t, ctx, repoPath) if err := syncCL(ctx); err != nil { t.Fatalf("%v", err) } assertFilesExist(t, ctx, []string{"A", "B"}) createCLWithFiles(t, ctx, "editme", "C") if err := ctx.Run().WriteFile("B", []byte("Will I dream?"), 0644); err != nil { t.Fatalf("%v", err) } if err := ctx.Git().Add("B"); err != nil { t.Fatalf("%v", err) } if err := ctx.Git().CommitWithMessage("editing stuff"); err != nil { t.Fatalf("git commit failed: %v", err) } review, err := newReview(ctx, gerrit.CLOpts{ Remote: gerritPath, Reviewers: parseEmails("run1"), // See hack note about TestLabelsInCommitMessage }) if err != nil { t.Fatalf("%v", err) } setTopicFlag = false if err := review.run(); err != nil { t.Fatalf("run() failed: %v", err) } if err := newCL(ctx, []string{"deleteme"}); err != nil { t.Fatalf("%v", err) } if err := ctx.Git().Remove("B", "C"); err != nil { t.Fatalf("git rm B C failed: %v", err) } if err := ctx.Git().CommitWithMessage("deleting stuff"); err != nil { t.Fatalf("git commit failed: %v", err) } review, err = newReview(ctx, gerrit.CLOpts{ Remote: gerritPath, Reviewers: parseEmails("run2"), }) if err != nil { t.Fatalf("%v", err) } if err := review.run(); err != nil { t.Fatalf("run() failed: %v", err) } chdir(t, ctx, gerritPath) expectedRef := gerrit.Reference(review.CLOpts) if err := ctx.Git().CheckoutBranch(expectedRef); err != nil { t.Fatalf("%v", err) } assertFilesExist(t, ctx, []string{"A"}) assertFilesDoNotExist(t, ctx, []string{"B", "C"}) }
// TestSendReview checks the various options for sending a review. func TestSendReview(t *testing.T) { ctx, cwd, root, repoPath, _, gerritPath := setupTest(t, true) defer teardownTest(t, ctx, cwd, root) branch := "my-branch" if err := ctx.Git().CreateAndCheckoutBranch(branch); err != nil { t.Fatalf("%v", err) } files := []string{"file1"} commitFiles(t, ctx, files) { // Test with draft = false, no reviewiers, and no ccs. review, err := newReview(ctx, gerrit.CLOpts{Remote: gerritPath}) if err != nil { t.Fatalf("%v", err) } if err := review.send(); err != nil { t.Fatalf("failed to send a review: %v", err) } expectedRef := gerrit.Reference(review.CLOpts) assertFilesPushedToRef(t, ctx, repoPath, gerritPath, expectedRef, files) } { // Test with draft = true, no reviewers, and no ccs. review, err := newReview(ctx, gerrit.CLOpts{ Draft: true, Remote: gerritPath, }) if err != nil { t.Fatalf("%v", err) } if err := review.send(); err != nil { t.Fatalf("failed to send a review: %v", err) } expectedRef := gerrit.Reference(review.CLOpts) assertFilesPushedToRef(t, ctx, repoPath, gerritPath, expectedRef, files) } { // Test with draft = false, reviewers, and no ccs. review, err := newReview(ctx, gerrit.CLOpts{ Remote: gerritPath, Reviewers: parseEmails("reviewer1,[email protected]"), }) if err != nil { t.Fatalf("%v", err) } if err := review.send(); err != nil { t.Fatalf("failed to send a review: %v", err) } expectedRef := gerrit.Reference(review.CLOpts) assertFilesPushedToRef(t, ctx, repoPath, gerritPath, expectedRef, files) } { // Test with draft = true, reviewers, and ccs. review, err := newReview(ctx, gerrit.CLOpts{ Ccs: parseEmails("[email protected],cc2"), Draft: true, Remote: gerritPath, Reviewers: parseEmails("[email protected],reviewer4"), }) if err != nil { t.Fatalf("%v", err) } if err := review.send(); err != nil { t.Fatalf("failed to send a review: %v", err) } expectedRef := gerrit.Reference(review.CLOpts) assertFilesPushedToRef(t, ctx, repoPath, gerritPath, expectedRef, files) } }