1 package net.mograsim.logic.model.am2900;
3 import static org.junit.jupiter.api.Assertions.fail;
5 import java.lang.reflect.Field;
6 import java.util.HashMap;
7 import java.util.HashSet;
8 import java.util.LinkedList;
9 import java.util.Objects;
10 import java.util.Queue;
12 import java.util.TreeSet;
14 import net.mograsim.logic.core.components.BitDisplay;
15 import net.mograsim.logic.core.components.ManualSwitch;
16 import net.mograsim.logic.core.timeline.Timeline;
17 import net.mograsim.logic.model.am2900.TestableCircuit.Result;
18 import net.mograsim.logic.model.model.ViewModel;
19 import net.mograsim.logic.model.model.ViewModelModifiable;
20 import net.mograsim.logic.model.model.components.GUIComponent;
21 import net.mograsim.logic.model.model.components.atomic.GUIBitDisplay;
22 import net.mograsim.logic.model.model.components.atomic.GUIManualSwitch;
23 import net.mograsim.logic.model.model.components.submodels.SubmodelComponent;
24 import net.mograsim.logic.model.model.wires.GUIWire;
25 import net.mograsim.logic.model.model.wires.Pin;
26 import net.mograsim.logic.model.modeladapter.LogicModelParameters;
27 import net.mograsim.logic.model.modeladapter.ViewLogicModelAdapter;
28 import net.mograsim.logic.model.serializing.IndirectGUIComponentCreator;
29 import net.mograsim.logic.model.util.ModellingTool;
31 public class TestEnvironmentHelper
33 private final TestableCircuit testEnvInstance;
34 private final Class<?> testEnvClass;
35 private final String modelId;
36 private Field componentField;
37 private Field timelineField;
39 private GUIComponent component;
40 private Timeline timeline;
41 private ViewModelModifiable viewModel;
42 private ModellingTool modellingTool;
43 private HashMap<String, GUIManualSwitch> idSwitchMap = new HashMap<>();
44 private HashMap<String, GUIBitDisplay> idDisplayMap = new HashMap<>();
46 private DebugState debug = DebugState.NO_DEBUG;
47 private Set<String> wireDebugChangeSet;
48 private boolean debugWires = false;
49 public int debugEventThreshold = 10_000;
50 public int debugEventCount = 500;
51 private int eventCounter;
53 public TestEnvironmentHelper(TestableCircuit testEnv, String modelId)
55 this.testEnvInstance = testEnv;
56 this.modelId = modelId;
57 this.testEnvClass = testEnvInstance.getClass();
58 for (Field f : testEnvClass.getDeclaredFields())
60 if (GUIComponent.class.isAssignableFrom(f.getType()))
63 componentField.setAccessible(true);
64 } else if (Timeline.class.isAssignableFrom(f.getType()))
67 timelineField.setAccessible(true);
70 if (componentField == null || timelineField == null)
71 throw new IllegalStateException("No component or timeline field found!");
74 public void setup(DebugState debug)
78 viewModel = new ViewModelModifiable();
79 modellingTool = ModellingTool.createFor(viewModel);
80 component = IndirectGUIComponentCreator.createComponent(viewModel, modelId);
81 setField(componentField, component);
83 component.getPins().values().forEach(this::extendModelPin);
86 LogicModelParameters params = new LogicModelParameters();
87 params.gateProcessTime = 50;
88 params.wireTravelTime = 10;
89 timeline = ViewLogicModelAdapter.convert(viewModel, params);
90 setField(timelineField, timeline);
92 // Bind switches/displays to this test class
93 component.getPins().values().forEach(this::bindModelPin);
95 if (debug == DebugState.DEBUG_AT_PERFORMANCE_COST)
99 timeline.addEventAddedListener(te -> eventCounter++);
102 private void extendModelPin(Pin p)
104 String javaIdentId = idToJavaIdentifier(p.name);
107 Field f = testEnvClass.getDeclaredField(javaIdentId);
108 Class<?> type = f.getType();
109 if (ManualSwitch.class.isAssignableFrom(type))
111 GUIManualSwitch gms = new GUIManualSwitch(viewModel, p.logicWidth);
112 modellingTool.connect(p, gms.getOutputPin());
113 idSwitchMap.put(p.name, gms);
114 } else if (BitDisplay.class.isAssignableFrom(type))
116 GUIBitDisplay gbd = new GUIBitDisplay(viewModel, p.logicWidth);
117 modellingTool.connect(p, gbd.getInputPin());
118 idDisplayMap.put(p.name, gbd);
119 } else if (SwitchWithDisplay.class.isAssignableFrom(type))
121 SwitchWithDisplay swd = new SwitchWithDisplay(viewModel, p);
125 fail("unkown field type " + type);
128 catch (NoSuchFieldException | SecurityException e)
134 private void bindModelPin(Pin p)
136 String javaIdentId = idToJavaIdentifier(p.name);
137 if (idDisplayMap.containsKey(p.name))
138 setField(javaIdentId, idDisplayMap.get(p.name).getBitDisplay());
139 if (idSwitchMap.containsKey(p.name))
140 setField(javaIdentId, idSwitchMap.get(p.name).getManualSwitch());
143 private void setupDebugging()
146 HashSet<GUIWire> wiresIncludingSubmodels = new HashSet<>();
147 Queue<ViewModel> modelsToIterate = new LinkedList<>();
148 modelsToIterate.add(viewModel);
149 while (modelsToIterate.size() > 0)
151 ViewModel model = modelsToIterate.poll();
152 wiresIncludingSubmodels.addAll(model.getWiresByName().values());
153 for (GUIComponent comp : model.getComponentsByName().values())
154 if (comp instanceof SubmodelComponent)
155 modelsToIterate.offer(((SubmodelComponent) comp).submodel);
157 System.out.println(wiresIncludingSubmodels.size());
158 viewModel.setRedrawHandler(() -> wiresIncludingSubmodels.forEach(w ->
162 wireDebugChangeSet.add(w.toString());
169 // Normal execution until completion or eventLimit
170 int eventLimit = debugEventThreshold;
173 while (eventCounter < eventLimit)
175 timeline.executeNext();
176 if (!timeline.hasNext())
177 return Result.SUCCESS;
180 // Start debugging if event limit is reached (if debug is active)
181 if (debug == DebugState.DEBUG_AT_PERFORMANCE_COST)
182 return debugThisRun();
184 return Result.OUT_OF_TIME;
187 private Result debugThisRun()
189 int eventLimit = debugEventThreshold;
191 wireDebugChangeSet = new TreeSet<>();
192 Set<String> oldChangeSet;
193 // observe wire changes to detect, if we are really stuck in an endless loop
196 eventLimit += debugEventCount;
197 oldChangeSet = wireDebugChangeSet;
198 wireDebugChangeSet = new TreeSet<>();
199 while (eventCounter < eventLimit)
201 timeline.executeNext();
202 if (!timeline.hasNext())
204 // no endless loop, but more events needed than expected
205 System.out.println("run() took longer than expected: " + eventCounter);
206 return Result.SUCCESS;
209 } while (!oldChangeSet.equals(wireDebugChangeSet));
210 // if stuck, abort execution and print wires
211 System.err.print("Problematic Wire updates:");
212 wireDebugChangeSet.forEach(System.out::println);
213 System.err.println("run() failed: " + eventCounter);
214 return Result.OUT_OF_TIME;
217 private static String idToJavaIdentifier(String s)
219 StringBuilder sb = new StringBuilder(s.length());
220 char c = s.charAt(0);
221 sb.append(Character.isJavaIdentifierStart(c) ? c : '_');
222 for (int i = 1; i < s.length(); i++)
223 sb.append(Character.isJavaIdentifierPart(c = s.charAt(i)) ? c : '_');
224 return sb.toString();
227 private <S> void setField(Field f, S value)
231 f.set(testEnvInstance, Objects.requireNonNull(value));
239 private <S> void setField(String name, S value)
243 Field f = testEnvClass.getDeclaredField(name);
244 f.setAccessible(true);
245 f.set(testEnvInstance, Objects.requireNonNull(value));
253 public enum DebugState
255 NO_DEBUG, DEBUG_AT_PERFORMANCE_COST;