module LazyHClose (testSuite) where

import Control.Monad (void, forM_)
import Data.ByteString.Internal (toForeignPtr)
import Foreign.C.String (withCString)
import Foreign.ForeignPtr (finalizeForeignPtr)
import System.IO (openFile, openTempFile, hClose, hPutStrLn, IOMode(..))
import System.Posix.Internals (c_unlink)
import Test.Tasty (TestTree, testGroup, withResource)
import Test.Tasty.QuickCheck (testProperty, ioProperty)

import qualified Data.ByteString            as S
import qualified Data.ByteString.Char8      as S8
import qualified Data.ByteString.Lazy       as L
import qualified Data.ByteString.Lazy.Char8 as L8

n :: Int
n = 1000

testSuite :: TestTree
testSuite = withResource
  (do (fn, h) <- openTempFile "." "lazy-hclose-test.tmp"; hPutStrLn h "x"; hClose h; pure fn)
  removeFile $ \fn' ->
    testGroup "LazyHClose"
    [ testProperty "Testing resource leaks for Strict.readFile" $ ioProperty $
      forM_ [1..n] $ const $ do
        fn <- fn'
        r <- S.readFile fn
        appendFile fn "" -- will fail, if fn has not been closed yet

    , testProperty "Testing resource leaks for Lazy.readFile" $ ioProperty $
      forM_ [1..n] $ const $ do
        fn <- fn'
        r <- L.readFile fn
        L.length r `seq` return ()
        appendFile fn "" -- will fail, if fn has not been closed yet

    , testProperty "Testing resource leaks when converting lazy to strict" $ ioProperty $
      forM_ [1..n] $ const $ do
        fn <- fn'
        let release c = finalizeForeignPtr fp where (fp,_,_) = toForeignPtr c
        r <- L.readFile fn
        mapM_ release (L.toChunks r)
        appendFile fn "" -- will fail, if fn has not been closed yet

    , testProperty "Testing strict hGetContents" $ ioProperty $
      forM_ [1..n] $ const $ do
        fn <- fn'
        h <- openFile fn ReadMode
        r <- S.hGetContents h
        S.last r `seq` return ()
        appendFile fn "" -- will fail, if fn has not been closed yet

    , testProperty "Testing lazy hGetContents" $ ioProperty $
      forM_ [1..n] $ const $ do
        fn <- fn'
        h <- openFile fn ReadMode
        r <- L.hGetContents h
        L.last r `seq` return ()
        appendFile fn "" -- will fail, if fn has not been closed yet
    ]

removeFile :: String -> IO ()
removeFile fn = void $ withCString fn c_unlink
