com.piketec.jenkins.plugins.tpt.TptPluginSingleJobExecutor.java Source code

Java tutorial

Introduction

Here is the source code for com.piketec.jenkins.plugins.tpt.TptPluginSingleJobExecutor.java

Source

/*
 * The MIT License (MIT)
 * 
 * Copyright (c) 2018 PikeTec GmbH
 * 
 * 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 com.piketec.jenkins.plugins.tpt;

import java.io.File;
import java.io.IOException;
import java.util.List;
import java.util.concurrent.TimeUnit;

import org.apache.commons.lang.StringUtils;

import com.piketec.jenkins.plugins.tpt.TptLog.LogLevel;
import com.piketec.jenkins.plugins.tpt.Configuration.JenkinsConfiguration;

import hudson.FilePath;
import hudson.Launcher;
import hudson.Proc;
import hudson.model.AbstractBuild;
import hudson.model.BuildListener;

/**
 * Executes the TPT test cases via command line on a single node.
 */
class TptPluginSingleJobExecutor {

    private TptLogger logger;

    private Launcher launcher;

    private AbstractBuild<?, ?> build;

    private BuildListener listener;

    private FilePath[] exePaths;

    private String arguments;

    private List<JenkinsConfiguration> executionConfigs;

    private String jUnitXmlPath;

    private LogLevel jUnitLogLevel;

    private boolean enableJunit;

    /**
     * @param build
     *          used to get the workspace
     * @param launcher
     *          used to execute a process
     * @param listener
     *          to join TPT with a given timeout
     * @param exePaths
     *          the paths to the Tpt Executables
     * @param arguments
     *          commandline arguments for running tpt from the commandline
     * @param executionConfigs
     *          all the jenkins configurations given in the descriptor, used to get the
     *          Files,Execution Configuration, test Set, testDataDir, reportDir,etc
     * @param jUnitXmlPath
     *          the path where the jUnit XML is going to be created
     * @param jUnitLogLevel
     * @param enableJunit
     *          to know if is necessary to generate the jUnit XML
     */
    TptPluginSingleJobExecutor(AbstractBuild<?, ?> build, Launcher launcher, BuildListener listener,
            FilePath[] exePaths, String arguments, List<JenkinsConfiguration> executionConfigs, String jUnitXmlPath,
            LogLevel jUnitLogLevel, boolean enableJunit) {
        logger = new TptLogger(listener.getLogger());
        this.launcher = launcher;
        this.build = build;
        this.listener = listener;
        this.exePaths = exePaths;
        this.arguments = arguments;
        this.executionConfigs = executionConfigs;
        this.jUnitXmlPath = jUnitXmlPath;
        this.jUnitLogLevel = jUnitLogLevel;
        this.enableJunit = enableJunit;
    }

    /**
     * It looks for the tpt installation . Then prepares the test- and data directories. After that it
     * creates a command ( @see buildCommand ) in order to execute Tpt from the commandline. Then it
     * runs that command through the launcher and publish the Junit XML if necessary.
     * 
     * @return true if the execution from the tpt file was successful.
     */
    boolean execute() {
        boolean success = true;
        FilePath workspace = build.getWorkspace();
        File workspaceDir;
        try {
            workspaceDir = Utils.getWorkspaceDir(workspace, logger);
        } catch (InterruptedException e) {
            logger.interrupt(e.getMessage());
            return false;
        }
        // use first found (existing) TPT installation
        FilePath exeFile = null;
        for (FilePath f : exePaths) {
            try {
                if (f.exists()) {
                    exeFile = f;
                    break;
                }
            } catch (IOException e) {
                // NOP, just try next file
            } catch (InterruptedException e) {
                logger.interrupt(e.getMessage());
                return false;
            }
        }
        if (exeFile == null) {
            logger.error("No TPT installation found");
            return false;
        }
        // execute the sub-configuration
        for (JenkinsConfiguration ec : executionConfigs) {
            if (ec.isEnableTest()) {
                String testdataDir = Utils.getGeneratedTestDataDir(ec);
                FilePath testDataPath = new FilePath(build.getWorkspace(), testdataDir);
                String reportDir = Utils.getGeneratedReportDir(ec);
                FilePath reportPath = new FilePath(build.getWorkspace(), reportDir);
                File tptFile = Utils.getAbsolutePath(workspaceDir, new File(ec.getTptFile()));
                String configurationName = ec.getConfiguration();
                String tesSet = ec.getTestSet();
                logger.info("*** Running TPT-File \"" + tptFile + //
                        "\" with configuration \"" + configurationName + "\" now. ***");
                if (Utils.createParentDir(new File(testdataDir), workspace)
                        && Utils.createParentDir(new File(reportDir), workspace)) {
                    String cmd = buildCommand(exeFile, arguments, tptFile, testDataPath.getRemote(),
                            reportPath.getRemote(), configurationName, tesSet);
                    try {
                        // run the test...
                        boolean successOnlyForOneConfig = launchTPT(launcher, listener, cmd, ec.getTimeout());
                        success &= successOnlyForOneConfig;
                        if (successOnlyForOneConfig) {
                            TPTBuildStepEntries.addEntry(ec, build);
                        }
                        if (enableJunit) {
                            // transform TPT results into JUnit results
                            logger.info("*** Publishing results now ***");
                            Utils.publishAsJUnitResults(workspace, ec, testDataPath, jUnitXmlPath, jUnitLogLevel,
                                    logger);
                        }
                    } catch (IOException e) {
                        logger.error(e.getMessage());
                        success = false;
                        // continue with next config in case of I/O error
                    } catch (InterruptedException e) {
                        logger.interrupt(e.getMessage());
                        return false;
                    }
                } else {
                    logger.error("Failed to create parent directories for " + testdataDir + " and/or " + reportDir);
                    success = false;
                }
            }
        }
        return success;
    }

