Tüm eğitimler
TEKNİK REHBER GÖMÜLÜ LİNUX KUnit 2026

KUnit
Linux Kernel Unit Test Çatısı

Kernel içinde birim testi — test suite, assertion, fixture ve kunit.py ile UML kernel üzerinde CI entegrasyonu.

00 Kernel testine neden ihtiyaç var

Linux kernel geliştirme süreçlerinde hataların büyük çoğunluğu regresyon kaynaklıdır — önceki testlerden geçen bir kod parçası yeni bir değişiklikle bozulur ve sürücü kalitesi düşer.

Kernel geliştirmenin test edilmesindeki güçlükler

Donanım bağımlılığıÇoğu sürücü gerçek donanım olmadan test edilemez — her değişiklik için fiziksel board gerekebilir
Kullanıcı alanı izolasyonuglibc, syscall, userspace framework'leri çalışmaz; standart C test kütüphaneleri (Google Test, Unity) kernel space'te kullanılamaz
Panik riskiBir sürücü hatası sistemi çökertebilir — test ortamı geri dönülemez olabilir
Yavaş feedback döngüsüGeleneksel kernel testi: kernel derle → flash → boot → manuel test → hata raporla — dakikalar/saatler sürer

Test türleri

Test türüAraçÇalıştığı yerUygun olduğu alan
Unit testKUnitUML kernel veya gerçek kernelSürücü alt fonksiyonları, veri yapıları, algoritmalar
Functional testkselftestGerçek kernel, user space testSyscall davranışı, ABI uyumluluğu
Integration testLTPGerçek kernel, rootfsSistem geneli davranış, stress
FuzzingsyzkallerQEMU/KVMGüvenlik açığı keşfi, edge case

KUnit'in avantajları

Değişiklik yaz → kunit.py run → UML kernel boot → testler çalışır → TAP raporu → 30 saniye içinde sonuç

KUnit, Google'ın kernel ekibinin 2019'da Linux'a katkıladığı bir birim test çatısıdır. User Mode Linux (UML) üzerinde gerçek donanım olmadan saniyeler içinde çalışır; CI pipeline'larına kolayca entegre edilebilir.

Bu bölümde

  • Kernel regresyonlar, donanım bağımlılığı ve panik riski testi zorlaştırır
  • KUnit: UML üzerinde saniyeler içinde çalışan kernel içi birim test çatısı
  • kselftest: kullanıcı alanından syscall testi; LTP: sistem stres testi
  • KUnit 2019'dan itibaren mainline Linux'ta; drivers/*/tests/ dizinlerinde örnekler mevcut

01 KUnit mimarisi

KUnit, test suite ve test case hiyerarşisi üzerine kuruludur. Her test case bir struct kunit bağlamında çalışır; sonuçlar TAP (Test Anything Protocol) formatında raporlanır.

Terim sözlüğü

test caseTek bir fonksiyon — kunit_test_suite() içinde tanımlanır. Bir konuyu tek odak noktasıyla sınar.
test suiteİlgili test case'lerin kümesi — init/exit hook'ları paylaşır. kunit_test_suite() makrosu ile kayıt edilir.
assertionKUNIT_EXPECT_* veya KUNIT_ASSERT_* — başarısız olduğunda raporlar. ASSERT türü testi durdurur, EXPECT devam ettirir.
TAP outputTest Anything Protocol — her test "ok" veya "not ok" satırı üretir; CI araçları bu formatı otomatik ayrıştırır.
struct kunitTest çalışma zamanı bağlamı — bellek yönetimi, log, beklenti durumunu taşır. Her test case argüman olarak alır.

KUnit kaynak ağacı

Yolİçerik
lib/kunit/KUnit çekirdek: test.c, assert.c, resource.c, string-stream.c
include/kunit/test.hAna başlık — tüm makro ve struct tanımları
include/kunit/mock.hMock altyapısı (deneysel)
tools/testing/kunit/kunit.py — UML runner, wrapper script
drivers/*/tests/Sürücü birim testleri — iio, regmap, clk, drm alt sistemleri
lib/kunit/kunit-example-test.cReferans örnek — yeni başlayanlar için şablon

TAP çıktısı formatı

tap
TAP version 14
    # Subtest: example_test_suite
    1..3
    ok 1 - example_simple_test
    not ok 2 - example_failing_test
        # example_failing_test: FAILED at lib/kunit/kunit-example-test.c:42
        # Expected 1 + 1 == 3, but:
        #        1 + 1 == 2
        #        3 == 3 is false
    ok 3 - example_init_test
