Skip to content

Commit 66893e3

Browse files
authored
Merge pull request #361 from ojeda/test
Support unit tests and doctests
2 parents 6470c0c + 9623325 commit 66893e3

21 files changed

+186
-39
lines changed

.github/workflows/ci.yaml

+3
Original file line numberDiff line numberDiff line change
@@ -398,6 +398,9 @@ jobs:
398398
# Docs
399399
- run: make ${{ env.MAKE_ARCH }} ${{ env.MAKE_CROSS_COMPILE }} ${{ env.MAKE_TOOLCHAIN }} ${{ env.MAKE_OUTPUT }} ${{ env.MAKE_SYSROOT }} -j3 rustdoc
400400

401+
# Tests
402+
- run: make ${{ env.MAKE_ARCH }} ${{ env.MAKE_CROSS_COMPILE }} ${{ env.MAKE_TOOLCHAIN }} ${{ env.MAKE_OUTPUT }} ${{ env.MAKE_SYSROOT }} -j3 rusttest
403+
401404
# Formatting
402405
- run: make rustfmtcheck
403406

Makefile

+7
Original file line numberDiff line numberDiff line change
@@ -1739,6 +1739,8 @@ help:
17391739
@echo ' is formatted, printing a diff otherwise.'
17401740
@echo ' rustdoc - Generate Rust documentation'
17411741
@echo ' (requires kernel .config)'
1742+
@echo ' rusttest - Runs the Rust tests'
1743+
@echo ' (requires kernel .config)'
17421744
@echo ' rust-analyzer - Generate rust-project.json rust-analyzer support file'
17431745
@echo ' (requires kernel .config)'
17441746
@echo ''
@@ -1824,6 +1826,11 @@ PHONY += rustdoc
18241826
rustdoc: prepare0
18251827
$(Q)$(MAKE) $(build)=rust $@
18261828

1829+
# Testing target
1830+
PHONY += rusttest
1831+
rusttest: prepare0
1832+
$(Q)$(MAKE) $(build)=rust $@
1833+
18271834
# Formatting targets
18281835
PHONY += rustfmt rustfmtcheck
18291836

rust/.gitignore

+2-1
Original file line numberDiff line numberDiff line change
@@ -2,4 +2,5 @@
22

33
bindings_generated.rs
44
exports_*_generated.h
5-
doc/
5+
doc/
6+
test/

rust/Makefile

+57-15
Original file line numberDiff line numberDiff line change
@@ -19,39 +19,81 @@ obj-$(CONFIG_RUST) += exports.o
1919

2020
RUSTDOC = rustdoc
2121

22-
quiet_cmd_rustdoc_host = RUSTDOC $<
23-
cmd_rustdoc_host = \
24-
RUST_BINDINGS_FILE=$(abspath $(objtree)/rust/bindings_generated.rs) \
25-
$(RUSTDOC) $(filter-out --emit=%, $(rustc_flags)) \
26-
$(rustdoc_target_flags) -L $(objtree)/rust/ \
27-
--output $(objtree)/rust/doc --crate-name $(subst rustdoc-,,$@) \
28-
-Fmissing-docs @$(objtree)/include/generated/rustc_cfg $<
29-
30-
quiet_cmd_rustdoc = RUSTDOC $<
22+
quiet_cmd_rustdoc = RUSTDOC $(if $(filter --test,$(rustdoc_target_flags)),T, ) $(if $(rustdoc_host),H, ) $<
3123
cmd_rustdoc = \
3224
RUST_BINDINGS_FILE=$(abspath $(objtree)/rust/bindings_generated.rs) \
33-
$(RUSTDOC) $(rustc_cross_flags) $(filter-out --emit=%, $(rustc_flags)) \
34-
$(rustdoc_target_flags) -L $(objtree)/rust/ \
35-
--output $(objtree)/rust/doc --crate-name $(subst rustdoc-,,$@) \
25+
$(RUSTDOC) $(if $(rustdoc_host),,$(rustc_cross_flags)) \
26+
$(filter-out --emit=%, $(rustc_flags)) $(rustc_target_flags) $(rustdoc_target_flags) \
27+
-L $(objtree)/rust/$(if $(filter --test,$(rustdoc_target_flags)),test/) \
28+
--output $(objtree)/rust/doc --crate-name $(subst rusttest-,,$(subst rustdoc-,,$@)) \
3629
-Fmissing-docs @$(objtree)/include/generated/rustc_cfg $<
3730

