Skip to content

Commit

Permalink
[JENKINS-37781] More tests
Browse files Browse the repository at this point in the history
Fixed some issues those tests revealed.
Also fixed a bug where the last line of a script or when closure wasn't parsed
  • Loading branch information
rsandell committed Oct 26, 2016
1 parent 9676206 commit 819bbdf
Show file tree
Hide file tree
Showing 8 changed files with 205 additions and 7 deletions.
Expand Up @@ -25,6 +25,7 @@

package org.jenkinsci.plugins.pipeline.modeldefinition.ast;

import net.sf.json.JSONObject;
import org.jenkinsci.plugins.pipeline.modeldefinition.validator.ModelValidator;

/**
Expand All @@ -43,6 +44,15 @@ public String toString() {
"}";
}

@Override
public JSONObject toJSON() {
JSONObject o = new JSONObject();
if (getArgs() != null) {
o.accumulate("arguments", getArgs().toJSON());
}
return o;
}

@Override
public void validate(ModelValidator validator) {
super.validate(validator);
Expand Down
Expand Up @@ -907,16 +907,34 @@ class ModelParser {
*/
protected String getSourceText(ASTNode n) {
def result = new StringBuilder();
for (int x = n.getLineNumber(); x <= n.getLastLineNumber(); x++) {
int beginLine = n.getLineNumber()
int endLine = n.getLastLineNumber()
int beginLineColumn = n.getColumnNumber()
int endLineLastColumn = n.getLastColumnNumber()

//The node seems to be lying about the last line, so go through each statement to try to make sure
if (n instanceof BlockStatement) {
for (Statement s : n.statements) {
if (s.lineNumber < beginLine) {
beginLine = s.lineNumber
beginLineColumn = s.columnNumber
}
if (s.lastLineNumber > endLine) {
endLine = s.lastLineNumber
endLineLastColumn = s.lastColumnNumber
}
}
}
for (int x = beginLine; x <= endLine; x++) {
String line = sourceUnit.source.getLine(x, null);
if (line == null)
throw new AssertionError("Unable to get source line"+x);

if (x == n.getLastLineNumber()) {
line = line.substring(0, n.getLastColumnNumber() - 1);
if (x == endLine) {
line = line.substring(0, endLineLastColumn - 1);
}
if (x == n.getLineNumber()) {
line = line.substring(n.getColumnNumber() - 1);
if (x == beginLine) {
line = line.substring(beginLineColumn - 1);
}
result.append(line).append('\n');
}
Expand Down
Expand Up @@ -194,6 +194,11 @@ class ModelValidatorImpl implements ModelValidator {

public boolean validateElement(ModelASTWhen when) {
//TODO can we evaluate if the closure will return something that can be tries for true?
if (when.toGroovy() =~ /when\s[{\s}]*/) {
errorCollector.error(when, "Empty when closure, remove the property or add some content.")
return false
}
return true
}


Expand Down
16 changes: 16 additions & 0 deletions pipeline-model-definition/src/main/resources/ast-schema.json
Expand Up @@ -275,6 +275,19 @@
}
]
},
"when": {
"description": "Closure to evaluate if the stage should run or not",
"type": "object",
"properties": {
"arguments": {
"$ref": "#/definitions/rawArgument"
}

},

"additionalProperties": false

},
"notifications": {
"description": "An array of build conditions with blocks of steps to run if those conditions are satisfied at the end of the build",
"type": "object",
Expand Down Expand Up @@ -319,6 +332,9 @@
"agent": {
"$ref": "#/definitions/agent"
},
"when": {
"$ref": "#/definitions/when"
},
"post": {
"$ref": "#/definitions/notifications"
},
Expand Down
Expand Up @@ -25,11 +25,24 @@

package org.jenkinsci.plugins.pipeline.modeldefinition;

import com.gargoylesoftware.htmlunit.HttpMethod;
import com.gargoylesoftware.htmlunit.WebRequest;
import com.gargoylesoftware.htmlunit.util.NameValuePair;
import hudson.model.Result;
import hudson.model.Slave;
import net.sf.json.JSONObject;
import org.jenkinsci.plugins.pipeline.modeldefinition.endpoints.ModelConverterAction;
import org.jenkinsci.plugins.pipeline.modeldefinition.model.Stage;
import org.junit.BeforeClass;
import org.junit.Test;
import org.jvnet.hudson.test.JenkinsRule;

import java.io.IOException;
import java.util.Collections;

import static org.jenkinsci.plugins.pipeline.modeldefinition.util.IsJsonObjectContaining.hasEntry;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertThat;


/**
Expand All @@ -48,9 +61,54 @@ public static void setUpAgent() throws Exception {
@Test
public void simpleWhen() throws Exception {
env(s).put("SECOND_STAGE", "NOPE").set();
ExpectationsBuilder expect = expect("simpleWhen");
ExpectationsBuilder expect = expect("when", "simpleWhen").runFromRepo(false);
expect.logContains("One", "Hello", "Should I run?").logNotContains("Two", "World").go();
env(s).put("SECOND_STAGE", "RUN").set();
expect.resetForNewRun(Result.SUCCESS).logContains("One", "Hello", "Should I run?", "Two", "World").go();
}

@Test
public void whenException() throws Exception {
env(s).put("SECOND_STAGE", "NOPE").set();
expect(Result.FAILURE, "when", "whenException").runFromRepo(false)
.logContains("One", "Hello", "Should I run?", "NullPointerException")
.logNotContains("Two", "World").go();
}

@Test
public void whenEmpty() throws Exception {
env(s).put("SECOND_STAGE", "NOPE").set();
expect(Result.FAILURE, "when", "whenEmpty").runFromRepo(false)
.logContains("Empty when closure").logNotContains("Two", "World").go();
}

@Test
public void toJson() throws IOException {
final String rawJenkinsfile = fileContentsFromResources("when/simpleWhen.groovy", true);
JenkinsRule.WebClient wc = j.createWebClient();
WebRequest req = new WebRequest(wc.createCrumbedUrl(ModelConverterAction.PIPELINE_CONVERTER_URL + "/toJson"), HttpMethod.POST);

assertNotNull(rawJenkinsfile);

NameValuePair pair = new NameValuePair("jenkinsfile", rawJenkinsfile);
req.setRequestParameters(Collections.singletonList(pair));

String rawResult = wc.getPage(req).getWebResponse().getContentAsString();
assertNotNull(rawResult);

JSONObject result = JSONObject.fromObject(rawResult);
assertNotNull(result);
assertThat(result, hasEntry("status", "ok"));
assertThat(result, hasEntry("data", hasEntry("result", "success")));

req = new WebRequest(wc.createCrumbedUrl(ModelConverterAction.PIPELINE_CONVERTER_URL + "/toJenkinsfile"), HttpMethod.POST);
pair = new NameValuePair("json", result.getJSONObject("data").getJSONObject("json").toString());
req.setRequestParameters(Collections.singletonList(pair));

rawResult = wc.getPage(req).getWebResponse().getContentAsString();
assertNotNull(rawResult);
result = JSONObject.fromObject(rawResult);
assertThat(result, hasEntry("status", "ok"));
assertThat(result, hasEntry("data", hasEntry("result", "success")));
}
}
Expand Up @@ -37,7 +37,11 @@ pipeline {
return env.SECOND_STAGE == "RUN"
}
steps {
echo "World"
script {
echo "World"
echo "Heal it"
}

}
}
}
Expand Down
43 changes: 43 additions & 0 deletions pipeline-model-definition/src/test/resources/when/whenEmpty.groovy
@@ -0,0 +1,43 @@
/*
* The MIT License
*
* Copyright (c) 2016, CloudBees, Inc.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
*/

pipeline {
agent label: "here"
stages {
stage("One") {
steps {
echo "Hello"
}
}
stage("Two") {
when {

}
steps {
echo "World"
}
}
}
}
@@ -0,0 +1,44 @@
/*
* The MIT License
*
* Copyright (c) 2016, CloudBees, Inc.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
*/

pipeline {
agent label: "here"
stages {
stage("One") {
steps {
echo "Hello"
}
}
stage("Two") {
when {
echo "Should I run?"
throw new NullPointerException("Gah")
}
steps {
echo "World"
}
}
}
}

0 comments on commit 819bbdf

Please sign in to comment.