packages feed

psx-0.1.0.0: test/TestCases.hsc

module TestCases (
      psxSyscall3Works
    , psxSyscall6Works
    , psxWorking
    , sigfillsetWrapped
    , psxDetected
    ) where

#include <signal.h>
#include <unistd.h>
#include <sys/prctl.h>
#include <sys/syscall.h>

#include <hs-psx.h>

import Control.Concurrent.Async (wait, withAsyncBound)
import Control.Concurrent.MVar (newEmptyMVar, putMVar, readMVar)
import Control.Monad (void)
import Data.Int (Int32, Int64)
import Data.Word (Word64)
import Foreign.C.Error (throwErrnoIfMinus1)
import Foreign.Marshal (alloca)
import Foreign.Ptr (Ptr)
import Foreign.Storable (Storable(..))
import Test.Tasty (TestTree)
import Test.Tasty.HUnit ((@?=), assertBool, testCase, testCaseSteps)

foreign import ccall unsafe "hs-psx.h hs_psx_syscall3"
    psxSyscall3 :: #{type long}
                -> #{type long}
                -> #{type long}
                -> #{type long}
                -> IO #{type long}

foreign import ccall unsafe "hs-psx.h hs_psx_syscall6"
    psxSyscall6 :: #{type long}
                -> #{type long}
                -> #{type long}
                -> #{type long}
                -> #{type long}
                -> #{type long}
                -> #{type long}
                -> IO #{type long}

foreign import ccall unsafe "unistd.h getpid"
    c_getpid :: IO #{type pid_t}

getpid :: IO #{type pid_t}
getpid = throwErrnoIfMinus1 "getpid" c_getpid

foreign import ccall unsafe "unistd.h gettid"
    c_gettid :: IO #{type pid_t}

gettid :: IO #{type pid_t}
gettid = throwErrnoIfMinus1 "gettid" c_gettid

psxSyscall3Works :: TestTree
psxSyscall3Works = testCase "hs_psx_syscall3 works" $ do
    pid <- getpid
    tid <- gettid
    let sig = #{const SIGURG}
    void $ throwErrnoIfMinus1 "tgkill" $ psxSyscall3 #{const __NR_tgkill} (fromIntegral pid) (fromIntegral tid) sig

psxSyscall6Works :: TestTree
psxSyscall6Works = testCase "hs_psx_syscall6 works" $ do
    void $ throwErrnoIfMinus1 "prctl" $ psxSyscall6 #{const __NR_prctl} #{const PR_GET_NO_NEW_PRIVS} 0 0 0 0 0


foreign import ccall unsafe "sys/prctl.h prctl"
    _prctl :: #{type int}
           -> #{type unsigned long}
           -> #{type unsigned long}
           -> #{type unsigned long}
           -> #{type unsigned long}
           -> IO #{type int}

prctl :: #{type int}
      -> #{type unsigned long}
      -> #{type unsigned long}
      -> #{type unsigned long}
      -> #{type unsigned long}
      -> IO #{type int}
prctl option arg2 arg3 arg4 arg5 = throwErrnoIfMinus1 "prctl" $ _prctl option arg2 arg3 arg4 arg5

prctlPsx :: #{type int}
         -> #{type unsigned long}
         -> #{type unsigned long}
         -> #{type unsigned long}
         -> #{type unsigned long}
          -> IO #{type int}
prctlPsx option arg2 arg3 arg4 arg5 = do
    rc <- throwErrnoIfMinus1 "prctl" $
        psxSyscall6 #{const __NR_prctl} (fromIntegral option) (fromIntegral arg2) (fromIntegral arg3) (fromIntegral arg4) (fromIntegral arg5) 0
    return $ fromIntegral rc


psxWorking :: TestTree
psxWorking = testCaseSteps "psx is working" $ \step -> do
    tidMVar <- newEmptyMVar
    continueMVar <- newEmptyMVar

    step "Launching thread"
    withAsyncBound (thread step tidMVar continueMVar) $ \async -> do
        assertNoNewPrivs 0

        myTid <- gettid
        step "Waiting for thread TID"
        itsTid <- readMVar tidMVar

        step "Comparing TIDs"
        assertBool "TIDs are not different" $ myTid /= itsTid

        step "Setting PR_SET_NO_NEW_PRIVS"
        0 <- prctlPsx #{const PR_SET_NO_NEW_PRIVS} 1 0 0 0

        step "Letting thread continue"
        putMVar continueMVar ()

        assertNoNewPrivs 1

        step "Joining thread"
        wait async
  where
    getNoNewPrivs = prctl #{const PR_GET_NO_NEW_PRIVS} 0 0 0 0
    assertNoNewPrivs v =
        getNoNewPrivs >>= \r -> r @?= v

    thread step tidMVar continueMVar = do
        step "In thread" :: IO ()
        assertNoNewPrivs 0

        myTid <- gettid
        step "Sending TID"
        putMVar tidMVar myTid

        step "Waiting to continue"
        () <- readMVar continueMVar
        assertNoNewPrivs 1



data SigsetT

instance Storable SigsetT where
    sizeOf _ = #{size sigset_t}
    alignment _ = #{alignment sigset_t}
    peek _ = error "peek not implemented"
    poke _ _ = error "poke not implemented"

foreign import ccall unsafe "signal.h sigfillset"
    sigfillset :: Ptr SigsetT -> IO #{type int}

foreign import ccall unsafe "signal.h sigismember"
    sigismember :: Ptr SigsetT -> #{type int} -> IO #{type int}

sigfillsetWrapped :: TestTree
sigfillsetWrapped = testCase "sigfillset is wrapped" $ alloca $ \set -> do
    0 <- throwErrnoIfMinus1 "sigfillset" $ sigfillset set
    isMember <- throwErrnoIfMinus1 "sigismember" $ sigismember set #{const HS_PSX_SIGNAL}
    isMember @?= 0


foreign import ccall unsafe "detect-psx.h detect_psx"
    detectPsx :: IO #{type int}

psxDetected :: TestTree
psxDetected = testCase "psx detected by C code" $ do
    assertBool "psx not detected" . toBool =<< detectPsx
  where
    toBool n = n /= 0