函式重定向 API

概述

在編寫單元測試時,能夠將正在測試的程式碼與其他核心部分隔離非常重要。這確保了測試的可靠性(不會受到外部因素的影響),減少了對特定硬體或配置選項的依賴(使測試更容易執行),並保護了系統其餘部分的穩定性(降低了測試特定狀態干擾系統其餘部分的可能性)。

雖然對於某些程式碼(通常是通用資料結構、助手函式和其他“純函式”)來說,這很簡單,但對於其他程式碼(如裝置驅動程式、檔案系統、核心子系統),程式碼與其他核心部分的耦合度很高。

這種耦合通常是由於某種全域性狀態:無論是裝置全域性列表、檔案系統還是某些硬體狀態。測試需要仔細管理、隔離和恢復狀態,或者可以透過用“假的”或“模擬”變體替換對此狀態的訪問和修改來完全避免它。

透過重構對此類狀態的訪問,例如透過引入一個間接層,該間接層可以使用或模擬一個單獨的測試狀態集。然而,這種重構有其自身的成本(並且在能夠編寫測試之前進行大量的重構是不理想的)。

一種更簡單的攔截和替換某些函式呼叫的方法是透過靜態存根進行函式重定向。

靜態存根

靜態存根是一種將對一個函式(“真實”函式)的呼叫重定向到另一個函式(“替換”函式)的方法。

它的工作原理是在“真實”函式中新增一個宏,該宏檢查是否正在執行測試,以及是否有一個替換函式可用。如果是,則呼叫該函式來代替原始函式。

使用靜態存根非常簡單

  1. KUNIT_STATIC_STUB_REDIRECT() 宏新增到“真實”函式的開頭。

    這應該是函式中的第一個語句,在任何變數宣告之後。KUNIT_STATIC_STUB_REDIRECT() 接受函式名,後跟傳遞給真實函式的所有引數。

    例如

    void send_data_to_hardware(const char *str)
    {
            KUNIT_STATIC_STUB_REDIRECT(send_data_to_hardware, str);
            /* real implementation */
    }
    
  2. 編寫一個或多個替換函式。

    這些函式應該具有與真實函式相同的函式簽名。如果它們需要訪問或修改測試特定的狀態,它們可以使用 kunit_get_current_test() 來獲取一個 struct kunit 指標。然後可以將此指標傳遞給期望/斷言宏,或用於查詢 KUnit 資源。

    例如

    void fake_send_data_to_hardware(const char *str)
    {
            struct kunit *test = kunit_get_current_test();
            KUNIT_EXPECT_STREQ(test, str, "Hello World!");
    }
    
  3. 從您的測試中啟用靜態存根。

    在測試中,可以使用 kunit_activate_static_stub() 啟用重定向,它接受一個 struct kunit 指標、真實函式和替換函式。您可以多次呼叫此函式,使用不同的替換函式來交換函式的實現。

    在我們的示例中,這將是

    kunit_activate_static_stub(test,
                               send_data_to_hardware,
                               fake_send_data_to_hardware);
    
  4. 呼叫(可能間接)真實函式。

    一旦啟用重定向,對真實函式的任何呼叫都將呼叫替換函式。這些呼叫可能深埋在另一個函式的實現中,但必須從測試的 kthread 中發生。

    例如

    send_data_to_hardware("Hello World!"); /* Succeeds */
    send_data_to_hardware("Something else"); /* Fails the test. */
    
  5. (可選)停用存根。

    當您不再需要它時,使用 kunit_deactivate_static_stub() 停用重定向(從而恢復“真實”函式的原始行為)。否則,它將在測試退出時自動停用。

    例如

    kunit_deactivate_static_stub(test, send_data_to_hardware);
    

也可以使用這些替換函式來測試是否呼叫了某個函式,例如

void send_data_to_hardware(const char *str)
{
        KUNIT_STATIC_STUB_REDIRECT(send_data_to_hardware, str);
        /* real implementation */
}

/* In test file */
int times_called = 0;
void fake_send_data_to_hardware(const char *str)
{
        times_called++;
}
...
/* In the test case, redirect calls for the duration of the test */
kunit_activate_static_stub(test, send_data_to_hardware, fake_send_data_to_hardware);

send_data_to_hardware("hello");
KUNIT_EXPECT_EQ(test, times_called, 1);

/* Can also deactivate the stub early, if wanted */
kunit_deactivate_static_stub(test, send_data_to_hardware);

send_data_to_hardware("hello again");
KUNIT_EXPECT_EQ(test, times_called, 1);

API 參考

KUNIT_STATIC_STUB_REDIRECT

KUNIT_STATIC_STUB_REDIRECT (real_fn_name, args...)

如果存在替換“靜態存根”,則呼叫它

引數

real_fn_name

此函式的名稱(作為識別符號,而不是字串)

args...

傳遞給此函式的所有引數

描述

這是一個函式序言,用於允許 KUnit 測試重定向對此函式的呼叫。KUnit 測試可以呼叫 kunit_activate_static_stub() 來傳遞一個替換函式。KUNIT_STATIC_STUB_REDIRECT() 將呼叫替換函式,然後從函式返回。如果呼叫者不在 KUnit 上下文中,該函式將像往常一樣繼續執行。

int real_func(int n)
{
        KUNIT_STATIC_STUB_REDIRECT(real_func, n);
        return 0;
}

int replacement_func(int n)
{
        return 42;
}

void example_test(struct kunit *test)
{
        kunit_activate_static_stub(test, real_func, replacement_func);
        KUNIT_EXPECT_EQ(test, real_func(1), 42);
}

示例

kunit_activate_static_stub

kunit_activate_static_stub (test, real_fn_addr, replacement_addr)

使用靜態存根替換函式。

引數

test

指向當前測試的“struct kunit”測試上下文的指標。

real_fn_addr

要替換的函式的地址。

replacement_addr

用於替換它的函式的地址。

描述

啟用後,從此測試中呼叫 real_fn_addr(即使是間接呼叫)將改為呼叫 replacement_addr。real_fn_addr 指向的函式必須以 KUNIT_STATIC_STUB_REDIRECT() 中的靜態存根序言開始才能正常工作。real_fn_addr 和 replacement_addr 必須具有相同的型別。

可以使用 kunit_deactivate_static_stub() 再次停用重定向。

void kunit_deactivate_static_stub(struct kunit *test, void *real_fn_addr)

停用函式重定向

引數

struct kunit *test

指向當前測試的“struct kunit”測試上下文的指標。

void *real_fn_addr

不再重定向的函式的地址

描述

停用使用 kunit_activate_static_stub() 配置的重定向。在此函式返回後,對 real_fn_addr() 的呼叫將執行原始的 real_fn,而不是任何先前配置的替換。