feat(fls): file systems of PCs and notebooks as Java loads them
readSystemSoftware reads a host's file system: Java's tree of DefaultMutableTreeNodes, directories named by their user object, files as Datei (by setters or the three-argument constructor), rooted at arbeitsVerzeichnis (up to 1.10) or root. All 23 corpus files match Java, every file compared by name, type and the SHA-256 of its content. Routers and home routers have files in Java too, but their constructor makes them from templates (the start page of the firewall's web interface); they are not in the file and not claimed here. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -3,6 +3,7 @@
|
||||
// position and interfaces, each cable with both ends, each documentation
|
||||
// item — field by field. Then the rules the corpus does not exercise.
|
||||
|
||||
import { createHash } from 'node:crypto';
|
||||
import { readdirSync, readFileSync, statSync } from 'node:fs';
|
||||
import { dirname, join, relative } from 'node:path';
|
||||
import { fileURLToPath } from 'node:url';
|
||||
@@ -12,6 +13,7 @@ import {
|
||||
readFlsArchive,
|
||||
readTopology,
|
||||
type FlsDhcpServer,
|
||||
type FlsFileTree,
|
||||
type FlsModem,
|
||||
type FlsNodeClass,
|
||||
type FlsTopology,
|
||||
@@ -42,6 +44,25 @@ const TYPE_LABELS: Record<FlsNodeClass, string> = {
|
||||
Modem: 'Modem',
|
||||
};
|
||||
|
||||
/** FlsOracle.fileTree's shape: contents as length and SHA-256 of their UTF-8 bytes. */
|
||||
function javaFileTree(tree: FlsFileTree): unknown {
|
||||
const children = tree.children.map(javaFileTree);
|
||||
switch (tree.kind) {
|
||||
case 'directory':
|
||||
return { directory: tree.name, children };
|
||||
case 'file':
|
||||
return {
|
||||
file: tree.name,
|
||||
type: tree.type,
|
||||
length: tree.content?.length ?? null,
|
||||
sha256: tree.content === null ? null : createHash('sha256').update(tree.content, 'utf8').digest('hex'),
|
||||
children,
|
||||
};
|
||||
case 'other':
|
||||
return { userObject: tree.className, children };
|
||||
}
|
||||
}
|
||||
|
||||
/** Java's property names, as FlsOracle.java writes them. */
|
||||
function javaDhcpServer(server: FlsDhcpServer) {
|
||||
return {
|
||||
@@ -84,6 +105,7 @@ function asJavaView(topology: FlsTopology, javaBounds: (number[] | null)[]) {
|
||||
dhcpServer: javaDhcpServer(node.system.dhcpServer),
|
||||
}),
|
||||
...(node.system.modem === undefined ? {} : { modem: javaModem(node.system.modem) }),
|
||||
...(node.system.fileSystem === undefined ? {} : { fileSystem: javaFileTree(node.system.fileSystem) }),
|
||||
...(node.system.firewall === undefined
|
||||
? {}
|
||||
: {
|
||||
@@ -116,6 +138,23 @@ function asJavaView(topology: FlsTopology, javaBounds: (number[] | null)[]) {
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Java's view without the file systems of routers and home routers: their
|
||||
* files come from Java's constructor (templates of the firewall's web
|
||||
* interface), not from the .fls file, and the reader does not claim them.
|
||||
*/
|
||||
function withoutTemplateFiles(view: unknown): unknown {
|
||||
const { nodes, ...rest } = view as { nodes: { class: string; system: Record<string, unknown> }[] };
|
||||
return {
|
||||
...rest,
|
||||
nodes: nodes.map((node) => {
|
||||
if (node.class !== 'Vermittlungsrechner' && node.class !== 'Gateway') return node;
|
||||
const { fileSystem: _templateFiles, ...system } = node.system;
|
||||
return { ...node, system };
|
||||
}),
|
||||
};
|
||||
}
|
||||
|
||||
describe('corpus: the topology as Java loads it', () => {
|
||||
for (const file of flsFiles(corpusRoot)) {
|
||||
const name = relative(corpusRoot, file);
|
||||
@@ -124,7 +163,7 @@ describe('corpus: the topology as Java loads it', () => {
|
||||
const topology = readTopology(values);
|
||||
const expected = javaView[name] as { nodes: { bounds: number[] | null }[] };
|
||||
|
||||
expect(asJavaView(topology, expected.nodes.map((node) => node.bounds))).toEqual(expected);
|
||||
expect(asJavaView(topology, expected.nodes.map((node) => node.bounds))).toEqual(withoutTemplateFiles(expected));
|
||||
expect(topology.warnings).toEqual([]);
|
||||
});
|
||||
}
|
||||
@@ -296,6 +335,39 @@ describe('readTopology rules', () => {
|
||||
]);
|
||||
});
|
||||
|
||||
it('reads a file system from `root` or `arbeitsVerzeichnis`, with files by setters or constructor', () => {
|
||||
const { nodes, warnings } = topologyOf(`<object class="java.util.LinkedList">
|
||||
${nodeItem('<object class="filius.hardware.knoten.Notebook"/>')}
|
||||
${nodeItem(`<object class="filius.hardware.knoten.Rechner"><void property="systemSoftware"><void property="dateisystem">
|
||||
<void property="root"><object class="javax.swing.tree.DefaultMutableTreeNode"><string>/</string>
|
||||
<void method="add"><object class="javax.swing.tree.DefaultMutableTreeNode">
|
||||
<void method="add"><object class="javax.swing.tree.DefaultMutableTreeNode">
|
||||
<object class="filius.software.system.Datei"><string>a.txt</string><string>text</string><string>hallo</string>
|
||||
<void property="dateiTyp"><string>txt</string></void>
|
||||
</object>
|
||||
</object></void>
|
||||
</object></void>
|
||||
<void method="insert"><object class="javax.swing.tree.DefaultMutableTreeNode"/><int>0</int></void>
|
||||
</object></void>
|
||||
</void></void></object>`)}</object>`);
|
||||
|
||||
expect(nodes.map((node) => node.system.fileSystem)).toEqual([
|
||||
{ kind: 'directory', name: 'root', children: [] },
|
||||
{
|
||||
kind: 'directory',
|
||||
name: '/',
|
||||
children: [
|
||||
{
|
||||
kind: 'directory',
|
||||
name: null,
|
||||
children: [{ kind: 'file', name: 'a.txt', type: 'txt', content: 'hallo', children: [] }],
|
||||
},
|
||||
],
|
||||
},
|
||||
]);
|
||||
expect(warnings).toEqual(['node 2: file system: insert on a node is not read']);
|
||||
});
|
||||
|
||||
it("gives a modem Java's defaults and keeps an address the file sets to null", () => {
|
||||
const { nodes } = topologyOf(`<object class="java.util.LinkedList">
|
||||
${nodeItem('<object class="filius.hardware.knoten.Modem"/>')}
|
||||
|
||||
Reference in New Issue
Block a user