-
Notifications
You must be signed in to change notification settings - Fork 5
Expand file tree
/
Copy pathfuse_test.go
More file actions
193 lines (176 loc) · 4.54 KB
/
fuse_test.go
File metadata and controls
193 lines (176 loc) · 4.54 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
// Copyright (c) Tailscale Inc & AUTHORS
// SPDX-License-Identifier: BSD-3-Clause
// no FUSE support on Windows
//go:build !windows
package gomodfs
import (
"flag"
"fmt"
"os"
"os/exec"
"path/filepath"
"runtime"
"testing"
"time"
"github.com/hanwen/go-fuse/v2/fs"
"github.com/hanwen/go-fuse/v2/fuse"
"github.com/tailscale/gomodfs/store/gitstore"
)
var debugFUSE = flag.Bool("debug-fuse", false, "verbose FUSE debugging")
var hitNetwork = flag.Bool("run-network-tests", os.Getenv("CI") == "true", "run network tests")
func TestFilesystem(t *testing.T) {
if !*hitNetwork {
t.Skip("Skipping network tests; set --run-network-tests to run them")
}
type testEnv struct {
*testing.T
mnt string
}
type checker func(t testEnv) error
pathContents := func(path, want string) checker {
return func(t testEnv) error {
got, err := os.ReadFile(filepath.Join(t.mnt, path))
if err != nil {
return err
}
if string(got) != want {
return fmt.Errorf("unexpected content for %q: got %q, want %q", path, got, want)
}
return nil
}
}
fileSize := func(path string, wantSize int64) checker {
return func(t testEnv) error {
fi, err := os.Stat(filepath.Join(t.mnt, path))
if err != nil {
return err
}
if fi.IsDir() {
return fmt.Errorf("expected file %q to be a regular file, but it is a directory", path)
}
if fi.Size() != wantSize {
return fmt.Errorf("unexpected size for %q: got %d, want %d", path, fi.Size(), wantSize)
}
return nil
}
}
tests := []struct {
name string
checks []checker
}{
{
name: "hit-cache-dir-first",
checks: []checker{
pathContents(
"cache/download/go4.org/mem/@v/v0.0.0-20240501181205-ae6ca9944745.mod",
"module go4.org/mem\n\ngo 1.14\n",
),
fileSize(
"go4.org/mem@v0.0.0-20240501181205-ae6ca9944745/mem.go",
12195,
),
},
},
{
name: "hit-file-dir-first",
checks: []checker{
fileSize(
"go4.org/mem@v0.0.0-20240501181205-ae6ca9944745/mem.go",
12195,
),
pathContents(
"cache/download/go4.org/mem/@v/v0.0.0-20240501181205-ae6ca9944745.mod",
"module go4.org/mem\n\ngo 1.14\n",
),
},
},
{
name: "module-without-slash",
checks: []checker{
fileSize(
"gocloud.dev@v0.20.0/blob/fileblob/example_test.go",
2137,
),
},
},
{
// This test is a regression test for a bug where this repo contains
// a tree with a "testing" directory and "testing.md" file. Per
// normal sorting rules, "testing" should come first in the tree
// object, but git has an undocumented rule that tree entries
// pointing to trees have an implicit final "/" at the end of their
// name before sorting happens. Without that, git fsck will fail
// and git index-pack --strict won't accept the pack file.
name: "tree-not-sorted-error",
checks: []checker{
fileSize(
"cache/download/google.golang.org/api/@v/v0.57.0.mod",
661,
),
},
},
{
name: "file-with-spaces", // whoops
checks: []checker{
fileSize(
"github.com/docker/cli@v25.0.0+incompatible/cli/command/image/testdata/remove-command-success.Image Deleted and Untagged.golden",
33,
),
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
goModCacheDir := t.TempDir()
t.Logf("mount: %v", goModCacheDir)
gitDir := t.TempDir()
cmd := exec.Command("git", "init", gitDir)
cmd.Dir = gitDir
if err := cmd.Run(); err != nil {
t.Fatalf("Failed to init git repo: %v", err)
}
store := &gitstore.Storage{GitRepo: gitDir}
mfs := &FS{
Store: store,
Verbose: true,
}
root := &moduleNameNode{
fs: mfs,
}
server, err := fs.Mount(goModCacheDir, root, &fs.Options{
MountOptions: fuse.MountOptions{Debug: *debugFUSE},
})
if err != nil {
if runtime.GOOS == "darwin" && os.Getenv("CI") == "true" {
t.Skipf("Mount failed on GitHub macOS runners (no FUSE kernel module); skipping test. Got error: %v", err)
}
t.Fatalf("Mount: %v", err)
}
defer func() {
err := server.Unmount()
if err != nil {
t.Errorf("Unmount error: %v", err)
}
if t.Failed() {
t.Logf("Sleeping for 5m... to allow inspection of %s", goModCacheDir)
time.Sleep(5 * time.Minute)
}
}()
didWait := make(chan struct{})
go func() {
defer close(didWait)
server.Wait()
}()
for i, check := range tt.checks {
env := testEnv{
T: t,
mnt: goModCacheDir,
// TODO: stats on HTTP faults
}
if err := check(env); err != nil {
t.Errorf("check[%d]: %v", i, err)
}
}
})
}
}