-- 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: -- -- compareFunc :: String -> (a -> b) -> CompParams (SameAs a) b -> Comparison (SameAs a) b -> TestBench -- | As with compareFunc but allow for polymorphic inputs by -- specifying the constraint to be used. compareFuncConstraint :: Proxy ca -> String -> (forall a. (ca a) => a -> b) -> CompParams ca b -> Comparison ca b -> TestBench -- | The union of two (* -> Constraint) values. -- -- Whilst type EqNum a = (Eq a, Num a) is a valid -- specification of a Constraint when using the -- ConstraintKinds extension, it cannot be used with -- compareFuncConstraint as type aliases cannot be partially -- applied. -- -- As such, you can use type EqNum = CUnion Eq Num instead. class (c1 a, c2 a) => CUnion c1 c2 a -- | Monoidally build up the parameters used to control a Comparison -- environment. -- -- This will typically be a combination of benchNormalForm with -- either baseline or testWith. data CompParams ca b -- | Evaluate all benchmarks to normal form. benchNormalForm :: (NFData b) => CompParams ca b -- | Allow specifying how benchmarks should be evaluated. This may allow -- usage of methods such as nfIO, but this has not been tested -- as yet. withBenchMode :: (forall a. (ca a) => (a -> b) -> a -> Benchmarkable) -> CompParams ca b -- | Don't run any benchmarks. I'm not sure why you'd want to do this as -- there's surely easier/better testing environments available, but this -- way it's possible. noBenchmarks :: CompParams ca b -- | Specify a sample baseline value to benchmark and test against (such -- that the result of applying the function to this a is what -- everything should match). -- -- You shouldn't specify this more than once, nor mix it with -- noTests or testWith. baseline :: (ca a, Eq b, Show b) => String -> a -> CompParams ca b -- | Specify a predicate that all results should satisfy. -- -- Note that the last statement between testWith, baseline -- and noTests "wins" in specifying which testing (if any) to do. testWith :: (b -> Assertion) -> CompParams ca b -- | Don't run any tests. This isn't recommended, but could be useful if -- all you want to do is run comparisons (potentially because no -- meaningful tests are possible). noTests :: CompParams ca b -- | A specialised monad used solely for running comparisons. -- -- No lifting is permitted; the only operations permitted are -- comp, compBench and compTest. type Comparison ca b = ComparisonM ca b () -- | Benchmark and test (if specified) this value against the specified -- function. comp :: (ca a) => String -> a -> Comparison ca b -- | Only benchmark (but do not test) this value against the specified -- function. compBench :: (ca a) => String -> a -> Comparison ca b -- | Only test (but do not benchmark) this value against the specified -- function. compTest :: (ca a) => String -> a -> Comparison ca b data ComparisonM ca b r -- | An alias for readability. type SameAs a = (~) a instance Control.Monad.IO.Class.MonadIO (TestBench.ComparisonM ca b) instance GHC.Base.Monad (TestBench.ComparisonM ca b) instance GHC.Base.Applicative (TestBench.ComparisonM ca b) instance GHC.Base.Functor (TestBench.ComparisonM ca b) instance GHC.Base.Monoid (TestBench.CompParams ca b) instance Control.Monad.IO.Class.MonadIO TestBench.TestBenchM instance GHC.Base.Monad TestBench.TestBenchM instance GHC.Base.Applicative TestBench.TestBenchM instance GHC.Base.Functor TestBench.TestBenchM instance (c1 a, c2 a) => TestBench.CUnion c1 c2 a