jni-0.8.0: tests/Foreign/JNISpec.hs
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE KindSignatures #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE TypeApplications #-}
module Foreign.JNISpec where
import Control.Concurrent (runInBoundThread)
import Control.Exception (try)
import Data.List (sort)
import Data.Singletons
import Foreign.JNI.Internal (jniMethodToJavaSignature)
import Foreign.JNI.String (fromChars)
import Foreign.JNI.Types
import Foreign.JNI.Unsafe
import qualified Foreign.JNI.Unsafe.Internal as Internal
import Foreign.JNI.Unsafe.Internal.Introspection
import Test.Hspec
spec :: Spec
spec = do
describe "runInAttachedThread" $ do
it "can run jni calls in another thread" $
runInBoundThread $ runInAttachedThread $ do
jclass <- findClass $
referenceTypeName (sing :: Sing ('Class "java.lang.Long"))
deleteLocalRef jclass
it "is needed to run jni calls in another thread" $
runInBoundThread $ do
findClass (referenceTypeName (sing :: Sing ('Class "java.lang.Long")))
`shouldThrow` \ThreadNotAttached -> True
around_ (runInBoundThread . runInAttachedThread) $ do
describe "isInstanceOf" $ do
it "identifies a class name as a String" $ do
klong <- findClass (referenceTypeName (sing :: Sing ('Class "java.lang.Long")))
name <- callObjectMethod klong classGetNameMethod []
kstring <- findClass (referenceTypeName (sing :: Sing ('Class "java.lang.String")))
isInstanceOf name kstring `shouldReturn` True
it "identifies a class name as an Object" $ do
klong <- findClass (referenceTypeName (sing :: Sing ('Class "java.lang.Long")))
name <- callObjectMethod klong classGetNameMethod []
kobject <- findClass (referenceTypeName (sing :: Sing ('Class "java.lang.Object")))
isInstanceOf name kobject `shouldReturn` True
it "doesn't identify a class name as a Long" $ do
klong <- findClass (referenceTypeName (sing :: Sing ('Class "java.lang.Long")))
name <- callObjectMethod klong classGetNameMethod []
isInstanceOf name klong `shouldReturn` False
describe "getMethodID" $ do
it "gives correct hints on a mistake" $ do
kstring <- findClass (referenceTypeName (sing :: Sing ('Class "java.lang.String")))
let sig = methodSignature [] (sing @('Class "java.lang.String"))
result <- try $ getMethodID kstring (fromChars "replace") sig
case result of
Left e -> do
sort (noSuchMethodOverloadings e) `shouldBe`
[ "public java.lang.String java.lang.String.replace(char,char)"
, "public java.lang.String java.lang.String.replace(java.lang.CharSequence,java.lang.CharSequence)"
]
_ -> expectationFailure "call should have failed with a NoSuchMethod exception"
describe "jniMethodToJavaSignature" $
it "converts JNI signatures correctly" $ do
jniMethodToJavaSignature "()J" `shouldBe` Right ([], "long")
jniMethodToJavaSignature "(I)B" `shouldBe` Right (["int"], "byte")
jniMethodToJavaSignature "(SF)V" `shouldBe` Right (["short", "float"], "void")
jniMethodToJavaSignature "(C[Ljava/lang/String;D)Z"
`shouldBe` Right (["char", "java.lang.String[]", "double"], "boolean")
describe "showException" $
it "correctly displays exceptions" $ do
kinteger <- findClass (referenceTypeName (sing :: Sing ('Class "java.lang.Integer")))
let sig = methodSignature [] (sing :: Sing 'Void)
result <- try $ Internal.getMethodID kinteger (fromChars "toString") sig
case result of
Left (e :: JVMException) -> showException e `shouldReturn` "java.lang.NoSuchMethodError: toString\n"
_ -> expectationFailure "call should have failed with a JVMException"