excluded GUI test from maven build

This commit is contained in:
hneemann 2018-01-19 17:10:57 +01:00
parent c74883c7eb
commit 086dce3ac4
5 changed files with 410 additions and 160 deletions

View File

@ -7,9 +7,9 @@ work properly.
Preparation Preparation
[ ] Don't use the 'experimental mode'! [ ] Don't use the 'experimental mode'!
[ ] Remove jar library from settings. [ ] Remove jar library from settings.
[ ] Run the GUI tests! (digital/src/test/java/de/neemann/digital/integration/TestInGUI.java)
General General
[ ] execute gui error handling tests in "src/test/resources/dig/manualError"
[ ] build a MS-JK-FF with diagonal wires, use Copy&Paste and 'L' shortcuts [ ] build a MS-JK-FF with diagonal wires, use Copy&Paste and 'L' shortcuts
[ ] save circuit [ ] save circuit
[ ] copy test case from the example (JK-MS.dig) to the created circuit [ ] copy test case from the example (JK-MS.dig) to the created circuit
@ -24,17 +24,11 @@ General
[ ] build a xor with two 7400 [ ] build a xor with two 7400
[ ] analyse the circuit [ ] analyse the circuit
Analysis
[ ] create circuit from the default expression
[ ] analyse circuit, result should to be "Y=B*C"
Truth Tables Truth Tables
[ ] create truth table of "two out of three" function, show KV map [ ] create truth table of "two out of three" function, show KV map
[ ] create truth table of "4 input parity" function, show KV map [ ] create truth table of "4 input parity" function, show KV map
[ ] create 4 bit counter, build circuit with JK-FFs, check if it's running [ ] create 4 bit counter, build circuit with JK-FFs, check if it's running
[ ] build same circuit with D-FFs, check if it's running [ ] build same circuit with D-FFs, check if it's running
[ ] create 8 bit counter and start "ATF1502->TT2/JED" export.
Info "Design fits successfully" should pop up. (Needs WinCupl to be installed!)
Remote Interface Remote Interface
[ ] start and stop "Conways Game of Live" (Conway.asm) via the assembler GUI in the [ ] start and stop "Conways Game of Live" (Conway.asm) via the assembler GUI in the

View File

@ -96,6 +96,9 @@
<artifactId>maven-surefire-plugin</artifactId> <artifactId>maven-surefire-plugin</artifactId>
<version>2.19.1</version> <version>2.19.1</version>
<configuration> <configuration>
<excludes>
<exclude>**/TestInGUI.java</exclude>
</excludes>
<systemProperties> <systemProperties>
<property> <property>
<name>testdata</name> <name>testdata</name>

View File

