@Grab('org.yaml:snakeyaml:1.17') import org.yaml.snakeyaml.Yaml class TestRunnerThread extends Thread { static def workspacesRootDir static def libraryVersionUnderTest static def repositoryUnderTest Process currentProcess final StringBuilder stdOut = new StringBuilder() final StringBuilder stdErr = new StringBuilder() int lastPrintedStdOutLine = -1 public def returnCode = -1 public def lastCommand def area def testCase def uniqueName def testCaseRootDir def testCaseWorkspace def testCaseConfig TestRunnerThread(File testCaseFile) { this.testCaseConfig = new Yaml().load(testCaseFile.text) if (!System.getenv(testCaseConfig.deployCredentialEnv.username) || !System.getenv(testCaseConfig.deployCredentialEnv.password)) { throw new RuntimeException("Environment variables '${testCaseConfig.deployCredentialEnv.username}' and '${testCaseConfig.deployCredentialEnv.password}' need to be set.") } // Regex pattern expects a folder structure such as '/rootDir/areaDir/testCase.extension' def testCaseMatches = (testCaseFile.toString() =~ /^[\w\-]+\\/([\w\-]+)\\/([\w\-]+)\..*\u0024/) this.area = testCaseMatches[0][1] this.testCase = testCaseMatches[0][2] if (!area || !testCase) { throw new RuntimeException("Expecting file structure '/rootDir/areaDir/testCase.yml' " + "but got '${testCaseFile}'.") } this.uniqueName = "${area}|${testCase}" this.testCaseRootDir = new File("${workspacesRootDir}/${area}/${testCase}") this.testCaseWorkspace = "${testCaseRootDir}/workspace" } void run() { println "[INFO] Test case '${uniqueName}' launched." if (testCaseRootDir.exists() || !testCaseRootDir.mkdirs()) { throw new RuntimeException("Creation of dir '${testCaseRootDir}' failed.") } executeShell("git clone -b ${testCaseConfig.referenceAppRepo.branch} " + "${testCaseConfig.referenceAppRepo.url} ${testCaseWorkspace}") addJenkinsYmlToWorkspace() setLibraryVersionInJenkinsfile() //Commit the changed version because artifactSetVersion expects the git repo not to be dirty executeShell(["git", "-C", "${testCaseWorkspace}", "commit", "--all", '--author="piper-testing-bot "', '--message="Set piper lib version for test"']) executeShell("docker run --rm -v /var/run/docker.sock:/var/run/docker.sock " + "-v ${System.getenv('PWD')}/${testCaseWorkspace}:/workspace -v /tmp " + "-e CASC_JENKINS_CONFIG=/workspace/jenkins.yml " + "-e ${testCaseConfig.deployCredentialEnv.username} " + "-e ${testCaseConfig.deployCredentialEnv.password} " + "-e BRANCH_NAME=${testCaseConfig.referenceAppRepo.branch} ppiper/jenkinsfile-runner") println "*****[INFO] Test case '${uniqueName}' finished successfully.*****" printOutput() } // Configure path to library-repository under test in Jenkins config private void addJenkinsYmlToWorkspace() { def sourceFile = 'jenkins.yml' def sourceText = new File(sourceFile).text.replaceAll( '__REPO_SLUG__', repositoryUnderTest) def target = new File("${testCaseWorkspace}/${sourceFile}") target.write(sourceText) } // Force usage of library version under test by setting it in the Jenkinsfile, // which is then the first definition and thus has the highest precedence. private void setLibraryVersionInJenkinsfile() { def jenkinsfile = new File("${testCaseWorkspace}/Jenkinsfile") def manipulatedText = "@Library(\"piper-library-os@${libraryVersionUnderTest}\") _\n" + jenkinsfile.text jenkinsfile.write(manipulatedText) } private void executeShell(command) { lastCommand = command def startOfCommandString = "Shell command: '${command}'\n" stdOut << startOfCommandString stdErr << startOfCommandString currentProcess = command.execute() currentProcess.waitForProcessOutput(stdOut, stdErr) returnCode = currentProcess.exitValue() currentProcess = null if (returnCode > 0) { throw new ReturnCodeNotZeroException("Test case: [${uniqueName}]; " + "shell command '${command} exited with return code '${returnCode}") } } void printOutput() { println "\n[INFO] stdout output from test case ${uniqueName}:" stdOut.eachLine { line, i -> println "${i} [${uniqueName}] ${line}" lastPrintedStdOutLine = i } println "\n[INFO] stderr output from test case ${uniqueName}:" stdErr.eachLine { line, i -> println "${i} [${uniqueName}] ${line}" } } public void printRunningStdOut() { stdOut.eachLine { line, i -> if (i > lastPrintedStdOutLine) { println "${i} [${uniqueName}] ${line}" lastPrintedStdOutLine = i } } } @Override public String toString() { return uniqueName } } class ReturnCodeNotZeroException extends Exception { ReturnCodeNotZeroException(message) { super(message) } }