Test & CI
TEKNİK REHBER TEST & CI KAPSAMA 2026

gcov & lcov —
kod kapsama analizi.

--coverage bayrağından genhtml HTML raporuna, cross-compile uyarılarından kernel gcov'a ve CI kapsama kapısına adım adım rehber.

00 Kod kapsama nedir? — temel kavramlar

Kod kapsama (code coverage), testlerin kaynak kodun ne kadarını çalıştırdığını ölçen bir yazılım kalite metriğidir. Hangi satırların, dalların ve fonksiyonların test edilmediğini görünür kılar.

Kapsama türleri

Kapsama türüÖlçtüğü şeyFormül
Line coverageÇalıştırılan kaynak satırlarıçalışan satırlar / toplam satırlar
Branch coverageif/else, switch dallarıalınan dallar / toplam dal sayısı
Function coverageÇağrılan fonksiyonlarçağrılan fonksiyon / toplam fonksiyon
Condition coverageBoolean alt ifadelerdeğerlendirilen koşullar / toplam koşullar

gcov vs llvm-cov

gcov, GCC'nin yerleşik kapsama aracıdır. -fprofile-arcs -ftest-coverage (veya kısaca --coverage) bayraklarıyla derlenen binary'ler çalıştığında .gcda dosyaları üretir. gcov bu dosyaları okuyarak satır bazlı kapsama raporu oluşturur.

llvm-cov, Clang/LLVM'in eşdeğer aracıdır. Daha detaylı bölge kapsama (region coverage) bilgisi sunar ve JSON çıktısı üretebilir. Clang kullanan projelerde tercih edilir.

Embedded sistemlerde kapsama kısıtlamaları

Dikkat — Embedded kısıtlamaları

Gömülü sistemlerde kapsama analizi birkaç önemli kısıtla gelir: (1) .gcda dosyaları yazılabilir bir filesystem gerektirir — ROM veya read-only rootfs'te çalışmaz; (2) dosya yazma yolu, cross-compile araç zincirine göre ayarlanmalıdır (GCOV_PREFIX); (3) kapsama enstrümantasyonu binary boyutunu ve yürütme hızını artırır — üretim image'larına dahil edilmemelidir.

01 Userspace gcov — gcc --coverage

Kullanıcı alanı C/C++ kodunda gcov kapsama verisi toplamak için --coverage derleyici bayrağı yeterlidir.

Derleme ve çalıştırma

bash — gcov enstrümantasyonu ile derleme
# --coverage = -fprofile-arcs -ftest-coverage (hem derleme hem link)
gcc --coverage -O0 -g -o myprogram main.c sensor.c utils.c

# Programa özgü testleri çalıştır
./myprogram --test-mode

# Derleme sonrası üretilen dosyalar:
ls *.gc*
# main.gcno    sensor.gcno    utils.gcno     ← derleme zamanı (kaynak→arc bilgisi)
# main.gcda    sensor.gcda    utils.gcda     ← çalışma zamanı (sayım verisi)

gcno ve gcda dosyaları

.gcno (graph notes)
Derleme sırasında üretilir. Kaynak kod yapısını ve arc (dallanma) bilgisini içerir. Binary ile birlikte tutulmalıdır — değişirse .gcda geçersiz olur.
.gcda (data)
Program çalışırken üretilir ve güncellenir. Kaç kez her arc'tan geçildiğini sayar. Program temiz çıkış yapmazsa (crash) veriler kaybolabilir.

Ham gcov raporu

bash — gcov ham rapor
# Tek dosya için kapsama raporu
gcov sensor.c

# Çıktı:
# File 'sensor.c'
# Lines executed:72.34% of 47
# Creating 'sensor.c.gcov'

# .gcov dosyasını oku
cat sensor.c.gcov
# -:    0:Source:sensor.c
# -:    1:#include "sensor.h"
# 1:    5:int sensor_init(sensor_t *s) {
# 1:    6:    s->calibrated = 0;
# #####: 7:    return -1;    ← Bu satır hiç çalışmadı!
# -:    8:}

CMake entegrasyonu

CMakeLists.txt — coverage build type
cmake_minimum_required(VERSION 3.18)
project(myproject C)

option(ENABLE_COVERAGE "Kod kapsama enstrümantasyonu" OFF)

