packages feed

arrayfire-0.8.0.0: test/ArrayFire/ExceptionSpec.hs

{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE TypeApplications    #-}
module ArrayFire.ExceptionSpec where

import           Control.Exception          (evaluate, try)
import qualified ArrayFire                  as A
import           ArrayFire.Exception
import           ArrayFire.Internal.Defines (AFErr (..))
import           Test.Hspec

spec :: Spec
spec = describe "Exception spec" $ do

  -- The error-code → constructor table is the heart of the FFI error path;
  -- a wrong entry silently mislabels every failure of that kind.
  describe "toAFExceptionType" $ do

    it "maps every documented AFErr code to its constructor" $
      map (toAFExceptionType . AFErr)
        [101,102,103,201,202,203,204,205,207,208,301,302,303,401,402,501,502,503,998,999]
        `shouldBe`
        [ NoMemoryError, DriverError, RuntimeError, InvalidArrayError, ArgError
        , SizeError, TypeError, DiffTypeError, BatchError, DeviceError
        , NotSupportedError, NotConfiguredError, NonFreeError, NoDblError
        , NoGfxError, LoadLibError, LoadSymError, BackendMismatchError
        , InternalError, UnknownError
        ]

    it "maps unrecognized codes to UnhandledError" $ do
      toAFExceptionType (AFErr 0)     `shouldBe` UnhandledError
      toAFExceptionType (AFErr 12345) `shouldBe` UnhandledError

  -- End-to-end: a genuine ArrayFire failure must cross the FFI boundary as a
  -- typed 'AFException', not a crash or an opaque error.
  describe "library errors surface as AFException" $

    it "a matmul dimension mismatch throws a typed AFException" $ do
      let a = A.mkArray @Double [2,3] [1..6]   -- 2x3
          b = A.mkArray @Double [2,2] [1..4]   -- 2x2  (inner dims 3 /= 2)
      r <- try (evaluate (A.getElements (A.matmul a b A.None A.None)))
             :: IO (Either AFException Int)
      case r of
        Right n ->
          expectationFailure ("expected an AFException, but got " ++ show n)
        Left (AFException ty code _msg) -> do
          ty   `shouldSatisfy` (`elem` [SizeError, ArgError])
          code `shouldSatisfy` (> 0)