mirror of
https://github.com/urbit/shrub.git
synced 2024-11-29 14:57:12 +03:00
184 lines
5.7 KiB
Haskell
184 lines
5.7 KiB
Haskell
{-# OPTIONS_GHC -Wno-deprecations #-}
|
|
|
|
|
|
module Main (main) where
|
|
|
|
--------------------------------------------------------------------------------
|
|
|
|
import Data.IORef
|
|
import Numeric.Natural
|
|
import Prelude
|
|
import System.Exit
|
|
import System.IO.Unsafe
|
|
import Test.QuickCheck
|
|
|
|
import Control.Monad (when)
|
|
import Data.ByteString (ByteString)
|
|
import Data.Vector.Primitive (Prim, Vector)
|
|
|
|
import qualified Data.ByteString as BS
|
|
import qualified Data.ByteString.Unsafe as BS
|
|
import qualified Data.Vector.Primitive as VP
|
|
import qualified Urbit.Atom.Fast as F
|
|
import qualified Urbit.Atom.Slow as S
|
|
|
|
|
|
-- Instances -------------------------------------------------------------------
|
|
|
|
instance Arbitrary Natural where
|
|
arbitrary = fromInteger . abs <$> arbitrary
|
|
|
|
instance Arbitrary ByteString where
|
|
arbitrary = BS.pack <$> arbitrary
|
|
|
|
instance (Prim a, Arbitrary a) => Arbitrary (Vector a) where
|
|
arbitrary = VP.fromList <$> arbitrary
|
|
|
|
|
|
-- Utils -----------------------------------------------------------------------
|
|
|
|
stripBytes :: ByteString -> ByteString
|
|
stripBytes buf = BS.take (len - go 0 (len - 1)) buf
|
|
where
|
|
len = BS.length buf
|
|
go n i | i < 0 = n
|
|
| 0 == BS.unsafeIndex buf i = go (n + 1) (i - 1)
|
|
| otherwise = n
|
|
|
|
stripWords :: Vector Word -> Vector Word
|
|
stripWords vec = VP.take (len - go 0 (len - 1)) vec
|
|
where
|
|
len = VP.length vec
|
|
go n i | i < 0 = n
|
|
| 0 == VP.unsafeIndex vec i = go (n + 1) (i - 1)
|
|
| otherwise = n
|
|
|
|
dumpLoad :: Eq i => (i -> o) -> (o -> i) -> (i -> Bool)
|
|
dumpLoad dump load x = x == load (dump x)
|
|
|
|
loadDump :: Eq o => (o -> i) -> (i -> o) -> (o -> o) -> (o -> Bool)
|
|
loadDump load dump norm x = norm x == dump (load x)
|
|
|
|
|
|
-- Test Reference Implementation -----------------------------------------------
|
|
|
|
prop_atom_bytes_roundtrip :: Natural -> Bool
|
|
prop_atom_bytes_roundtrip = dumpLoad S.atomBytes S.bytesAtom
|
|
|
|
prop_atom_words_roundtrip :: Natural -> Bool
|
|
prop_atom_words_roundtrip = dumpLoad S.atomWords S.wordsAtom
|
|
|
|
prop_bytes_atom_roundtrip :: ByteString -> Bool
|
|
prop_bytes_atom_roundtrip = loadDump S.bytesAtom S.atomBytes stripBytes
|
|
|
|
prop_words_atom_roundtrip :: Vector Word -> Bool
|
|
prop_words_atom_roundtrip = loadDump S.wordsAtom S.atomWords stripWords
|
|
|
|
|
|
-- Test Fast Implementation ----------------------------------------------------
|
|
|
|
prop_fast_atom_bytes_roundtrip :: Natural -> Bool
|
|
prop_fast_atom_bytes_roundtrip = dumpLoad F.atomBytes F.bytesAtom
|
|
|
|
prop_fast_atom_words_roundtrip :: Natural -> Bool
|
|
prop_fast_atom_words_roundtrip = dumpLoad F.atomWords F.wordsAtom
|
|
|
|
prop_fast_bytes_atom_roundtrip :: ByteString -> Bool
|
|
prop_fast_bytes_atom_roundtrip = loadDump F.bytesAtom F.atomBytes stripBytes
|
|
|
|
prop_fast_words_atom_roundtrip :: Vector Word -> Bool
|
|
prop_fast_words_atom_roundtrip = loadDump F.wordsAtom F.atomWords stripWords
|
|
|
|
|
|
-- Fast and Reference Implementations are the Same -----------------------------
|
|
|
|
prop_fast_words_atom_correct :: Vector Word -> Bool
|
|
prop_fast_words_atom_correct x = F.wordsAtom x == S.wordsAtom x
|
|
|
|
prop_fast_atom_words_correct :: Natural -> Bool
|
|
prop_fast_atom_words_correct x = F.atomWords x == S.atomWords x
|
|
|
|
prop_fast_bytes_atom_correct :: ByteString -> Bool
|
|
prop_fast_bytes_atom_correct x = F.bytesAtom x == S.bytesAtom x
|
|
|
|
prop_fast_atom_import_correct :: ByteString -> Bool
|
|
prop_fast_atom_import_correct x = F.importBytes x == S.bytesAtom x
|
|
|
|
prop_fast_atom_bytes_correct :: Natural -> Bool
|
|
prop_fast_atom_bytes_correct x = F.atomBytes x == S.atomBytes x
|
|
|
|
prop_fast_atom_export_correct :: Natural -> Bool
|
|
prop_fast_atom_export_correct x = F.exportBytes x == S.atomBytes x
|
|
|
|
|
|
--------------------------------------------------------------------------------
|
|
|
|
failed :: IORef Int
|
|
failed = unsafePerformIO (newIORef 0)
|
|
|
|
checkProp :: Testable prop => String -> prop -> IO ()
|
|
checkProp nm chk = do
|
|
putStrLn nm
|
|
res <- quickCheckResult chk
|
|
putStrLn ""
|
|
|
|
case res of
|
|
Success{} -> pure ()
|
|
_ -> modifyIORef' failed succ
|
|
|
|
main :: IO ()
|
|
main = do
|
|
checkProp "Reference: Atom <-> ByteString roundtrip"
|
|
prop_atom_bytes_roundtrip
|
|
|
|
checkProp "Reference: Atom <-> Vector Word roundtrip"
|
|
prop_atom_words_roundtrip
|
|
|
|
checkProp "Reference: ByteString <-> Atom roundtrip"
|
|
prop_bytes_atom_roundtrip
|
|
|
|
checkProp "Reference: Vector Word <-> Atom roundtrip"
|
|
prop_words_atom_roundtrip
|
|
|
|
checkProp "Fast: Atom <-> ByteString roundtrip"
|
|
prop_fast_atom_bytes_roundtrip
|
|
|
|
checkProp "Fast: Atom <-> Vector Word roundtrip"
|
|
prop_fast_atom_words_roundtrip
|
|
|
|
checkProp "Fast: Bytestring <-> Atom roundtrip"
|
|
prop_fast_bytes_atom_roundtrip
|
|
|
|
checkProp "Fast: Export->Import roundtrip" $ do
|
|
withMaxSuccess 100000 (dumpLoad F.exportBytes F.importBytes)
|
|
|
|
checkProp "Fast: Import->Export roundtrip" $ do
|
|
withMaxSuccess 10000 (loadDump F.importBytes F.exportBytes stripBytes)
|
|
|
|
checkProp "Fast: Vector Word <-> Atom roundtrip"
|
|
prop_fast_words_atom_roundtrip
|
|
|
|
checkProp "Fast matches reference: Vector Words -> Atom"
|
|
(withMaxSuccess 10000 prop_fast_words_atom_correct)
|
|
|
|
checkProp "Fast matches reference: Atom -> Vector Word"
|
|
(withMaxSuccess 10000 prop_fast_atom_words_correct)
|
|
|
|
checkProp "Fast matches reference: ByteString -> Atom"
|
|
(withMaxSuccess 10000 prop_fast_bytes_atom_correct)
|
|
|
|
checkProp "Fast matches reference: Atom -> ByteString"
|
|
(withMaxSuccess 10000 prop_fast_atom_bytes_correct)
|
|
|
|
checkProp "Fast matches reference: Atom Import"
|
|
(withMaxSuccess 10000 prop_fast_atom_import_correct)
|
|
|
|
checkProp "Fast matches reference: Atom Export"
|
|
(withMaxSuccess 10000 prop_fast_atom_export_correct)
|
|
|
|
res <- readIORef failed
|
|
when (res /= 0) $ do
|
|
putStrLn $ "FAILURE: " <> show res <> " tests failed."
|
|
exitWith (ExitFailure 1)
|
|
putStrLn $ "SUCCESS: All tests passed"
|