Index: src/main/java/com/jtunisie/osgi/test/extender/Activator.java =================================================================== --- src/main/java/com/jtunisie/osgi/test/extender/Activator.java (revision 10) +++ src/main/java/com/jtunisie/osgi/test/extender/Activator.java (working copy) @@ -39,6 +39,7 @@ buffer.append("---Testing commands---\n\t"); buffer.append("test [bundle id] - test bundle fragment id\n\t"); buffer.append("testall - test all fragments\n\t"); + buffer.append("integrate [bundle id]\n\t"); buffer.append("help - Print this help\n"); return buffer.toString(); } @@ -53,6 +54,12 @@ testExtender.testAll(); return null; } + + public Object _integrate(CommandInterpreter intp) { + String nextArgument = intp.nextArgument(); + testExtender.integrationTest(Long.parseLong(nextArgument)); + return null; + } public Object _helpTest(CommandInterpreter intp) { String help = getHelp(); Index: src/main/java/com/jtunisie/osgi/test/extender/EBundleUtils.java =================================================================== --- src/main/java/com/jtunisie/osgi/test/extender/EBundleUtils.java (revision 10) +++ src/main/java/com/jtunisie/osgi/test/extender/EBundleUtils.java (working copy) @@ -21,6 +21,7 @@ import java.util.List; import org.osgi.framework.Bundle; import org.osgi.framework.BundleContext; +import org.osgi.framework.Version; /** * @@ -33,24 +34,102 @@ public static final Bundle getHostBundle(BundleContext context,Bundle bundle) { String fragment = bundle.getHeaders().get(org.osgi.framework.Constants.FRAGMENT_HOST) + ""; + + // In the fragment host string we might have the bundle version parameter + Version hostMinimum = Version.emptyVersion; + boolean minInclusive = false; + Version hostMaximum = null; + boolean maxInclusive = false; + String versionDelimiter = ";bundle-version="; + if (fragment.contains(versionDelimiter)) { + // the version string is delimited by \" so we use that to parse it easily + String versionString = fragment.substring(fragment.indexOf("\"")+1,fragment.lastIndexOf("\"")); + fragment = fragment.substring(0, fragment.indexOf(versionDelimiter)); + + // we check if it includes a minimum host version as well as a maximum (in a format like "[0.0.1,0.2.0)") + int minMaxDelimiterIndex = versionString.indexOf(","); + if (minMaxDelimiterIndex>=0) { + + String hostMinimumString = versionString.substring(1,minMaxDelimiterIndex); + minInclusive = versionString.substring(0,1).equals("["); + hostMinimum = Version.parseVersion(hostMinimumString); + + int versionStringLength = versionString.length(); + hostMaximum = Version.parseVersion(versionString.substring(minMaxDelimiterIndex+1, versionStringLength-1)); + maxInclusive = versionString.substring(versionStringLength-1,versionStringLength).equals("]"); + + } else { + hostMinimum = Version.parseVersion(versionString); + } + + } + Bundle[] bundles = context.getBundles(); for (Bundle ibundle : bundles) { - if (ibundle.getSymbolicName().equals(fragment)) { - return ibundle; - } + + if (isBundleAttachedToFragment(ibundle,hostMinimum,minInclusive,hostMaximum,maxInclusive,fragment)) { + return ibundle; + } + + } - throw new RuntimeException(); + throw new RuntimeException("Fragment host not found or fragment is not attached to any host"); } - public static final Class loadClass(String clazz, Bundle bundleHost) { - try { - Class loadClass = bundleHost.loadClass(clazz); - return loadClass; + private static boolean isBundleAttachedToFragment(Bundle bundle, Version hostMinimum, + boolean minInclusive, Version hostMaximum, boolean maxInclusive, + String fragment) { + + String symbolicName = bundle.getSymbolicName(); + if (symbolicName.equals(fragment)) { + + // we check if its the correct version + Version version = bundle.getVersion(); + + int minCompare = version.compareTo(hostMinimum); + boolean minOk = (minInclusive) ? minCompare>=0 : minCompare>0; + + boolean maxOk = true; + if (hostMaximum!=null) { + int maxCompare = version.compareTo(hostMaximum); + maxOk = (maxInclusive) ? maxCompare<=0 : maxCompare<0; + } + + return (minOk && maxOk); + + } + + return false; + } + + public static final Class loadClass(String clazz, Bundle bundleHost) { + Class loadClass = null; + try { + loadClass = bundleHost.loadClass(clazz); + + } catch (NoClassDefFoundError e2) { + + // Workaround for weird Eclipse bug: if a bundle is created using the wizard, it compiles + // classes onto the '/bin' folder (which doesn't happen when exported), this means that + // when running inside Eclipse the class will have an incorrect prefix (eg: bin.com.foo.bar.Test) + // Therefore we make an extra attempt. This is recursive so it works with nested compilation folders. + // To prevent infinite recursion we don't do it for classnames that do not have a "package" prefix + // as this is covered by the base case that has just thrown the exception. + // TODO: add some refactoring so thrown exception is more informative + int packageDelimiterIndex = clazz.indexOf("."); + if (packageDelimiterIndex>=0) { + loadClass = loadClass(clazz.substring(packageDelimiterIndex+1, clazz.length()),bundleHost); + } else { + throw new RuntimeException("Couldn't find test class"); + } } catch (Exception e) { + e.printStackTrace(); - throw new RuntimeException(e); + throw new RuntimeException(e); + } + return loadClass; } public static final List> getTestClass(BundleContext context,Bundle bundle) { @@ -65,6 +144,7 @@ String file = e.getFile(); String className = file.replaceAll("/", ".").replaceAll(".class", "").replaceFirst(".", ""); + Class clazz = loadClass(className, hostBundle); clazzs.add(clazz); } Index: src/main/java/com/jtunisie/osgi/test/extender/EClassUtils.java =================================================================== --- src/main/java/com/jtunisie/osgi/test/extender/EClassUtils.java (revision 10) +++ src/main/java/com/jtunisie/osgi/test/extender/EClassUtils.java (working copy) @@ -119,7 +119,7 @@ method.invoke(object, new Object[0]); System.out.println("Method : [ "+ method.getName()+" ] PASS " ); } catch (Exception ex) { - System.out.println("Method : [ "+ method.getName()+" ] ERROR " ); + System.out.println("Method : [ "+ method.getName()+" ] ERROR - "+ex.getCause().getMessage() ); } } finally { if (testClass.getAfter() != null) { Index: src/main/java/com/jtunisie/osgi/test/extender/TestExtender.java =================================================================== --- src/main/java/com/jtunisie/osgi/test/extender/TestExtender.java (revision 10) +++ src/main/java/com/jtunisie/osgi/test/extender/TestExtender.java (working copy) @@ -15,6 +15,8 @@ package com.jtunisie.osgi.test.extender; import com.jtunisie.osgi.test.extender.EClassUtils.Test; + +import java.lang.reflect.Constructor; import java.util.Collections; import java.util.HashMap; import java.util.List; @@ -78,7 +80,27 @@ public void test(long bundleId) { - Bundle bundle = bundles.get(bundleId); + + boolean unitTesting = true; + + testImpl(bundleId, unitTesting); + + } + + + + + /** Internal implementation of the test method, if true we do plain isolated unit testing + * if false, we do integration testing, passing the osgi context onto the bundle + * @param bundleId to be tested + * @param unitTesting if true, we use new Foo(), if false, we use new Foo(context) + *////////////////////////////////////////////////////////////////////////////// + protected void testImpl(long bundleId, boolean unitTesting) { + Bundle bundle = bundles.get(bundleId); + // requested bundle may not be on the list of testable fragments + if (bundle==null) { + System.err.println("Bundle: [ Id="+bundleId+"] is not testable (should be fragment and have the 'Unit-Test:' MANIFEST header"); + } List> testClazzs = getTestClass(context,bundle); System.out.println("Bundle : ["+bundleId+"] : "+ bundle.getSymbolicName()); System.out.println("_"); @@ -86,14 +108,30 @@ try { System.out.println("CLASS : ["+clazz.getName()+"]"); Test inspectClass = EClassUtils.inspectClass(clazz); - EClassUtils.testClass(inspectClass, clazz.newInstance()); + if (unitTesting) { + EClassUtils.testClass(inspectClass, clazz.newInstance()); + } else { + Constructor[] constructors = clazz.getConstructors(); + boolean foundConstructor = false; + for (int i = 0; i < constructors.length && !foundConstructor; i++) { + Constructor constructor = constructors[i]; + Class[] types = constructor.getParameterTypes(); + if (types.length==1 && types[0].isInstance(context)) { + foundConstructor = true; + EClassUtils.testClass(inspectClass, constructor.newInstance(context)); + } + } // for + if (!foundConstructor) { + System.err.println("No suitable integration test constructor present"); + } + } } catch (Exception ex) { ex.printStackTrace(); } } System.out.println("_"); - } + } public void testAll() { @@ -104,4 +142,11 @@ } System.out.println("============================================================================"); } + + + + + public void integrationTest(long bundleId) { + testImpl(bundleId, false); + } }