dhodovsk / source-git / ghc-aeson

Forked from source-git/ghc-aeson 4 years ago
Clone

Blame tests/SerializationFormatSpec.hs

Packit 9a2dfb
{-# LANGUAGE CPP #-}
Packit 9a2dfb
{-# LANGUAGE DeriveGeneric #-}
Packit 9a2dfb
{-# LANGUAGE GADTs #-}
Packit 9a2dfb
{-# LANGUAGE OverloadedStrings #-}
Packit 9a2dfb
#if __GLASGOW_HASKELL__ >= 708
Packit 9a2dfb
{-# LANGUAGE DataKinds #-}
Packit 9a2dfb
#endif
Packit 9a2dfb
Packit 9a2dfb
------------------------------------------------------------------------------
Packit 9a2dfb
-- These tests assert that the JSON serialization doesn't change by accident.
Packit 9a2dfb
-----------------------------------------------------------------------------
Packit 9a2dfb
Packit 9a2dfb
module SerializationFormatSpec
Packit 9a2dfb
  (
Packit 9a2dfb
    tests
Packit 9a2dfb
  ) where
Packit 9a2dfb
Packit 9a2dfb
import Prelude ()
Packit 9a2dfb
import Prelude.Compat
Packit 9a2dfb
Packit 9a2dfb
import Control.Applicative (Const(..))
Packit 9a2dfb
import Data.Aeson (FromJSON(..), decode, encode, genericParseJSON, genericToEncoding, genericToJSON)
Packit 9a2dfb
import Data.Aeson.Types (Options(..), SumEncoding(..), ToJSON(..), defaultOptions)
Packit 9a2dfb
import Data.Fixed (Pico)
Packit 9a2dfb
import Data.Foldable (for_, toList)
Packit 9a2dfb
import Data.Functor.Compose (Compose(..))
Packit 9a2dfb
import Data.Functor.Identity (Identity(..))
Packit 9a2dfb
import Data.Functor.Product (Product(..))
Packit 9a2dfb
import Data.Functor.Sum (Sum(..))
Packit 9a2dfb
import Data.List.NonEmpty (NonEmpty(..))
Packit 9a2dfb
import Data.Proxy (Proxy(..))
Packit 9a2dfb
import Data.Scientific (Scientific)
Packit 9a2dfb
import Data.Tagged (Tagged(..))
Packit 9a2dfb
import Data.Time (fromGregorian)
Packit 9a2dfb
import Data.Word (Word8)
Packit 9a2dfb
import GHC.Generics (Generic)
Packit 9a2dfb
import Instances ()
Packit 9a2dfb
import Test.Framework (Test, testGroup)
Packit 9a2dfb
import Test.Framework.Providers.HUnit (testCase)
Packit 9a2dfb
import Test.HUnit (assertFailure, assertEqual)
Packit 9a2dfb
import Types (Approx(..), Compose3, Compose3', I)
Packit 9a2dfb
import qualified Data.ByteString.Lazy.Char8 as L
Packit 9a2dfb
import qualified Data.DList as DList
Packit 9a2dfb
import qualified Data.HashMap.Strict as HM
Packit 9a2dfb
import qualified Data.HashSet as HashSet
Packit 9a2dfb
import qualified Data.IntMap as IntMap
Packit 9a2dfb
import qualified Data.IntSet as IntSet
Packit 9a2dfb
import qualified Data.Map as M
Packit 9a2dfb
import qualified Data.Monoid as Monoid
Packit 9a2dfb
import qualified Data.Semigroup as Semigroup
Packit 9a2dfb
import qualified Data.Sequence as Seq
Packit 9a2dfb
import qualified Data.Set as Set
Packit 9a2dfb
import qualified Data.Tree as Tree
Packit 9a2dfb
import qualified Data.UUID.Types as UUID
Packit 9a2dfb
import qualified Data.Vector as Vector
Packit 9a2dfb
Packit 9a2dfb
tests :: [Test]
Packit 9a2dfb
tests =
Packit 9a2dfb
  [
Packit 9a2dfb
    testGroup "To JSON representation" $ fmap assertJsonEncodingExample jsonEncodingExamples
Packit 9a2dfb
  , testGroup "From JSON representation" $ fmap assertJsonExample jsonDecodingExamples
Packit 9a2dfb
  , testGroup "To/From JSON representation" $ fmap assertJsonExample jsonExamples
Packit 9a2dfb
Packit 9a2dfb
  ]
Packit 9a2dfb
Packit 9a2dfb
jsonExamples :: [Example]
Packit 9a2dfb
jsonExamples =
Packit 9a2dfb
  [
Packit 9a2dfb
    example "Either Left" "{\"Left\":1}"  (Left 1 :: Either Int Int)
Packit 9a2dfb
  , example "Either Right" "{\"Right\":1}"  (Right 1 :: Either Int Int)
Packit 9a2dfb
  , example "Nothing"  "null"  (Nothing :: Maybe Int)
Packit 9a2dfb
  , example "Just"  "1"  (Just 1 :: Maybe Int)
Packit 9a2dfb
  , example "Proxy Int" "null"  (Proxy :: Proxy Int)
Packit 9a2dfb
  , example "Tagged Char Int" "1"  (Tagged 1 :: Tagged Char Int)
Packit 9a2dfb
#if __GLASGOW_HASKELL__ >= 708
Packit 9a2dfb
    -- Test Tagged instance is polykinded
Packit 9a2dfb
  , example "Tagged 123 Int" "1"  (Tagged 1 :: Tagged 123 Int)
Packit 9a2dfb
#endif
Packit 9a2dfb
  , example "Const Char Int" "\"c\""  (Const 'c' :: Const Char Int)
Packit 9a2dfb
  , example "Tuple" "[1,2]"  ((1, 2) :: (Int, Int))
Packit 9a2dfb
  , example "NonEmpty" "[1,2,3]"  (1 :| [2, 3] :: NonEmpty Int)
Packit 9a2dfb
  , example "Seq" "[1,2,3]"  (Seq.fromList [1, 2, 3] ::  Seq.Seq Int)
Packit 9a2dfb
  , example "DList" "[1,2,3]"  (DList.fromList [1, 2, 3] :: DList.DList Int)
Packit 9a2dfb
  , example "()" "[]"  ()
Packit 9a2dfb
Packit 9a2dfb
  , Example "HashMap Int Int"
Packit 9a2dfb
        [ "{\"0\":1,\"2\":3}", "{\"2\":3,\"0\":1}"]
Packit 9a2dfb
        (HM.fromList [(0,1),(2,3)] :: HM.HashMap Int Int)
Packit 9a2dfb
  , Example "Map Int Int"
Packit 9a2dfb
        [ "{\"0\":1,\"2\":3}", "{\"2\":3,\"0\":1}"]
Packit 9a2dfb
        (M.fromList [(0,1),(2,3)] :: M.Map Int Int)
Packit 9a2dfb
  , Example "Map (Tagged Int Int) Int"
Packit 9a2dfb
        [ "{\"0\":1,\"2\":3}", "{\"2\":3,\"0\":1}"]
Packit 9a2dfb
        (M.fromList [(Tagged 0,1),(Tagged 2,3)] :: M.Map (Tagged Int Int) Int)
Packit 9a2dfb
  , example "Map [Int] Int"
Packit 9a2dfb
        "[[[0],1],[[2],3]]"
Packit 9a2dfb
        (M.fromList [([0],1),([2],3)] :: M.Map [Int] Int)
Packit 9a2dfb
  , Example "Map [Char] Int"
Packit 9a2dfb
        [ "{\"ab\":1,\"cd\":3}", "{\"cd\":3,\"ab\":1}" ]
Packit 9a2dfb
        (M.fromList [("ab",1),("cd",3)] :: M.Map String Int)
Packit 9a2dfb
  , Example "Map [I Char] Int"
Packit 9a2dfb
        [ "{\"ab\":1,\"cd\":3}", "{\"cd\":3,\"ab\":1}" ]
Packit 9a2dfb
        (M.fromList [(map pure "ab",1),(map pure "cd",3)] :: M.Map [I Char] Int)
Packit 9a2dfb
Packit 9a2dfb
  , example "nan :: Double" "null"  (Approx $ 0/0 :: Approx Double)
Packit 9a2dfb
Packit 9a2dfb
  , example "Ordering LT" "\"LT\"" LT
Packit 9a2dfb
  , example "Ordering EQ" "\"EQ\"" EQ
Packit 9a2dfb
  , example "Ordering GT" "\"GT\"" GT
Packit 9a2dfb
Packit 9a2dfb
  , example "Float" "3.14" (3.14 :: Float)
Packit 9a2dfb
  , example "Pico" "3.14" (3.14 :: Pico)
Packit 9a2dfb
  , example "Scientific" "3.14" (3.14 :: Scientific)
Packit 9a2dfb
Packit 9a2dfb
  , example "UUID" "\"c2cc10e1-57d6-4b6f-9899-38d972112d8c\"" $ UUID.fromWords
Packit 9a2dfb
      0xc2cc10e1 0x57d64b6f 0x989938d9 0x72112d8c
Packit 9a2dfb
Packit 9a2dfb
  , example "Set Int" "[1,2,3]" (Set.fromList [3, 2, 1] :: Set.Set Int)
Packit 9a2dfb
  , example "IntSet"  "[1,2,3]" (IntSet.fromList [3, 2, 1])
Packit 9a2dfb
  , example "IntMap" "[[1,2],[3,4]]" (IntMap.fromList [(3,4), (1,2)] :: IntMap.IntMap Int)
Packit 9a2dfb
  , example "Vector" "[1,2,3]" (Vector.fromList [1, 2, 3] :: Vector.Vector Int)
Packit 9a2dfb
  , example "HashSet Int" "[1,2,3]" (HashSet.fromList [3, 2, 1] :: HashSet.HashSet Int)
Packit 9a2dfb
  , example "Tree Int" "[1,[[2,[[3,[]],[4,[]]]],[5,[]]]]" (let n = Tree.Node in n 1 [n 2 [n 3 [], n 4 []], n 5 []] :: Tree.Tree Int)
Packit 9a2dfb
Packit 9a2dfb
  -- Three separate cases, as ordering in HashMap is not defined
Packit 9a2dfb
  , example "HashMap Float Int, NaN" "{\"NaN\":1}"  (Approx $ HM.singleton (0/0) 1 :: Approx (HM.HashMap Float Int))
Packit 9a2dfb
  , example "HashMap Float Int, Infinity" "{\"Infinity\":1}"  (HM.singleton (1/0) 1 :: HM.HashMap Float Int)
Packit 9a2dfb
  , example "HashMap Float Int, +Infinity" "{\"-Infinity\":1}"  (HM.singleton (negate 1/0) 1 :: HM.HashMap Float Int)
Packit 9a2dfb
Packit 9a2dfb
  -- Functors
Packit 9a2dfb
  , example "Identity Int" "1"  (pure 1 :: Identity Int)
Packit 9a2dfb
Packit 9a2dfb
  , example "Identity Char" "\"x\""      (pure 'x' :: Identity Char)
Packit 9a2dfb
  , example "Identity String" "\"foo\""  (pure "foo" :: Identity String)
Packit 9a2dfb
  , example "[Identity Char]" "\"xy\""   ([pure 'x', pure 'y'] :: [Identity Char])
Packit 9a2dfb
Packit 9a2dfb
  , example "Maybe Char" "\"x\""              (pure 'x' :: Maybe Char)
Packit 9a2dfb
  , example "Maybe String" "\"foo\""          (pure "foo" :: Maybe String)
Packit 9a2dfb
  , example "Maybe [Identity Char]" "\"xy\""  (pure [pure 'x', pure 'y'] :: Maybe [Identity Char])
Packit 9a2dfb
Packit 9a2dfb
  , example "Day; year >= 1000" "\"1999-10-12\""        (fromGregorian 1999    10 12)
Packit 9a2dfb
  , example "Day; year > 0 && < 1000" "\"0500-03-04\""  (fromGregorian 500     3  4)
Packit 9a2dfb
  , example "Day; year == 0" "\"0000-02-20\""           (fromGregorian 0       2  20)
Packit 9a2dfb
  , example "Day; year < 0" "\"-0234-01-01\""           (fromGregorian (-234)  1  1)
Packit 9a2dfb
  , example "Day; year < -1000" "\"-1234-01-01\""       (fromGregorian (-1234) 1  1)
Packit 9a2dfb
Packit 9a2dfb
  , example "Product I Maybe Int" "[1,2]"         (Pair (pure 1) (pure 2) :: Product I Maybe Int)
Packit 9a2dfb
  , example "Product I Maybe Int" "[1,null]"      (Pair (pure 1) Nothing :: Product I Maybe Int)
Packit 9a2dfb
  , example "Product I [] Char" "[\"a\",\"foo\"]" (Pair (pure 'a') "foo" :: Product I [] Char)
Packit 9a2dfb
Packit 9a2dfb
  , example "Sum I [] Int: InL"  "{\"InL\":1}"       (InL (pure 1) :: Sum I [] Int)
Packit 9a2dfb
  , example "Sum I [] Int: InR"  "{\"InR\":[1,2]}"   (InR [1, 2] :: Sum I [] Int)
Packit 9a2dfb
  , example "Sum I [] Char: InR" "{\"InR\":\"foo\"}" (InR "foo" :: Sum I [] Char)
Packit 9a2dfb
Packit 9a2dfb
  , example "Compose I  I  Int" "1"      (pure 1 :: Compose I I   Int)
Packit 9a2dfb
  , example "Compose I  [] Int" "[1]"    (pure 1 :: Compose I []  Int)
Packit 9a2dfb
  , example "Compose [] I  Int" "[1]"    (pure 1 :: Compose [] I  Int)
Packit 9a2dfb
  , example "Compose [] [] Int" "[[1]]"  (pure 1 :: Compose [] [] Int)
Packit 9a2dfb
Packit 9a2dfb
  , example "Compose I  I  Char" "\"x\""    (pure 'x' :: Compose I  I  Char)
Packit 9a2dfb
  , example "Compose I  [] Char" "\"x\""    (pure 'x' :: Compose I  [] Char)
Packit 9a2dfb
  , example "Compose [] I  Char" "\"x\""    (pure 'x' :: Compose [] I  Char)
Packit 9a2dfb
  , example "Compose [] [] Char" "[\"x\"]"  (pure 'x' :: Compose [] [] Char)
Packit 9a2dfb
Packit 9a2dfb
  , example "Compose3 I  I  I  Char" "\"x\""      (pure 'x' :: Compose3 I  I  I  Char)
Packit 9a2dfb
  , example "Compose3 I  I  [] Char" "\"x\""      (pure 'x' :: Compose3 I  I  [] Char)
Packit 9a2dfb
  , example "Compose3 I  [] I  Char" "\"x\""      (pure 'x' :: Compose3 I  [] I  Char)
Packit 9a2dfb
  , example "Compose3 I  [] [] Char" "[\"x\"]"    (pure 'x' :: Compose3 I  [] [] Char)
Packit 9a2dfb
  , example "Compose3 [] I  I  Char" "\"x\""      (pure 'x' :: Compose3 [] I  I  Char)
Packit 9a2dfb
  , example "Compose3 [] I  [] Char" "[\"x\"]"    (pure 'x' :: Compose3 [] I  [] Char)
Packit 9a2dfb
  , example "Compose3 [] [] I  Char" "[\"x\"]"    (pure 'x' :: Compose3 [] [] I  Char)
Packit 9a2dfb
  , example "Compose3 [] [] [] Char" "[[\"x\"]]"  (pure 'x' :: Compose3 [] [] [] Char)
Packit 9a2dfb
Packit 9a2dfb
  , example "Compose3' I  I  I  Char" "\"x\""      (pure 'x' :: Compose3' I  I  I  Char)
Packit 9a2dfb
  , example "Compose3' I  I  [] Char" "\"x\""      (pure 'x' :: Compose3' I  I  [] Char)
Packit 9a2dfb
  , example "Compose3' I  [] I  Char" "\"x\""      (pure 'x' :: Compose3' I  [] I  Char)
Packit 9a2dfb
  , example "Compose3' I  [] [] Char" "[\"x\"]"    (pure 'x' :: Compose3' I  [] [] Char)
Packit 9a2dfb
  , example "Compose3' [] I  I  Char" "\"x\""      (pure 'x' :: Compose3' [] I  I  Char)
Packit 9a2dfb
  , example "Compose3' [] I  [] Char" "[\"x\"]"    (pure 'x' :: Compose3' [] I  [] Char)
Packit 9a2dfb
  , example "Compose3' [] [] I  Char" "[\"x\"]"    (pure 'x' :: Compose3' [] [] I  Char)
Packit 9a2dfb
  , example "Compose3' [] [] [] Char" "[[\"x\"]]"  (pure 'x' :: Compose3' [] [] [] Char)
Packit 9a2dfb
Packit 9a2dfb
  , example "MyEither Int String: Left"  "42"      (MyLeft 42     :: MyEither Int String)
Packit 9a2dfb
  , example "MyEither Int String: Right" "\"foo\"" (MyRight "foo" :: MyEither Int String)
Packit 9a2dfb
Packit 9a2dfb
  -- newtypes from Monoid/Semigroup
Packit 9a2dfb
  , example "Monoid.Dual Int" "2" (pure 2 :: Monoid.Dual Int)
Packit 9a2dfb
  , example "Monoid.First Int" "2" (pure 2 :: Monoid.First Int)
Packit 9a2dfb
  , example "Monoid.Last Int" "2" (pure 2 :: Monoid.Last Int)
Packit 9a2dfb
  , example "Semigroup.Min Int" "2" (pure 2 :: Semigroup.Min Int)
Packit 9a2dfb
  , example "Semigroup.Max Int" "2" (pure 2 :: Semigroup.Max Int)
Packit 9a2dfb
  , example "Semigroup.First Int" "2" (pure 2 :: Semigroup.First Int)
Packit 9a2dfb
  , example "Semigroup.Last Int" "2" (pure 2 :: Semigroup.Last Int)
Packit 9a2dfb
  , example "Semigroup.WrappedMonoid Int" "2" (Semigroup.WrapMonoid 2 :: Semigroup.WrappedMonoid Int)
Packit 9a2dfb
  , example "Semigroup.Option Just" "2" (pure 2 :: Semigroup.Option Int)
Packit 9a2dfb
  , example "Semigroup.Option Nothing" "null" (Semigroup.Option (Nothing :: Maybe Bool))
Packit 9a2dfb
  ]
Packit 9a2dfb
Packit 9a2dfb
jsonEncodingExamples :: [Example]
Packit 9a2dfb
jsonEncodingExamples =
Packit 9a2dfb
  [
Packit 9a2dfb
  -- Maybe serialising is lossy
Packit 9a2dfb
  -- https://github.com/bos/aeson/issues/376
Packit 9a2dfb
    example "Just Nothing" "null" (Just Nothing :: Maybe (Maybe Int))
Packit 9a2dfb
  -- infinities cannot be recovered, null is decoded as NaN
Packit 9a2dfb
  , example "inf :: Double" "null" (Approx $ 1/0 :: Approx Double)
Packit 9a2dfb
  ]
Packit 9a2dfb
Packit 9a2dfb
jsonDecodingExamples :: [Example]
Packit 9a2dfb
jsonDecodingExamples = [
Packit 9a2dfb
  -- Maybe serialising is lossy
Packit 9a2dfb
  -- https://github.com/bos/aeson/issues/376
Packit 9a2dfb
    MaybeExample "Nothing"      "null" (Just Nothing :: Maybe (Maybe Int))
Packit 9a2dfb
  , MaybeExample "Just"         "1"    (Just $ Just 1 :: Maybe (Maybe Int))
Packit 9a2dfb
  , MaybeExample "Just Nothing" "null" (Just Nothing :: Maybe (Maybe (Maybe Int)))
Packit 9a2dfb
  -- Integral values are truncated, and overflowed
Packit 9a2dfb
  -- https://github.com/bos/aeson/issues/317
Packit 9a2dfb
  , MaybeExample "Word8 3"    "3"    (Just 3 :: Maybe Word8)
Packit 9a2dfb
  , MaybeExample "Word8 3.00" "3.00" (Just 3 :: Maybe Word8)
Packit 9a2dfb
  , MaybeExample "Word8 3.14" "3.14" (Nothing :: Maybe Word8)
Packit 9a2dfb
  , MaybeExample "Word8 -1"   "-1"   (Nothing :: Maybe Word8)
Packit 9a2dfb
  , MaybeExample "Word8 300"  "300"  (Nothing :: Maybe Word8)
Packit 9a2dfb
  -- Negative zero year, encoding never produces such:
Packit 9a2dfb
  , MaybeExample "Day -0000-02-03" "\"-0000-02-03\"" (Just (fromGregorian 0 2 3))
Packit 9a2dfb
  ]
Packit 9a2dfb
Packit 9a2dfb
data Example where
Packit 9a2dfb
    Example
Packit 9a2dfb
        :: (Eq a, Show a, ToJSON a, FromJSON a)
Packit 9a2dfb
        => String -> [L.ByteString] -> a -> Example -- empty bytestring will fail, any p [] == False
Packit 9a2dfb
    MaybeExample
Packit 9a2dfb
        :: (Eq a, Show a, FromJSON a)
Packit 9a2dfb
        => String -> L.ByteString -> Maybe a -> Example
Packit 9a2dfb
Packit 9a2dfb
example :: (Eq a, Show a, ToJSON a, FromJSON a)
Packit 9a2dfb
        => String -> L.ByteString -> a -> Example
Packit 9a2dfb
example n bs x = Example n [bs] x
Packit 9a2dfb
Packit 9a2dfb
data MyEither a b = MyLeft a | MyRight b
Packit 9a2dfb
  deriving (Generic, Show, Eq)
Packit 9a2dfb
Packit 9a2dfb
instance (ToJSON a, ToJSON b) => ToJSON (MyEither a b) where
Packit 9a2dfb
    toJSON = genericToJSON defaultOptions { sumEncoding = UntaggedValue }
Packit 9a2dfb
    toEncoding = genericToEncoding defaultOptions { sumEncoding = UntaggedValue }
Packit 9a2dfb
Packit 9a2dfb
instance (FromJSON a, FromJSON b) => FromJSON (MyEither a b) where
Packit 9a2dfb
    parseJSON = genericParseJSON defaultOptions { sumEncoding = UntaggedValue }
Packit 9a2dfb
Packit 9a2dfb
assertJsonExample :: Example -> Test
Packit 9a2dfb
assertJsonExample (Example name bss val) = testCase name $ do
Packit 9a2dfb
    assertSomeEqual "encode"           bss        (encode val)
Packit 9a2dfb
    assertSomeEqual "encode/via value" bss        (encode $ toJSON val)
Packit 9a2dfb
    for_ bss $ \bs ->
Packit 9a2dfb
        assertEqual "decode"           (Just val) (decode bs)
Packit 9a2dfb
assertJsonExample (MaybeExample name bs mval) = testCase name $
Packit 9a2dfb
    assertEqual "decode" mval (decode bs)
Packit 9a2dfb
Packit 9a2dfb
assertJsonEncodingExample :: Example -> Test
Packit 9a2dfb
assertJsonEncodingExample (Example name bss val) = testCase name $ do
Packit 9a2dfb
    assertSomeEqual "encode"           bss (encode val)
Packit 9a2dfb
    assertSomeEqual "encode/via value" bss (encode $ toJSON val)
Packit 9a2dfb
assertJsonEncodingExample (MaybeExample name _ _) = testCase name $
Packit 9a2dfb
    assertFailure "cannot encode MaybeExample"
Packit 9a2dfb
Packit 9a2dfb
assertSomeEqual :: (Eq a, Show a, Foldable f) => String -> f a -> a -> IO ()
Packit 9a2dfb
assertSomeEqual preface expected actual
Packit 9a2dfb
    | actual `elem` expected = return ()
Packit 9a2dfb
    | otherwise = assertFailure $ preface
Packit 9a2dfb
        ++ ": expecting one of " ++ show (toList expected)
Packit 9a2dfb
        ++ ", got " ++ show actual
Packit 9a2dfb