Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

improve test function write_read #593

Merged
merged 7 commits into from
May 19, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 12 additions & 15 deletions tests/Tests/Properties/LowLevel.hs
Original file line number Diff line number Diff line change
Expand Up @@ -100,17 +100,14 @@ t_literal_foo = T.pack "foo"
-- tl_put_get = write_read TL.unlines TL.filter put get
-- where put h = withRedirect h IO.stdout . TL.putStr
-- get h = withRedirect h IO.stdin TL.getContents
t_write_read = write_read T.unlines T.filter T.hPutStr T.hGetContents
tl_write_read = write_read TL.unlines TL.filter TL.hPutStr TL.hGetContents
t_write_read = write_read T.unlines T.filter T.hPutStr T.hGetContents id
tl_write_read = write_read TL.unlines TL.filter TL.hPutStr TL.hGetContents id

t_write_read_line m b t = write_read (T.concat . take 1) T.filter T.hPutStrLn
T.hGetLine m b [t]
tl_write_read_line m b t = write_read (TL.concat . take 1) TL.filter TL.hPutStrLn
TL.hGetLine m b [t]
t_write_read_line = write_read (T.concat . take 1) T.filter T.hPutStrLn T.hGetLine (: [])
tl_write_read_line = write_read (TL.concat . take 1) TL.filter TL.hPutStrLn TL.hGetLine (: [])

utf8_write_read = write_read T.unlines T.filter TU.hPutStr TU.hGetContents
utf8_write_read_line m b t = write_read (T.concat . take 1) T.filter TU.hPutStrLn
TU.hGetLine m b [t]
utf8_write_read = write_read T.unlines T.filter TU.hPutStr TU.hGetContents id
utf8_write_read_line = write_read (T.concat . take 1) T.filter TU.hPutStrLn TU.hGetLine (: [])

testLowLevel :: TestTree
testLowLevel =
Expand Down Expand Up @@ -143,12 +140,12 @@ testLowLevel =
],

