Skip to content

Support unit tests and doctests #361

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 5 commits into from
Jun 7, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .github/workflows/ci.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -394,6 +394,9 @@ jobs:
# Docs
- run: make ${{ env.MAKE_ARCH }} ${{ env.MAKE_CROSS_COMPILE }} ${{ env.MAKE_TOOLCHAIN }} ${{ env.MAKE_OUTPUT }} ${{ env.MAKE_SYSROOT }} -j3 rustdoc

# Tests
- run: make ${{ env.MAKE_ARCH }} ${{ env.MAKE_CROSS_COMPILE }} ${{ env.MAKE_TOOLCHAIN }} ${{ env.MAKE_OUTPUT }} ${{ env.MAKE_SYSROOT }} -j3 rusttest

# Formatting
- run: make rustfmtcheck

Expand Down
7 changes: 7 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -1739,6 +1739,8 @@ help:
@echo ' is formatted, printing a diff otherwise.'
@echo ' rustdoc - Generate Rust documentation'
@echo ' (requires kernel .config)'
@echo ' rusttest - Runs the Rust tests'
@echo ' (requires kernel .config)'
@echo ' rust-analyzer - Generate rust-project.json rust-analyzer support file'
@echo ' (requires kernel .config)'
@echo ''
Expand Down Expand Up @@ -1824,6 +1826,11 @@ PHONY += rustdoc
rustdoc: prepare0
$(Q)$(MAKE) $(build)=rust $@

# Testing target
PHONY += rusttest
rusttest: prepare0
$(Q)$(MAKE) $(build)=rust $@

# Formatting targets
PHONY += rustfmt rustfmtcheck

Expand Down
3 changes: 2 additions & 1 deletion rust/.gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,5 @@

bindings_generated.rs
exports_*_generated.h
doc/
doc/
test/
72 changes: 57 additions & 15 deletions rust/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -19,39 +19,81 @@ obj-$(CONFIG_RUST) += exports.o

RUSTDOC = rustdoc

quiet_cmd_rustdoc_host = RUSTDOC $<
cmd_rustdoc_host = \
RUST_BINDINGS_FILE=$(abspath $(objtree)/rust/bindings_generated.rs) \
$(RUSTDOC) $(filter-out --emit=%, $(rustc_flags)) \
$(rustdoc_target_flags) -L $(objtree)/rust/ \
--output $(objtree)/rust/doc --crate-name $(subst rustdoc-,,$@) \
-Fmissing-docs @$(objtree)/include/generated/rustc_cfg $<

quiet_cmd_rustdoc = RUSTDOC $<
quiet_cmd_rustdoc = RUSTDOC $(if $(filter --test,$(rustdoc_target_flags)),T, ) $(if $(rustdoc_host),H, ) $<
cmd_rustdoc = \
RUST_BINDINGS_FILE=$(abspath $(objtree)/rust/bindings_generated.rs) \
$(RUSTDOC) $(rustc_cross_flags) $(filter-out --emit=%, $(rustc_flags)) \
$(rustdoc_target_flags) -L $(objtree)/rust/ \
--output $(objtree)/rust/doc --crate-name $(subst rustdoc-,,$@) \
$(RUSTDOC) $(if $(rustdoc_host),,$(rustc_cross_flags)) \
$(filter-out --emit=%, $(rustc_flags)) $(rustc_target_flags) $(rustdoc_target_flags) \
-L $(objtree)/rust/$(if $(filter --test,$(rustdoc_target_flags)),test/) \
--output $(objtree)/rust/doc --crate-name $(subst rusttest-,,$(subst rustdoc-,,$@)) \
-Fmissing-docs @$(objtree)/include/generated/rustc_cfg $<

rustdoc: rustdoc-macros rustdoc-compiler_builtins rustdoc-kernel

rustdoc-macros: private rustdoc_target_flags = --crate-type proc-macro \
rustdoc-macros: private rustdoc_host = yes
rustdoc-macros: private rustc_target_flags = --crate-type proc-macro \
--extern proc_macro
rustdoc-macros: $(srctree)/rust/macros/lib.rs FORCE
$(call if_changed,rustdoc_host)
$(call if_changed,rustdoc)

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

