This repository has been archived on 2024-10-24. You can view files and clone it, but cannot push or open issues or pull requests.
fradrive-old/test/Test/QuickCheck/Classes/JSON.hs
2022-10-12 09:35:16 +02:00

76 lines
3.1 KiB
Haskell

-- SPDX-FileCopyrightText: 2022 Gregor Kleen <gregor.kleen@ifi.lmu.de>
--
-- SPDX-License-Identifier: AGPL-3.0-or-later
module Test.QuickCheck.Classes.JSON
( jsonLaws
, jsonKeyLaws
) where
import ClassyPrelude
import Test.QuickCheck
import Test.QuickCheck.Property (failed, Property(..))
import Test.QuickCheck.Classes hiding (jsonLaws)
import Data.Aeson
import Data.Aeson.Encoding.Internal
import Data.Aeson.Types (parseEither)
import Data.Proxy
import Data.Coerce
jsonKeyLaws :: forall a. (Arbitrary a, FromJSONKey a, ToJSONKey a, Eq a, Show a, FromJSON a, ToJSON a) => Proxy a -> Laws
jsonKeyLaws _ = Laws "ToJSONKey/FromJSONKey"
[ ("Partial Isomorphism", property $ \(a :: a) -> partialIsomorphism a)
, ("Partial Isomorphism (List)", property $ \(as :: [a]) -> partialIsomorphism as)
, ("Encoding Equals Value", property $ \(a :: a)
-> let (toVal, toEnc) = case toJSONKey of
ToJSONKeyText toVal' toEnc' -> (String . toVal', retagEncoding . toEnc')
ToJSONKeyValue toVal' toEnc' -> (toVal', toEnc')
in eitherDecode (encodingToLazyByteString $ toEnc a) == Right (toVal a)
)
]
where
partialIsomorphism :: forall a'. (FromJSONKey a', ToJSONKey a', Eq a') => a' -> Property
partialIsomorphism a = case (toJSONKey, fromJSONKey) of
(ToJSONKeyText toVal _, FromJSONKeyCoerce)
-> property $ coerce (toVal a) == a
(ToJSONKeyText toVal _, FromJSONKeyText fromVal)
-> property $ fromVal (toVal a) == a
(ToJSONKeyText toVal _, FromJSONKeyTextParser parser)
-> property $ parseEither parser (toVal a) == Right a
(ToJSONKeyValue toVal _, FromJSONKeyValue parser)
-> property $ parseEither parser (toVal a) == Right a
(_, _)
-> property failed
jsonLaws :: (ToJSON a, FromJSON a, Show a, Arbitrary a, Eq a) => Proxy a -> Laws
jsonLaws p = Laws "ToJSON/FromJSON"
[ ("Partial Isomorphism", jsonEncodingPartialIsomorphism p)
, ("Encoding Equals Value", jsonEncodingEqualsValue p)
]
-- TODO: improve the quality of the error message if
-- something does not pass this test.
jsonEncodingEqualsValue :: forall a. (ToJSON a, Show a, Arbitrary a) => Proxy a -> Property
jsonEncodingEqualsValue _ = property $ \(a :: a) ->
case decode (encode a) of
Nothing -> False
Just (v :: Value) -> v == toJSON a
jsonEncodingPartialIsomorphism :: forall a. (ToJSON a, FromJSON a, Show a, Eq a, Arbitrary a) => Proxy a -> Property
jsonEncodingPartialIsomorphism _ = again $
MkProperty $
arbitrary >>= \(x :: a) ->
unProperty $
shrinking shrink x $ \x' ->
let desc1 = "Right"
desc2 = "Data.Aeson.eitherDecode . Data.Aeson.encode"
name1 = "Data.Aeson.encode a"
name2 = "Data.Aeson.eitherDecode (Data.Aeson.encode a)"
b1 = encode x'
b2 = eitherDecode b1
sb1 = show b1
sb2 = show b2
description = " Description: " ++ desc1 ++ " == " ++ desc2
err = description ++ "\n" ++ unlines (map (" " ++) (["a = " ++ show x'])) ++ " " ++ name1 ++ " = " ++ sb1 ++ "\n " ++ name2 ++ " = " ++ sb2
in counterexample err (Right x' == b2)