rclone/lib/cache/cache_test.go
Josh Soref ce3b65e6dc all: fix spelling across the project
* abcdefghijklmnopqrstuvwxyz
* accounting
* additional
* allowed
* almost
* already
* appropriately
* arise
* bandwidth
* behave
* bidirectional
* brackets
* cached
* characters
* cloud
* committing
* concatenating
* configured
* constructs
* current
* cutoff
* deferred
* different
* directory
* disposition
* dropbox
* either way
* error
* excess
* experiments
* explicitly
* externally
* files
* github
* gzipped
* hierarchies
* huffman
* hyphen
* implicitly
* independent
* insensitive
* integrity
* libraries
* literally
* metadata
* mimics
* missing
* modification
* multipart
* multiple
* nightmare
* nonexistent
* number
* obscure
* ourselves
* overridden
* potatoes
* preexisting
* priority
* received
* remote
* replacement
* represents
* reproducibility
* response
* satisfies
* sensitive
* separately
* separator
* specifying
* string
* successful
* synchronization
* syncing
* šenfeld
* take
* temporarily
* testcontents
* that
* the
* themselves
* throttling
* timeout
* transaction
* transferred
* unnecessary
* using
* webbrowser
* which
* with
* workspace

Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com>
2022-08-30 11:16:26 +02:00

370 lines
7.1 KiB
Go

