{-| Module : Test Description : Defines the Test datatype. Copyright : (c) Christopher Wells, 2016 License : MIT Maintainer : cwellsny@nycap.rr.com -} module Test.SystemTest.Test where import Control.Monad (mzero) import Data.Aeson import Data.Text import System.IO (hGetContents) import System.Process (CreateProcess(..), createProcess, shell, StdStream(CreatePipe), waitForProcess) -- | Represents a single test, with a name, a command, and an expected result. data Test = Test { name :: String -- ^ The name of the Test. , command :: String -- ^ The command the to be run for the Test. , expectedOutput :: String -- ^ The expected result of the output of the command. } -- | Shows the contents of the values of the Test. instance Show Test where show test = "Test {name = " ++ show (name test) ++ ", command = " ++ show (command test) ++ ", expectedOutput = " ++ show (expectedOutput test) ++ "}" -- | Converts JSON into Test. instance FromJSON Test where parseJSON (Object v) = Test <$> v .: pack "name" <*> v .: pack "command" <*> v .: pack "expectedOutput" parseJSON _ = mzero -- | Represents the results of a single test, including if it passed or failed, -- and the actual output. type TestResults = (Bool, String) {-| Runs the command of a Test and checks to see if the output of the command is the same as the expected output of the Test. If the result of running the command is equal to the expected result, then True will be returned. However, if the expected and actual results are not equal, then False will be returned. The actual output of the test is also returned, so that it can be printed out in the case where the test fails. >>> runTest (Test "Hello" "echo 'Hello, World!'" "Hello, World!") (True,"Hello, World!") >>> runTest (Test "Hello" "echo 'Goodbye, World!'" "Hello, World!") (False,"Goodbye, World!") -} runTest :: Test -> IO TestResults runTest (Test _ testCommand expected) = do (_, Just stdout, _, handler) <- createProcess (shell testCommand){ std_out = CreatePipe } _ <- waitForProcess handler actualNL <- hGetContents stdout let actual = if not $ Prelude.null actualNL then Prelude.init actualNL else actualNL let result = expected == actual return (result, actual) {-| Returns a String showing the results of the Test. Includes the name of the Test and if it passed or failed. >>> let test = Test "HelloTest" "echo 'Hello, World!'" "Hello, World!" >>> result <- runTest test >>> showResults test result "HelloTest: Passed" >>> let test = Test "GoodbyeTest" "echo 'Goodbye, World!'" "Hello, World!" >>> result <- runTest test >>> showResults test result "GoodbyeTest: Failed\n Expected: Hello, World!\n Actual: Goodbye, World!" -} showResults :: Test -> TestResults -> String showResults (Test testName _ _) (True, _) = testName ++ ": Passed" showResults (Test testName _ expected) (False, actual) = testName ++ ": Failed\n Expected: " ++ expected ++ "\n Actual: " ++ actual