    /**
     * Just creates the command line string to start the TPT execution.
     * 
     * @param exeFile
     *          Path to tpt.exe
     * @param arguments
     *          arguments for TPT
     * @param tptFile
     *          TPT file to load
     * @param dataDir
     *          directory where TPT will store the execution data
     * @param reportDir
     *          directory where TPT will create the report
     * @param configurationName
     *          the name of the execution configuration to execute
     * @return The concatenated string to start the test execution via command line.
     */
    private String buildCommand(FilePath exeFile, String arguments, File tptFile, String dataDir, String reportDir,
            String configurationName, String testSet) {
        StringBuilder cmd = new StringBuilder();
        String exeString = exeFile.getRemote();
        // surround path with ""
        if (!exeString.startsWith("\"")) {
            cmd.append('"');
        }
        cmd.append(exeString);
        if (!exeString.endsWith("\"")) {
            cmd.append('"');
        }
        cmd.append(' ');
        cmd.append(arguments);
        cmd.append(' ');
        String tptFileString = tptFile.toString();
        // surround path with ""
        if (!tptFileString.startsWith("\"")) {
            cmd.append('"');
        }
        cmd.append(tptFileString);
        if (!tptFileString.endsWith("\"")) {
            cmd.append('"');
        }
        cmd.append(' ');
        // surround name with ""
        if (!configurationName.startsWith("\"")) {
            cmd.append('"');
        }
        cmd.append(configurationName);
        if (!configurationName.endsWith("\"")) {
            cmd.append('"');
        }
        if (StringUtils.isNotEmpty(testSet)) {
            cmd.append(" --testSet ");
            // surround path with ""
            if (!testSet.startsWith("\"")) {
                cmd.append('"');
            }
            cmd.append(testSet);
            if (!testSet.endsWith("\"")) {
                cmd.append('"');
            }
            logger.info("Running " + testSet);
        }
        cmd.append(" --dataDir ");
        // surround path with ""
        if (!dataDir.startsWith("\"")) {
            cmd.append('"');
        }
        cmd.append(dataDir);
        if (!dataDir.endsWith("\"")) {
            cmd.append('"');
        }
        cmd.append(" --reportDir ");
        // surround path with ""
        if (!reportDir.startsWith("\"")) {
            cmd.append('"');
        }
        cmd.append(reportDir);
        if (!reportDir.endsWith("\"")) {
            cmd.append('"');
        }
        return cmd.toString();
    }

    /**
     * Starts TPT via command line and executes the given tests.
     * 
     * @param launcher
     *          to start the process
     * @param listener
     *          to join TPT with a given timeout
     * @param cmd
     *          The command to execute via command line
     * @param timeout
     *          The maximum allowed runtime for TPT.
     * @throws InterruptedException
     * @throws IOException
     */
    private boolean launchTPT(Launcher launcher, BuildListener listener, String cmd, long timeout)
            throws InterruptedException, IOException {
        boolean exitCodeWasNull = true;
        logger.info("Launching \"" + cmd + "\"");
        Launcher.ProcStarter starter = launcher.new ProcStarter();
        starter.cmdAsSingleString(cmd);
        starter.stdout(logger.getLogger());
        starter.stderr(logger.getLogger());
        Proc tpt = null;
        try {
            tpt = starter.start();
            if (timeout <= 0) {
                timeout = JenkinsConfiguration.DescriptorImpl.getDefaultTimeout();
            }
            logger.info("Waiting for TPT to complete. Timeout: " + timeout + "h");
            int exitcode = tpt.joinWithTimeout(timeout, TimeUnit.HOURS, listener);
            if (exitcode != 0) {
                logger.error("TPT process stops with exit code " + exitcode);
                exitCodeWasNull = false;
            }
        } catch (IOException e) {
            throw new IOException("TPT launch error: " + e.getMessage());
        } catch (InterruptedException e) {
            try {
                tpt.kill();
            } catch (IOException | InterruptedException e1) {
                throw new IOException(
                        "TPT launch error: Interrupt requested, but cannot kill the TPT process. Please kill it manually.");
            }
            throw e;
        }
        return exitCodeWasNull;
    }

}