- SQLite mempertahankan keandalan dan ketangguhan yang tinggi melalui sistem pengujian otomatis yang sangat menyeluruh, dengan kode pengujian 590 kali lebih banyak daripada kode utamanya
- Empat harness pengujian independen (TCL, TH3, SQL Logic Test, dbsqlfuzz) memverifikasi pustaka inti, dengan ratusan juta pengujian dijalankan
- Melalui pengujian kondisi abnormal (OOM, error I/O, simulasi crash) dan fuzz testing, dipastikan tetap bekerja stabil terhadap input tidak normal dan kegagalan sistem
- Menjaga prosedur verifikasi berlapis seperti cakupan branch 100% dan MC/DC, deteksi kebocoran sumber daya, serta Valgrind, analisis statis, dan checklist
- Berkat pengujian sistematis ini, SQLite dinilai sebagai basis data open source dengan keandalan dan kualitas setara DB komersial
1. Gambaran umum
- Keandalan dan ketangguhan SQLite berasal dari proses pengujian yang sangat rinci
- Per versi 3.42.0, SQLite terdiri dari sekitar 155.8 KSLOC kode C dan 92053.1 KSLOC kode pengujian
- Sistem pengujiannya mencakup 4 harness independen, cakupan branch 100%, dan jutaan test case
- Termasuk OOM, error I/O, crash, fuzz, nilai batas, regresi, file DB abnormal, pengujian dengan optimisasi dinonaktifkan, dan banyak item lainnya
2. Harness pengujian
- TCL Tests
- Kumpulan pengujian publik yang terutama digunakan selama pengembangan SQLite
- Terdiri dari 27.2 KSLOC kode C dan 1390 file skrip (23.2MB)
- Sekitar 50 ribu test case, dan dengan parameterisasi total eksekusi mencapai jutaan pengujian
- TH3
- Kumpulan pengujian komersial berbasis C yang mencapai cakupan branch 100% dan MC/DC
- Dapat berjalan di lingkungan embedded, mencakup 1055.4 KSLOC dan sekitar 50 ribu case
- Untuk pengujian cakupan penuh, sekitar 2.4 juta pengujian dijalankan, dan sebelum rilis dilakukan 248 juta soak test
- SQL Logic Test (SLT)
- Membandingkan hasil SQLite dengan PostgreSQL, MySQL, SQL Server, dan Oracle 10g
- Terdiri dari 7.2 juta query dan 1.12GB data
- dbsqlfuzz
- Fuzzer berbasis libFuzzer yang memodifikasi SQL dan file basis data secara bersamaan
- Menjalankan sekitar 1 miliar uji mutasi per hari, memverifikasi ketangguhan terhadap input berbahaya
- Alat tambahan
- speedtest1.c, mptester.c, threadtest3.c, fuzzershell.c, jfuzz, dan lainnya
- Semua pengujian harus lulus di berbagai platform dan konfigurasi kompilasi agar bisa dirilis
3. Pengujian kondisi abnormal
- Pengujian OOM
- Mensimulasikan kegagalan
malloc() untuk memverifikasi apakah pemulihan berjalan normal saat memori habis
- Dilakukan berulang sambil menaikkan penghitung titik kegagalan
- Pengujian error I/O
- Menggunakan virtual file system (VFS) untuk mensimulasikan error disk
- Setelah error, memeriksa kerusakan data dengan
PRAGMA integrity_check
- Pengujian crash
- Mensimulasikan kondisi listrik padam atau OS crash
- Harness TCL berbasis child process, sedangkan TH3 menggunakan VFS berbasis memori
- Memverifikasi bahwa transaksi sepenuhnya di-rollback atau sepenuhnya selesai
- Pengujian kegagalan gabungan
- Juga memverifikasi skenario ketika setelah crash terjadi OOM atau error I/O secara berurutan
4. Fuzz testing
- SQL Fuzz
- Menghasilkan SQL yang valid secara sintaks tetapi tidak normal untuk memverifikasi respons SQLite
- American Fuzzy Lop (AFL)
- Fuzzer berbasis profil yang diperkenalkan pada 2014 untuk menelusuri jalur kontrol baru
- Menemukan banyak assert failure, crash, dan hasil yang salah di SQLite
- Google OSS Fuzz
- Sejak 2016 menjalankan fuzzing otomatis di infrastruktur Google
- Mendeteksi masalah intermiten pada commit baru
- dbsqlfuzz / jfuzz
- Diperkenalkan sebagai fuzzer internal sejak 2018, memodifikasi SQL dan file DB secara bersamaan
- Menguji lebih dari 500 juta kali per hari, hingga laporan bug dari fuzzer eksternal hampir lenyap
- Sejak 2024, jfuzz menambahkan validasi input JSONB
- Fuzzer pihak ketiga dan fuzzcheck
- Peneliti eksternal (misalnya Manuel Rigger) menemukan banyak kasus perhitungan hasil yang salah
- Utilitas fuzzcheck memverifikasi ulang ribuan case “menarik” dari fuzz case sebelumnya
- Relasi tegang antara MC/DC dan fuzz testing
- MC/DC meminimalkan kode defensif, sedangkan fuzz membutuhkan kode defensif
- SQLite menjalankan kedua pendekatan ini secara bersamaan untuk menjaga kode yang tangguh terhadap input normal maupun berbahaya
5. Pengujian regresi
- Bug yang dilaporkan setelah diperbaiki wajib ditambahkan sebagai test case baru
- Tujuannya mencegah bug lama muncul kembali
6. Deteksi otomatis kebocoran sumber daya
- Harness TCL dan TH3 secara otomatis memantau kebocoran memori, file, thread, dan mutex
- Bahkan setelah OOM atau error I/O, tidak boleh ada kebocoran memori
7. Cakupan pengujian
- Core SQLite mencapai cakupan branch 100% menurut TH3
- Ekstensi seperti FTS3 dan RTree tidak termasuk
- Statement vs Branch Coverage
- Cakupan branch lebih ketat daripada statement coverage, karena memverifikasi semua percabangan kondisi ke dua arah
- Cakupan kode defensif
- Kondisi defensif dinyatakan dengan makro ALWAYS() dan NEVER()
- Pengujian diulang dengan tiga bentuk definisi untuk memverifikasi konsistensi
- Pengujian nilai batas dan vektor boolean
- Makro testcase() memverifikasi hasil positif maupun negatif dari kondisi
- Digunakan 1184 testcase()
- Pencapaian MC/DC
- Melalui makro testcase(), dampak independen dari setiap kondisi diverifikasi
- Pengukuran berbasis gcov
- Cakupan diukur dengan opsi
-fprofile-arcs -ftest-coverage
- Perbandingan hasil digunakan untuk mendeteksi bug compiler atau undefined behavior
- Mutation Testing
- Mengubah instruksi branch untuk memastikan pengujian dapat mendeteksinya
- Branch optimisasi (
/*OPTIMIZATION-IF-TRUE*/) diperlakukan sebagai pengecualian
- Pengalaman cakupan penuh
- Berkat pengujian semua branch, efek samping saat kode diubah dapat diminimalkan
- Biaya pemeliharaannya tinggi, tetapi dapat dibenarkan untuk pustaka infrastruktur yang didistribusikan luas
8. Analisis dinamis
- Assert()
- Sebanyak 6754 pernyataan assert memverifikasi prakondisi, pascakondisi, dan invariant loop
- Hanya aktif pada build
SQLITE_DEBUG
- Valgrind
- Mendeteksi error memori, stack overflow, dan akses ke memori yang belum diinisialisasi
- Sebelum rilis, pengujian veryquick dan TH3 dijalankan dengan Valgrind
- Memsys2
- Pada build
SQLITE_MEMDEBUG, wrapper dimasukkan untuk memantau error memori
- Memungkinkan verifikasi berulang lebih cepat daripada Valgrind
- Mutex Asserts
- Memverifikasi sinkronisasi multithread dengan
sqlite3_mutex_held() dan lainnya
- Journal Tests
- Memastikan rollback journal ditulis lebih dulu daripada DB, menjamin atomicity transaksi
- Undefined Behavior Checks
- Mendeteksi undefined behavior dengan
-ftrapv, -fsanitize=undefined, /RTC1, dan lainnya
- Dilakukan berulang di 32/64-bit, endian berbeda, dan berbagai arsitektur CPU
9. Pengujian dengan optimisasi dinonaktifkan
- Menonaktifkan optimisasi dengan
sqlite3_test_control(SQLITE_TESTCTRL_OPTIMIZATIONS)
- Hasil yang dihasilkan harus sama terlepas dari ada atau tidaknya optimisasi
- Beberapa pengujian untuk pengukuran performa menjadi pengecualian
10. Checklist
- Sebelum rilis, diverifikasi checklist manual berisi sekitar 200 item
- Sebagian memerlukan beberapa detik, sebagian lain beberapa jam
- Jika ditemukan masalah, item langsung ditambahkan untuk perbaikan berkelanjutan
11. Analisis statis
- Dikompilasi di GCC, Clang, dan MSVC tanpa warning
- Juga tidak ada warning valid dari Clang Static Analyzer
- Efektivitas analisis statis untuk menemukan bug nyata terbatas
12. Ringkasan
- Meski open source, SQLite mempertahankan kualitas setingkat komersial dan tingkat cacat yang rendah
- Faktor utamanya adalah pengujian yang sangat ketat dan desain kode
- Setiap rilis melalui prosedur di atas dan disajikan sebagai engine DB yang dapat dipercaya bahkan di lingkungan mission-critical
Belum ada komentar.