Skip to content

Commit e675ace

Browse files
committed
Add a custom TestKey type for compact index tests
Outside of direct tests on the compact index, clashes are arguably not so important: we can assume that the compact index handles clashes correctly because we already test this extensively. This also fixes #580, restoring the generation of clashing keys.
1 parent a285e36 commit e675ace

File tree

1 file changed

+60
-2
lines changed
  • test/Test/Database/LSMTree/Internal/Index

1 file changed

+60
-2
lines changed

test/Test/Database/LSMTree/Internal/Index/Compact.hs

Lines changed: 60 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,10 @@ import qualified Data.Vector.Unboxed as VU
2727
import qualified Data.Vector.Unboxed.Base as VU
2828
import Data.Word
2929
import Database.LSMTree.Extras
30-
import Database.LSMTree.Extras.Generators as Gen
30+
import Database.LSMTree.Extras.Generators (BiasedKeyForIndexCompact,
31+
ChunkSize (..), LogicalPageSummaries,
32+
LogicalPageSummary (..), Pages (..), genRawBytes,
33+
isKeyForIndexCompact, labelPages, toAppends)
3134
import Database.LSMTree.Extras.Index (Append (..), appendToCompact)
3235
import Database.LSMTree.Internal.BitMath
3336
import Database.LSMTree.Internal.Chunk as Chunk (toByteString)
@@ -36,6 +39,8 @@ import Database.LSMTree.Internal.Index.Compact
3639
import Database.LSMTree.Internal.Index.CompactAcc
3740
import Database.LSMTree.Internal.Page (PageNo (PageNo), PageSpan,
3841
multiPage, singlePage)
42+
import Database.LSMTree.Internal.RawBytes (RawBytes (..))
43+
import qualified Database.LSMTree.Internal.RawBytes as RB
3944
import Database.LSMTree.Internal.Serialise
4045
import Numeric (showHex)
4146
import Prelude hiding (max, min, pi)
@@ -51,7 +56,9 @@ import Text.Printf (printf)
5156

5257
tests :: TestTree
5358
tests = testGroup "Test.Database.LSMTree.Internal.Index.Compact" [
54-
testProperty "prop_distribution @BiasedKeyForIndexCompact" $
59+
testGroup "TestKey" $
60+
prop_arbitraryAndShrinkPreserveInvariant @TestKey noTags isTestKey
61+
, testProperty "prop_distribution @BiasedKeyForIndexCompact" $
5562
prop_distribution @BiasedKeyForIndexCompact
5663
, testProperty "prop_searchMinMaxKeysAfterConstruction" $
5764
prop_searchMinMaxKeysAfterConstruction @BiasedKeyForIndexCompact 100
@@ -127,6 +134,57 @@ tests = testGroup "Test.Database.LSMTree.Internal.Index.Compact" [
127134
]
128135
]
129136

137+
{-------------------------------------------------------------------------------
138+
Test key
139+
-------------------------------------------------------------------------------}
140+
141+
-- | Key type for compact index tests
142+
--
143+
-- Tests outside this module don't have to worry about generating clashing keys.
144+
-- We can assume that the compact index handles clashes correctly, because we
145+
-- test this extensively in this module already.
146+
newtype TestKey = TestKey RawBytes
147+
deriving stock (Show, Eq, Ord)
148+
deriving newtype SerialiseKey
149+
150+
-- | Generate keys with a non-neglible probability of clashes. This generates
151+
-- sliced keys too.
152+
--
153+
-- Note: recall that keys /clash/ only if their primary bits (first 8 bytes)
154+
-- match. It does not matter whether the other bytes do not match.
155+
instance Arbitrary TestKey where
156+
arbitrary = do
157+
-- Generate primary bits from a relatively small distribution. This
158+
-- ensures that we get clashes between keys with a non-negligible
159+
-- probability.
160+
primBits <- do
161+
lastPrefixByte <- QC.getSmall <$> arbitrary
162+
pure $ RB.pack ([0,0,0,0,0,0,0] <> [lastPrefixByte])
163+
-- The rest of the bits after the primary bits can be anything
164+
restBits <- genRawBytes
165+
-- The compact index should store keys without retaining unused memory.
166+
-- Therefore, we generate slices of keys too.
167+
prefix <- elements [RB.pack [], RB.pack [0]]
168+
suffix <- elements [RB.pack [], RB.pack [0]]
169+
-- Combine the bytes and make sure to take out only the slice we need.
170+
let bytes = prefix <> primBits <> restBits <> suffix
171+
n = RB.size primBits + RB.size restBits
172+
bytes' = RB.take n $ RB.drop (RB.size prefix) bytes
173+
pure $ TestKey bytes'
174+
175+
-- Shrink keys extensively: most failures will occur in small counterexamples,
176+
-- so we don't have to limit the number of shrinks as much.
177+
shrink (TestKey bytes) = [
178+
TestKey bytes'
179+
| let RawBytes vec = bytes
180+
, vec' <- VP.fromList <$> shrink (VP.toList vec)
181+
, let bytes' = RawBytes vec'
182+
, isKeyForIndexCompact bytes'
183+
]
184+
185+
isTestKey :: TestKey -> Bool
186+
isTestKey (TestKey bytes) = isKeyForIndexCompact bytes
187+
130188
{-------------------------------------------------------------------------------
131189
Properties
132190
-------------------------------------------------------------------------------}

0 commit comments

Comments
 (0)