Skip to content

Commit fba5b71

Browse files
committed
Compare parsers on PackageTests projects
1 parent d001bf9 commit fba5b71

2 files changed

Lines changed: 65 additions & 6 deletions

File tree

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
11
module Main where
22

3-
import Test.Tasty (defaultMain)
4-
import Tests.ParserTests (parserTests)
3+
import Test.Tasty (defaultMain, testGroup)
4+
import Tests.ParserTests (packageTestsProjectTests, parserTests)
55

66
main :: IO ()
7-
main = defaultMain parserTests
7+
main = do
8+
packageTests <- packageTestsProjectTests
9+
defaultMain $ testGroup "parser-tests" [parserTests, packageTests]

‎cabal-install/parser-tests/Tests/ParserTests.hs‎

Lines changed: 60 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,14 @@
22
{-# LANGUAGE RecordWildCards #-}
33

44
-- | Tests for the project file parser
5-
module Tests.ParserTests (parserTests) where
5+
module Tests.ParserTests (parserTests, packageTestsProjectTests) where
66

7+
import Control.Exception (try)
78
import Control.Monad.IO.Class
89
( MonadIO (liftIO)
910
)
1011
import Data.Either (fromRight)
12+
import Data.List (isInfixOf)
1113
import qualified Data.Map as Map
1214
import Data.Maybe (fromJust)
1315
import qualified Data.Set as Set
@@ -60,8 +62,8 @@ import Distribution.Utils.NubList
6062
import Distribution.Verbosity
6163
import GHC.Stack (HasCallStack)
6264
import Network.URI (parseURI)
63-
import System.Directory (canonicalizePath, doesFileExist)
64-
import System.FilePath (normalise, (</>))
65+
import System.Directory (canonicalizePath, doesDirectoryExist, doesFileExist, listDirectory)
66+
import System.FilePath (makeRelative, normalise, takeDirectory, takeExtension, takeFileName, (</>))
6567
import Prelude ()
6668

6769
import Test.Tasty (TestTree, testGroup)
@@ -607,6 +609,61 @@ readConfig testSubDir projectFileName = do
607609
assertEdiffEqual "Parsec parser disagrees with the legacy parser" legacy parsec
608610
return (parsec, legacy)
609611

612+
-- | The project files of the cabal-testsuite package tests, each read with
613+
-- both parsers, which must agree. These are real project files exercising
614+
-- far more of the grammar than the fixtures here, so with the legacy parser
615+
-- as the oracle they are the widest check that the parsec parser has of what
616+
-- a field means.
617+
--
618+
-- Files with an import over HTTP are skipped so that the test needs no
619+
-- network. Files that both parsers reject are accepted without comparing the
620+
-- errors, but a file that only one parser rejects is a failure.
621+
--
622+
-- The tests are empty when the cabal-testsuite directory is not there, as in
623+
-- an sdist of cabal-install.
624+
packageTestsProjectTests :: IO TestTree
625+
packageTestsProjectTests = do
626+
exists <- doesDirectoryExist packageTestsDir
627+
files <- if exists then filterM isOffline =<< findProjectFiles packageTestsDir else pure []
628+
httpTransport <- configureTransport silentVerbosity [] Nothing
629+
pure $
630+
testGroup
631+
"cabal-testsuite project files"
632+
[ testCase (makeRelative packageTestsDir file) (assertParsersAgree httpTransport file)
633+
| file <- files
634+
]
635+
where
636+
packageTestsDir = ".." </> "cabal-testsuite" </> "PackageTests"
637+
isOffline file = not . any importsHttp . lines <$> readFile file
638+
importsHttp l = "import:" `isPrefixOf` dropWhile isSpace l && "http" `isInfixOf` l
639+
640+
-- | Every @.project@ file under a directory.
641+
findProjectFiles :: FilePath -> IO [FilePath]
642+
findProjectFiles dir = do
643+
entries <- map (dir </>) <$> listDirectory dir
644+
dirs <- filterM doesDirectoryExist entries
645+
let files = [e | e <- entries, takeExtension e == ".project"]
646+
(sort files ++) . concat <$> traverse findProjectFiles (sort dirs)
647+
648+
-- | Reads one project file with both parsers and checks that they agree.
649+
assertParsersAgree :: HttpTransport -> FilePath -> Assertion
650+
assertParsersAgree httpTransport file = do
651+
projectRootDir <- canonicalizePath (takeDirectory file)
652+
let projectRoot = ProjectRootExplicit projectRootDir (takeFileName file)
653+
distDirLayout = defaultDistDirLayout projectRoot Nothing Nothing
654+
readWith reader = try . runRebuild projectRootDir $ reader silentVerbosity httpTransport distDirLayout ProjectFileKeyMain
655+
parsec <- readWith readProjectFileSkeletonParsec
656+
legacy <- readWith readProjectFileSkeletonLegacy
657+
case (legacy, parsec) of
658+
(Right l, Right p) -> assertEdiffEqual "Parsec parser disagrees with the legacy parser" l p
659+
(Left _, Left _) -> pure ()
660+
(Left e, Right _) -> assertFailure $ "Legacy parser failed where the parsec parser succeeded:\n" ++ displayException (e :: SomeException)
661+
(Right _, Left e) -> assertFailure $ "Parsec parser failed where the legacy parser succeeded:\n" ++ displayException (e :: SomeException)
662+
663+
-- | Neither parser's warnings are of interest when comparing them.
664+
silentVerbosity :: Verbosity
665+
silentVerbosity = mkVerbosity defaultVerbosityHandles silent
666+
610667
-- | Like 'assertEqual' but the failure shows a tree diff of the two values
611668
-- instead of two 'show' dumps.
612669
assertEdiffEqual :: (Eq a, ToExpr a, HasCallStack) => String -> a -> a -> Assertion

0 commit comments

Comments
 (0)