1 /*
2  * Copyright (c) 1997, 2016, Oracle and/or its affiliates. All rights reserved.
3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4  *
5  * This code is free software; you can redistribute it and/or modify it
6  * under the terms of the GNU General Public License version 2 only, as
7  * published by the Free Software Foundation. Oracle designates this
8  * particular file as subject to the "Classpath" exception as provided
9  * by Oracle in the LICENSE file that accompanied this code.
10  *
11  * This code is distributed in the hope that it will be useful, but WITHOUT
12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
14  * version 2 for more details (a copy is included in the LICENSE file that
15  * accompanied this code).
16  *
17  * You should have received a copy of the GNU General Public License version
18  * 2 along with this work; if not, write to the Free Software Foundation,
19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20  *
21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22  * or visit www.oracle.com if you need additional information or have any
23  * questions.
24  */
25 package org.netbeans.jemmy;
26 
27 import java.awt.AWTEvent;
28 import java.awt.Toolkit;
29 import java.awt.event.AWTEventListener;
30 import java.lang.ref.Reference;
31 import java.lang.ref.WeakReference;
32 import java.lang.reflect.Field;
33 import java.lang.reflect.Modifier;
34 import java.util.Vector;
35 
36 /**
37  *
38  * Provides methods to check last dispatched events, to wait for events of
39  * specific types, or to guarantee that events of specific types are not
40  * dispatched during some time frame.
41  * <BR><BR>
42  * All possible listeners are added during this class initialization in case if
43  * "jemmy.event_listening" system property is not equal to "no", so, by default,
44  * all events are listened.
45  *
46  * Uses timeouts:<BR>
47  * EventTool.WaitEventTimeout - time to wait for AWT events.<BR>
48  * EventTool.WaitNoEventTimeout - when checking for the absence of incoming AWT
49  * events.<BR>
50  * EventTool.EventCheckingDelta - time delta between checks for AWT events.
51  *
52  * @author Alexandre Iline (alexandre.iline@oracle.com)
53  */
54 public class EventTool implements Timeoutable, Outputable {
55 
56     private static final long WAIT_EVENT_TIMEOUT = 60000;
57     private static final long WAIT_NO_EVENT_TIMEOUT = 180000;
58     private static final long EVENT_CHECKING_DELTA = 10;
59 
60     private static ListenerSet listenerSet;
61     private static long currentEventMask = 0;
62 
63     private TestOut output;
64     private Timeouts timeouts;
65 
66     /**
67      * Constructor.
68      */
EventTool()69     public EventTool() {
70         setOutput(JemmyProperties.getProperties().getOutput());
71         setTimeouts(JemmyProperties.getProperties().getTimeouts());
72     }
73 
74     /**
75      * Returns time of the last dispatched event under mask.
76      *
77      * @param eventMask Events types to be searched.
78      * {@code AWTEvent.*_EVENT_MASK} fields combination.
79      * @return time in milliseconds
80      * @see #addListeners(long)
81      */
getLastEventTime(long eventMask)82     public static long getLastEventTime(long eventMask) {
83         return listenerSet.getLastEventTime(eventMask);
84     }
85 
86     /**
87      * Returns last dispatched event under mask.
88      *
89      * @param eventMask Events types to be searched.
90      * {@code AWTEvent.*_EVENT_MASK} fields combination.
91      * @return AWTEvent
92      * @see #addListeners(long)
93      */
getLastEvent(long eventMask)94     public static AWTEvent getLastEvent(long eventMask) {
95         return listenerSet.getLastEvent(eventMask);
96     }
97 
98     /**
99      * Returns time of the last dispatched event.
100      *
101      * @return time in milliseconds
102      * @see #addListeners(long)
103      */
getLastEventTime()104     public static long getLastEventTime() {
105         return getLastEventTime(listenerSet.getTheWholeMask());
106     }
107 
108     /**
109      * Returns last dispatched event.
110      *
111      * @return AWTEvent
112      * @see #addListeners(long)
113      */
getLastEvent()114     public static AWTEvent getLastEvent() {
115         return getLastEvent(listenerSet.getTheWholeMask());
116     }
117 
118     /**
119      * Adds listeners to listen events under mask. Invokes
120      * {@code removeListeners()} first, so any event history is lost.
121      *
122      * @param eventMask Mask to listen events under.
123      * {@code AWTEvent.*_EVENT_MASK} fields combination.
124      * @see #addListeners()
125      * @see #removeListeners()
126      */
addListeners(long eventMask)127     public static void addListeners(long eventMask) {
128         removeListeners();
129         listenerSet.addListeners(eventMask);
130         currentEventMask = eventMask;
131     }
132 
133     /**
134      * Adds listeners to listen all types of events. Invokes
135      * {@code removeListeners()} first, so any event history is lost. This
136      * method is invoked during static section of this class.
137      *
138      * @see #addListeners(long)
139      * @see #removeListeners()
140      * @see #getTheWholeEventMask()
141      */
addListeners()142     public static void addListeners() {
143         addListeners(listenerSet.getTheWholeMask());
144     }
145 
146     /**
147      * Removes all listeners.
148      *
149      * @see #addListeners(long)
150      * @see #addListeners()
151      */
removeListeners()152     public static void removeListeners() {
153         listenerSet.removeListeners();
154     }
155 
156     /**
157      * Returns event mask last time used by {@code addListeners(long)}
158      * method. In case if {@code addListeners()} method was used last,
159      * {@code getTheWholeEventMask() } result is returned.
160      *
161      * @return a long representing the current event mask value
162      * @see #getTheWholeEventMask()
163      */
getCurrentEventMask()164     public static long getCurrentEventMask() {
165         return currentEventMask;
166     }
167 
168     /**
169      * Returns a combination of all {@code AWTEvent.*_EVENT_MASK} fields..
170      *
171      * @return a combination of all {@code AWTEvent.*_EVENT_MASK} fields.
172      */
getTheWholeEventMask()173     public static long getTheWholeEventMask() {
174         return listenerSet.getTheWholeMask();
175     }
176 
177     static {
178         Timeouts.initDefault("EventTool.WaitEventTimeout", WAIT_EVENT_TIMEOUT);
179         Timeouts.initDefault("EventTool.WaitNoEventTimeout", WAIT_NO_EVENT_TIMEOUT);
180         Timeouts.initDefault("EventTool.EventCheckingDelta", EVENT_CHECKING_DELTA);
181         listenerSet = new ListenerSet();
182         if (System.getProperty("jemmy.event_listening") == null
183                 || !System.getProperty("jemmy.event_listening").equals("no")) {
listenerSet.addListeners()184             listenerSet.addListeners();
185         }
186     }
187 
188     /**
189      * Defines current timeouts.
190      *
191      * @param ts ?t? A collection of timeout assignments.
192      * @see org.netbeans.jemmy.Timeouts
193      * @see org.netbeans.jemmy.Timeoutable
194      * @see #getTimeouts
195      */
196     @Override
setTimeouts(Timeouts ts)197     public void setTimeouts(Timeouts ts) {
198         timeouts = ts;
199     }
200 
201     /**
202      * Return current timeouts.
203      *
204      * @return the collection of current timeout assignments.
205      * @see org.netbeans.jemmy.Timeouts
206      * @see org.netbeans.jemmy.Timeoutable
207      * @see #setTimeouts
208      */
209     @Override
getTimeouts()210     public Timeouts getTimeouts() {
211         return timeouts;
212     }
213 
214     /**
215      * Defines print output streams or writers.
216      *
217      * @param out Identify the streams or writers used for print output.
218      * @see org.netbeans.jemmy.Outputable
219      * @see org.netbeans.jemmy.TestOut
220      * @see #getOutput
221      */
222     @Override
setOutput(TestOut out)223     public void setOutput(TestOut out) {
224         output = out;
225     }
226 
227     /**
228      * Returns print output streams or writers.
229      *
230      * @return an object that contains references to objects for printing to
231      * output and err streams.
232      * @see org.netbeans.jemmy.Outputable
233      * @see org.netbeans.jemmy.TestOut
234      * @see #setOutput
235      */
236     @Override
getOutput()237     public TestOut getOutput() {
238         return output;
239     }
240 
241     /**
242      * Waits for the first event under mask. Waits during
243      * {@code EventTool.WaitEventTimeout} milliseconds.
244      *
245      * @param eventMask Mask to wait events under.
246      * {@code AWTEvent.*_EVENT_MASK} fields combination.
247      * @return an AWTEvent object
248      * @see #waitEvent()
249      * @throws TimeoutExpiredException
250      */
waitEvent(long eventMask)251     public AWTEvent waitEvent(long eventMask) {
252         return (waitEvent(eventMask,
253                 timeouts.getTimeout("EventTool.WaitEventTimeout"),
254                 output.createErrorOutput()));
255     }
256 
257     /**
258      * Waits for the first event. Waits during
259      * {@code EventTool.WaitEventTimeout} milliseconds.
260      *
261      * @return an AWTEvent object
262      * @see #waitEvent(long)
263      * @see #getTheWholeEventMask()
264      * @throws TimeoutExpiredException
265      */
waitEvent()266     public AWTEvent waitEvent() {
267         return waitEvent(listenerSet.getTheWholeMask());
268     }
269 
270     /**
271      * Check that no event under mask will be dispatched during time specified.
272      *
273      * @param eventMask Mask to wait events under.
274      * {@code AWTEvent.*_EVENT_MASK} fields combination.
275      * @param waitTime Quiet time (millisecons).
276      * @return true if no event ahs found.
277      * @see #checkNoEvent(long)
278      */
checkNoEvent(long eventMask, long waitTime)279     public boolean checkNoEvent(long eventMask, long waitTime) {
280         return checkNoEvent(eventMask, waitTime, output);
281     }
282 
283     /**
284      * Check that no event will be dispatched during time specified.
285      *
286      * @param waitTime Quiet time (millisecons).
287      * @return true if no event ahs found.
288      * @see #checkNoEvent(long, long)
289      * @see #getTheWholeEventMask()
290      */
checkNoEvent(long waitTime)291     public boolean checkNoEvent(long waitTime) {
292         return checkNoEvent(listenerSet.getTheWholeMask(), waitTime);
293     }
294 
295     /**
296      * During {@code EventTool.WaitNoEventTimeout} time waits for true
297      * result of checkNoEvent(long, long) method.
298      *
299      * @param eventMask Mask to wait events under.
300      * {@code AWTEvent.*_EVENT_MASK} fields combination.
301      * @param waitTime Quiet time (millisecons).
302      * @see #checkNoEvent(long, long)
303      * @see #waitNoEvent(long)
304      * @throws TimeoutExpiredException
305      */
waitNoEvent(long eventMask, long waitTime)306     public void waitNoEvent(long eventMask, long waitTime) {
307         NoEventWaiter waiter = new NoEventWaiter(eventMask, waitTime);
308         waiter.setTimeouts(timeouts.cloneThis());
309         waiter.getTimeouts().
310                 setTimeout("Waiter.WaitingTime",
311                         timeouts.getTimeout("EventTool.WaitNoEventTimeout"));
312         waiter.getTimeouts().
313                 setTimeout("Waiter.TimeDelta",
314                         timeouts.getTimeout("EventTool.EventCheckingDelta"));
315         try {
316             waiter.waitAction(null);
317         } catch (InterruptedException e) {
318             output.printStackTrace(e);
319         }
320     }
321 
322     /**
323      * During {@code EventTool.WaitNoEventTimeout} time waits for true
324      * result of {@code checkNoEvent(long)} method.
325      *
326      * @param waitTime Quiet time (millisecons).
327      * @see #checkNoEvent(long)
328      * @see #waitNoEvent(long, long)
329      * @throws TimeoutExpiredException
330      */
waitNoEvent(long waitTime)331     public void waitNoEvent(long waitTime) {
332         ListenerSet ls = listenerSet;
333         if (ls != null) {
334             // surprisingly this field can be null in case of massive
335             // garbage collecting efforts like in NbTestCase.assertGC
336             waitNoEvent(ls.getTheWholeMask(), waitTime);
337         }
338     }
339 
waitEvent(long eventMask, long waitTime, TestOut waiterOutput)340     private AWTEvent waitEvent(long eventMask, long waitTime, TestOut waiterOutput) {
341         EventWaiter waiter = new EventWaiter(eventMask);
342         waiter.setTimeouts(timeouts.cloneThis());
343         waiter.setOutput(waiterOutput);
344         waiter.getTimeouts().
345                 setTimeout("Waiter.WaitingTime",
346                         waitTime);
347         waiter.getTimeouts().
348                 setTimeout("Waiter.TimeDelta",
349                         timeouts.getTimeout("EventTool.EventCheckingDelta"));
350         try {
351             return waiter.waitAction(null);
352         } catch (InterruptedException e) {
353             output.printStackTrace(e);
354             return null;
355         }
356     }
357 
checkNoEvent(long eventMask, long waitTime, TestOut waiterOutput)358     private boolean checkNoEvent(long eventMask, long waitTime, TestOut waiterOutput) {
359         try {
360             AWTEvent event = waitEvent(eventMask, waitTime, TestOut.getNullOutput());
361             waiterOutput.printLine("AWT event was produced during waiting: ");
362             // used instead of event.toString() because it is not thread safe
363             waiterOutput.printLine(event.getClass().getName());
364             return false;
365         } catch (TimeoutExpiredException e) {
366             return true;
367         }
368     }
369 
370     private static class EventType implements AWTEventListener {
371 
372         long eventMask;
373         long eventTime;
374         private Reference<AWTEvent> eventRef;
375 
EventType(long eventMask)376         public EventType(long eventMask) {
377             this.eventMask = eventMask;
378             eventRef = new WeakReference<>(null);
379             eventTime = -1;
380         }
381 
382         @Override
eventDispatched(AWTEvent event)383         public void eventDispatched(AWTEvent event) {
384             eventRef = new WeakReference<>(event);
385             eventTime = System.currentTimeMillis();
386         }
387 
getEvent()388         public AWTEvent getEvent() {
389             return eventRef.get();
390         }
391 
getTime()392         public long getTime() {
393             return eventTime;
394         }
395 
getEventMask()396         public long getEventMask() {
397             return eventMask;
398         }
399     }
400 
401     private static class ListenerSet {
402 
403         private Vector<EventType> eventTypes;
404         private long theWholeMask;
405 
ListenerSet()406         public ListenerSet() {
407             eventTypes = new Vector<>();
408             try {
409                 Class<?> eventClass = Class.forName("java.awt.AWTEvent");
410                 Field[] fields = eventClass.getFields();
411                 theWholeMask = 0;
412                 long eventMask;
413                 for (Field field : fields) {
414                     if ((field.getModifiers()
415                             & (Modifier.PUBLIC | Modifier.STATIC)) != 0
416                             && field.getType().equals(Long.TYPE)
417                             && field.getName().endsWith("_EVENT_MASK")) {
418                         eventMask = (Long) field.get(null);
419                         eventTypes.add(new EventType(eventMask));
420                         theWholeMask = theWholeMask | eventMask;
421                     }
422                 }
423             } catch (ClassNotFoundException | IllegalAccessException e) {
424                 JemmyProperties.getCurrentOutput().printStackTrace(e);
425             }
426         }
427 
addListeners(long eventMask)428         public void addListeners(long eventMask) {
429             Toolkit dtk = Toolkit.getDefaultToolkit();
430             for (EventType et : eventTypes) {
431                 if ((et.getEventMask() & eventMask) != 0) {
432                     dtk.addAWTEventListener(et, et.getEventMask());
433                 }
434             }
435         }
436 
addListeners()437         public void addListeners() {
438             addListeners(getTheWholeMask());
439         }
440 
removeListeners()441         public void removeListeners() {
442             Toolkit dtk = Toolkit.getDefaultToolkit();
443             for (EventType eventType : eventTypes) {
444                 dtk.removeAWTEventListener(eventType);
445             }
446         }
447 
getTheWholeMask()448         public long getTheWholeMask() {
449             return theWholeMask;
450         }
451 
getLastEventTime(long eventMask)452         public long getLastEventTime(long eventMask) {
453             EventType et = getLastEventType(eventMask);
454             return (et == null) ? -1 : et.getTime();
455         }
456 
getLastEvent(long eventMask)457         public AWTEvent getLastEvent(long eventMask) {
458             EventType et = getLastEventType(eventMask);
459             return (et == null) ? null : et.getEvent();
460         }
461 
getLastEventType(long eventMask)462         private EventType getLastEventType(long eventMask) {
463             long maxTime = -1;
464             EventType maxType = null;
465             for (EventType et : eventTypes) {
466                 if ((eventMask & et.getEventMask()) != 0
467                         && et.getTime() > maxTime) {
468                     maxType = et;
469                     maxTime = maxType.getTime();
470                 }
471             }
472             return maxType;
473         }
474     }
475 
476     private static class EventWaiter extends Waiter<AWTEvent, Void> {
477 
478         long eventMask;
479         long startTime;
480 
EventWaiter(long eventMask)481         public EventWaiter(long eventMask) {
482             this.eventMask = eventMask;
483             startTime = getLastEventTime(eventMask);
484         }
485 
486         @Override
actionProduced(Void obj)487         public AWTEvent actionProduced(Void obj) {
488             EventType et = listenerSet.getLastEventType(eventMask);
489             if (et != null
490                     && et.getTime() > startTime) {
491                 return et.getEvent();
492             } else {
493                 return null;
494             }
495         }
496 
497         @Override
getDescription()498         public String getDescription() {
499             return ("Last event under "
500                     + Long.toString(eventMask, 2) + " event mask");
501         }
502 
503         @Override
toString()504         public String toString() {
505             return "EventWaiter{" + "eventMask=" + Long.toString(eventMask, 2) + ", startTime=" + startTime + '}';
506         }
507     }
508 
509     private class NoEventWaiter extends Waiter<String, Void> {
510 
511         long eventMask;
512         long waitTime;
513 
NoEventWaiter(long eventMask, long waitTime)514         public NoEventWaiter(long eventMask, long waitTime) {
515             this.eventMask = eventMask;
516             this.waitTime = waitTime;
517         }
518 
519         @Override
actionProduced(Void obj)520         public String actionProduced(Void obj) {
521             return (checkNoEvent(eventMask, waitTime, TestOut.getNullOutput())
522                     ? "Reached!"
523                     : null);
524         }
525 
526         @Override
getDescription()527         public String getDescription() {
528             return ("No event under "
529                     + Long.toString(eventMask, 2)
530                     + " event mask during "
531                     + Long.toString(waitTime)
532                     + " milliseconds");
533         }
534 
535         @Override
toString()536         public String toString() {
537             return "NoEventWaiter{" + "eventMask=" + Long.toString(eventMask, 2) + ", waitTime=" + waitTime + '}';
538         }
539     }
540 }
541