001: /*
002: * Copyright 2002-2005 Timothy Wall
003: *
004: */
005: package abbot.util;
006:
007: import java.awt.*;
008: import java.awt.event.*;
009:
010: import javax.swing.*;
011:
012: import junit.extensions.abbot.*;
013: import junit.extensions.abbot.Timer;
014: import abbot.tester.Robot;
015: import abbot.tester.ComponentTester;
016: import abbot.Platform;
017:
018: /**
019: Provides platform bugs detection. Also serves as a repository of tests for
020: all VM or OS-related bugs which affect Abbot operation. A boolean test for
021: each of these may appear in <code>abbot.util.Bugs</code>. <p>
022: Add tests that show the bug, then disable them only for the specific
023: platform/VM versions that are known to display the bug (i.e. use
024: Bugs.hasXXX() to avoid running the test). This ensures that the bug
025: is detected/tested for on new platforms/VMs, but doesn't needlessly clutter
026: test results with known failures. Define abbot.test.show_bugs=true in order
027: to see the failures when running tests.
028:
029: @author twall
030: */
031: public class BugsTest extends ComponentTestFixture {
032:
033: /** Indicates whether to test for bugs on a platform where they are known
034: * to exist, as opposed to silently ignoring them.
035: */
036: private static final boolean SHOW_BUGS = Boolean
037: .getBoolean("abbot.test.show_bugs");
038: private java.awt.Robot robot;
039:
040: protected void setUp() throws Exception {
041: robot = new java.awt.Robot();
042: }
043:
044: // OSX 1.4+
045: public void testWindowMotionBug() throws Exception {
046: // NOTE: this test fails in AWT mode
047: if (Robot.getEventMode() == Robot.EM_AWT)
048: return;
049:
050: if (!SHOW_BUGS && Bugs.hasMissingWindowMouseMotion())
051: return;
052:
053: Frame f = new Frame(getName());
054: Window w = new Window(f);
055: showWindow(w, new Dimension(100, 100));
056: class Flag {
057: volatile boolean flag;
058: }
059: final Flag flag1 = new Flag();
060: final Flag flag2 = new Flag();
061: AWTEventListener listener = new AWTEventListener() {
062: public void eventDispatched(AWTEvent e) {
063: if (e.getID() == MouseEvent.MOUSE_MOVED) {
064: flag1.flag = true;
065: }
066: }
067: };
068: w.addMouseMotionListener(new MouseMotionAdapter() {
069: public void mouseMoved(MouseEvent e) {
070: flag2.flag = true;
071: }
072: });
073: new WeakAWTEventListener(listener,
074: MouseEvent.MOUSE_MOTION_EVENT_MASK);
075:
076: int x = w.getX() + w.getWidth() / 2;
077: int y = w.getY() + w.getHeight() / 2;
078: robot.mouseMove(x, y);
079: robot.mouseMove(x, y + 1);
080: Timer timer = new Timer();
081: while (!flag1.flag || !flag2.flag) {
082: if (timer.elapsed() > 5000) {
083: fail("No motion event received: AWTEventListener="
084: + flag1.flag + ", MouseListener=" + flag2.flag);
085: }
086: }
087: }
088:
089: // OSX 1.4+
090: public void testButtonSwapBug() throws Throwable {
091: // NOTE: this test fails in AWT mode
092: if (Robot.getEventMode() == Robot.EM_AWT)
093: return;
094: if (!SHOW_BUGS && Bugs.hasRobotButtonsSwapped())
095: return;
096: javax.swing.JLabel label = new javax.swing.JLabel(getName());
097: MouseWatcher watcher = new MouseWatcher();
098: label.addMouseListener(watcher);
099: showFrame(label);
100: java.awt.Point pt = label.getLocationOnScreen();
101: robot.mouseMove(pt.x + label.getWidth() / 2, pt.y
102: + label.getHeight() / 2);
103: robot.mousePress(MouseEvent.BUTTON2_MASK);
104: robot.mouseRelease(MouseEvent.BUTTON2_MASK);
105: Timer timer = new Timer();
106: while (!watcher.gotClick) {
107: if (timer.elapsed() > EVENT_GENERATION_DELAY)
108: fail("Never received button 2 click event");
109: robot.delay(200);
110: }
111: assertEquals(
112: "This platform has bad robot button 2 mask mapping",
113: MouseEvent.BUTTON2_MASK, watcher.modifiers);
114:
115: watcher.gotClick = false;
116: robot.mousePress(MouseEvent.BUTTON3_MASK);
117: robot.mouseRelease(MouseEvent.BUTTON3_MASK);
118: timer.reset();
119: while (!watcher.gotClick) {
120: if (timer.elapsed() > EVENT_GENERATION_DELAY)
121: fail("Never received button 3 click event");
122: robot.delay(200);
123: }
124: assertEquals(
125: "This platform has bad robot button 3 mask mapping",
126: MouseEvent.BUTTON3_MASK, watcher.modifiers);
127: }
128:
129: // Found on several platforms. A subsequent mouse click on an entirely
130: // different component is considered to have a click count of 2 if it
131: // occurs within the double-click interval.
132: public void testMultiClickFrameBug() {
133: if (!SHOW_BUGS && Bugs.hasMultiClickFrameBug())
134: return;
135: String[] data = { "one", "two", "three", "four", "five" };
136: JFrame frame1 = new JFrame(getName() + "1");
137: JList list1 = new JList(data);
138: list1.setName("List 1");
139: frame1.getContentPane().add(list1);
140:
141: JFrame frame2 = new JFrame(getName() + "2");
142: JList list2 = new JList(data);
143: list2.setName("List 2");
144: frame2.getContentPane().add(list2);
145:
146: MouseWatcher mw1 = new MouseWatcher();
147: MouseWatcher mw2 = new MouseWatcher();
148: list1.addMouseListener(mw1);
149: list2.addMouseListener(mw2);
150: showWindow(frame1);
151:
152: Point pt = list1.getLocationOnScreen();
153: robot.mouseMove(pt.x + list1.getWidth() / 2, pt.y
154: + list1.getHeight() / 2);
155: robot.mousePress(MouseEvent.BUTTON1_MASK);
156: robot.mouseRelease(MouseEvent.BUTTON1_MASK);
157: robot.waitForIdle();
158:
159: frame1.setVisible(false);
160: showWindow(frame2);
161: pt = list2.getLocationOnScreen();
162: robot.mouseMove(pt.x + list2.getWidth() / 2, pt.y
163: + list2.getHeight() / 2);
164: robot.mousePress(MouseEvent.BUTTON1_MASK);
165: robot.mouseRelease(MouseEvent.BUTTON1_MASK);
166: robot.waitForIdle();
167: Timer timer = new Timer();
168: while (!mw1.gotClick) {
169: if (timer.elapsed() > EVENT_GENERATION_DELAY)
170: fail("Never received click event on first frame");
171: robot.delay(200);
172: }
173: timer.reset();
174: while (!mw2.gotClick) {
175: if (timer.elapsed() > EVENT_GENERATION_DELAY)
176: fail("Never received click event on second frame");
177: robot.delay(200);
178: }
179: assertEquals("Multi-click counting should not span frames", 1,
180: mw1.clickCount);
181: assertEquals("Multi-click counting should not span frames", 1,
182: mw2.clickCount);
183: }
184:
185: /** W32 systems block on AWT PopupMenu.show. If the method is invoked on
186: the EDT, the result is that Robot.waitForIdle will lock up and events
187: posted by Robot.postEvent will not be processed until the AWT popup
188: goes away.
189: */
190: public void testShowAWTPopupMenuBlocks() throws Exception {
191: boolean blocks = Bugs.showAWTPopupMenuBlocks();
192: if (!SHOW_BUGS && blocks)
193: return;
194:
195: Frame frame = new Frame(getName());
196: final PopupMenu popup = new PopupMenu();
197: MenuItem mi = new MenuItem("Open");
198: popup.add(mi);
199: final Label label = new Label(getName());
200: frame.add(label);
201: label.add(popup);
202: showWindow(frame);
203: assertEquals("Detected AWT popup state", false, AWT
204: .isAWTPopupMenuBlocking());
205: try {
206: class Flag {
207: volatile boolean flag;
208: }
209: final Flag flag = new Flag();
210: Runnable show = new Runnable() {
211: public void run() {
212: popup.show(label, 0, label.getHeight());
213: }
214: };
215: SwingUtilities.invokeLater(show);
216: SwingUtilities.invokeLater(new Runnable() {
217: public void run() {
218: flag.flag = true;
219: }
220: });
221: Timer timer = new Timer();
222: while (!flag.flag) {
223: if (timer.elapsed() > 5000)
224: fail("An active AWT Popup blocks the event queue");
225: Thread.sleep(10);
226: }
227: } finally {
228: AWT.dismissAWTPopup();
229: }
230: }
231:
232: /** Robot won't generate an ESC on OSX 1.3.1 (Apple VM bug). */
233: public void testEscapeKeyGenerationFailure() throws Exception {
234: final JTextField tf = new JTextField();
235: tf.setColumns(10);
236: KeyWatcher kw = new KeyWatcher();
237: tf.addKeyListener(kw);
238: showFrame(tf);
239: ComponentTester tester = new ComponentTester();
240: tester.actionFocus(tf);
241: int code = KeyEvent.VK_ESCAPE;
242: int mods = 0;
243: tester.actionKeyStroke(code, mods);
244: assertTrue("Never received key press", kw.gotPress);
245: assertTrue("Never received release", kw.gotRelease);
246: assertEquals("Wrong key code", code, kw.code);
247: assertEquals("Wrong modifiers", mods, kw.modifiers);
248: }
249:
250: public void testReportsIncorrectLockingKeyState() throws Throwable {
251: // NOTE: this test fails in AWT mode
252: if (Robot.getEventMode() == Robot.EM_AWT)
253: return;
254:
255: if (!SHOW_BUGS && Bugs.reportsIncorrectLockingKeyState())
256: return;
257:
258: // On w32, locking key state requires a non-disposed frame to work
259: // properly! Not sure if this is a VM bug or not.
260: // FIXME still gets sporadic failures on w32
261: if (Platform.isWindows()) {
262: showFrame(new JLabel(getName()));
263: }
264:
265: String[] KEYS = { "CAPS", "NUM", "SCROLL", "KANA", };
266: int[] CODES = { KeyEvent.VK_CAPS_LOCK, KeyEvent.VK_NUM_LOCK,
267: KeyEvent.VK_SCROLL_LOCK, KeyEvent.VK_KANA_LOCK, };
268: ComponentTester tester = new ComponentTester();
269: for (int i = 0; i < CODES.length; i++) {
270: try {
271: boolean state = Toolkit.getDefaultToolkit()
272: .getLockingKeyState(CODES[i]);
273: try {
274: tester.actionKeyStroke(CODES[i]);
275: boolean state2 = Toolkit.getDefaultToolkit()
276: .getLockingKeyState(CODES[i]);
277: assertEquals("Reported state of locking key '"
278: + KEYS[i] + "' did not change", !state,
279: state2);
280: } finally {
281: tester.actionKeyStroke(CODES[i]);
282: }
283: } catch (UnsupportedOperationException e) {
284: // ignore
285: }
286:
287: }
288: }
289:
290: public void testFileDialogMisreportsBounds() throws Exception {
291: if (!SHOW_BUGS && Bugs.fileDialogMisreportsBounds())
292: return;
293: JLabel label = new JLabel(getName());
294: Dimension size = Toolkit.getDefaultToolkit().getScreenSize();
295: Frame f = showFrame(label, size);
296: getRobot().move(f, 0, 0);
297: getRobot().waitForIdle();
298: FileDialog d = new FileDialog(f, getName(), FileDialog.LOAD);
299: class Flag {
300: volatile boolean flag;
301: }
302: final Flag flag = new Flag();
303: FocusListener listener = new FocusAdapter() {
304: public void focusGained(FocusEvent e) {
305: flag.flag = true;
306: }
307: };
308: size = new Dimension(400, 400);
309: showWindow(d, size, false);
310: getRobot().move(d, 100, 100);
311: getRobot().waitForIdle();
312:
313: robot.delay(500);
314: getRobot().waitForIdle();
315: Point loc = d.getLocation();
316: Dimension dsize = d.getSize();
317: Rectangle actual = new Rectangle(loc.x, loc.y, dsize.width,
318: dsize.height);
319: assertEquals("FileDialog reports wrong location or size",
320: new Rectangle(100, 100, 400, 400), actual);
321:
322: // Click inside the dialog; if the click misses, the frame will get
323: // focus.
324: f.addFocusListener(listener);
325: robot.mouseMove(loc.x + 50, loc.y + 50);
326: robot.mousePress(InputEvent.BUTTON1_MASK);
327: robot.mouseRelease(InputEvent.BUTTON1_MASK);
328: robot.delay(500);
329: robot.waitForIdle();
330: // Note that no FOCUS_LOST or WINDOW_ACTIVATED is posted on the
331: // FileDialog.
332: assertFalse("Mouse click should not go to frame", flag.flag);
333: }
334:
335: public void testFileDialogRequiresDismiss() throws Exception {
336: if (!SHOW_BUGS && Bugs.fileDialogRequiresDismiss())
337: return;
338:
339: JLabel label = new JLabel(getName());
340: Frame f = showFrame(label);
341:
342: FileDialog d = new FileDialog(f, getName(), FileDialog.LOAD);
343: showWindow(d, null, false);
344: // FIXME what was this for? It sometimes causes the frame to be moved
345: // on linux
346: /*
347: getRobot().move(f, (size.width - f.getWidth())/2,
348: (size.height - f.getHeight())/2);
349: */
350: getRobot().waitForIdle();
351: d.dispose();
352: assertFalse("Dispose should hide Dialog", d.isShowing());
353:
354: // Try to click on the main frame; if the dialog is still showing,
355: // it will swallow the event
356: MouseWatcher mw = new MouseWatcher();
357: label.addMouseListener(mw);
358:
359: // Ensure dialog has time to go away
360: getRobot().delay(500);
361: getRobot().click(label);
362: getRobot().waitForIdle();
363: assertTrue("File Dialog is still in front of main frame",
364: mw.gotPress);
365: }
366:
367: public void testHasKeyInputDelay() {
368: if (!SHOW_BUGS && Bugs.hasKeyInputDelay())
369: return;
370:
371: //fail("No automated test yet");
372: }
373:
374: public void testNoAWTInputOnTextField() {
375: if (!SHOW_BUGS && Bugs.hasNoAWTInputOnTextFieldBug())
376: return;
377:
378: // X11 fails to generate TextField input in AWT mode (fixed in 1.5).
379: // Use AWT_TOOLKIT=XToolkit to fix (post-1.5)
380: // So does OSX (1.6)
381: TextField tf = new TextField();
382: tf.setColumns(40);
383: showFrame(tf);
384: int old = Robot.getEventMode();
385: Robot.setEventMode(Robot.EM_AWT);
386: try {
387: ComponentTester tester = new ComponentTester();
388: final String TEXT = "The quick brown fox";
389: tester.actionKeyString(tf, TEXT);
390: assertEquals("AWT mode did not generate text in TextField",
391: TEXT, tf.getText());
392: } finally {
393: Robot.setEventMode(old);
394: }
395: }
396:
397: // Make this last, 'cuz it nukes the robot
398: public void testLocksUpOnScreenCapture() throws Exception {
399: if (!SHOW_BUGS && Bugs.locksUpOnScreenCapture())
400: return;
401:
402: // this happens in ComponentTestFixture.fixtureSetup, but
403: // make it explicit for the purposes of illustrating the failure
404: final java.awt.Robot robot = new java.awt.Robot();
405: robot.keyPress(KeyEvent.VK_SHIFT);
406: robot.keyRelease(KeyEvent.VK_SHIFT);
407:
408: final Image[] ref = { null };
409: Thread t = new Thread("Screen capture") {
410: public void run() {
411: Rectangle rect = new Rectangle(0, 0, 10, 10);
412: ref[0] = robot.createScreenCapture(rect);
413: }
414: };
415: t.start();
416: t.join(5000);
417: assertNotNull("Screen capture is hung", ref[0]);
418: }
419:
420: private class MouseWatcher extends MouseAdapter {
421: public volatile boolean gotPress = false;
422: public volatile boolean gotRelease = false;
423: public volatile boolean gotClick = false;
424: public volatile int clickCount = 0;
425: public volatile boolean popupTrigger = false;
426: public volatile Component source = null;
427: public volatile int modifiers = 0;
428: public volatile Point where = null;
429:
430: public void mousePressed(MouseEvent me) {
431: gotPress = true;
432: source = me.getComponent();
433: popupTrigger = me.isPopupTrigger();
434: modifiers = me.getModifiers();
435: where = me.getPoint();
436: }
437:
438: public void mouseReleased(MouseEvent me) {
439: gotRelease = true;
440: popupTrigger = popupTrigger || me.isPopupTrigger();
441: }
442:
443: public void mouseClicked(MouseEvent me) {
444: gotClick = true;
445: clickCount = me.getClickCount();
446: }
447: }
448:
449: private class KeyWatcher extends KeyAdapter {
450: public volatile int code = KeyEvent.VK_UNDEFINED;
451: public volatile int modifiers = 0;
452: public volatile boolean gotPress = false;
453: public volatile boolean gotRelease = false;
454:
455: public void keyPressed(KeyEvent ke) {
456: gotPress = true;
457: code = ke.getKeyCode();
458: modifiers = ke.getModifiers();
459: }
460:
461: public void keyReleased(KeyEvent ke) {
462: gotRelease = true;
463: }
464: }
465:
466: public static void main(String[] args) {
467: if (!SHOW_BUGS) {
468: System.out
469: .println("Use -Dabbot.test.show_bugs=true to run tests "
470: + "for known bugs on "
471: + System.getProperty("os.name") + ".");
472: System.out.println("By default, such tests are skipped.");
473: }
474: RepeatHelper.runTests(args, BugsTest.class);
475: }
476: }
|