Skip to content

Commit 4d6cca9

Browse files
Ubuntuclaude
andcommitted
fix: use SEPARATE_THREAD mode for @timeout to kill CPU-bound tests
The default SAME_THREAD mode uses Thread.interrupt() which is silently ignored by CPU-bound code like naive recursive fibonacci. SEPARATE_THREAD runs the test in a new thread and fails it with TimeoutException when the deadline passes, which actually works for tight computational loops. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1 parent ded6063 commit 4d6cca9

2 files changed

Lines changed: 36 additions & 27 deletions

File tree

codeflash/languages/java/instrumentation.py

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -596,9 +596,18 @@ def _add_per_test_timeout(source: str, timeout_seconds: int = _PER_TEST_TIMEOUT_
596596
"""Add @Timeout annotation to each @Test method to prevent individual tests from hanging.
597597
598598
This inserts `import org.junit.jupiter.api.Timeout;` and adds
599-
`@Timeout(N)` after every `@Test` annotation in the source.
599+
`@Timeout(value = N, threadMode = Timeout.ThreadMode.SEPARATE_THREAD)`
600+
after every `@Test` annotation in the source.
601+
602+
SEPARATE_THREAD is required because the default SAME_THREAD mode uses
603+
Thread.interrupt(), which is ignored by CPU-bound code (e.g. naive
604+
recursive fibonacci). SEPARATE_THREAD runs the test in a new thread
605+
and fails it with TimeoutException when the deadline passes.
600606
"""
601607
timeout_import = "import org.junit.jupiter.api.Timeout;"
608+
timeout_annotation = (
609+
f"@Timeout(value = {timeout_seconds}, threadMode = Timeout.ThreadMode.SEPARATE_THREAD)"
610+
)
602611

603612
# Add import if not already present
604613
if timeout_import not in source:
@@ -638,7 +647,7 @@ def _add_per_test_timeout(source: str, timeout_seconds: int = _PER_TEST_TIMEOUT_
638647
next_idx += 1
639648
if next_idx >= len(lines) or not lines[next_idx].strip().startswith("@Timeout"):
640649
indent = line[: len(line) - len(line.lstrip())]
641-
result_lines.append(f"{indent}@Timeout({timeout_seconds})")
650+
result_lines.append(f"{indent}{timeout_annotation}")
642651

643652
return "\n".join(result_lines)
644653

tests/test_languages/test_java/test_instrumentation.py

Lines changed: 25 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -136,7 +136,7 @@ def test_instrument_behavior_mode_simple(self, tmp_path: Path):
136136
137137
public class CalculatorTest__perfinstrumented {
138138
@Test
139-
@Timeout(30)
139+
@Timeout(value = 30, threadMode = Timeout.ThreadMode.SEPARATE_THREAD)
140140
public void testAdd() {
141141
// Codeflash behavior instrumentation
142142
int _cf_loop1 = Integer.parseInt(System.getenv("CODEFLASH_LOOP_INDEX"));
@@ -252,7 +252,7 @@ def test_instrument_behavior_mode_assert_throws_expression_lambda(self, tmp_path
252252
253253
public class FibonacciTest__perfinstrumented {
254254
@Test
255-
@Timeout(30)
255+
@Timeout(value = 30, threadMode = Timeout.ThreadMode.SEPARATE_THREAD)
256256
void testNegativeInput_ThrowsIllegalArgumentException() {
257257
// Codeflash behavior instrumentation
258258
int _cf_loop1 = Integer.parseInt(System.getenv("CODEFLASH_LOOP_INDEX"));
@@ -268,7 +268,7 @@ def test_instrument_behavior_mode_assert_throws_expression_lambda(self, tmp_path
268268
}
269269
270270
@Test
271-
@Timeout(30)
271+
@Timeout(value = 30, threadMode = Timeout.ThreadMode.SEPARATE_THREAD)
272272
void testZeroInput_ReturnsZero() {
273273
// Codeflash behavior instrumentation
274274
int _cf_loop2 = Integer.parseInt(System.getenv("CODEFLASH_LOOP_INDEX"));
@@ -383,7 +383,7 @@ def test_instrument_behavior_mode_assert_throws_block_lambda(self, tmp_path: Pat
383383
384384
public class FibonacciTest__perfinstrumented {
385385
@Test
386-
@Timeout(30)
386+
@Timeout(value = 30, threadMode = Timeout.ThreadMode.SEPARATE_THREAD)
387387
void testNegativeInput_ThrowsIllegalArgumentException() {
388388
// Codeflash behavior instrumentation
389389
int _cf_loop1 = Integer.parseInt(System.getenv("CODEFLASH_LOOP_INDEX"));
@@ -401,7 +401,7 @@ def test_instrument_behavior_mode_assert_throws_block_lambda(self, tmp_path: Pat
401401
}
402402
403403
@Test
404-
@Timeout(30)
404+
@Timeout(value = 30, threadMode = Timeout.ThreadMode.SEPARATE_THREAD)
405405
void testZeroInput_ReturnsZero() {
406406
// Codeflash behavior instrumentation
407407
int _cf_loop2 = Integer.parseInt(System.getenv("CODEFLASH_LOOP_INDEX"));
@@ -501,7 +501,7 @@ def test_instrument_performance_mode_simple(self, tmp_path: Path):
501501
502502
public class CalculatorTest__perfonlyinstrumented {
503503
@Test
504-
@Timeout(30)
504+
@Timeout(value = 30, threadMode = Timeout.ThreadMode.SEPARATE_THREAD)
505505
public void testAdd() {
506506
// Codeflash timing instrumentation with inner loop for JIT warmup
507507
int _cf_outerLoop1 = Integer.parseInt(System.getenv("CODEFLASH_LOOP_INDEX"));
@@ -575,7 +575,7 @@ def test_instrument_performance_mode_multiple_tests(self, tmp_path: Path):
575575
576576
public class MathTest__perfonlyinstrumented {
577577
@Test
578-
@Timeout(30)
578+
@Timeout(value = 30, threadMode = Timeout.ThreadMode.SEPARATE_THREAD)
579579
public void testAdd() {
580580
// Codeflash timing instrumentation with inner loop for JIT warmup
581581
int _cf_outerLoop1 = Integer.parseInt(System.getenv("CODEFLASH_LOOP_INDEX"));
@@ -604,7 +604,7 @@ def test_instrument_performance_mode_multiple_tests(self, tmp_path: Path):
604604
}
605605
606606
@Test
607-
@Timeout(30)
607+
@Timeout(value = 30, threadMode = Timeout.ThreadMode.SEPARATE_THREAD)
608608
public void testSubtract() {
609609
// Codeflash timing instrumentation with inner loop for JIT warmup
610610
int _cf_outerLoop2 = Integer.parseInt(System.getenv("CODEFLASH_LOOP_INDEX"));
@@ -683,7 +683,7 @@ def test_instrument_preserves_annotations(self, tmp_path: Path):
683683
684684
public class ServiceTest__perfonlyinstrumented {
685685
@Test
686-
@Timeout(30)
686+
@Timeout(value = 30, threadMode = Timeout.ThreadMode.SEPARATE_THREAD)
687687
@DisplayName("Test service call")
688688
public void testService() {
689689
// Codeflash timing instrumentation with inner loop for JIT warmup
@@ -714,7 +714,7 @@ def test_instrument_preserves_annotations(self, tmp_path: Path):
714714
715715
@Disabled
716716
@Test
717-
@Timeout(30)
717+
@Timeout(value = 30, threadMode = Timeout.ThreadMode.SEPARATE_THREAD)
718718
public void testDisabled() {
719719
service.other();
720720
}
@@ -1287,7 +1287,7 @@ def test_instrument_generated_test_behavior_mode(self):
12871287
12881288
public class CalculatorTest__perfinstrumented {
12891289
@Test
1290-
@Timeout(30)
1290+
@Timeout(value = 30, threadMode = Timeout.ThreadMode.SEPARATE_THREAD)
12911291
public void testAdd() {
12921292
// Codeflash behavior instrumentation
12931293
int _cf_loop1 = Integer.parseInt(System.getenv("CODEFLASH_LOOP_INDEX"));
@@ -1562,15 +1562,15 @@ def test_instrumented_code_has_balanced_braces(self, tmp_path: Path):
15621562
15631563
public class BraceTest__perfonlyinstrumented {
15641564
@Test
1565-
@Timeout(30)
1565+
@Timeout(value = 30, threadMode = Timeout.ThreadMode.SEPARATE_THREAD)
15661566
public void testOne() {
15671567
if (true) {
15681568
doSomething();
15691569
}
15701570
}
15711571
15721572
@Test
1573-
@Timeout(30)
1573+
@Timeout(value = 30, threadMode = Timeout.ThreadMode.SEPARATE_THREAD)
15741574
public void testTwo() {
15751575
// Codeflash timing instrumentation with inner loop for JIT warmup
15761576
int _cf_outerLoop2 = Integer.parseInt(System.getenv("CODEFLASH_LOOP_INDEX"));
@@ -1651,7 +1651,7 @@ def test_instrumented_code_preserves_imports(self, tmp_path: Path):
16511651
16521652
public class ImportTest__perfonlyinstrumented {
16531653
@Test
1654-
@Timeout(30)
1654+
@Timeout(value = 30, threadMode = Timeout.ThreadMode.SEPARATE_THREAD)
16551655
public void testCollections() {
16561656
// Codeflash timing instrumentation with inner loop for JIT warmup
16571657
int _cf_outerLoop1 = Integer.parseInt(System.getenv("CODEFLASH_LOOP_INDEX"));
@@ -1723,7 +1723,7 @@ def test_empty_test_method(self, tmp_path: Path):
17231723
17241724
public class EmptyTest__perfonlyinstrumented {
17251725
@Test
1726-
@Timeout(30)
1726+
@Timeout(value = 30, threadMode = Timeout.ThreadMode.SEPARATE_THREAD)
17271727
public void testEmpty() {
17281728
}
17291729
}
@@ -1773,7 +1773,7 @@ def test_test_with_nested_braces(self, tmp_path: Path):
17731773
17741774
public class NestedTest__perfonlyinstrumented {
17751775
@Test
1776-
@Timeout(30)
1776+
@Timeout(value = 30, threadMode = Timeout.ThreadMode.SEPARATE_THREAD)
17771777
public void testNested() {
17781778
// Codeflash timing instrumentation with inner loop for JIT warmup
17791779
int _cf_outerLoop1 = Integer.parseInt(System.getenv("CODEFLASH_LOOP_INDEX"));
@@ -1857,15 +1857,15 @@ class InnerTests {
18571857
18581858
public class InnerClassTest__perfonlyinstrumented {
18591859
@Test
1860-
@Timeout(30)
1860+
@Timeout(value = 30, threadMode = Timeout.ThreadMode.SEPARATE_THREAD)
18611861
public void testOuter() {
18621862
outerMethod();
18631863
}
18641864
18651865
@Nested
18661866
class InnerTests {
18671867
@Test
1868-
@Timeout(30)
1868+
@Timeout(value = 30, threadMode = Timeout.ThreadMode.SEPARATE_THREAD)
18691869
public void testInner() {
18701870
innerMethod();
18711871
}
@@ -1927,7 +1927,7 @@ def test_instrument_with_cjk_in_string_literal(self, tmp_path: Path):
19271927
'\n'
19281928
'public class Utf8Test__perfonlyinstrumented {\n'
19291929
' @Test\n'
1930-
' @Timeout(30)\n'
1930+
' @Timeout(value = 30, threadMode = Timeout.ThreadMode.SEPARATE_THREAD)\n'
19311931
' public void testWithCjk() {\n'
19321932
' // Codeflash timing instrumentation with inner loop for JIT warmup\n'
19331933
' int _cf_outerLoop1 = Integer.parseInt(System.getenv("CODEFLASH_LOOP_INDEX"));\n'
@@ -2003,7 +2003,7 @@ def test_instrument_with_multibyte_in_comment(self, tmp_path: Path):
20032003
'\n'
20042004
'public class AccentTest__perfonlyinstrumented {\n'
20052005
' @Test\n'
2006-
' @Timeout(30)\n'
2006+
' @Timeout(value = 30, threadMode = Timeout.ThreadMode.SEPARATE_THREAD)\n'
20072007
' public void testWithAccent() {\n'
20082008
' // Codeflash timing instrumentation with inner loop for JIT warmup\n'
20092009
' int _cf_outerLoop1 = Integer.parseInt(System.getenv("CODEFLASH_LOOP_INDEX"));\n'
@@ -2303,7 +2303,7 @@ def test_run_and_parse_performance_mode(self, java_project):
23032303
23042304
public class MathUtilsTest__perfonlyinstrumented {
23052305
@Test
2306-
@Timeout(30)
2306+
@Timeout(value = 30, threadMode = Timeout.ThreadMode.SEPARATE_THREAD)
23072307
public void testMultiply() {
23082308
// Codeflash timing instrumentation with inner loop for JIT warmup
23092309
int _cf_outerLoop1 = Integer.parseInt(System.getenv("CODEFLASH_LOOP_INDEX"));
@@ -2686,7 +2686,7 @@ def test_behavior_mode_writes_to_sqlite(self, java_project):
26862686
26872687
public class CounterTest__perfinstrumented {
26882688
@Test
2689-
@Timeout(30)
2689+
@Timeout(value = 30, threadMode = Timeout.ThreadMode.SEPARATE_THREAD)
26902690
public void testIncrement() {
26912691
// Codeflash behavior instrumentation
26922692
int _cf_loop1 = Integer.parseInt(System.getenv("CODEFLASH_LOOP_INDEX"));
@@ -2909,7 +2909,7 @@ def test_performance_mode_inner_loop_timing_markers(self, java_project):
29092909
29102910
public class FibonacciTest__perfonlyinstrumented {
29112911
@Test
2912-
@Timeout(30)
2912+
@Timeout(value = 30, threadMode = Timeout.ThreadMode.SEPARATE_THREAD)
29132913
public void testFib() {
29142914
// Codeflash timing instrumentation with inner loop for JIT warmup
29152915
int _cf_outerLoop1 = Integer.parseInt(System.getenv("CODEFLASH_LOOP_INDEX"));
@@ -3183,7 +3183,7 @@ def test_time_correction_instrumentation(self, java_project):
31833183
31843184
public class SpinWaitTest__perfonlyinstrumented {
31853185
@Test
3186-
@Timeout(30)
3186+
@Timeout(value = 30, threadMode = Timeout.ThreadMode.SEPARATE_THREAD)
31873187
public void testSpinShort() {
31883188
// Codeflash timing instrumentation with inner loop for JIT warmup
31893189
int _cf_outerLoop1 = Integer.parseInt(System.getenv("CODEFLASH_LOOP_INDEX"));
@@ -3212,7 +3212,7 @@ def test_time_correction_instrumentation(self, java_project):
32123212
}
32133213
32143214
@Test
3215-
@Timeout(30)
3215+
@Timeout(value = 30, threadMode = Timeout.ThreadMode.SEPARATE_THREAD)
32163216
public void testSpinLong() {
32173217
// Codeflash timing instrumentation with inner loop for JIT warmup
32183218
int _cf_outerLoop2 = Integer.parseInt(System.getenv("CODEFLASH_LOOP_INDEX"));

0 commit comments

Comments
 (0)