feat: F4.7 the data exchange window, measured against Java's
One row per frame (its highest layer's), Java's detail tree, the table as Java's text file, "Tabellen löschen" for every card (clearCapture in the room), a tab per card with Java's title, Java's layer colours as tokens, the chosen frame and autoscroll per tab. New oracle ExchangeWindowRun. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,293 @@
|
||||
// Uses FILIUS' data exchange window (AggregatedExchangeDialog, the one
|
||||
// filius.ini chooses, with its details as MessageDetailsPanel, the default
|
||||
// without the layer visualization) on web-lan as a person does: the
|
||||
// browser's card opened before any traffic, a page fetched by name (DNS, then
|
||||
// HTTP), each row chosen and its details read, the table exported as text,
|
||||
// the server's card opened beside it and the first chosen again, "Tabellen
|
||||
// löschen" from the table's menu, a second fetch, the simulation stopped and
|
||||
// started again without the server (as if removed in the design) and the
|
||||
// window activated. After each step it writes what the window shows: the
|
||||
// tabs' titles, the chosen tab, each table's rows (all seven cells) and, for
|
||||
// comparison, the card's rows as Lauscher.getDaten gives them. The reference
|
||||
// for the editor's capture window (packages/core/test/exchangeWindow.test.ts).
|
||||
//
|
||||
// Usage: java -cp filius.jar:lib/*:classes ExchangeWindowRun <scenario.json> <output json>
|
||||
|
||||
import filius.gui.nachrichtensicht.AggregatedExchangeDialog;
|
||||
import filius.gui.nachrichtensicht.AggregatedMessageTable;
|
||||
import filius.hardware.knoten.Host;
|
||||
import filius.hardware.knoten.InternetKnoten;
|
||||
import filius.rahmenprogramm.nachrichten.Lauscher;
|
||||
import filius.software.system.Dateisystem;
|
||||
import filius.software.system.Datei;
|
||||
import filius.software.system.InternetKnotenBetriebssystem;
|
||||
import filius.software.www.WebBrowser;
|
||||
import filius.software.www.WebServer;
|
||||
|
||||
import java.awt.Component;
|
||||
import java.awt.Container;
|
||||
import java.awt.event.MouseEvent;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.FileOutputStream;
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.Method;
|
||||
import java.net.URL;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Enumeration;
|
||||
import java.util.Hashtable;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import javax.swing.JFrame;
|
||||
import javax.swing.JMenuItem;
|
||||
import javax.swing.JPanel;
|
||||
import javax.swing.JPopupMenu;
|
||||
import javax.swing.JTabbedPane;
|
||||
import javax.swing.JTree;
|
||||
import javax.swing.SwingUtilities;
|
||||
import javax.swing.table.TableModel;
|
||||
import javax.swing.tree.DefaultMutableTreeNode;
|
||||
|
||||
public class ExchangeWindowRun {
|
||||
static final String BROWSER = "00000000-0000-0000-0000-000000000201";
|
||||
static final String SERVER = "00000000-0000-0000-0000-000000000203";
|
||||
|
||||
static final List<Object> steps = new ArrayList<>();
|
||||
static ScenarioNetwork network;
|
||||
static AggregatedExchangeDialog dialog;
|
||||
|
||||
static Object field(Class<?> type, Object object, String name) throws Exception {
|
||||
Field field = type.getDeclaredField(name);
|
||||
field.setAccessible(true);
|
||||
return field.get(object);
|
||||
}
|
||||
|
||||
static JTabbedPane panel() throws Exception {
|
||||
return (JTabbedPane) field(AggregatedExchangeDialog.class, dialog, "exchangePanel");
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
static AggregatedMessageTable table(String mac) throws Exception {
|
||||
return ((Hashtable<String, AggregatedMessageTable>) field(panel().getClass(), panel(), "tabellen")).get(mac);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
static JPanel tab(String mac) throws Exception {
|
||||
return ((Hashtable<String, JPanel>) field(panel().getClass(), panel(), "openedTabs")).get(mac);
|
||||
}
|
||||
|
||||
static String mac(String node) {
|
||||
return ((InternetKnoten) network.nodes.get(node)).getNetzwerkInterfaces().get(0).getMac();
|
||||
}
|
||||
|
||||
static InternetKnotenBetriebssystem os(String node) {
|
||||
return network.system(node);
|
||||
}
|
||||
|
||||
static void onEdt(Runnable action) throws Exception {
|
||||
SwingUtilities.invokeAndWait(action);
|
||||
Thread.sleep(300);
|
||||
}
|
||||
|
||||
static List<List<String>> rows(TableModel model) {
|
||||
List<List<String>> rows = new ArrayList<>();
|
||||
for (int row = 0; row < model.getRowCount(); row++) {
|
||||
List<String> cells = new ArrayList<>();
|
||||
for (int column = 0; column < model.getColumnCount(); column++) {
|
||||
Object value = model.getValueAt(row, column);
|
||||
cells.add(value == null ? null : String.valueOf(value));
|
||||
}
|
||||
rows.add(cells);
|
||||
}
|
||||
return rows;
|
||||
}
|
||||
|
||||
static List<List<String>> lauscherRows(String mac, boolean withAddresses) {
|
||||
List<List<String>> rows = new ArrayList<>();
|
||||
for (Object[] row : Lauscher.getLauscher().getDaten(mac, withAddresses, 0)) {
|
||||
List<String> cells = new ArrayList<>();
|
||||
for (Object cell : row) cells.add(cell == null ? null : String.valueOf(cell));
|
||||
rows.add(cells);
|
||||
}
|
||||
return rows;
|
||||
}
|
||||
|
||||
/** The details below the table: each node of the tree, in order, as [depth, text]. */
|
||||
static List<Object> details(String mac) throws Exception {
|
||||
List<Object> nodes = new ArrayList<>();
|
||||
JTree tree = find(tab(mac), JTree.class);
|
||||
if (tree == null) return nodes;
|
||||
DefaultMutableTreeNode root = (DefaultMutableTreeNode) tree.getModel().getRoot();
|
||||
for (Enumeration<?> each = root.preorderEnumeration(); each.hasMoreElements();) {
|
||||
DefaultMutableTreeNode node = (DefaultMutableTreeNode) each.nextElement();
|
||||
List<Object> entry = new ArrayList<>();
|
||||
entry.add(node.getLevel());
|
||||
entry.add(String.valueOf(node.getUserObject()));
|
||||
nodes.add(entry);
|
||||
}
|
||||
return nodes;
|
||||
}
|
||||
|
||||
static <T> T find(Component component, Class<T> type) {
|
||||
if (type.isInstance(component)) return type.cast(component);
|
||||
if (component instanceof Container) {
|
||||
for (Component child : ((Container) component).getComponents()) {
|
||||
T found = find(child, type);
|
||||
if (found != null) return found;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
static Map<String, Object> window() throws Exception {
|
||||
Map<String, Object> shown = new LinkedHashMap<>();
|
||||
JTabbedPane tabs = panel();
|
||||
List<String> titles = new ArrayList<>();
|
||||
Method title = AggregatedExchangeComponentTitle.method();
|
||||
for (int index = 0; index < tabs.getTabCount(); index++) {
|
||||
Object component = tabs.getTabComponentAt(index);
|
||||
titles.add(component == null ? null : (String) title.invoke(component));
|
||||
}
|
||||
shown.put("titles", titles);
|
||||
shown.put("selected", tabs.getSelectedIndex());
|
||||
shown.put("visible", dialog.isVisible());
|
||||
Map<String, Object> tables = new LinkedHashMap<>();
|
||||
for (String node : new String[] { BROWSER, SERVER }) {
|
||||
AggregatedMessageTable table = table(mac(node));
|
||||
if (table == null) continue;
|
||||
Map<String, Object> each = new LinkedHashMap<>();
|
||||
each.put("rows", rows(table.getModel()));
|
||||
each.put("layerRows", lauscherRows(mac(node), true));
|
||||
tables.put(node, each);
|
||||
}
|
||||
shown.put("tables", tables);
|
||||
return shown;
|
||||
}
|
||||
|
||||
/** TabTitle.getTitle, a private class's method. */
|
||||
static final class AggregatedExchangeComponentTitle {
|
||||
static Method method() throws Exception {
|
||||
for (Class<?> inner : Class.forName("filius.gui.nachrichtensicht.AggregatedExchangePanel").getDeclaredClasses()) {
|
||||
if (inner.getSimpleName().equals("TabTitle")) {
|
||||
Method method = inner.getDeclaredMethod("getTitle");
|
||||
method.setAccessible(true);
|
||||
return method;
|
||||
}
|
||||
}
|
||||
throw new IllegalStateException("no TabTitle");
|
||||
}
|
||||
}
|
||||
|
||||
static void step(String name, Map<String, Object> shown) {
|
||||
Map<String, Object> result = new LinkedHashMap<>();
|
||||
result.put("step", name);
|
||||
result.put("at", System.currentTimeMillis() - network.start);
|
||||
result.putAll(shown);
|
||||
steps.add(result);
|
||||
}
|
||||
|
||||
static void fetch(WebBrowser browser, String url) throws Exception {
|
||||
browser.holeWebseite(new URL(url));
|
||||
Thread.sleep(9000);
|
||||
}
|
||||
|
||||
public static void main(String[] args) throws Exception {
|
||||
try {
|
||||
run(args);
|
||||
} catch (Throwable error) {
|
||||
error.printStackTrace();
|
||||
System.exit(1);
|
||||
}
|
||||
System.exit(0);
|
||||
}
|
||||
|
||||
static void run(String[] args) throws Exception {
|
||||
network = ScenarioNetwork.read(args[0]);
|
||||
os(SERVER).installApp("filius.software.www.WebServer", false);
|
||||
os(BROWSER).installApp("filius.software.www.WebBrowser", false);
|
||||
WebServer server = (WebServer) os(SERVER).holeSoftware("filius.software.www.WebServer");
|
||||
WebBrowser browser = (WebBrowser) os(BROWSER).holeSoftware("filius.software.www.WebBrowser");
|
||||
Dateisystem files = os(SERVER).getDateisystem();
|
||||
files.speicherDatei("/webserver", new Datei("text.txt", "txt", "Nur Text\nzwei Zeilen"));
|
||||
server.setAktiv(true);
|
||||
network.nodes.get(BROWSER).setName("Anna");
|
||||
((Host) network.nodes.get(SERVER)).setUseIPAsName(true);
|
||||
network.start();
|
||||
Thread.sleep(500);
|
||||
|
||||
JFrame frame = new JFrame();
|
||||
SwingUtilities.invokeAndWait(() -> {
|
||||
frame.setSize(1200, 900);
|
||||
frame.setVisible(true);
|
||||
dialog = AggregatedExchangeDialog.getInstance(frame);
|
||||
});
|
||||
onEdt(() -> {
|
||||
dialog.addTable(os(BROWSER), mac(BROWSER));
|
||||
dialog.setVisible(true);
|
||||
});
|
||||
step("open", window());
|
||||
|
||||
fetch(browser, "http://www.filius.de/text.txt");
|
||||
Map<String, Object> shown = window();
|
||||
// Each row chosen, the details below it.
|
||||
AggregatedMessageTable table = table(mac(BROWSER));
|
||||
List<Object> details = new ArrayList<>();
|
||||
for (int row = 0; row < table.getModel().getRowCount(); row++) {
|
||||
final int chosen = row;
|
||||
onEdt(() -> table.setRowSelectionInterval(chosen, chosen));
|
||||
details.add(details(mac(BROWSER)));
|
||||
}
|
||||
shown.put("details", details);
|
||||
shown.put("layered", lauscherRows(mac(BROWSER), false));
|
||||
ByteArrayOutputStream export = new ByteArrayOutputStream();
|
||||
table.writeToStream(export);
|
||||
shown.put("export", export.toString(StandardCharsets.UTF_8.name()));
|
||||
step("after the fetch", shown);
|
||||
|
||||
onEdt(() -> dialog.addTable(os(SERVER), mac(SERVER)));
|
||||
step("the server's card beside it", window());
|
||||
onEdt(() -> dialog.addTable(os(BROWSER), mac(BROWSER)));
|
||||
step("the browser's card again", window());
|
||||
|
||||
// "Tabellen löschen", the first entry of the table's menu (without the layer visualization).
|
||||
JPopupMenu menu = (JPopupMenu) field(AggregatedMessageTable.class, table, "menu");
|
||||
List<String> entries = new ArrayList<>();
|
||||
for (Component entry : menu.getComponents()) entries.add(((JMenuItem) entry).getText());
|
||||
JMenuItem clear = (JMenuItem) menu.getComponent(0);
|
||||
onEdt(() -> clear.dispatchEvent(new MouseEvent(clear, MouseEvent.MOUSE_PRESSED, System.currentTimeMillis(), 0, 1, 1, 1, false, MouseEvent.BUTTON1)));
|
||||
Map<String, Object> cleared = window();
|
||||
cleared.put("menu", entries);
|
||||
step("cleared", cleared);
|
||||
|
||||
fetch(browser, "http://www.filius.de/text.txt");
|
||||
step("fetched again", window());
|
||||
|
||||
// Stopped and started again (GUIContainer.closeDialogs hides the window, reopenDialogs shows it): the server's
|
||||
// computer as if removed in the design, its system not started again; then the window activated.
|
||||
network.stop();
|
||||
Thread.sleep(500);
|
||||
for (Map.Entry<String, filius.hardware.knoten.Knoten> node : network.nodes.entrySet()) {
|
||||
if (!node.getKey().equals(SERVER)) node.getValue().getSystemSoftware().starten();
|
||||
}
|
||||
Thread.sleep(500);
|
||||
Method clearUnavailable = panel().getClass().getDeclaredMethod("clearUnavailableComponents");
|
||||
clearUnavailable.setAccessible(true);
|
||||
onEdt(() -> {
|
||||
try {
|
||||
clearUnavailable.invoke(panel());
|
||||
} catch (Exception error) {
|
||||
throw new RuntimeException(error);
|
||||
}
|
||||
});
|
||||
step("started again without the server", window());
|
||||
network.stop();
|
||||
|
||||
Map<String, Object> output = new LinkedHashMap<>();
|
||||
output.put("steps", steps);
|
||||
try (FileOutputStream out = new FileOutputStream(args[1])) {
|
||||
out.write((FlsOracle.Json.write(output, "") + "\n").getBytes(StandardCharsets.UTF_8));
|
||||
}
|
||||
System.out.println("exchange window steps: " + steps.size());
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user