SurefireArchiver.java 13.5 KB
Newer Older
K
kohsuke 已提交
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
/*
 * The MIT License
 * 
 * Copyright (c) 2004-2009, Sun Microsystems, Inc., Kohsuke Kawaguchi, Jason Chaffee, Maciek Starzyk
 * 
 * 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.
 */
24 25
package hudson.maven.reporters;

26
import hudson.Extension;
27
import hudson.Util;
S
Seiji Sogabe 已提交
28
import hudson.maven.Maven3Builder;
29 30 31
import hudson.maven.MavenBuild;
import hudson.maven.MavenBuildProxy;
import hudson.maven.MavenBuildProxy.BuildCallable;
K
kohsuke 已提交
32
import hudson.maven.MavenBuilder;
33
import hudson.maven.MavenModule;
34
import hudson.maven.MavenProjectActionBuilder;
35 36 37
import hudson.maven.MavenReporter;
import hudson.maven.MavenReporterDescriptor;
import hudson.maven.MojoInfo;
38
import hudson.model.Action;
39 40 41
import hudson.model.BuildListener;
import hudson.model.Result;
import hudson.tasks.junit.TestResult;
42
import hudson.tasks.test.TestResultProjectAction;
43 44 45

import java.io.File;
import java.io.IOException;
46 47
import java.util.Collection;
import java.util.Collections;
48 49
import java.util.List;
import java.util.ListIterator;
50
import java.util.concurrent.ConcurrentHashMap;
51
import java.util.concurrent.ConcurrentMap;
52

53 54 55 56 57 58 59 60 61 62
import org.apache.commons.lang.StringUtils;
import org.apache.maven.artifact.versioning.ComparableVersion;
import org.apache.maven.plugin.MojoFailureException;
import org.apache.maven.project.MavenProject;
import org.apache.tools.ant.DirectoryScanner;
import org.apache.tools.ant.types.FileSet;
import org.codehaus.plexus.component.configurator.ComponentConfigurationException;
import org.codehaus.plexus.configuration.xml.XmlPlexusConfiguration;
import org.codehaus.plexus.util.xml.Xpp3Dom;

63 64 65 66 67
/**
 * Records the surefire test result.
 * @author Kohsuke Kawaguchi
 */
public class SurefireArchiver extends MavenReporter {
68
    private TestResult result;
69 70 71 72 73 74
    
