@Test
public void testWriteFailsFastOnClosedChannel() throws Exception {

    Bootstrap cb = new Bootstrap();
    ServerBootstrap sb = new ServerBootstrap();
    cb.group(group1)
            .channel(LocalChannel.class)
            .handler(new TestHandler());
    sb.group(group2)
            .channel(LocalServerChannel.class)
            .childHandler(new ChannelInitializer<LocalChannel>() {
                @Override
                public void initChannel(LocalChannel ch) throws Exception {
                    ch.pipeline().addLast(new TestHandler());
                }
            });
    Channel sc = null;
    Channel cc = null;
    try {
        // Start server
        sc = sb.bind(TEST_ADDRESS).sync().channel();
        // Connect to the server
        cc = cb.connect(sc.localAddress()).sync().channel();
        // Close the channel and write something.
        cc.close().sync();
        try {
            cc.writeAndFlush(new Object()).sync();
            fail("must raise a ClosedChannelException");
        } catch (Exception e) {
            assertThat(e, is(instanceOf(ClosedChannelException.class)));
            // Ensure that the actual write attempt on a closed channel was never made by asserting that
            // the ClosedChannelException has been created by AbstractUnsafe rather than transport implementations.
            if (e.getStackTrace().length > 0) {
                assertThat(
                        e.getStackTrace()[0].getClassName(), is(AbstractChannel.class.getName() +
                                "$AbstractUnsafe"));
                e.printStackTrace();
            }
        }
    } finally {
        closeChannel(cc);
        closeChannel(sc);
    }
}