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  }