@@ -2317,8 +2317,7 @@ def get_default_xml_output_filename():
2317
2317
os .path .splitext (os .path .basename (sys .argv [0 ]))[0 ] + '.xml' )
2318
2318
2319
2319
2320
- def _setup_filtering (argv ):
2321
- # type: (MutableSequence[Text]) -> None
2320
+ def _setup_filtering (argv : MutableSequence [str ]) -> bool :
2322
2321
"""Implements the bazel test filtering protocol.
2323
2322
2324
2323
The following environment variable is used in this method:
@@ -2333,16 +2332,20 @@ def _setup_filtering(argv):
2333
2332
2334
2333
Args:
2335
2334
argv: the argv to mutate in-place.
2335
+
2336
+ Returns:
2337
+ Whether test filtering is requested.
2336
2338
"""
2337
2339
test_filter = os .environ .get ('TESTBRIDGE_TEST_ONLY' )
2338
2340
if argv is None or not test_filter :
2339
- return
2341
+ return False
2340
2342
2341
2343
filters = shlex .split (test_filter )
2342
2344
if sys .version_info [:2 ] >= (3 , 7 ):
2343
2345
filters = ['-k=' + test_filter for test_filter in filters ]
2344
2346
2345
2347
argv [1 :1 ] = filters
2348
+ return True
2346
2349
2347
2350
2348
2351
def _setup_test_runner_fail_fast (argv ):
@@ -2369,8 +2372,9 @@ def _setup_test_runner_fail_fast(argv):
2369
2372
argv [1 :1 ] = ['--failfast' ]
2370
2373
2371
2374
2372
- def _setup_sharding (custom_loader = None ):
2373
- # type: (Optional[unittest.TestLoader]) -> unittest.TestLoader
2375
+ def _setup_sharding (
2376
+ custom_loader : Optional [unittest .TestLoader ] = None ,
2377
+ ) -> Tuple [unittest .TestLoader , Optional [int ]]:
2374
2378
"""Implements the bazel sharding protocol.
2375
2379
2376
2380
The following environment variables are used in this method:
@@ -2389,8 +2393,10 @@ def _setup_sharding(custom_loader=None):
2389
2393
custom_loader: A TestLoader to be made sharded.
2390
2394
2391
2395
Returns:
2392
- The test loader for shard-filtering or the standard test loader, depending
2393
- on the sharding environment variables.
2396
+ A tuple of ``(test_loader, shard_index)``. ``test_loader`` is for
2397
+ shard-filtering or the standard test loader depending on the sharding
2398
+ environment variables. ``shard_index`` is the shard index, or ``None`` when
2399
+ sharding is not used.
2394
2400
"""
2395
2401
2396
2402
# It may be useful to write the shard file even if the other sharding
@@ -2408,7 +2414,7 @@ def _setup_sharding(custom_loader=None):
2408
2414
base_loader = custom_loader or TestLoader ()
2409
2415
if 'TEST_TOTAL_SHARDS' not in os .environ :
2410
2416
# Not using sharding, use the expected test loader.
2411
- return base_loader
2417
+ return base_loader , None
2412
2418
2413
2419
total_shards = int (os .environ ['TEST_TOTAL_SHARDS' ])
2414
2420
shard_index = int (os .environ ['TEST_SHARD_INDEX' ])
@@ -2437,25 +2443,70 @@ def getShardedTestCaseNames(testCaseClass):
2437
2443
return [x for x in ordered_names if x in filtered_names ]
2438
2444
2439
2445
base_loader .getTestCaseNames = getShardedTestCaseNames
2440
- return base_loader
2446
+ return base_loader , shard_index
2447
+
2448
+
2449
+ def _run_and_get_tests_result (
2450
+ argv : MutableSequence [str ],
2451
+ args : Sequence [Any ],
2452
+ kwargs : MutableMapping [str , Any ],
2453
+ xml_test_runner_class : Type [unittest .TextTestRunner ],
2454
+ ) -> Tuple [unittest .TestResult , bool ]:
2455
+ """Same as run_tests, but it doesn't exit.
2441
2456
2457
+ Args:
2458
+ argv: sys.argv with the command-line flags removed from the front, i.e. the
2459
+ argv with which :func:`app.run()<absl.app.run>` has called
2460
+ ``__main__.main``. It is passed to
2461
+ ``unittest.TestProgram.__init__(argv=)``, which does its own flag parsing.
2462
+ It is ignored if kwargs contains an argv entry.
2463
+ args: Positional arguments passed through to
2464
+ ``unittest.TestProgram.__init__``.
2465
+ kwargs: Keyword arguments passed through to
2466
+ ``unittest.TestProgram.__init__``.
2467
+ xml_test_runner_class: The type of the test runner class.
2442
2468
2443
- # pylint: disable=line-too-long
2444
- def _run_and_get_tests_result ( argv , args , kwargs , xml_test_runner_class ):
2445
- # type: (MutableSequence[Text], Sequence[Any], MutableMapping[Text, Any], Type) -> unittest.TestResult
2446
- # pylint: enable=line-too-long
2447
- """Same as run_tests, except it returns the result instead of exiting."""
2469
+ Returns:
2470
+ A tuple of ``(test_result, fail_when_no_tests_ran)``.
2471
+ ``fail_when_no_tests_ran`` indicates whether the test should fail when
2472
+ no tests ran.
2473
+ """
2448
2474
2449
2475
# The entry from kwargs overrides argv.
2450
2476
argv = kwargs .pop ('argv' , argv )
2451
2477
2478
+ if sys .version_info [:2 ] >= (3 , 12 ):
2479
+ # Python 3.12 unittest changed the behavior from PASS to FAIL in
2480
+ # https://github.com/python/cpython/pull/102051. absltest follows this.
2481
+ fail_when_no_tests_ran = True
2482
+ else :
2483
+ # Historically, absltest and unittest before Python 3.12 passes if no tests
2484
+ # ran.
2485
+ fail_when_no_tests_ran = False
2486
+
2452
2487
# Set up test filtering if requested in environment.
2453
- _setup_filtering (argv )
2488
+ if _setup_filtering (argv ):
2489
+ # When test filtering is requested, ideally we also want to fail when no
2490
+ # tests ran. However, the test filters are usually done when running bazel.
2491
+ # When you run multiple targets, e.g. `bazel test //my_dir/...
2492
+ # --test_filter=MyTest`, you don't necessarily want individual tests to fail
2493
+ # because no tests match in that particular target.
2494
+ # Due to this use case, we don't fail when test filtering is requested via
2495
+ # the environment variable from bazel.
2496
+ fail_when_no_tests_ran = False
2497
+
2454
2498
# Set up --failfast as requested in environment
2455
2499
_setup_test_runner_fail_fast (argv )
2456
2500
2457
2501
# Shard the (default or custom) loader if sharding is turned on.
2458
- kwargs ['testLoader' ] = _setup_sharding (kwargs .get ('testLoader' , None ))
2502
+ kwargs ['testLoader' ], shard_index = _setup_sharding (
2503
+ kwargs .get ('testLoader' , None )
2504
+ )
2505
+ if shard_index is not None and shard_index > 0 :
2506
+ # When sharding is requested, all the shards except the first one shall not
2507
+ # fail when no tests ran. This happens when the shard count is greater than
2508
+ # the test case count.
2509
+ fail_when_no_tests_ran = False
2459
2510
2460
2511
# XML file name is based upon (sorted by priority):
2461
2512
# --xml_output_file flag, XML_OUTPUT_FILE variable,
@@ -2533,9 +2584,13 @@ def _run_and_get_tests_result(argv, args, kwargs, xml_test_runner_class):
2533
2584
# on argv, which is sys.argv without the command-line flags.
2534
2585
kwargs ['argv' ] = argv
2535
2586
2587
+ # Request unittest.TestProgram to not exit. The exit will be handled by
2588
+ # `absltest.run_tests`.
2589
+ kwargs ['exit' ] = False
2590
+
2536
2591
try :
2537
2592
test_program = unittest .TestProgram (* args , ** kwargs )
2538
- return test_program .result
2593
+ return test_program .result , fail_when_no_tests_ran
2539
2594
finally :
2540
2595
if xml_buffer :
2541
2596
try :
@@ -2545,9 +2600,11 @@ def _run_and_get_tests_result(argv, args, kwargs, xml_test_runner_class):
2545
2600
xml_buffer .close ()
2546
2601
2547
2602
2548
- def run_tests (argv , args , kwargs ): # pylint: disable=line-too-long
2549
- # type: (MutableSequence[Text], Sequence[Any], MutableMapping[Text, Any]) -> None
2550
- # pylint: enable=line-too-long
2603
+ def run_tests (
2604
+ argv : MutableSequence [Text ],
2605
+ args : Sequence [Any ],
2606
+ kwargs : MutableMapping [Text , Any ],
2607
+ ) -> None :
2551
2608
"""Executes a set of Python unit tests.
2552
2609
2553
2610
Most users should call absltest.main() instead of run_tests.
@@ -2568,8 +2625,13 @@ def run_tests(argv, args, kwargs): # pylint: disable=line-too-long
2568
2625
kwargs: Keyword arguments passed through to
2569
2626
``unittest.TestProgram.__init__``.
2570
2627
"""
2571
- result = _run_and_get_tests_result (
2572
- argv , args , kwargs , xml_reporter .TextAndXMLTestRunner )
2628
+ result , fail_when_no_tests_ran = _run_and_get_tests_result (
2629
+ argv , args , kwargs , xml_reporter .TextAndXMLTestRunner
2630
+ )
2631
+ if fail_when_no_tests_ran and result .testsRun == 0 :
2632
+ # Python 3.12 unittest exits with 5 when no tests ran. The code comes from
2633
+ # pytest which does the same thing.
2634
+ sys .exit (5 )
2573
2635
sys .exit (not result .wasSuccessful ())
2574
2636
2575
2637
0 commit comments