You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@felix.apache.org by cl...@apache.org on 2008/11/11 17:55:06 UTC

svn commit: r713080 - in /felix/sandbox/clement/ipojo-utils/maven-junit4osgi-plugin: ./ src/ src/main/ src/main/java/ src/main/java/org/ src/main/java/org/apache/ src/main/java/org/apache/felix/ src/main/java/org/apache/felix/ipojo/ src/main/java/org/a...

Author: clement
Date: Tue Nov 11 08:55:06 2008
New Revision: 713080

URL: http://svn.apache.org/viewvc?rev=713080&view=rev
Log:
Commit of the maven-junit4osgi-plugin integrating the execution of junit4osgi test in a maven build process.

Added:
    felix/sandbox/clement/ipojo-utils/maven-junit4osgi-plugin/   (with props)
    felix/sandbox/clement/ipojo-utils/maven-junit4osgi-plugin/pom.xml
    felix/sandbox/clement/ipojo-utils/maven-junit4osgi-plugin/src/
    felix/sandbox/clement/ipojo-utils/maven-junit4osgi-plugin/src/main/
    felix/sandbox/clement/ipojo-utils/maven-junit4osgi-plugin/src/main/java/
    felix/sandbox/clement/ipojo-utils/maven-junit4osgi-plugin/src/main/java/org/
    felix/sandbox/clement/ipojo-utils/maven-junit4osgi-plugin/src/main/java/org/apache/
    felix/sandbox/clement/ipojo-utils/maven-junit4osgi-plugin/src/main/java/org/apache/felix/
    felix/sandbox/clement/ipojo-utils/maven-junit4osgi-plugin/src/main/java/org/apache/felix/ipojo/
    felix/sandbox/clement/ipojo-utils/maven-junit4osgi-plugin/src/main/java/org/apache/felix/ipojo/junit4osgi/
    felix/sandbox/clement/ipojo-utils/maven-junit4osgi-plugin/src/main/java/org/apache/felix/ipojo/junit4osgi/plugin/
    felix/sandbox/clement/ipojo-utils/maven-junit4osgi-plugin/src/main/java/org/apache/felix/ipojo/junit4osgi/plugin/Installer.java
    felix/sandbox/clement/ipojo-utils/maven-junit4osgi-plugin/src/main/java/org/apache/felix/ipojo/junit4osgi/plugin/Junit4osgiPlugin.java
    felix/sandbox/clement/ipojo-utils/maven-junit4osgi-plugin/src/main/java/org/apache/felix/ipojo/junit4osgi/plugin/Report.java
    felix/sandbox/clement/ipojo-utils/maven-junit4osgi-plugin/src/main/java/org/apache/felix/ipojo/junit4osgi/plugin/XMLReport.java

Propchange: felix/sandbox/clement/ipojo-utils/maven-junit4osgi-plugin/
------------------------------------------------------------------------------
--- svn:ignore (added)
+++ svn:ignore Tue Nov 11 08:55:06 2008
@@ -0,0 +1,4 @@
+.classpath
+.project
+.settings
+target

