allows the test case to react on the model state, closes #474

This commit is contained in:
hneemann 2020-06-08 21:48:57 +02:00
parent 39c21d1eac
commit e902d3e9e0
12 changed files with 610 additions and 9 deletions

View File

@ -110,7 +110,7 @@ public class TestExecutor {
model.init();
lines.emitLines(new LineListenerResolveDontCare(values -> checkRow(model, values), inputs), new Context());
lines.emitLines(new LineListenerResolveDontCare(values -> checkRow(model, values), inputs), new Context().setModel(model));
return this;
}

View File

@ -5,6 +5,8 @@
*/
package de.neemann.digital.testing.parser;
import de.neemann.digital.core.Model;
import de.neemann.digital.core.Signal;
import de.neemann.digital.lang.Lang;
import java.util.HashMap;
@ -16,6 +18,7 @@ import java.util.Map;
public class Context {
private final Context parent;
private HashMap<String, Long> map;
private Model model;
/**
* Creates an empty context
@ -42,8 +45,14 @@ public class Context {
*/
public long getVar(String name) throws ParserException {
if (map == null || !map.containsKey(name)) {
if (parent == null)
if (parent == null) {
if (model != null) {
for (Signal s : model.getSignals())
if (s.getName().equals(name))
return s.getValue().getValue();
}
throw new ParserException(Lang.get("err_variable_N0_notFound", name));
}
return parent.getVar(name);
} else
return map.get(name);
@ -83,4 +92,15 @@ public class Context {
sb.append(e.getKey()).append("=").append(e.getValue());
}
}
/**
* Sets the model where tis context is used with.
*
* @param model the model
* @return this for chained calls
*/
public Context setModel(Model model) {
this.model = model;
return this;
}
}

View File

@ -0,0 +1,31 @@
/*
* Copyright (c) 2020 Helmut Neemann.
* Use of this source code is governed by the GPL v3 license
* that can be found in the LICENSE file.
*/
package de.neemann.digital.testing.parser;
/**
* Line emitter which implements the while loop
*/
public class LineEmitterWhile implements LineEmitter {
private final Expression condition;
private final LineEmitter inner;
/**
* Creates a new instance
*
* @param condition the condition
* @param inner the inner LineEmitter
*/
public LineEmitterWhile(Expression condition, LineEmitter inner) {
this.condition = condition;
this.inner = inner;
}
@Override
public void emitLines(LineListener listener, Context context) throws ParserException {
while (condition.value(context) != 0)
inner.emitLines(listener, context);
}
}

View File

