1
0
mirror of https://github.com/SAP/jenkins-library.git synced 2024-12-14 11:03:09 +02:00
sap-jenkins-library/consumer-test/consumerTestController.groovy
2019-07-23 14:54:38 +02:00

229 lines
7.0 KiB
Groovy

import groovy.io.FileType
import static groovy.json.JsonOutput.toJson
COMMIT_HASH = null
RUNNING_LOCALLY = false
AUXILIARY_SLEEP_MS = 10000
START_TIME_MS = System.currentTimeMillis()
WORKSPACES_ROOT = 'workspaces'
TEST_CASES_DIR = 'testCases'
LIBRARY_VERSION_UNDER_TEST = "git log --format=%H -n 1".execute().text.trim()
EXCLUDED_FROM_CONSUMER_TESTING_REGEXES = [
/^documentation\/.*$/,
/^.travis.yml$/,
/^test\/.*$/
]
newEmptyDir(WORKSPACES_ROOT)
TestRunnerThread.workspacesRootDir = WORKSPACES_ROOT
TestRunnerThread.libraryVersionUnderTest = LIBRARY_VERSION_UNDER_TEST
TestRunnerThread.repositoryUnderTest = System.getenv('TRAVIS_REPO_SLUG') ?: 'SAP/jenkins-library'
def testCaseThreads
def cli = new CliBuilder(
usage: 'groovy consumerTestController.groovy [<options>]',
header: 'Options:',
footer: 'If no options are set, all tests are run centrally, i.e. on travisCI.')
cli.with {
h longOpt: 'help', 'Print this help text and exit.'
l longOpt: 'run-locally', 'Run consumer tests locally in Docker, i.e. skip reporting of GitHub status.'
s longOpt: 'single-test', args: 1, argName: 'filePath', 'Run single test.'
}
def options = cli.parse(args)
if (options.h) {
cli.usage()
return
}
if (options.l) {
RUNNING_LOCALLY = true
}
if (!RUNNING_LOCALLY) {
/*
In case the build is performed for a pull request TRAVIS_COMMIT is a merge
commit between the base branch and the PR branch HEAD. That commit is actually built.
But for notifying about a build status we need the commit which is currently
the HEAD of the PR branch.
In case the build is performed for a simple branch (not associated with a PR)
In this case there is no merge commit between any base branch and HEAD of a PR branch.
The commit which we need for notifying about a build status is in this case simply
TRAVIS_COMMIT itself.
*/
COMMIT_HASH = System.getenv('TRAVIS_PULL_REQUEST_SHA') ?: System.getenv('TRAVIS_COMMIT')
if (changeDoesNotNeedConsumerTesting()) {
println 'No consumer tests necessary.'
notifyGithub("success", "No consumer tests necessary.")
return
} else {
notifyGithub("pending", "Consumer tests are in progress.")
}
}
if (options.s) {
def file = new File(options.s)
if (!file.exists()) {
exitPrematurely("Test case configuration file '${file}' does not exist. " +
"Please provide path to a configuration file of structure '/rootDir/areaDir/testCase.yml'.")
}
testCaseThreads = [new TestRunnerThread(file)]
} else {
testCaseThreads = listTestCaseThreads()
}
testCaseThreads.each { it ->
it.start()
}
//The thread below will print to console while the test cases are running.
//Otherwise the job would be canceled after 10 minutes without output.
def done = false
Thread.start {
def outputWasPrintedPrematurely = false
def singleTestCase = (testCaseThreads.size() == 1)
if (singleTestCase) {
AUXILIARY_SLEEP_MS = 1000 //for a single test case we print the running output every second
}
for (; ;) {
if (singleTestCase) {
testCaseThreads[0].printRunningStdOut()
} else {
println "[INFO] Consumer tests are still running."
}
// Build is killed at 50 min, print log to console at minute 45
int MINUTES_SINCE_START = (System.currentTimeMillis() - START_TIME_MS) / (1000 * 60)
if (!singleTestCase && MINUTES_SINCE_START > 44 && !outputWasPrintedPrematurely) {
testCaseThreads.each { thread ->
thread.printOutput()
}
outputWasPrintedPrematurely = true
}
sleep(AUXILIARY_SLEEP_MS)
if (done) {
break
}
}
}
testCaseThreads.each { it ->
it.join()
}
done = true
def failedThreads = testCaseThreads.findAll { thread ->
thread.returnCode != 0
}
def status
def statusMessage
if (failedThreads.size() == 0) {
status = "success"
statusMessage = "All consumer tests finished successfully. Congratulations!"
} else {
failedThreads.each { failedThread ->
println "[ERROR] ${failedThread.uniqueName}: Process execution of command: '${failedThread.lastCommand}' failed. " +
"Return code: ${failedThread.returnCode}."
failedThread.printOutput()
}
status = "failure"
statusMessage = "The following consumer test(s) failed: ${failedThreads}"
}
if (!RUNNING_LOCALLY) {
notifyGithub(status, statusMessage)
}
println statusMessage
if (status == "failure") {
System.exit(1)
}
def listTestCaseThreads() {
//Each dir that includes a yml file is a test case
def threads = []
new File(TEST_CASES_DIR).traverse(type: FileType.FILES, nameFilter: ~/^.+\.yml\u0024/) { file ->
threads << new TestRunnerThread(file)
}
return threads
}
def notifyGithub(state, description) {
println "[INFO] Notifying about state '${state}' for commit '${COMMIT_HASH}'."
URL url = new URL("https://api.github.com/repos/SAP/jenkins-library/statuses/${COMMIT_HASH}")
HttpURLConnection con = (HttpURLConnection) url.openConnection()
con.setRequestMethod('POST')
con.setRequestProperty("Content-Type", "application/json; utf-8");
con.setRequestProperty('User-Agent', 'groovy-script')
con.setRequestProperty('Authorization', "token ${System.getenv('INTEGRATION_TEST_VOTING_TOKEN')}")
def postBody = [
state : state,
target_url : System.getenv('TRAVIS_BUILD_WEB_URL'),
description: description,
context : "integration-tests"
]
con.setDoOutput(true)
con.getOutputStream().withStream { os ->
os.write(toJson(postBody).getBytes("UTF-8"))
}
int responseCode = con.getResponseCode()
if (responseCode != HttpURLConnection.HTTP_CREATED) {
exitPrematurely("[ERROR] Posting status to github failed. Expected response code " +
"'${HttpURLConnection.HTTP_CREATED}', but got '${responseCode}'. " +
"Response message: '${con.getResponseMessage()}'",
34) // Error code taken from curl: CURLE_HTTP_POST_ERROR
}
}
def changeDoesNotNeedConsumerTesting() {
if (System.getenv('TRAVIS_BRANCH') == 'master') {
return false
}
def excludesRegex = '(' + EXCLUDED_FROM_CONSUMER_TESTING_REGEXES.join('|') + ')'
"git remote add sap https://github.com/SAP/jenkins-library.git".execute().waitFor()
"git fetch sap".execute().waitFor()
def diff = "git diff --name-only sap/master ${LIBRARY_VERSION_UNDER_TEST}".execute().text.trim()
for (def line : diff.readLines()) {
if (!(line ==~ excludesRegex)) {
return false
}
}
return true
}
static def newEmptyDir(String dirName) {
def dir = new File(dirName)
if (dir.exists()) {
if (!dir.deleteDir()) {
exitPrematurely("Deletion of dir '${dirName}' failed.")
}
}
if (!dir.mkdirs()) {
exitPrematurely("Creation of dir '${dirName}' failed.")
}
}
static def exitPrematurely(String message, int returnCode = 1) {
println message
System.exit(returnCode)
}