Added: felix/sandbox/clement/ipojo-utils/maven-junit4osgi-plugin/pom.xml
URL: http://svn.apache.org/viewvc/felix/sandbox/clement/ipojo-utils/maven-junit4osgi-plugin/pom.xml?rev=713080&view=auto
==============================================================================
--- felix/sandbox/clement/ipojo-utils/maven-junit4osgi-plugin/pom.xml (added)
+++ felix/sandbox/clement/ipojo-utils/maven-junit4osgi-plugin/pom.xml Tue Nov 11 08:55:06 2008
@@ -0,0 +1,83 @@
+<!--
+	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.
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+	<modelVersion>4.0.0</modelVersion>
+	<groupId>org.apache.felix.ipojo.junit4osgi</groupId>
+	<artifactId>maven-junit4osgi-plugin</artifactId>
+	<packaging>maven-plugin</packaging>
+	<version>1.1.0-SNAPSHOT</version>
+	<name>maven-junit4osgi-plugin Maven Mojo</name>
+	<url>http://maven.apache.org</url>
+	<dependencies>
+		<dependency>
+			<groupId>org.apache.maven</groupId>
+			<artifactId>maven-plugin-api</artifactId>
+			<version>2.0</version>
+		</dependency>
+		<dependency>
+			<groupId>junit</groupId>
+			<artifactId>junit</artifactId>
+			<version>3.8.1</version>
+			<scope>test</scope>
+		</dependency>
+		<dependency>
+			<groupId>org.apache.felix</groupId>
+			<artifactId>org.apache.felix.framework</artifactId>
+			<version>1.4.0</version>
+		</dependency>
+		<dependency>
+			<groupId>org.apache.maven</groupId>
+			<artifactId>maven-artifact</artifactId>
+			<version>2.0.7</version>
+		</dependency>
+		<dependency>
+			<groupId>org.apache.maven</groupId>
+			<artifactId>maven-project</artifactId>
+			<version>2.0.7</version>
+		</dependency>
+		<dependency>
+			<groupId>org.apache.maven</groupId>
+			<artifactId>maven-model</artifactId>
+			<version>2.0.7</version>
+		</dependency>
+		<dependency>
+			<groupId>org.apache.maven</groupId>
+			<artifactId>maven-plugin-api</artifactId>
+			<version>2.0.7</version>
+		</dependency>
+		<dependency>
+			<groupId>org.apache.felix</groupId>
+			<artifactId>org.apache.felix.ipojo</artifactId>
+			<version>1.1.0-SNAPSHOT</version>
+			<scope>runtime</scope>
+		</dependency>
+		<dependency>
+			<groupId>org.apache.felix</groupId>
+			<artifactId>org.apache.felix.ipojo.handler.extender</artifactId>
+			<version>1.1.0-SNAPSHOT</version>
+			<scope>runtime</scope>
+		</dependency>
+		<dependency>
+			<groupId>ipojo.examples</groupId>
+			<artifactId>org.apache.felix.ipojo.junit4osgi</artifactId>
+			<version>1.1.0-SNAPSHOT</version>
+		</dependency>
+	</dependencies>
+</project>
\ No newline at end of file

