feat(fls): write the topology of a .fls, loaded by Java as the original

topologyWriter.ts writes konfiguration.xml from Java's view of a project:
the version line, the node items (label position, device with name,
host label flags, interfaces or ports), the cable items and the
documentation items. What a device's constructor makes is changed through
getters, as XMLEncoder does, and more is added; fewer than it makes set the
list anew. Cable ends name the port objects the getters return (the writer
now names a getter's result, <void id="Port0" property="port"/>), and each
cable carries the nodes the editor draws it between.

Checked by Java against Java: every corpus file, read and written again,
loads in Java FILIUS (the oracle's loading method, --sha256 ties its result
to the written bytes) to the same devices, names, positions, interfaces,
cables and documentation as the original; so does a synthetic topology with
what the corpus never sets. The oracle and the reader now also report a
cable's drawing ends (JCablePanel ziel1/ziel2): in the corpus the nodes it
joins, for all 183 cables. Mutation probes rewrite the files and let Java
load each mutant.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-09-30 08:04:21 +00:00
co-authored by Claude Opus 5.5
parent 3795316fae
commit 314f696515
17 changed files with 16251 additions and 204 deletions
+21 -1
View File
@@ -6,6 +6,7 @@
import filius.gui.netzwerksicht.GUIDocuItem;
import filius.gui.netzwerksicht.GUIKabelItem;
import filius.gui.netzwerksicht.JCablePanel;
import filius.gui.netzwerksicht.GUIKnotenItem;
import filius.hardware.Kabel;
import filius.hardware.NetzwerkInterface;
@@ -72,7 +73,9 @@ public class FlsOracle {
for (Path file : files) {
String name = root.relativize(file).toString();
try {
result.put(name, read(file));
Map<String, Object> view = read(file);
if (args.length > 2 && args[2].equals("--sha256")) view.put("sha256", sha256(Files.readAllBytes(file)));
result.put(name, view);
} catch (Throwable error) {
Throwable cause = error instanceof java.lang.reflect.InvocationTargetException && error.getCause() != null
? error.getCause()
@@ -96,6 +99,19 @@ public class FlsOracle {
System.exit(0);
}
/** The position of an item in a list by identity, null when absent. */
static Integer indexOf(List<?> list, Object item) {
if (item == null) return null;
for (int index = 0; index < list.size(); index++) if (list.get(index) == item) return index;
return null;
}
static String sha256(byte[] bytes) throws Exception {
StringBuilder hex = new StringBuilder();
for (byte value : java.security.MessageDigest.getInstance("SHA-256").digest(bytes)) hex.append(String.format("%02x", value));
return hex.toString();
}
/**
* Loads the file twice. What FILIUS does not find in a file it rolls at
* random (MAC addresses, a switch's SSID) — such values differ between
@@ -208,6 +224,10 @@ public class FlsOracle {
}
json.put("ends", ends);
json.put("wireless", cable != null && cable.getWireless());
// The nodes the editor draws the cable between (JCablePanel).
JCablePanel panel = item.getKabelpanel();
json.put("panel", panel == null ? null
: Arrays.asList(indexOf(nodes, panel.getZiel1()), indexOf(nodes, panel.getZiel2())));
cableList.add(json);
}