changeset 238:72f7c3d2a0c6

Improve GUI tests for MainWindow and ClientConfigurationFrame. Reviewed-by: omajid Review-thread: http://icedtea.classpath.org/pipermail/thermostat/2012-April/000811.html
author Roman Kennke <rkennke@redhat.com>
date Mon, 16 Apr 2012 23:16:07 +0200
parents 2bdcc631a4dc
children 7711b0e95139
files client/pom.xml client/src/main/java/com/redhat/thermostat/client/ui/EdtHelper.java client/src/main/java/com/redhat/thermostat/client/ui/MainWindow.java client/src/test/java/com/redhat/thermostat/client/ui/ClientConfigurationFrameTest.java client/src/test/java/com/redhat/thermostat/client/ui/EdtHelperTest.java client/src/test/java/com/redhat/thermostat/client/ui/MainWindowTest.java
diffstat 6 files changed, 366 insertions(+), 34 deletions(-) [+]
line wrap: on
line diff
--- a/client/pom.xml	Mon Apr 16 23:15:52 2012 +0200
+++ b/client/pom.xml	Mon Apr 16 23:16:07 2012 +0200
@@ -94,7 +94,7 @@
       </releases>
       <snapshots>
         <enabled>true</enabled>
-        <updatePolicy>always</updatePolicy>
+        <updatePolicy>never</updatePolicy>
         <checksumPolicy>fail</checksumPolicy>
       </snapshots>
       <id>sonatype-snapshots</id>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/client/src/main/java/com/redhat/thermostat/client/ui/EdtHelper.java	Mon Apr 16 23:16:07 2012 +0200
@@ -0,0 +1,94 @@
+/*
+ * Copyright 2012 Red Hat, Inc.
+ *
+ * This file is part of Thermostat.
+ *
+ * Thermostat is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published
+ * by the Free Software Foundation; either version 2, or (at your
+ * option) any later version.
+ *
+ * Thermostat 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
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Thermostat; see the file COPYING.  If not see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * Linking this code with other modules is making a combined work
+ * based on this code.  Thus, the terms and conditions of the GNU
+ * General Public License cover the whole combination.
+ *
+ * As a special exception, the copyright holders of this code give
+ * you permission to link this code with independent modules to
+ * produce an executable, regardless of the license terms of these
+ * independent modules, and to copy and distribute the resulting
+ * executable under terms of your choice, provided that you also
+ * meet, for each linked independent module, the terms and conditions
+ * of the license of that module.  An independent module is a module
+ * which is not derived from or based on this code.  If you modify
+ * this code, you may extend this exception to your version of the
+ * library, but you are not obligated to do so.  If you do not wish
+ * to do so, delete this exception statement from your version.
+ */
+
+package com.redhat.thermostat.client.ui;
+
+import java.awt.EventQueue;
+import java.lang.reflect.InvocationTargetException;
+import java.util.concurrent.Callable;
+
+public class EdtHelper {
+
+    private static class CallableException extends RuntimeException {
+
+        private CallableException(Exception ex) {
+            super(ex);
+        }
+        
+    }
+
+    private static class CallableWrapper<T> implements Runnable {
+
+        private Callable<T> callable;
+        private T result;
+
+        private CallableWrapper(Callable<T> c) {
+            callable = c;
+        }
+
+        @Override
+        public void run() {
+            try {
+                result = callable.call();
+            } catch (Exception ex) {
+                throw new CallableException(ex);
+            }
+        }
+
+        private T getResult() {
+            return result;
+        }
+    }
+
+    public void callAndWait(Runnable r) throws InvocationTargetException, InterruptedException {
+        if (EventQueue.isDispatchThread()) {
+            try {
+                r.run();
+            } catch (Exception ex) {
+                throw new InvocationTargetException(ex);
+            }
+        } else {
+            EventQueue.invokeAndWait(r);
+        }
+    }
+
+    public <T> T callAndWait(Callable<T> c) throws InvocationTargetException, InterruptedException {
+        CallableWrapper<T> w = new CallableWrapper<>(c);
+        callAndWait(w);
+        return w.getResult();
+    }
+
+}
--- a/client/src/main/java/com/redhat/thermostat/client/ui/MainWindow.java	Mon Apr 16 23:15:52 2012 +0200
+++ b/client/src/main/java/com/redhat/thermostat/client/ui/MainWindow.java	Mon Apr 16 23:16:07 2012 +0200
@@ -49,9 +49,11 @@
 import java.awt.event.WindowAdapter;
 import java.awt.event.WindowEvent;
 import java.io.PrintStream;
