remove dependency on expect library

This commit is contained in:
Karl Seguin
2022-03-03 09:01:45 +08:00
parent 92cae1a07a
commit 95f74b4e85
9 changed files with 405 additions and 344 deletions

106
assert/assert.go Normal file
View File

@@ -0,0 +1,106 @@
// A wrapper around *testing.T. I hate the if a != b { t.ErrorF(....) } pattern.
// Packages should prefer using the tests package (which exposes all of
// these functions). The only reason to use this package directly is if
// the tests package depends on your package (and thus you have a cyclical
// dependency)
package assert
import (
"math"
"reflect"
"strings"
"testing"
"time"
)
// a == b
func Equal[T comparable](t *testing.T, actual T, expected T) {
t.Helper()
if actual != expected {
t.Errorf("expected '%v' to equal '%v'", actual, expected)
t.FailNow()
}
}
// Two lists are equal (same length & same values in the same order)
func List[T comparable](t *testing.T, actuals []T, expecteds []T) {
t.Helper()
Equal(t, len(actuals), len(expecteds))
for i, actual := range actuals {
Equal(t, actual, expecteds[i])
}
}
// needle not in []haystack
func DoesNotContain[T comparable](t *testing.T, haystack []T, needle T) {
t.Helper()
for _, v := range haystack {
if v == needle {
t.Errorf("expected '%v' to not be in '%v'", needle, haystack)
t.FailNow()
}
}
}
// A value is nil
func Nil(t *testing.T, actual interface{}) {
t.Helper()
if actual != nil && !reflect.ValueOf(actual).IsNil() {
t.Errorf("expected %v to be nil", actual)
t.FailNow()
}
}
// A value is not nil
func NotNil(t *testing.T, actual interface{}) {
t.Helper()
if actual == nil {
t.Errorf("expected %v to be not nil", actual)
t.FailNow()
}
}
// A value is true
func True(t *testing.T, actual bool) {
t.Helper()
if !actual {
t.Error("expected true, got false")
t.FailNow()
}
}
// A value is false
func False(t *testing.T, actual bool) {
t.Helper()
if actual {
t.Error("expected false, got true")
t.FailNow()
}
}
// The string contains the given value
func StringContains(t *testing.T, actual string, expected string) {
t.Helper()
if !strings.Contains(actual, expected) {
t.Errorf("expected %s to contain %s", actual, expected)
t.FailNow()
}
}
func Error(t *testing.T, actual error, expected error) {
t.Helper()
if actual != expected {
t.Errorf("expected '%s' to be '%s'", actual, expected)
t.FailNow()
}
}
func Nowish(t *testing.T, actual time.Time) {
t.Helper()
diff := math.Abs(time.Now().UTC().Sub(actual).Seconds())
if diff > 1 {
t.Errorf("expected '%s' to be nowish", actual)
t.FailNow()
}
}

View File

