Skip to content
Open
Show file tree
Hide file tree
Changes from 40 commits
Commits
Show all changes
47 commits
Select commit Hold shift + click to select a range
f4a4ac6
Refactor Java test runner tests to use MavenStrategy
HeshamHM28 Mar 9, 2026
0a63c1b
fix: add missing CompletedProcess[str] type parameters in Java strate…
github-actions[bot] Mar 9, 2026
cd90aee
Optimize MavenStrategy.get_build_output_dir
codeflash-ai[bot] Mar 9, 2026
6700ce8
Merge pull request #1801 from codeflash-ai/codeflash/optimize-pr1774-…
HeshamHM28 Mar 9, 2026
67b3b21
Optimize _validate_test_filter
codeflash-ai[bot] Mar 9, 2026
4e2197e
Merge pull request #1803 from codeflash-ai/codeflash/optimize-pr1774-…
claude[bot] Mar 9, 2026
73fc5b0
feat: add Gradle build configuration and update versioning
HeshamHM28 Mar 9, 2026
bf00132
Optimize _get_empty_result
codeflash-ai[bot] Mar 9, 2026
078a85c
style: auto-fix linting issues (unused uuid import, trailing blank line)
github-actions[bot] Mar 9, 2026
3aea939
Refactor Maven strategy for Codeflash integration
HeshamHM28 Mar 10, 2026
b912046
feat: add default timeout configuration for JUnit Jupiter execution
HeshamHM28 Mar 10, 2026
ce72525
fix: resolve ruff formatting and mypy type errors in Java strategy files
github-actions[bot] Mar 10, 2026
3119e20
Merge pull request #1806 from codeflash-ai/codeflash/optimize-pr1774-…
claude[bot] Mar 10, 2026
4d4e946
feat: initialize Java Gradle project with basic structure and Fibonac…
HeshamHM28 Mar 10, 2026
30fa5ce
style: auto-fix ruff formatting in gradle_strategy.py
github-actions[bot] Mar 10, 2026
1ec829d
refactor: replace find_gradle_executable and find_maven_executable wi…
HeshamHM28 Mar 10, 2026
3d7b562
fix: update Gradle init scripts to use projectsEvaluated for better c…
HeshamHM28 Mar 10, 2026
04edb54
feat: add allow_fallback parameter to get_optimized_code_for_module f…
HeshamHM28 Mar 10, 2026
3af4e10
feat: add check for empty test results in compare_candidate_results m…
HeshamHM28 Mar 10, 2026
68cb665
fix: resolve temp file leak and mypy issues in Gradle strategy
github-actions[bot] Mar 10, 2026
547b794
feat: add extract_parameterized_test_index function and refactor test…
HeshamHM28 Mar 11, 2026
4836964
feat: add normalization for Gradle JUnit XML reports to match Maven S…
HeshamHM28 Mar 12, 2026
15145fc
feat: add project_classpath parameter to compare_test_results for Jav…
HeshamHM28 Mar 12, 2026
8345c8d
feat: normalize Gradle XML reports and handle missing message attribu…
HeshamHM28 Mar 12, 2026
133acb1
feat: add Gradle validation skip tasks to improve test execution effi…
HeshamHM28 Mar 12, 2026
98e573d
feat: add --continue flag for Gradle coverage and multi-module test runs
HeshamHM28 Mar 12, 2026
c8d31a4
feat: add test framework detection in JavaSupport configuration
HeshamHM28 Mar 12, 2026
73b3eec
refactor: use tree-sitter for Gradle build file dependency injection
HeshamHM28 Mar 12, 2026
10016f8
fix: use init script for Gradle validation skip instead of -x flags
HeshamHM28 Mar 12, 2026
39c1e3e
feat: disable test failure on no matching tests in Gradle strategy
HeshamHM28 Mar 12, 2026
17c731c
refactor: streamline temporary file handling in JUnit XML generation
HeshamHM28 Mar 12, 2026
cbbc605
feat: disable error-prone checks and compiler warnings in Gradle and …
HeshamHM28 Mar 12, 2026
203cb7c
add compilation cache
HeshamHM28 Mar 13, 2026
88fe83b
fix recompile on error
HeshamHM28 Mar 16, 2026
3860890
skip candidates on compilation failure instead of falling back to bui…
HeshamHM28 Mar 16, 2026
623beec
add uv.lock
HeshamHM28 Mar 16, 2026
16b3c2f
Merge branch 'main' into feat/gradle-executor-from-java
HeshamHM28 Mar 16, 2026
1c7cd04
Add Downlaod logic
HeshamHM28 Mar 17, 2026
647cbfc
fix tests
HeshamHM28 Mar 17, 2026
b5e9924
style: fix lint issues in Java Gradle support files
github-actions[bot] Mar 17, 2026
faeff25
Update codeflash/languages/java/function_optimizer.py
HeshamHM28 Mar 17, 2026
bf958b6
fix: declare _cached_project_classpath class attribute for mypy
github-actions[bot] Mar 17, 2026
cd0e193
fix multi_root
HeshamHM28 Mar 18, 2026
6b2a852
fix running tests
HeshamHM28 Mar 18, 2026
ba36a1f
fix jar file problems
HeshamHM28 Mar 18, 2026
2a35716
Merge branch 'main' into feat/gradle-executor-from-java
HeshamHM28 Mar 18, 2026
dd20db2
fix setup_test_config
HeshamHM28 Mar 18, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ __pycache__/
# Distribution / packaging
.Python
build/
.gradle/
develop-eggs/
cli/dist/
downloads/
Expand Down
2 changes: 2 additions & 0 deletions code_to_optimize/java-gradle/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
.gradle/
build/
29 changes: 29 additions & 0 deletions code_to_optimize/java-gradle/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
plugins {
java
jacoco
}

