Improved Test experience by a lot; added first tests for Am2904, Am2910
authorChristian Femers <femers@in.tum.de>
Mon, 2 Sep 2019 07:50:42 +0000 (09:50 +0200)
committerChristian Femers <femers@in.tum.de>
Mon, 2 Sep 2019 07:50:42 +0000 (09:50 +0200)
The current version of the tests includes a failureRule that displays
the state of the failed test in a plain SWT window that opens
automatically. This should make debugging a lot easier.

Some restructuring was done, too.

17 files changed:
net.mograsim.logic.model.am2900/test/net/mograsim/logic/model/am2900/SwitchWithDisplay.java [deleted file]
net.mograsim.logic.model.am2900/test/net/mograsim/logic/model/am2900/TestEnvironmentHelper.java [deleted file]
net.mograsim.logic.model.am2900/test/net/mograsim/logic/model/am2900/TestUtil.java [deleted file]
net.mograsim.logic.model.am2900/test/net/mograsim/logic/model/am2900/TestableCircuit.java
net.mograsim.logic.model.am2900/test/net/mograsim/logic/model/am2900/am2901/Am2901Test.java
net.mograsim.logic.model.am2900/test/net/mograsim/logic/model/am2900/am2901/TestableAm2901.java
net.mograsim.logic.model.am2900/test/net/mograsim/logic/model/am2900/am2901/TestableAm2901Impl.java
net.mograsim.logic.model.am2900/test/net/mograsim/logic/model/am2900/am2904/Am2904Test.java [new file with mode: 0644]
net.mograsim.logic.model.am2900/test/net/mograsim/logic/model/am2900/am2904/TestableAm2904.java
net.mograsim.logic.model.am2900/test/net/mograsim/logic/model/am2900/am2904/TestableAm2904Impl.java
net.mograsim.logic.model.am2900/test/net/mograsim/logic/model/am2900/am2910/Am2910Test.java [new file with mode: 0644]
net.mograsim.logic.model.am2900/test/net/mograsim/logic/model/am2900/am2910/Am2910Testbench.java
net.mograsim.logic.model.am2900/test/net/mograsim/logic/model/am2900/am2910/TestableAm2910Impl.java
net.mograsim.logic.model.am2900/test/net/mograsim/logic/model/am2900/util/DisplayStateOnFailure.java [new file with mode: 0644]
net.mograsim.logic.model.am2900/test/net/mograsim/logic/model/am2900/util/SwitchWithDisplay.java [new file with mode: 0644]
net.mograsim.logic.model.am2900/test/net/mograsim/logic/model/am2900/util/TestEnvironmentHelper.java [new file with mode: 0644]
net.mograsim.logic.model.am2900/test/net/mograsim/logic/model/am2900/util/TestUtil.java [new file with mode: 0644]