# example_test_suite: 2 passed, 1 failed
not ok 1 - example_test_suite

Kernel konfigürasyonu

bash
# KUnit'i etkinleştir
# CONFIG_KUNIT=y           — KUnit çatısı (zorunlu)
# CONFIG_KUNIT_TEST=y      — KUnit kendi öz testleri
# CONFIG_KUNIT_EXAMPLE_TEST=y — örnek test modülü

# Kernel config'te etkinleştir:
./scripts/config --enable CONFIG_KUNIT
./scripts/config --enable CONFIG_KUNIT_TEST
./scripts/config --enable CONFIG_KUNIT_EXAMPLE_TEST

# kunit.py otomatik bu ayarları yapar (ayrıca el ile gerekmez)

Bu bölümde

  • test case → test suite → kunit_test_suite() kayıt hiyerarşisi
  • struct kunit: her test'e geçilen bağlam nesnesi
  • TAP çıktısı: CI araçlarının ayrıştırdığı standart format
  • lib/kunit/ çekirdek; tools/testing/kunit/kunit.py UML runner

02 İlk KUnit testi

Sıfırdan bir KUnit test modülü yazmak — test case fonksiyonu, suite tanımı ve Kconfig/Makefile entegrasyonu.

Test dosyası yapısı

c — drivers/misc/mydrv/mydrv-test.c
// SPDX-License-Identifier: GPL-2.0
/*
 * mydrv KUnit birim testleri
 * Bu dosya yalnızca CONFIG_MYDRV_KUNIT_TEST=y ile derlenir.
 */
#include <kunit/test.h>
#include "mydrv.h"   /* test edilen modülün başlığı */

/* ------------------------------------------------------------------ */
/*  Test case 1: temel ekleme fonksiyonu                               */
/* ------------------------------------------------------------------ */
static void mydrv_add_test(struct kunit *test)
{
    /* Beklenti: 2 + 3 = 5 */
    KUNIT_EXPECT_EQ(test, mydrv_add(2, 3), 5);
    KUNIT_EXPECT_EQ(test, mydrv_add(0, 0), 0);
    KUNIT_EXPECT_EQ(test, mydrv_add(-1, 1), 0);
}

/* ------------------------------------------------------------------ */
/*  Test case 2: sınır değerleri                                       */
/* ------------------------------------------------------------------ */
static void mydrv_boundary_test(struct kunit *test)
{
    /* INT_MAX + 1 taşma kontrolü (örnek) */
    KUNIT_EXPECT_TRUE(test, mydrv_is_valid_range(0));
    KUNIT_EXPECT_TRUE(test, mydrv_is_valid_range(255));
    KUNIT_EXPECT_FALSE(test, mydrv_is_valid_range(256));
    KUNIT_EXPECT_FALSE(test, mydrv_is_valid_range(-1));
}

/* ------------------------------------------------------------------ */
/*  Test suite tanımı                                                  */
/* ------------------------------------------------------------------ */
static struct kunit_case mydrv_test_cases[] = {
    KUNIT_CASE(mydrv_add_test),
    KUNIT_CASE(mydrv_boundary_test),
    {}   /* sentinel — dizi sonu */
};

static struct kunit_suite mydrv_test_suite = {
    .name  = "mydrv",
    .test_cases = mydrv_test_cases,
};

/* Suite'i KUnit'e kaydet */
kunit_test_suite(mydrv_test_suite);

Kconfig girdisi

kconfig — drivers/misc/mydrv/Kconfig
config MYDRV
    tristate "My example driver"
    help
      Basit örnek sürücü.

config MYDRV_KUNIT_TEST
    tristate "KUnit tests for mydrv" if !KUNIT_ALL_TESTS
    depends on MYDRV && KUNIT
    default KUNIT_ALL_TESTS
    help
      mydrv KUnit birim testleri.
      CONFIG_KUNIT=y/m gerektirir.

Makefile girdisi

makefile — drivers/misc/mydrv/Makefile
obj-$(CONFIG_MYDRV)            += mydrv.o
obj-$(CONFIG_MYDRV_KUNIT_TEST) += mydrv-test.o

kunit.py ile hızlı çalıştırma

bash
# Test modülünü isme göre çalıştır (UML kernel)
./tools/testing/kunit/kunit.py run --filter_glob 'mydrv*'

