On Mon, Sep 26, 2011 at 11:57 AM, Iustin Pop <[email protected]> wrote:
> This makes error message change from "Test 4 failed …" to "Test
> prop_Loader_mergeData failed", which is much more readable. It also
> removes the duplication of test suite names in the test.hs file.
> ---
> htools/Ganeti/HTools/QC.hs | 194
> +++++++++++++++++++-------------------
> htools/Ganeti/HTools/QCHelper.hs | 47 +++++++++
> htools/test.hs | 64 +++++++------
> 3 files changed, 178 insertions(+), 127 deletions(-)
> create mode 100644 htools/Ganeti/HTools/QCHelper.hs
>
> diff --git a/htools/Ganeti/HTools/QC.hs b/htools/Ganeti/HTools/QC.hs
> index cbd9b18..68bcc0d 100644
> --- a/htools/Ganeti/HTools/QC.hs
> +++ b/htools/Ganeti/HTools/QC.hs
> @@ -1,3 +1,5 @@
> +{-# LANGUAGE TemplateHaskell #-}
> +
> {-| Unittests for ganeti-htools.
>
> -}
> @@ -71,8 +73,7 @@ import qualified Ganeti.HTools.Program.Hbal
> import qualified Ganeti.HTools.Program.Hscan
> import qualified Ganeti.HTools.Program.Hspace
>
> -run :: Testable prop => prop -> Args -> IO Result
> -run = flip quickCheckWithResult
> +import Ganeti.HTools.QCHelper (testSuite)
>
> -- * Constants
>
> @@ -364,16 +365,16 @@ prop_Utils_parseUnit (NonNegative n) =
> where _types = n::Int
>
> -- | Test list for the Utils module.
> -testUtils =
> - [ run prop_Utils_commaJoinSplit
> - , run prop_Utils_commaSplitJoin
> - , run prop_Utils_fromObjWithDefault
> - , run prop_Utils_if'if
> - , run prop_Utils_select
> - , run prop_Utils_select_undefd
> - , run prop_Utils_select_undefv
> - , run prop_Utils_parseUnit
> - ]
> +testSuite "Utils"
> + [ 'prop_Utils_commaJoinSplit
> + , 'prop_Utils_commaSplitJoin
> + , 'prop_Utils_fromObjWithDefault
> + , 'prop_Utils_if'if
> + , 'prop_Utils_select
> + , 'prop_Utils_select_undefd
> + , 'prop_Utils_select_undefv
> + , 'prop_Utils_parseUnit
> + ]
>
> -- ** PeerMap tests
>
> @@ -413,13 +414,13 @@ prop_PeerMap_maxElem pmap =
> puniq = PeerMap.accumArray const pmap
>
> -- | List of tests for the PeerMap module.
> -testPeerMap =
> - [ run prop_PeerMap_addIdempotent
> - , run prop_PeerMap_removeIdempotent
> - , run prop_PeerMap_maxElem
> - , run prop_PeerMap_addFind
> - , run prop_PeerMap_findMissing
> - ]
> +testSuite "PeerMap"
> + [ 'prop_PeerMap_addIdempotent
> + , 'prop_PeerMap_removeIdempotent
> + , 'prop_PeerMap_maxElem
> + , 'prop_PeerMap_addFind
> + , 'prop_PeerMap_findMissing
> + ]
>
> -- ** Container tests
>
> @@ -458,11 +459,11 @@ prop_Container_findByName node othername =
> Container.findByName nl' (Node.alias target) == Just target &&
> Container.findByName nl' othername == Nothing
>
> -testContainer =
> - [ run prop_Container_addTwo
> - , run prop_Container_nameOf
> - , run prop_Container_findByName
> - ]
> +testSuite "Container"
> + [ 'prop_Container_addTwo
> + , 'prop_Container_nameOf
> + , 'prop_Container_findByName
> + ]
>
> -- ** Instance tests
>
> @@ -551,24 +552,24 @@ prop_Instance_setMovable inst m =
> Instance.movable inst' == m
> where inst' = Instance.setMovable inst m
>
> -testInstance =
> - [ run prop_Instance_creat
> - , run prop_Instance_setIdx
> - , run prop_Instance_setName
> - , run prop_Instance_setAlias
> - , run prop_Instance_setPri
> - , run prop_Instance_setSec
> - , run prop_Instance_setBoth
> - , run prop_Instance_runStatus_True
> - , run prop_Instance_runStatus_False
> - , run prop_Instance_shrinkMG
> - , run prop_Instance_shrinkMF
> - , run prop_Instance_shrinkCG
> - , run prop_Instance_shrinkCF
> - , run prop_Instance_shrinkDG
> - , run prop_Instance_shrinkDF
> - , run prop_Instance_setMovable
> - ]
> +testSuite "Instance"
> + [ 'prop_Instance_creat
> + , 'prop_Instance_setIdx
> + , 'prop_Instance_setName
> + , 'prop_Instance_setAlias
> + , 'prop_Instance_setPri
> + , 'prop_Instance_setSec
> + , 'prop_Instance_setBoth
> + , 'prop_Instance_runStatus_True
> + , 'prop_Instance_runStatus_False
> + , 'prop_Instance_shrinkMG
> + , 'prop_Instance_shrinkMF
> + , 'prop_Instance_shrinkCG
> + , 'prop_Instance_shrinkCF
> + , 'prop_Instance_shrinkDG
> + , 'prop_Instance_shrinkDF
> + , 'prop_Instance_setMovable
> + ]
>
> -- ** Text backend tests
>
> @@ -663,13 +664,13 @@ prop_Text_NodeLSIdempotent node =
> -- override failN1 to what loadNode returns by default
> where n = node { Node.failN1 = True, Node.offline = False }
>
> -testText =
> - [ run prop_Text_Load_Instance
> - , run prop_Text_Load_InstanceFail
> - , run prop_Text_Load_Node
> - , run prop_Text_Load_NodeFail
> - , run prop_Text_NodeLSIdempotent
> - ]
> +testSuite "Text"
> + [ 'prop_Text_Load_Instance
> + , 'prop_Text_Load_InstanceFail
> + , 'prop_Text_Load_Node
> + , 'prop_Text_Load_NodeFail
> + , 'prop_Text_NodeLSIdempotent
> + ]
>
> -- ** Node tests
>
> @@ -790,7 +791,6 @@ prop_Node_showField node =
> fst (Node.showHeader field) /= Types.unknownField &&
> Node.showField node field /= Types.unknownField
>
> -
> prop_Node_computeGroups nodes =
> let ng = Node.computeGroups nodes
> onlyuuid = map fst ng
> @@ -799,23 +799,22 @@ prop_Node_computeGroups nodes =
> length (nub onlyuuid) == length onlyuuid &&
> (null nodes || not (null ng))
>
> -testNode =
> - [ run prop_Node_setAlias
> - , run prop_Node_setOffline
> - , run prop_Node_setMcpu
> - , run prop_Node_setXmem
> - , run prop_Node_addPriFM
> - , run prop_Node_addPriFD
> - , run prop_Node_addPriFC
> - , run prop_Node_addSec
> - , run prop_Node_rMem
> - , run prop_Node_setMdsk
> - , run prop_Node_tagMaps_idempotent
> - , run prop_Node_tagMaps_reject
> - , run prop_Node_showField
> - , run prop_Node_computeGroups
> - ]
> -
> +testSuite "Node"
> + [ 'prop_Node_setAlias
> + , 'prop_Node_setOffline
> + , 'prop_Node_setMcpu
> + , 'prop_Node_setXmem
> + , 'prop_Node_addPriFM
> + , 'prop_Node_addPriFD
> + , 'prop_Node_addPriFC
> + , 'prop_Node_addSec
> + , 'prop_Node_rMem
> + , 'prop_Node_setMdsk
> + , 'prop_Node_tagMaps_idempotent
> + , 'prop_Node_tagMaps_reject
> + , 'prop_Node_showField
> + , 'prop_Node_computeGroups
> + ]
>
> -- ** Cluster tests
>
> @@ -959,16 +958,16 @@ prop_ClusterSplitCluster node inst =
> all (\(guuid, (nl'', _)) -> all ((== guuid) . Node.group)
> (Container.elems nl'')) gni
>
> -testCluster =
> - [ run prop_Score_Zero
> - , run prop_CStats_sane
> - , run prop_ClusterAlloc_sane
> - , run prop_ClusterCanTieredAlloc
> - , run prop_ClusterAllocEvac
> - , run prop_ClusterAllocBalance
> - , run prop_ClusterCheckConsistency
> - , run prop_ClusterSplitCluster
> - ]
> +testSuite "Cluster"
> + [ 'prop_Score_Zero
> + , 'prop_CStats_sane
> + , 'prop_ClusterAlloc_sane
> + , 'prop_ClusterCanTieredAlloc
> + , 'prop_ClusterAllocEvac
> + , 'prop_ClusterAllocBalance
> + , 'prop_ClusterCheckConsistency
> + , 'prop_ClusterSplitCluster
> + ]
>
> -- ** OpCodes tests
>
> @@ -979,9 +978,8 @@ prop_OpCodes_serialization op =
> J.Ok op' -> op == op'
> where _types = op::OpCodes.OpCode
>
> -testOpCodes =
> - [ run prop_OpCodes_serialization
> - ]
> +testSuite "OpCodes"
> + [ 'prop_OpCodes_serialization ]
>
> -- ** Jobs tests
>
> @@ -998,10 +996,10 @@ prop_JobStatus_serialization js =
> J.Ok js' -> js == js'
> where _types = js::Jobs.JobStatus
>
> -testJobs =
> - [ run prop_OpStatus_serialization
> - , run prop_JobStatus_serialization
> - ]
> +testSuite "Jobs"
> + [ 'prop_OpStatus_serialization
> + , 'prop_JobStatus_serialization
> + ]
>
> -- ** Loader tests
>
> @@ -1046,14 +1044,14 @@ prop_Loader_compareNameComponent_prefix (NonEmpty s1)
> s2 =
> Loader.compareNameComponent (s1 ++ "." ++ s2) s1 ==
> Loader.LookupResult Loader.PartialMatch s1
>
> -testLoader =
> - [ run prop_Loader_lookupNode
> - , run prop_Loader_lookupInstance
> - , run prop_Loader_assignIndices
> - , run prop_Loader_mergeData
> - , run prop_Loader_compareNameComponent_equal
> - , run prop_Loader_compareNameComponent_prefix
> - ]
> +testSuite "Loader"
> + [ 'prop_Loader_lookupNode
> + , 'prop_Loader_lookupInstance
> + , 'prop_Loader_assignIndices
> + , 'prop_Loader_mergeData
> + , 'prop_Loader_compareNameComponent_equal
> + , 'prop_Loader_compareNameComponent_prefix
> + ]
>
> -- ** Types tests
>
> @@ -1090,9 +1088,9 @@ prop_Types_eitherToResult ei =
> where r = Types.eitherToResult ei
> _types = ei::Either String Int
>
> -testTypes =
> - [ run prop_Types_AllocPolicy_serialisation
> - , run prop_Types_DiskTemplate_serialisation
> - , run prop_Types_opToResult
> - , run prop_Types_eitherToResult
> - ]
> +testSuite "Types"
> + [ 'prop_Types_AllocPolicy_serialisation
> + , 'prop_Types_DiskTemplate_serialisation
> + , 'prop_Types_opToResult
> + , 'prop_Types_eitherToResult
> + ]
> diff --git a/htools/Ganeti/HTools/QCHelper.hs
> b/htools/Ganeti/HTools/QCHelper.hs
> new file mode 100644
> index 0000000..5057b5f
> --- /dev/null
> +++ b/htools/Ganeti/HTools/QCHelper.hs
> @@ -0,0 +1,47 @@
> +{-# LANGUAGE TemplateHaskell #-}
> +
> +{-| Unittest helpers for ganeti-htools
> +
> +-}
> +
> +{-
> +
> +Copyright (C) 2011 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 Ganeti.HTools.QCHelper
> + ( testSuite
> + ) where
> +
> +import Test.QuickCheck
> +import Language.Haskell.TH
> +
> +run :: Testable prop => prop -> Args -> IO Result
> +run = flip quickCheckWithResult
> +
> +testSuite :: String -> [Name] -> Q [Dec]
> +testSuite tsname tdef = do
> + let fullname = mkName $ "test" ++ tsname
> + tests <- mapM (\n -> [| (run $(varE n), $(litE (StringL (nameBase n)))) |])
> + tdef
> + sigtype <- [t| (String, [(Args -> IO Result, String)]) |]
> + return $ [ SigD fullname sigtype
> + , ValD (VarP fullname) (NormalB (TupE [LitE (StringL tsname),
> + ListE tests])) []
> + ]
This I find little hard to read, because of the brackets (especially
tests <- ..). And yes, I am a huge fan of '.' and '$' ;)
> diff --git a/htools/test.hs b/htools/test.hs
> index 34bd05a..4c9ddf2 100644
> --- a/htools/test.hs
> +++ b/htools/test.hs
> @@ -27,7 +27,7 @@ module Main(main) where
>
> import Data.IORef
> import Test.QuickCheck
> -import System.Console.GetOpt
> +import System.Console.GetOpt ()
> import System.IO
> import System.Exit
> import System (getArgs)
> @@ -63,55 +63,61 @@ incIORef ir = atomicModifyIORef ir (\x -> (x + 1, ()))
>
> -- | Wrapper over a test runner with error counting.
> wrapTest :: IORef Int
> - -> (Args -> IO Result)
> + -> (Args -> IO Result, String)
> -> Args
> - -> IO (Result, Char)
> -wrapTest ir test opts = do
> + -> IO (Result, Char, String)
> +wrapTest ir (test, desc) opts = do
> r <- test opts
> c <- case r of
> Success {} -> return '.'
> GaveUp {} -> return '?'
> Failure {} -> incIORef ir >> return '#'
> NoExpectedFailure {} -> incIORef ir >> return '*'
> - return (r, c)
> + return (r, c, desc)
> +
> +runTests :: String
> + -> Args
> + -> [Args -> IO (Result, Char, String)]
> + -> Int
> + -> IO [(Result, String)]
>
> runTests name opts tests max_count = do
> _ <- printf "%25s : " name
> hFlush stdout
> results <- mapM (\t -> do
> - (r, c) <- t opts
> + (r, c, desc) <- t opts
> putChar c
> hFlush stdout
> - return r
> + return (r, desc)
> ) tests
> - let alldone = sum . map numTests $ results
> + let alldone = sum . map (numTests . fst) $ results
> _ <- printf "%*s(%d)\n" (max_count - length tests + 1) " " alldone
> - mapM_ (\(idx, r) ->
> + mapM_ (\(r, desc) ->
> case r of
> Failure { output = o, usedSeed = u, usedSize = size } ->
> - printf "Test %d failed (seed was %s, test size %d): %s\n"
> - idx (show u) size o
> + printf "Test %s failed (seed was %s, test size %d): %s\n"
> + desc (show u) size o
> GaveUp { numTests = passed } ->
> - printf "Test %d incomplete: gave up with only %d\
> + printf "Test %s incomplete: gave up with only %d\
> \ passes after discarding %d tests\n"
> - idx passed (maxDiscard opts)
> + desc passed (maxDiscard opts)
> _ -> return ()
> - ) $ zip ([1..]::[Int]) results
> + ) results
> return results
>
> -allTests :: [(String, Args, [Args -> IO Result])]
> +allTests :: [(Args, (String, [(Args -> IO Result, String)]))]
> allTests =
> - [ ("Utils", fast, testUtils)
> - , ("PeerMap", fast, testPeerMap)
> - , ("Container", fast, testContainer)
> - , ("Instance", fast, testInstance)
> - , ("Node", fast, testNode)
> - , ("Text", fast, testText)
> - , ("OpCodes", fast, testOpCodes)
> - , ("Jobs", fast, testJobs)
> - , ("Loader", fast, testLoader)
> - , ("Types", fast, testTypes)
> - , ("Cluster", slow, testCluster)
> + [ (fast, testUtils)
> + , (fast, testPeerMap)
> + , (fast, testContainer)
> + , (fast, testInstance)
> + , (fast, testNode)
> + , (fast, testText)
> + , (fast, testOpCodes)
> + , (fast, testJobs)
> + , (fast, testLoader)
> + , (fast, testTypes)
> + , (slow, testCluster)
> ]
>
> transformTestOpts :: Args -> Options -> IO Args
> @@ -135,9 +141,9 @@ main = do
> (opts, args) <- parseOpts cmd_args "test" options
> let tests = if null args
> then allTests
> - else filter (\(name, _, _) -> name `elem` args) allTests
> - max_count = maximum $ map (\(_, _, t) -> length t) tests
> - mapM_ (\(name, targs, tl) ->
> + else filter (\(_, (name, _)) -> name `elem` args) allTests
> + max_count = maximum $ map (\(_, (_, t)) -> length t) tests
> + mapM_ (\(targs, (name, tl)) ->
> transformTestOpts targs opts >>= \newargs ->
> runTests name newargs (wrap tl) max_count) tests
> terr <- readIORef errs
> --
> 1.7.3.1
>
>
Rest LGTM