Package org.apache.felix.ipojo.junit4osgi.plugin

Source Code of org.apache.felix.ipojo.junit4osgi.plugin.Junit4osgiPlugin$ResultListener

/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements.  See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership.  The ASF licenses this file
* to you 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.apache.felix.ipojo.junit4osgi.plugin;

import java.io.File;
import java.io.PrintStream;
import java.lang.reflect.Method;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.jar.JarFile;

import junit.framework.AssertionFailedError;
import junit.framework.Test;
import junit.framework.TestCase;
import junit.framework.TestFailure;
import junit.framework.TestListener;
import junit.framework.TestResult;

import org.apache.felix.framework.Felix;
import org.apache.felix.ipojo.junit4osgi.OSGiJunitRunner;
import org.apache.felix.ipojo.junit4osgi.plugin.log.LogServiceImpl;
import org.apache.maven.artifact.Artifact;
import org.apache.maven.plugin.AbstractMojo;
import org.apache.maven.plugin.MojoFailureException;
import org.apache.maven.project.MavenProject;
import org.osgi.framework.Bundle;
import org.osgi.framework.BundleContext;
import org.osgi.framework.BundleException;
import org.osgi.framework.Constants;
import org.osgi.framework.InvalidSyntaxException;
import org.osgi.framework.ServiceReference;

/**
* Goal starting Felix and executing junit4osgi tests.
*
* @goal test
* @phase integration-test
* @requiresDependencyResolution runtime
* @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
*
*/
public class Junit4osgiPlugin extends AbstractMojo {

    /**
     * The Maven project.
     *
     * @parameter expression="${project}"
     * @required
     * @readonly
     */
    private MavenProject m_project;

    /**
     * Dependencies of the current plugin.
     * @parameter expression="${plugin.artifacts}"
     */
    private java.util.List m_pluginArtifacts;

    /**
     * Base directory where all reports are written to.
     *
     * @parameter expression="${project.build.directory}/surefire-reports"
     */
    private File m_reportsDirectory;

    /**
     * Base directory where all reports are written to.
     *
     * @parameter expression="${project.build.directory}"
     */
    private File m_targetDir;

    /**
     * Must the current artifact be deployed?
     *
     * @parameter expression="${deployProjectArtifact}" default-value="true"
     */
    private boolean m_deployProjectArtifact;

    /**
     * Required bundles.
     *
     * @parameter
     */
    private List bundles;

    /**
     * Felix configuration.
     *
     * @parameter
     */
    private Map configuration;

    /**
     * Enables / Disables the log service provided by the plugin.
     *
     * @parameter expression="${logService}" default-value="true"
     */
    private boolean m_logEnable;

    /**
     * Number of executed test case.
     */
    private int m_total;

    /**
     * Number of failing test case.
     */
    private int m_totalFailures;

    /**
     * Number of test case in error .
     */
    private int m_totalErrors;

    /**
     * Test results in error.
     */
    private List m_errors = new ArrayList();

    /**
     * Failing test results.
     */
    private List m_failures = new ArrayList();

    /**
     * Test results.
     */
    private List m_results = new ArrayList();

    /**
     * Log Service exposed by the plug-in framework.
     */
    private LogServiceImpl m_logService;

    /**
     * Set this to 'true' to bypass unit tests entirely. Its use is NOT RECOMMENDED, especially if you
     * enable it using the "maven.test.skip" property, because maven.test.skip disables both running the
     * tests and compiling the tests.  Consider using the skipTests parameter instead.
     *
     * @parameter expression="${maven.test.skip}"
     */
    private boolean skip;

    /**
     * Set this to true to ignore a failure during testing. Its use is NOT RECOMMENDED, but quite convenient on
     * occasion.
     *
     * @parameter expression="${maven.test.failure.ignore}"
     */
    private boolean testFailureIgnore;

    /**
     * Set this to avoid printing test execution trace on System.out and System.err. This will be written in the
     * reports.
     * @parameter
     */
    private boolean hideOutputs;

    /**
     * Felix configuration.
     */
    private Map felixConf;


