tasty 1.2.3 → 1.3
raw patch · 12 files changed
+201/−80 lines, 12 filesdep +semigroupsdep −ghc-primdep ~base
Dependencies added: semigroups
Dependencies removed: ghc-prim
Dependency ranges changed: base
Files
- CHANGELOG.md +17/−0
- README.md +16/−5
- Test/Tasty/CmdLine.hs +114/−14
- Test/Tasty/Ingredients/Basic.hs +1/−0
- Test/Tasty/Ingredients/ConsoleReporter.hs +21/−16
- Test/Tasty/Options.hs +16/−8
- Test/Tasty/Options/Core.hs +1/−0
- Test/Tasty/Patterns/Eval.hs +1/−1
- Test/Tasty/Patterns/Parser.hs +0/−10
- Test/Tasty/Runners/Reducers.hs +1/−6
- Test/Tasty/Runners/Utils.hs +7/−12
- tasty.cabal +6/−8
CHANGELOG.md view
@@ -1,6 +1,23 @@ Changes ======= +Version 1.3+-----------++* `IsOption` has a new method `showDefaultValue` for customizing how+ `defaultValue`s are rendered in the `--help` output.+* Drop support for GHCs older than 5 years+* Do not install handlers for the signals that dump core+* Export the `AnsiTricks` type/option+* In addition to a `Parser`, `optionParser` and `suiteOptionParser` now return+ a `[String]` representing warning messages:+ * A warning is emitted if an `IsOption` instance defines multiple options in+ the implementation of `optionCLParser`.+ * An warning is emitted if an `IsOption` instance's `optionCLParser`+ implementation assigns a default value (e.g., with+ `Options.Applicative.value`), as this interferes with `tasty`'s ability to+ read environment variable arguments.+ Version 1.2.3 -------------
README.md view
@@ -181,10 +181,11 @@ Usage: test [-p|--pattern PATTERN] [-t|--timeout DURATION] [-l|--list-tests] [-j|--num-threads NUMBER] [-q|--quiet] [--hide-successes]- [--color never|always|auto] [--quickcheck-tests NUMBER]+ [--color never|always|auto] [--ansi-tricks ARG]+ [--smallcheck-depth NUMBER] [--quickcheck-tests NUMBER] [--quickcheck-replay SEED] [--quickcheck-show-replay] [--quickcheck-max-size NUMBER] [--quickcheck-max-ratio NUMBER]- [--quickcheck-verbose] [--smallcheck-depth NUMBER]+ [--quickcheck-verbose] [--quickcheck-shrinks NUMBER] Available options: -h,--help Show this help text@@ -194,11 +195,16 @@ default: s) -l,--list-tests Do not run the tests; just print their names -j,--num-threads NUMBER Number of threads to use for tests execution+ (default: # of cores/capabilities) -q,--quiet Do not produce any output; indicate success only by the exit code --hide-successes Do not print tests that passed successfully --color never|always|auto When to use colored output (default: 'auto')+ --ansi-tricks ARG Enable various ANSI terminal tricks. Can be set to+ 'true' (default) or 'false'.+ --smallcheck-depth NUMBER+ Depth to use for smallcheck tests --quickcheck-tests NUMBER Number of test cases for QuickCheck to generate --quickcheck-replay SEED Random seed to use for replaying a previous test run@@ -210,8 +216,9 @@ Maximum number of discared tests per successful test before giving up --quickcheck-verbose Show the generated test cases- --smallcheck-depth NUMBER- Depth to use for smallcheck tests+ --quickcheck-shrinks NUMBER+ Number of shrinks allowed before QuickCheck will fail+ a test ``` Every option can be passed via environment. To obtain the environment variable@@ -728,7 +735,7 @@ Here are some caveats to keep in mind regarding dependencies in Tasty: -1. If Test B depends on Test A, remember that either of the may be filtered out+1. If Test B depends on Test A, remember that either of them may be filtered out using the `--pattern` option. Collecting the dependency info happens *after* filtering. Therefore, if Test A is filtered out, Test B will run unconditionally, and if Test B is filtered out, it simply won't run.@@ -800,6 +807,10 @@ [custom-options-article]: https://ro-che.info/articles/2013-12-20-tasty-custom-options.html [awk-patterns-article]: https://ro-che.info/articles/2018-01-08-tasty-new-patterns [tasty-directories]: https://nmattia.com/posts/2018-04-30-tasty-test-names.html++## GHC version support policy++We only support the GHC/base versions [from the last 5 years](https://wiki.haskell.org/Base_package#Versions). Maintainers -----------
Test/Tasty/CmdLine.hs view
@@ -7,16 +7,21 @@ , defaultMainWithIngredients ) where -import Options.Applicative-import Data.Monoid ((<>))+import Control.Arrow+import Control.Monad+import Data.Maybe import Data.Proxy-import Data.Foldable (foldMap)+import Data.Typeable (typeRep)+import Options.Applicative+import Options.Applicative.Common (evalParser)+import qualified Options.Applicative.Types as Applicative (Option(..))+import Options.Applicative.Types (Parser(..), OptProperties(..)) import Prelude -- Silence AMP and FTP import warnings import System.Exit import System.IO--#if !MIN_VERSION_base(4,9,0)+#if !MIN_VERSION_base(4,11,0) import Data.Monoid+import Data.Foldable (foldMap) #endif import Test.Tasty.Core@@ -26,15 +31,108 @@ import Test.Tasty.Options.Env import Test.Tasty.Runners.Reducers --- | Generate a command line parser from a list of option descriptions-optionParser :: [OptionDescription] -> Parser OptionSet-optionParser = getApp . foldMap toSet where- toSet :: OptionDescription -> Ap Parser OptionSet- toSet (Option (Proxy :: Proxy v)) = Ap $- (singleOption <$> (optionCLParser :: Parser v)) <|> pure mempty+-- | Generate a command line parser from a list of option descriptions,+-- alongside any related warning messages.+optionParser :: [OptionDescription] -> ([String], Parser OptionSet)+optionParser = second getApp . foldMap toSet where+ toSet :: OptionDescription -> ([String], Ap Parser OptionSet)+ toSet (Option p) = second+ (\parser -> Ap $ (singleOption <$> parser) <|> pure mempty)+ (finalizeCLParser p optionCLParser) --- | The command line parser for the test suite-suiteOptionParser :: [Ingredient] -> TestTree -> Parser OptionSet+-- Do two things:+--+-- 1. Replace an `optionCLParser`'s 'propShowDefault' with 'showDefaultValue'+-- from the 'IsOption' class.+-- 2. Generate warning messages if the 'optionCLParser' does anything+-- suspicious. Currently, the only suspicious things we check for are+-- (a) if the 'Parser' defines multiple options and, (b) if the 'Parser'+-- assigns a default value outside of 'defaultValue'.+finalizeCLParser :: forall proxy v . IsOption v+ => proxy v -> Parser v -> ([String], Parser v)+finalizeCLParser _ p = (warnings, setCLParserShowDefaultValue mbDef p)+ where+ mbDef :: Maybe String+ mbDef = showDefaultValue (defaultValue :: v)++ warnings :: [String]+ warnings = catMaybes [multipleOptPsWarning, badDefaultWarning]++ -- Warn if a Parser defines multiple options, as this breaks an assumption+ -- that setCLParserShowDefaultValue relies on.+ multipleOptPsWarning :: Maybe String+ multipleOptPsWarning+ | numOptPs p > 1+ = Just $ unlines+ [ prov+ , "optionCLParser defines multiple options. Consider only defining"+ , "a single option here, as defining multiple options does not play"+ , "well with how tasty displays default values."+ ]+ | otherwise+ = Nothing++ -- Warning if a Parser has a default value (outside of IsOption's+ -- defaultValue method, that is), as this interferes with tasty's ability+ -- to read arguments from environment variables. For more on this point,+ -- see the Haddocks for optionCLParser.+ badDefaultWarning :: Maybe String+ badDefaultWarning+ -- evalParser will only return Just if has a default value declared with+ -- e.g. the Options.Applicative.value function.+ | isJust (evalParser p)+ = Just $ unlines+ [ prov+ , "Using default values (e.g., with Options.Applicative.value) in"+ , "optionCLParser is prohibited, as it interferes with tasty's ability"+ , "to read environment variable options properly. Moreover, assigning"+ , "default values is unnecessary, as their functionality is subsumed"+ , "by the defaultValue method of IsOption."+ ]+ | otherwise+ = Nothing++ prov :: String+ prov = "WARNING (in the IsOption instance for "+ ++ show (typeRep (Proxy :: Proxy v)) ++ "):"++-- Replace an `optionCLParser`'s 'propShowDefault' with 'showDefaultValue' from+-- the 'IsOption' class. It's tempting to try doing this when constructing the+-- 'Parser' itself using 'optionMod', but @optparse-applicative@'s 'mkParser'+-- function always overrides the result of 'optionMod'. Ugh.+setCLParserShowDefaultValue :: Maybe String -> Parser a -> Parser a+setCLParserShowDefaultValue mbDef = go+ where+ go :: Parser a -> Parser a+ -- Note that we /always/ replace the Option's optProps, regardless of+ -- what type it may have. This can produce unexpected results if an+ -- optionCLParser defines multiple options, which is why we emit a warning+ -- (in finalizeCLParser) if a Parser does this.+ go (OptP o) = OptP o{Applicative.optProps =+ modifyDefault (Applicative.optProps o)}+ go p@NilP{} = p+ go (MultP p1 p2) = MultP (go p1) (go p2)+ go (AltP p1 p2) = AltP (go p1) (go p2)+ go (BindP p1 p2) = BindP (go p1) (fmap go p2)++ modifyDefault :: OptProperties -> OptProperties+ modifyDefault op = op{propShowDefault = mbDef}++-- Note: this is a conservative estimate, since we cannot count the number+-- of OptPs in the continuation argument of BindP. But BindP is really only+-- used for ParserM purposes, and since ParserM is an internal+-- optparse-applicative definition, most optionCLParser instances are+-- unlikely to use it in practice.+numOptPs :: Parser a -> Int+numOptPs OptP{} = 1+numOptPs NilP{} = 0+numOptPs (MultP p1 p2) = numOptPs p1 + numOptPs p2+numOptPs (AltP p1 p2) = numOptPs p1 + numOptPs p2+numOptPs (BindP p1 _p2) = numOptPs p1++-- | The command line parser for the test suite, alongside any related+-- warnings.+suiteOptionParser :: [Ingredient] -> TestTree -> ([String], Parser OptionSet) suiteOptionParser ins tree = optionParser $ suiteOptions ins tree -- | Parse the command-line and environment options passed to tasty.@@ -48,8 +146,10 @@ -- pass 'defaultIngredients'. parseOptions :: [Ingredient] -> TestTree -> IO OptionSet parseOptions ins tree = do+ let (warnings, parser) = suiteOptionParser ins tree+ mapM_ (hPutStrLn stderr) warnings cmdlineOpts <- execParser $- info (helper <*> suiteOptionParser ins tree)+ info (helper <*> parser) ( fullDesc <> header "Mmm... tasty test suite" )
Test/Tasty/Ingredients/Basic.hs view
@@ -9,6 +9,7 @@ consoleTestReporter , Quiet(..) , HideSuccesses(..)+ , AnsiTricks(..) -- ** Listing tests , listingTests , ListTests(..)
Test/Tasty/Ingredients/ConsoleReporter.hs view
@@ -1,4 +1,4 @@--- vim:fdm=marker:foldtext=foldtext()+-- vim:fdm=marker {-# LANGUAGE BangPatterns, ImplicitParams, MultiParamTypeClasses, DeriveDataTypeable, FlexibleContexts #-} -- | Console reporter ingredient module Test.Tasty.Ingredients.ConsoleReporter@@ -48,15 +48,11 @@ import Options.Applicative hiding (str, Success, Failure) import System.IO import System.Console.ANSI-#if !MIN_VERSION_base(4,8,0)-import Data.Proxy-import Data.Foldable hiding (concatMap,elem,sequence_)-#endif-#if MIN_VERSION_base(4,9,0)+#if !MIN_VERSION_base(4,11,0) import Data.Semigroup (Semigroup) import qualified Data.Semigroup (Semigroup((<>)))-#else import Data.Monoid+import Data.Foldable (foldMap) #endif --------------------------------------------------@@ -86,10 +82,8 @@ instance Monoid TestOutput where mempty = Skip mappend = Seq-#if MIN_VERSION_base(4,9,0) instance Semigroup TestOutput where (<>) = mappend-#endif type Level = Int @@ -283,10 +277,8 @@ instance Monoid Statistics where Statistics t1 f1 `mappend` Statistics t2 f2 = Statistics (t1 + t2) (f1 + f2) mempty = Statistics 0 0-#if MIN_VERSION_base(4,9,0) instance Semigroup Statistics where (<>) = mappend-#endif -- | @computeStatistics@ computes a summary 'Statistics' for -- a given state of the 'StatusMap'.@@ -466,8 +458,9 @@ defaultValue = Auto parseValue = parseUseColor optionName = return "color"- optionHelp = return "When to use colored output (default: 'auto')"+ optionHelp = return "When to use colored output" optionCLParser = mkOptionCLParser $ metavar "never|always|auto"+ showDefaultValue = Just . displayUseColor -- | By default, when the option @--hide-successes@ is given and the output -- goes to an ANSI-capable terminal, we employ some ANSI terminal tricks to@@ -483,7 +476,7 @@ -- -- When that happens, this option can be used to disable the tricks. In -- that case, the test name will be printed only once the test fails.-newtype AnsiTricks = AnsiTricks Bool+newtype AnsiTricks = AnsiTricks { getAnsiTricks :: Bool } deriving Typeable instance IsOption AnsiTricks where@@ -493,8 +486,15 @@ optionHelp = return $ -- Multiline literals don't work because of -XCPP. "Enable various ANSI terminal tricks. " ++- "Can be set to 'true' (default) or 'false'."+ "Can be set to 'true' or 'false'."+ showDefaultValue = Just . displayBool . getAnsiTricks +displayBool :: Bool -> String+displayBool b =+ case b of+ False -> "false"+ True -> "true"+ -- | @useColor when isTerm@ decides if colors should be used, -- where @isTerm@ indicates whether @stdout@ is a terminal device. --@@ -514,6 +514,13 @@ "auto" -> return Auto _ -> Nothing +displayUseColor :: UseColor -> String+displayUseColor uc =+ case uc of+ Never -> "never"+ Always -> "always"+ Auto -> "auto"+ -- }}} --------------------------------------------------@@ -574,10 +581,8 @@ Maximum a `mappend` Maximum b = Maximum (a `max` b) MinusInfinity `mappend` a = a a `mappend` MinusInfinity = a-#if MIN_VERSION_base(4,9,0) instance Ord a => Semigroup (Maximum a) where (<>) = mappend-#endif -- | Compute the amount of space needed to align \"OK\"s and \"FAIL\"s computeAlignment :: OptionSet -> TestTree -> Int
Test/Tasty/Options.hs view
@@ -25,6 +25,7 @@ import qualified Data.Map as Map import Data.Map (Map)+import Data.Maybe import Data.Char (toLower) import Data.Tagged import Data.Proxy@@ -33,7 +34,7 @@ import Data.Foldable import Prelude hiding (mod) -- Silence FTP import warnings import Options.Applicative-#if MIN_VERSION_base(4,9,0)+#if !MIN_VERSION_base(4,11,0) import Data.Semigroup (Semigroup) import qualified Data.Semigroup (Semigroup((<>))) #endif@@ -52,6 +53,12 @@ -- | The option description or help string. This can be an arbitrary -- string. optionHelp :: Tagged v String+ -- | How a 'defaultValue' should be displayed in the help string. 'Nothing'+ -- (the default implementation) will result in nothing being displayed, while+ -- @'Just' def@ will result in @def@ being advertised as the default in the+ -- help string.+ showDefaultValue :: v -> Maybe String+ showDefaultValue _ = Nothing -- | A command-line option parser. -- -- It has a default implementation in terms of the other methods.@@ -62,11 +69,14 @@ -- Even if you override this, you still should implement all the methods -- above, to allow alternative interfaces. --- -- Do not supply a default value here for this parser!- -- This is because if no value was provided on the command line we may- -- lookup the option e.g. in the environment. But if the parser always- -- succeeds, we have no way to tell whether the user really provided the- -- option on the command line.+ -- Do not supply a default value (e.g., with the 'value' function) here+ -- for this parser! This is because if no value was provided on the command+ -- line we may lookup the option e.g. in the environment. But if the parser+ -- always succeeds, we have no way to tell whether the user really provided+ -- the option on the command line.+ --+ -- Similarly, do not use 'showDefaultWith' here, as it will be ignored. Use+ -- the 'showDefaultValue' method of 'IsOption' instead. -- (If we don't specify a default, the option becomes mandatory. -- So, when we build the complete parser for OptionSet, we turn a@@ -88,10 +98,8 @@ mempty = OptionSet mempty OptionSet a `mappend` OptionSet b = OptionSet $ Map.unionWith (flip const) a b-#if MIN_VERSION_base(4,9,0) instance Semigroup OptionSet where (<>) = mappend-#endif -- | Set the option value setOption :: IsOption v => v -> OptionSet -> OptionSet
Test/Tasty/Options/Core.hs view
@@ -37,6 +37,7 @@ optionName = return "num-threads" optionHelp = return "Number of threads to use for tests execution" optionCLParser = mkOptionCLParser (short 'j' <> metavar "NUMBER")+ showDefaultValue _ = Just "# of cores/capabilities" -- | Filtering function to prevent non-positive number of threads onlyPositive :: NumThreads -> Bool
Test/Tasty/Patterns/Eval.hs view
@@ -10,7 +10,7 @@ import Data.Maybe import Data.Char import Test.Tasty.Patterns.Types-#if !MIN_VERSION_base(4,8,0)+#if !MIN_VERSION_base(4,9,0) import Control.Applicative import Data.Traversable #endif
Test/Tasty/Patterns/Parser.hs view
@@ -28,16 +28,6 @@ newtype Parser a = Parser (ReadP a) deriving (Functor, Applicative, Alternative, Monad, MonadPlus) -#if !MIN_VERSION_base(4,6,0)-instance Applicative ReadP where- pure = return- (<*>) = ap-instance Alternative ReadP where- empty = mzero- (<|>) = mplus-#endif-- data ParseResult a = Success a | Invalid | Ambiguous [a] deriving Show
Test/Tasty/Runners/Reducers.hs view
@@ -43,10 +43,9 @@ import Control.Applicative import Prelude -- Silence AMP import warnings-#if MIN_VERSION_base(4,9,0)+#if !MIN_VERSION_base(4,11,0) import Data.Semigroup (Semigroup) import qualified Data.Semigroup (Semigroup((<>)))-#else import Data.Monoid #endif @@ -55,10 +54,8 @@ instance Applicative f => Monoid (Traversal f) where mempty = Traversal $ pure () Traversal f1 `mappend` Traversal f2 = Traversal $ f1 *> f2-#if MIN_VERSION_base(4,9,0) instance Applicative f => Semigroup (Traversal f) where (<>) = mappend-#endif -- | Monoid generated by @'liftA2' ('<>')@ --@@ -69,7 +66,5 @@ instance (Applicative f, Monoid a) => Monoid (Ap f a) where mempty = pure mempty mappend = liftA2 mappend-#if MIN_VERSION_base(4,9,0) instance (Applicative f, Monoid a) => Semigroup (Ap f a) where (<>) = mappend-#endif
Test/Tasty/Runners/Utils.hs view
@@ -5,6 +5,8 @@ import Control.Exception import Control.Applicative+import Control.Concurrent (mkWeakThreadId, myThreadId)+import Control.Monad (forM_) #ifndef VERSION_clock import Data.Time.Clock.POSIX (getPOSIXTime) #endif@@ -16,22 +18,16 @@ import qualified System.Clock as Clock #endif -import Test.Tasty.Core (Time)---- We install handlers only on UNIX (obviously) and on GHC >= 7.6.--- GHC 7.4 lacks mkWeakThreadId (see #181), and this is not important--- enough to look for an alternative implementation, so we just disable it--- there.-#define INSTALL_HANDLERS defined __UNIX__ && MIN_VERSION_base(4,6,0)+-- Install handlers only on UNIX+#define INSTALL_HANDLERS defined __UNIX__ #if INSTALL_HANDLERS-import Control.Concurrent (mkWeakThreadId, myThreadId)-import Control.Exception (Exception(..), throwTo)-import Control.Monad (forM_) import System.Posix.Signals import System.Mem.Weak (deRefWeak) #endif +import Test.Tasty.Core (Time)+ -- | Catch possible exceptions that may arise when evaluating a string. -- For normal (total) strings, this is a no-op. --@@ -71,8 +67,7 @@ #if INSTALL_HANDLERS main_thread_id <- myThreadId weak_tid <- mkWeakThreadId main_thread_id- forM_ [ sigABRT, sigBUS, sigFPE, sigHUP, sigILL, sigQUIT, sigSEGV,- sigSYS, sigTERM, sigUSR1, sigUSR2, sigXCPU, sigXFSZ ] $ \sig ->+ forM_ [ sigHUP, sigTERM, sigUSR1, sigUSR2, sigXCPU, sigXFSZ ] $ \sig -> installHandler sig (Catch $ send_exception weak_tid sig) Nothing where send_exception weak_tid sig = do
tasty.cabal view
@@ -2,7 +2,7 @@ -- see http://haskell.org/cabal/users-guide/ name: tasty-version: 1.2.3+version: 1.3 synopsis: Modern and extensible testing framework description: Tasty is a modern testing framework for Haskell. It lets you combine your unit tests, golden@@ -14,7 +14,7 @@ maintainer: Roman Cheplyaka <roma@ro-che.info> homepage: https://github.com/feuerbach/tasty bug-reports: https://github.com/feuerbach/tasty/issues--- copyright: +-- copyright: category: Testing build-type: Simple extra-source-files: CHANGELOG.md, README.md@@ -58,7 +58,7 @@ Test.Tasty.Ingredients.ListTests Test.Tasty.Ingredients.IncludingOptions build-depends:- base >= 4.5 && < 5,+ base >= 4.7 && < 5, stm >= 2.3, containers, mtl >= 2.1.3.1,@@ -67,22 +67,20 @@ unbounded-delays >= 0.1, async >= 2.0, ansi-terminal >= 0.9+ if(!impl(ghc >= 8.0))+ build-depends: semigroups if flag(clock) build-depends: clock >= 0.4.4.0 else build-depends: time >= 1.4 - if impl(ghc < 7.6)- -- for GHC.Generics- build-depends: ghc-prim- if !os(windows) && !impl(ghcjs) build-depends: unix, wcwidth cpp-options: -D__UNIX__ - -- hs-source-dirs: + -- hs-source-dirs: default-language: Haskell2010 default-extensions: CPP, ScopedTypeVariables, DeriveDataTypeable ghc-options: -Wall