SQLite는 어떻게 테스트되는가

1 month ago 14

  • SQLite는 철저한 자동화 테스트 체계를 통해 높은 신뢰성과 견고성을 유지하며, 코드보다 590배 많은 테스트 코드가 존재함
  • 네 가지 독립 테스트 하니스(TCL, TH3, SQL Logic Test, dbsqlfuzz) 가 핵심 라이브러리를 검증하며, 수억 건의 테스트를 수행
  • 이상 상황 테스트(OOM, I/O 오류, 크래시 시뮬레이션)퍼즈 테스트(fuzz testing) 를 통해 비정상 입력과 시스템 장애에도 안정적으로 동작함을 확인
  • 100% 분기 및 MC/DC 커버리지, 리소스 누수 검출, Valgrind·정적 분석·체크리스트 등 다층적 검증 절차를 유지
  • 이러한 체계적 테스트 덕분에 SQLite는 상용 DB 수준의 신뢰성과 품질을 확보한 오픈소스 데이터베이스로 평가됨

1. 개요

  • SQLite의 신뢰성과 견고성은 세밀한 테스트 과정에서 비롯됨
    • 버전 3.42.0 기준, SQLite는 약 155.8 KSLOC의 C 코드와 92053.1 KSLOC의 테스트 코드로 구성
  • 테스트 체계는 4개의 독립 하니스, 100% 분기 커버리지, 수백만 건의 테스트 케이스를 포함
    • OOM, I/O 오류, 크래시, 퍼즈, 경계값, 회귀, 비정상 DB 파일, 최적화 비활성화 테스트 등 다수 항목 포함

2. 테스트 하니스

  • TCL Tests

    • SQLite 개발 중 주로 사용되는 공개 테스트 세트
    • 27.2 KSLOC의 C 코드와 1390개 스크립트 파일(23.2MB)로 구성
    • 5만여 개 테스트 케이스, 매개변수화로 전체 실행 시 수백만 건 수행
  • TH3

    • 상용 C 기반 테스트 세트로 100% 분기 및 MC/DC 커버리지 달성
    • 임베디드 환경에서도 동작하며, 1055.4 KSLOC·약 5만여 케이스 포함
    • 전체 커버리지 테스트 시 약 2.4백만 건, 릴리스 전 2.48억 건 soak 테스트 수행
  • SQL Logic Test (SLT)

    • SQLite와 PostgreSQL, MySQL, SQL Server, Oracle 10g 결과를 비교
    • 7.2백만 쿼리, 1.12GB 데이터로 구성
  • dbsqlfuzz

    • SQL과 데이터베이스 파일을 동시에 변형하는 libFuzzer 기반 퍼저
    • 하루 약 10억 번의 변이 테스트 수행, 악의적 입력에 대한 견고성 검증
  • 추가 도구

    • speedtest1.c, mptester.c, threadtest3.c, fuzzershell.c, jfuzz 등
    • 모든 테스트는 다중 플랫폼·컴파일 설정에서 통과해야 릴리스 가능

3. 이상 상황 테스트

  • OOM 테스트

    • malloc() 실패를 시뮬레이션하여 메모리 부족 시 정상 복구 여부 검증
    • 실패 시점 카운터를 증가시키며 반복 수행
  • I/O 오류 테스트

    • 가상 파일 시스템(VFS)을 이용해 디스크 오류를 시뮬레이션
    • 오류 후 PRAGMA integrity_check로 데이터 손상 여부 확인
  • 크래시 테스트

    • 전원 차단·OS 크래시 상황을 시뮬레이션
    • TCL 하니스는 자식 프로세스 기반, TH3는 메모리 기반 VFS 사용
    • 트랜잭션의 완전 롤백 또는 완전 완료 여부 검증
  • 복합 실패 테스트

    • 크래시 후 OOM 또는 I/O 오류가 연속 발생하는 상황까지 검증

4. 퍼즈 테스트

  • SQL Fuzz

    • 문법적으로 유효하지만 비정상적인 SQL을 생성해 SQLite 반응 검증
  • American Fuzzy Lop (AFL)

    • 2014년 도입된 프로파일 기반 퍼저로, 새로운 제어 경로를 탐색
    • SQLite의 assert 실패·크래시·잘못된 결과를 다수 발견
  • Google OSS Fuzz

    • 2016년부터 Google 인프라에서 자동 퍼징 수행
    • 신규 커밋에서 간헐적 문제를 탐지
  • dbsqlfuzz / jfuzz

    • 2018년 이후 내부 퍼저로 도입, SQL과 DB 파일 동시 변이
    • 하루 5억 건 이상 테스트, 외부 퍼저의 버그 리포트 거의 소멸
    • 2024년부터 jfuzz가 JSONB 입력 검증 추가
  • 타사 퍼저 및 fuzzcheck

    • 외부 연구자(예: Manuel Rigger)가 잘못된 결과 계산 사례 다수 발견
    • fuzzcheck 유틸리티가 과거 퍼즈 케이스 중 ‘흥미로운’ 수천 건을 재검증
  • MC/DC와 퍼즈 테스트의 긴장 관계

    • MC/DC는 방어 코드 최소화, 퍼즈는 방어 코드 필요
    • SQLite는 두 접근을 병행해 정상·악의적 입력 모두에 견고한 코드 유지