# Tüm testleri çalıştır
./tools/testing/kunit/kunit.py run

# Beklenen çıktı:
# [PASSED] mydrv_add_test
# [PASSED] mydrv_boundary_test
# ============================================================
# Testing complete. 2 tests run. 0 failures. 0 crashed.
NOT

Test dosyasının adı geleneksel olarak <modül>-test.c veya <modül>_test.c şeklindedir. Bazı alt sistemler tests/ alt dizini kullanır (ör. drivers/iio/tests/). Hangi düzeni kullanırsanız kullanın, test kodu ana modül kodundan ayrı bir derleme birimi (obj-) olmalıdır.

Bu bölümde

  • Test case: static void foo(struct kunit *test) — KUNIT_EXPECT_* ile sına
  • kunit_test_cases[] dizisi + kunit_test_suite() + kunit_test_suite() kayıt
  • Kconfig: depends on MYDRV && KUNIT, default KUNIT_ALL_TESTS
  • Makefile: obj-$(CONFIG_MYDRV_KUNIT_TEST) += mydrv-test.o

03 Assertion türleri

KUnit iki kategoride assertion sunar: KUNIT_EXPECT_* (başarısızlıkta test devam eder) ve KUNIT_ASSERT_* (başarısızlıkta test anında durur). Her ikisi de zengin karşılaştırma makro setiyle gelir.

EXPECT vs ASSERT farkı

Makro ailesiBaşarısızlık davranışıNe zaman kullanılır
KUNIT_EXPECT_*Hatayı raporlar, test devam ederBirden fazla bağımsız koşul — hepsini raporlamak istersiniz
KUNIT_ASSERT_*Hatayı raporlar, test anında dururÖnceki bir koşulun doğru olması sonraki adım için zorunluysa

Temel karşılaştırma makroları

c
/* Eşitlik */
KUNIT_EXPECT_EQ(test, left, right);      /* left == right */
KUNIT_EXPECT_NE(test, left, right);      /* left != right */

/* Büyük/küçük */
KUNIT_EXPECT_LT(test, left, right);      /* left < right */
KUNIT_EXPECT_LE(test, left, right);      /* left <= right */
KUNIT_EXPECT_GT(test, left, right);      /* left > right */
KUNIT_EXPECT_GE(test, left, right);      /* left >= right */

/* Boolean */
KUNIT_EXPECT_TRUE(test, condition);
KUNIT_EXPECT_FALSE(test, condition);

/* Pointer */
KUNIT_EXPECT_NULL(test, ptr);
KUNIT_EXPECT_NOT_NULL(test, ptr);
KUNIT_EXPECT_PTR_EQ(test, ptr1, ptr2);
KUNIT_EXPECT_PTR_NE(test, ptr1, ptr2);

/* Bellek karşılaştırma */
KUNIT_EXPECT_MEMEQ(test, buf1, buf2, size);    /* memcmp == 0 */
KUNIT_EXPECT_MEMNEQ(test, buf1, buf2, size);   /* memcmp != 0 */

/* String karşılaştırma */
KUNIT_EXPECT_STREQ(test, str1, str2);    /* strcmp == 0 */
KUNIT_EXPECT_STRNEQ(test, str1, str2);   /* strcmp != 0 */

Hata mesajı özelleştirme

c
/* _MSG varyantları: başarısızlıkta özel mesaj yazdırır */
KUNIT_EXPECT_EQ_MSG(test, actual, expected,
                    "Reg 0x%x beklenen 0x%x, okunan 0x%x",
                    reg_addr, expected, actual);

/* kunit_fail_msg: koşulsuz test başarısızlığı ve özel mesaj */
if (ptr == NULL) {
    kunit_fail(test, "kmalloc NULL döndürdü — bellek yetersiz olabilir");
    return;  /* bu noktadan sonra devam etmek anlamsız */
}

/* kunit_info / kunit_warn / kunit_err: log seviyeleri */
kunit_info(test, "Test verisi: %d eleman\n", count);
kunit_warn(test, "Timeout yaklasik, sonuclar guvenilmeyebilir\n");

ASSERT kullanım örneği

