Package org.sonar.process.monitor

Source Code of org.sonar.process.monitor.MonitorTest$HttpProcessClient

/*
* SonarQube, open source software quality management tool.
* Copyright (C) 2008-2014 SonarSource
* mailto:contact AT sonarsource DOT com
*
* SonarQube is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 3 of the License, or (at your option) any later version.
*
* SonarQube is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
*/
package org.sonar.process.monitor;

import com.github.kevinsawicki.http.HttpRequest;
import org.apache.commons.io.FileUtils;
import org.junit.After;
import org.junit.BeforeClass;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.TemporaryFolder;
import org.junit.rules.Timeout;
import org.sonar.process.NetworkUtils;
import org.sonar.process.Lifecycle.State;
import org.sonar.process.SystemExit;

import java.io.File;
import java.io.IOException;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;

import static org.fest.assertions.Assertions.assertThat;
import static org.fest.assertions.Fail.fail;
import static org.mockito.Mockito.mock;

public class MonitorTest {

  static File testJar;
  Monitor monitor;
  SystemExit exit = mock(SystemExit.class);

  /**
   * Find the JAR file containing the test apps. Classes can't be moved in sonar-process-monitor because
   * they require sonar-process dependencies when executed here (sonar-process, commons-*, ...).
   */
  @BeforeClass
  public static void initTestJar() {
    File targetDir = new File("server/sonar-process/target");
    if (!targetDir.exists() || !targetDir.isDirectory()) {
      targetDir = new File("../sonar-process/target");
    }
    if (!targetDir.exists() || !targetDir.isDirectory()) {
      throw new IllegalStateException("target dir of sonar-process module not found. Please build it.");
    }
    Collection<File> jars = FileUtils.listFiles(targetDir, new String[] {"jar"}, false);
    for (File jar : jars) {
      if (jar.getName().startsWith("sonar-process-") && jar.getName().endsWith("-test-jar-with-dependencies.jar")) {
        testJar = jar;
        return;
      }
    }
    throw new IllegalStateException("No sonar-process-*-test-jar-with-dependencies.jar in " + targetDir);
  }

  /**
   * Safeguard
   */
  @Rule
  public Timeout globalTimeout = new Timeout(30000);

  /**
   * Temporary directory is used to interact with monitored processes, which write in it.
   */
  @Rule
  public TemporaryFolder temp = new TemporaryFolder();

  /**
   * Safeguard
   */
  @After
  public void tearDown() throws Exception {
    try {
      if (monitor != null) {
        monitor.stop();
      }
    } catch (Throwable ignored) {
    }
  }

  @Test
  public void fail_to_start_if_no_commands() throws Exception {
    monitor = newDefaultMonitor();
    try {
      monitor.start(Collections.<JavaCommand>emptyList());
      fail();
    } catch (IllegalArgumentException e) {
      assertThat(e).hasMessage("At least one command is required");
    }
  }

  @Test
  public void fail_to_start_multiple_times() throws Exception {
    monitor = newDefaultMonitor();
    monitor.start(Arrays.asList(newStandardProcessCommand()));
    boolean failed = false;
    try {
      monitor.start(Arrays.asList(newStandardProcessCommand()));
    } catch (IllegalStateException e) {
      failed = e.getMessage().equals("Can not start multiple times");
    }
    monitor.stop();
    assertThat(failed);
  }

  @Test
  public void start_then_stop_gracefully() throws Exception {
    monitor = newDefaultMonitor();
    HttpProcessClient client = new HttpProcessClient("test");
    // blocks until started
    monitor.start(Arrays.asList(client.newCommand()));

    assertThat(client.isReady()).isTrue();
    assertThat(client.wasReadyAt()).isLessThanOrEqualTo(System.currentTimeMillis());

    // blocks until stopped
    monitor.stop();
    assertThat(client.isReady()).isFalse();
    assertThat(client.wasGracefullyTerminated()).isTrue();
    assertThat(monitor.getState()).isEqualTo(State.STOPPED);
  }

  @Test
  public void start_then_stop_sequence_of_commands() throws Exception {
    monitor = newDefaultMonitor();
    HttpProcessClient p1 = new HttpProcessClient("p1"), p2 = new HttpProcessClient("p2");
    monitor.start(Arrays.asList(p1.newCommand(), p2.newCommand()));

    // start p2 when p1 is fully started (ready)
    assertThat(p1.isReady()).isTrue();
    assertThat(p2.isReady()).isTrue();
    assertThat(p2.wasStartingAt()).isGreaterThanOrEqualTo(p1.wasReadyAt());

    monitor.stop();

    // stop in inverse order
    assertThat(p1.isReady()).isFalse();
    assertThat(p2.isReady()).isFalse();
    assertThat(p1.wasGracefullyTerminated()).isTrue();
    assertThat(p2.wasGracefullyTerminated()).isTrue();
    assertThat(p2.wasGracefullyTerminatedAt()).isLessThanOrEqualTo(p1.wasGracefullyTerminatedAt());
  }

  @Test
  public void stop_all_processes_if_monitor_shutdowns() throws Exception {
    monitor = newDefaultMonitor();
    HttpProcessClient p1 = new HttpProcessClient("p1"), p2 = new HttpProcessClient("p2");
    monitor.start(Arrays.asList(p1.newCommand(), p2.newCommand()));
    assertThat(p1.isReady()).isTrue();
    assertThat(p2.isReady()).isTrue();

    // emulate CTRL-C
    monitor.getShutdownHook().run();
    monitor.getShutdownHook().join();

    assertThat(p1.wasGracefullyTerminated()).isTrue();
    assertThat(p2.wasGracefullyTerminated()).isTrue();
  }

