diff options
Diffstat (limited to 'pkg/aflow/cache_test.go')
| -rw-r--r-- | pkg/aflow/cache_test.go | 144 |
1 files changed, 144 insertions, 0 deletions
diff --git a/pkg/aflow/cache_test.go b/pkg/aflow/cache_test.go new file mode 100644 index 000000000..244defdd3 --- /dev/null +++ b/pkg/aflow/cache_test.go @@ -0,0 +1,144 @@ +// Copyright 2026 syzkaller project authors. All rights reserved. +// Use of this source code is governed by Apache 2 LICENSE that can be found in the LICENSE file. + +package aflow + +import ( + "bytes" + "fmt" + "os" + "path/filepath" + "testing" + "time" + + "github.com/google/syzkaller/pkg/osutil" + "github.com/stretchr/testify/require" +) + +func TestCache(t *testing.T) { + var mockedTime time.Time + timeNow := func() time.Time { + return mockedTime + } + tempDir := t.TempDir() + c, err := newTestCache(t, tempDir, 1<<40, timeNow) + require.NoError(t, err) + dir1, err := c.Create("foo", "1", func(dir string) error { + return osutil.WriteFile(filepath.Join(dir, "bar"), []byte("abc")) + }) + require.NoError(t, err) + data, err := os.ReadFile(filepath.Join(dir1, "bar")) + require.NoError(t, err) + require.Equal(t, data, []byte("abc")) + c.Release(dir1) + + dir2, err := c.Create("foo", "1", func(dir string) error { + t.Fatal("must not be called") + return nil + }) + require.NoError(t, err) + require.Equal(t, dir2, dir1) + data, err = os.ReadFile(filepath.Join(dir2, "bar")) + require.NoError(t, err) + require.Equal(t, data, []byte("abc")) + c.Release(dir2) + + dir3, err := c.Create("foo", "2", func(dir string) error { + return osutil.WriteFile(filepath.Join(dir, "baz"), []byte("def")) + }) + require.NoError(t, err) + require.NotEqual(t, dir3, dir1) + data, err = os.ReadFile(filepath.Join(dir3, "baz")) + require.NoError(t, err) + require.Equal(t, data, []byte("def")) + c.Release(dir3) + + failedDir := "" + dir4, err := c.Create("foo", "3", func(dir string) error { + failedDir = dir + return fmt.Errorf("failed") + }) + require.Error(t, err) + require.Empty(t, dir4) + require.False(t, osutil.IsExist(failedDir)) + + // Create a new cache, it should pick up the state from disk. + c, err = newTestCache(t, tempDir, 1<<40, timeNow) + require.NoError(t, err) + + dir5, err := c.Create("foo", "1", func(dir string) error { + t.Fatal("must not be called") + return nil + }) + require.NoError(t, err) + require.Equal(t, dir5, dir1) + data, err = os.ReadFile(filepath.Join(dir5, "bar")) + require.NoError(t, err) + require.Equal(t, data, []byte("abc")) + c.Release(dir5) + + // Model an incomplete dir without metadata, it should be removed. + strayDir := filepath.Join(tempDir, "a", "b") + require.NoError(t, osutil.MkdirAll(strayDir)) + require.NoError(t, osutil.WriteFile(filepath.Join(strayDir, "foo"), []byte("foo"))) + + // With 0 max size everything unused should be purged. + _, err = newTestCache(t, tempDir, 0, timeNow) + require.NoError(t, err) + require.False(t, osutil.IsExist(dir1)) + require.False(t, osutil.IsExist(dir3)) + require.False(t, osutil.IsExist(strayDir)) + + // Test incremental purging of files. + c, err = newTestCache(t, tempDir, 100<<10, timeNow) + require.NoError(t, err) + + mockedTime = mockedTime.Add(time.Minute) + dir6, err := c.Create("foo", "1", func(dir string) error { + return osutil.WriteFile(filepath.Join(dir, "bar"), bytes.Repeat([]byte{'a'}, 5<<10)) + }) + require.NoError(t, err) + c.Release(dir6) + + mockedTime = mockedTime.Add(time.Minute) + dir7, err := c.Create("foo", "2", func(dir string) error { + return osutil.WriteFile(filepath.Join(dir, "bar"), bytes.Repeat([]byte{'a'}, 5<<10)) + }) + require.NoError(t, err) + c.Release(dir7) + + mockedTime = mockedTime.Add(time.Minute) + dir8, err := c.Create("foo", "3", func(dir string) error { + return osutil.WriteFile(filepath.Join(dir, "bar"), bytes.Repeat([]byte{'a'}, 60<<10)) + }) + require.NoError(t, err) + c.Release(dir8) + + // Force update of the last access time for the first dir. + mockedTime = mockedTime.Add(time.Minute) + dir9, err := c.Create("foo", "1", func(dir string) error { + t.Fatal("must not be called") + return nil + }) + require.NoError(t, err) + require.Equal(t, dir6, dir9) + c.Release(dir9) + + // Both dirs should exist since they should fit into cache size. + require.True(t, osutil.IsExist(dir6)) + require.True(t, osutil.IsExist(dir7)) + require.True(t, osutil.IsExist(dir8)) + + mockedTime = mockedTime.Add(time.Minute) + dir10, err := c.Create("foo", "4", func(dir string) error { + return osutil.WriteFile(filepath.Join(dir, "bar"), bytes.Repeat([]byte{'a'}, 60<<10)) + }) + require.NoError(t, err) + c.Release(dir10) + + // Two oldest dirs should be purged. + require.True(t, osutil.IsExist(dir6)) + require.False(t, osutil.IsExist(dir7)) + require.False(t, osutil.IsExist(dir8)) + require.True(t, osutil.IsExist(dir10)) +} |