c
static void mydrv_parse_test(struct kunit *test)
{
    struct mydrv_config *cfg;
    const char *raw = "speed=100,mode=duplex";

    /* cfg NULL ise devam etmek anlamsız — ASSERT kullan */
    cfg = mydrv_parse_config(raw);
    KUNIT_ASSERT_NOT_NULL(test, cfg);

    /* cfg geçerli — alanları tek tek kontrol et */
    KUNIT_EXPECT_EQ(test, cfg->speed, 100);
    KUNIT_EXPECT_EQ(test, cfg->mode,  MYDRV_DUPLEX);

    mydrv_free_config(cfg);
}
DİKKAT

KUNIT_ASSERT_* başarısızlığında kunit_do_failed_assertion() çağrılır ve test case fonksiyonu kernel'deki kunit_try_catch mekanizması aracılığıyla longjmp benzeri bir şekilde sonlandırılır. Bu nedenle KUNIT_ASSERT_* sonrasına temizleme (cleanup) kodları koymayın — fixture exit hook kullanın.

Bu bölümde

  • KUNIT_EXPECT_*: devam eden test — birden fazla bağımsız koşulu raporlar
  • KUNIT_ASSERT_*: durduran test — sonraki adım için ön koşul gerektiğinde
  • _MSG varyantları özel hata mesajı; kunit_fail() koşulsuz başarısızlık
  • ASSERT sonrası temizleme yapma — fixture exit hook'unu kullan

04 Test fixtures ve init/exit

Test fixture'ları, birden fazla test case'in paylaştığı kurulum (setup) ve temizlik (teardown) kodunu merkezi hâle getirir. KUnit'te bunu struct kunit_suite'in init/exit alanları ve kunit_alloc_resource() ile sağlarsınız.

Suite seviyesi init/exit

c
/* Suite genelindeki veri — tüm test case'ler paylaşır */
struct mydrv_fixture {
    struct mydrv_dev  *dev;
    void              *shared_buf;
};

/* Her test case çalışmadan ÖNCE çağrılır */
static int mydrv_test_init(struct kunit *test)
{
    struct mydrv_fixture *fixture;

    /* kunit_kzalloc: test bitince otomatik serbest bırakılır */
    fixture = kunit_kzalloc(test, sizeof(*fixture), GFP_KERNEL);
    KUNIT_ASSERT_NOT_NULL(test, fixture);

    /* Sahte (fake) bir cihaz oluştur */
    fixture->dev = mydrv_fake_device_create();
    KUNIT_ASSERT_NOT_NULL(test, fixture->dev);

    fixture->shared_buf = kunit_kzalloc(test, 4096, GFP_KERNEL);
    KUNIT_ASSERT_NOT_NULL(test, fixture->shared_buf);

    /* Bağlamı test'e bağla */
    test->priv = fixture;
    return 0;
}

/* Her test case bittikten SONRA çağrılır */
static void mydrv_test_exit(struct kunit *test)
{
    struct mydrv_fixture *fixture = test->priv;
    if (fixture && fixture->dev)
        mydrv_fake_device_destroy(fixture->dev);
    /* kunit_kzalloc ile alınan bellek otomatik serbest bırakılır */
}

/* Test case: fixture'dan cihaza eriş */
static void mydrv_reg_read_test(struct kunit *test)
{
    struct mydrv_fixture *f = test->priv;
    u32 val;

    KUNIT_ASSERT_EQ(test, mydrv_reg_read(f->dev, 0x00, &val), 0);
    KUNIT_EXPECT_EQ(test, val, 0xDEADBEEF);   /* reset değeri */
}

static struct kunit_case mydrv_reg_cases[] = {
    KUNIT_CASE(mydrv_reg_read_test),
    {}
};

static struct kunit_suite mydrv_reg_suite = {
    .name       = "mydrv_reg",
    .init       = mydrv_test_init,
    .exit       = mydrv_test_exit,
    .test_cases = mydrv_reg_cases,
};

kunit_test_suite(mydrv_reg_suite);

kunit_alloc_resource() ile otomatik temizleme

c
#include <kunit/resource.h>

/* Kaynak tanımı */
static void mydrv_buf_free(struct kunit_resource *res)
{
    kfree(res->data);
}

