2021-04-16 21:22:38

by Daniel Latypov

[permalink] [raw]
Subject: [PATCH v6] lib: add basic KUnit test for lib/math

Add basic test coverage for files that don't require any config options:
* part of math.h (what seem to be the most commonly used macros)
* gcd.c
* lcm.c
* int_sqrt.c
* reciprocal_div.c
(Ignored int_pow.c since it's a simple textbook algorithm.)

These tests aren't particularly interesting, but they
* provide short and simple examples of parameterized tests
* provide a place to add tests for any new files in this dir
* are written so adding new test cases to cover edge cases should be easy
* looking at code coverage, we hit all the branches in the .c files

Signed-off-by: Daniel Latypov <[email protected]>
Reviewed-by: David Gow <[email protected]>
---
Changes since v5:
* add in test cases for roundup/rounddown
* address misc comments from David

Changes since v4:
* add in test cases for some math.h macros (abs, round_up/round_down,
div_round_down/closest)
* use parameterized testing less to keep things terser

Changes since v3:
* fix `checkpatch.pl --strict` warnings
* add test cases for gcd(0,0) and lcm(0,0)
* minor: don't test both gcd(a,b) and gcd(b,a) when a == b

Changes since v2: mv math_test.c => math_kunit.c

Changes since v1:
* Rebase and rewrite to use the new parameterized testing support.
* misc: fix overflow in literal and inline int_sqrt format string.
* related: commit 1f0e943df68a ("Documentation: kunit: provide guidance
for testing many inputs") was merged explaining the patterns shown here.
* there's an in-flight patch to update it for parameterized testing.
---
lib/math/Kconfig | 12 ++
lib/math/Makefile | 2 +
lib/math/math_kunit.c | 291 ++++++++++++++++++++++++++++++++++++++++++
3 files changed, 305 insertions(+)
create mode 100644 lib/math/math_kunit.c

diff --git a/lib/math/Kconfig b/lib/math/Kconfig
index f19bc9734fa7..a974d4db0f9c 100644
--- a/lib/math/Kconfig
+++ b/lib/math/Kconfig
@@ -15,3 +15,15 @@ config PRIME_NUMBERS

config RATIONAL
bool
+
+config MATH_KUNIT_TEST
+ tristate "KUnit test for lib/math and math.h" if !KUNIT_ALL_TESTS
+ depends on KUNIT
+ default KUNIT_ALL_TESTS
+ help
+ This builds unit tests for lib/math and math.h.
+
+ For more information on KUnit and unit tests in general, please refer
+ to the KUnit documentation in Documentation/dev-tools/kunit/.
+
+ If unsure, say N.
diff --git a/lib/math/Makefile b/lib/math/Makefile
index be6909e943bd..30abb7a8d564 100644
--- a/lib/math/Makefile
+++ b/lib/math/Makefile
@@ -4,3 +4,5 @@ obj-y += div64.o gcd.o lcm.o int_pow.o int_sqrt.o reciprocal_div.o
obj-$(CONFIG_CORDIC) += cordic.o
obj-$(CONFIG_PRIME_NUMBERS) += prime_numbers.o
obj-$(CONFIG_RATIONAL) += rational.o
+
+obj-$(CONFIG_MATH_KUNIT_TEST) += math_kunit.o
diff --git a/lib/math/math_kunit.c b/lib/math/math_kunit.c
new file mode 100644
index 000000000000..556c23b17c3c
--- /dev/null
+++ b/lib/math/math_kunit.c
@@ -0,0 +1,291 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Simple KUnit suite for math helper funcs that are always enabled.
+ *
+ * Copyright (C) 2020, Google LLC.
+ * Author: Daniel Latypov <[email protected]>
+ */
+
+#include <kunit/test.h>
+#include <linux/gcd.h>
+#include <linux/kernel.h>
+#include <linux/lcm.h>
+#include <linux/reciprocal_div.h>
+
+static void abs_test(struct kunit *test)
+{
+ KUNIT_EXPECT_EQ(test, abs((char)0), (char)0);
+ KUNIT_EXPECT_EQ(test, abs((char)42), (char)42);
+ KUNIT_EXPECT_EQ(test, abs((char)-42), (char)42);
+
+ /* The expression in the macro is actually promoted to an int. */
+ KUNIT_EXPECT_EQ(test, abs((short)0), 0);
+ KUNIT_EXPECT_EQ(test, abs((short)42), 42);
+ KUNIT_EXPECT_EQ(test, abs((short)-42), 42);
+
+ KUNIT_EXPECT_EQ(test, abs(0), 0);
+ KUNIT_EXPECT_EQ(test, abs(42), 42);
+ KUNIT_EXPECT_EQ(test, abs(-42), 42);
+
+ KUNIT_EXPECT_EQ(test, abs(0L), 0L);
+ KUNIT_EXPECT_EQ(test, abs(42L), 42L);
+ KUNIT_EXPECT_EQ(test, abs(-42L), 42L);
+
+ KUNIT_EXPECT_EQ(test, abs(0LL), 0LL);
+ KUNIT_EXPECT_EQ(test, abs(42LL), 42LL);
+ KUNIT_EXPECT_EQ(test, abs(-42LL), 42LL);
+
+ /* Unsigned types get casted to signed. */
+ KUNIT_EXPECT_EQ(test, abs(0ULL), 0LL);
+ KUNIT_EXPECT_EQ(test, abs(42ULL), 42LL);
+}
+
+static void int_sqrt_test(struct kunit *test)
+{
+ KUNIT_EXPECT_EQ(test, int_sqrt(0UL), 0UL);
+ KUNIT_EXPECT_EQ(test, int_sqrt(1UL), 1UL);
+ KUNIT_EXPECT_EQ(test, int_sqrt(4UL), 2UL);
+ KUNIT_EXPECT_EQ(test, int_sqrt(5UL), 2UL);
+ KUNIT_EXPECT_EQ(test, int_sqrt(8UL), 2UL);
+ KUNIT_EXPECT_EQ(test, int_sqrt(1UL << 30), 1UL << 15);
+}
+
+static void round_up_test(struct kunit *test)
+{
+ KUNIT_EXPECT_EQ(test, round_up(0, 1), 0);
+ KUNIT_EXPECT_EQ(test, round_up(1, 2), 2);
+ KUNIT_EXPECT_EQ(test, round_up(3, 2), 4);
+ KUNIT_EXPECT_EQ(test, round_up((1 << 30) - 1, 2), 1 << 30);
+ KUNIT_EXPECT_EQ(test, round_up((1 << 30) - 1, 1 << 29), 1 << 30);
+}
+
+static void round_down_test(struct kunit *test)
+{
+ KUNIT_EXPECT_EQ(test, round_down(0, 1), 0);
+ KUNIT_EXPECT_EQ(test, round_down(1, 2), 0);
+ KUNIT_EXPECT_EQ(test, round_down(3, 2), 2);
+ KUNIT_EXPECT_EQ(test, round_down((1 << 30) - 1, 2), (1 << 30) - 2);
+ KUNIT_EXPECT_EQ(test, round_down((1 << 30) - 1, 1 << 29), 1 << 29);
+}
+
+/* These versions can round to numbers that aren't a power of two */
+static void roundup_test(struct kunit *test)
+{
+ KUNIT_EXPECT_EQ(test, roundup(0, 1), 0);
+ KUNIT_EXPECT_EQ(test, roundup(1, 2), 2);
+ KUNIT_EXPECT_EQ(test, roundup(3, 2), 4);
+ KUNIT_EXPECT_EQ(test, roundup((1 << 30) - 1, 2), 1 << 30);
+ KUNIT_EXPECT_EQ(test, roundup((1 << 30) - 1, 1 << 29), 1 << 30);
+
+ KUNIT_EXPECT_EQ(test, roundup(3, 2), 4);
+ KUNIT_EXPECT_EQ(test, roundup(4, 3), 6);
+}
+
+static void rounddown_test(struct kunit *test)
+{
+ KUNIT_EXPECT_EQ(test, rounddown(0, 1), 0);
+ KUNIT_EXPECT_EQ(test, rounddown(1, 2), 0);
+ KUNIT_EXPECT_EQ(test, rounddown(3, 2), 2);
+ KUNIT_EXPECT_EQ(test, rounddown((1 << 30) - 1, 2), (1 << 30) - 2);
+ KUNIT_EXPECT_EQ(test, rounddown((1 << 30) - 1, 1 << 29), 1 << 29);
+
+ KUNIT_EXPECT_EQ(test, rounddown(3, 2), 2);
+ KUNIT_EXPECT_EQ(test, rounddown(4, 3), 3);
+}
+
+static void div_round_up_test(struct kunit *test)
+{
+ KUNIT_EXPECT_EQ(test, DIV_ROUND_UP(0, 1), 0);
+ KUNIT_EXPECT_EQ(test, DIV_ROUND_UP(20, 10), 2);
+ KUNIT_EXPECT_EQ(test, DIV_ROUND_UP(21, 10), 3);
+ KUNIT_EXPECT_EQ(test, DIV_ROUND_UP(21, 20), 2);
+ KUNIT_EXPECT_EQ(test, DIV_ROUND_UP(21, 99), 1);
+}
+
+static void div_round_closest_test(struct kunit *test)
+{
+ KUNIT_EXPECT_EQ(test, DIV_ROUND_CLOSEST(0, 1), 0);
+ KUNIT_EXPECT_EQ(test, DIV_ROUND_CLOSEST(20, 10), 2);
+ KUNIT_EXPECT_EQ(test, DIV_ROUND_CLOSEST(21, 10), 2);
+ KUNIT_EXPECT_EQ(test, DIV_ROUND_CLOSEST(25, 10), 3);
+}
+
+/* Generic test case for unsigned long inputs. */
+struct test_case {
+ unsigned long a, b;
+ unsigned long result;
+};
+
+static struct test_case gcd_cases[] = {
+ {
+ .a = 0, .b = 0,
+ .result = 0,
+ },
+ {
+ .a = 0, .b = 1,
+ .result = 1,
+ },
+ {
+ .a = 2, .b = 2,
+ .result = 2,
+ },
+ {
+ .a = 2, .b = 4,
+ .result = 2,
+ },
+ {
+ .a = 3, .b = 5,
+ .result = 1,
+ },
+ {
+ .a = 3 * 9, .b = 3 * 5,
+ .result = 3,
+ },
+ {
+ .a = 3 * 5 * 7, .b = 3 * 5 * 11,
+ .result = 15,
+ },
+ {
+ .a = 1 << 21,
+ .b = (1 << 21) - 1,
+ .result = 1,
+ },
+};
+
+KUNIT_ARRAY_PARAM(gcd, gcd_cases, NULL);
+
+static void gcd_test(struct kunit *test)
+{
+ const char *message_fmt = "gcd(%lu, %lu)";
+ const struct test_case *test_param = test->param_value;
+
+ KUNIT_EXPECT_EQ_MSG(test, test_param->result,
+ gcd(test_param->a, test_param->b),
+ message_fmt, test_param->a,
+ test_param->b);
+
+ if (test_param->a == test_param->b)
+ return;
+
+ /* gcd(a,b) == gcd(b,a) */
+ KUNIT_EXPECT_EQ_MSG(test, test_param->result,
+ gcd(test_param->b, test_param->a),
+ message_fmt, test_param->b,
+ test_param->a);
+}
+
+static struct test_case lcm_cases[] = {
+ {
+ .a = 0, .b = 0,
+ .result = 0,
+ },
+ {
+ .a = 0, .b = 1,
+ .result = 0,
+ },
+ {
+ .a = 1, .b = 2,
+ .result = 2,
+ },
+ {
+ .a = 2, .b = 2,
+ .result = 2,
+ },
+ {
+ .a = 3 * 5, .b = 3 * 7,
+ .result = 3 * 5 * 7,
+ },
+};
+
+KUNIT_ARRAY_PARAM(lcm, lcm_cases, NULL);
+
+static void lcm_test(struct kunit *test)
+{
+ const char *message_fmt = "lcm(%lu, %lu)";
+ const struct test_case *test_param = test->param_value;
+
+ KUNIT_EXPECT_EQ_MSG(test, test_param->result,
+ lcm(test_param->a, test_param->b),
+ message_fmt, test_param->a,
+ test_param->b);
+
+ if (test_param->a == test_param->b)
+ return;
+
+ /* lcm(a,b) == lcm(b,a) */
+ KUNIT_EXPECT_EQ_MSG(test, test_param->result,
+ lcm(test_param->b, test_param->a),
+ message_fmt, test_param->b,
+ test_param->a);
+}
+
+struct u32_test_case {
+ u32 a, b;
+ u32 result;
+};
+
+static struct u32_test_case reciprocal_div_cases[] = {
+ {
+ .a = 0, .b = 1,
+ .result = 0,
+ },
+ {
+ .a = 42, .b = 20,
+ .result = 2,
+ },
+ {
+ .a = 42, .b = 9999,
+ .result = 0,
+ },
+ {
+ .a = (1 << 16), .b = (1 << 14),
+ .result = 1 << 2,
+ },
+};
+
+KUNIT_ARRAY_PARAM(reciprocal_div, reciprocal_div_cases, NULL);
+
+static void reciprocal_div_test(struct kunit *test)
+{
+ const struct u32_test_case *test_param = test->param_value;
+ struct reciprocal_value rv = reciprocal_value(test_param->b);
+
+ KUNIT_EXPECT_EQ_MSG(test, test_param->result,
+ reciprocal_divide(test_param->a, rv),
+ "reciprocal_divide(%u, %u)",
+ test_param->a, test_param->b);
+}
+
+static void reciprocal_scale_test(struct kunit *test)
+{
+ KUNIT_EXPECT_EQ(test, reciprocal_scale(0u, 100), 0u);
+ KUNIT_EXPECT_EQ(test, reciprocal_scale(1u, 100), 0u);
+ KUNIT_EXPECT_EQ(test, reciprocal_scale(1u << 4, 1 << 28), 1u);
+ KUNIT_EXPECT_EQ(test, reciprocal_scale(1u << 16, 1 << 28), 1u << 12);
+ KUNIT_EXPECT_EQ(test, reciprocal_scale(~0u, 1 << 28), (1u << 28) - 1);
+}
+
+static struct kunit_case math_test_cases[] = {
+ KUNIT_CASE(abs_test),
+ KUNIT_CASE(int_sqrt_test),
+ KUNIT_CASE(round_up_test),
+ KUNIT_CASE(round_down_test),
+ KUNIT_CASE(roundup_test),
+ KUNIT_CASE(rounddown_test),
+ KUNIT_CASE(div_round_up_test),
+ KUNIT_CASE(div_round_closest_test),
+ KUNIT_CASE_PARAM(gcd_test, gcd_gen_params),
+ KUNIT_CASE_PARAM(lcm_test, lcm_gen_params),
+ KUNIT_CASE_PARAM(reciprocal_div_test, reciprocal_div_gen_params),
+ KUNIT_CASE(reciprocal_scale_test),
+ {}
+};
+
+static struct kunit_suite math_test_suite = {
+ .name = "lib-math",
+ .test_cases = math_test_cases,
+};
+
+kunit_test_suites(&math_test_suite);
+
+MODULE_LICENSE("GPL v2");

base-commit: 7e25f40eab52c57ff6772d27d2aef3640a3237d7
--
2.31.1.368.gbe11c130af-goog


2021-04-17 04:18:32

by David Gow

[permalink] [raw]
Subject: Re: [PATCH v6] lib: add basic KUnit test for lib/math

On Sat, Apr 17, 2021 at 2:04 AM Daniel Latypov <[email protected]> wrote:
>
> Add basic test coverage for files that don't require any config options:
> * part of math.h (what seem to be the most commonly used macros)
> * gcd.c
> * lcm.c
> * int_sqrt.c
> * reciprocal_div.c
> (Ignored int_pow.c since it's a simple textbook algorithm.)
>
> These tests aren't particularly interesting, but they
> * provide short and simple examples of parameterized tests
> * provide a place to add tests for any new files in this dir
> * are written so adding new test cases to cover edge cases should be easy
> * looking at code coverage, we hit all the branches in the .c files
>
> Signed-off-by: Daniel Latypov <[email protected]>
> Reviewed-by: David Gow <[email protected]>
> ---

Thanks: I've tested this version, and am happy with it. A part of me
still kind-of would like there to be names for the parameters, but I
definitely understand that it doesn't really work well for the lcm and
gcd cases where we're doing both (a,b) and (b,a). So let's keep it
as-is.

Hopefully we can get these in for 5.13!

Cheers,
-- David

2021-04-17 06:01:20

by kernel test robot

[permalink] [raw]
Subject: Re: [PATCH v6] lib: add basic KUnit test for lib/math

Hi Daniel,

Thank you for the patch! Perhaps something to improve:

[auto build test WARNING on 7e25f40eab52c57ff6772d27d2aef3640a3237d7]

url: https://github.com/0day-ci/linux/commits/Daniel-Latypov/lib-add-basic-KUnit-test-for-lib-math/20210417-020619
base: 7e25f40eab52c57ff6772d27d2aef3640a3237d7
config: powerpc-randconfig-c004-20210416 (attached as .config)
compiler: powerpc-linux-gcc (GCC) 9.3.0
reproduce (this is a W=1 build):
wget https://raw.githubusercontent.com/intel/lkp-tests/master/sbin/make.cross -O ~/bin/make.cross
chmod +x ~/bin/make.cross
# https://github.com/0day-ci/linux/commit/0f1888ffeaa6baa1bc2a99eac8ba7d1df29c8450
git remote add linux-review https://github.com/0day-ci/linux
git fetch --no-tags linux-review Daniel-Latypov/lib-add-basic-KUnit-test-for-lib-math/20210417-020619
git checkout 0f1888ffeaa6baa1bc2a99eac8ba7d1df29c8450
# save the attached .config to linux build tree
COMPILER_INSTALL_PATH=$HOME/0day COMPILER=gcc-9.3.0 make.cross W=1 ARCH=powerpc

If you fix the issue, kindly add following tag as appropriate
Reported-by: kernel test robot <[email protected]>

All warnings (new ones prefixed by >>):

lib/math/math_kunit.c: In function 'abs_test':
>> lib/math/math_kunit.c:41:1: warning: the frame size of 1088 bytes is larger than 1024 bytes [-Wframe-larger-than=]
41 | }
| ^


vim +41 lib/math/math_kunit.c

14
15 static void abs_test(struct kunit *test)
16 {
17 KUNIT_EXPECT_EQ(test, abs((char)0), (char)0);
18 KUNIT_EXPECT_EQ(test, abs((char)42), (char)42);
19 KUNIT_EXPECT_EQ(test, abs((char)-42), (char)42);
20
21 /* The expression in the macro is actually promoted to an int. */
22 KUNIT_EXPECT_EQ(test, abs((short)0), 0);
23 KUNIT_EXPECT_EQ(test, abs((short)42), 42);
24 KUNIT_EXPECT_EQ(test, abs((short)-42), 42);
25
26 KUNIT_EXPECT_EQ(test, abs(0), 0);
27 KUNIT_EXPECT_EQ(test, abs(42), 42);
28 KUNIT_EXPECT_EQ(test, abs(-42), 42);
29
30 KUNIT_EXPECT_EQ(test, abs(0L), 0L);
31 KUNIT_EXPECT_EQ(test, abs(42L), 42L);
32 KUNIT_EXPECT_EQ(test, abs(-42L), 42L);
33
34 KUNIT_EXPECT_EQ(test, abs(0LL), 0LL);
35 KUNIT_EXPECT_EQ(test, abs(42LL), 42LL);
36 KUNIT_EXPECT_EQ(test, abs(-42LL), 42LL);
37
38 /* Unsigned types get casted to signed. */
39 KUNIT_EXPECT_EQ(test, abs(0ULL), 0LL);
40 KUNIT_EXPECT_EQ(test, abs(42ULL), 42LL);
> 41 }
42

---
0-DAY CI Kernel Test Service, Intel Corporation
https://lists.01.org/hyperkitty/list/[email protected]


Attachments:
(No filename) (2.63 kB)
.config.gz (31.49 kB)
Download all attachments

2024-05-16 10:20:11

by Devarsh Thakkar

[permalink] [raw]
Subject: Re: [PATCH v6] lib: add basic KUnit test for lib/math

Hi Daniel, Andy,

On 16/04/21 23:34, Daniel Latypov wrote:
> Add basic test coverage for files that don't require any config options:
> * part of math.h (what seem to be the most commonly used macros)
> * gcd.c
> * lcm.c
> * int_sqrt.c
> * reciprocal_div.c
> (Ignored int_pow.c since it's a simple textbook algorithm.)
>
> These tests aren't particularly interesting, but they
> * provide short and simple examples of parameterized tests
> * provide a place to add tests for any new files in this dir
> * are written so adding new test cases to cover edge cases should be easy
> * looking at code coverage, we hit all the branches in the .c files
>
> Signed-off-by: Daniel Latypov <[email protected]>
> Reviewed-by: David Gow <[email protected]>

Just checking if something else was pending on this patch-set for this not
getting merged?

I needed this patch-set for adding tests for new macros I am adding in math.h
as suggested in this thread [1], so wanted to pull this in my series and add
changes on top of that for new macros.

Kindly let me know your thoughts on this.

[1]: https://lore.kernel.org/all/[email protected]/#t

Regards
Devarsh
> ---
> Changes since v5:
> * add in test cases for roundup/rounddown
> * address misc comments from David
>
> Changes since v4:
> * add in test cases for some math.h macros (abs, round_up/round_down,
> div_round_down/closest)
> * use parameterized testing less to keep things terser
>
> Changes since v3:
> * fix `checkpatch.pl --strict` warnings
> * add test cases for gcd(0,0) and lcm(0,0)
> * minor: don't test both gcd(a,b) and gcd(b,a) when a == b
>
> Changes since v2: mv math_test.c => math_kunit.c
>
> Changes since v1:
> * Rebase and rewrite to use the new parameterized testing support.
> * misc: fix overflow in literal and inline int_sqrt format string.
> * related: commit 1f0e943df68a ("Documentation: kunit: provide guidance
> for testing many inputs") was merged explaining the patterns shown here.
> * there's an in-flight patch to update it for parameterized testing.
> ---
> lib/math/Kconfig | 12 ++
> lib/math/Makefile | 2 +
> lib/math/math_kunit.c | 291 ++++++++++++++++++++++++++++++++++++++++++
> 3 files changed, 305 insertions(+)
> create mode 100644 lib/math/math_kunit.c
>
> diff --git a/lib/math/Kconfig b/lib/math/Kconfig
> index f19bc9734fa7..a974d4db0f9c 100644
> --- a/lib/math/Kconfig
> +++ b/lib/math/Kconfig
> @@ -15,3 +15,15 @@ config PRIME_NUMBERS
>
> config RATIONAL
> bool
> +
> +config MATH_KUNIT_TEST
> + tristate "KUnit test for lib/math and math.h" if !KUNIT_ALL_TESTS
> + depends on KUNIT
> + default KUNIT_ALL_TESTS
> + help
> + This builds unit tests for lib/math and math.h.
> +
> + For more information on KUnit and unit tests in general, please refer
> + to the KUnit documentation in Documentation/dev-tools/kunit/.
> +
> + If unsure, say N.
> diff --git a/lib/math/Makefile b/lib/math/Makefile
> index be6909e943bd..30abb7a8d564 100644
> --- a/lib/math/Makefile
> +++ b/lib/math/Makefile
> @@ -4,3 +4,5 @@ obj-y += div64.o gcd.o lcm.o int_pow.o int_sqrt.o reciprocal_div.o
> obj-$(CONFIG_CORDIC) += cordic.o
> obj-$(CONFIG_PRIME_NUMBERS) += prime_numbers.o
> obj-$(CONFIG_RATIONAL) += rational.o
> +
> +obj-$(CONFIG_MATH_KUNIT_TEST) += math_kunit.o
> diff --git a/lib/math/math_kunit.c b/lib/math/math_kunit.c
> new file mode 100644
> index 000000000000..556c23b17c3c
> --- /dev/null
> +++ b/lib/math/math_kunit.c
> @@ -0,0 +1,291 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * Simple KUnit suite for math helper funcs that are always enabled.
> + *
> + * Copyright (C) 2020, Google LLC.
> + * Author: Daniel Latypov <[email protected]>
> + */
> +
> +#include <kunit/test.h>
> +#include <linux/gcd.h>
> +#include <linux/kernel.h>
> +#include <linux/lcm.h>
> +#include <linux/reciprocal_div.h>
> +
> +static void abs_test(struct kunit *test)
> +{
> + KUNIT_EXPECT_EQ(test, abs((char)0), (char)0);
> + KUNIT_EXPECT_EQ(test, abs((char)42), (char)42);
> + KUNIT_EXPECT_EQ(test, abs((char)-42), (char)42);
> +
> + /* The expression in the macro is actually promoted to an int. */
> + KUNIT_EXPECT_EQ(test, abs((short)0), 0);
> + KUNIT_EXPECT_EQ(test, abs((short)42), 42);
> + KUNIT_EXPECT_EQ(test, abs((short)-42), 42);
> +
> + KUNIT_EXPECT_EQ(test, abs(0), 0);
> + KUNIT_EXPECT_EQ(test, abs(42), 42);
> + KUNIT_EXPECT_EQ(test, abs(-42), 42);
> +
> + KUNIT_EXPECT_EQ(test, abs(0L), 0L);
> + KUNIT_EXPECT_EQ(test, abs(42L), 42L);
> + KUNIT_EXPECT_EQ(test, abs(-42L), 42L);
> +
> + KUNIT_EXPECT_EQ(test, abs(0LL), 0LL);
> + KUNIT_EXPECT_EQ(test, abs(42LL), 42LL);
> + KUNIT_EXPECT_EQ(test, abs(-42LL), 42LL);
> +
> + /* Unsigned types get casted to signed. */
> + KUNIT_EXPECT_EQ(test, abs(0ULL), 0LL);
> + KUNIT_EXPECT_EQ(test, abs(42ULL), 42LL);
> +}
> +
> +static void int_sqrt_test(struct kunit *test)
> +{
> + KUNIT_EXPECT_EQ(test, int_sqrt(0UL), 0UL);
> + KUNIT_EXPECT_EQ(test, int_sqrt(1UL), 1UL);
> + KUNIT_EXPECT_EQ(test, int_sqrt(4UL), 2UL);
> + KUNIT_EXPECT_EQ(test, int_sqrt(5UL), 2UL);
> + KUNIT_EXPECT_EQ(test, int_sqrt(8UL), 2UL);
> + KUNIT_EXPECT_EQ(test, int_sqrt(1UL << 30), 1UL << 15);
> +}
> +
> +static void round_up_test(struct kunit *test)
> +{
> + KUNIT_EXPECT_EQ(test, round_up(0, 1), 0);
> + KUNIT_EXPECT_EQ(test, round_up(1, 2), 2);
> + KUNIT_EXPECT_EQ(test, round_up(3, 2), 4);
> + KUNIT_EXPECT_EQ(test, round_up((1 << 30) - 1, 2), 1 << 30);
> + KUNIT_EXPECT_EQ(test, round_up((1 << 30) - 1, 1 << 29), 1 << 30);
> +}
> +
> +static void round_down_test(struct kunit *test)
> +{
> + KUNIT_EXPECT_EQ(test, round_down(0, 1), 0);
> + KUNIT_EXPECT_EQ(test, round_down(1, 2), 0);
> + KUNIT_EXPECT_EQ(test, round_down(3, 2), 2);
> + KUNIT_EXPECT_EQ(test, round_down((1 << 30) - 1, 2), (1 << 30) - 2);
> + KUNIT_EXPECT_EQ(test, round_down((1 << 30) - 1, 1 << 29), 1 << 29);
> +}
> +
> +/* These versions can round to numbers that aren't a power of two */
> +static void roundup_test(struct kunit *test)
> +{
> + KUNIT_EXPECT_EQ(test, roundup(0, 1), 0);
> + KUNIT_EXPECT_EQ(test, roundup(1, 2), 2);
> + KUNIT_EXPECT_EQ(test, roundup(3, 2), 4);
> + KUNIT_EXPECT_EQ(test, roundup((1 << 30) - 1, 2), 1 << 30);
> + KUNIT_EXPECT_EQ(test, roundup((1 << 30) - 1, 1 << 29), 1 << 30);
> +
> + KUNIT_EXPECT_EQ(test, roundup(3, 2), 4);
> + KUNIT_EXPECT_EQ(test, roundup(4, 3), 6);
> +}
> +
> +static void rounddown_test(struct kunit *test)
> +{
> + KUNIT_EXPECT_EQ(test, rounddown(0, 1), 0);
> + KUNIT_EXPECT_EQ(test, rounddown(1, 2), 0);
> + KUNIT_EXPECT_EQ(test, rounddown(3, 2), 2);
> + KUNIT_EXPECT_EQ(test, rounddown((1 << 30) - 1, 2), (1 << 30) - 2);
> + KUNIT_EXPECT_EQ(test, rounddown((1 << 30) - 1, 1 << 29), 1 << 29);
> +
> + KUNIT_EXPECT_EQ(test, rounddown(3, 2), 2);
> + KUNIT_EXPECT_EQ(test, rounddown(4, 3), 3);
> +}
> +
> +static void div_round_up_test(struct kunit *test)
> +{
> + KUNIT_EXPECT_EQ(test, DIV_ROUND_UP(0, 1), 0);
> + KUNIT_EXPECT_EQ(test, DIV_ROUND_UP(20, 10), 2);
> + KUNIT_EXPECT_EQ(test, DIV_ROUND_UP(21, 10), 3);
> + KUNIT_EXPECT_EQ(test, DIV_ROUND_UP(21, 20), 2);
> + KUNIT_EXPECT_EQ(test, DIV_ROUND_UP(21, 99), 1);
> +}
> +
> +static void div_round_closest_test(struct kunit *test)
> +{
> + KUNIT_EXPECT_EQ(test, DIV_ROUND_CLOSEST(0, 1), 0);
> + KUNIT_EXPECT_EQ(test, DIV_ROUND_CLOSEST(20, 10), 2);
> + KUNIT_EXPECT_EQ(test, DIV_ROUND_CLOSEST(21, 10), 2);
> + KUNIT_EXPECT_EQ(test, DIV_ROUND_CLOSEST(25, 10), 3);
> +}
> +
> +/* Generic test case for unsigned long inputs. */
> +struct test_case {
> + unsigned long a, b;
> + unsigned long result;
> +};
> +
> +static struct test_case gcd_cases[] = {
> + {
> + .a = 0, .b = 0,
> + .result = 0,
> + },
> + {
> + .a = 0, .b = 1,
> + .result = 1,
> + },
> + {
> + .a = 2, .b = 2,
> + .result = 2,
> + },
> + {
> + .a = 2, .b = 4,
> + .result = 2,
> + },
> + {
> + .a = 3, .b = 5,
> + .result = 1,
> + },
> + {
> + .a = 3 * 9, .b = 3 * 5,
> + .result = 3,
> + },
> + {
> + .a = 3 * 5 * 7, .b = 3 * 5 * 11,
> + .result = 15,
> + },
> + {
> + .a = 1 << 21,
> + .b = (1 << 21) - 1,
> + .result = 1,
> + },
> +};
> +
> +KUNIT_ARRAY_PARAM(gcd, gcd_cases, NULL);
> +
> +static void gcd_test(struct kunit *test)
> +{
> + const char *message_fmt = "gcd(%lu, %lu)";
> + const struct test_case *test_param = test->param_value;
> +
> + KUNIT_EXPECT_EQ_MSG(test, test_param->result,
> + gcd(test_param->a, test_param->b),
> + message_fmt, test_param->a,
> + test_param->b);
> +
> + if (test_param->a == test_param->b)
> + return;
> +
> + /* gcd(a,b) == gcd(b,a) */
> + KUNIT_EXPECT_EQ_MSG(test, test_param->result,
> + gcd(test_param->b, test_param->a),
> + message_fmt, test_param->b,
> + test_param->a);
> +}
> +
> +static struct test_case lcm_cases[] = {
> + {
> + .a = 0, .b = 0,
> + .result = 0,
> + },
> + {
> + .a = 0, .b = 1,
> + .result = 0,
> + },
> + {
> + .a = 1, .b = 2,
> + .result = 2,
> + },
> + {
> + .a = 2, .b = 2,
> + .result = 2,
> + },
> + {
> + .a = 3 * 5, .b = 3 * 7,
> + .result = 3 * 5 * 7,
> + },
> +};
> +
> +KUNIT_ARRAY_PARAM(lcm, lcm_cases, NULL);
> +
> +static void lcm_test(struct kunit *test)
> +{
> + const char *message_fmt = "lcm(%lu, %lu)";
> + const struct test_case *test_param = test->param_value;
> +
> + KUNIT_EXPECT_EQ_MSG(test, test_param->result,
> + lcm(test_param->a, test_param->b),
> + message_fmt, test_param->a,
> + test_param->b);
> +
> + if (test_param->a == test_param->b)
> + return;
> +
> + /* lcm(a,b) == lcm(b,a) */
> + KUNIT_EXPECT_EQ_MSG(test, test_param->result,
> + lcm(test_param->b, test_param->a),
> + message_fmt, test_param->b,
> + test_param->a);
> +}
> +
> +struct u32_test_case {
> + u32 a, b;
> + u32 result;
> +};
> +
> +static struct u32_test_case reciprocal_div_cases[] = {
> + {
> + .a = 0, .b = 1,
> + .result = 0,
> + },
> + {
> + .a = 42, .b = 20,
> + .result = 2,
> + },
> + {
> + .a = 42, .b = 9999,
> + .result = 0,
> + },
> + {
> + .a = (1 << 16), .b = (1 << 14),
> + .result = 1 << 2,
> + },
> +};
> +
> +KUNIT_ARRAY_PARAM(reciprocal_div, reciprocal_div_cases, NULL);
> +
> +static void reciprocal_div_test(struct kunit *test)
> +{
> + const struct u32_test_case *test_param = test->param_value;
> + struct reciprocal_value rv = reciprocal_value(test_param->b);
> +
> + KUNIT_EXPECT_EQ_MSG(test, test_param->result,
> + reciprocal_divide(test_param->a, rv),
> + "reciprocal_divide(%u, %u)",
> + test_param->a, test_param->b);
> +}
> +
> +static void reciprocal_scale_test(struct kunit *test)
> +{
> + KUNIT_EXPECT_EQ(test, reciprocal_scale(0u, 100), 0u);
> + KUNIT_EXPECT_EQ(test, reciprocal_scale(1u, 100), 0u);
> + KUNIT_EXPECT_EQ(test, reciprocal_scale(1u << 4, 1 << 28), 1u);
> + KUNIT_EXPECT_EQ(test, reciprocal_scale(1u << 16, 1 << 28), 1u << 12);
> + KUNIT_EXPECT_EQ(test, reciprocal_scale(~0u, 1 << 28), (1u << 28) - 1);
> +}
> +
> +static struct kunit_case math_test_cases[] = {
> + KUNIT_CASE(abs_test),
> + KUNIT_CASE(int_sqrt_test),
> + KUNIT_CASE(round_up_test),
> + KUNIT_CASE(round_down_test),
> + KUNIT_CASE(roundup_test),
> + KUNIT_CASE(rounddown_test),
> + KUNIT_CASE(div_round_up_test),
> + KUNIT_CASE(div_round_closest_test),
> + KUNIT_CASE_PARAM(gcd_test, gcd_gen_params),
> + KUNIT_CASE_PARAM(lcm_test, lcm_gen_params),
> + KUNIT_CASE_PARAM(reciprocal_div_test, reciprocal_div_gen_params),
> + KUNIT_CASE(reciprocal_scale_test),
> + {}
> +};
> +
> +static struct kunit_suite math_test_suite = {
> + .name = "lib-math",
> + .test_cases = math_test_cases,
> +};
> +
> +kunit_test_suites(&math_test_suite);
> +
> +MODULE_LICENSE("GPL v2");
>
> base-commit: 7e25f40eab52c57ff6772d27d2aef3640a3237d7

2024-05-16 11:36:44

by Andy Shevchenko

[permalink] [raw]
Subject: Re: [PATCH v6] lib: add basic KUnit test for lib/math

On Thu, May 16, 2024 at 03:49:44PM +0530, Devarsh Thakkar wrote:
> Hi Daniel, Andy,
>
> On 16/04/21 23:34, Daniel Latypov wrote:
> > Add basic test coverage for files that don't require any config options:
> > * part of math.h (what seem to be the most commonly used macros)
> > * gcd.c
> > * lcm.c
> > * int_sqrt.c
> > * reciprocal_div.c
> > (Ignored int_pow.c since it's a simple textbook algorithm.)
> >
> > These tests aren't particularly interesting, but they
> > * provide short and simple examples of parameterized tests
> > * provide a place to add tests for any new files in this dir
> > * are written so adding new test cases to cover edge cases should be easy
> > * looking at code coverage, we hit all the branches in the .c files
> >
> > Signed-off-by: Daniel Latypov <[email protected]>
> > Reviewed-by: David Gow <[email protected]>
>
> Just checking if something else was pending on this patch-set for this not
> getting merged?
>
> I needed this patch-set for adding tests for new macros I am adding in math.h
> as suggested in this thread [1], so wanted to pull this in my series and add
> changes on top of that for new macros.
>
> Kindly let me know your thoughts on this.

Wow, blast from the past!
But good finding, it would be good to have more math.h related test cases.

--
With Best Regards,
Andy Shevchenko



2024-05-16 15:17:23

by Daniel Latypov

[permalink] [raw]
Subject: Re: [PATCH v6] lib: add basic KUnit test for lib/math

On Thu, May 16, 2024 at 3:19 AM Devarsh Thakkar <[email protected]> wrote:
>
> Hi Daniel, Andy,
>
> On 16/04/21 23:34, Daniel Latypov wrote:
> > Add basic test coverage for files that don't require any config options:
> > * part of math.h (what seem to be the most commonly used macros)
> > * gcd.c
> > * lcm.c
> > * int_sqrt.c
> > * reciprocal_div.c
> > (Ignored int_pow.c since it's a simple textbook algorithm.)
> >
> > These tests aren't particularly interesting, but they
> > * provide short and simple examples of parameterized tests
> > * provide a place to add tests for any new files in this dir
> > * are written so adding new test cases to cover edge cases should be easy
> > * looking at code coverage, we hit all the branches in the .c files
> >
> > Signed-off-by: Daniel Latypov <[email protected]>
> > Reviewed-by: David Gow <[email protected]>
>
> Just checking if something else was pending on this patch-set for this not
> getting merged?
>
> I needed this patch-set for adding tests for new macros I am adding in math.h
> as suggested in this thread [1], so wanted to pull this in my series and add
> changes on top of that for new macros.
>
> Kindly let me know your thoughts on this.

This patch just fell through the cracks for me.
I had (wrongly) inferred that Andy might have had some lingering
reservations about this patch (that it was too contrived, might not be
useful to have tests for stuff like abs(), etc.).

Feel free to pull this into your series.

Looking over the code itself, I think this still looks valid and
stylistically correct with regard to KUnit.
I haven't gone and validated that it still compiles and runs just yet, though.
But if you do run into any problems, let me know and I can help send
you a fixed version.

Thanks for picking this up,
Daniel