packages feed

configurator-ng-0.0.0.0: tests/Test.hs

{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE OverloadedStrings   #-}

module Main where

import Prelude hiding (lookup)

import           Control.Concurrent
import           Control.Exception
import           Control.Monad
import qualified Data.ByteString.Lazy.Char8 as L
import           Data.Configurator
import           Data.Configurator.Parser
import           Data.Configurator.Types
import           Data.Functor
import           Data.Int
import           Data.Maybe
import           Data.Text (Text)
import           Data.Word
import           System.Directory
import           System.Environment
import           System.FilePath
import           System.IO
import           Test.Framework
import           Test.Framework.Providers.HUnit
import           Test.HUnit hiding (Test)

main :: IO ()
main = defaultMain tests

tests :: [Test]
tests =
    [ testCase "load" loadTest
    , testCase "types" typesTest
    , testCase "interp" interpTest
    , testCase "scoped-interp" scopedInterpTest
    , testCase "import" importTest
--    , testCase "reload" reloadTest
    ]

withLoad :: FilePath -> (ConfigCache -> IO ()) -> IO ()
withLoad name t = do
    mb <- try $ load (testFile name)
    case mb of
        Left (err :: SomeException) -> assertFailure (show err)
        Right cfg -> t cfg

withReload :: FilePath -> ([Maybe FilePath] -> ConfigCache -> IO ()) -> IO ()
withReload name t = do
    tmp   <- getTemporaryDirectory
    temps <- forM (testFile name) $ \f -> do
        exists <- doesFileExist (worth f)
        if exists
            then do
                (p,h) <- openBinaryTempFile tmp "test.cfg"
                L.hPut h =<< L.readFile (worth f)
                hClose h
                return (p <$ f, Just p)
            else do
                return (f, Nothing)
    flip finally (mapM_ removeFile (catMaybes (map snd temps))) $ do
        mb <- try $ autoReload autoConfig (map fst temps)
        case mb of
            Left (err :: SomeException) -> assertFailure (show err)
            Right (cfg, tid) -> t (map snd temps) cfg >> killThread tid

testFile :: FilePath -> [Worth FilePath]
testFile name = [Required $ "tests" </> "resources" </> name]

takeMVarTimeout :: Int -> MVar a -> IO (Maybe a)
takeMVarTimeout millis v = do
    w <- newEmptyMVar
    tid <- forkIO $ do
        putMVar w . Just =<< takeMVar v
    forkIO $ do
        threadDelay (millis * 1000)
        killThread tid
        tryPutMVar w Nothing
        return ()
    takeMVar w

loadTest :: Assertion
loadTest =
  withLoad "pathological.cfg" $ \cfgcache -> do
    cfg <- readConfig cfgcache

    let (aa, _errs) = runParserM (key "aa") cfg
    assertEqual "int property" aa $ (Just 1 :: Maybe Int)

    let (ab, _errs) = runParserM (key "ab") cfg
    assertEqual "string property" ab (Just "foo" :: Maybe Text)

    let (acx, _errs) = runParserM (key "ac.x") cfg
    assertEqual "nested int" acx (Just 1 :: Maybe Int)

    let (acy, _errs) = runParserM (key "ac.y") cfg
    assertEqual "nested bool" acy (Just True :: Maybe Bool)

    let (ad, _errs) = runParserM (key "ad") cfg
    assertEqual "simple bool" ad (Just False :: Maybe Bool)

    let (ae, _errs) = runParserM (key "ae") cfg
    assertEqual "simple int 2" ae (Just 1 :: Maybe Int)

    let (af, _errs) = runParserM (key "af") cfg
    assertEqual "list property" af (Just (2,3) :: Maybe (Int,Int))

    let (deep, _errs) = runParserM (key "ag.q-e.i_u9.a") cfg
    assertEqual "deep bool" deep (Just False :: Maybe Bool)

    let (notacomment, _errs) = runParserM (key "notacomment") cfg
    assertEqual "not a comment" notacomment (Just 42 :: Maybe Int)

    let (comment, _errs) = runParserM (key "comment.x") cfg
    assertEqual "comment" comment (Nothing :: Maybe Value)

typesTest :: Assertion
typesTest =
  withLoad "pathological.cfg" $ \cfgcache -> do
    cfg <- readConfig cfgcache

    let (asInt, _errs) = runParserM (key "aa" :: ConfigParserM Int) cfg
    assertEqual "int" asInt (Just 1)

    let (asInteger, _errs) = runParserM (key "aa" :: ConfigParserM Integer) cfg
    assertEqual "int" asInteger (Just 1)

    let (asWord, _errs) = runParserM (key "aa" :: ConfigParserM Word) cfg
    assertEqual "int" asWord (Just 1)

    let (asInt8, _errs) = runParserM (key "aa" :: ConfigParserM Int8) cfg
    assertEqual "int8" asInt8 (Just 1)

    let (asInt16, _errs) = runParserM (key "aa" :: ConfigParserM Int16) cfg
    assertEqual "int16" asInt16 (Just 1)

    let (asInt32, _errs) = runParserM (key "aa" :: ConfigParserM Int32) cfg
    assertEqual "int32" asInt32 (Just 1)

    let (asInt64, _errs) = runParserM (key "aa" :: ConfigParserM Int64) cfg
    assertEqual "int64" asInt64 (Just 1)

    let (asWord8, _errs) = runParserM (key "aa" :: ConfigParserM Word8) cfg
    assertEqual "word8" asWord8 (Just 1)

    let (asWord16, _errs) = runParserM (key "aa" :: ConfigParserM Word16) cfg
    assertEqual "word16" asWord16 (Just 1)

    let (asWord32, _errs) = runParserM (key "aa" :: ConfigParserM Word32) cfg
    assertEqual "word32" asWord32 (Just 1)

    let (asWord64, _errs) = runParserM (key "aa" :: ConfigParserM Word64) cfg
    assertEqual "word64" asWord64 (Just 1)

    let (asTextBad, _errs) = runParserM (key "aa" :: ConfigParserM Text) cfg
    assertEqual "bad text" asTextBad Nothing

    let (asTextGood, _errs) = runParserM (key "ab" :: ConfigParserM Text) cfg
    assertEqual "good text" asTextGood (Just "foo")

    let (asStringGood, _errs) = runParserM (key "ab" :: ConfigParserM String) cfg
    assertEqual "string" asStringGood (Just "foo")

    let (asInts, _errs) = runParserM (key "xs" :: ConfigParserM [Int]) cfg
    assertEqual "ints" asInts (Just [1,2,3])

    let (asChar, _errs) = runParserM (key "c" :: ConfigParserM Char) cfg
    assertEqual "char" asChar (Just 'x')

interpTest :: Assertion
interpTest =
  withLoad "pathological.cfg" $ \cfgcache -> do
    cfg <- readConfig cfgcache

    home    <- getEnv "HOME"
    let (cfgHome, _errs) = runParserM (key "ba") cfg
    assertEqual "home interp" (Just home) cfgHome

scopedInterpTest :: Assertion
scopedInterpTest = withLoad "interp.cfg" $ \cfgcache -> do
    cfg <- readConfig cfgcache
    home    <- getEnv "HOME"

    let (a, _err) = runParserM (key "myprogram.exec") cfg
    assertEqual "myprogram.exec" (Just $ home++"/services/myprogram/myprogram") a

    let (b, _err) = runParserM (key "myprogram.stdout") cfg
    assertEqual "myprogram.stdout" (Just $ home++"/services/myprogram/stdout") b

    let (c, _err) = runParserM (key "top.layer1.layer2.dir") cfg
    assertEqual "nested scope" (Just $ home++"/top/layer1/layer2") c

importTest :: Assertion
importTest =
  withLoad "import.cfg" $ \cfgcache -> do
    cfg <- readConfig cfgcache
    let (aa, _errs) = runParserM (key "x.aa" :: ConfigParserM Int) cfg
    assertEqual "simple" aa (Just 1)
    let (acx, _errs) = runParserM (key "x.ac.x" :: ConfigParserM Int) cfg
    assertEqual "nested" acx (Just 1)

{--
reloadTest :: Assertion
reloadTest =
  withReload "pathological.cfg" $ \[Just f] cfgcache -> do
    aa <- lookup cfg "aa"
    assertEqual "simple property 1" aa $ Just (1 :: Int)

    dongly <- newEmptyMVar
    wongly <- newEmptyMVar
    subscribe cfg "dongly" $ \ _ _ -> putMVar dongly ()
    subscribe cfg "wongly" $ \ _ _ -> putMVar wongly ()
    L.appendFile f "\ndongly = 1"
    r1 <- takeMVarTimeout 2000 dongly
    assertEqual "notify happened" r1 (Just ())
    r2 <- takeMVarTimeout 2000 wongly
    assertEqual "notify not happened" r2 Nothing
--}