diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml
index 04dbca9..4df9239 100644
--- a/.github/workflows/codeql.yml
+++ b/.github/workflows/codeql.yml
@@ -30,15 +30,40 @@ jobs:
java-version: '21'
distribution: 'temurin'
+ - name: Cache Maven dependencies
+ uses: actions/cache@v4
+ with:
+ path: ~/.m2/repository
+ key: ${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }}
+ restore-keys: |
+ ${{ runner.os }}-maven-
+
+ - name: Configure Maven Central mirror
+ run: |
+ mkdir -p ~/.m2
+ cat > ~/.m2/settings.xml <<'EOF'
+
+
+
+ central-mirror
+ Central Mirror
+ https://repo1.maven.org/maven2
+ central
+
+
+
+ EOF
+
- name: Initialize CodeQL
- uses: github/codeql-action/init@v3
+ uses: github/codeql-action/init@v4
with:
languages: ${{ matrix.language }}
+ build-mode: manual
- - name: Build with Maven
- run: mvn clean compile
+ - name: Build and test with Maven
+ run: mvn -B clean compile -Dmaven.wagon.http.retryHandler.count=5
- name: Perform CodeQL Analysis
- uses: github/codeql-action/analyze@v3
+ uses: github/codeql-action/analyze@v4
with:
category: "/language:${{matrix.language}}"
\ No newline at end of file
diff --git a/.github/workflows/maven.yml b/.github/workflows/maven.yml
index 92823cb..065cd47 100644
--- a/.github/workflows/maven.yml
+++ b/.github/workflows/maven.yml
@@ -22,7 +22,7 @@ jobs:
- name: Build project and run UI test
uses: coactions/setup-xvfb@v1
with:
- run: mvn clean verify -Pci
+ run: mvn -B clean verify -Pci
- name: Upload coverage reports to Codecov
uses: codecov/codecov-action@v5
diff --git a/.github/workflows/maven_central_publish.yml b/.github/workflows/maven_central_publish.yml
index dcbc3d0..64b6e28 100644
--- a/.github/workflows/maven_central_publish.yml
+++ b/.github/workflows/maven_central_publish.yml
@@ -39,6 +39,6 @@ jobs:
}]
- name: Release package
- run: mvn clean deploy -Prelease,maven-central -DskipTests
+ run: mvn -B clean deploy -Prelease,maven-central -DskipTests
env:
GPG_SECRET_KEY_PASSPHRASE: ${{ secrets.GPG_SECRET_KEY_PASSPHRASE }}
diff --git a/.github/workflows/maven_github_publish.yml b/.github/workflows/maven_github_publish.yml
index dbd87ff..f16ee3c 100644
--- a/.github/workflows/maven_github_publish.yml
+++ b/.github/workflows/maven_github_publish.yml
@@ -38,7 +38,7 @@ jobs:
}]
- name: Publish package
- run: mvn --batch-mode deploy -Prelease -DskipTests
+ run: mvn -B deploy -Prelease -DskipTests
env:
GITHUB_TOKEN: ${{ secrets.GH_TOKEN }}
GPG_SECRET_KEY_PASSPHRASE: ${{ secrets.GPG_SECRET_KEY_PASSPHRASE }}
\ No newline at end of file
diff --git a/README.md b/README.md
index f1874c0..c577f41 100644
--- a/README.md
+++ b/README.md
@@ -1,9 +1,9 @@
# WindowTester Pro
-
-[](https://mvnrepository.com/search?q=windowtester)
-
-
+
+[](https://mvnrepository.com/search?q=windowtester)
+
+
[](LICENSE.md)
[](https://github.com/r4fterman/windowtester/actions/workflows/maven.yml)
[](https://codecov.io/gh/r4fterman/windowtester)
@@ -34,7 +34,7 @@ can be run within your IDE, or they can be automated to run using [Maven](http:/
## Requirements
- JDK 21
-- JUnit 5
+- JUnit 6
Supported platforms:
diff --git a/abbot/src/main/java/abbot/editor/EditorConstants.java b/abbot/src/main/java/abbot/editor/EditorConstants.java
index 640af39..51a5e41 100644
--- a/abbot/src/main/java/abbot/editor/EditorConstants.java
+++ b/abbot/src/main/java/abbot/editor/EditorConstants.java
@@ -8,61 +8,6 @@
*/
public interface EditorConstants {
- String MENU_FILE = "menus.file";
- String MENU_EDIT = "menus.edit";
- String MENU_TEST = "menus.test";
- String MENU_INSERT = "menus.insert";
- String MENU_CAPTURE = "menus.capture";
- String MENU_HELP = "menus.help";
-
String ACTION_PREFIX = "actions.";
- String ACTION_EDITOR_ABOUT = "editor-about";
- String ACTION_EDITOR_EMAIL = "editor-email";
- String ACTION_EDITOR_BUGREPORT = "editor-submit-bug";
- String ACTION_EDITOR_WEBSITE = "editor-website";
- String ACTION_EDITOR_USERGUIDE = "editor-userguide";
- String ACTION_EDITOR_QUIT = "editor-quit";
- String ACTION_SCRIPT_OPEN = "script-open";
- String ACTION_SCRIPT_NEW = "script-new";
- String ACTION_SCRIPT_DUPLICATE = "script-duplicate";
- String ACTION_SCRIPT_SAVE = "script-save";
- String ACTION_SCRIPT_SAVE_AS = "script-save-as";
- String ACTION_SCRIPT_RENAME = "script-rename";
- String ACTION_SCRIPT_CLOSE = "script-close";
- String ACTION_SCRIPT_DELETE = "script-delete";
- String ACTION_SCRIPT_CLEAR = "script-clear";
- String ACTION_STEP_CUT = "step-cut";
- String ACTION_STEP_MOVE_UP = "step-move-up";
- String ACTION_STEP_MOVE_DOWN = "step-move-down";
- String ACTION_STEP_GROUP = "step-group";
- String ACTION_SELECT_TESTSUITE = "select-testsuite";
- String ACTION_EXPORT_HIERARCHY = "export-hierarchy";
- String ACTION_RUN = "run";
- String ACTION_RUN_TO = "run-to";
- String ACTION_RUN_SELECTED = "run-selected";
- String ACTION_RUN_LAUNCH = "run-launch";
- String ACTION_RUN_TERMINATE = "run-terminate";
- String ACTION_GET_VMARGS = "run-get-vmargs";
String ACTION_TOGGLE_FORKED = "toggle-forked";
- String ACTION_TOGGLE_SLOW_PLAYBACK = "toggle-slow-playback";
- String ACTION_TOGGLE_AWT_MODE = "toggle-awt-mode";
- String ACTION_TOGGLE_STOP_ON_FAILURE = "toggle-stop-on-failure";
- String ACTION_TOGGLE_STOP_ON_ERROR = "toggle-stop-on-error";
- String ACTION_INSERT_LAUNCH = "insert-launch";
- String ACTION_INSERT_APPLET = "insert-applet";
- String ACTION_INSERT_TERMINATE = "insert-terminate";
- String ACTION_INSERT_CALL = "insert-call";
- String ACTION_INSERT_SAMPLE = "insert-sample";
- String ACTION_INSERT_SEQUENCE = "insert-sequence";
- String ACTION_INSERT_SCRIPT = "insert-script";
- String ACTION_INSERT_FIXTURE = "insert-fixture";
- String ACTION_INSERT_COMMENT = "insert-comment";
- String ACTION_INSERT_EXPRESSION = "insert-expression";
- String ACTION_INSERT_ANNOTATION = "insert-annotation";
- String ACTION_DYNAMIC = "dynamic-actions";
- String ACTION_CAPTURE_IMAGE = "capture-image";
- String ACTION_CAPTURE_COMPONENT = "capture-component";
- String ACTION_SELECT_COMPONENT = "select-component";
- String ACTION_CAPTURE = "capture";
- String ACTION_CAPTURE_ALL = "capture-all";
}
diff --git a/abbot/src/main/java/abbot/editor/editors/AppletviewerEditor.java b/abbot/src/main/java/abbot/editor/editors/AppletviewerEditor.java
deleted file mode 100644
index 8b32e52..0000000
--- a/abbot/src/main/java/abbot/editor/editors/AppletviewerEditor.java
+++ /dev/null
@@ -1,134 +0,0 @@
-package abbot.editor.editors;
-
-import abbot.editor.widgets.ArrayEditor;
-import abbot.i18n.Strings;
-import abbot.script.Appletviewer;
-import java.awt.Component;
-import java.awt.event.ActionEvent;
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.Map;
-import javax.swing.Box;
-import javax.swing.BoxLayout;
-import javax.swing.JPanel;
-import javax.swing.JTextField;
-
-/**
- * Provide convenient editing of an applet launch step.
- */
-public class AppletviewerEditor extends StepEditor {
-
- public static final String HELP_DESC = Strings.get("editor.applet.desc");
-
- private final Appletviewer applet;
-
- private final JTextField code;
- private final ArrayEditor params;
- private final JTextField codebase;
- private final JTextField archive;
- private final JTextField width;
- private final JTextField height;
-
- public AppletviewerEditor(Appletviewer applet) {
- super(applet);
- this.applet = applet;
-
- code = addTextField(Strings.get("editor.applet.code"), applet.getCode());
- width = addTextField(Strings.get("editor.applet.width"), applet.getWidth());
- height = addTextField(Strings.get("editor.applet.height"), applet.getHeight());
- // For some reason, if we *don't* futz with the layout of
- // width/height, the pane never reconfigures properly for the array
- // editor.
- Component c;
- ArrayList list = new ArrayList();
- while ((c = getComponent(getComponentCount() - 1)) != code) {
- remove(c);
- list.add(c);
- }
- JPanel p = new JPanel();
- // p.setBorder(new TitledBorder(Strings.get("editor.applet.size")));
- p.setLayout(new BoxLayout(p, BoxLayout.X_AXIS));
- for (int i = list.size() - 1; i >= 0; i--) {
- p.add((Component) list.get(i));
- if (i != 0) {
- p.add(Box.createHorizontalStrut(MARGIN));
- }
- }
- add(p);
-
- params = addArrayEditor(Strings.get("editor.applet.params"), applet.getParamsAsArray());
-
- codebase = addTextField(Strings.get("editor.applet.codebase"), applet.getCodebase());
- archive = addTextField(Strings.get("editor.applet.archive"), applet.getArchive());
- }
-
- public void actionPerformed(ActionEvent ev) {
- Object src = ev.getSource();
- if (src == code) {
- applet.setCode(code.getText());
- fireStepChanged();
- } else if (src == params) {
- Object[] values = params.getValues();
- Map map = new HashMap();
- for (int i = 0; i < values.length; i++) {
- String v = (String) values[i];
- int eq = v.indexOf("=");
- if (eq != -1) {
- String key = v.substring(0, eq);
- String value = v.substring(eq + 1);
- map.put(key, value);
- }
- }
- applet.setParams(map);
- fireStepChanged();
- } else if (src == codebase) {
- String value = codebase.getText();
- if ("".equals(value)) {
- value = null;
- }
- applet.setCodebase(value);
- fireStepChanged();
- } else if (src == archive) {
- String value = archive.getText();
- if ("".equals(value)) {
- value = null;
- }
- applet.setArchive(value);
- fireStepChanged();
- } else if (src == width) {
- String value = width.getText();
- if ("".equals(value)) {
- value = null;
- }
- try {
- Integer.parseInt(value);
- applet.setWidth(value);
- width.setForeground(DEFAULT_FOREGROUND);
- fireStepChanged();
- } catch (NumberFormatException e) {
- width.setForeground(ERROR_FOREGROUND);
- }
- } else if (src == height) {
- String value = height.getText();
- if ("".equals(value)) {
- value = null;
- }
- try {
- Integer.parseInt(value);
- applet.setHeight(value);
- width.setForeground(DEFAULT_FOREGROUND);
- fireStepChanged();
- } catch (NumberFormatException e) {
- width.setForeground(ERROR_FOREGROUND);
- }
- } else {
- super.actionPerformed(ev);
- }
-
- // Remove the default placeholder description
- if (HELP_DESC.equals(applet.getDescription())) {
- applet.setDescription(null);
- fireStepChanged();
- }
- }
-}
diff --git a/abbot/src/main/java/abbot/finder/AWTHierarchy.java b/abbot/src/main/java/abbot/finder/AWTHierarchy.java
index b09d215..06cf130 100644
--- a/abbot/src/main/java/abbot/finder/AWTHierarchy.java
+++ b/abbot/src/main/java/abbot/finder/AWTHierarchy.java
@@ -42,24 +42,19 @@ public boolean contains(Component component) {
@Override
public void dispose(Window window) {
- if (AWT.isAppletViewerFrame(window)) {
- // Don't dispose, it must quit on its own
- return;
- }
-
Log.debug("Dispose " + window);
Arrays.stream(window.getOwnedWindows()).forEach(this::dispose);
if (AWT.isSharedInvisibleFrame(window)) {
- // Don't dispose, or any child windows which may be currently
- // ignored (but not hidden) will be hidden and disposed.
+ // Don't dispose, or any child windows that may be currently
+ // ignored (but not hidden) will be hidden and disposed of.
return;
}
// Ensure the disposal is done on the swing thread so we can catch any
// exceptions. If Window.dispose is called from a non-Swing thread,
- // it will invoke the dispose action on the Swing thread but in that
+ // it will invoke the dispose action on the Swing thread, but in that
// case we have no control over exceptions.
Runnable action =
() -> {
@@ -114,15 +109,14 @@ public Collection getComponents(Component component) {
new ArrayList<>(Arrays.asList(((Container) component).getComponents()));
// Add other components which are not explicitly children, but
// that are conceptually descendents
- if (component instanceof JMenu menu) {
- list.add(menu.getPopupMenu());
- } else if (component instanceof Window window) {
- list.addAll(Arrays.asList(window.getOwnedWindows()));
- } else if (component instanceof JDesktopPane) {
- // Add iconified frames, which are otherwise unreachable.
- // For consistency, they are still considerered children of
- // the desktop pane.
- list.addAll(findInternalFramesFromIcons((Container) component));
+ switch (component) {
+ case JMenu menu -> list.add(menu.getPopupMenu());
+ case Window window -> list.addAll(Arrays.asList(window.getOwnedWindows()));
+ case JDesktopPane jDesktopPane ->
+ // Add iconified frames, which are otherwise unreachable.
+ // For consistency, they are still considered children of the desktop pane.
+ list.addAll(findInternalFramesFromIcons((Container) component));
+ default -> {}
}
return list;
}
diff --git a/abbot/src/main/java/abbot/finder/TestHierarchy.java b/abbot/src/main/java/abbot/finder/TestHierarchy.java
index 51c6827..e7c734c 100644
--- a/abbot/src/main/java/abbot/finder/TestHierarchy.java
+++ b/abbot/src/main/java/abbot/finder/TestHierarchy.java
@@ -22,9 +22,6 @@ public class TestHierarchy extends AWTHierarchy {
// Map of components to ignore
private final Map filtered = new WeakHashMap<>();
- private static final boolean TRACK_APPLET_CONSOLE =
- Boolean.getBoolean("abbot.applet.track_console");
-
/**
* Create a new TestHierarchy which does not contain any UI Components which might already exist.
*/
@@ -88,10 +85,6 @@ public boolean isFiltered(Component component) {
return false;
}
- if ("sun.plugin.ConsoleWindow".equals(component.getClass().getName())) {
- return !TRACK_APPLET_CONSOLE;
- }
-
return filtered.containsKey(component)
|| ((component instanceof Window) && isFiltered(component.getParent()))
|| (!(component instanceof Window) && isWindowFiltered(component));
diff --git a/abbot/src/main/java/abbot/script/AppClassLoader.java b/abbot/src/main/java/abbot/script/AppClassLoader.java
index 732ca5a..76dbc7e 100644
--- a/abbot/src/main/java/abbot/script/AppClassLoader.java
+++ b/abbot/src/main/java/abbot/script/AppClassLoader.java
@@ -15,8 +15,7 @@
* parent class loader gets a chance to look for the class (instead of the default behavior, which always delegates to
* the parent class loader first). This behavior enables the class to be reloaded simply by using a new instance of
* this class loader with each launch of the app.
This class mimics the behavior of sun.misc.Launcher$AppClassLoader
- * as much as possible.
Bootstrap classes are always delegated to the bootstrap loader, with the exception of the
- * sun.applet package, which should never be delegated, since it does not work properly unless it is reloaded.
The
+ * as much as possible.
Bootstrap classes are always delegated to the bootstrap loader.
The
* parent of this class loader will be the normal, default AppClassLoader (specifically, the class loader which loaded
* this class will be used).
*/
@@ -37,11 +36,6 @@ public class AppClassLoader extends NonDelegatingClassLoader {
*/
private final NonDelegatingClassLoader extensionsLoader;
- /**
- * Whether the framework itself is being tested.
- */
- private final boolean frameworkIsUnderTest = false;
-
/**
* Old class loader context for the thread where this loader was installed.
*/
@@ -50,7 +44,7 @@ public class AppClassLoader extends NonDelegatingClassLoader {
private Thread installedThread = null;
private String oldClassPath = null;
- private class InstallationLock {}
+ private static class InstallationLock {}
private final InstallationLock lock = new InstallationLock();
@@ -98,15 +92,9 @@ public boolean isEventDispatchThread() {
// FIXME we should only need the delegate flag if stuff in the classpath
// is also found on the system classpath, e.g. the framework itself
- // Maybe just set it internally in case the classpaths overlap?
+ // Maybe just set it internally in case the class paths overlap?
protected boolean shouldDelegate(String name) {
- return bootstrapLoader.shouldDelegate(name)
- && !isExtension(name)
- && !(frameworkIsUnderTest && isFrameworkClass(name));
- }
-
- private boolean isFrameworkClass(String name) {
- return name.startsWith("abbot.") || name.startsWith("junit.extensions.abbot.");
+ return bootstrapLoader.shouldDelegate(name) && !isExtension(name);
}
private boolean isExtension(String name) {
@@ -123,14 +111,6 @@ private boolean isExtension(String name) {
* @throws ClassNotFoundException if the class could not be found
*/
public Class findClass(String name) throws ClassNotFoundException {
- if (isBootstrapClassRequiringReload(name)) {
- try {
- return bootstrapLoader.findClass(name);
- } catch (ClassNotFoundException cnf) {
- Log.warn(cnf);
- }
- }
-
// Look for extensions first in the framework class path (with a
// special loader), then in the app class path.
// Extensions *must* have the same class loader as the corresponding
@@ -179,8 +159,7 @@ public void install() {
eventQueue = new AppEventQueue();
eventQueue.install();
- Thread current = Thread.currentThread();
- installedThread = current;
+ installedThread = Thread.currentThread();
oldClassLoader = installedThread.getContextClassLoader();
installedThread.setContextClassLoader(this);
}
@@ -248,6 +227,7 @@ public void uninstall() {
pop();
thread = null;
} catch (EmptyStackException ese) {
+ // ignore
}
Log.debug("AppEventQueue uninstalled");
}
@@ -258,29 +238,7 @@ public String toString() {
}
/**
- * List of bootstrap classes we most definitely want to be loaded by this class loader, rather than any parent, or
- * the bootstrap loader.
- */
- private final String[] mustReloadPrefixes = {
- "sun.applet.", // need the whole package, not just AppletViewer/Main
- };
-
- /**
- * Does the given class absolutely need to be preloaded?
- */
- private boolean isBootstrapClassRequiringReload(String name) {
- for (int i = 0; i < mustReloadPrefixes.length; i++) {
- if (name.startsWith(mustReloadPrefixes[i])) {
- return true;
- }
- }
- return false;
- }
-
- /**
- * Returns the path to the primary JRE classes, not including any extensions. This is primarily needed for loading
- * sun.applet.AppletViewer/Main, since most other classes in the bootstrap path should only be loaded by the
- * bootstrap loader.
+ * Returns the path to the primary JRE classes, not including any extensions.
*/
private static String getBootstrapPath() {
return System.getProperty("sun.boot.class.path");
@@ -289,17 +247,13 @@ private static String getBootstrapPath() {
/**
* Provide access to bootstrap classes that we need to be able to reload.
*/
- private class BootstrapClassLoader extends NonDelegatingClassLoader {
+ private static class BootstrapClassLoader extends NonDelegatingClassLoader {
public BootstrapClassLoader() {
super(getBootstrapPath(), null);
}
protected boolean shouldDelegate(String name) {
- // Exclude all bootstrap classes, except for those we know we
- // *must* be reloaded on each run in order to have function
- // properly (e.g. applet)
- return !isBootstrapClassRequiringReload(name)
- && !"abbot.script.AppletSecurityManager".equals(name);
+ return true;
}
}
diff --git a/abbot/src/main/java/abbot/script/Appletviewer.java b/abbot/src/main/java/abbot/script/Appletviewer.java
deleted file mode 100644
index 93fcf04..0000000
--- a/abbot/src/main/java/abbot/script/Appletviewer.java
+++ /dev/null
@@ -1,396 +0,0 @@
-package abbot.script;
-
-import abbot.Log;
-import abbot.NoExitSecurityManager;
-import abbot.finder.BasicFinder;
-import abbot.finder.ComponentFinder;
-import abbot.finder.ComponentSearchException;
-import abbot.finder.Matcher;
-import abbot.finder.matchers.ClassMatcher;
-import abbot.i18n.Strings;
-import abbot.tester.ComponentTester;
-import java.applet.Applet;
-import java.awt.Component;
-import java.awt.Frame;
-import java.awt.event.WindowAdapter;
-import java.awt.event.WindowEvent;
-import java.io.File;
-import java.io.FileOutputStream;
-import java.lang.reflect.Constructor;
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.Iterator;
-import java.util.Map;
-import java.util.TreeMap;
-import javax.swing.SwingUtilities;
-
-/**
- * Provides applet launch capability. Usage:
- *
- * The attributes are equivalent to those provided in the HTML
- * applet tag. The params attribute is a
- * comma-separated list of name=value pairs, which will be passed to the applet within the
- * applet tag as param elements.
- *
- * WARNING: Closing the appletviewer window from the window manager
- * close button will result applet-spawned event dispatch threads being left running. To avoid this situation, always
- * use the appletviewer Quit menu item or use the {@link #terminate()} method of this class.
- */
-public class Appletviewer extends Launch {
-
- private String code;
- private Map params;
- private String codebase;
- private String archive;
- private String width;
- private String height;
- private ClassLoader appletClassLoader;
- private Frame appletViewerFrame;
- private transient SecurityManager oldSM;
- private transient boolean terminating;
-
- private static final String DEFAULT_WIDTH = "100";
- private static final String DEFAULT_HEIGHT = "100";
- private static final String CLASS_NAME = "sun.applet.Main";
- private static final String METHOD_NAME = "main";
- private static final int LAUNCH_TIMEOUT = 30000;
-
- private static final String USAGE =
- "";
-
- private static void quitApplet(final Frame frame) {
- new ComponentTester().selectAWTMenuItem(frame, "Applet|Quit");
- }
-
- private static Map patchAttributes(Map map) {
- map.put(TAG_CLASS, CLASS_NAME);
- map.put(TAG_METHOD, METHOD_NAME);
- return map;
- }
-
- public Appletviewer(Resolver resolver, Map attributes) {
- super(resolver, patchAttributes(attributes));
- code = (String) attributes.get(TAG_CODE);
- params = parseParams((String) attributes.get(TAG_PARAMS));
- codebase = (String) attributes.get(TAG_CODEBASE);
- archive = (String) attributes.get(TAG_ARCHIVE);
- width = (String) attributes.get(TAG_WIDTH);
- height = (String) attributes.get(TAG_HEIGHT);
- }
-
- public Appletviewer(
- Resolver resolver,
- String description,
- String code,
- Map params,
- String codebase,
- String archive,
- String classpath) {
- super(resolver, description, CLASS_NAME, METHOD_NAME, null, classpath, false);
- this.code = code;
- this.params = params;
- this.codebase = codebase;
- this.archive = archive;
- }
-
- /**
- * Run this step. Causes the appropriate HTML file to be written for use by appletviewer and its path used as the
- * sole argument.
- */
- @Override
- public void runStep() throws Throwable {
- File dir = new File(System.getProperty("user.dir"));
- File htmlFile = File.createTempFile("abbot-applet", ".html", dir);
- htmlFile.deleteOnExit();
- try {
- FileOutputStream os = new FileOutputStream(htmlFile);
- os.write(generateHTML().getBytes());
- os.close();
- setArguments(new String[] {"[" + htmlFile.getName() + "]"});
- super.runStep();
- // Wait for the applet to become visible
- long start = System.currentTimeMillis();
- ComponentFinder finder = new BasicFinder(getResolver().getHierarchy());
- Matcher matcher = new ClassMatcher(Applet.class, true);
- while (true) {
- try {
- Component c = finder.find(matcher);
- appletViewerFrame = (Frame) SwingUtilities.getWindowAncestor(c);
- addCloseListener(appletViewerFrame);
- appletClassLoader = c.getClass().getClassLoader();
- break;
- } catch (ComponentSearchException e) {
- }
- if (System.currentTimeMillis() - start > LAUNCH_TIMEOUT) {
- throw new RuntimeException(Strings.get("step.appletviewer.launch_timed_out"));
- }
- Thread.sleep(200);
- }
- } finally {
- htmlFile.delete();
- }
- }
-
- private void addCloseListener(final Frame frame) {
- // Workaround for lockup when applet is closed via WM close button and
- // then relaunched. Can't figure out how to kill those extant threads.
- // This avoids the lockup, but leaves threads around.
- frame.addWindowListener(
- new WindowAdapter() {
- public void windowClosing(WindowEvent e) {
- // Invoke the frame's quit menu item
- quitApplet(frame);
- }
- });
- }
-
- protected String generateHTML() {
- StringBuffer html = new StringBuffer();
- html.append("");
- return html.toString();
- }
-
- @Override
- public void setTargetClassName(String name) {
- if (CLASS_NAME.equals(name)) {
- super.setTargetClassName(name);
- } else {
- throw new IllegalArgumentException(Strings.get("step.call.immutable_class"));
- }
- }
-
- @Override
- public void setMethodName(String name) {
- if (METHOD_NAME.equals(name)) {
- super.setMethodName(name);
- } else {
- throw new IllegalArgumentException(Strings.get("step.call.immutable_method"));
- }
- }
-
- public void setCode(String code) {
- this.code = code;
- }
-
- public String getCode() {
- return code;
- }
-
- public void setCodebase(String codebase) {
- this.codebase = codebase;
- }
-
- public String getCodebase() {
- return codebase;
- }
-
- public void setArchive(String archive) {
- this.archive = archive;
- }
-
- public String getArchive() {
- return archive;
- }
-
- public String getWidth() {
- return width != null ? width : DEFAULT_WIDTH;
- }
-
- public void setWidth(String width) {
- this.width = width;
- try {
- Integer.parseInt(width);
- } catch (NumberFormatException e) {
- this.width = null;
- }
- }
-
- public String getHeight() {
- return height != null ? height : DEFAULT_HEIGHT;
- }
-
- public void setHeight(String height) {
- this.height = height;
- try {
- Integer.parseInt(height);
- } catch (NumberFormatException e) {
- this.height = null;
- }
- }
-
- public Map getParams() {
- return params;
- }
-
- public void setParams(Map params) {
- this.params = params;
- }
-
- protected Map parseParams(String attribute) {
- Map map = new HashMap();
- if (attribute != null) {
- String[] list = ArgumentParser.parseArgumentList(attribute);
- for (int i = 0; i < list.length; i++) {
- String p = list[i];
- int eq = p.indexOf("=");
- map.put(p.substring(0, eq), p.substring(eq + 1));
- }
- }
- return map;
- }
-
- public String[] getParamsAsArray() {
- ArrayList list = new ArrayList();
- // Ensure we always get a consistent order
- Iterator iter = new TreeMap(params).keySet().iterator();
- while (iter.hasNext()) {
- String key = (String) iter.next();
- String value = (String) params.get(key);
- list.add(key + "=" + value);
- }
- return (String[]) list.toArray(new String[list.size()]);
- }
-
- public String getParamsAttribute() {
- return ArgumentParser.encodeArguments(getParamsAsArray());
- }
-
- @Override
- public Map getAttributes() {
- Map map = super.getAttributes();
- map.put(TAG_CODE, getCode());
- if (params.size() > 0) {
- map.put(TAG_PARAMS, getParamsAttribute());
- }
- if (getCodebase() != null) {
- map.put(TAG_CODEBASE, getCodebase());
- }
- if (getArchive() != null) {
- map.put(TAG_ARCHIVE, getArchive());
- }
- if (!DEFAULT_WIDTH.equals(getWidth())) {
- map.put(TAG_WIDTH, getWidth());
- }
- if (!DEFAULT_HEIGHT.equals(getHeight())) {
- map.put(TAG_HEIGHT, getHeight());
- }
-
- // don't need to store these
- map.remove(TAG_CLASS);
- map.remove(TAG_METHOD);
- map.remove(TAG_THREADED);
- map.remove(TAG_ARGS);
-
- return map;
- }
-
- @Override
- public String getDefaultDescription() {
- String desc = Strings.get("step.appletviewer", new Object[] {getCode()});
- return desc;
- }
-
- @Override
- public String getUsage() {
- return USAGE;
- }
-
- @Override
- public String getXMLTag() {
- return TAG_APPLETVIEWER;
- }
-
- public ClassLoader getContextClassLoader() {
- // Maybe use codebase/archive to have an alternative classpath?
- ClassLoader cl = super.getContextClassLoader();
- return appletClassLoader != null ? appletClassLoader : cl;
- }
-
- protected void install() {
- super.install();
-
- // Appletviewer expects the security manager to be an instance of
- // AppletSecurity. Use the custom class loader, *not* the one for the
- // applet.
- installAppletSecurityManager(super.getContextClassLoader());
- }
-
- /**
- * Install a security manager if there is none; this is a workaround to prevent sun's applet viewer's security
- * manager from preventing any classes from being loaded.
- */
- private void installAppletSecurityManager(ClassLoader cl) {
- oldSM = System.getSecurityManager();
- if (oldSM == null || (oldSM instanceof NoExitSecurityManager)) {
- Log.debug("install security manager");
- // NOTE: the security manager *must* be loaded with the same class
- // loader as the appletviewer.
- try {
- Class cls = Class.forName("abbot.script.AppletSecurityManager", true, cl);
- Constructor ctor = cls.getConstructor(SecurityManager.class);
- SecurityManager sm = (SecurityManager) ctor.newInstance(new Object[] {oldSM});
- System.setSecurityManager(sm);
- } catch (Exception exc) {
- Log.warn(exc);
- }
- } else {
- Log.debug("old sm=" + oldSM);
- }
- }
-
- /**
- * To properly terminate, we need to invoke AppletViewer's appletQuit() method (protected, but accessible).
- */
- public void terminate() {
- synchronized (this) {
- // Avoid recursion, since we'll return here when the applet
- // invokes System.exit.
- if (terminating) {
- return;
- }
-
- terminating = true;
- }
- Frame frame = appletViewerFrame;
- appletViewerFrame = null;
- try {
- // FIXME: figure out why closing the appletviewer window causes an
- // EDT hangup. (maybe need to post this to the applet's event
- // queue?)
- // Also figure out who's creating all the extra EDTs and dispose of
- // them properly, but it's probably not worth the effort.
- if (frame != null) {
- quitApplet(frame);
- }
- // Now clean up normally
- super.terminate();
- if (oldSM != null) {
- Log.debug("restore sm=" + oldSM);
- System.setSecurityManager(oldSM);
- }
- appletClassLoader = null;
- } finally {
- synchronized (this) {
- terminating = false;
- }
- }
- }
-}
diff --git a/abbot/src/main/java/abbot/script/ComponentReference.java b/abbot/src/main/java/abbot/script/ComponentReference.java
index 4e5657e..a1efff7 100644
--- a/abbot/src/main/java/abbot/script/ComponentReference.java
+++ b/abbot/src/main/java/abbot/script/ComponentReference.java
@@ -10,7 +10,6 @@
import abbot.tester.Robot;
import abbot.util.AWT;
import com.windowtester.runtime.util.StringComparator;
-import java.applet.Applet;
import java.awt.Component;
import java.awt.Container;
import java.awt.Dialog;
@@ -52,12 +51,12 @@
*
* The component reference ID may be used in scripts in place of the actual component to which this
* reference refers. The conversion will be made when the actual component is needed. The ID is
- * arbitrary, and may be changed in scripts to any unique string (just remember to change all
- * references to the ID in other places in the script as well).
A number of optional tags are
- * supported to provide an increasingly precise specification of the desired component:
+ * arbitrary and may be changed in scripts to any unique string (remember to change all references
+ * to the ID in other places in the script as well).
A number of optional tags are supported to
+ * provide an increasingly precise specification of the desired component:
*
*
weighted refers to the name of an available
- * attribute which should be weighted more heavily in comparisons, e.g. the
+ * attribute which should be weighted more heavily in comparisons, e.g., the
* label on a JButton.
*
parent the reference id of this component's
* parent.
@@ -69,7 +68,7 @@
*
Resolver.addComponent(Component) - creates a reference if one doesn't
* already exist and modifiers the Resolver to include it.
*
getReference(Resolver, Component, Map) - create a reference only if a
- * matching one does not exist, but does not modify the Resolver.
+ * matching one does not exist but does not modify the Resolver.
*
ComponentReference#init - create a new reference.
*
*/
@@ -82,7 +81,7 @@
/*
To extend, probably want to make a static function here that stores
attributes and a lookup interface to read that attribute. Do this only if
- it is directly needed.
+ it is directly necessary.
Should the JRE class be used instead of a custom class?
pros: doesn't save custom classes, which might change
@@ -93,14 +92,14 @@ attributes and a lookup interface to read that attribute. Do this only if
*/
/*
- Optimization note: All lookups are cached, so that at most we have to
+ Optimization note: All lookups are cached, so that at most we have to
traverse the hierarchy once.
Successful lookups are cached until the referenced component is GCd,
removed from the hierarchy, or otherwise marked invalid.
Unsuccessful lookups are cached for the duration of a particular lookup.
These happen in several places.
1) when resolving a cref into a component (getComponent())
- 2) when a script is checking for existing references prior to creating a
+ 2) when a script is checking for existing references before creating a
new one (getReference()).
3) when creating a new reference (ComponentReference().
4) when looking for a matching, existing reference (matchExisting()).
@@ -109,14 +108,14 @@ attributes and a lookup interface to read that attribute. Do this only if
see also NOTES
*/
-public class ComponentReference implements XMLConstants, XMLifiable, Comparable {
+public class ComponentReference
+ implements XMLConstants, XMLifiable, Comparable {
public static final String SHARED_FRAME_ID = "shared frame";
// Matching weights for various attributes
private static final int MW_NAME = 100;
private static final int MW_ROOT = 25;
- // private static final int MW_WEIGHTED = 50;
private static final int MW_TAG = 50;
private static final int MW_PARENT = 25; //
private static final int MW_WINDOW = 25;
@@ -128,15 +127,12 @@ public class ComponentReference implements XMLConstants, XMLifiable, Comparable
private static final int MW_ICON = 25;
private static final int MW_INDEX = 10; //
private static final int MW_CLASS = 1;
- // Pretty much for applets only, or other embedded frames
+ // Pretty much for embedded frames
private static final int MW_PARAMS = 1;
private static final int MW_DOCBASE = 1;
- // Mostly for distinguishing between multiple components that would
- // otherwise all match
+ // Mostly for distinguishing between multiple components that would otherwise all match
private static final int MW_HORDER = 1;
private static final int MW_VORDER = 1;
- // private static final int MW_ENABLED = 1;
- // private static final int MW_FOCUSED = 1;
private static final int MW_SHOWING = 1;
/**
@@ -150,15 +146,16 @@ public class ComponentReference implements XMLConstants, XMLifiable, Comparable
private final Map attributes = new HashMap<>();
// This helps component reference creation by an order of magnitude,
// especially when dealing with ordered attributes.
- private WeakReference cachedLookup;
+ private WeakReference cachedLookup;
/**
* This ThreadLocal allows us to keep track of unresolved components on a per-thread (basically
* per-lookup) basis.
*/
- private static final ThreadLocal lookupFailures =
- new ThreadLocal() {
- protected synchronized Object initialValue() {
+ private static final ThreadLocal