hudson.plugins.testlink.AbstractTestLinkBuilder.java Source code

Java tutorial

Introduction

Here is the source code for hudson.plugins.testlink.AbstractTestLinkBuilder.java

Source

/* 
 * The MIT License
 * 
 * Copyright (c) 2010 Bruno P. Kinoshita <http://www.kinoshita.eti.br>
 * 
 * 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 hudson.plugins.testlink;

import hudson.EnvVars;
import hudson.Util;
import hudson.model.Action;
import hudson.model.AbstractProject;
import hudson.plugins.testlink.result.ResultSeeker;
import hudson.plugins.testlink.util.ExecutionOrderComparator;
import hudson.tasks.BuildStep;
import hudson.tasks.Builder;
import hudson.util.VariableResolver;

import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.StringTokenizer;

import org.apache.commons.lang.StringUtils;

import br.eti.kinoshita.testlinkjavaapi.constants.ExecutionStatus;

/**
 * Contains basic logic for a Builder for TestLink plug-in. This class was 
 * created to reduce complexity and reduce the length of the code present 
 * in the Builder itself.
 * 
 * @author Bruno P. Kinoshita - http://www.kinoshita.eti.br
 * @since 2.4
 */
public class AbstractTestLinkBuilder extends Builder {

    /* --- Job properties --- */
    /**
     * Comma constant for custom fields separated with delimiter.
     */
    private static final String COMMA = ",";
    /**
     * The name of the TestLink installation.
     */
    protected final String testLinkName;
    /**
     * The name of the Test Project.
     */
    protected final String testProjectName;
    /**
     * The name of the Test Plan.
     */
    protected final String testPlanName;
    /**
     * The name of the Build.
     */
    protected String buildName;
    /**
     * The platform name.
     */
    protected final String platformName;
    /**
     * Comma separated list of custom fields to download from TestLink.
     */
    protected final String customFields;

    /**
     * Tests that have not been run.
     */
    protected final Boolean executionStatusNotRun;

    /**
     * Tests that have passed.
     */
    protected final Boolean executionStatusPassed;

    /**
     * Tests that have failed.
     */
    protected final Boolean executionStatusFailed;

    /**
     * Tests that are blocked.
     */
    protected final Boolean executionStatusBlocked;

    /**
     * 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 this property is true, not more build steps are executed for this 
     * Build.
     */
    protected final Boolean transactional;

    /**
     * If the plug-in should mark the Build in Jenkins as failure if it 
     * contains failed tests.
     */
    protected final Boolean failedTestsMarkBuildAsFailure;

    /**
     * Fail the build if no test results are present.
     */
    protected final Boolean failIfNoResults;

    /**
     * Create failure if any of the tests are set as not-run
     */
    protected final Boolean failOnNotRun;

    /*
     * Test life cycle commands. With these hooks you can execute command before 
     * the single test command, after the single test command, before the 
     * iterative test command and after the test command.
     */

    /* --- Other members --- */

    /**
     * Used to sort test cases marked as automated.
     */
    protected final ExecutionOrderComparator executionOrderComparator = new ExecutionOrderComparator();

    /**
     * Flag to check if any failure happened.
     */
    protected boolean failure = false;

    /**
     * Results seekers.
     */
    private List<ResultSeeker> resultSeekers;

    /**
     * Kept for backward compatibility. Don't add new fields here.
     * @deprecated
     */
    public AbstractTestLinkBuilder(String testLinkName, String testProjectName, String testPlanName,
            String buildName, String customFields, Boolean executionStatusNotRun, Boolean executionStatusPassed,
            Boolean executionStatusFailed, Boolean executionStatusBlocked, List<BuildStep> singleBuildSteps,
            List<BuildStep> beforeIteratingAllTestCasesBuildSteps, List<BuildStep> iterativeBuildSteps,
            List<BuildStep> afterIteratingAllTestCasesBuildSteps, Boolean transactional,
            Boolean failedTestsMarkBuildAsFailure, Boolean failIfNoResults, List<ResultSeeker> resultSeekers) {
        super();
        this.testLinkName = testLinkName;
        this.testProjectName = testProjectName;
        this.testPlanName = testPlanName;
        this.platformName = null;
        this.buildName = buildName;
        this.customFields = customFields;
        this.executionStatusNotRun = executionStatusNotRun;
        this.executionStatusPassed = executionStatusPassed;
        this.executionStatusFailed = executionStatusFailed;
        this.executionStatusBlocked = executionStatusBlocked;
        this.singleBuildSteps = singleBuildSteps;
        this.beforeIteratingAllTestCasesBuildSteps = beforeIteratingAllTestCasesBuildSteps;
        this.iterativeBuildSteps = iterativeBuildSteps;
        this.afterIteratingAllTestCasesBuildSteps = afterIteratingAllTestCasesBuildSteps;
        this.transactional = transactional;
        this.failedTestsMarkBuildAsFailure = failedTestsMarkBuildAsFailure;
        this.failIfNoResults = failIfNoResults;
        this.resultSeekers = resultSeekers;
        this.failOnNotRun = false;
    }

