FELIX-1030: add device access tests

git-svn-id: https://svn.apache.org/repos/asf/felix/trunk@765537 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/deviceaccess/pom.xml b/deviceaccess/pom.xml
index e22b55a..9968fc2 100644
--- a/deviceaccess/pom.xml
+++ b/deviceaccess/pom.xml
@@ -18,25 +18,35 @@
  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>

-    <parent>

-        <groupId>org.apache.felix</groupId>

-        <artifactId>felix</artifactId>

-        <version>1.0.4</version>

-        <relativePath>../pom/pom.xml</relativePath>

-    </parent>

 

-    <artifactId>org.apache.felix.devicemanager</artifactId>

-    <version>0.9.0-SNAPSHOT</version>

-    <packaging>bundle</packaging>

+  <modelVersion>4.0.0</modelVersion>

 

-    <name>Apache Felix Device Manager</name>

-    <description>

-        Implementation of the OSGi Device Access Specification 1.1

-    </description>

+  <parent>

+    <groupId>org.apache.felix</groupId>

+    <artifactId>felix</artifactId>

+    <version>1.0.5-SNAPSHOT</version>

+    <relativePath>../pom/pom.xml</relativePath>

+  </parent>

+

+  <artifactId>org.apache.felix.devicemanager</artifactId>

+  <version>0.9.0-SNAPSHOT</version>

+  <packaging>bundle</packaging>

+

+  <name>Apache Felix Device Manager</name>

+

+  <description>

+      Implementation of the OSGi Device Access Specification 1.1

+  </description>

+

   <dependencies>

     <dependency>

       <groupId>${pom.groupId}</groupId>

+      <artifactId>org.apache.felix.framework</artifactId>

+      <version>1.6.0</version>

+      <scope>test</scope>

+    </dependency>

+    <dependency>

+      <groupId>${pom.groupId}</groupId>

       <artifactId>org.osgi.core</artifactId>

       <version>1.0.0</version>

       <scope>provided</scope>

@@ -50,17 +60,14 @@
     <dependency>

       <groupId>${pom.groupId}</groupId>

       <artifactId>org.apache.felix.dependencymanager</artifactId>

-      <version>2.0.1-SNAPSHOT</version>

+      <version>2.0.2-SNAPSHOT</version>

       <scope>provided</scope>

     </dependency>

-    </dependencies>

+  </dependencies>

+

   <build>

     <plugins>

       <plugin>

-        <artifactId>maven-surefire-plugin</artifactId>

-        <version>2.3</version>

-      </plugin>

-      <plugin>

         <groupId>org.apache.felix</groupId>

         <artifactId>maven-bundle-plugin</artifactId>

         <version>1.4.0</version>

