add tests, change module structure, api.

This commit is contained in:
philopon 2014-08-27 02:41:27 +09:00
parent 08973f15ba
commit 5696afaa75
11 changed files with 703 additions and 296 deletions

View File

@ -12,8 +12,12 @@ cabal-version: >=1.10
library
exposed-modules: Database.Memcached.Binary
Database.Memcached.Binary.Exception
Database.Memcached.Binary.Maybe
Database.Memcached.Binary.Either
Database.Memcached.Binary.IO
Database.Memcached.Binary.Types
Database.Memcached.Binary.Types.Exception
Database.Memcached.Binary.Internal
Database.Memcached.Binary.Internal.Definition
build-depends: base >=4.6 && <4.8
@ -40,4 +44,6 @@ test-suite test
, test-framework-hunit >=0.3 && <0.4
, process >=1.2 && <1.3
, network >=2.6 && <2.7
, HUnit >=1.2 && <1.3
, bytestring >=0.10 && <0.11
default-language: Haskell2010

View File

@ -1,291 +1,5 @@
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE NoMonomorphismRestriction #-}
module Database.Memcached.Binary
( -- * connection
I.Connection, I.withConnection, I.connect, I.close
-- * get
, get, get_
, get', get'_
, getMaybe, getMaybe_
-- * set
, set, add, replace
, set', add', replace'
-- * delete
, delete, delete'
-- * increment/decrement
, increment, decrement
, increment', decrement'
-- * flush
, flushAll, flushAll'
-- * version
, version, version'
-- * noOp
, noOp, noOp'
-- * append/prepend
, append, prepend
, append', prepend'
-- * touch
, touch, getAndTouch, getAndTouch_, getMaybeAndTouch
, touch', getAndTouch', getAndTouch'_, getMaybeAndTouch_
-- * modify
, modify , modify_
-- * reexports
, module Database.Memcached.Binary.Types
-- | def
, module Data.Default.Class
-- | PortID(..)
, module Network
module Database.Memcached.Binary {-# WARNING "use Database.Memcached.Binary.Maybe(or Either, IO) instead." #-}
( module Database.Memcached.Binary.Maybe
) where
import Control.Exception
import Network(PortID(..))
import Data.Default.Class(def)
import qualified Data.ByteString as S
import Database.Memcached.Binary.Types
import Database.Memcached.Binary.Exception
import Database.Memcached.Binary.Internal.Definition
import qualified Database.Memcached.Binary.Internal as I
failureIO :: I.Failure a
failureIO w m = throwIO $ MemcachedException w m
failureMaybe :: I.Failure (Maybe a)
failureMaybe _ _ = return Nothing
failureBool :: I.Failure Bool
failureBool _ _ = return False
--------------------------------------------------------------------------------
-- | get value and flags. if error occured, throw MemcachedException.
get :: Key -> I.Connection -> IO (Flags, Value)
get = I.useConnection . I.get (\_ f v -> return (f,v)) failureIO
-- | get value and flags. if error occured, return Nothing.
--
-- @
-- get' == getMaybe
-- @
getMaybe, get' :: Key -> I.Connection -> IO (Maybe (Flags, Value))
getMaybe = I.useConnection . I.get (\_ f v -> return $ Just (f,v)) failureMaybe
get' = getMaybe
-- | get value. if error occured, throw MemcachedException.
get_ :: Key -> I.Connection -> IO Value
get_ = I.useConnection . I.get (\_ _ v -> return v) failureIO
-- | get value. if error occured, return Nothing.
--
-- @
-- get'_ == getMaybe_
-- @
getMaybe_, get'_ :: Key -> I.Connection -> IO (Maybe Value)
getMaybe_ = I.useConnection . I.get (\_ _ v -> return $ Just v) failureMaybe
get'_ = getMaybe_
--------------------------------------------------------------------------------
setAddReplace :: OpCode -> Flags -> Expiry
-> Key -> Value -> I.Connection -> IO ()
setAddReplace op = \f e key value -> I.useConnection $
I.setAddReplace (const $ return ()) failureIO op (CAS 0) key value f e
-- | set value. if error occured, throw MemcachedException.
set :: Flags -> Expiry -> Key -> Value -> I.Connection -> IO ()
set = setAddReplace opSet
-- | add value. if error occured, throw MemcachedException.
add :: Flags -> Expiry -> Key -> Value -> I.Connection -> IO ()
add = setAddReplace opAdd
-- | replace value. if error occured, throw MemcachedException.
replace :: Flags -> Expiry -> Key -> Value -> I.Connection -> IO ()
replace = setAddReplace opReplace
setAddReplace' :: OpCode -> Flags -> Expiry
-> Key -> Value -> I.Connection -> IO Bool
setAddReplace' op = \f e key value -> I.useConnection $
I.setAddReplace (const $ return True) failureBool op (CAS 0) key value f e
-- | set value. if error occured, return False.
set' :: Flags -> Expiry -> Key -> Value -> I.Connection -> IO Bool
set' = setAddReplace' opSet
-- | add value. if error occured, return False.
add' :: Flags -> Expiry -> Key -> Value -> I.Connection -> IO Bool
add' = setAddReplace' opAdd
-- | replace value. if error occured, return False.
replace' :: Flags -> Expiry -> Key -> Value -> I.Connection -> IO Bool
replace' = setAddReplace' opReplace
--------------------------------------------------------------------------------
-- | delete value. if error occured, throw MemcachedException.
delete :: Key -> I.Connection -> IO ()
delete = I.useConnection . I.delete (\_ -> return ()) failureIO (CAS 0)
-- | delete value. if error occured, return False.
delete' :: Key -> I.Connection -> IO Bool
delete' = I.useConnection . I.delete (\_ -> return True) failureBool (CAS 0)
--------------------------------------------------------------------------------
-- | modify value in transaction. if error occured, throw MemcachedException.
modify :: Expiry -> Key -> (Flags -> Value -> (Flags, Value, a))
-> I.Connection -> IO a
modify e key fn = I.useConnection $ \h ->
I.get (\c f v ->
let (f', v', r) = fn f v
in I.setAddReplace (const $ return r) failureIO opSet c key v' f' e h
) failureIO key h
-- | modify value in transaction. if error occured, throw MemcachedException.
modify_ :: Expiry
-> Key -> (Flags -> Value -> (Flags, Value))
-> I.Connection -> IO ()
modify_ e key fn = I.useConnection $ \h ->
I.get (\c f v ->
let (f', v') = fn f v
in I.setAddReplace (const $ return ()) failureIO opSet c key v' f' e h
) failureIO key h
--------------------------------------------------------------------------------
incrDecr :: OpCode -> Expiry
-> Key -> Delta -> Initial -> I.Connection -> IO Counter
incrDecr op = \e k d i -> I.useConnection $
I.incrDecr (\_ w -> return w) failureIO op (CAS 0) k d i e
incrDecr' :: OpCode -> Expiry
-> Key -> Delta -> Initial -> I.Connection -> IO (Maybe Counter)
incrDecr' op e k d i = I.useConnection $
I.incrDecr (\_ w -> return $ Just w) failureMaybe op (CAS 0) k d i e
-- | increment value. if error occured, throw MemcachedException.
increment :: Expiry -> Key -> Delta -> Initial -> I.Connection -> IO Counter
increment = incrDecr opIncrement
-- | decrement value. if error occured, throw MemcachedException.
decrement :: Expiry -> Key -> Delta -> Initial -> I.Connection -> IO Counter
decrement = incrDecr opDecrement
-- | increment value. if error occured, return Nothing.
increment' :: Expiry -> Key -> Delta -> Initial
-> I.Connection -> IO (Maybe Counter)
increment' = incrDecr' opIncrement
-- | decrement value. if error occured, return Nothing.
decrement' :: Expiry -> Key -> Delta -> Initial
-> I.Connection -> IO (Maybe Counter)
decrement' = incrDecr' opDecrement
--------------------------------------------------------------------------------
-- | flush all value. if error occured, throw MemcachedException.
flushAll :: I.Connection -> IO ()
flushAll = I.useConnection $ I.flushAll (return ()) failureIO
-- | flush all value. if error occured, return False.
flushAll' :: I.Connection -> IO Bool
flushAll' = I.useConnection $ I.flushAll (return True) failureBool
--------------------------------------------------------------------------------
-- | get version string. if error occured, throw MemcachedException.
version :: I.Connection -> IO S.ByteString
version = I.useConnection $ I.version return failureIO
-- | get version string. if error occured, return False.
version' :: I.Connection -> IO (Maybe S.ByteString)
version' = I.useConnection $ I.version (return . Just) failureMaybe
--------------------------------------------------------------------------------
-- | noop(use for keepalive). if error occured, throw MemcachedException.
noOp :: I.Connection -> IO ()
noOp = I.useConnection $ I.noOp (return ()) failureIO
-- | noop(use for keepalive). if error occured, return False.
noOp' :: I.Connection -> IO Bool
noOp' = I.useConnection $ I.noOp (return True) failureBool
--------------------------------------------------------------------------------
appendPrepend :: OpCode -> Key -> Value -> I.Connection -> IO ()
appendPrepend o = \k v -> I.useConnection $
I.appendPrepend (\_ -> return ()) failureIO o (CAS 0) k v
appendPrepend' :: OpCode -> Key -> Value -> I.Connection -> IO Bool
appendPrepend' o = \k v -> I.useConnection $
I.appendPrepend (\_ -> return False) failureBool o (CAS 0) k v
-- | apeend value. if error occured, throw MemcachedException.
append :: Key -> Value -> I.Connection -> IO ()
append = appendPrepend opAppend
-- | prepend value. if error occured, throw MemcachedException.
prepend :: Key -> Value -> I.Connection -> IO ()
prepend = appendPrepend opPrepend
-- | append value. if error occured, return False.
append' :: Key -> Value -> I.Connection -> IO Bool
append' = appendPrepend' opAppend
-- | preppend value. if error occured, return False.
prepend' :: Key -> Value -> I.Connection -> IO Bool
prepend' = appendPrepend' opPrepend
--------------------------------------------------------------------------------
-- | change expiry. if error occured, throw MemcachedException.
touch :: Key -> Expiry -> I.Connection -> IO ()
touch k e = I.useConnection $
I.touch (\_ _ _ -> return ()) failureIO opTouch k e
-- | change expiry. if error occured, return False.
touch' :: Key -> Expiry -> I.Connection -> IO Bool
touch' k e = I.useConnection $
I.touch (\_ _ _ -> return True) failureBool opTouch k e
-- | get value and flags, then change expiry.
-- if error occured, throw MemcachedException.
getAndTouch :: Key -> Expiry -> I.Connection -> IO (Flags, Value)
getAndTouch k e = I.useConnection $
I.touch (\_ f v -> return (f,v)) failureIO opGAT k e
-- | get value and flags, then change expiry.
-- if error occured, return Nothing.
--
-- @
-- getMaybeAndTouch == getAndTouch'
-- @
getAndTouch', getMaybeAndTouch
:: Key -> Expiry -> I.Connection -> IO (Maybe (Flags, Value))
getAndTouch' k e = I.useConnection $
I.touch (\_ f v -> return $ Just (f,v)) failureMaybe opGAT k e
getMaybeAndTouch = getAndTouch'
-- | get value then change expiry.
-- if error occured, throw MemcachedException.
getAndTouch_ :: Key -> Expiry -> I.Connection -> IO Value
getAndTouch_ k e = I.useConnection $
I.touch (\_ _ v -> return v) failureIO opGAT k e
-- | get value then change expiry.
-- if error occured, return Nothing.
--
-- @
-- getMaybeAndTouch_ == getAndTouch'_
-- @
getAndTouch'_, getMaybeAndTouch_
:: Key -> Expiry -> I.Connection -> IO (Maybe Value)
getAndTouch'_ k e = I.useConnection $
I.touch (\_ _ v -> return $ Just v) failureMaybe opGAT k e
getMaybeAndTouch_ = getAndTouch'_
import Database.Memcached.Binary.Maybe

View File

@ -0,0 +1,116 @@
--------------------------------------------------------------------------------
get :: Key -> I.Connection -> IO (HasReturn (Flags, Value))
get = I.useConnection . I.get (\_ f v -> successHasReturn (f,v)) failureHasReturn
get_ :: Key -> I.Connection -> IO (HasReturn Value)
get_ = I.useConnection . I.get (\_ _ v -> successHasReturn v) failureHasReturn
--------------------------------------------------------------------------------
setAddReplace :: OpCode -> Flags -> Expiry
-> Key -> Value -> I.Connection -> IO NoReturn
setAddReplace op = \f e key value -> I.useConnection $
I.setAddReplace (const $ successNoReturn) failureNoReturn op (CAS 0) key value f e
set :: Flags -> Expiry -> Key -> Value -> I.Connection -> IO NoReturn
set = setAddReplace opSet
add :: Flags -> Expiry -> Key -> Value -> I.Connection -> IO NoReturn
add = setAddReplace opAdd
replace :: Flags -> Expiry -> Key -> Value -> I.Connection -> IO NoReturn
replace = setAddReplace opReplace
--------------------------------------------------------------------------------
delete :: Key -> I.Connection -> IO NoReturn
delete = I.useConnection . I.delete (\_ -> successNoReturn) failureNoReturn (CAS 0)
--------------------------------------------------------------------------------
-- | modify value in transaction.
modify :: Expiry -> Key -> (Flags -> Value -> (Flags, Value, a))
-> I.Connection -> IO (HasReturn a)
modify e key fn = I.useConnection $ \h ->
I.get (\c f v ->
let (f', v', r) = fn f v
in I.setAddReplace (const $ successHasReturn r)
failureHasReturn opSet c key v' f' e h
) failureHasReturn key h
-- | modify value in transaction.
modify_ :: Expiry
-> Key -> (Flags -> Value -> (Flags, Value))
-> I.Connection -> IO NoReturn
modify_ e key fn = I.useConnection $ \h ->
I.get (\c f v ->
let (f', v') = fn f v
in I.setAddReplace (const $ successNoReturn)
failureNoReturn opSet c key v' f' e h
) failureNoReturn key h
--------------------------------------------------------------------------------
incrDecr :: OpCode -> Expiry
-> Key -> Delta -> Initial -> I.Connection -> IO (HasReturn Counter)
incrDecr op e k d i = I.useConnection $
I.incrDecr (\_ w -> successHasReturn w) failureHasReturn op (CAS 0) k d i e
increment :: Expiry -> Key -> Delta -> Initial
-> I.Connection -> IO (HasReturn Counter)
increment = incrDecr opIncrement
decrement :: Expiry -> Key -> Delta -> Initial
-> I.Connection -> IO (HasReturn Counter)
decrement = incrDecr opDecrement
--------------------------------------------------------------------------------
-- | flush all value.
flushAll :: I.Connection -> IO NoReturn
flushAll = I.useConnection $ I.flushAll successNoReturn failureNoReturn
--------------------------------------------------------------------------------
-- | get version string.
version :: I.Connection -> IO (HasReturn S.ByteString)
version = I.useConnection $ I.version successHasReturn failureHasReturn
--------------------------------------------------------------------------------
-- | noop(use for keepalive).
noOp :: I.Connection -> IO NoReturn
noOp = I.useConnection $ I.noOp successNoReturn failureNoReturn
--------------------------------------------------------------------------------
appendPrepend :: OpCode -> Key -> Value -> I.Connection -> IO NoReturn
appendPrepend o = \k v -> I.useConnection $
I.appendPrepend (\_ -> successNoReturn) failureNoReturn o (CAS 0) k v
append :: Key -> Value -> I.Connection -> IO NoReturn
append = appendPrepend opAppend
prepend :: Key -> Value -> I.Connection -> IO NoReturn
prepend = appendPrepend opPrepend
--------------------------------------------------------------------------------
-- | change expiry.
touch :: Expiry -> Key -> I.Connection -> IO NoReturn
touch e k = I.useConnection $
I.touch (\_ _ _ -> successNoReturn) failureNoReturn opTouch k e
-- | get value/change expiry.
getAndTouch :: Expiry -> Key -> I.Connection -> IO (HasReturn (Flags, Value))
getAndTouch e k = I.useConnection $
I.touch (\_ f v -> successHasReturn (f,v)) failureHasReturn opGAT k e
-- | get value/change expiry.
getAndTouch_ :: Expiry -> Key -> I.Connection -> IO (HasReturn Value)
getAndTouch_ e k = I.useConnection $
I.touch (\_ _ v -> successHasReturn v) failureHasReturn opGAT k e

View File

@ -0,0 +1,28 @@
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE NoMonomorphismRestriction #-}
{-# LANGUAGE CPP #-}
module Database.Memcached.Binary.Either
#include "Header.txt"
#define NoReturn (Maybe MemcachedException)
#define HasReturn Either MemcachedException
successHasReturn :: a -> IO (HasReturn a)
successHasReturn = return . Right
{-# INLINE successHasReturn #-}
successNoReturn :: IO NoReturn
successNoReturn = return Nothing
{-# INLINE successNoReturn #-}
failureHasReturn :: I.Failure (HasReturn a)
failureHasReturn i m = return . Left $ MemcachedException i m
{-# INLINE failureHasReturn #-}
failureNoReturn :: I.Failure NoReturn
failureNoReturn i m = return . Just $ MemcachedException i m
{-# INLINE failureNoReturn #-}
#include "Common.hs"

View File

@ -0,0 +1,46 @@
( -- * connection
I.Connection, I.withConnection, I.connect, I.close
-- * get
, get, get_
-- * set
, set, add, replace
-- * delete
, delete
-- * increment/decrement
, increment, decrement
-- * flush
, flushAll
-- * version
, version
-- * noOp
, noOp
-- * append/prepend
, append, prepend
-- * touch
, touch
, getAndTouch
, getAndTouch_
-- * modify
, modify, modify_
-- * reexports
, module Database.Memcached.Binary.Types
, module Database.Memcached.Binary.Types.Exception
-- | def
, module Data.Default.Class
-- | PortID(..)
, module Network
) where
import Network(PortID(..))
import Data.Default.Class(def)
import qualified Data.ByteString as S
import Database.Memcached.Binary.Types
import Database.Memcached.Binary.Types.Exception
import Database.Memcached.Binary.Internal.Definition
import qualified Database.Memcached.Binary.Internal as I

View File

@ -0,0 +1,30 @@
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE NoMonomorphismRestriction #-}
{-# LANGUAGE CPP #-}
module Database.Memcached.Binary.IO
#include "Header.txt"
import Control.Exception
#define NoReturn ()
#define HasReturn
successHasReturn :: a -> IO (HasReturn a)
successHasReturn = return
{-# INLINE successHasReturn #-}
successNoReturn :: IO NoReturn
successNoReturn = return ()
{-# INLINE successNoReturn #-}
failureHasReturn :: I.Failure (HasReturn a)
failureHasReturn i m = throwIO $ MemcachedException i m
{-# INLINE failureHasReturn #-}
failureNoReturn :: I.Failure NoReturn
failureNoReturn i m = throwIO $ MemcachedException i m
{-# INLINE failureNoReturn #-}
#include "Common.hs"

View File

@ -27,7 +27,7 @@ import qualified Data.ByteString.Lazy as L
import qualified Data.ByteString.Unsafe as S
import Database.Memcached.Binary.Types
import Database.Memcached.Binary.Exception
import Database.Memcached.Binary.Types.Exception
import Database.Memcached.Binary.Internal.Definition
data Connection

View File

@ -0,0 +1,27 @@
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE NoMonomorphismRestriction #-}
{-# LANGUAGE CPP #-}
module Database.Memcached.Binary.Maybe
#include "Header.txt"
#define NoReturn Bool
#define HasReturn Maybe
successHasReturn :: a -> IO (HasReturn a)
successHasReturn = return . Just
{-# INLINE successHasReturn #-}
successNoReturn :: IO NoReturn
successNoReturn = return True
{-# INLINE successNoReturn #-}
failureHasReturn :: I.Failure (HasReturn a)
failureHasReturn _ _ = return Nothing
{-# INLINE failureHasReturn #-}
failureNoReturn :: I.Failure NoReturn
failureNoReturn _ _ = return False
{-# INLINE failureNoReturn #-}
#include "Common.hs"

View File

@ -23,6 +23,10 @@ data ConnectInfo = ConnectInfo
, connectionIdleTime :: NominalDiffTime
} deriving Show
-- |
-- @
-- def = ConnectInfo "localhost" (PortNumber 11211) [] 1 20
-- @
instance Default ConnectInfo where
def = ConnectInfo "localhost" (PortNumber 11211) [] 1 20

View File

@ -1,7 +1,7 @@
{-# LANGUAGE DeriveDataTypeable #-}
{-# LANGUAGE CPP #-}
module Database.Memcached.Binary.Exception where
module Database.Memcached.Binary.Types.Exception where
import Control.Exception
import Data.Word

View File

@ -1,10 +1,30 @@
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE DeriveDataTypeable #-}
{-# LANGUAGE LambdaCase #-}
module Main (main) where
import Network
import System.Process
import Control.Exception
import Control.Concurrent
import Control.Monad
import Network
import System.Process
import Database.Memcached.Binary
import Data.Default.Class
import Data.Maybe
import Data.Word
import Data.Typeable
import qualified Data.ByteString.Char8 as S
import qualified Data.ByteString.Lazy.Char8 as L
import Database.Memcached.Binary.Exception
import Database.Memcached.Binary.IO (Connection, withConnection)
import qualified Database.Memcached.Binary.IO as McIO
import qualified Database.Memcached.Binary.Maybe as McMaybe
import Test.HUnit hiding (Test)
import Test.Framework
import Test.Framework.Providers.HUnit
@ -18,7 +38,423 @@ startMemcached = do
wait i = handle (\(_ ::SomeException) -> threadDelay 100000 >> wait (i-1)) $
void $ connectTo "localhost" $ PortNumber 11211
precond :: Connection -> IO ()
precond c = do
McIO.flushAll c
void $ McIO.set 0 0 "foo" "foovalue" c
void $ McIO.set 1 0 "bar" "1234567890" c
testMc :: TestName -> (Connection -> IO ()) -> Test
testMc title m = testCase title $ withConnection def (\c -> precond c >> m c)
newtype ByPassException = ByPassException String deriving (Typeable)
instance Show ByPassException where
show (ByPassException s) = s
instance Exception ByPassException
assertException :: Word16 -> S.ByteString -> IO a -> IO ()
assertException ex msg m =
(m >> throwIO (ByPassException "exception not occured.")) `catch`
(\e -> case fromException e :: Maybe MemcachedException of
Nothing -> assertFn e
Just e'@(MemcachedException i _) -> unless (i == ex) (assertFn e'))
where
assertFn e = assertFailure $ unlines
[ "not expected exception occured:"
, "expected: " ++ show (MemcachedException ex msg)
, "occured: " ++ show e
]
testGet :: Test
testGet = testGroup "get"
[ testGroup "IO module"
[ testGroup "get"
[ doTest "foo" (0, "foovalue") McIO.get
, doTest "bar" (1, "1234567890") McIO.get
, doTestException "notexist" McIO.get
]
, testGroup "get_"
[ doTest "foo" "foovalue" McIO.get_
, doTest "bar" "1234567890" McIO.get_
, doTestException "notexist" McIO.get_
]
]
, testGroup "Maybe module"
[ testGroup "get"
[ doTest "foo" (Just (0, "foovalue")) McMaybe.get
, doTest "bar" (Just (1, "1234567890")) McMaybe.get
, doTest "notexist" Nothing McMaybe.get
]
, testGroup "get_"
[ doTest "foo" (Just "foovalue") McMaybe.get_
, doTest "bar" (Just "1234567890") McMaybe.get_
, doTest "notexist" Nothing McMaybe.get_
]
]
]
where
doTest key ex fn = testMc (S.unpack key) $ \c -> do
v <- fn key c
v @?= ex
doTestException key fn = testMc (S.unpack key) $ \c ->
assertException 1 "Not found" $ fn key c
testSetAddReplace :: Test
testSetAddReplace = testGroup "set/add/replace"
[ testGroup "set"
[ testMc "set foo to foomod" $ \c -> do
McIO.set 100 0 "foo" "foomod" c
v <- McIO.get "foo" c
v @?= (100, "foomod")
, testMc "set notexist to exist" $ \c -> do
McIO.set 100 0 "notexist" "exist" c
v <- McIO.get "notexist" c
v @?= (100, "exist")
]
, testGroup "add"
[ testMc "add foo to foomod" $ \c ->
assertException 2 "Data exists for key." $
McIO.add 100 0 "foo" "foomod" c
, testMc "add notexist to exist" $ \c -> do
McIO.add 100 0 "notexist" "exist" c
v <- McIO.get "notexist" c
v @?= (100, "exist")
]
, testGroup "replace"
[ testMc "set foo to foomod" $ \c -> do
McIO.replace 100 0 "foo" "foomod" c
v <- McIO.get "foo" c
v @?= (100, "foomod")
, testMc "set notexist to exist" $ \c ->
assertException 1 "Not found" $
McIO.replace 100 0 "notexist" "exist" c
]
]
testDelete :: Test
testDelete = testGroup "delete"
[ testGroup "IO module"
[ testMc "foo" $ \c -> do
McIO.delete "foo" c
r <- McMaybe.get "foo" c
r @?= Nothing
, testMc "notexist" $ \c ->
assertException 1 "Not found" $ McIO.delete "notexist" c
]
, testGroup "Maybe module"
[ testMc "foo" $ \c -> do
b <- McMaybe.delete "foo" c
r <- McMaybe.get "foo" c
b @?= True
r @?= Nothing
, testMc "notexist" $ \c -> do
b <- McMaybe.delete "notexist" c
r <- McMaybe.get "notexist" c
b @?= False
r @?= Nothing
]
]
testIncrDecr :: Test
testIncrDecr = testGroup "increment/decrement"
[ testGroup "IO module"
[ testGroup "increment"
[ testMc "foo" $ \c ->
assertException 6 "Non-numeric server-side value for incr or decr" $
McIO.increment 0 "foo" 10 10 c
, testMc "bar" $ \c -> do
a <- McIO.increment 0 "bar" 10 10 c
b <- McIO.get_ "bar" c
a @?= 1234567900
b @?= "1234567900"
, testMc "notexist" $ \c -> do
a <- McIO.increment 0 "notexist" 10 10 c
b <- McIO.get_ "notexist" c
a @?= 10
b @?= "10"
]
, testGroup "decrement"
[ testMc "foo" $ \c ->
assertException 6 "Non-numeric server-side value for incr or decr" $
McIO.decrement 0 "foo" 10 10 c
, testMc "bar" $ \c -> do
a <- McIO.decrement 0 "bar" 10 10 c
b <- McIO.get_ "bar" c
a @?= 1234567880
b @?= "1234567880"
, testMc "notexist" $ \c -> do
a <- McIO.decrement 0 "notexist" 10 10 c
b <- McIO.get_ "notexist" c
a @?= 10
b @?= "10"
]
]
, testGroup "Maybe module"
[ testGroup "increment"
[ testMc "foo" $ \c -> do
r <- McMaybe.increment 0 "foo" 10 10 c
b <- McIO.get_ "foo" c
r @?= Nothing
b @?= "foovalue"
, testMc "bar" $ \c -> do
a <- McMaybe.increment 0 "bar" 10 10 c
b <- McIO.get_ "bar" c
a @?= Just 1234567900
b @?= "1234567900"
, testMc "notexist" $ \c -> do
a <- McMaybe.increment 0 "notexist" 10 10 c
b <- McIO.get_ "notexist" c
a @?= Just 10
b @?= "10"
]
, testGroup "decrement'"
[ testMc "foo" $ \c -> do
r <- McMaybe.decrement 0 "foo" 10 10 c
b <- McIO.get_ "foo" c
r @?= Nothing
b @?= "foovalue"
, testMc "bar" $ \c -> do
a <- McMaybe.decrement 0 "bar" 10 10 c
b <- McIO.get_ "bar" c
a @?= Just 1234567880
b @?= "1234567880"
, testMc "notexist" $ \c -> do
a <- McMaybe.decrement 0 "notexist" 10 10 c
b <- McIO.get_ "notexist" c
a @?= Just 10
b @?= "10"
]
]
]
testFlush :: Test
testFlush = testGroup "flush"
[ testGroup "IO module"
[ testMc "flushAll" $ \c -> do
McIO.flushAll c
a <- McMaybe.get "foo" c
a @?= Nothing
]
, testGroup "Maybe module"
[ testMc "flushAll" $ \c -> do
r <- McMaybe.flushAll c
a <- McMaybe.get "foo" c
r @?= True
a @?= Nothing
]
]
testVersion :: Test
testVersion = testGroup "version"
[ testMc "IO module" $ \c -> do
v <- McIO.version c
assertBool (show v ++ " is not version like.") $ isVersionLike v
, testMc "Maybe module" $ \c -> do
Just v <- McMaybe.version c
assertBool (show v ++ " is not version like.") $ isVersionLike v
]
where
isVersionLike s0 = isJust $ do
(_, s1) <- S.readInt s0
unless (S.head s1 == '.') Nothing
(_, s2) <- S.readInt (S.tail s1)
unless (S.head s2 == '.') Nothing
(_, s3) <- S.readInt (S.tail s2)
unless (S.null s3) Nothing
testNoOp :: Test
testNoOp = testGroup "noOp"
[ testMc "IO module" McIO.noOp
, testMc "Maybe module" $ \c -> do
b <- McMaybe.noOp c
b @?= True
]
testAppendPrepend :: Test
testAppendPrepend = testGroup "append/prepend"
[ testGroup "IO module"
[ testGroup "append"
[ testMc "foo" $ \c -> do
McIO.append "foo" "!!" c
a <- McIO.get_ "foo" c
a @?= "foovalue!!"
, testMc "bar" $ \c -> do
McIO.append "bar" "!!" c
a <- McIO.get_ "bar" c
a @?= "1234567890!!"
, testMc "notexist" $ \c ->
assertException 5 "Not stored." $ McIO.append "notexist" "!!" c
]
, testGroup "prepend"
[ testMc "foo" $ \c -> do
McIO.prepend "foo" "!!" c
a <- McIO.get_ "foo" c
a @?= "!!foovalue"
, testMc "bar" $ \c -> do
McIO.prepend "bar" "!!" c
a <- McIO.get_ "bar" c
a @?= "!!1234567890"
, testMc "notexist" $ \c ->
assertException 5 "Not stored." $ McIO.prepend "notexist" "!!" c
]
]
, testGroup "maybe module"
[ testGroup "append'"
[ testMc "foo" $ \c -> do
b <- McMaybe.append "foo" "!!" c
a <- McIO.get_ "foo" c
b @?= True
a @?= "foovalue!!"
, testMc "bar" $ \c -> do
b <- McMaybe.append "bar" "!!" c
a <- McIO.get_ "bar" c
b @?= True
a @?= "1234567890!!"
, testMc "notexist" $ \c -> do
b <- McMaybe.append "notexist" "!!" c
b @?= False
]
, testGroup "prepend'"
[ testMc "foo" $ \c -> do
b <- McMaybe.prepend "foo" "!!" c
a <- McIO.get_ "foo" c
b @?= True
a @?= "!!foovalue"
, testMc "bar" $ \c -> do
b <- McMaybe.prepend "bar" "!!" c
a <- McIO.get_ "bar" c
b @?= True
a @?= "!!1234567890"
, testMc "notexist" $ \c -> do
b <- McMaybe.prepend "notexist" "!!" c
b @?= False
]
]
]
testTouchGAT :: Test
testTouchGAT = testGroup "touch/GAT"
[ testGroup "IO module"
[ testGroup "touch"
[ testMc "foo" $ \c -> do
a <- McMaybe.get_ "foo" c
McIO.touch 1 "foo" c
threadDelay 1100000
b <- McMaybe.get_ "foo" c
a @?= (Just "foovalue")
b @?= Nothing
, testMc "notexist" $ \c ->
assertException 1 "Not found" $ McIO.touch 1 "notexist" c
]
, testGroup "getAndTouch"
[ testMc "foo" $ \c -> do
x <- McMaybe.get "foo" c
y <- McIO.getAndTouch 1 "foo" c
threadDelay 1100000
z <- McMaybe.get "foo" c
x @?= Just (0, "foovalue")
y @?= (0, "foovalue")
z @?= Nothing
, testMc "notexist" $ \c ->
assertException 1 "Not found" $ McIO.getAndTouch 1 "notexist" c
]
, testGroup "getAndTouch_"
[ testMc "foo" $ \c -> do
x <- McMaybe.get_ "foo" c
y <- McIO.getAndTouch_ 1 "foo" c
threadDelay 1100000
z <- McMaybe.get_ "foo" c
x @?= Just "foovalue"
y @?= "foovalue"
z @?= Nothing
, testMc "notexist" $ \c ->
assertException 1 "Not found" $ McIO.getAndTouch_ 1 "notexist" c
]
]
, testGroup "Maybe module"
[ testGroup "touch"
[ testMc "foo" $ \c -> do
a <- McMaybe.get_ "foo" c
r <- McMaybe.touch 1 "foo" c
threadDelay 1100000
b <- McMaybe.get_ "foo" c
a @?= (Just "foovalue")
r @?= True
b @?= Nothing
, testMc "notexist" $ \c -> do
r <- McMaybe.touch 1 "notexist" c
a <- McMaybe.get "notexist" c
r @?= False
a @?= Nothing
]
, testGroup "getAndTouch'/getMaybeAndTouch"
[ testMc "foo" $ \c -> do
a <- McMaybe.get "foo" c
r <- McMaybe.getAndTouch 1 "foo" c
threadDelay 1100000
b <- McMaybe.get_ "foo" c
a @?= Just (0, "foovalue")
r @?= Just (0, "foovalue")
b @?= Nothing
, testMc "notexist" $ \c -> do
r <- McMaybe.getAndTouch 1 "notexist" c
a <- McMaybe.get "notexist" c
r @?= Nothing
a @?= Nothing
]
, testGroup "getAndTouch'_/getMaybeAndTouch_"
[ testMc "foo" $ \c -> do
a <- McMaybe.get_ "foo" c
r <- McMaybe.getAndTouch_ 1 "foo" c
threadDelay 1100000
b <- McMaybe.get_ "foo" c
a @?= Just "foovalue"
r @?= Just "foovalue"
b @?= Nothing
, testMc "notexist" $ \c -> do
r <- McMaybe.getAndTouch_ 1 "notexist" c
a <- McMaybe.get "notexist" c
r @?= Nothing
a @?= Nothing
]
]
]
testModify :: Test
testModify = testGroup "modify"
[ testMc "reverse foo" $ \c -> do
r <- McIO.modify 0 "foo" (\f v -> (f + 100, L.reverse v, v)) c
a <- McMaybe.get "foo" c
r @?= "foovalue"
a @?= Just (100, "eulavoof")
, testMc "notexist" $ \c ->
assertException 1 "Not found" $
McIO.modify 0 "notexist" (\f v -> (f,v,())) c
]
testModify_ :: Test
testModify_ = testGroup "modify_"
[ testMc "reverse foo" $ \c -> do
McIO.modify_ 0 "foo" (\f v -> (f + 100, L.reverse v)) c
a <- McMaybe.get "foo" c
a @?= Just (100, "eulavoof")
, testMc "notexist" $ \c ->
assertException 1 "Not found" $
McIO.modify_ 0 "notexist" (\f v -> (f,v)) c
]
main :: IO ()
main = bracket startMemcached terminateProcess $ \_ -> defaultMain
[ testCase "version" . void $ withConnection def version
[ testGet
, testSetAddReplace
, testDelete
, testIncrDecr
, testFlush
, testVersion
, testNoOp
, testAppendPrepend
, testTouchGAT
, testModify
, testModify_
]