module Test.Ganeti.Luxi (testLuxi) where
import Test.HUnit
import Test.QuickCheck
import Test.QuickCheck.Monadic (monadicIO, run, stop)
import Data.List
import Control.Concurrent (forkIO)
import Control.Exception (bracket)
import qualified Text.JSON as J
import Test.Ganeti.OpCodes ()
import Test.Ganeti.Query.Language (genFilter)
import Test.Ganeti.TestCommon
import Test.Ganeti.TestHelper
import Test.Ganeti.Types (genReasonTrail)
import Ganeti.BasicTypes
import qualified Ganeti.Luxi as Luxi
import qualified Ganeti.UDSServer as US
$(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.ReqQueryFilters -> Luxi.QueryFilters <$> arbitrary <*> genFields
Luxi.ReqReplaceFilter -> Luxi.ReplaceFilter <$> genMaybe genUUID <*>
arbitrary <*> arbitrary <*> arbitrary <*>
genReasonTrail
Luxi.ReqDeleteFilter -> Luxi.DeleteFilter <$> genUUID
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 -> do
kind <- arbitrary
Luxi.QueryTags kind <$> genLuxiTagName kind
Luxi.ReqSubmitJob -> Luxi.SubmitJob <$> resize maxOpCodes arbitrary
Luxi.ReqSubmitJobToDrainedQueue -> Luxi.SubmitJobToDrainedQueue <$>
resize maxOpCodes arbitrary
Luxi.ReqSubmitManyJobs -> Luxi.SubmitManyJobs <$>
resize maxOpCodes arbitrary
Luxi.ReqWaitForJobChange -> Luxi.WaitForJobChange <$> arbitrary <*>
genFields <*> pure J.JSNull <*>
pure J.JSNull <*> arbitrary
Luxi.ReqPickupJob -> Luxi.PickupJob <$> arbitrary
Luxi.ReqArchiveJob -> Luxi.ArchiveJob <$> arbitrary
Luxi.ReqAutoArchiveJobs -> Luxi.AutoArchiveJobs <$> arbitrary <*>
arbitrary
Luxi.ReqCancelJob -> Luxi.CancelJob <$> arbitrary <*> 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 =
(US.parseCall (US.buildCall (Luxi.strOfOp op) (Luxi.opToArgs op))
>>= uncurry Luxi.decodeLuxiCall) ==? Ok op
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 "luxitest"
server <- run $ Luxi.getLuxiServer False fpath
_ <- run . forkIO $
bracket
(Luxi.acceptClient server)
(\c -> Luxi.closeClient c >> Luxi.closeServer server)
luxiServerPong
replies <- run $
bracket
(Luxi.getLuxiClient fpath)
Luxi.closeClient
(`luxiClientPong` msgs)
_ <- stop $ replies ==? msgs
return ()
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:\n" ++
unlines extra_hs) (null extra_hs)
testSuite "Luxi"
[ 'prop_CallEncoding
, 'prop_ClientServer
, 'case_AllDefined
]