3831
rustdoc: rustdoc-macros rustdoc-compiler_builtins rustdoc-kernel
3932

40-
rustdoc-macros: private rustdoc_target_flags = --crate-type proc-macro \
33+
rustdoc-macros: private rustdoc_host = yes
34+
rustdoc-macros: private rustc_target_flags = --crate-type proc-macro \
4135
--extern proc_macro
4236
rustdoc-macros: $(srctree)/rust/macros/lib.rs FORCE
43-
$(call if_changed,rustdoc_host)
37+
$(call if_changed,rustdoc)
4438

4539
rustdoc-compiler_builtins: $(srctree)/rust/compiler_builtins.rs FORCE
4640
$(call if_changed,rustdoc)
4741

48-
rustdoc-kernel: private rustdoc_target_flags = --extern alloc \
42+
rustdoc-kernel: private rustc_target_flags = --extern alloc \
4943
--extern build_error \
5044
--extern macros=$(objtree)/rust/libmacros.so
5145
rustdoc-kernel: $(srctree)/rust/kernel/lib.rs rustdoc-macros \
5246
$(objtree)/rust/libmacros.so $(objtree)/rust/bindings_generated.rs FORCE
5347
$(call if_changed,rustdoc)
5448

49+
quiet_cmd_rustc_test_library = RUSTC TL $<
50+
cmd_rustc_test_library = \
51+
RUST_BINDINGS_FILE=$(abspath $(objtree)/rust/bindings_generated.rs) \
52+
$(RUSTC) $(filter-out -Cpanic=abort, $(filter-out --emit=%, $(rustc_flags))) \
53+
$(rustc_target_flags) --crate-type $(if $(rustc_test_library_proc),proc-macro,rlib) \
54+
--out-dir $(objtree)/rust/test/ --cfg testlib \
55+
-L $(objtree)/rust/test/ --crate-name $(subst rusttest-,,$(subst rusttestlib-,,$@)) $<
56+
57+
rusttestlib-build_error: $(srctree)/rust/build_error.rs FORCE
58+
$(call if_changed,rustc_test_library)
59+
60+
rusttestlib-macros: private rustc_target_flags = --extern proc_macro
61+
rusttestlib-macros: private rustc_test_library_proc = yes
62+
rusttestlib-macros: $(srctree)/rust/macros/lib.rs FORCE
63+
$(call if_changed,rustc_test_library)
64+
65+
# We cannot use `-Zpanic-abort-tests` because some tests are dynamic,
66+
# so for the moment we skip `-Cpanic=abort`.
67+
quiet_cmd_rustc_test = RUSTC T $<
68+
cmd_rustc_test = \
69+
RUST_BINDINGS_FILE=$(abspath $(objtree)/rust/bindings_generated.rs) \
70+
$(RUSTC) --test $(filter-out -Cpanic=abort, $(filter-out --emit=%, $(rustc_flags))) \
71+
$(rustc_target_flags) --out-dir $(objtree)/rust/test \
72+
-L $(objtree)/rust/test/ --crate-name $(subst rusttest-,,$@) $<; \
73+
$(objtree)/rust/test/$(subst rusttest-,,$@) $(rustc_test_run_flags)
74+
75+
rusttest: rusttest-macros rusttest-kernel
76+
77+
rusttest-macros: private rustc_target_flags = --extern proc_macro
78+
rusttest-macros: private rustdoc_host = yes
79+
rusttest-macros: private rustdoc_target_flags = --test --crate-type proc-macro
80+
rusttest-macros: $(srctree)/rust/macros/lib.rs FORCE
81+
$(call if_changed,rustc_test)
82+
$(call if_changed,rustdoc)
83+
84+
rusttest-kernel: private rustc_target_flags = --extern alloc \
85+
--extern build_error \
86+
--extern macros=$(objtree)/rust/test/libmacros.so
87+
rusttest-kernel: private rustc_test_run_flags = \
88+
--skip bindgen_test_layout_
89+
rusttest-kernel: private rustdoc_host = yes
90+
rusttest-kernel: private rustdoc_target_flags = --test
91+
rusttest-kernel: $(srctree)/rust/kernel/lib.rs rusttestlib-build_error \
92+
rusttestlib-macros FORCE
93+
$(call if_changed,rustc_test)
94+
$(call if_changed,rustc_test_library)
95+
$(call if_changed,rustdoc)
96+
5597
ifdef CONFIG_CC_IS_CLANG
5698
bindgen_c_flags = $(c_flags)
5799
else

