From 9ec6f522067d8f9241b18eed3fdfcdb4df98a400 Mon Sep 17 00:00:00 2001 From: hneemann Date: Thu, 7 Jul 2016 08:49:16 +0200 Subject: [PATCH] improved test result dialog --- .../gui/components/test/MatchedValue.java | 40 +++++++ .../digital/gui/components/test/TestData.java | 8 +- .../gui/components/test/TestDataParser.java | 11 +- .../gui/components/test/TestResult.java | 110 +++++++----------- .../gui/components/test/TestResultDialog.java | 49 +++++++- .../digital/gui/components/test/Value.java | 78 +++++++++++++ src/main/resources/lang/lang_de.xml | 1 + src/main/resources/lang/lang_en.xml | 1 + .../components/test/TestDataParserTest.java | 20 +++- .../gui/components/test/TestResultTest.java | 8 +- .../gui/components/test/ValueTest.java | 45 +++++++ 11 files changed, 281 insertions(+), 90 deletions(-) create mode 100644 src/main/java/de/neemann/digital/gui/components/test/MatchedValue.java create mode 100644 src/main/java/de/neemann/digital/gui/components/test/Value.java create mode 100644 src/test/java/de/neemann/digital/gui/components/test/ValueTest.java diff --git a/src/main/java/de/neemann/digital/gui/components/test/MatchedValue.java b/src/main/java/de/neemann/digital/gui/components/test/MatchedValue.java new file mode 100644 index 000000000..4dce03718 --- /dev/null +++ b/src/main/java/de/neemann/digital/gui/components/test/MatchedValue.java @@ -0,0 +1,40 @@ +package de.neemann.digital.gui.components.test; + +import de.neemann.digital.core.ObservableValue; +import de.neemann.digital.lang.Lang; + +/** + * A matched value. + * The value itself represents the result and the expected value is contained as a member variable + * + * @author hneemann + */ +public class MatchedValue extends Value { + private final Value expected; + + /** + * Creates a new instance + * + * @param expected the expected value + * @param found the found value + */ + public MatchedValue(Value expected, ObservableValue found) { + super(found); + this.expected = expected; + } + + /** + * @return true if test is passed + */ + public boolean isPassed() { + return isEqualTo(expected); + } + + @Override + public String toString() { + if (isPassed()) + return super.toString(); + else + return Lang.get("msg_testExp_N0_found_N1", expected, super.toString()); + } +} diff --git a/src/main/java/de/neemann/digital/gui/components/test/TestData.java b/src/main/java/de/neemann/digital/gui/components/test/TestData.java index 6879de3fb..393b96a06 100644 --- a/src/main/java/de/neemann/digital/gui/components/test/TestData.java +++ b/src/main/java/de/neemann/digital/gui/components/test/TestData.java @@ -8,7 +8,7 @@ import java.util.Iterator; * * @author hneemann */ -public class TestData implements Iterable { +public class TestData implements Iterable { /** * the default instance @@ -16,7 +16,7 @@ public class TestData implements Iterable { public static final TestData DEFAULT = new TestData(""); private String dataString; - private transient ArrayList lines; + private transient ArrayList lines; private transient ArrayList names; TestData(String data) { @@ -33,7 +33,7 @@ public class TestData implements Iterable { } @Override - public Iterator iterator() { + public Iterator iterator() { return lines.iterator(); } @@ -74,7 +74,7 @@ public class TestData implements Iterable { /** * @return the data lines */ - public ArrayList getLines() { + public ArrayList getLines() { check(); return lines; } diff --git a/src/main/java/de/neemann/digital/gui/components/test/TestDataParser.java b/src/main/java/de/neemann/digital/gui/components/test/TestDataParser.java index c1515966c..cbc803396 100644 --- a/src/main/java/de/neemann/digital/gui/components/test/TestDataParser.java +++ b/src/main/java/de/neemann/digital/gui/components/test/TestDataParser.java @@ -14,7 +14,7 @@ import java.util.StringTokenizer; public class TestDataParser { private final BufferedReader r; - private final ArrayList lines; + private final ArrayList lines; private final ArrayList names; private int lineNumber; @@ -46,7 +46,7 @@ public class TestDataParser { String line; while ((line = readNonEmptyLine(r)) != null) { - int[] row = new int[names.size()]; + Value[] row = new Value[names.size()]; tok = new StringTokenizer(line); int cols = tok.countTokens(); if (cols != names.size()) @@ -56,10 +56,7 @@ public class TestDataParser { String num = null; try { num = tok.nextToken(); - if (num.toUpperCase().equals("X")) - row[i] = -1; - else - row[i] = Integer.parseInt(num); + row[i]=new Value(num); } catch (NumberFormatException e) { throw new DataException(Lang.get("err_notANumber_N0_inLine_N1", num, lineNumber)); } @@ -86,7 +83,7 @@ public class TestDataParser { /** * @return Returns the data lines */ - public ArrayList getLines() { + public ArrayList getLines() { return lines; } diff --git a/src/main/java/de/neemann/digital/gui/components/test/TestResult.java b/src/main/java/de/neemann/digital/gui/components/test/TestResult.java index 10e0048e7..f7c90512f 100644 --- a/src/main/java/de/neemann/digital/gui/components/test/TestResult.java +++ b/src/main/java/de/neemann/digital/gui/components/test/TestResult.java @@ -2,6 +2,7 @@ package de.neemann.digital.gui.components.test; import de.neemann.digital.core.Model; import de.neemann.digital.core.NodeException; +import de.neemann.digital.core.ObservableValue; import de.neemann.digital.core.Signal; import de.neemann.digital.core.wiring.Clock; import de.neemann.digital.lang.Lang; @@ -16,10 +17,10 @@ import java.util.ArrayList; public class TestResult implements TableModel { private final ArrayList names; - private final ArrayList lines; - private final ArrayList values; - private ArrayList inputs; - private ArrayList outputs; + private final ArrayList lines; + private final ArrayList results; + private ArrayList inputs; + private ArrayList outputs; private boolean allPassed; /** @@ -29,8 +30,8 @@ public class TestResult implements TableModel { */ public TestResult(TestData testData) { names = testData.getNames(); - values = testData.getLines(); - lines = new ArrayList<>(); + lines = testData.getLines(); + results = new ArrayList<>(); } /** @@ -46,29 +47,33 @@ public class TestResult implements TableModel { inputs = new ArrayList<>(); outputs = new ArrayList<>(); for (Signal s : model.getInputs()) - inputs.add(new TestSignal(s, getIndexOf(s.getName()))); + inputs.add(new TestSignal(getIndexOf(s.getName()), s.getValue())); for (Clock c : model.getClocks()) - inputs.add(new TestClock(c, getIndexOf(c.getClockOutput().getName()))); + inputs.add(new TestSignal(getIndexOf(c.getClockOutput().getName()), c.getClockOutput())); for (Signal s : model.getOutputs()) - outputs.add(new TestSignal(s, getIndexOf(s.getName()))); + outputs.add(new TestSignal(getIndexOf(s.getName()), s.getValue())); model.init(); - for (int[] row : values) { - for (TestInput in : inputs) - in.setFrom(row); + for (Value[] row : lines) { + + MatchedValue[] res = new MatchedValue[row.length]; + + for (TestSignal in : inputs) { + row[in.index].setTo(in.value); + res[in.index]=new MatchedValue(row[in.index], in.value); + } model.doStep(); - boolean ok = true; - for (TestOutput out : outputs) { - if (!out.check(row)) { - ok = false; + for (TestSignal out : outputs) { + MatchedValue matchedValue = new MatchedValue(row[out.index], out.value); + res[out.index]= matchedValue; + if (!matchedValue.isPassed()) allPassed = false; - } } - lines.add(new Line(row, ok)); + results.add(res); } return this; @@ -107,75 +112,34 @@ public class TestResult implements TableModel { } } - private interface TestInput { - void setFrom(int[] row); - } - - private interface TestOutput { - boolean check(int[] row); - } - - private class TestSignal implements TestInput, TestOutput { - private final Signal s; + private class TestSignal { private final int index; + private final ObservableValue value; - TestSignal(Signal s, int index) { - this.s = s; + TestSignal(int index, ObservableValue value) { this.index = index; - } - - @Override - public void setFrom(int[] row) { - s.getValue().setValue(row[index]); - } - - @Override - public boolean check(int[] row) { - int val = row[index]; - return val < 0 || val == s.getValue().getValue(); + this.value = value; } } - private class TestClock implements TestInput { - private final Clock c; - private final int index; - - TestClock(Clock c, int index) { - this.c = c; - this.index = index; - } - - @Override - public void setFrom(int[] row) { - c.getClockOutput().setValue(row[index]); - } - } - - @Override public int getRowCount() { - return lines.size(); + return results.size(); } @Override public int getColumnCount() { - return names.size() + 1; + return names.size(); } @Override public String getColumnName(int columnIndex) { - if (columnIndex < names.size()) - return names.get(columnIndex); - else - return ""; + return names.get(columnIndex); } @Override public Class getColumnClass(int columnIndex) { - if (columnIndex < names.size()) - return Integer.class; - else - return Boolean.class; + return MatchedValue.class; } @Override @@ -185,7 +149,17 @@ public class TestResult implements TableModel { @Override public Object getValueAt(int rowIndex, int columnIndex) { - return lines.get(rowIndex).getCol(columnIndex); + return getValue(rowIndex, columnIndex); + } + + /** + * Returns the typed value + * @param rowIndex rowIndex + * @param columnIndex columnIndex + * @return the value + */ + public MatchedValue getValue(int rowIndex, int columnIndex) { + return results.get(rowIndex)[columnIndex]; } @Override diff --git a/src/main/java/de/neemann/digital/gui/components/test/TestResultDialog.java b/src/main/java/de/neemann/digital/gui/components/test/TestResultDialog.java index daaa9a253..c1cde662d 100644 --- a/src/main/java/de/neemann/digital/gui/components/test/TestResultDialog.java +++ b/src/main/java/de/neemann/digital/gui/components/test/TestResultDialog.java @@ -5,13 +5,27 @@ import de.neemann.digital.core.NodeException; import de.neemann.digital.lang.Lang; import javax.swing.*; +import javax.swing.table.DefaultTableCellRenderer; +import java.awt.*; import java.util.ArrayList; /** + * Dialog to show the test results. + * * @author hneemann */ public class TestResultDialog extends JDialog { + private static final Color FAILED_COLOR = new Color(255, 200, 200); + /** + * Creates a new result dialog. + * + * @param owner the parent frame + * @param tsl list of test sets + * @param model the model to test + * @throws NodeException NodeException + * @throws DataException DataException + */ public TestResultDialog(JFrame owner, ArrayList tsl, Model model) throws NodeException, DataException { super(owner, Lang.get("msg_testResult"), true); setDefaultCloseOperation(DISPOSE_ON_CLOSE); @@ -19,12 +33,16 @@ public class TestResultDialog extends JDialog { JTabbedPane tp = new JTabbedPane(); for (TestSet ts : tsl) { TestResult tr = new TestResult(ts.data).create(model); + + JTable table = new JTable(tr); + table.setDefaultRenderer(MatchedValue.class, new MyRenderer()); + String tabName; if (tr.isAllPassed()) tabName = Lang.get("msg_test_N_Passed", ts.name); else tabName = Lang.get("msg_test_N_Failed", ts.name); - tp.addTab(tabName, new JScrollPane(new JTable(tr))); + tp.addTab(tabName, new JScrollPane(table)); } getContentPane().add(tp); @@ -32,14 +50,43 @@ public class TestResultDialog extends JDialog { setLocationRelativeTo(owner); } + /** + * A TestSet contains the {@link TestData} and the name of the TestData. + * Is only a value bean + */ public static class TestSet { private final TestData 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(TestData data, String name) { this.data = data; this.name = name; } } + + private class MyRenderer extends DefaultTableCellRenderer { + + @Override + public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) { + JLabel comp = (JLabel) super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column); + MatchedValue v = (MatchedValue) value; + + comp.setText(v.toString()); + comp.setHorizontalAlignment(JLabel.CENTER); + + if (v.isPassed()) + comp.setBackground(Color.WHITE); + else + comp.setBackground(FAILED_COLOR); + + return comp; + } + } } diff --git a/src/main/java/de/neemann/digital/gui/components/test/Value.java b/src/main/java/de/neemann/digital/gui/components/test/Value.java new file mode 100644 index 000000000..a211dd780 --- /dev/null +++ b/src/main/java/de/neemann/digital/gui/components/test/Value.java @@ -0,0 +1,78 @@ +package de.neemann.digital.gui.components.test; + +import de.neemann.digital.core.ObservableValue; + +/** + * @author hneemann + */ +public class Value { + + private final long value; + private final boolean highZ; + private final boolean dontCare; + + public Value(ObservableValue ov) { + this.value = ov.getValue(); + this.highZ = ov.isHighZ(); + this.dontCare = false; + } + + public Value(String val) { + val = val.toUpperCase().trim(); + if (val.equals("X")) { + highZ = false; + value = 0; + dontCare = true; + } else { + dontCare = false; + if (val.equals("Z")) { + highZ = true; + value = 0; + } else { + highZ = false; + value = Long.parseLong(val); + } + } + } + + public boolean isEqualTo(ObservableValue ov) { + return isEqualTo(new Value(ov)); + } + + public boolean isEqualTo(Value v) { + if (this == v) return true; + if (v == null) return false; + + if (dontCare || v.dontCare) return true; + + if (highZ && v.highZ) return true; + if (highZ != v.highZ) return false; + + return value == v.value; + } + + @Override + public String toString() { + if (dontCare) + return "X"; + if (highZ) + return "Z"; + return ObservableValue.getHexString(value); + } + + public boolean isDontCare() { + return dontCare; + } + + public boolean isHighZ() { + return highZ; + } + + public long getValue() { + return value; + } + + public void setTo(ObservableValue ov) { + ov.set(value, isHighZ()); + } +} diff --git a/src/main/resources/lang/lang_de.xml b/src/main/resources/lang/lang_de.xml index e06d9f504..32fcf3522 100644 --- a/src/main/resources/lang/lang_de.xml +++ b/src/main/resources/lang/lang_de.xml @@ -365,6 +365,7 @@ Die Icons stammen aus dem Tango Desktop Project. Testergebnis {0}: ok {0}: Fehler + E: {0} / F: {1} Ok 180° diff --git a/src/main/resources/lang/lang_en.xml b/src/main/resources/lang/lang_en.xml index b9a9e2c0e..7538ff692 100644 --- a/src/main/resources/lang/lang_en.xml +++ b/src/main/resources/lang/lang_en.xml @@ -366,6 +366,7 @@ The icons are taken from the Tango Desktop Project. Test result {0} passed {0} failed + E: {0} / F: {1} Ok 180° diff --git a/src/test/java/de/neemann/digital/gui/components/test/TestDataParserTest.java b/src/test/java/de/neemann/digital/gui/components/test/TestDataParserTest.java index d4bcb7eaf..428ca837a 100644 --- a/src/test/java/de/neemann/digital/gui/components/test/TestDataParserTest.java +++ b/src/test/java/de/neemann/digital/gui/components/test/TestDataParserTest.java @@ -12,12 +12,20 @@ public class TestDataParserTest extends TestCase { assertEquals(2,td.getNames().size()); assertEquals(3,td.getLines().size()); - assertEquals(0, td.getLines().get(0)[0]); - assertEquals(1, td.getLines().get(0)[1]); - assertEquals(1, td.getLines().get(1)[0]); - assertEquals(0, td.getLines().get(1)[1]); - assertEquals(-1, td.getLines().get(2)[0]); - assertEquals(-1, td.getLines().get(2)[1]); + assertEquals(0, td.getLines().get(0)[0].getValue()); + assertFalse(td.getLines().get(0)[0].isDontCare()); + + assertEquals(1, td.getLines().get(0)[1].getValue()); + assertFalse(td.getLines().get(0)[1].isDontCare()); + + assertEquals(1, td.getLines().get(1)[0].getValue()); + assertFalse(td.getLines().get(1)[0].isDontCare()); + + assertEquals(0, td.getLines().get(1)[1].getValue()); + assertFalse(td.getLines().get(1)[0].isDontCare()); + + assertTrue(td.getLines().get(2)[0].isDontCare()); + assertTrue(td.getLines().get(2)[1].isDontCare()); } public void testMissingValue() { diff --git a/src/test/java/de/neemann/digital/gui/components/test/TestResultTest.java b/src/test/java/de/neemann/digital/gui/components/test/TestResultTest.java index 358af42ec..4c2312942 100644 --- a/src/test/java/de/neemann/digital/gui/components/test/TestResultTest.java +++ b/src/test/java/de/neemann/digital/gui/components/test/TestResultTest.java @@ -55,10 +55,10 @@ public class TestResultTest extends TestCase { + "1 1 0\n"); TestResult tr = new TestResult(data).create(model); assertFalse(tr.isAllPassed()); - assertEquals(true, tr.getValueAt(0,4)); - assertEquals(true, tr.getValueAt(1,4)); - assertEquals(true, tr.getValueAt(2,4)); - assertEquals(false, tr.getValueAt(3,4)); + assertEquals(true, tr.getValue(0,2).isPassed()); + assertEquals(true, tr.getValue(1,2).isPassed()); + assertEquals(true, tr.getValue(2,2).isPassed()); + assertEquals(false, tr.getValue(3,2).isPassed()); } public void testResultErrorDC() throws Exception { diff --git a/src/test/java/de/neemann/digital/gui/components/test/ValueTest.java b/src/test/java/de/neemann/digital/gui/components/test/ValueTest.java new file mode 100644 index 000000000..6aeb15aef --- /dev/null +++ b/src/test/java/de/neemann/digital/gui/components/test/ValueTest.java @@ -0,0 +1,45 @@ +package de.neemann.digital.gui.components.test; + +import junit.framework.TestCase; + +/** + * @author hneemann + */ +public class ValueTest extends TestCase { + + public void testSimple() throws Exception { + Value v = new Value("X"); + assertTrue(v.isDontCare()); + + v = new Value("Z"); + assertTrue(v.isHighZ()); + assertFalse(v.isDontCare()); + + v = new Value("8"); + assertFalse(v.isHighZ()); + assertFalse(v.isDontCare()); + assertEquals(8, v.getValue()); + } + + public void testCompare() throws Exception { + assertTrue(new Value("X").isEqualTo(new Value("X"))); + assertTrue(new Value("Z").isEqualTo(new Value("X"))); + assertTrue(new Value("X").isEqualTo(new Value("Z"))); + assertTrue(new Value("1").isEqualTo(new Value("X"))); + assertTrue(new Value("X").isEqualTo(new Value("1"))); + + assertTrue(new Value("Z").isEqualTo(new Value("Z"))); + assertFalse(new Value("Z").isEqualTo(new Value("1"))); + assertFalse(new Value("1").isEqualTo(new Value("Z"))); + + assertTrue(new Value("0").isEqualTo(new Value("0"))); + assertTrue(new Value("1").isEqualTo(new Value("1"))); + assertFalse(new Value("0").isEqualTo(new Value("1"))); + } + + public void testToString() throws Exception { + assertEquals("X",new Value("X").toString()); + assertEquals("Z",new Value("Z").toString()); + assertEquals("2",new Value("2").toString()); + } +} \ No newline at end of file