/**
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
* specific language governing permissions and limitations under the License.
*
* Copyright 2012-2015 the original author or authors.
*/
package org.assertj.swing.core;
import static java.lang.Math.max;
import static java.lang.Math.min;
import static org.assertj.swing.core.ComponentLookupScope.DEFAULT;
import static org.assertj.swing.core.MouseButton.LEFT_BUTTON;
import static org.assertj.swing.util.Platform.isOSX;
import static org.assertj.swing.util.Platform.isWindows;
import static org.assertj.swing.util.Platform.isX11;
import java.io.IOException;
import java.io.InputStream;
import java.util.Properties;
import java.util.function.Function;
import javax.annotation.Nonnull;
import org.assertj.core.util.VisibleForTesting;
/**
* <p>
* AssertJ-Swing configuration settings. There are two kinds of configuration entries, writable and only readable
* entries.
* </p>
* <p>
* The read-only entries have hard-coded defaults which may be overwritten by <code>assertj-swing.properties</code>,
* which may be overwritten by system properties. See the javadoc of each method to find out the key which is identical
* in the properties file and as system property. You can identify read-only properties by the static methods in this
* class.
* </p>
* <p>
* The writable entries are not static but depend on the {@link Settings} object which is created in {@link BasicRobot}
* and can be accessed through the robot instance. The initialisation is the same as for read-only properties, but you
* may change the value at runtime calling the setter methods.
* </p>
*
* @author Alex Ruiz
*/
public class Settings {
private static final int DEFAULT_TIMEOUT_VISIBILITY;
private static final int DEFAULT_TIMEOUT_POPUP;
private static final int DEFAULT_TIMEOUT_SUBMENU;
private static final int DEFAULT_DELAY_BETWEEN_EVENTS;
private static final int DEFAULT_DELAY_DRAG;
private static final int DEFAULT_DELAY_DROP;
private static final int DEFAULT_DELAY_POSTING_EVENTS;
private static final ComponentLookupScope DEFAULT_LOOKUP_SCOPE;
private static final int DEFAULT_TIMEOUT_IDLE;
private static final boolean DEFAULT_CLICK_ON_DISABLED;
private static final MouseButton DEFAULT_DRAG_BUTTON;
private static final boolean PRESERVE_SCREENSHOTS;
static {
Properties p = new Properties();
try {
InputStream stream = Settings.class.getResourceAsStream("/assertj-swing.properties");
if (stream != null) {
p.load(stream);
}
} catch (IOException e) {
e.printStackTrace();
}
DEFAULT_TIMEOUT_VISIBILITY = get(p, "timeout.visibility", 30000);
DEFAULT_TIMEOUT_POPUP = get(p, "timeout.popup", 30000);
DEFAULT_TIMEOUT_SUBMENU = get(p, "timeout.submenu", 100);
DEFAULT_DELAY_BETWEEN_EVENTS = get(p, "delay.between_events", 60);
DEFAULT_DELAY_DRAG = get(p, "delay.drag", 0);
DEFAULT_DELAY_DROP = get(p, "delay.drop", 0);
DEFAULT_DELAY_POSTING_EVENTS = get(p, "delay.posting_events", 100);
DEFAULT_LOOKUP_SCOPE = getGeneric(p, "lookup_scope", t -> ComponentLookupScope.valueOf(t), DEFAULT);
DEFAULT_TIMEOUT_IDLE = get(p, "timeout.idle", 10000);
DEFAULT_CLICK_ON_DISABLED = get(p, "allow_click_on_disabled_component", true);
DEFAULT_DRAG_BUTTON = getGeneric(p, "drag.button", t -> MouseButton.valueOf(t), LEFT_BUTTON);
PRESERVE_SCREENSHOTS = get(p, "preserve_screenshots", false);
}
private static <T> T getGeneric(Properties p, String suffix, Function<String, T> convert, T defaultValue) {
String key = "org.assertj.swing." + suffix;
String systemProperty = System.getProperty(key);
if (systemProperty != null) {
return convert.apply(systemProperty);
}
String property = p.getProperty(key);
if (property != null) {
return convert.apply(property);
}
return defaultValue;
}
private static int get(Properties p, String suffix, int defaultValue) {
return getGeneric(p, suffix, t -> Integer.parseInt(t), defaultValue);
}
private static boolean get(Properties p, String suffix, boolean defaultValue) {
return getGeneric(p, suffix, t -> Boolean.parseBoolean(t), defaultValue);
}
private ComponentLookupScope componentLookupScope;
private int timeoutToBeVisible;
private int timeoutToFindPopup;
private int timeoutToFindSubMenu;
private int delayBetweenEvents;
private int dragDelay;
private int dropDelay;
private int eventPostingDelay;
private int idleTimeout;
private boolean simpleWaitForIdle;
private boolean allowClickOnDisabled;
private MouseButton dragButton;
private java.awt.Robot robot;
public Settings() {
timeoutToBeVisible(DEFAULT_TIMEOUT_VISIBILITY);
timeoutToFindPopup(DEFAULT_TIMEOUT_POPUP);
timeoutToFindSubMenu(DEFAULT_TIMEOUT_SUBMENU);
delayBetweenEvents(DEFAULT_DELAY_BETWEEN_EVENTS);
dragDelay(DEFAULT_DELAY_DRAG);
dropDelay(DEFAULT_DELAY_DROP);
eventPostingDelay(DEFAULT_DELAY_POSTING_EVENTS);
componentLookupScope(DEFAULT_LOOKUP_SCOPE);
idleTimeout(DEFAULT_TIMEOUT_IDLE);
clickOnDisabledComponentsAllowed(DEFAULT_CLICK_ON_DISABLED);
dragButton(DEFAULT_DRAG_BUTTON);
}
public static boolean shouldPreserveScreenshots() {
return PRESERVE_SCREENSHOTS;
}
void attachTo(@Nonnull java.awt.Robot newRobot) {
robot = newRobot;
if (delayBetweenEvents < 0) {
delayBetweenEvents = robot.getAutoDelay();
} else {
updateRobotAutoDelay();
}
}
@VisibleForTesting
@Nonnull
java.awt.Robot robot() {
return robot;
}
/**
* @return a value representing the millisecond count in between generated events.
* @see #delayBetweenEvents(int) for default value and configuration key
*/
public int delayBetweenEvents() {
return delayBetweenEvents;
}
/**
* Updates the value representing the millisecond count in between generated events. Usually just set to 100-200 if
* you want to slow down the playback to simulate actual user input. The default is 60 milliseconds.
* <p>
* The property key for configuration file and system properties is
* <code>org.assertj.swing.delay.between_events</code>
* </p>
* <p>
* To change the speed of a GUI test, you need to change the values of both {@code delayBetweenEvents} and
* {@code eventPostingDelay}.
* </p>
*
* @param ms the millisecond count in between generated events. It should be between -1 and 60000.
* @see #eventPostingDelay(int)
*/
public void delayBetweenEvents(int ms) {
delayBetweenEvents = valueToUpdate(ms, -1, 60000);
if (robot != null) {
updateRobotAutoDelay();
}
}
private void updateRobotAutoDelay() {
robot.setAutoDelay(delayBetweenEvents);
}
/**
* @return the number of milliseconds to wait for an AWT or Swing {@code Component} to be visible.
* @see #timeoutToBeVisible(int) for default value and configuration key
*/
public int timeoutToBeVisible() {
return timeoutToBeVisible;
}
/**
* Updates the number of milliseconds to wait for an AWT or Swing {@code Component} to be visible. The default value
* is 30,000 milliseconds.
* <p>
* The property key for configuration file and system properties is <code>org.assertj.swing.timeout.visibility</code>
* </p>
*
* @param ms the time in milliseconds. It should be between 0 and 60000.
*/
public void timeoutToBeVisible(int ms) {
timeoutToBeVisible = valueToUpdate(ms, 0, 60000);
}
/**
* @return the number of milliseconds to wait before failing to find a pop-up menu that should appear.
* @see #timeoutToFindPopup(int) for default value and configuration key
*/
public int timeoutToFindPopup() {
return timeoutToFindPopup;
}
/**
* Updates the number of milliseconds to wait before failing to find a pop-up menu that should appear. The default
* value is 30000 milliseconds.
* <p>
* The property key for configuration file and system properties is <code>org.assertj.swing.timeout.popup</code>
* </p>
*
* @param ms the time in milliseconds. It should be between 0 and 60000.
*/
public void timeoutToFindPopup(int ms) {
timeoutToFindPopup = valueToUpdate(ms, 0, 60000);
}
/**
* @return the number of milliseconds to wait for a sub-menu to appear.
* @see #timeoutToFindSubMenu(int) for default value and configuration key
*/
public int timeoutToFindSubMenu() {
return timeoutToFindSubMenu;
}
/**
* Updates the number of milliseconds to wait for a sub-menu to appear. The default value is 100 milliseconds.
* <p>
* The property key for configuration file and system properties is <code>org.assertj.swing.timeout.submenu</code>
* </p>
*
* @param ms the time in milliseconds. It should be between 0 and 10000.
*/
public void timeoutToFindSubMenu(int ms) {
timeoutToFindSubMenu = valueToUpdate(ms, 0, 10000);
}
/**
* @return the number of milliseconds to wait between a pressing a mouse button and moving the mouse.
* @see #dragDelay(int) for default value and configuration key
*/
public int dragDelay() {
return dragDelay;
}
/**
* Updates the number of milliseconds to wait between a pressing a mouse button and moving the mouse. The default
* value for Mac OS X or the X11 Windowing system is 100 milliseconds. For other platforms, the default value is 0.
* <p>
* The property key for configuration file and system properties is <code>org.assertj.swing.delay.drag</code>
* </p>
*
* @param ms the time in milliseconds. For Mac OS X or the X11 Windowing system, the minimum value is 100. For other
* platforms the minimum value is 0. The maximum value for all platforms is 60000.
*/
public void dragDelay(int ms) {
int min = isX11() || isOSX() ? 100 : 0;
dragDelay = valueToUpdate(ms, min, 60000);
}
/**
* @return the number of milliseconds before checking for idle.
* @see #eventPostingDelay(int) for default value and configuration key
*/
public int eventPostingDelay() {
return eventPostingDelay;
}
/**
* <p>
* Updates the number of milliseconds before checking for idle. This allows the system a little time to put a native
* event onto the AWT event queue. The default value is 100 milliseconds.
* </p>
* <p>
* The property key for configuration file and system properties is
* <code>org.assertj.swing.delay.posting_events</code>
* </p>
*
* <p>
* To change the speed of a GUI test, you need to change the values of both {@code delayBetweenEvents} and
* {@code eventPostingDelay}.
* </p>
*
* @param ms the time in milliseconds. It should be between 0 and 1000.
* @see #delayBetweenEvents(int)
*/
public void eventPostingDelay(int ms) {
eventPostingDelay = valueToUpdate(ms, 0, 1000);
}
/**
* @return the number of milliseconds between the final mouse movement and mouse release to ensure drop ends.
* @see #dropDelay(int) for default value and configuration key
*/
public int dropDelay() {
return dropDelay;
}
/**
* Updates the number of milliseconds between the final mouse movement and mouse release to ensure drop ends. The
* default value for Windows is 200. For other platforms, the default value is 0.
* <p>
* The property key for configuration file and system properties is <code>org.assertj.swing.delay.drop</code>
* </p>
*
* @param ms the time in milliseconds. For Windows, the minimum value is 200. For other platforms, the minimum value
* is 0. The maximum value for all platforms is 60000.
*/
public void dropDelay(int ms) {
int min = isWindows() ? 200 : 0;
dropDelay = valueToUpdate(ms, min, 60000);
}
/**
* @return the scope of AWT or Swing {@code Component} lookups. This setting only affects the classes in the package
* {@code org.assertj.swing.fixture}.
* @see #componentLookupScope(ComponentLookupScope) for default value and configuration key
*/
public @Nonnull ComponentLookupScope componentLookupScope() {
return componentLookupScope;
}
/**
* Updates the scope of AWT or Swing {@code Component} lookups. This setting only affects the classes in the package
* {@code org.assertj.swing.fixture}. The default value is {@link ComponentLookupScope#DEFAULT}.
* <p>
* The property key for configuration file and system properties is <code>org.assertj.swing.lookup_scope</code>
* </p>
*
* @param scope the new value for the scope.
*/
public void componentLookupScope(@Nonnull ComponentLookupScope scope) {
componentLookupScope = scope;
}
/**
* @return the time (in milliseconds) to wait for an idle AWT event queue.
* @see #idleTimeout(int) for default value and configuration key
*/
public int idleTimeout() {
return idleTimeout;
}
/**
* Updates the time (in milliseconds) to wait for an idle AWT event queue.
* <p>
* The property key for configuration file and system properties is <code>org.assertj.swing.timeout.idle</code>
* </p>
*
* @param ms the new time. The value should be equal to or greater than zero.
*/
public void idleTimeout(int ms) {
idleTimeout = valueToUpdate(ms, 0, Integer.MAX_VALUE);
}
private int valueToUpdate(int value, int min, int max) {
return max(min, min(max, value));
}
/**
* @return the simple waitForIdle implementation is on or off
*/
public boolean simpleWaitForIdle() {
return simpleWaitForIdle;
}
/**
* turns on or off the simple waitForIdle implementation
*
* @param simpleWaitForIdle <code>true</code> if simple implementation should be used
*/
public void simpleWaitForIdle(boolean simpleWaitForIdle) {
this.simpleWaitForIdle = simpleWaitForIdle;
}
/**
* @return <code>true</code> if clicking on disabled components should be possible.
* @see #clickOnDisabledComponentsAllowed(boolean) for default value and configuration key
*/
public boolean clickOnDisabledComponentsAllowed() {
return allowClickOnDisabled;
}
/**
* Default is <code>true</code>.
* <p>
* The property key for configuration file and system properties is
* <code>org.assertj.swing.allow_click_on_disabled_component</code>.
* </p>
*
* @param allow new value for {@link #clickOnDisabledComponentsAllowed(boolean)}.
*/
public void clickOnDisabledComponentsAllowed(boolean allow) {
allowClickOnDisabled = allow;
}
/**
* @return the {@link MouseButton} to use for drag operations.
* @see #dragButton(MouseButton) for default value and configuration key
*/
public MouseButton dragButton() {
return dragButton;
}
/**
* Changes the {@link MouseButton} to use for drag operations. Default is {@link MouseButton#LEFT_BUTTON}.
* <p>
* The property key for configuration file and system properties is <code>org.assertj.swing.drag.button</code>
* </p>
*
* @param button the {@link MouseButton} to use from now on
*/
public void dragButton(MouseButton button) {
dragButton = button;
}
}