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
Test türleri
| Test türü | Araç | Çalıştığı yer | Uygun olduğu alan |
|---|---|---|---|
| Unit test | KUnit | UML kernel veya gerçek kernel | Sürücü alt fonksiyonları, veri yapıları, algoritmalar |
| Functional test | kselftest | Gerçek kernel, user space test | Syscall davranışı, ABI uyumluluğu |
| Integration test | LTP | Gerçek kernel, rootfs | Sistem geneli davranış, stress |
| Fuzzing | syzkaller | QEMU/KVM | Gü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üğü
KUnit kaynak ağacı
| Yol | İçerik |
|---|---|
lib/kunit/ | KUnit çekirdek: test.c, assert.c, resource.c, string-stream.c |
include/kunit/test.h | Ana başlık — tüm makro ve struct tanımları |
include/kunit/mock.h | Mock 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.c | Referans örnek — yeni başlayanlar için şablon |
TAP çıktısı formatı
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
# 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ı
// 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
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
obj-$(CONFIG_MYDRV) += mydrv.o
obj-$(CONFIG_MYDRV_KUNIT_TEST) += mydrv-test.o
kunit.py ile hızlı çalıştırma
# 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.
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 ailesi | Başarısızlık davranışı | Ne zaman kullanılır |
|---|---|---|
KUNIT_EXPECT_* | Hatayı raporlar, test devam eder | Birden 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ı
/* 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
/* _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
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);
}
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
/* 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
#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ı
| Mekanizma | Kapsam | Çağrılma zamanı | Tipik kullanım |
|---|---|---|---|
| suite.init / suite.exit | Her test case | Her case öncesi/sonrası | Cihaz mock, paylaşılan tampon |
| suite.suite_init / suite_exit | Tüm suite | Suite başı/sonu (tek sefer) | Pahalı kaynaklar, global register |
| kunit_kzalloc | Test case içi | Otomatik, case bitince | Tek 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
/* 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;
/* 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)
/*
* 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 */
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
# 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
# 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
#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
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
# 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ı
# 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ı
# 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
# 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
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
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)
defconfig ile KUnit etkinleştirme
# 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
# 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 platformu | Entegrasyon yöntemi | Süre (tipik) |
|---|---|---|
| GitHub Actions | ubuntu-22.04 runner + kunit.py | 3–8 dakika |
| GitLab CI | Docker gcc:12 + kunit.py | 3–8 dakika |
| Jenkins | Makefile hedef + TAP parser plugin | 5–10 dakika |
| LKFT/LAVA | Gerçek donanım, QEMU — kselftest + KUnit | 15–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