packages feed

clr-inline-0.1.0.0: test/InlineSpec.hs

{-# LANGUAGE BangPatterns #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE QuasiQuotes #-}
{-# LANGUAGE TemplateHaskell #-}
module InlineSpec where

import Control.Concurrent
import Control.Monad
import Clr.Inline
import Data.Int
import Data.Text as Text (pack)
import Data.Word
import Test.Hspec
import System.Mem
import System.Mem.Weak

[csharp| using System;
         using System.Collections.Generic;
       |]

[fsharp|
open System
open System.Collections.Generic
|]

type SystemDateTime = Clr "System.DateTime"
type DateTime = Clr "DateTime"

main = hspec spec

h_i   = 2 :: Int
h_i16 = 2 :: Int16
h_i32 = 2 :: Int32
h_i64 = 2 :: Int64
h_w16 = 1 :: Word16
h_w32 = 1 :: Word32
h_w64 = 1 :: Word64
h_d = 2.2 :: Double
h_b = False
h_s = "Hello from Haskell"
h_t = Text.pack h_s

spec :: Spec
spec = beforeAll_ startClr $ do

  it "F# inlines pick up imports" $
    [fsharp| ignore <| Dictionary<int,int>() |]

  it "C# inlines pick up imports" $
    [csharp| var foo = new Dictionary<int,int>(); return ;|]

  it "F# inlines return all primitive types" $ do
    [fsharp| int{2}|] `shouldReturn` 2
    [fsharp| bool{false}|] `shouldReturn` False
    [fsharp| double{2.2}|] `shouldReturn` 2.2
    [fsharp| int16{2s}|] `shouldReturn` 2
    [fsharp| int32{2}|] `shouldReturn` 2
    [fsharp| int64{2L}|] `shouldReturn` 2
    [fsharp| long{2L}|] `shouldReturn` 2
    [fsharp| uint16{2us}|] `shouldReturn` 2
    [fsharp| word16{2us}|] `shouldReturn` 2
    [fsharp| uint32{2u}|] `shouldReturn` 2
    [fsharp| word32{2u}|] `shouldReturn` 2
    [fsharp| uint64{2UL}|] `shouldReturn` 2
    [fsharp| word64{2UL}|] `shouldReturn` 2
    [fsharp| text{"hello"}|] `shouldReturn` pack "hello"
    [fsharp| string{"hello"}|] `shouldReturn` "hello"

  it "F# inlines return reference types" $ do
    date <- [fsharp| DateTime{ DateTime.Today}|]
    return ()

  it "F# supports antiquoting int" $
    [fsharp|bool{$h_i:int = 2}|] `shouldReturn` True
  it "F# supports antiquoting int16" $
    [fsharp|bool{$h_i16:int16 = 2s}|] `shouldReturn` True
  it "F# supports antiquoting int32" $
    [fsharp|bool{$h_i32:int32 = 2}|] `shouldReturn` True
  it "F# supports antiquoting int64" $
    [fsharp|bool{$h_i64:int64 = 2L}|] `shouldReturn` True
  it "F# supports antiquoting long" $
    [fsharp|bool{$h_i64:long  = 2L}|] `shouldReturn` True
  it "F# supports antiquoting uint16" $
    [fsharp|bool{$h_w16:uint16 = 1us}|] `shouldReturn` True
  it "F# supports antiquoting uint32" $
    [fsharp|bool{$h_w32:uint32 = 1u}|] `shouldReturn` True
  it "F# supports antiquoting uint64" $
    [fsharp|bool{$h_w64:uint64 = 1UL}|] `shouldReturn` True
  it "F# supports antiquoting bool" $
    [fsharp|bool{$h_b:bool = false}|] `shouldReturn` True
  it "F# supports antiquoting double" $
    [fsharp|bool{$h_d:double = 2.2}|] `shouldReturn` True
  it "F# supports antiquoting string" $
    [fsharp|bool{$h_s:string = "Hello from Haskell"}|] `shouldReturn` True
  it "F# supports antiquoting text" $
    [fsharp|bool{$h_t:text = "Hello from Haskell"}|] `shouldReturn` True

  it "F# handles two antiquotations" $
    [fsharp|bool{$h_i32:int32 + $h_i:int = 4}|] `shouldReturn` True

  it "F# tuples are handled correctly" $ do
    tuple <- [fsharp| int*bool{8,true}|]
    [fsharp| bool{snd $tuple:int*bool}|] `shouldReturn` True
    [fsharp| int {fst $tuple:int*bool}|] `shouldReturn` 8

  it "Reference types are released when Haskell GCs them" $ do
    !tuple <-
      [fsharp|WeakReference*Object{
           let d = obj()
           let w = WeakReference(d)
           (w,d)}|]
    !w <- [fsharp|WeakReference{fst $tuple:WeakReference*Object}|]
    gcUntil [fsharp|bool{System.GC.Collect(); not ($w:WeakReference).IsAlive}|]

  it "But not any earlier" $ do
    tuple <-
      [fsharp|WeakReference*Object{
           let d = Object()
           let w = WeakReference(d)
           if not (Object.ReferenceEquals(w.Target,d)) then
               failwithf "This is not a good test(%O,%O)" w.Target d
           (w,d)}|]
    w <- [fsharp|WeakReference{fst $tuple:WeakReference*Object}|]
    d <- [fsharp|Object{snd $tuple:WeakReference*Object}|]
    performGC
    threadDelay 50000
    [fsharp|bool{
           GC.Collect()
           let res = ($w:WeakReference).IsAlive
           GC.KeepAlive($d:Object)
           res}
           |] `shouldReturn` True

  it "F# generics are handled" $ do
        dict <- [fsharp| Map<int,string> {
                    [ 1,"Foo" ; 2, "bar" ] |> Map.ofSeq
                 }|]
        [fsharp| string{ ($dict:Map<int,string>).[1] }|]
          `shouldReturn` "Foo"

  it "C# arrays are handled" $ do
      i_array <- [csharp| int[] {
                        int[] a = new int[4]{0,0,0,0};
                        for(int i=0; i < 4; i++) {
                          a[i] = i;
                        }
                        return a;
                        }|]
      [csharp|int{return ($i_array:int[])[2];}|] `shouldReturn` 2

gcUntil cond = do
  let loop 10 = error "gc: tried too many times"
      loop i  = do
        performGC
        threadDelay (i * 10000)
        success <- cond
        unless success $ loop (i+1)
  loop 0