github.com/ndau/noms@v1.0.5/go/types/value_store_test.go (about) 1 // Copyright 2016 Attic Labs, Inc. All rights reserved. 2 // Licensed under the Apache License, version 2.0: 3 // http://www.apache.org/licenses/LICENSE-2.0 4 5 package types 6 7 import ( 8 "testing" 9 10 "github.com/ndau/noms/go/chunks" 11 "github.com/ndau/noms/go/hash" 12 "github.com/stretchr/testify/assert" 13 ) 14 15 func TestValueReadWriteRead(t *testing.T) { 16 assert := assert.New(t) 17 18 s := String("hello") 19 vs := newTestValueStore() 20 assert.Nil(vs.ReadValue(s.Hash())) // nil 21 h := vs.WriteValue(s).TargetHash() 22 vs.Commit(vs.Root(), vs.Root()) 23 v := vs.ReadValue(h) // non-nil 24 if assert.NotNil(v) { 25 assert.True(s.Equals(v), "%s != %s", EncodedValue(s), EncodedValue(v)) 26 } 27 } 28 29 func TestReadWriteCache(t *testing.T) { 30 assert := assert.New(t) 31 storage := &chunks.TestStorage{} 32 ts := storage.NewView() 33 vs := NewValueStore(ts) 34 35 var v Value = Bool(true) 36 r := vs.WriteValue(v) 37 assert.NotEqual(hash.Hash{}, r.TargetHash()) 38 vs.Commit(vs.Root(), vs.Root()) 39 assert.Equal(1, ts.Writes) 40 41 v = vs.ReadValue(r.TargetHash()) 42 assert.True(v.Equals(Bool(true))) 43 assert.Equal(1, ts.Reads) 44 45 v = vs.ReadValue(r.TargetHash()) 46 assert.True(v.Equals(Bool(true))) 47 assert.Equal(1, ts.Reads) 48 } 49 50 func TestValueReadMany(t *testing.T) { 51 assert := assert.New(t) 52 53 vals := ValueSlice{String("hello"), Bool(true), Number(42)} 54 vs := newTestValueStore() 55 hashes := hash.HashSlice{} 56 for _, v := range vals { 57 h := vs.WriteValue(v).TargetHash() 58 hashes = append(hashes, h) 59 vs.Commit(vs.Root(), vs.Root()) 60 } 61 62 // Get one Value into vs's Value cache 63 vs.ReadValue(vals[0].Hash()) 64 65 // Get one Value into vs's pendingPuts 66 three := Number(3) 67 vals = append(vals, three) 68 vs.WriteValue(three) 69 hashes = append(hashes, three.Hash()) 70 71 // Add one Value to request that's not in vs 72 hashes = append(hashes, Bool(false).Hash()) 73 74 found := map[hash.Hash]Value{} 75 readValues := vs.ReadManyValues(hashes) 76 77 for i, v := range readValues { 78 if v != nil { 79 found[hashes[i]] = v 80 } 81 } 82 83 assert.Len(found, len(vals)) 84 for _, v := range vals { 85 assert.True(v.Equals(found[v.Hash()])) 86 } 87 } 88 89 func TestValueWriteFlush(t *testing.T) { 90 assert := assert.New(t) 91 92 vals := ValueSlice{String("hello"), Bool(true), Number(42)} 93 vs := newTestValueStore() 94 hashes := hash.HashSet{} 95 for _, v := range vals { 96 hashes.Insert(vs.WriteValue(v).TargetHash()) 97 } 98 assert.NotZero(vs.bufferedChunkSize) 99 100 vs.Commit(vs.Root(), vs.Root()) 101 assert.Zero(vs.bufferedChunkSize) 102 } 103 104 type checkingChunkStore struct { 105 chunks.ChunkStore 106 a *assert.Assertions 107 expectedOrder hash.HashSlice 108 } 109 110 func (cbs *checkingChunkStore) expect(rs ...Ref) { 111 for _, r := range rs { 112 cbs.expectedOrder = append(cbs.expectedOrder, r.TargetHash()) 113 } 114 } 115 116 func (cbs *checkingChunkStore) Put(c chunks.Chunk) { 117 if cbs.a.NotZero(len(cbs.expectedOrder), "Unexpected Put of %s", c.Hash()) { 118 cbs.a.Equal(cbs.expectedOrder[0], c.Hash()) 119 cbs.expectedOrder = cbs.expectedOrder[1:] 120 } 121 cbs.ChunkStore.Put(c) 122 } 123 124 func (cbs *checkingChunkStore) Flush() { 125 cbs.a.Empty(cbs.expectedOrder) 126 } 127 128 func TestFlushOrder(t *testing.T) { 129 assert := assert.New(t) 130 storage := &chunks.TestStorage{} 131 ccs := &checkingChunkStore{storage.NewView(), assert, nil} 132 vs := NewValueStore(ccs) 133 // Graph, which should be flushed grandchildren-first, bottom-up 134 // l 135 // / \ 136 // ml1 ml2 137 // / \ \ 138 // b ml f 139 // / \ 140 // s n 141 // 142 // Expected order: s, n, b, ml, f, ml1, ml2, l 143 s := String("oy") 144 n := Number(42) 145 sr, nr := vs.WriteValue(s), vs.WriteValue(n) 146 ccs.expect(sr, nr) 147 ml := NewList(vs, sr, nr) 148 149 b := NewEmptyBlob(vs) 150 br, mlr := vs.WriteValue(b), vs.WriteValue(ml) 151 ccs.expect(br, mlr) 152 ml1 := NewList(vs, br, mlr) 153 154 f := Bool(false) 155 fr := vs.WriteValue(f) 156 ccs.expect(fr) 157 ml2 := NewList(vs, fr) 158 159 ml1r, ml2r := vs.WriteValue(ml1), vs.WriteValue(ml2) 160 ccs.expect(ml1r, ml2r) 161 l := NewList(vs, ml1r, ml2r) 162 163 r := vs.WriteValue(l) 164 ccs.expect(r) 165 vs.Commit(vs.Root(), vs.Root()) 166 } 167 168 func TestFlushOverSize(t *testing.T) { 169 assert := assert.New(t) 170 storage := &chunks.TestStorage{} 171 ccs := &checkingChunkStore{storage.NewView(), assert, nil} 172 vs := newValueStoreWithCacheAndPending(ccs, 0, 30) 173 174 s := String("oy") 175 sr := vs.WriteValue(s) 176 ccs.expect(sr) 177 NewList(vs, sr) // will write the root chunk 178 } 179 180 func TestTolerateTopDown(t *testing.T) { 181 assert := assert.New(t) 182 storage := &chunks.TestStorage{} 183 ccs := &checkingChunkStore{storage.NewView(), assert, nil} 184 vs := NewValueStore(ccs) 185 // Once the L-ML-S portion of this graph is written once, it's legal to make a Struct ST that contains a ref directly to ML and write it. Then you can write S and ML and Flush ST, which contitutes top-down writing. 186 // L ST 187 // \ / 188 // ML 189 // / 190 // S 191 S := String("oy") 192 sr := vs.WriteValue(S) 193 ccs.expect(sr) 194 195 ML := NewList(vs, sr) 196 mlr := vs.WriteValue(ML) 197 ccs.expect(mlr) 198 199 L := NewList(vs, mlr) 200 lr := vs.WriteValue(L) 201 ccs.expect(lr) 202 203 vs.Commit(vs.Root(), vs.Root()) 204 205 assert.Zero(len(vs.bufferedChunks)) 206 207 ST := NewStruct("", StructData{"r": mlr}) 208 str := vs.WriteValue(ST) // ST into bufferedChunks 209 vs.WriteValue(S) // S into bufferedChunks 210 vs.WriteValue(ML) // ML into bufferedChunks AND withBufferedChunks 211 212 // At this point, ValueStore believes ST is a standalone chunk, and that ML -> S 213 // So, it'll look at ML, the one parent it knows about, first and write its child (S). Then, it'll write ML, and then it'll flush the remaining buffered chunks, which is just ST. 214 ccs.expect(sr, mlr, str) 215 vs.Commit(vs.Root(), vs.Root()) 216 } 217 218 func TestPanicOnBadVersion(t *testing.T) { 219 storage := &chunks.MemoryStorage{} 220 t.Run("Read", func(t *testing.T) { 221 cvs := NewValueStore(&badVersionStore{ChunkStore: storage.NewView()}) 222 assert.Panics(t, func() { cvs.ReadValue(hash.Hash{}) }) 223 }) 224 t.Run("Write", func(t *testing.T) { 225 cvs := NewValueStore(&badVersionStore{ChunkStore: storage.NewView()}) 226 assert.Panics(t, func() { 227 cvs.WriteValue(NewEmptyBlob(cvs)) 228 cvs.Commit(cvs.Root(), cvs.Root()) 229 }) 230 }) 231 } 232 233 func TestPanicIfDangling(t *testing.T) { 234 assert := assert.New(t) 235 vs := newTestValueStore() 236 237 r := NewRef(Bool(true)) 238 l := NewList(vs, r) 239 vs.WriteValue(l) 240 241 assert.Panics(func() { 242 vs.Commit(vs.Root(), vs.Root()) 243 }) 244 } 245 246 func TestSkipEnforceCompleteness(t *testing.T) { 247 vs := newTestValueStore() 248 vs.SetEnforceCompleteness(false) 249 250 r := NewRef(Bool(true)) 251 l := NewList(vs, r) 252 vs.WriteValue(l) 253 254 vs.Commit(vs.Root(), vs.Root()) 255 } 256 257 type badVersionStore struct { 258 chunks.ChunkStore 259 } 260 261 func (b *badVersionStore) Version() string { 262 return "BAD" 263 }