blob: b9a505f0caed88ec577f5d24f801e66338175a60 [file] [log] [blame]
{-# LANGUAGE TemplateHaskell #-}
{-# OPTIONS_GHC -fno-warn-orphans #-}
{-| Unittests for the 'Ganeti.Common' module.
-}
{-
Copyright (C) 2009, 2010, 2011, 2012, 2013 Google Inc.
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 2 of the License, or
(at your option) 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, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
02110-1301, USA.
-}
module Test.Ganeti.Common
( testCommon
, checkOpt
, passFailOpt
, checkEarlyExit
) where
import Test.QuickCheck hiding (Result)
import Test.HUnit
import qualified System.Console.GetOpt as GetOpt
import System.Exit
import Test.Ganeti.TestHelper
import Test.Ganeti.TestCommon
import Ganeti.BasicTypes
import Ganeti.Common
import Ganeti.HTools.Program.Main (personalities)
{-# ANN module "HLint: ignore Use camelCase" #-}
-- | Helper to check for correct parsing of an option.
checkOpt :: (StandardOptions b) =>
(a -> Maybe String) -- ^ Converts the value into a cmdline form
-> b -- ^ The default options
-> (String -> c) -- ^ Fail test function
-> (String -> d -> d -> c) -- ^ Check for equality function
-> (a -> d) -- ^ Transforms the value to a compare val
-> (a, GenericOptType b, b -> d) -- ^ Triple of value, the
-- option, function to
-- extract the set value
-- from the options
-> c
checkOpt repr defaults failfn eqcheck valfn
(val, opt@(GetOpt.Option _ longs _ _, _), fn) =
case longs of
[] -> failfn "no long options?"
cmdarg:_ ->
case parseOptsInner defaults
["--" ++ cmdarg ++ maybe "" ("=" ++) (repr val)]
"prog" [opt] [] of
Left e -> failfn $ "Failed to parse option '" ++ cmdarg ++ ": " ++
show e
Right (options, _) -> eqcheck ("Wrong value in option " ++
cmdarg ++ "?") (valfn val) (fn options)
-- | Helper to check for correct and incorrect parsing of an option.
passFailOpt :: (StandardOptions b) =>
b -- ^ The default options
-> (String -> c) -- ^ Fail test function
-> c -- ^ Pass function
-> (GenericOptType b, String, String)
-- ^ The list of enabled options, fail value and pass value
-> c
passFailOpt defaults failfn passfn
(opt@(GetOpt.Option _ longs _ _, _), bad, good) =
let first_opt = case longs of
[] -> error "no long options?"
x:_ -> x
prefix = "--" ++ first_opt ++ "="
good_cmd = prefix ++ good
bad_cmd = prefix ++ bad in
case (parseOptsInner defaults [bad_cmd] "prog" [opt] [],
parseOptsInner defaults [good_cmd] "prog" [opt] []) of
(Left _, Right _) -> passfn
(Right _, Right _) -> failfn $ "Command line '" ++ bad_cmd ++
"' succeeded when it shouldn't"
(Left _, Left _) -> failfn $ "Command line '" ++ good_cmd ++
"' failed when it shouldn't"
(Right _, Left _) ->
failfn $ "Command line '" ++ bad_cmd ++
"' succeeded when it shouldn't, while command line '" ++
good_cmd ++ "' failed when it shouldn't"
-- | Helper to test that a given option is accepted OK with quick exit.
checkEarlyExit :: (StandardOptions a) =>
a -> String -> [GenericOptType a] -> [ArgCompletion]
-> Assertion
checkEarlyExit defaults name options arguments =
mapM_ (\param ->
case parseOptsInner defaults [param] name options arguments of
Left (code, _) ->
assertEqual ("Program " ++ name ++
" returns invalid code " ++ show code ++
" for option " ++ param) ExitSuccess code
_ -> assertFailure $ "Program " ++ name ++
" doesn't consider option " ++
param ++ " as early exit one"
) ["-h", "--help", "-V", "--version"]
-- | Test parseYesNo.
prop_parse_yes_no :: Bool -> Bool -> String -> Property
prop_parse_yes_no def testval val =
forAll (elements [val, "yes", "no"]) $ \actual_val ->
if testval
then parseYesNo def Nothing ==? Ok def
else let result = parseYesNo def (Just actual_val)
in if actual_val `elem` ["yes", "no"]
then result ==? Ok (actual_val == "yes")
else property $ isBad result
-- | Check that formatCmdUsage works similar to Python _FormatUsage.
case_formatCommands :: Assertion
case_formatCommands =
assertEqual "proper wrap for HTools Main"
resCmdTest (formatCommands personalities)
where resCmdTest :: [String]
resCmdTest =
[ " hail - Ganeti IAllocator plugin that implements the instance\
\ placement and"
, " movement using the same algorithm as hbal(1)"
, " harep - auto-repair tool that detects certain kind of problems\
\ with instances"
, " and applies the allowed set of solutions"
, " hbal - cluster balancer that looks at the current state of\
\ the cluster and"
, " computes a series of steps designed to bring the\
\ cluster into a"
, " better state"
, " hcheck - cluster checker; prints information about cluster's\
\ health and checks"
, " whether a rebalance done using hbal would help"
, " hinfo - cluster information printer; it prints information\
\ about the current"
, " cluster state and its residing nodes/instances"
, " hroller - cluster rolling maintenance helper; it helps\
\ scheduling node reboots"
, " in a manner that doesn't conflict with the instances'\
\ topology"
, " hscan - tool for scanning clusters via RAPI and saving their\
\ data in the"
, " input format used by hbal(1) and hspace(1)"
, " hspace - computes how many additional instances can be fit on a\
\ cluster, while"
, " maintaining N+1 status."
]
testSuite "Common"
[ 'prop_parse_yes_no
, 'case_formatCommands
]