/*
* GeoTools - The Open Source Java GIS Toolkit
* http://geotools.org
*
* (C) 2011, Open Source Geospatial Foundation (OSGeo)
*
* 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.geotools.swing.dialog;
import java.awt.AWTEvent;
import java.awt.Dialog;
import java.awt.Toolkit;
import java.awt.datatransfer.Clipboard;
import java.awt.datatransfer.DataFlavor;
import java.io.ByteArrayOutputStream;
import java.util.Arrays;
import java.util.concurrent.Callable;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.Executors;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import java.util.logging.Formatter;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.logging.SimpleFormatter;
import java.util.logging.StreamHandler;
import javax.swing.JTextArea;
import org.fest.swing.fixture.DialogFixture;
import org.fest.swing.fixture.JTextComponentFixture;
import org.geotools.util.logging.Logging;
import org.geotools.swing.dialog.JTextReporter.Connection;
import org.geotools.swing.testutils.GraphicsTestBase;
import org.geotools.swing.testutils.GraphicsTestRunner;
import org.geotools.swing.testutils.WindowActivatedListener;
import org.junit.After;
import org.junit.Before;
import org.junit.Ignore;
import org.junit.Test;
import org.junit.runner.RunWith;
import static org.junit.Assert.*;
/**
* Tests for {@linkplain JTextReporter}.
* <p>
* This test class uses an {@linkplain ExecutorService} to launch the dialog which
* avoids a deadlock between the dialog waiting for its Connection object and this class
* waiting for the dialog to show up on the event thread.
*
* @author Michael Bedward
* @since 8.0
* @source $URL$
* @version $Id$
*/
@RunWith(GraphicsTestRunner.class)
public class JTextReporterTest extends GraphicsTestBase<Dialog> {
private static final Class<? extends Dialog> DIALOG_CLASS = JTextReporter.TextDialog.class;
private static final long LISTENER_TIMEOUT = 1000;
private static final String TITLE = "Test text reporter";
private static final String[] TEXT = {
"A thing is called finite after its kind,",
"when it can be limited by another thing of the same nature;",
"for instance, a body is called finite because we always",
"conceive another greater body.",
"So, also, a thought is limited by another thought,",
"but a body is not limited by thought, nor a thought by body"
};
private static final ExecutorService executor = Executors.newSingleThreadExecutor();
private WindowActivatedListener listener;
private StreamHandler handler;
private ByteArrayOutputStream out;
@Before
public void setup() {
listener = new WindowActivatedListener(DIALOG_CLASS);
Toolkit.getDefaultToolkit().addAWTEventListener(listener, AWTEvent.WINDOW_EVENT_MASK);
}
@After
public void cleanup() {
Toolkit.getDefaultToolkit().removeAWTEventListener(listener);
}
@Test
public void showDefaultDialog() throws Exception {
showDialog(TITLE);
assertEquals(TITLE, windowFixture.component().getTitle());
// check text area is present and correct size
JTextComponentFixture textBox = windowFixture.textBox();
assertTrue(textBox.component() instanceof JTextArea);
JTextArea textArea = (JTextArea) textBox.component();
assertEquals(JTextReporter.DEFAULT_TEXTAREA_ROWS, textArea.getRows());
assertEquals(JTextReporter.DEFAULT_TEXTAREA_COLS, textArea.getColumns());
// check all buttons are present
getButton("Copy to clipboard");
getButton("Save...");
getButton("Clear");
getButton("Close");
// check dialog state
DialogFixture df = (DialogFixture) windowFixture;
boolean expectModal = (JTextReporter.DEFAULT_FLAGS & JTextReporter.FLAG_MODAL) > 0;
assertEquals(expectModal, df.component().isModal());
boolean expectResizable = (JTextReporter.DEFAULT_FLAGS & JTextReporter.FLAG_RESIZABLE) > 0;
assertEquals(expectResizable, df.component().isResizable());
boolean expectAlwaysOnTop = (JTextReporter.DEFAULT_FLAGS & JTextReporter.FLAG_ALWAYS_ON_TOP) > 0;
assertEquals(expectAlwaysOnTop, df.component().isAlwaysOnTop());
}
@Test
public void nullTitleIsOK() throws Exception {
showDialog(null);
}
@Test
public void nullInitialTextIsOK() throws Exception {
showDialog(TITLE, null);
}
@Test
public void initialText() throws Exception {
showDialog(TITLE, TEXT[0]);
windowFixture.textBox().requireText(TEXT[0]);
}
@Test
public void connectionObjectIsReturned() throws Exception {
Connection conn = null;
try {
conn = showDialog(TITLE).get(DISPLAY_TIMEOUT, TimeUnit.MILLISECONDS);
} catch (TimeoutException ex) {
fail("connection object not returned");
}
assertNotNull(conn);
}
@Test
public void appendText() throws Exception {
Connection conn = showDialog(TITLE).get();
conn.append(TEXT[0]);
windowFixture.robot.waitForIdle();
windowFixture.textBox().requireText(TEXT[0]);
}
@Test
public void appendTextWithFormattedNewlines() throws Exception {
final String text = getConcatenatedText(String.format("%n"));
Connection conn = showDialog(TITLE).get();
conn.append(text);
windowFixture.robot.waitForIdle();
String displayedText = windowFixture.textBox().text();
assertEquals(text, displayedText);
}
@Test
public void appendTextWithEmbeddedNewlines() throws Exception {
final String text = getConcatenatedText("\n");
Connection conn = showDialog(TITLE).get();
conn.append(text);
windowFixture.robot.waitForIdle();
String displayedText = windowFixture.textBox().text();
String regex = "(\\n|\\r)+";
assertEquals(text.replaceAll(regex, "|"), displayedText.replaceAll(regex, "|"));
}
@Test
public void appendNewline() throws Exception {
Connection conn = showDialog(TITLE, TEXT[0]).get();
conn.appendNewline();
conn.appendNewline();
conn.append(TEXT[1]);
conn.appendNewline();
windowFixture.robot.waitForIdle();
String displayedText = windowFixture.textBox().text();
String expectedText = String.format("%s%n%n%s%n", TEXT[0], TEXT[1]);
assertEquals(expectedText, displayedText);
}
@Test
public void appendDefaultSeparatorLine() throws Exception {
Connection conn = showDialog(TITLE, TEXT[0] + "\n").get();
final int N = 10;
conn.appendSeparatorLine(N);
char[] chars = new char[N];
Arrays.fill(chars, JTextReporter.DEFAULT_SEPARATOR_CHAR);
String expected = String.format("%s%n%s%n", TEXT[0], String.valueOf(chars));
String displayedText = windowFixture.textBox().text();
assertEquals(expected, displayedText);
}
@Test
public void appendCustomSeparatorLine() throws Exception {
Connection conn = showDialog(TITLE, TEXT[0] + "\n").get();
final int N = 10;
final char c = '#';
conn.appendSeparatorLine(N, c);
char[] chars = new char[N];
Arrays.fill(chars, c);
String expected = String.format("%s%n%s%n", TEXT[0], String.valueOf(chars));
String displayedText = windowFixture.textBox().text();
assertEquals(expected, displayedText);
}
@Test
public void appendMethodsCanBeChained() throws Exception {
Connection conn = showDialog(TITLE).get();
conn.append(TEXT[0]).appendNewline().append(TEXT[1]);
windowFixture.robot.waitForIdle();
String actual = windowFixture.textBox().text();
String expected = String.format("%s%n%s", TEXT[0], TEXT[1]);
assertEquals(expected, actual);
}
@Test
public void appendAfterDialogDismissedCausesLoggerMessage() throws Exception {
Connection conn = showDialog(TITLE).get();
windowFixture.close();
windowFixture.robot.waitForIdle();
captureLogger();
conn.append("This should not work");
assertLogMessage(Level.SEVERE.toString());
assertLogMessage("appending text to an expired JTextReporter connection");
releaseLogger();
}
@Test
public void getTextFromConnection() throws Exception {
final String text = getConcatenatedText(String.format("%n"));
Connection conn = showDialog(TITLE, text).get();
assertEquals(text, conn.getText());
}
@Test
public void getTextWhenNoneIsDisplayedReturnsEmptyString() throws Exception {
Connection conn = showDialog(TITLE).get();
String text = conn.getText();
assertNotNull(text);
assertTrue(text.length() == 0);
}
@Test
public void listenerInformedWhenTextIsUpdated() throws Exception {
Connection conn = showDialog(TITLE).get();
final CountDownLatch latch = new CountDownLatch(1);
conn.addListener(new TextReporterListener() {
@Override
public void onReporterClosed() {}
@Override
public void onReporterUpdated() {
latch.countDown();
}
});
conn.append(TEXT[0]);
assertTrue( latch.await(LISTENER_TIMEOUT, TimeUnit.MILLISECONDS) );
}
@Test
public void listenerInformedWhenDialogIsClosedViaSystemButton() throws Exception {
Connection conn = showDialog(TITLE).get();
final CountDownLatch latch = new CountDownLatch(1);
conn.addListener(new TextReporterListener() {
@Override
public void onReporterClosed() {
latch.countDown();
}
@Override
public void onReporterUpdated() {}
});
windowFixture.close();
assertTrue( latch.await(LISTENER_TIMEOUT, TimeUnit.MILLISECONDS) );
}
@Test
public void listenerInformedWhenDialogIsClosedViaCloseButton() throws Exception {
Connection conn = showDialog(TITLE).get();
final CountDownLatch latch = new CountDownLatch(1);
conn.addListener(new TextReporterListener() {
@Override
public void onReporterClosed() {
latch.countDown();
}
@Override
public void onReporterUpdated() {}
});
getButton("Close").click();
assertTrue( latch.await(LISTENER_TIMEOUT, TimeUnit.MILLISECONDS) );
}
@Ignore("have to work out how to do this one")
@Test
public void saveTextToFile() throws Exception {
showDialog(TITLE, TEXT[0]).get();
getButton("Save...").click();
}
@Test
public void clearText() throws Exception {
showDialog(TITLE, TEXT[0]).get();
getButton("Clear").click();
windowFixture.robot.waitForIdle();
assertEquals(0, windowFixture.textBox().component().getDocument().getLength());
}
@Test
public void copyTextToClipboard() throws Exception {
final String text = getConcatenatedText(String.format("%n"));
showDialog(TITLE, text).get();
getButton("Copy to clipboard").click();
windowFixture.robot.waitForIdle();
Clipboard clip = Toolkit.getDefaultToolkit().getSystemClipboard();
String clipText = (String) clip.getData(DataFlavor.stringFlavor);
assertEquals(text, clipText);
}
@Test
public void closeDialogViaConnection() throws Exception {
Connection conn = showDialog(TITLE).get();
conn.closeDialog();
windowFixture.robot.waitForIdle();
windowFixture.requireNotVisible();
assertFalse(conn.isOpen());
}
@Test
public void callingCloseDialogTwiceRaisesLogMessage() throws Exception {
Connection conn = showDialog(TITLE).get();
conn.closeDialog();
windowFixture.robot.waitForIdle();
captureLogger();
conn.closeDialog();
assertLogMessage("INFO");
assertLogMessage("connection has expired");
releaseLogger();
}
@Test
public void callingCloseDialogAfterGUICloseRaisesLogMessage() throws Exception {
Connection conn = showDialog(TITLE).get();
getButton("Close").click();
windowFixture.robot.waitForIdle();
captureLogger();
conn.closeDialog();
assertLogMessage("INFO");
assertLogMessage("connection has expired");
releaseLogger();
}
/**
* Launches the dialog in a new thread.
*
* @param dialog title
*
* @return the Future for the dialog task
*/
private Future<JTextReporter.Connection> showDialog(String title) throws Exception {
return showDialog(title, null);
}
/**
* Launches the dialog in a new thread.
*
* @param dialog title
* @param initial text to be displayed
*
* @return the Future for the dialog task
*/
private Future<JTextReporter.Connection> showDialog(final String title,
final String initialText) throws Exception {
Future<Connection> future = executor.submit(
new Callable<JTextReporter.Connection>() {
@Override
public Connection call() throws Exception {
return JTextReporter.showDialog(title, initialText);
}
});
assertComponentDisplayed(DIALOG_CLASS);
windowFixture = listener.getFixture(DISPLAY_TIMEOUT);
return future;
}
private void captureLogger() {
Logger logger = Logging.getLogger("org.geotools.swing");
Formatter formatter = new SimpleFormatter();
out = new ByteArrayOutputStream();
handler = new StreamHandler(out, formatter);
logger.addHandler(handler);
logger.setUseParentHandlers(false);
}
private void releaseLogger() {
Logger logger = Logging.getLogger("org.geotools.swing");
logger.removeHandler(handler);
logger.setUseParentHandlers(true);
}
private void assertLogMessage(String expectedMsg) {
handler.flush();
String logMsg = out.toString();
assertNotNull(logMsg);
assertTrue(logMsg.toLowerCase().contains(expectedMsg.toLowerCase()));
}
private String getConcatenatedText(String concatStr) {
StringBuilder sb = new StringBuilder();
for (int i = 0; i < TEXT.length; i++) {
sb.append(TEXT[i]);
if (i < TEXT.length - 1) {
sb.append(concatStr);
}
}
return sb.toString();
}
}