Download
Getting Started
Members
Projects
Community
Marketplace
Events
Planet Eclipse
Newsletter
Videos
Participate
Report a Bug
Forums
Mailing Lists
Wiki
IRC
How to Contribute
Working Groups
Automotive
Internet of Things
LocationTech
Long-Term Support
PolarSys
Science
OpenMDM
More
Community
Marketplace
Events
Planet Eclipse
Newsletter
Videos
Participate
Report a Bug
Forums
Mailing Lists
Wiki
IRC
How to Contribute
Working Groups
Automotive
Internet of Things
LocationTech
Long-Term Support
PolarSys
Science
OpenMDM
Toggle navigation
Bugzilla – Attachment 225163 Details for
Bug 394179
Enhancement: Refactoring and generalization of the RC Swing and SWT
Home
|
New
|
Browse
|
Search
|
[?]
|
Reports
|
Requests
|
Help
|
Log In
[x]
|
Terms of Use
|
Copyright Agent
Some Eclipse Foundation services are deprecated, or will be soon. Please ensure you've read
this important communication.
[patch]
Refactoring of the Application actions
application.patch (text/plain), 74.64 KB, created by
Marvin Mueller
on 2013-01-03 03:55:08 EST
(
hide
)
Description:
Refactoring of the Application actions
Filename:
MIME Type:
Creator:
Marvin Mueller
Created:
2013-01-03 03:55:08 EST
Size:
74.64 KB
patch
obsolete
>From 2eb130c9a2c7f69ece371bc977d39203f6fb5bc1 Mon Sep 17 00:00:00 2001 >From: Marvin Mueller <marvin.mueller@bredex.de> >Date: Wed, 19 Dec 2012 09:54:11 +0100 >Subject: [PATCH] Sprint task: Generalization of application actions in SWT > and Swing > >--- > .../rc/common/caps/AbstractApplicationCAPs.java | 749 +++++++++++++++++++++ > .../rc/swing/swing/caps/SwingApplicationCAPs.java | 577 ++++++++++++++++ > .../jubula/rc/swt/caps/SwtApplicationCAPs.java | 548 +++++++++++++++ > .../resources/xml/ComponentConfiguration.xml | 2 +- > .../resources/xml/ComponentConfiguration.xml | 2 +- > 5 files changed, 1876 insertions(+), 2 deletions(-) > create mode 100644 org.eclipse.jubula.rc.common/src/org/eclipse/jubula/rc/common/caps/AbstractApplicationCAPs.java > create mode 100644 org.eclipse.jubula.rc.swing/src/org/eclipse/jubula/rc/swing/swing/caps/SwingApplicationCAPs.java > create mode 100644 org.eclipse.jubula.rc.swt/src/org/eclipse/jubula/rc/swt/caps/SwtApplicationCAPs.java > >diff --git a/org.eclipse.jubula.rc.common/src/org/eclipse/jubula/rc/common/caps/AbstractApplicationCAPs.java b/org.eclipse.jubula.rc.common/src/org/eclipse/jubula/rc/common/caps/AbstractApplicationCAPs.java >new file mode 100644 >index 0000000..ce7b602 >--- /dev/null >+++ b/org.eclipse.jubula.rc.common/src/org/eclipse/jubula/rc/common/caps/AbstractApplicationCAPs.java >@@ -0,0 +1,749 @@ >+/******************************************************************************* >+ * Copyright (c) 2012 BREDEX GmbH. >+ * 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: >+ * BREDEX GmbH - initial API and implementation >+ *******************************************************************************/ >+package org.eclipse.jubula.rc.common.caps; >+ >+import java.awt.AWTException; >+import java.awt.Dimension; >+import java.awt.Graphics2D; >+import java.awt.Rectangle; >+import java.awt.RenderingHints; >+import java.awt.Toolkit; >+import java.awt.datatransfer.StringSelection; >+import java.awt.image.BufferedImage; >+import java.io.File; >+import java.io.IOException; >+ >+import javax.imageio.ImageIO; >+ >+import org.eclipse.jubula.rc.common.driver.ClickOptions; >+import org.eclipse.jubula.rc.common.driver.IRobot; >+import org.eclipse.jubula.rc.common.driver.KeyTyper; >+import org.eclipse.jubula.rc.common.exception.ExecutionEvent; >+import org.eclipse.jubula.rc.common.exception.OsNotSupportedException; >+import org.eclipse.jubula.rc.common.exception.RobotException; >+import org.eclipse.jubula.rc.common.exception.StepExecutionException; >+import org.eclipse.jubula.rc.common.implclasses.Comparer; >+import org.eclipse.jubula.rc.common.implclasses.IBaseImplementationClass; >+import org.eclipse.jubula.rc.common.implclasses.Verifier; >+import org.eclipse.jubula.rc.common.logger.AutServerLogger; >+import org.eclipse.jubula.rc.common.util.KeyStrokeUtil; >+import org.eclipse.jubula.tools.constants.StringConstants; >+import org.eclipse.jubula.tools.objects.event.EventFactory; >+import org.eclipse.jubula.tools.objects.event.TestErrorEvent; >+import org.eclipse.jubula.tools.utils.ExternalCommandExecutor; >+import org.eclipse.jubula.tools.utils.TimeUtil; >+import org.eclipse.jubula.tools.utils.ExternalCommandExecutor.MonitorTask; >+/** >+ * >+ * @author BREDEX GmbH >+ * >+ */ >+public abstract class AbstractApplicationCAPs >+ implements IBaseImplementationClass { >+ /** >+ * String for sequential numbering for screenshots >+ */ >+ public static final String RENAME = "rename"; //$NON-NLS-1$ >+ >+ /** >+ * String for overwriting for screenshots >+ */ >+ public static final String OVERWRITE = "overwrite"; //$NON-NLS-1$ >+ >+ /** >+ * The default format to use when writing images to disk. >+ */ >+ private static final String DEFAULT_IMAGE_FORMAT = "png"; //$NON-NLS-1$ >+ >+ /** >+ * The string used to separate filename and file extension. >+ */ >+ private static final String EXTENSION_SEPARATOR = "."; //$NON-NLS-1$ >+ >+ /** constants for communication */ >+ private static final String POS_UNIT_PIXEL = "Pixel"; //$NON-NLS-1$ >+ >+ /** constants for communication */ >+ private static final String POS_UNI_PERCENT = "Percent"; //$NON-NLS-1$ >+ >+ /** >+ * The logging. >+ */ >+ private static AutServerLogger log = >+ new AutServerLogger(AbstractApplicationCAPs.class); >+ >+ /** >+ * @param text text to type >+ */ >+ public void gdInputText(String text) { >+ getRobot().type(getFocusOwner(), text); >+ } >+ >+ /** >+ * Executes the given command and waits for it to finish. If the >+ * execution does not finish in good time, a timeout will occur. If the >+ * exit code for the execution is not the same as the expected code, the >+ * test step fails. >+ * >+ * @param cmd The command to execute. >+ * @param expectedExitCode The expected exit code of the command. >+ * @param local <code>true</code> if the command should be executed on the >+ * local (client) machine. Otherwise (should run on the server side), >+ * this value should be <code>false</code>. >+ * @param timeout The amount of time (in milliseconds) to wait for the >+ * execution to finish. >+ */ >+ public void gdExecuteExternalCommand(String cmd, int expectedExitCode, >+ boolean local, int timeout) { >+ >+ if (!local) { >+ MonitorTask mt = new ExternalCommandExecutor().executeCommand( >+ null, cmd, timeout); >+ >+ if (!mt.wasCmdValid()) { >+ throw new StepExecutionException( >+ "Command not found.", //$NON-NLS-1$ >+ EventFactory.createActionError( >+ TestErrorEvent.NO_SUCH_COMMAND)); >+ } >+ >+ if (mt.hasTimeoutOccurred()) { >+ throw new StepExecutionException( >+ "Timeout received before completing execution of script.", //$NON-NLS-1$ >+ EventFactory.createActionError( >+ TestErrorEvent.CONFIRMATION_TIMEOUT)); >+ } >+ >+ int actualExitValue = mt.getExitCode(); >+ if (actualExitValue != expectedExitCode) { >+ throw new StepExecutionException( >+ "Verification of exit code failed.", //$NON-NLS-1$ >+ EventFactory.createVerifyFailed( >+ String.valueOf(expectedExitCode), >+ String.valueOf(actualExitValue))); >+ } >+ } >+ >+ } >+ >+ /** >+ * {@inheritDoc} >+ */ >+ public void setComponent(Object graphicsComponent) { >+ // Do nothing; Application has no correspaonding component >+ } >+ >+ /** >+ * Takes a screenshot and saves the image to disk. >+ * >+ * @param destination >+ * Path and filename for the created image. If the extension is not >+ * ".jpeg" (case-insensitive), ".jpeg" will be appended to the >+ * filename. >+ * @param delay >+ * Amount of time to wait (in milliseconds) before taking the >+ * screenshot. >+ * @param fileAccess >+ * Determines how the file will be created if a file with the >+ * given name and path already exists:<br> >+ * <code>SwingApplicationImplClass.RENAME</code> - >+ * The screenshot will be saved with a sequential integer appended >+ * to the filename.<br> >+ * <code>SwingApplicationImplClass.OVERWRITE</code> - >+ * The screenshot will overwrite the file. >+ * @param scaling >+ * Degree to which the image should be scaled, in percent. A >+ * <code>scaling</code> value of <code>100</code> produces an >+ * unscaled image. This value must be greater than <code>0</code> >+ * and less than or equal to <code>200</code>. >+ * @param createDirs >+ * Determines whether a path will be created if it does not already >+ * exist. A value of <code>true</code> means that all necessary >+ * directories that do not exist will be created automatically. >+ */ >+ public void gdTakeScreenshot(String destination, int delay, >+ String fileAccess, int scaling, boolean createDirs) { >+ // Determine current screen size >+ Toolkit toolkit = Toolkit.getDefaultToolkit(); >+ Dimension screenSize = toolkit.getScreenSize(); >+ >+ // If screen !(resolution%2==0) --> bad scaling >+ int screenWidth = (int)screenSize.getWidth(); >+ int screenHeight = (int)screenSize.getHeight(); >+ if (!(screenWidth % 2 == 0)) { >+ screenWidth = screenWidth - 1; >+ } >+ if (!(screenHeight % 2 == 0)) { >+ screenHeight = screenHeight - 1; >+ } >+ >+ screenSize.setSize(screenWidth, screenHeight); >+ >+ Rectangle screenRect = new Rectangle(screenSize); >+ >+ takeScreenshot(destination, delay, fileAccess, scaling, createDirs, >+ screenRect); >+ } >+ >+ /** >+ * Takes a screenshot and saves the image to disk. >+ * >+ * @param destination >+ * Path and filename for the created image. If the extension is >+ * not ".jpeg" (case-insensitive), ".jpeg" will be appended to >+ * the filename. >+ * @param delay >+ * Amount of time to wait (in milliseconds) before taking the >+ * screenshot. >+ * @param fileAccess >+ * Determines how the file will be created if a file with the >+ * given name and path already exists:<br> >+ * <code>SwingApplicationImplClass.RENAME</code> - The screenshot >+ * will be saved with a sequential integer appended to the >+ * filename.<br> >+ * <code>SwingApplicationImplClass.OVERWRITE</code> - The >+ * screenshot will overwrite the file. >+ * @param scaling >+ * Degree to which the image should be scaled, in percent. A >+ * <code>scaling</code> value of <code>100</code> produces an >+ * unscaled image. This value must be greater than <code>0</code> >+ * and less than or equal to <code>200</code>. >+ * @param createDirs >+ * Determines whether a path will be created if it does not >+ * already exist. A value of <code>true</code> means that all >+ * necessary directories that do not exist will be created >+ * automatically. >+ * @param marginTop >+ * the extra top margin >+ * @param marginRight >+ * the extra right margin >+ * @param marginBottom >+ * the extra bottom margin >+ * @param marginLeft >+ * the extra left margin >+ */ >+ public void gdTakeScreenshotOfActiveWindow(String destination, int delay, >+ String fileAccess, int scaling, boolean createDirs, int marginTop, >+ int marginRight, int marginBottom, int marginLeft) { >+ Rectangle activeWindowBounds = getActiveWindowBounds(); >+ >+ if (activeWindowBounds == null) { >+ throw new StepExecutionException("No active window found", //$NON-NLS-1$ >+ EventFactory >+ .createActionError(TestErrorEvent.NO_ACTIVE_WINDOW)); >+ } >+ >+ int x = activeWindowBounds.x - marginLeft; >+ int y = activeWindowBounds.y - marginTop; >+ int width = activeWindowBounds.width + marginLeft + marginRight; >+ int height = activeWindowBounds.height + marginTop + marginBottom; >+ >+ if (width < 1 || height < 1) { >+ throw new StepExecutionException("Margin parameter lead to negative height or width", //$NON-NLS-1$ >+ EventFactory >+ .createActionError(TestErrorEvent.INVALID_INPUT)); >+ } >+ >+ Rectangle screenRect = new Rectangle(x, y, width, height); >+ >+ takeScreenshot(destination, delay, fileAccess, scaling, createDirs, >+ screenRect); >+ } >+ >+ /** >+ * @return an awt rectangle which represents the absolute active window >+ * bounds; may return null e.g. if no active window could be found >+ */ >+ public abstract Rectangle getActiveWindowBounds(); >+ >+ >+ /** >+ * Takes a screenshot and saves the image to disk, in JPEG format. >+ * >+ * @param destination >+ * Path and filename for the created image. If the extension is not >+ * ".jpeg" (case-insensitive), ".jpeg" will be appended to the >+ * filename. >+ * @param delay >+ * Amount of time to wait (in milliseconds) before taking the >+ * screenshot. >+ * @param fileAccess >+ * Determines how the file will be created if a file with the >+ * given name and path already exists:<br> >+ * <code>SwingApplicationImplClass.RENAME</code> - >+ * The screenshot will be saved with a sequential integer appended >+ * to the filename.<br> >+ * <code>SwingApplicationImplClass.OVERWRITE</code> - >+ * The screenshot will overwrite the file. >+ * @param scaling >+ * Degree to which the image should be scaled, in percent. A >+ * <code>scaling</code> value of <code>100</code> produces an >+ * unscaled image. This value must be greater than <code>0</code> >+ * and less than or equal to <code>200</code>. >+ * @param createDirs >+ * Determines whether a path will be created if it does not already >+ * exist. A value of <code>true</code> means that all necessary >+ * directories that do not exist will be created automatically. >+ * @param screenShotRect >+ * the rectangle to take the screenshot of >+ */ >+ private void takeScreenshot(String destination, int delay, >+ String fileAccess, int scaling, boolean createDirs, >+ Rectangle screenShotRect) { >+ if (scaling <= 0 || scaling > 200) { >+ throw new StepExecutionException( >+ "Invalid scaling factor: Must be between 1 and 200", //$NON-NLS-1$ >+ EventFactory.createActionError( >+ TestErrorEvent.INVALID_PARAM_VALUE)); >+ } >+ >+ double scaleFactor = scaling * 0.01; >+ >+ // Check if file name is valid >+ String outFileName = destination; >+ String imageExtension = getExtension(outFileName); >+ if (imageExtension.length() == 0) { >+ // If not, then we simply append the default extension >+ imageExtension = DEFAULT_IMAGE_FORMAT; >+ outFileName += EXTENSION_SEPARATOR + imageExtension; >+ } >+ >+ // Wait for a user-specified time >+ if (delay > 0) { >+ TimeUtil.delay(delay); >+ } >+ >+ // Create path, if necessary >+ File pic = new File(outFileName); >+ if (pic.getParent() == null) { >+ throw new StepExecutionException( >+ "Invalid file name: specify a file name", //$NON-NLS-1$ >+ EventFactory.createActionError( >+ TestErrorEvent.INVALID_PARAM_VALUE)); >+ } >+ >+ File path = new File(pic.getParent()); >+ if (createDirs && !path.exists() && !path.mkdirs()) { >+ throw new StepExecutionException( >+ "Directory path does not exist and could not be created", //$NON-NLS-1$ >+ EventFactory.createActionError( >+ TestErrorEvent.FILE_IO_ERROR)); >+ } >+ >+ // Rename file if file already exists >+ // FIXME zeb This naming scheme can lead to sorting problems when >+ // filenames have varying numbers of digits (ex. "pic_9" and >+ // "pic_10") >+ if (fileAccess.equals(RENAME)) { >+ String completeExtension = >+ EXTENSION_SEPARATOR + imageExtension.toLowerCase(); >+ int extensionIndex = >+ pic.getName().toLowerCase().lastIndexOf(completeExtension); >+ String fileName = pic.getName().substring(0, extensionIndex); >+ for (int i = 1; pic.exists(); i++) { >+ pic = new File(pic.getParent(), fileName + "_" + i + completeExtension); //$NON-NLS-1$ >+ } >+ } >+ >+ takeScreenshot(screenShotRect, scaleFactor, pic); >+ } >+ >+ /** >+ * Takes a screenshot and saves the image to disk. This method will attempt >+ * to encode the image according to the file extension of the given >+ * output file. If this is not possible (because the encoding type >+ * is not supported), then the default encoding type will be used. If >+ * the default encoding type is used, an appropriate extension will be added >+ * to the filename. >+ * >+ * @param captureRect >+ * Rect to capture in screen coordinates. >+ * @param scaleFactor >+ * Degree to which the image should be scaled, in percent. A >+ * <code>scaleFactor</code> of <code>100</code> produces an >+ * unscaled image. This value must be greater than <code>0</code> >+ * and less than or equal to <code>200</code>. >+ * @param outputFile >+ * Path and filename for the created image. >+ */ >+ public void takeScreenshot( >+ Rectangle captureRect, double scaleFactor, File outputFile) { >+ // Create screenshot >+ java.awt.Robot robot; >+ File out = outputFile; >+ >+ try { >+ robot = new java.awt.Robot(); >+ BufferedImage image = robot.createScreenCapture(captureRect); >+ >+ int scaledWidth = (int) Math.floor(image.getWidth() * scaleFactor); >+ int scaledHeight = >+ (int) Math.floor(image.getHeight() * scaleFactor); >+ BufferedImage imageOut = >+ new BufferedImage(scaledWidth, >+ scaledHeight, BufferedImage.TYPE_INT_RGB); >+ // Scale it to the new size on-the-fly >+ Graphics2D graphics2D = imageOut.createGraphics(); >+ graphics2D.setRenderingHint(RenderingHints.KEY_RENDERING, >+ RenderingHints.VALUE_RENDER_QUALITY); >+ graphics2D.drawImage(image, 0, 0, scaledWidth, scaledHeight, null); >+ >+ // Save captured image using given format, if supported. >+ String extension = getExtension(out.getName()); >+ if (extension.length() == 0 >+ || !ImageIO.getImageWritersBySuffix(extension).hasNext() >+ || !ImageIO.write(imageOut, extension, out)) { >+ >+ // Otherwise, save using default format >+ out = new File(outputFile.getPath() >+ + EXTENSION_SEPARATOR + DEFAULT_IMAGE_FORMAT); >+ if (!ImageIO.write(imageOut, DEFAULT_IMAGE_FORMAT, out)) { >+ >+ // This should never happen, so log as error if it does. >+ // In this situation, the screenshot will not be saved, but >+ // the test step will still be marked as successful. >+ log.error("Screenshot could not be saved. " + //$NON-NLS-1$ >+ "Default image format (" + DEFAULT_IMAGE_FORMAT //$NON-NLS-1$ >+ + ") is not supported."); //$NON-NLS-1$ >+ } >+ } >+ >+ } catch (AWTException e) { >+ throw new RobotException(e); >+ } catch (IOException e) { >+ throw new StepExecutionException( >+ "Screenshot could not be saved", //$NON-NLS-1$ >+ EventFactory.createActionError( >+ TestErrorEvent.FILE_IO_ERROR)); >+ } >+ } >+ >+ /** >+ * Waits a specified time. >+ * @param timeMilliSec the time to wait in MilliSec >+ */ >+ public void gdWait(int timeMilliSec) { >+ TimeUtil.delay(timeMilliSec); >+ } >+ >+ /** >+ * shows a ConfirmDialog and Pause the Execution of the Test until Window >+ * is closed >+ */ >+ public void gdPause() { >+ throw new ExecutionEvent(ExecutionEvent.PAUSE_EXECUTION); >+ } >+ >+ /** >+ * Does nothing! The restart is implemented in the client but the server >+ * must have an action to execute. >+ */ >+ public void gdRestart() { >+ // nothing >+ } >+ >+ /** >+ * Types the given text without checking location or event confirmation. >+ * >+ * @param text The text to type. >+ */ >+ public void gdNativeInputText(String text) { >+ try { >+ KeyTyper.getInstance().nativeTypeString(text); >+ } catch (AWTException e) { >+ throw new RobotException(e); >+ } >+ } >+ >+ /** >+ * Action to perform a manual test step on server side; opens a window and >+ * wait's for real user interaction >+ * >+ * @param actionToPerform >+ * a textual description of the action to perform in the AUT >+ * @param expectedBehavior >+ * a textual description of the expected behaviour >+ * @param timeout >+ * the timeout >+ */ >+ public void gdManualTestStep(String actionToPerform, >+ String expectedBehavior, int timeout) { >+ // empty implementation: implementation can be found in the corresponding >+ // postExecutionCommand >+ } >+ >+ /** >+ * Perform a keystroke specified according <a >+ * href=http://java.sun.com/j2se/1.4.2/docs/api/javax/swing/KeyStroke.html#getKeyStroke(java.lang.String)> >+ * string representation of a keystroke </a>. >+ * This method does not wait for event confirmation, as we have no way of >+ * confirming events on OS-native widgets. >+ * >+ * @param modifierSpec the string representation of the modifiers >+ * @param keySpec the string representation of the key >+ */ >+ public void gdNativeKeyStroke(String modifierSpec, String keySpec) { >+ if (keySpec == null || keySpec.trim().length() == 0) { >+ throw new StepExecutionException("The base key of the key stroke " //$NON-NLS-1$ >+ + "must not be null or empty", //$NON-NLS-1$ >+ EventFactory.createActionError()); >+ } >+ >+ try { >+ >+ KeyTyper typer = KeyTyper.getInstance(); >+ String keyStrokeSpec = keySpec.trim().toUpperCase(); >+ String mod = KeyStrokeUtil.getModifierString(modifierSpec); >+ if (mod.length() > 0) { >+ keyStrokeSpec = mod + " " + keyStrokeSpec; //$NON-NLS-1$ >+ } >+ >+ typer.type(keyStrokeSpec, null, null, null); >+ >+ } catch (AWTException e) { >+ throw new RobotException(e); >+ } >+ >+ } >+ >+ /** >+ * Action to set the value of a variable in the Client. >+ * >+ * @param variable The name of the variable. >+ * @param value The new value for the variable. >+ * @return the new value for the variable. >+ */ >+ public String gdSetValue(String variable, String value) { >+ return value; >+ } >+ >+ /** >+ * @return The Robot instance >+ */ >+ protected abstract IRobot getRobot(); >+ >+ /** >+ * >+ * @param filename A filename for which to find the extension. >+ * @return the file extension for the given filename. For example, >+ * "png" for "example.png". Returns an empty string if the given >+ * name has no extension. This is the case if the given name does >+ * not contain an instance of the extension separator or ends with >+ * the extension separator. For example, "example" or "example.". >+ */ >+ private String getExtension(String filename) { >+ File file = new File(filename); >+ int extensionIndex = file.getName().lastIndexOf(EXTENSION_SEPARATOR); >+ return extensionIndex == -1 >+ || extensionIndex == file.getName().length() - 1 >+ ? StringConstants.EMPTY >+ : file.getName().substring(extensionIndex + 1); >+ } >+ >+ /** >+ * method to copy a string to the system clipboard >+ * >+ * @param text The text to copy >+ */ >+ public void gdCopyToClipboard(final String text) { >+ StringSelection strSel = new StringSelection(text); >+ try { >+ Toolkit.getDefaultToolkit().getSystemClipboard() >+ .setContents(strSel, null); >+ } catch (IllegalStateException ise) { >+ throw new StepExecutionException( >+ "Clipboard not available.", //$NON-NLS-1$ >+ EventFactory.createActionError( >+ TestErrorEvent.CLIPBOARD_NOT_AVAILABLE)); >+ } >+ } >+ >+ /** >+ * method to compare to values >+ * >+ * @param value1 >+ * the first value for comparison >+ * @param comparisonMethod >+ * the comparison method >+ * @param value2 >+ * the second value for comparison >+ */ >+ public void gdCheckValues(final String value1, >+ final String comparisonMethod, final String value2) { >+ Comparer.compare(value1, value2, comparisonMethod); >+ } >+ >+ /** >+ * method to compare to strings >+ * >+ * @param value1 >+ * the first value for comparison >+ * @param operator >+ * the comparison method >+ * @param value2 >+ * the second value for comparison >+ */ >+ public void gdCheckStringValues(final String value1, >+ final String operator, final String value2) { >+ Verifier.match(value1, value2, operator); >+ } >+ >+ /** >+ * Does nothing! The start timer is implemented in the client but the server >+ * must have an action to execute. >+ * @param timerName the name for the timer >+ * @param variableName the variable name to store the current time in millisecs in >+ */ >+ public void gdStartTimer(String timerName, String variableName) { >+ // empty >+ } >+ >+ /** >+ * Does nothing! The read timer is implemented in the client but the server >+ * must have an action to execute. >+ * @param timerName the name for the timer >+ * @param variableName the variable name to store the current time delta in millisecs in >+ */ >+ public void gdReadTimer(String timerName, String variableName) { >+ // empty >+ } >+ >+ /** >+ * activate the AUT >+ * >+ * @param method activation method >+ */ >+ public void gdActivate(String method) { >+ getRobot().activateApplication(method); >+ } >+ >+ /** >+ * clicks into the active window. >+ * >+ * @param count amount of clicks >+ * @param button what mouse button should be used >+ * @param xPos what x position >+ * @param xUnits should x position be pixel or percent values >+ * @param yPos what y position >+ * @param yUnits should y position be pixel or percent values >+ * @throws StepExecutionException error >+ */ >+ public void gdClickDirect(int count, int button, >+ int xPos, String xUnits, int yPos, String yUnits) >+ throws StepExecutionException { >+ >+ Object activeWindow = getActiveWindow(); >+ if (activeWindow != null) { >+ getRobot().click(activeWindow, null, >+ ClickOptions.create() >+ .setClickCount(count) >+ .setConfirmClick(false) >+ .setMouseButton(button), >+ xPos, >+ xUnits.equalsIgnoreCase(POS_UNIT_PIXEL), >+ yPos, >+ yUnits.equalsIgnoreCase(POS_UNIT_PIXEL)); >+ } else { >+ throw new StepExecutionException("No active window.", //$NON-NLS-1$ >+ EventFactory.createActionError( >+ TestErrorEvent.NO_ACTIVE_WINDOW)); >+ } >+ } >+ >+ /** >+ * Checks for the existence of a window with the given title >+ * >+ * @param title >+ * the title >+ * @param operator >+ * the comparing operator >+ * @param exists >+ * <code>True</code> if the window is expected to exist and be >+ * visible, otherwise <code>false</code>. >+ */ >+// public void gdCheckExistenceOfWindow(final String title, String operator, >+// boolean exists) { >+// Verifier.equals(exists, isWindowOpen(title, operator)); >+// } >+ >+ /** >+ * Just a server side method, not useable as action. >+ * >+ * @param keyCode The key code >+ */ >+ public void gdKeyType(int keyCode) { >+ getRobot().keyType(null, keyCode); >+ } >+ >+ /** >+ * Just a server side method, not useable as action. >+ * >+ * note : this action only works if application got focus, >+ * because using defaultToolkit does not work. You have to >+ * use component.getToolKit()s >+ * @param key to set >+ * numlock Num Lock 1 >+ * caplock Caps Lock 2 >+ * scolllock Scroll 3 >+ * @param activated >+ * boolean >+ */ >+ public void gdToggle(int key, boolean activated) { >+ >+ int event = getEventCode(key); >+ if (event != 0) { >+ try { >+ getRobot().keyToggle(getFocusOwner(), >+ event, activated); >+ } catch (UnsupportedOperationException usoe) { >+ throw new StepExecutionException( >+ TestErrorEvent.UNSUPPORTED_OPERATION_ERROR, >+ EventFactory.createActionError( >+ TestErrorEvent.UNSUPPORTED_OPERATION_ERROR)); >+ } catch (OsNotSupportedException e) { >+ throw new StepExecutionException( >+ TestErrorEvent.UNSUPPORTED_OPERATION_ERROR, >+ EventFactory.createActionError( >+ TestErrorEvent.UNSUPPORTED_OPERATION_ERROR)); >+ } >+ } >+ } >+ >+ /** >+ * perform a keystroke >+ * @param modifierSpec the string representation of the modifiers >+ * @param keySpec the string representation of the key >+ */ >+ public abstract void gdKeyStroke(String modifierSpec, String keySpec); >+ /** >+ * >+ * @return the Focus Owner >+ */ >+ protected abstract Object getFocusOwner(); >+ /** >+ * >+ * @param key to set >+ * numlock Num Lock 1 >+ * caplock Caps Lock 2 >+ * scolllock Scroll 3 >+ * @return the toolkit specific eventcode >+ */ >+ protected abstract int getEventCode(int key); >+ >+ /** >+ * >+ * @return The active application window, or <code>null</code> if no >+ * application window is currently active. >+ */ >+ protected abstract Object getActiveWindow(); >+ >+ >+} >diff --git a/org.eclipse.jubula.rc.swing/src/org/eclipse/jubula/rc/swing/swing/caps/SwingApplicationCAPs.java b/org.eclipse.jubula.rc.swing/src/org/eclipse/jubula/rc/swing/swing/caps/SwingApplicationCAPs.java >new file mode 100644 >index 0000000..b962944 >--- /dev/null >+++ b/org.eclipse.jubula.rc.swing/src/org/eclipse/jubula/rc/swing/swing/caps/SwingApplicationCAPs.java >@@ -0,0 +1,577 @@ >+/******************************************************************************* >+ * Copyright (c) 2012 BREDEX GmbH. >+ * 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: >+ * BREDEX GmbH - initial API and implementation >+ *******************************************************************************/ >+package org.eclipse.jubula.rc.swing.swing.caps; >+ >+import java.awt.AWTEvent; >+import java.awt.Component; >+import java.awt.Dialog; >+import java.awt.Frame; >+import java.awt.Rectangle; >+import java.awt.Toolkit; >+import java.awt.Window; >+import java.awt.event.AWTEventListener; >+import java.awt.event.ComponentEvent; >+import java.awt.event.KeyEvent; >+import java.awt.event.WindowEvent; >+import java.util.Collection; >+import java.util.ConcurrentModificationException; >+import java.util.Iterator; >+ >+import org.eclipse.jubula.rc.common.AUTServer; >+import org.eclipse.jubula.rc.common.caps.AbstractApplicationCAPs; >+import org.eclipse.jubula.rc.common.driver.ClickOptions; >+import org.eclipse.jubula.rc.common.driver.IRobot; >+import org.eclipse.jubula.rc.common.exception.StepExecutionException; >+import org.eclipse.jubula.rc.common.implclasses.MatchUtil; >+import org.eclipse.jubula.rc.common.implclasses.Verifier; >+import org.eclipse.jubula.rc.common.listener.EventLock; >+import org.eclipse.jubula.rc.common.logger.AutServerLogger; >+import org.eclipse.jubula.rc.common.util.KeyStrokeUtil; >+import org.eclipse.jubula.rc.swing.components.SwingComponent; >+import org.eclipse.jubula.rc.swing.listener.ComponentHandler; >+import org.eclipse.jubula.rc.swing.listener.FocusTracker; >+import org.eclipse.jubula.rc.swing.swing.implclasses.EventListener; >+import org.eclipse.jubula.rc.swing.swing.implclasses.WindowHelper; >+import org.eclipse.jubula.rc.swing.swing.interfaces.IGraphicApplication; >+import org.eclipse.jubula.tools.constants.StringConstants; >+import org.eclipse.jubula.tools.objects.event.EventFactory; >+import org.eclipse.jubula.tools.objects.event.TestErrorEvent; >+import org.eclipse.jubula.tools.utils.TimeUtil; >+/** >+ * >+ * @author BREDEX GmbH >+ * >+ */ >+public class SwingApplicationCAPs extends AbstractApplicationCAPs implements >+ IGraphicApplication { >+ >+ /** >+ * This condition is true if the event is an 'window opened' event >+ * and the event source is a frame/dialog with a certain title. >+ * It is also true if the event is a 'component shown' event and the >+ * event source is a frame/dialog with a certain title. >+ */ >+ private static class WindowOpenedCondition >+ implements EventListener.Condition { >+ /** >+ * the title >+ */ >+ private final String m_title; >+ >+ /** the matches operation */ >+ private final String m_operator; >+ >+ /** >+ * constructor >+ * >+ * @param title the title >+ * @param operator the matches operation >+ */ >+ public WindowOpenedCondition(String title, String operator) { >+ m_title = title; >+ m_operator = operator; >+ } >+ /** >+ * {@inheritDoc} >+ */ >+ public boolean isTrue(AWTEvent event) { >+ if (event.getID() != WindowEvent.WINDOW_OPENED >+ && event.getID() != ComponentEvent.COMPONENT_SHOWN) { >+ return false; >+ } >+ if (event.getSource() instanceof Frame) { >+ Frame frame = (Frame)event.getSource(); >+ return MatchUtil.getInstance().match( >+ frame.getTitle(), m_title, m_operator); >+ } else if (event.getSource() instanceof Dialog) { >+ Dialog dialog = (Dialog)event.getSource(); >+ return MatchUtil.getInstance().match( >+ dialog.getTitle(), m_title, m_operator); >+ } else { >+ return false; >+ } >+ } >+ } >+ >+ /** >+ * This condition is true if the event is an 'window activated' event >+ * and the event source is a frame/dialog with a certain title. >+ */ >+ private static class WindowActivatedCondition >+ implements EventListener.Condition { >+ /** >+ * the title >+ */ >+ private final String m_title; >+ >+ /** the matches operation */ >+ private final String m_operator; >+ >+ /** >+ * constructor >+ * >+ * @param title the title >+ * @param operator the matches operation >+ */ >+ public WindowActivatedCondition(String title, String operator) { >+ m_title = title; >+ m_operator = operator; >+ } >+ /** >+ * {@inheritDoc} >+ */ >+ public boolean isTrue(AWTEvent event) { >+ if (event.getID() != WindowEvent.WINDOW_ACTIVATED) { >+ return false; >+ } >+ if (event.getSource() instanceof Frame) { >+ Frame frame = (Frame)event.getSource(); >+ return MatchUtil.getInstance().match( >+ frame.getTitle(), m_title, m_operator); >+ } else if (event.getSource() instanceof Dialog) { >+ Dialog dialog = (Dialog)event.getSource(); >+ return MatchUtil.getInstance().match( >+ dialog.getTitle(), m_title, m_operator); >+ } else { >+ return false; >+ } >+ } >+ } >+ >+ /** >+ * This condition is true if the event is an 'window closed' event >+ * and the event source is a frame/dialog with a certain title. >+ * It is also true if the event is a 'component hidden' event and the >+ * event source is a frame/dialog with a certain title. >+ */ >+ private static class WindowClosedCondition >+ implements EventListener.Condition { >+ /** >+ * the title >+ */ >+ private final String m_title; >+ >+ /** the matches operation */ >+ private final String m_operator; >+ >+ /** >+ * constructor >+ * >+ * @param title the title >+ * @param operator the matches operation >+ */ >+ public WindowClosedCondition(String title, String operator) { >+ m_title = title; >+ m_operator = operator; >+ } >+ /** >+ * {@inheritDoc} >+ */ >+ public boolean isTrue(AWTEvent event) { >+ if (event.getID() != WindowEvent.WINDOW_CLOSED >+ && event.getID() != ComponentEvent.COMPONENT_HIDDEN) { >+ return false; >+ } >+ if (event.getSource() instanceof Frame) { >+ Frame frame = (Frame)event.getSource(); >+ return MatchUtil.getInstance().match( >+ frame.getTitle(), m_title, m_operator); >+ } else if (event.getSource() instanceof Dialog) { >+ Dialog dialog = (Dialog)event.getSource(); >+ return MatchUtil.getInstance().match( >+ dialog.getTitle(), m_title, m_operator); >+ } else { >+ return false; >+ } >+ } >+ } >+ >+ /** >+ * The logging. >+ */ >+ private static AutServerLogger log = >+ new AutServerLogger(SwingApplicationCAPs.class); >+ >+ /** >+ * {@inheritDoc} >+ */ >+ public String[] getTextArrayFromComponent() { >+ return null; >+ } >+ >+ >+ /** >+ * Types <code>text</code> into the component. This replaces the shown >+ * content. >+ * @param text the text to type in >+ * @deprecated Removed without substitution: >+ * Testcases with this action are fragile, because this action assumes the >+ * availabality of a text component. Any other case breaks the test. >+ */ >+ public void gdReplaceText(String text) { >+ getRobot().click(FocusTracker.getFocusOwner(), null, >+ ClickOptions.create().setClickCount(3).left()); >+ if (StringConstants.EMPTY.equals(text)) { >+ getRobot().keyStroke("DELETE"); //$NON-NLS-1$ >+ } >+ gdInputText(text); >+ } >+ >+ /** >+ * Waits <code>timeMillSec</code> if the application opens a window >+ * with the given title. >+ * >+ * @param title the title >+ * @param operator the comparing operator >+ * @param pTimeout the time in ms >+ * @param delay delay after the window is shown >+ */ >+ public void gdWaitForWindow(final String title, String operator, >+ int pTimeout, int delay) { >+ >+ EventListener.Condition cond = >+ new WindowOpenedCondition(title, operator); >+ EventLock lock = new EventLock(); >+ AWTEventListener listener = new EventListener(lock, cond); >+ Toolkit.getDefaultToolkit().addAWTEventListener(listener, >+ AWTEvent.WINDOW_EVENT_MASK | AWTEvent.COMPONENT_EVENT_MASK); >+ >+ if (isWindowOpen(title, operator)) { >+ lock.release(); >+ } >+ try { >+ synchronized (lock) { >+ long timeout = pTimeout; >+ long done = System.currentTimeMillis() + timeout; >+ long now; >+ while (!lock.isReleased() && (timeout > 0)) { >+ try { >+ lock.wait(timeout); >+ now = System.currentTimeMillis(); >+ timeout = done - now; >+ } catch (InterruptedException e) { >+ // ignore >+ } >+ } >+ } >+ } finally { >+ Toolkit.getDefaultToolkit().removeAWTEventListener(listener); >+ } >+ if (!lock.isReleased() && !isWindowOpen(title, operator)) { >+ throw new StepExecutionException("window did not open", //$NON-NLS-1$ >+ EventFactory.createActionError( >+ TestErrorEvent.TIMEOUT_EXPIRED)); >+ } >+ TimeUtil.delay(delay); >+ } >+ /** >+ * Waits <code>timeMillSec</code> if the application activates a window >+ * with the given title. >+ * >+ * @param title the title >+ * @param operator the comparing operator >+ * @param pTimeout the time in ms >+ * @param delay delay after the window is activated >+ */ >+ public void gdWaitForWindowActivation(final String title, String operator, >+ int pTimeout, int delay) { >+ >+ EventListener.Condition cond = new WindowActivatedCondition(title, >+ operator); >+ EventLock lock = new EventLock(); >+ AWTEventListener listener = new EventListener(lock, cond); >+ Toolkit.getDefaultToolkit().addAWTEventListener(listener, >+ AWTEvent.WINDOW_EVENT_MASK); >+ >+ if (isWindowActive(title, operator)) { >+ lock.release(); >+ } >+ try { >+ synchronized (lock) { >+ long timeout = pTimeout; >+ long done = System.currentTimeMillis() + timeout; >+ long now; >+ while (!lock.isReleased() && (timeout > 0)) { >+ try { >+ lock.wait(timeout); >+ now = System.currentTimeMillis(); >+ timeout = done - now; >+ } catch (InterruptedException e) { >+ // ignore >+ } >+ >+ } >+ } >+ } finally { >+ Toolkit.getDefaultToolkit().removeAWTEventListener(listener); >+ } >+ if (!lock.isReleased() && !isWindowActive(title, operator)) { >+ throw new StepExecutionException("window was not activated", //$NON-NLS-1$ >+ EventFactory.createActionError( >+ TestErrorEvent.TIMEOUT_EXPIRED)); >+ } >+ TimeUtil.delay(delay); >+ } >+ >+ /** >+ * Waits <code>timeMillSec</code> if the application closes (or hides) >+ * a window with the given title. If no window with the given title can >+ * be found, then it is assumed that the window has already closed. >+ * >+ * @param title the title >+ * @param operator the comparing operator >+ * @param pTimeout the time in ms >+ * @param delay delay after the window is closed >+ */ >+ public void gdWaitForWindowToClose(final String title, String operator, >+ int pTimeout, int delay) { >+ >+ EventListener.Condition cond = >+ new WindowClosedCondition(title, operator); >+ EventLock lock = new EventLock(); >+ AWTEventListener listener = new EventListener(lock, cond); >+ >+ Toolkit.getDefaultToolkit().addAWTEventListener(listener, >+ AWTEvent.WINDOW_EVENT_MASK); >+ if (!isWindowOpen(title, operator)) { >+ lock.release(); >+ } >+ >+ try { >+ synchronized (lock) { >+ long timeout = pTimeout; >+ long done = System.currentTimeMillis() + timeout; >+ long now; >+ while (!lock.isReleased() && (timeout > 0)) { >+ try { >+ lock.wait(timeout); >+ now = System.currentTimeMillis(); >+ timeout = done - now; >+ } catch (InterruptedException e) { >+ // ignore >+ } >+ } >+ } >+ } finally { >+ Toolkit.getDefaultToolkit().removeAWTEventListener(listener); >+ } >+ if (!lock.isReleased() && isWindowOpen(title, operator)) { >+ throw new StepExecutionException("window did not close", //$NON-NLS-1$ >+ EventFactory.createActionError( >+ TestErrorEvent.TIMEOUT_EXPIRED)); >+ } >+ >+ TimeUtil.delay(delay); >+ } >+ >+ /** >+ * Checks for the existence of a window with the given title >+ * >+ * @param title >+ * the title >+ * @param operator >+ * the comparing operator >+ * @param exists >+ * <code>True</code> if the window is expected to exist and be >+ * visible, otherwise <code>false</code>. >+ */ >+ public void gdCheckExistenceOfWindow(final String title, String operator, >+ boolean exists) { >+ Verifier.equals(exists, isWindowOpen(title, operator)); >+ } >+ >+ /** >+ * {@inheritDoc} >+ */ >+ public Rectangle getActiveWindowBounds() { >+ Window activeWindow = WindowHelper.getActiveWindow(); >+ if (activeWindow != null) { >+ Rectangle activeWindowBounds = >+ new Rectangle(activeWindow.getBounds()); >+ activeWindowBounds.setLocation(activeWindow.getLocationOnScreen()); >+ >+ return activeWindowBounds; >+ } >+ return null; >+ } >+ /** >+ * {@inheritDoc} >+ */ >+ protected IRobot getRobot() { >+ return AUTServer.getInstance().getRobot(); >+ } >+ >+ /** >+ * perform a keystroke specified according <a >+ * href=http://java.sun.com/j2se/1.4.2/docs/api/javax/swing/KeyStroke.html#getKeyStroke(java.lang.String)> >+ * string representation of a keystroke </a>, >+ * >+ * @param modifierSpec the string representation of the modifiers >+ * @param keySpec the string representation of the key >+ */ >+ public void gdKeyStroke(String modifierSpec, String keySpec) { >+ if (keySpec == null || keySpec.trim().length() == 0) { >+ throw new StepExecutionException( >+ "The base key of the key stroke must not be null or empty", //$NON-NLS-1$ >+ EventFactory.createActionError()); >+ } >+ String key = keySpec.trim().toUpperCase(); >+ String mod = KeyStrokeUtil.getModifierString(modifierSpec); >+ if (mod.length() > 0) { >+ getRobot().keyStroke(mod.toString() + " " + key); //$NON-NLS-1$ >+ } else { >+ int code = getKeyCode(key); >+ if (code != -1) { >+ gdKeyType(code); >+ } else { >+ getRobot().keyStroke(key); >+ } >+ } >+ } >+ /** >+ * {@inheritDoc} >+ */ >+ protected Object getFocusOwner() { >+ return FocusTracker.getFocusOwner(); >+ } >+ >+ /** >+ * {@inheritDoc} >+ */ >+ protected int getEventCode(int key) { >+ int event = 0; >+ switch (key) { >+ case 1 : >+ event = KeyEvent.VK_NUM_LOCK; >+ break; >+ case 2 : >+ event = KeyEvent.VK_CAPS_LOCK; >+ break; >+ case 3 : >+ event = KeyEvent.VK_SCROLL_LOCK; >+ break; >+ default : >+ break; >+ } >+ return event; >+ } >+ >+ /** >+ * {@inheritDoc} >+ */ >+ protected Object getActiveWindow() { >+ return WindowHelper.getActiveWindow(); >+ } >+ >+ /** >+ * Returns <code>true</code> if a window with the given title is open and >+ * visible. >+ * >+ * @param title the title >+ * @param operator the matches/equals operator >+ * @return if the window is open and visible >+ */ >+ private boolean isWindowOpen(String title, String operator) { >+ boolean wasInterrupted; >+ do { >+ try { >+ wasInterrupted = false; >+ Collection components = ComponentHandler.getAutHierarchy() >+ .getHierarchyMap().keySet(); >+ for (Iterator it = components.iterator(); it.hasNext();) { >+ Component c = ((SwingComponent)it.next()) >+ .getRealComponent(); >+ if (c.isShowing()) { >+ if (c instanceof Frame) { >+ Frame frame = (Frame)c; >+ if (MatchUtil.getInstance().match(frame.getTitle(), >+ title, operator)) { >+ >+ return true; >+ } >+ } >+ if (c instanceof Dialog) { >+ Dialog dialog = (Dialog)c; >+ if (MatchUtil.getInstance().match(dialog.getTitle(), >+ title, operator)) { >+ >+ return true; >+ } >+ } >+ } >+ } >+ } catch (ConcurrentModificationException e) { >+ log.debug("hierarchy modified while traversing", e); //$NON-NLS-1$ >+ wasInterrupted = true; >+ } >+ } while (wasInterrupted); >+ return false; >+ } >+ >+ /** >+ * Returns <code>true</code> if a window with the given title has focus >+ * >+ * @param title the title >+ * @param operator the matches/equals operator >+ * @return if the window has focus >+ */ >+ private boolean isWindowActive(String title, String operator) { >+ >+ Window activeWindow = WindowHelper.getActiveWindow(); >+ if (activeWindow != null) { >+ String windowTitle = null; >+ if (activeWindow instanceof Dialog) { >+ windowTitle = ((Dialog)activeWindow).getTitle(); >+ } else if (activeWindow instanceof Frame) { >+ windowTitle = ((Frame)activeWindow).getTitle(); >+ } >+ >+ if (MatchUtil.getInstance().match(windowTitle, title, operator)) { >+ return true; >+ } >+ } >+ >+ return false; >+ } >+ >+ /** >+ * @param keyCodeName >+ * The name of a key code, e.g. <code>TAB</code> for a >+ * tabulator key code >+ * @return The key code or <code>-1</code>, if the key code name doesn't >+ * exist in the <code>KeyEvent</code> class >+ * @throws StepExecutionException >+ * If the key code name cannot be converted to a key code due to >+ * the reflection call >+ */ >+ public int getKeyCode(String keyCodeName) throws StepExecutionException { >+ int code = -1; >+ String codeName = "VK_" + keyCodeName; //$NON-NLS-1$ >+ try { >+ code = KeyEvent.class.getField(codeName).getInt(KeyEvent.class); >+ } catch (IllegalArgumentException e) { >+ throw new StepExecutionException(e.getMessage(), EventFactory >+ .createActionError()); >+ } catch (SecurityException e) { >+ throw new StepExecutionException(e.getMessage(), EventFactory >+ .createActionError()); >+ } catch (IllegalAccessException e) { >+ throw new StepExecutionException(e.getMessage(), EventFactory >+ .createActionError()); >+ } catch (NoSuchFieldException e) { >+ if (log.isInfoEnabled()) { >+ log.info("The key expression '" + keyCodeName //$NON-NLS-1$ >+ + "' is not a key code, typed as key stroke instead"); //$NON-NLS-1$ >+ } >+ } >+ return code; >+ } >+} >diff --git a/org.eclipse.jubula.rc.swt/src/org/eclipse/jubula/rc/swt/caps/SwtApplicationCAPs.java b/org.eclipse.jubula.rc.swt/src/org/eclipse/jubula/rc/swt/caps/SwtApplicationCAPs.java >new file mode 100644 >index 0000000..8b70a01 >--- /dev/null >+++ b/org.eclipse.jubula.rc.swt/src/org/eclipse/jubula/rc/swt/caps/SwtApplicationCAPs.java >@@ -0,0 +1,548 @@ >+/******************************************************************************* >+ * Copyright (c) 2012 BREDEX GmbH. >+ * 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: >+ * BREDEX GmbH - initial API and implementation >+ *******************************************************************************/ >+package org.eclipse.jubula.rc.swt.caps; >+ >+import java.awt.Rectangle; >+import java.util.Collection; >+import java.util.ConcurrentModificationException; >+import java.util.Iterator; >+ >+import org.eclipse.jubula.rc.common.AUTServer; >+import org.eclipse.jubula.rc.common.caps.AbstractApplicationCAPs; >+import org.eclipse.jubula.rc.common.driver.ClickOptions; >+import org.eclipse.jubula.rc.common.driver.IEventThreadQueuer; >+import org.eclipse.jubula.rc.common.driver.IRobot; >+import org.eclipse.jubula.rc.common.driver.IRobotFactory; >+import org.eclipse.jubula.rc.common.driver.IRunnable; >+import org.eclipse.jubula.rc.common.exception.StepExecutionException; >+import org.eclipse.jubula.rc.common.implclasses.MatchUtil; >+import org.eclipse.jubula.rc.common.implclasses.Verifier; >+import org.eclipse.jubula.rc.common.listener.EventLock; >+import org.eclipse.jubula.rc.common.logger.AutServerLogger; >+import org.eclipse.jubula.rc.common.util.KeyStrokeUtil; >+import org.eclipse.jubula.rc.common.util.WorkaroundUtil; >+import org.eclipse.jubula.rc.swt.SwtAUTServer; >+import org.eclipse.jubula.rc.swt.components.SwtComponent; >+import org.eclipse.jubula.rc.swt.driver.EventThreadQueuerSwtImpl; >+import org.eclipse.jubula.rc.swt.driver.RobotFactoryConfig; >+import org.eclipse.jubula.rc.swt.implclasses.EventListener; >+import org.eclipse.jubula.rc.swt.implclasses.EventListener.Condition; >+import org.eclipse.jubula.rc.swt.interfaces.IGraphicApplication; >+import org.eclipse.jubula.rc.swt.listener.ComponentHandler; >+import org.eclipse.jubula.rc.swt.listener.FocusTracker; >+import org.eclipse.jubula.rc.swt.utils.SwtPointUtil; >+import org.eclipse.jubula.tools.constants.StringConstants; >+import org.eclipse.jubula.tools.objects.event.EventFactory; >+import org.eclipse.jubula.tools.objects.event.TestErrorEvent; >+import org.eclipse.jubula.tools.utils.EnvironmentUtils; >+import org.eclipse.jubula.tools.utils.TimeUtil; >+import org.eclipse.swt.SWT; >+import org.eclipse.swt.widgets.Display; >+import org.eclipse.swt.widgets.Event; >+import org.eclipse.swt.widgets.Listener; >+import org.eclipse.swt.widgets.Shell; >+import org.eclipse.swt.widgets.Widget; >+/** >+ * >+ * @author BREDEX GmbH >+ * >+ */ >+public class SwtApplicationCAPs extends AbstractApplicationCAPs implements >+ IGraphicApplication { >+ >+ /** The logging. */ >+ private static AutServerLogger log = >+ new AutServerLogger(SwtApplicationCAPs.class); >+ >+ /** >+ * This condition is true if the event source is a Shell with a matching >+ * title. >+ * >+ * @author BREDEX GmbH >+ * @created Jun 17, 2009 >+ */ >+ private static class WindowEventCondition implements Condition { >+ >+ /** the expected window title */ >+ private String m_windowTitle; >+ >+ /** the operator used for matching the window title */ >+ private String m_matchingOperator; >+ >+ /** >+ * determines whether the event source being disposed should be >+ * treated as a match >+ */ >+ private boolean m_valForDisposed; >+ >+ /** >+ * Constructor >+ * >+ * @param windowTitle The expected window title. >+ * @param matchingOperator The operator used for matching the >+ * window title. >+ * @param valForDisposed Whether the event source being disposed >+ * should be treated as a match. >+ */ >+ public WindowEventCondition(String windowTitle, >+ String matchingOperator, boolean valForDisposed) { >+ m_windowTitle = windowTitle; >+ m_matchingOperator = matchingOperator; >+ m_valForDisposed = valForDisposed; >+ } >+ >+ /** >+ * {@inheritDoc} >+ */ >+ public boolean isTrue(Event event) { >+ if (event.widget instanceof Shell) { >+ Shell window = (Shell)event.widget; >+ if (window.isDisposed()) { >+ return m_valForDisposed; >+ } >+ return MatchUtil.getInstance().match(window.getText(), >+ m_windowTitle, m_matchingOperator); >+ >+ } >+ >+ return false; >+ } >+ >+ } >+ >+ /** The Robot factory. */ >+ private IRobotFactory m_robotFactory; >+ >+ /** >+ * @return The Robot factory instance >+ */ >+ private IRobotFactory getRobotFactory() { >+ if (m_robotFactory == null) { >+ m_robotFactory = new RobotFactoryConfig().getRobotFactory(); >+ } >+ return m_robotFactory; >+ } >+ >+ /** >+ * {@inheritDoc} >+ */ >+ public String[] getTextArrayFromComponent() { >+ return null; >+ } >+ >+ /** >+ * Types <code>text</code> into the component. This replaces the shown >+ * content. >+ * @param text the text to type in >+ * @deprecated Removed without substitution: >+ * Testcases with this action are fragile, because this action assumes the >+ * availabality of a text component. Any other case breaks the test. >+ */ >+ public void gdReplaceText(String text) { >+ // The number of clicks differs from the Swing implementation >+ // because a double-click selects all of the text >+ getRobot().click(FocusTracker.getFocusOwner(), null, >+ ClickOptions.create().setClickCount(2).left()); >+ if (StringConstants.EMPTY.equals(text)) { >+ getRobot().keyStroke("DELETE"); //$NON-NLS-1$ >+ } >+ gdInputText(text); >+ } >+ >+ /** >+ * Waits <code>timeMillSec</code> if the application opens a window with the given title. >+ * @param title the title >+ * @param operator the comparing operator >+ * @param timeout the time in ms >+ * @param delay delay after the window is shown >+ */ >+ public void gdWaitForWindow(final String title, final String operator, >+ int timeout, int delay) { >+ >+ final EventListener.Condition cond = >+ new WindowEventCondition(title, operator, false); >+ final EventLock lock = new EventLock(); >+ final Listener listener = new EventListener(lock, cond); >+ final Display display = >+ ((SwtAUTServer)AUTServer.getInstance()).getAutDisplay(); >+ final IEventThreadQueuer queuer = new EventThreadQueuerSwtImpl(); >+ >+ queuer.invokeAndWait("addWindowOpenedListeners", new IRunnable() { //$NON-NLS-1$ >+ public Object run() { >+ display.addFilter(SWT.Activate, listener); >+ display.addFilter(SWT.Show, listener); >+ if (isWindowOpen(title, operator)) { >+ lock.release(); >+ } >+ >+ return null; >+ } >+ }); >+ >+ try { >+ synchronized (lock) { >+ long currentTimeout = timeout; >+ long done = System.currentTimeMillis() + timeout; >+ long now; >+ while (!lock.isReleased() && (currentTimeout > 0)) { >+ try { >+ lock.wait(currentTimeout); >+ now = System.currentTimeMillis(); >+ currentTimeout = done - now; >+ } catch (InterruptedException e) { >+ // ignore >+ } >+ } >+ } >+ } finally { >+ queuer.invokeAndWait("removeWindowOpenedListeners", new IRunnable() { //$NON-NLS-1$ >+ public Object run() { >+ display.removeFilter(SWT.Activate, listener); >+ display.removeFilter(SWT.Show, listener); >+ >+ return null; >+ } >+ }); >+ } >+ if (!lock.isReleased()) { >+ throw new StepExecutionException("window did not open", //$NON-NLS-1$ >+ EventFactory.createActionError( >+ TestErrorEvent.TIMEOUT_EXPIRED)); >+ } >+ TimeUtil.delay(delay); >+ } >+ >+ /** >+ * Waits <code>timeMillSec</code> if the application activates a window >+ * with the given title. >+ * >+ * @param title the title >+ * @param operator the comparing operator >+ * @param timeout the time in ms >+ * @param delay delay after the window is activated >+ */ >+ public void gdWaitForWindowActivation(final String title, >+ final String operator, final int timeout, int delay) { >+ >+ final EventListener.Condition cond = >+ new WindowEventCondition(title, operator, false); >+ final EventLock lock = new EventLock(); >+ final Listener listener = new EventListener(lock, cond); >+ final Display display = >+ ((SwtAUTServer)AUTServer.getInstance()).getAutDisplay(); >+ final IEventThreadQueuer queuer = new EventThreadQueuerSwtImpl(); >+ >+ queuer.invokeAndWait("addWindowActiveListeners", new IRunnable() { //$NON-NLS-1$ >+ public Object run() { >+ display.addFilter(SWT.Activate, listener); >+ if (isWindowActive(title, operator)) { >+ lock.release(); >+ } >+ >+ return null; >+ } >+ }); >+ >+ try { >+ synchronized (lock) { >+ long currentTimeout = timeout; >+ long done = System.currentTimeMillis() + timeout; >+ long now; >+ while (!lock.isReleased() && (currentTimeout > 0)) { >+ try { >+ lock.wait(currentTimeout); >+ now = System.currentTimeMillis(); >+ currentTimeout = done - now; >+ } catch (InterruptedException e) { >+ // ignore >+ } >+ } >+ } >+ } finally { >+ queuer.invokeAndWait("removeWindowActiveListeners", new IRunnable() { //$NON-NLS-1$ >+ public Object run() { >+ display.removeFilter(SWT.Activate, listener); >+ >+ return null; >+ } >+ }); >+ } >+ if (!lock.isReleased()) { >+ throw new StepExecutionException("window was not activated", //$NON-NLS-1$ >+ EventFactory.createActionError( >+ TestErrorEvent.TIMEOUT_EXPIRED)); >+ } >+ TimeUtil.delay(delay); >+ } >+ >+ /** >+ * Waits <code>timeMillSec</code> if the application closes (or hides) >+ * a window with the given title. If no window with the given title can >+ * be found, then it is assumed that the window has already closed. >+ * >+ * @param title the title >+ * @param operator the comparing operator >+ * @param timeout the time in ms >+ * @param delay delay after the window is activated >+ */ >+ public void gdWaitForWindowToClose(final String title, >+ final String operator, int timeout, int delay) { >+ >+ final EventListener.Condition cond = >+ new WindowEventCondition(title, operator, true); >+ final EventLock lock = new EventLock(); >+ final Listener listener = new EventListener(lock, cond); >+ final Display display = >+ ((SwtAUTServer)AUTServer.getInstance()).getAutDisplay(); >+ final IEventThreadQueuer queuer = new EventThreadQueuerSwtImpl(); >+ >+ queuer.invokeAndWait("addWindowClosedListeners", new IRunnable() { //$NON-NLS-1$ >+ public Object run() { >+ display.addFilter(SWT.Close, listener); >+ display.addFilter(SWT.Hide, listener); >+ display.addFilter(SWT.Dispose, listener); >+ if (!isWindowOpen(title, operator)) { >+ lock.release(); >+ } >+ >+ return null; >+ } >+ }); >+ >+ try { >+ synchronized (lock) { >+ long currentTimeout = timeout; >+ long done = System.currentTimeMillis() + timeout; >+ long now; >+ while (!lock.isReleased() && (currentTimeout > 0)) { >+ try { >+ lock.wait(currentTimeout); >+ now = System.currentTimeMillis(); >+ currentTimeout = done - now; >+ } catch (InterruptedException e) { >+ // ignore >+ } >+ } >+ } >+ } finally { >+ queuer.invokeAndWait("removeWindowClosedListeners", new IRunnable() { //$NON-NLS-1$ >+ public Object run() { >+ display.removeFilter(SWT.Close, listener); >+ display.removeFilter(SWT.Hide, listener); >+ display.removeFilter(SWT.Dispose, listener); >+ >+ return null; >+ } >+ }); >+ } >+ if (!lock.isReleased()) { >+ throw new StepExecutionException("window did not close", //$NON-NLS-1$ >+ EventFactory.createActionError( >+ TestErrorEvent.TIMEOUT_EXPIRED)); >+ } >+ TimeUtil.delay(delay); >+ } >+ >+ /** >+ * Returns <code>true</code> if a window with the given title is open and >+ * visible >+ * >+ * @param title the title >+ * @param operator the matches/equals operator >+ * @return if the window is open and visible >+ */ >+ private boolean isWindowOpen(final String title, final String operator) { >+ boolean wasInterrupted = false; >+ boolean equal = false; >+ do { >+ try { >+ wasInterrupted = false; >+ Collection components = ComponentHandler >+ .getAutHierarchy().getHierarchyMap() >+ .keySet(); >+ for (Iterator it = components.iterator(); it.hasNext();) { >+ >+ Widget comp = ((SwtComponent)it.next()).getRealComponent(); >+ if (comp instanceof Shell >+ && !comp.isDisposed() >+ && ((Shell)comp).isVisible()) { >+ >+ Shell frame = (Shell)comp; >+ if (MatchUtil.getInstance().match( >+ frame.getText(), title, operator)) { >+ >+ equal = true; >+ break; >+ } >+ } >+ } >+ >+ } catch (ConcurrentModificationException e) { >+ log.debug("hierarchy modified while traversing", e); //$NON-NLS-1$ >+ wasInterrupted = true; >+ } >+ } while (wasInterrupted); >+ return equal; >+ } >+ >+ /** >+ * Checks for the existence of a window with the given title >+ * >+ * @param title >+ * the title >+ * @param operator >+ * the comparing operator >+ * @param exists >+ * <code>True</code> if the component is expected to exist and be >+ * visible, otherwise <code>false</code>. >+ */ >+ public void gdCheckExistenceOfWindow(final String title, >+ final String operator, final boolean exists) { >+ IEventThreadQueuer queuer = new EventThreadQueuerSwtImpl(); >+ Boolean windowExists = (Boolean)queuer.invokeAndWait( >+ "isWindowOpen", new IRunnable() { //$NON-NLS-1$ >+ public Object run() throws StepExecutionException { >+ return new Boolean(isWindowOpen(title, operator)); >+ } >+ }); >+ Verifier.equals(exists, windowExists.booleanValue()); >+ } >+ >+ /** >+ * {@inheritDoc} >+ */ >+ public Rectangle getActiveWindowBounds() { >+ org.eclipse.swt.graphics.Rectangle activeWindowSize = >+ (org.eclipse.swt.graphics.Rectangle)getRobotFactory() >+ .getEventThreadQueuer().invokeAndWait( >+ this.getClass().getName() + ".getActiveWindowBounds", //$NON-NLS-1$ >+ new IRunnable() { >+ public Object run() { // SYNCH THREAD START >+ Display d = ((SwtAUTServer)AUTServer >+ .getInstance()).getAutDisplay(); >+ if (d != null && d.getActiveShell() != null) { >+ return d.getActiveShell().getBounds(); >+ } >+ return null; >+ } >+ }); >+ if (activeWindowSize != null) { >+ return SwtPointUtil.toAwtRectangle(activeWindowSize); >+ } >+ return null; >+ } >+ /** >+ * {@inheritDoc} >+ */ >+ protected IRobot getRobot() { >+ return AUTServer.getInstance().getRobot(); >+ } >+ >+ /** >+ * perform a keystroke >+ * @param modifierSpec the string representation of the modifiers >+ * @param keySpec the string representation of the key >+ */ >+ public void gdKeyStroke(String modifierSpec, String keySpec) { >+ if (keySpec == null || keySpec.trim().length() == 0) { >+ throw new StepExecutionException( >+ "The base key of the key stroke must not be null or empty", //$NON-NLS-1$ >+ EventFactory.createActionError( >+ TestErrorEvent.INVALID_PARAM_VALUE)); >+ } >+ String keyStrokeSpec = keySpec.trim(); >+ String mod = KeyStrokeUtil.getModifierString(modifierSpec); >+ if (mod.length() > 0) { >+ keyStrokeSpec = mod + " " + keyStrokeSpec; //$NON-NLS-1$ >+ } >+ String keySpecification = keySpec.trim().toLowerCase(); >+ if (EnvironmentUtils.isMacOS() && keySpecification.length() == 1 >+ && keySpecification.charAt(0) == WorkaroundUtil.CHAR_B) { >+ gdNativeKeyStroke(modifierSpec, keySpec); >+ } else { >+ // at this the key stroke specification is not fully fullfilled as the >+ // key stroke spec base key is not definitly upper case >+ getRobot().keyStroke(keyStrokeSpec); >+ } >+ } >+ /** >+ * {@inheritDoc} >+ */ >+ protected Object getFocusOwner() { >+ return FocusTracker.getFocusOwner(); >+ } >+ /** >+ * {@inheritDoc} >+ */ >+ protected int getEventCode(int key) { >+ int event = 0; >+ switch (key) { >+ case 1 : >+ event = SWT.NUM_LOCK; >+ break; >+ case 2 : >+ event = SWT.CAPS_LOCK; >+ break; >+ case 3 : >+ event = SWT.SCROLL_LOCK; >+ break; >+ default : >+ break; >+ } >+ return event; >+ } >+ /** >+ * {@inheritDoc} >+ */ >+ protected Object getActiveWindow() { >+ Shell activeWindow = (Shell)getRobotFactory().getEventThreadQueuer() >+ .invokeAndWait(this.getClass().getName() + ".getActiveWindow", //$NON-NLS-1$ >+ >+ new IRunnable() { >+ public Object run() { // SYNCH THREAD START >+ Display d = >+ ((SwtAUTServer)AUTServer.getInstance()).getAutDisplay(); >+ return d.getActiveShell(); >+ >+ } >+ } >+ >+ ); >+ >+ return activeWindow; >+ } >+ >+ /** >+ * Returns <code>true</code> if a window with the given title is active >+ * (the window with focus). >+ * >+ * @param title the title >+ * @param operator the matches/equals operator >+ * @return if the window is open and visible >+ */ >+ private boolean isWindowActive(final String title, final String operator) { >+ final Shell activeWindow = (Shell) getActiveWindow(); >+ >+ if (activeWindow == null) { >+ if (log.isWarnEnabled()) { >+ log.warn("No active Window found while searching for Window with title: '" //$NON-NLS-1$ >+ + String.valueOf(title) + "'! " + //$NON-NLS-1$ >+ "(SwtApplicationImplClass#isWindowActive(String, String))"); //$NON-NLS-1$ >+ } >+ return false; >+ } >+ >+ final String windowTitle = activeWindow.getText(); >+ >+ return MatchUtil.getInstance().match(windowTitle, title, operator); >+ } >+ >+} >diff --git a/org.eclipse.jubula.toolkit.provider.swing/resources/xml/ComponentConfiguration.xml b/org.eclipse.jubula.toolkit.provider.swing/resources/xml/ComponentConfiguration.xml >index 5379fdd..adb6323 100644 >--- a/org.eclipse.jubula.toolkit.provider.swing/resources/xml/ComponentConfiguration.xml >+++ b/org.eclipse.jubula.toolkit.provider.swing/resources/xml/ComponentConfiguration.xml >@@ -21,7 +21,7 @@ > <typeFactory>org.eclipse.jubula.rc.common.implclasses.DefaultComponentFactory</typeFactory> > </defaultMapping> > <realizes>guidancer.concrete.GraphicApplication</realizes> >- <testerClass>org.eclipse.jubula.rc.swing.swing.implclasses.SwingApplicationImplClass</testerClass> >+ <testerClass>org.eclipse.jubula.rc.swing.swing.caps.SwingApplicationCAPs</testerClass> > <componentClass name="com.bredexsw.guidancer.autserver.swing.implclasses.GraphicApplication" /> > </toolkitComponent> > >diff --git a/org.eclipse.jubula.toolkit.provider.swt/resources/xml/ComponentConfiguration.xml b/org.eclipse.jubula.toolkit.provider.swt/resources/xml/ComponentConfiguration.xml >index 1e25940..7353acf 100644 >--- a/org.eclipse.jubula.toolkit.provider.swt/resources/xml/ComponentConfiguration.xml >+++ b/org.eclipse.jubula.toolkit.provider.swt/resources/xml/ComponentConfiguration.xml >@@ -194,7 +194,7 @@ > <typeFactory>org.eclipse.jubula.rc.common.implclasses.DefaultComponentFactory</typeFactory> > </defaultMapping> > <realizes>guidancer.concrete.GraphicApplication</realizes> >- <testerClass>org.eclipse.jubula.rc.swt.implclasses.SwtApplicationImplClass</testerClass> >+ <testerClass>org.eclipse.jubula.rc.swt.caps.SwtApplicationCAPs</testerClass> > <componentClass name="com.bredexsw.guidancer.autswtserver.implclasses.GraphicApplication" /> > </toolkitComponent> > >-- >1.7.11 >
You cannot view the attachment while viewing its details because your browser does not support IFRAMEs.
View the attachment on a separate page
.
View Attachment As Diff
View Attachment As Raw
Flags:
markus.tiede
:
iplog+
Actions:
View
|
Diff
Attachments on
bug 394179
:
223499
|
223848
|
223948
|
223950
|
224096
|
224662
| 225163