001: /*
002: * Licensed to the Apache Software Foundation (ASF) under one or more
003: * contributor license agreements. See the NOTICE file distributed with
004: * this work for additional information regarding copyright ownership.
005: * The ASF licenses this file to You under the Apache License, Version 2.0
006: * (the "License"); you may not use this file except in compliance with
007: * the License. You may obtain a copy of the License at
008: *
009: * http://www.apache.org/licenses/LICENSE-2.0
010: *
011: * Unless required by applicable law or agreed to in writing, software
012: * distributed under the License is distributed on an "AS IS" BASIS,
013: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014: * See the License for the specific language governing permissions and
015: * limitations under the License.
016: */
017:
018: /**
019: * @author Anton Avtamonov
020: * @version $Revision$
021: */package javax.swing;
022:
023: import java.awt.Component;
024: import java.awt.Container;
025: import java.awt.Font;
026: import java.awt.FontMetrics;
027: import java.awt.Frame;
028: import java.awt.Graphics;
029: import java.awt.Robot;
030: import java.awt.Toolkit;
031: import java.awt.image.BufferedImage;
032: import java.beans.PropertyChangeEvent;
033: import java.beans.PropertyChangeListener;
034: import java.io.ByteArrayInputStream;
035: import java.io.ByteArrayOutputStream;
036: import java.io.IOException;
037: import java.io.InputStream;
038: import java.io.ObjectInputStream;
039: import java.io.ObjectOutputStream;
040: import java.io.Serializable;
041: import java.util.EventListener;
042: import java.util.HashMap;
043: import java.util.Iterator;
044: import java.util.Map;
045:
046: import javax.swing.event.ChangeEvent;
047: import javax.swing.event.ChangeListener;
048: import javax.swing.text.BadLocationException;
049:
050: import junit.framework.TestCase;
051:
052: public abstract class BasicSwingTestCase extends TestCase {
053: public static final long DEFAULT_TIMEOUT_DELAY = 10000;
054:
055: private boolean ignoreNotImplemented;
056: private Robot robot;
057: private LookAndFeel previousLF;
058:
059: private static long defaultTimeoutDelay;
060: protected static long timeoutDelay;
061:
062: static {
063: String timeoutProp = System.getProperty("test.timeout");
064: if (timeoutProp == null || timeoutProp.length() == 0) {
065: defaultTimeoutDelay = DEFAULT_TIMEOUT_DELAY;
066: } else {
067: defaultTimeoutDelay = Integer.parseInt(timeoutProp);
068: }
069: }
070:
071: @Override
072: protected void setUp() throws Exception {
073: timeoutDelay = defaultTimeoutDelay;
074: previousLF = UIManager.getLookAndFeel();
075: super .setUp();
076: }
077:
078: @Override
079: protected void tearDown() throws Exception {
080: super .tearDown();
081: UIManager.getDefaults().clear();
082: if (previousLF != null) {
083: UIManager.setLookAndFeel(previousLF);
084: } else {
085: UIManager.setLookAndFeel(UIManager
086: .getCrossPlatformLookAndFeelClassName());
087: }
088: JFrame.setDefaultLookAndFeelDecorated(false);
089: JDialog.setDefaultLookAndFeelDecorated(false);
090: closeAllFrames();
091: }
092:
093: public static void assertEquals(final Object[] expected,
094: final Object[] actual) {
095: if (expected == null && actual == null) {
096: return;
097: }
098: if (expected == null || actual == null) {
099: fail("Arrays are not same: one of them is null");
100: }
101: assertEquals("Arrays of different types", expected.getClass(),
102: actual.getClass());
103: assertEquals("Arrays of different lengths", expected.length,
104: actual.length);
105: for (int i = 0; i < expected.length; i++) {
106: assertEquals("Arrays content at index " + i
107: + " is not same", expected[i], actual[i]);
108: }
109: }
110:
111: public BasicSwingTestCase() {
112: }
113:
114: public BasicSwingTestCase(final String name) {
115: super (name);
116: }
117:
118: protected void runBareSuper() throws Throwable {
119: super .runBare();
120: }
121:
122: protected Throwable runBareImpl() throws Throwable {
123: try {
124: runBareSuper();
125: } catch (Throwable e) {
126: return e;
127: }
128:
129: return null;
130: }
131:
132: @Override
133: public void runBare() throws Throwable {
134: final Throwable[] exception = new Throwable[1];
135: Thread thread = new Thread(new Runnable() {
136: public void run() {
137: try {
138: exception[0] = runBareImpl();
139: } catch (Throwable e) {
140: }
141: }
142: });
143: thread.start();
144: thread.join(timeoutDelay);
145: int interruptAttempts = 0;
146: while (thread.isAlive()) {
147: thread.interrupt();
148: if (interruptAttempts++ > 5) {
149: fail("Test interrupted due timeout");
150: }
151: }
152: if (exception[0] != null) {
153: rethrow(exception[0]);
154: }
155: }
156:
157: public void setIgnoreNotImplemented(final boolean b) {
158: ignoreNotImplemented = b;
159: }
160:
161: protected static class Marker {
162: private final boolean autoreset;
163:
164: private boolean occured;
165: private Object auxiliary;
166:
167: public Marker() {
168: this (false);
169: }
170:
171: /**
172: * If autoreset is turned on, then the call to <code>isOccurred()</code>
173: * resets the state just before returning the value.
174: *
175: * @param autoreset enables the autoreset feature
176: */
177: public Marker(final boolean autoreset) {
178: this .autoreset = autoreset;
179: }
180:
181: public void setOccurred() {
182: setOccurred(true);
183: }
184:
185: public void setOccurred(final boolean occured) {
186: this .occured = occured;
187: }
188:
189: /**
190: * Returns the state of this marker. If autoreset feature is enabled,
191: * <code>reset()</code> method will be called before return. Beware that
192: * any auxiliary data will also be reset, if the feature is enabled.
193: *
194: * @return true if an event occurred
195: */
196: public boolean isOccurred() {
197: boolean result = occured;
198: if (autoreset) {
199: reset();
200: }
201: return result;
202: }
203:
204: public void setAuxiliary(final Object aux) {
205: auxiliary = aux;
206: }
207:
208: public Object getAuxiliary() {
209: return auxiliary;
210: }
211:
212: public void reset() {
213: occured = false;
214: auxiliary = null;
215: }
216:
217: public boolean isAutoreset() {
218: return autoreset;
219: }
220: }
221:
222: private class FixedFontMetrics extends FontMetrics {
223: private static final long serialVersionUID = 1L;
224: private final int charWidth;
225: private final int charHeight;
226:
227: public FixedFontMetrics(final Font fnt, final int charWidth) {
228: super (fnt);
229: this .charWidth = charWidth;
230: charHeight = super .getHeight();
231: }
232:
233: public FixedFontMetrics(final Font fnt, final int charWidth,
234: final int charHeight) {
235: super (fnt);
236: this .charWidth = charWidth;
237: this .charHeight = charHeight;
238: }
239:
240: @Override
241: public int stringWidth(final String str) {
242: return ((str == null || str.equals("")) ? 0 : str.length()
243: * charWidth);
244: }
245:
246: @Override
247: public int charWidth(final int ch) {
248: return charWidth;
249: }
250:
251: @Override
252: public int charWidth(final char ch) {
253: return charWidth;
254: }
255:
256: @Override
257: public int getAscent() {
258: return charWidth;
259: }
260:
261: @Override
262: public int getDescent() {
263: return charWidth * 2;
264: }
265:
266: @Override
267: public int getLeading() {
268: return 2;
269: }
270:
271: @Override
272: public int getHeight() {
273: return charHeight;
274: }
275: };
276:
277: protected abstract class ExceptionalCase {
278: private Class<?> clazz;
279: private String msg;
280:
281: public abstract void exceptionalAction() throws Exception;
282:
283: public ExceptionalCase() {
284: }
285:
286: public ExceptionalCase(String msg, Class<?> clazz) {
287: this .msg = msg;
288: this .clazz = clazz;
289: }
290:
291: public Class<?> expectedExceptionClass() {
292: return clazz;
293: }
294:
295: public String expectedExceptionMessage() {
296: return msg;
297: }
298: }
299:
300: protected abstract class BadLocationCase extends ExceptionalCase {
301: @Override
302: public Class<?> expectedExceptionClass() {
303: return BadLocationException.class;
304: }
305: }
306:
307: protected abstract class ClassCastCase extends ExceptionalCase {
308: @Override
309: public Class<?> expectedExceptionClass() {
310: return ClassCastException.class;
311: }
312: }
313:
314: protected abstract class IllegalArgumentCase extends
315: ExceptionalCase {
316: @Override
317: public Class<?> expectedExceptionClass() {
318: return IllegalArgumentException.class;
319: }
320: }
321:
322: protected abstract class NullPointerCase extends ExceptionalCase {
323: @Override
324: public Class<?> expectedExceptionClass() {
325: return NullPointerException.class;
326: }
327: }
328:
329: protected abstract class StringIndexOutOfBoundsCase extends
330: ExceptionalCase {
331: @Override
332: public Class<?> expectedExceptionClass() {
333: return StringIndexOutOfBoundsException.class;
334: }
335: }
336:
337: protected static class EventsController implements Serializable {
338: private static final long serialVersionUID = 1L;
339: private boolean isVerbose;
340: private Object lastEventHappened;
341: private final Map<Object, Object> events = new HashMap<Object, Object>();
342:
343: protected EventsController() {
344: this (false);
345: }
346:
347: protected EventsController(final boolean verbose) {
348: isVerbose = verbose;
349: }
350:
351: public void setVerbose(final boolean verbose) {
352: isVerbose = verbose;
353: }
354:
355: protected boolean isVerbose() {
356: return isVerbose;
357: }
358:
359: public void reset() {
360: lastEventHappened = null;
361: events.clear();
362: }
363:
364: public boolean isChanged() {
365: return events.size() != 0;
366: }
367:
368: public boolean isChanged(final Object key) {
369: if (!isChanged() || key == null) {
370: return false;
371: }
372: for (Iterator<Object> it = events.keySet().iterator(); it
373: .hasNext();) {
374: String property = (String) it.next();
375: if (key.equals(property)) {
376: return true;
377: }
378: }
379:
380: return false;
381: }
382:
383: protected void addEvent(final Object key, final Object event) {
384: events.put(key, event);
385: lastEventHappened = event;
386: }
387:
388: public Object getEvent(final Object key) {
389: return events.get(key);
390: }
391:
392: public Object getLastEvent() {
393: return lastEventHappened;
394: }
395:
396: public int getNumEvents() {
397: return events.size();
398: }
399:
400: public int findMe(final Object[] listenersArray) {
401: int found = 0;
402: for (Object element : listenersArray) {
403: if (element == this ) {
404: found++;
405: }
406: }
407:
408: return found;
409: }
410: }
411:
412: protected class PropertyChangeController extends EventsController
413: implements PropertyChangeListener {
414: private static final long serialVersionUID = 1L;
415:
416: public PropertyChangeController() {
417: super (false);
418: }
419:
420: public PropertyChangeController(final boolean verbose) {
421: super (verbose);
422: }
423:
424: public void propertyChange(final PropertyChangeEvent e) {
425: addEvent(e.getPropertyName(), e);
426: if (isVerbose()) {
427: System.out.println("Changed property "
428: + e.getPropertyName() + ", old value = '"
429: + e.getOldValue() + "', new value = '"
430: + e.getNewValue() + "'");
431: }
432: }
433:
434: public void checkLastPropertyFired(final Object source,
435: final String propertyName, final Object oldValue,
436: final Object newValue) {
437: PropertyChangeEvent lastEvent = (PropertyChangeEvent) getLastEvent();
438: checkEvent(lastEvent, source, propertyName, oldValue,
439: newValue);
440: }
441:
442: public void checkPropertyFired(final Object source,
443: final String propertyName, final Object oldValue,
444: final Object newValue) {
445: PropertyChangeEvent e = (PropertyChangeEvent) getEvent(propertyName);
446: checkEvent(e, source, propertyName, oldValue, newValue);
447: }
448:
449: private void checkEvent(PropertyChangeEvent event,
450: final Object source, final String propertyName,
451: final Object oldValue, final Object newValue) {
452: assertNotNull("event's been fired ", event);
453: assertEquals("event's property name ", propertyName, event
454: .getPropertyName());
455: assertEquals("event's oldValue ", oldValue, event
456: .getOldValue());
457: assertEquals("event's newValue ", newValue, event
458: .getNewValue());
459: assertSame("event's source ", source, event.getSource());
460: }
461: }
462:
463: protected class ChangeController extends EventsController implements
464: ChangeListener {
465: private static final long serialVersionUID = 1L;
466:
467: public ChangeController() {
468: super (false);
469: }
470:
471: public ChangeController(final boolean verbose) {
472: super (verbose);
473: }
474:
475: public void stateChanged(ChangeEvent e) {
476: addEvent(new Integer(getNumEvents()), e);
477: if (isVerbose()) {
478: System.out.println("Changed");
479: }
480: }
481:
482: public ChangeEvent getEvent() {
483: return (ChangeEvent) getLastEvent();
484: }
485: }
486:
487: protected interface TraverseAction {
488: void componentTraversed(Component c);
489: }
490:
491: /**
492: * PropertyChangeListener for test purposes to easily check that some property was changed
493: */
494: protected PropertyChangeController propertyChangeController;
495:
496: public static boolean isHarmony() {
497: return Toolkit.getDefaultToolkit().getClass().getName().equals(
498: "java.awt.ToolkitImpl");
499: }
500:
501: /**
502: * Serializes an object and returns the deserialized version of this object.
503: *
504: * @param objectToWrite object to serialize
505: * @return the deserialized object if successful
506: * @throws IOException if write or read operation throws this exception.
507: * @throws ClassNotFoundException if object cannot be read
508: * from an input stream
509: */
510: public static Object serializeObject(final Object objectToWrite)
511: throws IOException, ClassNotFoundException {
512:
513: ByteArrayOutputStream output = new ByteArrayOutputStream();
514: ObjectOutputStream oos = new ObjectOutputStream(output);
515: oos.writeObject(objectToWrite);
516: oos.close();
517: output.close();
518:
519: InputStream input = new ByteArrayInputStream(output
520: .toByteArray());
521: ObjectInputStream ois = new ObjectInputStream(input);
522: Object result = ois.readObject();
523: ois.close();
524: input.close();
525:
526: return result;
527: }
528:
529: protected Graphics createTestGraphics() {
530: return new BufferedImage(300, 300, BufferedImage.TYPE_3BYTE_BGR)
531: .createGraphics();
532: }
533:
534: protected FontMetrics getFontMetrics(final Font fnt) {
535: return new FixedFontMetrics(fnt, fnt.getSize());
536: }
537:
538: protected FontMetrics getFontMetrics(final Font fnt,
539: final int charWidth) {
540: return new FixedFontMetrics(fnt, charWidth);
541: }
542:
543: protected FontMetrics getFontMetrics(final Font fnt,
544: final int charWidth, final int charHeight) {
545: return new FixedFontMetrics(fnt, charWidth, charHeight);
546: }
547:
548: protected void testExceptionalCase(final ExceptionalCase ec) {
549: try {
550: ec.exceptionalAction();
551: fail("Exceptional case was not detected!");
552: } catch (final Exception e) {
553: if (ec.expectedExceptionClass() != null) {
554: if (!ec.expectedExceptionClass().isAssignableFrom(
555: e.getClass())) {
556: fail("Exception of wrong type " + e.getClass()
557: + " is produced!");
558: }
559: }
560: if (ec.expectedExceptionMessage() != null) {
561: assertEquals("Wrong exception message", ec
562: .expectedExceptionMessage(), e.getMessage());
563: }
564: }
565: }
566:
567: protected void traverseComponentTree(final Component root,
568: final TraverseAction action) {
569: action.componentTraversed(root);
570: if (root instanceof Container) {
571: for (int i = 0; i < ((Container) root).getComponentCount(); i++) {
572: traverseComponentTree(((Container) root)
573: .getComponent(i), action);
574: }
575: }
576: }
577:
578: protected void rethrow(final Throwable exception) throws Throwable {
579: String msg = exception.getMessage();
580: if (!ignoreNotImplemented || msg == null
581: || exception.getMessage().indexOf("implemented") == -1) {
582: throw exception;
583: }
584: }
585:
586: protected void waitForIdle() throws Exception {
587: if (SwingUtilities.isEventDispatchThread()) {
588: getRobot().waitForIdle();
589: } else {
590: SwingUtilities.invokeAndWait(new Thread());
591: }
592: }
593:
594: protected boolean hasListener(final EventListener[] listeners,
595: final Class<?> listenerClass) {
596: return getListener(listeners, listenerClass) != null;
597: }
598:
599: protected EventListener getListener(
600: final EventListener[] listeners,
601: final Class<?> listenerClass) {
602: for (EventListener element : listeners) {
603: if (element.getClass().isAssignableFrom(listenerClass)) {
604: return element;
605: }
606: }
607:
608: return null;
609: }
610:
611: protected boolean waitForFocus(final Component c) throws Exception {
612: int counter = 0;
613: while (true) {
614: if (isFocusOwner(c)) {
615: return true;
616: }
617: Thread.sleep(100);
618: counter++;
619: if (counter > 10) {
620: return false;
621: }
622: }
623: }
624:
625: protected boolean isSystemWindow(final Frame f) {
626: return "JUnit".equals(f.getTitle());
627: }
628:
629: protected Component findComponent(final Container root,
630: final Class<?> findClass, final boolean exactClassMatch) {
631: if (exactClassMatch && findClass == root.getClass()
632: || !exactClassMatch
633: && findClass.isAssignableFrom(root.getClass())) {
634:
635: return root;
636: }
637:
638: for (int i = 0; i < root.getComponentCount(); i++) {
639: Component child = root.getComponent(i);
640: if (child instanceof Container) {
641: child = findComponent((Container) child, findClass,
642: exactClassMatch);
643: if (child != null) {
644: return child;
645: }
646: } else if (exactClassMatch && findClass == child.getClass()
647: || !exactClassMatch
648: && findClass.isAssignableFrom(child.getClass())) {
649:
650: return child;
651: }
652: }
653:
654: return null;
655: }
656:
657: private boolean isFocusOwner(final Component c) throws Exception {
658: final Marker result = new Marker();
659: SwingUtilities.invokeAndWait(new Runnable() {
660: public void run() {
661: result.setOccurred(c.isFocusOwner());
662: }
663: });
664:
665: return result.isOccurred();
666: }
667:
668: private Robot getRobot() throws Exception {
669: if (robot == null) {
670: robot = new Robot();
671: }
672:
673: return robot;
674: }
675:
676: private void closeAllFrames() {
677: Frame[] frames = Frame.getFrames();
678: for (Frame f : frames) {
679: if (f.isDisplayable()) {
680: if (!isSystemWindow(f)) {
681: f.dispose();
682: }
683: }
684: }
685: }
686: }
|