1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135
|
{-# LANGUAGE NoImplicitPrelude #-}
{-# LANGUAGE OverloadedStrings #-}
-- | Test suite for Stack.Dot
module Stack.DotSpec
( dummyPayload
, spec
, sublistOf
, pkgName
, stubLoader
) where
import Data.List ((\\))
import qualified Data.Map as Map
import qualified Data.Set as Set
import Distribution.License ( License (BSD3) )
import qualified RIO.Text as T
import Stack.DependencyGraph ( pruneGraph, resolveDependencies )
import Stack.Prelude hiding ( pkgName )
import Stack.Types.DependencyTree ( DotPayload (..) )
import Test.Hspec ( Spec, describe, it, shouldBe )
import Test.Hspec.QuickCheck ( prop )
import Test.QuickCheck ( Gen, choose, forAll )
dummyPayload :: DotPayload
dummyPayload = DotPayload (parseVersion "0.0.0.0") (Just (Right BSD3)) Nothing
spec :: Spec
spec = do
let graph =
Map.mapKeys pkgName
. fmap (\p -> (Set.map pkgName p, dummyPayload))
. Map.fromList $ [("one",Set.fromList ["base","free"])
,("two",Set.fromList ["base","free","mtl","transformers","one"])
]
describe "Stack.Dot" $ do
it "does nothing if depth is 0" $
resolveDependencies (Just 0) graph stubLoader `shouldBe` pure graph
it "with depth 1, more dependencies are resolved" $ do
let graph' = Map.insert (pkgName "cycle")
(Set.singleton (pkgName "cycle"), dummyPayload)
graph
resultGraph = runIdentity (resolveDependencies (Just 0) graph stubLoader)
resultGraph' = runIdentity (resolveDependencies (Just 1) graph' stubLoader)
Map.size resultGraph < Map.size resultGraph' `shouldBe` True
it "cycles are ignored" $ do
let graph' = Map.insert (pkgName "cycle")
(Set.singleton (pkgName "cycle"), dummyPayload)
graph
resultGraph = resolveDependencies Nothing graph stubLoader
resultGraph' = resolveDependencies Nothing graph' stubLoader
fmap Map.size resultGraph' `shouldBe` fmap ((+1) . Map.size) resultGraph
let graphElem e = Set.member e . Set.unions . Map.elems
prop "requested packages are pruned" $ do
let resolvedGraph = runIdentity (resolveDependencies Nothing graph stubLoader)
allPackages g = Map.keysSet g `Set.union` foldMap fst g
forAll (sublistOf (Set.toList (allPackages resolvedGraph))) $ \toPrune ->
let pruned = pruneGraph [pkgName "one", pkgName "two"] toPrune resolvedGraph
in Set.null (allPackages pruned `Set.intersection` Set.fromList toPrune)
prop "pruning removes orphans" $ do
let resolvedGraph = runIdentity (resolveDependencies Nothing graph stubLoader)
allPackages g = Map.keysSet g `Set.union` foldMap fst g
orphans g = Map.filterWithKey (\k _ -> not (graphElem k g)) g
forAll (sublistOf (Set.toList (allPackages resolvedGraph))) $ \toPrune ->
let pruned = pruneGraph [pkgName "one", pkgName "two"] toPrune resolvedGraph
in null (Map.keys (orphans (fmap fst pruned)) \\ [pkgName "one", pkgName "two"])
{- Helper functions below -}
-- Backport from QuickCheck 2.8 to 2.7.6
sublistOf :: [a] -> Gen [a]
sublistOf = filterM (\_ -> choose (False, True))
-- Unsafe internal helper to create a package name
pkgName :: Text -> PackageName
pkgName = fromMaybe failure . parsePackageName . T.unpack
where
failure = error "Internal error during package name creation in DotSpec.pkgName"
-- Stub, simulates the function to load package dependencies
stubLoader :: PackageName -> Identity (Set PackageName, DotPayload)
stubLoader name = pure $ (, dummyPayload) . Set.fromList . map pkgName $
case show name of
"StateVar" -> ["stm", "transformers"]
"array" -> []
"bifunctors" -> ["semigroupoids", "semigroups", "tagged"]
"binary" -> ["array", "bytestring", "containers"]
"bytestring" -> ["deepseq", "ghc-prim", "integer-gmp"]
"comonad" -> [ "containers", "contravariant", "distributive", "semigroups"
, "tagged","transformers","transformers-compat"
]
"cont" -> [ "StateVar", "semigroups", "transformers", "transformers-compat"
, "void"
]
"containers" -> ["array", "deepseq", "ghc-prim"]
"deepseq" -> ["array"]
"distributive" -> [ "ghc-prim", "tagged", "transformers"
, "transformers-compat"
]
"free" -> [ "bifunctors", "comonad", "distributive", "mtl", "prelude-extras"
, "profunctors", "semigroupoids", "semigroups", "template-haskell"
, "transformers"
]
"ghc" -> []
"hashable" -> ["bytestring", "ghc-prim", "integer-gmp", "text"]
"integer" -> []
"mtl" -> ["transformers"]
"nats" -> []
"one" -> ["free"]
"prelude" -> []
"profunctors" -> [ "comonad", "distributive", "semigroupoids", "tagged"
, "transformers"
]
"semigroupoids" -> [ "comonad", "containers", "contravariant"
, "distributive", "semigroups", "transformers"
, "transformers-compat"
]
"semigroups" -> [ "bytestring", "containers", "deepseq", "hashable", "nats"
, "text", "unordered-containers"
]
"stm" -> ["array"]
"tagged" -> ["template-haskell"]
"template" -> []
"text" -> [ "array", "binary", "bytestring", "deepseq", "ghc-prim"
, "integer-gmp"
]
"transformers" -> []
"two" -> ["free", "mtl", "one", "transformers"]
"unordered" -> ["deepseq", "hashable"]
"void" -> ["ghc-prim", "hashable", "semigroups"]
_ -> []
|