module Test.Ganeti.Luxi (testLuxi) where
import Test.HUnit
import Test.QuickCheck
import Test.QuickCheck.Monadic (monadicIO, run, stop)
import Data.List
import Control.Applicative
import Control.Concurrent (forkIO)
import Control.Exception (bracket)
import System.Directory (getTemporaryDirectory, removeFile)
import System.IO (hClose, openTempFile)
import qualified Text.JSON as J
import Test.Ganeti.TestHelper
import Test.Ganeti.TestCommon
import Test.Ganeti.Query.Language (genFilter)
import Test.Ganeti.OpCodes ()
import Ganeti.BasicTypes
import qualified Ganeti.Luxi as Luxi
$(genArbitrary ''Luxi.LuxiReq)
instance Arbitrary Luxi.LuxiOp where
arbitrary = do
lreq <- arbitrary
case lreq of
Luxi.ReqQuery -> Luxi.Query <$> arbitrary <*> genFields <*> genFilter
Luxi.ReqQueryFields -> Luxi.QueryFields <$> arbitrary <*> genFields
Luxi.ReqQueryNodes -> Luxi.QueryNodes <$> listOf genFQDN <*>
genFields <*> arbitrary
Luxi.ReqQueryGroups -> Luxi.QueryGroups <$> arbitrary <*>
arbitrary <*> arbitrary
Luxi.ReqQueryNetworks -> Luxi.QueryNetworks <$> arbitrary <*>
arbitrary <*> arbitrary
Luxi.ReqQueryInstances -> Luxi.QueryInstances <$> listOf genFQDN <*>
genFields <*> arbitrary
Luxi.ReqQueryJobs -> Luxi.QueryJobs <$> arbitrary <*> genFields
Luxi.ReqQueryExports -> Luxi.QueryExports <$>
listOf genFQDN <*> arbitrary
Luxi.ReqQueryConfigValues -> Luxi.QueryConfigValues <$> genFields
Luxi.ReqQueryClusterInfo -> pure Luxi.QueryClusterInfo
Luxi.ReqQueryTags -> Luxi.QueryTags <$> arbitrary
Luxi.ReqSubmitJob -> Luxi.SubmitJob <$> resize maxOpCodes arbitrary
Luxi.ReqSubmitManyJobs -> Luxi.SubmitManyJobs <$>
resize maxOpCodes arbitrary
Luxi.ReqWaitForJobChange -> Luxi.WaitForJobChange <$> arbitrary <*>
genFields <*> pure J.JSNull <*>
pure J.JSNull <*> arbitrary
Luxi.ReqArchiveJob -> Luxi.ArchiveJob <$> arbitrary
Luxi.ReqAutoArchiveJobs -> Luxi.AutoArchiveJobs <$> arbitrary <*>
arbitrary
Luxi.ReqCancelJob -> Luxi.CancelJob <$> arbitrary
Luxi.ReqChangeJobPriority -> Luxi.ChangeJobPriority <$> arbitrary <*>
arbitrary
Luxi.ReqSetDrainFlag -> Luxi.SetDrainFlag <$> arbitrary
Luxi.ReqSetWatcherPause -> Luxi.SetWatcherPause <$> arbitrary
prop_CallEncoding :: Luxi.LuxiOp -> Property
prop_CallEncoding op =
(Luxi.validateCall (Luxi.buildCall op) >>= Luxi.decodeCall) ==? Ok op
getTempFileName :: IO FilePath
getTempFileName = do
tempdir <- getTemporaryDirectory
(fpath, handle) <- openTempFile tempdir "luxitest"
_ <- hClose handle
removeFile fpath
return fpath
luxiServerPong :: Luxi.Client -> IO ()
luxiServerPong c = do
msg <- Luxi.recvMsgExt c
case msg of
Luxi.RecvOk m -> Luxi.sendMsg c m >> luxiServerPong c
_ -> return ()
luxiClientPong :: Luxi.Client -> [String] -> IO [String]
luxiClientPong c =
mapM (\m -> Luxi.sendMsg c m >> Luxi.recvMsg c)
prop_ClientServer :: [[DNSChar]] -> Property
prop_ClientServer dnschars = monadicIO $ do
let msgs = map (map dnsGetChar) dnschars
fpath <- run getTempFileName
server <- run $ Luxi.getServer False fpath
_ <- run . forkIO $
bracket
(Luxi.acceptClient server)
(\c -> Luxi.closeClient c >> Luxi.closeServer fpath server)
luxiServerPong
replies <- run $
bracket
(Luxi.getClient fpath)
Luxi.closeClient
(`luxiClientPong` msgs)
stop $ replies ==? msgs
case_AllDefined :: Assertion
case_AllDefined = do
py_stdout <- runPython "from ganeti import luxi\n\
\print '\\n'.join(luxi.REQ_ALL)" "" >>=
checkPythonResult
let py_ops = sort $ lines py_stdout
hs_ops = Luxi.allLuxiCalls
extra_py = py_ops \\ hs_ops
extra_hs = hs_ops \\ py_ops
assertBool ("Luxi calls missing from Haskell code:\n" ++
unlines extra_py) (null extra_py)
assertBool ("Extra Luxi calls in the Haskell code code:\n" ++
unlines extra_hs) (null extra_hs)
testSuite "Luxi"
[ 'prop_CallEncoding
, 'prop_ClientServer
, 'case_AllDefined
]