packages feed

BerkeleyDB-0.7: examples/tests.hs

import System.Directory
import Database.Berkeley.Db
import Test.HUnit
import Control.Exception
import Control.Monad
import qualified Data.ByteString as B
import Data.Char
import Prelude hiding (catch)


pack :: String -> B.ByteString
pack = B.pack . map (fromIntegral . ord)

unpack :: B.ByteString -> String
unpack = map (chr . fromIntegral) . B.unpack 

cleanDir :: IO () -> IO ()
cleanDir code = do
    original <- getCurrentDirectory
    removeDirectoryRecursive "test" `catch` ((\exc -> return ())::SomeException -> IO ())
    createDirectory "test"
    setCurrentDirectory "test"
    code
    setCurrentDirectory original

cleanEnv :: (DbEnv -> IO ()) -> IO ()
cleanEnv code = do
    cleanDir $
        bracket
            (do
                dbenv <- dbEnv_create []
                dbEnv_open [DB_CREATE,DB_INIT_LOCK,DB_INIT_LOG,DB_INIT_MPOOL,
                    DB_INIT_TXN,DB_THREAD] 0 dbenv "."
                return dbenv)
            (dbEnv_close [])
            code

cleanDb :: (DbEnv -> Db -> IO ()) -> IO ()
cleanDb code =
    cleanEnv $ \dbenv -> do
        bracket
            (do
                db <- db_create [] dbenv
                db_open [DB_CREATE,DB_THREAD,DB_AUTO_COMMIT] DB_BTREE 0 db Nothing "test.db" Nothing
                return db)
            (db_close [])
            (code dbenv)

test_exception = do
    cleanEnv $ \dbenv -> do
        answer <-
            catch
                (do
                    bracket
                        (db_create [] dbenv)
                        (db_close [])
                        (\db -> db_open [] DB_HASH 0 db Nothing "not-there.db" Nothing)
                    return Nothing)
                (\exc -> do
                        case fromException exc of
                            Just (DbException _ code) -> return $ Just code
                            Nothing                   -> throwIO exc
                    )
        assertEqual "exception" (Just (SYSTEM_ERROR 2)) answer  -- 2 == ENOENT

withCase1 action = do
    cleanDb $ \dbenv db -> do
        dbEnv_withTxn [] [] dbenv Nothing $ \txn -> do
            db_put [] db (Just txn) (pack "car")   (pack "triumph")
            db_put [] db (Just txn) (pack "fruit") (pack "banana")
            db_put [] db (Just txn) (pack "car")   (pack "honda")
            db_put [] db (Just txn) (pack "tree")  (pack "poplar")
        action dbenv db

test_putget = do
    withCase1 $ \dbenv db -> do
        assertEqual "putget_fruit" (Just $ pack "banana") =<< db_get [] db Nothing (pack "fruit")
        assertEqual "putget_car"   (Just $ pack "honda")  =<< db_get [] db Nothing (pack "car")
        assertEqual "putget_tree"  (Just $ pack "poplar") =<< db_get [] db Nothing (pack "tree")
        assertEqual "putget_pet"   Nothing                =<< db_get [] db Nothing (pack "pet")

test_txn = do
    cleanDb $ \dbenv db -> do
        dbEnv_withTxn [] [] dbenv Nothing $ \txn -> do
            db_put [] db (Just txn) (pack "fruit") (pack "apricot")
        assertEqual "txn_1" (Just $ pack "apricot") =<< db_get [] db Nothing (pack "fruit")
          -- Check that a rolled back exception doesn't modify the database
        catch
            (dbEnv_withTxn [] [] dbenv Nothing $ \txn -> do
                db_put [] db (Just txn) (pack "fruit") (pack "pomegranate")
                throwIO $ DbException "" (SYSTEM_ERROR 0))
            ((\exc -> return ()) :: SomeException -> IO ())
        assertEqual "txn_1" (Just $ pack "apricot") =<< db_get [] db Nothing (pack "fruit")