@ -33,8 +33,8 @@ public class Parser {
private final ArrayList<String> names;
private final Tokenizer tok;
private final HashMap<String, Function> functions = new HashMap<>();
private LineEmitter emitter;
private HashMap<String, Function> functions = new HashMap<>();
/**
* Creates a new instance
@ -111,7 +111,7 @@ public class Parser {
case LET:
tok.consume();
expect(Tokenizer.Token.IDENT);
final String varName=tok.getIdent();
final String varName = tok.getIdent();
expect(Tokenizer.Token.EQUAL);
final Expression intValue = parseExpression();
expect(Tokenizer.Token.SEMICOLON);
@ -134,6 +134,13 @@ public class Parser {
expect(Tokenizer.Token.CLOSE);
list.add(new LineEmitterRepeat(var, count, parseRows(Tokenizer.Token.LOOP)));
break;
case WHILE:
tok.consume();
expect(Tokenizer.Token.OPEN);
final Expression condition = parseExpression();
expect(Tokenizer.Token.CLOSE);
list.add(new LineEmitterWhile(condition, parseRows(Tokenizer.Token.WHILE)));
break;
default:
throw newUnexpectedToken(t);
}
@ -283,6 +290,7 @@ public class Parser {
}
return ac;
}
private Expression parseGreaterEqual() throws IOException, ParserException {
Expression ac = parseEquals();
while (isToken(Tokenizer.Token.GREATEREQUAL)) {
@ -440,7 +448,7 @@ public class Parser {
return (c) -> ~notExp.value(c);
case LOG_NOT:
Expression boolNotExp = parseIdent();
return (c) -> boolNotExp.value(c)==0?1:0;
return (c) -> boolNotExp.value(c) == 0 ? 1 : 0;
case OPEN:
Expression exp = parseExpression();
expect(Tokenizer.Token.CLOSE);

View File

@ -16,7 +16,8 @@ public class Tokenizer {
enum Token {
UNKNOWN, IDENT, AND, OR, XOR, BIN_NOT, OPEN, CLOSE, NUMBER, EOL, EOF, SHIFTLEFT, SHIFTRIGHT, COMMA, EQUAL,
ADD, SUB, MUL, GREATER, GREATEREQUAL, SMALER, SMALEREQUAL, DIV, MOD, END, LOOP, REPEAT, BITS, SEMICOLON, LET, LOG_NOT
ADD, SUB, MUL, GREATER, GREATEREQUAL, SMALER, SMALEREQUAL, DIV, MOD, END, LOOP, REPEAT, BITS, SEMICOLON,
LET, LOG_NOT, WHILE
}
private static HashMap<String, Token> statementMap = new HashMap<>();
@ -27,6 +28,7 @@ public class Tokenizer {
statementMap.put("repeat", Token.REPEAT);
statementMap.put("bits", Token.BITS);
statementMap.put("let", Token.LET);
statementMap.put("while", Token.WHILE);
}
private final Reader in;

View File

@ -2029,6 +2029,23 @@ loop(a,16)
end loop
</pre>
<p>Unter Umständen kann es notwendig sein, auf den Startzustand der Schaltung
reagieren zu können. Daher können die Signale, die in der Schaltung bereitgestellt werden,
innerhalb des Testfalls verwendet werden. Soll zum Beispiel ein Zähler, welcher in
einem undefinierten Zustand startet, getestet werden, kann dieser in einen definierten
Zustand getaktet werden:</p>
<pre>C Q_3 Q_2 Q_1 Q_0
# clock counter to 1111
while(!(Q_3 & Q_2 & Q_1 & Q_0))
C x x x x
end while
# start the test execution
repeat(16) C bits(4,n)
</pre>
<p>Unter Umständen ist die Erzeugung von Zufallszahlen in Testfällen hilfreich.
Diese können mit der Funktion 'random([n])' erzeugt werden. Die erzeugte Zahl ist
größer gleich Null und kleiner als [n]. Betrachtet man einen 16-Bit Multiplikator als Beispiel,

View File

@ -1982,6 +1982,22 @@ loop(a,16)
end loop
</pre>
<p>Under certain circumstances it may be necessary to be able to react to the initial
state of the circuit. Therefore the signals provided in the circuit can be used within
the test case. For example, if a counter that starts in an undefined state is to be
tested, it can be clocked to a defined state:</p>
<pre>C Q_3 Q_2 Q_1 Q_0
# clock counter to 1111
while(!(Q_3 & Q_2 & Q_1 & Q_0))
C x x x x
end while
# start the test execution
repeat(16) C bits(4,n)
</pre>
<p>It may be helpful to generate random numbers in test cases.
These can be created with the function 'random([n])'. The generated number is greater
than or equal to zero and less than [n]. Considering a 16-bit multiplier as an example,

View File

@ -43,8 +43,8 @@ public class TestExamples extends TestCase {
*/
public void testTestExamples() throws Exception {
File examples = new File(Resources.getRoot(), "/dig/test");
assertEquals(189, new FileScanner(this::check).scan(examples));
assertEquals(177, testCasesInFiles);
assertEquals(191, new FileScanner(this::check).scan(examples));
assertEquals(179, testCasesInFiles);
}
/**

View File

@ -10,6 +10,7 @@ import de.neemann.digital.data.Value;
import java.util.ArrayList;
/**
*
*/
public class LineCollector implements LineListener {
private final ArrayList<String> names;
@ -22,8 +23,12 @@ public class LineCollector implements LineListener {
}
public LineCollector(Parser parser) throws ParserException {
this(parser, new Context());
}
public LineCollector(Parser parser, Context context) throws ParserException {
this.list = new ArrayList<>();
parser.getLines().emitLines(this, new Context());
parser.getLines().emitLines(this, context);
names = parser.getNames();
}

View File

@ -5,6 +5,9 @@
*/
package de.neemann.digital.testing.parser;
import de.neemann.digital.core.Model;
import de.neemann.digital.core.ObservableValue;
import de.neemann.digital.core.Signal;
import de.neemann.digital.data.Value;
import de.neemann.digital.testing.TestingDataException;
import junit.framework.TestCase;
@ -191,4 +194,16 @@ public class ParserTest extends TestCase {
assertEquals(1, td.getLines().size());
}
public void test_modelInitState() throws IOException, ParserException {
Model model = new Model();
model.addSignal(new Signal("A", new ObservableValue("A", 3).setValue(2)));
model.addSignal(new Signal("B", new ObservableValue("B", 3).setValue(3)));
Parser parser = new Parser("A B Y\n" +
"let a=A+B;\n" +
"1 1 1\n#test").parse();
Context context = new Context().setModel(model);
LineCollector td = new LineCollector(parser, context);
assertEquals(5, context.getVar("a"));
}
}

View File

@ -0,0 +1,242 @@
<?xml version="1.0" encoding="utf-8"?>
<circuit>
<version>1</version>
<attributes/>
<visualElements>
<visualElement>
<elementName>T_FF</elementName>
<elementAttributes>
<entry>
<string>withEnable</string>
<boolean>false</boolean>
</entry>
</elementAttributes>
<pos x="420" y="160"/>
</visualElement>
<visualElement>
<elementName>T_FF</elementName>
<elementAttributes>
<entry>
<string>withEnable</string>
<boolean>false</boolean>
</entry>
<entry>
<string>inverterConfig</string>
<inverterConfig>
<string>C</string>
</inverterConfig>
</entry>
</elementAttributes>
<pos x="540" y="160"/>
</visualElement>
<visualElement>
<elementName>Clock</elementName>
<elementAttributes>
<entry>
<string>Label</string>
<string>C</string>
</entry>
</elementAttributes>
<pos x="400" y="160"/>
</visualElement>
<visualElement>
<elementName>T_FF</elementName>
<elementAttributes>
<entry>
<string>withEnable</string>
<boolean>false</boolean>
</entry>
<entry>
<string>inverterConfig</string>
<inverterConfig>
<string>C</string>
</inverterConfig>
</entry>
</elementAttributes>
<pos x="660" y="160"/>
</visualElement>
<visualElement>
<elementName>T_FF</elementName>
<elementAttributes>
<entry>
<string>withEnable</string>
<boolean>false</boolean>
</entry>
<entry>
<string>inverterConfig</string>
<inverterConfig>
<string>C</string>
</inverterConfig>
</entry>
</elementAttributes>
<pos x="780" y="160"/>
</visualElement>
<visualElement>
<elementName>T_FF</elementName>
<elementAttributes>
<entry>
<string>withEnable</string>
<boolean>false</boolean>
</entry>
<entry>
<string>inverterConfig</string>
<inverterConfig>
<string>C</string>
</inverterConfig>
</entry>
</elementAttributes>
<pos x="900" y="160"/>
</visualElement>
<visualElement>
<elementName>Out</elementName>
<elementAttributes>
<entry>
<string>rotation</string>
<rotation rotation="3"/>
</entry>
<entry>
<string>Label</string>
<string>Q_0</string>
</entry>
</elementAttributes>
<pos x="500" y="200"/>
</visualElement>
<visualElement>
<elementName>Out</elementName>
<elementAttributes>
<entry>
<string>rotation</string>
<rotation rotation="3"/>
</entry>
<entry>
<string>Label</string>
<string>Q_1</string>
</entry>
</elementAttributes>
<pos x="620" y="200"/>
</visualElement>
<visualElement>
<elementName>Out</elementName>
<elementAttributes>
<entry>
<string>rotation</string>
<rotation rotation="3"/>
</entry>
<entry>
<string>Label</string>
<string>Q_2</string>
</entry>
</elementAttributes>
<pos x="740" y="200"/>
</visualElement>
<visualElement>
<elementName>Out</elementName>
<elementAttributes>
<entry>
<string>rotation</string>
<rotation rotation="3"/>
</entry>
<entry>
<string>Label</string>
<string>Q_3</string>
</entry>
</elementAttributes>
<pos x="860" y="200"/>
</visualElement>
<visualElement>
<elementName>Out</elementName>
<elementAttributes>
<entry>
<string>rotation</string>
<rotation rotation="3"/>
</entry>
<entry>
<string>Label</string>
<string>Q_4</string>
</entry>
</elementAttributes>
<pos x="980" y="200"/>
</visualElement>
<visualElement>
<elementName>Testcase</elementName>
<elementAttributes>
<entry>
<string>Testdata</string>
<testData>
<dataString>C Q_4 Q_3 Q_2 Q_1 Q_0
# get the current model state and store it in n0
let n0=(Q_4&lt;&lt;4)|(Q_3&lt;&lt;3)|(Q_2&lt;&lt;2)|(Q_1&lt;&lt;1)|Q_0;
# start the test execution
repeat(32) C bits(5,n+n0+1)</dataString>
</testData>
</entry>
</elementAttributes>
<pos x="640" y="300"/>
</visualElement>
</visualElements>
<wires>
<wire>
<p1 x="480" y="160"/>
<p2 x="500" y="160"/>
</wire>
<wire>
<p1 x="400" y="160"/>
<p2 x="420" y="160"/>
</wire>
<wire>
<p1 x="600" y="160"/>
<p2 x="620" y="160"/>
</wire>
<wire>
<p1 x="720" y="160"/>
<p2 x="740" y="160"/>
</wire>
<wire>
<p1 x="840" y="160"/>
<p2 x="860" y="160"/>
</wire>
<wire>
<p1 x="960" y="160"/>
<p2 x="980" y="160"/>
</wire>
<wire>
<p1 x="620" y="160"/>
<p2 x="640" y="160"/>
</wire>
<wire>
<p1 x="860" y="160"/>
<p2 x="880" y="160"/>
</wire>
<wire>
<p1 x="500" y="160"/>
<p2 x="520" y="160"/>
</wire>
<wire>
<p1 x="740" y="160"/>
<p2 x="760" y="160"/>
</wire>
<wire>
<p1 x="500" y="160"/>
<p2 x="500" y="200"/>
</wire>
<wire>
<p1 x="740" y="160"/>
<p2 x="740" y="200"/>
</wire>
<wire>
<p1 x="980" y="160"/>
<p2 x="980" y="200"/>
</wire>
<wire>
<p1 x="620" y="160"/>
<p2 x="620" y="200"/>
</wire>
<wire>
<p1 x="860" y="160"/>
<p2 x="860" y="200"/>
</wire>
</wires>
<measurementOrdering/>
</circuit>

View File

@ -0,0 +1,245 @@
<?xml version="1.0" encoding="utf-8"?>
<circuit>
<version>1</version>
<attributes/>
<visualElements>
<visualElement>
<elementName>T_FF</elementName>
<elementAttributes>
<entry>
<string>withEnable</string>
<boolean>false</boolean>
</entry>
</elementAttributes>
<pos x="420" y="160"/>
</visualElement>
<visualElement>
<elementName>T_FF</elementName>
<elementAttributes>
<entry>
<string>withEnable</string>
<boolean>false</boolean>
</entry>
<entry>
<string>inverterConfig</string>
<inverterConfig>
<string>C</string>
</inverterConfig>
</entry>
</elementAttributes>
<pos x="540" y="160"/>
</visualElement>
<visualElement>
<elementName>Clock</elementName>
<elementAttributes>
<entry>
<string>Label</string>
<string>C</string>
</entry>
</elementAttributes>
<pos x="400" y="160"/>
</visualElement>
<visualElement>
<elementName>T_FF</elementName>
<elementAttributes>
<entry>
<string>withEnable</string>
<boolean>false</boolean>
</entry>
<entry>
<string>inverterConfig</string>
<inverterConfig>
<string>C</string>
</inverterConfig>
</entry>
</elementAttributes>
<pos x="660" y="160"/>
</visualElement>
<visualElement>
<elementName>T_FF</elementName>
<elementAttributes>
<entry>
<string>withEnable</string>
<boolean>false</boolean>
</entry>
<entry>
<string>inverterConfig</string>
<inverterConfig>
<string>C</string>
</inverterConfig>
</entry>
</elementAttributes>
<pos x="780" y="160"/>
</visualElement>
<visualElement>
<elementName>T_FF</elementName>
<elementAttributes>
<entry>
<string>withEnable</string>
<boolean>false</boolean>
</entry>
<entry>
<string>inverterConfig</string>
<inverterConfig>
<string>C</string>
</inverterConfig>
</entry>
</elementAttributes>
<pos x="900" y="160"/>
</visualElement>
<visualElement>
<elementName>Out</elementName>
<elementAttributes>
<entry>
<string>rotation</string>
<rotation rotation="3"/>
</entry>
<entry>
<string>Label</string>
<string>Q_0</string>
</entry>
</elementAttributes>
<pos x="500" y="200"/>
</visualElement>
<visualElement>
<elementName>Out</elementName>
<elementAttributes>
<entry>
<string>rotation</string>
<rotation rotation="3"/>
</entry>
<entry>
<string>Label</string>
<string>Q_1</string>
</entry>
</elementAttributes>
<pos x="620" y="200"/>
</visualElement>
<visualElement>
<elementName>Out</elementName>
<elementAttributes>
<entry>
<string>rotation</string>
<rotation rotation="3"/>
</entry>
<entry>
<string>Label</string>
<string>Q_2</string>
</entry>
</elementAttributes>
<pos x="740" y="200"/>
</visualElement>
<visualElement>
<elementName>Out</elementName>
<elementAttributes>
<entry>
<string>rotation</string>
<rotation rotation="3"/>
</entry>
<entry>
<string>Label</string>
<string>Q_3</string>
</entry>
</elementAttributes>
<pos x="860" y="200"/>
</visualElement>
<visualElement>
<elementName>Out</elementName>
<elementAttributes>
<entry>
<string>rotation</string>
<rotation rotation="3"/>
</entry>
<entry>
<string>Label</string>
<string>Q_4</string>
</entry>
</elementAttributes>
<pos x="980" y="200"/>
</visualElement>
<visualElement>
<elementName>Testcase</elementName>
<elementAttributes>
<entry>
<string>Testdata</string>
<testData>
<dataString>C Q_4 Q_3 Q_2 Q_1 Q_0
# clock counter to 11111
while(!(Q_4&amp;Q_3&amp;Q_2&amp;Q_1&amp;Q_0))
C x x x x x
end while
# start the test execution
repeat(32) C bits(5,n)</dataString>
</testData>
</entry>
</elementAttributes>
<pos x="640" y="300"/>
</visualElement>
</visualElements>
<wires>
<wire>
<p1 x="480" y="160"/>
<p2 x="500" y="160"/>
</wire>
<wire>
<p1 x="400" y="160"/>
<p2 x="420" y="160"/>
</wire>
<wire>
<p1 x="600" y="160"/>
<p2 x="620" y="160"/>
</wire>
<wire>
<p1 x="720" y="160"/>
<p2 x="740" y="160"/>
</wire>
<wire>
<p1 x="840" y="160"/>
<p2 x="860" y="160"/>
</wire>
<wire>
<p1 x="960" y="160"/>
<p2 x="980" y="160"/>
</wire>
<wire>
<p1 x="620" y="160"/>
<p2 x="640" y="160"/>
</wire>
<wire>
<p1 x="860" y="160"/>
<p2 x="880" y="160"/>
</wire>
<wire>
<p1 x="500" y="160"/>
<p2 x="520" y="160"/>
</wire>
<wire>
<p1 x="740" y="160"/>
<p2 x="760" y="160"/>
</wire>
<wire>
<p1 x="500" y="160"/>
<p2 x="500" y="200"/>
</wire>
<wire>
<p1 x="740" y="160"/>
<p2 x="740" y="200"/>
</wire>
<wire>
<p1 x="980" y="160"/>
<p2 x="980" y="200"/>
</wire>
<wire>
<p1 x="620" y="160"/>
<p2 x="620" y="200"/>
</wire>
<wire>
<p1 x="860" y="160"/>
<p2 x="860" y="200"/>
</wire>
</wires>
<measurementOrdering/>
</circuit>