Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Improve test config change handling (#164) #177

Merged
merged 18 commits into from
Nov 15, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,7 @@ dependencies {
testImplementation('org.jenkins-ci.plugins.workflow:workflow-basic-steps') {//:1042.ve7b_140c4a_e0c') {
exclude group: 'org.jenkins-ci.modules', module: 'instance-identity'
}
testImplementation 'org.jenkins-ci.plugins:http_request'
testImplementation 'org.jenkins-ci.plugins.workflow:workflow-cps'
testImplementation('org.jenkins-ci.plugins.workflow:workflow-cps') {artifact{ classifier = 'tests'}}
testImplementation 'org.jenkins-ci.plugins.workflow:workflow-job'
Expand Down
17 changes: 11 additions & 6 deletions docs/AdvancedUsage.md
Original file line number Diff line number Diff line change
Expand Up @@ -91,12 +91,17 @@ node {

## TestConfig

| Properties | Default Value | Description |
|----------------------------------------------|---------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| **tbcPath**: String | '' | The relative path of the .tbc file in the Configurations directory to be started for this execution. Use "KEEP" to use the currently loaded test bench configuration. If empty, no test bench configuration will be loaded. |
| **tcfPath**: String | '' | The relative path of the .tcf file in the Configurations directory to be started for this execution. Use "KEEP" to use the currently loaded test configuration. If empty, no test configuration will be loaded. |
| **forceConfigurationReload**: boolean | false | If true, always reload the configuration even if the same one is still active. Hint: This flag is only required for ecu.test versions less than 2023.4! |
| **constants**: List\<[Constant](#constant)> | [] | The configured global constants remain available throughout the entire test execution. |
| Properties | Default Value | Description |
|----------------------------------------------|---------------|------------------------------------------------------------------------------------------------------------------------------------------------------------|
| **tbcPath**: String | null | The relative path of the .tbc file in the Configurations directory to be started for this execution. If empty, no test bench configuration will be loaded. |
| **tcfPath**: String | null | The relative path of the .tcf file in the Configurations directory to be started for this execution. If empty, no test configuration will be loaded. |
| **forceConfigurationReload**: boolean | false | If true, always reload the configuration even if the same one is still active. |
| **constants**: List\<[Constant](#constant)> | [] | The configured global constants remain available throughout the entire test execution. |

### Configuration Change Options

- **Load Configuration**: The TestConfiguration and/or the TestBenchConfiguration files must be explicitly set whenever a new configuration is needed. If both are empty, Test Configuration will be unloaded. Setting `forceConfigurationReload` to `true` forces a configuration reload, even if the same configuration is still active.
- **Keep Configuration**: Enable this option by not specifying the testConfig property, for example `ttRunTestPackage '<myPackageName>.pkg'` this option retains the existing configuration for continued use throughout the execution.

## PublishConfig

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -72,8 +72,7 @@ class ExecutionOrderBuilder implements Serializable {
settings = new AdditionalSettings(testConfig.forceConfigurationReload)
}

ExecutionOrder executionOrder = new ExecutionOrder(testCasePath, settings, testConfig.tbcPath,
testConfig.tcfPath, testConfig.constants)
ExecutionOrder executionOrder = new ExecutionOrder(testCasePath, testConfig, settings)

return executionOrder
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -127,25 +127,34 @@ class RestApiClientV2 extends RestApiClientV2WithIdleHandle implements RestApiCl
ExecutionApi executionApi = new ExecutionApi(apiClient)
de.tracetronic.cxs.generated.et.client.model.v2.ExecutionOrder executionOrderV2
executionOrderV2 = executionOrder.toExecutionOrderV2()

List<LabeledValue> constants = []
executionOrder.constants.each { constant ->
constants.add(new LabeledValue().label(constant.label).value(constant.value))
}

ConfigurationOrder configOrder = new ConfigurationOrder()
.tbc(new TestbenchConfiguration().tbcPath(executionOrder.tbcPath))
.tcf(new TestConfiguration().tcfPath(executionOrder.tcfPath))
.constants(constants)
.action(ConfigurationOrder.ActionEnum.START)
try {
if (configOrder.tbc.tbcPath || configOrder.tcf.tcfPath || constants) {
if(executionOrder.loadConfig) {
ConfigurationApi configApi = new ConfigurationApi(apiClient)
configApi.manageConfiguration(configOrder)

Closure<Boolean> checkConfigStatus = { ModelConfiguration configuration ->
configuration?.status?.key in [null, ConfigurationStatus.KeyEnum.WAITING, ConfigurationStatus.KeyEnum.RUNNING]
}

if(executionOrder.forceReload) {
ConfigurationOrder loadConfigOrder = new ConfigurationOrder().action(ConfigurationOrder.ActionEnum.STOP)
configApi.manageConfiguration(loadConfigOrder)

while (checkConfigStatus(configApi.lastConfigurationOrder)) {
sleep(1000)
}
}

configApi.manageConfiguration(configOrder)
while (checkConfigStatus(configApi.lastConfigurationOrder)) {
sleep(1000)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@

package de.tracetronic.jenkins.plugins.ecutestexecution.clients.model

import de.tracetronic.jenkins.plugins.ecutestexecution.configs.TestConfig
import de.tracetronic.jenkins.plugins.ecutestexecution.model.Constant
import jline.internal.Nullable

Expand All @@ -17,22 +18,25 @@ class ExecutionOrder implements Serializable {

public String testCasePath
public AdditionalSettings additionalSetting
public boolean loadConfig
public boolean forceReload
@Nullable public String tbcPath
@Nullable public String tcfPath
@Nullable public List<Constant> constants

ExecutionOrder(String testCasePath, AdditionalSettings additionalSetting,
String tbcPath, String tcfPath, List<Constant> constants) {
ExecutionOrder(String testCasePath, AdditionalSettings additionalSetting) {
this.testCasePath = testCasePath
this.additionalSetting = additionalSetting
this.tbcPath = tbcPath
this.tcfPath = tcfPath
this.constants = constants
}

ExecutionOrder(String testCasePath, AdditionalSettings additionalSetting) {
ExecutionOrder(String testCasePath, TestConfig testConfig, AdditionalSettings additionalSettings) {
this.testCasePath = testCasePath
this.additionalSetting = additionalSetting
this.additionalSetting = additionalSettings
this.tbcPath = testConfig.tbcPath
this.tcfPath = testConfig.tcfPath
this.constants = testConfig.constants
this.loadConfig = testConfig.loadConfig
this.forceReload = testConfig.forceConfigurationReload
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,51 +12,63 @@ import hudson.Extension
import hudson.model.AbstractDescribableImpl
import hudson.model.Descriptor
import hudson.util.FormValidation
import jline.internal.Nullable
import net.sf.json.JSONObject
import org.apache.commons.lang.StringUtils
import org.kohsuke.stapler.DataBoundConstructor
import org.kohsuke.stapler.DataBoundSetter
import org.kohsuke.stapler.QueryParameter
import org.kohsuke.stapler.StaplerRequest

class TestConfig extends AbstractDescribableImpl<TestConfig> implements ExpandableConfig, Serializable {

private static final long serialVersionUID = 1L

private boolean loadConfig
private String tbcPath
private String tcfPath
private boolean forceConfigurationReload
private List<Constant> constants

@DataBoundConstructor
TestConfig() {
this.tbcPath = ''
this.tcfPath = ''
this.forceConfigurationReload = false
this.tcfPath = null
this.tbcPath = null
this.constants = []
this.forceConfigurationReload = false
this.loadConfig = false
}

TestConfig(TestConfig config) {
this.tbcPath = config.getTbcPath()
this.tcfPath = config.getTcfPath()
this.constants = config.getConstants()
this.forceConfigurationReload = config.forceConfigurationReload
this()

this.loadConfig = config.tcfPath != null || config.tbcPath != null
if(this.loadConfig) {
this.tbcPath = config.getTbcPath()
this.tcfPath = config.getTcfPath()
MartinGroscheTT marked this conversation as resolved.
Show resolved Hide resolved
this.constants = config.getConstants()
this.forceConfigurationReload = config.isForceConfigurationReload()
}
}

@Nullable
String getTbcPath() {
return tbcPath
}

@DataBoundSetter
void setTbcPath(String tbcPath) {
this.tbcPath = StringUtils.trimToEmpty(tbcPath)
this.tbcPath = tbcPath
}

@Nullable
String getTcfPath() {
return tcfPath
}

@DataBoundSetter
void setTcfPath(String tcfPath) {
this.tcfPath = StringUtils.trimToEmpty(tcfPath)
this.tcfPath = tcfPath
}

boolean isForceConfigurationReload() {
Expand All @@ -77,13 +89,18 @@ class TestConfig extends AbstractDescribableImpl<TestConfig> implements Expandab
this.constants = constants ? removeEmptyConstants(constants) : []
}

boolean getLoadConfig() {
return loadConfig
}

@Override
String toString() {
"""
-> tbcPath: ${tbcPath}
-> tcfPath: ${tcfPath}
-> forceConfigurationReload: ${forceConfigurationReload}
-> constants: ${constants.each { it }}
-> loadConfig: ${loadConfig}
""".stripIndent().trim()
}

Expand All @@ -92,6 +109,7 @@ class TestConfig extends AbstractDescribableImpl<TestConfig> implements Expandab
TestConfig expConfig = new TestConfig()
expConfig.setTbcPath(envVars.expand(tbcPath))
expConfig.setTcfPath(envVars.expand(tcfPath))
expConfig.setForceConfigurationReload(forceConfigurationReload)
expConfig.setConstants(constants.collect { constant -> constant.expand(envVars) })
return expConfig
}
Expand All @@ -108,12 +126,31 @@ class TestConfig extends AbstractDescribableImpl<TestConfig> implements Expandab
'TestConfig'
}

/**
* Creates a new instance of {@link TestConfig} from form data.
* If loadConfig is present and false, removes tbcPath and tcfPath from the form data
* before creating the instance to ensure they are null in the resulting configuration.
*/
@Override
TestConfig newInstance(StaplerRequest req, JSONObject formData) throws FormException {
def processedFormData = processFormData(formData)
return (TestConfig) super.newInstance(req, processedFormData);
}

FormValidation doCheckTbcPath(@QueryParameter String value) {
return ValidationUtil.validateConfigFile(value, '.tbc')
return ValidationUtil.validateFileExtension(value, '.tbc')
}

FormValidation doCheckTcfPath(@QueryParameter final String value) {
return ValidationUtil.validateConfigFile(value, '.tcf')
return ValidationUtil.validateFileExtension(value, '.tcf')
}

protected static JSONObject processFormData(JSONObject formData) {
if (formData.containsKey("loadConfig") && !formData.getBoolean("loadConfig")) {
formData.remove("tbcPath")
formData.remove("tcfPath")
}
return formData
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -159,7 +159,11 @@ class RunPackageStep extends RunTestStep {
* @return the form validation
*/
FormValidation doCheckTestCasePath(@QueryParameter String value) {
return ValidationUtil.validateParameterizedValue(value, true)
FormValidation valid = ValidationUtil.validateParameterizedValue(value, true)
if (valid == FormValidation.ok()) {
return ValidationUtil.validateFileExtension(value, '.pkg')
}
return valid
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -121,7 +121,11 @@ class RunProjectStep extends RunTestStep {
* @return the form validation
*/
FormValidation doCheckTestCasePath(@QueryParameter String value) {
return ValidationUtil.validateParameterizedValue(value, true)
FormValidation valid = ValidationUtil.validateParameterizedValue(value, true)
if (valid == FormValidation.ok()) {
return ValidationUtil.validateFileExtension(value, '.prj')
}
return valid
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -40,15 +40,19 @@ class LogConfigUtil implements Serializable {
}

private void logTestConfig() {
if (testConfig.tbcPath) {
listener.logger.println("-> With TBC=${testConfig.tbcPath}")
}
if (testConfig.tcfPath) {
listener.logger.println("-> With TCF=${testConfig.tcfPath}")
if (!testConfig.loadConfig) {
return
}

listener.logger.println("-> With TBC='${testConfig.tbcPath}'")
listener.logger.println("-> With TCF='${testConfig.tcfPath}'")

if (testConfig.constants) {
listener.logger.println("-> With global constants=[${testConfig.constants.each { it.toString() }}]")
}
if (testConfig.forceConfigurationReload) {
listener.logger.println("-> With ForceConfigurationReload=${testConfig.forceConfigurationReload}")
}
}

private void logPackageConfig() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -54,12 +54,12 @@ class ValidationUtil {
return returnValue
}

static validateConfigFile(String configFilePath, String fileExtension) {
static validateFileExtension(String configFilePath, String fileExtension) {
FormValidation returnValue = validateParameterizedValue(configFilePath, false)
if (returnValue == FormValidation.ok() && !StringUtils.isEmpty(configFilePath)) {
if (!configFilePath.endsWith(fileExtension) && (configFilePath != 'KEEP')) {
if (!configFilePath.endsWith(fileExtension)) {
returnValue = FormValidation.error(
"${configFilePath} has to be empty, either of file type ${fileExtension} or \"KEEP\".")
"${configFilePath} has to be of file type '${fileExtension}'")
}
}
return returnValue
Expand Down
Original file line number Diff line number Diff line change
@@ -1,20 +1,28 @@
<?jelly escape-by-default='true'?>
<j:jelly xmlns:j="jelly:core" xmlns:f="/lib/form">
<f:section title="${%configSection.title}">
<f:entry title="${%tbcPath.title}" description="${%tbcPath.description}" field="tbcPath">
<f:textbox/>
</f:entry>
<f:entry title="${%tcfPath.title}" description="${%tcfPath.description}" field="tcfPath">
<f:textbox/>
</f:entry>
<f:entry title="${%forceConfigurationReload.title}" description="${%forceConfigurationReload.description}"
field="forceConfigurationReload">
<f:checkbox/>
</f:entry>
<f:advanced title="${%constants.title}">
<f:entry title="${%constants.title}" description="${%constants.description}" field="constants">
<f:repeatableProperty field="constants" add="${%constants.add}" minimum="0"/>
<f:optionalBlock name="loadConfig" title="${%configOption.loadConfig}"
help="/plugin/ecu-test-execution/help/TestConfig/loadConfig.html" inline="true"
checked="${instance.loadConfig}"
>
<j:set var="tbcValue" value="${it.loadConfig ? instance.tbcPath : null}"/>
<j:set var="tcfValue" value="${it.loadConfig ? instance.tcfPath : null}"/>

<f:description>${%loadConfig.description}</f:description>
<f:entry title="${%tbcPath.title}" description="${%tbcPath.description}" field="tbcPath">
<f:textbox value="${tbcValue}"/>
</f:entry>
</f:advanced>
<f:entry title="${%tcfPath.title}" description="${%tcfPath.description}" field="tcfPath">
<f:textbox value="${tcfValue}"/>
</f:entry>
<f:advanced title="${%constants.title}">
<f:entry title="${%constants.title}" description="${%constants.description}" field="constants">
<f:repeatableProperty field="constants" add="${%constants.add}" minimum="0"/>
</f:entry>
</f:advanced>
<f:entry title="${%forceConfigurationReload.title}" description="${%forceConfigurationReload.description}" field="forceConfigurationReload">
<f:checkbox />
</f:entry>
</f:optionalBlock>
</f:section>
</j:jelly>
Original file line number Diff line number Diff line change
@@ -1,17 +1,20 @@
configSection.title=Test Configuration
configOption.loadConfig=Load Configuration
loadConfig.description=The TestConfiguration and/or the TestBenchConfiguration files must be explicitly set whenever\
a new configuration is needed. If both are empty, Test Configuration will be unloaded. \
Setting <tt>forceConfigurationReload</tt> to <tt>true</tt> forces a configuration reload,\
even if the same configuration is still active.
constants.add=Add Global Constant
constants.description=The configured global constants remain available throughout the entire test execution.
constants.title=Global Constants
forceConfigurationReload.description=If true, always reload the configuration even if the same one is still active. Hint: This flag is only required for ecu.test versions less than 2023.4!
forceConfigurationReload.description=If true, always reload the configuration even if the same one is still active.
forceConfigurationReload.title=Reload Configuration
packageParameters.add=Add Package Parameter
packageParameters.description=Parameters for package execution.
packageParameters.title=Package Parameters
tbcPath.description=The relative path of the .tbc file in the Configurations directory to be started for this execution. \
Use "KEEP" to use the currently loaded test bench configuration. \
If empty, no test bench configuration will be loaded.
tbcPath.title=Test Bench Configuration
tcfPath.description=The relative path of the .tcf file in the Configurations directory to be started for this execution. \
Use "KEEP" to use the currently loaded test configuration. \
If empty, no test configuration will be loaded.
tcfPath.title=Test Configuration
Loading
Loading