@@ -11,6 +11,7 @@ use camino::{Utf8Path, Utf8PathBuf};
1111use clap:: { builder:: BoolishValueParser , ArgAction , Args , Parser , Subcommand , ValueEnum } ;
1212use guppy:: graph:: PackageGraph ;
1313use itertools:: Itertools ;
14+ use log:: warn;
1415use nextest_filtering:: FilteringExpr ;
1516use nextest_metadata:: BuildPlatform ;
1617use nextest_runner:: {
@@ -406,8 +407,7 @@ impl NtrOpts {
406407 & self . run_opts . reporter_opts ,
407408 cli_args,
408409 output_writer,
409- ) ?;
410- Ok ( 0 )
410+ )
411411 }
412412}
413413
@@ -754,6 +754,33 @@ pub struct TestRunnerOpts {
754754 /// Run all tests regardless of failure
755755 #[ arg( long, conflicts_with = "no-run" , overrides_with = "fail-fast" ) ]
756756 no_fail_fast : bool ,
757+
758+ /// Behavior if there are no tests to run.
759+ ///
760+ /// The default is currently `warn`, but it will change to `fail` in the future.
761+ #[ arg(
762+ long,
763+ value_enum,
764+ conflicts_with = "no-run" ,
765+ value_name = "ACTION" ,
766+ require_equals = true ,
767+ env = "NEXTEST_NO_TESTS"
768+ ) ]
769+ no_tests : Option < NoTestsBehavior > ,
770+ }
771+
772+ #[ derive( Clone , Copy , Debug , Default , ValueEnum ) ]
773+ enum NoTestsBehavior {
774+ /// Silently exit with code 0.
775+ Pass ,
776+
777+ /// Produce a warning and exit with code 0.
778+ #[ default]
779+ Warn ,
780+
781+ /// Produce an error message and exit with code 4.
782+ #[ clap( alias = "error" ) ]
783+ Fail ,
757784}
758785
759786impl TestRunnerOpts {
@@ -1563,7 +1590,7 @@ impl App {
15631590 reporter_opts : & TestReporterOpts ,
15641591 cli_args : Vec < String > ,
15651592 output_writer : & mut OutputWriter ,
1566- ) -> Result < ( ) > {
1593+ ) -> Result < i32 > {
15671594 let ( version_only_config, config) = self . base . load_config ( ) ?;
15681595 let profile = self . base . load_profile ( profile_name, & config) ?;
15691596
@@ -1638,7 +1665,7 @@ impl App {
16381665 Some ( runner_builder) => runner_builder,
16391666 None => {
16401667 // This means --no-run was passed in. Exit.
1641- return Ok ( ( ) ) ;
1668+ return Ok ( 0 ) ;
16421669 }
16431670 } ;
16441671
@@ -1656,15 +1683,32 @@ impl App {
16561683 // Write and flush the event.
16571684 reporter. report_event ( event)
16581685 } ) ?;
1686+ reporter. finish ( ) ;
16591687 self . base
16601688 . check_version_config_final ( version_only_config. nextest_version ( ) ) ?;
16611689
16621690 match run_stats. summarize_final ( ) {
1663- FinalRunStats :: Success => Ok ( ( ) ) ,
1691+ FinalRunStats :: Success => Ok ( 0 ) ,
16641692 FinalRunStats :: NoTestsRun => {
1665- // This currently does not exit with a non-zero code, but will in the future:
1666- // https://github.com/nextest-rs/nextest/issues/1639
1667- Ok ( ( ) )
1693+ match runner_opts. no_tests {
1694+ Some ( NoTestsBehavior :: Pass ) => Ok ( 0 ) ,
1695+ Some ( NoTestsBehavior :: Warn ) => {
1696+ warn ! ( "no tests to run" ) ;
1697+ Ok ( 0 )
1698+ }
1699+ Some ( NoTestsBehavior :: Fail ) => {
1700+ Err ( ExpectedError :: NoTestsRun { is_default : false } )
1701+ }
1702+ None => {
1703+ // This currently does not exit with a non-zero code, but will in the
1704+ // future: https://github.com/nextest-rs/nextest/issues/1639
1705+ warn ! (
1706+ "no tests to run -- this will become an error in the future\n \
1707+ (hint: use `--no-tests` to customize)"
1708+ ) ;
1709+ Ok ( 0 )
1710+ }
1711+ }
16681712 }
16691713 FinalRunStats :: Canceled ( RunStatsFailureKind :: SetupScript )
16701714 | FinalRunStats :: Failed ( RunStatsFailureKind :: SetupScript ) => {
0 commit comments