{-# LANGUAGE AllowAmbiguousTypes #-}
{-# LANGUAGE ConstraintKinds #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE RankNTypes #-}
{-# LANGUAGE RecordWildCards #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE TypeApplications #-}
{-# OPTIONS_GHC -Wno-orphans #-}
module Test.Cardano.Ledger.Binary.Vintage.Helpers (
byronProtVer,
U,
U24,
extensionProperty,
cborFlatTermValid,
SizeTestConfig (..),
cfg,
scfg,
sizeTest,
)
where
import Cardano.Ledger.Binary (
DecCBOR (..),
EncCBOR (..),
Range (..),
Size,
SizeOverride (..),
byronProtVer,
decodeListLenOf,
decodeNestedCborBytes,
encodeListLen,
encodeNestedCborBytes,
serialize,
szSimplify,
szWithCtx,
unsafeDeserialize,
)
import Cardano.Ledger.Binary.FlatTerm (toFlatTerm, validFlatTerm)
import qualified Data.ByteString as BS
import qualified Data.ByteString.Lazy as LBS
import qualified Data.Map as M
import Data.Text.Lazy (unpack)
import Data.Text.Lazy.Builder (toLazyText)
import Data.Typeable (TypeRep)
import Data.Word (Word8)
import Formatting (Buildable, bprint, build)
import Hedgehog (annotate, failure, forAllWith, success)
import qualified Hedgehog as HH
import qualified Hedgehog.Gen as HH.Gen
import Numeric.Natural (Natural)
import Test.Cardano.Ledger.Binary.Arbitrary ()
import Test.Hspec ()
import Test.Hspec.QuickCheck ()
import Test.QuickCheck (
Arbitrary (arbitrary),
Gen,
Property,
choose,
forAll,
property,
(===),
)
cborFlatTermValid :: EncCBOR a => a -> Property
cborFlatTermValid :: forall a. EncCBOR a => a -> Property
cborFlatTermValid = forall prop. Testable prop => prop -> Property
property forall b c a. (b -> c) -> (a -> b) -> a -> c
. FlatTerm -> Bool
validFlatTerm forall b c a. (b -> c) -> (a -> b) -> a -> c
. Version -> Encoding -> FlatTerm
toFlatTerm Version
byronProtVer forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall a. EncCBOR a => a -> Encoding
encCBOR
data U = U Word8 BS.ByteString deriving (Int -> U -> ShowS
[U] -> ShowS
U -> String
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
showList :: [U] -> ShowS
$cshowList :: [U] -> ShowS
show :: U -> String
$cshow :: U -> String
showsPrec :: Int -> U -> ShowS
$cshowsPrec :: Int -> U -> ShowS
Show, U -> U -> Bool
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
/= :: U -> U -> Bool
$c/= :: U -> U -> Bool
== :: U -> U -> Bool
$c== :: U -> U -> Bool
Eq)
instance EncCBOR U where
encCBOR :: U -> Encoding
encCBOR (U Word8
word8 ByteString
bs) =
Word -> Encoding
encodeListLen Word
2
forall a. Semigroup a => a -> a -> a
<> forall a. EncCBOR a => a -> Encoding
encCBOR (Word8
word8 :: Word8)
forall a. Semigroup a => a -> a -> a
<> ByteString -> Encoding
encodeNestedCborBytes
(ByteString -> ByteString
LBS.fromStrict ByteString
bs)
instance DecCBOR U where
decCBOR :: forall s. Decoder s U
decCBOR = do
forall s. Int -> Decoder s ()
decodeListLenOf Int
2
Word8 -> ByteString -> U
U forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> forall a s. DecCBOR a => Decoder s a
decCBOR forall (f :: * -> *) a b. Applicative f => f (a -> b) -> f a -> f b
<*> forall s. Decoder s ByteString
decodeNestedCborBytes
instance Arbitrary U where
arbitrary :: Gen U
arbitrary = Word8 -> ByteString -> U
U forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> forall a. Random a => (a, a) -> Gen a
choose (Word8
0, Word8
255) forall (f :: * -> *) a b. Applicative f => f (a -> b) -> f a -> f b
<*> forall a. Arbitrary a => Gen a
arbitrary
data U24 = U24 Word8 BS.ByteString deriving (Int -> U24 -> ShowS
[U24] -> ShowS
U24 -> String
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
showList :: [U24] -> ShowS
$cshowList :: [U24] -> ShowS
show :: U24 -> String
$cshow :: U24 -> String
showsPrec :: Int -> U24 -> ShowS
$cshowsPrec :: Int -> U24 -> ShowS
Show, U24 -> U24 -> Bool
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
/= :: U24 -> U24 -> Bool
$c/= :: U24 -> U24 -> Bool
== :: U24 -> U24 -> Bool
$c== :: U24 -> U24 -> Bool
Eq)
instance DecCBOR U24 where
decCBOR :: forall s. Decoder s U24
decCBOR = do
forall s. Int -> Decoder s ()
decodeListLenOf Int
2
Word8 -> ByteString -> U24
U24 forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> forall a s. DecCBOR a => Decoder s a
decCBOR forall (f :: * -> *) a b. Applicative f => f (a -> b) -> f a -> f b
<*> forall s. Decoder s ByteString
decodeNestedCborBytes
instance EncCBOR U24 where
encCBOR :: U24 -> Encoding
encCBOR (U24 Word8
word8 ByteString
bs) =
Word -> Encoding
encodeListLen Word
2
forall a. Semigroup a => a -> a -> a
<> forall a. EncCBOR a => a -> Encoding
encCBOR (Word8
word8 :: Word8)
forall a. Semigroup a => a -> a -> a
<> ByteString -> Encoding
encodeNestedCborBytes
(ByteString -> ByteString
LBS.fromStrict ByteString
bs)
extensionProperty ::
forall a. (Arbitrary a, Eq a, Show a, DecCBOR a, EncCBOR a) => Property
extensionProperty :: forall a.
(Arbitrary a, Eq a, Show a, DecCBOR a, EncCBOR a) =>
Property
extensionProperty = forall a prop.
(Show a, Testable prop) =>
Gen a -> (a -> prop) -> Property
forAll @a (forall a. Arbitrary a => Gen a
arbitrary :: Gen a) forall a b. (a -> b) -> a -> b
$ \a
input ->
let serialized :: ByteString
serialized = forall a. EncCBOR a => Version -> a -> ByteString
serialize Version
byronProtVer a
input
(U
u :: U) = forall a. DecCBOR a => Version -> ByteString -> a
unsafeDeserialize Version
byronProtVer ByteString
serialized
(a
encoded :: a) = forall a. DecCBOR a => Version -> ByteString -> a
unsafeDeserialize Version
byronProtVer (forall a. EncCBOR a => Version -> a -> ByteString
serialize Version
byronProtVer U
u)
in a
encoded forall a. (Eq a, Show a) => a -> a -> Property
=== a
input
bshow :: Buildable a => a -> String
bshow :: forall a. Buildable a => a -> String
bshow = Text -> String
unpack forall b c a. (b -> c) -> (a -> b) -> a -> c
. Builder -> Text
toLazyText forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall a. Format Builder a -> a
bprint forall a r. Buildable a => Format r (a -> r)
build
data SizeTestConfig a = SizeTestConfig
{ forall a. SizeTestConfig a -> a -> String
debug :: a -> String
, forall a. SizeTestConfig a -> Gen a
gen :: HH.Gen a
, forall a. SizeTestConfig a -> Bool
precise :: Bool
, forall a. SizeTestConfig a -> Map TypeRep SizeOverride
addlCtx :: M.Map TypeRep SizeOverride
, forall a. SizeTestConfig a -> a -> Map TypeRep SizeOverride
computedCtx :: a -> M.Map TypeRep SizeOverride
}
cfg :: Buildable a => SizeTestConfig a
cfg :: forall a. Buildable a => SizeTestConfig a
cfg =
SizeTestConfig
{ debug :: a -> String
debug = forall a. Buildable a => a -> String
bshow
, gen :: Gen a
gen = forall (m :: * -> *) a. MonadGen m => m a
HH.Gen.discard
, precise :: Bool
precise = Bool
False
, addlCtx :: Map TypeRep SizeOverride
addlCtx = forall k a. Map k a
M.empty
, computedCtx :: a -> Map TypeRep SizeOverride
computedCtx = forall a b. a -> b -> a
const forall k a. Map k a
M.empty
}
scfg :: Show a => SizeTestConfig a
scfg :: forall a. Show a => SizeTestConfig a
scfg =
SizeTestConfig
{ debug :: a -> String
debug = forall a. Show a => a -> String
show
, gen :: Gen a
gen = forall (m :: * -> *) a. MonadGen m => m a
HH.Gen.discard
, precise :: Bool
precise = Bool
False
, addlCtx :: Map TypeRep SizeOverride
addlCtx = forall k a. Map k a
M.empty
, computedCtx :: a -> Map TypeRep SizeOverride
computedCtx = forall a b. a -> b -> a
const forall k a. Map k a
M.empty
}
sizeTest :: forall a. EncCBOR a => SizeTestConfig a -> HH.Property
sizeTest :: forall a. EncCBOR a => SizeTestConfig a -> Property
sizeTest SizeTestConfig {Bool
Map TypeRep SizeOverride
Gen a
a -> String
a -> Map TypeRep SizeOverride
computedCtx :: a -> Map TypeRep SizeOverride
addlCtx :: Map TypeRep SizeOverride
precise :: Bool
gen :: Gen a
debug :: a -> String
computedCtx :: forall a. SizeTestConfig a -> a -> Map TypeRep SizeOverride
addlCtx :: forall a. SizeTestConfig a -> Map TypeRep SizeOverride
precise :: forall a. SizeTestConfig a -> Bool
gen :: forall a. SizeTestConfig a -> Gen a
debug :: forall a. SizeTestConfig a -> a -> String
..} = HasCallStack => PropertyT IO () -> Property
HH.property forall a b. (a -> b) -> a -> b
$ do
a
x <- forall (m :: * -> *) a.
(Monad m, HasCallStack) =>
(a -> String) -> Gen a -> PropertyT m a
forAllWith a -> String
debug Gen a
gen
let ctx :: Map TypeRep SizeOverride
ctx = forall k a. Ord k => Map k a -> Map k a -> Map k a
M.union (a -> Map TypeRep SizeOverride
computedCtx a
x) Map TypeRep SizeOverride
addlCtx
badBounds :: Natural -> Range Natural -> HH.PropertyT IO ()
badBounds :: Natural -> Range Natural -> PropertyT IO ()
badBounds Natural
sz Range Natural
bounds = do
forall (m :: * -> *). (MonadTest m, HasCallStack) => String -> m ()
annotate (String
"Computed bounds: " forall a. Semigroup a => a -> a -> a
<> forall a. Buildable a => a -> String
bshow Range Natural
bounds)
forall (m :: * -> *). (MonadTest m, HasCallStack) => String -> m ()
annotate (String
"Actual size: " forall a. Semigroup a => a -> a -> a
<> forall a. Show a => a -> String
show Natural
sz)
forall (m :: * -> *). (MonadTest m, HasCallStack) => String -> m ()
annotate (String
"Value: " forall a. Semigroup a => a -> a -> a
<> a -> String
debug a
x)
case forall a.
EncCBOR a =>
Map TypeRep SizeOverride -> a -> ComparisonResult
szVerify Map TypeRep SizeOverride
ctx a
x of
ComparisonResult
Exact -> forall (m :: * -> *). MonadTest m => m ()
success
WithinBounds Natural
_ Range Natural
_ | Bool -> Bool
not Bool
precise -> forall (m :: * -> *). MonadTest m => m ()
success
WithinBounds Natural
sz Range Natural
bounds -> do
Natural -> Range Natural -> PropertyT IO ()
badBounds Natural
sz Range Natural
bounds
forall (m :: * -> *). (MonadTest m, HasCallStack) => String -> m ()
annotate String
"Bounds were not exact."
forall (m :: * -> *) a. (MonadTest m, HasCallStack) => m a
failure
BoundsAreSymbolic Size
bounds -> do
forall (m :: * -> *). (MonadTest m, HasCallStack) => String -> m ()
annotate (String
"Bounds are symbolic: " forall a. Semigroup a => a -> a -> a
<> forall a. Buildable a => a -> String
bshow Size
bounds)
forall (m :: * -> *) a. (MonadTest m, HasCallStack) => m a
failure
OutOfBounds Natural
sz Range Natural
bounds -> do
Natural -> Range Natural -> PropertyT IO ()
badBounds Natural
sz Range Natural
bounds
forall (m :: * -> *). (MonadTest m, HasCallStack) => String -> m ()
annotate String
"Size fell outside of bounds."
forall (m :: * -> *) a. (MonadTest m, HasCallStack) => m a
failure
data ComparisonResult
=
Exact
|
WithinBounds Natural (Range Natural)
|
BoundsAreSymbolic Size
|
OutOfBounds Natural (Range Natural)
szVerify :: EncCBOR a => M.Map TypeRep SizeOverride -> a -> ComparisonResult
szVerify :: forall a.
EncCBOR a =>
Map TypeRep SizeOverride -> a -> ComparisonResult
szVerify Map TypeRep SizeOverride
ctx a
x = case Size -> Either Size (Range Natural)
szSimplify (forall a. EncCBOR a => Map TypeRep SizeOverride -> Proxy a -> Size
szWithCtx Map TypeRep SizeOverride
ctx (forall (f :: * -> *) a. Applicative f => a -> f a
pure a
x)) of
Left Size
bounds -> Size -> ComparisonResult
BoundsAreSymbolic Size
bounds
Right Range Natural
range
| forall b. Range b -> b
lo Range Natural
range forall a. Ord a => a -> a -> Bool
<= Natural
sz Bool -> Bool -> Bool
&& Natural
sz forall a. Ord a => a -> a -> Bool
<= forall b. Range b -> b
hi Range Natural
range ->
if forall b. Range b -> b
lo Range Natural
range forall a. Eq a => a -> a -> Bool
== forall b. Range b -> b
hi Range Natural
range then ComparisonResult
Exact else Natural -> Range Natural -> ComparisonResult
WithinBounds Natural
sz Range Natural
range
Right Range Natural
range -> Natural -> Range Natural -> ComparisonResult
OutOfBounds Natural
sz Range Natural
range
where
sz :: Natural
sz :: Natural
sz = forall a b. (Integral a, Num b) => a -> b
fromIntegral forall a b. (a -> b) -> a -> b
$ ByteString -> Int64
LBS.length forall a b. (a -> b) -> a -> b
$ forall a. EncCBOR a => Version -> a -> ByteString
serialize Version
byronProtVer a
x