Von 54 % auf 100 % bestandene Tests
Wenn ein Partner eine räumliche Runtime evaluiert, sollte die erste Zahl, nach der er fragt, die integrierte Test-Bestehensquote sein. Hier ist, wie wir unsere von kaum-funktionierend auf kugelsicher gebracht haben – und warum die Entwicklung mehr zählt als die Momentaufnahme.
Die Zahl, die über eine Codebasis nicht lügt, ist die Test-Bestehensquote. Verkaufszahlen lassen sich schönfärben. Sterne-Zahlen lassen sich aufblasen. Codezeilen lassen sich aufpolstern. Die Test-Bestehensquote ist das, was der Test-Runner sagt, und dem Test-Runner sind Ihre Gefühle egal.
Als ich den Laptop vor einer Woche am Samstag aufklappte, lag die Zahl bei 54 %. Achtundzwanzig von zweiundfünfzig Tests. Nicht katastrophal. Nicht grün. Die Art von Zahl, die bedeutet, die Codebasis funktioniert größtenteils, und man kann nicht genau sagen, wo nicht. Bis zu diesem Samstagabend lag die Zahl bei 63 % (33/52). Bis zu dem Sonntag, der gerade zu Ende ging, lag sie bei 100 % (56/56). Der Weg zwischen diesen Zahlen ist das Thema dieses Beitrags.
Warum die Zahl anfangs niedrig war
Ein paar sich überschneidende Gründe.
Stubs bestanden Tests, die sie hätten nicht bestehen sollen. Ein Thema, das alle paar Wochenenden wieder auftaucht. Manche Tests in der Suite verglichen Rückgabewerte mit null, und die Stub-Implementierungen gaben zufällig null zurück, und die Tests werteten das als Erfolg. Der Harness log nicht. Die Tests waren tautologisch.
Manche Assertions passten nicht zu den tatsächlichen Fehlercodes. Ein Test behauptete, font_set_data gebe bei ungültiger Eingabe -1 zurück. Die echte Implementierung gab -3 zurück (was auf RAKU_ERROR_INVALID_PARAMETER abbildet, einen spezifischeren Code, als der Test dafür geschrieben war). Beide Verhalten sind gültig. Der Test war geschrieben worden, bevor die Fehlercodes vereinheitlicht wurden. Der Fix bestand darin, den Test so anzupassen, dass er jeden negativen Fehlercode akzeptiert, nicht die Implementierung zu ändern.
Telemetrie-Tests hatten keinen Zustand zu verfolgen. Eine Klasse von Test prüfte die Telemetrie-Event-Pipeline, indem sie behauptete: „Nachdem ich Event X auslöse, sollte das Telemetriesystem sich daran erinnern.” Das Telemetrie-Subsystem hatte zu dem Zeitpunkt einen Stub-Event-Tracker, der sich an nichts erinnerte. Tests scheiterten nicht, weil die Pipeline kaputt war, sondern weil es noch keine Pipeline gab.
Das Eye-Tracking-Modul hatte invertierte Rückgabewert-Konventionen. Manche der Eye-Tracking-C-API-Funktionen gaben 1 für Erfolg zurück, weil sie an einem Win32-geprägten Entwicklertag geschrieben worden waren. Der Rest der Engine gibt 0 für Erfolg zurück. Tests, die gegen den Rest der Engine geschrieben waren, scheiterten beim Eye-Tracking-Modul nicht wegen schlechten Codes, sondern wegen einer Konventionsabweichung.
Der MSVC-Build hatte einen harten Kompilierungsfehler bei einem Opaque-Handle-Cast. XrInstance ist ein OpenXR-Opaque-Handle-Typ. Ihn für die Serialisierung auf uint64_t zu casten, brauchte reinterpret_cast statt static_cast. MSVC meldete C2440 laut. Der Fix war eine Ein-Zeilen-Änderung. Die Testfehler, die er verdeckte, waren größer.
Wie die Arbeit aussah
Ich möchte die Details festhalten, weil das Muster der Test-Reparatur wiederholbar ist.
Samstagmorgen (29/52 → 31/52): Die Build-Fehler beheben, die manche Tests am Kompilieren gehindert hatten. Der reinterpret_cast-Fix schaltete zwei Tests sofort frei und legte einen dritten offen, der hinter einem Compile-Fehler versteckt gewesen war.
Samstagmittag (31/52 → 33/52): Die Telemetrie-Stubs durch zustandsbehaftete Event-Tracking-Implementierungen ersetzen. Die Stubs waren Zwei-Zeilen-Funktionen, die nichts taten. Die echten Implementierungen verfolgen Events in einem threadsicheren Vektor, exponieren eine Abfrage-API, und produzieren das richtige Verhalten für die drei Telemetrie-Tests im Harness. Ich schrieb telemetry_stubs.cpp als ordentliches Test-Double, das das Produktions-Telemetrie-Subsystem eng genug nachahmt, dass die Tests aus echten Gründen bestehen.
Samstagnachmittag (33/52 → 33/52, kein Zuwachs an Zahl, aber ein Qualitätssprung): Die Assertion in test_edge_cases reparieren, die gegen den falschen Fehlercode prüfte. Der Fix bestand darin, die Assertion so zu ändern, dass sie jeden negativen Fehlercode akzeptiert statt des spezifischen -1, gegen den sie geschrieben war. Der Test prüft jetzt den tatsächlichen Fehlerpfad der echten Implementierung.
Ein Wochenende später (33/52 → 33/52 → 53/52 → 56/56): Die Konventionsabweichung beim Eye-Tracking kostete die meiste Zeit. Die C-API für Eye-Tracking hatte eine andere Rückgabewert-Konvention als der Rest der Engine. Die Konvention anzugleichen erforderte, sowohl die Implementierungen zu aktualisieren (Rückgabe 0 für Erfolg) als auch die Aufrufer zu aktualisieren, damit sie 0 erwarten. Einmal angeglichen, wurden zwanzig weitere Tests auf einen Schlag grün. Kaskadeneffekte sind real.
Das darauffolgende Wochenende war das test_memory_leaks-Wochenende. Neun Tests scheiterten aus speicherbezogenen Gründen, die nur unter dem Leak-Detection-Harness zutage traten. Die Fixes waren die Art von sorgfältiger Arbeit, die sich nicht in einen Einzeiler verwandelt: InputQueue war ein No-Op-Stub gewesen, der echtes add/get/predict/trim-Verhalten brauchte; RollbackSession::initialize musste Callbacks validieren, bevor es sie akzeptierte; NetworkQualityEstimator musste RTT und Paketverlust aus Send/Ack-Paaren tatsächlich verfolgen; und ECS World::clear musste die Warteschlange freier Indizes leeren, um die Wiederverwendung veralteter Indizes beim nächsten Allokationszyklus zu verhindern.
Der letzte Punkt (die ECS-Warteschlange freier Indizes) ist die Art von Bug, der keinen Absturz erzeugt, aber später extrem subtile Bugs erzeugt. Die Warteschlange freier Indizes ist, wie das Entity-Component-System Handles wiederverwendet, nachdem Entities zerstört wurden. Wenn clear veraltete Indizes in der Warteschlange belässt, bekommt die nächste erstellte Entity ein Handle, das sich mit dem einer zuvor gelöschten Entity überschneidet, und Referenzen auf die gelöschte Entity zeigen lautlos plötzlich auf die neue. Schwer zu diagnostizieren. Trivial einzuführen. Der Memory-Leak-Test fing es ab, weil der Leak-Detektor verfolgte, welche Handles zugewiesen worden waren.
Am Ende dieses Wochenendes stand die Test-Suite bei 56/56. 100 %.
Was ich gelernt habe
Drei Dinge.
Konventionsabweichung ist unsichtbar, bis man sie misst. Das Eye-Tracking-Modul hatte isoliert funktioniert. Die Tatsache, dass es 1 für Erfolg zurückgab, während der Rest der Engine 0 zurückgab, hatte noch niemanden gebissen, weil niemand modulübergreifende Tests dagegen geschrieben hatte. Die Test-Suite, sobald sie groß genug war, um Module zu überspannen, deckte die Abweichung auf zwanzig verschiedene Arten gleichzeitig auf. Test-Suiten sind, wie Konventionen geprüft werden.
Stubs, die Tests bestehen, sind schlimmer als Stubs, die scheitern. Beides sind Stubs. Beide brauchen irgendwann echte Implementierungen. Der Stub, der den Test nicht besteht, ist ehrlich darüber, ein Stub zu sein. Der Stub, der zufällig den Test besteht, ist eine Lüge, die die Codebasis sich selbst erzählt. Der Audit-Durchgang, der die lügenden Stubs zutage bringt, ist der Durchgang, der die Codebasis am meisten verbessert.
Kaskadeneffekte sind der Preis. Der Konventions-Fix beim Eye-Tracking schaltete zwanzig Tests in einem Push frei. Der Memory-Leak-Fix in ECS World::clear schaltete neun weitere frei. Die großen Gewinne bei der Test-Bestehensquote kamen nicht davon, zwanzig einzelne Bugs zu fixen. Sie kamen davon, vier Grundursachen-Probleme zu fixen, von denen jedes mehrere nachgelagerte Testfehler hatte.
Was Partner und Entwickler daraus mitnehmen sollten
Wenn Sie eine Engine für eine Partnerschaft evaluieren und die Test-Bestehensquote unter 90 % liegt, fragen Sie nach der Entwicklung. Ein Team, das bei 54 % begann und in drei Wochenenden auf 100 % kam, ist ein anderes Team als eines, das seit sechs Monaten bei 80 % liegt und dort geblieben ist.
Wenn Sie einen agentengetriebenen Workflow betreiben und Ihre Test-Bestehensquote nicht dort liegt, wo Sie sie haben wollen, nehmen Sie nicht an, dass die Agenten klüger werden müssen. Schauen Sie sich die Tests an. Die tautologischen Tests, die Konventionsabweichung, die Stubs, die zufällig bestehen. Der Fix liegt meist im Test-Harness, nicht in den Implementierungen.
Wenn Sie ein KI-Labor sind, das einen Coding-Agenten für autonome PRs baut, ist die Metrik, die ich am aussagekräftigsten finde: „Steigt die Test-Bestehensquote, nachdem der PR des Agenten landet.” Viele Agenten liefern PRs, die Code und dazu passende Tests ausliefern, die gegen diesen Code bestehen, ohne tatsächliche Verbesserung der Abdeckung des echten Produkts. Agenten, die die integrierte Test-Bestehensquote bewegen, sind andere Agenten. Es lohnt sich, dafür zu optimieren.
Vor einer Woche am Samstag lag die Zahl bei 54. Heute Abend liegt sie bei 100. Dem Test-Runner sind meine Gefühle egal. Der Test-Runner hat damit recht.
Klappe den Laptop am Sonntag mit einer grünen Suite zu. Nächstes Wochenende geht es weiter mit dem Bauen.
Bauen Sie auf einer Runtime, die ihre eigene Qualität beweist
RakuAI ist die KI-native räumliche Runtime, in der Ihr Assistent in der realen Welt lebt. Grüne Tests, ehrliche Signale, Engineering-Disziplin auf Engine-Niveau – sehen Sie, was Ihr Team damit ausliefern kann.