Skip to content

Commit f7238f9

Browse files
committed
Add tests for the changes detection code
This adds 3 tests: Verify that ChangesDirs() returns nothing when run on a copy of the same directory. Verify that various mutations of a directory get reported with the right changes. Verify that ExportChanges() + ApplyLayer() of the above mutation gets an identical directory. Unfortunately the last test is disabled because it fails in multiple ways atm. But I want to get it in so that we can fix it.
1 parent d69a6a2 commit f7238f9

1 file changed

Lines changed: 297 additions & 0 deletions

File tree

archive/changes_test.go

Lines changed: 297 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,297 @@
1+
package archive
2+
3+
import (
4+
"io/ioutil"
5+
"os"
6+
"os/exec"
7+
"path"
8+
"sort"
9+
"testing"
10+
"time"
11+
)
12+
13+
func max(x, y int) int {
14+
if x >= y {
15+
return x
16+
}
17+
return y
18+
}
19+
20+
func copyDir(src, dst string) error {
21+
cmd := exec.Command("cp", "-a", src, dst)
22+
if err := cmd.Run(); err != nil {
23+
return err
24+
}
25+
return nil
26+
}
27+
28+
// Helper to sort []Change by path
29+
type byPath struct{ changes []Change }
30+
31+
func (b byPath) Less(i, j int) bool { return b.changes[i].Path < b.changes[j].Path }
32+
func (b byPath) Len() int { return len(b.changes) }
33+
func (b byPath) Swap(i, j int) { b.changes[i], b.changes[j] = b.changes[j], b.changes[i] }
34+
35+
type FileType uint32
36+
37+
const (
38+
Regular FileType = iota
39+
Dir
40+
Symlink
41+
)
42+
43+
type FileData struct {
44+
filetype FileType
45+
path string
46+
contents string
47+
permissions os.FileMode
48+
}
49+
50+
func createSampleDir(t *testing.T, root string) {
51+
files := []FileData{
52+
{Regular, "file1", "file1\n", 0600},
53+
{Regular, "file2", "file2\n", 0666},
54+
{Regular, "file3", "file3\n", 0404},
55+
{Regular, "file4", "file4\n", 0600},
56+
{Regular, "file5", "file5\n", 0600},
57+
{Regular, "file6", "file6\n", 0600},
58+
{Regular, "file7", "file7\n", 0600},
59+
{Dir, "dir1", "", 0740},
60+
{Regular, "dir1/file1-1", "file1-1\n", 01444},
61+
{Regular, "dir1/file1-2", "file1-2\n", 0666},
62+
{Dir, "dir2", "", 0700},
63+
{Regular, "dir2/file2-1", "file2-1\n", 0666},
64+
{Regular, "dir2/file2-2", "file2-2\n", 0666},
65+
{Dir, "dir3", "", 0700},
66+
{Regular, "dir3/file3-1", "file3-1\n", 0666},
67+
{Regular, "dir3/file3-2", "file3-2\n", 0666},
68+
{Dir, "dir4", "", 0700},
69+
{Regular, "dir4/file3-1", "file4-1\n", 0666},
70+
{Regular, "dir4/file3-2", "file4-2\n", 0666},
71+
{Symlink, "symlink1", "target1", 0666},
72+
{Symlink, "symlink2", "target2", 0666},
73+
}
74+
for _, info := range files {
75+
if info.filetype == Dir {
76+
if err := os.MkdirAll(path.Join(root, info.path), info.permissions); err != nil {
77+
t.Fatal(err)
78+
}
79+
} else if info.filetype == Regular {
80+
if err := ioutil.WriteFile(path.Join(root, info.path), []byte(info.contents), info.permissions); err != nil {
81+
t.Fatal(err)
82+
}
83+
} else if info.filetype == Symlink {
84+
if err := os.Symlink(info.contents, path.Join(root, info.path)); err != nil {
85+
t.Fatal(err)
86+
}
87+
}
88+
}
89+
}
90+
91+
// Create an directory, copy it, make sure we report no changes between the two
92+
func TestChangesDirsEmpty(t *testing.T) {
93+
src, err := ioutil.TempDir("", "docker-changes-test")
94+
if err != nil {
95+
t.Fatal(err)
96+
}
97+
createSampleDir(t, src)
98+
dst := src + "-copy"
99+
if err := copyDir(src, dst); err != nil {
100+
t.Fatal(err)
101+
}
102+
changes, err := ChangesDirs(dst, src)
103+
if err != nil {
104+
t.Fatal(err)
105+
}
106+
107+
if len(changes) != 0 {
108+
t.Fatalf("Reported changes for identical dirs: %v", changes)
109+
}
110+
os.RemoveAll(src)
111+
os.RemoveAll(dst)
112+
}
113+
114+
func mutateSampleDir(t *testing.T, root string) {
115+
// Remove a regular file
116+
if err := os.RemoveAll(path.Join(root, "file1")); err != nil {
117+
t.Fatal(err)
118+
}
119+
120+
// Remove a directory
121+
if err := os.RemoveAll(path.Join(root, "dir1")); err != nil {
122+
t.Fatal(err)
123+
}
124+
125+
// Remove a symlink
126+
if err := os.RemoveAll(path.Join(root, "symlink1")); err != nil {
127+
t.Fatal(err)
128+
}
129+
130+
// Rewrite a file
131+
if err := ioutil.WriteFile(path.Join(root, "file2"), []byte("fileN\n"), 0777); err != nil {
132+
t.Fatal(err)
133+
}
134+
135+
// Replace a file
136+
if err := os.RemoveAll(path.Join(root, "file3")); err != nil {
137+
t.Fatal(err)
138+
}
139+
if err := ioutil.WriteFile(path.Join(root, "file3"), []byte("fileM\n"), 0404); err != nil {
140+
t.Fatal(err)
141+
}
142+
143+
// Touch file
144+
if err := os.Chtimes(path.Join(root, "file4"), time.Now(), time.Now()); err != nil {
145+
t.Fatal(err)
146+
}
147+
148+
// Replace file with dir
149+
if err := os.RemoveAll(path.Join(root, "file5")); err != nil {
150+
t.Fatal(err)
151+
}
152+
if err := os.MkdirAll(path.Join(root, "file5"), 0666); err != nil {
153+
t.Fatal(err)
154+
}
155+
156+
// Create new file
157+
if err := ioutil.WriteFile(path.Join(root, "filenew"), []byte("filenew\n"), 0777); err != nil {
158+
t.Fatal(err)
159+
}
160+
161+
// Create new dir
162+
if err := os.MkdirAll(path.Join(root, "dirnew"), 0766); err != nil {
163+
t.Fatal(err)
164+
}
165+
166+
// Create a new symlink
167+
if err := os.Symlink("targetnew", path.Join(root, "symlinknew")); err != nil {
168+
t.Fatal(err)
169+
}
170+
171+
// Change a symlink
172+
if err := os.RemoveAll(path.Join(root, "symlink2")); err != nil {
173+
t.Fatal(err)
174+
}
175+
if err := os.Symlink("target2change", path.Join(root, "symlink2")); err != nil {
176+
t.Fatal(err)
177+
}
178+
179+
// Replace dir with file
180+
if err := os.RemoveAll(path.Join(root, "dir2")); err != nil {
181+
t.Fatal(err)
182+
}
183+
if err := ioutil.WriteFile(path.Join(root, "dir2"), []byte("dir2\n"), 0777); err != nil {
184+
t.Fatal(err)
185+
}
186+
187+
// Touch dir
188+
if err := os.Chtimes(path.Join(root, "dir3"), time.Now(), time.Now()); err != nil {
189+
t.Fatal(err)
190+
}
191+
}
192+
193+
func TestChangesDirsMutated(t *testing.T) {
194+
src, err := ioutil.TempDir("", "docker-changes-test")
195+
if err != nil {
196+
t.Fatal(err)
197+
}
198+
createSampleDir(t, src)
199+
dst := src + "-copy"
200+
if err := copyDir(src, dst); err != nil {
201+
t.Fatal(err)
202+
}
203+
mutateSampleDir(t, dst)
204+
205+
changes, err := ChangesDirs(dst, src)
206+
if err != nil {
207+
t.Fatal(err)
208+
}
209+
210+
sort.Sort(byPath{changes})
211+
212+
expectedChanges := []Change{
213+
{"/dir1", ChangeDelete},
214+
{"/dir2", ChangeModify},
215+
{"/dir3", ChangeModify},
216+
{"/dirnew", ChangeAdd},
217+
{"/file1", ChangeDelete},
218+
{"/file2", ChangeModify},
219+
{"/file3", ChangeModify},
220+
{"/file4", ChangeModify},
221+
{"/file5", ChangeModify},
222+
{"/filenew", ChangeAdd},
223+
{"/symlink1", ChangeDelete},
224+
{"/symlink2", ChangeModify},
225+
{"/symlinknew", ChangeAdd},
226+
}
227+
228+
i := 0
229+
for ; i < max(len(changes), len(expectedChanges)); i++ {
230+
if i >= len(expectedChanges) {
231+
t.Fatalf("unexpected change %s\n", changes[i].String())
232+
}
233+
if i >= len(changes) {
234+
t.Fatalf("no change for expected change %s\n", expectedChanges[i].String())
235+
}
236+
if changes[i].Path == expectedChanges[i].Path {
237+
if changes[i] != expectedChanges[i] {
238+
t.Fatalf("Wrong change for %s, expected %s, got %d\n", changes[i].Path, changes[i].String(), expectedChanges[i].String())
239+
}
240+
} else if changes[i].Path < expectedChanges[i].Path {
241+
t.Fatalf("unexpected change %s\n", changes[i].String())
242+
} else {
243+
t.Fatalf("no change for expected change %s\n", expectedChanges[i].String())
244+
}
245+
}
246+
for ; i < len(expectedChanges); i++ {
247+
}
248+
249+
os.RemoveAll(src)
250+
os.RemoveAll(dst)
251+
}
252+
253+
func TestApplyLayer(t *testing.T) {
254+
return // Disable this for now as it is broken
255+
256+
src, err := ioutil.TempDir("", "docker-changes-test")
257+
if err != nil {
258+
t.Fatal(err)
259+
}
260+
createSampleDir(t, src)
261+
dst := src + "-copy"
262+
if err := copyDir(src, dst); err != nil {
263+
t.Fatal(err)
264+
}
265+
mutateSampleDir(t, dst)
266+
267+
changes, err := ChangesDirs(dst, src)
268+
if err != nil {
269+
t.Fatal(err)
270+
}
271+
272+
layer, err := ExportChanges(dst, changes)
273+
if err != nil {
274+
t.Fatal(err)
275+
}
276+
277+
layerCopy, err := NewTempArchive(layer, "")
278+
if err != nil {
279+
t.Fatal(err)
280+
}
281+
282+
if err := ApplyLayer(src, layerCopy); err != nil {
283+
t.Fatal(err)
284+
}
285+
286+
changes2, err := ChangesDirs(src, dst)
287+
if err != nil {
288+
t.Fatal(err)
289+
}
290+
291+
if len(changes2) != 0 {
292+
t.Fatalf("Unexpected differences after re applying mutation: %v", changes)
293+
}
294+
295+
os.RemoveAll(src)
296+
os.RemoveAll(dst)
297+
}

0 commit comments

Comments
 (0)