group = "com.example"
version = "1.0.0"

java {
sourceCompatibility = JavaVersion.VERSION_11
targetCompatibility = JavaVersion.VERSION_11
}

repositories {
mavenCentral()
mavenLocal()
}

dependencies {
testImplementation("org.junit.jupiter:junit-jupiter:5.10.0")
testImplementation("org.junit.jupiter:junit-jupiter-params:5.10.0")
testImplementation("org.xerial:sqlite-jdbc:3.42.0.0")
testRuntimeOnly("org.junit.platform:junit-platform-launcher")
testImplementation(files("/Users/heshammohamed/Work/codeflash/code_to_optimize/java-gradle/libs/codeflash-runtime-1.0.0.jar")) // codeflash-runtime
}

tasks.test {
useJUnitPlatform()
}
4 changes: 4 additions & 0 deletions code_to_optimize/java-gradle/codeflash.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
[tool.codeflash]
module-root = "src/main/java"
tests-root = "src/test/java"
formatter-cmds = []
Binary file not shown.
1 change: 1 addition & 0 deletions code_to_optimize/java-gradle/settings.gradle.kts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
rootProject.name = "codeflash-java-gradle-sample"
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package com.example;

public class Fibonacci {

public static long fibonacci(int n) {
if (n < 0) {
throw new IllegalArgumentException("n must be non-negative");
}
if (n <= 1) {
return n;
}
return fibonacci(n - 1) + fibonacci(n - 2);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
package com.example;

import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.*;

class FibonacciTest {

@Test
void testBaseCase() {
assertEquals(0, Fibonacci.fibonacci(0));
assertEquals(1, Fibonacci.fibonacci(1));
}

@Test
void testSmallValues() {
assertEquals(1, Fibonacci.fibonacci(2));
assertEquals(2, Fibonacci.fibonacci(3));
assertEquals(5, Fibonacci.fibonacci(5));
assertEquals(8, Fibonacci.fibonacci(6));
assertEquals(55, Fibonacci.fibonacci(10));
}

@Test
void testNegative() {
assertThrows(IllegalArgumentException.class, () -> Fibonacci.fibonacci(-1));
}
}
65 changes: 65 additions & 0 deletions codeflash-java-runtime/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
plugins {
java
id("com.gradleup.shadow") version "9.0.0-beta12"
}

group = "com.codeflash"
version = "1.0.0"

java {
sourceCompatibility = JavaVersion.VERSION_11
targetCompatibility = JavaVersion.VERSION_11
}

repositories {
mavenCentral()
}

dependencies {
implementation("com.google.code.gson:gson:2.10.1")
implementation("com.esotericsoftware:kryo:5.6.2")
implementation("org.objenesis:objenesis:3.4")
implementation("org.xerial:sqlite-jdbc:3.45.0.0")
implementation("org.ow2.asm:asm:9.7.1")
implementation("org.ow2.asm:asm-commons:9.7.1")

testImplementation("org.junit.jupiter:junit-jupiter:5.10.1")
testRuntimeOnly("org.junit.platform:junit-platform-launcher")
}

tasks.test {
useJUnitPlatform()
jvmArgs(
"--add-opens", "java.base/java.util=ALL-UNNAMED",
"--add-opens", "java.base/java.lang=ALL-UNNAMED",
"--add-opens", "java.base/java.lang.reflect=ALL-UNNAMED",
"--add-opens", "java.base/java.math=ALL-UNNAMED",
"--add-opens", "java.base/java.io=ALL-UNNAMED",
"--add-opens", "java.base/java.net=ALL-UNNAMED",
"--add-opens", "java.base/java.time=ALL-UNNAMED",
)
}

tasks.shadowJar {
archiveBaseName.set("codeflash-runtime")
archiveVersion.set("1.0.0")
archiveClassifier.set("")

relocate("org.objectweb.asm", "com.codeflash.asm")

manifest {
attributes(
"Main-Class" to "com.codeflash.Comparator",
"Premain-Class" to "com.codeflash.profiler.ProfilerAgent",
"Can-Retransform-Classes" to "true",
)
}

exclude("META-INF/*.SF")
exclude("META-INF/*.DSA")
exclude("META-INF/*.RSA")
}

tasks.build {
dependsOn(tasks.shadowJar)
}
1 change: 1 addition & 0 deletions codeflash-java-runtime/settings.gradle.kts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
rootProject.name = "codeflash-runtime"
Original file line number Diff line number Diff line change
Expand Up @@ -54,8 +54,8 @@ void testAllPlaceholderSkips() throws Exception {
KryoPlaceholder.create(new Object(), "unserializable", "root")
);

insertRow(originalDb, "iter_1_0", 1, placeholderBytes);
insertRow(candidateDb, "iter_1_1", 1, placeholderBytes);
insertRow(originalDb, "1", 1, placeholderBytes);
insertRow(candidateDb, "1", 1, placeholderBytes);

String json = Comparator.compareDatabases(originalDb.toString(), candidateDb.toString());
Map<String, Object> result = parseJson(json);
Expand All @@ -74,8 +74,8 @@ void testDeserializationErrorSkipped() throws Exception {
// Insert corrupted byte data that will fail Kryo deserialization
byte[] corruptedBytes = new byte[]{0x01, 0x02, 0x03, (byte) 0xFF, (byte) 0xFE};

insertRow(originalDb, "iter_1_0", 1, corruptedBytes);
insertRow(candidateDb, "iter_1_1", 1, corruptedBytes);
insertRow(originalDb, "1", 1, corruptedBytes);
insertRow(candidateDb, "1", 1, corruptedBytes);

String json = Comparator.compareDatabases(originalDb.toString(), candidateDb.toString());
Map<String, Object> result = parseJson(json);
Expand All @@ -97,12 +97,12 @@ void testMixedRealAndPlaceholder() throws Exception {
KryoPlaceholder.create(new Object(), "unserializable", "root")
);

insertRow(originalDb, "iter_1_0", 1, realBytes1);
insertRow(candidateDb, "iter_1_1", 1, realBytes1);
insertRow(originalDb, "iter_2_0", 1, realBytes2);
insertRow(candidateDb, "iter_2_1", 1, realBytes2);
insertRow(originalDb, "iter_3_0", 1, placeholderBytes);
insertRow(candidateDb, "iter_3_1", 1, placeholderBytes);
insertRow(originalDb, "1", 1, realBytes1);
insertRow(candidateDb, "1", 1, realBytes1);
insertRow(originalDb, "2", 1, realBytes2);
insertRow(candidateDb, "2", 1, realBytes2);
insertRow(originalDb, "3", 1, placeholderBytes);
insertRow(candidateDb, "3", 1, placeholderBytes);

String json = Comparator.compareDatabases(originalDb.toString(), candidateDb.toString());
Map<String, Object> result = parseJson(json);
Expand All @@ -121,10 +121,10 @@ void testNormalHappyPath() throws Exception {
byte[] bytes1 = Serializer.serialize(100);
byte[] bytes2 = Serializer.serialize("world");

insertRow(originalDb, "iter_1_0", 1, bytes1);
insertRow(candidateDb, "iter_1_1", 1, bytes1);
insertRow(originalDb, "iter_2_0", 1, bytes2);
insertRow(candidateDb, "iter_2_1", 1, bytes2);
insertRow(originalDb, "1", 1, bytes1);
insertRow(candidateDb, "1", 1, bytes1);
insertRow(originalDb, "2", 1, bytes2);
insertRow(candidateDb, "2", 1, bytes2);

String json = Comparator.compareDatabases(originalDb.toString(), candidateDb.toString());
Map<String, Object> result = parseJson(json);
Expand All @@ -144,8 +144,8 @@ void testNormalMismatch() throws Exception {
byte[] origBytes = Serializer.serialize(42);
byte[] candBytes = Serializer.serialize(99);

insertRow(originalDb, "iter_1_0", 1, origBytes);
insertRow(candidateDb, "iter_1_1", 1, candBytes);
insertRow(originalDb, "1", 1, origBytes);
insertRow(candidateDb, "1", 1, candBytes);

String json = Comparator.compareDatabases(originalDb.toString(), candidateDb.toString());
Map<String, Object> result = parseJson(json);
Expand All @@ -161,8 +161,8 @@ void testVoidMethodsBothNull() throws Exception {
createTestDb(candidateDb);

// Insert rows with NULL return_value (void methods)
insertRow(originalDb, "iter_1_0", 1, null);
insertRow(candidateDb, "iter_1_1", 1, null);
insertRow(originalDb, "1", 1, null);
insertRow(candidateDb, "1", 1, null);

String json = Comparator.compareDatabases(originalDb.toString(), candidateDb.toString());
Map<String, Object> result = parseJson(json);
Expand All @@ -178,7 +178,7 @@ void testOneSideEmpty() throws Exception {
createTestDb(candidateDb);

byte[] bytes = Serializer.serialize(42);
insertRow(originalDb, "iter_1_0", 1, bytes);
insertRow(originalDb, "1", 1, bytes);
// candidateDb has no rows

String json = Comparator.compareDatabases(originalDb.toString(), candidateDb.toString());
Expand Down Expand Up @@ -222,7 +222,7 @@ private void createTestDb(Path dbPath) throws Exception {
+ "iteration_id TEXT NOT NULL, "
+ "loop_index INTEGER NOT NULL, "
+ "return_value BLOB, "
+ "PRIMARY KEY (iteration_id, loop_index))");
+ "verification_type TEXT)");
}
}

Expand Down
13 changes: 13 additions & 0 deletions codeflash/code_utils/code_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -338,6 +338,19 @@ def get_qualified_name(module_name: str, full_qualified_name: str) -> str:
return full_qualified_name[len(module_name) + 1 :]


_PARAMETERIZED_INDEX_RE = re.compile(r"\[(\d+)")


def extract_parameterized_test_index(test_name: str) -> int:
"""Extract the numeric index from a parameterized test name.

Handles formats like ``test[ 0 ]``, ``test[1]``, and
``test[1] input=foo, expected=bar``. Returns 1 when no numeric index is found.
"""
m = _PARAMETERIZED_INDEX_RE.search(test_name)
return int(m.group(1)) if m else 1


def module_name_from_file_path(file_path: Path, project_root_path: Path, *, traverse_up: bool = False) -> str:
try:
relative_path = file_path.resolve().relative_to(project_root_path.resolve())
Expand Down
11 changes: 9 additions & 2 deletions codeflash/languages/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -681,14 +681,21 @@ def extract_calling_function_source(self, source_code: str, function_name: str,
# === Test Result Comparison ===

def compare_test_results(
self, original_results_path: Path, candidate_results_path: Path, project_root: Path | None = None
) -> tuple[bool, list[Any]]:
self,
original_results_path: Path,
candidate_results_path: Path,
project_root: Path | None = None,
project_classpath: str | None = None,
) -> tuple[bool, list]:
"""Compare test results between original and candidate code.

Args:
original_results_path: Path to original test results (e.g., SQLite DB).
candidate_results_path: Path to candidate test results.
project_root: Project root directory (for finding node_modules, etc.).
project_classpath: Full project classpath string (Java only). Needed so
the Comparator JVM can resolve project-specific classes during Kryo
deserialization.

Returns:
Tuple of (are_equivalent, list of TestDiff objects).
Expand Down
12 changes: 10 additions & 2 deletions codeflash/languages/code_replacer.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,14 +21,19 @@
_SOURCE_CRITERIA = FunctionFilterCriteria(require_return=False, require_export=False)


def get_optimized_code_for_module(relative_path: Path, optimized_code: CodeStringsMarkdown) -> str:
def get_optimized_code_for_module(
relative_path: Path, optimized_code: CodeStringsMarkdown, allow_fallback: bool = True
) -> str:
from codeflash.languages.current import is_python

file_to_code_context = optimized_code.file_to_path()
module_optimized_code = file_to_code_context.get(str(relative_path))
if module_optimized_code is not None:
return module_optimized_code

if not allow_fallback:
return ""

# Fallback 1: single code block with no file path
if "None" in file_to_code_context and len(file_to_code_context) == 1:
logger.debug(f"Using code block with None file_path for {relative_path}")
Expand Down Expand Up @@ -70,7 +75,10 @@ def replace_function_definitions_for_language(
and LanguageSupport.discover_functions.
"""
original_source_code: str = module_abspath.read_text(encoding="utf8")
code_to_apply = get_optimized_code_for_module(module_abspath.relative_to(project_root_path), optimized_code)
is_target_file = function_to_optimize is not None and function_to_optimize.file_path == module_abspath
code_to_apply = get_optimized_code_for_module(
module_abspath.relative_to(project_root_path), optimized_code, allow_fallback=is_target_file
)

if not code_to_apply.strip():
return False
Expand Down
Loading
Loading