// 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.test;
import fi.jumi.core.config.SuiteConfiguration;
import fi.jumi.core.network.*;
import fi.jumi.launcher.JumiLauncher;
import org.junit.*;
import org.junit.rules.Timeout;
import sample.*;
import java.io.IOException;
import java.util.*;
import java.util.regex.*;
import static fi.jumi.core.util.AsyncAssert.assertEventually;
import static fi.jumi.test.util.ProcessMatchers.*;
import static org.fest.assertions.Assertions.assertThat;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.*;
public class DaemonProcessTest {
@Rule
public final AppRunner app = new AppRunner();
@Rule
public final Timeout timeout = Timeouts.forEndToEndTest();
private JumiLauncher launcher;
private Process daemonProcess;
@Test
public void daemon_process_prints_the_program_name_and_version_number_on_startup() throws Exception {
startDaemonProcess();
assertThat(firstLine(app.getCurrentDaemonOutput()))
.matches("Jumi " + TestEnvironment.VERSION_NUMBERING.getPattern() + " starting up");
}
@Test
public void daemon_process_can_be_closed_by_sending_it_a_shutdown_command() throws Exception {
// Use a high enough idle timeout to avoid the daemon shutting down automatically
app.daemon.setIdleTimeout(Timeouts.END_TO_END_TEST * 2);
startDaemonProcess();
launcher.shutdownDaemon();
assertEventually(daemonProcess, is(dead()), Timeouts.ASSERTION);
assertThat(app.getFinishedDaemonOutput(), containsString("The system will now exit: ordered to shut down"));
}
@Test
public void daemon_process_will_exit_after_a_timeout_after_all_clients_disconnect() throws Exception {
app.daemon.setIdleTimeout(0);
startDaemonProcess();
launcher.close();
assertEventually(daemonProcess, is(dead()), Timeouts.ASSERTION);
assertThat(app.getFinishedDaemonOutput(), containsString("The system will now exit: timed out after everybody disconnected"));
}
@Test
public void daemon_process_will_exit_if_it_cannot_connect_to_the_launcher_on_startup() throws Exception {
app.setMockNetworkServer(new NonFunctionalNetworkServer());
app.daemon.setStartupTimeout(0);
startDaemonProcessAsynchronously();
assertEventually(daemonProcess, is(dead()), Timeouts.ASSERTION);
assertThat(app.getFinishedDaemonOutput(), containsString("The system will now exit: timed out before anybody connected"));
}
@Test
public void classes_showing_up_in_actor_logs_have_custom_toString_methods() throws Exception {
List<String> irrelevantClasses = Arrays.asList(
// JDK classes
"java.util.concurrent.ThreadPoolExecutor",
// dummies
"sample.BuggyDriver",
"sample.BuggyDriver$$Lambda$1",
"fi.jumi.simpleunit.SimpleUnit",
"fi.jumi.simpleunit.SimpleUnit$RunTestMethod",
// TODO: remove me; investigate the cause and create a better toString for the actor
"fi.jumi.core.events.suiteListener.SuiteListenerToEvent"
);
app.daemon.setLogActorMessages(true);
// sample test classes to produce all possible events
startDaemonProcess(
OnePassingTest.class, // the most common events
OneFailingTest.class, // onFailure
BuggyDriverTest.class // onInternalError
);
launcher.close();
Set<String> classes = findClassesWithDefaultToString(app.getFinishedDaemonOutput());
classes.removeAll(irrelevantClasses);
assertThat("did not expect the following classes to have default toString() method", classes, is(empty()));
}
private static Set<String> findClassesWithDefaultToString(String subject) {
Set<String> classNames = new HashSet<>();
Matcher m = Pattern.compile("([\\w\\.\\$]+)@[0-9a-f]{6,8}").matcher(subject);
while (m.find()) {
classNames.add(m.group(1));
}
return classNames;
}
// helpers
private void startDaemonProcess(Class<?>... testClasses) throws Exception {
app.runTests(testClasses);
initTestHelpers();
}
private void startDaemonProcessAsynchronously() throws Exception {
app.startSuiteAsynchronously(new SuiteConfiguration());
initTestHelpers();
}
private void initTestHelpers() throws Exception {
launcher = app.getLauncher();
daemonProcess = app.getDaemonProcess();
assertThat(daemonProcess, is(alive()));
}
private static String firstLine(String output) {
return new Scanner(output).nextLine();
}
private static class NonFunctionalNetworkServer implements NetworkServer {
@Override
public <In, Out> int listenOnAnyPort(NetworkEndpointFactory<In, Out> endpointFactory) {
return 10; // unassigned port according to http://en.wikipedia.org/wiki/List_of_TCP_and_UDP_port_numbers
}
@Override
public void close() throws IOException {
}
}
}