    /**
     * Executes the plug-in.
     * @throws MojoFailureException when the test execution failed.
     * @see org.apache.maven.plugin.AbstractMojo#execute()
     */
    public void execute() throws MojoFailureException {

        if (skip) {
            getLog().info("Tests are skipped");
            return;
        }


        List bundles = parseBundleList();
        bundles.addAll(getTestBundle());

        List activators = new ArrayList();
        m_logService = new LogServiceImpl();
        if (m_logEnable) { // Starts the log service if enabled
            activators.add(m_logService);
        } else {
            getLog().info("Log Service disabled");
        }
        activators.add(new Installer(m_pluginArtifacts, bundles, m_project, m_deployProjectArtifact));
        felixConf = new HashMap();
        felixConf.put("felix.systembundle.activators", activators);
        felixConf.put("org.osgi.framework.storage.clean", "onFirstInit");
        felixConf.put("ipojo.log.level", "WARNING");
        // Use a boot delagation to share classes between the host and the embedded Felix.
        // The cobertura package is used during code coverage collection
        //felixConf.put("org.osgi.framework.bootdelegation", "net.sourceforge.cobertura.coveragedata");
        felixConf.put("org.osgi.framework.system.packages.extra", "org.osgi.service.log;version=1.3, junit.framework;version=1.3");

        //felixConf.put("org.osgi.framework.system.packages.extra", "org.osgi.service.log, junit.framework");

        felixConf.put("org.osgi.framework.storage", m_targetDir.getAbsolutePath() + "/felix-cache");

        felixConf.put(Constants.FRAMEWORK_BUNDLE_PARENT, Constants.FRAMEWORK_BUNDLE_PARENT_FRAMEWORK);

        if (configuration != null) {
            felixConf.putAll(configuration);
            // Check boot delegation
//            String bd = (String) felixConf.get("org.osgi.framework.bootdelegation");
////            if (bd.indexOf("junit.framework") == -1) {
////                bd.concat(", junit.framework");
////            }
////            if (bd.indexOf("org.osgi.service.log") == -1) {
////                bd.concat(", org.osgi.service.log");
////            }
//            if (bd.indexOf("net.sourceforge.cobertura.coveragedata") == -1) {
//                bd.concat(", net.sourceforge.cobertura.coveragedata");
//            }
        }

        System.out.println("");
        System.out.println("-------------------------------------------------------");
        System.out.println(" T E S T S");
        System.out.println("-------------------------------------------------------");

        Felix felix = new Felix(felixConf);
        try {
            felix.start();
        } catch (BundleException e) {
            e.printStackTrace();
        }

        getLog().info("Felix started - Waiting for stability");

        waitForStability(felix.getBundleContext());

        getLog().info("Bundle Stability Reached - Waiting for runner service");

        Object runner = waitForRunnerService(felix.getBundleContext());

        if (runner == null) {
            throw new MojoFailureException("Cannot intialize the testing framework");
        }

        getLog().info("Runner Service available");

        invokeRun(runner, felix.getBundleContext());

        try {
            felix.stop();
            felix.waitForStop(5000);
            // Delete felix-cache
            File cache = new File(m_targetDir.getAbsolutePath() + "/felix-cache");
            cache.delete();
        } catch (Exception e) {
            getLog().error(e);
        }

        if (m_totalErrors > 0 || m_totalFailures > 0) {
            if (! testFailureIgnore) {
            throw new MojoFailureException("There are test failures. \n\n"
                    + "Please refer to " + m_reportsDirectory.getAbsolutePath()
                    + " for the individual test results.");
            } else {
                getLog().warn("There are test failures. \n\n"
                    + "Please refer to " + m_reportsDirectory.getAbsolutePath()
                    + " for the individual test results.");
            }
        }

    }

