packages feed

weeder-2.9.0: test/Main.hs

module Main (main) where

import qualified Weeder.Main
import qualified Weeder.Run
import qualified Weeder
import qualified TOML
import qualified UnitTests.Weeder.ConfigSpec

import Data.Maybe
import Algebra.Graph.Export.Dot
import GHC.Types.Name.Occurrence (occNameString)
import System.Directory
import System.FilePath
import System.IO (stderr, hPrint)
import Test.Tasty (TestTree, defaultMain, testGroup)
import Control.Exception ( throwIO, IOException, handle )
import Data.List (find, sortOn)
import qualified Data.ByteString.Lazy as LBS
import Data.Text (pack)
import Data.Text.Encoding (encodeUtf8)
import Test.Tasty.Golden

main :: IO ()
main = do
  testOutputFiles <- fmap sortTests discoverIntegrationTests
  let hieDirectories = map (dropExtension . snd) testOutputFiles
  defaultMain $ 
    testGroup "Weeder"
      [ testGroup "Weeder.Run" $
          [ testGroup "runWeeder" $
              zipWith (uncurry integrationTest) 
                testOutputFiles 
                hieDirectories
          ]
      , UnitTests.Weeder.ConfigSpec.tests 
      ]
  where
    -- Sort the output files such that the failing ones go last
    sortTests = sortOn (isJust . fst)

-- | Run weeder on @hieDirectory@, comparing the output to @stdoutFile@.
--
-- The directory containing @hieDirectory@ must also have a @.toml@ file
-- with the same name as @hieDirectory@.
--
-- If @failingFile@ is @Just@, it is used as the expected output instead of
-- @stdoutFile@, and a different failure message is printed if the output
-- matches @stdoutFile@.
integrationTest :: Maybe FilePath -> FilePath -> FilePath -> TestTree
integrationTest failingFile stdoutFile hieDirectory = do
  goldenVsString (integrationTestText ++ hieDirectory) (fromMaybe stdoutFile failingFile) $ 
    integrationTestOutput hieDirectory
  where
    integrationTestText = case failingFile of
      Nothing -> "produces the expected output for "
      Just _ -> "produces the expected (wrong) output for "

-- | Returns detected .failing and .stdout files in ./test/Spec
discoverIntegrationTests :: IO [(Maybe FilePath, FilePath)]
discoverIntegrationTests = do
  contents <- listDirectory testPath
  let stdoutFiles = map (testPath </>) $
        filter (".stdout" `isExtensionOf`) contents
  pure . map (\s -> (findFailing s contents, s)) $ stdoutFiles
    where
      findFailing s = fmap (testPath </>) . find (takeBaseName s <.> ".failing" ==)
      testPath = "./test/Spec"

-- | Run weeder on the given directory for .hie files, returning stdout
-- Also creates a dotfile containing the dependency graph as seen by Weeder
integrationTestOutput :: FilePath -> IO LBS.ByteString
integrationTestOutput hieDirectory = do
  hieFiles <- Weeder.Main.getHieFiles ".hie" [hieDirectory] False
  weederConfig <- TOML.decodeFile configExpr >>= either throwIO pure
  let (weeds, analysis) = Weeder.Run.runWeeder weederConfig hieFiles
      graph = Weeder.dependencyGraph analysis
      graph' = export (defaultStyle (occNameString . Weeder.declOccName)) graph
  handle (\e -> hPrint stderr (e :: IOException)) $
    writeFile (hieDirectory <.> ".dot") graph'
  pure (LBS.fromStrict $ encodeUtf8 $ pack $ unlines $ map Weeder.Run.formatWeed weeds)
  where
    configExpr = hieDirectory <.> ".toml"