diff -r 000000000000 -r 6474c204b198 mobile/android/base/tests/helpers/JavascriptMessageParser.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mobile/android/base/tests/helpers/JavascriptMessageParser.java Wed Dec 31 06:09:35 2014 +0100 @@ -0,0 +1,100 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +package org.mozilla.gecko.tests.helpers; + +import org.mozilla.gecko.Assert; + +import junit.framework.AssertionFailedError; + +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +/** + * Route messages from Javascript's head.js test framework into Java's + * Mochitest framework. + */ +public final class JavascriptMessageParser { + + /** + * The Javascript test harness sends test events to Java. + * Each such test event is wrapped in a Robocop:JS event. + */ + public static final String EVENT_TYPE = "Robocop:JS"; + + // Messages matching this pattern are handled specially. Messages not + // matching this pattern are still printed. This pattern should be able + // to handle having multiple lines in a message. + private static final Pattern testMessagePattern = + Pattern.compile("TEST-([A-Z\\-]+) \\| (.*?) \\| (.*)", Pattern.DOTALL); + + private final Assert asserter; + // Used to help print stack traces neatly. + private String lastTestName = ""; + // Have we seen a message saying the test is finished? + private boolean testFinishedMessageSeen = false; + private final boolean endOnAssertionFailure; + + /** + * Constructs a message parser for test result messages sent from JavaScript. When seeing an + * assertion failure, the message parser can use the given {@link org.mozilla.gecko.Assert} + * instance to immediately end the test (typically if the underlying JS framework is not able + * to end the test itself) or to swallow the Errors - this functionality is determined by the + * endOnAssertionFailure parameter. + * + * @param asserter The Assert instance to which test results should be passed. + * @param endOnAssertionFailure + * true if the test should end if we see a JS assertion failure, false otherwise. + */ + public JavascriptMessageParser(final Assert asserter, final boolean endOnAssertionFailure) { + this.asserter = asserter; + this.endOnAssertionFailure = endOnAssertionFailure; + } + + public boolean isTestFinished() { + return testFinishedMessageSeen; + } + + public void logMessage(final String str) { + final Matcher m = testMessagePattern.matcher(str.trim()); + + if (m.matches()) { + final String type = m.group(1); + final String name = m.group(2); + final String message = m.group(3); + + if ("INFO".equals(type)) { + asserter.info(name, message); + testFinishedMessageSeen = testFinishedMessageSeen || + "exiting test".equals(message); + } else if ("PASS".equals(type)) { + asserter.ok(true, name, message); + } else if ("UNEXPECTED-FAIL".equals(type)) { + try { + asserter.ok(false, name, message); + } catch (AssertionFailedError e) { + // Above, we call the assert, allowing it to log. + // Now we can end the test, if applicable. + if (this.endOnAssertionFailure) { + throw e; + } + // Otherwise, swallow the Error. The JS framework we're + // logging messages from is likely capable of ending tests + // when it needs to, and we want to see all of its failures, + // not just the first one! + } + } else if ("KNOWN-FAIL".equals(type)) { + asserter.todo(false, name, message); + } else if ("UNEXPECTED-PASS".equals(type)) { + asserter.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. + asserter.info(lastTestName, str.trim()); + } + } +}