Skip to content

Commit e2aa833

Browse files
committed
feat(issue558): add snapshot corruption to state machine tests
1 parent d2a5bbc commit e2aa833

File tree

3 files changed

+65
-16
lines changed

3 files changed

+65
-16
lines changed

test/Database/LSMTree/Class.hs

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ module Database.LSMTree.Class (
1414
, module Types
1515
) where
1616

17+
import Control.Monad (void)
1718
import Control.Monad.Class.MonadThrow (MonadThrow (..))
1819
import Data.Kind (Constraint, Type)
1920
import Data.List.NonEmpty (NonEmpty)
@@ -24,6 +25,10 @@ import Database.LSMTree as Types (LookupResult (..), QueryResult (..),
2425
resolveDeserialised)
2526
import qualified Database.LSMTree as R
2627
import Database.LSMTree.Class.Common as Common
28+
import qualified Database.LSMTree.Internal as RI (SessionEnv (..), Table (..),
29+
Table' (..), withOpenSession)
30+
import qualified Database.LSMTree.Internal.Paths as RIP
31+
import Test.Util.FS (flipRandomBitInRandomFile)
2732
import Test.Util.QC (Choice)
2833

2934
-- | Class abstracting over table operations.
@@ -231,6 +236,21 @@ withCursor offset tbl = bracket (newCursor offset tbl) (closeCursor (Proxy @h))
231236
Real instance
232237
-------------------------------------------------------------------------------}
233238

