git.deanishe.net/deanishe/awgo.git@v0.15.0/cache_test.go (about)

     1  //
     2  // Copyright (c) 2017 Dean Jackson <deanishe@deanishe.net>
     3  //
     4  // MIT Licence. See http://opensource.org/licenses/MIT
     5  //
     6  // Created on 2017-08-08
     7  //
     8  
     9  package aw
    10  
    11  import (
    12  	"bytes"
    13  	"fmt"
    14  	"os"
    15  	"path/filepath"
    16  	"testing"
    17  	"time"
    18  
    19  	"github.com/deanishe/awgo/util"
    20  )
    21  
    22  // WithTempDir creates a temporary directory, calls function fn, then deletes the directory.
    23  func WithTempDir(fn func(dir string)) {
    24  	root := os.TempDir()
    25  	p := filepath.Join(root, fmt.Sprintf("awgo-%d.%d", os.Getpid(), time.Now().Nanosecond()))
    26  	util.MustExist(p)
    27  	defer os.RemoveAll(p)
    28  	fn(p)
    29  }
    30  
    31  // TestStoreAndLoad checks that data are stored and loaded correctly
    32  func TestStoreAndLoad(t *testing.T) {
    33  	WithTempDir(func(dir string) {
    34  		c := NewCache(dir)
    35  		s := "this is a test"
    36  		n := "test.txt"
    37  
    38  		// Sanity checks
    39  		p := c.path(n)
    40  		if util.PathExists(p) {
    41  			t.Errorf("cache file already exists: %s", p)
    42  		}
    43  
    44  		// Delete non-existent store
    45  		if err := c.Store(n, nil); err != nil {
    46  			t.Errorf("unexpected error clearing cache: %v", err)
    47  		}
    48  
    49  		// Non-existent cache exists
    50  		if c.Exists(n) {
    51  			t.Errorf("non-existent cache exists")
    52  		}
    53  
    54  		// Non-existent cache has expired
    55  		if !c.Expired(n, 0) {
    56  			t.Errorf("non-existent cache hasn't expired")
    57  		}
    58  
    59  		// Store data
    60  		data := []byte(s)
    61  		if err := c.Store(n, data); err != nil {
    62  			t.Errorf("couldn't cache data to %s: %v", n, err)
    63  		}
    64  		if !util.PathExists(p) {
    65  			t.Errorf("cache file does not exist: %s", p)
    66  		}
    67  
    68  		if c.Exists(n) != util.PathExists(p) {
    69  			t.Errorf("cache file does not exist: %s", p)
    70  		}
    71  
    72  		// Load data
    73  		data2, err := c.Load(n)
    74  		if err != nil {
    75  			t.Errorf("couldn't load cached data: %v", err)
    76  		}
    77  		if bytes.Compare(data, data2) != 0 {
    78  			t.Errorf("loaded data does not match saved data: expected=%v, got=%v", data, data2)
    79  		}
    80  
    81  		// Data age
    82  		age, err := c.Age(n)
    83  		if err != nil {
    84  			t.Errorf("couldn't get age of cache %s: %v", n, err)
    85  		}
    86  		if age == 0 {
    87  			t.Errorf("age is zero")
    88  		}
    89  
    90  		// Delete data
    91  		if err := c.Store(n, nil); err != nil {
    92  			t.Errorf("couldn't delete cache %s: %v", p, err)
    93  		}
    94  
    95  		_, err = c.Age(n)
    96  		if err == nil {
    97  			t.Errorf("no error getting age of non-existent cache %s: %v", n, err)
    98  		}
    99  		if !os.IsNotExist(err) {
   100  			t.Errorf("deleted cache exists %s: %v", n, err)
   101  		}
   102  
   103  		// Load non-existent cache
   104  		if _, err := c.Load(n); err == nil {
   105  			t.Errorf("no error loading non-existent cache")
   106  		}
   107  	})
   108  }
   109  
   110  // TestLoadOrStore tests LoadOrStore API.
   111  func TestLoadOrStore(t *testing.T) {
   112  	s := "this is a test"
   113  	var reloadCalled bool
   114  	reload := func() ([]byte, error) {
   115  		reloadCalled = true
   116  		return []byte(s), nil
   117  	}
   118  
   119  	WithTempDir(func(dir string) {
   120  		c := NewCache(dir)
   121  		n := "test.txt"
   122  		maxAge := time.Duration(time.Second * 1)
   123  
   124  		// Sanity checks
   125  		p := c.path(n)
   126  		if util.PathExists(p) {
   127  			t.Errorf("cache file already exists: %s", p)
   128  		}
   129  
   130  		// Cache empty
   131  		data, err := c.LoadOrStore(n, maxAge, reload)
   132  		if err != nil {
   133  			t.Errorf("couldn't load/store cached data: %v", err)
   134  		}
   135  		if bytes.Compare(data, []byte(s)) != 0 {
   136  			t.Errorf("unexpected cache data. Expected=%v, Got=%v", []byte(s), data)
   137  		}
   138  		if !reloadCalled {
   139  			t.Errorf("reload wasn't called")
   140  		}
   141  
   142  		if c.Expired(n, maxAge) {
   143  			t.Errorf("cache expired")
   144  		}
   145  
   146  		// Load cached data
   147  		reloadCalled = false
   148  		data, err = c.LoadOrStore(n, maxAge, reload)
   149  		if err != nil {
   150  			t.Errorf("couldn't load/store cached data: %v", err)
   151  		}
   152  		if bytes.Compare(data, []byte(s)) != 0 {
   153  			t.Errorf("unexpected cache data. Expected=%v, Got=%v", []byte(s), data)
   154  		}
   155  		if reloadCalled {
   156  			t.Errorf("reload was called")
   157  		}
   158  
   159  		// Load with 0 maxAge
   160  		reloadCalled = false
   161  		data, err = c.LoadOrStore(n, 0, reload)
   162  		if err != nil {
   163  			t.Errorf("couldn't load/store cached data: %v", err)
   164  		}
   165  		if bytes.Compare(data, []byte(s)) != 0 {
   166  			t.Errorf("unexpected cache data. Expected=%v, Got=%v", []byte(s), data)
   167  		}
   168  		if reloadCalled {
   169  			t.Errorf("reload was called")
   170  		}
   171  
   172  		time.Sleep(time.Duration(time.Second * 1))
   173  
   174  		if !c.Expired(n, maxAge) {
   175  			t.Errorf("cache hasn't expired")
   176  		}
   177  
   178  		// Reload data
   179  		reloadCalled = false
   180  		data, err = c.LoadOrStore(n, maxAge, reload)
   181  		if err != nil {
   182  			t.Errorf("couldn't load/store cached data: %v", err)
   183  		}
   184  		if bytes.Compare(data, []byte(s)) != 0 {
   185  			t.Errorf("unexpected cache data. Expected=%v, Got=%v", []byte(s), data)
   186  		}
   187  		if !reloadCalled {
   188  			t.Errorf("reload wasn't called")
   189  		}
   190  	})
   191  }
   192  
   193  // TestData is for testing JSON serialisation.
   194  type TestData struct {
   195  	A string
   196  	B string
   197  }
   198  
   199  func (td *TestData) Eq(other *TestData) bool {
   200  	if td.A != other.A {
   201  		return false
   202  	}
   203  	if td.B != other.B {
   204  		return false
   205  	}
   206  	return true
   207  }
   208  
   209  // TestStoreJSON round-trips data through the JSON caching API.
   210  func TestStoreJSON(t *testing.T) {
   211  	WithTempDir(func(dir string) {
   212  		n := "test.json"
   213  		c := NewCache(dir)
   214  		p := c.path(n)
   215  
   216  		// Delete non-existent store
   217  		if err := c.StoreJSON(n, nil); err != nil {
   218  			t.Errorf("unexpected error clearing cache: %v", err)
   219  		}
   220  
   221  		a := &TestData{"one", "two"}
   222  		if err := c.StoreJSON(n, a); err != nil {
   223  			t.Errorf("couldn't store JSON: %v", err)
   224  		}
   225  
   226  		if !util.PathExists(p) {
   227  			t.Errorf("cache doesn't exist")
   228  		}
   229  
   230  		b := &TestData{}
   231  		if err := c.LoadJSON(n, b); err != nil {
   232  			t.Errorf("couldn't load cached JSON: %v", err)
   233  		}
   234  
   235  		if !b.Eq(a) {
   236  			t.Errorf("unexpected data. Expected=%+v, Got=%+v", a, b)
   237  		}
   238  
   239  		// Delete store
   240  		if err := c.StoreJSON(n, nil); err != nil {
   241  			t.Errorf("unexpected error clearing cache: %v", err)
   242  		}
   243  
   244  		if util.PathExists(p) {
   245  			t.Errorf("couldn't delete cache %s", p)
   246  		}
   247  
   248  		// Try to load non-existent cache
   249  		b = &TestData{}
   250  		if err := c.LoadJSON(n, b); err == nil {
   251  			t.Errorf("no error loading non-existent cache")
   252  		}
   253  	})
   254  }
   255  
   256  // TestLoadOrStoreJSON tests JSON serialisation.
   257  func TestLoadOrStoreJSON(t *testing.T) {
   258  	var reloadCalled bool
   259  	var a, b *TestData
   260  
   261  	reload := func() (interface{}, error) {
   262  		reloadCalled = true
   263  		return &TestData{"one", "two"}, nil
   264  	}
   265  
   266  	WithTempDir(func(dir string) {
   267  		n := "test.json"
   268  		c := NewCache(dir)
   269  		maxAge := time.Duration(time.Second * 1)
   270  
   271  		// Sanity checks
   272  		p := c.path(n)
   273  		if util.PathExists(p) {
   274  			t.Errorf("cache file already exists: %s", p)
   275  		}
   276  
   277  		a = &TestData{"one", "two"}
   278  		b = &TestData{}
   279  		// Cache empty
   280  		err := c.LoadOrStoreJSON(n, maxAge, reload, b)
   281  		if err != nil {
   282  			t.Errorf("couldn't load/store cached data: %v", err)
   283  		}
   284  		if !a.Eq(b) {
   285  			t.Errorf("unexpected cache data. Expected=%v, Got=%v", a, b)
   286  		}
   287  		if !reloadCalled {
   288  			t.Errorf("reload wasn't called")
   289  		}
   290  
   291  		if c.Expired(n, maxAge) {
   292  			t.Errorf("cache expired")
   293  		}
   294  
   295  		// Load cached data
   296  		reloadCalled = false
   297  		a = &TestData{"one", "two"}
   298  		b = &TestData{}
   299  		err = c.LoadOrStoreJSON(n, maxAge, reload, b)
   300  		if err != nil {
   301  			t.Errorf("couldn't load/store cached data: %v", err)
   302  		}
   303  		if !b.Eq(a) {
   304  			t.Errorf("unexpected cache data. Expected=%v, Got=%v", a, b)
   305  		}
   306  		if reloadCalled {
   307  			t.Errorf("reload was called")
   308  		}
   309  
   310  		// Load with 0 maxAge
   311  		reloadCalled = false
   312  		a = &TestData{"one", "two"}
   313  		b = &TestData{}
   314  		err = c.LoadOrStoreJSON(n, 0, reload, b)
   315  		if err != nil {
   316  			t.Errorf("couldn't load/store cached data: %v", err)
   317  		}
   318  		if !b.Eq(a) {
   319  			t.Errorf("unexpected cache data. Expected=%v, Got=%v", a, b)
   320  		}
   321  		if reloadCalled {
   322  			t.Errorf("reload was called")
   323  		}
   324  
   325  		time.Sleep(time.Duration(time.Second * 1))
   326  
   327  		if !c.Expired(n, maxAge) {
   328  			t.Errorf("cache hasn't expired")
   329  		}
   330  
   331  		// Reload data
   332  		reloadCalled = false
   333  		a = &TestData{"one", "two"}
   334  		b = &TestData{}
   335  		err = c.LoadOrStoreJSON(n, maxAge, reload, b)
   336  		if err != nil {
   337  			t.Errorf("couldn't load/store cached data: %v", err)
   338  		}
   339  		if !b.Eq(a) {
   340  			t.Errorf("unexpected cache data. Expected=%v, Got=%v", a, b)
   341  		}
   342  		if !reloadCalled {
   343  			t.Errorf("reload wasn't called")
   344  		}
   345  	})
   346  }
   347  
   348  // TestBadReloadError checks reload funcs that return errors
   349  func TestBadReloadError(t *testing.T) {
   350  	reloadB := func() ([]byte, error) {
   351  		return nil, fmt.Errorf("an error")
   352  	}
   353  
   354  	reloadJSON := func() (interface{}, error) {
   355  		return nil, fmt.Errorf("an error")
   356  	}
   357  
   358  	WithTempDir(func(dir string) {
   359  		c := NewCache(dir)
   360  		n := "test"
   361  		if _, err := c.LoadOrStore(n, 0, reloadB); err == nil {
   362  			t.Error("no error returned by reloadB")
   363  		}
   364  		v := &TestData{}
   365  		if err := c.LoadOrStoreJSON(n, 0, reloadJSON, v); err == nil {
   366  			t.Error("no error returned by reloadJSON")
   367  		}
   368  	})
   369  }
   370  
   371  // TestSession tests session-scoped caching.
   372  func TestSession(t *testing.T) {
   373  	WithTempDir(func(dir string) {
   374  		sid := NewSessionID()
   375  		s := NewSession(dir, sid)
   376  		data := []byte("this is a test")
   377  		n := "test.txt"
   378  
   379  		// Sanity checks
   380  		p := s.cache.path(s.name(n))
   381  		if util.PathExists(p) {
   382  			t.Errorf("cache file already exists: %s", p)
   383  		}
   384  
   385  		// Delete non-existent store
   386  		if err := s.Store(n, nil); err != nil {
   387  			t.Errorf("unexpected error clearing cache: %v", err)
   388  		}
   389  
   390  		// Non-existent cache exists
   391  		if s.Exists(n) {
   392  			t.Errorf("non-existent cache exists")
   393  		}
   394  
   395  		// Store data
   396  		if err := s.Store(n, data); err != nil {
   397  			t.Errorf("couldn't cache data to %s: %v", n, err)
   398  		}
   399  		if !util.PathExists(p) {
   400  			t.Errorf("cache file does not exist: %s", p)
   401  		}
   402  
   403  		if s.Exists(n) != util.PathExists(p) {
   404  			t.Errorf("cache file does not exist: %s", p)
   405  		}
   406  
   407  		// Load data
   408  		data2, err := s.Load(n)
   409  		if err != nil {
   410  			t.Errorf("couldn't load cached data: %v", err)
   411  		}
   412  		if bytes.Compare(data, data2) != 0 {
   413  			t.Errorf("loaded data does not match saved data: expected=%v, got=%v", data, data2)
   414  		}
   415  
   416  		// Clear session
   417  		s.Clear(false) // Leave current session data
   418  		if !util.PathExists(p) {
   419  			t.Errorf("cache file does not exist: %s", p)
   420  		}
   421  		// Clear this session's data, too
   422  		s.Clear(true)
   423  		if util.PathExists(p) {
   424  			t.Errorf("cache file exists: %s", p)
   425  		}
   426  
   427  		// Load non-existent cache
   428  		if _, err := s.Load(n); err == nil {
   429  			t.Errorf("no error loading non-existent cache")
   430  		}
   431  
   432  		// Clear old sessions
   433  		sid1 := NewSessionID()
   434  		sid2 := NewSessionID()
   435  		s = NewSession(dir, sid1)
   436  		s.Store(n, data)
   437  
   438  		if !s.Exists(n) {
   439  			t.Errorf("cached data do not exist: %s", n)
   440  		}
   441  
   442  		s = NewSession(dir, sid2)
   443  		s.Clear(false)
   444  
   445  		if s.Exists(n) {
   446  			t.Errorf("expired data still exist: %s", n)
   447  		}
   448  	})
   449  }