Skip to content

Commit

Permalink
check for old report folders (#143)
Browse files Browse the repository at this point in the history
  • Loading branch information
MxEh-TT committed Jul 25, 2024
1 parent f983eb1 commit 81bbc2c
Show file tree
Hide file tree
Showing 8 changed files with 181 additions and 81 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -73,11 +73,23 @@ interface RestApiClient {
*/
abstract UploadResult uploadReport(String reportId, TGUploadOrder order)

/**
* Get de.tracetronic.jenkins.plugins.ecutestexecution.clients.model.ReportInfo
* of all available test reports in the ecu.test instance.
* @return List of ReportInfo with report IDs
*/
abstract List<ReportInfo> getAllReports()

/**
* Get the IDs of all available test reports in the ecu.test instance.
* @return List of strings with report IDs
*/
abstract List<String> getAllReportIds()


/**
* Download the report folder of the given reportId from ecu.test
* Only available in api v2
* @return File
*/
abstract File downloadReportFolder(String reportID)
}
Original file line number Diff line number Diff line change
Expand Up @@ -206,17 +206,30 @@ class RestApiClientV1 implements RestApiClient {
"Report upload for ${reportId} failed", '')
}

/**
* Get de.tracetronic.jenkins.plugins.ecutestexecution.clients.model.ReportInfo
* of all available test reports in the ecu.test instance.
* @return List of ReportInfo with report IDs
*/
List<ReportInfo> getAllReports() {
de.tracetronic.cxs.generated.et.client.api.v2.ReportApi apiInstance = new de.tracetronic.cxs.generated.et.client.api.v2.ReportApi(apiClient)
return apiInstance.getAllReports()
}

/**
* Get the IDs of all available test reports in the ecu.test instance.
* @return List of strings with report IDs
*/
List<String> getAllReportIds() {
ReportApi apiInstance = new ReportApi(apiClient)
List<de.tracetronic.cxs.generated.et.client.model.v1.ReportInfo> reports = apiInstance.getAllReports()
return reports*.testReportId
return getAllReports()*.testReportId
}

/**
* Download the report folder of the given reportId from ecu.test
* Only available in api v2
* @return File
*/
File downloadReportFolder(String reportID) {
throw new Exception("Downloading report folders is not supported by api v1")
throw new de.tracetronic.cxs.generated.et.client.v1.ApiException("Downloading report folders is not supported by api v1. Use ecu.test > 2024.2 with api v2 instead.")
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -229,16 +229,28 @@ class RestApiClientV2 extends RestApiClientV2WithIdleHandle implements RestApiCl
"Report upload for ${reportId} failed", '')
}

/**
* Get de.tracetronic.jenkins.plugins.ecutestexecution.clients.model.ReportInfo
* of all available test reports in the ecu.test instance.
* @return List of ReportInfo with report IDs
*/
List<ReportInfo> getAllReports() {
ReportApi apiInstance = new ReportApi(apiClient)
return apiInstance.getAllReports()
}

/**
* Get the IDs of all available test reports in the ecu.test instance.
* @return List of strings with report IDs
*/
List<String> getAllReportIds() {
ReportApi apiInstance = new ReportApi(apiClient)
List<de.tracetronic.cxs.generated.et.client.model.v2.ReportInfo> reports = apiInstance.getAllReports()
return reports*.testReportId
return getAllReports()*.testReportId
}

