Skip to content
Permalink
Browse files
Merge pull request #8 from helloeve/master
[JENKINS-26340] Pipeline Integration
  • Loading branch information
jtnord committed Sep 15, 2016
2 parents 48dda7f + e122457 commit d87afb14e45b754d023ca76f2d47e5e3e531780e
Show file tree
Hide file tree
Showing 17 changed files with 359 additions and 1,097 deletions.
24 pom.xml
@@ -2,9 +2,9 @@
<!--
~ The MIT License
~
~ Copyright (c) 2014 James Nord
~ Copyright (c) 2014-2016 James Nord
~ 2013, Cisco Systems, Inc., a California corporation
~ 2015 CloudBees, Inc.
~ 2015,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
@@ -78,6 +78,25 @@
<artifactId>junit</artifactId>
<version>1.2-beta-3</version>
</dependency>
<dependency>
<groupId>org.jenkins-ci.plugins.workflow</groupId>
<artifactId>workflow-aggregator</artifactId>
<version>1.13</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.jenkins-ci.plugins.workflow</groupId>
<artifactId>workflow-support</artifactId>
<version>1.13</version>
<type>test-jar</type>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.jenkins-ci.plugins.workflow</groupId>
<artifactId>workflow-step-api</artifactId>
<version>1.14</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.jenkins-ci.plugins</groupId>
<artifactId>matrix-project</artifactId>
@@ -101,6 +120,7 @@
<version>4.11</version>
<scope>test</scope>
</dependency>

<dependency>
<groupId>org.hamcrest</groupId>
<artifactId>hamcrest-library</artifactId>
@@ -27,7 +27,7 @@
import java.util.Collection;

