blob: 5cd2e4d2b1ada5935a9e80bef79163a40294aab6 [file] [log] [blame]
/*
* Copyright (c) 1997, 2016, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package org.netbeans.jemmy;
import java.awt.AWTEvent;
import java.awt.Toolkit;
import java.awt.event.AWTEventListener;
import java.lang.ref.Reference;
import java.lang.ref.WeakReference;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.util.Vector;
/**
*
* Provides methods to check last dispatched events, to wait for events of
* specific types, or to guarantee that events of specific types are not
* dispatched during some time frame.
* <BR><BR>
* All possible listeners are added during this class initialization in case if
* "jemmy.event_listening" system property is not equal to "no", so, by default,
* all events are listened.
*
* Uses timeouts:<BR>
* EventTool.WaitEventTimeout - time to wait for AWT events.<BR>
* EventTool.WaitNoEventTimeout - when checking for the absence of incoming AWT
* events.<BR>
* EventTool.EventCheckingDelta - time delta between checks for AWT events.
*
* @author Alexandre Iline (alexandre.iline@oracle.com)
*/
public class EventTool implements Timeoutable, Outputable {
private static final long WAIT_EVENT_TIMEOUT = 60000;
private static final long WAIT_NO_EVENT_TIMEOUT = 180000;
private static final long EVENT_CHECKING_DELTA = 10;
private static ListenerSet listenerSet;
private static long currentEventMask = 0;
private TestOut output;
private Timeouts timeouts;
/**
* Constructor.
*/
public EventTool() {
setOutput(JemmyProperties.getProperties().getOutput());
setTimeouts(JemmyProperties.getProperties().getTimeouts());
}
/**
* Returns time of the last dispatched event under mask.
*
* @param eventMask Events types to be searched.
* {@code AWTEvent.*_EVENT_MASK} fields combination.
* @return time in milliseconds
* @see #addListeners(long)
*/
public static long getLastEventTime(long eventMask) {
return listenerSet.getLastEventTime(eventMask);
}
/**
* Returns last dispatched event under mask.
*
* @param eventMask Events types to be searched.
* {@code AWTEvent.*_EVENT_MASK} fields combination.
* @return AWTEvent
* @see #addListeners(long)
*/
public static AWTEvent getLastEvent(long eventMask) {
return listenerSet.getLastEvent(eventMask);
}
/**
* Returns time of the last dispatched event.
*
* @return time in milliseconds
* @see #addListeners(long)
*/
public static long getLastEventTime() {
return getLastEventTime(listenerSet.getTheWholeMask());
}
/**
* Returns last dispatched event.
*
* @return AWTEvent
* @see #addListeners(long)
*/
public static AWTEvent getLastEvent() {
return getLastEvent(listenerSet.getTheWholeMask());
}
/**
* Adds listeners to listen events under mask. Invokes
* {@code removeListeners()} first, so any event history is lost.
*
* @param eventMask Mask to listen events under.
* {@code AWTEvent.*_EVENT_MASK} fields combination.
* @see #addListeners()
* @see #removeListeners()
*/
public static void addListeners(long eventMask) {
removeListeners();
listenerSet.addListeners(eventMask);
currentEventMask = eventMask;
}
/**
* Adds listeners to listen all types of events. Invokes
* {@code removeListeners()} first, so any event history is lost. This
* method is invoked during static section of this class.
*
* @see #addListeners(long)
* @see #removeListeners()
* @see #getTheWholeEventMask()
*/
public static void addListeners() {
addListeners(listenerSet.getTheWholeMask());
}
/**
* Removes all listeners.
*
* @see #addListeners(long)
* @see #addListeners()
*/
public static void removeListeners() {
listenerSet.removeListeners();
}
/**
* Returns event mask last time used by {@code addListeners(long)}
* method. In case if {@code addListeners()} method was used last,
* {@code getTheWholeEventMask() } result is returned.
*
* @return a long representing the current event mask value
* @see #getTheWholeEventMask()
*/
public static long getCurrentEventMask() {
return currentEventMask;
}
/**
* Returns a combination of all {@code AWTEvent.*_EVENT_MASK} fields..
*
* @return a combination of all {@code AWTEvent.*_EVENT_MASK} fields.
*/
public static long getTheWholeEventMask() {
return listenerSet.getTheWholeMask();
}
static {
Timeouts.initDefault("EventTool.WaitEventTimeout", WAIT_EVENT_TIMEOUT);
Timeouts.initDefault("EventTool.WaitNoEventTimeout", WAIT_NO_EVENT_TIMEOUT);
Timeouts.initDefault("EventTool.EventCheckingDelta", EVENT_CHECKING_DELTA);
listenerSet = new ListenerSet();
if (System.getProperty("jemmy.event_listening") == null
|| !System.getProperty("jemmy.event_listening").equals("no")) {
listenerSet.addListeners();
}
}
/**
* Defines current timeouts.
*
* @param ts ?t? A collection of timeout assignments.
* @see org.netbeans.jemmy.Timeouts
* @see org.netbeans.jemmy.Timeoutable
* @see #getTimeouts
*/
@Override
public void setTimeouts(Timeouts ts) {
timeouts = ts;
}
/**
* Return current timeouts.
*
* @return the collection of current timeout assignments.
* @see org.netbeans.jemmy.Timeouts
* @see org.netbeans.jemmy.Timeoutable
* @see #setTimeouts
*/
@Override
public Timeouts getTimeouts() {
return timeouts;
}
/**
* Defines print output streams or writers.
*
* @param out Identify the streams or writers used for print output.
* @see org.netbeans.jemmy.Outputable
* @see org.netbeans.jemmy.TestOut
* @see #getOutput
*/
@Override
public void setOutput(TestOut out) {
output = out;
}
/**
* Returns print output streams or writers.
*
* @return an object that contains references to objects for printing to
* output and err streams.
* @see org.netbeans.jemmy.Outputable
* @see org.netbeans.jemmy.TestOut
* @see #setOutput
*/
@Override
public TestOut getOutput() {
return output;
}
/**
* Waits for the first event under mask. Waits during
* {@code EventTool.WaitEventTimeout} milliseconds.
*
* @param eventMask Mask to wait events under.
* {@code AWTEvent.*_EVENT_MASK} fields combination.
* @return an AWTEvent object
* @see #waitEvent()
* @throws TimeoutExpiredException
*/
public AWTEvent waitEvent(long eventMask) {
return (waitEvent(eventMask,
timeouts.getTimeout("EventTool.WaitEventTimeout"),
output.createErrorOutput()));
}
/**
* Waits for the first event. Waits during
* {@code EventTool.WaitEventTimeout} milliseconds.
*
* @return an AWTEvent object
* @see #waitEvent(long)
* @see #getTheWholeEventMask()
* @throws TimeoutExpiredException
*/
public AWTEvent waitEvent() {
return waitEvent(listenerSet.getTheWholeMask());
}
/**
* Check that no event under mask will be dispatched during time specified.
*
* @param eventMask Mask to wait events under.
* {@code AWTEvent.*_EVENT_MASK} fields combination.
* @param waitTime Quiet time (millisecons).
* @return true if no event ahs found.
* @see #checkNoEvent(long)
*/
public boolean checkNoEvent(long eventMask, long waitTime) {
return checkNoEvent(eventMask, waitTime, output);
}
/**
* Check that no event will be dispatched during time specified.
*
* @param waitTime Quiet time (millisecons).
* @return true if no event ahs found.
* @see #checkNoEvent(long, long)
* @see #getTheWholeEventMask()
*/
public boolean checkNoEvent(long waitTime) {
return checkNoEvent(listenerSet.getTheWholeMask(), waitTime);
}
/**
* During {@code EventTool.WaitNoEventTimeout} time waits for true
* result of checkNoEvent(long, long) method.
*
* @param eventMask Mask to wait events under.
* {@code AWTEvent.*_EVENT_MASK} fields combination.
* @param waitTime Quiet time (millisecons).
* @see #checkNoEvent(long, long)
* @see #waitNoEvent(long)
* @throws TimeoutExpiredException
*/
public void waitNoEvent(long eventMask, long waitTime) {
NoEventWaiter waiter = new NoEventWaiter(eventMask, waitTime);
waiter.setTimeouts(timeouts.cloneThis());
waiter.getTimeouts().
setTimeout("Waiter.WaitingTime",
timeouts.getTimeout("EventTool.WaitNoEventTimeout"));
waiter.getTimeouts().
setTimeout("Waiter.TimeDelta",
timeouts.getTimeout("EventTool.EventCheckingDelta"));
try {
waiter.waitAction(null);
} catch (InterruptedException e) {
output.printStackTrace(e);
}
}
/**
* During {@code EventTool.WaitNoEventTimeout} time waits for true
* result of {@code checkNoEvent(long)} method.
*
* @param waitTime Quiet time (millisecons).
* @see #checkNoEvent(long)
* @see #waitNoEvent(long, long)
* @throws TimeoutExpiredException
*/
public void waitNoEvent(long waitTime) {
ListenerSet ls = listenerSet;
if (ls != null) {
// surprisingly this field can be null in case of massive
// garbage collecting efforts like in NbTestCase.assertGC
waitNoEvent(ls.getTheWholeMask(), waitTime);
}
}
private AWTEvent waitEvent(long eventMask, long waitTime, TestOut waiterOutput) {
EventWaiter waiter = new EventWaiter(eventMask);
waiter.setTimeouts(timeouts.cloneThis());
waiter.setOutput(waiterOutput);
waiter.getTimeouts().
setTimeout("Waiter.WaitingTime",
waitTime);
waiter.getTimeouts().
setTimeout("Waiter.TimeDelta",
timeouts.getTimeout("EventTool.EventCheckingDelta"));
try {
return waiter.waitAction(null);
} catch (InterruptedException e) {
output.printStackTrace(e);
return null;
}
}
private boolean checkNoEvent(long eventMask, long waitTime, TestOut waiterOutput) {
try {
AWTEvent event = waitEvent(eventMask, waitTime, TestOut.getNullOutput());
waiterOutput.printLine("AWT event was produced during waiting: ");
// used instead of event.toString() because it is not thread safe
waiterOutput.printLine(event.getClass().getName());
return false;
} catch (TimeoutExpiredException e) {
return true;
}
}
private static class EventType implements AWTEventListener {
long eventMask;
long eventTime;
private Reference<AWTEvent> eventRef;
public EventType(long eventMask) {
this.eventMask = eventMask;
eventRef = new WeakReference<>(null);
eventTime = -1;
}
@Override
public void eventDispatched(AWTEvent event) {
eventRef = new WeakReference<>(event);
eventTime = System.currentTimeMillis();
}
public AWTEvent getEvent() {
return eventRef.get();
}
public long getTime() {
return eventTime;
}
public long getEventMask() {
return eventMask;
}
}
private static class ListenerSet {
private Vector<EventType> eventTypes;
private long theWholeMask;
public ListenerSet() {
eventTypes = new Vector<>();
try {
Class<?> eventClass = Class.forName("java.awt.AWTEvent");
Field[] fields = eventClass.getFields();
theWholeMask = 0;
long eventMask;
for (Field field : fields) {
if ((field.getModifiers()
& (Modifier.PUBLIC | Modifier.STATIC)) != 0
&& field.getType().equals(Long.TYPE)
&& field.getName().endsWith("_EVENT_MASK")) {
eventMask = (Long) field.get(null);
eventTypes.add(new EventType(eventMask));
theWholeMask = theWholeMask | eventMask;
}
}
} catch (ClassNotFoundException | IllegalAccessException e) {
JemmyProperties.getCurrentOutput().printStackTrace(e);
}
}
public void addListeners(long eventMask) {
Toolkit dtk = Toolkit.getDefaultToolkit();
for (EventType et : eventTypes) {
if ((et.getEventMask() & eventMask) != 0) {
dtk.addAWTEventListener(et, et.getEventMask());
}
}
}
public void addListeners() {
addListeners(getTheWholeMask());
}
public void removeListeners() {
Toolkit dtk = Toolkit.getDefaultToolkit();
for (EventType eventType : eventTypes) {
dtk.removeAWTEventListener(eventType);
}
}
public long getTheWholeMask() {
return theWholeMask;
}
public long getLastEventTime(long eventMask) {
EventType et = getLastEventType(eventMask);
return (et == null) ? -1 : et.getTime();
}
public AWTEvent getLastEvent(long eventMask) {
EventType et = getLastEventType(eventMask);
return (et == null) ? null : et.getEvent();
}
private EventType getLastEventType(long eventMask) {
long maxTime = -1;
EventType maxType = null;
for (EventType et : eventTypes) {
if ((eventMask & et.getEventMask()) != 0
&& et.getTime() > maxTime) {
maxType = et;
maxTime = maxType.getTime();
}
}
return maxType;
}
}
private static class EventWaiter extends Waiter<AWTEvent, Void> {
long eventMask;
long startTime;
public EventWaiter(long eventMask) {
this.eventMask = eventMask;
startTime = getLastEventTime(eventMask);
}
@Override
public AWTEvent actionProduced(Void obj) {
EventType et = listenerSet.getLastEventType(eventMask);
if (et != null
&& et.getTime() > startTime) {
return et.getEvent();
} else {
return null;
}
}
@Override
public String getDescription() {
return ("Last event under "
+ Long.toString(eventMask, 2) + " event mask");
}
@Override
public String toString() {
return "EventWaiter{" + "eventMask=" + Long.toString(eventMask, 2) + ", startTime=" + startTime + '}';
}
}
private class NoEventWaiter extends Waiter<String, Void> {
long eventMask;
long waitTime;
public NoEventWaiter(long eventMask, long waitTime) {
this.eventMask = eventMask;
this.waitTime = waitTime;
}
@Override
public String actionProduced(Void obj) {
return (checkNoEvent(eventMask, waitTime, TestOut.getNullOutput())
? "Reached!"
: null);
}
@Override
public String getDescription() {
return ("No event under "
+ Long.toString(eventMask, 2)
+ " event mask during "
+ Long.toString(waitTime)
+ " milliseconds");
}
@Override
public String toString() {
return "NoEventWaiter{" + "eventMask=" + Long.toString(eventMask, 2) + ", waitTime=" + waitTime + '}';
}
}
}