<commit_before>{-# OPTIONS_GHC -fno-warn-orphans #-}

{-| Common helper functions and instances for all Ganeti tests.

-}

{-

Copyright (C) 2009, 2010, 2011, 2012, 2013 Google Inc.
All rights reserved.

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:

1. Redistributions of source code must retain the above copyright notice,
this list of conditions and the following disclaimer.

2. Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

-}

module Test.Ganeti.TestCommon
  ( maxMem
  , maxDsk
  , maxCpu
  , maxSpindles
  , maxVcpuRatio
  , maxSpindleRatio
  , maxNodes
  , maxOpCodes
  , (==?)
  , (/=?)
  , failTest
  , passTest
  , stableCover
  , pythonCmd
  , runPython
  , checkPythonResult
  , DNSChar(..)
  , genPrintableAsciiChar
  , genPrintableAsciiString
  , genPrintableAsciiStringNE
  , genName
  , genFQDN
  , genUUID
  , genMaybe
  , genSublist
  , genMap
  , genTags
  , genFields
  , genUniquesList
  , SmallRatio(..)
  , genSetHelper
  , genSet
  , genListSet
  , genAndRestArguments
  , genIPv4Address
  , genIPv4Network
  , genIp6Addr
  , genIp6Net
  , genOpCodesTagName
  , genLuxiTagName
  , netmask2NumHosts
  , testSerialisation
  , testArraySerialisation
  , testDeserialisationFail
  , resultProp
  , readTestData
  , genSample
  , testParser
  , genPropParser
  , genNonNegative
  , relativeError
  , getTempFileName
  ) where

import Control.Applicative
import Control.Exception (catchJust)
import Control.Monad
import Data.Attoparsec.Text (Parser, parseOnly)
import Data.List
import qualified Data.Map as M
import Data.Text (pack)
import Data.Word
import qualified Data.Set as Set
import System.Directory (getTemporaryDirectory, removeFile)
import System.Environment (getEnv)
import System.Exit (ExitCode(..))
import System.IO (hClose, openTempFile)
import System.IO.Error (isDoesNotExistError)
import System.Process (readProcessWithExitCode)
import qualified Test.HUnit as HUnit
import Test.QuickCheck
import Test.QuickCheck.Monadic
import qualified Text.JSON as J
import Numeric

import qualified Ganeti.BasicTypes as BasicTypes
import Ganeti.JSON (ArrayObject(..))
import Ganeti.Types

-- * Arbitrary orphan instances

instance (Ord k, Arbitrary k, Arbitrary a) => Arbitrary (M.Map k a) where
  arbitrary = M.fromList <$> arbitrary
  shrink m = M.fromList <$> shrink (M.toList m)


-- * 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 spindles (1024, somewhat random value).
maxSpindles :: Int
maxSpindles = 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. The first argument is
-- the computed value, the second one the expected value.
(==?) :: (Show a, Eq a) => a -> a -> Property
(==?) x y = printTestCase
            ("Expected equality, but got mismatch\nexpected: " ++
             show y ++ "\n but got: " ++ show x) (x == y)
infix 3 ==?

-- | Checks for inequality with proper annotation. The first argument
-- is the computed value, the second one the expected (not equal)
-- value.
(/=?) :: (Show a, Eq a) => a -> a -> Property
(/=?) x y = printTestCase
            ("Expected inequality, but got equality: '" ++
             show x ++ "'.") (x /= y)
infix 3 /=?

-- | Show a message and fail the test.
failTest :: String -> Property
failTest msg = printTestCase msg False

-- | A 'True' property.
passTest :: Property
passTest = property True

-- | A stable version of QuickCheck's `cover`. In its current implementation,
-- cover will not detect insufficient coverage if the actual coverage in the
-- sample is 0. Work around this by lifting the probability to at least
-- 10 percent.
-- The underlying issue is tracked at
-- https://github.com/nick8325/quickcheck/issues/26
stableCover :: Testable prop => Bool -> Int -> String -> prop -> Property
stableCover pred percent label prop =
  let newlabel = "(stabilized to at least 10%) " ++ label
  in forAll (frequency [(1, return True), (9, return False)]) $ \ basechance ->
     cover (basechance || pred) (10 + (percent * 9 `div` 10)) newlabel prop

-- | Return the python binary to use. If the PYTHON environment
-- variable is defined, use its value, otherwise use just \"python\".
pythonCmd :: IO String
pythonCmd = catchJust (guard . isDoesNotExistError)
            (getEnv "PYTHON") (const (return "python"))

-- | Run Python with an expression, returning the exit code, standard
-- output and error.
runPython :: String -> String -> IO (ExitCode, String, String)<commit_msg>reaname variables to avoid name shadowing<commit_after>{-# OPTIONS_GHC -fno-warn-orphans #-}

{-| Common helper functions and instances for all Ganeti tests.

-}

{-

Copyright (C) 2009, 2010, 2011, 2012, 2013 Google Inc.
All rights reserved.

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:

1. Redistributions of source code must retain the above copyright notice,
this list of conditions and the following disclaimer.

2. Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

-}

module Test.Ganeti.TestCommon
  ( maxMem
  , maxDsk
  , maxCpu
  , maxSpindles
  , maxVcpuRatio
  , maxSpindleRatio
  , maxNodes
  , maxOpCodes
  , (==?)
  , (/=?)
  , failTest
  , passTest
  , stableCover
  , pythonCmd
  , runPython
  , checkPythonResult
  , DNSChar(..)
  , genPrintableAsciiChar
  , genPrintableAsciiString
  , genPrintableAsciiStringNE
  , genName
  , genFQDN
  , genUUID
  , genMaybe
  , genSublist
  , genMap
  , genTags
  , genFields
  , genUniquesList
  , SmallRatio(..)
  , genSetHelper
  , genSet
  , genListSet
  , genAndRestArguments
  , genIPv4Address
  , genIPv4Network
  , genIp6Addr
  , genIp6Net
  , genOpCodesTagName
  , genLuxiTagName
  , netmask2NumHosts
  , testSerialisation
  , testArraySerialisation
  , testDeserialisationFail
  , resultProp
  , readTestData
  , genSample
  , testParser
  , genPropParser
  , genNonNegative
  , relativeError
  , getTempFileName
  ) where

import Control.Applicative
import Control.Exception (catchJust)
import Control.Monad
import Data.Attoparsec.Text (Parser, parseOnly)
import Data.List
import qualified Data.Map as M
import Data.Text (pack)
import Data.Word
import qualified Data.Set as Set
import System.Directory (getTemporaryDirectory, removeFile)
import System.Environment (getEnv)
import System.Exit (ExitCode(..))
import System.IO (hClose, openTempFile)
import System.IO.Error (isDoesNotExistError)
import System.Process (readProcessWithExitCode)
import qualified Test.HUnit as HUnit
import Test.QuickCheck
import Test.QuickCheck.Monadic
import qualified Text.JSON as J
import Numeric

import qualified Ganeti.BasicTypes as BasicTypes
import Ganeti.JSON (ArrayObject(..))
import Ganeti.Types

-- * Arbitrary orphan instances

instance (Ord k, Arbitrary k, Arbitrary a) => Arbitrary (M.Map k a) where
  arbitrary = M.fromList <$> arbitrary
  shrink m = M.fromList <$> shrink (M.toList m)


-- * 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 spindles (1024, somewhat random value).
maxSpindles :: Int
maxSpindles = 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. The first argument is
-- the computed value, the second one the expected value.
(==?) :: (Show a, Eq a) => a -> a -> Property
(==?) x y = printTestCase
            ("Expected equality, but got mismatch\nexpected: " ++
             show y ++ "\n but got: " ++ show x) (x == y)
infix 3 ==?

-- | Checks for inequality with proper annotation. The first argument
-- is the computed value, the second one the expected (not equal)
-- value.
(/=?) :: (Show a, Eq a) => a -> a -> Property
(/=?) x y = printTestCase
            ("Expected inequality, but got equality: '" ++
             show x ++ "'.") (x /= y)
infix 3 /=?

-- | Show a message and fail the test.
failTest :: String -> Property
failTest msg = printTestCase msg False

-- | A 'True' property.
passTest :: Property
passTest = property True

-- | A stable version of QuickCheck's `cover`. In its current implementation,
-- cover will not detect insufficient coverage if the actual coverage in the
-- sample is 0. Work around this by lifting the probability to at least
-- 10 percent.
-- The underlying issue is tracked at
-- https://github.com/nick8325/quickcheck/issues/26
stableCover :: Testable prop => Bool -> Int -> String -> prop -> Property
stableCover p percent s prop =
  let newlabel = "(stabilized to at least 10%) " ++ s
  in forAll (frequency [(1, return True), (9, return False)]) $ \ basechance ->
     cover (basechance || p) (10 + (percent * 9 `div` 10)) newlabel prop

-- | Return the python binary to use. If the PYTHON environment
-- variable is defined, use its value, otherwise use just \"python\".
pythonCmd :: IO String
pythonCmd = catchJust (guard . isDoesNotExistError)
            (getEnv "PYTHON") (const (return "python"))

-- | Run Python with an expression, returning the exit code, standard
-- output and error.
runPython :: String -> String -> IO (ExitCode, String, String)
{'fraction_non_alphanumeric': 0.09245220830586684, 'fraction_numerical': 0.015820698747528016, 'mean_word_length': 3.741015625, 'pattern_counts': {'":': 0, '<': 7, '<?xml version=': 0, '>': 39, 'https://': 2, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '1191374', 'n_tokens_mistral': 3843, 'n_tokens_neox': 3527, 'n_words': 1422}