tasty-coverage 0.1.1.0 → 0.1.2.0
raw patch · 4 files changed
+129/−49 lines, 4 filesdep ~tastyPVP ok
version bump matches the API change (PVP)
Dependency ranges changed: tasty
API changes (from Hackage documentation)
+ Test.Tasty.CoverageReporter: instance Test.Tasty.Options.IsOption Test.Tasty.CoverageReporter.TixDir
Files
- CHANGELOG.md +5/−0
- CONTRIBUTING.md +33/−0
- src/Test/Tasty/CoverageReporter.hs +82/−45
- tasty-coverage.cabal +9/−4
CHANGELOG.md view
@@ -1,5 +1,10 @@ # Revision history for tasty-test +## 0.1.2.0 -- 2023-09-10++* Add compatibility with Tasty 1.5+* Add `--tix-dir` flag for specifying directory for tix files.+ ## 0.1.1.0 -- 2023-09-07 * Be more specific with the file suffixes which are computed from the test outcome. Previously only "PASSED" and "FAILED" were used. We now also use "EXCEPTION", "TIMEOUT" and "SKIPPED".
+ CONTRIBUTING.md view
@@ -0,0 +1,33 @@+Thanks for contributing to `tasty-coverage`. Issues, feature requests and pull requests are always welcome :)++# Code style++The code must be formatted using the `fourmolu` code formatter. See install instructions on their [website](https://github.com/fourmolu/fourmolu).+The following command should work:++```console+> fourmolu --mode inplace $(git ls-files '*.hs')+```++# Keeping a changelog++Any nontrivial change must be recorded in the CHANGELOG.md before the pull-request is merged.+We follow the [keepachangelog.com](https://keepachangelog.com/en/1.1.0/) standard.+Before a new version is released on Hackage, the changes are collected in the `Unreleased` section of the changelog.++# Releasing to Hackage++There is one difficulty which prevents us from releasing the package "as-is" to Hackage.+The testsuite requires the `tasty-coverage-example` to be compiled with the `-fhpc` option, since we test that during execution+the correct .tix files are generated. But hard-coding the `-fhpc` flag in the cabal file is an error according to `cabal check`+and Hackage won't let us upload the package. Therefore, the `create-sdist.sh` script removes the testsuite section from the cabal+file before `cabal sdist` is invoked.++# Release checklist++- Bump the version in the cabal file.+- Create a [git tag](https://git-scm.com/book/en/v2/Git-Basics-Tagging) for the new version and push it.+ Use `git tag -a x.x.x.x -m "Version x.x.x.x"` and then `git push origin x.x.x.x`+- Replace the `Unreleased` part of the `CHANGELOG.md` with the current date and the new version.+- Create a candidate on Hackage and check that everything is correct before releasing.+
src/Test/Tasty/CoverageReporter.hs view
@@ -1,30 +1,43 @@-{-# LANGUAGE InstanceSigs, NamedFieldPuns #-}+{-# LANGUAGE CPP #-}+{-# LANGUAGE InstanceSigs #-}+{-# LANGUAGE NamedFieldPuns #-}++-- |+-- Module : Test.Tasty.CoverageReporter+-- Description : Ingredient for producing per-test coverage reports+--+-- This module provides an ingredient for the tasty framework which allows+-- to generate one coverage file per individual test. module Test.Tasty.CoverageReporter (coverageReporter) where +import Control.Monad (forM_)+import Data.Bifunctor (first)+import Data.Foldable (fold)+import Data.List.NonEmpty (NonEmpty)+import qualified Data.List.NonEmpty as NE+import Data.Typeable+import System.FilePath ((<.>), (</>)) import Test.Tasty import Test.Tasty.Ingredients import Test.Tasty.Options-import Test.Tasty.Runners import Test.Tasty.Providers-import Data.Typeable-import Trace.Hpc.Reflect ( clearTix, examineTix )-import Trace.Hpc.Tix ( writeTix, Tix(..), TixModule(..) )-import System.FilePath ( (<.>), (</>) )-import Control.Monad (forM_)-import qualified Data.List.NonEmpty as NE-import Data.List.NonEmpty ( NonEmpty )-import Data.Foldable (fold)-import Data.Bifunctor (first)+import Test.Tasty.Runners+import Trace.Hpc.Reflect (clearTix, examineTix)+import Trace.Hpc.Tix (Tix (..), TixModule (..), writeTix) +-------------------------------------------------------------------------------+-- Options+-------------------------------------------------------------------------------+ newtype ReportCoverage = MkReportCoverage Bool deriving (Eq, Ord, Typeable) instance IsOption ReportCoverage where- defaultValue = MkReportCoverage False- parseValue = fmap MkReportCoverage . safeReadBool- optionName = pure "report-coverage"- optionHelp = pure "Generate per-test coverage data"- optionCLParser = mkFlagCLParser mempty (MkReportCoverage True)+ defaultValue = MkReportCoverage False+ parseValue = fmap MkReportCoverage . safeReadBool+ optionName = pure "report-coverage"+ optionHelp = pure "Generate per-test coverage data"+ optionCLParser = mkFlagCLParser mempty (MkReportCoverage True) newtype RemoveTixHash = MkRemoveTixHash Bool deriving (Eq, Ord, Typeable)@@ -36,41 +49,61 @@ optionHelp = pure "Remove hash from tix file (used for golden tests)" optionCLParser = mkFlagCLParser mempty (MkRemoveTixHash True) -coverageOptions :: [OptionDescription]-coverageOptions = [ Option (Proxy :: Proxy ReportCoverage)- , Option (Proxy :: Proxy RemoveTixHash)- ]+newtype TixDir = MkTixDir FilePath +instance IsOption TixDir where+ defaultValue = MkTixDir "tix"+ parseValue str = Just (MkTixDir str)+ optionName = pure "tix-dir"+ optionHelp = pure "Specify directory for generated tix files"+ showDefaultValue (MkTixDir dir) = Just dir -tixDir :: FilePath-tixDir = "tix"+coverageOptions :: [OptionDescription]+coverageOptions =+ [ Option (Proxy :: Proxy ReportCoverage),+ Option (Proxy :: Proxy RemoveTixHash),+ Option (Proxy :: Proxy TixDir)+ ] +-------------------------------------------------------------------------------+-- coverageReporter Ingredient+-------------------------------------------------------------------------------+ -- | Obtain the list of all tests in the suite testNames :: OptionSet -> TestTree -> IO ()-testNames os tree = forM_ (foldTestTree coverageFold os tree) $ \(s,f) -> f (fold (NE.intersperse "." s))+testNames os tree = forM_ (foldTestTree coverageFold os tree) $ \(s, f) -> f (fold (NE.intersperse "." s)) +type FoldResult = [(NonEmpty TestName, String -> IO ())] +#if MIN_VERSION_tasty(1,5,0)+groupFold :: OptionSet -> TestName -> [FoldResult] -> FoldResult+groupFold _ groupName acc = fmap (first (NE.cons groupName)) (concat acc)+#else+groupFold :: OptionSet -> TestName -> FoldResult -> FoldResult+groupFold _ groupName acc = fmap (first (NE.cons groupName)) acc+#endif -- | Collect all tests and-coverageFold :: TreeFold [(NonEmpty TestName, String -> IO ())]-coverageFold = trivialFold- { foldSingle = \opts name test -> do- let f n = do- -- Collect the coverage data for exactly this test.- clearTix- result <- run opts test (\_ -> pure ())- tix <- examineTix- let filepath = tixFilePath n result- writeTix filepath (removeHash opts tix)- putStrLn ("Wrote coverage file: " <> filepath)- pure (NE.singleton name, f),- -- Append the name of the testgroup to the list of TestNames- foldGroup = \_ groupName acc -> fmap (first (NE.cons groupName)) acc- }+coverageFold :: TreeFold FoldResult+coverageFold =+ trivialFold+ { foldSingle = \opts name test -> do+ let f n = do+ -- Collect the coverage data for exactly this test.+ clearTix+ result <- run opts test (\_ -> pure ())+ tix <- examineTix+ let filepath = tixFilePath opts n result+ writeTix filepath (removeHash opts tix)+ putStrLn ("Wrote coverage file: " <> filepath)+ pure (NE.singleton name, f),+ -- Append the name of the testgroup to the list of TestNames+ foldGroup = groupFold+ } -tixFilePath :: TestName -> Result -> FilePath-tixFilePath tn Result { resultOutcome } =- tixDir </> generateValidFilepath tn <.> outcomeSuffix resultOutcome <.> ".tix"+tixFilePath :: OptionSet -> TestName -> Result -> FilePath+tixFilePath opts tn Result {resultOutcome} = case lookupOption opts of+ MkTixDir tixDir -> tixDir </> generateValidFilepath tn <.> outcomeSuffix resultOutcome <.> ".tix" -- | We want to compute the file suffix that we use to distinguish -- tix files for failing and succeeding tests.@@ -81,10 +114,15 @@ outcomeSuffix (Failure (TestTimedOut _)) = "TIMEOUT" outcomeSuffix (Failure TestDepFailed) = "SKIPPED" +-- | This ingredient implements its own test-runner which can be executed with+-- the @--report-coverage@ command line option.+-- The testrunner executes the tests sequentially and emits one coverage file+-- per executed test.+--+-- @since 0.1.0.0 coverageReporter :: Ingredient coverageReporter = TestManager coverageOptions coverageRunner - coverageRunner :: OptionSet -> TestTree -> Maybe (IO Bool) coverageRunner opts tree = case lookupOption opts of MkReportCoverage False -> Nothing@@ -101,8 +139,8 @@ where -- Include both Windows and Posix, so that generated .tix files -- are consistent among systems.- pathSeparators = ['\\','/']- + pathSeparators = ['\\', '/']+ removeHash :: OptionSet -> Tix -> Tix removeHash opts (Tix txs) = case lookupOption opts of MkRemoveTixHash False -> Tix txs@@ -110,4 +148,3 @@ removeHashModule :: TixModule -> TixModule removeHashModule (TixModule name _hash i is) = TixModule name 0 i is-
tasty-coverage.cabal view
@@ -1,6 +1,6 @@ cabal-version: 3.0 name: tasty-coverage-version: 0.1.1.0+version: 0.1.2.0 license: BSD-3-Clause license-file: LICENSE copyright: David Binder, 2023@@ -20,21 +20,26 @@ build-type: Simple extra-doc-files: CHANGELOG.md+ CONTRIBUTING.md README.md source-repository head type: git location: git@github.com:BinderDavid/tasty-coverage.git +common deps+ build-depends:+ base >=4.16 && <4.20,+ tasty >= 1.4 && < 1.6+ library+ import: deps exposed-modules: Test.Tasty.CoverageReporter hs-source-dirs: src default-language: Haskell2010 ghc-options: -Wall build-depends:- base >=4.16 && <4.20, filepath >=1.3.8 && <=1.5,- tasty ^>=1.4, hpc >=0.6 && <0.8,- +