Skip to content

Commit 0d99599

Browse files
committed
contenthash: add tests to check needsScan's correctness
Most of these tests revolve around making sure that we are scanning the correct path during Checksum, and that we don't think a scan is required for subpaths we have already scanned. Signed-off-by: Aleksa Sarai <[email protected]>
1 parent 65c9146 commit 0d99599

File tree

1 file changed

+126
-7
lines changed

1 file changed

+126
-7
lines changed

cache/contenthash/checksum_test.go

Lines changed: 126 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,125 @@ func TestChecksumSymlinkNoParentScan(t *testing.T) {
6565
dgst, err := cc.Checksum(context.TODO(), ref, "aa/ln/bb/cc/dd", ChecksumOpts{FollowLinks: true}, nil)
6666
require.NoError(t, err)
6767
require.Equal(t, dgstFileData0, dgst)
68+
69+
// The above checksum request should have only checksummed aa/bb/cc, and so
70+
// any parent directories should need a scan but non-existent (or existent)
71+
// children should not.
72+
root := cc.tree.Root()
73+
74+
for _, path := range []string{
75+
// Paths not within the scanned /aa/bb/cc/.
76+
"/", "/aa", "/aa/bb", "/aa/bb/ff", "/non-exist",
77+
} {
78+
needs1, err := cc.needsScan(root, path, false)
79+
require.NoErrorf(t, err, "needsScan(%q, followTrailing=false)", path)
80+
require.Truef(t, needs1, "needsScan(%q, followTrailing=false)", path)
81+
82+
needs2, err := cc.needsScan(root, path, true)
83+
require.NoErrorf(t, err, "needsScan(%q, followTrailing=true)", path)
84+
require.Truef(t, needs2, "needsScan(%q, followTrailing=true)", path)
85+
}
86+
87+
for _, path := range []string{
88+
// Paths within the scanned /aa/bb/cc, even if they don't exist.
89+
"/aa/bb/cc", "/aa/bb/cc/non-exist", "/aa/bb/cc/dd/ee/ff", "/aa/bb/cc/non-exist/xx/yy/zz",
90+
} {
91+
needs1, err := cc.needsScan(root, path, false)
92+
require.NoErrorf(t, err, "needsScan(%q, followTrailing=false)", path)
93+
require.Falsef(t, needs1, "needsScan(%q, followTrailing=false)", path)
94+
95+
needs2, err := cc.needsScan(root, path, true)
96+
require.NoErrorf(t, err, "needsScan(%q, followTrailing=true)", path)
97+
require.Falsef(t, needs2, "needsScan(%q, followTrailing=true)", path)
98+
}
99+
100+
// /aa was not scanned, but during the walk we went through /aa/ln and so
101+
// we know the contents of the link. However, if we want to scan it with
102+
// followTrailing=true, we will need a scan because we didn't scan /aa.
103+
path := "/aa/ln"
104+
needs1, err := cc.needsScan(root, path, false)
105+
require.NoErrorf(t, err, "needsScan(%q, followTrailing=false)", path)
106+
require.Falsef(t, needs1, "needsScan(%q, followTrailing=false)", path)
107+
108+
needs2, err := cc.needsScan(root, path, true)
109+
require.NoErrorf(t, err, "needsScan(%q, followTrailing=true)", path)
110+
require.Truef(t, needs2, "needsScan(%q, followTrailing=true)", path)
111+
}
112+
113+
// https://github.com/moby/buildkit/issues/5042
114+
func TestNeedScanChecksumRegression(t *testing.T) {
115+
tmpdir := t.TempDir()
116+
117+
snapshotter, err := native.NewSnapshotter(filepath.Join(tmpdir, "snapshots"))
118+
require.NoError(t, err)
119+
cm, cleanup := setupCacheManager(t, tmpdir, "native", snapshotter)
120+
t.Cleanup(cleanup)
121+
122+
ch := []string{
123+
"ADD aa dir",
124+
"ADD aa/bb dir",
125+
"ADD aa/bb/cc file data0",
126+
"ADD aa/ln symlink /aa",
127+
"ADD aa/root symlink /",
128+
"ADD bb symlink aa/bb",
129+
}
130+
131+
ref := createRef(t, cm, ch)
132+
133+
cc, err := newCacheContext(ref)
134+
require.NoError(t, err)
135+
136+
// Checksumming /aa/bb while following links will result in /aa being scanned.
137+
_, err = cc.Checksum(context.TODO(), ref, "/bb", ChecksumOpts{FollowLinks: true}, nil)
138+
require.NoError(t, err)
139+
140+
root := cc.tree.Root()
141+
for _, test := range []struct {
142+
path string
143+
followTrailing, expectNeedsScan bool
144+
}{
145+
// Any path under /aa will not result in a re-scan.
146+
{"/aa", true, false},
147+
{"/aa/ln", true, false},
148+
{"/aa/ln", false, false},
149+
{"/aa/non-exist", true, false},
150+
{"/aa/bb/non-exist", true, false},
151+
{"/aa/bb/cc", true, false},
152+
{"/aa/bb/cc/non-exist", true, false},
153+
// followTrailing=false on a symlink to /.
154+
{"/aa/root", false, false},
155+
// /bb itself was scanned during the lookup in Checksum.
156+
{"/bb", true, false},
157+
{"/bb", false, false},
158+
// A path outside /aa will need a scan.
159+
{"/non-exist", true, true},
160+
{"/non-exist", false, true},
161+
{"/aa/root", true, true},
162+
{"/", true, true},
163+
} {
164+
needs, err := cc.needsScan(root, test.path, test.followTrailing)
165+
require.NoErrorf(t, err, "needsScan(%q, followTrailing=%v)", test.path, test.followTrailing)
166+
require.Equalf(t, test.expectNeedsScan, needs, "needsScan(%q, followTrailing=%v)", test.path, test.followTrailing)
167+
}
168+
169+
// Looking up a non-existent path in / will checksum the whole tree. See
170+
// <https://github.com/moby/buildkit/issues/5042> for more information.
171+
// This means that needsScan will return true for any path.
172+
_, err = cc.Checksum(context.TODO(), ref, "/non-existent", ChecksumOpts{FollowLinks: true}, nil)
173+
require.Error(t, err)
174+
175+
root = cc.tree.Root()
176+
for _, path := range []string{
177+
"/", "/non-exist", "/ff", "/aa/root", "/non-exist/child", "/different-non-exist",
178+
} {
179+
needs1, err := cc.needsScan(root, path, false)
180+
require.NoErrorf(t, err, "needsScan(%q, followTrailing=false)", path)
181+
require.Falsef(t, needs1, "needsScan(%q, followTrailing=false)", path)
182+
183+
needs2, err := cc.needsScan(root, path, true)
184+
require.NoErrorf(t, err, "needsScan(%q, followTrailing=true)", path)
185+
require.Falsef(t, needs2, "needsScan(%q, followTrailing=true)", path)
186+
}
68187
}
69188

