mirror of
https://github.com/urbit/shrub.git
synced 2024-12-30 07:35:19 +03:00
188 lines
5.9 KiB
Haskell
188 lines
5.9 KiB
Haskell
module LogTests (tests) where
|
|
|
|
import Data.Acquire
|
|
import Test.QuickCheck hiding ((.&.))
|
|
import Test.Tasty
|
|
import Test.Tasty.QuickCheck
|
|
import Test.Tasty.TH
|
|
import UrbitPrelude
|
|
import Vere.Log
|
|
import Vere.Pier.Types
|
|
import Data.Conduit
|
|
import Data.Conduit.List hiding (filter)
|
|
|
|
import Control.Concurrent (threadDelay, runInBoundThread)
|
|
import Data.LargeWord (LargeKey(..))
|
|
import GHC.Natural (Natural)
|
|
|
|
import qualified Vere.Log as Log
|
|
|
|
|
|
-- Utils -----------------------------------------------------------------------
|
|
|
|
withTestDir :: (FilePath -> IO a) -> IO a
|
|
withTestDir = withTempDirectory "./" ".testlog."
|
|
|
|
data NotEqual = NotEqual String String
|
|
deriving (Eq, Ord, Show)
|
|
|
|
instance Exception NotEqual where
|
|
|
|
assertEqual :: (Show a, Eq a) => a -> a -> IO ()
|
|
assertEqual x y = do
|
|
unless (x == y) $
|
|
throwIO (NotEqual (show x) (show y))
|
|
|
|
|
|
-- Database Operations ---------------------------------------------------------
|
|
|
|
data Db = Db LogIdentity [ByteString] (Map Word64 ByteString)
|
|
deriving (Eq, Ord, Show)
|
|
|
|
addEvents :: Db -> [ByteString] -> Db
|
|
addEvents (Db id evs efs) new = Db id (evs <> new) efs
|
|
|
|
readDb :: EventLog -> IO Db
|
|
readDb log = do
|
|
events <- runConduit (streamEvents log 1 .| consume)
|
|
effects <- runConduit (streamEffectsRows log 0 .| consume)
|
|
pure $ Db (Log.identity log) events (mapFromList effects)
|
|
|
|
withDb :: FilePath -> Db -> (EventLog -> IO a) -> IO a
|
|
withDb dir (Db dId dEvs dFx) act = do
|
|
with (Log.new dir dId) $ \log -> do
|
|
Log.appendEvents log (fromList dEvs)
|
|
for_ (mapToList dFx) $ \(k,v) ->
|
|
Log.writeEffectsRow log k v
|
|
act log
|
|
|
|
--------------------------------------------------------------------------------
|
|
|
|
tryReadIdentity :: Property
|
|
tryReadIdentity = forAll arbitrary (ioProperty . runTest)
|
|
where
|
|
runTest :: LogIdentity -> IO Bool
|
|
runTest ident = do
|
|
runInBoundThread $
|
|
withTestDir $ \dir -> do
|
|
with (Log.new dir ident) $ \log ->
|
|
assertEqual ident (Log.identity log)
|
|
with (Log.existing dir) $ \log ->
|
|
assertEqual ident (Log.identity log)
|
|
with (Log.existing dir) $ \log ->
|
|
assertEqual ident (Log.identity log)
|
|
pure True
|
|
|
|
tryReadDatabase :: Property
|
|
tryReadDatabase = forAll arbitrary (ioProperty . runTest)
|
|
where
|
|
runTest :: Db -> IO Bool
|
|
runTest db = do
|
|
runInBoundThread $
|
|
withTestDir $ \dir -> do
|
|
withDb dir db $ \log -> do
|
|
readDb log >>= assertEqual db
|
|
pure True
|
|
|
|
tryReadDatabaseFuzz :: Property
|
|
tryReadDatabaseFuzz = forAll arbitrary (ioProperty . runTest)
|
|
where
|
|
runTest :: Db -> IO Bool
|
|
runTest db = do
|
|
runInBoundThread $
|
|
withTestDir $ \dir -> do
|
|
withDb dir db $ \log -> do
|
|
readDb log >>= assertEqual db
|
|
with (Log.existing dir) $ \log -> do
|
|
readDb log >>= assertEqual db
|
|
with (Log.existing dir) $ \log -> do
|
|
readDb log >>= assertEqual db
|
|
readDb log >>= assertEqual db
|
|
pure True
|
|
|
|
tryAppend :: Property
|
|
tryAppend = forAll arbitrary (ioProperty . runTest)
|
|
where
|
|
runTest :: ([ByteString], Db) -> IO Bool
|
|
runTest (extra, db) = do
|
|
runInBoundThread $
|
|
withTestDir $ \dir -> do
|
|
db' <- pure (addEvents db extra)
|
|
withDb dir db $ \log -> do
|
|
readDb log >>= assertEqual db
|
|
Log.appendEvents log (fromList extra)
|
|
readDb log >>= assertEqual db'
|
|
with (Log.existing dir) $ \log -> do
|
|
readDb log >>= assertEqual db'
|
|
pure True
|
|
|
|
tryAppendHuge :: Property
|
|
tryAppendHuge = forAll arbitrary (ioProperty . runTest)
|
|
where
|
|
runTest :: ([ByteString], Db) -> IO Bool
|
|
runTest (extra, db) = do
|
|
runInBoundThread $ do
|
|
extra <- do b <- readFile "/home/benajmin/r/urbit/bin/brass.pill"
|
|
pure (extra <> [b] <> extra)
|
|
withTestDir $ \dir -> do
|
|
db' <- pure (addEvents db extra)
|
|
withDb dir db $ \log -> do
|
|
readDb log >>= assertEqual db
|
|
Log.appendEvents log (fromList extra)
|
|
readDb log >>= assertEqual db'
|
|
with (Log.existing dir) $ \log -> do
|
|
readDb log >>= assertEqual db'
|
|
pure True
|
|
|
|
|
|
tests :: TestTree
|
|
tests =
|
|
testGroup "Log"
|
|
[ localOption (QuickCheckTests 10) $
|
|
testProperty "Read/Write Log Identity" $
|
|
tryReadIdentity
|
|
, localOption (QuickCheckTests 15) $
|
|
testProperty "Read/Write Database" $
|
|
tryReadDatabase
|
|
, localOption (QuickCheckTests 5) $
|
|
testProperty "Read/Write Database Multiple Times" $
|
|
tryReadDatabaseFuzz
|
|
, localOption (QuickCheckTests 10) $
|
|
testProperty "Append Random Events" $
|
|
tryAppend
|
|
, localOption (QuickCheckTests 1) $
|
|
testProperty "Append Huge Events" $
|
|
tryAppendHuge
|
|
]
|
|
|
|
|
|
-- Generate Arbitrary Values ---------------------------------------------------
|
|
|
|
arb :: Arbitrary a => Gen a
|
|
arb = arbitrary
|
|
|
|
instance Arbitrary ByteString where
|
|
arbitrary = pack <$> arbitrary
|
|
|
|
instance (Arbitrary a, Arbitrary b) => Arbitrary (LargeKey a b) where
|
|
arbitrary = LargeKey <$> arb <*> arb
|
|
|
|
instance Arbitrary Ship where
|
|
arbitrary = Ship <$> arb
|
|
|
|
arbEffects :: [ByteString] -> Gen (Map Word64 ByteString)
|
|
arbEffects evs = do
|
|
hax <- for (zip [1..] evs) $ \(i, bs) -> do keep :: Bool <- arbitrary
|
|
pure (keep, (i, bs))
|
|
pure $ mapFromList $ snd <$> filter fst hax
|
|
|
|
instance Arbitrary Db where
|
|
arbitrary = do
|
|
ident <- arbitrary
|
|
evs <- arbitrary
|
|
efs <- arbEffects evs
|
|
pure (Db ident evs efs)
|
|
|
|
instance Arbitrary LogIdentity where
|
|
arbitrary = LogIdentity <$> arb <*> arb <*> arb
|