Skip to content

Commit c3bdc81

Browse files
authored
Merge pull request #380 from jfontan/improvement/test-filesystem-errors
Test filesystem errors
2 parents 09bc815 + e822b37 commit c3bdc81

File tree

3 files changed

+262
-2
lines changed

3 files changed

+262
-2
lines changed

files.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -249,6 +249,8 @@ func (i *filesIter) Next() (sql.Row, error) {
249249
i.files = nil
250250
continue
251251
}
252+
253+
return nil, err
252254
}
253255

254256
if !i.shouldVisitFile(f) {

fs_error_test.go

Lines changed: 241 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,241 @@
1+
package gitbase
2+
3+
import (
4+
"context"
5+
"fmt"
6+
"os"
7+
"path/filepath"
8+
"testing"
9+
"time"
10+
11+
"github.com/sirupsen/logrus"
12+
"github.com/stretchr/testify/require"
13+
billy "gopkg.in/src-d/go-billy.v4"
14+
fixtures "gopkg.in/src-d/go-git-fixtures.v3"
15+
git "gopkg.in/src-d/go-git.v4"
16+
"gopkg.in/src-d/go-git.v4/storage/filesystem"
17+
"gopkg.in/src-d/go-mysql-server.v0/sql"
18+
)
19+
20+
func TestFSErrorTables(t *testing.T) {
21+
logrus.SetLevel(logrus.FatalLevel)
22+
23+
tests := []struct {
24+
table string
25+
rows int
26+
}{
27+
{BlobsTableName, 14},
28+
{CommitBlobsTableName, 88},
29+
{CommitFilesTableName, 88},
30+
{CommitTreesTableName, 40},
31+
{CommitsTableName, 9},
32+
{FilesTableName, 82},
33+
{RefCommitsTableName, 64},
34+
{ReferencesTableName, 8},
35+
{RepositoriesTableName, 3},
36+
{TreeEntriesTableName, 45},
37+
}
38+
39+
for _, test := range tests {
40+
t.Run(test.table, func(t *testing.T) {
41+
testTable(t, test.table, test.rows)
42+
})
43+
}
44+
}
45+
46+
// setupErrorRepos creates a pool with three repos. One with read error in
47+
// packfile, another with an index file missing (and ghost packfile) and
48+
// finally a correct repository.
49+
func setupErrorRepos(t *testing.T) (*sql.Context, CleanupFunc) {
50+
require := require.New(t)
51+
52+
t.Helper()
53+
54+
require.NoError(fixtures.Init())
55+
56+
fixture := fixtures.ByTag("worktree").One()
57+
baseFS := fixture.Worktree()
58+
59+
pool := NewRepositoryPool()
60+
61+
repo, err := brokenRepo(brokenPackfile, baseFS)
62+
require.NoError(err)
63+
pool.AddInitialized("packfile", repo)
64+
65+
repo, err = brokenRepo(brokenIndex, baseFS)
66+
require.NoError(err)
67+
pool.AddInitialized("index", repo)
68+
69+
repo, err = brokenRepo(0, baseFS)
70+
require.NoError(err)
71+
pool.AddInitialized("ok", repo)
72+
73+
session := NewSession(pool, WithSkipGitErrors(true))
74+
ctx := sql.NewContext(context.TODO(), sql.WithSession(session))
75+
76+
cleanup := func() {
77+
t.Helper()
78+
require.NoError(fixtures.Clean())
79+
}
80+
81+
return ctx, cleanup
82+
}
83+
84+
func brokenRepo(
85+
brokenType brokenType,
86+
fs billy.Filesystem,
87+
) (*git.Repository, error) {
88+
dotFS, err := fs.Chroot(".git")
89+
if err != nil {
90+
return nil, err
91+
}
92+
93+
var brokenFS billy.Filesystem
94+
if brokenType == 0 {
95+
brokenFS = dotFS
96+
} else {
97+
brokenFS = NewBrokenFS(brokenType, dotFS)
98+
}
99+
100+
storage, err := filesystem.NewStorage(brokenFS)
101+
if err != nil {
102+
return nil, err
103+
}
104+
105+
return git.Open(storage, fs)
106+
}
107+
108+
func testTable(t *testing.T, tableName string, number int) {
109+
require := require.New(t)
110+
111+
ctx, cleanup := setupErrorRepos(t)
112+
defer cleanup()
113+
114+
table := getTable(require, tableName)
115+
rows, err := sql.NodeToRows(ctx, table)
116+
require.NoError(err)
117+
118+
if len(rows) < number {
119+
t.Errorf("table %s returned %v rows and it should be at least %v",
120+
tableName, len(rows), number)
121+
t.FailNow()
122+
}
123+
}
124+
125+
type brokenType uint64
126+
127+
const (
128+
// packfile has read errors
129+
brokenPackfile brokenType = 1 << iota
130+
// there's no index for one packfile
131+
brokenIndex
132+
133+
packFileGlob = "objects/pack/pack-*.pack"
134+
packBrokenName = "pack-ffffffffffffffffffffffffffffffffffffffff.pack"
135+
)
136+
137+
func NewBrokenFS(b brokenType, fs billy.Filesystem) billy.Filesystem {
138+
return &BrokenFS{
139+
Filesystem: fs,
140+
brokenType: b,
141+
}
142+
}
143+
144+
type BrokenFS struct {
145+
billy.Filesystem
146+
brokenType brokenType
147+
}
148+
149+
func (fs *BrokenFS) Open(filename string) (billy.File, error) {
150+
return fs.OpenFile(filename, os.O_RDONLY, 0)
151+
}
152+
153+
func (fs *BrokenFS) OpenFile(
154+
name string,
155+
flag int,
156+
perm os.FileMode,
157+
) (billy.File, error) {
158+
file, err := fs.Filesystem.OpenFile(name, flag, perm)
159+
if err != nil {
160+
return nil, err
161+
}
162+
163+
if fs.brokenType&brokenPackfile == 0 {
164+
return file, err
165+
}
166+
167+
match, err := filepath.Match(packFileGlob, name)
168+
if err != nil {
169+
return nil, err
170+
}
171+
172+
if !match {
173+
return file, nil
174+
}
175+
176+
return &BrokenFile{
177+
File: file,
178+
}, nil
179+
}
180+
181+
func (fs *BrokenFS) ReadDir(path string) ([]os.FileInfo, error) {
182+
files, err := fs.Filesystem.ReadDir(path)
183+
if err != nil {
184+
return nil, err
185+
}
186+
187+
if fs.brokenType&brokenIndex != 0 {
188+
dummyPack := &brokenFileInfo{packBrokenName}
189+
files = append(files, dummyPack)
190+
}
191+
192+
return files, err
193+
}
194+
195+
type BrokenFile struct {
196+
billy.File
197+
count int
198+
}
199+
200+
func (fs *BrokenFile) Read(p []byte) (int, error) {
201+
_, err := fs.Seek(0, os.SEEK_CUR)
202+
if err != nil {
203+
return 0, err
204+
}
205+
206+
fs.count++
207+
208+
if fs.count == 10 {
209+
return 0, fmt.Errorf("could not read from broken file")
210+
}
211+
212+
return fs.File.Read(p)
213+
}
214+
215+
type brokenFileInfo struct {
216+
name string
217+
}
218+
219+
func (b *brokenFileInfo) Name() string {
220+
return b.name
221+
}
222+
223+
func (b *brokenFileInfo) Size() int64 {
224+
return 1024 * 1024
225+
}
226+
227+
func (b *brokenFileInfo) Mode() os.FileMode {
228+
return 0600
229+
}
230+
231+
func (b *brokenFileInfo) ModTime() time.Time {
232+
return time.Now()
233+
}
234+
235+
func (b *brokenFileInfo) IsDir() bool {
236+
return false
237+
}
238+
239+
func (b *brokenFileInfo) Sys() interface{} {
240+
return nil
241+
}

repository_pool.go

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -82,13 +82,15 @@ func NewSivaRepositoryFromPath(id, path string) (*Repository, error) {
8282
type repository struct {
8383
kind repoKind
8484
path string
85+
repo *git.Repository
8586
}
8687

8788
type repoKind byte
8889

8990
const (
9091
gitRepo repoKind = iota
9192
sivaRepo
93+
initializedrepo
9294
)
9395

9496
// RepositoryPool holds a pool git repository paths and
@@ -105,14 +107,18 @@ func NewRepositoryPool() *RepositoryPool {
105107
}
106108
}
107109

108-
// Add inserts a new repository in the pool
110+
// Add inserts a new repository in the pool.
109111
func (p *RepositoryPool) Add(id, path string, kind repoKind) error {
112+
return p.add(id, repository{kind, path, nil})
113+
}
114+
115+
func (p *RepositoryPool) add(id string, repo repository) error {
110116
if r, ok := p.repositories[id]; ok {
111117
return errRepoAlreadyRegistered.New(r.path)
112118
}
113119

114120
p.idOrder = append(p.idOrder, id)
115-
p.repositories[id] = repository{kind, path}
121+
p.repositories[id] = repo
116122

117123
return nil
118124
}
@@ -213,6 +219,11 @@ func (p *RepositoryPool) addSivaFile(root, path string, f os.FileInfo) {
213219
}
214220
}
215221

222+
// AddInitialized inserts an already initialized repository to the pool.
223+
func (p *RepositoryPool) AddInitialized(id string, repo *git.Repository) error {
224+
return p.add(id, repository{initializedrepo, "", repo})
225+
}
226+
216227
// GetPos retrieves a repository at a given position. If the position is
217228
// out of bounds it returns io.EOF.
218229
func (p *RepositoryPool) GetPos(pos int) (*Repository, error) {
@@ -245,6 +256,8 @@ func (p *RepositoryPool) GetRepo(id string) (*Repository, error) {
245256
repo, err = NewRepositoryFromPath(id, r.path)
246257
case sivaRepo:
247258
repo, err = NewSivaRepositoryFromPath(id, r.path)
259+
case initializedrepo:
260+
repo = NewRepository(id, r.repo)
248261
default:
249262
err = errInvalidRepoKind.New(r.kind)
250263
}
@@ -369,6 +382,10 @@ func (i *rowRepoIter) Next() (sql.Row, error) {
369382

370383
i.currRepoIter, err = i.iter.NewIterator(repo)
371384
if err != nil {
385+
if i.session.SkipGitErrors {
386+
continue
387+
}
388+
372389
return nil, err
373390
}
374391
}

0 commit comments

Comments
 (0)