inspection-testing
GHC plugin to do inspection testing
https://github.com/nomeata/inspection-testing
LTS Haskell 23.1: | 0.5.0.3@rev:3 |
Stackage Nightly 2024-12-27: | 0.5.0.3@rev:3 |
Latest on Hackage: | 0.5.0.3@rev:3 |
inspection-testing-0.5.0.3@sha256:48719f5921c6ee6efb8e8df3cb7d44dfddeb9c369ea764db6843ae8d6089c6ac,8674
Module documentation for 0.5.0.3
Inspection Testing for Haskell
This GHC plugin allows you to embed assertions about the intermediate code into your Haskell code, and have them checked by GHC. This is called inspection testing (as it automates what you do when you manually inspect the intermediate code).
Synopsis
See the Test.Inspection
module for the documentation, but there really isn’t much
more to it than:
{-# LANGUAGE TemplateHaskell #-}
module Simple where
import Test.Inspection
import Data.Maybe
lhs, rhs :: (a -> b) -> Maybe a -> Bool
lhs f x = isNothing (fmap f x)
rhs f Nothing = True
rhs f (Just _) = False
inspect $ 'lhs === 'rhs
If you compile this, you will reassurringly read:
$ ghc Simple.hs
[1 of 1] Compiling Simple ( Simple.hs, Simple.o )
examples/Simple.hs:14:1: lhs === rhs passed.
inspection testing successful
expected successes: 1
See the examples/
directory for more examples of working proofs.
If an assertion fails, for example
bad1, bad2 :: Int
bad1 = 2 + 2
bad2 = 5
inspect $ 'bad1 === 'bad2
then the compiler will tell you so, and abort the compilation:
$ ghc Simple.hs -dsuppress-idinfo
[5 of 5] Compiling Simple ( examples/Simple.hs, examples/Simple.o )
examples/Simple.hs:14:1: lhs === rhs passed.
examples/Simple.hs:20:1: bad1 === bad2 failed:
LHS:
bad1 :: Int
bad1 = I# 4#
RHS:
bad2 :: Int
bad2 = I# 5#
examples/Simple.hs: error:
inspection testing unsuccessful
expected successes: 1
unexpected failures: 1
What can I check for?
Currently, inspection-testing supports
- checking two definitions to be equal (useful in the context of generic programming)
- checking the absence of a certain type (useful in the context of list or stream fusion)
- checking the absence of a a use of certian functions
- checking the absence of allocation (generally useful)
- checking the absence of typeclass-overloaded code
In general, the checks need to be placed in the same module as the checked-definition.
Possible further applications includes
- checking that all recursive functions are (efficiently called) join-points
- asserting strictness properties (e.g. in
Data.Map.Strict
) - peforming some of these checks only within recursive loops
Let me know if you need any of these, or have further ideas.
Help, I am drowning in Core!
inspection-testing prints the Core more or less like GHC would, and the same flags can be used to control the level of detail. In particular, you might want to pass to GHC a selection of the following flags:
-dsuppress-idinfo -dsuppress-coercions -dsuppress-type-applications
-dsuppress-module-prefixes -dsuppress-type-signatures -dsuppress-uniques
It does not seem to do anything (on GHC < 8.4)
Add this line to your module:
{-# OPTIONS_GHC -O -fplugin Test.Inspection.Plugin #-}
Can I comment or help?
Sure! We can use the GitHub issue tracker for discussions, and obviously contributions are welcome.
Changes
Revision history for inspection-testing
0.5.0.3 – 2023-12-29
- Support GHC 9.10 (thanks Bodigrim)
0.5.0.2 – 2023-07-10
- Support GHC 9.8 (thanks Bodigrim)
0.5.0.1 – 2023-01-15
- Support mtl-2.3 and GHC 9.6 (thanks Bodigrim)
0.5 – 2022-06-15
- New equivalence
==~
that accepts different order of bindings in lets. (thanks @phadej) - When printing terms that differ, common up a common prefix of lambdas (thanks @phadej)
0.4.6.1 – 2022-05-20
- Support GHC 9.4 (thanks @parsonsmatt)
0.4.6.0 – 2020-08-23
- Support GHC 9.2 (thanks @Bodigrim)
0.4.5.0 – 2020-04-28
- Export some internals from
Test.Inspection.Plugin
, to make integration into testing frameworks easier (thanks @Bodigrim)
0.4.4.0 – 2020-04-21
- More GHC-9.0 compatibility (thanks @aadaa-fgtaa)
0.4.3.0 – 2020-01-26
- Ignores HPC ticks in
(==-)
(thanks @konn) - Add
(=/-)
operator (thanks @lysxia) - Add skip-O0 plugin option (thanks @AndrasKovacs)
- GHC-9.0 compatibility (thanks @konn)
- CI now runs on Github Actions (thanks @phadej)
0.4.2.4 – 2020-01-26
- Now prints the name of the type class on which a test fails, thanks to Harendra Kumar
- More examples, thanks to Rafe
0.4.2.3 – 2020-01-26
- Support GHC-8.10, thanks to Ryan Scott via head.hackage for the patch
0.4.2.1 – 2019-06-07
- Bugfix release
0.4.2 – 2019-06-05
- Be less picky if mutually recursive definitions appear in a different order in the source
- Add obligation
coreOf
, which succeeds, but lets you dump the core of a single symbol (thanks to @phadej) - Support
-fplugin-opt=Test.Inspection.Plugin:keep-going-O0
(thanks to @phadej)
0.4.1.2 – 2019-02-23
- Do not force recompilation with GHC >= 8.6
- Support
-fplugin-opt=Test.Inspection.Plugin:quiet
0.4.1.1 – 2018-11-17
- Fix a bug with
doesNotUse
and data constructors
0.4.1 – 2018-11-17
- New obligation
doesNotUse
- Use the Obligation’s testName in the plugin output.
- In
inspect
, do not overridesrcLoc
if already present.
0.4 – 2018-10-12
- Support GHC-8.6
- On GHC-8.4 or newer,
inspect
andinspectTest
will automatically load the plugin.
0.3 – 2018-07-07
- On GHC-8.5 or newer, use of
inspect
orinspectTest
without actually loading the plugin will cause compilation to fail at type-checking time (thanks to @adamgundry for the idea) - Support for
hasNoTypeClass
(thanks to @phadej) - Support for
hasNoGenerics
(thanks to @isovector) - No need to keep referenced variables alive using annotations: Simply mentioning them in a Template Haskell splice keeps them alive!
0.2.0.1 – 2018-02-02
- Support GHC HEAD (8.5)
0.2 – 2018-01-17
- With
$(inspectTest obligation)
you can now get the result of inspection testing at run-time, for integration into your test suite.
0.1.2 – 2017-11-20
- Make
(==-)
a bit more liberal, and look through variable redefinitions that only change the type
0.1.1.2 – 2017-11-12
- Hotfix: Do not abort if there are expected failures
0.1.1.1 – 2017-11-12
- Show summary stats
- Pull in less tests, to make inclusion in stackage easier
0.1.1 – 2017-11-09
- More complete output when
(===)
fails - Variant
(==-)
that ignores types when comparing terms
0.1 – 2017-11-09
- Repackaged as inspection-testing
0.1.1 – 2017-09-05
- Also run simplifier in stage 0
0.1 – 2017-08-26
- Initial release to hackage
0 – 2017-02-06
- Development of ghc-proofs commences