Skip to content

Commit 599e028

Browse files
committed
Add unit tests for rebasing a review.
This commit also fixes some bugs that were uncovered by the unit tests. In particular, the code has been updated to properly handle the case that rebasing the review results in a detached head (due to the review ref not being a branch; e.g. 'refs/reviews/myreview').
1 parent 1913b00 commit 599e028

File tree

3 files changed

+283
-52
lines changed

3 files changed

+283
-52
lines changed

repository/mock_repo.go

Lines changed: 132 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -36,10 +36,11 @@ import (
3636
// Where commits "B" and "D" represent reviews that have been submitted, and "G"
3737
// is a pending review.
3838
const (
39-
TestTargetRef = "refs/heads/master"
40-
TestReviewRef = "refs/heads/ojarjur/mychange"
41-
TestRequestsRef = "refs/notes/devtools/reviews"
42-
TestCommentsRef = "refs/notes/devtools/discuss"
39+
TestTargetRef = "refs/heads/master"
40+
TestReviewRef = "refs/heads/ojarjur/mychange"
41+
TestAlternateReviewRef = "refs/review/mychange"
42+
TestRequestsRef = "refs/notes/devtools/reviews"
43+
TestCommentsRef = "refs/notes/devtools/discuss"
4344

4445
TestCommitA = "A"
4546
TestCommitB = "B"
@@ -78,6 +79,21 @@ type mockRepoForTest struct {
7879
Notes map[string]map[string]string `json:"notes,omitempty"`
7980
}
8081

82+
func (r *mockRepoForTest) createCommit(message string, time string, parents []string) (string, error) {
83+
newCommit := mockCommit{
84+
Message: message,
85+
Time: time,
86+
Parents: parents,
87+
}
88+
newCommitJSON, err := json.Marshal(newCommit)
89+
if err != nil {
90+
return "", err
91+
}
92+
newCommitHash := fmt.Sprintf("%x", sha1.Sum([]byte(newCommitJSON)))
93+
r.Commits[newCommitHash] = newCommit
94+
return newCommitHash, nil
95+
}
96+
8197
// NewMockRepoForTest returns a mocked-out instance of the Repo interface that has been pre-populated with test data.
8298
func NewMockRepoForTest() Repo {
8399
commitA := mockCommit{
@@ -130,11 +146,12 @@ func NewMockRepoForTest() Repo {
130146
Time: "6",
131147
Parents: []string{TestCommitF},
132148
}
133-
return mockRepoForTest{
149+
return &mockRepoForTest{
134150
Head: TestTargetRef,
135151
Refs: map[string]string{
136-
TestTargetRef: TestCommitJ,
137-
TestReviewRef: TestCommitI,
152+
TestTargetRef: TestCommitJ,
153+
TestReviewRef: TestCommitI,
154+
TestAlternateReviewRef: TestCommitI,
138155
},
139156
Commits: map[string]mockCommit{
140157
TestCommitA: commitA,
@@ -163,10 +180,10 @@ func NewMockRepoForTest() Repo {
163180
}
164181

165182
// GetPath returns the path to the repo.
166-
func (r mockRepoForTest) GetPath() string { return "~/mockRepo/" }
183+
func (r *mockRepoForTest) GetPath() string { return "~/mockRepo/" }
167184

168185
// GetRepoStateHash returns a hash which embodies the entire current state of a repository.
169-
func (r mockRepoForTest) GetRepoStateHash() (string, error) {
186+
func (r *mockRepoForTest) GetRepoStateHash() (string, error) {
170187
repoJSON, err := json.Marshal(r)
171188
if err != nil {
172189
return "", err
@@ -175,18 +192,21 @@ func (r mockRepoForTest) GetRepoStateHash() (string, error) {
175192
}
176193

177194
// GetUserEmail returns the email address that the user has used to configure git.
178-
func (r mockRepoForTest) GetUserEmail() (string, error) { return "user@example.com", nil }
195+
func (r *mockRepoForTest) GetUserEmail() (string, error) { return "user@example.com", nil }
179196

180197
// GetCoreEditor returns the name of the editor that the user has used to configure git.
181-
func (r mockRepoForTest) GetCoreEditor() (string, error) { return "vi", nil }
198+
func (r *mockRepoForTest) GetCoreEditor() (string, error) { return "vi", nil }
182199

183200
// GetSubmitStrategy returns the way in which a review is submitted
184-
func (r mockRepoForTest) GetSubmitStrategy() (string, error) { return "merge", nil }
201+
func (r *mockRepoForTest) GetSubmitStrategy() (string, error) { return "merge", nil }
185202

186203
// HasUncommittedChanges returns true if there are local, uncommitted changes.
187-
func (r mockRepoForTest) HasUncommittedChanges() (bool, error) { return false, nil }
204+
func (r *mockRepoForTest) HasUncommittedChanges() (bool, error) { return false, nil }
188205

189-
func (r mockRepoForTest) resolveLocalRef(ref string) (string, error) {
206+
func (r *mockRepoForTest) resolveLocalRef(ref string) (string, error) {
207+
if ref == "HEAD" {
208+
ref = r.Head
209+
}
190210
if commit, ok := r.Refs[ref]; ok {
191211
return commit, nil
192212
}
@@ -197,29 +217,29 @@ func (r mockRepoForTest) resolveLocalRef(ref string) (string, error) {
197217
}
198218

199219
// VerifyCommit verifies that the supplied hash points to a known commit.
200-
func (r mockRepoForTest) VerifyCommit(hash string) error {
220+
func (r *mockRepoForTest) VerifyCommit(hash string) error {
201221
if _, ok := r.Commits[hash]; !ok {
202222
return fmt.Errorf("The given hash %q is not a known commit", hash)
203223
}
204224
return nil
205225
}
206226

207227
// VerifyGitRef verifies that the supplied ref points to a known commit.
208-
func (r mockRepoForTest) VerifyGitRef(ref string) error {
228+
func (r *mockRepoForTest) VerifyGitRef(ref string) error {
209229
_, err := r.resolveLocalRef(ref)
210230
return err
211231
}
212232

213233
// GetHeadRef returns the ref that is the current HEAD.
214-
func (r mockRepoForTest) GetHeadRef() (string, error) { return r.Head, nil }
234+
func (r *mockRepoForTest) GetHeadRef() (string, error) { return r.Head, nil }
215235

216236
// GetCommitHash returns the hash of the commit pointed to by the given ref.
217-
func (r mockRepoForTest) GetCommitHash(ref string) (string, error) {
237+
func (r *mockRepoForTest) GetCommitHash(ref string) (string, error) {
218238
err := r.VerifyGitRef(ref)
219239
if err != nil {
220240
return "", err
221241
}
222-
return r.Refs[ref], nil
242+
return r.resolveLocalRef(ref)
223243
}
224244

225245
// ResolveRefCommit returns the commit pointed to by the given ref, which may be a remote ref.
@@ -231,20 +251,20 @@ func (r mockRepoForTest) GetCommitHash(ref string) (string, error) {
231251
// This method should be used when a command may be performed by either the reviewer or the
232252
// reviewee, while GetCommitHash should be used when the encompassing command should only be
233253
// performed by the reviewee.
234-
func (r mockRepoForTest) ResolveRefCommit(ref string) (string, error) {
254+
func (r *mockRepoForTest) ResolveRefCommit(ref string) (string, error) {
235255
if commit, err := r.resolveLocalRef(ref); err == nil {
236256
return commit, err
237257
}
238258
return r.resolveLocalRef(strings.Replace(ref, "refs/heads/", "refs/remotes/origin/", 1))
239259
}
240260

241-
func (r mockRepoForTest) getCommit(ref string) (mockCommit, error) {
261+
func (r *mockRepoForTest) getCommit(ref string) (mockCommit, error) {
242262
commit, err := r.resolveLocalRef(ref)
243263
return r.Commits[commit], err
244264
}
245265

246266
// GetCommitMessage returns the message stored in the commit pointed to by the given ref.
247-
func (r mockRepoForTest) GetCommitMessage(ref string) (string, error) {
267+
func (r *mockRepoForTest) GetCommitMessage(ref string) (string, error) {
248268
commit, err := r.getCommit(ref)
249269
if err != nil {
250270
return "", err
@@ -253,7 +273,7 @@ func (r mockRepoForTest) GetCommitMessage(ref string) (string, error) {
253273
}
254274

255275
// GetCommitTime returns the commit time of the commit pointed to by the given ref.
256-
func (r mockRepoForTest) GetCommitTime(ref string) (string, error) {
276+
func (r *mockRepoForTest) GetCommitTime(ref string) (string, error) {
257277
commit, err := r.getCommit(ref)
258278
if err != nil {
259279
return "", err
@@ -262,7 +282,7 @@ func (r mockRepoForTest) GetCommitTime(ref string) (string, error) {
262282
}
263283

264284
// GetLastParent returns the last parent of the given commit (as ordered by git).
265-
func (r mockRepoForTest) GetLastParent(ref string) (string, error) {
285+
func (r *mockRepoForTest) GetLastParent(ref string) (string, error) {
266286
commit, err := r.getCommit(ref)
267287
if len(commit.Parents) > 0 {
268288
return commit.Parents[len(commit.Parents)-1], err
@@ -271,7 +291,7 @@ func (r mockRepoForTest) GetLastParent(ref string) (string, error) {
271291
}
272292

273293
// GetCommitDetails returns the details of a commit's metadata.
274-
func (r mockRepoForTest) GetCommitDetails(ref string) (*CommitDetails, error) {
294+
func (r *mockRepoForTest) GetCommitDetails(ref string) (*CommitDetails, error) {
275295
commit, err := r.getCommit(ref)
276296
if err != nil {
277297
return nil, err
@@ -286,7 +306,7 @@ func (r mockRepoForTest) GetCommitDetails(ref string) (*CommitDetails, error) {
286306
}
287307

288308
// ancestors returns the breadth-first traversal of a commit's ancestors
289-
func (r mockRepoForTest) ancestors(commit string) ([]string, error) {
309+
func (r *mockRepoForTest) ancestors(commit string) ([]string, error) {
290310
queue := []string{commit}
291311
var ancestors []string
292312
for queue != nil {
@@ -306,7 +326,16 @@ func (r mockRepoForTest) ancestors(commit string) ([]string, error) {
306326
}
307327

308328
// IsAncestor determines if the first argument points to a commit that is an ancestor of the second.
309-
func (r mockRepoForTest) IsAncestor(ancestor, descendant string) (bool, error) {
329+
func (r *mockRepoForTest) IsAncestor(ancestor, descendant string) (bool, error) {
330+
var err error
331+
ancestor, err = r.resolveLocalRef(ancestor)
332+
if err != nil {
333+
return false, err
334+
}
335+
descendant, err = r.resolveLocalRef(descendant)
336+
if err != nil {
337+
return false, err
338+
}
310339
if ancestor == descendant {
311340
return true, nil
312341
}
@@ -323,7 +352,7 @@ func (r mockRepoForTest) IsAncestor(ancestor, descendant string) (bool, error) {
323352
}
324353

325354
// MergeBase determines if the first commit that is an ancestor of the two arguments.
326-
func (r mockRepoForTest) MergeBase(a, b string) (string, error) {
355+
func (r *mockRepoForTest) MergeBase(a, b string) (string, error) {
327356
ancestors, err := r.ancestors(a)
328357
if err != nil {
329358
return "", err
@@ -337,17 +366,17 @@ func (r mockRepoForTest) MergeBase(a, b string) (string, error) {
337366
}
338367

339368
// Diff computes the diff between two given commits.
340-
func (r mockRepoForTest) Diff(left, right string, diffArgs ...string) (string, error) {
369+
func (r *mockRepoForTest) Diff(left, right string, diffArgs ...string) (string, error) {
341370
return fmt.Sprintf("Diff between %q and %q", left, right), nil
342371
}
343372

344373
// Show returns the contents of the given file at the given commit.
345-
func (r mockRepoForTest) Show(commit, path string) (string, error) {
374+
func (r *mockRepoForTest) Show(commit, path string) (string, error) {
346375
return fmt.Sprintf("%s:%s", commit, path), nil
347376
}
348377

349378
// SwitchToRef changes the currently-checked-out ref.
350-
func (r mockRepoForTest) SwitchToRef(ref string) error {
379+
func (r *mockRepoForTest) SwitchToRef(ref string) error {
351380
r.Head = ref
352381
return nil
353382
}
@@ -361,23 +390,82 @@ func (r mockRepoForTest) SwitchToRef(ref string) error {
361390
//
362391
// If the ref pointed to by the 'archive' argument does not exist
363392
// yet, then it will be created.
364-
func (r mockRepoForTest) ArchiveRef(ref, archive string) error { return nil }
393+
func (r *mockRepoForTest) ArchiveRef(ref, archive string) error {
394+
commitToArchive, err := r.resolveLocalRef(ref)
395+
if err != nil {
396+
return err
397+
}
398+
var archiveParents []string
399+
if archiveCommit, err := r.resolveLocalRef(archive); err == nil {
400+
archiveParents = []string{archiveCommit, commitToArchive}
401+
} else {
402+
archiveParents = []string{commitToArchive}
403+
}
404+
archiveCommit, err := r.createCommit("Archiving", "Nowish", archiveParents)
405+
if err != nil {
406+
return err
407+
}
408+
r.Refs[archive] = archiveCommit
409+
return nil
410+
}
365411

366412
// MergeRef merges the given ref into the current one.
367413
//
368414
// The ref argument is the ref to merge, and fastForward indicates that the
369415
// current ref should only move forward, as opposed to creating a bubble merge.
370-
func (r mockRepoForTest) MergeRef(ref string, fastForward bool, messages ...string) error { return nil }
416+
func (r *mockRepoForTest) MergeRef(ref string, fastForward bool, messages ...string) error {
417+
newCommitHash, err := r.resolveLocalRef(ref)
418+
if err != nil {
419+
return err
420+
}
421+
if !fastForward {
422+
origCommit, err := r.resolveLocalRef(r.Head)
423+
if err != nil {
424+
return err
425+
}
426+
newCommit, err := r.getCommit(ref)
427+
if err != nil {
428+
return err
429+
}
430+
message := strings.Join(messages, "\n\n")
431+
time := newCommit.Time
432+
parents := []string{origCommit, newCommitHash}
433+
newCommitHash, err = r.createCommit(message, time, parents)
434+
if err != nil {
435+
return err
436+
}
437+
}
438+
r.Refs[r.Head] = newCommitHash
439+
return nil
440+
}
371441

372442
// RebaseRef rebases the current ref onto the given one.
373-
func (r mockRepoForTest) RebaseRef(ref string) error { return nil }
443+
func (r *mockRepoForTest) RebaseRef(ref string) error {
444+
parentHash := r.Refs[ref]
445+
origCommit, err := r.getCommit(r.Head)
446+
if err != nil {
447+
return err
448+
}
449+
newCommitHash, err := r.createCommit(origCommit.Message, origCommit.Time, []string{parentHash})
450+
if err != nil {
451+
return err
452+
}
453+
if strings.HasPrefix(r.Head, "refs/heads/") {
454+
r.Refs[r.Head] = newCommitHash
455+
} else {
456+
// The current head is not a branch, so updating
457+
// it should leave us in a detached-head state.
458+
r.Head = newCommitHash
459+
}
460+
return nil
461+
}
374462

375463
// ListCommits returns the list of commits reachable from the given ref.
376464
//
377465
// The generated list is in chronological order (with the oldest commit first).
378466
//
379467
// If the specified ref does not exist, then this method returns an empty result.
380-
func (r mockRepoForTest) ListCommits(ref string) []string { return nil }
468+
func (r *mockRepoForTest) ListCommits(ref string) []string { return nil }
381469

382470
// ListCommitsBetween returns the list of commits between the two given revisions.
383471
//
@@ -393,7 +481,7 @@ func (r mockRepoForTest) ListCommits(ref string) []string { return nil }
393481
// method compatible with git's built-in "rev-list" command.
394482
//
395483
// The generated list is in chronological order (with the oldest commit first).
396-
func (r mockRepoForTest) ListCommitsBetween(from, to string) ([]string, error) {
484+
func (r *mockRepoForTest) ListCommitsBetween(from, to string) ([]string, error) {
397485
commits := []string{to}
398486
potentialCommits, _ := r.ancestors(to)
399487
for _, commit := range potentialCommits {
@@ -409,7 +497,7 @@ func (r mockRepoForTest) ListCommitsBetween(from, to string) ([]string, error) {
409497
}
410498

411499
// GetNotes reads the notes from the given ref that annotate the given revision.
412-
func (r mockRepoForTest) GetNotes(notesRef, revision string) []Note {
500+
func (r *mockRepoForTest) GetNotes(notesRef, revision string) []Note {
413501
notesText := r.Notes[notesRef][revision]
414502
var notes []Note
415503
for _, line := range strings.Split(notesText, "\n") {
@@ -423,7 +511,7 @@ func (r mockRepoForTest) GetNotes(notesRef, revision string) []Note {
423511
// The returned value is a mapping from commit hash to the list of notes for that commit.
424512
//
425513
// This is the batch version of the corresponding GetNotes(...) method.
426-
func (r mockRepoForTest) GetAllNotes(notesRef string) (map[string][]Note, error) {
514+
func (r *mockRepoForTest) GetAllNotes(notesRef string) (map[string][]Note, error) {
427515
notesMap := make(map[string][]Note)
428516
for _, commit := range r.ListNotedRevisions(notesRef) {
429517
notesMap[commit] = r.GetNotes(notesRef, commit)
@@ -432,15 +520,15 @@ func (r mockRepoForTest) GetAllNotes(notesRef string) (map[string][]Note, error)
432520
}
433521

434522
// AppendNote appends a note to a revision under the given ref.
435-
func (r mockRepoForTest) AppendNote(ref, revision string, note Note) error {
523+
func (r *mockRepoForTest) AppendNote(ref, revision string, note Note) error {
436524
existingNotes := r.Notes[ref][revision]
437525
newNotes := existingNotes + "\n" + string(note)
438526
r.Notes[ref][revision] = newNotes
439527
return nil
440528
}
441529

442530
// ListNotedRevisions returns the collection of revisions that are annotated by notes in the given ref.
443-
func (r mockRepoForTest) ListNotedRevisions(notesRef string) []string {
531+
func (r *mockRepoForTest) ListNotedRevisions(notesRef string) []string {
444532
var revisions []string
445533
for revision := range r.Notes[notesRef] {
446534
if _, ok := r.Commits[revision]; ok {
@@ -451,15 +539,15 @@ func (r mockRepoForTest) ListNotedRevisions(notesRef string) []string {
451539
}
452540

453541
// PushNotes pushes git notes to a remote repo.
454-
func (r mockRepoForTest) PushNotes(remote, notesRefPattern string) error { return nil }
542+
func (r *mockRepoForTest) PushNotes(remote, notesRefPattern string) error { return nil }
455543

456544
// PullNotes fetches the contents of the given notes ref from a remote repo,
457545
// and then merges them with the corresponding local notes using the
458546
// "cat_sort_uniq" strategy.
459-
func (r mockRepoForTest) PullNotes(remote, notesRefPattern string) error { return nil }
547+
func (r *mockRepoForTest) PullNotes(remote, notesRefPattern string) error { return nil }
460548

461549
// PushNotesAndArchive pushes the given notes and archive refs to a remote repo.
462-
func (r mockRepoForTest) PushNotesAndArchive(remote, notesRefPattern, archiveRefPattern string) error {
550+
func (r *mockRepoForTest) PushNotesAndArchive(remote, notesRefPattern, archiveRefPattern string) error {
463551
return nil
464552
}
465553

@@ -475,6 +563,6 @@ func (r mockRepoForTest) PushNotesAndArchive(remote, notesRefPattern, archiveRef
475563
// so we do not maintain any consistency with their tree objects. Instead,
476564
// we merely ensure that their history graph includes every commit that we
477565
// intend to keep.
478-
func (r mockRepoForTest) PullNotesAndArchive(remote, notesRefPattern, archiveRefPattern string) error {
566+
func (r *mockRepoForTest) PullNotesAndArchive(remote, notesRefPattern, archiveRefPattern string) error {
479567
return nil
480568
}

0 commit comments

Comments
 (0)