5. 회귀 테스트

  • 보고된 버그는 수정 후 반드시 새 테스트 케이스로 추가
    • 과거 버그의 재발 방지 목적

6. 자동 리소스 누수 검출

  • TCL·TH3 하니스가 메모리·파일·스레드·뮤텍스 누수 자동 감시
    • OOM·I/O 오류 후에도 메모리 누수가 없어야 함

7. 테스트 커버리지

  • SQLite 코어는 TH3 기준 100% 분기 커버리지 달성

    • FTS3, RTree 등 확장은 제외
  • Statement vs Branch Coverage

    • 분기 커버리지는 문장 커버리지보다 엄격하며, 모든 조건 분기를 양방향으로 검증
  • 방어 코드 커버리지

    • ALWAYS(), NEVER() 매크로로 방어 조건을 명시
    • 세 가지 정의 형태로 테스트를 반복해 일관성 검증
  • 경계값 및 불리언 벡터 테스트

    • testcase() 매크로로 조건의 양·음 결과 모두 검증
    • 1184개 testcase() 사용
  • MC/DC 달성

    • testcase() 매크로를 통해 모든 조건의 독립적 영향 검증
  • gcov 기반 측정

    • -fprofile-arcs -ftest-coverage 옵션으로 커버리지 측정
    • 결과 비교를 통해 컴파일러 버그나 정의되지 않은 동작 탐지
  • Mutation Testing

    • 분기 명령을 변경해 테스트가 이를 감지하는지 확인
    • 최적화 분기(/*OPTIMIZATION-IF-TRUE*/)는 예외 처리
  • 완전 커버리지 경험

    • 모든 분기 테스트 덕분에 코드 변경 시 부작용 최소화
    • 유지 비용은 높지만, 광범위 배포되는 인프라 라이브러리로서 정당화됨

8. 동적 분석

  • Assert()

    • 6754개 assert 문으로 전·후조건 및 루프 불변식 검증
    • SQLITE_DEBUG 빌드에서만 활성화
  • Valgrind

    • 메모리 오류·스택 오버플로·초기화되지 않은 메모리 접근 탐지
    • 릴리스 전 veryquick 및 TH3 테스트를 Valgrind로 실행
  • Memsys2

    • SQLITE_MEMDEBUG 빌드 시 메모리 오류 감시용 래퍼 삽입
    • Valgrind보다 빠르게 반복 검증 가능
  • Mutex Asserts

    • sqlite3_mutex_held() 등으로 멀티스레드 동기화 검증
  • Journal Tests

    • 롤백 저널이 DB보다 먼저 기록되는지 확인, 트랜잭션 원자성 보장
  • Undefined Behavior Checks

    • -ftrapv, -fsanitize=undefined, /RTC1 등으로 비정의 동작 탐지
    • 32/64비트, 엔디언, 다양한 CPU 아키텍처에서 반복 수행

9. 최적화 비활성화 테스트

  • sqlite3_test_control(SQLITE_TESTCTRL_OPTIMIZATIONS)으로 최적화 끄기
    • 최적화 유무에 관계없이 동일 결과를 산출해야 함
    • 일부 성능 측정용 테스트는 예외

10. 체크리스트

  • 릴리스 전 약 200개 항목의 수동 체크리스트 검증
    • 일부는 수초, 일부는 수시간 소요
    • 문제 발견 시 즉시 항목 추가, 지속적 개선

11. 정적 분석

  • GCC·Clang·MSVC에서 경고 없이 컴파일
    • Clang Static Analyzer에서도 유효 경고 없음
    • 정적 분석은 실제 버그 탐지 효과가 제한적

12. 요약

  • SQLite는 오픈소스임에도 상용 수준의 품질과 낮은 결함률을 유지
    • 철저한 테스트와 코드 설계가 핵심 요인
    • 모든 릴리스는 위 절차를 거쳐 미션 크리티컬 환경에서도 신뢰 가능한 DB 엔진으로 제공됨

Read Entire Article