golds-gym-0.4.0.0: Golden testing framework for performance benchmarks
Copyright(c) 2026
LicenseMIT
Maintainer@ocramz
Safe HaskellNone
LanguageHaskell2010

Test.Hspec.BenchGolden.Lenses

Description

This module provides van Laarhoven lenses for GoldenStats fields and expectation combinators for building custom performance assertions.

Quick Start

import Test.Hspec
import Test.Hspec.BenchGolden
import Test.Hspec.BenchGolden.Lenses

main :: IO ()
main = hspec $ do
  describe "Custom Expectations" $ do
    -- Expect median within 10% tolerance
    benchGoldenWithExpectation "median-based" defaultBenchConfig
      [expect _statsMedian (Percent 10.0)]
      myAction

    -- Expect IQR within absolute 0.5ms
    benchGoldenWithExpectation "low variance" defaultBenchConfig
      [expect _statsIQR (Absolute 0.5)]
      myAction

    -- Compose multiple expectations (both must pass)
    benchGoldenWithExpectation "composed" defaultBenchConfig
      [ expect _statsMean (Percent 15.0) &&~
        expect _statsMAD (Percent 50.0)
      ]
      myAction

Lenses

Simple van Laarhoven lenses provide access to GoldenStats fields:

Smart Selectors

metricFor and varianceFor automatically select the appropriate lens based on BenchConfig settings:

let lens = metricFor config  -- Returns _statsTrimmedMean if useRobustStatistics
    baseline = golden ^. lens
    current = actual ^. lens

Expectation Combinators

Build expectations with expect and compose them:

  • Percent tolerance - e.g., Percent 15.0 for ±15%
  • Absolute tolerance - e.g., Absolute 0.01 for ±0.01ms
  • Hybrid tolerance - e.g., Hybrid 15.0 0.01 (pass if either satisfied)

Boolean composition operators:

  • (&&~) - AND (both expectations must pass)
  • (||~) - OR (either expectation can pass)

Infix Operators

For concise tolerance checking:

  • (@~) - Within percentage: baseline @~ 15.0 $ actual
  • (@<) - Within absolute: baseline @< 0.01 $ actual
  • (@<<) - Must be faster (negative tolerance): baseline @<< 5.0 $ actual
  • (@>>) - Must be slower (positive tolerance): baseline @>> 5.0 $ actual
Synopsis

Lenses for GoldenStats

_statsMean :: Lens' GoldenStats Double Source #

Lens for mean execution time in milliseconds.

_statsStddev :: Lens' GoldenStats Double Source #

Lens for standard deviation in milliseconds.

_statsMedian :: Lens' GoldenStats Double Source #

Lens for median execution time in milliseconds.

_statsMin :: Lens' GoldenStats Double Source #

Lens for minimum execution time in milliseconds.

_statsMax :: Lens' GoldenStats Double Source #

Lens for maximum execution time in milliseconds.

_statsTrimmedMean :: Lens' GoldenStats Double Source #

Lens for trimmed mean (with tails removed) in milliseconds.

_statsMAD :: Lens' GoldenStats Double Source #

Lens for median absolute deviation (MAD) in milliseconds.

_statsIQR :: Lens' GoldenStats Double Source #

Lens for interquartile range (IQR = Q3 - Q1) in milliseconds.

Smart Metric Selectors

metricFor :: BenchConfig -> Lens' GoldenStats Double Source #

Select the appropriate central tendency metric based on configuration.

Returns:

Example:

let lens = metricFor config
    baseline = golden ^. lens
    current = actual ^. lens

varianceFor :: BenchConfig -> Lens' GoldenStats Double Source #

Select the appropriate dispersion metric based on configuration.

Returns:

Example:

let vLens = varianceFor config
    goldenVar = golden ^. vLens
    actualVar = actual ^. vLens

Expectation Types

data Expectation Source #

An expectation for comparing golden and actual statistics.

Expectations can be composed using boolean operators:

expect _statsMean (Percent 15.0) &&~ expect _statsMAD (Percent 50.0)

Constructors

