You are viewing a plain text version of this content. The canonical link for it is here.
Posted to surefire-commits@maven.apache.org by kr...@apache.org on 2011/02/09 20:49:38 UTC

svn commit: r1069058 - in /maven/surefire/trunk: maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/booterclient/ maven-surefire-common/src/test/java/org/apache/maven/plugin/surefire/booterclient/ surefire-booter/src/main/java/org/apa...

Author: krosenvold
Date: Wed Feb  9 19:49:37 2011
New Revision: 1069058

URL: http://svn.apache.org/viewvc?rev=1069058&view=rev
Log:
[SUREFIRE-696] Improve class design

    Patch by Stefan Birkner. Applied with only a minor formatting adjustment

Modified:
    maven/surefire/trunk/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/booterclient/ForkStarter.java
    maven/surefire/trunk/maven-surefire-common/src/test/java/org/apache/maven/plugin/surefire/booterclient/BooterDeserializerStartupConfigurationTest.java
    maven/surefire/trunk/surefire-booter/src/main/java/org/apache/maven/surefire/booter/BooterDeserializer.java
    maven/surefire/trunk/surefire-booter/src/main/java/org/apache/maven/surefire/booter/Classpath.java
    maven/surefire/trunk/surefire-booter/src/main/java/org/apache/maven/surefire/booter/ClasspathConfiguration.java
    maven/surefire/trunk/surefire-booter/src/main/java/org/apache/maven/surefire/booter/SurefireStarter.java
    maven/surefire/trunk/surefire-booter/src/test/java/org/apache/maven/surefire/booter/ClasspathTest.java

Modified: maven/surefire/trunk/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/booterclient/ForkStarter.java
URL: http://svn.apache.org/viewvc/maven/surefire/trunk/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/booterclient/ForkStarter.java?rev=1069058&r1=1069057&r2=1069058&view=diff
==============================================================================
--- maven/surefire/trunk/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/booterclient/ForkStarter.java (original)
+++ maven/surefire/trunk/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/booterclient/ForkStarter.java Wed Feb  9 19:49:37 2011
@@ -196,7 +196,7 @@ public class ForkStarter
             ? startupConfiguration.getClasspathConfiguration().getTestClasspath()
             : null;
 
-        Classpath bootClasspath = bootClasspathConfiguration.append( additionlClassPathUrls );
+        Classpath bootClasspath = Classpath.join( bootClasspathConfiguration, additionlClassPathUrls );
 
         Commandline cli = forkConfiguration.createCommandLine( bootClasspath.getClassPath(),
                                                                startupConfiguration.getClassLoaderConfiguration() );

Modified: maven/surefire/trunk/maven-surefire-common/src/test/java/org/apache/maven/plugin/surefire/booterclient/BooterDeserializerStartupConfigurationTest.java
URL: http://svn.apache.org/viewvc/maven/surefire/trunk/maven-surefire-common/src/test/java/org/apache/maven/plugin/surefire/booterclient/BooterDeserializerStartupConfigurationTest.java?rev=1069058&r1=1069057&r2=1069058&view=diff
==============================================================================
--- maven/surefire/trunk/maven-surefire-common/src/test/java/org/apache/maven/plugin/surefire/booterclient/BooterDeserializerStartupConfigurationTest.java (original)
+++ maven/surefire/trunk/maven-surefire-common/src/test/java/org/apache/maven/plugin/surefire/booterclient/BooterDeserializerStartupConfigurationTest.java Wed Feb  9 19:49:37 2011
@@ -19,6 +19,16 @@ package org.apache.maven.plugin.surefire
  * under the License.
  */
 
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Properties;
+
+import junit.framework.TestCase;
+
 import org.apache.maven.surefire.booter.BooterConstants;
 import org.apache.maven.surefire.booter.BooterDeserializer;
 import org.apache.maven.surefire.booter.ClassLoaderConfiguration;
@@ -31,15 +41,6 @@ import org.apache.maven.surefire.testset
 import org.apache.maven.surefire.testset.TestArtifactInfo;
 import org.apache.maven.surefire.testset.TestRequest;
 
