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

Java tutorial

Introduction

Here is the source code for com.piketec.jenkins.plugins.tpt.TptPluginSlaveExecutor.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.rmi.RemoteException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

import org.apache.commons.lang.StringUtils;

import com.piketec.tpt.api.ApiException;
import com.piketec.tpt.api.ExecutionConfiguration;
import com.piketec.tpt.api.ExecutionConfigurationItem;
import com.piketec.tpt.api.ExecutionStatus;
import com.piketec.tpt.api.OpenResult;
import com.piketec.tpt.api.Scenario;
import com.piketec.tpt.api.ScenarioGroup;
import com.piketec.tpt.api.ScenarioOrGroup;
import com.piketec.tpt.api.TestSet;
import com.piketec.tpt.api.TptApi;

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

/**
 * Executes one test case via TPT API.
 * 
 * @author jkuhnert, PikeTec GmbH
 */
class TptPluginSlaveExecutor {

    private TptLogger logger;

    private Launcher launcher;

    private AbstractBuild<?, ?> build;

    private BuildListener listener;

    private FilePath[] exePaths;

    private int tptPort;

    private String tptBindingName;

    private File tptFile;

    private String execCfg;

    private String testDataDir;

    private String reportDir;

    private List<String> testSetString;

    private long tptStartupWaitTime;

    private AbstractBuild masterId;

    private String testSetName;

    private FilePath masterWorkspace;

    /**
     * @param launcher
     *          passed for executing a process
     * @param build
     *          current build, used to get the workspace and for binding to the TptApi
     * @param listener
     *          for the logs
     * @param exePaths
     *          the paths to the Tpt Executables
     * @param tptPort
     *          the port for binding to the TptApi
     * @param tptBindingName
     *          the binding name used to connect to the TptApi (for the registry)
     * @param tptFile
     *          the tpt file that will be executed
     * @param execCfg
     *          the tpt execution configuration as String
     * @param testDataDir
     *          the path to the test data dir
     * @param testSet
     *          a chunk of test
     * @param tptStartupWaitTime
     *          the time it should wait before start tpt
     * @param masterId
     *          actual build, used for getting an unique id
     * @param testSetName
     *          the name of the test set if given
     * @param masterWorkspace
     *          the workspace from the master, to know where to copy the results
     */
    TptPluginSlaveExecutor(Launcher launcher, AbstractBuild<?, ?> build, BuildListener listener,
            FilePath[] exePaths, int tptPort, String tptBindingName, File tptFile, String execCfg,
            String testDataDir, String reportDir, List<String> testSet, long tptStartupWaitTime,
            AbstractBuild masterId, String testSetName, FilePath masterWorkspace) {
        this.logger = new TptLogger(listener.getLogger());
        this.launcher = launcher;
        this.build = build;
        this.listener = listener;
        this.exePaths = exePaths;
        this.tptPort = tptPort;
        this.tptBindingName = tptBindingName;
        this.tptFile = tptFile;
        this.execCfg = execCfg;
        this.testDataDir = testDataDir;
        this.reportDir = reportDir;
        this.testSetString = testSet;
        this.tptStartupWaitTime = tptStartupWaitTime;
        this.masterId = masterId;
        this.testSetName = testSetName;
        this.masterWorkspace = masterWorkspace;
    }

