{-# LANGUAGE NumericUnderscores #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE RecordWildCards #-}

module Test.Syd.Output.Terse where

import Data.Text (Text)
import qualified Data.Text as T
import qualified Data.Text.Lazy.Builder as Text
import GHC.Stack
import Safe
import Test.Syd.OptParse
import Test.Syd.Output.Common
import Test.Syd.Run
import Test.Syd.SpecDef
import Test.Syd.SpecForest
import Text.Colour
import Text.Printf

-- | Render a terse report
renderTerseSummary :: Settings -> Timed ResultForest -> Text.Builder
renderTerseSummary :: Settings -> Timed ResultForest -> Builder
renderTerseSummary Settings
settings Timed ResultForest
trf =
  [Builder] -> Builder
forall a. Monoid a => [a] -> a
mconcat ([Builder] -> Builder) -> [Builder] -> Builder
forall a b. (a -> b) -> a -> b
$
    ([Chunk] -> Builder) -> [[Chunk]] -> [Builder]
forall a b. (a -> b) -> [a] -> [b]
map
      (\[Chunk]
line -> TerminalCapabilities -> [Chunk] -> Builder
forall (f :: * -> *).
Foldable f =>
TerminalCapabilities -> f Chunk -> Builder
renderChunksBuilder (Settings -> TerminalCapabilities
settingTerminalCapabilities Settings
settings) [Chunk]
line Builder -> Builder -> Builder
forall a. Semigroup a => a -> a -> a
<> Builder
"\n")
      (Settings -> Timed ResultForest -> [[Chunk]]
outputTerseSummary Settings
settings Timed ResultForest
trf)

-- | Output the terse report as chunks.
outputTerseSummary :: Settings -> Timed ResultForest -> [[Chunk]]
outputTerseSummary :: Settings -> Timed ResultForest -> [[Chunk]]
outputTerseSummary Settings
settings Timed ResultForest
trf =
  let rf :: ResultForest
rf = Timed ResultForest -> ResultForest
forall a. Timed a -> a
timedValue Timed ResultForest
trf
      failures :: [([Text], TDef (Timed TestRunReport))]
failures = (([Text], TDef (Timed TestRunReport)) -> Bool)
-> [([Text], TDef (Timed TestRunReport))]
-> [([Text], TDef (Timed TestRunReport))]
forall a. (a -> Bool) -> [a] -> [a]
filter (Settings -> TestRunReport -> Bool
testRunReportFailed Settings
settings (TestRunReport -> Bool)
-> (([Text], TDef (Timed TestRunReport)) -> TestRunReport)
-> ([Text], TDef (Timed TestRunReport))
-> Bool
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Timed TestRunReport -> TestRunReport
forall a. Timed a -> a
timedValue (Timed TestRunReport -> TestRunReport)
-> (([Text], TDef (Timed TestRunReport)) -> Timed TestRunReport)
-> ([Text], TDef (Timed TestRunReport))
-> TestRunReport
forall b c a. (b -> c) -> (a -> b) -> a -> c
. TDef (Timed TestRunReport) -> Timed TestRunReport
forall value. TDef value -> value
testDefVal (TDef (Timed TestRunReport) -> Timed TestRunReport)
-> (([Text], TDef (Timed TestRunReport))
    -> TDef (Timed TestRunReport))
-> ([Text], TDef (Timed TestRunReport))
-> Timed TestRunReport
forall b c a. (b -> c) -> (a -> b) -> a -> c
. ([Text], TDef (Timed TestRunReport)) -> TDef (Timed TestRunReport)
forall a b. (a, b) -> b
snd) ([([Text], TDef (Timed TestRunReport))]
 -> [([Text], TDef (Timed TestRunReport))])
-> [([Text], TDef (Timed TestRunReport))]
-> [([Text], TDef (Timed TestRunReport))]
forall a b. (a -> b) -> a -> b
$ ResultForest -> [([Text], TDef (Timed TestRunReport))]
forall a. SpecForest a -> [([Text], a)]
flattenSpecForest ResultForest
rf
      stats :: TestSuiteStats
stats = Settings -> ResultForest -> TestSuiteStats
computeTestSuiteStats Settings
settings ResultForest
rf
      totalTimeSeconds :: Double
totalTimeSeconds = Word64 -> Double
forall a b. (Integral a, Num b) => a -> b
fromIntegral (Timed ResultForest -> Word64
forall a. Timed a -> Word64
timedTime Timed ResultForest
trf) Double -> Double -> Double
forall a. Fractional a => a -> a -> a
/ Double
1_000_000_000 :: Double
   in [[[Chunk]]] -> [[Chunk]]
forall (t :: * -> *) a. Foldable t => t [a] -> [a]
concat
        [ (([Text], TDef (Timed TestRunReport)) -> [[Chunk]])
-> [([Text], TDef (Timed TestRunReport))] -> [[Chunk]]
forall (t :: * -> *) a b. Foldable t => (a -> [b]) -> t a -> [b]
concatMap (Settings -> ([Text], TDef (Timed TestRunReport)) -> [[Chunk]]
outputTerseFailure Settings
settings) [([Text], TDef (Timed TestRunReport))]
failures,
          [TestSuiteStats -> Double -> [Chunk]
outputTerseStats TestSuiteStats
stats Double
totalTimeSeconds]
        ]

-- | Output a single failure in terse format.
outputTerseFailure :: Settings -> ([Text], TDef (Timed TestRunReport)) -> [[Chunk]]
outputTerseFailure :: Settings -> ([Text], TDef (Timed TestRunReport)) -> [[Chunk]]
outputTerseFailure Settings
_settings ([Text]
ts, TDef Timed TestRunReport
timed CallStack
cs) =
  let testRunReport :: TestRunReport
testRunReport = Timed TestRunReport -> TestRunReport
forall a. Timed a -> a
timedValue Timed TestRunReport
timed
      TestRunResult {[String]
Maybe String
Maybe Word
Maybe (Map String Int)
Maybe (Map String (Map String Int))
Maybe (Map [String] Int)
Maybe SomeException
Maybe GoldenCase
TestStatus
testRunResultStatus :: TestStatus
testRunResultException :: Maybe SomeException
testRunResultNumTests :: Maybe Word
testRunResultNumShrinks :: Maybe Word
testRunResultFailingInputs :: [String]
testRunResultLabels :: Maybe (Map [String] Int)
testRunResultClasses :: Maybe (Map String Int)
testRunResultTables :: Maybe (Map String (Map String Int))
testRunResultGoldenCase :: Maybe GoldenCase
testRunResultExtraInfo :: Maybe String
testRunResultExtraInfo :: TestRunResult -> Maybe String
testRunResultGoldenCase :: TestRunResult -> Maybe GoldenCase
testRunResultTables :: TestRunResult -> Maybe (Map String (Map String Int))
testRunResultClasses :: TestRunResult -> Maybe (Map String Int)
testRunResultLabels :: TestRunResult -> Maybe (Map [String] Int)
testRunResultFailingInputs :: TestRunResult -> [String]
testRunResultNumShrinks :: TestRunResult -> Maybe Word
testRunResultNumTests :: TestRunResult -> Maybe Word
testRunResultException :: TestRunResult -> Maybe SomeException
testRunResultStatus :: TestRunResult -> TestStatus
..} = TestRunReport -> TestRunResult
testRunReportReportedRun TestRunReport
testRunReport
      location :: String
location = case [(String, SrcLoc)] -> Maybe (String, SrcLoc)
forall a. [a] -> Maybe a
headMay ([(String, SrcLoc)] -> Maybe (String, SrcLoc))
-> [(String, SrcLoc)] -> Maybe (String, SrcLoc)
forall a b. (a -> b) -> a -> b
$ CallStack -> [(String, SrcLoc)]
getCallStack CallStack
cs of
        Maybe (String, SrcLoc)
Nothing -> String
"Unknown location"
        Just (String
_, SrcLoc {Int
String
srcLocPackage :: String
srcLocModule :: String
srcLocFile :: String
srcLocStartLine :: Int
srcLocStartCol :: Int
srcLocEndLine :: Int
srcLocEndCol :: Int
srcLocEndCol :: SrcLoc -> Int
srcLocEndLine :: SrcLoc -> Int
srcLocStartCol :: SrcLoc -> Int
srcLocStartLine :: SrcLoc -> Int
srcLocFile :: SrcLoc -> String
srcLocModule :: SrcLoc -> String
srcLocPackage :: SrcLoc -> String
..}) -> [String] -> String
forall (t :: * -> *) a. Foldable t => t [a] -> [a]
concat [String
srcLocFile, String
":", Int -> String
forall a. Show a => a -> String
show Int
srcLocStartLine]
      testPath :: Text
testPath = Text -> [Text] -> Text
T.intercalate Text
"." [Text]
ts
   in [[[Chunk]]] -> [[Chunk]]
forall (t :: * -> *) a. Foldable t => t [a] -> [a]
concat
        [ [ [ Text -> Chunk
chunk Text
"FAIL ",
              Text -> Chunk
chunk (String -> Text
T.pack String
location),
              Text -> Chunk
chunk Text
" ",
              Text -> Chunk
chunk Text
testPath
            ]
          ],
          ([Chunk] -> [Chunk]) -> [[Chunk]] -> [[Chunk]]
forall a b. (a -> b) -> [a] -> [b]
map
            (\[Chunk]
l -> if [Chunk] -> Bool
forall a. [a] -> Bool
forall (t :: * -> *) a. Foldable t => t a -> Bool
null [Chunk]
l then [Chunk]
l else Chunk
padding Chunk -> [Chunk] -> [Chunk]
forall a. a -> [a] -> [a]
: [Chunk]
l)
            ([[Chunk]]
-> (SomeException -> [[Chunk]]) -> Maybe SomeException -> [[Chunk]]
forall b a. b -> (a -> b) -> Maybe a -> b
maybe [] SomeException -> [[Chunk]]
outputSomeException Maybe SomeException
testRunResultException),
          [[Text -> Chunk
chunk Text
""]]
        ]

-- | Output the summary line in terse format.
--
-- Format: Summary: X failed, Y passed, Z pending (Ns)
outputTerseStats :: TestSuiteStats -> Double -> [Chunk]
outputTerseStats :: TestSuiteStats -> Double -> [Chunk]
outputTerseStats TestSuiteStats {Word
Word64
testSuiteStatSuccesses :: Word
testSuiteStatExamples :: Word
testSuiteStatFailures :: Word
testSuiteStatFlakyTests :: Word
testSuiteStatPending :: Word
testSuiteStatSumTime :: Word64
testSuiteStatSumTime :: TestSuiteStats -> Word64
testSuiteStatPending :: TestSuiteStats -> Word
testSuiteStatFlakyTests :: TestSuiteStats -> Word
testSuiteStatFailures :: TestSuiteStats -> Word
testSuiteStatExamples :: TestSuiteStats -> Word
testSuiteStatSuccesses :: TestSuiteStats -> Word
..} Double
totalTimeSeconds =
  [[Chunk]] -> [Chunk]
forall (t :: * -> *) a. Foldable t => t [a] -> [a]
concat ([[Chunk]] -> [Chunk]) -> [[Chunk]] -> [Chunk]
forall a b. (a -> b) -> a -> b
$
    [[[Chunk]]] -> [[Chunk]]
forall (t :: * -> *) a. Foldable t => t [a] -> [a]
concat
      [ [[Chunk
padding]],
        [ [ Text -> Chunk
chunk Text
"Passed: ",
            ( if Word
testSuiteStatSuccesses Word -> Word -> Bool
forall a. Ord a => a -> a -> Bool
<= Word
0
                then Colour -> Chunk -> Chunk
fore Colour
red
                else Colour -> Chunk -> Chunk
fore Colour
green
            )
              (Chunk -> Chunk) -> Chunk -> Chunk
forall a b. (a -> b) -> a -> b
$ Text -> Chunk
chunk (String -> Text
T.pack (Word -> String
forall a. Show a => a -> String
show Word
testSuiteStatSuccesses))
          ],
          [ Text -> Chunk
chunk Text
", Failed: ",
            ( if Word
testSuiteStatFailures Word -> Word -> Bool
forall a. Ord a => a -> a -> Bool
> Word
0
                then Colour -> Chunk -> Chunk
fore Colour
red
                else Colour -> Chunk -> Chunk
fore Colour
green
            )
              (Chunk -> Chunk) -> Chunk -> Chunk
forall a b. (a -> b) -> a -> b
$ Text -> Chunk
chunk (String -> Text
T.pack (Word -> String
forall a. Show a => a -> String
show Word
testSuiteStatFailures))
          ]
        ],
        [ [ Text -> Chunk
chunk Text
", Flaky: ",
            Colour -> Chunk -> Chunk
fore Colour
red (Chunk -> Chunk) -> Chunk -> Chunk
forall a b. (a -> b) -> a -> b
$ Text -> Chunk
chunk (String -> Text
T.pack (Word -> String
forall a. Show a => a -> String
show Word
testSuiteStatFlakyTests))
          ]
          | Word
testSuiteStatFlakyTests Word -> Word -> Bool
forall a. Ord a => a -> a -> Bool
> Word
0
        ],
        [ [ Text -> Chunk
chunk Text
", Pending: ",
            Colour -> Chunk -> Chunk
fore Colour
magenta (Chunk -> Chunk) -> Chunk -> Chunk
forall a b. (a -> b) -> a -> b
$ Text -> Chunk
chunk (String -> Text
T.pack (Word -> String
forall a. Show a => a -> String
show Word
testSuiteStatPending))
          ]
          | Word
testSuiteStatPending Word -> Word -> Bool
forall a. Ord a => a -> a -> Bool
> Word
0
        ],
        [ [ Colour -> Chunk -> Chunk
fore Colour
yellow (Chunk -> Chunk) -> Chunk -> Chunk
forall a b. (a -> b) -> a -> b
$ Text -> Chunk
chunk (Text -> Chunk) -> Text -> Chunk
forall a b. (a -> b) -> a -> b
$ String -> Text
T.pack (String -> Double -> String
forall r. PrintfType r => String -> r
printf String
" (%0.2f s)" Double
totalTimeSeconds)
          ]
        ]
      ]