Received: by 2002:a05:6a10:16a7:0:0:0:0 with SMTP id gp39csp2937460pxb; Mon, 16 Nov 2020 00:55:51 -0800 (PST) X-Google-Smtp-Source: ABdhPJxtbEzUx1yXy/be55vh4BGQYSIStov5kiOzpKU4CGmSM3eJgfRl71coVZQdEOXBQZ9e96wA X-Received: by 2002:a05:6402:1acb:: with SMTP id ba11mr14326415edb.48.1605516950921; Mon, 16 Nov 2020 00:55:50 -0800 (PST) ARC-Seal: i=1; a=rsa-sha256; t=1605516950; cv=none; d=google.com; s=arc-20160816; b=J6cScN3ERFRMminJX2Ch/ne20nOcolRzUeX49q3j8ek0vip+5JU5R4bfgRkGIHctx5 3NtrTGW1lbYKYBfjfC9oKsOaYC0hy3QVA13hhtfOjhvkRlnloIsSmj2tJJXp7PciaeEX YxCUpk7d1/zbi5ziB9K4z0/nyFTVyNFBJ9gagVyLGRdqAjH31JwmSuwRYWtR9wJtwOEs J0PA1imZd5CHOmWou2dTsRQhZOytZGL8iiVmY6Ylp9F2cqA7SXIyjxa7cOqWFvcZ1bHH YVxDV76nzqoL7yo5MHikMbYJeWiM+6gQ1djcFbJcX63lq/ux0vwL0fGugn9oDD4WARo+ kOLQ== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=list-id:precedence:cc:to:subject:message-id:date:from:in-reply-to :references:mime-version:dkim-signature; bh=HkX3czK+fbRYaZuXoB4DEn+0Ko5lqadBX0VN06YRjVc=; b=dv3n6xx5qI4qYqNqrBR0XG3uKB+zjogBpOqK+fFa09gdAqtVTgzcJuVLhFCetYJjjb VqotFtT2jXlG/wS94o0fvZ/G9izkuBtKOSJSxnmbC1vIMpQEHemfIPZjMv8LL8KvVfyr QWT9rF8LWpfIugTlyoC1DuFIeyID4nN/PFuoLMAPn8TKIxrG3bTmgSnSL3RCi5xGeAor zOHuGY3+D8Levx50JsREQNWIKzMnSZDy/qny0PfLP/6nhI+N4Ks4IySQKIRnnC/8+fZ+ fODSurnpRVuKQQK+2pSGo3Zh6bknATH/E5JFZ/JDZ3KIzVrZsIdDzTqEwUx3GqfByUlf CpGw== ARC-Authentication-Results: i=1; mx.google.com; dkim=pass header.i=@google.com header.s=20161025 header.b=GGNV+n7l; spf=pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 23.128.96.18 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org; dmarc=pass (p=REJECT sp=REJECT dis=NONE) header.from=google.com Return-Path: Received: from vger.kernel.org (vger.kernel.org. [23.128.96.18]) by mx.google.com with ESMTP id x16si11689855eds.110.2020.11.16.00.55.28; Mon, 16 Nov 2020 00:55:50 -0800 (PST) Received-SPF: pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 23.128.96.18 as permitted sender) client-ip=23.128.96.18; Authentication-Results: mx.google.com; dkim=pass header.i=@google.com header.s=20161025 header.b=GGNV+n7l; spf=pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 23.128.96.18 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 S1726291AbgKPIxk (ORCPT + 99 others); Mon, 16 Nov 2020 03:53:40 -0500 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:48860 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1728140AbgKPIxj (ORCPT ); Mon, 16 Nov 2020 03:53:39 -0500 Received: from mail-oi1-x242.google.com (mail-oi1-x242.google.com [IPv6:2607:f8b0:4864:20::242]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 41FD7C0613D2 for ; Mon, 16 Nov 2020 00:53:39 -0800 (PST) Received: by mail-oi1-x242.google.com with SMTP id w145so17986310oie.9 for ; Mon, 16 Nov 2020 00:53:39 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20161025; h=mime-version:references:in-reply-to:from:date:message-id:subject:to :cc; bh=HkX3czK+fbRYaZuXoB4DEn+0Ko5lqadBX0VN06YRjVc=; b=GGNV+n7lWD5VBi9GsyrUcq5BUfeLfvqjxQI38yd/159lWM8mBmUIb+IhbnFfVRHM5O 5yjyg3yOZjWavw4PbGUSMtsj6gfuD+lXpU5uVMJEYxoTiV9ZEynHYpxKSgXrzscRbUOi GdHE4WUmVlO+Y3xRFTyKv5p0wRvtFfoNFYsuc2UgdpQtj0F8CvWHxZ7itIM6yo9Z6qGJ SE+Hesr4awK9p1cqoDwg1X6rHKdfk9Jg8t01StD7wSW+dzsRW2tCDOqnE4jd6lRmZGuI ubwGEfTjAprjpb4SrrqBGnsVgeIOdaj3oSiZhMb9fsbaTbolqmIQZCH23hFhOP+jR6Al w5Aw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:mime-version:references:in-reply-to:from:date :message-id:subject:to:cc; bh=HkX3czK+fbRYaZuXoB4DEn+0Ko5lqadBX0VN06YRjVc=; b=clWqPFLav6ETgujga7covPX9KjiR5q2nnw6mN9CJK797qTUf5z8dAcjT9E7XGq01Mf PaF0N062imUOt7esKQu4eRCAXmhXcKRlLywyKyFFvsfasTR2FhDXar0rQ6fpY1vB0V2I qi1TgnJz7sNsUO2vIFijqv/N3LAJZci8A05YHeft5MHeSwRdxsuUP8/uFQc+rfsU/Wo8 TiorHVpZMi1G/4W11e+3lHtio5HKsWtgzcyeAs2u6ecLP6ct7ilBH042obK//lGbUyb9 R9+AOqGU5uZ6a2XL0E0fjSltEqpk0ehpE2XvNv+YKf4id7QhcyA3VB51Xi2TsNMNf2FK IKEA== X-Gm-Message-State: AOAM532C/LVdPOX/VyGO4O7+1pPIkYpo4/DtKRKZouCcjQFyal0nqDch aYD2/imQrDQUx8/WNX3P3QLIn5loqnEel0YqPOOWaQ== X-Received: by 2002:aca:a988:: with SMTP id s130mr9148649oie.172.1605516818373; Mon, 16 Nov 2020 00:53:38 -0800 (PST) MIME-Version: 1.0 References: <20201116054035.211498-1-98.arpi@gmail.com> In-Reply-To: <20201116054035.211498-1-98.arpi@gmail.com> From: Marco Elver Date: Mon, 16 Nov 2020 09:53:27 +0100 Message-ID: Subject: Re: [PATCH v9 1/2] kunit: Support for Parameterized Testing To: Arpitha Raghunandan <98.arpi@gmail.com> Cc: Brendan Higgins , Shuah Khan , Iurii Zaikin , "Theodore Ts'o" , Andreas Dilger , Tim Bird , David Gow , "open list:KERNEL SELFTEST FRAMEWORK" , KUnit Development , LKML , linux-kernel-mentees@lists.linuxfoundation.org, linux-ext4@vger.kernel.org Content-Type: text/plain; charset="UTF-8" Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org On Mon, 16 Nov 2020 at 06:41, Arpitha Raghunandan <98.arpi@gmail.com> wrote: > > Implementation of support for parameterized testing in KUnit. This > approach requires the creation of a test case using the > KUNIT_CASE_PARAM() macro that accepts a generator function as input. > > This generator function should return the next parameter given the > previous parameter in parameterized tests. It also provides a macro to > generate common-case generators based on arrays. Generators may also > optionally provide a human-readable description of parameters, which is > displayed where available. > > Note, currently the result of each parameter run is displayed in > diagnostic lines, and only the overall test case output summarizes > TAP-compliant success or failure of all parameter runs. In future, when > supported by kunit-tool, these can be turned into subsubtest outputs. > > Signed-off-by: Arpitha Raghunandan <98.arpi@gmail.com> > Co-developed-by: Marco Elver > Signed-off-by: Marco Elver This and patch 2/2 look good to me. Thank you! -- Marco > --- > Changes v8->v9: > - No change to this patch of the patch series > > Changes v7->v8: > - Increase KUNIT_PARAM_DESC_SIZE to 128 > - Format pointer style appropriately > > Changes v6->v7: > - Clarify commit message. > - Introduce ability to optionally generate descriptions for parameters; > if no description is provided, we'll still print 'param-N'. > - Change diagnostic line format to: > # : N - [] > > Changes v5->v6: > - Fix alignment to maintain consistency > > Changes v4->v5: > - Update kernel-doc comments. > - Use const void* for generator return and prev value types. > - Add kernel-doc comment for KUNIT_ARRAY_PARAM. > - Rework parameterized test case execution strategy: each parameter is executed > as if it was its own test case, with its own test initialization and cleanup > (init and exit are called, etc.). However, we cannot add new test cases per TAP > protocol once we have already started execution. Instead, log the result of > each parameter run as a diagnostic comment. > > Changes v3->v4: > - Rename kunit variables > - Rename generator function helper macro > - Add documentation for generator approach > - Display test case name in case of failure along with param index > > Changes v2->v3: > - Modifictaion of generator macro and method > > Changes v1->v2: > - Use of a generator method to access test case parameters > Changes v6->v7: > - Clarify commit message. > - Introduce ability to optionally generate descriptions for parameters; > if no description is provided, we'll still print 'param-N'. > - Change diagnostic line format to: > # : N - [] > - Before execution of parameterized test case, count number of > parameters and display number of parameters. Currently also as a > diagnostic line, but this may be used in future to generate a subsubtest > plan. A requirement of this change is that generators must generate a > deterministic number of parameters. > > Changes v5->v6: > - Fix alignment to maintain consistency > > Changes v4->v5: > - Update kernel-doc comments. > - Use const void* for generator return and prev value types. > - Add kernel-doc comment for KUNIT_ARRAY_PARAM. > - Rework parameterized test case execution strategy: each parameter is executed > as if it was its own test case, with its own test initialization and cleanup > (init and exit are called, etc.). However, we cannot add new test cases per TAP > protocol once we have already started execution. Instead, log the result of > each parameter run as a diagnostic comment. > > Changes v3->v4: > - Rename kunit variables > - Rename generator function helper macro > - Add documentation for generator approach > - Display test case name in case of failure along with param index > > Changes v2->v3: > - Modifictaion of generator macro and method > > Changes v1->v2: > - Use of a generator method to access test case parameters > > include/kunit/test.h | 51 ++++++++++++++++++++++++++++++++++++++ > lib/kunit/test.c | 59 ++++++++++++++++++++++++++++++++++---------- > 2 files changed, 97 insertions(+), 13 deletions(-) > > diff --git a/include/kunit/test.h b/include/kunit/test.h > index db1b0ae666c4..27b42a008c7a 100644 > --- a/include/kunit/test.h > +++ b/include/kunit/test.h > @@ -94,6 +94,9 @@ struct kunit; > /* Size of log associated with test. */ > #define KUNIT_LOG_SIZE 512 > > +/* Maximum size of parameter description string. */ > +#define KUNIT_PARAM_DESC_SIZE 128 > + > /* > * TAP specifies subtest stream indentation of 4 spaces, 8 spaces for a > * sub-subtest. See the "Subtests" section in > @@ -107,6 +110,7 @@ struct kunit; > * > * @run_case: the function representing the actual test case. > * @name: the name of the test case. > + * @generate_params: the generator function for parameterized tests. > * > * A test case is a function with the signature, > * ``void (*)(struct kunit *)`` > @@ -141,6 +145,7 @@ struct kunit; > struct kunit_case { > void (*run_case)(struct kunit *test); > const char *name; > + const void* (*generate_params)(const void *prev, char *desc); > > /* private: internal use only. */ > bool success; > @@ -163,6 +168,27 @@ static inline char *kunit_status_to_string(bool status) > */ > #define KUNIT_CASE(test_name) { .run_case = test_name, .name = #test_name } > > +/** > + * KUNIT_CASE_PARAM - A helper for creation a parameterized &struct kunit_case > + * > + * @test_name: a reference to a test case function. > + * @gen_params: a reference to a parameter generator function. > + * > + * The generator function:: > + * > + * const void* gen_params(const void *prev, char *desc) > + * > + * is used to lazily generate a series of arbitrarily typed values that fit into > + * a void*. The argument @prev is the previously returned value, which should be > + * used to derive the next value; @prev is set to NULL on the initial generator > + * call. When no more values are available, the generator must return NULL. > + * Optionally write a string into @desc (size of KUNIT_PARAM_DESC_SIZE) > + * describing the parameter. > + */ > +#define KUNIT_CASE_PARAM(test_name, gen_params) \ > + { .run_case = test_name, .name = #test_name, \ > + .generate_params = gen_params } > + > /** > * struct kunit_suite - describes a related collection of &struct kunit_case > * > @@ -208,6 +234,10 @@ struct kunit { > const char *name; /* Read only after initialization! */ > char *log; /* Points at case log after initialization */ > struct kunit_try_catch try_catch; > + /* param_value is the current parameter value for a test case. */ > + const void *param_value; > + /* param_index stores the index of the parameter in parameterized tests. */ > + int param_index; > /* > * success starts as true, and may only be set to false during a > * test case; thus, it is safe to update this across multiple > @@ -1742,4 +1772,25 @@ do { \ > fmt, \ > ##__VA_ARGS__) > > +/** > + * KUNIT_ARRAY_PARAM() - Define test parameter generator from an array. > + * @name: prefix for the test parameter generator function. > + * @array: array of test parameters. > + * @get_desc: function to convert param to description; NULL to use default > + * > + * Define function @name_gen_params which uses @array to generate parameters. > + */ > +#define KUNIT_ARRAY_PARAM(name, array, get_desc) \ > + static const void *name##_gen_params(const void *prev, char *desc) \ > + { \ > + typeof((array)[0]) *__next = prev ? ((typeof(__next)) prev) + 1 : (array); \ > + if (__next - (array) < ARRAY_SIZE((array))) { \ > + void (*__get_desc)(typeof(__next), char *) = get_desc; \ > + if (__get_desc) \ > + __get_desc(__next, desc); \ > + return __next; \ > + } \ > + return NULL; \ > + } > + > #endif /* _KUNIT_TEST_H */ > diff --git a/lib/kunit/test.c b/lib/kunit/test.c > index 750704abe89a..ec9494e914ef 100644 > --- a/lib/kunit/test.c > +++ b/lib/kunit/test.c > @@ -325,39 +325,72 @@ static void kunit_catch_run_case(void *data) > * occur in a test case and reports them as failures. > */ > static void kunit_run_case_catch_errors(struct kunit_suite *suite, > - struct kunit_case *test_case) > + struct kunit_case *test_case, > + struct kunit *test) > { > struct kunit_try_catch_context context; > struct kunit_try_catch *try_catch; > - struct kunit test; > > - kunit_init_test(&test, test_case->name, test_case->log); > - try_catch = &test.try_catch; > + kunit_init_test(test, test_case->name, test_case->log); > + try_catch = &test->try_catch; > > kunit_try_catch_init(try_catch, > - &test, > + test, > kunit_try_run_case, > kunit_catch_run_case); > - context.test = &test; > + context.test = test; > context.suite = suite; > context.test_case = test_case; > kunit_try_catch_run(try_catch, &context); > > - test_case->success = test.success; > - > - kunit_print_ok_not_ok(&test, true, test_case->success, > - kunit_test_case_num(suite, test_case), > - test_case->name); > + test_case->success = test->success; > } > > int kunit_run_tests(struct kunit_suite *suite) > { > + char param_desc[KUNIT_PARAM_DESC_SIZE]; > struct kunit_case *test_case; > > kunit_print_subtest_start(suite); > > - kunit_suite_for_each_test_case(suite, test_case) > - kunit_run_case_catch_errors(suite, test_case); > + kunit_suite_for_each_test_case(suite, test_case) { > + struct kunit test = { .param_value = NULL, .param_index = 0 }; > + bool test_success = true; > + > + if (test_case->generate_params) { > + /* Get initial param. */ > + param_desc[0] = '\0'; > + test.param_value = test_case->generate_params(NULL, param_desc); > + } > + > + do { > + kunit_run_case_catch_errors(suite, test_case, &test); > + test_success &= test_case->success; > + > + if (test_case->generate_params) { > + if (param_desc[0] == '\0') { > + snprintf(param_desc, sizeof(param_desc), > + "param-%d", test.param_index); > + } > + > + kunit_log(KERN_INFO, &test, > + KUNIT_SUBTEST_INDENT > + "# %s: %s %d - %s", > + test_case->name, > + kunit_status_to_string(test.success), > + test.param_index + 1, param_desc); > + > + /* Get next param. */ > + param_desc[0] = '\0'; > + test.param_value = test_case->generate_params(test.param_value, param_desc); > + test.param_index++; > + } > + } while (test.param_value); > + > + kunit_print_ok_not_ok(&test, true, test_success, > + kunit_test_case_num(suite, test_case), > + test_case->name); > + } > > kunit_print_subtest_end(suite); > > -- > 2.25.1 >