diff --git a/deviceaccess/src/test/java/org/apache/felix/das/ActivatorTest.java b/deviceaccess/src/test/java/org/apache/felix/das/ActivatorTest.java
new file mode 100644
index 0000000..bb1656f
--- /dev/null
+++ b/deviceaccess/src/test/java/org/apache/felix/das/ActivatorTest.java
@@ -0,0 +1,86 @@
+/*
+ * 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.das;
+
+import org.apache.felix.dependencymanager.DependencyManager;
+import org.apache.felix.dependencymanager.Service;
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.Mock;
+import org.mockito.Mockito;
+import org.mockito.MockitoAnnotations;
+import org.osgi.framework.BundleContext;
+
+
+
+/**
+ * 
+ * Tests the Activator.
+ * 
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public class ActivatorTest {
+
+	
+	
+	private Activator m_activator;
+	
+	@Mock
+	private BundleContext m_context;
+	
+	@Mock
+	private DependencyManager m_manager;
+	
+	@Before
+	public void before() {
+
+		MockitoAnnotations.initMocks(this);
+		m_activator = new Activator();
+		
+	}
+
+	
+	
+	
+	@Test
+	public void VerifyActivatorInit() throws Exception {
+		
+		m_activator.init(m_context, m_manager);
+		
+		Mockito.verify(m_manager).add(Mockito.isA(Service.class));
+		
+	}
+	
+	/**
+	 * Verify we do not actively perform any actions during the destroy.
+	 * 
+	 * @throws Exception
+	 */
+	@Test
+	public void VerifyActivatorDestroy() throws Exception {
+		
+		m_activator.destroy(m_context, m_manager);
+		
+		Mockito.verifyZeroInteractions(m_context);
+		Mockito.verifyZeroInteractions(m_manager);
+		
+	}
+	
+	
+}
diff --git a/deviceaccess/src/test/java/org/apache/felix/das/DeviceManagerTest.java b/deviceaccess/src/test/java/org/apache/felix/das/DeviceManagerTest.java
new file mode 100644
index 0000000..de384b2
--- /dev/null
+++ b/deviceaccess/src/test/java/org/apache/felix/das/DeviceManagerTest.java
@@ -0,0 +1,914 @@
+/*
+ * 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.das;
+
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.Dictionary;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Properties;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
+import junit.framework.Assert;
+
+import org.apache.felix.das.util.DriverLoader;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Ignore;
+import org.junit.Test;
+import org.mockito.Mock;
+import org.mockito.Mockito;
+import org.mockito.MockitoAnnotations;
+import org.mockito.invocation.InvocationOnMock;
+import org.mockito.stubbing.Answer;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.BundleException;
+import org.osgi.framework.ServiceReference;
+import org.osgi.service.device.Constants;
+import org.osgi.service.device.Device;
+import org.osgi.service.device.Driver;
+import org.osgi.service.device.DriverLocator;
+import org.osgi.service.device.DriverSelector;
+import org.osgi.service.device.Match;
+import org.osgi.service.log.LogService;
+
+
+
+/**
+ * Test the actual implementation.
+ * 
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public class DeviceManagerTest
+{
+
+	@Mock
+    private DeviceManager m_manager;
+
+	@Mock Bundle m_systemBundle;
+	
+	@Mock
+	private LogService m_log;
+	
+	
+	private BundleContext m_context;
+	
+	
+    private OSGiMock m_osgi;
+
+
+    @Before
+    public void setUp() throws Exception
+    {
+        m_osgi = new OSGiMock();
+
+    	MockitoAnnotations.initMocks(this);
+    	
+    	m_context = m_osgi.getBundleContext();
+    	
+        m_manager = new DeviceManager( m_context );
+        
+        Utils.invoke( m_manager, "init" );
+        
+        Utils.inject( m_manager, LogService.class, m_log );
+        
+        Mockito.when( m_context.getBundle( 0 ) ).thenReturn( m_systemBundle );
+
+        final CountDownLatch latch = new CountDownLatch( 1 );
+
+        Answer<Integer> answer = new Answer<Integer>()
+        {
+        	public Integer answer(InvocationOnMock invocation) throws Throwable
+            {
+                latch.countDown();
+                return Bundle.ACTIVE;
+            }
+        };
+
+        Mockito.when( m_systemBundle.getState() ).thenAnswer( answer );
+
+        Utils.invoke( m_manager, "start" );
+        latch.await( 5, TimeUnit.SECONDS );
+
+        Mockito.when( m_context.installBundle(Mockito.isA( String.class ), ( InputStream ) Mockito.isNull() ) )
+        	.thenThrow(new NullPointerException( "inputstream is null exception" ) );
+    }
+
+
+    @After
+    public void tearDown() throws Exception
+    {
+        Utils.invoke( m_manager, "stop" );
+        Utils.invoke( m_manager, "destroy" );
+    }
+
+    private Driver tstCreateDriver( String driverId, int match ) throws Exception
+    {
+        Properties p = new Properties();
+        p.put( Constants.DRIVER_ID, driverId );
+        p.put( "match", Integer.toString( match ) );
+
+        return tstCreateDriver( p );
+    }
+
+
+    private Driver tstCreateDriver( Properties p ) throws Exception
+    {
+        Driver driver = Mockito.mock( Driver.class );
+        
+        ServiceReference ref = m_osgi.registerService( 
+        		new String[]{ Driver.class.getName() }, driver, p );
+
+        MatchAnswer answer = new MatchAnswer( ref );
+
+        Mockito.when( driver.match( Mockito.isA( ServiceReference.class ) ) )
+        	.thenAnswer( answer );
+        
+        Bundle bundle = m_osgi.getBundle( ref );
+        Mockito.when( bundle.getLocation() )
+        	.thenReturn( 
+            DriverLoader.DRIVER_LOCATION_PREFIX  + p.getProperty( Constants.DRIVER_ID ));
+        
+        return driver;
+    }
+
+
+    private Device tstCreateDevice( String[] cat )
+    {
+        return tstCreateDevice( cat, true );
+    }
+
+
+    private Device tstCreateDevice( String[] cat, boolean isDevice )
+    {
+        Properties p = new Properties();
+        p.put( Constants.DEVICE_CATEGORY, cat );
+        if ( isDevice )
+        {
+            return ( Device ) tstCreateService( p, Device.class );
+        }
+        return tstCreateService( p, Object.class );
+    }
+
+
+    @SuppressWarnings("unchecked")
+    private <T> T tstCreateService( Properties p, Class<?> iface )
+    {
+        T svc = ( T ) Mockito.mock( iface, iface.getSimpleName() );
+        
+        m_osgi.registerService( new String[]
+            { iface.getName() }, svc, p );
+        return svc;
+    }
+
+
+    /**
+     * 
+     * prepared all expected behavior for the installation of a dynamic driver
+     * bundle based on an acquired InputStream.
+     * 
+     * 
+     * @param driverId
+     * @param match
+     * @param in
+     * @return
+     * @throws BundleException
+     * @throws Exception
+     */
+    private Driver tstExpectInstallDriverBundle( String driverId, int match, InputStream in ) throws BundleException,
+        Exception
+    {
+
+        Bundle bundle = Mockito.mock( Bundle.class, "driverBundle" );
+        Mockito.when( m_context.installBundle( 
+        		Mockito.eq( "_DD_" + driverId ), Mockito.eq( in ) ) )
+        		.thenReturn( bundle );
+
+        final Driver driver = tstCreateDriver( driverId, match );
+        final ServiceReference driverRef = m_osgi.getReference( driver );
+
+        Answer<Object> answer = new Answer<Object>()
+        {
+        	
+        	public Object answer(InvocationOnMock invocation) throws Throwable
+            {
+                m_manager.driverAdded( driverRef, driver );
+                return null;
+            }
+        };
+
+        //bundle start leads to the addition of the driver to
+        //the device manager.
+        Mockito.doAnswer(answer).when(bundle).start();
+
+        Mockito.when( bundle.getRegisteredServices() )
+        	.thenReturn( new ServiceReference[]{ driverRef } );
+
+        return driver;
+    }
+
+
+    /**
+     * returns a CountDownLatch.
+     * This countdown latch will count down as soon as <code>Device.noDriverFound()</code>
+     * has been called.
+     * @param device the Device
+     * @return the countdown latch
+     */
+    private CountDownLatch tstExpectNoDriverFound( Device device )
+    {
+        final CountDownLatch latch = new CountDownLatch( 1 );
+
+        //countdown when noDriverFound is called
+        Answer<Object> answer = new Answer<Object>()
+        {
+        	public Object answer(InvocationOnMock invocation) throws Throwable
+            {
+                latch.countDown();
+                return null;
+            }
+        };
+
+        Mockito.doAnswer( answer ).when(device).noDriverFound();
+
+        return latch;
+
+    }
+
+
+    private CountDownLatch tstExpectAttach( Driver driver, Object device ) throws Exception
+    {
+        final CountDownLatch latch = new CountDownLatch( 1 );
+        Answer<String> answer = new Answer<String>()
+        {
+            public String answer(InvocationOnMock invocation) throws Throwable
+            {
+                latch.countDown();
+                return null;
+            }
+        };
+
+        //successful attach
+        Mockito.when( driver.attach( m_osgi.getReference( device ) ) )
+        	.thenAnswer( answer );
+
+        return latch;
+    }
+
+    private CountDownLatch tstExpectUnloadDriverBundle( Driver driver ) throws BundleException {
+    	
+    	
+    	final CountDownLatch latch = new CountDownLatch( 1 );
+        Answer<String> answer = new Answer<String>()
+        {
+            public String answer(InvocationOnMock invocation) throws Throwable
+            {
+                latch.countDown();
+                return null;
+            }
+        };
+
+        Bundle bundle = m_osgi.getBundle( m_osgi.getReference( driver ) );
+        
+        Mockito.doAnswer(answer).when( bundle ).uninstall();
+
+        return latch;    	
+    }
+
+    /**
+     * This method generates behavior on the provided DriverLocator.
+     * 
+     * The given driver Ids and their matches are expected as drivers found
+     * by this driver locator.
+     * Also, if a driver is found, we can also expect that loadDriver is called;
+     * resulting in an InputStream. That particular input stream should, when installed
+     * using a bundle context, lead to the registration of a driver with 
+     * the correcsponding driver id.
+     *  
+     * @param locator
+     * @param driverIds
+     * @param matches
+     * @return
+     * @throws Exception
+     */
+    private Map<String, Driver> tstExpectDriverLocatorFor( final DriverLocator locator, final String[] driverIds,
+        int[] matches ) throws Exception
+    {
+
+        Mockito.when( locator.findDrivers( Mockito.isA( Dictionary.class ) ) )
+        	.thenReturn( driverIds );
+
+        Map<String, Driver> drivers = new HashMap<String, Driver>();
+
+        final Map<String, InputStream> streams = new HashMap<String, InputStream>();
+
+        for ( String driverId : driverIds )
+        {
+            InputStream in = Mockito.mock(InputStream.class, "[InputStream for: " + driverId + "]");
+            streams.put( driverId, in );
+        }
+
+        Answer<InputStream> answer = new Answer<InputStream>()
+        {
+        	public InputStream answer(InvocationOnMock invocation) throws Throwable
+            {
+                final String id = invocation.getArguments()[0].toString();
+
+                for ( String driverId : driverIds )
+                {
+                    if ( id.equals( driverId ) )
+                    {
+                        return streams.get( id );
+                    }
+                }
+                throw new IOException( "no such driverId defined in this locator: " + locator );
+            }
+        };
+
+        
+        Mockito.when( locator.loadDriver( Mockito.isA( String.class ) ) )
+        	.thenAnswer( answer );
+
+        int i = 0;
+        for ( String driverId : driverIds )
+        {
+            Driver driver = tstExpectInstallDriverBundle( driverId, matches[i], streams.get( driverId ) );
+            drivers.put( driverId, driver );
+            i++;
+        }
+
+        return drivers;
+
+    }
+
+
+    /**
+     * does not really test anything special, but ensures that the internal
+     * structure is able to parse the addition
+     */
+    @Test
+    public void LocatorAdded()
+    {
+
+        DriverLocator locator = Mockito.mock( DriverLocator.class );
+        m_manager.locatorAdded( locator );
+
+    }
+
+
+    /**
+     * does not really test anything special, but ensures that the internal
+     * structure is able to parse the addition/ removal
+     */
+    @Test
+    public void LocatorRemoved()
+    {
+
+        DriverLocator locator = Mockito.mock( DriverLocator.class );
+
+        m_manager.locatorAdded( locator );
+        m_manager.locatorRemoved( locator );
+
+    }
+
+
+    /**
+     * does not really test anything special, but ensures that the internal
+     * structure is able to parse the addition
+     * @throws Exception 
+     */
+    @Test
+    public void DriverAdded() throws Exception
+    {
+
+        Driver driver = tstCreateDriver( "org.apache.felix.driver-1.0", 1 );
+
+        m_manager.driverAdded( m_osgi.getReference( driver ), driver );
+
+    }
+
+
+    /**
+     * does not really test anything special, but ensures that the internal
+     * structure is able to parse the addition/ removal
+     * @throws Exception 
+     */
+    @Test
+    public void DriverRemoved() throws Exception
+    {
+
+        Driver driver = tstCreateDriver( "org.apache.felix.driver-1.0", 1 );
+
+        ServiceReference ref = m_osgi.getReference( driver );
+
+        m_manager.driverAdded( ref, driver );
+        m_manager.driverRemoved( ref );
+    }
+
+    /**
+     * does not really test anything special, but ensures that the internal
+     * structure is able to parse the addition/ removal
+     * @throws Exception 
+     */
+    @Test
+    public void DeviceRemoved() throws Exception
+    {
+
+    	Properties p = new Properties();
+    	p.put(Constants.DEVICE_CATEGORY, new String[]{"dummy"});
+    	
+        ServiceReference ref = OSGiMock.createReference(p);
+        
+        Object device = new Object();
+
+        m_manager.deviceAdded( ref, device );
+        m_manager.deviceRemoved( ref );
+    }
+
+    /**
+     * does not really test anything special, but ensures that the internal
+     * structure is able to parse the addition/ removal
+     * @throws Exception 
+     */
+    @Test
+    public void DeviceModified() throws Exception
+    {
+
+    	Properties p = new Properties();
+    	p.put(Constants.DEVICE_CATEGORY, new String[]{"dummy"});
+    	
+        ServiceReference ref = OSGiMock.createReference(p);
+        Object device = new Object();
+        
+        m_manager.deviceAdded( ref, new Object() );
+        m_manager.deviceModified(ref, device);
+    }
+    //intended flow, various configurations
+    /**
+     * 	We add a device, but there are no driver locators, so
+     *  the noDriverFound method must be called
+     * @throws InterruptedException 
+     */
+    @Test
+    public void DeviceAddedNoDriverLocator() throws InterruptedException
+    {
+
+        //create a mocked device
+        Device device = tstCreateDevice( new String[]
+            { "org.apache.felix" } );
+
+        CountDownLatch latch = tstExpectNoDriverFound( device );
+
+        m_manager.deviceAdded( m_osgi.getReference( device ), device );
+
+        if ( !latch.await( 5, TimeUnit.SECONDS ) )
+        {
+            Assert.fail( "expected call noDriverFound" );
+        }
+        
+    }
+
+
+    /**
+     * 	We add a device, but there are no driver locators, however, there is a driver
+     *  that matches. Thus an attach must follow.
+     * @throws Exception 
+     */
+    @Test
+    public void DeviceAddedNoDriverLocatorSuccessfulAttach() throws Exception
+    {
+
+        Device device = tstCreateDevice( new String[] { "org.apache.felix" } );
+        Driver driver = tstCreateDriver( "org.apache.felix.driver-1.0", 1 );
+
+        CountDownLatch attachLatch = tstExpectAttach( driver, device );
+
+        m_manager.driverAdded( m_osgi.getReference( driver ), driver );
+        m_manager.deviceAdded( m_osgi.getReference( device ), device );
+
+        if ( !attachLatch.await( 5, TimeUnit.SECONDS ) )
+        {
+            Assert.fail( "expected attach" );
+        }
+
+    }
+
+
+    /**
+     * 	We add a device, but there are no driver locators, however, there is a driver
+     *  but it sadly doesn't match. Thus a <code>noDriverFound()</code> is called.
+     *  
+     * @throws Exception 
+     */
+    @Test
+    public void DeviceAddedNoDriverLocatorAttachFails() throws Exception
+    {
+
+        Device device = tstCreateDevice( new String[] { "org.apache.felix" } );
+        Driver driver = tstCreateDriver( "org.apache.felix.driver-1.0", Device.MATCH_NONE );
+
+        CountDownLatch attachLatch = tstExpectNoDriverFound( device );
+
+        m_manager.driverAdded( m_osgi.getReference( driver ), driver );
+        m_manager.deviceAdded( m_osgi.getReference( device ), device );
+
+        if ( !attachLatch.await( 5, TimeUnit.SECONDS ) )
+        {
+            Assert.fail( "expected attach" );
+        }
+
+    }
+
+
+    /**
+     * We add a device while there's one driverlocator that will successfully
+     * locate and load two driver bundles. We expect a <code>Driver.attach()</code> for 
+     * the best matching driver. There's already a driver loaded that should not match.
+     * 
+     * @throws Exception
+     */
+    @Test
+    public void DeviceAddedWithADriverLocator() throws Exception
+    {
+
+        final String driverId1 = "org.apache.felix.driver-1.0";
+        final String driverId2 = "org.apache.felix.driver-1.1";
+        final String notMatchingButLoadedDriverId = "dotorg.apache.felix.driver-1.0";
+
+
+        DriverLocator locator = Mockito.mock( DriverLocator.class );
+
+        Map<String, Driver> drivers = tstExpectDriverLocatorFor( locator, 
+    		new String[] { driverId1, driverId2 }, 
+    		new int[] { 30, 3 } );
+
+        Driver noMatcher = tstCreateDriver( notMatchingButLoadedDriverId, 100 );
+
+        Device device = tstCreateDevice( new String[]{ "org.apache.felix" } );
+
+        final CountDownLatch attachLatch = tstExpectAttach( drivers.get( driverId1 ), device );
+
+        final CountDownLatch unloadDriverLatch = tstExpectUnloadDriverBundle( drivers.get ( driverId2 ) );
+
+        m_manager.locatorAdded( locator );
+
+        m_manager.driverAdded( m_osgi.getReference( noMatcher ), noMatcher );
+
+        m_manager.deviceAdded( m_osgi.getReference( device ), device );
+
+        if ( !attachLatch.await( 5, TimeUnit.SECONDS ) )
+        {
+            Assert.fail( "expected an attach" );
+        }
+        
+        //since driver1 is attached, we expect an uninstall()
+        //of all other (dynamically loaded) driver bundles
+        if ( !unloadDriverLatch.await( 5, TimeUnit.SECONDS ) )
+        {
+            Assert.fail( "expected an unload" );
+        }
+
+    }
+
+    @Test
+    public void DeviceAddedWithADriverLocatorUnloadFails() throws Exception
+    {
+
+        final String driverId1 = "org.apache.felix.driver-1.0";
+        final String driverId2 = "org.apache.felix.driver-1.1";
+        final String notMatchingButLoadedDriverId = "dotorg.apache.felix.driver-1.0";
+
+
+        DriverLocator locator = Mockito.mock( DriverLocator.class );
+
+        Map<String, Driver> drivers = tstExpectDriverLocatorFor( locator, 
+    		new String[] { driverId1, driverId2 }, 
+    		new int[] { 30, 3 } );
+
+        Driver noMatcher = tstCreateDriver( notMatchingButLoadedDriverId, 100 );
+
+        Device device = tstCreateDevice( new String[]{ "org.apache.felix" } );
+
+        final CountDownLatch attachLatch = tstExpectAttach( drivers.get( driverId1 ), device );
+
+        final CountDownLatch unloadDriverLatch = new CountDownLatch( 1 );
+        
+        ServiceReference driver2Ref = m_osgi.getReference( drivers.get( driverId2 ) );
+        Bundle driver2Bundle = m_osgi.getBundle( driver2Ref );
+        
+        Answer<Object> answer = new Answer<Object>() {
+        	
+        	public Object answer(InvocationOnMock invocation) throws Throwable {
+        		try {
+        			throw new BundleException("test driverBundle uninstall failed");
+        		}
+        		finally {
+        			unloadDriverLatch.countDown();
+        		}
+        	}
+        };
+        
+        Mockito.doAnswer(answer).when(driver2Bundle).uninstall();
+        
+        m_manager.locatorAdded( locator );
+
+        m_manager.driverAdded( m_osgi.getReference( noMatcher ), noMatcher );
+
+        m_manager.deviceAdded( m_osgi.getReference( device ), device );
+
+        if ( !attachLatch.await( 5, TimeUnit.SECONDS ) )
+        {
+            Assert.fail( "expected an attach" );
+        }
+        
+        if ( !unloadDriverLatch.await( 5, TimeUnit.SECONDS ) )
+        {
+            Assert.fail( "expected an unload" );
+        }
+
+        
+        //since driver1 is attached, we expect an uninstall()
+        //of all other (dynamically loaded) driver bundles
+        //Driver driver = drivers.get( driverId2 );
+        //tstVerifyBundleUninstall( driver );
+    }
+
+    /**
+     * Two drivers equally match the device. There is a driver selector
+     * that comes to the rescue that selects driver2. 
+     * 
+     * @throws Exception
+     */
+    @Test
+    public void EqualMatchWithDriverSelector() throws Exception
+    {
+
+        final String driverId1 = "org.apache.felix.driver-1.0";
+        final String driverId2 = "org.apache.felix.driver-1.1";
+
+        DriverLocator locator = Mockito.mock( DriverLocator.class );
+
+        Map<String, Driver> drivers = tstExpectDriverLocatorFor( locator, 
+    		new String[] { driverId1, driverId2 }, 
+    		new int[] { 20, 20 } );
+
+        Device device = tstCreateDevice( new String[]{ "org.apache.felix" } );
+
+        DriverSelector selector = Mockito.mock( DriverSelector.class );
+
+        SelectorMatcher matcher = new SelectorMatcher( driverId2 );
+
+        Mockito.when( selector.select( 
+        		Mockito.eq( m_osgi.getReference( device ) ), 
+        		Mockito.isA(Match[].class) ) ).thenAnswer( matcher );
+
+        final CountDownLatch attachLatch = tstExpectAttach( drivers.get( driverId2 ), device );
+
+
+        Utils.inject( m_manager, DriverSelector.class, selector );
+
+        m_manager.locatorAdded( locator );
+
+        m_manager.deviceAdded( m_osgi.getReference( device ), device );
+
+        if ( !attachLatch.await( 5, TimeUnit.SECONDS ) )
+        {
+            Assert.fail( "expected an attach" );
+        }
+        
+        
+        //driver2 is attached, so driver1 bundle should uninstall.
+        //Driver driver = drivers.get( driverId1 );
+        //tstVerifyBundleUninstall( driver );
+
+    }
+
+
+    //exceptional flow
+    @Test
+    public void DriverLocator_findDriverFails() throws Exception
+    {
+
+        final CountDownLatch latch = new CountDownLatch( 1 );
+
+        Answer<String[]> answer = new Answer<String[]>()
+        {
+
+            public String[] answer(InvocationOnMock invocation) throws Throwable
+            {
+                latch.countDown();
+                throw new RuntimeException( "test exception" );
+            }
+        };
+
+        DriverLocator locator = Mockito.mock( DriverLocator.class, "locator" );
+        Mockito.when( locator.findDrivers( Mockito.isA( Dictionary.class ) ) )
+        	.thenAnswer( answer );
+
+        Device device = tstCreateDevice( new String[]
+            { "org.apache.felix" } );
+
+        final CountDownLatch latch2 = new CountDownLatch( 1 );
+
+        Answer<Object> answer2 = new Answer<Object>()
+        {
+            public Object answer(InvocationOnMock invocation) throws Throwable
+            {
+                latch2.countDown();
+                return null;
+            }
+        };
+        
+        Mockito.doAnswer(answer2).when(device).noDriverFound();
+        
+
+        m_manager.locatorAdded( locator );
+        m_manager.deviceAdded( m_osgi.getReference( device ), device );
+
+        if ( !latch.await( 5, TimeUnit.SECONDS ) )
+        {
+            Assert.fail( "expected a call to DriverLocator.findDrivers" );
+        }
+
+        if ( !latch2.await( 5, TimeUnit.SECONDS ) )
+        {
+            Assert.fail( "expected a call to Driver.noDriverFound" );
+        }
+
+    }
+
+
+    /**
+     * This test verified correct behavior when after a driver
+     * attach led to a referral, this referral leads to an exception.
+     * 
+     * 
+     * @throws Exception
+     */
+    @Ignore
+    public void DriverReferral_ReferralFails() throws Exception
+    {
+
+        final String referredDriver = "org.apache.felix.driver-2.0";
+
+        String[] driverIds = new String[]
+            { "org.apache.felix.driver-1.0", "org.apache.felix.driver-1.1" };
+        
+        int[] driverMatches = new int[]{ 1, Device.MATCH_NONE };
+        
+        DriverLocator locator = Mockito.mock( DriverLocator.class, "locator for v1.x" );
+        Map<String, Driver> drivers = tstExpectDriverLocatorFor( locator, driverIds, driverMatches );
+
+        
+        DriverLocator locatorv2 = Mockito.mock( DriverLocator.class, "locator for v2.x (fails always)" );
+        Mockito.when( locatorv2.findDrivers( Mockito.isA( Dictionary.class ) ) )
+        	.thenReturn( null );
+
+        Mockito.when( locatorv2.loadDriver( Mockito.startsWith( "org.apache.felix.driver-1" ) ) )
+    		.thenReturn( null );
+        
+        InputStream referredInputStream = Mockito.mock(InputStream.class);
+        Mockito.when( locatorv2.loadDriver( referredDriver ) ).thenReturn( referredInputStream );
+
+
+        //this is what initial driver referral eventually leads
+        //to: the loading of a driver bundle
+        //we fake it, so that it fails
+        Mockito.when( m_context.installBundle( 
+        		Mockito.anyString(), 
+        		Mockito.isA( InputStream.class ) ) )
+        	.thenThrow(new BundleException( "test exception" ) );
+
+        Driver matched = drivers.get( "org.apache.felix.driver-1.0" );
+
+        final CountDownLatch latch = new CountDownLatch( 1 );
+
+        Answer<String> driver10_attach = new Answer<String>()
+        {
+            public String answer(InvocationOnMock invocation) throws Throwable
+            {
+            	System.out.println("driver10_attach()");
+                latch.countDown();
+                return referredDriver;
+            }
+        };
+
+        Device device = tstCreateDevice( new String[]{ "org.apache.felix" } );
+        
+
+        Mockito.when( matched.match( m_osgi.getReference( device ) ) ).thenReturn( 10 );
+
+        Mockito.when( matched.attach( Mockito.isA( ServiceReference.class ) ) )
+        	.thenAnswer( driver10_attach );
+
+//        for ( String driverId : driverIds )
+//        {
+//            Driver driver = drivers.get( driverId );
+//            tstExpectBundleUninstall( driver );
+//        }
+
+
+        //the actual test
+        
+        m_manager.locatorAdded( locator );
+        m_manager.locatorAdded( locatorv2 );
+        
+        //depman induced callback
+        m_manager.deviceAdded( m_osgi.getReference( device ), device );
+
+        
+        if ( !latch.await( 5, TimeUnit.SECONDS ) )
+        {
+            Assert.fail( "expected an attach to: " + driverIds[0] );
+        }
+
+        
+        Mockito.verify(device).noDriverFound();
+    }
+
+    /**
+     * @author dennisg
+     *
+     */
+    private class MatchAnswer implements Answer<Integer>
+    {
+
+        private final ServiceReference m_driverRef;
+
+        public MatchAnswer( ServiceReference driverRef )
+        {
+            m_driverRef = driverRef;
+        }
+
+
+        public Integer answer(InvocationOnMock invocation) throws Throwable
+        {
+            ServiceReference deviceRef = ( ServiceReference ) invocation.getArguments()[0];
+            String[] categories = String[].class.cast( deviceRef.getProperty( Constants.DEVICE_CATEGORY ) );
+            String driverId = String.class.cast( m_driverRef.getProperty( Constants.DRIVER_ID ) );
+
+            for ( String string : categories )
+            {
+                if ( driverId.startsWith( string ) )
+                {
+                    Object match = m_driverRef.getProperty( "match" );
+                    return Integer.valueOf( match.toString() );
+                }
+            }
+            return Device.MATCH_NONE;
+        }
+
+    }
+        
+
+    private class SelectorMatcher implements Answer<Integer>
+    {
+
+        private String m_driverId;
+
+
+        public SelectorMatcher( String driverId )
+        {
+            m_driverId = driverId;
+        }
+
+		public Integer answer(InvocationOnMock invocation) throws Throwable
+        {
+            int i = 0;
+            Match[] matches = (Match[])invocation.getArguments()[1];
+            
+            for ( Match match : matches )
+            {
+                if ( match.getDriver().getProperty( Constants.DRIVER_ID ).equals( m_driverId ) )
+                {
+                    return i;
+                }
+                i++;
+            }
+            return DriverSelector.SELECT_NONE;
+        }
+
+
+    }
+    
+}
diff --git a/deviceaccess/src/test/java/org/apache/felix/das/DriverAttributesTest.java b/deviceaccess/src/test/java/org/apache/felix/das/DriverAttributesTest.java
new file mode 100644
index 0000000..56cef12
--- /dev/null
+++ b/deviceaccess/src/test/java/org/apache/felix/das/DriverAttributesTest.java
@@ -0,0 +1,174 @@
+/*
+ * 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.das;
+
+
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.Mock;
+import org.mockito.Mockito;
+import org.mockito.MockitoAnnotations;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.Constants;
+import org.osgi.framework.ServiceReference;
+import org.osgi.service.device.Device;
+import org.osgi.service.device.Driver;
+
+/**
+ * 
+ * Some simple tests for the DriverAttributes class.
+ * 
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ *
+ */
+public class DriverAttributesTest {
+
+	
+	private DriverAttributes m_attributes;
+	
+	@Mock
+	private ServiceReference m_ref;
+	
+	@Mock
+	private Driver m_driver;
+	
+	@Mock
+	private Bundle m_bundle;
+	
+	@Before
+	public void setUp() throws Exception {
+		
+		MockitoAnnotations.initMocks(this);
+		
+		Mockito.when(m_ref.getBundle()).thenReturn(m_bundle);
+		
+		Mockito.when(m_bundle.getLocation()).thenReturn("_DD_test-driverbundle");
+		
+		m_attributes = new DriverAttributes(m_ref, m_driver);
+	}
+
+
+	@Test
+	public void VerifyDriverReferenceReturned() throws Exception {
+		
+		Assert.assertEquals(m_ref, m_attributes.getReference());
+	}
+
+	@Test
+	public void VerifyDriverInUseByDevice() throws Exception {
+		
+		ServiceReference ref = Mockito.mock(ServiceReference.class);
+		
+		Mockito.when(ref.getProperty(Constants.OBJECTCLASS))
+			.thenReturn(new String[]{Object.class.getName()});
+		
+		Mockito.when(ref.getProperty(
+			org.osgi.service.device.Constants.DEVICE_CATEGORY))
+			.thenReturn(new String[]{"dummy"});
+		
+		Mockito.when(m_bundle.getServicesInUse()).thenReturn(new ServiceReference[]{ref});
+		
+		m_attributes.tryUninstall();
+		
+		Mockito.verify(m_bundle).getLocation();
+		Mockito.verify(m_bundle).getServicesInUse();
+		Mockito.verifyNoMoreInteractions(m_bundle);
+
+	}
+	
+	@Test
+	public void VerifyDriverInUseByDeviceInstance() throws Exception {
+		
+		ServiceReference ref = Mockito.mock(ServiceReference.class);
+		
+		Mockito.when(ref.getProperty(Constants.OBJECTCLASS)).thenReturn(new String[]{Device.class.getName()});
+		Mockito.when(ref.getProperty(
+				org.osgi.service.device.Constants.DEVICE_CATEGORY))
+				.thenReturn(new String[]{"dummy"});
+		
+		Mockito.when(m_bundle.getServicesInUse()).thenReturn(new ServiceReference[]{ref});
+		
+		m_attributes.tryUninstall();
+		
+		Mockito.verify(m_bundle).getLocation();
+		Mockito.verify(m_bundle).getServicesInUse();
+		Mockito.verifyNoMoreInteractions(m_bundle);
+
+	}
+	
+	
+	@Test
+	public void VerifyDriverInUseByNoDevice() throws Exception {
+		
+		ServiceReference ref = Mockito.mock(ServiceReference.class);
+		
+		Mockito.when(ref.getProperty(Constants.OBJECTCLASS)).thenReturn(new String[]{Object.class.getName()});
+		Mockito.when(m_bundle.getServicesInUse()).thenReturn(new ServiceReference[]{ref});
+		
+		m_attributes.tryUninstall();
+		
+		Mockito.verify(m_bundle).getLocation();
+		Mockito.verify(m_bundle).getServicesInUse();
+		Mockito.verify(m_bundle).uninstall();
+
+	}
+	@Test
+	public void VerifyDriverNotInUseLeadsToUnInstall1() throws Exception {
+		
+		Mockito.when(m_bundle.getServicesInUse()).thenReturn(new ServiceReference[0]);
+		
+		m_attributes.tryUninstall();
+		
+		Mockito.verify(m_bundle).uninstall();
+		
+
+	}
+	
+	@Test
+	public void VerifyDriverNotInUseLeadsToUnInstall2() throws Exception {
+		
+		m_attributes.tryUninstall();
+		
+		Mockito.verify(m_bundle).uninstall();
+		
+	}	
+	
+	@Test
+	public void VerifyAttachCalledOnDriver() throws Exception {
+		
+		
+		ServiceReference ref = Mockito.mock(ServiceReference.class);
+		m_attributes.attach(ref);
+		
+		Mockito.verify(m_driver).attach(Mockito.eq(ref));
+		
+	}
+
+	@Test
+	public void VerifyMatchCalledOnDriver() throws Exception {
+		
+		
+		ServiceReference ref = Mockito.mock(ServiceReference.class);
+		m_attributes.match(ref);
+		
+		Mockito.verify(m_driver).match(Mockito.eq(ref));
+		
+	}
+}
diff --git a/deviceaccess/src/test/java/org/apache/felix/das/OSGiMock.java b/deviceaccess/src/test/java/org/apache/felix/das/OSGiMock.java
new file mode 100644
index 0000000..c0acf3e
--- /dev/null
+++ b/deviceaccess/src/test/java/org/apache/felix/das/OSGiMock.java
@@ -0,0 +1,166 @@
+/*
+ * 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.das;
+
+
+import java.util.Dictionary;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Properties;
+
+import org.mockito.Mock;
+import org.mockito.Mockito;
+import org.mockito.MockitoAnnotations;
+import org.mockito.invocation.InvocationOnMock;
+import org.mockito.stubbing.Answer;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.Constants;
+import org.osgi.framework.ServiceReference;
+
+
+/**
+ * 
+ * a very simple mock of an osgi framework. enables the registration of services.
+ * automatically generates mocked service references for them.
+ *
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public class OSGiMock
+{
+
+	@Mock
+    private BundleContext m_context;
+
+    private Map<Object, ServiceReference> m_references;
+
+    private Map<ServiceReference, Bundle> m_bundles;
+
+    private int m_serviceIndex = 1;
+
+    public OSGiMock()
+    {
+    	MockitoAnnotations.initMocks(this);
+        m_references = new HashMap<Object, ServiceReference>();
+        m_bundles = new HashMap<ServiceReference, Bundle>();
+    }
+
+    public static ServiceReference createReference(final Properties p) 
+    {
+        ServiceReference ref = Mockito.mock( ServiceReference.class );
+
+        Mockito.when(ref.getProperty(Mockito.anyString())).thenAnswer(new Answer<Object>() {
+        	public Object answer(InvocationOnMock invocation) throws Throwable {
+        		return p.get(invocation.getArguments()[0].toString());
+        	}
+        });
+        
+        Mockito.when(ref.getPropertyKeys()).thenAnswer(new Answer<Object>() {
+        	public Object answer(InvocationOnMock invocation) throws Throwable {
+        		return p.keySet().toArray(new String[0]);
+        	}
+        });
+        
+        
+        return ref;
+    }
+
+
+
+    public BundleContext getBundleContext()
+    {
+        return m_context;
+    }
+
+
+    @SuppressWarnings("all")
+    public ServiceReference registerService( String[] ifaces, Object impl, Properties props )
+    {
+
+        ServiceReference ref = createReference( ifaces, props );
+
+        Mockito.when( m_context.registerService( ifaces, impl, props ) )
+        	.thenReturn( null );
+
+        Mockito.when( m_context.getService( ref ) ).thenReturn( impl );
+
+        m_references.put( impl, ref );
+
+        return ref;
+    }
+
+
+    public ServiceReference getReference( Object service )
+    {
+        return m_references.get( service );
+    }
+
+
+    public Bundle getBundle( ServiceReference ref )
+    {
+        return m_bundles.get( ref );
+    }
+
+
+
+    @SuppressWarnings("all")
+    public ServiceReference createReference( String[] ifaces, Properties props )
+    {
+
+        final ServiceReference ref = Mockito.mock( ServiceReference.class );
+
+        RefPropAnswer answer = new RefPropAnswer( props, ifaces );
+
+        Mockito.when( ref.getProperty( Mockito.anyString() ) )
+        	.thenAnswer( answer );
+
+        Mockito.when( ref.getPropertyKeys() )
+        	.thenReturn( props.keySet().toArray( new String[0] ) );
+
+        Bundle bundle = Mockito.mock( Bundle.class );
+        
+        Mockito.when( ref.getBundle() ).thenReturn( bundle );
+
+        m_bundles.put( ref, bundle );
+
+        return ref;
+    }
+
+    @SuppressWarnings({ "unchecked" })
+    private class RefPropAnswer implements Answer<Object>
+    {
+        private final Dictionary m_p;
+
+
+        public RefPropAnswer( Dictionary p, String[] iface )
+        {
+            m_p = p;
+            m_p.put( Constants.OBJECTCLASS, iface );
+            m_p.put( Constants.SERVICE_ID, m_serviceIndex++ );
+        }
+
+
+        public Object answer(InvocationOnMock invocation) throws Throwable
+        {
+        	String key = (String)invocation.getArguments()[0];
+            return m_p.get( key );
+        }
+
+    }
+}
diff --git a/deviceaccess/src/test/java/org/apache/felix/das/Utils.java b/deviceaccess/src/test/java/org/apache/felix/das/Utils.java
new file mode 100644
index 0000000..9398e13
--- /dev/null
+++ b/deviceaccess/src/test/java/org/apache/felix/das/Utils.java
@@ -0,0 +1,78 @@
+/*
+ * 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.das;
+
+
+import java.lang.reflect.Field;
+import java.lang.reflect.Method;
+
+
+/**
+ * Utility class for injecting objects and invoking
+ * methods that are normally invoked by the dependency manager.
+ * 
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ *
+ */
+public class Utils
+{
+
+    public static void invoke( Object target, String method )
+    {
+        try
+        {
+            Method m = target.getClass().getDeclaredMethod( method, new Class[0] );
+            m.setAccessible( true );
+            m.invoke( target, new Object[0] );
+        }
+        catch ( Exception e )
+        {
+            e.printStackTrace();
+            junit.framework.Assert.fail( e.getMessage() );
+        }
+    }
+
+
+    public static void inject( Object target, Class<?> clazz, Object injectable )
+    {
+
+        Field[] fields = target.getClass().getDeclaredFields();
+
+        for ( Field field : fields )
+        {
+            if ( clazz == field.getType() )
+            {
+                field.setAccessible( true );
+                try
+                {
+                    field.set( target, injectable );
+                }
+                catch ( IllegalArgumentException e )
+                {
+                    e.printStackTrace();
+                }
+                catch ( IllegalAccessException e )
+                {
+                    e.printStackTrace();
+                }
+            }
+        }
+    }
+
+}
diff --git a/deviceaccess/src/test/java/org/apache/felix/das/util/DriverAnalyzerTest.java b/deviceaccess/src/test/java/org/apache/felix/das/util/DriverAnalyzerTest.java
new file mode 100644
index 0000000..e44c37e
--- /dev/null
+++ b/deviceaccess/src/test/java/org/apache/felix/das/util/DriverAnalyzerTest.java
@@ -0,0 +1,113 @@
+package org.apache.felix.das.util;
+
+
+import java.util.Properties;
+
+import org.apache.felix.das.OSGiMock;
+import org.apache.felix.das.Utils;
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.Mock;
+import org.mockito.Mockito;
+import org.mockito.MockitoAnnotations;
+import org.osgi.framework.ServiceReference;
+import org.osgi.service.device.Constants;
+import org.osgi.service.device.Driver;
+import org.osgi.service.log.LogService;
+
+
+/**
+ * 
+ * Tests the Driver Analyzer.
+ * 
+ * Nothing fancy is being tested, but if something is changed this
+ * validates that at least the most basic feedback can be expected. 
+ * 
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ *
+ */
+public class DriverAnalyzerTest {
+
+	
+	
+	private DriverAnalyzer m_analyzer;
+	
+	
+	@Mock
+	private LogService m_log;
+	
+	private OSGiMock m_osgi;
+	
+	@Before
+	public void setUp() throws Exception {
+		
+		MockitoAnnotations.initMocks(this);
+		
+		m_osgi = new OSGiMock();
+		m_analyzer = new DriverAnalyzer();
+
+		Utils.inject(m_analyzer, LogService.class, m_log);
+	}
+	
+	
+	@Test
+	public void VerifyCorrectDriverIsIgnored() {
+		
+		
+		Properties p = new Properties();
+		p.put(Constants.DRIVER_ID, "a-driver-id");
+		
+		
+		ServiceReference ref = m_osgi.createReference(new String[]{Driver.class.getName()}, p); 
+		
+		m_analyzer.driverAdded(ref);
+		
+		Mockito.verifyZeroInteractions(m_log);
+		
+	}
+
+	@Test
+	public void VerifyIncorrectDriverNoDriverId() {
+		
+		
+		Properties p = new Properties();
+		
+		ServiceReference ref = m_osgi.createReference(new String[]{Driver.class.getName()}, p); 
+		
+		m_analyzer.driverAdded(ref);
+		
+		Mockito.verify(m_log).log(Mockito.eq(LogService.LOG_ERROR), Mockito.anyString());
+		Mockito.verifyNoMoreInteractions(m_log);
+		
+	}
+	
+	@Test
+	public void VerifyIncorrectDriverInvalidDriverId() {
+		
+		Properties p = new Properties();
+		p.put(Constants.DRIVER_ID, new Object());
+
+		ServiceReference ref = m_osgi.createReference(new String[]{Driver.class.getName()}, p); 
+		
+		m_analyzer.driverAdded(ref);
+		
+		Mockito.verify(m_log).log(Mockito.eq(LogService.LOG_ERROR), Mockito.anyString());
+		Mockito.verifyNoMoreInteractions(m_log);
+		
+	}
+
+	@Test
+	public void VerifyIncorrectDriverEmptyDriverId() {
+		
+		Properties p = new Properties();
+		p.put(Constants.DRIVER_ID, "");
+
+		ServiceReference ref = m_osgi.createReference(new String[]{Driver.class.getName()}, p); 
+		
+		m_analyzer.driverAdded(ref);
+		
+		Mockito.verify(m_log).log(Mockito.eq(LogService.LOG_ERROR), Mockito.anyString());
+		Mockito.verifyNoMoreInteractions(m_log);
+		
+	}
+}
diff --git a/deviceaccess/src/test/java/org/apache/felix/das/util/DriverLoaderTest.java b/deviceaccess/src/test/java/org/apache/felix/das/util/DriverLoaderTest.java
new file mode 100644
index 0000000..789e2dd
--- /dev/null
+++ b/deviceaccess/src/test/java/org/apache/felix/das/util/DriverLoaderTest.java
@@ -0,0 +1,262 @@
+/*
+ * 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.das.util;
+
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Dictionary;
+import java.util.List;
+import java.util.Properties;
+
+import org.apache.felix.das.DeviceManager;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.Mock;
+import org.mockito.Mockito;
+import org.mockito.MockitoAnnotations;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.BundleException;
+import org.osgi.framework.ServiceReference;
+import org.osgi.service.device.Constants;
+import org.osgi.service.device.DriverLocator;
+
+
+/**
+ * The Device Manager delegates driver loading to the DriverLoader.
+ * This JUnit test tests the behavior of that DriverMatcher.
+ * 
+ * Tests all kinds of driver loading flows.
+ * all flows pertaining driver loading are grouped in the DriverLoader.
+ * 
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ *
+ */
+public class DriverLoaderTest
+{
+
+
+    private DriverLoader m_loader;
+
+    @Mock
+    private BundleContext m_context;
+
+    @Mock
+    private DeviceManager m_log;
+    
+    @Before
+    public void setUp() throws Exception
+    {
+    	
+    	MockitoAnnotations.initMocks(this);
+        m_loader = new DriverLoader( m_log, m_context );
+    }
+
+
+    private DriverLocator tstExpectDriverIdsFor(String[] ids) {
+    	
+        DriverLocator dl = Mockito.mock(DriverLocator.class );
+        Mockito.when( dl.findDrivers( Mockito.isA(Dictionary.class) ) ).thenReturn( ids );
+        return dl;
+    }
+    
+    /**
+     * test whether the driver loader can handle a situation where
+     * there are no DriverLocators.
+     * 
+     */
+    @Test
+    public void findDriversNoDriverLocators()
+    {
+
+        List<DriverLocator> locators = new ArrayList<DriverLocator>();
+
+
+        List<String> driverIds = m_loader.findDrivers( locators, new Properties() );
+        Assert.assertTrue( "should be an empty list", driverIds.size() == 0 );
+        
+    }
+
+
+    /**
+     * in this test there is a driver locator. the driver locator is instructed to 
+     * even return some driver ids.
+     * this test tests whether these driver ids are really returned. 
+     */
+    @Test
+    public void findDriversWithDriverLocator()
+    {
+
+        List<DriverLocator> locators = new ArrayList<DriverLocator>();
+
+        DriverLocator dl = tstExpectDriverIdsFor( 
+            new String[] { "org.apache.felix.driver-1.0", "org.apache.felix.driver-1.1" } );
+
+        locators.add( dl );
+
+        Properties dict = new Properties();
+        List<String> driverIds = m_loader.findDrivers( locators, dict );
+
+        Assert.assertEquals( "should not be an empty list", 2, driverIds.size());
+
+    }
+
+
+    /**
+     * in this test there are several driver locators, some of which return
+     * driver Ids, some don't. we expect an accurate number of driver ids being returned
+     * from the driverloader.
+     */
+    @Test
+    public void findDriversWithDriverLocators()
+    {
+
+        List<DriverLocator> locators = new ArrayList<DriverLocator>();
+
+        DriverLocator dl1 = tstExpectDriverIdsFor( 
+            new String[]{ "org.apache.felix.driver-1.0", "org.apache.felix.driver-1.1" } );
+        locators.add( dl1 );
+        
+        DriverLocator dl2 = tstExpectDriverIdsFor( 
+            new String[]{ "org.apache.felix.driver-1.2", "org.apache.felix.driver-1.3" } );
+        locators.add( dl2 );
+        
+        DriverLocator dl3 = tstExpectDriverIdsFor( null );
+        locators.add( dl3 );
+
+        
+        Properties dict = new Properties();
+        List<String> driverIds = m_loader.findDrivers( locators, dict );
+
+        Assert.assertEquals( "should not be an empty list", 4, driverIds.size() );
+
+    }
+
+
+    @Test
+    public void findDriversWithDriverLocatorFails()
+    {
+
+        Properties dict = new Properties();
+        List<DriverLocator> locators = new ArrayList<DriverLocator>();
+
+        DriverLocator dl = Mockito.mock( DriverLocator.class, "dl" );
+        locators.add( dl );
+
+        Mockito.when( dl.findDrivers( Mockito.eq( dict ) ) ).thenThrow( new RuntimeException( "test exception" ) );
+
+        List<String> driverIds = m_loader.findDrivers( locators, dict );
+
+        Assert.assertTrue( "should be an empty list", driverIds.size() == 0 );
+
+    }
+
+
+    @Test
+    public void loadDrivers() throws IOException, BundleException
+    {
+
+        List<DriverLocator> locators = new ArrayList<DriverLocator>();
+
+        DriverLocator dl = Mockito.mock( DriverLocator.class, "dl" );
+        locators.add( dl );
+
+        String[] driverIds = new String[]
+            { "org.apache.felix.driver-1.0", "org.apache.felix.driver-1.1", };
+
+        for ( String string : driverIds )
+        {
+            Mockito.when( dl.loadDriver( Mockito.eq( string ) ) ).thenReturn( null );
+            Bundle bundle = Mockito.mock( Bundle.class );
+            
+            Mockito.when( m_context.installBundle( "_DD_" + string, null ) ).thenReturn( bundle );
+            bundle.start();
+            
+            ServiceReference ref = Mockito.mock( ServiceReference.class );
+            Mockito.when( ref.getProperty( Constants.DRIVER_ID ) ).thenReturn( string );
+            Mockito.when( bundle.getRegisteredServices() ).thenReturn( new ServiceReference[]
+                { ref } );
+        }
+
+        List<ServiceReference> refs = m_loader.loadDrivers( locators, driverIds );
+
+        Assert.assertEquals( "", 2, refs.size() );
+        for ( ServiceReference serviceReference : refs )
+        {
+            String driverId = "" + serviceReference.getProperty( Constants.DRIVER_ID );
+            if ( !driverId.equals( driverIds[0] ) && !driverId.equals( driverIds[1] ) )
+            {
+                Assert.fail( "unexpected driverId" );
+            }
+        }
+
+    }
+
+
+    @Test
+    public void loadDrivers_LoadFails() throws IOException, BundleException
+    {
+
+        List<DriverLocator> locators = new ArrayList<DriverLocator>();
+
+        DriverLocator dl = Mockito.mock( DriverLocator.class, "dl" );
+        locators.add( dl );
+
+        String[] driverIds = new String[]
+            { "org.apache.felix.driver-1.0", "org.apache.felix.driver-1.1", };
+
+        for ( String string : driverIds )
+        {
+            Mockito.when( dl.loadDriver( string ) ).thenThrow( new IOException( "test exception" ) );
+        }
+
+        List<ServiceReference> refs = m_loader.loadDrivers( locators, driverIds );
+
+        Assert.assertEquals( "", 0, refs.size() );
+
+    }
+
+
+    @Test
+    public void loadDrivers_InstallFails() throws IOException, BundleException
+    {
+
+        List<DriverLocator> locators = new ArrayList<DriverLocator>();
+
+        DriverLocator dl = Mockito.mock( DriverLocator.class, "dl" );
+        locators.add( dl );
+
+        String[] driverIds = new String[]
+            { "org.apache.felix.driver-1.0", "org.apache.felix.driver-1.1", };
+
+        for ( String string : driverIds )
+        {
+        	Mockito.when( dl.loadDriver( string ) ).thenReturn( null );
+        	Mockito.when( m_context.installBundle( DriverLoader.DRIVER_LOCATION_PREFIX + string, null ) )
+        		.thenThrow(new BundleException( "test exception" ) );
+        }
+
+        List<ServiceReference> refs = m_loader.loadDrivers( locators, driverIds );
+
+        Assert.assertEquals( "", 0, refs.size() );
+    }
+
+}
diff --git a/deviceaccess/src/test/java/org/apache/felix/das/util/DriverMatcherTest.java b/deviceaccess/src/test/java/org/apache/felix/das/util/DriverMatcherTest.java
new file mode 100644
index 0000000..c020d7e
--- /dev/null
+++ b/deviceaccess/src/test/java/org/apache/felix/das/util/DriverMatcherTest.java
@@ -0,0 +1,329 @@
+/*
+ * 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.das.util;
+
+
+
+import org.apache.felix.das.DriverAttributes;
+import org.apache.felix.das.DeviceManager;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.Mock;
+import org.mockito.Mockito;
+import org.mockito.MockitoAnnotations;
+import org.mockito.invocation.InvocationOnMock;
+import org.mockito.stubbing.Answer;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.Constants;
+import org.osgi.framework.ServiceReference;
+import org.osgi.service.device.Driver;
+import org.osgi.service.device.DriverSelector;
+import org.osgi.service.device.Match;
+
+
+/**
+ * The Device Manager delegates driver matching to the DriverMatcher.
+ * This JUnit test tests the behavior of that DriverMatcher.
+ * 
+ * 
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public class DriverMatcherTest
+{
+
+    private DriverMatcher m_matcherImpl;
+
+    private int m_serviceId;
+
+    @Mock
+    private DeviceManager m_log;
+
+    @Before
+    public void setUp() throws Exception
+    {
+
+        m_serviceId = 0;
+
+        MockitoAnnotations.initMocks(this);
+    	
+        m_matcherImpl = new DriverMatcher( m_log );
+
+    }
+
+
+    private String tstDriverId( Match match )
+    {
+        return ( String ) match.getDriver().getProperty( org.osgi.service.device.Constants.DRIVER_ID );
+    }
+
+
+    private DriverAttributes tstCreateDriverAttributes( String id, int match, int ranking ) throws Exception
+    {
+
+        Bundle bundle = Mockito.mock( Bundle.class );
+        ServiceReference ref = Mockito.mock( ServiceReference.class );
+        
+        
+        Mockito.when(ref.getBundle()).thenReturn(bundle);
+        Mockito.when(bundle.getLocation()).thenReturn(DriverLoader.DRIVER_LOCATION_PREFIX + "-" + id);
+        
+        Mockito.when(ref.getProperty(Constants.SERVICE_ID))
+        	.thenReturn(m_serviceId++);
+        
+        Mockito.when(ref.getProperty(org.osgi.service.device.Constants.DRIVER_ID))
+        	.thenReturn(id);
+        
+        
+        
+        if ( ranking > 0 )
+        {
+        	Mockito.when( ref.getProperty( Constants.SERVICE_RANKING ) ).thenReturn( ranking );
+        }
+        else if ( ranking == 0 )
+        {
+        	Mockito.when( ref.getProperty( Constants.SERVICE_RANKING ) ).thenReturn( null );
+        }
+        else
+        {
+            //an invalid ranking object
+        	Mockito.when( ref.getProperty( Constants.SERVICE_RANKING ) ).thenReturn( new Object() );
+        }
+
+        Driver driver = Mockito.mock( Driver.class );
+        Mockito.when( driver.match( Mockito.isA( ServiceReference.class ) ) ).thenReturn( match );
+
+        return new DriverAttributes( ref, driver );
+
+    }
+
+
+    private void add( String id, int match ) throws Exception
+    {
+        add( id, match, 0 );
+    }
+
+
+    private void add( String id, int match, int ranking ) throws Exception
+    {
+        m_matcherImpl.add( match, tstCreateDriverAttributes( id, match, ranking ) );
+    }
+
+
+    @Test
+    public void GetBestMatchWithNoDriver() throws Exception
+    {
+
+        Match match = m_matcherImpl.getBestMatch();
+        Assert.assertNull( match );
+
+    }
+
+
+    @Test
+    public void GetBestMatchWithOneDriver() throws Exception
+    {
+
+        add( "org.apache.felix.driver-1.0", 1 );
+
+        Match match = m_matcherImpl.getBestMatch();
+        Assert.assertNotNull( match );
+        Assert.assertEquals( "org.apache.felix.driver-1.0", tstDriverId( match ) );
+
+    }
+
+    
+    @Test
+    public void GetSelectBestMatchThrowsException() throws Exception
+    {
+    	
+    	ServiceReference deviceRef = Mockito.mock(ServiceReference.class);
+    	DriverSelector selector = Mockito.mock(DriverSelector.class);
+
+    	Mockito.when(selector.select(Mockito.eq(deviceRef), Mockito.isA(Match[].class)))
+    		.thenThrow(new IllegalArgumentException("test"));
+    	
+        Match match = m_matcherImpl.selectBestMatch(deviceRef, selector);
+        Assert.assertNull( match );
+
+    }
+
+    @Test
+    public void GetBestMatchWithMultipleDrivers() throws Exception
+    {
+
+        add( "org.apache.felix.driver.a-1.0", 1 );
+        add( "org.apache.felix.driver.b-1.0", 1 );
+        add( "org.apache.felix.driver.c-1.0", 10 );
+
+        Match match = m_matcherImpl.getBestMatch();
+        Assert.assertNotNull( match );
+        Assert.assertEquals( "org.apache.felix.driver.c-1.0", tstDriverId( match ) );
+
+    }
+
+
+    @Test
+    public void GetBestMatchWithInvalidRanking() throws Exception
+    {
+
+        add( "org.apache.felix.driver.a-1.0", 1, 0 );
+        add( "org.apache.felix.driver.b-1.0", 1, -1 );
+
+        Match match = m_matcherImpl.getBestMatch();
+        Assert.assertNotNull( match );
+        Assert.assertEquals( "org.apache.felix.driver.a-1.0", tstDriverId( match ) );
+
+    }
+
+
+    @Test
+    public void GetBestMatchWithSameRanking() throws Exception
+    {
+
+        add( "org.apache.felix.driver.a-1.0", 1 );
+        add( "org.apache.felix.driver.b-1.0", 1 );
+
+        Match match = m_matcherImpl.getBestMatch();
+        Assert.assertNotNull( match );
+        Assert.assertEquals( "org.apache.felix.driver.a-1.0", tstDriverId( match ) );
+        Assert.assertEquals( 1, match.getMatchValue() );
+    }
+
+
+    @Test
+    public void GetBestMatchWithDifferentRanking() throws Exception
+    {
+
+        add( "org.apache.felix.driver.a-1.0", 1, 2 );
+        add( "org.apache.felix.driver.b-1.0", 1 );
+
+        Match match = m_matcherImpl.getBestMatch();
+
+        Assert.assertNotNull( match );
+        final String driverId = "org.apache.felix.driver.a-1.0";
+
+        Assert.assertEquals( driverId, tstDriverId( match ) );
+        Assert.assertEquals( 1, match.getMatchValue() );
+    }
+
+
+    @Test
+    public void GetBestMatchWithDifferentMatchValue() throws Exception
+    {
+
+        add( "org.apache.felix.driver.a-1.0", 1 );
+        add( "org.apache.felix.driver.b-1.0", 2 );
+        add( "org.apache.felix.driver.c-1.0", 1 );
+
+        Match match = m_matcherImpl.getBestMatch();
+        Assert.assertNotNull( match );
+        Assert.assertEquals( "org.apache.felix.driver.b-1.0", tstDriverId( match ) );
+
+        Assert.assertEquals( 2, match.getMatchValue() );
+    }
+
+
+    @Test
+    public void selectBestDriver() throws Exception
+    {
+
+        DriverSelector selector = Mockito.mock( DriverSelector.class );
+        ServiceReference deviceRef = Mockito.mock( ServiceReference.class );
+
+        add( "org.apache.felix.driver-1.0", 1 );
+        add( "org.apache.felix.driver-1.1", 1 );
+        add( "org.apache.felix.driver-1.2", 1 );
+        add( "org.apache.felix.driver-1.3", 1 );
+        add( "org.apache.felix.driver-1.4", 1 );
+        add( "org.apache.felix.driver-1.5", 1 );
+        
+        
+        
+        //this is the actual driverselector implementation
+        Mockito.when( selector.select( Mockito.isA(ServiceReference.class), Mockito.isA(Match[].class) ) )
+        	.thenAnswer( new Answer<Integer>()
+        {
+        	
+        	public Integer answer(InvocationOnMock invocation) throws Throwable
+            {
+                Match[] matches = ( Match[] ) invocation.getArguments()[1];
+                int index = 0;
+                for ( Match m : matches )
+                {
+                    if ( tstDriverId( m ).endsWith( "1.3" ) )
+                    {
+                        return index;
+                    }
+                    index++;
+                }
+                Assert.fail( "expected unreachable" );
+                return null;
+            }
+        } );
+
+
+        Match match = m_matcherImpl.selectBestMatch( deviceRef, selector );
+
+        Assert.assertNotNull( "no match returned", match );
+        String driverId = tstDriverId( match );
+
+        Assert.assertEquals( "org.apache.felix.driver-1.3", driverId );
+    }
+
+
+    @Test
+    public void selectFails() throws Exception
+    {
+
+        DriverSelector selector = Mockito.mock( DriverSelector.class );
+        ServiceReference deviceRef = Mockito.mock( ServiceReference.class );
+
+        Mockito.when( selector.select( Mockito.eq( deviceRef ), Mockito.isA(Match[].class) ) )
+            .thenThrow( new RuntimeException( "test exception" ) );
+
+        add( "org.apache.felix.driver-1.5", 1 );
+
+        Match match = m_matcherImpl.selectBestMatch( deviceRef, selector );
+
+        Assert.assertNull( match );
+
+    }
+    
+    
+    @Test
+    public void VerifyMatchToString() throws Exception
+    {
+
+        DriverSelector selector = Mockito.mock( DriverSelector.class );
+        ServiceReference deviceRef = Mockito.mock( ServiceReference.class );
+
+        Mockito.when( selector.select( Mockito.eq( deviceRef ), Mockito.isA(Match[].class) ) )
+            .thenReturn( 0 );
+
+        add( "org.apache.felix.driver-1.5", 2 );
+
+        Match match = m_matcherImpl.selectBestMatch( deviceRef, selector );
+
+        Assert.assertNotNull( match );
+        Assert.assertNotNull( match.toString() );
+
+    }
+
+}
diff --git a/deviceaccess/src/test/java/org/apache/felix/das/util/UtilTest.java b/deviceaccess/src/test/java/org/apache/felix/das/util/UtilTest.java
new file mode 100644
index 0000000..c19a690
--- /dev/null
+++ b/deviceaccess/src/test/java/org/apache/felix/das/util/UtilTest.java
@@ -0,0 +1,220 @@
+/*
+ * 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.das.util;
+
+
+import java.util.Properties;
+
+import junit.framework.Assert;
+
+import org.apache.felix.das.Utils;
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.Mock;
+import org.mockito.Mockito;
+import org.mockito.MockitoAnnotations;
+import org.mockito.invocation.InvocationOnMock;
+import org.mockito.stubbing.Answer;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.Filter;
+import org.osgi.framework.FrameworkUtil;
+import org.osgi.framework.InvalidSyntaxException;
+import org.osgi.framework.ServiceReference;
+import org.osgi.service.device.Constants;
+import org.osgi.service.device.Device;
+import org.osgi.service.log.LogService;
+
+
+/**
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ *
+ */
+public class UtilTest
+{
+
+    private DeviceAnalyzer m_devA;
+
+    @Mock
+    private LogService m_log;
+
+    @Mock
+    private BundleContext m_context;
+
+
+    @Before
+    public void before() throws Exception
+    {
+
+    	MockitoAnnotations.initMocks(this);
+    	
+        m_devA = new DeviceAnalyzer( m_context );
+        Utils.inject( m_devA, LogService.class, m_log );
+        
+        
+        String f1 = "(objectClass=org.osgi.service.device.Device)";
+        Filter deviceFilter = FrameworkUtil.createFilter(f1);
+        Mockito.when(m_context.createFilter(Mockito.eq(f1))).thenReturn(deviceFilter);
+        
+        String f2 = "(DEVICE_CATEGORY=*)";
+        Filter driverFilter = FrameworkUtil.createFilter(f2);
+        Mockito.when(m_context.createFilter(f2)).thenReturn(driverFilter);
+        
+
+        Utils.invoke( m_devA, "start" );
+    }
+
+    
+    
+    private ServiceReference createReference(final Properties p) 
+    {
+        ServiceReference ref = Mockito.mock( ServiceReference.class );
+
+        Mockito.when(ref.getProperty(Mockito.anyString())).thenAnswer(new Answer<Object>() {
+        	public Object answer(InvocationOnMock invocation) throws Throwable {
+        		return p.get(invocation.getArguments()[0].toString());
+        	}
+        });
+        
+        Mockito.when(ref.getPropertyKeys()).thenAnswer(new Answer<Object>() {
+        	public Object answer(InvocationOnMock invocation) throws Throwable {
+        		return p.keySet().toArray(new String[0]);
+        	}
+        });
+        
+        
+        return ref;
+    }
+
+    @Test
+    public void ShowDeviceIfThereIsAnInvalidCategory() throws Exception
+    {
+
+        Properties p = new Properties();
+        p.put( org.osgi.framework.Constants.OBJECTCLASS, new String[]{Object.class.getName()} );
+        p.put(Constants.DEVICE_CATEGORY, "dummy");
+
+        ServiceReference ref = createReference(p);
+        
+        m_devA.deviceAdded( ref );
+        
+        Mockito.verify(m_log).log(Mockito.eq(LogService.LOG_ERROR), Mockito.anyString());
+
+    }
+
+
+    @Test
+    public void ShowDeviceIfThereIsNoCategory() throws Exception
+    {
+
+        Properties p = new Properties();
+        p.put( org.osgi.framework.Constants.OBJECTCLASS, new String[]{Object.class.getName()} );
+
+        ServiceReference ref = createReference(p);
+        
+        m_devA.deviceAdded( ref );
+        
+        Mockito.verify(m_log).log( Mockito.eq( LogService.LOG_ERROR ), Mockito.isA( String.class ) );
+    }
+
+    @Test 
+    public void VerifyValidIsDeviceInstanceValidationIfDevice() throws InvalidSyntaxException 
+    {
+
+        Properties p = new Properties();
+        p.put( org.osgi.framework.Constants.OBJECTCLASS, new String[]{Device.class.getName()} );
+        
+    	ServiceReference ref = createReference(p);
+    
+    	Assert.assertTrue( "Incorrectly determined as no device", Util.isDeviceInstance(ref) );
+    }
+
+    @Test 
+    public void VerifyValidIsDeviceInstanceValidationThrowsException() throws InvalidSyntaxException 
+    {
+
+        Properties p = new Properties();
+        p.put( org.osgi.framework.Constants.OBJECTCLASS, new String[]{Device.class.getName()} );
+        
+    	ServiceReference ref = createReference(p);
+    
+    	Assert.assertTrue( "Incorrectly determined as no device", Util.isDeviceInstance(ref) );
+    }
+
+    @Test 
+    public void VerifyValidFilterStringCreation() throws InvalidSyntaxException {
+    	
+    	Object[] data = new Object[]{"a","b","c","d"};
+    	String str = Util.createFilterString("(|(%s=%s)(%s=%s))", data);
+    	
+    	Assert.assertEquals("filter string mismatch","(|(a=b)(c=d))", str);
+    }
+
+    @Test 
+    public void VerifyValidFilterCreation() throws InvalidSyntaxException {
+    	
+    	Object[] data = new Object[]{Constants.DEVICE_CATEGORY, "dummy"};
+    	Filter filter = Util.createFilter("(%s=%s)", data);
+    	
+    	
+    	Properties matching = new Properties();
+    	matching.put(Constants.DEVICE_CATEGORY, new String[]{"dummy", "nonsense"});
+    	Assert.assertTrue("matching filter does not match", filter.match(matching));
+    	
+    	Properties notmatching = new Properties();
+    	notmatching.put(Constants.DEVICE_CATEGORY, new String[]{"lummy", "nonsense"});
+    	Assert.assertFalse("notmatching filter does match", filter.match(notmatching));
+
+    	
+    }
+    
+ 
+    
+    
+    
+    @Test
+    public void ShowDeviceIfThereIsAnEmptyCategory() throws Exception
+    {
+
+        Properties p = new Properties();
+        p.put( org.osgi.framework.Constants.OBJECTCLASS, new String[]{Object.class.getName()} );
+        p.put( Constants.DEVICE_CATEGORY, new String[0] );
+
+        
+        ServiceReference ref = createReference(p);
+
+        m_devA.deviceAdded( ref );
+
+        Mockito.verify(m_log).log( Mockito.eq( LogService.LOG_ERROR ), Mockito.isA( String.class ) );
+
+    }
+
+
+    @Test
+    public void NoShowDeviceIfThereIsAValidCategory() throws Exception
+    {
+
+        Properties p = new Properties();
+        p.put( org.osgi.framework.Constants.OBJECTCLASS, new String[]{Device.class.getName()} );
+        p.put( Constants.DEVICE_CATEGORY, new String[]{"dummy"} );
+
+        ServiceReference ref = createReference(p);
+
+        m_devA.deviceAdded( ref );
+    }
+}