blob: 7029470265839ed6cb4fd45d144f4b03928c5382 [file]
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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.
*/
/**
* @author Anton Avtamonov
* @version $Revision$
*/
package javax.swing;
import java.awt.Component;
import java.awt.Container;
import java.awt.Font;
import java.awt.FontMetrics;
import java.awt.Frame;
import java.awt.Graphics;
import java.awt.Robot;
import java.awt.Toolkit;
import java.awt.image.BufferedImage;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
import java.util.EventListener;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
import javax.swing.text.BadLocationException;
import junit.framework.TestCase;
public abstract class BasicSwingTestCase extends TestCase {
public static final long DEFAULT_TIMEOUT_DELAY = 10000;
private boolean ignoreNotImplemented;
private Robot robot;
private LookAndFeel previousLF;
private static long defaultTimeoutDelay;
protected static long timeoutDelay;
static {
String timeoutProp = System.getProperty("test.timeout");
if (timeoutProp == null || timeoutProp.length() == 0) {
defaultTimeoutDelay = DEFAULT_TIMEOUT_DELAY;
} else {
defaultTimeoutDelay = Integer.parseInt(timeoutProp);
}
}
@Override
protected void setUp() throws Exception {
timeoutDelay = defaultTimeoutDelay;
previousLF = UIManager.getLookAndFeel();
super.setUp();
}
@Override
protected void tearDown() throws Exception {
super.tearDown();
UIManager.getDefaults().clear();
if (previousLF != null) {
UIManager.setLookAndFeel(previousLF);
} else {
UIManager.setLookAndFeel(UIManager.getCrossPlatformLookAndFeelClassName());
}
JFrame.setDefaultLookAndFeelDecorated(false);
JDialog.setDefaultLookAndFeelDecorated(false);
closeAllFrames();
}
public static void assertEquals(final Object[] expected, final Object[] actual) {
if (expected == null && actual == null) {
return;
}
if (expected == null || actual == null) {
fail("Arrays are not same: one of them is null");
}
assertEquals("Arrays of different types", expected.getClass(), actual.getClass());
assertEquals("Arrays of different lengths", expected.length, actual.length);
for (int i = 0; i < expected.length; i++) {
assertEquals("Arrays content at index " + i + " is not same", expected[i], actual[i]);
}
}
public BasicSwingTestCase() {
}
public BasicSwingTestCase(final String name) {
super(name);
}
protected void runBareSuper() throws Throwable {
super.runBare();
}
protected Throwable runBareImpl() throws Throwable {
try {
runBareSuper();
} catch (Throwable e) {
return e;
}
return null;
}
@Override
public void runBare() throws Throwable {
final Throwable[] exception = new Throwable[1];
Thread thread = new Thread(new Runnable() {
public void run() {
try {
exception[0] = runBareImpl();
} catch (Throwable e) {
}
}
});
thread.start();
thread.join(timeoutDelay);
int interruptAttempts = 0;
while (thread.isAlive()){
thread.interrupt();
if (interruptAttempts++ > 5) {
fail("Test interrupted due timeout");
}
}
if (exception[0] != null) {
rethrow(exception[0]);
}
}
public void setIgnoreNotImplemented(final boolean b) {
ignoreNotImplemented = b;
}
protected static class Marker {
private final boolean autoreset;
private boolean occured;
private Object auxiliary;
public Marker() {
this(false);
}
/**
* If autoreset is turned on, then the call to <code>isOccurred()</code>
* resets the state just before returning the value.
*
* @param autoreset enables the autoreset feature
*/
public Marker(final boolean autoreset) {
this.autoreset = autoreset;
}
public void setOccurred() {
setOccurred(true);
}
public void setOccurred(final boolean occured) {
this.occured = occured;
}
/**
* Returns the state of this marker. If autoreset feature is enabled,
* <code>reset()</code> method will be called before return. Beware that
* any auxiliary data will also be reset, if the feature is enabled.
*
* @return true if an event occurred
*/
public boolean isOccurred() {
boolean result = occured;
if (autoreset) {
reset();
}
return result;
}
public void setAuxiliary(final Object aux) {
auxiliary = aux;
}
public Object getAuxiliary() {
return auxiliary;
}
public void reset() {
occured = false;
auxiliary = null;
}
public boolean isAutoreset() {
return autoreset;
}
}
private class FixedFontMetrics extends FontMetrics {
private static final long serialVersionUID = 1L;
private final int charWidth;
private final int charHeight;
public FixedFontMetrics(final Font fnt, final int charWidth) {
super(fnt);
this.charWidth = charWidth;
charHeight = super.getHeight();
}
public FixedFontMetrics(final Font fnt, final int charWidth, final int charHeight) {
super(fnt);
this.charWidth = charWidth;
this.charHeight = charHeight;
}
@Override
public int stringWidth(final String str) {
return ((str == null || str.equals("")) ? 0 : str.length() * charWidth);
}
@Override
public int charWidth(final int ch) {
return charWidth;
}
@Override
public int charWidth(final char ch) {
return charWidth;
}
@Override
public int getAscent() {
return charWidth;
}
@Override
public int getDescent() {
return charWidth*2;
}
@Override
public int getLeading() {
return 2;
}
@Override
public int getHeight() {
return charHeight;
}
};
protected abstract class ExceptionalCase {
private Class<?> clazz;
private String msg;
public abstract void exceptionalAction() throws Exception;
public ExceptionalCase() {
}
public ExceptionalCase(String msg, Class<?> clazz) {
this.msg = msg;
this.clazz = clazz;
}
public Class<?> expectedExceptionClass() {
return clazz;
}
public String expectedExceptionMessage() {
return msg;
}
}
protected abstract class BadLocationCase extends ExceptionalCase {
@Override
public Class<?> expectedExceptionClass() {
return BadLocationException.class;
}
}
protected abstract class ClassCastCase extends ExceptionalCase {
@Override
public Class<?> expectedExceptionClass() {
return ClassCastException.class;
}
}
protected abstract class IllegalArgumentCase extends ExceptionalCase {
@Override
public Class<?> expectedExceptionClass() {
return IllegalArgumentException.class;
}
}
protected abstract class NullPointerCase extends ExceptionalCase {
@Override
public Class<?> expectedExceptionClass() {
return NullPointerException.class;
}
}
protected abstract class StringIndexOutOfBoundsCase extends ExceptionalCase {
@Override
public Class<?> expectedExceptionClass() {
return StringIndexOutOfBoundsException.class;
}
}
protected static class EventsController implements Serializable {
private static final long serialVersionUID = 1L;
private boolean isVerbose;
private Object lastEventHappened;
private final Map<Object, Object> events = new HashMap<Object, Object>();
protected EventsController() {
this(false);
}
protected EventsController(final boolean verbose) {
isVerbose = verbose;
}
public void setVerbose(final boolean verbose) {
isVerbose = verbose;
}
protected boolean isVerbose() {
return isVerbose;
}
public void reset() {
lastEventHappened = null;
events.clear();
}
public boolean isChanged() {
return events.size() != 0;
}
public boolean isChanged(final Object key) {
if (!isChanged() || key == null) {
return false;
}
for (Iterator<Object> it = events.keySet().iterator(); it.hasNext(); ) {
String property = (String)it.next();
if (key.equals(property)) {
return true;
}
}
return false;
}
protected void addEvent(final Object key, final Object event) {
events.put(key, event);
lastEventHappened = event;
}
public Object getEvent(final Object key) {
return events.get(key);
}
public Object getLastEvent() {
return lastEventHappened;
}
public int getNumEvents() {
return events.size();
}
public int findMe(final Object[] listenersArray) {
int found = 0;
for (Object element : listenersArray) {
if (element == this) {
found++;
}
}
return found;
}
}
protected class PropertyChangeController extends EventsController implements PropertyChangeListener {
private static final long serialVersionUID = 1L;
public PropertyChangeController() {
super(false);
}
public PropertyChangeController(final boolean verbose) {
super(verbose);
}
public void propertyChange(final PropertyChangeEvent e) {
addEvent(e.getPropertyName(), e);
if (isVerbose()) {
System.out.println("Changed property " + e.getPropertyName() + ", old value = '" + e.getOldValue() + "', new value = '" + e.getNewValue() + "'");
}
}
public void checkLastPropertyFired(final Object source, final String propertyName,
final Object oldValue, final Object newValue) {
PropertyChangeEvent lastEvent = (PropertyChangeEvent)getLastEvent();
checkEvent(lastEvent, source, propertyName, oldValue, newValue);
}
public void checkPropertyFired(final Object source, final String propertyName,
final Object oldValue, final Object newValue) {
PropertyChangeEvent e = (PropertyChangeEvent)getEvent(propertyName);
checkEvent(e, source, propertyName, oldValue, newValue);
}
private void checkEvent(PropertyChangeEvent event, final Object source,
final String propertyName,
final Object oldValue, final Object newValue) {
assertNotNull("event's been fired ", event);
assertEquals("event's property name ", propertyName, event.getPropertyName());
assertEquals("event's oldValue ", oldValue, event.getOldValue());
assertEquals("event's newValue ", newValue, event.getNewValue());
assertSame("event's source ", source, event.getSource());
}
}
protected class ChangeController extends EventsController implements ChangeListener {
private static final long serialVersionUID = 1L;
public ChangeController() {
super(false);
}
public ChangeController(final boolean verbose) {
super(verbose);
}
public void stateChanged(ChangeEvent e) {
addEvent(new Integer(getNumEvents()), e);
if (isVerbose()) {
System.out.println("Changed");
}
}
public ChangeEvent getEvent() {
return (ChangeEvent)getLastEvent();
}
}
protected interface TraverseAction {
void componentTraversed(Component c);
}
/**
* PropertyChangeListener for test purposes to easily check that some property was changed
*/
protected PropertyChangeController propertyChangeController;
public static boolean isHarmony() {
return Toolkit.getDefaultToolkit().getClass().getName().equals("java.awt.ToolkitImpl");
}
/**
* Serializes an object and returns the deserialized version of this object.
*
* @param objectToWrite object to serialize
* @return the deserialized object if successful
* @throws IOException if write or read operation throws this exception.
* @throws ClassNotFoundException if object cannot be read
* from an input stream
*/
public static Object serializeObject(final Object objectToWrite)
throws IOException, ClassNotFoundException {
ByteArrayOutputStream output = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(output);
oos.writeObject(objectToWrite);
oos.close();
output.close();
InputStream input = new ByteArrayInputStream(output.toByteArray());
ObjectInputStream ois = new ObjectInputStream(input);
Object result = ois.readObject();
ois.close();
input.close();
return result;
}
protected Graphics createTestGraphics() {
return new BufferedImage(300, 300, BufferedImage.TYPE_3BYTE_BGR).createGraphics();
}
protected FontMetrics getFontMetrics(final Font fnt) {
return new FixedFontMetrics(fnt, fnt.getSize());
}
protected FontMetrics getFontMetrics(final Font fnt, final int charWidth) {
return new FixedFontMetrics(fnt, charWidth);
}
protected FontMetrics getFontMetrics(final Font fnt, final int charWidth, final int charHeight) {
return new FixedFontMetrics(fnt, charWidth, charHeight);
}
protected void testExceptionalCase(final ExceptionalCase ec) {
try {
ec.exceptionalAction();
fail("Exceptional case was not detected!");
} catch (final Exception e) {
if (ec.expectedExceptionClass() != null) {
if (!ec.expectedExceptionClass().isAssignableFrom(e.getClass())) {
fail("Exception of wrong type " + e.getClass() + " is produced!");
}
}
if (ec.expectedExceptionMessage() != null) {
assertEquals("Wrong exception message", ec.expectedExceptionMessage(),
e.getMessage());
}
}
}
protected void traverseComponentTree(final Component root, final TraverseAction action) {
action.componentTraversed(root);
if (root instanceof Container) {
for (int i = 0; i < ((Container)root).getComponentCount(); i++) {
traverseComponentTree(((Container)root).getComponent(i), action);
}
}
}
protected void rethrow(final Throwable exception) throws Throwable {
String msg = exception.getMessage();
if (!ignoreNotImplemented || msg == null
|| exception.getMessage().indexOf("implemented") == -1) {
throw exception;
}
}
protected void waitForIdle() throws Exception {
if (SwingUtilities.isEventDispatchThread()) {
getRobot().waitForIdle();
} else {
SwingUtilities.invokeAndWait(new Thread());
}
}
protected boolean hasListener(final EventListener[] listeners, final Class<?> listenerClass) {
return getListener(listeners, listenerClass) != null;
}
protected EventListener getListener(final EventListener[] listeners, final Class<?> listenerClass) {
for (EventListener element : listeners) {
if (element.getClass().isAssignableFrom(listenerClass)) {
return element;
}
}
return null;
}
protected boolean waitForFocus(final Component c) throws Exception {
int counter = 0;
while(true) {
if (isFocusOwner(c)) {
return true;
}
Thread.sleep(100);
counter++;
if (counter > 10) {
return false;
}
}
}
protected boolean isSystemWindow(final Frame f) {
return "JUnit".equals(f.getTitle());
}
protected Component findComponent(final Container root, final Class<?> findClass, final boolean exactClassMatch) {
if (exactClassMatch && findClass == root.getClass()
|| !exactClassMatch && findClass.isAssignableFrom(root.getClass())) {
return root;
}
for (int i = 0; i < root.getComponentCount(); i++) {
Component child = root.getComponent(i);
if (child instanceof Container) {
child = findComponent((Container)child, findClass, exactClassMatch);
if (child != null) {
return child;
}
} else if (exactClassMatch && findClass == child.getClass()
|| !exactClassMatch && findClass.isAssignableFrom(child.getClass())) {
return child;
}
}
return null;
}
private boolean isFocusOwner(final Component c) throws Exception {
final Marker result = new Marker();
SwingUtilities.invokeAndWait(new Runnable() {
public void run() {
result.setOccurred(c.isFocusOwner());
}
});
return result.isOccurred();
}
private Robot getRobot() throws Exception {
if (robot == null) {
robot = new Robot();
}
return robot;
}
private void closeAllFrames() {
Frame[] frames = Frame.getFrames();
for (Frame f : frames) {
if (f.isDisplayable()) {
if (!isSystemWindow(f)) {
f.dispose();
}
}
}
}
}