org.everit.osgi.dev.maven.IntegrationTestMojo.java Source code

Java tutorial

Introduction

Here is the source code for org.everit.osgi.dev.maven.IntegrationTestMojo.java

Source

/*
 * Copyright (C) 2011 Everit Kft. (http://everit.org)
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *         http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.everit.osgi.dev.maven;

import java.io.BufferedReader;
import java.io.Closeable;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Set;
import java.util.jar.Attributes;
import java.util.logging.Logger;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;

import org.apache.maven.artifact.Artifact;
import org.apache.maven.plugin.MojoExecutionException;
import org.apache.maven.plugin.MojoFailureException;
import org.apache.maven.plugin.logging.Log;
import org.apache.maven.plugins.annotations.LifecyclePhase;
import org.apache.maven.plugins.annotations.Mojo;
import org.apache.maven.plugins.annotations.Parameter;
import org.apache.maven.plugins.annotations.ResolutionScope;
import org.everit.osgi.dev.maven.jaxb.dist.definition.ArtifactType;
import org.everit.osgi.dev.maven.jaxb.dist.definition.ArtifactsType;
import org.everit.osgi.dev.maven.jaxb.dist.definition.BundleDataType;
import org.everit.osgi.dev.maven.jaxb.dist.definition.CommandType;
import org.everit.osgi.dev.maven.jaxb.dist.definition.LauncherType;
import org.everit.osgi.dev.maven.jaxb.dist.definition.LaunchersType;
import org.everit.osgi.dev.maven.jaxb.dist.definition.OSGiActionType;
import org.everit.osgi.dev.maven.util.PluginUtil;
import org.everit.osgi.dev.testrunner.TestRunnerConstants;
import org.rzo.yajsw.os.OperatingSystem;
import org.rzo.yajsw.os.Process;
import org.rzo.yajsw.os.ProcessManager;
import org.rzo.yajsw.os.ms.win.w32.WindowsXPProcess;
import org.rzo.yajsw.os.posix.bsd.BSDProcess;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.xml.sax.SAXException;

/**
 * Runs the integration-tests on OSGi environment. It is necessary to add
 * <i>org.everit.osgi.dev.testrunner</i> and one of the engines as a dependency to the project to
 * make this goal work.
 */
@Mojo(name = "integration-test", defaultPhase = LifecyclePhase.INTEGRATION_TEST, requiresProject = true, requiresDependencyResolution = ResolutionScope.TEST)
public class IntegrationTestMojo extends DistMojo {

    /**
     * A shutdown hook that stops the started OSGi container.
     */
    private class ShutdownHook extends Thread {

        private final Process process;

        private final int shutdownTimeout;

        public ShutdownHook(final Process process, final int shutdownTimeout) {
            this.process = process;
            this.shutdownTimeout = shutdownTimeout;
        }

        @Override
        public void run() {
            shutdownProcess(process, shutdownTimeout, 0);
        }
    }

    /**
     * The most simple implementation of an output stream that redirects all writings to a writer.
     */
    private static final class SimpleWriterOutputStream extends OutputStream {

        private final OutputStream outputStream;

        public SimpleWriterOutputStream(final OutputStream outputStream) {
            this.outputStream = outputStream;
        }

        @Override
        public void close() {
        }

        @Override
        public void write(final int b) throws IOException {
            outputStream.write(b);
        }
    }

    /**
     * Struct of test results.
     */
    private static class TestResult {

        public String environmentId;

        public int error;

        public int expectedTestNum;

        public int failure;

        public int skipped;

        public int tests;
    }

    /**
     * Constant of the MANIFEST header key to count the {@link #expectedNumberOfIntegrationTests}.
     */
    private static final String EXPECTED_NUMBER_OF_INTEGRATION_TESTS = "EOSGi-TestNum";

    private static final int MILLISECOND_NUM_IN_SECOND = 1000;

    private static final int TIMEOUT_CHECK_INTERVAL = 10;

    private static int convertTestSuiteAttributeToInt(final Element element, final String attribute,
            final File resultFile) throws MojoFailureException {
        String stringValue = element.getAttribute(attribute);
        if ("".equals(stringValue.trim())) {
            throw new MojoFailureException("Invalid test result file " + resultFile.getAbsolutePath()
                    + ". The attribute " + attribute + " in testSuite is not defined.");
        }

        try {
            return Integer.parseInt(stringValue);
        } catch (NumberFormatException e) {
            throw new MojoFailureException("Invalid test result file " + resultFile.getAbsolutePath()
                    + ". The attribute " + attribute + " is invalid.");
        }
    }