diff --git a/net.mograsim.logic.model.am2900/test/net/mograsim/logic/model/am2900/SwitchWithDisplay.java b/net.mograsim.logic.model.am2900/test/net/mograsim/logic/model/am2900/SwitchWithDisplay.java
deleted file mode 100644 (file)
index c5c3ab7..0000000
+++ /dev/null
@@ -1,65 +0,0 @@
-package net.mograsim.logic.model.am2900;
-
-import net.mograsim.logic.core.components.BitDisplay;
-import net.mograsim.logic.core.components.ManualSwitch;
-import net.mograsim.logic.core.types.BitVector;
-import net.mograsim.logic.model.model.ViewModelModifiable;
-import net.mograsim.logic.model.model.components.atomic.GUIBitDisplay;
-import net.mograsim.logic.model.model.components.atomic.GUIManualSwitch;
-import net.mograsim.logic.model.model.wires.Pin;
-import net.mograsim.logic.model.model.wires.WireCrossPoint;
-import net.mograsim.logic.model.util.ModellingTool;
-
-public class SwitchWithDisplay
-{
-       private final Pin pin;
-       private final GUIBitDisplay guiBitDisplay;
-       private final GUIManualSwitch guiManualSwitch;
-
-       public SwitchWithDisplay(ViewModelModifiable model, Pin target)
-       {
-               pin = target;
-               guiBitDisplay = new GUIBitDisplay(model, pin.logicWidth);
-               guiManualSwitch = new GUIManualSwitch(model, pin.logicWidth);
-
-               ModellingTool tool = ModellingTool.createFor(model);
-               WireCrossPoint crossPoint = new WireCrossPoint(model, pin.logicWidth);
-               tool.connect(guiBitDisplay.getInputPin(), crossPoint);
-               tool.connect(guiManualSwitch.getOutputPin(), crossPoint);
-       }
-
-       public final BitVector getDisplayedValue()
-       {
-               return guiBitDisplay.getBitDisplay().getDisplayedValue();
-       }
-
-       public final void setState(BitVector bits)
-       {
-               guiManualSwitch.getManualSwitch().setState(bits);
-       }
-
-       public final Pin getPin()
-       {
-               return pin;
-       }
-
-       public final BitDisplay getBitDisplay()
-       {
-               return guiBitDisplay.getBitDisplay();
-       }
-
-       public final ManualSwitch getManualSwitch()
-       {
-               return guiManualSwitch.getManualSwitch();
-       }
-
-       final GUIBitDisplay getGuiBitDisplay()
-       {
-               return guiBitDisplay;
-       }
-
-       final GUIManualSwitch getGuiManualSwitch()
-       {
-               return guiManualSwitch;
-       }
-}
diff --git a/net.mograsim.logic.model.am2900/test/net/mograsim/logic/model/am2900/TestEnvironmentHelper.java b/net.mograsim.logic.model.am2900/test/net/mograsim/logic/model/am2900/TestEnvironmentHelper.java
deleted file mode 100644 (file)
index 9ac4030..0000000
+++ /dev/null
@@ -1,257 +0,0 @@
-package net.mograsim.logic.model.am2900;
-
-import static org.junit.jupiter.api.Assertions.fail;
-
-import java.lang.reflect.Field;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.LinkedList;
-import java.util.Objects;
-import java.util.Queue;
-import java.util.Set;
-import java.util.TreeSet;
-
-import net.mograsim.logic.core.components.BitDisplay;
-import net.mograsim.logic.core.components.ManualSwitch;
-import net.mograsim.logic.core.timeline.Timeline;
-import net.mograsim.logic.model.am2900.TestableCircuit.Result;
-import net.mograsim.logic.model.model.ViewModel;
-import net.mograsim.logic.model.model.ViewModelModifiable;
-import net.mograsim.logic.model.model.components.GUIComponent;
-import net.mograsim.logic.model.model.components.atomic.GUIBitDisplay;
-import net.mograsim.logic.model.model.components.atomic.GUIManualSwitch;
-import net.mograsim.logic.model.model.components.submodels.SubmodelComponent;
-import net.mograsim.logic.model.model.wires.GUIWire;
-import net.mograsim.logic.model.model.wires.Pin;
-import net.mograsim.logic.model.modeladapter.LogicModelParameters;
-import net.mograsim.logic.model.modeladapter.ViewLogicModelAdapter;
-import net.mograsim.logic.model.serializing.IndirectGUIComponentCreator;
-import net.mograsim.logic.model.util.ModellingTool;
-
-public class TestEnvironmentHelper
-{
-       private final TestableCircuit testEnvInstance;
-       private final Class<?> testEnvClass;
-       private final String modelId;
-       private Field componentField;
-       private Field timelineField;
-
-       private GUIComponent component;
-       private Timeline timeline;
-       private ViewModelModifiable viewModel;
-       private ModellingTool modellingTool;
-       private HashMap<String, GUIManualSwitch> idSwitchMap = new HashMap<>();
-       private HashMap<String, GUIBitDisplay> idDisplayMap = new HashMap<>();
-
-       private DebugState debug = DebugState.NO_DEBUG;
-       private Set<String> wireDebugChangeSet;
-       private boolean debugWires = false;
-       public int debugEventThreshold = 10_000;
-       public int debugEventCount = 500;
-       private int eventCounter;
-
-       public TestEnvironmentHelper(TestableCircuit testEnv, String modelId)
-       {
-               this.testEnvInstance = testEnv;
-               this.modelId = modelId;
-               this.testEnvClass = testEnvInstance.getClass();
-               for (Field f : testEnvClass.getDeclaredFields())
-               {
-                       if (GUIComponent.class.isAssignableFrom(f.getType()))
-                       {
-                               componentField = f;
-                               componentField.setAccessible(true);
-                       } else if (Timeline.class.isAssignableFrom(f.getType()))
-                       {
-                               timelineField = f;
-                               timelineField.setAccessible(true);
-                       }
-               }
-               if (componentField == null || timelineField == null)
-                       throw new IllegalStateException("No component or timeline field found!");
-       }
-
-       public void setup(DebugState debug)
-       {
-               this.debug = debug;
-               // Create view model
-               viewModel = new ViewModelModifiable();
-               modellingTool = ModellingTool.createFor(viewModel);
-               component = IndirectGUIComponentCreator.createComponent(viewModel, modelId);
-               setField(componentField, component);
-
-               component.getPins().values().forEach(this::extendModelPin);
-
-               // Create logic model
-               LogicModelParameters params = new LogicModelParameters();
-               params.gateProcessTime = 50;
-               params.wireTravelTime = 10;
-               timeline = ViewLogicModelAdapter.convert(viewModel, params);
-               setField(timelineField, timeline);
-
-               // Bind switches/displays to this test class
-               component.getPins().values().forEach(this::bindModelPin);
-
-               if (debug == DebugState.DEBUG_AT_PERFORMANCE_COST)
-               {
-                       setupDebugging();
-               }
-               timeline.addEventAddedListener(te -> eventCounter++);
-       }
-
-       private void extendModelPin(Pin p)
-       {
-               String javaIdentId = idToJavaIdentifier(p.name);
-               try
-               {
-                       Field f = testEnvClass.getDeclaredField(javaIdentId);
-                       Class<?> type = f.getType();
-                       if (ManualSwitch.class.isAssignableFrom(type))
-                       {
-                               GUIManualSwitch gms = new GUIManualSwitch(viewModel, p.logicWidth);
-                               modellingTool.connect(p, gms.getOutputPin());
-                               idSwitchMap.put(p.name, gms);
-                       } else if (BitDisplay.class.isAssignableFrom(type))
-                       {
-                               GUIBitDisplay gbd = new GUIBitDisplay(viewModel, p.logicWidth);
-                               modellingTool.connect(p, gbd.getInputPin());
-                               idDisplayMap.put(p.name, gbd);
-                       } else if (SwitchWithDisplay.class.isAssignableFrom(type))
-                       {
-                               SwitchWithDisplay swd = new SwitchWithDisplay(viewModel, p);
-                               setField(f, swd);
-                       } else
-                       {
-                               fail("unkown field type " + type);
-                       }
-               }
-               catch (NoSuchFieldException | SecurityException e)
-               {
-                       fail(e);
-               }
-       }
-
-       private void bindModelPin(Pin p)
-       {
-               String javaIdentId = idToJavaIdentifier(p.name);
-               if (idDisplayMap.containsKey(p.name))
-                       setField(javaIdentId, idDisplayMap.get(p.name).getBitDisplay());
-               if (idSwitchMap.containsKey(p.name))
-                       setField(javaIdentId, idSwitchMap.get(p.name).getManualSwitch());
-       }
-
-       private void setupDebugging()
-       {
-               // Debug code
-               HashSet<GUIWire> wiresIncludingSubmodels = new HashSet<>();
-               Queue<ViewModel> modelsToIterate = new LinkedList<>();
-               modelsToIterate.add(viewModel);
-               while (modelsToIterate.size() > 0)
-               {
-                       ViewModel model = modelsToIterate.poll();
-                       wiresIncludingSubmodels.addAll(model.getWiresByName().values());
-                       for (GUIComponent comp : model.getComponentsByName().values())
-                               if (comp instanceof SubmodelComponent)
-                                       modelsToIterate.offer(((SubmodelComponent) comp).submodel);
-               }
-               System.out.println(wiresIncludingSubmodels.size());
-               viewModel.setRedrawHandler(() -> wiresIncludingSubmodels.forEach(w ->
-               {
-                       if (debugWires)
-                       {
-                               wireDebugChangeSet.add(w.toString());
-                       }
-               }));
-       }
-
-       public Result run()
-       {
-               // Normal execution until completion or eventLimit
-               int eventLimit = debugEventThreshold;
-               eventCounter = 0;
-               debugWires = false;
-               while (eventCounter < eventLimit)
-               {
-                       timeline.executeNext();
-                       if (!timeline.hasNext())
-                               return Result.SUCCESS;
-               }
-
-               // Start debugging if event limit is reached (if debug is active)
-               if (debug == DebugState.DEBUG_AT_PERFORMANCE_COST)
-                       return debugThisRun();
-
-               return Result.OUT_OF_TIME;
-       }
-
-       private Result debugThisRun()
-       {
-               int eventLimit = debugEventThreshold;
-               debugWires = true;
-               wireDebugChangeSet = new TreeSet<>();
-               Set<String> oldChangeSet;
-               // observe wire changes to detect, if we are really stuck in an endless loop
-               do
-               {
-                       eventLimit += debugEventCount;
-                       oldChangeSet = wireDebugChangeSet;
-                       wireDebugChangeSet = new TreeSet<>();
-                       while (eventCounter < eventLimit)
-                       {
-                               timeline.executeNext();
-                               if (!timeline.hasNext())
-                               {
-                                       // no endless loop, but more events needed than expected
-                                       System.out.println("run() took longer than expected: " + eventCounter);
-                                       return Result.SUCCESS;
-                               }
-                       }
-               } while (!oldChangeSet.equals(wireDebugChangeSet));
-               // if stuck, abort execution and print wires
-               System.err.print("Problematic Wire updates:");
-               wireDebugChangeSet.forEach(System.out::println);
-               System.err.println("run() failed: " + eventCounter);
-               return Result.OUT_OF_TIME;
-       }
-
-       private static String idToJavaIdentifier(String s)
-       {
-               StringBuilder sb = new StringBuilder(s.length());
-               char c = s.charAt(0);
-               sb.append(Character.isJavaIdentifierStart(c) ? c : '_');
-               for (int i = 1; i < s.length(); i++)
-                       sb.append(Character.isJavaIdentifierPart(c = s.charAt(i)) ? c : '_');
-               return sb.toString();
-       }
-
-       private <S> void setField(Field f, S value)
-       {
-               try
-               {
-                       f.set(testEnvInstance, Objects.requireNonNull(value));
-               }
-               catch (Exception e)
-               {
-                       fail(e);
-               }
-       }
-
-       private <S> void setField(String name, S value)
-       {
-               try
-               {
-                       Field f = testEnvClass.getDeclaredField(name);
-                       f.setAccessible(true);
-                       f.set(testEnvInstance, Objects.requireNonNull(value));
-               }
-               catch (Exception e)
-               {
-                       fail(e);
-               }
-       }
-
-       public enum DebugState
-       {
-               NO_DEBUG, DEBUG_AT_PERFORMANCE_COST;
-       }
-}
diff --git a/net.mograsim.logic.model.am2900/test/net/mograsim/logic/model/am2900/TestUtil.java b/net.mograsim.logic.model.am2900/test/net/mograsim/logic/model/am2900/TestUtil.java
deleted file mode 100644 (file)
index 46a4b92..0000000
+++ /dev/null
@@ -1,106 +0,0 @@
-package net.mograsim.logic.model.am2900;
-
-import static org.junit.jupiter.api.Assertions.fail;
-
-import java.lang.reflect.Field;
-import java.util.Objects;
-
-import net.mograsim.logic.core.types.Bit;
-import net.mograsim.logic.core.types.BitVector;
-import net.mograsim.logic.core.types.BitVector.BitVectorMutator;
-import net.mograsim.logic.model.am2900.am2901.TestableAm2901Impl;
-
-public final class TestUtil
-{
-       private TestUtil()
-       {
-
-       }
-
-       /**
-        * Transforms the last four bits of an int to a string that contains the binary ('1' and '0') representation of the 4 bits
-        * 
-        * @author Christian Femers
-        */
-       public static String to4bitBin(int x)
-       {
-               StringBuilder sb = new StringBuilder(4);
-               sb.append((x & 0b1000) == 0 ? '0' : '1');
-               sb.append((x & 0b0100) == 0 ? '0' : '1');
-               sb.append((x & 0b0010) == 0 ? '0' : '1');
-               sb.append((x & 0b0001) == 0 ? '0' : '1');
-               return sb.toString();
-       }
-
-       /**
-        * Transforms the given boolean to a string that contains the binary ('1' and '0') representation of the bit
-        * 
-        * @author Christian Femers
-        */
-       public static String to1bitBin(boolean bitIsSet)
-       {
-               return bitIsSet ? "1" : "0";
-       }
-
-       /**
-        * Transforms the given int to a string that contains the binary ('1' and '0') representation of the int. "0" is only returned when the
-        * int is equal to zero.
-        * 
-        * @author Christian Femers
-        */
-       public static String to1bitBin(int someInt)
-       {
-               return someInt != 0 ? "1" : "0";
-       }
-
-       /**
-        * Transforms a 4 bit signed integer (-8 to 7) to a int representing the same number. (Adding leading 1-bits if the 4 bit int is
-        * negative)
-        * 
-        * @author Christian Femers
-        */
-       public static int signed4ToSigned32(int signed4bit)
-       {
-               if ((signed4bit & 0b1000) > 0)
-                       return signed4bit | 0xFF_FF_FF_F0;
-               return signed4bit & 0x00_00_00_0F;
-       }
-
-       /**
-        * Transforms a 16 bit signed integer (-32768 to 32767 - a short) to a int representing the same number. (Adding leading 1-bits if the
-        * 16 bit int is negative)
-        * 
-        * @author Christian Femers
-        */
-       public static int signed16ToSigned32(int signed16bit)
-       {
-               return (short) signed16bit;
-       }
-
-       /**
-        * Transforms the last n bits of an int to a string that contains the binary ('1' and '0') representation of the n bits
-        * 
-        * @author Christian Femers
-        */
-       public static String toNbitString(int x, int n)
-       {
-               StringBuilder sb = new StringBuilder(n);
-               for (int i = 0; i < n; i++)
-               {
-                       sb.append((x >> i) & 1);
-               }
-               return sb.reverse().toString();
-       }
-
-       public static BitVector of(int value, int length)
-       {
-               BitVectorMutator mutator = BitVectorMutator.ofLength(length);
-               int val = value;
-               for (int i = length - 1; i >= 0; i--)
-               {
-                       mutator.setMSBit(i, Bit.lastBitOf(val));
-                       val >>>= 1;
-               }
-               return mutator.toBitVector();
-       }
-}
index 4b1a5a6..d36d80f 100644 (file)
@@ -2,6 +2,8 @@ package net.mograsim.logic.model.am2900;
 
 import static org.junit.jupiter.api.Assertions.assertEquals;
 
