Allows to set the simulator to a certain line in the a test result.

This commit is contained in:
hneemann 2020-11-26 13:19:15 +01:00
parent 34f305d3e3
commit 7c086d8796
6 changed files with 188 additions and 44 deletions

View File

@ -140,10 +140,20 @@ public class ValueTable extends Observable implements Iterable<TestRow> {
* @return the value stored at the given position
*/
public Value getTableValue(int rowIndex, int columnIndex) {
return getTableRow(rowIndex).getValue(columnIndex);
}
/**
* Returns a table row
*
* @param rowIndex the index of the table row
* @return the table row
*/
public TestRow getTableRow(int rowIndex) {
if (tableRowIndex == null)
return values.get(rowIndex).getValue(columnIndex);
return values.get(rowIndex);
else
return values.get(tableRowIndex.get(rowIndex)).getValue(columnIndex);
return values.get(tableRowIndex.get(rowIndex));
}
/**

View File

@ -6,6 +6,7 @@
package de.neemann.digital.data;
import de.neemann.digital.core.Observer;
import de.neemann.digital.testing.parser.TestRow;
import javax.swing.*;
import javax.swing.event.TableModelEvent;
@ -20,7 +21,7 @@ import java.util.ArrayList;
public class ValueTableModel implements TableModel, Observer {
private final ValueTable values;
private ArrayList<TableModelListener> listeners;
private final ArrayList<TableModelListener> listeners;
/**
* Creates a new table model
@ -99,4 +100,14 @@ public class ValueTableModel implements TableModel, Observer {
l.tableChanged(tme);
});
}
/**
* Returns a table row
*
* @param row the number of the table row
* @return the table row
*/
public TestRow getRow(int row) {
return values.getTableRow(row);
}
}

View File

@ -1137,7 +1137,7 @@ public final class Main extends JFrame implements ClosingWindowListener.ConfirmS
.setVisible(true);
ensureModelIsStopped();
} catch (NodeException | ElementNotFoundException | PinException | TestingDataException | RuntimeException e1) {
} catch (TestingDataException | RuntimeException e1) {
showError(Lang.get("msg_runningTestError"), e1);
}
}
@ -1589,6 +1589,26 @@ public final class Main extends JFrame implements ClosingWindowListener.ConfirmS
}
}
/**
* Starts the simulation and allows to advance the model to a certain state.
*
* @param advanceSimulator needs to be implemented to advance the model
*/
public void startSimulation(AdvanceSimulator advanceSimulator) {
SwingUtilities.invokeLater(() -> {
runModelState.enter(false, null);
SwingUtilities.invokeLater(() -> {
if (model != null && model.isRunning()) {
try {
advanceSimulator.advance(Main.this);
circuitComponent.graphicHasChanged();
} catch (Exception e) {
showError("Message", e);
}
}
});
});
}
@Override
public void hasChanged() {
@ -2113,4 +2133,17 @@ public final class Main extends JFrame implements ClosingWindowListener.ConfirmS
}
}
}
/**
* Allows to start the simulation and advance the model to a certain state.
*/
public interface AdvanceSimulator {
/**
* Advances the model to a certain state
*
* @param main main
* @throws Exception Exception
*/
void advance(Main main) throws Exception;
}
}

View File

