// Feeds FILIUS' command line (Terminal, with the input handling of its window // GUIApplicationTerminalWindow) with lines as typed, without a running // network, and writes for each line what the program told the window (each // text and each Boolean "busy" mark), what it returned, the working directory // after it, and every file and directory of the host: the reference for the // core's command line (packages/core/src/runtime/apps/terminal.ts). It also // writes the terminal's texts of the message bundle, as Java loads them. // Each case starts on host 10.0.0.2 of a scenario (ScenarioNetwork) with the // files below and a new Terminal. The cases file is ASCII, with Java-style // unicode escapes (backslash, u, four hex digits) for anything else: // // case // // // Usage: java -cp filius.jar:lib/*:classes TerminalCases import filius.rahmenprogramm.Base64; import filius.rahmenprogramm.Information; import filius.software.lokal.Terminal; import filius.software.system.Datei; import filius.software.system.Dateisystem; import filius.software.system.InternetKnotenBetriebssystem; import java.io.FileOutputStream; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.nio.charset.StandardCharsets; import java.nio.file.Files; import java.nio.file.Path; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.Enumeration; import java.util.LinkedHashMap; import java.util.List; import java.util.Locale; import java.util.Map; import java.util.ResourceBundle; import java.util.StringTokenizer; import java.util.TreeMap; import javax.swing.tree.DefaultMutableTreeNode; import javax.swing.tree.TreeNode; public class TerminalCases { static final String HOST = "00000000-0000-0000-0000-000000000301"; /** A tiny PNG (1 x 1 pixel), as FILIUS keeps images: Base64. */ static final String IMAGE = "iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNk+M9QDwADhgGAWjR9awAAAABJRU5ErkJggg=="; /** The image as FILIUS writes a file's bytes (Base64.encodeBytes, with its line breaks). */ static String imageAsFiliusWritesIt() { try { return Base64.encodeBytes(Base64.decode(IMAGE)); } catch (Exception error) { throw new IllegalStateException(error); } } /** The host's files at the start of each case, in this order. */ static void files(Dateisystem files) { files.getRoot().removeAllChildren(); files.speicherDatei("/", new Datei("notizen.txt", "txt", "Hallo Welt")); files.erstelleVerzeichnis("/", "Dokumente"); files.speicherDatei("/Dokumente", new Datei("brief.txt", "txt", "Sehr geehrte Damen und Herren,\nviele Grüße")); files.erstelleVerzeichnis("/Dokumente", "Alt"); files.speicherDatei("/", new Datei("bild.png", "png", imageAsFiliusWritesIt())); files.speicherDatei("/", new Datei("kurz.txt", "txt", "SGFsbG8=")); files.erstelleVerzeichnis("/", "leer"); } public static void main(String[] args) throws Exception { // Before any class reads the message bundle (I18n.messages is read once). Information.getInformation().setLocale(args[0].equals("en") ? new Locale("en", "GB") : Locale.GERMANY); ScenarioNetwork network = ScenarioNetwork.read(args[1]); InternetKnotenBetriebssystem system = network.system(HOST); List cases = new ArrayList<>(); List steps = null; Terminal terminal = null; List told = new ArrayList<>(); for (String line : Files.readAllLines(Path.of(args[2]), StandardCharsets.US_ASCII)) { if (line.startsWith("#")) continue; if (line.startsWith("case ")) { Map current = new LinkedHashMap<>(); steps = new ArrayList<>(); current.put("name", line.substring("case ".length())); current.put("steps", steps); cases.add(current); files(system.getDateisystem()); system.getInstallierteAnwendungen().remove("filius.software.lokal.Terminal"); system.installApp("filius.software.lokal.Terminal", false); terminal = (Terminal) system.holeSoftware("filius.software.lokal.Terminal"); terminal.setSystemSoftware(system); terminal.hinzuBeobachter((observable, data) -> { if (data instanceof Boolean) told.add(Arrays.asList("busy", data)); else if (data != null) told.add(Arrays.asList("text", data.toString())); }); current.put("start", tree(system.getDateisystem())); continue; } String typed = IpCheckCases.unescape(line); Map step = new LinkedHashMap<>(); step.put("typed", typed); told.clear(); Object returned = null; String window = null; // GUIApplicationTerminalWindow: the first word is the command, the others its parameters, then at least three empty ones. if (typed.trim().isEmpty()) { window = "blank"; } else { StringTokenizer tokens = new StringTokenizer(typed, " "); String command = tokens.nextToken(); String[] parameters = new String[3 + tokens.countTokens()]; Arrays.fill(parameters, ""); for (int index = 0; tokens.hasMoreTokens(); index++) parameters[index] = tokens.nextToken(); if (command.equals("exit")) { window = "exit"; } else if (command.equals("reset") || command.equals("cls")) { window = "clear"; } else { // Terminal.terminalEingabeAuswerten, but in this thread instead of the program's. try { Method method = Terminal.class.getDeclaredMethod(command, String[].class); terminal.setInterrupt(false); returned = method.invoke(terminal, (Object) parameters); } catch (NoSuchMethodException error) { terminal.benachrichtigeBeobachter(terminal.messages.getString("terminal_msg2") + "\n" + terminal.messages.getString("terminal_msg3")); } catch (InvocationTargetException error) { returned = "!" + error.getCause().getClass().getSimpleName(); } } } step.put("window", window); step.put("told", new ArrayList<>(told)); step.put("returned", returned == null ? null : returned.toString()); step.put("directory", Dateisystem.absoluterPfad(terminal.getAktuellerOrdner())); step.put("files", tree(system.getDateisystem())); steps.add(step); } Map out = new LinkedHashMap<>(); out.put("cases", cases); ResourceBundle bundle = terminal.messages; Map texts = new TreeMap<>(); for (String key : Collections.list(bundle.getKeys())) { if (key.startsWith("sw_terminal") || key.startsWith("terminal_msg")) texts.put(key, bundle.getString(key)); } out.put("texts", texts); try (FileOutputStream stream = new FileOutputStream(args[3])) { stream.write((FlsOracle.Json.write(out, "") + "\n").getBytes(StandardCharsets.UTF_8)); } System.out.println("terminal cases (" + args[0] + "): " + cases.size()); System.exit(0); } /** Every node below the root in preorder: path, and for a file its type, content and size (Datei.holeGroesse). */ static List tree(Dateisystem files) { List nodes = new ArrayList<>(); Enumeration all = files.getRoot().preorderEnumeration(); while (all.hasMoreElements()) { DefaultMutableTreeNode node = (DefaultMutableTreeNode) all.nextElement(); if (node == files.getRoot()) continue; if (node.getUserObject() instanceof Datei) { Datei file = (Datei) node.getUserObject(); nodes.add(Arrays.asList(Dateisystem.absoluterPfad(node), file.getDateiTyp(), file.getDateiInhalt(), file.holeGroesse())); } else { nodes.add(Arrays.asList(Dateisystem.absoluterPfad(node))); } } return nodes; } }