Skip to content

Commit af32332

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 816c616 commit af32332

3 files changed

Lines changed: 276 additions & 2 deletions

File tree

fs/fstest/compare_test.go

Lines changed: 268 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,268 @@
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+
"testing"
22+
)
23+
24+
func TestCheckDirectoryEqualBasic(t *testing.T) {
25+
d1 := t.TempDir()
26+
d2 := t.TempDir()
27+
28+
a := Apply(
29+
CreateDir("/d", 0o755),
30+
CreateFile("/d/f1", []byte("hello"), 0o644),
31+
CreateFile("/f2", []byte("world"), 0o600),
32+
)
33+
if err := a.Apply(d1); err != nil {
34+
t.Fatal(err)
35+
}
36+
if err := a.Apply(d2); err != nil {
37+
t.Fatal(err)
38+
}
39+
40+
if err := CheckDirectoryEqual(d1, d2); err != nil {
41+
t.Fatalf("identical directories should be equal: %v", err)
42+
}
43+
}
44+
45+
func TestCheckDirectoryEqualDetectsDifference(t *testing.T) {
46+
d1 := t.TempDir()
47+
d2 := t.TempDir()
48+
49+
a1 := Apply(
50+
CreateFile("/f", []byte("aaa"), 0o644),
51+
)
52+
a2 := Apply(
53+
CreateFile("/f", []byte("bbb"), 0o644),
54+
)
55+
if err := a1.Apply(d1); err != nil {
56+
t.Fatal(err)
57+
}
58+
if err := a2.Apply(d2); err != nil {
59+
t.Fatal(err)
60+
}
61+
62+
if err := CheckDirectoryEqual(d1, d2); err == nil {
63+
t.Fatal("directories with different content should not be equal")
64+
}
65+
}
66+
67+
func TestCheckDirectoryEqualExtraFile(t *testing.T) {
68+
d1 := t.TempDir()
69+
d2 := t.TempDir()
70+
71+
a := Apply(
72+
CreateFile("/f1", []byte("hello"), 0o644),
73+
)
74+
if err := a.Apply(d1); err != nil {
75+
t.Fatal(err)
76+
}
77+
if err := a.Apply(d2); err != nil {
78+
t.Fatal(err)
79+
}
80+
// Extra file in d2
81+
if err := CreateFile("/f2", []byte("extra"), 0o644).Apply(d2); err != nil {
82+
t.Fatal(err)
83+
}
84+
85+
if err := CheckDirectoryEqual(d1, d2); err == nil {
86+
t.Fatal("directory with extra file should not be equal")
87+
}
88+
}
89+
90+
func TestCheckDirectoryEqualMissingFile(t *testing.T) {
91+
d1 := t.TempDir()
92+
d2 := t.TempDir()
93+
94+
a1 := Apply(
95+
CreateFile("/f1", []byte("hello"), 0o644),
96+
CreateFile("/f2", []byte("world"), 0o644),
97+
)
98+
a2 := Apply(
99+
CreateFile("/f1", []byte("hello"), 0o644),
100+
)
101+
if err := a1.Apply(d1); err != nil {
102+
t.Fatal(err)
103+
}
104+
if err := a2.Apply(d2); err != nil {
105+
t.Fatal(err)
106+
}
107+
108+
if err := CheckDirectoryEqual(d1, d2); err == nil {
109+
t.Fatal("directory with missing file should not be equal")
110+
}
111+
}
112+
113+
func TestCheckDirectoryEqualSymlinks(t *testing.T) {
114+
d1 := t.TempDir()
115+
d2 := t.TempDir()
116+
117+
a := Apply(
118+
CreateFile("/target", []byte("data"), 0o644),
119+
Symlink("target", "/link"),
120+
)
121+
if err := a.Apply(d1); err != nil {
122+
t.Fatal(err)
123+
}
124+
if err := a.Apply(d2); err != nil {
125+
t.Fatal(err)
126+
}
127+
128+
if err := CheckDirectoryEqual(d1, d2); err != nil {
129+
t.Fatalf("identical symlink directories should be equal: %v", err)
130+
}
131+
}
132+
133+
func TestCheckDirectoryEqualSymlinkDifference(t *testing.T) {
134+
d1 := t.TempDir()
135+
d2 := t.TempDir()
136+
137+
a1 := Apply(
138+
CreateFile("/target1", []byte("data"), 0o644),
139+
CreateFile("/target2", []byte("data"), 0o644),
140+
Symlink("target1", "/link"),
141+
)
142+
a2 := Apply(
143+
CreateFile("/target1", []byte("data"), 0o644),
144+
CreateFile("/target2", []byte("data"), 0o644),
145+
Symlink("target2", "/link"),
146+
)
147+
if err := a1.Apply(d1); err != nil {
148+
t.Fatal(err)
149+
}
150+
if err := a2.Apply(d2); err != nil {
151+
t.Fatal(err)
152+
}
153+
154+
if err := CheckDirectoryEqual(d1, d2); err == nil {
155+
t.Fatal("directories with different symlink targets should not be equal")
156+
}
157+
}
158+
159+
func TestCheckDirectoryEqualHardlinks(t *testing.T) {
160+
d1 := t.TempDir()
161+
d2 := t.TempDir()
162+
163+
a := Apply(
164+
CreateFile("/f1", []byte("hello"), 0o644),
165+
Link("/f1", "/f2"),
166+
)
167+
if err := a.Apply(d1); err != nil {
168+
t.Fatal(err)
169+
}
170+
if err := a.Apply(d2); err != nil {
171+
t.Fatal(err)
172+
}
173+
174+
if err := CheckDirectoryEqual(d1, d2); err != nil {
175+
t.Fatalf("identical hardlink directories should be equal: %v", err)
176+
}
177+
}
178+
179+
func TestCheckDirectoryEqualPermissionDifference(t *testing.T) {
180+
d1 := t.TempDir()
181+
d2 := t.TempDir()
182+
183+
if err := CreateFile("/f", []byte("hello"), 0o644).Apply(d1); err != nil {
184+
t.Fatal(err)
185+
}
186+
if err := CreateFile("/f", []byte("hello"), 0o600).Apply(d2); err != nil {
187+
t.Fatal(err)
188+
}
189+
190+
if err := CheckDirectoryEqual(d1, d2); err == nil {
191+
t.Fatal("directories with different permissions should not be equal")
192+
}
193+
}
194+
195+
func TestCheckDirectoryEqualWithApplier(t *testing.T) {
196+
d := t.TempDir()
197+
198+
a := Apply(
199+
CreateDir("/d", 0o755),
200+
CreateFile("/d/f", []byte("content"), 0o644),
201+
)
202+
if err := a.Apply(d); err != nil {
203+
t.Fatal(err)
204+
}
205+
206+
if err := CheckDirectoryEqualWithApplier(d, a); err != nil {
207+
t.Fatalf("directory should equal its applier: %v", err)
208+
}
209+
}
210+
211+
func TestBuildResources(t *testing.T) {
212+
d := t.TempDir()
213+
214+
a := Apply(
215+
CreateDir("/a", 0o755),
216+
CreateFile("/a/f1", []byte("one"), 0o644),
217+
CreateFile("/b", []byte("two"), 0o600),
218+
Symlink("b", "/c"),
219+
)
220+
if err := a.Apply(d); err != nil {
221+
t.Fatal(err)
222+
}
223+
224+
resources, err := buildResources(d)
225+
if err != nil {
226+
t.Fatal(err)
227+
}
228+
229+
// Should have 4 entries: /a, /a/f1, /b, /c
230+
if len(resources) != 4 {
231+
t.Fatalf("expected 4 resources, got %d", len(resources))
232+
}
233+
234+
// Verify sorted order
235+
for i := 1; i < len(resources); i++ {
236+
if resources[i].path <= resources[i-1].path {
237+
t.Fatalf("resources not sorted: %q <= %q", resources[i].path, resources[i-1].path)
238+
}
239+
}
240+
241+
// Verify types
242+
for _, r := range resources {
243+
switch r.path {
244+
case "/a":
245+
if !r.mode.IsDir() {
246+
t.Errorf("/a should be directory, got %v", r.mode)
247+
}
248+
case "/a/f1":
249+
if !r.mode.IsRegular() {
250+
t.Errorf("/a/f1 should be regular file, got %v", r.mode)
251+
}
252+
if r.size != 3 {
253+
t.Errorf("/a/f1 should have size 3, got %d", r.size)
254+
}
255+
case "/b":
256+
if !r.mode.IsRegular() {
257+
t.Errorf("/b should be regular file, got %v", r.mode)
258+
}
259+
case "/c":
260+
if r.mode&os.ModeSymlink == 0 {
261+
t.Errorf("/c should be symlink, got %v", r.mode)
262+
}
263+
if r.target != "b" {
264+
t.Errorf("/c target should be 'b', got %q", r.target)
265+
}
266+
}
267+
}
268+
}

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: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,11 @@
1818

1919
package fstest
2020

21-
import "testing"
21+
import (
22+
"testing"
23+
24+
_ "github.com/containerd/continuity/testutil"
25+
)
2226

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

0 commit comments

Comments
 (0)