150 lines
2.6 KiB
Go
150 lines
2.6 KiB
Go
package kvmemcache
|
|
|
|
import (
|
|
"errors"
|
|
"fmt"
|
|
"testing"
|
|
"time"
|
|
)
|
|
|
|
type State struct {
|
|
Keys []string
|
|
Stats Stats
|
|
}
|
|
|
|
func (c *Cache) assert(state State) error {
|
|
c.lock.Lock()
|
|
defer c.lock.Unlock()
|
|
|
|
if len(c.cache) != len(state.Keys) {
|
|
return fmt.Errorf(
|
|
"Expected %d keys but found %d.",
|
|
len(state.Keys),
|
|
len(c.cache))
|
|
}
|
|
|
|
for _, k := range state.Keys {
|
|
if _, ok := c.cache[k]; !ok {
|
|
return fmt.Errorf(
|
|
"Expected key %s not found.",
|
|
k)
|
|
}
|
|
}
|
|
|
|
if c.stats.Hits != state.Stats.Hits {
|
|
return fmt.Errorf(
|
|
"Expected %d hits, but found %d.",
|
|
state.Stats.Hits,
|
|
c.stats.Hits)
|
|
}
|
|
|
|
if c.stats.Misses != state.Stats.Misses {
|
|
return fmt.Errorf(
|
|
"Expected %d misses, but found %d.",
|
|
state.Stats.Misses,
|
|
c.stats.Misses)
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
var TestError = errors.New("Hello")
|
|
|
|
func TestCache_Basic(t *testing.T) {
|
|
c := New(Config{
|
|
MaxSize: 4,
|
|
TTL: 50 * time.Millisecond,
|
|
Src: func(key string) (interface{}, error) {
|
|
if key == "err" {
|
|
return nil, TestError
|
|
}
|
|
return key, nil
|
|
},
|
|
})
|
|
|
|
type testCase struct {
|
|
name string
|
|
sleep time.Duration
|
|
key string
|
|
state State
|
|
}
|
|
|
|
cases := []testCase{
|
|
{
|
|
name: "get a",
|
|
key: "a",
|
|
state: State{
|
|
Keys: []string{"a"},
|
|
Stats: Stats{Hits: 0, Misses: 1},
|
|
},
|
|
}, {
|
|
name: "get a again",
|
|
key: "a",
|
|
state: State{
|
|
Keys: []string{"a"},
|
|
Stats: Stats{Hits: 1, Misses: 1},
|
|
},
|
|
}, {
|
|
name: "sleep, then get a again",
|
|
sleep: 55 * time.Millisecond,
|
|
key: "a",
|
|
state: State{
|
|
Keys: []string{"a"},
|
|
Stats: Stats{Hits: 1, Misses: 2},
|
|
},
|
|
}, {
|
|
name: "get b",
|
|
key: "b",
|
|
state: State{
|
|
Keys: []string{"a", "b"},
|
|
Stats: Stats{Hits: 1, Misses: 3},
|
|
},
|
|
}, {
|
|
name: "get c",
|
|
key: "c",
|
|
state: State{
|
|
Keys: []string{"a", "b", "c"},
|
|
Stats: Stats{Hits: 1, Misses: 4},
|
|
},
|
|
}, {
|
|
name: "get d",
|
|
key: "d",
|
|
state: State{
|
|
Keys: []string{"a", "b", "c", "d"},
|
|
Stats: Stats{Hits: 1, Misses: 5},
|
|
},
|
|
}, {
|
|
name: "get e",
|
|
key: "e",
|
|
state: State{
|
|
Keys: []string{"b", "c", "d", "e"},
|
|
Stats: Stats{Hits: 1, Misses: 6},
|
|
},
|
|
}, {
|
|
name: "get c again",
|
|
key: "c",
|
|
state: State{
|
|
Keys: []string{"b", "c", "d", "e"},
|
|
Stats: Stats{Hits: 2, Misses: 6},
|
|
},
|
|
},
|
|
}
|
|
|
|
for _, tc := range cases {
|
|
time.Sleep(tc.sleep)
|
|
val, err := c.Get(tc.key)
|
|
if tc.key == "err" && err != TestError {
|
|
t.Fatal(tc.name, val)
|
|
}
|
|
if tc.key != "err" && val.(string) != tc.key {
|
|
t.Fatal(tc.name, tc.key, val)
|
|
}
|
|
|
|
if err := c.assert(tc.state); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
}
|
|
}
|
|
|
|
// TODO: Test thundering herd mitigation.
|