cabal-doctest: A Setup.hs helper for running doctests

This is a package candidate release! Here you can preview how this package release will appear once published to the main package index (which can be accomplished via the 'maintain' link below). Please note that once a package has been published to the main package index it cannot be undone! Please consult the package uploading documentation for more information.

[maintain] [Publish]

As of now (end of 2024), there isn't cabal doctest command. Yet, to properly work, doctest needs plenty of configuration. This library provides the common bits for writing a custom Setup.hs.


[Skip to Readme]

Properties

Versions 1, 1.0.1, 1.0.2, 1.0.3, 1.0.4, 1.0.5, 1.0.6, 1.0.7, 1.0.8, 1.0.9, 1.0.10, 1.0.11, 1.0.11
Change log changelog.md
Dependencies base (>=4.9 && <5), Cabal (>=1.24 && <3.16), directory (>=1.3 && <2), filepath (>=1.4 && <2) [details]
License BSD-3-Clause
Copyright (c) 2017-2020 Oleg Grenrus, 2020- package maintainers
Author Oleg Grenrus <oleg.grenrus@iki.fi>
Maintainer Max Ulidtko <ulidtko@gmail.com>
Category Distribution
Home page https://github.com/ulidtko/cabal-doctest
Source repo head: git clone https://github.com/ulidtko/cabal-doctest
Uploaded by ulidtko at 2024-11-22T14:55:44Z

Modules

[Index] [Quick Jump]

Downloads

Maintainer's Corner

Package maintainers

For package maintainers and hackage trustees


Readme for cabal-doctest-1.0.11

[back to package description]

cabal-doctest

Hackage Haskell-CI stack test

A Setup.hs helper for running doctests.

Why this exists

Doctesting is a nifty technique that stimulates 3 good things to happen:

That's what the doctest tool does — not this package! — just for clarity. Off the shelf, doctest doesn't require any package management mumbo-jumbo: you just run it on a source file with haddocks with doctests.

Issues come in when library authors and maintainers wish to integrate doctests into CI pipelines. When doctests start to require dependencies or non-default compiler flags: that's when it gets hairy. There, if you want stack test and/or cabal test to run doctests too with minimal shenanigans, then read on.

Among different available approaches, this package cabal-doctest helps with one, which is known as custom setup, build-type: Custom more precisely. You should stick to the default build-type: Simple, unless you know what you're doing.

In a nutshell, this custom Setup.hs shim generates a module Build_doctests that allows your doctest driver test-suite to look like this:

module Main where

import Build_doctests (flags, pkgs, module_sources)
import Test.Doctest (doctest)

main :: IO ()
main = doctest (flags ++ pkgs ++ module_sources)

More detailed examples below.

Regardless of the name, this also works with Stack.

For old versions of stack, cabal-install, GHC, see caveats below.

Simple example

Follow simple example for the common case of a single-library .cabal package with doctests.

To recap the example's code:

  1. specify build-type: Custom in your .cabal file;

  2. declare dependencies of Setup.hs:

    custom-setup
     setup-depends:
       base >= 4 && <5,
       cabal-doctest >= 1 && <1.1
    

    See Notes below for a caveat with cabal-install < 2.4.

  3. Populate Setup.hs like so:

    module Main where
    
    import Distribution.Extra.Doctest (defaultMainWithDoctests)
    
    main :: IO ()
    main = defaultMainWithDoctests "doctests"
    

    Assuming your test-suite is called doctests, this Setup will generate a Build_doctests module during package build. If your test-suite goes by name foo, defaultMainWithDoctests "foo" creates a Build_foo module.

  4. Use the generated module in a testsuite, simply like so:

    module Main where
    
    import Build_doctests (flags, pkgs, module_sources)
    import Data.Foldable (traverse_)
    import System.Environment (unsetEnv)
    import Test.DocTest (doctest)
    
    main :: IO ()
    main = do
        traverse_ putStrLn args -- optionally print arguments
        unsetEnv "GHC_ENVIRONMENT" -- see 'Notes'; you may not need this
        doctest args
      where
        args = flags ++ pkgs ++ module_sources
    

Ultimately, cabal test or stack test should run the doctests of your package.

Example with multiple cabal components

cabal-doctest also supports more exotic use cases where a .cabal file contains more components with doctests than just the main library, including:

Unlike the simple example shown above, these examples involve named components. You don't need to change the Setup.hs script to support this use case. However, in this scenario Build_doctests will generate extra copies of the flags, pkgs, and module_sources values for each additional named component.

The simplest approach is to use x-doctest-components field in .cabal:

x-doctest-components: lib lib:internal exe:example

In that case, the test driver is generally:

module Main where

import Build_doctests (Component (..), components)
import Data.Foldable (for_)
import System.Environment (unsetEnv)
import Test.DocTest (doctest)

main :: IO ()
main = for_ components $ \(Component name flags pkgs sources) -> do
    print name
    putStrLn "----------------------------------------"
    let args = flags ++ pkgs ++ sources
    for_ args putStrLn
    unsetEnv "GHC_ENVIRONMENT"
    doctest args

There is also a more explicit approach: if you have an executable named foo, then Build_doctest will contain flags_exe_foo, pkgs_exe_foo, and module_sources_exe_foo. If the name has hyphens in it (e.g., my-exe), cabal-doctest will convert them to underscores (e.g., you'd get flags_my_exe, pkgs_my_exe, module_sources_my_exe). Internal library bar values will have a _lib_bar suffix.

An example testsuite driver for this use case might look like this:

module Main where

import Build_doctests
       (flags,            pkgs,            module_sources,
        flags_exe_my_exe, pkgs_exe_my_exe, module_sources_exe_my_exe)
import Data.Foldable (traverse_)
import System.Environment (unsetEnv)
import Test.DocTest

main :: IO ()
main = do
    unsetEnv "GHC_ENVRIONMENT"
    -- doctests for library
    traverse_ putStrLn libArgs
    doctest libArgs

    -- doctests for executable
    traverse_ putStrLn exeArgs
    doctest exeArgs
  where
    libArgs = flags            ++ pkgs            ++ module_sources
    exeArgs = flags_exe_my_exe ++ pkgs_exe_my_exe ++ module_sources_exe_my_exe

See the multiple-components-example.

Additional configuration

The cabal-doctest based Setup.hs supports a few extensions fields in pkg.cabal files to customize the doctest runner behavior, without customizing the default doctest.hs.

test-suite doctests:
  if impl(ghc >= 8.0)
    x-doctest-options: -fdiagnostics-color=never
  x-doctest-source-dirs: test
  x-doctest-modules: Servant.Utils.LinksSpec

Notes

Copyright 2017 Oleg Grenrus.

With contributions from:

Available under the BSD 3-clause license.