Java tutorial
/* * The MIT License * * Copyright (c) <2012> <Bruno P. Kinoshita> * * 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. */ package jenkins.plugins.testopia; import hudson.AbortException; import hudson.EnvVars; import hudson.Extension; import hudson.Launcher; import hudson.model.Action; import hudson.model.BuildListener; import hudson.model.EnvironmentContributingAction; import hudson.model.Result; import hudson.model.AbstractBuild; import hudson.model.AbstractProject; import hudson.tasks.BuildStep; import hudson.tasks.Builder; import java.io.IOException; import java.net.URL; import java.util.List; import java.util.StringTokenizer; import java.util.logging.Level; import java.util.logging.Logger; import jenkins.plugins.testopia.result.ResultSeeker; import jenkins.plugins.testopia.result.ResultSeekerException; import jenkins.plugins.testopia.result.TestCaseWrapper; import jenkins.plugins.testopia.util.Messages; import org.apache.commons.lang.StringUtils; import org.kohsuke.stapler.DataBoundConstructor; import org.mozilla.testopia.TestopiaAPI; import org.mozilla.testopia.model.TestRun; /** * Testopia Builder. * @author Bruno P. Kinoshita - http://www.kinoshita.eti.br * @since 0.1 */ public class TestopiaBuilder extends Builder { // Used for HTTP basic auth private static final String BASIC_HTTP_PASSWORD = "basicPassword"; private static final Logger LOGGER = Logger.getLogger("jenkins.plugins.testopia"); /** * Testopia installation name. */ protected final String testopiaInstallationName; /** * Testopia test run ID. */ protected final Integer testRunId; /** * List of build steps that are executed only once per job execution. */ protected final List<BuildStep> singleBuildSteps; /** * List of build steps that are executed before iterating all test cases. */ protected final List<BuildStep> beforeIteratingAllTestCasesBuildSteps; /** * List of build steps that are executed for each test case. */ protected final List<BuildStep> iterativeBuildSteps; /** * List of build steps that are executed after iterating all test cases. */ protected final List<BuildStep> afterIteratingAllTestCasesBuildSteps; /** * If <code>true</code> and a test fails, the build is marked as FAILURE. Otherwise UNSTABLE. */ protected final Boolean failedTestsMarkBuildAsFailure; /** * List of result seeking strategies. */ protected List<ResultSeeker> resultSeekers; /** * Le descriptor. */ @Extension public static final TestopiaBuilderDescriptor DESCRIPTOR = new TestopiaBuilderDescriptor(); /** * This constructor is bound to a stapler request. The parameters are * passed from Jenkins UI. * @param testopiaInstallationName * @param testRunId * @param singleBuildSteps * @param beforeIteratingAllTestCasesBuildSteps * @param iterativeBuildSteps * @param afterIteratingAllTestCasesBuildSteps * @param failedTestsMarkBuildAsFailure * @param resultSeekers */ @DataBoundConstructor public TestopiaBuilder(String testopiaInstallationName, Integer testRunId, List<BuildStep> singleBuildSteps, List<BuildStep> beforeIteratingAllTestCasesBuildSteps, List<BuildStep> iterativeBuildSteps, List<BuildStep> afterIteratingAllTestCasesBuildSteps, Boolean failedTestsMarkBuildAsFailure, List<ResultSeeker> resultSeekers) { this.testopiaInstallationName = testopiaInstallationName; this.testRunId = testRunId; this.singleBuildSteps = singleBuildSteps; this.beforeIteratingAllTestCasesBuildSteps = beforeIteratingAllTestCasesBuildSteps; this.iterativeBuildSteps = iterativeBuildSteps; this.afterIteratingAllTestCasesBuildSteps = afterIteratingAllTestCasesBuildSteps; this.failedTestsMarkBuildAsFailure = failedTestsMarkBuildAsFailure; this.resultSeekers = resultSeekers; } /** * @return the testopiaInstallationName */ public String getTestopiaInstallationName() { return testopiaInstallationName; } /** * @return the testRunId */ public Integer getTestRunId() { return testRunId; } /** * @return the singleBuildSteps */ public List<BuildStep> getSingleBuildSteps() { return singleBuildSteps; } /** * @return the beforeIteratingAllTestCasesBuildSteps */ public List<BuildStep> getBeforeIteratingAllTestCasesBuildSteps() { return beforeIteratingAllTestCasesBuildSteps; } /** * @return the iterativeBuildSteps */ public List<BuildStep> getIterativeBuildSteps() { return iterativeBuildSteps; } /** * @return the afterIteratingAllTestCasesBuildSteps */ public List<BuildStep> getAfterIteratingAllTestCasesBuildSteps() { return afterIteratingAllTestCasesBuildSteps; } /** * @return the failedTestsMarkBuildAsFailure */ public Boolean getFailedTestsMarkBuildAsFailure() { return failedTestsMarkBuildAsFailure; } /** * @return the resultSeekers */ public List<ResultSeeker> getResultSeekers() { return resultSeekers; } /** * @param resultSeekers the resultSeekers to set */ public void setResultSeekers(List<ResultSeeker> resultSeekers) { this.resultSeekers = resultSeekers; } /* (non-Javadoc) * @see hudson.tasks.BuildStepCompatibilityLayer#getProjectAction(hudson.model.AbstractProject) */ @Override public Action getProjectAction(AbstractProject<?, ?> project) { return new TestopiaProjectAction(project); } /** * {@inheritDoc} * * Executes Testopia automated tests. */ @Override public boolean perform(AbstractBuild<?, ?> build, Launcher launcher, BuildListener listener) throws InterruptedException, IOException { listener.getLogger().println(Messages.Testopia_Builder_Connecting()); TestopiaInstallation installation = DESCRIPTOR.getInstallationByName(this.testopiaInstallationName); if (installation == null) { throw new AbortException(Messages.Testopia_Builder_InvalidInstallation()); } if (StringUtils.isNotBlank(installation.getProperties())) { listener.getLogger().println(Messages.Testopia_Builder_PreparingConnectionProperties()); setProperties(installation.getProperties(), listener); } TestopiaAPI api = new TestopiaAPI(new URL(installation.getUrl())); String token = ""; try { token = api.login(installation.getUsername(), installation.getPassword()); } catch (Exception e) { e.printStackTrace(listener.getLogger()); throw new AbortException(e.getMessage()); } //TestRun testRun = testRunSvc.get(this.getTestRunId()); if (LOGGER.isLoggable(Level.FINE)) { LOGGER.log(Level.FINE, Messages.Testopia_Builder_Filtering()); } TestRun testCaseRun = api.getTestRun(this.getTestRunId(), token); TestopiaSite testopia = new TestopiaSite(api); TestCaseWrapper[] testCases = testopia.getTestCases(testCaseRun, api.getTestCases(this.getTestRunId())); if (LOGGER.isLoggable(Level.FINE)) { for (TestCaseWrapper tc : testCases) { LOGGER.log(Level.FINE, Messages.Testopia_Builder_AutomatedTestCase(tc.getId(), tc.getSummary())); } } // sort and filter test cases listener.getLogger().println(Messages.Testopia_Builder_SingleBuildSteps()); this.executeSingleBuildSteps(build, launcher, listener); listener.getLogger().println(Messages.Testopia_Builder_IterativeBuildSteps()); this.executeIterativeBuildSteps(testCases, build, launcher, listener); // Here we search for test results. The return if a wrapped Test Case // that // contains attachments, platform and notes. try { listener.getLogger().println(Messages.Testopia_Builder_Seeking()); if (getResultSeekers() != null) { for (ResultSeeker resultSeeker : getResultSeekers()) { LOGGER.log(Level.INFO, Messages .Testopia_Builder_SeekingDetails(resultSeeker.getDescriptor().getDisplayName())); resultSeeker.seek(testCases, build, launcher, listener, testopia); } } } catch (ResultSeekerException trse) { trse.printStackTrace(listener.fatalError(trse.getMessage())); throw new AbortException(Messages.Testopia_Builder_SeekingError(trse.getMessage())); } // This report is used to generate the graphs and to store the list of // test cases with each found status. final Report report = testopia.getReport(); listener.getLogger().println(Messages.Testopia_Builder_Found(report.getTestsTotal())); final TestopiaResult result = new TestopiaResult(report, build); final TestopiaBuildAction buildAction = new TestopiaBuildAction(build, result); build.addAction(buildAction); if (report.getFailed() > 0) { if (this.failedTestsMarkBuildAsFailure != null && this.failedTestsMarkBuildAsFailure) { build.setResult(Result.FAILURE); } else { build.setResult(Result.UNSTABLE); } } LOGGER.log(Level.INFO, Messages.Testopia_Builder_Finished()); // end return Boolean.TRUE; } /** * Executes the list of single build steps. * * @param build * Jenkins build. * @param launcher * @param listener * @throws IOException * @throws InterruptedException */ protected void executeSingleBuildSteps(AbstractBuild<?, ?> build, Launcher launcher, BuildListener listener) throws IOException, InterruptedException { if (singleBuildSteps != null) { for (BuildStep b : singleBuildSteps) { boolean success = b.perform(build, launcher, listener); if (!success) { build.setResult(Result.UNSTABLE); } } } } /** * <p> * Executes iterative build steps. For each automated test case found in the * array of automated test cases, this method executes the iterative builds * steps using Jenkins objects. * </p> * * @param testCases * array of automated test cases * @param launcher * @param listener * @throws InterruptedException * @throws IOException */ protected void executeIterativeBuildSteps(TestCaseWrapper[] testCases, AbstractBuild<?, ?> build, Launcher launcher, BuildListener listener) throws IOException, InterruptedException { if (beforeIteratingAllTestCasesBuildSteps != null) { for (BuildStep b : beforeIteratingAllTestCasesBuildSteps) { final boolean success = b.perform(build, launcher, listener); if (!success) { build.setResult(Result.UNSTABLE); } } } if (iterativeBuildSteps != null) { for (TestCaseWrapper automatedTestCase : testCases) { if (automatedTestCase == null) { continue; } if (LOGGER.isLoggable(Level.FINE)) { LOGGER.log(Level.FINE, Messages.Testopia_Builder_IterativeBuildStep()); LOGGER.log(Level.FINE, Messages.Testopia_Builder_AutomatedTestCase(automatedTestCase.getId(), automatedTestCase.getScript())); } final EnvVars iterativeEnvVars = Utils.buildTestCaseEnvVars(automatedTestCase); build.addAction(new EnvironmentContributingAction() { public void buildEnvVars(AbstractBuild<?, ?> build, EnvVars env) { env.putAll(iterativeEnvVars); } public String getUrlName() { return null; } public String getIconFileName() { return null; } public String getDisplayName() { return null; } }); for (BuildStep b : iterativeBuildSteps) { final boolean success = b.perform(build, launcher, listener); if (!success) { build.setResult(Result.UNSTABLE); } } } } if (afterIteratingAllTestCasesBuildSteps != null) { for (BuildStep b : afterIteratingAllTestCasesBuildSteps) { final boolean success = b.perform(build, launcher, listener); if (!success) { build.setResult(Result.UNSTABLE); } } } } /** * <p>Define properties. Following is the list of available properties.</p> * * <ul> * <li>xmlrpc.basicEncoding</li> * <li>xmlrpc.basicPassword</li> * <li>xmlrpc.basicUsername</li> * <li>xmlrpc.connectionTimeout</li> * <li>xmlrpc.contentLengthOptional</li> * <li>xmlrpc.enabledForExceptions</li> * <li>xmlrpc.encoding</li> * <li>xmlrpc.gzipCompression</li> * <li>xmlrpc.gzipRequesting</li> * <li>xmlrpc.replyTimeout</li> * <li>xmlrpc.userAgent</li> * </ul> * * @param properties List of comma separated properties * @param listener Jenkins Build listener */ public static void setProperties(String properties, BuildListener listener) { if (StringUtils.isNotBlank(properties)) { final StringTokenizer tokenizer = new StringTokenizer(properties, ","); if (tokenizer.countTokens() > 0) { while (tokenizer.hasMoreTokens()) { String systemProperty = tokenizer.nextToken(); maybeAddSystemProperty(systemProperty, listener); } } } } /** * Maybe adds a system property if it is in format <key>=<value>. * * @param systemProperty System property entry in format <key>=<value>. * @param listener Jenkins Build listener */ public static void maybeAddSystemProperty(String systemProperty, BuildListener listener) { final StringTokenizer tokenizer = new StringTokenizer(systemProperty, "=:"); if (tokenizer.countTokens() == 2) { final String key = tokenizer.nextToken(); final String value = tokenizer.nextToken(); if (StringUtils.isNotBlank(key) && StringUtils.isNotBlank(value)) { if (key.contains(BASIC_HTTP_PASSWORD)) { listener.getLogger().println(Messages.Testopia_Builder_Password(key)); } else { listener.getLogger().println(Messages.Testopia_Builder_Setting(key, value)); } try { System.setProperty(key, value); } catch (SecurityException se) { se.printStackTrace(listener.getLogger()); } } } } }