git-svn-id: https://svn.apache.org/repos/asf/ant/core/trunk@271354 13f79535-47bb-0310-9956-ffa450edef68master
@@ -69,13 +69,15 @@ public class BriefFormatter extends SummaryFormatter { | |||
ResourceManager.getPackageResources(BriefFormatter.class); | |||
public void onTestFailure(TestRunEvent evt) { | |||
String msg = RES.getString("brief.status-failure.msg", evt.getName(), evt.getStackTrace()); | |||
String msg = RES.getString("brief.status-failure.msg", evt.getName(), | |||
evt.getError().getStackTrace()); | |||
getWriter().println(msg); | |||
super.onTestFailure(evt); | |||
} | |||
public void onTestError(TestRunEvent evt) { | |||
String msg = RES.getString("brief.status-error.msg", evt.getName(), evt.getStackTrace()); | |||
String msg = RES.getString("brief.status-error.msg", evt.getName(), | |||
evt.getError().getStackTrace()); | |||
getWriter().println(msg); | |||
super.onTestError(evt); | |||
} | |||
@@ -57,6 +57,7 @@ import java.util.StringTokenizer; | |||
import org.apache.tools.ant.util.StringUtils; | |||
import org.apache.tools.ant.taskdefs.optional.rjunit.remote.TestRunEvent; | |||
import org.apache.tools.ant.taskdefs.optional.rjunit.remote.ExceptionData; | |||
/** | |||
* Filtered Formatter that strips out unwanted stack frames from the full | |||
@@ -103,17 +104,24 @@ public class FilterStackFormatter extends FilterFormatter { | |||
} | |||
public void onTestFailure(TestRunEvent evt) { | |||
String filteredTrace = filter(evt.getStackTrace()); | |||
evt.setStackTrace(filteredTrace); | |||
filterEvent(evt); | |||
super.onTestFailure(evt); | |||
} | |||
public void onTestError(TestRunEvent evt) { | |||
String filteredTrace = filter(evt.getStackTrace()); | |||
evt.setStackTrace(filteredTrace); | |||
filterEvent(evt); | |||
super.onTestFailure(evt); | |||
} | |||
protected void filterEvent(TestRunEvent evt){ | |||
String filteredTrace = filter(evt.getError().getStackTrace()); | |||
ExceptionData error = new ExceptionData( | |||
evt.getError().getType(), | |||
evt.getError().getMessage(), | |||
filteredTrace); | |||
evt.setError(error); | |||
} | |||
protected String filter(String trace){ | |||
StringTokenizer st = new StringTokenizer(trace, "\r\n"); | |||
StringBuffer buf = new StringBuffer(trace.length()); | |||
@@ -84,12 +84,12 @@ public class PlainFormatter extends BaseStreamFormatter { | |||
public void onTestFailure(TestRunEvent evt) { | |||
getWriter().println(" failure: " + evt.getName()); | |||
getWriter().println(evt.getStackTrace()); | |||
getWriter().println(evt.getError().getStackTrace()); | |||
} | |||
public void onTestError(TestRunEvent evt) { | |||
getWriter().println(" error: " + evt.getName()); | |||
getWriter().println(evt.getStackTrace()); | |||
getWriter().println(evt.getError().getStackTrace()); | |||
} | |||
public void onRunEnded(TestRunEvent evt) { | |||
@@ -57,6 +57,7 @@ import java.io.IOException; | |||
import java.util.Enumeration; | |||
import java.util.Hashtable; | |||
import java.util.Properties; | |||
import java.util.Date; | |||
import javax.xml.parsers.DocumentBuilder; | |||
import javax.xml.parsers.DocumentBuilderFactory; | |||
@@ -68,7 +69,10 @@ import org.apache.tools.ant.BuildException; | |||
import org.apache.tools.ant.taskdefs.optional.rjunit.JUnitHelper; | |||
import org.apache.tools.ant.taskdefs.optional.rjunit.remote.TestRunEvent; | |||
import org.apache.tools.ant.taskdefs.optional.rjunit.remote.TestSummary; | |||
import org.apache.tools.ant.taskdefs.optional.rjunit.remote.ExceptionData; | |||
import org.apache.tools.ant.util.DOMElementWriter; | |||
import org.apache.tools.ant.util.DateUtils; | |||
import org.apache.tools.ant.util.StringUtils; | |||
/** | |||
* XML Formatter. Due to the nature of the XML we are forced to store | |||
@@ -153,6 +157,7 @@ public class XMLFormatter extends BaseStreamFormatter { | |||
private Element lastTestElement = null; | |||
private TestRunEvent lastTestEvent = null; | |||
private Element lastSuiteElement = null; | |||
private long programStart; | |||
public void onSuiteStarted(TestRunEvent evt) { | |||
String fullclassname = evt.getName(); | |||
@@ -180,10 +185,12 @@ public class XMLFormatter extends BaseStreamFormatter { | |||
} | |||
public void onRunEnded(TestRunEvent evt) { | |||
final String elapsedTime = String.valueOf(evt.getTimeStamp() - programStart); | |||
rootElement.setAttribute("elapsed_time", elapsedTime); | |||
// Output properties | |||
Element propsElement = doc.createElement(PROPERTIES); | |||
final Element propsElement = doc.createElement(PROPERTIES); | |||
rootElement.appendChild(propsElement); | |||
Properties props = evt.getProperties(); | |||
final Properties props = evt.getProperties(); | |||
if (props != null) { | |||
Enumeration e = props.propertyNames(); | |||
while (e.hasMoreElements()) { | |||
@@ -198,11 +205,13 @@ public class XMLFormatter extends BaseStreamFormatter { | |||
} | |||
public void onRunStarted(TestRunEvent evt) { | |||
// | |||
programStart = evt.getTimeStamp(); | |||
final String date = DateUtils.format(programStart, DateUtils.ISO8601_DATETIME_PATTERN); | |||
rootElement.setAttribute("program_start", date); | |||
} | |||
public void onRunStopped(TestRunEvent evt) { | |||
// add a stop attribute ? | |||
rootElement.setAttribute("stopped", "true"); | |||
onRunEnded(evt); | |||
} | |||
@@ -242,22 +251,21 @@ public class XMLFormatter extends BaseStreamFormatter { | |||
String type = evt.getType() == TestRunEvent.TEST_FAILURE ? FAILURE : ERROR; | |||
Element nested = doc.createElement(type); | |||
lastTestElement.appendChild(nested); | |||
String[] args = parseFirstLine(evt.getStackTrace()); | |||
if (args[1] != null && args[1].length() > 0) { | |||
nested.setAttribute(ATTR_MESSAGE, args[1]); | |||
} | |||
nested.setAttribute(ATTR_TYPE, args[0]); | |||
Text text = doc.createTextNode(evt.getStackTrace()); | |||
ExceptionData error = evt.getError(); | |||
nested.setAttribute(ATTR_MESSAGE, error.getMessage()); | |||
nested.setAttribute(ATTR_TYPE, error.getType()); | |||
Text text = doc.createTextNode(error.getStackTrace()); | |||
nested.appendChild(text); | |||
onTestEnded(evt); | |||
} | |||
protected void close() { | |||
DOMElementWriter domWriter = new DOMElementWriter(); | |||
// the underlying writer uses UTF8 encoding | |||
getWriter().println("<?xml version=\"1.0\" encoding=\"UTF-8\" ?>"); | |||
getWriter().println("<?xml version='1.0' encoding='UTF-8' ?>"); | |||
String now = DateUtils.format(new Date(), DateUtils.ISO8601_DATETIME_PATTERN); | |||
rootElement.setAttribute("snapshot_created", now); | |||
try { | |||
final DOMElementWriter domWriter = new DOMElementWriter(); | |||
domWriter.write(rootElement, getWriter(), 0, " "); | |||
} catch (IOException e) { | |||
throw new BuildException(e); | |||
@@ -274,18 +282,4 @@ public class XMLFormatter extends BaseStreamFormatter { | |||
} | |||
} | |||
protected static String[] parseFirstLine(String trace) { | |||
int pos = trace.indexOf('\n'); | |||
if (pos == -1) { | |||
return new String[]{trace, ""}; | |||
} | |||
String line = trace.substring(0, pos); | |||
pos = line.indexOf(": "); | |||
if (pos != -1) { | |||
String classname = line.substring(0, pos).trim(); | |||
String message = line.substring(pos + 1).trim(); | |||
return new String[]{classname, message}; | |||
} | |||
return new String[]{trace, ""}; | |||
} | |||
} |
@@ -53,142 +53,127 @@ | |||
*/ | |||
package org.apache.tools.ant.taskdefs.optional.rjunit.remote; | |||
import java.util.Vector; | |||
import java.io.InputStream; | |||
import java.io.IOException; | |||
import java.io.BufferedReader; | |||
import java.io.InputStreamReader; | |||
import java.util.ArrayList; | |||
import java.util.HashMap; | |||
/** | |||
* Dispatch messages to appropriate listener methode based on event id. | |||
* | |||
* @author <a href="mailto:sbailliez@apache.org">Stephane Bailliez</a> | |||
*/ | |||
public class EventDispatcher { | |||
private final static HashMap eventMap = new HashMap(3); | |||
static { | |||
registerDefaults(); | |||
} | |||
/** the set of registered listeners */ | |||
private Vector listeners = new Vector(); | |||
private ArrayList listeners = new ArrayList(); | |||
/** | |||
/** | |||
* Add a new listener. | |||
* @param listener a listener that will receive events from the client. | |||
*/ | |||
public void addListener(TestRunListener listener) { | |||
listeners.addElement(listener); | |||
listeners.add(listener); | |||
} | |||
public void removeListener(org.apache.tools.ant.taskdefs.optional.rjunit.remote.TestRunListener listener) { | |||
listeners.removeElement(listener); | |||
public void removeListener(TestRunListener listener) { | |||
listeners.remove(listener); | |||
} | |||
/** | |||
/** | |||
* Process a message from the client and dispatch the | |||
* appropriate message to the listeners. | |||
*/ | |||
public void dispatchEvent(TestRunEvent evt) { | |||
// I hate switch/case but no need to design a complex | |||
// system for limited events. | |||
switch (evt.getType()){ | |||
case TestRunEvent.RUN_STARTED: | |||
fireRunStarted(evt); | |||
break; | |||
case TestRunEvent.RUN_ENDED: | |||
fireRunEnded(evt); | |||
break; | |||
case TestRunEvent.RUN_STOPPED: | |||
fireRunStopped(evt); | |||
break; | |||
case TestRunEvent.TEST_STARTED: | |||
fireTestStarted(evt); | |||
break; | |||
case TestRunEvent.TEST_ERROR: | |||
fireTestError(evt); | |||
break; | |||
case TestRunEvent.TEST_FAILURE: | |||
fireTestFailure(evt); | |||
break; | |||
case TestRunEvent.TEST_ENDED: | |||
fireTestEnded(evt); | |||
break; | |||
case TestRunEvent.SUITE_ENDED: | |||
fireSuiteEnded(evt); | |||
break; | |||
case TestRunEvent.SUITE_STARTED: | |||
fireSuiteStarted(evt); | |||
break; | |||
default: | |||
// should not happen | |||
final Integer type = new Integer(evt.getType()); | |||
final EventAction action = (EventAction) eventMap.get(type); | |||
if (action == null) { | |||
return; | |||
} | |||
} | |||
protected void fireRunStarted(TestRunEvent evt) { | |||
synchronized (listeners) { | |||
for (int i = 0; i < listeners.size(); i++) { | |||
((org.apache.tools.ant.taskdefs.optional.rjunit.remote.TestRunListener) listeners.elementAt(i)).onRunStarted(evt); | |||
final int count = listeners.size(); | |||
for (int i = 0; i < count; i++) { | |||
TestRunListener listener = (TestRunListener) listeners.get(i); | |||
action.dispatch(listener, evt); | |||
} | |||
} | |||
} | |||
protected void fireRunEnded(TestRunEvent evt) { | |||
synchronized (listeners) { | |||
for (int i = 0; i < listeners.size(); i++) { | |||
((org.apache.tools.ant.taskdefs.optional.rjunit.remote.TestRunListener) listeners.elementAt(i)).onRunEnded(evt); | |||
} | |||
private static void registerDefaults() { | |||
registerAction(TestRunEvent.RUN_STARTED, new RunStartedAction()); | |||
registerAction(TestRunEvent.RUN_ENDED, new RunEndedAction()); | |||
registerAction(TestRunEvent.TEST_STARTED, new TestStartedAction()); | |||
registerAction(TestRunEvent.TEST_ENDED, new TestEndedAction()); | |||
registerAction(TestRunEvent.TEST_FAILURE, new TestFailureAction()); | |||
registerAction(TestRunEvent.TEST_ERROR, new TestErrorAction()); | |||
registerAction(TestRunEvent.SUITE_STARTED, new SuiteStartedAction()); | |||
registerAction(TestRunEvent.SUITE_ENDED, new SuiteEndedAction()); | |||
registerAction(TestRunEvent.RUN_STOPPED, new RunStoppedAction()); | |||
} | |||
private static void registerAction(int id, EventAction action){ | |||
eventMap.put(new Integer(id), action); | |||
} | |||
public interface EventAction { | |||
public void dispatch(TestRunListener listener, TestRunEvent evt); | |||
} | |||
private static class RunStartedAction implements EventAction { | |||
public void dispatch(TestRunListener listener, TestRunEvent evt) { | |||
listener.onRunStarted(evt); | |||
} | |||
} | |||
protected void fireTestStarted(TestRunEvent evt) { | |||
synchronized (listeners) { | |||
for (int i = 0; i < listeners.size(); i++) { | |||
((org.apache.tools.ant.taskdefs.optional.rjunit.remote.TestRunListener) listeners.elementAt(i)).onTestStarted(evt); | |||
} | |||
private static class RunEndedAction implements EventAction { | |||
public void dispatch(TestRunListener listener, TestRunEvent evt) { | |||
listener.onRunEnded(evt); | |||
} | |||
} | |||
protected void fireTestEnded(TestRunEvent evt) { | |||
synchronized (listeners) { | |||
for (int i = 0; i < listeners.size(); i++) { | |||
((org.apache.tools.ant.taskdefs.optional.rjunit.remote.TestRunListener) listeners.elementAt(i)).onTestEnded(evt); | |||
} | |||
private static class TestStartedAction implements EventAction { | |||
public void dispatch(TestRunListener listener, TestRunEvent evt) { | |||
listener.onTestStarted(evt); | |||
} | |||
} | |||
protected void fireTestFailure(TestRunEvent evt) { | |||
synchronized (listeners) { | |||
for (int i = 0; i < listeners.size(); i++) { | |||
((org.apache.tools.ant.taskdefs.optional.rjunit.remote.TestRunListener) listeners.elementAt(i)).onTestFailure(evt); | |||
} | |||
private static class TestEndedAction implements EventAction { | |||
public void dispatch(TestRunListener listener, TestRunEvent evt) { | |||
listener.onTestEnded(evt); | |||
} | |||
} | |||
protected void fireTestError(TestRunEvent evt) { | |||
synchronized (listeners) { | |||
for (int i = 0; i < listeners.size(); i++) { | |||
((org.apache.tools.ant.taskdefs.optional.rjunit.remote.TestRunListener) listeners.elementAt(i)).onTestError(evt); | |||
} | |||
private static class TestFailureAction implements EventAction { | |||
public void dispatch(TestRunListener listener, TestRunEvent evt) { | |||
listener.onTestFailure(evt); | |||
} | |||
} | |||
protected void fireSuiteStarted(TestRunEvent evt) { | |||
synchronized (listeners) { | |||
for (int i = 0; i < listeners.size(); i++) { | |||
((org.apache.tools.ant.taskdefs.optional.rjunit.remote.TestRunListener) listeners.elementAt(i)).onSuiteStarted(evt); | |||
} | |||
private static class TestErrorAction implements EventAction { | |||
public void dispatch(TestRunListener listener, TestRunEvent evt) { | |||
listener.onTestError(evt); | |||
} | |||
} | |||
protected void fireSuiteEnded(TestRunEvent evt) { | |||
synchronized (listeners) { | |||
for (int i = 0; i < listeners.size(); i++) { | |||
((org.apache.tools.ant.taskdefs.optional.rjunit.remote.TestRunListener) listeners.elementAt(i)).onSuiteEnded(evt); | |||
} | |||
private static class SuiteStartedAction implements EventAction { | |||
public void dispatch(TestRunListener listener, TestRunEvent evt) { | |||
listener.onSuiteStarted(evt); | |||
} | |||
} | |||
protected void fireRunStopped(TestRunEvent evt) { | |||
synchronized (listeners) { | |||
for (int i = 0; i < listeners.size(); i++) { | |||
((org.apache.tools.ant.taskdefs.optional.rjunit.remote.TestRunListener) listeners.elementAt(i)).onRunStopped(evt); | |||
} | |||
private static class SuiteEndedAction implements EventAction { | |||
public void dispatch(TestRunListener listener, TestRunEvent evt) { | |||
listener.onSuiteEnded(evt); | |||
} | |||
} | |||
private static class RunStoppedAction implements EventAction { | |||
public void dispatch(TestRunListener listener, TestRunEvent evt) { | |||
listener.onRunStopped(evt); | |||
} | |||
} | |||
@@ -0,0 +1,133 @@ | |||
/* | |||
* The Apache Software License, Version 1.1 | |||
* | |||
* Copyright (c) 2002 The Apache Software Foundation. All rights | |||
* reserved. | |||
* | |||
* Redistribution and use in source and binary forms, with or without | |||
* modification, are permitted provided that the following conditions | |||
* are met: | |||
* | |||
* 1. Redistributions of source code must retain the above copyright | |||
* notice, this list of conditions and the following disclaimer. | |||
* | |||
* 2. Redistributions in binary form must reproduce the above copyright | |||
* notice, this list of conditions and the following disclaimer in | |||
* the documentation and/or other materials provided with the | |||
* distribution. | |||
* | |||
* 3. The end-user documentation included with the redistribution, if | |||
* any, must include the following acknowlegement: | |||
* "This product includes software developed by the | |||
* Apache Software Foundation (http://www.apache.org/)." | |||
* Alternately, this acknowlegement may appear in the software itself, | |||
* if and wherever such third-party acknowlegements normally appear. | |||
* | |||
* 4. The names "The Jakarta Project", "Ant", and "Apache Software | |||
* Foundation" must not be used to endorse or promote products derived | |||
* from this software without prior written permission. For written | |||
* permission, please contact apache@apache.org. | |||
* | |||
* 5. Products derived from this software may not be called "Apache" | |||
* nor may "Apache" appear in their names without prior written | |||
* permission of the Apache Group. | |||
* | |||
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED | |||
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES | |||
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE | |||
* DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR | |||
* ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | |||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT | |||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF | |||
* USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND | |||
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, | |||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT | |||
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF | |||
* SUCH DAMAGE. | |||
* ==================================================================== | |||
* | |||
* This software consists of voluntary contributions made by many | |||
* individuals on behalf of the Apache Software Foundation. For more | |||
* information on the Apache Software Foundation, please see | |||
* <http://www.apache.org/>. | |||
*/ | |||
package org.apache.tools.ant.taskdefs.optional.rjunit.remote; | |||
import java.io.Serializable; | |||
import org.apache.tools.ant.util.StringUtils; | |||
/** | |||
* A wrapper around an exception since an exception stacktrace is | |||
* not serializable. | |||
* | |||
* @author <a href="mailto:sbailliez@apache.org">Stephane Bailliez</a> | |||
*/ | |||
public class ExceptionData implements Serializable { | |||
/** the stacktrace of the exception */ | |||
private final String stacktrace; | |||
/** the classname of an exception */ | |||
private final String type; | |||
/** the message associated to this exception */ | |||
private final String message; | |||
/** | |||
* Create a new error. | |||
* @param exception the exception to run as | |||
*/ | |||
public ExceptionData(Throwable exception) { | |||
this(exception.getClass().getName(), | |||
exception.getMessage(), | |||
StringUtils.getStackTrace(exception)); | |||
} | |||
/** | |||
* Create a new error. | |||
* @param type the type of the error (ie classname). | |||
* @param message the message associated to this error. | |||
* @param stacktrace the full stacktrace of this error. | |||
*/ | |||
public ExceptionData(String type, String message, String stacktrace) { | |||
this.stacktrace = stacktrace; | |||
this.type = type; | |||
this.message = message; | |||
} | |||
/** | |||
* @return the type of the error (ie classname) | |||
*/ | |||
public String getType() { | |||
return type; | |||
} | |||
/** | |||
* @return the message associated to this error. | |||
*/ | |||
public String getMessage() { | |||
return message; | |||
} | |||
/** | |||
* @return the stacktrace for this error. | |||
*/ | |||
public String getStackTrace() { | |||
return stacktrace; | |||
} | |||
public boolean equals(Object o){ | |||
if ( o instanceof ExceptionData ){ | |||
ExceptionData other = (ExceptionData)o; | |||
return ( ( type == null ? other.type == null : type.equals(other.type) ) && | |||
( message == null ? other.message == null : message.equals(other.message) ) && | |||
( stacktrace == null ? other.stacktrace == null : stacktrace.equals(other.stacktrace) ) ); | |||
} | |||
return false; | |||
} | |||
public String toString() { | |||
return (message != null) ? (type + ": " + message) : type; | |||
} | |||
} |
@@ -60,6 +60,7 @@ import java.io.ObjectInputStream; | |||
import java.io.ObjectOutputStream; | |||
/** | |||
* Read or write events to/from appropriate streams. | |||
* | |||
* @author <a href="mailto:sbailliez@apache.org">Stephane Bailliez</a> | |||
*/ | |||
@@ -90,8 +91,12 @@ public class Messenger { | |||
} | |||
} | |||
public TestRunEvent read() throws Exception { | |||
return (TestRunEvent)((ObjectInputStream)in).readObject(); | |||
public TestRunEvent read() { | |||
try { | |||
return (TestRunEvent)((ObjectInputStream)in).readObject(); | |||
} catch (Exception e){ | |||
return null; | |||
} | |||
} | |||
public void writeEvent(TestRunEvent evt) throws IOException { | |||
@@ -91,7 +91,7 @@ public class TestRunEvent extends EventObject { | |||
private String name; | |||
/** stacktrace for error or failure */ | |||
private String stacktrace; | |||
private ExceptionData error; | |||
/** properties for end of testrun */ | |||
private Properties props; | |||
@@ -122,7 +122,7 @@ public class TestRunEvent extends EventObject { | |||
public TestRunEvent(Integer id, int type, String name, Throwable t){ | |||
this(id, type, name); | |||
this.stacktrace = StringUtils.getStackTrace(t); | |||
this.error = new ExceptionData(t); | |||
} | |||
public void setType(int type) { | |||
@@ -133,8 +133,8 @@ public class TestRunEvent extends EventObject { | |||
this.timestamp = timestamp; | |||
} | |||
public void setStackTrace(String stacktrace) { | |||
this.stacktrace = stacktrace; | |||
public void setError(ExceptionData error) { | |||
this.error = error; | |||
} | |||
public void setName(String name) { | |||
@@ -161,8 +161,8 @@ public class TestRunEvent extends EventObject { | |||
return result; | |||
} | |||
public String getStackTrace(){ | |||
return stacktrace; | |||
public ExceptionData getError(){ | |||
return error; | |||
} | |||
public Properties getProperties(){ | |||
@@ -175,7 +175,7 @@ public class TestRunEvent extends EventObject { | |||
return ( (type == other.type) && | |||
(timestamp == other.timestamp) && | |||
( name == null ? other.name == null : name.equals(other.name) ) && | |||
( stacktrace == null ? other.stacktrace == null : stacktrace.equals(other.stacktrace) ) && | |||
( error == null ? other.error == null : error.equals(other.error) ) && | |||
( props == null ? other.props == null : props.equals(other.props) ) && | |||
( result == null ? other.result == null : result.equals(other.result) ) ); | |||
} | |||