-import java.io.File;
-import java.io.FileInputStream;
-import java.io.IOException;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Properties;
-
-import junit.framework.TestCase;
-
 /**
  * Performs roundtrip testing of serialization/deserialization of The StartupConfiguration
  *
@@ -66,10 +67,12 @@ public class BooterDeserializerStartupCo
             getReloadedStartupConfiguration().getClasspathConfiguration();
         Properties props = new Properties();
         classpathConfiguration.setForkProperties( props );
+        List testClassPathUrls = classpathConfiguration.getTestClasspath().getClassPath();
         assertEquals( "true", props.get( BooterConstants.ENABLE_ASSERTIONS ) );
         assertEquals( "true", props.get( BooterConstants.CHILD_DELEGATION ) );
-        assertEquals( "CP1", classpathConfiguration.getTestClasspath().get( 0 ) );
-        assertEquals( "CP2", classpathConfiguration.getTestClasspath().get( 1 ) );
+        assertEquals( 2, testClassPathUrls.size() );
+        assertEquals( "CP1", testClassPathUrls.get( 0 ) );
+        assertEquals( "CP2", testClassPathUrls.get( 1 ) );
         assertEquals( "SP1", props.get( BooterConstants.SUREFIRE_CLASSPATHURL + "0" ) );
         assertEquals( "SP2", props.get( BooterConstants.SUREFIRE_CLASSPATHURL + "1" ) );
     }

Modified: maven/surefire/trunk/surefire-booter/src/main/java/org/apache/maven/surefire/booter/BooterDeserializer.java
URL: http://svn.apache.org/viewvc/maven/surefire/trunk/surefire-booter/src/main/java/org/apache/maven/surefire/booter/BooterDeserializer.java?rev=1069058&r1=1069057&r2=1069058&view=diff
==============================================================================
--- maven/surefire/trunk/surefire-booter/src/main/java/org/apache/maven/surefire/booter/BooterDeserializer.java (original)
+++ maven/surefire/trunk/surefire-booter/src/main/java/org/apache/maven/surefire/booter/BooterDeserializer.java Wed Feb  9 19:49:37 2011
@@ -101,8 +101,8 @@ public class BooterDeserializer
         boolean useManifestOnlyJar = properties.getBooleanProperty( USEMANIFESTONLYJAR );
         String providerConfiguration = properties.getProperty( PROVIDER_CONFIGURATION );
 
-        final List classpath = properties.getStringList( CLASSPATH_URL );
-        final List sureFireClasspath = properties.getStringList( SUREFIRE_CLASSPATHURL );
+        Classpath classpath = Classpath.readFromForkProperties( properties, CLASSPATH_URL );
+        Classpath sureFireClasspath = Classpath.readFromForkProperties( properties, SUREFIRE_CLASSPATHURL );
 
         ClassLoaderConfiguration classLoaderConfiguration =
             new ClassLoaderConfiguration( useSystemClassLoader, useManifestOnlyJar );
@@ -113,10 +113,4 @@ public class BooterDeserializer
         return StartupConfiguration.inForkedVm( providerConfiguration, classpathConfiguration,
                                                 classLoaderConfiguration );
     }
-
-    private Boolean valueOf( boolean aBoolean )
-    {  // jdk1.3 compat
-        return aBoolean ? Boolean.TRUE : Boolean.FALSE;
-    }
-
 }

Modified: maven/surefire/trunk/surefire-booter/src/main/java/org/apache/maven/surefire/booter/Classpath.java
URL: http://svn.apache.org/viewvc/maven/surefire/trunk/surefire-booter/src/main/java/org/apache/maven/surefire/booter/Classpath.java?rev=1069058&r1=1069057&r2=1069058&view=diff
==============================================================================
--- maven/surefire/trunk/surefire-booter/src/main/java/org/apache/maven/surefire/booter/Classpath.java (original)
+++ maven/surefire/trunk/surefire-booter/src/main/java/org/apache/maven/surefire/booter/Classpath.java Wed Feb  9 19:49:37 2011
@@ -19,16 +19,15 @@ package org.apache.maven.surefire.booter
  * under the License.
  */
 
-import org.apache.maven.surefire.util.UrlUtils;
-
 import java.io.File;
 import java.net.MalformedURLException;
 import java.util.ArrayList;
-import java.util.HashSet;
+import java.util.Collection;
 import java.util.Iterator;
 import java.util.List;
 import java.util.Properties;
