From 2733df514086a25ff2c603eb39e6fab82abb9785 Mon Sep 17 00:00:00 2001
From: Iustin Pop <iustin@google.com>
Date: Wed, 29 Aug 2012 17:49:14 +0200
Subject: [PATCH] Split one more module out of QC and add test helpers
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

This splits the confd/utils tests, and adds the TestCommon module for
shared test code.

Signed-off-by: Iustin Pop <iustin@google.com>
Reviewed-by: RenΓ© Nussbaumer <rn@google.com>
---
 Makefile.am                      |   8 +-
 htest/Test/Ganeti/Confd/Utils.hs | 111 +++++++++++++++++++++++
 htest/Test/Ganeti/TestCommon.hs  | 110 +++++++++++++++++++++++
 htest/test.hs                    |   3 +-
 htools/Ganeti/HTools/QC.hs       | 149 +------------------------------
 5 files changed, 230 insertions(+), 151 deletions(-)
 create mode 100644 htest/Test/Ganeti/Confd/Utils.hs
 create mode 100644 htest/Test/Ganeti/TestCommon.hs

diff --git a/Makefile.am b/Makefile.am
index 76d91733e..dabec8241 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -62,7 +62,8 @@ HTOOLS_DIRS = \
 	htools/Ganeti/Query \
 	htest \
 	htest/Test \
-	htest/Test/Ganeti
+	htest/Test/Ganeti \
+	htest/Test/Ganeti/Confd
 
 DIRS = \
 	autotools \
@@ -377,6 +378,7 @@ HPCEXCL = --exclude Main \
 	--exclude Ganeti.HTools.QC \
 	--exclude Ganeti.HTools.Version \
 	--exclude Test.Ganeti.TestHelper \
+	--exclude Test.Ganeti.TestCommon \
 	$(patsubst htools.%,--exclude Test.%,$(subst /,.,$(patsubst %.hs,%, $(filter-out htest/%,$(HS_LIB_SRCS)))))
 
 HS_LIB_SRCS = \
@@ -430,7 +432,9 @@ HS_LIB_SRCS = \
 	htools/Ganeti/Ssconf.hs \
 	htools/Ganeti/THH.hs \
 	htest/Test/Ganeti/TestHelper.hs \
-	htest/Test/Ganeti/Objects.hs
+	htest/Test/Ganeti/TestCommon.hs \
+	htest/Test/Ganeti/Objects.hs \
+	htest/Test/Ganeti/Confd/Utils.hs
 
 
 HS_BUILT_SRCS = htools/Ganeti/HTools/Version.hs htools/Ganeti/Constants.hs
