// Feeds what FILIUS' file explorer, text editor and image viewer do to a // host's files, as their windows call FILIUS (GUIApplicationFileExplorerWindow, // GUIApplicationTextEditorWindow; the image viewer only reads), without a // running network, and writes after each step what it gave back (a message, // an exception) and every file and directory of the host, and the explorer's // messages as Java loads them: the reference for the core's file programs // (packages/core/src/runtime/apps/filePrograms.ts). // Each case starts on host 10.0.0.2 of a scenario with TerminalCases' files. // The cases file is ASCII, with Java-style unicode escapes; a step is an // operation and its fields, separated by tabs: // // mkdir the explorer's "new folder" (an empty name: nothing) // delete the explorer's "delete" of an entry of the folder // copy|cut the explorer's clipboard // paste the explorer's "paste" into the folder // rename the explorer's "rename" // import // the explorer's import of a local file (FileExplorer.addFile); // content @image (TerminalCases' image), @binary (bytes 0 to 255), // @bytes:N (N times 'a'), else the text // save the editor's "save" of the open file // saveas the editor's "save as" // // Usage: java -cp filius.jar:lib/*:classes FileCases import filius.rahmenprogramm.Base64; import filius.rahmenprogramm.Information; import filius.software.lokal.FileExplorer; import filius.software.system.Datei; import filius.software.system.Dateisystem; import filius.software.system.InternetKnotenBetriebssystem; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.FileOutputStream; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; 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.LinkedHashMap; import java.util.List; import java.util.Locale; import java.util.Map; import javax.swing.tree.DefaultMutableTreeNode; public class FileCases { static DefaultMutableTreeNode clipboard; static boolean cut; public static void main(String[] args) throws Exception { Information.getInformation().setLocale(args[0].equals("en") ? new Locale("en", "GB") : Locale.GERMANY); ScenarioNetwork network = ScenarioNetwork.read(args[1]); InternetKnotenBetriebssystem system = network.system(TerminalCases.HOST); system.installApp("filius.software.lokal.FileExplorer", false); FileExplorer explorer = (FileExplorer) system.holeSoftware("filius.software.lokal.FileExplorer"); explorer.setSystemSoftware(system); Dateisystem files = system.getDateisystem(); Path local = Files.createTempDirectory("filecases"); List cases = new ArrayList<>(); List steps = null; 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); TerminalCases.files(files); clipboard = null; current.put("start", TerminalCases.tree(files)); continue; } String[] fields = line.split("\t", -1); for (int index = 0; index < fields.length; index++) fields[index] = IpCheckCases.unescape(fields[index]); Map step = new LinkedHashMap<>(); step.put("step", Arrays.asList(fields)); Object returned; try { returned = run(fields, files, explorer, local); } catch (Exception error) { returned = "!" + error.getClass().getSimpleName(); } step.put("returned", returned == null ? null : returned.toString()); step.put("files", TerminalCases.tree(files)); steps.add(step); } Map out = new LinkedHashMap<>(); out.put("cases", cases); // The messages the explorer shows (its import's, and its paste's refusal), as Java loads them. Map texts = new LinkedHashMap<>(); for (String key : new String[] { "sw_fileexplorer_msg1", "sw_fileexplorer_msg2", "sw_fileexplorer_msg3", "fileexplorer_msg19" }) { texts.put(key, explorer.messages.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("file cases (" + args[0] + "): " + cases.size()); System.exit(0); } static Object run(String[] fields, Dateisystem files, FileExplorer explorer, Path local) throws Exception { DefaultMutableTreeNode folder = files.verzeichnisKnoten(fields[1]); switch (fields[0]) { case "mkdir": // "Neuer Ordner": the name asked for, nothing when it is empty. if (fields[2].equals("")) return null; return files.erstelleVerzeichnis(folder, fields[2]); case "delete": // "Loeschen": the selected entry, found by its name, removed from the folder. folder.remove(Dateisystem.verzeichnisKnoten(folder, fields[2])); return null; case "copy": case "cut": clipboard = Dateisystem.verzeichnisKnoten(folder, fields[2]); cut = fields[0].equals("cut"); return null; case "paste": // "Einfuegen": not into the copied entry itself or below it; a deep copy added, a cut one removed. if (clipboard.isNodeDescendant(folder)) return "fileexplorer_msg19"; folder.add(deepCopy(clipboard)); if (cut) { ((DefaultMutableTreeNode) clipboard.getParent()).remove(clipboard); clipboard = null; } return null; case "rename": { // "Umbenennen": a name that is not blank and not there yet in the folder. String name = fields[3]; if (name.trim().isEmpty() || files.dateiVorhanden(folder, name)) return null; DefaultMutableTreeNode node = Dateisystem.verzeichnisKnoten(folder, fields[2]); if (node.getUserObject() instanceof Datei) ((Datei) node.getUserObject()).setName(name); else node.setUserObject(name); return null; } case "import": { Path file = local.resolve(fields[3]); Files.write(file, content(fields[4])); return explorer.addFile(local.toString() + "/", fields[3], folder, fields[2]); } case "save": // "Speichern": the open file's content. files.holeDatei(fields[1]).setDateiInhalt(fields[2]); return null; case "saveas": // "Speichern unter": a new text file (texteditor_msg8) stored in the folder chosen. return files.speicherDatei(fields[1], new Datei(fields[2], explorer.messages.getString("texteditor_msg8"), fields[3])); default: throw new IllegalArgumentException("unknown step " + fields[0]); } } static byte[] content(String spec) throws Exception { if (spec.equals("@image")) return Base64.decode(TerminalCases.IMAGE); if (spec.equals("@binary")) { byte[] bytes = new byte[256]; for (int index = 0; index < bytes.length; index++) bytes[index] = (byte) index; return bytes; } if (spec.startsWith("@bytes:")) { byte[] bytes = new byte[Integer.parseInt(spec.substring("@bytes:".length()))]; Arrays.fill(bytes, (byte) 'a'); return bytes; } return spec.getBytes(StandardCharsets.UTF_8); } /** GUIApplicationFileExplorerWindow.tiefesKopieren: the node with all below it, serialized and read back. */ static DefaultMutableTreeNode deepCopy(DefaultMutableTreeNode original) throws Exception { ByteArrayOutputStream buffer = new ByteArrayOutputStream(); try (ObjectOutputStream out = new ObjectOutputStream(buffer)) { out.writeObject(original); } try (ObjectInputStream in = new ObjectInputStream(new ByteArrayInputStream(buffer.toByteArray()))) { return (DefaultMutableTreeNode) in.readObject(); } } }