/* * Copyright 2009 Toni Menzel. * * 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.ops4j.pax.exam.spi.intern; import static org.ops4j.pax.exam.Constants.PROBE_EXECUTABLE; import static org.ops4j.pax.tinybundles.core.TinyBundles.bundle; import static org.ops4j.pax.tinybundles.core.TinyBundles.withClassicBuilder; import java.io.File; import java.io.IOException; import java.io.InputStream; import java.lang.reflect.Method; import java.net.URISyntaxException; import java.net.URL; import java.util.ArrayList; import java.util.HashMap; import java.util.HashSet; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import java.util.Properties; import java.util.Set; import org.ops4j.pax.exam.TestAddress; import org.ops4j.pax.exam.TestContainerException; import org.ops4j.pax.exam.TestInstantiationInstruction; import org.ops4j.pax.exam.TestProbeBuilder; import org.ops4j.pax.exam.TestProbeProvider; import org.ops4j.pax.exam.spi.ContentCollector; import org.ops4j.pax.tinybundles.core.TinyBundle; import org.ops4j.store.Store; import org.osgi.framework.Constants; /** * Default implementation allows you to dynamically create a probe from current classpath. * * @author Toni Menzel * @since Dec 2, 2009 */ public class TestProbeBuilderImpl implements TestProbeBuilder { private static final String DEFAULT_PROBE_METHOD_NAME = "probe"; private final Map<TestAddress, TestInstantiationInstruction> probeCalls = new LinkedHashMap<TestAddress, TestInstantiationInstruction>(); private final List<Class<?>> anchors; private final Properties extraProperties; private final Set<String> ignorePackages = new HashSet<String>(); private File tempDir; private final Store<InputStream> store; public TestProbeBuilderImpl(File tempDir, Store<InputStream> store) throws IOException { this.anchors = new ArrayList<Class<?>>(); this.tempDir = tempDir; this.store = store; extraProperties = new Properties(); } public TestAddress addTest(Class<?> clazz, String methodName, Object... args) { TestAddress address = new DefaultTestAddress(clazz.getName() + "." + methodName, args); probeCalls.put(address, new TestInstantiationInstruction(clazz.getName() + ";" + methodName)); addAnchor(clazz); return address; } public TestAddress addTest(Class<?> clazz, Object... args) { return addTest(clazz, DEFAULT_PROBE_METHOD_NAME, args); } public List<TestAddress> addTests(Class<?> clazz, Method... methods) { List<TestAddress> list = new ArrayList<TestAddress>(); for (Method method : methods) { list.add(addTest(clazz, method.getName())); } return list; } public TestProbeBuilder addAnchor(Class<?> clazz) { anchors.add(clazz); return this; } public TestProbeBuilder setHeader(String key, String value) { extraProperties.put(key, value); return this; } // when your testclass contains clutter in non-test methods, // bnd generates too many impports. // This makes packages optional. public TestProbeBuilder ignorePackageOf(Class<?>... classes) { for (Class<?> c : classes) { ignorePackages.add(c.getPackage().getName()); } return this; } public TestProbeProvider build() { if (anchors.size() == 0) { throw new TestContainerException("No tests added to setup!"); } constructProbeTag(extraProperties); try { TinyBundle bundle = prepareProbeBundle(createExtraIgnores()); return new DefaultTestProbeProvider(getTests(), store, store.store(bundle .build(withClassicBuilder()))); } catch (IOException e) { throw new TestContainerException(e); } } private TinyBundle prepareProbeBundle(Properties p) throws IOException { TinyBundle bundle = bundle(store).set(Constants.DYNAMICIMPORT_PACKAGE, "*"); bundle.set(Constants.BUNDLE_SYMBOLICNAME, ""); bundle.set(Constants.BUNDLE_MANIFESTVERSION, "2"); for (Object key : extraProperties.keySet()) { bundle.set((String) key, (String) extraProperties.get(key)); } for (Object key : p.keySet()) { bundle.set((String) key, (String) p.get(key)); } Map<String, URL> map = collectResources(); for (String item : map.keySet()) { bundle.add(item, map.get(item)); } return bundle; } private Map<String, URL> collectResources() throws IOException { ContentCollector collector = selectCollector(); Map<String, URL> map = new HashMap<String, URL>(); collector.collect(map); return map; } static String convertClassToPath(Class<?> c) { return c.getName().replace(".", File.separator) + ".class"; } /** * @param clazz * to find the root classes folder for. * * @return A File instance being the exact folder on disk or null, if it hasn't been found. * * @throws java.io.IOException * if a problem occurs (method crawls folders on disk..) */ public static File findClassesFolder(Class<?> clazz) throws IOException { ClassLoader classLoader = clazz.getClassLoader(); String clazzPath = convertClassToPath(clazz); URL url = classLoader.getResource(clazzPath); if (url == null || !"file".equals(url.getProtocol())) { return null; } else { try { File file = new File(url.toURI()); String fullPath = file.getCanonicalPath(); String parentDirPath = fullPath .substring(0, fullPath.length() - clazzPath.length()); return new File(parentDirPath); } catch (URISyntaxException e) { // this should not happen as the uri was obtained from getResource throw new TestContainerException(e); } } } private ContentCollector selectCollector() throws IOException { File root = findClassesFolder(anchors.get(0)); if (root != null) { return new CompositeCollector(new CollectFromBase(root), new CollectFromItems(anchors)); } else { return new CollectFromItems(anchors); } } public Set<TestAddress> getTests() { return probeCalls.keySet(); } private Properties createExtraIgnores() { Properties properties = new Properties(); StringBuilder sb = new StringBuilder(); for (String p : ignorePackages) { if (sb.length() > 0) { sb.append(","); } sb.append(p); } properties.put("Ignore-Package", sb.toString()); return properties; } private void constructProbeTag(Properties p) { // construct out of added Tests StringBuilder sbKeyChain = new StringBuilder(); for (TestAddress address : probeCalls.keySet()) { sbKeyChain.append(address.identifier()); sbKeyChain.append(","); p.put(address.identifier(), probeCalls.get(address).toString()); } p.put(PROBE_EXECUTABLE, sbKeyChain.toString()); } @Override public File getTempDir() { return tempDir; } @Override public void setTempDir(File tempDir) { this.tempDir = tempDir; } }