if(ENABLE_COVERAGE)
  if(NOT CMAKE_BUILD_TYPE STREQUAL "Debug")
    message(WARNING "Coverage için Debug build type önerilir")
  endif()
  target_compile_options(mylib PRIVATE --coverage -O0 -g)
  target_link_options(mylib    PRIVATE --coverage)
  target_compile_options(tests PRIVATE --coverage -O0 -g)
  target_link_options(tests    PRIVATE --coverage)
endif()

# Kullanım:
# cmake -B build -DENABLE_COVERAGE=ON -DCMAKE_BUILD_TYPE=Debug
# cmake --build build
# cd build && ctest
# lcov --capture --directory . --output-file coverage.info

02 Cross-compile uyarıları — GCOV_PREFIX

Cross-compile ortamında gcov kullanmak, .gcda dosyalarının yazılacağı yolu açıkça belirlemeyi gerektirir. Aksi hâlde binary, host dosya sistemindeki derleme yoluna yazmaya çalışır.

Sorun: hardcoded derleme yolu

gcov enstrümantasyonu, .gcda dosyalarını derleme zamanındaki mutlak yola yazar. Arm çapraz derleyici ile /home/builder/project/src/sensor.c derlendiyse, hedef sistemde de bu yolu açmaya çalışır. Gömülü sistemde bu yol mevcut değildir ve .gcda dosyaları oluşturulamaz.

Çözüm: GCOV_PREFIX ve GCOV_PREFIX_STRIP

bash — GCOV_PREFIX ile .gcda yönlendirme
# DUT üzerinde (hedef sistemde):

# GCOV_PREFIX: .gcda dosyalarının yazılacağı yeni kök
# GCOV_PREFIX_STRIP: özgün yoldan baştan kaç bileşen silineceği

export GCOV_PREFIX=/tmp/coverage
export GCOV_PREFIX_STRIP=4
# Örnek: /home/builder/project/src/sensor.gcda
# Strip 4 → /project/src/sensor.gcda
# Prefix  → /tmp/coverage/project/src/sensor.gcda

./myprogram --test-mode

# .gcda dosyaları artık /tmp/coverage/ altında
ls -R /tmp/coverage/

Cross-compile kapsama iş akışı

  Host: cross-compile --coverage ile
       │
       ▼
  DUT: GCOV_PREFIX=/tmp/coverage ./myprogram
       │  .gcda dosyaları /tmp/coverage/ altına yazılır
       ▼
  Host: SCP ile .gcda dosyaları geri al
  scp -r root@dut:/tmp/coverage ./build/
       │
       ▼
  Host: .gcno dosyaları ile birleştir
  lcov --capture --directory ./build/ --output-file cov.info
       │
       ▼
  genhtml ile HTML rapor üret
    

.gcda sysroot stratejisi

bash — .gcda dosyalarını host'a aktarma
# DUT'ta çalışır: testleri çalıştır ve .gcda'ları topla
ssh root@${DUT_IP} "
  export GCOV_PREFIX=/tmp/cov
  export GCOV_PREFIX_STRIP=3
  /opt/myprogram/bin/myprogram --self-test
  tar czf /tmp/cov.tar.gz /tmp/cov/
"

# Host'a aktar
scp root@${DUT_IP}:/tmp/cov.tar.gz ./

# Build dizinine çıkart (.gcno dosyaları ile aynı yerde olmalı)
tar xzf cov.tar.gz --strip-components=2 -C ./build/

# lcov ile birleştir
lcov --capture --directory ./build/ --output-file coverage.info \
  --gcov-tool arm-linux-gnueabihf-gcov

03 lcov kullanımı — veri toplama ve işleme

lcov, gcov verilerini toplayan, filtreleyen ve genhtml için hazırlayan üst düzey bir araçtır. Karmaşık proje yapılarında gcov'u doğrudan kullanmaktan çok daha pratiktir.

Temel lcov iş akışı

bash — lcov tam iş akışı
# 1. Başlangıç (sıfır kapsama baseline) — isteğe bağlı
lcov --capture --initial \
  --directory ./build \
  --output-file baseline.info

# 2. Testleri çalıştır
./build/run_tests

