1
16
17 package fs
18
19 import (
20 "context"
21 "errors"
22 "io"
23 "math/rand"
24 "os"
25 "path/filepath"
26 "runtime"
27 "testing"
28
29 "github.com/containerd/continuity/fs/fstest"
30 )
31
32 var errNotImplemented = errors.New("check not implemented")
33
34 func TestUsage(t *testing.T) {
35 align, dirs, err := getTmpAlign(t)
36 if err != nil {
37 t.Fatal(err)
38 }
39
40 type testCase struct {
41 name string
42 fs fstest.Applier
43 size int64
44 }
45 testCases := []testCase{
46 {
47 name: "SingleSmallFile",
48 fs: fstest.Apply(
49 fstest.CreateDir("/dir", 0o755),
50 fstest.CreateRandomFile("/dir/file", 1, 5, 0o644),
51 ),
52 size: dirs(2) + align(5),
53 },
54 {
55 name: "MultipleSmallFile",
56 fs: fstest.Apply(
57 fstest.CreateDir("/dir", 0o755),
58 fstest.CreateRandomFile("/dir/file1", 2, 5, 0o644),
59 fstest.CreateRandomFile("/dir/file2", 3, 5, 0o644),
60 ),
61 size: dirs(2) + align(5)*2,
62 },
63 {
64 name: "BiggerFiles",
65 fs: fstest.Apply(
66 fstest.CreateDir("/dir", 0o755),
67 fstest.CreateRandomFile("/dir/file1", 4, 5, 0o644),
68 fstest.CreateRandomFile("/dir/file2", 5, 1024, 0o644),
69 fstest.CreateRandomFile("/dir/file3", 6, 50*1024, 0o644),
70 ),
71 size: dirs(2) + align(5) + align(1024) + align(50*1024),
72 },
73 }
74 if runtime.GOOS != "windows" {
75 testCases = append(testCases, []testCase{
76 {
77 name: "Hardlinks",
78 fs: fstest.Apply(
79 fstest.CreateDir("/dir", 0o755),
80 fstest.CreateRandomFile("/dir/file1", 11, 60*1024, 0o644),
81 fstest.Link("/dir/file1", "/dir/link1"),
82 ),
83 size: dirs(2) + align(60*1024),
84 },
85 {
86 name: "HardlinkSparefile",
87 fs: fstest.Apply(
88 fstest.CreateDir("/dir", 0o755),
89 createSparseFile("/dir/file1", 10, 0o644, 30*1024, 1024*1024*1024, 30*1024),
90 fstest.Link("/dir/file1", "/dir/link1"),
91 ),
92 size: dirs(2) + align(30*1024)*2,
93 },
94 }...)
95 }
96 if runtime.GOOS != "windows" && runtime.GOOS != "darwin" {
97 testCases = append(testCases, []testCase{
98 {
99 name: "SparseFiles",
100 fs: fstest.Apply(
101 fstest.CreateDir("/dir", 0o755),
102 fstest.CreateRandomFile("/dir/file1", 7, 5, 0o644),
103 createSparseFile("/dir/sparse1", 8, 0o644, 5, 1024*1024, 5),
104 createSparseFile("/dir/sparse2", 9, 0o644, 0, 1024*1024),
105 createSparseFile("/dir/sparse2", 10, 0o644, 0, 1024*1024*1024, 1024),
106 ),
107 size: dirs(2) + align(5)*3 + align(1024),
108 },
109 }...)
110 }
111
112 for i := range testCases {
113 tc := testCases[i]
114 t.Run(tc.name, func(t *testing.T) {
115 t.Parallel()
116
117 t1 := t.TempDir()
118 if err := tc.fs.Apply(t1); err != nil {
119 t.Fatal("Failed to apply base filesystem:", err)
120 }
121
122 usage, err := DiskUsage(context.Background(), t1)
123 if err != nil {
124 t.Fatal(err)
125 }
126 if usage.Size != tc.size {
127 t.Fatalf("Wrong usage size %d, expected %d", usage.Size, tc.size)
128 }
129
130 du, err := duCheck(t1)
131 if err != nil && err != errNotImplemented {
132 t.Fatal("Failed calling du:", err)
133 }
134 if err == nil && usage.Size != du {
135 t.Fatalf("Wrong usage size %d, du reported %d", usage.Size, du)
136 }
137 })
138 }
139 }
140
141
142
143
144
145 func createSparseFile(name string, seed int64, perm os.FileMode, parts ...int64) fstest.Applier {
146 return sparseFile{
147 name: name,
148 seed: seed,
149 parts: parts,
150 perm: perm,
151 }
152 }
153
154 type sparseFile struct {
155 name string
156 seed int64
157 parts []int64
158 perm os.FileMode
159 }
160
161 func (sf sparseFile) Apply(root string) (retErr error) {
162 fullPath := filepath.Join(root, sf.name)
163 f, err := os.OpenFile(fullPath, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, sf.perm)
164 if err != nil {
165 return err
166 }
167 defer func() {
168 err := f.Close()
169 if err != nil && retErr == nil {
170 retErr = err
171 }
172 }()
173
174 rr := rand.New(rand.NewSource(sf.seed))
175
176 parts := sf.parts
177 for len(parts) > 0 {
178
179 if parts[0] > 0 {
180 _, err = io.Copy(f, io.LimitReader(rr, parts[0]))
181 if err != nil {
182 return err
183 }
184 }
185 parts = parts[1:]
186
187 if len(parts) > 0 {
188 if parts[0] != 0 {
189 f.Seek(parts[0], io.SeekCurrent)
190 }
191 parts = parts[1:]
192 }
193 }
194 return os.Chmod(fullPath, sf.perm)
195 }
196
View as plain text