/* Test case içinde otomatik temizlenen kaynak */
static void mydrv_dma_test(struct kunit *test)
{
    void *dma_buf;

    /* Test bittiğinde mydrv_buf_free() otomatik çağrılır */
    dma_buf = kunit_alloc_resource(test,
                                   NULL,           /* alloc — NULL: data manuel set */
                                   mydrv_buf_free, /* free  */
                                   GFP_KERNEL,
                                   NULL);
    /* Alternatif: kunit_kzalloc kullan (daha yaygın) */
    dma_buf = kunit_kzalloc(test, PAGE_SIZE, GFP_KERNEL);
    KUNIT_ASSERT_NOT_NULL(test, dma_buf);

    /* DMA transfer testi */
    mydrv_dma_fill(dma_buf, 0xAB, PAGE_SIZE);
    KUNIT_EXPECT_EQ(test, ((u8 *)dma_buf)[0], 0xAB);
    KUNIT_EXPECT_EQ(test, ((u8 *)dma_buf)[PAGE_SIZE - 1], 0xAB);
}

Suite vs case seviyesi init karşılaştırması

MekanizmaKapsamÇağrılma zamanıTipik kullanım
suite.init / suite.exitHer test caseHer case öncesi/sonrasıCihaz mock, paylaşılan tampon
suite.suite_init / suite_exitTüm suiteSuite başı/sonu (tek sefer)Pahalı kaynaklar, global register
kunit_kzallocTest case içiOtomatik, case bitinceTek testte gerekli küçük tampon

Bu bölümde

  • suite.init / suite.exit: her test case için çalışır — cihaz ve tampon kurulumu
  • test->priv: fixture verisini test case'e taşımak için pointer
  • kunit_kzalloc: test bitince otomatik serbest bırakılır — manuel free gerekmez
  • suite_init/suite_exit: tüm suite için tek seferlik pahalı kaynak yönetimi

05 Mocking ve bağımlılık enjeksiyonu

Kernel sürücüleri donanıma doğrudan bağımlıdır. Birim testinde donanımı simüle etmek için bağımlılık enjeksiyonu (dependency injection) ve struct üzerinden fonksiyon işaretçisi (ops struct) mocking yöntemi kullanılır.

Ops struct ile bağımlılık enjeksiyonu

c — mydrv.h
/* Sürücü operasyon tablosu — gerçek veya sahte (fake) */
struct mydrv_ops {
    int  (*reg_read) (void *priv, u32 offset, u32 *val);
    int  (*reg_write)(void *priv, u32 offset, u32  val);
    void (*reset)    (void *priv);
};

struct mydrv_dev {
    const struct mydrv_ops *ops;
    void                   *priv;      /* hw private data */
    /* sürücü durumu ... */
};

/* Gerçek donanım uygulaması */
extern const struct mydrv_ops mydrv_hw_ops;
c — mydrv-test.c
/* Test verisi: kayıt deposu */
struct fake_hw {
    u32 regs[256];
    int reset_count;
};

/* Sahte (fake) register okuma */
static int fake_reg_read(void *priv, u32 offset, u32 *val)
{
    struct fake_hw *hw = priv;
    if (offset >= ARRAY_SIZE(hw->regs)) return -EINVAL;
    *val = hw->regs[offset];
    return 0;
}

/* Sahte register yazma */
static int fake_reg_write(void *priv, u32 offset, u32 val)
{
    struct fake_hw *hw = priv;
    if (offset >= ARRAY_SIZE(hw->regs)) return -EINVAL;
    hw->regs[offset] = val;
    return 0;
}

static void fake_reset(void *priv)
{
    struct fake_hw *hw = priv;
    memset(hw->regs, 0, sizeof(hw->regs));
    hw->reset_count++;
}

/* Sahte ops tablosu */
static const struct mydrv_ops fake_ops = {
    .reg_read  = fake_reg_read,
    .reg_write = fake_reg_write,
    .reset     = fake_reset,
};

/* Test: reset sonrası tüm register'lar sıfır olmalı */
static void mydrv_reset_clears_regs(struct kunit *test)
{
    struct fake_hw  *hw  = kunit_kzalloc(test, sizeof(*hw),  GFP_KERNEL);
    struct mydrv_dev *dev = kunit_kzalloc(test, sizeof(*dev), GFP_KERNEL);
    KUNIT_ASSERT_NOT_NULL(test, hw);
    KUNIT_ASSERT_NOT_NULL(test, dev);

    /* Sahte donanımı enjekte et */
    dev->ops  = &fake_ops;
    dev->priv = hw;

    /* Bir register'a değer yaz */
    mydrv_write_reg(dev, 0x10, 0xCAFEBABE);

    /* Reset uygula */
    mydrv_reset(dev);

    /* Register sıfırlanmış olmalı */
    u32 val;
    KUNIT_ASSERT_EQ(test, mydrv_read_reg(dev, 0x10, &val), 0);
    KUNIT_EXPECT_EQ(test, val, 0U);
    KUNIT_EXPECT_EQ(test, hw->reset_count, 1);
}

