diff --git a/core/src/test/java/com/threerings/io/FrameTest.java b/core/src/test/java/com/threerings/io/FrameTest.java index bde4fb2b2..b3ce5b8ec 100644 --- a/core/src/test/java/com/threerings/io/FrameTest.java +++ b/core/src/test/java/com/threerings/io/FrameTest.java @@ -24,22 +24,19 @@ package com.threerings.io; import java.io.DataInputStream; import java.io.DataOutputStream; import java.io.IOException; +import java.util.*; +import java.nio.ByteBuffer; import java.nio.channels.Pipe; import java.nio.channels.ReadableByteChannel; import java.nio.channels.WritableByteChannel; import java.nio.channels.spi.SelectorProvider; -import junit.framework.Test; -import junit.framework.TestCase; +import org.junit.Test; +import static org.junit.Assert.*; -public class FrameTest extends TestCase +public class FrameTest { - public FrameTest () - { - super(FrameTest.class.getName()); - } - public void writeFrames (WritableByteChannel out) throws IOException { @@ -90,28 +87,142 @@ public class FrameTest extends TestCase assertTrue("hit eof", fin.read() == -1); } - @Override - public void runTest () + // @Test + public void testReadWriteFrames () throws IOException { - try { - Pipe pipe = SelectorProvider.provider().openPipe(); - writeFrames(pipe.sink()); - readFrames(pipe.source()); + Pipe pipe = SelectorProvider.provider().openPipe(); + writeFrames(pipe.sink()); + readFrames(pipe.source()); + } - } catch (IOException ioe) { - ioe.printStackTrace(System.err); + @Test + public void testInvariantFailure () throws Exception { + final int[] FRAME_COUNTS = new int[] { + 14349, 14054, 15574, 575, 15497, 5417, 3732, 13746, 3043, 3829, 15862, 2285, 4242, + 4903, 16361, 14213, 7981, 4591, 12263, 16268, 15296, 11822, 2713, 4353, 15013, 9810, + 3015, 189, 7988, 6347, 5859, 13433, 3551, 13795, 5164, 7821, 2383, 1513, 16383, 2106, + 7376, 8168 + }; + + for (int cc = 0; cc < 50; cc += 1) { + final Pipe pipe = SelectorProvider.provider().openPipe(); + final WritableByteChannel out = pipe.sink(); + final ReadableByteChannel in = pipe.source(); + + // write out the frames of the specified sizes + Thread writer = new Thread() { + public void run () { + try { + FramingOutputStream fout = new FramingOutputStream(); + DataOutputStream dout = new DataOutputStream(fout); + + for (int count : FRAME_COUNTS) { + for (int ii = 0; ii < count; ii++) dout.writeInt(ii); + ByteBuffer frame = fout.frameAndReturnBuffer(); + int wrote = 0, size = frame.remaining(), window = 128; + while (wrote < size) { + // write out the buffer in small chunks so we trigger the failure in + // the reader + frame.limit(Math.min(window, size)); + wrote += out.write(frame); + window += 128; + } + fout.resetFrame(); + // System.out.println("Sent frame @ " + count); + } + + } catch (IOException ioe) { + fail(ioe.getMessage()); + } + } + }; + writer.start(); + + // read them back in and check their contents + FramedInputStream fin = new FramedInputStream(); + DataInputStream din = new DataInputStream(fin); + for (int ff = 0; ff < FRAME_COUNTS.length; ff += 1) { + try { + while (!fin.readFrame(in)) {} // loop! + int count = fin.available() / 4; + for (int ii = 0; ii < count; ii++) assertEquals(ii, din.readInt()); + // System.out.println("Read frame " + ff + " @ " + count); + } catch (IllegalArgumentException iae) { + System.err.println("Invariant violation reading frame " + ff + ": " + iae); + throw iae; + } + } + + writer.join(); + in.close(); + out.close(); } } - public static Test suite () - { - return new FrameTest(); - } + // this soak test was used to generate a series of frame sizes that triggered the nasty bug + // that is isolated and tested by the above testInvariantFailure test; let's hope we go another + // 17 years before we have to use the soak test again - public static void main (String[] args) - { - FrameTest test = new FrameTest(); - test.runTest(); + // @Test + public void testManyRandomFrames () throws Exception { + final int PAIRS = 102400; + + for (int pp = 0; pp < PAIRS; pp++) { + if (pp % 128 == 0) System.out.println("Starting reader/writer pair " + pp); + + final Pipe pipe = SelectorProvider.provider().openPipe(); + final WritableByteChannel out = pipe.sink(); + final ReadableByteChannel in = pipe.source(); + final int FRAMES = 100; + final List frameSizes = new ArrayList(); + + // write out a series of randomly sized frames + Thread writer = new Thread() { + public void run () { + try { + Random rando = new Random(); + FramingOutputStream fout = new FramingOutputStream(); + DataOutputStream dout = new DataOutputStream(fout); + + for (int ff = 0; ff < FRAMES; ff++) { + int count = rando.nextInt(16*1024); + frameSizes.add(count); + for (int ii = 0; ii < count; ii++) dout.writeInt(ii); + ByteBuffer frame = fout.frameAndReturnBuffer(); + int wrote = 0; + while (wrote < frame.limit()) wrote += out.write(frame); + fout.resetFrame(); + // if (ff % 1000 == 0) System.out.println("Sent frame " + ff); + } + + } catch (IOException ioe) { + fail(ioe.getMessage()); + } + } + }; + writer.start(); + + // read them back in and check their contents + FramedInputStream fin = new FramedInputStream(); + DataInputStream din = new DataInputStream(fin); + for (int ff = 0; ff < FRAMES; ff++) { + // if (ff % 1000 == 0) System.out.println("Reading frame " + ff); + try { + while (!fin.readFrame(in)) {} // loop! + int ints = fin.available() / 4; + for (int ii = 0; ii < ints; ii++) assertEquals(ii, din.readInt()); + } catch (IllegalArgumentException iae) { + System.err.println("Invariant violation: " + iae.getMessage()); + System.err.println("While reading frame: " + ff); + System.err.println("Frame sizes: " + frameSizes); + throw iae; + } + } + + writer.join(); + in.close(); + out.close(); + } } protected static final String STRING1 = "This is a test."; @@ -121,11 +232,9 @@ public class FrameTest extends TestCase "this frame and someone would probably be enjoying themselves."; protected static final String STRING4 = - "Now is the time for all good men to come to the aid of " + - "their country."; + "Now is the time for all good men to come to the aid of their country."; protected static final String STRING5 = "Every good boy deserves fudge."; - protected static final String STRING6 = - "The quick brown fox jumped over the lazy dog."; + protected static final String STRING6 = "The quick brown fox jumped over the lazy dog."; protected static final String STRING7 = "Third time is the charm."; }