/*******************************************************************************
* 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.runner;
import java.io.IOException;
import org.eclipse.core.runtime.IConfigurationElement;
import org.eclipse.core.runtime.IExtension;
import org.eclipse.core.runtime.IPlatformRunnable;
import org.eclipse.core.runtime.Platform;
import org.eclipse.equinox.app.IApplication;
import org.eclipse.swt.widgets.Display;
import com.windowtester.runner.util.Logger;
/**
* {@link WindowTesterRunner} delegates test execution to this class if it cannot be
* delegated to org.eclipse.test.UITestApplication in the org.eclipse.test plugin. Much of
* this is copied from org.eclipse.test.UITestApplication.
*/
public class LocalTestRunner
implements IPlatformRunnable
/* $codepro.preprocessor.if version >= 3.3 $ */
, org.eclipse.equinox.app.IApplication
/* $codepro.preprocessor.endif $ */
{
private static final String TEST_APPLICATION_OPTION = "-testApplication";
private static final String DEFAULT_APP_3_0 = "org.eclipse.ui.ide.workbench"; //$NON-NLS-1$
private static final String TEST_PRODUCT_OPTION = "-testProduct";
/**
* The results from running the tests
*/
private int fTestRunnerResult = -1;
/**
* The main entry point for {@link IApplication}. Cache the application context and
* call {@link #run(Object)} to launch the applications and run the tests.
*
* @param context the application context
* @return the return value of the application
* @throws Exception
*/
/* $codepro.preprocessor.if version >= 3.3 $ */
public Object start(org.eclipse.equinox.app.IApplicationContext context) throws Exception {
appContext = context;
String[] args = (String[]) appContext.getArguments().get(
org.eclipse.equinox.app.IApplicationContext.APPLICATION_ARGS);
if (args == null)
args = new String[0];
return run(args);
}
public void stop() {
// TODO [author=Dan] stop the tests
}
private org.eclipse.equinox.app.IApplicationContext appContext;
/* $codepro.preprocessor.endif $ */
/**
* The main entry point for {@link IPlatformRunnable}. Launch the application and run
* the tests
*
* @param args the argument(s) to pass to the application
* @return the return value of the application
* @see org.eclipse.core.runtime.IPlatformRunnable#run(java.lang.Object)
*/
public Object run(Object args) throws Exception {
String appId = getProductApplication((String[]) args);
if (appId == null)
appId = getApplicationToRun((String[])args);
Object application = getApplication(appId);
queueTests(args);
launchApplication(args, application);
return new Integer(fTestRunnerResult);
}
/**
* Determine the application to run.
*
* @return the application, or null if not even the default application is found.
*/
private Object getApplication(String appId) throws Exception {
// Assume we are in 3.0 mode.
// Find the name of the application as specified by the PDE JUnit launcher.
// If no application is specified, the 3.0 default workbench application
// is returned.
IExtension extension = Platform.getExtensionRegistry().getExtension(Platform.PI_RUNTIME,
Platform.PT_APPLICATIONS, appId);
if (extension == null)
fail("Failed to find " + Platform.PT_APPLICATIONS + " extension for " + appId);
// If the extension does not have the correct grammar, throw an exception.
// Otherwise, return the application object.
IConfigurationElement[] elements = extension.getConfigurationElements();
if (elements.length == 0)
fail("Failed to find " + Platform.PT_APPLICATIONS + " child elements for " + appId);
IConfigurationElement[] runs = elements[0].getChildren("run"); //$NON-NLS-1$
if (runs.length == 0)
fail("Failed to find " + Platform.PT_APPLICATIONS + " run child element for " + appId);
Object application = runs[0].createExecutableExtension("class"); //$NON-NLS-1$
if (application instanceof IPlatformRunnable)
return application;
/* $codepro.preprocessor.if version >= 3.3 $ */
if (application instanceof org.eclipse.equinox.app.IApplication)
return application;
/* $codepro.preprocessor.endif $ */
String className = application != null ? application.getClass().getName() : "null";
fail(className + " does not implement IApplication or IPlatformRunnable");
// fail throws RuntimeException, so this return is never reached
return null;
}
/**
* Run the application
*
* @param application the application to run
* @param args the argument(s) to pass to the application
*/
private void launchApplication(Object args, Object application) throws Exception {
Object result;
/* $codepro.preprocessor.if version >= 3.3 $ */
if (application instanceof org.eclipse.equinox.app.IApplication)
result = ((org.eclipse.equinox.app.IApplication) application).start(appContext);
else
/* $codepro.preprocessor.endif $ */
result = ((IPlatformRunnable) application).run(args);
if (!IPlatformRunnable.EXIT_OK.equals(result)) {
System.err.println("UITestRunner: Unexpected result from running application " + application + ": "
+ result);
}
}
/**
* Wait for a window to open and then run the tests
*
* @param args the argument(s) to pass to the application
*/
private void queueTests(Object args) {
final Thread testThread = new Thread("Launch Tests Thread") {
private boolean started = false;
public void run() {
pause(5000);
final Display display = Display.getDefault();
while (!started) {
pause(1000);
display.syncExec(new Runnable() {
public void run() {
started = display.getShells().length > 0;
}
});
}
display.asyncExec(new Runnable() {
public void run() {
while (display.readAndDispatch()) {
// continue processing events until the application is "idle"
}
try {
fTestRunnerResult = EclipseTestRunner.run(Platform.getCommandLineArgs());
}
catch (IOException e) {
e.printStackTrace();
}
}
});
}
};
testThread.setPriority(Thread.MIN_PRIORITY);
testThread.setDaemon(true);
testThread.start();
}
/**
* The -testApplication argument specifies the application to be run. If the PDE JUnit
* launcher did not set this argument, then return the name of the default
* application. In 3.0, the default is the "org.eclipse.ui.ide.worbench" application.
*/
private String getApplicationToRun(String[] args) {
for (int i = 0; i < args.length; i++) {
if (args[i].equals(TEST_APPLICATION_OPTION) && i < args.length - 1) //$NON-NLS-1$
return args[i + 1];
}
Logger.log("No " + TEST_APPLICATION_OPTION + " specified so assuming " + DEFAULT_APP_3_0);
return DEFAULT_APP_3_0;
}
/**
* The -testProduct arguments speciifes the product to be run. If this returns null, then look
* for the test application argument
* @param args
* @return
*/
private String getProductToRun(String[] args){
for (int i = 0; i < args.length; i++) {
if (args[i].equals(TEST_PRODUCT_OPTION) && i < args.length - 1) //$NON-NLS-1$
return args[i + 1];
}
Logger.log("No " + TEST_PRODUCT_OPTION + " specified");
return null;
}
/**
* get the application id from the product definition
* @param args
* @return
* @throws Exception
*/
public String getProductApplication(String[] args) throws Exception {
String productId = getProductToRun(args);
// if -testProduct is not specified, look for application
if (productId == null)
return null;
IExtension extension = Platform.getExtensionRegistry().getExtension(Platform.PI_RUNTIME,
Platform.PT_PRODUCT, productId);
if (extension == null)
fail("Failed to find " + Platform.PT_PRODUCT + " extension for " + productId);
// If the extension does not have the correct grammar, throw an exception.
// Otherwise, return the application object.
IConfigurationElement[] elements = extension.getConfigurationElements();
if (elements.length == 0)
fail("Failed to find " + Platform.PT_PRODUCT + " child elements for " + productId);
String appName = elements[0].getAttribute("application");
if (appName.length() == 0)
fail("Failed to find " + Platform.PT_PRODUCT + " application element for " + productId);
return appName;
}
/**
* Log the message and throw a {@link RuntimeException}
*
* @param msg the message
*/
private void fail(String msg) {
Logger.log(msg);
throw new RuntimeException(msg);
}
/**
* Causes the currently executing thread to sleep (temporarily cease execution) for
* the specified number of milliseconds.
*
* @param millis the length of time to sleep in milliseconds.
*/
public void pause(int millis) {
try {
Thread.sleep(millis);
}
catch (InterruptedException e) {
// ignored
}
}
}