@ -0,0 +1,220 @@
package de.neemann.digital.integration;
import de.neemann.digital.draw.elements.Circuit;
import de.neemann.digital.gui.Main;
import junit.framework.Assert;
import javax.swing.*;
import javax.swing.text.JTextComponent;
import java.awt.*;
import java.awt.event.KeyEvent;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
public class GuiTester {
private static final long SLEEP_TIME = 200;
private final ArrayList<Runnable> runnableList;
private Main main;
private Robot robot;
private String filename;
public GuiTester() {
this(null);
}
public GuiTester(String filename) {
this.filename = filename;
runnableList = new ArrayList<>();
}
public GuiTester add(Runnable runnable) {
runnableList.add(runnable);
return this;
}
public GuiTester delay(int ms) {
add(() -> Thread.sleep(ms));
return this;
}
public GuiTester press(String key, int n) {
final KeyStroke keyStroke = KeyStroke.getKeyStroke(key);
Assert.assertNotNull("invalid key code <" + key + ">", keyStroke);
int code = keyStroke.getKeyCode();
add((() -> {
for (int i = 0; i < n; i++) {
robot.keyPress(code);
robot.keyRelease(code);
}
}));
return this;
}
public GuiTester press(String key) {
final KeyStroke keyStroke = KeyStroke.getKeyStroke(key);
Assert.assertNotNull("invalid key code <" + key + ">", keyStroke);
return addCode(keyStroke.getKeyCode());
}
public GuiTester press(char c) {
return addCode(KeyEvent.getExtendedKeyCodeForChar(c));
}
public GuiTester pressCTRL(char c) {
int code = KeyEvent.getExtendedKeyCodeForChar(c);
Assert.assertTrue("keycode 0 is not allowed", code != 0);
add(() -> {
robot.keyPress(KeyEvent.VK_CONTROL);
robot.keyPress(code);
robot.keyRelease(code);
robot.keyRelease(KeyEvent.VK_CONTROL);
});
return this;
}
public GuiTester typeTempFile(String name) throws IOException {
File f = File.createTempFile(name, ".dig");
return type(f.getPath());
}
public GuiTester type(String s) {
add((() -> {
for (int i = 0; i < s.length(); i++) {
final char ch = s.charAt(i);
int code = KeyEvent.getExtendedKeyCodeForChar(ch);
if (ch == '/') {
robot.keyPress(KeyEvent.VK_SHIFT);
robot.keyPress(code);
robot.keyRelease(code);
robot.keyRelease(KeyEvent.VK_SHIFT);
} else {
robot.keyPress(code);
robot.keyRelease(code);
}
}
}));
return this;
}
private GuiTester addCode(int code) {
Assert.assertTrue("keycode 0 is not allowed", code != 0);
add(() -> {
robot.keyPress(code);
robot.keyRelease(code);
});
return this;
}
private boolean isDisplay() {
final boolean isDisplay = !GraphicsEnvironment.isHeadless();
if (!isDisplay)
System.err.println("running headless, skip test!");
return isDisplay;
}
public void execute() throws Exception {
if (isDisplay()) {
SwingUtilities.invokeAndWait(() -> {
if (filename != null) {
File file = new File(Resources.getRoot(), filename);
main = new Main.MainBuilder().setFileToOpen(file).build();
} else
main = new Main.MainBuilder().setCircuit(new Circuit()).build();
main.setVisible(true);
});
Thread.sleep(500);
try {
robot = new Robot();
robot.setAutoWaitForIdle(true);
int step = 0;
for (Runnable r : runnableList) {
if (step > 0) {
System.err.print("-");
Thread.sleep(SLEEP_TIME);
}
step++;
System.err.print(step);
r.run();
}
} finally {
SwingUtilities.invokeAndWait(() -> main.dispose());
}
System.err.println();
}
}
interface Runnable {
void run() throws Exception;
}
static class WindowCheck<W extends Window> implements Runnable {
private final Class<W> clazz;
public WindowCheck(Class<W> clazz) {
this.clazz = clazz;
}
@Override
public void run() throws Exception {
Window activeWindow = FocusManager.getCurrentManager().getActiveWindow();
if (activeWindow == null || !clazz.isAssignableFrom(activeWindow.getClass())) {
Thread.sleep(500);
activeWindow = FocusManager.getCurrentManager().getActiveWindow();
}
Assert.assertNotNull("no java window on top!", activeWindow);
Assert.assertTrue(getClass().getSimpleName()
+ ": wrong dialog on top! expected: <"
+ clazz.getSimpleName()
+ "> but was: <"
+ activeWindow.getClass().getSimpleName()
+ ">",
clazz.isAssignableFrom(activeWindow.getClass()));
checkWindow((W) activeWindow);
}
public void checkWindow(W window) {
}
}
public static class CheckDialogText extends WindowCheck<JDialog> {
private final String[] expected;
public CheckDialogText(String... expected) {
super(JDialog.class);
this.expected = expected;
}
@Override
public void checkWindow(JDialog dialog) {
StringBuilder text = new StringBuilder();
collectText(dialog.getContentPane(), text);
String t = text.toString();
for (String e : expected)
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);
}
}
}
}
public static class CloseTopMost implements Runnable {
@Override
public void run() {
Window activeWindow = FocusManager.getCurrentManager().getActiveWindow();
Assert.assertNotNull("no java window on top!", activeWindow);
activeWindow.dispose();
}
}
}

