refactor: migrate :src:dist-check Groovy tests to Kotlin
diff --git a/src/core/src/main/java/org/apache/jmeter/gui/action/HtmlReportGenerator.java b/src/core/src/main/java/org/apache/jmeter/gui/action/HtmlReportGenerator.java
index 781aadf..7b21789 100644
--- a/src/core/src/main/java/org/apache/jmeter/gui/action/HtmlReportGenerator.java
+++ b/src/core/src/main/java/org/apache/jmeter/gui/action/HtmlReportGenerator.java
@@ -28,6 +28,7 @@
 
 import org.apache.jmeter.util.JMeterUtils;
 import org.apache.jorphan.exec.SystemCommand;
+import org.jetbrains.annotations.VisibleForTesting;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -136,7 +137,8 @@
      *
      * @return whether or not the files are correct
      */
-    private List<String> checkArguments() {
+    @VisibleForTesting
+    List<String> checkArguments() {
         List<String> errors = new ArrayList<>();
 
         String csvError = checkFile(new File(csvFilePath));
diff --git a/src/core/src/main/java/org/apache/jmeter/gui/util/MenuFactory.java b/src/core/src/main/java/org/apache/jmeter/gui/util/MenuFactory.java
index 5c90832..92d825f 100644
--- a/src/core/src/main/java/org/apache/jmeter/gui/util/MenuFactory.java
+++ b/src/core/src/main/java/org/apache/jmeter/gui/util/MenuFactory.java
@@ -61,6 +61,7 @@
 import org.apache.jmeter.visualizers.Printable;
 import org.apache.jorphan.gui.GuiUtils;
 import org.apache.jorphan.reflect.ClassFinder;
+import org.jetbrains.annotations.VisibleForTesting;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -115,6 +116,11 @@
         }
     }
 
+    @VisibleForTesting
+    static Map<String, List<MenuInfo>> getMenuMap() {
+        return menuMap;
+    }
+
     private static Set<String> classesToSkip() {
         return Arrays.stream(JMeterUtils.getPropDefault("not_in_menu", "").split(","))
                 .map(String::trim)
@@ -378,7 +384,8 @@
         return pop;
     }
 
