Added emulation for FilterInputStream and FilterOutputStream

Added emulation for the following Java IO classes based
on the Android libcode implementation:

 - FilterInputStream
 - FilterOutputStream

Added corresponding unit tests.

Change-Id: I563d9fa3d4397457dff63ca8974fd93cf2541fe0
Review-Link: https://gwt-review.googlesource.com/#/c/13450/
diff --git a/user/super/com/google/gwt/emul/java/io/ByteArrayInputStream.java b/user/super/com/google/gwt/emul/java/io/ByteArrayInputStream.java
index 4093b38..6bdebc0 100644
--- a/user/super/com/google/gwt/emul/java/io/ByteArrayInputStream.java
+++ b/user/super/com/google/gwt/emul/java/io/ByteArrayInputStream.java
@@ -85,7 +85,7 @@
      * @return {@code count - pos}
      */
     @Override
-    public synchronized int available() {
+    public int available() {
         return count - pos;
     }
 
@@ -111,7 +111,7 @@
      * @see #reset()
      */
     @Override
-    public synchronized void mark(int readlimit) {
+    public void mark(int readlimit) {
         mark = pos;
     }
 
@@ -137,11 +137,11 @@
      * @return the byte read or -1 if the end of this stream has been reached.
      */
     @Override
-    public synchronized int read() {
+    public int read() {
         return pos < count ? buf[pos++] & 0xFF : -1;
     }
 
-    @Override public synchronized int read(byte[] buffer, int byteOffset, int byteCount) {
+    @Override public int read(byte[] buffer, int byteOffset, int byteCount) {
         IOUtils.checkOffsetAndCount(buffer, byteOffset, byteCount);
 
         // Are there any bytes available?
@@ -166,7 +166,7 @@
      * @see #mark(int)
      */
     @Override
-    public synchronized void reset() {
+    public void reset() {
         pos = mark;
     }
 
@@ -179,7 +179,7 @@
      * @return the number of bytes actually skipped.
      */
     @Override
-    public synchronized long skip(long byteCount) {
+    public long skip(long byteCount) {
         if (byteCount <= 0) {
             return 0;
         }
diff --git a/user/super/com/google/gwt/emul/java/io/FilterInputStream.java b/user/super/com/google/gwt/emul/java/io/FilterInputStream.java
new file mode 100644
index 0000000..172a67c
--- /dev/null
+++ b/user/super/com/google/gwt/emul/java/io/FilterInputStream.java
@@ -0,0 +1,156 @@
+// CHECKSTYLE_OFF: Copyrighted to the Android Open Source Project.
+/*
+ *  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.
+ */
+// CHECKSTYLE_ON
+
+package java.io;
+
+/**
+ * Wraps an existing {@link InputStream} and performs some transformation on
+ * the input data while it is being read. Transformations can be anything from a
+ * simple byte-wise filtering input data to an on-the-fly compression or
+ * decompression of the underlying stream. Input streams that wrap another input
+ * stream and provide some additional functionality on top of it usually inherit
+ * from this class.
+ *
+ * @see FilterOutputStream
+ */
+public class FilterInputStream extends InputStream {
+
+    /**
+     * The source input stream that is filtered.
+     */
+    protected volatile InputStream in;
+
+    /**
+     * Constructs a new {@code FilterInputStream} with the specified input
+     * stream as source.
+     *
+     * <p><strong>Warning:</strong> passing a null source creates an invalid
+     * {@code FilterInputStream}, that fails on every method that is not
+     * overridden. Subclasses should check for null in their constructors.
+     *
+     * @param in the input stream to filter reads on.
+     */
+    protected FilterInputStream(InputStream in) {
+        this.in = in;
+    }
+
+    @Override
+    public int available() throws IOException {
+        return in.available();
+    }
+
+    /**
+     * Closes this stream. This implementation closes the filtered stream.
+     *
+     * @throws IOException
+     *             if an error occurs while closing this stream.
+     */
+    @Override
+    public void close() throws IOException {
+        in.close();
+    }
+
+    /**
+     * Sets a mark position in this stream. The parameter {@code readlimit}
+     * indicates how many bytes can be read before the mark is invalidated.
+     * Sending {@code reset()} will reposition this stream back to the marked
+     * position, provided that {@code readlimit} has not been surpassed.
+     * <p>
+     * This implementation sets a mark in the filtered stream.
+     *
+     * @param readlimit
+     *            the number of bytes that can be read from this stream before
+     *            the mark is invalidated.
+     * @see #markSupported()
+     * @see #reset()
+     */
+    @Override
+    public void mark(int readlimit) {
+        in.mark(readlimit);
+    }
+
+    /**
+     * Indicates whether this stream supports {@code mark()} and {@code reset()}.
+     * This implementation returns whether or not the filtered stream supports
+     * marking.
+     *
+     * @return {@code true} if {@code mark()} and {@code reset()} are supported,
+     *         {@code false} otherwise.
+     * @see #mark(int)
+     * @see #reset()
+     * @see #skip(long)
+     */
+    @Override
+    public boolean markSupported() {
+        return in.markSupported();
+    }
+
+    /**
+     * Reads a single byte from the filtered stream and returns it as an integer
+     * in the range from 0 to 255. Returns -1 if the end of this stream has been
+     * reached.
+     *
+     * @return the byte read or -1 if the end of the filtered stream has been
+     *         reached.
+     * @throws IOException
+     *             if the stream is closed or another IOException occurs.
+     */
+    @Override
+    public int read() throws IOException {
+        return in.read();
+    }
+
+    @Override
+    public int read(byte[] buffer, int byteOffset, int byteCount) throws IOException {
+        return in.read(buffer, byteOffset, byteCount);
+    }
+
+    /**
+     * Resets this stream to the last marked location. This implementation
+     * resets the target stream.
+     *
+     * @throws IOException
+     *             if this stream is already closed, no mark has been set or the
+     *             mark is no longer valid because more than {@code readlimit}
+     *             bytes have been read since setting the mark.
+     * @see #mark(int)
+     * @see #markSupported()
+     */
+    @Override
+    public void reset() throws IOException {
+        in.reset();
+    }
+
+    /**
+     * Skips {@code byteCount} bytes in this stream. Subsequent
+     * calls to {@code read} will not return these bytes unless {@code reset} is
+     * used. This implementation skips {@code byteCount} bytes in the
+     * filtered stream.
+     *
+     * @return the number of bytes actually skipped.
+     * @throws IOException
+     *             if this stream is closed or another IOException occurs.
+     * @see #mark(int)
+     * @see #reset()
+     */
+    @Override
+    public long skip(long byteCount) throws IOException {
+        return in.skip(byteCount);
+    }
+}
diff --git a/user/super/com/google/gwt/emul/java/io/FilterOutputStream.java b/user/super/com/google/gwt/emul/java/io/FilterOutputStream.java
index e185e60..86008c0 100644
--- a/user/super/com/google/gwt/emul/java/io/FilterOutputStream.java
+++ b/user/super/com/google/gwt/emul/java/io/FilterOutputStream.java
@@ -1,29 +1,130 @@
+// CHECKSTYLE_OFF: Copyrighted to the Android Open Source Project.
 /*
- * Copyright 2006 Google Inc.
- * 
- * Licensed 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.
+ *  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.
  */
+// CHECKSTYLE_ON
+
 package java.io;
 
 /**
- * @skip
+ * Wraps an existing {@link OutputStream} and performs some transformation on
+ * the output data while it is being written. Transformations can be anything
+ * from a simple byte-wise filtering output data to an on-the-fly compression or
+ * decompression of the underlying stream. Output streams that wrap another
+ * output stream and provide some additional functionality on top of it usually
+ * inherit from this class.
+ *
+ * @see FilterOutputStream
  */
 public class FilterOutputStream extends OutputStream {
 
-  public FilterOutputStream(OutputStream out) {
-  }
+    /**
+     * The target output stream for this filter stream.
+     */
+    protected OutputStream out;
 
-  @Override
-  public void write(int oneByte) throws IOException {
-  }
+    /**
+     * Constructs a new {@code FilterOutputStream} with {@code out} as its
+     * target stream.
+     *
+     * @param out
+     *            the target stream that this stream writes to.
+     */
+    public FilterOutputStream(OutputStream out) {
+        this.out = out;
+    }
+
+    /**
+     * Closes this stream. This implementation closes the target stream.
+     *
+     * @throws IOException
+     *             if an error occurs attempting to close this stream.
+     */
+    @Override
+    public void close() throws IOException {
+        Throwable thrown = null;
+        try {
+            flush();
+        } catch (Throwable e) {
+            thrown = e;
+        }
+
+        try {
+            out.close();
+        } catch (Throwable e) {
+            if (thrown == null) {
+                thrown = e;
+            }
+        }
+
+        if (thrown != null) {
+            throw new IOException(thrown);
+        }
+    }
+
+    /**
+     * Ensures that all pending data is sent out to the target stream. This
+     * implementation flushes the target stream.
+     *
+     * @throws IOException
+     *             if an error occurs attempting to flush this stream.
+     */
+    @Override
+    public void flush() throws IOException {
+        out.flush();
+    }
+
+    /**
+     * Writes {@code count} bytes from the byte array {@code buffer} starting at
+     * {@code offset} to the target stream.
+     *
+     * @param buffer
+     *            the buffer to write.
+     * @param offset
+     *            the index of the first byte in {@code buffer} to write.
+     * @param length
+     *            the number of bytes in {@code buffer} to write.
+     * @throws IndexOutOfBoundsException
+     *             if {@code offset < 0} or {@code count < 0}, or if
+     *             {@code offset + count} is bigger than the length of
+     *             {@code buffer}.
+     * @throws IOException
+     *             if an I/O error occurs while writing to this stream.
+     */
+    @Override
+    public void write(byte[] buffer, int offset, int length) throws IOException {
+        IOUtils.checkOffsetAndCount(buffer, offset, length);
+        for (int i = 0; i < length; i++) {
+            // Call write() instead of out.write() since subclasses could
+            // override the write() method.
+            write(buffer[offset + i]);
+        }
+    }
+
+    /**
+     * Writes one byte to the target stream. Only the low order byte of the
+     * integer {@code oneByte} is written.
+     *
+     * @param oneByte
+     *            the byte to be written.
+     * @throws IOException
+     *             if an I/O error occurs while writing to this stream.
+     */
+    @Override
+    public void write(int oneByte) throws IOException {
+        out.write(oneByte);
+    }
 }
diff --git a/user/super/com/google/gwt/emul/java/io/InputStream.java b/user/super/com/google/gwt/emul/java/io/InputStream.java
index 6979c52..5d25c9e 100644
--- a/user/super/com/google/gwt/emul/java/io/InputStream.java
+++ b/user/super/com/google/gwt/emul/java/io/InputStream.java
@@ -215,7 +215,7 @@
      * @throws IOException
      *             if this stream is closed or another IOException occurs.
      */
-    public synchronized void reset() throws IOException {
+    public void reset() throws IOException {
         throw new IOException();
     }
 
diff --git a/user/test/com/google/gwt/emultest/EmulSuite.java b/user/test/com/google/gwt/emultest/EmulSuite.java
index 9a59baa..1e9e2f0 100644
--- a/user/test/com/google/gwt/emultest/EmulSuite.java
+++ b/user/test/com/google/gwt/emultest/EmulSuite.java
@@ -18,6 +18,8 @@
 import com.google.gwt.emultest.java.internal.CoercionsTest;
 import com.google.gwt.emultest.java.io.ByteArrayInputStreamTest;
 import com.google.gwt.emultest.java.io.ByteArrayOutputStreamTest;
+import com.google.gwt.emultest.java.io.FilterInputStreamTest;
+import com.google.gwt.emultest.java.io.FilterOutputStreamTest;
 import com.google.gwt.emultest.java.io.InputStreamTest;
 import com.google.gwt.emultest.java.io.OutputStreamTest;
 import com.google.gwt.emultest.java.lang.BooleanTest;
@@ -64,6 +66,8 @@
     //-- java.io
     suite.addTestSuite(ByteArrayInputStreamTest.class);
     suite.addTestSuite(ByteArrayOutputStreamTest.class);
+    suite.addTestSuite(FilterInputStreamTest.class);
+    suite.addTestSuite(FilterOutputStreamTest.class);
     suite.addTestSuite(InputStreamTest.class);
     suite.addTestSuite(OutputStreamTest.class);
     //-- java.lang
diff --git a/user/test/com/google/gwt/emultest/java/io/FilterInputStreamTest.java b/user/test/com/google/gwt/emultest/java/io/FilterInputStreamTest.java
new file mode 100644
index 0000000..a71cf81
--- /dev/null
+++ b/user/test/com/google/gwt/emultest/java/io/FilterInputStreamTest.java
@@ -0,0 +1,273 @@
+/*
+ * Copyright 2015 Google Inc.
+ *
+ * Licensed 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 com.google.gwt.emultest.java.io;
+
+import com.google.gwt.junit.client.GWTTestCase;
+
+import java.io.FilterInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+
+/**
+ * Unit test for the {@link java.io.FilterInputStream} emulated class.
+ */
+public class FilterInputStreamTest extends GWTTestCase {
+
+  /**
+   * Mock for {@link InputStream}.
+   */
+  private static class MockInputStream extends InputStream {
+    public static final int RETURNED_VALUE_FOR_AVAILABLE = 100;
+    public static final int RETURNED_VALUE_FOR_READ_BYTE = 150;
+    public static final int RETURNED_VALUE_FOR_READ_BYTES = 200;
+    public static final boolean RETURNED_VALUE_FOR_MARK_SUPPORTED = true;
+    public static final long RETURNED_VALUE_FOR_SKIP = 250;
+
+    // Flags for knowing whether or not the underlying stream methods have been called.
+    private boolean availableCalled;
+    private boolean closeCalled;
+    private boolean markCalled;
+    private boolean markSupportedCalled;
+    private boolean readByteCalled;
+    private boolean readBytesCalled;
+    private boolean resetCalled;
+    private boolean skipCalled;
+
+    // Input parameters to ensure the filter passes the right arguments to the stream.
+    private int requestedReadLimit;
+    private long requestedSkipBytes;
+    private byte[] requestedReadBuffer;
+    private int requestedReadOffset;
+    private int requestedReadLength;
+
+    public MockInputStream() {
+      availableCalled = false;
+      closeCalled = false;
+      markCalled = false;
+      markSupportedCalled = false;
+      readByteCalled = false;
+      readBytesCalled = false;
+      resetCalled = false;
+      skipCalled = false;
+
+      requestedReadLimit = 0;
+      requestedSkipBytes = 0L;
+      requestedReadBuffer = null;
+      requestedReadOffset = 0;
+      requestedReadLength = 0;
+    }
+
+    @Override
+    public int available() {
+      availableCalled = true;
+      return RETURNED_VALUE_FOR_AVAILABLE;
+    }
+
+    @Override
+    public void close() {
+      closeCalled = true;
+    }
+
+    @Override
+    public void mark(int readLimit) {
+      markCalled = true;
+      requestedReadLimit = readLimit;
+    }
+
+    @Override
+    public boolean markSupported() {
+      markSupportedCalled = true;
+      return RETURNED_VALUE_FOR_MARK_SUPPORTED;
+    }
+
+    @Override
+    public int read() {
+      readByteCalled = true;
+      return RETURNED_VALUE_FOR_READ_BYTE;
+    }
+
+    @Override
+    public int read(byte[] b, int off, int len) {
+      readBytesCalled = true;
+      requestedReadBuffer = b;
+      requestedReadOffset = off;
+      requestedReadLength = len;
+      return RETURNED_VALUE_FOR_READ_BYTES;
+    }
+
+    @Override
+    public void reset() {
+      resetCalled = true;
+    }
+
+    @Override
+    public long skip(long n) {
+      skipCalled = true;
+      requestedSkipBytes = n;
+      return RETURNED_VALUE_FOR_SKIP;
+    }
+
+    public boolean getAvailableCalled() {
+      return availableCalled;
+    }
+
+    public boolean getCloseCalled() {
+      return closeCalled;
+    }
+
+    public boolean getMarkCalled() {
+      return markCalled;
+    }
+
+    public int getMarkReadLimit() {
+      return requestedReadLimit;
+    }
+
+    public boolean getMarkSupportedCalled() {
+      return markSupportedCalled;
+    }
+
+    public boolean getReadByteCalled() {
+      return readByteCalled;
+    }
+
+    public boolean getReadBytesCalled() {
+      return readBytesCalled;
+    }
+
+    public boolean getResetCalled() {
+      return resetCalled;
+    }
+
+    public boolean getSkipCalled() {
+      return skipCalled;
+    }
+
+    public int getRequestedReadLimit() {
+      return requestedReadLimit;
+    }
+
+    public long getRequestedSkipBytes() {
+      return requestedSkipBytes;
+    }
+
+    public byte[] getRequestedReadBuffer() {
+      return requestedReadBuffer;
+    }
+
+    public int getRequestedReadOffset() {
+      return requestedReadOffset;
+    }
+
+    public int getRequestedReadLength() {
+      return requestedReadLength;
+    }
+  }
+
+  /**
+   * The constructor of {@link java.io.FilterInputStream} is protected. This class provides a public
+   * constructor so that it can be instantiated.
+   */
+  private static class InstantiableFilterInputStream extends FilterInputStream {
+    public InstantiableFilterInputStream(InputStream inputStream) {
+      super(inputStream);
+    }
+  }
+
+  private FilterInputStream filter;
+
+  protected MockInputStream mockInputStream;
+
+  /**
+   * Sets module name so that javascript compiler can operate.
+   */
+  @Override
+  public String getModuleName() {
+    return "com.google.gwt.emultest.EmulSuite";
+  }
+
+  @Override
+  protected void gwtSetUp() throws Exception {
+    super.gwtSetUp();
+    mockInputStream = new MockInputStream();
+    filter = new InstantiableFilterInputStream(mockInputStream);
+  }
+
+  public void testAvailable() throws IOException {
+    int available = filter.available();
+    assertTrue(mockInputStream.getAvailableCalled());
+    assertEquals(available, MockInputStream.RETURNED_VALUE_FOR_AVAILABLE);
+  }
+
+  public void testClose() throws IOException {
+    filter.close();
+    assertTrue(mockInputStream.getCloseCalled());
+  }
+
+  public void testMark() {
+    int readLimit = 12;
+    filter.mark(readLimit);
+    assertTrue(mockInputStream.getMarkCalled());
+    assertEquals(readLimit, mockInputStream.getMarkReadLimit());
+  }
+
+  public void testMarkSupported() {
+    boolean markSupported = filter.markSupported();
+    assertTrue(mockInputStream.getMarkSupportedCalled());
+    assertEquals(markSupported, MockInputStream.RETURNED_VALUE_FOR_MARK_SUPPORTED);
+  }
+
+  public void testReadValue() throws IOException {
+    int value = filter.read();
+    assertTrue(mockInputStream.getReadByteCalled());
+    assertEquals(value, MockInputStream.RETURNED_VALUE_FOR_READ_BYTE);
+  }
+
+  public void testReadArray() throws IOException {
+    byte[] b = new byte[500];
+    int bytesRead = filter.read(b);
+    assertTrue(mockInputStream.getReadBytesCalled());
+    assertEquals(b, mockInputStream.getRequestedReadBuffer());
+    assertEquals(0, mockInputStream.getRequestedReadOffset());
+    assertEquals(b.length, mockInputStream.getRequestedReadLength());
+    assertEquals(bytesRead, MockInputStream.RETURNED_VALUE_FOR_READ_BYTES);
+  }
+
+  public void testReadArrayRange() throws IOException {
+    byte[] b = new byte[500];
+    int offset = 100;
+    int length = 300;
+    int bytesRead = filter.read(b, offset, length);
+    assertTrue(mockInputStream.getReadBytesCalled());
+    assertEquals(b, mockInputStream.getRequestedReadBuffer());
+    assertEquals(offset, mockInputStream.getRequestedReadOffset());
+    assertEquals(length, mockInputStream.getRequestedReadLength());
+    assertEquals(bytesRead, MockInputStream.RETURNED_VALUE_FOR_READ_BYTES);
+  }
+
+  public void testReset() throws IOException {
+    filter.reset();
+    assertTrue(mockInputStream.getResetCalled());
+  }
+
+  public void testSkip() throws IOException {
+    long bytesToSkip = MockInputStream.RETURNED_VALUE_FOR_SKIP * 3;
+    long skippedBytes = filter.skip(bytesToSkip);
+    assertTrue(mockInputStream.getSkipCalled());
+    assertEquals(bytesToSkip, mockInputStream.getRequestedSkipBytes());
+    assertEquals(skippedBytes, mockInputStream.RETURNED_VALUE_FOR_SKIP);
+  }
+}
diff --git a/user/test/com/google/gwt/emultest/java/io/FilterOutputStreamTest.java b/user/test/com/google/gwt/emultest/java/io/FilterOutputStreamTest.java
new file mode 100644
index 0000000..ff9c9bf
--- /dev/null
+++ b/user/test/com/google/gwt/emultest/java/io/FilterOutputStreamTest.java
@@ -0,0 +1,149 @@
+/*
+ * Copyright 2015 Google Inc.
+ *
+ * Licensed 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 com.google.gwt.emultest.java.io;
+
+import com.google.gwt.junit.client.GWTTestCase;
+
+import java.io.FilterOutputStream;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.util.Arrays;
+import java.util.LinkedList;
+
+/**
+ * Unit test class for the {@link java.io.FilterOutputStream} emulated class.
+ */
+public class FilterOutputStreamTest extends GWTTestCase {
+
+  private static final byte[] BYTES_TO_WRITE = {
+      (byte) 0x10, (byte) 0x20, (byte) 0x30, (byte) 0x40, (byte) 0x50, (byte) 0x60
+  };
+
+  /**
+   * Mock for {@link OutputStream}.
+   */
+  private static class MockOutputStream extends OutputStream {
+
+    // Flags for knowing whether or not the underlying stream methods have been called.
+    private boolean closeCalled;
+    private boolean flushCalled;
+    private boolean writeByteCalled;
+
+    /**
+     * Stores all the values written with {@code write(int b)}.
+     */
+    private LinkedList<Byte> writtenBytes;
+
+    private MockOutputStream() {
+      closeCalled = false;
+      flushCalled = false;
+      writeByteCalled = false;
+
+      writtenBytes = new LinkedList<>();
+    }
+
+    @Override
+    public void close() {
+      closeCalled = true;
+    }
+
+    @Override
+    public void flush() {
+      flushCalled = true;
+    }
+
+    @Override
+    public void write(int b) {
+      writeByteCalled = true;
+      writtenBytes.add((byte) b);
+    }
+
+    public boolean getCloseCalled() {
+      return closeCalled;
+    }
+
+    public boolean getFlushCalled() {
+      return flushCalled;
+    }
+
+    public boolean getWriteByteCalled() {
+      return writeByteCalled;
+    }
+
+    public int getLastRequestedByteToWrite() {
+      return (int) writtenBytes.get(writtenBytes.size() - 1);
+    }
+
+    public byte[] getWrittenValues() {
+      int i = 0;
+      byte[] result = new byte[writtenBytes.size()];
+      for (Byte b : writtenBytes) {
+        result[i++] = b.byteValue();
+      }
+      return result;
+    }
+  }
+
+  private FilterOutputStream filter;
+
+  private MockOutputStream mockOutputStream;
+
+  /**
+   * Sets module name so that javascript compiler can operate.
+   */
+  @Override
+  public String getModuleName() {
+    return "com.google.gwt.emultest.EmulSuite";
+  }
+
+  @Override
+  public void gwtSetUp() throws Exception {
+    super.gwtSetUp();
+    mockOutputStream = new MockOutputStream();
+    filter = new FilterOutputStream(mockOutputStream);
+  }
+
+  public void testClose() throws IOException {
+    filter.close();
+    assertTrue(mockOutputStream.getCloseCalled());
+  }
+
+  public void testFlush() throws IOException {
+    filter.flush();
+    assertTrue(mockOutputStream.getFlushCalled());
+  }
+
+  public void testWriteValue() throws IOException {
+    int value = 12;
+    filter.write(value);
+    assertTrue(mockOutputStream.getWriteByteCalled());
+    assertEquals(value, mockOutputStream.getLastRequestedByteToWrite());
+  }
+
+  public void testWriteArray() throws IOException {
+    filter.write(BYTES_TO_WRITE);
+    assertTrue(Arrays.equals(BYTES_TO_WRITE, mockOutputStream.getWrittenValues()));
+  }
+
+  public void testWriteArrayRange() throws IOException {
+    int offset = 1;
+    int length = 2;
+    byte[] expectedWrittenValues = new byte[length];
+    System.arraycopy(BYTES_TO_WRITE, offset, expectedWrittenValues, 0, length);
+    filter.write(BYTES_TO_WRITE, offset, length);
+    assertTrue(Arrays.equals(expectedWrittenValues, mockOutputStream.getWrittenValues()));
+  }
+}