2019-05-09 10:28:54 +02:00
@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 ) {
2019-07-23 14:54:38 +02:00
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." )
}
2019-05-09 10:28:54 +02:00
// 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." )
}
2019-07-23 14:54:38 +02:00
executeShell ( "git clone -b ${testCaseConfig.referenceAppRepo.branch} " +
"${testCaseConfig.referenceAppRepo.url} ${testCaseWorkspace}" )
2019-05-09 10:28:54 +02:00
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 <piper-testing-bot@example.com>"' ,
'--message="Set piper lib version for test"' ] )
2019-07-23 14:54:38 +02:00
executeShell ( "docker run --rm -v /var/run/docker.sock:/var/run/docker.sock " +
2019-05-09 10:28:54 +02:00
"-v ${System.getenv('PWD')}/${testCaseWorkspace}:/workspace -v /tmp " +
2019-07-23 14:54:38 +02:00
"-e CASC_JENKINS_CONFIG=/workspace/jenkins.yml " +
"-e ${testCaseConfig.deployCredentialEnv.username} " +
"-e ${testCaseConfig.deployCredentialEnv.password} " +
2020-03-04 14:27:27 +02:00
"-e LIBRARY_VERSION_UNDER_TEST=${libraryVersionUnderTest} " +
"-e REPOSITORY_UNDER_TEST=${repositoryUnderTest} " +
2019-07-23 14:54:38 +02:00
"-e BRANCH_NAME=${testCaseConfig.referenceAppRepo.branch} ppiper/jenkinsfile-runner" )
2019-05-09 10:28:54 +02:00
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 =
2020-04-11 12:27:27 +02:00
"@Library(\"piper-lib-os@${libraryVersionUnderTest}\") _\n" +
2019-05-09 10:28:54 +02:00
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 )
}
}