1// Copyright 2014 The Go 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
5package main
6
7import (
8	"reflect"
9	"strings"
10	"testing"
11)
12
13func TestCurrentBranch(t *testing.T) {
14	gt := newGitTest(t)
15	defer gt.done()
16
17	t.Logf("on main")
18	checkCurrentBranch(t, "main", "origin/main", false, "", "")
19
20	t.Logf("on newbranch")
21	trun(t, gt.client, "git", "checkout", "--no-track", "-b", "newbranch")
22	checkCurrentBranch(t, "newbranch", "origin/main", false, "", "")
23
24	t.Logf("making change")
25	write(t, gt.client+"/file", "i made a change", 0644)
26	trun(t, gt.client, "git", "commit", "-a", "-m", "My change line.\n\nChange-Id: I0123456789abcdef0123456789abcdef\n")
27	checkCurrentBranch(t, "newbranch", "origin/main", true, "I0123456789abcdef0123456789abcdef", "My change line.")
28
29	t.Logf("on dev.branch")
30	trun(t, gt.client, "git", "checkout", "-t", "-b", "dev.branch", "origin/dev.branch")
31	checkCurrentBranch(t, "dev.branch", "origin/dev.branch", false, "", "")
32
33	t.Logf("on newdev")
34	trun(t, gt.client, "git", "checkout", "-t", "-b", "newdev", "origin/dev.branch")
35	checkCurrentBranch(t, "newdev", "origin/dev.branch", false, "", "")
36
37	t.Logf("making change")
38	write(t, gt.client+"/file", "i made another change", 0644)
39	trun(t, gt.client, "git", "commit", "-a", "-m", "My other change line.\n\nChange-Id: I1123456789abcdef0123456789abcdef\n")
40	checkCurrentBranch(t, "newdev", "origin/dev.branch", true, "I1123456789abcdef0123456789abcdef", "My other change line.")
41
42	t.Logf("detached head mode")
43	trun(t, gt.client, "git", "checkout", "main^0")
44	checkCurrentBranch(t, "HEAD", "", false, "", "")
45	trun(t, gt.client, "git", "checkout", "dev.branch^0")
46	checkCurrentBranch(t, "HEAD", "origin/dev.branch", false, "", "")
47}
48
49func checkCurrentBranch(t *testing.T, name, origin string, hasPending bool, changeID, subject string) {
50	t.Helper()
51	b := CurrentBranch()
52	if b.Name != name {
53		t.Errorf("b.Name = %q, want %q", b.Name, name)
54	}
55	if x := b.OriginBranch(); x != origin {
56		t.Errorf("b.OriginBranch() = %q, want %q", x, origin)
57	}
58	if x := b.HasPendingCommit(); x != hasPending {
59		t.Errorf("b.HasPendingCommit() = %v, want %v", x, hasPending)
60	}
61	if work := b.Pending(); len(work) > 0 {
62		c := work[0]
63		if x := c.ChangeID; x != changeID {
64			t.Errorf("b.Pending()[0].ChangeID = %q, want %q", x, changeID)
65		}
66		if x := c.Subject; x != subject {
67			t.Errorf("b.Pending()[0].Subject = %q, want %q", x, subject)
68		}
69	}
70}
71
72func TestLocalBranches(t *testing.T) {
73	gt := newGitTest(t)
74	defer gt.done()
75
76	t.Logf("on main")
77	checkLocalBranches(t, "main")
78
79	t.Logf("on dev branch")
80	trun(t, gt.client, "git", "checkout", "-b", "newbranch")
81	checkLocalBranches(t, "main", "newbranch")
82
83	t.Logf("detached head mode")
84	trun(t, gt.client, "git", "checkout", "HEAD^0")
85	checkLocalBranches(t, "HEAD", "main", "newbranch")
86}
87
88func checkLocalBranches(t *testing.T, want ...string) {
89	var names []string
90	branches := LocalBranches()
91	for _, b := range branches {
92		names = append(names, b.Name)
93	}
94	if !reflect.DeepEqual(names, want) {
95		t.Errorf("LocalBranches() = %v, want %v", names, want)
96	}
97}
98
99func TestAmbiguousRevision(t *testing.T) {
100	gt := newGitTest(t)
101	defer gt.done()
102	gt.work(t)
103
104	t.Logf("creating file paths that conflict with revision parameters")
105	mkdir(t, gt.client+"/origin")
106	write(t, gt.client+"/origin/main..work", "Uh-Oh! SpaghettiOs", 0644)
107	mkdir(t, gt.client+"/work..origin")
108	write(t, gt.client+"/work..origin/main", "Be sure to drink your Ovaltine", 0644)
109
110	b := CurrentBranch()
111	b.Submitted("I123456789")
112}
113
114func TestBranchpoint(t *testing.T) {
115	gt := newGitTest(t)
116	defer gt.done()
117
118	// Get hash corresponding to checkout (known to server).
119	hash := strings.TrimSpace(trun(t, gt.client, "git", "rev-parse", "HEAD"))
120
121	// Any work we do after this point should find hash as branchpoint.
122	for i := 0; i < 4; i++ {
123		testMain(t, "branchpoint")
124		t.Logf("numCommits=%d", i)
125		testPrintedStdout(t, hash)
126		testNoStderr(t)
127
128		gt.work(t)
129	}
130}
131
132func TestRebaseWork(t *testing.T) {
133	gt := newGitTest(t)
134	defer gt.done()
135
136	// Get hash corresponding to checkout (known to server).
137	// Any work we do after this point should find hash as branchpoint.
138	hash := strings.TrimSpace(trun(t, gt.client, "git", "rev-parse", "HEAD"))
139
140	testMainDied(t, "rebase-work", "-n")
141	testPrintedStderr(t, "no pending work")
142
143	write(t, gt.client+"/file", "uncommitted", 0644)
144	testMainDied(t, "rebase-work", "-n")
145	testPrintedStderr(t, "cannot rebase with uncommitted work")
146
147	gt.work(t)
148
149	for i := 0; i < 4; i++ {
150		testMain(t, "rebase-work", "-n")
151		t.Logf("numCommits=%d", i)
152		testPrintedStderr(t, "git rebase -i "+hash)
153
154		gt.work(t)
155	}
156}
157
158func TestBranchpointMerge(t *testing.T) {
159	gt := newGitTest(t)
160	defer gt.done()
161
162	// commit more work on main
163	write(t, gt.server+"/file", "more work", 0644)
164	trun(t, gt.server, "git", "commit", "-m", "work", "file")
165
166	// update client
167	trun(t, gt.client, "git", "checkout", "main")
168	trun(t, gt.client, "git", "pull")
169
170	hash := strings.TrimSpace(trun(t, gt.client, "git", "rev-parse", "HEAD"))
171
172	// Merge dev.branch but delete the codereview.cfg that comes in,
173	// or else we'll think we are on the wrong branch.
174	trun(t, gt.client, "git", "merge", "-m", "merge", "origin/dev.branch")
175	trun(t, gt.client, "git", "rm", "codereview.cfg")
176	trun(t, gt.client, "git", "commit", "-m", "rm codereview.cfg")
177
178	// check branchpoint is old head (despite this commit having two parents)
179	bp := CurrentBranch().Branchpoint()
180	if bp != hash {
181		t.Logf("branches:\n%s", trun(t, gt.client, "git", "branch", "-a", "-v"))
182		t.Logf("log:\n%s", trun(t, gt.client, "git", "log", "--graph", "--decorate"))
183		t.Logf("log origin/main..HEAD:\n%s", trun(t, gt.client, "git", "log", "origin/main..HEAD"))
184		t.Fatalf("branchpoint=%q, want %q", bp, hash)
185	}
186}
187