package org.mypsycho.test.app;
import java.awt.EventQueue;
import java.awt.Window;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.EventObject;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.logging.Level;
import org.fest.swing.core.BasicRobot;
import org.fest.swing.core.Robot;
import org.fest.swing.edt.FailOnThreadViolationRepaintManager;
import org.fest.swing.exception.UnexpectedException;
import org.fest.swing.fixture.DialogFixture;
import org.fest.swing.fixture.FrameFixture;
import org.fest.swing.security.ExitCallHook;
import org.fest.swing.security.NoExitSecurityManagerInstaller;
import org.junit.After;
import org.junit.Assert;
import org.junit.Before;
import org.junit.BeforeClass;
import org.mypsycho.swing.app.Application;
import org.mypsycho.swing.app.ApplicationListener;
import org.mypsycho.swing.app.SingleFrameApplication;
/**
* Framework for testing application.
*
* @author Peransin Nicolas
*/
public class AbstractAppTestContext extends Assert implements ApplicationListener {
protected static final String SYS_EXIT_EXPECTATION = "System.exit";
protected static final String APP_EXIT_EXPECTATION = "Application.exit";
protected static final Object ANY_VALUE_EXPECTED = new Object();
final boolean trapExit;
private NoExitSecurityManagerInstaller noXitInstaller;
Robot robot = null;
Application tested = null;
List<Throwable> appIssues = new ArrayList<Throwable>();
Map<String, List<Object>> expectations = new HashMap<String, List<Object>>();
protected AbstractAppTestContext() {
this(true);
}
protected AbstractAppTestContext(boolean exitHandled) {
trapExit = exitHandled;
}
@BeforeClass
public static void checkEdtViolation() {
// Some check should be performed with AOP.
// See http://weblogs.java.net/blog/alexfromsun/archive/2006/02/debugging_swing.html
// about Debugging Swing by Alexander Potochkin
FailOnThreadViolationRepaintManager.install();
}
@Before
public final void trapSystemExit() {
if (trapExit) {
ExitCallHook hook = new ExitCallHook() {
@Override
public void exitCalled(int status) {
happens(SYS_EXIT_EXPECTATION, status);
}
};
noXitInstaller = NoExitSecurityManagerInstaller.installNoExitSecurityManager(hook);
}
}
@After
public final void untrapSystemExit() {
if (noXitInstaller != null) {
noXitInstaller.uninstall();
}
}
protected synchronized void launch(Application sut, String... args) throws Exception {
assertNull(tested);
tested = sut;
tested.addApplicationListener(this);
tested.launch(true, args);
}
public Application getTested() {
return tested;
}
protected synchronized void expectsExit() {
expects(APP_EXIT_EXPECTATION);
expects(SYS_EXIT_EXPECTATION);
}
protected synchronized void expects(String name) {
expects(name, ANY_VALUE_EXPECTED);
}
protected synchronized void expects(String name, Object value) {
List<Object> expecting = expectations.get(name);
if (expecting == null) {
expecting = new ArrayList<Object>(2);
expectations.put(name, expecting);
}
expecting.add(value);
}
protected synchronized void unexpected(String name, Object value) {
appIssues.add(new UnexpectedException(name + "=" + value, null));
}
protected synchronized void happens(String name, Object value) {
List<Object> expecting = expectations.get(name);
if ((expecting == null) || expecting.isEmpty()) {
unexpected(name, value);
}
Object expected = expecting.remove(0);
if (expected != ANY_VALUE_EXPECTED) {
if (((expected == null) && (value != null))
|| ((expected != null) && !expected.equals(value))) {
unexpected(name, value);
}
}
}
@Override
public synchronized void exceptionThrown(Level level, Object id, String context, Throwable t) {
if (t != null) {
appIssues.add(t);
}
}
@Override
public void willExit(EventObject event) {
happens(APP_EXIT_EXPECTATION, event != null ? event.getSource() : null);
}
@After // if a @After fails, the test is KO, others are called
public synchronized void validate() throws Exception {
String expected = null;
for (Map.Entry<String, List<Object>> expect : expectations.entrySet()) {
if (!expect.getValue().isEmpty()) {
expected = ((expected == null) ? "" : ",") + expect.getKey();
}
}
if (expected != null) {
appIssues.add(new RuntimeException("ExpectedEvents=" + expected));
}
if (appIssues.isEmpty()) {
return;
}
if (appIssues.size() == 1) {
Throwable issue = appIssues.get(0);
if (issue instanceof Error) {
throw (Error) issue;
}
throw (Exception) issue;
}
String msg = "Multiple Exceptions=";
throw new RuntimeException(msg + Arrays.toString(appIssues.toArray()));
}
/**
* Returns the robot.
*
* @return the robot
*/
protected synchronized Robot getRobot() {
if (robot == null) {
robot = BasicRobot.robotWithCurrentAwtHierarchy();
}
return robot;
}
protected FrameFixture frame(String name) {
return new FrameFixture(getRobot(), name);
}
protected FrameFixture mainFrame() {
return frame(SingleFrameApplication.MAIN_FRAME_NAME);
}
protected DialogFixture dialog(String name) {
return new DialogFixture(getRobot(), name);
}
@After
public void tearDown() throws Exception {
if (robot != null) {
robot.cleanUp();
robot = null;
}
tested = null;
EventQueue.invokeAndWait(new Runnable() {
@Override
public void run() {
for (Window w: Window.getWindows()) {
w.dispose();
}
}
});
}
@Override
public boolean canExit(EventObject event) {
return true;
}
@Override
public void beforeCycle(String life, EventObject event) {}
@Override
public void afterCycle(String life, EventObject event, Exception failure) {}
}