bugsnag-hs-0.1.0.0: test/Spec/Data/Buffer.hs
{-# LANGUAGE NamedFieldPuns #-}
{-# LANGUAGE OverloadedStrings #-}
module Spec.Data.Buffer
( tests,
)
where
import qualified Control.Concurrent
import qualified Control.Concurrent.STM as STM
import qualified Data.Buffer as Buffer
import qualified Data.Foldable
import qualified Data.List
import qualified Data.List.NonEmpty as NonEmpty
import qualified Data.Maybe
import GHC.Natural (Natural)
import Hedgehog
import qualified Hedgehog.Gen as Gen
import qualified Hedgehog.Range as Range
import qualified System.Exit
tests :: IO ()
tests = do
res <- tests'
if res
then pure ()
else System.Exit.die "Spec.Data.Buffer tests failed"
tests' :: IO Bool
tests' =
checkParallel $
Group
"Spec.Data.Buffer"
[ ("all items get sent", allItemsGetSent)
]
allItemsGetSent :: Property
allItemsGetSent = property $ do
steps <- forAll $ numberItems <$> Gen.list (Range.linear 0 100) step
size <- forAll $ fromIntegral <$> Gen.int (Range.linear 1 10)
frequencyInMicroSeconds <- forAll $ waitMicroS
actualOutput <- evalIO (run size frequencyInMicroSeconds steps)
let expectedOutput = Data.Maybe.mapMaybe toItem steps
Data.List.sort actualOutput === Data.List.sort expectedOutput
data Step
= Push Item
| WaitMicroS Int
| Flush
deriving (Show)
newtype Item = Item Int
deriving (Eq, Ord, Show)
step :: Gen Step
step =
Gen.frequency
[ (5, Gen.constant (Push (Item 0))),
(5, WaitMicroS <$> waitMicroS),
(1, Gen.constant Flush)
]
numberItems :: [Step] -> [Step]
numberItems = snd . Data.List.mapAccumL numberItem 0
numberItem :: Int -> Step -> (Int, Step)
numberItem counter step =
case step of
Push (Item _) -> (counter + 1, Push (Item counter))
_ -> (counter, step)
waitMicroS :: Gen Int
waitMicroS = Gen.int (Range.linear 0 maxWaitMicroS)
maxWaitMicroS :: Int
maxWaitMicroS = 10000
toItem :: Step -> Maybe Item
toItem step =
case step of
Push item -> Just item
WaitMicroS _ -> Nothing
Flush -> Nothing
run :: Natural -> Int -> [Step] -> IO [Item]
run size frequencyInMicroSeconds steps = do
-- Setup the buffer to play the steps through.
resultQueue <- STM.newTQueueIO
buffer <-
Buffer.new
Buffer.defaultSettings
{ Buffer.write = writeToQueue resultQueue,
Buffer.size,
Buffer.frequencyInMicroSeconds
}
-- Play the steps one by one.
Data.Foldable.traverse_ (play buffer) steps
-- Give the queue the opportunity to flush by itself.
Control.Concurrent.threadDelay (2 * maxWaitMicroS)
mconcat <$> STM.atomically (STM.flushTQueue resultQueue)
play :: Buffer.Buffer Item -> Step -> IO ()
play buffer step =
case step of
Push item -> Buffer.push buffer item
WaitMicroS delay -> Control.Concurrent.threadDelay delay
Flush -> Buffer.flush buffer
writeToQueue :: STM.TQueue [a] -> NonEmpty.NonEmpty a -> IO ()
writeToQueue queue list = STM.atomically (STM.writeTQueue queue (NonEmpty.toList list))