    /**
     * Whether to log the output of the started test JVMs to the standard output and standard error or
     * not.
     */
    @Parameter(property = "eosgi.consoleLog", defaultValue = "true")
    protected boolean consoleLog = true;

    /**
     * Skipping this plugin.
     */
    @Parameter(property = "maven.test.skip", defaultValue = "false")
    protected boolean skipTests = false;

    private int calculateExpectedTestNum(final DistributedEnvironment distributedEnvironment) {
        int result = 0;
        ArtifactsType artifacts = distributedEnvironment.getDistributionPackage().getArtifacts();
        if (artifacts == null) {
            return 0;
        }
        List<ArtifactType> artifactList = artifacts.getArtifact();
        Set<String> artifactsKeys = new HashSet<>();
        for (ArtifactType artifactType : artifactList) {
            BundleDataType bundleDataType = artifactType.getBundle();
            if ((bundleDataType != null) && !OSGiActionType.NONE.equals(bundleDataType.getAction())) {
                String artifactKey = artifactType.getGroupId() + ":" + artifactType.getArtifactId() + ":"
                        + artifactType.getVersion() + ":" + evaluateArtifactType(artifactType.getType()) + ":"
                        + evaluateClassifier(artifactType.getClassifier());

                artifactsKeys.add(artifactKey);
            }
        }

        for (DistributableArtifact distributableArtifact : distributedEnvironment.getDistributableArtifacts()) {
            DistributableArtifactBundleMeta bundle = distributableArtifact.getBundle();
            if (bundle != null) {
                Artifact artifact = distributableArtifact.getArtifact();

                String artifactKey = artifact.getGroupId() + ":" + artifact.getArtifactId() + ":"
                        + artifact.getVersion() + ":" + evaluateArtifactType(artifact.getType()) + ":"
                        + evaluateClassifier(artifact.getClassifier());

                if (artifactsKeys.contains(artifactKey)) {
                    Attributes mainAttributes = distributableArtifact.getManifest().getMainAttributes();

                    String currentExpectedNumberString = mainAttributes
                            .getValue(EXPECTED_NUMBER_OF_INTEGRATION_TESTS);
                    if ((currentExpectedNumberString != null) && !currentExpectedNumberString.isEmpty()) {
                        long currentExpectedNumber = Long.valueOf(currentExpectedNumberString);
                        result += currentExpectedNumber;
                    }
                }
            }

        }
        return result;
    }

    private List<TestResult> calculateExpectedTestNumFailures(final List<TestResult> testResults) {
        List<TestResult> expecationTestNumFailures = new ArrayList<TestResult>();
        for (TestResult testResult : testResults) {
            if (testResult.expectedTestNum != testResult.tests) {
                expecationTestNumFailures.add(testResult);
            }
        }
        return expecationTestNumFailures;
    }

    private LauncherType calculateLauncherForCurrentOS(final DistributedEnvironment distributedEnvironment) {

        LaunchersType launchers = distributedEnvironment.getDistributionPackage().getLaunchers();
        if (launchers == null) {
            return null;
        }

        List<LauncherType> launcherList = launchers.getLauncher();

        if (launcherList.size() == 0) {
            return null;
        }

        String os = PluginUtil.getOS();

        LauncherType selectedLauncher = null;
        Iterator<LauncherType> iterator = launcherList.iterator();
        while ((selectedLauncher == null) && iterator.hasNext()) {
            LauncherType launcher = iterator.next();
            if (os.equals(launcher.getOs())) {
                selectedLauncher = launcher;
            }
        }
        return selectedLauncher;
    }

    private TestResult calculateTestResultSum(final List<TestResult> testResults) {
        TestResult resultSum = new TestResult();

        for (TestResult testResult : testResults) {
            resultSum.tests += testResult.tests;
            resultSum.error += testResult.error;
            resultSum.failure += testResult.failure;
            resultSum.skipped += testResult.skipped;
            resultSum.expectedTestNum += testResult.expectedTestNum;
        }
        return resultSum;
    }

