Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Kapitel 18 - Hardware-Sicht: Test-Binaries, Parallelität und Compiler-Lints

Hallo Thorsten! Nachdem wir uns mit der Test-Organisation und API-Dokumentation beschäftigt haben, werfen wir einen Blick hinter die Kulissen und analysieren, wie das Test-Framework auf System- und Hardwareebene arbeitet.

Als Systemprogrammierer gibst du dich nicht mit der Erklärung „Es testet einfach“ zufrieden. Du willst wissen: Wie sieht der Einstiegspunkt des Testprogramms aus? Warum erhöht Testcode nicht die Binärgröße des Release-Builds? Und wie verhalten sich parallele Tests auf Hardware-Ebene?

Schnapp dir einen Kaffee – wir steigen tief in die Systemebene ab!


1. Die Funktionsweise des Test-Harness (Test-Einstiegspunkt)

Wenn du cargo test ausführst, baut der Compiler dein Programm grundlegend anders als bei einem normalen cargo build:

  1. Generierung der Test-Binary: Der Compiler erzeugt ein temporäres, ausführbares Programm (die Test-Binary).
  2. Der Test-Harness: Rust fügt automatisch einen eigenen Einstiegspunkt (eine generierte main-Funktion, den sogenannten Test Harness) ein. Diese main-Funktion verweist auf die interne Bibliotheks-Kiste libtest der Standardbibliothek.
  3. Test-Entdeckung: Alle Funktionen, die im AST mit dem Attribut #[test] markiert wurden, werden vom Compiler in eine interne Liste (ein Array aus Funktionszeigern) eingetragen.
  4. Ausführung: Die generierte main-Funktion läuft dieses Array durch und ruft jeden Test nacheinander auf.

Warum testet #[cfg(test)] ohne Release-Spuren?

Das Attribut #[cfg(test)] ist ein Compiler-Flag. Wenn Sie cargo build --release aufrufen, wird das Flag test nicht gesetzt. Der Compiler entfernt das gesamte Testmodul bereits in der Parser-Phase (Conditional Compilation). Es findet keine Codegenerierung statt, und alle Tests sowie die Bibliothek libtest werden komplett aus der finalen Binärdatei herausgefiltert (Dead Code Elimination).


2. Parallele Testausführung auf CPU-Ebene

Standardmäßig führt der Test-Harness alle Tests parallel aus, um die CPU-Kerne optimal auszulasten. Jeder Test läuft in einem eigenen Thread.

Das Hardware-Problem: Konflikte auf globalen System-Ressourcen

Da die Threads parallel laufen, teilen sie sich globale Ressourcen des Betriebssystems. Wenn Ihre Tests auf solche Zustände zugreifen, kommt es zu physischen Konflikten auf Speicher- oder Festplattenebene:

  1. Umgebungsvariablen: Wenn ein Test std::env::set_var aufruft, modifiziert er die globale Umgebungstabelle des Prozesses. Ein parallel laufender Test liest zeitgleich verfälschte Daten.
  2. Dateisystem: Schreiben zwei Tests in dieselbe Datei temp.txt, überschreiben sie sich gegenseitig.
  3. Datenbanken: Parallele Schreiboperationen auf derselben Tabelle führen zu inkonsistenten Testdaten und scheiternden Zusicherungen (sogenannte Flaky Tests).

Die Lösung auf Hardware-Ebene:

  • Umgebung: Vermeiden Sie globale Zustände. Übergeben Sie Konfigurationen explizit an Ihre Funktionen.
  • Dateien: Nutzen Sie Bibliotheken wie tempfile. Diese erstellen für jeden Testlauf ein eindeutiges, temporäres Verzeichnis auf der Festplatte (oder im RAM-basierten /tmp), sodass sich die Dateizugriffe physikalisch nicht stören.
  • Serielle Ausführung: Zwingen Sie den Harness zur sequenziellen Ausführung auf einem einzigen CPU-Kern:
    cargo test -- --test-threads=1
    

3. Wie cargo doc unter der Haube arbeitet

Der Befehl cargo doc baut keine normale Binärdatei. Er verhält sich wie ein statischer Website-Generator auf Compiler-Basis:

  1. AST-Analyse: Der Compiler liest das Crate ein und analysiert die Struktur (Typen, Felder, Schnittstellen). Er ignoriert den eigentlichen Funktionscode im Körper der Funktionen, da dieser für die Dokumentation irrelevant ist.
  2. Markdown-Rendering: Alle Kommentare, die mit /// oder //! beginnen, werden extrahiert und durch einen eingebauten Markdown-Parser in HTML-Code übersetzt.
  3. Verlinkung (Cross-Referencing): Rust sucht nach Code-Symbolen in den Kommentaren (z. B. [Vektor](crate::Vec)) und verknüpft sie automatisch mit den entsprechenden lokalen HTML-Dokumentationsseiten.