test_afterclose = do
    cleanDir $ do
        mDbErr <- catch (do
            dbenv <- dbEnv_create []
            dbEnv_open [DB_CREATE,DB_INIT_LOCK,DB_INIT_LOG,DB_INIT_MPOOL,
                        DB_INIT_TXN,DB_THREAD] 0 dbenv "."
            dbEnv_close [] dbenv
            db <- db_create [] dbenv
            db_open [DB_CREATE,DB_THREAD,DB_AUTO_COMMIT] DB_BTREE 0 db Nothing "test.db" Nothing
            db_close [] db
            return Nothing)
            (\exc ->
                case fromException exc of
                    Just (DbException _ code) -> return $ Just code
                    Nothing                   -> throwIO exc)
        assertEqual "afterclose_1" (Just DB_ACCESSED_DB_ENV_AFTER_CLOSE) mDbErr

    cleanEnv $ \dbenv -> do
        mDbErr <- catch (do
            db <- db_create [] dbenv
            db_open [DB_CREATE,DB_THREAD,DB_AUTO_COMMIT] DB_BTREE 0 db Nothing "test.db" Nothing
            db_close [] db
            db_put [] db Nothing (pack "fruit") (pack "guava")
            return Nothing)
            (\exc ->
                case fromException exc of
                    Just (DbException _ code) -> return $ Just code
                    Nothing                   -> throwIO exc)
        assertEqual "afterclose_2" (Just DB_ACCESSED_DB_AFTER_CLOSE) mDbErr
        
    cleanDb $ \dbenv db -> do
        mDbErr <- catch (do
            txn <- dbEnv_txn_begin [] dbenv Nothing
            dbTxn_abort txn
            db_put [] db (Just txn) (pack "fruit") (pack "melon")
            return Nothing)
            (\exc ->
                case fromException exc of
                    Just (DbException _ code) -> return $ Just code
                    Nothing                   -> throwIO exc)
        assertEqual "afterclose_3" (Just DB_ACCESSED_DB_TXN_AFTER_CLOSE) mDbErr

test_lock = do
    cleanDb $ \dbenv db -> do
        -- This doesn't test very much: Just that locking doesn't cause any
        -- runtime errors, hangs or crashes.
        dbEnv_withTxn [] [] dbenv Nothing $ \txn -> do
            dbEnv_withLock [] DB_LOCK_WRITE (pack "my_lock") dbenv (dbTxn_id txn) $ do
                db_put [] db (Just txn) (pack "fruit") (pack "durian")

test_dbCursor_get =
    withCase1 $ \dbenv db ->
    dbEnv_withTxn [] [] dbenv Nothing $ \txn ->
    db_withCursor [] db (Just txn) $ \cur -> do
        assertEqual "dbCursor_get_car" (Just (pack "car", pack "honda")) =<< dbCursor_get [DB_NEXT] cur
        assertEqual "dbCursor_get_fruit" (Just (pack "fruit", pack "banana")) =<< dbCursor_get [DB_NEXT] cur
        assertEqual "dbCursor_get_tree" (Just (pack "tree", pack "poplar")) =<< dbCursor_get [DB_NEXT] cur
        assertEqual "dbCursor_get_Nothing" Nothing =<< dbCursor_get [DB_NEXT] cur

test_dbCursor_put1 =
    cleanDb $ \dbenv db ->
    dbEnv_withTxn [] [] dbenv Nothing $ \txn -> do
        db_withCursor [] db (Just txn) $ \cur -> do
            dbCursor_put [DB_KEYFIRST] cur (pack "dog") (pack "Xanthe")
            dbCursor_put [DB_KEYFIRST] cur (pack "cat") (pack "Jasper")
        db_withCursor [] db (Just txn) $ \cur -> do
            assertEqual "dbCursor_put1_cat" (Just (pack "cat", pack "Jasper")) =<< dbCursor_get [DB_NEXT] cur
            assertEqual "dbCursor_put1_dog" (Just (pack "dog", pack "Xanthe")) =<< dbCursor_get [DB_NEXT] cur

