{-# LANGUAGE LambdaCase #-} {-# LANGUAGE ViewPatterns #-} module Main where import Data.TreeDiff import Data.TreeDiff.Golden import Data.TreeDiff.Instances.Caliper () import Data.Void import System.FilePath import Test.Tasty import Test.Tasty.Golden.Advanced import Data.Text qualified as T import Data.Text.IO qualified as TIO import Data.Time.LocalTime import Text.Megaparsec import Caliper.Cli import Caliper.Syntax main :: IO () main = defaultMain tests type ParseResult = Either (ParseErrorBundle T.Text Void) tests :: TestTree tests = testGroup "Tests" [ parserTests , resolutionTests , queryTests , histogramTests ] parserTests :: TestTree parserTests = testGroup "Parser" [ parserTestOk "ok_001" "ok_001.clp" , parserTestOk "ok_002" "ok_002.clp" , parserTestOk "ok_003" "ok_003.clp" , parserTestOk "ok_004" "ok_004.clp" , parserTestOk "ok_005" "ok_005.clp" , parserTestOk "ok_006" "ok_006.clp" , parserTestOk "ok_007" "ok_007.clp" , parserTestOk "ok_008" "ok_008.clp" , parserTestOk "ok_009" "ok_009.clp" , parserTestOk "ok_010" "ok_010.clp" , parserTestOk "ok_012" "ok_012.clp" , rawParserTestErr "err_003" "err_003.clp" , rawParserTestErr "err_005" "err_005.clp" , parserTestOk "ok_013" "ok_013.clp" , parserTestOk "ok_014" "ok_014.clp" , rawParserTestOk "ok_014_raw" "ok_014.clp" , parserTestOk "ok_015" "ok_015.clp" ] -- Doesn't resolve rawParserTestOk :: String -> FilePath -> TestTree rawParserTestOk name fp = mkGoldenTestOk (\_src -> pure) name ("parser" fp) rawParserTestErr :: String -> FilePath -> TestTree rawParserTestErr name fp = mkGoldenTestErr name ("parser" fp) -- Resolves parserTestOk :: String -> FilePath -> TestTree parserTestOk name fp = mkGoldenTestOk (\_src -> pure . resolve) name ("parser" fp) resolutionTests :: TestTree resolutionTests = testGroup "Resolution" [ resolveTestErr "err_001" "err_001.clp" , resolveTestErr "err_002" "err_002.clp" , resolveTestErr "err_004" "err_004.clp" ] -- Parses but shouldn't resolve because of semantic errors resolveTestErr :: String -> FilePath -> TestTree resolveTestErr name fp = mkGoldenTestOk resolveShouldError name ("parser" fp) where resolveShouldError :: T.Text -> RawAst -> IO T.Text resolveShouldError src (resolve -> (errs, _)) = pure $ mconcat $ map (resolutionErrorPretty src) errs histogramTests :: TestTree histogramTests = testGroup "histogram" [ histogramTest groupEntriesByDay "001_groupByDay" "001.clp" , histogramTest (fmap histogramLine . groupEntriesByDay) "001_groupByDay_cli" "001.clp" ] histogramTest :: ToExpr expr => ([ResolvedEntry] -> expr) -> String -> FilePath -> TestTree histogramTest f name fp = mkGoldenTestOk (\_src -> pure . fmap f . resolve) name ("histogram" fp) queryTests :: TestTree queryTests = testGroup "Query" [ queryTest [HasKeyValue "foo" "bar"] "001_foobar" "001.clp" , queryTest [Negate $ HasKeyValue "foo" "bar"] "001_notfoobar" "001.clp" , beforeFilterTest (read "2025-10-02 22:59:06") "001_before_complete" "001.clp" , beforeFilterTest (read "2025-10-02 22:59:55") "001_before_truncate" "001.clp" , afterFilterTest (read "2025-10-02 22:59:06") "001_after_complete" "001.clp" , afterFilterTest (read "2025-10-02 22:59:55") "001_after_truncate" "001.clp" , beforeFilterTest (read "2025-10-02 22:59:06") "002_before_complete" "002.clp" , beforeFilterTest (read "2025-10-02 22:59:55") "002_before_truncate" "002.clp" , afterFilterTest (read "2025-10-02 22:59:06") "002_after_complete" "002.clp" , afterFilterTest (read "2025-10-02 22:59:55") "002_after_truncate" "002.clp" ] queryTest :: [TagPredicate] -> String -> FilePath -> TestTree queryTest filters name fp = mkGoldenTestOk (\_src -> pure . fmap (filterByTags filters) . resolve) name ("query" fp) beforeFilterTest :: LocalTime -> String -> FilePath -> TestTree beforeFilterTest thres name fp = mkGoldenTestOk (\_src -> pure . fmap (entriesBefore thres) . resolve) name ("query" fp) afterFilterTest :: LocalTime -> String -> FilePath -> TestTree afterFilterTest thres name fp = mkGoldenTestOk (\_src -> pure . fmap (entriesAfter thres) . resolve) name ("query" fp) parsedRight :: IO (ParseResult a) -> IO a parsedRight x = x >>= \case Left err -> fail $ errorBundlePretty err Right ok -> pure ok mkGoldenTestOk :: ToExpr expr => (T.Text -> RawAst -> IO expr) -> String -> FilePath -> TestTree mkGoldenTestOk f = mkGoldenTest $ \src -> \case Right output -> do output' <- f src output pure $ toExpr output' Left err -> fail $ errorBundlePretty err mkGoldenTestErr :: String -> FilePath -> TestTree mkGoldenTestErr = mkGoldenTest $ \_src -> \case Right output -> fail $ "Unexpected success:\n" <> show output Left err -> pure $ errorBundlePretty err mkGoldenTest :: ToExpr expr => ( forall s e . (VisualStream s, TraversableStream s, ShowErrorComponent e) => T.Text -> Either (ParseErrorBundle s e) RawAst -> IO expr ) -- ^ handle the parse result -> String -> FilePath -> TestTree mkGoldenTest f name fname = ediffGolden goldenTest name outFilePath $ do content <- TIO.readFile inFilePath f content $ runParser caliperAst inFilePath content where inFilePath = "test" "data" fname outFilePath = inFilePath `replaceFileName` name `addExtension` ".expr"