| | |
| | |
| | |
| |
|
| | package fips140cache |
| |
|
| | import ( |
| | "context" |
| | "errors" |
| | "runtime" |
| | "sync" |
| | "testing" |
| | "time" |
| | ) |
| |
|
| | func TestCache(t *testing.T) { |
| | c := new(Cache[key, value]) |
| | checkTrue := func(*value) bool { return true } |
| | checkFalse := func(*value) bool { return false } |
| | newNotCalled := func() (*value, error) { |
| | t.Helper() |
| | t.Fatal("new called") |
| | return nil, nil |
| | } |
| |
|
| | k1 := newKey() |
| | v1 := &value{} |
| |
|
| | v, err := c.Get(k1, func() (*value, error) { return v1, nil }, checkTrue) |
| | expectValue(t, v, err, v1) |
| |
|
| | |
| | v, err = c.Get(k1, newNotCalled, checkTrue) |
| | expectValue(t, v, err, v1) |
| |
|
| | |
| | v2 := &value{} |
| | v, err = c.Get(k1, func() (*value, error) { return v2, nil }, checkFalse) |
| | expectValue(t, v, err, v2) |
| | v, err = c.Get(k1, newNotCalled, checkTrue) |
| | expectValue(t, v, err, v2) |
| | expectMapSize(t, c, 1) |
| |
|
| | |
| | waitUnreachable(t, &k1) |
| | expectMapSize(t, c, 0) |
| |
|
| | |
| | k2 := newKey() |
| | err1 := errors.New("error") |
| | _, err = c.Get(k2, func() (*value, error) { return nil, err1 }, checkTrue) |
| | if err != err1 { |
| | t.Errorf("got %v, want %v", err, err1) |
| | } |
| | expectMapSize(t, c, 0) |
| |
|
| | |
| | v, err = c.Get(k2, func() (*value, error) { return v1, nil }, checkTrue) |
| | expectValue(t, v, err, v1) |
| | _, err = c.Get(k2, func() (*value, error) { return v2, err1 }, checkFalse) |
| | if err != err1 { |
| | t.Errorf("got %v, want %v", err, err1) |
| | } |
| | v, err = c.Get(k2, newNotCalled, checkTrue) |
| | expectValue(t, v, err, v1) |
| | expectMapSize(t, c, 1) |
| |
|
| | |
| | k3 := newKey() |
| | v, err = c.Get(k3, func() (*value, error) { return v1, nil }, checkTrue) |
| | expectValue(t, v, err, v1) |
| | expectMapSize(t, c, 2) |
| | waitUnreachable(t, &k2) |
| | waitUnreachable(t, &k3) |
| | expectMapSize(t, c, 0) |
| |
|
| | |
| | |
| | keys := make([]*key, 100) |
| | for i := range keys { |
| | keys[i] = newKey() |
| | v1, v2 := &value{}, &value{} |
| | start := make(chan struct{}) |
| | var wg sync.WaitGroup |
| | wg.Add(2) |
| | go func() { |
| | <-start |
| | v, err := c.Get(keys[i], func() (*value, error) { return v1, nil }, checkTrue) |
| | expectValue(t, v, err, v1, v2) |
| | wg.Done() |
| | }() |
| | go func() { |
| | <-start |
| | v, err := c.Get(keys[i], func() (*value, error) { return v2, nil }, checkTrue) |
| | expectValue(t, v, err, v1, v2) |
| | wg.Done() |
| | }() |
| | close(start) |
| | wg.Wait() |
| | v3 := &value{} |
| | v, err := c.Get(keys[i], func() (*value, error) { return v3, nil }, checkTrue) |
| | expectValue(t, v, err, v1, v2) |
| | } |
| | for i := range keys { |
| | waitUnreachable(t, &keys[i]) |
| | } |
| | expectMapSize(t, c, 0) |
| | } |
| |
|
| | type key struct { |
| | _ *int |
| | } |
| |
|
| | type value struct { |
| | _ *int |
| | } |
| |
|
| | |
| | |
| | |
| | func newKey() *key { |
| | return &key{} |
| | } |
| |
|
| | func expectValue(t *testing.T, v *value, err error, want ...*value) { |
| | t.Helper() |
| | if err != nil { |
| | t.Fatal(err) |
| | } |
| | for _, w := range want { |
| | if v == w { |
| | return |
| | } |
| | } |
| | t.Errorf("got %p, want %p", v, want) |
| | } |
| |
|
| | func expectMapSize(t *testing.T, c *Cache[key, value], want int) { |
| | t.Helper() |
| | var size int |
| | |
| | for range 10 { |
| | size = 0 |
| | c.m.Range(func(_, _ any) bool { |
| | size++ |
| | return true |
| | }) |
| | if size == want { |
| | return |
| | } |
| | time.Sleep(100 * time.Millisecond) |
| | } |
| | t.Errorf("got %d, want %d", size, want) |
| | } |
| |
|
| | func waitUnreachable(t *testing.T, k **key) { |
| | ctx, cancel := context.WithCancel(t.Context()) |
| | defer cancel() |
| | runtime.AddCleanup(*k, func(_ *int) { cancel() }, nil) |
| | *k = nil |
| | for ctx.Err() == nil { |
| | runtime.GC() |
| | } |
| | if ctx.Err() != context.Canceled { |
| | t.Fatal(ctx.Err()) |
| | } |
| | } |
| |
|