-- Copyright (c) 2013-2014 PivotCloud, Inc. -- -- Utils -- -- Please feel free to contact us at licensing@pivotmail.com with any -- contributions, additions, or other feedback; we would love to hear from -- you. -- -- Licensed under the Apache License, Version 2.0 (the "License"); you may -- not use this file except in compliance with the License. You may obtain a -- copy of the License at http://www.apache.org/licenses/LICENSE-2.0 -- -- Unless required by applicable law or agreed to in writing, software -- distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -- WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -- License for the specific language governing permissions and limitations -- under the License. -- | -- Module: Utils -- Copyright: Copyright (c) 2013-2014 PivotCloud, Inc. -- license: Apache License, Version 2.0 -- Maintainer: Lars Kuhtz -- Stability: experimental -- -- Utils for Tests for Haskell AWS bindints {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE ScopedTypeVariables #-} {-# LANGUAGE GADTs #-} {-# LANGUAGE CPP #-} module Utils ( -- * Parameters testRegion , testDataPrefix -- * General Utils , sshow , tryT , retryT , testData , evalTestT , evalTestTM , eitherTOnceTest0 , eitherTOnceTest1 , eitherTOnceTest2 -- * Generic Tests , test_jsonRoundtrip , prop_jsonRoundtrip ) where import Aws.General import Control.Concurrent (threadDelay) import Control.Error import Control.Monad import Control.Monad.Identity import Control.Monad.IO.Class import Data.Aeson (FromJSON, ToJSON, encode, eitherDecode) import Data.Monoid import Data.Proxy import Data.String import qualified Data.Text as T import Data.Typeable import Test.QuickCheck.Property import Test.QuickCheck.Monadic import Test.Tasty import Test.Tasty.QuickCheck -- -------------------------------------------------------------------------- -- -- Static Test parameters -- -- TODO make these configurable testRegion :: Region testRegion = UsWest2 -- | This prefix is used for the IDs and names of all entities that are -- created in the AWS account. -- testDataPrefix :: IsString a => a testDataPrefix = "__TEST_AWSHASKELLBINDINGS__" -- -------------------------------------------------------------------------- -- -- General Utils tryT :: MonadIO m => IO a -> EitherT T.Text m a tryT = fmapLT (T.pack . show) . syncIO testData :: (IsString a, Monoid a) => a -> a testData a = testDataPrefix <> a retryT :: MonadIO m => Int -> EitherT T.Text m a -> EitherT T.Text m a retryT i f = go 1 where go x | x >= i = fmapLT (\e -> "error after " <> sshow x <> " retries: " <> e) f | otherwise = f `catchT` \_ -> do liftIO $ threadDelay (1000000 * min 60 (2^(x-1))) go (succ x) sshow :: (Show a, IsString b) => a -> b sshow = fromString . show evalTestTM :: Functor f => String -- ^ test name -> f (EitherT T.Text IO a) -- ^ test -> f (PropertyM IO Bool) evalTestTM name = fmap $ (liftIO . runEitherT) >=> \r -> case r of Left e -> fail $ "failed to run test \"" <> name <> "\": " <> show e Right _ -> return True evalTestT :: String -- ^ test name -> EitherT T.Text IO a -- ^ test -> PropertyM IO Bool evalTestT name = runIdentity . evalTestTM name . Identity eitherTOnceTest0 :: String -- ^ test name -> EitherT T.Text IO a -- ^ test -> TestTree eitherTOnceTest0 name test = testProperty name . once . monadicIO $ evalTestT name test eitherTOnceTest1 :: (Arbitrary a, Show a) => String -- ^ test name -> (a -> EitherT T.Text IO b) -> TestTree eitherTOnceTest1 name test = testProperty name . once $ monadicIO . evalTestTM name test eitherTOnceTest2 :: (Arbitrary a, Show a, Arbitrary b, Show b) => String -- ^ test name -> (a -> b -> EitherT T.Text IO c) -> TestTree eitherTOnceTest2 name test = testProperty name . once $ \a b -> monadicIO $ (evalTestTM name $ uncurry test) (a, b) -- -------------------------------------------------------------------------- -- -- Generic Tests test_jsonRoundtrip :: forall a . (Eq a, Show a, FromJSON a, ToJSON a, Typeable a, Arbitrary a) => Proxy a -> TestTree test_jsonRoundtrip proxy = testProperty msg (prop_jsonRoundtrip :: a -> Property) where msg = "JSON roundtrip for " <> show typ #if MIN_VERSION_base(4,7,0) typ = typeRep proxy #else typ = typeOf (undefined :: a) #endif prop_jsonRoundtrip :: forall a . (Eq a, Show a, FromJSON a, ToJSON a) => a -> Property prop_jsonRoundtrip a = either (const $ property False) (\(b :: [a]) -> [a] === b) $ eitherDecode $ encode [a]