diff --git a/components/net/sf/briar/transport/ConnectionWriterImpl.java b/components/net/sf/briar/transport/ConnectionWriterImpl.java
index 495d60a0937a1016e4f17ddcb416e8b4fbf82140..c4b52cfe4d1f93d48458c2d48b5fdb815a262098 100644
--- a/components/net/sf/briar/transport/ConnectionWriterImpl.java
+++ b/components/net/sf/briar/transport/ConnectionWriterImpl.java
@@ -2,7 +2,6 @@ package net.sf.briar.transport;
 
 import static net.sf.briar.api.transport.TransportConstants.HEADER_LENGTH;
 import static net.sf.briar.api.transport.TransportConstants.MAC_LENGTH;
-import static net.sf.briar.util.ByteUtils.MAX_32_BIT_UNSIGNED;
 
 import java.io.IOException;
 import java.io.OutputStream;
@@ -22,7 +21,6 @@ class ConnectionWriterImpl extends OutputStream implements ConnectionWriter {
 	private final int frameLength;
 
 	private int length = 0;
-	private long frameNumber = 0L;
 
 	ConnectionWriterImpl(FrameWriter out, int frameLength) {
 		this.out = out;
@@ -80,9 +78,7 @@ class ConnectionWriterImpl extends OutputStream implements ConnectionWriter {
 	}
 
 	private void writeFrame(boolean finalFrame) throws IOException {
-		if(frameNumber > MAX_32_BIT_UNSIGNED) throw new IllegalStateException();
 		out.writeFrame(frame, length, finalFrame);
 		length = 0;
-		frameNumber++;
 	}
 }
diff --git a/components/net/sf/briar/transport/OutgoingEncryptionLayer.java b/components/net/sf/briar/transport/OutgoingEncryptionLayer.java
index 5e8ec05db996ca1ed0e737bcb4abdb9907664ebf..d691bd39bccccf836674835964af43c97162d2bd 100644
--- a/components/net/sf/briar/transport/OutgoingEncryptionLayer.java
+++ b/components/net/sf/briar/transport/OutgoingEncryptionLayer.java
@@ -69,6 +69,7 @@ class OutgoingEncryptionLayer implements FrameWriter {
 
 	public void writeFrame(byte[] frame, int payloadLength, boolean finalFrame)
 			throws IOException {
+		if(frameNumber > MAX_32_BIT_UNSIGNED) throw new IllegalStateException();
 		// If the initiator's side of the connection is closed without writing
 		// any data, don't write anything to the underlying transport
 		if(writeTag && finalFrame && payloadLength == 0) return;
diff --git a/test/net/sf/briar/transport/ConnectionReaderImplTest.java b/test/net/sf/briar/transport/ConnectionReaderImplTest.java
index f00b611ba90d7263ce420b6a55c81c963a68aaca..4bec12fc63964be0720a2a6dfcab4fed5439c2ca 100644
--- a/test/net/sf/briar/transport/ConnectionReaderImplTest.java
+++ b/test/net/sf/briar/transport/ConnectionReaderImplTest.java
@@ -33,6 +33,7 @@ public class ConnectionReaderImplTest extends BriarTestCase {
 		assertEquals(0, c.read()); // Read another byte
 		assertEquals(-1, c.read()); // Skip the second empty frame, reach EOF
 		assertEquals(-1, c.read()); // Still at EOF
+		context.assertIsSatisfied();
 	}
 
 	@Test
@@ -57,6 +58,7 @@ public class ConnectionReaderImplTest extends BriarTestCase {
 		assertEquals(-1, c.read(buf));
 		// Still at EOF
 		assertEquals(-1, c.read(buf));
+		context.assertIsSatisfied();
 	}
 
 	@Test
@@ -77,6 +79,7 @@ public class ConnectionReaderImplTest extends BriarTestCase {
 		assertEquals(MAX_PAYLOAD_LENGTH / 2, c.read(buf));
 		// Reach EOF
 		assertEquals(-1, c.read(buf, 0, buf.length));
+		context.assertIsSatisfied();
 	}
 
 	@Test
@@ -99,5 +102,6 @@ public class ConnectionReaderImplTest extends BriarTestCase {
 				MAX_PAYLOAD_LENGTH / 2));
 		// Reach EOF
 		assertEquals(-1, c.read(buf, 0, buf.length));
+		context.assertIsSatisfied();
 	}
 }
diff --git a/test/net/sf/briar/transport/ConnectionWriterImplTest.java b/test/net/sf/briar/transport/ConnectionWriterImplTest.java
index 21ac3c7e1b048e69ef256f676aef017484ee012a..c0c7437816fb9d5aa24300777b1bb36189107727 100644
--- a/test/net/sf/briar/transport/ConnectionWriterImplTest.java
+++ b/test/net/sf/briar/transport/ConnectionWriterImplTest.java
@@ -1,13 +1,123 @@
 package net.sf.briar.transport;
 