testGroup "input-output" [
testProperty "t_write_read" t_write_read,
testProperty "tl_write_read" tl_write_read,
testProperty "t_write_read_line" t_write_read_line,
testProperty "tl_write_read_line" tl_write_read_line,
testProperty "utf8_write_read" utf8_write_read,
testProperty "utf8_write_read_line" utf8_write_read_line
testGroup "t_write_read" t_write_read,
testGroup "tl_write_read" tl_write_read,
testGroup "t_write_read_line" t_write_read_line,
testGroup "tl_write_read_line" tl_write_read_line,
testGroup "utf8_write_read" utf8_write_read,
testGroup "utf8_write_read_line" utf8_write_read_line
-- These tests are subject to I/O race conditions
-- testProperty "t_put_get" t_put_get,
-- testProperty "tl_put_get" tl_put_get
Expand Down
78 changes: 48 additions & 30 deletions tests/Tests/QuickCheckUtils.hs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,9 @@
{-# LANGUAGE CPP #-}
{-# LANGUAGE DeriveFunctor #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE NamedFieldPuns #-}
{-# LANGUAGE RankNTypes #-}
{-# LANGUAGE ScopedTypeVariables #-}

{-# OPTIONS_GHC -fno-warn-orphans #-}

Expand All @@ -31,14 +34,16 @@ module Tests.QuickCheckUtils
) where

import Control.Arrow ((***))
import Control.DeepSeq (NFData (..), deepseq)
import Control.Exception (bracket)
import Data.Bool (bool)
import Data.Char (isSpace)
import Data.Text.Foreign (I8)
import Data.Text.Lazy.Builder.RealFloat (FPFormat(..))
import Data.Word (Word8, Word16)
import Test.QuickCheck (Arbitrary(..), arbitraryUnicodeChar, arbitraryBoundedEnum, getUnicodeString, arbitrarySizedIntegral, shrinkIntegral, Property, ioProperty, discard, counterexample, scale, (===), (.&&.), NonEmptyList(..))
import GHC.IO.Encoding.Types (TextEncoding(TextEncoding,textEncodingName))
import Test.QuickCheck (Arbitrary(..), arbitraryUnicodeChar, arbitraryBoundedEnum, getUnicodeString, arbitrarySizedIntegral, shrinkIntegral, Property, ioProperty, discard, counterexample, scale, (.&&.), NonEmptyList(..), forAll, getPositive)
import Test.QuickCheck.Gen (Gen, choose, chooseAny, elements, frequency, listOf, oneof, resize, sized)
import Test.Tasty (TestTree, testGroup)
import Test.Tasty.QuickCheck (testProperty)
import Tests.Utils
import qualified Data.ByteString as B
import qualified Data.ByteString.Lazy as BL
Expand All @@ -54,6 +59,9 @@ import qualified System.IO as IO
genWord8 :: Gen Word8
genWord8 = chooseAny

genWord16 :: Gen Word16
genWord16 = chooseAny

instance Arbitrary I8 where
arbitrary = arbitrarySizedIntegral
shrink = shrinkIntegral
Expand Down Expand Up @@ -227,7 +235,7 @@ instance Arbitrary IO.BufferMode where
return IO.LineBuffering,
return (IO.BlockBuffering Nothing),
(IO.BlockBuffering . Just . (+1) . fromIntegral) `fmap`
(arbitrary :: Gen Word16) ]
genWord16 ]

-- This test harness is complex! What property are we checking?
--
Expand All @@ -240,33 +248,39 @@ instance Arbitrary IO.BufferMode where
-- sometimes contain line endings.)
-- * Newline translation mode.
-- * Buffering.
write_read :: (NFData a, Eq a, Show a)
=> ([b] -> a)
-> ((Char -> Bool) -> a -> b)
-> (IO.Handle -> a -> IO ())
-> (IO.Handle -> IO a)
-> IO.NewlineMode
-> IO.BufferMode
-> [a]
-> Property
write_read _ _ _ _ (IO.NewlineMode IO.LF IO.CRLF) _ _ = discard
write_read unline filt writer reader nl buf ts = ioProperty $
(===t) <$> act
write_read :: forall a b c.
(Eq a, Show a, Show c, Arbitrary c)
=> ([b] -> a)
-> ((Char -> Bool) -> b -> b)
-> (IO.Handle -> a -> IO ())
-> (IO.Handle -> IO a)
-> (c -> [b])
-> [TestTree]
write_read unline filt writer reader modData
= encodings <&> \enc@TextEncoding {textEncodingName} -> testGroup textEncodingName
[ testProperty "NoBuffering" $ propTest enc (pure IO.NoBuffering)
, testProperty "LineBuffering" $ propTest enc (pure IO.LineBuffering)
, testProperty "BlockBuffering" $ propTest enc blockBuffering
]
where
t = unline . map (filt (not . (`elem` "\r\n"))) $ ts

act = withTempFile $ \path h -> do
IO.hSetEncoding h IO.utf8
IO.hSetNewlineMode h nl
IO.hSetBuffering h buf
() <- writer h t
IO.hClose h
bracket (IO.openFile path IO.ReadMode) IO.hClose $ \h' -> do
IO.hSetEncoding h' IO.utf8
IO.hSetNewlineMode h' nl
IO.hSetBuffering h' buf
r <- reader h'
r `deepseq` return r
propTest :: TextEncoding -> Gen IO.BufferMode -> IO.NewlineMode -> c -> Property
propTest _ _ (IO.NewlineMode IO.LF IO.CRLF) _ = discard
propTest enc genBufferMode nl d = forAll genBufferMode $ \mode -> ioProperty $ withTempFile $ \_ h -> do
let ts = modData d
t = unline . map (filt (not . (`elem` "\r\n"))) $ ts
IO.hSetEncoding h enc
IO.hSetNewlineMode h nl
IO.hSetBuffering h mode
() <- writer h t
IO.hSeek h IO.AbsoluteSeek 0
r <- reader h
let isEq = r == t
seq isEq $ pure $ counterexample (show r ++ bool " /= " " == " isEq ++ show t) isEq

encodings = [IO.utf8, IO.utf8_bom, IO.utf16, IO.utf16le, IO.utf16be, IO.utf32, IO.utf32le, IO.utf32be]

blockBuffering :: Gen IO.BufferMode
blockBuffering = IO.BlockBuffering <$> fmap (fmap $ min 4 . getPositive) arbitrary
BebeSparkelSparkel marked this conversation as resolved.
Show resolved Hide resolved

-- Generate various Unicode space characters with high probability
arbitrarySpacyChar :: Gen Char
Expand All @@ -287,3 +301,7 @@ newtype SkewedBool = Skewed { getSkewed :: Bool }

instance Arbitrary SkewedBool where
arbitrary = Skewed <$> frequency [(1, pure False), (5, pure True)]

(<&>) :: [a] -> (a -> b) -> [b]
(<&>) = flip fmap

8 changes: 4 additions & 4 deletions tests/Tests/Utils.hs
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,10 @@ module Tests.Utils
) where

import Control.Exception (SomeException, bracket, bracket_, evaluate, try)
import Control.Monad (when)
import Control.Monad (when, unless)
import GHC.IO.Handle.Internals (withHandle)
import System.Directory (removeFile)
import System.IO (Handle, hClose, hFlush, hIsOpen, hIsWritable, openTempFile)
import System.IO (Handle, hClose, hFlush, hIsOpen, hIsClosed, hIsWritable, openTempFile)
import Test.QuickCheck (Property, ioProperty, property, (===), counterexample)

-- Ensure that two potentially bottom values (in the sense of crashing
Expand All @@ -34,8 +34,8 @@ withTempFile :: (FilePath -> Handle -> IO a) -> IO a
withTempFile = bracket (openTempFile "." "crashy.txt") cleanupTemp . uncurry
where
cleanupTemp (path,h) = do
open <- hIsOpen h
when open (hClose h)
closed <- hIsClosed h
unless closed $ hClose h
BebeSparkelSparkel marked this conversation as resolved.
Show resolved Hide resolved
removeFile path

withRedirect :: Handle -> Handle -> IO a -> IO a
Expand Down
Loading