packages feed

grapesy-1.0.0: interop/Interop/Client.hs

{-# LANGUAGE OverloadedStrings #-}

module Interop.Client (runInteropClient) where

import Control.Exception
import Control.Monad
import Data.IORef
import System.Exit
import System.Timeout

import Interop.Cmdline
import Interop.Util.ANSI
import Interop.Util.Exceptions

import Interop.Client.TestCase.EmptyUnary                qualified as EmptyUnary
import Interop.Client.TestCase.LargeUnary                qualified as LargeUnary
import Interop.Client.TestCase.ClientCompressedUnary     qualified as ClientCompressedUnary
import Interop.Client.TestCase.ServerCompressedUnary     qualified as ServerCompressedUnary
import Interop.Client.TestCase.ClientStreaming           qualified as ClientStreaming
import Interop.Client.TestCase.ClientCompressedStreaming qualified as ClientCompressedStreaming
import Interop.Client.TestCase.ServerStreaming           qualified as ServerStreaming
import Interop.Client.TestCase.ServerCompressedStreaming qualified as ServerCompressedStreaming
import Interop.Client.TestCase.PingPong                  qualified as PingPong
import Interop.Client.TestCase.EmptyStream               qualified as EmptyStream
import Interop.Client.TestCase.CustomMetadata            qualified as CustomMetadata
import Interop.Client.TestCase.StatusCodeAndMessage      qualified as StatusCodeAndMessage
import Interop.Client.TestCase.SpecialStatusMessage      qualified as SpecialStatusMessage
import Interop.Client.TestCase.UnimplementedMethod       qualified as UnimplementedMethod
import Interop.Client.TestCase.UnimplementedService      qualified as UnimplementedService
import Interop.Client.TestCase.CancelAfterBegin          qualified as CancelAfterBegin
import Interop.Client.TestCase.CancelAfterFirstResponse  qualified as CancelAfterFirstResponse
import Interop.Client.TestCase.TimeoutOnSleepingServer   qualified as TimeoutOnSleepingServer

{-------------------------------------------------------------------------------
  Top-level
-------------------------------------------------------------------------------}

runInteropClient :: Cmdline -> IO ()
runInteropClient cmdline = do
    stats <- newIORef initTestStats

    case cmdTestCase cmdline of
      Nothing ->
        forM_ [minBound .. maxBound] $ testCase cmdline stats
      Just test ->
        testCase cmdline stats test

    ranAllTests stats

{-------------------------------------------------------------------------------
  Running individual tests
-------------------------------------------------------------------------------}

runTest :: TestCase -> Cmdline -> IO ()
runTest TestEmptyUnary                = EmptyUnary.runTest
runTest TestLargeUnary                = LargeUnary.runTest
runTest TestClientCompressedUnary     = ClientCompressedUnary.runTest
runTest TestServerCompressedUnary     = ServerCompressedUnary.runTest
runTest TestClientStreaming           = ClientStreaming.runTest
runTest TestClientCompressedStreaming = ClientCompressedStreaming.runTest
runTest TestServerStreaming           = ServerStreaming.runTest
runTest TestServerCompressedStreaming = ServerCompressedStreaming.runTest
runTest TestPingPong                  = PingPong.runTest
runTest TestEmptyStream               = EmptyStream.runTest
runTest TestCustomMetadata            = CustomMetadata.runTest
runTest TestStatusCodeAndMessage      = StatusCodeAndMessage.runTest
runTest TestSpecialStatusMessage      = SpecialStatusMessage.runTest
runTest TestUnimplementedMethod       = UnimplementedMethod.runTest
runTest TestUnimplementedService      = UnimplementedService.runTest
runTest TestCancelAfterBegin          = CancelAfterBegin.runTest
runTest TestCancelAfterFirstResponse  = CancelAfterFirstResponse.runTest
runTest TestTimeoutOnSleepingServer   = TimeoutOnSleepingServer.runTest

skips :: Cmdline -> TestCase -> Bool
skips cmdline test = or [
      elem test (cmdSkipTest cmdline)
    , cmdSkipCompression cmdline && elem test [
          TestClientCompressedUnary
        , TestServerCompressedUnary
        , TestClientCompressedStreaming
        , TestServerCompressedStreaming
        ]
    , cmdSkipClientCompression cmdline && elem test [
          TestClientCompressedUnary
        , TestClientCompressedStreaming
        ]
    ]

testCase :: Cmdline -> IORef TestStats -> TestCase -> IO ()
testCase cmdline stats test = unless (skips cmdline test) $ do
    result <- try $ timeout (cmdTimeoutTest cmdline * 1_000_000) $
                runTest test cmdline
    case result of
      Right (Just ()) ->
        testOK stats test
      Right Nothing ->
        testFailed stats test "timeout"
      Left err | Just (TestSkipped reason) <- fromException err ->
        testSkipped stats test reason
      Left err ->
        testFailed stats test (displayException err)

{-------------------------------------------------------------------------------
  Test stats
-------------------------------------------------------------------------------}

data TestStats = TestStats {
      numSucceeded :: Int
    , numFailed    :: Int
    , numSkipped   :: Int
    }

initTestStats :: TestStats
initTestStats = TestStats {
      numSucceeded = 0
    , numFailed    = 0
    , numSkipped   = 0
    }

testOK :: IORef TestStats -> TestCase -> IO ()
testOK statsRef test = do
    putDocLn $ mconcat [
        Show test
      , ": "
      , Color Green "OK"
      ]
    modifyIORef statsRef $ \stats ->
      stats { numSucceeded = numSucceeded stats + 1 }

testFailed :: IORef TestStats -> TestCase -> String -> IO ()
testFailed statsRef test err = do
    putDocLn $ mconcat [
        Show test
      , ": "
      , Color Red "Failed: "
      , fromString err
      ]
    modifyIORef statsRef $ \stats ->
      stats { numFailed = numFailed stats + 1 }

testSkipped :: IORef TestStats -> TestCase -> String -> IO ()
testSkipped statsRef test reason = do
    putDocLn $ mconcat [
        Show test
      , ": "
      , Color Yellow "Skipped: "
      , fromString reason
      ]
    modifyIORef statsRef $ \stats ->
      stats { numSkipped = numSkipped stats + 1 }

ranAllTests :: IORef TestStats -> IO ()
ranAllTests statsRef = do
    stats <- readIORef statsRef

    when (numSucceeded stats + numFailed stats > 1 || numSkipped stats > 0) $
      putStrLn $ concat [
          show $ numSucceeded stats
        , " succeeded, "
        , show $ numFailed stats
        , " failed, "
        , show $ numSkipped stats
        , " skipped."
        ]

    when (numFailed stats > 0) $
      exitFailure