stackage/Stackage/CheckBuildPlan.hs
Chris Done 45b33ac54d Flexible tests for checkBuildPlan (#375)
@snoyberg Can you confirm that this simple test suite works for you?
I've set this up so that I can then add some cyclic dependencies and add
an occurs check for that, but figured I'd setup tests for some of the
existing invariants first.

Should be like:

    $ cabal test --ghc-options=-O0; cat dist/test/stackage-0.4.0-spec.log
    Building stackage-0.4.0...
    Preprocessing library stackage-0.4.0...
    In-place registering stackage-0.4.0...
    Preprocessing executable 'stackage' for stackage-0.4.0...
    Preprocessing test suite 'spec' for stackage-0.4.0...
    [3 of 4] Compiling Stackage.BuildPlanSpec ( test/Stackage/BuildPlanSpec.hs, dist/build/spec/spec-tmp/Stackage/BuildPlanSpec.o )
    Linking dist/build/spec/spec ...
    Running 1 test suites...
    Test suite spec: RUNNING...
    Test suite spec: PASS
    Test suite logged to: dist/test/stackage-0.4.0-spec.log
    1 of 1 test suites (1 of 1 test cases) passed.
    Test suite spec: RUNNING...

    Stackage.BuildPlan
      simple package set
      bad version range on depdendency fails
      nonexistent package fails to check
      default package set checks ok
    Stackage.CorePackages
      works
      contains known core packages
      getCoreExecutables includes known executables
    Stackage.PackageIndex
      works
      getLatestDescriptions gives reasonable results

    Finished in 14.3302 seconds
    9 examples, 0 failures
    Test suite spec: PASS
    Test suite logged to: dist/test/stackage-0.4.0-spec.log
2015-01-04 22:54:33 +01:00

118 lines
3.9 KiB
Haskell

{-# LANGUAGE DeriveDataTypeable #-}
{-# LANGUAGE GeneralizedNewtypeDeriving #-}
{-# LANGUAGE NoImplicitPrelude #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE RecordWildCards #-}
{-# LANGUAGE ViewPatterns #-}
-- | Confirm that a build plan has a consistent set of dependencies.
module Stackage.CheckBuildPlan
( checkBuildPlan
, BadBuildPlan
) where
import Control.Monad.Writer.Strict (Writer, execWriter, tell)
import qualified Data.Text as T
import Stackage.BuildConstraints
import Stackage.BuildPlan
import Stackage.PackageDescription
import Stackage.Prelude
-- FIXME check cycles in dependencies, only looking at libraries and
-- executables
-- | Check the build plan for missing deps, wrong versions, etc.
checkBuildPlan :: MonadThrow m => BuildPlan -> m ()
checkBuildPlan BuildPlan {..}
| null errs' = return ()
| otherwise = throwM errs
where
allPackages = siCorePackages bpSystemInfo ++ map ppVersion bpPackages
errs@(BadBuildPlan errs') =
execWriter $ mapM_ (checkDeps allPackages) $ mapToList bpPackages
-- | For a given package name and plan, check that its dependencies are:
--
-- 1. Existent (existing in the provided package map)
-- 2. Within version range
checkDeps :: Map PackageName Version
-> (PackageName, PackagePlan)
-> Writer BadBuildPlan ()
checkDeps allPackages (user, pb) =
mapM_ go $ mapToList $ sdPackages $ ppDesc pb
where
go (dep, diRange -> range) =
case lookup dep allPackages of
Nothing -> tell $ BadBuildPlan $ singletonMap (dep, Nothing) errMap
Just version
| version `withinRange` range -> return ()
| otherwise -> tell $ BadBuildPlan $ singletonMap
(dep, Just version)
errMap
where
errMap = singletonMap pu range
pu = PkgUser
{ puName = user
, puVersion = ppVersion pb
, puMaintainer = pcMaintainer $ ppConstraints pb
, puGithubPings = ppGithubPings pb
}
data PkgUser = PkgUser
{ puName :: PackageName
, puVersion :: Version
, puMaintainer :: Maybe Maintainer
, puGithubPings :: Set Text
}
deriving (Eq, Ord)
pkgUserShow1 :: PkgUser -> Text
pkgUserShow1 PkgUser {..} = concat
[ display puName
, "-"
, display puVersion
]
pkgUserShow2 :: PkgUser -> Text
pkgUserShow2 PkgUser {..} = unwords
$ (maybe "No maintainer" unMaintainer puMaintainer ++ ".")
: map (cons '@') (setToList puGithubPings)
newtype BadBuildPlan =
BadBuildPlan (Map (PackageName, Maybe Version) (Map PkgUser VersionRange))
deriving Typeable
instance Exception BadBuildPlan
instance Show BadBuildPlan where
show (BadBuildPlan errs) =
unpack $ concatMap go $ mapToList errs
where
go ((dep, mdepVer), users) = unlines
$ ""
: showDepVer dep mdepVer
: map showUser (mapToList users)
showDepVer :: PackageName -> Maybe Version -> Text
showDepVer dep Nothing = display dep ++ " (not present) depended on by:"
showDepVer dep (Just version) = concat
[ display dep
, "-"
, display version
, " depended on by:"
]
showUser :: (PkgUser, VersionRange) -> Text
showUser (pu, range) = concat
[ "- "
, pkgUserShow1 pu
, " ("
-- add a space after < to avoid confusing Markdown processors (like
-- Github's issue tracker)
, T.replace "<" "< " $ display range
, "). "
, pkgUserShow2 pu
]
instance Monoid BadBuildPlan where
mempty = BadBuildPlan mempty
mappend (BadBuildPlan x) (BadBuildPlan y) =
BadBuildPlan $ unionWith (unionWith intersectVersionRanges) x y