Skip to content

Commit

Permalink
When running the shell on Windows, use cmd as the default. (#26)
Browse files Browse the repository at this point in the history
When running the shell on Windows, use `cmd` as the default.
  • Loading branch information
SamirTalwar authored Sep 30, 2019
2 parents 2fb6861 + 17c5874 commit 2137885
Show file tree
Hide file tree
Showing 13 changed files with 113 additions and 82 deletions.
19 changes: 9 additions & 10 deletions app/Test/Smoke/App/PrintErrors.hs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
module Test.Smoke.App.PrintErrors
( printError
, printDiscoveryError
, printExecutableError
, printPathError
, printSuiteError
, printTestError
) where
Expand All @@ -13,12 +13,12 @@ import Data.String (fromString)
import Data.Text (Text)
import Test.Smoke
import Test.Smoke.App.Print
import Test.Smoke.Paths

printSuiteError :: SuiteError -> Output ()
printSuiteError (SuiteDiscoveryError discoveryError) =
printDiscoveryError printError discoveryError
printSuiteError (SuiteExecutableError executableError) =
printExecutableError executableError
printSuiteError (SuitePathError pathError) = printPathError pathError

printTestError :: SmokeError -> Output ()
printTestError (DiscoveryError discoveryError) =
Expand All @@ -36,8 +36,8 @@ printTestError (PlanningError (CouldNotReadFixture path exception)) =
"The fixture " <> showPath path <> " could not be read."
printTestError (PlanningError (PlanningFilterError filterError)) =
printFilterError filterError
printTestError (PlanningError (PlanningExecutableError executableError)) =
printExecutableError executableError
printTestError (PlanningError (PlanningPathError pathError)) =
printPathError pathError
printTestError (ExecutionError (NonExistentWorkingDirectory (WorkingDirectory path))) =
printError $ "The working directory " <> showPath path <> " does not exist."
printTestError (ExecutionError (CouldNotExecuteCommand executable exception)) =
Expand Down Expand Up @@ -105,13 +105,12 @@ printFilterError (ExecutionFailed executable (Status status) (StdOut stdOut) (St
indentedAll messageIndentation stdOut <>
"\nSTDERR:\n" <>
indentedAll messageIndentation stdErr
printFilterError (FilterExecutableError executableError) =
printExecutableError executableError
printFilterError (FilterPathError pathError) = printPathError pathError

printExecutableError :: SmokeExecutableError -> Output ()
printExecutableError (CouldNotFindExecutable path) =
printPathError :: PathError -> Output ()
printPathError (CouldNotFindExecutable path) =
printError $ "The executable " <> showPath path <> " could not be found."
printExecutableError (FileIsNotExecutable path) =
printPathError (FileIsNotExecutable path) =
printError $ "The file at " <> showPath path <> " is not executable."

printError :: Text -> Output ()
Expand Down
12 changes: 4 additions & 8 deletions fixtures/broken-specs/smoke.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,12 @@ tests:
- name: no-command

- name: no-outputs
command:
- echo
command: echo
args:
- input

- name: missing-input-file
command:
- echo
command: echo
args:
- something
stdin:
Expand All @@ -18,16 +16,14 @@ tests:
output
- name: missing-output-file
command:
- echo
command: echo
args:
- something
stdout:
file: io/missing.out

- name: missing-error-file
command:
- echo
command: echo
args:
- something
stderr:
Expand Down
10 changes: 5 additions & 5 deletions fixtures/shell/local.yaml
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
tests:
- name: use the default shell
- name: use the default shell, on Unix or Windows
command: |
echo 'Something.' >&2
echo Something.>&2
false
echo 'Something else.' >&2
echo Something else.>&2
stderr: |
Something.
Something else.
Expand Down Expand Up @@ -41,7 +41,7 @@ tests:
stderr: |
Something else.
- name: pass args to a shell command
- name: pass args to the default shell command
command: |
echo $1 $2 $3
args:
Expand All @@ -53,7 +53,7 @@ tests:
stdout: |
a b c
- name: pass args to a shell command with a custom shell
- name: pass args to a custom shell command
command:
shell:
- ruby
Expand Down
8 changes: 8 additions & 0 deletions package.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,14 @@ library:
ghc-options:
- -Wall
- -Werror
when:
condition: os(windows)
then:
source-dirs:
- src/windows
else:
source-dirs:
- src/unix

executables:
smoke:
Expand Down
6 changes: 3 additions & 3 deletions spec/io/shell.out-unix
Original file line number Diff line number Diff line change
Expand Up @@ -2,17 +2,17 @@ global/runs the command with a custom shell
succeeded
global/runs the filter with a custom shell
succeeded
local/use the default shell
local/use the default shell, on Unix or Windows
succeeded
local/pipe STDIN to the script
succeeded
local/use custom shell flags
succeeded
local/use a custom shell
succeeded
local/pass args to a shell command
local/pass args to the default shell command
succeeded
local/pass args to a shell command with a custom shell
local/pass args to a custom shell command
succeeded
local/use a custom shell with an absolute path
succeeded
Expand Down
17 changes: 12 additions & 5 deletions spec/io/shell.out-windows
Original file line number Diff line number Diff line change
Expand Up @@ -2,17 +2,24 @@ global/runs the command with a custom shell
succeeded
global/runs the filter with a custom shell
succeeded
local/use the default shell
local/use the default shell, on Unix or Windows
succeeded
local/pipe STDIN to the script
succeeded
local/use custom shell flags
succeeded
local/use a custom shell
succeeded
local/pass args to a shell command
succeeded
local/pass args to a shell command with a custom shell
local/pass args to the default shell command
args: a
b
c
d
e
stdout: @@ -1 +1 @@
-a b c
+$1 $2 $3
local/pass args to a custom shell command
succeeded
local/use a custom shell with an absolute path
The executable "bin\sh" could not be found.
Expand All @@ -21,4 +28,4 @@ local/use a shell that doesn't exist
local/use a shell that isn't executable
The file at "fixtures\non_executable_application" is not executable.

11 tests, 3 failures
11 tests, 4 failures
46 changes: 8 additions & 38 deletions src/Test/Smoke/Executable.hs
Original file line number Diff line number Diff line change
@@ -1,36 +1,18 @@
{-# LANGUAGE OverloadedStrings #-}

module Test.Smoke.Executable where

import Control.Monad (unless)
import Control.Monad.IO.Class (liftIO)
import Control.Monad.Trans.Except (ExceptT, throwE)
import Control.Monad.Trans.Except (ExceptT)
import Data.Text (Text)
import qualified Data.Text.IO as Text.IO
import qualified Data.Vector as Vector
import Data.Vector (Vector)
import qualified System.Directory as Directory
import System.Exit (ExitCode)
import System.IO (hClose)
import System.IO.Temp (withSystemTempFile)
import System.Process (CreateProcess(..), proc)
import System.Process.Text (readCreateProcessWithExitCode)
import Test.Smoke.Paths
import Test.Smoke.Shell
import Test.Smoke.Types

defaultShell :: ExceptT SmokeExecutableError IO Shell
defaultShell = do
sh <- findExecutable $ parseFile "sh"
return $ Shell sh mempty

defaultShellExecute :: Vector String
defaultShellExecute = Vector.fromList ["sh", "-c"]

shellFromCommandLine :: CommandLine -> ExceptT SmokeExecutableError IO Shell
shellFromCommandLine (CommandLine shellName shellArgs) = do
shellCommand <- findExecutable shellName
return $ Shell shellCommand shellArgs

runExecutable ::
Executable
-> Args
Expand All @@ -45,7 +27,7 @@ runExecutable (ExecutableProgram executablePath executableArgs) args (StdIn stdI
{cwd = toFilePath . unWorkingDirectory <$> workingDirectory})
stdIn
runExecutable (ExecutableScript (Shell shellPath shellArgs) (Script script)) args stdIn workingDirectory =
withSystemTempFile "smoke.sh" $ \scriptPath scriptHandle -> do
withSystemTempFile defaultShellScriptName $ \scriptPath scriptHandle -> do
Text.IO.hPutStr scriptHandle script
hClose scriptHandle
let executableArgs = shellArgs <> Args (Vector.singleton scriptPath)
Expand All @@ -56,7 +38,7 @@ runExecutable (ExecutableScript (Shell shellPath shellArgs) (Script script)) arg
workingDirectory

convertCommandToExecutable ::
Maybe Shell -> Command -> ExceptT SmokeExecutableError IO Executable
Maybe Shell -> Command -> ExceptT PathError IO Executable
convertCommandToExecutable _ (CommandArgs (CommandLine executableName commandArgs)) = do
executablePath <- findExecutable executableName
return $ ExecutableProgram executablePath commandArgs
Expand All @@ -69,19 +51,7 @@ convertCommandToExecutable _ (CommandScript (Just commandLine) script) = do
shell <- shellFromCommandLine commandLine
return $ ExecutableScript shell script

findExecutable ::
RelativePath File -> ExceptT SmokeExecutableError IO (ResolvedPath File)
findExecutable path = do
exists <- liftIO $ Directory.doesFileExist (toFilePath path)
if exists
then do
permissions <- liftIO $ Directory.getPermissions (toFilePath path)
unless (Directory.executable permissions) $
throwE $ FileIsNotExecutable path
liftIO $ resolve path
else do
executable <- liftIO $ Directory.findExecutable (toFilePath path)
maybe
(throwE $ CouldNotFindExecutable path)
(liftIO . resolve . parseFile)
executable
shellFromCommandLine :: CommandLine -> ExceptT PathError IO Shell
shellFromCommandLine (CommandLine shellName shellArgs) = do
shellCommand <- findExecutable shellName
return $ Shell shellCommand shellArgs
2 changes: 1 addition & 1 deletion src/Test/Smoke/Filters.hs
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ applyFiltersFromFixtures fallbackShell (Fixtures fixtures) value =
runFilter :: FixtureType a => Maybe Shell -> Command -> a -> Filtering a
runFilter fallbackShell command value = do
executable <-
withExceptT FilterExecutableError $
withExceptT FilterPathError $
convertCommandToExecutable fallbackShell command
(exitCode, processStdOut, processStdErr) <-
withExceptT (CouldNotExecuteFilter executable) $
Expand Down
30 changes: 30 additions & 0 deletions src/Test/Smoke/Paths.hs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,9 @@ module Test.Smoke.Paths
, Path
, RelativePath
, ResolvedPath
, PathError(..)
, (</>)
, findExecutable
, findFilesInPath
, getCurrentWorkingDirectory
, parent
Expand All @@ -20,6 +22,10 @@ module Test.Smoke.Paths
, writeToPath
) where

import Control.Exception (Exception)
import Control.Monad (unless)
import Control.Monad.IO.Class (liftIO)
import Control.Monad.Trans.Except (ExceptT, throwE)
import Data.Aeson
import Data.Text (Text)
import qualified Data.Text as Text
Expand Down Expand Up @@ -126,6 +132,22 @@ resolve path = do
getCurrentWorkingDirectory :: IO (ResolvedPath Dir)
getCurrentWorkingDirectory = ResolvedPath <$> Directory.getCurrentDirectory

findExecutable :: RelativePath File -> ExceptT PathError IO (ResolvedPath File)
findExecutable path = do
exists <- liftIO $ Directory.doesFileExist (toFilePath path)
if exists
then do
permissions <- liftIO $ Directory.getPermissions (toFilePath path)
unless (Directory.executable permissions) $
throwE $ FileIsNotExecutable path
liftIO $ resolve path
else do
executable <- liftIO $ Directory.findExecutable (toFilePath path)
maybe
(throwE $ CouldNotFindExecutable path)
(liftIO . resolve . parseFile)
executable

-- Search
findFilesInPath ::
(Path p Dir, Path p File) => Glob.Pattern -> p Dir -> IO [p File]
Expand All @@ -138,3 +160,11 @@ readFromPath = Text.IO.readFile . toFilePath

writeToPath :: ResolvedPath File -> Text -> IO ()
writeToPath = Text.IO.writeFile . toFilePath

-- Errors
data PathError
= CouldNotFindExecutable (RelativePath File)
| FileIsNotExecutable (RelativePath File)
deriving (Eq, Show)

instance Exception PathError
4 changes: 2 additions & 2 deletions src/Test/Smoke/Plan.hs
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ planTests (TestSpecification specificationCommand suites) = do
runExceptT $ mapM shellFromCommandLine thisSuiteShellCommandLine
case shell of
Left exception ->
return $ SuitePlanError suiteName $ SuiteExecutableError exception
return $ SuitePlanError suiteName $ SuitePathError exception
Right fallbackShell -> do
let fallbackWorkingDirectory =
fromMaybe currentWorkingDirectory thisSuiteWorkingDirectory
Expand Down Expand Up @@ -74,7 +74,7 @@ readTest location fallbackWorkingDirectory fallbackShell fallbackCommand test =
command <-
maybe (throwE NoCommand) return (testCommand test <|> fallbackCommand)
executable <-
withExceptT PlanningExecutableError $
withExceptT PlanningPathError $
convertCommandToExecutable fallbackShell command
let args = fromMaybe mempty (testArgs test)
unfilteredStdIn <-
Expand Down
13 changes: 3 additions & 10 deletions src/Test/Smoke/Types/Errors.hs
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ data SmokePlanningError
| NoOutput
| NonExistentFixture (RelativePath File)
| CouldNotReadFixture (RelativePath File) IOError
| PlanningExecutableError SmokeExecutableError
| PlanningPathError PathError
| PlanningFilterError SmokeFilterError
deriving (Eq, Show)

Expand Down Expand Up @@ -61,19 +61,12 @@ data SmokeFilterError
= MissingFilterScript
| CouldNotExecuteFilter Executable IOError
| ExecutionFailed Executable Status StdOut StdErr
| FilterExecutableError SmokeExecutableError
| FilterPathError PathError
deriving (Eq, Show)

instance Exception SmokeFilterError

data SmokeExecutableError
= CouldNotFindExecutable (RelativePath File)
| FileIsNotExecutable (RelativePath File)
deriving (Eq, Show)

instance Exception SmokeExecutableError

data SuiteError
= SuiteDiscoveryError SmokeDiscoveryError
| SuiteExecutableError SmokeExecutableError
| SuitePathError PathError
deriving (Eq, Show)
13 changes: 13 additions & 0 deletions src/unix/Test/Smoke/Shell.hs
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
module Test.Smoke.Shell where

import Control.Monad.Trans.Except (ExceptT)
import Test.Smoke.Paths
import Test.Smoke.Types

defaultShellScriptName :: String
defaultShellScriptName = "smoke.sh"

defaultShell :: ExceptT PathError IO Shell
defaultShell = do
sh <- findExecutable $ parseFile "sh"
return $ Shell sh mempty
Loading

0 comments on commit 2137885

Please sign in to comment.