/*
* Geotoolkit.org - An Open Source Java GIS Toolkit
* http://www.geotoolkit.org
*
* (C) 2005-2012, Open Source Geospatial Foundation (OSGeo)
* (C) 2009-2012, Geomatys
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation;
* version 2.1 of the License.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*/
package org.geotoolkit.test.gui;
import java.io.File;
import javax.imageio.ImageIO;
import javax.swing.JComponent;
import java.awt.Graphics2D;
import java.awt.HeadlessException;
import java.awt.image.BufferedImage;
import org.junit.*;
import static org.junit.Assert.*;
import static org.junit.Assume.*;
/**
* Base class for tests on widgets. By default this test suite displays nothing;
* it merely:
* <p>
* <ul>
* <li>Ensure that no exception is thrown while creating the widget</li>
* <li>Ensure that no exception is thrown while painting in a buffered image</li>
* </ul>
* <p>
* However if the "{@code org.geotoolkit.showWidgetTests}" system property is
* set to "{@code true}", then the widgets will be shown as an internal frame.
*
* @param <T> The type of the widget to be tested.
*
* @author Martin Desruisseaux (IRD, Geomatys)
* @version 3.16
*
* @since 2.3
*/
@SuppressWarnings("serial")
public abstract strictfp class SwingTestBase<T extends JComponent> {
/**
* The name of a system property for setting whatever the widget should be show.
* If the value returned by the following is {@code true}, then the widgets will
* be shown:
*
* {@preformat java
* Boolean.getBoolean(SHOW_PROPERTY_KEY);
* }
*
* The value of this property key is {@value}.
*
* @see org.geotoolkit.test.TestBase#VERBOSE_KEY
*/
public static final String SHOW_PROPERTY_KEY = "org.geotoolkit.test.gui.show";
/**
* The type of the widget being tested.
*/
final Class<T> testing;
/**
* Number of invocation of {@link #create(int)} to perform.
*/
final int numTests;
/**
* Creates a new instance of {@code SwingTestBase} which will invoke
* {@link #create(int)} only once.
*
* @param testing The class being tested.
*/
protected SwingTestBase(final Class<T> testing) {
this(testing, 1);
}
/**
* Creates a new instance of {@code SwingTestBase}.
*
* @param testing The class being tested.
* @param numTests Number of invocation of {@link #create(int)} to perform.
*/
protected SwingTestBase(final Class<T> testing, final int numTests) {
assertTrue(testing.desiredAssertionStatus());
assertTrue(JComponent.class.isAssignableFrom(testing));
assertTrue(numTests >= 1);
this.testing = testing;
this.numTests = numTests;
}
/**
* If the widgets are to be show, prepares the desktop pane which will contain them.
* This method is invoked by JUnit and should not be invoked directly.
*
* @throws HeadlessException If the current environment does not allow the display of widgets.
*/
@BeforeClass
public static synchronized void prepareDesktop() throws HeadlessException {
if (isDisplayEnabled()) {
DesktopPane.prepareDesktop();
}
}
/**
* Returns {@code true} if the display of widgets is enabled.
*
* @return {@code true} if the display of widgets is enabled.
*/
public static boolean isDisplayEnabled() {
return Boolean.getBoolean(SHOW_PROPERTY_KEY);
}
/**
* Creates the widget. The widget is usually of type {@code T}, except if the
* widget has been put in a scroll pane.
* <p>
* This method can return {@code null} if the widget can not be created for a
* raison which is not a test failure, for example if the widget relies on some
* resources which may not be available on the classpath.
*
* @param index Index of the widget being created, from 0 inclusive to the value given
* at construction time, exclusive.
* @return The created widget, or {@code null} if the widget can not be created for
* an acceptable raison.
* @throws Exception If an exception occurred while creating the widget.
*/
protected abstract JComponent create(int index) throws Exception;
/**
* {@linkplain #create() Creates} the widget. If the "{@code org.geotoolkit.showWidgetTests}"
* system property is set to "{@code true}", then the widget will be show as an internal
* frame in the desktop.
*
* @throws Exception If an exception occurred while creating the widget.
*/
@Test
public void display() throws Exception {
final JComponent[] components = new JComponent[numTests];
for (int i=0; i<components.length; i++) {
assumeNotNull(components[i] = create(i));
}
if (!show(this, components)) {
for (int i=0; i<components.length; i++) {
final JComponent component = components[i];
component.setSize(component.getPreferredSize());
component.setVisible(true);
final int width = component.getWidth();
final int height = component.getHeight();
final BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB);
final Graphics2D gr = image.createGraphics();
try {
component.print(gr);
} finally {
gr.dispose();
}
/*
* Optionally save to a file in the current directory, for checking purpose.
* Actually the image is empty if we didn't overridden the Component.paint method,
* so the above check is useful only for widgets doing their own painting like
* ColorRamp.
*/
if (false) {
final File file = new File(DesktopPane.getTitle(component.getClass()) + '-' + i + ".png");
assertTrue(ImageIO.write(image, "png", file));
System.out.println("Image saved in " + file.getAbsolutePath());
}
}
} else {
animate(components);
}
}
/**
* Invoked in the JUnit thread if the widget have been shown. The default implementation
* does nothing. Subclasses can override this method for testing an animation.
*
* @param components The widgets that were created.
* @throws Exception If an exception occurred while animating the widget.
*
* @since 3.07
*/
protected void animate(final JComponent[] components) throws Exception {
}
/**
* Shows the given components, if the test is allowed to display widgets and
* the given component is not null.
*
* @param testCase The test case for which the component is added.
* @param components The components to show, or {@code null} if none.
* @return {@code true} if the component has been shown.
*/
protected static boolean show(final SwingTestBase<?> testCase, final JComponent... components) {
return isDisplayEnabled() && DesktopPane.show(testCase, components);
}
/**
* If a frame has been created, wait for its disposal. This method is invoked by JUnit
* and should not be invoked directly.
*
* @throws InterruptedException If the current thread has been interrupted while
* we were waiting for the frame disposal.
*/
@AfterClass
public static void waitForFrameDisposal() throws InterruptedException {
if (isDisplayEnabled()) {
DesktopPane.waitForFrameDisposal();
}
}
}