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