diff --git a/codeflash/languages/java/instrumentation.py b/codeflash/languages/java/instrumentation.py index 1655221ab..097cb43e9 100644 --- a/codeflash/languages/java/instrumentation.py +++ b/codeflash/languages/java/instrumentation.py @@ -811,12 +811,12 @@ def instrument_generated_java_test( Instrumented test source code. """ + if not test_code or not test_code.strip(): + return test_code + from codeflash.languages.java.remove_asserts import transform_java_assertions - # For behavior mode, remove assertions and capture function return values - # This converts the generated test into a regression test that captures outputs - if mode == "behavior": - test_code = transform_java_assertions(test_code, function_name, qualified_name) + test_code = transform_java_assertions(test_code, function_name, qualified_name) # Extract class name from the test code # Use pattern that starts at beginning of line to avoid matching words in comments @@ -827,14 +827,8 @@ def instrument_generated_java_test( original_class_name = class_match.group(1) - # For performance mode, add timing instrumentation - # Use original class name (without suffix) in timing markers for consistency with Python if mode == "performance": - # Rename class based on mode - if mode == "behavior": - new_class_name = f"{original_class_name}__perfinstrumented" - else: - new_class_name = f"{original_class_name}__perfonlyinstrumented" + new_class_name = f"{original_class_name}__perfonlyinstrumented" # Rename all references to the original class name in the source. # This includes the class declaration, return types, constructor calls, etc. @@ -852,6 +846,8 @@ def instrument_generated_java_test( function_to_optimize=function_to_optimize, test_class_name=original_class_name, ) + else: + modified_code = test_code logger.debug("Instrumented generated Java test for %s (mode=%s)", function_name, mode) return modified_code diff --git a/codeflash/languages/java/remove_asserts.py b/codeflash/languages/java/remove_asserts.py index 1f1c02cdb..7ae266e6f 100644 --- a/codeflash/languages/java/remove_asserts.py +++ b/codeflash/languages/java/remove_asserts.py @@ -213,34 +213,24 @@ class JavaAssertTransformer: if not assertions: return source - # Filter to only assertions that contain target calls - assertions_with_targets = [a for a in assertions if a.target_calls or a.is_exception_assertion] - - if not assertions_with_targets: - return source - # Sort by position (forward order) to assign counter numbers in source order - assertions_with_targets.sort(key=lambda a: a.start_pos) + assertions.sort(key=lambda a: a.start_pos) # Filter out nested assertions (e.g., assertEquals inside assertAll) - # An assertion is nested if it's completely contained within another assertion non_nested: list[AssertionMatch] = [] - for i, assertion in enumerate(assertions_with_targets): + for i, assertion in enumerate(assertions): is_nested = False - for j, other in enumerate(assertions_with_targets): + for j, other in enumerate(assertions): if i != j: - # Check if 'assertion' is nested inside 'other' if other.start_pos <= assertion.start_pos and assertion.end_pos <= other.end_pos: is_nested = True break if not is_nested: non_nested.append(assertion) - assertions_with_targets = non_nested - # Pre-compute all replacements with correct counter values replacements: list[tuple[int, int, str]] = [] - for assertion in assertions_with_targets: + for assertion in non_nested: replacement = self._generate_replacement(assertion) replacements.append((assertion.start_pos, assertion.end_pos, replacement)) @@ -822,8 +812,7 @@ class JavaAssertTransformer: return self._generate_exception_replacement(assertion) if not assertion.target_calls: - # No target calls found, just comment out the assertion - return f"{assertion.leading_whitespace}// Removed assertion: no target calls found" + return "" # Generate capture statements for each target call replacements = [] diff --git a/tests/test_java_assertion_removal.py b/tests/test_java_assertion_removal.py index a1dcd4dd7..a2ec11665 100644 --- a/tests/test_java_assertion_removal.py +++ b/tests/test_java_assertion_removal.py @@ -286,7 +286,6 @@ void testCalculator() { @Test void testCalculator() { Object _cf_result1 = calculator.add(2, 3); - assertEquals(6, calculator.multiply(2, 3)); }""" result = transform_java_assertions(source, "add") assert result == expected @@ -550,8 +549,13 @@ void testWithVariables() { int actual = calculator.fibonacci(10); assertEquals(expected, actual); }""" - # fibonacci is assigned to 'actual', not in the assertion - no transformation - expected = source + # Variable declarations are preserved, but assertEquals is removed (all assertions removed) + expected = """\ +@Test +void testWithVariables() { + int expected = 55; + int actual = calculator.fibonacci(10); +}""" result = transform_java_assertions(source, "fibonacci") assert result == expected @@ -670,8 +674,11 @@ void testNoAssertions() { void testOther() { assertEquals(5, helper.compute(3)); }""" - # No transformation since target function is not in the assertion - expected = source + # All assertions are removed regardless of target function + expected = """\ +@Test +void testOther() { +}""" result = transform_java_assertions(source, "fibonacci") assert result == expected @@ -912,9 +919,13 @@ void testBasicCompoundInterest() { assertNotNull(result); assertTrue(result.contains(".")); }""" - # assertNotNull(result) and assertTrue(result.contains(".")) don't contain the target function - # so they remain unchanged, and the variable assignment is also preserved - expected = source + # All assertions are removed; variable assignment is preserved + expected = """\ +@Test +@DisplayName("should calculate compound interest for basic case") +void testBasicCompoundInterest() { + String result = calculator.calculateCompoundInterest(1000.0, 0.05, 1, 12); +}""" result = transform_java_assertions(source, "calculateCompoundInterest") assert result == expected @@ -1018,13 +1029,12 @@ void testSynchronizedBlock() { assertTrue(cache.containsKey("key")); } }""" + # All assertions are removed; target-containing ones get Object capture expected = """\ @Test void testSynchronizedBlock() { synchronized (cache) { Object _cf_result1 = cache.size(); - assertNotNull(cache.get("key")); - assertTrue(cache.containsKey("key")); } }""" result = transform_java_assertions(source, "size") @@ -1210,6 +1220,8 @@ void testCircularBufferOperations() { assertFalse(buffer.isEmpty()); assertTrue(buffer.put(2)); }""" + # All assertions are removed; target-containing ones get Object capture, + # non-target assertions (assertTrue(buffer.put(2))) are deleted entirely expected = """\ @Test void testCircularBufferOperations() { @@ -1217,25 +1229,9 @@ void testCircularBufferOperations() { Object _cf_result1 = buffer.isEmpty(); buffer.put(1); Object _cf_result2 = buffer.isEmpty(); - Object _cf_result3 = buffer.put(2); }""" result = transform_java_assertions(source, "isEmpty") - # isEmpty is target for assertTrue/assertFalse; but put is NOT the target - # so only isEmpty calls inside assertions are transformed - # Actually: assertTrue(buffer.put(2)) also contains a non-target call - # Let's verify what actually happens - # put is not "isEmpty", so assertTrue(buffer.put(2)) has no target call -> untouched - expected_corrected = """\ -@Test -void testCircularBufferOperations() { - CircularBuffer buffer = new CircularBuffer<>(3); - Object _cf_result1 = buffer.isEmpty(); - buffer.put(1); - Object _cf_result2 = buffer.isEmpty(); - assertTrue(buffer.put(2)); -}""" - result = transform_java_assertions(source, "isEmpty") - assert result == expected_corrected + assert result == expected def test_concurrent_assertion_with_assertj(self): """AssertJ assertion on a synchronized method call is correctly transformed.""" @@ -1310,12 +1306,12 @@ void testNegativeInput() { ); assertEquals("Negative input not allowed", exception.getMessage()); }""" + # assertThrows becomes try/catch, and assertEquals after it is also removed expected = """\ @Test void testNegativeInput() { IllegalArgumentException exception = null; try { calculator.fibonacci(-1); } catch (IllegalArgumentException _cf_caught1) { exception = _cf_caught1; } catch (Exception _cf_ignored1) {} - assertEquals("Negative input not allowed", exception.getMessage()); }""" result = transform_java_assertions(source, "fibonacci") assert result == expected @@ -1330,12 +1326,12 @@ void testInvalidOperation() { }); assertEquals("Division by zero", ex.getMessage()); }""" + # assertThrows becomes try/catch, and assertEquals after it is also removed expected = """\ @Test void testInvalidOperation() { ArithmeticException ex = null; try { calculator.divide(10, 0); } catch (ArithmeticException _cf_caught1) { ex = _cf_caught1; } catch (Exception _cf_ignored1) {} - assertEquals("Division by zero", ex.getMessage()); }""" result = transform_java_assertions(source, "divide") assert result == expected @@ -1348,12 +1344,12 @@ void testGenericException() { Exception e = assertThrows(Exception.class, () -> processor.process(null)); assertNotNull(e.getMessage()); }""" + # assertThrows becomes try/catch, and assertNotNull after it is also removed expected = """\ @Test void testGenericException() { Exception e = null; try { processor.process(null); } catch (Exception _cf_caught1) { e = _cf_caught1; } catch (Exception _cf_ignored1) {} - assertNotNull(e.getMessage()); }""" result = transform_java_assertions(source, "process") assert result == expected @@ -1387,13 +1383,13 @@ void testComplexException() { ); assertTrue(exception.getMessage().contains("not initialized")); }""" + # assertThrows becomes try/catch, and assertTrue after it is also removed expected = """\ @Test void testComplexException() { IllegalStateException exception = null; try { processor.initialize(); processor.execute(); } catch (IllegalStateException _cf_caught1) { exception = _cf_caught1; } catch (Exception _cf_ignored1) {} - assertTrue(exception.getMessage().contains("not initialized")); }""" result = transform_java_assertions(source, "execute") assert result == expected diff --git a/tests/test_languages/test_java/test_instrumentation.py b/tests/test_languages/test_java/test_instrumentation.py index 30afdac07..7b6f9ea9b 100644 --- a/tests/test_languages/test_java/test_instrumentation.py +++ b/tests/test_languages/test_java/test_instrumentation.py @@ -295,7 +295,7 @@ public class CalculatorTest__perfonlyinstrumented { long _cf_start1 = System.nanoTime(); try { Calculator calc = new Calculator(); - assertEquals(4, calc.add(2, 2)); + Object _cf_result1 = calc.add(2, 2); } finally { long _cf_end1 = System.nanoTime(); long _cf_dur1 = _cf_end1 - _cf_start1; @@ -360,7 +360,6 @@ public class MathTest__perfonlyinstrumented { System.out.println("!$######" + _cf_mod1 + ":" + _cf_cls1 + ":" + _cf_fn1 + ":" + _cf_loop1 + ":" + _cf_i1 + "######$!"); long _cf_start1 = System.nanoTime(); try { - assertEquals(4, add(2, 2)); } finally { long _cf_end1 = System.nanoTime(); long _cf_dur1 = _cf_end1 - _cf_start1; @@ -382,7 +381,6 @@ public class MathTest__perfonlyinstrumented { System.out.println("!$######" + _cf_mod2 + ":" + _cf_cls2 + ":" + _cf_fn2 + ":" + _cf_loop2 + ":" + _cf_i2 + "######$!"); long _cf_start2 = System.nanoTime(); try { - assertEquals(0, subtract(2, 2)); } finally { long _cf_end2 = System.nanoTime(); long _cf_dur2 = _cf_end2 - _cf_start2; @@ -1256,7 +1254,7 @@ public class ImportTest__perfonlyinstrumented { long _cf_start1 = System.nanoTime(); try { List list = new ArrayList<>(); - assertEquals(0, list.size()); + Object _cf_result1 = list.size(); } finally { long _cf_end1 = System.nanoTime(); long _cf_dur1 = _cf_end1 - _cf_start1; diff --git a/tests/test_languages/test_java/test_remove_asserts.py b/tests/test_languages/test_java/test_remove_asserts.py new file mode 100644 index 000000000..022b73173 --- /dev/null +++ b/tests/test_languages/test_java/test_remove_asserts.py @@ -0,0 +1,1416 @@ +"""Tests for Java assertion removal transformer. + +Tests the transform_java_assertions function with exact string equality assertions +to ensure assertions are correctly removed while preserving target function calls. + +Covers: +- JUnit 4 assertions (org.junit.Assert.*) +- JUnit 5 assertions (org.junit.jupiter.api.Assertions.*) +- AssertJ fluent assertions (assertThat(...).isEqualTo(...)) +- Hamcrest assertions (assertThat(actual, is(expected))) +- assertThrows / assertDoesNotThrow with lambdas +- Variable assignments from assertThrows +- Multiple target calls in a single assertion +- Assertions without target calls (should be removed) +- Nested assertions (assertAll) +- Edge cases: static calls, qualified calls, method chaining +""" + +from codeflash.languages.java.remove_asserts import ( + JavaAssertTransformer, + transform_java_assertions, +) + + +class TestJUnit4Assertions: + """Tests for JUnit 4 style assertions (org.junit.Assert.*).""" + + def test_assertfalse_with_message(self): + source = """\ +import org.junit.Test; +import static org.junit.Assert.*; + +public class BitSetTest { + @Test + public void testGet_IndexZero_ReturnsFalse() { + assertFalse("New BitSet should have bit 0 unset", instance.get(0)); + } +} +""" + result = transform_java_assertions(source, "get") + assert 'assertFalse("New BitSet should have bit 0 unset", instance.get(0));' not in result + assert "Object _cf_result1 = instance.get(0);" in result + + def test_asserttrue_with_message(self): + source = """\ +import org.junit.Test; +import static org.junit.Assert.*; + +public class BitSetTest { + @Test + public void testGet_SetBit_DetectedTrue() { + assertTrue("Bit at index 67 should be detected as set", bs.get(67)); + } +} +""" + result = transform_java_assertions(source, "get") + assert 'assertTrue("Bit at index 67 should be detected as set", bs.get(67));' not in result + assert "Object _cf_result1 = bs.get(67);" in result + + def test_assertequals_with_static_call(self): + source = """\ +import org.junit.Test; +import static org.junit.Assert.*; + +public class FibonacciTest { + @Test + public void testFibonacci() { + assertEquals(55, Fibonacci.fibonacci(10)); + } +} +""" + result = transform_java_assertions(source, "fibonacci") + assert "assertEquals(55, Fibonacci.fibonacci(10));" not in result + assert "Object _cf_result1 = Fibonacci.fibonacci(10);" in result + + def test_assertequals_with_instance_call(self): + source = """\ +import org.junit.Test; +import static org.junit.Assert.*; + +public class CalculatorTest { + @Test + public void testAdd() { + Calculator calc = new Calculator(); + assertEquals(4, calc.add(2, 2)); + } +} +""" + result = transform_java_assertions(source, "add") + assert "assertEquals(4, calc.add(2, 2));" not in result + assert "Object _cf_result1 = calc.add(2, 2);" in result + # Non-assertion code should be preserved + assert "Calculator calc = new Calculator();" in result + + def test_assertnull(self): + source = """\ +import org.junit.Test; +import static org.junit.Assert.*; + +public class ParserTest { + @Test + public void testParseNull() { + assertNull(parser.parse(null)); + } +} +""" + result = transform_java_assertions(source, "parse") + assert "assertNull(parser.parse(null));" not in result + assert "Object _cf_result1 = parser.parse(null);" in result + + def test_assertnotnull(self): + source = """\ +import org.junit.Test; +import static org.junit.Assert.*; + +public class FibonacciTest { + @Test + public void testFibonacciSequence() { + assertNotNull(Fibonacci.fibonacciSequence(5)); + } +} +""" + result = transform_java_assertions(source, "fibonacciSequence") + assert "assertNotNull(Fibonacci.fibonacciSequence(5));" not in result + assert "Object _cf_result1 = Fibonacci.fibonacciSequence(5);" in result + + def test_assertnotequals(self): + source = """\ +import org.junit.Test; +import static org.junit.Assert.*; + +public class CalculatorTest { + @Test + public void testSubtract() { + assertNotEquals(0, calc.subtract(5, 3)); + } +} +""" + result = transform_java_assertions(source, "subtract") + assert "assertNotEquals(0, calc.subtract(5, 3));" not in result + assert "Object _cf_result1 = calc.subtract(5, 3);" in result + + def test_assertarrayequals(self): + source = """\ +import org.junit.Test; +import static org.junit.Assert.*; + +public class FibonacciTest { + @Test + public void testFibonacciSequence() { + assertArrayEquals(new long[]{0, 1, 1, 2, 3}, Fibonacci.fibonacciSequence(5)); + } +} +""" + result = transform_java_assertions(source, "fibonacciSequence") + assert "assertArrayEquals(new long[]{0, 1, 1, 2, 3}, Fibonacci.fibonacciSequence(5));" not in result + assert "Object _cf_result1 = Fibonacci.fibonacciSequence(5);" in result + + def test_qualified_assert_call(self): + """Test Assert.assertEquals (JUnit 4 qualified).""" + source = """\ +import org.junit.Test; +import org.junit.Assert; + +public class CalculatorTest { + @Test + public void testAdd() { + Assert.assertEquals(4, calc.add(2, 2)); + } +} +""" + result = transform_java_assertions(source, "add") + assert "Assert.assertEquals(4, calc.add(2, 2));" not in result + assert "Object _cf_result1 = calc.add(2, 2);" in result + + def test_expected_exception_annotation(self): + """Test that @Test(expected=...) tests with target calls are handled.""" + source = """\ +import org.junit.Test; +import static org.junit.Assert.*; + +public class BitSetTest { + @Test(expected = ArrayIndexOutOfBoundsException.class) + public void testGet_NegativeIndex_Throws() { + instance.get(-1); + } +} +""" + # No assertions to remove here, but the call should remain + result = transform_java_assertions(source, "get") + assert "instance.get(-1);" in result + + +class TestJUnit5Assertions: + """Tests for JUnit 5 style assertions (org.junit.jupiter.api.Assertions.*).""" + + def test_assertequals_static_import(self): + source = """\ +import org.junit.jupiter.api.Test; +import static org.junit.jupiter.api.Assertions.*; + +public class FibonacciTest { + @Test + void testFibonacci() { + assertEquals(0, Fibonacci.fibonacci(0)); + assertEquals(1, Fibonacci.fibonacci(1)); + assertEquals(55, Fibonacci.fibonacci(10)); + } +} +""" + result = transform_java_assertions(source, "fibonacci") + assert "assertEquals" not in result + assert "Object _cf_result1 = Fibonacci.fibonacci(0);" in result + assert "Object _cf_result2 = Fibonacci.fibonacci(1);" in result + assert "Object _cf_result3 = Fibonacci.fibonacci(10);" in result + + def test_assertequals_qualified(self): + """Test Assertions.assertEquals (JUnit 5 qualified).""" + source = """\ +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.Assertions; + +public class FibonacciTest { + @Test + void testFibonacci() { + Assertions.assertEquals(55, Fibonacci.fibonacci(10)); + } +} +""" + result = transform_java_assertions(source, "fibonacci") + assert "Assertions.assertEquals(55, Fibonacci.fibonacci(10));" not in result + assert "Object _cf_result1 = Fibonacci.fibonacci(10);" in result + + def test_assertthrows_expression_lambda(self): + source = """\ +import org.junit.jupiter.api.Test; +import static org.junit.jupiter.api.Assertions.*; + +public class FibonacciTest { + @Test + void testNegativeThrows() { + assertThrows(IllegalArgumentException.class, () -> Fibonacci.fibonacci(-1)); + } +} +""" + result = transform_java_assertions(source, "fibonacci") + assert "assertThrows" not in result + assert "try {" in result + assert "Fibonacci.fibonacci(-1);" in result + assert "catch (Exception" in result + + def test_assertthrows_block_lambda(self): + source = """\ +import org.junit.jupiter.api.Test; +import static org.junit.jupiter.api.Assertions.*; + +public class FibonacciTest { + @Test + void testNegativeThrows() { + assertThrows(IllegalArgumentException.class, () -> { + Fibonacci.fibonacci(-1); + }); + } +} +""" + result = transform_java_assertions(source, "fibonacci") + assert "assertThrows" not in result + assert "try {" in result + assert "Fibonacci.fibonacci(-1);" in result + assert "catch (Exception" in result + + def test_assertthrows_assigned_to_variable(self): + source = """\ +import org.junit.jupiter.api.Test; +import static org.junit.jupiter.api.Assertions.*; + +public class FibonacciTest { + @Test + void testNegativeThrows() { + IllegalArgumentException ex = assertThrows(IllegalArgumentException.class, () -> Fibonacci.fibonacci(-1)); + } +} +""" + result = transform_java_assertions(source, "fibonacci") + assert "assertThrows" not in result + assert "IllegalArgumentException ex = null;" in result + assert "Fibonacci.fibonacci(-1);" in result + assert "_cf_caught" in result + assert "ex = _cf_caught" in result + + def test_assertdoesnotthrow(self): + source = """\ +import org.junit.jupiter.api.Test; +import static org.junit.jupiter.api.Assertions.*; + +public class FibonacciTest { + @Test + void testDoesNotThrow() { + assertDoesNotThrow(() -> Fibonacci.fibonacci(10)); + } +} +""" + result = transform_java_assertions(source, "fibonacci") + assert "assertDoesNotThrow" not in result + assert "try {" in result + assert "Fibonacci.fibonacci(10);" in result + + def test_assertsame(self): + source = """\ +import org.junit.jupiter.api.Test; +import static org.junit.jupiter.api.Assertions.*; + +public class CacheTest { + @Test + void testCacheSameInstance() { + assertSame(expected, cache.get("key")); + } +} +""" + result = transform_java_assertions(source, "get") + assert "assertSame" not in result + assert 'Object _cf_result1 = cache.get("key");' in result + + def test_asserttrue_boolean_call(self): + source = """\ +import org.junit.jupiter.api.Test; +import static org.junit.jupiter.api.Assertions.*; + +public class FibonacciTest { + @Test + void testIsFibonacci() { + assertTrue(Fibonacci.isFibonacci(5)); + } +} +""" + result = transform_java_assertions(source, "isFibonacci") + assert "assertTrue(Fibonacci.isFibonacci(5));" not in result + assert "Object _cf_result1 = Fibonacci.isFibonacci(5);" in result + + def test_assertfalse_boolean_call(self): + source = """\ +import org.junit.jupiter.api.Test; +import static org.junit.jupiter.api.Assertions.*; + +public class FibonacciTest { + @Test + void testIsNotFibonacci() { + assertFalse(Fibonacci.isFibonacci(4)); + } +} +""" + result = transform_java_assertions(source, "isFibonacci") + assert "assertFalse(Fibonacci.isFibonacci(4));" not in result + assert "Object _cf_result1 = Fibonacci.isFibonacci(4);" in result + + +class TestAssertJFluent: + """Tests for AssertJ fluent style assertions.""" + + def test_assertthat_isequalto(self): + source = """\ +import org.junit.jupiter.api.Test; +import static org.assertj.core.api.Assertions.assertThat; + +public class FibonacciTest { + @Test + void testFibonacci() { + assertThat(Fibonacci.fibonacci(10)).isEqualTo(55); + } +} +""" + result = transform_java_assertions(source, "fibonacci") + assert "assertThat(Fibonacci.fibonacci(10)).isEqualTo(55);" not in result + assert "Object _cf_result1 = Fibonacci.fibonacci(10);" in result + + def test_assertthat_chained(self): + source = """\ +import org.junit.jupiter.api.Test; +import static org.assertj.core.api.Assertions.assertThat; + +public class ListTest { + @Test + void testGetItems() { + assertThat(store.getItems()).isNotNull().hasSize(3).contains("apple"); + } +} +""" + result = transform_java_assertions(source, "getItems") + assert 'assertThat(store.getItems()).isNotNull().hasSize(3).contains("apple");' not in result + assert "Object _cf_result1 = store.getItems();" in result + + def test_assertthat_isnull(self): + source = """\ +import org.junit.jupiter.api.Test; +import static org.assertj.core.api.Assertions.assertThat; + +public class ParserTest { + @Test + void testParseReturnsNull() { + assertThat(parser.parse("invalid")).isNull(); + } +} +""" + result = transform_java_assertions(source, "parse") + assert 'assertThat(parser.parse("invalid")).isNull();' not in result + assert 'Object _cf_result1 = parser.parse("invalid");' in result + + def test_assertthat_qualified(self): + """Test Assertions.assertThat (qualified call).""" + source = """\ +import org.junit.jupiter.api.Test; +import org.assertj.core.api.Assertions; + +public class CalcTest { + @Test + void testAdd() { + Assertions.assertThat(calc.add(1, 2)).isEqualTo(3); + } +} +""" + result = transform_java_assertions(source, "add") + assert "assertThat" not in result + assert "Object _cf_result1 = calc.add(1, 2);" in result + + +class TestHamcrestAssertions: + """Tests for Hamcrest style assertions.""" + + def test_hamcrest_assertthat_is(self): + source = """\ +import org.junit.Test; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.is; + +public class CalculatorTest { + @Test + public void testAdd() { + assertThat(calc.add(2, 3), is(5)); + } +} +""" + result = transform_java_assertions(source, "add") + assert "assertThat(calc.add(2, 3), is(5));" not in result + assert "Object _cf_result1 = calc.add(2, 3);" in result + + def test_hamcrest_qualified_assertthat(self): + source = """\ +import org.junit.Test; +import org.hamcrest.MatcherAssert; +import static org.hamcrest.Matchers.*; + +public class CalculatorTest { + @Test + public void testAdd() { + MatcherAssert.assertThat(calc.add(2, 3), equalTo(5)); + } +} +""" + result = transform_java_assertions(source, "add") + assert "assertThat" not in result + assert "Object _cf_result1 = calc.add(2, 3);" in result + + +class TestMultipleTargetCalls: + """Tests for assertions containing multiple target function calls.""" + + def test_multiple_calls_in_one_assertion(self): + source = """\ +import org.junit.jupiter.api.Test; +import static org.junit.jupiter.api.Assertions.*; + +public class FibonacciTest { + @Test + void testConsecutive() { + assertTrue(Fibonacci.areConsecutiveFibonacci(Fibonacci.fibonacci(5), Fibonacci.fibonacci(6))); + } +} +""" + result = transform_java_assertions(source, "fibonacci") + assert "assertTrue" not in result + assert "Object _cf_result1 = Fibonacci.fibonacci(5);" in result + assert "Object _cf_result2 = Fibonacci.fibonacci(6);" in result + + def test_multiple_assertions_in_one_method(self): + source = """\ +import org.junit.jupiter.api.Test; +import static org.junit.jupiter.api.Assertions.*; + +public class FibonacciTest { + @Test + void testMultiple() { + assertEquals(0, Fibonacci.fibonacci(0)); + assertEquals(1, Fibonacci.fibonacci(1)); + assertEquals(1, Fibonacci.fibonacci(2)); + assertEquals(2, Fibonacci.fibonacci(3)); + assertEquals(5, Fibonacci.fibonacci(5)); + } +} +""" + result = transform_java_assertions(source, "fibonacci") + assert "assertEquals" not in result + assert "Object _cf_result1 = Fibonacci.fibonacci(0);" in result + assert "Object _cf_result2 = Fibonacci.fibonacci(1);" in result + assert "Object _cf_result3 = Fibonacci.fibonacci(2);" in result + assert "Object _cf_result4 = Fibonacci.fibonacci(3);" in result + assert "Object _cf_result5 = Fibonacci.fibonacci(5);" in result + + +class TestNoTargetCalls: + """Tests for assertions that do NOT contain calls to the target function.""" + + def test_assertion_without_target_removed(self): + """Assertions not containing the target function should be removed.""" + source = """\ +import org.junit.jupiter.api.Test; +import static org.junit.jupiter.api.Assertions.*; + +public class SetupTest { + @Test + void testSetup() { + assertNotNull(config); + assertEquals(55, Fibonacci.fibonacci(10)); + } +} +""" + result = transform_java_assertions(source, "fibonacci") + # The assertNotNull without target call should be removed + assert "assertNotNull(config);" not in result + # The assertEquals with target call should be transformed + assert "Object _cf_result1 = Fibonacci.fibonacci(10);" in result + + def test_no_assertions_at_all(self): + """Source with no assertions should be returned unchanged.""" + source = """\ +import org.junit.jupiter.api.Test; + +public class FibonacciTest { + @Test + void testPrint() { + System.out.println(Fibonacci.fibonacci(10)); + } +} +""" + result = transform_java_assertions(source, "fibonacci") + assert result == source + + +class TestEdgeCases: + """Tests for edge cases and special scenarios.""" + + def test_empty_source(self): + result = transform_java_assertions("", "fibonacci") + assert result == "" + + def test_whitespace_only_source(self): + result = transform_java_assertions(" \n\n ", "fibonacci") + assert result == " \n\n " + + def test_multiline_assertion(self): + source = """\ +import org.junit.jupiter.api.Test; +import static org.junit.jupiter.api.Assertions.*; + +public class FibonacciTest { + @Test + void testFibonacci() { + assertEquals( + 55, + Fibonacci.fibonacci(10) + ); + } +} +""" + result = transform_java_assertions(source, "fibonacci") + assert "assertEquals" not in result + assert "Object _cf_result1 = Fibonacci.fibonacci(10);" in result + + def test_assertion_with_string_containing_parens(self): + source = """\ +import org.junit.jupiter.api.Test; +import static org.junit.jupiter.api.Assertions.*; + +public class ParserTest { + @Test + void testParse() { + assertEquals("result(1)", parser.parse("input(1)")); + } +} +""" + result = transform_java_assertions(source, "parse") + assert "assertEquals" not in result + assert 'Object _cf_result1 = parser.parse("input(1)");' in result + + def test_preserves_non_test_code(self): + """Non-assertion code like setup, variable declarations should be preserved.""" + source = """\ +import org.junit.jupiter.api.Test; +import static org.junit.jupiter.api.Assertions.*; + +public class FibonacciTest { + @Test + void testSequence() { + int n = 10; + long[] expected = {0, 1, 1, 2, 3, 5, 8, 13, 21, 34}; + assertArrayEquals(expected, Fibonacci.fibonacciSequence(n)); + } +} +""" + result = transform_java_assertions(source, "fibonacciSequence") + assert "int n = 10;" in result + assert "long[] expected = {0, 1, 1, 2, 3, 5, 8, 13, 21, 34};" in result + assert "Object _cf_result1 = Fibonacci.fibonacciSequence(n);" in result + + def test_nested_method_calls(self): + """Target function call nested inside another method call inside assertion.""" + source = """\ +import org.junit.jupiter.api.Test; +import static org.junit.jupiter.api.Assertions.*; + +public class FibonacciTest { + @Test + void testIndex() { + assertEquals(10, Fibonacci.fibonacciIndex(Fibonacci.fibonacci(10))); + } +} +""" + result = transform_java_assertions(source, "fibonacci") + assert "assertEquals" not in result + # Should capture the inner fibonacci call + assert "Object _cf_result1 = Fibonacci.fibonacci(10);" in result + + def test_chained_method_on_result(self): + """Target function call with chained method (e.g., result.toString()).""" + source = """\ +import org.junit.jupiter.api.Test; +import static org.junit.jupiter.api.Assertions.*; + +public class FibonacciTest { + @Test + void testUpTo() { + assertEquals(7, Fibonacci.fibonacciUpTo(20).size()); + } +} +""" + result = transform_java_assertions(source, "fibonacciUpTo") + assert "assertEquals" not in result + assert "Object _cf_result1 = Fibonacci.fibonacciUpTo(20);" in result + + +class TestBitSetLikeQuestDB: + """Tests modeled after the QuestDB BitSetTest pattern shown by the user. + + This covers the real-world scenario of JUnit 4 tests with message strings, + reflection-based setup, expected exceptions, and multiple assertion types. + """ + + BITSET_TEST_SOURCE = """\ +package io.questdb.std; + +import org.junit.Before; +import org.junit.Test; + +import java.lang.reflect.Field; + +import static org.junit.Assert.*; + +public class BitSetTest { + private BitSet instance; + + @Before + public void setUp() { + instance = new BitSet(); + } + + @Test + public void testGet_IndexZero_ReturnsFalse() { + assertFalse("New BitSet should have bit 0 unset", instance.get(0)); + } + + @Test + public void testGet_SpecificIndexWithinRange_ReturnsFalse() { + assertFalse("New BitSet should have bit 100 unset", instance.get(100)); + } + + @Test + public void testGet_LastIndexOfInitialRange_ReturnsFalse() { + int lastIndex = 16 * BitSet.BITS_PER_WORD - 1; + assertFalse("Last index of initial range should be unset", instance.get(lastIndex)); + } + + @Test + public void testGet_IndexBeyondAllocated_ReturnsFalse() { + int beyond = 16 * BitSet.BITS_PER_WORD; + assertFalse("Index beyond allocated range should return false", instance.get(beyond)); + } + + @Test(expected = ArrayIndexOutOfBoundsException.class) + public void testGet_NegativeIndex_ThrowsArrayIndexOutOfBoundsException() { + instance.get(-1); + } + + @Test + public void testGet_SetWordUsingReflection_DetectedTrue() throws Exception { + BitSet bs = new BitSet(128); + Field wordsField = BitSet.class.getDeclaredField("words"); + wordsField.setAccessible(true); + long[] words = new long[2]; + words[1] = 1L << 3; + wordsField.set(bs, words); + assertTrue("Bit at index 67 should be detected as set", bs.get(64 + 3)); + } + + @Test + public void testGet_LargeIndexDoesNotThrow_ReturnsFalse() { + assertFalse("Very large index should return false without throwing", instance.get(Integer.MAX_VALUE)); + } + + @Test + public void testGet_BitBoundaryWordEdge63_ReturnsFalse() { + assertFalse("Bit index 63 (end of first word) should be unset by default", instance.get(63)); + } + + @Test + public void testGet_BitBoundaryWordEdge64_ReturnsFalse() { + assertFalse("Bit index 64 (start of second word) should be unset by default", instance.get(64)); + } + + @Test + public void testGet_LargeBitSetLastIndex_ReturnsFalse() { + int nBits = 1_000_000; + BitSet big = new BitSet(nBits); + int last = nBits - 1; + assertFalse("Last bit of a large BitSet should be unset by default", big.get(last)); + } +} +""" + + def test_all_assertfalse_transformed(self): + result = transform_java_assertions(self.BITSET_TEST_SOURCE, "get") + # All assertFalse calls with target should be transformed + assert "Object _cf_result1 = instance.get(0);" in result + assert "Object _cf_result2 = instance.get(100);" in result + assert "Object _cf_result3 = instance.get(lastIndex);" in result + assert "Object _cf_result4 = instance.get(beyond);" in result + + def test_asserttrue_transformed(self): + result = transform_java_assertions(self.BITSET_TEST_SOURCE, "get") + assert "Object" in result + # assertTrue should also be transformed + assert "bs.get(64 + 3);" in result + + def test_setup_code_preserved(self): + result = transform_java_assertions(self.BITSET_TEST_SOURCE, "get") + assert "instance = new BitSet();" in result + assert "int lastIndex = 16 * BitSet.BITS_PER_WORD - 1;" in result + assert "int beyond = 16 * BitSet.BITS_PER_WORD;" in result + + def test_reflection_code_preserved(self): + result = transform_java_assertions(self.BITSET_TEST_SOURCE, "get") + assert 'Field wordsField = BitSet.class.getDeclaredField("words");' in result + assert "wordsField.setAccessible(true);" in result + assert "long[] words = new long[2];" in result + assert "words[1] = 1L << 3;" in result + assert "wordsField.set(bs, words);" in result + + def test_expected_exception_test_preserved(self): + result = transform_java_assertions(self.BITSET_TEST_SOURCE, "get") + # The expected-exception test has no assertion, just the call + assert "instance.get(-1);" in result + assert "@Test(expected = ArrayIndexOutOfBoundsException.class)" in result + + def test_package_and_imports_preserved(self): + result = transform_java_assertions(self.BITSET_TEST_SOURCE, "get") + assert "package io.questdb.std;" in result + assert "import org.junit.Before;" in result + assert "import org.junit.Test;" in result + assert "import java.lang.reflect.Field;" in result + + def test_class_structure_preserved(self): + result = transform_java_assertions(self.BITSET_TEST_SOURCE, "get") + assert "public class BitSetTest {" in result + assert "private BitSet instance;" in result + assert "@Before" in result + assert "public void setUp() {" in result + + def test_large_index_assertions_transformed(self): + result = transform_java_assertions(self.BITSET_TEST_SOURCE, "get") + assert "instance.get(Integer.MAX_VALUE);" in result + assert "instance.get(63);" in result + assert "instance.get(64);" in result + assert "big.get(last);" in result + + def test_no_assertfalse_remain(self): + """After transformation, no assertFalse with 'get' calls should remain.""" + result = transform_java_assertions(self.BITSET_TEST_SOURCE, "get") + import re + + # Find any remaining assertFalse/assertTrue that contain a .get( call + remaining = re.findall(r"assert(?:True|False)\(.*\.get\(", result) + assert remaining == [], f"Found untransformed assertions: {remaining}" + + +class TestTransformMethod: + """Tests for JavaAssertTransformer.transform() — each branch and code path.""" + + # --- Early returns --- + + def test_none_source_returns_unchanged(self): + """transform() returns empty string unchanged.""" + transformer = JavaAssertTransformer("fibonacci") + assert transformer.transform("") == "" + + def test_whitespace_only_returns_unchanged(self): + """transform() returns whitespace-only source unchanged.""" + transformer = JavaAssertTransformer("fibonacci") + ws = " \n\t\n " + assert transformer.transform(ws) == ws + + def test_no_assertions_found_returns_unchanged(self): + """Source with code but no assertions → _find_assertions returns [] → early return.""" + transformer = JavaAssertTransformer("fibonacci") + source = """\ +import org.junit.jupiter.api.Test; + +public class FibTest { + @Test + void test1() { + long result = Fibonacci.fibonacci(10); + System.out.println(result); + } +} +""" + result = transformer.transform(source) + assert result == source + assert transformer.invocation_counter == 0 + + def test_assertions_exist_but_no_target_calls_are_removed(self): + """Assertions found but none contain target function are removed (empty replacement).""" + transformer = JavaAssertTransformer("fibonacci") + source = """\ +import org.junit.jupiter.api.Test; +import static org.junit.jupiter.api.Assertions.*; + +public class FibTest { + @Test + void test1() { + assertEquals(4, calculator.add(2, 2)); + assertTrue(validator.isValid("x")); + } +} +""" + result = transformer.transform(source) + assert "assertEquals(4, calculator.add(2, 2));" not in result + assert 'assertTrue(validator.isValid("x"))' not in result + assert transformer.invocation_counter == 0 + + # --- Counter numbering in source order --- + + def test_counters_assigned_in_source_order(self): + """Counters _cf_result1, _cf_result2, etc. follow source position (top to bottom).""" + transformer = JavaAssertTransformer("fibonacci") + source = """\ +import org.junit.jupiter.api.Test; +import static org.junit.jupiter.api.Assertions.*; + +public class FibTest { + @Test + void testA() { + assertEquals(0, Fibonacci.fibonacci(0)); + } + @Test + void testB() { + assertEquals(55, Fibonacci.fibonacci(10)); + } + @Test + void testC() { + assertEquals(1, Fibonacci.fibonacci(1)); + } +} +""" + result = transformer.transform(source) + # First assertion in source gets _cf_result1, second gets _cf_result2, etc. + pos1 = result.index("_cf_result1") + pos2 = result.index("_cf_result2") + pos3 = result.index("_cf_result3") + assert pos1 < pos2 < pos3 + assert "Fibonacci.fibonacci(0)" in result.split("_cf_result1")[1].split("\n")[0] + assert "Fibonacci.fibonacci(10)" in result.split("_cf_result2")[1].split("\n")[0] + assert "Fibonacci.fibonacci(1)" in result.split("_cf_result3")[1].split("\n")[0] + assert transformer.invocation_counter == 3 + + def test_counter_increments_across_transform_call(self): + """Counter keeps incrementing across a single transform() call.""" + transformer = JavaAssertTransformer("fibonacci") + source = """\ +import org.junit.jupiter.api.Test; +import static org.junit.jupiter.api.Assertions.*; + +public class FibTest { + @Test + void test() { + assertEquals(0, Fibonacci.fibonacci(0)); + assertEquals(1, Fibonacci.fibonacci(1)); + assertEquals(55, Fibonacci.fibonacci(10)); + } +} +""" + transformer.transform(source) + assert transformer.invocation_counter == 3 + + # --- Nested assertion filtering --- + + def test_nested_assertions_inside_assertall_only_outer_replaced(self): + """assertEquals inside assertAll is nested → only assertAll is replaced, not inner ones individually.""" + transformer = JavaAssertTransformer("fibonacci") + source = """\ +import org.junit.jupiter.api.Test; +import static org.junit.jupiter.api.Assertions.*; + +public class FibTest { + @Test + void test() { + assertAll( + () -> assertEquals(0, Fibonacci.fibonacci(0)), + () -> assertEquals(1, Fibonacci.fibonacci(1)) + ); + } +} +""" + result = transformer.transform(source) + # assertAll is the outer assertion and should be replaced + assert "assertAll" not in result + # The individual assertEquals should NOT remain as separate replacements + # (they are nested inside assertAll, so the nesting filter removes them) + # But the target calls should still be captured + lines = [l.strip() for l in result.splitlines() if "_cf_result" in l] + assert len(lines) >= 1 # At least the outer replacement should produce captures + + def test_non_nested_assertions_all_replaced(self): + """Multiple top-level assertions (not nested) are all removed.""" + transformer = JavaAssertTransformer("fibonacci") + source = """\ +import org.junit.jupiter.api.Test; +import static org.junit.jupiter.api.Assertions.*; + +public class FibTest { + @Test + void test() { + assertEquals(0, Fibonacci.fibonacci(0)); + assertTrue(Fibonacci.isFibonacci(5)); + assertFalse(Fibonacci.isFibonacci(4)); + } +} +""" + result = transformer.transform(source) + assert "assertEquals" not in result + # assertEquals with Fibonacci.fibonacci(0) has target call, gets captured + assert "Object _cf_result1 = Fibonacci.fibonacci(0);" in result + # assertTrue/assertFalse don't contain "fibonacci" calls, so they are removed (empty) + assert "assertTrue(Fibonacci.isFibonacci(5));" not in result + assert "assertFalse(Fibonacci.isFibonacci(4));" not in result + + # --- Reverse replacement preserves positions --- + + def test_reverse_replacement_preserves_all_positions(self): + """Replacing in reverse order ensures positions stay correct for multi-replacement.""" + transformer = JavaAssertTransformer("compute") + source = """\ +import org.junit.jupiter.api.Test; +import static org.junit.jupiter.api.Assertions.*; + +public class CalcTest { + @Test + void test() { + assertEquals(1, engine.compute(1)); + assertEquals(4, engine.compute(2)); + assertEquals(9, engine.compute(3)); + assertEquals(16, engine.compute(4)); + assertEquals(25, engine.compute(5)); + } +} +""" + result = transformer.transform(source) + assert "assertEquals" not in result + assert "Object _cf_result1 = engine.compute(1);" in result + assert "Object _cf_result2 = engine.compute(2);" in result + assert "Object _cf_result3 = engine.compute(3);" in result + assert "Object _cf_result4 = engine.compute(4);" in result + assert "Object _cf_result5 = engine.compute(5);" in result + assert transformer.invocation_counter == 5 + + # --- Mixed assertions: some with target, some without --- + + def test_mixed_assertions_all_removed(self): + """All assertions are removed; targeted ones get capture statements.""" + transformer = JavaAssertTransformer("fibonacci") + source = """\ +import org.junit.jupiter.api.Test; +import static org.junit.jupiter.api.Assertions.*; + +public class FibTest { + @Test + void test() { + assertNotNull(config); + assertEquals(0, Fibonacci.fibonacci(0)); + assertTrue(isReady); + assertEquals(1, Fibonacci.fibonacci(1)); + assertFalse(isDone); + } +} +""" + result = transformer.transform(source) + # Non-targeted assertions are removed + assert "assertNotNull(config);" not in result + assert "assertTrue(isReady);" not in result + assert "assertFalse(isDone);" not in result + # Targeted assertions are replaced with capture statements + assert "Object _cf_result1 = Fibonacci.fibonacci(0);" in result + assert "Object _cf_result2 = Fibonacci.fibonacci(1);" in result + assert transformer.invocation_counter == 2 + + # --- Exception assertions in transform --- + + def test_exception_assertion_without_target_calls_still_replaced(self): + """assertThrows is replaced even if lambda doesn't contain the target function, + because is_exception_assertion=True passes the filter.""" + transformer = JavaAssertTransformer("fibonacci") + source = """\ +import org.junit.jupiter.api.Test; +import static org.junit.jupiter.api.Assertions.*; + +public class FibTest { + @Test + void test() { + assertThrows(Exception.class, () -> thrower.doSomething()); + } +} +""" + result = transformer.transform(source) + # assertThrows is an exception assertion so it passes the filter + assert "assertThrows" not in result + assert "try {" in result + + # --- Full output exact equality --- + + def test_single_assertion_exact_output(self): + """Verify exact output for the simplest single-assertion case.""" + transformer = JavaAssertTransformer("fibonacci") + source = """\ +import org.junit.jupiter.api.Test; +import static org.junit.jupiter.api.Assertions.*; + +public class FibTest { + @Test + void test() { + assertEquals(55, Fibonacci.fibonacci(10)); + } +} +""" + result = transformer.transform(source) + expected = """\ +import org.junit.jupiter.api.Test; +import static org.junit.jupiter.api.Assertions.*; + +public class FibTest { + @Test + void test() { + Object _cf_result1 = Fibonacci.fibonacci(10); + } +} +""" + assert result == expected + + def test_multiple_assertions_exact_output(self): + """Verify exact output when multiple assertions are replaced.""" + transformer = JavaAssertTransformer("add") + source = """\ +import org.junit.jupiter.api.Test; +import static org.junit.jupiter.api.Assertions.*; + +public class CalcTest { + @Test + void test() { + assertEquals(3, calc.add(1, 2)); + assertEquals(7, calc.add(3, 4)); + } +} +""" + result = transformer.transform(source) + expected = """\ +import org.junit.jupiter.api.Test; +import static org.junit.jupiter.api.Assertions.*; + +public class CalcTest { + @Test + void test() { + Object _cf_result1 = calc.add(1, 2); + Object _cf_result2 = calc.add(3, 4); + } +} +""" + assert result == expected + + # --- Idempotency --- + + def test_transform_already_transformed_is_noop(self): + """Running transform on already-transformed code (no assertions) returns it unchanged.""" + transformer1 = JavaAssertTransformer("fibonacci") + source = """\ +import org.junit.jupiter.api.Test; +import static org.junit.jupiter.api.Assertions.*; + +public class FibTest { + @Test + void test() { + assertEquals(55, Fibonacci.fibonacci(10)); + } +} +""" + first_pass = transformer1.transform(source) + # Second pass with a new transformer should be a no-op (no assertions left) + transformer2 = JavaAssertTransformer("fibonacci") + second_pass = transformer2.transform(first_pass) + assert second_pass == first_pass + assert transformer2.invocation_counter == 0 + + +class TestJavaAssertTransformerClass: + """Tests for the JavaAssertTransformer class directly.""" + + def test_invocation_counter_increments(self): + transformer = JavaAssertTransformer("fibonacci") + source = """\ +import org.junit.jupiter.api.Test; +import static org.junit.jupiter.api.Assertions.*; + +public class FibTest { + @Test + void test1() { + assertEquals(0, Fibonacci.fibonacci(0)); + } + + @Test + void test2() { + assertEquals(55, Fibonacci.fibonacci(10)); + } +} +""" + result = transformer.transform(source) + assert "_cf_result1" in result + assert "_cf_result2" in result + assert transformer.invocation_counter == 2 + + def test_framework_detection_junit5(self): + transformer = JavaAssertTransformer("fibonacci") + source = "import org.junit.jupiter.api.Test;\nimport static org.junit.jupiter.api.Assertions.*;\n" + framework = transformer._detect_framework(source) + assert framework == "junit5" + + def test_framework_detection_junit4(self): + transformer = JavaAssertTransformer("fibonacci") + source = "import org.junit.Test;\nimport static org.junit.Assert.*;\n" + framework = transformer._detect_framework(source) + assert framework == "junit4" + + def test_framework_detection_assertj(self): + transformer = JavaAssertTransformer("fibonacci") + source = "import org.assertj.core.api.Assertions;\n" + framework = transformer._detect_framework(source) + assert framework == "assertj" + + def test_framework_detection_hamcrest(self): + transformer = JavaAssertTransformer("fibonacci") + source = "import org.hamcrest.MatcherAssert;\nimport org.hamcrest.Matchers;\n" + framework = transformer._detect_framework(source) + assert framework == "hamcrest" + + def test_framework_detection_testng(self): + transformer = JavaAssertTransformer("fibonacci") + source = "import org.testng.Assert;\n" + framework = transformer._detect_framework(source) + assert framework == "testng" + + def test_framework_detection_default_junit5(self): + transformer = JavaAssertTransformer("fibonacci") + source = "public class Test {}" + framework = transformer._detect_framework(source) + assert framework == "junit5" + + +class TestAssertAll: + """Tests for assertAll (JUnit 5 grouped assertions).""" + + def test_assertall_with_target_calls(self): + source = """\ +import org.junit.jupiter.api.Test; +import static org.junit.jupiter.api.Assertions.*; + +public class FibonacciTest { + @Test + void testMultipleFibonacci() { + assertAll( + () -> assertEquals(0, Fibonacci.fibonacci(0)), + () -> assertEquals(1, Fibonacci.fibonacci(1)), + () -> assertEquals(55, Fibonacci.fibonacci(10)) + ); + } +} +""" + result = transform_java_assertions(source, "fibonacci") + # assertAll should be transformed (it contains target calls) + assert "assertAll" not in result + + +class TestAssertThrowsEdgeCases: + """Edge cases for assertThrows transformation.""" + + def test_assertthrows_with_multiline_lambda(self): + source = """\ +import org.junit.jupiter.api.Test; +import static org.junit.jupiter.api.Assertions.*; + +public class FibonacciTest { + @Test + void testNegativeThrows() { + assertThrows( + IllegalArgumentException.class, + () -> Fibonacci.fibonacci(-1) + ); + } +} +""" + result = transform_java_assertions(source, "fibonacci") + assert "assertThrows" not in result + assert "try {" in result + assert "Fibonacci.fibonacci(-1);" in result + + def test_assertthrows_with_complex_lambda_body(self): + source = """\ +import org.junit.jupiter.api.Test; +import static org.junit.jupiter.api.Assertions.*; + +public class FibonacciTest { + @Test + void testNegativeThrows() { + assertThrows(IllegalArgumentException.class, () -> { + int n = -5; + Fibonacci.fibonacci(n); + }); + } +} +""" + result = transform_java_assertions(source, "fibonacci") + assert "assertThrows" not in result + assert "try {" in result + + def test_assertthrows_with_final_variable(self): + """Test assertThrows assigned to a final variable.""" + source = """\ +import org.junit.jupiter.api.Test; +import static org.junit.jupiter.api.Assertions.*; + +public class FibonacciTest { + @Test + void testNegativeThrows() { + final IllegalArgumentException ex = assertThrows(IllegalArgumentException.class, () -> Fibonacci.fibonacci(-1)); + } +} +""" + result = transform_java_assertions(source, "fibonacci") + assert "assertThrows" not in result + assert "Fibonacci.fibonacci(-1);" in result + + +class TestAllAssertionsRemoved: + """Tests verifying that ALL assertions are removed (the default behavior).""" + + MULTI_FUNCTION_TEST = """\ +import org.junit.jupiter.api.Test; +import static org.junit.jupiter.api.Assertions.*; + +public class FibonacciTest { + + @Test + void testFibonacci() { + assertEquals(0, Fibonacci.fibonacci(0)); + assertEquals(1, Fibonacci.fibonacci(1)); + assertEquals(5, Fibonacci.fibonacci(5)); + } + + @Test + void testIsFibonacci() { + assertTrue(Fibonacci.isFibonacci(0)); + assertTrue(Fibonacci.isFibonacci(1)); + assertFalse(Fibonacci.isFibonacci(4)); + } + + @Test + void testIsPerfectSquare() { + assertTrue(Fibonacci.isPerfectSquare(0)); + assertTrue(Fibonacci.isPerfectSquare(4)); + assertFalse(Fibonacci.isPerfectSquare(5)); + } + + @Test + void testFibonacciSequence() { + assertArrayEquals(new long[]{0, 1, 1}, Fibonacci.fibonacciSequence(3)); + } + + @Test + void testFibonacciIndex() { + assertEquals(0, Fibonacci.fibonacciIndex(0)); + assertEquals(5, Fibonacci.fibonacciIndex(5)); + } + + @Test + void testSumFibonacci() { + assertEquals(0, Fibonacci.sumFibonacci(0)); + assertEquals(4, Fibonacci.sumFibonacci(4)); + } + + @Test + void testFibonacciNegative() { + assertThrows(IllegalArgumentException.class, () -> Fibonacci.fibonacci(-1)); + } +} +""" + + def test_all_assertions_removed(self): + result = transform_java_assertions(self.MULTI_FUNCTION_TEST, "fibonacci") + # ALL assertions should be removed + assert "assertEquals(0, Fibonacci.fibonacci(0))" not in result + assert "assertEquals(1, Fibonacci.fibonacci(1))" not in result + assert "assertTrue(Fibonacci.isFibonacci(0))" not in result + assert "assertTrue(Fibonacci.isPerfectSquare(0))" not in result + assert "assertArrayEquals" not in result + assert "assertEquals(0, Fibonacci.fibonacciIndex(0))" not in result + assert "assertEquals(0, Fibonacci.sumFibonacci(0))" not in result + assert "assertFalse" not in result + # Target function calls should be captured + assert "Object _cf_result" in result + assert "Fibonacci.fibonacci(0)" in result + # Exception assertion should be converted to try/catch + assert "assertThrows" not in result + assert "Fibonacci.fibonacci(-1);" in result + + def test_preserves_non_assertion_code(self): + source = """\ +import org.junit.jupiter.api.Test; +import static org.junit.jupiter.api.Assertions.*; + +public class CalcTest { + + @Test + void testAdd() { + Calculator calc = new Calculator(); + int result = calc.setup(); + assertEquals(5, calc.add(2, 3)); + assertTrue(calc.isReady()); + } +} +""" + result = transform_java_assertions(source, "add") + # Non-assertion code should be preserved + assert "Calculator calc = new Calculator();" in result + assert "int result = calc.setup();" in result + # All assertions should be removed + assert "assertEquals(5, calc.add(2, 3))" not in result + assert "assertTrue(calc.isReady())" not in result + # Target function call should be captured + assert "Object _cf_result" in result + assert "calc.add(2, 3)" in result + + def test_assertj_all_removed(self): + source = """\ +import org.assertj.core.api.Assertions; +import static org.assertj.core.api.Assertions.assertThat; + +public class FibTest { + @Test + void test() { + assertThat(Fibonacci.fibonacci(5)).isEqualTo(5); + assertThat(Fibonacci.isFibonacci(5)).isTrue(); + } +} +""" + result = transform_java_assertions(source, "fibonacci") + # assertThat calls should be removed (only import references remain) + assert "assertThat(Fibonacci.fibonacci(5))" not in result + assert "assertThat(Fibonacci.isFibonacci(5))" not in result + assert "Fibonacci.fibonacci(5)" in result + assert "isTrue" not in result + assert "isEqualTo" not in result + + def test_mixed_frameworks_all_removed(self): + source = """\ +import org.junit.jupiter.api.Test; +import static org.junit.jupiter.api.Assertions.*; + +public class MixedTest { + @Test + void test() { + assertEquals(5, obj.target(1)); + assertNull(obj.other()); + assertNotNull(obj.another()); + assertTrue(obj.check()); + } +} +""" + result = transform_java_assertions(source, "target") + assert "assertEquals" not in result + assert "assertNull" not in result + assert "assertNotNull" not in result + assert "assertTrue" not in result + assert "Object _cf_result" in result + assert "obj.target(1)" in result