+import java.lang.reflect.InvocationTargetException;
 import java.util.Collection;
 import java.util.Collections;
 import java.util.List;
+import java.util.concurrent.Callable;
 import java.util.concurrent.ExecutionException;
 
 import javax.swing.BorderFactory;
@@ -566,13 +568,39 @@
 
     @Override
     public void showMainWindow() {
-        pack();
-        setVisible(true);
+        try {
+            new EdtHelper().callAndWait(new Runnable() {
+                
+                @Override
+                public void run() {
+                    pack();
+                    setVisible(true);
+                }
+            });
+        } catch (InvocationTargetException e) {
+            throw new RuntimeException(e);
+        } catch (InterruptedException e) {
+            Thread.currentThread().interrupt();
+        }
     }
 
     @Override
     public String getHostVmTreeFilter() {
-        return searchField.getText();
+        try {
+            return new EdtHelper().callAndWait(new Callable<String>() {
+
+                @Override
+                public String call() throws Exception {
+                    return searchField.getText();
+                }
+                
+            });
+        } catch (InvocationTargetException e) {
+            throw new RuntimeException(e);
+        } catch (InterruptedException e) {
+            Thread.currentThread().interrupt();
+            return null;
+        }
     }
 
 }
--- a/client/src/test/java/com/redhat/thermostat/client/ui/ClientConfigurationFrameTest.java	Mon Apr 16 23:15:52 2012 +0200
+++ b/client/src/test/java/com/redhat/thermostat/client/ui/ClientConfigurationFrameTest.java	Mon Apr 16 23:16:07 2012 +0200
@@ -39,30 +39,46 @@
 import static org.mockito.Matchers.eq;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.verify;
+import net.java.openjdk.cacio.ctc.junit.CacioFESTRunner;
 
 import org.fest.swing.annotation.GUITest;
+import org.fest.swing.edt.FailOnThreadViolationRepaintManager;
 import org.fest.swing.edt.GuiActionRunner;
 import org.fest.swing.edt.GuiTask;
 import org.fest.swing.fixture.FrameFixture;
 import org.fest.swing.fixture.JButtonFixture;
 import org.junit.After;
 import org.junit.Before;
+import org.junit.BeforeClass;
 import org.junit.Test;
 import org.junit.experimental.categories.Category;
+import org.junit.runner.RunWith;
 
 import com.redhat.thermostat.common.ActionEvent;
 import com.redhat.thermostat.common.ActionListener;
 
