packages feed

serdoc-core-0.1.0.0: test/Data/SerDoc/Test/Class.hs

{-# LANGUAGE TypeApplications #-}
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE TypeOperators #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE TemplateHaskell #-}
{-# LANGUAGE DerivingVia #-}
{-# LANGUAGE StandaloneDeriving #-}
{-# LANGUAGE UndecidableInstances #-}

module Data.SerDoc.Test.Class
where

import Test.Tasty
import Test.Tasty.QuickCheck
import Data.Proxy
import Control.Monad.Identity
import Control.Monad.Except
import Data.ByteString (ByteString)
import qualified Data.ByteString as BS
import Control.Monad.Writer
import Control.Monad.State
import Data.Kind

import Data.SerDoc.Class
import Data.SerDoc.Info
import Data.SerDoc.TH
import Data.SerDoc.TestUtil

data ShowCodec (m :: Type -> Type)

instance Codec (ShowCodec m) where
  type MonadEncode (ShowCodec m) = Writer ByteString
  type MonadDecode (ShowCodec m) = ExceptT String (State ByteString)

instance HasInfo (ShowCodec m) () where
  info _ _ = basicField "()" (FixedSize $ BS.length $ showBS ())

instance HasInfo (ShowCodec m) Int where
  info _ _ = basicField "Int" (RangeSize (FixedSize 1) (FixedSize $ length $ show (minBound :: Int)))

newtype ViaShow a = ViaShow { viaShow :: a }

instance (Show a, Read a) => Serializable (ShowCodec m) (ViaShow a) where
  encode _ = tell . showBS . viaShow
  decode _ = do
    decodedMay <- readMaybeBS <$> get
    case decodedMay of
      Nothing ->
        throwError "Read: no parse"
      Just (x, rest) -> do
        put rest
        return (ViaShow x)

deriving via (ViaShow ()) instance Serializable (ShowCodec m) ()

deriving via (ViaShow Int) instance Serializable (ShowCodec m) Int


data Record =
  Record
    { firstField :: ()
    , secondField :: Int
    }
    deriving (Show, Read, Eq, Ord)

instance Arbitrary Record where
  arbitrary = Record <$> arbitrary <*> arbitrary
  shrink (Record a b) =
    Record a <$> shrink b

$(deriveSerDoc ''ShowCodec [] ''Record)

data Record1 a =
  Record1
    { firstField1 :: a
    , secondField1 :: [a]
    }
    deriving (Show, Read, Eq, Ord)

instance Arbitrary a => Arbitrary (Record1 a) where
  arbitrary = Record1 <$> arbitrary <*> arbitrary
  shrink (Record1 a b) =
    (Record1 a <$> shrink b)
    ++
    (Record1 <$> shrink a <*> pure b)

$(deriveSerDoc ''ShowCodec [] ''Record1)

tests :: TestTree
tests = testGroup "Class"
          [ testGroup "ShowCodec m"
              [ testGroup "HasInfo"
                  [ testProperty "()" $ pUnitHasInfo (Proxy @(ShowCodec IO))
                  ]
              , testGroup "Serializable"
                  [ testProperty "()" $ pRoundTrip @() (Proxy @(ShowCodec IO)) execWriter (runState . runExceptT)
                  -- , testProperty "Int" $ pRoundTrip @Int (Proxy @(ShowCodec IO)) execWriter runState
                  -- , testProperty "Record" $ pRoundTrip @Record (Proxy @(ShowCodec IO)) execWriter runState
                  ]
              ]
          ]

pUnitHasInfo :: (Codec codec, HasInfo codec ())
            => Proxy codec
            -> Property
pUnitHasInfo pCodec =
  actual === expected
  where
    actual = info pCodec (Proxy @())
    expected = basicField "()" (FixedSize $ BS.length $ showBS ())

pRoundTrip :: forall a codec err encoded mdecode.
              ( Codec codec
              , Serializable codec a
              , Arbitrary a
              , Eq a
              , Show a
              , Monad (MonadEncode codec)
              , Monad mdecode
              , MonadDecode codec ~ ExceptT err mdecode
              , Eq encoded
              , Show encoded
              , Monoid encoded
              , Eq err
              , Show err
              )
           => Proxy codec
           -> (MonadEncode codec () -> encoded)
           -> (MonadDecode codec a -> encoded -> (Either err a, encoded))
           -> a
           -> Property
pRoundTrip pCodec runEncode runDecode expected =
  actual === (Right expected, mempty)
  where
    encoded = runEncode (encode pCodec expected)
    actual = runDecode (decode pCodec) encoded