Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP

Comparing changes

Choose two branches to see what’s changed or to start a new pull request. If you need to, you can also compare across forks.

Open a pull request

Create a new pull request by comparing changes across two branches. If you need to, you can also compare across forks.
...
  • 3 commits
  • 7 files changed
  • 0 commit comments
  • 1 contributor
Commits on Jan 23, 2012
Florence.Chabanois Fix message f39abe5
Florence.Chabanois Move package name d1f6e4e
Florence.Chabanois Fix JENKINS-12336 be78af4
View
4 src/main/java/com/thalesgroup/hudson/plugins/xunit/XUnitPublisher.java
@@ -436,6 +436,10 @@ public Publisher newInstance(StaplerRequest req, JSONObject formData) throws For
}
}
+
+ private Object readResolve() {
+ return new org.jenkinsci.plugins.xunit.XUnitPublisher(types, thresholds);
+ }
}
View
2  src/main/java/com/thalesgroup/hudson/plugins/xunit/service/XUnitConversionService.java
@@ -115,7 +115,7 @@ private File convertInputMetricXSL(XUnitToolInfo xUnitToolInfo, File inputFile,
return convertInputMetricXSLWithUserXSL(inputFile, junitTargetFile, inputMetricXSL, userXSLFilePath);
} catch (XUnitException xe) {
xUnitLog.errorConsoleLogger("Error occurs on the use of the user stylesheet: " + xe.getMessage());
- xUnitLog.infoConsoleLogger("Using the native embedded stylesheet.");
+ xUnitLog.infoConsoleLogger("Trying to use the native embedded stylesheet.");
inputMetric.convert(inputFile, junitTargetFile);
return junitTargetFile;
}
View
420 src/main/java/org/jenkinsci/plugins/xunit/XUnitPublisher.java
@@ -0,0 +1,420 @@
+package org.jenkinsci.plugins.xunit;
+
+import com.google.inject.AbstractModule;
+import com.google.inject.Guice;
+import com.google.inject.Singleton;
+import com.thalesgroup.dtkit.metrics.hudson.api.descriptor.TestTypeDescriptor;
+import com.thalesgroup.dtkit.metrics.hudson.api.type.TestType;
+import com.thalesgroup.dtkit.metrics.model.InputMetric;
+import com.thalesgroup.hudson.plugins.xunit.exception.XUnitException;
+import com.thalesgroup.hudson.plugins.xunit.service.*;
+import com.thalesgroup.hudson.plugins.xunit.types.CustomType;
+import hudson.*;
+import hudson.model.*;
+import hudson.remoting.VirtualChannel;
+import hudson.tasks.BuildStepDescriptor;
+import hudson.tasks.BuildStepMonitor;
+import hudson.tasks.Publisher;
+import hudson.tasks.Recorder;
+import hudson.tasks.junit.JUnitResultArchiver;
+import hudson.tasks.junit.TestResult;
+import hudson.tasks.junit.TestResultAction;
+import hudson.tasks.test.TestResultProjectAction;
+import net.sf.json.JSONObject;
+import org.apache.tools.ant.DirectoryScanner;
+import org.apache.tools.ant.types.FileSet;
+import org.jenkinsci.lib.dryrun.DryRun;
+import org.jenkinsci.plugins.xunit.threshold.FailedThreshold;
+import org.jenkinsci.plugins.xunit.threshold.SkippedThreshold;
+import org.jenkinsci.plugins.xunit.threshold.XUnitThreshold;
+import org.jenkinsci.plugins.xunit.threshold.XUnitThresholdDescriptor;
+import org.kohsuke.stapler.StaplerRequest;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.Serializable;
+import java.util.List;
+
+/**
+ * Class that converting custom reports to Junit reports and records them
+ *
+ * @author Gregory Boissinot
+ */
+@SuppressWarnings({"unchecked", "unused"})
+public class XUnitPublisher extends Recorder implements DryRun, Serializable {
+
+ public static final String GENERATED_JUNIT_DIR = "generatedJUnitFiles";
+
+ private TestType[] types;
+
+ private XUnitThreshold[] thresholds;
+
+
+ public XUnitPublisher(TestType[] types, XUnitThreshold[] thresholds) {
+ this.types = types;
+ this.thresholds = thresholds;
+ }
+
+ public TestType[] getTypes() {
+ return types;
+ }
+
+ public XUnitThreshold[] getThresholds() {
+ return thresholds;
+ }
+
+ @Override
+ public Action getProjectAction(AbstractProject<?, ?> project) {
+ JUnitResultArchiver jUnitResultArchiver = project.getPublishersList().get(JUnitResultArchiver.class);
+ if (jUnitResultArchiver == null) {
+ return new TestResultProjectAction(project);
+ }
+ return null;
+ }
+
+ @Override
+ public boolean perform(final AbstractBuild<?, ?> build, Launcher launcher, final BuildListener listener)
+ throws InterruptedException, IOException {
+ return performXUnit(false, build, listener);
+ }
+
+ public boolean performDryRun(AbstractBuild<?, ?> build, Launcher launcher, BuildListener listener)
+ throws InterruptedException, IOException {
+ try {
+ performXUnit(true, build, listener);
+ } catch (Throwable t) {
+ listener.getLogger().println("[ERROR] - There is an error: " + t.getCause().getMessage());
+ }
+ //Always exit on success (returned code and status)
+ build.setResult(Result.SUCCESS);
+ return true;
+ }
+
+
+ private boolean performXUnit(boolean dryRun, AbstractBuild<?, ?> build, BuildListener listener)
+ throws IOException, InterruptedException {
+ final XUnitLog xUnitLog = getXUnitLogObject(listener);
+ try {
+
+ xUnitLog.infoConsoleLogger("Starting to record.");
+
+ boolean noProcessingErrors = performTests(dryRun, xUnitLog, build, listener);
+ if (!noProcessingErrors) {
+ build.setResult(Result.FAILURE);
+ xUnitLog.infoConsoleLogger("Stopping recording.");
+ return true;
+ }
+
+ recordTestResult(dryRun, build, listener, xUnitLog);
+ processDeletion(dryRun, build, xUnitLog);
+ setBuildStatus(dryRun, build, xUnitLog);
+ xUnitLog.infoConsoleLogger("Stopping recording.");
+ return true;
+
+ } catch (XUnitException xe) {
+ xUnitLog.errorConsoleLogger("The plugin hasn't been performed correctly: " + xe.getMessage());
+ build.setResult(Result.FAILURE);
+ return false;
+ }
+ }
+
+ private XUnitLog getXUnitLogObject(final BuildListener listener) {
+ return Guice.createInjector(new AbstractModule() {
+ @Override
+ protected void configure() {
+ bind(BuildListener.class).toInstance(listener);
+ }
+ }).getInstance(XUnitLog.class);
+ }
+
+ private XUnitReportProcessorService getXUnitReportProcessorServiceObject(final BuildListener listener) {
+ return Guice.createInjector(new AbstractModule() {
+ @Override
+ protected void configure() {
+ bind(BuildListener.class).toInstance(listener);
+ }
+ }).getInstance(XUnitReportProcessorService.class);
+ }
+
+ private boolean performTests(boolean dryRun, XUnitLog xUnitLog, AbstractBuild<?, ?> build, BuildListener listener) throws IOException, InterruptedException {
+ XUnitReportProcessorService xUnitReportService = getXUnitReportProcessorServiceObject(listener);
+ boolean noProcessingErrors = true;
+ for (TestType tool : types) {
+ xUnitLog.infoConsoleLogger("Processing " + tool.getDescriptor().getDisplayName());
+ if (!isEmptyGivenPattern(xUnitReportService, tool)) {
+ String expandedPattern = getExpandedResolvedPattern(tool, build, listener);
+ XUnitToolInfo xUnitToolInfo = getXUnitToolInfoObject(tool, expandedPattern, build);
+ XUnitTransformer xUnitTransformer = getXUnitTransformerObject(xUnitToolInfo, listener);
+ boolean resultTransformation = getWorkspace(build).act(xUnitTransformer);
+ if (!resultTransformation) {
+ noProcessingErrors = true;
+ }
+ }
+ }
+ return noProcessingErrors;
+ }
+
+ private boolean isEmptyGivenPattern(XUnitReportProcessorService xUnitReportService, TestType tool) {
+ return xUnitReportService.isEmptyPattern(tool.getPattern());
+ }
+
+ private String getExpandedResolvedPattern(TestType tool, AbstractBuild build, BuildListener listener) throws IOException, InterruptedException {
+ String newExpandedPattern = tool.getPattern();
+ newExpandedPattern = newExpandedPattern.replaceAll("[\t\r\n]+", " ");
+ return Util.replaceMacro(newExpandedPattern, build.getEnvironment(listener));
+ }
+
+ private XUnitToolInfo getXUnitToolInfoObject(TestType tool, String expandedPattern, AbstractBuild build) {
+ return new XUnitToolInfo(
+ new FilePath(new File(Hudson.getInstance().getRootDir(), "userContent")),
+ tool.getInputMetric(),
+ expandedPattern,
+ tool.isFaildedIfNotNew(),
+ tool.isDeleteOutputFiles(), tool.isStopProcessingIfError(),
+ build.getTimeInMillis(),
+ (tool instanceof CustomType) ? getWorkspace(build).child(((CustomType) tool).getCustomXSL()) : null);
+ }
+
+ private FilePath getWorkspace(AbstractBuild build) {
+ FilePath workspace = build.getWorkspace();
+ if (workspace == null) {
+ workspace = build.getProject().getSomeWorkspace();
+ }
+ return workspace;
+ }
+
+ private XUnitTransformer getXUnitTransformerObject(final XUnitToolInfo xUnitToolInfo, final BuildListener listener) {
+ return Guice.createInjector(new AbstractModule() {
+ @Override
+ protected void configure() {
+ bind(BuildListener.class).toInstance(listener);
+ bind(XUnitToolInfo.class).toInstance(xUnitToolInfo);
+ bind(XUnitValidationService.class).in(Singleton.class);
+ bind(XUnitConversionService.class).in(Singleton.class);
+ bind(XUnitLog.class).in(Singleton.class);
+ bind(XUnitReportProcessorService.class).in(Singleton.class);
+ }
+ }).getInstance(XUnitTransformer.class);
+ }
+
+
+ private TestResultAction getTestResultAction(AbstractBuild<?, ?> build) {
+ return build.getAction(TestResultAction.class);
+ }
+
+ private TestResultAction getPreviousTestResultAction(AbstractBuild<?, ?> build) {
+ AbstractBuild previousBuild = build.getPreviousBuild();
+ if (previousBuild == null) {
+ return null;
+ }
+ return getTestResultAction(previousBuild);
+ }
+
+ /**
+ * Records the test results into the current build and return the number of tests
+ */
+ private void recordTestResult(boolean dryRun, AbstractBuild<?, ?> build, BuildListener listener, XUnitLog xUnitLog) throws XUnitException {
+
+ if (!dryRun) {
+ TestResultAction existingAction = build.getAction(TestResultAction.class);
+ final long buildTime = build.getTimestamp().getTimeInMillis();
+ final long nowMaster = System.currentTimeMillis();
+
+ TestResult existingTestResults = null;
+ if (existingAction != null) {
+ existingTestResults = existingAction.getResult();
+ }
+
+ TestResult result = getTestResult(build, "**/TEST-*.xml", existingTestResults, buildTime, nowMaster);
+ if (result != null) {
+ TestResultAction action;
+ if (existingAction == null) {
+ action = new TestResultAction(build, result, listener);
+ } else {
+ action = existingAction;
+ action.setResult(result, listener);
+ }
+
+ if (result.getPassCount() == 0 && result.getFailCount() == 0) {
+ xUnitLog.warningConsoleLogger("All test reports are empty.");
+ }
+
+ if (existingAction == null) {
+ build.getActions().add(action);
+ }
+ }
+ }
+ }
+
+ /**
+ * Gets a Test result object (a new one if any)
+ *
+ * @param build the current build
+ * @param junitFilePattern the JUnit search pattern
+ * @param existingTestResults the existing test result
+ * @param buildTime the build time
+ * @param nowMaster the time on master
+ * @return the test result object
+ * @throws XUnitException the plugin exception
+ */
+ private TestResult getTestResult(final AbstractBuild<?, ?> build,
+ final String junitFilePattern,
+ final TestResult existingTestResults,
+ final long buildTime, final long nowMaster)
+ throws XUnitException {
+
+ try {
+ return getWorkspace(build).act(new FilePath.FileCallable<TestResult>() {
+
+ public TestResult invoke(File ws, VirtualChannel channel) throws IOException {
+ final long nowSlave = System.currentTimeMillis();
+ FileSet fs = Util.createFileSet(new File(ws, GENERATED_JUNIT_DIR), junitFilePattern);
+ DirectoryScanner ds = fs.getDirectoryScanner();
+ String[] files = ds.getIncludedFiles();
+
+ if (files.length == 0) {
+ // no test result. Most likely a configuration error or fatal problem
+ return null;
+
+ }
+ try {
+ if (existingTestResults == null) {
+ return new TestResult(buildTime + (nowSlave - nowMaster), ds, true);
+ } else {
+ existingTestResults.parse(buildTime + (nowSlave - nowMaster), ds);
+ return existingTestResults;
+ }
+ } catch (IOException ioe) {
+ throw new IOException(ioe);
+ }
+ }
+
+ });
+
+ } catch (IOException ioe) {
+ throw new XUnitException(ioe.getMessage(), ioe);
+ } catch (InterruptedException ie) {
+ throw new XUnitException(ie.getMessage(), ie);
+ }
+ }
+
+ private void setBuildStatus(boolean dryRun, AbstractBuild<?, ?> build, XUnitLog xUnitLog) {
+ Result curResult = getResultWithThreshold(xUnitLog, build);
+ Result previousResultStep = build.getResult();
+ if (previousResultStep.isWorseOrEqualTo(curResult)) {
+ curResult = previousResultStep;
+ }
+ xUnitLog.infoConsoleLogger("Setting the build status to " + curResult);
+ build.setResult(curResult);
+ }
+
+ private Result getResultWithThreshold(XUnitLog log, AbstractBuild<?, ?> build) {
+ TestResultAction testResultAction = getTestResultAction(build);
+ TestResultAction previousTestResultAction = getPreviousTestResultAction(build);
+ if (testResultAction == null) {
+ return Result.FAILURE;
+ } else {
+ return processResultThreshold(log, build, testResultAction, previousTestResultAction);
+ }
+ }
+
+ private Result processResultThreshold(XUnitLog log,
+ AbstractBuild<?, ?> build,
+ TestResultAction testResultAction,
+ TestResultAction previousTestResultAction) {
+
+ if (thresholds != null) {
+ for (XUnitThreshold threshold : thresholds) {
+ log.infoConsoleLogger(String.format("Check '%s' threshold.", threshold.getDescriptor().getDisplayName()));
+ Result result = threshold.getResultThreshold(log, build, testResultAction, previousTestResultAction);
+ if (result.isWorseThan(Result.SUCCESS)) {
+ return result;
+ }
+ }
+ }
+
+ return Result.SUCCESS;
+ }
+
+
+ private void processDeletion(boolean dryRun, AbstractBuild<?, ?> build, XUnitLog xUnitLog) throws XUnitException {
+ try {
+ boolean keepJUnitDirectory = false;
+ for (TestType tool : types) {
+ InputMetric inputMetric = tool.getInputMetric();
+
+ if (dryRun || tool.isDeleteOutputFiles()) {
+ getWorkspace(build).child(GENERATED_JUNIT_DIR + "/" + inputMetric.getToolName()).deleteRecursive();
+ } else {
+ //Mark the tool file parent directory to no deletion
+ keepJUnitDirectory = true;
+ }
+ }
+ if (!keepJUnitDirectory) {
+ getWorkspace(build).child(GENERATED_JUNIT_DIR).deleteRecursive();
+ }
+ } catch (IOException ioe) {
+ throw new XUnitException("Problem on deletion", ioe);
+ } catch (InterruptedException ie) {
+ throw new XUnitException("Problem on deletion", ie);
+ }
+ }
+
+ public BuildStepMonitor getRequiredMonitorService() {
+ return BuildStepMonitor.NONE;
+ }
+
+ @Extension
+ @SuppressWarnings("unused")
+ public static final class XUnitDescriptorPublisher extends BuildStepDescriptor<Publisher> {
+
+ public XUnitDescriptorPublisher() {
+ super(XUnitPublisher.class);
+ load();
+ }
+
+ @Override
+ public String getDisplayName() {
+ return Messages.xUnit_PublisherName();
+ }
+
+ @Override
+ public boolean isApplicable(Class type) {
+ return true;
+ }
+
+ @Override
+ public String getHelpFile() {
+ return "/plugin/xunit/help.html";
+ }
+
+ public DescriptorExtensionList<TestType, TestTypeDescriptor<?>> getListXUnitTypeDescriptors() {
+ return TestTypeDescriptor.all();
+ }
+
+ public DescriptorExtensionList<XUnitThreshold, XUnitThresholdDescriptor<?>> getListXUnitThresholdDescriptors() {
+ return XUnitThresholdDescriptor.all();
+ }
+
+ public XUnitThreshold[] getListXUnitThresholdInstance() {
+ return new XUnitThreshold[]{
+ new FailedThreshold(),
+ new SkippedThreshold()
+ };
+ }
+
+ @Override
+ public Publisher newInstance(StaplerRequest req, JSONObject formData) throws FormException {
+ List<TestType> types = Descriptor.newInstancesFromHeteroList(
+ req, formData, "tools", getListXUnitTypeDescriptors());
+ List<XUnitThreshold> thresholds = Descriptor.newInstancesFromHeteroList(
+ req, formData, "thresholds", getListXUnitThresholdDescriptors());
+ return new XUnitPublisher(types.toArray(new TestType[types.size()]), thresholds.toArray(new XUnitThreshold[thresholds.size()]));
+ }
+ }
+
+}
+
+
+
+
View
4 src/main/java/org/jenkinsci/plugins/xunit/types/CheckType.java
@@ -14,8 +14,8 @@
public class CheckType extends TestType {
@DataBoundConstructor
- public CheckType(String pattern, boolean failureIfNotNew, boolean deleteOutputFiles, boolean stopProcessingIfError) {
- super(pattern, failureIfNotNew, deleteOutputFiles, stopProcessingIfError);
+ public CheckType(String pattern, boolean faildedIfNotNew, boolean deleteOutputFiles, boolean stopProcessingIfError) {
+ super(pattern, faildedIfNotNew, deleteOutputFiles, stopProcessingIfError);
}
@Extension
View
4 src/main/java/org/jenkinsci/plugins/xunit/types/JUnitType.java
@@ -14,8 +14,8 @@
public class JUnitType extends TestType {
@DataBoundConstructor
- public JUnitType(String pattern, boolean failureIfNotNew, boolean deleteOutputFiles, boolean stopProcessingIfError) {
- super(pattern, failureIfNotNew, deleteOutputFiles, stopProcessingIfError);
+ public JUnitType(String pattern, boolean faildedIfNotNew, boolean deleteOutputFiles, boolean stopProcessingIfError) {
+ super(pattern, faildedIfNotNew, deleteOutputFiles, stopProcessingIfError);
}
@Extension
View
1  src/main/resources/com/thalesgroup/hudson/plugins/xunit/Messages.properties
@@ -1 +0,0 @@
-xUnit.PublisherName=Publish testing tools result report
View
0  ...plugins/xunit/XUnitPublisher/config.jelly → ...plugins/xunit/XUnitPublisher/config.jelly
File renamed without changes

No commit comments for this range

Something went wrong with that request. Please try again.