From ffeb000dd89964bd89ca0607345472f53c79159d Mon Sep 17 00:00:00 2001 From: David Binder <david.binder@uni-tuebingen.de> Date: Mon, 27 Nov 2023 21:05:49 +0100 Subject: [PATCH] Add tests from libraries/process/tests and libraries/Win32/tests to GHC These tests were previously part of the libraries, which themselves are submodules of the GHC repository. This commit moves the tests directly to the GHC repository. --- testsuite/tests/Win32/HandleConversion.hs | 16 +++ testsuite/tests/Win32/HandleConversion.stdout | 3 + testsuite/tests/Win32/Makefile | 7 ++ testsuite/tests/Win32/PokeTZI.hs | 13 +++ testsuite/tests/Win32/Semaphores.hs | 60 ++++++++++ testsuite/tests/Win32/Semaphores.stdout | 19 ++++ testsuite/tests/Win32/T4452.hs | 13 +++ testsuite/tests/Win32/all.T | 12 ++ testsuite/tests/Win32/helloworld.hs | 104 ++++++++++++++++++ testsuite/tests/Win32/lasterror.hs | 16 +++ testsuite/tests/Win32/registry001.hs | 16 +++ testsuite/tests/Win32/registry001.stdout | 1 + testsuite/tests/process/.gitignore | 31 ++++++ testsuite/tests/process/Makefile | 15 +++ testsuite/tests/process/T1780.hs | 19 ++++ testsuite/tests/process/T1780.stdout | 1 + testsuite/tests/process/T3231.hs | 22 ++++ testsuite/tests/process/T3231.stdout | 1 + testsuite/tests/process/T3994.hs | 22 ++++ testsuite/tests/process/T3994.stdout | 2 + testsuite/tests/process/T3994app.hs | 10 ++ testsuite/tests/process/T4198.hs | 3 + testsuite/tests/process/T4198.stdout | 1 + testsuite/tests/process/T4198.stdout-mingw32 | 1 + testsuite/tests/process/T4889.hs | 10 ++ testsuite/tests/process/T4889.stdout | 2 + testsuite/tests/process/T8343.hs | 8 ++ testsuite/tests/process/T8343.stdout | 2 + testsuite/tests/process/T9775/Makefile | 12 ++ testsuite/tests/process/T9775/T9775_fail.hs | 7 ++ .../tests/process/T9775/T9775_fail.stdout | 2 + testsuite/tests/process/T9775/T9775_good.hs | 7 ++ .../tests/process/T9775/T9775_good.stdout | 2 + testsuite/tests/process/T9775/all.T | 14 +++ testsuite/tests/process/T9775/main.c | 6 + testsuite/tests/process/T9775/ok.c | 8 ++ testsuite/tests/process/all.T | 53 +++++++++ testsuite/tests/process/exitminus1.c | 1 + testsuite/tests/process/process001.hs | 10 ++ testsuite/tests/process/process002.hs | 9 ++ testsuite/tests/process/process003.hs | 24 ++++ testsuite/tests/process/process003.stdout | 4 + testsuite/tests/process/process004.hs | 23 ++++ testsuite/tests/process/process004.stdout | 2 + ...process004.stdout-javascript-unknown-ghcjs | 2 + .../tests/process/process004.stdout-mingw32 | 2 + testsuite/tests/process/process005.hs | 26 +++++ testsuite/tests/process/process005.stdin | 3 + testsuite/tests/process/process005.stdout | 4 + testsuite/tests/process/process006.hs | 15 +++ testsuite/tests/process/process006.stderr | 1 + testsuite/tests/process/process006.stdout | 4 + testsuite/tests/process/process007.hs | 24 ++++ testsuite/tests/process/process007.stdout | 2 + testsuite/tests/process/process007_fd.c | 41 +++++++ testsuite/tests/process/process008.hs | 9 ++ testsuite/tests/process/process008.stdout | 2 + testsuite/tests/process/process009.hs | 24 ++++ testsuite/tests/process/process009.stdout | 3 + testsuite/tests/process/process010.hs | 13 +++ testsuite/tests/process/process010.stdout | 4 + .../process010.stdout-i386-unknown-solaris2 | 4 + ...process010.stdout-javascript-unknown-ghcjs | 4 + .../tests/process/process010.stdout-mingw32 | 4 + testsuite/tests/process/process011.hs | 73 ++++++++++++ testsuite/tests/process/process011.stdout | 12 ++ testsuite/tests/process/process011_c.c | 9 ++ testsuite/tests/process/processT251.hs | 39 +++++++ testsuite/tests/process/processT251.stdout | 6 + 69 files changed, 944 insertions(+) create mode 100644 testsuite/tests/Win32/HandleConversion.hs create mode 100644 testsuite/tests/Win32/HandleConversion.stdout create mode 100644 testsuite/tests/Win32/Makefile create mode 100644 testsuite/tests/Win32/PokeTZI.hs create mode 100644 testsuite/tests/Win32/Semaphores.hs create mode 100644 testsuite/tests/Win32/Semaphores.stdout create mode 100644 testsuite/tests/Win32/T4452.hs create mode 100644 testsuite/tests/Win32/all.T create mode 100644 testsuite/tests/Win32/helloworld.hs create mode 100644 testsuite/tests/Win32/lasterror.hs create mode 100644 testsuite/tests/Win32/registry001.hs create mode 100644 testsuite/tests/Win32/registry001.stdout create mode 100644 testsuite/tests/process/.gitignore create mode 100644 testsuite/tests/process/Makefile create mode 100644 testsuite/tests/process/T1780.hs create mode 100644 testsuite/tests/process/T1780.stdout create mode 100644 testsuite/tests/process/T3231.hs create mode 100644 testsuite/tests/process/T3231.stdout create mode 100644 testsuite/tests/process/T3994.hs create mode 100644 testsuite/tests/process/T3994.stdout create mode 100644 testsuite/tests/process/T3994app.hs create mode 100644 testsuite/tests/process/T4198.hs create mode 100644 testsuite/tests/process/T4198.stdout create mode 100644 testsuite/tests/process/T4198.stdout-mingw32 create mode 100644 testsuite/tests/process/T4889.hs create mode 100644 testsuite/tests/process/T4889.stdout create mode 100644 testsuite/tests/process/T8343.hs create mode 100644 testsuite/tests/process/T8343.stdout create mode 100644 testsuite/tests/process/T9775/Makefile create mode 100644 testsuite/tests/process/T9775/T9775_fail.hs create mode 100644 testsuite/tests/process/T9775/T9775_fail.stdout create mode 100644 testsuite/tests/process/T9775/T9775_good.hs create mode 100644 testsuite/tests/process/T9775/T9775_good.stdout create mode 100644 testsuite/tests/process/T9775/all.T create mode 100644 testsuite/tests/process/T9775/main.c create mode 100644 testsuite/tests/process/T9775/ok.c create mode 100644 testsuite/tests/process/all.T create mode 100644 testsuite/tests/process/exitminus1.c create mode 100644 testsuite/tests/process/process001.hs create mode 100644 testsuite/tests/process/process002.hs create mode 100644 testsuite/tests/process/process003.hs create mode 100644 testsuite/tests/process/process003.stdout create mode 100644 testsuite/tests/process/process004.hs create mode 100644 testsuite/tests/process/process004.stdout create mode 100644 testsuite/tests/process/process004.stdout-javascript-unknown-ghcjs create mode 100644 testsuite/tests/process/process004.stdout-mingw32 create mode 100644 testsuite/tests/process/process005.hs create mode 100644 testsuite/tests/process/process005.stdin create mode 100644 testsuite/tests/process/process005.stdout create mode 100644 testsuite/tests/process/process006.hs create mode 100644 testsuite/tests/process/process006.stderr create mode 100644 testsuite/tests/process/process006.stdout create mode 100644 testsuite/tests/process/process007.hs create mode 100644 testsuite/tests/process/process007.stdout create mode 100644 testsuite/tests/process/process007_fd.c create mode 100644 testsuite/tests/process/process008.hs create mode 100644 testsuite/tests/process/process008.stdout create mode 100644 testsuite/tests/process/process009.hs create mode 100644 testsuite/tests/process/process009.stdout create mode 100644 testsuite/tests/process/process010.hs create mode 100644 testsuite/tests/process/process010.stdout create mode 100644 testsuite/tests/process/process010.stdout-i386-unknown-solaris2 create mode 100644 testsuite/tests/process/process010.stdout-javascript-unknown-ghcjs create mode 100644 testsuite/tests/process/process010.stdout-mingw32 create mode 100644 testsuite/tests/process/process011.hs create mode 100644 testsuite/tests/process/process011.stdout create mode 100644 testsuite/tests/process/process011_c.c create mode 100644 testsuite/tests/process/processT251.hs create mode 100644 testsuite/tests/process/processT251.stdout diff --git a/testsuite/tests/Win32/HandleConversion.hs b/testsuite/tests/Win32/HandleConversion.hs new file mode 100644 index 000000000000..55483fe233e5 --- /dev/null +++ b/testsuite/tests/Win32/HandleConversion.hs @@ -0,0 +1,16 @@ +module Main where + +import Graphics.Win32.Misc +import System.IO +import System.Win32.Types + +testStdHandle :: Handle -> StdHandleId -> IO () +testStdHandle haskHandle winStdHandle = do + winHandle <- getStdHandle winStdHandle + withHandleToHANDLE haskHandle $ print . (== winHandle) + +main :: IO () +main = do + testStdHandle stdin sTD_INPUT_HANDLE + testStdHandle stdout sTD_OUTPUT_HANDLE + testStdHandle stderr sTD_ERROR_HANDLE diff --git a/testsuite/tests/Win32/HandleConversion.stdout b/testsuite/tests/Win32/HandleConversion.stdout new file mode 100644 index 000000000000..b8ca7e7ef092 --- /dev/null +++ b/testsuite/tests/Win32/HandleConversion.stdout @@ -0,0 +1,3 @@ +True +True +True diff --git a/testsuite/tests/Win32/Makefile b/testsuite/tests/Win32/Makefile new file mode 100644 index 000000000000..e17baf38bbf0 --- /dev/null +++ b/testsuite/tests/Win32/Makefile @@ -0,0 +1,7 @@ +# This Makefile runs the tests using GHC's testsuite framework. It +# assumes the package is part of a GHC build tree with the testsuite +# installed in ../../../testsuite. + +TOP=../.. +include $(TOP)/mk/boilerplate.mk +include $(TOP)/mk/test.mk diff --git a/testsuite/tests/Win32/PokeTZI.hs b/testsuite/tests/Win32/PokeTZI.hs new file mode 100644 index 000000000000..72e893ef4ab4 --- /dev/null +++ b/testsuite/tests/Win32/PokeTZI.hs @@ -0,0 +1,13 @@ +module Main where + +import Control.Exception (assert) +import Foreign +import System.Win32.Time + +main :: IO () +main = do + (_, tzi) <- getTimeZoneInformation + alloca $ \buf -> do + poke buf tzi + tzi' <- peek buf + assert (tzi == tzi') $ return () diff --git a/testsuite/tests/Win32/Semaphores.hs b/testsuite/tests/Win32/Semaphores.hs new file mode 100644 index 000000000000..bca08a1466fd --- /dev/null +++ b/testsuite/tests/Win32/Semaphores.hs @@ -0,0 +1,60 @@ +module Main where + +import Control.Concurrent + ( forkIO, threadDelay ) +import Control.Monad + ( void ) +import Data.Foldable + ( for_ ) + +import System.Win32.Event + ( waitForSingleObject ) +import System.Win32.File + ( closeHandle ) +import System.Win32.Semaphore + ( Semaphore(..), createSemaphore, releaseSemaphore ) + +main :: IO () +main = do + + (test_sem, ex1) <- mk_test_sem + (_, ex2) <- mk_test_sem + + let sem_name = "win32-test-semaphore" + (sem, ex3) <- createSemaphore Nothing 2 3 (Just sem_name) + + putStrLn (show ex1 ++ " " ++ show ex2 ++ " " ++ show ex3) + -- False True False + + putStrLn "==========" + for_ [1,2,3] (run_thread sem) + -- finish: 1, 2 + + putStrLn "==========" + void $ releaseSemaphore sem 3 + -- finish: 3 + + threadDelay 5000 -- 5 ms + for_ [4,5,6,7] (run_thread sem) + -- finish: 4, 5 + + threadDelay 1000 -- 1 ms + putStrLn "==========" + void $ releaseSemaphore sem 1 + -- finish: 6 + + threadDelay 100000 -- 100 ms + putStrLn "==========" + closeHandle (semaphoreHandle test_sem) + closeHandle (semaphoreHandle sem) + +run_thread :: Semaphore -> Int -> IO () +run_thread sem i = do + threadDelay 1000 -- 1 ms + putStrLn ("start " ++ show i) + void $ forkIO $ do + res <- waitForSingleObject (semaphoreHandle sem) 50 -- 50 ms + putStrLn ("finish " ++ show i ++ ": " ++ show res) + +mk_test_sem :: IO (Semaphore, Bool) +mk_test_sem = createSemaphore Nothing 1 1 (Just "test-sem") diff --git a/testsuite/tests/Win32/Semaphores.stdout b/testsuite/tests/Win32/Semaphores.stdout new file mode 100644 index 000000000000..e1541b06906d --- /dev/null +++ b/testsuite/tests/Win32/Semaphores.stdout @@ -0,0 +1,19 @@ +False True False +========== +start 1 +finish 1: 0 +start 2 +finish 2: 0 +start 3 +========== +finish 3: 0 +start 4 +finish 4: 0 +start 5 +finish 5: 0 +start 6 +start 7 +========== +finish 6: 0 +finish 7: 258 +========== diff --git a/testsuite/tests/Win32/T4452.hs b/testsuite/tests/Win32/T4452.hs new file mode 100644 index 000000000000..9a6793a23b85 --- /dev/null +++ b/testsuite/tests/Win32/T4452.hs @@ -0,0 +1,13 @@ +module Main where + +import Control.Monad +import Foreign.Ptr +import Graphics.Win32.GDI.Clip + +main = do + openClipboard nullPtr + go 0 + where + go n = do + n' <- enumClipboardFormats n + unless (n == 0) (go n') diff --git a/testsuite/tests/Win32/all.T b/testsuite/tests/Win32/all.T new file mode 100644 index 000000000000..dd2c48170493 --- /dev/null +++ b/testsuite/tests/Win32/all.T @@ -0,0 +1,12 @@ +test('registry001', normal, compile_and_run, ['']) + +# This isn't a very good test to run automatically at the moment, since +# it doesn't terminate +test('helloworld', skip, compile_and_run, ['-package lang -package win32']) + +test('lasterror', normal, compile_and_run, ['-package Win32']) +test('T4452', normal, compile_and_run, ['-package Win32']) +test('PokeTZI', ignore_stdout, compile_and_run, ['-package Win32']) +test('HandleConversion', normal, compile_and_run, ['-package Win32']) + +test('Semaphores', normal, compile_and_run, ['-threaded -package Win32']) diff --git a/testsuite/tests/Win32/helloworld.hs b/testsuite/tests/Win32/helloworld.hs new file mode 100644 index 000000000000..bfba551c340e --- /dev/null +++ b/testsuite/tests/Win32/helloworld.hs @@ -0,0 +1,104 @@ +-- Haskell version of "Hello, World" using the Win32 library. +-- Demonstrates how the Win32 library can be put to use. +-- (c) sof 1999 + + +module Main(main) where + +import qualified Win32 +import Addr + +-- Toplevel main just creates a window and pumps messages. +-- The window procedure (wndProc) we pass in is partially +-- applied with the user action that takes care of responding +-- to repaint messages (WM_PAINT). + +main :: IO () +main = do + lpps <- Win32.malloc Win32.sizeofPAINTSTRUCT + hwnd <- createWindow 200 200 (wndProc lpps onPaint) + messagePump hwnd + +-- OnPaint handler for a window - draw a string centred +-- inside it. +onPaint :: Win32.RECT -> Win32.HDC -> IO () +onPaint (_,_,w,h) hdc = do + Win32.setBkMode hdc Win32.tRANSPARENT + Win32.setTextColor hdc (Win32.rgb 255 255 0) + let y | h==10 = 0 + | otherwise = ((h-10) `div` 2) + x | w==50 = 0 + | otherwise = (w-50) `div` 2 + Win32.textOut hdc x y "Hello, world" + return () + +-- Simple window procedure - one way to improve and generalise +-- it would be to pass it a message map (represented as a +-- finite map from WindowMessages to actions, perhaps). + +wndProc :: Win32.LPPAINTSTRUCT + -> (Win32.RECT -> Win32.HDC -> IO ()) -- on paint action + -> Win32.HWND + -> Win32.WindowMessage + -> Win32.WPARAM + -> Win32.LPARAM + -> IO Win32.LRESULT +wndProc lpps onPaint hwnd wmsg wParam lParam + | wmsg == Win32.wM_DESTROY = do + Win32.sendMessage hwnd Win32.wM_QUIT 1 0 + return 0 + | wmsg == Win32.wM_PAINT && hwnd /= nullAddr = do + r <- Win32.getClientRect hwnd + paintWith lpps hwnd (onPaint r) + return 0 + | otherwise = + Win32.defWindowProc (Just hwnd) wmsg wParam lParam + +createWindow :: Int -> Int -> Win32.WindowClosure -> IO Win32.HWND +createWindow width height wndProc = do + let winClass = Win32.mkClassName "Hello" + icon <- Win32.loadIcon Nothing Win32.iDI_APPLICATION + cursor <- Win32.loadCursor Nothing Win32.iDC_ARROW + bgBrush <- Win32.createSolidBrush (Win32.rgb 0 0 255) + mainInstance <- Win32.getModuleHandle Nothing + Win32.registerClass + ( Win32.cS_VREDRAW + Win32.cS_HREDRAW + , mainInstance + , Just icon + , Just cursor + , Just bgBrush + , Nothing + , winClass + ) + w <- Win32.createWindow + winClass + "Hello, World example" + Win32.wS_OVERLAPPEDWINDOW + Nothing Nothing -- leave it to the shell to decide the position + -- at where to put the window initially + (Just width) + (Just height) + Nothing -- no parent, i.e, root window is the parent. + Nothing -- no menu handle + mainInstance + wndProc + Win32.showWindow w Win32.sW_SHOWNORMAL + Win32.updateWindow w + return w + +messagePump :: Win32.HWND -> IO () +messagePump hwnd = do + msg <- Win32.getMessage (Just hwnd) `catch` \ _ -> return nullAddr + if msg == nullAddr then + return () + else do + Win32.translateMessage msg + Win32.dispatchMessage msg + messagePump hwnd + +paintWith :: Win32.LPPAINTSTRUCT -> Win32.HWND -> (Win32.HDC -> IO a) -> IO a +paintWith lpps hwnd p = do + hdc <- Win32.beginPaint hwnd lpps + a <- p hdc + Win32.endPaint hwnd lpps + return a diff --git a/testsuite/tests/Win32/lasterror.hs b/testsuite/tests/Win32/lasterror.hs new file mode 100644 index 000000000000..21fcbae0ab80 --- /dev/null +++ b/testsuite/tests/Win32/lasterror.hs @@ -0,0 +1,16 @@ +{-# LANGUAGE ForeignFunctionInterface #-} +-- Test that the Win32 error code from getLastError is thread-local. + +import System.Win32 +import Control.Monad +import Control.Concurrent + +main = do + setLastError 42 + r <- getLastError + when (r /= 42) $ fail ("wrong: " ++ show r) + m <- newEmptyMVar + forkIO $ do setLastError 43; putMVar m () + takeMVar m + r <- getLastError + when (r /= 42) $ fail ("wrong: " ++ show r) diff --git a/testsuite/tests/Win32/registry001.hs b/testsuite/tests/Win32/registry001.hs new file mode 100644 index 000000000000..842221029f1d --- /dev/null +++ b/testsuite/tests/Win32/registry001.hs @@ -0,0 +1,16 @@ +import System.Win32 +import Control.Exception +import Control.Monad + +x = "bumble" +name = "test_registry001" + +-- Create, read, and delete a value (test for bug #3241) +main = do + k1 <- regCreateKey hKEY_CURRENT_USER "Software" + k2 <- regCreateKey k1 "Haskell" + k3 <- regCreateKey k2 "GHC" + flip finally (regDeleteValue k3 name) $ do + regSetStringValue k3 name x + r <- regQueryDefaultValue k3 name + print r diff --git a/testsuite/tests/Win32/registry001.stdout b/testsuite/tests/Win32/registry001.stdout new file mode 100644 index 000000000000..17a7bd97bb15 --- /dev/null +++ b/testsuite/tests/Win32/registry001.stdout @@ -0,0 +1 @@ +"bumble" diff --git a/testsuite/tests/process/.gitignore b/testsuite/tests/process/.gitignore new file mode 100644 index 000000000000..73f38bb5de1a --- /dev/null +++ b/testsuite/tests/process/.gitignore @@ -0,0 +1,31 @@ +.hpc*/ +*.o +*.hi +*.comp.std* +*.run.std* +*.eventlog +*.genscript +*.exe + +# specific files +/T1780 +/T3231 +/T3994 +/T4198 +/T4889 +/T8343 +/process001 +/process001.out +/process002 +/process002.out +/process003 +/process004 +/process005 +/process006 +/process007 +/process007.tmp +/process007_fd +/process008 +/process009 +/process010 +/process011 diff --git a/testsuite/tests/process/Makefile b/testsuite/tests/process/Makefile new file mode 100644 index 000000000000..e842a110d08d --- /dev/null +++ b/testsuite/tests/process/Makefile @@ -0,0 +1,15 @@ +# This Makefile runs the tests using GHC's testsuite framework. It +# assumes the package is part of a GHC build tree with the testsuite +# installed in ../../../testsuite. + +TOP=../.. +include $(TOP)/mk/boilerplate.mk +include $(TOP)/mk/test.mk + +.PHONY: process007_fd +process007_fd: + '$(TEST_HC)' -optc='-Wall' -no-hs-main -no-auto-link-packages process007_fd.c -o process007_fd + +.PHONY: T3994app +T3994app: + '$(TEST_HC)' $(TEST_HC_OPTS) T3994app.hs -threaded diff --git a/testsuite/tests/process/T1780.hs b/testsuite/tests/process/T1780.hs new file mode 100644 index 000000000000..b56721fa5caf --- /dev/null +++ b/testsuite/tests/process/T1780.hs @@ -0,0 +1,19 @@ +module Main where + +import Control.Concurrent +import System.IO +import System.Process + +launch :: String -> IO String +launch i = do (hin,hout,herr,ph) <- runInteractiveProcess "cat" [] Nothing Nothing + -- forkIO $ collect ph -- This doesn't seem to be relevant to the problem. + forkIO $ do hPutStr hin i + hClose hin + hGetContents hout + +main :: IO () +main = do o <- foldl (>>=) (return "foo") (replicate 5 launch) + t <- myThreadId + -- timeout + forkIO $ do threadDelay 5000000; killThread t + putStrLn o diff --git a/testsuite/tests/process/T1780.stdout b/testsuite/tests/process/T1780.stdout new file mode 100644 index 000000000000..257cc5642cb1 --- /dev/null +++ b/testsuite/tests/process/T1780.stdout @@ -0,0 +1 @@ +foo diff --git a/testsuite/tests/process/T3231.hs b/testsuite/tests/process/T3231.hs new file mode 100644 index 000000000000..e6b9859d1120 --- /dev/null +++ b/testsuite/tests/process/T3231.hs @@ -0,0 +1,22 @@ +module Main (main) where + +import Control.Concurrent +import System.IO +import System.Cmd +import System.Directory + +main = do + hSetBuffering stdout NoBuffering + forkIO $ f "foo1.txt" + forkIO $ f "foo2.txt" + threadDelay $ 2*1000000 + putStrLn "Finished successfully" + +f file = do + h <- openFile file WriteMode + hPutStrLn h "fjkladsf" + system "sleep 1" + -- putChar '.' + hClose h + removeFile file + f file diff --git a/testsuite/tests/process/T3231.stdout b/testsuite/tests/process/T3231.stdout new file mode 100644 index 000000000000..c34ed5b83ff0 --- /dev/null +++ b/testsuite/tests/process/T3231.stdout @@ -0,0 +1 @@ +Finished successfully diff --git a/testsuite/tests/process/T3994.hs b/testsuite/tests/process/T3994.hs new file mode 100644 index 000000000000..78ba977cd574 --- /dev/null +++ b/testsuite/tests/process/T3994.hs @@ -0,0 +1,22 @@ +module Main where + +import Control.Concurrent +import System.IO +import System.Process + +main :: IO () +main = do (_,Just hout,_,p) <- createProcess (proc "./T3994app" ["start", "10000"]) + { std_out = CreatePipe, create_group = True } + start <- hGetLine hout + putStrLn start + interruptProcessGroupOf p + t <- myThreadId + -- timeout + forkIO $ do + threadDelay 5000000 + putStrLn "Interrupting a Running Process Failed" + hFlush stdout + killThread t + waitForProcess p + putStrLn "end" + return () diff --git a/testsuite/tests/process/T3994.stdout b/testsuite/tests/process/T3994.stdout new file mode 100644 index 000000000000..5d0fb3b2d2ed --- /dev/null +++ b/testsuite/tests/process/T3994.stdout @@ -0,0 +1,2 @@ +start +end diff --git a/testsuite/tests/process/T3994app.hs b/testsuite/tests/process/T3994app.hs new file mode 100644 index 000000000000..09e574ffc5ef --- /dev/null +++ b/testsuite/tests/process/T3994app.hs @@ -0,0 +1,10 @@ +module Main where + +import Control.Concurrent +import System.Environment + +main :: IO () +main = do (str:time:_) <- getArgs + putStrLn str + threadDelay (read time) + return () diff --git a/testsuite/tests/process/T4198.hs b/testsuite/tests/process/T4198.hs new file mode 100644 index 000000000000..003b70e20aac --- /dev/null +++ b/testsuite/tests/process/T4198.hs @@ -0,0 +1,3 @@ +import System.Process +import System.FilePath +main = system ("." </> "exitminus1") >>= print diff --git a/testsuite/tests/process/T4198.stdout b/testsuite/tests/process/T4198.stdout new file mode 100644 index 000000000000..daf2f5f2d83c --- /dev/null +++ b/testsuite/tests/process/T4198.stdout @@ -0,0 +1 @@ +ExitFailure 255 diff --git a/testsuite/tests/process/T4198.stdout-mingw32 b/testsuite/tests/process/T4198.stdout-mingw32 new file mode 100644 index 000000000000..6af223b76249 --- /dev/null +++ b/testsuite/tests/process/T4198.stdout-mingw32 @@ -0,0 +1 @@ +ExitFailure (-1) diff --git a/testsuite/tests/process/T4889.hs b/testsuite/tests/process/T4889.hs new file mode 100644 index 000000000000..d8feb4760ee5 --- /dev/null +++ b/testsuite/tests/process/T4889.hs @@ -0,0 +1,10 @@ +module Main where + +import System.Process + +main :: IO () +main = do + let text = unlines . map show $ [1..10000 :: Int] + (code, out, _) <- readProcessWithExitCode "head" ["-n", "1"] text + print code + putStr out diff --git a/testsuite/tests/process/T4889.stdout b/testsuite/tests/process/T4889.stdout new file mode 100644 index 000000000000..d72cac55b230 --- /dev/null +++ b/testsuite/tests/process/T4889.stdout @@ -0,0 +1,2 @@ +ExitSuccess +1 diff --git a/testsuite/tests/process/T8343.hs b/testsuite/tests/process/T8343.hs new file mode 100644 index 000000000000..23363a50d725 --- /dev/null +++ b/testsuite/tests/process/T8343.hs @@ -0,0 +1,8 @@ +import System.Process +import System.Timeout + +main = timeout 1000000 $ do -- The outer timeout shouldn't trigger + timeout 10000 $ print =<< readProcess "sleep" ["7200"] "" + putStrLn "Good!" + timeout 10000 $ print =<< readProcessWithExitCode "sleep" ["7200"] "" + putStrLn "Good!" diff --git a/testsuite/tests/process/T8343.stdout b/testsuite/tests/process/T8343.stdout new file mode 100644 index 000000000000..75c573d5701c --- /dev/null +++ b/testsuite/tests/process/T8343.stdout @@ -0,0 +1,2 @@ +Good! +Good! diff --git a/testsuite/tests/process/T9775/Makefile b/testsuite/tests/process/T9775/Makefile new file mode 100644 index 000000000000..8e1cd6e31850 --- /dev/null +++ b/testsuite/tests/process/T9775/Makefile @@ -0,0 +1,12 @@ +# This Makefile runs the tests using GHC's testsuite framework. It +# assumes the package is part of a GHC build tree with the testsuite +# installed in ../../../testsuite. + +TOP=../../../../testsuite +include $(TOP)/mk/boilerplate.mk +include $(TOP)/mk/test.mk + +.PHONY: T9775 +T9775: + '$(TEST_CC)' $(TEST_CC_OPTS) ok.c -o ok.exe + '$(TEST_CC)' $(TEST_CC_OPTS) main.c -o main.exe diff --git a/testsuite/tests/process/T9775/T9775_fail.hs b/testsuite/tests/process/T9775/T9775_fail.hs new file mode 100644 index 000000000000..b2cc020ddd0b --- /dev/null +++ b/testsuite/tests/process/T9775/T9775_fail.hs @@ -0,0 +1,7 @@ +module Main where + +import System.Process + +main + = do (_,_,_,p) <- createProcess (proc "main" []) + waitForProcess p >>= print diff --git a/testsuite/tests/process/T9775/T9775_fail.stdout b/testsuite/tests/process/T9775/T9775_fail.stdout new file mode 100644 index 000000000000..7374c53f462a --- /dev/null +++ b/testsuite/tests/process/T9775/T9775_fail.stdout @@ -0,0 +1,2 @@ +ExitSuccess +bye bye diff --git a/testsuite/tests/process/T9775/T9775_good.hs b/testsuite/tests/process/T9775/T9775_good.hs new file mode 100644 index 000000000000..a66c3165d76f --- /dev/null +++ b/testsuite/tests/process/T9775/T9775_good.hs @@ -0,0 +1,7 @@ +module Main where + +import System.Process + +main + = do (_,_,_,p) <- createProcess ((proc "main" []){ use_process_jobs = True }) + waitForProcess p >>= print diff --git a/testsuite/tests/process/T9775/T9775_good.stdout b/testsuite/tests/process/T9775/T9775_good.stdout new file mode 100644 index 000000000000..14b2f72ee233 --- /dev/null +++ b/testsuite/tests/process/T9775/T9775_good.stdout @@ -0,0 +1,2 @@ +bye bye +ExitSuccess diff --git a/testsuite/tests/process/T9775/all.T b/testsuite/tests/process/T9775/all.T new file mode 100644 index 000000000000..ae07e48ae046 --- /dev/null +++ b/testsuite/tests/process/T9775/all.T @@ -0,0 +1,14 @@ + +test('T9775_fail', + [extra_files(['ok.c', 'main.c']), + unless(opsys('mingw32'),skip), + pre_cmd('$MAKE -s --no-print-directory T9775'), + req_process], + compile_and_run, ['']) + +test('T9775_good', + [unless(opsys('mingw32'),skip), + extra_files(['ok.c', 'main.c']), + pre_cmd('$MAKE -s --no-print-directory T9775'), + req_process], + compile_and_run, ['']) diff --git a/testsuite/tests/process/T9775/main.c b/testsuite/tests/process/T9775/main.c new file mode 100644 index 000000000000..2c891b1a69f5 --- /dev/null +++ b/testsuite/tests/process/T9775/main.c @@ -0,0 +1,6 @@ +#include <unistd.h> + +int main(int argc, char *argv[]) { + char * args[2] = { "ok", NULL }; + execv("./ok", args); +} diff --git a/testsuite/tests/process/T9775/ok.c b/testsuite/tests/process/T9775/ok.c new file mode 100644 index 000000000000..50191dc03a96 --- /dev/null +++ b/testsuite/tests/process/T9775/ok.c @@ -0,0 +1,8 @@ +#include <stdio.h> +#include <windows.h> + +int main() { + Sleep(2000); + printf("bye bye\n"); + return 120; +} diff --git a/testsuite/tests/process/all.T b/testsuite/tests/process/all.T new file mode 100644 index 000000000000..afc0bb1a8cac --- /dev/null +++ b/testsuite/tests/process/all.T @@ -0,0 +1,53 @@ +# some platforms use spawnp instead of exec in some cases, resulting +# in spurious error output changes. +normalise_exec = normalise_fun(lambda s: s.replace('posix_spawnp', 'exec')) + +test('process001', [req_process], compile_and_run, ['']) +test('process002', [fragile_for(16547, concurrent_ways), req_process], compile_and_run, ['']) +test('process003', [fragile_for(17245, concurrent_ways), req_process], compile_and_run, ['']) +test('process004', [normalise_exec, normalise_exe, req_process], compile_and_run, ['']) +test('T1780', [req_process], compile_and_run, ['']) +test('process005', [omit_ghci, req_process], compile_and_run, ['']) +test('process006', [req_process], compile_and_run, ['']) + +test('process007', + [when(opsys('mingw32'), skip), + pre_cmd('$MAKE -s --no-print-directory process007_fd'), + js_broken(22349), + req_process], + compile_and_run, ['']) +test('process008', [req_process], compile_and_run, ['']) + +# not the normal way: this test runs processes from multiple threads, and +# will get stuck without the threaded RTS. +test('T3231', + [only_ways(['threaded1','threaded2']), + req_process], + compile_and_run, + ['']) +test('T4198', + [pre_cmd('{compiler} exitminus1.c -no-hs-main -o exitminus1'), + js_broken(22349), + req_process], + compile_and_run, + ['']) + +test('T3994', [only_ways(['threaded1','threaded2']), + extra_files(['T3994app.hs']), + pre_cmd('$MAKE -s --no-print-directory T3994app'), + req_process], + compile_and_run, ['']) +test('T4889',[req_process], compile_and_run, ['']) + +test('process009', [when(opsys('mingw32'), skip), req_process], compile_and_run, ['']) +test('process010', [ + normalise_fun(lambda s: s.replace('illegal operation (Inappropriate ioctl for device)', 'does not exist (No such file or directory)')), + normalise_exec, + req_process +], compile_and_run, ['']) +test('process011', + [when(opsys('mingw32'), skip), pre_cmd('{compiler} -no-hs-main -o process011_c process011_c.c'), js_broken(22349), req_process], + compile_and_run, ['']) + +test('T8343', [req_process], compile_and_run, ['']) +test('processT251', [omit_ghci, req_process], compile_and_run, ['']) diff --git a/testsuite/tests/process/exitminus1.c b/testsuite/tests/process/exitminus1.c new file mode 100644 index 000000000000..043d9ff975fc --- /dev/null +++ b/testsuite/tests/process/exitminus1.c @@ -0,0 +1 @@ +int main() { return -1; } diff --git a/testsuite/tests/process/process001.hs b/testsuite/tests/process/process001.hs new file mode 100644 index 000000000000..2ad5a46583fd --- /dev/null +++ b/testsuite/tests/process/process001.hs @@ -0,0 +1,10 @@ +{-# OPTIONS -cpp #-} +import System.IO +import System.Process + +test = do + h <- openFile "process001.out" WriteMode + ph <- runProcess "ls" [] Nothing Nothing Nothing (Just h) Nothing + waitForProcess ph + +main = test >> test >> return () diff --git a/testsuite/tests/process/process002.hs b/testsuite/tests/process/process002.hs new file mode 100644 index 000000000000..bf681ad06223 --- /dev/null +++ b/testsuite/tests/process/process002.hs @@ -0,0 +1,9 @@ +{-# OPTIONS -cpp #-} +import System.Process +import System.IO + +main = do + h <- openFile "process002.out" WriteMode + ph <- runProcess "ls" [] Nothing Nothing Nothing (Just h) (Just h) + waitForProcess ph + return () diff --git a/testsuite/tests/process/process003.hs b/testsuite/tests/process/process003.hs new file mode 100644 index 000000000000..9d8d7d245ebd --- /dev/null +++ b/testsuite/tests/process/process003.hs @@ -0,0 +1,24 @@ +-- [ ghc-Bugs-1249226 ] runInteractiveProcess and closed stdin. +-- Fixed in rev 1.9 of fptools/libraries/base/cbits/runProcess.c + +-- This test doesn't work in GHCi, because FD 0 gets re-allocated to +-- the IO manager pipe, which isn't set to non-blocking mode, and the +-- interactive prompt ends up blocking on a read from this descriptor. + +import System.IO +import Control.Concurrent +import System.Process + +main = do + hClose stdin -- everything works as expected if the handle isn't closed. + putStrLn "Running cat ..." + (inp, out, err, pid) <- runInteractiveProcess "cat" [] Nothing Nothing + forkIO (hPutStrLn inp "foo" >> hClose inp) + mout <- newEmptyMVar + merr <- newEmptyMVar + forkIO (hGetContents out >>= \s -> length s `seq` putMVar mout s) + forkIO (hGetContents err >>= \s -> length s `seq` putMVar merr s) + -- Don't want to deal with waitForProcess and -threaded right now. + takeMVar mout >>= putStrLn + takeMVar merr >>= putStrLn + return () diff --git a/testsuite/tests/process/process003.stdout b/testsuite/tests/process/process003.stdout new file mode 100644 index 000000000000..12cd09d08469 --- /dev/null +++ b/testsuite/tests/process/process003.stdout @@ -0,0 +1,4 @@ +Running cat ... +foo + + diff --git a/testsuite/tests/process/process004.hs b/testsuite/tests/process/process004.hs new file mode 100644 index 000000000000..d72dc6dbad24 --- /dev/null +++ b/testsuite/tests/process/process004.hs @@ -0,0 +1,23 @@ +module Main where + +import System.IO.Error +import System.Process + +main :: IO () +main = do test1 `catchIOError` \e -> putStrLn ("Exc: " ++ show e) + test2 `catchIOError` \e -> putStrLn ("Exc: " ++ show e) + +test1 :: IO () +test1 = do + (_, _, _, commhand) <- + runInteractiveProcess "true" [] (Just "/no/such/dir") Nothing + exitCode <- waitForProcess commhand + print exitCode + +test2 :: IO () +test2 = do + commhand <- runProcess "true" [] (Just "/no/such/dir") Nothing + Nothing Nothing Nothing + exitCode <- waitForProcess commhand + print exitCode + diff --git a/testsuite/tests/process/process004.stdout b/testsuite/tests/process/process004.stdout new file mode 100644 index 000000000000..e8220702ad50 --- /dev/null +++ b/testsuite/tests/process/process004.stdout @@ -0,0 +1,2 @@ +Exc: true: runInteractiveProcess: chdir: invalid argument (Bad file descriptor) +Exc: true: runProcess: chdir: does not exist (No such file or directory) diff --git a/testsuite/tests/process/process004.stdout-javascript-unknown-ghcjs b/testsuite/tests/process/process004.stdout-javascript-unknown-ghcjs new file mode 100644 index 000000000000..e90c998d8a4e --- /dev/null +++ b/testsuite/tests/process/process004.stdout-javascript-unknown-ghcjs @@ -0,0 +1,2 @@ +Exc: true: runInteractiveProcess: does not exist (No such file or directory) +Exc: true: runProcess: does not exist (No such file or directory) diff --git a/testsuite/tests/process/process004.stdout-mingw32 b/testsuite/tests/process/process004.stdout-mingw32 new file mode 100644 index 000000000000..e9e0e0cdf7f1 --- /dev/null +++ b/testsuite/tests/process/process004.stdout-mingw32 @@ -0,0 +1,2 @@ +Exc: true: runInteractiveProcess: invalid argument (Invalid argument) +Exc: true: runProcess: invalid argument (Invalid argument) diff --git a/testsuite/tests/process/process005.hs b/testsuite/tests/process/process005.hs new file mode 100644 index 000000000000..adb181092d3c --- /dev/null +++ b/testsuite/tests/process/process005.hs @@ -0,0 +1,26 @@ +module Main where + +import Control.Concurrent +import System.IO +import System.Process + +main :: IO () +main = do p <- foldl (>>=) (return stdin) (replicate 10 docat) >>= docat0 + t <- myThreadId + -- timeout + forkIO $ do threadDelay 5000000; killThread t + waitForProcess p + putStrLn "end" + return () + +docat :: Handle -> IO Handle +docat hin = do + (_, Just hout, _, ph) <- + createProcess (proc "cat" []){ std_in = UseHandle hin, + std_out = CreatePipe } + return hout + +docat0 :: Handle -> IO ProcessHandle +docat0 hin = do + (_,_,_,ph) <- createProcess (proc "cat" []){ std_in = UseHandle hin } + return ph diff --git a/testsuite/tests/process/process005.stdin b/testsuite/tests/process/process005.stdin new file mode 100644 index 000000000000..d2b8b3d27224 --- /dev/null +++ b/testsuite/tests/process/process005.stdin @@ -0,0 +1,3 @@ +testing +testing +123 diff --git a/testsuite/tests/process/process005.stdout b/testsuite/tests/process/process005.stdout new file mode 100644 index 000000000000..e09696b20020 --- /dev/null +++ b/testsuite/tests/process/process005.stdout @@ -0,0 +1,4 @@ +testing +testing +123 +end diff --git a/testsuite/tests/process/process006.hs b/testsuite/tests/process/process006.hs new file mode 100644 index 000000000000..63e13591a4bf --- /dev/null +++ b/testsuite/tests/process/process006.hs @@ -0,0 +1,15 @@ +module Main where + +import Control.Concurrent +import System.IO +import System.Process +import Control.Monad +import Control.Exception + +main :: IO () +main = do + print =<< readProcess "cat" [] "yan\ntan\tether\n" + print =<< readProcessWithExitCode "cat" [] "yan\ntan\tether\n" + print =<< readProcessWithExitCode "sh" ["-c", "echo stdout; echo stderr 1>&2; exit 3"] "" + e <- (try $ readProcess "sh" ["-c", "echo stdout; echo stderr 1>&2; exit 3"] "") + print (e :: Either SomeException String) diff --git a/testsuite/tests/process/process006.stderr b/testsuite/tests/process/process006.stderr new file mode 100644 index 000000000000..af6415db3c72 --- /dev/null +++ b/testsuite/tests/process/process006.stderr @@ -0,0 +1 @@ +stderr diff --git a/testsuite/tests/process/process006.stdout b/testsuite/tests/process/process006.stdout new file mode 100644 index 000000000000..1e1186b31e84 --- /dev/null +++ b/testsuite/tests/process/process006.stdout @@ -0,0 +1,4 @@ +"yan\ntan\tether\n" +(ExitSuccess,"yan\ntan\tether\n","") +(ExitFailure 3,"stdout\n","stderr\n") +Left readCreateProcess: sh "-c" "echo stdout; echo stderr 1>&2; exit 3" (exit 3): failed diff --git a/testsuite/tests/process/process007.hs b/testsuite/tests/process/process007.hs new file mode 100644 index 000000000000..506a0ca40bd1 --- /dev/null +++ b/testsuite/tests/process/process007.hs @@ -0,0 +1,24 @@ + +import System.Process +import System.IO +import System.Posix +import System.Exit + +tmpfile = "process007.tmp" + +main = do + writeFile tmpfile "You bad pie-rats!\n" + fd <- handleToFd =<< openFile tmpfile ReadMode + rawSystem "./process007_fd" [show fd] + closeFd fd + + fd <- handleToFd =<< openFile tmpfile ReadMode + nul <- openFile "/dev/null" WriteMode + (_,_,_,p) <- createProcess (shell ("./process007_fd " ++ show fd)) + { close_fds = True, + std_err = UseHandle nul } + e <- waitForProcess p + case e of + ExitSuccess -> putStrLn "eek!" + _ -> putStrLn "failed, as expected" + closeFd fd diff --git a/testsuite/tests/process/process007.stdout b/testsuite/tests/process/process007.stdout new file mode 100644 index 000000000000..7a9b0bf1ab77 --- /dev/null +++ b/testsuite/tests/process/process007.stdout @@ -0,0 +1,2 @@ +You bad pie-rats! +failed, as expected diff --git a/testsuite/tests/process/process007_fd.c b/testsuite/tests/process/process007_fd.c new file mode 100644 index 000000000000..f62ec249a76f --- /dev/null +++ b/testsuite/tests/process/process007_fd.c @@ -0,0 +1,41 @@ + +#include <errno.h> +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> + +#define SIZE 1024 + +int main(int argc, char **argv) { + int fd; + char buf[SIZE]; + int nRead, nWrite; + + if (argc != 2) { + printf("Bad arguments\n"); + exit(1); + } + + fd = atoi(argv[1]); + + while ((nRead = read(fd, buf, SIZE)) != 0) { + if (nRead > 0) { + ssize_t nWritten = 0; + while (nWritten < nRead) { + nWrite = write(STDOUT_FILENO, buf + nWritten, nRead - nWritten); + if (nWrite < 0) { + perror("printf failed"); + exit(1); + } + nWritten += nWrite; + } + } + else if (errno != EAGAIN && errno != EWOULDBLOCK && errno != EINTR) { + perror("read failed"); + exit(1); + } + } + + return 0; +} + diff --git a/testsuite/tests/process/process008.hs b/testsuite/tests/process/process008.hs new file mode 100644 index 000000000000..f02f55b7aa58 --- /dev/null +++ b/testsuite/tests/process/process008.hs @@ -0,0 +1,9 @@ +{-# OPTIONS -cpp #-} +import System.IO +import System.Cmd +import System.Environment + +-- echo can't be run outside of the shell in MSYS, hence: +test = rawSystem "sh" ["-c","echo testing"] + +main = test >> test >> return () diff --git a/testsuite/tests/process/process008.stdout b/testsuite/tests/process/process008.stdout new file mode 100644 index 000000000000..755cc82b6727 --- /dev/null +++ b/testsuite/tests/process/process008.stdout @@ -0,0 +1,2 @@ +testing +testing diff --git a/testsuite/tests/process/process009.hs b/testsuite/tests/process/process009.hs new file mode 100644 index 000000000000..7cffd335a4c0 --- /dev/null +++ b/testsuite/tests/process/process009.hs @@ -0,0 +1,24 @@ +import Control.Monad +import System.Exit +import System.Process +import Data.Maybe +import Data.List (intercalate) + +-- Test that we get the right exit code for processes that terminate +-- with a signal (#7229) + +main = do + let script = intercalate " " + [ "exec python3 2>/dev/null" + , "-c" + , "'import os; os.kill(os.getpid(), 1)'" + ] + (_,_,_,p) <- createProcess (shell script) + waitForProcess p >>= print + getProcessExitCode p >>= print + + (_,_,_,p) <- createProcess (shell script) + forever $ do + r <- getProcessExitCode p + if (isJust r) then do print r; exitWith ExitSuccess else return () + diff --git a/testsuite/tests/process/process009.stdout b/testsuite/tests/process/process009.stdout new file mode 100644 index 000000000000..751a73aa6bf2 --- /dev/null +++ b/testsuite/tests/process/process009.stdout @@ -0,0 +1,3 @@ +ExitFailure (-1) +Just (ExitFailure (-1)) +Just (ExitFailure (-1)) diff --git a/testsuite/tests/process/process010.hs b/testsuite/tests/process/process010.hs new file mode 100644 index 000000000000..ea188eefd3c1 --- /dev/null +++ b/testsuite/tests/process/process010.hs @@ -0,0 +1,13 @@ + +import System.IO.Error +import System.Process + +main :: IO () +main = do run "true" + run "false" + run "/non/existent" + putStrLn "Done" + +run :: FilePath -> IO () +run fp = (rawSystem fp [] >>= print) + `catchIOError` \e -> putStrLn ("Exc: " ++ show e) diff --git a/testsuite/tests/process/process010.stdout b/testsuite/tests/process/process010.stdout new file mode 100644 index 000000000000..1c78052da1bb --- /dev/null +++ b/testsuite/tests/process/process010.stdout @@ -0,0 +1,4 @@ +ExitSuccess +ExitFailure 1 +Exc: /non/existent: rawSystem: posix_spawnp: illegal operation (Inappropriate ioctl for device) +Done diff --git a/testsuite/tests/process/process010.stdout-i386-unknown-solaris2 b/testsuite/tests/process/process010.stdout-i386-unknown-solaris2 new file mode 100644 index 000000000000..316b23c7740a --- /dev/null +++ b/testsuite/tests/process/process010.stdout-i386-unknown-solaris2 @@ -0,0 +1,4 @@ +ExitSuccess +ExitFailure 255 +Exc: /non/existent: rawSystem: runInteractiveProcess: exec: does not exist (No such file or directory) +Done diff --git a/testsuite/tests/process/process010.stdout-javascript-unknown-ghcjs b/testsuite/tests/process/process010.stdout-javascript-unknown-ghcjs new file mode 100644 index 000000000000..17d996a89259 --- /dev/null +++ b/testsuite/tests/process/process010.stdout-javascript-unknown-ghcjs @@ -0,0 +1,4 @@ +ExitSuccess +ExitFailure 1 +Exc: /non/existent: rawSystem: does not exist (No such file or directory) +Done diff --git a/testsuite/tests/process/process010.stdout-mingw32 b/testsuite/tests/process/process010.stdout-mingw32 new file mode 100644 index 000000000000..17d996a89259 --- /dev/null +++ b/testsuite/tests/process/process010.stdout-mingw32 @@ -0,0 +1,4 @@ +ExitSuccess +ExitFailure 1 +Exc: /non/existent: rawSystem: does not exist (No such file or directory) +Done diff --git a/testsuite/tests/process/process011.hs b/testsuite/tests/process/process011.hs new file mode 100644 index 000000000000..b711fe491485 --- /dev/null +++ b/testsuite/tests/process/process011.hs @@ -0,0 +1,73 @@ +import System.Process +import System.IO +import Control.Exception +import Control.Concurrent +import Data.List (intercalate) + +-- Test control-C delegation (#2301) + +main :: IO () +main = do + hSetBuffering stdout LineBuffering + + putStrLn "===================== test 1" + + -- shell kills itself with SIGINT, + -- delegation off, exit code (death by signal) reported as normal + do let script = "./process011_c" + (_,_,_,p) <- createProcess (proc script []) { delegate_ctlc = False } + waitForProcess p >>= print + + putStrLn "===================== test 2" + + -- shell kills itself with SIGINT, + -- delegation on, so expect to throw UserInterrupt + do let script = "./process011_c" + (_,_,_,p) <- createProcess (proc script []) { delegate_ctlc = True } + (waitForProcess p >>= print) + `catchUserInterrupt` \e -> putStrLn $ "caught: " ++ show e + + putStrLn "===================== test 3" + + -- shell sends itself SIGINT but traps it, + -- delegation on, but the shell terminates normally so just normal exit code + do let script = intercalate "; " + [ "trap 'echo shell trapped SIGINT' INT" + , "kill -INT $$" + , "exit 42" ] + (_,_,_,p) <- createProcess (shell script) { delegate_ctlc = True } + waitForProcess p >>= print + + putStrLn "===================== test 4" + + -- shell sends us SIGINT. + -- delegation on, so we should not get the SIGINT ourselves + -- shell terminates normally so just normal exit code + do let script = intercalate "; " + [ "kill -INT $PPID" + , "kill -INT $PPID" + , "exit 42" ] + (_,_,_,p) <- createProcess (shell script) { delegate_ctlc = True } + waitForProcess p >>= print + + putStrLn "===================== test 5" + + -- shell sends us SIGINT. + -- delegation off, so we should get the SIGINT ourselves (async) + do let script = intercalate "; " + [ "kill -INT $PPID" + , "exit 42" ] + (_,_,_,p) <- createProcess (shell script) { delegate_ctlc = False } + exit <- waitForProcess p + -- need to allow for the async exception to arrive + threadDelay 1000000 + -- we should never make it to here... + putStrLn "never caught interrupt" + print exit + `catchUserInterrupt` \e -> putStrLn $ "caught: " ++ show e + + putStrLn "===================== done" + +catchUserInterrupt :: IO a -> (AsyncException -> IO a) -> IO a +catchUserInterrupt = + catchJust (\e -> case e of UserInterrupt -> Just e; _ -> Nothing) diff --git a/testsuite/tests/process/process011.stdout b/testsuite/tests/process/process011.stdout new file mode 100644 index 000000000000..2c9a46fefd30 --- /dev/null +++ b/testsuite/tests/process/process011.stdout @@ -0,0 +1,12 @@ +===================== test 1 +ExitFailure (-2) +===================== test 2 +caught: user interrupt +===================== test 3 +shell trapped SIGINT +ExitFailure 42 +===================== test 4 +ExitFailure 42 +===================== test 5 +caught: user interrupt +===================== done diff --git a/testsuite/tests/process/process011_c.c b/testsuite/tests/process/process011_c.c new file mode 100644 index 000000000000..6e271c9cebbe --- /dev/null +++ b/testsuite/tests/process/process011_c.c @@ -0,0 +1,9 @@ +#include <unistd.h> +#include <signal.h> + +int main() { + kill(getpid(), SIGINT); + sleep(1); + return 0; +} + diff --git a/testsuite/tests/process/processT251.hs b/testsuite/tests/process/processT251.hs new file mode 100644 index 000000000000..863b46e4d6ca --- /dev/null +++ b/testsuite/tests/process/processT251.hs @@ -0,0 +1,39 @@ +import Control.Exception +import GHC.IO.Exception +import System.Environment +import System.Exit +import System.Process + +main :: IO () +main = do + args <- getArgs + case args of + [] -> parent + ["child"] -> child + ["child2"] -> child2 + _ -> fail "unknown mode" + +parent :: IO () +parent = do + putStrLn "parent start" + (_, _, _, phdl) <- createProcess $ (proc "./processT251" ["child"]) { std_in = NoStream } + ExitSuccess <- waitForProcess phdl + putStrLn "parent done" + +child :: IO () +child = do + putStrLn "child start" + (_, _, _, phdl) <- createProcess $ (proc "./processT251" ["child2"]) { std_in = NoStream } + ExitSuccess <- waitForProcess phdl + putStrLn "child done" + +child2 :: IO () +child2 = do + putStrLn "child2 start" + -- Unfortunate, there isn't a reliable way to test that stdin has been closed. + -- Afterall, if any file is opened in the child, it may reuse the + -- supposedly-closed fd 0. In particular this tends to happen in the + -- threaded RTS, since the event manager's control pipe is opened during + -- RTS initialzation. + putStrLn "child2 done" + diff --git a/testsuite/tests/process/processT251.stdout b/testsuite/tests/process/processT251.stdout new file mode 100644 index 000000000000..0f78e8ab2a38 --- /dev/null +++ b/testsuite/tests/process/processT251.stdout @@ -0,0 +1,6 @@ +child2 start +child2 done +child start +child done +parent start +parent done -- GitLab