rustdoc-kernel: private rustdoc_target_flags = --extern alloc \
rustdoc-kernel: private rustc_target_flags = --extern alloc \
--extern build_error \
--extern macros=$(objtree)/rust/libmacros.so
rustdoc-kernel: $(srctree)/rust/kernel/lib.rs rustdoc-macros \
$(objtree)/rust/libmacros.so $(objtree)/rust/bindings_generated.rs FORCE
$(call if_changed,rustdoc)

quiet_cmd_rustc_test_library = RUSTC TL $<
cmd_rustc_test_library = \
RUST_BINDINGS_FILE=$(abspath $(objtree)/rust/bindings_generated.rs) \
$(RUSTC) $(filter-out -Cpanic=abort, $(filter-out --emit=%, $(rustc_flags))) \
$(rustc_target_flags) --crate-type $(if $(rustc_test_library_proc),proc-macro,rlib) \
--out-dir $(objtree)/rust/test/ --cfg testlib \
-L $(objtree)/rust/test/ --crate-name $(subst rusttest-,,$(subst rusttestlib-,,$@)) $<

rusttestlib-build_error: $(srctree)/rust/build_error.rs FORCE
$(call if_changed,rustc_test_library)

rusttestlib-macros: private rustc_target_flags = --extern proc_macro
rusttestlib-macros: private rustc_test_library_proc = yes
rusttestlib-macros: $(srctree)/rust/macros/lib.rs FORCE
$(call if_changed,rustc_test_library)

# We cannot use `-Zpanic-abort-tests` because some tests are dynamic,
# so for the moment we skip `-Cpanic=abort`.
quiet_cmd_rustc_test = RUSTC T $<
cmd_rustc_test = \
RUST_BINDINGS_FILE=$(abspath $(objtree)/rust/bindings_generated.rs) \
$(RUSTC) --test $(filter-out -Cpanic=abort, $(filter-out --emit=%, $(rustc_flags))) \
$(rustc_target_flags) --out-dir $(objtree)/rust/test \
-L $(objtree)/rust/test/ --crate-name $(subst rusttest-,,$@) $<; \
$(objtree)/rust/test/$(subst rusttest-,,$@) $(rustc_test_run_flags)

rusttest: rusttest-macros rusttest-kernel

rusttest-macros: private rustc_target_flags = --extern proc_macro
rusttest-macros: private rustdoc_host = yes
rusttest-macros: private rustdoc_target_flags = --test --crate-type proc-macro
rusttest-macros: $(srctree)/rust/macros/lib.rs FORCE
$(call if_changed,rustc_test)
$(call if_changed,rustdoc)

rusttest-kernel: private rustc_target_flags = --extern alloc \
--extern build_error \
--extern macros=$(objtree)/rust/test/libmacros.so
rusttest-kernel: private rustc_test_run_flags = \
--skip bindgen_test_layout_
rusttest-kernel: private rustdoc_host = yes
rusttest-kernel: private rustdoc_target_flags = --test
rusttest-kernel: $(srctree)/rust/kernel/lib.rs rusttestlib-build_error \
rusttestlib-macros FORCE
$(call if_changed,rustc_test)
$(call if_changed,rustc_test_library)
$(call if_changed,rustdoc)

ifdef CONFIG_CC_IS_CLANG
bindgen_c_flags = $(c_flags)
else
Expand Down
3 changes: 3 additions & 0 deletions rust/bindgen_parameters
Original file line number Diff line number Diff line change
Expand Up @@ -8,3 +8,6 @@
# If SMP is disabled, `arch_spinlock_t` is defined as a ZST which triggers a Rust
# warning. We don't need to peek into it anyway.
--opaque-type spinlock

# `seccomp`'s comment gets understood as a doctest
--no-doc-comments
3 changes: 3 additions & 0 deletions rust/kernel/allocator.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,9 @@ unsafe impl GlobalAlloc for KernelAllocator {
}
}

#[global_allocator]
static ALLOCATOR: KernelAllocator = KernelAllocator;

