Just realized this overflow scheme isn't going to work. Checking in

progress for posterity.
This commit is contained in:
Tom McLaughlin 2021-02-26 17:22:36 -08:00
parent 990757846e
commit f48ddeecc8
2 changed files with 61 additions and 36 deletions

View File

@ -5,6 +5,7 @@ module Main where
import Control.Concurrent import Control.Concurrent
import Control.Monad.IO.Class import Control.Monad.IO.Class
import Data.Time import Data.Time
import GHC.Stack
import Test.Sandwich import Test.Sandwich
import Test.Sandwich.Formatters.Print import Test.Sandwich.Formatters.Print
import Test.Sandwich.Formatters.Slack import Test.Sandwich.Formatters.Slack
@ -14,12 +15,12 @@ simple :: TopSpec
simple = parallel $ do simple = parallel $ do
it "does the thing 1" sleepThenSucceed it "does the thing 1" sleepThenSucceed
it "does the thing 2" sleepThenSucceed it "does the thing 2" sleepThenSucceed
it "does the thing 3" sleepThenFail it "does the thing 3" deepSleepThenFail
describe "should happen sequentially" $ do describe "should happen sequentially" $ do
it "sequential 1" sleepThenSucceed it "sequential 1" sleepThenSucceed
it "sequential 2" sleepThenFail it "sequential 2" sleepThenFail
it "sequential 3" sleepThenSucceed it "sequential 3" sleepThenSucceed
it "does the thing 4" sleepThenFail it "does the thing 4" deepSleepThenFail
it "does the thing 5" sleepThenSucceed it "does the thing 5" sleepThenSucceed
it "does the thing 6" sleepThenSucceed it "does the thing 6" sleepThenSucceed
@ -29,9 +30,11 @@ slackFormatter = defaultSlackFormatter {
, slackFormatterTopMessage = Just "Top message" , slackFormatterTopMessage = Just "Top message"
, slackFormatterChannel = "test-channel" , slackFormatterChannel = "test-channel"
, slackFormatterMaxFailures = Nothing -- Just 2 , slackFormatterMaxFailures = Just 2
, slackFormatterMaxFailureReasonLines = Just 0 , slackFormatterMaxFailureReasonLines = Just 0
, slackFormatterMaxCallStackLines = Just 0 , slackFormatterMaxFailureReasonOverflowLines = Just 10
, slackFormatterMaxCallStackLines = Just 1
, slackFormatterMaxCallStackOverflowLines = Just 10
, slackFormatterVisibilityThreshold = Just 50 , slackFormatterVisibilityThreshold = Just 50
} }
@ -49,17 +52,27 @@ main = runSandwich options simple
-- * Util -- * Util
sleepThenSucceed :: ExampleM context () sleepThenSucceed :: (HasCallStack) => ExampleM context ()
sleepThenSucceed = do sleepThenSucceed = do
-- liftIO $ threadDelay (2 * 10^1) -- liftIO $ threadDelay (2 * 10^1)
liftIO $ threadDelay (2 * 10^5) liftIO $ threadDelay (2 * 10^5)
-- liftIO $ threadDelay (1 * 10^6) -- liftIO $ threadDelay (1 * 10^6)
-- liftIO $ threadDelay (3 * 10^6) -- liftIO $ threadDelay (3 * 10^6)
sleepThenFail :: ExampleM context () sleepThenFail :: (HasCallStack) => ExampleM context ()
sleepThenFail = do sleepThenFail = do
-- liftIO $ threadDelay (2 * 10^1) -- liftIO $ threadDelay (2 * 10^1)
liftIO $ threadDelay (2 * 10^5) liftIO $ threadDelay (2 * 10^5)
-- liftIO $ threadDelay (1 * 10^6) -- liftIO $ threadDelay (1 * 10^6)
-- liftIO $ threadDelay (3 * 10^6) -- liftIO $ threadDelay (3 * 10^6)
2 `shouldBe` 3 2 `shouldBe` 3
deepSleepThenFail :: (HasCallStack) => ExampleM context ()
deepSleepThenFail = deepSleepThenFail2
deepSleepThenFail2 :: (HasCallStack) => ExampleM context ()
deepSleepThenFail2 = deepSleepThenFail3
deepSleepThenFail3 :: (HasCallStack) => ExampleM context ()
deepSleepThenFail3 = sleepThenFail

