added some more gui test cases

This commit is contained in:
hneemann 2018-01-20 15:48:06 +01:00
parent 9b51dc50b0
commit 30b7e69870
6 changed files with 300 additions and 30 deletions

View File

@ -193,5 +193,5 @@ If you want to build Digital from the source code:
* Before you send a pull request, make sure that at least `mvn install` runs without errors.
* Don't introduce new findbugs issues.
* Try to keep the test coverage high. The target is 80% test coverage at all non GUI components.
* So far, there are only a few GUI tests, so that the overall test coverage is only slightly below 70%.
* So far, there are only a few GUI tests, so that the overall test coverage is only slightly above 70%.
Try to keep the amount of untested GUI code low.

View File

@ -285,6 +285,13 @@ public class ElementLibrary implements Iterable<ElementLibrary.ElementContainer>
this.shapeFactory = shapeFactory;
}
/**
* @return the shape factory
*/
public ShapeFactory getShapeFactory() {
return shapeFactory;
}
/**
* @return the node with the custom elements
*/

View File

@ -19,6 +19,7 @@ import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.io.IOException;
import java.util.HashSet;
/**
* Dialog to show and edit the testing data source.
@ -46,6 +47,10 @@ public class TestCaseDescriptionDialog extends JDialog {
JTextArea text = new JTextArea(data.getDataString(), 30, 50);
text.setFont(new Font(Font.MONOSPACED, Font.PLAIN, Screen.getInstance().getFontSize()));
HashSet<AWTKeyStroke> set = new HashSet<>(text.getFocusTraversalKeys(KeyboardFocusManager.FORWARD_TRAVERSAL_KEYS));
set.add(KeyStroke.getKeyStroke("F1"));
text.setFocusTraversalKeys(KeyboardFocusManager.FORWARD_TRAVERSAL_KEYS, set);
JScrollPane scrollPane = new JScrollPane(text);
getContentPane().add(scrollPane);
scrollPane.setRowHeaderView(new TextLineNumber(text, 3));

View File

@ -2,8 +2,10 @@ package de.neemann.digital.integration;
import de.neemann.digital.draw.elements.Circuit;
import de.neemann.digital.gui.Main;
import jdk.nashorn.internal.scripts.JD;
import junit.framework.Assert;
import javax.sql.rowset.JdbcRowSet;
import javax.swing.FocusManager;
import javax.swing.*;
import javax.swing.text.JTextComponent;
@ -14,6 +16,7 @@ import java.io.IOException;
import java.util.ArrayList;
import static java.awt.event.InputEvent.*;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.fail;
public class GuiTester {
@ -79,7 +82,7 @@ public class GuiTester {
for (int i = 0; i < s.length(); i++) {
final char ch = s.charAt(i);
int code = KeyEvent.getExtendedKeyCodeForChar(ch);
if (ch == '/') {
if (ch == '/' || Character.isUpperCase(ch)) {
gt.keyPress(KeyEvent.VK_SHIFT);
gt.keyPress(code);
gt.keyRelease(code);
@ -114,6 +117,11 @@ public class GuiTester {
return this;
}
public GuiTester mouseClick(int x, int y, int buttons) {
add((gs) -> gs.mouseClickNow(x, y, buttons));
return this;
}
private boolean isDisplay() {
final boolean isDisplay = !GraphicsEnvironment.isHeadless();
if (!isDisplay)
@ -194,6 +202,28 @@ public class GuiTester {
if ((mod & ALT_DOWN_MASK) != 0) keyRelease(KeyEvent.VK_ALT);
}
/**
* Clicks the mouse
*
* @param x the x coordinate relative to the topmost window
* @param y the x coordinate relative to the topmost window
* @param buttons the button mask
*/
public void mouseClickNow(int x, int y, int buttons) {
Container activeWindow = FocusManager.getCurrentManager().getActiveWindow();
Point p = new Point(x, y);
if (activeWindow instanceof JDialog)
activeWindow = ((JDialog) activeWindow).getContentPane();
else if (activeWindow instanceof JFrame)
activeWindow = ((JFrame) activeWindow).getContentPane();
SwingUtilities.convertPointToScreen(p, activeWindow);
robot.mouseMove(p.x, p.y);
robot.mousePress(buttons);
robot.mouseRelease(buttons);
}
private void keyPress(int keyCode) {
robot.keyPress(keyCode);
}
@ -248,54 +278,113 @@ public class GuiTester {
+ activeWindow.getClass().getSimpleName()
+ ">",
expectedClass.isAssignableFrom(activeWindow.getClass()));
checkWindow((W) activeWindow);
checkWindow(guiTester, (W) activeWindow);
}
/**
* Is called if the expected window was found.
* Override this method to implement own tests of the window found.
*
* @param window the found window of expected type
* @param guiTester the GuiTester
* @param window the found window of expected type
*/
public void checkWindow(W window) {
public void checkWindow(GuiTester guiTester, W window) {
}
}
/**
* Traverses all the components in the topmost window.
*/
public static abstract class ComponentTraverse<W extends Window> extends WindowCheck<W> {
/**
* creates a new instance
*/
public ComponentTraverse(Class<W> expected) {
super(expected);
}
@Override
public void checkWindow(GuiTester guiTester, W dialog) {
traverseComponents(dialog);
}
void traverseComponents(Container cp) {
for (int i = 0; i < cp.getComponentCount(); i++) {
Component component = cp.getComponent(i);
visit(component);
if (component instanceof Container) {
traverseComponents((Container) component);
}
}
}
public abstract void visit(Component component);
}
/**
* Checks if the topmost dialog contains the given strings.
*/
public static class CheckDialogText extends WindowCheck<JDialog> {
public static class CheckTextInWindow<W extends Window> extends ComponentTraverse<W> {
private final String[] expected;
private StringBuilder text;
/**
* Checks if the topmost dialog contains the given strings.
*
* @param expected test fails if one of the strings is missing
*/
public CheckDialogText(String... expected) {
super(JDialog.class);
public CheckTextInWindow(Class<W> expectedClass, String... expected) {
super(expectedClass);
this.expected = expected;
text = new StringBuilder();
}
@Override
public void checkWindow(JDialog dialog) {
StringBuilder text = new StringBuilder();
collectText(dialog.getContentPane(), text);
public void checkWindow(GuiTester guiTester, W window) {
super.checkWindow(guiTester, window);
String t = text.toString();
for (String e : expected)
Assert.assertTrue(t + " does not contain " + e, t.contains(e));
Assert.assertTrue("<" + t + "> does not contain <" + e + ">", t.contains(e));
}
void collectText(Container cp, StringBuilder text) {
for (int i = 0; i < cp.getComponentCount(); i++) {
Component component = cp.getComponent(i);
if (component instanceof JLabel) {
text.append(((JLabel) component).getText());
} else if (component instanceof JTextComponent) {
text.append((((JTextComponent) component).getText()));
} else if (component instanceof Container) {
collectText((Container) component, text);
}
@Override
public void visit(Component component) {
if (component instanceof JTabbedPane) {
JTabbedPane t = ((JTabbedPane) component);
for (int j = 0; j < t.getTabCount(); j++)
text.append(t.getTitleAt(j));
} else if (component instanceof JLabel) {
text.append(((JLabel) component).getText());
} else if (component instanceof JTextComponent) {
text.append((((JTextComponent) component).getText()));
}
}
}
public static class CheckTableRows<W extends Window> extends ComponentTraverse<W> {
private final int expectedRows;
private int rows;
private int tableCount;
public CheckTableRows(Class<W> expectedClass, int expectedRows) {
super(expectedClass);
this.expectedRows = expectedRows;
}
@Override
public void checkWindow(GuiTester guiTester, W window) {
super.checkWindow(guiTester, window);
assertEquals("row count does not match", expectedRows, rows);
assertEquals("only one table allowed", 1, tableCount);
}
@Override
public void visit(Component component) {
if (component instanceof JTable) {
rows = ((JTable) component).getModel().getRowCount();
tableCount++;
}
}
}
@ -311,4 +400,5 @@ public class GuiTester {
activeWindow.dispose();
}
}
}

View File

@ -2,17 +2,28 @@ package de.neemann.digital.integration;
import de.neemann.digital.analyse.expression.Expression;
import de.neemann.digital.draw.elements.Circuit;
import de.neemann.digital.draw.elements.VisualElement;
import de.neemann.digital.draw.elements.Wire;
import de.neemann.digital.draw.graphics.GraphicMinMax;
import de.neemann.digital.draw.graphics.Vector;
import de.neemann.digital.gui.Main;
import de.neemann.digital.gui.components.karnaugh.KarnaughMapDialog;
import de.neemann.digital.gui.components.table.AllSolutionsDialog;
import de.neemann.digital.gui.components.table.ExpressionListenerStore;
import de.neemann.digital.gui.components.table.TableDialog;
import de.neemann.digital.gui.components.testing.ValueTableDialog;
import de.neemann.digital.lang.Lang;
import de.neemann.gui.ErrorMessage;
import junit.framework.TestCase;
import javax.swing.*;
import java.awt.*;
import java.awt.event.InputEvent;
import java.io.File;
import java.util.List;
import static de.neemann.digital.draw.shapes.GenericShape.SIZE;
/**
* These tests are excluded from the maven build because gui tests are sometimes fragile.
* They may not behave as expected on all systems.
@ -109,7 +120,7 @@ public class TestInGUI extends TestCase {
.delay(500)
.add(new GuiTester.WindowCheck<KarnaughMapDialog>(KarnaughMapDialog.class) {
@Override
public void checkWindow(KarnaughMapDialog kMapDialog) {
public void checkWindow(GuiTester guiTester, KarnaughMapDialog kMapDialog) {
List<ExpressionListenerStore.Result> res = kMapDialog.getResults();
assertEquals(1, res.size());
Expression r = res.get(0).getExpression();
@ -120,7 +131,7 @@ public class TestInGUI extends TestCase {
.press("F2")
.add(new GuiTester.WindowCheck<Main>(Main.class) {
@Override
public void checkWindow(Main main) {
public void checkWindow(GuiTester guiTester, Main main) {
Circuit c = main.getCircuitComponent().getCircuit();
assertEquals(4, c.getElements().size());
}
@ -143,7 +154,7 @@ public class TestInGUI extends TestCase {
.delay(500)
.add(new GuiTester.WindowCheck<Main>(Main.class) {
@Override
public void checkWindow(Main main) {
public void checkWindow(GuiTester guiTester, Main main) {
Circuit c = main.getCircuitComponent().getCircuit();
assertEquals(7, c.getElements().size());
}
@ -224,9 +235,9 @@ public class TestInGUI extends TestCase {
.press("DOWN", 2)
.press("ENTER")
.delay(500)
.add(new GuiTester.WindowCheck<AllSolutionsDialog>(AllSolutionsDialog.class){
.add(new GuiTester.WindowCheck<AllSolutionsDialog>(AllSolutionsDialog.class) {
@Override
public void checkWindow(AllSolutionsDialog asd) {
public void checkWindow(GuiTester guiTester, AllSolutionsDialog asd) {
asd.getParent().requestFocus();
}
})
@ -260,6 +271,25 @@ public class TestInGUI extends TestCase {
.execute();
}
public void testDraw() {
new GuiTester()
.add(new DrawCircuit("../../main/dig/sequential/JK-MS.dig"))
.press("F8")
.delay(500)
.add(new GuiTester.CheckTextInWindow<>(ValueTableDialog.class, "ok"))
.add(new GuiTester.CheckTableRows<>(ValueTableDialog.class, 8))
.add(new GuiTester.CloseTopMost())
.press("control typed z",65)
.delay(1000)
.press("control typed y",65)
.press("F8")
.delay(500)
.add(new GuiTester.CheckTextInWindow<>(ValueTableDialog.class, "ok"))
.add(new GuiTester.CheckTableRows<>(ValueTableDialog.class, 8))
.add(new GuiTester.CloseTopMost())
.execute();
}
public void testHardware() {
new GuiTester("dig/manualError/10_hardware.dig")
.press("F9")
@ -278,7 +308,28 @@ public class TestInGUI extends TestCase {
.typeTempFile("test")
.press("ENTER")
.delay(3000)
.add(new GuiTester.CheckDialogText("Design fits successfully"))
.add(new GuiTester.CheckTextInWindow<>(JDialog.class, "Design fits successfully"))
.add(new GuiTester.CloseTopMost())
.add(new GuiTester.CloseTopMost())
.execute();
}
public void testTestEditor() {
new GuiTester("dig/manualError/11_editTest.dig")
.mouseClick(200, 200, InputEvent.BUTTON3_MASK)
.type("testIdentzz")
.press("TAB")
.press("SPACE")
.type("A B C\n0 0 0\n0 1 0\n1 0 0\n1 1 1")
.press("F1")
.press("TAB")
.press("SPACE")
.press("TAB", 4)
.press("SPACE")
.press("F8")
.delay(500)
.add(new GuiTester.CheckTextInWindow<>(ValueTableDialog.class, "testIdentzz", "ok"))
.add(new GuiTester.CheckTableRows<>(ValueTableDialog.class, 4))
.add(new GuiTester.CloseTopMost())
.add(new GuiTester.CloseTopMost())
.execute();
@ -293,7 +344,7 @@ public class TestInGUI extends TestCase {
}
@Override
public void checkWindow(ErrorMessage.ErrorDialog errorDialog) {
public void checkWindow(GuiTester guiTester, ErrorMessage.ErrorDialog errorDialog) {
String errorMessage = errorDialog.getErrorMessage();
for (String e : expected)
assertTrue(errorMessage + " does not contain " + e, errorMessage.contains(e));
@ -309,7 +360,7 @@ public class TestInGUI extends TestCase {
}
@Override
public void checkWindow(TableDialog td) {
public void checkWindow(GuiTester guiTester, TableDialog td) {
ExpressionListenerStore exp = td.getLastGeneratedExpressions();
assertEquals(1, exp.getResults().size());
Expression res = exp.getResults().get(0).getExpression();
@ -335,4 +386,60 @@ public class TestInGUI extends TestCase {
}
}
}
private class DrawCircuit extends GuiTester.WindowCheck<Main> {
private final String filename;
public DrawCircuit(String filename) {
super(Main.class);
this.filename = filename;
}
@Override
public void checkWindow(GuiTester guiTester, Main main) {
File file = new File(Resources.getRoot(), filename);
try {
Circuit circuit = Circuit.loadCircuit(file, main.getCircuitComponent().getLibrary().getShapeFactory());
int xMin = Integer.MAX_VALUE;
int yMin = Integer.MAX_VALUE;
for (Wire w : circuit.getWires()) {
if (w.p1.x < xMin) xMin = w.p1.x;
if (w.p2.x < xMin) xMin = w.p2.x;
if (w.p1.y < yMin) yMin = w.p1.y;
if (w.p2.y < yMin) yMin = w.p2.y;
}
Point loc = main.getCircuitComponent().getLocation();
xMin -= loc.x + SIZE * 5;
yMin -= loc.y + SIZE * 2;
for (Wire w : circuit.getWires()) {
guiTester.mouseClickNow(w.p1.x - xMin, w.p1.y - yMin, InputEvent.BUTTON1_MASK);
Thread.sleep(100);
if (w.p1.x != w.p2.x && w.p1.y != w.p2.y)
guiTester.typeNow("typed d");
guiTester.mouseClickNow(w.p2.x - xMin, w.p2.y - yMin, InputEvent.BUTTON1_MASK);
guiTester.mouseClickNow(w.p2.x - xMin, w.p2.y - yMin, InputEvent.BUTTON3_MASK);
Thread.sleep(100);
}
for (VisualElement v : circuit.getElements()) {
Vector pos = v.getPos();
v.setPos(new Vector(0, 0));
final GraphicMinMax minMax = v.getMinMax(false);
pos = pos.add(minMax.getMax());
main.getCircuitComponent().setPartToInsert(v);
guiTester.mouseClickNow(pos.x - xMin, pos.y - yMin, InputEvent.BUTTON1_MASK);
Thread.sleep(200);
}
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}
}

View File

@ -0,0 +1,61 @@
<?xml version="1.0" encoding="utf-8"?>
<circuit>
<version>1</version>
<attributes/>
<visualElements>
<visualElement>
<elementName>Testcase</elementName>
<elementAttributes/>
<pos x="400" y="100"/>
</visualElement>
<visualElement>
<elementName>And</elementName>
<elementAttributes/>
<pos x="500" y="200"/>
</visualElement>
<visualElement>
<elementName>In</elementName>
<elementAttributes>
<entry>
<string>Label</string>
<string>A</string>
</entry>
</elementAttributes>
<pos x="480" y="200"/>
</visualElement>
<visualElement>
<elementName>In</elementName>
<elementAttributes>
<entry>
<string>Label</string>
<string>B</string>
</entry>
</elementAttributes>
<pos x="480" y="240"/>
</visualElement>
<visualElement>
<elementName>Out</elementName>
<elementAttributes>
<entry>
<string>Label</string>
<string>C</string>
</entry>
</elementAttributes>
<pos x="580" y="220"/>
</visualElement>
</visualElements>
<wires>
<wire>
<p1 x="480" y="240"/>
<p2 x="500" y="240"/>
</wire>
<wire>
<p1 x="480" y="200"/>
<p2 x="500" y="200"/>
</wire>
<wire>
<p1 x="560" y="220"/>
<p2 x="580" y="220"/>
</wire>
</wires>
</circuit>