Skip to content
This repository

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
  • 3 commits
  • 7 files changed
  • 0 comments
  • 1 contributor
Jan 23, 2012
Fix message f39abe5
Move package name d1f6e4e
Fix JENKINS-12336 be78af4
4  src/main/java/com/thalesgroup/hudson/plugins/xunit/XUnitPublisher.java
@@ -436,6 +436,10 @@ public Publisher newInstance(StaplerRequest req, JSONObject formData) throws For
436 436
         }
437 437
     }
438 438
 
  439
+
  440
+    private Object readResolve() {
  441
+        return new org.jenkinsci.plugins.xunit.XUnitPublisher(types, thresholds);
  442
+    }
439 443
 }
440 444
 
441 445
 
2  src/main/java/com/thalesgroup/hudson/plugins/xunit/service/XUnitConversionService.java
@@ -115,7 +115,7 @@ private File convertInputMetricXSL(XUnitToolInfo xUnitToolInfo, File inputFile,
115 115
                 return convertInputMetricXSLWithUserXSL(inputFile, junitTargetFile, inputMetricXSL, userXSLFilePath);
116 116
             } catch (XUnitException xe) {
117 117
                 xUnitLog.errorConsoleLogger("Error occurs on the use of the user stylesheet: " + xe.getMessage());
118  
-                xUnitLog.infoConsoleLogger("Using the native embedded stylesheet.");
  118
+                xUnitLog.infoConsoleLogger("Trying to use the native embedded stylesheet.");
119 119
                 inputMetric.convert(inputFile, junitTargetFile);
120 120
                 return junitTargetFile;
121 121
             }
420  src/main/java/org/jenkinsci/plugins/xunit/XUnitPublisher.java
... ...
@@ -0,0 +1,420 @@
  1
+package org.jenkinsci.plugins.xunit;
  2
+
  3
+import com.google.inject.AbstractModule;
  4
+import com.google.inject.Guice;
  5
+import com.google.inject.Singleton;
  6
+import com.thalesgroup.dtkit.metrics.hudson.api.descriptor.TestTypeDescriptor;
  7
+import com.thalesgroup.dtkit.metrics.hudson.api.type.TestType;
  8
+import com.thalesgroup.dtkit.metrics.model.InputMetric;
  9
+import com.thalesgroup.hudson.plugins.xunit.exception.XUnitException;
  10
+import com.thalesgroup.hudson.plugins.xunit.service.*;
  11
+import com.thalesgroup.hudson.plugins.xunit.types.CustomType;
  12
+import hudson.*;
  13
+import hudson.model.*;
  14
+import hudson.remoting.VirtualChannel;
  15
+import hudson.tasks.BuildStepDescriptor;
  16
+import hudson.tasks.BuildStepMonitor;
  17
+import hudson.tasks.Publisher;
  18
+import hudson.tasks.Recorder;
  19
+import hudson.tasks.junit.JUnitResultArchiver;
  20
+import hudson.tasks.junit.TestResult;
  21
+import hudson.tasks.junit.TestResultAction;
  22
+import hudson.tasks.test.TestResultProjectAction;
  23
+import net.sf.json.JSONObject;
  24
+import org.apache.tools.ant.DirectoryScanner;
  25
+import org.apache.tools.ant.types.FileSet;
  26
+import org.jenkinsci.lib.dryrun.DryRun;
  27
+import org.jenkinsci.plugins.xunit.threshold.FailedThreshold;
  28
+import org.jenkinsci.plugins.xunit.threshold.SkippedThreshold;
  29
+import org.jenkinsci.plugins.xunit.threshold.XUnitThreshold;
  30
+import org.jenkinsci.plugins.xunit.threshold.XUnitThresholdDescriptor;
  31
+import org.kohsuke.stapler.StaplerRequest;
  32
+
  33
+import java.io.File;
  34
+import java.io.IOException;
  35
+import java.io.Serializable;
  36
+import java.util.List;
  37
+
  38
