    {-# LANGUAGE TupleSections #-}
    -- |
    -- Module      :  $Header$
    -- Copyright   :  (c) 2013-2015 Galois, Inc.
    -- License     :  BSD3
    -- Maintainer  :  cryptol@galois.com
    -- Stability   :  provisional
    -- Portability :  portable
    --
    -- Evaluate test cases and handle exceptions appropriately

    module Cryptol.Testing.Eval where

    import Cryptol.Eval.Error
    import Cryptol.Eval.Value
    import Cryptol.Utils.Panic (panic)

    import qualified Control.Exception as X

    -- | A test result is either a pass, a failure due to evaluating to
    -- @False@, or a failure due to an exception raised during evaluation
    data TestResult
      = Pass
      | FailFalse [Value]
      | FailError EvalError [Value]

    -- | Apply a testable value to some arguments.
    -- Note that this function assumes that the values come from a call to
    -- `testableType` (i.e., things are type-correct). We run in the IO
    -- monad in order to catch any @EvalError@s.
    runOneTest :: Value -> [Value] -> IO TestResult
    runOneTest v0 vs0 = run `X.catch` handle
      where
        run = do
          result <- X.evaluate (go v0 vs0)
          if result
            then return Pass
            else return (FailFalse vs0)
        handle e = return (FailError e vs0)

        go :: Value -> [Value] -> Bool
        go (VFun f) (v : vs) = go (f v) vs
        go (VFun _) []       = panic "Not enough arguments while applying function"
                               []
        go (VBit b) []       = b
        go v vs              = panic "Type error while running test" $
                               [ "Function:"
                               , show $ ppValue defaultPPOpts v
                               , "Arguments:"
                               ] ++ map (show . ppValue defaultPPOpts) vs