-- Hoogle documentation, generated by Haddock -- See Hoogle, http://www.haskell.org/hoogle/ -- | Create tests and benchmarks together -- -- Test your benchmarks! Benchmark your tests! -- -- It's too easy to accidentally try and benchmark apples and oranges -- together. Wouldn't it be nice if you could somehow guarantee that your -- benchmarks satisfy some simple tests (e.g. a group of comparisons all -- return the same value)? -- -- Furthermore, trying to compare multiple inputs/functions against each -- other requires a lot of boilerplate, making it even easier to -- accidentally compare the wrong things (e.g. using whnf instead -- of nf). -- -- testbench aims to help solve these problems and more by making -- it easier to write unit tests and benchmarks together by stating -- up-front what requirements are needed and then using simple functions -- to state the next parameter to be tested/benchmarked. @package testbench @version 0.1.0.0 -- | Make it easier to compare benchmarks and to test that benchmarks are -- indeed valid. -- -- At the top level you will probably run the testBench function, -- and create comparisons using compareFunc or -- compareFuncConstraint. -- -- For example: -- --
-- main :: IO ()
-- main = testBench $ do
-- -- Compare how long it takes to make a list of the specified length.
-- compareFunc "List length"
-- (\n -> length (replicate n ()) == n)
-- (testWith (@? "Not as long as specified") `mappend` benchNormalForm)
-- (mapM_ (\n -> comp ("len == " ++ show n) n) [1..5])
--
-- -- Polymorphic comparisons.
-- --
-- -- Currently it isn't possible to use a Proxy as the argument to
-- -- the function (this will probably require Injective Type Families
-- -- in GHC 8.0), so we're using 'undefined' to specify the type.
-- compareFuncConstraint (Proxy :: Proxy (CUnion Eq Num))
-- "Number type equality"
-- (join (==) . (0`asTypeOf`))
-- (baseline "Integer" (undefined :: Integer) `mappend` benchNormalForm)
-- $ do comp "Int" (undefined :: Int)
-- comp "Double" (undefined :: Double)
--
--
-- When run, the output will look something like:
--
-- -- Cases: 7 Tried: 7 Errors: 0 Failures: 0 -- Mean MeanLB MeanUB Stddev StddevLB StddevUB OutlierVariance -- List length -- len == 1 323.8 ns 318.6 ns 335.9 ns 23.86 ns 5.834 ns 40.90 ns 83% -- len == 2 352.8 ns 349.1 ns 358.1 ns 15.05 ns 11.76 ns 19.62 ns 61% -- len == 3 372.4 ns 358.4 ns 393.8 ns 62.50 ns 39.83 ns 90.85 ns 96% -- len == 4 396.3 ns 378.4 ns 419.2 ns 67.83 ns 46.71 ns 94.74 ns 96% -- len == 5 426.0 ns 407.0 ns 459.5 ns 82.23 ns 53.37 ns 110.2 ns 97% -- Number type equality -- Integer 75.43 ns 74.48 ns 76.71 ns 3.615 ns 2.748 ns 5.524 ns 69% -- Int 74.39 ns 73.48 ns 76.24 ns 3.964 ns 2.500 ns 7.235 ns 74% -- Double 78.05 ns 75.84 ns 82.50 ns 9.790 ns 6.133 ns 16.99 ns 94% --module TestBench -- | An environment for combining testing and benchmarking. type TestBench = TestBenchM () -- | Run the specified benchmarks if and only if all tests pass, using a -- comparison-based format for benchmarking output. -- -- Please note that this is currently very simplistic: no parameters, -- configuration, etc. Also, benchmark results will not be shown until -- all benchmarks are complete. -- -- For more control, use getTestBenches. testBench :: TestBench -> IO () -- | Obtain the resulting tests and benchmarks from the specified -- TestBench. getTestBenches :: TestBench -> IO (Test, BenchForest) -- | A more explicit tree-like structure for benchmarks than using -- Criterion's Benchmark type. type BenchTree = LabelTree (String, Benchmarkable) type BenchForest = [BenchTree] -- | Remove the explicit tree-like structure into the implicit one used by -- Criterion. -- -- Useful for embedding the results into an existing benchmark suite. flattenBenchForest :: BenchForest -> [Benchmark] -- | Run the specified benchmarks, printing the results (once they're all -- complete) to stdout in a tabular format for easier comparisons. benchmarkForest :: Config -> BenchForest -> IO () data TestBenchM r -- | A tree of operations. type OpTree = LabelTree Operation -- | An individual operation potentially consisting of a benchmark and/or -- test. data Operation -- | A simple labelled rose-tree data structure. data LabelTree a Leaf :: a -> LabelTree a Branch :: String -> [LabelTree a] -> LabelTree a -- | Label a sub-part of a TestBench. collection :: String -> TestBench -> TestBench -- | Create a single benchmark evaluated to normal form, where the results -- should equal the value specified. nfEq :: (NFData b, Show b, Eq b) => b -> (a -> b) -> String -> a -> TestBench -- | Create a single benchmark evaluated to weak head normal form, where -- the results should equal the value specified. whnfEq :: (Show b, Eq b) => b -> (a -> b) -> String -> a -> TestBench -- | A way of writing custom testing/benchmarking statements. You will -- probably want to use one of the pre-defined versions instead. mkTestBench :: ((a -> b) -> a -> Maybe Benchmarkable) -> (b -> Maybe Assertion) -> (a -> b) -> String -> a -> TestBench -- | Compare how various input values (of the same type) behave for a -- specific function. -- -- By default: -- --