{-# LANGUAGE LinearTypes #-} {-# LANGUAGE RankNTypes #-} {-# LANGUAGE ScopedTypeVariables #-} {-# LANGUAGE TypeApplications #-} {-# LANGUAGE NoImplicitPrelude #-} module Test.Foreign (foreignGCTests) where import Control.Exception hiding (assert) import Control.Monad (void) import Data.Typeable import qualified Foreign.Heap as Heap import Foreign.List (List) import qualified Foreign.List as List import qualified Foreign.Marshal.Pure as Manual import Hedgehog import qualified Hedgehog.Gen as Gen import qualified Hedgehog.Range as Range import Prelude.Linear import Test.Tasty import Test.Tasty.Hedgehog (testProperty) import qualified Prelude -- # Organizing tests ------------------------------------------------------------------------------- foreignGCTests :: TestTree foreignGCTests = testGroup "foreignGCTests" [ listExampleTests, heapExampleTests ] listExampleTests :: TestTree listExampleTests = testGroup "list tests" [ testProperty "List.toList . List.fromList = id" invertNonGCList, testProperty "map id = id" mapIdNonGCList, testProperty "memory freed post-exception" testExecptionOnMem ] heapExampleTests :: TestTree heapExampleTests = testGroup "heap tests" [testProperty "sort = heapsort" nonGCHeapSort] -- # Internal library ------------------------------------------------------------------------------- list :: Gen [Int] list = Gen.list (Range.linear 0 1000) (Gen.int (Range.linear 0 100)) eqList :: forall a. (Manual.Representable a, Movable a, Eq a) => List a %1 -> List a %1 -> Ur Bool eqList l1 l2 = move $ (List.toList l1) == (List.toList l2) data InjectedError = InjectedError deriving (Typeable, Show) instance Exception InjectedError -- # Properties ------------------------------------------------------------------------------- invertNonGCList :: Property invertNonGCList = property $ do xs <- forAll list let xs' = unur $ Manual.withPool (\p -> move $ List.toList $ List.ofList xs p) xs === xs' mapIdNonGCList :: Property mapIdNonGCList = property $ do xs <- forAll list let boolTest = unur $ Manual.withPool $ \p -> dup3 p & \(p0, p1, p2) -> eqList (List.ofList xs p0) (List.map id (List.ofList xs p1) p2) assert boolTest testExecptionOnMem :: Property testExecptionOnMem = property $ do xs <- forAll list let bs = xs ++ (throw InjectedError) let writeBadList = Manual.withPool (move . List.toList . List.ofRList bs) let ignoreCatch = \_ -> Prelude.return () evalIO (catch @InjectedError (void (evaluate writeBadList)) ignoreCatch) nonGCHeapSort :: Property nonGCHeapSort = property $ do xs <- forAll list let ys :: [(Int, ())] = zip xs $ Prelude.replicate (Prelude.length xs) () (Heap.sort ys) === (reverse $ sort ys)