@@ -27,7 +27,9 @@ import qualified Data.Vector.Unboxed as VU
2727import qualified Data.Vector.Unboxed.Base as VU
2828import Data.Word
2929import 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 )
3133import Database.LSMTree.Extras.Index (Append (.. ), appendToCompact )
3234import Database.LSMTree.Internal.BitMath
3335import Database.LSMTree.Internal.Chunk as Chunk (toByteString )
@@ -36,6 +38,8 @@ import Database.LSMTree.Internal.Index.Compact
3638import Database.LSMTree.Internal.Index.CompactAcc
3739import Database.LSMTree.Internal.Page (PageNo (PageNo ), PageSpan ,
3840 multiPage , singlePage )
41+ import Database.LSMTree.Internal.RawBytes (RawBytes (.. ))
42+ import qualified Database.LSMTree.Internal.RawBytes as RB
3943import Database.LSMTree.Internal.Serialise
4044import Numeric (showHex )
4145import Prelude hiding (max , min , pi )
@@ -51,14 +55,16 @@ import Text.Printf (printf)
5155
5256tests :: TestTree
5357tests = 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
5662 , testProperty " prop_searchMinMaxKeysAfterConstruction" $
57- prop_searchMinMaxKeysAfterConstruction @ BiasedKeyForIndexCompact 100
63+ prop_searchMinMaxKeysAfterConstruction @ TestKey 100
5864 , testProperty " prop_differentChunkSizesSameResults" $
59- prop_differentChunkSizesSameResults @ BiasedKeyForIndexCompact
65+ prop_differentChunkSizesSameResults @ TestKey
6066 , testProperty " prop_singlesEquivMulti" $
61- prop_singlesEquivMulti @ BiasedKeyForIndexCompact
67+ prop_singlesEquivMulti @ TestKey
6268 , testGroup " (De)serialisation" [
6369 testGroup " Chunks generator" $
6470 prop_arbitraryAndShrinkPreserveInvariant noTags chunksInvariant
@@ -119,14 +125,65 @@ tests = testGroup "Test.Database.LSMTree.Internal.Index.Compact" [
119125 , testProperty " prop_roundtrip_chunks" $
120126 prop_roundtrip_chunks
121127 , testProperty " prop_roundtrip" $
122- prop_roundtrip @ BiasedKeyForIndexCompact
128+ prop_roundtrip @ TestKey
123129 , testProperty " prop_total_deserialisation" $ withMaxSuccess 10000
124130 prop_total_deserialisation
125131 , testProperty " prop_total_deserialisation_whitebox" $ withMaxSuccess 10000
126132 prop_total_deserialisation_whitebox
127133 ]
128134 ]
129135
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+
130187{- ------------------------------------------------------------------------------
131188 Properties
132189-------------------------------------------------------------------------------}
@@ -319,11 +376,15 @@ fromListSingles maxcsize apps = runST $ do
319376
320377labelIndex :: IndexCompact -> (Property -> Property )
321378labelIndex 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)
325385 . 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"
327388 where nclashes = countClashes ic
328389 nscontig = countContiguousClashes ic
329390
0 commit comments