Otomatik mock üretimi (deneysel)

c
/*
 * include/kunit/mock.h — DEFINE_STRUCT_CLASS_MOCK_VOID_RETURN ile
 * otomatik mock fonksiyonu oluşturma (Linux 6.4+ deneysel özellik)
 * Henüz mainline değil; kullanımı için kunit-mock-example.c'ye bakın.
 *
 * Kararlı yol: ops struct + fake fonksiyon (yukarıdaki yöntem)
 */

/* En sağlam yaklaşım: struct ops üzerinden dependency injection */
TAVSIYE

Sürücünüzü başından itibaren ops struct (ör. mydrv_ops) üzerine kurarsanız hem gerçek hem fake implementasyonu kolayca değiştirebilirsiniz. Bu, testability için temel tasarım prensibidir. Donanımı doğrudan ioread32() gibi çağrılarla kullanan sürücüler test edilemez; ops üzerinden soyutlayın.

Bu bölümde

  • Ops struct + fonksiyon işaretçisi: kernel'de standart dependency injection yöntemi
  • Sahte (fake) implementasyon: bellek tabanlı kayıt deposu, donanım simülasyonu
  • kunit_kzalloc ile fake_hw ve mydrv_dev otomatik temizleme
  • include/kunit/mock.h: deneysel — kararlı kod için ops struct kullanın

06 Kernel modülü olarak çalıştırma

KUnit testleri kernel içine gömülü (built-in) derlenebileceği gibi, yüklenebilir modül (loadable module) olarak da derlenip çalışma zamanında insmod ile yüklenebilir.

Modül olarak derleme ve yükleme

bash
# Kconfig'i modül (=m) olarak ayarla
./scripts/config --module CONFIG_MYDRV_KUNIT_TEST

# Sadece test modülünü derle (hızlı yöntem)
make -j$(nproc) M=drivers/misc/mydrv modules

# Hedef sistemde yükle
insmod mydrv.ko           # önce ana modül
insmod mydrv-test.ko      # test modülü yüklenince testler otomatik çalışır

# Sonuçları kernel log'dan oku
dmesg | grep -A 20 'TAP version'
# veya daha temiz:
dmesg | grep -E '(ok|not ok|TAP|#)'

debugfs üzerinden sonuçlara erişim

bash
# debugfs bağlı değilse:
mount -t debugfs none /sys/kernel/debug

# Tüm KUnit sonuçları
cat /sys/kernel/debug/kunit/results

# Belirli suite sonuçları
cat /sys/kernel/debug/kunit/mydrv/results

# Mevcut suite listesi
ls /sys/kernel/debug/kunit/

kunit_run_all_tests() ile programatik çalıştırma

c
#include <kunit/test.h>

/* Belirli bir suite'i programatik olarak çalıştır */
static int __init mydrv_test_module_init(void)
{
    /*
     * kunit_test_suite() makrosu init() sırasında çağrılır.
     * Module yüklenince testler otomatik başlar.
     * Ek programatik başlatma gerekmez; ancak kunit_run_suite()
     * ile belirli sürümde manuel kontrol mümkündür.
     */
    return 0;
}

static void __exit mydrv_test_module_exit(void)
{
    /* Temizlik: kunit_alloc_resource kaynakları zaten serbest bırakıldı */
}

module_init(mydrv_test_module_init);
module_exit(mydrv_test_module_exit);
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("mydrv KUnit testleri");

Modül derleme sonrası test akışı

make M=... modules → scp *.ko target:/ → insmod mydrv.ko → insmod mydrv-test.ko → dmesg TAP → pass/fail
DİKKAT

KUnit testleri çalışma zamanında gerçek kernel bağlamında çalışır — bir KUNIT_ASSERT_* panik yerine testi durdursa da, test kodu NULL pointer erişimi veya yanlış lock kullanımı gibi bir hata yaparsa sistem çökebilir. Testleri önce UML (kunit.py) üzerinde doğrulayın, ardından gerçek donanımda çalıştırın.

