github.com/10XDev/rclone@v1.52.3-0.20200626220027-16af9ab76b2a/lib/cache/cache_test.go (about)

     1  package cache
     2  
     3  import (
     4  	"errors"
     5  	"fmt"
     6  	"testing"
     7  	"time"
     8  
     9  	"github.com/stretchr/testify/assert"
    10  	"github.com/stretchr/testify/require"
    11  )
    12  
    13  var (
    14  	called      = 0
    15  	errSentinel = errors.New("an error")
    16  	errCached   = errors.New("a cached error")
    17  )
    18  
    19  func setup(t *testing.T) (*Cache, CreateFunc) {
    20  	called = 0
    21  	create := func(path string) (interface{}, bool, error) {
    22  		assert.Equal(t, 0, called)
    23  		called++
    24  		switch path {
    25  		case "/":
    26  			return "/", true, nil
    27  		case "/file.txt":
    28  			return "/file.txt", true, errCached
    29  		case "/error":
    30  			return nil, false, errSentinel
    31  		}
    32  		panic(fmt.Sprintf("Unknown path %q", path))
    33  	}
    34  	c := New()
    35  	return c, create
    36  }
    37  
    38  func TestGet(t *testing.T) {
    39  	c, create := setup(t)
    40  
    41  	assert.Equal(t, 0, len(c.cache))
    42  
    43  	f, err := c.Get("/", create)
    44  	require.NoError(t, err)
    45  
    46  	assert.Equal(t, 1, len(c.cache))
    47  
    48  	f2, err := c.Get("/", create)
    49  	require.NoError(t, err)
    50  
    51  	assert.Equal(t, f, f2)
    52  }
    53  
    54  func TestGetFile(t *testing.T) {
    55  	c, create := setup(t)
    56  
    57  	assert.Equal(t, 0, len(c.cache))
    58  
    59  	f, err := c.Get("/file.txt", create)
    60  	require.Equal(t, errCached, err)
    61  
    62  	assert.Equal(t, 1, len(c.cache))
    63  
    64  	f2, err := c.Get("/file.txt", create)
    65  	require.Equal(t, errCached, err)
    66  
    67  	assert.Equal(t, f, f2)
    68  }
    69  
    70  func TestGetError(t *testing.T) {
    71  	c, create := setup(t)
    72  
    73  	assert.Equal(t, 0, len(c.cache))
    74  
    75  	f, err := c.Get("/error", create)
    76  	require.Equal(t, errSentinel, err)
    77  	require.Equal(t, nil, f)
    78  
    79  	assert.Equal(t, 0, len(c.cache))
    80  }
    81  
    82  func TestPut(t *testing.T) {
    83  	c, create := setup(t)
    84  
    85  	assert.Equal(t, 0, len(c.cache))
    86  
    87  	c.Put("/alien", "slime")
    88  
    89  	assert.Equal(t, 1, len(c.cache))
    90  
    91  	fNew, err := c.Get("/alien", create)
    92  	require.NoError(t, err)
    93  	require.Equal(t, "slime", fNew)
    94  
    95  	assert.Equal(t, 1, len(c.cache))
    96  }
    97  
    98  func TestCacheExpire(t *testing.T) {
    99  	c, create := setup(t)
   100  
   101  	c.expireInterval = time.Millisecond
   102  	assert.Equal(t, false, c.expireRunning)
   103  
   104  	_, err := c.Get("/", create)
   105  	require.NoError(t, err)
   106  
   107  	c.mu.Lock()
   108  	entry := c.cache["/"]
   109  	assert.Equal(t, 1, len(c.cache))
   110  	c.mu.Unlock()
   111  
   112  	c.cacheExpire()
   113  
   114  	c.mu.Lock()
   115  	assert.Equal(t, 1, len(c.cache))
   116  	entry.lastUsed = time.Now().Add(-c.expireDuration - 60*time.Second)
   117  	assert.Equal(t, true, c.expireRunning)
   118  	c.mu.Unlock()
   119  
   120  	time.Sleep(250 * time.Millisecond)
   121  
   122  	c.mu.Lock()
   123  	assert.Equal(t, false, c.expireRunning)
   124  	assert.Equal(t, 0, len(c.cache))
   125  	c.mu.Unlock()
   126  }
   127  
   128  func TestCachePin(t *testing.T) {
   129  	c, create := setup(t)
   130  
   131  	_, err := c.Get("/", create)
   132  	require.NoError(t, err)
   133  
   134  	// Pin a non existent item to show nothing happens
   135  	c.Pin("notfound")
   136  
   137  	c.mu.Lock()
   138  	entry := c.cache["/"]
   139  	assert.Equal(t, 1, len(c.cache))
   140  	c.mu.Unlock()
   141  
   142  	c.cacheExpire()
   143  
   144  	c.mu.Lock()
   145  	assert.Equal(t, 1, len(c.cache))
   146  	c.mu.Unlock()
   147  
   148  	// Pin the entry and check it does not get expired
   149  	c.Pin("/")
   150  
   151  	// Reset last used to make the item expirable
   152  	c.mu.Lock()
   153  	entry.lastUsed = time.Now().Add(-c.expireDuration - 60*time.Second)
   154  	c.mu.Unlock()
   155  
   156  	c.cacheExpire()
   157  
   158  	c.mu.Lock()
   159  	assert.Equal(t, 1, len(c.cache))
   160  	c.mu.Unlock()
   161  
   162  	// Unpin the entry and check it does get expired now
   163  	c.Unpin("/")
   164  
   165  	// Reset last used
   166  	c.mu.Lock()
   167  	entry.lastUsed = time.Now().Add(-c.expireDuration - 60*time.Second)
   168  	c.mu.Unlock()
   169  
   170  	c.cacheExpire()
   171  
   172  	c.mu.Lock()
   173  	assert.Equal(t, 0, len(c.cache))
   174  	c.mu.Unlock()
   175  }
   176  
   177  func TestClear(t *testing.T) {
   178  	c, create := setup(t)
   179  
   180  	assert.Equal(t, 0, len(c.cache))
   181  
   182  	_, err := c.Get("/", create)
   183  	require.NoError(t, err)
   184  
   185  	assert.Equal(t, 1, len(c.cache))
   186  
   187  	c.Clear()
   188  
   189  	assert.Equal(t, 0, len(c.cache))
   190  }
   191  
   192  func TestEntries(t *testing.T) {
   193  	c, create := setup(t)
   194  
   195  	assert.Equal(t, 0, c.Entries())
   196  
   197  	_, err := c.Get("/", create)
   198  	require.NoError(t, err)
   199  
   200  	assert.Equal(t, 1, c.Entries())
   201  
   202  	c.Clear()
   203  
   204  	assert.Equal(t, 0, c.Entries())
   205  }
   206  
   207  func TestGetMaybe(t *testing.T) {
   208  	c, create := setup(t)
   209  
   210  	value, found := c.GetMaybe("/")
   211  	assert.Equal(t, false, found)
   212  	assert.Nil(t, value)
   213  
   214  	f, err := c.Get("/", create)
   215  	require.NoError(t, err)
   216  
   217  	value, found = c.GetMaybe("/")
   218  	assert.Equal(t, true, found)
   219  	assert.Equal(t, f, value)
   220  
   221  	c.Clear()
   222  
   223  	value, found = c.GetMaybe("/")
   224  	assert.Equal(t, false, found)
   225  	assert.Nil(t, value)
   226  }
   227  
   228  func TestCacheRename(t *testing.T) {
   229  	c := New()
   230  	create := func(path string) (interface{}, bool, error) {
   231  		return path, true, nil
   232  	}
   233  
   234  	existing1, err := c.Get("existing1", create)
   235  	require.NoError(t, err)
   236  	_, err = c.Get("existing2", create)
   237  	require.NoError(t, err)
   238  
   239  	assert.Equal(t, 2, c.Entries())
   240  
   241  	// rename to non existent
   242  	value, found := c.Rename("existing1", "EXISTING1")
   243  	assert.Equal(t, true, found)
   244  	assert.Equal(t, existing1, value)
   245  
   246  	assert.Equal(t, 2, c.Entries())
   247  
   248  	// rename to existent and check existing value is returned
   249  	value, found = c.Rename("existing2", "EXISTING1")
   250  	assert.Equal(t, true, found)
   251  	assert.Equal(t, existing1, value)
   252  
   253  	assert.Equal(t, 1, c.Entries())
   254  
   255  	// rename non existent
   256  	value, found = c.Rename("notfound", "NOTFOUND")
   257  	assert.Equal(t, false, found)
   258  	assert.Nil(t, value)
   259  
   260  	assert.Equal(t, 1, c.Entries())
   261  }