  @Test
  public void stop_all_processes_if_one_shutdowns() throws Exception {
    monitor = newDefaultMonitor();
    HttpProcessClient p1 = new HttpProcessClient("p1"), p2 = new HttpProcessClient("p2");
    monitor.start(Arrays.asList(p1.newCommand(), p2.newCommand()));
    assertThat(p1.isReady()).isTrue();
    assertThat(p2.isReady()).isTrue();

    // kill p1 -> waiting for detection by monitor than termination of p2
    p1.kill();
    monitor.awaitTermination();

    assertThat(p1.isReady()).isFalse();
    assertThat(p2.isReady()).isFalse();
    assertThat(p1.wasGracefullyTerminated()).isFalse();
    assertThat(p2.wasGracefullyTerminated()).isTrue();
  }

  @Test
  public void stop_all_processes_if_one_fails_to_start() throws Exception {
    monitor = newDefaultMonitor();
    HttpProcessClient p1 = new HttpProcessClient("p1"), p2 = new HttpProcessClient("p2", -1);
    try {
      monitor.start(Arrays.asList(p1.newCommand(), p2.newCommand()));
      fail();
    } catch (Exception expected) {
      assertThat(p1.wasReady()).isTrue();
      assertThat(p2.wasReady()).isFalse();
      assertThat(p1.wasGracefullyTerminated()).isTrue();
      // self "gracefully terminated", even if startup went bad
      assertThat(p2.wasGracefullyTerminated()).isTrue();
    }
  }

  @Test
  public void force_stop_if_too_long() throws Exception {
    // TODO
  }

  @Test
  public void fail_to_start_if_bad_class_name() throws Exception {
    monitor = newDefaultMonitor();
    JavaCommand command = new JavaCommand("test")
      .addClasspath(testJar.getAbsolutePath())
      .setClassName("org.sonar.process.test.Unknown")
      .setTempDir(temp.newFolder());

    try {
      monitor.start(Arrays.asList(command));
      fail();
    } catch (Exception e) {
      // expected
      // TODO improve, too many stacktraces logged
    }
  }

  private Monitor newDefaultMonitor() {
    Timeouts timeouts = new Timeouts();
    return new Monitor(new JavaProcessLauncher(timeouts), exit, new TerminatorThread(timeouts));
  }

  /**
   * Interaction with {@link org.sonar.process.test.HttpProcess}
   */
  private class HttpProcessClient {
    private final int httpPort;
    private final String commandKey;
    private final File tempDir;

    private HttpProcessClient(String commandKey) throws IOException {
      this(commandKey, NetworkUtils.freePort());
    }

    /**
     * Use httpPort=-1 to make server fail to start
     */
    private HttpProcessClient(String commandKey, int httpPort) throws IOException {
      this.commandKey = commandKey;
      this.tempDir = temp.newFolder(commandKey);
      this.httpPort = httpPort;
    }

    JavaCommand newCommand() throws IOException {
      return new JavaCommand(commandKey)
        .addClasspath(testJar.getAbsolutePath())
        .setClassName("org.sonar.process.test.HttpProcess")
        .setArgument("httpPort", String.valueOf(httpPort))
        .setTempDir(tempDir);
    }

    /**
     * @see org.sonar.process.test.HttpProcess
     */
    boolean isReady() {
      try {
        HttpRequest httpRequest = HttpRequest.get("http://localhost:" + httpPort + "/ping")
          .readTimeout(2000).connectTimeout(2000);
        return httpRequest.ok() && httpRequest.body().equals("ping");
      } catch (HttpRequest.HttpRequestException e) {
        return false;
      }
    }

    /**
     * @see org.sonar.process.test.HttpProcess
     */
    void kill() {
      try {
        HttpRequest.post("http://localhost:" + httpPort + "/kill")
          .readTimeout(5000).connectTimeout(5000).ok();
      } catch (Exception e) {
        // HTTP request can't be fully processed, as web server hardly
        // calls "System.exit()"
      }
    }

    /**
     * @see org.sonar.process.test.HttpProcess
     */
    boolean wasGracefullyTerminated() {
      return fileExists("terminatedAt");
    }

    long wasStartingAt() throws IOException {
      return readTimeFromFile("startingAt");
    }

    long wasGracefullyTerminatedAt() throws IOException {
      return readTimeFromFile("terminatedAt");
    }

    boolean wasReady() throws IOException {
      return fileExists("readyAt");
    }

    long wasReadyAt() throws IOException {
      return readTimeFromFile("readyAt");
    }

    private long readTimeFromFile(String filename) throws IOException {
      File file = new File(tempDir, filename);
      if (file.isFile() && file.exists()) {
        return Long.parseLong(FileUtils.readFileToString(file));
      }
      throw new IllegalStateException("File does not exist");
    }

    private boolean fileExists(String filename) {
      File file = new File(tempDir, filename);
      return file.isFile() && file.exists();
    }
  }

  private JavaCommand newStandardProcessCommand() throws IOException {
    return new JavaCommand("standard")
      .addClasspath(testJar.getAbsolutePath())
      .setClassName("org.sonar.process.test.StandardProcess")
      .setTempDir(temp.newFolder());
  }

}
TOP

Related Classes of org.sonar.process.monitor.MonitorTest$HttpProcessClient

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.