Cabal-1.24.0.0: tests/PackageTests.hs
-- The intention is that this will be the new unit test framework.
-- Please add any working tests here. This file should do nothing
-- but import tests from other modules.
--
-- Stephen Blackheath, 2009
module Main where
import PackageTests.Options
import PackageTests.PackageTester
import PackageTests.Tests
import Distribution.Simple.Configure
( ConfigStateFileError(..), findDistPrefOrDefault, getConfigStateFile
, interpretPackageDbFlags )
import Distribution.Simple.Compiler (PackageDB(..), PackageDBStack)
import Distribution.Simple.LocalBuildInfo (LocalBuildInfo(..))
import Distribution.Simple.Program.Types (Program(..), programPath, programVersion)
import Distribution.Simple.Program.Builtin
( ghcProgram, ghcPkgProgram, haddockProgram )
import Distribution.Simple.Program.Db (requireProgram)
import Distribution.Simple.Setup (Flag(..), readPackageDbList, showPackageDbList)
import Distribution.Simple.Utils (cabalVersion)
import Distribution.Text (display, simpleParse)
import Distribution.Verbosity (normal, flagToVerbosity)
import Distribution.ReadE (readEOrFail)
import Control.Exception
import Data.Proxy ( Proxy(..) )
import Distribution.Compat.Environment ( lookupEnv )
import System.Directory
import Test.Tasty
import Test.Tasty.Options
import Test.Tasty.Ingredients
import Data.Maybe
#if MIN_VERSION_base(4,6,0)
import System.Environment ( getExecutablePath )
#endif
main :: IO ()
main = do
-- In abstract, the Cabal test suite makes calls to the "Setup"
-- executable and tests the output of Cabal. However, we have to
-- responsible for building this executable in the first place,
-- since (1) Cabal doesn't support a test-suite depending on an
-- executable, so we can't put a "Setup" executable in the Cabal
-- file and then depend on it, (2) we don't want to call the Cabal
-- functions *directly* because we need to capture and save the
-- stdout and stderr, and (3) even if we could do all that, we will
-- want to test some Custom setup scripts, which will be specific to
-- the test at hand and need to be compiled against Cabal.
--
-- To be able to build the executable, there is some information
-- we need:
--
-- 1. We need to know what ghc to use,
--
-- 2. We need to know what package databases (plural!) contain
-- all of the necessary dependencies to make our Cabal package
-- well-formed.
--
-- We could have the user pass these all in as arguments (TODO: this
-- should be an option), but there's a more convenient way to get
-- this information: the *build configuration* that was used to
-- build the Cabal library (and this test suite) in the first place.
-- To do this, we need to find the 'dist' directory that was set as
-- the build directory for Cabal.
dist_dir <- guessDistDir
-- Might be bottom, if we can't figure it out. If you override
-- all the relevant parameters you might still succeed.
lbi <- getPersistBuildConfig_ (dist_dir </> "setup-config")
-- You need to run the test suite in the right directory, sorry.
test_dir <- getCurrentDirectory
-- Pull out the information we need from the LBI
-- TODO: The paths to GHC are configurable by command line, but you
-- have to be careful: some tests might depend on the Cabal library,
-- in which case you REALLY need to have built and installed Cabal
-- for the version that the test suite is being built against. The
-- easiest thing to do is make sure you built Cabal the same way as
-- you will run the tests.
let getExePathFromEnvOrLBI env_name prog = do
r <- lookupEnv env_name
case r of
Nothing -> do
(conf, _) <- requireProgram normal prog (withPrograms lbi)
return (programPath conf)
Just x -> return x
-- It is too much damn work to actually properly configure it
-- (Cabal will go off and probe GHC and we really aren't keen
-- on doing this every time we run the test suite.)
ghc_path <- getExePathFromEnvOrLBI "CABAL_PACKAGETESTS_GHC" ghcProgram
ghc_pkg_path <- getExePathFromEnvOrLBI "CABAL_PACKAGETESTS_GHC_PKG"
ghcPkgProgram
haddock_path <- getExePathFromEnvOrLBI "CABAL_PACKAGETESTS_HADDOCK"
haddockProgram
with_ghc_path <- fromMaybe ghc_path
`fmap` lookupEnv "CABAL_PACKAGETESTS_WITH_GHC"
ghc_version_env <- lookupEnv "CABAL_PACKAGETESTS_GHC_VERSION"
ghc_version <- case ghc_version_env of
Nothing -> do
(ghcConf, _) <- requireProgram normal ghcProgram (withPrograms lbi)
return (fromJust (programVersion ghcConf))
Just str ->
return (fromJust (simpleParse str))
with_ghc_version <- do
version <- programFindVersion ghcProgram normal with_ghc_path
case version of
Nothing -> error "Cannot determine version of GHC used for --with-ghc"
Just v -> return v
-- Package DBs are not guaranteed to be absolute, so make them so in
-- case a subprocess using the package DB needs a different CWD.
db_stack_env <- lookupEnv "CABAL_PACKAGETESTS_DB_STACK"
let packageDBStack0 = case db_stack_env of
Nothing -> withPackageDB lbi
Just str -> interpretPackageDbFlags True -- user install? why not.
(concatMap readPackageDbList
(splitSearchPath str))
packageDBStack1 <- mapM canonicalizePackageDB packageDBStack0
-- The packageDBStack is worth some commentary. The database
-- stack we extract from the LBI will contain enough package
-- databases to make the Cabal package well-formed. However,
-- it does not *contain* the inplace installed Cabal package.
-- So we need to add that to the stack.
let packageDBStack2
= packageDBStack1 ++
[SpecificPackageDB
(dist_dir </> "package.conf.inplace")]
-- THIS ISN'T EVEN MY FINAL FORM. The package database stack
-- controls where we install a package; specifically, the package is
-- installed to the top-most package on the stack (this makes the
-- most sense, since it could depend on any of the packages below
-- it.) If the test wants to register anything (as opposed to just
-- working in place), then we need to have another temporary
-- database we can install into (and not accidentally clobber any of
-- the other stacks.) This is done on a per-test basis.
--
-- ONE MORE THING. On the subject of installing the package (with
-- copy/register) it is EXTREMELY important that we also overload
-- the install directories, so we don't clobber anything in the
-- default install paths. VERY IMPORTANT.
-- TODO: make this controllable by a flag
verbosity <- maybe normal (readEOrFail flagToVerbosity)
`fmap` lookupEnv "VERBOSE"
-- The inplaceDB is where the Cabal library was registered
-- in place (and is usable.) inplaceConfig is a convenient
-- set of flags to make sure we make it visible.
let suite = SuiteConfig
{ cabalDistPref = dist_dir
, ghcPath = ghc_path
, ghcVersion = ghc_version
, ghcPkgPath = ghc_pkg_path
, withGhcPath = with_ghc_path
, withGhcVersion = with_ghc_version
, packageDBStack = packageDBStack2
, suiteVerbosity = verbosity
, absoluteCWD = test_dir
}
putStrLn $ "Cabal test suite - testing cabal version "
++ display cabalVersion
putStrLn $ "Cabal build directory: " ++ dist_dir
putStrLn $ "Test directory: " ++ test_dir
-- TODO: it might be useful to factor this out so that ./Setup
-- configure dumps this file, so we can read it without in a version
-- stable way.
putStrLn $ "Environment:"
putStrLn $ "CABAL_PACKAGETESTS_GHC=" ++ show ghc_path ++ " \\"
putStrLn $ "CABAL_PACKAGETESTS_GHC_VERSION="
++ show (display ghc_version) ++ " \\"
putStrLn $ "CABAL_PACKAGETESTS_GHC_PKG=" ++ show ghc_pkg_path ++ " \\"
putStrLn $ "CABAL_PACKAGETESTS_WITH_GHC=" ++ show with_ghc_path ++ " \\"
putStrLn $ "CABAL_PACKAGETESTS_HADDOCK=" ++ show haddock_path ++ " \\"
-- For brevity, do pre-canonicalization
putStrLn $ "CABAL_PACKAGETESTS_DB_STACK=" ++
show (intercalate [searchPathSeparator]
(showPackageDbList (uninterpretPackageDBFlags
packageDBStack0)))
-- Create a shared Setup executable to speed up Simple tests
putStrLn $ "Building shared ./Setup executable"
rawCompileSetup verbosity suite [] "tests"
defaultMainWithIngredients options $
runTestTree "Package Tests" (tests suite)
-- Reverse of 'interpretPackageDbFlags'.
-- prop_idem stk b
-- = interpretPackageDbFlags b (uninterpretPackageDBFlags stk) == stk
uninterpretPackageDBFlags :: PackageDBStack -> [Maybe PackageDB]
uninterpretPackageDBFlags stk = Nothing : map (\x -> Just x) stk
-- | Guess what the 'dist' directory Cabal was installed in is. There's
-- no 100% reliable way to find this, but there are a few good shots:
--
-- 1. Test programs are ~always built in-place, in a directory
-- that looks like dist/build/package-tests/package-tests;
-- thus the directory can be determined by looking at $0.
-- This method is robust against sandboxes, Nix local
-- builds, and Stack, but doesn't work if you're running
-- in an interpreter.
--
-- 2. We can use the normal input methods (as per Cabal),
-- checking for the CABAL_BUILDDIR environment variable as
-- well as the default location in the current working directory.
--
-- NB: If you update this, also update its copy in cabal-install's
-- IntegrationTests
guessDistDir :: IO FilePath
guessDistDir = do
#if MIN_VERSION_base(4,6,0)
-- Method (1)
-- TODO: this needs to be BC'ified, probably.
exe_path <- canonicalizePath =<< getExecutablePath
-- exe_path is something like /path/to/dist/build/package-tests/package-tests
let dist0 = dropFileName exe_path </> ".." </> ".."
b <- doesFileExist (dist0 </> "setup-config")
#else
let dist0 = error "no path"
b = False
#endif
-- Method (2)
if b then canonicalizePath dist0
else findDistPrefOrDefault NoFlag >>= canonicalizePath
canonicalizePackageDB :: PackageDB -> IO PackageDB
canonicalizePackageDB (SpecificPackageDB path)
= SpecificPackageDB `fmap` canonicalizePath path
canonicalizePackageDB x = return x
-- | Like Distribution.Simple.Configure.getPersistBuildConfig but
-- doesn't check that the Cabal version matches, which it doesn't when
-- we run Cabal's own test suite, due to bootstrapping issues.
-- Here's the situation:
--
-- 1. There's some system Cabal-1.0 installed. We use this
-- to build Setup.hs
-- 2. We run ./Setup configure, which uses Cabal-1.0 to
-- write out the LocalBuildInfo
-- 3. We build the Cabal library, whose version is Cabal-2.0
-- 4. We build the package-tests executable, which LINKS AGAINST
-- Cabal-2.0
-- 5. We try to read the LocalBuildInfo that ./Setup configure
-- wrote out, but it's Cabal-1.0 format!
--
-- It's a bit skeevy that we're trying to read Cabal-1.0 LocalBuildInfo
-- using Cabal-2.0's parser, but this seems to work OK in practice
-- because LocalBuildInfo is a slow-moving data structure. If
-- we ever make a major change, this won't work, and we'll have to
-- take a different approach (either setting "build-type: Custom"
-- so we bootstrap with the most recent Cabal, or by writing the
-- information we need in another format.)
getPersistBuildConfig_ :: FilePath -> IO LocalBuildInfo
getPersistBuildConfig_ filename = do
eLBI <- try $ getConfigStateFile filename
case eLBI of
Left (ConfigStateFileBadVersion _ _ (Right lbi)) -> return lbi
-- These errors are lazy! We might not need these parameters.
Left (ConfigStateFileBadVersion _ _ (Left err))
-> return . error $
"We couldn't understand the build configuration. Try " ++
"editing Cabal.cabal to have 'build-type: Custom' " ++
"and then rebuilding, or manually specifying CABAL_PACKAGETESTS_* " ++
"environment variables (see README.md for more details)." ++
"\n\nOriginal error: " ++
show err
Left err -> return (throw err)
Right lbi -> return lbi
options :: [Ingredient]
options = includingOptions
[Option (Proxy :: Proxy OptionEnableAllTests)] :
defaultIngredients