-import org.junit.Test;
-
+import static net.sf.briar.api.transport.TransportConstants.HEADER_LENGTH;
+import static net.sf.briar.api.transport.TransportConstants.MAC_LENGTH;
 import net.sf.briar.BriarTestCase;
 
+import org.jmock.Expectations;
+import org.jmock.Mockery;
+import org.junit.Test;
+
 public class ConnectionWriterImplTest extends BriarTestCase {
 
-	// FIXME: This is an arbitrary change to test the synchronization view
+	private static final int FRAME_LENGTH = 1024;
+	private static final int MAX_PAYLOAD_LENGTH =
+			FRAME_LENGTH - HEADER_LENGTH - MAC_LENGTH;
+
+	@Test
+	public void testCloseWithoutWritingWritesFinalFrame() throws Exception {
+		Mockery context = new Mockery();
+		final FrameWriter writer = context.mock(FrameWriter.class);
+		context.checking(new Expectations() {{
+			// Write an empty final frame
+			oneOf(writer).writeFrame(with(any(byte[].class)), with(0),
+					with(true));
+			// Flush the stream
+			oneOf(writer).flush();
+		}});
+		ConnectionWriterImpl c = new ConnectionWriterImpl(writer, FRAME_LENGTH);
+		c.close();
+		context.assertIsSatisfied();
+	}
+
+	@Test
+	public void testFlushWithoutBufferedDataWritesFrame() throws Exception {
+		Mockery context = new Mockery();
+		final FrameWriter writer = context.mock(FrameWriter.class);
+		ConnectionWriterImpl c = new ConnectionWriterImpl(writer, FRAME_LENGTH);
+		context.checking(new Expectations() {{
+			// Flush the stream
+			oneOf(writer).flush();
+		}});
+		c.flush();
+		context.assertIsSatisfied();
+	}
+
+	@Test
+	public void testFlushWithBufferedDataWritesFrameAndFlushes()
+			throws Exception {
+		Mockery context = new Mockery();
+		final FrameWriter writer = context.mock(FrameWriter.class);
+		ConnectionWriterImpl c = new ConnectionWriterImpl(writer, FRAME_LENGTH);
+		context.checking(new Expectations() {{
+			// Write a non-final frame with one payload byte
+			oneOf(writer).writeFrame(with(any(byte[].class)), with(1),
+					with(false));
+			// Flush the stream
+			oneOf(writer).flush();
+		}});
+		c.write(0);
+		c.flush();
+		context.assertIsSatisfied();
+	}
+
+	@Test
+	public void testSingleByteWritesWriteFullFrame() throws Exception {
+		Mockery context = new Mockery();
+		final FrameWriter writer = context.mock(FrameWriter.class);
+		ConnectionWriterImpl c = new ConnectionWriterImpl(writer, FRAME_LENGTH);
+		context.checking(new Expectations() {{
+			// Write a full non-final frame
+			oneOf(writer).writeFrame(with(any(byte[].class)),
+					with(MAX_PAYLOAD_LENGTH), with(false));
+		}});
+		for(int i = 0; i < MAX_PAYLOAD_LENGTH; i++) {
+			c.write(0);
+		}
+		context.assertIsSatisfied();
+	}
+
+	@Test
+	public void testMultiByteWritesWriteFullFrames() throws Exception {
+		Mockery context = new Mockery();
+		final FrameWriter writer = context.mock(FrameWriter.class);
+		ConnectionWriterImpl c = new ConnectionWriterImpl(writer, FRAME_LENGTH);
+		context.checking(new Expectations() {{
+			// Write two full non-final frames
+			exactly(2).of(writer).writeFrame(with(any(byte[].class)),
+					with(MAX_PAYLOAD_LENGTH), with(false));
+		}});
+		// Sanity check
+		assertEquals(0, MAX_PAYLOAD_LENGTH % 2);
+		// Write two full payloads using four multi-byte writes
+		byte[] b = new byte[MAX_PAYLOAD_LENGTH / 2];
+		c.write(b);
+		c.write(b);
+		c.write(b);
+		c.write(b);
+		context.assertIsSatisfied();
+	}
 
 	@Test
-	public void testNothing() {}
+	public void testLargeMultiByteWriteWritesFullFrames() throws Exception {
+		Mockery context = new Mockery();
+		final FrameWriter writer = context.mock(FrameWriter.class);
+		ConnectionWriterImpl c = new ConnectionWriterImpl(writer, FRAME_LENGTH);
+		context.checking(new Expectations() {{
+			// Write two full non-final frames
+			exactly(2).of(writer).writeFrame(with(any(byte[].class)),
+					with(MAX_PAYLOAD_LENGTH), with(false));
+			// Write a final frame with a one-byte payload
+			oneOf(writer).writeFrame(with(any(byte[].class)), with(1),
+					with(true));
+			// Flush the stream
+			oneOf(writer).flush();
+		}});
+		// Write two full payloads using one large multi-byte write
+		byte[] b = new byte[MAX_PAYLOAD_LENGTH * 2 + 1];
+		c.write(b);
+		// There should be one byte left in the buffer
+		c.close();
+		context.assertIsSatisfied();
+	}
 }