    /**
     * Waits for stability:
     * <ul>
     * <li>all bundles are activated
     * <li>service count is stable
     * </ul>
     * If the stability can't be reached after a specified time,
     * the method throws a {@link MojoFailureException}.
     * @param context the bundle context
     * @throws MojoFailureException when the stability can't be reach after a several attempts.
     */
    private void waitForStability(BundleContext context) throws MojoFailureException {
        // Wait for bundle initialization.
        boolean bundleStability = getBundleStability(context);
        int count = 0;
        while (!bundleStability && count < 500) {
            try {
                Thread.sleep(5);
            } catch (InterruptedException e) {
                // Interrupted
            }
            count++;
            bundleStability = getBundleStability(context);
        }

        if (count == 500) {
            getLog().error("Bundle stability isn't reached after 500 tries");
            dumpBundles(context);
            throw new MojoFailureException("Cannot reach the bundle stability");
        }

        //DEBUG
        Bundle[] bundles = context.getBundles();
        getLog().debug("Bundles List");
        for (int i = 0; i < bundles.length; i++) {
            getLog().debug(bundles[i].getSymbolicName() + " - " + bundles[i].getVersion() + " - " + bundles[i].getState());
        }
        getLog().debug("--------------");
        // END DEBUG

        boolean serviceStability = false;
        count = 0;
        int count1 = 0;
        int count2 = 0;
        while (! serviceStability && count < 500) {
            try {
                ServiceReference[] refs = context.getServiceReferences((String) null, null);
                count1 = refs.length;
                Thread.sleep(500);
                refs = context.getServiceReferences((String) null, null);
                count2 = refs.length;
                serviceStability = count1 == count2;
            } catch (Exception e) {
                getLog().error(e);
                serviceStability = false;
                // Nothing to do, while recheck the condition
            }
            count++;
        }

        if (count == 500) {
            getLog().error("Service stability isn't reached after 500 tries (" + count1 + " != " + count2);
            dumpBundles(context);
            throw new MojoFailureException("Cannot reach the service stability");
        }

        try {
            ServiceReference[] refs = context.getServiceReferences((String) null, null);
            getLog().debug("Service List");
            for (int i = 0; i < refs.length; i++) {
                String[] itfs = (String[]) refs[i].getProperty(Constants.OBJECTCLASS);
                List list = Arrays.asList(itfs);
                if (list.contains("org.apache.felix.ipojo.architecture.Architecture")) {
                    getLog().debug(list.toString() + " - " + refs[i].getProperty("architecture.instance"));
                } else {
                    getLog().debug(list.toString());
                }
            }
            getLog().debug("--------------");
        } catch (Exception e) {}

    }

    /**
     * Are bundle stables.
     * @param bc the bundle context
     * @return <code>true</code> if every bundles are activated.
     */
    private boolean getBundleStability(BundleContext bc) {
        boolean stability = true;
        Bundle[] bundles = bc.getBundles();
        for (int i = 0; i < bundles.length; i++) {
            stability = stability && (bundles[i].getState() == Bundle.ACTIVE);
        }
        return stability;
    }

    /**
     * Computes the URL list of bundles to install from
     * the <code>bundles</code> parameter.
     * @return the list of url of bundles to install.
     */
    private List parseBundleList() {
        List toDeploy = new ArrayList();
        if (bundles == null) {
            return toDeploy;
        }
        for (int i = 0; i < bundles.size(); i++) {
            String bundle = (String) bundles.get(i);
            try {
                URL url = new URL(bundle);
                toDeploy.add(url);
            } catch (MalformedURLException e) {
                // Not a valid url,
                getLog().error(bundle + " is not a valid url, bundle ignored");
            }
        }

        return toDeploy;
    }

