-- FIXME reconcile issues with reports {-# OPTIONS_GHC -fno-warn-missing-signatures -fno-warn-orphans -fno-warn-unused-binds -fno-warn-unused-do-bind #-} {-# LANGUAGE DeriveDataTypeable #-} import System.Console.CmdArgs hiding (ignore) import System.Environment import Control.Monad.Trans.Either import Control.Monad.IO.Class import qualified System.Console.CmdArgs.Explicit as Exp import qualified Data.List as L import qualified Data.Suite as S import qualified Data.Integrated as I import qualified Data.Integrated.IOState as IOS import Control.Monad.State import Control.Lens import qualified Filesystem.Path.CurrentOS as FS import Prelude hiding (FilePath) import Data.Aeson hiding (json) import Data.AesonData import Data.Suite.Ops import Data.Integrated.TestModule (Map) import qualified Data.Map as M import Data.ModulePath import Data.Property import Data.String.Indent import qualified Data.Suite as Su import qualified Data.ByteString.Lazy.Char8 as BS data Args = Args { json :: Maybe String, masking :: Bool, ignore :: Bool, suite :: String, tests :: [String] } deriving (Show, Data, Typeable) integration = let description = "This program set-combines quickcheck property-containing modules with " ++ "an IDE parsable test harness." in Args { json = def -- &= typ "json file path" &= help "generate a report of operations on the target suite in JSON" &= opt "" , masking = def &= help "keep existing tests in suite not part of sync" , ignore = def &= help "ignore test file parse failures (only parsed will be set-combined)" , suite = def &= typ "" &= argPos 0 , tests = [] &= args &= typ "files/dirs" } &= help "The tasty suite file integrator" &= program "tasty-integrate" &= summary "tasty-integrate v0.0 (C) John P. Feltz" &= details [description] &= verbosity fromArgs :: Args -> EitherT String IO I.Integrated fromArgs a = if (L.null . tests) a then left "no tests provided" else do fs_state <- liftIO $ IOS.toState (tests a) (suite a) integrated <- liftIO $ do (maybe_integrated, errors) <- runStateT (ranState fs_state) [] putStr $ L.intercalate "\n" $ map show errors return maybe_integrated maybe (left "\n\nHalted due to errors.") right integrated where fromIgnore :: Bool -> I.ErrorObservance fromIgnore True = I.Ignored fromIgnore _ = I.Dependent ranState = I.toIntegrated (fromIgnore $ ignore a) (masking a) fromIntegrated :: Maybe Verbosity -> Maybe String -> I.Integrated -> IO () fromIntegrated v j i = do ops <- I.fromIntegrated (S.toBuf S.tastyImports) i let (suite_fp, original_suite) = view I.suiteFile i -- Print the summary putStrLn ('\n': toSummary original_suite suite_fp ops v) -- Write json to console or fileput if necessary maybe (return ()) (writeJson suite_fp ops) j where writeJson :: FS.FilePath -> Ops -> String -> IO () writeJson suite_fp ops p = if L.null p then putStr "\n" >> putStrLn encoded else writeFile p encoded where encoded :: String encoded = BS.unpack . encode $ AesonData ops suite_fp fromProps :: [Property] -> String fromProps [] = "none" fromProps non_empty = L.intercalate "\n" . map func $ non_empty toModuleListing :: Map -> String toModuleListing m = if M.null m then " none" else L.intercalate "\n" . map (uncurry fromMod) . M.toList . M.map snd $ m where fromMod :: ModulePath -> [Property] -> String fromMod mp props = show mp ++ '\n':indent 1 (fromProps props) toNumericListing :: Map -> String toNumericListing m = if M.null m then "none" else indent 1 (L.intercalate "\n" . map (uncurry fromMod) . M.toList . M.map snd $ m) where fromMod :: ModulePath -> [Property] -> String fromMod mp props = show mp ++ ", " ++ (show . L.length $ props) ++ " property(s)" toSummary :: Su.Suite -> FS.FilePath -> Ops -> Maybe Verbosity -> String toSummary _ _ _ (Just Quiet) = "" toSummary s fp ops (Just Loud) = fromOriginal toModuleListing s fp ops ++ "\n" toSummary s fp ops _ = fromOriginal toNumericListing s fp ops ++ "\n" fromOps :: (Map -> String) -> Ops -> String fromOps f ops = L.intercalate "\n" $ map (\(label, l) -> label ++ ": \n" ++ indent 1 (f . view l $ ops)) [ ("added", added), ("removed", removed), ("modified", modified), ("unmodified", unmodified) ] fromOriginal :: (Map -> String) -> Su.Suite -> FS.FilePath -> Ops -> String fromOriginal f Su.Uncreated suite_fp ops = if not . M.null $ view added ops then "(created) suite at: " ++ FS.encodeString suite_fp ++ "\n" ++ "with: \n" ++ indent 1 (f (view added ops)) else "no suite created" fromOriginal f (Su.Suite _) suite_fp ops = if mutation then "(changed) suite at: " ++ FS.encodeString suite_fp ++ "\n" ++ "with: \n" ++ indent 1 (fromOps f ops) else "(unchanged) suite at: " ++ FS.encodeString suite_fp where mutation = any (not . M.null . ($ ops) . view) [added, removed, modified] main :: IO () main = do env_args <- getArgs case Exp.process (cmdArgsMode integration) env_args of Right parsed_args -> case cmdArgsHelp parsed_args of (Just h) -> putStr h Nothing -> let program_args = cmdArgsValue parsed_args v = cmdArgsVerbosity parsed_args in do result <- runEitherT . fromArgs $ program_args case result of Left e -> dispatchHelp e Right i -> fromIntegrated v (json program_args) i Left err -> dispatchHelp err where dispatchHelp :: String -> IO () dispatchHelp err = putStr $ err ++ "\n " ++ "(--help for assistance)\n"