    private void checkExitCode(final Process process, final String environmentId) throws MojoExecutionException {
        int exitCode = process.getExitCode();
        if (exitCode != 0) {
            throw new MojoExecutionException(
                    "Test Process of environment " + environmentId + " finished with exit code " + exitCode);
        }
    }

    private void checkExitError(final File resultFolder, final String environmentId) throws MojoFailureException {
        File exitErrorFile = new File(resultFolder, TestRunnerConstants.SYSTEM_EXIT_ERROR_FILE_NAME);
        if (exitErrorFile.exists()) {
            StringBuilder sb = new StringBuilder();

            try (FileInputStream fin = new FileInputStream(exitErrorFile)) {
                InputStreamReader reader = new InputStreamReader(fin, Charset.defaultCharset());
                BufferedReader br = new BufferedReader(reader);
                String line = br.readLine();
                while (line != null) {
                    sb.append(line).append("\n");
                    line = br.readLine();
                }
            } catch (FileNotFoundException e) {
                getLog().error("Could not find file " + exitErrorFile.getAbsolutePath(), e);
            } catch (IOException e) {
                getLog().error("Error during reading exit error file " + exitErrorFile.getAbsolutePath(), e);
            }
            getLog().error("Error during stopping the JVM of the environment " + environmentId
                    + ". Information can be found at " + exitErrorFile.getAbsolutePath()
                    + ". Content of the file is: \n" + sb.toString());

            throw new MojoFailureException("Could not shut down the JVM of the environment " + environmentId
                    + " in a nice way. For more information, see the content of the file: "
                    + exitErrorFile.getAbsolutePath());

        }
    }

    private File createTempFolder() throws IOException {
        File tmpPath = File.createTempFile("eosgi-", "-tmp");
        tmpPath.delete();
        tmpPath.mkdir();
        return tmpPath;
    }

    private Process createTestProcess(final DistributedEnvironment distributedEnvironment,
            final CommandType startCommand, final File resultFolder, final File tmpPath) {
        OperatingSystem operatingSystem = OperatingSystem.instance();

        getLog().info("Operating system is " + operatingSystem.getOperatingSystemName());

        String lowerCaseOperatingSystemName = operatingSystem.getOperatingSystemName()
                .toLowerCase(Locale.getDefault());

        Process process;
        if (lowerCaseOperatingSystemName.contains("linux") || lowerCaseOperatingSystemName.startsWith("mac os x")) {
            getLog().info("Starting BSD process");
            process = new BSDProcess();
        } else {
            ProcessManager processManager = operatingSystem.processManagerInstance();
            process = processManager.createProcess();
        }
        process.setTitle("EOSGi TestProcess - " + distributedEnvironment.getEnvironment().getId());

        process.setCommand(startCommand.getValue().split(" "));
        getLog().info("Setting tmp path: " + tmpPath.getAbsolutePath());
        process.setTmpPath(tmpPath.getAbsolutePath());
        process.setVisible(false);
        process.setTeeName(null);
        process.setPipeStreams(true, false);
        process.setLogger(Logger.getLogger("eosgi"));

        Map<String, String> envMap = new HashMap<String, String>(System.getenv());
        envMap.put(TestRunnerConstants.ENV_STOP_AFTER_TESTS, Boolean.TRUE.toString());
        envMap.put(TestRunnerConstants.ENV_TEST_RESULT_FOLDER, resultFolder.getAbsolutePath());

        List<String[]> env = PluginUtil.convertMapToList(envMap);

        process.setEnvironment(env);
        File commandFolder = resolveCommandFolder(distributedEnvironment, startCommand);
        process.setWorkingDir(commandFolder.getAbsolutePath());
        return process;
    }

    private void defineStandardOutputs(final File stdOutFile, final List<OutputStream> stdOuts)
            throws MojoExecutionException {
        FileOutputStream stdOutFileOut;
        try {
            stdOutFileOut = new FileOutputStream(stdOutFile);
        } catch (FileNotFoundException e) {
            throw new MojoExecutionException("Could not open standard output file for writing", e);
        }

        stdOuts.add(stdOutFileOut);
        if (consoleLog) {
            stdOuts.add(new SimpleWriterOutputStream(System.out));
        }
    }

