{-# LANGUAGE OverloadedStrings #-} -- Copyright (C) 2012 John Millikin -- -- Licensed under the Apache License, Version 2.0 (the "License"); -- you may not use this file except in compliance with the License. -- You may obtain a copy of the License at -- -- http://www.apache.org/licenses/LICENSE-2.0 -- -- Unless required by applicable law or agreed to in writing, software -- distributed under the License is distributed on an "AS IS" BASIS, -- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -- See the License for the specific language governing permissions and -- limitations under the License. module DBusTests.Integration (test_Integration) where import Control.Exception (finally) import System.Directory (removeFile) import System.Exit import System.IO (hGetLine) import System.Process import Test.Tasty import Test.Tasty.HUnit import DBus import DBus.Socket import DBus.Client import DBusTests.Util test_Integration :: TestTree test_Integration = testGroup "Integration" [ test_Socket , test_Client ] test_Socket :: TestTree test_Socket = withDaemon "socket" $ \addr -> do let hello = (methodCall "/org/freedesktop/DBus" "org.freedesktop.DBus" "Hello") { methodCallDestination = Just "org.freedesktop.DBus" } sock <- open addr serial <- send sock hello return assertBool "invalid serial" $ serialValue serial >= 1 received <- receive sock let ReceivedMethodReturn _ ret = received methodReturnSerial ret @?= serial methodReturnSender ret @?= Just "org.freedesktop.DBus" close sock test_Client :: TestTree test_Client = withDaemon "client" $ \addr -> do clientA <- connect addr clientB <- connect addr export clientA "/" defaultInterface { interfaceName = "com.example.Echo" , interfaceMethods = [ Method "Echo" (signature_ [TypeString]) (signature_ []) ( \msg -> if map variantType (methodCallBody msg) == [TypeString] then return (ReplyReturn (methodCallBody msg)) else return $ ReplyError "com.example.Error" [toVariant ("bad body: " ++ show (methodCallBody msg))]) ] } -- TODO: get bus address of clientA with a function let busAddrA = ":1.0" -- Successful call let bodyGood = [toVariant ("test" :: String)] retGood <- call clientB (methodCall "/" "com.example.Echo" "Echo") { methodCallDestination = Just busAddrA , methodCallBody = bodyGood } ret <- requireRight retGood methodReturnBody ret @?= bodyGood -- Failed call let bodyBad = [toVariant True] retBad <- call clientB (methodCall "/" "com.example.Echo" "Echo") { methodCallDestination = Just busAddrA , methodCallBody = bodyBad } err <- requireLeft retBad methodErrorName err @?= "com.example.Error" methodErrorBody err @?= [toVariant ("bad body: [Variant True]" :: String)] disconnect clientA disconnect clientB configFileContent :: String configFileContent = "\ \\ \\ \ session\ \ \ \ unix:tmpdir=/tmp\ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \" withDaemon :: String -> (Address -> Assertion) -> TestTree withDaemon name io = testCase name $ do (versionExit, _, _) <- readProcessWithExitCode "dbus-daemon" ["--version"] "" case versionExit of ExitFailure _ -> assertFailure $ "dbus-daemon failed: " ++ show versionExit ExitSuccess -> do configFilePath <- getTempPath writeFile configFilePath configFileContent daemon <- createProcess (proc "dbus-daemon" ["--config-file=" ++ configFilePath, "--print-address"]) { std_out = CreatePipe , close_fds = True } let (_, Just daemonStdout, _, daemonProc) = daemon finally (do addrString <- hGetLine daemonStdout case parseAddress addrString of Nothing -> assertFailure $ "dbus-daemon returned invalid address: " ++ show addrString Just addr -> io addr) (do terminateProcess daemonProc _ <- waitForProcess daemonProc removeFile configFilePath return ())