diff --git a/blocks/blockstore/arc_cache_test.go b/blocks/blockstore/arc_cache_test.go index 505f7e1ea11..b37092602ca 100644 --- a/blocks/blockstore/arc_cache_test.go +++ b/blocks/blockstore/arc_cache_test.go @@ -2,6 +2,7 @@ package blockstore import ( "github.com/ipfs/go-ipfs/blocks" + "github.com/ipfs/go-ipfs/blocks/key" "testing" ds "gx/ipfs/QmTxLSvdhwg68WJimdS6icLPhZi28aTp6b7uihC2Yb47Xk/go-datastore" @@ -9,6 +10,8 @@ import ( context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" ) +var exampleBlock = blocks.NewBlock([]byte("foo")) + func testArcCached(bs GCBlockstore, ctx context.Context) (*arccache, error) { if ctx == nil { ctx = context.TODO() @@ -24,15 +27,29 @@ func testArcCached(bs GCBlockstore, ctx context.Context) (*arccache, error) { } } -func TestRemoveCacheEntryOnDelete(t *testing.T) { - b := blocks.NewBlock([]byte("foo")) +func createStores(t *testing.T) (*arccache, *blockstore, *callbackDatastore) { cd := &callbackDatastore{f: func() {}, ds: ds.NewMapDatastore()} bs := NewBlockstore(syncds.MutexWrap(cd)) - cachedbs, err := testArcCached(bs, nil) + arc, err := testArcCached(bs, nil) if err != nil { t.Fatal(err) } - cachedbs.Put(b) + return arc, bs, cd +} + +func trap(message string, cd *callbackDatastore, t *testing.T) { + cd.SetFunc(func() { + t.Fatal(message) + }) +} +func untrap(cd *callbackDatastore) { + cd.SetFunc(func() {}) +} + +func TestRemoveCacheEntryOnDelete(t *testing.T) { + arc, _, cd := createStores(t) + + arc.Put(exampleBlock) cd.Lock() writeHitTheDatastore := false @@ -42,26 +59,119 @@ func TestRemoveCacheEntryOnDelete(t *testing.T) { writeHitTheDatastore = true }) - cachedbs.DeleteBlock(b.Key()) - cachedbs.Put(b) + arc.DeleteBlock(exampleBlock.Key()) + arc.Put(exampleBlock) if !writeHitTheDatastore { t.Fail() } } func TestElideDuplicateWrite(t *testing.T) { - cd := &callbackDatastore{f: func() {}, ds: ds.NewMapDatastore()} - bs := NewBlockstore(syncds.MutexWrap(cd)) - cachedbs, err := testArcCached(bs, nil) + arc, _, cd := createStores(t) + + arc.Put(exampleBlock) + trap("write hit datastore", cd, t) + arc.Put(exampleBlock) +} + +func TestHasRequestTriggersCache(t *testing.T) { + arc, _, cd := createStores(t) + + arc.Has(exampleBlock.Key()) + trap("has hit datastore", cd, t) + if has, err := arc.Has(exampleBlock.Key()); has || err != nil { + t.Fatal("has was true but there is no such block") + } + + untrap(cd) + err := arc.Put(exampleBlock) if err != nil { t.Fatal(err) } - b1 := blocks.NewBlock([]byte("foo")) + trap("has hit datastore", cd, t) - cachedbs.Put(b1) - cd.SetFunc(func() { - t.Fatal("write hit the datastore") - }) - cachedbs.Put(b1) + if has, err := arc.Has(exampleBlock.Key()); !has || err != nil { + t.Fatal("has returned invalid result") + } +} + +func TestGetFillsCache(t *testing.T) { + arc, _, cd := createStores(t) + + if bl, err := arc.Get(exampleBlock.Key()); bl != nil || err == nil { + t.Fatal("block was found or there was no error") + } + + trap("has hit datastore", cd, t) + + if has, err := arc.Has(exampleBlock.Key()); has || err != nil { + t.Fatal("has was true but there is no such block") + } + + untrap(cd) + + if err := arc.Put(exampleBlock); err != nil { + t.Fatal(err) + } + + trap("has hit datastore", cd, t) + + if has, err := arc.Has(exampleBlock.Key()); !has || err != nil { + t.Fatal("has returned invalid result") + } +} + +func TestGetAndDeleteFalseShortCirciot(t *testing.T) { + arc, _, cd := createStores(t) + + arc.Has(exampleBlock.Key()) + + trap("get hit datastore", cd, t) + + if bl, err := arc.Get(exampleBlock.Key()); bl != nil || err != ErrNotFound { + t.Fatal("get returned invalid result") + } + + if arc.DeleteBlock(exampleBlock.Key()) != ErrNotFound { + t.Fatal("expected ErrNotFound error") + } +} + +func TestArcCreationFailure(t *testing.T) { + if arc, err := arcCached(nil, -1); arc != nil || err == nil { + t.Fatal("expected error and no cache") + } +} + +func TestInvalidKey(t *testing.T) { + arc, _, _ := createStores(t) + + bl, err := arc.Get(key.Key("")) + + if bl != nil { + t.Fatal("blocks should be nil") + } + if err == nil { + t.Fatal("expected error") + } +} + +func TestHasAfterSucessfulGetIsCached(t *testing.T) { + arc, bs, cd := createStores(t) + + bs.Put(exampleBlock) + + arc.Get(exampleBlock.Key()) + + trap("has hit datastore", cd, t) + arc.Has(exampleBlock.Key()) +} + +func TestPutManyCaches(t *testing.T) { + arc, _, cd := createStores(t) + arc.PutMany([]blocks.Block{exampleBlock}) + + trap("has hit datastore", cd, t) + arc.Has(exampleBlock.Key()) } diff --git a/blocks/blockstore/blockstore_test.go b/blocks/blockstore/blockstore_test.go index 2f026914133..2cca07cfbb1 100644 --- a/blocks/blockstore/blockstore_test.go +++ b/blocks/blockstore/blockstore_test.go @@ -57,16 +57,21 @@ func TestRuntimeHashing(t *testing.T) { bs := NewBlockstore(ds_sync.MutexWrap(ds.NewMapDatastore())) bl := blocks.NewBlock([]byte("some data")) blBad, err := blocks.NewBlockWithHash([]byte("some other data"), bl.Key().ToMultihash()) + bl2 := blocks.NewBlock([]byte("some other data")) if err != nil { t.Fatal("Debug is enabled") } - bs.Put(blBad) + bs.Put(bl2) bs.RuntimeHashing(true) if _, err := bs.Get(bl.Key()); err != ErrHashMismatch { t.Fatalf("Expected '%v' got '%v'\n", ErrHashMismatch, err) } + + if b, err := bs.Get(bl2.Key()); err != nil || b.String() != bl2.String() { + t.Fatal("got wrong blocks") + } } func newBlockStoreWithKeys(t *testing.T, d ds.Datastore, N int) (Blockstore, []key.Key) { diff --git a/blocks/blockstore/bloom_cache_test.go b/blocks/blockstore/bloom_cache_test.go index fbffd42f531..2a2638eaf79 100644 --- a/blocks/blockstore/bloom_cache_test.go +++ b/blocks/blockstore/bloom_cache_test.go @@ -66,6 +66,31 @@ func TestHasIsBloomCached(t *testing.T) { if float64(cacheFails)/float64(1000) > float64(0.05) { t.Fatal("Bloom filter has cache miss rate of more than 5%") } + + cacheFails = 0 + block := blocks.NewBlock([]byte("newBlock")) + + cachedbs.PutMany([]blocks.Block{block}) + if cacheFails != 2 { + t.Fatalf("expected two datastore hits: %d", cacheFails) + } + cachedbs.Put(block) + if cacheFails != 3 { + t.Fatalf("expected datastore hit: %d", cacheFails) + } + + if has, err := cachedbs.Has(block.Key()); !has || err != nil { + t.Fatal("has gave wrong response") + } + + bl, err := cachedbs.Get(block.Key()) + if bl.String() != block.String() { + t.Fatal("block data doesn't match") + } + + if err != nil { + t.Fatal("there should't be an error") + } } type callbackDatastore struct { diff --git a/blocks/blockstore/caching_test.go b/blocks/blockstore/caching_test.go new file mode 100644 index 00000000000..473f79a3007 --- /dev/null +++ b/blocks/blockstore/caching_test.go @@ -0,0 +1,35 @@ +package blockstore + +import "testing" + +func TestCachingOptsLessThanZero(t *testing.T) { + opts := DefaultCacheOpts() + opts.HasARCCacheSize = -1 + + if _, err := CachedBlockstore(nil, nil, opts); err == nil { + t.Fatal() + } + + opts = DefaultCacheOpts() + opts.HasBloomFilterSize = -1 + + if _, err := CachedBlockstore(nil, nil, opts); err == nil { + t.Fatal() + } + + opts = DefaultCacheOpts() + opts.HasBloomFilterHashes = -1 + + if _, err := CachedBlockstore(nil, nil, opts); err == nil { + t.Fatal() + } +} + +func TestBloomHashesAtZero(t *testing.T) { + opts := DefaultCacheOpts() + opts.HasBloomFilterHashes = 0 + + if _, err := CachedBlockstore(nil, nil, opts); err == nil { + t.Fatal() + } +}