    private Closeable doStreamRedirections(final Process process, final File resultFolder)
            throws MojoExecutionException {

        File stdOutFile = new File(resultFolder, "system-out.txt");
        File stdErrFile = new File(resultFolder, "system-error.txt");

        List<OutputStream> stdOuts = new ArrayList<OutputStream>();
        List<OutputStream> stdErrs = new ArrayList<OutputStream>();

        defineStandardOutputs(stdOutFile, stdOuts);
        defineStandardOutputs(stdErrFile, stdErrs);

        final DaemonStreamRedirector deamonFileWriterStreamPoller = new DaemonStreamRedirector(
                process.getInputStream(), stdOuts.toArray(new OutputStream[0]), getLog());
        try {
            deamonFileWriterStreamPoller.start();
        } catch (IOException e) {
            try {
                deamonFileWriterStreamPoller.close();
            } catch (IOException e1) {
                e.addSuppressed(e1);
            }
            throw new MojoExecutionException("Could not start stream redirector for standard output", e);
        }

        final DaemonStreamRedirector deamonStdErrPoller = new DaemonStreamRedirector(process.getErrorStream(),
                stdErrs.toArray(new OutputStream[0]), getLog());
        try {
            deamonStdErrPoller.start();
        } catch (IOException e) {
            try {
                deamonFileWriterStreamPoller.close();
            } catch (IOException e1) {
                e.addSuppressed(e1);
            }
            try {
                deamonStdErrPoller.close();
            } catch (IOException e1) {
                e.addSuppressed(e1);
            }
            throw new MojoExecutionException("Could not start stream redirector for standard output", e);
        }

        return new Closeable() {

            @Override
            public void close() throws IOException {
                IOException thrownE = null;
                try {
                    deamonFileWriterStreamPoller.close();
                } catch (IOException e) {
                    thrownE = e;
                }
                try {
                    deamonStdErrPoller.close();
                } catch (IOException e) {
                    if (thrownE != null) {
                        thrownE.addSuppressed(e);
                    } else {
                        thrownE = e;
                    }
                }
                if (thrownE != null) {
                    throw thrownE;
                }
            }
        };
    }

    private String evaluateArtifactType(final String artifactType) {
        if ((artifactType == null) || "".equals(artifactType.trim())) {
            return "jar";
        }
        return artifactType;
    }

    private String evaluateClassifier(final String classifier) {
        if ((classifier == null) || (classifier.trim().length() == 0)) {
            return null;
        }
        return classifier;
    }

    @Override
    public void execute() throws MojoExecutionException, MojoFailureException {
        if (skipTests) {
            return;
        }

        super.execute();

        getLog().info("OSGi Integrations tests running started");

        File testReportFolderFile = initializeReportFolder();

        List<TestResult> testResults = new ArrayList<TestResult>();
        for (DistributedEnvironment distributedEnvironment : distributedEnvironments) {
            runIntegrationTestsOnEnvironment(testReportFolderFile, testResults, distributedEnvironment);
        }

        TestResult resultSum = calculateTestResultSum(testResults);

        List<TestResult> expectedTestNumFailures = calculateExpectedTestNumFailures(testResults);

        printTestResultSum(resultSum);

        throwExceptionsBasedOnTestResultsIfNecesssary(expectedTestNumFailures, resultSum);
    }

    private File initializeReportFolder() {
        File testReportFolderFile = new File(reportFolder);
        getLog().info("Integration test output directory: " + testReportFolderFile.getAbsolutePath());

        if (testReportFolderFile.exists()) {
            PluginUtil.deleteFolderRecurse(testReportFolderFile);
        }
        testReportFolderFile.mkdirs();
        return testReportFolderFile;
    }

    private void printEnvironmentProcessStartToLog(final DistributedEnvironment distributedEnvironment) {
        StringBuilder startEnvLogTextSB = new StringBuilder(
                "\n-------------------------------------------------------\n");
        startEnvLogTextSB.append("Starting test environment: ")
                .append(distributedEnvironment.getEnvironment().getId()).append("\n")
                .append("-------------------------------------------------------\n\n");
        getLog().info(startEnvLogTextSB.toString());
    }

    private void printTestResultsOfEnvironment(final DistributedEnvironment distributedEnvironment,
            final TestResult testResult) {
        StringBuilder stopEnvLogTextSB = new StringBuilder(
                "\n-------------------------------------------------------\n");
        stopEnvLogTextSB.append("Test environment finished: ")
                .append(distributedEnvironment.getEnvironment().getId()).append("\n")
                .append("-------------------------------------------------------\n\n").append("Tests run: ")
                .append(testResult.tests).append(", Failures: ").append(testResult.failure).append(", Errors: ")
                .append(testResult.error).append(", Skipped: ").append(testResult.skipped).append("\n");
        getLog().info(stopEnvLogTextSB.toString());
    }

