lists.openwall.net   lists  /  announce  owl-users  owl-dev  john-users  john-dev  passwdqc-users  yescrypt  popa3d-users  /  oss-security  kernel-hardening  musl  sabotage  tlsify  passwords  /  crypt-dev  xvendor  /  Bugtraq  Full-Disclosure  linux-kernel  linux-netdev  linux-ext4  linux-hardening  PHC 
Open Source and information security mailing list archives
 
Hash Suite: Windows password security audit tool. GUI, reports in PDF.
[<prev] [next>] [<thread-prev] [day] [month] [year] [list]
Date:   Fri, 6 Nov 2020 21:46:54 +0530
From:   Arpitha Raghunandan <98.arpi@...il.com>
To:     Marco Elver <elver@...gle.com>
Cc:     Brendan Higgins <brendanhiggins@...gle.com>,
        skhan@...uxfoundation.org, Iurii Zaikin <yzaikin@...gle.com>,
        Theodore Ts'o <tytso@....edu>,
        Andreas Dilger <adilger.kernel@...ger.ca>,
        "open list:KERNEL SELFTEST FRAMEWORK" 
        <linux-kselftest@...r.kernel.org>,
        KUnit Development <kunit-dev@...glegroups.com>,
        LKML <linux-kernel@...r.kernel.org>,
        linux-kernel-mentees@...ts.linuxfoundation.org,
        linux-ext4@...r.kernel.org
Subject: Re: [PATCH v4 1/2] kunit: Support for Parameterized Testing

On 06/11/20 6:04 pm, Marco Elver wrote:
> On Fri, Nov 06, 2020 at 09:11AM +0100, Marco Elver wrote:
>> On Fri, 6 Nov 2020 at 06:54, Arpitha Raghunandan <98.arpi@...il.com> wrote:
> [...]
>>> I think this format of output should be fine for parameterized tests.
>>> But, this patch has the same issue as earlier. While, the tests run and
>>> this is the output that can be seen using dmesg, it still causes an issue on
>>> using the kunit tool. It gives a similar error:
>>>
>>> [11:07:38] [ERROR]  expected 7 test suites, but got -1
>>> [11:07:38] [ERROR] expected_suite_index -1, but got 2
>>> [11:07:38] [ERROR] got unexpected test suite: kunit-try-catch-test
>>> [ERROR] no tests run!
>>> [11:07:38] ============================================================
>>> [11:07:38] Testing complete. 0 tests run. 0 failed. 0 crashed.
>>>
>>
>> I'd suggest testing without these patches and diffing the output.
>> AFAIK we're not adding any new non-# output, so it might be a
>> pre-existing bug in some parsing code. Either that, or the parsing
>> code does not respect the # correctly?
> 
> Hmm, tools/testing/kunit/kunit_parser.py has
> 
> 	SUBTEST_DIAGNOSTIC = re.compile(r'^[\s]+# .*?: (.*)$')
> 
> , which seems to expect a ': ' in there. Not sure if this is required by
> TAP, but let's leave this alone for now.
> 

Oh okay.

> We can change the output to not trip this up, which might also be more a
> consistent diagnostic format vs. other diagnostics. See the revised
> patch below. With it the output is as follows:
> 
> | TAP version 14
> | 1..6
> |     # Subtest: ext4_inode_test
> |     1..1
> |     # inode_test_xtimestamp_decoding: param-0 ok
> |     # inode_test_xtimestamp_decoding: param-1 ok
> |     # inode_test_xtimestamp_decoding: param-2 ok
> |     # inode_test_xtimestamp_decoding: param-3 ok
> |     # inode_test_xtimestamp_decoding: param-4 ok
> |     # inode_test_xtimestamp_decoding: param-5 ok
> |     # inode_test_xtimestamp_decoding: param-6 ok
> |     # inode_test_xtimestamp_decoding: param-7 ok
> |     # inode_test_xtimestamp_decoding: param-8 ok
> |     # inode_test_xtimestamp_decoding: param-9 ok
> |     # inode_test_xtimestamp_decoding: param-10 ok
> |     # inode_test_xtimestamp_decoding: param-11 ok
> |     # inode_test_xtimestamp_decoding: param-12 ok
> |     # inode_test_xtimestamp_decoding: param-13 ok
> |     # inode_test_xtimestamp_decoding: param-14 ok
> |     # inode_test_xtimestamp_decoding: param-15 ok
> |     ok 1 - inode_test_xtimestamp_decoding
> | ok 1 - ext4_inode_test
> 
> And kunit-tool seems to be happy as well.
> 

Yes this works as expected. Thank you!
I will send this patch as v5.

> Thanks,
> -- Marco
> 
> ------ >8 ------
> 
> From 13a94d75d6b1b430e89fcff2cd76629b56b9d636 Mon Sep 17 00:00:00 2001
> From: Arpitha Raghunandan <98.arpi@...il.com>
> Date: Tue, 27 Oct 2020 23:16:30 +0530
> Subject: [PATCH] kunit: Support for Parameterized Testing
> 
> 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.
> 
> Signed-off-by: Arpitha Raghunandan <98.arpi@...il.com>
> Co-developed-by: Marco Elver <elver@...gle.com>
> Signed-off-by: Marco Elver <elver@...gle.com>
> ---
> 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 | 36 ++++++++++++++++++++++++++++++++++
>  lib/kunit/test.c     | 46 +++++++++++++++++++++++++++++++-------------
>  2 files changed, 69 insertions(+), 13 deletions(-)
> 
> diff --git a/include/kunit/test.h b/include/kunit/test.h
> index 9197da792336..ae5488a37e48 100644
> --- a/include/kunit/test.h
> +++ b/include/kunit/test.h
> @@ -107,6 +107,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 +142,7 @@ struct kunit;
>  struct kunit_case {
>  	void (*run_case)(struct kunit *test);
>  	const char *name;
> +	const void* (*generate_params)(const void *prev);
>  
>  	/* private: internal use only. */
>  	bool success;
> @@ -163,6 +165,22 @@ 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)`` 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.
> + */
> +#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 +226,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 +1764,18 @@ 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.
> + *
> + * Define function @name_gen_params which uses @array to generate parameters.
> + */
> +#define KUNIT_ARRAY_PARAM(name, array)								\
> +	static const void *name##_gen_params(const void *prev)					\
> +	{											\
> +		typeof((array)[0]) * __next = prev ? ((typeof(__next)) prev) + 1 : (array);	\
> +		return __next - (array) < ARRAY_SIZE((array)) ? __next : NULL;			\
> +	}
> +
>  #endif /* _KUNIT_TEST_H */
> diff --git a/lib/kunit/test.c b/lib/kunit/test.c
> index 750704abe89a..329fee9e0634 100644
> --- a/lib/kunit/test.c
> +++ b/lib/kunit/test.c
> @@ -325,29 +325,25 @@ 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)
> @@ -356,8 +352,32 @@ int kunit_run_tests(struct kunit_suite *suite)
>  
>  	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)
> +			test.param_value = test_case->generate_params(NULL);
> +
> +		do {
> +			kunit_run_case_catch_errors(suite, test_case, &test);
> +			test_success &= test_case->success;
> +
> +			if (test_case->generate_params) {
> +				kunit_log(KERN_INFO, &test,
> +					  KUNIT_SUBTEST_INDENT
> +					  "# %s: param-%d %s",
> +					  test_case->name, test.param_index,
> +					  kunit_status_to_string(test.success));
> +				test.param_value = test_case->generate_params(test.param_value);
> +				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);
>  
> 

Powered by blists - more mailing lists