Bu bölümde

  • CONFIG_MYDRV_KUNIT_TEST=m → insmod ile yükleme → otomatik test çalışması
  • dmesg | grep TAP: kernel log'dan sonuçları oku
  • /sys/kernel/debug/kunit/: debugfs üzerinden sonuç dosyaları
  • Gerçek kernel testleri öncesi UML üzerinde doğrula — panik riski

07 kunit.py ile çalıştırma

kunit.py, User Mode Linux (UML) kernel'i otomatik derleyip başlatan, testleri çalıştıran ve TAP sonuçlarını ayrıştıran wrapper script'tir. Gerçek donanım olmadan saniyeler içinde test mümkün kılar.

kunit.py kurulumu ve gereksinimleri

bash
# Gereksinimler: Python 3.7+, GCC (x86_64), qemu-system-x86_64 (isteğe bağlı)
python3 --version   # >= 3.7

# kunit.py yolu: Linux kaynak ağacı kökünden
cd /path/to/linux
ls tools/testing/kunit/kunit.py

# İlk çalıştırma: UML kernel'i derler (~3-5 dakika, ikinci seferden hızlı)
./tools/testing/kunit/kunit.py run

# Yardım
./tools/testing/kunit/kunit.py --help

Yaygın kunit.py komutları

bash
# 1. Tüm testleri çalıştır
./tools/testing/kunit/kunit.py run

# 2. Belirli test suite(ları) — glob filtresi
./tools/testing/kunit/kunit.py run --filter_glob 'mydrv*'
./tools/testing/kunit/kunit.py run --filter_glob 'mydrv.mydrv_add_test'

# 3. Özel .kunitconfig dosyası (hangi testlerin derleneceği)
./tools/testing/kunit/kunit.py run --kunitconfig drivers/misc/mydrv/.kunitconfig

# 4. Sadece derle, çalıştırma
./tools/testing/kunit/kunit.py build

# 5. Sonuçları ayrıştır (önceden kaydedilmiş TAP çıktısından)
./tools/testing/kunit/kunit.py parse kunit_output.log

# 6. QEMU ile çalıştır (x86_64)
./tools/testing/kunit/kunit.py run --arch x86_64

.kunitconfig dosyası

kconfig — drivers/misc/mydrv/.kunitconfig
# UML kernel için minimal config — sadece ilgili testleri içerir
CONFIG_KUNIT=y
CONFIG_KUNIT_DEBUGFS=y

# Test edilen modül ve test bağımlılıkları
CONFIG_MYDRV=y
CONFIG_MYDRV_KUNIT_TEST=y

# İsteğe bağlı: KUnit öz testleri
CONFIG_KUNIT_TEST=y

Çıktı yorumlama

bash
# Başarılı çalıştırma çıktısı:
# [PASSED] mydrv.mydrv_add_test
# [PASSED] mydrv.mydrv_boundary_test
# [PASSED] mydrv.mydrv_reset_clears_regs
# ============================================================
# Testing complete. 3 tests run. 0 failures. 0 crashed.

# Başarısız test:
# [FAILED] mydrv.mydrv_add_test
# ====== FAILED CASES ======
# mydrv.mydrv_add_test:
#   EXPECTATION FAILED at drivers/misc/mydrv/mydrv-test.c:18
#   Expected mydrv_add(2, 3) == 5, but:
#     mydrv_add(2, 3) == 4
#     5 == 5

# Hata kodu: 0=başarı, 1=test başarısız, 2=build hatası
echo $?

UML çalışma zamanı ve sınırlar

HızUML kernel boot + test çalışması tipik olarak 10–30 saniye. İkinci çalıştırma derleme olmadığından çok daha hızlı.
MimariVarsayılan UML (x86_64). ARM/RISC-V testleri için --arch arm64 + QEMU gerekir.
Donanım erişimiUML'de gerçek donanıma erişim yoktur — fake/mock kullanmak zorunludur. Bu KUnit tasarım prensibidir.
TimeoutVarsayılan 300 saniye. --timeout ile değiştirilebilir. Sonsuz döngü yapan test zaman aşımına düşer.

Bu bölümde

  • kunit.py run: UML kernel derle + boot + test + TAP ayrıştır — tek komutla
  • --filter_glob: isim tabanlı test seçimi — hızlı geliştirme döngüsü
  • .kunitconfig: UML derlemesine hangi modüllerin gireceğini belirler
  • Dönüş kodu 0/1/2: CI pipeline'da pass/fail kararı için

08 CI entegrasyonu