+import net.mograsim.logic.model.am2900.util.TestEnvironmentHelper;
+
 public interface TestableCircuit
 {
        void setup();
@@ -10,6 +12,8 @@ public interface TestableCircuit
 
        void clockOn(boolean isClockOn);
 
+       TestEnvironmentHelper getTestEnvironmentHelper();
+
        default void assertRunSuccess()
        {
                assertEquals(Result.SUCCESS, run());
@@ -24,6 +28,11 @@ public interface TestableCircuit
                assertRunSuccess();
        }
 
+       default void displayState()
+       {
+               getTestEnvironmentHelper().displayState();
+       }
+
        public enum Result
        {
                SUCCESS, OUT_OF_TIME, ERROR;
index 57461c6..0d55c75 100644 (file)
@@ -1,10 +1,10 @@
 package net.mograsim.logic.model.am2900.am2901;
 
-import static net.mograsim.logic.model.am2900.TestUtil.*;
 import static net.mograsim.logic.model.am2900.am2901.TestableAm2901.Am2901_Dest.*;
 import static net.mograsim.logic.model.am2900.am2901.TestableAm2901.Am2901_Func.*;
 import static net.mograsim.logic.model.am2900.am2901.TestableAm2901.Am2901_Src.*;
 import static net.mograsim.logic.model.am2900.am2901.TestableAm2901.Register.*;
+import static net.mograsim.logic.model.am2900.util.TestUtil.*;
 import static org.junit.jupiter.api.Assertions.*;
 
 import java.awt.Point;
@@ -17,16 +17,21 @@ import org.junit.jupiter.api.Order;
 import org.junit.jupiter.api.MethodOrderer.OrderAnnotation;
 import org.junit.jupiter.api.Test;
 import org.junit.jupiter.api.TestMethodOrder;
+import org.junit.jupiter.api.extension.RegisterExtension;
 import org.junit.jupiter.params.ParameterizedTest;
 import org.junit.jupiter.params.provider.EnumSource;
 
 import net.mograsim.logic.model.am2900.am2901.TestableAm2901.Register;
+import net.mograsim.logic.model.am2900.util.DisplayStateOnFailure;
 
 @DisplayName("Am2901 Tests")
 @TestMethodOrder(OrderAnnotation.class)
 public class Am2901Test
 {
-       private TestableAm2901 am2901;
+       private TestableAm2901 am2901 = new TestableAm2901Impl();
+
+       @RegisterExtension
+       DisplayStateOnFailure failureRule = new DisplayStateOnFailure(am2901);
 
        @BeforeEach
        void initialize()
@@ -37,7 +42,6 @@ public class Am2901Test
 
        void createAndSetup()
        {
-               am2901 = new TestableAm2901Impl();
                am2901.setup();
        }
 
index ba915b3..d889908 100644 (file)
@@ -3,8 +3,8 @@ package net.mograsim.logic.model.am2900.am2901;
 import java.util.Arrays;
 import java.util.stream.Stream;
 
-import net.mograsim.logic.model.am2900.TestUtil;
 import net.mograsim.logic.model.am2900.TestableCircuit;
+import net.mograsim.logic.model.am2900.util.TestUtil;
 
 public interface TestableAm2901 extends TestableCircuit
 {
index 34c68c1..22ee120 100644 (file)
@@ -2,18 +2,16 @@ package net.mograsim.logic.model.am2900.am2901;
 
 import net.mograsim.logic.core.components.BitDisplay;
 import net.mograsim.logic.core.components.ManualSwitch;
-import net.mograsim.logic.core.timeline.Timeline;
 import net.mograsim.logic.core.types.Bit;
 import net.mograsim.logic.core.types.BitVector;
-import net.mograsim.logic.model.am2900.TestEnvironmentHelper;
-import net.mograsim.logic.model.am2900.TestEnvironmentHelper.DebugState;
-import net.mograsim.logic.model.am2900.TestUtil;
+import net.mograsim.logic.model.am2900.util.TestEnvironmentHelper;
+import net.mograsim.logic.model.am2900.util.TestUtil;
+import net.mograsim.logic.model.am2900.util.TestEnvironmentHelper.DebugState;
 import net.mograsim.logic.model.model.components.GUIComponent;
 
 public class TestableAm2901Impl implements TestableAm2901
 {
        private GUIComponent am2901;
-       private Timeline timeline;
        private ManualSwitch I8, I7, I6, I5, I4, I3, I2, I1, I0;
        private ManualSwitch C;
        private ManualSwitch Cn;
@@ -226,4 +224,10 @@ public class TestableAm2901Impl implements TestableAm2901
                        return "qreg.q";
                return "regs.c" + r.toBitString() + ".q";
        }
+
+       @Override
+       public TestEnvironmentHelper getTestEnvironmentHelper()
+       {
+               return testHelper;
+       }
 }
diff --git a/net.mograsim.logic.model.am2900/test/net/mograsim/logic/model/am2900/am2904/Am2904Test.java b/net.mograsim.logic.model.am2900/test/net/mograsim/logic/model/am2900/am2904/Am2904Test.java
new file mode 100644 (file)
index 0000000..cc255e0
--- /dev/null
@@ -0,0 +1,192 @@
+package net.mograsim.logic.model.am2900.am2904;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.DisplayName;
+import org.junit.jupiter.api.Order;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.TestMethodOrder;
+import org.junit.jupiter.api.extension.RegisterExtension;
+import org.junit.jupiter.api.MethodOrderer.OrderAnnotation;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.EnumSource;
+
+import net.mograsim.logic.model.am2900.am2904.TestableAm2904.Am2904_Carry;
+import net.mograsim.logic.model.am2900.am2904.TestableAm2904.Am2904_Inst;
+import net.mograsim.logic.model.am2900.am2904.TestableAm2904.Am2904_ShiftDir;
+import net.mograsim.logic.model.am2900.am2904.TestableAm2904.Register;
+import net.mograsim.logic.model.am2900.util.DisplayStateOnFailure;
+
+@DisplayName("Am2904 Tests")
+@TestMethodOrder(OrderAnnotation.class)
+public class Am2904Test
+{
+       private TestableAm2904 am2904 = new TestableAm2904Impl();
+
+       @RegisterExtension
+       DisplayStateOnFailure failureRule = new DisplayStateOnFailure(am2904);
+
+       @BeforeEach
+       void initialize()
+       {
+               createAndSetup();
+               setStandardInputs();
+       }
+
+       void createAndSetup()
+       {
+               am2904.setup();
+       }
+
+       void setStandardInputs()
+       {
+               am2904.set_CEµ("0");
+               am2904.set_CEM("0");
+               am2904.setI("0000");
+               am2904.set_E("0000");
+               am2904.set_OEY("1");
+               am2904.set_OECT("0");
+               am2904.setCarry(Am2904_Carry.CI0);
+               am2904.setCX("0");
+               am2904.setI10(Am2904_ShiftDir.RIGHT);
+               am2904.setShiftCode("0000");
+               am2904.setInstruction(Am2904_Inst.Load_Load_I_Z);
+               am2904.setSIO0("Z");
+               am2904.setSIO3("Z");
+               am2904.setQIO0("Z");
+               am2904.setQIO3("Z");
+               am2904.set_SE("1");
+               am2904.setY("ZZZZ");
+               am2904.clockOn(true);
+               am2904.assertRunSuccess();
+       }
+
+       @ParameterizedTest(name = "{0}")
+       @Order(1)
+       @DisplayName("Direct / high level access")
+       @EnumSource(Register.class)
+       void testDirectAccess(Register r)
+       {
+               assertEquals("U", am2904.getDirectly(r));
+
+               am2904.setDirectly(r, "1");
+
+               assertEquals("1", am2904.getDirectly(r));
+       }
+
+       @Test
+       @Order(2)
+       void testBasicStateAndOutputs()
+       {
+               am2904.assertFullCycleSuccess();
+
+               assertEquals("0", am2904.getC0());
+               assertEquals("0", am2904.getCT());
+               assertEquals("Z", am2904.getQIO0());
+               assertEquals("Z", am2904.getQIO3());
+               assertEquals("Z", am2904.getSIO0());
+               assertEquals("Z", am2904.getSIO3());
+               assertEquals("ZZZZ", am2904.getY());
+       }
+
+       @Test
+       @Order(3)
+       void testSimpleLoadTestIZ()
+       {
+               am2904.setInstruction(Am2904_Inst.Load_Load_I_Z);
+               am2904.assertFullCycleSuccess();
+
+               assertEquals("0", am2904.getCT());
+
+               am2904.setI("1000");
+               am2904.assertFullCycleSuccess();
+
+               assertEquals("1", am2904.getCT());
+
+               am2904.setInstruction(Am2904_Inst.Load_Load_I_notZ);
+               am2904.assertFullCycleSuccess();
+
+               assertEquals("0", am2904.getCT());
+
+               am2904.setI("0000");
+               am2904.assertFullCycleSuccess();
+
+               assertEquals("1", am2904.getCT());
+       }
+
+       @Test
+       @Order(3)
+       void testSimpleLoadTestIC()
+       {
+               am2904.setInstruction(Am2904_Inst.Load_Load_I_C);
+               am2904.assertFullCycleSuccess();
+
+               assertEquals("0", am2904.getCT());
+
+               am2904.setI("0100");
+               am2904.assertFullCycleSuccess();
+
+               assertEquals("1", am2904.getCT());
+
+               am2904.setInstruction(Am2904_Inst.Load_Load_I_notC);
+               am2904.assertFullCycleSuccess();
+
+               assertEquals("0", am2904.getCT());
+
+               am2904.setI("0000");
+               am2904.assertFullCycleSuccess();
+
+               assertEquals("1", am2904.getCT());
+       }
+
+       @Test
+       @Order(3)
+       void testSimpleLoadTestIN()
+       {
+               am2904.setInstruction(Am2904_Inst.Load_Load_I_N);
+               am2904.assertFullCycleSuccess();
+
+               assertEquals("0", am2904.getCT());
+
+               am2904.setI("0010");
+               am2904.assertFullCycleSuccess();
+
+               assertEquals("1", am2904.getCT());
+
+               am2904.setInstruction(Am2904_Inst.Load_Load_I_notN);
+               am2904.assertFullCycleSuccess();
+
+               assertEquals("0", am2904.getCT());
+
+               am2904.setI("0000");
+               am2904.assertFullCycleSuccess();
+
+               assertEquals("1", am2904.getCT());
+       }
+
+       @Test
+       @Order(3)
+       void testSimpleLoadTestIOVR()
+       {
+               am2904.setInstruction(Am2904_Inst.Load_Load_I_OVR);
+               am2904.assertFullCycleSuccess();
+
+               assertEquals("0", am2904.getCT());
+
+               am2904.setI("0001");
+               am2904.assertFullCycleSuccess();
+
+               assertEquals("1", am2904.getCT());
+
+               am2904.setInstruction(Am2904_Inst.Load_Load_I_notOVR);
+               am2904.assertFullCycleSuccess();
+
+               assertEquals("0", am2904.getCT());
+
+               am2904.setI("0000");
+               am2904.assertFullCycleSuccess();
+
+               assertEquals("1", am2904.getCT());
+       }
+}
\ No newline at end of file
index d9c9233..db58d56 100644 (file)
@@ -19,7 +19,7 @@ public interface TestableAm2904 extends TestableCircuit
 
        void setCX(String val_1_bit);
 
-       void setY3(String val_1_bit);
+       void setY(String ovr_n_c_z);
 
        void setIZ(String val_1_bit);
 
@@ -29,12 +29,12 @@ public interface TestableAm2904 extends TestableCircuit
 
        void setIN(String val_1_bit);
 
-       default void setI(String z_c_ovr_n)
+       default void setI(String z_c_n_ovr)
        {
-               setIZ(z_c_ovr_n.substring(0, 1));
-               setIC(z_c_ovr_n.substring(1, 2));
-               setIOVR(z_c_ovr_n.substring(2, 3));
-               setIN(z_c_ovr_n.substring(3, 4));
+               setIZ(z_c_n_ovr.substring(0, 1));
+               setIC(z_c_n_ovr.substring(1, 2));
+               setIN(z_c_n_ovr.substring(2, 3));
+               setIOVR(z_c_n_ovr.substring(3, 4));
        }
 
        void set_CEM(String val_1_bit);
@@ -55,12 +55,12 @@ public interface TestableAm2904 extends TestableCircuit
 
        void set_EN(String val_1_bit);
 
-       default void set_E(String z_c_ovr_n)
+       default void set_E(String z_c_n_ovr)
        {
-               set_EZ(z_c_ovr_n.substring(0, 1));
-               set_EC(z_c_ovr_n.substring(1, 2));
-               set_EOVR(z_c_ovr_n.substring(2, 3));
-               set_EN(z_c_ovr_n.substring(3, 4));
+               set_EZ(z_c_n_ovr.substring(0, 1));
+               set_EC(z_c_n_ovr.substring(1, 2));
+               set_EN(z_c_n_ovr.substring(2, 3));
+               set_EOVR(z_c_n_ovr.substring(3, 4));
        }
 
        void setSIO0(String val_1_bit);
@@ -71,11 +71,13 @@ public interface TestableAm2904 extends TestableCircuit
 
        void setQIO3(String val_1_bit);
 
+       void setDirectly(Register r, String val_1_bit);
+
        String getC0();
 
        String getCT();
 
-       String getY3();
+       String getY();
 
        String getSIO0();
 
@@ -85,6 +87,8 @@ public interface TestableAm2904 extends TestableCircuit
 
        String getQIO3();
 
+       String getDirectly(Register r);
+
        enum Am2904_ShiftDir
        {
                RIGHT, LEFT;
@@ -214,4 +218,9 @@ public interface TestableAm2904 extends TestableCircuit
                        return (code & 0b001_110) == 0b001_000;
                }
        }
+
+       enum Register
+       {
+               ÂµZ, ÂµC, ÂµN, ÂµOVR, MZ, MC, MN, MOVR;
+       }
 }
index eeaf9c0..e36ca6d 100644 (file)
 package net.mograsim.logic.model.am2900.am2904;
 
+import net.mograsim.logic.core.components.BitDisplay;
+import net.mograsim.logic.core.components.ManualSwitch;
+import net.mograsim.logic.core.types.Bit;
+import net.mograsim.logic.core.types.BitVector;
+import net.mograsim.logic.model.am2900.util.SwitchWithDisplay;
+import net.mograsim.logic.model.am2900.util.TestEnvironmentHelper;
+import net.mograsim.logic.model.am2900.util.TestEnvironmentHelper.DebugState;
+import net.mograsim.logic.model.model.components.GUIComponent;
+
 public class TestableAm2904Impl implements TestableAm2904
 {
 
+       private GUIComponent am2904;
+       private ManualSwitch I;
+       private ManualSwitch C;
+       private ManualSwitch Cx;
+       private ManualSwitch IC, IN, IOVR, IZ;
+       private ManualSwitch _CEM, _CEmu;
+       private ManualSwitch _EC, _EN, _EOVR, _EZ;
+       private ManualSwitch _OECT, _OEY;
+       private ManualSwitch _SE;
+       private BitDisplay C0;
+       private BitDisplay CT;
+       private SwitchWithDisplay SIO0, SIOn, QIO0, QIOn;
+       private SwitchWithDisplay YC, YN, YOVR, YZ;
+
+       private final TestEnvironmentHelper testHelper = new TestEnvironmentHelper(this, "file:components/am2904/GUIAm2904.json");
+
        @Override
        public void setup()
        {
-               // TODO Auto-generated method stub
-
+               testHelper.setup(DebugState.NO_DEBUG);
        }
 
        @Override
        public Result run()
        {
-               // TODO Auto-generated method stub
-               return null;
+               return testHelper.run();
        }
 
        @Override
        public void clockOn(boolean isClockOn)
        {
-               // TODO Auto-generated method stub
-
+               if (isClockOn)
+                       C.switchFullOn();
+               else
+                       C.switchFullOff();
        }
 
        @Override
        public void setInstruction(Am2904_Inst inst)
        {
-               // TODO Auto-generated method stub
-
+               var old = I.getValues();
+               var newPart = BitVector.from(inst.ordinal(), 6);
+               I.setState(old.subVector(0, 7).concat(newPart));
        }
 
        @Override
-       public void setCarry(Am2904_Carry carry)
+       public void setShiftCode(String val_4_bit)
        {
-               // TODO Auto-generated method stub
-
+               var old = I.getValues();
+               var newPart = BitVector.parse(val_4_bit);
+               I.setState(old.subVector(0, 3).concat(newPart).concat(old.subVector(7)));
        }
 
        @Override
-       public void setShiftCode(String val_4_bit)
+       public void setI10(Am2904_ShiftDir dir)
        {
-               // TODO Auto-generated method stub
-
+               var old = I.getValues();
+               var newPart = BitVector.from(dir.ordinal(), 1);
+               I.setState(old.subVector(0, 2).concat(newPart).concat(old.subVector(3)));
        }
 
        @Override
-       public void setI10(Am2904_ShiftDir dir)
+       public void setCarry(Am2904_Carry carry)
        {
-               // TODO Auto-generated method stub
-
+               var old = I.getValues();
+               var newPart = BitVector.from(carry.ordinal(), 2);
+               I.setState(newPart.concat(old.subVector(2)));
        }
 
        @Override
        public void setCX(String val_1_bit)
        {
-               // TODO Auto-generated method stub
-
+               Cx.setState(BitVector.parse(val_1_bit));
        }
 
        @Override
-       public void setY3(String val_1_bit)
+       public void setY(String ovr_n_c_z)
        {
-               // TODO Auto-generated method stub
-
+               var bv = BitVector.parse(ovr_n_c_z);
+               // correct order apparently unknown :/
+               YOVR.setState(bv.getLSBit(3).toVector());
+               YN.setState(bv.getLSBit(2).toVector());
+               YC.setState(bv.getLSBit(1).toVector());
+               YZ.setState(bv.getLSBit(0).toVector());
        }
 
        @Override
        public void setIZ(String val_1_bit)
        {
-               // TODO Auto-generated method stub
-
+               IZ.setState(BitVector.parse(val_1_bit));
        }
 
        @Override
        public void setIC(String val_1_bit)
        {
-               // TODO Auto-generated method stub
-
+               IC.setState(BitVector.parse(val_1_bit));
        }
 
        @Override
        public void setIOVR(String val_1_bit)
        {
-               // TODO Auto-generated method stub
-
+               IOVR.setState(BitVector.parse(val_1_bit));
        }
 
        @Override
        public void setIN(String val_1_bit)
        {
-               // TODO Auto-generated method stub
-
+               IN.setState(BitVector.parse(val_1_bit));
        }
 
        @Override
        public void set_CEM(String val_1_bit)
        {
-               // TODO Auto-generated method stub
-
+               _CEM.setState(BitVector.parse(val_1_bit));
        }
 
        @Override
        public void set_CEµ(String val_1_bit)
        {
-               // TODO Auto-generated method stub
-
+               _CEmu.setState(BitVector.parse(val_1_bit));
        }
 
        @Override
        public void set_OEY(String val_1_bit)
        {
-               // TODO Auto-generated method stub
-
+               _OEY.setState(BitVector.parse(val_1_bit));
        }
 
        @Override
        public void set_OECT(String val_1_bit)
        {
-               // TODO Auto-generated method stub
-
+               _OECT.setState(BitVector.parse(val_1_bit));
        }
 
        @Override
        public void set_SE(String val_1_bit)
        {
-               // TODO Auto-generated method stub
-
+               _SE.setState(BitVector.parse(val_1_bit));
        }
 
        @Override
        public void set_EZ(String val_1_bit)
        {
-               // TODO Auto-generated method stub
-
+               _EZ.setState(BitVector.parse(val_1_bit));
        }
 
        @Override
        public void set_EC(String val_1_bit)
        {
-               // TODO Auto-generated method stub
-
+               _EC.setState(BitVector.parse(val_1_bit));
        }
 
        @Override
        public void set_EOVR(String val_1_bit)
        {
-               // TODO Auto-generated method stub
-
+               _EOVR.setState(BitVector.parse(val_1_bit));
        }
 
        @Override
        public void set_EN(String val_1_bit)
        {
-               // TODO Auto-generated method stub
-
+               _EN.setState(BitVector.parse(val_1_bit));
        }
 
        @Override
        public void setSIO0(String val_1_bit)
        {
-               // TODO Auto-generated method stub
-
+               SIO0.setState(BitVector.parse(val_1_bit));
        }
 
        @Override
        public void setSIO3(String val_1_bit)
        {
-               // TODO Auto-generated method stub
-
+               SIOn.setState(BitVector.parse(val_1_bit));
        }
 
        @Override
        public void setQIO0(String val_1_bit)
        {
-               // TODO Auto-generated method stub
-
+               QIO0.setState(BitVector.parse(val_1_bit));
        }
 
        @Override
        public void setQIO3(String val_1_bit)
        {
-               // TODO Auto-generated method stub
+               QIOn.setState(BitVector.parse(val_1_bit));
+       }
 
+       @Override
+       public void setDirectly(Register r, String val_1_bit)
+       {
+               var bv = (BitVector) am2904.getHighLevelState(regToStateID(r));
+               bv = bv.withBitChanged(3 - r.ordinal() % 4, b -> Bit.parse(val_1_bit));
+               am2904.setHighLevelState(regToStateID(r), bv);
        }
 
        @Override
        public String getC0()
        {
-               // TODO Auto-generated method stub
-               return null;
+               return C0.getDisplayedValue().toString();
        }
 
        @Override
        public String getCT()
        {
-               // TODO Auto-generated method stub
-               return null;
+               return CT.getDisplayedValue().toString();
        }
 
        @Override
-       public String getY3()
+       public String getY()
        {
-               // TODO Auto-generated method stub
-               return null;
+               // correct order apparently unknown :/
+               var y3 = YOVR.getDisplayedValue();
+               var y2 = YN.getDisplayedValue();
+               var y1 = YC.getDisplayedValue();
+               var y0 = YZ.getDisplayedValue();
+               return y3.concat(y2).concat(y1).concat(y0).toString();
        }
 
        @Override
        public String getSIO0()
        {
-               // TODO Auto-generated method stub
-               return null;
+               return SIO0.getDisplayedValue().toString();
        }
 
        @Override
        public String getSIO3()
        {
-               // TODO Auto-generated method stub
-               return null;
+               return SIOn.getDisplayedValue().toString();
        }
 
        @Override
        public String getQIO0()
        {
-               // TODO Auto-generated method stub
-               return null;
+               return QIO0.getDisplayedValue().toString();
        }
 
        @Override
        public String getQIO3()
        {
-               // TODO Auto-generated method stub
-               return null;
+               return QIOn.getDisplayedValue().toString();
+       }
+
+       @Override
+       public String getDirectly(Register r)
+       {
+               var bv = (BitVector) am2904.getHighLevelState(regToStateID(r));
+               return bv.getLSBit(r.ordinal() % 4).getSymbol();
        }
 
+       private static String regToStateID(Register r)
+       {
+               if (r.ordinal() > 3)
+                       return "msr.q";
+               return "musr.q";
+       }
+
+       @Override
+       public TestEnvironmentHelper getTestEnvironmentHelper()
+       {
+               return testHelper;
+       }
 }
diff --git a/net.mograsim.logic.model.am2900/test/net/mograsim/logic/model/am2900/am2910/Am2910Test.java b/net.mograsim.logic.model.am2900/test/net/mograsim/logic/model/am2900/am2910/Am2910Test.java
new file mode 100644 (file)
index 0000000..18b2578
--- /dev/null
@@ -0,0 +1,69 @@
+package net.mograsim.logic.model.am2900.am2910;
+
+import static net.mograsim.logic.model.am2900.am2910.TestableAm2910.Am2910_Inst.*;
+import static net.mograsim.logic.core.types.Bit.*;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.DisplayName;
+import org.junit.jupiter.api.Order;
+import org.junit.jupiter.api.TestMethodOrder;
+import org.junit.jupiter.api.extension.RegisterExtension;
+import org.junit.jupiter.api.MethodOrderer.OrderAnnotation;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.EnumSource;
+
+import net.mograsim.logic.core.types.BitVector;
+import net.mograsim.logic.model.am2900.am2910.TestableAm2910.Register;
+import net.mograsim.logic.model.am2900.util.DisplayStateOnFailure;
+
+@DisplayName("Am2910 Tests")
+@TestMethodOrder(OrderAnnotation.class)
+public class Am2910Test
+{
+       private TestableAm2910 am2910 = new TestableAm2910Impl();
+
+       @RegisterExtension
+       DisplayStateOnFailure failureRule = new DisplayStateOnFailure(am2910);
+
+       @BeforeEach
+       void initialize()
+       {
+               createAndSetup();
+               setStandardInputs();
+       }
+
+       void createAndSetup()
+       {
+               am2910.setup();
+       }
+
+       void setStandardInputs()
+       {
+               am2910.set_CC("0");
+               am2910.set_CCEN("0");
+               am2910.set_OE("0");
+               am2910.set_RLD("1");
+               am2910.setCI("1");
+               am2910.setD("000000000000");
+               am2910.setInstruction(JZ);
+               am2910.clockOn(true);
+               am2910.assertRunSuccess();
+       }
+
+       @ParameterizedTest(name = "{0}")
+       @Order(1)
+       @DisplayName("Direct / high level access")
+       @EnumSource(Register.class)
+       void testDirectAccess(Register r)
+       {
+               String us = U.toVector(r.size()).toString();
+               String three = BitVector.from(3, r.size()).toString();
+
+               assertEquals(us, am2910.getDirectly(r));
+
+               am2910.setDirectly(r, three);
+
+               assertEquals(three, am2910.getDirectly(r));
+       }
+}
index 028de6e..5bf827d 100644 (file)
@@ -7,7 +7,10 @@ import net.mograsim.logic.model.SimpleLogicUIStandalone;
 import net.mograsim.logic.model.SimpleLogicUIStandalone.VisualisationObjects;
 import net.mograsim.logic.model.model.ViewModelModifiable;
 import net.mograsim.logic.model.model.components.GUIComponent;
+import net.mograsim.logic.model.model.components.Orientation;
 import net.mograsim.logic.model.model.components.atomic.GUIBitDisplay;
+import net.mograsim.logic.model.model.components.atomic.GUIClock;
+import net.mograsim.logic.model.model.components.atomic.GUIClock.GUIClockParams;
 import net.mograsim.logic.model.model.components.atomic.GUIManualSwitch;
 import net.mograsim.logic.model.model.wires.GUIWire;
 import net.mograsim.logic.model.serializing.IndirectGUIComponentCreator;
@@ -23,7 +26,7 @@ public class Am2910Testbench
        public static void create(ViewModelModifiable model)
        {
                GUIComponent am2910 = IndirectGUIComponentCreator.createComponent(model, "file:components/am2910/GUIAm2910.json", "Am2910");
-               GUIManualSwitch C = new GUIManualSwitch(model, 1, "C");
+               GUIClock C = new GUIClock(model, new GUIClockParams(1000, Orientation.RIGHT));
                GUIManualSwitch D = new GUIManualSwitch(model, 12, "D");
                GUIManualSwitch _RLD = new GUIManualSwitch(model, 1, "_RLD");
                GUIManualSwitch _CC = new GUIManualSwitch(model, 1, "_CC");
index 8b2cddf..53fd654 100644 (file)
@@ -2,17 +2,15 @@ package net.mograsim.logic.model.am2900.am2910;
 
 import net.mograsim.logic.core.components.BitDisplay;
 import net.mograsim.logic.core.components.ManualSwitch;
-import net.mograsim.logic.core.timeline.Timeline;
 import net.mograsim.logic.core.types.BitVector;
-import net.mograsim.logic.model.am2900.TestEnvironmentHelper;
-import net.mograsim.logic.model.am2900.TestEnvironmentHelper.DebugState;
+import net.mograsim.logic.model.am2900.util.TestEnvironmentHelper;
+import net.mograsim.logic.model.am2900.util.TestEnvironmentHelper.DebugState;
 import net.mograsim.logic.model.model.components.GUIComponent;
 
 public class TestableAm2910Impl implements TestableAm2910
 {
 
        private GUIComponent am2901;
-       private Timeline timeline;
        private ManualSwitch I;
        private ManualSwitch C;
        private ManualSwitch CI;
@@ -152,4 +150,10 @@ public class TestableAm2910Impl implements TestableAm2910
                        throw new IllegalArgumentException("unknown: " + r);
                }
        }
+
+       @Override
+       public TestEnvironmentHelper getTestEnvironmentHelper()
+       {
+               return testHelper;
+       }
 }