/**
* Download the report folder of the given reportId from ecu.test
* @return File
*/
File downloadReportFolder(String reportID) {
ReportApi apiInstance = new ReportApi(apiClient)
return apiInstance.reportDownload(reportID)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,20 +9,19 @@ package de.tracetronic.jenkins.plugins.ecutestexecution.steps
import com.google.common.collect.ImmutableSet
import de.tracetronic.jenkins.plugins.ecutestexecution.clients.RestApiClient
import de.tracetronic.jenkins.plugins.ecutestexecution.clients.RestApiClientFactory
import de.tracetronic.jenkins.plugins.ecutestexecution.model.GenerationResult
import de.tracetronic.jenkins.plugins.ecutestexecution.actions.ProvideReportLogsAction
import de.tracetronic.jenkins.plugins.ecutestexecution.clients.model.ApiException
import de.tracetronic.jenkins.plugins.ecutestexecution.clients.model.ReportInfo
import de.tracetronic.jenkins.plugins.ecutestexecution.util.PathUtil

import hudson.EnvVars
import hudson.Extension
import hudson.FilePath
import hudson.Launcher
import hudson.model.Executor
import hudson.model.Run
import hudson.model.TaskListener
import hudson.util.ListBoxModel
import io.jenkins.cli.shaded.org.apache.commons.io.FileUtils
import jenkins.model.StandardArtifactManager
import jenkins.model.Jenkins
import jenkins.security.MasterToSlaveCallable
import org.apache.commons.lang.StringUtils
import org.jenkinsci.plugins.workflow.steps.Step
Expand All @@ -31,8 +30,8 @@ import org.jenkinsci.plugins.workflow.steps.StepDescriptor
import org.jenkinsci.plugins.workflow.steps.StepExecution
import org.jenkinsci.plugins.workflow.steps.SynchronousNonBlockingStepExecution
import org.kohsuke.stapler.DataBoundConstructor
import org.kohsuke.stapler.DataBoundSetter

import java.text.SimpleDateFormat
import java.util.zip.ZipEntry
import java.util.zip.ZipInputStream

Expand Down Expand Up @@ -78,10 +77,9 @@ class ProvideReportLogsStep extends Step {
TaskListener listener = context.get(TaskListener.class)

if (workspace.child(logDirName).list().size() > 0) {
listener.logger.println("[WARNING] workspace report folder includes old files")
listener.logger.println("[WARNING] jenkins workspace report folder includes old files")
}

long startTimeMillis = run.getTimeInMillis()
long startTimeMillis = run.getStartTimeInMillis()
def logDirPath = PathUtil.makeAbsoluteInPipelineHome("${logDirName}", context)

// Download report logs to workspace
Expand All @@ -106,7 +104,7 @@ class ProvideReportLogsStep extends Step {
run.addAction(new ProvideReportLogsAction(run))

}
listener.logger.println("Cleaning report folder in workspace")
listener.logger.println("Cleaning report folder in jenkins workspace")
workspace.child(logDirName).deleteContents()
listener.logger.flush()
}
Expand Down Expand Up @@ -141,40 +139,73 @@ class ProvideReportLogsStep extends Step {
listener.logger.println("Providing ecu.test report logs to jenkins.")
List<String> logs = []
RestApiClient apiClient = RestApiClientFactory.getRestApiClient(envVars.get('ET_API_HOSTNAME'), envVars.get('ET_API_PORT'))
reportIds = apiClient.getAllReportIds()
List<ReportInfo> reports = apiClient.getAllReports()

if (reportIds == null || reportIds.isEmpty()) {
if (reports == null || reports.isEmpty()) {
listener.logger.println("[WARNING] No report files returned by ecu.test")
}
reportIds.each { reportId ->
reports*.reportDir.each { String reportDir ->
if (!checkReportFolderCreationDate(reportDir)) {
listener.logger.println("[WARNING] ecu.test report folder includes files older than this run. ${reportDir}")
}
}

reports*.testReportId.each { String reportId ->
listener.logger.println("Downloading reportFolder for ${reportId}")
File reportFolderZip = apiClient.downloadReportFolder(reportId)
def fileNameToExtract = "test/ecu.test_out.log"
ZipInputStream zipInputStream = new ZipInputStream(new FileInputStream(reportFolderZip))
ZipEntry entry

while ((entry = zipInputStream.nextEntry) != null) {
if (entry.name == fileNameToExtract) {
def outputFile = new File("${logDirPath}/${reportId}.log")
outputFile.parentFile.mkdirs()

def outputStream = new FileOutputStream(outputFile)
try {
outputStream << zipInputStream
} finally {
outputStream.close()
}

listener.logger.println("Extracted ${fileNameToExtract} to ${logDirPath}")
logs.add(outputFile.name)
try {
File reportFolderZip = apiClient.downloadReportFolder(reportId)
File log = extractLogFileFromZip(reportFolderZip, "test/ecu.test_out.log", reportId)
logs.add(log.name)
}
catch (ApiException e) {
if (e instanceof de.tracetronic.cxs.generated.et.client.v2.ApiException && e.code == 404) {
throw new de.tracetronic.cxs.generated.et.client.v2.ApiException("[ERROR] Downloading reportFolder is not available for ecu.test < 2024.2!")
} else {
throw e
}
}
zipInputStream.close()
}
listener.logger.flush()
return logs

}

boolean checkReportFolderCreationDate(String reportDir) {
def df = "yyyy-MM-dd_HHmmss"
def pattern = /\d{4}-\d{2}-\d{2}_\d{6}/
def matcher = reportDir =~ pattern
if (matcher) {
def date = matcher[0]
Date dateTime1 = new Date().parse(df, date)
Date dateTime2 = new Date(startTimeMillis)
return dateTime1 > dateTime2
} else {
throw new Exception("No date and time found in the input string.")
}
}

File extractLogFileFromZip(File reportFolderZip, String fileNameToExtract, String newName) {
ZipInputStream zipInputStream = new ZipInputStream(new FileInputStream(reportFolderZip))
ZipEntry entry
while ((entry = zipInputStream.nextEntry) != null) {
newName
if (entry.name == fileNameToExtract) {
File outputFile = new File("${logDirPath}/${newName}.log")
outputFile.parentFile.mkdirs()

def outputStream = new FileOutputStream(outputFile)
try {
outputStream << zipInputStream
} finally {
outputStream.close()
}
listener.logger.println("Extracted ${fileNameToExtract} to ${logDirPath}")
zipInputStream.close()
return outputFile
}
}
throw new Exception("No ecu.test log not found in given report zip!")
}
}

@Extension
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -347,43 +347,4 @@ abstract class ETContainerTest extends ContainerTest {
jenkins.assertLogContains("-> result: SUCCESS", run)
jenkins.assertLogContains("-> reportDir: ${ET_WS_PATH}/TestReports/test_", run)
}

def "Perform provide report logs step with no reports"() {
given: "a test execution pipeline"
String script = """
node {
withEnv(['ET_API_HOSTNAME=${etContainer.host}', 'ET_API_PORT=${etContainer.getMappedPort(ET_PORT)}']) {
ttProvideReportLogs()
}
}
""".stripIndent()
WorkflowJob job = jenkins.createProject(WorkflowJob.class, "pipeline")
job.setDefinition(new CpsFlowDefinition(script, true))
when: "scheduling a new build"
WorkflowRun run = jenkins.buildAndAssertStatus(Result.SUCCESS, job)

then: "expect successful test completion"
jenkins.assertLogContains("Providing ecu.test report logs to jenkins.", run)
jenkins.assertLogContains("[WARNING] No report files returned by ecu.test", run)
}

def "Perform provide report logs step with reports"() {
given: "a test execution pipeline"
String script = """
node {
withEnv(['ET_API_HOSTNAME=${etContainer.host}', 'ET_API_PORT=${etContainer.getMappedPort(ET_PORT)}']) {
ttRunPackage testCasePath: 'test.pkg'
ttProvideReportLogs()
}
}
""".stripIndent()
WorkflowJob job = jenkins.createProject(WorkflowJob.class, "pipeline")
job.setDefinition(new CpsFlowDefinition(script, true))
when: "scheduling a new build"
WorkflowRun run = jenkins.buildAndAssertStatus(Result.SUCCESS, job)

then: "expect successful test completion"
jenkins.assertLogContains("Providing ecu.test report logs to jenkins.", run)
jenkins.assertLogContains("Adding report logs to artifacts", run)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,10 @@
*/
package de.tracetronic.jenkins.plugins.ecutestexecution


import hudson.model.Result
import org.jenkinsci.plugins.workflow.cps.CpsFlowDefinition
import org.jenkinsci.plugins.workflow.job.WorkflowJob
import org.jenkinsci.plugins.workflow.job.WorkflowRun
import org.slf4j.Logger
import org.slf4j.LoggerFactory
import org.testcontainers.containers.BindMode
Expand Down Expand Up @@ -35,4 +38,24 @@ class ETV1ContainerTest extends ETContainerTest {
.withLogConsumer(new Slf4jLogConsumer(LOGGER))
.waitingFor(Wait.forHttp("/api/v1/live"))
}

def "Perform provide report logs step with reports"() {
given: "a test execution pipeline"
String script = """
node {
withEnv(['ET_API_HOSTNAME=${etContainer.host}', 'ET_API_PORT=${etContainer.getMappedPort(ET_PORT)}']) {
ttRunPackage testCasePath: 'test.pkg'
ttProvideReportLogs()
}
}
""".stripIndent()
WorkflowJob job = jenkins.createProject(WorkflowJob.class, "pipeline")
job.setDefinition(new CpsFlowDefinition(script, true))
when: "scheduling a new build"
WorkflowRun run = jenkins.buildAndAssertStatus(Result.FAILURE, job)

then: "expect successful test completion"
jenkins.assertLogContains("Providing ecu.test report logs to jenkins.", run)
jenkins.assertLogContains("Adding report logs to artifacts", run)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,10 @@
*/
package de.tracetronic.jenkins.plugins.ecutestexecution


import hudson.model.Result
import org.jenkinsci.plugins.workflow.cps.CpsFlowDefinition
import org.jenkinsci.plugins.workflow.job.WorkflowJob
import org.jenkinsci.plugins.workflow.job.WorkflowRun
import org.slf4j.Logger
import org.slf4j.LoggerFactory
import org.testcontainers.containers.BindMode
Expand Down Expand Up @@ -35,4 +38,43 @@ class ETV2ContainerTest extends ETContainerTest {
.withLogConsumer(new Slf4jLogConsumer(LOGGER))
.waitingFor(Wait.forHttp("/api/v2/live"))
}

def "Perform provide report logs step with no reports"() {
given: "a test execution pipeline"
String script = """
node {
withEnv(['ET_API_HOSTNAME=${etContainer.host}', 'ET_API_PORT=${etContainer.getMappedPort(ET_PORT)}']) {
ttProvideReportLogs()
}
}
""".stripIndent()
WorkflowJob job = jenkins.createProject(WorkflowJob.class, "pipeline")
job.setDefinition(new CpsFlowDefinition(script, true))
when: "scheduling a new build"
WorkflowRun run = jenkins.buildAndAssertStatus(Result.SUCCESS, job)

then: "expect successful test completion"
jenkins.assertLogContains("Providing ecu.test report logs to jenkins.", run)
jenkins.assertLogContains("[WARNING] No report files returned by ecu.test", run)
}

def "Perform provide report logs step with reports"() {
given: "a test execution pipeline"
String script = """
node {
withEnv(['ET_API_HOSTNAME=${etContainer.host}', 'ET_API_PORT=${etContainer.getMappedPort(ET_PORT)}']) {
ttRunPackage testCasePath: 'test.pkg'
ttProvideReportLogs()
}
}
""".stripIndent()
WorkflowJob job = jenkins.createProject(WorkflowJob.class, "pipeline")
job.setDefinition(new CpsFlowDefinition(script, true))
when: "scheduling a new build"
WorkflowRun run = jenkins.buildAndAssertStatus(Result.SUCCESS, job)

then: "expect successful test completion"
jenkins.assertLogContains("Providing ecu.test report logs to jenkins.", run)
jenkins.assertLogContains("Adding report logs to artifacts", run)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -47,10 +47,16 @@ class MockRestApiClient implements RestApiClient {
return null
}

@Override
List<ReportInfo> getAllReports(){
return null
}

@Override
List<String> getAllReportIds() {
return null
}

@Override
File downloadReportFolder(String reportID){
return null
Expand Down

0 comments on commit 81bbc2c

Please sign in to comment.