From patchwork Mon Jul 24 16:28:21 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Rae Moar X-Patchwork-Id: 705955 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id 5D0D4C00528 for ; Mon, 24 Jul 2023 16:30:33 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S230297AbjGXQab (ORCPT ); Mon, 24 Jul 2023 12:30:31 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:57022 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S230349AbjGXQaa (ORCPT ); Mon, 24 Jul 2023 12:30:30 -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 A850010F7 for ; Mon, 24 Jul 2023 09:30:24 -0700 (PDT) Received: by mail-yw1-x114a.google.com with SMTP id 00721157ae682-583f048985bso17042777b3.2 for ; Mon, 24 Jul 2023 09:30:24 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20221208; t=1690216223; x=1690821023; h=cc:to:from:subject:message-id:references:mime-version:in-reply-to :date:from:to:cc:subject:date:message-id:reply-to; bh=wZodKXjzwaMblNUGTJ/e9OGmsCh/NzKmrPm/sVrnOGs=; b=71lpv9k2cfcGlRhTHzZE0I5yOQDBURs4QcLpkoigzk0Tpu5h1Kc2REqVH5dKvlhv26 Y8gsPh/tyN5yV2i8TdZ8RmiuzgsXnkOWB/c8R56bwfPRLZLU49Is7zyIEmbzo1Tw2A9b M7MhENYCF9VPGZWeFcnd1eQLpOpToV/sxK9mYtXTtguX7mAMGbc9aYs6qMH0LWZYuZSY ztlccMBytAFtoq/Kc6gtoZ7UA4L3MMTo15BmgBhNNipKuF8+OTI0FWz6Aiw2gdWQmOkB DutDQfACAhu8RgXQ0+5tZqB4ehMawELJynLgn4LJgq1hA3eNuA4tIXMpE8Rqd7TqfNJS 0ijw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20221208; t=1690216223; x=1690821023; h=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=wZodKXjzwaMblNUGTJ/e9OGmsCh/NzKmrPm/sVrnOGs=; b=U9PG6XrrLaUnH14Drp47v1SymTTKS8vjg6Oh/qLQ1LScIy64DWUO0VZ0U2fodPbaou 6EHKvFKLJ2ZAEWDrq1S/bD+AsWSJ7WzGqSWVv/VS/QgQrAM8SiBPjk6Fx00Ppq0Xuck+ eumEfN/rl93PGLeMxZbstrKqG0Hr6yGZ4elGpkSuizd/4TpHrogXmRlUQd9TJwONeYgF PyMJ+kRH43XPeuHh+7r29CgKrWT9WuLXIFWdRbwSTVxZJ3cWVVviVxpFHXbElvkgLWE9 9TPis8ipFY+NcKtaX69cR/YF7fNGIwbNHA/vMVtBVSOphyfQae59gPl5RDj+B8KCvJf0 fVzQ== X-Gm-Message-State: ABy/qLYIbn5Brk0dv4DN48J+bD5WB+sF27JJOSmHXz5+kmnTzUBCoqHB TBm65byjsT+vf2mOqPNpHQjaO5vNcg== X-Google-Smtp-Source: APBJJlFtkay6ZMGiBUQ0ZKoSGhP5FfT/lTPH9qFCf5jCGSluiHm25kmByyWZt+etk/Vfbrhgjn77KQ8ZMA== X-Received: from rmoar-specialist.c.googlers.com ([fda3:e722:ac3:cc00:2b:7d90:c0a8:45d3]) (user=rmoar job=sendgmr) by 2002:a25:dc43:0:b0:d05:7ba4:67f9 with SMTP id y64-20020a25dc43000000b00d057ba467f9mr46506ybe.3.1690216223803; Mon, 24 Jul 2023 09:30:23 -0700 (PDT) Date: Mon, 24 Jul 2023 16:28:21 +0000 In-Reply-To: <20230724162834.1354164-1-rmoar@google.com> Mime-Version: 1.0 References: <20230724162834.1354164-1-rmoar@google.com> X-Mailer: git-send-email 2.41.0.487.g6d72f3e995-goog Message-ID: <20230724162834.1354164-2-rmoar@google.com> Subject: [PATCH v2 1/9] kunit: Add test attributes API structure From: Rae Moar To: shuah@kernel.org, davidgow@google.com, dlatypov@google.com, brendan.higgins@linux.dev Cc: linux-kselftest@vger.kernel.org, kunit-dev@googlegroups.com, linux-kernel@vger.kernel.org, keescook@chromium.org, linux-hardening@vger.kernel.org, jstultz@google.com, tglx@linutronix.de, sboyd@kernel.org, Rae Moar Precedence: bulk List-ID: X-Mailing-List: linux-kselftest@vger.kernel.org Add the basic structure of the test attribute API to KUnit, which can be used to save and access test associated data. Add attributes.c and attributes.h to hold associated structs and functions for the API. Create a struct that holds a variety of associated helper functions for each test attribute. These helper functions will be used to get the attribute value, convert the value to a string, and filter based on the value. This struct is flexible by design to allow for attributes of numerous types and contexts. Add a method to print test attributes in the format of "# [.]: ". Example for a suite: "# speed: slow" Example for a test case: "# test_case.speed: very_slow" Use this method to report attributes in the KTAP output (KTAP spec: https://docs.kernel.org/dev-tools/ktap.html) and _list_tests output when kernel's new kunit.action=list_attr option is used. Note this is derivative of the kunit.action=list option. In test.h, add fields and associated helper functions to test cases and suites to hold user-inputted test attributes. Signed-off-by: Rae Moar Reviewed-by: David Gow --- Changes since v1: - No major changes Changes since RFC v2: - No major changes Changes since RFC v1: - Add list_attr option to only include attribute in the _list_tests output when this module param is set - Add printing options for attributes to print always, print only for suites, or print never. include/kunit/attributes.h | 19 +++++++++ include/kunit/test.h | 33 ++++++++++++++++ lib/kunit/Makefile | 3 +- lib/kunit/attributes.c | 80 ++++++++++++++++++++++++++++++++++++++ lib/kunit/executor.c | 21 ++++++++-- lib/kunit/test.c | 17 ++++---- 6 files changed, 161 insertions(+), 12 deletions(-) create mode 100644 include/kunit/attributes.h create mode 100644 lib/kunit/attributes.c diff --git a/include/kunit/attributes.h b/include/kunit/attributes.h new file mode 100644 index 000000000000..9fcd184cce36 --- /dev/null +++ b/include/kunit/attributes.h @@ -0,0 +1,19 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * KUnit API to save and access test attributes + * + * Copyright (C) 2023, Google LLC. + * Author: Rae Moar + */ + +#ifndef _KUNIT_ATTRIBUTES_H +#define _KUNIT_ATTRIBUTES_H + +/* + * Print all test attributes for a test case or suite. + * Output format for test cases: "# .: " + * Output format for test suites: "# : " + */ +void kunit_print_attr(void *test_or_suite, bool is_test, unsigned int test_level); + +#endif /* _KUNIT_ATTRIBUTES_H */ diff --git a/include/kunit/test.h b/include/kunit/test.h index 23120d50499e..1fc9155988e9 100644 --- a/include/kunit/test.h +++ b/include/kunit/test.h @@ -63,12 +63,16 @@ enum kunit_status { KUNIT_SKIPPED, }; +/* Holds attributes for each test case and suite */ +struct kunit_attributes {}; + /** * struct kunit_case - represents an individual test case. * * @run_case: the function representing the actual test case. * @name: the name of the test case. * @generate_params: the generator function for parameterized tests. + * @attr: the attributes associated with the test * * A test case is a function with the signature, * ``void (*)(struct kunit *)`` @@ -104,6 +108,7 @@ struct kunit_case { void (*run_case)(struct kunit *test); const char *name; const void* (*generate_params)(const void *prev, char *desc); + struct kunit_attributes attr; /* private: internal use only. */ enum kunit_status status; @@ -133,6 +138,18 @@ static inline char *kunit_status_to_ok_not_ok(enum kunit_status status) */ #define KUNIT_CASE(test_name) { .run_case = test_name, .name = #test_name } +/** + * KUNIT_CASE_ATTR - A helper for creating a &struct kunit_case + * with attributes + * + * @test_name: a reference to a test case function. + * @attributes: a reference to a struct kunit_attributes object containing + * test attributes + */ +#define KUNIT_CASE_ATTR(test_name, attributes) \ + { .run_case = test_name, .name = #test_name, \ + .attr = attributes } + /** * KUNIT_CASE_PARAM - A helper for creation a parameterized &struct kunit_case * @@ -154,6 +171,20 @@ static inline char *kunit_status_to_ok_not_ok(enum kunit_status status) { .run_case = test_name, .name = #test_name, \ .generate_params = gen_params } +/** + * KUNIT_CASE_PARAM_ATTR - A helper for creating a parameterized &struct + * kunit_case with attributes + * + * @test_name: a reference to a test case function. + * @gen_params: a reference to a parameter generator function. + * @attributes: a reference to a struct kunit_attributes object containing + * test attributes + */ +#define KUNIT_CASE_PARAM_ATTR(test_name, gen_params, attributes) \ + { .run_case = test_name, .name = #test_name, \ + .generate_params = gen_params, \ + .attr = attributes } + /** * struct kunit_suite - describes a related collection of &struct kunit_case * @@ -163,6 +194,7 @@ static inline char *kunit_status_to_ok_not_ok(enum kunit_status status) * @init: called before every test case. * @exit: called after every test case. * @test_cases: a null terminated array of test cases. + * @attr: the attributes associated with the test suite * * A kunit_suite is a collection of related &struct kunit_case s, such that * @init is called before every test case and @exit is called after every @@ -182,6 +214,7 @@ struct kunit_suite { int (*init)(struct kunit *test); void (*exit)(struct kunit *test); struct kunit_case *test_cases; + struct kunit_attributes attr; /* private: internal use only */ char status_comment[KUNIT_STATUS_COMMENT_SIZE]; diff --git a/lib/kunit/Makefile b/lib/kunit/Makefile index cb417f504996..46f75f23dfe4 100644 --- a/lib/kunit/Makefile +++ b/lib/kunit/Makefile @@ -6,7 +6,8 @@ kunit-objs += test.o \ string-stream.o \ assert.o \ try-catch.o \ - executor.o + executor.o \ + attributes.o ifeq ($(CONFIG_KUNIT_DEBUGFS),y) kunit-objs += debugfs.o diff --git a/lib/kunit/attributes.c b/lib/kunit/attributes.c new file mode 100644 index 000000000000..9bda5a5f4030 --- /dev/null +++ b/lib/kunit/attributes.c @@ -0,0 +1,80 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * KUnit API to save and access test attributes + * + * Copyright (C) 2023, Google LLC. + * Author: Rae Moar + */ + +#include +#include + +/* Options for printing attributes: + * PRINT_ALWAYS - attribute is printed for every test case and suite if set + * PRINT_SUITE - attribute is printed for every suite if set but not for test cases + * PRINT_NEVER - attribute is never printed + */ +enum print_ops { + PRINT_ALWAYS, + PRINT_SUITE, + PRINT_NEVER, +}; + +/** + * struct kunit_attr - represents a test attribute and holds flexible + * helper functions to interact with attribute. + * + * @name: name of test attribute, eg. speed + * @get_attr: function to return attribute value given a test + * @to_string: function to return string representation of given + * attribute value + * @filter: function to indicate whether a given attribute value passes a + * filter + */ +struct kunit_attr { + const char *name; + void *(*get_attr)(void *test_or_suite, bool is_test); + const char *(*to_string)(void *attr, bool *to_free); + int (*filter)(void *attr, const char *input, int *err); + void *attr_default; + enum print_ops print; +}; + +/* List of all Test Attributes */ + +static struct kunit_attr kunit_attr_list[] = {}; + +/* Helper Functions to Access Attributes */ + +void kunit_print_attr(void *test_or_suite, bool is_test, unsigned int test_level) +{ + int i; + bool to_free; + void *attr; + const char *attr_name, *attr_str; + struct kunit_suite *suite = is_test ? NULL : test_or_suite; + struct kunit_case *test = is_test ? test_or_suite : NULL; + + for (i = 0; i < ARRAY_SIZE(kunit_attr_list); i++) { + if (kunit_attr_list[i].print == PRINT_NEVER || + (test && kunit_attr_list[i].print == PRINT_SUITE)) + continue; + attr = kunit_attr_list[i].get_attr(test_or_suite, is_test); + if (attr) { + attr_name = kunit_attr_list[i].name; + attr_str = kunit_attr_list[i].to_string(attr, &to_free); + if (test) { + kunit_log(KERN_INFO, test, "%*s# %s.%s: %s", + KUNIT_INDENT_LEN * test_level, "", test->name, + attr_name, attr_str); + } else { + kunit_log(KERN_INFO, suite, "%*s# %s: %s", + KUNIT_INDENT_LEN * test_level, "", attr_name, attr_str); + } + + /* Free to_string of attribute if needed */ + if (to_free) + kfree(attr_str); + } + } +} diff --git a/lib/kunit/executor.c b/lib/kunit/executor.c index 74982b83707c..12e38a48a5cc 100644 --- a/lib/kunit/executor.c +++ b/lib/kunit/executor.c @@ -2,6 +2,7 @@ #include #include +#include #include #include @@ -24,7 +25,8 @@ module_param_named(action, action_param, charp, 0); MODULE_PARM_DESC(action, "Changes KUnit executor behavior, valid values are:\n" ": run the tests like normal\n" - "'list' to list test names instead of running them.\n"); + "'list' to list test names instead of running them.\n" + "'list_attr' to list test names and attributes instead of running them.\n"); /* glob_match() needs NULL terminated strings, so we need a copy of filter_glob_param. */ struct kunit_test_filter { @@ -172,7 +174,7 @@ static void kunit_exec_run_tests(struct suite_set *suite_set) __kunit_test_suites_init(suite_set->start, num_suites); } -static void kunit_exec_list_tests(struct suite_set *suite_set) +static void kunit_exec_list_tests(struct suite_set *suite_set, bool include_attr) { struct kunit_suite * const *suites; struct kunit_case *test_case; @@ -180,10 +182,19 @@ static void kunit_exec_list_tests(struct suite_set *suite_set) /* Hack: print a ktap header so kunit.py can find the start of KUnit output. */ pr_info("KTAP version 1\n"); - for (suites = suite_set->start; suites < suite_set->end; suites++) + for (suites = suite_set->start; suites < suite_set->end; suites++) { + /* Print suite name and suite attributes */ + pr_info("%s\n", (*suites)->name); + if (include_attr) + kunit_print_attr((void *)(*suites), false, 0); + + /* Print test case name and attributes in suite */ kunit_suite_for_each_test_case((*suites), test_case) { pr_info("%s.%s\n", (*suites)->name, test_case->name); + if (include_attr) + kunit_print_attr((void *)test_case, true, 0); } + } } int kunit_run_all_tests(void) @@ -206,7 +217,9 @@ int kunit_run_all_tests(void) if (!action_param) kunit_exec_run_tests(&suite_set); else if (strcmp(action_param, "list") == 0) - kunit_exec_list_tests(&suite_set); + kunit_exec_list_tests(&suite_set, false); + else if (strcmp(action_param, "list_attr") == 0) + kunit_exec_list_tests(&suite_set, true); else pr_err("kunit executor: unknown action '%s'\n", action_param); diff --git a/lib/kunit/test.c b/lib/kunit/test.c index 84e4666555c9..9ee55139ecd1 100644 --- a/lib/kunit/test.c +++ b/lib/kunit/test.c @@ -9,6 +9,7 @@ #include #include #include +#include #include #include #include @@ -168,6 +169,13 @@ size_t kunit_suite_num_test_cases(struct kunit_suite *suite) } EXPORT_SYMBOL_GPL(kunit_suite_num_test_cases); +/* Currently supported test levels */ +enum { + KUNIT_LEVEL_SUITE = 0, + KUNIT_LEVEL_CASE, + KUNIT_LEVEL_CASE_PARAM, +}; + static void kunit_print_suite_start(struct kunit_suite *suite) { /* @@ -181,17 +189,11 @@ static void kunit_print_suite_start(struct kunit_suite *suite) pr_info(KUNIT_SUBTEST_INDENT "KTAP version 1\n"); pr_info(KUNIT_SUBTEST_INDENT "# Subtest: %s\n", suite->name); + kunit_print_attr((void *)suite, false, KUNIT_LEVEL_CASE); pr_info(KUNIT_SUBTEST_INDENT "1..%zd\n", kunit_suite_num_test_cases(suite)); } -/* Currently supported test levels */ -enum { - KUNIT_LEVEL_SUITE = 0, - KUNIT_LEVEL_CASE, - KUNIT_LEVEL_CASE_PARAM, -}; - static void kunit_print_ok_not_ok(struct kunit *test, unsigned int test_level, enum kunit_status status, @@ -651,6 +653,7 @@ int kunit_run_tests(struct kunit_suite *suite) } } + kunit_print_attr((void *)test_case, true, KUNIT_LEVEL_CASE); kunit_print_test_stats(&test, param_stats); From patchwork Mon Jul 24 16:28:23 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Rae Moar X-Patchwork-Id: 705954 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id 3040EC001DF for ; Mon, 24 Jul 2023 16:30:51 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S229903AbjGXQau (ORCPT ); Mon, 24 Jul 2023 12:30:50 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:57118 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S231256AbjGXQap (ORCPT ); Mon, 24 Jul 2023 12:30:45 -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 D6C23173D for ; Mon, 24 Jul 2023 09:30:34 -0700 (PDT) Received: by mail-yw1-x1149.google.com with SMTP id 00721157ae682-5734d919156so45345537b3.3 for ; Mon, 24 Jul 2023 09:30:34 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20221208; t=1690216234; x=1690821034; h=cc:to:from:subject:message-id:references:mime-version:in-reply-to :date:from:to:cc:subject:date:message-id:reply-to; bh=xYclt7C8Cnec7nSlcn3YbnEz7lr6gerZGctKIdSjFiE=; b=1I4X670hGtqj2vuZjqjsOA9suQ+K7kLRDustPlq47ad6feZhkPgLsWG/gGcrGZU8UX dP63nlUVzZaTlqpa0O8NyLPc+6V3V1sXoKR5ropA1Fi/Q8t42/t6MtqpUgFkgVzSHq9m ZRV4pa/SlPjVFA7xl0g2R2NJu01q7rFVqcCGcA722a6puSneVbph3xmfo6VLq7EoxqRk Q5b3B2lkbA8xp3l/8+B/2g9Zm5GPMtmH1F/AXzJMONhRSOLxkJf47SQImknfPhIURf+x QHMHq+FYu1TeF3+76V+RvbVprY8T4e5GaP6mD/Uq5+XFhBg4U+2/GFVIomWbDnSsMWe2 Vq5w== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20221208; t=1690216234; x=1690821034; h=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=xYclt7C8Cnec7nSlcn3YbnEz7lr6gerZGctKIdSjFiE=; b=Lf7mL1wjBKZqXvYKbTniE2cbpQlNBA0A+kNUdtrk0QBOO4GKtTYywzJcq3Enl4g+mu Utb+/p06bxUnkCbXOE7Fwg5hxCyVEZ5uHPtHb3M6T6oozi/yaQKzOBZwEG1+/jaYLDoL VC0/hGQSwu7cfbkVsIBUPzmUeNa+wMwBjCQN4jukwPzD1FG7q2OyamTPyje82eYspMgq nautL+YaropK0sKg2tRWCLqDJqGvlcfko039YWArNlCamZ1UTOYIFd6h+XBzITx94O8G aRS5JI3v1jj7rix10+JwfjzWda51dVDXgTHvhfRlWyaNe0rsQzu46DYnqFfa0gbiHVNl 2RgA== X-Gm-Message-State: ABy/qLYYQH7dUDzwxqMscv47OESTwS2/JpTj6JfsBhpq1CR6uvFN07eK 96atd7kaRWDGlEwejsYolab+tc2hZw== X-Google-Smtp-Source: APBJJlE9G1n4GQXKKGZG15FX+Izzel3QORJOc3SWOJJiOmoCxmhEzIMFpxKleTlCyTxqtNw2MS8wOQAzrw== X-Received: from rmoar-specialist.c.googlers.com ([fda3:e722:ac3:cc00:2b:7d90:c0a8:45d3]) (user=rmoar job=sendgmr) by 2002:a81:ad55:0:b0:583:6f04:4267 with SMTP id l21-20020a81ad55000000b005836f044267mr67658ywk.8.1690216234064; Mon, 24 Jul 2023 09:30:34 -0700 (PDT) Date: Mon, 24 Jul 2023 16:28:23 +0000 In-Reply-To: <20230724162834.1354164-1-rmoar@google.com> Mime-Version: 1.0 References: <20230724162834.1354164-1-rmoar@google.com> X-Mailer: git-send-email 2.41.0.487.g6d72f3e995-goog Message-ID: <20230724162834.1354164-4-rmoar@google.com> Subject: [PATCH v2 3/9] kunit: Add module attribute From: Rae Moar To: shuah@kernel.org, davidgow@google.com, dlatypov@google.com, brendan.higgins@linux.dev Cc: linux-kselftest@vger.kernel.org, kunit-dev@googlegroups.com, linux-kernel@vger.kernel.org, keescook@chromium.org, linux-hardening@vger.kernel.org, jstultz@google.com, tglx@linutronix.de, sboyd@kernel.org, Rae Moar Precedence: bulk List-ID: X-Mailing-List: linux-kselftest@vger.kernel.org Add module attribute to the test attribute API. This attribute stores the module name associated with the test using KBUILD_MODNAME. The name of a test suite and the module name often do not match. A reference to the module name associated with the suite could be extremely helpful in running tests as modules without needing to check the codebase. This attribute will be printed for each suite. Reviewed-by: David Gow Signed-off-by: Rae Moar --- Changes since v1: - Change kunit_attr_list definition to fix compile error Changes since RFC v2: - No changes. Changes: since RFC v1: - This is a new patch. include/kunit/test.h | 13 ++++++++----- lib/kunit/attributes.c | 25 +++++++++++++++++++++++++ 2 files changed, 33 insertions(+), 5 deletions(-) diff --git a/include/kunit/test.h b/include/kunit/test.h index ed5f5000a095..011e0d6bb506 100644 --- a/include/kunit/test.h +++ b/include/kunit/test.h @@ -131,6 +131,7 @@ struct kunit_case { /* private: internal use only. */ enum kunit_status status; + char *module_name; char *log; }; @@ -155,7 +156,9 @@ static inline char *kunit_status_to_ok_not_ok(enum kunit_status status) * &struct kunit_case object from it. See the documentation for * &struct kunit_case for an example on how to use it. */ -#define KUNIT_CASE(test_name) { .run_case = test_name, .name = #test_name } +#define KUNIT_CASE(test_name) \ + { .run_case = test_name, .name = #test_name, \ + .module_name = KBUILD_MODNAME} /** * KUNIT_CASE_ATTR - A helper for creating a &struct kunit_case @@ -167,7 +170,7 @@ static inline char *kunit_status_to_ok_not_ok(enum kunit_status status) */ #define KUNIT_CASE_ATTR(test_name, attributes) \ { .run_case = test_name, .name = #test_name, \ - .attr = attributes } + .attr = attributes, .module_name = KBUILD_MODNAME} /** * KUNIT_CASE_SLOW - A helper for creating a &struct kunit_case @@ -178,7 +181,7 @@ static inline char *kunit_status_to_ok_not_ok(enum kunit_status status) #define KUNIT_CASE_SLOW(test_name) \ { .run_case = test_name, .name = #test_name, \ - .attr.speed = KUNIT_SPEED_SLOW } + .attr.speed = KUNIT_SPEED_SLOW, .module_name = KBUILD_MODNAME} /** * KUNIT_CASE_PARAM - A helper for creation a parameterized &struct kunit_case @@ -199,7 +202,7 @@ static inline char *kunit_status_to_ok_not_ok(enum kunit_status status) */ #define KUNIT_CASE_PARAM(test_name, gen_params) \ { .run_case = test_name, .name = #test_name, \ - .generate_params = gen_params } + .generate_params = gen_params, .module_name = KBUILD_MODNAME} /** * KUNIT_CASE_PARAM_ATTR - A helper for creating a parameterized &struct @@ -213,7 +216,7 @@ static inline char *kunit_status_to_ok_not_ok(enum kunit_status status) #define KUNIT_CASE_PARAM_ATTR(test_name, gen_params, attributes) \ { .run_case = test_name, .name = #test_name, \ .generate_params = gen_params, \ - .attr = attributes } + .attr = attributes, .module_name = KBUILD_MODNAME} /** * struct kunit_suite - describes a related collection of &struct kunit_case diff --git a/lib/kunit/attributes.c b/lib/kunit/attributes.c index ffd0d692b334..9dce4f4d726c 100644 --- a/lib/kunit/attributes.c +++ b/lib/kunit/attributes.c @@ -61,6 +61,12 @@ static const char *attr_speed_to_string(void *attr, bool *to_free) return attr_enum_to_string(attr, speed_str_list, to_free); } +static const char *attr_string_to_string(void *attr, bool *to_free) +{ + *to_free = false; + return (char *) attr; +} + /* Get Attribute Methods */ static void *attr_speed_get(void *test_or_suite, bool is_test) @@ -74,6 +80,18 @@ static void *attr_speed_get(void *test_or_suite, bool is_test) return ((void *) suite->attr.speed); } +static void *attr_module_get(void *test_or_suite, bool is_test) +{ + struct kunit_suite *suite = is_test ? NULL : test_or_suite; + struct kunit_case *test = is_test ? test_or_suite : NULL; + + // Suites get their module attribute from their first test_case + if (test) + return ((void *) test->module_name); + else + return ((void *) suite->test_cases[0].module_name); +} + /* List of all Test Attributes */ static struct kunit_attr kunit_attr_list[] = { @@ -84,6 +102,13 @@ static struct kunit_attr kunit_attr_list[] = { .attr_default = (void *)KUNIT_SPEED_NORMAL, .print = PRINT_ALWAYS, }, + { + .name = "module", + .get_attr = attr_module_get, + .to_string = attr_string_to_string, + .attr_default = (void *)"", + .print = PRINT_SUITE, + } }; /* Helper Functions to Access Attributes */ From patchwork Mon Jul 24 16:28:25 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: Rae Moar X-Patchwork-Id: 705953 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id 6A90FC0015E for ; Mon, 24 Jul 2023 16:31:11 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S231292AbjGXQbK (ORCPT ); Mon, 24 Jul 2023 12:31:10 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:57240 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S231567AbjGXQbB (ORCPT ); Mon, 24 Jul 2023 12:31:01 -0400 Received: from mail-yb1-xb4a.google.com (mail-yb1-xb4a.google.com [IPv6:2607:f8b0:4864:20::b4a]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 2B0B210FE for ; Mon, 24 Jul 2023 09:30:46 -0700 (PDT) Received: by mail-yb1-xb4a.google.com with SMTP id 3f1490d57ef6-d052f49702dso3850738276.3 for ; Mon, 24 Jul 2023 09:30:46 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20221208; t=1690216245; x=1690821045; 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=jUmS3FOe7YJqZHhJU3CAEjB98NcP2nuXrsbdapCKpa8=; b=vMUWoMFiggHwFILIAX4klNC2qBSqawpfOh1e0hwb03kTtZVDQMG98jJp4GLoppHZbN ASDz5cAgtMjJCQ6RSiqKB+BS+z/4DMLrR7D4P7rnBz2bsegIgXUhULGef0FkCo9d40k3 yFkF8zuB/rmc9a4F2HE/TEhcUZkOU/S9FCKFVG6JMwadWEGWZNXoOqQgAN6qhgzUCHjV 3X6+Bx2JRydwiPiX6SlKMKAJZxDlrOcHayvlCLXcM4mnZS9KYg0kXuq3SLh8vhDMQ7Jv EHKbTG4q77n5JplW9oY3Z+qV9kT6rzwuKtKtKVPJz4Zdhim1sxhwGvcqJlqDIzBLoAbY OOng== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20221208; t=1690216245; x=1690821045; 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=jUmS3FOe7YJqZHhJU3CAEjB98NcP2nuXrsbdapCKpa8=; b=Jy3pLMwStTXrznk4RMnQ7iR+WrdBLwOR2dR9ABMrbsncUUOvx5ZD4B7UefCSPNfPmz FYnNxGf3r6z4cJjGFTby8VHRoBoGay1w2u0rtapG7lwkq2Ak02vtsAdZghKL+28JKpLN gNBF+Yzffx9KL3Bev8nQTBnCDm91i3+IzBHpqZJhzsAXYRSSm41IYLvGHkBfn4ef8QuY xEfUNQMhhB5DVLarNTNzsz/Y2OTAY6H4PE5qmirNrPTCuquAiP/Dky7++n+tk53QCWif Kk73VQBAITlo7ZhFJ/MfTdqoVRYw5O+gPsQnBy3FP/DAUlV719JIalo5tXtJcs33MzM8 bw6A== X-Gm-Message-State: ABy/qLYa2VPxEikhweBdL+KMx6gbQ2rZjU3ABAeuzG8gvBRAzAiivSXy HaMPb126sE+vU/y+tjv3fna8Ca02Uw== X-Google-Smtp-Source: APBJJlGrlyoNoSc7XXQyYqMXwqNdGPpDx/uuGeV/u5xqSUMA1sUV085Plswyd3xYeP5P1S9DrO1Pt5TZMw== X-Received: from rmoar-specialist.c.googlers.com ([fda3:e722:ac3:cc00:2b:7d90:c0a8:45d3]) (user=rmoar job=sendgmr) by 2002:a25:46d4:0:b0:c00:3434:f639 with SMTP id t203-20020a2546d4000000b00c003434f639mr59909yba.1.1690216245263; Mon, 24 Jul 2023 09:30:45 -0700 (PDT) Date: Mon, 24 Jul 2023 16:28:25 +0000 In-Reply-To: <20230724162834.1354164-1-rmoar@google.com> Mime-Version: 1.0 References: <20230724162834.1354164-1-rmoar@google.com> X-Mailer: git-send-email 2.41.0.487.g6d72f3e995-goog Message-ID: <20230724162834.1354164-6-rmoar@google.com> Subject: [PATCH v2 5/9] kunit: tool: Add command line interface to filter and report attributes From: Rae Moar To: shuah@kernel.org, davidgow@google.com, dlatypov@google.com, brendan.higgins@linux.dev Cc: linux-kselftest@vger.kernel.org, kunit-dev@googlegroups.com, linux-kernel@vger.kernel.org, keescook@chromium.org, linux-hardening@vger.kernel.org, jstultz@google.com, tglx@linutronix.de, sboyd@kernel.org, Rae Moar Precedence: bulk List-ID: X-Mailing-List: linux-kselftest@vger.kernel.org Add ability to kunit.py to filter attributes and report a list of tests including attributes without running tests. Add flag "--filter" to input filters on test attributes. Tests will be filtered out if they do not match all inputted filters. Example: --filter speed=slow (This filter would run only the tests that are marked as slow) Filters have operations: <, >, <=, >=, !=, and =. But note that the characters < and > are often interpreted by the shell, so they may need to be quoted or escaped. Example: --filter "speed>slow" or --filter speed\>slow (This filter would run only the tests that have the speed faster than slow. Additionally, multiple filters can be used. Example: --filter "speed=slow, module!=example" (This filter would run only the tests that have the speed slow and are not in the "example" module) Note if the user wants to skip filtered tests instead of not running/showing them use the "--filter_action=skip" flag. Expose the output of kunit.action=list option with flag "--list_tests" to output a list of tests. Additionally, add flag "--list_tests_attr" to output a list of tests and their attributes. These flags are useful to see tests and test attributes without needing to run tests. Example of the output of "--list_tests_attr": example example.test_1 example.test_2 # example.test_2.speed: slow This output includes a suite, example, with two test cases, test_1 and test_2. And in this instance test_2 has been marked as slow. Signed-off-by: Rae Moar --- Changes since v1: - No major changes Changes since RFC v2: - Remove --filter_skip flag and replace with –-filter_action - Make KUnit executor errors visible in kunit.py and raw_output - Fix up help comments Changes since RFC v1: - Change method for inputting filters to allow for spaces in filtering values - Add option to skip filtered tests instead of not run or show them with the -–filter_skip flag - Separate the new feature to list tests and their attributes into both –-list_tests (lists just tests) and –-list_tests_attr (lists all) tools/testing/kunit/kunit.py | 70 ++++++++++++++++++++++++-- tools/testing/kunit/kunit_kernel.py | 8 ++- tools/testing/kunit/kunit_parser.py | 11 +++- tools/testing/kunit/kunit_tool_test.py | 39 +++++++------- 4 files changed, 99 insertions(+), 29 deletions(-) diff --git a/tools/testing/kunit/kunit.py b/tools/testing/kunit/kunit.py index 3905c43369c3..bc74088c458a 100755 --- a/tools/testing/kunit/kunit.py +++ b/tools/testing/kunit/kunit.py @@ -55,8 +55,12 @@ class KunitExecRequest(KunitParseRequest): build_dir: str timeout: int filter_glob: str + filter: str + filter_action: Optional[str] kernel_args: Optional[List[str]] run_isolated: Optional[str] + list_tests: bool + list_tests_attr: bool @dataclass class KunitRequest(KunitExecRequest, KunitBuildRequest): @@ -102,19 +106,41 @@ def config_and_build_tests(linux: kunit_kernel.LinuxSourceTree, def _list_tests(linux: kunit_kernel.LinuxSourceTree, request: KunitExecRequest) -> List[str]: args = ['kunit.action=list'] + + if request.kernel_args: + args.extend(request.kernel_args) + + output = linux.run_kernel(args=args, + timeout=request.timeout, + filter_glob=request.filter_glob, + filter=request.filter, + filter_action=request.filter_action, + build_dir=request.build_dir) + lines = kunit_parser.extract_tap_lines(output) + # Hack! Drop the dummy TAP version header that the executor prints out. + lines.pop() + + # Filter out any extraneous non-test output that might have gotten mixed in. + return [l for l in output if re.match(r'^[^\s.]+\.[^\s.]+$', l)] + +def _list_tests_attr(linux: kunit_kernel.LinuxSourceTree, request: KunitExecRequest) -> Iterable[str]: + args = ['kunit.action=list_attr'] + if request.kernel_args: args.extend(request.kernel_args) output = linux.run_kernel(args=args, timeout=request.timeout, filter_glob=request.filter_glob, + filter=request.filter, + filter_action=request.filter_action, build_dir=request.build_dir) lines = kunit_parser.extract_tap_lines(output) # Hack! Drop the dummy TAP version header that the executor prints out. lines.pop() # Filter out any extraneous non-test output that might have gotten mixed in. - return [l for l in lines if re.match(r'^[^\s.]+\.[^\s.]+$', l)] + return lines def _suites_from_test_list(tests: List[str]) -> List[str]: """Extracts all the suites from an ordered list of tests.""" @@ -128,10 +154,18 @@ def _suites_from_test_list(tests: List[str]) -> List[str]: suites.append(suite) return suites - - def exec_tests(linux: kunit_kernel.LinuxSourceTree, request: KunitExecRequest) -> KunitResult: filter_globs = [request.filter_glob] + if request.list_tests: + output = _list_tests(linux, request) + for line in output: + print(line.rstrip()) + return KunitResult(status=KunitStatus.SUCCESS, elapsed_time=0.0) + if request.list_tests_attr: + attr_output = _list_tests_attr(linux, request) + for line in attr_output: + print(line.rstrip()) + return KunitResult(status=KunitStatus.SUCCESS, elapsed_time=0.0) if request.run_isolated: tests = _list_tests(linux, request) if request.run_isolated == 'test': @@ -155,6 +189,8 @@ def exec_tests(linux: kunit_kernel.LinuxSourceTree, request: KunitExecRequest) - args=request.kernel_args, timeout=request.timeout, filter_glob=filter_glob, + filter=request.filter, + filter_action=request.filter_action, build_dir=request.build_dir) _, test_result = parse_tests(request, metadata, run_result) @@ -341,6 +377,16 @@ def add_exec_opts(parser: argparse.ArgumentParser) -> None: nargs='?', default='', metavar='filter_glob') + parser.add_argument('--filter', + help='Filter KUnit tests with attributes, ' + 'e.g. module=example or speed>slow', + type=str, + default='') + parser.add_argument('--filter_action', + help='If set to skip, filtered tests will be skipped, ' + 'e.g. --filter_action=skip. Otherwise they will not run.', + type=str, + choices=['skip']) parser.add_argument('--kernel_args', help='Kernel command-line parameters. Maybe be repeated', action='append', metavar='') @@ -350,6 +396,12 @@ def add_exec_opts(parser: argparse.ArgumentParser) -> None: 'what ran before it.', type=str, choices=['suite', 'test']) + parser.add_argument('--list_tests', help='If set, list all tests that will be ' + 'run.', + action='store_true') + parser.add_argument('--list_tests_attr', help='If set, list all tests and test ' + 'attributes.', + action='store_true') def add_parse_opts(parser: argparse.ArgumentParser) -> None: parser.add_argument('--raw_output', help='If set don\'t parse output from kernel. ' @@ -398,8 +450,12 @@ def run_handler(cli_args: argparse.Namespace) -> None: json=cli_args.json, timeout=cli_args.timeout, filter_glob=cli_args.filter_glob, + filter=cli_args.filter, + filter_action=cli_args.filter_action, kernel_args=cli_args.kernel_args, - run_isolated=cli_args.run_isolated) + run_isolated=cli_args.run_isolated, + list_tests=cli_args.list_tests, + list_tests_attr=cli_args.list_tests_attr) result = run_tests(linux, request) if result.status != KunitStatus.SUCCESS: sys.exit(1) @@ -441,8 +497,12 @@ def exec_handler(cli_args: argparse.Namespace) -> None: json=cli_args.json, timeout=cli_args.timeout, filter_glob=cli_args.filter_glob, + filter=cli_args.filter, + filter_action=cli_args.filter_action, kernel_args=cli_args.kernel_args, - run_isolated=cli_args.run_isolated) + run_isolated=cli_args.run_isolated, + list_tests=cli_args.list_tests, + list_tests_attr=cli_args.list_tests_attr) result = exec_tests(linux, exec_request) stdout.print_with_timestamp(( 'Elapsed time: %.3fs\n') % (result.elapsed_time)) diff --git a/tools/testing/kunit/kunit_kernel.py b/tools/testing/kunit/kunit_kernel.py index 7f648802caf6..0b6488efed47 100644 --- a/tools/testing/kunit/kunit_kernel.py +++ b/tools/testing/kunit/kunit_kernel.py @@ -330,11 +330,15 @@ class LinuxSourceTree: return False return self.validate_config(build_dir) - def run_kernel(self, args: Optional[List[str]]=None, build_dir: str='', filter_glob: str='', timeout: Optional[int]=None) -> Iterator[str]: + def run_kernel(self, args: Optional[List[str]]=None, build_dir: str='', filter_glob: str='', filter: str='', filter_action: Optional[str]=None, timeout: Optional[int]=None) -> Iterator[str]: if not args: args = [] if filter_glob: - args.append('kunit.filter_glob='+filter_glob) + args.append('kunit.filter_glob=' + filter_glob) + if filter: + args.append('kunit.filter="' + filter + '"') + if filter_action: + args.append('kunit.filter_action=' + filter_action) args.append('kunit.enable=1') process = self._ops.start(args, build_dir) diff --git a/tools/testing/kunit/kunit_parser.py b/tools/testing/kunit/kunit_parser.py index fbc094f0567e..79d8832c862a 100644 --- a/tools/testing/kunit/kunit_parser.py +++ b/tools/testing/kunit/kunit_parser.py @@ -212,6 +212,7 @@ KTAP_START = re.compile(r'\s*KTAP version ([0-9]+)$') TAP_START = re.compile(r'\s*TAP version ([0-9]+)$') KTAP_END = re.compile(r'\s*(List of all partitions:|' 'Kernel panic - not syncing: VFS:|reboot: System halted)') +EXECUTOR_ERROR = re.compile(r'\s*kunit executor: (.*)$') def extract_tap_lines(kernel_output: Iterable[str]) -> LineStream: """Extracts KTAP lines from the kernel output.""" @@ -242,6 +243,8 @@ def extract_tap_lines(kernel_output: Iterable[str]) -> LineStream: # remove the prefix, if any. line = line[prefix_len:] yield line_num, line + elif EXECUTOR_ERROR.search(line): + yield line_num, line return LineStream(lines=isolate_ktap_output(kernel_output)) KTAP_VERSIONS = [1] @@ -447,7 +450,7 @@ def parse_diagnostic(lines: LineStream) -> List[str]: Log of diagnostic lines """ log = [] # type: List[str] - non_diagnostic_lines = [TEST_RESULT, TEST_HEADER, KTAP_START] + non_diagnostic_lines = [TEST_RESULT, TEST_HEADER, KTAP_START, TAP_START] while lines and not any(re.match(lines.peek()) for re in non_diagnostic_lines): log.append(lines.pop()) @@ -713,6 +716,11 @@ def parse_test(lines: LineStream, expected_num: int, log: List[str], is_subtest: """ test = Test() test.log.extend(log) + + # Parse any errors prior to parsing tests + err_log = parse_diagnostic(lines) + test.log.extend(err_log) + if not is_subtest: # If parsing the main/top-level test, parse KTAP version line and # test plan @@ -774,6 +782,7 @@ def parse_test(lines: LineStream, expected_num: int, log: List[str], is_subtest: # Don't override a bad status if this test had one reported. # Assumption: no subtests means CRASHED is from Test.__init__() if test.status in (TestStatus.TEST_CRASHED, TestStatus.SUCCESS): + print_log(test.log) test.status = TestStatus.NO_TESTS test.add_error('0 tests run!') diff --git a/tools/testing/kunit/kunit_tool_test.py b/tools/testing/kunit/kunit_tool_test.py index be35999bb84f..b28c1510be2e 100755 --- a/tools/testing/kunit/kunit_tool_test.py +++ b/tools/testing/kunit/kunit_tool_test.py @@ -597,7 +597,7 @@ class KUnitMainTest(unittest.TestCase): self.assertEqual(self.linux_source_mock.build_reconfig.call_count, 0) self.assertEqual(self.linux_source_mock.run_kernel.call_count, 1) self.linux_source_mock.run_kernel.assert_called_once_with( - args=None, build_dir='.kunit', filter_glob='', timeout=300) + args=None, build_dir='.kunit', filter_glob='', filter='', filter_action=None, timeout=300) self.print_mock.assert_any_call(StrContains('Testing complete.')) def test_run_passes_args_pass(self): @@ -605,7 +605,7 @@ class KUnitMainTest(unittest.TestCase): self.assertEqual(self.linux_source_mock.build_reconfig.call_count, 1) self.assertEqual(self.linux_source_mock.run_kernel.call_count, 1) self.linux_source_mock.run_kernel.assert_called_once_with( - args=None, build_dir='.kunit', filter_glob='', timeout=300) + args=None, build_dir='.kunit', filter_glob='', filter='', filter_action=None, timeout=300) self.print_mock.assert_any_call(StrContains('Testing complete.')) def test_exec_passes_args_fail(self): @@ -629,7 +629,7 @@ class KUnitMainTest(unittest.TestCase): kunit.main(['run']) self.assertEqual(e.exception.code, 1) self.linux_source_mock.run_kernel.assert_called_once_with( - args=None, build_dir='.kunit', filter_glob='', timeout=300) + args=None, build_dir='.kunit', filter_glob='', filter='', filter_action=None, timeout=300) self.print_mock.assert_any_call(StrContains(' 0 tests run!')) def test_exec_raw_output(self): @@ -670,13 +670,13 @@ class KUnitMainTest(unittest.TestCase): self.linux_source_mock.run_kernel = mock.Mock(return_value=[]) kunit.main(['run', '--raw_output', 'filter_glob']) self.linux_source_mock.run_kernel.assert_called_once_with( - args=None, build_dir='.kunit', filter_glob='filter_glob', timeout=300) + args=None, build_dir='.kunit', filter_glob='filter_glob', filter='', filter_action=None, timeout=300) def test_exec_timeout(self): timeout = 3453 kunit.main(['exec', '--timeout', str(timeout)]) self.linux_source_mock.run_kernel.assert_called_once_with( - args=None, build_dir='.kunit', filter_glob='', timeout=timeout) + args=None, build_dir='.kunit', filter_glob='', filter='', filter_action=None, timeout=timeout) self.print_mock.assert_any_call(StrContains('Testing complete.')) def test_run_timeout(self): @@ -684,7 +684,7 @@ class KUnitMainTest(unittest.TestCase): kunit.main(['run', '--timeout', str(timeout)]) self.assertEqual(self.linux_source_mock.build_reconfig.call_count, 1) self.linux_source_mock.run_kernel.assert_called_once_with( - args=None, build_dir='.kunit', filter_glob='', timeout=timeout) + args=None, build_dir='.kunit', filter_glob='', filter='', filter_action=None, timeout=timeout) self.print_mock.assert_any_call(StrContains('Testing complete.')) def test_run_builddir(self): @@ -692,7 +692,7 @@ class KUnitMainTest(unittest.TestCase): kunit.main(['run', '--build_dir=.kunit']) self.assertEqual(self.linux_source_mock.build_reconfig.call_count, 1) self.linux_source_mock.run_kernel.assert_called_once_with( - args=None, build_dir=build_dir, filter_glob='', timeout=300) + args=None, build_dir=build_dir, filter_glob='', filter='', filter_action=None, timeout=300) self.print_mock.assert_any_call(StrContains('Testing complete.')) def test_config_builddir(self): @@ -710,7 +710,7 @@ class KUnitMainTest(unittest.TestCase): build_dir = '.kunit' kunit.main(['exec', '--build_dir', build_dir]) self.linux_source_mock.run_kernel.assert_called_once_with( - args=None, build_dir=build_dir, filter_glob='', timeout=300) + args=None, build_dir=build_dir, filter_glob='', filter='', filter_action=None, timeout=300) self.print_mock.assert_any_call(StrContains('Testing complete.')) def test_run_kunitconfig(self): @@ -786,7 +786,7 @@ class KUnitMainTest(unittest.TestCase): kunit.main(['run', '--kernel_args=a=1', '--kernel_args=b=2']) self.assertEqual(self.linux_source_mock.build_reconfig.call_count, 1) self.linux_source_mock.run_kernel.assert_called_once_with( - args=['a=1','b=2'], build_dir='.kunit', filter_glob='', timeout=300) + args=['a=1','b=2'], build_dir='.kunit', filter_glob='', filter='', filter_action=None, timeout=300) self.print_mock.assert_any_call(StrContains('Testing complete.')) def test_list_tests(self): @@ -794,13 +794,11 @@ class KUnitMainTest(unittest.TestCase): self.linux_source_mock.run_kernel.return_value = ['TAP version 14', 'init: random output'] + want got = kunit._list_tests(self.linux_source_mock, - kunit.KunitExecRequest(None, None, '.kunit', 300, 'suite*', None, 'suite')) - + kunit.KunitExecRequest(None, None, '.kunit', 300, 'suite*', '', None, None, 'suite', False, False)) self.assertEqual(got, want) # Should respect the user's filter glob when listing tests. self.linux_source_mock.run_kernel.assert_called_once_with( - args=['kunit.action=list'], build_dir='.kunit', filter_glob='suite*', timeout=300) - + args=['kunit.action=list'], build_dir='.kunit', filter_glob='suite*', filter='', filter_action=None, timeout=300) @mock.patch.object(kunit, '_list_tests') def test_run_isolated_by_suite(self, mock_tests): @@ -809,10 +807,10 @@ class KUnitMainTest(unittest.TestCase): # Should respect the user's filter glob when listing tests. mock_tests.assert_called_once_with(mock.ANY, - kunit.KunitExecRequest(None, None, '.kunit', 300, 'suite*.test*', None, 'suite')) + kunit.KunitExecRequest(None, None, '.kunit', 300, 'suite*.test*', '', None, None, 'suite', False, False)) self.linux_source_mock.run_kernel.assert_has_calls([ - mock.call(args=None, build_dir='.kunit', filter_glob='suite.test*', timeout=300), - mock.call(args=None, build_dir='.kunit', filter_glob='suite2.test*', timeout=300), + mock.call(args=None, build_dir='.kunit', filter_glob='suite.test*', filter='', filter_action=None, timeout=300), + mock.call(args=None, build_dir='.kunit', filter_glob='suite2.test*', filter='', filter_action=None, timeout=300), ]) @mock.patch.object(kunit, '_list_tests') @@ -822,13 +820,12 @@ class KUnitMainTest(unittest.TestCase): # Should respect the user's filter glob when listing tests. mock_tests.assert_called_once_with(mock.ANY, - kunit.KunitExecRequest(None, None, '.kunit', 300, 'suite*', None, 'test')) + kunit.KunitExecRequest(None, None, '.kunit', 300, 'suite*', '', None, None, 'test', False, False)) self.linux_source_mock.run_kernel.assert_has_calls([ - mock.call(args=None, build_dir='.kunit', filter_glob='suite.test1', timeout=300), - mock.call(args=None, build_dir='.kunit', filter_glob='suite.test2', timeout=300), - mock.call(args=None, build_dir='.kunit', filter_glob='suite2.test1', timeout=300), + mock.call(args=None, build_dir='.kunit', filter_glob='suite.test1', filter='', filter_action=None, timeout=300), + mock.call(args=None, build_dir='.kunit', filter_glob='suite.test2', filter='', filter_action=None, timeout=300), + mock.call(args=None, build_dir='.kunit', filter_glob='suite2.test1', filter='', filter_action=None, timeout=300), ]) - if __name__ == '__main__': unittest.main() From patchwork Mon Jul 24 16:28:27 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Rae Moar X-Patchwork-Id: 705952 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id DDA49C00528 for ; Mon, 24 Jul 2023 16:31:40 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S231579AbjGXQbk (ORCPT ); Mon, 24 Jul 2023 12:31:40 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:58100 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S231587AbjGXQbM (ORCPT ); Mon, 24 Jul 2023 12:31:12 -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 62C9C1FC9 for ; Mon, 24 Jul 2023 09:30:57 -0700 (PDT) Received: by mail-yw1-x1149.google.com with SMTP id 00721157ae682-58378ae25bfso57027827b3.0 for ; Mon, 24 Jul 2023 09:30:57 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20221208; t=1690216256; x=1690821056; h=cc:to:from:subject:message-id:references:mime-version:in-reply-to :date:from:to:cc:subject:date:message-id:reply-to; bh=G5j34NW7MgcGMBHuRGUcTaJqW07I0/xnHGpldPUn/Ww=; b=S2ZqeCnl65tgF1NOidFcgWC4NjLl7ygrpsBLxwpC5gNYvhMElUFzesFQ54EhtucwWH E+VfzotvqWxFVa2fUNNYxczoZf6mWuJdhpU6NpEhwzepMXnn2RbWhmO7tryCCamNcljZ TFZ/A6vxzy7yvMSzSy7wwu0MY6Y5uy4RSWrM1xAjWw8Jp+cx/+uvrRaoB+0p54LfWFdQ u13dVIxGoD1iC313Q0XEmqUM7cGrygEVtipcbDv4GLM6wtMPY3bXGEaSjaVVmY9jkkh6 QOeW3NNvE2a1cGa323Ldx6hSyAi402y2x7xzE/CU4MnDYdhC3T9FUwB/or76pIuSXiFF Q0ZQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20221208; t=1690216256; x=1690821056; h=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=G5j34NW7MgcGMBHuRGUcTaJqW07I0/xnHGpldPUn/Ww=; b=bBQsw649fA3SZsRaHrP7SpeQMaJEd2k/3CyEjG9BmhRJRWrLcH7f5CsrpGUWwNoZLC e+dXjB+1/Z5v1SMatU6h8LJXF1/GdtchtlUUfavNHnwavHTpm4r/wrddxB2C9qnC31R6 jRiAIqeELxYZNe6CmGKhkkZUHPId7DEF6Iml9s7jdivxfoes9sR5rlYuyYn2rs62aJfW 5GLV65Y0hzUxIo0cW+CvnA+xIYj2YulCTPmEe7bE7nWGsiOfJzJk2kVxvZydbqybChM4 k3okcSliaqnGgQ6nwb2vIVkR8XnFAxM+sI9fULW78eUBkT7mUh+AaiLOiJSPpjD0LHM+ Edeg== X-Gm-Message-State: ABy/qLbRJ/To2xTKXBTn/rUkRy3Ibhlfk8J96W7SK5MEmTu65lk0/WtI fkR+iU79NuNpLKzSoiQ5HquzWl074w== X-Google-Smtp-Source: APBJJlHP0ywTKtk7Dfde1Vwf+7bCD8q0bSRqx5AjPWkZ8anuNNKbDWGNiL//GJRGLAmhmzHC8ogzUkDpqQ== X-Received: from rmoar-specialist.c.googlers.com ([fda3:e722:ac3:cc00:2b:7d90:c0a8:45d3]) (user=rmoar job=sendgmr) by 2002:a81:ae54:0:b0:57a:141f:b4f7 with SMTP id g20-20020a81ae54000000b0057a141fb4f7mr64651ywk.6.1690216256539; Mon, 24 Jul 2023 09:30:56 -0700 (PDT) Date: Mon, 24 Jul 2023 16:28:27 +0000 In-Reply-To: <20230724162834.1354164-1-rmoar@google.com> Mime-Version: 1.0 References: <20230724162834.1354164-1-rmoar@google.com> X-Mailer: git-send-email 2.41.0.487.g6d72f3e995-goog Message-ID: <20230724162834.1354164-8-rmoar@google.com> Subject: [PATCH v2 7/9] kunit: time: Mark test as slow using test attributes From: Rae Moar To: shuah@kernel.org, davidgow@google.com, dlatypov@google.com, brendan.higgins@linux.dev Cc: linux-kselftest@vger.kernel.org, kunit-dev@googlegroups.com, linux-kernel@vger.kernel.org, keescook@chromium.org, linux-hardening@vger.kernel.org, jstultz@google.com, tglx@linutronix.de, sboyd@kernel.org, Rae Moar Precedence: bulk List-ID: X-Mailing-List: linux-kselftest@vger.kernel.org Mark the time KUnit test, time64_to_tm_test_date_range, as slow using test attributes. This test ran relatively much slower than most other KUnit tests. By marking this test as slow, the test can now be filtered using the KUnit test attribute filtering feature. Example: --filter "speed>slow". This will run only the tests that have speeds faster than slow. The slow attribute will also be outputted in KTAP. Reviewed-by: David Gow Signed-off-by: Rae Moar --- Changes since v1: - No changes. Changes since RFC v2: - No changes. Changes since RFC v1: - No changes. kernel/time/time_test.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/kernel/time/time_test.c b/kernel/time/time_test.c index 831e8e779ace..ca058c8af6ba 100644 --- a/kernel/time/time_test.c +++ b/kernel/time/time_test.c @@ -86,7 +86,7 @@ static void time64_to_tm_test_date_range(struct kunit *test) } static struct kunit_case time_test_cases[] = { - KUNIT_CASE(time64_to_tm_test_date_range), + KUNIT_CASE_SLOW(time64_to_tm_test_date_range), {} }; From patchwork Mon Jul 24 16:28:29 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Rae Moar X-Patchwork-Id: 705951 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id E0A9CC0015E for ; Mon, 24 Jul 2023 16:31:51 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S231469AbjGXQbt (ORCPT ); Mon, 24 Jul 2023 12:31:49 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:57480 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S231527AbjGXQb3 (ORCPT ); Mon, 24 Jul 2023 12:31:29 -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 923BB1BC0 for ; Mon, 24 Jul 2023 09:31:06 -0700 (PDT) Received: by mail-yb1-xb49.google.com with SMTP id 3f1490d57ef6-d07cb52a768so2738662276.1 for ; Mon, 24 Jul 2023 09:31:06 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20221208; t=1690216263; x=1690821063; h=cc:to:from:subject:message-id:references:mime-version:in-reply-to :date:from:to:cc:subject:date:message-id:reply-to; bh=uqm376vq44xYL7pGc8vCNS4lmZToKX968V5jfP2E92g=; b=CCEeNNid+1u8ipNijlzJiuI+VrYX8to7wo9C+O4tvigQ92g6wk2DMVTUy+kOzD962W x+bgJWgc7UeL2IUjnsbjWgG2UY4CO5nqun7Vn4FpPwB6K56o5GMQM2t+g3tRuuHhqh/L oRtyLWDO9TEYVTdUVZbCRyUQTs6B+miwMN5YGtLWf3TajBFPq2FQUGD4MuXGeiz4kqXJ Ieb/7KmlISn+pI5xv/o04l4X6SVJ6V+j4ip7tUMsBzp6a+Oey7vem/fvIa01z8MGWyyC sKZzg3wUKYML/HB5FkM1pMPtn2KTPYqGhUXfM8VU9fKyUum5uA9PTN2fX96+h7W9O5Io fhwg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20221208; t=1690216263; x=1690821063; h=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=uqm376vq44xYL7pGc8vCNS4lmZToKX968V5jfP2E92g=; b=Gb18VE8PhtkZc83o/O02ns8L3eMUxfS8PBzjQr3CPej1Y9FyS8o1Kwyk2fqd8c4CwW 0uqUGxqs2s+Rmfu6l1gEgeFvO6ioUd6rTL2wXjtnFWO5gFjlBl4GLF1pweGkuzWjk3at MuXgHM0Dl5QkBqTSqB8yQB08uo/ujeIjhoCL2PO9+TQq5RVvyjsnYf9P2DSCiLoBI4QJ oekk2aiLrYPAUjrf1+i7ftFtnz0Cuub2Sq+kzqwhx38osgY43SCPlpZDoVNeTIr9Pnqo gr28NhOM0zIQMQ80Vm4mtImYSYhurgiajGAPrbM/QgaK7Ri9pe/qM8gC0wXj2WTCoJ9H qHQw== X-Gm-Message-State: ABy/qLawUKNF/Tya+hw/WNMN5RyHMj5r4mKrnwlp+3tqJQ3kBLi0OxSp jzebCbrbopFJPnRkdMbdEt9Jw7hLmg== X-Google-Smtp-Source: APBJJlFPBOO2Nq7xuAMq+FQyjqlb6gr2mp8JXz438YNxw1mjg90fCxpzWi6SG2hVjQ4P4P+gUxnt+862+w== X-Received: from rmoar-specialist.c.googlers.com ([fda3:e722:ac3:cc00:2b:7d90:c0a8:45d3]) (user=rmoar job=sendgmr) by 2002:a05:6902:1610:b0:d0c:77a8:1f6b with SMTP id bw16-20020a056902161000b00d0c77a81f6bmr32122ybb.10.1690216263153; Mon, 24 Jul 2023 09:31:03 -0700 (PDT) Date: Mon, 24 Jul 2023 16:28:29 +0000 In-Reply-To: <20230724162834.1354164-1-rmoar@google.com> Mime-Version: 1.0 References: <20230724162834.1354164-1-rmoar@google.com> X-Mailer: git-send-email 2.41.0.487.g6d72f3e995-goog Message-ID: <20230724162834.1354164-10-rmoar@google.com> Subject: [PATCH v2 9/9] kunit: Add documentation of KUnit test attributes From: Rae Moar To: shuah@kernel.org, davidgow@google.com, dlatypov@google.com, brendan.higgins@linux.dev Cc: linux-kselftest@vger.kernel.org, kunit-dev@googlegroups.com, linux-kernel@vger.kernel.org, keescook@chromium.org, linux-hardening@vger.kernel.org, jstultz@google.com, tglx@linutronix.de, sboyd@kernel.org, Rae Moar Precedence: bulk List-ID: X-Mailing-List: linux-kselftest@vger.kernel.org Add documentation on the use of test attributes under the section "Tips for Running KUnit Tests" in the KUnit docs. Documentation includes three sections on how to mark tests with attributes, how attributes are reported, and how the user can filter tests using test attributes. Reviewed-by: David Gow Signed-off-by: Rae Moar --- Changes since v1: - No changes. Changes since RFC v2: - Add comment on KUNIT_CASE_SLOW() to documentation. - Add comment on how to expose raw kernel output. - Remove an extra line at the end of file. Changes since RFC v1: - This is a new patch. .../dev-tools/kunit/running_tips.rst | 166 ++++++++++++++++++ 1 file changed, 166 insertions(+) diff --git a/Documentation/dev-tools/kunit/running_tips.rst b/Documentation/dev-tools/kunit/running_tips.rst index 8e8c493f17d1..766f9cdea0fa 100644 --- a/Documentation/dev-tools/kunit/running_tips.rst +++ b/Documentation/dev-tools/kunit/running_tips.rst @@ -262,3 +262,169 @@ other code executed during boot, e.g. # Reset coverage counters before running the test. $ echo 0 > /sys/kernel/debug/gcov/reset $ modprobe kunit-example-test + + +Test Attributes and Filtering +============================= + +Test suites and cases can be marked with test attributes, such as speed of +test. These attributes will later be printed in test output and can be used to +filter test execution. + +Marking Test Attributes +----------------------- + +Tests are marked with an attribute by including a ``kunit_attributes`` object +in the test definition. + +Test cases can be marked using the ``KUNIT_CASE_ATTR(test_name, attributes)`` +macro to define the test case instead of ``KUNIT_CASE(test_name)``. + +.. code-block:: c + + static const struct kunit_attributes example_attr = { + .speed = KUNIT_VERY_SLOW, + }; + + static struct kunit_case example_test_cases[] = { + KUNIT_CASE_ATTR(example_test, example_attr), + }; + +.. note:: + To mark a test case as slow, you can also use ``KUNIT_CASE_SLOW(test_name)``. + This is a helpful macro as the slow attribute is the most commonly used. + +Test suites can be marked with an attribute by setting the "attr" field in the +suite definition. + +.. code-block:: c + + static const struct kunit_attributes example_attr = { + .speed = KUNIT_VERY_SLOW, + }; + + static struct kunit_suite example_test_suite = { + ..., + .attr = example_attr, + }; + +.. note:: + Not all attributes need to be set in a ``kunit_attributes`` object. Unset + attributes will remain uninitialized and act as though the attribute is set + to 0 or NULL. Thus, if an attribute is set to 0, it is treated as unset. + These unset attributes will not be reported and may act as a default value + for filtering purposes. + +Reporting Attributes +-------------------- + +When a user runs tests, attributes will be present in the raw kernel output (in +KTAP format). Note that attributes will be hidden by default in kunit.py output +for all passing tests but the raw kernel output can be accessed using the +``--raw_output`` flag. This is an example of how test attributes for test cases +will be formatted in kernel output: + +.. code-block:: none + + # example_test.speed: slow + ok 1 example_test + +This is an example of how test attributes for test suites will be formatted in +kernel output: + +.. code-block:: none + + KTAP version 2 + # Subtest: example_suite + # module: kunit_example_test + 1..3 + ... + ok 1 example_suite + +Additionally, users can output a full attribute report of tests with their +attributes, using the command line flag ``--list_tests_attr``: + +.. code-block:: bash + + kunit.py run "example" --list_tests_attr + +.. note:: + This report can be accessed when running KUnit manually by passing in the + module_param ``kunit.action=list_attr``. + +Filtering +--------- + +Users can filter tests using the ``--filter`` command line flag when running +tests. As an example: + +.. code-block:: bash + + kunit.py run --filter speed=slow + + +You can also use the following operations on filters: "<", ">", "<=", ">=", +"!=", and "=". Example: + +.. code-block:: bash + + kunit.py run --filter "speed>slow" + +This example will run all tests with speeds faster than slow. Note that the +characters < and > are often interpreted by the shell, so they may need to be +quoted or escaped, as above. + +Additionally, you can use multiple filters at once. Simply separate filters +using commas. Example: + +.. code-block:: bash + + kunit.py run --filter "speed>slow, module=kunit_example_test" + +.. note:: + You can use this filtering feature when running KUnit manually by passing + the filter as a module param: ``kunit.filter="speed>slow, speed<=normal"``. + +Filtered tests will not run or show up in the test output. You can use the +``--filter_action=skip`` flag to skip filtered tests instead. These tests will be +shown in the test output in the test but will not run. To use this feature when +running KUnit manually, use the module param ``kunit.filter_action=skip``. + +Rules of Filtering Procedure +---------------------------- + +Since both suites and test cases can have attributes, there may be conflicts +between attributes during filtering. The process of filtering follows these +rules: + +- Filtering always operates at a per-test level. + +- If a test has an attribute set, then the test's value is filtered on. + +- Otherwise, the value falls back to the suite's value. + +- If neither are set, the attribute has a global "default" value, which is used. + +List of Current Attributes +-------------------------- + +``speed`` + +This attribute indicates the speed of a test's execution (how slow or fast the +test is). + +This attribute is saved as an enum with the following categories: "normal", +"slow", or "very_slow". The assumed default speed for tests is "normal". This +indicates that the test takes a relatively trivial amount of time (less than +1 second), regardless of the machine it is running on. Any test slower than +this could be marked as "slow" or "very_slow". + +The macro ``KUNIT_CASE_SLOW(test_name)`` can be easily used to set the speed +of a test case to "slow". + +``module`` + +This attribute indicates the name of the module associated with the test. + +This attribute is automatically saved as a string and is printed for each suite. +Tests can also be filtered using this attribute.