test: F3 acceptance — the classroom classics on FILIUS' examples against Java
A web page by its DNS name (internetwork_komplett), an e-mail between two networks through NAT and MX relay (Internet_Komplett_..._DE), a file found and fetched with Gnutella across a router (zwei_rechnernetze_komplett): ExampleRun records frames, tables, program lines and end states; the core plays the same calls and matches card by card. Equal RIP ties take Java's choice, RIP beacons are left out, the search's two connections compared flow by flow. Also: a core test that an equal RIP route from another router neither replaces nor refreshes the route, and nine stale type errors in core tests fixed. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -996,3 +996,69 @@ Weitere Abläufe von `TerminalRun` auf `terminal-net`. Gemessen (02.10.2026):
|
||||
Tabelle (Uhrzeit, Protokoll, Quelle > Ziel, Kommentar), alle 100 ms;
|
||||
Zeilenumbrüche werden zu zwei Leerzeichen; Strg+C beendet es nach dem
|
||||
nächsten Blick. Ein während dessen getippter Befehl wartet bis danach.
|
||||
|
||||
## Unterrichtsklassiker (Abnahme F3)
|
||||
|
||||
`ExampleRun` hat drei weitere Abläufe auf Beispielen von Filius. Was jedes
|
||||
Programm seinem Fenster meldet, steht unter `programs` (Zeit, Programm,
|
||||
Text; ein Aufruf des Ablaufs als `call …`), wo die Programme enden unter
|
||||
`end`:
|
||||
|
||||
* `web` (internetwork_komplett): Notebook 1.10 tippt `www.filius.de` in
|
||||
seinen Browser. Der Name wird bei DNS-Server 2.10 im anderen Netz
|
||||
erfragt, die Seite samt Bild kommt von Webserver 5.10 zwei Router weiter.
|
||||
* `email` (Internet_Komplett_…_DE): Wenn die DHCP-Clients ihre Adressen
|
||||
haben (8 s), schreibt herbert@gmx.de (das erste Notebook, im Schulnetz
|
||||
hinter der Firewall) an frimp@webmail.de. mail.gmx.de reicht die Mail an
|
||||
mail.webmail.de im anderen Netz weiter, bei 22 s holt frimp (Notebook#2)
|
||||
ab.
|
||||
* `gnutella` (zwei_rechnernetze_komplett): Filius' Gnutella-Programm auf
|
||||
Notebook 1.10, Notebook 1.11 und DNS-Server 2.10 (der „Lied der
|
||||
Woche.txt“ teilt), je höchstens drei Teilnehmer. 1.10 tritt bei 2.10 bei,
|
||||
dann 1.11 bei 1.10: Der Ping geht über 1.10 weiter zu 2.10, der Pong auf
|
||||
demselben Weg zurück. 1.10 sucht „woche“ (2.10 reicht die Query an 1.11
|
||||
weiter) und holt den Treffer über den Router.
|
||||
|
||||
Aufnahmelängen 12 s (`web`), 40 s (`email`), 26 s (`gnutella`): Das Ende
|
||||
liegt jeweils, wo nichts mehr geschieht. Bei 34 s fiel Javas letzter Abbau
|
||||
einer Verbindung knapp hinter das Ende, bei 14 s ein wiederholtes SYN.
|
||||
|
||||
Gemessen (03.10.2026):
|
||||
|
||||
* Die Datei hält die Konten der E-Mail-Programme nur in der älteren
|
||||
`kontoListe`, die Filius 2.14 nicht mehr liest: Nach dem Laden hat Java
|
||||
kein Konto. Der Leser übernimmt sie (`importEmail.ts`). Das Orakel trägt
|
||||
sie so ein, wie man sie im Kontodialog eintippt, mit den Werten der
|
||||
Liste.
|
||||
* Notebook 1.11 in zwei_rechnernetze hat kein Gateway (mit Absicht im
|
||||
Beispiel). Lernt es über einen Pong einen Teilnehmer im anderen Netz,
|
||||
bleibt in Java der Thread jeder Verbindung dorthin in
|
||||
`TCPSocket.verbinden` hängen: kein SYN auf der Leitung, keine Meldung, ein
|
||||
Download kommt nie an. Der Ablauf lässt darum 1.10 suchen.
|
||||
* Jeder TCP-Socket zieht beim Anlegen die nächste Anfangsnummer (ein
|
||||
gemeinsamer Zähler). Ein Server legt seinen ersten lauschenden Socket beim
|
||||
Start in seinem eigenen Thread an (`ServerAnwendung.annehmenVerbindungen`):
|
||||
Welcher Server eines Beispiels welche Nummer zieht, entscheidet der Takt
|
||||
der Threads. Verglichen wird unterhalb der Millionen.
|
||||
* Eine Suche geht zugleich an beide Teilnehmer von 1.10. An seiner Karte
|
||||
kommen die Frames der zwei Verbindungen in beliebiger Folge, einige zehn
|
||||
Millisekunden auseinander (in einer Aufnahme vertauscht, in der nächsten
|
||||
nicht).
|
||||
* 2.10 reicht die Query an 1.11 weiter, das ohne Gateway nicht antworten
|
||||
kann: Java schickt das SYN einmal und gibt nach einer Wartezeit auf (wie
|
||||
in `closedPort` von `TcpRun`); 1.11 seinerseits erreicht 2.10 gar nicht
|
||||
(s. o., kein Frame).
|
||||
* **Gleich lange RIP-Wege, gemessen**: Router #3 erreicht das Schulnetz
|
||||
(42.0.0.0) über Router `""` oder über #2, je zwei Sprünge. Jede Antwort
|
||||
an die Schule läuft dort durch, die des DNS 8.8.8.8 wie die von webmail.
|
||||
In beiden ruhigen Aufnahmen nahm Java #2, der Kern `""` (die zuerst
|
||||
gehörte Route); über #2 kosten zwei neue ARP-Suchen rund 270 ms. Der Test
|
||||
gibt dem Kern Javas Wahl, wie eine von Java gezogene Zufallszahl, und nur
|
||||
bei gleicher Länge.
|
||||
* RIP-Beacons kommen alle 30 s nach Zufallspausen, so viele, wie ein Router
|
||||
seit dem Start gezogen hat (s. RIP oben); verglichen werden in den
|
||||
Klassikern die Frames der Programme, RIP nicht.
|
||||
* Java fällt an jedem Verbindungsaufbau um einen Blick (rund 80 ms) zurück,
|
||||
dazwischen um wenige Millisekunden je Sekunde: In der E-Mail-Geschichte
|
||||
folgt die Weitergabe gmx → webmail auf herberts Verbindung, ihre Frames
|
||||
liegen bis zu 272 ms hinter dem Kern.
|
||||
|
||||
@@ -322,9 +322,12 @@ for run in pingLan:8000 pingRouted:13000 pingNobody:49000 pingUnreachable:45000
|
||||
done
|
||||
|
||||
# FILIUS' own examples as Java FILIUS runs them, opened and started as FILIUS
|
||||
# does (ExampleRun): the acceptance of F2 (packages/fls/test/examplesJava.test.ts).
|
||||
# does (ExampleRun): the acceptance of F2 (packages/fls/test/examplesJava.test.ts)
|
||||
# and the classics of F3 (web, email, gnutella; packages/fls/test/classicsJava.test.ts).
|
||||
for run in internetwork_komplett:routing:12000 einfaches_rechnernetz_komplett:dhcp:20000 dns_server:dns:12000 \
|
||||
nat_with_mapped_port:nat:15000 Internet_Komplett_mit_eMail_Webserver_Intranet_Portforwarding_Firewall_DHCP_DE:firewall:16000; do
|
||||
nat_with_mapped_port:nat:15000 Internet_Komplett_mit_eMail_Webserver_Intranet_Portforwarding_Firewall_DHCP_DE:firewall:16000 \
|
||||
internetwork_komplett:web:12000 Internet_Komplett_mit_eMail_Webserver_Intranet_Portforwarding_Firewall_DHCP_DE:email:40000 \
|
||||
zwei_rechnernetze_komplett:gnutella:26000; do
|
||||
file="${run%%:*}"; rest="${run#*:}"; story="${rest%%:*}"
|
||||
output="$root/packages/fls/test/examples/$story.java.json"
|
||||
rm -f "$output"
|
||||
|
||||
@@ -22,9 +22,43 @@
|
||||
// firewall's WAN address. Targets with one shortest RIP path
|
||||
// each: where two are equal, Java takes whichever its threads
|
||||
// learn first
|
||||
//
|
||||
// The classics of the classroom (the acceptance of F3), with what each
|
||||
// program told its window ("programs": time, program, text) and where each
|
||||
// ended ("end"):
|
||||
// web internetwork_komplett: Notebook 1.10 types www.filius.de into
|
||||
// its browser; the name is asked of DNS-Server 2.10 in another
|
||||
// network, the page comes from Webserver 5.10, two routers away
|
||||
// email Internet_Komplett_…_DE: when the DHCP clients have their
|
||||
// addresses (8 s), herbert@gmx.de (the first Notebook, in the
|
||||
// school's network behind the firewall) writes to
|
||||
// frimp@webmail.de; mail.gmx.de hands it on to mail.webmail.de
|
||||
// in another network; at 22 s frimp (Notebook#2) fetches. The
|
||||
// file keeps the programs' accounts only in the older kontoListe,
|
||||
// which FILIUS 2.14 no longer reads (the reader takes it,
|
||||
// importEmail.ts): they are set as one types them into the
|
||||
// program's account dialog, with the values the list holds
|
||||
// gnutella zwei_rechnernetze_komplett with FILIUS' Gnutella program on
|
||||
// Notebook 1.10, Notebook 1.11 and DNS-Server 2.10 (in the other
|
||||
// network; it shares "Lied der Woche.txt"), each keeping three
|
||||
// peers: 1.10 joins 2.10; 1.11 joins 1.10 (its ping goes on to
|
||||
// 2.10, the pong comes back through 1.10); 1.10 searches "woche"
|
||||
// (1.11 passes the query on) and downloads the hit through the
|
||||
// router (1.11 has no gateway: it could not fetch from the other
|
||||
// network)
|
||||
|
||||
import filius.hardware.knoten.Knoten;
|
||||
import filius.software.dateiaustausch.PeerToPeerAnwendung;
|
||||
import filius.software.dns.Resolver;
|
||||
import filius.software.email.Email;
|
||||
import filius.software.email.EmailAnwendung;
|
||||
import filius.software.email.EmailKonto;
|
||||
import filius.software.email.EmailServer;
|
||||
import filius.software.email.EmailUtils;
|
||||
import filius.software.system.Datei;
|
||||
import filius.software.www.HTTPNachricht;
|
||||
import filius.software.www.WebBrowser;
|
||||
import filius.software.www.WebServer;
|
||||
import filius.software.rip.RIPRoute;
|
||||
import filius.software.rip.RIPTable;
|
||||
import filius.software.system.VermittlungsrechnerBetriebssystem;
|
||||
@@ -72,6 +106,58 @@ public class ExampleRun {
|
||||
events.add(new Object[] { System.currentTimeMillis() - network.start, what });
|
||||
}
|
||||
|
||||
/** What a program told its window: time, program, text. */
|
||||
static final List<Object[]> told = new ArrayList<>();
|
||||
|
||||
static synchronized void tell(String program, String text) {
|
||||
told.add(new Object[] { System.currentTimeMillis() - network.start, program, text });
|
||||
}
|
||||
|
||||
static <T> T program(String node, String name) {
|
||||
@SuppressWarnings("unchecked")
|
||||
T program = (T) network.system(node).holeSoftware(name);
|
||||
return program;
|
||||
}
|
||||
|
||||
static void watchServer(String node, String program) {
|
||||
filius.software.Anwendung server = program(node, program.equals("webServer") ? "filius.software.www.WebServer" : "filius.software.email.EmailServer");
|
||||
server.hinzuBeobachter((observable, data) -> tell(node, String.valueOf(data)));
|
||||
}
|
||||
|
||||
/** GUIApplicationEmailAnwendungWindow's send button, as EmailClientRun makes it. */
|
||||
static void send(EmailAnwendung program, EmailKonto account, String to, String subject, String text) {
|
||||
Email mail = new Email();
|
||||
mail.setAbsender(account.getVorname() + (!account.getNachname().isEmpty() ? (" " + account.getNachname()) : "") + " <"
|
||||
+ account.getEmailAdresse() + ">");
|
||||
mail.setEmpfaenger(EmailUtils.stringToAddressEntryList(to));
|
||||
mail.setCc(EmailUtils.stringToAddressEntryList(""));
|
||||
mail.setBcc(EmailUtils.stringToAddressEntryList(""));
|
||||
mail.setBetreff(subject);
|
||||
mail.setText(text);
|
||||
program.versendeEmail(account.getSmtpserver(), mail, account.getEmailAdresse());
|
||||
}
|
||||
|
||||
static EmailKonto account(EmailAnwendung program) {
|
||||
return program.holeKontoListe().values().iterator().next();
|
||||
}
|
||||
|
||||
/** The account dialog's fields: user, password, first name, last name, address, and the server for both POP3 and SMTP. */
|
||||
static void setAccount(EmailAnwendung program, String user, String password, String first, String last, String address, String server) {
|
||||
EmailKonto account = new EmailKonto();
|
||||
account.setBenutzername(user);
|
||||
account.setPasswort(password);
|
||||
account.setVorname(first);
|
||||
account.setNachname(last);
|
||||
account.setEmailAdresse(address);
|
||||
account.setPop3server(server);
|
||||
account.setPop3port("110");
|
||||
account.setSmtpserver(server);
|
||||
account.setSmtpport("25");
|
||||
program.setzeKonto(account);
|
||||
}
|
||||
|
||||
static final String[] GNUTELLA_PEERS = { "Notebook 1.10", "Notebook 1.11", "DNS-Server 2.10" };
|
||||
|
||||
interface Call {
|
||||
String run() throws Exception;
|
||||
}
|
||||
@@ -101,6 +187,40 @@ public class ExampleRun {
|
||||
network = ScenarioNetwork.fromFile(args[0]);
|
||||
String story = args[1];
|
||||
long end = Long.parseLong(args[2]);
|
||||
if (story.equals("web")) {
|
||||
watchServer("Webserver 5.10", "webServer");
|
||||
WebBrowser browser = program("Notebook 1.10", "filius.software.www.WebBrowser");
|
||||
browser.hinzuBeobachter((observable, data) -> {
|
||||
if (data instanceof HTTPNachricht) {
|
||||
HTTPNachricht page = (HTTPNachricht) data;
|
||||
tell("browser", "page " + page.getStatusCode() + " " + page.getContentType() + "\n" + page.getDaten());
|
||||
} else {
|
||||
tell("browser", data == null ? "update" : String.valueOf(data));
|
||||
}
|
||||
});
|
||||
}
|
||||
if (story.equals("email")) {
|
||||
watchServer("mail.gmx.de (130.100.200.25)", "emailServer");
|
||||
watchServer("mail.webmail.de (140.100.200.25)", "emailServer");
|
||||
setAccount(program("Notebook", "filius.software.email.EmailAnwendung"), "herbert", "herbert1234", "Herbert", "Schmidt", "herbert@gmx.de", "mail.gmx.de");
|
||||
setAccount(program("Notebook#2", "filius.software.email.EmailAnwendung"), "frimp", "frimp1234", "Frimp", "Mueller", "frimp@webmail.de", "mail.webmail.de");
|
||||
for (String node : new String[] { "Notebook", "Notebook#2" }) {
|
||||
EmailAnwendung client = program(node, "filius.software.email.EmailAnwendung");
|
||||
client.hinzuBeobachter((observable, data) -> tell(node, String.valueOf(data)));
|
||||
}
|
||||
}
|
||||
if (story.equals("gnutella")) {
|
||||
for (String node : GNUTELLA_PEERS) {
|
||||
network.system(node).installApp("filius.software.dateiaustausch.PeerToPeerAnwendung", false);
|
||||
PeerToPeerAnwendung peer = program(node, "filius.software.dateiaustausch.PeerToPeerAnwendung");
|
||||
peer.setMaxTeilnehmerZahl(3);
|
||||
peer.hinzuBeobachter((observable, data) -> {
|
||||
tell(node, "peers " + String.join(",", peer.holeBekanntePeerToPeerTeilnehmer()));
|
||||
tell(node, "results " + String.join("|", peer.holeErgebnisse()));
|
||||
});
|
||||
}
|
||||
network.system("DNS-Server 2.10").getDateisystem().speicherDatei("/peer2peer", new Datei("Lied der Woche.txt", "txt", "La la la"));
|
||||
}
|
||||
network.start();
|
||||
Thread poller = new Thread(() -> {
|
||||
while (true) {
|
||||
@@ -136,6 +256,42 @@ public class ExampleRun {
|
||||
ping("Notebook", "130.100.200.25", 2);
|
||||
ping("mail.gmx.de (130.100.200.25)", "42.0.0.10", 3);
|
||||
break;
|
||||
case "web": {
|
||||
tell("browser", "call holeWebseite www.filius.de");
|
||||
WebBrowser browser = program("Notebook 1.10", "filius.software.www.WebBrowser");
|
||||
browser.holeWebseite(WebRun.url("www.filius.de"));
|
||||
break;
|
||||
}
|
||||
case "email": {
|
||||
Thread.sleep(7500);
|
||||
EmailAnwendung herbert = program("Notebook", "filius.software.email.EmailAnwendung");
|
||||
tell("Notebook", "call versendeEmail frimp@webmail.de");
|
||||
send(herbert, account(herbert), "frimp@webmail.de", "Treffen", "Hallo Frimp,\nbis morgen!\nHerbert");
|
||||
long until = network.start + 22000 - System.currentTimeMillis();
|
||||
if (until > 0) Thread.sleep(until);
|
||||
EmailAnwendung frimp = program("Notebook#2", "filius.software.email.EmailAnwendung");
|
||||
EmailKonto account = account(frimp);
|
||||
tell("Notebook#2", "call emailsAbholenEmails");
|
||||
frimp.emailsAbholenEmails(account.getBenutzername(), account.getPasswort(), account.getPop3port(), account.getPop3server());
|
||||
break;
|
||||
}
|
||||
case "gnutella": {
|
||||
PeerToPeerAnwendung searching = program("Notebook 1.10", "filius.software.dateiaustausch.PeerToPeerAnwendung");
|
||||
PeerToPeerAnwendung near = program("Notebook 1.11", "filius.software.dateiaustausch.PeerToPeerAnwendung");
|
||||
tell("Notebook 1.10", "call join 141.99.2.10");
|
||||
searching.beitretenNetzwerk("141.99.2.10");
|
||||
Thread.sleep(network.start + 3000 - System.currentTimeMillis());
|
||||
tell("Notebook 1.11", "call join 141.99.1.10");
|
||||
near.beitretenNetzwerk("141.99.1.10");
|
||||
Thread.sleep(network.start + 5500 - System.currentTimeMillis());
|
||||
tell("Notebook 1.10", "call search woche");
|
||||
searching.sucheDatei("woche");
|
||||
Thread.sleep(network.start + 8500 - System.currentTimeMillis());
|
||||
tell("Notebook 1.10", "call download 0");
|
||||
if (searching.holeErgebnisse().isEmpty()) tell("Notebook 1.10", "skipped download 0");
|
||||
else searching.herunterladenDatei(0);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
throw new IllegalArgumentException("unknown story " + story);
|
||||
}
|
||||
@@ -157,6 +313,48 @@ public class ExampleRun {
|
||||
for (Object[] each : events) out.open('[').number((Long) each[0]).string((String) each[1]).close(']');
|
||||
out.close(']');
|
||||
}
|
||||
synchronized (ExampleRun.class) {
|
||||
out.key("programs").open('[');
|
||||
for (Object[] each : told) out.open('[').number((Long) each[0]).string((String) each[1]).string((String) each[2]).close(']');
|
||||
out.close(']');
|
||||
}
|
||||
// Where the programs ended: each e-mail server's accounts with their mails, each e-mail program's
|
||||
// inbox and sent mails (as EmailClientRun writes them); each Gnutella program's peers, results and files.
|
||||
out.key("end").open('{');
|
||||
if (story.equals("email")) {
|
||||
for (String node : new String[] { "mail.gmx.de (130.100.200.25)", "mail.webmail.de (140.100.200.25)" }) {
|
||||
out.key(node);
|
||||
EmailClientRun.accounts(out, program(node, "filius.software.email.EmailServer"));
|
||||
}
|
||||
for (String node : new String[] { "Notebook", "Notebook#2" }) {
|
||||
EmailAnwendung client = program(node, "filius.software.email.EmailAnwendung");
|
||||
out.key(node).open('{');
|
||||
out.key("inbox");
|
||||
EmailClientRun.mails(out, client.getEmpfangeneNachrichten());
|
||||
out.key("sent");
|
||||
EmailClientRun.mails(out, client.getGesendeteNachrichten());
|
||||
out.close('}');
|
||||
}
|
||||
}
|
||||
if (story.equals("gnutella")) {
|
||||
for (String node : GNUTELLA_PEERS) {
|
||||
PeerToPeerAnwendung peer = program(node, "filius.software.dateiaustausch.PeerToPeerAnwendung");
|
||||
out.key(node).open('{');
|
||||
out.key("peers").open('[');
|
||||
for (String each : peer.holeBekanntePeerToPeerTeilnehmer()) out.string(each);
|
||||
out.close(']');
|
||||
out.key("results").open('[');
|
||||
for (String each : peer.holeErgebnisse()) out.string(each);
|
||||
out.close(']');
|
||||
out.key("files").open('[');
|
||||
for (Datei file : network.system(node).getDateisystem().holeDateien(peer.holeVerzeichnis())) {
|
||||
out.open('[').string(file.getName()).string(file.getDateiTyp()).string(file.getDateiInhalt()).close(']');
|
||||
}
|
||||
out.close(']');
|
||||
out.close('}');
|
||||
}
|
||||
}
|
||||
out.close('}');
|
||||
out.close('}');
|
||||
network.stop();
|
||||
ScenarioNetwork.write(args[3], out);
|
||||
|
||||
Reference in New Issue
Block a user