packages feed

hdr-histogram-0.1.0.0: test/Data/HdrHistogram/ConfigSpec.hs

{-# LANGUAGE ScopedTypeVariables #-}
module Data.HdrHistogram.ConfigSpec where

import           Data.Bits                         (Bits, FiniteBits)
import           Data.HdrHistogram.Config.Internal
import qualified Data.Int                          as Int
import qualified Data.Word                         as Word
import           System.Random                     (Random)
import           Test.Expectations
import           Test.Hspec
import           Test.Hspec.QuickCheck             (prop)
import           Test.QuickCheck

data ConfigAndVal a = ConfigAndVal (HistogramConfig a) a
                    deriving (Show, Eq)

instance (Random a, Arbitrary a, Bounded a, Integral a, Bits a) => Arbitrary (ConfigAndVal a) where
  arbitrary = do
    config' <- arbitrary
    val <- choose (lowest config', highest config')
    return $ ConfigAndVal config' val

data ConfigAndIndex a = ConfigAndIndex (HistogramConfig a) Int
                    deriving (Show, Eq)

instance (Random a, Arbitrary a, Bounded a, Integral a, Bits a) => Arbitrary (ConfigAndIndex a) where
  arbitrary = do
    config' <- arbitrary
    i <- choose (0, size config' - 1)
    return $ ConfigAndIndex config' i


data SpecType a = SpecType Spec

runSpecType :: SpecType a -> Spec
runSpecType (SpecType s) = s

typeSpec :: forall a. (Show a, Integral a, Random a, Arbitrary a, Bounded a, FiniteBits a) => SpecType a
typeSpec = SpecType $ do
  prop "should generate bucket indices in bounds" $ \(ConfigAndVal config' (val :: a)) -> do
    let
      bi = asIndex config' val
      i = asInt config' bi
    bucket bi `shouldBeLessThan` bucketCount config'
    bucket bi `shouldBeGreaterThanOrEqual` 0
    subBucket bi `shouldBeLessThan` subBucketCount config'
    subBucket bi `shouldBeGreaterThanOrEqual` 0
    i `shouldBeLessThan` size config'
    i `shouldBeGreaterThanOrEqual` 0

  prop "index 0 should contain the lowest value" $ \(config' :: HistogramConfig a) -> do
    let
      val' = fromIndex config' $ fromInt config' 0
    upper val' `shouldBeGreaterThanOrEqual` lowest config'
    lower val' `shouldBeLessThanOrEqual` lowest config'

  prop "highest index should contain the highest value" $ \(config' :: HistogramConfig a) -> do
    pendingWith "Unsure this property should hold"
    let
      val' = fromIndex config' $ fromInt config' (size config' - 1)
    upper val' `shouldBeGreaterThanOrEqual` highest config'
    lower val' `shouldBeLessThanOrEqual` highest config'

  prop "asInt . fromInt == id" $ \(ConfigAndIndex (c :: HistogramConfig a) i) ->
    (asInt c . fromInt c) i `shouldBe` i

  prop "fromIndex . asIndex should give contain value" $ \(ConfigAndVal c (val :: a)) -> do
    let
      range = (fromIndex c . asIndex c) val
    upper range `shouldBeGreaterThanOrEqual` val
    lower range `shouldBeLessThanOrEqual` val

spec :: Spec
spec = do
  describe "bitLength" $ do
    it "should match the reference" $ do
      bitLength (0x1 :: Int) `shouldBe` 1
      bitLength (0x2 :: Int) `shouldBe` 2
      bitLength (0x4 :: Int) `shouldBe` 3
      bitLength (0x1000 :: Int) `shouldBe` 13
      bitLength (0x1000000 :: Int) `shouldBe` (64 - 39)

    prop "should return the smallest power of two containing the value"
      $ \(NonNegative a) -> do
      let
        i = fromIntegral (bitLength a)
      floor ((2 :: Double) ** i) `shouldBeGreaterThan` (a :: Int)
      floor (2 ** (i - 1)) `shouldBeLessThanOrEqual` (a :: Int)

  describe "HistogramConfig Int" $ runSpecType (typeSpec :: SpecType Int)
  describe "HistogramConfig Int16" $ runSpecType (typeSpec :: SpecType Int.Int16)
  describe "HistogramConfig Int32" $ runSpecType (typeSpec :: SpecType Int.Int32)
  describe "HistogramConfig Int64" $ runSpecType (typeSpec :: SpecType Int.Int64)
  describe "HistogramConfig Word16" $ runSpecType (typeSpec :: SpecType Word.Word16)
  describe "HistogramConfig Word32" $ runSpecType (typeSpec :: SpecType Word.Word32)
  describe "HistogramConfig Word64" $ runSpecType (typeSpec :: SpecType Word.Word64)