hspec-discover 2.1.10 → 2.11.17
raw patch · 19 files changed
Files
- LICENSE +1/−3
- driver/hspec-discover.hs +8/−0
- hspec-discover.cabal +49/−33
- src/Config.hs +0/−45
- src/Main.hs +0/−8
- src/Run.hs +0/−148
- src/Test/Hspec/Discover/Config.hs +48/−0
- src/Test/Hspec/Discover/Run.hs +227/−0
- src/Test/Hspec/Discover/Sort.hs +33/−0
- test-data/empty-dir/Foo/Bar/Baz/.placeholder +0/−0
- test-data/nested-spec/Foo/Bar/BazSpec.hs +0/−0
- test-data/nested-spec/Foo/BarSpec.hs +0/−0
- test-data/nested-spec/FooSpec.hs +0/−0
- test/ConfigSpec.hs +0/−47
- test/RunSpec.hs +0/−126
- test/Test/Hspec/Discover/ConfigSpec.hs +47/−0
- test/Test/Hspec/Discover/RunSpec.hs +171/−0
- test/Test/Hspec/Discover/SortSpec.hs +83/−0
- version.yaml +7/−0
LICENSE view
@@ -1,6 +1,4 @@-Copyright (c) 2011-2015 Simon Hengel <sol@typeful.net>-Copyright (c) 2011-2012 Trystan Spangler <trystan.s@comcast.net>-Copyright (c) 2011-2011 Greg Weber <greg@gregweber.info>+Copyright (c) 2012-2026 Simon Hengel <sol@typeful.net> Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal
+ driver/hspec-discover.hs view
@@ -0,0 +1,8 @@+module Main (main) where++import System.Environment++import Test.Hspec.Discover.Run (run)++main :: IO ()+main = getArgs >>= run
hspec-discover.cabal view
@@ -1,26 +1,27 @@+cabal-version: 1.12++-- This file has been generated from package.yaml by hpack version 0.39.1.+--+-- see: https://github.com/sol/hpack+ name: hspec-discover-version: 2.1.10+version: 2.11.17 license: MIT license-file: LICENSE-copyright: (c) 2012-2015 Simon Hengel+copyright: (c) 2012-2026 Simon Hengel author: Simon Hengel <sol@typeful.net> maintainer: Simon Hengel <sol@typeful.net> build-type: Simple-cabal-version: >= 1.10 category: Testing stability: experimental bug-reports: https://github.com/hspec/hspec/issues-homepage: http://hspec.github.io/+homepage: https://hspec.github.io/ synopsis: Automatically discover and run Hspec tests description: Automatically discover and run Hspec tests .- <http://hspec.github.io/hspec-discover.html>-+ <https://hspec.github.io/hspec-discover.html> extra-source-files:- test-data/nested-spec/FooSpec.hs- test-data/nested-spec/Foo/Bar/BazSpec.hs- test-data/nested-spec/Foo/BarSpec.hs- test-data/empty-dir/Foo/Bar/Baz/.placeholder+ version.yaml source-repository head type: git@@ -28,41 +29,56 @@ subdir: hspec-discover library+ hs-source-dirs:+ src+ ghc-options: -Wall -fno-warn-incomplete-uni-patterns+ build-depends:+ base >=4.5.0.0 && <5+ , directory+ , filepath+ exposed: False+ exposed-modules:+ Test.Hspec.Discover.Config+ Test.Hspec.Discover.Run+ Test.Hspec.Discover.Sort+ other-modules:+ Paths_hspec_discover default-language: Haskell2010 executable hspec-discover- ghc-options:- -Wall+ ghc-options: -Wall -fno-warn-incomplete-uni-patterns hs-source-dirs:- src- main-is:- Main.hs- other-modules:- Run- Config+ driver+ main-is: hspec-discover.hs build-depends:- base == 4.*- , filepath+ base >=4.5.0.0 && <5 , directory+ , filepath+ , hspec-discover+ other-modules:+ Paths_hspec_discover default-language: Haskell2010 test-suite spec- type:- exitcode-stdio-1.0- ghc-options:- -Wall+ type: exitcode-stdio-1.0+ ghc-options: -Wall -fno-warn-incomplete-uni-patterns hs-source-dirs:- src- , test- main-is:- Spec.hs+ test+ main-is: Spec.hs other-modules: Helper- RunSpec- ConfigSpec+ Test.Hspec.Discover.ConfigSpec+ Test.Hspec.Discover.RunSpec+ Test.Hspec.Discover.SortSpec+ Paths_hspec_discover build-depends:- base == 4.*- , filepath+ QuickCheck >=2.7+ , base >=4.5.0.0 && <5 , directory- , hspec-meta >= 2.1.5+ , filepath+ , hspec-discover+ , hspec-meta ==2.11.17+ , mockery >=0.3.5+ build-tool-depends:+ hspec-meta:hspec-meta-discover default-language: Haskell2010
− src/Config.hs
@@ -1,45 +0,0 @@-module Config (- Config (..)-, defaultConfig-, parseConfig-, usage-) where--import Data.Maybe-import System.Console.GetOpt--data Config = Config {- configNested :: Bool-, configFormatter :: Maybe String-, configNoMain :: Bool-, configModuleName :: Maybe String-} deriving (Eq, Show)--defaultConfig :: Config-defaultConfig = Config False Nothing False Nothing--options :: [OptDescr (Config -> Config)]-options = [- Option [] ["nested"] (NoArg $ \c -> c {configNested = True}) ""- , Option [] ["formatter"] (ReqArg (\s c -> c {configFormatter = Just s}) "FORMATTER") ""- , Option [] ["module-name"] (ReqArg (\s c -> c {configModuleName = Just s}) "NAME") ""- , Option [] ["no-main"] (NoArg $ \c -> c {configNoMain = True}) ""- ]--usage :: String -> String-usage prog = "\nUsage: " ++ prog ++ " SRC CUR DST [--module-name=NAME]\n"--parseConfig :: String -> [String] -> Either String Config-parseConfig prog args = case getOpt Permute options args of- (opts, [], []) -> let- c = (foldl (flip id) defaultConfig opts)- in- if (configNoMain c && isJust (configFormatter c))- then- formatError "option `--formatter=<fmt>' does not make sense with `--no-main'\n"- else- Right c- (_, _, err:_) -> formatError err- (_, arg:_, _) -> formatError ("unexpected argument `" ++ arg ++ "'\n")- where- formatError err = Left (prog ++ ": " ++ err ++ usage prog)
− src/Main.hs
@@ -1,8 +0,0 @@-module Main (main) where--import System.Environment--import Run (run)--main :: IO ()-main = getArgs >>= run
− src/Run.hs
@@ -1,148 +0,0 @@-{-# LANGUAGE TypeSynonymInstances, FlexibleInstances, OverloadedStrings #-}-{-# OPTIONS_GHC -fno-warn-orphans #-}--- | A preprocessor that finds and combines specs.-module Run (- run---- exported for testing-, Spec(..)-, importList-, fileToSpec-, findSpecs-, getFilesRecursive-, driverWithFormatter-, moduleNameFromId-, pathToModule-) where-import Control.Monad-import Control.Applicative-import Data.List-import Data.Char-import Data.Maybe-import Data.String-import System.Environment-import System.Exit-import System.IO-import System.Directory (doesDirectoryExist, getDirectoryContents, doesFileExist)-import System.FilePath hiding (combine)--import Config--instance IsString ShowS where- fromString = showString--data Spec = Spec {- specFile :: FilePath-, specModule :: String-} deriving (Eq, Show)--run :: [String] -> IO ()-run args_ = do- name <- getProgName- case args_ of- src : _ : dst : args -> case parseConfig name args of- Left err -> do- hPutStrLn stderr err- exitFailure- Right conf -> do- when (configNested conf) (hPutStrLn stderr "hspec-discover: WARNING - The `--nested' flag is deprecated and will be removed in a future release!")- specs <- findSpecs src- writeFile dst (mkSpecModule src conf specs)- _ -> do- hPutStrLn stderr (usage name)- exitFailure--mkSpecModule :: FilePath -> Config -> [Spec] -> String-mkSpecModule src conf nodes =- ( "{-# LINE 1 " . shows src . " #-}\n"- . showString "{-# OPTIONS_GHC -fno-warn-warnings-deprecations #-}\n"- . showString ("module " ++ moduleName src conf ++" where\n")- . importList nodes- . showString "import Test.Hspec.Discover\n"- . maybe driver driverWithFormatter (configFormatter conf)- . showString "spec :: Spec\n"- . showString "spec = "- . formatSpecs nodes- ) "\n"- where- driver =- case configNoMain conf of- False ->- showString "main :: IO ()\n"- . showString "main = hspec spec\n"- True -> ""--moduleName :: FilePath -> Config -> String-moduleName src conf = fromMaybe (if configNoMain conf then pathToModule src else "Main") (configModuleName conf)---- | Derive module name from specified path.-pathToModule :: FilePath -> String-pathToModule f = toUpper m:ms- where- fileName = last $ splitDirectories f- m:ms = takeWhile (/='.') fileName--driverWithFormatter :: String -> ShowS-driverWithFormatter f =- showString "import qualified " . showString (moduleNameFromId f) . showString "\n"- . showString "main :: IO ()\n"- . showString "main = hspecWithFormatter " . showString f . showString " spec\n"---- | Return module name of a fully qualified identifier.-moduleNameFromId :: String -> String-moduleNameFromId = reverse . dropWhile (== '.') . dropWhile (/= '.') . reverse---- | Generate imports for a list of specs.-importList :: [Spec] -> ShowS-importList = foldr (.) "" . map f- where- f :: Spec -> ShowS- f spec = "import qualified " . showString (specModule spec) . "Spec\n"---- | Combine a list of strings with (>>).-sequenceS :: [ShowS] -> ShowS-sequenceS = foldr (.) "" . intersperse " >> "---- | Convert a list of specs to code.-formatSpecs :: [Spec] -> ShowS-formatSpecs xs- | null xs = "return ()"- | otherwise = sequenceS (map formatSpec xs)---- | Convert a spec to code.-formatSpec :: Spec -> ShowS-formatSpec (Spec file name) = "postProcessSpec " . shows file . " (describe " . shows name . " " . showString name . "Spec.spec)"--findSpecs :: FilePath -> IO [Spec]-findSpecs src = do- let (dir, file) = splitFileName src- mapMaybe (fileToSpec dir) . filter (/= file) <$> getFilesRecursive dir--fileToSpec :: FilePath -> FilePath -> Maybe Spec-fileToSpec dir file = case reverse $ splitDirectories file of- x:xs -> case stripSuffix "Spec.hs" x <|> stripSuffix "Spec.lhs" x of- Just name | isValidModuleName name && all isValidModuleName xs ->- Just . Spec (dir </> file) $ (intercalate "." . reverse) (name : xs)- _ -> Nothing- _ -> Nothing- where- stripSuffix :: Eq a => [a] -> [a] -> Maybe [a]- stripSuffix suffix str = reverse <$> stripPrefix (reverse suffix) (reverse str)---- See `Cabal.Distribution.ModuleName` (http://git.io/bj34)-isValidModuleName :: String -> Bool-isValidModuleName [] = False-isValidModuleName (c:cs) = isUpper c && all isValidModuleChar cs--isValidModuleChar :: Char -> Bool-isValidModuleChar c = isAlphaNum c || c == '_' || c == '\''--getFilesRecursive :: FilePath -> IO [FilePath]-getFilesRecursive baseDir = sort <$> go []- where- go :: FilePath -> IO [FilePath]- go dir = do- c <- map (dir </>) . filter (`notElem` [".", ".."]) <$> getDirectoryContents (baseDir </> dir)- dirs <- filterM (doesDirectoryExist . (baseDir </>)) c >>= mapM go- files <- filterM (doesFileExist . (baseDir </>)) c- return (files ++ concat dirs)
+ src/Test/Hspec/Discover/Config.hs view
@@ -0,0 +1,48 @@+-- |+-- /NOTE:/ This module is not meant for public consumption. For user+-- documentation look at https://hspec.github.io/hspec-discover.html.+module Test.Hspec.Discover.Config (+ Config (..)+, defaultConfig+, parseConfig+, usage+) where++import Data.Maybe+import System.Console.GetOpt++data Config = Config {+ configNested :: Bool+, configFormatter :: Maybe String+, configNoMain :: Bool+, configModuleName :: Maybe String+} deriving (Eq, Show)++defaultConfig :: Config+defaultConfig = Config False Nothing False Nothing++options :: [OptDescr (Config -> Config)]+options = [+ Option [] ["nested"] (NoArg $ \c -> c {configNested = True}) ""+ , Option [] ["formatter"] (ReqArg (\s c -> c {configFormatter = Just s}) "FORMATTER") ""+ , Option [] ["module-name"] (ReqArg (\s c -> c {configModuleName = Just s}) "NAME") ""+ , Option [] ["no-main"] (NoArg $ \c -> c {configNoMain = True}) ""+ ]++usage :: String -> String+usage prog = "\nUsage: " ++ prog ++ " SRC CUR DST [--module-name=NAME]\n"++parseConfig :: String -> [String] -> Either String Config+parseConfig prog args = case getOpt Permute options args of+ (opts, [], []) -> let+ c = foldl (flip id) defaultConfig opts+ in+ if configNoMain c && isJust (configFormatter c)+ then+ formatError "option `--formatter=<fmt>' does not make sense with `--no-main'\n"+ else+ Right c+ (_, _, err:_) -> formatError err+ (_, arg:_, _) -> formatError ("unexpected argument `" ++ arg ++ "'\n")+ where+ formatError err = Left (prog ++ ": " ++ err ++ usage prog)
+ src/Test/Hspec/Discover/Run.hs view
@@ -0,0 +1,227 @@+{-# LANGUAGE FlexibleInstances, OverloadedStrings #-}+{-# OPTIONS_GHC -fno-warn-orphans #-}+-- | A preprocessor that finds and combines specs.+--+-- /NOTE:/ This module is not meant for public consumption. For user+-- documentation look at https://hspec.github.io/hspec-discover.html.+module Test.Hspec.Discover.Run (+ run++-- exported for testing+, Spec(..)+, importList+, driverWithFormatter+, moduleNameFromId+, pathToModule+, Tree(..)+, Forest(..)+, Hook(..)+, discover+) where+import Control.Monad+import Control.Applicative+import Data.List+import Data.Char+import Data.Maybe+import Data.String+import System.Environment+import System.Exit+import System.IO+import System.Directory (doesDirectoryExist, getDirectoryContents, doesFileExist)+import System.FilePath hiding (combine)++import Test.Hspec.Discover.Config+import Test.Hspec.Discover.Sort++instance IsString ShowS where+ fromString = showString++data Spec = Spec String | Hook String [Spec]+ deriving (Eq, Show)++run :: [String] -> IO ()+run args_ = do+ name <- getProgName+ case args_ of+ src : _ : dst : args -> case parseConfig name args of+ Left err -> do+ hPutStrLn stderr err+ exitFailure+ Right conf -> do+ when (configNested conf) (hPutStrLn stderr "hspec-discover: WARNING - The `--nested' option is deprecated and will be removed in a future release!")+ when (configNoMain conf) (hPutStrLn stderr "hspec-discover: WARNING - The `--no-main' option is deprecated and will be removed in a future release!")+ when (isJust $ configFormatter conf) (hPutStrLn stderr "hspec-discover: WARNING - The `--formatter' option is deprecated and will be removed in a future release!")+ specs <- findSpecs src+ writeFile dst (mkSpecModule src conf specs)+ _ -> do+ hPutStrLn stderr (usage name)+ exitFailure++mkSpecModule :: FilePath -> Config -> Maybe [Spec] -> String+mkSpecModule src conf nodes =+ ( "{-# LINE 1 " . shows src . " #-}\n"+ . showString "{-# LANGUAGE NoImplicitPrelude #-}\n"+ . showString "{-# OPTIONS_GHC -w -Wall -fno-warn-warnings-deprecations #-}\n"+ . showString ("module " ++ moduleName src conf ++ " where\n")+ . importList nodes+ . showString "import Test.Hspec.Discover\n"+ . maybe driver driverWithFormatter (configFormatter conf)+ . showString "spec :: Spec\n"+ . showString "spec = "+ . formatSpecs nodes+ ) "\n"+ where+ driver =+ case configNoMain conf of+ False ->+ showString "main :: IO ()\n"+ . showString "main = hspec spec\n"+ True -> ""++moduleName :: FilePath -> Config -> String+moduleName src conf = fromMaybe (if configNoMain conf then pathToModule src else "Main") (configModuleName conf)++-- | Derive module name from specified path.+pathToModule :: FilePath -> String+pathToModule f = toUpper m:ms+ where+ fileName = last $ splitDirectories f+ m:ms = takeWhile (/= '.') fileName++driverWithFormatter :: String -> ShowS+driverWithFormatter f =+ showString "import qualified " . showString (moduleNameFromId f) . showString "\n"+ . showString "main :: IO ()\n"+ . showString "main = hspecWithFormatter " . showString f . showString " spec\n"++-- | Return module name of a fully qualified identifier.+moduleNameFromId :: String -> String+moduleNameFromId = reverse . dropWhile (== '.') . dropWhile (/= '.') . reverse++-- | Generate imports for a list of specs.+importList :: Maybe [Spec] -> ShowS+importList = foldr (.) "" . map f . maybe [] moduleNames+ where+ f :: String -> ShowS+ f spec = "import qualified " . showString spec . "\n"++moduleNames :: [Spec] -> [String]+moduleNames = fromForest+ where+ fromForest :: [Spec] -> [String]+ fromForest = concatMap fromTree++ fromTree :: Spec -> [String]+ fromTree tree = case tree of+ Spec name -> [name ++ "Spec"]+ Hook name forest -> name : fromForest forest++-- | Combine a list of strings with (>>).+sequenceS :: [ShowS] -> ShowS+sequenceS = foldr (.) "" . intersperse " >> "++formatSpecs :: Maybe [Spec] -> ShowS+formatSpecs = maybe "return ()" fromForest+ where+ fromForest :: [Spec] -> ShowS+ fromForest = sequenceS . map fromTree++ fromTree :: Spec -> ShowS+ fromTree tree = case tree of+ Spec name -> "describe " . shows name . " " . showString name . "Spec.spec"+ Hook name forest -> "(" . showString name . ".hook $ " . fromForest forest . ")"++findSpecs :: FilePath -> IO (Maybe [Spec])+findSpecs = fmap (fmap toSpecs) . discover++toSpecs :: Forest -> [Spec]+toSpecs = fromForest []+ where+ fromForest :: [String] -> Forest -> [Spec]+ fromForest names (Forest WithHook xs) = [Hook (mkModule ("SpecHook" : names)) $ concatMap (fromTree names) xs]+ fromForest names (Forest WithoutHook xs) = concatMap (fromTree names) xs++ fromTree :: [String] -> Tree -> [Spec]+ fromTree names spec = case spec of+ Leaf name -> [Spec $ mkModule (name : names )]+ Node name forest -> fromForest (name : names) forest++ mkModule :: [String] -> String+ mkModule = intercalate "." . reverse++-- See `Cabal.Distribution.ModuleName` (https://git.io/bj34)+isValidModuleName :: String -> Bool+isValidModuleName [] = False+isValidModuleName (c:cs) = isUpper c && all isValidModuleChar cs++isValidModuleChar :: Char -> Bool+isValidModuleChar c = isAlphaNum c || c == '_' || c == '\''++data Tree = Leaf String | Node String Forest+ deriving (Eq, Show)++data Forest = Forest Hook [Tree]+ deriving (Eq, Show)++data Hook = WithHook | WithoutHook+ deriving (Eq, Show)++sortKey :: Tree -> (String, Int)+sortKey tree = case tree of+ Leaf name -> (name, 0)+ Node name _ -> (name, 1)++discover :: FilePath -> IO (Maybe Forest)+discover src = (>>= filterSrc) <$> specForest dir+ where+ filterSrc :: Forest -> Maybe Forest+ filterSrc (Forest hook xs) = ensureForest hook $ maybe id (filter . (/=)) (toSpec file) xs++ (dir, file) = splitFileName src++specForest :: FilePath -> IO (Maybe Forest)+specForest dir = do+ files <- listDirectory dir+ hook <- mkHook dir files+ ensureForest hook . sortNaturallyBy sortKey . catMaybes <$> mapM toSpecTree files+ where+ toSpecTree :: FilePath -> IO (Maybe Tree)+ toSpecTree name+ | isValidModuleName name = do+ doesDirectoryExist (dir </> name) `fallback` Nothing $ do+ xs <- specForest (dir </> name)+ return $ Node name <$> xs+ | otherwise = do+ doesFileExist (dir </> name) `fallback` Nothing $ do+ return $ toSpec name++mkHook :: FilePath -> [FilePath] -> IO Hook+mkHook dir files+ | "SpecHook.hs" `elem` files = do+ doesFileExist (dir </> "SpecHook.hs") `fallback` WithoutHook $ do+ return WithHook+ | otherwise = return WithoutHook++fallback :: IO Bool -> a -> IO a -> IO a+fallback p def action = do+ bool <- p+ if bool then action else return def++toSpec :: FilePath -> Maybe Tree+toSpec file = Leaf <$> (spec >>= ensure isValidModuleName)+ where+ spec :: Maybe String+ spec = stripSuffix "Spec.hs" file <|> stripSuffix "Spec.lhs" file++ stripSuffix :: Eq a => [a] -> [a] -> Maybe [a]+ stripSuffix suffix str = reverse <$> stripPrefix (reverse suffix) (reverse str)++ensure :: (a -> Bool) -> a -> Maybe a+ensure p a = guard (p a) >> Just a++ensureForest :: Hook -> [Tree] -> Maybe Forest+ensureForest hook = fmap (Forest hook) . ensure (not . null)++listDirectory :: FilePath -> IO [FilePath]+listDirectory path = filter f <$> getDirectoryContents path+ where f filename = filename /= "." && filename /= ".."
+ src/Test/Hspec/Discover/Sort.hs view
@@ -0,0 +1,33 @@+-- |+-- /NOTE:/ This module is not meant for public consumption. For user+-- documentation look at https://hspec.github.io/hspec-discover.html.+module Test.Hspec.Discover.Sort (+ sortNaturallyBy+, NaturalSortKey+, naturalSortKey+) where++import Control.Arrow+import Data.Char+import Data.List+import Data.Ord++sortNaturallyBy :: (a -> (String, Int)) -> [a] -> [a]+sortNaturallyBy f = sortBy (comparing ((\ (k, t) -> (naturalSortKey k, t)) . f))++newtype NaturalSortKey = NaturalSortKey [Chunk]+ deriving (Eq, Ord)++data Chunk = Numeric Integer Int | Textual [(Char, Char)]+ deriving (Eq, Ord)++naturalSortKey :: String -> NaturalSortKey+naturalSortKey = NaturalSortKey . chunks+ where+ chunks [] = []+ chunks s@(c:_)+ | isDigit c = Numeric (read num) (length num) : chunks afterNum+ | otherwise = Textual (map (toLower &&& id) str) : chunks afterStr+ where+ (num, afterNum) = span isDigit s+ (str, afterStr) = break isDigit s
− test-data/empty-dir/Foo/Bar/Baz/.placeholder
− test-data/nested-spec/Foo/Bar/BazSpec.hs
− test-data/nested-spec/Foo/BarSpec.hs
− test-data/nested-spec/FooSpec.hs
− test/ConfigSpec.hs
@@ -1,47 +0,0 @@-module ConfigSpec (main, spec) where--import Helper--import Config--main :: IO ()-main = hspec spec--spec :: Spec-spec = do- describe "parseConfig" $ do- let parse = parseConfig "hspec-discover"-- it "recognizes --nested" $ do- parse ["--nested"] `shouldBe` Right (defaultConfig {configNested = True})-- it "recognizes --formatter" $ do- parse ["--formatter", "someFormatter"] `shouldBe` Right (defaultConfig {configFormatter = Just "someFormatter"})-- it "recognizes --no-main" $ do- parse ["--no-main"] `shouldBe` Right (defaultConfig {configNoMain = True})-- it "returns error message on unrecognized option" $ do- parse ["--foo"] `shouldBe` (Left . unlines) [- "hspec-discover: unrecognized option `--foo'"- , ""- , "Usage: hspec-discover SRC CUR DST [--module-name=NAME]"- ]-- it "returns error message on unexpected argument" $ do- parse ["foo"] `shouldBe` (Left . unlines) [- "hspec-discover: unexpected argument `foo'"- , ""- , "Usage: hspec-discover SRC CUR DST [--module-name=NAME]"- ]-- it "returns error message on --formatter=<fmt> with --no-main" $ do- parse ["--no-main", "--formatter=foo"] `shouldBe` (Left . unlines) [- "hspec-discover: option `--formatter=<fmt>' does not make sense with `--no-main'"- , ""- , "Usage: hspec-discover SRC CUR DST [--module-name=NAME]"- ]-- context "when option is given multiple times" $ do- it "gives the last occurrence precedence" $ do- parse ["--formatter", "foo", "--formatter", "bar"] `shouldBe` Right (defaultConfig {configFormatter = Just "bar"})
− test/RunSpec.hs
@@ -1,126 +0,0 @@-module RunSpec (main, spec) where--import Helper--import System.IO-import System.Directory-import System.FilePath-import Data.List (sort)--import Run hiding (Spec)-import qualified Run--main :: IO ()-main = hspec spec--withTempFile :: (FilePath -> IO a) -> IO a-withTempFile action = do- dir <- getTemporaryDirectory- (file, h) <- openTempFile dir ""- hClose h- action file <* removeFile file---spec :: Spec-spec = do- describe "run" $ do- it "generates test driver" $ withTempFile $ \f -> do- run ["test-data/nested-spec/Spec.hs", "", f]- readFile f `shouldReturn` unlines [- "{-# LINE 1 \"test-data/nested-spec/Spec.hs\" #-}"- , "{-# OPTIONS_GHC -fno-warn-warnings-deprecations #-}"- , "module Main where"- , "import qualified Foo.Bar.BazSpec"- , "import qualified Foo.BarSpec"- , "import qualified FooSpec"- , "import Test.Hspec.Discover"- , "main :: IO ()"- , "main = hspec spec"- , "spec :: Spec"- , "spec = " ++ unwords [- "postProcessSpec \"test-data/nested-spec/Foo/Bar/BazSpec.hs\" (describe \"Foo.Bar.Baz\" Foo.Bar.BazSpec.spec)"- , ">> postProcessSpec \"test-data/nested-spec/Foo/BarSpec.hs\" (describe \"Foo.Bar\" Foo.BarSpec.spec)"- , ">> postProcessSpec \"test-data/nested-spec/FooSpec.hs\" (describe \"Foo\" FooSpec.spec)"- ]- ]-- it "generates test driver for an empty directory" $ withTempFile $ \f -> do- run ["test-data/empty-dir/Spec.hs", "", f]- readFile f `shouldReturn` unlines [- "{-# LINE 1 \"test-data/empty-dir/Spec.hs\" #-}"- , "{-# OPTIONS_GHC -fno-warn-warnings-deprecations #-}"- , "module Main where"- , "import Test.Hspec.Discover"- , "main :: IO ()"- , "main = hspec spec"- , "spec :: Spec"- , "spec = return ()"- ]-- describe "pathToModule" $ do- it "derives module name from a given path" $ do- pathToModule "test/Spec.hs" `shouldBe` "Spec"-- describe "getFilesRecursive" $ do- it "recursively returns all file entries of a given directory" $ do- getFilesRecursive "test-data" `shouldReturn` sort [- "empty-dir/Foo/Bar/Baz/.placeholder"- , "nested-spec/Foo/Bar/BazSpec.hs"- , "nested-spec/Foo/BarSpec.hs"- , "nested-spec/FooSpec.hs"- ]-- describe "fileToSpec" $ do- it "converts path to spec name" $ do- fileToSpec "" "FooSpec.hs" `shouldBe` Just (spec_ "FooSpec.hs" "Foo")-- it "rejects spec with empty name" $ do- fileToSpec "" "Spec.hs" `shouldBe` Nothing-- it "works for lhs files" $ do- fileToSpec "" "FooSpec.lhs" `shouldBe` Just (spec_ "FooSpec.lhs" "Foo")-- it "returns Nothing for invalid spec name" $ do- fileToSpec "" "foo" `shouldBe` Nothing-- context "when spec does not have a valid module name" $ do- it "returns Nothing" $ do- fileToSpec "" "flycheck_Spec.hs" `shouldBe` Nothing-- context "when any component of a hierarchical module name is not valid"$ do- it "returns Nothing" $ do- fileToSpec "" ("Valid" </> "invalid" </>"MiddleNamesSpec.hs") `shouldBe` Nothing-- context "when path has directory component" $ do- it "converts path to spec name" $ do- let file = "Foo" </> "Bar" </> "BazSpec.hs"- fileToSpec "" file `shouldBe` Just (spec_ file "Foo.Bar.Baz")-- it "rejects spec with empty name" $ do- fileToSpec "" ("Foo" </> "Bar" </> "Spec.hs") `shouldBe` Nothing-- describe "findSpecs" $ do- it "finds specs" $ do- let dir = "test-data/nested-spec"- findSpecs (dir </> "Spec.hs") `shouldReturn` [spec_ (dir </> "Foo/Bar/BazSpec.hs") "Foo.Bar.Baz", spec_ (dir </> "Foo/BarSpec.hs") "Foo.Bar", spec_ (dir </> "FooSpec.hs") "Foo"]-- describe "driverWithFormatter" $ do- it "generates a test driver that uses a custom formatter" $ do- driverWithFormatter "Some.Module.formatter" "" `shouldBe` unlines [- "import qualified Some.Module"- , "main :: IO ()"- , "main = hspecWithFormatter Some.Module.formatter spec"- ]-- describe "moduleNameFromId" $ do- it "returns the module name of a fully qualified identifier" $ do- moduleNameFromId "Some.Module.someId" `shouldBe` "Some.Module"-- describe "importList" $ do- it "generates imports for a list of specs" $ do- importList [spec_ "FooSpec.hs" "Foo", spec_ "BarSpec.hs" "Bar"] "" `shouldBe` unlines [- "import qualified FooSpec"- , "import qualified BarSpec"- ]- where- spec_ = Run.Spec
+ test/Test/Hspec/Discover/ConfigSpec.hs view
@@ -0,0 +1,47 @@+module Test.Hspec.Discover.ConfigSpec (main, spec) where++import Helper++import Test.Hspec.Discover.Config++main :: IO ()+main = hspec spec++spec :: Spec+spec = do+ describe "parseConfig" $ do+ let parse = parseConfig "hspec-discover"++ it "recognizes --nested" $ do+ parse ["--nested"] `shouldBe` Right (defaultConfig {configNested = True})++ it "recognizes --formatter" $ do+ parse ["--formatter", "someFormatter"] `shouldBe` Right (defaultConfig {configFormatter = Just "someFormatter"})++ it "recognizes --no-main" $ do+ parse ["--no-main"] `shouldBe` Right (defaultConfig {configNoMain = True})++ it "returns error message on unrecognized option" $ do+ parse ["--foo"] `shouldBe` (Left . unlines) [+ "hspec-discover: unrecognized option `--foo'"+ , ""+ , "Usage: hspec-discover SRC CUR DST [--module-name=NAME]"+ ]++ it "returns error message on unexpected argument" $ do+ parse ["foo"] `shouldBe` (Left . unlines) [+ "hspec-discover: unexpected argument `foo'"+ , ""+ , "Usage: hspec-discover SRC CUR DST [--module-name=NAME]"+ ]++ it "returns error message on --formatter=<fmt> with --no-main" $ do+ parse ["--no-main", "--formatter=foo"] `shouldBe` (Left . unlines) [+ "hspec-discover: option `--formatter=<fmt>' does not make sense with `--no-main'"+ , ""+ , "Usage: hspec-discover SRC CUR DST [--module-name=NAME]"+ ]++ context "when option is given multiple times" $ do+ it "gives the last occurrence precedence" $ do+ parse ["--formatter", "foo", "--formatter", "bar"] `shouldBe` Right (defaultConfig {configFormatter = Just "bar"})
+ test/Test/Hspec/Discover/RunSpec.hs view
@@ -0,0 +1,171 @@+module Test.Hspec.Discover.RunSpec (spec) where++import Helper+import Test.Mockery.Directory++import Test.Hspec.Discover.Run hiding (Spec)+import qualified Test.Hspec.Discover.Run as Run++spec :: Spec+spec = do+ describe "run" $ around_ inTempDirectory $ do+ it "generates a test driver" $ do+ touch "test/FooSpec.hs"+ touch "test/Foo/Bar/BazSpec.hs"+ touch "test/Foo/BarSpec.hs"+ run ["test/Spec.hs", "", "out"]+ readFile "out" `shouldReturn` unlines [+ "{-# LINE 1 \"test/Spec.hs\" #-}"+ , "{-# LANGUAGE NoImplicitPrelude #-}"+ , "{-# OPTIONS_GHC -w -Wall -fno-warn-warnings-deprecations #-}"+ , "module Main where"+ , "import qualified FooSpec"+ , "import qualified Foo.BarSpec"+ , "import qualified Foo.Bar.BazSpec"+ , "import Test.Hspec.Discover"+ , "main :: IO ()"+ , "main = hspec spec"+ , "spec :: Spec"+ , "spec = " ++ unwords [+ "describe \"Foo\" FooSpec.spec"+ , ">> describe \"Foo.Bar\" Foo.BarSpec.spec"+ , ">> describe \"Foo.Bar.Baz\" Foo.Bar.BazSpec.spec"+ ]+ ]++ it "generates a test driver with no Main/main" $ do+ touch "test/FooSpec.hs"+ touch "test/Foo/Bar/BazSpec.hs"+ touch "test/Foo/BarSpec.hs"+ run ["test/Spec.hs", "", "out", "--no-main"]+ readFile "out" `shouldReturn` unlines [+ "{-# LINE 1 \"test/Spec.hs\" #-}"+ , "{-# LANGUAGE NoImplicitPrelude #-}"+ , "{-# OPTIONS_GHC -w -Wall -fno-warn-warnings-deprecations #-}"+ , "module Spec where"+ , "import qualified FooSpec"+ , "import qualified Foo.BarSpec"+ , "import qualified Foo.Bar.BazSpec"+ , "import Test.Hspec.Discover"+ , "spec :: Spec"+ , "spec = " ++ unwords [+ "describe \"Foo\" FooSpec.spec"+ , ">> describe \"Foo.Bar\" Foo.BarSpec.spec"+ , ">> describe \"Foo.Bar.Baz\" Foo.Bar.BazSpec.spec"+ ]+ ]++ it "generates a test driver with hooks" $ do+ touch "test/FooSpec.hs"+ touch "test/Foo/Bar/BazSpec.hs"+ touch "test/Foo/BarSpec.hs"+ touch "test/Foo/SpecHook.hs"+ touch "test/SpecHook.hs"+ run ["test/Spec.hs", "", "out"]+ readFile "out" `shouldReturn` unlines [+ "{-# LINE 1 \"test/Spec.hs\" #-}"+ , "{-# LANGUAGE NoImplicitPrelude #-}"+ , "{-# OPTIONS_GHC -w -Wall -fno-warn-warnings-deprecations #-}"+ , "module Main where"+ , "import qualified SpecHook"+ , "import qualified FooSpec"+ , "import qualified Foo.SpecHook"+ , "import qualified Foo.BarSpec"+ , "import qualified Foo.Bar.BazSpec"+ , "import Test.Hspec.Discover"+ , "main :: IO ()"+ , "main = hspec spec"+ , "spec :: Spec"+ , "spec = " ++ unwords [+ "(SpecHook.hook $ describe \"Foo\" FooSpec.spec"+ , ">> (Foo.SpecHook.hook $ describe \"Foo.Bar\" Foo.BarSpec.spec"+ , ">> describe \"Foo.Bar.Baz\" Foo.Bar.BazSpec.spec))"+ ]+ ]++ it "generates a test driver for an empty directory" $ do+ touch "test/Foo/Bar/Baz/.placeholder"+ run ["test/Spec.hs", "", "out"]+ readFile "out" `shouldReturn` unlines [+ "{-# LINE 1 \"test/Spec.hs\" #-}"+ , "{-# LANGUAGE NoImplicitPrelude #-}"+ , "{-# OPTIONS_GHC -w -Wall -fno-warn-warnings-deprecations #-}"+ , "module Main where"+ , "import Test.Hspec.Discover"+ , "main :: IO ()"+ , "main = hspec spec"+ , "spec :: Spec"+ , "spec = return ()"+ ]++ describe "pathToModule" $ do+ it "derives module name from a given path" $ do+ pathToModule "test/Spec.hs" `shouldBe` "Spec"++ describe "driverWithFormatter" $ do+ it "generates a test driver that uses a custom formatter" $ do+ driverWithFormatter "Some.Module.formatter" "" `shouldBe` unlines [+ "import qualified Some.Module"+ , "main :: IO ()"+ , "main = hspecWithFormatter Some.Module.formatter spec"+ ]++ describe "moduleNameFromId" $ do+ it "returns the module name of a fully qualified identifier" $ do+ moduleNameFromId "Some.Module.someId" `shouldBe` "Some.Module"++ describe "importList" $ do+ it "generates imports for a list of specs" $ do+ importList (Just [Run.Spec "Foo", Run.Spec "Bar"]) "" `shouldBe` unlines [+ "import qualified FooSpec"+ , "import qualified BarSpec"+ ]++ describe "discover" $ do+ it "discovers spec files" $ do+ inTempDirectory $ do+ touch "test/Spec.hs"+ touch "test/FooSpec.hs"+ touch "test/BarSpec.hs"+ discover "test/Spec.hs" `shouldReturn` Just (Forest WithoutHook [Leaf "Bar", Leaf "Foo"])++ it "discovers nested spec files" $ do+ inTempDirectory $ do+ touch "test/Spec.hs"+ touch "test/Foo/BarSpec.hs"+ touch "test/Foo/BazSpec.hs"+ discover "test/Spec.hs" `shouldReturn` Just (Forest WithoutHook [Node "Foo" (Forest WithoutHook [Leaf "Bar", Leaf "Baz"])])++ it "discovers hooks" $ do+ inTempDirectory $ do+ touch "test/Spec.hs"+ touch "test/FooSpec.hs"+ touch "test/BarSpec.hs"+ touch "test/SpecHook.hs"+ discover "test/Spec.hs" `shouldReturn` Just (Forest WithHook [Leaf "Bar", Leaf "Foo"])++ it "discovers nested hooks" $ do+ inTempDirectory $ do+ touch "test/Spec.hs"+ touch "test/Foo/BarSpec.hs"+ touch "test/Foo/BazSpec.hs"+ touch "test/Foo/SpecHook.hs"+ discover "test/Spec.hs" `shouldReturn` Just (Forest WithoutHook [Node "Foo" (Forest WithHook [Leaf "Bar", Leaf "Baz"])])++ it "ignores invalid module names" $ do+ inTempDirectory $ do+ touch "test/Spec.hs"+ touch "test/barSpec.hs"+ discover "test/Spec.hs" `shouldReturn` Nothing++ it "ignores empty directories" $ do+ inTempDirectory $ do+ touch "test/Spec.hs"+ touch "test/Foo/.keep"+ discover "test/Spec.hs" `shouldReturn` Nothing++ it "ignores directories with extension" $ do+ inTempDirectory $ do+ touch "test/Spec.hs"+ touch "test/Foo.hs/BarSpec.hs"+ discover "test/Spec.hs" `shouldReturn` Nothing
+ test/Test/Hspec/Discover/SortSpec.hs view
@@ -0,0 +1,83 @@+module Test.Hspec.Discover.SortSpec (spec) where++import Helper+import Test.QuickCheck++import Test.Hspec.Discover.Sort++shuffleAndSort :: [String] -> IO [String]+shuffleAndSort xs = sortNaturally <$> generate (shuffle xs)++sortNaturally :: [String] -> [String]+sortNaturally = sortNaturallyBy $ \ name -> (name, 0)++spec :: Spec+spec = do+ describe "naturalSortKey" $ do+ it "is injective" $ property $ \ a b -> do+ a /= b ==> naturalSortKey a /= naturalSortKey b++ describe "sortNaturally" $ do+ it "gives shorter strings precedence" $ do+ let expected = [+ ""+ , "a"+ , "aa"+ ]+ shuffleAndSort expected `shouldReturn` expected++ it "gives numbers precedence" $ do+ let expected = [+ "Hello2World"+ , "Hello World"+ ]+ shuffleAndSort expected `shouldReturn` expected++ it "sorts numbers in ascending order" $ do+ let expected = [+ "Spec9.hs"+ , "Spec10.hs"+ ]+ shuffleAndSort expected `shouldReturn` expected++ it "breaks numeric ties by string length" $ do+ let expected = [+ "Hello 2 World"+ , "Hello 02 World"+ ]+ shuffleAndSort expected `shouldReturn` expected++ it "given upper-case letters precedence over lower-case letters" $ do+ let+ expected = [+ "AA.hs"+ , "Aa.hs"+ , "aA.hs"+ , "aa.hs"+ , "B.hs"+ , "b.hs"+ ]+ shuffleAndSort expected `shouldReturn` expected++ it "sorts number separated strings" $ do+ let expected = [+ "Hello2World9"+ , "Hello2World!0"+ ]+ shuffleAndSort expected `shouldReturn` expected++ it "sorts string separated numbers" $ do+ let expected = [+ "3.1.415"+ , "3.14.15"+ ]+ shuffleAndSort expected `shouldReturn` expected++ it "groups common string prefixes together" $ do+ let expected = [+ "SpecFoo.hs"+ , "SpecFoo.lhs"+ , "Specfoo.hs"+ , "Specfoo.lhs"+ ]+ shuffleAndSort expected `shouldReturn` expected
+ version.yaml view
@@ -0,0 +1,7 @@+version: &version 2.11.17+synopsis: A Testing Framework for Haskell+author: Simon Hengel <sol@typeful.net>+maintainer: Simon Hengel <sol@typeful.net>+category: Testing+stability: experimental+homepage: https://hspec.github.io/