packages feed

blaze-json-0.1.0: tests/tasty.hs

{-# LANGUAGE CPP #-}

import Control.Monad

import Test.QuickCheck
import Test.Tasty
import Test.Tasty.QuickCheck

import qualified Data.Aeson as A
import Data.Scientific
import qualified Data.Text as T
import qualified Data.Vector as V
import qualified Data.HashMap.Strict as H

import qualified Text.Blaze.JSON as B

config :: B.EncodeConfig
#if MIN_VERSION_aeson(0,8,0)
config = B.def
#else
config = B.def { B.escapeHtml = True }
#endif


fromAeson :: A.Value -> B.JSON
fromAeson (A.Object o) = B.object . map (fmap fromAeson) $ H.toList o
fromAeson (A.Array  a) = B.array $ fmap fromAeson a
fromAeson (A.String t) = B.text t
fromAeson (A.Number n) = either B.double B.integral (floatingOrInteger n :: Either Double Int)
fromAeson (A.Bool   b) = B.bool b
fromAeson A.Null       = B.null

text :: Gen T.Text
text = sized $ \n -> do
    k <- choose (0, n)
    s <- sequence
        [ oneof
            [ oneof $ map return "\\\""
            , oneof $ map return ['\0' .. '\x20']
            , toEnum `fmap` choose (0, 0xffff)
            ]
        | _ <- [1 .. k]
        ]
    return $ T.pack s

scolar :: Gen A.Value
scolar = oneof
     [ return A.Null
     , A.Bool `fmap` arbitrary
     , (A.Number . normalize . fromIntegral) `fmap` (arbitrary :: Gen Int)
     , (A.Number . normalize . fromFloatDigits) `fmap` (arbitrary :: Gen Double)
     , fmap A.String text
     ]

arbit :: Int -> Gen A.Value
arbit 0 = scolar
arbit d = oneof
    [ scolar
    , sized $ \l ->
        fmap (A.Array . V.fromList) $ vectorOf l (arbit $ d-1)
    , sized $ \l ->
        fmap (A.Object . H.fromList) $ vectorOf l ((,) `fmap` text `ap` arbit (d-1))
    ]

newtype Aeson = Aeson A.Value deriving Show

instance Arbitrary Aeson where
    arbitrary = Aeson `fmap` arbit 2

main :: IO ()
main = defaultMain $ testGroup ""
    [ testProperty "same to aeson" $ \(Aeson value) ->
        A.encode value === B.encodeWith config (fromAeson value)
    ]