{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE TemplateHaskell #-}

-- Copyright (C) 2012 John Millikin <john@john-millikin.com>
--
-- This program is free software: you can redistribute it and/or modify
-- it under the terms of the GNU General Public License as published by
-- the Free Software Foundation, either version 3 of the License, or
-- any later version.
--
-- This program is distributed in the hope that it will be useful,
-- but WITHOUT ANY WARRANTY; without even the implied warranty of
-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-- GNU General Public License for more details.
--
-- You should have received a copy of the GNU General Public License
-- along with this program.  If not, see <http://www.gnu.org/licenses/>.

module DBusTests.Socket (test_Socket) where

import           Test.Chell

import           Control.Concurrent
import           Control.Monad.IO.Class (MonadIO, liftIO)
import qualified Data.Map as Map

import           DBus
import           DBus.Socket
import           DBus.Transport

import           DBusTests.Util (forkVar)

test_Socket :: Suite
test_Socket = suite "Socket"
	[ test_Listen
	, test_ListenWith_CustomAuth
	, test_SendReceive
	]

test_Listen :: Test
test_Listen = assertions "listen" $ do
	uuid <- liftIO randomUUID
	let Just addr = address "unix" (Map.fromList
		[ ("abstract", formatUUID uuid)
		])
	
	listener <- liftIO (listen addr)
	afterTest (closeListener listener)
	
	acceptedVar <- forkVar (accept listener)
	openedVar <- forkVar (open addr)
	
	sock1 <- liftIO (takeMVar acceptedVar)
	afterTest (close sock1)
	
	sock2 <- liftIO (takeMVar openedVar)
	afterTest (close sock2)

test_ListenWith_CustomAuth :: Test
test_ListenWith_CustomAuth = assertions "listenWith-custom-auth" $ do
	uuid <- liftIO randomUUID
	let Just addr = address "unix" (Map.fromList
		[ ("abstract", formatUUID uuid)
		])
	
	listener <- liftIO (listenWith (defaultSocketOptions
		{ socketAuthenticator = dummyAuth
		}) addr)
	afterTest (closeListener listener)
	
	acceptedVar <- forkVar (accept listener)
	openedVar <- forkVar (openWith (defaultSocketOptions
		{ socketAuthenticator = dummyAuth
		}) addr)
	
	sock1 <- liftIO (takeMVar acceptedVar)
	afterTest (close sock1)
	
	sock2 <- liftIO (takeMVar openedVar)
	afterTest (close sock2)

test_SendReceive :: Test
test_SendReceive = assertions "send-receive" $ do
	uuid <- liftIO randomUUID
	let Just addr = address "unix" (Map.fromList
		[ ("abstract", formatUUID uuid)
		])
	
	let msg = (methodCall "/" "org.example.iface" "Foo")
		{ methodCallSender = Just "org.example.src"
		, methodCallDestination = Just "org.example.dst"
		, methodCallAutoStart = False
		, methodCallReplyExpected = False
		, methodCallBody = [toVariant True]
		}
	
	listener <- liftIO (listen addr)
	afterTest (closeListener listener)
	
	acceptedVar <- forkVar (accept listener)
	openedVar <- forkVar (open addr)
	
	sock1 <- liftIO (takeMVar acceptedVar)
	afterTest (close sock1)
	
	sock2 <- liftIO (takeMVar openedVar)
	afterTest (close sock2)
	
	-- client -> server
	do
		serialVar <- liftIO newEmptyMVar
		sentVar <- forkVar (send sock2 msg (putMVar serialVar))
		receivedVar <- forkVar (receive sock1)
		
		serial <- liftIO (takeMVar serialVar)
		sent <- liftIO (takeMVar sentVar)
		received <- liftIO (takeMVar receivedVar)
		
		$assert (equal sent ())
		$assert (equal received (ReceivedMethodCall serial msg))
	
	-- server -> client
	do
		serialVar <- liftIO newEmptyMVar
		sentVar <- forkVar (send sock1 msg (putMVar serialVar))
		receivedVar <- forkVar (receive sock2)
		
		serial <- liftIO (takeMVar serialVar)
		sent <- liftIO (takeMVar sentVar)
		received <- liftIO (takeMVar receivedVar)
		
		$assert (equal sent ())
		$assert (equal received (ReceivedMethodCall serial msg))

dummyAuth :: Transport t => Authenticator t
dummyAuth = authenticator
	{ authenticatorClient = dummyAuthClient
	, authenticatorServer = dummyAuthServer
	}

dummyAuthClient :: Transport t => t -> IO Bool
dummyAuthClient t = do
	transportPut t "\x00"
	resp <- transportGet t 4
	return (resp == "OK\r\n")

dummyAuthServer :: Transport t => t -> UUID -> IO Bool
dummyAuthServer t _ = do
	c <- transportGet t 1
	if c == "\x00"
		then do
			transportPut t "OK\r\n"
			return True
		else return False
