packages feed

packman-0.3.0: Test/TestExceptions.hs

{-
  Some tests to verify that serialisation works as expected
-}
module Main(mytests, main)
    where

import GHC.Packing

import qualified Data.Array.IArray as A
import Control.Concurrent

import System.Environment
import System.IO

import System.Directory
import qualified Data.ByteString as B
import Control.Exception
import Data.Typeable

-- import Distribution.TestSuite
import System.Exit
import Control.Monad

-- this test uses the trySerialize routine. We expect to trigger some
-- exceptions and catch them as appropriate.

catchPackExc :: IO () -> IO ()
catchPackExc io = io `catch` (\e -> putStrLn (show (e::PackException)))

expectException :: (Show a) => PackException -> IO a -> IO Bool
expectException exception action
    = do putStrLn ("expect exception:\n" ++ show exception)
         action >>= print
         return False
      `catch` \e -> do putStrLn ("Got: " ++ show e)
                       return (e == exception)

main :: IO ()
main = do putStrLn "Running all tests"
          args <- getArgs
          let n    = if (length args < 2) then 3 else read (args!!1)
              size = if null args then 128 else read (head args)::Int
          -- test data from arguments, avoiding over-optimisation
              arr, arr2, output :: A.Array Int Int
              arr  = A.array (0,127) [ (i,i) | i <- [0..127] ]
              output = A.amap (2*) arr
              arr2 = A.listArray (0,2*n-1) (take (2*n) (A.elems arr))
              cfg  = (output, arr2, n)
          (mapM_ (runIt cfg) mytests) `finally` (rmv testfile)

-- file used for all tests
testfile :: FilePath
testfile = "testfile"

type Config = (A.Array Int Int, A.Array Int Int, Int)

type MyTest = Config -> (String, IO Bool)

-- run a defined test
runIt :: Config -> MyTest -> IO ()
runIt cfg f
    = do putStrLn $ "Test: " ++ name ++ ": "
         result <- action
         putStrLn $ "Result: " ++ show result
         if not result then error "test failed!" else return ()
    where (name, action) = f cfg

-- all configured tests, see below
mytests :: [ MyTest ]
mytests = [ evalArray,  packArray, packThreadId, packMVar,
            unpackOther, unpackWrongType, unpackTruncated, unpackGarbled ]

evalArray :: MyTest
evalArray (output, _, n)
    = ("eval. array",
       do let out = show $ take n $ A.elems output
          putStrLn $ "Evaluated: " ++ out
          return (out == "[0,2,4]"))

packArray :: MyTest
packArray (output, arr2, n)
    = ("duplicating an array of 128 elements",
              do packet1 <- trySerialize output
                 putStrLn (take (3*80) (show packet1) ++ "...")
                 putStrLn "now unpacking (deserialize):"
                 copy <- deserialize packet1
                 putStrLn ("unpacked, now evaluate")
                 putStrLn (show copy)
                 return $
                    take (2*n) (A.elems copy) == A.elems (A.amap (2*) arr2)
             )

packThreadId :: MyTest
packThreadId _ = ("packing a thread ID (unsupported)",
                  do t <- myThreadId
                     expectException P_UNSUPPORTED $ trySerialize t
                 )
packMVar :: MyTest
packMVar _ = ("packing an MVar (should be cannotpack)",
              do m <- newEmptyMVar :: IO (MVar Integer)
                 expectException P_CANNOTPACK $ trySerialize m
             )

unpackOther :: MyTest
unpackOther _ = ("deserialise other binary's data (binary mismatch)",
                 expectException P_BinaryMismatch
                 (decodeFromFile "pack.old" :: IO (A.Array Int Int))
                )

unpackWrongType :: MyTest
unpackWrongType  (output, arr2, n)
    = ("deserialise wrong type from file (type mismatch)",
                   do encodeToFile testfile arr2
                      expectException P_TypeMismatch
                       (decodeFromFile testfile :: IO (A.Array Int Double))
                  )

unpackTruncated :: MyTest
unpackTruncated  (output, arr2, n)
    = ("deserialise truncated data. Expected: parse error",
       do encodeToFile testfile arr2
          blob <- B.readFile testfile
          B.writeFile testfile (B.take 50 blob)
               -- take more than FingerPrint (4 x Word64)
          expectException P_ParseError
              (decodeFromFile testfile :: IO (A.Array Int Int))
      )

unpackGarbled :: MyTest
unpackGarbled (output, arr2, n)
    = ("deserialise garbled data. Expected: garbled data",
       do encodeToFile testfile arr2
          blob <- B.readFile testfile
          B.writeFile testfile (tamperWith blob)
          expectException P_GARBLED
           (decodeFromFile testfile :: IO (A.Array Int Int))
      )

tamperWith :: B.ByteString -> B.ByteString
tamperWith b = B.concat [b1, B.pack [11,11], B.drop 2 b2]
    where n       = B.length b
          (b1,b2) = B.splitAt (n - 10) b
-- do not touch the FingerPrints (4 x Word64)

-- file cleanup, catching all exceptions
rmv :: FilePath -> IO ()
rmv f = (removeFile f) `catch` (\e -> print (e::SomeException))


-- need a time-wasting function which allocates...
nfib :: Integer -> Integer
nfib 0 = 1
nfib 1 = 1
nfib n = let n1 = nfib (n-1)
             n2 = nfib (n-2)
         in 1 + 2*n1 + n2 - n1