1 {-# LANGUAGE RecordWildCards #-}
3 -- | Utilities for creating HUnit test cases with the solver DSL.
4 module UnitTests
.Distribution
.Solver
.Modular
.DSL
.TestCaseUtils
8 , disableFineGrainedConflicts
12 , allowBootLibInstalls
15 , disableSolveExecutables
32 import Distribution
.Solver
.Compat
.Prelude
35 import Data
.List
(elemIndex)
38 import Test
.Tasty
as TF
39 import Test
.Tasty
.HUnit
(assertBool
, assertEqual
, testCase
)
42 import qualified Distribution
.PackageDescription
as C
43 import Distribution
.Verbosity
44 import Language
.Haskell
.Extension
(Extension
(..), Language
(..))
48 import Distribution
.Client
.Dependency
(foldProgress
)
49 import qualified Distribution
.Solver
.Types
.PackagePath
as P
50 import Distribution
.Solver
.Types
.PkgConfigDb
(PkgConfigDb
(..), pkgConfigDbFromList
)
51 import Distribution
.Solver
.Types
.Settings
52 import Distribution
.Solver
.Types
.Variable
53 import UnitTests
.Distribution
.Solver
.Modular
.DSL
54 import UnitTests
.Options
56 maxBackjumps
:: Maybe Int -> SolverTest
-> SolverTest
57 maxBackjumps mbj test
= test
{testMaxBackjumps
= mbj
}
59 disableFineGrainedConflicts
:: SolverTest
-> SolverTest
60 disableFineGrainedConflicts test
=
61 test
{testFineGrainedConflicts
= FineGrainedConflicts
False}
63 minimizeConflictSet
:: SolverTest
-> SolverTest
64 minimizeConflictSet test
=
65 test
{testMinimizeConflictSet
= MinimizeConflictSet
True}
67 -- | Combinator to turn on --independent-goals behavior, i.e. solve
68 -- for the goals as if we were solving for each goal independently.
69 independentGoals
:: SolverTest
-> SolverTest
70 independentGoals test
= test
{testIndepGoals
= IndependentGoals
True}
72 -- | Combinator to turn on --prefer-oldest
73 preferOldest
:: SolverTest
-> SolverTest
74 preferOldest test
= test
{testPreferOldest
= PreferOldest
True}
76 allowBootLibInstalls
:: SolverTest
-> SolverTest
77 allowBootLibInstalls test
=
78 test
{testAllowBootLibInstalls
= AllowBootLibInstalls
True}
80 onlyConstrained
:: SolverTest
-> SolverTest
81 onlyConstrained test
=
82 test
{testOnlyConstrained
= OnlyConstrainedAll
}
84 disableBackjumping
:: SolverTest
-> SolverTest
85 disableBackjumping test
=
86 test
{testEnableBackjumping
= EnableBackjumping
False}
88 disableSolveExecutables
:: SolverTest
-> SolverTest
89 disableSolveExecutables test
=
90 test
{testSolveExecutables
= SolveExecutables
False}
92 goalOrder
:: [ExampleVar
] -> SolverTest
-> SolverTest
93 goalOrder
order test
= test
{testGoalOrder
= Just
order}
95 constraints
:: [ExConstraint
] -> SolverTest
-> SolverTest
96 constraints cs test
= test
{testConstraints
= cs
}
98 preferences
:: [ExPreference
] -> SolverTest
-> SolverTest
99 preferences prefs test
= test
{testSoftConstraints
= prefs
}
101 -- | Increase the solver's verbosity. This is necessary for test cases that
102 -- check the contents of the verbose log.
103 setVerbose
:: SolverTest
-> SolverTest
104 setVerbose test
= test
{testVerbosity
= verbose
}
106 enableAllTests
:: SolverTest
-> SolverTest
107 enableAllTests test
= test
{testEnableAllTests
= EnableAllTests
True}
109 {-------------------------------------------------------------------------------
111 -------------------------------------------------------------------------------}
113 data SolverTest
= SolverTest
114 { testLabel
:: String
115 , testTargets
:: [String]
116 , testResult
:: SolverResult
117 , testMaxBackjumps
:: Maybe Int
118 , testFineGrainedConflicts
:: FineGrainedConflicts
119 , testMinimizeConflictSet
:: MinimizeConflictSet
120 , testIndepGoals
:: IndependentGoals
121 , testPreferOldest
:: PreferOldest
122 , testAllowBootLibInstalls
:: AllowBootLibInstalls
123 , testOnlyConstrained
:: OnlyConstrained
124 , testEnableBackjumping
:: EnableBackjumping
125 , testSolveExecutables
:: SolveExecutables
126 , testGoalOrder
:: Maybe [ExampleVar
]
127 , testConstraints
:: [ExConstraint
]
128 , testSoftConstraints
:: [ExPreference
]
129 , testVerbosity
:: Verbosity
130 , testDb
:: ExampleDb
131 , testSupportedExts
:: Maybe [Extension
]
132 , testSupportedLangs
:: Maybe [Language
]
133 , testPkgConfigDb
:: Maybe PkgConfigDb
134 , testEnableAllTests
:: EnableAllTests
137 -- | Expected result of a solver test.
138 data SolverResult
= SolverResult
139 { resultLogPredicate
:: [String] -> Bool
140 -- ^ The solver's log should satisfy this predicate. Note that we also print
141 -- the log, so evaluating a large log here can cause a space leak.
142 , resultErrorMsgPredicateOrPlan
:: Either (String -> Bool) [(String, Int)]
143 -- ^ Fails with an error message satisfying the predicate, or succeeds with
147 solverSuccess
:: [(String, Int)] -> SolverResult
148 solverSuccess
= SolverResult
(const True) . Right
150 solverFailure
:: (String -> Bool) -> SolverResult
151 solverFailure
= SolverResult
(const True) . Left
153 -- | Can be used for test cases where we just want to verify that
154 -- they fail, but do not care about the error message.
155 anySolverFailure
:: SolverResult
156 anySolverFailure
= solverFailure
(const True)
158 -- | Makes a solver test case, consisting of the following components:
160 -- 1. An 'ExampleDb', representing the package database (both
161 -- installed and remote) we are doing dependency solving over,
162 -- 2. A 'String' name for the test,
163 -- 3. A list '[String]' of package names to solve for
164 -- 4. The expected result, either 'Nothing' if there is no
165 -- satisfying solution, or a list '[(String, Int)]' of
166 -- packages to install, at which versions.
168 -- See 'UnitTests.Distribution.Solver.Modular.DSL' for how
169 -- to construct an 'ExampleDb', as well as definitions of 'db1' etc.
177 mkTest
= mkTestExtLangPC Nothing Nothing
(Just
[])
186 mkTestExts exts
= mkTestExtLangPC
(Just exts
) Nothing
(Just
[])
195 mkTestLangs langs
= mkTestExtLangPC Nothing
(Just langs
) (Just
[])
198 :: Maybe [(String, String)]
204 mkTestPCDepends mPkgConfigDb
= mkTestExtLangPC Nothing Nothing mPkgConfigDb
209 -> Maybe [(String, String)]
215 mkTestExtLangPC exts langs mPkgConfigDb db label targets result
=
218 , testTargets
= targets
219 , testResult
= result
220 , testMaxBackjumps
= Nothing
221 , testFineGrainedConflicts
= FineGrainedConflicts
True
222 , testMinimizeConflictSet
= MinimizeConflictSet
False
223 , testIndepGoals
= IndependentGoals
False
224 , testPreferOldest
= PreferOldest
False
225 , testAllowBootLibInstalls
= AllowBootLibInstalls
False
226 , testOnlyConstrained
= OnlyConstrainedNone
227 , testEnableBackjumping
= EnableBackjumping
True
228 , testSolveExecutables
= SolveExecutables
True
229 , testGoalOrder
= Nothing
230 , testConstraints
= []
231 , testSoftConstraints
= []
232 , testVerbosity
= normal
234 , testSupportedExts
= exts
235 , testSupportedLangs
= langs
236 , testPkgConfigDb
= pkgConfigDbFromList
<$> mPkgConfigDb
237 , testEnableAllTests
= EnableAllTests
False
240 runTest
:: SolverTest
-> TF
.TestTree
241 runTest SolverTest
{..} = askOption
$ \(OptionShowSolverLog showSolverLog
) ->
242 testCase testLabel
$ do
251 (CountConflicts
True)
252 testFineGrainedConflicts
253 testMinimizeConflictSet
257 testAllowBootLibInstalls
259 testEnableBackjumping
261 (sortGoals
<$> testGoalOrder
)
266 printMsg msg
= when showSolverLog
$ putStrLn msg
267 msgs
= foldProgress
(:) (const []) (const []) progress
268 assertBool
("Unexpected solver log:\n" ++ unlines msgs
) $
269 resultLogPredicate testResult
$
271 result
<- foldProgress
((>>) . printMsg
) (return . Left
) (return . Right
) progress
275 ("Unexpected error:\n" ++ err
)
276 (checkErrorMsg testResult err
)
277 Right plan
-> assertEqual
"" (toMaybe testResult
) (Just
(extractInstallPlan plan
))
279 toMaybe
:: SolverResult
-> Maybe [(String, Int)]
280 toMaybe
= either (const Nothing
) Just
. resultErrorMsgPredicateOrPlan
282 checkErrorMsg
:: SolverResult
-> String -> Bool
283 checkErrorMsg result msg
=
284 case resultErrorMsgPredicateOrPlan result
of
293 sortGoals
= orderFromList
. map toVariable
295 -- Sort elements in the list ahead of elements not in the list. Otherwise,
296 -- follow the order in the list.
297 orderFromList
:: Eq a
=> [a
] -> a
-> a
-> Ordering
299 comparing
$ \x
-> let i
= elemIndex x xs
in (isNothing i
, i
)
301 toVariable
:: ExampleVar
-> Variable P
.QPN
302 toVariable
(P q pn
) = PackageVar
(toQPN q pn
)
303 toVariable
(F q pn fn
) = FlagVar
(toQPN q pn
) (C
.mkFlagName fn
)
304 toVariable
(S q pn stanza
) = StanzaVar
(toQPN q pn
) stanza
306 toQPN
:: ExampleQualifier
-> ExamplePkgName
-> P
.QPN
307 toQPN q pn
= P
.Q pp
(C
.mkPackageName pn
)
310 QualNone
-> P
.PackagePath P
.DefaultNamespace P
.QualToplevel
313 (P
.Independent
$ C
.mkPackageName p
)
318 (P
.QualSetup
(C
.mkPackageName s
))
319 QualIndepSetup p s
->
321 (P
.Independent
$ C
.mkPackageName p
)
322 (P
.QualSetup
(C
.mkPackageName s
))
326 (P
.QualExe
(C
.mkPackageName p1
) (C
.mkPackageName p2
))