View File

@ -14,9 +14,13 @@ module Test.Sandwich.Formatters.Slack (
, slackFormatterSlackConfig , slackFormatterSlackConfig
, slackFormatterTopMessage , slackFormatterTopMessage
, slackFormatterChannel , slackFormatterChannel
, slackFormatterMaxFailures , slackFormatterMaxFailures
, slackFormatterMaxFailureReasonLines , slackFormatterMaxFailureReasonLines
, slackFormatterMaxFailureReasonOverflowLines
, slackFormatterMaxCallStackLines , slackFormatterMaxCallStackLines
, slackFormatterMaxCallStackOverflowLines
, slackFormatterVisibilityThreshold , slackFormatterVisibilityThreshold
, SlackFormatterShowCallStacks(..) , SlackFormatterShowCallStacks(..)
@ -65,10 +69,18 @@ data SlackFormatter = SlackFormatter {
, slackFormatterMaxFailureReasonLines :: Maybe Int , slackFormatterMaxFailureReasonLines :: Maybe Int
-- ^ Maximum number of lines to devote to showing the failure reason underneath a failure. -- ^ Maximum number of lines to devote to showing the failure reason underneath a failure.
-- Set to 'Just 0' to disable showing failure reasons. -- Set to 'Just 0' to disable showing failure reasons.
, slackFormatterMaxFailureReasonOverflowLines :: Maybe Int
-- ^ Maximum number of lines to allow to spill into an overflow section, which requires
-- clicking a button to view.
-- Set to 'Just 0' to disable overflow for failure reasons.
, slackFormatterMaxCallStackLines :: Maybe Int , slackFormatterMaxCallStackLines :: Maybe Int
-- ^ Maximum number of lines to devote to showing the call stack underneath a failure. -- ^ Maximum number of lines to devote to showing the call stack underneath a failure.
-- Set to 'Just 0' to disable showing call stacks. -- Set to 'Just 0' to disable showing call stacks.
, slackFormatterMaxCallStackOverflowLines :: Maybe Int
-- ^ Maximum number of lines to allow to spill into an overflow section, which requires
-- clicking a button to view.
-- Set to 'Just 0' to disable overflow for callstacks.
, slackFormatterVisibilityThreshold :: Maybe Int , slackFormatterVisibilityThreshold :: Maybe Int
-- ^ If present, filter the headings on failures to only include nodes whose visibility -- ^ If present, filter the headings on failures to only include nodes whose visibility
@ -83,7 +95,9 @@ defaultSlackFormatter = SlackFormatter {
, slackFormatterMaxFailures = Just 30 , slackFormatterMaxFailures = Just 30
, slackFormatterMaxFailureReasonLines = Just 5 , slackFormatterMaxFailureReasonLines = Just 5
, slackFormatterMaxFailureReasonOverflowLines = Just 10
, slackFormatterMaxCallStackLines = Just 5 , slackFormatterMaxCallStackLines = Just 5
, slackFormatterMaxCallStackOverflowLines = Just 10
, slackFormatterVisibilityThreshold = Nothing , slackFormatterVisibilityThreshold = Nothing
@ -122,7 +136,7 @@ runApp sf@(SlackFormatter {..}) rts _bc = do
let pbi' = publishTree sf idToLabelAndVisibilityThreshold (diffUTCTime now startTime) newFixedTree let pbi' = publishTree sf idToLabelAndVisibilityThreshold (diffUTCTime now startTime) newFixedTree
tryAny (liftIO $ updateProgressBar slackFormatterSlackConfig pb pbi') >>= \case tryAny (liftIO $ updateProgressBar slackFormatterSlackConfig pb pbi') >>= \case
Left err -> logError [i|Error updating progress bar: '#{err}'|] Left err -> logError [i|Error updating progress bar: '#{err}'|]
Right (Left err) -> logError [i|Inner error updating progress bar: '#{err}'|] Right (Left err) -> logError [i|Inner error updating progress bar: '#{err}'. Blocks were '#{A.encode $ progressBarInfoBlocks pbi'}'|]
Right (Right ()) -> return () Right (Right ()) -> return ()
if | allIsDone newFixedTree -> do if | allIsDone newFixedTree -> do
@ -135,33 +149,34 @@ runApp sf@(SlackFormatter {..}) rts _bc = do
publishTree sf idToLabelAndVisibilityThreshold elapsed tree = pbi publishTree sf idToLabelAndVisibilityThreshold elapsed tree = pbi
where where
pbi = ProgressBarInfo { progressBarInfoTopMessage = T.pack <$> (slackFormatterTopMessage sf) pbi = ProgressBarInfo {
, progressBarInfoBottomMessage = Just fullBottomMessage progressBarInfoTopMessage = T.pack <$> (slackFormatterTopMessage sf)
, progressBarInfoSize = Just (100.0 * (fromIntegral (succeeded + pending + failed) / (fromIntegral total))) , progressBarInfoBottomMessage = Just fullBottomMessage
, progressBarInfoAttachments = Nothing , progressBarInfoSize = Just (100.0 * (fromIntegral (succeeded + pending + failed) / (fromIntegral total)))
, progressBarInfoBlocks = Just $ case slackFormatterMaxFailures sf of , progressBarInfoAttachments = Nothing
Nothing -> mconcat blocks , progressBarInfoBlocks = Just $ case slackFormatterMaxFailures sf of
Just n -> case L.splitAt n blocks of Nothing -> mconcat blocks
(xs, []) -> mconcat xs Just n -> case L.splitAt n blocks of
(xs, rest) -> mconcat xs <> [extraFailuresBlock (L.length rest)] (xs, []) -> mconcat xs
} (xs, rest) -> mconcat xs <> [extraFailuresBlock (L.length rest)]
}
runningMessage = headMay $ L.sort $ catMaybes $ concatMap (extractValues (\node -> if isRunningItBlock node then Just $ runTreeLabel $ runNodeCommon node else Nothing)) tree runningMessage = headMay $ L.sort $ catMaybes $ flip concatMap tree $
extractValues (\node -> if isRunningItBlock node then Just $ runTreeLabel $ runNodeCommon node else Nothing)
fullBottomMessage = case runningMessage of fullBottomMessage = case runningMessage of
Nothing -> bottomMessage Nothing -> bottomMessage
Just t -> T.pack t <> "\n" <> bottomMessage Just t -> T.pack t <> "\n" <> bottomMessage
bottomMessage = [i|#{succeeded} succeeded, #{failed} failed, #{pending} pending, #{totalRunningTests} running of #{total} (#{formatNominalDiffTime elapsed} elapsed)|] bottomMessage = [i|#{succeeded} succeeded, #{failed} failed, #{pending} pending, #{totalRunningTests} running of #{total} (#{formatNominalDiffTime elapsed} elapsed)|]
blocks = flip concatMap tree $ extractValuesControlRecurse $ \case blocks = catMaybes $ flip concatMap tree $ extractValuesControlRecurse $ \case
-- Recurse into grouping nodes, because their failures are actually just derived from child failures -- Recurse into grouping nodes, because their failures are actually just derived from child failures
RunNodeDescribe {} -> (True, []) RunNodeDescribe {} -> (True, Nothing)
RunNodeParallel {} -> (True, []) RunNodeParallel {} -> (True, Nothing)
((runTreeStatus . runNodeCommon) -> ((runTreeStatus . runNodeCommon) -> (Done {statusResult=(Failure (Pending {}))})) -> (False, Nothing)
(Done {statusResult=(Failure (Pending {}))})) -> (False, [])
node@((runTreeStatus . runNodeCommon) -> (Done {statusResult=(Failure reason)})) | isFailedBlock node -> node@((runTreeStatus . runNodeCommon) -> (Done {statusResult=(Failure reason)})) | isFailedBlock node ->
(False, singleFailureBlocks sf idToLabelAndVisibilityThreshold node reason) (False, Just $ singleFailureBlocks sf idToLabelAndVisibilityThreshold node reason)
_ -> (True, []) _ -> (True, Nothing)
total = countWhere isItBlock tree total = countWhere isItBlock tree
succeeded = countWhere isSuccessItBlock tree succeeded = countWhere isSuccessItBlock tree
@ -178,21 +193,13 @@ singleFailureBlocks sf idToLabelAndVisibilityThreshold node reason = catMaybes [
, case (markdownLinesToShow, overflowMarkdownLines) of , case (markdownLinesToShow, overflowMarkdownLines) of
([], _) -> Nothing ([], _) -> Nothing
(toShow, []) -> Just $ markdownSectionWithLines toShow (toShow, []) -> Just $ markdownSectionWithLines toShow
(toShow, overflow) -> Just $ A.object [ (toShow, overflow) -> Just $ sectionsWithTextAndOverflow (markdownBlockWithLines toShow) [markdownBlockWithLines overflow]
("type", A.String "section")
, ("text", markdownBlockWithLines toShow)
, ("accessory", A.object [("type", "overflow"), ("options", A.Array (V.fromList [markdownBlockWithLines overflow]))])
]
-- Callstack info -- Callstack info
, case (callStackLinesToShow, overflowCallStackLines) of , case (callStackLinesToShow, overflowCallStackLines) of
([], _) -> Nothing ([], _) -> Nothing
(toShow, []) -> Just $ markdownSectionWithLines toShow (toShow, []) -> Just $ markdownSectionWithLines toShow
(toShow, overflow) -> Just $ A.object [ (toShow, overflow) -> Just $ sectionsWithTextAndOverflow (markdownBlockWithLines toShow) [markdownBlockWithLines overflow]
("type", A.String "section")
, ("text", markdownBlockWithLines toShow)
, ("accessory", A.object [("type", "overflow"), ("options", A.Array (V.fromList [markdownBlockWithLines overflow]))])
]
] ]
where where
allMarkdownLines = T.lines $ toMarkdown reason allMarkdownLines = T.lines $ toMarkdown reason
@ -216,12 +223,17 @@ singleFailureBlocks sf idToLabelAndVisibilityThreshold node reason = catMaybes [
_ -> Just l _ -> Just l
label = T.intercalate ", " $ mapMaybe filterFn $ toList $ runTreeAncestors $ runNodeCommon node label = T.intercalate ", " $ mapMaybe filterFn $ toList $ runTreeAncestors $ runNodeCommon node
extraFailuresBlock numExtraFailures = markdownSectionWithLines [[i|+ #{numExtraFailures} more|]] extraFailuresBlock numExtraFailures = markdownSectionWithLines [[i|+ #{numExtraFailures} more failure|]]
markdownBlockWithLines ls = A.object [("type", A.String "mrkdwn"), ("text", A.String $ T.unlines ls)] markdownBlockWithLines ls = A.object [("type", A.String "mrkdwn"), ("text", A.String $ T.unlines ls)]
markdownSectionWithLines ls = A.object [("type", A.String "section"), ("text", markdownBlockWithLines ls)] markdownSectionWithLines ls = A.object [("type", A.String "section"), ("text", markdownBlockWithLines ls)]
sectionsWithTextAndOverflow text overflow = A.object [
("type", A.String "section")
, ("text", text)
, ("accessory", A.object [("type", "overflow"), ("options", A.Array (V.fromList overflow))])
]
allIsDone :: [RunNodeFixed context] -> Bool allIsDone :: [RunNodeFixed context] -> Bool
allIsDone = all (isDone . runTreeStatus . runNodeCommon) allIsDone = all (isDone . runTreeStatus . runNodeCommon)