    /**
     * Store the filesets here as we want to track ignores between multiple runs of this class<br/>
     * Note: Because this class can be run with different mojo goals with different path settings, 
     * we track multiple {@link FileSet}s for each encountered <tt>reportsDir</tt>
     */
75
    private transient ConcurrentMap<File, FileSet> fileSets = new ConcurrentHashMap<File,FileSet>();
76

77
    public boolean preExecute(MavenBuildProxy build, MavenProject pom, MojoInfo mojo, BuildListener listener) throws InterruptedException, IOException {
K
kohsuke 已提交
78
        if (isSurefireTest(mojo)) {
79 80 81 82 83
            if (!mojo.is("org.apache.maven.plugins", "maven-failsafe-plugin", "integration-test")) {
                // tell surefire:test to keep going even if there was a failure,
                // so that we can record this as yellow.
                // note that because of the way Maven works, just updating system property at this point is too late
                XmlPlexusConfiguration c = (XmlPlexusConfiguration) mojo.configuration.getChild("testFailureIgnore");
84
                if(c!=null && c.getValue() != null && c.getValue().equals("${maven.test.failure.ignore}") && System.getProperty("maven.test.failure.ignore")==null) {
85 86 87 88 89 90 91 92 93 94 95 96 97 98
                    if (maven3orLater( build.getMavenBuildInformation().getMavenVersion() )) {
                        String fieldName = "testFailureIgnore";
                        if (mojo.mojoExecution.getConfiguration().getChild( fieldName ) != null) {
                          mojo.mojoExecution.getConfiguration().getChild( fieldName ).setValue( Boolean.TRUE.toString() );
                        } else {
                            Xpp3Dom child = new Xpp3Dom( fieldName );
                            child.setValue( Boolean.TRUE.toString() );
                            mojo.mojoExecution.getConfiguration().addChild( child );
                        }
                        
                    } else {
                        c.setValue(Boolean.TRUE.toString());
                    }
                }
99
            }
K
kohsuke 已提交
100
        }
101 102 103
        return true;
    }

104
    public boolean postExecute(MavenBuildProxy build, MavenProject pom, MojoInfo mojo, final BuildListener listener, Throwable error) throws InterruptedException, IOException {
105 106
        if (!isSurefireTest(mojo)) return true;

K
i18n  
kohsuke 已提交
107
        listener.getLogger().println(Messages.SurefireArchiver_Recording());
108

109
        File reportsDir;
110 111
        if (mojo.is("org.apache.maven.plugins", "maven-surefire-plugin", "test") ||
            mojo.is("org.apache.maven.plugins", "maven-failsafe-plugin", "integration-test")) {
112 113 114 115 116 117 118 119 120 121
            try {
                reportsDir = mojo.getConfigurationValue("reportsDirectory", File.class);
            } catch (ComponentConfigurationException e) {
                e.printStackTrace(listener.fatalError(Messages.SurefireArchiver_NoReportsDir()));
                build.setResult(Result.FAILURE);
                return true;
            }
        }
        else {
            reportsDir = new File(pom.getBasedir(), "target/surefire-reports");
122 123 124 125 126
        }

        if(reportsDir.exists()) {
            // surefire:test just skips itself when the current project is not a java project

127
            synchronized (build) {
128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164
            	FileSet fileSet = getFileSet(reportsDir);
            	
                DirectoryScanner ds = fileSet.getDirectoryScanner();
                
                if(ds.getIncludedFilesCount()==0)
                    // no test in this module
                    return true;
                
                String[] reportFiles = ds.getIncludedFiles();
                rememberCheckedFiles(reportsDir, reportFiles);
    
                if(result==null)    result = new TestResult();
                result.parse(System.currentTimeMillis() - build.getMilliSecsSinceBuildStart(), reportsDir, reportFiles);
    
                int failCount = build.execute(new BuildCallable<Integer, IOException>() {
                        public Integer call(MavenBuild build) throws IOException, InterruptedException {
                            SurefireReport sr = build.getAction(SurefireReport.class);
                            if(sr==null)
                                build.getActions().add(new SurefireReport(build, result, listener));
                            else
                                sr.setResult(result,listener);
                            if(result.getFailCount()>0)
                                build.setResult(Result.UNSTABLE);
                            build.registerAsProjectAction(new FactoryImpl());
                            return result.getFailCount();
                        }
                    });
                
                // if surefire plugin is going to kill maven because of a test failure,
                // intercept that (or otherwise build will be marked as failure)
                if(failCount>0 && error instanceof MojoFailureException) {
                    MavenBuilder.markAsSuccess = true;
                }
                // TODO currenlty error is empty : will be here with maven 3.0.2+
                if(failCount>0) {
                    Maven3Builder.markAsSuccess = true;
                }
165
            }
166 167 168 169
        }

        return true;
    }
170 171 172 173 174 175
    
    /**
     * Returns the appropriate FileSet for the selected baseDir
     * @param baseDir
     * @return
     */
176 177
    FileSet getFileSet(File baseDir) {
    	FileSet fs = fileSets.get(baseDir);
178 179
    	if (fs == null) {
    		fs = Util.createFileSet(baseDir, "*.xml","testng-results.xml,testng-failed.xml");
180 181 182 183
    		FileSet previous = fileSets.putIfAbsent(baseDir, fs);
    		if (previous != null) {
    		    return previous;
    		}
184 185 186 187 188 189 190 191
    	}
    	
    	return fs;
    }
    