Added: felix/sandbox/clement/ipojo-utils/maven-junit4osgi-plugin/src/main/java/org/apache/felix/ipojo/junit4osgi/plugin/Installer.java
URL: http://svn.apache.org/viewvc/felix/sandbox/clement/ipojo-utils/maven-junit4osgi-plugin/src/main/java/org/apache/felix/ipojo/junit4osgi/plugin/Installer.java?rev=713080&view=auto
==============================================================================
--- felix/sandbox/clement/ipojo-utils/maven-junit4osgi-plugin/src/main/java/org/apache/felix/ipojo/junit4osgi/plugin/Installer.java (added)
+++ felix/sandbox/clement/ipojo-utils/maven-junit4osgi-plugin/src/main/java/org/apache/felix/ipojo/junit4osgi/plugin/Installer.java Tue Nov 11 08:55:06 2008
@@ -0,0 +1,196 @@
+/*
+ * 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.net.MalformedURLException;
+import java.net.URL;
+import java.util.List;
+
+import org.apache.maven.artifact.Artifact;
+import org.apache.maven.project.MavenProject;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.BundleActivator;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.BundleException;
+
+/**
+ * Bundle Activator installing bundles in the embedded OSGi.
+ * Installed bundles are the junit4osgi framework, the required bundle and the artifact bundle (if enable).
+ * Bundles are installed from the local maven repository.
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public class Installer implements BundleActivator {
+    
+    /**
+     * The list of artifact containing bundles for the junit4osgi framework.
+     */
+    private List artifacts;
+    
+    /**
+     * The current maven project. 
+     */
+    private MavenProject project;
+    
+    /**
+     * Flag enabling/disbling the deployment of the current
+     * project artifact.
+     */
+    private boolean deployCurrent;
+    
+    /**
+     * List of bundle URLs to install. 
+     */
+    private List bundles;
+    
+    
+    /**
+     * Creates a Installer
+     * @param artifacts the list of artifact containing bundles for the junit4osgi framework.
+     * @param bundles the list of bundle URLs to install 
+     * @param project the current maven project
+     * @param deployCurrentArtifact flag enabling/disabling the deployment of the current project artifact
+     */
+    public Installer(List artifacts, List bundles, MavenProject project, boolean deployCurrentArtifact) {
+        this.artifacts = artifacts;
+        this.project = project;
+        deployCurrent = deployCurrentArtifact;
+        this.bundles = bundles;
+    }
+    
+    /**
+     * Installs and starts the iPOJO bundle.
+     * @param context  the bundle context.
+     * @throws BundleException when the bundle cannot be installed or started correctly
+     */
+    private void installIPOJO(BundleContext context) throws BundleException {
+        String path = getUrlByArtifactId("org.apache.felix.ipojo").toString();
+        Bundle bundle = context.installBundle(path);
+        bundle.start();
+    }
+    
+    /**
+     * Installs and starts the Junit4OSGi bundle.
+     * @param context  the bundle context.
+     * @throws BundleException when the bundle cannot be installed or started correctly
+     */
+    private void installJunit(BundleContext context) throws BundleException {
+        String path = getUrlByArtifactId("org.apache.felix.ipojo.junit4osgi").toString();
+        Bundle bundle = context.installBundle(path);
+        bundle.start();
+    }
+    
+    /**
+     * Installs and starts the iPOJO Extender Handler bundle.
+     * @param context  the bundle context.
+     * @throws BundleException when the bundle cannot be installed or started correctly
+     */
+    private void installExtender(BundleContext context) throws BundleException {
+        String path = getUrlByArtifactId("org.apache.felix.ipojo.handler.extender").toString();
+        Bundle bundle = context.installBundle(path);
+        bundle.start();
+    }
+    
+    /**
+     * Installs and Starts required bundles.
+     * @throws BundleException when a bundle cannot be installed or started correctly
+     */
+    private void deployBundles(BundleContext context) throws BundleException {
+        for (int i = 0; i < bundles.size(); i++) {
+            URL url = (URL) bundles.get(i);
+            Bundle bundle = context.installBundle(url.toString());
+            bundle.start();
+        }
+    }
+    
+    /**
+     * Gets the bundle URL from the artifact list.
+     * @param id the dependency id.
+     * @return the bundle URL or <code>null</code> if the URL cannot
+     * be found.
+     */
+    private URL getUrlByArtifactId(String id) {
+        for (int i = 0; i < artifacts.size(); i++) {
+            Artifact artifact = (Artifact) artifacts.get(i);
+            if (artifact.getArtifactId().equals(id)) {
+                try {
+                    return artifact.getFile().toURL();
+                } catch (MalformedURLException e) {
+                    e.printStackTrace();
+                }
+            }
+        }
+        return null;
+    }
+
+    /**
+     * Deploys the current bundle if enable.
+     * @param context the bundle context
+     * @throws BundleException when the bundle cannot be installed or started correctly.
+     */
+    private void deployProjectArtifact(BundleContext context)
+            throws BundleException {
+        if (!deployCurrent) {
+            return;
+        }
+
+        File file = project.getArtifact().getFile();
+        if (file.exists()) {
+            URL url = null;
+            try {
+                url = file.toURL();
+            } catch (MalformedURLException e) {
+                e.printStackTrace();
+            }
+            Bundle bundle = context.installBundle(url.toString());
+            bundle.start();
+        } else {
+            throw new BundleException("The current project artifact does not exist (" + file.getAbsolutePath() + ")");
+        }
+    }
+
+
+    /**
+     * Start bundles.
+     * @param context the bundle context.
+     * @see org.osgi.framework.BundleActivator#start(org.osgi.framework.BundleContext)
+     */
+    public void start(BundleContext context) {
+        try {
+            installIPOJO(context);
+            installExtender(context);
+            installJunit(context);
+            deployBundles(context);
+            deployProjectArtifact(context);
+        } catch (BundleException e) {
+            System.err.println("Cannot start the framework : " + e.getMessage());
+            return;
+        }
+    }
+
+    /**
+     * Stopping methods.
+     * @param context the bundle context.
+     * @see org.osgi.framework.BundleActivator#stop(org.osgi.framework.BundleContext)
+     */
+    public void stop(BundleContext context) {
+        // Do nothing.
+    }
+
+}
\ No newline at end of file

