From patchwork Thu Jul 20 06:38:52 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: David Gow X-Patchwork-Id: 123092 Return-Path: Delivered-To: ouuuleilei@gmail.com Received: by 2002:a59:c923:0:b0:3e4:2afc:c1 with SMTP id j3csp2979009vqt; Thu, 20 Jul 2023 01:57:35 -0700 (PDT) X-Google-Smtp-Source: APBJJlG/ISndvHwSAVWGsILg2cDbjqwmcC3UvShpHa2ApcMcOpDzHe4qcNdyNtvDBhVPfV4hXp2y X-Received: by 2002:a17:906:2d1:b0:994:5407:9ac9 with SMTP id 17-20020a17090602d100b0099454079ac9mr5009569ejk.5.1689843455494; Thu, 20 Jul 2023 01:57:35 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1689843455; cv=none; d=google.com; s=arc-20160816; b=OAh8w2893L6RvTOj0hmxsnVvoLd5KCgt7MHoHn1HDR/i2HtqHblJPxKJnia9eGO9z4 c5avgWWUbPJoyBCYPhFFZBexyMhqi8d16xlr1ZvVRqf/yyH8KhXAiOL4nPtlESNsaviZ mTPp6qA4lujjPOmCEwLJP4Tx/Cy6da+YWMNCiKUL8QgJo/dNI17M9knVfKzS21bWCRfa pV59YaT/V/Ts1rBHCm35595bU9ei7TcWNRSZHOJ3k4nXee2YsGrD2Ln4dEp+IMVZmd/u qxSc5NYRfQb2Z8uScusbID1L3AQK6hdgM2HgHyggdoVxR/XWVw4W0GlnG/yPDzJTWRwR +AzQ== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=list-id:precedence:content-transfer-encoding:cc:to:from:subject :message-id:references:mime-version:in-reply-to:date:dkim-signature; bh=EPm0nt3jgFstVkuP2L4I35/ImZIIIbkQiLEGJDnMhOQ=; fh=tt14ALlvSCOaeGq2WOiQl00uBX8L7oEnWR0UCRJ1Mu8=; b=FXEOx7Is0mG/kYZjw6/KnUxyCsE4eN7Qfsksxhk6Un4tXR3++g2ZTk4zyG9clVnxio TN9dsdXqt2eLnoPLtwVrLOKn4ogozOJ9H/A/cKNS8N9MRO7k4eVWACAkwDO5PktGE2NG X6m/lhjl1y9c94WTtwnyDsVHnZ2Mmtc9imTp/TBCKcQac+be+ZSW41WlzHGGw3jXXvIQ Ve3IwlHLuipXrb8qtJk3qgeJK1ihQa+Z7aqIO0njGPFaIXDbWADJPGic4kA6WP0eP4tX qR4HRDGiuo0HTbthg9gWMYxncljNsj5OWLA1Ew6V73hrJozX23IaWttOnE12Z7m5TSyp uNeg== ARC-Authentication-Results: i=1; mx.google.com; dkim=pass header.i=@google.com header.s=20221208 header.b=68FGNx3i; spf=pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 2620:137:e000::1:20 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org; dmarc=pass (p=REJECT sp=REJECT dis=NONE) header.from=google.com Received: from out1.vger.email (out1.vger.email. [2620:137:e000::1:20]) by mx.google.com with ESMTP id v13-20020a1709060b4d00b00988ab18386bsi366695ejg.1050.2023.07.20.01.57.11; Thu, 20 Jul 2023 01:57:35 -0700 (PDT) Received-SPF: pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 2620:137:e000::1:20 as permitted sender) client-ip=2620:137:e000::1:20; Authentication-Results: mx.google.com; dkim=pass header.i=@google.com header.s=20221208 header.b=68FGNx3i; spf=pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 2620:137:e000::1:20 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org; dmarc=pass (p=REJECT sp=REJECT dis=NONE) header.from=google.com Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S231295AbjGTGlA (ORCPT + 99 others); Thu, 20 Jul 2023 02:41:00 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:48278 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S231149AbjGTGkn (ORCPT ); Thu, 20 Jul 2023 02:40:43 -0400 Received: from mail-yw1-x114a.google.com (mail-yw1-x114a.google.com [IPv6:2607:f8b0:4864:20::114a]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id DF5293596 for ; Wed, 19 Jul 2023 23:40:06 -0700 (PDT) Received: by mail-yw1-x114a.google.com with SMTP id 00721157ae682-5766e49b802so5182237b3.1 for ; Wed, 19 Jul 2023 23:40:06 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20221208; t=1689835200; x=1690440000; h=content-transfer-encoding:cc:to:from:subject:message-id:references :mime-version:in-reply-to:date:from:to:cc:subject:date:message-id :reply-to; bh=EPm0nt3jgFstVkuP2L4I35/ImZIIIbkQiLEGJDnMhOQ=; b=68FGNx3i914y2L8NGEkfyE+WJUK8YVUoYjjrHxNQrhrZIS5NkcY4CsAhDke0igeMZq lqjYx2csTSo+bHRoVhMFJu7BQZhHOwmsLJCXdBV264iGosUApSlREqcy4sHpzdlKj4Po Wxq/0PhkHH4a3epUVcE6b8NK3sR3Vpm/TwFf/25FmBGvknU3Fm6pMZDQQxeuj3mMa/6e wo9Wz0HQFTnTM+ZeIzLa5c9iBam6dxhVY2sPT/RCU1lAPVaoLuEFK2YXLcQdF/6fpn1p LevtoOgLMWrWL/nIN+VGRpCMG3P8cPAXLf02SCcDM/PMG9Vc7OrfxQWokQUkg8xmiaZM OdSg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20221208; t=1689835200; x=1690440000; h=content-transfer-encoding:cc:to:from:subject:message-id:references :mime-version:in-reply-to:date:x-gm-message-state:from:to:cc:subject :date:message-id:reply-to; bh=EPm0nt3jgFstVkuP2L4I35/ImZIIIbkQiLEGJDnMhOQ=; b=QYwG+qU0zGiuiaV51bqgKFgQIQ78ewVf4SRrwBR+z3tXOuDy/n+f4L8FUjV4XZATWm c+mqA952vv53fK/DBnoYyhjRCmw/VvblWl7i3Tq+u0+ApBFt4UPyN9BeWoHcv4y9/daO Tc9Wph2A8gpdzCHdIyrtcZejfJ68nbW+WmwWVkh5y0NgsFmaXk3JsV1effowAxTPHLQM nS3qgsk/7b8xDFvyfGPjO9dE+zZlhEXVC3JBfB6XmsE/BwJsDVhp6eJ43y2NJ8u2H36g tnYLHfEEPNnGGuF8C+Ag/TANb3yCDEOnaMgDaTTk9Q6H1x7W3c565rIu33At3l9urdUi 0G6A== X-Gm-Message-State: ABy/qLbaxCS3hMMn5c1KNYdkmg0ETh+cHYowquLAAma83KJBzHoPp1Xs TZSZX3PUuJGfFdZKDO/Q7ATirjGXgigOLg== X-Received: from slicestar.c.googlers.com ([fda3:e722:ac3:cc00:4f:4b78:c0a8:20a1]) (user=davidgow job=sendgmr) by 2002:a81:c50a:0:b0:55a:3133:86fa with SMTP id k10-20020a81c50a000000b0055a313386famr199503ywi.3.1689835200553; Wed, 19 Jul 2023 23:40:00 -0700 (PDT) Date: Thu, 20 Jul 2023 14:38:52 +0800 In-Reply-To: <20230720-rustbind-v1-0-c80db349e3b5@google.com> Mime-Version: 1.0 References: <20230720-rustbind-v1-0-c80db349e3b5@google.com> X-Mailer: b4 0.13-dev-099c9 Message-ID: <20230720-rustbind-v1-1-c80db349e3b5@google.com> Subject: [PATCH 1/3] rust: kunit: add KUnit case and suite macros From: David Gow To: Brendan Higgins , Miguel Ojeda , Alex Gaynor , Wedson Almeida Filho , Boqun Feng , Gary Guo , Benno Lossin Cc: David Gow , " =?utf-8?b?Sm9zw6kgRXhww7NzaXRv?= " , " =?utf-8?q?Bj=C3=B6rn_Roy_Baron?= " , linux-kselftest@vger.kernel.org, kunit-dev@googlegroups.com, rust-for-linux@vger.kernel.org, linux-kernel@vger.kernel.org X-Spam-Status: No, score=-9.6 required=5.0 tests=BAYES_00,DKIMWL_WL_MED, DKIM_SIGNED,DKIM_VALID,DKIM_VALID_AU,DKIM_VALID_EF, RCVD_IN_DNSWL_BLOCKED,SPF_HELO_NONE,SPF_PASS,T_SCC_BODY_TEXT_LINE, USER_IN_DEF_DKIM_WL autolearn=unavailable autolearn_force=no version=3.4.6 X-Spam-Checker-Version: SpamAssassin 3.4.6 (2021-04-09) on lindbergh.monkeyblade.net Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org X-getmail-retrieved-from-mailbox: INBOX X-GMAIL-THRID: 1771929291148544635 X-GMAIL-MSGID: 1771929291148544635 From: José Expósito Add a couple of Rust macros to allow to develop KUnit tests without relying on generated C code: - The `kunit_unsafe_test_suite!` Rust macro is similar to the `kunit_test_suite` C macro. - The `kunit_case!` Rust macro is similar to the `KUNIT_CASE` C macro. It can be used with parameters to generate a test case or without parameters to be used as delimiter in `kunit_test_suite!`. While these macros can be used on their own, a future patch will introduce another macro to create KUnit tests using a user-space like syntax. Co-developed-by: David Gow Signed-off-by: David Gow Signed-off-by: José Expósito --- rust/kernel/kunit.rs | 92 ++++++++++++++++++++++++++++++++++++++++++++++++++++ rust/kernel/lib.rs | 1 + 2 files changed, 93 insertions(+) diff --git a/rust/kernel/kunit.rs b/rust/kernel/kunit.rs index 722655b2d62d..4cffc71e463b 100644 --- a/rust/kernel/kunit.rs +++ b/rust/kernel/kunit.rs @@ -161,3 +161,95 @@ macro_rules! kunit_assert_eq { $crate::kunit_assert!($name, $file, $diff, $left == $right); }}; } + +/// Represents an individual test case. +/// +/// The test case should have the signature +/// `unsafe extern "C" fn test_case(test: *mut crate::bindings::kunit)`. +/// +/// The `kunit_unsafe_test_suite!` macro expects a NULL-terminated list of test cases. This macro +/// can be invoked without parameters to generate the delimiter. +#[macro_export] +macro_rules! kunit_case { + () => { + $crate::bindings::kunit_case { + run_case: None, + name: core::ptr::null_mut(), + generate_params: None, + status: $crate::bindings::kunit_status_KUNIT_SUCCESS, + log: core::ptr::null_mut(), + } + }; + ($name:ident, $run_case:ident) => { + $crate::bindings::kunit_case { + run_case: Some($run_case), + name: $crate::c_str!(core::stringify!($name)).as_char_ptr(), + generate_params: None, + status: $crate::bindings::kunit_status_KUNIT_SUCCESS, + log: core::ptr::null_mut(), + } + }; +} + +/// Registers a KUnit test suite. +/// +/// # Safety +/// +/// `test_cases` must be a NULL terminated array of test cases. +/// +/// # Examples +/// +/// ```ignore +/// unsafe extern "C" fn test_fn(_test: *mut crate::bindings::kunit) { +/// let actual = 1 + 1; +/// let expected = 2; +/// assert_eq!(actual, expected); +/// } +/// +/// static mut KUNIT_TEST_CASE: crate::bindings::kunit_case = crate::kunit_case!(name, test_fn); +/// static mut KUNIT_NULL_CASE: crate::bindings::kunit_case = crate::kunit_case!(); +/// static mut KUNIT_TEST_CASES: &mut[crate::bindings::kunit_case] = unsafe { +/// &mut[KUNIT_TEST_CASE, KUNIT_NULL_CASE] +/// }; +/// crate::kunit_unsafe_test_suite!(suite_name, KUNIT_TEST_CASES); +/// ``` +#[macro_export] +macro_rules! kunit_unsafe_test_suite { + ($name:ident, $test_cases:ident) => { + const _: () = { + static KUNIT_TEST_SUITE_NAME: [i8; 256] = { + let name_u8 = core::stringify!($name).as_bytes(); + let mut ret = [0; 256]; + + let mut i = 0; + while i < name_u8.len() { + ret[i] = name_u8[i] as i8; + i += 1; + } + + ret + }; + + // SAFETY: `test_cases` is valid as it should be static. + static mut KUNIT_TEST_SUITE: core::cell::UnsafeCell<$crate::bindings::kunit_suite> = + core::cell::UnsafeCell::new($crate::bindings::kunit_suite { + name: KUNIT_TEST_SUITE_NAME, + test_cases: unsafe { $test_cases.as_mut_ptr() }, + suite_init: None, + suite_exit: None, + init: None, + exit: None, + status_comment: [0; 256usize], + debugfs: core::ptr::null_mut(), + log: core::ptr::null_mut(), + suite_init_err: 0, + }); + + // SAFETY: `KUNIT_TEST_SUITE` is static. + #[used] + #[link_section = ".kunit_test_suites"] + static mut KUNIT_TEST_SUITE_ENTRY: *const $crate::bindings::kunit_suite = + unsafe { KUNIT_TEST_SUITE.get() }; + }; + }; +} diff --git a/rust/kernel/lib.rs b/rust/kernel/lib.rs index 3642cadc34b1..ec81fd28d71a 100644 --- a/rust/kernel/lib.rs +++ b/rust/kernel/lib.rs @@ -18,6 +18,7 @@ #![feature(new_uninit)] #![feature(receiver_trait)] #![feature(unsize)] +#![feature(const_mut_refs)] // Ensure conditional compilation based on the kernel configuration works; // otherwise we may silently break things like initcall handling. From patchwork Thu Jul 20 06:38:53 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: David Gow X-Patchwork-Id: 123065 Return-Path: Delivered-To: ouuuleilei@gmail.com Received: by 2002:a59:c923:0:b0:3e4:2afc:c1 with SMTP id j3csp2959005vqt; Thu, 20 Jul 2023 01:06:22 -0700 (PDT) X-Google-Smtp-Source: APBJJlEENLGydoTI429YVqzi+IdMq18J8be7Kcko9rNis45+DeAcs+9JmU4PMXWYtaWZD4bLPC7Y X-Received: by 2002:aa7:d1ca:0:b0:51d:fa7c:c330 with SMTP id g10-20020aa7d1ca000000b0051dfa7cc330mr4448403edp.26.1689840381896; Thu, 20 Jul 2023 01:06:21 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1689840381; cv=none; d=google.com; s=arc-20160816; b=VJVMnGlAzv7uQ3xHpy8833OcRB/WdTNXFYbIXXvgoehS0VGOHdHN+5rPwARE99Xntc MEiJHIIB9tjhtUnM7CQsBSgbcu7wLkErMHrTimh6cexeCpQ7P3Oxdo4UozsXnNvc3gnw qJULykLW56phgq9SRBHj9ed/e1LVJNR08Z75JZFWGQkxyIlDnQM00w5ZICwA+W90Hve/ chO7ZxljY9AkS9/R3sCnin5l29J/ZUiMPwApCVkti9JApo5mZpEjlQRkJqG8EfY289cg EqJ2txSHoptB/4JX44Ht9/dmYoVtkB04B7OZqSoBpWwR0cEmBQ75BuuiR3VaxcMgQoKX Oh7g== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=list-id:precedence:content-transfer-encoding:cc:to:from:subject :message-id:references:mime-version:in-reply-to:date:dkim-signature; bh=5518GRtCGTsC8V9nNP/WKn16Z2CeoFYtE0lstBJ/N+g=; fh=tt14ALlvSCOaeGq2WOiQl00uBX8L7oEnWR0UCRJ1Mu8=; b=E0Qdm2S2DyOeIGCyEYHcDy01+kltWZlMlLom1cRF+Ko2UQ2kJ7LuQC/I80MBaJ+PqF PL0gWJ6W3Fy1PP/rObbQZNhS9xKqmJbHykPiAdSU010zb/tAUhuB7OfXBLxVQ+u8HyWF Snib5HWUkncAPyxEZrodlHdMknPCCqbZvufpx8nkEDZiYwS9LiMgpVH/eI0FbDBD5YdQ f8xdj3WdnbWJzAtAcG6vCNndv7OdZyYxkTtwHWr5KKmcF0YRf1/LEtLzocnUVyj44F9o MdKvwqT8jqbw5qNe2ZBkV0vwQg25RgBLGdN0dv3HRRCA3/3FfD36LX3q/vC2/NIe5GGp WjWA== ARC-Authentication-Results: i=1; mx.google.com; dkim=pass header.i=@google.com header.s=20221208 header.b=1ZKBxBRQ; spf=pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 2620:137:e000::1:20 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org; dmarc=pass (p=REJECT sp=REJECT dis=NONE) header.from=google.com Received: from out1.vger.email (out1.vger.email. [2620:137:e000::1:20]) by mx.google.com with ESMTP id i26-20020a056402055a00b0051e52bd68f2si455474edx.192.2023.07.20.01.05.58; Thu, 20 Jul 2023 01:06:21 -0700 (PDT) Received-SPF: pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 2620:137:e000::1:20 as permitted sender) client-ip=2620:137:e000::1:20; Authentication-Results: mx.google.com; dkim=pass header.i=@google.com header.s=20221208 header.b=1ZKBxBRQ; spf=pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 2620:137:e000::1:20 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org; dmarc=pass (p=REJECT sp=REJECT dis=NONE) header.from=google.com Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S231308AbjGTGlL (ORCPT + 99 others); Thu, 20 Jul 2023 02:41:11 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:49746 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S231304AbjGTGks (ORCPT ); Thu, 20 Jul 2023 02:40:48 -0400 Received: from mail-yw1-x1149.google.com (mail-yw1-x1149.google.com [IPv6:2607:f8b0:4864:20::1149]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 17971269E for ; Wed, 19 Jul 2023 23:40:14 -0700 (PDT) Received: by mail-yw1-x1149.google.com with SMTP id 00721157ae682-573a92296c7so5239267b3.1 for ; Wed, 19 Jul 2023 23:40:14 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20221208; t=1689835205; x=1690440005; h=content-transfer-encoding:cc:to:from:subject:message-id:references :mime-version:in-reply-to:date:from:to:cc:subject:date:message-id :reply-to; bh=5518GRtCGTsC8V9nNP/WKn16Z2CeoFYtE0lstBJ/N+g=; b=1ZKBxBRQ8hr4t0f2fuCNrFrn4dVRrOB6k/W3B8yh2hmnDX0msPZaC3KW7iaO1yTpTv 8A21ffDBmMrYqzsQ02KbKfYzWSZ+2Dla0TWdlOdqVvTkbf8UnzXr6Isgr79pevYXVW44 e6ewqZrUl9aH/pwdEjta0HEAbJE68LID+WSqtbZVlgQ6odfKtceeIY7lJLe9cxbzD7Tc +il22mA7Y/hPjRjEj+Ovug2wGYNGFEzdFObQ3ijlfKHHLXAMUPeiYNA+1mL3fO5fl/WS 2WgQczr7iYs2up+RP+gotFd2veL/q9aPpKtBPV/JNXR4GIWoVOfgZSjhEqezKXxc/lL9 ATmQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20221208; t=1689835205; x=1690440005; h=content-transfer-encoding:cc:to:from:subject:message-id:references :mime-version:in-reply-to:date:x-gm-message-state:from:to:cc:subject :date:message-id:reply-to; bh=5518GRtCGTsC8V9nNP/WKn16Z2CeoFYtE0lstBJ/N+g=; b=CaiWH/BjpRtaDOdc6RBL/vk0lx+qn+02XmOerY6AHG825rU8TKhQXMiycLviVAbElr ld5+LihAhEJvadzQfiKiNdDYLeU1QwmuW/fZ2DTTlG8SYz1CIBtJj8ZBXsq4nPuw8Yqh X1oFKiNH4LeZn+0bMe8lYHK0u2ji5+CsETua26DBe4C9zfyn6ZQohrz42JH1o9tmZfjw BAGD0dpi/CHDJaVYYkHCSPdWomejMGfMIYrciDkeN8A4F1C2A106wjsvFPb6ZBPAVcrn AX9b1vcVSv0U1rZsNJG/X7N8DQJCKh8cTb6GPWYRxOLsEVE70SdSC4vH589731fzsTk8 Jesg== X-Gm-Message-State: ABy/qLaXaD2aXpyRpd5YxKqzikCicEtKT3oYIYv8SzzSDY8YfVwbt1Us cxqCfk/DAV8A06s3hpHQXHY8oI+NJJg4Yg== X-Received: from slicestar.c.googlers.com ([fda3:e722:ac3:cc00:4f:4b78:c0a8:20a1]) (user=davidgow job=sendgmr) by 2002:a25:69ce:0:b0:ce8:e6f:440c with SMTP id e197-20020a2569ce000000b00ce80e6f440cmr36778ybc.6.1689835205186; Wed, 19 Jul 2023 23:40:05 -0700 (PDT) Date: Thu, 20 Jul 2023 14:38:53 +0800 In-Reply-To: <20230720-rustbind-v1-0-c80db349e3b5@google.com> Mime-Version: 1.0 References: <20230720-rustbind-v1-0-c80db349e3b5@google.com> X-Mailer: b4 0.13-dev-099c9 Message-ID: <20230720-rustbind-v1-2-c80db349e3b5@google.com> Subject: [PATCH 2/3] rust: macros: add macro to easily run KUnit tests From: David Gow To: Brendan Higgins , Miguel Ojeda , Alex Gaynor , Wedson Almeida Filho , Boqun Feng , Gary Guo , Benno Lossin Cc: David Gow , " =?utf-8?b?Sm9zw6kgRXhww7NzaXRv?= " , " =?utf-8?q?Bj=C3=B6rn_Roy_Baron?= " , linux-kselftest@vger.kernel.org, kunit-dev@googlegroups.com, rust-for-linux@vger.kernel.org, linux-kernel@vger.kernel.org X-Spam-Status: No, score=-9.6 required=5.0 tests=BAYES_00,DKIMWL_WL_MED, DKIM_SIGNED,DKIM_VALID,DKIM_VALID_AU,DKIM_VALID_EF, RCVD_IN_DNSWL_BLOCKED,SPF_HELO_NONE,SPF_PASS,T_SCC_BODY_TEXT_LINE, USER_IN_DEF_DKIM_WL autolearn=unavailable autolearn_force=no version=3.4.6 X-Spam-Checker-Version: SpamAssassin 3.4.6 (2021-04-09) on lindbergh.monkeyblade.net Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org X-getmail-retrieved-from-mailbox: INBOX X-GMAIL-THRID: 1771926068130651571 X-GMAIL-MSGID: 1771926068130651571 From: José Expósito Add a new procedural macro (`#[kunit_tests(kunit_test_suit_name)]`) to run KUnit tests using a user-space like syntax. The macro, that should be used on modules, transforms every `#[test]` in a `kunit_case!` and adds a `kunit_unsafe_test_suite!` registering all of them. The only difference with user-space tests is that instead of using `#[cfg(test)]`, `#[kunit_tests(kunit_test_suit_name)]` is used. Note that `#[cfg(CONFIG_KUNIT)]` is added so the test module is not compiled when `CONFIG_KUNIT` is set to `n`. Reviewed-by: David Gow Signed-off-by: José Expósito --- MAINTAINERS | 1 + rust/kernel/kunit.rs | 11 ++++ rust/macros/kunit.rs | 149 +++++++++++++++++++++++++++++++++++++++++++++++++++ rust/macros/lib.rs | 29 ++++++++++ 4 files changed, 190 insertions(+) diff --git a/MAINTAINERS b/MAINTAINERS index 2a942fe59144..c32ba6b29a96 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -11373,6 +11373,7 @@ F: Documentation/dev-tools/kunit/ F: include/kunit/ F: lib/kunit/ F: rust/kernel/kunit.rs +F: rust/macros/kunit.rs F: scripts/rustdoc_test_* F: tools/testing/kunit/ diff --git a/rust/kernel/kunit.rs b/rust/kernel/kunit.rs index 4cffc71e463b..44ea67028316 100644 --- a/rust/kernel/kunit.rs +++ b/rust/kernel/kunit.rs @@ -40,6 +40,8 @@ pub fn info(args: fmt::Arguments<'_>) { } } +use macros::kunit_tests; + /// Asserts that a boolean expression is `true` at runtime. /// /// Public but hidden since it should only be used from generated tests. @@ -253,3 +255,12 @@ macro_rules! kunit_unsafe_test_suite { }; }; } + +#[kunit_tests(rust_kernel_kunit)] +mod tests { + #[test] + fn rust_test_kunit_kunit_tests() { + let running = true; + assert_eq!(running, true); + } +} diff --git a/rust/macros/kunit.rs b/rust/macros/kunit.rs new file mode 100644 index 000000000000..69dac253232f --- /dev/null +++ b/rust/macros/kunit.rs @@ -0,0 +1,149 @@ +// SPDX-License-Identifier: GPL-2.0 + +//! Procedural macro to run KUnit tests using a user-space like syntax. +//! +//! Copyright (c) 2023 José Expósito + +use proc_macro::{Delimiter, Group, TokenStream, TokenTree}; +use std::fmt::Write; + +pub(crate) fn kunit_tests(attr: TokenStream, ts: TokenStream) -> TokenStream { + if attr.to_string().is_empty() { + panic!("Missing test name in #[kunit_tests(test_name)] macro") + } + + let mut tokens: Vec<_> = ts.into_iter().collect(); + + // Scan for the "mod" keyword. + tokens + .iter() + .find_map(|token| match token { + TokenTree::Ident(ident) => match ident.to_string().as_str() { + "mod" => Some(true), + _ => None, + }, + _ => None, + }) + .expect("#[kunit_tests(test_name)] attribute should only be applied to modules"); + + // Retrieve the main body. The main body should be the last token tree. + let body = match tokens.pop() { + Some(TokenTree::Group(group)) if group.delimiter() == Delimiter::Brace => group, + _ => panic!("cannot locate main body of module"), + }; + + // Get the functions set as tests. Search for `[test]` -> `fn`. + let mut body_it = body.stream().into_iter(); + let mut tests = Vec::new(); + while let Some(token) = body_it.next() { + match token { + TokenTree::Group(ident) if ident.to_string() == "[test]" => match body_it.next() { + Some(TokenTree::Ident(ident)) if ident.to_string() == "fn" => { + let test_name = match body_it.next() { + Some(TokenTree::Ident(ident)) => ident.to_string(), + _ => continue, + }; + tests.push(test_name); + } + _ => continue, + }, + _ => (), + } + } + + // Add `#[cfg(CONFIG_KUNIT)]` before the module declaration. + let config_kunit = "#[cfg(CONFIG_KUNIT)]".to_owned().parse().unwrap(); + tokens.insert( + 0, + TokenTree::Group(Group::new(Delimiter::None, config_kunit)), + ); + + // Generate the test KUnit test suite and a test case for each `#[test]`. + // The code generated for the following test module: + // + // ``` + // #[kunit_tests(kunit_test_suit_name)] + // mod tests { + // #[test] + // fn foo() { + // assert_eq!(1, 1); + // } + // + // #[test] + // fn bar() { + // assert_eq!(2, 2); + // } + // ``` + // + // Looks like: + // + // ``` + // unsafe extern "C" fn kunit_rust_wrapper_foo(_test: *mut kernel::bindings::kunit) { + // foo(); + // } + // static mut KUNIT_CASE_FOO: kernel::bindings::kunit_case = + // kernel::kunit_case!(foo, kunit_rust_wrapper_foo); + // + // unsafe extern "C" fn kunit_rust_wrapper_bar(_test: * mut kernel::bindings::kunit) { + // bar(); + // } + // static mut KUNIT_CASE_BAR: kernel::bindings::kunit_case = + // kernel::kunit_case!(bar, kunit_rust_wrapper_bar); + // + // static mut KUNIT_CASE_NULL: kernel::bindings::kunit_case = kernel::kunit_case!(); + // + // static mut TEST_CASES : &mut[kernel::bindings::kunit_case] = unsafe { + // &mut [KUNIT_CASE_FOO, KUNIT_CASE_BAR, KUNIT_CASE_NULL] + // }; + // + // kernel::kunit_unsafe_test_suite!(kunit_test_suit_name, TEST_CASES); + // ``` + let mut kunit_macros = "".to_owned(); + let mut test_cases = "".to_owned(); + for test in tests { + let kunit_wrapper_fn_name = format!("kunit_rust_wrapper_{}", test); + let kunit_case_name = format!("KUNIT_CASE_{}", test.to_uppercase()); + let kunit_wrapper = format!( + "unsafe extern \"C\" fn {}(_test: *mut kernel::bindings::kunit) {{ {}(); }}", + kunit_wrapper_fn_name, test + ); + let kunit_case = format!( + "static mut {}: kernel::bindings::kunit_case = kernel::kunit_case!({}, {});", + kunit_case_name, test, kunit_wrapper_fn_name + ); + writeln!(kunit_macros, "{kunit_wrapper}").unwrap(); + writeln!(kunit_macros, "{kunit_case}").unwrap(); + writeln!(test_cases, "{kunit_case_name},").unwrap(); + } + + writeln!( + kunit_macros, + "static mut KUNIT_CASE_NULL: kernel::bindings::kunit_case = kernel::kunit_case!();" + ) + .unwrap(); + + writeln!( + kunit_macros, + "static mut TEST_CASES : &mut[kernel::bindings::kunit_case] = unsafe {{ &mut[{test_cases} KUNIT_CASE_NULL] }};" + ) + .unwrap(); + + writeln!( + kunit_macros, + "kernel::kunit_unsafe_test_suite!({attr}, TEST_CASES);" + ) + .unwrap(); + + let new_body: TokenStream = vec![body.stream(), kunit_macros.parse().unwrap()] + .into_iter() + .collect(); + + // Remove the `#[test]` macros. + let new_body = new_body.to_string().replace("#[test]", ""); + tokens.push(TokenTree::Group(Group::new( + Delimiter::Brace, + new_body.parse().unwrap(), + ))); + + tokens.into_iter().collect() +} diff --git a/rust/macros/lib.rs b/rust/macros/lib.rs index 3fc74cb4ea19..cd0b720514ff 100644 --- a/rust/macros/lib.rs +++ b/rust/macros/lib.rs @@ -6,6 +6,7 @@ mod quote; mod concat_idents; mod helpers; +mod kunit; mod module; mod pin_data; mod pinned_drop; @@ -246,3 +247,31 @@ pub fn pin_data(inner: TokenStream, item: TokenStream) -> TokenStream { pub fn pinned_drop(args: TokenStream, input: TokenStream) -> TokenStream { pinned_drop::pinned_drop(args, input) } + +/// Registers a KUnit test suite and its test cases using a user-space like syntax. +/// +/// This macro should be used on modules. If `CONFIG_KUNIT` (in `.config`) is `n`, the target module +/// is ignored. +/// +/// # Examples +/// +/// ```ignore +/// # use macros::kunit_tests; +/// +/// #[kunit_tests(kunit_test_suit_name)] +/// mod tests { +/// #[test] +/// fn foo() { +/// assert_eq!(1, 1); +/// } +/// +/// #[test] +/// fn bar() { +/// assert_eq!(2, 2); +/// } +/// } +/// ``` +#[proc_macro_attribute] +pub fn kunit_tests(attr: TokenStream, ts: TokenStream) -> TokenStream { + kunit::kunit_tests(attr, ts) +} From patchwork Thu Jul 20 06:38:54 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: David Gow X-Patchwork-Id: 123040 Return-Path: Delivered-To: ouuuleilei@gmail.com Received: by 2002:a59:c923:0:b0:3e4:2afc:c1 with SMTP id j3csp2938173vqt; Thu, 20 Jul 2023 00:14:33 -0700 (PDT) X-Google-Smtp-Source: APBJJlEkmXwtn64peDiR6ibJ1jL0601MKGN70nGk7Qw+WYNx6KHqhm6C+MwyCcdeKBAFngwk5ysm X-Received: by 2002:a17:902:d483:b0:1b8:b48a:5e7f with SMTP id c3-20020a170902d48300b001b8b48a5e7fmr7792524plg.25.1689837272940; Thu, 20 Jul 2023 00:14:32 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1689837272; cv=none; d=google.com; s=arc-20160816; b=wHaWVpQyI+eueUwJJLNYYy9K/j0AHhIGk37K0f3vS5j/EieyRj2HvUYAT3ec1j7uo3 Y5vFU+JZ+iyNQGFpSFWf9t/pbr/dy4efsZJS2Pa+SKO1HGpkxu1huqqw4Xv8NcHCTIZR 4t6gljzq6eVTaXaYsfM8jJmLp175AGymgxebe9HL4TyCJcXta2I22evvug4YJihmPOiq GnPnkZoKk33dfovdLbScOHdgO/ulEJcjaIWltQGXpvVxu6CpvI4MyWxiIjpNqoY0f8C5 y2Hyfj1htM9ojT9jCVbPDtxHFM672/7Cpkgbt7QE4RhTDWCEkKeDhoVk5jsIc3dfkxqb kCTw== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=list-id:precedence:content-transfer-encoding:cc:to:from:subject :message-id:references:mime-version:in-reply-to:date:dkim-signature; bh=i3Gim78LlW0ImU8Nf7KfkXlFKv0mM0hU/1p3btyr4nM=; fh=tt14ALlvSCOaeGq2WOiQl00uBX8L7oEnWR0UCRJ1Mu8=; b=Nt+vqhhTi3JxWXzSyLqvs5JGpcKCROf9RzBLn9uIdrROAdFMh4ptJ6qxgoafdSKXoz ZuSvJwcEyGPiSjBn4+R42n+DGFtKU1t8QKCSrHnFlXb8s/6Axp4jtSnzk4OCSm6Edwme /0PvnsmDATTv80s7kRkZu35th74V9+Vy9qCv6R3INU6w/pFpSCyXRwl2OhS62ztXxyhm AzuJc13jPf7IA8czQGADC4qb/HWXKYnKO5fiBpc97hPF1kH95RjCeoJ+LpF4FmmIHQbP yMm1gPOfJOIh1Tr17e4CoE5xPHvu0TpA7KO9aKWN93gU6+/2Kt6NyTdWubZwHNcWOKpA KNtA== ARC-Authentication-Results: i=1; mx.google.com; dkim=pass header.i=@google.com header.s=20221208 header.b=5f9C5eFX; spf=pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 2620:137:e000::1:20 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org; dmarc=pass (p=REJECT sp=REJECT dis=NONE) header.from=google.com Received: from out1.vger.email (out1.vger.email. [2620:137:e000::1:20]) by mx.google.com with ESMTP id t1-20020a1709027fc100b001b3d2717f7bsi373100plb.61.2023.07.20.00.14.19; Thu, 20 Jul 2023 00:14:32 -0700 (PDT) Received-SPF: pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 2620:137:e000::1:20 as permitted sender) client-ip=2620:137:e000::1:20; Authentication-Results: mx.google.com; dkim=pass header.i=@google.com header.s=20221208 header.b=5f9C5eFX; spf=pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 2620:137:e000::1:20 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org; dmarc=pass (p=REJECT sp=REJECT dis=NONE) header.from=google.com Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S229709AbjGTGlU (ORCPT + 99 others); Thu, 20 Jul 2023 02:41:20 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:48830 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S231327AbjGTGk5 (ORCPT ); Thu, 20 Jul 2023 02:40:57 -0400 Received: from mail-yb1-xb49.google.com (mail-yb1-xb49.google.com [IPv6:2607:f8b0:4864:20::b49]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 6324235A4 for ; Wed, 19 Jul 2023 23:40:22 -0700 (PDT) Received: by mail-yb1-xb49.google.com with SMTP id 3f1490d57ef6-cac213f9264so441083276.3 for ; Wed, 19 Jul 2023 23:40:22 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20221208; t=1689835209; x=1690440009; h=content-transfer-encoding:cc:to:from:subject:message-id:references :mime-version:in-reply-to:date:from:to:cc:subject:date:message-id :reply-to; bh=i3Gim78LlW0ImU8Nf7KfkXlFKv0mM0hU/1p3btyr4nM=; b=5f9C5eFXQp0hGJsDoxGCFRckc7Px3DcrknxoAjktifh5StDa+zVV6XWxRyWl4wpUPD 2biiL+iilf0AgDI5sxpf7W8VW2o0Ux3ZuggzoLmM7PLy8Z3I7GtxySWk8m0rZZelUNRE b5/PlSp8f59mHceQUG85M8pnRcVcV4A8ywL9iY1GwLH8GJZ7rvmTDJSt7644yc1aLCaA ZhyM3Rj69OEIFlI4947vSLvC36fFxWnjkMCqrdbw/aScWuf6p3TgnB15frSw0eE8uYlm /9lOVmJGVLAqX/jHl5ozL85NB1xCDQNkvCsYR2J83zbZ+G9ODeeKJ8pYFrPrxCvliuaF uocQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20221208; t=1689835209; x=1690440009; h=content-transfer-encoding:cc:to:from:subject:message-id:references :mime-version:in-reply-to:date:x-gm-message-state:from:to:cc:subject :date:message-id:reply-to; bh=i3Gim78LlW0ImU8Nf7KfkXlFKv0mM0hU/1p3btyr4nM=; b=ZrG3Jezux2g9G8fHaBkiJlVAAOBfiBQl+G773aliaX2dAm6NlzcoFO/C6Jc6IcRzMt UcIpD1ub2whNz9BaVNcaDiB+e0Mf+hHxGjGGq8WBIZv/g2hMIRFzX9FHrDCqVqcJtnMp 8hIZdK+ZyvML6SycqIRinB6kZtZof96Ejvr1CKx/cLsvU3fd2fWnhItxOWRPxtFHBS5E R7N4lyPTIKo10IZoaBH1xRMQwJgsU3oiI9gFFjxJh1oDaTGsfgsXKJUgJ0wY+2L0pA4m bEI8jkNCV0IAWsrUbKIuSkW+27n0W6AoExGnezDtvhUQwuq5BjR7pbW+pPcLW30OIIzr LWfw== X-Gm-Message-State: ABy/qLZ0bkXL8mXbbfcckVqMbn19+lBV9lAFhb5vUQ32B7uKwloFOVfG iYFyeNeMej6y2xEANN3hSw4I78K0cL4tnQ== X-Received: from slicestar.c.googlers.com ([fda3:e722:ac3:cc00:4f:4b78:c0a8:20a1]) (user=davidgow job=sendgmr) by 2002:a05:6902:1709:b0:ccf:6bfa:2a03 with SMTP id by9-20020a056902170900b00ccf6bfa2a03mr37240ybb.7.1689835209388; Wed, 19 Jul 2023 23:40:09 -0700 (PDT) Date: Thu, 20 Jul 2023 14:38:54 +0800 In-Reply-To: <20230720-rustbind-v1-0-c80db349e3b5@google.com> Mime-Version: 1.0 References: <20230720-rustbind-v1-0-c80db349e3b5@google.com> X-Mailer: b4 0.13-dev-099c9 Message-ID: <20230720-rustbind-v1-3-c80db349e3b5@google.com> Subject: [PATCH 3/3] rust: kunit: allow to know if we are in a test From: David Gow To: Brendan Higgins , Miguel Ojeda , Alex Gaynor , Wedson Almeida Filho , Boqun Feng , Gary Guo , Benno Lossin Cc: David Gow , " =?utf-8?b?Sm9zw6kgRXhww7NzaXRv?= " , " =?utf-8?q?Bj=C3=B6rn_Roy_Baron?= " , linux-kselftest@vger.kernel.org, kunit-dev@googlegroups.com, rust-for-linux@vger.kernel.org, linux-kernel@vger.kernel.org X-Spam-Status: No, score=-9.6 required=5.0 tests=BAYES_00,DKIMWL_WL_MED, DKIM_SIGNED,DKIM_VALID,DKIM_VALID_AU,DKIM_VALID_EF,RCVD_IN_DNSWL_NONE, SPF_HELO_NONE,SPF_PASS,T_SCC_BODY_TEXT_LINE,USER_IN_DEF_DKIM_WL autolearn=unavailable autolearn_force=no version=3.4.6 X-Spam-Checker-Version: SpamAssassin 3.4.6 (2021-04-09) on lindbergh.monkeyblade.net Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org X-getmail-retrieved-from-mailbox: INBOX X-GMAIL-THRID: 1771922808148204132 X-GMAIL-MSGID: 1771922808148204132 From: José Expósito In some cases, you need to call test-only code from outside the test case, for example, to mock a function or a module. In order to check whether we are in a test or not, we need to test if `CONFIG_KUNIT` is set. Unfortunately, we cannot rely only on this condition because some distros compile KUnit in production kernels, so checking at runtime that `current->kunit_test != NULL` is required. Note that the C function `kunit_get_current_test()` can not be used because it is not present in the current Rust tree yet. Once it is available we might want to change our Rust wrapper to use it. This patch adds a function to know whether we are in a KUnit test or not and examples showing how to mock a function and a module. Reviewed-by: David Gow Signed-off-by: José Expósito --- rust/kernel/kunit.rs | 78 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/rust/kernel/kunit.rs b/rust/kernel/kunit.rs index 44ea67028316..dcaac19bb108 100644 --- a/rust/kernel/kunit.rs +++ b/rust/kernel/kunit.rs @@ -40,6 +40,8 @@ pub fn info(args: fmt::Arguments<'_>) { } } +use crate::task::Task; +use core::ops::Deref; use macros::kunit_tests; /// Asserts that a boolean expression is `true` at runtime. @@ -256,11 +258,87 @@ macro_rules! kunit_unsafe_test_suite { }; } +/// In some cases, you need to call test-only code from outside the test case, for example, to +/// create a function mock. This function can be invoked to know whether we are currently running a +/// KUnit test or not. +/// +/// # Examples +/// +/// This example shows how a function can be mocked to return a well-known value while testing: +/// +/// ``` +/// # use kernel::kunit::in_kunit_test; +/// # +/// fn fn_mock_example(n: i32) -> i32 { +/// if in_kunit_test() { +/// 100 +/// } else { +/// n + 1 +/// } +/// } +/// +/// let mock_res = fn_mock_example(5); +/// assert_eq!(mock_res, 100); +/// ``` +/// +/// Sometimes, you don't control the code that needs to be mocked. This example shows how the +/// `bindings` module can be mocked: +/// +/// ``` +/// // Import our mock naming it as the real module. +/// #[cfg(CONFIG_KUNIT)] +/// use bindings_mock_example as bindings; +/// +/// // This module mocks `bindings`. +/// mod bindings_mock_example { +/// use kernel::kunit::in_kunit_test; +/// use kernel::bindings::u64_; +/// +/// // Make the other binding functions available. +/// pub(crate) use kernel::bindings::*; +/// +/// // Mock `ktime_get_boot_fast_ns` to return a well-known value when running a KUnit test. +/// pub(crate) unsafe fn ktime_get_boot_fast_ns() -> u64_ { +/// if in_kunit_test() { +/// 1234 +/// } else { +/// unsafe { kernel::bindings::ktime_get_boot_fast_ns() } +/// } +/// } +/// } +/// +/// // This is the function we want to test. Since `bindings` has been mocked, we can use its +/// // functions seamlessly. +/// fn get_boot_ns() -> u64 { +/// unsafe { bindings::ktime_get_boot_fast_ns() } +/// } +/// +/// let time = get_boot_ns(); +/// assert_eq!(time, 1234); +/// ``` +pub fn in_kunit_test() -> bool { + if cfg!(CONFIG_KUNIT) { + // SAFETY: By the type invariant, we know that `*Task::current().deref().0` is valid. + let test = unsafe { (*Task::current().deref().0.get()).kunit_test }; + !test.is_null() + } else { + false + } +} + #[kunit_tests(rust_kernel_kunit)] mod tests { + use super::*; + #[test] fn rust_test_kunit_kunit_tests() { let running = true; assert_eq!(running, true); } + + #[test] + fn rust_test_kunit_in_kunit_test() { + let in_kunit = in_kunit_test(); + assert_eq!(in_kunit, true); + } }