diff --git a/README.md b/README.md index bb716cd312..6767bcd6d9 100644 --- a/README.md +++ b/README.md @@ -129,8 +129,12 @@ Available options can be queried by using: The most important options are: -* `--maxTimeInSeconds ` -Maximum number of seconds allowed for the search. +* `--maxTime ` +Maximum allowed time for the search, in the form `?h?m?s`, where it can be specified for how many hours (`h`), +minutes (`m`) and seconds (`s`) to run the search. +For example, `1h10m120s` would run the search for `72` minutes. +Each component (i.e., `h`, `m` and `s`) is optional, but at least one must be specified. +In other words, if you need to run the search for just `30` seconds, you can write `30s` instead of `0h0m30s`. **The more time is allowed, the better results one can expect**. But then of course the test generation will take longer. diff --git a/core/src/main/kotlin/org/evomaster/core/EMConfig.kt b/core/src/main/kotlin/org/evomaster/core/EMConfig.kt index 8b4ffacab5..e60cac8e9a 100644 --- a/core/src/main/kotlin/org/evomaster/core/EMConfig.kt +++ b/core/src/main/kotlin/org/evomaster/core/EMConfig.kt @@ -9,6 +9,7 @@ import org.evomaster.core.output.OutputFormat import org.evomaster.core.search.impact.GeneMutationSelectionMethod import java.net.MalformedURLException import java.net.URL +import java.util.regex.Pattern import kotlin.reflect.KMutableProperty import kotlin.reflect.jvm.javaType @@ -261,6 +262,14 @@ class EMConfig { } } } + + m.annotations.find { it is Regex }?.also { + it as Regex + if(! parameterValue.matches(kotlin.text.Regex(it.regex))){ + throw IllegalArgumentException("Parameter '${m.name}' with value $parameterValue is" + + " not matching the regex: ${it.regex}") + } + } } when(stoppingCriterion){ @@ -520,8 +529,9 @@ class EMConfig { @Cfg("Maximum number of seconds allowed for the search." + " The more time is allowed, the better results one can expect." + " But then of course the test generation will take longer." + - " Only applicable depending on the stopping criterion.") - @Min(1.0) + " Only applicable depending on the stopping criterion." + + " If this value is 0, the setting 'maxTime' will be used instead.") + @Min(0.0) var maxTimeInSeconds = defaultMaxTimeInSeconds @@ -532,9 +542,9 @@ class EMConfig { " But then of course the test generation will take longer." + " Only applicable depending on the stopping criterion." + " The time is expressed with a string where hours(h), minutes(m) and" + - " seconds(s) can be specified, e.g., '1h 10m 120s' and '72m' are both valid" + + " seconds(s) can be specified, e.g., '1h10m120s' and '72m' are both valid" + " and equivalent.") - @Regex("(?=[^ ]+)( *)(\\d+h)?( *)(\\d+m)?( *)(\\d+s)?( *)") + @Regex("( *)((?=([^ ]+))(\\d+h)?(\\d+m)?(\\d+s)?)( *)") var maxTime = defaultMaxTime @@ -897,18 +907,18 @@ class EMConfig { val s = maxTime.indexOf('s') val hours = if(h >= 0){ - maxTime.subSequence(0, h).toString().toInt() + maxTime.subSequence(0, h).toString().trim().toInt() } else 0 val minutes = if(m >=0 ){ - maxTime.subSequence( if(h>=0) h+1 else 0, m).toString().toInt() + maxTime.subSequence( if(h>=0) h+1 else 0, m).toString().trim().toInt() } else 0 val seconds = if(s >=0){ - maxTime.subSequence( if(m>=0) m+1 else (if(h>=0) h+1 else 0), s).toString().toInt() + maxTime.subSequence( if(m>=0) m+1 else (if(h>=0) h+1 else 0), s).toString().trim().toInt() } else 0 - return (h * 60 * 60) + (m * 60) + s + return (hours * 60 * 60) + (minutes * 60) + seconds } } \ No newline at end of file diff --git a/core/src/test/kotlin/org/evomaster/core/EMConfigTest.kt b/core/src/test/kotlin/org/evomaster/core/EMConfigTest.kt index 1b8b500c9c..319772e93d 100644 --- a/core/src/test/kotlin/org/evomaster/core/EMConfigTest.kt +++ b/core/src/test/kotlin/org/evomaster/core/EMConfigTest.kt @@ -164,4 +164,75 @@ internal class EMConfigTest{ options = parser.parse("--$name", noProtocol) assertThrows(Exception::class.java, {config.updateProperties(options)}) } + + + @ParameterizedTest + @ValueSource(strings = [""," ","1","42","-42s","1 42s","42s1m","1m 42s"]) + fun testTimeRegexWrong(value: String){ + + val parser = EMConfig.getOptionParser() + parser.recognizedOptions()["maxTime"] ?: throw Exception("Cannot find option") + + val config = EMConfig() + val options = parser.parse("--maxTime", value) + assertThrows(Exception::class.java, {config.updateProperties(options)}) + } + + @Test + fun testTimeRegexJustSeconds(){ + + val parser = EMConfig.getOptionParser() + parser.recognizedOptions()["maxTime"] ?: throw Exception("Cannot find option") + + val config = EMConfig() + val options = parser.parse("--maxTime", "42s") + config.updateProperties(options) + + val seconds = config.timeLimitInSeconds() + assertEquals(42, seconds) + } + + @Test + fun testTimeRegexJustMinutes(){ + + val parser = EMConfig.getOptionParser() + parser.recognizedOptions()["maxTime"] ?: throw Exception("Cannot find option") + + val config = EMConfig() + val options = parser.parse("--maxTime", "3m") + config.updateProperties(options) + + val seconds = config.timeLimitInSeconds() + assertEquals(180, seconds) + } + + @Test + fun testTimeRegexJustHours(){ + + val parser = EMConfig.getOptionParser() + parser.recognizedOptions()["maxTime"] ?: throw Exception("Cannot find option") + + val config = EMConfig() + val options = parser.parse("--maxTime", "2h") + config.updateProperties(options) + + val seconds = config.timeLimitInSeconds() + assertEquals(2 * 60 * 60, seconds) + } + + @Test + fun testTimeRegex(){ + + val parser = EMConfig.getOptionParser() + parser.recognizedOptions()["maxTime"] ?: throw Exception("Cannot find option") + + val config = EMConfig() + val options = parser.parse("--maxTime", " 1h10m120s ") + config.updateProperties(options) + + val seconds = config.timeLimitInSeconds() + assertEquals( (60 * 60) + 600 + 120, seconds) + } + + } \ No newline at end of file