import gherkin.formatter.model.Background;
import hudson.model.AbstractBuild;
import hudson.model.Run;
import hudson.tasks.test.TabulatedResult;
import hudson.tasks.test.TestObject;
import hudson.tasks.test.TestResult;
@@ -44,7 +44,7 @@ public class BackgroundResult extends TestResult {

private ScenarioResult parent;

private transient AbstractBuild<?, ?> owner;
private transient Run<?, ?> owner;

/* Recomputed by a call to {@link CucumberTestResult#tally()} */
// true if this test failed
@@ -58,11 +58,11 @@ public class BackgroundResult extends TestResult {
}

@Override
public AbstractBuild<?, ?> getOwner() {
public Run<?, ?> getRun() {
return owner;
}

void setOwner(AbstractBuild<?, ?> owner) {
void setOwner(Run<?, ?> owner) {
this.owner = owner;
for (StepResult sr : stepResults) {
sr.setOwner(owner);
@@ -29,7 +29,7 @@
import gherkin.formatter.model.Background;
import gherkin.formatter.model.Match;
import gherkin.formatter.model.Result;
import hudson.model.AbstractBuild;
import hudson.model.Run;
import hudson.tasks.test.TabulatedResult;
import hudson.tasks.test.TestObject;
import hudson.tasks.test.TestResult;
@@ -45,7 +45,7 @@ public class BeforeAfterResult extends TestResult {
private Match macth;
private Result result;

private transient AbstractBuild<?, ?> owner;
private transient Run<?, ?> owner;


public BeforeAfterResult(Match match, Result result) {
@@ -96,12 +96,12 @@ public int getSkipCount() {


@Override
public AbstractBuild<?, ?> getOwner() {
public Run<?, ?> getRun() {
return owner;
}


void setOwner(AbstractBuild<?, ?> owner) {
void setOwner(Run<?, ?> owner) {
this.owner = owner;
}

@@ -26,9 +26,10 @@
import gherkin.JSONParser;
import hudson.AbortException;
import hudson.Extension;
import hudson.FilePath;
import hudson.Launcher;
import hudson.model.Run;
import hudson.model.TaskListener;
import hudson.model.AbstractBuild;

import java.io.File;
import java.io.IOException;
@@ -91,12 +92,13 @@ protected CucumberTestResult parse(List<File> reportFiles, TaskListener listener


@Override
public CucumberTestResult parse(final String testResultLocations,
final AbstractBuild build,
public CucumberTestResult parseResult(final String testResultLocations,
final Run<?, ?> build,
final FilePath workspace,
final Launcher launcher,
final TaskListener listener) throws InterruptedException, IOException {
// overridden so we tally and set the owner on the master.
CucumberTestResult result = (CucumberTestResult) super.parse(testResultLocations, build, launcher, listener);
CucumberTestResult result = (CucumberTestResult) super.parseResult(testResultLocations, build, workspace, launcher, listener);
result.tally();
result.setOwner(build);
return result;
@@ -24,7 +24,7 @@
package org.jenkinsci.plugins.cucumber.jsontestsupport;

import gherkin.formatter.model.Tag;
import hudson.model.AbstractBuild;
import hudson.model.Run;
import hudson.tasks.test.MetaTabulatedResult;
import hudson.tasks.test.TestObject;
import hudson.tasks.test.TestResult;
@@ -71,7 +71,7 @@ public class CucumberTestResult extends MetaTabulatedResult {
*/
private transient Map<String, TagResult> tagMap = new HashMap<String, TagResult>();

private transient AbstractBuild<?, ?> owner;
private transient Run<?, ?> owner;

/* Recomputed by a call to {@link CucumberTestResult#tally()} */
private transient int passCount;
@@ -132,11 +132,11 @@ public Collection<ScenarioResult> getFailedTests() {


@Override
public AbstractBuild<?, ?> getOwner() {
public Run<?, ?> getRun() {
return owner;
}

void setOwner(AbstractBuild<?, ?> owner) {
void setOwner(Run<?, ?> owner) {
this.owner = owner;
for (FeatureResult fr : featureResults) {
fr.setOwner(owner);
@@ -25,8 +25,8 @@

import hudson.XmlFile;
import hudson.model.Action;
import hudson.model.BuildListener;
import hudson.model.AbstractBuild;
import hudson.model.Run;
import hudson.model.TaskListener;
import hudson.tasks.junit.TestResult;
import hudson.tasks.test.AbstractTestResultAction;
import hudson.util.HeapSpaceStringConverter;
@@ -76,15 +76,15 @@ public class CucumberTestResultAction extends AbstractTestResultAction<CucumberT



public CucumberTestResultAction(AbstractBuild owner, CucumberTestResult result, BuildListener listener) {
public CucumberTestResultAction(Run<?, ?> owner, CucumberTestResult result, TaskListener listener) {
super(owner);
setResult(result, listener);
}

/**
* Overwrites the {@link CucumberTestResult} by a new data set.
*/
public synchronized void setResult(CucumberTestResult result, BuildListener listener) {
public synchronized void setResult(CucumberTestResult result, TaskListener listener) {

totalCount = result.getTotalCount();
failCount = result.getFailCount();
@@ -102,7 +102,7 @@ public synchronized void setResult(CucumberTestResult result, BuildListener list
}

private XmlFile getDataFile() {
return new XmlFile(XSTREAM,new File(owner.getRootDir(), "cucumberResult.xml"));
return new XmlFile(XSTREAM,new File(run.getRootDir(), "cucumberResult.xml"));
}

/**
@@ -185,4 +185,24 @@ public String getDisplayName() {
public String getUrlName() {
return "cucumberTestReport";
}

/**
* Merge results from other into an existing set of results.
* @param other
* the result to merge with the current results.
* @param listener
*/
synchronized void mergeResult(CucumberTestResult other, TaskListener listener) {
CucumberTestResult cr = getResult();
for (FeatureResult fr : other.getFeatures()) {
// We need to add =the new results to the existing ones to keep the names stable
// otherwise any embedded items will be attached to the wrong result
// XXX this has the potential to cause a concurrentModificationException or other bad issues if someone is getting all the features...
cr.addFeatureResult(fr);
}
//cr.tally();
// XXX Do we need to add TagResults or call tally()?
// persist the new result to disk
this.setResult(cr, listener);
}
}
@@ -37,6 +37,8 @@
import hudson.model.Result;
import hudson.model.AbstractBuild;
import hudson.model.AbstractProject;
import hudson.model.Run;
import hudson.model.TaskListener;
import hudson.remoting.Callable;
import hudson.tasks.BuildStepDescriptor;
import hudson.tasks.BuildStepMonitor;
@@ -53,6 +55,7 @@
import java.util.Collections;
import java.util.logging.Logger;

import jenkins.tasks.SimpleBuildStep;
import net.sf.json.JSONObject;

import org.apache.tools.ant.types.FileSet;
@@ -68,7 +71,7 @@
* @author James Nord
* @author Kohsuke Kawaguchi (original JUnit code)
*/
public class CucumberTestResultArchiver extends Recorder implements MatrixAggregatable {
public class CucumberTestResultArchiver extends Recorder implements MatrixAggregatable, SimpleBuildStep {
private static final Logger LOGGER = Logger.getLogger(CucumberTestResultArchiver.class.getName());

/**
@@ -97,75 +100,80 @@ public boolean getIgnoreBadSteps(){
return ignoreBadSteps;
}

@Override
public boolean
perform(AbstractBuild build, Launcher launcher, BuildListener listener) throws InterruptedException,
IOException {
return publishReport(build, build.getWorkspace(), launcher, listener);
}


@Override
public void perform(Run<?, ?> run, FilePath filePath, Launcher launcher, TaskListener taskListener) throws InterruptedException, IOException {
publishReport(run, filePath, launcher, taskListener);
}


public boolean
perform(AbstractBuild build, Launcher launcher, BuildListener listener) throws InterruptedException,
publishReport(Run<?, ?> build, FilePath workspace, Launcher launcher, TaskListener listener) throws InterruptedException,
IOException {
// listener.getLogger().println(Messages.JUnitResultArchiver_Recording());

CucumberTestResultAction action;

final String _testResults = build.getEnvironment(listener).expand(this.testResults);

try {
CucumberJSONParser parser = new CucumberJSONParser(ignoreBadSteps);

CucumberTestResult result = parser.parse(_testResults, build, launcher, listener);

// TODO - look at all of the Scenarios and see if there are any embedded items contained with in them
String remoteTempDir = launcher.getChannel().call(new TmpDirCallable());

// if so we need to copy them to the master.
for (FeatureResult f : result.getFeatures()) {
for (ScenarioResult s : f.getScenarioResults()) {
for (EmbeddedItem item : s.getEmbeddedItems()) {
// this is the wrong place to do the copying...
// XXX Need to do something with MasterToSlaveCallable to makesure we are safe from evil
// injection
FilePath srcFilePath = new FilePath(launcher.getChannel(), remoteTempDir + '/' + item.getFilename());
// XXX when we support the workflow we will need to make sure that these files do not clash....
File destRoot = new File(build.getRootDir(), "/cucumber/embed/" + f.getSafeName() + '/' + s
.getSafeName() + '/');
destRoot.mkdirs();
File destFile = new File(destRoot, item.getFilename());
if (!destFile.getAbsolutePath().startsWith(destRoot.getAbsolutePath())) {
// someone is trying to trick us into writing abitrary files...
throw new IOException("Exploit attempt detected - Build attempted to write to " +
destFile.getAbsolutePath());
}
FilePath destFilePath = new FilePath(destFile);
srcFilePath.copyTo(destFilePath);
srcFilePath.delete();
CucumberJSONParser parser = new CucumberJSONParser(ignoreBadSteps);

CucumberTestResult result = parser.parseResult(_testResults, build, workspace, launcher, listener);

// TODO - look at all of the Scenarios and see if there are any embedded items contained with in them
String remoteTempDir = launcher.getChannel().call(new TmpDirCallable());

// if so we need to copy them to the master.
for (FeatureResult f : result.getFeatures()) {
for (ScenarioResult s : f.getScenarioResults()) {
for (EmbeddedItem item : s.getEmbeddedItems()) {
// this is the wrong place to do the copying...
// XXX Need to do something with MasterToSlaveCallable to makesure we are safe from evil
// injection
FilePath srcFilePath = new FilePath(launcher.getChannel(), remoteTempDir + '/' + item.getFilename());
// XXX when we support the workflow we will need to make sure that these files do not clash....
File destRoot = new File(build.getRootDir(), "/cucumber/embed/" + f.getSafeName() + '/' + s
.getSafeName() + '/');
destRoot.mkdirs();
File destFile = new File(destRoot, item.getFilename());
if (!destFile.getAbsolutePath().startsWith(destRoot.getAbsolutePath())) {
// someone is trying to trick us into writing abitrary files...
throw new IOException("Exploit attempt detected - Build attempted to write to " +
destFile.getAbsolutePath());
}
FilePath destFilePath = new FilePath(destFile);
srcFilePath.copyTo(destFilePath);
srcFilePath.delete();
}
}

}

action = build.getAction(CucumberTestResultAction.class);

if (action == null) {
action = new CucumberTestResultAction(build, result, listener);

if (result.getPassCount() == 0 && result.getFailCount() == 0 && result.getSkipCount() == 0)
throw new AbortException("No cucumber scenarios appear to have been run.");

CHECKPOINT.block();

}
catch (AbortException e) {
if (build.getResult() == Result.FAILURE) {
// most likely a build failed before it gets to the test phase.
// don't report confusing error message.
return true;
}
listener.getLogger().println(e.getMessage());
build.setResult(Result.FAILURE);
return true;
build.addAction(action);
CHECKPOINT.report();
}
catch (IOException e) {
e.printStackTrace(listener.error("Failed to archive cucumber reports"));
build.setResult(Result.FAILURE);
return true;
else {
CHECKPOINT.block();
action.mergeResult(result, listener);
build.save();
CHECKPOINT.report();
}
// action.setHealthScaleFactor(getHealthScaleFactor()); // overwrites previous value if appending


build.getActions().add(action);
CHECKPOINT.report();
if (result.getPassCount() == 0 && result.getFailCount() == 0 && result.getSkipCount() == 0)
throw new AbortException("No cucumber scenarios appear to have been run.");

if (action.getResult().getTotalCount() == action.getResult().getFailCount()){
build.setResult(Result.FAILURE);

0 comments on commit d87afb1

Please sign in to comment.