1- # Feature: detecting tests that "leak" threads
1+ # Feature: detecting tests that "leak" threads
22
33## Functionality
44
5- * It should be possible to add a ` @DetectThreadLeaks ` extension which detects new threads forked within the test
6- container. This extension takes a parameter - the scope of detection. Either we care about threads leaked
7- from the entire container or from each individual test. Here is an example of use:
5+ * It should be possible to add a ` @DetectThreadLeaks ` extension which detects new threads forked within the test
6+ container. This extension takes a parameter - the scope of detection. Either we care about threads leaked
7+ from the entire container or from each individual test. Here is an example of use:
88
99``` java
1010
@@ -13,35 +13,87 @@ public class TestClass {
1313 @Test
1414 public void testMethod () {
1515 new Thread (() - > {
16- try { Thread . sleep(1000 ); } catch (Exception e) {}
16+ try {
17+ Thread . sleep(1000 );
18+ } catch (Exception e) {
19+ }
1720 }). start();
1821 }
1922}
2023```
2124
2225* The extension is ** only functional in sequential mode** . It should emit a warning and do nothing if tests are
23- run in concurrent mode.
26+ run in concurrent mode.
2427
2528* Occasionally there will be threads that cannot be joined but will eventually terminate. One can specify an additional
26- "linger" time before the thread leak is reported, for example one second, below:
27-
29+ "linger" time before the thread leak is reported, for example one second, below:
30+
2831``` java
32+
2933@DetectThreadLeaks (scope = DetectThreadLeaks . Scope . SUITE )
3034@DetectThreadLeaks.LingerTime (millis = 1_000 )
3135public class TestClass {
3236 @Test
3337 public void testMethod () {
3438 new Thread (() - > {
35- try { Thread . sleep(1000 ); } catch (Exception e) {}
39+ try {
40+ Thread . sleep(1000 );
41+ } catch (Exception e) {
42+ }
3643 }). start();
3744 }
3845}
3946```
4047
4148* In certain cases, system threads or other threads beyond the test's control may be started and cannot be terminated
42- within the test's scope. The ` @DetectThreadLeaks.ExcludeThreads ` annotation can provide programmatic filters which
43- tell the extension to ignore certain threads.
49+ within the test's scope. The ` @DetectThreadLeaks.ExcludeThreads ` annotation can provide programmatic filters which
50+ tell the extension to ignore certain threads.
51+
52+ * When ` @DetectThreadLeaks ` is active, it also detects ** uncaught exceptions** thrown by any thread
53+ during the scope. Such exceptions are collected and reported as test failures. If both a thread leak and uncaught
54+ exceptions occur, all are reported: the leak error is thrown and the
55+ uncaught-exception errors are attached as suppressed exceptions.
56+
57+ ``` java
58+
59+ @DetectThreadLeaks (scope = DetectThreadLeaks . Scope . TEST )
60+ public class TestClass {
61+ @Test
62+ public void testMethod () throws InterruptedException {
63+ Thread t = new Thread (() - > {
64+ throw new RuntimeException (" background failure" );
65+ });
66+ t. start();
67+ t. join();
68+ }
69+ }
70+ ```
4471
4572## Migration notes (from randomizedtesting for junit4)
4673
74+ * ` @ThreadLeakScope ` is replaced with ` @DetectThreadLeaks(scope = ...) ` . The ` NONE ` scope
75+ (disabling all checks) has no equivalent; simply remove ` @DetectThreadLeaks ` from the class.
76+ The default scope changed: the old default was ` TEST ` ; the new default is ` SUITE ` .
77+
78+ * ` @ThreadLeakLingering(linger = N) ` is replaced with ` @DetectThreadLeaks.LingerTime(millis = N) ` .
79+ The annotation can now be placed on individual test methods as well as on the class, with the
80+ method-level value taking precedence.
81+
82+ * ` @ThreadLeakFilters(filters = {MyFilter.class}) ` : replace with
83+ ` @DetectThreadLeaks.ExcludeThreads(MyFilter.class) ` . The filter interface changed from
84+ ` ThreadFilter.reject(Thread) ` (return ` true ` to exclude) to ` Predicate<Thread>.test(Thread) `
85+ (return ` true ` to exclude). Rename and invert the logic accordingly. The ` defaultFilters `
86+ flag has no equivalent; built-in system-thread filters are always applied. Filters are now
87+ collected hierarchically from the method, the class, and all superclasses, and combined with OR.
88+
89+ * ` @ThreadLeakAction ` : interrupt-on-leak is now always performed as cleanup (no annotation
90+ needed). Leaked threads are interrupted and joined before the failure is reported. There is no
91+ equivalent of the ` WARN ` -only mode.
92+
93+ * ` @ThreadLeakZombies ` : zombie tracking (marking remaining tests as aborted when a leaked
94+ thread could not be killed) is not implemented. Threads that survive the interrupt/join budget
95+ are still reported in the failure message but subsequent tests are not skipped.
4796
97+ * ` @ThreadLeakGroup ` — the group scope (` ALL ` / ` MAIN ` / ` TESTGROUP ` ) is not configurable.
98+ The extension always uses ` Thread.getAllStackTraces() ` , equivalent to the old ` ALL ` group, and
99+ filters out known system threads automatically.
0 commit comments