70189
func TestChecksumNonLexicalSymlinks(t *testing.T) {
@@ -114,8 +233,8 @@ func TestChecksumNonLexicalSymlinks(t *testing.T) {
114233
"link3/target/file",
115234
} {
116235
dgst, err := cc.Checksum(context.TODO(), ref, path, ChecksumOpts{FollowLinks: true}, nil)
117-
require.NoError(t, err)
118-
require.Equal(t, dgstFileData0, dgst)
236+
require.NoErrorf(t, err, "Checksum(%q)", path)
237+
require.Equalf(t, dgstFileData0, dgst, "Checksum(%q)", path)
119238
}
120239

121240
// FollowLinks only affects final component resolution, so make sure that
@@ -130,8 +249,8 @@ func TestChecksumNonLexicalSymlinks(t *testing.T) {
130249
"link3/target/file",
131250
} {
132251
dgst, err := cc.Checksum(context.TODO(), ref, path, ChecksumOpts{FollowLinks: false}, nil)
133-
require.NoError(t, err)
134-
require.Equal(t, dgstFileData0, dgst)
252+
require.NoErrorf(t, err, "Checksum(%q)", path)
253+
require.Equalf(t, dgstFileData0, dgst, "Checksum(%q)", path)
135254
}
136255

137256
dgstLink1TargetFile, err := cc.Checksum(context.TODO(), ref, "link1/target_file", ChecksumOpts{FollowLinks: false}, nil)
@@ -159,9 +278,9 @@ func TestChecksumNonLexicalSymlinks(t *testing.T) {
159278
{"link3/target_file", dgstLink3TargetFile},
160279
} {
161280
dgst, err := cc.Checksum(context.TODO(), ref, test.path, ChecksumOpts{FollowLinks: false}, nil)
162-
require.NoError(t, err)
163-
require.NotEqual(t, dgstFileData0, dgst)
164-
require.Equal(t, test.expectedDgst, dgst)
281+
require.NoErrorf(t, err, "Checksum(%q)", test.path)
282+
require.NotEqualf(t, dgstFileData0, dgst, "Checksum(%q)", test.path)
283+
require.Equalf(t, test.expectedDgst, dgst, "Checksum(%q)", test.path)
165284
}
166285
}
167286

0 commit comments

Comments
 (0)