{-# LANGUAGE AllowAmbiguousTypes #-} {-# LANGUAGE ScopedTypeVariables #-} {-# LANGUAGE TypeApplications #-} module PrimitiveTests where import Control.Applicative (Const) import Data.Aeson (DotNetTime, Value, (.:)) import qualified Data.Aeson as A import Data.Aeson.Types (parseEither) import Data.DList (DList) import Data.Fixed (E12, Fixed) import Data.Functor.Identity (Identity) import Data.HashMap.Strict (HashMap) import Data.HashSet (HashSet) import Data.Int (Int8, Int16, Int32, Int64) import Data.IntMap (IntMap) import Data.IntSet (IntSet) import Data.List.NonEmpty (NonEmpty) import Data.Map (Map) import Data.Monoid (Dual) import Data.Proxy (Proxy) import Data.Ratio (Ratio) import Data.Scientific (Scientific) import Data.Semigroup (First, Last, Max, Min) import Data.Sequence (Seq) import Data.Set (Set) import Data.Text as T (Text, unpack) import Data.Text.Lazy as LT (Text) import Data.Time import Data.Tree (Tree) import Data.UUID.Types (UUID) import qualified Data.Vector as V import qualified Data.Vector.Primitive as VP import qualified Data.Vector.Storable as VS import qualified Data.Vector.Unboxed as VU import Data.Version as DV (Version) import Data.Word (Word8, Word16, Word32, Word64) import Foreign.C.Types (CTime) import Numeric.Natural (Natural) import Test.Tasty import Test.Tasty.HUnit (assertEqual, assertFailure, testCase) import Test.Tasty.QuickCheck (Arbitrary, testProperty) import Data.SafeJSON import Instances() primitiveTests :: TestTree primitiveTests = testGroup "Primitives" [ regularParsing , toJSONEquivalence , fromJSONEquivalence ] parseValueAnd :: forall a. SafeJSON a => T.Text -> (a -> IO ()) -> TestTree parseValueAnd t f = testCase (T.unpack t) $ do mVal <- A.decodeFileStrict "test/json/primitives.json" maybe (assertFailure "couldn't read file") (either fail f . parseEither go) mVal where go = A.withObject "test" $ \o -> do o .: t >>= safeFromJSON parseValue :: forall a. SafeJSON a => T.Text -> TestTree parseValue t = parseValueAnd t (const $ return () :: a -> IO ()) fromJSONTest :: forall a. (A.FromJSON a, SafeJSON a, Eq a, Show a) => T.Text -> TestTree fromJSONTest t = parseValueAnd t $ \val -> do let a = (parseEither A.parseJSON val :: Either String a) b = parseEither safeFromJSON val assertEqual "SafeJSON not equivalent to FromJSON" a b toJSONTest :: forall a. (A.ToJSON a, SafeJSON a, Arbitrary a, Show a) => String -> TestTree toJSONTest s = testProperty s $ \a -> A.toJSON a == safeToJSON (a :: a) regularParsing :: TestTree regularParsing = testGroup "Parsing from JSON" [ parseValue @Bool "Bool" , parseValue @Ordering "Ordering" , parseValue @() "Unit" , parseValue @Char "Char" , parseValue @Float "Float" , parseValue @Double "Double" , parseValue @Int "Int" , parseValue @Natural "Natural" , parseValue @Integer "Integer" , parseValue @Int8 "Int8" , parseValue @Int16 "Int16" , parseValue @Int32 "Int32" , parseValue @Int64 "Int64" , parseValue @Word "Word" , parseValue @Word8 "Word8" , parseValue @Word16 "Word16" , parseValue @Word32 "Word32" , parseValue @Word64 "Word64" , parseValue @T.Text "T.Text" , parseValue @LT.Text "LT.Text" , parseValue @DV.Version "Version" , parseValue @Scientific "Scientific" , parseValue @IntSet "IntSet" , parseValue @UUID "UUID" , parseValue @CTime "CTime" , parseValue @ZonedTime "ZonedTime" , parseValue @LocalTime "LocalTime" , parseValue @TimeOfDay "TimeOfDay" , parseValue @UTCTime "UTCTime" , parseValue @NominalDiffTime "NominalDiffTime" , parseValue @DiffTime "DiffTime" , parseValue @Day "Day" , parseValue @DotNetTime "DotNetTime" , parseValue @Value "Value" , parseValue @(Ratio Int) "Ratio" , parseValue @(Fixed E12) "Fixed" , parseValue @(Proxy ()) "Proxy" , parseValue @(Identity T.Text) "Identity" , parseValue @(First T.Text) "First" , parseValue @(Last T.Text) "Last" , parseValue @(Min T.Text) "Min" , parseValue @(Max T.Text) "Max" , parseValue @(Dual T.Text) "Dual" , parseValue @([Int]) "[]" , parseValue @(IntMap Bool) "IntMap" , parseValue @(NonEmpty Int) "NonEmpty" , parseValue @(Seq T.Text) "Seq" , parseValue @(Tree T.Text) "Tree" , parseValue @(Const T.Text ()) "Const" , parseValue @(Maybe T.Text) "Maybe" , parseValue @(Maybe T.Text) "Maybe2" , parseValue @(Either T.Text Bool) "Either" , parseValue @(Either T.Text Bool) "Either2" , parseValue @(DList Int) "DList" , parseValue @(V.Vector Int) "V.Vector" , parseValue @(VS.Vector Int) "VS.Vector" , parseValue @(VP.Vector Int) "VP.Vector" , parseValue @(VU.Vector Int) "VU.Vector" , parseValue @(Set Int) "Set" , parseValue @(Map T.Text Int) "Map" , parseValue @(HashSet Int) "HashSet" , parseValue @(HashMap T.Text Int) "HashMap" , parseValue @(Int, Bool) "Tuple2" , parseValue @(Int, Bool, T.Text) "Tuple3" , parseValue @(Int, Bool, T.Text, [Int]) "Tuple4" , parseValue @(Int, Bool, T.Text, [Int], Double) "Tuple5" ] -------------------------------- toJSONEquivalence :: TestTree toJSONEquivalence = testGroup "safeToJSON === toJSON" [ toJSONTest @Bool "Bool" , toJSONTest @Ordering "Ordering" , toJSONTest @() "()" , toJSONTest @Char "Char" , toJSONTest @Float "Float" , toJSONTest @Double "Double" , toJSONTest @Int "Int" , toJSONTest @Natural "Natural" , toJSONTest @Integer "Integer" , toJSONTest @Int8 "Int8" , toJSONTest @Int16 "Int16" , toJSONTest @Int32 "Int32" , toJSONTest @Int64 "Int64" , toJSONTest @Word "Word" , toJSONTest @Word8 "Word8" , toJSONTest @Word16 "Word16" , toJSONTest @Word32 "Word32" , toJSONTest @Word64 "Word64" , toJSONTest @T.Text "T.Text" , toJSONTest @LT.Text "LT.Text" , toJSONTest @DV.Version "DV.Version" , toJSONTest @Scientific "Scientific" , toJSONTest @IntSet "IntSet" , toJSONTest @UUID "UUID" , toJSONTest @CTime "CTime" , toJSONTest @ZonedTime "ZonedTime" , toJSONTest @LocalTime "LocalTime" , toJSONTest @TimeOfDay "TimeOfDay" , toJSONTest @UTCTime "UTCTime" , toJSONTest @NominalDiffTime "NominalDiffTime" , toJSONTest @DiffTime "DiffTime" , toJSONTest @Day "Day" , toJSONTest @DotNetTime "DotNetTime" , toJSONTest @Value "Value" , toJSONTest @(Ratio Int) "Ratio" , toJSONTest @(Fixed E12) "Fixed" , toJSONTest @(Proxy ()) "Proxy" , toJSONTest @(Identity T.Text) "Identity" , toJSONTest @(First T.Text) "First" , toJSONTest @(Last T.Text) "Last" , toJSONTest @(Min T.Text) "Min" , toJSONTest @(Max T.Text) "Max" , toJSONTest @(Dual T.Text) "Dual" , toJSONTest @([Int]) "[]" , toJSONTest @(IntMap Bool) "IntMap" , toJSONTest @(NonEmpty Int) "NonEmpty" , toJSONTest @(Seq T.Text) "Seq" , toJSONTest @(Tree T.Text) "Tree" , toJSONTest @(Const T.Text ()) "Const" , toJSONTest @(Maybe T.Text) "Maybe" , toJSONTest @(Maybe T.Text) "Maybe2" , toJSONTest @(Either T.Text Bool) "Either" , toJSONTest @(Either T.Text Bool) "Either2" , toJSONTest @(DList Int) "DList" , toJSONTest @(V.Vector Int) "V.Vector" , toJSONTest @(VS.Vector Int) "VS.Vector" , toJSONTest @(VP.Vector Int) "VP.Vector" , toJSONTest @(VU.Vector Int) "VU.Vector" , toJSONTest @(Set Int) "Set" , toJSONTest @(Map T.Text Int) "Map" , toJSONTest @(HashSet Int) "HashSet" , toJSONTest @(HashMap T.Text Int) "HashMap" , toJSONTest @(Int, Bool) "Tuple2" , toJSONTest @(Int, Bool, T.Text) "Tuple3" , toJSONTest @(Int, Bool, T.Text, [Int]) "Tuple4" , toJSONTest @(Int, Bool, T.Text, [Int], Double) "Tuple5" ] fromJSONEquivalence :: TestTree fromJSONEquivalence = testGroup "safeFromJSON === fromJSON" [ fromJSONTest @Bool "Bool" , fromJSONTest @Ordering "Ordering" , fromJSONTest @() "Unit" , fromJSONTest @Char "Char" , fromJSONTest @Float "Float" , fromJSONTest @Double "Double" , fromJSONTest @Int "Int" , fromJSONTest @Natural "Natural" , fromJSONTest @Integer "Integer" , fromJSONTest @Int8 "Int8" , fromJSONTest @Int16 "Int16" , fromJSONTest @Int32 "Int32" , fromJSONTest @Int64 "Int64" , fromJSONTest @Word "Word" , fromJSONTest @Word8 "Word8" , fromJSONTest @Word16 "Word16" , fromJSONTest @Word32 "Word32" , fromJSONTest @Word64 "Word64" , fromJSONTest @T.Text "T.Text" , fromJSONTest @LT.Text "LT.Text" , fromJSONTest @DV.Version "Version" , fromJSONTest @Scientific "Scientific" , fromJSONTest @IntSet "IntSet" , fromJSONTest @UUID "UUID" , fromJSONTest @CTime "CTime" -- , fromJSONTest @ZonedTime "ZonedTime" -- No Eq instance , fromJSONTest @LocalTime "LocalTime" , fromJSONTest @TimeOfDay "TimeOfDay" , fromJSONTest @UTCTime "UTCTime" , fromJSONTest @NominalDiffTime "NominalDiffTime" , fromJSONTest @DiffTime "DiffTime" , fromJSONTest @Day "Day" , fromJSONTest @DotNetTime "DotNetTime" , fromJSONTest @Value "Value" , fromJSONTest @(Ratio Int) "Ratio" , fromJSONTest @(Fixed E12) "Fixed" , fromJSONTest @(Proxy ()) "Proxy" , fromJSONTest @(Identity T.Text) "Identity" , fromJSONTest @(First T.Text) "First" , fromJSONTest @(Last T.Text) "Last" , fromJSONTest @(Min T.Text) "Min" , fromJSONTest @(Max T.Text) "Max" , fromJSONTest @(Dual T.Text) "Dual" , fromJSONTest @([Int]) "[]" , fromJSONTest @(IntMap Bool) "IntMap" , fromJSONTest @(NonEmpty Int) "NonEmpty" , fromJSONTest @(Seq T.Text) "Seq" , fromJSONTest @(Tree T.Text) "Tree" , fromJSONTest @(Const T.Text ()) "Const" , fromJSONTest @(Maybe T.Text) "Maybe" , fromJSONTest @(Maybe T.Text) "Maybe2" , fromJSONTest @(Either T.Text Bool) "Either" , fromJSONTest @(Either T.Text Bool) "Either2" , fromJSONTest @(DList Int) "DList" , fromJSONTest @(V.Vector Int) "V.Vector" , fromJSONTest @(VS.Vector Int) "VS.Vector" , fromJSONTest @(VP.Vector Int) "VP.Vector" , fromJSONTest @(VU.Vector Int) "VU.Vector" , fromJSONTest @(Set Int) "Set" , fromJSONTest @(Map T.Text Int) "Map" , fromJSONTest @(HashSet Int) "HashSet" , fromJSONTest @(HashMap T.Text Int) "HashMap" , fromJSONTest @(Int, Bool) "Tuple2" , fromJSONTest @(Int, Bool, T.Text) "Tuple3" , fromJSONTest @(Int, Bool, T.Text, [Int]) "Tuple4" , fromJSONTest @(Int, Bool, T.Text, [Int], Double) "Tuple5" ]