{- Copyright (c) Meta Platforms, Inc. and affiliates. All rights reserved. This source code is licensed under the BSD-style license found in the LICENSE file in the root directory of this source tree. -} {-# LANGUAGE TypeApplications #-} module TestFixtures (main) where import Control.Monad (unless, when) import Data.Aeson.Encode.Pretty import Data.List import Data.List.Extra import qualified Data.Text.Lazy as Text import qualified Data.Text.Lazy.Encoding as Text import Data.Typeable import System.Exit import System.FilePath import System.IO.Temp (withSystemTempDirectory) import System.Process import Test.HUnit import TestRunner import Util import Thrift.Compiler import Thrift.Compiler.GenHaskell import Thrift.Compiler.GenJSON import Thrift.Compiler.GenJSONLoc import Thrift.Compiler.Options import Thrift.Compiler.OptParse import Language.Haskell.Exts main :: IO () main = withFixtureOptions $ \opts -> do fixtures <- mapM genFixtures opts testRunner $ TestList $ map checkFixture $ concat fixtures genFixtures :: SomeOptions -> IO [ThriftModule] genFixtures (TheseOptions opts@Options{..}) = do (headModule, deps) <- typecheckInput opts =<< parseAll opts optsPath generator headModule deps where generator prog deps = case optsGenMode of EmitCode | Just (hsopts, ps) <- cast (opts, prog : deps) -> concat <$> mapM @[] (genHsCode hsopts) ps EmitJSON WithoutLoc | optsSingleOutput -> return [genJSONThriftModule (Just deps) prog] | otherwise -> return $ map (genJSONThriftModule Nothing) $ prog : deps EmitJSON WithLoc | optsSingleOutput -> return [genJSONLocThriftModule (Just deps) prog] | otherwise -> return $ map (genJSONLocThriftModule Nothing) $ prog : deps _ -> return [] prettyJSON = encodePretty' defConfig { confCompare = compare } genJSONThriftModule ds prog = ThriftModule { tmPath = uncurry () $ getAstPath prog , tmContents = Text.unpack $ Text.decodeUtf8 $ prettyJSON $ genJSON prog ds , tmModuleName = "" } genJSONLocThriftModule ds prog = ThriftModule { tmPath = uncurry () $ getAstPath prog , tmContents = Text.unpack $ Text.decodeUtf8 $ prettyJSON $ genJSONLoc prog ds , tmModuleName = "" } checkFixture :: ThriftModule -> Test checkFixture ThriftModule{..} = TestLabel mname $ TestCase $ do when (canParse tmPath) $ assertParseOk tmPath tmContents exts fixture <- readFile tmPath assertFileEqual tmPath fixture tmContents where mname = snd $ breakOnEnd "/test/fixtures/" tmPath canParse filename | isSuffixOf ".ast" filename = False | isSuffixOf "Service.hs" filename = False -- Service.hs does not parse due to -- https://github.com/haskell-suite/haskell-src-exts/issues/310 -- (I think) | otherwise = True exts = map EnableExtension [ConstraintKinds] assertParseOk :: FilePath -> String -> [Extension] -> IO () assertParseOk path contents exts = case parseFileContentsWithExts exts contents of ParseOk _ -> return () ParseFailed loc str -> do putStrLn path putStrLn "Contents:" putStrLn contents assertFailure $ "Parse failed at [" ++ path ++ "] (" ++ show (srcLine loc) ++ ":" ++ show (srcColumn loc) ++ "): " ++ str assertFileEqual :: FilePath -> String -> String -> IO () assertFileEqual _path expected obtained = unless (normalize expected == normalize obtained) $ withSystemTempDirectory "diff" $ \dir -> do let expectPath = dir "expect" obtainPath = dir "obtain" writeFile expectPath (normalize expected) writeFile obtainPath (normalize obtained) let cp = proc "diff" ["-U", "10", expectPath, obtainPath] (ec, out, _err) <- readCreateProcessWithExitCode cp "" unless (ExitSuccess == ec) $ do assertFailure out where -- In dependent-sum > 0.6 the This constructor was renamed to Some normalize = Text.unpack . Text.unlines . map fixLine . Text.lines . Text.pack fixLine = Text.replace "\"compiler/test/fixtures/" "\"test/fixtures/" . Text.replace "\"include_path\": \"compiler\"," "\"include_path\": \".\"," -- TODO: update the fixtures and remove this replace once 8.8 lands . Text.replace "Thrift.This" "Thrift.Some"