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

Lines changed: 3 additions & 0 deletions
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

Lines changed: 7 additions & 0 deletions
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

Lines changed: 2 additions & 1 deletion
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

Lines changed: 57 additions & 15 deletions
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

Lines changed: 3 additions & 0 deletions
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

Lines changed: 3 additions & 0 deletions
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

Lines changed: 5 additions & 0 deletions
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

Lines changed: 5 additions & 2 deletions
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

Lines changed: 9 additions & 2 deletions
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

Lines changed: 6 additions & 3 deletions
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;

0 commit comments

Comments
 (0)