KUnit testlerini her kod değişikliğinde otomatik çalıştırmak — LKFT altyapısı, GitHub Actions ile kunit.py ve defconfig + KUnit kombinasyonu.

GitHub Actions ile CI

yaml — .github/workflows/kunit.yml
name: KUnit Tests

on:
  push:
    branches: [ main, 'dev/**' ]
  pull_request:
    branches: [ main ]

jobs:
  kunit:
    runs-on: ubuntu-22.04
    steps:
      - name: Kaynak kodunu al
        uses: actions/checkout@v4

      - name: Araçları kur
        run: |
          sudo apt-get update -q
          sudo apt-get install -y \
            gcc gcc-aarch64-linux-gnu flex bison \
            libelf-dev libssl-dev python3 python3-pip

      - name: KUnit testlerini çalıştır (UML)
        run: |
          ./tools/testing/kunit/kunit.py run \
            --kunitconfig drivers/misc/mydrv/.kunitconfig \
            --jobs $(nproc) \
            --timeout 180

      - name: TAP sonuçlarını artifact olarak kaydet
        if: always()
        uses: actions/upload-artifact@v4
        with:
          name: kunit-results
          path: .kunit/test_output.log

LKFT (Linux Kernel Functional Testing)

LKFT nedirLinaro tarafından yürütülen, gerçek ARM ve x86 donanım üzerinde kernel testlerini CI ile otomatikleştiren açık altyapı
Desteklenen testlerkselftest, LTP, KUnit, perf — tüm mainline ve LTS branch'leri için otomatik çalışır
Sonuçlara erişimhttps://lkft.linaro.org — regresyon raporları, board bazlı sonuçlar, tarihsel karşılaştırma
Gönüllü katılımKendi board'unuzu LKFT ağına ekleyebilirsiniz — LAVA (Lab Automation and Validation Architecture) kullanılır

defconfig ile KUnit etkinleştirme

bash
# Mevcut defconfig'e KUnit ekle
cd /path/to/linux

# 1. defconfig'i yükle ve KUnit ekle
make ARCH=arm64 CROSS_COMPILE=aarch64-linux-gnu- defconfig
./scripts/config --enable CONFIG_KUNIT
./scripts/config --enable CONFIG_KUNIT_ALL_TESTS
./scripts/config --enable CONFIG_MYDRV_KUNIT_TEST

# 2. Kernel derle
make ARCH=arm64 CROSS_COMPILE=aarch64-linux-gnu- -j$(nproc) Image modules

# 3. Hedef sistemde (QEMU veya gerçek board) çalıştır
# KUnit testleri boot sırasında otomatik çalışır (built-in ise)
# veya insmod ile yükle

# 4. Boot log'dan TAP çıktısını çek ve ayrıştır
./tools/testing/kunit/kunit.py parse < boot.log

kunit_tool CI çıktı seçenekleri

bash
# JUnit XML formatında çıktı (Jenkins, GitLab CI için)
./tools/testing/kunit/kunit.py run --json > results.json

# Ayrıştırılmış TAP'ı JUnit'e dönüştür (üçüncü taraf araç)
# pip install junit-xml

# Çıktı renklendirme (terminal için)
./tools/testing/kunit/kunit.py run --raw_output   # ham TAP
./tools/testing/kunit/kunit.py run                    # renkli özet (varsayılan)

Tam CI test akışı

git push → CI tetiklenir → apt install araçlar → kunit.py run --kunitconfig ...
    → UML kernel boot → testler çalışır → TAP parse → 0 (pass) veya 1 (fail)
    → artifact kaydet → PR'a yorum/badge
CI platformuEntegrasyon yöntemiSüre (tipik)
GitHub Actionsubuntu-22.04 runner + kunit.py3–8 dakika
GitLab CIDocker gcc:12 + kunit.py3–8 dakika
JenkinsMakefile hedef + TAP parser plugin5–10 dakika
LKFT/LAVAGerçek donanım, QEMU — kselftest + KUnit15–30 dakika

Bu bölümde

  • GitHub Actions: ubuntu-22.04 + kunit.py run — tam çalışan örnek
  • LKFT: Linaro'nun gerçek donanım üzerinde otomatik kernel test altyapısı
  • kunit.py --json: makine okunabilir çıktı — CI araçlarına entegrasyon
  • Dönüş kodu 0=geçti, 1=başarısız, 2=derleme hatası — CI kararı için