1package git
2
3import (
4 "fmt"
5 "os"
6 "testing"
7 "time"
8
9 "github.com/go-git/go-git/v5/plumbing"
10 "github.com/go-git/go-git/v5/plumbing/cache"
11 "github.com/go-git/go-git/v5/plumbing/format/packfile"
12 "github.com/go-git/go-git/v5/plumbing/object"
13 "github.com/go-git/go-git/v5/storage/filesystem"
14 "github.com/go-git/go-git/v5/storage/memory"
15 "github.com/stretchr/testify/assert"
16 "github.com/stretchr/testify/suite"
17
18 "github.com/go-git/go-billy/v5"
19 "github.com/go-git/go-billy/v5/memfs"
20 "github.com/go-git/go-billy/v5/osfs"
21 "github.com/go-git/go-billy/v5/util"
22 fixtures "github.com/go-git/go-git-fixtures/v4"
23)
24
25type BaseFixtureSuite struct {
26 fixtures.Suite
27}
28
29type BaseSuite struct {
30 suite.Suite
31 BaseFixtureSuite
32 Repository *Repository
33
34 cache map[string]*Repository
35}
36
37func (s *BaseSuite) SetupSuite() {
38 s.buildBasicRepository()
39
40 s.cache = make(map[string]*Repository)
41}
42
43func (s *BaseSuite) buildBasicRepository() {
44 f := fixtures.Basic().One()
45 s.Repository = s.NewRepository(f)
46}
47
48// NewRepository returns a new repository using the .git folder, if the fixture
49// is tagged as worktree the filesystem from fixture is used, otherwise a new
50// memfs filesystem is used as worktree.
51func (s *BaseSuite) NewRepository(f *fixtures.Fixture) *Repository {
52 var worktree, dotgit billy.Filesystem
53 if f.Is("worktree") {
54 r, err := PlainOpen(f.Worktree().Root())
55 if err != nil {
56 panic(err)
57 }
58
59 return r
60 }
61
62 dotgit = f.DotGit()
63 worktree = memfs.New()
64
65 st := filesystem.NewStorage(dotgit, cache.NewObjectLRUDefault())
66
67 r, err := Open(st, worktree)
68 if err != nil {
69 panic(err)
70 }
71
72 return r
73}
74
75// NewRepositoryWithEmptyWorktree returns a new repository using the .git folder
76// from the fixture but without a empty memfs worktree, the index and the
77// modules are deleted from the .git folder.
78func NewRepositoryWithEmptyWorktree(f *fixtures.Fixture) *Repository {
79 dotgit := f.DotGit()
80 err := dotgit.Remove("index")
81 if err != nil {
82 panic(err)
83 }
84
85 err = util.RemoveAll(dotgit, "modules")
86 if err != nil {
87 panic(err)
88 }
89
90 worktree := memfs.New()
91
92 st := filesystem.NewStorage(dotgit, cache.NewObjectLRUDefault())
93
94 r, err := Open(st, worktree)
95 if err != nil {
96 panic(err)
97 }
98
99 return r
100}
101
102func (s *BaseSuite) NewRepositoryFromPackfile(f *fixtures.Fixture) *Repository {
103 h := f.PackfileHash
104 if r, ok := s.cache[h]; ok {
105 return r
106 }
107
108 storer := memory.NewStorage()
109 p := f.Packfile()
110 defer func() { _ = p.Close() }()
111
112 if err := packfile.UpdateObjectStorage(storer, p); err != nil {
113 panic(err)
114 }
115
116 err := storer.SetReference(plumbing.NewHashReference(plumbing.HEAD, plumbing.NewHash(f.Head)))
117 if err != nil {
118 panic(err)
119 }
120
121 r, err := Open(storer, memfs.New())
122 if err != nil {
123 panic(err)
124 }
125
126 s.cache[h] = r
127 return r
128}
129
130func (s *BaseSuite) GetBasicLocalRepositoryURL() string {
131 fixture := fixtures.Basic().One()
132 return s.GetLocalRepositoryURL(fixture)
133}
134
135func (s *BaseSuite) GetLocalRepositoryURL(f *fixtures.Fixture) string {
136 return f.DotGit().Root()
137}
138
139func (s *BaseSuite) TemporalHomeDir() (path string, clean func()) {
140 home, err := os.UserHomeDir()
141 if err != nil {
142 panic(err)
143 }
144
145 fs := osfs.New(home)
146 relPath, err := util.TempDir(fs, "", "")
147 if err != nil {
148 panic(err)
149 }
150
151 path = fs.Join(fs.Root(), relPath)
152 clean = func() {
153 _ = util.RemoveAll(fs, relPath)
154 }
155
156 return
157}
158
159func (s *BaseSuite) TemporalFilesystem() (fs billy.Filesystem) {
160 tmpDir, err := os.MkdirTemp("", "")
161 if err != nil {
162 panic(err)
163 }
164 fs = osfs.New(tmpDir)
165 path, err := util.TempDir(fs, "", "")
166 if err != nil {
167 panic(err)
168 }
169
170 fs, err = fs.Chroot(path)
171 if err != nil {
172 panic(err)
173 }
174
175 return
176}
177
178type SuiteCommon struct {
179 suite.Suite
180}
181
182func TestSuiteCommon(t *testing.T) {
183 suite.Run(t, new(SuiteCommon))
184}
185
186var countLinesTests = [...]struct {
187 i string // the string we want to count lines from
188 e int // the expected number of lines in i
189}{
190 {"", 0},
191 {"a", 1},
192 {"a\n", 1},
193 {"a\nb", 2},
194 {"a\nb\n", 2},
195 {"a\nb\nc", 3},
196 {"a\nb\nc\n", 3},
197 {"a\n\n\nb\n", 4},
198 {"first line\n\tsecond line\nthird line\n", 3},
199}
200
201func (s *SuiteCommon) TestCountLines() {
202 for i, t := range countLinesTests {
203 o := countLines(t.i)
204 s.Equal(t.e, o, fmt.Sprintf("subtest %d, input=%q", i, t.i))
205 }
206}
207
208func AssertReferences(t *testing.T, r *Repository, expected map[string]string) {
209 for name, target := range expected {
210 expected := plumbing.NewReferenceFromStrings(name, target)
211
212 obtained, err := r.Reference(expected.Name(), true)
213 assert.NoError(t, err)
214
215 assert.Equal(t, expected, obtained)
216 }
217}
218
219func AssertReferencesMissing(t *testing.T, r *Repository, expected []string) {
220 for _, name := range expected {
221 _, err := r.Reference(plumbing.ReferenceName(name), false)
222 assert.Error(t, err)
223 assert.ErrorIs(t, err, plumbing.ErrReferenceNotFound)
224 }
225}
226
227func CommitNewFile(t *testing.T, repo *Repository, fileName string) plumbing.Hash {
228 wt, err := repo.Worktree()
229 assert.NoError(t, err)
230
231 fd, err := wt.Filesystem.Create(fileName)
232 assert.NoError(t, err)
233
234 _, err = fd.Write([]byte("# test file"))
235 assert.NoError(t, err)
236
237 err = fd.Close()
238 assert.NoError(t, err)
239
240 _, err = wt.Add(fileName)
241 assert.NoError(t, err)
242
243 sha, err := wt.Commit("test commit", &CommitOptions{
244 Author: &object.Signature{
245 Name: "test",
246 Email: "test@example.com",
247 When: time.Now(),
248 },
249 Committer: &object.Signature{
250 Name: "test",
251 Email: "test@example.com",
252 When: time.Now(),
253 },
254 })
255 assert.NoError(t, err)
256
257 return sha
258}