Added: felix/sandbox/clement/ipojo-utils/maven-junit4osgi-plugin/src/main/java/org/apache/felix/ipojo/junit4osgi/plugin/Junit4osgiPlugin.java
URL: http://svn.apache.org/viewvc/felix/sandbox/clement/ipojo-utils/maven-junit4osgi-plugin/src/main/java/org/apache/felix/ipojo/junit4osgi/plugin/Junit4osgiPlugin.java?rev=713080&view=auto
==============================================================================
--- felix/sandbox/clement/ipojo-utils/maven-junit4osgi-plugin/src/main/java/org/apache/felix/ipojo/junit4osgi/plugin/Junit4osgiPlugin.java (added)
+++ felix/sandbox/clement/ipojo-utils/maven-junit4osgi-plugin/src/main/java/org/apache/felix/ipojo/junit4osgi/plugin/Junit4osgiPlugin.java Tue Nov 11 08:55:06 2008
@@ -0,0 +1,438 @@
+/*
+ * 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;/*
+
+ *
+ * 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.
+ */
+
+import java.io.File;
+import java.lang.reflect.Method;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.util.ArrayList;
+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.TestFailure;
+import junit.framework.TestListener;
+import junit.framework.TestResult;
+
+import org.apache.felix.framework.Felix;
+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.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 project;
+    
+    /** @parameter expression="${plugin.artifacts}" */
+    private java.util.List pluginArtifacts;
+    
+    /**
+     * Base directory where all reports are written to.
+     * 
+     * @parameter expression="${project.build.directory}/junit4osgi-reports"
+     */
+    private File reportsDirectory;
+    
+    /**
+     * Base directory where all reports are written to.
+     * 
+     * @parameter expression="${project.build.directory}"
+     */
+    private File targetDir;
+    
+    /**
+     * Must the current artifact be deployed?.
+     * 
+     * @parameter expression="${deployProjectArtifact}" default-value="false"
+     */
+    private boolean deployProjectArtifact;
+    
+    /**
+     * Required bundles
+     * 
+     * @parameter expression="${bundles}"
+     */
+    private ArrayList bundles;
+    
+    int total;
+    int totalFailures;
+    int totalErrors;
+    
+    List errors = new ArrayList();
+    List failures = new ArrayList();
+    List results = new ArrayList();
+    
+   
+    public void execute() throws MojoFailureException {
+        
+        List bundles = parseBundleList();
+        bundles.addAll(getTestBundle());
+        
+        List activators = new ArrayList();
+        activators.add(new Installer(pluginArtifacts, bundles, project, deployProjectArtifact));
+        Map map = new HashMap();
+        map.put("felix.systembundle.activators", activators);
+        map.put("org.osgi.framework.storage.clean", "onFirstInit");
+        map.put("ipojo.log.level", "WARNING");
+        map.put("org.osgi.framework.bootdelegation", "junit.framework");
+        map.put("org.osgi.framework.storage", targetDir.getAbsolutePath() + "/felix-cache"); 
+
+        
+        System.out.println("");
+        System.out.println("-------------------------------------------------------");
+        System.out.println(" T E S T S");        
+        System.out.println("-------------------------------------------------------");
+        
+        Felix felix = new Felix(map);
+        try {
+            felix.start();
+        } catch (BundleException e) {
+            e.printStackTrace();
+        }
+        // dumpBundles(felix.getBundleContext());
+        
+        waitForStability(felix.getBundleContext());
+        
+        Object runner = waitForRunnerService(felix.getBundleContext());
+        
+        invokeRun(runner, felix.getBundleContext());
+        
+        try {
+            felix.stop();
+            felix.waitForStop(5000);
+        } catch (Exception e) {
+            getLog().error(e);
+        }
+        
+        if (totalErrors > 0 || totalFailures > 0) {
+            throw new MojoFailureException("There are test failures. \n\n" +
+                    "Please refer to " + reportsDirectory.getAbsolutePath() + 
+                    " for the individual test results.");
+        }
+
+    }
+    
+    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");
+        }
+        
+        boolean serviceStability = false;
+        count = 0;
+        int count1 = 0, count2 = 0;
+        while(! serviceStability && count < 500) {
+            try {
+                ServiceReference[] refs = context.getServiceReferences(null, null);
+                count1 = refs.length;
+                Thread.sleep(500);
+                refs = context.getServiceReferences(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");
+        }
+        
+    }
+    
+    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;
+    }
+
+    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;
+    }
+    
+    private List getTestBundle() {
+        List toDeploy = new ArrayList();
+        Set dependencies = project.getDependencyArtifacts();
+        for (Iterator artifactIterator = dependencies.iterator(); artifactIterator.hasNext();) {
+            Artifact artifact = (Artifact) artifactIterator.next();
+            if (Artifact.SCOPE_TEST.equals(artifact.getScope())) { // Select scope=test.
+                File file = artifact.getFile();
+                try {
+                    JarFile jar = new JarFile(file);
+                    if (jar.getManifest().getMainAttributes().getValue("Bundle-ManifestVersion") != null) {
+                        toDeploy.add(file.toURL());
+                    }
+                } catch (Exception e) {
+                    getLog().error(e);
+                }
+            }
+        }
+        return toDeploy;
+    }
+    
+    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);
+            } 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");
+        return null;
+    }
+    
+    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 (failures.size() > 0) {
+                System.out.println("\nFailed tests:");
+                for (int i = 0; i < failures.size(); i++) {
+                    TestResult tr = (TestResult) failures.get(i);
+                    Enumeration e = tr.failures();
+                    while(e.hasMoreElements()) {
+                        TestFailure tf = (TestFailure) e.nextElement();
+                        System.out.println(" " + tf.toString());
+                    }
+                }
+            }
+            
+            if (failures.size() > 0) {
+                System.out.println("\nTests in error:");
+                for (int i = 0; i < errors.size(); i++) {
+                    TestResult tr = (TestResult) 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: " + total + ", Failures: " + totalFailures + ", Errors:" + totalErrors + "\n");          
+        } catch (Exception e) {
+            getLog().error(e);
+        } 
+    }
+    
+    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;
+    }
+    
+    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;
+        }
+            
+    }
+    
+    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);        
+        results.add(tr);
+        
+        
+        
+        if (tr.wasSuccessful()) {
+            System.out.println("Tests run: " + tr.runCount() + ", Failures: " + tr.failureCount() + ", Errors: " + tr.errorCount() + ", Time elapsed: " + report.elapsedTimeAsString(report.endTime - report.endTime) + " sec");
+        } else {
+            System.out.println("Tests run: " + tr.runCount() + ", Failures: " + tr.failureCount() + ", Errors: " + tr.errorCount() + ", Time elapsed: " + report.elapsedTimeAsString(report.endTime - report.endTime) + " sec <<< FAILURE!");
+            if (tr.errorCount() > 0) {
+                errors.add(tr);
+            }
+            if (tr.failureCount() > 0) {
+                failures.add(tr);
+            }
+        }
+        
+        total += tr.runCount();
+        totalFailures += tr.failureCount();
+        totalErrors += tr.errorCount();
+        
+        report.generateReport(test, tr, reportsDirectory, bc);
+        
+        } catch (Exception e) {
+            getLog().error(e);
+        }
+        
+       
+        
+    }
+    
+    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());
+        }
+    }
+    
+    
+    private class ResultListener implements TestListener {
+        
+        private XMLReport report;
+        boolean abort;
+        
+        public ResultListener(XMLReport report) {
+            this.report = report;
+        }
+
+        public void addError(Test test, Throwable throwable) {
+            report.testError(test, throwable);
+            abort = true;
+        }
+
+        public void addFailure(Test test,
+                AssertionFailedError assertionfailederror) {
+            report.testFailed(test, assertionfailederror);
+            abort = true;
+            
+        }
+
+        public void endTest(Test test) {
+           if (!abort) {
+               report.testSucceeded(test);
+           }
+        }
+
+        public void startTest(Test test) {
+            abort = false;
+            report.testStarting();
+        }
+        
+    }
+}

