content: {-# LANGUAGE TemplateHaskell #-}
  {-# LANGUAGE DataKinds #-}
  {-# LANGUAGE TypeFamilies #-}
  
  -- | Tests for the Data.LVar.AddRemoveSet module.
  
  module AddRemoveSetTests(tests, runTests) where
  
  import Control.Concurrent
  import Test.Tasty.HUnit 
  import Test.Tasty (TestTree, defaultMain, testGroup)
  --import Test.HUnit (Assertion, assertEqual, assertBool, Counts(..))
  import Test.Tasty.TH (testGroupGenerator)
  import qualified Test.HUnit as HU
  import           TestHelpers2 as T
  
  import qualified Data.Set as S
  
  import qualified Data.LVar.AddRemoveSet as ARS
  
  import           Control.LVish
  import           Control.LVish.DeepFrz (DeepFrz(..), Frzn, Trvrsbl, runParThenFreeze, runParThenFreezeIO)
  import           Control.LVish.Internal (liftIO)
  
  --------------------------------------------------------------------------------
  
  tests :: TestTree
  tests = $(testGroupGenerator)
  
  runTests :: IO ()
  runTests = defaultMain tests
  
  --------------------------------------------------------------------------------
  
  case_v1 :: Assertion
  case_v1 = v1 >>= assertEqual "freeze with 3 elements"
            (S.fromList [1..3] :: S.Set Int)
  
  -- If you have a computation that does freezing, you have to run it with
  -- runParQuasiDet / runParNonDet.
  v1 :: IO (S.Set Int)
  v1 = runParQuasiDet $
       do s <- ARS.newEmptySet
          ARS.insert 1 s
          ARS.insert 2 s
          ARS.insert 3 s
          ARS.waitAddedSize 3 s
          ARS.freezeSet s
  
  case_v2 :: Assertion
  case_v2 = v2 >>= assertEqual "freeze with 10 elements added, asynchronously"
            (S.fromList [1.. v2size] :: S.Set Int)
  
  v2 :: IO (S.Set Int)
  v2 = runParQuasiDet $
       do s <- ARS.newEmptySet
          mapM_ (\n -> fork $ do
                       -- liftIO$ threadDelay 5000 
                       logDbgLn 3$ " [AR-v2] Doing one insert: "++show n
                       ARS.insert n s) [1.. v2size]
          logDbgLn 3$ " [AR-v2] now waiting.."
          ARS.waitAddedSize v2size s
          logDbgLn 3$ " [AR-v2] now freezing.."
          ARS.freezeSet s
  
  v2size =
    case numElems of
      Just x -> x
      Nothing -> 10
  
  case_v3 :: Assertion
  case_v3 = stressTest T.stressTestReps 15 v3 (\()->True)
  
  -- "freeze with 3 elements added, asynchronously"
  -- If we're doing a guaranteed-deterministic computation we can't
  -- actually read out the contents of the set.
  v3 :: (HasPut e, HasGet e) => Par e s ()
  v3 = 
       do s <- ARS.newEmptySet
          mapM_ (\n -> fork $ ARS.insert n s) [1..10::Int]
          ARS.waitAddedSize 10 s
  
  -- Getting occasional failures here with -N2, don't know what's
  -- wrong. :(
  case_v4 :: Assertion
  case_v4 = stressTest T.stressTestReps 30 v4 (== (S.fromList [1..10] :: S.Set Int))
  
  -- "additions and removals"
  v4 :: (HasPut e, HasGet e, HasFreeze e) => Par e s (S.Set Int)
  v4 = 
       do s <- ARS.newEmptySet
          mapM_ (\n -> fork $ ARS.insert n s) [1..15]
          mapM_ (\n -> fork $ ARS.remove n s) [11..15]
          ARS.waitAddedSize 15 s 
          ARS.waitRemovedSize 5 s 
          ARS.freezeSet s
  
  -- This one is intentionally undersynchronized.
  case_i1 :: Assertion
  case_i1 = do
    allowSomeExceptions ["Attempt to change a frozen LVar"] $
      do x <- i1
         assertEqual "additions and removals, undersynchronized"
           (S.fromList [1..10] :: S.Set Int) x
    return ()
  
  -- Unblock too early, leaving a put-after-freeze possibility.
  i1 :: IO (S.Set Int)
  i1 = runParQuasiDet $
       do s <- ARS.newEmptySet
          mapM_ (\n -> fork $ ARS.insert n s) [1..15]
          mapM_ (\n -> fork $ ARS.remove n s) [11..15]
          -- If we don't wait for 15 additions, they might not all be
          -- there when we check.
          ARS.waitRemovedSize 5 s 
          ARS.freezeSet s
  