diff --git a/briar-tests/src/net/sf/briar/plugins/DuplexClientTest.java b/briar-tests/src/net/sf/briar/plugins/DuplexClientTest.java
index 2ffdace5eb168170e8cd7d40c6cf330071dc523d..942ff2bdd9af2499153b96f0b223ab7df94790ca 100644
--- a/briar-tests/src/net/sf/briar/plugins/DuplexClientTest.java
+++ b/briar-tests/src/net/sf/briar/plugins/DuplexClientTest.java
@@ -1,5 +1,7 @@
 package net.sf.briar.plugins;
 
+import static net.sf.briar.api.plugins.InvitationConstants.CONNECTION_TIMEOUT;
+
 import java.io.IOException;
 import java.util.Map;
 
@@ -17,37 +19,47 @@ public abstract class DuplexClientTest extends DuplexTest {
 		assert plugin != null;
 		// Start the plugin
 		System.out.println("Starting plugin");
-		plugin.start();
-		// Try to connect to the server
-		System.out.println("Creating connection");
-		DuplexTransportConnection d = plugin.createConnection(contactId);
-		if(d == null) {
-			System.out.println("Connection failed");
-		} else {
-			System.out.println("Connection created");
-			receiveChallengeSendResponse(d);
-		}
-		// Try to send an invitation
-		System.out.println("Sending invitation");
-		d = plugin.sendInvitation(getPseudoRandom(123), INVITATION_TIMEOUT);
-		if(d == null) {
-			System.out.println("Connection failed");
-		} else {
-			System.out.println("Connection created");
-			receiveChallengeSendResponse(d);
+		if(!plugin.start()) {
+			System.out.println("Plugin failed to start");
+			return;
 		}
-		// Try to accept an invitation
-		System.out.println("Accepting invitation");
-		d = plugin.acceptInvitation(getPseudoRandom(456), INVITATION_TIMEOUT);
-		if(d == null) {
-			System.out.println("Connection failed");
-		} else {
-			System.out.println("Connection created");
-			sendChallengeReceiveResponse(d);
+		try {
+			// Try to connect to the server
+			System.out.println("Creating connection");
+			DuplexTransportConnection d = plugin.createConnection(contactId);
+			if(d == null) {
+				System.out.println("Connection failed");
+				return;
+			} else {
+				System.out.println("Connection created");
+				receiveChallengeSendResponse(d);
+			}
+			// Try to send an invitation
+			System.out.println("Sending invitation");
+			d = plugin.sendInvitation(getPseudoRandom(123), CONNECTION_TIMEOUT);
+			if(d == null) {
+				System.out.println("Connection failed");
+				return;
+			} else {
+				System.out.println("Connection created");
+				receiveChallengeSendResponse(d);
+			}
+			// Try to accept an invitation
+			System.out.println("Accepting invitation");
+			d = plugin.acceptInvitation(getPseudoRandom(456),
+					CONNECTION_TIMEOUT);
+			if(d == null) {
+				System.out.println("Connection failed");
+				return;
+			} else {
+				System.out.println("Connection created");
+				sendChallengeReceiveResponse(d);
+			}
+		} finally {
+			// Stop the plugin
+			System.out.println("Stopping plugin");
+			plugin.stop();
 		}
-		// Stop the plugin
-		System.out.println("Stopping plugin");
-		plugin.stop();
 	}
 
 	protected static class ClientCallback implements DuplexPluginCallback {
diff --git a/briar-tests/src/net/sf/briar/plugins/DuplexServerTest.java b/briar-tests/src/net/sf/briar/plugins/DuplexServerTest.java
index 956eea2d33e09f207d9a57db16ad082a42f0db8a..d9d75c79868138ae96effb419bc95e1700071f7b 100644
--- a/briar-tests/src/net/sf/briar/plugins/DuplexServerTest.java
+++ b/briar-tests/src/net/sf/briar/plugins/DuplexServerTest.java
@@ -1,5 +1,8 @@
 package net.sf.briar.plugins;
 
+import static java.util.concurrent.TimeUnit.MILLISECONDS;
+import static net.sf.briar.api.plugins.InvitationConstants.CONNECTION_TIMEOUT;
+
 import java.util.Map;
 import java.util.concurrent.CountDownLatch;
 
@@ -18,32 +21,43 @@ public abstract class DuplexServerTest extends DuplexTest {
 		assert plugin != null;
 		// Start the plugin
 		System.out.println("Starting plugin");
-		plugin.start();
-		// Wait for a connection
-		System.out.println("Waiting for connection");
-		callback.latch.await();
-		// Try to accept an invitation
-		System.out.println("Accepting invitation");
-		DuplexTransportConnection d = plugin.acceptInvitation(
-				getPseudoRandom(123), INVITATION_TIMEOUT);
-		if(d == null) {
-			System.out.println("Connection failed");
-		} else {
-			System.out.println("Connection created");
-			sendChallengeReceiveResponse(d);
+		if(!plugin.start()) {
+			System.out.println("Plugin failed to start");
+			return;
 		}
-		// Try to send an invitation
-		System.out.println("Sending invitation");
-		d = plugin.sendInvitation(getPseudoRandom(456), INVITATION_TIMEOUT);
-		if(d == null) {
-			System.out.println("Connection failed");
-		} else {
-			System.out.println("Connection created");
-			receiveChallengeSendResponse(d);
+		try {
+			// Wait for a connection
+			System.out.println("Waiting for connection");
+			if(!callback.latch.await(CONNECTION_TIMEOUT, MILLISECONDS)) {
+				System.out.println("No connection received");
+				return;
+			}
+			// Try to accept an invitation
+			System.out.println("Accepting invitation");
+			DuplexTransportConnection d = plugin.acceptInvitation(
+					getPseudoRandom(123), CONNECTION_TIMEOUT);
+			if(d == null) {
+				System.out.println("Connection failed");
+				return;
+			} else {
+				System.out.println("Connection created");
+				sendChallengeReceiveResponse(d);
+			}
+			// Try to send an invitation
+			System.out.println("Sending invitation");
+			d = plugin.sendInvitation(getPseudoRandom(456), CONNECTION_TIMEOUT);
+			if(d == null) {
+				System.out.println("Connection failed");
+				return;
+			} else {
+				System.out.println("Connection created");
+				receiveChallengeSendResponse(d);
+			}
+		} finally {
+			// Stop the plugin
+			System.out.println("Stopping plugin");
+			plugin.stop();
 		}
-		// Stop the plugin
-		System.out.println("Stopping plugin");
-		plugin.stop();
 	}
 
 	protected class ServerCallback implements DuplexPluginCallback {
diff --git a/briar-tests/src/net/sf/briar/plugins/DuplexTest.java b/briar-tests/src/net/sf/briar/plugins/DuplexTest.java
index 80eee278f0c05acd481197c8fde43e62c57e04de..66b5fdf27630e741e2612e2e684e877e470e64c2 100644
--- a/briar-tests/src/net/sf/briar/plugins/DuplexTest.java
+++ b/briar-tests/src/net/sf/briar/plugins/DuplexTest.java
@@ -14,7 +14,6 @@ abstract class DuplexTest {
 
 	protected static final String CHALLENGE = "Carrots!";
 	protected static final String RESPONSE = "Potatoes!";
-	protected static final long INVITATION_TIMEOUT = 30 * 1000;
 
 	protected final ContactId contactId = new ContactId(234);
 
@@ -78,21 +77,13 @@ abstract class DuplexTest {
 	}
 
 	protected PseudoRandom getPseudoRandom(int seed) {
-		return new TestPseudoRandom(seed);
-	}
-
-	private static class TestPseudoRandom implements PseudoRandom {
-
-		private final Random r;
-
-		private TestPseudoRandom(int seed) {
-			r = new Random(seed);
-		}
-
-		public byte[] nextBytes(int bytes) {
-			byte[] b = new byte[bytes];
-			r.nextBytes(b);
-			return b;
-		}
+		final Random random = new Random(seed);
+		return new PseudoRandom() {
+			public byte[] nextBytes(int bytes) {
+				byte[] b = new byte[bytes];
+				random.nextBytes(b);
+				return b;
+			}
+		};
 	}
 }
diff --git a/briar-tests/src/net/sf/briar/plugins/modem/ModemClientTest.java b/briar-tests/src/net/sf/briar/plugins/modem/ModemClientTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..d4daf1f8ba812635cd57572eaeba587370f0f272
--- /dev/null
+++ b/briar-tests/src/net/sf/briar/plugins/modem/ModemClientTest.java
@@ -0,0 +1,44 @@
+package net.sf.briar.plugins.modem;
+
+import java.util.Collections;
+import java.util.Map;
+import java.util.concurrent.Executor;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+
+import net.sf.briar.api.ContactId;
+import net.sf.briar.api.TransportConfig;
+import net.sf.briar.api.TransportProperties;
+import net.sf.briar.plugins.DuplexClientTest;
+
+//This is not a JUnit test - it has to be run manually while the server test
+//is running on another machine
+public class ModemClientTest extends DuplexClientTest {
+
+	private ModemClientTest(Executor executor, String number) {
+		// Store the server's phone number
+		TransportProperties p = new TransportProperties();
+		p.put("number", number);
+		Map<ContactId, TransportProperties> remote =
+				Collections.singletonMap(contactId, p);
+		// Create the plugin
+		callback = new ClientCallback(new TransportConfig(),
+				new TransportProperties(), remote);
+		plugin = new ModemPlugin(executor, new ModemFactoryImpl(executor),
+				callback, 0L);
+	}
+
+	public static void main(String[] args) throws Exception {
+		if(args.length != 1) {
+			System.err.println("Please specify the server's phone number");
+			System.exit(1);
+		}
+		ExecutorService executor = Executors.newCachedThreadPool();
+		try {
+			new ModemClientTest(executor, args[0]).run();
+		} finally {
+			executor.shutdown();
+		}
+	}
+
+}
diff --git a/briar-tests/src/net/sf/briar/plugins/modem/ModemServerTest.java b/briar-tests/src/net/sf/briar/plugins/modem/ModemServerTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..ca572de126358b2bb05f2f1fe19daa15cdcddaeb
--- /dev/null
+++ b/briar-tests/src/net/sf/briar/plugins/modem/ModemServerTest.java
@@ -0,0 +1,33 @@
+package net.sf.briar.plugins.modem;
+
+import java.util.Collections;
+import java.util.concurrent.Executor;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+
+import net.sf.briar.api.TransportConfig;
+import net.sf.briar.api.TransportProperties;
+import net.sf.briar.plugins.DuplexServerTest;
+
+//This is not a JUnit test - it has to be run manually while the client test
+//is running on another machine
+public class ModemServerTest extends DuplexServerTest {
+
+	private ModemServerTest(Executor executor) {
+		// Create the plugin
+		callback = new ServerCallback(new TransportConfig(),
+				new TransportProperties(), Collections.singletonMap(contactId,
+						new TransportProperties()));
+		plugin = new ModemPlugin(executor, new ModemFactoryImpl(executor),
+				callback, 0L);
+	}
+
+	public static void main(String[] args) throws Exception {
+		ExecutorService executor = Executors.newCachedThreadPool();
+		try {
+			new ModemServerTest(executor).run();
+		} finally {
+			executor.shutdown();
+		}
+	}
+}