packages feed

phino-0.0.144: test/PoolSpec.hs

-- SPDX-FileCopyrightText: Copyright (c) 2025 Objectionary.com
-- SPDX-License-Identifier: MIT

module PoolSpec where

import Control.Concurrent (threadDelay)
import Control.Exception (ErrorCall (..), throwIO, try)
import Data.IORef (atomicModifyIORef', newIORef, readIORef)
import Pool (pooled)
import Test.Hspec (Spec, describe, it, shouldReturn)

spec :: Spec
spec = describe "Pool" $ do
  it "folds what the actions gave in the order they were listed" $
    pooled 3 [threadDelay (pause * 1000) >> pure pause | pause <- [9, 1, 7, 2, 5]] (\acc pause -> pure (acc ++ [pause])) []
      `shouldReturn` [9, 1, 7, 2, 5 :: Int]
  it "runs no more actions at once than it was given" $ do
    running <- newIORef (0 :: Int)
    most <- newIORef (0 :: Int)
    let action :: IO ()
        action = do
          now <- atomicModifyIORef' running (\count -> (count + 1, count + 1))
          atomicModifyIORef' most (\peak -> (max peak now, ()))
          threadDelay 2000
          atomicModifyIORef' running (\count -> (count - 1, ()))
    pooled 2 (replicate 7 action) (\_ _ -> pure ()) ()
    readIORef most `shouldReturn` 2
  it "throws what an action threw once the actions before it are folded" $ do
    folded <- newIORef []
    outcome <- try (pooled 2 [pure 'x', throwIO (ErrorCall "broken"), pure 'z'] (\_ ch -> atomicModifyIORef' folded (\seen -> (seen ++ [ch], ()))) ())
    (,) outcome <$> readIORef folded `shouldReturn` (Left (ErrorCall "broken"), "x")
  it "folds nothing where it is given no action" $
    pooled 4 ([] :: [IO Int]) (\acc val -> pure (acc + val)) 42 `shouldReturn` 42
  it "folds every action where it may run one at a time" $
    pooled 1 (map pure [3, 8, 1]) (\acc val -> pure (acc * 10 + val)) 0 `shouldReturn` (381 :: Int)