|  | // Copyright 2016 The Fuchsia Authors. All rights reserved. | 
|  | // Use of this source code is governed by a BSD-style license that can be | 
|  | // found in the LICENSE file. | 
|  |  | 
|  | package cache | 
|  |  | 
|  | import ( | 
|  | "math/rand" | 
|  | "testing" | 
|  | "time" | 
|  | ) | 
|  |  | 
|  | const ( | 
|  | // Arbitrarily chosen. | 
|  | defaultSize   = 571 | 
|  | numIterations = 385494 | 
|  | ) | 
|  |  | 
|  | type store struct { | 
|  | get func(Key) Value | 
|  | put func(Key, Value) | 
|  | } | 
|  |  | 
|  | func (s store) Get(k Key) Value { | 
|  | return s.get(k) | 
|  | } | 
|  |  | 
|  | func (s store) Put(k Key, v Value) { | 
|  | s.put(k, v) | 
|  | } | 
|  |  | 
|  | func setUp(t *testing.T) *rand.Rand { | 
|  | seed := time.Now().UTC().UnixNano() | 
|  | t.Log("Seed is ", seed) | 
|  | r := rand.New(rand.NewSource(seed)) | 
|  |  | 
|  | return r | 
|  | } | 
|  |  | 
|  | func TestGet(t *testing.T) { | 
|  | r := setUp(t) | 
|  | s := store{ | 
|  | get: func(k Key) Value { | 
|  | return Value(k) | 
|  | }, | 
|  | } | 
|  |  | 
|  | arc := New(defaultSize, s) | 
|  | k := r.Int63() | 
|  | if v := arc.Get(k).Value.(int64); v != k { | 
|  | t.Errorf("arc.Get(%v) = %v; want %v\n", k, v, k) | 
|  | } | 
|  | } | 
|  |  | 
|  | func TestPut(t *testing.T) { | 
|  | r := setUp(t) | 
|  | key, val := r.Int(), r.Int() | 
|  | var called bool | 
|  |  | 
|  | s := store{ | 
|  | put: func(k Key, v Value) { | 
|  | called = true | 
|  | if k != key { | 
|  | t.Errorf("put: k = %v; want %v\n", k, key) | 
|  | } | 
|  | if v != val { | 
|  | t.Errorf("put: v = %v; want %v\n", v, val) | 
|  | } | 
|  | }, | 
|  | } | 
|  |  | 
|  | arc := New(defaultSize, s) | 
|  | arc.Put(key, val) | 
|  | arc.Flush() // Will call s.put | 
|  |  | 
|  | if !called { | 
|  | t.Error("arc.Flush() did not call Put on the BackingStore") | 
|  | } | 
|  | } | 
|  |  | 
|  | func TestModify(t *testing.T) { | 
|  | r := setUp(t) | 
|  | var called bool | 
|  |  | 
|  | s := store{ | 
|  | get: func(k Key) Value { | 
|  | return Value(k) | 
|  | }, | 
|  | put: func(k Key, v Value) { | 
|  | called = true | 
|  | if v.(int64) != ^k.(int64) { | 
|  | t.Errorf("put: Value is %v for Key %v; want %v\n", v, k, ^k.(int64)) | 
|  | } | 
|  | }, | 
|  | } | 
|  |  | 
|  | arc := New(defaultSize, s) | 
|  | k := r.Int63() | 
|  | entry := arc.Get(k) | 
|  | entry.Value = ^k | 
|  | entry.IsDirty = true | 
|  |  | 
|  | arc.Flush() | 
|  |  | 
|  | if !called { | 
|  | t.Error("arc.Flush() did not call Put on the BackingStore") | 
|  | } | 
|  | } | 
|  |  | 
|  | type testEntry struct { | 
|  | isDirty bool | 
|  | value   Value | 
|  | } | 
|  |  | 
|  | type op int | 
|  |  | 
|  | const ( | 
|  | get op = iota | 
|  | put | 
|  | modify | 
|  | ) | 
|  |  | 
|  | func getOneElem(m map[Key]*testEntry) Key { | 
|  | for k := range m { | 
|  | return k | 
|  | } | 
|  |  | 
|  | return nil | 
|  | } | 
|  |  | 
|  | func TestFuzz(t *testing.T) { | 
|  | r := setUp(t) | 
|  |  | 
|  | checker := make(map[Key]*testEntry) | 
|  | s := store{ | 
|  | get: func(k Key) Value { | 
|  | checker[k] = &testEntry{ | 
|  | isDirty: false, | 
|  | value:   ^k.(int), | 
|  | } | 
|  | return ^k.(int) | 
|  | }, | 
|  | put: func(k Key, v Value) { | 
|  | val, ok := checker[k] | 
|  | if !ok { | 
|  | t.Errorf("Putting key %v in the backing store when it shouldn't exist in the cache\n", k) | 
|  | } | 
|  | if !val.isDirty { | 
|  | t.Errorf("Attempting to commit non-dirty entry for key %v to the backing store\n", k) | 
|  | } | 
|  | if val.value != v { | 
|  | t.Errorf("Putting value v = %v for key %v; want %v\n", v, k, val.value) | 
|  | } | 
|  | delete(checker, k) | 
|  | }, | 
|  | } | 
|  |  | 
|  | arc := New(defaultSize, s) | 
|  |  | 
|  | for i := 0; i < numIterations; i++ { | 
|  | operation := op(r.Intn(3)) | 
|  | var k Key | 
|  | if len(checker) > 0 && r.Int()&0x1 == 0 { | 
|  | // Use an existing key. | 
|  | k = getOneElem(checker) | 
|  | } else { | 
|  | k = Key(r.Int()) | 
|  | } | 
|  |  | 
|  | switch operation { | 
|  | case get: | 
|  | entry := arc.Get(k) | 
|  | if entry.Value != checker[k].value { | 
|  | t.Errorf("arc.Get(%v) = %v; want %v\n", k, entry.Value, checker[k].value) | 
|  | } | 
|  | case put: | 
|  | arc.Put(k, Value(k)) | 
|  | checker[k] = &testEntry{ | 
|  | isDirty: true, | 
|  | value:   Value(k), | 
|  | } | 
|  | case modify: | 
|  | entry := arc.Get(k) | 
|  | entry.IsDirty = true | 
|  | entry.Value = -k.(int) | 
|  | checker[k] = &testEntry{ | 
|  | isDirty: true, | 
|  | value:   -k.(int), | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | arc.Flush() | 
|  | } |