Skip to content

Commit 3c19578

Browse files
committed
Use process createPipe, make compat variant compat.
If we need localeEncoding, that can be set outside. Also add rawSystemIOWithEnvAndAction to allow draining the spawned process output without async (in a simple case of single output Handle).
1 parent 36ab7a0 commit 3c19578

File tree

6 files changed

+125
-44
lines changed

6 files changed

+125
-44
lines changed

Cabal/Cabal.cabal

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -669,10 +669,12 @@ test-suite unit-tests
669669
main-is: UnitTests.hs
670670
build-depends:
671671
array,
672+
async >= 2.2.2 && <2.3,
672673
base,
673674
binary,
674675
bytestring,
675676
containers,
677+
deepseq,
676678
directory,
677679
filepath,
678680
integer-logarithms >= 1.0.2 && <1.1,

Cabal/Distribution/Compat/CreatePipe.hs

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,10 @@
44

55
module Distribution.Compat.CreatePipe (createPipe) where
66

7-
import System.IO (Handle, hSetEncoding, localeEncoding)
7+
#if MIN_VERSION_process(1,2,1)
8+
import System.Process (createPipe)
9+
#else
10+
import System.IO (Handle, hSetBinaryMode)
811

912
import Prelude ()
1013
import Distribution.Compat.Prelude
@@ -40,8 +43,8 @@ createPipe = do
4043
return (readfd, writefd)
4144
(do readh <- fdToHandle readfd ReadMode
4245
writeh <- fdToHandle writefd WriteMode
43-
hSetEncoding readh localeEncoding
44-
hSetEncoding writeh localeEncoding
46+
hSetBinaryMode readh True
47+
hSetBinaryMode writeh True
4548
return (readh, writeh)) `onException` (close readfd >> close writefd)
4649
where
4750
fdToHandle :: CInt -> IOMode -> IO Handle
@@ -69,9 +72,10 @@ createPipe = do
6972
(readfd, writefd) <- Posix.createPipe
7073
readh <- fdToHandle readfd
7174
writeh <- fdToHandle writefd
72-
hSetEncoding readh localeEncoding
73-
hSetEncoding writeh localeEncoding
75+
hSetBinaryMode readh True
76+
hSetBinaryMode writeh True
7477
return (readh, writeh)
7578
where
7679
_ = callStack
7780
#endif
81+
#endif

Cabal/Distribution/Simple/Test/ExeV10.hs

Lines changed: 34 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -27,12 +27,13 @@ import Distribution.TestSuite
2727
import Distribution.Pretty
2828
import Distribution.Verbosity
2929

30-
import Control.Concurrent (forkIO)
3130
import System.Directory
3231
( createDirectoryIfMissing, doesDirectoryExist, doesFileExist
3332
, getCurrentDirectory, removeDirectoryRecursive )
3433
import System.FilePath ( (</>), (<.>) )
35-
import System.IO ( hGetContents, stdout, stderr )
34+
import System.IO ( stdout, stderr )
35+
36+
import qualified Data.ByteString.Lazy as LBS
3637

3738
runTest :: PD.PackageDescription
3839
-> LBI.LocalBuildInfo
@@ -66,20 +67,6 @@ runTest pkg_descr lbi clbi flags suite = do
6667
-- Write summary notices indicating start of test suite
6768
notice verbosity $ summarizeSuiteStart $ testName'
6869

69-
(wOut, wErr, logText) <- case details of
70-
Direct -> return (stdout, stderr, "")
71-
_ -> do
72-
(rOut, wOut) <- createPipe
73-
74-
-- Read test executable's output lazily (returns immediately)
75-
logText <- hGetContents rOut
76-
-- Force the IO manager to drain the test output pipe
77-
void $ forkIO $ length logText `seq` return ()
78-
79-
-- '--show-details=streaming': print the log output in another thread
80-
when (details == Streaming) $ void $ forkIO $ putStr logText
81-
82-
return (wOut, wOut, logText)
8370

8471
-- Run the test executable
8572
let opts = map (testOption pkg_descr lbi suite)
@@ -97,14 +84,34 @@ runTest pkg_descr lbi clbi flags suite = do
9784
return (addLibraryPath os paths shellEnv)
9885
else return shellEnv
9986

