mirror of
https://github.com/codeflash-ai/codeflash.git
synced 2026-05-04 18:25:17 +00:00
feat: add JaCoCo deps, fix checkstyle skip properties, and add auto-config integration tests
Add JaCoCo runtime and CLI dependencies to Gradle build. Split Maven validation skip properties into true/false groups so failOnViolation flags are set to false instead of true. Add Gradle wrapper and integration tests for Java auto-config. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
10f50b391c
commit
d2e99e2f51
7 changed files with 1028 additions and 6 deletions
|
|
@ -22,6 +22,8 @@ dependencies {
|
|||
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")
|
||||
implementation("org.jacoco:org.jacoco.agent:0.8.13:runtime")
|
||||
implementation("org.jacoco:org.jacoco.cli:0.8.13:nodeps")
|
||||
|
||||
testImplementation("org.junit.jupiter:junit-jupiter:5.10.1")
|
||||
testRuntimeOnly("org.junit.platform:junit-platform-launcher")
|
||||
|
|
|
|||
BIN
codeflash-java-runtime/gradle/wrapper/gradle-wrapper.jar
vendored
Normal file
BIN
codeflash-java-runtime/gradle/wrapper/gradle-wrapper.jar
vendored
Normal file
Binary file not shown.
5
codeflash-java-runtime/gradle/wrapper/gradle-wrapper.properties
vendored
Normal file
5
codeflash-java-runtime/gradle/wrapper/gradle-wrapper.properties
vendored
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
distributionBase=GRADLE_USER_HOME
|
||||
distributionPath=wrapper/dists
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-8.12-bin.zip
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
zipStorePath=wrapper/dists
|
||||
176
codeflash-java-runtime/gradlew
vendored
Executable file
176
codeflash-java-runtime/gradlew
vendored
Executable file
|
|
@ -0,0 +1,176 @@
|
|||
#!/usr/bin/env sh
|
||||
|
||||
##############################################################################
|
||||
##
|
||||
## Gradle start up script for UN*X
|
||||
##
|
||||
##############################################################################
|
||||
|
||||
# Attempt to set APP_HOME
|
||||
# Resolve links: $0 may be a link
|
||||
PRG="$0"
|
||||
# Need this for relative symlinks.
|
||||
while [ -h "$PRG" ] ; do
|
||||
ls=`ls -ld "$PRG"`
|
||||
link=`expr "$ls" : '.*-> \(.*\)$'`
|
||||
if expr "$link" : '/.*' > /dev/null; then
|
||||
PRG="$link"
|
||||
else
|
||||
PRG=`dirname "$PRG"`"/$link"
|
||||
fi
|
||||
done
|
||||
SAVED="`pwd`"
|
||||
cd "`dirname \"$PRG\"`/" >/dev/null
|
||||
APP_HOME="`pwd -P`"
|
||||
cd "$SAVED" >/dev/null
|
||||
|
||||
APP_NAME="Gradle"
|
||||
APP_BASE_NAME=`basename "$0"`
|
||||
|
||||
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
||||
DEFAULT_JVM_OPTS=""
|
||||
|
||||
# Use the maximum available, or set MAX_FD != -1 to use that value.
|
||||
MAX_FD="maximum"
|
||||
|
||||
warn () {
|
||||
echo "$*"
|
||||
}
|
||||
|
||||
die () {
|
||||
echo
|
||||
echo "$*"
|
||||
echo
|
||||
exit 1
|
||||
}
|
||||
|
||||
# OS specific support (must be 'true' or 'false').
|
||||
cygwin=false
|
||||
msys=false
|
||||
darwin=false
|
||||
nonstop=false
|
||||
case "`uname`" in
|
||||
CYGWIN* )
|
||||
cygwin=true
|
||||
;;
|
||||
Darwin* )
|
||||
darwin=true
|
||||
;;
|
||||
MINGW* )
|
||||
msys=true
|
||||
;;
|
||||
NONSTOP* )
|
||||
nonstop=true
|
||||
;;
|
||||
esac
|
||||
|
||||
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
|
||||
|
||||
# Determine the Java command to use to start the JVM.
|
||||
if [ -n "$JAVA_HOME" ] ; then
|
||||
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
|
||||
# IBM's JDK on AIX uses strange locations for the executables
|
||||
JAVACMD="$JAVA_HOME/jre/sh/java"
|
||||
else
|
||||
JAVACMD="$JAVA_HOME/bin/java"
|
||||
fi
|
||||
if [ ! -x "$JAVACMD" ] ; then
|
||||
die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
|
||||
|
||||
Please set the JAVA_HOME variable in your environment to match the
|
||||
location of your Java installation."
|
||||
fi
|
||||
else
|
||||
JAVACMD="java"
|
||||
which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
||||
|
||||
Please set the JAVA_HOME variable in your environment to match the
|
||||
location of your Java installation."
|
||||
fi
|
||||
|
||||
# Increase the maximum file descriptors if we can.
|
||||
if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
|
||||
MAX_FD_LIMIT=`ulimit -H -n`
|
||||
if [ $? -eq 0 ] ; then
|
||||
if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
|
||||
MAX_FD="$MAX_FD_LIMIT"
|
||||
fi
|
||||
ulimit -n $MAX_FD
|
||||
if [ $? -ne 0 ] ; then
|
||||
warn "Could not set maximum file descriptor limit: $MAX_FD"
|
||||
fi
|
||||
else
|
||||
warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
|
||||
fi
|
||||
fi
|
||||
|
||||
# For Darwin, add options to specify how the application appears in the dock
|
||||
if $darwin; then
|
||||
GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
|
||||
fi
|
||||
|
||||
# For Cygwin, switch paths to Windows format before running java
|
||||
if $cygwin ; then
|
||||
APP_HOME=`cygpath --path --mixed "$APP_HOME"`
|
||||
CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
|
||||
JAVACMD=`cygpath --unix "$JAVACMD"`
|
||||
|
||||
# We build the pattern for arguments to be converted via cygpath
|
||||
ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
|
||||
SEP=""
|
||||
for dir in $ROOTDIRSRAW ; do
|
||||
ROOTDIRS="$ROOTDIRS$SEP$dir"
|
||||
SEP="|"
|
||||
done
|
||||
OURCYGPATTERN="(^($ROOTDIRS))"
|
||||
# Add a user-defined pattern to the cygpath arguments
|
||||
if [ "$GRADLE_CYGPATTERN" != "" ] ; then
|
||||
OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
|
||||
fi
|
||||
# Now convert the arguments - kludge to limit ourselves to /bin/sh
|
||||
i=0
|
||||
for arg in "$@" ; do
|
||||
CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
|
||||
CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
|
||||
|
||||
if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
|
||||
eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
|
||||
else
|
||||
eval `echo args$i`="\"$arg\""
|
||||
fi
|
||||
i=$((i+1))
|
||||
done
|
||||
case $i in
|
||||
(0) set -- ;;
|
||||
(1) set -- "$args0" ;;
|
||||
(2) set -- "$args0" "$args1" ;;
|
||||
(3) set -- "$args0" "$args1" "$args2" ;;
|
||||
(4) set -- "$args0" "$args1" "$args2" "$args3" ;;
|
||||
(5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
|
||||
(6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
|
||||
(7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
|
||||
(8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
|
||||
(9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
|
||||
esac
|
||||
fi
|
||||
|
||||
# Escape application args
|
||||
save () {
|
||||
for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
|
||||
echo " "
|
||||
}
|
||||
APP_ARGS=$(save "$@")
|
||||
|
||||
# Collect all arguments for the java command, following the shell quoting and substitution rules
|
||||
if $JAVACMD --add-opens java.base/java.lang=ALL-UNNAMED -version ; then
|
||||
DEFAULT_JVM_OPTS="--add-opens java.base/java.lang=ALL-UNNAMED $DEFAULT_JVM_OPTS"
|
||||
fi
|
||||
|
||||
eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
|
||||
|
||||
# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong
|
||||
if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then
|
||||
cd "$(dirname "$0")"
|
||||
fi
|
||||
|
||||
exec "$JAVACMD" "$@"
|
||||
84
codeflash-java-runtime/gradlew.bat
vendored
Normal file
84
codeflash-java-runtime/gradlew.bat
vendored
Normal file
|
|
@ -0,0 +1,84 @@
|
|||
@if "%DEBUG%" == "" @echo off
|
||||
@rem ##########################################################################
|
||||
@rem
|
||||
@rem Gradle startup script for Windows
|
||||
@rem
|
||||
@rem ##########################################################################
|
||||
|
||||
@rem Set local scope for the variables with windows NT shell
|
||||
if "%OS%"=="Windows_NT" setlocal
|
||||
|
||||
set DIRNAME=%~dp0
|
||||
if "%DIRNAME%" == "" set DIRNAME=.
|
||||
set APP_BASE_NAME=%~n0
|
||||
set APP_HOME=%DIRNAME%
|
||||
|
||||
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
||||
set DEFAULT_JVM_OPTS=
|
||||
|
||||
@rem Find java.exe
|
||||
if defined JAVA_HOME goto findJavaFromJavaHome
|
||||
|
||||
set JAVA_EXE=java.exe
|
||||
%JAVA_EXE% -version >NUL 2>&1
|
||||
if "%ERRORLEVEL%" == "0" goto init
|
||||
|
||||
echo.
|
||||
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
||||
echo.
|
||||
echo Please set the JAVA_HOME variable in your environment to match the
|
||||
echo location of your Java installation.
|
||||
|
||||
goto fail
|
||||
|
||||
:findJavaFromJavaHome
|
||||
set JAVA_HOME=%JAVA_HOME:"=%
|
||||
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
|
||||
|
||||
if exist "%JAVA_EXE%" goto init
|
||||
|
||||
echo.
|
||||
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
|
||||
echo.
|
||||
echo Please set the JAVA_HOME variable in your environment to match the
|
||||
echo location of your Java installation.
|
||||
|
||||
goto fail
|
||||
|
||||
:init
|
||||
@rem Get command-line arguments, handling Windows variants
|
||||
|
||||
if not "%OS%" == "Windows_NT" goto win9xME_args
|
||||
|
||||
:win9xME_args
|
||||
@rem Slurp the command line arguments.
|
||||
set CMD_LINE_ARGS=
|
||||
set _SKIP=2
|
||||
|
||||
:win9xME_args_slurp
|
||||
if "x%~1" == "x" goto execute
|
||||
|
||||
set CMD_LINE_ARGS=%*
|
||||
|
||||
:execute
|
||||
@rem Setup the command line
|
||||
|
||||
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
|
||||
|
||||
@rem Execute Gradle
|
||||
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
|
||||
|
||||
:end
|
||||
@rem End local scope for the variables with windows NT shell
|
||||
if "%ERRORLEVEL%"=="0" goto mainEnd
|
||||
|
||||
:fail
|
||||
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
|
||||
rem the _cmd.exe /c_ return code!
|
||||
if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
|
||||
exit /b 1
|
||||
|
||||
:mainEnd
|
||||
if "%OS%"=="Windows_NT" endlocal
|
||||
|
||||
:omega
|
||||
|
|
@ -27,8 +27,11 @@ logger = logging.getLogger(__name__)
|
|||
_MAVEN_VALIDATION_SKIP_FLAGS = [
|
||||
"-Drat.skip=true",
|
||||
"-Dcheckstyle.skip=true",
|
||||
"-Ddisable.checks=true",
|
||||
"-Dcheckstyle.failOnViolation=false",
|
||||
"-Dcheckstyle.failsOnError=false",
|
||||
"-Dmaven-checkstyle-plugin.failsOnError=false",
|
||||
"-Dmaven-checkstyle-plugin.failOnViolation=false",
|
||||
"-Dspotbugs.skip=true",
|
||||
"-Dpmd.skip=true",
|
||||
"-Denforcer.skip=true",
|
||||
|
|
@ -154,12 +157,10 @@ def install_codeflash_runtime(project_root: Path, runtime_jar_path: Path, mvn: s
|
|||
return False
|
||||
|
||||
|
||||
_VALIDATION_SKIP_PROPERTIES = [
|
||||
# Properties set to "true" to enable skipping
|
||||
_VALIDATION_SKIP_PROPERTIES_TRUE = [
|
||||
"checkstyle.skip",
|
||||
"checkstyle.failOnViolation",
|
||||
"checkstyle.failsOnError",
|
||||
"maven-checkstyle-plugin.failsOnError",
|
||||
"maven-checkstyle-plugin.failOnViolation",
|
||||
"disable.checks",
|
||||
"spotbugs.skip",
|
||||
"pmd.skip",
|
||||
"rat.skip",
|
||||
|
|
@ -167,6 +168,14 @@ _VALIDATION_SKIP_PROPERTIES = [
|
|||
"japicmp.skip",
|
||||
]
|
||||
|
||||
# Properties set to "false" to disable failure on violations
|
||||
_VALIDATION_SKIP_PROPERTIES_FALSE = [
|
||||
"checkstyle.failOnViolation",
|
||||
"checkstyle.failsOnError",
|
||||
"maven-checkstyle-plugin.failsOnError",
|
||||
"maven-checkstyle-plugin.failOnViolation",
|
||||
]
|
||||
|
||||
# Plugin overrides that explicitly set <skip>true</skip> in the plugin <configuration>.
|
||||
# This handles parent POMs with custom execution IDs that ignore skip properties.
|
||||
_VALIDATION_PLUGIN_OVERRIDES = """\
|
||||
|
|
@ -209,7 +218,8 @@ def inject_validation_skip_properties(pom_path: Path) -> bool:
|
|||
if "<!-- codeflash-validation-skip -->" in content:
|
||||
return True
|
||||
|
||||
props_lines = "".join(f" <{p}>true</{p}>\n" for p in _VALIDATION_SKIP_PROPERTIES)
|
||||
props_lines = "".join(f" <{p}>true</{p}>\n" for p in _VALIDATION_SKIP_PROPERTIES_TRUE)
|
||||
props_lines += "".join(f" <{p}>false</{p}>\n" for p in _VALIDATION_SKIP_PROPERTIES_FALSE)
|
||||
|
||||
# 1. Inject properties
|
||||
closing_idx = content.find("</properties>")
|
||||
|
|
|
|||
745
tests/test_languages/test_java/test_auto_config_integration.py
Normal file
745
tests/test_languages/test_java/test_auto_config_integration.py
Normal file
|
|
@ -0,0 +1,745 @@
|
|||
"""Integration tests for Java auto-config logic across Gradle and Maven projects.
|
||||
|
||||
Tests the end-to-end flow: build tool detection → strategy selection →
|
||||
config parsing → write → read → remove, using realistic project layouts.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from pathlib import Path
|
||||
|
||||
import pytest
|
||||
|
||||
from codeflash.languages.java.build_config_strategy import (
|
||||
GradleConfigStrategy,
|
||||
MavenConfigStrategy,
|
||||
get_config_strategy,
|
||||
parse_java_project_config,
|
||||
)
|
||||
from codeflash.languages.java.build_tools import (
|
||||
BuildTool,
|
||||
detect_build_tool,
|
||||
find_source_root,
|
||||
find_test_root,
|
||||
get_project_info,
|
||||
)
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Helpers — create realistic project layouts in tmp_path
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
|
||||
def _make_maven_project(root: Path, *, with_namespace: bool = True, java_version: str = "17") -> Path:
|
||||
ns = ' xmlns="http://maven.apache.org/POM/4.0.0"' if with_namespace else ""
|
||||
pom = root / "pom.xml"
|
||||
pom.write_text(
|
||||
f'<?xml version="1.0" encoding="UTF-8"?>\n'
|
||||
f"<project{ns}>\n"
|
||||
f" <modelVersion>4.0.0</modelVersion>\n"
|
||||
f" <groupId>com.example</groupId>\n"
|
||||
f" <artifactId>demo-app</artifactId>\n"
|
||||
f" <version>1.0.0</version>\n"
|
||||
f" <properties>\n"
|
||||
f" <maven.compiler.source>{java_version}</maven.compiler.source>\n"
|
||||
f" <maven.compiler.target>{java_version}</maven.compiler.target>\n"
|
||||
f" </properties>\n"
|
||||
f"</project>\n",
|
||||
encoding="utf-8",
|
||||
)
|
||||
src = root / "src" / "main" / "java" / "com" / "example"
|
||||
src.mkdir(parents=True)
|
||||
(src / "App.java").write_text("package com.example;\npublic class App {}\n", encoding="utf-8")
|
||||
test = root / "src" / "test" / "java" / "com" / "example"
|
||||
test.mkdir(parents=True)
|
||||
(test / "AppTest.java").write_text(
|
||||
"package com.example;\nimport org.junit.jupiter.api.Test;\nclass AppTest {\n"
|
||||
" @Test void works() {}\n}\n",
|
||||
encoding="utf-8",
|
||||
)
|
||||
return root
|
||||
|
||||
|
||||
def _make_gradle_project(root: Path, *, kotlin_dsl: bool = False) -> Path:
|
||||
ext = ".kts" if kotlin_dsl else ""
|
||||
build_file = root / f"build.gradle{ext}"
|
||||
build_file.write_text(
|
||||
"plugins {\n id 'java'\n}\ngroup = 'com.example'\nversion = '1.0.0'\n",
|
||||
encoding="utf-8",
|
||||
)
|
||||
(root / f"settings.gradle{ext}").write_text(f"rootProject.name = 'demo'\n", encoding="utf-8")
|
||||
src = root / "src" / "main" / "java" / "com" / "example"
|
||||
src.mkdir(parents=True)
|
||||
(src / "App.java").write_text("package com.example;\npublic class App {}\n", encoding="utf-8")
|
||||
test = root / "src" / "test" / "java" / "com" / "example"
|
||||
test.mkdir(parents=True)
|
||||
(test / "AppTest.java").write_text(
|
||||
"package com.example;\nimport org.junit.jupiter.api.Test;\nclass AppTest {\n"
|
||||
" @Test void works() {}\n}\n",
|
||||
encoding="utf-8",
|
||||
)
|
||||
return root
|
||||
|
||||
|
||||
def _make_maven_multimodule(root: Path) -> Path:
|
||||
# Parent pom with modules
|
||||
(root / "pom.xml").write_text(
|
||||
'<?xml version="1.0" encoding="UTF-8"?>\n'
|
||||
'<project xmlns="http://maven.apache.org/POM/4.0.0">\n'
|
||||
" <modelVersion>4.0.0</modelVersion>\n"
|
||||
" <groupId>com.example</groupId>\n"
|
||||
" <artifactId>parent</artifactId>\n"
|
||||
" <version>1.0.0</version>\n"
|
||||
" <packaging>pom</packaging>\n"
|
||||
" <modules>\n"
|
||||
" <module>core</module>\n"
|
||||
" <module>api</module>\n"
|
||||
" <module>tests</module>\n"
|
||||
" </modules>\n"
|
||||
"</project>\n",
|
||||
encoding="utf-8",
|
||||
)
|
||||
|
||||
# core module — main source
|
||||
core = root / "core"
|
||||
core.mkdir()
|
||||
(core / "pom.xml").write_text(
|
||||
'<project xmlns="http://maven.apache.org/POM/4.0.0">\n'
|
||||
" <modelVersion>4.0.0</modelVersion>\n"
|
||||
" <parent>\n"
|
||||
" <groupId>com.example</groupId>\n"
|
||||
" <artifactId>parent</artifactId>\n"
|
||||
" <version>1.0.0</version>\n"
|
||||
" </parent>\n"
|
||||
" <artifactId>core</artifactId>\n"
|
||||
"</project>\n",
|
||||
encoding="utf-8",
|
||||
)
|
||||
core_src = core / "src" / "main" / "java" / "com" / "example"
|
||||
core_src.mkdir(parents=True)
|
||||
(core_src / "Core.java").write_text("package com.example;\npublic class Core {}\n", encoding="utf-8")
|
||||
(core_src / "Utils.java").write_text("package com.example;\npublic class Utils {}\n", encoding="utf-8")
|
||||
|
||||
# api module — fewer source files
|
||||
api = root / "api"
|
||||
api.mkdir()
|
||||
(api / "pom.xml").write_text(
|
||||
'<project xmlns="http://maven.apache.org/POM/4.0.0">\n'
|
||||
" <modelVersion>4.0.0</modelVersion>\n"
|
||||
" <parent>\n"
|
||||
" <groupId>com.example</groupId>\n"
|
||||
" <artifactId>parent</artifactId>\n"
|
||||
" <version>1.0.0</version>\n"
|
||||
" </parent>\n"
|
||||
" <artifactId>api</artifactId>\n"
|
||||
"</project>\n",
|
||||
encoding="utf-8",
|
||||
)
|
||||
api_src = api / "src" / "main" / "java" / "com" / "example"
|
||||
api_src.mkdir(parents=True)
|
||||
(api_src / "Api.java").write_text("package com.example;\npublic class Api {}\n", encoding="utf-8")
|
||||
|
||||
# tests module — integration tests
|
||||
tests = root / "tests"
|
||||
tests.mkdir()
|
||||
(tests / "pom.xml").write_text(
|
||||
'<project xmlns="http://maven.apache.org/POM/4.0.0">\n'
|
||||
" <modelVersion>4.0.0</modelVersion>\n"
|
||||
" <parent>\n"
|
||||
" <groupId>com.example</groupId>\n"
|
||||
" <artifactId>parent</artifactId>\n"
|
||||
" <version>1.0.0</version>\n"
|
||||
" </parent>\n"
|
||||
" <artifactId>tests</artifactId>\n"
|
||||
"</project>\n",
|
||||
encoding="utf-8",
|
||||
)
|
||||
test_src = tests / "src" / "test" / "java" / "com" / "example"
|
||||
test_src.mkdir(parents=True)
|
||||
(test_src / "IntegrationTest.java").write_text(
|
||||
"package com.example;\npublic class IntegrationTest {}\n", encoding="utf-8"
|
||||
)
|
||||
|
||||
return root
|
||||
|
||||
|
||||
def _make_gradle_multimodule(root: Path, *, kotlin_dsl: bool = False) -> Path:
|
||||
ext = ".kts" if kotlin_dsl else ""
|
||||
|
||||
(root / f"build.gradle{ext}").write_text("// root build\n", encoding="utf-8")
|
||||
(root / f"settings.gradle{ext}").write_text(
|
||||
"rootProject.name = 'multi'\ninclude 'core', 'api'\n", encoding="utf-8"
|
||||
)
|
||||
|
||||
for module_name in ["core", "api"]:
|
||||
mod = root / module_name
|
||||
mod.mkdir()
|
||||
(mod / f"build.gradle{ext}").write_text(
|
||||
f"plugins {{\n id 'java'\n}}\n", encoding="utf-8"
|
||||
)
|
||||
src = mod / "src" / "main" / "java" / "com" / "example"
|
||||
src.mkdir(parents=True)
|
||||
(src / f"{module_name.capitalize()}.java").write_text(
|
||||
f"package com.example;\npublic class {module_name.capitalize()} {{}}\n", encoding="utf-8"
|
||||
)
|
||||
test_dir = mod / "src" / "test" / "java" / "com" / "example"
|
||||
test_dir.mkdir(parents=True)
|
||||
(test_dir / f"{module_name.capitalize()}Test.java").write_text(
|
||||
f"package com.example;\nclass {module_name.capitalize()}Test {{}}\n", encoding="utf-8"
|
||||
)
|
||||
|
||||
return root
|
||||
|
||||
|
||||
# ===================================================================
|
||||
# Integration: Maven — detection through full config lifecycle
|
||||
# ===================================================================
|
||||
|
||||
|
||||
class TestMavenAutoConfigIntegration:
|
||||
"""End-to-end: detect Maven → get strategy → parse config → write → read → remove."""
|
||||
|
||||
def test_standard_maven_detection_to_config(self, tmp_path: Path) -> None:
|
||||
project = _make_maven_project(tmp_path)
|
||||
|
||||
assert detect_build_tool(project) == BuildTool.MAVEN
|
||||
assert find_source_root(project) == project / "src" / "main" / "java"
|
||||
assert find_test_root(project) == project / "src" / "test" / "java"
|
||||
|
||||
strategy = get_config_strategy(project)
|
||||
assert isinstance(strategy, MavenConfigStrategy)
|
||||
|
||||
config = parse_java_project_config(project)
|
||||
assert config is not None
|
||||
assert config["language"] == "java"
|
||||
assert config["module_root"] == str(project / "src" / "main" / "java")
|
||||
assert config["tests_root"] == str(project / "src" / "test" / "java")
|
||||
assert config["git_remote"] == "origin"
|
||||
assert config["disable_telemetry"] is False
|
||||
|
||||
def test_maven_full_lifecycle_write_read_remove(self, tmp_path: Path) -> None:
|
||||
project = _make_maven_project(tmp_path)
|
||||
strategy = get_config_strategy(project)
|
||||
|
||||
# Write config
|
||||
ok, msg = strategy.write_codeflash_properties(project, {
|
||||
"module-root": "custom/src",
|
||||
"tests-root": "custom/test",
|
||||
"git-remote": "upstream",
|
||||
"disable-telemetry": True,
|
||||
"ignore-paths": ["target", ".idea"],
|
||||
"formatter-cmds": ["spotless:apply"],
|
||||
})
|
||||
assert ok, msg
|
||||
|
||||
# Read back
|
||||
props = strategy.read_codeflash_properties(project)
|
||||
assert props["moduleRoot"] == "custom/src"
|
||||
assert props["testsRoot"] == "custom/test"
|
||||
assert props["gitRemote"] == "upstream"
|
||||
assert props["disableTelemetry"] == "true"
|
||||
assert props["ignorePaths"] == "target,.idea"
|
||||
assert props["formatterCmds"] == "spotless:apply"
|
||||
|
||||
# Verify non-codeflash properties are preserved
|
||||
pom_text = (project / "pom.xml").read_text(encoding="utf-8")
|
||||
assert "maven.compiler.source" in pom_text
|
||||
assert "maven.compiler.target" in pom_text
|
||||
|
||||
# Remove
|
||||
ok, msg = strategy.remove_codeflash_properties(project)
|
||||
assert ok, msg
|
||||
|
||||
# Verify removed
|
||||
props_after = strategy.read_codeflash_properties(project)
|
||||
assert props_after == {}
|
||||
|
||||
# Verify non-codeflash properties still preserved
|
||||
pom_after = (project / "pom.xml").read_text(encoding="utf-8")
|
||||
assert "maven.compiler.source" in pom_after
|
||||
|
||||
def test_maven_with_namespace_full_lifecycle(self, tmp_path: Path) -> None:
|
||||
project = _make_maven_project(tmp_path, with_namespace=True)
|
||||
strategy = get_config_strategy(project)
|
||||
|
||||
ok, _ = strategy.write_codeflash_properties(project, {"module-root": "lib/main"})
|
||||
assert ok
|
||||
|
||||
props = strategy.read_codeflash_properties(project)
|
||||
assert props["moduleRoot"] == "lib/main"
|
||||
|
||||
# Verify namespace preserved, no ns0: prefix
|
||||
pom_text = (project / "pom.xml").read_text(encoding="utf-8")
|
||||
assert 'xmlns="http://maven.apache.org/POM/4.0.0"' in pom_text
|
||||
assert "ns0:" not in pom_text
|
||||
|
||||
def test_maven_without_namespace_full_lifecycle(self, tmp_path: Path) -> None:
|
||||
project = _make_maven_project(tmp_path, with_namespace=False)
|
||||
strategy = get_config_strategy(project)
|
||||
|
||||
ok, _ = strategy.write_codeflash_properties(project, {"module-root": "src/main/java"})
|
||||
assert ok
|
||||
|
||||
props = strategy.read_codeflash_properties(project)
|
||||
assert props["moduleRoot"] == "src/main/java"
|
||||
|
||||
def test_maven_user_overrides_take_precedence(self, tmp_path: Path) -> None:
|
||||
project = _make_maven_project(tmp_path)
|
||||
strategy = get_config_strategy(project)
|
||||
|
||||
# Write user overrides to pom.xml
|
||||
ok, _ = strategy.write_codeflash_properties(project, {
|
||||
"module-root": "custom/src",
|
||||
"tests-root": "custom/test",
|
||||
"disable-telemetry": True,
|
||||
})
|
||||
assert ok
|
||||
|
||||
# Create the custom directories
|
||||
(project / "custom" / "src").mkdir(parents=True)
|
||||
(project / "custom" / "test").mkdir(parents=True)
|
||||
|
||||
# parse_java_project_config should use user overrides, not auto-detected paths
|
||||
config = parse_java_project_config(project)
|
||||
assert config is not None
|
||||
assert config["module_root"] == str((project / "custom" / "src").resolve())
|
||||
assert config["tests_root"] == str((project / "custom" / "test").resolve())
|
||||
assert config["disable_telemetry"] is True
|
||||
|
||||
def test_maven_project_info_extraction(self, tmp_path: Path) -> None:
|
||||
project = _make_maven_project(tmp_path, java_version="11")
|
||||
|
||||
info = get_project_info(project)
|
||||
assert info is not None
|
||||
assert info.build_tool == BuildTool.MAVEN
|
||||
assert info.group_id == "com.example"
|
||||
assert info.artifact_id == "demo-app"
|
||||
assert info.version == "1.0.0"
|
||||
assert info.java_version == "11"
|
||||
assert len(info.source_roots) == 1
|
||||
assert len(info.test_roots) == 1
|
||||
|
||||
def test_maven_overwrite_then_overwrite(self, tmp_path: Path) -> None:
|
||||
project = _make_maven_project(tmp_path)
|
||||
strategy = get_config_strategy(project)
|
||||
|
||||
# First write
|
||||
ok, _ = strategy.write_codeflash_properties(project, {"module-root": "v1"})
|
||||
assert ok
|
||||
assert strategy.read_codeflash_properties(project)["moduleRoot"] == "v1"
|
||||
|
||||
# Second write replaces previous values
|
||||
ok, _ = strategy.write_codeflash_properties(project, {"module-root": "v2", "git-remote": "upstream"})
|
||||
assert ok
|
||||
props = strategy.read_codeflash_properties(project)
|
||||
assert props["moduleRoot"] == "v2"
|
||||
assert props["gitRemote"] == "upstream"
|
||||
|
||||
|
||||
# ===================================================================
|
||||
# Integration: Maven multi-module
|
||||
# ===================================================================
|
||||
|
||||
|
||||
class TestMavenMultiModuleIntegration:
|
||||
"""End-to-end auto-config for Maven multi-module projects."""
|
||||
|
||||
def test_multimodule_detects_source_from_largest_module(self, tmp_path: Path) -> None:
|
||||
project = _make_maven_multimodule(tmp_path)
|
||||
|
||||
config = parse_java_project_config(project)
|
||||
assert config is not None
|
||||
# core has 2 java files, api has 1 → core should be chosen as source root
|
||||
assert "core" in config["module_root"]
|
||||
assert config["module_root"].endswith(str(Path("src") / "main" / "java"))
|
||||
|
||||
def test_multimodule_detects_test_module(self, tmp_path: Path) -> None:
|
||||
project = _make_maven_multimodule(tmp_path)
|
||||
|
||||
config = parse_java_project_config(project)
|
||||
assert config is not None
|
||||
# "tests" module has "test" in its name → should be detected as test root
|
||||
assert "tests" in config["tests_root"]
|
||||
|
||||
def test_multimodule_build_tool_detection(self, tmp_path: Path) -> None:
|
||||
project = _make_maven_multimodule(tmp_path)
|
||||
|
||||
assert detect_build_tool(project) == BuildTool.MAVEN
|
||||
strategy = get_config_strategy(project)
|
||||
assert isinstance(strategy, MavenConfigStrategy)
|
||||
|
||||
def test_multimodule_config_write_read_on_parent(self, tmp_path: Path) -> None:
|
||||
project = _make_maven_multimodule(tmp_path)
|
||||
strategy = get_config_strategy(project)
|
||||
|
||||
ok, _ = strategy.write_codeflash_properties(project, {"git-remote": "upstream"})
|
||||
assert ok
|
||||
|
||||
props = strategy.read_codeflash_properties(project)
|
||||
assert props["gitRemote"] == "upstream"
|
||||
|
||||
# Verify the parent pom still has modules
|
||||
pom_text = (project / "pom.xml").read_text(encoding="utf-8")
|
||||
assert "<module>core</module>" in pom_text
|
||||
assert "<module>api</module>" in pom_text
|
||||
|
||||
def test_multimodule_with_custom_source_directory(self, tmp_path: Path) -> None:
|
||||
project = _make_maven_multimodule(tmp_path)
|
||||
|
||||
# Modify core module to use a custom source directory
|
||||
core_pom = project / "core" / "pom.xml"
|
||||
core_pom.write_text(
|
||||
'<project xmlns="http://maven.apache.org/POM/4.0.0">\n'
|
||||
" <modelVersion>4.0.0</modelVersion>\n"
|
||||
" <parent>\n"
|
||||
" <groupId>com.example</groupId>\n"
|
||||
" <artifactId>parent</artifactId>\n"
|
||||
" <version>1.0.0</version>\n"
|
||||
" </parent>\n"
|
||||
" <artifactId>core</artifactId>\n"
|
||||
" <build>\n"
|
||||
" <sourceDirectory>src/main/custom</sourceDirectory>\n"
|
||||
" </build>\n"
|
||||
"</project>\n",
|
||||
encoding="utf-8",
|
||||
)
|
||||
custom_src = project / "core" / "src" / "main" / "custom"
|
||||
custom_src.mkdir(parents=True)
|
||||
(custom_src / "Main.java").write_text("public class Main {}\n", encoding="utf-8")
|
||||
|
||||
config = parse_java_project_config(project)
|
||||
assert config is not None
|
||||
# Should detect the custom source directory from the module pom
|
||||
# The exact path depends on which module has more java files
|
||||
assert config["module_root"] is not None
|
||||
|
||||
|
||||
# ===================================================================
|
||||
# Integration: Gradle — detection through full config lifecycle
|
||||
# ===================================================================
|
||||
|
||||
|
||||
class TestGradleAutoConfigIntegration:
|
||||
"""End-to-end: detect Gradle → get strategy → parse config → write → read → remove."""
|
||||
|
||||
def test_standard_gradle_detection_to_config(self, tmp_path: Path) -> None:
|
||||
project = _make_gradle_project(tmp_path)
|
||||
|
||||
assert detect_build_tool(project) == BuildTool.GRADLE
|
||||
assert find_source_root(project) == project / "src" / "main" / "java"
|
||||
assert find_test_root(project) == project / "src" / "test" / "java"
|
||||
|
||||
strategy = get_config_strategy(project)
|
||||
assert isinstance(strategy, GradleConfigStrategy)
|
||||
|
||||
config = parse_java_project_config(project)
|
||||
assert config is not None
|
||||
assert config["language"] == "java"
|
||||
assert config["module_root"] == str(project / "src" / "main" / "java")
|
||||
assert config["tests_root"] == str(project / "src" / "test" / "java")
|
||||
|
||||
def test_gradle_kotlin_dsl_detection_to_config(self, tmp_path: Path) -> None:
|
||||
project = _make_gradle_project(tmp_path, kotlin_dsl=True)
|
||||
|
||||
assert detect_build_tool(project) == BuildTool.GRADLE
|
||||
|
||||
strategy = get_config_strategy(project)
|
||||
assert isinstance(strategy, GradleConfigStrategy)
|
||||
|
||||
config = parse_java_project_config(project)
|
||||
assert config is not None
|
||||
assert config["language"] == "java"
|
||||
|
||||
def test_gradle_full_lifecycle_write_read_remove(self, tmp_path: Path) -> None:
|
||||
project = _make_gradle_project(tmp_path)
|
||||
strategy = get_config_strategy(project)
|
||||
|
||||
# Write config
|
||||
ok, msg = strategy.write_codeflash_properties(project, {
|
||||
"module-root": "custom/src",
|
||||
"tests-root": "custom/test",
|
||||
"git-remote": "upstream",
|
||||
"disable-telemetry": True,
|
||||
"ignore-paths": ["build", ".gradle"],
|
||||
"formatter-cmds": ["spotlessApply"],
|
||||
})
|
||||
assert ok, msg
|
||||
|
||||
# Read back
|
||||
props = strategy.read_codeflash_properties(project)
|
||||
assert props["moduleRoot"] == "custom/src"
|
||||
assert props["testsRoot"] == "custom/test"
|
||||
assert props["gitRemote"] == "upstream"
|
||||
assert props["disableTelemetry"] == "true"
|
||||
assert props["ignorePaths"] == "build,.gradle"
|
||||
assert props["formatterCmds"] == "spotlessApply"
|
||||
|
||||
# Verify gradle.properties has the codeflash header comment
|
||||
gp_text = (project / "gradle.properties").read_text(encoding="utf-8")
|
||||
assert "# Codeflash configuration" in gp_text
|
||||
|
||||
# Remove
|
||||
ok, msg = strategy.remove_codeflash_properties(project)
|
||||
assert ok, msg
|
||||
|
||||
# Verify removed
|
||||
props_after = strategy.read_codeflash_properties(project)
|
||||
assert props_after == {}
|
||||
|
||||
# Verify header comment also removed
|
||||
gp_after = (project / "gradle.properties").read_text(encoding="utf-8")
|
||||
assert "Codeflash" not in gp_after
|
||||
|
||||
def test_gradle_preserves_existing_properties(self, tmp_path: Path) -> None:
|
||||
project = _make_gradle_project(tmp_path)
|
||||
|
||||
# Pre-existing gradle.properties with user settings
|
||||
(project / "gradle.properties").write_text(
|
||||
"org.gradle.jvmargs=-Xmx4g -XX:MaxMetaspaceSize=512m\n"
|
||||
"org.gradle.parallel=true\n"
|
||||
"org.gradle.caching=true\n",
|
||||
encoding="utf-8",
|
||||
)
|
||||
|
||||
strategy = get_config_strategy(project)
|
||||
ok, _ = strategy.write_codeflash_properties(project, {"module-root": "lib/src"})
|
||||
assert ok
|
||||
|
||||
gp_text = (project / "gradle.properties").read_text(encoding="utf-8")
|
||||
assert "org.gradle.jvmargs=-Xmx4g" in gp_text
|
||||
assert "org.gradle.parallel=true" in gp_text
|
||||
assert "org.gradle.caching=true" in gp_text
|
||||
assert "codeflash.moduleRoot=lib/src" in gp_text
|
||||
|
||||
def test_gradle_user_overrides_take_precedence(self, tmp_path: Path) -> None:
|
||||
project = _make_gradle_project(tmp_path)
|
||||
strategy = get_config_strategy(project)
|
||||
|
||||
ok, _ = strategy.write_codeflash_properties(project, {
|
||||
"module-root": "custom/src",
|
||||
"tests-root": "custom/test",
|
||||
})
|
||||
assert ok
|
||||
|
||||
(project / "custom" / "src").mkdir(parents=True)
|
||||
(project / "custom" / "test").mkdir(parents=True)
|
||||
|
||||
config = parse_java_project_config(project)
|
||||
assert config is not None
|
||||
assert config["module_root"] == str((project / "custom" / "src").resolve())
|
||||
assert config["tests_root"] == str((project / "custom" / "test").resolve())
|
||||
|
||||
def test_gradle_overwrite_then_overwrite(self, tmp_path: Path) -> None:
|
||||
project = _make_gradle_project(tmp_path)
|
||||
strategy = get_config_strategy(project)
|
||||
|
||||
ok, _ = strategy.write_codeflash_properties(project, {"module-root": "v1"})
|
||||
assert ok
|
||||
assert strategy.read_codeflash_properties(project)["moduleRoot"] == "v1"
|
||||
|
||||
ok, _ = strategy.write_codeflash_properties(project, {"module-root": "v2", "git-remote": "upstream"})
|
||||
assert ok
|
||||
props = strategy.read_codeflash_properties(project)
|
||||
assert props["moduleRoot"] == "v2"
|
||||
assert props["gitRemote"] == "upstream"
|
||||
# Old values should not persist
|
||||
gp_text = (project / "gradle.properties").read_text(encoding="utf-8")
|
||||
assert gp_text.count("codeflash.moduleRoot") == 1
|
||||
|
||||
def test_gradle_project_info_extraction(self, tmp_path: Path) -> None:
|
||||
project = _make_gradle_project(tmp_path)
|
||||
|
||||
info = get_project_info(project)
|
||||
assert info is not None
|
||||
assert info.build_tool == BuildTool.GRADLE
|
||||
assert len(info.source_roots) == 1
|
||||
assert len(info.test_roots) == 1
|
||||
|
||||
|
||||
# ===================================================================
|
||||
# Integration: Gradle multi-module
|
||||
# ===================================================================
|
||||
|
||||
|
||||
class TestGradleMultiModuleIntegration:
|
||||
"""End-to-end auto-config for Gradle multi-module projects."""
|
||||
|
||||
def test_multimodule_root_detection(self, tmp_path: Path) -> None:
|
||||
project = _make_gradle_multimodule(tmp_path)
|
||||
|
||||
assert detect_build_tool(project) == BuildTool.GRADLE
|
||||
strategy = get_config_strategy(project)
|
||||
assert isinstance(strategy, GradleConfigStrategy)
|
||||
|
||||
def test_multimodule_config_write_read_at_root(self, tmp_path: Path) -> None:
|
||||
project = _make_gradle_multimodule(tmp_path)
|
||||
strategy = get_config_strategy(project)
|
||||
|
||||
ok, _ = strategy.write_codeflash_properties(project, {
|
||||
"module-root": "core/src/main/java",
|
||||
"tests-root": "core/src/test/java",
|
||||
})
|
||||
assert ok
|
||||
|
||||
props = strategy.read_codeflash_properties(project)
|
||||
assert props["moduleRoot"] == "core/src/main/java"
|
||||
assert props["testsRoot"] == "core/src/test/java"
|
||||
|
||||
def test_multimodule_kotlin_dsl(self, tmp_path: Path) -> None:
|
||||
project = _make_gradle_multimodule(tmp_path, kotlin_dsl=True)
|
||||
|
||||
assert detect_build_tool(project) == BuildTool.GRADLE
|
||||
config = parse_java_project_config(project)
|
||||
assert config is not None
|
||||
assert config["language"] == "java"
|
||||
|
||||
|
||||
# ===================================================================
|
||||
# Integration: cross-cutting scenarios
|
||||
# ===================================================================
|
||||
|
||||
|
||||
class TestCrossCuttingIntegration:
|
||||
"""Scenarios that test across both build tools or edge conditions."""
|
||||
|
||||
def test_maven_takes_precedence_over_gradle(self, tmp_path: Path) -> None:
|
||||
# Create both Maven and Gradle files
|
||||
_make_maven_project(tmp_path)
|
||||
(tmp_path / "build.gradle").write_text("plugins { id 'java' }\n", encoding="utf-8")
|
||||
|
||||
assert detect_build_tool(tmp_path) == BuildTool.MAVEN
|
||||
strategy = get_config_strategy(tmp_path)
|
||||
assert isinstance(strategy, MavenConfigStrategy)
|
||||
|
||||
def test_empty_directory_returns_unknown(self, tmp_path: Path) -> None:
|
||||
assert detect_build_tool(tmp_path) == BuildTool.UNKNOWN
|
||||
assert parse_java_project_config(tmp_path) is None
|
||||
with pytest.raises(ValueError, match="No supported Java build tool"):
|
||||
get_config_strategy(tmp_path)
|
||||
|
||||
def test_maven_config_with_all_properties(self, tmp_path: Path) -> None:
|
||||
project = _make_maven_project(tmp_path)
|
||||
strategy = get_config_strategy(project)
|
||||
|
||||
full_config = {
|
||||
"module-root": "src/main/java",
|
||||
"tests-root": "src/test/java",
|
||||
"git-remote": "upstream",
|
||||
"disable-telemetry": True,
|
||||
"ignore-paths": ["target", ".idea", "*.iml"],
|
||||
"formatter-cmds": ["mvn spotless:apply", "mvn formatter:format"],
|
||||
}
|
||||
|
||||
ok, _ = strategy.write_codeflash_properties(project, full_config)
|
||||
assert ok
|
||||
|
||||
props = strategy.read_codeflash_properties(project)
|
||||
assert len(props) == 6
|
||||
assert props["moduleRoot"] == "src/main/java"
|
||||
assert props["testsRoot"] == "src/test/java"
|
||||
assert props["gitRemote"] == "upstream"
|
||||
assert props["disableTelemetry"] == "true"
|
||||
assert props["ignorePaths"] == "target,.idea,*.iml"
|
||||
assert props["formatterCmds"] == "mvn spotless:apply,mvn formatter:format"
|
||||
|
||||
def test_gradle_config_with_all_properties(self, tmp_path: Path) -> None:
|
||||
project = _make_gradle_project(tmp_path)
|
||||
strategy = get_config_strategy(project)
|
||||
|
||||
full_config = {
|
||||
"module-root": "lib/src/main/java",
|
||||
"tests-root": "lib/src/test/java",
|
||||
"git-remote": "upstream",
|
||||
"disable-telemetry": False,
|
||||
"ignore-paths": ["build", ".gradle"],
|
||||
"formatter-cmds": ["./gradlew spotlessApply"],
|
||||
}
|
||||
|
||||
ok, _ = strategy.write_codeflash_properties(project, full_config)
|
||||
assert ok
|
||||
|
||||
props = strategy.read_codeflash_properties(project)
|
||||
assert len(props) == 6
|
||||
assert props["moduleRoot"] == "lib/src/main/java"
|
||||
assert props["disableTelemetry"] == "false"
|
||||
|
||||
def test_parse_config_feeds_ignore_paths_and_formatter_cmds(self, tmp_path: Path) -> None:
|
||||
project = _make_maven_project(tmp_path)
|
||||
strategy = get_config_strategy(project)
|
||||
|
||||
ok, _ = strategy.write_codeflash_properties(project, {
|
||||
"ignore-paths": ["target", "generated"],
|
||||
"formatter-cmds": ["mvn fmt:format"],
|
||||
})
|
||||
assert ok
|
||||
|
||||
config = parse_java_project_config(project)
|
||||
assert config is not None
|
||||
assert len(config["ignore_paths"]) == 2
|
||||
assert any("target" in p for p in config["ignore_paths"])
|
||||
assert any("generated" in p for p in config["ignore_paths"])
|
||||
assert config["formatter_cmds"] == ["mvn fmt:format"]
|
||||
|
||||
def test_parse_config_defaults_when_no_user_overrides(self, tmp_path: Path) -> None:
|
||||
project = _make_maven_project(tmp_path)
|
||||
|
||||
config = parse_java_project_config(project)
|
||||
assert config is not None
|
||||
assert config["git_remote"] == "origin"
|
||||
assert config["disable_telemetry"] is False
|
||||
assert config["ignore_paths"] == []
|
||||
assert config["formatter_cmds"] == []
|
||||
|
||||
def test_subdir_detection_from_child(self, tmp_path: Path) -> None:
|
||||
"""Build tool detection works from a subdirectory (multi-module child)."""
|
||||
project = _make_maven_project(tmp_path)
|
||||
child = project / "src" / "main" / "java"
|
||||
|
||||
# detect_build_tool should find pom.xml in parent directories
|
||||
assert detect_build_tool(child) == BuildTool.MAVEN
|
||||
|
||||
def test_gradle_write_creates_properties_file_if_missing(self, tmp_path: Path) -> None:
|
||||
project = _make_gradle_project(tmp_path)
|
||||
props_path = project / "gradle.properties"
|
||||
|
||||
# Ensure no gradle.properties exists
|
||||
if props_path.exists():
|
||||
props_path.unlink()
|
||||
assert not props_path.exists()
|
||||
|
||||
strategy = get_config_strategy(project)
|
||||
ok, _ = strategy.write_codeflash_properties(project, {"module-root": "src/main/java"})
|
||||
assert ok
|
||||
assert props_path.exists()
|
||||
|
||||
props = strategy.read_codeflash_properties(project)
|
||||
assert props["moduleRoot"] == "src/main/java"
|
||||
|
||||
def test_maven_remove_idempotent(self, tmp_path: Path) -> None:
|
||||
project = _make_maven_project(tmp_path)
|
||||
strategy = get_config_strategy(project)
|
||||
|
||||
# Remove when nothing was written
|
||||
ok1, _ = strategy.remove_codeflash_properties(project)
|
||||
assert ok1
|
||||
|
||||
# Write then remove twice
|
||||
strategy.write_codeflash_properties(project, {"module-root": "src"})
|
||||
ok2, _ = strategy.remove_codeflash_properties(project)
|
||||
assert ok2
|
||||
ok3, _ = strategy.remove_codeflash_properties(project)
|
||||
assert ok3
|
||||
|
||||
assert strategy.read_codeflash_properties(project) == {}
|
||||
|
||||
def test_gradle_remove_idempotent(self, tmp_path: Path) -> None:
|
||||
project = _make_gradle_project(tmp_path)
|
||||
strategy = get_config_strategy(project)
|
||||
|
||||
strategy.write_codeflash_properties(project, {"module-root": "src"})
|
||||
ok1, _ = strategy.remove_codeflash_properties(project)
|
||||
assert ok1
|
||||
ok2, _ = strategy.remove_codeflash_properties(project)
|
||||
assert ok2
|
||||
|
||||
assert strategy.read_codeflash_properties(project) == {}
|
||||
Loading…
Reference in a new issue