Skip to content

Commit 2137885

Browse files
authored
When running the shell on Windows, use cmd as the default. (#26)
When running the shell on Windows, use `cmd` as the default.
2 parents 2fb6861 + 17c5874 commit 2137885

13 files changed

Lines changed: 113 additions & 82 deletions

File tree

app/Test/Smoke/App/PrintErrors.hs

Lines changed: 9 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
module Test.Smoke.App.PrintErrors
44
( printError
55
, printDiscoveryError
6-
, printExecutableError
6+
, printPathError
77
, printSuiteError
88
, printTestError
99
) where
@@ -13,12 +13,12 @@ import Data.String (fromString)
1313
import Data.Text (Text)
1414
import Test.Smoke
1515
import Test.Smoke.App.Print
16+
import Test.Smoke.Paths
1617

1718
printSuiteError :: SuiteError -> Output ()
1819
printSuiteError (SuiteDiscoveryError discoveryError) =
1920
printDiscoveryError printError discoveryError
20-
printSuiteError (SuiteExecutableError executableError) =
21-
printExecutableError executableError
21+
printSuiteError (SuitePathError pathError) = printPathError pathError
2222

2323
printTestError :: SmokeError -> Output ()
2424
printTestError (DiscoveryError discoveryError) =
@@ -36,8 +36,8 @@ printTestError (PlanningError (CouldNotReadFixture path exception)) =
3636
"The fixture " <> showPath path <> " could not be read."
3737
printTestError (PlanningError (PlanningFilterError filterError)) =
3838
printFilterError filterError
39-
printTestError (PlanningError (PlanningExecutableError executableError)) =
40-
printExecutableError executableError
39+
printTestError (PlanningError (PlanningPathError pathError)) =
40+
printPathError pathError
4141
printTestError (ExecutionError (NonExistentWorkingDirectory (WorkingDirectory path))) =
4242
printError $ "The working directory " <> showPath path <> " does not exist."
4343
printTestError (ExecutionError (CouldNotExecuteCommand executable exception)) =
@@ -105,13 +105,12 @@ printFilterError (ExecutionFailed executable (Status status) (StdOut stdOut) (St
105105
indentedAll messageIndentation stdOut <>
106106
"\nSTDERR:\n" <>
107107
indentedAll messageIndentation stdErr
108-
printFilterError (FilterExecutableError executableError) =
109-
printExecutableError executableError
108+
printFilterError (FilterPathError pathError) = printPathError pathError
110109

111-
printExecutableError :: SmokeExecutableError -> Output ()
112-
printExecutableError (CouldNotFindExecutable path) =
110+
printPathError :: PathError -> Output ()
111+
printPathError (CouldNotFindExecutable path) =
113112
printError $ "The executable " <> showPath path <> " could not be found."
114-
printExecutableError (FileIsNotExecutable path) =
113+
printPathError (FileIsNotExecutable path) =
115114
printError $ "The file at " <> showPath path <> " is not executable."
116115

117116
printError :: Text -> Output ()

fixtures/broken-specs/smoke.yaml

Lines changed: 4 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,12 @@ tests:
22
- name: no-command
33

44
- name: no-outputs
5-
command:
6-
- echo
5+
command: echo
76
args:
87
- input
98

109
- name: missing-input-file
11-
command:
12-
- echo
10+
command: echo
1311
args:
1412
- something
1513
stdin:
@@ -18,16 +16,14 @@ tests:
1816
output
1917
2018
- name: missing-output-file
21-
command:
22-
- echo
19+
command: echo
2320
args:
2421
- something
2522
stdout:
2623
file: io/missing.out
2724

2825
- name: missing-error-file
29-
command:
30-
- echo
26+
command: echo
3127
args:
3228
- something
3329
stderr:

fixtures/shell/local.yaml

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
tests:
2-
- name: use the default shell
2+
- name: use the default shell, on Unix or Windows
33
command: |
4-
echo 'Something.' >&2
4+
echo Something.>&2
55
false
6-
echo 'Something else.' >&2
6+
echo Something else.>&2
77
stderr: |
88
Something.
99
Something else.
@@ -41,7 +41,7 @@ tests:
4141
stderr: |
4242
Something else.
4343
44-
- name: pass args to a shell command
44+
- name: pass args to the default shell command
4545
command: |
4646
echo $1 $2 $3
4747
args:
@@ -53,7 +53,7 @@ tests:
5353
stdout: |
5454
a b c
5555
56-
- name: pass args to a shell command with a custom shell
56+
- name: pass args to a custom shell command
5757
command:
5858
shell:
5959
- ruby

package.yaml

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,14 @@ library:
4040
ghc-options:
4141
- -Wall
4242
- -Werror
43+
when:
44+
condition: os(windows)
45+
then:
46+
source-dirs:
47+
- src/windows
48+
else:
49+
source-dirs:
50+
- src/unix
4351

4452
executables:
4553
smoke:

spec/io/shell.out-unix

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,17 +2,17 @@ global/runs the command with a custom shell
22
succeeded
33
global/runs the filter with a custom shell
44
succeeded
5-
local/use the default shell
5+
local/use the default shell, on Unix or Windows
66
succeeded
77
local/pipe STDIN to the script
88
succeeded
99
local/use custom shell flags
1010
succeeded
1111
local/use a custom shell
1212
succeeded
13-
local/pass args to a shell command
13+
local/pass args to the default shell command
1414
succeeded
15-
local/pass args to a shell command with a custom shell
15+
local/pass args to a custom shell command
1616
succeeded
1717
local/use a custom shell with an absolute path
1818
succeeded

spec/io/shell.out-windows

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,17 +2,24 @@ global/runs the command with a custom shell
22
succeeded
33
global/runs the filter with a custom shell
44
succeeded
5-
local/use the default shell
5+
local/use the default shell, on Unix or Windows
66
succeeded
77
local/pipe STDIN to the script
88
succeeded
99
local/use custom shell flags
1010
succeeded
1111
local/use a custom shell
1212
succeeded
13-
local/pass args to a shell command
14-
succeeded
15-
local/pass args to a shell command with a custom shell
13+
local/pass args to the default shell command
14+
args: a
15+
b
16+
c
17+
d
18+
e
19+
stdout: @@ -1 +1 @@
20+
-a b c
21+
+$1 $2 $3
22+
local/pass args to a custom shell command
1623
succeeded
1724
local/use a custom shell with an absolute path
1825
The executable "bin\sh" could not be found.
@@ -21,4 +28,4 @@ local/use a shell that doesn't exist
2128
local/use a shell that isn't executable
2229
The file at "fixtures\non_executable_application" is not executable.
2330

24-
11 tests, 3 failures
31+
11 tests, 4 failures

src/Test/Smoke/Executable.hs

Lines changed: 8 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -1,36 +1,18 @@
1-
{-# LANGUAGE OverloadedStrings #-}
2-
31
module Test.Smoke.Executable where
42

5-
import Control.Monad (unless)
6-
import Control.Monad.IO.Class (liftIO)
7-
import Control.Monad.Trans.Except (ExceptT, throwE)
3+
import Control.Monad.Trans.Except (ExceptT)
84
import Data.Text (Text)
95
import qualified Data.Text.IO as Text.IO
106
import qualified Data.Vector as Vector
11-
import Data.Vector (Vector)
12-
import qualified System.Directory as Directory
137
import System.Exit (ExitCode)
148
import System.IO (hClose)
159
import System.IO.Temp (withSystemTempFile)
1610
import System.Process (CreateProcess(..), proc)
1711
import System.Process.Text (readCreateProcessWithExitCode)
1812
import Test.Smoke.Paths
13+
import Test.Smoke.Shell
1914
import Test.Smoke.Types
2015

21-
defaultShell :: ExceptT SmokeExecutableError IO Shell
22-
defaultShell = do
23-
sh <- findExecutable $ parseFile "sh"
24-
return $ Shell sh mempty
25-
26-
defaultShellExecute :: Vector String
27-
defaultShellExecute = Vector.fromList ["sh", "-c"]
28-
29-
shellFromCommandLine :: CommandLine -> ExceptT SmokeExecutableError IO Shell
30-
shellFromCommandLine (CommandLine shellName shellArgs) = do
31-
shellCommand <- findExecutable shellName
32-
return $ Shell shellCommand shellArgs
33-
3416
runExecutable ::
3517
Executable
3618
-> Args
@@ -45,7 +27,7 @@ runExecutable (ExecutableProgram executablePath executableArgs) args (StdIn stdI
4527
{cwd = toFilePath . unWorkingDirectory <$> workingDirectory})
4628
stdIn
4729
runExecutable (ExecutableScript (Shell shellPath shellArgs) (Script script)) args stdIn workingDirectory =
48-
withSystemTempFile "smoke.sh" $ \scriptPath scriptHandle -> do
30+
withSystemTempFile defaultShellScriptName $ \scriptPath scriptHandle -> do
4931
Text.IO.hPutStr scriptHandle script
5032
hClose scriptHandle
5133
let executableArgs = shellArgs <> Args (Vector.singleton scriptPath)
@@ -56,7 +38,7 @@ runExecutable (ExecutableScript (Shell shellPath shellArgs) (Script script)) arg
5638
workingDirectory
5739

5840
convertCommandToExecutable ::
59-
Maybe Shell -> Command -> ExceptT SmokeExecutableError IO Executable
41+
Maybe Shell -> Command -> ExceptT PathError IO Executable
6042
convertCommandToExecutable _ (CommandArgs (CommandLine executableName commandArgs)) = do
6143
executablePath <- findExecutable executableName
6244
return $ ExecutableProgram executablePath commandArgs
@@ -69,19 +51,7 @@ convertCommandToExecutable _ (CommandScript (Just commandLine) script) = do
6951
shell <- shellFromCommandLine commandLine
7052
return $ ExecutableScript shell script
7153

72-
findExecutable ::
73-
RelativePath File -> ExceptT SmokeExecutableError IO (ResolvedPath File)
74-
findExecutable path = do
75-
exists <- liftIO $ Directory.doesFileExist (toFilePath path)
76-
if exists
77-
then do
78-
permissions <- liftIO $ Directory.getPermissions (toFilePath path)
79-
unless (Directory.executable permissions) $
80-
throwE $ FileIsNotExecutable path
81-
liftIO $ resolve path
82-
else do
83-
executable <- liftIO $ Directory.findExecutable (toFilePath path)
84-
maybe
85-
(throwE $ CouldNotFindExecutable path)
86-
(liftIO . resolve . parseFile)
87-
executable
54+
shellFromCommandLine :: CommandLine -> ExceptT PathError IO Shell
55+
shellFromCommandLine (CommandLine shellName shellArgs) = do
56+
shellCommand <- findExecutable shellName
57+
return $ Shell shellCommand shellArgs

src/Test/Smoke/Filters.hs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ applyFiltersFromFixtures fallbackShell (Fixtures fixtures) value =
3535
runFilter :: FixtureType a => Maybe Shell -> Command -> a -> Filtering a
3636
runFilter fallbackShell command value = do
3737
executable <-
38-
withExceptT FilterExecutableError $
38+
withExceptT FilterPathError $
3939
convertCommandToExecutable fallbackShell command
4040
(exitCode, processStdOut, processStdErr) <-
4141
withExceptT (CouldNotExecuteFilter executable) $

src/Test/Smoke/Paths.hs

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,9 @@ module Test.Smoke.Paths
88
, Path
99
, RelativePath
1010
, ResolvedPath
11+
, PathError(..)
1112
, (</>)
13+
, findExecutable
1214
, findFilesInPath
1315
, getCurrentWorkingDirectory
1416
, parent
@@ -20,6 +22,10 @@ module Test.Smoke.Paths
2022
, writeToPath
2123
) where
2224

25+
import Control.Exception (Exception)
26+
import Control.Monad (unless)
27+
import Control.Monad.IO.Class (liftIO)
28+
import Control.Monad.Trans.Except (ExceptT, throwE)
2329
import Data.Aeson
2430
import Data.Text (Text)
2531
import qualified Data.Text as Text
@@ -126,6 +132,22 @@ resolve path = do
126132
getCurrentWorkingDirectory :: IO (ResolvedPath Dir)
127133
getCurrentWorkingDirectory = ResolvedPath <$> Directory.getCurrentDirectory
128134

135+
findExecutable :: RelativePath File -> ExceptT PathError IO (ResolvedPath File)
136+
findExecutable path = do
137+
exists <- liftIO $ Directory.doesFileExist (toFilePath path)
138+
if exists
139+
then do
140+
permissions <- liftIO $ Directory.getPermissions (toFilePath path)
141+
unless (Directory.executable permissions) $
142+
throwE $ FileIsNotExecutable path
143+
liftIO $ resolve path
144+
else do
145+
executable <- liftIO $ Directory.findExecutable (toFilePath path)
146+
maybe
147+
(throwE $ CouldNotFindExecutable path)
148+
(liftIO . resolve . parseFile)
149+
executable
150+
129151
-- Search
130152
findFilesInPath ::
131153
(Path p Dir, Path p File) => Glob.Pattern -> p Dir -> IO [p File]
@@ -138,3 +160,11 @@ readFromPath = Text.IO.readFile . toFilePath
138160

139161
writeToPath :: ResolvedPath File -> Text -> IO ()
140162
writeToPath = Text.IO.writeFile . toFilePath
163+
164+
-- Errors
165+
data PathError
166+
= CouldNotFindExecutable (RelativePath File)
167+
| FileIsNotExecutable (RelativePath File)
168+
deriving (Eq, Show)
169+
170+
instance Exception PathError

src/Test/Smoke/Plan.hs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ planTests (TestSpecification specificationCommand suites) = do
3232
runExceptT $ mapM shellFromCommandLine thisSuiteShellCommandLine
3333
case shell of
3434
Left exception ->
35-
return $ SuitePlanError suiteName $ SuiteExecutableError exception
35+
return $ SuitePlanError suiteName $ SuitePathError exception
3636
Right fallbackShell -> do
3737
let fallbackWorkingDirectory =
3838
fromMaybe currentWorkingDirectory thisSuiteWorkingDirectory
@@ -74,7 +74,7 @@ readTest location fallbackWorkingDirectory fallbackShell fallbackCommand test =
7474
command <-
7575
maybe (throwE NoCommand) return (testCommand test <|> fallbackCommand)
7676
executable <-
77-
withExceptT PlanningExecutableError $
77+
withExceptT PlanningPathError $
7878
convertCommandToExecutable fallbackShell command
7979
let args = fromMaybe mempty (testArgs test)
8080
unfilteredStdIn <-

0 commit comments

Comments
 (0)