diff --git a/build.gradle b/build.gradle index fc2da45b..59041031 100644 --- a/build.gradle +++ b/build.gradle @@ -269,6 +269,10 @@ tasks.register('containerTest', Test) { useJUnitPlatform() } +tasks.withType(GroovyCompile).configureEach { + groovyOptions.optimizationOptions.indy = true +} + jacocoTestReport { dependsOn test reports { diff --git a/src/test/groovy/de/tracetronic/jenkins/plugins/ecutestexecution/util/ConverterUtilTest.groovy b/src/test/groovy/de/tracetronic/jenkins/plugins/ecutestexecution/util/ConverterUtilTest.groovy new file mode 100644 index 00000000..ee73eea6 --- /dev/null +++ b/src/test/groovy/de/tracetronic/jenkins/plugins/ecutestexecution/util/ConverterUtilTest.groovy @@ -0,0 +1,117 @@ +/* + * Copyright (c) 2024 tracetronic GmbH + * + * SPDX-License-Identifier: BSD-3-Clause + */ +package de.tracetronic.jenkins.plugins.ecutestexecution.util + + +import de.tracetronic.jenkins.plugins.ecutestexecution.model.PackageParameter +import de.tracetronic.jenkins.plugins.ecutestexecution.model.RecordingAsSetting +import spock.lang.Specification + +class ConverterUtilTest extends Specification { + + def "should return empty list"() { + expect: + ConverterUtil.recordingConverter(new ArrayList()) + .isEmpty() + } + + def "should convert single RecordingAsSetting to Recording"() { + given: + def recordingAsSetting = new RecordingAsSetting("/test/path") + recordingAsSetting.setRecordingGroup("testGroup") + recordingAsSetting.setMappingNames(["mapping1", "mapping2"]) + recordingAsSetting.setDeviceName("testDevice") + recordingAsSetting.setFormatDetails("testFormat") + + when: + def result = ConverterUtil.recordingConverter([recordingAsSetting]) + + then: + result.size() == 1 + with(result[0]) { + path == "/test/path" + recordingGroup == "testGroup" + mappingNames == ["mapping1", "mapping2"] + deviceName == "testDevice" + formatDetails == "testFormat" + } + } + + def "should convert multiple RecordingAsSettings to Recordings"() { + given: + def recordingAsSetting1 = new RecordingAsSetting("/test/path") + recordingAsSetting1.setRecordingGroup("testGroup") + recordingAsSetting1.setMappingNames(["mapping1", "mapping2"]) + recordingAsSetting1.setDeviceName("testDevice") + recordingAsSetting1.setFormatDetails("testFormat") + + def recordingAsSetting2 = new RecordingAsSetting("/test/path2") + recordingAsSetting2.setRecordingGroup("testGroup2") + recordingAsSetting2.setMappingNames(["mapping3", "mapping4"]) + recordingAsSetting2.setDeviceName("testDevice2") + recordingAsSetting2.setFormatDetails("testFormat2") + + when: + def result = ConverterUtil.recordingConverter([recordingAsSetting1, recordingAsSetting2]) + + then: + result.size() == 2 + with(result[0]) { + path == "/test/path" + recordingGroup == "testGroup" + mappingNames == ["mapping1", "mapping2"] + deviceName == "testDevice" + formatDetails == "testFormat" + } + with(result[1]) { + path == "/test/path2" + recordingGroup == "testGroup2" + mappingNames == ["mapping3", "mapping4"] + deviceName == "testDevice2" + formatDetails == "testFormat2" + } + } + + def "should convert empty list of PackageParameter to empty list of LabeledValue"() { + expect: + ConverterUtil.labeledValueConverter(new ArrayList()).isEmpty() + } + + def "should convert PackageParameter to LabeledValue"() { + given: + def pkgParam = new PackageParameter('label', 'value') + + when: + def result = ConverterUtil.labeledValueConverter([pkgParam]) + + then: + result.size() == 1 + with(result[0]) { + it.label == "label" + it.value == "value" + } + } + + def "should convert multiple PackageParameter to LabeledValues"() { + given: + def pkgParam1 = new PackageParameter('label', 'value') + def pkgParam2 = new PackageParameter('label2', 'value2') + + when: + def result = ConverterUtil.labeledValueConverter([pkgParam1, pkgParam2]) + + then: + result.size() == 2 + with(result[0]) { + it.label == "label" + it.value == "value" + } + with(result[1]) { + it.label == "label2" + it.value == "value2" + } + } +} diff --git a/src/test/groovy/de/tracetronic/jenkins/plugins/ecutestexecution/util/EnvVarUtilTest.groovy b/src/test/groovy/de/tracetronic/jenkins/plugins/ecutestexecution/util/EnvVarUtilTest.groovy new file mode 100644 index 00000000..43ab06a6 --- /dev/null +++ b/src/test/groovy/de/tracetronic/jenkins/plugins/ecutestexecution/util/EnvVarUtilTest.groovy @@ -0,0 +1,55 @@ +/* + * Copyright (c) 2024 tracetronic GmbH + * + * SPDX-License-Identifier: BSD-3-Clause + */ +package de.tracetronic.jenkins.plugins.ecutestexecution.util + +import hudson.EnvVars +import spock.lang.Specification + +class EnvVarUtilTest extends Specification { + + def "Unsupported class exception"() { + when: + new EnvVarUtil() + then: + def e = thrown(UnsupportedOperationException) + e.cause == null + e.message == "Utility class" + + } + + def "expandVar should return '#expectedResult' for envVar=#envVar and envVars=#vars"() { + given: + def envVars = new EnvVars() + vars.each { k, v -> envVars.put(k, v) } + + when: + def result = EnvVarUtil.expandVar(envVar, envVars, "default") + + then: + result == expectedResult + + where: + envVar | vars | expectedResult + 'TEST_VAR' | [TEST_VAR: "value"] | "TEST_VAR" + 'MISSING_VAR' | [TEST_VAR: "value"] | "MISSING_VAR" + '${TEST_VAR}' | [TEST_VAR: "value"] | "value" + '${VAR1} ${VAR2}' | [VAR1: "Hello", VAR2: "World"] | "Hello World" + } + + def "expandVar should return default value"() { + given: + def envVars = new EnvVars() + + when: + def result = EnvVarUtil.expandVar(envVar, envVars, "default") + + then: + result == "default" + + where: + envVar << [null, "", " "] + } +} diff --git a/src/test/groovy/de/tracetronic/jenkins/plugins/ecutestexecution/util/ProcessUtilTest.groovy b/src/test/groovy/de/tracetronic/jenkins/plugins/ecutestexecution/util/ProcessUtilTest.groovy index 39988ca6..5b6f5ab0 100644 --- a/src/test/groovy/de/tracetronic/jenkins/plugins/ecutestexecution/util/ProcessUtilTest.groovy +++ b/src/test/groovy/de/tracetronic/jenkins/plugins/ecutestexecution/util/ProcessUtilTest.groovy @@ -5,48 +5,159 @@ */ package de.tracetronic.jenkins.plugins.ecutestexecution.util -import spock.lang.IgnoreIf +import de.tracetronic.jenkins.plugins.ecutestexecution.ETInstallation +import hudson.Functions import spock.lang.Specification +import java.util.concurrent.TimeUnit + class ProcessUtilTest extends Specification { - @IgnoreIf({ sys["spock.skip.sandbox"] == 'true' }) - def 'test killProcess'(int timeout, expected) { - expect: - ProcessUtil.killProcess("doesReallyNotExistFoo", timeout) == expected - where: - timeout | expected - -1 | false - 0 | false - 1 | true + def "Unsupported class exception"() { + when: + new ProcessUtil() + then: + def e = thrown(UnsupportedOperationException) + e.cause == null + e.message == "Utility class" } - @IgnoreIf({ sys["spock.skip.sandbox"] == 'true' }) - def 'test killProcesses'(int timeout, expected) { - expect: - ProcessUtil.killProcesses(["doesReallyNotExistFoo", "doesReallyNotExistBar"], timeout) == expected - + def 'test killProcess with timeout'() { + given: + GroovyMock(Functions, global: true) + Functions.isWindows() >> false + and: + def mockProcess = GroovyMock(Process) + def mockBuilder = GroovyMock(ProcessBuilder) + GroovyMock(ProcessBuilder, global: true) + new ProcessBuilder() >> mockBuilder + mockBuilder.command(_) >> mockBuilder + mockBuilder.start() >> mockProcess + and: + def countWaitFor = 1 - countWaitForTimeout + when: + def result = ProcessUtil.killProcess("doesReallyNotExistFoo", timeout) + then: + result + countWaitForTimeout* mockProcess.waitFor() >> 0 + countWaitFor* mockProcess.waitFor(_, TimeUnit.SECONDS) >> true where: - timeout | expected - -1 | false - 0 | false - 1 | true + timeout | countWaitForTimeout + -1 | 1 + 0 | 1 + 1 | 0 } - def 'test killTTProcesses'(int timeout, expected) { - expect: - ProcessUtil.killTTProcesses(timeout) == expected + def 'test killProcess for different os and task name'() { + given: + GroovyMock(Functions, global: true) + Functions.isWindows() >> isWindows + and: + def mockProcess = GroovyMock(Process) + def mockBuilder = GroovyMock(ProcessBuilder) + GroovyMock(ProcessBuilder, global: true) + new ProcessBuilder() >> mockBuilder + def withArgs + mockBuilder.command(_) >> { args -> + withArgs = args[0] + mockBuilder + } + mockBuilder.start() >> mockProcess + mockProcess.waitFor() >> 0 + when: + def result = ProcessUtil.killProcess("doesReallyNotExistFoo", 0) + then: + result + withArgs.contains(processName) + where: + isWindows << [false, true] + processName << ['pkill', 'taskkill.exe'] + } + def 'test killProcesses with default timeout'() { + given: + def killProcessesCallCount = 0 + def capturedExecutables = [] + def capturedTimeout = 30 // default timeout if method not called + def responses = killProcessReturn.iterator() + ProcessUtil.metaClass.static.killProcess = { String executable, int timeout-> + killProcessesCallCount++ + capturedExecutables << executable + capturedTimeout = timeout + responses.next() + } + when: + def result = ProcessUtil.killProcesses(exeFiles) + then: + result == expected + killProcessesCallCount == methodCalls + capturedExecutables == exeFiles + capturedTimeout == 30 + cleanup: + ProcessUtil.metaClass = null where: - timeout | expected - -1 | false - 0 | false - 1 | true + exeFiles | killProcessReturn | methodCalls | expected + ['ecu.test.exe', 'trace.check.exe'] | [true, true] | 2 | true + ['ecu.test.exe', 'trace.check.exe'] | [false, true] | 2 | false + ['ecu.test.exe', 'trace.check.exe'] | [true, false] | 2 | false + ['ecu.test.exe'] | [true] | 1 | true + ['ecu.test.exe'] | [false] | 1 | false + [] | [] | 0 | true } - def 'test killTTProcesses default'() { - expect: - ProcessUtil.killTTProcesses() == true + def "Test killProcesses with timeout"() { + given: + def killProcessesCallCount = 0 + def capturedExecutables = [] + def capturedTimeout = 0 + ProcessUtil.metaClass.static.killProcess = { String executable, int timeoutArg-> + killProcessesCallCount++ + capturedExecutables << executable + capturedTimeout = timeoutArg + true + } + when: + def result = timeout ? ProcessUtil.killProcesses(exeFiles, timeout) : ProcessUtil.killProcesses(exeFiles) + then: + result + killProcessesCallCount == 2 + capturedExecutables == exeFiles + capturedTimeout == timeout ?: 30 // check default value for timeout + cleanup: + ProcessUtil.metaClass = null + where: + exeFiles | timeout + ['ecu.test.exe', 'trace.check.exe'] | null + ['ecu.test.exe', 'trace.check.exe'] | 60 + ['ecu.test.exe', 'trace.check.exe'] | 30 + + } + def 'test killTTProcesses arguments with default timeout'() { + given: + def exeFiles = ['ecu.test.exe', 'trace.check.exe'] + ETInstallation.metaClass.static.getExeFileNames = { -> exeFiles } + and: + def killProcessesCallCount = 0 + def capturedExecutables = [] + def capturedTimeout = 0 + ProcessUtil.metaClass.static.killProcesses = { List executables, int timeout -> + killProcessesCallCount++ + capturedExecutables = executables + capturedTimeout = timeout + expectedResult + } + when: + def result = ProcessUtil.killTTProcesses() + then: + result == expectedResult + capturedExecutables == exeFiles + capturedTimeout == 30 + killProcessesCallCount == 1 + cleanup: + ProcessUtil.metaClass = null + ETInstallation.metaClass = null + where: + expectedResult << [true, false] } } diff --git a/src/test/groovy/de/tracetronic/jenkins/plugins/ecutestexecution/util/ValidationUtilTest.groovy b/src/test/groovy/de/tracetronic/jenkins/plugins/ecutestexecution/util/ValidationUtilTest.groovy index d9f06d3e..cb8ab5d5 100644 --- a/src/test/groovy/de/tracetronic/jenkins/plugins/ecutestexecution/util/ValidationUtilTest.groovy +++ b/src/test/groovy/de/tracetronic/jenkins/plugins/ecutestexecution/util/ValidationUtilTest.groovy @@ -6,13 +6,21 @@ package de.tracetronic.jenkins.plugins.ecutestexecution.util import hudson.util.FormValidation -import org.apache.http.client.fluent.Form import spock.lang.Specification import java.nio.file.Paths class ValidationUtilTest extends Specification { + def "Unsupported class exception"() { + when: + new ValidationUtil() + then: + def e = thrown(UnsupportedOperationException) + e.cause == null + e.message == "Utility class" + } + def 'Validate parametrized values'(String value, boolean required, FormValidation.Kind expectedKind) { given: FormValidation validation = ValidationUtil.validateParameterizedValue(value, required) @@ -29,8 +37,6 @@ class ValidationUtilTest extends Specification { def 'Validate absolute path values'(String value, FormValidation.Kind expectedKind) { given: FormValidation validation = ValidationUtil.validateAbsolutePath(value) - ValidationUtil validationUtil = Mock() - validationUtil.validateParameterizedValue(value, true) >> FormValidation.ok() expect: validation.kind == expectedKind where: @@ -40,16 +46,22 @@ class ValidationUtilTest extends Specification { expectedKind << [FormValidation.Kind.ERROR, FormValidation.Kind.OK] } + def 'Validate absolute path values with invalid param'() { + expect: + FormValidation.Kind.ERROR == ValidationUtil.validateAbsolutePath("").kind + } + def 'Validate timeout values'(int value, FormValidation.Kind expectedKind) { given: FormValidation validation = ValidationUtil.validateTimeout(value) expect: validation.kind == expectedKind where: - value | expectedKind - 1 | FormValidation.Kind.OK - 0 | FormValidation.Kind.WARNING - -1 | FormValidation.Kind.ERROR + value | expectedKind + '1' | FormValidation.Kind.OK + 1 | FormValidation.Kind.OK + 0 | FormValidation.Kind.WARNING + -1 | FormValidation.Kind.ERROR } def 'Validate config files'(String configFilePath, String fileExtension, FormValidation.Kind expectedKind) { @@ -64,6 +76,7 @@ class ValidationUtilTest extends Specification { 'KEEP' | '' | FormValidation.Kind.OK 'KEEP' | '.tcf' | FormValidation.Kind.OK '${CONFIG}' | '' | FormValidation.Kind.WARNING + '' | '' | FormValidation.Kind.OK null | null | FormValidation.Kind.OK }