package com.google.jstestdriver.idea;
import com.google.jstestdriver.idea.execution.tree.JstdTestRunnerFailure;
import com.google.jstestdriver.idea.execution.tree.RemoteTestListener;
import com.google.jstestdriver.idea.execution.tree.TestResultProtocolMessage;
import com.google.jstestdriver.idea.util.CastUtils;
import com.intellij.openapi.application.ApplicationManager;
import java.io.EOFException;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.concurrent.CountDownLatch;
/**
* The IDE end of the socket communication from the TestRunner. Should be run in a background thread. When data is
* available on the socket, it will be read and trigger an event on the RemoteTestListener (on the AWT event thread),
* passing the deserialized test result.
*
* @author alexeagle@google.com (Alex Eagle)
*/
public class RemoteTestResultReceiver implements Runnable {
private final RemoteTestListener listener;
private final int port;
private final CountDownLatch serverStarted;
public RemoteTestResultReceiver(RemoteTestListener listener, int port, CountDownLatch serverStarted) {
this.listener = listener;
this.port = port;
this.serverStarted = serverStarted;
}
/**
* Create a socket, and read all the TestResultProtocolMessage objects which the TestRunner process has written to us.
* When we reach the end of the communication, notify the listener that it may shutdown.
*/
@Override public void run() {
Socket client = null;
Exception savedException = null;
ServerSocket serverSocket = null;
try {
serverSocket = new ServerSocket(port);
serverStarted.countDown();
client = serverSocket.accept();
ObjectInputStream in = new ObjectInputStream(client.getInputStream());
readTestResults(in);
} catch (Exception e) {
savedException = e;
} finally {
if (client != null) {
try {
// also closes client socket input stream
client.close();
} catch (Exception e) {
if (savedException != null) {
savedException = e;
}
}
}
if (serverSocket != null) {
try {
serverSocket.close();
} catch (Exception e) {
if (savedException != null) {
savedException = e;
}
}
}
}
if (savedException != null) {
savedException.printStackTrace();
throw new RuntimeException(savedException);
}
}
private void readTestResults(ObjectInputStream in) throws IOException {
while (true) {
try {
Object obj = in.readObject();
final TestResultProtocolMessage message = CastUtils.tryCast(obj, TestResultProtocolMessage.class);
if (message != null) {
ApplicationManager.getApplication().invokeLater(new Runnable() {
@Override
public void run() {
if (message.isDryRun()) {
listener.onTestStarted(message);
} else {
listener.onTestFinished(message);
}
}
});
}
final JstdTestRunnerFailure testRunnerFailure = CastUtils.tryCast(obj, JstdTestRunnerFailure.class);
if (testRunnerFailure != null) {
ApplicationManager.getApplication().invokeLater(new Runnable() {
@Override
public void run() {
listener.onTestRunnerFailed(testRunnerFailure);
}
});
}
} catch (EOFException e) {
break;
} catch (Exception e) {
throw new RuntimeException("Problem in communication with TestRunner process", e);
}
}
}
}