#[alloc_error_handler]
fn oom(_layout: Layout) -> ! {
panic!("Out of memory!");
Expand Down
5 changes: 5 additions & 0 deletions rust/kernel/bindings.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,11 @@
//!
//! Imports the generated bindings by `bindgen`.

// See https://github.com/rust-lang/rust-bindgen/issues/1651.
#![cfg_attr(test, allow(deref_nullptr))]
#![cfg_attr(test, allow(unaligned_references))]
#![cfg_attr(test, allow(unsafe_op_in_unsafe_fn))]

#[allow(
clippy::all,
non_camel_case_types,
Expand Down
7 changes: 5 additions & 2 deletions rust/kernel/build_assert.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,8 @@
/// be called, a build error will be triggered.
///
/// # Examples
/// ```no_run
/// ```
/// # use kernel::build_error;
/// #[inline]
/// fn foo(a: usize) -> usize {
/// a.checked_add(1).unwrap_or_else(|| build_error!("overflow"))
Expand Down Expand Up @@ -38,7 +39,8 @@ macro_rules! build_error {
/// These examples show that different types of [`assert!`] will trigger errors
/// at different stage of compilation. It is preferred to err as early as
/// possible, so [`static_assert!`] should be used whenever possible.
/// ```no_run
/// ```compile_fail
/// # use kernel::prelude::*;
/// fn foo() {
/// static_assert!(1 > 1); // Compile-time error
/// build_assert!(1 > 1); // Build-time error
Expand All @@ -49,6 +51,7 @@ macro_rules! build_error {
/// When the condition refers to generic parameters or parameters of an inline function,
/// [`static_assert!`] cannot be used. Use `build_assert!` in this scenario.
/// ```no_run
/// # use kernel::prelude::*;
/// fn foo<const N: usize>() {
/// // `static_assert!(N > 1);` is not allowed
/// build_assert!(N > 1); // Build-time check
Expand Down
11 changes: 9 additions & 2 deletions rust/kernel/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -190,7 +190,10 @@ where
///
/// # Examples
///
/// ```rust,no_run
/// ```ignore
/// # use kernel::from_kernel_result;
/// # use kernel::c_types;
/// # use kernel::bindings;
/// unsafe extern "C" fn probe_callback(
/// pdev: *mut bindings::platform_device,
/// ) -> c_types::c_int {
Expand Down Expand Up @@ -219,7 +222,11 @@ macro_rules! from_kernel_result {
///
/// # Examples
///
/// ```rust,no_run
/// ```ignore
/// # use kernel::prelude::*;
/// # use kernel::from_kernel_err_ptr;
/// # use kernel::c_types;
/// # use kernel::bindings;
/// fn devm_platform_ioremap_resource(
/// pdev: &mut PlatformDevice,
/// index: u32,
Expand Down
9 changes: 6 additions & 3 deletions rust/kernel/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,8 @@
#[cfg(not(CONFIG_RUST))]
compile_error!("Missing kernel configuration for conditional compilation");

#[cfg(not(test))]
#[cfg(not(testlib))]
mod allocator;

#[doc(hidden)]
Expand Down Expand Up @@ -155,6 +157,8 @@ impl<'a> Drop for KParamGuard<'a> {
/// # Example
///
/// ```
/// # use kernel::prelude::*;
/// # use kernel::offset_of;
/// struct Test {
/// a: u64,
/// b: u32,
Expand Down Expand Up @@ -193,6 +197,8 @@ macro_rules! offset_of {
/// # Example
///
/// ```
/// # use kernel::prelude::*;
/// # use kernel::container_of;
/// struct Test {
/// a: u64,
/// b: u32,
Expand All @@ -213,6 +219,3 @@ macro_rules! container_of {
unsafe { ($ptr as *const _ as *const u8).offset(-offset) as *const $type }
}}
}

#[global_allocator]
static ALLOCATOR: allocator::KernelAllocator = allocator::KernelAllocator;
2 changes: 1 addition & 1 deletion rust/kernel/module_param.rs
Original file line number Diff line number Diff line change
Expand Up @@ -210,7 +210,7 @@ macro_rules! impl_module_param {
/// Generate a static [`kernel_param_ops`](../../../include/linux/moduleparam.h) struct.
///
/// # Example
/// ```rust
/// ```ignore
/// make_param_ops!(
/// /// Documentation for new param ops.
/// PARAM_OPS_MYTYPE, // Name for the static.
Expand Down
2 changes: 1 addition & 1 deletion rust/kernel/prelude.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
//!
//! # Examples
//!
//! ```rust,no_run
//! ```
//! use kernel::prelude::*;
//! ```

Expand Down
19 changes: 19 additions & 0 deletions rust/kernel/print.rs
Original file line number Diff line number Diff line change
Expand Up @@ -163,6 +163,7 @@ pub fn call_printk_cont(args: fmt::Arguments<'_>) {
///
/// Public but hidden since it should only be used from public macros.
#[doc(hidden)]
#[cfg(not(testlib))]
#[macro_export]
macro_rules! print_macro (
// The non-continuation cases (most of them, e.g. `INFO`).
Expand All @@ -189,6 +190,15 @@ macro_rules! print_macro (
);
);

// Stub for doctests
#[cfg(testlib)]
#[macro_export]
macro_rules! print_macro (
($format_string:path, $e:expr, $($arg:tt)+) => (
()
);
);

// We could use a macro to generate these macros. However, doing so ends
// up being a bit ugly: it requires the dollar token trick to escape `$` as
// well as playing with the `doc` attribute. Furthermore, they cannot be easily
Expand All @@ -213,6 +223,7 @@ macro_rules! print_macro (
/// # Examples
///
/// ```
/// # use kernel::prelude::*;
/// pr_emerg!("hello {}\n", "there");
/// ```
#[macro_export]
Expand All @@ -237,6 +248,7 @@ macro_rules! pr_emerg (
/// # Examples
///
/// ```
/// # use kernel::prelude::*;
/// pr_alert!("hello {}\n", "there");
/// ```
#[macro_export]
Expand All @@ -261,6 +273,7 @@ macro_rules! pr_alert (
/// # Examples
///
/// ```
/// # use kernel::prelude::*;
/// pr_crit!("hello {}\n", "there");
/// ```
#[macro_export]
Expand All @@ -285,6 +298,7 @@ macro_rules! pr_crit (
/// # Examples
///
/// ```
/// # use kernel::prelude::*;
/// pr_err!("hello {}\n", "there");
/// ```
#[macro_export]
Expand All @@ -309,6 +323,7 @@ macro_rules! pr_err (
/// # Examples
///
/// ```
/// # use kernel::prelude::*;
/// pr_warn!("hello {}\n", "there");
/// ```
#[macro_export]
Expand All @@ -333,6 +348,7 @@ macro_rules! pr_warn (
/// # Examples
///
/// ```
/// # use kernel::prelude::*;
/// pr_notice!("hello {}\n", "there");
/// ```
#[macro_export]
Expand All @@ -357,6 +373,7 @@ macro_rules! pr_notice (
/// # Examples
///
/// ```
/// # use kernel::prelude::*;
/// pr_info!("hello {}\n", "there");
/// ```
#[macro_export]
Expand All @@ -382,6 +399,8 @@ macro_rules! pr_info (
/// # Examples
///
/// ```
/// # use kernel::prelude::*;
/// # use kernel::pr_cont;
/// pr_info!("hello");
/// pr_cont!(" {}\n", "there");
/// ```
Expand Down
1 change: 1 addition & 0 deletions rust/kernel/static_assert.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
/// # Examples
///
/// ```
/// # use kernel::prelude::*;
/// static_assert!(42 > 24);
/// static_assert!(core::mem::size_of::<u8>() == 1);
///
Expand Down
8 changes: 6 additions & 2 deletions rust/kernel/str.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,9 @@ pub type BStr = [u8];
///
/// # Examples
///
/// ```rust,no_run
/// ```
/// # use kernel::b_str;
/// # use kernel::str::BStr;
/// const MY_BSTR: &'static BStr = b_str!("My awesome BStr!");
/// ```
#[macro_export]
Expand Down Expand Up @@ -242,7 +244,9 @@ where
///
/// # Examples
///
/// ```rust,no_run
/// ```
/// # use kernel::c_str;
/// # use kernel::str::CStr;
/// const MY_CSTR: &'static CStr = c_str!("My awesome CStr!");
/// ```
#[macro_export]
Expand Down
4 changes: 2 additions & 2 deletions rust/kernel/sync/locked_by.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,8 @@ use core::{cell::UnsafeCell, ops::Deref, ptr};
/// locked; we enforce at run time that the right `InnerDirectory` is locked.
///
/// ```
/// use super::Mutex;
/// use alloc::{string::String, vec::Vec};
/// # use kernel::prelude::*;
/// use kernel::sync::{LockedBy, Mutex};
///
/// struct InnerFile {
/// bytes_used: u64,
Expand Down
Loading