View File

@ -10,105 +10,98 @@ import de.neemann.digital.lang.Lang;
import de.neemann.gui.ErrorMessage; import de.neemann.gui.ErrorMessage;
import junit.framework.TestCase; import junit.framework.TestCase;
import javax.swing.FocusManager;
import javax.swing.*;
import java.awt.*;
import java.awt.event.KeyEvent;
import java.io.File;
import java.util.ArrayList;
import java.util.List; import java.util.List;
/**
* These tests are excluded from the maven build because gui tests are sometimes fragile.
* They may not run on all systems as expected.
* Run this tests directly from your IDE.
*/
public class TestInGUI extends TestCase { public class TestInGUI extends TestCase {
public void testErrorAtStart1() throws Exception { public void testErrorAtStart1() throws Exception {
new GuiTest("dig/manualError/01_fastRuntime.dig") new GuiTester("dig/manualError/01_fastRuntime.dig")
.press(' ') .press(' ')
.add(new CheckErrorDialog("01_fastRuntime.dig", Lang.get("err_burnError"))) .add(new CheckErrorDialog("01_fastRuntime.dig", Lang.get("err_burnError")))
.add(new CloseTopMost()) .add(new GuiTester.CloseTopMost())
.add(new WindowCheck<>(Main.class)) .add(new GuiTester.WindowCheck<>(Main.class))
.execute(); .execute();
} }
public void testErrorAtStart2() throws Exception { public void testErrorAtStart2() throws Exception {
new GuiTest("dig/manualError/02_fastRuntimeEmbed.dig") new GuiTester("dig/manualError/02_fastRuntimeEmbed.dig")
.press(' ') .press(' ')
.add(new CheckErrorDialog("short.dig", Lang.get("err_burnError"))) .add(new CheckErrorDialog("short.dig", Lang.get("err_burnError")))
.add(new CloseTopMost()) .add(new GuiTester.CloseTopMost())
.add(new WindowCheck<>(Main.class)) .add(new GuiTester.WindowCheck<>(Main.class))
.execute(); .execute();
} }
public void testErrorAtStart3() throws Exception { public void testErrorAtStart3() throws Exception {
new GuiTest("dig/manualError/06_initPhase.dig") new GuiTester("dig/manualError/06_initPhase.dig")
.press(' ') .press(' ')
.add(new CheckErrorDialog("06_initPhase.dig", Lang.get("err_burnError"))) .add(new CheckErrorDialog("06_initPhase.dig", Lang.get("err_burnError")))
.add(new CloseTopMost()) .add(new GuiTester.CloseTopMost())
.add(new WindowCheck<>(Main.class)) .add(new GuiTester.WindowCheck<>(Main.class))
.execute(); .execute();
} }
public void testErrorAtStart4() throws Exception { public void testErrorAtStart4() throws Exception {
new GuiTest("dig/manualError/07_creationPhase.dig") new GuiTester("dig/manualError/07_creationPhase.dig")
.press(' ') .press(' ')
.add(new CheckErrorDialog("07_creationPhase.dig", "ErrorY")) .add(new CheckErrorDialog("07_creationPhase.dig", "ErrorY"))
.add(new CloseTopMost()) .add(new GuiTester.CloseTopMost())
.add(new WindowCheck<>(Main.class)) .add(new GuiTester.WindowCheck<>(Main.class))
.execute(); .execute();
} }
public void testErrorAtStart5() throws Exception { public void testErrorAtStart5() throws Exception {
new GuiTest("dig/manualError/08_twoFastClocks.dig") new GuiTester("dig/manualError/08_twoFastClocks.dig")
.press(' ') .press(' ')
.add(new CheckErrorDialog(Lang.get("err_moreThanOneFastClock"))) .add(new CheckErrorDialog(Lang.get("err_moreThanOneFastClock")))
.add(new CloseTopMost()) .add(new GuiTester.CloseTopMost())
.add(new WindowCheck<>(Main.class)) .add(new GuiTester.WindowCheck<>(Main.class))
.execute(); .execute();
} }
public void testErrorAtTestExecution() throws Exception { public void testErrorAtTestExecution() throws Exception {
new GuiTest("dig/manualError/04_testExecution.dig") new GuiTester("dig/manualError/04_testExecution.dig")
.press("F8") .press("F8")
.add(new CheckErrorDialog("04_testExecution.dig", Lang.get("err_burnError"))) .add(new CheckErrorDialog("04_testExecution.dig", Lang.get("err_burnError")))
.add(new CloseTopMost()) .add(new GuiTester.CloseTopMost())
.execute(); .execute();
} }
public void testErrorAtRunToBreak() throws Exception { public void testErrorAtRunToBreak() throws Exception {
new GuiTest("dig/manualError/05_runToBreak.dig") new GuiTester("dig/manualError/05_runToBreak.dig")
.press(' ') .press(' ')
.delay(500) .delay(500)
.press("F7") .press("F7")
.add(new CheckErrorDialog("05_runToBreak.dig", Lang.get("err_burnError"))) .add(new CheckErrorDialog("05_runToBreak.dig", Lang.get("err_burnError")))
.add(new CloseTopMost()) .add(new GuiTester.CloseTopMost())
.add(new WindowCheck<>(Main.class)) .add(new GuiTester.WindowCheck<>(Main.class))
.execute(); .execute();
} }
public void testErrorAtButtonPress() throws Exception { public void testErrorAtButtonPress() throws Exception {
new GuiTest("dig/manualError/03_fastRuntimeButton.dig") new GuiTester("dig/manualError/03_fastRuntimeButton.dig")
.press(' ') .press(' ')
.delay(500) .delay(500)
.press('A') .press('A')
.add(new CheckErrorDialog("03_fastRuntimeButton.dig", Lang.get("err_burnError"))) .add(new CheckErrorDialog("03_fastRuntimeButton.dig", Lang.get("err_burnError")))
.add(new CloseTopMost()) .add(new GuiTester.CloseTopMost())
.add(new WindowCheck<>(Main.class)) .add(new GuiTester.WindowCheck<>(Main.class))
.execute(); .execute();
} }
public void testAnalysis() throws Exception { public void testAnalysis() throws Exception {
new GuiTest("dig/manualError/09_analysis.dig") new GuiTester("dig/manualError/09_analysis.dig")
.press("F9") .press("F9")
.add(new WindowCheck<TableDialog>(TableDialog.class) { .delay(500)
@Override .add(new TableDialogCheck("and(B,C)"))
public void checkWindow(TableDialog td) {
ExpressionListenerStore exp = td.getLastGeneratedExpressions();
assertEquals(1, exp.getResults().size());
Expression res = exp.getResults().get(0).getExpression();
assertEquals("and(B,C)", res.toString());
}
})
.press("F1") .press("F1")
.add(new WindowCheck<KarnaughMapDialog>(KarnaughMapDialog.class) { .delay(500)
.add(new GuiTester.WindowCheck<KarnaughMapDialog>(KarnaughMapDialog.class) {
@Override @Override
public void checkWindow(KarnaughMapDialog kMapDialog) { public void checkWindow(KarnaughMapDialog kMapDialog) {
List<ExpressionListenerStore.Result> res = kMapDialog.getResults(); List<ExpressionListenerStore.Result> res = kMapDialog.getResults();
@ -117,129 +110,72 @@ public class TestInGUI extends TestCase {
assertEquals("and(B,C)", r.toString()); assertEquals("and(B,C)", r.toString());
} }
}) })
.add(new CloseTopMost()) .add(new GuiTester.CloseTopMost())
.press("F2") .press("F2")
.add(new WindowCheck<Main>(Main.class) { .add(new GuiTester.WindowCheck<Main>(Main.class) {
@Override @Override
public void checkWindow(Main main) { public void checkWindow(Main main) {
Circuit c = main.getCircuitComponent().getCircuit(); Circuit c = main.getCircuitComponent().getCircuit();
assertEquals(4, c.getElements().size()); assertEquals(4, c.getElements().size());
} }
}) })
.add(new CloseTopMost()) .add(new GuiTester.CloseTopMost())
.add(new WindowCheck<>(TableDialog.class)) .add(new GuiTester.WindowCheck<>(TableDialog.class))
.execute(); .execute();
} }
public static class GuiTest { public void testExpression() throws Exception {
private static final long SLEEP_TIME = 200; new GuiTester()
private final ArrayList<Runnable> runnableList; .press("F10")
private Main main; .press("RIGHT", 4)
private Robot robot; .press("DOWN", 3)
private String filename; .press("ENTER")
.pressCTRL('a')
public GuiTest(String filename) { .type("a b + b c")
this.filename = filename; .press("TAB", 2)
runnableList = new ArrayList<>(); .press("SPACE")
} .delay(500)
.add(new GuiTester.WindowCheck<Main>(Main.class) {
public GuiTest add(Runnable runnable) { @Override
runnableList.add(runnable); public void checkWindow(Main main) {
return this; Circuit c = main.getCircuitComponent().getCircuit();
} assertEquals(7, c.getElements().size());
public GuiTest delay(int ms) {
add(() -> Thread.sleep(ms));
return this;
}
public GuiTest press(String key) {
return addCode(KeyStroke.getKeyStroke(key).getKeyCode());
}
public GuiTest press(char c) {
return addCode(KeyEvent.getExtendedKeyCodeForChar(c));
}
private GuiTest addCode(int code) {
add(() -> {
robot.keyPress(code);
robot.keyRelease(code);
});
return this;
}
private boolean isDisplay() {
final boolean isDisplay = !GraphicsEnvironment.isHeadless();
if (!isDisplay)
System.err.println("running headless, skip test!");
return isDisplay;
}
public void execute() throws Exception {
if (isDisplay()) {
File file = new File(Resources.getRoot(), filename);
SwingUtilities.invokeAndWait(() -> {
main = new Main.MainBuilder().setFileToOpen(file).build();
main.setVisible(true);
});
Thread.sleep(500);
try {
robot = new Robot();
int step = 0;
for (Runnable r : runnableList) {
if (step > 0) {
System.err.print("-");
Thread.sleep(SLEEP_TIME);
}
step++;
System.err.print(step);
r.run();
} }
} finally { })
SwingUtilities.invokeAndWait(() -> main.dispose()); .press("F9")
} .delay(500)
System.err.println(); .add(new TableDialogCheck("or(and(a,b),and(b,c))"))
} .add(new GuiTester.CloseTopMost())
} .add(new GuiTester.CloseTopMost())
.execute();
} }
interface Runnable { public void testHardware() throws Exception {
void run() throws Exception; new GuiTester("dig/manualError/10_hardware.dig")
.press("F9")
.delay(500)
.press("F10")
.press("RIGHT", 4)
.press("DOWN", 5)
.press("RIGHT")
.press("DOWN", 2)
.press("RIGHT")
.press("DOWN")
.press("RIGHT", 2)
.press("DOWN")
.press("ENTER")
.pressCTRL('a')
.typeTempFile("test")
.press("ENTER")
.delay(2000)
.press("TAB", 2)
.add(new GuiTester.CheckDialogText("Design fits successfully"))
.add(new GuiTester.CloseTopMost())
.add(new GuiTester.CloseTopMost())
.execute();
} }
private static class WindowCheck<W extends Window> implements Runnable { public static class CheckErrorDialog extends GuiTester.WindowCheck<ErrorMessage.ErrorDialog> {
private final Class<W> clazz;
public WindowCheck(Class<W> clazz) {
this.clazz = clazz;
}
@Override
public void run() throws Exception {
Window activeWindow = FocusManager.getCurrentManager().getActiveWindow();
if (activeWindow == null || !clazz.isAssignableFrom(activeWindow.getClass())) {
Thread.sleep(500);
activeWindow = FocusManager.getCurrentManager().getActiveWindow();
}
assertNotNull("no java window on top!", activeWindow);
assertTrue(getClass().getSimpleName()
+ ": wrong dialog on top! expected: <"
+ clazz.getSimpleName()
+ "> but was: <"
+ activeWindow.getClass().getSimpleName()
+ ">",
clazz.isAssignableFrom(activeWindow.getClass()));
checkWindow((W) activeWindow);
}
public void checkWindow(W window) {
}
}
public static class CheckErrorDialog extends WindowCheck<ErrorMessage.ErrorDialog> {
private final String[] expected; private final String[] expected;
public CheckErrorDialog(String... expected) { public CheckErrorDialog(String... expected) {
@ -255,12 +191,20 @@ public class TestInGUI extends TestCase {
} }
} }
public static class CloseTopMost implements Runnable { private static class TableDialogCheck extends GuiTester.WindowCheck<TableDialog> {
private final String expected;
public TableDialogCheck(String expected) {
super(TableDialog.class);
this.expected = expected;
}
@Override @Override
public void run() { public void checkWindow(TableDialog td) {
Window activeWindow = FocusManager.getCurrentManager().getActiveWindow(); ExpressionListenerStore exp = td.getLastGeneratedExpressions();
assertNotNull("no java window on top!", activeWindow); assertEquals(1, exp.getResults().size());
activeWindow.dispose(); Expression res = exp.getResults().get(0).getExpression();
assertEquals(expected, res.toString());
} }
} }
} }

View File

@ -0,0 +1,89 @@
<?xml version="1.0" encoding="utf-8"?>
<circuit>
<version>1</version>
<attributes/>
<visualElements>
<visualElement>
<elementName>Out</elementName>
<elementAttributes>
<entry>
<string>Description</string>
<string>Pin 27</string>
</entry>
<entry>
<string>Label</string>
<string>C1</string>
</entry>
<entry>
<string>pinNumber</string>
<string>27</string>
</entry>
</elementAttributes>
<pos x="40" y="300"/>
</visualElement>
<visualElement>
<elementName>In</elementName>
<elementAttributes>
<entry>
<string>Description</string>
<string>Pin 26</string>
</entry>
<entry>
<string>Label</string>
<string>C0</string>
</entry>
<entry>
<string>pinNumber</string>
<string>26</string>
</entry>
<entry>
<string>InDefault</string>
<value v="1" z="false"/>
</entry>
</elementAttributes>
<pos x="-60" y="280"/>
</visualElement>
<visualElement>
<elementName>In</elementName>
<elementAttributes>
<entry>
<string>Description</string>
<string>Pin 28</string>
</entry>
<entry>
<string>Label</string>
<string>C2</string>
</entry>
<entry>
<string>pinNumber</string>
<string>28</string>
</entry>
<entry>
<string>InDefault</string>
<value v="1" z="false"/>
</entry>
</elementAttributes>
<pos x="-60" y="320"/>
</visualElement>
<visualElement>
<elementName>And</elementName>
<elementAttributes/>
<pos x="-40" y="280"/>
</visualElement>
</visualElements>
<wires>
<wire>
<p1 x="-60" y="320"/>
<p2 x="-40" y="320"/>
</wire>
<wire>
<p1 x="-60" y="280"/>
<p2 x="-40" y="280"/>
</wire>
<wire>
<p1 x="20" y="300"/>
<p2 x="40" y="300"/>
</wire>
</wires>
<measurementOrdering/>
</circuit>