packages feed

xfconf-4.8.0.0: Tests/TestGlib.hs

module Main where

import Test.QuickCheck
import Test.QuickCheck.Monadic              as QCM
import Test.Framework                       (defaultMain, testGroup)
import Test.Framework.Providers.QuickCheck2

import Control.Concurrent.MVar
import Control.Concurrent                   (forkIO, threadDelay)
import Control.Exception                    (bracket_)

import System.XFCE.Xfconf
import System.Glib.MainLoop
import System.Glib.Signals                  (signalDisconnect)

sleep :: Int -> IO ()
sleep = threadDelay . (1000000*)

msleep :: Int -> IO ()
msleep = threadDelay . (1000*)

-- | garbage string, just be careful to avoid C-String delimiter ('\0')
genUTFString :: Gen String
genUTFString = suchThat arbitrary (notElem '\0')

-- | same as above but without empty strings
genUTFString1 :: Gen String
genUTFString1 = suchThat genUTFString (not . null)

testSignal :: Property
testSignal = monadicIO $ do
        value  <- pick genUTFString1
        value' <- QCM.run $ setAndRetrieveWithGlib value
        QCM.assert (value == value')

debugInOut n = bracket_ (putStrLn i) (putStrLn o)
  where i = "inside " ++ n
        o = "outside " ++ n

setAndRetrieveWithGlib :: String -> IO String
setAndRetrieveWithGlib value = do
        if null value
           then error "rondedjiu, beware the deadlocks \
                      \with your empty strings!"
           else return ()

        let chanName = "QuickCheck"
            propName = "/SignalCheck"

        chan   <- channelGet chanName
        result <- newEmptyMVar

        -- Init property to a dummy value, since xfconfd will only
        -- trigger the callback if our future new value is different
        -- from the present one
        debugInOut "setDummyString" $ channelSetString chan propName "init"

        -- we sleep, otherwise our previous "set" action might get
        -- caught by the following signal handler
        debugInOut "sleep(1)" $ msleep 40

        -- We set our signal handler. Note that the glib loop has been
        -- forked away from this process so the following operation will
        -- be processed apart.
        sigid <- onPropertyChanged chan $ \_ maybeValue -> debugInOut "onPropertyChanged" $ do
            cond <- isEmptyMVar result
            if cond
               then case maybeValue of
                 Just (XfconfString s) -> putMVar result s
                 Nothing               -> putMVar result ""
                 _                     -> putMVar result "UNKNWOW value"
               else do putStrLn "DANGER, WILL ROBINSON !"
                       error "you are going to fast !"


        debugInOut "sleep(2)" $ msleep 40

        -- We resume our main thread here, by trigerring the previous
        -- signal (that what we were testing, remember ?)
        debugInOut "setNewString" $ channelSetString chan propName value

        -- ... and wait for the answer from the glib loop
        maybeValue' <- tryTakeMVar result

        -- pseudo process cleaning
        signalDisconnect sigid

        maybe (return "N/A") return maybeValue'
        --return value' -- return value' to QuickCheck.Monadic

main =  do
    loop <- mainLoopNew Nothing True
    debugInOut "fork" $ forkIO $ mainLoopRun loop
    msleep 100 >> setAndRetrieveWithGlib "foo" >>= putStr
    msleep 500 >> mainLoopQuit loop

main :: IO ()
main' = do
    loop <- mainLoopNew Nothing True
    forkIO $ mainLoopRun loop
    defaultMain testSuite >> mainLoopQuit loop

  where testSuite = [ testGroup "GLib loop"
                        [testProperty "onPropertyChanged" testSignal]
                    ]