    /**
     * Add checked files to the exclude list of the fileSet
     */
192
    private void rememberCheckedFiles(File baseDir, String[] reportFiles) {
193 194
    	FileSet fileSet = getFileSet(baseDir);
    	
195
    	for (String file : reportFiles) {
196
    		fileSet.createExclude().setName(file);
197 198
    	}
    }
199

200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215
    /**
     * Up to 1.372, there was a bug that causes Hudson to persist {@link SurefireArchiver} with the entire test result
     * in it. If we are loading those, fix it up in memory to reduce the memory footprint.
     *
     * It'd be nice we can save the record to remove problematic portion, but that might have
     * additional side effect.
     */
    public static void fixUp(List<MavenProjectActionBuilder> builders) {
        if (builders==null) return;
        for (ListIterator<MavenProjectActionBuilder> itr = builders.listIterator(); itr.hasNext();) {
            MavenProjectActionBuilder b =  itr.next();
            if (b instanceof SurefireArchiver)
                itr.set(new FactoryImpl());
        }
    }

216 217 218 219 220 221 222
    /**
     * Part of the serialization data attached to {@link MavenBuild}.
     */
    static final class FactoryImpl implements MavenProjectActionBuilder {
        public Collection<? extends Action> getProjectActions(MavenModule module) {
            return Collections.singleton(new TestResultProjectAction(module));
        }
223 224
    }

225
    private boolean isSurefireTest(MojoInfo mojo) {
226
        if ((!mojo.is("com.sun.maven", "maven-junit-plugin", "test"))
227
            && (!mojo.is("org.sonatype.flexmojos", "flexmojos-maven-plugin", "test-run"))
B
Benjamin Cabé 已提交
228
            && (!mojo.is("org.eclipse.tycho", "tycho-surefire-plugin", "test"))
229
            && (!mojo.is("org.sonatype.tycho", "maven-osgi-test-plugin", "test"))
230
            && (!mojo.is("org.codehaus.mojo", "gwt-maven-plugin", "test"))
231 232
            && (!mojo.is("org.apache.maven.plugins", "maven-surefire-plugin", "test"))
            && (!mojo.is("org.apache.maven.plugins", "maven-failsafe-plugin", "integration-test")))
K
kohsuke 已提交
233 234 235
            return false;

        try {
236 237 238
            if (mojo.is("org.apache.maven.plugins", "maven-surefire-plugin", "test")) {
                Boolean skip = mojo.getConfigurationValue("skip", Boolean.class);
                if (((skip != null) && (skip))) {
K
TAB->WS  
kohsuke 已提交
239 240
                    return false;
                }
241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256
                
                if (mojo.pluginName.version.compareTo("2.3") >= 0) {
                    Boolean skipExec = mojo.getConfigurationValue("skipExec", Boolean.class);
                    
                    if (((skipExec != null) && (skipExec))) {
                        return false;
                    }
                }
                
                if (mojo.pluginName.version.compareTo("2.4") >= 0) {
                    Boolean skipTests = mojo.getConfigurationValue("skipTests", Boolean.class);
                    
                    if (((skipTests != null) && (skipTests))) {
                        return false;
                    }
                }
K
TAB->WS  
kohsuke 已提交
257
            }
258
            else if (mojo.is("com.sun.maven", "maven-junit-plugin", "test")) {
K
TAB->WS  
kohsuke 已提交
259
                Boolean skipTests = mojo.getConfigurationValue("skipTests", Boolean.class);
260
                
K
TAB->WS  
kohsuke 已提交
261 262 263 264
                if (((skipTests != null) && (skipTests))) {
                    return false;
                }
            }
265
            else if (mojo.is("org.sonatype.flexmojos", "flexmojos-maven-plugin", "test-run")) {
266 267 268 269
                Boolean skipTests = mojo.getConfigurationValue("skipTest", Boolean.class);
                if (((skipTests != null) && (skipTests))) {
                    return false;
                }
270 271
	        } else if (mojo.is("org.sonatype.tycho", "maven-osgi-test-plugin", "test")) {
                Boolean skipTests = mojo.getConfigurationValue("skipTest", Boolean.class);
B
Benjamin Cabé 已提交
272 273 274 275 276
                if (((skipTests != null) && (skipTests))) {
                    return false;
                }
	        } else if (mojo.is("org.eclipse.tycho", "tycho-surefire-plugin", "test")) {
                Boolean skipTests = mojo.getConfigurationValue("skipTest", Boolean.class);
277 278 279 280
                if (((skipTests != null) && (skipTests))) {
                    return false;
                }
            }
J
jasonchaffee 已提交
281

K
kohsuke 已提交
282 283 284
        } catch (ComponentConfigurationException e) {
            return false;
        }
K
TAB->WS  
kohsuke 已提交
285

J
jasonchaffee 已提交
286
        return true;
287
    }
288 289 290 291 292 293 294
    
    public boolean maven3orLater(String mavenVersion) {
        // null or empty so false !
        if (StringUtils.isBlank( mavenVersion )) {
            return false;
        }
        return new ComparableVersion (mavenVersion).compareTo( new ComparableVersion ("3.0") ) >= 0;
295 296 297 298 299 300 301 302
    }
    
    // I'm not sure if SurefireArchiver is actually ever (de-)serialized,
    // but just to be sure, set fileSets here
    protected Object readResolve() {
        fileSets = new ConcurrentHashMap<File,FileSet>();
        return this;
    }
303

304
    @Extension
305 306
    public static final class DescriptorImpl extends MavenReporterDescriptor {
        public String getDisplayName() {
K
i18n  
kohsuke 已提交
307
            return Messages.SurefireArchiver_DisplayName();
308 309 310 311 312 313
        }

        public SurefireArchiver newAutoInstance(MavenModule module) {
            return new SurefireArchiver();
        }
    }
314 315

    private static final long serialVersionUID = 1L;
316
}