skeletest 0.3.0 → 0.3.1
raw patch · 24 files changed
+406/−380 lines, 24 filesPVP: major bump suggested
API removals or changes: PVP suggests a major version bump
API changes (from Hackage documentation)
- Skeletest.Internal.TestRunner: TestResultMessageSection :: BoxSpec -> TestResultMessage
- Skeletest.Internal.Utils.BoxDrawing: BoxHeader :: Text -> BoxSpecContent
- Skeletest.Internal.Utils.BoxDrawing: BoxText :: Text -> BoxSpecContent
- Skeletest.Internal.Utils.BoxDrawing: data BoxSpecContent
- Skeletest.Internal.Utils.BoxDrawing: drawBox :: BoxSpec -> IO Text
- Skeletest.Internal.Utils.BoxDrawing: instance GHC.Classes.Eq Skeletest.Internal.Utils.BoxDrawing.BoxSpecContent
- Skeletest.Internal.Utils.BoxDrawing: instance GHC.Show.Show Skeletest.Internal.Utils.BoxDrawing.BoxSpecContent
- Skeletest.Internal.Utils.BoxDrawing: type BoxSpec = [BoxSpecContent]
- Skeletest.Plugin: TestResultMessageSection :: BoxSpec -> TestResultMessage
+ Skeletest.Internal.Spec.Output: BoxHeader :: Text -> BoxSpecContent
+ Skeletest.Internal.Spec.Output: BoxText :: Text -> BoxSpecContent
+ Skeletest.Internal.Spec.Output: data BoxSpecContent
+ Skeletest.Internal.Spec.Output: indent :: IndentLevel -> Text -> Text
+ Skeletest.Internal.Spec.Output: instance GHC.Classes.Eq Skeletest.Internal.Spec.Output.BoxSpecContent
+ Skeletest.Internal.Spec.Output: instance GHC.Show.Show Skeletest.Internal.Spec.Output.BoxSpecContent
+ Skeletest.Internal.Spec.Output: renderPrettyFailure :: Text -> [Text] -> [(FilePath, Int, Int, Int)] -> IO Text
+ Skeletest.Internal.Spec.Output: reportGroup :: IndentLevel -> Text -> IO ()
+ Skeletest.Internal.Spec.Output: reportTestInProgress :: IndentLevel -> Text -> IO ()
+ Skeletest.Internal.Spec.Output: reportTestResultWithBoxMessage :: Maybe (Window Int) -> IndentLevel -> Text -> Text -> BoxSpec -> IO ()
+ Skeletest.Internal.Spec.Output: reportTestResultWithInlineMessage :: IndentLevel -> Text -> Text -> IO ()
+ Skeletest.Internal.Spec.Output: reportTestResultWithoutMessage :: Text -> IO ()
+ Skeletest.Internal.Spec.Output: type BoxSpec = [BoxSpecContent]
+ Skeletest.Internal.Spec.Output: type IndentLevel = Int
+ Skeletest.Internal.TestRunner: TestResultMessageBox :: BoxSpec -> TestResultMessage
+ Skeletest.Plugin: BoxHeader :: Text -> BoxSpecContent
+ Skeletest.Plugin: BoxText :: Text -> BoxSpecContent
+ Skeletest.Plugin: TestResultMessageBox :: BoxSpec -> TestResultMessage
+ Skeletest.Plugin: data BoxSpecContent
+ Skeletest.Plugin: type BoxSpec = [BoxSpecContent]
Files
- CHANGELOG.md +8/−0
- skeletest.cabal +2/−2
- src/Skeletest/Internal/Capture.hs +3/−3
- src/Skeletest/Internal/Spec.hs +17/−10
- src/Skeletest/Internal/Spec/Output.hs +144/−0
- src/Skeletest/Internal/TestRunner.hs +8/−59
- src/Skeletest/Internal/Utils/BoxDrawing.hs +0/−49
- src/Skeletest/Plugin.hs +3/−0
- test/Skeletest/AssertionsSpec.hs +15/−20
- test/Skeletest/Internal/CLISpec.hs +1/−2
- test/Skeletest/Internal/CaptureSpec.hs +6/−12
- test/Skeletest/Internal/FixturesSpec.hs +1/−2
- test/Skeletest/Internal/SnapshotSpec.hs +2/−4
- test/Skeletest/Internal/SpecSpec.hs +1/−2
- test/Skeletest/Internal/__snapshots__/CLISpec.snap.md +3/−5
- test/Skeletest/Internal/__snapshots__/FixturesSpec.snap.md +3/−5
- test/Skeletest/Internal/__snapshots__/SnapshotSpec.snap.md +20/−23
- test/Skeletest/MainSpec.hs +2/−4
- test/Skeletest/PredicateSpec.hs +6/−12
- test/Skeletest/PropSpec.hs +2/−4
- test/Skeletest/TestUtils/Integration.hs +16/−4
- test/Skeletest/__snapshots__/AssertionsSpec.snap.md +93/−102
- test/Skeletest/__snapshots__/PredicateSpec.snap.md +14/−15
- test/Skeletest/__snapshots__/PropSpec.snap.md +36/−41
CHANGELOG.md view
@@ -1,3 +1,11 @@+## v0.3.1++API changes:+* Re-export `BoxSpec` and `BoxContent` from `Skeletest.Plugin`++Runtime changes:+* Tweak formatting of test failures/errors+ ## v0.3.0 GHC support:
skeletest.cabal view
@@ -1,7 +1,7 @@ cabal-version: 3.0 name: skeletest-version: 0.3.0+version: 0.3.1 synopsis: Batteries-included, opinionated test framework description: Batteries-included, opinionated test framework. See README.md for more details. homepage: https://github.com/brandonchinn178/skeletest#readme@@ -48,10 +48,10 @@ Skeletest.Internal.Preprocessor Skeletest.Internal.Snapshot Skeletest.Internal.Spec+ Skeletest.Internal.Spec.Output Skeletest.Internal.TestInfo Skeletest.Internal.TestRunner Skeletest.Internal.TestTargets- Skeletest.Internal.Utils.BoxDrawing Skeletest.Internal.Utils.Color Skeletest.Internal.Utils.Diff Skeletest.Internal.Utils.HList
src/Skeletest/Internal/Capture.hs view
@@ -28,11 +28,11 @@ noCleanup, withCleanup, )+import Skeletest.Internal.Spec.Output (BoxSpecContent (..)) import Skeletest.Internal.TestRunner ( TestResult (..), TestResultMessage (..), )-import Skeletest.Internal.Utils.BoxDrawing (BoxSpecContent (..)) import System.Directory (removePathForcibly) import System.IO qualified as IO import UnliftIO.Exception (finally)@@ -85,7 +85,7 @@ updateResult output result = result { testResultMessage =- TestResultMessageSection . concat $+ TestResultMessageBox . concat $ [ toBoxContents (testResultMessage result) , renderOutput output ]@@ -93,7 +93,7 @@ toBoxContents = \case TestResultMessageNone -> [] TestResultMessageInline msg -> [BoxText msg]- TestResultMessageSection box -> box+ TestResultMessageBox box -> box renderOutput (stdout, stderr) = concat [ if Text.null stdout then [] else [BoxHeader "Captured stdout", BoxText stdout]
src/Skeletest/Internal/Spec.hs view
@@ -50,6 +50,13 @@ SomeMarker (..), findMarker, )+import Skeletest.Internal.Spec.Output (+ reportGroup,+ reportTestInProgress,+ reportTestResultWithBoxMessage,+ reportTestResultWithInlineMessage,+ reportTestResultWithoutMessage,+ ) import Skeletest.Internal.TestInfo (TestInfo (TestInfo), withTestInfo) import Skeletest.Internal.TestInfo qualified as TestInfo import Skeletest.Internal.TestRunner (@@ -60,11 +67,10 @@ ) import Skeletest.Internal.TestTargets (TestTarget, TestTargets, matchesTest) import Skeletest.Internal.TestTargets qualified as TestTargets-import Skeletest.Internal.Utils.BoxDrawing (drawBox) import Skeletest.Internal.Utils.Color qualified as Color import Skeletest.Plugin (Hooks (..), defaultHooks) import Skeletest.Prop.Internal (Property)-import System.IO qualified as IO+import System.Console.Terminal.Size qualified as Term import UnliftIO.Exception ( finally, fromException,@@ -164,12 +170,11 @@ runTree baseTestInfo = \case SpecGroup{..} -> do let lvl = getIndentLevel baseTestInfo- Text.putStrLn $ indent lvl groupLabel+ reportGroup lvl groupLabel runTrees baseTestInfo{TestInfo.testContexts = TestInfo.testContexts baseTestInfo <> [groupLabel]} groupTrees SpecTest{..} -> do let lvl = getIndentLevel baseTestInfo- Text.putStr $ indent lvl (testName <> ": ")- IO.hFlush IO.stdout+ reportTestInProgress lvl testName let testInfo = baseTestInfo@@ -181,11 +186,14 @@ tid <- myThreadId runTest testInfo testAction `finally` cleanupFixtures (PerTestFixtureKey tid) - Text.putStrLn testResultLabel case testResultMessage of- TestResultMessageNone -> pure ()- TestResultMessageInline msg -> Text.putStrLn $ indent (lvl + 1) msg- TestResultMessageSection box -> drawBox box >>= Text.putStrLn+ TestResultMessageNone -> do+ reportTestResultWithoutMessage testResultLabel+ TestResultMessageInline msg -> do+ reportTestResultWithInlineMessage lvl testResultLabel msg+ TestResultMessageBox box -> do+ termSize <- Term.size+ reportTestResultWithBoxMessage termSize lvl testName testResultLabel box pure testResultSuccess runTest info action =@@ -200,7 +208,6 @@ Nothing -> testResultFromError e getIndentLevel testInfo = length (TestInfo.testContexts testInfo) + 1 -- +1 to include the module name- indent lvl = Text.intercalate "\n" . map (Text.replicate (lvl * 4) " " <>) . Text.splitOn "\n" {----- Entrypoint -----}
+ src/Skeletest/Internal/Spec/Output.hs view
@@ -0,0 +1,144 @@+{-# LANGUAGE LambdaCase #-}+{-# LANGUAGE OverloadedStrings #-}++module Skeletest.Internal.Spec.Output (+ reportGroup,+ reportTestInProgress,+ reportTestResultWithoutMessage,+ reportTestResultWithInlineMessage,+ reportTestResultWithBoxMessage,+ renderPrettyFailure,+ BoxSpec,+ BoxSpecContent (..),+ IndentLevel,+ indent,+) where++import Data.Maybe (listToMaybe)+import Data.Text (Text)+import Data.Text qualified as Text+import Data.Text.IO qualified as Text+import System.Console.Terminal.Size qualified as Term+import System.IO qualified as IO+import UnliftIO.Exception (SomeException, try)++reportGroup :: IndentLevel -> Text -> IO ()+reportGroup lvl name = do+ Text.putStrLn $ indent lvl name++reportTestInProgress :: IndentLevel -> Text -> IO ()+reportTestInProgress lvl testName = do+ Text.putStr $ indent lvl (testName <> ": ")+ IO.hFlush IO.stdout++reportTestResultWithoutMessage :: Text -> IO ()+reportTestResultWithoutMessage testResultLabel = do+ Text.putStrLn testResultLabel++reportTestResultWithInlineMessage :: IndentLevel -> Text -> Text -> IO ()+reportTestResultWithInlineMessage lvl testResultLabel testResultMessage = do+ Text.putStrLn testResultLabel+ Text.putStrLn $ indent (lvl + 1) testResultMessage++reportTestResultWithBoxMessage :: Maybe (Term.Window Int) -> IndentLevel -> Text -> Text -> BoxSpec -> IO ()+reportTestResultWithBoxMessage termSize lvl testName testResultLabel box = do+ if null box+ then do+ Text.putStrLn testResultLabel+ else do+ Text.putStr "\r"+ Text.putStrLn $ drawBoxHeader lvl testName <> ": " <> testResultLabel+ Text.putStrLn $ drawBoxBody termSize box++type IndentLevel = Int++indentSize :: Int+indentSize = 4++indentWith :: Text -> IndentLevel -> Text -> Text+indentWith fill lvl = Text.intercalate "\n" . map (Text.replicate (lvl * indentSize) fill <>) . Text.splitOn "\n"++indent :: IndentLevel -> Text -> Text+indent = indentWith " "++-- | Render a test failure like:+--+-- @+-- At test/Skeletest/Internal/TestTargetsSpec.hs:19:+-- |+-- | parseTestTargets input `shouldBe` Right (Just expected)+-- | ^^^^^^^^+--+-- Right 1 ≠ Left 1+-- @+renderPrettyFailure ::+ -- | Message+ Text ->+ -- | Failure context+ [Text] ->+ -- | Call stack (file, line, startCol, endCol)+ [(FilePath, Int, Int, Int)] ->+ IO Text+renderPrettyFailure msg ctx callstack = do+ prettyStackTrace <- mapM renderCallLine . reverse $ callstack+ pure . Text.intercalate "\n\n" . concat $+ [ prettyStackTrace+ , if null ctx+ then []+ else [Text.intercalate "\n" $ reverse ctx]+ , [msg]+ ]+ where+ renderCallLine (path, lineNum, startCol, endCol) = do+ mLine <-+ try (Text.readFile path) >>= \case+ Right srcFile -> pure $ getLineNum lineNum srcFile+ Left (_ :: SomeException) -> pure Nothing+ let (srcLine, pointerLine) =+ case mLine of+ Just line ->+ ( line+ , Text.replicate (startCol - 1) " " <> Text.replicate (endCol - startCol) "^"+ )+ Nothing ->+ ( "<unknown line>"+ , ""+ )++ pure . Text.intercalate "\n" $+ [ Text.pack path <> ":" <> (Text.pack . show) lineNum <> ":"+ , "│"+ , "│ " <> srcLine+ , "│ " <> pointerLine+ ]++ getLineNum n = listToMaybe . take 1 . drop (n - 1) . Text.lines++type BoxSpec = [BoxSpecContent]++data BoxSpecContent+ = BoxText Text+ | BoxHeader Text+ deriving (Show, Eq)++drawBoxHeader :: IndentLevel -> Text -> Text+drawBoxHeader lvl testName = "╭" <> Text.drop 2 (indentWith "─" lvl "") <> " " <> testName++drawBoxBody :: Maybe (Term.Window Int) -> BoxSpec -> Text+drawBoxBody termSize boxContents = Text.intercalate "\n" $ concatMap draw boxContents <> [footer]+ where+ termWidth = maybe 80 Term.width termSize+ width =+ maximum . (termWidth :) . flip concatMap boxContents $ \case+ BoxHeader s -> [Text.length s + indentSize + 2]+ BoxText s -> [Text.length line + 2 | line <- Text.lines s]++ footer = "╰" <> Text.replicate (width - 1) "─"++ draw = \case+ BoxHeader s ->+ [ drawLine ""+ , "╞═══ " <> s+ ]+ BoxText s -> map drawLine $ Text.lines s+ drawLine s = "│ " <> s
src/Skeletest/Internal/TestRunner.hs view
@@ -20,17 +20,19 @@ import Control.Monad (guard) import Control.Monad.IO.Class (MonadIO)-import Data.Maybe (listToMaybe) import Data.Text (Text) import Data.Text qualified as Text-import Data.Text.IO qualified as Text import Data.Typeable (typeOf) import GHC.IO.Exception qualified as GHC import GHC.Stack (CallStack) import GHC.Stack qualified as GHC import Skeletest.Internal.Error (SkeletestError)+import Skeletest.Internal.Spec.Output (+ BoxSpec,+ BoxSpecContent (..),+ renderPrettyFailure,+ ) import Skeletest.Internal.TestInfo (TestInfo)-import Skeletest.Internal.Utils.BoxDrawing (BoxSpec, BoxSpecContent (..)) import Skeletest.Internal.Utils.Color qualified as Color import Text.Read (readMaybe) import UnliftIO.Exception (@@ -38,7 +40,6 @@ SomeException (..), displayException, fromException,- try, ) {----- Testable -----}@@ -65,7 +66,7 @@ data TestResultMessage = TestResultMessageNone | TestResultMessageInline Text- | TestResultMessageSection BoxSpec+ | TestResultMessageBox BoxSpec testResultPass :: TestResult testResultPass =@@ -82,7 +83,7 @@ TestResult { testResultSuccess = False , testResultLabel = Color.red "FAIL"- , testResultMessage = TestResultMessageSection [BoxText msg]+ , testResultMessage = TestResultMessageBox [BoxText msg] } testResultFromError :: SomeException -> IO TestResult@@ -92,7 +93,7 @@ TestResult { testResultSuccess = False , testResultLabel = Color.red "ERROR"- , testResultMessage = TestResultMessageSection [BoxText msg]+ , testResultMessage = TestResultMessageBox [BoxText msg] } where renderMsg@@ -184,55 +185,3 @@ [ (srcLocFile, srcLocStartLine, srcLocStartCol, srcLocEndCol) | (_, GHC.SrcLoc{..}) <- GHC.getCallStack callStack ]---- | Render a test failure like:------ @--- At test/Skeletest/Internal/TestTargetsSpec.hs:19:--- |--- | parseTestTargets input `shouldBe` Right (Just expected)--- | ^^^^^^^^------ Right 1 ≠ Left 1--- @-renderPrettyFailure ::- -- | Message- Text ->- FailContext ->- -- | Call stack (file, line, startCol, endCol)- [(FilePath, Int, Int, Int)] ->- IO Text-renderPrettyFailure msg ctx callstack = do- prettyStackTrace <- mapM renderCallLine . reverse $ callstack- pure . Text.intercalate "\n\n" . concat $- [ prettyStackTrace- , if null ctx- then []- else [Text.intercalate "\n" $ reverse ctx]- , [msg]- ]- where- renderCallLine (path, lineNum, startCol, endCol) = do- mLine <-- try (Text.readFile path) >>= \case- Right srcFile -> pure $ getLineNum lineNum srcFile- Left (_ :: SomeException) -> pure Nothing- let (srcLine, pointerLine) =- case mLine of- Just line ->- ( line- , Text.replicate (startCol - 1) " " <> Text.replicate (endCol - startCol) "^"- )- Nothing ->- ( "<unknown line>"- , ""- )-- pure . Text.intercalate "\n" $- [ Text.pack path <> ":" <> (Text.pack . show) lineNum <> ":"- , "|"- , "| " <> srcLine- , "| " <> pointerLine- ]-- getLineNum n = listToMaybe . take 1 . drop (n - 1) . Text.lines
− src/Skeletest/Internal/Utils/BoxDrawing.hs
@@ -1,49 +0,0 @@-{-# LANGUAGE LambdaCase #-}-{-# LANGUAGE OverloadedStrings #-}--module Skeletest.Internal.Utils.BoxDrawing (- BoxSpec,- BoxSpecContent (..),- drawBox,-) where--import Data.Text (Text)-import Data.Text qualified as Text-import System.Console.Terminal.Size qualified as Term--type BoxSpec = [BoxSpecContent]--data BoxSpecContent- = BoxText Text- | BoxHeader Text- deriving (Show, Eq)--drawBox :: BoxSpec -> IO Text-drawBox box = do- width <- maybe 80 (max 40 . Term.width) <$> Term.size- pure $ drawBox' width box--drawBox' :: Int -> BoxSpec -> Text-drawBox' width boxContents = Text.intercalate "\n" $ [header] <> concatMap draw boxContents <> [footer]- where- header = "╔" <> Text.replicate (width - 2) "═" <> "╗"- footer = "╚" <> Text.replicate (width - 2) "═" <> "╝"-- draw = \case- BoxHeader s ->- [ drawLine ""- , "╟─" <> cpad (width - 4) "─" ("⟨ " <> s <> " ⟩") <> "─╢"- ]- BoxText s ->- [ drawLine line- | rawLine <- Text.lines s- , line <- if Text.null rawLine then [""] else Text.chunksOf (width - 4) rawLine- ]- drawLine s = "║ " <> rpad (width - 4) " " s <> " ║"-- rpad n fill s = s <> Text.replicate (n - Text.length s) fill- cpad n fill s =- let total = n - Text.length s- left = total `div` 2- right = total - left- in Text.replicate left fill <> s <> Text.replicate right fill
src/Skeletest/Plugin.hs view
@@ -11,6 +11,8 @@ -- ** TestResult TestResult (..), TestResultMessage (..),+ BoxSpec,+ BoxSpecContent (..), -- ** TestInfo TestInfo (..),@@ -23,6 +25,7 @@ import Skeletest.Internal.CLI (Flag) import Skeletest.Internal.Markers (findMarker, hasMarkerNamed) import Skeletest.Internal.Snapshot (SnapshotRenderer)+import Skeletest.Internal.Spec.Output (BoxSpec, BoxSpecContent (..)) import Skeletest.Internal.TestInfo (TestInfo (..)) import Skeletest.Internal.TestRunner (TestResult (..), TestResultMessage (..))
test/Skeletest/AssertionsSpec.hs view
@@ -26,8 +26,7 @@ , "spec = it \"should fail\" $ 1 `shouldBe` (2 :: Int)" ] - (code, stdout, stderr) <- runTests runner []- code `shouldBe` ExitFailure 1+ (stdout, stderr) <- expectFailure $ runTests runner [] stderr `shouldBe` "" stdout `shouldSatisfy` P.matchesSnapshot @@ -45,8 +44,7 @@ , "spec = it \"should fail\" $ 1 `shouldNotBe` (1 :: Int)" ] - (code, stdout, stderr) <- runTests runner []- code `shouldBe` ExitFailure 1+ (stdout, stderr) <- expectFailure $ runTests runner [] stderr `shouldBe` "" stdout `shouldSatisfy` P.matchesSnapshot @@ -65,8 +63,7 @@ , "spec = it \"should fail\" $ (-1) `shouldSatisfy` P.gt (0 :: Int)" ] - (code, stdout, stderr) <- runTests runner []- code `shouldBe` ExitFailure 1+ (stdout, stderr) <- expectFailure $ runTests runner [] stderr `shouldBe` "" stdout `shouldSatisfy` P.matchesSnapshot @@ -85,8 +82,7 @@ , "spec = it \"should fail\" $ 1 `shouldNotSatisfy` P.gt (0 :: Int)" ] - (code, stdout, stderr) <- runTests runner []- code `shouldBe` ExitFailure 1+ (stdout, stderr) <- expectFailure $ runTests runner [] stderr `shouldBe` "" stdout `shouldSatisfy` P.matchesSnapshot @@ -103,8 +99,7 @@ , " 1 `shouldBe` (2 :: Int)" ] - (code, stdout, stderr) <- runTests runner []- code `shouldBe` ExitFailure 1+ (stdout, stderr) <- expectFailure $ runTests runner [] stderr `shouldBe` "" stdout `shouldSatisfy` P.matchesSnapshot @@ -119,8 +114,7 @@ , "spec = it \"should fail\" $ failTest \"error message\"" ] - (code, stdout, stderr) <- runTests runner []- code `shouldBe` ExitFailure 1+ (stdout, stderr) <- expectFailure $ runTests runner [] stderr `shouldBe` "" stdout `shouldSatisfy` P.matchesSnapshot @@ -141,8 +135,7 @@ , "expectGT x actual = actual `shouldSatisfy` P.gt x" ] - (code, stdout, stderr) <- runTests runner []- code `shouldBe` ExitFailure 1+ (stdout, stderr) <- expectFailure $ runTests runner [] stderr `shouldBe` "" stdout `shouldSatisfy` P.matchesSnapshot @@ -159,8 +152,7 @@ , " x `shouldBe` True" ] - (code, stdout, stderr) <- runTests runner []- code `shouldBe` ExitFailure 1+ (stdout, stderr) <- expectFailure $ runTests runner [] stderr `shouldBe` "" stdout `shouldSatisfy` P.matchesSnapshot @@ -177,8 +169,7 @@ , " pure ()" ] - (code, stdout, stderr) <- runTests runner []- code `shouldBe` ExitFailure 1+ (stdout, stderr) <- expectFailure $ runTests runner [] stderr `shouldBe` "" sanitizeTraceback stdout `shouldSatisfy` P.matchesSnapshot @@ -188,8 +179,12 @@ #if __GLASGOW_HASKELL__ == 910 sanitizeTraceback s = let (pre, post) = break (Text.pack "HasCallStack backtrace:" `Text.isInfixOf`) $ Text.lines $ Text.pack s- (_, post2) = break (Text.pack "╚" `Text.isPrefixOf`) $ drop 1 post- in Text.unpack . Text.unlines $ pre ++ post2+ (_, post2) = break (Text.pack "╰" `Text.isPrefixOf`) $ drop 1 post+ post2' =+ case post2 of+ [] -> []+ l : ls -> Text.take 80 l : ls+ in Text.unpack . Text.unlines $ pre ++ post2' #else sanitizeTraceback = id #endif
test/Skeletest/Internal/CLISpec.hs view
@@ -84,8 +84,7 @@ , " pure ()" ] - (code, stdout, stderr) <- runTests runner []- code `shouldBe` ExitFailure 1+ (stdout, stderr) <- expectFailure $ runTests runner [] stderr `shouldBe` "" stdout `shouldSatisfy` P.matchesSnapshot
test/Skeletest/Internal/CaptureSpec.hs view
@@ -64,10 +64,9 @@ , " " <> render_hPutStrLn handle "line2" , " 1 `shouldBe` 2" ]- (code, stdout, stderr) <- runTests runner []+ (stdout, stderr) <- expectFailure $ runTests runner [] stderr `shouldBe` "" stdout `shouldSatisfy` P.matchesSnapshot- code `shouldBe` ExitFailure 1 integration . it "is rendered on test error" $ do runner <- getFixture@@ -88,10 +87,9 @@ , " Just _ <- pure Nothing" , " pure ()" ]- (code, stdout, stderr) <- runTests runner []+ (stdout, stderr) <- expectFailure $ runTests runner [] stderr `shouldBe` "" stdout `shouldSatisfy` P.matchesSnapshot- code `shouldBe` ExitFailure 1 integration . it "is not captured with --capture-output=off" $ do runner <- getFixture@@ -141,10 +139,8 @@ , " s <- output." <> func , " s `shouldBe` " <> show "test1\ntest2\n" ]- (code, stdout, stderr) <- runTests runner []- stderr `shouldBe` ""- context stdout $- code `shouldBe` ExitSuccess+ _ <- expectSuccess $ runTests runner []+ pure () fixtureReadSpec :: (String, String) -> Spec fixtureReadSpec (handle, func) =@@ -173,10 +169,8 @@ , " s <- output." <> func , " s `shouldBe` " <> show "test2\n" ]- (code, stdout, stderr) <- runTests runner []- stderr `shouldBe` ""- context stdout $- code `shouldBe` ExitSuccess+ _ <- expectSuccess $ runTests runner []+ pure () render_hPutStrLn :: String -> String -> String render_hPutStrLn handle s = "IO.hPutStrLn IO." <> handle <> " " <> show s
test/Skeletest/Internal/FixturesSpec.hs view
@@ -51,7 +51,6 @@ , " pure ()" ] - (code, stdout, stderr) <- runTests runner []- code `shouldBe` ExitFailure 1+ (stdout, stderr) <- expectFailure $ runTests runner [] stderr `shouldBe` "" stdout `shouldSatisfy` P.matchesSnapshot
test/Skeletest/Internal/SnapshotSpec.hs view
@@ -43,8 +43,7 @@ ] addTestFile runner "__snapshots__/ExampleSpec.snap.md" ["asdf"] - (code, stdout, stderr) <- runTests runner []- code `shouldBe` ExitFailure 1+ (stdout, stderr) <- expectFailure $ runTests runner [] stderr `shouldBe` "" stdout `shouldSatisfy` P.matchesSnapshot @@ -105,8 +104,7 @@ , "```" ] - (code, stdout, stderr) <- runTests runner []- code `shouldBe` ExitFailure 1+ (stdout, stderr) <- expectFailure $ runTests runner [] stderr `shouldBe` "" stdout `shouldSatisfy` P.matchesSnapshot
test/Skeletest/Internal/SpecSpec.hs view
@@ -52,8 +52,7 @@ , " it \"should fail too\" $ pure ()" ] - (code, stdout, stderr) <- runTests runner []- code `shouldBe` ExitFailure 1+ (stdout, stderr) <- expectFailure $ runTests runner [] stderr `shouldBe` "" stdout `shouldSatisfy` P.matchesSnapshot
test/Skeletest/Internal/__snapshots__/CLISpec.snap.md view
@@ -4,9 +4,7 @@ ``` ./ExampleSpec.hs- should error: ERROR-╔══════════════════════════════════════════════════════════════════════════════╗-║ CLI flag 'my-flag' was not registered. Did you add it to cliFlags in Main.hs ║-║ ? ║-╚══════════════════════════════════════════════════════════════════════════════╝+╭── should error: ERROR+│ CLI flag 'my-flag' was not registered. Did you add it to cliFlags in Main.hs?+╰─────────────────────────────────────────────────────────────────────────────── ```
test/Skeletest/Internal/__snapshots__/FixturesSpec.snap.md view
@@ -4,9 +4,7 @@ ``` ./ExampleSpec.hs- should error: ERROR-╔══════════════════════════════════════════════════════════════════════════════╗-║ Found circular dependency when resolving fixtures: FixtureA -> FixtureB -> F ║-║ ixtureD -> FixtureA ║-╚══════════════════════════════════════════════════════════════════════════════╝+╭── should error: ERROR+│ Found circular dependency when resolving fixtures: FixtureA -> FixtureB -> FixtureD -> FixtureA+╰──────────────────────────────────────────────────────────────────────────────────────────────── ```
test/Skeletest/Internal/__snapshots__/SnapshotSpec.snap.md view
@@ -4,10 +4,9 @@ ``` ./ExampleSpec.hs- should error: ERROR-╔══════════════════════════════════════════════════════════════════════════════╗-║ Snapshot file was corrupted: ./__snapshots__/ExampleSpec.snap.md ║-╚══════════════════════════════════════════════════════════════════════════════╝+╭── should error: ERROR+│ Snapshot file was corrupted: ./__snapshots__/ExampleSpec.snap.md+╰─────────────────────────────────────────────────────────────────────────────── ``` ## renders JSON values@@ -25,23 +24,21 @@ ``` ./ExampleSpec.hs- fails: FAIL-╔══════════════════════════════════════════════════════════════════════════════╗-║ ./ExampleSpec.hs:7: ║-║ | ║-║ | unlines ["new1", "same1", "same2", "new2"] `shouldSatisfy` P.matchesSnap ║-║ shot ║-║ | ^^^^^^^^^^^^^^^ ║-║ ║-║ Result differed from snapshot. Update snapshot with --update. ║-║ --- expected ║-║ +++ actual ║-║ @@ -1,4 +1,4 @@ ║-║ +new1 ║-║ same1 ║-║ -old1 ║-║ same2 ║-║ -old2 ║-║ +new2 ║-╚══════════════════════════════════════════════════════════════════════════════╝+╭── fails: FAIL+│ ./ExampleSpec.hs:7:+│ │+│ │ unlines ["new1", "same1", "same2", "new2"] `shouldSatisfy` P.matchesSnapshot+│ │ ^^^^^^^^^^^^^^^+│ +│ Result differed from snapshot. Update snapshot with --update.+│ --- expected+│ +++ actual+│ @@ -1,4 +1,4 @@+│ +new1+│ same1+│ -old1+│ same2+│ -old2+│ +new2+╰───────────────────────────────────────────────────────────────────────────────── ```
test/Skeletest/MainSpec.hs view
@@ -14,8 +14,7 @@ setMainFile runner [] addTestFile runner "ExampleSpec.hs" (minimalTest "ExampleSpec") - (code, stdout, stderr) <- runTests runner []- code `shouldBe` ExitFailure 1+ (stdout, stderr) <- expectFailure $ runTests runner [] stdout `shouldBe` "" normalizePluginError stderr `shouldSatisfy` P.matchesSnapshot @@ -44,8 +43,7 @@ ] addTestFile runner "ExampleSpec.hs" (minimalTest "ExampleSpec") - (code, stdout, stderr) <- runTests runner []- code `shouldBe` ExitFailure 1+ (stdout, stderr) <- expectFailure $ runTests runner [] stdout `shouldBe` "" normalizeGhc29916 stderr `shouldSatisfy` P.matchesSnapshot
test/Skeletest/PredicateSpec.hs view
@@ -143,8 +143,7 @@ , " User \"alice\" `shouldSatisfy` P.con User{name = P.eq \"\"}" ] - (code, stdout, stderr) <- runTests runner []- code `shouldBe` ExitFailure 1+ (stdout, stderr) <- expectFailure $ runTests runner [] stderr `shouldBe` "" stdout `shouldSatisfy` P.matchesSnapshot @@ -162,8 +161,7 @@ , " User \"alice\" `shouldSatisfy` P.con User{foo = P.eq \"\"}" ] - (code, stdout, stderr) <- runTests runner []- code `shouldBe` ExitFailure 1+ (stdout, stderr) <- expectFailure $ runTests runner [] stdout `shouldBe` "" stderr `shouldSatisfy` P.matchesSnapshot @@ -181,8 +179,7 @@ , " User \"alice\" (Just 1) `shouldSatisfy` P.con (User (P.eq \"\"))" ] - (code, stdout, stderr) <- runTests runner []- code `shouldBe` ExitFailure 1+ (stdout, stderr) <- expectFailure $ runTests runner [] stdout `shouldBe` "" (normalizeConFailure . normalizeVars) stderr `shouldSatisfy` P.matchesSnapshot @@ -198,8 +195,7 @@ , " \"\" `shouldSatisfy` P.con \"\"" ] - (code, stdout, stderr) <- runTests runner []- code `shouldBe` ExitFailure 1+ (stdout, stderr) <- expectFailure $ runTests runner [] stdout `shouldBe` "" stderr `shouldSatisfy` P.matchesSnapshot @@ -215,8 +211,7 @@ , " \"\" `shouldSatisfy` P.con" ] - (code, stdout, stderr) <- runTests runner []- code `shouldBe` ExitFailure 1+ (stdout, stderr) <- expectFailure $ runTests runner [] stdout `shouldBe` "" stderr `shouldSatisfy` P.matchesSnapshot @@ -232,8 +227,7 @@ , " \"\" `shouldSatisfy` P.con 1 2" ] - (code, stdout, stderr) <- runTests runner []- code `shouldBe` ExitFailure 1+ (stdout, stderr) <- expectFailure $ runTests runner [] stdout `shouldBe` "" stderr `shouldSatisfy` P.matchesSnapshot
test/Skeletest/PropSpec.hs view
@@ -22,8 +22,7 @@ , " discard" ] - (code, stdout, stderr) <- runTests runner []- code `shouldBe` ExitFailure 1+ (stdout, stderr) <- expectFailure $ runTests runner [] stderr `shouldBe` "" stdout `shouldSatisfy` P.matchesSnapshot @@ -49,7 +48,6 @@ , " (read . show) P.=== id `shouldNotSatisfy` P.isoWith (Gen.int $ Range.linear 0 10)" ] - (code, stdout, stderr) <- runTests runner ["--seed=0:0"]- code `shouldBe` ExitFailure 1+ (stdout, stderr) <- expectFailure $ runTests runner ["--seed=0:0"] stderr `shouldBe` "" stdout `shouldSatisfy` P.matchesSnapshot
test/Skeletest/TestUtils/Integration.hs view
@@ -13,7 +13,9 @@ -- * runTests runTests,+ expectCode, expectSuccess,+ expectFailure, -- * Re-exports ExitCode (..),@@ -107,15 +109,25 @@ ] setCWD dir p = p{cwd = Just dir} - sanitize = Text.unpack . stripControlChars . Text.strip . Text.pack+ sanitize = Text.unpack . stripOverwrites . stripControlChars . Text.strip . Text.pack+ stripOverwrites s =+ case Text.breakOn "\r" s of+ (_, "") -> s+ (pre, post) -> Text.dropWhileEnd (/= '\n') pre <> stripOverwrites (Text.drop 1 post) stripControlChars s = case Text.breakOn "\x1b" s of (_, "") -> s (pre, post) -> pre <> stripControlChars (Text.drop 1 . Text.dropWhile (/= 'm') $ post) -expectSuccess :: (HasCallStack) => IO (ExitCode, String, String) -> IO (String, String)-expectSuccess m = do+expectCode :: (HasCallStack) => ExitCode -> IO (ExitCode, String, String) -> IO (String, String)+expectCode expected m = do (code, stdout, stderr) <- m context (unlines ["===== stdout =====", stdout, "===== stderr =====", stderr]) $- code `shouldBe` ExitSuccess+ code `shouldBe` expected pure (stdout, stderr)++expectSuccess :: (HasCallStack) => IO (ExitCode, String, String) -> IO (String, String)+expectSuccess = expectCode ExitSuccess++expectFailure :: (HasCallStack) => IO (ExitCode, String, String) -> IO (String, String)+expectFailure = expectCode $ ExitFailure 1
test/Skeletest/__snapshots__/AssertionsSpec.snap.md view
@@ -4,154 +4,145 @@ ``` ./ExampleSpec.hs- should fail: FAIL-╔══════════════════════════════════════════════════════════════════════════════╗-║ ./ExampleSpec.hs:7: ║-║ | ║-║ | 1 `shouldBe` (2 :: Int) ║-║ | ^^^^^^^^^^ ║-║ ║-║ hello ║-║ world ║-║ ║-║ 1 ≠ 2 ║-╚══════════════════════════════════════════════════════════════════════════════╝+╭── should fail: FAIL+│ ./ExampleSpec.hs:7:+│ │+│ │ 1 `shouldBe` (2 :: Int)+│ │ ^^^^^^^^^^+│ +│ hello+│ world+│ +│ 1 ≠ 2+╰─────────────────────────────────────────────────────────────────────────────── ``` ## failTest / should show failure ``` ./ExampleSpec.hs- should fail: FAIL-╔══════════════════════════════════════════════════════════════════════════════╗-║ ./ExampleSpec.hs:5: ║-║ | ║-║ | spec = it "should fail" $ failTest "error message" ║-║ | ^^^^^^^^ ║-║ ║-║ error message ║-╚══════════════════════════════════════════════════════════════════════════════╝+╭── should fail: FAIL+│ ./ExampleSpec.hs:5:+│ │+│ │ spec = it "should fail" $ failTest "error message"+│ │ ^^^^^^^^+│ +│ error message+╰─────────────────────────────────────────────────────────────────────────────── ``` ## shouldBe / should show helpful failure ``` ./ExampleSpec.hs- should fail: FAIL-╔══════════════════════════════════════════════════════════════════════════════╗-║ ./ExampleSpec.hs:5: ║-║ | ║-║ | spec = it "should fail" $ 1 `shouldBe` (2 :: Int) ║-║ | ^^^^^^^^^^ ║-║ ║-║ 1 ≠ 2 ║-╚══════════════════════════════════════════════════════════════════════════════╝+╭── should fail: FAIL+│ ./ExampleSpec.hs:5:+│ │+│ │ spec = it "should fail" $ 1 `shouldBe` (2 :: Int)+│ │ ^^^^^^^^^^+│ +│ 1 ≠ 2+╰─────────────────────────────────────────────────────────────────────────────── ``` ## shouldNotBe / should show helpful failure ``` ./ExampleSpec.hs- should fail: FAIL-╔══════════════════════════════════════════════════════════════════════════════╗-║ ./ExampleSpec.hs:5: ║-║ | ║-║ | spec = it "should fail" $ 1 `shouldNotBe` (1 :: Int) ║-║ | ^^^^^^^^^^^^^ ║-║ ║-║ 1 = 1 ║-║ ║-║ Expected: ║-║ ≠ 1 ║-║ ║-║ Got: ║-║ 1 ║-╚══════════════════════════════════════════════════════════════════════════════╝+╭── should fail: FAIL+│ ./ExampleSpec.hs:5:+│ │+│ │ spec = it "should fail" $ 1 `shouldNotBe` (1 :: Int)+│ │ ^^^^^^^^^^^^^+│ +│ 1 = 1+│ +│ Expected:+│ ≠ 1+│ +│ Got:+│ 1+╰─────────────────────────────────────────────────────────────────────────────── ``` ## shouldNotSatisfy / should show helpful failure ``` ./ExampleSpec.hs- should fail: FAIL-╔══════════════════════════════════════════════════════════════════════════════╗-║ ./ExampleSpec.hs:6: ║-║ | ║-║ | spec = it "should fail" $ 1 `shouldNotSatisfy` P.gt (0 :: Int) ║-║ | ^^^^^^^^^^^^^^^^^^ ║-║ ║-║ 1 > 0 ║-║ ║-║ Expected: ║-║ ≯ 0 ║-║ ║-║ Got: ║-║ 1 ║-╚══════════════════════════════════════════════════════════════════════════════╝+╭── should fail: FAIL+│ ./ExampleSpec.hs:6:+│ │+│ │ spec = it "should fail" $ 1 `shouldNotSatisfy` P.gt (0 :: Int)+│ │ ^^^^^^^^^^^^^^^^^^+│ +│ 1 > 0+│ +│ Expected:+│ ≯ 0+│ +│ Got:+│ 1+╰─────────────────────────────────────────────────────────────────────────────── ``` ## shouldSatisfy / should show helpful failure ``` ./ExampleSpec.hs- should fail: FAIL-╔══════════════════════════════════════════════════════════════════════════════╗-║ ./ExampleSpec.hs:6: ║-║ | ║-║ | spec = it "should fail" $ (-1) `shouldSatisfy` P.gt (0 :: Int) ║-║ | ^^^^^^^^^^^^^^^ ║-║ ║-║ -1 ≯ 0 ║-╚══════════════════════════════════════════════════════════════════════════════╝+╭── should fail: FAIL+│ ./ExampleSpec.hs:6:+│ │+│ │ spec = it "should fail" $ (-1) `shouldSatisfy` P.gt (0 :: Int)+│ │ ^^^^^^^^^^^^^^^+│ +│ -1 ≯ 0+╰─────────────────────────────────────────────────────────────────────────────── ``` ## shows backtrace of failed assertions ``` ./ExampleSpec.hs- should fail: FAIL-╔══════════════════════════════════════════════════════════════════════════════╗-║ ./ExampleSpec.hs:6: ║-║ | ║-║ | spec = it "should fail" $ expectPositive (-1) ║-║ | ^^^^^^^^^^^^^^ ║-║ ║-║ ./ExampleSpec.hs:9: ║-║ | ║-║ | expectPositive = expectGT 0 ║-║ | ^^^^^^^^ ║-║ ║-║ ./ExampleSpec.hs:12: ║-║ | ║-║ | expectGT x actual = actual `shouldSatisfy` P.gt x ║-║ | ^^^^^^^^^^^^^^^ ║-║ ║-║ -1 ≯ 0 ║-╚══════════════════════════════════════════════════════════════════════════════╝+╭── should fail: FAIL+│ ./ExampleSpec.hs:6:+│ │+│ │ spec = it "should fail" $ expectPositive (-1)+│ │ ^^^^^^^^^^^^^^+│ +│ ./ExampleSpec.hs:9:+│ │+│ │ expectPositive = expectGT 0+│ │ ^^^^^^^^+│ +│ ./ExampleSpec.hs:12:+│ │+│ │ expectGT x actual = actual `shouldSatisfy` P.gt x+│ │ ^^^^^^^^^^^^^^^+│ +│ -1 ≯ 0+╰─────────────────────────────────────────────────────────────────────────────── ``` ## shows helpful error on pattern match fail ``` ./ExampleSpec.hs- should fail: ERROR-╔══════════════════════════════════════════════════════════════════════════════╗-║ ExampleSpec.hs:7: ║-║ | ║-║ | Just x <- pure Nothing ║-║ | ^^^^^^ ║-║ ║-║ Pattern match failure in 'do' block ║-╚══════════════════════════════════════════════════════════════════════════════╝+╭── should fail: ERROR+│ ExampleSpec.hs:7:+│ │+│ │ Just x <- pure Nothing+│ │ ^^^^^^+│ +│ Pattern match failure in 'do' block+╰─────────────────────────────────────────────────────────────────────────────── ``` ## shows unrecognized exceptions ``` ./ExampleSpec.hs- should fail: ERROR-╔══════════════════════════════════════════════════════════════════════════════╗-║ Got exception of type `IOException`: ║-║ unknown-file.txt: openFile: does not exist (No such file or directory) ║-╚══════════════════════════════════════════════════════════════════════════════╝+╭── should fail: ERROR+│ Got exception of type `IOException`:+│ unknown-file.txt: openFile: does not exist (No such file or directory)+╰─────────────────────────────────────────────────────────────────────────────── ```
test/Skeletest/__snapshots__/PredicateSpec.snap.md view
@@ -252,21 +252,20 @@ ``` ./ExampleSpec.hs- should error: FAIL-╔══════════════════════════════════════════════════════════════════════════════╗-║ ./ExampleSpec.hs:9: ║-║ | ║-║ | User "alice" `shouldSatisfy` P.con User{name = P.eq ""} ║-║ | ^^^^^^^^^^^^^^^ ║-║ ║-║ "alice" ≠ [] ║-║ ║-║ Expected: ║-║ matches User{name = (= [])} ║-║ ║-║ Got: ║-║ User "alice" ║-╚══════════════════════════════════════════════════════════════════════════════╝+╭── should error: FAIL+│ ./ExampleSpec.hs:9:+│ │+│ │ User "alice" `shouldSatisfy` P.con User{name = P.eq ""}+│ │ ^^^^^^^^^^^^^^^+│ +│ "alice" ≠ []+│ +│ Expected:+│ matches User{name = (= [])}+│ +│ Got:+│ User "alice"+╰─────────────────────────────────────────────────────────────────────────────── ``` ## Data types / list / shows helpful failure messages
test/Skeletest/__snapshots__/PropSpec.snap.md view
@@ -4,51 +4,46 @@ ``` ./ExampleSpec.hs- is isomorphic: FAIL-╔══════════════════════════════════════════════════════════════════════════════╗-║ ./ExampleSpec.hs:10: ║-║ | ║-║ | (read . show) P.=== (+ 1) `shouldSatisfy` P.isoWith (Gen.int $ Range.l ║-║ inear 0 10) ║-║ | ^^^^^^^^^^^^^^^ ║-║ ║-║ Failed after 1 tests. ║-║ Rerun with --seed=0:0 to reproduce. ║-║ ║-║ ./ExampleSpec.hs:10:47 ==> 0 ║-║ ║-║ 0 ≠ 1 ║-║ where ║-║ 0 = (read . show) 0 ║-║ 1 = (+ 1) 0 ║-╚══════════════════════════════════════════════════════════════════════════════╝- is not isomorphic: FAIL-╔══════════════════════════════════════════════════════════════════════════════╗-║ ./ExampleSpec.hs:12: ║-║ | ║-║ | (read . show) P.=== id `shouldNotSatisfy` P.isoWith (Gen.int $ Range.l ║-║ inear 0 10) ║-║ | ^^^^^^^^^^^^^^^^^^ ║-║ ║-║ Failed after 1 tests. ║-║ Rerun with --seed=0:0 to reproduce. ║-║ ║-║ ./ExampleSpec.hs:12:47 ==> 0 ║-║ ║-║ 0 = 0 ║-║ where ║-║ 0 = (read . show) 0 ║-║ 0 = id 0 ║-╚══════════════════════════════════════════════════════════════════════════════╝+╭── is isomorphic: FAIL+│ ./ExampleSpec.hs:10:+│ │+│ │ (read . show) P.=== (+ 1) `shouldSatisfy` P.isoWith (Gen.int $ Range.linear 0 10)+│ │ ^^^^^^^^^^^^^^^+│ +│ Failed after 1 tests.+│ Rerun with --seed=0:0 to reproduce.+│ +│ ./ExampleSpec.hs:10:47 ==> 0+│ +│ 0 ≠ 1+│ where+│ 0 = (read . show) 0+│ 1 = (+ 1) 0+╰────────────────────────────────────────────────────────────────────────────────────────+╭── is not isomorphic: FAIL+│ ./ExampleSpec.hs:12:+│ │+│ │ (read . show) P.=== id `shouldNotSatisfy` P.isoWith (Gen.int $ Range.linear 0 10)+│ │ ^^^^^^^^^^^^^^^^^^+│ +│ Failed after 1 tests.+│ Rerun with --seed=0:0 to reproduce.+│ +│ ./ExampleSpec.hs:12:47 ==> 0+│ +│ 0 = 0+│ where+│ 0 = (read . show) 0+│ 0 = id 0+╰──────────────────────────────────────────────────────────────────────────────────────── ``` ## setDiscardLimit / sets discard limit ``` ./ExampleSpec.hs- discards: FAIL-╔══════════════════════════════════════════════════════════════════════════════╗-║ Gave up after 10 discards. ║-║ Passed 0 tests. ║-╚══════════════════════════════════════════════════════════════════════════════╝+╭── discards: FAIL+│ Gave up after 10 discards.+│ Passed 0 tests.+╰─────────────────────────────────────────────────────────────────────────────── ```