{-| Module : Crypto.Lol.Utils.Tests Description : Infrastructure for testing Lol. Copyright : (c) Eric Crockett, 2011-2017 Chris Peikert, 2011-2017 License : GPL-3 Maintainer : ecrockett0@email.com Stability : experimental Portability : POSIX Infrastructure for testing Lol. -} {-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE FlexibleInstances #-} {-# LANGUAGE GADTs #-} {-# LANGUAGE MultiParamTypeClasses #-} {-# LANGUAGE PolyKinds #-} {-# LANGUAGE RankNTypes #-} {-# LANGUAGE ScopedTypeVariables #-} {-# LANGUAGE TypeFamilies #-} module Crypto.Lol.Utils.Tests (nestGroup ,testIOWithGen ,testIOWithoutGen ,testWithGen ,testWithoutGen ,ApproxEqual(..) -- Re-exports ,QCG.choose ,QCG.chooseAny ,TF.testGroup ,QC.Gen ,TF.Test) where import Crypto.Lol.Prelude (Complex, Int64, imag, real) import Crypto.Lol.Types.Unsafe.ZqBasic import qualified Test.Framework as TF import qualified Test.Framework.Providers.QuickCheck2 as QC2 import qualified Test.QuickCheck as QC import qualified Test.QuickCheck.Gen as QCG import qualified Test.QuickCheck.Monadic as QCM -- The allowable distance from 1 the ratio of two Doubles is allowed to be in order for the numbers -- to be considered "approximately equal". That is, x =~= y iff -1-ε < x/y < 1+ε -- In the case that x == 0, we just measure that abs y < ε, and vice-versa for y == 0 approxEqualEpsilon :: Double approxEqualEpsilon = 1e-6 class ApproxEqual a where (=~=) :: a -> a -> Bool instance ApproxEqual Int64 where x =~= y = x == y instance Eq r => ApproxEqual (ZqBasic q r) where x =~= y = x == y instance ApproxEqual Double where x =~= y | x == 0 = abs y < approxEqualEpsilon | y == 0 = abs x < approxEqualEpsilon | isNaN x || isNaN y = False -- Calculate the distance of x/y and y/x from 1 and take the minimum | otherwise = let minDist = minimum $ map (\r -> abs $ abs r - 1) [x/y, y/x] in minDist < approxEqualEpsilon instance ApproxEqual (Complex Double) where x =~= y = (real x =~= real y) && (imag x =~= imag y) -- | Make a 'Test' given a name, a testing function, and a parameter generator testWithGen :: (Show a, QC.Testable prop) => String -> (a -> prop) -> QC.Gen a -> TF.Test testWithGen name f gen = QC2.testProperty name $ QC.forAll gen f -- | Make a 'Test' given a name, a monadic (IO only) testing function, and a parameter generator testIOWithGen :: (Show a, QC.Testable prop) => String -> (a -> IO prop) -> QC.Gen a -> TF.Test testIOWithGen name f gen = QC2.testProperty name $ QCM.monadicIO $ QCM.forAllM gen (QCM.run . f) -- | Make a 'Test' given a name and a 'QC.Testable' value testWithoutGen :: (QC.Testable prop) => String -> prop -> TF.Test testWithoutGen name p = QC2.testProperty name $ QC.property p -- | Make a 'Test' given a name and a monadic (IO only) 'QC.Testable' value testIOWithoutGen :: (QC.Testable prop) => String -> IO prop -> TF.Test testIOWithoutGen name p = QC2.testProperty name $ QCM.monadicIO $ QCM.run p -- | Apply parameters to a list of 'Test'. nestGroup :: String -> [QC.Gen a -> TF.Test] -> QC.Gen a -> TF.Test nestGroup name ts gen = TF.testGroup name $ map ($ gen) ts