/* 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.goanna.tests.helpers; import org.mozilla.goanna.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.goanna.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()); } } }