diff --git a/cardano-cli/cardano-cli.cabal b/cardano-cli/cardano-cli.cabal index 71f6aa0dfa..8e496db145 100644 --- a/cardano-cli/cardano-cli.cabal +++ b/cardano-cli/cardano-cli.cabal @@ -323,6 +323,7 @@ test-suite cardano-cli-test filepath, hedgehog, hedgehog-extras ^>=0.6.1.0, + parsec, regex-tdfa, tasty, tasty-hedgehog, @@ -340,6 +341,7 @@ test-suite cardano-cli-test Test.Cli.ITN Test.Cli.Json Test.Cli.MonadWarning + Test.Cli.Parser Test.Cli.Pioneers.Exercise1 Test.Cli.Pioneers.Exercise2 Test.Cli.Pioneers.Exercise3 diff --git a/cardano-cli/test/cardano-cli-test/Test/Cli/Parser.hs b/cardano-cli/test/cardano-cli-test/Test/Cli/Parser.hs new file mode 100644 index 0000000000..d95769aaae --- /dev/null +++ b/cardano-cli/test/cardano-cli-test/Test/Cli/Parser.hs @@ -0,0 +1,99 @@ +{-# LANGUAGE RankNTypes #-} +{-# LANGUAGE ScopedTypeVariables #-} +{-# LANGUAGE TypeApplications #-} + +module Test.Cli.Parser + ( hprop_integral_reader + , hprop_integral_pair_reader_positive + , hprop_integral_pair_reader_negative + ) +where + +import Cardano.CLI.EraBased.Options.Common (integralParsecParser, + pairIntegralParsecParser) + +import Data.Bits (Bits) +import Data.Data (Proxy (..), Typeable) +import Data.Either (isLeft, isRight) +import Data.Word (Word16) +import qualified Text.Parsec as Parsec + +import Hedgehog (Gen, Property, assert, property, (===)) +import Hedgehog.Extras (assertWith, propertyOnce) +import qualified Hedgehog.Gen as Gen +import Hedgehog.Internal.Property (forAll) +import qualified Hedgehog.Range as Gen +import qualified Hedgehog.Range as Range + +-- | Execute me with: +-- @cabal test cardano-cli-test --test-options '-p "/integral reader/"'@ +hprop_integral_reader :: Property +hprop_integral_reader = property $ do + parse @Word "0" === Right 0 + parse @Word "42" === Right 42 + assertWith (parse @Word "-1") isLeft + assertWith (parse @Word "18446744073709551616") isLeft + assertWith (parse @Word "-1987090") isLeft + + w <- forAll $ Gen.word $ Gen.linear minBound maxBound + parse @Word (show w) === Right w + + parse @Word16 "0" === Right 0 + parse @Word16 "42" === Right 42 + assertWith (parse @Word16 "-1") isLeft + assertWith (parse @Word16 "65536") isLeft + assertWith (parse @Word16 "298709870987") isLeft + assertWith (parse @Word16 "-1987090") isLeft + where + parse :: (Typeable a, Integral a, Bits a) => String -> Either String a + parse s = + case Parsec.runParser integralParsecParser () "" s of + Left parsecError -> Left $ show parsecError + Right x -> Right x + +-- | Execute me with: +-- @cabal test cardano-cli-test --test-options '-p "/integral pair reader positive/"'@ +hprop_integral_pair_reader_positive :: Property +hprop_integral_pair_reader_positive = property $ do + validArbitraryTuple <- forAll $ genNumberTuple (Proxy :: Proxy Word) + assert $ isRight $ parse @Word validArbitraryTuple + where + parse :: (Typeable a, Integral a, Bits a) => String -> Either String (a, a) + parse s = + case Parsec.runParser pairIntegralParsecParser () "" s of + Left parsecError -> Left $ show parsecError + Right x -> Right x + +genNumberTuple :: forall a. Integral a => Show a => Proxy a -> Gen String +genNumberTuple _ = do + x :: a <- Gen.integral (Range.linear 0 100) + y :: a <- Gen.integral (Range.linear 0 100) + space0 <- genArbitrarySpace + space1 <- genArbitrarySpace + space2 <- genArbitrarySpace + space3 <- genArbitrarySpace + return $ + space0 ++ "(" ++ space2 ++ show x ++ space1 ++ "," ++ space2 ++ show y ++ space1 ++ ")" ++ space3 + +genArbitrarySpace :: Gen String +genArbitrarySpace = Gen.string (Range.linear 0 5) (return ' ') + +-- | Execute me with: +-- @cabal test cardano-cli-test --test-options '-p "/integral pair reader negative/"'@ +hprop_integral_pair_reader_negative :: Property +hprop_integral_pair_reader_negative = propertyOnce $ do + assertWith (parse @Word "(0, 0, 0)") isLeft + assertWith (parse @Word "(-1, 0)") isLeft + assertWith (parse @Word "(18446744073709551616, 0)") isLeft + assertWith (parse @Word "(0, 18446744073709551616)") isLeft + assertWith (parse @Word "(0, -1)") isLeft + assertWith (parse @Word "0, 0)") isLeft + assertWith (parse @Word "(0, 0") isLeft + assertWith (parse @Word "(0 0)") isLeft + assertWith (parse @Word "( 0, 0") isLeft + where + parse :: (Typeable a, Integral a, Bits a) => String -> Either String (a, a) + parse s = + case Parsec.runParser pairIntegralParsecParser () "" s of + Left parsecError -> Left $ show parsecError + Right x -> Right x