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