module Main where import Control.Exception import Control.Concurrent import Database.Postgres.Temp import Control.Concurrent.Async import qualified Database.PostgreSQL.Simple as PG import System.Exit import System.IO.Temp import System.Process import Control.Monad (unless, forM_, forever, void) import System.Directory withConn :: DB -> (PG.Connection -> IO a) -> IO a withConn db = bracket (PG.connectPostgreSQL $ toConnectionString db ) PG.close countPostgresProcesses :: IO Int countPostgresProcesses = countProcesses "postgres" countInitdbProcesses :: IO Int countInitdbProcesses = countProcesses "initdb" countCreatedbProcesses :: IO Int countCreatedbProcesses = countProcesses "createdb" countProcesses :: String -> IO Int countProcesses processName = do -- TODO we should restrict to child process (exitCode, xs, _) <- readProcessWithExitCode "pgrep" [processName] [] unless (exitCode == ExitSuccess || exitCode == ExitFailure 1) $ throwIO exitCode pure $ length $ lines xs main :: IO () main = withTempDirectory "/tmp" "tmp-postgres-spec" $ \directoryPath -> forM_ [0, 10 .. 1000000] $ \theDelay -> do let theConfig = defaultConfig { temporaryDirectory = pure directoryPath , logger = pure print } initialContents <- listDirectory directoryPath initialPostgresCount <- countPostgresProcesses initialInitdbCount <- countInitdbProcesses initialCreatedbCount <- countCreatedbProcesses thread <- async $ withConfig theConfig $ flip withConn $ \conn -> forever $ do (_one :: Int) <- fmap (PG.fromOnly . head) $ PG.query_ conn "SELECT 1" threadDelay 1000 threadDelay theDelay throwTo (asyncThreadId thread) $ userError "stop already" void $ waitCatch thread finalContents <- listDirectory directoryPath finalPostgresCount <- countPostgresProcesses finalInitdbCount <- countInitdbProcesses finalCreatedbCount <- countCreatedbProcesses unless (initialPostgresCount == finalPostgresCount) $ throwIO $ userError $ unlines [ "failed to clean up postgres" , "had" , show initialPostgresCount , "initially and now have" , show finalPostgresCount ] unless (initialInitdbCount == finalInitdbCount) $ throwIO $ userError $ unlines [ "failed to clean up initdb" , "had" , show initialInitdbCount , "initially and now have" , show finalInitdbCount ] unless (initialCreatedbCount == finalCreatedbCount) $ throwIO $ userError $ unlines [ "failed to clean up createdb" , "had" , show initialCreatedbCount , "initially and now have" , show finalCreatedbCount ] unless (initialContents == finalContents) $ do renameDirectory directoryPath $ directoryPath <> "_failed" throwIO $ userError $ unlines [ "failed to clean up files" , "had" , unlines initialContents , "initially and now have" , unlines finalContents ]