    /**
     * Computes the URL list of bundles to install from
     * <code>test</code> scoped dependencies.
     * @return the list of url of bundles to install.
     */
    private List getTestBundle() {
        List toDeploy = new ArrayList();
        Set dependencies = m_project.getDependencyArtifacts();
        for (Iterator artifactIterator = dependencies.iterator(); artifactIterator.hasNext();) {
            Artifact artifact = (Artifact) artifactIterator.next();
            if (artifact.getScope() != null) { // Select not null scope... [Select every bundles with a scope TEST, COMPILE and RUNTIME]
                File file = artifact.getFile();
                try {
                    if (file.exists()) {
                        if (file.getName().endsWith("jar"&& ! file.getName().startsWith("org.apache.felix.ipojo-")) {
                            JarFile jar = new JarFile(file);
                            if (jar.getManifest().getMainAttributes().getValue("Bundle-ManifestVersion") != null) {
                                toDeploy.add(file.toURI().toURL());
                            }
                        } // else {
//                            getLog().info("The artifact " + artifact.getFile().getName() + " is not a Jar file.");
//                        }
                    } else {
                        getLog().info("The artifact " + artifact.getFile().getName() + " does not exist.");
                    }
                } catch (Exception e) {
                    getLog().error(file + " is not a valid bundle, this artifact is ignored");
                }
            }
        }
        return toDeploy;
    }

    /**
     * Waits until the {@link OSGiJunitRunner} service
     * is published.
     * @param bc the bundle context
     * @return the {@link OSGiJunitRunner} service object.
     */
    private Object waitForRunnerService(BundleContext bc) {
        ServiceReference ref = bc.getServiceReference(org.apache.felix.ipojo.junit4osgi.OSGiJunitRunner.class.getName());
        int count = 0;
        while (ref == null && count < 1000) {
            try {
                Thread.sleep(5);
                count++;
            } catch (InterruptedException e) {
                // Nothing to do
            }
            ref = bc.getServiceReference(org.apache.felix.ipojo.junit4osgi.OSGiJunitRunner.class.getName());
        }
        if (ref != null) {
            return bc.getService(ref);
        }
        getLog().error("Junit Runner service unavailable");

        dumpServices(bc);

        return null;
    }

    /**
     * Executes tests by using reflection.
     * @param runner the {@link OSGiJunitRunner} service object
     * @param bc the bundle context
     */
    private void invokeRun(Object runner, BundleContext bc) {
        Method getTest;

        try {
            getTest = runner.getClass().getMethod("getTests", new Class[0]);
            List tests = (List) getTest.invoke(runner, new Object[0]);
            Method run = getRunMethod(runner);
            for (int i = 0; i < tests.size(); i++) {
                executeTest(runner, (Test) tests.get(i), run, bc);
            }
            System.out.println("\nResults :");
            if (m_failures.size() > 0) {
                System.out.println("\nFailed tests:");
                for (int i = 0; i < m_failures.size(); i++) {
                    TestResult tr = (TestResult) m_failures.get(i);
                    Enumeration e = tr.failures();
                    while (e.hasMoreElements()) {
                        TestFailure tf = (TestFailure) e.nextElement();
                        System.out.println(" " + tf.toString());
                    }
                }
            }

            if (m_failures.size() > 0) {
                System.out.println("\nTests in error:");
                for (int i = 0; i < m_errors.size(); i++) {
                    TestResult tr = (TestResult) m_errors.get(i);
                    Enumeration e = tr.errors();
                    while (e.hasMoreElements()) {
                        TestFailure tf = (TestFailure) e.nextElement();
                        System.out.println(" " + tf.toString());
                    }
                }
            }

            System.out.println("\nTests run: " + m_total + ", Failures: " + m_totalFailures + ", Errors:" + m_totalErrors + "\n");
        } catch (Exception e) {
            getLog().error(e);
        }
    }

    /**
     * Gets the {@link OSGiJunitRunner#run(long)} method from the
     * {@link OSGiJunitRunner} service object.
     * @param runner the {@link OSGiJunitRunner} service object.
     * @return the Method object for the <code>run</code> method.
     */
    private Method getRunMethod(Object runner) {
        Method[] methods = runner.getClass().getMethods();
        for (int i = 0; i < methods.length; i++) {
            if (methods[i].getName().equals("run")
                    && methods[i].getParameterTypes().length == 1
                    && ! methods[i].getParameterTypes()[0].equals(Long.TYPE)) {
                return methods[i];
            }
        }
        getLog().error("Cannot find the run method");
        return null;
    }

    /**
     * Computes the name of the given test.
     * This method calls the {@link TestCase#getName()}
     * method by reflection. If no success, invokes the
     * {@link Object#toString()}  method.
     * @param test the test object.
     * @return the name of the given test.
     */
    private String getTestName(Object test) {
        try {
            Method getName = test.getClass().getMethod("getName", new Class[0]);
            String name = (String) getName.invoke(test, new Object[0]);
            if (name == null) {
                name = test.toString();
            }
            return name;
        } catch (Exception e) {
            getLog().error(e);
            return null;
        }

    }

    /**
     * Executes the given test.
     * @param runner the {@link OSGiJunitRunner} service object
     * @param test the test to run
     * @param run the {@link OSGiJunitRunner#run(long)} method
     * @param bc the bundle context
     */
    private void executeTest(Object runner, Test test, Method run,
            BundleContext bc) {
        try {
            XMLReport report = new XMLReport();
            String name = getTestName(test);
            System.out.println("Running " + name);

            TestResult tr = new TestResult();
            tr.addListener(new ResultListener(report));
            test.run(tr);
            m_results.add(tr);

            if (tr.wasSuccessful()) {
                System.out.println("Tests run: "
                        + tr.runCount()
                        + ", Failures: "
                        + tr.failureCount()
                        + ", Errors: "
                        + tr.errorCount()
                        + ", Time elapsed: "
                        + report.elapsedTimeAsString(report.m_endTime
                                - report.m_endTime) + " sec");
            } else {
                System.out.println("Tests run: "
                        + tr.runCount()
                        + ", Failures: "
                        + tr.failureCount()
                        + ", Errors: "
                        + tr.errorCount()
                        + ", Time elapsed: "
                        + report.elapsedTimeAsString(report.m_endTime
                                - report.m_endTime) + " sec <<< FAILURE!");
                if (tr.errorCount() > 0) {
                    m_errors.add(tr);
                }
                if (tr.failureCount() > 0) {
                    m_failures.add(tr);
                }
            }

            m_total += tr.runCount();
            m_totalFailures += tr.failureCount();
            m_totalErrors += tr.errorCount();

            report.generateReport(test, tr, m_reportsDirectory, bc, felixConf);

        } catch (Exception e) {
            getLog().error(e);
        }

    }

    /**
     * Prints the bundle list.
     * @param bc the bundle context.
     */
    public void dumpBundles(BundleContext bc) {
        getLog().info("Bundles:");
        Bundle[] bundles = bc.getBundles();
        for (int i = 0; i < bundles.length; i++) {
            getLog().info(bundles[i].getSymbolicName() + " - " + bundles[i].getState());
        }
    }

    /**
     * Prints the service list.
     * @param bc the bundle context.
     */
    public void dumpServices(BundleContext bc) {
        getLog().info("Services:");
        ServiceReference[] refs = null;
        try {
            refs = bc.getAllServiceReferences(null, null);
        } catch (InvalidSyntaxException e) {
            e.printStackTrace();
        }
        for (int i = 0; i < refs.length; i++) {
            String[] itfs = (String[]) refs[i].getProperty(Constants.OBJECTCLASS);
            String bundle = refs[i].getBundle().getSymbolicName();
            getLog().info(bundle + " : " + Arrays.toString(itfs));
        }
    }

    public LogServiceImpl getLogService() {
        return m_logService;
    }


    private class ResultListener implements TestListener {

        /**
         * The XML Report.
         */
        private XMLReport m_report;

        /**
         * Check if the test has failed or thrown an
         * error.
         */
        private boolean m_abort;

        /**
         * Backup of the {@link System#out} stream.
         */
        private PrintStream m_outBackup = System.out;

        /**
         * Backup of the {@link System#err} stream.
         */
        private PrintStream m_errBackup = System.err;

        /**
         * The output stream used during the test execution.
         */
        private StringOutputStream m_out = new StringOutputStream();

        /**
         * The error stream used during the test execution.
         */
        private StringOutputStream m_err = new StringOutputStream();;

        /**
         * Creates a ResultListener.
         * @param report the XML report
         */
        public ResultListener(XMLReport report) {
            this.m_report = report;
        }

        /**
         * An error occurs during the test execution.
         * @param test the test in error
         * @param throwable the thrown error
         * @see junit.framework.TestListener#addError(junit.framework.Test, java.lang.Throwable)
         */
        public void addError(Test test, Throwable throwable) {
            m_report.testError(test, throwable, m_out.toString(), m_err.toString(), getLogService().getLoggedMessages());
            m_abort = true;
        }

        /**
         * An failure occurs during the test execution.
         * @param test the failing test
         * @param assertionfailederror the failure
         * @see junit.framework.TestListener#addFailure(junit.framework.Test, junit.framework.AssertionFailedError)
         */
        public void addFailure(Test test,
                AssertionFailedError assertionfailederror) {
            m_report.testFailed(test, assertionfailederror, m_out.toString(), m_err.toString(), getLogService().getLoggedMessages());
            m_abort = true;

        }

        /**
         * The test ends.
         * @param test the test
         * @see junit.framework.TestListener#endTest(junit.framework.Test)
         */
        public void endTest(Test test) {
            if (!m_abort) {
                m_report.testSucceeded(test);
            }
            System.setErr(m_errBackup);
            System.setOut(m_outBackup);
            getLogService().reset();
        }

        /**
         * The test starts.
         * @param test the test
         * @see junit.framework.TestListener#startTest(junit.framework.Test)
         */
        public void startTest(Test test) {
            m_abort = false;
            m_report.testStarting();
            System.setErr(new ReportPrintStream(m_err,m_errBackup, hideOutputs));
            System.setOut(new ReportPrintStream(m_out, m_outBackup, hideOutputs));
            getLogService().enableOutputStream();
        }

    }

}
TOP

Related Classes of org.apache.felix.ipojo.junit4osgi.plugin.Junit4osgiPlugin$ResultListener

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.