/*
* The MIT License
*
* Copyright 2013 Sony Mobile Communications AB. All rights reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package com.sonymobile.tools.gerrit.gerritevents.watchdog;
import com.jcraft.jsch.JSchException;
import com.sonymobile.tools.gerrit.gerritevents.ConnectionListener;
import com.sonymobile.tools.gerrit.gerritevents.GerritConnection;
import com.sonymobile.tools.gerrit.gerritevents.GerritHandler;
import com.sonymobile.tools.gerrit.gerritevents.ssh.Authentication;
import com.sonymobile.tools.gerrit.gerritevents.mock.SshdServerMock;
import org.apache.sshd.SshServer;
import org.apache.sshd.server.Environment;
import org.junit.BeforeClass;
import org.junit.Test;
import java.io.IOException;
import java.util.Collections;
import static com.sonymobile.tools.gerrit.gerritevents.mock.SshdServerMock.GERRIT_STREAM_EVENTS;
import static java.util.concurrent.TimeUnit.MINUTES;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
//CS IGNORE MagicNumber FOR NEXT 200 LINES. REASON: TestData
/**
* Tests for {@link StreamWatchdog}.
*
* @author Robert Sandell <robert.sandell@sonymobile.com>
*/
public class StreamWatchdogTest {
private static int sshPort;
/**
* Setup before all tests.
*/
@BeforeClass
public static void setUp() {
try {
sshPort = new Integer(System.getProperty("gerrit.ssh.port"));
} catch (Exception ex) {
sshPort = SshdServerMock.GERRIT_SSH_PORT;
}
}
/**
* Tests that the {@link StreamWatchdog} actually performs a restart of the connection.
*
* @throws IOException if so.
* @throws InterruptedException if so.
* @throws NoSuchMethodException if so.
* @throws JSchException if so.
*/
@Test(timeout = 2 * 60 * 60 * 1000)
public void testFullTimeoutFlow() throws IOException, InterruptedException, NoSuchMethodException, JSchException {
System.out.println("====This will be a long running test ca. 2 minutes=====");
SshdServerMock.KeyPairFiles sshKey = SshdServerMock.generateKeyPair();
SshdServerMock server = new SshdServerMock();
SshServer sshd = SshdServerMock.startServer(sshPort, server);
server.returnCommandFor("gerrit version", SshdServerMock.EofCommandMock.class);
server.returnCommandFor("gerrit ls-projects", SshdServerMock.EofCommandMock.class);
server.returnCommandFor(GERRIT_STREAM_EVENTS, WaitLongTimeCommand.class, true,
new Object[]{MINUTES.toMillis(5)}, new Class<?>[]{Long.class});
server.returnCommandFor(GERRIT_STREAM_EVENTS, SshdServerMock.CommandMock.class);
GerritConnection connection = new GerritConnection("", "localhost", sshPort, "", "",
new Authentication(sshKey.getPrivateKey(), "jenkins"), 20,
new WatchTimeExceptionData(new int[0], Collections.<WatchTimeExceptionData.TimeSpan>emptyList()));
Listen connectionListener = new Listen();
connection.addListener(connectionListener);
GerritHandler handler = new GerritHandler();
connection.setHandler(handler);
Thread connectionThread = new Thread(connection);
connectionThread.start();
server.waitForCommand(GERRIT_STREAM_EVENTS, 8000);
Thread.sleep(2000);
assertTrue(connectionListener.isConnectionEstablished());
//wait for the connection to go down.
connectionListener.waitForConnectionDown();
server.waitForCommand(GERRIT_STREAM_EVENTS, 8000);
Thread.sleep(1000);
assertTrue(connectionListener.isConnectionEstablished());
assertEquals(1, connection.getReconnectCallCount());
System.out.println("====Shutting down GerritConnection=====");
connection.shutdown(true);
System.out.println("====Shutting down GerritHandler=====");
handler.shutdown(true);
System.out.println("====Shutting down SSHD=====");
sshd.stop(true);
System.out.println("====Done=====");
}
/**
* ConnectionListener to help with the testing to see that the connection actually goes down and up.
*/
public static class Listen implements ConnectionListener {
boolean connectionEstablished = false;
boolean connectionDown = true;
@Override
public synchronized void connectionEstablished() {
connectionEstablished = true;
connectionDown = false;
this.notifyAll();
}
@Override
public synchronized void connectionDown() {
connectionDown = true;
connectionEstablished = false;
this.notifyAll();
}
/**
* If the connection is established. I.e. if {@link #connectionEstablished()} has just been called.
*
* @return true if so.
*/
public synchronized boolean isConnectionEstablished() {
return connectionEstablished;
}
/**
* If the connection is down. I.e. if {@link #connectionDown()} has just been called.
*
* @return true if so.
*/
public synchronized boolean isConnectionDown() {
return connectionDown;
}
/**
* Waits for the {@link #connectionDown()} signal and returns after that.
*
* @throws InterruptedException if so.
*/
public void waitForConnectionDown() throws InterruptedException {
System.out.println("Waiting for connection to go down...");
if (isConnectionDown()) {
System.out.println("Connection is down!");
return;
}
while (!isConnectionDown()) {
synchronized (this) {
this.wait(1000);
}
}
System.out.println("Connection is down!");
}
}
/**
* A SSH command that sleeps for a specified period of time.
*/
public static class WaitLongTimeCommand extends SshdServerMock.CommandMock implements Runnable {
private long timeout;
private Thread thread;
/**
* Standard constructor. Will sleep for two minutes.
*
* @param command the command to "execute".
*/
public WaitLongTimeCommand(String command) {
super(command);
timeout = MINUTES.toMillis(2);
}
/**
* Standard constructor.
*
* @param command the command to "execute".
* @param timeout millis to sleep for.
*/
public WaitLongTimeCommand(String command, Long timeout) {
super(command);
this.timeout = timeout;
}
@Override
public void start(Environment environment) throws IOException {
thread = new Thread(this, "WaitLongTimeCommand " + this.command);
thread.setDaemon(true);
thread.start();
}
@Override
public void run() {
System.out.println("WaitLongTimeCommand starting...");
try {
Thread.sleep(timeout);
} catch (InterruptedException e) {
System.err.println("WaitLongTimeCommand interrupted!");
}
System.out.println("WaitLongTimeCommand finished.");
stop(0);
}
}
}