1package fscache // import "github.com/docker/docker/builder/fscache"
2
3import (
4	"context"
5	"io/ioutil"
6	"os"
7	"path/filepath"
8	"testing"
9	"time"
10
11	"github.com/moby/buildkit/session/filesync"
12	"gotest.tools/assert"
13	is "gotest.tools/assert/cmp"
14)
15
16func TestFSCache(t *testing.T) {
17	tmpDir, err := ioutil.TempDir("", "fscache")
18	assert.Check(t, err)
19	defer os.RemoveAll(tmpDir)
20
21	backend := NewNaiveCacheBackend(filepath.Join(tmpDir, "backend"))
22
23	opt := Opt{
24		Root:     tmpDir,
25		Backend:  backend,
26		GCPolicy: GCPolicy{MaxSize: 15, MaxKeepDuration: time.Hour},
27	}
28
29	fscache, err := NewFSCache(opt)
30	assert.Check(t, err)
31
32	defer fscache.Close()
33
34	err = fscache.RegisterTransport("test", &testTransport{})
35	assert.Check(t, err)
36
37	src1, err := fscache.SyncFrom(context.TODO(), &testIdentifier{"foo", "data", "bar"})
38	assert.Check(t, err)
39
40	dt, err := ioutil.ReadFile(filepath.Join(src1.Root().Path(), "foo"))
41	assert.Check(t, err)
42	assert.Check(t, is.Equal(string(dt), "data"))
43
44	// same id doesn't recalculate anything
45	src2, err := fscache.SyncFrom(context.TODO(), &testIdentifier{"foo", "data2", "bar"})
46	assert.Check(t, err)
47	assert.Check(t, is.Equal(src1.Root().Path(), src2.Root().Path()))
48
49	dt, err = ioutil.ReadFile(filepath.Join(src1.Root().Path(), "foo"))
50	assert.Check(t, err)
51	assert.Check(t, is.Equal(string(dt), "data"))
52	assert.Check(t, src2.Close())
53
54	src3, err := fscache.SyncFrom(context.TODO(), &testIdentifier{"foo2", "data2", "bar"})
55	assert.Check(t, err)
56	assert.Check(t, src1.Root().Path() != src3.Root().Path())
57
58	dt, err = ioutil.ReadFile(filepath.Join(src3.Root().Path(), "foo2"))
59	assert.Check(t, err)
60	assert.Check(t, is.Equal(string(dt), "data2"))
61
62	s, err := fscache.DiskUsage(context.TODO())
63	assert.Check(t, err)
64	assert.Check(t, is.Equal(s, int64(0)))
65
66	assert.Check(t, src3.Close())
67
68	s, err = fscache.DiskUsage(context.TODO())
69	assert.Check(t, err)
70	assert.Check(t, is.Equal(s, int64(5)))
71
72	// new upload with the same shared key shoutl overwrite
73	src4, err := fscache.SyncFrom(context.TODO(), &testIdentifier{"foo3", "data3", "bar"})
74	assert.Check(t, err)
75	assert.Check(t, src1.Root().Path() != src3.Root().Path())
76
77	dt, err = ioutil.ReadFile(filepath.Join(src3.Root().Path(), "foo3"))
78	assert.Check(t, err)
79	assert.Check(t, is.Equal(string(dt), "data3"))
80	assert.Check(t, is.Equal(src4.Root().Path(), src3.Root().Path()))
81	assert.Check(t, src4.Close())
82
83	s, err = fscache.DiskUsage(context.TODO())
84	assert.Check(t, err)
85	assert.Check(t, is.Equal(s, int64(10)))
86
87	// this one goes over the GC limit
88	src5, err := fscache.SyncFrom(context.TODO(), &testIdentifier{"foo4", "datadata", "baz"})
89	assert.Check(t, err)
90	assert.Check(t, src5.Close())
91
92	// GC happens async
93	time.Sleep(100 * time.Millisecond)
94
95	// only last insertion after GC
96	s, err = fscache.DiskUsage(context.TODO())
97	assert.Check(t, err)
98	assert.Check(t, is.Equal(s, int64(8)))
99
100	// prune deletes everything
101	released, err := fscache.Prune(context.TODO())
102	assert.Check(t, err)
103	assert.Check(t, is.Equal(released, uint64(8)))
104
105	s, err = fscache.DiskUsage(context.TODO())
106	assert.Check(t, err)
107	assert.Check(t, is.Equal(s, int64(0)))
108}
109
110type testTransport struct {
111}
112
113func (t *testTransport) Copy(ctx context.Context, id RemoteIdentifier, dest string, cs filesync.CacheUpdater) error {
114	testid := id.(*testIdentifier)
115	return ioutil.WriteFile(filepath.Join(dest, testid.filename), []byte(testid.data), 0600)
116}
117
118type testIdentifier struct {
119	filename  string
120	data      string
121	sharedKey string
122}
123
124func (t *testIdentifier) Key() string {
125	return t.filename
126}
127func (t *testIdentifier) SharedKey() string {
128	return t.sharedKey
129}
130func (t *testIdentifier) Transport() string {
131	return "test"
132}
133