+/**
  39
+ * Class that converting custom reports to Junit reports and records them
  40
+ *
  41
+ * @author Gregory Boissinot
  42
+ */
  43
+@SuppressWarnings({"unchecked", "unused"})
  44
+public class XUnitPublisher extends Recorder implements DryRun, Serializable {
  45
+
  46
+    public static final String GENERATED_JUNIT_DIR = "generatedJUnitFiles";
  47
+
  48
+    private TestType[] types;
  49
+
  50
+    private XUnitThreshold[] thresholds;
  51
+
  52
+
  53
+    public XUnitPublisher(TestType[] types, XUnitThreshold[] thresholds) {
  54
+        this.types = types;
  55
+        this.thresholds = thresholds;
  56
+    }
  57
+
  58
+    public TestType[] getTypes() {
  59
+        return types;
  60
+    }
  61
+
  62
+    public XUnitThreshold[] getThresholds() {
  63
+        return thresholds;
  64
+    }
  65
+
  66
+    @Override
  67
+    public Action getProjectAction(AbstractProject<?, ?> project) {
  68
+        JUnitResultArchiver jUnitResultArchiver = project.getPublishersList().get(JUnitResultArchiver.class);
  69
+        if (jUnitResultArchiver == null) {
  70
+            return new TestResultProjectAction(project);
  71
+        }
  72
+        return null;
  73
+    }
  74
+
  75
+    @Override
  76
+    public boolean perform(final AbstractBuild<?, ?> build, Launcher launcher, final BuildListener listener)
  77
+            throws InterruptedException, IOException {
  78
+        return performXUnit(false, build, listener);
  79
+    }
  80
+
  81
+    public boolean performDryRun(AbstractBuild<?, ?> build, Launcher launcher, BuildListener listener)
  82
+            throws InterruptedException, IOException {
  83
+        try {
  84
+            performXUnit(true, build, listener);
  85
+        } catch (Throwable t) {
  86
+            listener.getLogger().println("[ERROR] - There is an error: " + t.getCause().getMessage());
  87
+        }
  88
+        //Always exit on success (returned code and status)
  89
+        build.setResult(Result.SUCCESS);
  90
+        return true;
  91
+    }
  92
+
  93
+
  94
+    private boolean performXUnit(boolean dryRun, AbstractBuild<?, ?> build, BuildListener listener)
  95
+            throws IOException, InterruptedException {
  96
+        final XUnitLog xUnitLog = getXUnitLogObject(listener);
  97
+        try {
  98
+
  99
+            xUnitLog.infoConsoleLogger("Starting to record.");
  100
+
  101
+            boolean noProcessingErrors = performTests(dryRun, xUnitLog, build, listener);
  102
+            if (!noProcessingErrors) {
  103
+                build.setResult(Result.FAILURE);
  104
+                xUnitLog.infoConsoleLogger("Stopping recording.");
  105
+                return true;
  106
+            }
  107
+
  108
+            recordTestResult(dryRun, build, listener, xUnitLog);
  109
+            processDeletion(dryRun, build, xUnitLog);
  110
+            setBuildStatus(dryRun, build, xUnitLog);
  111
+            xUnitLog.infoConsoleLogger("Stopping recording.");
  112
+            return true;
  113
+
  114
+        } catch (XUnitException xe) {
  115
+            xUnitLog.errorConsoleLogger("The plugin hasn't been performed correctly: " + xe.getMessage());
  116
+            build.setResult(Result.FAILURE);
  117
+            return false;
  118
+        }
  119
+    }
  120
+
  121
+    private XUnitLog getXUnitLogObject(final BuildListener listener) {
  122
+        return Guice.createInjector(new AbstractModule() {
  123
+            @Override
  124
+            protected void configure() {
  125
+                bind(BuildListener.class).toInstance(listener);
  126
+            }
  127
+        }).getInstance(XUnitLog.class);
  128
+    }
  129
+
  130
+    private XUnitReportProcessorService getXUnitReportProcessorServiceObject(final BuildListener listener) {
  131
+        return Guice.createInjector(new AbstractModule() {
  132
+            @Override
  133
+            protected void configure() {
  134
+                bind(BuildListener.class).toInstance(listener);
  135
+            }
  136
+        }).getInstance(XUnitReportProcessorService.class);
  137
+    }
  138
+
  139
+    private boolean performTests(boolean dryRun, XUnitLog xUnitLog, AbstractBuild<?, ?> build, BuildListener listener) throws IOException, InterruptedException {
  140
+        XUnitReportProcessorService xUnitReportService = getXUnitReportProcessorServiceObject(listener);
  141
+        boolean noProcessingErrors = true;
  142
+        for (TestType tool : types) {
  143
+            xUnitLog.infoConsoleLogger("Processing " + tool.getDescriptor().getDisplayName());
  144
+            if (!isEmptyGivenPattern(xUnitReportService, tool)) {
  145
+                String expandedPattern = getExpandedResolvedPattern(tool, build, listener);
  146
+                XUnitToolInfo xUnitToolInfo = getXUnitToolInfoObject(tool, expandedPattern, build);
  147
+                XUnitTransformer xUnitTransformer = getXUnitTransformerObject(xUnitToolInfo, listener);
  148
+                boolean resultTransformation = getWorkspace(build).act(xUnitTransformer);
  149
+                if (!resultTransformation) {
  150
+                    noProcessingErrors = true;
  151
+                }
  152
+            }
  153
+        }
  154
+        return noProcessingErrors;
  155
+    }
  156
+
  157
+    private boolean isEmptyGivenPattern(XUnitReportProcessorService xUnitReportService, TestType tool) {
  158
+        return xUnitReportService.isEmptyPattern(tool.getPattern());
  159
+    }
  160
+
  161
+    private String getExpandedResolvedPattern(TestType tool, AbstractBuild build, BuildListener listener) throws IOException, InterruptedException {
  162
+        String newExpandedPattern = tool.getPattern();
  163
+        newExpandedPattern = newExpandedPattern.replaceAll("[\t\r\n]+", " ");
  164
+        return Util.replaceMacro(newExpandedPattern, build.getEnvironment(listener));
  165
+    }
  166
+
  167
+    private XUnitToolInfo getXUnitToolInfoObject(TestType tool, String expandedPattern, AbstractBuild build) {
  168
+        return new XUnitToolInfo(
  169
+                new FilePath(new File(Hudson.getInstance().getRootDir(), "userContent")),
  170
+                tool.getInputMetric(),
  171
+                expandedPattern,
  172
+                tool.isFaildedIfNotNew(),
  173
+                tool.isDeleteOutputFiles(), tool.isStopProcessingIfError(),
  174
+                build.getTimeInMillis(),
  175
+                (tool instanceof CustomType) ? getWorkspace(build).child(((CustomType) tool).getCustomXSL()) : null);
  176
+    }
  177
+
  178
+    private FilePath getWorkspace(AbstractBuild build) {
  179
+        FilePath workspace = build.getWorkspace();
  180
+        if (workspace == null) {
  181
+            workspace = build.getProject().getSomeWorkspace();
  182
+        }
  183
+        return workspace;
  184
+    }
  185
+
  186
+    private XUnitTransformer getXUnitTransformerObject(final XUnitToolInfo xUnitToolInfo, final BuildListener listener) {
  187
+        return Guice.createInjector(new AbstractModule() {
  188
+            @Override
  189
+            protected void configure() {
  190
+                bind(BuildListener.class).toInstance(listener);
  191
+                bind(XUnitToolInfo.class).toInstance(xUnitToolInfo);
  192
+                bind(XUnitValidationService.class).in(Singleton.class);
  193
+                bind(XUnitConversionService.class).in(Singleton.class);
  194
+                bind(XUnitLog.class).in(Singleton.class);
  195
+                bind(XUnitReportProcessorService.class).in(Singleton.class);
  196
+            }
  197
+        }).getInstance(XUnitTransformer.class);
  198
+    }
  199
+
  200
+
  201
+    private TestResultAction getTestResultAction(AbstractBuild<?, ?> build) {
  202
+        return build.getAction(TestResultAction.class);
  203
+    }
  204
+
  205
+    private TestResultAction getPreviousTestResultAction(AbstractBuild<?, ?> build) {
  206
+        AbstractBuild previousBuild = build.getPreviousBuild();
  207
+        if (previousBuild == null) {
  208
+            return null;
  209
+        }
  210
+        return getTestResultAction(previousBuild);
  211
+    }
  212
+
  213
+    /**
  214
+     * Records the test results into the current build and return the number of tests
  215
+     */
  216
+    private void recordTestResult(boolean dryRun, AbstractBuild<?, ?> build, BuildListener listener, XUnitLog xUnitLog) throws XUnitException {
  217
+
  218
+        if (!dryRun) {
  219
+            TestResultAction existingAction = build.getAction(TestResultAction.class);
  220
+            final long buildTime = build.getTimestamp().getTimeInMillis();
  221
+            final long nowMaster = System.currentTimeMillis();
  222
+
  223
+            TestResult existingTestResults = null;
  224
+            if (existingAction != null) {
  225
+                existingTestResults = existingAction.getResult();
  226
+            }
  227
+
  228
+            TestResult result = getTestResult(build, "**/TEST-*.xml", existingTestResults, buildTime, nowMaster);
  229
+            if (result != null) {
  230
+                TestResultAction action;
  231
+                if (existingAction == null) {
  232
+                    action = new TestResultAction(build, result, listener);
  233
+                } else {
  234
+                    action = existingAction;
  235
+                    action.setResult(result, listener);
  236
+                }
  237
+
  238
+                if (result.getPassCount() == 0 && result.getFailCount() == 0) {
  239
+                    xUnitLog.warningConsoleLogger("All test reports are empty.");
  240
+                }
  241
+
  242
+                if (existingAction == null) {
  243
+                    build.getActions().add(action);
  244
+                }
  245
+            }
  246
+        }
  247
+    }
  248
+
  249
+    /**
  250
+     * Gets a Test result object (a new one if any)
  251
+     *
  252
+     * @param build               the current build
  253
+     * @param junitFilePattern    the JUnit search pattern
  254
+     * @param existingTestResults the existing test result
  255
+     * @param buildTime           the build time
  256
+     * @param nowMaster           the time on master
  257
+     * @return the test result object
  258
+     * @throws XUnitException the plugin exception
  259
+     */
  260
+    private TestResult getTestResult(final AbstractBuild<?, ?> build,
  261
+                                     final String junitFilePattern,
  262
+                                     final TestResult existingTestResults,
  263
+                                     final long buildTime, final long nowMaster)
  264
+            throws XUnitException {
  265
+
  266
+        try {
  267
+            return getWorkspace(build).act(new FilePath.FileCallable<TestResult>() {
  268
+
  269
+                public TestResult invoke(File ws, VirtualChannel channel) throws IOException {
  270
+                    final long nowSlave = System.currentTimeMillis();
  271
+                    FileSet fs = Util.createFileSet(new File(ws, GENERATED_JUNIT_DIR), junitFilePattern);
  272
+                    DirectoryScanner ds = fs.getDirectoryScanner();
  273
+                    String[] files = ds.getIncludedFiles();
  274
+
  275
+                    if (files.length == 0) {
  276
+                        // no test result. Most likely a configuration error or fatal problem
  277
+                        return null;
  278
+
  279
+                    }
  280
+                    try {
  281
+                        if (existingTestResults == null) {
  282
+                            return new TestResult(buildTime + (nowSlave - nowMaster), ds, true);
  283
+                        } else {
  284
+                            existingTestResults.parse(buildTime + (nowSlave - nowMaster), ds);
  285
+                            return existingTestResults;
  286
+                        }
  287
+                    } catch (IOException ioe) {
  288
+                        throw new IOException(ioe);
  289
+                    }
  290
+                }
  291
+
  292
+            });
  293
+
  294
+        } catch (IOException ioe) {
  295
+            throw new XUnitException(ioe.getMessage(), ioe);
  296
+        } catch (InterruptedException ie) {
  297
+            throw new XUnitException(ie.getMessage(), ie);
  298
+        }
  299
+    }
  300
+
  301
+    private void setBuildStatus(boolean dryRun, AbstractBuild<?, ?> build, XUnitLog xUnitLog) {
  302
+        Result curResult = getResultWithThreshold(xUnitLog, build);
  303
+        Result previousResultStep = build.getResult();
  304
+        if (previousResultStep.isWorseOrEqualTo(curResult)) {
  305
+            curResult = previousResultStep;
  306
+        }
  307
+        xUnitLog.infoConsoleLogger("Setting the build status to " + curResult);
  308
+        build.setResult(curResult);
  309
+    }
  310
+
  311
+    private Result getResultWithThreshold(XUnitLog log, AbstractBuild<?, ?> build) {
  312
+        TestResultAction testResultAction = getTestResultAction(build);
  313
+        TestResultAction previousTestResultAction = getPreviousTestResultAction(build);
  314
+        if (testResultAction == null) {
  315
+            return Result.FAILURE;
  316
+        } else {
  317
+            return processResultThreshold(log, build, testResultAction, previousTestResultAction);
  318
+        }
  319
+    }
  320
+
  321
+    private Result processResultThreshold(XUnitLog log,
  322
+                                          AbstractBuild<?, ?> build,
  323
+                                          TestResultAction testResultAction,
  324
+                                          TestResultAction previousTestResultAction) {
  325
+
  326
+        if (thresholds != null) {
  327
+            for (XUnitThreshold threshold : thresholds) {
  328
+                log.infoConsoleLogger(String.format("Check '%s' threshold.", threshold.getDescriptor().getDisplayName()));
  329
+                Result result = threshold.getResultThreshold(log, build, testResultAction, previousTestResultAction);
  330
+                if (result.isWorseThan(Result.SUCCESS)) {
  331
+                    return result;
  332
+                }
  333
+            }
  334
+        }
  335
+
  336
+        return Result.SUCCESS;
  337
+    }
  338
+
  339
+
  340
+    private void processDeletion(boolean dryRun, AbstractBuild<?, ?> build, XUnitLog xUnitLog) throws XUnitException {
  341
+        try {
  342
+            boolean keepJUnitDirectory = false;
  343
+            for (TestType tool : types) {
  344
+                InputMetric inputMetric = tool.getInputMetric();
  345
+
  346
+                if (dryRun || tool.isDeleteOutputFiles()) {
  347
+                    getWorkspace(build).child(GENERATED_JUNIT_DIR + "/" + inputMetric.getToolName()).deleteRecursive();
  348
+                } else {
  349
+                    //Mark the tool file parent directory to no deletion
  350
+                    keepJUnitDirectory = true;
  351
+                }
  352
+            }
  353
+            if (!keepJUnitDirectory) {
  354
+                getWorkspace(build).child(GENERATED_JUNIT_DIR).deleteRecursive();
  355
+            }
  356
+        } catch (IOException ioe) {
  357
+            throw new XUnitException("Problem on deletion", ioe);
  358
+        } catch (InterruptedException ie) {
  359
+            throw new XUnitException("Problem on deletion", ie);
  360
+        }
  361
+    }
  362
+
  363
+    public BuildStepMonitor getRequiredMonitorService() {
  364
+        return BuildStepMonitor.NONE;
  365
+    }
  366
+
  367
+    @Extension
  368
+    @SuppressWarnings("unused")
  369
+    public static final class XUnitDescriptorPublisher extends BuildStepDescriptor<Publisher> {
  370
+
  371
+        public XUnitDescriptorPublisher() {
  372
+            super(XUnitPublisher.class);
  373
+            load();
  374
+        }
  375
+
  376
+        @Override
  377
+        public String getDisplayName() {
  378
+            return Messages.xUnit_PublisherName();
  379
+        }
  380
+
  381
+        @Override
  382
+        public boolean isApplicable(Class type) {
  383
+            return true;
  384
+        }
  385
+
  386
+        @Override
  387
+        public String getHelpFile() {
  388
+            return "/plugin/xunit/help.html";
  389
+        }
  390
+
  391
+        public DescriptorExtensionList<TestType, TestTypeDescriptor<?>> getListXUnitTypeDescriptors() {
  392
+            return TestTypeDescriptor.all();
  393
+        }
  394
+
  395
+        public DescriptorExtensionList<XUnitThreshold, XUnitThresholdDescriptor<?>> getListXUnitThresholdDescriptors() {
  396
+            return XUnitThresholdDescriptor.all();
  397
+        }
  398
+
  399
+        public XUnitThreshold[] getListXUnitThresholdInstance() {
  400
+            return new XUnitThreshold[]{
  401
+                    new FailedThreshold(),
  402
+                    new SkippedThreshold()
  403
+            };
  404
+        }
  405
+
  406
+        @Override
  407
+        public Publisher newInstance(StaplerRequest req, JSONObject formData) throws FormException {
  408
+            List<TestType> types = Descriptor.newInstancesFromHeteroList(
  409
+                    req, formData, "tools", getListXUnitTypeDescriptors());
  410
+            List<XUnitThreshold> thresholds = Descriptor.newInstancesFromHeteroList(
  411
+                    req, formData, "thresholds", getListXUnitThresholdDescriptors());
  412
+            return new XUnitPublisher(types.toArray(new TestType[types.size()]), thresholds.toArray(new XUnitThreshold[thresholds.size()]));
  413
+        }
  414
+    }
  415
+
  416
+}
  417