-import java.util.Set;
+
+import org.apache.maven.surefire.util.UrlUtils;
 
 /**
  * An ordered set of classpath elements
@@ -37,111 +36,95 @@ import java.util.Set;
  */
 public class Classpath
 {
-    private final List elements;
-
-    private final Set elementSet;
-
-    public Classpath()
+    static Classpath readFromForkProperties( PropertiesWrapper properties, String prefix )
     {
-        this( new ArrayList() );
+        List elements = properties.getStringList( prefix );
+        return new Classpath( elements );
     }
 
-    private Classpath( List elements )
+    public static Classpath join( Classpath firstClasspath, Classpath secondClasspath )
     {
-        this.elements = elements;
-        this.elementSet = new HashSet( elements );
-        if ( elements.size() != elementSet.size() )
-        {
-            throw new IllegalStateException( "This is not permitted and is a violation of contract" );
-        }
+        Classpath joinedClasspath = new Classpath();
+        joinedClasspath.addElementsOfClasspath( firstClasspath );
+        joinedClasspath.addElementsOfClasspath( secondClasspath );
+        return joinedClasspath;
     }
 
-    public List getClassPath()
+    private final List elements = new ArrayList();
+
+    public Classpath()
     {
-        return elements;
     }
 
-    public Classpath append( Classpath otherClassPathToAppend )
+    private Classpath( Collection elements )
     {
-        int additionalLength = otherClassPathToAppend != null ? otherClassPathToAppend.size() : 0;
-        List combinedClassPath = new ArrayList( elements.size() + additionalLength );
-
-        combinedClassPath.addAll( elements );
-
-        if ( otherClassPathToAppend != null )
-        {
-            Iterator iterator = otherClassPathToAppend.getClassPath().iterator();
-            while ( iterator.hasNext() )
-            {
-                String element = (String) iterator.next();
-                if ( !elementSet.contains( element ) )
-                {
-                    combinedClassPath.add( element );
-                }
-            }
-        }
-        return new Classpath( combinedClassPath );
+        this();
+        addElements( elements );
     }
 
-
     public void addClassPathElementUrl( String path )
     {
-        if ( !elementSet.contains( path ) )
+        if ( path == null )
+        {
+            throw new IllegalArgumentException( "Null is not a valid class path element url." );
+        }
+        else if ( !elements.contains( path ) )
         {
             elements.add( path );
-            elementSet.add( path );
         }
     }
 
-    public Object get( int index )
+    private void addElements( Collection additionalElements )
     {
-        return elements.get( index );
+        for ( Iterator it = additionalElements.iterator(); it.hasNext(); )
+        {
+            String element = (String) it.next();
+            addClassPathElementUrl( element );
+        }
     }
 
-    public int size()
+    private void addElementsOfClasspath( Classpath otherClasspath )
     {
-        return elements.size();
+        if ( otherClasspath != null )
+        {
+            addElements( otherClasspath.elements );
+        }
     }
 
-    public String getClassPathAsString()
+    public List getClassPath()
     {
-        StringBuffer sb = new StringBuffer();
-        for ( Iterator i = elements.iterator(); i.hasNext(); )
-        {
-            sb.append( (String) i.next() ).append( File.pathSeparatorChar );
-        }
-        return sb.toString();
+        return new ArrayList( elements );
     }
 
     public List getAsUrlList()
         throws MalformedURLException
     {
         List urls = new ArrayList();
-
         for ( Iterator i = elements.iterator(); i.hasNext(); )
         {
             String url = (String) i.next();
-
-            if ( url != null )
-            {
-                File f = new File( url );
-                urls.add( UrlUtils.getURL( f ) );
-            }
+            File f = new File( url );
+            urls.add( UrlUtils.getURL( f ) );
         }
         return urls;
     }
 
-    public void setForkProperties( Properties properties, String prefix )
+    void writeToForkProperties( Properties properties, String prefix )
     {
-        for ( int i = 0; i < elements.size(); i++ )
+        for ( int i = 0; i < elements.size(); ++i )
         {
-            String url = (String) elements.get( i );
-            properties.setProperty( prefix + i, url );
+            String element = (String) elements.get( i );
+            properties.setProperty( prefix + i, element );
         }
     }
 
-    public void setAsSystemProperty( String propertyName ){
-         System.setProperty( propertyName, getClassPathAsString());
+    public void writeToSystemProperty( String propertyName )
+    {
+        StringBuffer sb = new StringBuffer();
+        for ( Iterator i = elements.iterator(); i.hasNext(); )
+        {
+            sb.append( (String) i.next() ).append( File.pathSeparatorChar );
+        }
+        System.setProperty( propertyName, sb.toString() );
     }
-
 }

Modified: maven/surefire/trunk/surefire-booter/src/main/java/org/apache/maven/surefire/booter/ClasspathConfiguration.java
URL: http://svn.apache.org/viewvc/maven/surefire/trunk/surefire-booter/src/main/java/org/apache/maven/surefire/booter/ClasspathConfiguration.java?rev=1069058&r1=1069057&r2=1069058&view=diff
==============================================================================
--- maven/surefire/trunk/surefire-booter/src/main/java/org/apache/maven/surefire/booter/ClasspathConfiguration.java (original)
+++ maven/surefire/trunk/surefire-booter/src/main/java/org/apache/maven/surefire/booter/ClasspathConfiguration.java Wed Feb  9 19:49:37 2011
@@ -25,7 +25,6 @@ import java.lang.reflect.InvocationTarge
 import java.lang.reflect.Method;
 import java.net.MalformedURLException;
 import java.net.URL;
-import java.util.ArrayList;
 import java.util.Iterator;
 import java.util.List;
 import java.util.Properties;
@@ -33,7 +32,7 @@ import java.util.Properties;
 /**
  * Represents the classpaths for the BooterConfiguration.
  * <p/>
- *
+ * 
  * @author Jason van Zyl
  * @author Emmanuel Venisse
  * @author Kristian Rosenvold
@@ -55,44 +54,31 @@ public class ClasspathConfiguration
     // todo: @deprecated because the IsolatedClassLoader is really isolated - no parent.
     private final boolean childDelegation;
 
-
     public ClasspathConfiguration( boolean enableAssertions, boolean childDelegation )
     {
-        this( new ArrayList(), new ArrayList(), enableAssertions, childDelegation );
+        this( new Classpath(), new Classpath(), enableAssertions, childDelegation );
     }
 
     /*
     * Reads the config from the supplied stream. Closes the stream.
     */
-    public ClasspathConfiguration( List classPathUrls, List surefireClassPathUrls, boolean enableAssertions,
+    public ClasspathConfiguration( Classpath classPathUrls, Classpath surefireClassPathUrls, boolean enableAssertions,
                                    boolean childDelegation )
     {
-
         this.enableAssertions = enableAssertions;
         this.childDelegation = childDelegation;
-        this.classpathUrls = new Classpath();
-        for ( Iterator cpi = classPathUrls.iterator(); cpi.hasNext(); )
-        {
-            this.classpathUrls.addClassPathElementUrl( (String) cpi.next() );
-        }
-
-        this.surefireClasspathUrls = new Classpath();
-        for ( Iterator scpi = surefireClassPathUrls.iterator(); scpi.hasNext(); )
-        {
-            this.surefireClasspathUrls.addClassPathElementUrl( (String) scpi.next() );
-        }
+        this.classpathUrls = classPathUrls;
+        this.surefireClasspathUrls = surefireClassPathUrls;
     }
 
-
     public void setForkProperties( Properties properties )
     {
-        classpathUrls.setForkProperties( properties, BooterConstants.CLASSPATH_URL );
-        surefireClasspathUrls.setForkProperties( properties, BooterConstants.SUREFIRE_CLASSPATHURL );
+        classpathUrls.writeToForkProperties( properties, BooterConstants.CLASSPATH_URL );
+        surefireClasspathUrls.writeToForkProperties( properties, BooterConstants.SUREFIRE_CLASSPATHURL );
         properties.setProperty( BooterConstants.ENABLE_ASSERTIONS, String.valueOf( enableAssertions ) );
         properties.setProperty( BooterConstants.CHILD_DELEGATION, String.valueOf( childDelegation ) );
     }
 
-
     private static Method assertionStatusMethod;
 
     static

Modified: maven/surefire/trunk/surefire-booter/src/main/java/org/apache/maven/surefire/booter/SurefireStarter.java
URL: http://svn.apache.org/viewvc/maven/surefire/trunk/surefire-booter/src/main/java/org/apache/maven/surefire/booter/SurefireStarter.java?rev=1069058&r1=1069057&r2=1069058&view=diff
==============================================================================
--- maven/surefire/trunk/surefire-booter/src/main/java/org/apache/maven/surefire/booter/SurefireStarter.java (original)
+++ maven/surefire/trunk/surefire-booter/src/main/java/org/apache/maven/surefire/booter/SurefireStarter.java Wed Feb  9 19:49:37 2011
@@ -61,13 +61,11 @@ public class SurefireStarter
     public int runSuitesInProcess( Object testSet, File surefirePropertiesFile, Properties p )
         throws SurefireExecutionException, IOException
     {
-        final StartupConfiguration starterConfiguration = startupConfiguration;
-        final ClasspathConfiguration classpathConfiguration = starterConfiguration.getClasspathConfiguration();
-
-        classpathConfiguration.getTestClasspath().setAsSystemProperty( SUREFIRE_TEST_CLASSPATH );
+        writeSurefireTestClasspathProperty();
+        final ClasspathConfiguration classpathConfiguration = startupConfiguration.getClasspathConfiguration();
 
         ClassLoader testsClassLoader = classpathConfiguration.createTestClassLoaderConditionallySystem(
-            starterConfiguration.useSystemClassLoader() );
+            startupConfiguration.useSystemClassLoader() );
 
         ClassLoader surefireClassLoader = classpathConfiguration.createSurefireClassLoader( testsClassLoader );
 
@@ -95,27 +93,27 @@ public class SurefireStarter
     private ClassLoader createInProcessTestClassLoader()
         throws SurefireExecutionException
     {
-        ClassLoader testsClassLoader;
-
-        final ClasspathConfiguration classpathConfiguration = startupConfiguration.getClasspathConfiguration();
-
-        String testClassPath = classpathConfiguration.getTestClasspath().getClassPathAsString();
-
-        classpathConfiguration.getTestClasspath().setAsSystemProperty( SUREFIRE_TEST_CLASSPATH );
-
+        writeSurefireTestClasspathProperty();
+        ClasspathConfiguration classpathConfiguration = startupConfiguration.getClasspathConfiguration();
         if ( startupConfiguration.isManifestOnlyJarRequestedAndUsable() )
         {
-            testsClassLoader = getClass().getClassLoader(); // ClassLoader.getSystemClassLoader()
+            ClassLoader testsClassLoader = getClass().getClassLoader(); // ClassLoader.getSystemClassLoader()
             // SUREFIRE-459, trick the app under test into thinking its classpath was conventional
             // (instead of a single manifest-only jar)
             System.setProperty( "surefire.real.class.path", System.getProperty( "java.class.path" ) );
-            System.setProperty( "java.class.path", testClassPath );
+            classpathConfiguration.getTestClasspath().writeToSystemProperty( "java.class.path" );
+            return testsClassLoader;
         }
         else
         {
-            testsClassLoader = classpathConfiguration.createTestClassLoader();
+            return classpathConfiguration.createTestClassLoader();
         }
-        return testsClassLoader;
+    }
+
+    private void writeSurefireTestClasspathProperty()
+    {
+        ClasspathConfiguration classpathConfiguration = startupConfiguration.getClasspathConfiguration();
+        classpathConfiguration.getTestClasspath().writeToSystemProperty( SUREFIRE_TEST_CLASSPATH );
     }
 
     private static final String RESULTS_ERRORS = "errors";

Modified: maven/surefire/trunk/surefire-booter/src/test/java/org/apache/maven/surefire/booter/ClasspathTest.java
URL: http://svn.apache.org/viewvc/maven/surefire/trunk/surefire-booter/src/test/java/org/apache/maven/surefire/booter/ClasspathTest.java?rev=1069058&r1=1069057&r2=1069058&view=diff
==============================================================================
--- maven/surefire/trunk/surefire-booter/src/test/java/org/apache/maven/surefire/booter/ClasspathTest.java (original)
+++ maven/surefire/trunk/surefire-booter/src/test/java/org/apache/maven/surefire/booter/ClasspathTest.java Wed Feb  9 19:49:37 2011
@@ -19,69 +19,168 @@ package org.apache.maven.surefire.booter
  * under the License.
  */
 
-
-import junit.framework.TestCase;
-
 import java.io.File;
 import java.util.List;
 import java.util.Properties;
 
+import junit.framework.TestCase;
+
 /**
  * @author Kristian Rosenvold
  */
 public class ClasspathTest
     extends TestCase
 {
-    public void testGetClassPath()
+    private static final String DUMMY_PROPERTY_NAME = "dummyProperty";
+
+    private static final String DUMMY_URL_1 = "foo.jar";
+
+    private static final String DUMMY_URL_2 = "bar.jar";
+
+    public void testShouldWriteEmptyPropertyForEmptyClasspath()
         throws Exception
     {
         Classpath classpath = new Classpath();
-        classpath.addClassPathElementUrl( "foo.jar" );
-        classpath.addClassPathElementUrl( "bar.jar" );
-        assertEquals( expected, classpath.getClassPathAsString() );
+        classpath.writeToSystemProperty( DUMMY_PROPERTY_NAME );
+        assertEquals( "", System.getProperty( DUMMY_PROPERTY_NAME ) );
     }
 
-    public void testGetClassPathNoDupes()
+    public void testShouldWriteSeparatedElementsAsSystemProperty()
         throws Exception
     {
-        Classpath classPath = getWith2DistinctElements();
-        assertEquals( expected, classPath.getClassPathAsString() );
+        Classpath classpath = new Classpath();
+        classpath.addClassPathElementUrl( DUMMY_URL_1 );
+        classpath.addClassPathElementUrl( DUMMY_URL_2 );
+        classpath.writeToSystemProperty( DUMMY_PROPERTY_NAME );
+        assertEquals( DUMMY_URL_1 + File.pathSeparatorChar + DUMMY_URL_2 + File.pathSeparatorChar,
+                      System.getProperty( DUMMY_PROPERTY_NAME ) );
+    }
+
+    public void testShouldAddNoDuplicateElements()
+    {
+        Classpath classpath = new Classpath();
+        classpath.addClassPathElementUrl( DUMMY_URL_1 );
+        classpath.addClassPathElementUrl( DUMMY_URL_1 );
+        assertClasspathConsistsOfElements( classpath, new String[] { DUMMY_URL_1 } );
     }
 
-    public void testGetClassPathNoDupes2()
+    public void testGetAsUrlList()
         throws Exception
     {
-        Classpath classpath = getWith2DistinctElements();
-        assertEquals( expected, classpath.append( getWith2DistinctElements() ).getClassPathAsString() );
+        final List asUrlList = createClasspathWithTwoElements().getAsUrlList();
+        assertEquals( 2, asUrlList.size() );
+        assertTrue( asUrlList.get( 0 ).toString().endsWith( DUMMY_URL_1 ) );
+        assertTrue( asUrlList.get( 1 ).toString().endsWith( DUMMY_URL_2 ) );
     }
 
-    final String expected = "foo.jar" + File.pathSeparatorChar + "bar.jar" + File.pathSeparatorChar;
+    public void testSetForkProperties()
+        throws Exception
+    {
+        Properties properties = new Properties();
+        createClasspathWithTwoElements().writeToForkProperties( properties, "test" );
+        assertEquals( DUMMY_URL_1, properties.get( "test0" ) );
+        assertEquals( DUMMY_URL_2, properties.get( "test1" ) );
+    }
 
-    private Classpath getWith2DistinctElements()
+    public void testShouldThrowIllegalArgumentExceptionWhenNullIsAddedAsClassPathElementUrl()
+        throws Exception
     {
         Classpath classpath = new Classpath();
-        classpath.addClassPathElementUrl( "foo.jar" );
-        classpath.addClassPathElementUrl( "bar.jar" );
-        classpath.addClassPathElementUrl( "foo.jar" );
-        return classpath;
+        try
+        {
+            classpath.addClassPathElementUrl( null );
+            fail( "IllegalArgumentException not thrown." );
+        }
+        catch ( IllegalArgumentException expected )
+        {
+        }
     }
 
-    public void testGetAsUrlList()
+    public void testShouldNotAddNullAsClassPathElementUrl()
         throws Exception
     {
-        final List asUrlList = getWith2DistinctElements().getAsUrlList();
-        assertEquals( 2, asUrlList.size() );
-        assertTrue( asUrlList.get( 0 ).toString().endsWith( "foo.jar" ) );
-        assertTrue( asUrlList.get( 1 ).toString().endsWith( "bar.jar" ) );
+        Classpath classpath = new Classpath();
+        try
+        {
+            classpath.addClassPathElementUrl( null );
+        }
+        catch ( IllegalArgumentException ignored )
+        {
+        }
+        assertEmptyClasspath( classpath );
     }
 
-    public void testSetForkProperties()
+    public void testShouldJoinTwoNullClasspaths()
+    {
+        Classpath joinedClasspath = Classpath.join( null, null );
+        assertEmptyClasspath( joinedClasspath );
+    }
+
+    public void testShouldHaveAllElementsAfterJoiningTwoDifferentClasspaths()
         throws Exception
     {
-        Properties properties = new Properties();
-        getWith2DistinctElements().setForkProperties( properties, "test" );
-        assertEquals( "foo.jar", properties.get( "test0" ) );
-        assertEquals( "bar.jar", properties.get( "test1" ) );
+        Classpath firstClasspath = new Classpath();
+        firstClasspath.addClassPathElementUrl( DUMMY_URL_1 );
+        Classpath secondClasspath = new Classpath();
+        secondClasspath.addClassPathElementUrl( DUMMY_URL_2 );
+        Classpath joinedClasspath = Classpath.join( firstClasspath, secondClasspath );
+        assertClasspathConsistsOfElements( joinedClasspath, new String[] { DUMMY_URL_1, DUMMY_URL_2 } );
     }
 
+    public void testShouldNotHaveDuplicatesAfterJoiningTowClasspathsWithEqualElements()
+        throws Exception
+    {
+        Classpath firstClasspath = new Classpath();
+        firstClasspath.addClassPathElementUrl( DUMMY_URL_1 );
+        Classpath secondClasspath = new Classpath();
+        secondClasspath.addClassPathElementUrl( DUMMY_URL_1 );
+        Classpath joinedClasspath = Classpath.join( firstClasspath, secondClasspath );
+        assertClasspathConsistsOfElements( joinedClasspath, new String[] { DUMMY_URL_1 } );
+    }
+    
+    public void testShouldReadEmptyClasspathFromForkProperties() {
+        PropertiesWrapper properties = new PropertiesWrapper( new Properties() );
+        Classpath classpath = Classpath.readFromForkProperties( properties, "test" );
+        assertEmptyClasspath(classpath);
+    }
+    
+    public void testShouldReadClasspathWithToElementsFromForkProperties() {
+        PropertiesWrapper properties = new PropertiesWrapper( new Properties() );
+        properties.setProperty( "test0", DUMMY_URL_1 );
+        properties.setProperty( "test1", DUMMY_URL_2 );
+        Classpath classpath = Classpath.readFromForkProperties( properties, "test" );
+        assertClasspathConsistsOfElements( classpath, new String[] { DUMMY_URL_1, DUMMY_URL_2 } );
+    }
+    
+    public void testShouldNotBeAbleToRemoveElement()
+        throws Exception
+    {
+        Classpath classpath = createClasspathWithTwoElements();
+        classpath.getClassPath().remove( 0 );
+        assertEquals(2, classpath.getClassPath().size());
+    }
+
+    private void assertClasspathConsistsOfElements( Classpath classpath, String[] elements )
+    {
+        List classpathElements = classpath.getClassPath();
+        for ( int i = 0; i < elements.length; ++i )
+        {
+            assertTrue( "The element '" + elements[i] + " is missing.", classpathElements.contains( elements[i] ) );
+        }
+        assertEquals( "Wrong number of classpath elements.", elements.length, classpathElements.size() );
+    }
+
+    private void assertEmptyClasspath( Classpath classpath )
+    {
+        List classpathElements = classpath.getClassPath();
+        assertEquals( "Wrong number of classpath elements.", 0, classpathElements.size() );
+    }
+
+    private Classpath createClasspathWithTwoElements()
+    {
+        Classpath classpath = new Classpath();
+        classpath.addClassPathElementUrl( DUMMY_URL_1 );
+        classpath.addClassPathElementUrl( DUMMY_URL_2 );
+        return classpath;
+    }
 }