tasty-ant-xml-1.1.9/0000755000000000000000000000000007346545000012435 5ustar0000000000000000tasty-ant-xml-1.1.9/Changelog.md0000644000000000000000000000561007346545000014650 0ustar0000000000000000# 1.1.9 ## Other changes * Support `tasty-1.5`. Thanks to @sergv (PR #34). # 1.1.8 ## Other Changes * Support `tasty-1.4`. Due to major API changes, 1.4 is now the minimum version of `tasty` supported. Thanks to @jkachmar (PR #31). # 1.1.7 ## Other Changes * Bump upper bound of `tasty`. # 1.1.6 ## Functionality Changes * The order of tests is now reversed from what was previously output (which was itself reversed). This means that tests are now reported in the order they are defined, as expected. Thanks to @feuerbach for this fix. See https://github.com/ocharles/tasty-ant-xml/pull/28 for more information. # 1.1.5 ## Other Changes * Bump upper bound of `tasty`. # 1.1.4 ## Other Changes * Bump upper bound of `tasty`. # 1.1.3 ## Other Changes * Bump upper bound of `tasty`. # 1.1.2 ## Other Changes * Bump upper bound of `tasty`. # 1.1.1 ## Other Changes * Bump upper bound of `tasty`. # 1.1.0 ## Breaking Changes * The XML generated is now slightly different in order to satisfy Jenkins. In particular: * The `classname` attribute now joins the test path with `.` (like a Java class name). * `testsuite` nodes have a `tests` attribute, which is the amount of tests executed. For more discussion see https://github.com/ocharles/tasty-ant-xml/pull/20 and https://github.com/ocharles/tasty-ant-xml/commit/a01df06b59122c3086fc9f42854129b1d4a8c31c#commitcomment-21211275 Thanks to @liskin and @haishengwu-okta for this work. ## Other Changes * Increase the lower bound of `directory` to >= 1.2.3.0. Earlier versions may throw exceptions on some of the actions we are using. Thanks to @liskin for pointing this out. # 1.0.5 ## Other Changes * Reduce the lower bound of `filepath` to >= 1.0.0 * Reduce the lower bound of `directory` to >= 1.0.0 # 1.0.4 * We now use the `classname` attribute to indicate the test group a test came from. Thanks to @haishengwu-okta for this feature. # 1.0.3 * Now creates the directory containing the report XML file. Thanks to @haishengwu-okta for this feature. # 1.0.2 * Build with tasty < 0.12. # 1.0.1 * `tasty-ant-xml` now mentions timing information. Thanks @robdockins for this patch! # 1.0.0.11 * Remove dependency on `reducers`. Thanks to @jdnavarro # 1.0.0.9 * Build with tasty < 0.10 # 1.0.0.8 * Enable `-XFlexibleContexts`, which is required to build with GHC 7.9. # 1.0.0.7 * Relaxed lower bound on containers to build with GHC 7.4. # 1.0.0.6 * Updated to build with `tasty` 0.8. # 1.0.0.5 * Updated to build with `tasty` 0.7e. # 1.0.0.4 * Updated to build with `tasty` 0.5. # 1.0.0.3 * Change the .cabal file to include Changelog.md in source distribution. # 1.0.0.2 * This version contains only changes to documentation. # 1.0.0.1 * This version contains only changes to documentation. # 1.0.0 * Initial public release. Able to run a `TestTree` and produce XML that can be understood by Jenkins. tasty-ant-xml-1.1.9/LICENSE0000644000000000000000000000265407346545000013451 0ustar0000000000000000Copyright 2013 Oliver Charles All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 3. Neither the name of the author nor the names of his contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.tasty-ant-xml-1.1.9/Setup.hs0000644000000000000000000000005607346545000014072 0ustar0000000000000000import Distribution.Simple main = defaultMain tasty-ant-xml-1.1.9/Test/Tasty/Runners/0000755000000000000000000000000007346545000016114 5ustar0000000000000000tasty-ant-xml-1.1.9/Test/Tasty/Runners/AntXML.hs0000644000000000000000000001743507346545000017565 0ustar0000000000000000{-# LANGUAGE CPP #-} {-# LANGUAGE DeriveDataTypeable #-} {-# LANGUAGE DeriveGeneric #-} {-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE FlexibleInstances #-} {-# LANGUAGE NoMonomorphismRestriction #-} -- | Run a 'Tasty.TestTree' and produce an XML file summarising the test results -- in the same schema that would be produced by Apache Ant's JUnit test runner. -- This schema can be intepreted by the Jenkins continuous integration server, -- amongst other tools. module Test.Tasty.Runners.AntXML (antXMLRunner, AntXMLPath(..) ) where import Numeric (showFFloat) import Control.Applicative import Control.Arrow (first) import Control.Monad.IO.Class (liftIO) import Data.Foldable (fold) import Data.List (intercalate) import Data.Maybe (fromMaybe) import Data.Monoid (Monoid(..), Endo(..), Sum(..)) import Data.Proxy (Proxy(..)) import Data.Tagged (Tagged(..)) import Data.Typeable (Typeable) import GHC.Generics (Generic) import Generics.Deriving.Monoid (memptydefault, mappenddefault) import System.Directory (createDirectoryIfMissing, canonicalizePath) import System.FilePath (takeDirectory) import qualified Control.Concurrent.STM as STM import qualified Control.Monad.State as State import qualified Control.Monad.Reader as Reader import qualified Data.Functor.Compose as Functor import qualified Data.IntMap as IntMap import qualified Test.Tasty as Tasty import qualified Test.Tasty.Providers as Tasty import qualified Test.Tasty.Options as Tasty import qualified Test.Tasty.Runners as Tasty import qualified Text.XML.Light as XML -------------------------------------------------------------------------------- newtype AntXMLPath = AntXMLPath FilePath deriving (Typeable) instance Tasty.IsOption (Maybe AntXMLPath) where defaultValue = Nothing parseValue = Just . Just . AntXMLPath optionName = Tagged "xml" optionHelp = Tagged "A file path to store the test results in Ant-compatible XML" -------------------------------------------------------------------------------- data Summary = Summary { summaryFailures :: Sum Int , summaryErrors :: Sum Int , summarySuccesses :: Sum Int , xmlRenderer :: Endo [XML.Element] } deriving (Generic) instance Monoid Summary where mempty = memptydefault #if !MIN_VERSION_base(4,11,0) mappend = mappenddefault #else instance Semigroup Summary where (<>) = mappenddefault #endif -------------------------------------------------------------------------------- {-| To run tests using this ingredient, use 'Tasty.defaultMainWithIngredients', passing 'antXMLRunner' as one possible ingredient. This ingredient will run tests if you pass the @--xml@ command line option. For example, @--xml=junit.xml@ will run all the tests and generate @junit.xml@ as output. -} antXMLRunner :: Tasty.Ingredient antXMLRunner = Tasty.TestReporter optionDescription runner where optionDescription = [ Tasty.Option (Proxy :: Proxy (Maybe AntXMLPath)) ] runner options testTree = do AntXMLPath path <- Tasty.lookupOption options return $ \statusMap -> let timeDigits = 3 showTime time = showFFloat (Just timeDigits) time "" runTest :: (Tasty.IsTest t) => Tasty.OptionSet -> Tasty.TestName -> t -> Tasty.Traversal (Functor.Compose (Reader.ReaderT [String] (State.StateT IntMap.Key IO)) (Const Summary)) runTest _ testName _ = Tasty.Traversal $ Functor.Compose $ do i <- State.get groupNames <- Reader.ask summary <- liftIO $ STM.atomically $ do status <- STM.readTVar $ fromMaybe (error "Attempted to lookup test by index outside bounds") $ IntMap.lookup i statusMap let testCaseAttributes time = map (uncurry XML.Attr . first XML.unqual) [ ("name", testName) , ("time", showTime time) , ("classname", intercalate "." (reverse groupNames)) ] mkSummary contents = mempty { xmlRenderer = Endo (XML.node (XML.unqual "testcase") contents :) } mkSuccess time = (mkSummary (testCaseAttributes time)) { summarySuccesses = Sum 1 } mkFailure time reason = mkSummary ( testCaseAttributes time , XML.node (XML.unqual "failure") reason ) case status of -- If the test is done, generate XML for it Tasty.Done result | Tasty.resultSuccessful result -> pure (mkSuccess (Tasty.resultTime result)) | otherwise -> case resultException result of Just e -> pure $ (mkFailure (Tasty.resultTime result) (show e)) { summaryErrors = Sum 1 } Nothing -> pure $ if resultTimedOut result then (mkFailure (Tasty.resultTime result) "TimeOut") { summaryErrors = Sum 1 } else (mkFailure (Tasty.resultTime result) (Tasty.resultDescription result)) { summaryFailures = Sum 1 } -- Otherwise the test has either not been started or is currently -- executing _ -> STM.retry Const summary <$ State.modify (+ 1) runGroup :: Reader.MonadReader [String] f => String -> Tasty.Traversal (Functor.Compose f (Const Summary)) -> Tasty.Traversal (Functor.Compose f (Const Summary)) runGroup groupName children = Tasty.Traversal $ Functor.Compose $ do Const soFar <- Reader.local (groupName :) $ Functor.getCompose $ Tasty.getTraversal children let grouped = XML.node (XML.unqual "testsuite") ([ XML.Attr (XML.unqual "name") groupName , XML.Attr (XML.unqual "tests") (show . getSum . (summaryFailures `mappend` summaryErrors `mappend` summarySuccesses) $ soFar) ] , appEndo (xmlRenderer soFar) [] ) pure $ Const soFar { xmlRenderer = Endo (grouped :) } runGroup' _options groupName = #if MIN_VERSION_tasty(1, 5, 0) runGroup groupName . fold #else runGroup groupName #endif in do (Const summary, tests) <- flip State.runStateT 0 $ flip Reader.runReaderT [] $ Functor.getCompose $ Tasty.getTraversal $ Tasty.foldTestTree Tasty.trivialFold { Tasty.foldSingle = runTest, Tasty.foldGroup = runGroup' } options testTree return $ \elapsedTime -> do createPathDirIfMissing path writeFile path $ XML.showTopElement $ XML.node (XML.unqual "testsuites") ([ XML.Attr (XML.unqual "errors") (show . getSum . summaryErrors $ summary) , XML.Attr (XML.unqual "failures") (show . getSum . summaryFailures $ summary) , XML.Attr (XML.unqual "tests") (show tests) , XML.Attr (XML.unqual "time") (showTime elapsedTime) ] , appEndo (xmlRenderer summary) []) return (getSum ((summaryFailures `mappend` summaryErrors) summary) == 0) resultException r = case Tasty.resultOutcome r of Tasty.Failure (Tasty.TestThrewException e) -> Just e _ -> Nothing resultTimedOut r = case Tasty.resultOutcome r of Tasty.Failure (Tasty.TestTimedOut _) -> True _ -> False createPathDirIfMissing path = fmap takeDirectory (canonicalizePath path) >>= createDirectoryIfMissing True tasty-ant-xml-1.1.9/tasty-ant-xml.cabal0000644000000000000000000000162407346545000016146 0ustar0000000000000000name: tasty-ant-xml version: 1.1.9 synopsis: Render tasty output to XML for Jenkins description: A tasty ingredient to output test results in XML, using the Ant schema. This XML can be consumed by the Jenkins continuous integration framework. homepage: http://github.com/ocharles/tasty-ant-xml license: BSD3 license-file: LICENSE author: Oliver Charles maintainer: ollie@ocharles.org.uk copyright: Oliver Charles 2013 category: Testing build-type: Simple cabal-version: >=1.10 extra-source-files: Changelog.md library exposed-modules: Test.Tasty.Runners.AntXML build-depends: base >= 4.5 && < 5, ghc-prim, containers >= 0.4.2.0, generic-deriving >= 1.6.2, mtl >= 2.1.2, stm >= 2.4.2, tagged >= 0.7, tasty >= 1.4 && < 1.6, transformers >= 0.3.0.0, directory >= 1.2.3.0, filepath >= 1.0.0, xml >= 1.3.13 default-language: Haskell98 ghc-options: -Wall -O2