Skip to content

Commit 007da91

Browse files
committed
refactor: split off StackTest.Repl
1 parent 8e99af9 commit 007da91

File tree

6 files changed

+113
-93
lines changed

6 files changed

+113
-93
lines changed

stack.cabal

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -629,6 +629,7 @@ executable stack-integration-test
629629
main-is: IntegrationSpec.hs
630630
other-modules:
631631
StackTest
632+
StackTest.Repl
632633
Paths_stack
633634
autogen-modules:
634635
Paths_stack

tests/integration/lib/StackTest.hs

Lines changed: 5 additions & 91 deletions
Original file line numberDiff line numberDiff line change
@@ -13,14 +13,6 @@ module StackTest
1313
, stackCleanFull
1414
, stackIgnoreException
1515
, stackErr
16-
, Repl
17-
, ReplConnection (..)
18-
, nextPrompt
19-
, replCommand
20-
, replGetChar
21-
, replGetLine
22-
, runRepl
23-
, repl
2416
, stackStderr
2517
, stackCheckStderr
2618
, stackErrStderr
@@ -49,17 +41,11 @@ module StackTest
4941
, superslow
5042
) where
5143

52-
import Control.Monad ( forever, unless, void, when )
53-
import Control.Monad.IO.Class ( liftIO )
54-
import Control.Monad.Trans.Reader ( ReaderT, ask, asks, runReaderT )
55-
import qualified Control.Monad.Trans.State as State
56-
import Control.Monad.Trans ( lift )
57-
import Control.Concurrent ( forkIO )
44+
import Control.Monad ( unless, void, when )
5845
import Control.Exception
59-
( Exception (..), IOException, bracket_, catch, throw
46+
( Exception (..), IOException, bracket_, catch
6047
, throwIO
6148
)
62-
import Data.Maybe ( fromMaybe )
6349
import GHC.Stack ( HasCallStack )
6450
import System.Environment ( getEnv, lookupEnv )
6551
import System.Directory
@@ -68,14 +54,12 @@ import System.Directory
6854
, setCurrentDirectory
6955
)
7056
import System.IO
71-
( BufferMode (..), Handle, IOMode (..), hGetChar, hGetLine
72-
, hPutChar, hPutStr, hPutStrLn, hSetBuffering, stderr
73-
, withFile
57+
( hPutStr, hPutStrLn, stderr
7458
)
7559
import System.IO.Error
76-
( isDoesNotExistError, isEOFError )
60+
( isDoesNotExistError )
7761
import System.Process
78-
( CreateProcess (..), StdStream (..), createProcess, proc
62+
( CreateProcess (..), createProcess, proc
7963
, readCreateProcessWithExitCode, readProcessWithExitCode
8064
, shell, waitForProcess
8165
)
@@ -151,76 +135,6 @@ stackErr args = do
151135
ec <- stack' args
152136
when (ec == ExitSuccess) $ error "stack was supposed to fail, but didn't"
153137

154-
type Repl = ReaderT ReplConnection IO
155-
156-
data ReplConnection = ReplConnection
157-
{ replStdin :: Handle
158-
, replStdout :: Handle
159-
}
160-
161-
nextPrompt :: Repl ()
162-
nextPrompt = State.evalStateT poll "" where
163-
poll = do
164-
c <- lift (asks replStdout) >>= liftIO . hGetChar
165-
State.modify (++ [c]) -- FIXME crap perf
166-
when (c == '\n') $ do
167-
State.get >>= liftIO . putStr . ("ghci> " <>)
168-
State.put ""
169-
buf <- State.get
170-
unless (buf == "ghci> ")
171-
poll
172-
173-
replCommand :: String -> Repl ()
174-
replCommand cmd = do
175-
(ReplConnection replStdinHandle _) <- ask
176-
liftIO $ hPutStrLn replStdinHandle cmd
177-
178-
replGetLine :: Repl String
179-
replGetLine = ask >>= liftIO . hGetLine . replStdout
180-
181-
replGetChar :: Repl Char
182-
replGetChar = ask >>= liftIO . hGetChar . replStdout
183-
184-
runRepl ::
185-
HasCallStack
186-
=> FilePath
187-
-> [String]
188-
-> ReaderT ReplConnection IO ()
189-
-> IO ExitCode
190-
runRepl cmd args actions = do
191-
logInfo $ "Running: " ++ cmd ++ " " ++ unwords (map showProcessArgDebug args)
192-
(Just rStdin, Just rStdout, Just rStderr, ph) <-
193-
createProcess (proc cmd args)
194-
{ std_in = CreatePipe
195-
, std_out = CreatePipe
196-
, std_err = CreatePipe
197-
}
198-
hSetBuffering rStdin NoBuffering
199-
hSetBuffering rStdout NoBuffering
200-
hSetBuffering rStderr NoBuffering
201-
-- Log stack repl's standard error output
202-
tempDir <- if isWindows
203-
then fromMaybe "" <$> lookupEnv "TEMP"
204-
else pure "/tmp"
205-
let tempLogFile = tempDir ++ "/stderr"
206-
_ <- forkIO $ withFile tempLogFile WriteMode $ \logFileHandle -> do
207-
hSetBuffering logFileHandle NoBuffering
208-
forever $
209-
catch
210-
(hGetChar rStderr >>= hPutChar logFileHandle)
211-
(\e -> unless (isEOFError e) $ throw e)
212-
runReaderT actions (ReplConnection rStdin rStdout)
213-
waitForProcess ph
214-
215-
repl :: HasCallStack => [String] -> Repl () -> IO ()
216-
repl args action = do
217-
stackExe' <- stackExe
218-
ec <- runRepl stackExe' ("repl":args) action
219-
unless (ec == ExitSuccess) $ pure ()
220-
-- TODO: Understand why the exit code is 1 despite running GHCi tests
221-
-- successfully.
222-
-- else error $ "Exited with exit code: " ++ show ec
223-
224138
stackStderr :: HasCallStack => [String] -> IO (ExitCode, String)
225139
stackStderr args = do
226140
stackExe' <- stackExe
Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
1+
{- |
2+
Integration-test helpers & fixtures for testing `stack repl`
3+
-}
4+
module StackTest.Repl
5+
( Repl
6+
, ReplConnection (..)
7+
, nextPrompt
8+
, repl
9+
, replCommand
10+
, replGetLine
11+
) where
12+
13+
import Control.Concurrent (forkIO)
14+
import Control.Exception (throw, catch)
15+
import Control.Monad (forever, unless, when)
16+
import Control.Monad.IO.Class (liftIO)
17+
import Data.Maybe (fromMaybe)
18+
import GHC.Stack (HasCallStack)
19+
import System.Environment (lookupEnv)
20+
import System.Exit (ExitCode (..))
21+
import System.IO
22+
( BufferMode (NoBuffering), Handle, IOMode (WriteMode)
23+
, hGetChar, hGetLine, hPutChar, hPutStrLn, hSetBuffering
24+
, withFile
25+
)
26+
import System.IO.Error (isEOFError)
27+
28+
import Control.Monad.Trans (lift)
29+
import Control.Monad.Trans.Reader
30+
import Control.Monad.Trans.State qualified as State
31+
import System.Process
32+
( CreateProcess (std_err, std_in, std_out)
33+
, StdStream (CreatePipe)
34+
, createProcess, proc, waitForProcess
35+
)
36+
37+
import StackTest
38+
39+
type Repl = ReaderT ReplConnection IO
40+
41+
data ReplConnection = ReplConnection
42+
{ replStdin :: Handle
43+
, replStdout :: Handle
44+
}
45+
46+
nextPrompt :: Repl ()
47+
nextPrompt = State.evalStateT poll "" where
48+
poll = do
49+
c <- lift (asks replStdout) >>= liftIO . hGetChar
50+
State.modify (++ [c]) -- FIXME crap perf
51+
when (c == '\n') $ do
52+
State.get >>= liftIO . putStr . ("ghci> " <>)
53+
State.put ""
54+
buf <- State.get
55+
unless (buf == "ghci> ")
56+
poll
57+
58+
replCommand :: String -> Repl ()
59+
replCommand cmd = do
60+
(ReplConnection replStdinHandle _) <- ask
61+
liftIO $ hPutStrLn replStdinHandle cmd
62+
63+
replGetLine :: Repl String
64+
replGetLine = ask >>= liftIO . hGetLine . replStdout
65+
66+
runRepl
67+
:: HasCallStack
68+
=> FilePath
69+
-> [String]
70+
-> ReaderT ReplConnection IO ()
71+
-> IO ExitCode
72+
runRepl cmd args actions = do
73+
logInfo $ "Running: " ++ cmd ++ " " ++ unwords (map showProcessArgDebug args)
74+
(Just rStdin, Just rStdout, Just rStderr, ph) <-
75+
createProcess (proc cmd args)
76+
{ std_in = CreatePipe
77+
, std_out = CreatePipe
78+
, std_err = CreatePipe
79+
}
80+
hSetBuffering rStdin NoBuffering
81+
hSetBuffering rStdout NoBuffering
82+
hSetBuffering rStderr NoBuffering
83+
-- Log stack repl's standard error output
84+
tempDir <- if isWindows
85+
then fromMaybe "" <$> lookupEnv "TEMP"
86+
else pure "/tmp"
87+
let tempLogFile = tempDir ++ "/stderr"
88+
_ <- forkIO $ withFile tempLogFile WriteMode $ \logFileHandle -> do
89+
--hSetBuffering logFileHandle NoBuffering
90+
forever $
91+
catch
92+
(hGetChar rStderr >>= hPutChar logFileHandle)
93+
(\e -> unless (isEOFError e) $ throw e)
94+
runReaderT actions (ReplConnection rStdin rStdout)
95+
waitForProcess ph
96+
97+
repl :: HasCallStack => [String] -> Repl () -> IO ()
98+
repl args action = do
99+
stackExe' <- stackExe
100+
ec <- runRepl stackExe' ("repl":args) action
101+
unless (ec == ExitSuccess) $ pure ()
102+
-- TODO: Understand why the exit code is 1 despite running GHCi tests
103+
-- successfully.
104+
-- else error $ "Exited with exit code: " ++ show ec

tests/integration/tests/3926-ghci-with-sublibraries/Main.hs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import Control.Monad.IO.Class
22
import Control.Monad
33
import Data.List
44
import StackTest
5+
import StackTest.Repl
56

67
main :: IO ()
78
main

tests/integration/tests/4270-files-order/Main.hs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import Control.Monad
22
import StackTest
3+
import StackTest.Repl
34

45
main :: IO ()
56
main = do

tests/integration/tests/module-added-multiple-times/Main.hs

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
import Control.Monad
2-
import Data.List
3-
import StackTest
2+
import StackTest.Repl
43

54
main :: IO ()
65
main = repl ["--ghci-options=-ignore-dot-ghci"] $ do

0 commit comments

Comments
 (0)