    private void printTestResultSum(final TestResult resultSum) {
        StringBuilder testLogTextSB = new StringBuilder(
                "\n-------------------------------------------------------\n");
        testLogTextSB.append("I N T E G R A T I O N   T E S T S   ( O S G I)\n")
                .append("-------------------------------------------------------\n\n").append("Results:\n\n")
                .append("Tests run: ").append(resultSum.tests).append(", Failures: ").append(resultSum.failure)
                .append(", Errors: ").append(resultSum.error).append(", Skipped: ").append(resultSum.skipped)
                .append("\n");
        getLog().info(testLogTextSB.toString());
    }

    private void processResults(final File resultFolder, final TestResult results) throws MojoFailureException {
        DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance();
        DocumentBuilder documentBuilder = null;
        try {
            documentBuilder = documentBuilderFactory.newDocumentBuilder();
        } catch (ParserConfigurationException e) {
            throw new MojoFailureException("Failed to process test results", e);
        }

        if (resultFolder.exists() && resultFolder.isDirectory()) {
            File[] files = resultFolder.listFiles();
            for (File resultFile : files) {
                if (resultFile.getName().endsWith(".xml")) {
                    processResultXML(results, documentBuilder, resultFile);
                }
            }
        }
    }

    private void processResultXML(final TestResult results, final DocumentBuilder documentBuilder,
            final File resultFile) throws MojoFailureException {
        try {
            Document document = documentBuilder.parse(resultFile);
            Element testSuite = document.getDocumentElement();
            if (!"testsuite".equals(testSuite.getNodeName())) {
                throw new MojoFailureException("Invalid test result xml file " + resultFile.getAbsolutePath()
                        + ". Root element is not testsuite.");
            }

            results.tests += convertTestSuiteAttributeToInt(testSuite, "tests", resultFile);
            results.failure += convertTestSuiteAttributeToInt(testSuite, "failures", resultFile);
            results.error += convertTestSuiteAttributeToInt(testSuite, "errors", resultFile);
            results.skipped += convertTestSuiteAttributeToInt(testSuite, "skipped", resultFile);
        } catch (SAXException e) {
            throw new MojoFailureException("Invalid test result file " + resultFile.getAbsolutePath());
        } catch (IOException e) {
            throw new MojoFailureException("Error during processing result file " + resultFile.getAbsolutePath());
        }
    }

    private File resolveCommandFolder(final DistributedEnvironment distributedEnvironment,
            final CommandType command) {
        File commandFolder = distributedEnvironment.getDistributionFolder();

        String folder = command.getFolder();
        if (folder != null) {
            commandFolder = new File(commandFolder, folder);
        }
        return commandFolder;
    }

    private CommandType resolveStartCommandForEnvironment(final DistributedEnvironment distributedEnvironment)
            throws MojoFailureException {
        LauncherType launcher = calculateLauncherForCurrentOS(distributedEnvironment);

        if (launcher == null) {
            throw new MojoFailureException("No start command specified for tests in the distribution package of "
                    + distributedEnvironment.getEnvironment().getId());
        }

        CommandType startCommand = launcher.getStartCommand();
        return startCommand;
    }

