changeset 920:ce04776b8b81

Merge
author Roman Kennke <rkennke@redhat.com>
date Fri, 18 Jan 2013 18:43:12 +0100
parents 31c6026714f5 (current diff) 4a2301e50857 (diff)
children 5c92b1c6da21 8a6697503832
files
diffstat 6 files changed, 778 insertions(+), 116 deletions(-) [+]
line wrap: on
line diff
--- a/client/living-vm-filter/core/src/test/java/com/redhat/thermostat/client/filter/vm/core/VMFilterActivatorTest.java	Fri Jan 18 18:42:04 2013 +0100
+++ b/client/living-vm-filter/core/src/test/java/com/redhat/thermostat/client/filter/vm/core/VMFilterActivatorTest.java	Fri Jan 18 18:43:12 2013 +0100
@@ -40,13 +40,13 @@
 import static org.mockito.Mockito.mock;
 
 import org.junit.Test;
+import org.osgi.framework.ServiceReference;
+import org.osgi.framework.ServiceRegistration;
 
 import com.redhat.thermostat.client.core.Filter;
 import com.redhat.thermostat.client.osgi.service.MenuAction;
 import com.redhat.thermostat.common.dao.VmInfoDAO;
 import com.redhat.thermostat.test.StubBundleContext;
-import com.redhat.thermostat.test.StubServiceReference;
-import com.redhat.thermostat.test.StubServiceRegistration;
 
 public class VMFilterActivatorTest {
     
@@ -57,8 +57,8 @@
         activator.start(ctx);
         
         VmInfoDAO dao = mock(VmInfoDAO.class);
-        StubServiceRegistration reg = (StubServiceRegistration) ctx.registerService(VmInfoDAO.class, dao, null);
-        StubServiceReference ref = new StubServiceReference(reg.getInfo());
+        ServiceRegistration reg = ctx.registerService(VmInfoDAO.class, dao, null);
+        ServiceReference ref = reg.getReference();
         activator.vmInfoDaoTracker.addingService(ref);
         
         assertTrue(ctx.isServiceRegistered(MenuAction.class.getName(), LivingVMFilterMenuAction.class));
--- a/client/living-vm-filter/swing/src/test/java/com/redhat/thermostat/client/filter/vm/swing/VMFilterActivatorTest.java	Fri Jan 18 18:42:04 2013 +0100
+++ b/client/living-vm-filter/swing/src/test/java/com/redhat/thermostat/client/filter/vm/swing/VMFilterActivatorTest.java	Fri Jan 18 18:43:12 2013 +0100
@@ -40,12 +40,12 @@
 import static org.mockito.Mockito.mock;
 
 import org.junit.Test;
+import org.osgi.framework.ServiceReference;
+import org.osgi.framework.ServiceRegistration;
 
 import com.redhat.thermostat.client.osgi.service.DecoratorProvider;
 import com.redhat.thermostat.common.dao.VmInfoDAO;
 import com.redhat.thermostat.test.StubBundleContext;
-import com.redhat.thermostat.test.StubServiceReference;
-import com.redhat.thermostat.test.StubServiceRegistration;
 
 public class VMFilterActivatorTest {
     
@@ -56,8 +56,8 @@
         activator.start(ctx);
         
         VmInfoDAO dao = mock(VmInfoDAO.class);
-        StubServiceRegistration reg = (StubServiceRegistration) ctx.registerService(VmInfoDAO.class, dao, null);
-        StubServiceReference ref = new StubServiceReference(reg.getInfo());
+        ServiceRegistration reg = ctx.registerService(VmInfoDAO.class, dao, null);
+        ServiceReference ref = reg.getReference();
         activator.vmInfoDaoTracker.addingService(ref);
         
         assertTrue(ctx.isServiceRegistered(DecoratorProvider.class.getName(), LivingVMDecoratorProvider.class));
--- a/common/core/src/main/java/com/redhat/thermostat/test/StubBundleContext.java	Fri Jan 18 18:42:04 2013 +0100
+++ b/common/core/src/main/java/com/redhat/thermostat/test/StubBundleContext.java	Fri Jan 18 18:43:12 2013 +0100
@@ -39,20 +39,27 @@
 import java.io.File;
 import java.io.InputStream;
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.Collection;
 import java.util.Dictionary;
-import java.util.Iterator;
+import java.util.Enumeration;
+import java.util.HashMap;
+import java.util.Hashtable;
 import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
 
 import org.osgi.framework.Bundle;
 import org.osgi.framework.BundleContext;
 import org.osgi.framework.BundleException;
 import org.osgi.framework.BundleListener;
+import org.osgi.framework.Constants;
 import org.osgi.framework.Filter;
 import org.osgi.framework.FrameworkListener;
 import org.osgi.framework.FrameworkUtil;
 import org.osgi.framework.InvalidSyntaxException;
 import org.osgi.framework.ServiceEvent;
+import org.osgi.framework.ServiceFactory;
 import org.osgi.framework.ServiceListener;
 import org.osgi.framework.ServiceReference;
 import org.osgi.framework.ServiceRegistration;
@@ -68,31 +75,23 @@
 public class StubBundleContext implements BundleContext {
 
     static class ServiceInformation {
-        public String serviceInterface;
         public Object implementation;
         public Dictionary properties;
         public int exportedReferences;
 
-        public ServiceInformation(String className, Object impl, Dictionary props) {
-            this.serviceInterface = className;
+        public ServiceInformation(Object impl, Dictionary props) {
             this.implementation = impl;
             this.properties = props;
         }
     }
 
-    static class ListenerSpec {
-        public ServiceListener listener;
-        public Filter filter;
+    private int nextServiceId = 0;
 
-        public ListenerSpec(ServiceListener listener, Filter filter) {
-            this.listener = listener;
-            this.filter = filter;
-        }
-    }
-
+    private Map<String, String> frameworkProperties = new HashMap<>();
     private List<Bundle> bundles = new ArrayList<>();
     private List<ServiceInformation> registeredServices = new ArrayList<>();
-    private List<ListenerSpec> registeredListeners = new ArrayList<>();
+    private Map<ServiceListener, Filter> registeredListeners = new HashMap<>();
+    private Bundle contextBundle = null;
 
     /*
      * Interface methods
@@ -100,12 +99,17 @@
 
     @Override
     public String getProperty(String key) {
-        throw new NotImplementedException();
+        String result = null;
+        result = frameworkProperties.get(key);
+        if (result == null) {
+            result = System.getProperty(key);
+        }
+        return result;
     }
 
     @Override
     public Bundle getBundle() {
-        throw new NotImplementedException();
+        return contextBundle;
     }
 
     @Override
@@ -123,34 +127,40 @@
         if (id > Integer.MAX_VALUE) {
             throw new NotImplementedException();
         }
+        if (id >= bundles.size()) {
+            return null;
+        }
+
         return bundles.get((int) id);
     }
 
     @Override
+    public Bundle getBundle(String location) {
+        throw new NotImplementedException();
+    }
+
+    @Override
     public Bundle[] getBundles() {
-        throw new NotImplementedException();
+        return bundles.toArray(new Bundle[bundles.size()]);
     }
 
     @Override
     public void addServiceListener(ServiceListener listener, String filter) throws InvalidSyntaxException {
-        ListenerSpec spec = new ListenerSpec(listener, createFilter(filter));
-        registeredListeners.add(spec);
+        registeredListeners.put(listener, filter == null ? null : createFilter(filter));
     }
 
     @Override
     public void addServiceListener(ServiceListener listener) {
-        throw new NotImplementedException();
+        try {
+            addServiceListener(listener, null);
+        } catch (InvalidSyntaxException e) {
+            throw new AssertionError("a null filter can not have invalid systax");
+        }
     }
 
     @Override
     public void removeServiceListener(ServiceListener listener) {
-        Iterator<ListenerSpec> iter = registeredListeners.iterator();
-        while (iter.hasNext()) {
-            ListenerSpec item = iter.next();
-            if (item.listener == listener) {
-                iter.remove();
-            }
-        }
+        registeredListeners.remove(listener);
     }
 
     @Override
@@ -179,70 +189,125 @@
     }
 
     @Override
-    public ServiceRegistration registerService(String[] clazzes, Object service, Dictionary properties) {
-        throw new NotImplementedException();
+    public ServiceRegistration registerService(String className, Object service, Dictionary properties) {
+        return registerService(new String[] { className }, service, properties);
     }
 
     @Override
-    public ServiceRegistration registerService(String className, Object service, Dictionary properties) {
-        ServiceInformation info = new ServiceInformation(className, service, properties);
+    public ServiceRegistration registerService(String[] clazzes, Object service, Dictionary properties) {
+        if (service instanceof ServiceFactory) {
+            throw new NotImplementedException("support for service factories is not implemented");
+        }
+
+        for (String className : clazzes) {
+            try {
+                Class<?> clazz = Class.forName(className);
+                if (!clazz.isAssignableFrom(service.getClass())) {
+                    throw new IllegalArgumentException("service is not a subclass of " + className);
+                }
+            } catch (ClassNotFoundException classNotFound) {
+                throw new IllegalArgumentException("not a valid class: " + className);
+            }
+        }
+
+        Object specifiedRanking = null;
+        Hashtable<String, Object> newProperties = new Hashtable<>();
+        if (properties != null) {
+            Enumeration<?> enumeration = properties.keys();
+            while (enumeration.hasMoreElements()) {
+                Object key = enumeration.nextElement();
+                newProperties.put((String)key, properties.get(key));
+            }
+            specifiedRanking = properties.get(Constants.SERVICE_RANKING);
+        }
+
+        newProperties.put(Constants.OBJECTCLASS, clazzes);
+        newProperties.put(Constants.SERVICE_ID, nextServiceId);
+        nextServiceId++;
+        if (specifiedRanking == null || !(specifiedRanking instanceof Integer)) {
+            specifiedRanking = 0;
+        }
+        newProperties.put(Constants.SERVICE_RANKING, (Integer) specifiedRanking);
+
+        ServiceInformation info = new ServiceInformation(service, newProperties);
         registeredServices.add(info);
 
-        notifyServiceChange(new StubServiceReference(info), true);
+        notifyServiceChange(new StubServiceReference(info, contextBundle), true);
 
         return new StubServiceRegistration(this, info);
     }
 
     @Override
-    public ServiceReference[] getServiceReferences(String clazz, String filter) throws InvalidSyntaxException {
-        List<ServiceReference> toReturn = new ArrayList<>();
+    public ServiceReference getServiceReference(Class clazz) {
+        return getServiceReference(clazz.getName());
+    }
+
+    @Override
+    public ServiceReference getServiceReference(String clazz) {
+        try {
+            ServiceReference[] initial = getServiceReferences(clazz, null);
+            if (initial == null) {
+                return null;
+            }
 
-        if (filter == null) {
-            for (ServiceInformation info : registeredServices) {
-                if (info.serviceInterface.equals(clazz)) {
-                    toReturn.add(new StubServiceReference(info));
-                }
-            }
-        } else {
-            Filter toMatch = createFilter(filter);
-            for (ServiceInformation info : registeredServices) {
-                if (info.serviceInterface.equals(clazz) && toMatch.match(info.properties)) {
-                    toReturn.add(new StubServiceReference(info));
-                }
+            Arrays.sort(initial);
+            return initial[initial.length-1];
+        } catch (InvalidSyntaxException invalidFilterSyntax) {
+            throw new AssertionError("a null filter can not have an invalid syntax");
+        }
+    }
+
+    @Override
+    public Collection getServiceReferences(Class clazz, String filter) throws InvalidSyntaxException {
+        return Arrays.asList(getServiceReferences(clazz.getName(), filter));
+    }
+
+    @Override
+    public ServiceReference[] getServiceReferences(String clazz, String filter) throws InvalidSyntaxException {
+        ServiceReference[] allRefs = getAllServiceReferences(clazz, filter);
+        if (allRefs == null) {
+            return null;
+        }
+
+        List<ServiceReference> result = new ArrayList<>();
+        for (ServiceReference ref : allRefs) {
+            if (ref.isAssignableTo(contextBundle, clazz)) {
+                result.add(ref);
             }
         }
-        return toReturn.toArray(new ServiceReference[0]);
+        return result.toArray(new ServiceReference[0]);
     }
 
     @Override
     public ServiceReference[] getAllServiceReferences(String clazz, String filter) throws InvalidSyntaxException {
-        throw new NotImplementedException();
-    }
+        List<ServiceReference> toReturn = new ArrayList<>();
+
+        Filter toMatch = (filter == null) ? null : createFilter(filter);
 
-    @Override
-    public ServiceReference getServiceReference(String clazz) {
-        ServiceReference result = null;
         for (ServiceInformation info : registeredServices) {
-            if (info.serviceInterface.equals(clazz)) {
-                result = new StubServiceReference(info);
+            for (String serviceInterface : (String[]) info.properties.get(Constants.OBJECTCLASS)) {
+                if (clazz == null || serviceInterface.equals(clazz)) {
+                    if (toMatch == null || toMatch.match(info.properties)) {
+                        toReturn.add(new StubServiceReference(info, contextBundle));
+                    }
+                }
             }
         }
-        return result;
+
+        if (toReturn.size() == 0) {
+            return null;
+        }
+        return toReturn.toArray(new ServiceReference[0]);
     }
 
-    @Override
-    public ServiceReference getServiceReference(Class clazz) {
-        for (ServiceInformation info : registeredServices) {
-            if (info.serviceInterface.equals(clazz.getName())) {
-                return new StubServiceReference(info);
-            }
-        }
-        return null;
-    }
 
     @Override
-    public Collection getServiceReferences(Class clazz, String filter) throws InvalidSyntaxException {
-        throw new NotImplementedException();
+    public Filter createFilter(String filter) throws InvalidSyntaxException {
+        // FIXME this will break service trackers if FrameworkUtil is mocked.
+        // The following call will return null if FrameworkUtil is mocked.
+        // that's a problem because this is meant to be used (mostly) in test
+        // environments and that's where FrameworkUtil is likely to be mocked.
+        return FrameworkUtil.createFilter(filter);
     }
 
     @Override
@@ -260,6 +325,9 @@
         if (info.exportedReferences == 0) {
             return false;
         }
+        if (!registeredServices.contains(info)) {
+            return false;
+        }
         info.exportedReferences--;
         return true;
     }
@@ -269,32 +337,30 @@
         throw new NotImplementedException();
     }
 
-    @Override
-    public Filter createFilter(String filter) throws InvalidSyntaxException {
-        // FIXME this will break service trackers if FrameworkUtil is mocked
-        // the following call will return null if FrameworkUtil is mocked.
-        // that's a problem because this is meant to be used (mostly) in test
-        // environments and that's where FrameworkUtil is likely to be mocked.
-        return FrameworkUtil.createFilter(filter);
-    }
-
-    @Override
-    public Bundle getBundle(String location) {
-        throw new NotImplementedException();
-    }
-
     /*
      * Our custom methods
      */
+    public void setProperty(String key, String value) {
+        frameworkProperties.put(key, value);
+    }
 
+    /** Set the context bundle */
+    public void setBundle(Bundle bundle) {
+        this.contextBundle = bundle;
+    }
+
+    /** Set the bundle associated with an id */
     public void setBundle(int i, Bundle bundle) {
         bundles.add(i, bundle);
     }
 
     public boolean isServiceRegistered(String serviceName, Class<?> implementationClass) {
         for (ServiceInformation info : registeredServices) {
-            if (info.serviceInterface.equals(serviceName) && info.implementation.getClass().equals(implementationClass)) {
-                return true;
+            for (String serviceInterface : (String[]) info.properties.get(Constants.OBJECTCLASS)) {
+                if (serviceInterface.equals(serviceName)
+                        && info.implementation.getClass().equals(implementationClass)) {
+                    return true;
+                }
             }
         }
         return false;
@@ -304,24 +370,26 @@
         return registeredServices;
     }
 
-    public Collection<ListenerSpec> getServiceListeners() {
-        return registeredListeners;
+    public Collection<ServiceListener> getServiceListeners() {
+        return registeredListeners.keySet();
     }
 
     public void removeService(ServiceInformation info) {
         if (!registeredServices.contains(info)) {
-            throw new IllegalStateException("service not registered");
+            throw new IllegalArgumentException("service not registered");
         }
         registeredServices.remove(info);
-        notifyServiceChange(new StubServiceReference(info), false);
+        notifyServiceChange(new StubServiceReference(info, contextBundle), false);
     }
 
     private void notifyServiceChange(ServiceReference serviceReference, boolean registered) {
         int eventType = registered ? ServiceEvent.REGISTERED : ServiceEvent.UNREGISTERING;
         ServiceEvent event = new ServiceEvent(eventType, serviceReference);
-        for (ListenerSpec l : registeredListeners) {
-            if (l.filter.match(serviceReference)) {
-                l.listener.serviceChanged(event);
+        for (Entry<ServiceListener, Filter> entry : registeredListeners.entrySet()) {
+            ServiceListener listener = entry.getKey();
+            Filter filter = entry.getValue();
+            if (filter == null || filter.match(serviceReference)) {
+                listener.serviceChanged(event);
             }
         }
     }
--- a/common/core/src/main/java/com/redhat/thermostat/test/StubServiceReference.java	Fri Jan 18 18:42:04 2013 +0100
+++ b/common/core/src/main/java/com/redhat/thermostat/test/StubServiceReference.java	Fri Jan 18 18:43:12 2013 +0100
@@ -50,39 +50,33 @@
 
 public class StubServiceReference implements ServiceReference {
 
-    private ServiceInformation information;
+    private final Bundle sourceBundle;
+    private final ServiceInformation information;
 
-    public StubServiceReference(ServiceInformation info) {
+    public StubServiceReference(ServiceInformation info, Bundle sourceBundle) {
         this.information = info;
+        this.sourceBundle = sourceBundle;
     }
 
     @Override
     public Object getProperty(String key) {
-        if (Constants.OBJECTCLASS.equals(key)) {
-            return new String[] { information.serviceInterface };
-        }
-
         return information.properties.get(key);
     }
 
     @Override
     public String[] getPropertyKeys() {
-        if (information.properties == null) {
-            return new String[] { Constants.OBJECTCLASS };
-        } else {
-            Dictionary props = information.properties;
-            List<String> toReturn = new ArrayList<>(props.size());
-            Enumeration keyEnumeration = props.keys();
-            while (keyEnumeration.hasMoreElements()) {
-                toReturn.add((String) keyEnumeration.nextElement());
-            }
-            return toReturn.toArray(new String[0]);
+        Dictionary props = information.properties;
+        List<String> toReturn = new ArrayList<>(props.size());
+        Enumeration keyEnumeration = props.keys();
+        while (keyEnumeration.hasMoreElements()) {
+            toReturn.add((String) keyEnumeration.nextElement());
         }
+        return toReturn.toArray(new String[0]);
     }
 
     @Override
     public Bundle getBundle() {
-        throw new NotImplementedException();
+        return sourceBundle;
     }
 
     @Override
@@ -92,12 +86,39 @@
 
     @Override
     public boolean isAssignableTo(Bundle bundle, String className) {
+        if (sourceBundle == bundle) {
+            return true;
+        }
         throw new NotImplementedException();
     }
 
     @Override
     public int compareTo(Object reference) {
-        throw new NotImplementedException();
+        if (!(reference instanceof ServiceReference)) {
+            throw new NotImplementedException();
+        }
+
+        ServiceReference ref = (ServiceReference) reference;
+
+        Integer myRanking = (Integer) getProperty(Constants.SERVICE_RANKING);
+        Integer otherRanking = (Integer) ref.getProperty(Constants.SERVICE_RANKING);
+
+        if (myRanking > otherRanking) {
+            return 1;
+        } else if (myRanking < otherRanking) {
+            return -1;
+        } else {
+            Integer myServiceId = (Integer) getProperty(Constants.SERVICE_ID);
+            Integer otherServiceId = (Integer) ref.getProperty(Constants.SERVICE_ID);
+
+            if (myServiceId < otherServiceId) {
+                return 1;
+            } else if (myServiceId > otherServiceId) {
+                return -1;
+            } else {
+                return 0;
+            }
+        }
     }
 
     public ServiceInformation getInformation() {
--- a/common/core/src/main/java/com/redhat/thermostat/test/StubServiceRegistration.java	Fri Jan 18 18:42:04 2013 +0100
+++ b/common/core/src/main/java/com/redhat/thermostat/test/StubServiceRegistration.java	Fri Jan 18 18:43:12 2013 +0100
@@ -56,7 +56,7 @@
 
     @Override
     public ServiceReference getReference() {
-        throw new NotImplementedException();
+        return new StubServiceReference(info, bundleContext.getBundle());
     }
 
     @Override
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/common/core/src/test/java/com/redhat/thermostat/test/StubBundleContextTest.java	Fri Jan 18 18:43:12 2013 +0100
@@ -0,0 +1,573 @@
+/*
+ * Copyright 2013 Red Hat, Inc.
+ *
+ * This file is part of Thermostat.
+ *
+ * Thermostat is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published
+ * by the Free Software Foundation; either version 2, or (at your
+ * option) any later version.
+ *
+ * Thermostat is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Thermostat; see the file COPYING. If not see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * Linking this code with other modules is making a combined work
+ * based on this code. Thus, the terms and conditions of the GNU
+ * General Public License cover the whole combination.
+ *
+ * As a special exception, the copyright holders of this code give
+ * you permission to link this code with independent modules to
+ * produce an executable, regardless of the license terms of these
+ * independent modules, and to copy and distribute the resulting
+ * executable under terms of your choice, provided that you also
+ * meet, for each linked independent module, the terms and conditions
+ * of the license of that module. An independent module is a module
+ * which is not derived from or based on this code. If you modify
+ * this code, you may extend this exception to your version of the
+ * library, but you are not obligated to do so. If you do not wish
+ * to do so, delete this exception statement from your version.
+ */
+
+package com.redhat.thermostat.test;
+
+import static org.junit.Assert.assertArrayEquals;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertSame;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+import static org.mockito.Matchers.isA;
+import static org.mockito.Mockito.atLeast;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.verify;
+
+import java.io.ByteArrayInputStream;
+import java.util.Collection;
+import java.util.Hashtable;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.ArgumentCaptor;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.BundleException;
+import org.osgi.framework.BundleListener;
+import org.osgi.framework.Constants;
+import org.osgi.framework.Filter;
+import org.osgi.framework.FrameworkListener;
+import org.osgi.framework.InvalidSyntaxException;
+import org.osgi.framework.ServiceEvent;
+import org.osgi.framework.ServiceListener;
+import org.osgi.framework.ServiceReference;
+import org.osgi.framework.ServiceRegistration;
+
+import com.redhat.thermostat.common.NotImplementedException;
+import com.redhat.thermostat.test.StubBundleContext.ServiceInformation;
+
+/**
+ * Test that StubBundleContext behaves like a BundleContext, based on what is
+ * specified in the OSGi spec. Other optional methods to help in testing are
+ * great, but adherence to spec is most important.
+ */
+public class StubBundleContextTest {
+
+    private StubBundleContext bundleContext;
+
+    @Before
+    public void setUp() {
+        bundleContext = new StubBundleContext();
+        assertNotNull(bundleContext);
+    }
+
+    @Test
+    public void testSetAndGetContextBundle() {
+        assertEquals(null, bundleContext.getBundle());
+
+        Bundle bundle = mock(Bundle.class);
+        bundleContext.setBundle(bundle);
+        assertSame(bundle, bundleContext.getBundle());
+    }
+
+    @Test(expected = NotImplementedException.class)
+    public void testInstallBundleFromLocation() throws BundleException {
+        bundleContext.installBundle("");
+    }
+
+    @Test(expected = NotImplementedException.class)
+    public void testInstallBundleFromInputStream() throws BundleException {
+        bundleContext.installBundle("", new ByteArrayInputStream(new byte[0]));
+    }
+
+    @Test
+    public void testSetAndGetBundles() {
+        assertEquals(null, bundleContext.getBundle(0));
+
+        Bundle systemBundle = mock(Bundle.class);
+        bundleContext.setBundle(0, systemBundle);
+
+        assertEquals(systemBundle, bundleContext.getBundle(0));
+
+        assertArrayEquals(new Bundle[] { systemBundle }, bundleContext.getBundles());
+    }
+
+    @Test(expected = NotImplementedException.class)
+    public void testGetBundleByLargeId() {
+        bundleContext.getBundle(Long.MAX_VALUE);
+    }
+
+    @Test(expected = NotImplementedException.class)
+    public void testGetBundleByLocation() {
+        bundleContext.getBundle("");
+    }
+
+    @Test
+    public void testAddRemoveFrameworkListener() {
+        try {
+            bundleContext.addFrameworkListener(mock(FrameworkListener.class));
+            fail("not expected");
+        } catch (NotImplementedException notImplemented) {
+            /* okay: expected */
+        }
+
+        try {
+            bundleContext.removeFrameworkListener(mock(FrameworkListener.class));
+            fail("not expected");
+        } catch (NotImplementedException notImplemented) {
+            /* okay: expected */
+        }
+    }
+
+    @Test
+    public void testAddRemoveBundleListener() {
+        try {
+            bundleContext.addBundleListener(mock(BundleListener.class));
+            fail("not expected");
+        } catch (NotImplementedException notImplemented) {
+            /* okay: expected */
+        }
+
+        try {
+            bundleContext.removeBundleListener(mock(BundleListener.class));
+            fail("not expected");
+        } catch (NotImplementedException notImplemented) {
+            /* okay: expected */
+        }
+    }
+
+    @Test
+    public void testGetPropertyDelegatesToSystemProperties() {
+        assertEquals(System.getProperty("user.name"), bundleContext.getProperty("user.name"));
+    }
+
+    @Test
+    public void testGetAndSetProperty() {
+        final String PROPERTY_NAME = "foo.bar.baz";
+        final String PROPERTY_NEW_VALUE = "spam.eggs";
+
+        assertEquals(null, bundleContext.getProperty(PROPERTY_NAME));
+
+        bundleContext.setProperty(PROPERTY_NAME, PROPERTY_NEW_VALUE);
+
+        assertEquals(PROPERTY_NEW_VALUE, bundleContext.getProperty(PROPERTY_NAME));
+    }
+
+    @Test
+    public void testFilterCreationReturnsASaneFilter() throws InvalidSyntaxException {
+        Filter filter = bundleContext.createFilter("(foo=bar)");
+        assertNotNull(filter);
+
+        Hashtable<String, String> dict = new Hashtable<>();
+        dict.put("foo", "bar");
+
+        assertTrue(filter.match(dict));
+    }
+
+    @Test
+    public void testAddRemoveService() {
+        TestService service = mock(TestService.class);
+
+        assertEquals(0, bundleContext.getAllServices().size());
+        assertFalse(bundleContext.isServiceRegistered(TestService.class.getName(), service.getClass()));
+
+        ServiceRegistration registration = bundleContext.registerService(TestService.class, service, null);
+
+        assertEquals(1, bundleContext.getAllServices().size());
+        assertTrue(bundleContext.isServiceRegistered(TestService.class.getName(), service.getClass()));
+
+        registration.unregister();
+
+        assertEquals(0, bundleContext.getAllServices().size());
+        assertFalse(bundleContext.isServiceRegistered(TestService.class.getName(), service.getClass()));
+    }
+
+    @Test(expected = IllegalArgumentException.class)
+    public void testAddServiceThatDoesNotImplementInterfaceClass() {
+        AnotherTestService service = mock(AnotherTestService.class);
+
+        bundleContext.registerService(TestService.class.getName(), service, null);
+    }
+
+    @Test(expected = IllegalArgumentException.class)
+    public void testAddServiceThatDoesNotImplementInterfaceClass2() {
+        AnotherTestService service = mock(AnotherTestService.class);
+
+        bundleContext.registerService("foo.bar.Baz", service, null);
+    }
+
+    @Test
+    public void testAddServiceWithProperties() {
+        TestService service = mock(TestService.class);
+
+        Hashtable props = new Hashtable();
+        props.put(Constants.SERVICE_ID, -10);
+        props.put(Constants.SERVICE_RANKING, 10);
+        props.put(Constants.OBJECTCLASS, AnotherTestService.class.getName());
+
+        ServiceRegistration reg = bundleContext.registerService(TestService.class, service, props);
+
+        ServiceReference serviceRef = reg.getReference();
+
+        assertArrayEquals(new String[] { TestService.class.getName() },
+                (String[]) serviceRef.getProperty(Constants.OBJECTCLASS));
+
+        assertEquals(10, serviceRef.getProperty(Constants.SERVICE_RANKING));
+        assertEquals(0, serviceRef.getProperty(Constants.SERVICE_ID));
+    }
+
+    @Test
+    public void testAddServiceWithNonIntegerRanking() {
+        TestService service = mock(TestService.class);
+
+        Hashtable props = new Hashtable();
+        props.put(Constants.SERVICE_RANKING, new Object());
+
+        ServiceRegistration reg = bundleContext.registerService(TestService.class, service, props);
+
+        ServiceReference serviceRef = reg.getReference();
+
+        assertEquals(0, serviceRef.getProperty(Constants.SERVICE_RANKING));
+    }
+
+    @Test(expected = IllegalArgumentException.class)
+    public void testRemoveUnknownService() {
+        bundleContext.removeService(new ServiceInformation(mock(TestService.class), new Hashtable()));
+    }
+
+    @Test
+    public void testAddingAndRemovingListenerWorks() {
+        assertEquals(0, bundleContext.getServiceListeners().size());
+
+        ServiceListener listener = mock(ServiceListener.class);
+
+        bundleContext.addServiceListener(listener);
+
+        assertEquals(1, bundleContext.getServiceListeners().size());
+        assertEquals(listener, bundleContext.getServiceListeners().toArray()[0]);
+
+        bundleContext.removeServiceListener(listener);
+
+        assertEquals(0, bundleContext.getServiceListeners().size());
+    }
+
+    @Test
+    public void testAddingSameListenerReplacedTheOldOne() {
+        assertEquals(0, bundleContext.getServiceListeners().size());
+
+        ServiceListener listener = mock(ServiceListener.class);
+
+        bundleContext.addServiceListener(listener);
+
+        assertEquals(1, bundleContext.getServiceListeners().size());
+
+        bundleContext.addServiceListener(listener);
+
+        assertEquals(1, bundleContext.getServiceListeners().size());
+    }
+
+    @Test
+    public void testRemovingAnUnknownListenerIsANoOp() {
+        ServiceListener listener = mock(ServiceListener.class);
+        bundleContext.addServiceListener(listener);
+        assertEquals(1, bundleContext.getServiceListeners().size());
+
+        ServiceListener unknownListener = mock(ServiceListener.class);
+        bundleContext.removeServiceListener(unknownListener);
+
+        assertEquals(1, bundleContext.getServiceListeners().size());
+    }
+
+    @Test
+    public void testServiceListenersWithoutFiltersWork() {
+        ServiceListener listener = mock(ServiceListener.class);
+        bundleContext.addServiceListener(listener);
+
+        bundleContext.registerService(TestService.class, mock(TestService.class), null);
+
+        assertListenerRecievedRegisteredEvent(listener);
+    }
+
+    @Test
+    public void testServiceListenersWithFiltersWork() throws InvalidSyntaxException {
+        ServiceListener specificListener = mock(ServiceListener.class);
+        String specificFilter = "(" + Constants.OBJECTCLASS + "=" + TestService.class.getName() + ")";
+        bundleContext.addServiceListener(specificListener, specificFilter);
+
+        ServiceListener allListener = mock(ServiceListener.class);
+        String allListenerFilter = "(" + Constants.OBJECTCLASS + "=*)";
+        bundleContext.addServiceListener(allListener, allListenerFilter);
+
+        ServiceListener otherListener = mock(ServiceListener.class);
+        String otherListenerFilter = "(" + Constants.OBJECTCLASS + "=foo.bar.Baz)";
+        bundleContext.addServiceListener(otherListener, otherListenerFilter);
+
+        ServiceRegistration registration = bundleContext.registerService(TestService.class, mock(TestService.class), null);
+
+        assertListenerRecievedRegisteredEvent(specificListener);
+        assertListenerRecievedRegisteredEvent(allListener);
+
+        registration.unregister();
+
+        assertListenerRecievedUnregisteringEvent(specificListener);
+        assertListenerRecievedUnregisteringEvent(allListener);
+
+        verify(otherListener, never()).serviceChanged(isA(ServiceEvent.class));
+
+    }
+
+    private void assertListenerRecievedRegisteredEvent(ServiceListener listener) {
+        ArgumentCaptor<ServiceEvent> eventCaptor = ArgumentCaptor.forClass(ServiceEvent.class);
+        verify(listener).serviceChanged(eventCaptor.capture());
+        assertEquals(1, eventCaptor.getAllValues().size());
+        ServiceEvent event = eventCaptor.getValue();
+        assertEquals(ServiceEvent.REGISTERED, event.getType());
+        ServiceReference ref = event.getServiceReference();
+        assertTrue(ref instanceof StubServiceReference);
+    }
+
+    private void assertListenerRecievedUnregisteringEvent(ServiceListener listener) {
+        ArgumentCaptor<ServiceEvent> eventCaptor = ArgumentCaptor.forClass(ServiceEvent.class);
+        verify(listener, atLeast(1)).serviceChanged(eventCaptor.capture());
+
+        ServiceEvent event = eventCaptor.getValue();
+        assertEquals(ServiceEvent.UNREGISTERING, event.getType());
+        ServiceReference ref = event.getServiceReference();
+        assertTrue(ref instanceof StubServiceReference);
+    }
+
+    @Test
+    public void testGetServiceReferenceReturnsNullForNoMatch() {
+        assertNull(bundleContext.getServiceReference(TestService.class));
+    }
+
+    @Test
+    public void testGetServiceReferenceReturnsServiceWithHighestServiceRanking() {
+        TestService service1 = mock(TestService.class);
+        TestService service2 = mock(TestService.class);
+
+        Hashtable service2Props = new Hashtable();
+        service2Props.put(Constants.SERVICE_RANKING, 1000);
+
+        bundleContext.registerService(TestService.class, service1, null);
+        bundleContext.registerService(TestService.class, service2, service2Props);
+
+        ServiceReference ref = bundleContext.getServiceReference(TestService.class);
+
+        assertSame(service2, bundleContext.getService(ref));
+    }
+
+    @Test
+    public void testGetServiceReferenceReturnsServiceWithLowerServiceId() {
+        TestService service1 = mock(TestService.class);
+        TestService service2 = mock(TestService.class);
+
+        bundleContext.registerService(TestService.class, service1, null);
+        bundleContext.registerService(TestService.class, service2, null);
+
+        ServiceReference ref = bundleContext.getServiceReference(TestService.class);
+
+        assertSame(service1, bundleContext.getService(ref));
+    }
+
+    @Test
+    public void testGetServiceReferences() throws InvalidSyntaxException {
+        TestService service = mock(TestService.class);
+
+        bundleContext.registerService(TestService.class, service, null);
+        bundleContext.registerService(AnotherTestService.class, mock(AnotherTestService.class), null);
+
+        Collection refs = bundleContext.getServiceReferences(TestService.class, null);
+
+        assertEquals(1, refs.size());
+        assertEquals(service, ((StubServiceReference) refs.toArray()[0]).getInformation().implementation);
+    }
+
+    @Test
+    public void testGetAllServiceReferencesNoMatch() throws InvalidSyntaxException {
+        TestService service = mock(TestService.class);
+
+        bundleContext.registerService(TestService.class, service, null);
+        bundleContext.registerService(TestService.class, mock(TestService.class), null);
+
+        ServiceReference[] refs = bundleContext.getAllServiceReferences(AnotherTestService.class.getName(), null);
+
+        assertEquals(null, (Object) refs);
+    }
+
+    @Test
+    public void testGetAllServiceReferencesWithoutFilter() throws InvalidSyntaxException {
+        TestService service = mock(TestService.class);
+
+        bundleContext.registerService(TestService.class, service, null);
+        bundleContext.registerService(AnotherTestService.class, mock(AnotherTestService.class), null);
+
+        ServiceReference[] refs = bundleContext.getAllServiceReferences(TestService.class.getName(), null);
+
+        assertEquals(1, refs.length);
+        assertEquals(service, ((StubServiceReference) refs[0]).getInformation().implementation);
+    }
+
+    @Test
+    public void testGetAllServiceReferencesWithFilter() throws InvalidSyntaxException {
+        TestService service = mock(TestService.class);
+
+        bundleContext.registerService(TestService.class, service, null);
+        bundleContext.registerService(AnotherTestService.class, mock(AnotherTestService.class), null);
+
+        String filter = "(" + Constants.OBJECTCLASS + "=" + TestService.class.getName() + ")";
+        ServiceReference[] refs = bundleContext.getAllServiceReferences(TestService.class.getName(), filter);
+
+        assertEquals(1, refs.length);
+        assertEquals(service, ((StubServiceReference) refs[0]).getInformation().implementation);
+    }
+
+    @Test
+    public void testGetAllServiceReferencesWithNoClassNameJustFilter() throws InvalidSyntaxException {
+        TestService service = mock(TestService.class);
+
+        bundleContext.registerService(TestService.class, service, null);
+        bundleContext.registerService(AnotherTestService.class, mock(AnotherTestService.class), null);
+
+        String filter = "(" + Constants.OBJECTCLASS + "=" + TestService.class.getName() + ")";
+        ServiceReference[] refs = bundleContext.getAllServiceReferences(null, filter);
+
+        assertEquals(1, refs.length);
+        assertEquals(service, ((StubServiceReference) refs[0]).getInformation().implementation);
+    }
+
+    @Test
+    public void testGetServiceUsingClassObject() {
+        TestService service = mock(TestService.class);
+
+        bundleContext.registerService(TestService.class, service, null);
+
+        ServiceReference ref = bundleContext.getServiceReference(TestService.class);
+        assertNotNull(ref);
+
+        Object returnedService = bundleContext.getService(ref);
+
+        assertSame(service, returnedService);
+    }
+
+    @Test
+    public void testGetServiceUsingClassName() {
+        TestService service = mock(TestService.class);
+
+        bundleContext.registerService(TestService.class, service, null);
+
+        ServiceReference ref = bundleContext.getServiceReference(TestService.class.getName());
+        assertNotNull(ref);
+
+        Object returnedService = bundleContext.getService(ref);
+
+        assertSame(service, returnedService);
+    }
+
+    @Test
+    public void testUngetService() {
+        bundleContext.registerService(TestService.class, mock(TestService.class), null);
+
+        ServiceReference ref = bundleContext.getServiceReference(TestService.class.getName());
+        Object returnedService = bundleContext.getService(ref);
+
+        assertTrue(bundleContext.ungetService(ref));
+
+        assertFalse(bundleContext.ungetService(ref));
+    }
+
+    @Test
+    public void testUngetServiceOnUnregisteredService() {
+        ServiceRegistration registration = bundleContext.registerService(TestService.class, mock(TestService.class), null);
+
+        ServiceReference ref = bundleContext.getServiceReference(TestService.class.getName());
+        Object returnedService = bundleContext.getService(ref);
+
+        registration.unregister();
+
+        assertFalse(bundleContext.ungetService(ref));
+    }
+
+    @Test
+    public void testReferenceExportCountIsCorrect() {
+        TestService service = mock(TestService.class);
+
+        ServiceRegistration reg = bundleContext.registerService(TestService.class, service, null);
+
+        ServiceReference ref = bundleContext.getServiceReference(TestService.class);
+        assertNotNull(ref);
+
+        assertEquals(0, bundleContext.getExportedServiceCount(reg));
+
+        Object returnedService = bundleContext.getService(ref);
+
+        assertEquals(1, bundleContext.getExportedServiceCount(reg));
+
+        bundleContext.ungetService(ref);
+
+        assertEquals(0, bundleContext.getExportedServiceCount(reg));
+    }
+
+    @Test(expected = NotImplementedException.class)
+    public void testGetDataFile() {
+        bundleContext.getDataFile("");
+    }
+
+    @Test
+    public void testIsServiceRegisteredForRegisteredService() {
+        ComplexService service = mock(ComplexService.class);
+
+        String[] ifaces = new String[] { TestService.class.getName(), AnotherTestService.class.getName() };
+        ServiceRegistration reg = bundleContext.registerService(ifaces, service, null);
+
+        assertTrue(bundleContext.isServiceRegistered(AnotherTestService.class.getName(), service.getClass()));
+
+    }
+
+    @Test
+    public void testIsServiceRegisteredForUnregisteredService() {
+        ComplexService service = mock(ComplexService.class);
+        TestService someOtherService = mock(TestService.class);
+
+        String[] ifaces = new String[] { TestService.class.getName(), AnotherTestService.class.getName() };
+        ServiceRegistration reg = bundleContext.registerService(ifaces, service, null);
+
+        assertFalse(bundleContext.isServiceRegistered(AnotherTestService.class.getName(), someOtherService.getClass()));
+    }
+
+    /*
+     * Dummy service interfaces
+     */
+
+    static interface TestService { /* just a marker */}
+
+    static interface AnotherTestService { /* just a marker */}
+
+    static interface ComplexService extends TestService, AnotherTestService { /* ditto */}
+}