linux-next/scripts/rustdoc_test_builder.py
Miguel Ojeda 56bf83de2c scripts: add rustdoc_test_{builder,gen}.py scripts
Rust documentation tests are typically examples of usage of any
item (e.g. function, struct, module...). They are very convenient
because they are just written alongside the documentation, e.g.:

    /// Sums two numbers.
    ///
    /// # Examples
    ///
    /// ```
    /// assert_eq!(mymod::f(10, 20), 30);
    /// ```
    pub fn f(a: i32, b: i32) -> i32 {
        a + b
    }

These scripts are used to transform Rust documentation tests into
KUnit tests, so that they can be run in-kernel. In turn, this allows
us to run tests that use kernel APIs.

In particular, the test builder receives `rustdoc`-generated tests,
parses them and stores the result. Then, the test generator takes
the saved results and generates a KUnit suite where each original
documentation test is a test case.

For the moment, this is only done for the `kernel` crate, but
the plan is to generalize it for other crates and modules.

Co-developed-by: Alex Gaynor <alex.gaynor@gmail.com>
Signed-off-by: Alex Gaynor <alex.gaynor@gmail.com>
Co-developed-by: Wedson Almeida Filho <wedsonaf@google.com>
Signed-off-by: Wedson Almeida Filho <wedsonaf@google.com>
Signed-off-by: Miguel Ojeda <ojeda@kernel.org>
2022-05-23 02:24:55 +02:00

60 lines
1.5 KiB
Python
Executable File

#!/usr/bin/env python3
# SPDX-License-Identifier: GPL-2.0
"""rustdoc_test_builder - Test builder for `rustdoc`-generated tests.
"""
import json
import pathlib
import re
import sys
RUST_DIR = pathlib.Path("rust")
TESTS_DIR = RUST_DIR / "test" / "doctests" / "kernel"
# `[^\s]*` removes the prefix (e.g. `_doctest_main_`) plus any
# leading path (for `O=` builds).
MAIN_RE = re.compile(
r"^"
r"fn main\(\) { "
r"#\[allow\(non_snake_case\)\] "
r"fn ([^\s]*rust_kernel_([a-zA-Z0-9_]+))\(\) {"
r"$"
)
def main():
found_main = False
test_header = ""
test_body = ""
for line in sys.stdin.readlines():
main_match = MAIN_RE.match(line)
if main_match:
if found_main:
raise Exception("More than one `main` line found.")
found_main = True
function_name = main_match.group(1)
test_name = f"rust_kernel_doctest_{main_match.group(2)}"
continue
if found_main:
test_body += line
else:
test_header += line
if not found_main:
raise Exception("No `main` line found.")
call_line = f"}} {function_name}() }}"
if not test_body.endswith(call_line):
raise Exception("Unexpected end of test body.")
test_body = test_body[:-len(call_line)]
with open(TESTS_DIR / f"{test_name}.json", "w") as fd:
json.dump({
"name": test_name,
"header": test_header,
"body": test_body,
}, fd, sort_keys=True, indent=4)
if __name__ == "__main__":
main()