packages feed

jobqueue-0.1.5: test/JobQueue.hs

-- Copyright (c) Gree, Inc. 2013
-- License: MIT-style

{-# LANGUAGE TemplateHaskell #-}

module JobQueue (testJobQueue) where

import Control.Exception
import Test.Hspec
import Control.Monad
import System.Directory
import System.IO.Error (isDoesNotExistError)
import Control.Concurrent
import Control.Concurrent.Async
import System.IO

import Network.JobQueue

data JobEnv = JobEnv {
    jeHello      :: String
  } deriving (Eq, Show)

instance Env JobEnv where
instance Aux JobEnv where

data JobUnit = HelloStep | WorldStep deriving (Show, Read, Eq, Ord)

instance Unit JobUnit where
  getPriority _ju = 1
  getRecovery _ju = HelloStep

instance Desc JobUnit where

data Looping = Looping Int deriving (Show, Read, Eq, Ord)

instance Unit Looping where
  getPriority _ju = 1
  getRecovery _ju = (Looping 0)
  toBeLogged _ = False

instance Desc Looping where

testJobQueue :: String -> Spec
testJobQueue backend = do
  describe "job queue" $ do
    it "says hello" $ do
      let withJobQueue = buildJobQueue backend "/says_hello_1" $ do
            process $ \WorldStep -> commitIO (putStrLn "world") >> fin
            process $ \HelloStep -> do
              env <- getEnv
              commitIO (putStr $ (jeHello env) ++ ", ")
              next WorldStep
      withJobQueue $ \jq -> do
        scheduleJob jq HelloStep
        countJobQueue jq `shouldReturn` 1
      withJobQueue $ \jq -> do
        let loop = \env jq' -> do
              executeJob jq' env
              count <- countJobQueue jq'
              when (count > 0) $ loop env jq'
        loop (JobEnv "hello") jq
        countJobQueue jq `shouldReturn` 0
    
    it "suspends" $ do
      let withJobQueue = buildJobQueue backend "/suspends_1" $ do
            process $ \WorldStep -> commitIO (putStrLn "world") >> fin
            process $ \HelloStep -> do
              env <- getEnv
              commitIO (putStr $ (jeHello env) ++ ", ")
              next WorldStep
      withJobQueue $ \jq -> do
        scheduleJob jq HelloStep
        suspendJobQueue jq `shouldReturn` True
        suspendJobQueue jq `shouldReturn` False
        step (JobEnv "hello") jq 5
        countJobQueue jq `shouldReturn` 2
      withJobQueue $ \jq -> do
        resumeJobQueue jq `shouldReturn` True
        step (JobEnv "hello") jq 5
        countJobQueue jq `shouldReturn` 0

    it "can be used concurrently" $ do
      let p = process $ \(Looping count) -> do
                if count > 0
                  then liftIO (hPutStr stderr $ " " ++ show count) >> fork (Looping (count - 1))
                  else liftIO (hPutStrLn stderr ".") >> fin
          env0 = (JobEnv "hello")
      buildJobQueue backend "/concurrently_1" p $ \jq -> do
        scheduleJob jq (Looping 100)
        countJobQueue jq `shouldReturn` 1
      bracket (openSession backend) (closeSession) $ \session -> do
        let loop = \env jq' -> do
                        executeJob jq' env
                        count <- countJobQueue jq'
                        when (count > 0) $ loop env jq'
        _ <- flip mapConcurrently [1..50] $ \_ -> do
          jq <- openJobQueue session "/concurrently_1" p
          loop env0 jq
          closeJobQueue jq
        return ()
      buildJobQueue backend "/concurrently_1" p $ \jq -> do
        executeJob jq env0
        countJobQueue jq `shouldReturn` 0
      return ()

---------------------------------------------------------------- Utils

step :: (Aux e, Env e, Unit a) => e -> JobQueue e a -> Int -> IO ()
step env jq c
  | c > 0 = do
    executeJob jq env
    step env jq (pred c)
  | otherwise = return ()