Package fi.jumi.core.ipc.channel

Source Code of fi.jumi.core.ipc.channel.IpcProtocolTest

// Copyright © 2011-2014, 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.ipc.channel;

import fi.jumi.actors.eventizers.Event;
import fi.jumi.actors.queue.MessageSender;
import fi.jumi.core.Timeouts;
import fi.jumi.core.api.*;
import fi.jumi.core.events.SuiteListenerEventizer;
import fi.jumi.core.ipc.TestUtil;
import fi.jumi.core.ipc.buffer.*;
import fi.jumi.core.ipc.encoding.*;
import fi.jumi.core.runs.RunIdSequence;
import fi.jumi.core.util.SpyListener;
import org.junit.*;
import org.junit.rules.*;

import java.nio.file.*;
import java.util.concurrent.locks.LockSupport;

import static fi.jumi.core.util.ConcurrencyUtil.runConcurrently;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.is;
import static org.mockito.Mockito.mock;

public class IpcProtocolTest {

    @Rule
    public final ExpectedException thrown = ExpectedException.none();

    @Rule
    public final TemporaryFolder tempDir = new TemporaryFolder();

    @Rule
    public final Timeout timeout = Timeouts.forUnitTest();

    @Test
    public void test_concurrent_producer_and_consumer() throws Exception {
        Path mmf = tempDir.getRoot().toPath().resolve("mmf");
        SpyListener<SuiteListener> expectations = new SpyListener<>(SuiteListener.class);
        lotsOfEventsForConcurrencyTesting(expectations.getListener(), 0);
        expectations.replay();

        Runnable producer = () -> {
            IpcWriter<SuiteListener> writer = IpcChannel.writer(mmf, SuiteListenerEncoding::new);
            lotsOfEventsForConcurrencyTesting(sendTo(writer), 1);
            writer.close();
        };
        Runnable consumer = () -> {
            IpcReader<SuiteListener> reader = IpcChannel.reader(mmf, SuiteListenerEncoding::new);
            try {
                IpcReaders.decodeAll(reader, expectations.getListener());
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
            }
        };
        runConcurrently(producer, consumer);

        expectations.verify();
    }

    private static void lotsOfEventsForConcurrencyTesting(SuiteListener listener, int nanosToPark) {
        TestFile testFile = TestFile.fromClassName("DummyTest");
        RunIdSequence runIds = new RunIdSequence();
        for (int i = 0; i < 10; i++) {
            RunId runId = runIds.nextRunId();

            // Not a realistic scenario, because we are only interested in concurrency testing
            // the IPC protocol and not the specifics of a particular interface.
            listener.onSuiteStarted();
            LockSupport.parkNanos(nanosToPark);
            listener.onRunStarted(runId, testFile);
            LockSupport.parkNanos(nanosToPark);
            listener.onRunFinished(runId);
            LockSupport.parkNanos(nanosToPark);
            listener.onSuiteFinished();
            LockSupport.parkNanos(nanosToPark);
        }
    }

    @Test
    public void producer_will_always_decide_segment_size_except_for_the_first_segment() throws Exception {
        Path mmf = tempDir.getRoot().toPath().resolve("mmf");
        SpyListener<SuiteListener> expectations = new SpyListener<>(SuiteListener.class);
        smallEventsForSegmentSizeConcurrencyTesting(expectations.getListener(), 0);
        expectations.replay();

        Runnable producer = () -> {
            IpcWriter<SuiteListener> writer = IpcChannel.writer(new FileSegmenter(mmf, 1, 1), SuiteListenerEncoding::new);
            smallEventsForSegmentSizeConcurrencyTesting(sendTo(writer), 10000000);
            writer.close();
        };
        Runnable consumer = () -> {
            IpcReader<SuiteListener> reader = IpcChannel.reader(new FileSegmenter(mmf, 2, 2), SuiteListenerEncoding::new);
            try {
                IpcReaders.decodeAll(reader, expectations.getListener());
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
            }
        };
        runConcurrently(producer, consumer);

        expectations.verify();
        try (DirectoryStream<Path> segments = Files.newDirectoryStream(tempDir.getRoot().toPath())) {
            for (Path segment : segments) {
                if (segment.equals(mmf)) {
                    // XXX: ignoring the first segment
                    // We can't make the consumer to wait on the producer without it opening
                    // at least one segment, so the producer may decide the size for the first segment.
                    // So we'll need to take care of it at a higher level, that the consumer won't know
                    // the file name before the producer has had time to create it.
                    continue;
                }

                assertThat("size of " + segment, Files.size(segment), is(1L));
            }
        }
    }

    private static void smallEventsForSegmentSizeConcurrencyTesting(SuiteListener listener, int nanosToPark) {
        for (int i = 0; i < 10; i++) {

            // Not a realistic scenario, because we are only interested in concurrency testing
            // the IPC protocol and not the specifics of a particular interface.
            listener.onSuiteStarted();
            LockSupport.parkNanos(nanosToPark);
        }
    }


    // headers

    @Test
    public void cannot_decode_if_header_has_wrong_magic_bytes() {
        IpcBuffer buffer = encodeSomeEvents();

        buffer.setInt(0, 0x0A0B0C0D);

        thrown.expect(IllegalArgumentException.class);
        thrown.expectMessage("wrong header: expected 4A 75 6D 69 but was 0A 0B 0C 0D");
        tryToDecode(buffer);
    }

    @Test
    public void cannot_decode_if_header_has_wrong_protocol_version() {
        IpcBuffer buffer = encodeSomeEvents();

        buffer.setInt(4, 9999);

        thrown.expect(IllegalArgumentException.class);
        thrown.expectMessage("unsupported protocol version: 9999");
        tryToDecode(buffer);
    }

    @Test
    public void cannot_decode_if_header_has_wrong_interface() {
        IpcBuffer buffer = encodeSomeEvents();

        buffer.position(8);
        StringEncoding.writeString(buffer, "com.example.AnotherInterface");

        thrown.expect(IllegalArgumentException.class);
        thrown.expectMessage("wrong interface: expected fi.jumi.core.api.SuiteListener but was com.example.AnotherInterface");
        tryToDecode(buffer);
    }

    @Test
    public void cannot_decode_if_header_has_wrong_interface_version() {
        IpcBuffer buffer = encodeSomeEvents();

        buffer.position(8);
        StringEncoding.readString(buffer); // go to interface version's position
        buffer.writeInt(9999);

        thrown.expect(IllegalArgumentException.class);
        thrown.expectMessage("unsupported interface version: 9999");
        tryToDecode(buffer);
    }

    private static IpcBuffer encodeSomeEvents() {
        IpcBuffer buffer = TestUtil.newIpcBuffer();
        IpcProtocol<SuiteListener> protocol = newIpcProtocol(buffer);
        protocol.start();
        sendTo(protocol).onSuiteStarted();
        protocol.close();
        return buffer;
    }

    private static void tryToDecode(IpcBuffer buffer) {
        buffer.position(0);
        IpcProtocol<SuiteListener> protocol = newIpcProtocol(buffer);
        try {
            IpcReaders.decodeAll(protocol, mock(SuiteListener.class));
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
        }
    }

    private static IpcProtocol<SuiteListener> newIpcProtocol(IpcBuffer buffer) {
        return new IpcProtocol<>(buffer, SuiteListenerEncoding::new);
    }

    private static SuiteListener sendTo(MessageSender<Event<SuiteListener>> target) {
        return new SuiteListenerEventizer().newFrontend(target);
    }
}
TOP

Related Classes of fi.jumi.core.ipc.channel.IpcProtocolTest

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.