# 3. Test sonrası kapsama verisi topla
lcov --capture \
  --directory ./build \
  --output-file test.info \
  --gcov-tool gcov   # cross-compile için: --gcov-tool arm-linux-gnueabihf-gcov

# 4. Baseline ile birleştir (isteğe bağlı, çalışmayan kodu da göster)
lcov --add-tracefile baseline.info \
     --add-tracefile test.info \
     --output-file combined.info

# 5. Filtrele: sistem ve üçüncü taraf başlıkları hariç tut
lcov --remove combined.info \
  '/usr/*' \
  '*/tests/*' \
  '*/external/*' \
  --output-file filtered.info

# 6. Özet yazdır
lcov --summary filtered.info

lcov --summary çıktısı

lcov summary örnek çıktı
Reading tracefile filtered.info
Summary coverage rate:
  lines......: 83.2% (1245 of 1496 lines)
  functions..: 91.4% (96 of 105 functions)
  branches...: 71.8% (447 of 622 branches)

genhtml ile HTML rapor üretme

bash — genhtml HTML rapor
genhtml filtered.info \
  --output-directory coverage-report/ \
  --title "MyProject Kod Kapsama Raporu" \
  --legend \
  --show-details \
  --highlight \
  --num-spaces 4

# Raporu aç
xdg-open coverage-report/index.html

# Web sunucusu üzerinden yayınla (CI artifact'larında)
python3 -m http.server 8080 --directory coverage-report/

lcov yapılandırma dosyası

.lcovrc — proje seviyesi yapılandırma
# Otomatik filtre: test ve harici kod hariç
lcov_excl_pattern = .*_test\.c
lcov_excl_pattern = .*/external/.*

# Yüksek/orta/düşük kapsama eşikleri (HTML'de renklendirme)
genhtml_hi_limit = 90
genhtml_med_limit = 75

# Branch kapsama göster
lcov_branch_coverage = 1

# gcov aracı (cross-compile için özelleştir)
# gcov_tool = arm-linux-gnueabihf-gcov

04 HTML rapor okuma

genhtml'nin ürettiği HTML rapor, kapsanmayan kodu hızla bulmak için oldukça kullanışlıdır. Renk kodlaması ve metrikler bir bakışta neyin test edilmediğini gösterir.

Rapor yapısı

HTML rapor üç seviyeden oluşur: index.html tüm proje özetini ve dizin bazlı istatistikleri gösterir. Her dizine tıklandığında o dizindeki dosyaların listesi görülür. Dosya adına tıklandığında kaynak kod görünümü açılır.

Renk kodlaması