    /**
     * Executes a small chunks of tests. It binds to the Tpt Api , check if the given Execution
     * Configuration exists. Prepares the test- and data-directories. Creates a temporary testSet from
     * the chunk of test (if no testSet is given). Then through the tpt api executes the testCases and
     * then it copies the results to the master workspace.
     * 
     * @return true if the tpt execution has been successfully.
     */
    public boolean execute() {
        logger = new TptLogger(listener.getLogger());
        try {
            // start tpt and recieve API
            TptApi api;
            try {
                api = Utils.getTptApi(build, launcher, logger, exePaths, tptPort, tptBindingName,
                        tptStartupWaitTime);
            } catch (InterruptedException e) {
                logger.interrupt(e.getMessage());
                return false;
            }
            if (api == null) {
                return false;
            }
            // open TPT File
            OpenResult openProject = api.openProject(tptFile);
            if (openProject.getProject() == null) {
                logger.error("Could not open project:\n" + Utils.toString(openProject.getLogs(), "\n"));
                return false;
            }
            new CleanUpTask(openProject.getProject(), masterId);
            // search execution configuration by name
            Collection<ExecutionConfiguration> execConfigs = openProject.getProject().getExecutionConfigurations()
                    .getItems();
            ExecutionConfiguration config = null;
            for (ExecutionConfiguration elem : execConfigs) {
                if (elem.getName().equals(execCfg)) {
                    config = elem;
                    break;
                }
            }
            if (config == null) {
                logger.error("Could not find config");
                return false;
            }
            // adjust config to execute only the given one test case
            File oldReportDir = config.getReportDir();
            File oldTestDataDir = config.getDataDir();

            Collection<Scenario> foundScenearios = new HashSet<>();
            find(openProject.getProject().getTopLevelTestlet().getTopLevelScenarioOrGroup().getItems(),
                    testSetString, foundScenearios);
            if (foundScenearios.size() != testSetString.size()) {
                logger.error("Could only find " + foundScenearios.size() + " of " + testSetString.size() + ".");
                return false;
            }

            FilePath slaveDataDir = null;
            FilePath slaveReportDir = null;
            try {
                slaveDataDir = new FilePath(build.getWorkspace(), testDataDir).absolutize();
                if (!masterWorkspace.equals(build.getWorkspace())) {
                    logger.info("Creating and/or cleaning test data directory");
                    Utils.deleteFiles(slaveDataDir);
                }
            } catch (IOException e) {
                logger.error("Could not create or clear test data dir");
                return false;
            } catch (InterruptedException e) {
                logger.interrupt(e.getMessage());
                return false;
            }
            logger.info("Setting test data directory to " + slaveDataDir.getRemote());
            config.setDataDir(new File(slaveDataDir.getRemote()));

            try {
                slaveReportDir = new FilePath(build.getWorkspace(), reportDir).absolutize();
                if (!masterWorkspace.equals(build.getWorkspace())) {
                    logger.info("Creating and/or cleaning report directory");
                    slaveReportDir.mkdirs();
                    slaveReportDir.deleteContents();
                }
            } catch (IOException e) {
                logger.error(e.getMessage());
                config.setDataDir(oldTestDataDir);
                return false;
            } catch (InterruptedException e) {
                logger.interrupt(e.getMessage());
                config.setDataDir(oldTestDataDir);
                return false;
            }
            logger.info("Setting report directory to " + slaveReportDir.getRemote());
            config.setReportDir(new File(slaveReportDir.getRemote()));

            // store information to undo changes
            List<TestSet> oldTestSets = new ArrayList<>();
            List<TestSet> newTestSets = new ArrayList<>();
            List<ExecutionConfigurationItem> deactivated = new ArrayList<>();
            int i = 0;
            if (StringUtils.isEmpty(testSetName)) {
                for (ExecutionConfigurationItem item : config.getItems()) {
                    oldTestSets.add(item.getTestSet());
                    if (item.isActive()) {
                        Collection<Scenario> intersectionSet = intersectByHash(
                                item.getTestSet().getTestCases().getItems(), foundScenearios);
                        if (intersectionSet.isEmpty()) {
                            item.setActive(false);
                            deactivated.add(item);
                        } else {
                            String tmpTestSetName = "JENKINS Exec " + i;
                            i++;
                            logger.info("Create test set \"" + tmpTestSetName + "\" for execution of \""
                                    + remoteScenarioSetToString(intersectionSet) + "\"");
                            TestSet testSet = openProject.getProject().createTestSet(tmpTestSetName);
                            newTestSets.add(testSet);
                            for (Scenario scen : intersectionSet) {
                                testSet.addTestCase(scen);
                            }
                            item.setTestSet(testSet);
                        }
                    }
                }
            } else {
                String tmpTestSetName = "JENKINS Exec " + testSetName;
                logger.info("Create test set \"" + tmpTestSetName + "\" for execution of \""
                        + remoteScenarioSetToString(foundScenearios) + "\" from File " + tptFile.getName());

                TestSet testSet = openProject.getProject().createTestSet(tmpTestSetName);
                newTestSets.add(testSet);
                for (Scenario scen : foundScenearios) {
                    testSet.addTestCase(scen);
                }
                for (ExecutionConfigurationItem item : config.getItems()) {
                    oldTestSets.add(item.getTestSet());
                    if (item.isActive()) {
                        item.setTestSet(testSet);
                    }
                }
            }
            // execute test
            ExecutionStatus execStatus = api.run(config);
            while (execStatus.isRunning() || execStatus.isPending()) {
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    logger.interrupt(e.getMessage());
                    execStatus.cancel();
                    break;
                }
            }
            // undo changes
            logger.info("Set test sets in execution config to old values.");
            for (ExecutionConfigurationItem item : config.getItems()) {
                item.setTestSet(oldTestSets.remove(0));
            }
            try {
                slaveDataDir.copyRecursiveTo(new FilePath(masterWorkspace, testDataDir));
                slaveReportDir.copyRecursiveTo(new FilePath(masterWorkspace, reportDir));

                logger.info("Copied all data to master from File " + tptFile.getName() + " to "
                        + masterWorkspace.getRemote());

            } catch (InterruptedException e) {
                logger.interrupt(e.getMessage());
                return false;
            } catch (IOException e) {
                logger.error("could not copy results to master: " + e.getMessage());
            }
            logger.info("reset test data and report directory to " + oldTestDataDir.getPath() + " and "
                    + oldReportDir.getPath());
            config.setDataDir(oldTestDataDir);
            config.setReportDir(oldReportDir);
            for (TestSet testSet : newTestSets) {
                logger.info("delete temporary test set \"" + testSet.getName() + "\"");
                openProject.getProject().getTestSets().delete(testSet);
            }
            logger.info("Reactivate temporary deactivated execution config items.");
            for (ExecutionConfigurationItem item : deactivated) {
                item.setActive(true);
            }
        } catch (RemoteException e) {
            logger.error(e.getLocalizedMessage());
            e.printStackTrace(logger.getLogger());
            return false;
        } catch (ApiException e) {
            logger.error(e.getLocalizedMessage());
            e.printStackTrace(logger.getLogger());
            return false;
        }
        return true;
    }

    /**
     * Finds all the test cases of a given test set
     * 
     * @param sogs
     * @param names
     * @param result
     * @throws RemoteException
     * @throws ApiException
     */

    private void find(Collection<ScenarioOrGroup> sogs, Collection<String> names, Collection<Scenario> result)
            throws RemoteException, ApiException {
        for (ScenarioOrGroup sog : sogs) {
            if (sog instanceof Scenario) {
                if (names.contains(sog.getName())) {
                    result.add((Scenario) sog);
                }
            } else {
                find(((ScenarioGroup) sog).getItems(), names, result);
            }
        }
    }

    /**
     * Matches the tests cases from a test set with all the test cases found.
     * 
     * @param scenColl1
     * @param scenCol2
     * @return the intersected test cases
     * @throws RemoteException
     * @throws ApiException
     */
    static Collection<Scenario> intersectByHash(Collection<Scenario> scenColl1, Collection<Scenario> scenCol2)
            throws RemoteException, ApiException {
        Set<String> scenCol1Names = new HashSet<>();
        ArrayList<Scenario> result = new ArrayList<>();
        for (Scenario scen : scenColl1) {
            scenCol1Names.add(scen.getName());
        }
        for (Scenario scen : scenCol2) {
            if (scenCol1Names.contains(scen.getName())) {
                result.add(scen);
            }
        }
        return result;
    }

    /**
     * Convert the given test cases to a String
     * 
     * @param intersectionSet
     *          to be converted
     * @return the given test cases as a String , separated with a comma
     * @throws RemoteException
     * @throws ApiException
     */
    private String remoteScenarioSetToString(Collection<Scenario> intersectionSet)
            throws RemoteException, ApiException {
        StringBuilder sb = new StringBuilder();
        for (Scenario scen : intersectionSet) {
            if (sb.length() > 0) {
                sb.append(", ");
            }
            sb.append(scen.getName());
        }
        return sb.toString();
    }

}