/*******************************************************************************
* Copyright (c) 2009, 2010 Sven Kiera
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*******************************************************************************/
package org.phpsrc.eclipse.pti.tools.phpunit.core.model;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.json.JSONException;
import org.json.JSONObject;
import org.phpsrc.eclipse.pti.ui.Logger;
public class JsonTestRunnerClient extends AbstractTestRunnerClient {
private static final String STATUS_PASS = "pass"; //$NON-NLS-1$
private static final String STATUS_FAIL = "fail"; //$NON-NLS-1$
private static final String KEY_SUITE = "suite"; //$NON-NLS-1$
private static final String KEY_TEST = "test"; //$NON-NLS-1$
private static final String KEY_TESTS = "tests"; //$NON-NLS-1$
private static final String KEY_EVENT = "event"; //$NON-NLS-1$
private static final String KEY_STATUS = "status"; //$NON-NLS-1$
private static final String KEY_TRACE = "trace"; //$NON-NLS-1$
private static final String KEY_MESSAGE = "message"; //$NON-NLS-1$
private static final String EVENT_SUITESTART = "suiteStart"; //$NON-NLS-1$
private static final String EVENT_TEST = "test"; //$NON-NLS-1$
private static final String EVENT_TESTSTART = "testStart"; //$NON-NLS-1$
public static final String PARAM_SEP = "<,>";
private StringBuilder outputCache;
private StringBuilder jsonOutputCache;
private boolean testRunStarted = false;
private boolean testStarted = false;
private String lastTestKey;
private Pattern errorPattern = Pattern.compile("Fatal error: .*");
private Pattern failedAssertingPattern = Pattern.compile(
"Failed asserting that (.*) is equal to (.*)\\.", Pattern.MULTILINE
| Pattern.DOTALL);
private int jsonObjectLevel = 0;
/**
* Start listening to a test run. Start a server connection that the
* RemoteTestRunner can connect to.
*
* @param listeners
* listeners to inform
* @param port
* port on which the server socket will be opened
*/
public synchronized void startListening(ITestRunListener[] listeners) {
outputCache = new StringBuilder();
super.startListening(listeners);
}
protected void parseOutput(String text) {
for (char c : text.toCharArray()) {
if (c == '{') {
if (jsonObjectLevel == 0) {
jsonOutputCache = new StringBuilder();
}
++jsonObjectLevel;
}
if (jsonOutputCache != null) {
jsonOutputCache.append(c);
if (c == '}') {
--jsonObjectLevel;
if (jsonObjectLevel == 0) {
parseJson(jsonOutputCache.toString());
jsonOutputCache = null;
}
}
} else {
outputCache.append(c);
}
}
}
private void parseJson(String json) {
try {
JSONObject jsonObj = new JSONObject(json);
if (jsonObj.has(KEY_EVENT)) {
String event = jsonObj.getString(KEY_EVENT);
if (EVENT_SUITESTART.equals(event)) {
for (ITestRunListener listener : fListeners) {
if (!testRunStarted) {
listener.testRunStarted(jsonObj.getInt(KEY_TESTS));
testRunStarted = true;
}
listener.testTreeEntry(jsonObj.getString(KEY_SUITE)
+ PARAM_SEP + jsonObj.getString(KEY_SUITE)
+ PARAM_SEP + "true" + PARAM_SEP
+ jsonObj.getInt(KEY_TESTS));
}
} else if (EVENT_TESTSTART.equals(event)) {
for (ITestRunListener listener : fListeners) {
startTest(listener, jsonObj.getString(KEY_TEST));
testStarted = true;
lastTestKey = jsonObj.getString(KEY_TEST);
}
} else if (EVENT_TEST.equals(event)) {
for (ITestRunListener listener : fListeners) {
if (!testStarted)
startTest(listener, jsonObj.getString(KEY_TEST));
else
testStarted = false;
String status = jsonObj.getString(KEY_STATUS);
if (STATUS_PASS.equals(status)) {
listener.testEnded(jsonObj.getString(KEY_TEST),
jsonObj.getString(KEY_TEST));
} else {
int statusCode = STATUS_FAIL.equals(status) ? ITestRunListener.STATUS_FAILURE
: ITestRunListener.STATUS_ERROR;
String expected = "";
String actual = "";
String msg = jsonObj.getString(KEY_MESSAGE).trim();
Matcher m = failedAssertingPattern.matcher(msg);
if (m.matches()) {
expected = m.group(2);
actual = m.group(1);
}
listener.testFailed(statusCode,
jsonObj.getString(KEY_TEST),
jsonObj.getString(KEY_TEST), msg, expected,
actual);
}
}
}
}
} catch (JSONException e) {
Logger.logException(e);
}
}
private void startTest(ITestRunListener listener, String key) {
listener.testTreeEntry(key + PARAM_SEP + key + PARAM_SEP + "false"
+ PARAM_SEP + "0");
listener.testStarted(key, key);
}
protected void notifyTestRunEnded(long elapsedTime) {
if (testStarted) {
StringBuilder error = new StringBuilder();
Matcher m = errorPattern.matcher(outputCache.toString());
while (m.find()) {
if (error.length() > 0)
error.append('\n');
error.append(m.group().trim());
}
for (ITestRunListener listener : fListeners) {
listener.testFailed(ITestRunListener.STATUS_ERROR, lastTestKey,
lastTestKey, error.toString(), null, null);
}
}
super.notifyTestRunEnded(elapsedTime);
}
}