@@ -4,49 +4,42 @@ import (
"testing"
"time"
. "github.com/karlseguin/expect"
"github.com/karlseguin/ccache/v3/assert"
)
type BucketTests struct {
}
func Test_Bucket(t *testing.T) {
Expectify(new(BucketTests), t)
}
func (_ *BucketTests) GetMissFromBucket() {
func Test_Bucket_GetMissFromBucket(t *testing.T) {
bucket := testBucket()
Expect(bucket.get("invalid")).To.Equal(nil)
assert.Nil(t, bucket.get("invalid"))
}
func (_ *BucketTests) GetHitFromBucket() {
func Test_Bucket_GetHitFromBucket(t *testing.T) {
bucket := testBucket()
item := bucket.get("power")
assertValue(item, "9000")
assertValue(t, item, "9000")
}
func (_ *BucketTests) DeleteItemFromBucket() {
func Test_Bucket_DeleteItemFromBucket(t *testing.T) {
bucket := testBucket()
bucket.delete("power")
Expect(bucket.get("power")).To.Equal(nil)
assert.Nil(t, bucket.get("power"))
}
func (_ *BucketTests) SetsANewBucketItem() {
func Test_Bucket_SetsANewBucketItem(t *testing.T) {
bucket := testBucket()
item, existing := bucket.set("spice", "flow", time.Minute, false)
assertValue(item, "flow")
assertValue(t, item, "flow")
item = bucket.get("spice")
assertValue(item, "flow")
Expect(existing).To.Equal(nil)
assertValue(t, item, "flow")
assert.Equal(t, existing, nil)
}
func (_ *BucketTests) SetsAnExistingItem() {
func Test_Bucket_SetsAnExistingItem(t *testing.T) {
bucket := testBucket()
item, existing := bucket.set("power", "9001", time.Minute, false)
assertValue(item, "9001")
assertValue(t, item, "9001")
item = bucket.get("power")
assertValue(item, "9001")
assertValue(existing, "9000")
assertValue(t, item, "9001")
assertValue(t, existing, "9000")
}
func testBucket() *bucket[string] {
@@ -58,6 +51,6 @@ func testBucket() *bucket[string] {
return b
}
func assertValue(item *Item[string], expected string) {
Expect(item.value).To.Equal(expected)
func assertValue(t *testing.T, item *Item[string], expected string) {
assert.Equal(t, item.value, expected)
}

View File

@@ -7,58 +7,52 @@ import (
"testing"
"time"
. "github.com/karlseguin/expect"
"github.com/karlseguin/ccache/v3/assert"
)
type CacheTests struct{}
func Test_Cache(t *testing.T) {
Expectify(new(CacheTests), t)
}
func (_ CacheTests) DeletesAValue() {
func Test_CacheDeletesAValue(t *testing.T) {
cache := New[string](Configure[string]())
defer cache.Stop()
Expect(cache.ItemCount()).To.Equal(0)
assert.Equal(t, cache.ItemCount(), 0)
cache.Set("spice", "flow", time.Minute)
cache.Set("worm", "sand", time.Minute)
Expect(cache.ItemCount()).To.Equal(2)
assert.Equal(t, cache.ItemCount(), 2)
cache.Delete("spice")
Expect(cache.Get("spice")).To.Equal(nil)
Expect(cache.Get("worm").Value()).To.Equal("sand")
Expect(cache.ItemCount()).To.Equal(1)
assert.Equal(t, cache.Get("spice"), nil)
assert.Equal(t, cache.Get("worm").Value(), "sand")
assert.Equal(t, cache.ItemCount(), 1)
}
func (_ CacheTests) DeletesAPrefix() {
func Test_CacheDeletesAPrefix(t *testing.T) {
cache := New[string](Configure[string]())
defer cache.Stop()
Expect(cache.ItemCount()).To.Equal(0)
assert.Equal(t, cache.ItemCount(), 0)
cache.Set("aaa", "1", time.Minute)
cache.Set("aab", "2", time.Minute)
cache.Set("aac", "3", time.Minute)
cache.Set("ac", "4", time.Minute)
cache.Set("z5", "7", time.Minute)
Expect(cache.ItemCount()).To.Equal(5)
assert.Equal(t, cache.ItemCount(), 5)
Expect(cache.DeletePrefix("9a")).To.Equal(0)
Expect(cache.ItemCount()).To.Equal(5)
assert.Equal(t, cache.DeletePrefix("9a"), 0)
assert.Equal(t, cache.ItemCount(), 5)
Expect(cache.DeletePrefix("aa")).To.Equal(3)
Expect(cache.Get("aaa")).To.Equal(nil)
Expect(cache.Get("aab")).To.Equal(nil)
Expect(cache.Get("aac")).To.Equal(nil)
Expect(cache.Get("ac").Value()).To.Equal("4")
Expect(cache.Get("z5").Value()).To.Equal("7")
Expect(cache.ItemCount()).To.Equal(2)
assert.Equal(t, cache.DeletePrefix("aa"), 3)
assert.Equal(t, cache.Get("aaa"), nil)
assert.Equal(t, cache.Get("aab"), nil)
assert.Equal(t, cache.Get("aac"), nil)
assert.Equal(t, cache.Get("ac").Value(), "4")
assert.Equal(t, cache.Get("z5").Value(), "7")
assert.Equal(t, cache.ItemCount(), 2)
}
func (_ CacheTests) DeletesAFunc() {
func Test_CacheDeletesAFunc(t *testing.T) {
cache := New[int](Configure[int]())
defer cache.Stop()
Expect(cache.ItemCount()).To.Equal(0)
assert.Equal(t, cache.ItemCount(), 0)
cache.Set("a", 1, time.Minute)
cache.Set("b", 2, time.Minute)
@@ -66,26 +60,26 @@ func (_ CacheTests) DeletesAFunc() {
cache.Set("d", 4, time.Minute)
cache.Set("e", 5, time.Minute)
cache.Set("f", 6, time.Minute)
Expect(cache.ItemCount()).To.Equal(6)
assert.Equal(t, cache.ItemCount(), 6)
Expect(cache.DeleteFunc(func(key string, item *Item[int]) bool {
assert.Equal(t, cache.DeleteFunc(func(key string, item *Item[int]) bool {
return false
})).To.Equal(0)
Expect(cache.ItemCount()).To.Equal(6)
}), 0)
assert.Equal(t, cache.ItemCount(), 6)
Expect(cache.DeleteFunc(func(key string, item *Item[int]) bool {
assert.Equal(t, cache.DeleteFunc(func(key string, item *Item[int]) bool {
return item.Value() < 4
})).To.Equal(3)
Expect(cache.ItemCount()).To.Equal(3)
}), 3)
assert.Equal(t, cache.ItemCount(), 3)
Expect(cache.DeleteFunc(func(key string, item *Item[int]) bool {
assert.Equal(t, cache.DeleteFunc(func(key string, item *Item[int]) bool {
return key == "d"
})).To.Equal(1)
Expect(cache.ItemCount()).To.Equal(2)
}), 1)
assert.Equal(t, cache.ItemCount(), 2)
}
func (_ CacheTests) OnDeleteCallbackCalled() {
func Test_CacheOnDeleteCallbackCalled(t *testing.T) {
onDeleteFnCalled := int32(0)
onDeleteFn := func(item *Item[string]) {
if item.key == "spice" {
@@ -102,35 +96,35 @@ func (_ CacheTests) OnDeleteCallbackCalled() {
cache.Delete("spice")
cache.SyncUpdates()
Expect(cache.Get("spice")).To.Equal(nil)
Expect(cache.Get("worm").Value()).To.Equal("sand")
Expect(atomic.LoadInt32(&onDeleteFnCalled)).To.Eql(1)
assert.Equal(t, cache.Get("spice"), nil)
assert.Equal(t, cache.Get("worm").Value(), "sand")
assert.Equal(t, atomic.LoadInt32(&onDeleteFnCalled), 1)
}
func (_ CacheTests) FetchesExpiredItems() {
func Test_CacheFetchesExpiredItems(t *testing.T) {
cache := New[string](Configure[string]())
fn := func() (string, error) { return "moo-moo", nil }
cache.Set("beef", "moo", time.Second*-1)
Expect(cache.Get("beef").Value()).To.Equal("moo")
assert.Equal(t, cache.Get("beef").Value(), "moo")
out, _ := cache.Fetch("beef", time.Second, fn)
Expect(out.Value()).To.Equal("moo-moo")
assert.Equal(t, out.Value(), "moo-moo")
}
func (_ CacheTests) GCsTheOldestItems() {
func Test_CacheGCsTheOldestItems(t *testing.T) {
cache := New[int](Configure[int]().ItemsToPrune(10))
for i := 0; i < 500; i++ {
cache.Set(strconv.Itoa(i), i, time.Minute)
}
cache.SyncUpdates()
cache.GC()
Expect(cache.Get("9")).To.Equal(nil)
Expect(cache.Get("10").Value()).To.Equal(10)
Expect(cache.ItemCount()).To.Equal(490)
assert.Equal(t, cache.Get("9"), nil)
assert.Equal(t, cache.Get("10").Value(), 10)
assert.Equal(t, cache.ItemCount(), 490)
}
func (_ CacheTests) PromotedItemsDontGetPruned() {
func Test_CachePromotedItemsDontGetPruned(t *testing.T) {
cache := New[int](Configure[int]().ItemsToPrune(10).GetsPerPromote(1))
for i := 0; i < 500; i++ {
cache.Set(strconv.Itoa(i), i, time.Minute)
@@ -139,12 +133,12 @@ func (_ CacheTests) PromotedItemsDontGetPruned() {
cache.Get("9")
cache.SyncUpdates()
cache.GC()
Expect(cache.Get("9").Value()).To.Equal(9)
Expect(cache.Get("10")).To.Equal(nil)
Expect(cache.Get("11").Value()).To.Equal(11)
assert.Equal(t, cache.Get("9").Value(), 9)
assert.Equal(t, cache.Get("10"), nil)
assert.Equal(t, cache.Get("11").Value(), 11)
}
func (_ CacheTests) TrackerDoesNotCleanupHeldInstance() {
func Test_CacheTrackerDoesNotCleanupHeldInstance(t *testing.T) {
cache := New[int](Configure[int]().ItemsToPrune(11).Track())
item0 := cache.TrackingSet("0", 0, time.Minute)
for i := 1; i < 11; i++ {
@@ -153,16 +147,16 @@ func (_ CacheTests) TrackerDoesNotCleanupHeldInstance() {
item1 := cache.TrackingGet("1")
cache.SyncUpdates()
cache.GC()
Expect(cache.Get("0").Value()).To.Equal(0)
Expect(cache.Get("1").Value()).To.Equal(1)
assert.Equal(t, cache.Get("0").Value(), 0)
assert.Equal(t, cache.Get("1").Value(), 1)
item0.Release()
item1.Release()
cache.GC()
Expect(cache.Get("0")).To.Equal(nil)
Expect(cache.Get("1")).To.Equal(nil)
assert.Equal(t, cache.Get("0"), nil)
assert.Equal(t, cache.Get("1"), nil)
}
func (_ CacheTests) RemovesOldestItemWhenFull() {
func Test_CacheRemovesOldestItemWhenFull(t *testing.T) {
onDeleteFnCalled := false
onDeleteFn := func(item *Item[int]) {
if item.key == "0" {
@@ -175,134 +169,134 @@ func (_ CacheTests) RemovesOldestItemWhenFull() {
cache.Set(strconv.Itoa(i), i, time.Minute)
}
cache.SyncUpdates()
Expect(cache.Get("0")).To.Equal(nil)
Expect(cache.Get("1")).To.Equal(nil)
Expect(cache.Get("2").Value()).To.Equal(2)
Expect(onDeleteFnCalled).To.Equal(true)
Expect(cache.ItemCount()).To.Equal(5)
assert.Equal(t, cache.Get("0"), nil)
assert.Equal(t, cache.Get("1"), nil)
assert.Equal(t, cache.Get("2").Value(), 2)
assert.Equal(t, onDeleteFnCalled, true)
assert.Equal(t, cache.ItemCount(), 5)
}
func (_ CacheTests) RemovesOldestItemWhenFullBySizer() {
func Test_CacheRemovesOldestItemWhenFullBySizer(t *testing.T) {
cache := New[*SizedItem](Configure[*SizedItem]().MaxSize(9).ItemsToPrune(2))
for i := 0; i < 7; i++ {
cache.Set(strconv.Itoa(i), &SizedItem{i, 2}, time.Minute)
}
cache.SyncUpdates()
Expect(cache.Get("0")).To.Equal(nil)
Expect(cache.Get("1")).To.Equal(nil)
Expect(cache.Get("2")).To.Equal(nil)
Expect(cache.Get("3")).To.Equal(nil)
Expect(cache.Get("4").Value().id).To.Equal(4)
Expect(cache.GetDropped()).To.Equal(4)
Expect(cache.GetDropped()).To.Equal(0)
assert.Equal(t, cache.Get("0"), nil)
assert.Equal(t, cache.Get("1"), nil)
assert.Equal(t, cache.Get("2"), nil)
assert.Equal(t, cache.Get("3"), nil)
assert.Equal(t, cache.Get("4").Value().id, 4)
assert.Equal(t, cache.GetDropped(), 4)
assert.Equal(t, cache.GetDropped(), 0)
}
func (_ CacheTests) SetUpdatesSizeOnDelta() {
func Test_CacheSetUpdatesSizeOnDelta(t *testing.T) {
cache := New[*SizedItem](Configure[*SizedItem]())
cache.Set("a", &SizedItem{0, 2}, time.Minute)
cache.Set("b", &SizedItem{0, 3}, time.Minute)
cache.SyncUpdates()
Expect(cache.GetSize()).To.Eql(5)
assert.Equal(t, cache.GetSize(), 5)
cache.Set("b", &SizedItem{0, 3}, time.Minute)
cache.SyncUpdates()
Expect(cache.GetSize()).To.Eql(5)
assert.Equal(t, cache.GetSize(), 5)
cache.Set("b", &SizedItem{0, 4}, time.Minute)
cache.SyncUpdates()
Expect(cache.GetSize()).To.Eql(6)
assert.Equal(t, cache.GetSize(), 6)
cache.Set("b", &SizedItem{0, 2}, time.Minute)
cache.SyncUpdates()
Expect(cache.GetSize()).To.Eql(4)
assert.Equal(t, cache.GetSize(), 4)
cache.Delete("b")
cache.SyncUpdates()
Expect(cache.GetSize()).To.Eql(2)
assert.Equal(t, cache.GetSize(), 2)
}
func (_ CacheTests) ReplaceDoesNotchangeSizeIfNotSet() {
func Test_CacheReplaceDoesNotchangeSizeIfNotSet(t *testing.T) {
cache := New[*SizedItem](Configure[*SizedItem]())
cache.Set("1", &SizedItem{1, 2}, time.Minute)
cache.Set("2", &SizedItem{1, 2}, time.Minute)
cache.Set("3", &SizedItem{1, 2}, time.Minute)
cache.Replace("4", &SizedItem{1, 2})
cache.SyncUpdates()
Expect(cache.GetSize()).To.Eql(6)
assert.Equal(t, cache.GetSize(), 6)
}
func (_ CacheTests) ReplaceChangesSize() {
func Test_CacheReplaceChangesSize(t *testing.T) {
cache := New[*SizedItem](Configure[*SizedItem]())
cache.Set("1", &SizedItem{1, 2}, time.Minute)
cache.Set("2", &SizedItem{1, 2}, time.Minute)
cache.Replace("2", &SizedItem{1, 2})
cache.SyncUpdates()
Expect(cache.GetSize()).To.Eql(4)
assert.Equal(t, cache.GetSize(), 4)
cache.Replace("2", &SizedItem{1, 1})
cache.SyncUpdates()
Expect(cache.GetSize()).To.Eql(3)
assert.Equal(t, cache.GetSize(), 3)
cache.Replace("2", &SizedItem{1, 3})
cache.SyncUpdates()
Expect(cache.GetSize()).To.Eql(5)
assert.Equal(t, cache.GetSize(), 5)
}
func (_ CacheTests) ResizeOnTheFly() {
func Test_CacheResizeOnTheFly(t *testing.T) {
cache := New[int](Configure[int]().MaxSize(9).ItemsToPrune(1))
for i := 0; i < 5; i++ {
cache.Set(strconv.Itoa(i), i, time.Minute)
}
cache.SetMaxSize(3)
cache.SyncUpdates()
Expect(cache.GetDropped()).To.Equal(2)
Expect(cache.Get("0")).To.Equal(nil)
Expect(cache.Get("1")).To.Equal(nil)
Expect(cache.Get("2").Value()).To.Equal(2)
Expect(cache.Get("3").Value()).To.Equal(3)
Expect(cache.Get("4").Value()).To.Equal(4)
assert.Equal(t, cache.GetDropped(), 2)
assert.Equal(t, cache.Get("0"), nil)
assert.Equal(t, cache.Get("1"), nil)
assert.Equal(t, cache.Get("2").Value(), 2)
assert.Equal(t, cache.Get("3").Value(), 3)
assert.Equal(t, cache.Get("4").Value(), 4)
cache.Set("5", 5, time.Minute)
cache.SyncUpdates()
Expect(cache.GetDropped()).To.Equal(1)
Expect(cache.Get("2")).To.Equal(nil)
Expect(cache.Get("3").Value()).To.Equal(3)
Expect(cache.Get("4").Value()).To.Equal(4)
Expect(cache.Get("5").Value()).To.Equal(5)
assert.Equal(t, cache.GetDropped(), 1)
assert.Equal(t, cache.Get("2"), nil)
assert.Equal(t, cache.Get("3").Value(), 3)
assert.Equal(t, cache.Get("4").Value(), 4)
assert.Equal(t, cache.Get("5").Value(), 5)
cache.SetMaxSize(10)
cache.Set("6", 6, time.Minute)
cache.SyncUpdates()
Expect(cache.GetDropped()).To.Equal(0)
Expect(cache.Get("3").Value()).To.Equal(3)
Expect(cache.Get("4").Value()).To.Equal(4)
Expect(cache.Get("5").Value()).To.Equal(5)
Expect(cache.Get("6").Value()).To.Equal(6)
assert.Equal(t, cache.GetDropped(), 0)
assert.Equal(t, cache.Get("3").Value(), 3)
assert.Equal(t, cache.Get("4").Value(), 4)
assert.Equal(t, cache.Get("5").Value(), 5)
assert.Equal(t, cache.Get("6").Value(), 6)
}
func (_ CacheTests) ForEachFunc() {
func Test_CacheForEachFunc(t *testing.T) {
cache := New[int](Configure[int]().MaxSize(3).ItemsToPrune(1))
Expect(forEachKeys[int](cache)).To.Equal([]string{})
assert.List(t, forEachKeys[int](cache), []string{})
cache.Set("1", 1, time.Minute)
Expect(forEachKeys(cache)).To.Equal([]string{"1"})
assert.List(t, forEachKeys(cache), []string{"1"})
cache.Set("2", 2, time.Minute)
cache.SyncUpdates()
Expect(forEachKeys(cache)).To.Equal([]string{"1", "2"})
assert.List(t, forEachKeys(cache), []string{"1", "2"})
cache.Set("3", 3, time.Minute)
cache.SyncUpdates()
Expect(forEachKeys(cache)).To.Equal([]string{"1", "2", "3"})
assert.List(t, forEachKeys(cache), []string{"1", "2", "3"})
cache.Set("4", 4, time.Minute)
cache.SyncUpdates()
Expect(forEachKeys(cache)).To.Equal([]string{"2", "3", "4"})
assert.List(t, forEachKeys(cache), []string{"2", "3", "4"})
cache.Set("stop", 5, time.Minute)
cache.SyncUpdates()
Expect(forEachKeys(cache)).Not.To.Contain("stop")
assert.DoesNotContain(t, forEachKeys(cache), "stop")
cache.Set("6", 6, time.Minute)
cache.SyncUpdates()
Expect(forEachKeys(cache)).Not.To.Contain("stop")
assert.DoesNotContain(t, forEachKeys(cache), "stop")
}
type SizedItem struct {

View File

@@ -3,22 +3,16 @@ package ccache
import (
"testing"
. "github.com/karlseguin/expect"
"github.com/karlseguin/ccache/v3/assert"
)
type ConfigurationTests struct{}
func Test_Configuration(t *testing.T) {
Expectify(new(ConfigurationTests), t)
}
func (_ *ConfigurationTests) BucketsPowerOf2() {
func Test_Configuration_BucketsPowerOf2(t *testing.T) {
for i := uint32(0); i < 31; i++ {
c := Configure[int]().Buckets(i)
if i == 1 || i == 2 || i == 4 || i == 8 || i == 16 {
Expect(c.buckets).ToEqual(int(i))
assert.Equal(t, c.buckets, int(i))
} else {
Expect(c.buckets).ToEqual(16)
assert.Equal(t, c.buckets, 16)
}
}
}

4
go.mod
View File

@@ -1,7 +1,3 @@
module github.com/karlseguin/ccache/v3
go 1.18
require github.com/karlseguin/expect v1.0.7
require github.com/wsxiaoys/terminal v0.0.0-20160513160801-0940f3fc43a0 // indirect

4
go.sum
View File

@@ -1,4 +0,0 @@
github.com/karlseguin/expect v1.0.7 h1:OF4mqjblc450v8nKARBS5Q0AweBNR0A+O3VjjpxwBrg=
github.com/karlseguin/expect v1.0.7/go.mod h1:lXdI8iGiQhmzpnnmU/EGA60vqKs8NbRNFnhhrJGoD5g=
github.com/wsxiaoys/terminal v0.0.0-20160513160801-0940f3fc43a0 h1:3UeQBvD0TFrlVjOeLOBz+CPAI8dnbqNSVwUwRrkp7vQ=
github.com/wsxiaoys/terminal v0.0.0-20160513160801-0940f3fc43a0/go.mod h1:IXCdmsXIht47RaVFLEdVnh1t+pgYtTAhQGj73kz+2DM=

View File

@@ -5,45 +5,39 @@ import (
"testing"
"time"
. "github.com/karlseguin/expect"
"github.com/karlseguin/ccache/v3/assert"
)
type ItemTests struct{}
func Test_Item(t *testing.T) {
Expectify(new(ItemTests), t)
}
func (_ *ItemTests) Promotability() {
func Test_Item_Promotability(t *testing.T) {
item := &Item[int]{promotions: 4}
Expect(item.shouldPromote(5)).To.Equal(true)
Expect(item.shouldPromote(5)).To.Equal(false)
assert.Equal(t, item.shouldPromote(5), true)
assert.Equal(t, item.shouldPromote(5), false)
}
func (_ *ItemTests) Expired() {
func Test_Item_Expired(t *testing.T) {
now := time.Now().UnixNano()
item1 := &Item[int]{expires: now + (10 * int64(time.Millisecond))}
item2 := &Item[int]{expires: now - (10 * int64(time.Millisecond))}
Expect(item1.Expired()).To.Equal(false)
Expect(item2.Expired()).To.Equal(true)
assert.Equal(t, item1.Expired(), false)
assert.Equal(t, item2.Expired(), true)
}
func (_ *ItemTests) TTL() {
func Test_Item_TTL(t *testing.T) {
now := time.Now().UnixNano()
item1 := &Item[int]{expires: now + int64(time.Second)}
item2 := &Item[int]{expires: now - int64(time.Second)}
Expect(int(math.Ceil(item1.TTL().Seconds()))).To.Equal(1)
Expect(int(math.Ceil(item2.TTL().Seconds()))).To.Equal(-1)
assert.Equal(t, int(math.Ceil(item1.TTL().Seconds())), 1)
assert.Equal(t, int(math.Ceil(item2.TTL().Seconds())), -1)
}
func (_ *ItemTests) Expires() {
func Test_Item_Expires(t *testing.T) {
now := time.Now().UnixNano()
item := &Item[int]{expires: now + (10)}
Expect(item.Expires().UnixNano()).To.Equal(now + 10)
assert.Equal(t, item.Expires().UnixNano(), now+10)
}
func (_ *ItemTests) Extend() {
func Test_Item_Extend(t *testing.T) {
item := &Item[int]{expires: time.Now().UnixNano() + 10}
item.Extend(time.Minute * 2)
Expect(item.Expires().Unix()).To.Equal(time.Now().Unix() + 120)
assert.Equal(t, item.Expires().Unix(), time.Now().Unix()+120)
}

View File

@@ -7,75 +7,69 @@ import (
"testing"
"time"
. "github.com/karlseguin/expect"
"github.com/karlseguin/ccache/v3/assert"
)
type LayeredCacheTests struct{}
func Test_LayeredCache(t *testing.T) {
Expectify(new(LayeredCacheTests), t)
}
func (_ *LayeredCacheTests) GetsANonExistantValue() {
func Test_LayedCache_GetsANonExistantValue(t *testing.T) {
cache := newLayered[string]()
Expect(cache.Get("spice", "flow")).To.Equal(nil)
Expect(cache.ItemCount()).To.Equal(0)
assert.Equal(t, cache.Get("spice", "flow"), nil)
assert.Equal(t, cache.ItemCount(), 0)
}
func (_ *LayeredCacheTests) SetANewValue() {
func Test_LayedCache_SetANewValue(t *testing.T) {
cache := newLayered[string]()
cache.Set("spice", "flow", "a value", time.Minute)
Expect(cache.Get("spice", "flow").Value()).To.Equal("a value")
Expect(cache.Get("spice", "stop")).To.Equal(nil)
Expect(cache.ItemCount()).To.Equal(1)
assert.Equal(t, cache.Get("spice", "flow").Value(), "a value")
assert.Equal(t, cache.Get("spice", "stop"), nil)
assert.Equal(t, cache.ItemCount(), 1)
}
func (_ *LayeredCacheTests) SetsMultipleValueWithinTheSameLayer() {
func Test_LayedCache_SetsMultipleValueWithinTheSameLayer(t *testing.T) {
cache := newLayered[string]()
cache.Set("spice", "flow", "value-a", time.Minute)
cache.Set("spice", "must", "value-b", time.Minute)
cache.Set("leto", "sister", "ghanima", time.Minute)
Expect(cache.Get("spice", "flow").Value()).To.Equal("value-a")
Expect(cache.Get("spice", "must").Value()).To.Equal("value-b")
Expect(cache.Get("spice", "worm")).To.Equal(nil)
assert.Equal(t, cache.Get("spice", "flow").Value(), "value-a")
assert.Equal(t, cache.Get("spice", "must").Value(), "value-b")
assert.Equal(t, cache.Get("spice", "worm"), nil)
Expect(cache.Get("leto", "sister").Value()).To.Equal("ghanima")
Expect(cache.Get("leto", "brother")).To.Equal(nil)
Expect(cache.Get("baron", "friend")).To.Equal(nil)
Expect(cache.ItemCount()).To.Equal(3)
assert.Equal(t, cache.Get("leto", "sister").Value(), "ghanima")
assert.Equal(t, cache.Get("leto", "brother"), nil)
assert.Equal(t, cache.Get("baron", "friend"), nil)
assert.Equal(t, cache.ItemCount(), 3)
}
func (_ *LayeredCacheTests) ReplaceDoesNothingIfKeyDoesNotExist() {
func Test_LayedCache_ReplaceDoesNothingIfKeyDoesNotExist(t *testing.T) {
cache := newLayered[string]()
Expect(cache.Replace("spice", "flow", "value-a")).To.Equal(false)
Expect(cache.Get("spice", "flow")).To.Equal(nil)
assert.Equal(t, cache.Replace("spice", "flow", "value-a"), false)
assert.Equal(t, cache.Get("spice", "flow"), nil)
}
func (_ *LayeredCacheTests) ReplaceUpdatesTheValue() {
func Test_LayedCache_ReplaceUpdatesTheValue(t *testing.T) {
cache := newLayered[string]()
cache.Set("spice", "flow", "value-a", time.Minute)
Expect(cache.Replace("spice", "flow", "value-b")).To.Equal(true)
Expect(cache.Get("spice", "flow").Value()).To.Equal("value-b")
Expect(cache.ItemCount()).To.Equal(1)
assert.Equal(t, cache.Replace("spice", "flow", "value-b"), true)
assert.Equal(t, cache.Get("spice", "flow").Value(), "value-b")
assert.Equal(t, cache.ItemCount(), 1)
//not sure how to test that the TTL hasn't changed sort of a sleep..
}
func (_ *LayeredCacheTests) DeletesAValue() {
func Test_LayedCache_DeletesAValue(t *testing.T) {
cache := newLayered[string]()
cache.Set("spice", "flow", "value-a", time.Minute)
cache.Set("spice", "must", "value-b", time.Minute)
cache.Set("leto", "sister", "ghanima", time.Minute)
cache.Delete("spice", "flow")
Expect(cache.Get("spice", "flow")).To.Equal(nil)
Expect(cache.Get("spice", "must").Value()).To.Equal("value-b")
Expect(cache.Get("spice", "worm")).To.Equal(nil)
Expect(cache.Get("leto", "sister").Value()).To.Equal("ghanima")
Expect(cache.ItemCount()).To.Equal(2)
assert.Equal(t, cache.Get("spice", "flow"), nil)
assert.Equal(t, cache.Get("spice", "must").Value(), "value-b")
assert.Equal(t, cache.Get("spice", "worm"), nil)
assert.Equal(t, cache.Get("leto", "sister").Value(), "ghanima")
assert.Equal(t, cache.ItemCount(), 2)
}
func (_ *LayeredCacheTests) DeletesAPrefix() {
func Test_LayedCache_DeletesAPrefix(t *testing.T) {
cache := newLayered[string]()
Expect(cache.ItemCount()).To.Equal(0)
assert.Equal(t, cache.ItemCount(), 0)
cache.Set("spice", "aaa", "1", time.Minute)
cache.Set("spice", "aab", "2", time.Minute)
@@ -83,23 +77,23 @@ func (_ *LayeredCacheTests) DeletesAPrefix() {
cache.Set("leto", "aac", "3", time.Minute)
cache.Set("spice", "ac", "4", time.Minute)
cache.Set("spice", "z5", "7", time.Minute)
Expect(cache.ItemCount()).To.Equal(6)
assert.Equal(t, cache.ItemCount(), 6)
Expect(cache.DeletePrefix("spice", "9a")).To.Equal(0)
Expect(cache.ItemCount()).To.Equal(6)
assert.Equal(t, cache.DeletePrefix("spice", "9a"), 0)
assert.Equal(t, cache.ItemCount(), 6)
Expect(cache.DeletePrefix("spice", "aa")).To.Equal(3)
Expect(cache.Get("spice", "aaa")).To.Equal(nil)
Expect(cache.Get("spice", "aab")).To.Equal(nil)
Expect(cache.Get("spice", "aac")).To.Equal(nil)
Expect(cache.Get("spice", "ac").Value()).To.Equal("4")
Expect(cache.Get("spice", "z5").Value()).To.Equal("7")
Expect(cache.ItemCount()).To.Equal(3)
assert.Equal(t, cache.DeletePrefix("spice", "aa"), 3)
assert.Equal(t, cache.Get("spice", "aaa"), nil)
assert.Equal(t, cache.Get("spice", "aab"), nil)
assert.Equal(t, cache.Get("spice", "aac"), nil)
assert.Equal(t, cache.Get("spice", "ac").Value(), "4")
assert.Equal(t, cache.Get("spice", "z5").Value(), "7")
assert.Equal(t, cache.ItemCount(), 3)
}
func (_ *LayeredCacheTests) DeletesAFunc() {
func Test_LayedCache_DeletesAFunc(t *testing.T) {
cache := newLayered[int]()
Expect(cache.ItemCount()).To.Equal(0)
assert.Equal(t, cache.ItemCount(), 0)
cache.Set("spice", "a", 1, time.Minute)
cache.Set("leto", "b", 2, time.Minute)
@@ -107,26 +101,26 @@ func (_ *LayeredCacheTests) DeletesAFunc() {
cache.Set("spice", "d", 4, time.Minute)
cache.Set("spice", "e", 5, time.Minute)
cache.Set("spice", "f", 6, time.Minute)
Expect(cache.ItemCount()).To.Equal(6)
assert.Equal(t, cache.ItemCount(), 6)
Expect(cache.DeleteFunc("spice", func(key string, item *Item[int]) bool {
assert.Equal(t, cache.DeleteFunc("spice", func(key string, item *Item[int]) bool {
return false
})).To.Equal(0)
Expect(cache.ItemCount()).To.Equal(6)
}), 0)
assert.Equal(t, cache.ItemCount(), 6)
Expect(cache.DeleteFunc("spice", func(key string, item *Item[int]) bool {
assert.Equal(t, cache.DeleteFunc("spice", func(key string, item *Item[int]) bool {
return item.Value() < 4
})).To.Equal(2)
Expect(cache.ItemCount()).To.Equal(4)
}), 2)
assert.Equal(t, cache.ItemCount(), 4)
Expect(cache.DeleteFunc("spice", func(key string, item *Item[int]) bool {
assert.Equal(t, cache.DeleteFunc("spice", func(key string, item *Item[int]) bool {
return key == "d"
})).To.Equal(1)
Expect(cache.ItemCount()).To.Equal(3)
}), 1)
assert.Equal(t, cache.ItemCount(), 3)
}
func (_ *LayeredCacheTests) OnDeleteCallbackCalled() {
func Test_LayedCache_OnDeleteCallbackCalled(t *testing.T) {
onDeleteFnCalled := int32(0)
onDeleteFn := func(item *Item[string]) {
if item.group == "spice" && item.key == "flow" {
@@ -143,27 +137,27 @@ func (_ *LayeredCacheTests) OnDeleteCallbackCalled() {
cache.Delete("spice", "flow")
cache.SyncUpdates()
Expect(cache.Get("spice", "flow")).To.Equal(nil)
Expect(cache.Get("spice", "must").Value()).To.Equal("value-b")
Expect(cache.Get("spice", "worm")).To.Equal(nil)
Expect(cache.Get("leto", "sister").Value()).To.Equal("ghanima")
assert.Equal(t, cache.Get("spice", "flow"), nil)
assert.Equal(t, cache.Get("spice", "must").Value(), "value-b")
assert.Equal(t, cache.Get("spice", "worm"), nil)
assert.Equal(t, cache.Get("leto", "sister").Value(), "ghanima")
Expect(atomic.LoadInt32(&onDeleteFnCalled)).To.Eql(1)
assert.Equal(t, atomic.LoadInt32(&onDeleteFnCalled), 1)
}
func (_ *LayeredCacheTests) DeletesALayer() {
func Test_LayedCache_DeletesALayer(t *testing.T) {
cache := newLayered[string]()
cache.Set("spice", "flow", "value-a", time.Minute)
cache.Set("spice", "must", "value-b", time.Minute)
cache.Set("leto", "sister", "ghanima", time.Minute)
cache.DeleteAll("spice")
Expect(cache.Get("spice", "flow")).To.Equal(nil)
Expect(cache.Get("spice", "must")).To.Equal(nil)
Expect(cache.Get("spice", "worm")).To.Equal(nil)
Expect(cache.Get("leto", "sister").Value()).To.Equal("ghanima")
assert.Equal(t, cache.Get("spice", "flow"), nil)
assert.Equal(t, cache.Get("spice", "must"), nil)
assert.Equal(t, cache.Get("spice", "worm"), nil)
assert.Equal(t, cache.Get("leto", "sister").Value(), "ghanima")
}
func (_ LayeredCacheTests) GCsTheOldestItems() {
func Test_LayeredCache_GCsTheOldestItems(t *testing.T) {
cache := Layered[int](Configure[int]().ItemsToPrune(10))
cache.Set("xx", "a", 23, time.Minute)
for i := 0; i < 500; i++ {
@@ -173,14 +167,14 @@ func (_ LayeredCacheTests) GCsTheOldestItems() {
//let the items get promoted (and added to our list)
cache.SyncUpdates()
cache.GC()
Expect(cache.Get("xx", "a")).To.Equal(nil)
Expect(cache.Get("xx", "b").Value()).To.Equal(9001)
Expect(cache.Get("8", "a")).To.Equal(nil)
Expect(cache.Get("9", "a").Value()).To.Equal(9)
Expect(cache.Get("10", "a").Value()).To.Equal(10)
assert.Equal(t, cache.Get("xx", "a"), nil)
assert.Equal(t, cache.Get("xx", "b").Value(), 9001)
assert.Equal(t, cache.Get("8", "a"), nil)
assert.Equal(t, cache.Get("9", "a").Value(), 9)
assert.Equal(t, cache.Get("10", "a").Value(), 10)
}
func (_ LayeredCacheTests) PromotedItemsDontGetPruned() {
func Test_LayeredCache_PromotedItemsDontGetPruned(t *testing.T) {
cache := Layered[int](Configure[int]().ItemsToPrune(10).GetsPerPromote(1))
for i := 0; i < 500; i++ {
cache.Set(strconv.Itoa(i), "a", i, time.Minute)
@@ -189,12 +183,12 @@ func (_ LayeredCacheTests) PromotedItemsDontGetPruned() {
cache.Get("9", "a")
cache.SyncUpdates()
cache.GC()
Expect(cache.Get("9", "a").Value()).To.Equal(9)
Expect(cache.Get("10", "a")).To.Equal(nil)
Expect(cache.Get("11", "a").Value()).To.Equal(11)
assert.Equal(t, cache.Get("9", "a").Value(), 9)
assert.Equal(t, cache.Get("10", "a"), nil)
assert.Equal(t, cache.Get("11", "a").Value(), 11)
}
func (_ LayeredCacheTests) TrackerDoesNotCleanupHeldInstance() {
func Test_LayeredCache_TrackerDoesNotCleanupHeldInstance(t *testing.T) {
cache := Layered[int](Configure[int]().ItemsToPrune(10).Track())
item0 := cache.TrackingSet("0", "a", 0, time.Minute)
for i := 1; i < 11; i++ {
@@ -203,16 +197,16 @@ func (_ LayeredCacheTests) TrackerDoesNotCleanupHeldInstance() {
item1 := cache.TrackingGet("1", "a")
cache.SyncUpdates()
cache.GC()
Expect(cache.Get("0", "a").Value()).To.Equal(0)
Expect(cache.Get("1", "a").Value()).To.Equal(1)
assert.Equal(t, cache.Get("0", "a").Value(), 0)
assert.Equal(t, cache.Get("1", "a").Value(), 1)
item0.Release()
item1.Release()
cache.GC()
Expect(cache.Get("0", "a")).To.Equal(nil)
Expect(cache.Get("1", "a")).To.Equal(nil)
assert.Equal(t, cache.Get("0", "a"), nil)
assert.Equal(t, cache.Get("1", "a"), nil)
}
func (_ LayeredCacheTests) RemovesOldestItemWhenFull() {
func Test_LayeredCache_RemovesOldestItemWhenFull(t *testing.T) {
cache := Layered[int](Configure[int]().MaxSize(5).ItemsToPrune(1))
cache.Set("xx", "a", 23, time.Minute)
for i := 0; i < 7; i++ {
@@ -220,17 +214,17 @@ func (_ LayeredCacheTests) RemovesOldestItemWhenFull() {
}
cache.Set("xx", "b", 9001, time.Minute)
cache.SyncUpdates()
Expect(cache.Get("xx", "a")).To.Equal(nil)
Expect(cache.Get("0", "a")).To.Equal(nil)
Expect(cache.Get("1", "a")).To.Equal(nil)
Expect(cache.Get("2", "a")).To.Equal(nil)
Expect(cache.Get("3", "a").Value()).To.Equal(3)
Expect(cache.Get("xx", "b").Value()).To.Equal(9001)
Expect(cache.GetDropped()).To.Equal(4)
Expect(cache.GetDropped()).To.Equal(0)
assert.Equal(t, cache.Get("xx", "a"), nil)
assert.Equal(t, cache.Get("0", "a"), nil)
assert.Equal(t, cache.Get("1", "a"), nil)
assert.Equal(t, cache.Get("2", "a"), nil)
assert.Equal(t, cache.Get("3", "a").Value(), 3)
assert.Equal(t, cache.Get("xx", "b").Value(), 9001)
assert.Equal(t, cache.GetDropped(), 4)
assert.Equal(t, cache.GetDropped(), 0)
}
func (_ LayeredCacheTests) ResizeOnTheFly() {
func Test_LayeredCache_ResizeOnTheFly(t *testing.T) {
cache := Layered[int](Configure[int]().MaxSize(9).ItemsToPrune(1))
for i := 0; i < 5; i++ {
cache.Set(strconv.Itoa(i), "a", i, time.Minute)
@@ -239,120 +233,120 @@ func (_ LayeredCacheTests) ResizeOnTheFly() {
cache.SetMaxSize(3)
cache.SyncUpdates()
Expect(cache.GetDropped()).To.Equal(2)
Expect(cache.Get("0", "a")).To.Equal(nil)
Expect(cache.Get("1", "a")).To.Equal(nil)
Expect(cache.Get("2", "a").Value()).To.Equal(2)
Expect(cache.Get("3", "a").Value()).To.Equal(3)
Expect(cache.Get("4", "a").Value()).To.Equal(4)
assert.Equal(t, cache.GetDropped(), 2)
assert.Equal(t, cache.Get("0", "a"), nil)
assert.Equal(t, cache.Get("1", "a"), nil)
assert.Equal(t, cache.Get("2", "a").Value(), 2)
assert.Equal(t, cache.Get("3", "a").Value(), 3)
assert.Equal(t, cache.Get("4", "a").Value(), 4)
cache.Set("5", "a", 5, time.Minute)
cache.SyncUpdates()
Expect(cache.GetDropped()).To.Equal(1)
Expect(cache.Get("2", "a")).To.Equal(nil)
Expect(cache.Get("3", "a").Value()).To.Equal(3)
Expect(cache.Get("4", "a").Value()).To.Equal(4)
Expect(cache.Get("5", "a").Value()).To.Equal(5)
assert.Equal(t, cache.GetDropped(), 1)
assert.Equal(t, cache.Get("2", "a"), nil)
assert.Equal(t, cache.Get("3", "a").Value(), 3)
assert.Equal(t, cache.Get("4", "a").Value(), 4)
assert.Equal(t, cache.Get("5", "a").Value(), 5)
cache.SetMaxSize(10)
cache.Set("6", "a", 6, time.Minute)
cache.SyncUpdates()
Expect(cache.GetDropped()).To.Equal(0)
Expect(cache.Get("3", "a").Value()).To.Equal(3)
Expect(cache.Get("4", "a").Value()).To.Equal(4)
Expect(cache.Get("5", "a").Value()).To.Equal(5)
Expect(cache.Get("6", "a").Value()).To.Equal(6)
assert.Equal(t, cache.GetDropped(), 0)
assert.Equal(t, cache.Get("3", "a").Value(), 3)
assert.Equal(t, cache.Get("4", "a").Value(), 4)
assert.Equal(t, cache.Get("5", "a").Value(), 5)
assert.Equal(t, cache.Get("6", "a").Value(), 6)
}
func (_ LayeredCacheTests) RemovesOldestItemWhenFullBySizer() {
func Test_LayeredCache_RemovesOldestItemWhenFullBySizer(t *testing.T) {
cache := Layered[*SizedItem](Configure[*SizedItem]().MaxSize(9).ItemsToPrune(2))
for i := 0; i < 7; i++ {
cache.Set("pri", strconv.Itoa(i), &SizedItem{i, 2}, time.Minute)
}
cache.SyncUpdates()
Expect(cache.Get("pri", "0")).To.Equal(nil)
Expect(cache.Get("pri", "1")).To.Equal(nil)
Expect(cache.Get("pri", "2")).To.Equal(nil)
Expect(cache.Get("pri", "3")).To.Equal(nil)
Expect(cache.Get("pri", "4").Value().id).To.Equal(4)
assert.Equal(t, cache.Get("pri", "0"), nil)
assert.Equal(t, cache.Get("pri", "1"), nil)
assert.Equal(t, cache.Get("pri", "2"), nil)
assert.Equal(t, cache.Get("pri", "3"), nil)
assert.Equal(t, cache.Get("pri", "4").Value().id, 4)
}
func (_ LayeredCacheTests) SetUpdatesSizeOnDelta() {
func Test_LayeredCache_SetUpdatesSizeOnDelta(t *testing.T) {
cache := Layered[*SizedItem](Configure[*SizedItem]())
cache.Set("pri", "a", &SizedItem{0, 2}, time.Minute)
cache.Set("pri", "b", &SizedItem{0, 3}, time.Minute)
cache.SyncUpdates()
Expect(cache.GetSize()).To.Eql(5)
assert.Equal(t, cache.GetSize(), 5)
cache.Set("pri", "b", &SizedItem{0, 3}, time.Minute)
cache.SyncUpdates()
Expect(cache.GetSize()).To.Eql(5)
assert.Equal(t, cache.GetSize(), 5)
cache.Set("pri", "b", &SizedItem{0, 4}, time.Minute)
cache.SyncUpdates()
Expect(cache.GetSize()).To.Eql(6)
assert.Equal(t, cache.GetSize(), 6)
cache.Set("pri", "b", &SizedItem{0, 2}, time.Minute)
cache.Set("sec", "b", &SizedItem{0, 3}, time.Minute)
cache.SyncUpdates()
Expect(cache.GetSize()).To.Eql(7)
assert.Equal(t, cache.GetSize(), 7)
cache.Delete("pri", "b")
cache.SyncUpdates()
Expect(cache.GetSize()).To.Eql(5)
assert.Equal(t, cache.GetSize(), 5)
}
func (_ LayeredCacheTests) ReplaceDoesNotchangeSizeIfNotSet() {
func Test_LayeredCache_ReplaceDoesNotchangeSizeIfNotSet(t *testing.T) {
cache := Layered[*SizedItem](Configure[*SizedItem]())
cache.Set("pri", "1", &SizedItem{1, 2}, time.Minute)
cache.Set("pri", "2", &SizedItem{1, 2}, time.Minute)
cache.Set("pri", "3", &SizedItem{1, 2}, time.Minute)
cache.Replace("sec", "3", &SizedItem{1, 2})
cache.SyncUpdates()
Expect(cache.GetSize()).To.Eql(6)
assert.Equal(t, cache.GetSize(), 6)
}
func (_ LayeredCacheTests) ReplaceChangesSize() {
func Test_LayeredCache_ReplaceChangesSize(t *testing.T) {
cache := Layered[*SizedItem](Configure[*SizedItem]())
cache.Set("pri", "1", &SizedItem{1, 2}, time.Minute)
cache.Set("pri", "2", &SizedItem{1, 2}, time.Minute)
cache.Replace("pri", "2", &SizedItem{1, 2})
cache.SyncUpdates()
Expect(cache.GetSize()).To.Eql(4)
assert.Equal(t, cache.GetSize(), 4)
cache.Replace("pri", "2", &SizedItem{1, 1})
cache.SyncUpdates()
Expect(cache.GetSize()).To.Eql(3)
assert.Equal(t, cache.GetSize(), 3)
cache.Replace("pri", "2", &SizedItem{1, 3})
cache.SyncUpdates()
Expect(cache.GetSize()).To.Eql(5)
assert.Equal(t, cache.GetSize(), 5)
}
func (_ LayeredCacheTests) EachFunc() {
func Test_LayeredCache_EachFunc(t *testing.T) {
cache := Layered[int](Configure[int]().MaxSize(3).ItemsToPrune(1))
Expect(forEachKeysLayered[int](cache, "1")).To.Equal([]string{})
assert.List(t, forEachKeysLayered[int](cache, "1"), []string{})
cache.Set("1", "a", 1, time.Minute)
Expect(forEachKeysLayered[int](cache, "1")).To.Equal([]string{"a"})
assert.List(t, forEachKeysLayered[int](cache, "1"), []string{"a"})
cache.Set("1", "b", 2, time.Minute)
cache.SyncUpdates()
Expect(forEachKeysLayered[int](cache, "1")).To.Equal([]string{"a", "b"})
assert.List(t, forEachKeysLayered[int](cache, "1"), []string{"a", "b"})
cache.Set("1", "c", 3, time.Minute)
cache.SyncUpdates()
Expect(forEachKeysLayered[int](cache, "1")).To.Equal([]string{"a", "b", "c"})
assert.List(t, forEachKeysLayered[int](cache, "1"), []string{"a", "b", "c"})
cache.Set("1", "d", 4, time.Minute)
cache.SyncUpdates()
Expect(forEachKeysLayered[int](cache, "1")).To.Equal([]string{"b", "c", "d"})
assert.List(t, forEachKeysLayered[int](cache, "1"), []string{"b", "c", "d"})
// iteration is non-deterministic, all we know for sure is "stop" should not be in there
cache.Set("1", "stop", 5, time.Minute)
cache.SyncUpdates()
Expect(forEachKeysLayered[int](cache, "1")).Not.To.Contain("stop")
assert.DoesNotContain(t, forEachKeysLayered[int](cache, "1"), "stop")
cache.Set("1", "e", 6, time.Minute)
cache.SyncUpdates()
Expect(forEachKeysLayered[int](cache, "1")).Not.To.Contain("stop")
assert.DoesNotContain(t, forEachKeysLayered[int](cache, "1"), "stop")
}
func newLayered[T any]() *LayeredCache[T] {

View File

@@ -5,91 +5,85 @@ import (
"testing"
"time"
. "github.com/karlseguin/expect"
"github.com/karlseguin/ccache/v3/assert"
)
type SecondaryCacheTests struct{}
func Test_SecondaryCache(t *testing.T) {
Expectify(new(SecondaryCacheTests), t)
}
func (_ SecondaryCacheTests) GetsANonExistantValue() {
func Test_SecondaryCache_GetsANonExistantValue(t *testing.T) {
cache := newLayered[string]().GetOrCreateSecondaryCache("foo")
Expect(cache).Not.To.Equal(nil)
assert.Equal(t, cache == nil, false)
}
func (_ SecondaryCacheTests) SetANewValue() {
func Test_SecondaryCache_SetANewValue(t *testing.T) {
cache := newLayered[string]()
cache.Set("spice", "flow", "a value", time.Minute)
sCache := cache.GetOrCreateSecondaryCache("spice")
Expect(sCache.Get("flow").Value()).To.Equal("a value")
Expect(sCache.Get("stop")).To.Equal(nil)
assert.Equal(t, sCache.Get("flow").Value(), "a value")
assert.Equal(t, sCache.Get("stop"), nil)
}
func (_ SecondaryCacheTests) ValueCanBeSeenInBothCaches1() {
func Test_SecondaryCache_ValueCanBeSeenInBothCaches1(t *testing.T) {
cache := newLayered[string]()
cache.Set("spice", "flow", "a value", time.Minute)
sCache := cache.GetOrCreateSecondaryCache("spice")
sCache.Set("orinoco", "another value", time.Minute)
Expect(sCache.Get("orinoco").Value()).To.Equal("another value")
Expect(cache.Get("spice", "orinoco").Value()).To.Equal("another value")
assert.Equal(t, sCache.Get("orinoco").Value(), "another value")
assert.Equal(t, cache.Get("spice", "orinoco").Value(), "another value")
}
func (_ SecondaryCacheTests) ValueCanBeSeenInBothCaches2() {
func Test_SecondaryCache_ValueCanBeSeenInBothCaches2(t *testing.T) {
cache := newLayered[string]()
sCache := cache.GetOrCreateSecondaryCache("spice")
sCache.Set("flow", "a value", time.Minute)
Expect(sCache.Get("flow").Value()).To.Equal("a value")
Expect(cache.Get("spice", "flow").Value()).To.Equal("a value")
assert.Equal(t, sCache.Get("flow").Value(), "a value")
assert.Equal(t, cache.Get("spice", "flow").Value(), "a value")
}
func (_ SecondaryCacheTests) DeletesAreReflectedInBothCaches() {
func Test_SecondaryCache_DeletesAreReflectedInBothCaches(t *testing.T) {
cache := newLayered[string]()
cache.Set("spice", "flow", "a value", time.Minute)
cache.Set("spice", "sister", "ghanima", time.Minute)
sCache := cache.GetOrCreateSecondaryCache("spice")
cache.Delete("spice", "flow")
Expect(cache.Get("spice", "flow")).To.Equal(nil)
Expect(sCache.Get("flow")).To.Equal(nil)
assert.Equal(t, cache.Get("spice", "flow"), nil)
assert.Equal(t, sCache.Get("flow"), nil)
sCache.Delete("sister")
Expect(cache.Get("spice", "sister")).To.Equal(nil)
Expect(sCache.Get("sister")).To.Equal(nil)
assert.Equal(t, cache.Get("spice", "sister"), nil)
assert.Equal(t, sCache.Get("sister"), nil)
}
func (_ SecondaryCacheTests) ReplaceDoesNothingIfKeyDoesNotExist() {
func Test_SecondaryCache_ReplaceDoesNothingIfKeyDoesNotExist(t *testing.T) {
cache := newLayered[string]()
sCache := cache.GetOrCreateSecondaryCache("spice")
Expect(sCache.Replace("flow", "value-a")).To.Equal(false)
Expect(cache.Get("spice", "flow")).To.Equal(nil)
assert.Equal(t, sCache.Replace("flow", "value-a"), false)
assert.Equal(t, cache.Get("spice", "flow"), nil)
}
func (_ SecondaryCacheTests) ReplaceUpdatesTheValue() {
func Test_SecondaryCache_ReplaceUpdatesTheValue(t *testing.T) {
cache := newLayered[string]()
cache.Set("spice", "flow", "value-a", time.Minute)
sCache := cache.GetOrCreateSecondaryCache("spice")
Expect(sCache.Replace("flow", "value-b")).To.Equal(true)
Expect(cache.Get("spice", "flow").Value()).To.Equal("value-b")
assert.Equal(t, sCache.Replace("flow", "value-b"), true)
assert.Equal(t, cache.Get("spice", "flow").Value(), "value-b")
}
func (_ SecondaryCacheTests) FetchReturnsAnExistingValue() {
func Test_SecondaryCache_FetchReturnsAnExistingValue(t *testing.T) {
cache := newLayered[string]()
cache.Set("spice", "flow", "value-a", time.Minute)
sCache := cache.GetOrCreateSecondaryCache("spice")
val, _ := sCache.Fetch("flow", time.Minute, func() (string, error) { return "a fetched value", nil })
Expect(val.Value()).To.Equal("value-a")
assert.Equal(t, val.Value(), "value-a")
}
func (_ SecondaryCacheTests) FetchReturnsANewValue() {
func Test_SecondaryCache_FetchReturnsANewValue(t *testing.T) {
cache := newLayered[string]()
sCache := cache.GetOrCreateSecondaryCache("spice")
val, _ := sCache.Fetch("flow", time.Minute, func() (string, error) { return "a fetched value", nil })
Expect(val.Value()).To.Equal("a fetched value")
assert.Equal(t, val.Value(), "a fetched value")
}
func (_ SecondaryCacheTests) TrackerDoesNotCleanupHeldInstance() {
func Test_SecondaryCache_TrackerDoesNotCleanupHeldInstance(t *testing.T) {
cache := Layered[int](Configure[int]().ItemsToPrune(10).Track())
for i := 0; i < 10; i++ {
cache.Set(strconv.Itoa(i), "a", i, time.Minute)
@@ -98,9 +92,9 @@ func (_ SecondaryCacheTests) TrackerDoesNotCleanupHeldInstance() {
item := sCache.TrackingGet("a")
time.Sleep(time.Millisecond * 10)
cache.GC()
Expect(cache.Get("0", "a").Value()).To.Equal(0)
Expect(cache.Get("1", "a")).To.Equal(nil)
assert.Equal(t, cache.Get("0", "a").Value(), 0)
assert.Equal(t, cache.Get("1", "a"), nil)
item.Release()
cache.GC()
Expect(cache.Get("0", "a")).To.Equal(nil)
assert.Equal(t, cache.Get("0", "a"), nil)
}