diff --git a/htest/Test/Ganeti/Confd/Utils.hs b/htest/Test/Ganeti/Confd/Utils.hs
new file mode 100644
index 000000000..bc6fa8388
--- /dev/null
+++ b/htest/Test/Ganeti/Confd/Utils.hs
@@ -0,0 +1,111 @@
+{-# LANGUAGE TemplateHaskell #-}
+{-# OPTIONS_GHC -fno-warn-orphans #-}
+
+{-| Unittests for ganeti-htools.
+
+-}
+
+{-
+
+Copyright (C) 2009, 2010, 2011, 2012 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.Confd.Utils (testConfdUtils) where
+
+import Control.Applicative
+import Test.QuickCheck
+import qualified Text.JSON as J
+
+import Test.Ganeti.TestHelper
+import Test.Ganeti.TestCommon
+
+import qualified Ganeti.BasicTypes as BasicTypes
+import qualified Ganeti.Confd as Confd
+import qualified Ganeti.Confd.Utils as Confd.Utils
+import qualified Ganeti.Constants as C
+import qualified Ganeti.Hash as Hash
+
+instance Arbitrary Confd.ConfdRequestType where
+  arbitrary = elements [minBound..maxBound]
+
+instance Arbitrary Confd.ConfdReqField where
+  arbitrary = elements [minBound..maxBound]
+
+instance Arbitrary Confd.ConfdReqQ where
+  arbitrary = Confd.ConfdReqQ <$> arbitrary <*> arbitrary <*>
+              arbitrary <*> arbitrary
+
+instance Arbitrary Confd.ConfdQuery where
+  arbitrary = oneof [ pure Confd.EmptyQuery
+                    , Confd.PlainQuery <$> getName
+                    , Confd.DictQuery <$> arbitrary
+                    ]
+
+instance Arbitrary Confd.ConfdRequest where
+  arbitrary = Confd.ConfdRequest <$> arbitrary <*> arbitrary <*> arbitrary
+              <*> arbitrary
+
+-- | Test that signing messages and checking signatures is correct. It
+-- also tests, indirectly the serialisation of messages so we don't
+-- need a separate test for that.
+prop_ConfdUtils_req_sign :: Hash.HashKey        -- ^ The hash key
+                    -> NonNegative Integer -- ^ The base timestamp
+                    -> Positive Integer    -- ^ Delta for out of window
+                    -> Bool                -- ^ Whether delta should be + or -
+                    -> Confd.ConfdRequest
+                    -> Property
+prop_ConfdUtils_req_sign key (NonNegative timestamp) (Positive bad_delta)
+                         pm crq =
+  forAll (choose (0, fromIntegral C.confdMaxClockSkew)) $ \ good_delta ->
+  let encoded = J.encode crq
+      salt = show timestamp
+      signed = J.encode $ Confd.Utils.signMessage key salt encoded
+      good_timestamp = timestamp + if pm then good_delta else (-good_delta)
+      bad_delta' = fromIntegral C.confdMaxClockSkew + bad_delta
+      bad_timestamp = timestamp + if pm then bad_delta' else (-bad_delta')
+      ts_ok = Confd.Utils.parseMessage key signed good_timestamp
+      ts_bad = Confd.Utils.parseMessage key signed bad_timestamp
+  in printTestCase "Failed to parse good message"
+       (ts_ok ==? BasicTypes.Ok (encoded, crq)) .&&.
+     printTestCase ("Managed to deserialise message with bad\
+                    \ timestamp, got " ++ show ts_bad)
+       (ts_bad ==? BasicTypes.Bad "Too old/too new timestamp or clock skew")
+
+-- | Tests that signing with a different key fails detects failure
+-- correctly.
+prop_ConfdUtils_bad_key :: String             -- ^ Salt
+                   -> Confd.ConfdRequest -- ^ Request
+                   -> Property
+prop_ConfdUtils_bad_key salt crq =
+  -- fixme: we hardcode here the expected length of a sha1 key, as
+  -- otherwise we could have two short keys that differ only in the
+  -- final zero elements count, and those will be expanded to be the
+  -- same
+  forAll (vector 20) $ \key_sign ->
+  forAll (vector 20 `suchThat` (/= key_sign)) $ \key_verify ->
+  let signed = Confd.Utils.signMessage key_sign salt (J.encode crq)
+      encoded = J.encode signed
+  in printTestCase ("Accepted message signed with different key" ++ encoded) $
+    BasicTypes.Bad "HMAC verification failed" ==?
+     Confd.Utils.parseRequest key_verify encoded
+
+testSuite "ConfdUtils"
+  [ 'prop_ConfdUtils_req_sign
+  , 'prop_ConfdUtils_bad_key
+  ]
diff --git a/htest/Test/Ganeti/TestCommon.hs b/htest/Test/Ganeti/TestCommon.hs
new file mode 100644
index 000000000..5ac3f638c
--- /dev/null
+++ b/htest/Test/Ganeti/TestCommon.hs
@@ -0,0 +1,110 @@
+{-| Unittest helpers for ganeti-htools.
+
+-}
+
+{-
+
+Copyright (C) 2009, 2010, 2011, 2012 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.TestCommon where
+
+import Control.Applicative
+import Data.List
+import Test.QuickCheck
+
+-- * Constants
+
+-- | Maximum memory (1TiB, somewhat random value).
+maxMem :: Int
+maxMem = 1024 * 1024
+
+-- | Maximum disk (8TiB, somewhat random value).
+maxDsk :: Int
+maxDsk = 1024 * 1024 * 8
+
+-- | Max CPUs (1024, somewhat random value).
+maxCpu :: Int
+maxCpu = 1024
+
+-- | Max vcpu ratio (random value).
+maxVcpuRatio :: Double
+maxVcpuRatio = 1024.0
+
+-- | Max spindle ratio (random value).
+maxSpindleRatio :: Double
+maxSpindleRatio = 1024.0
+
+-- | Max nodes, used just to limit arbitrary instances for smaller
+-- opcode definitions (e.g. list of nodes in OpTestDelay).
+maxNodes :: Int
+maxNodes = 32
+
+-- | Max opcodes or jobs in a submit job and submit many jobs.
+maxOpCodes :: Int
+maxOpCodes = 16
+
+-- * Helper functions
+
+-- | Checks for equality with proper annotation.
+(==?) :: (Show a, Eq a) => a -> a -> Property
+(==?) x y = printTestCase
+            ("Expected equality, but '" ++
+             show x ++ "' /= '" ++ show y ++ "'") (x == y)
+infix 3 ==?
+
+-- | Show a message and fail the test.
+failTest :: String -> Property
+failTest msg = printTestCase msg False
+
+
+-- * Arbitrary instances
+
+-- | Defines a DNS name.
+newtype DNSChar = DNSChar { dnsGetChar::Char }
+
+instance Arbitrary DNSChar where
+  arbitrary = do
+    x <- elements (['a'..'z'] ++ ['0'..'9'] ++ "_-")
+    return (DNSChar x)
+
+instance Show DNSChar where
+  show = show . dnsGetChar
+
+-- | Generates a single name component.
+getName :: Gen String
+getName = do
+  n <- choose (1, 64)
+  dn <- vector n
+  return (map dnsGetChar dn)
+
+-- | Generates an entire FQDN.
+getFQDN :: Gen String
+getFQDN = do
+  ncomps <- choose (1, 4)
+  names <- vectorOf ncomps getName
+  return $ intercalate "." names
+
+-- | Combinator that generates a 'Maybe' using a sub-combinator.
+getMaybe :: Gen a -> Gen (Maybe a)
+getMaybe subgen = do
+  bool <- arbitrary
+  if bool
+    then Just <$> subgen
+    else return Nothing
diff --git a/htest/test.hs b/htest/test.hs
index d4fefcd7c..2cb65f4d0 100644
--- a/htest/test.hs
+++ b/htest/test.hs
@@ -30,6 +30,7 @@ import Test.Framework
 import System.Environment (getArgs)
 
 import Ganeti.HTools.QC
+import Test.Ganeti.Confd.Utils
 import Test.Ganeti.Objects
 
 -- | Our default test options, overring the built-in test-framework
@@ -71,7 +72,7 @@ allTests =
   , (True, testSsconf)
   , (True, testQlang)
   , (True, testRpc)
-  , (True, testConfd)
+  , (True, testConfdUtils)
   , (True, testObjects)
   , (False, testCluster)
   ]
diff --git a/htools/Ganeti/HTools/QC.hs b/htools/Ganeti/HTools/QC.hs
index 07501420a..60cc24e10 100644
--- a/htools/Ganeti/HTools/QC.hs
+++ b/htools/Ganeti/HTools/QC.hs
@@ -118,37 +118,7 @@ import qualified Ganeti.HTools.Program.Hscan
 import qualified Ganeti.HTools.Program.Hspace
 
 import Test.Ganeti.TestHelper (testSuite)
-
--- * Constants
-
--- | Maximum memory (1TiB, somewhat random value).
-maxMem :: Int
-maxMem = 1024 * 1024
-
--- | Maximum disk (8TiB, somewhat random value).
-maxDsk :: Int
-maxDsk = 1024 * 1024 * 8
-
--- | Max CPUs (1024, somewhat random value).
-maxCpu :: Int
-maxCpu = 1024
-
--- | Max vcpu ratio (random value).
-maxVcpuRatio :: Double
-maxVcpuRatio = 1024.0
-
--- | Max spindle ratio (random value).
-maxSpindleRatio :: Double
-maxSpindleRatio = 1024.0
-
--- | Max nodes, used just to limit arbitrary instances for smaller
--- opcode definitions (e.g. list of nodes in OpTestDelay).
-maxNodes :: Int
-maxNodes = 32
-
--- | Max opcodes or jobs in a submit job and submit many jobs.
-maxOpCodes :: Int
-maxOpCodes = 16
+import Test.Ganeti.TestCommon
 
 -- | All disk templates (used later)
 allDiskTemplates :: [Types.DiskTemplate]
@@ -203,17 +173,6 @@ isFailure :: Types.OpResult a -> Bool
 isFailure (Types.OpFail _) = True
 isFailure _ = False
 
--- | Checks for equality with proper annotation.
-(==?) :: (Show a, Eq a) => a -> a -> Property
-(==?) x y = printTestCase
-            ("Expected equality, but '" ++
-             show x ++ "' /= '" ++ show y ++ "'") (x == y)
-infix 3 ==?
-
--- | Show a message and fail the test.
-failTest :: String -> Property
-failTest msg = printTestCase msg False
-
 -- | Return the python binary to use. If the PYTHON environment
 -- variable is defined, use its value, otherwise use just \"python\".
 pythonCmd :: IO String
@@ -315,41 +274,6 @@ evacModeOptions Types.MirrorNone     = []
 evacModeOptions Types.MirrorInternal = [minBound..maxBound] -- DRBD can do all
 evacModeOptions Types.MirrorExternal = [Types.ChangePrimary, Types.ChangeAll]
 
--- * Arbitrary instances
-
--- | Defines a DNS name.
-newtype DNSChar = DNSChar { dnsGetChar::Char }
-
-instance Arbitrary DNSChar where
-  arbitrary = do
-    x <- elements (['a'..'z'] ++ ['0'..'9'] ++ "_-")
-    return (DNSChar x)
-
-instance Show DNSChar where
-  show = show . dnsGetChar
-
--- | Generates a single name component.
-getName :: Gen String
-getName = do
-  n <- choose (1, 64)
-  dn <- vector n
-  return (map dnsGetChar dn)
-
--- | Generates an entire FQDN.
-getFQDN :: Gen String
-getFQDN = do
-  ncomps <- choose (1, 4)
-  names <- vectorOf ncomps getName
-  return $ intercalate "." names
-
--- | Combinator that generates a 'Maybe' using a sub-combinator.
-getMaybe :: Gen a -> Gen (Maybe a)
-getMaybe subgen = do
-  bool <- arbitrary
-  if bool
-    then Just <$> subgen
-    else return Nothing
-
 -- | Generates a fields list. This uses the same character set as a
 -- DNS name (just for simplicity).
 getFields :: Gen [String]
@@ -2101,74 +2025,3 @@ testSuite "Qlang"
   [ 'prop_Qlang_Serialisation
   , 'prop_Qlang_FilterRegex_instances
   ]
-
-
--- * Confd tests (generic library)
-
-instance Arbitrary Confd.ConfdRequestType where
-  arbitrary = elements [minBound..maxBound]
-
-instance Arbitrary Confd.ConfdReqField where
-  arbitrary = elements [minBound..maxBound]
-
-instance Arbitrary Confd.ConfdReqQ where
-  arbitrary = Confd.ConfdReqQ <$> arbitrary <*> arbitrary <*>
-              arbitrary <*> arbitrary
-
-instance Arbitrary Confd.ConfdQuery where
-  arbitrary = oneof [ pure Confd.EmptyQuery
-                    , Confd.PlainQuery <$> getName
-                    , Confd.DictQuery <$> arbitrary
-                    ]
-
-instance Arbitrary Confd.ConfdRequest where
-  arbitrary = Confd.ConfdRequest <$> arbitrary <*> arbitrary <*> arbitrary
-              <*> arbitrary
-
--- | Test that signing messages and checking signatures is correct. It
--- also tests, indirectly the serialisation of messages so we don't
--- need a separate test for that.
-prop_Confd_req_sign :: Hash.HashKey        -- ^ The hash key
-                    -> NonNegative Integer -- ^ The base timestamp
-                    -> Positive Integer    -- ^ Delta for out of window
-                    -> Bool                -- ^ Whether delta should be + or -
-                    -> Confd.ConfdRequest
-                    -> Property
-prop_Confd_req_sign key (NonNegative timestamp) (Positive bad_delta) pm crq =
-  forAll (choose (0, fromIntegral C.confdMaxClockSkew)) $ \ good_delta ->
-  let encoded = J.encode crq
-      salt = show timestamp
-      signed = J.encode $ Confd.Utils.signMessage key salt encoded
-      good_timestamp = timestamp + if pm then good_delta else (-good_delta)
-      bad_delta' = fromIntegral C.confdMaxClockSkew + bad_delta
-      bad_timestamp = timestamp + if pm then bad_delta' else (-bad_delta')
-      ts_ok = Confd.Utils.parseMessage key signed good_timestamp
-      ts_bad = Confd.Utils.parseMessage key signed bad_timestamp
-  in printTestCase "Failed to parse good message"
-       (ts_ok ==? Types.Ok (encoded, crq)) .&&.
-     printTestCase ("Managed to deserialise message with bad\
-                    \ timestamp, got " ++ show ts_bad)
-       (ts_bad ==? Types.Bad "Too old/too new timestamp or clock skew")
-
--- | Tests that signing with a different key fails detects failure
--- correctly.
-prop_Confd_bad_key :: String             -- ^ Salt
-                   -> Confd.ConfdRequest -- ^ Request
-                   -> Property
-prop_Confd_bad_key salt crq =
-  -- fixme: we hardcode here the expected length of a sha1 key, as
-  -- otherwise we could have two short keys that differ only in the
-  -- final zero elements count, and those will be expanded to be the
-  -- same
-  forAll (vector 20) $ \key_sign ->
-  forAll (vector 20 `suchThat` (/= key_sign)) $ \key_verify ->
-  let signed = Confd.Utils.signMessage key_sign salt (J.encode crq)
-      encoded = J.encode signed
-  in printTestCase ("Accepted message signed with different key" ++ encoded) $
-    Types.Bad "HMAC verification failed" ==?
-     Confd.Utils.parseRequest key_verify encoded
-
-testSuite "Confd"
-  [ 'prop_Confd_req_sign
-  , 'prop_Confd_bad_key
-  ]
-- 
GitLab