/******************************************************************************* * Copyright (c) 2012, 2015 VMware Inc. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * VMware Inc. - initial contribution *******************************************************************************/ package org.eclipse.equinox.region.internal.tests.hook; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; import java.util.*; import org.eclipse.equinox.region.*; import org.eclipse.equinox.region.internal.tests.RegionReflectionUtils; import org.eclipse.virgo.teststubs.osgi.framework.StubBundle; import org.eclipse.virgo.teststubs.osgi.framework.StubBundleContext; import org.junit.*; import org.osgi.framework.*; import org.osgi.framework.hooks.resolver.ResolverHook; import org.osgi.framework.wiring.*; import org.osgi.resource.Capability; import org.osgi.resource.Requirement; public class RegionResolverHookTests { private static final String PACKAGE_A = "package.a"; private static final String PACKAGE_B = "package.b"; private static final String PACKAGE_C = "package.c"; private static final String PACKAGE_D = "package.d"; private static final String PACKAGE_X = "package.x"; private static final String PACKAGE_DUP = "duplicate"; private static final String BUNDLE_X = "X"; private static final Version BUNDLE_VERSION = new Version("0"); private long bundleId; private static final String REGION_A = "RegionA"; private static final String BUNDLE_A = "BundleA"; private static final String REGION_B = "RegionB"; private static final String BUNDLE_B = "BundleB"; private static final String REGION_C = "RegionC"; private static final String BUNDLE_C = "BundleC"; private static final String REGION_D = "RegionD"; private static final String BUNDLE_D = "BundleD"; private RegionDigraph digraph; private ResolverHook resolverHook; private Map<String, Region> regions; private Map<String, Bundle> bundles; private Collection<BundleCapability> candidates; private ThreadLocal<Region> threadLocal; @Before public void setUp() throws Exception { this.bundleId = 1L; this.regions = new HashMap<String, Region>(); this.bundles = new HashMap<String, Bundle>(); this.threadLocal = new ThreadLocal<Region>(); StubBundle stubSystemBundle = new StubBundle(0L, "osgi.framework", new Version("0"), "loc"); StubBundleContext stubBundleContext = new StubBundleContext(); stubBundleContext.addInstalledBundle(stubSystemBundle); this.digraph = RegionReflectionUtils.newStandardRegionDigraph(stubBundleContext, this.threadLocal); this.resolverHook = RegionReflectionUtils.newRegionResolverHook(this.digraph); this.candidates = new HashSet<BundleCapability>(); // Create regions A, B, C, D containing bundles A, B, C, D, respectively. createRegion(REGION_A, BUNDLE_A); createRegion(REGION_B, BUNDLE_B); createRegion(REGION_C, BUNDLE_C); createRegion(REGION_D, BUNDLE_D); createBundle(BUNDLE_X); } @After public void tearDown() throws Exception { // nothing } @Test public void testResolveInSameRegion() { this.candidates.add(packageCapability(BUNDLE_A, PACKAGE_A)); this.resolverHook.filterMatches(bundleRequirement(BUNDLE_A), this.candidates); assertTrue(this.candidates.contains(packageCapability(BUNDLE_A, PACKAGE_A))); } @Test public void testNoRegionResolvable() { Collection<BundleRevision> resolvable = new ArrayList<BundleRevision>(Collections.singleton(new StubBundleRevision(bundle(BUNDLE_X)))); this.resolverHook.filterResolvable(resolvable); assertTrue("Resolvable is not empty" + resolvable, resolvable.isEmpty()); } @Test public void testResolveInDisconnectedRegion() { this.candidates.add(packageCapability(BUNDLE_B, PACKAGE_B)); this.resolverHook.filterMatches(bundleRequirement(BUNDLE_A), this.candidates); assertFalse(this.candidates.contains(packageCapability(BUNDLE_B, PACKAGE_B))); } @Test public void testResolveConnectedRegionAllowed() throws BundleException, InvalidSyntaxException { RegionFilter filter = createFilter(PACKAGE_B); region(REGION_A).connectRegion(region(REGION_B), filter); this.candidates.add(packageCapability(BUNDLE_B, PACKAGE_B)); this.resolverHook.filterMatches(bundleRequirement(BUNDLE_A), this.candidates); assertTrue(this.candidates.contains(packageCapability(BUNDLE_B, PACKAGE_B))); } @Test public void testResolveBundleCapabilityConnectedRegionAllowed() throws BundleException, InvalidSyntaxException { RegionFilter filter = createBundleFilter(BUNDLE_B, BUNDLE_VERSION); region(REGION_A).connectRegion(region(REGION_B), filter); this.candidates.add(bundleCapability(BUNDLE_B)); this.resolverHook.filterMatches(bundleRequirement(BUNDLE_A), this.candidates); assertTrue(this.candidates.contains(bundleCapability(BUNDLE_B))); } @Test public void testResolveConnectedRegionFiltering() throws BundleException, InvalidSyntaxException { region(REGION_A).connectRegion(region(REGION_B), createFilter(PACKAGE_B)); Bundle x = createBundle(BUNDLE_X); region(REGION_B).addBundle(x); this.candidates.add(packageCapability(BUNDLE_B, PACKAGE_B)); this.candidates.add(packageCapability(BUNDLE_X, PACKAGE_X)); this.resolverHook.filterMatches(bundleRequirement(BUNDLE_A), this.candidates); assertTrue(this.candidates.contains(packageCapability(BUNDLE_B, PACKAGE_B))); assertFalse(this.candidates.contains(packageCapability(BUNDLE_X, PACKAGE_X))); } @Test public void testResolveBundleConnectedRegionFiltering() throws BundleException, InvalidSyntaxException { RegionFilter filter = createBundleFilter(BUNDLE_B, BUNDLE_VERSION); region(REGION_A).connectRegion(region(REGION_B), filter); Bundle x = createBundle(BUNDLE_X); region(REGION_B).addBundle(x); this.candidates.add(bundleCapability(BUNDLE_B)); this.candidates.add(bundleCapability(BUNDLE_X)); this.resolverHook.filterMatches(bundleRequirement(BUNDLE_A), this.candidates); assertTrue(this.candidates.contains(bundleCapability(BUNDLE_B))); assertFalse(this.candidates.contains(bundleCapability(BUNDLE_X))); } @Test public void testResolveSingletonInSameRegions() { List<BundleCapability> collisionCandidates = new ArrayList<BundleCapability>(); collisionCandidates.add(bundleCapability(BUNDLE_B)); collisionCandidates.add(bundleCapability(BUNDLE_C)); collisionCandidates.add(bundleCapability(BUNDLE_D)); this.resolverHook.filterSingletonCollisions(bundleCapability(BUNDLE_A), collisionCandidates); assertEquals("Wrong number of collitions", 0, collisionCandidates.size()); } @Test public void testResolveSingletonInDifferentRegions() throws BundleException { region(REGION_A).addBundle(bundle(BUNDLE_X)); BundleCapability collision = bundleCapability(BUNDLE_X); List<BundleCapability> collisionCandidates = new ArrayList<BundleCapability>(); collisionCandidates.add(collision); collisionCandidates.add(bundleCapability(BUNDLE_B)); collisionCandidates.add(bundleCapability(BUNDLE_C)); collisionCandidates.add(bundleCapability(BUNDLE_D)); this.resolverHook.filterSingletonCollisions(bundleCapability(BUNDLE_A), collisionCandidates); assertEquals("Wrong number of collitions", 1, collisionCandidates.size()); collisionCandidates.contains(collision); } @Test public void testResolveSingletonConnectedRegions() throws BundleException, InvalidSyntaxException { RegionFilter filter = createBundleFilter(BUNDLE_B, BUNDLE_VERSION); region(REGION_A).connectRegion(region(REGION_B), filter); region(REGION_A).addBundle(bundle(BUNDLE_X)); BundleCapability collisionX = bundleCapability(BUNDLE_X); BundleCapability collisionB = bundleCapability(BUNDLE_B); List<BundleCapability> collisionCandidates = new ArrayList<BundleCapability>(); collisionCandidates.add(collisionX); collisionCandidates.add(collisionB); collisionCandidates.add(bundleCapability(BUNDLE_C)); collisionCandidates.add(bundleCapability(BUNDLE_D)); this.resolverHook.filterSingletonCollisions(bundleCapability(BUNDLE_A), collisionCandidates); assertEquals("Wrong number of collitions", 2, collisionCandidates.size()); collisionCandidates.contains(collisionX); collisionCandidates.contains(collisionB); } @Test public void testResolveTransitive() throws BundleException, InvalidSyntaxException { region(REGION_A).connectRegion(region(REGION_B), createFilter(PACKAGE_C)); region(REGION_B).connectRegion(region(REGION_C), createFilter(PACKAGE_C)); region(REGION_C).addBundle(bundle(BUNDLE_X)); this.candidates.add(packageCapability(BUNDLE_B, PACKAGE_B)); this.candidates.add(packageCapability(BUNDLE_C, PACKAGE_C)); this.candidates.add(packageCapability(BUNDLE_X, PACKAGE_X)); this.resolverHook.filterMatches(bundleRequirement(BUNDLE_A), this.candidates); assertTrue(this.candidates.contains(packageCapability(BUNDLE_C, PACKAGE_C))); assertFalse(this.candidates.contains(packageCapability(BUNDLE_B, PACKAGE_B))); assertFalse(this.candidates.contains(packageCapability(BUNDLE_X, PACKAGE_X))); } @Test public void testResolveTransitiveDups() throws BundleException, InvalidSyntaxException { region(REGION_A).connectRegion(region(REGION_B), createFilter(PACKAGE_B)); region(REGION_A).connectRegion(region(REGION_C), createFilter(PACKAGE_DUP)); region(REGION_A).connectRegion(region(REGION_D), createFilter(PACKAGE_DUP)); region(REGION_B).connectRegion(region(REGION_C), createFilter(PACKAGE_DUP)); region(REGION_C).connectRegion(region(REGION_D), createFilter(PACKAGE_DUP)); region(REGION_D).connectRegion(region(REGION_A), createFilter(PACKAGE_DUP)); this.candidates.add(packageCapability(BUNDLE_A, PACKAGE_DUP)); this.candidates.add(packageCapability(BUNDLE_B, PACKAGE_DUP)); this.candidates.add(packageCapability(BUNDLE_C, PACKAGE_DUP)); this.candidates.add(packageCapability(BUNDLE_D, PACKAGE_DUP)); Collection<BundleCapability> testCandidates = new ArrayList<BundleCapability>(this.candidates); this.resolverHook.filterMatches(bundleRequirement(BUNDLE_A), testCandidates); assertTrue(testCandidates.contains(packageCapability(BUNDLE_A, PACKAGE_DUP))); assertFalse(testCandidates.contains(packageCapability(BUNDLE_B, PACKAGE_DUP))); assertTrue(testCandidates.contains(packageCapability(BUNDLE_C, PACKAGE_DUP))); assertTrue(testCandidates.contains(packageCapability(BUNDLE_D, PACKAGE_DUP))); testCandidates = new ArrayList<BundleCapability>(this.candidates); this.resolverHook.filterMatches(bundleRequirement(BUNDLE_B), testCandidates); assertTrue(testCandidates.contains(packageCapability(BUNDLE_A, PACKAGE_DUP))); assertTrue(testCandidates.contains(packageCapability(BUNDLE_B, PACKAGE_DUP))); assertTrue(testCandidates.contains(packageCapability(BUNDLE_C, PACKAGE_DUP))); assertTrue(testCandidates.contains(packageCapability(BUNDLE_D, PACKAGE_DUP))); } @Test public void testResolveInCyclicGraph() throws BundleException, InvalidSyntaxException { region(REGION_D).addBundle(bundle(BUNDLE_X)); region(REGION_A).connectRegion(region(REGION_B), createFilter(PACKAGE_D, PACKAGE_X)); region(REGION_B).connectRegion(region(REGION_A), createFilter()); region(REGION_B).connectRegion(region(REGION_D), createFilter(PACKAGE_D)); region(REGION_D).connectRegion(region(REGION_B), createFilter()); region(REGION_B).connectRegion(region(REGION_C), createFilter(PACKAGE_X)); region(REGION_C).connectRegion(region(REGION_B), createFilter()); region(REGION_C).connectRegion(region(REGION_D), createFilter(PACKAGE_X)); region(REGION_D).connectRegion(region(REGION_C), createFilter()); region(REGION_A).connectRegion(region(REGION_C), createFilter()); region(REGION_C).connectRegion(region(REGION_A), createFilter()); region(REGION_D).connectRegion(region(REGION_A), createFilter()); region(REGION_A).connectRegion(region(REGION_D), createFilter()); // Find from region A. this.candidates.add(packageCapability(BUNDLE_B, PACKAGE_B)); this.candidates.add(packageCapability(BUNDLE_C, PACKAGE_C)); this.candidates.add(packageCapability(BUNDLE_D, PACKAGE_D)); this.candidates.add(packageCapability(BUNDLE_X, PACKAGE_X)); this.resolverHook.filterMatches(bundleRequirement(BUNDLE_A), this.candidates); assertEquals(2, this.candidates.size()); assertTrue(this.candidates.contains(packageCapability(BUNDLE_D, PACKAGE_D))); assertTrue(this.candidates.contains(packageCapability(BUNDLE_X, PACKAGE_X))); // Find from region B this.candidates.add(packageCapability(BUNDLE_B, PACKAGE_B)); this.candidates.add(packageCapability(BUNDLE_C, PACKAGE_C)); this.candidates.add(packageCapability(BUNDLE_D, PACKAGE_D)); this.candidates.add(packageCapability(BUNDLE_X, PACKAGE_X)); this.resolverHook.filterMatches(bundleRequirement(BUNDLE_B), this.candidates); assertEquals(3, this.candidates.size()); assertTrue(this.candidates.contains(packageCapability(BUNDLE_B, PACKAGE_B))); assertTrue(this.candidates.contains(packageCapability(BUNDLE_D, PACKAGE_D))); assertTrue(this.candidates.contains(packageCapability(BUNDLE_X, PACKAGE_X))); } @Test public void testResolveFromSystemBundle() { this.candidates.add(packageCapability(BUNDLE_A, PACKAGE_A)); Bundle stubBundle = new StubBundle(0L, "sys", BUNDLE_VERSION, ""); this.resolverHook.filterMatches(new StubBundleRequirement(stubBundle), this.candidates); assertEquals(1, this.candidates.size()); assertTrue(this.candidates.contains(packageCapability(BUNDLE_A, PACKAGE_A))); } @Test public void testResolveFromBundleInNoRegion() { this.candidates.add(packageCapability(BUNDLE_A, PACKAGE_A)); Bundle stranger = createBundle("stranger"); this.resolverHook.filterMatches(new StubBundleRequirement(stranger), this.candidates); assertEquals(0, this.candidates.size()); } @Test public void testUnimplementedMethods() { this.resolverHook.end(); } private BundleCapability packageCapability(final String bundleSymbolicName, String packageName) { return new StubPackageCapability(bundleSymbolicName, packageName); } private BundleCapability bundleCapability(String bundleSymbolicName) { return new StubBundleCapability(bundleSymbolicName); } private Region createRegion(String regionName, String... bundleSymbolicNames) throws BundleException { Region region = this.digraph.createRegion(regionName); for (String bundleSymbolicName : bundleSymbolicNames) { Bundle stubBundle = createBundle(bundleSymbolicName); region.addBundle(stubBundle); } this.regions.put(regionName, region); return region; } private Region region(String regionName) { return this.regions.get(regionName); } private RegionFilter createFilter(final String... packageNames) throws InvalidSyntaxException { Collection<String> filters = new ArrayList<String>(packageNames.length); for (String pkg : packageNames) { filters.add('(' + RegionFilter.VISIBLE_PACKAGE_NAMESPACE + '=' + pkg + ')'); } RegionFilterBuilder builder = digraph.createRegionFilterBuilder(); for (String filter : filters) { builder.allow(RegionFilter.VISIBLE_PACKAGE_NAMESPACE, filter); } return builder.build(); } private RegionFilter createBundleFilter(String bundleSymbolicName, Version bundleVersion) throws InvalidSyntaxException { String bundleFilter = "(&(" + RegionFilter.VISIBLE_BUNDLE_NAMESPACE + '=' + bundleSymbolicName + ')' + '(' + Constants.BUNDLE_VERSION_ATTRIBUTE + ">=" + (bundleVersion == null ? "0" : bundleVersion.toString()) + "))"; RegionFilterBuilder builder = digraph.createRegionFilterBuilder(); return builder.allow(RegionFilter.VISIBLE_BUNDLE_NAMESPACE, bundleFilter).build(); } private Bundle createBundle(String bundleSymbolicName) { Bundle stubBundle = new StubBundle(this.bundleId++, bundleSymbolicName, BUNDLE_VERSION, "loc:" + bundleSymbolicName); this.bundles.put(bundleSymbolicName, stubBundle); return stubBundle; } BundleRequirement bundleRequirement(String bundleSymbolicName) { return new StubBundleRequirement(bundle(bundleSymbolicName)); } Bundle bundle(String bundleSymbolicName) { Bundle bundleA = this.bundles.get(bundleSymbolicName); return bundleA; } final class StubPackageCapability implements BundleCapability { private final String bundleSymbolicName; private final String packageName; StubPackageCapability(String bundleSymbolicName, String packageName) { this.bundleSymbolicName = bundleSymbolicName; this.packageName = packageName; } @Override public String getNamespace() { return BundleRevision.PACKAGE_NAMESPACE; } @Override public Map<String, String> getDirectives() { return new HashMap<String, String>(); } @Override public Map<String, Object> getAttributes() { HashMap<String, Object> attributes = new HashMap<String, Object>(); attributes.put(BundleRevision.PACKAGE_NAMESPACE, this.packageName); return attributes; } @Override public BundleRevision getResource() { return getRevision(); } @Override public BundleRevision getRevision() { return new StubBundleRevision(bundle(this.bundleSymbolicName)); } @Override public int hashCode() { final int prime = 31; int result = 1; result = prime * result + getOuterType().hashCode(); result = prime * result + (this.bundleSymbolicName == null ? 0 : this.bundleSymbolicName.hashCode()); result = prime * result + (this.packageName == null ? 0 : this.packageName.hashCode()); return result; } @Override public boolean equals(Object obj) { if (this == obj) { return true; } if (obj == null) { return false; } if (!(obj instanceof StubPackageCapability)) { return false; } StubPackageCapability other = (StubPackageCapability) obj; if (!getOuterType().equals(other.getOuterType())) { return false; } if (this.bundleSymbolicName == null) { if (other.bundleSymbolicName != null) { return false; } } else if (!this.bundleSymbolicName.equals(other.bundleSymbolicName)) { return false; } if (this.packageName == null) { if (other.packageName != null) { return false; } } else if (!this.packageName.equals(other.packageName)) { return false; } return true; } private RegionResolverHookTests getOuterType() { return RegionResolverHookTests.this; } } final class StubBundleCapability implements BundleCapability { private final String bundleSymbolicName; StubBundleCapability(String bundleSymbolicName) { this.bundleSymbolicName = bundleSymbolicName; } @Override public String getNamespace() { return BundleRevision.BUNDLE_NAMESPACE; } @Override public Map<String, String> getDirectives() { return new HashMap<String, String>(); } @Override public Map<String, Object> getAttributes() { HashMap<String, Object> attributes = new HashMap<String, Object>(); attributes.put(BundleRevision.BUNDLE_NAMESPACE, bundleSymbolicName); return attributes; } @Override public BundleRevision getResource() { return getRevision(); } @Override public BundleRevision getRevision() { return new StubBundleRevision(bundle(this.bundleSymbolicName)); } @Override public int hashCode() { final int prime = 31; int result = 1; result = prime * result + getOuterType().hashCode(); result = prime * result + ((bundleSymbolicName == null) ? 0 : bundleSymbolicName.hashCode()); return result; } @Override public boolean equals(Object obj) { if (this == obj) return true; if (obj == null) return false; if (!(obj instanceof StubBundleCapability)) return false; StubBundleCapability other = (StubBundleCapability) obj; if (!getOuterType().equals(other.getOuterType())) return false; if (bundleSymbolicName == null) { if (other.bundleSymbolicName != null) return false; } else if (!bundleSymbolicName.equals(other.bundleSymbolicName)) return false; return true; } private RegionResolverHookTests getOuterType() { return RegionResolverHookTests.this; } } final class StubBundleRequirement implements BundleRequirement { private final StubBundleRevision bundleRevision; StubBundleRequirement(Bundle bundle) { this.bundleRevision = new StubBundleRevision(bundle); } @Override public String getNamespace() { throw new UnsupportedOperationException(); } @Override public Map<String, String> getDirectives() { throw new UnsupportedOperationException(); } @Override public Map<String, Object> getAttributes() { throw new UnsupportedOperationException(); } @Override public BundleRevision getResource() { return getRevision(); } @Override public BundleRevision getRevision() { return this.bundleRevision; } @Override public boolean matches(BundleCapability capability) { throw new UnsupportedOperationException(); } } final class StubBundleRevision implements BundleRevision { private final Bundle bundle; StubBundleRevision(Bundle bundle) { this.bundle = bundle; } @Override public Bundle getBundle() { return this.bundle; } @Override public String getSymbolicName() { return this.bundle.getSymbolicName(); } @Override public Version getVersion() { return this.bundle.getVersion(); } @Override public List<BundleCapability> getDeclaredCapabilities(String namespace) { throw new UnsupportedOperationException(); } @Override public int getTypes() { throw new UnsupportedOperationException(); } @Override public List<BundleRequirement> getDeclaredRequirements(String namespace) { throw new UnsupportedOperationException(); } @Override public BundleWiring getWiring() { throw new UnsupportedOperationException(); } @SuppressWarnings({"cast", "unchecked", "rawtypes"}) @Override public List<Capability> getCapabilities(String namespace) { return (List<Capability>) (List) getDeclaredCapabilities(namespace); } @SuppressWarnings({"cast", "unchecked", "rawtypes"}) @Override public List<Requirement> getRequirements(String namespace) { return (List<Requirement>) (List) getDeclaredRequirements(namespace); } } }