+
  418
+
  419
+
  420
+
4  src/main/java/org/jenkinsci/plugins/xunit/types/CheckType.java
@@ -14,8 +14,8 @@
14 14
 public class CheckType extends TestType {
15 15
 
16 16
     @DataBoundConstructor
17  
-    public CheckType(String pattern, boolean failureIfNotNew, boolean deleteOutputFiles, boolean stopProcessingIfError) {
18  
-        super(pattern, failureIfNotNew, deleteOutputFiles, stopProcessingIfError);
  17
+    public CheckType(String pattern, boolean faildedIfNotNew, boolean deleteOutputFiles, boolean stopProcessingIfError) {
  18
+        super(pattern, faildedIfNotNew, deleteOutputFiles, stopProcessingIfError);
19 19
     }
20 20
 
21 21
     @Extension
4  src/main/java/org/jenkinsci/plugins/xunit/types/JUnitType.java
@@ -14,8 +14,8 @@
14 14
 public class JUnitType extends TestType {
15 15
 
16 16
     @DataBoundConstructor
17  
-    public JUnitType(String pattern, boolean failureIfNotNew, boolean deleteOutputFiles, boolean stopProcessingIfError) {
18  
-        super(pattern, failureIfNotNew, deleteOutputFiles, stopProcessingIfError);
  17
+    public JUnitType(String pattern, boolean faildedIfNotNew, boolean deleteOutputFiles, boolean stopProcessingIfError) {
  18
+        super(pattern, faildedIfNotNew, deleteOutputFiles, stopProcessingIfError);
19 19
     }
20 20
 
21 21
     @Extension
1  src/main/resources/com/thalesgroup/hudson/plugins/xunit/Messages.properties
... ...
@@ -1 +0,0 @@
1  
-xUnit.PublisherName=Publish testing tools result report
0  .../hudson/plugins/xunit/XUnitPublisher/config.jelly → ...nkinsci/plugins/xunit/XUnitPublisher/config.jelly
File renamed without changes

No commit comments for this range

Something went wrong with that request. Please try again.