Merge branch 'genericTestCases'

This commit is contained in:
hneemann 2020-11-08 08:18:53 +01:00
commit 4208c4de87
28 changed files with 593 additions and 363 deletions

View File

@ -108,7 +108,111 @@ shiftBits:=bitsNeededFor(args.dataBits-1);</string>
direction := &quot;left&quot;;</string> direction := &quot;left&quot;;</string>
</entry> </entry>
</elementAttributes> </elementAttributes>
<pos x="400" y="0"/> <pos x="360" y="200"/>
</visualElement>
<visualElement>
<elementName>Testcase</elementName>
<elementAttributes>
<entry>
<string>Label</string>
<string>4 bits, left</string>
</entry>
<entry>
<string>Testdata</string>
<testData>
<dataString>D_in sh D
1 0 1
1 1 2
1 2 4
1 3 8
1 4 1
2 0 2
2 1 4
2 2 8
2 3 0
2 4 2
</dataString>
</testData>
</entry>
<entry>
<string>generic</string>
<string>dataBits := 4;
direction := &quot;left&quot;;</string>
</entry>
</elementAttributes>
<pos x="200" y="0"/>
</visualElement>
<visualElement>
<elementName>Testcase</elementName>
<elementAttributes>
<entry>
<string>Label</string>
<string>4 bits, right</string>
</entry>
<entry>
<string>Testdata</string>
<testData>
<dataString>D_in sh D
1 0 1
2 1 1
4 2 1
8 3 1
1 4 1
4 0 4
4 1 2
4 2 1
4 3 0
4 4 4
15 1 7</dataString>
</testData>
</entry>
<entry>
<string>generic</string>
<string>dataBits := 4;
direction := &quot;right&quot;;</string>
</entry>
</elementAttributes>
<pos x="380" y="0"/>
</visualElement>
<visualElement>
<elementName>Testcase</elementName>
<elementAttributes>
<entry>
<string>Label</string>
<string>4 bits, arith</string>
</entry>
<entry>
<string>Testdata</string>
<testData>
<dataString>D_in sh D
1 0 1
2 1 1
4 2 1
8 3 15
1 4 1
4 0 4
4 1 2
4 2 1
4 3 0
4 4 4
15 1 15</dataString>
</testData>
</entry>
<entry>
<string>generic</string>
<string>dataBits := 4;
direction := &quot;arith&quot;;</string>
</entry>
</elementAttributes>
<pos x="540" y="0"/>
</visualElement> </visualElement>
</visualElements> </visualElements>
<wires> <wires>

View File

