@@ -27,7 +27,9 @@ import qualified Data.Vector.Unboxed as VU
27
27
import qualified Data.Vector.Unboxed.Base as VU
28
28
import Data.Word
29
29
import Database.LSMTree.Extras
30
- import Database.LSMTree.Extras.Generators as Gen
30
+ import Database.LSMTree.Extras.Generators (ChunkSize (.. ),
31
+ LogicalPageSummaries , LogicalPageSummary (.. ), Pages (.. ),
32
+ genRawBytes , isKeyForIndexCompact , labelPages , toAppends )
31
33
import Database.LSMTree.Extras.Index (Append (.. ), appendToCompact )
32
34
import Database.LSMTree.Internal.BitMath
33
35
import Database.LSMTree.Internal.Chunk as Chunk (toByteString )
@@ -36,6 +38,8 @@ import Database.LSMTree.Internal.Index.Compact
36
38
import Database.LSMTree.Internal.Index.CompactAcc
37
39
import Database.LSMTree.Internal.Page (PageNo (PageNo ), PageSpan ,
38
40
multiPage , singlePage )
41
+ import Database.LSMTree.Internal.RawBytes (RawBytes (.. ))
42
+ import qualified Database.LSMTree.Internal.RawBytes as RB
39
43
import Database.LSMTree.Internal.Serialise
40
44
import Numeric (showHex )
41
45
import Prelude hiding (max , min , pi )
@@ -51,14 +55,16 @@ import Text.Printf (printf)
51
55
52
56
tests :: TestTree
53
57
tests = testGroup " Test.Database.LSMTree.Internal.Index.Compact" [
54
- testProperty " prop_distribution @BiasedKeyForIndexCompact" $
55
- prop_distribution @ BiasedKeyForIndexCompact
58
+ testGroup " TestKey" $
59
+ prop_arbitraryAndShrinkPreserveInvariant @ TestKey noTags isTestKey
60
+ , testProperty " prop_distribution @TestKey" $
61
+ prop_distribution @ TestKey
56
62
, testProperty " prop_searchMinMaxKeysAfterConstruction" $
57
- prop_searchMinMaxKeysAfterConstruction @ BiasedKeyForIndexCompact 100
63
+ prop_searchMinMaxKeysAfterConstruction @ TestKey 100
58
64
, testProperty " prop_differentChunkSizesSameResults" $
59
- prop_differentChunkSizesSameResults @ BiasedKeyForIndexCompact
65
+ prop_differentChunkSizesSameResults @ TestKey
60
66
, testProperty " prop_singlesEquivMulti" $
61
- prop_singlesEquivMulti @ BiasedKeyForIndexCompact
67
+ prop_singlesEquivMulti @ TestKey
62
68
, testGroup " (De)serialisation" [
63
69
testGroup " Chunks generator" $
64
70
prop_arbitraryAndShrinkPreserveInvariant noTags chunksInvariant
@@ -119,14 +125,65 @@ tests = testGroup "Test.Database.LSMTree.Internal.Index.Compact" [
119
125
, testProperty " prop_roundtrip_chunks" $
120
126
prop_roundtrip_chunks
121
127
, testProperty " prop_roundtrip" $
122
- prop_roundtrip @ BiasedKeyForIndexCompact
128
+ prop_roundtrip @ TestKey
123
129
, testProperty " prop_total_deserialisation" $ withMaxSuccess 10000
124
130
prop_total_deserialisation
125
131
, testProperty " prop_total_deserialisation_whitebox" $ withMaxSuccess 10000
126
132
prop_total_deserialisation_whitebox
127
133
]
128
134
]
129
135
136
+ {- ------------------------------------------------------------------------------
137
+ Test key
138
+ -------------------------------------------------------------------------------}
139
+
140
+ -- | Key type for compact index tests
141
+ --
142
+ -- Tests outside this module don't have to worry about generating clashing keys.
143
+ -- We can assume that the compact index handles clashes correctly, because we
144
+ -- test this extensively in this module already.
145
+ newtype TestKey = TestKey RawBytes
146
+ deriving stock (Show , Eq , Ord )
147
+ deriving newtype SerialiseKey
148
+
149
+ -- | Generate keys with a non-neglible probability of clashes. This generates
150
+ -- sliced keys too.
151
+ --
152
+ -- Note: recall that keys /clash/ only if their primary bits (first 8 bytes)
153
+ -- match. It does not matter whether the other bytes do not match.
154
+ instance Arbitrary TestKey where
155
+ arbitrary = do
156
+ -- Generate primary bits from a relatively small distribution. This
157
+ -- ensures that we get clashes between keys with a non-negligible
158
+ -- probability.
159
+ primBits <- do
160
+ lastPrefixByte <- QC. getSmall <$> arbitrary
161
+ pure $ RB. pack ([0 ,0 ,0 ,0 ,0 ,0 ,0 ] <> [lastPrefixByte])
162
+ -- The rest of the bits after the primary bits can be anything
163
+ restBits <- genRawBytes
164
+ -- The compact index should store keys without retaining unused memory.
165
+ -- Therefore, we generate slices of keys too.
166
+ prefix <- elements [RB. pack [] , RB. pack [0 ]]
167
+ suffix <- elements [RB. pack [] , RB. pack [0 ]]
168
+ -- Combine the bytes and make sure to take out only the slice we need.
169
+ let bytes = prefix <> primBits <> restBits <> suffix
170
+ n = RB. size primBits + RB. size restBits
171
+ bytes' = RB. take n $ RB. drop (RB. size prefix) bytes
172
+ pure $ TestKey bytes'
173
+
174
+ -- Shrink keys extensively: most failures will occur in small counterexamples,
175
+ -- so we don't have to limit the number of shrinks as much.
176
+ shrink (TestKey bytes) = [
177
+ TestKey bytes'
178
+ | let RawBytes vec = bytes
179
+ , vec' <- VP. fromList <$> shrink (VP. toList vec)
180
+ , let bytes' = RawBytes vec'
181
+ , isKeyForIndexCompact bytes'
182
+ ]
183
+
184
+ isTestKey :: TestKey -> Bool
185
+ isTestKey (TestKey bytes) = isKeyForIndexCompact bytes
186
+
130
187
{- ------------------------------------------------------------------------------
131
188
Properties
132
189
-------------------------------------------------------------------------------}
@@ -319,11 +376,15 @@ fromListSingles maxcsize apps = runST $ do
319
376
320
377
labelIndex :: IndexCompact -> (Property -> Property )
321
378
labelIndex ic =
322
- QC. tabulate " # Clashes" [showPowersOf10 nclashes]
323
- . QC. tabulate " # Contiguous clash runs" [showPowersOf10 (length nscontig)]
324
- . QC. tabulate " Length of contiguous clash runs" (fmap (showPowersOf10 . snd ) nscontig)
379
+ checkCoverage
380
+ . QC. tabulate " # Clashes" [showPowersOf 2 nclashes]
381
+ . QC. cover 60 (nclashes > 0 ) " Has clashes"
382
+ . QC. tabulate " # Contiguous clash runs" [showPowersOf 2 (length nscontig)]
383
+ . QC. cover 30 (not (null nscontig)) " Has contiguous clash runs"
384
+ . QC. tabulate " Length of contiguous clash runs" (fmap (showPowersOf 2 . snd ) nscontig)
325
385
. QC. tabulate " Contiguous clashes contain multi-page values" (fmap (show . fst ) nscontig)
326
- . QC. classify (multiPageValuesClash ic) " Has clashing multi-page values"
386
+ . QC. cover 3 (any fst nscontig) " Has contiguous clashes that contain multi-page values"
387
+ . QC. cover 0.1 (multiPageValuesClash ic) " Has clashing multi-page values"
327
388
where nclashes = countClashes ic
328
389
nscontig = countContiguousClashes ic
329
390
0 commit comments