Skip to content

Commit 60920b2

Browse files
committed
Add tests for fstest directory comparison
Cover CheckDirectoryEqual, CheckDirectoryEqualWithApplier, and buildResources with tests for: identical directories, content differences, extra/missing files, symlinks, symlink target differences, hardlinks, and permission differences. Signed-off-by: Derek McGowan <derek@mcg.dev>
1 parent 56e64e4 commit 60920b2

3 files changed

Lines changed: 279 additions & 2 deletions

File tree

fs/fstest/compare_test.go

Lines changed: 273 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,273 @@
1+
/*
2+
Copyright The containerd Authors.
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
*/
16+
17+
package fstest
18+
19+
import (
20+
"os"
21+
"runtime"
22+
"testing"
23+
)
24+
25+
func TestCheckDirectoryEqualBasic(t *testing.T) {
26+
d1 := t.TempDir()
27+
d2 := t.TempDir()
28+
29+
a := Apply(
30+
CreateDir("/d", 0o755),
31+
CreateFile("/d/f1", []byte("hello"), 0o644),
32+
CreateFile("/f2", []byte("world"), 0o600),
33+
)
34+
if err := a.Apply(d1); err != nil {
35+
t.Fatal(err)
36+
}
37+
if err := a.Apply(d2); err != nil {
38+
t.Fatal(err)
39+
}
40+
41+
if err := CheckDirectoryEqual(d1, d2); err != nil {
42+
t.Fatalf("identical directories should be equal: %v", err)
43+
}
44+
}
45+
46+
func TestCheckDirectoryEqualDetectsDifference(t *testing.T) {
47+
d1 := t.TempDir()
48+
d2 := t.TempDir()
49+
50+
a1 := Apply(
51+
CreateFile("/f", []byte("aaa"), 0o644),
52+
)
53+
a2 := Apply(
54+
CreateFile("/f", []byte("bbb"), 0o644),
55+
)
56+
if err := a1.Apply(d1); err != nil {
57+
t.Fatal(err)
58+
}
59+
if err := a2.Apply(d2); err != nil {
60+
t.Fatal(err)
61+
}
62+
63+
if err := CheckDirectoryEqual(d1, d2); err == nil {
64+
t.Fatal("directories with different content should not be equal")
65+
}
66+
}
67+
68+
func TestCheckDirectoryEqualExtraFile(t *testing.T) {
69+
d1 := t.TempDir()
70+
d2 := t.TempDir()
71+
72+
a := Apply(
73+
CreateFile("/f1", []byte("hello"), 0o644),
74+
)
75+
if err := a.Apply(d1); err != nil {
76+
t.Fatal(err)
77+
}
78+
if err := a.Apply(d2); err != nil {
79+
t.Fatal(err)
80+
}
81+
// Extra file in d2
82+
if err := CreateFile("/f2", []byte("extra"), 0o644).Apply(d2); err != nil {
83+
t.Fatal(err)
84+
}
85+
86+
if err := CheckDirectoryEqual(d1, d2); err == nil {
87+
t.Fatal("directory with extra file should not be equal")
88+
}
89+
}
90+
91+
func TestCheckDirectoryEqualMissingFile(t *testing.T) {
92+
d1 := t.TempDir()
93+
d2 := t.TempDir()
94+
95+
a1 := Apply(
96+
CreateFile("/f1", []byte("hello"), 0o644),
97+
CreateFile("/f2", []byte("world"), 0o644),
98+
)
99+
a2 := Apply(
100+
CreateFile("/f1", []byte("hello"), 0o644),
101+
)
102+
if err := a1.Apply(d1); err != nil {
103+
t.Fatal(err)
104+
}
105+
if err := a2.Apply(d2); err != nil {
106+
t.Fatal(err)
107+
}
108+
109+
if err := CheckDirectoryEqual(d1, d2); err == nil {
110+
t.Fatal("directory with missing file should not be equal")
111+
}
112+
}
113+
114+
func TestCheckDirectoryEqualSymlinks(t *testing.T) {
115+
d1 := t.TempDir()
116+
d2 := t.TempDir()
117+
118+
a := Apply(
119+
CreateFile("/target", []byte("data"), 0o644),
120+
Symlink("target", "/link"),
121+
)
122+
if err := a.Apply(d1); err != nil {
123+
t.Fatal(err)
124+
}
125+
if err := a.Apply(d2); err != nil {
126+
t.Fatal(err)
127+
}
128+
129+
if err := CheckDirectoryEqual(d1, d2); err != nil {
130+
t.Fatalf("identical symlink directories should be equal: %v", err)
131+
}
132+
}
133+
134+
func TestCheckDirectoryEqualSymlinkDifference(t *testing.T) {
135+
d1 := t.TempDir()
136+
d2 := t.TempDir()
137+
138+
a1 := Apply(
139+
CreateFile("/target1", []byte("data"), 0o644),
140+
CreateFile("/target2", []byte("data"), 0o644),
141+
Symlink("target1", "/link"),
142+
)
143+
a2 := Apply(
144+
CreateFile("/target1", []byte("data"), 0o644),
145+
CreateFile("/target2", []byte("data"), 0o644),
146+
Symlink("target2", "/link"),
147+
)
148+
if err := a1.Apply(d1); err != nil {
149+
t.Fatal(err)
150+
}
151+
if err := a2.Apply(d2); err != nil {
152+
t.Fatal(err)
153+
}
154+
155+
if err := CheckDirectoryEqual(d1, d2); err == nil {
156+
t.Fatal("directories with different symlink targets should not be equal")
157+
}
158+
}
159+
160+
func TestCheckDirectoryEqualHardlinks(t *testing.T) {
161+
d1 := t.TempDir()
162+
d2 := t.TempDir()
163+
164+
a := Apply(
165+
CreateFile("/f1", []byte("hello"), 0o644),
166+
Link("/f1", "/f2"),
167+
)
168+
if err := a.Apply(d1); err != nil {
169+
t.Fatal(err)
170+
}
171+
if err := a.Apply(d2); err != nil {
172+
t.Fatal(err)
173+
}
174+
175+
if err := CheckDirectoryEqual(d1, d2); err != nil {
176+
t.Fatalf("identical hardlink directories should be equal: %v", err)
177+
}
178+
}
179+
180+
func TestCheckDirectoryEqualPermissionDifference(t *testing.T) {
181+
if runtime.GOOS == "windows" {
182+
t.Skip("Windows does not support Unix-style file permissions")
183+
}
184+
185+
d1 := t.TempDir()
186+
d2 := t.TempDir()
187+
188+
if err := CreateFile("/f", []byte("hello"), 0o644).Apply(d1); err != nil {
189+
t.Fatal(err)
190+
}
191+
if err := CreateFile("/f", []byte("hello"), 0o600).Apply(d2); err != nil {
192+
t.Fatal(err)
193+
}
194+
195+
if err := CheckDirectoryEqual(d1, d2); err == nil {
196+
t.Fatal("directories with different permissions should not be equal")
197+
}
198+
}
199+
200+
func TestCheckDirectoryEqualWithApplier(t *testing.T) {
201+
d := t.TempDir()
202+
203+
a := Apply(
204+
CreateDir("/d", 0o755),
205+
CreateFile("/d/f", []byte("content"), 0o644),
206+
)
207+
if err := a.Apply(d); err != nil {
208+
t.Fatal(err)
209+
}
210+
211+
if err := CheckDirectoryEqualWithApplier(d, a); err != nil {
212+
t.Fatalf("directory should equal its applier: %v", err)
213+
}
214+
}
215+
216+
func TestBuildResources(t *testing.T) {
217+
d := t.TempDir()
218+
219+
a := Apply(
220+
CreateDir("/a", 0o755),
221+
CreateFile("/a/f1", []byte("one"), 0o644),
222+
CreateFile("/b", []byte("two"), 0o600),
223+
Symlink("b", "/c"),
224+
)
225+
if err := a.Apply(d); err != nil {
226+
t.Fatal(err)
227+
}
228+
229+
resources, err := buildResources(d)
230+
if err != nil {
231+
t.Fatal(err)
232+
}
233+
234+
// Should have 4 entries: /a, /a/f1, /b, /c
235+
if len(resources) != 4 {
236+
t.Fatalf("expected 4 resources, got %d", len(resources))
237+
}
238+
239+
// Verify sorted order
240+
for i := 1; i < len(resources); i++ {
241+
if resources[i].path <= resources[i-1].path {
242+
t.Fatalf("resources not sorted: %q <= %q", resources[i].path, resources[i-1].path)
243+
}
244+
}
245+
246+
// Verify types
247+
for _, r := range resources {
248+
switch r.path {
249+
case "/a":
250+
if !r.mode.IsDir() {
251+
t.Errorf("/a should be directory, got %v", r.mode)
252+
}
253+
case "/a/f1":
254+
if !r.mode.IsRegular() {
255+
t.Errorf("/a/f1 should be regular file, got %v", r.mode)
256+
}
257+
if r.size != 3 {
258+
t.Errorf("/a/f1 should have size 3, got %d", r.size)
259+
}
260+
case "/b":
261+
if !r.mode.IsRegular() {
262+
t.Errorf("/b should be regular file, got %v", r.mode)
263+
}
264+
case "/c":
265+
if r.mode&os.ModeSymlink == 0 {
266+
t.Errorf("/c should be symlink, got %v", r.mode)
267+
}
268+
if r.target != "b" {
269+
t.Errorf("/c target should be 'b', got %q", r.target)
270+
}
271+
}
272+
}
273+
}

fs/fstest/mkfs_linux.go

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,8 +24,10 @@ import (
2424
"github.com/containerd/continuity/testutil/loopback"
2525
)
2626

27+
// WithMkfs creates a loopback device, formats it with the given mkfs command,
28+
// mounts it, and runs f with TMPDIR set to the mount point.
29+
// The caller should ensure root access before calling this function.
2730
func WithMkfs(t *testing.T, f func(), mkfs ...string) {
28-
testutil.RequiresRoot(t)
2931
mnt := t.TempDir()
3032
loop, err := loopback.New(100 << 20) // 100 MB
3133
if err != nil {

fs/fstest/mkfs_others.go

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,9 @@
1818

1919
package fstest
2020

21-
import "testing"
21+
import (
22+
"testing"
23+
)
2224

2325
func WithMkfs(t *testing.T, f func(), mkfs ...string) {
2426
t.Fatal("WithMkfs requires Linux")

0 commit comments

Comments
 (0)