239+
-- | Snapshot corruption for the real instance.
240+
-- Implemented here, instead of as part of the public API.
241+
rCorruptSnapshot ::
242+
IOLike m
243+
=> Choice
244+
-> SnapshotName
245+
-> R.Table m k v b
246+
-> m ()
247+
rCorruptSnapshot choice name (RI.Table' t) =
248+
RI.withOpenSession (RI.tableSession t) $ \seshEnv ->
249+
let hfs = RI.sessionHasFS seshEnv
250+
root = RI.sessionRoot seshEnv
251+
namedSnapDir = RIP.getNamedSnapshotDir (RIP.namedSnapshotDir root name)
252+
in void $ flipRandomBitInRandomFile hfs choice namedSnapDir
253+
234254
instance IsTable R.Table where
235255
type Session R.Table = R.Session
236256
type TableConfig R.Table = R.TableConfig
@@ -253,7 +273,7 @@ instance IsTable R.Table where
253273
readCursor _ = R.readCursor
254274

255275
createSnapshot = R.createSnapshot
256-
corruptSnapshot = error "TODO: not yet implemented"
276+
corruptSnapshot = rCorruptSnapshot
257277
openSnapshot sesh snap = R.openSnapshot sesh R.configNoOverride snap
258278

259279
duplicate = R.duplicate

test/Test/Database/LSMTree/StateMachine.hs

Lines changed: 34 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,7 @@ import Control.Tracer (Tracer, nullTracer)
8383
import Data.Bifunctor (Bifunctor (..))
8484
import Data.Constraint (Dict (..))
8585
import Data.Either (partitionEithers)
86+
import Data.Foldable (for_)
8687
import Data.Kind (Type)
8788
import Data.List.NonEmpty (NonEmpty (..))
8889
import qualified Data.List.NonEmpty as NE
@@ -136,7 +137,8 @@ import qualified Test.QuickCheck.StateModel.Lockstep.Defaults as Lockstep.Defaul
136137
import qualified Test.QuickCheck.StateModel.Lockstep.Run as Lockstep.Run
137138
import Test.Tasty (TestTree, testGroup)
138139
import Test.Tasty.QuickCheck (testProperty)
139-
import Test.Util.FS (approximateEqStream, noRemoveDirectoryRecursiveE,
140+
import Test.Util.FS (SilentCorruption (..), SilentCorruptions (..),
141+
approximateEqStream, noRemoveDirectoryRecursiveE,
140142
propNoOpenHandles, propNumOpenHandles)
141143
import Test.Util.PrettyProxy
142144
import qualified Test.Util.QLS as QLS
@@ -636,7 +638,7 @@ instance ( Show (Class.TableConfig h)
636638
-- Snapshots
637639
CreateSnapshot ::
638640
C k v b
639-
=> Maybe Errors
641+
=> Maybe (Either SilentCorruptions Errors)
640642
-> R.SnapshotLabel -> R.SnapshotName -> Var h (WrapTable h IO k v b)
641643
-> Act h ()
642644
OpenSnapshot ::
@@ -1170,11 +1172,12 @@ runModel lookUp = \case
11701172
RetrieveBlobs blobsVar ->
11711173
wrap (MVector . fmap (MBlob . WrapBlob))
11721174
. Model.runModelM (Model.retrieveBlobs (getBlobRefs . lookUp $ blobsVar))
1173-
CreateSnapshot merrs label name tableVar ->
1174-
wrap MUnit
1175-
. Model.runModelMWithInjectedErrors merrs
1176-
(Model.createSnapshot label name (getTable $ lookUp tableVar))
1177-
(pure ())
1175+
CreateSnapshot mcorrsOrErrs label name tableVar ->
1176+
wrap MUnit .
1177+
let mCreateSnapshot = Model.createSnapshot label name (getTable $ lookUp tableVar)
1178+
in case sequence mcorrsOrErrs of
1179+
Left _corrs -> Model.runModelM (mCreateSnapshot >> Model.corruptSnapshot name)
1180+
Right merrs -> Model.runModelMWithInjectedErrors merrs mCreateSnapshot (pure ())
11781181
OpenSnapshot _ merrs label name ->
11791182
wrap MTable
11801183
. Model.runModelMWithInjectedErrors merrs
@@ -1256,10 +1259,18 @@ runIO action lookUp = ReaderT $ \ !env -> do
12561259
Class.mupserts (unwrapTable $ lookUp' tableVar) kmups
12571260
RetrieveBlobs blobRefsVar -> catchErr handlers $
12581261
fmap WrapBlob <$> Class.retrieveBlobs (Proxy @h) session (unwrapBlobRef <$> lookUp' blobRefsVar)
1259-
CreateSnapshot merrs label name tableVar ->
1260-
runRealWithInjectedErrors "CreateSnapshot" env merrs
1261-
(Class.createSnapshot label name (unwrapTable $ lookUp' tableVar))
1262-
(\() -> Class.deleteSnapshot session name)
1262+
CreateSnapshot mcorrsOrErrs label name tableVar ->
1263+
let rCreateSnapshot = Class.createSnapshot label name (unwrapTable $ lookUp' tableVar) in
1264+
case sequence mcorrsOrErrs of
1265+
Left (SilentCorruptions corrs) -> do
1266+
rCreateSnapshot
1267+
for_ corrs $ \corr ->
1268+
Class.corruptSnapshot (bitChoice corr) name (unwrapTable $ lookUp' tableVar)
1269+
pure (Right ())
1270+
Right merrs ->
1271+
runRealWithInjectedErrors "CreateSnapshot" env merrs
1272+
rCreateSnapshot
1273+
(\() -> Class.deleteSnapshot session name)
12631274
OpenSnapshot _ merrs label name ->
12641275
runRealWithInjectedErrors "OpenSnapshot" env merrs
12651276
(WrapTable <$> Class.openSnapshot session label name)
@@ -1318,10 +1329,18 @@ runIOSim action lookUp = ReaderT $ \ !env -> do
13181329
Class.mupserts (unwrapTable $ lookUp' tableVar) kmups
13191330
RetrieveBlobs blobRefsVar -> catchErr handlers $
13201331
fmap WrapBlob <$> Class.retrieveBlobs (Proxy @h) session (unwrapBlobRef <$> lookUp' blobRefsVar)
1321-
CreateSnapshot merrs label name tableVar ->
1322-
runRealWithInjectedErrors "CreateSnapshot" env merrs
1323-
(Class.createSnapshot label name (unwrapTable $ lookUp' tableVar))
1324-
(\() -> Class.deleteSnapshot session name)
1332+
CreateSnapshot mcorrsOrErrs label name tableVar ->
1333+
let rCreateSnapshot = Class.createSnapshot label name (unwrapTable $ lookUp' tableVar) in
1334+
case sequence mcorrsOrErrs of
1335+
Left (SilentCorruptions corrs) -> do
1336+
rCreateSnapshot
1337+
for_ corrs $ \corr ->
1338+
Class.corruptSnapshot (bitChoice corr) name (unwrapTable $ lookUp' tableVar)
1339+
pure (Right ())
1340+
Right merrs ->
1341+
runRealWithInjectedErrors "CreateSnapshot" env merrs
1342+
rCreateSnapshot
1343+
(\() -> Class.deleteSnapshot session name)
13251344
OpenSnapshot _ merrs label name ->
13261345
runRealWithInjectedErrors "OpenSnapshot" env merrs
13271346
(WrapTable <$> Class.openSnapshot session label name)

test/Test/Util/FS.hs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,8 @@ module Test.Util.FS (
3030
, listDirectoryRecursive
3131
, listDirectoryRecursiveFiles
3232
-- * Corruption
33+
, SilentCorruptions (..)
34+
, SilentCorruption (..)
3335
, flipRandomBitInRandomFile
3436
, flipFileBit
3537
, hFlipBit
@@ -351,6 +353,14 @@ listDirectoryFiles hfs = go Set.empty
351353
Corruption
352354
-------------------------------------------------------------------------------}
353355

356+
newtype SilentCorruptions = SilentCorruptions {unSilentCorruptions :: NonEmpty SilentCorruption}
357+
deriving stock (Eq, Show)
358+
deriving newtype (Arbitrary)
359+
360+
newtype SilentCorruption = SilentCorruption {bitChoice :: Choice}
361+
deriving stock (Eq, Show)
362+
deriving newtype (Arbitrary)
363+
354364
-- | Flip a random bit in a random file in a given directory.
355365
flipRandomBitInRandomFile ::
356366
(PrimMonad m, MonadThrow m)

0 commit comments

Comments
 (0)