100-
exit <- case testWrapper flags of
101-
Flag path -> rawSystemIOWithEnv verbosity path (cmd:opts) Nothing (Just shellEnv')
102-
-- these handles are automatically closed
103-
Nothing (Just wOut) (Just wErr)
87+
-- Output logger
88+
(wOut, wErr, getLogText) <- case details of
89+
Direct -> return (stdout, stderr, return LBS.empty)
90+
_ -> do
91+
(rOut, wOut) <- createPipe
92+
93+
return $ (,,) wOut wOut $ do
94+
-- Read test executables' output
95+
logText <- LBS.hGetContents rOut
96+
97+
-- '--show-details=streaming': print the log output in another thread
98+
when (details == Streaming) $ LBS.putStr logText
99+
100+
-- drain the output.
101+
evaluate (force logText)
102+
103+
(exit, logText) <- case testWrapper flags of
104+
Flag path -> rawSystemIOWithEnvAndAction
105+
verbosity path (cmd:opts) Nothing (Just shellEnv')
106+
getLogText
107+
-- these handles are automatically closed
108+
Nothing (Just wOut) (Just wErr)
104109

105-
NoFlag -> rawSystemIOWithEnv verbosity cmd opts Nothing (Just shellEnv')
106-
-- these handles are automatically closed
107-
Nothing (Just wOut) (Just wErr)
110+
NoFlag -> rawSystemIOWithEnvAndAction
111+
verbosity cmd opts Nothing (Just shellEnv')
112+
getLogText
113+
-- these handles are automatically closed
114+
Nothing (Just wOut) (Just wErr)
108115

109116
-- Generate TestSuiteLog from executable exit code and a machine-
110117
-- readable test log.
@@ -115,7 +122,7 @@ runTest pkg_descr lbi clbi flags suite = do
115122

116123
-- Append contents of temporary log file to the final human-
117124
-- readable log file
118-
appendFile (logFile suiteLog) logText
125+
LBS.appendFile (logFile suiteLog) logText
119126

120127
-- Write end-of-suite summary notice to log file
121128
appendFile (logFile suiteLog) $ summarizeSuiteFinish suiteLog
@@ -127,7 +134,9 @@ runTest pkg_descr lbi clbi flags suite = do
127134
details == Failures && not (suitePassed $ testLogs suiteLog))
128135
-- verbosity overrides show-details
129136
&& verbosity >= normal
130-
whenPrinting $ putStr $ unlines $ lines logText
137+
whenPrinting $ do
138+
LBS.putStr logText
139+
putChar '\n'
131140

132141
-- Write summary notice to terminal indicating end of test suite
133142
notice verbosity $ summarizeSuiteFinish suiteLog

Cabal/Distribution/Simple/Test/LibV09.hs

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -34,13 +34,14 @@ import Distribution.Pretty
3434
import Distribution.Verbosity
3535

3636
import qualified Control.Exception as CE
37+
import qualified Data.ByteString.Lazy as LBS
3738
import System.Directory
3839
( createDirectoryIfMissing, canonicalizePath
3940
, doesDirectoryExist, doesFileExist
4041
, getCurrentDirectory, removeDirectoryRecursive, removeFile
4142
, setCurrentDirectory )
4243
import System.FilePath ( (</>), (<.>) )
43-
import System.IO ( hClose, hGetContents, hPutStr )
44+
import System.IO ( hClose, hPutStr )
4445
import System.Process (StdStream(..), waitForProcess)
4546

4647
runTest :: PD.PackageDescription
@@ -78,6 +79,8 @@ runTest pkg_descr lbi clbi flags suite = do
7879

7980
suiteLog <- CE.bracket openCabalTemp deleteIfExists $ \tempLog -> do
8081

82+
-- TODO: this setup is broken,
83+
-- if the test output is too big, we will deadlock.
8184
(rOut, wOut) <- createPipe
8285

8386
-- Run test executable
@@ -112,9 +115,9 @@ runTest pkg_descr lbi clbi flags suite = do
112115

113116
-- Append contents of temporary log file to the final human-
114117
-- readable log file
115-
logText <- hGetContents rOut
118+
logText <- LBS.hGetContents rOut
116119
-- Force the IO manager to drain the test output pipe
117-
length logText `seq` return ()
120+
_ <- evaluate (force logText)
118121

119122
exitcode <- waitForProcess process
120123
unless (exitcode == ExitSuccess) $ do
@@ -134,7 +137,7 @@ runTest pkg_descr lbi clbi flags suite = do
134137
-- Write summary notice to log file indicating start of test suite
135138
appendFile (logFile suiteLog) $ summarizeSuiteStart testName'
136139

137-
appendFile (logFile suiteLog) logText
140+
LBS.appendFile (logFile suiteLog) logText
138141

139142
-- Write end-of-suite summary notice to log file
140143
appendFile (logFile suiteLog) $ summarizeSuiteFinish suiteLog
@@ -145,7 +148,9 @@ runTest pkg_descr lbi clbi flags suite = do
145148
whenPrinting = when $ (details > Never)
146149
&& (not (suitePassed $ testLogs suiteLog) || details == Always)
147150
&& verbosity >= normal
148-
whenPrinting $ putStr $ unlines $ lines logText
151+
whenPrinting $ do
152+
LBS.putStr logText
153+
putChar '\n'
149154

150155
return suiteLog
151156

@@ -158,7 +163,7 @@ runTest pkg_descr lbi clbi flags suite = do
158163
return suiteLog
159164
where
160165
testName' = unUnqualComponentName $ PD.testName suite
161-
166+
162167
deleteIfExists file = do
163168
exists <- doesFileExist file
164169
when exists $ removeFile file

Cabal/Distribution/Simple/Utils.hs

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@ module Distribution.Simple.Utils (
5050
rawSystemStdout,
5151
rawSystemStdInOut,
5252
rawSystemIOWithEnv,
53+
rawSystemIOWithEnvAndAction,
5354
createProcessWithEnv,
5455
maybeExit,
5556
xargs,
@@ -765,6 +766,29 @@ rawSystemIOWithEnv verbosity path args mcwd menv inp out err = withFrozenCallSta
765766
mbToStd :: Maybe Handle -> Process.StdStream
766767
mbToStd = maybe Process.Inherit Process.UseHandle
767768

769+
rawSystemIOWithEnvAndAction
770+
:: Verbosity
771+
-> FilePath
772+
-> [String]
773+
-> Maybe FilePath -- ^ New working dir or inherit
774+
-> Maybe [(String, String)] -- ^ New environment or inherit
775+
-> IO a -- ^ action to perform after process is created, but before 'waitForProcess'.
776+
-> Maybe Handle -- ^ stdin
777+
-> Maybe Handle -- ^ stdout
778+
-> Maybe Handle -- ^ stderr
779+
-> IO (ExitCode, a)
780+
rawSystemIOWithEnvAndAction verbosity path args mcwd menv action inp out err = withFrozenCallStack $ do
781+
(_,_,_,ph) <- createProcessWithEnv verbosity path args mcwd menv
782+
(mbToStd inp) (mbToStd out) (mbToStd err)
783+
a <- action
784+
exitcode <- waitForProcess ph
785+
unless (exitcode == ExitSuccess) $ do
786+
debug verbosity $ path ++ " returned " ++ show exitcode
787+
return (exitcode, a)
788+
where
789+
mbToStd :: Maybe Handle -> Process.StdStream
790+
mbToStd = maybe Process.Inherit Process.UseHandle
791+
768792
createProcessWithEnv ::
769793
Verbosity
770794
-> FilePath
Lines changed: 45 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,56 @@
11
module UnitTests.Distribution.Compat.CreatePipe (tests) where
22

3+
import Control.Concurrent.Async (async, wait)
4+
import Control.DeepSeq (force)
5+
import Control.Exception (evaluate)
6+
import System.IO (hClose, hGetContents, hPutStr, hSetEncoding, localeEncoding)
7+
import Test.Tasty (TestTree)
8+
import Test.Tasty.HUnit (Assertion, assertEqual, testCase)
9+
10+
import qualified Data.ByteString as BS
11+
312
import Distribution.Compat.CreatePipe
4-
import System.IO (hClose, hGetContents, hPutStr, hSetEncoding, localeEncoding)
5-
import Test.Tasty
6-
import Test.Tasty.HUnit
713

814
tests :: [TestTree]
9-
tests = [testCase "Locale Encoding" case_Locale_Encoding]
15+
tests =
16+
[ testCase "Locale Encoding" case_Locale_Encoding
17+
, testCase "Binary ByteStrings are not affected" case_ByteString
18+
]
1019

1120
case_Locale_Encoding :: Assertion
1221
case_Locale_Encoding = do
13-
let str = "\0252"
22+
let str = "\0252foobar"
1423
(r, w) <- createPipe
1524
hSetEncoding w localeEncoding
16-
out <- hGetContents r
17-
hPutStr w str
18-
hClose w
25+
hSetEncoding r localeEncoding
26+
27+
ra <- async $ do
28+
out <- hGetContents r
29+
evaluate (force out)
30+
31+
wa <- async $ do
32+
hPutStr w str
33+
hClose w
34+
35+
out <- wait ra
36+
wait wa
37+
1938
assertEqual "createPipe should support Unicode roundtripping" str out
39+
40+
case_ByteString :: Assertion
41+
case_ByteString = do
42+
let bs = BS.pack[ 1..255]
43+
(r, w) <- createPipe
44+
45+
ra <- async $ do
46+
out <- BS.hGetContents r
47+
evaluate (force out)
48+
49+
wa <- async $ do
50+
BS.hPutStr w bs
51+
hClose w
52+
53+
out <- wait ra
54+
wait wa
55+
56+
assertEqual "createPipe should support Unicode roundtripping" bs out

0 commit comments

Comments
 (0)