1// SPDX-License-Identifier: MIT
2
3package git
4
5import (
6 "context"
7 "fmt"
8 "slices"
9 "testing"
10)
11
12// lastOracle runs git log -n 1 for each name.
13func (w *wfix) lastOracle(tip int, dir string, names []string) []ObjectID {
14 w.t.Helper()
15 out := make([]ObjectID, len(names))
16 for i, n := range names {
17 p := n
18 if dir != "" {
19 p = dir + "/" + n
20 }
21 if l := lines(w.git("", "-c", "core.commitGraph=false", "log", "-n", "1", "--format=%H", w.ids[tip], "--", p)); len(l) > 0 {
22 out[i] = mustID(w.t, l[0])
23 }
24 }
25 return out
26}
27
28var lastDirs = map[string][]string{
29 "": {"a", "b", "m", "d", "q", "nope", "gone", "readded", "x", "a"},
30 "d": {"x", "y", "e", "nope"},
31 "d/e": {"z", "w", "nope"},
32 "d/": {"x", "e"},
33 "./d/e": {"z"},
34 "nodir": {"a", "x"},
35}
36
37func checkLast(t *testing.T, w *wfix, tips []int) {
38 t.Helper()
39 type key struct {
40 tip int
41 dir string
42 }
43 want := map[key][]ObjectID{}
44 for _, tip := range tips {
45 for dir, names := range lastDirs {
46 want[key{tip, dir}] = w.lastOracle(tip, dir, names)
47 }
48 }
49 for _, g := range wgraphs {
50 t.Run(g.name, func(t *testing.T) {
51 w.setGraph(g)
52 r, err := Open(w.dir)
53 if err != nil {
54 t.Fatal(err)
55 }
56 defer func() { _ = r.Close() }()
57 for _, tip := range tips {
58 for dir, names := range lastDirs {
59 got, err := r.LastCommits(context.Background(), mustID(t, w.ids[tip]), dir, names)
60 if err != nil {
61 t.Fatal(err)
62 }
63 if exp := want[key{tip, dir}]; !slices.Equal(got, exp) {
64 t.Fatalf("tip %d dir %q:\n got %v\nwant %v", tip, dir, got, exp)
65 }
66 }
67 }
68 })
69 }
70}
71
72func TestLastCommitsRandom(t *testing.T) {
73 requireGit(t)
74 seeds := []int64{1, 2, 3, 4, 5, 6, 7, 8}
75 if testing.Short() {
76 seeds = seeds[:2]
77 }
78 for _, seed := range seeds {
79 t.Run(fmt.Sprintf("seed%d", seed), func(t *testing.T) {
80 cs := randomHistory(seed, 140)
81 checkLast(t, buildWFix(t, cs), lastTips(len(cs)))
82 })
83 }
84}
85
86func TestLastCommitsHandcrafted(t *testing.T) {
87 requireGit(t)
88 for name, cs := range handHistories() {
89 t.Run(name, func(t *testing.T) {
90 tips := make([]int, len(cs))
91 for i := range tips {
92 tips[i] = i
93 }
94 checkLast(t, buildWFix(t, cs), tips)
95 })
96 }
97}
98
99func TestLastCommitSingle(t *testing.T) {
100 requireGit(t)
101 cs := randomHistory(9, 80)
102 w := buildWFix(t, cs)
103 w.setGraph(wgraphs[4])
104 r, err := Open(w.dir)
105 if err != nil {
106 t.Fatal(err)
107 }
108 defer func() { _ = r.Close() }()
109 tip := len(cs) - 1
110 for _, p := range []string{"a", "d", "d/e/z", "nope", ""} {
111 id, ok, err := r.LastCommit(context.Background(), mustID(t, w.ids[tip]), p)
112 if err != nil {
113 t.Fatal(err)
114 }
115 exp := w.gitLog(tip, 0, 1, p)
116 if ok != (len(exp) == 1) || (ok && id.String() != exp[0]) {
117 t.Errorf("%q: got %v %v, want %v", p, id, ok, exp)
118 }
119 }
120 for _, bad := range []string{"", ".", "a/b", "../x"} {
121 if _, err := r.LastCommits(context.Background(), mustID(t, w.ids[tip]), "", []string{bad}); !errorsIs(err, ErrInvalidPath) {
122 t.Errorf("name %q: err = %v", bad, err)
123 }
124 }
125 if got, err := r.LastCommits(context.Background(), mustID(t, w.ids[tip]), "", nil); err != nil || len(got) != 0 {
126 t.Errorf("no names: %v %v", got, err)
127 }
128}