← Documents Documentation/dev-tools/kunit/api/functionredirection.rst GitHub 원문 ↗

Linux 6.18.37 · Dev Tools

Function Redirection API

KUNIT_STATIC_STUB_REDIRECT와 activation API로 kernel 함수 호출을 test replacement로 바꾸고 검증하는 방법을 설명합니다.

Source pathDocumentation/dev-tools/kunit/api/functionredirection.rst
Source versionLinux v6.18.37
TranslationDUJINLABS 전문 번역 + 해설

요약·해설과 원문, 전문 번역을 서로 분리했습니다. API 이름, symbol, source path는 원문 표기를 사용합니다.

1. 요약·해설

원문의 핵심 논리와 kernel programming 관점의 보충 설명입니다. 아래의 전문 번역과는 별도로 작성했습니다.

요약과 해설

functionredirection.rst:1-162

Static stub은 큰 refactoring 없이 hardware나 global state에 강하게 결합된 호출을 test replacement로 바꿉니다. Real function 첫 statement에 redirect macro를 두고 동일 signature의 fake를 등록하면 테스트 kthread에서 발생하는 호출만 교체할 수 있습니다.

Replacement는 kunit_get_current_test()로 현재 test state와 assertion API를 사용할 수 있습니다. Stub은 다른 implementation으로 교체하거나 일찍 해제할 수 있으며 해제하지 않아도 test 종료 시 자동 정리됩니다.

2. 영어 원문 전체

번역 기준이 된 Linux v6.18.37 원문입니다. 줄 번호는 이 버전의 파일 좌표입니다.