@ -6,19 +6,19 @@
package de.neemann.digital.gui.components.testing;
import de.neemann.digital.core.ErrorDetector;
import de.neemann.digital.core.NodeException;
import de.neemann.digital.data.Value;
import de.neemann.digital.data.ValueTable;
import de.neemann.digital.data.ValueTableModel;
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.gui.Main;
import de.neemann.digital.gui.SaveAsHelper;
import de.neemann.digital.gui.components.data.GraphDialog;
import de.neemann.digital.lang.Lang;
import de.neemann.digital.testing.TestCaseDescription;
import de.neemann.digital.testing.TestExecutor;
import de.neemann.digital.testing.TestingDataException;
import de.neemann.digital.testing.parser.TestRow;
import de.neemann.gui.IconCreator;
import de.neemann.gui.LineBreaker;
import de.neemann.gui.MyFileChooser;
@ -29,6 +29,8 @@ import javax.swing.filechooser.FileNameExtensionFilter;
import javax.swing.table.DefaultTableCellRenderer;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.util.ArrayList;
import java.util.Collections;
@ -50,7 +52,7 @@ public class ValueTableDialog extends JDialog {
private static final Icon ICON_GRAPH = IconCreator.create("measurement-graph.png");
private final ArrayList<ValueTable> resultTableData;
private final ArrayList<ValueTableHolder> resultTableData;
private final JTabbedPane tp;
private final Window owner;
private final ToolTipAction asGraph;
@ -80,7 +82,7 @@ public class ValueTableDialog extends JDialog {
JFileChooser fileChooser = new MyFileChooser();
fileChooser.setFileFilter(new FileNameExtensionFilter("Comma Separated Values", "csv"));
new SaveAsHelper(ValueTableDialog.this, fileChooser, "csv")
.checkOverwrite(resultTableData.get(tab)::saveCSV);
.checkOverwrite(resultTableData.get(tab).valueTable::saveCSV);
}
}.setToolTip(Lang.get("menu_saveData_tt")).createJMenuItem());
@ -90,7 +92,7 @@ public class ValueTableDialog extends JDialog {
public void actionPerformed(ActionEvent actionEvent) {
int tab = tp.getSelectedIndex();
if (tab < 0) tab = 0;
new GraphDialog(ValueTableDialog.this, Lang.get("win_testdata_N", tp.getTitleAt(tab)), resultTableData.get(tab))
new GraphDialog(ValueTableDialog.this, Lang.get("win_testdata_N", tp.getTitleAt(tab)), resultTableData.get(tab).valueTable)
.disableTable()
.setVisible(true);
}
@ -113,12 +115,9 @@ public class ValueTableDialog extends JDialog {
* @param circuit the circuit
* @param library the library to use
* @return this for chained calls
* @throws NodeException NodeException
* @throws TestingDataException DataException
* @throws PinException PinException
* @throws ElementNotFoundException ElementNotFoundException
* @throws TestingDataException DataException
*/
public ValueTableDialog addTestResult(java.util.List<Circuit.TestCase> 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 {
Collections.sort(tsl);
int i = 0;
int errorTabIndex = -1;
@ -142,10 +141,11 @@ public class ValueTableDialog extends JDialog {
if (testResult.toManyResults())
tabName += " " + Lang.get("msg_test_missingLines");
tp.addTab(tabName, tabIcon, new JScrollPane(createTable(testResult.getValueTable())));
ValueTableHolder vth = new ValueTableHolder(testResult.getValueTable(), ts.getTestCaseDescription());
tp.addTab(tabName, tabIcon, new JScrollPane(createTable(vth)));
if (testResult.toManyResults())
tp.setToolTipTextAt(i, new LineBreaker().toHTML().breakLines(Lang.get("msg_test_missingLines_tt")));
resultTableData.add(testResult.getValueTable());
resultTableData.add(vth);
i++;
errorDetector.check();
} catch (Exception e) {
@ -168,18 +168,32 @@ public class ValueTableDialog extends JDialog {
* @return this for chained calls
*/
public ValueTableDialog addValueTable(String name, ValueTable valueTable) {
tp.addTab(name, new JScrollPane(createTable(valueTable)));
resultTableData.add(valueTable);
tp.addTab(name, new JScrollPane(createTable(new ValueTableHolder(valueTable))));
resultTableData.add(new ValueTableHolder(valueTable));
pack();
setLocationRelativeTo(owner);
return this;
}
private JTable createTable(ValueTable valueTable) {
JTable table = new JTable(new ValueTableModel(valueTable));
private JTable createTable(ValueTableHolder valueTableHolder) {
ValueTableModel vtm = new ValueTableModel(valueTableHolder.valueTable);
JTable table = new JTable(vtm);
table.setDefaultRenderer(Value.class, new ValueRenderer());
table.setDefaultRenderer(Integer.class, new NumberRenderer());
table.addMouseListener(new MouseAdapter() {
@Override
public void mouseClicked(MouseEvent e) {
int r = table.getSelectedRow();
if (r > 0 && r < vtm.getRowCount() && valueTableHolder.testCaseDescription != null) {
TestRow row = vtm.getRow(r);
if (owner instanceof Main) {
Main main = (Main) owner;
main.startSimulation(m -> new TestExecutor(valueTableHolder.testCaseDescription, m.getModel()).executeTo(row.getRow()));
}
}
}
});
final Font font = table.getFont();
table.setRowHeight(font.getSize() * 6 / 5);
return table;
@ -233,4 +247,18 @@ public class ValueTableDialog extends JDialog {
}
}
private static final class ValueTableHolder {
private final ValueTable valueTable;
private final TestCaseDescription testCaseDescription;
private ValueTableHolder(ValueTable valueTable) {
this.valueTable = valueTable;
testCaseDescription = null;
}
private ValueTableHolder(ValueTable valueTable, TestCaseDescription testCaseDescription) {
this.valueTable = valueTable;
this.testCaseDescription = testCaseDescription;
}
}
}

View File

@ -37,6 +37,7 @@ public class TestExecutor {
private boolean errorOccurred;
private int failedCount;
private int passedCount;
private int rowCount;
private boolean toManyResults = false;
private ArrayList<TestSignal> inputs;
private ArrayList<TestSignal> outputs;
@ -85,15 +86,51 @@ public class TestExecutor {
lines = testCase.getLines();
}
/**
* Sets the model to the given row.
*
* @param row the row to advance the model to
* @throws TestingDataException DataException
* @throws ParserException ParserException
*/
public void executeTo(int row) throws TestingDataException, ParserException {
execute(new LineListener() {
private int r = row;
@Override
public void add(TestRow testRow) {
Value[] values = testRow.getValues();
Value[] res = new Value[values.length];
if (r >= 0) {
advanceModel(model, testRow, values, res);
r--;
}
}
}, false);
}
/**
* Creates the result by comparing the testing vector with the given model
*
* @return the result of the test execution
* @throws TestingDataException DataException
* @throws NodeException NodeException
* @throws ParserException ParserException
*/
public TestExecutor.Result execute() throws TestingDataException, NodeException, ParserException {
public TestExecutor.Result execute() throws TestingDataException, ParserException {
return execute(values -> checkRow(model, values), true);
}
/**
* Executes the test and sends all the test lines to the {@link LineListener} provided.
*
* @param lineListener the line listener to use
* @param closeModel if true the model is closed
* @return the result of the test execution
* @throws TestingDataException DataException
* @throws ParserException ParserException
*/
private TestExecutor.Result execute(LineListener lineListener, boolean closeModel) throws TestingDataException, ParserException {
try {
HashSet<String> usedSignals = new HashSet<>();
inputs = new ArrayList<>();
@ -160,11 +197,12 @@ public class TestExecutor {
errorOccurred = true;
}, ModelEventType.ERROR_OCCURRED);
lines.emitLines(new LineListenerResolveDontCare(values -> checkRow(model, values), inputs), context);
lines.emitLines(new LineListenerResolveDontCare(lineListener, inputs), context);
return new Result();
} finally {
model.close();
if (closeModel)
model.close();
}
}
@ -178,6 +216,30 @@ public class TestExecutor {
Value[] values = testRow.getValues();
Value[] res = new Value[values.length];
advanceModel(model, testRow, values, res);
boolean ok = true;
for (TestSignal out : outputs) {
MatchedValue matchedValue = new MatchedValue(values[out.index], out.value);
res[out.index] = matchedValue;
if (!matchedValue.isPassed())
ok = false;
}
if (ok)
passedCount++;
else
failedCount++;
if (visibleRows < (ok ? MAX_RESULTS : ERR_RESULTS)) {
visibleRows++;
results.add(new TestRow(res, testRow.getDescription()).setRow(rowCount));
rowCount++;
} else
toManyResults = true;
}
private void advanceModel(Model model, TestRow testRow, Value[] values, Value[] res) {
boolean clockIsUsed = false;
// set all values except the clocks
for (TestSignal in : inputs) {
@ -219,25 +281,6 @@ public class TestExecutor {
errorOccurred = true;
throw e;
}
boolean ok = true;
for (TestSignal out : outputs) {
MatchedValue matchedValue = new MatchedValue(values[out.index], out.value);
res[out.index] = matchedValue;
if (!matchedValue.isPassed())
ok = false;
}
if (ok)
passedCount++;
else
failedCount++;
if (visibleRows < (ok ? MAX_RESULTS : ERR_RESULTS)) {
visibleRows++;
results.add(new TestRow(res, testRow.getDescription()));
} else
toManyResults = true;
}
private void addClockRow(int cols, String description) {

View File

@ -13,6 +13,7 @@ import de.neemann.digital.data.Value;
public class TestRow {
private final Value[] values;
private final String description;
private int rowCount;
/**
* Creates a new instance
@ -58,4 +59,22 @@ public class TestRow {
public String getDescription() {
return description;
}
/**
* Sets the row number
*
* @param rowCount the number to set
* @return this for chained calls
*/
public TestRow setRow(int rowCount) {
this.rowCount = rowCount;
return this;
}
/**
* @return the row number of this row
*/
public int getRow() {
return rowCount;
}
}