diff options
Diffstat (limited to 'third_party/go/git-appraise/review/review_test.go')
-rw-r--r-- | third_party/go/git-appraise/review/review_test.go | 870 |
1 files changed, 870 insertions, 0 deletions
diff --git a/third_party/go/git-appraise/review/review_test.go b/third_party/go/git-appraise/review/review_test.go new file mode 100644 index 000000000000..af699afd9aeb --- /dev/null +++ b/third_party/go/git-appraise/review/review_test.go @@ -0,0 +1,870 @@ +/* +Copyright 2015 Google Inc. All rights reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package review + +import ( + "github.com/google/git-appraise/repository" + "github.com/google/git-appraise/review/comment" + "github.com/google/git-appraise/review/request" + "sort" + "testing" +) + +func TestCommentSorting(t *testing.T) { + sampleComments := []*comment.Comment{ + &comment.Comment{ + Timestamp: "012400", + Description: "Fourth", + }, + &comment.Comment{ + Timestamp: "012400", + Description: "Fifth", + }, + &comment.Comment{ + Timestamp: "012346", + Description: "Second", + }, + &comment.Comment{ + Timestamp: "012345", + Description: "First", + }, + &comment.Comment{ + Timestamp: "012347", + Description: "Third", + }, + } + sort.Stable(commentsByTimestamp(sampleComments)) + descriptions := []string{} + for _, comment := range sampleComments { + descriptions = append(descriptions, comment.Description) + } + if !(descriptions[0] == "First" && descriptions[1] == "Second" && descriptions[2] == "Third" && descriptions[3] == "Fourth" && descriptions[4] == "Fifth") { + t.Fatalf("Comment ordering failed. Got %v", sampleComments) + } +} + +func TestThreadSorting(t *testing.T) { + sampleThreads := []CommentThread{ + CommentThread{ + Comment: comment.Comment{ + Timestamp: "012400", + Description: "Fourth", + }, + }, + CommentThread{ + Comment: comment.Comment{ + Timestamp: "012400", + Description: "Fifth", + }, + }, + CommentThread{ + Comment: comment.Comment{ + Timestamp: "012346", + Description: "Second", + }, + }, + CommentThread{ + Comment: comment.Comment{ + Timestamp: "012345", + Description: "First", + }, + }, + CommentThread{ + Comment: comment.Comment{ + Timestamp: "012347", + Description: "Third", + }, + }, + } + sort.Stable(byTimestamp(sampleThreads)) + descriptions := []string{} + for _, thread := range sampleThreads { + descriptions = append(descriptions, thread.Comment.Description) + } + if !(descriptions[0] == "First" && descriptions[1] == "Second" && descriptions[2] == "Third" && descriptions[3] == "Fourth" && descriptions[4] == "Fifth") { + t.Fatalf("Comment thread ordering failed. Got %v", sampleThreads) + } +} + +func TestRequestSorting(t *testing.T) { + sampleRequests := []request.Request{ + request.Request{ + Timestamp: "012400", + Description: "Fourth", + }, + request.Request{ + Timestamp: "012400", + Description: "Fifth", + }, + request.Request{ + Timestamp: "012346", + Description: "Second", + }, + request.Request{ + Timestamp: "012345", + Description: "First", + }, + request.Request{ + Timestamp: "012347", + Description: "Third", + }, + } + sort.Stable(requestsByTimestamp(sampleRequests)) + descriptions := []string{} + for _, r := range sampleRequests { + descriptions = append(descriptions, r.Description) + } + if !(descriptions[0] == "First" && descriptions[1] == "Second" && descriptions[2] == "Third" && descriptions[3] == "Fourth" && descriptions[4] == "Fifth") { + t.Fatalf("Review request ordering failed. Got %v", sampleRequests) + } +} + +func validateUnresolved(t *testing.T, resolved *bool) { + if resolved != nil { + t.Fatalf("Expected resolved status to be unset, but instead it was %v", *resolved) + } +} + +func validateAccepted(t *testing.T, resolved *bool) { + if resolved == nil { + t.Fatal("Expected resolved status to be true, but it was unset") + } + if !*resolved { + t.Fatal("Expected resolved status to be true, but it was false") + } +} + +func validateRejected(t *testing.T, resolved *bool) { + if resolved == nil { + t.Fatal("Expected resolved status to be false, but it was unset") + } + if *resolved { + t.Fatal("Expected resolved status to be false, but it was true") + } +} + +func (commentThread *CommentThread) validateUnresolved(t *testing.T) { + validateUnresolved(t, commentThread.Resolved) +} + +func (commentThread *CommentThread) validateAccepted(t *testing.T) { + validateAccepted(t, commentThread.Resolved) +} + +func (commentThread *CommentThread) validateRejected(t *testing.T) { + validateRejected(t, commentThread.Resolved) +} + +func TestSimpleAcceptedThreadStatus(t *testing.T) { + resolved := true + simpleThread := CommentThread{ + Comment: comment.Comment{ + Resolved: &resolved, + }, + } + simpleThread.updateResolvedStatus() + simpleThread.validateAccepted(t) +} + +func TestSimpleRejectedThreadStatus(t *testing.T) { + resolved := false + simpleThread := CommentThread{ + Comment: comment.Comment{ + Resolved: &resolved, + }, + } + simpleThread.updateResolvedStatus() + simpleThread.validateRejected(t) +} + +func TestFYIThenAcceptedThreadStatus(t *testing.T) { + accepted := true + sampleThread := CommentThread{ + Comment: comment.Comment{ + Resolved: nil, + }, + Children: []CommentThread{ + CommentThread{ + Comment: comment.Comment{ + Timestamp: "012345", + Resolved: &accepted, + }, + }, + }, + } + sampleThread.updateResolvedStatus() + sampleThread.validateUnresolved(t) +} + +func TestFYIThenFYIThreadStatus(t *testing.T) { + sampleThread := CommentThread{ + Comment: comment.Comment{ + Resolved: nil, + }, + Children: []CommentThread{ + CommentThread{ + Comment: comment.Comment{ + Timestamp: "012345", + Resolved: nil, + }, + }, + }, + } + sampleThread.updateResolvedStatus() + sampleThread.validateUnresolved(t) +} + +func TestFYIThenRejectedThreadStatus(t *testing.T) { + rejected := false + sampleThread := CommentThread{ + Comment: comment.Comment{ + Resolved: nil, + }, + Children: []CommentThread{ + CommentThread{ + Comment: comment.Comment{ + Timestamp: "012345", + Resolved: &rejected, + }, + }, + }, + } + sampleThread.updateResolvedStatus() + sampleThread.validateRejected(t) +} + +func TestAcceptedThenAcceptedThreadStatus(t *testing.T) { + accepted := true + sampleThread := CommentThread{ + Comment: comment.Comment{ + Resolved: &accepted, + }, + Children: []CommentThread{ + CommentThread{ + Comment: comment.Comment{ + Timestamp: "012345", + Resolved: &accepted, + }, + }, + }, + } + sampleThread.updateResolvedStatus() + sampleThread.validateAccepted(t) +} + +func TestAcceptedThenFYIThreadStatus(t *testing.T) { + accepted := true + sampleThread := CommentThread{ + Comment: comment.Comment{ + Resolved: &accepted, + }, + Children: []CommentThread{ + CommentThread{ + Comment: comment.Comment{ + Timestamp: "012345", + Resolved: nil, + }, + }, + }, + } + sampleThread.updateResolvedStatus() + sampleThread.validateAccepted(t) +} + +func TestAcceptedThenRejectedThreadStatus(t *testing.T) { + accepted := true + rejected := false + sampleThread := CommentThread{ + Comment: comment.Comment{ + Resolved: &accepted, + }, + Children: []CommentThread{ + CommentThread{ + Comment: comment.Comment{ + Timestamp: "012345", + Resolved: &rejected, + }, + }, + }, + } + sampleThread.updateResolvedStatus() + sampleThread.validateRejected(t) +} + +func TestRejectedThenAcceptedThreadStatus(t *testing.T) { + accepted := true + rejected := false + sampleThread := CommentThread{ + Comment: comment.Comment{ + Resolved: &rejected, + }, + Children: []CommentThread{ + CommentThread{ + Comment: comment.Comment{ + Timestamp: "012345", + Resolved: &accepted, + }, + }, + }, + } + sampleThread.updateResolvedStatus() + sampleThread.validateUnresolved(t) +} + +func TestRejectedThenFYIThreadStatus(t *testing.T) { + rejected := false + sampleThread := CommentThread{ + Comment: comment.Comment{ + Resolved: &rejected, + }, + Children: []CommentThread{ + CommentThread{ + Comment: comment.Comment{ + Timestamp: "012345", + Resolved: nil, + }, + }, + }, + } + sampleThread.updateResolvedStatus() + sampleThread.validateRejected(t) +} + +func TestRejectedThenRejectedThreadStatus(t *testing.T) { + rejected := false + sampleThread := CommentThread{ + Comment: comment.Comment{ + Resolved: &rejected, + }, + Children: []CommentThread{ + CommentThread{ + Comment: comment.Comment{ + Timestamp: "012345", + Resolved: &rejected, + }, + }, + }, + } + sampleThread.updateResolvedStatus() + sampleThread.validateRejected(t) +} + +func TestRejectedThenAcceptedThreadsStatus(t *testing.T) { + accepted := true + rejected := false + threads := []CommentThread{ + CommentThread{ + Comment: comment.Comment{ + Timestamp: "012345", + Resolved: &rejected, + }, + }, + CommentThread{ + Comment: comment.Comment{ + Timestamp: "012346", + Resolved: &accepted, + }, + }, + } + status := updateThreadsStatus(threads) + validateRejected(t, status) +} + +func TestRejectedThenFYIThreadsStatus(t *testing.T) { + rejected := false + threads := []CommentThread{ + CommentThread{ + Comment: comment.Comment{ + Timestamp: "012345", + Resolved: &rejected, + }, + }, + CommentThread{ + Comment: comment.Comment{ + Timestamp: "012346", + Resolved: nil, + }, + }, + } + status := updateThreadsStatus(threads) + validateRejected(t, status) +} + +func TestRejectedThenRejectedThreadsStatus(t *testing.T) { + rejected := false + threads := []CommentThread{ + CommentThread{ + Comment: comment.Comment{ + Timestamp: "012345", + Resolved: &rejected, + }, + }, + CommentThread{ + Comment: comment.Comment{ + Timestamp: "012346", + Resolved: &rejected, + }, + }, + } + status := updateThreadsStatus(threads) + validateRejected(t, status) +} + +func TestAcceptedThenAcceptedThreadsStatus(t *testing.T) { + accepted := true + threads := []CommentThread{ + CommentThread{ + Comment: comment.Comment{ + Timestamp: "012345", + Resolved: &accepted, + }, + }, + CommentThread{ + Comment: comment.Comment{ + Timestamp: "012346", + Resolved: &accepted, + }, + }, + } + status := updateThreadsStatus(threads) + validateAccepted(t, status) +} + +func TestAcceptedThenFYIThreadsStatus(t *testing.T) { + accepted := true + threads := []CommentThread{ + CommentThread{ + Comment: comment.Comment{ + Timestamp: "012345", + Resolved: &accepted, + }, + }, + CommentThread{ + Comment: comment.Comment{ + Timestamp: "012346", + Resolved: nil, + }, + }, + } + status := updateThreadsStatus(threads) + validateAccepted(t, status) +} + +func TestAcceptedThenRejectedThreadsStatus(t *testing.T) { + accepted := true + rejected := false + threads := []CommentThread{ + CommentThread{ + Comment: comment.Comment{ + Timestamp: "012345", + Resolved: &accepted, + }, + }, + CommentThread{ + Comment: comment.Comment{ + Timestamp: "012346", + Resolved: &rejected, + }, + }, + } + status := updateThreadsStatus(threads) + validateRejected(t, status) +} + +func TestFYIThenAcceptedThreadsStatus(t *testing.T) { + accepted := true + threads := []CommentThread{ + CommentThread{ + Comment: comment.Comment{ + Timestamp: "012345", + Resolved: nil, + }, + }, + CommentThread{ + Comment: comment.Comment{ + Timestamp: "012346", + Resolved: &accepted, + }, + }, + } + status := updateThreadsStatus(threads) + validateAccepted(t, status) +} + +func TestFYIThenFYIThreadsStatus(t *testing.T) { + threads := []CommentThread{ + CommentThread{ + Comment: comment.Comment{ + Timestamp: "012345", + Resolved: nil, + }, + }, + CommentThread{ + Comment: comment.Comment{ + Timestamp: "012346", + Resolved: nil, + }, + }, + } + status := updateThreadsStatus(threads) + validateUnresolved(t, status) +} + +func TestFYIThenRejectedThreadsStatus(t *testing.T) { + rejected := false + threads := []CommentThread{ + CommentThread{ + Comment: comment.Comment{ + Timestamp: "012345", + Resolved: nil, + }, + }, + CommentThread{ + Comment: comment.Comment{ + Timestamp: "012346", + Resolved: &rejected, + }, + }, + } + status := updateThreadsStatus(threads) + validateRejected(t, status) +} + +func TestBuildCommentThreads(t *testing.T) { + rejected := false + accepted := true + root := comment.Comment{ + Timestamp: "012345", + Resolved: nil, + Description: "root", + } + rootHash, err := root.Hash() + if err != nil { + t.Fatal(err) + } + child := comment.Comment{ + Timestamp: "012346", + Resolved: nil, + Parent: rootHash, + Description: "child", + } + childHash, err := child.Hash() + updatedChild := comment.Comment{ + Timestamp: "012346", + Resolved: &rejected, + Original: childHash, + Description: "updated child", + } + updatedChildHash, err := updatedChild.Hash() + if err != nil { + t.Fatal(err) + } + leaf := comment.Comment{ + Timestamp: "012347", + Resolved: &accepted, + Parent: childHash, + Description: "leaf", + } + leafHash, err := leaf.Hash() + if err != nil { + t.Fatal(err) + } + commentsByHash := map[string]comment.Comment{ + rootHash: root, + childHash: child, + updatedChildHash: updatedChild, + leafHash: leaf, + } + threads := buildCommentThreads(commentsByHash) + if len(threads) != 1 { + t.Fatalf("Unexpected threads: %v", threads) + } + rootThread := threads[0] + if rootThread.Comment.Description != "root" { + t.Fatalf("Unexpected root thread: %v", rootThread) + } + if !rootThread.Edited { + t.Fatalf("Unexpected root thread edited status: %v", rootThread) + } + if len(rootThread.Children) != 1 { + t.Fatalf("Unexpected root children: %v", rootThread.Children) + } + rootChild := rootThread.Children[0] + if rootChild.Comment.Description != "updated child" { + t.Fatalf("Unexpected updated child: %v", rootChild) + } + if rootChild.Original.Description != "child" { + t.Fatalf("Unexpected original child: %v", rootChild) + } + if len(rootChild.Edits) != 1 { + t.Fatalf("Unexpected child history: %v", rootChild.Edits) + } + if len(rootChild.Children) != 1 { + t.Fatalf("Unexpected leaves: %v", rootChild.Children) + } + threadLeaf := rootChild.Children[0] + if threadLeaf.Comment.Description != "leaf" { + t.Fatalf("Unexpected leaf: %v", threadLeaf) + } + if len(threadLeaf.Children) != 0 { + t.Fatalf("Unexpected leaf children: %v", threadLeaf.Children) + } + if threadLeaf.Edited { + t.Fatalf("Unexpected leaf edited status: %v", threadLeaf) + } +} + +func TestGetHeadCommit(t *testing.T) { + repo := repository.NewMockRepoForTest() + + submittedSimpleReview, err := Get(repo, repository.TestCommitB) + if err != nil { + t.Fatal(err) + } + submittedSimpleReviewHead, err := submittedSimpleReview.GetHeadCommit() + if err != nil { + t.Fatal("Unable to compute the head commit for a known review of a simple commit: ", err) + } + if submittedSimpleReviewHead != repository.TestCommitB { + t.Fatal("Unexpected head commit computed for a known review of a simple commit.") + } + + submittedModifiedReview, err := Get(repo, repository.TestCommitD) + if err != nil { + t.Fatal(err) + } + submittedModifiedReviewHead, err := submittedModifiedReview.GetHeadCommit() + if err != nil { + t.Fatal("Unable to compute the head commit for a known, multi-commit review: ", err) + } + if submittedModifiedReviewHead != repository.TestCommitE { + t.Fatal("Unexpected head commit for a known, multi-commit review.") + } + + pendingReview, err := Get(repo, repository.TestCommitG) + if err != nil { + t.Fatal(err) + } + pendingReviewHead, err := pendingReview.GetHeadCommit() + if err != nil { + t.Fatal("Unable to compute the head commit for a known review of a merge commit: ", err) + } + if pendingReviewHead != repository.TestCommitI { + t.Fatal("Unexpected head commit computed for a pending review.") + } +} + +func TestGetBaseCommit(t *testing.T) { + repo := repository.NewMockRepoForTest() + + submittedSimpleReview, err := Get(repo, repository.TestCommitB) + if err != nil { + t.Fatal(err) + } + submittedSimpleReviewBase, err := submittedSimpleReview.GetBaseCommit() + if err != nil { + t.Fatal("Unable to compute the base commit for a known review of a simple commit: ", err) + } + if submittedSimpleReviewBase != repository.TestCommitA { + t.Fatal("Unexpected base commit computed for a known review of a simple commit.") + } + + submittedMergeReview, err := Get(repo, repository.TestCommitD) + if err != nil { + t.Fatal(err) + } + submittedMergeReviewBase, err := submittedMergeReview.GetBaseCommit() + if err != nil { + t.Fatal("Unable to compute the base commit for a known review of a merge commit: ", err) + } + if submittedMergeReviewBase != repository.TestCommitC { + t.Fatal("Unexpected base commit computed for a known review of a merge commit.") + } + + pendingReview, err := Get(repo, repository.TestCommitG) + if err != nil { + t.Fatal(err) + } + pendingReviewBase, err := pendingReview.GetBaseCommit() + if err != nil { + t.Fatal("Unable to compute the base commit for a known review of a merge commit: ", err) + } + if pendingReviewBase != repository.TestCommitF { + t.Fatal("Unexpected base commit computed for a pending review.") + } + + abandonRequest := pendingReview.Request + abandonRequest.TargetRef = "" + abandonNote, err := abandonRequest.Write() + if err != nil { + t.Fatal(err) + } + if err := repo.AppendNote(request.Ref, repository.TestCommitG, abandonNote); err != nil { + t.Fatal(err) + } + abandonedReview, err := Get(repo, repository.TestCommitG) + if err != nil { + t.Fatal(err) + } + if abandonedReview.IsOpen() { + t.Fatal("Failed to update a review to be abandoned") + } + abandonedReviewBase, err := abandonedReview.GetBaseCommit() + if err != nil { + t.Fatal("Unable to compute the base commit for an abandoned review: ", err) + } + if abandonedReviewBase != repository.TestCommitE { + t.Fatal("Unexpected base commit computed for an abandoned review.") + } +} + +func TestGetRequests(t *testing.T) { + repo := repository.NewMockRepoForTest() + pendingReview, err := Get(repo, repository.TestCommitG) + if err != nil { + t.Fatal(err) + } + if len(pendingReview.AllRequests) != 3 || pendingReview.Request.Description != "Final description of G" { + t.Fatal("Unexpected requests for a pending review: ", pendingReview.AllRequests, pendingReview.Request) + } +} + +func TestRebase(t *testing.T) { + repo := repository.NewMockRepoForTest() + pendingReview, err := Get(repo, repository.TestCommitG) + if err != nil { + t.Fatal(err) + } + + // Rebase the review and then confirm that it has been updated correctly. + if err := pendingReview.Rebase(true); err != nil { + t.Fatal(err) + } + reviewJSON, err := pendingReview.GetJSON() + if err != nil { + t.Fatal(err) + } + headRef, err := repo.GetHeadRef() + if err != nil { + t.Fatal(err) + } + if headRef != pendingReview.Request.ReviewRef { + t.Fatal("Failed to switch to the review ref during a rebase") + } + isAncestor, err := repo.IsAncestor(pendingReview.Revision, archiveRef) + if err != nil { + t.Fatal(err) + } + if !isAncestor { + t.Fatalf("Commit %q is not archived", pendingReview.Revision) + } + reviewCommit, err := repo.GetCommitHash(pendingReview.Request.ReviewRef) + if err != nil { + t.Fatal(err) + } + reviewAlias := pendingReview.Request.Alias + if reviewAlias == "" || reviewAlias == pendingReview.Revision || reviewCommit != reviewAlias { + t.Fatalf("Failed to set the review alias: %q", reviewJSON) + } + + // Submit the review. + if err := repo.SwitchToRef(pendingReview.Request.TargetRef); err != nil { + t.Fatal(err) + } + if err := repo.MergeRef(pendingReview.Request.ReviewRef, true); err != nil { + t.Fatal(err) + } + + // Reread the review and confirm that it has been submitted. + submittedReview, err := Get(repo, pendingReview.Revision) + if err != nil { + t.Fatal(err) + } + submittedReviewJSON, err := submittedReview.GetJSON() + if err != nil { + t.Fatal(err) + } + if !submittedReview.Submitted { + t.Fatalf("Failed to submit the review: %q", submittedReviewJSON) + } +} + +func TestRebaseDetachedHead(t *testing.T) { + repo := repository.NewMockRepoForTest() + pendingReview, err := Get(repo, repository.TestCommitG) + if err != nil { + t.Fatal(err) + } + + // Switch the review to having a review ref that is not a branch. + pendingReview.Request.ReviewRef = repository.TestAlternateReviewRef + newNote, err := pendingReview.Request.Write() + if err != nil { + t.Fatal(err) + } + if err := repo.AppendNote(request.Ref, pendingReview.Revision, newNote); err != nil { + t.Fatal(err) + } + pendingReview, err = Get(repo, repository.TestCommitG) + if err != nil { + t.Fatal(err) + } + + // Rebase the review and then confirm that it has been updated correctly. + if err := pendingReview.Rebase(true); err != nil { + t.Fatal(err) + } + headRef, err := repo.GetHeadRef() + if err != nil { + t.Fatal(err) + } + if headRef != pendingReview.Request.Alias { + t.Fatal("Failed to switch to a detached head during a rebase") + } + isAncestor, err := repo.IsAncestor(pendingReview.Revision, archiveRef) + if err != nil { + t.Fatal(err) + } + if !isAncestor { + t.Fatalf("Commit %q is not archived", pendingReview.Revision) + } + + // Submit the review. + if err := repo.SwitchToRef(pendingReview.Request.TargetRef); err != nil { + t.Fatal(err) + } + reviewHead, err := pendingReview.GetHeadCommit() + if err != nil { + t.Fatal(err) + } + if err := repo.MergeRef(reviewHead, true); err != nil { + t.Fatal(err) + } + + // Reread the review and confirm that it has been submitted. + submittedReview, err := Get(repo, pendingReview.Revision) + if err != nil { + t.Fatal(err) + } + submittedReviewJSON, err := submittedReview.GetJSON() + if err != nil { + t.Fatal(err) + } + if !submittedReview.Submitted { + t.Fatalf("Failed to submit the review: %q", submittedReviewJSON) + } +} |