@ -35,6 +35,10 @@
<visualElement> <visualElement>
<elementName>Testcase</elementName> <elementName>Testcase</elementName>
<elementAttributes> <elementAttributes>
<entry>
<string>Label</string>
<string>3 bits</string>
</entry>
<entry> <entry>
<string>Testdata</string> <string>Testdata</string>
<testData> <testData>
@ -47,8 +51,12 @@ C 0
</dataString> </dataString>
</testData> </testData>
</entry> </entry>
<entry>
<string>generic</string>
<string>bits := 3;</string>
</entry>
</elementAttributes> </elementAttributes>
<pos x="620" y="40"/> <pos x="680" y="40"/>
</visualElement> </visualElement>
<visualElement> <visualElement>
<elementName>Text</elementName> <elementName>Text</elementName>
@ -129,6 +137,10 @@ ist als drei.}}</string>
<string>Bits</string> <string>Bits</string>
<int>3</int> <int>3</int>
</entry> </entry>
<entry>
<string>generic</string>
<string>this.Bits=int(args.bits);</string>
</entry>
</elementAttributes> </elementAttributes>
<pos x="680" y="340"/> <pos x="680" y="340"/>
</visualElement> </visualElement>
@ -153,7 +165,59 @@ if (args.bits&gt;3) {
<string>bits := 3;</string> <string>bits := 3;</string>
</entry> </entry>
</elementAttributes> </elementAttributes>
<pos x="200" y="160"/> <pos x="680" y="200"/>
</visualElement>
<visualElement>
<elementName>Testcase</elementName>
<elementAttributes>
<entry>
<string>Label</string>
<string>4 bits</string>
</entry>
<entry>
<string>Testdata</string>
<testData>
<dataString>C G
0 0
loop (n,(1&lt;&lt;4)-1)
C ((n+1) ^ ((n+1)&gt;&gt;1))
end loop
C 0
</dataString>
</testData>
</entry>
<entry>
<string>generic</string>
<string>bits := 4;</string>
</entry>
</elementAttributes>
<pos x="780" y="40"/>
</visualElement>
<visualElement>
<elementName>Testcase</elementName>
<elementAttributes>
<entry>
<string>Label</string>
<string>5 bits</string>
</entry>
<entry>
<string>Testdata</string>
<testData>
<dataString>C G
0 0
loop (n,(1&lt;&lt;5)-1)
C ((n+1) ^ ((n+1)&gt;&gt;1))
end loop
C 0
</dataString>
</testData>
</entry>
<entry>
<string>generic</string>
<string>bits := 5;</string>
</entry>
</elementAttributes>
<pos x="680" y="120"/>
</visualElement> </visualElement>
</visualElements> </visualElements>
<wires> <wires>

View File

@ -17,6 +17,7 @@ import de.neemann.digital.draw.library.ElementNotFoundException;
import de.neemann.digital.draw.library.LibraryInterface; import de.neemann.digital.draw.library.LibraryInterface;
import de.neemann.digital.hdl.hgs.*; import de.neemann.digital.hdl.hgs.*;
import de.neemann.digital.lang.Lang; import de.neemann.digital.lang.Lang;
import de.neemann.digital.testing.TestCaseDescription;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
@ -206,9 +207,30 @@ public class SubstituteLibrary implements LibraryInterface {
Key k = Keys.getKeyByName(key); Key k = Keys.getKeyByName(key);
if (k == null) { if (k == null) {
throw new HGSEvalException("key " + key + " is invalid"); throw new HGSEvalException("key " + key + " is invalid");
} else } else {
Class<?> expectedClass = k.getDefault().getClass();
val = doImplicitTypeCasts(expectedClass, val);
boolean isAssignable = expectedClass.isAssignableFrom(val.getClass());
if (!isAssignable)
throw new HGSEvalException("error writing to " + key + ": value of type " + val.getClass().getSimpleName() + " can't be assigned to " + expectedClass.getSimpleName());
attr.set(k, val); attr.set(k, val);
} }
}
private Object doImplicitTypeCasts(Class<?> expectedClass, Object val) {
if (expectedClass == TestCaseDescription.class)
return new TestCaseDescription(val.toString());
if (expectedClass == Integer.class && val instanceof Long) {
long l = (Long) val;
if (l <= Integer.MAX_VALUE && l >= Integer.MIN_VALUE)
return (int) l;
}
return val;
}
@Override @Override
public Object hgsMapGet(String key) { public Object hgsMapGet(String key) {

View File

@ -9,18 +9,14 @@ import de.neemann.digital.cli.cli.Argument;
import de.neemann.digital.cli.cli.BasicCommand; import de.neemann.digital.cli.cli.BasicCommand;
import de.neemann.digital.cli.cli.CLIException; import de.neemann.digital.cli.cli.CLIException;
import de.neemann.digital.core.ErrorDetector; import de.neemann.digital.core.ErrorDetector;
import de.neemann.digital.core.Model;
import de.neemann.digital.draw.elements.Circuit; import de.neemann.digital.draw.elements.Circuit;
import de.neemann.digital.draw.elements.VisualElement;
import de.neemann.digital.lang.Lang; import de.neemann.digital.lang.Lang;
import de.neemann.digital.testing.TestCaseDescription;
import de.neemann.digital.testing.TestCaseElement;
import de.neemann.digital.testing.TestExecutor; import de.neemann.digital.testing.TestExecutor;
import java.io.File; import java.io.File;
import java.io.IOException; import java.io.IOException;
import java.io.PrintStream; import java.io.PrintStream;
import java.util.ArrayList; import java.util.List;
/** /**
* Tester used from the command line * Tester used from the command line
@ -28,7 +24,7 @@ import java.util.ArrayList;
public class CommandLineTester { public class CommandLineTester {
private final CircuitLoader circuitLoader; private final CircuitLoader circuitLoader;
private ArrayList<TestCase> testCases; private List<Circuit.TestCase> testCases;
private int testsPassed; private int testsPassed;
private boolean allowMissingInputs; private boolean allowMissingInputs;
@ -51,19 +47,10 @@ public class CommandLineTester {
*/ */
public CommandLineTester useTestCasesFrom(File file) throws IOException { public CommandLineTester useTestCasesFrom(File file) throws IOException {
Circuit c = Circuit.loadCircuit(file, circuitLoader.getShapeFactory()); Circuit c = Circuit.loadCircuit(file, circuitLoader.getShapeFactory());
testCases = getTestCasesFrom(c); testCases = c.getTestCases();
return this; return this;
} }
private ArrayList<TestCase> getTestCasesFrom(Circuit circuit) {
ArrayList<TestCase> tsl = new ArrayList<>();
for (VisualElement el : circuit.getTestCases())
tsl.add(new TestCase(
el.getElementAttributes().get(TestCaseElement.TESTDATA),
el.getElementAttributes().getLabel()));
return tsl;
}
/** /**
* Executes test test * Executes test test
* *
@ -72,7 +59,7 @@ public class CommandLineTester {
*/ */
public int execute(PrintStream out) { public int execute(PrintStream out) {
if (testCases == null) if (testCases == null)
testCases = getTestCasesFrom(circuitLoader.getCircuit()); testCases = circuitLoader.getCircuit().getTestCases();
int errorCount = 0; int errorCount = 0;
@ -80,28 +67,27 @@ public class CommandLineTester {
out.println("no test cases given"); out.println("no test cases given");
errorCount++; errorCount++;
} else { } else {
for (TestCase t : testCases) { for (Circuit.TestCase t : testCases) {
String label = t.getLabel(); String label = t.getLabel();
if (label.isEmpty()) if (label.isEmpty())
label = "unnamed"; label = "unnamed";
try { try {
Model model = circuitLoader.createModel();
ErrorDetector errorDetector = new ErrorDetector(); ErrorDetector errorDetector = new ErrorDetector();
model.addObserver(errorDetector); TestExecutor.Result tr = new TestExecutor(t, circuitLoader.getCircuit(), circuitLoader.getLibrary())
TestExecutor te = new TestExecutor(t.getTestCaseDescription())
.setAllowMissingInputs(allowMissingInputs) .setAllowMissingInputs(allowMissingInputs)
.create(model); .addObserver(errorDetector)
.execute();
if (te.allPassed()) { if (tr.allPassed()) {
out.println(label + ": passed"); out.println(label + ": passed");
testsPassed++; testsPassed++;
} else { } else {
String message = label + ": failed"; String message = label + ": failed";
if (te.isErrorOccurred()) if (tr.isErrorOccurred())
message += " due to an error"; message += " due to an error";
else else
message += " (" + te.failedPercent() + "%)"; message += " (" + tr.failedPercent() + "%)";
out.println(message); out.println(message);
errorCount++; errorCount++;
} }
@ -123,24 +109,6 @@ public class CommandLineTester {
return testsPassed; return testsPassed;
} }
private static final class TestCase {
private final TestCaseDescription testCaseDescription;
private final String label;
private TestCase(TestCaseDescription testCaseDescription, String label) {
this.testCaseDescription = testCaseDescription;
this.label = label;
}
private TestCaseDescription getTestCaseDescription() {
return testCaseDescription;
}
private String getLabel() {
return label;
}
}
private CommandLineTester setAllowMissingInputs(boolean allowMissingInputs) { private CommandLineTester setAllowMissingInputs(boolean allowMissingInputs) {
this.allowMissingInputs = allowMissingInputs; this.allowMissingInputs = allowMissingInputs;
return this; return this;

View File

@ -20,6 +20,7 @@ import de.neemann.digital.draw.library.ElementLibrary;
import de.neemann.digital.draw.model.InverterConfig; import de.neemann.digital.draw.model.InverterConfig;
import de.neemann.digital.draw.shapes.CustomCircuitShapeType; import de.neemann.digital.draw.shapes.CustomCircuitShapeType;
import de.neemann.digital.draw.shapes.custom.CustomShapeDescription; import de.neemann.digital.draw.shapes.custom.CustomShapeDescription;
import de.neemann.digital.testing.TestCaseDescription;
import de.neemann.gui.Screen; import de.neemann.gui.Screen;
import de.neemann.gui.language.Language; import de.neemann.gui.language.Language;
@ -869,4 +870,10 @@ public final class Keys {
public static final Key<Boolean> MIRROR = public static final Key<Boolean> MIRROR =
new Key<>("mirror", false).allowGroupEdit().setSecondary(); new Key<>("mirror", false).allowGroupEdit().setSecondary();
/**
* The test data
*/
public static final Key<TestCaseDescription> TESTDATA =
new Key<>("Testdata", () -> new TestCaseDescription(""));
} }

View File

@ -348,8 +348,79 @@ public class Circuit implements Copyable<Circuit> {
* *
* @return the test case elements * @return the test case elements
*/ */
public List<VisualElement> getTestCases() { public List<TestCase> getTestCases() {
return getElements(v -> v.equalsDescription(TestCaseElement.TESTCASEDESCRIPTION) && v.getElementAttributes().get(Keys.ENABLED)); ArrayList<TestCase> tc = new ArrayList<>();
for (VisualElement ve : getElements(v -> v.equalsDescription(TestCaseElement.DESCRIPTION) && v.getElementAttributes().get(Keys.ENABLED))) {
tc.add(new TestCase(ve));
}
return tc;
}
/**
* A simple java bean to encapsulate a test case description
*/
public static final class TestCase implements Comparable<TestCase> {
private final String label;
private final TestCaseDescription testCaseDescription;
private final boolean hasGenericCode;
private final VisualElement visualElement;
private TestCase(VisualElement visualElement) {
this.visualElement = visualElement;
ElementAttributes attr = visualElement.getElementAttributes();
this.label = attr.getLabel();
this.testCaseDescription = attr.get(Keys.TESTDATA);
this.hasGenericCode = !attr.get(Keys.GENERIC).isEmpty();
}
/**
* @return the label of the test case
*/
public String getLabel() {
return label;
}
/**
* @return the test case description
*/
public TestCaseDescription getTestCaseDescription() {
return testCaseDescription;
}
/**
* @return true if the test case has generic code
*/
public boolean hasGenericCode() {
return hasGenericCode;
}
/**
* @return the visual element which contains the test case
*/
public VisualElement getVisualElement() {
return visualElement;
}
@Override
public int compareTo(TestCase o) {
return label.compareTo(o.label);
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
TestCase testCase = (TestCase) o;
return label.equals(testCase.label);
}
@Override
public int hashCode() {
return label.hashCode();
}
} }
/** /**

View File

@ -219,7 +219,7 @@ public class ElementLibrary implements Iterable<ElementLibrary.ElementContainer>
.add(FGNFET.DESCRIPTION) .add(FGNFET.DESCRIPTION)
.add(TransGate.DESCRIPTION)) .add(TransGate.DESCRIPTION))
.add(new LibraryNode(Lang.get("lib_misc")) .add(new LibraryNode(Lang.get("lib_misc"))
.add(TestCaseElement.TESTCASEDESCRIPTION) .add(TestCaseElement.DESCRIPTION)
.add(GenericInitCode.DESCRIPTION) .add(GenericInitCode.DESCRIPTION)
.add(DummyElement.RECTDESCRIPTION) .add(DummyElement.RECTDESCRIPTION)
.add(PowerSupply.DESCRIPTION) .add(PowerSupply.DESCRIPTION)

View File

@ -158,7 +158,7 @@ public final class ShapeFactory {
map.put(DummyElement.TEXTDESCRIPTION.getName(), TextShape::new); map.put(DummyElement.TEXTDESCRIPTION.getName(), TextShape::new);
map.put(DummyElement.RECTDESCRIPTION.getName(), RectShape::new); map.put(DummyElement.RECTDESCRIPTION.getName(), RectShape::new);
map.put(TestCaseElement.TESTCASEDESCRIPTION.getName(), TestCaseShape::new); map.put(TestCaseElement.DESCRIPTION.getName(), TestCaseShape::new);
map.put(GenericInitCode.DESCRIPTION.getName(), GenericInitCodeShape::new); map.put(GenericInitCode.DESCRIPTION.getName(), GenericInitCodeShape::new);
map.put(AsyncSeq.DESCRIPTION.getName(), AsyncClockShape::new); map.put(AsyncSeq.DESCRIPTION.getName(), AsyncClockShape::new);

View File

@ -54,7 +54,6 @@ import de.neemann.digital.hdl.printer.CodePrinter;
import de.neemann.digital.hdl.verilog2.VerilogGenerator; import de.neemann.digital.hdl.verilog2.VerilogGenerator;
import de.neemann.digital.hdl.vhdl2.VHDLGenerator; import de.neemann.digital.hdl.vhdl2.VHDLGenerator;
import de.neemann.digital.lang.Lang; import de.neemann.digital.lang.Lang;
import de.neemann.digital.testing.TestCaseElement;
import de.neemann.digital.testing.TestingDataException; import de.neemann.digital.testing.TestingDataException;
import de.neemann.digital.toolchain.Configuration; import de.neemann.digital.toolchain.Configuration;
import de.neemann.digital.undo.ChangedListener; import de.neemann.digital.undo.ChangedListener;
@ -1128,11 +1127,7 @@ public final class Main extends JFrame implements ClosingWindowListener.ConfirmS
*/ */
public void startTests() { public void startTests() {
try { try {
ArrayList<ValueTableDialog.TestSet> tsl = new ArrayList<>(); List<Circuit.TestCase> tsl = circuitComponent.getCircuit().getTestCases();
for (VisualElement el : circuitComponent.getCircuit().getTestCases())
tsl.add(new ValueTableDialog.TestSet(
el.getElementAttributes().get(TestCaseElement.TESTDATA),
el.getElementAttributes().getLabel()));
if (tsl.isEmpty()) if (tsl.isEmpty())
throw new TestingDataException(Lang.get("err_noTestData")); throw new TestingDataException(Lang.get("err_noTestData"));

View File

@ -25,6 +25,7 @@ import de.neemann.digital.gui.Main;
import de.neemann.digital.gui.Settings; import de.neemann.digital.gui.Settings;
import de.neemann.digital.gui.components.modification.*; import de.neemann.digital.gui.components.modification.*;
import de.neemann.digital.lang.Lang; import de.neemann.digital.lang.Lang;
import de.neemann.digital.testing.TestCaseElement;
import de.neemann.digital.undo.*; import de.neemann.digital.undo.*;
import de.neemann.gui.*; import de.neemann.gui.*;
@ -1121,7 +1122,8 @@ public class CircuitComponent extends JComponent implements ChangedListener, Lib
} }
} }
if (elementType == GenericInitCode.DESCRIPTION) { if (getCircuit().getAttributes().get(Keys.IS_GENERIC)) {
if (elementType == GenericInitCode.DESCRIPTION || elementType == TestCaseElement.DESCRIPTION) {
if (element.getElementAttributes().get(Keys.GENERIC).isEmpty()) { if (element.getElementAttributes().get(Keys.GENERIC).isEmpty()) {
try { try {
element.getElementAttributes().set(Keys.GENERIC, ElementTypeDescriptionCustom.createDeclarationDefault(getCircuit())); element.getElementAttributes().set(Keys.GENERIC, ElementTypeDescriptionCustom.createDeclarationDefault(getCircuit()));
@ -1130,6 +1132,7 @@ public class CircuitComponent extends JComponent implements ChangedListener, Lib
} }
} }
} }
}
Point p = new Point(e.getX(), e.getY()); Point p = new Point(e.getX(), e.getY());
SwingUtilities.convertPointToScreen(p, CircuitComponent.this); SwingUtilities.convertPointToScreen(p, CircuitComponent.this);

View File

@ -5,6 +5,7 @@
*/ */
package de.neemann.digital.gui.components.testing; package de.neemann.digital.gui.components.testing;
import de.neemann.digital.core.element.Keys;
import de.neemann.digital.draw.elements.PinException; import de.neemann.digital.draw.elements.PinException;
import de.neemann.digital.draw.elements.VisualElement; import de.neemann.digital.draw.elements.VisualElement;
import de.neemann.digital.gui.Main; import de.neemann.digital.gui.Main;
@ -14,7 +15,6 @@ import de.neemann.digital.gui.components.modification.ModifyAttribute;
import de.neemann.digital.gui.components.table.ShowStringDialog; import de.neemann.digital.gui.components.table.ShowStringDialog;
import de.neemann.digital.lang.Lang; import de.neemann.digital.lang.Lang;
import de.neemann.digital.testing.TestCaseDescription; import de.neemann.digital.testing.TestCaseDescription;
import de.neemann.digital.testing.TestCaseElement;
import de.neemann.digital.testing.Transitions; import de.neemann.digital.testing.Transitions;
import de.neemann.digital.testing.parser.ParserException; import de.neemann.digital.testing.parser.ParserException;
import de.neemann.gui.ErrorMessage; import de.neemann.gui.ErrorMessage;
@ -103,7 +103,7 @@ public class TestCaseDescriptionDialog extends JDialog {
data.setDataString(text.getText()); data.setDataString(text.getText());
if (parent instanceof Main) { if (parent instanceof Main) {
CircuitComponent cc = ((Main) parent).getCircuitComponent(); CircuitComponent cc = ((Main) parent).getCircuitComponent();
element.getElementAttributes().set(TestCaseElement.TESTDATA, data); element.getElementAttributes().set(Keys.TESTDATA, data);
cc.getMain().startTests(); cc.getMain().startTests();
} }
} catch (ParserException | IOException e1) { } catch (ParserException | IOException e1) {
@ -122,7 +122,7 @@ public class TestCaseDescriptionDialog extends JDialog {
&& !initialDataString.equals(data.getDataString()) && !initialDataString.equals(data.getDataString())
&& parent instanceof Main) { && parent instanceof Main) {
CircuitComponent cc = ((Main) parent).getCircuitComponent(); CircuitComponent cc = ((Main) parent).getCircuitComponent();
cc.modify(new ModifyAttribute<>(element, TestCaseElement.TESTDATA, new TestCaseDescription(data))); cc.modify(new ModifyAttribute<>(element, Keys.TESTDATA, new TestCaseDescription(data)));
} }
dispose(); dispose();
} catch (ParserException | IOException e1) { } catch (ParserException | IOException e1) {

View File

@ -6,7 +6,6 @@
package de.neemann.digital.gui.components.testing; package de.neemann.digital.gui.components.testing;
import de.neemann.digital.core.ErrorDetector; import de.neemann.digital.core.ErrorDetector;
import de.neemann.digital.core.Model;
import de.neemann.digital.core.NodeException; import de.neemann.digital.core.NodeException;
import de.neemann.digital.data.Value; import de.neemann.digital.data.Value;
import de.neemann.digital.data.ValueTable; import de.neemann.digital.data.ValueTable;
@ -15,11 +14,9 @@ import de.neemann.digital.draw.elements.Circuit;
import de.neemann.digital.draw.elements.PinException; import de.neemann.digital.draw.elements.PinException;
import de.neemann.digital.draw.library.ElementLibrary; import de.neemann.digital.draw.library.ElementLibrary;
import de.neemann.digital.draw.library.ElementNotFoundException; import de.neemann.digital.draw.library.ElementNotFoundException;
import de.neemann.digital.draw.model.ModelCreator;
import de.neemann.digital.gui.SaveAsHelper; import de.neemann.digital.gui.SaveAsHelper;
import de.neemann.digital.gui.components.data.GraphDialog; import de.neemann.digital.gui.components.data.GraphDialog;
import de.neemann.digital.lang.Lang; import de.neemann.digital.lang.Lang;
import de.neemann.digital.testing.TestCaseDescription;
import de.neemann.digital.testing.TestExecutor; import de.neemann.digital.testing.TestExecutor;
import de.neemann.digital.testing.TestingDataException; import de.neemann.digital.testing.TestingDataException;
import de.neemann.gui.IconCreator; import de.neemann.gui.IconCreator;
@ -121,40 +118,38 @@ public class ValueTableDialog extends JDialog {
* @throws PinException PinException * @throws PinException PinException
* @throws ElementNotFoundException ElementNotFoundException * @throws ElementNotFoundException ElementNotFoundException
*/ */
public ValueTableDialog addTestResult(ArrayList<TestSet> tsl, Circuit circuit, ElementLibrary library) throws TestingDataException, ElementNotFoundException, PinException, NodeException { public ValueTableDialog addTestResult(java.util.List<Circuit.TestCase> tsl, Circuit circuit, ElementLibrary library) throws TestingDataException, ElementNotFoundException, PinException, NodeException {
Collections.sort(tsl); Collections.sort(tsl);
int i = 0; int i = 0;
int errorTabIndex = -1; int errorTabIndex = -1;
for (TestSet ts : tsl) { for (Circuit.TestCase ts : tsl) {
Model model = new ModelCreator(circuit, library).createModel(false);
ErrorDetector errorDetector = new ErrorDetector(); ErrorDetector errorDetector = new ErrorDetector();
model.addObserver(errorDetector);
try { try {
TestExecutor testExecutor = new TestExecutor(ts.data).create(model); TestExecutor.Result testResult = new TestExecutor(ts, circuit, library)
.addObserver(errorDetector)
.execute();
String tabName; String tabName;
Icon tabIcon; Icon tabIcon;
if (testExecutor.allPassed()) { if (testResult.allPassed()) {
tabName = Lang.get("msg_test_N_Passed", ts.name); tabName = Lang.get("msg_test_N_Passed", ts.getLabel());
tabIcon = ICON_PASSED; tabIcon = ICON_PASSED;
} else { } else {
tabName = Lang.get("msg_test_N_Failed", ts.name); tabName = Lang.get("msg_test_N_Failed", ts.getLabel());
tabIcon = ICON_FAILED; tabIcon = ICON_FAILED;
errorTabIndex = i; errorTabIndex = i;
} }
if (testExecutor.toManyResults()) if (testResult.toManyResults())
tabName += " " + Lang.get("msg_test_missingLines"); tabName += " " + Lang.get("msg_test_missingLines");
tp.addTab(tabName, tabIcon, new JScrollPane(createTable(testExecutor.getResult()))); tp.addTab(tabName, tabIcon, new JScrollPane(createTable(testResult.getValueTable())));
if (testExecutor.toManyResults()) if (testResult.toManyResults())
tp.setToolTipTextAt(i, new LineBreaker().toHTML().breakLines(Lang.get("msg_test_missingLines_tt"))); tp.setToolTipTextAt(i, new LineBreaker().toHTML().breakLines(Lang.get("msg_test_missingLines_tt")));
resultTableData.add(testExecutor.getResult()); resultTableData.add(testResult.getValueTable());
i++; i++;
errorDetector.check(); errorDetector.check();
} catch (Exception e) { } catch (Exception e) {
throw new TestingDataException(Lang.get("err_whileExecutingTests_N0", ts.name), e); throw new TestingDataException(Lang.get("err_whileExecutingTests_N0", ts.getLabel()), e);
} finally {
model.close();
} }
} }
if (errorTabIndex >= 0) if (errorTabIndex >= 0)
@ -200,48 +195,6 @@ public class ValueTableDialog extends JDialog {
return this; return this;
} }
/**
* A TestSet contains the {@link TestCaseDescription} and the name of the TestData.
* Is only a value bean
*/
public static class TestSet implements Comparable<TestSet> {
private final TestCaseDescription data;
private final String name;
/**
* Creates a new instance
*
* @param data the TestData
* @param name the name of the data, eg. the used label
*/
public TestSet(TestCaseDescription data, String name) {
this.data = data;
this.name = name;
}
@Override
public int compareTo(TestSet o) {
return name.compareTo(o.name);
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
TestSet testSet = (TestSet) o;
return name.equals(testSet.name);
}
@Override
public int hashCode() {
return name.hashCode();
}
}
private static class ValueRenderer extends DefaultTableCellRenderer { private static class ValueRenderer extends DefaultTableCellRenderer {
@Override @Override

View File

@ -241,7 +241,7 @@ public class HDLCircuit implements Iterable<HDLNode>, HDLModel.BitProvider, Prin
&& !v.equalsDescription(DummyElement.TEXTDESCRIPTION) && !v.equalsDescription(DummyElement.TEXTDESCRIPTION)
&& !v.equalsDescription(DummyElement.DATADESCRIPTION) && !v.equalsDescription(DummyElement.DATADESCRIPTION)
&& !v.equalsDescription(DummyElement.RECTDESCRIPTION) && !v.equalsDescription(DummyElement.RECTDESCRIPTION)
&& !v.equalsDescription(TestCaseElement.TESTCASEDESCRIPTION) && !v.equalsDescription(TestCaseElement.DESCRIPTION)
&& !v.equalsDescription(GenericInitCode.DESCRIPTION); && !v.equalsDescription(GenericInitCode.DESCRIPTION);
} }

View File

@ -5,17 +5,15 @@
*/ */
package de.neemann.digital.hdl.verilog2; package de.neemann.digital.hdl.verilog2;
import de.neemann.digital.hdl.vhdl2.*;
import de.neemann.digital.core.element.ElementAttributes;
import de.neemann.digital.data.Value; import de.neemann.digital.data.Value;
import de.neemann.digital.draw.elements.Circuit; import de.neemann.digital.draw.elements.Circuit;
import de.neemann.digital.draw.elements.VisualElement;
import de.neemann.digital.hdl.model2.HDLCircuit; import de.neemann.digital.hdl.model2.HDLCircuit;
import de.neemann.digital.hdl.model2.HDLException; import de.neemann.digital.hdl.model2.HDLException;
import de.neemann.digital.hdl.model2.HDLModel; import de.neemann.digital.hdl.model2.HDLModel;
import de.neemann.digital.hdl.model2.HDLPort; import de.neemann.digital.hdl.model2.HDLPort;
import de.neemann.digital.hdl.printer.CodePrinter; import de.neemann.digital.hdl.printer.CodePrinter;
import de.neemann.digital.hdl.printer.CodePrinterStr; import de.neemann.digital.hdl.printer.CodePrinterStr;
import de.neemann.digital.hdl.vhdl2.Separator;
import de.neemann.digital.lang.Lang; import de.neemann.digital.lang.Lang;
import de.neemann.digital.testing.TestCaseDescription; import de.neemann.digital.testing.TestCaseDescription;
import de.neemann.digital.testing.TestingDataException; import de.neemann.digital.testing.TestingDataException;
@ -27,9 +25,7 @@ import de.neemann.digital.testing.parser.TestRow;
import java.io.File; import java.io.File;
import java.io.IOException; import java.io.IOException;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List;
import static de.neemann.digital.testing.TestCaseElement.TESTDATA;
import java.util.logging.Level; import java.util.logging.Level;
import java.util.logging.Logger; import java.util.logging.Logger;
@ -38,7 +34,7 @@ import java.util.logging.Logger;
* The needed test date is taken from the test cases in the circuit * The needed test date is taken from the test cases in the circuit
*/ */
public class VerilogTestBenchCreator { public class VerilogTestBenchCreator {
private final ArrayList<ElementAttributes> testCases; private final List<Circuit.TestCase> testCases;
private final HDLCircuit main; private final HDLCircuit main;
private final String topModuleName; private final String topModuleName;
private final HDLModel.Renaming renaming; private final HDLModel.Renaming renaming;
@ -54,9 +50,7 @@ public class VerilogTestBenchCreator {
public VerilogTestBenchCreator(Circuit circuit, HDLModel model, String topModuleName) { public VerilogTestBenchCreator(Circuit circuit, HDLModel model, String topModuleName) {
this.main = model.getMain(); this.main = model.getMain();
this.topModuleName = topModuleName; this.topModuleName = topModuleName;
testCases = new ArrayList<>(); testCases = circuit.getTestCases();
for (VisualElement ve : circuit.getTestCases())
testCases.add(ve.getElementAttributes());
testFileWritten = new ArrayList<>(); testFileWritten = new ArrayList<>();
renaming = model.getRenaming(); renaming = model.getRenaming();
} }
@ -75,7 +69,10 @@ public class VerilogTestBenchCreator {
if (p > 0) if (p > 0)
filename = filename.substring(0, p); filename = filename.substring(0, p);
for (ElementAttributes tc : testCases) { for (Circuit.TestCase tc : testCases) {
if (tc.hasGenericCode())
throw new HDLException(Lang.get("err_hdlTestCaseHasGenericCode"));
String testName = tc.getLabel(); String testName = tc.getLabel();
if (testName.length() > 0) if (testName.length() > 0)
testName = filename + "_" + testName + "_tb"; testName = filename + "_" + testName + "_tb";
@ -107,7 +104,7 @@ public class VerilogTestBenchCreator {
return testFileWritten; return testFileWritten;
} }
private void writeTestBench(CodePrinter out, String moduleName, String testName, ElementAttributes tc) throws IOException, HDLException, TestingDataException, ParserException { private void writeTestBench(CodePrinter out, String moduleName, String testName, Circuit.TestCase tc) throws IOException, HDLException, TestingDataException, ParserException {
out.print("// A testbench for ").println(testName); out.print("// A testbench for ").println(testName);
out.println("`timescale 1us/1ns").println(); out.println("`timescale 1us/1ns").println();
out.print("module ").print(testName).println(";"); out.print("module ").print(testName).println(";");
@ -131,7 +128,7 @@ public class VerilogTestBenchCreator {
} }
out.dec().println().print(");").println().println(); out.dec().println().print(");").println().println();
TestCaseDescription testdata = tc.get(TESTDATA); TestCaseDescription testdata = tc.getTestCaseDescription();
ArrayList<HDLPort> dataOrder = new ArrayList<>(); ArrayList<HDLPort> dataOrder = new ArrayList<>();
ArrayList<HDLPort> inputsInOrder = new ArrayList<>(); ArrayList<HDLPort> inputsInOrder = new ArrayList<>();

View File

@ -5,10 +5,8 @@
*/ */
package de.neemann.digital.hdl.vhdl2; package de.neemann.digital.hdl.vhdl2;
import de.neemann.digital.core.element.ElementAttributes;
import de.neemann.digital.data.Value; import de.neemann.digital.data.Value;
import de.neemann.digital.draw.elements.Circuit; import de.neemann.digital.draw.elements.Circuit;
import de.neemann.digital.draw.elements.VisualElement;
import de.neemann.digital.hdl.model2.HDLCircuit; import de.neemann.digital.hdl.model2.HDLCircuit;
import de.neemann.digital.hdl.model2.HDLException; import de.neemann.digital.hdl.model2.HDLException;
import de.neemann.digital.hdl.model2.HDLModel; import de.neemann.digital.hdl.model2.HDLModel;
@ -25,15 +23,14 @@ import de.neemann.digital.testing.parser.TestRow;
import java.io.File; import java.io.File;
import java.io.IOException; import java.io.IOException;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List;
import static de.neemann.digital.testing.TestCaseElement.TESTDATA;
/** /**
* Creates a test bench for a model. * Creates a test bench for a model.
* The needed test data is taken from the test cases in the circuit * The needed test data is taken from the test cases in the circuit
*/ */
public class VHDLTestBenchCreator { public class VHDLTestBenchCreator {
private final ArrayList<ElementAttributes> testCases; private final List<Circuit.TestCase> testCases;
private final HDLCircuit main; private final HDLCircuit main;
private final HDLModel.Renaming renaming; private final HDLModel.Renaming renaming;
private ArrayList<File> testFileWritten; private ArrayList<File> testFileWritten;
@ -47,9 +44,7 @@ public class VHDLTestBenchCreator {
VHDLTestBenchCreator(Circuit circuit, HDLModel model) { VHDLTestBenchCreator(Circuit circuit, HDLModel model) {
this.main = model.getMain(); this.main = model.getMain();
this.renaming = model.getRenaming(); this.renaming = model.getRenaming();
testCases = new ArrayList<>(); testCases = circuit.getTestCases();
for (VisualElement ve : circuit.getTestCases())
testCases.add(ve.getElementAttributes());
testFileWritten = new ArrayList<>(); testFileWritten = new ArrayList<>();
} }
@ -68,7 +63,10 @@ public class VHDLTestBenchCreator {
filename = filename.substring(0, p); filename = filename.substring(0, p);
VHDLRenaming renaming = new VHDLRenaming(); VHDLRenaming renaming = new VHDLRenaming();
for (ElementAttributes tc : testCases) { for (Circuit.TestCase tc : testCases) {
if (tc.hasGenericCode())
throw new HDLException(Lang.get("err_hdlTestCaseHasGenericCode"));
String testName = tc.getLabel(); String testName = tc.getLabel();
if (testName.length() > 0) { if (testName.length() > 0) {
testName = filename + "_" + renaming.checkName(testName) + "_tb"; testName = filename + "_" + renaming.checkName(testName) + "_tb";
@ -95,7 +93,7 @@ public class VHDLTestBenchCreator {
return testFileWritten; return testFileWritten;
} }
private void writeTestBench(CodePrinter out, String testName, ElementAttributes tc) throws IOException, TestingDataException, ParserException { private void writeTestBench(CodePrinter out, String testName, Circuit.TestCase tc) throws IOException, TestingDataException, ParserException {
out.print("-- A testbench for ").println(testName); out.print("-- A testbench for ").println(testName);
out.println("LIBRARY ieee;"); out.println("LIBRARY ieee;");
out.println("USE ieee.std_logic_1164.all;"); out.println("USE ieee.std_logic_1164.all;");
@ -140,7 +138,7 @@ public class VHDLTestBenchCreator {
out.println("process").inc(); out.println("process").inc();
TestCaseDescription testdata = tc.get(TESTDATA); TestCaseDescription testdata = tc.getTestCaseDescription();
ArrayList<HDLPort> dataOrder = new ArrayList<>(); ArrayList<HDLPort> dataOrder = new ArrayList<>();
out.println("type pattern_type is record").inc(); out.println("type pattern_type is record").inc();

View File

@ -5,11 +5,9 @@
*/ */
package de.neemann.digital.testing; package de.neemann.digital.testing;
import de.neemann.digital.core.Model;
import de.neemann.digital.core.NodeException; import de.neemann.digital.core.NodeException;
import de.neemann.digital.draw.elements.Circuit; import de.neemann.digital.draw.elements.Circuit;
import de.neemann.digital.draw.elements.PinException; import de.neemann.digital.draw.elements.PinException;
import de.neemann.digital.draw.elements.VisualElement;
import de.neemann.digital.draw.library.ElementLibrary; import de.neemann.digital.draw.library.ElementLibrary;
import de.neemann.digital.draw.library.ElementNotFoundException; import de.neemann.digital.draw.library.ElementNotFoundException;
import de.neemann.digital.draw.model.ModelCreator; import de.neemann.digital.draw.model.ModelCreator;
@ -22,6 +20,7 @@ import java.io.IOException;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.Comparator; import java.util.Comparator;
import java.util.List;
/** /**
* Runs all tests in al circuits in a folder * Runs all tests in al circuits in a folder
@ -185,12 +184,7 @@ public class FolderTestRunner {
FileToTest f = files.get(i); FileToTest f = files.get(i);
try { try {
Circuit circuit = Circuit.loadCircuit(f.file, shapeFactory); Circuit circuit = Circuit.loadCircuit(f.file, shapeFactory);
ArrayList<TestCase> testCases = new ArrayList<>(); List<Circuit.TestCase> testCases = circuit.getTestCases();
for (VisualElement el : circuit.getTestCases()) {
String label = el.getElementAttributes().getLabel();
TestCaseDescription testData = el.getElementAttributes().get(TestCaseElement.TESTDATA);
testCases.add(new TestCase(label, testData));
}
if (testCases.isEmpty()) { if (testCases.isEmpty()) {
// if no test data is available, at least check if the model is error free // if no test data is available, at least check if the model is error free
try { try {
@ -203,21 +197,20 @@ public class FolderTestRunner {
} else { } else {
StringBuilder sb = new StringBuilder(); StringBuilder sb = new StringBuilder();
int rowCount = 0; int rowCount = 0;
for (TestCase tc : testCases) { for (Circuit.TestCase tc : testCases) {
Model model = new ModelCreator(circuit, library).createModel(false);
try { try {
TestExecutor te = new TestExecutor(tc.testData).create(model); TestExecutor.Result tr = new TestExecutor(tc, circuit, library).execute();
if (te.allPassed()) { if (tr.allPassed()) {
rowCount += te.getResult().getRows(); rowCount += tr.getValueTable().getRows();
} else { } else {
if (sb.length() > 0) if (sb.length() > 0)
sb.append("; "); sb.append("; ");
sb.append(Lang.get("msg_test_N_Failed", tc.label)); sb.append(Lang.get("msg_test_N_Failed", tc.getLabel()));
} }
} catch (TestingDataException | NodeException e) { } catch (TestingDataException | NodeException e) {
if (sb.length() > 0) if (sb.length() > 0)
sb.append("; "); sb.append("; ");
sb.append(tc.label).append(": ").append(e.getMessage()); sb.append(tc.getLabel()).append(": ").append(e.getMessage());
} }
} }
if (sb.length() == 0) { if (sb.length() == 0) {
@ -227,7 +220,7 @@ public class FolderTestRunner {
setMessage(f, i, sb.toString(), FileToTest.Status.failed); setMessage(f, i, sb.toString(), FileToTest.Status.failed);
} }
} catch (IOException | NodeException | ElementNotFoundException | PinException | ParserException | RuntimeException e) { } catch (IOException | ElementNotFoundException | PinException | ParserException | RuntimeException e) {
setMessage(f, i, e.getMessage(), FileToTest.Status.error); setMessage(f, i, e.getMessage(), FileToTest.Status.error);
} }
} }
@ -239,16 +232,6 @@ public class FolderTestRunner {
} }
} }
private static final class TestCase {
private final String label;
private final TestCaseDescription testData;
private TestCase(String label, TestCaseDescription testData) {
this.label = label;
this.testData = testData;
}
}
/** /**
* Interface to notify a listener for changes * Interface to notify a listener for changes
*/ */

View File

@ -63,7 +63,7 @@ public class TestCaseDescription {
} }
private void check() throws TestingDataException { private void check() throws TestingDataException {
if (lines == null) { if (lines == null || names == null) {
try { try {
Parser tdp = new Parser(dataString).parse(); Parser tdp = new Parser(dataString).parse();
lines = tdp.getLines(); lines = tdp.getLines();
@ -106,4 +106,9 @@ public class TestCaseDescription {
public int hashCode() { public int hashCode() {
return dataString != null ? dataString.hashCode() : 0; return dataString != null ? dataString.hashCode() : 0;
} }
@Override
public String toString() {
return dataString;
}
} }

View File

@ -15,18 +15,13 @@ import de.neemann.digital.core.element.*;
*/ */
public class TestCaseElement implements Element { public class TestCaseElement implements Element {
/**
* the used {@link ElementAttributes} key
*/
public static final Key<TestCaseDescription> TESTDATA = new Key<>("Testdata", () -> new TestCaseDescription(""));
/** /**
* The TestCaseElement description * The TestCaseElement description
*/ */
public static final ElementTypeDescription TESTCASEDESCRIPTION public static final ElementTypeDescription DESCRIPTION
= new ElementTypeDescription("Testcase", TestCaseElement.class) = new ElementTypeDescription("Testcase", TestCaseElement.class)
.addAttribute(Keys.LABEL) .addAttribute(Keys.LABEL)
.addAttribute(TESTDATA) .addAttribute(Keys.TESTDATA)
.addAttribute(Keys.ENABLED) .addAttribute(Keys.ENABLED)
.supportsHDL(); .supportsHDL();

View File

@ -6,9 +6,16 @@
package de.neemann.digital.testing; package de.neemann.digital.testing;
import de.neemann.digital.core.*; import de.neemann.digital.core.*;
import de.neemann.digital.core.element.Keys;
import de.neemann.digital.core.wiring.Clock; import de.neemann.digital.core.wiring.Clock;
import de.neemann.digital.data.Value; import de.neemann.digital.data.Value;
import de.neemann.digital.data.ValueTable; import de.neemann.digital.data.ValueTable;
import de.neemann.digital.draw.elements.Circuit;
import de.neemann.digital.draw.elements.PinException;
import de.neemann.digital.draw.library.ElementLibrary;
import de.neemann.digital.draw.library.ElementNotFoundException;
import de.neemann.digital.draw.library.ResolveGenerics;
import de.neemann.digital.draw.model.ModelCreator;
import de.neemann.digital.lang.Lang; import de.neemann.digital.lang.Lang;
import de.neemann.digital.testing.parser.Context; import de.neemann.digital.testing.parser.Context;
import de.neemann.digital.testing.parser.LineEmitter; import de.neemann.digital.testing.parser.LineEmitter;
@ -26,6 +33,7 @@ public class TestExecutor {
private static final int ERR_RESULTS = MAX_RESULTS * 2; private static final int ERR_RESULTS = MAX_RESULTS * 2;
private final ArrayList<String> names; private final ArrayList<String> names;
private final Model model;
private final LineEmitter lines; private final LineEmitter lines;
private final ValueTable results; private final ValueTable results;
private boolean errorOccurred; private boolean errorOccurred;
@ -38,30 +46,57 @@ public class TestExecutor {
private boolean allowMissingInputs; private boolean allowMissingInputs;
/** /**
* Creates a new testing result * Creates a new testing result.
* *
* @param testCaseDescription the testing data * @param testCase the testing data
* @param circuit the circuit
* @param library the library
* @throws TestingDataException DataException * @throws TestingDataException DataException
* @throws ElementNotFoundException ElementNotFoundException
* @throws PinException PinException
* @throws NodeException NodeException
*/ */
public TestExecutor(TestCaseDescription testCaseDescription) throws TestingDataException { public TestExecutor(Circuit.TestCase testCase, Circuit circuit, ElementLibrary library) throws TestingDataException, NodeException, ElementNotFoundException, PinException {
names = testCaseDescription.getNames(); this(testCase.getTestCaseDescription(), createModel(testCase, circuit, library));
results = new ValueTable(names); }
visibleRows = 0;
lines = testCaseDescription.getLines(); static private Model createModel(Circuit.TestCase testCase, Circuit circuit, ElementLibrary library) throws NodeException, ElementNotFoundException, PinException {
final Model model;
if (circuit != null && circuit.getAttributes().get(Keys.IS_GENERIC) && testCase.hasGenericCode()) {
Circuit c = new ResolveGenerics().resolveCircuit(testCase.getVisualElement(), circuit, library).getCircuit();
model = new ModelCreator(c, library, false).createModel(false);
} else
model = new ModelCreator(circuit, library).createModel(false);
return model;
} }
/** /**
* Creates the result by comparing the testing vector with the given model- * Use for tests only! Don't use this constructor with a model you have created from a circuit.
* If a circuit is available use the constructor above.
* *
* @param model the model to check * @param testCase the test case
* @return this for chained calls * @param model the model
* @throws TestingDataException TestingDataException
*/
public TestExecutor(TestCaseDescription testCase, Model model) throws TestingDataException {
names = testCase.getNames();
this.model = model;
results = new ValueTable(names);
visibleRows = 0;
lines = testCase.getLines();
}
/**
* Creates the result by comparing the testing vector with the given model
*
* @return the result of the test execution
* @throws TestingDataException DataException * @throws TestingDataException DataException
* @throws NodeException NodeException * @throws NodeException NodeException
* @throws ParserException ParserException * @throws ParserException ParserException
*/ */
public TestExecutor create(Model model) throws TestingDataException, NodeException, ParserException { public TestExecutor.Result execute() throws TestingDataException, NodeException, ParserException {
try {
HashSet<String> usedSignals = new HashSet<>(); HashSet<String> usedSignals = new HashSet<>();
inputs = new ArrayList<>(); inputs = new ArrayList<>();
outputs = new ArrayList<>(); outputs = new ArrayList<>();
for (Signal s : model.getInputs()) { for (Signal s : model.getInputs()) {
@ -118,7 +153,10 @@ public class TestExecutor {
lines.emitLines(new LineListenerResolveDontCare(values -> checkRow(model, values), inputs), new Context().setModel(model)); lines.emitLines(new LineListenerResolveDontCare(values -> checkRow(model, values), inputs), new Context().setModel(model));
return this; return new Result();
} finally {
model.close();
}
} }
private void addTo(HashSet<String> signals, String name) throws TestingDataException { private void addTo(HashSet<String> signals, String name) throws TestingDataException {
@ -205,6 +243,68 @@ public class TestExecutor {
toManyResults = true; toManyResults = true;
} }
private int getIndexOf(String name) {
if (name == null || name.length() == 0)
return -1;
for (int i = 0; i < names.size(); i++) {
String n = names.get(i);
if (n.equals(name))
return i;
}
return -1;
}
/**
* Allow missing inputs
*
* @param allowMissingInputs if true, missing inputs are allowed
* @return this for chained calls
*/
public TestExecutor setAllowMissingInputs(boolean allowMissingInputs) {
this.allowMissingInputs = allowMissingInputs;
return this;
}
/**
* Adds a observer to the model of this test executor
*
* @param observer the observer to add
* @return this for chained calls
*/
public TestExecutor addObserver(ModelStateObserverTyped observer) {
model.addObserver(observer);
return this;
}
/**
* A test signal
*/
public final static class TestSignal {
private final int index;
private final ObservableValue value;
private TestSignal(int index, ObservableValue value) {
this.index = index;
this.value = value;
}
/**
* @return the index of this value
*/
public int getIndex() {
return index;
}
}
/**
* The result of the test execution
*/
public final class Result {
private Result() {
}
/** /**
* @return true if all tests have passed * @return true if all tests have passed
*/ */
@ -241,54 +341,11 @@ public class TestExecutor {
return toManyResults; return toManyResults;
} }
private int getIndexOf(String name) {
if (name == null || name.length() == 0)
return -1;
for (int i = 0; i < names.size(); i++) {
String n = names.get(i);
if (n.equals(name))
return i;
}
return -1;
}
/** /**
* @return return the result * @return the value table containing the detailed result
*/ */
public ValueTable getResult() { public ValueTable getValueTable() {
return results; return results;
} }
/**
* Allow missing inputs
*
* @param allowMissingInputs if true, missing inputs are allowed
* @return this for chained calls
*/
public TestExecutor setAllowMissingInputs(boolean allowMissingInputs) {
this.allowMissingInputs = allowMissingInputs;
return this;
}
/**
* A test signal
*/
public static class TestSignal {
private final int index;
private final ObservableValue value;
TestSignal(int index, ObservableValue value) {
this.index = index;
this.value = value;
}
/**
* @return the index of this value
*/
public int getIndex() {
return index;
} }
} }
}

View File

@ -1047,6 +1047,9 @@ Sind evtl. die Namen der Variablen nicht eindeutig?</string>
<string name="err_vhdlANameIsMissing">Es fehlt ein Name. Sind z.B. alle Pins benannt?</string> <string name="err_vhdlANameIsMissing">Es fehlt ein Name. Sind z.B. alle Pins benannt?</string>
<string name="err_hdlMultipleOutputsConnectedToNet_N_N_N">Es sind mehrere Ausgänge miteinander verbunden. <string name="err_hdlMultipleOutputsConnectedToNet_N_N_N">Es sind mehrere Ausgänge miteinander verbunden.
Diese Art der Verschaltung wird beim HDL-Export nicht unterstützt ({0}, {1}, {2}).</string> Diese Art der Verschaltung wird beim HDL-Export nicht unterstützt ({0}, {1}, {2}).</string>
<string name="err_hdlTestCaseHasGenericCode">Testfälle mit generischer Parametrisierung werden im HDL-Export nicht
unterstützt!
</string>
<string name="err_unnamedNet">unbenanntes Netz</string> <string name="err_unnamedNet">unbenanntes Netz</string>
<string name="err_toManyVars">Zu viele Variablen!</string> <string name="err_toManyVars">Zu viele Variablen!</string>
<string name="err_invalidExpression">Ungültiger Ausdruck!</string> <string name="err_invalidExpression">Ungültiger Ausdruck!</string>

View File

@ -1057,6 +1057,9 @@
<string name="err_vhdlANameIsMissing">A name is missing. Have e.g. all pins a label set?</string> <string name="err_vhdlANameIsMissing">A name is missing. Have e.g. all pins a label set?</string>
<string name="err_hdlMultipleOutputsConnectedToNet_N_N_N">Several outputs are connected to each other. <string name="err_hdlMultipleOutputsConnectedToNet_N_N_N">Several outputs are connected to each other.
This type of interconnection is not supported for HDL export. ({0}, {1}, {2}).</string> This type of interconnection is not supported for HDL export. ({0}, {1}, {2}).</string>
<string name="err_hdlTestCaseHasGenericCode">Test cases with generic parameterization are not supported in HDL
export!
</string>
<string name="err_unnamedNet">unnamed net</string> <string name="err_unnamedNet">unnamed net</string>
<string name="err_toManyVars">Too many variables!</string> <string name="err_toManyVars">Too many variables!</string>
<string name="err_invalidExpression">Invalid expression!</string> <string name="err_invalidExpression">Invalid expression!</string>

View File

@ -6,7 +6,6 @@
package de.neemann.digital.docu; package de.neemann.digital.docu;
import de.neemann.digital.analyse.TruthTable; import de.neemann.digital.analyse.TruthTable;
import de.neemann.digital.analyse.TruthTableTableModel;
import de.neemann.digital.analyse.expression.format.FormatToExpression; import de.neemann.digital.analyse.expression.format.FormatToExpression;
import de.neemann.digital.core.element.Keys; import de.neemann.digital.core.element.Keys;
import de.neemann.digital.fsm.gui.FSMFrame; import de.neemann.digital.fsm.gui.FSMFrame;
@ -353,7 +352,7 @@ public class ScreenShots {
.add(new GuiTester.WindowCheck<>(Main.class, (gt, w) -> w.setSize(WIN_DX, WIN_DY))) .add(new GuiTester.WindowCheck<>(Main.class, (gt, w) -> w.setSize(WIN_DX, WIN_DY)))
.delay(500) .delay(500)
.add(new ScreenShot<>(Main.class)) .add(new ScreenShot<>(Main.class))
.add(new TestInGUI.SetMouseToElement((v) -> v.equalsDescription(TestCaseElement.TESTCASEDESCRIPTION))) .add(new TestInGUI.SetMouseToElement((v) -> v.equalsDescription(TestCaseElement.DESCRIPTION)))
.mouseClick(InputEvent.BUTTON3_MASK) .mouseClick(InputEvent.BUTTON3_MASK)
.delay(500) .delay(500)
.add(new GuiTester.WindowCheck<>(AttributeDialog.class, (gt, w) -> { .add(new GuiTester.WindowCheck<>(AttributeDialog.class, (gt, w) -> {
@ -391,7 +390,7 @@ public class ScreenShots {
.add(new GuiTester.WindowCheck<>(Main.class, (gt, w) -> w.setSize(WIN_DX, WIN_DY))) .add(new GuiTester.WindowCheck<>(Main.class, (gt, w) -> w.setSize(WIN_DX, WIN_DY)))
.delay(500) .delay(500)
.add(new ScreenShot<>(Main.class)) .add(new ScreenShot<>(Main.class))
.add(new TestInGUI.SetMouseToElement((v) -> v.equalsDescription(TestCaseElement.TESTCASEDESCRIPTION))) .add(new TestInGUI.SetMouseToElement((v) -> v.equalsDescription(TestCaseElement.DESCRIPTION)))
.mouseClick(InputEvent.BUTTON3_MASK) .mouseClick(InputEvent.BUTTON3_MASK)
.delay(500) .delay(500)
.add(new GuiTester.WindowCheck<>(AttributeDialog.class, (gt, w) -> { .add(new GuiTester.WindowCheck<>(AttributeDialog.class, (gt, w) -> {

View File

@ -5,11 +5,9 @@
*/ */
package de.neemann.digital.draw.library; package de.neemann.digital.draw.library;
import de.neemann.digital.draw.elements.VisualElement; import de.neemann.digital.draw.elements.Circuit;
import de.neemann.digital.integration.Resources; import de.neemann.digital.integration.Resources;
import de.neemann.digital.integration.ToBreakRunner; import de.neemann.digital.integration.ToBreakRunner;
import de.neemann.digital.testing.TestCaseDescription;
import de.neemann.digital.testing.TestCaseElement;
import de.neemann.digital.testing.TestExecutor; import de.neemann.digital.testing.TestExecutor;
import junit.framework.TestCase; import junit.framework.TestCase;
@ -35,13 +33,7 @@ public class JarComponentManagerTest extends TestCase {
} }
}; };
for (VisualElement ve : br.getCircuit().getElements()) { for (Circuit.TestCase tc : br.getCircuit().getTestCases())
if (ve.equalsDescription(TestCaseElement.TESTCASEDESCRIPTION)) { assertTrue(new TestExecutor(tc, br.getCircuit(), br.getLibrary()).execute().allPassed());
TestCaseDescription td = ve.getElementAttributes().get(TestCaseElement.TESTDATA);
TestExecutor tr = new TestExecutor(td).create(br.getModel());
assertTrue(tr.allPassed());
} }
} }
}
}

View File

@ -43,7 +43,7 @@ public class TestHDLExportFlag extends TestCase {
implicitSupported.add(Tunnel.DESCRIPTION); implicitSupported.add(Tunnel.DESCRIPTION);
implicitSupported.add(Splitter.DESCRIPTION); implicitSupported.add(Splitter.DESCRIPTION);
implicitSupported.add(TestCaseElement.TESTCASEDESCRIPTION); implicitSupported.add(TestCaseElement.DESCRIPTION);
implicitSupported.add(GenericInitCode.DESCRIPTION); implicitSupported.add(GenericInitCode.DESCRIPTION);
} }

View File

@ -375,6 +375,19 @@ public class ParserTest extends TestCase {
assertEquals("false;", c.toString()); assertEquals("false;", c.toString());
} }
public void testStringCreation() throws IOException, ParserException, HGSEvalException {
String code = "?>Test:<? print(n); str:=output();";
Statement s = new Parser(code).parse(false);
Context c = new Context().declareVar("n", 3);
s.execute(c);
assertEquals("Test:3", c.getVar("str"));
c = new Context().declareVar("n", 12);
s.execute(c);
assertEquals("Test:12", c.getVar("str"));
}
public void testAddFunction() throws IOException, ParserException, HGSEvalException { public void testAddFunction() throws IOException, ParserException, HGSEvalException {
Statement s = new Parser("a : in <?=type(Bits)?>;").parse(); Statement s = new Parser("a : in <?=type(Bits)?>;").parse();
Context funcs = new Context().declareVar("type", new Function(1) { Context funcs = new Context().declareVar("type", new Function(1) {

View File

@ -6,7 +6,6 @@
package de.neemann.digital.integration; package de.neemann.digital.integration;
import de.neemann.digital.core.ErrorDetector; import de.neemann.digital.core.ErrorDetector;
import de.neemann.digital.core.Model;
import de.neemann.digital.core.NodeException; import de.neemann.digital.core.NodeException;
import de.neemann.digital.core.element.Keys; import de.neemann.digital.core.element.Keys;
import de.neemann.digital.draw.elements.Circuit; import de.neemann.digital.draw.elements.Circuit;
@ -17,8 +16,6 @@ import de.neemann.digital.draw.library.ElementNotFoundException;
import de.neemann.digital.draw.library.GenericInitCode; import de.neemann.digital.draw.library.GenericInitCode;
import de.neemann.digital.draw.library.ResolveGenerics; import de.neemann.digital.draw.library.ResolveGenerics;
import de.neemann.digital.draw.model.ModelCreator; import de.neemann.digital.draw.model.ModelCreator;
import de.neemann.digital.testing.TestCaseDescription;
import de.neemann.digital.testing.TestCaseElement;
import de.neemann.digital.testing.TestExecutor; import de.neemann.digital.testing.TestExecutor;
import junit.framework.TestCase; import junit.framework.TestCase;
@ -42,7 +39,7 @@ public class TestExamples extends TestCase {
public void testDistExamples() throws Exception { public void testDistExamples() throws Exception {
File examples = new File(Resources.getRoot().getParentFile().getParentFile(), "/main/dig"); File examples = new File(Resources.getRoot().getParentFile().getParentFile(), "/main/dig");
assertEquals(302, new FileScanner(this::check).scan(examples)); assertEquals(302, new FileScanner(this::check).scan(examples));
assertEquals(198, testCasesInFiles); assertEquals(203, testCasesInFiles);
} }
/** /**
@ -79,27 +76,21 @@ public class TestExamples extends TestCase {
assertEquals("wrong locked mode", isLib, (boolean) br.getCircuit().getAttributes().get(Keys.LOCKED_MODE)); assertEquals("wrong locked mode", isLib, (boolean) br.getCircuit().getAttributes().get(Keys.LOCKED_MODE));
try { try {
for (VisualElement el : br.getCircuit().getElements()) for (Circuit.TestCase tc : br.getCircuit().getTestCases()) {
if (el.equalsDescription(TestCaseElement.TESTCASEDESCRIPTION)) {
testCasesInFiles++; testCasesInFiles++;
String label = el.getElementAttributes().getLabel(); String label = tc.getLabel();
TestCaseDescription td = el.getElementAttributes().get(TestCaseElement.TESTDATA);
Model model = new ModelCreator(br.getCircuit(), br.getLibrary()).createModel(false);
ErrorDetector ed = new ErrorDetector(); ErrorDetector ed = new ErrorDetector();
model.addObserver(ed); TestExecutor.Result tr = new TestExecutor(tc, br.getCircuit(), br.getLibrary())
try { .addObserver(ed)
TestExecutor tr = new TestExecutor(td).create(model); .execute();
if (label.contains("Failing")) if (label.contains("Failing"))
assertFalse(dig.getName() + ":" + label, tr.allPassed()); assertFalse(dig.getName() + ":" + label, tr.allPassed());
else else
assertTrue(dig.getName() + ":" + label, tr.allPassed()); assertTrue(dig.getName() + ":" + label, tr.allPassed());
} finally {
model.close();
}
ed.check(); ed.check();
} }
} catch (Exception e) { } catch (Exception e) {
@ -114,8 +105,15 @@ public class TestExamples extends TestCase {
br.close(); br.close();
} }
if (br.getCircuit().getAttributes().get(Keys.IS_GENERIC)) if (br.getCircuit().
checkGeneric(br.getCircuit(), br.getLibrary());
getAttributes().
get(Keys.IS_GENERIC))
checkGeneric(br.getCircuit(), br.
getLibrary());
} }
private void checkGeneric(Circuit circuit, ElementLibrary library) throws NodeException, ElementNotFoundException, PinException { private void checkGeneric(Circuit circuit, ElementLibrary library) throws NodeException, ElementNotFoundException, PinException {

View File

@ -61,7 +61,7 @@ import java.util.List;
import static de.neemann.digital.draw.shapes.GenericShape.SIZE; import static de.neemann.digital.draw.shapes.GenericShape.SIZE;
import static de.neemann.digital.draw.shapes.GenericShape.SIZE2; import static de.neemann.digital.draw.shapes.GenericShape.SIZE2;
import static de.neemann.digital.integration.GuiTester.getBaseContainer; import static de.neemann.digital.integration.GuiTester.getBaseContainer;
import static de.neemann.digital.testing.TestCaseElement.TESTDATA; import static de.neemann.digital.core.element.Keys.TESTDATA;
/** /**
* These tests are excluded from the maven build because gui tests are sometimes fragile. * These tests are excluded from the maven build because gui tests are sometimes fragile.
@ -451,7 +451,7 @@ public class TestInGUI extends TestCase {
public void testTestEditor() { public void testTestEditor() {
new GuiTester("dig/manualError/11_editTest.dig") new GuiTester("dig/manualError/11_editTest.dig")
.delay(300) .delay(300)
.add(new SetMouseToElement((v) -> v.equalsDescription(TestCaseElement.TESTCASEDESCRIPTION))) .add(new SetMouseToElement((v) -> v.equalsDescription(TestCaseElement.DESCRIPTION)))
.mouseClick(InputEvent.BUTTON3_DOWN_MASK) .mouseClick(InputEvent.BUTTON3_DOWN_MASK)
.delay(300) .delay(300)
.type("testIdentzz") .type("testIdentzz")
@ -1376,7 +1376,7 @@ public class TestInGUI extends TestCase {
@Override @Override
public void checkWindow(GuiTester gt, Main main) { public void checkWindow(GuiTester gt, Main main) {
main.getCircuitComponent().getCircuit().add( main.getCircuitComponent().getCircuit().add(
new VisualElement(TestCaseElement.TESTCASEDESCRIPTION.getName()) new VisualElement(TestCaseElement.DESCRIPTION.getName())
.setAttribute(TESTDATA, new TestCaseDescription(testdata)) .setAttribute(TESTDATA, new TestCaseDescription(testdata))
.setShapeFactory(main.getCircuitComponent().getLibrary().getShapeFactory())); .setShapeFactory(main.getCircuitComponent().getLibrary().getShapeFactory()));
} }

View File

@ -47,8 +47,8 @@ public class TestResultTest extends TestCase {
+ "0 1 1\n" + "0 1 1\n"
+ "1 0 1\n" + "1 0 1\n"
+ "1 1 0\n"); + "1 1 0\n");
TestExecutor tr = new TestExecutor(data).create(model); TestExecutor.Result tr = new TestExecutor(data, model).execute();
assertEquals(4,tr.getResult().getRows()); assertEquals(4, tr.getValueTable().getRows());
assertTrue(tr.allPassed()); assertTrue(tr.allPassed());
} }
@ -60,14 +60,14 @@ public class TestResultTest extends TestCase {
+ "0 1 1\n" + "0 1 1\n"
+ "1 0 1\n" + "1 0 1\n"
+ "1 1 0\n"); + "1 1 0\n");
TestExecutor te = new TestExecutor(data).create(model); TestExecutor.Result tr = new TestExecutor(data, model).execute();
ValueTable tr = te.getResult(); ValueTable valueTable = tr.getValueTable();
assertEquals(4,tr.getRows()); assertEquals(4, valueTable.getRows());
assertFalse(te.allPassed()); assertFalse(tr.allPassed());
assertEquals(true, ((MatchedValue) tr.getValue(0, 2)).isPassed()); assertEquals(true, ((MatchedValue) valueTable.getValue(0, 2)).isPassed());
assertEquals(true, ((MatchedValue) tr.getValue(1, 2)).isPassed()); assertEquals(true, ((MatchedValue) valueTable.getValue(1, 2)).isPassed());
assertEquals(true, ((MatchedValue) tr.getValue(2, 2)).isPassed()); assertEquals(true, ((MatchedValue) valueTable.getValue(2, 2)).isPassed());
assertEquals(false, ((MatchedValue) tr.getValue(3, 2)).isPassed()); assertEquals(false, ((MatchedValue) valueTable.getValue(3, 2)).isPassed());
} }
public void testResultDontCare() throws Exception { public void testResultDontCare() throws Exception {
@ -78,10 +78,10 @@ public class TestResultTest extends TestCase {
+ "0 1 1\n" + "0 1 1\n"
+ "1 0 1\n" + "1 0 1\n"
+ "1 1 x\n"); + "1 1 x\n");
TestExecutor te = new TestExecutor(data).create(model); TestExecutor.Result tr = new TestExecutor(data, model).execute();
ValueTable tr = te.getResult(); ValueTable valueTable = tr.getValueTable();
assertEquals(4,tr.getRows()); assertEquals(4, valueTable.getRows());
assertTrue(te.allPassed()); assertTrue(tr.allPassed());
} }
public void testResultDontCare2() throws Exception { public void testResultDontCare2() throws Exception {
@ -92,10 +92,10 @@ public class TestResultTest extends TestCase {
+ "0 1 1\n" + "0 1 1\n"
+ "1 0 1\n" + "1 0 1\n"
+ "1 1 1\n"); + "1 1 1\n");
TestExecutor te = new TestExecutor(data).create(model); TestExecutor.Result tr = new TestExecutor(data, model).execute();
ValueTable tr = te.getResult(); ValueTable valueTable = tr.getValueTable();
assertEquals(4,tr.getRows()); assertEquals(4, valueTable.getRows());
assertTrue(te.allPassed()); assertTrue(tr.allPassed());
} }
public void testResultDontCareInput() throws Exception { public void testResultDontCareInput() throws Exception {
@ -104,10 +104,10 @@ public class TestResultTest extends TestCase {
"A B Y\n" "A B Y\n"
+ "x 0 0\n" + "x 0 0\n"
+ "x 1 1\n"); + "x 1 1\n");
TestExecutor te = new TestExecutor(data).create(model); TestExecutor.Result tr = new TestExecutor(data, model).execute();
ValueTable tr = te.getResult(); ValueTable valueTable = tr.getValueTable();
assertEquals(4,tr.getRows()); assertEquals(4, valueTable.getRows());
assertTrue(te.allPassed()); assertTrue(tr.allPassed());
} }
public void testResultDontCareInput2() throws Exception { public void testResultDontCareInput2() throws Exception {
@ -116,10 +116,10 @@ public class TestResultTest extends TestCase {
"A B C Y\n" "A B C Y\n"
+ "x x 0 0\n" + "x x 0 0\n"
+ "x x 1 1\n"); + "x x 1 1\n");
TestExecutor te = new TestExecutor(data).create(model); TestExecutor.Result tr = new TestExecutor(data, model).execute();
ValueTable tr = te.getResult(); ValueTable valueTable = tr.getValueTable();
assertEquals(8,tr.getRows()); assertEquals(8, valueTable.getRows());
assertTrue(te.allPassed()); assertTrue(tr.allPassed());
} }
} }