1 {-# LANGUAGE RecordWildCards #-}
3 -- | cabal-install CLI command: test
5 module Distribution
.Client
.CmdTest
(
6 -- * The @test@ CLI and action
10 -- * Internals exposed for testing
11 isSubComponentProblem
,
18 import Distribution
.Client
.Compat
.Prelude
21 import Distribution
.Client
.ProjectOrchestration
22 import Distribution
.Client
.CmdErrorMessages
23 ( renderTargetSelector
, showTargetSelector
, targetSelectorFilter
, plural
,
25 renderTargetProblemNoTargets
, targetSelectorPluralPkgs
)
26 import Distribution
.Client
.TargetProblem
27 ( TargetProblem
(..) )
28 import Distribution
.Client
.NixStyleOptions
29 ( NixStyleFlags
(..), nixStyleOptions
, defaultNixStyleFlags
)
30 import Distribution
.Client
.Setup
31 ( GlobalFlags
(..), ConfigFlags
(..) )
32 import Distribution
.Simple
.Setup
33 ( TestFlags
(..), fromFlagOrDefault
)
34 import Distribution
.Simple
.Command
35 ( CommandUI
(..), usageAlternatives
)
36 import Distribution
.Simple
.Flag
38 import Distribution
.Verbosity
40 import Distribution
.Simple
.Utils
41 ( notice
, wrapText
, die
' )
43 import qualified System
.Exit
(exitSuccess
)
46 testCommand
:: CommandUI
(NixStyleFlags
())
47 testCommand
= CommandUI
48 { commandName
= "v2-test"
49 , commandSynopsis
= "Run test-suites."
50 , commandUsage
= usageAlternatives
"v2-test" [ "[TARGETS] [FLAGS]" ]
51 , commandDescription
= Just
$ \_
-> wrapText
$
52 "Runs the specified test-suites, first ensuring they are up to "
55 ++ "Any test-suite in any package in the project can be specified. "
56 ++ "A package can be specified in which case all the test-suites in the "
57 ++ "package are run. The default is to run all the test-suites in the "
58 ++ "package in the current directory.\n\n"
60 ++ "Dependencies are built or rebuilt as necessary. Additional "
61 ++ "configuration flags can be specified on the command line and these "
62 ++ "extend the project configuration from the 'cabal.project', "
63 ++ "'cabal.project.local' and other files.\n\n"
65 ++ "To pass command-line arguments to a test suite, see the "
67 , commandNotes
= Just
$ \pname
->
69 ++ " " ++ pname
++ " v2-test\n"
70 ++ " Run all the test-suites in the package in the current directory\n"
71 ++ " " ++ pname
++ " v2-test pkgname\n"
72 ++ " Run all the test-suites in the package named pkgname\n"
73 ++ " " ++ pname
++ " v2-test cname\n"
74 ++ " Run the test-suite named cname\n"
75 ++ " " ++ pname
++ " v2-test cname --enable-coverage\n"
76 ++ " Run the test-suite built with code coverage (including local libs used)\n"
78 , commandDefaultFlags
= defaultNixStyleFlags
()
79 , commandOptions
= nixStyleOptions
(const [])
84 -- | The @test@ command is very much like @build@. It brings the install plan
85 -- up to date, selects that part of the plan needed by the given or implicit
86 -- test target(s) and then executes the plan.
88 -- Compared to @build@ the difference is that there's also test targets
89 -- which are ephemeral.
91 -- For more details on how this works, see the module
92 -- "Distribution.Client.ProjectOrchestration"
94 testAction
:: NixStyleFlags
() -> [String] -> GlobalFlags
-> IO ()
95 testAction flags
@NixStyleFlags
{..} targetStrings globalFlags
= do
97 baseCtx
<- establishProjectBaseContext verbosity cliConfig OtherCommand
99 targetSelectors
<- either (reportTargetSelectorProblems verbosity
) return
100 =<< readTargetSelectors
(localPackages baseCtx
) (Just TestKind
) targetStrings
103 runProjectPreBuildPhase verbosity baseCtx
$ \elaboratedPlan
-> do
105 when (buildSettingOnlyDeps
(buildSettings baseCtx
)) $
107 "The test command does not support '--only-dependencies'. "
108 ++ "You may wish to use 'build --only-dependencies' and then "
111 -- Interpret the targets on the command line as test targets
112 -- (as opposed to say build or haddock targets).
113 targets
<- either (reportTargetProblems verbosity failWhenNoTestSuites
) return
116 selectComponentTarget
121 let elaboratedPlan
' = pruneInstallPlanToTargets
125 return (elaboratedPlan
', targets
)
127 printPlan verbosity baseCtx buildCtx
129 buildOutcomes
<- runProjectBuildPhase verbosity baseCtx buildCtx
130 runProjectPostBuildPhase verbosity baseCtx buildCtx buildOutcomes
132 failWhenNoTestSuites
= testFailWhenNoTestSuites testFlags
133 verbosity
= fromFlagOrDefault normal
(configVerbosity configFlags
)
134 cliConfig
= commandLineFlagsToProjectConfig globalFlags flags mempty
-- ClientInstallFlags
136 -- | This defines what a 'TargetSelector' means for the @test@ command.
137 -- It selects the 'AvailableTarget's that the 'TargetSelector' refers to,
138 -- or otherwise classifies the problem.
140 -- For the @test@ command we select all buildable test-suites,
141 -- or fail if there are no test-suites or no buildable test-suites.
143 selectPackageTargets
:: TargetSelector
144 -> [AvailableTarget k
] -> Either TestTargetProblem
[k
]
145 selectPackageTargets targetSelector targets
147 -- If there are any buildable test-suite targets then we select those
148 |
not (null targetsTestsBuildable
)
149 = Right targetsTestsBuildable
151 -- If there are test-suites but none are buildable then we report those
152 |
not (null targetsTests
)
153 = Left
(TargetProblemNoneEnabled targetSelector targetsTests
)
155 -- If there are no test-suite but some other targets then we report that
157 = Left
(noTestsProblem targetSelector
)
159 -- If there are no targets at all then we report that
161 = Left
(TargetProblemNoTargets targetSelector
)
163 targetsTestsBuildable
= selectBuildableTargets
164 . filterTargetsKind TestKind
167 targetsTests
= forgetTargetsDetail
168 . filterTargetsKind TestKind
172 -- | For a 'TargetComponent' 'TargetSelector', check if the component can be
175 -- For the @test@ command we just need to check it is a test-suite, in addition
176 -- to the basic checks on being buildable etc.
178 selectComponentTarget
:: SubComponentTarget
179 -> AvailableTarget k
-> Either TestTargetProblem k
180 selectComponentTarget subtarget
@WholeComponent t
181 | CTestName _
<- availableTargetComponentName t
182 = either Left
return $
183 selectComponentTargetBasic subtarget t
185 = Left
(notTestProblem
186 (availableTargetPackageId t
)
187 (availableTargetComponentName t
))
189 selectComponentTarget subtarget t
190 = Left
(isSubComponentProblem
191 (availableTargetPackageId t
)
192 (availableTargetComponentName t
)
195 -- | The various error conditions that can occur when matching a
196 -- 'TargetSelector' against 'AvailableTarget's for the @test@ command.
199 -- | The 'TargetSelector' matches targets but no test-suites
200 TargetProblemNoTests TargetSelector
202 -- | The 'TargetSelector' refers to a component that is not a test-suite
203 | TargetProblemComponentNotTest PackageId ComponentName
205 -- | Asking to test an individual file or module is not supported
206 | TargetProblemIsSubComponent PackageId ComponentName SubComponentTarget
210 type TestTargetProblem
= TargetProblem TestProblem
213 noTestsProblem
:: TargetSelector
-> TargetProblem TestProblem
214 noTestsProblem
= CustomTargetProblem
. TargetProblemNoTests
216 notTestProblem
:: PackageId
-> ComponentName
-> TargetProblem TestProblem
217 notTestProblem pkgid name
= CustomTargetProblem
$ TargetProblemComponentNotTest pkgid name
219 isSubComponentProblem
222 -> SubComponentTarget
223 -> TargetProblem TestProblem
224 isSubComponentProblem pkgid name subcomponent
= CustomTargetProblem
$
225 TargetProblemIsSubComponent pkgid name subcomponent
227 reportTargetProblems
:: Verbosity
-> Flag
Bool -> [TestTargetProblem
] -> IO a
228 reportTargetProblems verbosity failWhenNoTestSuites problems
=
229 case (failWhenNoTestSuites
, problems
) of
230 (Flag
True, [CustomTargetProblem
(TargetProblemNoTests _
)]) ->
231 die
' verbosity problemsMessage
232 (_
, [CustomTargetProblem
(TargetProblemNoTests selector
)]) -> do
233 notice verbosity
(renderAllowedNoTestsProblem selector
)
234 System
.Exit
.exitSuccess
235 (_
, _
) -> die
' verbosity problemsMessage
237 problemsMessage
= unlines . map renderTestTargetProblem
$ problems
239 -- | Unless @--test-fail-when-no-test-suites@ flag is passed, we don't
240 -- @die@ when the target problem is 'TargetProblemNoTests'.
241 -- Instead, we display a notice saying that no tests have run and
242 -- indicate how this behaviour was enabled.
243 renderAllowedNoTestsProblem
:: TargetSelector
-> String
244 renderAllowedNoTestsProblem selector
=
245 "No tests to run for " ++ renderTargetSelector selector
247 renderTestTargetProblem
:: TestTargetProblem
-> String
248 renderTestTargetProblem
(TargetProblemNoTargets targetSelector
) =
249 case targetSelectorFilter targetSelector
of
250 Just kind | kind
/= TestKind
251 -> "The test command is for running test suites, but the target '"
252 ++ showTargetSelector targetSelector
++ "' refers to "
253 ++ renderTargetSelector targetSelector
++ "."
254 ++ "\n" ++ show targetSelector
256 _
-> renderTargetProblemNoTargets
"test" targetSelector
257 renderTestTargetProblem problem
=
258 renderTargetProblem
"test" renderTestProblem problem
261 renderTestProblem
:: TestProblem
-> String
262 renderTestProblem
(TargetProblemNoTests targetSelector
) =
263 "Cannot run tests for the target '" ++ showTargetSelector targetSelector
264 ++ "' which refers to " ++ renderTargetSelector targetSelector
266 ++ plural
(targetSelectorPluralPkgs targetSelector
) "it does" "they do"
267 ++ " not contain any test suites."
269 renderTestProblem
(TargetProblemComponentNotTest pkgid cname
) =
270 "The test command is for running test suites, but the target '"
271 ++ showTargetSelector targetSelector
++ "' refers to "
272 ++ renderTargetSelector targetSelector
++ " from the package "
273 ++ prettyShow pkgid
++ "."
275 targetSelector
= TargetComponent pkgid cname WholeComponent
277 renderTestProblem
(TargetProblemIsSubComponent pkgid cname subtarget
) =
278 "The test command can only run test suites as a whole, "
279 ++ "not files or modules within them, but the target '"
280 ++ showTargetSelector targetSelector
++ "' refers to "
281 ++ renderTargetSelector targetSelector
++ "."
283 targetSelector
= TargetComponent pkgid cname subtarget