module Test.Hspec.WebDriver.Internal(
TestCapabilities(..)
, createSessionManager
, createSessionManager'
, withCaps
) where
import Control.Concurrent.STM
import Control.Exception.Lifted (finally, onException)
import Control.Monad.IO.Class (liftIO)
import Data.Hashable
import Data.List (partition)
import Data.Maybe (fromJust)
import Data.Typeable
import Data.Word (Word16)
import System.IO.Unsafe (unsafePerformIO)
import Test.WebDriver
import Test.WebDriver.Classes
import qualified Data.HashMap.Lazy as M
class (Eq c, Enum c, Typeable c) => TestCapabilities c where
matchesCaps :: c -> Capabilities -> Bool
newCaps :: c -> WD Capabilities
instance TestCapabilities () where
matchesCaps () _ = False
newCaps () = error "Cannot create caps for ()"
data SomeCap = forall c. TestCapabilities c => SomeCap c
instance Eq SomeCap where
SomeCap c1 == SomeCap c2 = case cast c2 of
Just c2' -> c1 == c2'
Nothing -> False
instance Hashable SomeCap where
hashWithSalt i (SomeCap c) = hashUsing fromEnum i c
data ManagedSessions = ManagedSessions
{ maxSessions :: Int
, managedSessions :: M.HashMap SomeCap (TVar ([SessionId],Int))
, initialSessions :: [(SessionId,Capabilities)]
, mwdHost :: String
, mwdPort :: Word16
, mwdBasePath :: String
}
sessionManager :: TVar (Maybe ManagedSessions)
sessionManager = unsafePerformIO (newTVarIO Nothing)
createWdMan :: Int -> Maybe (String, Word16, String) -> WD ()
createWdMan maxSess mSettings = do
let (host,port,bpath) = case mSettings of
Just s -> s
Nothing -> (wdHost defaultSession, wdPort defaultSession, wdBasePath defaultSession)
sess <- sessions
let manager = ManagedSessions maxSess M.empty sess host port bpath
liftIO $ atomically $ do
mm <- readTVar sessionManager
case mm of
Just _ -> return ()
Nothing -> writeTVar sessionManager $ Just manager
createSessionManager :: Int
-> IO ()
createSessionManager maxSess = runWD defaultSession $ createWdMan maxSess Nothing
createSessionManager' :: Int
-> String
-> Word16
-> String
-> IO ()
createSessionManager' maxSess host port bpath = do
let sess = WDSession { wdHost = host
, wdPort = port
, wdBasePath = bpath
, wdSessId = Nothing
, lastHTTPRequest = Nothing
}
runWD sess $ createWdMan maxSess $ Just (host, port, bpath)
createSessionId :: SomeCap -> WD SessionId
createSessionId (SomeCap c) = do
caps <- newCaps c
sess <- createSession caps
return $ fromJust $ wdSessId sess
findSession :: SomeCap -> ManagedSessions -> STM (WD SessionId)
findSession sc@(SomeCap c) m =
case M.lookup sc $ managedSessions m of
Just tvar -> do
(sess,count) <- readTVar tvar
case sess of
(s:ss) -> do writeTVar tvar (ss, count + 1)
return $ return s
[] | count >= maxSessions m -> retry
| otherwise -> do writeTVar tvar ([], count + 1)
let create = createSessionId sc `onException` (liftIO $ atomically $ do
(s',cnt) <- readTVar tvar
writeTVar tvar (s', cnt 1))
return create
Nothing -> do
let (sess, unmanaged') = partition (\(_,cap) -> matchesCaps c cap) $ initialSessions m
tvar <- newTVar (map fst sess, 0)
let m' = m { initialSessions = unmanaged'
, managedSessions = M.insert sc tvar $ managedSessions m
}
writeTVar sessionManager $ Just m'
findSession sc m'
takeSession :: TestCapabilities s => s -> WD ()
takeSession s = do
msess <- liftIO $ atomically $ do
mm <- readTVar sessionManager
case mm of
Nothing -> return Nothing
Just m -> do r <- findSession (SomeCap s) m
return $ Just (r, mwdHost m, mwdPort m, mwdBasePath m)
case msess of
Just (r, host, port, bpath) -> do
let sess = WDSession { wdHost = host
, wdPort = port
, wdBasePath = bpath
, wdSessId = Nothing
, lastHTTPRequest = Nothing
}
putSession sess
sid <- r
putSession sess { wdSessId = Just sid }
Nothing -> do
createWdMan 1 Nothing
takeSession s
putSessionId :: TestCapabilities s => s -> SessionId -> WD ()
putSessionId s sid = liftIO $ atomically $ do
mm <- readTVar sessionManager
let m = maybe (error "Cannot put a session to an uninitialized manager") id mm
case M.lookup (SomeCap s) $ managedSessions m of
Nothing -> error "Cannot put a session to a cap that does not exist"
Just tvar -> do
(ss,cnt) <- readTVar tvar
writeTVar tvar (sid:ss,cnt1)
withCaps :: TestCapabilities s => s -> WD a -> WD a
withCaps tc test = do
takeSession tc
sess <- getSession
test `finally` putSessionId tc (fromJust $ wdSessId sess)