package org.mozilla.gecko.tests; import org.mozilla.gecko.*; import android.util.Log; import java.util.regex.Matcher; import java.util.regex.Pattern; import junit.framework.AssertionFailedError; import org.json.JSONArray; import org.json.JSONException; import org.json.JSONObject; public class JavascriptTest extends BaseTest { public static final String LOGTAG = "JavascriptTest"; public final String javascriptUrl; public JavascriptTest(String javascriptUrl) { super(); this.javascriptUrl = javascriptUrl; } @Override protected int getTestType() { return TEST_MOCHITEST; } /** * Route messages from Javascript's head.js test framework into Java's * Mochitest framework. */ protected static class JavascriptTestMessageParser { // Messages matching this pattern are handled specially. Messages not // matching this pattern are still printed. private static final Pattern testMessagePattern = Pattern.compile("\n+TEST-(.*) \\| (.*) \\| (.*)\n*"); private final Assert mAsserter; // Used to help print stack traces neatly. private String lastTestName = ""; // Have we seen a message saying the test is finished? private boolean testFinishedMessageSeen = false; public JavascriptTestMessageParser(final Assert asserter) { this.mAsserter = asserter; } private boolean testIsFinished() { return testFinishedMessageSeen; } private void logMessage(String str) { Matcher m = testMessagePattern.matcher(str); if (m.matches()) { String type = m.group(1); String name = m.group(2); String message = m.group(3); if ("INFO".equals(type)) { mAsserter.info(name, message); testFinishedMessageSeen = testFinishedMessageSeen || "exiting test".equals(message); } else if ("PASS".equals(type)) { mAsserter.ok(true, name, message); } else if ("UNEXPECTED-FAIL".equals(type)) { try { mAsserter.ok(false, name, message); } catch (junit.framework.AssertionFailedError e) { // Swallow this exception. We want to see all the // Javascript failures, not die on the very first one! } } else if ("KNOWN-FAIL".equals(type)) { mAsserter.todo(false, name, message); } else if ("UNEXPECTED-PASS".equals(type)) { mAsserter.todo(true, name, message); } lastTestName = name; } else { // Generally, these extra lines are stack traces from failures, // so we print them with the name of the last test seen. mAsserter.info(lastTestName, str.trim()); } } } public void testJavascript() throws Exception { blockForGeckoReady(); // We want to be waiting for Robocop messages before the page is loaded // because the test harness runs each test in the suite (and possibly // completes testing) before the page load event is fired. final Actions.EventExpecter expecter = mActions.expectGeckoEvent("Robocop:Status"); mAsserter.dumpLog("Registered listener for Robocop:Status"); final String url = getAbsoluteUrl("/robocop/robocop_javascript.html?path=" + javascriptUrl); mAsserter.dumpLog("Loading JavaScript test from " + url); loadUrl(url); final JavascriptTestMessageParser testMessageParser = new JavascriptTestMessageParser(mAsserter); try { while (true) { if (Log.isLoggable(LOGTAG, Log.VERBOSE)) { Log.v(LOGTAG, "Waiting for Robocop:Status"); } String data = expecter.blockForEventData(); if (Log.isLoggable(LOGTAG, Log.VERBOSE)) { Log.v(LOGTAG, "Got Robocop:Status with data '" + data + "'"); } JSONObject o = new JSONObject(data); String innerType = o.getString("innerType"); if (!"progress".equals(innerType)) { throw new Exception("Unexpected Robocop:Status innerType " + innerType); } String message = o.getString("message"); if (message == null) { throw new Exception("Robocop:Status progress message must not be null"); } testMessageParser.logMessage(message); if (testMessageParser.testIsFinished()) { if (Log.isLoggable(LOGTAG, Log.DEBUG)) { Log.d(LOGTAG, "Got test finished message"); } break; } } } finally { expecter.unregisterListener(); mAsserter.dumpLog("Unregistered listener for Robocop:Status"); } } }