rust/bindgen_parameters

+3
Original file line numberDiff line numberDiff line change
@@ -8,3 +8,6 @@
88
# If SMP is disabled, `arch_spinlock_t` is defined as a ZST which triggers a Rust
99
# warning. We don't need to peek into it anyway.
1010
--opaque-type spinlock
11+
12+
# `seccomp`'s comment gets understood as a doctest
13+
--no-doc-comments

rust/kernel/allocator.rs

+3
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,9 @@ unsafe impl GlobalAlloc for KernelAllocator {
2424
}
2525
}
2626

27+
#[global_allocator]
28+
static ALLOCATOR: KernelAllocator = KernelAllocator;
29+
2730
#[alloc_error_handler]
2831
fn oom(_layout: Layout) -> ! {
2932
panic!("Out of memory!");

rust/kernel/bindings.rs

+5
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,11 @@
44
//!
55
//! Imports the generated bindings by `bindgen`.
66
7+
// See https://github.com/rust-lang/rust-bindgen/issues/1651.
8+
#![cfg_attr(test, allow(deref_nullptr))]
9+
#![cfg_attr(test, allow(unaligned_references))]
10+
#![cfg_attr(test, allow(unsafe_op_in_unsafe_fn))]
11+
712
#[allow(
813
clippy::all,
914
non_camel_case_types,

rust/kernel/build_assert.rs

+5-2
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,8 @@
99
/// be called, a build error will be triggered.
1010
///
1111
/// # Examples
12-
/// ```no_run
12+
/// ```
13+
/// # use kernel::build_error;
1314
/// #[inline]
1415
/// fn foo(a: usize) -> usize {
1516
/// a.checked_add(1).unwrap_or_else(|| build_error!("overflow"))
@@ -38,7 +39,8 @@ macro_rules! build_error {
3839
/// These examples show that different types of [`assert!`] will trigger errors
3940
/// at different stage of compilation. It is preferred to err as early as
4041
/// possible, so [`static_assert!`] should be used whenever possible.
41-
/// ```no_run
42+
/// ```compile_fail
43+
/// # use kernel::prelude::*;
4244
/// fn foo() {
4345
/// static_assert!(1 > 1); // Compile-time error
4446
/// build_assert!(1 > 1); // Build-time error
@@ -49,6 +51,7 @@ macro_rules! build_error {
4951
/// When the condition refers to generic parameters or parameters of an inline function,
5052
/// [`static_assert!`] cannot be used. Use `build_assert!` in this scenario.
5153
/// ```no_run
54+
/// # use kernel::prelude::*;
5255
/// fn foo<const N: usize>() {
5356
/// // `static_assert!(N > 1);` is not allowed
5457
/// build_assert!(N > 1); // Build-time check

rust/kernel/error.rs

+9-2
Original file line numberDiff line numberDiff line change
@@ -190,7 +190,10 @@ where
190190
///
191191
/// # Examples
192192
///
193-
/// ```rust,no_run
193+
/// ```ignore
194+
/// # use kernel::from_kernel_result;
195+
/// # use kernel::c_types;
196+
/// # use kernel::bindings;
194197
/// unsafe extern "C" fn probe_callback(
195198
/// pdev: *mut bindings::platform_device,
196199
/// ) -> c_types::c_int {
@@ -219,7 +222,11 @@ macro_rules! from_kernel_result {
219222
///
220223
/// # Examples
221224
///
222-
/// ```rust,no_run
225+
/// ```ignore
226+
/// # use kernel::prelude::*;
227+
/// # use kernel::from_kernel_err_ptr;
228+
/// # use kernel::c_types;
229+
/// # use kernel::bindings;
223230
/// fn devm_platform_ioremap_resource(
224231
/// pdev: &mut PlatformDevice,
225232
/// index: u32,

rust/kernel/lib.rs

+6-3
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,8 @@
3434
#[cfg(not(CONFIG_RUST))]
3535
compile_error!("Missing kernel configuration for conditional compilation");
3636

37+
#[cfg(not(test))]
38+
#[cfg(not(testlib))]
3739
mod allocator;
3840

3941
#[doc(hidden)]
@@ -155,6 +157,8 @@ impl<'a> Drop for KParamGuard<'a> {
155157
/// # Example
156158
///
157159
/// ```
160+
/// # use kernel::prelude::*;
161+
/// # use kernel::offset_of;
158162
/// struct Test {
159163
/// a: u64,
160164
/// b: u32,
@@ -193,6 +197,8 @@ macro_rules! offset_of {
193197
/// # Example
194198
///
195199
/// ```
200+
/// # use kernel::prelude::*;
201+
/// # use kernel::container_of;
196202
/// struct Test {
197203
/// a: u64,
198204
/// b: u32,
@@ -213,6 +219,3 @@ macro_rules! container_of {
213219
unsafe { ($ptr as *const _ as *const u8).offset(-offset) as *const $type }
214220
}}
215221
}
216-
217-
#[global_allocator]
218-
static ALLOCATOR: allocator::KernelAllocator = allocator::KernelAllocator;

rust/kernel/module_param.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -210,7 +210,7 @@ macro_rules! impl_module_param {
210210
/// Generate a static [`kernel_param_ops`](../../../include/linux/moduleparam.h) struct.
211211
///
212212
/// # Example
213-
/// ```rust
213+
/// ```ignore
214214
/// make_param_ops!(
215215
/// /// Documentation for new param ops.
216216
/// PARAM_OPS_MYTYPE, // Name for the static.

rust/kernel/prelude.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
//!
88
//! # Examples
99
//!
10-
//! ```rust,no_run
10+
//! ```
1111
//! use kernel::prelude::*;
1212
//! ```
1313

rust/kernel/print.rs

+19
Original file line numberDiff line numberDiff line change
@@ -163,6 +163,7 @@ pub fn call_printk_cont(args: fmt::Arguments<'_>) {
163163
///
164164
/// Public but hidden since it should only be used from public macros.
165165
#[doc(hidden)]
166+
#[cfg(not(testlib))]
166167
#[macro_export]
167168
macro_rules! print_macro (
168169
// The non-continuation cases (most of them, e.g. `INFO`).
@@ -189,6 +190,15 @@ macro_rules! print_macro (
189190
);
190191
);
191192

193+
// Stub for doctests
194+
#[cfg(testlib)]
195+
#[macro_export]
196+
macro_rules! print_macro (
197+
($format_string:path, $e:expr, $($arg:tt)+) => (
198+
()
199+
);
200+
);
201+
192202
// We could use a macro to generate these macros. However, doing so ends
193203
// up being a bit ugly: it requires the dollar token trick to escape `$` as
194204
// well as playing with the `doc` attribute. Furthermore, they cannot be easily
@@ -213,6 +223,7 @@ macro_rules! print_macro (
213223
/// # Examples
214224
///
215225
/// ```
226+
/// # use kernel::prelude::*;
216227
/// pr_emerg!("hello {}\n", "there");
217228
/// ```
218229
#[macro_export]
@@ -237,6 +248,7 @@ macro_rules! pr_emerg (
237248
/// # Examples
238249
///
239250
/// ```
251+
/// # use kernel::prelude::*;
240252
/// pr_alert!("hello {}\n", "there");
241253
/// ```
242254
#[macro_export]
@@ -261,6 +273,7 @@ macro_rules! pr_alert (
261273
/// # Examples
262274
///
263275
/// ```
276+
/// # use kernel::prelude::*;
264277
/// pr_crit!("hello {}\n", "there");
265278
/// ```
266279
#[macro_export]
@@ -285,6 +298,7 @@ macro_rules! pr_crit (
285298
/// # Examples
286299
///
287300
/// ```
301+
/// # use kernel::prelude::*;
288302
/// pr_err!("hello {}\n", "there");
289303
/// ```
290304
#[macro_export]
@@ -309,6 +323,7 @@ macro_rules! pr_err (
309323
/// # Examples
310324
///
311325
/// ```
326+
/// # use kernel::prelude::*;
312327
/// pr_warn!("hello {}\n", "there");
313328
/// ```
314329
#[macro_export]
@@ -333,6 +348,7 @@ macro_rules! pr_warn (
333348
/// # Examples
334349
///
335350
/// ```
351+
/// # use kernel::prelude::*;
336352
/// pr_notice!("hello {}\n", "there");
337353
/// ```
338354
#[macro_export]
@@ -357,6 +373,7 @@ macro_rules! pr_notice (
357373
/// # Examples
358374
///
359375
/// ```
376+
/// # use kernel::prelude::*;
360377
/// pr_info!("hello {}\n", "there");
361378
/// ```
362379
#[macro_export]
@@ -382,6 +399,8 @@ macro_rules! pr_info (
382399
/// # Examples
383400
///
384401
/// ```
402+
/// # use kernel::prelude::*;
403+
/// # use kernel::pr_cont;
385404
/// pr_info!("hello");
386405
/// pr_cont!(" {}\n", "there");
387406
/// ```

rust/kernel/static_assert.rs

+1
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
/// # Examples
1616
///
1717
/// ```
18+
/// # use kernel::prelude::*;
1819
/// static_assert!(42 > 24);
1920
/// static_assert!(core::mem::size_of::<u8>() == 1);
2021
///

rust/kernel/str.rs

+6-2
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,9 @@ pub type BStr = [u8];
1919
///
2020
/// # Examples
2121
///
22-
/// ```rust,no_run
22+
/// ```
23+
/// # use kernel::b_str;
24+
/// # use kernel::str::BStr;
2325
/// const MY_BSTR: &'static BStr = b_str!("My awesome BStr!");
2426
/// ```
2527
#[macro_export]
@@ -242,7 +244,9 @@ where
242244
///
243245
/// # Examples
244246
///
245-
/// ```rust,no_run
247+
/// ```
248+
/// # use kernel::c_str;
249+
/// # use kernel::str::CStr;
246250
/// const MY_CSTR: &'static CStr = c_str!("My awesome CStr!");
247251
/// ```
248252
#[macro_export]

rust/kernel/sync/locked_by.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -24,8 +24,8 @@ use core::{cell::UnsafeCell, ops::Deref, ptr};
2424
/// locked; we enforce at run time that the right `InnerDirectory` is locked.
2525
///
2626
/// ```
27-
/// use super::Mutex;
28-
/// use alloc::{string::String, vec::Vec};
27+
/// # use kernel::prelude::*;
28+
/// use kernel::sync::{LockedBy, Mutex};
2929
///
3030
/// struct InnerFile {
3131
/// bytes_used: u64,

0 commit comments

Comments
 (0)