@@ -9,7 +9,6 @@ import Control.Monad.IOSim (runSimOrThrow)
99import Control.Tracer
1010import Data.Bifunctor (Bifunctor (.. ))
1111import Data.Maybe (fromJust )
12- import qualified Data.Set as Set
1312import qualified Data.Vector as V
1413import Data.Word
1514import Database.LSMTree.Extras (showPowersOf10 )
@@ -28,10 +27,10 @@ import System.FS.Sim.Error hiding (genErrors)
2827import qualified System.FS.Sim.MockFS as MockFS
2928import Test.Database.LSMTree.Internal.Snapshot.Codec ()
3029import Test.QuickCheck
31- import Test.QuickCheck.Gen (genDouble )
3230import Test.Tasty
3331import Test.Tasty.QuickCheck
3432import Test.Util.FS
33+ import Test.Util.QC (Choice )
3534
3635tests :: TestTree
3736tests = testGroup " Test.Database.LSMTree.Internal.Snapshot.FS" [
@@ -162,15 +161,7 @@ instance Arbitrary TestErrors where
162161 Snapshot corruption
163162-------------------------------------------------------------------------------}
164163
165- -- | A 'Double' in the @[0, 1)@ range.
166- newtype Double_0_1 = Double_0_1 Double
167- deriving stock (Show , Eq )
168-
169- instance Arbitrary Double_0_1 where
170- arbitrary = Double_0_1 <$> genDouble
171- shrink (Double_0_1 x) = [Double_0_1 x' | x' <- shrink x, 0 <= x', x' < 1 ]
172-
173- -- TODO: an alternative to generating doubles a priori is to run the monadic
164+ -- TODO: an alternative to generating a Choice a priori is to run the monadic
174165-- code in @PropertyM (IOSim s)@, and then we can do quantification inside the
175166-- monadic property using @pick@. This complicates matters, however, because
176167-- functions like @withSimHasBlockIO@ and @withTable@ would have to run in
@@ -179,14 +170,9 @@ instance Arbitrary Double_0_1 where
179170prop_flipSnapshotBit ::
180171 Positive (Small Int )
181172 -> V. Vector (Word64 , Entry Word64 Word64 )
182- -> Double_0_1 -- ^ Used to pick which file to corrupt
183- -> Double_0_1 -- ^ Used to pick which bit to flip in the file we picked
173+ -> Choice -- ^ Used to pick which file/bit to corrupt.
184174 -> Property
185- prop_flipSnapshotBit
186- (Positive (Small bufferSize))
187- es
188- (Double_0_1 pickFile)
189- (Double_0_1 pickBit) =
175+ prop_flipSnapshotBit (Positive (Small bufferSize)) es pickFileBit =
190176 runSimOrThrow $
191177 withSimHasBlockIO propNoOpenHandles MockFS. empty $ \ hfs hbio _fsVar ->
192178 withSession nullTracer hfs hbio root $ \ s ->
@@ -195,42 +181,28 @@ prop_flipSnapshotBit
195181 updates resolve es' t
196182 createSnap t
197183
198- -- Pick a random file from the named snapshot directory
199- files <- listDirectoryRecursiveFiles hfs (getNamedSnapshotDir namedSnapDir)
200- let i = round (fromIntegral (Set. size files - 1 ) * pickFile)
201- let file = Set. elemAt i files
202- let path = getNamedSnapshotDir namedSnapDir </> file
203- -- Pick a random bit from the file that we want to corrupt
204- n <- withFile hfs path ReadMode $ hGetSize hfs
205- let j = round (fromIntegral (n * 8 - 1 ) * pickBit)
206-
207- -- Some info for the test output
208- let
209- tabCorruptedFile = tabulate " Corrupted file" [show path]
210- counterCorruptedFile = counterexample (" Corrupted file: " ++ show path)
211- tabFlippedBit = tabulate " Flipped bit" [showPowersOf10 j]
212- counterFlippedBit = counterexample (" Flipped bit: " ++ show j)
213-
214- -- TODO: check forgotten refs
215- if n <= 0 then -- file is empty
216- pure $ tabulate " Result" [" No corruption applied" ] True
217- else do -- file is non-empty
218-
219- -- Flip a bit and try to open the snapshot
220- flipFileBit hfs path j
221- t' <- try @ _ @ SomeException $ bracket (openSnap s) close $ \ _ -> pure ()
222-
223- pure $
224- tabCorruptedFile $ counterCorruptedFile $ tabFlippedBit $ counterFlippedBit $
225- case t' of
226- -- If we find an error, we detected corruption. Success!
227- Left e ->
228- tabulate
229- " Result"
230- [" Corruption detected: " <> getConstructorName e]
231- True
232- -- The corruption was not detected. Failure!
233- Right _ -> property False
184+ -- Corrupt the snapshot
185+ flipRandomBitInRandomFile hfs pickFileBit (getNamedSnapshotDir namedSnapDir) >>= \ case
186+ Nothing -> pure $ property False
187+ Just (path, j) -> do
188+ -- Some info for the test output
189+ let tabCorruptedFile = tabulate " Corrupted file" [show path]
190+ counterCorruptedFile = counterexample (" Corrupted file: " ++ show path)
191+ tabFlippedBit = tabulate " Flipped bit" [showPowersOf10 j]
192+ counterFlippedBit = counterexample (" Flipped bit: " ++ show j)
193+
194+ t' <- try @ _ @ SomeException $ bracket (openSnap s) close $ \ _ -> pure ()
195+ pure $
196+ tabCorruptedFile $ counterCorruptedFile $ tabFlippedBit $ counterFlippedBit $
197+ case t' of
198+ -- If we find an error, we detected corruption. Success!
199+ Left e ->
200+ tabulate
201+ " Result"
202+ [" Corruption detected: " <> getConstructorName e]
203+ True
204+ -- The corruption was not detected. Failure!
205+ Right _ -> property False
234206 where
235207 root = FS. mkFsPath []
236208 namedSnapDir = namedSnapshotDir (SessionRoot root) snapName
0 commit comments