+@RunWith(CacioFESTRunner.class)
 public class ClientConfigurationFrameTest {
 
     private ClientConfigurationFrame frame;
     private FrameFixture frameFixture;
     private ActionListener<ClientConfigurationView.Action> l;
 
+    @BeforeClass
+    public static void setUpOnce() {
+        FailOnThreadViolationRepaintManager.install();
+    }
+
     @SuppressWarnings("unchecked") // ActionListener
     @Before
     public void setUp() {
-        frame = new ClientConfigurationFrame();
+        GuiActionRunner.execute(new GuiTask() {
+            
+            @Override
+            protected void executeInEDT() throws Throwable {
+                frame = new ClientConfigurationFrame();
+            }
+        });
         l = mock(ActionListener.class);
         frame.addListener(l);
         frameFixture = new FrameFixture(frame);
@@ -71,6 +87,13 @@
 
     @After
     public void tearDown() {
+        GuiActionRunner.execute(new GuiTask() {
+            @Override
+            protected void executeInEDT() throws Throwable {
+                frame.hideDialog();
+            }
+        });
+
         frameFixture.cleanUp();
         frame.removeListener(l);
         frame = null;
@@ -85,13 +108,6 @@
         JButtonFixture button = frameFixture.button("ok");
         button.click();
 
-        GuiActionRunner.execute(new GuiTask() {
-            @Override
-            protected void executeInEDT() throws Throwable {
-                frame.hideDialog();
-            }
-        });
-
         verify(l).actionPerformed(eq(new ActionEvent<>(frame, ClientConfigurationView.Action.CLOSE_ACCEPT)));
 
 
@@ -105,13 +121,6 @@
         JButtonFixture button = frameFixture.button("cancel");
         button.click();
 
-        GuiActionRunner.execute(new GuiTask() {
-            @Override
-            protected void executeInEDT() throws Throwable {
-                frame.hideDialog();
-            }
-        });
-
         verify(l).actionPerformed(eq(new ActionEvent<>(frame, ClientConfigurationView.Action.CLOSE_CANCEL)));
 
     }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/client/src/test/java/com/redhat/thermostat/client/ui/EdtHelperTest.java	Mon Apr 16 23:16:07 2012 +0200
@@ -0,0 +1,194 @@
+/*
+ * Copyright 2012 Red Hat, Inc.
+ *
+ * This file is part of Thermostat.
+ *
+ * Thermostat is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published
+ * by the Free Software Foundation; either version 2, or (at your
+ * option) any later version.
+ *
+ * Thermostat 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
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Thermostat; see the file COPYING.  If not see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * Linking this code with other modules is making a combined work
+ * based on this code.  Thus, the terms and conditions of the GNU
+ * General Public License cover the whole combination.
+ *
+ * As a special exception, the copyright holders of this code give
+ * you permission to link this code with independent modules to
+ * produce an executable, regardless of the license terms of these
+ * independent modules, and to copy and distribute the resulting
+ * executable under terms of your choice, provided that you also
+ * meet, for each linked independent module, the terms and conditions
+ * of the license of that module.  An independent module is a module
+ * which is not derived from or based on this code.  If you modify
+ * this code, you may extend this exception to your version of the
+ * library, but you are not obligated to do so.  If you do not wish
+ * to do so, delete this exception statement from your version.
+ */
+
+package com.redhat.thermostat.client.ui;
+
+import static org.junit.Assert.assertSame;
+import static org.junit.Assert.assertTrue;
+
+import java.awt.EventQueue;
+import java.lang.reflect.InvocationTargetException;
+import java.util.concurrent.Callable;
+
+import javax.swing.SwingUtilities;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+
+public class EdtHelperTest {
+
+    private class ExceptionCallable implements Callable<Object>  {
+
+        @Override
+        public Object call() throws Exception {
+            throw new Exception("fluff");
+        }
+        
+    }
+
+    private class ResultCallable implements Callable<Object> {
+    
+        private Object result;
+        private ResultCallable(Object r) {
+            result = r;
+        }
+        @Override
+        public Object call() throws Exception {
+            // By waiting here, we make sure the EDTHelper actually waits for the call.
+            try {
+                Thread.sleep(100);
+            } catch (InterruptedException e) {
+                e.printStackTrace();
+            }
+            if (EventQueue.isDispatchThread()) {
+                calledOnEDT = true;
+            }
+            return result;
+        }
+        
+    }
+
+    private class TestRunnable implements Runnable {
+
+        @Override
+        public void run() {
+            // By waiting here, we make sure the EDTHelper actually waits for the call.
+            try {
+                Thread.sleep(100);
+            } catch (InterruptedException e) {
+                Thread.currentThread().interrupt();
+            }
+            if (EventQueue.isDispatchThread()) {
+                calledOnEDT = true;
+            }
+        }
+        
+    }
+
+    private volatile boolean calledOnEDT;
+
+    @Before
+    public void setUp() {
+        calledOnEDT = false;
+    }
+
+    @After
+    public void tearDown() {
+        calledOnEDT = false;
+    }
+
+    @Test
+    public void testCallRunnableFromNonEDT() throws InvocationTargetException, InterruptedException {
+        Runnable r = new TestRunnable();
+        new EdtHelper().callAndWait(r);
+        assertTrue(calledOnEDT);
+    }
+
+    @Test
+    public void testCallRunnableFromEDT() throws InvocationTargetException, InterruptedException {
+        final Runnable r = new TestRunnable();
+        SwingUtilities.invokeAndWait(new Runnable() {
+            
+            @Override
+            public void run() {
+                try {
+                    new EdtHelper().callAndWait(r);
+                } catch (InvocationTargetException e) {
+                    throw new RuntimeException(e);
+                } catch (InterruptedException e) {
+                    Thread.currentThread().interrupt();
+                }
+            }
+        });
+        assertTrue(calledOnEDT);
+    }
+
+    @Test
+    public void testCallCallableFromNoEDT() throws InvocationTargetException, InterruptedException {
+        final Object expected = new Object();
+        Callable<Object> c = new ResultCallable(expected);
+        Object result = new EdtHelper().callAndWait(c);
+        assertTrue(calledOnEDT);
+        assertSame(expected, result);
+    }
+
+    @Test
+    public void testCallCallableFromEDT() throws InvocationTargetException, InterruptedException {
+        final Object expected = new Object();
+        final Callable<Object> c = new ResultCallable(expected);
+        final Object[] result = new Object[1];
+        SwingUtilities.invokeAndWait(new Runnable() {
+            
+            @Override
+            public void run() {
+                try {
+                    result[0] = new EdtHelper().callAndWait(c);
+                } catch (InvocationTargetException | InterruptedException e) {
+                    throw new RuntimeException();
+                }
+            }
+        });
+        assertTrue(calledOnEDT);
+        assertSame(expected, result[0]);
+    }
+
+    @Test(expected=InvocationTargetException.class)
+    public void testCallCallableFromNoEDTThrowingException() throws InvocationTargetException, InterruptedException {
+        Callable<Object> c = new ExceptionCallable();
+        new EdtHelper().callAndWait(c);
+    }
+
+    @Test
+    public void testCallCallableFromEDTThrowingException() throws InvocationTargetException, InterruptedException {
+        final boolean[] exceptionThrown = new boolean[1];
+        final Callable<Object> c = new ExceptionCallable();
+        SwingUtilities.invokeAndWait(new Runnable() {
+            
+            @Override
+            public void run() {
+                try {
+                    new EdtHelper().callAndWait(c);
+                } catch (InvocationTargetException e) {
+                    exceptionThrown[0] = true;
+                } catch (InterruptedException e) {
+                    Thread.currentThread().interrupt();
+                }
+            }
+        });
+        assertTrue(exceptionThrown[0]);
+    }
+}
--- a/client/src/test/java/com/redhat/thermostat/client/ui/MainWindowTest.java	Mon Apr 16 23:15:52 2012 +0200
+++ b/client/src/test/java/com/redhat/thermostat/client/ui/MainWindowTest.java	Mon Apr 16 23:16:07 2012 +0200
@@ -41,16 +41,18 @@
 import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
+import net.java.openjdk.cacio.ctc.junit.CacioFESTRunner;
 
 import org.fest.swing.annotation.GUITest;
+import org.fest.swing.edt.FailOnThreadViolationRepaintManager;
 import org.fest.swing.edt.GuiActionRunner;
 import org.fest.swing.edt.GuiTask;
 import org.fest.swing.fixture.FrameFixture;
 import org.fest.swing.fixture.JMenuItemFixture;
 import org.fest.swing.fixture.JTextComponentFixture;
-import org.fest.swing.junit.v4_5.runner.GUITestRunner;
 import org.junit.After;
 import org.junit.Before;
+import org.junit.BeforeClass;
 import org.junit.Test;
 import org.junit.experimental.categories.Category;
 import org.junit.runner.RunWith;
@@ -62,13 +64,18 @@
 import com.redhat.thermostat.common.ActionEvent;
 import com.redhat.thermostat.common.ActionListener;
 
-@RunWith(GUITestRunner.class)
+@RunWith(CacioFESTRunner.class)
 public class MainWindowTest {
 
     private FrameFixture frameFixture;
     private MainWindow window;
     private ActionListener<MainView.Action> l;
 
+    @BeforeClass
+    public static void setUpOnce() {
+        FailOnThreadViolationRepaintManager.install();
+    }
+
     @SuppressWarnings("unchecked") // mock(ActionListener.class)
     @Before
     public void setUp() {
@@ -77,12 +84,18 @@
         when(summaryPanelFacade.getTotalMonitoredHosts()).thenReturn(new ChangeableText("totalConnectedAgents"));
         when(summaryPanelFacade.getTotalMonitoredVms()).thenReturn(new ChangeableText("connectedVms"));
 
-        UiFacadeFactory uiFacadeFactory = mock(UiFacadeFactory.class);
+        final UiFacadeFactory uiFacadeFactory = mock(UiFacadeFactory.class);
         when(uiFacadeFactory.getSummaryPanel()).thenReturn(summaryPanelFacade);
 
-        window = new MainWindow(uiFacadeFactory);
-        l = mock(ActionListener.class);
-        window.addActionListener(l);
+        GuiActionRunner.execute(new GuiTask() {
+            
+            @Override
+            protected void executeInEDT() throws Throwable {
+                window = new MainWindow(uiFacadeFactory);
+                l = mock(ActionListener.class);
+                window.addActionListener(l);
+            }
+        });
 
         frameFixture = new FrameFixture(window);
     }
@@ -119,13 +132,7 @@
     @Category(GUITest.class)
     @Test
     public void verifyShowMainWindowShowsWindow() {
-        GuiActionRunner.execute(new GuiTask() {
-
-            @Override
-            protected void executeInEDT() throws Throwable {
-                window.showMainWindow();
-            }
-        });
+        window.showMainWindow();
         frameFixture.requireVisible();
     }
 
@@ -160,8 +167,8 @@
         frameFixture.show();
         JTextComponentFixture hostVMTreeFilterField = frameFixture.textBox("hostVMTreeFilter");
         hostVMTreeFilterField.enterText("test");
-
-        assertEquals("test", window.getHostVmTreeFilter());
+        String actual = window.getHostVmTreeFilter();
+        assertEquals("test", actual);
     }