    /**
      * Kept for backward compatibility. Don't add new fields here.
      * @deprecated
      */
    public AbstractTestLinkBuilder(String testLinkName, String testProjectName, String testPlanName,
            String buildName, String customFields, Boolean executionStatusNotRun, Boolean executionStatusPassed,
            Boolean executionStatusFailed, Boolean executionStatusBlocked, List<BuildStep> singleBuildSteps,
            List<BuildStep> beforeIteratingAllTestCasesBuildSteps, List<BuildStep> iterativeBuildSteps,
            List<BuildStep> afterIteratingAllTestCasesBuildSteps, Boolean transactional,
            Boolean failedTestsMarkBuildAsFailure, Boolean failIfNoResults, Boolean failOnNotRun,
            List<ResultSeeker> resultSeekers) {
        super();
        this.testLinkName = testLinkName;
        this.testProjectName = testProjectName;
        this.testPlanName = testPlanName;
        this.platformName = null;
        this.buildName = buildName;
        this.customFields = customFields;
        this.executionStatusNotRun = executionStatusNotRun;
        this.executionStatusPassed = executionStatusPassed;
        this.executionStatusFailed = executionStatusFailed;
        this.executionStatusBlocked = executionStatusBlocked;
        this.singleBuildSteps = singleBuildSteps;
        this.beforeIteratingAllTestCasesBuildSteps = beforeIteratingAllTestCasesBuildSteps;
        this.iterativeBuildSteps = iterativeBuildSteps;
        this.afterIteratingAllTestCasesBuildSteps = afterIteratingAllTestCasesBuildSteps;
        this.transactional = transactional;
        this.failedTestsMarkBuildAsFailure = failedTestsMarkBuildAsFailure;
        this.failIfNoResults = failIfNoResults;
        this.failOnNotRun = failOnNotRun;
        this.resultSeekers = resultSeekers;
    }

    /**
     * This constructor is bound to a stapler request. All parameters here are 
     * passed by Jenkins.
     * 
     * @param testLinkName TestLink Installation name.
     * @param testProjectName TestLink Test Project name.
     * @param testPlanName TestLink Test Plan name.
     * @param platformName TestLink Platform name.
     * @param buildName TestLink Build name.
     * @param customFields TestLink comma-separated list of Custom Fields.
     * @param keyCustomField Key custom field.
     * @param singleBuildSteps List of build steps to execute once for all automated test cases.
     * @param beforeIteratingAllTestCasesBuildSteps Command executed before iterating all test cases.
     * @param iterativeBuildSteps List of build steps to execute for each Automated Test Case.
     * @param afterIteratingAllTestCasesBuildSteps Command executed after iterating all test cases.
     * @param transactional Whether the build's execution is transactional or not.
     * @param failedTestsMarkBuildAsFailure Whether failed tests mark the build as failure or not.
     * @param failIfNoResults If true marks the build as FAILURE.
     * @param resultSeekers List of result seekers.
     */
    public AbstractTestLinkBuilder(String testLinkName, String testProjectName, String testPlanName,
            String platformName, String buildName, String customFields, Boolean executionStatusNotRun,
            Boolean executionStatusPassed, Boolean executionStatusFailed, Boolean executionStatusBlocked,
            List<BuildStep> singleBuildSteps, List<BuildStep> beforeIteratingAllTestCasesBuildSteps,
            List<BuildStep> iterativeBuildSteps, List<BuildStep> afterIteratingAllTestCasesBuildSteps,
            Boolean transactional, Boolean failedTestsMarkBuildAsFailure, Boolean failIfNoResults,
            Boolean failOnNotRun, List<ResultSeeker> resultSeekers) {
        super();
        this.testLinkName = testLinkName;
        this.testProjectName = testProjectName;
        this.testPlanName = testPlanName;
        this.platformName = platformName;
        this.buildName = buildName;
        this.customFields = customFields;
        this.executionStatusNotRun = executionStatusNotRun;
        this.executionStatusPassed = executionStatusPassed;
        this.executionStatusFailed = executionStatusFailed;
        this.executionStatusBlocked = executionStatusBlocked;
        this.singleBuildSteps = singleBuildSteps;
        this.beforeIteratingAllTestCasesBuildSteps = beforeIteratingAllTestCasesBuildSteps;
        this.iterativeBuildSteps = iterativeBuildSteps;
        this.afterIteratingAllTestCasesBuildSteps = afterIteratingAllTestCasesBuildSteps;
        this.transactional = transactional;
        this.failedTestsMarkBuildAsFailure = failedTestsMarkBuildAsFailure;
        this.failIfNoResults = failIfNoResults;
        this.failOnNotRun = failOnNotRun;
        this.resultSeekers = resultSeekers;
    }

