From b00d23f5fee9ef46d6437f3dcf7876bff614b82d Mon Sep 17 00:00:00 2001 From: Man Zhang Date: Wed, 11 Feb 2026 23:00:25 +0800 Subject: [PATCH 01/11] add more options --- .../kotlin/org/evomaster/core/EMConfig.kt | 27 +++++++++++++++++++ docs/options.md | 4 +++ 2 files changed, 31 insertions(+) diff --git a/core/src/main/kotlin/org/evomaster/core/EMConfig.kt b/core/src/main/kotlin/org/evomaster/core/EMConfig.kt index 02392b3310..b61343d656 100644 --- a/core/src/main/kotlin/org/evomaster/core/EMConfig.kt +++ b/core/src/main/kotlin/org/evomaster/core/EMConfig.kt @@ -807,6 +807,15 @@ class EMConfig { if(dockerLocalhost && !runningInDocker){ throw ConfigProblemException("Specifying 'dockerLocalhost' only makes sense when running EvoMaster inside Docker.") } + + if(useEnvVarsForPathInTests){ + if (jdkEnvVarName.isNotEmpty()) + throw ConfigProblemException("'jdkEnvVarName' must be specified if 'useEnvVarsForPathInTests' is enabled.") + if (sutDistEnvVarName.isNotEmpty()) + throw ConfigProblemException("'sutDistEnvVarName' must be specified if 'useEnvVarsForPathInTests' is enabled.") + if (sutJarEnvVarName.isNotEmpty()) + throw ConfigProblemException("'sutJarEnvVarName' must be specified if 'useEnvVarsForPathInTests' is enabled.") + } } private fun checkPropertyConstraints(m: KMutableProperty<*>) { @@ -2636,6 +2645,24 @@ class EMConfig { "Note that flakiness is now supported only for fuzzing REST APIs") var handleFlakiness = false + @Experimental + @Cfg("Use environment variables to define the paths required by External Drivers. " + + "This is necessary when the generated tests are executed on the same machine. " + + "Note that this setting only affects the generated test cases.") + var useEnvVarsForPathInTests = false + + @Experimental + @Cfg("Specify name of the environment variable that provides the JDK path.") + var jdkEnvVarName = "" + + @Experimental + @Cfg("Specify name of the environment variable that provides the SUT dist.") + var sutDistEnvVarName = "" + + @Experimental + @Cfg("Specify name of the environment variable that provides the SUT JAR path.") + var sutJarEnvVarName = "" + @Experimental @Cfg("Specify a method to select the first external service spoof IP address.") var externalServiceIPSelectionStrategy = ExternalServiceIPSelectionStrategy.NONE diff --git a/docs/options.md b/docs/options.md index 042c0d7399..878fa4cf8e 100644 --- a/docs/options.md +++ b/docs/options.md @@ -286,6 +286,7 @@ There are 3 types of options: |`instrumentMR_NET`| __Boolean__. Execute instrumentation for method replace with category NET. Note: this applies only for languages in which instrumentation is applied at runtime, like Java/Kotlin on the JVM. *Default value*: `false`.| |`instrumentMR_OPENSEARCH`| __Boolean__. Execute instrumentation for method replace with category OPENSEARCH. Note: this applies only for languages in which instrumentation is applied at runtime, like Java/Kotlin on the JVM. *Default value*: `false`.| |`instrumentMR_REDIS`| __Boolean__. Execute instrumentation for method replace with category REDIS. Note: this applies only for languages in which instrumentation is applied at runtime, like Java/Kotlin on the JVM. *Default value*: `false`.| +|`jdkEnvVarName`| __String__. Specify name of the environment variable that provides the JDK path. *Default value*: `""`.| |`languageModelConnector`| __Boolean__. Enable language model connector. *Default value*: `false`.| |`languageModelConnectorNumberOfThreads`| __Int__. Number of threads for language model connector. No more threads than numbers of processors will be used. *Constraints*: `min=1.0`. *Default value*: `2`.| |`languageModelName`| __String__. Large-language model name as listed in Ollama. *Default value*: `llama3.2:latest`.| @@ -321,10 +322,13 @@ There are 3 types of options: |`ssrf`| __Boolean__. To apply SSRF detection as part of security testing. *Depends on*: `security=true`. *Default value*: `false`.| |`structureMutationProFS`| __Double__. Specify a probability of applying structure mutator during the focused search. *Constraints*: `probability 0.0-1.0`. *Default value*: `0.0`.| |`structureMutationProbStrategy`| __Enum__. Specify a strategy to handle a probability of applying structure mutator during the focused search. *Valid values*: `SPECIFIED, SPECIFIED_FS, DPC_TO_SPECIFIED_BEFORE_FS, DPC_TO_SPECIFIED_AFTER_FS, ADAPTIVE_WITH_IMPACT`. *Default value*: `SPECIFIED`.| +|`sutDistEnvVarName`| __String__. Specify name of the environment variable that provides the SUT dist. *Default value*: `""`.| +|`sutJarEnvVarName`| __String__. Specify name of the environment variable that provides the SUT JAR path. *Default value*: `""`.| |`taintForceSelectionOfGenesWithSpecialization`| __Boolean__. During mutation, force the mutation of genes that have newly discovered specialization from previous fitness evaluations, based on taint analysis. *Default value*: `false`.| |`targetHeuristicsFile`| __String__. Where the target heuristic values file (if any) is going to be written (in CSV format). It is only used when processFormat is TARGET_HEURISTIC. *Default value*: `targets.csv`.| |`testResourcePathToSaveMockedResponse`| __String__. Specify test resource path where to save mocked responses as separated files. *Default value*: `""`.| |`thresholdDistanceForDataPool`| __Int__. Threshold of Levenshtein Distance for key-matching in Data Pool. *Constraints*: `min=0.0`. *Default value*: `2`.| +|`useEnvVarsForPathInTests`| __Boolean__. Use environment variables to define the paths required by External Drivers. This is necessary when the generated tests are executed on the same machine. Note that this setting only affects the generated test cases. *Default value*: `false`.| |`useGlobalTaintInfoProbability`| __Double__. When sampling new individual, check whether to use already existing info on tainted values. *Constraints*: `probability 0.0-1.0`. *Default value*: `0.0`.| |`useInsertionForSqlHeuristics`| __Boolean__. Specify whether insertions should be used to calculate SQL heuristics instead of retrieving data from real databases. *Default value*: `false`.| |`useTestMethodOrder`| __Boolean__. Adds TestMethodOrder annotation for JUnit 5 tests. *Default value*: `false`.| From 6fea6443e0917ae26a57f9056929e8277008ae4c Mon Sep 17 00:00:00 2001 From: Man Zhang Date: Wed, 11 Feb 2026 23:27:50 +0800 Subject: [PATCH 02/11] add unit tests --- .../main/kotlin/org/evomaster/core/EMConfig.kt | 6 +++--- .../kotlin/org/evomaster/core/EMConfigTest.kt | 17 +++++++++++++++++ 2 files changed, 20 insertions(+), 3 deletions(-) diff --git a/core/src/main/kotlin/org/evomaster/core/EMConfig.kt b/core/src/main/kotlin/org/evomaster/core/EMConfig.kt index b61343d656..d727005e6c 100644 --- a/core/src/main/kotlin/org/evomaster/core/EMConfig.kt +++ b/core/src/main/kotlin/org/evomaster/core/EMConfig.kt @@ -809,11 +809,11 @@ class EMConfig { } if(useEnvVarsForPathInTests){ - if (jdkEnvVarName.isNotEmpty()) + if (jdkEnvVarName.isEmpty()) throw ConfigProblemException("'jdkEnvVarName' must be specified if 'useEnvVarsForPathInTests' is enabled.") - if (sutDistEnvVarName.isNotEmpty()) + if (sutDistEnvVarName.isEmpty()) throw ConfigProblemException("'sutDistEnvVarName' must be specified if 'useEnvVarsForPathInTests' is enabled.") - if (sutJarEnvVarName.isNotEmpty()) + if (sutJarEnvVarName.isEmpty()) throw ConfigProblemException("'sutJarEnvVarName' must be specified if 'useEnvVarsForPathInTests' is enabled.") } } diff --git a/core/src/test/kotlin/org/evomaster/core/EMConfigTest.kt b/core/src/test/kotlin/org/evomaster/core/EMConfigTest.kt index 75bb5dbecf..d6dc8c0d90 100644 --- a/core/src/test/kotlin/org/evomaster/core/EMConfigTest.kt +++ b/core/src/test/kotlin/org/evomaster/core/EMConfigTest.kt @@ -719,4 +719,21 @@ internal class EMConfigTest{ assertEquals(config.maxTestCaseNameLength, different) } + + @Test + fun testUseEnvVarsForPathInTests(){ + + val parser = EMConfig.getOptionParser() + parser.recognizedOptions()["useEnvVarsForPathInTests"] ?: throw Exception("Cannot find option") + + val config = EMConfig() + val optionAllEmpty = parser.parse("--useEnvVarsForPathInTests", "true") + assertThrows(Exception::class.java, {config.updateProperties(optionAllEmpty)}) + + val optionOneMissing = parser.parse("--useEnvVarsForPathInTests", "true", "--jdkEnvVarName", "JDK_HOME", "--sutDistEnvVarName", "WFC_HOME") + assertThrows(Exception::class.java, {config.updateProperties(optionOneMissing)}) + + val optionAllSpecified = parser.parse("--useEnvVarsForPathInTests", "true", "--jdkEnvVarName", "JDK_HOME", "--sutDistEnvVarName", "WFC_HOME", "--sutJarEnvVarName", "sut-jar.jar") + assertDoesNotThrow({config.updateProperties(optionAllSpecified)}) + } } From b289f88164ce843d5856baf5a0c0aba3ccc93aee Mon Sep 17 00:00:00 2001 From: Man Zhang Date: Thu, 12 Feb 2026 10:04:11 +0800 Subject: [PATCH 03/11] modify cfg --- core/src/main/kotlin/org/evomaster/core/EMConfig.kt | 7 ++++--- docs/options.md | 6 +++--- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/core/src/main/kotlin/org/evomaster/core/EMConfig.kt b/core/src/main/kotlin/org/evomaster/core/EMConfig.kt index d727005e6c..d0aec623b1 100644 --- a/core/src/main/kotlin/org/evomaster/core/EMConfig.kt +++ b/core/src/main/kotlin/org/evomaster/core/EMConfig.kt @@ -2652,15 +2652,16 @@ class EMConfig { var useEnvVarsForPathInTests = false @Experimental - @Cfg("Specify name of the environment variable that provides the JDK path.") + @Cfg("Specify name of the environment variable that provides the JDK installation path. " + + "Note that the executable path will be resolved by appending 'bin/java'.") var jdkEnvVarName = "" @Experimental - @Cfg("Specify name of the environment variable that provides the SUT dist.") + @Cfg("Specify name of the environment variable that provides the the base distribution directory of the SUT.") var sutDistEnvVarName = "" @Experimental - @Cfg("Specify name of the environment variable that provides the SUT JAR path.") + @Cfg("Specifies the name of the SUT JAR file that will be used together with `sutDistEnvVarName` to resolve the full SUT JAR path.") var sutJarEnvVarName = "" @Experimental diff --git a/docs/options.md b/docs/options.md index 878fa4cf8e..5054f72c34 100644 --- a/docs/options.md +++ b/docs/options.md @@ -286,7 +286,7 @@ There are 3 types of options: |`instrumentMR_NET`| __Boolean__. Execute instrumentation for method replace with category NET. Note: this applies only for languages in which instrumentation is applied at runtime, like Java/Kotlin on the JVM. *Default value*: `false`.| |`instrumentMR_OPENSEARCH`| __Boolean__. Execute instrumentation for method replace with category OPENSEARCH. Note: this applies only for languages in which instrumentation is applied at runtime, like Java/Kotlin on the JVM. *Default value*: `false`.| |`instrumentMR_REDIS`| __Boolean__. Execute instrumentation for method replace with category REDIS. Note: this applies only for languages in which instrumentation is applied at runtime, like Java/Kotlin on the JVM. *Default value*: `false`.| -|`jdkEnvVarName`| __String__. Specify name of the environment variable that provides the JDK path. *Default value*: `""`.| +|`jdkEnvVarName`| __String__. Specify name of the environment variable that provides the JDK installation path. Note that the executable path will be resolved by appending 'bin/java'. *Default value*: `""`.| |`languageModelConnector`| __Boolean__. Enable language model connector. *Default value*: `false`.| |`languageModelConnectorNumberOfThreads`| __Int__. Number of threads for language model connector. No more threads than numbers of processors will be used. *Constraints*: `min=1.0`. *Default value*: `2`.| |`languageModelName`| __String__. Large-language model name as listed in Ollama. *Default value*: `llama3.2:latest`.| @@ -322,8 +322,8 @@ There are 3 types of options: |`ssrf`| __Boolean__. To apply SSRF detection as part of security testing. *Depends on*: `security=true`. *Default value*: `false`.| |`structureMutationProFS`| __Double__. Specify a probability of applying structure mutator during the focused search. *Constraints*: `probability 0.0-1.0`. *Default value*: `0.0`.| |`structureMutationProbStrategy`| __Enum__. Specify a strategy to handle a probability of applying structure mutator during the focused search. *Valid values*: `SPECIFIED, SPECIFIED_FS, DPC_TO_SPECIFIED_BEFORE_FS, DPC_TO_SPECIFIED_AFTER_FS, ADAPTIVE_WITH_IMPACT`. *Default value*: `SPECIFIED`.| -|`sutDistEnvVarName`| __String__. Specify name of the environment variable that provides the SUT dist. *Default value*: `""`.| -|`sutJarEnvVarName`| __String__. Specify name of the environment variable that provides the SUT JAR path. *Default value*: `""`.| +|`sutDistEnvVarName`| __String__. Specify name of the environment variable that provides the the base distribution directory of the SUT. *Default value*: `""`.| +|`sutJarEnvVarName`| __String__. Specifies the name of the SUT JAR file that will be used together with `sutDistEnvVarName` to resolve the full SUT JAR path. *Default value*: `""`.| |`taintForceSelectionOfGenesWithSpecialization`| __Boolean__. During mutation, force the mutation of genes that have newly discovered specialization from previous fitness evaluations, based on taint analysis. *Default value*: `false`.| |`targetHeuristicsFile`| __String__. Where the target heuristic values file (if any) is going to be written (in CSV format). It is only used when processFormat is TARGET_HEURISTIC. *Default value*: `targets.csv`.| |`testResourcePathToSaveMockedResponse`| __String__. Specify test resource path where to save mocked responses as separated files. *Default value*: `""`.| From 230cbc7a11c4d3d29bc9df51a509c6bc0c40f5d5 Mon Sep 17 00:00:00 2001 From: Man Zhang Date: Thu, 12 Feb 2026 10:04:40 +0800 Subject: [PATCH 04/11] add util methods to extract env variable --- .../controller/internal/SutController.java | 65 +++++++++++++++++++ 1 file changed, 65 insertions(+) diff --git a/client-java/controller/src/main/java/org/evomaster/client/java/controller/internal/SutController.java b/client-java/controller/src/main/java/org/evomaster/client/java/controller/internal/SutController.java index 83efa59392..f8623780f9 100644 --- a/client-java/controller/src/main/java/org/evomaster/client/java/controller/internal/SutController.java +++ b/client-java/controller/src/main/java/org/evomaster/client/java/controller/internal/SutController.java @@ -63,6 +63,8 @@ import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.net.InetSocketAddress; +import java.nio.file.Path; +import java.nio.file.Paths; import java.sql.Connection; import java.sql.SQLException; import java.util.*; @@ -1953,6 +1955,69 @@ public final String readFileAsStringFromTestResource(String fileName){ .lines().collect(Collectors.joining(System.lineSeparator())); } + /** + * Resolves the absolute path to the Java executable using the given + * JDK environment variable name. + * + *

This method expects the environment variable (e.g. {@code JAVA_HOME}) + * to point to a JDK installation directory. It appends {@code "bin"} and + * {@code "java"} to construct the full path to the Java executable.

+ * + * + * @param jdkEnvVarName the name of the JDK environment variable + * (e.g. {@code "JAVA_HOME"}) + * @return the absolute path to the Java executable as a String + * @throws RuntimeException if the environment variable is not defined or empty + */ + public final String extractJDKPathWithEnvVarName(String jdkEnvVarName){ + return extractPathWithEnvVar(jdkEnvVarName, "bin", "java").toString(); + } + + /** + * Resolves the absolute path to a System-Under-Test (SUT) JAR file + * using environment variables. + * + * + * @param sutDistEnvVarName the environment variable that contains the base + * directory of the SUT distribution + * @param sutJarEnvVarName the name of the JAR file (or relative path inside the distribution) + * @return the absolute path to the SUT JAR file as a String + * @throws RuntimeException if the distribution environment variable is not defined or empty + */ + public final String extractSutJarNameWithEnvVarName(String sutDistEnvVarName, String sutJarEnvVarName){ + return extractPathWithEnvVar(sutDistEnvVarName, sutJarEnvVarName).toString(); + } + + /** + * Resolves an absolute {@link Path} using the value of a given environment variable + * as the base directory and appending additional path segments. + * + *

For example, if {@code envVarName} is {@code "JAVA_HOME"} and + * {@code others} contains {@code "bin", "java"}, this method will return:

+ * + *
+     * $JAVA_HOME/bin/java   (Linux/macOS)
+     * %JAVA_HOME%\bin\java  (Windows)
+     * 
+ * + *

The resulting path is converted to an absolute path.

+ * + * @param envVarName the name of the environment variable (e.g. {@code "JAVA_HOME"}) + * @param others additional path segments to append to the environment variable path + * @return the resolved absolute {@link Path} + * @throws RuntimeException if the environment variable is not defined or empty + */ + private Path extractPathWithEnvVar(String envVarName, String... others){ + String javaHome = System.getenv(envVarName); + + if (javaHome == null || javaHome.isEmpty()) { + throw new RuntimeException("Cannot find "+envVarName); + } + + Path javaExecutable = Paths.get(javaHome, others); + return javaExecutable.toAbsolutePath(); + } + @Override public Map getExceptionImportanceLevels() { return null; From 13f447477bf528459314dfa88cbd62462cbc88eb Mon Sep 17 00:00:00 2001 From: Man Zhang Date: Thu, 12 Feb 2026 11:19:40 +0800 Subject: [PATCH 05/11] add tests for problem --- core/src/main/kotlin/org/evomaster/core/EMConfig.kt | 3 +++ core/src/test/kotlin/org/evomaster/core/EMConfigTest.kt | 6 +++++- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/core/src/main/kotlin/org/evomaster/core/EMConfig.kt b/core/src/main/kotlin/org/evomaster/core/EMConfig.kt index d0aec623b1..f22ef1ced9 100644 --- a/core/src/main/kotlin/org/evomaster/core/EMConfig.kt +++ b/core/src/main/kotlin/org/evomaster/core/EMConfig.kt @@ -809,6 +809,9 @@ class EMConfig { } if(useEnvVarsForPathInTests){ + if(problemType != ProblemType.DEFAULT && problemType != ProblemType.REST) + throw ConfigProblemException("'useEnvVarsForPathInTests' can be applied only for REST problem.") + if (jdkEnvVarName.isEmpty()) throw ConfigProblemException("'jdkEnvVarName' must be specified if 'useEnvVarsForPathInTests' is enabled.") if (sutDistEnvVarName.isEmpty()) diff --git a/core/src/test/kotlin/org/evomaster/core/EMConfigTest.kt b/core/src/test/kotlin/org/evomaster/core/EMConfigTest.kt index d6dc8c0d90..8e5f21ea3e 100644 --- a/core/src/test/kotlin/org/evomaster/core/EMConfigTest.kt +++ b/core/src/test/kotlin/org/evomaster/core/EMConfigTest.kt @@ -734,6 +734,10 @@ internal class EMConfigTest{ assertThrows(Exception::class.java, {config.updateProperties(optionOneMissing)}) val optionAllSpecified = parser.parse("--useEnvVarsForPathInTests", "true", "--jdkEnvVarName", "JDK_HOME", "--sutDistEnvVarName", "WFC_HOME", "--sutJarEnvVarName", "sut-jar.jar") - assertDoesNotThrow({config.updateProperties(optionAllSpecified)}) + assertDoesNotThrow ({config.updateProperties(optionAllSpecified)}) + + val optionAllSpecifiedNotRest = parser.parse("--useEnvVarsForPathInTests", "true", "--jdkEnvVarName", "JDK_HOME", "--sutDistEnvVarName", "WFC_HOME", "--sutJarEnvVarName", "sut-jar.jar", "--problemType", "RPC") + assertThrows (Exception::class.java,{config.updateProperties(optionAllSpecifiedNotRest)}) + } } From f09ff83c92a517f05af068eda4f7466f66c2f04d Mon Sep 17 00:00:00 2001 From: Man Zhang Date: Thu, 12 Feb 2026 11:23:37 +0800 Subject: [PATCH 06/11] use env path in generated tests --- .../core/output/service/TestSuiteWriter.kt | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/core/src/main/kotlin/org/evomaster/core/output/service/TestSuiteWriter.kt b/core/src/main/kotlin/org/evomaster/core/output/service/TestSuiteWriter.kt index 28f698ae8a..a89d6c7b88 100644 --- a/core/src/main/kotlin/org/evomaster/core/output/service/TestSuiteWriter.kt +++ b/core/src/main/kotlin/org/evomaster/core/output/service/TestSuiteWriter.kt @@ -63,6 +63,10 @@ class TestSuiteWriter { private const val fixture = "_fixture" private const val browser = "browser" private var containsDtos = false + + private const val EXTRACT_JDK_PATH_ENV_VAR_METHOD = "extractJDKPathWithEnvVarName" + private const val EXTRACT_SUT_PATH_ENV_VAR_METHOD = "extractSutJarNameWithEnvVarName" + } @Inject @@ -609,6 +613,10 @@ class TestSuiteWriter { } private fun getJavaCommand(): String { + if (config.useEnvVarsForPathInTests){ + return ".setJavaCommand($EXTRACT_JDK_PATH_ENV_VAR_METHOD(\"${config.jdkEnvVarName}\"))" + } + if (config.javaCommand != "java") { val java = config.javaCommand.replace("\\", "\\\\") return ".setJavaCommand(\"$java\")" @@ -625,8 +633,12 @@ class TestSuiteWriter { val wireMockServers = getActiveWireMockServers() - val executable = if (controllerInput.isNullOrBlank()) "" - else "\"$controllerInput\"".replace("\\", "\\\\") + val executable = if (config.useEnvVarsForPathInTests){ + "$EXTRACT_SUT_PATH_ENV_VAR_METHOD(${config.sutDistEnvVarName}, ${config.sutJarEnvVarName})" + }else{ + if (controllerInput.isNullOrBlank()) "" + else "\"$controllerInput\"".replace("\\", "\\\\") + } if (config.outputFormat.isJava()) { if (!config.blackBox || config.bbExperiments) { From a0d46384ea6eda7686dffab6dec127a32a20416e Mon Sep 17 00:00:00 2001 From: Man Zhang Date: Thu, 12 Feb 2026 11:43:33 +0800 Subject: [PATCH 07/11] fix a bug in generated tests with env --- .../kotlin/org/evomaster/core/output/service/TestSuiteWriter.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/src/main/kotlin/org/evomaster/core/output/service/TestSuiteWriter.kt b/core/src/main/kotlin/org/evomaster/core/output/service/TestSuiteWriter.kt index a89d6c7b88..51f87e7288 100644 --- a/core/src/main/kotlin/org/evomaster/core/output/service/TestSuiteWriter.kt +++ b/core/src/main/kotlin/org/evomaster/core/output/service/TestSuiteWriter.kt @@ -634,7 +634,7 @@ class TestSuiteWriter { val wireMockServers = getActiveWireMockServers() val executable = if (config.useEnvVarsForPathInTests){ - "$EXTRACT_SUT_PATH_ENV_VAR_METHOD(${config.sutDistEnvVarName}, ${config.sutJarEnvVarName})" + "$EXTRACT_SUT_PATH_ENV_VAR_METHOD(\"${config.sutDistEnvVarName}\", \"${config.sutJarEnvVarName}\")" }else{ if (controllerInput.isNullOrBlank()) "" else "\"$controllerInput\"".replace("\\", "\\\\") From 378b3e57558ba7e15af742f786a47d7ff37870e3 Mon Sep 17 00:00:00 2001 From: Man Zhang Date: Thu, 12 Feb 2026 11:43:39 +0800 Subject: [PATCH 08/11] add unit tests --- .../output/service/TestSuiteWriterTest.kt | 53 +++++++++++++++++++ 1 file changed, 53 insertions(+) diff --git a/core/src/test/kotlin/org/evomaster/core/output/service/TestSuiteWriterTest.kt b/core/src/test/kotlin/org/evomaster/core/output/service/TestSuiteWriterTest.kt index 1ece169ef0..c6044a0066 100644 --- a/core/src/test/kotlin/org/evomaster/core/output/service/TestSuiteWriterTest.kt +++ b/core/src/test/kotlin/org/evomaster/core/output/service/TestSuiteWriterTest.kt @@ -149,6 +149,59 @@ class TestSuiteWriterTest{ assertTrue(generatedUtils == PyLoader::class.java.getResource("/${TestSuiteWriter.pythonUtilsFilename}").readText()) } + @Test + fun testEmptySuiteWithEnabledEnvVar(){ + + val injector = getInjector() + + val config = injector.getInstance(EMConfig::class.java) + config.createTests = true + config.outputFormat = OutputFormat.KOTLIN_JUNIT_5 + config.outputFolder = "$baseTargetFolder/empty_suite_use_env_var" + config.outputFilePrefix = "Foo_testEmptySuiteUseEnvVar" + config.outputFileSuffix = "" + config.useEnvVarsForPathInTests = true + config.jdkEnvVarName = "JAVA_HOME_FAKE" + config.sutDistEnvVarName = "WFC_HOME_FAKE" + config.sutJarEnvVarName = "fake-sut.jar" + + val solution = getEmptySolution(config) + + + //make sure we delete any existing folder from previous test runs + val srcFolder = File(config.outputFolder) + srcFolder.deleteRecursively() + + //this is what used by Maven and IntelliJ + val testClassFolder = File("target/test-classes") + val expectedCompiledFile = testClassFolder.toPath() + .resolve("${config.outputFilePrefix}.class") + .toFile() + expectedCompiledFile.delete() + assertFalse(expectedCompiledFile.exists()) + + + val writer = injector.getInstance(TestSuiteWriter::class.java) + + + //write the test suite + writer.writeTests(solution, FakeController::class.qualifiedName!!, null) + + val generatedTest = Paths.get("${config.outputFolder}/${config.outputFilePrefix}.kt") + assertTrue(Files.exists(generatedTest)) + + /* + here, we only check the generated in text + as it requires to use method in SutHandler + */ + val testContent = String(Files.readAllBytes(generatedTest)) + val expectedJdkPath = "org.evomaster.core.output.service.FakeController(extractSutJarNameWithEnvVarName(\"${config.sutDistEnvVarName}\", \"${config.sutJarEnvVarName}\"))" + val expectedSutPath = ".setJavaCommand(extractJDKPathWithEnvVarName(\"${config.jdkEnvVarName}\"))" + + assertTrue(testContent.contains(expectedJdkPath)) + assertTrue(testContent.contains(expectedSutPath)) + } + private fun getEmptySolution(config: EMConfig): Solution { return Solution( mutableListOf(), From 8357aa3db6a180aa40af2978bd25f5f368484c11 Mon Sep 17 00:00:00 2001 From: Man Zhang Date: Thu, 12 Feb 2026 15:07:57 +0800 Subject: [PATCH 09/11] move the methods to EMTestUtils --- .../controller/internal/SutController.java | 62 ------------------ .../org/evomaster/test/utils/EMTestUtils.java | 65 +++++++++++++++++++ .../kotlin/org/evomaster/core/EMConfig.kt | 3 +- docs/options.md | 2 +- 4 files changed, 68 insertions(+), 64 deletions(-) diff --git a/client-java/controller/src/main/java/org/evomaster/client/java/controller/internal/SutController.java b/client-java/controller/src/main/java/org/evomaster/client/java/controller/internal/SutController.java index f8623780f9..bc16b30cd5 100644 --- a/client-java/controller/src/main/java/org/evomaster/client/java/controller/internal/SutController.java +++ b/client-java/controller/src/main/java/org/evomaster/client/java/controller/internal/SutController.java @@ -1955,68 +1955,6 @@ public final String readFileAsStringFromTestResource(String fileName){ .lines().collect(Collectors.joining(System.lineSeparator())); } - /** - * Resolves the absolute path to the Java executable using the given - * JDK environment variable name. - * - *

This method expects the environment variable (e.g. {@code JAVA_HOME}) - * to point to a JDK installation directory. It appends {@code "bin"} and - * {@code "java"} to construct the full path to the Java executable.

- * - * - * @param jdkEnvVarName the name of the JDK environment variable - * (e.g. {@code "JAVA_HOME"}) - * @return the absolute path to the Java executable as a String - * @throws RuntimeException if the environment variable is not defined or empty - */ - public final String extractJDKPathWithEnvVarName(String jdkEnvVarName){ - return extractPathWithEnvVar(jdkEnvVarName, "bin", "java").toString(); - } - - /** - * Resolves the absolute path to a System-Under-Test (SUT) JAR file - * using environment variables. - * - * - * @param sutDistEnvVarName the environment variable that contains the base - * directory of the SUT distribution - * @param sutJarEnvVarName the name of the JAR file (or relative path inside the distribution) - * @return the absolute path to the SUT JAR file as a String - * @throws RuntimeException if the distribution environment variable is not defined or empty - */ - public final String extractSutJarNameWithEnvVarName(String sutDistEnvVarName, String sutJarEnvVarName){ - return extractPathWithEnvVar(sutDistEnvVarName, sutJarEnvVarName).toString(); - } - - /** - * Resolves an absolute {@link Path} using the value of a given environment variable - * as the base directory and appending additional path segments. - * - *

For example, if {@code envVarName} is {@code "JAVA_HOME"} and - * {@code others} contains {@code "bin", "java"}, this method will return:

- * - *
-     * $JAVA_HOME/bin/java   (Linux/macOS)
-     * %JAVA_HOME%\bin\java  (Windows)
-     * 
- * - *

The resulting path is converted to an absolute path.

- * - * @param envVarName the name of the environment variable (e.g. {@code "JAVA_HOME"}) - * @param others additional path segments to append to the environment variable path - * @return the resolved absolute {@link Path} - * @throws RuntimeException if the environment variable is not defined or empty - */ - private Path extractPathWithEnvVar(String envVarName, String... others){ - String javaHome = System.getenv(envVarName); - - if (javaHome == null || javaHome.isEmpty()) { - throw new RuntimeException("Cannot find "+envVarName); - } - - Path javaExecutable = Paths.get(javaHome, others); - return javaExecutable.toAbsolutePath(); - } @Override public Map getExceptionImportanceLevels() { diff --git a/client-java/test-utils-java/src/main/java/org/evomaster/test/utils/EMTestUtils.java b/client-java/test-utils-java/src/main/java/org/evomaster/test/utils/EMTestUtils.java index e3c238ac15..4c1cb8b9d5 100644 --- a/client-java/test-utils-java/src/main/java/org/evomaster/test/utils/EMTestUtils.java +++ b/client-java/test-utils-java/src/main/java/org/evomaster/test/utils/EMTestUtils.java @@ -13,6 +13,8 @@ is used in the EvoMaster Core (eg, when making HTTP calls) and */ import java.net.URI; +import java.nio.file.Path; +import java.nio.file.Paths; /** * Class containing utility functions that can be used in the @@ -137,4 +139,67 @@ public static boolean isValidURIorEmpty(String uri){ return false; } } + + /** + * Resolves the absolute path to the Java executable using the given + * JDK environment variable name. + * + *

This method expects the environment variable (e.g. {@code JAVA_HOME}) + * to point to a JDK installation directory. It appends {@code "bin"} and + * {@code "java"} to construct the full path to the Java executable.

+ * + * + * @param jdkEnvVarName the name of the JDK environment variable + * (e.g. {@code "JAVA_HOME"}) + * @return the absolute path to the Java executable as a String + * @throws RuntimeException if the environment variable is not defined or empty + */ + public static String extractJDKPathWithEnvVarName(String jdkEnvVarName){ + return extractPathWithEnvVar(jdkEnvVarName, "bin", "java").toString(); + } + + /** + * Resolves the absolute path to a System-Under-Test (SUT) JAR file + * using environment variables. + * + * + * @param sutDistEnvVarName the environment variable that contains the base + * directory of the SUT distribution + * @param sutJarEnvVarName the name of the JAR file (or relative path inside the distribution) + * @return the absolute path to the SUT JAR file as a String + * @throws RuntimeException if the distribution environment variable is not defined or empty + */ + public static String extractSutJarNameWithEnvVarName(String sutDistEnvVarName, String sutJarEnvVarName){ + return extractPathWithEnvVar(sutDistEnvVarName, sutJarEnvVarName).toString(); + } + + /** + * Resolves an absolute {@link Path} using the value of a given environment variable + * as the base directory and appending additional path segments. + * + *

For example, if {@code envVarName} is {@code "JAVA_HOME"} and + * {@code others} contains {@code "bin", "java"}, this method will return:

+ * + *
+     * $JAVA_HOME/bin/java   (Linux/macOS)
+     * %JAVA_HOME%\bin\java  (Windows)
+     * 
+ * + *

The resulting path is converted to an absolute path.

+ * + * @param envVarName the name of the environment variable (e.g. {@code "JAVA_HOME"}) + * @param others additional path segments to append to the environment variable path + * @return the resolved absolute {@link Path} + * @throws RuntimeException if the environment variable is not defined or empty + */ + private static Path extractPathWithEnvVar(String envVarName, String... others){ + String javaHome = System.getenv(envVarName); + + if (javaHome == null || javaHome.isEmpty()) { + throw new RuntimeException("Cannot find "+envVarName); + } + + Path javaExecutable = Paths.get(javaHome, others); + return javaExecutable.toAbsolutePath(); + } } diff --git a/core/src/main/kotlin/org/evomaster/core/EMConfig.kt b/core/src/main/kotlin/org/evomaster/core/EMConfig.kt index f22ef1ced9..d7febbeea1 100644 --- a/core/src/main/kotlin/org/evomaster/core/EMConfig.kt +++ b/core/src/main/kotlin/org/evomaster/core/EMConfig.kt @@ -2660,7 +2660,8 @@ class EMConfig { var jdkEnvVarName = "" @Experimental - @Cfg("Specify name of the environment variable that provides the the base distribution directory of the SUT.") + @Cfg("Specify name of the environment variable that provides the the base distribution directory of the " + + "SUT, e.g., 'dist' directory of WFD.") var sutDistEnvVarName = "" @Experimental diff --git a/docs/options.md b/docs/options.md index 5054f72c34..10b323b665 100644 --- a/docs/options.md +++ b/docs/options.md @@ -322,7 +322,7 @@ There are 3 types of options: |`ssrf`| __Boolean__. To apply SSRF detection as part of security testing. *Depends on*: `security=true`. *Default value*: `false`.| |`structureMutationProFS`| __Double__. Specify a probability of applying structure mutator during the focused search. *Constraints*: `probability 0.0-1.0`. *Default value*: `0.0`.| |`structureMutationProbStrategy`| __Enum__. Specify a strategy to handle a probability of applying structure mutator during the focused search. *Valid values*: `SPECIFIED, SPECIFIED_FS, DPC_TO_SPECIFIED_BEFORE_FS, DPC_TO_SPECIFIED_AFTER_FS, ADAPTIVE_WITH_IMPACT`. *Default value*: `SPECIFIED`.| -|`sutDistEnvVarName`| __String__. Specify name of the environment variable that provides the the base distribution directory of the SUT. *Default value*: `""`.| +|`sutDistEnvVarName`| __String__. Specify name of the environment variable that provides the the base distribution directory of the SUT, e.g., 'dist' directory of WFD. *Default value*: `""`.| |`sutJarEnvVarName`| __String__. Specifies the name of the SUT JAR file that will be used together with `sutDistEnvVarName` to resolve the full SUT JAR path. *Default value*: `""`.| |`taintForceSelectionOfGenesWithSpecialization`| __Boolean__. During mutation, force the mutation of genes that have newly discovered specialization from previous fitness evaluations, based on taint analysis. *Default value*: `false`.| |`targetHeuristicsFile`| __String__. Where the target heuristic values file (if any) is going to be written (in CSV format). It is only used when processFormat is TARGET_HEURISTIC. *Default value*: `targets.csv`.| From 2cbc046880b095b8268af4e7245b310b60c0ff0b Mon Sep 17 00:00:00 2001 From: Man Zhang Date: Thu, 12 Feb 2026 23:36:41 +0800 Subject: [PATCH 10/11] fix misconfigured cfg --- core/src/main/kotlin/org/evomaster/core/EMConfig.kt | 2 +- docs/options.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/core/src/main/kotlin/org/evomaster/core/EMConfig.kt b/core/src/main/kotlin/org/evomaster/core/EMConfig.kt index d7febbeea1..c8b37c817d 100644 --- a/core/src/main/kotlin/org/evomaster/core/EMConfig.kt +++ b/core/src/main/kotlin/org/evomaster/core/EMConfig.kt @@ -2650,7 +2650,7 @@ class EMConfig { @Experimental @Cfg("Use environment variables to define the paths required by External Drivers. " + - "This is necessary when the generated tests are executed on the same machine. " + + "This is necessary when the generated tests are executed on the different machine. " + "Note that this setting only affects the generated test cases.") var useEnvVarsForPathInTests = false diff --git a/docs/options.md b/docs/options.md index 10b323b665..f3af6f0b39 100644 --- a/docs/options.md +++ b/docs/options.md @@ -328,7 +328,7 @@ There are 3 types of options: |`targetHeuristicsFile`| __String__. Where the target heuristic values file (if any) is going to be written (in CSV format). It is only used when processFormat is TARGET_HEURISTIC. *Default value*: `targets.csv`.| |`testResourcePathToSaveMockedResponse`| __String__. Specify test resource path where to save mocked responses as separated files. *Default value*: `""`.| |`thresholdDistanceForDataPool`| __Int__. Threshold of Levenshtein Distance for key-matching in Data Pool. *Constraints*: `min=0.0`. *Default value*: `2`.| -|`useEnvVarsForPathInTests`| __Boolean__. Use environment variables to define the paths required by External Drivers. This is necessary when the generated tests are executed on the same machine. Note that this setting only affects the generated test cases. *Default value*: `false`.| +|`useEnvVarsForPathInTests`| __Boolean__. Use environment variables to define the paths required by External Drivers. This is necessary when the generated tests are executed on the different machine. Note that this setting only affects the generated test cases. *Default value*: `false`.| |`useGlobalTaintInfoProbability`| __Double__. When sampling new individual, check whether to use already existing info on tainted values. *Constraints*: `probability 0.0-1.0`. *Default value*: `0.0`.| |`useInsertionForSqlHeuristics`| __Boolean__. Specify whether insertions should be used to calculate SQL heuristics instead of retrieving data from real databases. *Default value*: `false`.| |`useTestMethodOrder`| __Boolean__. Adds TestMethodOrder annotation for JUnit 5 tests. *Default value*: `false`.| From 18679204e94e5a8dd217df890e37894c06c86d62 Mon Sep 17 00:00:00 2001 From: Man Zhang Date: Thu, 12 Feb 2026 23:38:04 +0800 Subject: [PATCH 11/11] fix --- .../client/java/controller/internal/SutController.java | 2 -- .../src/main/java/org/evomaster/test/utils/EMTestUtils.java | 2 +- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/client-java/controller/src/main/java/org/evomaster/client/java/controller/internal/SutController.java b/client-java/controller/src/main/java/org/evomaster/client/java/controller/internal/SutController.java index bc16b30cd5..bbbde17774 100644 --- a/client-java/controller/src/main/java/org/evomaster/client/java/controller/internal/SutController.java +++ b/client-java/controller/src/main/java/org/evomaster/client/java/controller/internal/SutController.java @@ -63,8 +63,6 @@ import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.net.InetSocketAddress; -import java.nio.file.Path; -import java.nio.file.Paths; import java.sql.Connection; import java.sql.SQLException; import java.util.*; diff --git a/client-java/test-utils-java/src/main/java/org/evomaster/test/utils/EMTestUtils.java b/client-java/test-utils-java/src/main/java/org/evomaster/test/utils/EMTestUtils.java index 4c1cb8b9d5..e106cbecce 100644 --- a/client-java/test-utils-java/src/main/java/org/evomaster/test/utils/EMTestUtils.java +++ b/client-java/test-utils-java/src/main/java/org/evomaster/test/utils/EMTestUtils.java @@ -196,7 +196,7 @@ private static Path extractPathWithEnvVar(String envVarName, String... others){ String javaHome = System.getenv(envVarName); if (javaHome == null || javaHome.isEmpty()) { - throw new RuntimeException("Cannot find "+envVarName); + throw new IllegalArgumentException("Environment variable does not seem to be defined: " + envVarName); } Path javaExecutable = Paths.get(javaHome, others);