package cache
import (
"errors"
"fmt"
"testing"
"time"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
var (
called = 0
errSentinel = errors.New("an error")
errCached = errors.New("a cached error")
)
func setup(t *testing.T) (*Cache, CreateFunc) {
called = 0
create := func(path string) (interface{}, bool, error) {
assert.Equal(t, 0, called)
called++
switch path {
case "/":
return "/", true, nil
case "/file.txt":
return "/file.txt", true, errCached
case "/error":
return nil, false, errSentinel
case "/err":
return nil, false, errSentinel
}
panic(fmt.Sprintf("Unknown path %q", path))
}
c := New()
return c, create
}
func TestGet(t *testing.T) {
c, create := setup(t)
assert.Equal(t, 0, len(c.cache))
f, err := c.Get("/", create)
require.NoError(t, err)
assert.Equal(t, 1, len(c.cache))
f2, err := c.Get("/", create)
require.NoError(t, err)
assert.Equal(t, f, f2)
}
func TestGetFile(t *testing.T) {
c, create := setup(t)
assert.Equal(t, 0, len(c.cache))
f, err := c.Get("/file.txt", create)
require.Equal(t, errCached, err)
assert.Equal(t, 1, len(c.cache))
f2, err := c.Get("/file.txt", create)
require.Equal(t, errCached, err)
assert.Equal(t, f, f2)
}
func TestGetError(t *testing.T) {
c, create := setup(t)
assert.Equal(t, 0, len(c.cache))
f, err := c.Get("/error", create)
require.Equal(t, errSentinel, err)
require.Equal(t, nil, f)
assert.Equal(t, 0, len(c.cache))
}
func TestPut(t *testing.T) {
c, create := setup(t)
assert.Equal(t, 0, len(c.cache))
c.Put("/alien", "slime")
assert.Equal(t, 1, len(c.cache))
fNew, err := c.Get("/alien", create)
require.NoError(t, err)
require.Equal(t, "slime", fNew)
assert.Equal(t, 1, len(c.cache))
}
func TestCacheExpire(t *testing.T) {
c, create := setup(t)
c.SetExpireInterval(time.Millisecond)
assert.Equal(t, false, c.expireRunning)
_, err := c.Get("/", create)
require.NoError(t, err)
c.mu.Lock()
entry := c.cache["/"]
assert.Equal(t, 1, len(c.cache))
c.mu.Unlock()
c.cacheExpire()
c.mu.Lock()
assert.Equal(t, 1, len(c.cache))
entry.lastUsed = time.Now().Add(-c.expireDuration - 60*time.Second)
assert.Equal(t, true, c.expireRunning)
c.mu.Unlock()
time.Sleep(250 * time.Millisecond)
c.mu.Lock()
assert.Equal(t, false, c.expireRunning)
assert.Equal(t, 0, len(c.cache))
c.mu.Unlock()
}
func TestCacheNoExpire(t *testing.T) {
c, create := setup(t)
assert.False(t, c.noCache())
c.SetExpireDuration(0)
assert.Equal(t, false, c.expireRunning)
assert.True(t, c.noCache())
f, err := c.Get("/", create)
require.NoError(t, err)
require.NotNil(t, f)
c.mu.Lock()
assert.Equal(t, 0, len(c.cache))
c.mu.Unlock()
c.Put("/alien", "slime")
c.mu.Lock()
assert.Equal(t, 0, len(c.cache))
c.mu.Unlock()
}
func TestCachePin(t *testing.T) {
c, create := setup(t)
_, err := c.Get("/", create)
require.NoError(t, err)
// Pin a nonexistent item to show nothing happens
c.Pin("notfound")
c.mu.Lock()
entry := c.cache["/"]
assert.Equal(t, 1, len(c.cache))
c.mu.Unlock()
c.cacheExpire()
c.mu.Lock()
assert.Equal(t, 1, len(c.cache))
c.mu.Unlock()
// Pin the entry and check it does not get expired
c.Pin("/")
// Reset last used to make the item expirable
c.mu.Lock()
entry.lastUsed = time.Now().Add(-c.expireDuration - 60*time.Second)
c.mu.Unlock()
c.cacheExpire()
c.mu.Lock()
assert.Equal(t, 1, len(c.cache))
c.mu.Unlock()
// Unpin the entry and check it does get expired now
c.Unpin("/")
// Reset last used
c.mu.Lock()
entry.lastUsed = time.Now().Add(-c.expireDuration - 60*time.Second)
c.mu.Unlock()
c.cacheExpire()
c.mu.Lock()
assert.Equal(t, 0, len(c.cache))
c.mu.Unlock()
}
func TestClear(t *testing.T) {
c, create := setup(t)
assert.Equal(t, 0, len(c.cache))
_, err := c.Get("/", create)
require.NoError(t, err)
assert.Equal(t, 1, len(c.cache))
c.Clear()
assert.Equal(t, 0, len(c.cache))
}
func TestEntries(t *testing.T) {
c, create := setup(t)
assert.Equal(t, 0, c.Entries())
_, err := c.Get("/", create)
require.NoError(t, err)
assert.Equal(t, 1, c.Entries())
c.Clear()
assert.Equal(t, 0, c.Entries())
}
func TestGetMaybe(t *testing.T) {
c, create := setup(t)
value, found := c.GetMaybe("/")
assert.Equal(t, false, found)
assert.Nil(t, value)
f, err := c.Get("/", create)
require.NoError(t, err)
value, found = c.GetMaybe("/")
assert.Equal(t, true, found)
assert.Equal(t, f, value)
c.Clear()
value, found = c.GetMaybe("/")
assert.Equal(t, false, found)
assert.Nil(t, value)
}
func TestDelete(t *testing.T) {
c, create := setup(t)
assert.Equal(t, 0, len(c.cache))
_, err := c.Get("/", create)
require.NoError(t, err)
assert.Equal(t, 1, len(c.cache))
assert.Equal(t, false, c.Delete("notfound"))
assert.Equal(t, 1, len(c.cache))
assert.Equal(t, true, c.Delete("/"))
assert.Equal(t, 0, len(c.cache))
assert.Equal(t, false, c.Delete("/"))
assert.Equal(t, 0, len(c.cache))
}
func TestDeletePrefix(t *testing.T) {
create := func(path string) (interface{}, bool, error) {
return path, true, nil
}
c := New()
_, err := c.Get("remote:path", create)
require.NoError(t, err)
_, err = c.Get("remote:path2", create)
require.NoError(t, err)
_, err = c.Get("remote:", create)
require.NoError(t, err)
_, err = c.Get("remote", create)
require.NoError(t, err)
assert.Equal(t, 4, len(c.cache))
assert.Equal(t, 3, c.DeletePrefix("remote:"))
assert.Equal(t, 1, len(c.cache))
assert.Equal(t, 1, c.DeletePrefix(""))
assert.Equal(t, 0, len(c.cache))
assert.Equal(t, 0, c.DeletePrefix(""))
assert.Equal(t, 0, len(c.cache))
}
func TestCacheRename(t *testing.T) {
c := New()
create := func(path string) (interface{}, bool, error) {
return path, true, nil
}
existing1, err := c.Get("existing1", create)
require.NoError(t, err)
_, err = c.Get("existing2", create)
require.NoError(t, err)
assert.Equal(t, 2, c.Entries())
// rename to nonexistent
value, found := c.Rename("existing1", "EXISTING1")
assert.Equal(t, true, found)
assert.Equal(t, existing1, value)
assert.Equal(t, 2, c.Entries())
// rename to existent and check existing value is returned
value, found = c.Rename("existing2", "EXISTING1")
assert.Equal(t, true, found)
assert.Equal(t, existing1, value)
assert.Equal(t, 1, c.Entries())
// rename nonexistent
value, found = c.Rename("notfound", "NOTFOUND")
assert.Equal(t, false, found)
assert.Nil(t, value)
assert.Equal(t, 1, c.Entries())
}
func TestCacheFinalize(t *testing.T) {
c := New()
numCalled := 0
c.SetFinalizer(func(v interface{}) {
numCalled++
})
create := func(path string) (interface{}, bool, error) {
return path, true, nil
}
_, _ = c.Get("ok", create)
assert.Equal(t, 0, numCalled)
c.Clear()
assert.Equal(t, 1, numCalled)
_, _ = c.Get("ok", create)
c.Delete("ok")
assert.Equal(t, 2, numCalled)
_, _ = c.Get("ok", create)
c.DeletePrefix("ok")
assert.Equal(t, 3, numCalled)
_, _ = c.Get("old", create)
_, _ = c.Get("new", create)
c.Rename("old", "new")
assert.Equal(t, 4, numCalled)
c.expireDuration = 1 * time.Millisecond
_, _ = c.Get("ok", create)
time.Sleep(2 * time.Millisecond)
c.cacheExpire() // "ok" and "new" fall out of cache
assert.Equal(t, 6, numCalled)
}