Skip to content

Commit

Permalink
chore: Rework Simulations scan
Browse files Browse the repository at this point in the history
Motivation:

The existing file based filter is bad, in particular as it requires naming the Simulation classes as `XXXSimulation`.

Modification:

* use standard scan mechanism
* don't use filters for the user defined simulation class name (gatlingRun-FQCN)
* replace file path filters matching source files with ant based filters on class names
* throw an IllegalArgumentException when no Simulation class is found
* throw an IllegalArgumentException when the user defined Simulation class is not found
* throw an IllegalArgumentException when no Simulation classes remain to be run after the filters
  • Loading branch information
slandelle committed Feb 14, 2024
1 parent 4376e83 commit b3f76be
Show file tree
Hide file tree
Showing 10 changed files with 54 additions and 226 deletions.
2 changes: 1 addition & 1 deletion build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ repositories {

dependencies {
implementation "io.gatling:gatling-enterprise-plugin-commons:1.9.0-M8"
implementation "org.apache.ant:ant:1.10.11"
implementation "org.codehaus.plexus:plexus-utils:4.0.0"
constraints {
implementation('com.fasterxml.jackson.core:jackson-databind') {
version {
Expand Down
8 changes: 1 addition & 7 deletions src/main/groovy/io/gatling/gradle/GatlingPlugin.groovy
Original file line number Diff line number Diff line change
Expand Up @@ -98,13 +98,7 @@ final class GatlingPlugin implements Plugin<Project> {
group = "Gatling"

if (simulationFQN) {
simulations = {
include(
"${simulationFQN.replace('.', '/')}.java",
"${simulationFQN.replace('.', '/')}.scala",
"${simulationFQN.replace('.', '/')}.kt"
)
}
simulationClassName = simulationFQN
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -303,8 +303,6 @@ class GatlingPluginExtension {

static final String SCALA_VERSION = '2.13.12'

static final Closure DEFAULT_SIMULATIONS = { include("**/*Simulation*.java", "**/*Simulation*.kt", "**/*Simulation*.scala") }

static final String DEFAULT_LOG_LEVEL = "WARN"
static final LogHttp DEFAULT_LOG_HTTP = LogHttp.NONE

Expand All @@ -327,7 +325,8 @@ class GatlingPluginExtension {
gatlingVersion = toolVersion
}

Closure simulations = DEFAULT_SIMULATIONS
List<String> includes = null
List<String> excludes = null

Boolean includeMainOutput = true
Boolean includeTestOutput = true
Expand Down
10 changes: 4 additions & 6 deletions src/main/groovy/io/gatling/gradle/GatlingRunTask.groovy
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ class GatlingRunTask extends DefaultTask {
Map environment = [:]

@Internal
Closure simulations
String simulationClassName

@OutputDirectory
File gatlingReportDir = project.file("${project.reportsDir}/gatling")
Expand All @@ -52,7 +52,9 @@ class GatlingRunTask extends DefaultTask {
void gatlingRun() {
def gatlingExt = project.extensions.getByType(GatlingPluginExtension)

Map<String, ExecResult> results = simulationFilesToFQN().collectEntries { String simulationClass ->
def simulationClasses = simulationClassName? List.of(simulationClassName) : SimulationFilesUtils.resolveSimulations(project, simulationClassName, project.gatling.includes, project.gatling.excludes)

Map<String, ExecResult> results = simulationClasses.collectEntries { String simulationClass ->
[(simulationClass): project.javaexec({ JavaExecSpec exec ->
exec.mainClass.set(GatlingPluginExtension.GATLING_MAIN_CLASS)
exec.classpath = project.configurations.gatlingRuntimeClasspath
Expand Down Expand Up @@ -83,8 +85,4 @@ class GatlingRunTask extends DefaultTask {
throw new TaskExecutionException(this, new RuntimeException("There're failed simulations: ${failed.keySet().sort().join(", ")}"))
}
}

Iterable<String> simulationFilesToFQN() {
return SimulationFilesUtils.resolveSimulations(project, this.simulations)
}
}
84 changes: 42 additions & 42 deletions src/main/groovy/io/gatling/gradle/SimulationFilesUtils.groovy
Original file line number Diff line number Diff line change
@@ -1,64 +1,64 @@
package io.gatling.gradle

import io.gatling.scanner.SimulationScanner
import org.codehaus.plexus.util.SelectorUtils
import org.gradle.api.Project
import org.gradle.api.file.FileTree
import org.gradle.api.tasks.InputFiles

import java.nio.file.Path
import java.nio.file.Paths
import java.util.stream.Collectors

final class SimulationFilesUtils {

static Iterable<String> resolveSimulations(Project project, Closure simulations) {
def javaSrcDirs = project.sourceSets.gatling.java.srcDirs.collect { Paths.get(it.absolutePath) }
def javaFiles = getJavaSimulationSources(project, simulations).collect { Paths.get(it.absolutePath) }
static List<String> resolveSimulations(Project project, String simulationClassName, List<String> includes, List<String> excludes) {
def dependencies = new ArrayList<File>()
def classDirectories = new ArrayList<File>()

def javaFQNs = javaFiles.collect { Path srcFile ->
javaSrcDirs.find { srcFile.startsWith(it) }.relativize(srcFile).join(".") - ".java"
project.configurations.gatlingRuntimeClasspath.getFiles().forEach {file ->
if (file.isDirectory()) {
classDirectories.add(file)
} else if (file.isFile()) {
dependencies.add(file)
}
}

List<String> kotlinFQNs
if (project.sourceSets.gatling.hasProperty("kotlin")) {
def kotlinSrcDirs = project.sourceSets.gatling.kotlin.srcDirs.collect { Paths.get(it.absolutePath) }
def kotlinFiles = getKotlinSimulationSources(project, simulations).collect { Paths.get(it.absolutePath) }
def allSimulationClasses = SimulationScanner.scan(dependencies, classDirectories).simulationClasses

kotlinFQNs = kotlinFiles.collect { Path srcFile ->
kotlinSrcDirs.find { srcFile.startsWith(it) }.relativize(srcFile).join(".") - ".kt"
}
} else {
kotlinFQNs = []
if (allSimulationClasses.isEmpty()) {
throw new IllegalArgumentException("Can't find any Simulation class")
}

def scalaSrcDirs = project.sourceSets.gatling.scala.srcDirs.collect { Paths.get(it.absolutePath) }
def scalaFiles = getScalaSimulationSources(project, simulations).collect { Paths.get(it.absolutePath) }

def scalaFQNs = scalaFiles.collect { Path srcFile ->
scalaSrcDirs.find { srcFile.startsWith(it) }.relativize(srcFile).join(".") - ".scala"
if (simulationClassName != null) {
if (allSimulationClasses.contains(simulationClassName)) {
return List.of(simulationClassName)
} else {
throw new IllegalArgumentException("Can't find Simulation class " + simulationClassName)
}
}

return javaFQNs + kotlinFQNs + scalaFQNs
}
List<String> includesList = includes? includes : List.of()
List<String> excludesList = excludes? excludes : List.of()

@InputFiles
static FileTree getJavaSimulationSources(Project project, Closure simulations) {
def simulationFilter = simulations ?: project.gatling.simulations
return project.sourceSets.gatling.java.matching(simulationFilter)
}
List<String> filteredSimulationClasses = allSimulationClasses.stream()
.filter(
className -> {
boolean isIncluded = includesList.isEmpty() || match(includesList, className)
boolean isExcluded = !excludesList.isEmpty() && match(excludesList, className)
return isIncluded && !isExcluded
})
.collect(Collectors.toList())

@InputFiles
static FileTree getKotlinSimulationSources(Project project, Closure simulations) {
if (project.sourceSets.gatling.hasProperty("kotlin")) {
def simulationFilter = simulations ?: project.gatling.simulations
return project.sourceSets.gatling.kotlin.matching(simulationFilter)
} else {
return project.files().asFileTree
if (filteredSimulationClasses.isEmpty()) {
throw new IllegalArgumentException("Can't find any Simulation class matching filters includes=" + includes + " excludes=" + excludes)
}
}

@InputFiles
static FileTree getScalaSimulationSources(Project project, Closure simulations) {
def simulationFilter = simulations ?: project.gatling.simulations
return project.sourceSets.gatling.scala.matching(simulationFilter)
return filteredSimulationClasses
}

private static boolean match(List<String> patterns, String string) {
for (String pattern : patterns) {
if (pattern != null && SelectorUtils.match(pattern, string)) {
return true
}
}
return false
}
}
15 changes: 3 additions & 12 deletions src/test/groovy/func/WhenRunFailingSimulationSpec.groovy
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,7 @@ class WhenRunFailingSimulationSpec extends GatlingFuncSpec {
given:
buildFile << """
gatling {
simulations = {
include 'computerdatabase/AFailedSimulation.scala'
include 'computerdatabase/BasicSimulation.scala'
}
includes = ['computerdatabase.AFailedSimulation', 'computerdatabase.BasicSimulation']
}
"""
and: "add incorrect simulation"
Expand Down Expand Up @@ -58,10 +55,7 @@ class AFailedSimulation extends Simulation {
given:
buildFile << """
gatling {
simulations = {
include 'computerdatabase/AFailedSimulation.scala'
include 'computerdatabase/BasicSimulation.scala'
}
includes = ['computerdatabase.AFailedSimulation', 'computerdatabase.BasicSimulation']
}
"""
and: "add incorrect simulation"
Expand Down Expand Up @@ -92,10 +86,7 @@ class AFailedSimulation extends Simulation {
given:
buildFile << """
gatling {
simulations = {
include 'computerdatabase/BasicSimulation.scala'
include 'computerdatabase/AFailedSimulation.scala'
}
includes = ['computerdatabase.BasicSimulation', 'computerdatabase.AFailedSimulation']
}
"""
and: "add incorrect simulation"
Expand Down
2 changes: 1 addition & 1 deletion src/test/groovy/func/WhenRunSimulationSpec.groovy
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,7 @@ gatling.charting.noReports = true
given:
prepareTest()
buildFile << """
gatling { simulations = { include 'computerdatabase/BasicSimulation.scala' } }
gatling { includes = ['computerdatabase.BasicSimulation'] }
"""
when: '1st time'
BuildResult result = executeGradle("$GATLING_RUN_TASK_NAME")
Expand Down
1 change: 0 additions & 1 deletion src/test/groovy/helper/GatlingUnitSpec.groovy
Original file line number Diff line number Diff line change
Expand Up @@ -18,5 +18,4 @@ abstract class GatlingUnitSpec extends GatlingSpec {

gatlingExt = project.extensions.getByType(GatlingPluginExtension)
}

}
2 changes: 0 additions & 2 deletions src/test/groovy/unit/GatlingPluginTest.groovy
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,6 @@ class GatlingPluginTest extends GatlingUnitSpec {
expect:
with(gatlingExt) {
it instanceof GatlingPluginExtension
it.simulations == DEFAULT_SIMULATIONS
it.jvmArgs == GatlingConstants.DEFAULT_JVM_OPTIONS_GATLING
it.systemProperties == DEFAULT_SYSTEM_PROPS
}
Expand Down Expand Up @@ -88,7 +87,6 @@ class GatlingPluginTest extends GatlingUnitSpec {
expect:
with(project.tasks.getByName(GATLING_RUN_TASK_NAME)) {
it instanceof GatlingRunTask
it.simulations == null
it.jvmArgs == null
it.systemProperties == null
it.dependsOn.size() == 2
Expand Down
151 changes: 0 additions & 151 deletions src/test/groovy/unit/GatlingRunTaskTest.groovy

This file was deleted.

0 comments on commit b3f76be

Please sign in to comment.