urbit/pkg/hs/urbit-king/test/LogTests.hs

200 lines
6.4 KiB
Haskell
Raw Normal View History

module LogTests (tests) where
import Data.Acquire
2020-01-23 07:16:09 +03:00
import Data.Conduit
import Data.Conduit.List hiding (filter)
import Test.QuickCheck hiding ((.&.))
import Test.Tasty
import Test.Tasty.QuickCheck
import Test.Tasty.TH
import Urbit.EventLog.LMDB
import Urbit.Prelude
import Urbit.Vere.Pier.Types
2019-09-18 06:33:38 +03:00
import Control.Concurrent (runInBoundThread, threadDelay)
import Data.LargeWord (LargeKey(..))
import GHC.Natural (Natural)
import Urbit.King.App (KingEnv, runKingEnvNoLog)
import qualified Options
import qualified Urbit.EventLog.LMDB as Log
-- Utils -----------------------------------------------------------------------
2019-09-18 06:33:38 +03:00
withTestDir :: (FilePath -> RIO e a) -> RIO e a
withTestDir = withTempDirectory "./" ".testlog."
data NotEqual = NotEqual String String
deriving (Eq, Ord, Show)
instance Exception NotEqual where
2019-09-18 06:33:38 +03:00
assertEqual :: MonadIO m => (Show a, Eq a) => a -> a -> m ()
assertEqual x y = do
2019-09-18 06:33:38 +03:00
unless (x == y) $ io $ throwIO $ NotEqual (show x) (show y)
-- Database Operations ---------------------------------------------------------
2019-08-21 02:32:46 +03:00
data Db = Db LogIdentity [ByteString] (Map Word64 ByteString)
deriving (Eq, Ord, Show)
addEvents :: Db -> [ByteString] -> Db
2019-08-21 02:32:46 +03:00
addEvents (Db id evs efs) new = Db id (evs <> new) efs
2020-05-30 01:57:35 +03:00
readDb :: EventLog -> RIO KingEnv Db
readDb log = do
2019-08-21 02:32:46 +03:00
events <- runConduit (streamEvents log 1 .| consume)
effects <- runConduit (streamEffectsRows log 0 .| consume)
pure $ Db (Log.identity log) events (mapFromList effects)
2020-05-30 01:57:35 +03:00
withDb :: FilePath -> Db -> (EventLog -> RIO KingEnv a) -> RIO KingEnv a
2019-08-21 02:32:46 +03:00
withDb dir (Db dId dEvs dFx) act = do
2019-09-18 06:33:38 +03:00
rwith (Log.new dir dId) $ \log -> do
Log.appendEvents log (fromList dEvs)
2019-08-21 02:32:46 +03:00
for_ (mapToList dFx) $ \(k,v) ->
Log.writeEffectsRow log k v
act log
--------------------------------------------------------------------------------
runApp :: RIO KingEnv a -> IO a
runApp = runKingEnvNoLog
2020-05-30 01:57:35 +03:00
tryReadIdentity :: Property
2019-09-18 06:33:38 +03:00
tryReadIdentity = forAll arbitrary (ioProperty . runApp . runTest)
where
2020-05-30 01:57:35 +03:00
runTest :: LogIdentity -> RIO KingEnv Bool
runTest ident = do
2019-09-18 06:33:38 +03:00
env <- ask
io $ runInBoundThread $ runRIO env $
withTestDir $ \dir -> do
2019-09-18 06:33:38 +03:00
rwith (Log.new dir ident) $ \log ->
assertEqual ident (Log.identity log)
2019-09-18 06:33:38 +03:00
rwith (Log.existing dir) $ \log ->
assertEqual ident (Log.identity log)
2019-09-18 06:33:38 +03:00
rwith (Log.existing dir) $ \log ->
assertEqual ident (Log.identity log)
pure True
tryReadDatabase :: Property
2019-09-18 06:33:38 +03:00
tryReadDatabase = forAll arbitrary (ioProperty . runApp . runTest)
2019-08-21 02:32:46 +03:00
where
2020-05-30 01:57:35 +03:00
runTest :: Db -> RIO KingEnv Bool
2019-08-21 02:32:46 +03:00
runTest db = do
2019-09-18 06:33:38 +03:00
env <- ask
io $ runInBoundThread $ runRIO env $
2019-08-21 02:32:46 +03:00
withTestDir $ \dir -> do
withDb dir db $ \log -> do
readDb log >>= assertEqual db
pure True
tryReadDatabaseFuzz :: Property
2019-09-18 06:33:38 +03:00
tryReadDatabaseFuzz = forAll arbitrary (ioProperty . runApp . runTest)
where
2020-05-30 01:57:35 +03:00
runTest :: Db -> RIO KingEnv Bool
runTest db = do
2019-09-18 06:33:38 +03:00
env <- ask
io $ runInBoundThread $ runRIO env $
withTestDir $ \dir -> do
withDb dir db $ \log -> do
readDb log >>= assertEqual db
2019-09-18 06:33:38 +03:00
rwith (Log.existing dir) $ \log -> do
readDb log >>= assertEqual db
2019-09-18 06:33:38 +03:00
rwith (Log.existing dir) $ \log -> do
readDb log >>= assertEqual db
readDb log >>= assertEqual db
pure True
tryAppend :: Property
2019-09-18 06:33:38 +03:00
tryAppend = forAll arbitrary (ioProperty . runApp . runTest)
where
2020-05-30 01:57:35 +03:00
runTest :: ([ByteString], Db) -> RIO KingEnv Bool
runTest (extra, db) = do
2019-09-18 06:33:38 +03:00
env <- ask
io $ runInBoundThread $ runRIO env $
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'
2019-09-18 06:33:38 +03:00
rwith (Log.existing dir) $ \log -> do
readDb log >>= assertEqual db'
pure True
tryAppendHuge :: Options.Brass -> Property
tryAppendHuge brass =
forAll arbitrary (ioProperty . runApp . runTest)
where
2020-05-30 01:57:35 +03:00
runTest :: ([ByteString], Db) -> RIO KingEnv Bool
runTest (extra, db) = do
2019-09-18 06:33:38 +03:00
env <- ask
io $ runInBoundThread $ runRIO env $ do
extra <- do
b <- readFile =<< Options.getPillPath brass
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'
2019-09-18 06:33:38 +03:00
rwith (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
2019-08-21 02:32:46 +03:00
, localOption (QuickCheckTests 15) $
testProperty "Read/Write Database" $
tryReadDatabase
2019-08-21 02:32:46 +03:00
, localOption (QuickCheckTests 5) $
testProperty "Read/Write Database Multiple Times" $
tryReadDatabaseFuzz
, localOption (QuickCheckTests 10) $
testProperty "Append Random Events" $
tryAppend
, localOption (QuickCheckTests 1) $
askOption $ \path ->
testProperty "Append Huge Events" $
tryAppendHuge path
]
-- 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
2019-08-21 02:32:46 +03:00
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
2019-08-21 02:32:46 +03:00
arbitrary = do
ident <- arbitrary
evs <- arbitrary
efs <- arbEffects evs
pure (Db ident evs efs)
instance Arbitrary LogIdentity where
arbitrary = LogIdentity <$> arb <*> arb <*> arb