fi.jumi.core.stdout.SynchronizedPrintStreamTest.java Source code

Java tutorial

Introduction

Here is the source code for fi.jumi.core.stdout.SynchronizedPrintStreamTest.java

Source

// Copyright  2011-2013, Esko Luontola <www.orfjackal.net>
// This software is released under the Apache License 2.0.
// The license text is at http://www.apache.org/licenses/LICENSE-2.0

package fi.jumi.core.stdout;

import net.sf.cglib.proxy.Factory;
import org.apache.commons.io.output.NullOutputStream;
import org.junit.*;
import org.junit.rules.Timeout;

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

import static fi.jumi.core.util.ConcurrencyUtil.runConcurrently;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.*;

@SuppressWarnings("SynchronizationOnLocalVariableOrMethodParameter")
public class SynchronizedPrintStreamTest {

    @Rule
    public final Timeout timeout = new Timeout(1000);

    private final Object lock = new Object();

    @Test
    public void synchronizes_all_methods_on_the_lock_given_as_parameter() {
        SpyOutputStream spy = new SpyOutputStream();
        PrintStream printStream = SynchronizedPrintStream.create(spy, Charset.defaultCharset(), lock);

        printStream.println("foo");

        assertThat("was called", spy.wasCalled, is(true));
        assertThat("used the lock", spy.lockWasHeldByCurrentThread, is(true));
    }

    /**
     * For example {@link Throwable#printStackTrace} does this, we must be careful to always acquire a lock on the
     * monitor of the PrintStream first, before all other locks.
     */
    @Test
    public void does_not_deadlock_if_somebody_locks_in_the_PrintStream_externally() throws Exception {
        final int ITERATIONS = 10;
        PrintStream printStream = SynchronizedPrintStream.create(new NullOutputStream(), Charset.defaultCharset(),
                lock);

        // will fail with a test timeout if a deadlock happens
        runConcurrently(() -> {
            // what Thread.printStackTrace() would do
            synchronized (printStream) {
                for (int i = 0; i < ITERATIONS; i++) {
                    Thread.yield();
                    printStream.print("X");
                }
            }
        }, () -> {
            // what a normal printer would do
            for (int i = 0; i < ITERATIONS; i++) {
                Thread.yield();
                printStream.print("X");
            }
        });
    }

    @Test
    public void the_class_name_in_stack_traces_gives_a_hint_of_who_generated_the_proxy_class() {
        PrintStream printStream = SynchronizedPrintStream.create(new NullOutputStream(), Charset.defaultCharset(),
                lock);

        assertThat(printStream.getClass().getName(), startsWith(SynchronizedPrintStream.class.getName()));
    }

    @Test
    public void does_not_expose_the_CGLIB_Factory_interface() {
        PrintStream printStream = SynchronizedPrintStream.create(new NullOutputStream(), Charset.defaultCharset(),
                lock);

        assertThat(printStream, not(instanceOf(Factory.class)));
    }

    private class SpyOutputStream extends OutputStream {
        boolean wasCalled = false;
        boolean lockWasHeldByCurrentThread = false;

        @Override
        public void write(int b) {
            wasCalled = true;
            lockWasHeldByCurrentThread = Thread.holdsLock(lock);
        }
    }
}