diff --git a/net.mograsim.logic.model.am2900/test/net/mograsim/logic/model/am2900/util/DisplayStateOnFailure.java b/net.mograsim.logic.model.am2900/test/net/mograsim/logic/model/am2900/util/DisplayStateOnFailure.java
new file mode 100644 (file)
index 0000000..3f68c67
--- /dev/null
@@ -0,0 +1,30 @@
+package net.mograsim.logic.model.am2900.util;
+
+import java.util.Objects;
+
+import org.junit.jupiter.api.extension.AfterTestExecutionCallback;
+import org.junit.jupiter.api.extension.ExtensionContext;
+
+import net.mograsim.logic.model.am2900.TestableCircuit;
+
+public class DisplayStateOnFailure implements AfterTestExecutionCallback
+{
+       public static final boolean ACTIVE = true;
+
+       private final TestableCircuit circuitUnderTest;
+
+       public DisplayStateOnFailure(TestableCircuit circuitUnderTest)
+       {
+               this.circuitUnderTest = Objects.requireNonNull(circuitUnderTest);
+       }
+
+       @Override
+       public void afterTestExecution(ExtensionContext context) throws Exception
+       {
+               if (ACTIVE && context.getExecutionException().isPresent())
+               {
+                       context.getExecutionException().get().printStackTrace();
+                       circuitUnderTest.displayState();
+               }
+       }
+}
diff --git a/net.mograsim.logic.model.am2900/test/net/mograsim/logic/model/am2900/util/SwitchWithDisplay.java b/net.mograsim.logic.model.am2900/test/net/mograsim/logic/model/am2900/util/SwitchWithDisplay.java
new file mode 100644 (file)
index 0000000..2591e3a
--- /dev/null
@@ -0,0 +1,65 @@
+package net.mograsim.logic.model.am2900.util;
+
+import net.mograsim.logic.core.components.BitDisplay;
+import net.mograsim.logic.core.components.ManualSwitch;
+import net.mograsim.logic.core.types.BitVector;
+import net.mograsim.logic.model.model.ViewModelModifiable;
+import net.mograsim.logic.model.model.components.atomic.GUIBitDisplay;
+import net.mograsim.logic.model.model.components.atomic.GUIManualSwitch;
+import net.mograsim.logic.model.model.wires.Pin;
+import net.mograsim.logic.model.model.wires.WireCrossPoint;
+import net.mograsim.logic.model.util.ModellingTool;
+
+public class SwitchWithDisplay
+{
+       private final Pin pin;
+       private final GUIBitDisplay guiBitDisplay;
+       private final GUIManualSwitch guiManualSwitch;
+
+       public SwitchWithDisplay(ViewModelModifiable model, Pin target)
+       {
+               pin = target;
+               guiBitDisplay = new GUIBitDisplay(model, pin.logicWidth);
+               guiManualSwitch = new GUIManualSwitch(model, pin.logicWidth);
+
+               ModellingTool tool = ModellingTool.createFor(model);
+               WireCrossPoint crossPoint = new WireCrossPoint(model, pin.logicWidth);
+               tool.connect(guiBitDisplay.getInputPin(), crossPoint);
+               tool.connect(guiManualSwitch.getOutputPin(), crossPoint);
+       }
+
+       public final BitVector getDisplayedValue()
+       {
+               return guiBitDisplay.getBitDisplay().getDisplayedValue();
+       }
+
+       public final void setState(BitVector bits)
+       {
+               guiManualSwitch.getManualSwitch().setState(bits);
+       }
+
+       public final Pin getPin()
+       {
+               return pin;
+       }
+
+       public final BitDisplay getBitDisplay()
+       {
+               return guiBitDisplay.getBitDisplay();
+       }
+
+       public final ManualSwitch getManualSwitch()
+       {
+               return guiManualSwitch.getManualSwitch();
+       }
+
+       final GUIBitDisplay getGuiBitDisplay()
+       {
+               return guiBitDisplay;
+       }
+
+       final GUIManualSwitch getGuiManualSwitch()
+       {
+               return guiManualSwitch;
+       }
+}
diff --git a/net.mograsim.logic.model.am2900/test/net/mograsim/logic/model/am2900/util/TestEnvironmentHelper.java b/net.mograsim.logic.model.am2900/test/net/mograsim/logic/model/am2900/util/TestEnvironmentHelper.java
new file mode 100644 (file)
index 0000000..ae4b28e
--- /dev/null
@@ -0,0 +1,274 @@
+package net.mograsim.logic.model.am2900.util;
+
+import static org.junit.jupiter.api.Assertions.fail;
+
+import java.lang.reflect.Field;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.LinkedList;
+import java.util.Objects;
+import java.util.Optional;
+import java.util.Queue;
+import java.util.Set;
+import java.util.TreeSet;
+
+import net.mograsim.logic.core.components.BitDisplay;
+import net.mograsim.logic.core.components.ManualSwitch;
+import net.mograsim.logic.core.timeline.Timeline;
+import net.mograsim.logic.model.LogicUIStandaloneGUI;
+import net.mograsim.logic.model.am2900.TestableCircuit;
+import net.mograsim.logic.model.am2900.TestableCircuit.Result;
+import net.mograsim.logic.model.model.ViewModel;
+import net.mograsim.logic.model.model.ViewModelModifiable;
+import net.mograsim.logic.model.model.components.GUIComponent;
+import net.mograsim.logic.model.model.components.atomic.GUIBitDisplay;
+import net.mograsim.logic.model.model.components.atomic.GUIManualSwitch;
+import net.mograsim.logic.model.model.components.submodels.SubmodelComponent;
+import net.mograsim.logic.model.model.wires.GUIWire;
+import net.mograsim.logic.model.model.wires.Pin;
+import net.mograsim.logic.model.modeladapter.LogicModelParameters;
+import net.mograsim.logic.model.modeladapter.ViewLogicModelAdapter;
+import net.mograsim.logic.model.serializing.IndirectGUIComponentCreator;
+import net.mograsim.logic.model.util.ModellingTool;
+
+public class TestEnvironmentHelper
+{
+       private final TestableCircuit testEnvInstance;
+       private final Class<?> testEnvClass;
+       private final String modelId;
+       private Field componentField;
+       private Optional<Field> timelineField = Optional.empty();
+
+       private GUIComponent component;
+       private Timeline timeline;
+       private ViewModelModifiable viewModel;
+       private ModellingTool modellingTool;
+       private HashMap<String, GUIManualSwitch> idSwitchMap = new HashMap<>();
+       private HashMap<String, GUIBitDisplay> idDisplayMap = new HashMap<>();
+
+       private DebugState debug = DebugState.NO_DEBUG;
+       private Set<String> wireDebugChangeSet;
+       private boolean debugWires = false;
+       public int debugEventThreshold = 10_000;
+       public int debugEventCount = 500;
+       private int eventCounter;
+
+       public TestEnvironmentHelper(TestableCircuit testEnv, String modelId)
+       {
+               this.testEnvInstance = testEnv;
+               this.modelId = modelId;
+               this.testEnvClass = testEnvInstance.getClass();
+               for (Field f : testEnvClass.getDeclaredFields())
+               {
+                       if (GUIComponent.class.isAssignableFrom(f.getType()))
+                       {
+                               componentField = f;
+                               componentField.setAccessible(true);
+                       } else if (Timeline.class.isAssignableFrom(f.getType()))
+                       {
+                               f.setAccessible(true);
+                               timelineField = Optional.of(f);
+                       }
+               }
+               if (componentField == null)
+                       throw new IllegalStateException("No component or timeline field found!");
+       }
+
+       public void setup(DebugState debug)
+       {
+               this.debug = debug;
+               // Create view model
+               viewModel = new ViewModelModifiable();
+               modellingTool = ModellingTool.createFor(viewModel);
+               component = IndirectGUIComponentCreator.createComponent(viewModel, modelId);
+               setField(componentField, component);
+
+               component.getPins().values().forEach(this::extendModelPin);
+
+               // Create logic model
+               LogicModelParameters params = new LogicModelParameters();
+               params.gateProcessTime = 50;
+               params.wireTravelTime = 10;
+               timeline = ViewLogicModelAdapter.convert(viewModel, params);
+               timelineField.ifPresent(f -> setField(f, timeline));
+
+               // Bind switches/displays to this test class
+               component.getPins().values().forEach(this::bindModelPin);
+
+               if (debug == DebugState.DEBUG_AT_PERFORMANCE_COST)
+               {
+                       setupDebugging();
+               }
+               timeline.addEventAddedListener(te -> eventCounter++);
+       }
+
+       private void extendModelPin(Pin p)
+       {
+               String javaIdentId = idToJavaIdentifier(p.name);
+               try
+               {
+                       Field f = testEnvClass.getDeclaredField(javaIdentId);
+                       Class<?> type = f.getType();
+                       if (ManualSwitch.class.isAssignableFrom(type))
+                       {
+                               GUIManualSwitch gms = new GUIManualSwitch(viewModel, p.logicWidth);
+                               modellingTool.connect(p, gms.getOutputPin());
+                               idSwitchMap.put(p.name, gms);
+                       } else if (BitDisplay.class.isAssignableFrom(type))
+                       {
+                               GUIBitDisplay gbd = new GUIBitDisplay(viewModel, p.logicWidth);
+                               modellingTool.connect(p, gbd.getInputPin());
+                               idDisplayMap.put(p.name, gbd);
+                       } else if (SwitchWithDisplay.class.isAssignableFrom(type))
+                       {
+                               SwitchWithDisplay swd = new SwitchWithDisplay(viewModel, p);
+                               setField(f, swd);
+                       } else
+                       {
+                               fail("unkown field type " + type);
+                       }
+               }
+               catch (NoSuchFieldException | SecurityException e)
+               {
+                       fail(e);
+               }
+       }
+
+       private void bindModelPin(Pin p)
+       {
+               String javaIdentId = idToJavaIdentifier(p.name);
+               if (idDisplayMap.containsKey(p.name))
+                       setField(javaIdentId, idDisplayMap.get(p.name).getBitDisplay());
+               if (idSwitchMap.containsKey(p.name))
+                       setField(javaIdentId, idSwitchMap.get(p.name).getManualSwitch());
+       }
+
+       private void setupDebugging()
+       {
+               // Debug code
+               HashSet<GUIWire> wiresIncludingSubmodels = new HashSet<>();
+               Queue<ViewModel> modelsToIterate = new LinkedList<>();
+               modelsToIterate.add(viewModel);
+               while (modelsToIterate.size() > 0)
+               {
+                       ViewModel model = modelsToIterate.poll();
+                       wiresIncludingSubmodels.addAll(model.getWiresByName().values());
+                       for (GUIComponent comp : model.getComponentsByName().values())
+                               if (comp instanceof SubmodelComponent)
+                                       modelsToIterate.offer(((SubmodelComponent) comp).submodel);
+               }
+               System.out.println(wiresIncludingSubmodels.size());
+               viewModel.setRedrawHandler(() -> wiresIncludingSubmodels.forEach(w ->
+               {
+                       if (debugWires)
+                       {
+                               wireDebugChangeSet.add(w.toString());
+                       }
+               }));
+       }
+
+       public Result run()
+       {
+               // Normal execution until completion or eventLimit
+               int eventLimit = debugEventThreshold;
+               eventCounter = 0;
+               debugWires = false;
+               while (eventCounter < eventLimit)
+               {
+                       timeline.executeNext();
+                       if (!timeline.hasNext())
+                               return Result.SUCCESS;
+               }
+
+               // Start debugging if event limit is reached (if debug is active)
+               if (debug == DebugState.DEBUG_AT_PERFORMANCE_COST)
+                       return debugThisRun();
+
+               return Result.OUT_OF_TIME;
+       }
+
+       private Result debugThisRun()
+       {
+               int eventLimit = debugEventThreshold;
+               debugWires = true;
+               wireDebugChangeSet = new TreeSet<>();
+               Set<String> oldChangeSet;
+               // observe wire changes to detect, if we are really stuck in an endless loop
+               do
+               {
+                       eventLimit += debugEventCount;
+                       oldChangeSet = wireDebugChangeSet;
+                       wireDebugChangeSet = new TreeSet<>();
+                       while (eventCounter < eventLimit)
+                       {
+                               timeline.executeNext();
+                               if (!timeline.hasNext())
+                               {
+                                       // no endless loop, but more events needed than expected
+                                       System.out.println("run() took longer than expected: " + eventCounter);
+                                       return Result.SUCCESS;
+                               }
+                       }
+               } while (!oldChangeSet.equals(wireDebugChangeSet));
+               // if stuck, abort execution and print wires
+               System.err.print("Problematic Wire updates:");
+               wireDebugChangeSet.forEach(System.out::println);
+               System.err.println("run() failed: " + eventCounter);
+               return Result.OUT_OF_TIME;
+       }
+
+       private static String idToJavaIdentifier(String s)
+       {
+               StringBuilder sb = new StringBuilder(s.length());
+               char c = s.charAt(0);
+               sb.append(Character.isJavaIdentifierStart(c) ? c : '_');
+               for (int i = 1; i < s.length(); i++)
+                       sb.append(Character.isJavaIdentifierPart(c = s.charAt(i)) ? c : '_');
+               return sb.toString();
+       }
+
+       private <S> void setField(Field f, S value)
+       {
+               try
+               {
+                       f.setAccessible(true);
+                       f.set(testEnvInstance, Objects.requireNonNull(value));
+               }
+               catch (Exception e)
+               {
+                       fail(e);
+               }
+       }
+
+       private <S> void setField(String name, S value)
+       {
+               try
+               {
+                       Field f = testEnvClass.getDeclaredField(name);
+                       f.setAccessible(true);
+                       f.set(testEnvInstance, Objects.requireNonNull(value));
+               }
+               catch (Exception e)
+               {
+                       fail(e);
+               }
+       }
+
+       public void displayState()
+       {
+               try
+               {
+                       new LogicUIStandaloneGUI(viewModel).run();
+                       viewModel.setRedrawHandler(null);
+               }
+               catch (Exception e)
+               {
+                       e.printStackTrace();
+               }
+       }
+
+       public enum DebugState
+       {
+               NO_DEBUG, DEBUG_AT_PERFORMANCE_COST;
+       }
+}
diff --git a/net.mograsim.logic.model.am2900/test/net/mograsim/logic/model/am2900/util/TestUtil.java b/net.mograsim.logic.model.am2900/test/net/mograsim/logic/model/am2900/util/TestUtil.java
new file mode 100644 (file)
index 0000000..5989dfb
--- /dev/null
@@ -0,0 +1,100 @@
+package net.mograsim.logic.model.am2900.util;
+
+import net.mograsim.logic.core.types.Bit;
+import net.mograsim.logic.core.types.BitVector;
+import net.mograsim.logic.core.types.BitVector.BitVectorMutator;
+
+public final class TestUtil
+{
+       private TestUtil()
+       {
+
+       }
+
+       /**
+        * Transforms the last four bits of an int to a string that contains the binary ('1' and '0') representation of the 4 bits
+        * 
+        * @author Christian Femers
+        */
+       public static String to4bitBin(int x)
+       {
+               StringBuilder sb = new StringBuilder(4);
+               sb.append((x & 0b1000) == 0 ? '0' : '1');
+               sb.append((x & 0b0100) == 0 ? '0' : '1');
+               sb.append((x & 0b0010) == 0 ? '0' : '1');
+               sb.append((x & 0b0001) == 0 ? '0' : '1');
+               return sb.toString();
+       }
+
+       /**
+        * Transforms the given boolean to a string that contains the binary ('1' and '0') representation of the bit
+        * 
+        * @author Christian Femers
+        */
+       public static String to1bitBin(boolean bitIsSet)
+       {
+               return bitIsSet ? "1" : "0";
+       }
+
+       /**
+        * Transforms the given int to a string that contains the binary ('1' and '0') representation of the int. "0" is only returned when the
+        * int is equal to zero.
+        * 
+        * @author Christian Femers
+        */
+       public static String to1bitBin(int someInt)
+       {
+               return someInt != 0 ? "1" : "0";
+       }
+
+       /**
+        * Transforms a 4 bit signed integer (-8 to 7) to a int representing the same number. (Adding leading 1-bits if the 4 bit int is
+        * negative)
+        * 
+        * @author Christian Femers
+        */
+       public static int signed4ToSigned32(int signed4bit)
+       {
+               if ((signed4bit & 0b1000) > 0)
+                       return signed4bit | 0xFF_FF_FF_F0;
+               return signed4bit & 0x00_00_00_0F;
+       }
+
+       /**
+        * Transforms a 16 bit signed integer (-32768 to 32767 - a short) to a int representing the same number. (Adding leading 1-bits if the
+        * 16 bit int is negative)
+        * 
+        * @author Christian Femers
+        */
+       public static int signed16ToSigned32(int signed16bit)
+       {
+               return (short) signed16bit;
+       }
+
+       /**
+        * Transforms the last n bits of an int to a string that contains the binary ('1' and '0') representation of the n bits
+        * 
+        * @author Christian Femers
+        */
+       public static String toNbitString(int x, int n)
+       {
+               StringBuilder sb = new StringBuilder(n);
+               for (int i = 0; i < n; i++)
+               {
+                       sb.append((x >> i) & 1);
+               }
+               return sb.reverse().toString();
+       }
+
+       public static BitVector of(int value, int length)
+       {
+               BitVectorMutator mutator = BitVectorMutator.ofLength(length);
+               int val = value;
+               for (int i = length - 1; i >= 0; i--)
+               {
+                       mutator.setMSBit(i, Bit.lastBitOf(val));
+                       val >>>= 1;
+               }
+               return mutator.toBitVector();
+       }
+}