{-# LANGUAGE DataKinds #-}
{-# LANGUAGE DerivingVia #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE TypeApplications #-}
module Main where
import Control.DeepSeq
import Control.Exception
import Control.Monad
import Data.List.NonEmpty
import qualified Data.List.NonEmpty as NE
import Data.Maybe
import qualified Data.Text as T
import Data.Text.Arbitrary
import qualified Data.Text.Lazy as TL
import qualified Data.Text.Lazy.Builder as TB
import System.Timeout
import Test.Tasty
import Test.Tasty.HUnit
import Test.Tasty.QuickCheck
import Data.Text.Display
main :: IO ()
main = defaultMain spec
shouldThrow :: (HasCallStack, Exception e, Eq e) => IO a -> e -> Assertion
shouldThrow action expectedException = do
result <- try action
case result of
Right _ ->
assertFailure $ "Expected test to fail with " <> show expectedException
Left actualException ->
assertEqual
("Expected test to fail with" <> show expectedException <> " but failed with " <> show actualException <> " instead.")
expectedException
actualException
data AutomaticallyDerived = AD
deriving stock (Show)
deriving (Display) via (ShowInstance AutomaticallyDerived)
data ManualType = MT Int
instance Display ManualType where
displayPrec prec (MT i) = displayParen (prec > 10) $ "MT " <> displayPrec 11 i
data OpaqueType = OpaqueType Int
deriving
(Display)
via (OpaqueInstance "<opaque>" OpaqueType)
-- | @v \`shouldEvaluateWithin\` n@ sets the expectation that evaluating @v@
-- should take no longer than @n@ microseconds.
shouldEvaluateWithin :: (HasCallStack, NFData a) => a -> Int -> Assertion
shouldEvaluateWithin a n = do
res <- timeout n (evaluate $ force a)
when (isNothing res) $ do
assertFailure ("evaluation timed out in " <> show n <> " microseconds")
spec :: TestTree
spec =
testGroup
"Display Tests"
[ testGroup
"Instances Tests"
[ testCase "Display instance for Text stays the same" $
display ("3" :: Text) @?= ("3" :: Text)
, testCase "Deriving via its own Show instance works" $
T.unpack (display AD) @?= show AD
, testCase "Opaque types stay opaque" $
display (OpaqueType 3 :: OpaqueType) @?= "<opaque>"
, testCase "Manual instance is stays the same" $
display (MT 32) @?= "MT 32"
, testCase "List instance is equivalent to Show" $ do
let list = [1 .. 5] :: [Int]
T.unpack (display list) @?= show list
, testCase "Single-element List instance is equivalent to Show" $ do
let list = [1] :: [Int]
T.unpack (display list) @?= show list
, testCase "List instance is streamed lazily" $ do
let list = [1 ..] :: [Int]
TL.take 20 (TB.toLazyText $ displayBuilder list) `shouldEvaluateWithin` 100000
, testCase "NonEmpty instance is equivalent to Show" $ do
let ne = NE.fromList [1 .. 5] :: NonEmpty Int
T.unpack (display ne) @?= show ne
, testCase "Just True instance is equivalent to Show" $ do
T.unpack (display (Just True)) @?= show (Just True)
, testCase "Nested Maybe instance is equivalent to Show" $ do
let nestedMaybe = Just (Just 5) :: Maybe (Maybe Int)
T.unpack (display nestedMaybe) @?= show nestedMaybe
, testCase "Nothing instance is equivalent to Show" $ do
T.unpack (display (Nothing @Bool)) @?= show (Nothing @Bool)
, testCase "Char '\'' instance is equivalent to Text" $ do
display '\'' @?= T.singleton '\''
, testCase "2-Tuple instance is equivalent to Show" $ do
let tuple = (1 :: Int, True)
T.unpack (display tuple) @?= show tuple
, testCase "3-Tuple instance is equivalent to Show" $ do
let tuple = (1 :: Int, True, "hahahha" :: String)
display tuple @?= "(" <> display (1 :: Int) <> "," <> display True <> "," <> display @String "hahahha" <> ")"
]
, testGroup
"Properties Tests"
[ testProperty "Text instance stays the same" $ do
\string -> display (string :: Text) === string
, testProperty "String instance is equivalent to Text" $ do
\string -> display (string :: String) === T.pack string
, testProperty "Chars are packed" $
\c -> display (c :: Char) === T.singleton c
]
, testGroup
"`displayParen` tests"
[ testCase "Surrounds with parens when True" $
displayParen True "foo" @?= "(foo)"
, testCase "Doesn't surround with parens when False" $
displayParen False "foo" @?= "foo"
, testCase "Surrounds deeply-nested Maybes with a prec of 10" $
displayPrec 10 (Just (Just (Just (3 :: Int)))) @?= "Just (Just (Just 3))"
, testCase "Surrounds deeply-nested Maybes with a prec of 11" $
displayPrec 11 (Just (Just (Just (3 :: Int)))) @?= "(Just (Just (Just 3)))"
]
]