GuardedOutputStreamTest.java

package tools.jackson.core.unittest.io;

import java.io.*;
import java.nio.charset.StandardCharsets;

import org.junit.jupiter.api.Test;

import tools.jackson.core.io.GuardedOutputStream;
import tools.jackson.core.io.UTF8Writer;
import tools.jackson.core.unittest.*;

import static org.junit.jupiter.api.Assertions.*;

// [core#1738]
class GuardedOutputStreamTest
    extends JacksonCoreTestBase
{
    static class TrackingStream extends ByteArrayOutputStream {
        int flushCount, closeCount, singleByteWrites;

        @Override
        public void write(int b) {
            ++singleByteWrites;
            super.write(b);
        }

        @Override
        public void flush() { ++flushCount; }

        @Override
        public void close() { ++closeCount; }
    }

    @Test
    void nullStreamRejected()
    {
        assertThrows(NullPointerException.class, () -> new GuardedOutputStream(null));
    }

    @Test
    void writesPassedThrough() throws Exception
    {
        TrackingStream target = new TrackingStream();
        try (OutputStream out = new GuardedOutputStream(target)) {
            out.write('a');
            out.write("bcd".getBytes(StandardCharsets.UTF_8));
            out.write("xefx".getBytes(StandardCharsets.UTF_8), 1, 2);
        }
        assertEquals("abcdef", target.toString(StandardCharsets.UTF_8));
        // only the explicit single-byte write; arrays must not be split
        assertEquals(1, target.singleByteWrites);
    }

    @Test
    void flushAndCloseNotPassed() throws Exception
    {
        TrackingStream target = new TrackingStream();
        GuardedOutputStream out = new GuardedOutputStream(target);
        out.write('a');
        out.flush();
        out.close();
        assertEquals(0, target.flushCount);
        assertEquals(0, target.closeCount);
    }

    @Test
    void writesAfterCloseFail() throws Exception
    {
        TrackingStream target = new TrackingStream();
        GuardedOutputStream out = new GuardedOutputStream(target);
        out.write('a');
        out.close();
        assertThrows(IOException.class, () -> out.write('b'));
        assertThrows(IOException.class, () -> out.write(new byte[] { 'c' }));
        assertThrows(IOException.class, () -> out.write(new byte[] { 'd' }, 0, 1));
        assertEquals("a", target.toString(StandardCharsets.UTF_8));
        // second close is a no-op, as is flush after close
        out.close();
        out.flush();
    }

    // Usage as by textual format backends
    @Test
    void utf8WriterOverGuardedStream() throws Exception
    {
        TrackingStream target = new TrackingStream();
        Writer w = new UTF8Writer(testIOContext(), new GuardedOutputStream(target));
        w.write("abc");
        w.flush();
        assertEquals("abc", target.toString(StandardCharsets.UTF_8));
        w.write("d��f");
        w.close();
        assertEquals("abcd��f", target.toString(StandardCharsets.UTF_8));
        assertEquals(0, target.flushCount);
        assertEquals(0, target.closeCount);
        assertEquals(0, target.singleByteWrites);
    }

    @Test
    void writerOverGuardedStream() throws Exception
    {
        TrackingStream target = new TrackingStream();
        Writer w = new OutputStreamWriter(new GuardedOutputStream(target), StandardCharsets.UTF_8);
        w.write("abc");
        w.flush();
        assertEquals("abc", target.toString(StandardCharsets.UTF_8));
        w.write("def");
        w.close();
        assertEquals("abcdef", target.toString(StandardCharsets.UTF_8));
        assertEquals(0, target.flushCount);
        assertEquals(0, target.closeCount);
    }
}