content: {-# OPTIONS_GHC -Wno-deprecations #-}
  
  -- |
  -- Module      : Streamly.Test.Prelude.WSerial
  -- Copyright   : (c) 2020 Composewell Technologies
  --
  -- License     : BSD-3-Clause
  -- Maintainer  : streamly@composewell.com
  -- Stability   : experimental
  -- Portability : GHC
  
  module Streamly.Test.Prelude.WSerial (main) where
  
  import Data.List (sort)
  import Test.QuickCheck (Property, forAll)
  import Test.Hspec.QuickCheck
  import Test.QuickCheck.Monadic (monadicIO, run)
  import Test.Hspec as H
  
  import qualified Streamly.Internal.Data.Stream.Serial as Serial
  
  import Streamly.Prelude hiding (repeat)
  import qualified Streamly.Prelude as S
  
  import Streamly.Test.Common
  import Streamly.Test.Prelude.Common
  
  associativityCheck
      :: String
      -> (WSerialT IO Int -> SerialT IO Int)
      -> Spec
  associativityCheck desc t = prop desc assocCheckProp
    where
      assocCheckProp :: [Int] -> [Int] -> [Int] -> Property
      assocCheckProp xs ys zs =
          monadicIO $ do
              let xStream = S.fromList xs
                  yStream = S.fromList ys
                  zStream = S.fromList zs
              infixAssocstream <-
                  run $ S.toList $ t $ xStream `wSerial` yStream `wSerial` zStream
              assocStream <- run $ S.toList $ t $ xStream <> yStream <> zStream
              listEquals (==) infixAssocstream assocStream
  
  interleaveCheck :: IsStream t
      => (t IO Int -> SerialT IO Int)
      -> (t IO Int -> t IO Int -> t IO Int)
      -> Spec
  interleaveCheck t f =
      it "Interleave four" $
          (S.toList . t) ((singleton 0 `f` singleton 1) `f` (singleton 100 `f` singleton 101))
              `shouldReturn` [0, 100, 1, 101]
  
      where
  
      singleton :: IsStream t => a -> t m a
      singleton a = a .: nil
  
  wSerialMinLengthProp :: Property
  wSerialMinLengthProp =
      forAll (chooseInt (0, 10))
          $ \len ->
              S.length (fromWSerial $ combined len) `shouldReturn` 2 * len + 1
  
      where
  
      finiteStream len = S.take len $ S.repeat (1 :: Int)
      infiniteStream = S.repeat 1
      combined len = infiniteStream `Serial.wSerialMin` finiteStream len
  
  moduleName :: String
  moduleName = "Prelude.WSerial"
  
  main :: IO ()
  main = hspec
    $ H.parallel
  #ifdef COVERAGE_BUILD
    $ modifyMaxSuccess (const 10)
  #endif
    $ describe moduleName $ do
      let wSerialOps :: IsStream t => ((WSerialT IO a -> t IO a) -> Spec) -> Spec
          wSerialOps spec = mapOps spec $ makeOps fromWSerial
  #ifndef COVERAGE_BUILD
              <> [("rate AvgRate 0.00000001", fromWSerial . avgRate 0.00000001)]
              <> [("maxBuffer (-1)", fromWSerial . maxBuffer (-1))]
  #endif
  
      describe "Construction" $ do
          wSerialOps  $ prop "wSerially repeat" . constructWithRepeat
          wSerialOps  $ prop "wSerially repeatM" . constructWithRepeatM
          wSerialOps  $ prop "wSerially replicateM" . constructWithReplicate
          wSerialOps  $ prop "wSerially replicateM" . constructWithReplicateM
          wSerialOps $ prop "wSerially cons" . constructWithCons S.cons
          wSerialOps $ prop "wSerially consM" . constructWithConsM S.consM id
          wSerialOps $ prop "wSerially (.:)" . constructWithCons (S..:)
          wSerialOps $ prop "wSerially (|:)" . constructWithConsM (S.|:) id
  
      describe "Functor operations" $ do
          wSerialOps   $ functorOps S.fromFoldable "wSerially" (==)
          wSerialOps   $ functorOps folded "wSerially folded" (==)
  
      describe "Monoid operations" $ do
          wSerialOps   $ monoidOps "wSerially" mempty sortEq
  
      describe "Bind and Monoidal composition combinations" $ do
          -- XXX Taking a long time when wSerialOps is used.
          bindAndComposeSimpleOps "WSerial" sortEq fromWSerial
          bindAndComposeHierarchyOps "WSerial" fromWSerial
          wSerialOps $ nestTwoStreams "WSerial" id sort
          wSerialOps $ nestTwoStreamsApp "WSerial" id sort
          composeAndComposeSimpleSerially
              "WSerial <> "
              [ [1, 4, 2, 7, 3, 5, 8, 6, 9]
              , [1, 7, 4, 8, 2, 9, 5, 3, 6]
              , [1, 4, 2, 7, 3, 5, 8, 6, 9]
              , [1, 7, 4, 8, 2, 9, 5, 3, 6]
              ]
              fromWSerial
          composeAndComposeSimpleWSerially
              "WSerial <> "
              [ [1, 4, 2, 7, 3, 5, 8, 6, 9]
              , [1, 7, 4, 8, 2, 9, 5, 3, 6]
              , [1, 4, 3, 7, 2, 6, 9, 5, 8]
              , [1, 7, 4, 9, 3, 8, 6, 2, 5]
              ]
              fromWSerial
  
      describe "Semigroup operations" $ do
          wSerialOps $ semigroupOps "wSerially" (==)
          wSerialOps $ associativityCheck "wSerial == <>"
  
      describe "Applicative operations" $ do
          wSerialOps $ applicativeOps S.fromFoldable "wSerially applicative" sortEq
          wSerialOps $ applicativeOps folded "wSerially applicative folded" sortEq
  
      -- XXX add tests for indexed/indexedR
      describe "Zip operations" $ do
          -- We test only the serial zip with serial streams and the parallel
          -- stream, because the rate setting in these streams can slow down
          -- zipAsync.
          wSerialOps  $ prop "zip monadic wSerially" . zipMonadic S.fromFoldable (==)
          wSerialOps  $ prop "zip monadic wSerially folded" . zipMonadic folded (==)
  
      describe "Monad operations" $ do
          wSerialOps  $ prop "wSerially monad then" . monadThen S.fromFoldable sortEq
          wSerialOps  $ prop "wSerially monad then folded" . monadThen folded sortEq
          wSerialOps  $ prop "wSerially monad bind" . monadBind S.fromFoldable sortEq
          wSerialOps  $ prop "wSerially monad bind folded" . monadBind folded sortEq
  
      describe "Stream transform and combine operations" $ do
          wSerialOps   $ transformCombineOpsCommon S.fromFoldable "wSerially" sortEq
          wSerialOps   $ transformCombineOpsCommon folded "wSerially" sortEq
  
      describe "Stream elimination operations" $ do
          wSerialOps   $ eliminationOps S.fromFoldable "wSerially"
          wSerialOps   $ eliminationOps folded "wSerially folded"
          wSerialOps   $ eliminationOpsWord8 S.fromFoldable "wSerially"
          wSerialOps   $ eliminationOpsWord8 folded "wSerially folded"
  
      -- XXX Add a test where we chain all transformation APIs and make sure that
      -- the state is being passed through all of them.
      describe "Stream serial elimination operations" $ do
          wSerialOps   $ eliminationOpsOrdered S.fromFoldable "wSerially"
          wSerialOps   $ eliminationOpsOrdered folded "wSerially folded"
  
      ---------------------------------------------------------------------------
      -- Semigroup/Monoidal Composition strict ordering checks
      ---------------------------------------------------------------------------
  
      describe "WSerial interleaved (<>) ordering check" $
          interleaveCheck fromWSerial (<>)
      describe "WSerial interleaved mappend ordering check" $
          interleaveCheck fromWSerial mappend
  
      describe "Tests for exceptions" $ wSerialOps $ exceptionOps "wSerially"
      describe "Composed MonadThrow wSerially" $ composeWithMonadThrow fromWSerial
  
      ---------------------------------------------------------------------------
      -- Termination checks
      ---------------------------------------------------------------------------
  
      describe "wSerialMin termination" $
               prop "len (infinite `wSerialMin` finite) = 2 * len(finite) + 1"
                    wSerialMinLengthProp
  