test_dbCursor_put2 =
    withCase1 $ \dbenv db ->
    dbEnv_withTxn [] [] dbenv Nothing $ \txn ->
    db_withCursor [] db (Just txn) $ \cur -> do
        assertEqual "dbCursor_put2_car" (Just (pack "car", pack "honda")) =<< dbCursor_get [DB_NEXT] cur
        assertEqual "dbCursor_put2_fruit" (Just (pack "fruit", pack "banana")) =<< dbCursor_get [DB_NEXT] cur
        dbCursor_put [DB_CURRENT] cur (pack "") (pack "orange")
        assertEqual "dbCursor_put2_car" (Just (pack "car", pack "honda")) =<< dbCursor_get [DB_FIRST] cur
        assertEqual "dbCursor_put2_fruit" (Just (pack "fruit", pack "orange")) =<< dbCursor_get [DB_NEXT] cur
        assertEqual "dbCursor_put2_tree" (Just (pack "tree", pack "poplar")) =<< dbCursor_get [DB_NEXT] cur

test_dbCursor_set =
    withCase1 $ \dbenv db ->
    dbEnv_withTxn [] [] dbenv Nothing $ \txn ->
    db_withCursor [] db (Just txn) $ \cur -> do
        assertEqual "dbCursor_set_tree" (Just $ pack "poplar") =<< dbCursor_set [] cur (pack "tree")
        assertEqual "dbCursor_set_Nothing" Nothing =<< dbCursor_get [DB_NEXT] cur

test_dbCursor_dup =
    withCase1 $ \dbenv db ->
    dbEnv_withTxn [] [] dbenv Nothing $ \txn ->
    db_withCursor [] db (Just txn) $ \cur1 -> do
        assertEqual "dbCursor_dup_car" (Just (pack "car", pack "honda")) =<< dbCursor_get [DB_NEXT] cur1
        dbCursor_withCursor [DB_POSITION] cur1 $ \cur2 -> do
            assertEqual "dbCursor_dup_2_fruit" (Just (pack "fruit", pack "banana")) =<< dbCursor_get [DB_NEXT] cur2
            assertEqual "dbCursor_dup_2_tree" (Just (pack "tree", pack "poplar")) =<< dbCursor_get [DB_NEXT] cur2
        assertEqual "dbCursor_dup_fruit" (Just (pack "fruit", pack "banana")) =<< dbCursor_get [DB_NEXT] cur1
        assertEqual "dbCursor_dup_tree" (Just (pack "tree", pack "poplar")) =<< dbCursor_get [DB_NEXT] cur1
        assertEqual "dbCursor_dup_Nothing" Nothing =<< dbCursor_get [DB_NEXT] cur1

test_dbCursor_del =
    withCase1 $ \dbenv db ->
    dbEnv_withTxn [] [] dbenv Nothing $ \txn ->
    db_withCursor [] db (Just txn) $ \cur -> do
        assertEqual "dbCursor_del_1_car" (Just (pack "car", pack "honda")) =<< dbCursor_get [DB_NEXT] cur
        assertEqual "dbCursor_del_1_fruit" (Just (pack "fruit", pack "banana")) =<< dbCursor_get [DB_NEXT] cur
        dbCursor_del [] cur
        assertEqual "dbCursor_del_2_car" (Just (pack "car", pack "honda")) =<< dbCursor_get [DB_FIRST] cur
        assertEqual "dbCursor_del_2_tree" (Just (pack "tree", pack "poplar")) =<< dbCursor_get [DB_NEXT] cur
        assertEqual "dbCursor_del_2_Nothing" Nothing =<< dbCursor_get [DB_NEXT] cur

test_dbCursor_count =
    withCase1 $ \dbenv db ->
    dbEnv_withTxn [] [] dbenv Nothing $ \txn ->
    db_withCursor [] db (Just txn) $ \cur -> do
        assertEqual "dbCursor_count_car" (Just (pack "car", pack "honda")) =<< dbCursor_get [DB_NEXT] cur
        assertEqual "dbCursor_count_val" 1 =<< dbCursor_count [] cur

main = do
    runTestTT $ TestList [
            TestCase test_exception,
            TestCase test_putget,
            TestCase test_txn,
            TestCase test_afterclose,
            TestCase test_lock,
            TestCase test_dbCursor_get,
            TestCase test_dbCursor_put1,
            TestCase test_dbCursor_put2,
            TestCase test_dbCursor_set,
            TestCase test_dbCursor_dup,
            TestCase test_dbCursor_del,
            TestCase test_dbCursor_count
        ]