1{-# LANGUAGE TypeApplications #-}23module Golden (eqivTests) where45import Control.Applicative ((<|>))6import Control.Exception (catch)7import Mach.Error (MakeErr)8import Mach.Main (run)9import System.Directory (withCurrentDirectory)10import System.Exit (ExitCode (ExitFailure, ExitSuccess))11import System.FilePath12import System.IO (IOMode (WriteMode), hClose, hGetContents, openFile)13import System.Process14 ( StdStream (CreatePipe, UseHandle),15 createPipe,16 createProcess,17 cwd,18 proc,19 std_err,20 std_out,21 waitForProcess,22 )23import Test.Tasty24import Test.Tasty.Golden.Advanced25import Util2627-- The golden test do not use a golden expected file but instead compare28-- the "output" of a reference make(1) implementation and mach. The29-- output is defined by 'MakeResult'. Presently it includes:30--31-- • A boolean indicating a succesfully exit32-- • Standard output as a 'String'33-- • A 'FilePAth' which refers to the dir where make(1) was invoked34--35type MakeResult = (Bool, String, FilePath)3637runMach :: [String] -> FilePath -> IO MakeResult38runMach flags skel = do39 destDir <- prepTempDir "actual" skel4041 (readEnd, writeEnd) <- createPipe42 success <-43 catch @MakeErr44 ( do45 e <- withCurrentDirectory destDir $ run writeEnd flags46 case e of47 ExitSuccess -> pure True48 _ -> pure False49 )50 (const $ pure False)5152 -- The readEnd is semi-close by hGetContents, should be53 -- closed once the entire handle content has been read.54 out <- hGetContents readEnd <* hClose writeEnd5556 pure (success, out, destDir)5758runGolden :: [String] -> FilePath -> IO MakeResult59runGolden flags skel = do60 destDir <- prepTempDir "golden" skel61 devNull <- openFile "/dev/null" WriteMode6263 (_, Just hout, _, p) <-64 createProcess65 (proc "pdpmake" flags)66 { cwd = Just destDir,67 std_out = CreatePipe,68 std_err = UseHandle devNull69 }7071 ret <- waitForProcess p <* hClose devNull72 out <- hGetContents hout73 case ret of74 ExitSuccess -> pure (True, out, destDir)75 ExitFailure _ -> pure (False, out, destDir)7677compareRuns :: MakeResult -> MakeResult -> IO (Maybe String)78compareRuns (succG, outG, fpG) (succA, outA, fpA) = do79 out <- compareStdout80 dir <- compareDirectory81 ret <- pure compareExit82 pure (out <|> dir <|> ret)83 where84 compareStdout :: IO (Maybe String)85 compareStdout =86 pure $87 if outG /= outA88 then Just "standard output differs"89 else Nothing9091 compareDirectory :: IO (Maybe String)92 compareDirectory = do93 let diffArgs = ["-upr", fpG, fpA]94 (_, Just hout, _, p) <-95 createProcess96 (proc "diff" diffArgs) {std_out = CreatePipe}9798 exitCode <- waitForProcess p99 case exitCode of100 ExitSuccess -> pure Nothing101 ExitFailure _ -> Just <$> hGetContents hout102103 compareExit :: Maybe String104 compareExit =105 case (succG, succA) of106 (True, False) -> Just "expected zero exit"107 (False, True) -> Just "expected non-zero exit"108 _ -> Nothing109110runMake :: TestName -> [String] -> FilePath -> TestTree111runMake name flags makeDir =112 goldenTest113 name114 (runGolden flags makeDir)115 (runMach flags makeDir)116 compareRuns117 (\_ -> pure ())118119------------------------------------------------------------------------120121runTest :: String -> [String] -> TestTree122runTest name flags =123 runMake124 (if null flags then name else name ++ " [" ++ unwords flags ++ "]")125 flags126 ("test" </> "golden" </> name)127128eqivTests :: TestTree129eqivTests =130 testGroup131 "eviqTests"132 [ runTest "expand-delayed" [],133 runTest "expand-immediate" [],134 runTest "expand-conditional" [],135 runTest "default-rule" [],136 runTest "include-makefile" [],137 runTest "single-suffix-inference" [],138 runTest "double-suffix-inference" [],139 runTest "silent-exec" [],140 runTest "ignore-error" [],141 runTest "ignore-error-silent" [],142 runTest "silent-selected-targets" [],143 runTest "silent-all" [],144 runTest "silent-all-cmdline" ["-s"],145 runTest "expand-append" [],146 runTest "substitute-expand" [],147 runTest "builtin-c-compilation1" [],148 runTest "builtin-c-compilation2" [],149 runTest "silent-append" [],150 runTest "ignore-all" [],151 runTest "ignore-single" [],152 runTest "ignore-all-cmdline" ["-i"],153 runTest "continue-execution" [],154 runTest "continue-execution" ["-k", "-i"],155 runTest "continue-execution" ["-S", "-k", "-i"],156 runTest "continue-execution" ["-k", "-i", "-S"],157 runTest "continue-execution" ["-k", "-i", "-S", "-k"],158 runTest "continue-execution" ["-k"],159 runTest "dry-run" ["-n"],160 runTest "dry-run-with-exec" ["-n"],161 runTest "no-builtins" ["-r"],162 runTest "lvalue-macro" []163 -- TODO: Need to support $(MAKE) for this.164 -- runTest "append-prerequisites",165 ]