    private void runIntegrationTestsOnEnvironment(final File testReportFolderFile,
            final List<TestResult> testResults, final DistributedEnvironment distributedEnvironment)
            throws MojoFailureException, MojoExecutionException {
        printEnvironmentProcessStartToLog(distributedEnvironment);

        TestResult testResult = new TestResult();
        testResult.environmentId = distributedEnvironment.getEnvironment().getId();
        testResult.expectedTestNum = calculateExpectedTestNum(distributedEnvironment);
        testResults.add(testResult);

        CommandType startCommand = resolveStartCommandForEnvironment(distributedEnvironment);

        try {

            File resultFolder = new File(testReportFolderFile, distributedEnvironment.getEnvironment().getId());
            resultFolder.mkdirs();
            File tmpPath = createTempFolder();

            Process process = createTestProcess(distributedEnvironment, startCommand, resultFolder, tmpPath);

            boolean timeoutHappened = false;
            ShutdownHook shutdownHook = new ShutdownHook(process,
                    distributedEnvironment.getEnvironment().getShutdownTimeout());
            Runtime.getRuntime().addShutdownHook(shutdownHook);

            boolean started = process.start();
            if (!started) {
                throw new MojoFailureException("Could not start environment with command " + process.getCommand()
                        + " in working dir " + process.getWorkingDir());
            }

            AutoCloseable redirectionCloseable = doStreamRedirections(process, resultFolder);
            try {
                waitForProcessWithTimeoutAndLogging(process, distributedEnvironment.getEnvironment());

                if (process.isRunning()) {
                    getLog().warn("Test running process did not stop until timeout. Forcing to stop it...");
                    timeoutHappened = true;
                    shutdownProcess(process, distributedEnvironment.getEnvironment().getShutdownTimeout(), -1);
                }
            } finally {
                try {
                    redirectionCloseable.close();
                } catch (Exception e) {
                    throw new MojoExecutionException("Could not close stream redirectors", e);
                }
            }

            PluginUtil.deleteFolderRecurse(tmpPath);

            String environmentId = distributedEnvironment.getEnvironment().getId();

            if (timeoutHappened) {
                throw new MojoExecutionException(
                        "Test process of environment " + environmentId + " did not finish within timeout");
            }

            checkExitError(resultFolder, environmentId);
            checkExitCode(process, environmentId);

            getLog().info("Analyzing test results...");

            processResults(resultFolder, testResult);

            printTestResultsOfEnvironment(distributedEnvironment, testResult);
        } catch (IOException e) {
            throw new MojoExecutionException("Error during running integration tests", e);
        }
    }

    private void shutdownProcess(final Process process, final int shutdownTimeout, final int code) {
        getLog().warn("Stopping test process: " + process.getPid());
        if (process.isRunning()) {
            if (process instanceof WindowsXPProcess) {
                // In case of windows xp process we must kill the process with a command as there is no
                // visible
                // window and kill tree command of YAJSW does not work. Hopefully this is a temporary
                // solution.
                Log log = getLog();

                OperatingSystem operatingSystem = OperatingSystem.instance();
                ProcessManager processManagerInstance = operatingSystem.processManagerInstance();
                Process killProcess = processManagerInstance.createProcess();
                String killCommand = "taskkill /F /T /PID " + process.getPid();
                log.warn("Killing windows process with command: " + killCommand + "");
                killProcess.setCommand(killCommand);
                killProcess.setVisible(false);
                killProcess.start();
                process.waitFor(shutdownTimeout);
            } else {
                process.stop(shutdownTimeout, code);
            }
        }
    }

    private void throwExceptionsBasedOnTestResultsIfNecesssary(final List<TestResult> expecationErroredResults,
            final TestResult resultSum) throws MojoFailureException {
        if ((resultSum.error > 0) || (resultSum.failure > 0)) {
            throw new MojoFailureException("Error during running OSGi integration tests");
        }

        if (expecationErroredResults.size() > 0) {
            for (TestResult testResult : expecationErroredResults) {
                getLog().error("Error at test environment '" + testResult.environmentId
                        + "'. Expected test number is " + testResult.expectedTestNum + " while " + testResult.tests
                        + " number of tests ran.");
            }
            throw new MojoFailureException("Number of expected tests " + resultSum.expectedTestNum + " while "
                    + resultSum.tests + " tests ran.");
        }
    }

    private void waitForProcessWithTimeoutAndLogging(final Process process,
            final EnvironmentConfiguration environment) {
        final long loggingInterval = 5000;
        final long timeout = environment.getTimeout();
        final long startTime = System.currentTimeMillis();

        long nextExpectedLogging = startTime + loggingInterval;

        final long latestEndTime = startTime + timeout;
        long currentTime = startTime;
        while (process.isRunning() && (currentTime < latestEndTime)) {
            try {
                Thread.sleep(TIMEOUT_CHECK_INTERVAL);
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
                getLog().info("Waiting for tests was interrupted.");
                return;
            }
            if (!consoleLog && (currentTime > nextExpectedLogging)) {
                long secondsSinceStart = (nextExpectedLogging - startTime) / MILLISECOND_NUM_IN_SECOND;
                getLog().info("Waiting for test results since " + secondsSinceStart + "s");
                nextExpectedLogging = nextExpectedLogging + loggingInterval;
            }
            currentTime = System.currentTimeMillis();
        }
    }
}