fi.jumi.launcher.remote.ProcessStartingDaemonSummonerTest.java Source code

Java tutorial

Introduction

Here is the source code for fi.jumi.launcher.remote.ProcessStartingDaemonSummonerTest.java

Source

// 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.launcher.remote;

import fi.jumi.actors.ActorRef;
import fi.jumi.actors.eventizers.Event;
import fi.jumi.actors.queue.MessageSender;
import fi.jumi.core.api.*;
import fi.jumi.core.config.*;
import fi.jumi.core.events.SuiteListenerEventizer;
import fi.jumi.core.events.suiteListener.OnSuiteStartedEvent;
import fi.jumi.core.ipc.api.RequestListener;
import fi.jumi.core.network.*;
import fi.jumi.core.util.SpyListener;
import fi.jumi.launcher.FakeProcess;
import fi.jumi.launcher.daemon.Steward;
import fi.jumi.launcher.process.*;
import org.apache.commons.io.output.WriterOutputStream;
import org.junit.*;
import org.junit.rules.Timeout;

import java.io.*;
import java.nio.file.*;
import java.util.concurrent.*;

import static fi.jumi.core.util.AsyncAssert.assertEventually;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.*;
import static org.mockito.Mockito.*;

public class ProcessStartingDaemonSummonerTest {

    private static final int TIMEOUT = 1000;

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

    private final Steward steward = mock(Steward.class);
    private final SpyProcessStarter processStarter = new SpyProcessStarter();
    private final SpyNetworkServer daemonConnector = new SpyNetworkServer();
    private final StringWriter outputListener = new StringWriter();

    private final ProcessStartingDaemonSummoner daemonSummoner = new ProcessStartingDaemonSummoner(steward,
            processStarter, daemonConnector, new WriterOutputStream(outputListener));

    private final SuiteConfiguration dummySuiteConfig = new SuiteConfiguration();
    private final DaemonConfiguration dummyDaemonConfig = new DaemonConfiguration();
    private final DaemonListener daemonListener = mock(DaemonListener.class);
    private final Path dummyDaemonDir = Paths.get("dummy-daemon-dir");

    {
        stub(steward.createDaemonDir(dummyDaemonConfig.getJumiHome())).toReturn(dummyDaemonDir);
    }

    @Test
    public void tells_to_daemon_the_daemon_directory_to_use() {
        daemonSummoner.connectToDaemon(dummySuiteConfig, dummyDaemonConfig, ActorRef.wrap(daemonListener));

        DaemonConfiguration daemonConfig = parseDaemonArguments(processStarter.lastArgs);
        assertThat(daemonConfig.getDaemonDir(), is(dummyDaemonDir));
    }

    @Test
    public void tells_to_daemon_the_socket_to_contact() {
        daemonConnector.portToReturn = 123;

        daemonSummoner.connectToDaemon(dummySuiteConfig, dummyDaemonConfig, ActorRef.wrap(daemonListener));

        DaemonConfiguration daemonConfig = parseDaemonArguments(processStarter.lastArgs);
        assertThat(daemonConfig.getLauncherPort(), is(123));
    }

    @Test
    public void tells_to_output_listener_what_the_daemon_prints() {
        processStarter.processToReturn.inputStream = new ByteArrayInputStream("hello".getBytes());

        daemonSummoner.connectToDaemon(dummySuiteConfig, dummyDaemonConfig, ActorRef.wrap(daemonListener));

        assertEventually(outputListener, hasToString("hello"), TIMEOUT);
    }

    @Test
    public void tells_to_daemon_listener_what_events_the_daemon_sends() {
        daemonSummoner.connectToDaemon(dummySuiteConfig, dummyDaemonConfig, ActorRef.wrap(daemonListener));

        NetworkEndpoint<Event<SuiteListener>, Event<RequestListener>> endpoint = daemonConnector.lastEndpointFactory
                .createEndpoint();

        OnSuiteStartedEvent anyMessage = new OnSuiteStartedEvent();
        endpoint.onMessage(anyMessage);
        verify(daemonListener).onMessage(anyMessage);
    }

    @Test
    public void reports_an_internal_error_if_the_daemon_fails_to_connect_within_a_timeout()
            throws InterruptedException {
        SpyListener<SuiteListener> spy = new SpyListener<>(SuiteListener.class);
        SuiteListener expect = spy.getListener();
        CountDownLatch expectedMessagesArrived = new CountDownLatch(3);

        expect.onSuiteStarted();
        expect.onInternalError("Failed to start the test runner daemon process",
                StackTrace.from(new RuntimeException("Could not connect to the daemon: timed out after 0 ms")));
        expect.onSuiteFinished();
        spy.replay();

        daemonSummoner.connectToDaemon(dummySuiteConfig,
                new DaemonConfigurationBuilder().setStartupTimeout(0).freeze(),
                actorRef(new FakeDaemonListener(countMessages(expect, expectedMessagesArrived))));

        expectedMessagesArrived.await(TIMEOUT / 2, TimeUnit.MILLISECONDS);
        spy.verify();
    }

    // helpers

    private static ActorRef<DaemonListener> actorRef(DaemonListener proxy) {
        return ActorRef.wrap(proxy);
    }

    private static DaemonConfiguration parseDaemonArguments(String[] args) {
        return new DaemonConfigurationBuilder().parseProgramArgs(args).freeze();
    }

    private static SuiteListener countMessages(SuiteListener target, CountDownLatch latch) {
        SuiteListenerEventizer eventizer = new SuiteListenerEventizer();
        MessageSender<Event<SuiteListener>> backend = eventizer.newBackend(target);
        MessageSender<Event<SuiteListener>> counter = message -> {
            backend.send(message);
            latch.countDown();
        };
        return eventizer.newFrontend(counter);
    }

    private static class SpyProcessStarter implements ProcessStarter {

        public String[] lastArgs;
        public FakeProcess processToReturn = new FakeProcess();

        @Override
        public Process startJavaProcess(JvmArgs jvmArgs) throws IOException {
            this.lastArgs = jvmArgs.programArgs.toArray(new String[0]);
            return processToReturn;
        }
    }

    private static class SpyNetworkServer implements NetworkServer {

        public NetworkEndpointFactory<Event<SuiteListener>, Event<RequestListener>> lastEndpointFactory;
        public int portToReturn = 1;

        @Override
        public <In, Out> int listenOnAnyPort(NetworkEndpointFactory<In, Out> endpointFactory) {
            this.lastEndpointFactory = (NetworkEndpointFactory<Event<SuiteListener>, Event<RequestListener>>) endpointFactory;
            return portToReturn;
        }

        @Override
        public void close() throws IOException {
        }
    }

    private static class FakeDaemonListener implements DaemonListener {

        private final SuiteListener suiteListener;

        private FakeDaemonListener(SuiteListener suiteListener) {
            this.suiteListener = suiteListener;
        }

        @Override
        public void onConnected(NetworkConnection connection, MessageSender<Event<RequestListener>> sender) {
        }

        @Override
        public void onMessage(Event<SuiteListener> message) {
            message.fireOn(suiteListener);
        }

        @Override
        public void onDisconnected() {
        }
    }
}