mirror of
https://github.com/hneemann/Digital.git
synced 2025-09-18 17:34:43 -04:00
added some more gui test cases
This commit is contained in:
parent
9b51dc50b0
commit
30b7e69870
@ -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.
|
||||
|
@ -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
|
||||
*/
|
||||
|
@ -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));
|
||||
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
61
src/test/resources/dig/manualError/11_editTest.dig
Normal file
61
src/test/resources/dig/manualError/11_editTest.dig
Normal 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>
|
Loading…
x
Reference in New Issue
Block a user