ExpectStat !(Lens' GoldenStats Double) !Tolerance

Expect a specific field to be within tolerance

And !Expectation !Expectation

Both expectations must pass

Or !Expectation !Expectation

Either expectation can pass

Instances

Instances details
Show Expectation Source # 
Instance details

Defined in Test.Hspec.BenchGolden.Lenses

Eq Expectation Source # 
Instance details

Defined in Test.Hspec.BenchGolden.Lenses

data Tolerance Source #

Tolerance specification for performance comparison.

Constructors

Percent !Double

Percentage tolerance (e.g., Percent 15.0 = ±15%)

Absolute !Double

Absolute tolerance in milliseconds (e.g., Absolute 0.01 = ±0.01ms)

Hybrid !Double !Double

Hybrid tolerance: pass if EITHER percentage OR absolute is satisfied (e.g., Hybrid 15.0 0.01 = pass if within ±15% OR ±0.01ms)

MustImprove !Double

Must be faster by at least this percentage (e.g., MustImprove 10.0 = must be ≥10% faster)

MustRegress !Double

Must be slower by at least this percentage (e.g., MustRegress 5.0 = must be ≥5% slower)

Instances

Instances details
Show Tolerance Source # 
Instance details

Defined in Test.Hspec.BenchGolden.Lenses

Eq Tolerance Source # 
Instance details

Defined in Test.Hspec.BenchGolden.Lenses

Expectation Combinators

expect :: Lens' GoldenStats Double -> Tolerance -> Expectation Source #

Create an expectation for a specific statistic field.

Example:

expect _statsMedian (Percent 10.0)
expect _statsIQR (Absolute 0.5)
expect _statsMean (Hybrid 15.0 0.01)
expect _statsMean (MustImprove 10.0)

expectStat :: Lens' GoldenStats Double -> Tolerance -> Expectation Source #

Create an expectation using a custom lens.

This is an alias for expect for compatibility.

checkExpectation :: Expectation -> GoldenStats -> GoldenStats -> Bool Source #

Check if an expectation is satisfied for the given golden and actual stats.

Returns True if the expectation passes, False otherwise.

Tolerance Checking Functions

withinPercent :: Double -> Double -> Double -> Bool Source #

Check if value is within percentage tolerance.

withinPercent 15.0 baseline actual  -- within ±15%

withinAbsolute :: Double -> Double -> Double -> Bool Source #

Check if value is within absolute tolerance (milliseconds).

withinAbsolute 0.01 baseline actual  -- within ±0.01ms

withinHybrid :: Double -> Double -> Double -> Double -> Bool Source #

Check if value satisfies hybrid tolerance (percentage OR absolute).

withinHybrid 15.0 0.01 baseline actual  -- within ±15% OR ±0.01ms

mustImprove :: Double -> Double -> Double -> Bool Source #

Check if actual is faster than baseline by at least the given percentage.

mustImprove 10.0 baseline actual  -- must be ≥10% faster

mustRegress :: Double -> Double -> Double -> Bool Source #

Check if actual is slower than baseline by at least the given percentage.

mustRegress 5.0 baseline actual  -- must be ≥5% slower

Infix Operators

(@~) :: Double -> Double -> Double -> Bool infixl 4 Source #

Infix operator for percentage tolerance check.

baseline @~ 15.0 $ actual  -- within ±15%

(@<) :: Double -> Double -> Double -> Bool infixl 4 Source #

Infix operator for absolute tolerance check.

baseline @< 0.01 $ actual  -- within ±0.01ms

(@<<) :: Double -> Double -> Double -> Bool infixl 4 Source #

Infix operator for "must improve" check.

baseline @<< 10.0 $ actual  -- must be ≥10% faster

(@>>) :: Double -> Double -> Double -> Bool infixl 4 Source #

Infix operator for "must regress" check.

baseline @>> 5.0 $ actual  -- must be ≥5% slower  

Boolean Composition

(&&~) :: Expectation -> Expectation -> Expectation infixr 3 Source #

AND composition of expectations (both must pass).

expect _statsMean (Percent 15.0) &&~ expect _statsMAD (Percent 50.0)

(||~) :: Expectation -> Expectation -> Expectation infixr 2 Source #

OR composition of expectations (either can pass).

expect _statsMedian (Percent 10.0) ||~ expect _statsMin (Absolute 0.01)

Utilities

percentDiff :: Double -> Double -> Double Source #

Calculate percentage difference between baseline and actual.

Returns: ((actual - baseline) / baseline) * 100

  • Positive = regression (slower)
  • Negative = improvement (faster)
  • Zero = no change

absDiff :: Double -> Double -> Double Source #

Calculate absolute difference between baseline and actual.

Returns: abs(actual - baseline)

toleranceFromExpectation :: Expectation -> (Double, Maybe Double) Source #

Extract tolerance description from an expectation for error messages. For compound expectations (And/Or), returns the first tolerance found.

toleranceValues :: Tolerance -> (Double, Maybe Double) Source #

Extract percentage and optional absolute tolerance from a Tolerance.