packages feed

hindent-5.0.0: src/main/TestGenerate.hs

{-# LANGUAGE FlexibleContexts #-}
module Main(main) where

import Control.Monad
import Control.Applicative
import Data.List (find, unfoldr, isPrefixOf, intercalate)

import System.Directory
import System.Environment
import qualified Data.Text as T
import qualified Data.Text.Lazy as L
import qualified Data.Text.Lazy.Builder as L

import qualified HIndent

testDir, expectedDir :: String
testDir = "tests"
expectedDir = "expected"

-- This executable generates a test suite for a style using a test suite
-- from a previous style. Given arguments `from` and `to` which are style names,
-- it will take all the input files for `from`, create identical input files for `to`,
-- run each block in the input files through the `to` style, and generate expectation files matching the output.
main :: IO ()
main = do
  args <- getArgs
  case args of
    [from, to] -> generateTests from to
    _          -> putStrLn "Two arguments required: source tests and destination tests."

generateTests :: String -> String -> IO ()
generateTests from to = do
  --  Find the source and destination styles.
  let Just toStyle = findStyle (T.pack to)

  --  Get all the test and expectation files for the original test suite.
  testFilenamesFrom <- filter (not . isPrefixOf ".") <$> getDirectoryContents (tests from)

  -- Verify the target directories exist.
  createDirectoryIfMissing True $ tests to
  createDirectoryIfMissing True $ expected to

  -- Copy test files to the new style test directory.
  forM_ testFilenamesFrom $ \filename ->
    copyFile (tests from ++ filename) (tests to ++ filename)

  -- Generate expectation files for the new style test directory.
  forM_ testFilenamesFrom $ \filename -> do
    let dstFilename = expected to ++ expectedFilename filename
    contents <- expectationFileContents toStyle <$> readFile (tests to ++ filename)
    writeFile dstFilename contents

  where
    findStyle style = find ((== style). HIndent.styleName) [HIndent.johanTibell]
    tests style = "test/" ++ style ++ "/" ++ testDir ++ "/"
    expected style = "test/" ++ style ++ "/" ++ expectedDir ++ "/"
    expectedFilename filename = take (length filename - 4) filename ++ "exp"

expectationFileContents :: HIndent.Style -> String -> String
expectationFileContents style contents =
  let testDecls = parsePieces contents
      fmt input = L.unpack $ L.toLazyText $ case HIndent.reformat style Nothing $ L.pack input of
                                              Left err      -> error err
                                              Right builder -> builder
      outputs = map (replaceEmptyNewlines . fmt) testDecls
  in intercalate "\n" outputs
  where
    replaceEmptyNewlines = unlines . map replaceNewline . lines
    replaceNewline "" = ">"
    replaceNewline x = x

parsePieces :: String -> [String]
parsePieces str = map (intercalate "\n" . map mkNewlines) pieces
  where
    pieces = unfoldr unfolder (lines str)

    unfolder :: [String] -> Maybe ([String], [String])
    unfolder [] = Nothing
    unfolder remaining = Just $
     case break pieceBreak (zip remaining (tail remaining ++ [""]))  of
       (nonNull, [])     -> (map fst nonNull, [])
       (nonNull, _:rest) -> (map fst nonNull, map fst rest)

    pieceBreak :: (String, String) -> Bool
    pieceBreak ("", "") = error "Two consecutive line breaks!"
    pieceBreak (line, next) = null line && head next /= ' '

    mkNewlines :: String -> String
    mkNewlines ">" = ""
    mkNewlines x = x