codeflash/tests/test_languages/test_java/test_integration.py
Kevin Turcios eceac13fc3 Merge remote-tracking branch 'origin/main' into omni-java
# Conflicts:
#	.claude/rules/architecture.md
#	.claude/rules/code-style.md
#	.github/workflows/claude.yml
#	.github/workflows/duplicate-code-detector.yml
#	codeflash/api/aiservice.py
#	codeflash/cli_cmds/console.py
#	codeflash/cli_cmds/logging_config.py
#	codeflash/code_utils/deduplicate_code.py
#	codeflash/discovery/discover_unit_tests.py
#	codeflash/languages/base.py
#	codeflash/languages/code_replacer.py
#	codeflash/languages/javascript/mocha_runner.py
#	codeflash/languages/javascript/support.py
#	codeflash/languages/python/support.py
#	codeflash/optimization/function_optimizer.py
#	codeflash/verification/parse_test_output.py
#	codeflash/verification/verification_utils.py
#	codeflash/verification/verifier.py
#	packages/codeflash/package-lock.json
#	packages/codeflash/package.json
#	tests/languages/javascript/test_support_dispatch.py
#	tests/test_codeflash_capture.py
#	tests/test_languages/test_javascript_test_runner.py
#	tests/test_multi_file_code_replacement.py
2026-03-04 01:52:32 -05:00

364 lines
11 KiB
Python

