packages feed

inline-java-0.10.0: tests/common/Language/Java/InlineSpec.hs

{-# LANGUAGE DataKinds #-}
{-# LANGUAGE ExplicitNamespaces #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE QuasiQuotes #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE TemplateHaskell #-}
{-# LANGUAGE TypeOperators #-}
-- Test that inline-java produces code without warnings or errors.
{-# OPTIONS_GHC -dcore-lint -Wall -Werror #-}

module Language.Java.InlineSpec(spec) where

import Data.Char
import Data.Int
import qualified Data.Text as Text
import qualified Data.Vector.Storable as Vector
import Data.Vector.Storable.Mutable (IOVector)
import Foreign.JNI (JVMException)
import Language.Java
import Language.Java.Inline
import Test.Hspec
import Test.Hspec.QuickCheck
import Test.QuickCheck
import Test.QuickCheck.Unicode

type ObjectClass = 'Class "java.lang.Object"
type ListClass = 'Iface "java.util.List"

type JJObject = JObject
type List a = J (ListClass <> '[a])

imports "java.util.*"

spec :: Spec
spec = do
    describe "Java quasiquoter" $ do
      it "Can return ()" $ do
        [java| { } |] :: IO ()

      it "Evaluates simple expressions" $ do
        [java| 1 + 1 |] `shouldReturn` (2 :: Int32)

      it "Evaluates simple blocks" $ do
        [java| {
             int x = 1;
             int y = 2;
             return x + y;
           } |] `shouldReturn` (3 :: Int32)

      it "Supports antiquotation variables" $ do
        let x = 1 :: Int32
        [java| $x + 1 |] `shouldReturn` (2 :: Int32)

      describe "Type synonyms" $ do
        it "Supports top-level type synonym'ed antiquotation variables" $ do
          obj <- [java| new Object() {} |]
          let obj1 = obj :: JObject
          _ :: JObject <- [java| $obj1 |]
          return ()

        it "Supports inner type synonym'ed antiquotation variables" $ do
          obj <- [java| new Object() {} |]
          let obj1 = obj :: J ObjectClass
          _ :: J ObjectClass <- [java| $obj1 |]
          return ()

        it "Supports chained type synonym'ed antiquotation variables" $ do
          obj <- [java| new Object() {} |]
          let obj1 = obj :: JJObject
          _ :: JJObject <- [java| $obj1 |]
          return ()

        it "Supports parameterized type synonyms" $ do
          obj :: List ObjectClass <- [java| new ArrayList() |]
          _ :: List ObjectClass <- [java| $obj |]
          return ()

      it "Supports multiple antiquotation variables" $ do
        let foo = 1 :: Int32
            bar = 2 :: Int32
        [java| $foo + $bar |] `shouldReturn` (3 :: Int32)

      it "Supports repeated antiquotation variables" $ do
        obj :: JObject <- [java| new Object() {} |]
        ([java| $obj.equals($obj) |] >>= reify) `shouldReturn` True

      it "Supports antiquotation variables in blocks" $ do
        let z = 1 :: Int32
        [java| { return $z + 1; } |] `shouldReturn` (2 :: Int32)

      it "Supports anonymous classes" $ do
        _ :: JObject <- [java| new Object() {} |]
        return ()

      it "Supports multiple anonymous classes" $ do
        [java| new Object() {}.equals(new Object() {}) |] `shouldReturn` False

      it "Supports using antiquotation variables in inner classes" $ do
        let foo = 1 :: Int32
        [java| { class Foo { int f() { return $foo; } }; } |] :: IO ()

      it "Supports import declarations" $ do
        -- Arrays comes from the java.util package.
        _ <- [java| Arrays.asList().toArray() |] :: IO JObjectArray
        return ()

      it "Supports anonymous functions" $ do
        [java| {
          List<Integer> xs = Arrays.asList(1, 2);
          Collections.sort(xs, (a, b) -> b.compareTo(a));
          return xs.get(0);
          } |] `shouldReturn` (2 :: Int32)

      it "Can be used inside brackets" $ do
        $([| let _x = 1 :: Int32 -- Named _x to avoid spurious "unused" warning
              in (+1) <$> [java| $_x |]
           |]) `shouldReturn` (2 :: Int32)

      it "Can throw checked exceptions" $ do
        ([java| { throw new InterruptedException(); } |] :: IO ())
          `shouldThrow` \(_ :: JVMException) -> True

      it "Type-checks generics" $ do
          obj :: List ('Class "java.lang.String") <-
            [java| new ArrayList<String>() |]
          _ :: List ('Class "java.lang.String") <- [java| $obj |]
          return ()

      it "Can access inner classes" $ do
          st :: J ('Class "java.lang.Thread$State") <-
            [java| Thread.State.NEW |]
          [java| $st == Thread.State.NEW |] `shouldReturn` True

      it "Supports modified utf-8 encoding from Haskell to Java" $
          withLocalRef (reflect ("a\NULb" :: Text.Text)) $ \jString ->
            [java| "a\0b".equals($jString) |] `shouldReturn` True

      it "Supports modified utf-8 encoding from Java to Haskell" $ do
          withLocalRef [java| "a\0b" |] reify `shouldReturn` ("a\NULb" :: Text.Text)

      prop "Processes Unicode code points as the JVM does" $ do
        \u -> ioProperty $ do
          let chars :: [Char] = fromUnicode u
          let text = Text.pack chars
          codePoints :: IOVector Int32 <- Vector.thaw $ Vector.fromList $ map (fromIntegral . ord) chars
          withLocalRef (reflect codePoints) $ \jPoints -> do
            jString <- [java| new String($jPoints, 0, $jPoints.length) |]
            jText <- reify jString
            return $ jText === text