Added: felix/sandbox/clement/ipojo-utils/maven-junit4osgi-plugin/src/main/java/org/apache/felix/ipojo/junit4osgi/plugin/Report.java
URL: http://svn.apache.org/viewvc/felix/sandbox/clement/ipojo-utils/maven-junit4osgi-plugin/src/main/java/org/apache/felix/ipojo/junit4osgi/plugin/Report.java?rev=713080&view=auto
==============================================================================
--- felix/sandbox/clement/ipojo-utils/maven-junit4osgi-plugin/src/main/java/org/apache/felix/ipojo/junit4osgi/plugin/Report.java (added)
+++ felix/sandbox/clement/ipojo-utils/maven-junit4osgi-plugin/src/main/java/org/apache/felix/ipojo/junit4osgi/plugin/Report.java Tue Nov 11 08:55:06 2008
@@ -0,0 +1,238 @@
+/*
+ * 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.PrintWriter;
+import java.io.StringWriter;
+import java.text.NumberFormat;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Locale;
+
+import junit.framework.Test;
+
+import org.codehaus.plexus.util.StringUtils;
+
+/**
+ * Test report. 
+ * This class is provides the basics to support several output format.
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public class Report {
+    
+    /**
+     * Number of ran tests.
+     */
+    protected int completedCount;
+
+    /**
+     * Number of errors
+     */
+    protected int errorsCount;
+    
+    /**
+     * Number of failures
+     */
+    protected int failuresCount;
+    
+    
+    /**
+     * Failing tests.
+     */
+    private List failureSources = new ArrayList();
+    
+    /**
+     * Tests in error
+     */
+    private List errorSources = new ArrayList();
+    
+    /**
+     * Time at the beginning of the test execution. 
+     */
+    protected long startTime;
+
+    /**
+     * Time at the end of the test execution. 
+     */
+    protected long endTime;
+
+    /**
+     * Double format. 
+     */
+    private NumberFormat numberFormat = NumberFormat.getInstance( Locale.US );
+
+    /**
+     * New line constant.
+     */
+    protected static final String NL = System.getProperty( "line.separator" );
+   
+    /**
+     * Gets failing tests.
+     * @return the list of failing tests.
+     */
+    public List getFailureSources() {
+        return this.failureSources;
+    }
+
+    /**
+     * Gets tests in error.
+     * @return the list of test throwing unexpected exceptions
+     */
+    public List getErrorSources() {
+        return this.errorSources;
+    }
+    
+    /**
+     * Callback called when a test starts.
+     */
+    public void testStarting() {
+        startTime = System.currentTimeMillis();
+    }
+
+    /**
+     * Callback called when a test ends successfully.
+     */
+    public void testSucceeded() {
+        endTest();
+    }
+
+    /**
+     * Callback called when a test throws an unexpected error.
+     * @param test the test in error.
+     */
+    public void testError(Test test) {
+        ++errorsCount;
+        errorSources.add(test.toString());
+        endTest();
+    }
+
+    /**
+     * Callback called when a test fails.
+     * @param test the failing test.
+     */
+    public void testFailed(Test test) {
+        ++failuresCount;
+        failureSources.add(test.toString());
+        endTest();
+    }
+
+    /**
+     * Callback called when a test ends.
+     * This method handles common action when a test ends.
+     */
+    private void endTest() {
+        ++completedCount;
+
+        endTime = System.currentTimeMillis();
+
+        if (startTime == 0) {
+            startTime = endTime;
+        }
+    }
+    
+    
+    public int getNumErrors() {
+        return errorsCount;
+    }
+
+    public int getNumFailures() {
+        return failuresCount;
+    }
+
+    public int getNumTests() {
+        return completedCount;
+    }
+
+    /**
+     * Reset the report.
+     */
+    public void reset() {
+        errorsCount = 0;
+
+        failuresCount = 0;
+
+        completedCount = 0;
+
+        this.failureSources = new ArrayList();
+
+        this.errorSources = new ArrayList();
+
+    }
+
+
+    /**
+     * Returns the formatted String to display the given double.
+     * @param runTime the elapsed time
+     * @return the String displaying the elapsed time
+     */
+    protected String elapsedTimeAsString(long runTime) {
+        return numberFormat.format((double) runTime / 1000);
+    }
+
+    /**
+     * Returns the stacktrace as String.
+     * @param report ReportEntry object.
+     * @return stacktrace as string.
+     */
+    protected String getStackTrace(Test test, Throwable e) {
+
+        if (e == null) {
+            return "";
+        }
+
+        StringWriter w = new StringWriter();
+        if (e != null) {
+            e.printStackTrace(new PrintWriter(w));
+            w.flush();
+        }
+        String text = w.toString();
+        String marker = "at " + test.toString();
+
+        String[] lines = StringUtils.split(text, "\n");
+        int lastLine = lines.length - 1;
+        int causedByLine = -1;
+        // skip first
+        for (int i = 1; i < lines.length; i++) {
+            String line = lines[i].trim();
+            if (line.startsWith(marker)) {
+                lastLine = i;
+            } else if (line.startsWith("Caused by")) {
+                causedByLine = i;
+                break;
+            }
+        }
+
+        StringBuffer trace = new StringBuffer();
+        for (int i = 0; i <= lastLine; i++) {
+            trace.append(lines[i]);
+            trace.append("\n");
+        }
+
+        if (causedByLine != -1) {
+            for (int i = causedByLine; i < lines.length; i++) {
+                trace.append(lines[i]);
+                trace.append("\n");
+            }
+        }
+        return trace.toString();
+    }
+    
+    
+
+}