-    private static JMenu createDefaultAddMenu() {
+    @VisibleForTesting
+    static JMenu createDefaultAddMenu() {
         String addAction = ActionNames.ADD;
         JMenu addMenu = new JMenu(JMeterUtils.getResString("add")); // $NON-NLS-1$
         addDefaultAddMenuToMenu(addMenu, addAction);
diff --git a/src/dist-check/src/test/kotlin/org/apache/jmeter/engine/util/PackageTest.kt b/src/dist-check/src/test/kotlin/org/apache/jmeter/engine/util/PackageTest.kt
index 130ba43..043719d 100644
--- a/src/dist-check/src/test/kotlin/org/apache/jmeter/engine/util/PackageTest.kt
+++ b/src/dist-check/src/test/kotlin/org/apache/jmeter/engine/util/PackageTest.kt
@@ -18,121 +18,138 @@
 package org.apache.jmeter.engine.util
 
 import org.apache.jmeter.samplers.SampleResult
-import org.apache.jmeter.testelement.property.JMeterProperty
+import org.apache.jmeter.testelement.property.FunctionProperty
 import org.apache.jmeter.testelement.property.StringProperty
+import org.apache.jmeter.threads.JMeterContext
 import org.apache.jmeter.threads.JMeterContextService
 import org.apache.jmeter.threads.JMeterVariables
-
-import spock.lang.Specification
-import spock.lang.Unroll
+import org.junit.jupiter.api.Assertions.assertEquals
+import org.junit.jupiter.api.BeforeEach
+import org.junit.jupiter.api.Test
+import org.junit.jupiter.api.fail
+import org.junit.jupiter.params.ParameterizedTest
+import org.junit.jupiter.params.provider.MethodSource
 
 /**
  * To run this test stand-alone, ensure that ApacheJMeter_functions.jar is on the classpath,
  * as it is needed to resolve the functions.
  */
-class PackageSpec extends Specification {
+class PackageTest {
 
-    def transformer
-    def jmctx
+    lateinit var transformer: ReplaceStringWithFunctions
+    lateinit var jmctx: JMeterContext
 
-    def setup() {
-        def variables = ["my_regex": ".*",
-                         "server"  : "jakarta.apache.org"]
-        transformer = new ReplaceStringWithFunctions(new CompoundVariable(), variables)
-        jmctx = JMeterContextService.getContext()
-        jmctx.setVariables(new JMeterVariables())
-        jmctx.setSamplingStarted(true)
-        def result = new SampleResult()
-        result.setResponseData('<a>hello world</a> costs: $3.47,$5.67', null)
-        jmctx.setPreviousResult(result)
-        jmctx.getVariables().put("server", "jakarta.apache.org")
-        jmctx.getVariables().put("my_regex", ".*")
-    }
+    data class TransformCase(
+        val propertyValue: String,
+        val stringValue: String,
+        val type: Class<*> = StringProperty::class.java,
+    )
 
-    def testFunctionParse1() {
-        given:
-            StringProperty prop = new StringProperty("date",
-                    '${__javaScript((new Date().getDate() / 100).toString()' +
-                            '.substr(${__javaScript(1+1,d\\,ay)}\\,2),heute)}')
-        when:
-            JMeterProperty newProp = transformer.transformValue(prop)
-            newProp.setRunningVersion(true)
-        then:
-            newProp.getClass().getName() == "org.apache.jmeter.testelement.property.FunctionProperty"
-            newProp.recoverRunningVersion(null)
-            Integer.parseInt(newProp.getStringValue()) >= 0
-            jmctx.getVariables().getObject("d,ay") == "2"
-    }
-
-    @Unroll
-    def "test parsing StringProperty '#propertyValue' == '#stringValue'"() {
-        given:
-            StringProperty prop = new StringProperty("a", propertyValue)
-        when:
-            JMeterProperty newProp = transformer.transformValue(prop)
-            newProp.setRunningVersion(true)
-        then:
-            newProp.getClass().getName() == 'org.apache.jmeter.testelement.property.StringProperty'
-            newProp.getStringValue() == stringValue
-        where:
-            propertyValue    | stringValue
-            ""               | ""
-            "just some text" | "just some text"
-    }
-
-    @Unroll
-    def "test parsing FunctionProperty '#propertyValue' == '#stringValue'"() {
-        given:
-            StringProperty prop = new StringProperty("a", propertyValue)
-        when:
-            JMeterProperty newProp = transformer.transformValue(prop)
-            newProp.setRunningVersion(true)
-        then:
-            newProp.getClass().getName() == 'org.apache.jmeter.testelement.property.FunctionProperty'
-            newProp.getStringValue() == stringValue
-        where:
-            propertyValue                                                                | stringValue
-            '${__regexFunction(<a>(.*)</a>,$1$)}'                                        | "hello world"
-            'It should say:\\${${__regexFunction(<a>(.+o)(.*)</a>,$1$$2$)}}'             | 'It should say:${hello world}'
-            '${non - existing; function}'                                                | '${non - existing; function}'
-            '${server}'                                                                  | "jakarta.apache.org"
-            '${__regexFunction(<([a-z]*)>,$1$)}'                                         | "a"
-            '${__regexFunction((\\\\$\\d+\\.\\d+),$1$)}'                                 | '$3.47'
-            '${__regexFunction(([$]\\d+\\.\\d+),$1$)}'                                   | '$3.47'
-            '${__regexFunction((\\\\\\$\\d+\\.\\d+\\,\\\\$\\d+\\.\\d+),$1$)}'            | '$3.47,$5.67'
-
+    companion object {
+        @JvmStatic
+        fun transformCases() = listOf(
+            TransformCase("", ""),
+            TransformCase("just some text", "just some text"),
+            TransformCase("\${__regexFunction(<a>(.*)</a>,$1$)}", "hello world", type = FunctionProperty::class.java),
+            TransformCase(
+                "It should say:\\\${\${__regexFunction(<a>(.+o)(.*)</a>,$1$$2$)}}",
+                "It should say:\${hello world}",
+                type = FunctionProperty::class.java,
+            ),
+            TransformCase("\${non - existing; function}", "\${non - existing; function}", type = FunctionProperty::class.java),
+            TransformCase("\${server}", "jakarta.apache.org", type = FunctionProperty::class.java),
+            TransformCase("\${__regexFunction(<([a-z]*)>,$1$)}", "a", type = FunctionProperty::class.java),
+            TransformCase("\${__regexFunction((\\\\$\\d+\\.\\d+),$1$)}", "$3.47", type = FunctionProperty::class.java),
+            TransformCase("\${__regexFunction(([$]\\d+\\.\\d+),$1$)}", "$3.47", type = FunctionProperty::class.java),
+            TransformCase("\${__regexFunction((\\\\\\$\\d+\\.\\d+\\,\\\\$\\d+\\.\\d+),$1$)}", "$3.47,$5.67", type = FunctionProperty::class.java),
             // Nested examples
-            '${__regexFunction(<a>(${my_regex})</a>,$1$)}'                               | "hello world"
-            '${__regexFunction(<a>(${my_regex})</a>,$1$)}${__regexFunction(<a>(.),$1$)}' | "hello worldh"
-    }
-
-    @Unroll
-    def "Backslashes are removed before escaped dollar, comma and backslash with FunctionProperty"() {
-        // N.B. See Bug 46831 which wanted to changed the behaviour of \$
-        // It's too late now, as this would invalidate some existing test plans,
-        // so document the current behaviour with some more tests.
-        given:
-            StringProperty prop = new StringProperty("a", propertyValue)
-        when:
-            JMeterProperty newProp = transformer.transformValue(prop)
-            newProp.setRunningVersion(true)
-        then:
-            newProp.getClass().getName() == 'org.apache.jmeter.testelement.property.' + className
-            newProp.getStringValue() == stringValue
-
-        where:
-            propertyValue | className        | stringValue
+            TransformCase("\${__regexFunction(<a>(\${my_regex})</a>,$1$)}", "hello world", type = FunctionProperty::class.java),
+            TransformCase(
+                "\${__regexFunction(<a>(\${my_regex})</a>,$1$)}\${__regexFunction(<a>(.),$1$)}",
+                "hello worldh",
+                type = FunctionProperty::class.java,
+            ),
+            // N.B. See Bug 46831 which wanted to changed the behaviour of \$
+            // It's too late now, as this would invalidate some existing test plans,
+            // so document the current behaviour with some more tests.
             // With no variable reference
-            '\\$a'        | "StringProperty" | '\\$a'
-            '\\,'         | "StringProperty" | '\\,'
-            '\\\\'        | "StringProperty" | '\\\\'
-            '\\'          | "StringProperty" | '\\'
-            '\\x'         | "StringProperty" | '\\x'
-        and: // With variable reference
-            '\\$a \\, \\\\ \\ \\x ${server} \\$b\\,z'       | "FunctionProperty" | '$a , \\ \\ \\x jakarta.apache.org $b,z'
-            '\\$a \\, \\\\ \\ \\x ${__missing(a)} \\$b\\,z' | "FunctionProperty" | '$a , \\ \\ \\x ${__missing(a)} $b,z'
-            '\\$a \\, \\\\ \\ \\x ${missing}      \\$b\\,z' | "FunctionProperty" | '$a , \\ \\ \\x ${missing}      $b,z'
-            '\\$a \\, \\\\ ${ z'                            | "FunctionProperty" | '$a , \\  z'
+            TransformCase("\\\$a", "\\\$a"),
+            TransformCase("\\,", "\\,"),
+            TransformCase("\\\\", "\\\\"),
+            TransformCase("\\", "\\"),
+            TransformCase("\\x", "\\x"),
+            TransformCase(
+                "\\\$a \\, \\\\ \\ \\x \${server} \\\$b\\,z",
+                "\$a , \\ \\ \\x jakarta.apache.org \$b,z",
+                type = FunctionProperty::class.java
+            ),
+            TransformCase(
+                "\\\$a \\, \\\\ \\ \\x \${__missing(a)} \\\$b\\,z",
+                "\$a , \\ \\ \\x \${__missing(a)} \$b,z",
+                type = FunctionProperty::class.java
+            ),
+            TransformCase(
+                "\\\$a \\, \\\\ \\ \\x \${missing}      \\\$b\\,z",
+                "\$a , \\ \\ \\x \${missing}      \$b,z",
+                type = FunctionProperty::class.java
+            ),
+            TransformCase("\\\$a \\, \\\\ \${ z", "\$a , \\  z", type = FunctionProperty::class.java),
+        )
     }
 
+    @BeforeEach
+    fun setup() {
+        val variables = mapOf(
+            "my_regex" to ".*",
+            "server" to "jakarta.apache.org"
+        )
+        transformer = ReplaceStringWithFunctions(CompoundVariable(), variables)
+        jmctx = JMeterContextService.getContext()
+        jmctx.variables = JMeterVariables()
+        jmctx.isSamplingStarted = true
+        val result = SampleResult()
+        result.setResponseData("<a>hello world</a> costs: $3.47,$5.67", null)
+        jmctx.previousResult = result
+        jmctx.variables.put("server", "jakarta.apache.org")
+        jmctx.variables.put("my_regex", ".*")
+    }
+
+    @Test
+    fun testFunctionParse1() {
+        val prop = StringProperty(
+            "date",
+            "\${__javaScript((new Date().getDate() / 100).toString()" +
+                ".substr(\${__javaScript(1+1,d\\,ay)}\\,2),heute)}"
+        )
+        val newProp = transformer.transformValue(prop)
+        newProp.setRunningVersion(true)
+
+        assertEquals(FunctionProperty::class.java, newProp::class.java, "class of property $prop after transformation")
+        newProp.recoverRunningVersion(null)
+        newProp.getStringValue().let {
+            if (it?.toIntOrNull().let { it == null || it < 0 }) {
+                fail("Property value should be positive integer, but was: $it")
+            }
+        }
+        assertEquals("2", jmctx.variables.getObject("d,ay"), "Variable 'd,ay' value")
+    }
+
+    @ParameterizedTest
+    @MethodSource("transformCases")
+    fun transformValue(case: TransformCase) {
+        val prop = StringProperty("a", case.propertyValue)
+        val newProp = transformer.transformValue(prop)
+        newProp.setRunningVersion(true)
+
+        assertEquals(
+            case.stringValue,
+            newProp.getStringValue(),
+            "stringValue of property with input ${case.propertyValue} (should parse as ${case.type})"
+        )
+        assertEquals(
+            case.type,
+            newProp::class.java,
+            "class of property with input ${case.propertyValue} after transformation"
+        )
+    }
 }
diff --git a/src/dist-check/src/test/kotlin/org/apache/jmeter/gui/action/HtmlReportGeneratorTest.kt b/src/dist-check/src/test/kotlin/org/apache/jmeter/gui/action/HtmlReportGeneratorTest.kt
index 7f1f4db..3b8104b 100644
--- a/src/dist-check/src/test/kotlin/org/apache/jmeter/gui/action/HtmlReportGeneratorTest.kt
+++ b/src/dist-check/src/test/kotlin/org/apache/jmeter/gui/action/HtmlReportGeneratorTest.kt
@@ -17,121 +17,141 @@
 
 package org.apache.jmeter.gui.action
 
-import java.net.URL
+import com.fasterxml.jackson.databind.ObjectMapper
+import org.apache.jmeter.junit.JMeterTestCase
+import org.apache.jmeter.util.JMeterUtils
+import org.junit.jupiter.api.Assertions.assertEquals
+import org.junit.jupiter.api.Test
+import org.junit.jupiter.api.fail
+import org.junit.jupiter.api.io.TempDir
+import org.junit.jupiter.params.ParameterizedTest
+import org.junit.jupiter.params.provider.MethodSource
+import java.io.File
 import java.nio.file.Paths
 import java.text.MessageFormat
 
-import org.apache.commons.io.FileUtils
-import org.apache.jmeter.junit.spock.JMeterSpec
-import org.apache.jmeter.util.JMeterUtils
+class HtmlReportGeneratorTest : JMeterTestCase() {
+    @TempDir
+    lateinit var testDirectory: File
 
-import com.fasterxml.jackson.databind.JsonNode
-import com.fasterxml.jackson.databind.ObjectMapper
+    data class CheckArgumentsCase(val csvPath: String, val userPropertiesPath: String, val outputDirectoryPath: String, val expected: List<String>)
 
-class HtmlReportGeneratorSpec extends JMeterSpec {
+    companion object {
+        /**
+         * Combine the given path parts to one path with the correct path separator of the current platform.
+         * The current JMeter bin directory will be prepended to the path.
+         *
+         * @param paths to be combined (should contain no path separators)
+         * @return combined path as string
+         */
+        fun combine(vararg paths: String) =
+            Paths.get(JMeterUtils.getJMeterBinDir(), *paths).toString()
 
-    /**
-     * Combine the given path parts to one path with the correct path separator of the current platform.
-     * The current JMeter bin directory will be prepended to the path.
-     *
-     * @param paths to be combined (should contain no path separators)
-     * @return combined path as string
-     */
-    def combine(String... paths) {
-        Paths.get(JMeterUtils.getJMeterBinDir(), paths).toString()
-    }
-
-    def "check if generation from csv: '#csvPath' with properties: '#userPropertiesPath' in folder: '#outputDirectoryPath' contains the expected error"() {
-        when:
-            HtmlReportGenerator htmlReportGenerator = new HtmlReportGenerator(csvPath, userPropertiesPath, outputDirectoryPath)
-            List<String> resultList = htmlReportGenerator.checkArguments()
-        then:
-            resultList.equals(expected)
-        where:
-            csvPath                                               | userPropertiesPath                  | outputDirectoryPath                     | expected
-            combine("testfiles", "HTMLReportTestFile.csv")        | combine("user.properties")          | combine("testfiles", "testReport")      | []
-            combine("testfiles", "HTMLReportTestFile.csv")        | combine("user.properties")          | combine("testfiles")                    | [
-                JMeterUtils.getResString("generate_report_ui.output_directory") + MessageFormat.format(JMeterUtils.getResString(HtmlReportGenerator.NOT_EMPTY_DIRECTORY), outputDirectoryPath)
-            ]
-            combine("testfiles", "HTMLReportTestFileMissing.csv") | combine("user.properties")          | combine("testfiles", "testReport")      | [
-                JMeterUtils.getResString("generate_report_ui.csv_file") + MessageFormat.format(JMeterUtils.getResString(HtmlReportGenerator.NO_FILE), csvPath)
-            ]
-            ""                                                    | ""                                  | ""                                      | [
-                JMeterUtils.getResString("generate_report_ui.csv_file") + MessageFormat.format(JMeterUtils.getResString(HtmlReportGenerator.NO_FILE), csvPath),
-                JMeterUtils.getResString("generate_report_ui.user_properties_file") + MessageFormat.format(JMeterUtils.getResString(HtmlReportGenerator.NO_FILE), userPropertiesPath),
-                JMeterUtils.getResString("generate_report_ui.output_directory") + MessageFormat.format(JMeterUtils.getResString(HtmlReportGenerator.CANNOT_CREATE_DIRECTORY), outputDirectoryPath)
-            ]
-            combine("testfiles", "HTMLReportTestFile.csv")        | combine("user.properties")          | combine("testfiles", "testReport", "oneLevel", "twolevel") | [
-                JMeterUtils.getResString("generate_report_ui.output_directory") + MessageFormat.format(JMeterUtils.getResString(HtmlReportGenerator.CANNOT_CREATE_DIRECTORY), outputDirectoryPath)
-            ]
-    }
-
-    def "check that report generation succeeds and statistic are generated"(){
-        setup:
-            File testDirectory = new File(combine("testfiles", "testReport"))
-            if(testDirectory.exists()) {
-                if (testDirectory.list().length>0) {
-                    FileUtils.cleanDirectory(testDirectory)
-                }
-            } else {
-                testDirectory.mkdir()
-            }
-            ObjectMapper mapper = new ObjectMapper()
-            URL expected = HtmlReportGenerator.class.getResource("/org/apache/jmeter/gui/report/HTMLReportExpect.json");
-            JsonNode expectedRoot = null;
-            expected.withReader { jsonFileReader ->
-                expectedRoot = mapper.readTree(jsonFileReader)
-            }
-        when:
-            HtmlReportGenerator htmlReportGenerator = new HtmlReportGenerator(
+        @JvmStatic
+        fun checkArgumentsCases() = listOf(
+            CheckArgumentsCase(
+                combine("testfiles", "HTMLReportTestFile.csv"),
+                combine("user.properties"),
+                combine("testfiles", "testReport"),
+                listOf()
+            ),
+            combine("testfiles").let { outputDirectoryPath ->
+                CheckArgumentsCase(
                     combine("testfiles", "HTMLReportTestFile.csv"),
                     combine("user.properties"),
-                    testDirectory.toString())
-            List<String> resultList = htmlReportGenerator.run()
-            File statistics = new File(combine("testfiles", "testReport", "statistics.json"))
-            JsonNode actualRoot = null;
-            if (statistics.exists()) {
-                statistics.withReader { jsonFileReader ->
-                    actualRoot = mapper.readTree(jsonFileReader)
-                }
-            }
-        then:
-            resultList.isEmpty()
-            statistics.exists()
-            expectedRoot != null
-            expectedRoot == actualRoot
-        cleanup:
-            if(testDirectory.exists()) {
-                if (testDirectory.list().length>0) {
-                    FileUtils.cleanDirectory(testDirectory)
-                }
-            }
+                    outputDirectoryPath,
+                    listOf(
+                        JMeterUtils.getResString("generate_report_ui.output_directory") +
+                            MessageFormat.format(
+                                JMeterUtils.getResString(HtmlReportGenerator.NOT_EMPTY_DIRECTORY),
+                                outputDirectoryPath
+                            )
+                    )
+                )
+            },
+            combine("testfiles", "HTMLReportTestFileMissing.csv").let { csvPath ->
+                CheckArgumentsCase(
+                    csvPath,
+                    combine("user.properties"),
+                    combine("testfiles", "testReport"),
+                    listOf(
+                        JMeterUtils.getResString("generate_report_ui.csv_file") +
+                            MessageFormat.format(
+                                JMeterUtils.getResString(HtmlReportGenerator.NO_FILE),
+                                csvPath
+                            )
+                    )
+                )
+            },
+            CheckArgumentsCase(
+                "",
+                "",
+                "",
+                listOf(
+                    JMeterUtils.getResString("generate_report_ui.csv_file") +
+                        MessageFormat.format(JMeterUtils.getResString(HtmlReportGenerator.NO_FILE), ""),
+                    JMeterUtils.getResString("generate_report_ui.user_properties_file") +
+                        MessageFormat.format(JMeterUtils.getResString(HtmlReportGenerator.NO_FILE), ""),
+                    JMeterUtils.getResString("generate_report_ui.output_directory") +
+                        MessageFormat.format(JMeterUtils.getResString(HtmlReportGenerator.CANNOT_CREATE_DIRECTORY), "")
+                )
+            ),
+            combine("testfiles", "testReport", "oneLevel", "twolevel").let { outputDirectoryPath ->
+                CheckArgumentsCase(
+                    combine("testfiles", "HTMLReportTestFile.csv"),
+                    combine("user.properties"),
+                    outputDirectoryPath,
+                    listOf(
+                        JMeterUtils.getResString("generate_report_ui.output_directory") +
+                            MessageFormat.format(
+                                JMeterUtils.getResString(HtmlReportGenerator.CANNOT_CREATE_DIRECTORY),
+                                outputDirectoryPath
+                            )
+                    )
+                )
+            },
+        )
     }
 
-    def "report generation fails when format does not match and error is reported"() {
-        setup:
-            File testDirectory = new File(combine("testfiles", "testReportThatShouldBeEmpty"))
-            if(testDirectory.exists()) {
-                if (testDirectory.list().length>0) {
-                    FileUtils.cleanDirectory(testDirectory)
-                }
-            } else {
-                testDirectory.mkdir()
-            }
-        when:
-            HtmlReportGenerator htmlReportGenerator = new HtmlReportGenerator(
-                    combine("testfiles", "HTMLReportFalseTestFile.csv"),
-                    combine("user.properties"),
-                    testDirectory.toString())
-            List<String> resultList = htmlReportGenerator.run()
-        then:
-            testDirectory.list().length == 0
-            resultList.get(0).contains("An error occurred: Error while processing samples: Consumer failed with message")
-        cleanup:
-            if(testDirectory.exists()) {
-                if (testDirectory.list().length>0) {
-                    FileUtils.cleanDirectory(testDirectory)
-                }
-            }
+    @ParameterizedTest
+    @MethodSource("checkArgumentsCases")
+    fun checkArguments(case: CheckArgumentsCase) {
+        val htmlReportGenerator = HtmlReportGenerator(case.csvPath, case.userPropertiesPath, case.outputDirectoryPath)
+        val resultList = htmlReportGenerator.checkArguments()
+
+        assertEquals(case.expected, resultList, "resultList")
+    }
+
+    @Test
+    fun `check that report generation succeeds and statistic are generated`() {
+        val mapper = ObjectMapper()
+        val expected = HtmlReportGenerator::class.java.getResource("/org/apache/jmeter/gui/report/HTMLReportExpect.json")
+        val expectedRoot = mapper.readTree(expected)
+        val htmlReportGenerator = HtmlReportGenerator(
+            combine("testfiles", "HTMLReportTestFile.csv"),
+            combine("user.properties"),
+            testDirectory.toString()
+        )
+        val resultList = htmlReportGenerator.run()
+        val statistics = File(testDirectory, "statistics.json")
+        val actualRoot = mapper.readTree(statistics)
+
+        assertEquals(expectedRoot, actualRoot, "test report json file")
+    }
+
+    @Test
+    fun `report generation fails when format does not match and error is reported`() {
+        val htmlReportGenerator = HtmlReportGenerator(
+            combine("testfiles", "HTMLReportFalseTestFile.csv"),
+            combine("user.properties"),
+            testDirectory.toString()
+        )
+        val resultList = htmlReportGenerator.run()
+        assertEquals("[]", testDirectory.list()?.contentDeepToString(), "testDirectory contents")
+        val firstMessage = resultList.firstOrNull()
+        val expectedError = "An error occurred: Error while processing samples: Consumer failed with message"
+        if (firstMessage?.contains(expectedError) != true) {
+            fail("First result message should contain '$expectedError', but was '$firstMessage'")
+        }
     }
 }
diff --git a/src/dist-check/src/test/kotlin/org/apache/jmeter/gui/util/MenuFactoryTest.kt b/src/dist-check/src/test/kotlin/org/apache/jmeter/gui/util/MenuFactoryTest.kt
index ad2d0ed..f1d8d24 100644
--- a/src/dist-check/src/test/kotlin/org/apache/jmeter/gui/util/MenuFactoryTest.kt
+++ b/src/dist-check/src/test/kotlin/org/apache/jmeter/gui/util/MenuFactoryTest.kt
@@ -17,18 +17,23 @@
 
 package org.apache.jmeter.gui.util
 
-import org.apache.jmeter.junit.spock.JMeterSpec
+import org.apache.jmeter.junit.JMeterTestCase
+import org.junit.jupiter.api.Assertions.assertEquals
+import org.junit.jupiter.api.Assertions.assertNotEquals
+import org.junit.jupiter.api.Test
 
-class MenuFactorySpec extends JMeterSpec {
+class MenuFactoryTest : JMeterTestCase() {
 
-    def "ensure each menu has something in it"() {
-        expect:
-            MenuFactory.menuMap.size() == 12
-            MenuFactory.menuMap.every { !it.value.isEmpty() }
+    @Test
+    fun `ensure each menu has something in it`() {
+        assertEquals(12, MenuFactory.getMenuMap().size, "MenuFactory.getMenuMap().size")
+        MenuFactory.getMenuMap().forEach { (group, items) ->
+            assertNotEquals(0, items.size, "MenuFactory.getMenuMap()[$group].size")
+        }
     }
 
-    def "default add menu has expected item count"() {
-        expect:
-            MenuFactory.createDefaultAddMenu().itemCount == 6 + 3 // items + separators
+    @Test
+    fun `default add menu has expected item count`() {
+        assertEquals(6 + 3, MenuFactory.createDefaultAddMenu().itemCount, "items + separators")
     }
 }
diff --git a/src/dist-check/src/test/kotlin/org/apache/jmeter/report/dashboard/ReportGeneratorTest.kt b/src/dist-check/src/test/kotlin/org/apache/jmeter/report/dashboard/ReportGeneratorTest.kt
index 5a6e046..6191f7a 100644
--- a/src/dist-check/src/test/kotlin/org/apache/jmeter/report/dashboard/ReportGeneratorTest.kt
+++ b/src/dist-check/src/test/kotlin/org/apache/jmeter/report/dashboard/ReportGeneratorTest.kt
@@ -17,17 +17,21 @@
 
 package org.apache.jmeter.report.dashboard
 
-import java.net.URL
+import com.fasterxml.jackson.databind.ObjectMapper
+import org.apache.jmeter.junit.JMeterTestCase
+import org.apache.jmeter.util.JMeterUtils
+import org.junit.jupiter.api.Assertions.assertEquals
+import org.junit.jupiter.api.Test
+import org.junit.jupiter.api.assertThrows
+import org.junit.jupiter.api.io.TempDir
+import org.junit.jupiter.api.parallel.Isolated
+import java.io.File
 import java.nio.file.Paths
 
-import org.apache.commons.io.FileUtils
-import org.apache.jmeter.junit.spock.JMeterSpec
-import org.apache.jmeter.util.JMeterUtils
-
-import com.fasterxml.jackson.databind.JsonNode
-import com.fasterxml.jackson.databind.ObjectMapper
-
-class ReportGeneratorSpec extends JMeterSpec{
+@Isolated("modifies shared properties")
+class ReportGeneratorTest : JMeterTestCase() {
+    @TempDir
+    lateinit var testDirectory: File
 
     /**
      * Combine the given path parts to one path with the correct path separator of the current platform.
@@ -36,72 +40,34 @@
      * @param paths to be combined (should contain no path separators)
      * @return combined path as string
      */
-    def combine(String... paths) {
-       Paths.get(JMeterUtils.getJMeterBinDir(), paths).toString()
+    fun combine(vararg paths: String) =
+        Paths.get(JMeterUtils.getJMeterBinDir(), *paths).toString()
+
+    @Test
+    fun `check that report generation succeeds and statistics json are generated`() {
+        val mapper = ObjectMapper()
+        val expected = ReportGenerator::class.java.getResource("/org/apache/jmeter/gui/report/HTMLReportExpect.json")
+        val expectedRoot = mapper.readTree(expected)
+
+        JMeterUtils.setProperty("jmeter.reportgenerator.outputdir", testDirectory.absolutePath)
+        val reportGenerator = ReportGenerator(
+            combine("testfiles", "HTMLReportTestFile.csv"), null
+        )
+        reportGenerator.generate()
+        val statistics = File(testDirectory, "statistics.json")
+        val actualRoot = mapper.readTree(statistics)
+
+        assertEquals(expectedRoot, actualRoot, "test report json file")
     }
 
-    def "check that report generation succeeds and statistics.json are generated"(){
-        setup:
-            File testDirectory = new File(combine("testfiles", "testReport"))
-            if(testDirectory.exists()) {
-                if (testDirectory.list().length>0) {
-                    FileUtils.cleanDirectory(testDirectory)
-                }
-            } else {
-                testDirectory.mkdir()
-            }
-            ObjectMapper mapper = new ObjectMapper()
-            URL expected = ReportGenerator.class.getResource("/org/apache/jmeter/gui/report/HTMLReportExpect.json");
-            JsonNode expectedRoot = null;
-            expected.withReader { jsonFileReader ->
-                expectedRoot = mapper.readTree(jsonFileReader)
-            }
-        when:
-            JMeterUtils.setProperty("jmeter.reportgenerator.outputdir", testDirectory.getAbsolutePath())
-            ReportGenerator reportGenerator = new ReportGenerator(
-                    combine("testfiles", "HTMLReportTestFile.csv"),null)
+    @Test
+    fun `check that report generation fails when format does not match and error is reported`() {
+        assertThrows<GenerationException> {
+            val reportGenerator = ReportGenerator(
+                combine("testfiles", "HTMLReportFalseTestFile.csv"), null
+            )
             reportGenerator.generate()
-            File statistics = new File(combine("testfiles", "testReport", "statistics.json"))
-            JsonNode actualRoot = null;
-            if (statistics.exists()) {
-                statistics.withReader { jsonFileReader ->
-                    actualRoot = mapper.readTree(jsonFileReader)
-                }
-            }
-        then:
-            statistics.exists()
-            expectedRoot != null
-            expectedRoot == actualRoot
-        cleanup:
-            if(testDirectory.exists()) {
-                if (testDirectory.list().length>0) {
-                    FileUtils.cleanDirectory(testDirectory)
-                }
-            }
-    }
-
-    def "check that report generation fails when format does not match and error is reported"(){
-        setup:
-            File testDirectory = new File(combine("testfiles", "testReportThatShouldBeEmpty"))
-            if(testDirectory.exists()) {
-                if (testDirectory.list().length>0) {
-                    FileUtils.cleanDirectory(testDirectory)
-                }
-            } else {
-                testDirectory.mkdir()
-            }
-        when:
-            ReportGenerator reportGenerator = new ReportGenerator(
-                combine("testfiles", "HTMLReportFalseTestFile.csv"), null)
-            reportGenerator.generate()
-        then:
-            thrown(GenerationException)
-            testDirectory.list().length == 0
-        cleanup:
-            if(testDirectory.exists()) {
-                if (testDirectory.list().length>0) {
-                    FileUtils.cleanDirectory(testDirectory)
-                }
-            }
+        }
+        assertEquals("[]", testDirectory.list()?.contentDeepToString(), "testDirectory contents")
     }
 }