원문 전체 펼치기
1 .. SPDX-License-Identifier: GPL-2.0
2
3 ========================
4 Function Redirection API
5 ========================
6
7 Overview
8 ========
9
10 When writing unit tests, it's important to be able to isolate the code being
11 tested from other parts of the kernel. This ensures the reliability of the test
12 (it won't be affected by external factors), reduces dependencies on specific
13 hardware or config options (making the test easier to run), and protects the
14 stability of the rest of the system (making it less likely for test-specific
15 state to interfere with the rest of the system).
16
17 While for some code (typically generic data structures, helpers, and other
18 "pure functions") this is trivial, for others (like device drivers,
19 filesystems, core subsystems) the code is heavily coupled with other parts of
20 the kernel.
21
22 This coupling is often due to global state in some way: be it a global list of
23 devices, the filesystem, or some hardware state. Tests need to either carefully
24 manage, isolate, and restore state, or they can avoid it altogether by
25 replacing access to and mutation of this state with a "fake" or "mock" variant.
26
27 By refactoring access to such state, such as by introducing a layer of
28 indirection which can use or emulate a separate set of test state. However,
29 such refactoring comes with its own costs (and undertaking significant
30 refactoring before being able to write tests is suboptimal).
31
32 A simpler way to intercept and replace some of the function calls is to use
33 function redirection via static stubs.
34
35
36 Static Stubs
37 ============
38
39 Static stubs are a way of redirecting calls to one function (the "real"
40 function) to another function (the "replacement" function).
41
42 It works by adding a macro to the "real" function which checks to see if a test
43 is running, and if a replacement function is available. If so, that function is
44 called in place of the original.
45
46 Using static stubs is pretty straightforward:
47
48 1. Add the KUNIT_STATIC_STUB_REDIRECT() macro to the start of the "real"
49 function.
50
51 This should be the first statement in the function, after any variable
52 declarations. KUNIT_STATIC_STUB_REDIRECT() takes the name of the
53 function, followed by all of the arguments passed to the real function.
54
55 For example:
56
57 .. code-block:: c
58
59 void send_data_to_hardware(const char *str)
60 {
61 KUNIT_STATIC_STUB_REDIRECT(send_data_to_hardware, str);
62 /* real implementation */
63 }
64
65 2. Write one or more replacement functions.
66
67 These functions should have the same function signature as the real function.
68 In the event they need to access or modify test-specific state, they can use
69 kunit_get_current_test() to get a struct kunit pointer. This can then
70 be passed to the expectation/assertion macros, or used to look up KUnit
71 resources.
72
73 For example:
74
75 .. code-block:: c
76
77 void fake_send_data_to_hardware(const char *str)
78 {
79 struct kunit *test = kunit_get_current_test();
80 KUNIT_EXPECT_STREQ(test, str, "Hello World!");
81 }
82
83 3. Activate the static stub from your test.
84
85 From within a test, the redirection can be enabled with
86 kunit_activate_static_stub(), which accepts a struct kunit pointer,
87 the real function, and the replacement function. You can call this several
88 times with different replacement functions to swap out implementations of the
89 function.
90
91 In our example, this would be
92
93 .. code-block:: c
94
95 kunit_activate_static_stub(test,
96 send_data_to_hardware,
97 fake_send_data_to_hardware);
98
99 4. Call (perhaps indirectly) the real function.
100
101 Once the redirection is activated, any call to the real function will call
102 the replacement function instead. Such calls may be buried deep in the
103 implementation of another function, but must occur from the test's kthread.
104
105 For example:
106
107 .. code-block:: c
108
109 send_data_to_hardware("Hello World!"); /* Succeeds */
110 send_data_to_hardware("Something else"); /* Fails the test. */
111
112 5. (Optionally) disable the stub.
113
114 When you no longer need it, disable the redirection (and hence resume the
115 original behaviour of the 'real' function) using
116 kunit_deactivate_static_stub(). Otherwise, it will be automatically disabled
117 when the test exits.
118
119 For example:
120
121 .. code-block:: c
122
123 kunit_deactivate_static_stub(test, send_data_to_hardware);
124
125
126 It's also possible to use these replacement functions to test to see if a
127 function is called at all, for example:
128
129 .. code-block:: c
130
131 void send_data_to_hardware(const char *str)
132 {
133 KUNIT_STATIC_STUB_REDIRECT(send_data_to_hardware, str);
134 /* real implementation */
135 }
136
137 /* In test file */
138 int times_called = 0;
139 void fake_send_data_to_hardware(const char *str)
140 {
141 times_called++;
142 }
143 ...
144 /* In the test case, redirect calls for the duration of the test */
145 kunit_activate_static_stub(test, send_data_to_hardware, fake_send_data_to_hardware);
146
147 send_data_to_hardware("hello");
148 KUNIT_EXPECT_EQ(test, times_called, 1);
149
150 /* Can also deactivate the stub early, if wanted */
151 kunit_deactivate_static_stub(test, send_data_to_hardware);
152
153 send_data_to_hardware("hello again");
154 KUNIT_EXPECT_EQ(test, times_called, 1);
155
156
157
158 API Reference
159 =============
160
161 .. kernel-doc:: include/kunit/static_stub.h
162 :internal:
163

3. 한국어 전문 번역

영어 원문의 문단 순서와 의미를 유지한 전체 번역입니다. 코드, 함수명, symbol과 URL은 원문 표기를 유지합니다.

함수 redirection이 필요한 이유

1-35

SPDX 라이선스 식별자: GPL-2.0

Function Redirection API

개요

Unit test를 작성할 때는 검사 대상 코드를 커널의 다른 부분과 격리할 수 있어야 합니다. 그래야 외부 요인에 영향을 받지 않아 테스트의 신뢰성이 높아지고, 특정 hardware나 config option에 대한 dependency가 줄어 테스트를 쉽게 실행할 수 있으며, 테스트 전용 상태가 시스템의 나머지 부분에 간섭할 가능성이 낮아져 전체 시스템의 안정성을 보호할 수 있습니다.

일반적인 data structure, helper, 기타 pure function처럼 쉽게 격리되는 코드도 있지만 device driver, filesystem, core subsystem처럼 커널의 다른 부분과 강하게 결합된 코드도 있습니다.

이 결합은 흔히 어떤 형태로든 global state에서 비롯됩니다. 전체 device 목록, filesystem, hardware state가 그 예입니다. 테스트는 상태를 신중히 관리하고 격리한 뒤 복원하거나, state 접근과 변경을 fake 또는 mock variant로 바꾸어 실제 state를 전혀 건드리지 않을 수 있습니다.

별도의 test state 집합을 사용하거나 모방하는 indirection layer를 도입하는 식으로 state 접근을 refactoring할 수 있습니다. 하지만 refactoring 자체에도 비용이 들며, 테스트를 작성하기 전에 큰 refactoring부터 수행해야 하는 것은 바람직하지 않습니다.

일부 함수 호출을 더 간단히 가로채고 교체하는 방법은 static stub을 통한 function redirection을 사용하는 것입니다.

Static stub 설정과 해제

36-125

Static Stub

Static stub은 한 함수, 즉 real function으로 향하는 호출을 다른 replacement function으로 redirect하는 방법입니다.

Real function에 macro를 추가하면 현재 테스트가 실행 중인지와 replacement function이 제공되었는지를 검사합니다. 둘 다 만족하면 원래 함수 대신 replacement function을 호출합니다.

Static stub 사용 절차는 다음과 같습니다.

1. Real function 시작 부분에 KUNIT_STATIC_STUB_REDIRECT() macro를 추가합니다. Variable 선언 다음의 첫 statement여야 합니다. Macro 인수에는 function 이름과 real function에 전달된 모든 argument를 순서대로 넣습니다.

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

2. Replacement function을 하나 이상 작성합니다. 이 함수는 real function과 같은 function signature를 가져야 합니다. Test 전용 state에 접근하거나 이를 수정해야 한다면 kunit_get_current_test()로 struct kunit pointer를 가져올 수 있습니다. 이 pointer를 expectation 또는 assertion macro에 전달하거나 KUnit resource 조회에 사용할 수 있습니다.

void fake_send_data_to_hardware(const char *str)
{
        struct kunit *test = kunit_get_current_test();
        KUNIT_EXPECT_STREQ(test, str, "Hello World!");
}

3. 테스트에서 static stub을 활성화합니다. kunit_activate_static_stub()은 struct kunit pointer, real function, replacement function을 받습니다. 서로 다른 replacement function으로 여러 번 호출하여 구현을 바꿀 수 있습니다.

kunit_activate_static_stub(test,
                           send_data_to_hardware,
                           fake_send_data_to_hardware);

4. Real function을 직접 또는 간접적으로 호출합니다. Redirection이 활성화된 뒤에는 real function 호출이 replacement function을 호출합니다. 이런 호출은 다른 함수 구현의 깊은 곳에 있을 수 있지만 반드시 테스트의 kthread에서 발생해야 합니다.

send_data_to_hardware("Hello World!"); /* Succeeds */
send_data_to_hardware("Something else"); /* Fails the test. */

5. 필요하다면 stub을 비활성화합니다. 더 이상 필요하지 않으면 kunit_deactivate_static_stub()으로 redirection을 끄고 real function의 원래 동작으로 돌아갑니다. 명시적으로 끄지 않아도 테스트 종료 시 자동으로 비활성화됩니다.

kunit_deactivate_static_stub(test, send_data_to_hardware);
KUnit static stub 호출 흐름
Real function 계측KUNIT_STATIC_STUB_REDIRECT()를 첫 statement로 배치
Replacement 작성동일 signature와 KUnit assertion 사용
Stub 활성화kunit_activate_static_stub()로 대응 관계 등록
호출 검증테스트 kthread의 real call이 replacement로 이동
해제명시적 deactivate 또는 test 종료 시 자동 정리

테스트가 real function 호출을 replacement function으로 전환하고 종료 시 복원하는 과정입니다.

호출 여부와 횟수 검증

126-157

Replacement function을 사용해 함수가 실제로 호출되는지 검사할 수도 있습니다. 다음 예에서는 counter를 증가시키는 fake function으로 redirect하고, 해제 전후의 호출 횟수를 확인합니다.

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);
호출 횟수 예제의 상태 변화
시점호출 대상times_called
활성화 전Real implementation0
활성화 후 첫 호출fake_send_data_to_hardware()1
명시적 해제Stub mapping 제거1
해제 후 호출Real implementation1 유지

Static stub 활성화와 해제에 따라 real call이 counter에 미치는 영향을 정리했습니다.

API reference

158-162

API Reference

다음 kernel-doc 지시문은 `include/kunit/static_stub.h`의 internal API 문서를 포함합니다.

.. kernel-doc:: include/kunit/static_stub.h
   :internal: