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