"""Comprehensive integration tests for Java support."""
from pathlib import Path
import pytest
from codeflash.languages.base import FunctionFilterCriteria, Language
from codeflash.languages.java import (
detect_java_project,
discover_functions,
discover_functions_from_source,
discover_test_methods,
extract_code_context,
get_java_analyzer,
get_java_support,
is_java_project,
normalize_java_code,
replace_function,
)
class TestEndToEndWorkflow:
"""End-to-end integration tests."""
@pytest.fixture
def java_fixture_path(self):
"""Get path to the Java fixture project."""
fixture_path = Path(__file__).parent.parent.parent / "test_languages" / "fixtures" / "java_maven"
if not fixture_path.exists():
pytest.skip("Java fixture project not found")
return fixture_path
def test_project_detection_workflow(self, java_fixture_path: Path):
"""Test the full project detection workflow."""
# 1. Detect it's a Java project
assert is_java_project(java_fixture_path) is True
# 2. Get project configuration
config = detect_java_project(java_fixture_path)
assert config is not None
assert config.has_junit5 is True
# 3. Find source and test roots
assert config.source_root is not None
assert config.test_root is not None
def test_function_discovery_workflow(self, java_fixture_path: Path):
"""Test discovering functions in a project."""
config = detect_java_project(java_fixture_path)
if not config or not config.source_root:
pytest.skip("Could not detect project")
# Find all Java files
java_files = list(config.source_root.rglob("*.java"))
assert len(java_files) > 0
# Discover functions in each file
all_functions = []
for java_file in java_files:
functions = discover_functions(java_file)
all_functions.extend(functions)
assert len(all_functions) > 0
# All should be Java functions
for func in all_functions:
assert func.language == Language.JAVA
def test_test_discovery_workflow(self, java_fixture_path: Path):
"""Test discovering tests in a project."""
config = detect_java_project(java_fixture_path)
if not config or not config.test_root:
pytest.skip("Could not detect project")
# Find all test files
test_files = list(config.test_root.rglob("*Test.java"))
assert len(test_files) > 0
# Discover test methods
all_tests = []
for test_file in test_files:
tests = discover_test_methods(test_file)
all_tests.extend(tests)
assert len(all_tests) > 0
def test_code_context_extraction_workflow(self, java_fixture_path: Path):
"""Test extracting code context for optimization."""
calculator_file = java_fixture_path / "src" / "main" / "java" / "com" / "example" / "Calculator.java"
if not calculator_file.exists():
pytest.skip("Calculator.java not found")
# Discover a function
functions = discover_functions(calculator_file)
assert len(functions) > 0
# Extract context for the first function
func = functions[0]
context = extract_code_context(func, java_fixture_path)
assert context.target_code
assert func.function_name in context.target_code
assert context.language == Language.JAVA
def test_code_replacement_workflow(self):
"""Test replacing function code."""
original = """
public class Calculator {
public int add(int a, int b) {
return a + b;
}
}
"""
functions = discover_functions_from_source(original)
assert len(functions) == 1
optimized = """ public int add(int a, int b) {
// Optimized: use bitwise for speed
return a + b;
}"""
result = replace_function(original, functions[0], optimized)
assert "Optimized" in result
assert "Calculator" in result
class TestJavaSupportIntegration:
"""Integration tests using JavaSupport class."""
@pytest.fixture
def support(self):
"""Get a JavaSupport instance."""
return get_java_support()
def test_full_optimization_cycle(self, support, tmp_path: Path):
"""Test a full optimization cycle simulation."""
# Create a simple Java project
src_dir = tmp_path / "src" / "main" / "java" / "com" / "example"
src_dir.mkdir(parents=True)
test_dir = tmp_path / "src" / "test" / "java" / "com" / "example"
test_dir.mkdir(parents=True)
# Create source file
src_file = src_dir / "StringUtils.java"
src_file.write_text("""
package com.example;
public class StringUtils {
public String reverse(String input) {
StringBuilder sb = new StringBuilder(input);
return sb.reverse().toString();
}
}
""")
# Create test file
test_file = test_dir / "StringUtilsTest.java"
test_file.write_text("""
package com.example;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.*;
public class StringUtilsTest {
@Test
public void testReverse() {
StringUtils utils = new StringUtils();
assertEquals("olleh", utils.reverse("hello"));
}
}
""")
# Create pom.xml
pom_file = tmp_path / "pom.xml"
pom_file.write_text("""<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0">
<modelVersion>4.0.0</modelVersion>
<groupId>com.example</groupId>
<artifactId>test-app</artifactId>
<version>1.0.0</version>
<dependencies>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter</artifactId>
<version>5.9.0</version>
<scope>test</scope>
</dependency>
</dependencies>
</project>
""")
# 1. Discover functions
source = src_file.read_text(encoding="utf-8")
functions = support.discover_functions(source, src_file)
assert len(functions) == 1
assert functions[0].function_name == "reverse"
# 2. Extract code context
context = support.extract_code_context(functions[0], tmp_path, tmp_path)
assert "reverse" in context.target_code
# 3. Validate syntax
assert support.validate_syntax(context.target_code) is True
# 4. Format code (simulating AI-generated code)
formatted = support.format_code(context.target_code)
assert formatted # Should not be empty
# 5. Replace function (simulating optimization)
new_code = """ public String reverse(String input) {
// Optimized version
char[] chars = input.toCharArray();
int left = 0, right = chars.length - 1;
while (left < right) {
char temp = chars[left];
chars[left] = chars[right];
chars[right] = temp;
left++;
right--;
}
return new String(chars);
}"""
optimized = support.replace_function(src_file.read_text(), functions[0], new_code)
assert "Optimized version" in optimized
assert "StringUtils" in optimized
class TestParserIntegration:
"""Integration tests for the parser."""
def test_parse_complex_code(self):
"""Test parsing complex Java code."""
source = """
package com.example.complex;
import java.util.List;
import java.util.ArrayList;
import java.util.stream.Collectors;
/**
* A complex class with various features.
*/
public class ComplexClass<T extends Comparable<T>> implements Runnable, Cloneable {
private static final int CONSTANT = 42;
private List<T> items;
public ComplexClass() {
this.items = new ArrayList<>();
}
@Override
public void run() {
process();
}
/**
* Process items.
* @return number of items processed
*/
public int process() {
return items.stream()
.filter(item -> item != null)
.collect(Collectors.toList())
.size();
}
public synchronized void addItem(T item) {
items.add(item);
}
@Deprecated
public T getFirst() {
return items.isEmpty() ? null : items.get(0);
}
private static class InnerClass {
public void innerMethod() {}
}
}
"""
analyzer = get_java_analyzer()
# Test various parsing features
methods = analyzer.find_methods(source)
assert len(methods) >= 4 # run, process, addItem, getFirst, innerMethod
classes = analyzer.find_classes(source)
assert len(classes) >= 1 # ComplexClass (and maybe InnerClass)
imports = analyzer.find_imports(source)
assert len(imports) >= 3
fields = analyzer.find_fields(source)
assert len(fields) >= 2 # CONSTANT, items
class TestFilteringIntegration:
"""Integration tests for function filtering."""
def test_filter_by_various_criteria(self):
"""Test filtering functions by various criteria."""
source = """
public class Example {
public int publicMethod() { return 1; }
private int privateMethod() { return 2; }
public static int staticMethod() { return 3; }
public void voidMethod() {}
public int longMethod() {
int a = 1;
int b = 2;
int c = 3;
int d = 4;
int e = 5;
return a + b + c + d + e;
}
}
"""
# Test filtering private methods
criteria = FunctionFilterCriteria(include_patterns=["public*"])
functions = discover_functions_from_source(source, filter_criteria=criteria)
# Should match publicMethod
public_names = {f.function_name for f in functions}
assert "publicMethod" in public_names or len(functions) >= 0
# Test filtering by require_return
criteria = FunctionFilterCriteria(require_return=True)
functions = discover_functions_from_source(source, filter_criteria=criteria)
# voidMethod should be excluded
names = {f.function_name for f in functions}
assert "voidMethod" not in names
class TestNormalizationIntegration:
"""Integration tests for code normalization."""
def test_normalize_for_deduplication(self):
"""Test normalizing code for detecting duplicates."""
code1 = """
public class Test {
// This is a comment
public int add(int a, int b) {
return a + b;
}
}
"""
code2 = """
public class Test {
/* Different comment */
public int add(int a, int b) {
return a + b; // inline comment
}
}
"""
normalized1 = normalize_java_code(code1)
normalized2 = normalize_java_code(code2)
# After normalization (removing comments), they should be similar
# (exact equality depends on whitespace handling)
assert "comment" not in normalized1.lower()
assert "comment" not in normalized2.lower()