/*******************************************************************************
* Copyright (c) 2012 Google, Inc.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Google, Inc. - initial API and implementation
*******************************************************************************/
package com.windowtester.runtime.swt.internal.monitor;
import org.eclipse.swt.SWT;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Event;
import org.eclipse.swt.widgets.Listener;
import com.windowtester.internal.runtime.monitor.UIThreadMonitorCommon;
import com.windowtester.runtime.IUIContext;
import com.windowtester.runtime.monitor.IUIThreadMonitor;
/**
* Monitors the UI Thread and notifies listeners if the UI thread is either hung or idle
* for an extended period of time. This is accomplished by launching a background thread
* (minimum priority) that checks to see if the UI is responsive and processing input. If
* the UI becomes unresponsive or idle for a period longer than expected, then the
* associated {@link com.windowtester.runtime.monitor.IUIThreadMonitorListener} (see
* {@link #setListener(com.windowtester.runtime.monitor.IUIThreadMonitorListener)}) is
* notified.
*/
public class UIThreadMonitorSWT extends UIThreadMonitorCommon
implements IUIThreadMonitor
{
/**
* Types of SWT events that should be happening in a UI test.
*/
protected static final int[] EVENT_TYPES = {
SWT.KeyDown, SWT.KeyUp, SWT.HardKeyDown, SWT.HardKeyUp, SWT.MenuDetect, SWT.MouseDown, SWT.MouseUp,
SWT.MouseMove, SWT.MouseEnter, SWT.MouseExit, SWT.MouseDoubleClick
};
/**
* The display whose UI thread is to be monitored.
*/
private final Display _display;
/**
* An SWT Event listener to notice proper flow of UI events.
*/
public final Listener _swtEventListener = new Listener() {
public void handleEvent(Event event) {
markEventProcessed();
trace("event processed", event.type);
}
};
/**
* A runnable to notice responsivenes of the UI thread.
*/
public final Runnable _threadResponsiveRunnable = new Runnable() {
public void run() {
markUIThreadResponsive();
}
};
// //////////////////////////////////////////////////////////////////////////
//
// Constructor
//
// //////////////////////////////////////////////////////////////////////////
/**
* Construct a new instance to monitor the health of the user interface thread.
*
* @param uiContext the user interface context (not <code>null</code>)
*/
public UIThreadMonitorSWT(IUIContext uiContext, Display display) {
super(uiContext);
_display = display;
}
// //////////////////////////////////////////////////////////////////////////
//
// IUIThreadMonitor Accessors
//
// //////////////////////////////////////////////////////////////////////////
/**
* Add event listeners to monitor events being processed by the UI.
*/
protected void addEventListeners() {
_display.syncExec(new Runnable() {
public void run() {
for (int i = 0; i < EVENT_TYPES.length; i++) {
_display.addFilter(EVENT_TYPES[i], _swtEventListener);
}
}
});
}
/**
* Remove the event listeners added to monitor events being processed by the UI.
*/
protected void removeEventListeners() {
_display.syncExec(new Runnable() {
public void run() {
for (int i = 0; i < EVENT_TYPES.length; i++)
_display.removeFilter(EVENT_TYPES[i], _swtEventListener);
}
});
}
/**
* Determine if the test has ended.
*
* @return <code>true</code> if test has ended, else <code>false</code>
*/
protected boolean hasTestEnded() {
return _display.isDisposed() || getListener() == null;
}
/**
* Wait for up to one second to determine if the user interface thread is responsive
* and processing new events.
*
* @return <code>true</code> if the user interface thread is responsive, or
* <code>false</code> if it has not processed any new events within the last
* one second
*/
protected boolean isUIThreadResponsive() {
synchronized (_lock) {
_uiThreadResponsive = false;
}
_display.asyncExec(_threadResponsiveRunnable);
for (int i = 0; i < 100; i++) {
boolean b;
b = wasUIThreadResponsive();
if (b)
break;
try {
Thread.sleep(10);
}
catch (InterruptedException e) {
// ignored
}
}
synchronized (_lock) {
return _uiThreadResponsive;
}
}
protected boolean wasUIThreadResponsive() {
boolean b;
synchronized (_lock) {
b = _uiThreadResponsive;
}
return b;
}
}