Added: felix/sandbox/clement/ipojo-utils/maven-junit4osgi-plugin/src/main/java/org/apache/felix/ipojo/junit4osgi/plugin/XMLReport.java
URL: http://svn.apache.org/viewvc/felix/sandbox/clement/ipojo-utils/maven-junit4osgi-plugin/src/main/java/org/apache/felix/ipojo/junit4osgi/plugin/XMLReport.java?rev=713080&view=auto
==============================================================================
--- felix/sandbox/clement/ipojo-utils/maven-junit4osgi-plugin/src/main/java/org/apache/felix/ipojo/junit4osgi/plugin/XMLReport.java (added)
+++ felix/sandbox/clement/ipojo-utils/maven-junit4osgi-plugin/src/main/java/org/apache/felix/ipojo/junit4osgi/plugin/XMLReport.java Tue Nov 11 08:55:06 2008
@@ -0,0 +1,299 @@
+/*
+ * 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.BufferedWriter;
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.OutputStreamWriter;
+import java.io.PrintWriter;
+import java.io.UnsupportedEncodingException;
+import java.util.ArrayList;
+import java.util.Enumeration;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Properties;
+import java.util.StringTokenizer;
+
+import junit.framework.Test;
+import junit.framework.TestResult;
+
+import org.codehaus.plexus.util.IOUtil;
+import org.codehaus.plexus.util.xml.PrettyPrintXMLWriter;
+import org.codehaus.plexus.util.xml.Xpp3Dom;
+import org.codehaus.plexus.util.xml.Xpp3DomWriter;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.BundleContext;
+
+/**
+ * This class generates test result as XML files compatible with Surefire.
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public class XMLReport extends Report {
+    
+    /**
+     * List of results.
+     */
+    private List results = new ArrayList();
+
+    /**
+     * A test ends successfully.
+     * @param test the test executed successfully.
+     */
+    public void testSucceeded(Test test) {
+        super.testSucceeded();
+
+        long runTime = this.endTime - this.startTime;
+
+        Xpp3Dom testCase = createTestElement(test, runTime);
+
+        results.add(testCase);
+    }
+
+    /**
+     * A test throws an unexpected errors.
+     * @param test the test in error
+     * @param e the thrown exception
+     */
+    public void testError(Test test, Throwable e) {
+        super.testError(test);
+
+        writeTestProblems(test, e, "error");
+    }
+
+    /**
+     * A test fails.
+     * @param test the failing test
+     * @param e the thrown failure
+     */
+    public void testFailed(Test test, Throwable e) {
+        super.testFailed(test);
+
+        writeTestProblems(test, e, "failure");
+    }
+
+    /**
+     * Utility method writing failed and in error test result in the report.
+     * @param test the test
+     * @param e the thrown error
+     * @param name type of failure ("error" or "failure")
+     */
+    private void writeTestProblems(Test test, Throwable e, String name) {
+
+        long runTime = endTime - startTime;
+
+        Xpp3Dom testCase = createTestElement(test, runTime);
+
+        Xpp3Dom element = createElement(testCase, name);
+
+        String stackTrace = getStackTrace(test, e);
+
+        Throwable t = e;
+
+        if (t != null) {
+
+            String message = t.getMessage();
+
+            if (message != null) {
+                element.setAttribute("message", message);
+
+                element.setAttribute("type",
+                        (stackTrace.indexOf(":") > -1 ? stackTrace.substring(0,
+                                stackTrace.indexOf(":")) : stackTrace));
+            } else {
+                element.setAttribute("type", new StringTokenizer(stackTrace)
+                        .nextToken());
+            }
+        }
+
+        if (stackTrace != null) {
+            element.setValue(stackTrace);
+        }
+
+        results.add(testCase);
+    }
+
+    /**
+     * Generates the XML reports.
+     * @param test the test 
+     * @param tr the test result
+     * @param reportsDirectory the directory in which reports are created.
+     * @param bc the bundle context (to get installed bundles)
+     * @throws Exception when the XML report cannot be generated correctly
+     */
+    public void generateReport(Test test, TestResult tr, File reportsDirectory,
+            BundleContext bc) throws Exception {
+        long runTime = this.endTime - this.startTime;
+
+        Xpp3Dom testSuite = createTestSuiteElement(test, runTime);
+
+        showProperties(testSuite, bc);
+
+        testSuite.setAttribute("tests", String.valueOf(tr.runCount()));
+
+        testSuite.setAttribute("errors", String.valueOf(tr.errorCount()));
+
+        testSuite.setAttribute("failures", String.valueOf(tr.failureCount()));
+
+        for (Iterator i = results.iterator(); i.hasNext();) {
+            Xpp3Dom testcase = (Xpp3Dom) i.next();
+            testSuite.addChild(testcase);
+        }
+
+        File reportFile = new File(reportsDirectory, "TEST-"
+                + getReportName(test).replace(' ', '_') + ".xml");
+
+        File reportDir = reportFile.getParentFile();
+
+        reportDir.mkdirs();
+
+        PrintWriter writer = null;
+
+        try {
+            writer = new PrintWriter(new BufferedWriter(new OutputStreamWriter(
+                    new FileOutputStream(reportFile), "UTF-8")));
+
+            writer.write("<?xml version=\"1.0\" encoding=\"UTF-8\" ?>" + NL);
+
+            Xpp3DomWriter.write(new PrettyPrintXMLWriter(writer), testSuite);
+        } catch (UnsupportedEncodingException e) {
+            throw new Exception("Unable to use UTF-8 encoding", e);
+        } catch (FileNotFoundException e) {
+            throw new Exception("Unable to create file: " + e.getMessage(), e);
+        }
+
+        finally {
+            IOUtil.close(writer);
+        }
+    }
+
+    /**
+     * Creates a XML test case element.
+     * @param test the test
+     * @param runTime the elapsed time to execute the test.
+     * @return the XML element describing the given test.
+     */
+    private Xpp3Dom createTestElement(Test test, long runTime) {
+        Xpp3Dom testCase = new Xpp3Dom("testcase");
+        testCase.setAttribute("name", getReportName(test));
+        testCase.setAttribute("time", Long.toString(runTime) + " sec");
+        return testCase;
+    }
+
+    /**
+     * Creates a XML test suite element.
+     * @param test the test
+     * @param runTime the elapsed time to execute the test suite.
+     * @return the XML element describing the given test suite.
+     */
+    private Xpp3Dom createTestSuiteElement(Test test, long runTime) {
+        Xpp3Dom testCase = new Xpp3Dom("testsuite");
+        testCase.setAttribute("name", getReportName(test));
+        testCase.setAttribute("time", Long.toString(runTime) + " sec");
+        return testCase;
+    }
+
+    /**
+     * Computes report name.
+     * @param test the test
+     * @return the report name
+     */
+    private static String getReportName(Test test) {
+        String report = test.toString();
+
+        if (report.indexOf("(") > 0) {
+            report = report.substring(0, report.indexOf("("));
+        }
+        return report;
+    }
+
+    /**
+     * Creates an XML element
+     * @param element the parent element
+     * @param name the name of the element to create
+     * @return the resulting XML tree.
+     */
+    private Xpp3Dom createElement(Xpp3Dom element, String name) {
+        Xpp3Dom component = new Xpp3Dom(name);
+
+        element.addChild(component);
+
+        return component;
+    }
+
+    /**
+     * Adds system properties to the XML report.
+     * This method also adds installed bundles.
+     * @param testSuite the XML element.
+     * @param bc the bundle context
+     */
+    private void showProperties(Xpp3Dom testSuite, BundleContext bc) {
+        Xpp3Dom properties = createElement(testSuite, "properties");
+
+        Properties systemProperties = System.getProperties();
+
+        if (systemProperties != null) {
+            Enumeration propertyKeys = systemProperties.propertyNames();
+
+            while (propertyKeys.hasMoreElements()) {
+                String key = (String) propertyKeys.nextElement();
+
+                String value = systemProperties.getProperty(key);
+
+                if (value == null) {
+                    value = "null";
+                }
+
+                Xpp3Dom property = createElement(properties, "property");
+
+                property.setAttribute("name", key);
+
+                property.setAttribute("value", value);
+
+            }
+        }
+
+        Xpp3Dom buns = createElement(properties, "bundles");
+        Bundle[] bundles = bc.getBundles();
+        for (int i = 0; i < bundles.length; i++) {
+            String sn = bundles[i].getSymbolicName();
+            String state = "UNKNOWN";
+            switch (bundles[i].getState()) {
+                case Bundle.ACTIVE:
+                    state = "ACTIVE";
+                    break;
+                case Bundle.INSTALLED:
+                    state = "INSTALLED";
+                    break;
+                case Bundle.RESOLVED:
+                    state = "RESOLVED";
+                    break;
+                case Bundle.UNINSTALLED:
+                    state = "UNINSTALLED";
+                    break;
+            }
+            Xpp3Dom bundle = createElement(buns, "bundle");
+            bundle.setAttribute("symbolic-name", sn);
+            bundle.setAttribute("state", state);
+        }
+
+    }
+
+}