    public String getTestLinkName() {
        return this.testLinkName;
    }

    public String getTestProjectName() {
        return this.testProjectName;
    }

    /**
     * Expands a text variable like BUILD-$VAR replacing the $VAR part with 
     * a environment variable that matches its name, minus $.
     * 
     * @param variableResolver Jenkins Build Variable Resolver.
     * @param envVars Jenkins Build Environment Variables.
     * @param variable Variable value (includes mask).
     * @return Expanded test project name job configuration property.
     */
    public String expandVariable(VariableResolver<String> variableResolver, EnvVars envVars, String variable) {
        return Util.replaceMacro(envVars.expand(variable), variableResolver);
    }

    public String getTestPlanName() {
        return this.testPlanName;
    }

    public String getPlatformName() {
        return this.platformName;
    }

    public String getBuildName() {
        return this.buildName;
    }

    public String getCustomFields() {
        return this.customFields;
    }

    public Boolean getExecutionStatusNotRun() {
        return executionStatusNotRun;
    }

    public Boolean getExecutionStatusPassed() {
        return executionStatusPassed;
    }

    public Boolean getExecutionStatusFailed() {
        return executionStatusFailed;
    }

    public Boolean getExecutionStatusBlocked() {
        return executionStatusBlocked;
    }

    public List<BuildStep> getSingleBuildSteps() {
        return this.singleBuildSteps;
    }

    public List<BuildStep> getBeforeIteratingAllTestCasesBuildSteps() {
        return beforeIteratingAllTestCasesBuildSteps;
    }

    public List<BuildStep> getIterativeBuildSteps() {
        return this.iterativeBuildSteps;
    }

    public List<BuildStep> getAfterIteratingAllTestCasesBuildSteps() {
        return afterIteratingAllTestCasesBuildSteps;
    }

    /**
     * Returns whether it is a transactional build or not. A transactional 
     * build stops executing once a test fails. All tests must succeed or it 
     * won't finish its execution and will mark all remaining tests with 
     * Blocked status.
     * 
     * @return If the build step should be transactional or not
     */
    public Boolean getTransactional() {
        return this.transactional;
    }

    /**
     * @return the failedTestsMarkBuildAsUnstable
     */
    public Boolean getFailedTestsMarkBuildAsUnstable() {
        return failedTestsMarkBuildAsFailure;
    }

    /**
     * @return the failIfNoResults
     */
    public Boolean getFailIfNoResults() {
        return failIfNoResults;
    }

    /**
     * @return the failOnNotRun
     */
    public Boolean getFailOnNotRun() {
        return failOnNotRun;
    }

    /**
     * @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 TestLinkProjectAction(project);
    }

    /* --- Utility methods --- */

    /**
     * Creates array of custom fields names using the Job configuration data.
     * @param variableResolver Jenkins variable resolver
     * @param envVars Jenkins environment variables
     * 
     * @return Array of custom fields names.
     */
    protected String[] createArrayOfCustomFieldsNames(final VariableResolver<String> variableResolver,
            final EnvVars envVars) {
        String[] customFieldNamesArray = new String[0];
        String customFields = expandVariable(variableResolver, envVars, this.getCustomFields());

        if (StringUtils.isNotBlank(customFields)) {
            StringTokenizer tokenizer = new StringTokenizer(customFields, COMMA);
            if (tokenizer.countTokens() > 0) {
                customFieldNamesArray = new String[tokenizer.countTokens()];
                int index = 0;
                while (tokenizer.hasMoreTokens()) {
                    String customFieldName = tokenizer.nextToken();
                    customFieldName = customFieldName.trim();
                    customFieldNamesArray[index] = customFieldName;
                    index = index + 1;
                }
            }
        }

        return customFieldNamesArray;
    }

    /**
     * Return a set of execution statuses that we are interested in. If none are
     * true, then assume that job is to run tests of all execution statuses.
     * 
     * @return a set of execution statuses
     */
    Set<ExecutionStatus> getExecutionStatuses() {
        Set<ExecutionStatus> statuses = new HashSet<ExecutionStatus>();
        if (Boolean.TRUE.equals(executionStatusNotRun)) {
            statuses.add(ExecutionStatus.NOT_RUN);
        }
        if (Boolean.TRUE.equals(executionStatusPassed)) {
            statuses.add(ExecutionStatus.PASSED);
        }
        if (Boolean.TRUE.equals(executionStatusFailed)) {
            statuses.add(ExecutionStatus.FAILED);
        }
        if (Boolean.TRUE.equals(executionStatusBlocked)) {
            statuses.add(ExecutionStatus.BLOCKED);
        }
        if (statuses.size() == 0) {
            statuses.add(ExecutionStatus.NOT_RUN);
            statuses.add(ExecutionStatus.PASSED);
            statuses.add(ExecutionStatus.FAILED);
            statuses.add(ExecutionStatus.BLOCKED);
        }
        return statuses;
    }

}