- SQLite erhält seine hohe Zuverlässigkeit und Robustheit durch ein gründlich automatisiertes Testsystem; es gibt 590-mal mehr Testcode als Produktivcode
- Vier unabhängige Test-Harnesses (TCL, TH3, SQL Logic Test, dbsqlfuzz) validieren die Kernbibliothek und führen Hunderte Millionen Tests aus
- Durch Tests von Ausnahmezuständen (OOM, I/O-Fehler, Crash-Simulationen) und Fuzz-Testing wird geprüft, dass das System auch bei fehlerhaften Eingaben und Systemausfällen stabil arbeitet
- Es wird ein mehrschichtiger Verifizierungsprozess mit 100 % Branch- und MC/DC-Coverage, Erkennung von Ressourcenlecks, Valgrind, statischer Analyse und Checklisten aufrechterhalten
- Dank dieser systematischen Tests wird SQLite als Open-Source-Datenbank mit Zuverlässigkeit und Qualität auf kommerziellem DB-Niveau bewertet
1. Überblick
- Die Zuverlässigkeit und Robustheit von SQLite resultiert aus einem detaillierten Testprozess
- Mit Stand Version 3.42.0 besteht SQLite aus etwa 155,8 KSLOC C-Code und 92053,1 KSLOC Testcode
- Das Testsystem umfasst 4 unabhängige Harnesses, 100 % Branch-Coverage und Millionen von Testfällen
- Enthalten sind unter anderem OOM-, I/O-Fehler-, Crash-, Fuzz-, Grenzwert-, Regressions-, fehlerhafte-DB-Datei- und Tests mit deaktivierten Optimierungen
2. Test-Harnesses
- TCL Tests
- Öffentlich verfügbares Testset, das hauptsächlich während der SQLite-Entwicklung verwendet wird
- Besteht aus 27,2 KSLOC C-Code und 1390 Skriptdateien (23,2 MB)
- Etwa 50.000 Testfälle; durch Parametrisierung werden bei einer vollständigen Ausführung Hunderte Millionen Tests durchgeführt
- TH3
- Kommerzielles C-basiertes Testset, das 100 % Branch- und MC/DC-Coverage erreicht
- Läuft auch in Embedded-Umgebungen und umfasst 1055,4 KSLOC sowie etwa 50.000 Fälle
- Bei vollständigen Coverage-Tests etwa 2,4 Millionen Ausführungen, vor Releases 248 Millionen Soak-Tests
- SQL Logic Test (SLT)
- Vergleicht Ergebnisse von SQLite mit PostgreSQL, MySQL, SQL Server und Oracle 10g
- Besteht aus 7,2 Millionen Queries und 1,12 GB Daten
- dbsqlfuzz
- libFuzzer-basierter Fuzzer, der SQL und Datenbankdateien gleichzeitig mutiert
- Führt pro Tag etwa 1 Milliarde Mutationstests durch und prüft die Robustheit gegenüber bösartigen Eingaben
- Zusätzliche Werkzeuge
- speedtest1.c, mptester.c, threadtest3.c, fuzzershell.c, jfuzz usw.
- Ein Release ist nur möglich, wenn alle Tests auf mehreren Plattformen und mit verschiedenen Compiler-Konfigurationen bestehen
3. Tests von Ausnahmezuständen
- OOM-Tests
- Simulieren
malloc()-Fehlschläge, um zu prüfen, ob bei Speichermangel korrekt wiederhergestellt wird
- Die Tests werden wiederholt ausgeführt, wobei der Zähler für den Fehlerzeitpunkt schrittweise erhöht wird
- I/O-Fehlertests
- Simulieren Plattenfehler mithilfe eines virtuellen Dateisystems (VFS)
- Nach einem Fehler wird mit
PRAGMA integrity_check geprüft, ob Daten beschädigt wurden
- Crash-Tests
- Simulieren Stromausfälle und OS-Abstürze
- Das TCL-Harness basiert auf Kindprozessen, TH3 verwendet ein speicherbasiertes VFS
- Es wird verifiziert, dass Transaktionen entweder vollständig zurückgerollt oder vollständig abgeschlossen werden
- Kombinierte Fehlertests
- Prüfen auch Szenarien, in denen nach einem Crash zusätzlich OOM- oder I/O-Fehler nacheinander auftreten
4. Fuzz-Testing
- SQL Fuzz
- Erzeugt syntaktisch gültiges, aber ungewöhnliches SQL, um die Reaktion von SQLite zu prüfen
- American Fuzzy Lop (AFL)
- Profilbasierter Fuzzer, eingeführt 2014, der neue Kontrollpfade erkundet
- Fand zahlreiche
assert-Fehlschläge, Abstürze und fehlerhafte Ergebnisse in SQLite
- Google OSS Fuzz
- Führt seit 2016 automatisches Fuzzing auf der Google-Infrastruktur durch
- Erkennt sporadische Probleme in neuen Commits
- dbsqlfuzz / jfuzz
- Seit 2018 als interne Fuzzer eingeführt; mutieren SQL und DB-Dateien gleichzeitig
- Mehr als 500 Millionen Tests pro Tag; Bug-Reports von externen Fuzzern sind fast vollständig verschwunden
- Seit 2024 ergänzt jfuzz die Validierung von JSONB-Eingaben
- Fuzzer von Drittanbietern und fuzzcheck
- Externe Forschende (z. B. Manuel Rigger) fanden zahlreiche Fälle falsch berechneter Ergebnisse
- Das Hilfsprogramm fuzzcheck validiert erneut Tausende „interessante“ frühere Fuzz-Fälle
- Spannungsverhältnis zwischen MC/DC und Fuzz-Testing
- MC/DC minimiert defensiven Code, Fuzzing erfordert defensiven Code
- SQLite kombiniert beide Ansätze, um Code aufrechtzuerhalten, der sowohl bei normalen als auch bei bösartigen Eingaben robust ist
5. Regressionstests
- Gemeldete Bugs werden nach der Behebung zwingend als neue Testfälle ergänzt
- Ziel ist es, das Wiederauftreten früherer Bugs zu verhindern
6. Automatische Erkennung von Ressourcenlecks
- TCL- und TH3-Harnesses überwachen automatisch Speicher-, Datei-, Thread- und Mutex-Lecks
- Auch nach OOM- oder I/O-Fehlern darf es keine Speicherlecks geben
7. Testabdeckung
- Der SQLite-Core erreicht 100 % Branch-Coverage nach TH3
- Erweiterungen wie FTS3 und RTree sind ausgenommen
- Statement- vs. Branch-Coverage
- Branch-Coverage ist strenger als Statement-Coverage und prüft alle Bedingungszweige in beide Richtungen
- Coverage für defensiven Code
- Defensive Bedingungen werden mit den Makros ALWAYS() und NEVER() gekennzeichnet
- Durch wiederholte Tests mit drei Definitionsformen wird Konsistenz verifiziert
- Grenzwert- und Boolean-Vektor-Tests
- Mit dem Makro testcase() werden sowohl positive als auch negative Ergebnisse einer Bedingung geprüft
- 1184 Verwendungen von testcase()
- Erreichen von MC/DC
- Über das Makro testcase() wird der unabhängige Einfluss jeder Bedingung validiert
- gcov-basierte Messung
- Die Coverage wird mit den Optionen
-fprofile-arcs -ftest-coverage gemessen
- Durch den Vergleich der Ergebnisse werden Compiler-Bugs oder undefiniertes Verhalten erkannt
- Mutation Testing
- Ändert Branch-Anweisungen, um zu prüfen, ob die Tests dies erkennen
- Optimierungszweige (
/*OPTIMIZATION-IF-TRUE*/) werden als Ausnahme behandelt
- Erfahrung mit vollständiger Coverage
- Dank der Tests aller Branches werden Nebenwirkungen bei Codeänderungen minimiert
- Die Wartungskosten sind hoch, aber für eine breit eingesetzte Infrastruktur-Bibliothek gerechtfertigt
8. Dynamische Analyse
- Assert()
- 6754
assert-Anweisungen prüfen Vor- und Nachbedingungen sowie Schleifeninvarianten
- Nur in
SQLITE_DEBUG-Builds aktiv
- Valgrind
- Erkennt Speicherfehler, Stack-Overflows und Zugriffe auf nicht initialisierten Speicher
- Vor Releases werden veryquick- und TH3-Tests unter Valgrind ausgeführt
- Memsys2
- In
SQLITE_MEMDEBUG-Builds werden Wrapper zur Überwachung von Speicherfehlern eingefügt
- Wiederholte Prüfungen sind damit schneller als mit Valgrind möglich
- Mutex Asserts
- Validieren Multithreading-Synchronisation mit
sqlite3_mutex_held() usw.
- Journal-Tests
- Prüfen, ob das Rollback-Journal vor der DB geschrieben wird, und gewährleisten so die Atomizität von Transaktionen
- Checks auf undefiniertes Verhalten
- Erkennen undefiniertes Verhalten mit
-ftrapv, -fsanitize=undefined, /RTC1 usw.
- Werden wiederholt auf 32-/64-Bit-Systemen, mit unterschiedlicher Endianness und auf verschiedenen CPU-Architekturen ausgeführt
9. Tests mit deaktivierten Optimierungen
- Optimierungen können mit
sqlite3_test_control(SQLITE_TESTCTRL_OPTIMIZATIONS) deaktiviert werden
- Unabhängig davon, ob Optimierungen aktiv sind oder nicht, müssen identische Ergebnisse erzeugt werden
- Einige Tests zur Leistungsmessung sind ausgenommen
10. Checkliste
- Vor einem Release wird eine manuelle Checkliste mit etwa 200 Punkten geprüft
- Einige Punkte dauern nur Sekunden, andere mehrere Stunden
- Wenn Probleme entdeckt werden, werden sofort neue Punkte ergänzt, um das Verfahren kontinuierlich zu verbessern
11. Statische Analyse
- Kompiliert auf GCC, Clang und MSVC ohne Warnungen
- Auch der Clang Static Analyzer liefert keine gültigen Warnungen
- Die tatsächliche Wirksamkeit statischer Analyse bei der Erkennung realer Bugs ist begrenzt
12. Zusammenfassung
- Trotz seines Open-Source-Status hält SQLite Qualität auf kommerziellem Niveau und eine niedrige Fehlerrate aufrecht
- Gründliche Tests und Code-Design sind die entscheidenden Faktoren
- Jedes Release durchläuft die oben genannten Verfahren und wird so als auch in mission-kritischen Umgebungen vertrauenswürdige DB-Engine bereitgestellt
Noch keine Kommentare.