RenkAnlamı
Yeşil arka planSatır test tarafından çalıştırıldı
Kırmızı arka planSatır hiç çalıştırılmadı (ölü kod adayı)
Sarı/TuruncuKısmi branch coverage (if'in sadece bir kolu test edildi)
Mavi rakamO satırın kaç kez çalıştırıldığı (hit count)

Branch coverage görünümü

Kaynak kod görünümü — branch annotasyonu
// gcov branch anotasyonu örneği:
// [ branch 0: 45 geçti,  branch 1: 0 geçti ]
if (sensor->calibrated) {           // ← sadece true kolu test edildi!
    return sensor->read();
} else {
    return -EINVAL;                  // ← bu kol hiç çalışmadı
}

Kapsanmayan kodu yorumlama

Kapsanmayan satırların her zaman test eksikliği anlamına gelmediğine dikkat edin. Bazı yaygın durumlar: hata işleme yolları (malloc başarısızlığı, NULL pointer — kasıtlı olarak test edilmeyebilir), platform özgü kod (#ifdef ile ayrılan dallar), ölü kod (gerçekten ulaşılamaz satırlar). lcov'un LCOV_EXCL_LINE ve LCOV_EXCL_START/STOP direktifleri ile bu tür satırlar rapordan hariç tutulabilir.

C — lcov hariç tutma direktifleri
// Tek satırı hariç tut
void *buf = malloc(size);
if (!buf) return -ENOMEM;  // LCOV_EXCL_LINE

// Bir bloğu hariç tut
// LCOV_EXCL_START
#ifdef DEBUG
    dump_registers();
#endif
// LCOV_EXCL_STOP

05 Kernel gcov — CONFIG_GCOV_KERNEL

Linux kernel de gcov enstrümantasyonunu destekler. Kernel kodu kapsama analizi, sürücü test kalitesini değerlendirmek için değerli bilgi sağlar.

Kernel konfigürasyonu

Kconfig — kernel gcov seçenekleri
CONFIG_GCOV_KERNEL=y
# Otomatik format algılama (önerilen — kernel versiyonuna göre)
CONFIG_GCOV_FORMAT_AUTODETECT=y

# Veya açıkça belirt:
# CONFIG_GCOV_FORMAT_GCOV4 — GCC 4.7-9
# CONFIG_GCOV_FORMAT_GCOV9 — GCC ≥ 10

# debugfs gerekli (gcov verisine erişim için)
CONFIG_DEBUG_FS=y

# Belirli bir modülü enstrümanlamak için:
# drivers/i2c/Makefile içine ekle:
# GCOV_PROFILE_i2c-core.o := y
# Veya tüm dizin için:
# GCOV_PROFILE := y

Kernel gcov verisi toplama

bash — kernel gcov veri toplama
# debugfs'i mount et
mount -t debugfs nodev /sys/kernel/debug

# gcov dizini
ls /sys/kernel/debug/gcov/
# /sys/kernel/debug/gcov/proc/
# /sys/kernel/debug/gcov/drivers/
# /sys/kernel/debug/gcov/kernel/

# .gcda dosyaları burada yaşar (her modül/dosya için)
ls /sys/kernel/debug/gcov/drivers/i2c/
# i2c-core.gcda  i2c-dev.gcda

# Tüm gcov verilerini kopyala
mkdir -p /tmp/kernel-cov
cp -r /sys/kernel/debug/gcov/* /tmp/kernel-cov/

Kernel kapsama raporu üretme

bash — kernel-gcov-report.sh
#!/bin/bash
# Kernel gcov raporu — host üzerinde çalıştır

KERNEL_SRC="/path/to/linux"
KERNEL_BUILD="/path/to/build"
GCDA_DIR="/tmp/kernel-cov"

# .gcda dosyalarını build dizinine kopyala
# (kernel gcov, .gcno dosyaları ile aynı yerde .gcda bekler)
find "$GCDA_DIR" -name "*.gcda" | while read f; do
    rel="${f#$GCDA_DIR/}"
    dest="$KERNEL_BUILD/$rel"
    mkdir -p "$(dirname "$dest")"
    cp "$f" "$dest"
done

# lcov ile veri topla
lcov --capture \
  --directory "$KERNEL_BUILD" \
  --output-file kernel-raw.info \
  --gcov-tool gcov \
  --rc lcov_branch_coverage=1

# Filtrele: yalnızca hedef sürücü
lcov --extract kernel-raw.info \
  "*/drivers/i2c/*" \
  --output-file i2c-coverage.info

# HTML rapor
genhtml i2c-coverage.info \
  --output-directory kernel-coverage-html/ \
  --title "Linux Kernel i2c Sürücü Kapsamı"

echo "Rapor: kernel-coverage-html/index.html"

gcov-tool kullanımı

bash — gcov-tool ile .gcda birleştirme
# Birden fazla test çalıştırmasının .gcda verilerini birleştir
# (kernel modülü birden fazla test senaryosunda kullanıldıysa)
gcov-tool merge run1/ run2/ -o merged/

# gcov-tool özet
gcov-tool summary merged/ --format=json

06 CI kapsama kapısı — failure threshold

CI kapsama kapısı, kapsama oranı belirlenen eşiğin altına düştüğünde pipeline'ın başarısız olmasını sağlar. Bu mekanizma, kod tabanında kapsama regresyonunu önler.

lcov kapsama eşiği kontrolü

bash — kapsama eşik kontrolü scripti
#!/bin/bash
# coverage-gate.sh — %80 eşik kontrolü

THRESHOLD=${1:-80}
INFO_FILE="${2:-filtered.info}"

# lcov summary'den yüzde değerini çek
COVERAGE=$(lcov --summary "$INFO_FILE" 2>&1 | \
  grep "lines" | grep -oP '\d+\.\d+(?=%)' | head -1)

echo "Satır kapsamı: %${COVERAGE}"
echo "Eşik: %${THRESHOLD}"

# Python ile karşılaştır (bash float karşılaştırması güvensiz)
python3 - << EOF
coverage = float("${COVERAGE}")
threshold = float("${THRESHOLD}")
if coverage >= threshold:
    print(f"PASS: %{coverage:.1f} >= %{threshold}")
    exit(0)
else:
    print(f"FAIL: %{coverage:.1f} < %{threshold}")
    exit(1)
EOF

coverage.py XML ve GitLab kapsama badge

bash — Cobertura XML üretimi (lcov → cobertura)
# lcov2cobertura ile Cobertura XML formatına dönüştür
pip install lcov-cobertura
lcov_cobertura filtered.info \
  --base-dir src/ \
  --output coverage.xml

# GitLab CI artifacts:reports:coverage_report için Cobertura formatı gerekir

GitLab kapsama regex ile badge

.gitlab-ci.yml — kapsama badge yapılandırması
coverage-job:
  script:
    - lcov --summary filtered.info 2>&1 | tee coverage-summary.txt
  # GitLab bu regex ile kapsama yüzdesini çeker ve badge gösterir
  coverage: '/lines\.*:\s+(\d+\.\d+)%/'

07 Pratik: %80 kapsama hedefli GitLab CI pipeline

Embedded C kütüphanesi için sıfırdan kapsama altyapısı kurulumu: derleme, test çalıştırma, rapor üretme ve GitLab CI ile entegrasyon.

Proje yapısı

Proje dizin yapısı
libsensor/
├── src/
│   ├── sensor.c
│   ├── sensor_cal.c
│   └── sensor_filter.c
├── include/
│   └── sensor.h
├── tests/
│   ├── test_sensor.c
│   ├── test_calibration.c
│   └── CMakeLists.txt
├── CMakeLists.txt
├── .lcovrc
└── .gitlab-ci.yml

CMakeLists.txt — kapsama desteği

CMakeLists.txt
cmake_minimum_required(VERSION 3.20)
project(libsensor C)

set(CMAKE_C_STANDARD 11)

add_library(sensor STATIC
    src/sensor.c
    src/sensor_cal.c
    src/sensor_filter.c
)
target_include_directories(sensor PUBLIC include/)

# Kapsama seçeneği
option(COVERAGE "Kapsama enstrümantasyonu" OFF)
if(COVERAGE)
    target_compile_options(sensor PUBLIC
        --coverage -O0 -g --no-inline
    )
    target_link_options(sensor PUBLIC --coverage)
endif()

# Testler
enable_testing()
add_subdirectory(tests)

Tam .gitlab-ci.yml

.gitlab-ci.yml — %80 kapsama hedefli pipeline
image: debian:bookworm-slim

stages:
  - build
  - test
  - coverage
  - report

variables:
  BUILD_DIR:      "${CI_PROJECT_DIR}/build-cov"
  COVERAGE_DIR:   "${CI_PROJECT_DIR}/coverage"
  THRESHOLD:      "80"

before_script:
  - apt-get update -qq
  - apt-get install -y -qq gcc cmake lcov python3-pip git
  - pip install lcov-cobertura -q

# ── Kapsama ile Derleme ──────────────────────────────

build-with-coverage:
  stage: build
  script:
    - cmake -B ${BUILD_DIR}
        -DCMAKE_BUILD_TYPE=Debug
        -DCOVERAGE=ON
        -DCMAKE_C_COMPILER=gcc
    - cmake --build ${BUILD_DIR} -j$(nproc)
  artifacts:
    paths:
      - ${BUILD_DIR}/
    expire_in: 2h

# ── Testleri Çalıştır ────────────────────────────────

run-tests:
  stage: test
  needs: [build-with-coverage]
  script:
    - cd ${BUILD_DIR}
    - ctest --output-on-failure -j$(nproc)
  artifacts:
    paths:
      - ${BUILD_DIR}/
    expire_in: 2h

# ── Kapsama Raporu Üret ──────────────────────────────

generate-coverage:
  stage: coverage
  needs: [run-tests]
  script:
    - mkdir -p ${COVERAGE_DIR}

    # .gcda verilerini topla
    - lcov --capture
        --directory ${BUILD_DIR}
        --output-file ${COVERAGE_DIR}/raw.info
        --rc lcov_branch_coverage=1

    # Filtrele: sadece kaynak kodu dahil et
    - lcov --extract ${COVERAGE_DIR}/raw.info
        "${CI_PROJECT_DIR}/src/*"
        --output-file ${COVERAGE_DIR}/filtered.info
        --rc lcov_branch_coverage=1

    # Özet yazdır (GitLab badge için regex uyumlu)
    - lcov --summary ${COVERAGE_DIR}/filtered.info
        --rc lcov_branch_coverage=1 2>&1 | tee ${COVERAGE_DIR}/summary.txt

    # HTML rapor
    - genhtml ${COVERAGE_DIR}/filtered.info
        --output-directory ${COVERAGE_DIR}/html/
        --title "libsensor Kapsama"
        --legend --show-details
        --branch-coverage

    # Cobertura XML (GitLab artifacts için)
    - lcov_cobertura ${COVERAGE_DIR}/filtered.info
        --base-dir src/
        --output ${COVERAGE_DIR}/cobertura.xml

    # Eşik kontrolü
    - |
      LINES=$(lcov --summary ${COVERAGE_DIR}/filtered.info 2>&1 | \
        grep "lines" | grep -oP '\d+\.\d+(?=%)' | head -1)
      echo "Satır kapsamı: ${LINES}%  (eşik: ${THRESHOLD}%)"
      python3 -c "
      cov = float('${LINES}')
      thr = float('${THRESHOLD}')
      print(f'PASS: {cov:.1f}% >= {thr}%' if cov >= thr else f'FAIL: {cov:.1f}% < {thr}%')
      import sys; sys.exit(0 if cov >= thr else 1)
      "

  coverage: '/lines\.*:\s+(\d+\.\d+)%/'

  artifacts:
    reports:
      coverage_report:
        coverage_format: cobertura
        path: coverage/cobertura.xml
    paths:
      - coverage/html/
      - coverage/summary.txt
    when: always
    expire_in: 1 week

# ── Raporu Yayınla ────────────────────────────────────

pages:
  stage: report
  needs: [generate-coverage]
  script:
    - mkdir -p public
    - cp -r ${COVERAGE_DIR}/html/ public/coverage/
  artifacts:
    paths:
      - public/
  only:
    - main

Cross-compile için pipeline değişiklikleri

.gitlab-ci.yml — ARM cross-compile kapsama
build-arm-coverage:
  stage: build
  image: registry.example.com/arm-toolchain:latest
  script:
    - cmake -B ${BUILD_DIR}
        -DCMAKE_TOOLCHAIN_FILE=cmake/arm-toolchain.cmake
        -DCOVERAGE=ON
        -DCMAKE_BUILD_TYPE=Debug
    - cmake --build ${BUILD_DIR} -j$(nproc)

run-arm-tests:
  stage: test
  tags: [arm-hil-board]  # Gerçek ARM donanımında çalıştır
  script:
    - scp ${BUILD_DIR}/tests/test_sensor root@${DUT_IP}:/tmp/
    - ssh root@${DUT_IP} "
        export GCOV_PREFIX=/tmp/cov
        export GCOV_PREFIX_STRIP=5
        /tmp/test_sensor
        tar czf /tmp/cov.tar.gz /tmp/cov/ 2>/dev/null || true
      "
    - scp root@${DUT_IP}:/tmp/cov.tar.gz ${BUILD_DIR}/
    - tar xzf ${BUILD_DIR}/cov.tar.gz -C ${BUILD_DIR}/

generate-arm-coverage:
  stage: coverage
  script:
    - lcov --capture
        --directory ${BUILD_DIR}
        --gcov-tool arm-linux-gnueabihf-gcov
        --output-file ${COVERAGE_DIR}/raw.info
İpucu

Kapsama enstrümantasyonu binary boyutunu %20-50 artırabilir ve çalışma hızını düşürebilir. Bu nedenle kapsama derlemesi her zaman ayrı bir build konfigürasyonu (-DCOVERAGE=ON) olarak tutulmalı ve üretim binary'lerine asla dahil edilmemelidir. CMake'de if(COVERAGE) bloğu bu ayrımı temiz biçimde sağlar.