daemons-0.1.0: Test/Daemon.hs
{-# LANGUAGE OverloadedStrings, ScopedTypeVariables #-}
module Main where
import Control.Concurrent
import Control.Exception
import Control.Monad
import Data.Default
import Data.Monoid
import System.Directory
import System.Posix.Daemon
import System.Posix.Process
import Test.Framework
import Test.Framework.Providers.HUnit
import Test.HUnit
main :: IO ()
main = defaultMainWithOpts
[ testCase "firstRun" testFirst
, testCase "withPid" testWithPid
, testCase "isRunning" testIsRunning
, testCase "exclusion" testExclusion
, testCase "release" testRelease
, testCase "redirection" testRedirection
] mempty
ensureRemoved :: [FilePath] -> IO ()
ensureRemoved filepaths = forM_ filepaths $ \filepath -> do
exists <- doesFileExist filepath
when exists $ do
removeFile filepath
-- Wait the given number of ms.
sleep :: Int -> IO ()
sleep n = threadDelay (n * 1000)
testFirst :: Assertion
testFirst = flip finally (ensureRemoved ["tmp"]) $ do
let txtExp = "42"
runDetached Nothing def $ do
writeFile "tmp" txtExp
sleep 500
txt <- readFile "tmp"
txt @?= txtExp
testWithPid :: Assertion
testWithPid = flip finally (ensureRemoved ["pid", "tmp"]) $ do
let txtExp = "42"
runDetached (Just "pid") def $ do
pid <- getProcessID
pid' <- readFile "pid"
if show pid == pid'
then writeFile "tmp" txtExp
else writeFile "tmp" "wrong pid recorded"
sleep 500
txt <- readFile "tmp"
txt @?= txtExp
pid <- readFile "pid"
null pid @?= False
testIsRunning :: Assertion
testIsRunning = flip finally (ensureRemoved ["pid", "tmp"]) $ do
runDetached (Just "pid") def $ do
running <- isRunning "pid"
writeFile "tmp" (show running)
sleep 10000
sleep 500
-- FIXME There's some weird behaviour when the process that has locked
-- the file (or its ancestors, or its descendents) use 'isRunning'.
--
-- The semantics of 'fnctl' are "try to aquire the requested lock; if
-- there is an incompatible lock in place, return it". Of course,
-- this means that the process that acquired the lock sees it as
-- unlocked.
--
-- We mitigated the obvious part of the problem (same process) by
-- checking the pid in the pidfile. Now, we're left with the case
-- where an ancestor of the process thinks it can set the lock.
-- running <- isRunning "pid"
-- running @?= True
txt <- readFile "tmp"
txt @?= "True"
testExclusion :: Assertion
testExclusion = flip finally (ensureRemoved ["pid", "tmp"]) $ do
let txtExp = "ok"
runDetached (Just "pid") def $ do
sleep 1000
sleep 500
handle (\(_ :: SomeException) -> writeFile "tmp" txtExp)
(runDetached (Just "pid") def $ do
writeFile "tmp" "failed")
sleep 500
txt <- readFile "tmp"
txt @?= txtExp
testRelease :: Assertion
testRelease = flip finally (ensureRemoved ["pid", "tmp"]) $ do
let txtExp = "ok"
runDetached (Just "pid") def $ do
writeFile "tmp" txtExp
sleep 500
txt <- readFile "tmp"
txt @?= txtExp
let txtExp' = "ok"
runDetached (Just "pid") def $ do
writeFile "tmp" txtExp'
sleep 500
txt' <- readFile "tmp"
txt' @?= txtExp'
testRedirection :: Assertion
testRedirection = flip finally (ensureRemoved ["tmp"]) $ do
let txtExp = "ok"
runDetached Nothing (ToFile "tmp") $ do
putStr "ok"
sleep 500
txt <- readFile "tmp"
txt @?= txtExp