### Question:
AsyncTimeout extends Timeout { public final Sink sink(final Sink sink) { return new Sink() { @Override public void write(Buffer source, long byteCount) throws IOException { boolean throwOnTimeout = false; enter(); try { sink.write(source, byteCount); throwOnTimeout = true; } catch (IOException e) { throw exit(e); } finally { exit(throwOnTimeout); } } @Override public void flush() throws IOException { boolean throwOnTimeout = false; enter(); try { sink.flush(); throwOnTimeout = true; } catch (IOException e) { throw exit(e); } finally { exit(throwOnTimeout); } } @Override public void close() throws IOException { boolean throwOnTimeout = false; enter(); try { sink.close(); throwOnTimeout = true; } catch (IOException e) { throw exit(e); } finally { exit(throwOnTimeout); } } @Override public Timeout timeout() { return AsyncTimeout.this; } @Override public String toString() { return "AsyncTimeout.sink(" + sink + ")"; } }; }  final void enter(); final boolean exit(); final Sink sink(final Sink sink); final Source source(final Source source);  }### Answer:
@Test public void wrappedThrowsWithTimeout() throws Exception { Sink sink = new ForwardingSink(new Buffer()) { @Override public void write(Buffer source, long byteCount) throws IOException { try { Thread.sleep(500); throw new IOException("exception and timeout"); } catch (InterruptedException e) { throw new AssertionError(); } } }; AsyncTimeout timeout = new AsyncTimeout(); timeout.timeout(250, TimeUnit.MILLISECONDS); Sink timeoutSink = timeout.sink(sink); try { timeoutSink.write(null, 0); fail(); } catch (InterruptedIOException expected) { assertEquals("timeout", expected.getMessage()); assertEquals("exception and timeout", expected.getCause().getMessage()); } }
@Test public void wrappedThrowsWithoutTimeout() throws Exception { Sink sink = new ForwardingSink(new Buffer()) { @Override public void write(Buffer source, long byteCount) throws IOException { throw new IOException("no timeout occurred"); } }; AsyncTimeout timeout = new AsyncTimeout(); timeout.timeout(250, TimeUnit.MILLISECONDS); Sink timeoutSink = timeout.sink(sink); try { timeoutSink.write(null, 0); fail(); } catch (IOException expected) { assertEquals("no timeout occurred", expected.getMessage()); } }
@Test public void wrappedSinkTimesOut() throws Exception { Sink sink = new ForwardingSink(new Buffer()) { @Override public void write(Buffer source, long byteCount) throws IOException { try { Thread.sleep(500); } catch (InterruptedException e) { throw new AssertionError(); } } }; AsyncTimeout timeout = new AsyncTimeout(); timeout.timeout(250, TimeUnit.MILLISECONDS); Sink timeoutSink = timeout.sink(sink); try { timeoutSink.write(null, 0); fail(); } catch (InterruptedIOException expected) { } }