2022-01-08 01:23:26

by Daniel Latypov

[permalink] [raw]
Subject: [PATCH 0/6] kunit: refactor assertions to use less stack

Every KUNIT_ASSERT/EXPECT() invocation puts a `kunit_assert` object onto
the stack. The most common one is `kunit_binary_assert` which is 88
bytes on UML. So in the cases where the compiler doesn't optimize this
away, we can very quickly blow up the stack size.

This series implements Linus' suggestion in [1].
Namely, we split out the file, line number, and assert_type
(EXPECT/ASSERT) out of kunit_assert.

We can also drop the entirely unused `struct kunit *test` field, saving
a bit more space as well.

All together, sizeof(struct kunit_assert) went from 48 to 24 on UML.
Note: the other assert types are bigger, see [2].

This series also adds in an example test that uses all the KUNIT_EXPECT
macros to both advertise their existence to new users and serve as a
smoketest for all these changes here.

[1] https://groups.google.com/g/kunit-dev/c/i3fZXgvBrfA/m/VULQg1z6BAAJ
[2] e.g. consider the most commonly used assert (also the biggest)
struct kunit_binary_assert {
struct kunit_assert assert;
const char *operation;
const char *left_text;
long long left_value;
const char *right_text;
long long right_value;
};
So sizeof(struct kunit_binary_assert) = went from 88 to 64.
I.e. only a 27% reduction instead of 50% in the most common case.

All 3 of the `const char*` could be split out into a `static` var as well,
but that's a bit trickier to do with how all the macros are written.

Daniel Latypov (6):
kunit: add example test case showing off all the expect macros
kunit: move check if assertion passed into the macros
kunit: drop unused kunit* field in kunit_assert
kunit: factor out kunit_base_assert_format() call into kunit_fail()
kunit: split out part of kunit_assert into a static const
kunit: drop unused assert_type from kunit_assert and clean up macros

include/kunit/assert.h | 88 +++++++++++-----------------------
include/kunit/test.h | 52 ++++++++++----------
lib/kunit/assert.c | 15 ++----
lib/kunit/kunit-example-test.c | 46 ++++++++++++++++++
lib/kunit/test.c | 27 +++++------
5 files changed, 120 insertions(+), 108 deletions(-)


base-commit: ad659ccb5412874c6a89d3588cb18857c00e9d0f
--
2.34.1.575.g55b058a8bb-goog



2022-01-08 01:23:30

by Daniel Latypov

[permalink] [raw]
Subject: [PATCH 1/6] kunit: add example test case showing off all the expect macros

Currently, these macros are only really documented near the bottom of
https://www.kernel.org/doc/html/latest/dev-tools/kunit/api/test.html#c.KUNIT_FAIL.

E.g. it's likely someone might just not realize that
KUNIT_EXPECT_STREQ() exists and instead use KUNIT_EXPECT_FALSE(strcmp())
or similar.

This can also serve as a basic smoketest that the KUnit assert machinery
still works for all the macros.

Signed-off-by: Daniel Latypov <[email protected]>
---
lib/kunit/kunit-example-test.c | 46 ++++++++++++++++++++++++++++++++++
1 file changed, 46 insertions(+)

diff --git a/lib/kunit/kunit-example-test.c b/lib/kunit/kunit-example-test.c
index 51099b0ca29c..182a64c12541 100644
--- a/lib/kunit/kunit-example-test.c
+++ b/lib/kunit/kunit-example-test.c
@@ -69,6 +69,51 @@ static void example_mark_skipped_test(struct kunit *test)
/* This line should run */
kunit_info(test, "You should see this line.");
}
+
+/*
+ * This test shows off all the KUNIT_EXPECT macros.
+ */
+static void example_all_expect_macros_test(struct kunit *test)
+{
+ KUNIT_EXPECT_TRUE(test, true);
+ KUNIT_EXPECT_FALSE(test, false);
+
+ KUNIT_EXPECT_EQ(test, 1, 1);
+ KUNIT_EXPECT_GE(test, 1, 1);
+ KUNIT_EXPECT_LE(test, 1, 1);
+ KUNIT_EXPECT_NE(test, 1, 0);
+ KUNIT_EXPECT_GT(test, 1, 0);
+ KUNIT_EXPECT_LT(test, 0, 1);
+
+ KUNIT_EXPECT_NOT_ERR_OR_NULL(test, test);
+ KUNIT_EXPECT_PTR_EQ(test, NULL, NULL);
+ KUNIT_EXPECT_PTR_NE(test, test, NULL);
+
+ KUNIT_EXPECT_STREQ(test, "hi", "hi");
+ KUNIT_EXPECT_STRNEQ(test, "hi", "bye");
+
+ /*
+ * There are also _MSG variants of all of the above that let you include
+ * additional text on failure.
+ */
+ KUNIT_EXPECT_TRUE_MSG(test, true, "msg");
+ KUNIT_EXPECT_FALSE_MSG(test, false, "msg");
+
+ KUNIT_EXPECT_EQ_MSG(test, 1, 1, "msg");
+ KUNIT_EXPECT_GE_MSG(test, 1, 1, "msg");
+ KUNIT_EXPECT_LE_MSG(test, 1, 1, "msg");
+ KUNIT_EXPECT_NE_MSG(test, 1, 0, "msg");
+ KUNIT_EXPECT_GT_MSG(test, 1, 0, "msg");
+ KUNIT_EXPECT_LT_MSG(test, 0, 1, "msg");
+
+ KUNIT_EXPECT_NOT_ERR_OR_NULL_MSG(test, test, "msg");
+ KUNIT_EXPECT_PTR_EQ_MSG(test, NULL, NULL, "msg");
+ KUNIT_EXPECT_PTR_NE_MSG(test, test, NULL, "msg");
+
+ KUNIT_EXPECT_STREQ_MSG(test, "hi", "hi", "msg");
+ KUNIT_EXPECT_STRNEQ_MSG(test, "hi", "bye", "msg");
+}
+
/*
* Here we make a list of all the test cases we want to add to the test suite
* below.
@@ -83,6 +128,7 @@ static struct kunit_case example_test_cases[] = {
KUNIT_CASE(example_simple_test),
KUNIT_CASE(example_skip_test),
KUNIT_CASE(example_mark_skipped_test),
+ KUNIT_CASE(example_all_expect_macros_test),
{}
};

--
2.34.1.575.g55b058a8bb-goog


2022-01-08 01:23:35

by Daniel Latypov

[permalink] [raw]
Subject: [PATCH 2/6] kunit: move check if assertion passed into the macros

Currently the code always calls kunit_do_assertion() even though it does
nothing when `pass` is true.

This change moves the `if(!(pass))` check into the macro instead
and renames the function to kunit_failed_assertion().
I feel this a bit easier to read and understand.

This has the potential upside of avoiding a function call that does
nothing most of the time (assuming your tests are passing) but comes
with the downside of generating a bit more code and branches.

This also means we don't have to initialize structs that we don't need,
which will become a tiny bit more expensive if we switch over to using
static variables to try and reduce stack usage. (There's runtime code
to check if the variable has been initialized yet or not).

Signed-off-by: Daniel Latypov <[email protected]>
---
include/kunit/test.h | 20 ++++++++++----------
lib/kunit/test.c | 13 ++++---------
2 files changed, 14 insertions(+), 19 deletions(-)

diff --git a/include/kunit/test.h b/include/kunit/test.h
index b26400731c02..690a28dfc795 100644
--- a/include/kunit/test.h
+++ b/include/kunit/test.h
@@ -770,18 +770,18 @@ void __printf(2, 3) kunit_log_append(char *log, const char *fmt, ...);
*/
#define KUNIT_SUCCEED(test) do {} while (0)

-void kunit_do_assertion(struct kunit *test,
- struct kunit_assert *assert,
- bool pass,
- const char *fmt, ...);
+void kunit_failed_assertion(struct kunit *test,
+ struct kunit_assert *assert,
+ const char *fmt, ...);

#define KUNIT_ASSERTION(test, pass, assert_class, INITIALIZER, fmt, ...) do { \
- struct assert_class __assertion = INITIALIZER; \
- kunit_do_assertion(test, \
- &__assertion.assert, \
- pass, \
- fmt, \
- ##__VA_ARGS__); \
+ if (!(pass)) { \
+ struct assert_class __assertion = INITIALIZER; \
+ kunit_failed_assertion(test, \
+ &__assertion.assert, \
+ fmt, \
+ ##__VA_ARGS__); \
+ } \
} while (0)


diff --git a/lib/kunit/test.c b/lib/kunit/test.c
index c7ed4aabec04..5ad671745483 100644
--- a/lib/kunit/test.c
+++ b/lib/kunit/test.c
@@ -275,16 +275,11 @@ static void __noreturn kunit_abort(struct kunit *test)
WARN_ONCE(true, "Throw could not abort from test!\n");
}

-void kunit_do_assertion(struct kunit *test,
- struct kunit_assert *assert,
- bool pass,
- const char *fmt, ...)
+void kunit_failed_assertion(struct kunit *test,
+ struct kunit_assert *assert,
+ const char *fmt, ...)
{
va_list args;
-
- if (pass)
- return;
-
va_start(args, fmt);

assert->message.fmt = fmt;
@@ -297,7 +292,7 @@ void kunit_do_assertion(struct kunit *test,
if (assert->type == KUNIT_ASSERTION)
kunit_abort(test);
}
-EXPORT_SYMBOL_GPL(kunit_do_assertion);
+EXPORT_SYMBOL_GPL(kunit_failed_assertion);

void kunit_init_test(struct kunit *test, const char *name, char *log)
{
--
2.34.1.575.g55b058a8bb-goog


2022-01-08 01:23:38

by Daniel Latypov

[permalink] [raw]
Subject: [PATCH 3/6] kunit: drop unused kunit* field in kunit_assert

The `struct kunit* test` field in kunit_assert is unused.
Note: we have access to `test` where we need it via the string_stream
object. I assume `test` in `kunit_assert` predates this and was leftover
after some refactoring.

This patch removes the field and cleans up the macros to avoid
needlessly passing around `test`.

Signed-off-by: Daniel Latypov <[email protected]>
---
include/kunit/assert.h | 45 ++++++++++++------------------------------
include/kunit/test.h | 14 +++++--------
2 files changed, 18 insertions(+), 41 deletions(-)

diff --git a/include/kunit/assert.h b/include/kunit/assert.h
index ad889b539ab3..3da6c792496c 100644
--- a/include/kunit/assert.h
+++ b/include/kunit/assert.h
@@ -30,7 +30,6 @@ enum kunit_assert_type {

/**
* struct kunit_assert - Data for printing a failed assertion or expectation.
- * @test: the test case this expectation/assertion is associated with.
* @type: the type (either an expectation or an assertion) of this kunit_assert.
* @line: the source code line number that the expectation/assertion is at.
* @file: the file path of the source file that the expectation/assertion is in.
@@ -41,7 +40,6 @@ enum kunit_assert_type {
* format a string to a user reporting the failure.
*/
struct kunit_assert {
- struct kunit *test;
enum kunit_assert_type type;
int line;
const char *file;
@@ -60,14 +58,12 @@ struct kunit_assert {

/**
* KUNIT_INIT_ASSERT_STRUCT() - Initializer for a &struct kunit_assert.
- * @kunit: The test case that this expectation/assertion is associated with.
* @assert_type: The type (assertion or expectation) of this kunit_assert.
* @fmt: The formatting function which builds a string out of this kunit_assert.
*
* The base initializer for a &struct kunit_assert.
*/
-#define KUNIT_INIT_ASSERT_STRUCT(kunit, assert_type, fmt) { \
- .test = kunit, \
+#define KUNIT_INIT_ASSERT_STRUCT(assert_type, fmt) { \
.type = assert_type, \
.file = __FILE__, \
.line = __LINE__, \
@@ -96,15 +92,13 @@ void kunit_fail_assert_format(const struct kunit_assert *assert,

/**
* KUNIT_INIT_FAIL_ASSERT_STRUCT() - Initializer for &struct kunit_fail_assert.
- * @test: The test case that this expectation/assertion is associated with.
* @type: The type (assertion or expectation) of this kunit_assert.
*
* Initializes a &struct kunit_fail_assert. Intended to be used in
* KUNIT_EXPECT_* and KUNIT_ASSERT_* macros.
*/
-#define KUNIT_INIT_FAIL_ASSERT_STRUCT(test, type) { \
- .assert = KUNIT_INIT_ASSERT_STRUCT(test, \
- type, \
+#define KUNIT_INIT_FAIL_ASSERT_STRUCT(type) { \
+ .assert = KUNIT_INIT_ASSERT_STRUCT(type, \
kunit_fail_assert_format) \
}

@@ -129,7 +123,6 @@ void kunit_unary_assert_format(const struct kunit_assert *assert,

/**
* KUNIT_INIT_UNARY_ASSERT_STRUCT() - Initializes &struct kunit_unary_assert.
- * @test: The test case that this expectation/assertion is associated with.
* @type: The type (assertion or expectation) of this kunit_assert.
* @cond: A string representation of the expression asserted true or false.
* @expect_true: True if of type KUNIT_{EXPECT|ASSERT}_TRUE, false otherwise.
@@ -137,9 +130,8 @@ void kunit_unary_assert_format(const struct kunit_assert *assert,
* Initializes a &struct kunit_unary_assert. Intended to be used in
* KUNIT_EXPECT_* and KUNIT_ASSERT_* macros.
*/
-#define KUNIT_INIT_UNARY_ASSERT_STRUCT(test, type, cond, expect_true) { \
- .assert = KUNIT_INIT_ASSERT_STRUCT(test, \
- type, \
+#define KUNIT_INIT_UNARY_ASSERT_STRUCT(type, cond, expect_true) { \
+ .assert = KUNIT_INIT_ASSERT_STRUCT(type, \
kunit_unary_assert_format), \
.condition = cond, \
.expected_true = expect_true \
@@ -167,7 +159,6 @@ void kunit_ptr_not_err_assert_format(const struct kunit_assert *assert,
/**
* KUNIT_INIT_PTR_NOT_ERR_ASSERT_STRUCT() - Initializes a
* &struct kunit_ptr_not_err_assert.
- * @test: The test case that this expectation/assertion is associated with.
* @type: The type (assertion or expectation) of this kunit_assert.
* @txt: A string representation of the expression passed to the expectation.
* @val: The actual evaluated pointer value of the expression.
@@ -175,9 +166,8 @@ void kunit_ptr_not_err_assert_format(const struct kunit_assert *assert,
* Initializes a &struct kunit_ptr_not_err_assert. Intended to be used in
* KUNIT_EXPECT_* and KUNIT_ASSERT_* macros.
*/
-#define KUNIT_INIT_PTR_NOT_ERR_STRUCT(test, type, txt, val) { \
- .assert = KUNIT_INIT_ASSERT_STRUCT(test, \
- type, \
+#define KUNIT_INIT_PTR_NOT_ERR_STRUCT(type, txt, val) { \
+ .assert = KUNIT_INIT_ASSERT_STRUCT(type, \
kunit_ptr_not_err_assert_format), \
.text = txt, \
.value = val \
@@ -212,7 +202,6 @@ void kunit_binary_assert_format(const struct kunit_assert *assert,
/**
* KUNIT_INIT_BINARY_ASSERT_STRUCT() - Initializes a
* &struct kunit_binary_assert.
- * @test: The test case that this expectation/assertion is associated with.
* @type: The type (assertion or expectation) of this kunit_assert.
* @op_str: A string representation of the comparison operator (e.g. "==").
* @left_str: A string representation of the expression in the left slot.
@@ -223,15 +212,13 @@ void kunit_binary_assert_format(const struct kunit_assert *assert,
* Initializes a &struct kunit_binary_assert. Intended to be used in
* KUNIT_EXPECT_* and KUNIT_ASSERT_* macros.
*/
-#define KUNIT_INIT_BINARY_ASSERT_STRUCT(test, \
- type, \
+#define KUNIT_INIT_BINARY_ASSERT_STRUCT(type, \
op_str, \
left_str, \
left_val, \
right_str, \
right_val) { \
- .assert = KUNIT_INIT_ASSERT_STRUCT(test, \
- type, \
+ .assert = KUNIT_INIT_ASSERT_STRUCT(type, \
kunit_binary_assert_format), \
.operation = op_str, \
.left_text = left_str, \
@@ -269,7 +256,6 @@ void kunit_binary_ptr_assert_format(const struct kunit_assert *assert,
/**
* KUNIT_INIT_BINARY_PTR_ASSERT_STRUCT() - Initializes a
* &struct kunit_binary_ptr_assert.
- * @test: The test case that this expectation/assertion is associated with.
* @type: The type (assertion or expectation) of this kunit_assert.
* @op_str: A string representation of the comparison operator (e.g. "==").
* @left_str: A string representation of the expression in the left slot.
@@ -280,15 +266,13 @@ void kunit_binary_ptr_assert_format(const struct kunit_assert *assert,
* Initializes a &struct kunit_binary_ptr_assert. Intended to be used in
* KUNIT_EXPECT_* and KUNIT_ASSERT_* macros.
*/
-#define KUNIT_INIT_BINARY_PTR_ASSERT_STRUCT(test, \
- type, \
+#define KUNIT_INIT_BINARY_PTR_ASSERT_STRUCT(type, \
op_str, \
left_str, \
left_val, \
right_str, \
right_val) { \
- .assert = KUNIT_INIT_ASSERT_STRUCT(test, \
- type, \
+ .assert = KUNIT_INIT_ASSERT_STRUCT(type, \
kunit_binary_ptr_assert_format), \
.operation = op_str, \
.left_text = left_str, \
@@ -326,7 +310,6 @@ void kunit_binary_str_assert_format(const struct kunit_assert *assert,
/**
* KUNIT_INIT_BINARY_STR_ASSERT_STRUCT() - Initializes a
* &struct kunit_binary_str_assert.
- * @test: The test case that this expectation/assertion is associated with.
* @type: The type (assertion or expectation) of this kunit_assert.
* @op_str: A string representation of the comparison operator (e.g. "==").
* @left_str: A string representation of the expression in the left slot.
@@ -337,15 +320,13 @@ void kunit_binary_str_assert_format(const struct kunit_assert *assert,
* Initializes a &struct kunit_binary_str_assert. Intended to be used in
* KUNIT_EXPECT_* and KUNIT_ASSERT_* macros.
*/
-#define KUNIT_INIT_BINARY_STR_ASSERT_STRUCT(test, \
- type, \
+#define KUNIT_INIT_BINARY_STR_ASSERT_STRUCT(type, \
op_str, \
left_str, \
left_val, \
right_str, \
right_val) { \
- .assert = KUNIT_INIT_ASSERT_STRUCT(test, \
- type, \
+ .assert = KUNIT_INIT_ASSERT_STRUCT(type, \
kunit_binary_str_assert_format), \
.operation = op_str, \
.left_text = left_str, \
diff --git a/include/kunit/test.h b/include/kunit/test.h
index 690a28dfc795..ebd45593321e 100644
--- a/include/kunit/test.h
+++ b/include/kunit/test.h
@@ -789,7 +789,7 @@ void kunit_failed_assertion(struct kunit *test,
KUNIT_ASSERTION(test, \
false, \
kunit_fail_assert, \
- KUNIT_INIT_FAIL_ASSERT_STRUCT(test, assert_type), \
+ KUNIT_INIT_FAIL_ASSERT_STRUCT(assert_type), \
fmt, \
##__VA_ARGS__)

@@ -819,8 +819,7 @@ void kunit_failed_assertion(struct kunit *test,
KUNIT_ASSERTION(test, \
!!(condition) == !!expected_true, \
kunit_unary_assert, \
- KUNIT_INIT_UNARY_ASSERT_STRUCT(test, \
- assert_type, \
+ KUNIT_INIT_UNARY_ASSERT_STRUCT(assert_type, \
#condition, \
expected_true), \
fmt, \
@@ -878,8 +877,7 @@ do { \
KUNIT_ASSERTION(test, \
__left op __right, \
assert_class, \
- ASSERT_CLASS_INIT(test, \
- assert_type, \
+ ASSERT_CLASS_INIT(assert_type, \
#op, \
#left, \
__left, \
@@ -1233,8 +1231,7 @@ do { \
KUNIT_ASSERTION(test, \
strcmp(__left, __right) op 0, \
kunit_binary_str_assert, \
- KUNIT_INIT_BINARY_STR_ASSERT_STRUCT(test, \
- assert_type, \
+ KUNIT_INIT_BINARY_STR_ASSERT_STRUCT(assert_type, \
#op, \
#left, \
__left, \
@@ -1293,8 +1290,7 @@ do { \
KUNIT_ASSERTION(test, \
!IS_ERR_OR_NULL(__ptr), \
kunit_ptr_not_err_assert, \
- KUNIT_INIT_PTR_NOT_ERR_STRUCT(test, \
- assert_type, \
+ KUNIT_INIT_PTR_NOT_ERR_STRUCT(assert_type, \
#ptr, \
__ptr), \
fmt, \
--
2.34.1.575.g55b058a8bb-goog


2022-01-08 01:23:41

by Daniel Latypov

[permalink] [raw]
Subject: [PATCH 4/6] kunit: factor out kunit_base_assert_format() call into kunit_fail()

We call this function first thing for all the assertion `format()`
functions.
This is the part that prints the file and line number and assertion type
(EXPECTATION, ASSERTION).

Having it as part of the format functions lets us have the flexibility
to not print that information (or print it differently) for new
assertion types, but I think this we don't need that.

And in the future, we'd like to consider factoring that data (file,
line#, type) out of the kunit_assert struct and into a `static`
variable, as Linus suggested [1], so we'd need to extract it anyways.

[1] https://groups.google.com/g/kunit-dev/c/i3fZXgvBrfA/m/VULQg1z6BAAJ

Signed-off-by: Daniel Latypov <[email protected]>
---
lib/kunit/assert.c | 6 ------
lib/kunit/test.c | 1 +
2 files changed, 1 insertion(+), 6 deletions(-)

diff --git a/lib/kunit/assert.c b/lib/kunit/assert.c
index b972bda61c0c..4d9a1295efc7 100644
--- a/lib/kunit/assert.c
+++ b/lib/kunit/assert.c
@@ -40,7 +40,6 @@ EXPORT_SYMBOL_GPL(kunit_assert_print_msg);
void kunit_fail_assert_format(const struct kunit_assert *assert,
struct string_stream *stream)
{
- kunit_base_assert_format(assert, stream);
string_stream_add(stream, "%pV", &assert->message);
}
EXPORT_SYMBOL_GPL(kunit_fail_assert_format);
@@ -52,7 +51,6 @@ void kunit_unary_assert_format(const struct kunit_assert *assert,

unary_assert = container_of(assert, struct kunit_unary_assert, assert);

- kunit_base_assert_format(assert, stream);
if (unary_assert->expected_true)
string_stream_add(stream,
KUNIT_SUBTEST_INDENT "Expected %s to be true, but is false\n",
@@ -73,7 +71,6 @@ void kunit_ptr_not_err_assert_format(const struct kunit_assert *assert,
ptr_assert = container_of(assert, struct kunit_ptr_not_err_assert,
assert);

- kunit_base_assert_format(assert, stream);
if (!ptr_assert->value) {
string_stream_add(stream,
KUNIT_SUBTEST_INDENT "Expected %s is not null, but is\n",
@@ -119,7 +116,6 @@ void kunit_binary_assert_format(const struct kunit_assert *assert,
binary_assert = container_of(assert, struct kunit_binary_assert,
assert);

- kunit_base_assert_format(assert, stream);
string_stream_add(stream,
KUNIT_SUBTEST_INDENT "Expected %s %s %s, but\n",
binary_assert->left_text,
@@ -147,7 +143,6 @@ void kunit_binary_ptr_assert_format(const struct kunit_assert *assert,
binary_assert = container_of(assert, struct kunit_binary_ptr_assert,
assert);

- kunit_base_assert_format(assert, stream);
string_stream_add(stream,
KUNIT_SUBTEST_INDENT "Expected %s %s %s, but\n",
binary_assert->left_text,
@@ -187,7 +182,6 @@ void kunit_binary_str_assert_format(const struct kunit_assert *assert,
binary_assert = container_of(assert, struct kunit_binary_str_assert,
assert);

- kunit_base_assert_format(assert, stream);
string_stream_add(stream,
KUNIT_SUBTEST_INDENT "Expected %s %s %s, but\n",
binary_assert->left_text,
diff --git a/lib/kunit/test.c b/lib/kunit/test.c
index 5ad671745483..735c1b67d843 100644
--- a/lib/kunit/test.c
+++ b/lib/kunit/test.c
@@ -255,6 +255,7 @@ static void kunit_fail(struct kunit *test, struct kunit_assert *assert)
return;
}

+ kunit_base_assert_format(assert, stream);
assert->format(assert, stream);

kunit_print_string_stream(test, stream);
--
2.34.1.575.g55b058a8bb-goog


2022-01-08 01:23:43

by Daniel Latypov

[permalink] [raw]
Subject: [PATCH 5/6] kunit: split out part of kunit_assert into a static const

This is per Linus's suggestion in [1].

The issue there is that every KUNIT_EXPECT/KUNIT_ASSERT puts a
kunit_assert object onto the stack. Normally we rely on compilers to
elide this, but when that doesn't work out, this blows up the stack
usage of kunit test functions.

We can move some data off the stack by making it static.
This change introduces a new `struct kunit_loc` to hold the file and
line number and then just passing assert_type (EXPECT or ASSERT) as an
argument.

In [1], it was suggested to also move out the format string as well, but
users could theoretically craft a format string at runtime, so we can't.

This change leaves a copy of `assert_type` in kunit_assert for now
because cleaning up all the macros to not pass it around is a bit more
involved.

Here's an example of the expanded code for KUNIT_FAIL():
if (!(false)) {
static const struct kunit_loc loc = { .file = ... };
struct kunit_unary_assert __assertion = { .assert = { .type ... };
kunit_failed_assertion(test, &loc, &__assertion.assert, ((void *)0));
};

[1] https://groups.google.com/g/kunit-dev/c/i3fZXgvBrfA/m/VULQg1z6BAAJ

Signed-off-by: Daniel Latypov <[email protected]>
Suggested-by: Linus Torvalds <[email protected]>
---
include/kunit/assert.h | 25 ++++++++++++++++---------
include/kunit/test.h | 12 +++++++++++-
lib/kunit/assert.c | 9 +++++----
lib/kunit/test.c | 15 +++++++++------
4 files changed, 41 insertions(+), 20 deletions(-)

diff --git a/include/kunit/assert.h b/include/kunit/assert.h
index 3da6c792496c..4f91dbdb886a 100644
--- a/include/kunit/assert.h
+++ b/include/kunit/assert.h
@@ -28,11 +28,21 @@ enum kunit_assert_type {
KUNIT_EXPECTATION,
};

+/**
+ * struct kunit_loc - Identifies the source location of a line of code.
+ * @line: the line number in the file.
+ * @file: the file name.
+ */
+struct kunit_loc {
+ int line;
+ const char *file;
+};
+
+#define KUNIT_CURRENT_LOC { .file = __FILE__, .line = __LINE__ }
+
/**
* struct kunit_assert - Data for printing a failed assertion or expectation.
* @type: the type (either an expectation or an assertion) of this kunit_assert.
- * @line: the source code line number that the expectation/assertion is at.
- * @file: the file path of the source file that the expectation/assertion is in.
* @message: an optional message to provide additional context.
* @format: a function which formats the data in this kunit_assert to a string.
*
@@ -40,9 +50,7 @@ enum kunit_assert_type {
* format a string to a user reporting the failure.
*/
struct kunit_assert {
- enum kunit_assert_type type;
- int line;
- const char *file;
+ enum kunit_assert_type type; // TODO([email protected]): delete this
struct va_format message;
void (*format)(const struct kunit_assert *assert,
struct string_stream *stream);
@@ -65,14 +73,13 @@ struct kunit_assert {
*/
#define KUNIT_INIT_ASSERT_STRUCT(assert_type, fmt) { \
.type = assert_type, \
- .file = __FILE__, \
- .line = __LINE__, \
.message = KUNIT_INIT_VA_FMT_NULL, \
.format = fmt \
}

-void kunit_base_assert_format(const struct kunit_assert *assert,
- struct string_stream *stream);
+void kunit_assert_prologue(const struct kunit_loc *loc,
+ enum kunit_assert_type type,
+ struct string_stream *stream);

void kunit_assert_print_msg(const struct kunit_assert *assert,
struct string_stream *stream);
diff --git a/include/kunit/test.h b/include/kunit/test.h
index ebd45593321e..6e201b45ada6 100644
--- a/include/kunit/test.h
+++ b/include/kunit/test.h
@@ -771,13 +771,18 @@ void __printf(2, 3) kunit_log_append(char *log, const char *fmt, ...);
#define KUNIT_SUCCEED(test) do {} while (0)

void kunit_failed_assertion(struct kunit *test,
+ const struct kunit_loc *loc,
+ enum kunit_assert_type type,
struct kunit_assert *assert,
const char *fmt, ...);

-#define KUNIT_ASSERTION(test, pass, assert_class, INITIALIZER, fmt, ...) do { \
+#define KUNIT_ASSERTION(test, assert_type, pass, assert_class, INITIALIZER, fmt, ...) do { \
if (!(pass)) { \
+ static const struct kunit_loc loc = KUNIT_CURRENT_LOC; \
struct assert_class __assertion = INITIALIZER; \
kunit_failed_assertion(test, \
+ &loc, \
+ assert_type, \
&__assertion.assert, \
fmt, \
##__VA_ARGS__); \
@@ -787,6 +792,7 @@ void kunit_failed_assertion(struct kunit *test,

#define KUNIT_FAIL_ASSERTION(test, assert_type, fmt, ...) \
KUNIT_ASSERTION(test, \
+ assert_type, \
false, \
kunit_fail_assert, \
KUNIT_INIT_FAIL_ASSERT_STRUCT(assert_type), \
@@ -817,6 +823,7 @@ void kunit_failed_assertion(struct kunit *test,
fmt, \
...) \
KUNIT_ASSERTION(test, \
+ assert_type, \
!!(condition) == !!expected_true, \
kunit_unary_assert, \
KUNIT_INIT_UNARY_ASSERT_STRUCT(assert_type, \
@@ -875,6 +882,7 @@ do { \
typeof(right) __right = (right); \
\
KUNIT_ASSERTION(test, \
+ assert_type, \
__left op __right, \
assert_class, \
ASSERT_CLASS_INIT(assert_type, \
@@ -1229,6 +1237,7 @@ do { \
const char *__right = (right); \
\
KUNIT_ASSERTION(test, \
+ assert_type, \
strcmp(__left, __right) op 0, \
kunit_binary_str_assert, \
KUNIT_INIT_BINARY_STR_ASSERT_STRUCT(assert_type, \
@@ -1288,6 +1297,7 @@ do { \
typeof(ptr) __ptr = (ptr); \
\
KUNIT_ASSERTION(test, \
+ assert_type, \
!IS_ERR_OR_NULL(__ptr), \
kunit_ptr_not_err_assert, \
KUNIT_INIT_PTR_NOT_ERR_STRUCT(assert_type, \
diff --git a/lib/kunit/assert.c b/lib/kunit/assert.c
index 4d9a1295efc7..9f4492a8e24e 100644
--- a/lib/kunit/assert.c
+++ b/lib/kunit/assert.c
@@ -10,12 +10,13 @@

#include "string-stream.h"

-void kunit_base_assert_format(const struct kunit_assert *assert,
+void kunit_assert_prologue(const struct kunit_loc *loc,
+ enum kunit_assert_type type,
struct string_stream *stream)
{
const char *expect_or_assert = NULL;

- switch (assert->type) {
+ switch (type) {
case KUNIT_EXPECTATION:
expect_or_assert = "EXPECTATION";
break;
@@ -25,9 +26,9 @@ void kunit_base_assert_format(const struct kunit_assert *assert,
}

string_stream_add(stream, "%s FAILED at %s:%d\n",
- expect_or_assert, assert->file, assert->line);
+ expect_or_assert, loc->file, loc->line);
}
-EXPORT_SYMBOL_GPL(kunit_base_assert_format);
+EXPORT_SYMBOL_GPL(kunit_assert_prologue);

void kunit_assert_print_msg(const struct kunit_assert *assert,
struct string_stream *stream)
diff --git a/lib/kunit/test.c b/lib/kunit/test.c
index 735c1b67d843..3108ed0575d4 100644
--- a/lib/kunit/test.c
+++ b/lib/kunit/test.c
@@ -240,7 +240,8 @@ static void kunit_print_string_stream(struct kunit *test,
}
}

-static void kunit_fail(struct kunit *test, struct kunit_assert *assert)
+static void kunit_fail(struct kunit *test, const struct kunit_loc *loc,
+ enum kunit_assert_type type, struct kunit_assert *assert)
{
struct string_stream *stream;

@@ -250,12 +251,12 @@ static void kunit_fail(struct kunit *test, struct kunit_assert *assert)
if (!stream) {
WARN(true,
"Could not allocate stream to print failed assertion in %s:%d\n",
- assert->file,
- assert->line);
+ loc->file,
+ loc->line);
return;
}

- kunit_base_assert_format(assert, stream);
+ kunit_assert_prologue(loc, type, stream);
assert->format(assert, stream);

kunit_print_string_stream(test, stream);
@@ -277,6 +278,8 @@ static void __noreturn kunit_abort(struct kunit *test)
}

void kunit_failed_assertion(struct kunit *test,
+ const struct kunit_loc *loc,
+ enum kunit_assert_type type,
struct kunit_assert *assert,
const char *fmt, ...)
{
@@ -286,11 +289,11 @@ void kunit_failed_assertion(struct kunit *test,
assert->message.fmt = fmt;
assert->message.va = &args;

- kunit_fail(test, assert);
+ kunit_fail(test, loc, type, assert);

va_end(args);

- if (assert->type == KUNIT_ASSERTION)
+ if (type == KUNIT_ASSERTION)
kunit_abort(test);
}
EXPORT_SYMBOL_GPL(kunit_failed_assertion);
--
2.34.1.575.g55b058a8bb-goog


2022-01-08 01:23:47

by Daniel Latypov

[permalink] [raw]
Subject: [PATCH 6/6] kunit: drop unused assert_type from kunit_assert and clean up macros

This field has been split out from kunit_assert to make the struct less
heavy along with the filename and line number.

This change drops the assert_type field and cleans up all the macros
that were plumbing assert_type into kunit_assert.

Signed-off-by: Daniel Latypov <[email protected]>
---
include/kunit/assert.h | 46 +++++++++++++-----------------------------
include/kunit/test.h | 14 +++++--------
2 files changed, 19 insertions(+), 41 deletions(-)

diff --git a/include/kunit/assert.h b/include/kunit/assert.h
index 4f91dbdb886a..21299232c120 100644
--- a/include/kunit/assert.h
+++ b/include/kunit/assert.h
@@ -42,7 +42,6 @@ struct kunit_loc {

/**
* struct kunit_assert - Data for printing a failed assertion or expectation.
- * @type: the type (either an expectation or an assertion) of this kunit_assert.
* @message: an optional message to provide additional context.
* @format: a function which formats the data in this kunit_assert to a string.
*
@@ -50,7 +49,6 @@ struct kunit_loc {
* format a string to a user reporting the failure.
*/
struct kunit_assert {
- enum kunit_assert_type type; // TODO([email protected]): delete this
struct va_format message;
void (*format)(const struct kunit_assert *assert,
struct string_stream *stream);
@@ -66,13 +64,11 @@ struct kunit_assert {

/**
* KUNIT_INIT_ASSERT_STRUCT() - Initializer for a &struct kunit_assert.
- * @assert_type: The type (assertion or expectation) of this kunit_assert.
* @fmt: The formatting function which builds a string out of this kunit_assert.
*
* The base initializer for a &struct kunit_assert.
*/
-#define KUNIT_INIT_ASSERT_STRUCT(assert_type, fmt) { \
- .type = assert_type, \
+#define KUNIT_INIT_ASSERT_STRUCT(fmt) { \
.message = KUNIT_INIT_VA_FMT_NULL, \
.format = fmt \
}
@@ -98,15 +94,13 @@ void kunit_fail_assert_format(const struct kunit_assert *assert,
struct string_stream *stream);

/**
- * KUNIT_INIT_FAIL_ASSERT_STRUCT() - Initializer for &struct kunit_fail_assert.
- * @type: The type (assertion or expectation) of this kunit_assert.
+ * KUNIT_INIT_FAIL_ASSERT_STRUCT - Initializer for &struct kunit_fail_assert.
*
* Initializes a &struct kunit_fail_assert. Intended to be used in
* KUNIT_EXPECT_* and KUNIT_ASSERT_* macros.
*/
-#define KUNIT_INIT_FAIL_ASSERT_STRUCT(type) { \
- .assert = KUNIT_INIT_ASSERT_STRUCT(type, \
- kunit_fail_assert_format) \
+#define KUNIT_INIT_FAIL_ASSERT_STRUCT { \
+ .assert = KUNIT_INIT_ASSERT_STRUCT(kunit_fail_assert_format) \
}

/**
@@ -130,16 +124,14 @@ void kunit_unary_assert_format(const struct kunit_assert *assert,

/**
* KUNIT_INIT_UNARY_ASSERT_STRUCT() - Initializes &struct kunit_unary_assert.
- * @type: The type (assertion or expectation) of this kunit_assert.
* @cond: A string representation of the expression asserted true or false.
* @expect_true: True if of type KUNIT_{EXPECT|ASSERT}_TRUE, false otherwise.
*
* Initializes a &struct kunit_unary_assert. Intended to be used in
* KUNIT_EXPECT_* and KUNIT_ASSERT_* macros.
*/
-#define KUNIT_INIT_UNARY_ASSERT_STRUCT(type, cond, expect_true) { \
- .assert = KUNIT_INIT_ASSERT_STRUCT(type, \
- kunit_unary_assert_format), \
+#define KUNIT_INIT_UNARY_ASSERT_STRUCT(cond, expect_true) { \
+ .assert = KUNIT_INIT_ASSERT_STRUCT(kunit_unary_assert_format), \
.condition = cond, \
.expected_true = expect_true \
}
@@ -166,16 +158,14 @@ void kunit_ptr_not_err_assert_format(const struct kunit_assert *assert,
/**
* KUNIT_INIT_PTR_NOT_ERR_ASSERT_STRUCT() - Initializes a
* &struct kunit_ptr_not_err_assert.
- * @type: The type (assertion or expectation) of this kunit_assert.
* @txt: A string representation of the expression passed to the expectation.
* @val: The actual evaluated pointer value of the expression.
*
* Initializes a &struct kunit_ptr_not_err_assert. Intended to be used in
* KUNIT_EXPECT_* and KUNIT_ASSERT_* macros.
*/
-#define KUNIT_INIT_PTR_NOT_ERR_STRUCT(type, txt, val) { \
- .assert = KUNIT_INIT_ASSERT_STRUCT(type, \
- kunit_ptr_not_err_assert_format), \
+#define KUNIT_INIT_PTR_NOT_ERR_STRUCT(txt, val) { \
+ .assert = KUNIT_INIT_ASSERT_STRUCT(kunit_ptr_not_err_assert_format), \
.text = txt, \
.value = val \
}
@@ -209,7 +199,6 @@ void kunit_binary_assert_format(const struct kunit_assert *assert,
/**
* KUNIT_INIT_BINARY_ASSERT_STRUCT() - Initializes a
* &struct kunit_binary_assert.
- * @type: The type (assertion or expectation) of this kunit_assert.
* @op_str: A string representation of the comparison operator (e.g. "==").
* @left_str: A string representation of the expression in the left slot.
* @left_val: The actual evaluated value of the expression in the left slot.
@@ -219,14 +208,12 @@ void kunit_binary_assert_format(const struct kunit_assert *assert,
* Initializes a &struct kunit_binary_assert. Intended to be used in
* KUNIT_EXPECT_* and KUNIT_ASSERT_* macros.
*/
-#define KUNIT_INIT_BINARY_ASSERT_STRUCT(type, \
- op_str, \
+#define KUNIT_INIT_BINARY_ASSERT_STRUCT(op_str, \
left_str, \
left_val, \
right_str, \
right_val) { \
- .assert = KUNIT_INIT_ASSERT_STRUCT(type, \
- kunit_binary_assert_format), \
+ .assert = KUNIT_INIT_ASSERT_STRUCT(kunit_binary_assert_format), \
.operation = op_str, \
.left_text = left_str, \
.left_value = left_val, \
@@ -273,14 +260,12 @@ void kunit_binary_ptr_assert_format(const struct kunit_assert *assert,
* Initializes a &struct kunit_binary_ptr_assert. Intended to be used in
* KUNIT_EXPECT_* and KUNIT_ASSERT_* macros.
*/
-#define KUNIT_INIT_BINARY_PTR_ASSERT_STRUCT(type, \
- op_str, \
+#define KUNIT_INIT_BINARY_PTR_ASSERT_STRUCT(op_str, \
left_str, \
left_val, \
right_str, \
right_val) { \
- .assert = KUNIT_INIT_ASSERT_STRUCT(type, \
- kunit_binary_ptr_assert_format), \
+ .assert = KUNIT_INIT_ASSERT_STRUCT(kunit_binary_ptr_assert_format), \
.operation = op_str, \
.left_text = left_str, \
.left_value = left_val, \
@@ -317,7 +302,6 @@ void kunit_binary_str_assert_format(const struct kunit_assert *assert,
/**
* KUNIT_INIT_BINARY_STR_ASSERT_STRUCT() - Initializes a
* &struct kunit_binary_str_assert.
- * @type: The type (assertion or expectation) of this kunit_assert.
* @op_str: A string representation of the comparison operator (e.g. "==").
* @left_str: A string representation of the expression in the left slot.
* @left_val: The actual evaluated value of the expression in the left slot.
@@ -327,14 +311,12 @@ void kunit_binary_str_assert_format(const struct kunit_assert *assert,
* Initializes a &struct kunit_binary_str_assert. Intended to be used in
* KUNIT_EXPECT_* and KUNIT_ASSERT_* macros.
*/
-#define KUNIT_INIT_BINARY_STR_ASSERT_STRUCT(type, \
- op_str, \
+#define KUNIT_INIT_BINARY_STR_ASSERT_STRUCT(op_str, \
left_str, \
left_val, \
right_str, \
right_val) { \
- .assert = KUNIT_INIT_ASSERT_STRUCT(type, \
- kunit_binary_str_assert_format), \
+ .assert = KUNIT_INIT_ASSERT_STRUCT(kunit_binary_str_assert_format), \
.operation = op_str, \
.left_text = left_str, \
.left_value = left_val, \
diff --git a/include/kunit/test.h b/include/kunit/test.h
index 6e201b45ada6..6f9074ec1995 100644
--- a/include/kunit/test.h
+++ b/include/kunit/test.h
@@ -795,7 +795,7 @@ void kunit_failed_assertion(struct kunit *test,
assert_type, \
false, \
kunit_fail_assert, \
- KUNIT_INIT_FAIL_ASSERT_STRUCT(assert_type), \
+ KUNIT_INIT_FAIL_ASSERT_STRUCT, \
fmt, \
##__VA_ARGS__)

@@ -826,8 +826,7 @@ void kunit_failed_assertion(struct kunit *test,
assert_type, \
!!(condition) == !!expected_true, \
kunit_unary_assert, \
- KUNIT_INIT_UNARY_ASSERT_STRUCT(assert_type, \
- #condition, \
+ KUNIT_INIT_UNARY_ASSERT_STRUCT(#condition, \
expected_true), \
fmt, \
##__VA_ARGS__)
@@ -885,8 +884,7 @@ do { \
assert_type, \
__left op __right, \
assert_class, \
- ASSERT_CLASS_INIT(assert_type, \
- #op, \
+ ASSERT_CLASS_INIT(#op, \
#left, \
__left, \
#right, \
@@ -1240,8 +1238,7 @@ do { \
assert_type, \
strcmp(__left, __right) op 0, \
kunit_binary_str_assert, \
- KUNIT_INIT_BINARY_STR_ASSERT_STRUCT(assert_type, \
- #op, \
+ KUNIT_INIT_BINARY_STR_ASSERT_STRUCT(#op, \
#left, \
__left, \
#right, \
@@ -1300,8 +1297,7 @@ do { \
assert_type, \
!IS_ERR_OR_NULL(__ptr), \
kunit_ptr_not_err_assert, \
- KUNIT_INIT_PTR_NOT_ERR_STRUCT(assert_type, \
- #ptr, \
+ KUNIT_INIT_PTR_NOT_ERR_STRUCT(#ptr, \
__ptr), \
fmt, \
##__VA_ARGS__); \
--
2.34.1.575.g55b058a8bb-goog


2022-01-10 22:14:13

by Brendan Higgins

[permalink] [raw]
Subject: Re: [PATCH 1/6] kunit: add example test case showing off all the expect macros

On Fri, Jan 7, 2022 at 8:23 PM Daniel Latypov <[email protected]> wrote:
>
> Currently, these macros are only really documented near the bottom of
> https://www.kernel.org/doc/html/latest/dev-tools/kunit/api/test.html#c.KUNIT_FAIL.
>
> E.g. it's likely someone might just not realize that
> KUNIT_EXPECT_STREQ() exists and instead use KUNIT_EXPECT_FALSE(strcmp())
> or similar.
>
> This can also serve as a basic smoketest that the KUnit assert machinery
> still works for all the macros.
>
> Signed-off-by: Daniel Latypov <[email protected]>

I still don't like how much this bloats the example test; aside from
that, this looks good.

Reviewed-by: Brendan Higgins <[email protected]>

2022-01-10 22:21:55

by Brendan Higgins

[permalink] [raw]
Subject: Re: [PATCH 2/6] kunit: move check if assertion passed into the macros

On Fri, Jan 7, 2022 at 8:23 PM Daniel Latypov <[email protected]> wrote:
>
> Currently the code always calls kunit_do_assertion() even though it does
> nothing when `pass` is true.
>
> This change moves the `if(!(pass))` check into the macro instead
> and renames the function to kunit_failed_assertion().
> I feel this a bit easier to read and understand.
>
> This has the potential upside of avoiding a function call that does
> nothing most of the time (assuming your tests are passing) but comes
> with the downside of generating a bit more code and branches.
>
> This also means we don't have to initialize structs that we don't need,
> which will become a tiny bit more expensive if we switch over to using
> static variables to try and reduce stack usage. (There's runtime code
> to check if the variable has been initialized yet or not).
>
> Signed-off-by: Daniel Latypov <[email protected]>

Tiny nit, see below. Otherwise:

Reviewed-by: Brendan Higgins <[email protected]>

> ---
> include/kunit/test.h | 20 ++++++++++----------
> lib/kunit/test.c | 13 ++++---------
> 2 files changed, 14 insertions(+), 19 deletions(-)
>
> diff --git a/include/kunit/test.h b/include/kunit/test.h
> index b26400731c02..690a28dfc795 100644
> --- a/include/kunit/test.h
> +++ b/include/kunit/test.h
> @@ -770,18 +770,18 @@ void __printf(2, 3) kunit_log_append(char *log, const char *fmt, ...);
> */
> #define KUNIT_SUCCEED(test) do {} while (0)
>
> -void kunit_do_assertion(struct kunit *test,
> - struct kunit_assert *assert,
> - bool pass,
> - const char *fmt, ...);
> +void kunit_failed_assertion(struct kunit *test,
> + struct kunit_assert *assert,
> + const char *fmt, ...);

Tiny nit: I think this should be kunit_fail_assertion. I think
functions should be in the active tense, imperative mood since when
you call a function you are telling it to do something.

Also, do we need to worry about this getting confused with KUNIT_FAIL,
or KUNIT_FAIL_ASSERTION:

https://elixir.bootlin.com/linux/v5.16/source/include/kunit/test.h#L788

?

> #define KUNIT_ASSERTION(test, pass, assert_class, INITIALIZER, fmt, ...) do { \
> - struct assert_class __assertion = INITIALIZER; \
> - kunit_do_assertion(test, \
> - &__assertion.assert, \
> - pass, \
> - fmt, \
> - ##__VA_ARGS__); \
> + if (!(pass)) { \
> + struct assert_class __assertion = INITIALIZER; \
> + kunit_failed_assertion(test, \
> + &__assertion.assert, \
> + fmt, \
> + ##__VA_ARGS__); \
> + } \
> } while (0)
>
>
> diff --git a/lib/kunit/test.c b/lib/kunit/test.c
> index c7ed4aabec04..5ad671745483 100644
> --- a/lib/kunit/test.c
> +++ b/lib/kunit/test.c
> @@ -275,16 +275,11 @@ static void __noreturn kunit_abort(struct kunit *test)
> WARN_ONCE(true, "Throw could not abort from test!\n");
> }
>
> -void kunit_do_assertion(struct kunit *test,
> - struct kunit_assert *assert,
> - bool pass,
> - const char *fmt, ...)
> +void kunit_failed_assertion(struct kunit *test,
> + struct kunit_assert *assert,
> + const char *fmt, ...)
> {
> va_list args;
> -
> - if (pass)
> - return;
> -
> va_start(args, fmt);
>
> assert->message.fmt = fmt;
> @@ -297,7 +292,7 @@ void kunit_do_assertion(struct kunit *test,
> if (assert->type == KUNIT_ASSERTION)
> kunit_abort(test);
> }
> -EXPORT_SYMBOL_GPL(kunit_do_assertion);
> +EXPORT_SYMBOL_GPL(kunit_failed_assertion);
>
> void kunit_init_test(struct kunit *test, const char *name, char *log)
> {
> --
> 2.34.1.575.g55b058a8bb-goog
>

2022-01-10 22:24:21

by Brendan Higgins

[permalink] [raw]
Subject: Re: [PATCH 3/6] kunit: drop unused kunit* field in kunit_assert

On Fri, Jan 7, 2022 at 8:23 PM Daniel Latypov <[email protected]> wrote:
>
> The `struct kunit* test` field in kunit_assert is unused.
> Note: we have access to `test` where we need it via the string_stream
> object. I assume `test` in `kunit_assert` predates this and was leftover
> after some refactoring.
>
> This patch removes the field and cleans up the macros to avoid
> needlessly passing around `test`.
>
> Signed-off-by: Daniel Latypov <[email protected]>

Looks good. Thanks!

Reviewed-by: Brendan Higgins <[email protected]>

2022-01-10 22:25:22

by Daniel Latypov

[permalink] [raw]
Subject: Re: [PATCH 1/6] kunit: add example test case showing off all the expect macros

On Mon, Jan 10, 2022 at 2:14 PM Brendan Higgins
<[email protected]> wrote:
>
> On Fri, Jan 7, 2022 at 8:23 PM Daniel Latypov <[email protected]> wrote:
> >
> > Currently, these macros are only really documented near the bottom of
> > https://www.kernel.org/doc/html/latest/dev-tools/kunit/api/test.html#c.KUNIT_FAIL.
> >
> > E.g. it's likely someone might just not realize that
> > KUNIT_EXPECT_STREQ() exists and instead use KUNIT_EXPECT_FALSE(strcmp())
> > or similar.
> >
> > This can also serve as a basic smoketest that the KUnit assert machinery
> > still works for all the macros.
> >
> > Signed-off-by: Daniel Latypov <[email protected]>
>
> I still don't like how much this bloats the example test; aside from
> that, this looks good.

Agreed, it does add bloat.
I just wanted something *somewhere* I could use to smoketest the later changes.
I just remembered how people weren't very aware of the _MSG variants
and thought this could help.

If others have a preference, I'll happily move out and into kunit-test.c.
I'm fine either way as I initially was going to put it there to begin with.

>
> Reviewed-by: Brendan Higgins <[email protected]>

2022-01-10 22:32:12

by Brendan Higgins

[permalink] [raw]
Subject: Re: [PATCH 4/6] kunit: factor out kunit_base_assert_format() call into kunit_fail()

On Fri, Jan 7, 2022 at 8:23 PM Daniel Latypov <[email protected]> wrote:
>
> We call this function first thing for all the assertion `format()`
> functions.
> This is the part that prints the file and line number and assertion type
> (EXPECTATION, ASSERTION).
>
> Having it as part of the format functions lets us have the flexibility
> to not print that information (or print it differently) for new
> assertion types, but I think this we don't need that.

nit: drop the "this".

> And in the future, we'd like to consider factoring that data (file,
> line#, type) out of the kunit_assert struct and into a `static`
> variable, as Linus suggested [1], so we'd need to extract it anyways.
>
> [1] https://groups.google.com/g/kunit-dev/c/i3fZXgvBrfA/m/VULQg1z6BAAJ
>
> Signed-off-by: Daniel Latypov <[email protected]>
> ---
> lib/kunit/assert.c | 6 ------
> lib/kunit/test.c | 1 +
> 2 files changed, 1 insertion(+), 6 deletions(-)
>
> diff --git a/lib/kunit/assert.c b/lib/kunit/assert.c
> index b972bda61c0c..4d9a1295efc7 100644
> --- a/lib/kunit/assert.c
> +++ b/lib/kunit/assert.c
> @@ -40,7 +40,6 @@ EXPORT_SYMBOL_GPL(kunit_assert_print_msg);
> void kunit_fail_assert_format(const struct kunit_assert *assert,
> struct string_stream *stream)
> {
> - kunit_base_assert_format(assert, stream);
> string_stream_add(stream, "%pV", &assert->message);
> }
> EXPORT_SYMBOL_GPL(kunit_fail_assert_format);
> @@ -52,7 +51,6 @@ void kunit_unary_assert_format(const struct kunit_assert *assert,
>
> unary_assert = container_of(assert, struct kunit_unary_assert, assert);
>
> - kunit_base_assert_format(assert, stream);
> if (unary_assert->expected_true)
> string_stream_add(stream,
> KUNIT_SUBTEST_INDENT "Expected %s to be true, but is false\n",
> @@ -73,7 +71,6 @@ void kunit_ptr_not_err_assert_format(const struct kunit_assert *assert,
> ptr_assert = container_of(assert, struct kunit_ptr_not_err_assert,
> assert);
>
> - kunit_base_assert_format(assert, stream);
> if (!ptr_assert->value) {
> string_stream_add(stream,
> KUNIT_SUBTEST_INDENT "Expected %s is not null, but is\n",
> @@ -119,7 +116,6 @@ void kunit_binary_assert_format(const struct kunit_assert *assert,
> binary_assert = container_of(assert, struct kunit_binary_assert,
> assert);
>
> - kunit_base_assert_format(assert, stream);
> string_stream_add(stream,
> KUNIT_SUBTEST_INDENT "Expected %s %s %s, but\n",
> binary_assert->left_text,
> @@ -147,7 +143,6 @@ void kunit_binary_ptr_assert_format(const struct kunit_assert *assert,
> binary_assert = container_of(assert, struct kunit_binary_ptr_assert,
> assert);
>
> - kunit_base_assert_format(assert, stream);
> string_stream_add(stream,
> KUNIT_SUBTEST_INDENT "Expected %s %s %s, but\n",
> binary_assert->left_text,
> @@ -187,7 +182,6 @@ void kunit_binary_str_assert_format(const struct kunit_assert *assert,
> binary_assert = container_of(assert, struct kunit_binary_str_assert,
> assert);
>
> - kunit_base_assert_format(assert, stream);
> string_stream_add(stream,
> KUNIT_SUBTEST_INDENT "Expected %s %s %s, but\n",
> binary_assert->left_text,
> diff --git a/lib/kunit/test.c b/lib/kunit/test.c
> index 5ad671745483..735c1b67d843 100644
> --- a/lib/kunit/test.c
> +++ b/lib/kunit/test.c
> @@ -255,6 +255,7 @@ static void kunit_fail(struct kunit *test, struct kunit_assert *assert)
> return;
> }
>
> + kunit_base_assert_format(assert, stream);

I think my thinking in having this function called by the other assert
functions was to take advantage of inheritance. I was treating
kunit_base_assert_format as the parent method that other methods were
inheriting from, so I wanted to have them inherit some of the common
behavior by calling the original function.

If you decide to make this change, I think it would be a good idea to
change the name of kunit_base_assert_format to not mislead to this
effect.

> assert->format(assert, stream);
>
> kunit_print_string_stream(test, stream);
> --
> 2.34.1.575.g55b058a8bb-goog
>

2022-01-10 22:33:04

by Daniel Latypov

[permalink] [raw]
Subject: Re: [PATCH 2/6] kunit: move check if assertion passed into the macros

On Mon, Jan 10, 2022 at 2:21 PM Brendan Higgins
<[email protected]> wrote:
>
> On Fri, Jan 7, 2022 at 8:23 PM Daniel Latypov <[email protected]> wrote:
> >
> > Currently the code always calls kunit_do_assertion() even though it does
> > nothing when `pass` is true.
> >
> > This change moves the `if(!(pass))` check into the macro instead
> > and renames the function to kunit_failed_assertion().
> > I feel this a bit easier to read and understand.
> >
> > This has the potential upside of avoiding a function call that does
> > nothing most of the time (assuming your tests are passing) but comes
> > with the downside of generating a bit more code and branches.
> >
> > This also means we don't have to initialize structs that we don't need,
> > which will become a tiny bit more expensive if we switch over to using
> > static variables to try and reduce stack usage. (There's runtime code
> > to check if the variable has been initialized yet or not).
> >
> > Signed-off-by: Daniel Latypov <[email protected]>
>
> Tiny nit, see below. Otherwise:
>
> Reviewed-by: Brendan Higgins <[email protected]>
>
> > ---
> > include/kunit/test.h | 20 ++++++++++----------
> > lib/kunit/test.c | 13 ++++---------
> > 2 files changed, 14 insertions(+), 19 deletions(-)
> >
> > diff --git a/include/kunit/test.h b/include/kunit/test.h
> > index b26400731c02..690a28dfc795 100644
> > --- a/include/kunit/test.h
> > +++ b/include/kunit/test.h
> > @@ -770,18 +770,18 @@ void __printf(2, 3) kunit_log_append(char *log, const char *fmt, ...);
> > */
> > #define KUNIT_SUCCEED(test) do {} while (0)
> >
> > -void kunit_do_assertion(struct kunit *test,
> > - struct kunit_assert *assert,
> > - bool pass,
> > - const char *fmt, ...);
> > +void kunit_failed_assertion(struct kunit *test,
> > + struct kunit_assert *assert,
> > + const char *fmt, ...);
>
> Tiny nit: I think this should be kunit_fail_assertion. I think
> functions should be in the active tense, imperative mood since when
> you call a function you are telling it to do something.
>
> Also, do we need to worry about this getting confused with KUNIT_FAIL,
> or KUNIT_FAIL_ASSERTION:

So do we want to try and pick a different name from
kunit_fail_assertion() to avoid confusion with the macro?
That's partly why I went with past tense.
Perhaps: "kunit_do_assertion() => kunit_do_failed_assertion()" instead?

Tangent: we have some similar confusing names, e.g. KUNIT_ASSERTION is
both the name of a macro and an enum (kunit_assert_type), and those
have the exact same case.

>
> https://elixir.bootlin.com/linux/v5.16/source/include/kunit/test.h#L788
>
> ?
>
> > #define KUNIT_ASSERTION(test, pass, assert_class, INITIALIZER, fmt, ...) do { \
> > - struct assert_class __assertion = INITIALIZER; \
> > - kunit_do_assertion(test, \
> > - &__assertion.assert, \
> > - pass, \
> > - fmt, \
> > - ##__VA_ARGS__); \
> > + if (!(pass)) { \
> > + struct assert_class __assertion = INITIALIZER; \
> > + kunit_failed_assertion(test, \
> > + &__assertion.assert, \
> > + fmt, \
> > + ##__VA_ARGS__); \
> > + } \
> > } while (0)
> >
> >
> > diff --git a/lib/kunit/test.c b/lib/kunit/test.c
> > index c7ed4aabec04..5ad671745483 100644
> > --- a/lib/kunit/test.c
> > +++ b/lib/kunit/test.c
> > @@ -275,16 +275,11 @@ static void __noreturn kunit_abort(struct kunit *test)
> > WARN_ONCE(true, "Throw could not abort from test!\n");
> > }
> >
> > -void kunit_do_assertion(struct kunit *test,
> > - struct kunit_assert *assert,
> > - bool pass,
> > - const char *fmt, ...)
> > +void kunit_failed_assertion(struct kunit *test,
> > + struct kunit_assert *assert,
> > + const char *fmt, ...)
> > {
> > va_list args;
> > -
> > - if (pass)
> > - return;
> > -
> > va_start(args, fmt);
> >
> > assert->message.fmt = fmt;
> > @@ -297,7 +292,7 @@ void kunit_do_assertion(struct kunit *test,
> > if (assert->type == KUNIT_ASSERTION)
> > kunit_abort(test);
> > }
> > -EXPORT_SYMBOL_GPL(kunit_do_assertion);
> > +EXPORT_SYMBOL_GPL(kunit_failed_assertion);
> >
> > void kunit_init_test(struct kunit *test, const char *name, char *log)
> > {
> > --
> > 2.34.1.575.g55b058a8bb-goog
> >

2022-01-10 22:35:22

by Daniel Latypov

[permalink] [raw]
Subject: Re: [PATCH 4/6] kunit: factor out kunit_base_assert_format() call into kunit_fail()

On Mon, Jan 10, 2022 at 2:32 PM 'Brendan Higgins' via KUnit
Development <[email protected]> wrote:
>
> On Fri, Jan 7, 2022 at 8:23 PM Daniel Latypov <[email protected]> wrote:
> >
> > We call this function first thing for all the assertion `format()`
> > functions.
> > This is the part that prints the file and line number and assertion type
> > (EXPECTATION, ASSERTION).
> >
> > Having it as part of the format functions lets us have the flexibility
> > to not print that information (or print it differently) for new
> > assertion types, but I think this we don't need that.
>
> nit: drop the "this".
>
> > And in the future, we'd like to consider factoring that data (file,
> > line#, type) out of the kunit_assert struct and into a `static`
> > variable, as Linus suggested [1], so we'd need to extract it anyways.
> >
> > [1] https://groups.google.com/g/kunit-dev/c/i3fZXgvBrfA/m/VULQg1z6BAAJ
> >
> > Signed-off-by: Daniel Latypov <[email protected]>
> > ---
> > lib/kunit/assert.c | 6 ------
> > lib/kunit/test.c | 1 +
> > 2 files changed, 1 insertion(+), 6 deletions(-)
> >
> > diff --git a/lib/kunit/assert.c b/lib/kunit/assert.c
> > index b972bda61c0c..4d9a1295efc7 100644
> > --- a/lib/kunit/assert.c
> > +++ b/lib/kunit/assert.c
> > @@ -40,7 +40,6 @@ EXPORT_SYMBOL_GPL(kunit_assert_print_msg);
> > void kunit_fail_assert_format(const struct kunit_assert *assert,
> > struct string_stream *stream)
> > {
> > - kunit_base_assert_format(assert, stream);
> > string_stream_add(stream, "%pV", &assert->message);
> > }
> > EXPORT_SYMBOL_GPL(kunit_fail_assert_format);
> > @@ -52,7 +51,6 @@ void kunit_unary_assert_format(const struct kunit_assert *assert,
> >
> > unary_assert = container_of(assert, struct kunit_unary_assert, assert);
> >
> > - kunit_base_assert_format(assert, stream);
> > if (unary_assert->expected_true)
> > string_stream_add(stream,
> > KUNIT_SUBTEST_INDENT "Expected %s to be true, but is false\n",
> > @@ -73,7 +71,6 @@ void kunit_ptr_not_err_assert_format(const struct kunit_assert *assert,
> > ptr_assert = container_of(assert, struct kunit_ptr_not_err_assert,
> > assert);
> >
> > - kunit_base_assert_format(assert, stream);
> > if (!ptr_assert->value) {
> > string_stream_add(stream,
> > KUNIT_SUBTEST_INDENT "Expected %s is not null, but is\n",
> > @@ -119,7 +116,6 @@ void kunit_binary_assert_format(const struct kunit_assert *assert,
> > binary_assert = container_of(assert, struct kunit_binary_assert,
> > assert);
> >
> > - kunit_base_assert_format(assert, stream);
> > string_stream_add(stream,
> > KUNIT_SUBTEST_INDENT "Expected %s %s %s, but\n",
> > binary_assert->left_text,
> > @@ -147,7 +143,6 @@ void kunit_binary_ptr_assert_format(const struct kunit_assert *assert,
> > binary_assert = container_of(assert, struct kunit_binary_ptr_assert,
> > assert);
> >
> > - kunit_base_assert_format(assert, stream);
> > string_stream_add(stream,
> > KUNIT_SUBTEST_INDENT "Expected %s %s %s, but\n",
> > binary_assert->left_text,
> > @@ -187,7 +182,6 @@ void kunit_binary_str_assert_format(const struct kunit_assert *assert,
> > binary_assert = container_of(assert, struct kunit_binary_str_assert,
> > assert);
> >
> > - kunit_base_assert_format(assert, stream);
> > string_stream_add(stream,
> > KUNIT_SUBTEST_INDENT "Expected %s %s %s, but\n",
> > binary_assert->left_text,
> > diff --git a/lib/kunit/test.c b/lib/kunit/test.c
> > index 5ad671745483..735c1b67d843 100644
> > --- a/lib/kunit/test.c
> > +++ b/lib/kunit/test.c
> > @@ -255,6 +255,7 @@ static void kunit_fail(struct kunit *test, struct kunit_assert *assert)
> > return;
> > }
> >
> > + kunit_base_assert_format(assert, stream);
>
> I think my thinking in having this function called by the other assert
> functions was to take advantage of inheritance. I was treating
> kunit_base_assert_format as the parent method that other methods were
> inheriting from, so I wanted to have them inherit some of the common
> behavior by calling the original function.
>
> If you decide to make this change, I think it would be a good idea to
> change the name of kunit_base_assert_format to not mislead to this
> effect.

The child patch renames it to kunit_assert_prologue().
I can rename it in this change if you prefer.

I had just initially left it with the same name to keep this diff a
bit smaller and more focused.
But now you point it out, I think it would be cleaner to rename it here.

>
> > assert->format(assert, stream);
> >
> > kunit_print_string_stream(test, stream);
> > --
> > 2.34.1.575.g55b058a8bb-goog
> >
>
> --
> You received this message because you are subscribed to the Google Groups "KUnit Development" group.
> To unsubscribe from this group and stop receiving emails from it, send an email to [email protected].
> To view this discussion on the web visit https://groups.google.com/d/msgid/kunit-dev/CAFd5g47r8aQBWPtt6ffHokqqN2sMi10p1Q5QA3xGVLTVDQh98Q%40mail.gmail.com.

2022-01-11 06:51:07

by David Gow

[permalink] [raw]
Subject: Re: [PATCH 1/6] kunit: add example test case showing off all the expect macros

On Sat, Jan 8, 2022 at 9:23 AM Daniel Latypov <[email protected]> wrote:
>
> Currently, these macros are only really documented near the bottom of
> https://www.kernel.org/doc/html/latest/dev-tools/kunit/api/test.html#c.KUNIT_FAIL.
>
> E.g. it's likely someone might just not realize that
> KUNIT_EXPECT_STREQ() exists and instead use KUNIT_EXPECT_FALSE(strcmp())
> or similar.
>
> This can also serve as a basic smoketest that the KUnit assert machinery
> still works for all the macros.
>
> Signed-off-by: Daniel Latypov <[email protected]>
> ---

I think this is a great idea. I will note that this definitely isn't a
full test _of_ the assertion macros (in that it only exercises the
success case), so keeping it as an example is probably best.

A few possible ideas below, but I'm happy enough with this as-is regardless.

Reviewed-by: David Gow <[email protected]>

> lib/kunit/kunit-example-test.c | 46 ++++++++++++++++++++++++++++++++++
> 1 file changed, 46 insertions(+)
>
> diff --git a/lib/kunit/kunit-example-test.c b/lib/kunit/kunit-example-test.c
> index 51099b0ca29c..182a64c12541 100644
> --- a/lib/kunit/kunit-example-test.c
> +++ b/lib/kunit/kunit-example-test.c
> @@ -69,6 +69,51 @@ static void example_mark_skipped_test(struct kunit *test)
> /* This line should run */
> kunit_info(test, "You should see this line.");
> }
> +
> +/*
> + * This test shows off all the KUNIT_EXPECT macros.
> + */
> +static void example_all_expect_macros_test(struct kunit *test)
> +{
> + KUNIT_EXPECT_TRUE(test, true);
> + KUNIT_EXPECT_FALSE(test, false);

_Maybe_ it's worth having a comment for each of these groups ('boolean
assertions', 'integer assertions', 'pointer assertions', etc)?

> +
> + KUNIT_EXPECT_EQ(test, 1, 1);
> + KUNIT_EXPECT_GE(test, 1, 1);
> + KUNIT_EXPECT_LE(test, 1, 1);
> + KUNIT_EXPECT_NE(test, 1, 0);
> + KUNIT_EXPECT_GT(test, 1, 0);
> + KUNIT_EXPECT_LT(test, 0, 1);
> +
> + KUNIT_EXPECT_NOT_ERR_OR_NULL(test, test);
> + KUNIT_EXPECT_PTR_EQ(test, NULL, NULL);
> + KUNIT_EXPECT_PTR_NE(test, test, NULL);
> +
> + KUNIT_EXPECT_STREQ(test, "hi", "hi");
> + KUNIT_EXPECT_STRNEQ(test, "hi", "bye");
> +
> + /*
> + * There are also _MSG variants of all of the above that let you include
> + * additional text on failure.
> + */

There are also the ASSERT vs EXPECT variations. While it may be
excessive to also include all of these, particularly in an example, it
might be worth mentioning them in a comment somewhere?

Alternatively, if this is bloating the example too much, we could have
only one example each of the ASSERT and _MSG variants.

> + KUNIT_EXPECT_TRUE_MSG(test, true, "msg");
> + KUNIT_EXPECT_FALSE_MSG(test, false, "msg");

Part of me feels that a better message than "msg" would be nice to
have here, but I can't think of a good one. Maybe (particularly for
the less obvious integer/string/pointer macros below), having a
description of what's being asserted?



> +
> + KUNIT_EXPECT_EQ_MSG(test, 1, 1, "msg");
> + KUNIT_EXPECT_GE_MSG(test, 1, 1, "msg");
> + KUNIT_EXPECT_LE_MSG(test, 1, 1, "msg");
> + KUNIT_EXPECT_NE_MSG(test, 1, 0, "msg");
> + KUNIT_EXPECT_GT_MSG(test, 1, 0, "msg");
> + KUNIT_EXPECT_LT_MSG(test, 0, 1, "msg");
> +
> + KUNIT_EXPECT_NOT_ERR_OR_NULL_MSG(test, test, "msg");
> + KUNIT_EXPECT_PTR_EQ_MSG(test, NULL, NULL, "msg");
> + KUNIT_EXPECT_PTR_NE_MSG(test, test, NULL, "msg");
> +
> + KUNIT_EXPECT_STREQ_MSG(test, "hi", "hi", "msg");
> + KUNIT_EXPECT_STRNEQ_MSG(test, "hi", "bye", "msg");
> +}
> +
> /*
> * Here we make a list of all the test cases we want to add to the test suite
> * below.
> @@ -83,6 +128,7 @@ static struct kunit_case example_test_cases[] = {
> KUNIT_CASE(example_simple_test),
> KUNIT_CASE(example_skip_test),
> KUNIT_CASE(example_mark_skipped_test),
> + KUNIT_CASE(example_all_expect_macros_test),
> {}
> };
>
> --
> 2.34.1.575.g55b058a8bb-goog
>

2022-01-11 06:51:29

by David Gow

[permalink] [raw]
Subject: Re: [PATCH 2/6] kunit: move check if assertion passed into the macros

On Tue, Jan 11, 2022 at 6:33 AM Daniel Latypov <[email protected]> wrote:
>
> On Mon, Jan 10, 2022 at 2:21 PM Brendan Higgins
> <[email protected]> wrote:
> >
> > On Fri, Jan 7, 2022 at 8:23 PM Daniel Latypov <[email protected]> wrote:
> > >
> > > Currently the code always calls kunit_do_assertion() even though it does
> > > nothing when `pass` is true.
> > >
> > > This change moves the `if(!(pass))` check into the macro instead
> > > and renames the function to kunit_failed_assertion().
> > > I feel this a bit easier to read and understand.
> > >
> > > This has the potential upside of avoiding a function call that does
> > > nothing most of the time (assuming your tests are passing) but comes
> > > with the downside of generating a bit more code and branches.
> > >
> > > This also means we don't have to initialize structs that we don't need,
> > > which will become a tiny bit more expensive if we switch over to using
> > > static variables to try and reduce stack usage. (There's runtime code
> > > to check if the variable has been initialized yet or not).
> > >
> > > Signed-off-by: Daniel Latypov <[email protected]>
> >
> > Tiny nit, see below. Otherwise:
> >
> > Reviewed-by: Brendan Higgins <[email protected]>
> >
> > > ---
> > > include/kunit/test.h | 20 ++++++++++----------
> > > lib/kunit/test.c | 13 ++++---------
> > > 2 files changed, 14 insertions(+), 19 deletions(-)
> > >
> > > diff --git a/include/kunit/test.h b/include/kunit/test.h
> > > index b26400731c02..690a28dfc795 100644
> > > --- a/include/kunit/test.h
> > > +++ b/include/kunit/test.h
> > > @@ -770,18 +770,18 @@ void __printf(2, 3) kunit_log_append(char *log, const char *fmt, ...);
> > > */
> > > #define KUNIT_SUCCEED(test) do {} while (0)
> > >
> > > -void kunit_do_assertion(struct kunit *test,
> > > - struct kunit_assert *assert,
> > > - bool pass,
> > > - const char *fmt, ...);
> > > +void kunit_failed_assertion(struct kunit *test,
> > > + struct kunit_assert *assert,
> > > + const char *fmt, ...);
> >
> > Tiny nit: I think this should be kunit_fail_assertion. I think
> > functions should be in the active tense, imperative mood since when
> > you call a function you are telling it to do something.
> >
> > Also, do we need to worry about this getting confused with KUNIT_FAIL,
> > or KUNIT_FAIL_ASSERTION:
>
> So do we want to try and pick a different name from
> kunit_fail_assertion() to avoid confusion with the macro?
> That's partly why I went with past tense.
> Perhaps: "kunit_do_assertion() => kunit_do_failed_assertion()" instead?

I'm not particularly picky about the name personally. But if I had to
join the bikeshedding, I'd probably go with kunit_assertion_fail() or
similar (kunit_assertion_failed works too, past-tense-wise.)

But kunit_do_fail{,ed}_assertion() would work too.


>
> Tangent: we have some similar confusing names, e.g. KUNIT_ASSERTION is
> both the name of a macro and an enum (kunit_assert_type), and those
> have the exact same case.
>
> >
> > https://elixir.bootlin.com/linux/v5.16/source/include/kunit/test.h#L788
> >
> > ?
> >
> > > #define KUNIT_ASSERTION(test, pass, assert_class, INITIALIZER, fmt, ...) do { \
> > > - struct assert_class __assertion = INITIALIZER; \
> > > - kunit_do_assertion(test, \
> > > - &__assertion.assert, \
> > > - pass, \
> > > - fmt, \
> > > - ##__VA_ARGS__); \
> > > + if (!(pass)) { \
> > > + struct assert_class __assertion = INITIALIZER; \
> > > + kunit_failed_assertion(test, \
> > > + &__assertion.assert, \
> > > + fmt, \
> > > + ##__VA_ARGS__); \
> > > + } \
> > > } while (0)
> > >
> > >
> > > diff --git a/lib/kunit/test.c b/lib/kunit/test.c
> > > index c7ed4aabec04..5ad671745483 100644
> > > --- a/lib/kunit/test.c
> > > +++ b/lib/kunit/test.c
> > > @@ -275,16 +275,11 @@ static void __noreturn kunit_abort(struct kunit *test)
> > > WARN_ONCE(true, "Throw could not abort from test!\n");
> > > }
> > >
> > > -void kunit_do_assertion(struct kunit *test,
> > > - struct kunit_assert *assert,
> > > - bool pass,
> > > - const char *fmt, ...)
> > > +void kunit_failed_assertion(struct kunit *test,
> > > + struct kunit_assert *assert,
> > > + const char *fmt, ...)
> > > {
> > > va_list args;
> > > -
> > > - if (pass)
> > > - return;
> > > -
> > > va_start(args, fmt);
> > >
> > > assert->message.fmt = fmt;
> > > @@ -297,7 +292,7 @@ void kunit_do_assertion(struct kunit *test,
> > > if (assert->type == KUNIT_ASSERTION)
> > > kunit_abort(test);
> > > }
> > > -EXPORT_SYMBOL_GPL(kunit_do_assertion);
> > > +EXPORT_SYMBOL_GPL(kunit_failed_assertion);
> > >
> > > void kunit_init_test(struct kunit *test, const char *name, char *log)
> > > {
> > > --
> > > 2.34.1.575.g55b058a8bb-goog
> > >

2022-01-11 06:51:46

by David Gow

[permalink] [raw]
Subject: Re: [PATCH 3/6] kunit: drop unused kunit* field in kunit_assert

On Sat, Jan 8, 2022 at 9:23 AM Daniel Latypov <[email protected]> wrote:
>
> The `struct kunit* test` field in kunit_assert is unused.
> Note: we have access to `test` where we need it via the string_stream
> object. I assume `test` in `kunit_assert` predates this and was leftover
> after some refactoring.

Note that I don't like the idea of accessing 'test' via the string
stream in general, but we don't seem to ever actually do this (as far
as I can tell). Maybe if we wanted to be super nitpicky, rewording the
note to say "if we need it" rather than "where we need it" would be
clearer.

>
> This patch removes the field and cleans up the macros to avoid
> needlessly passing around `test`.
>
> Signed-off-by: Daniel Latypov <[email protected]>
> ---

Looks good, thanks!

Reviewed-by: David Gow <[email protected]>


> include/kunit/assert.h | 45 ++++++++++++------------------------------
> include/kunit/test.h | 14 +++++--------
> 2 files changed, 18 insertions(+), 41 deletions(-)
>
> diff --git a/include/kunit/assert.h b/include/kunit/assert.h
> index ad889b539ab3..3da6c792496c 100644
> --- a/include/kunit/assert.h
> +++ b/include/kunit/assert.h
> @@ -30,7 +30,6 @@ enum kunit_assert_type {
>
> /**
> * struct kunit_assert - Data for printing a failed assertion or expectation.
> - * @test: the test case this expectation/assertion is associated with.
> * @type: the type (either an expectation or an assertion) of this kunit_assert.
> * @line: the source code line number that the expectation/assertion is at.
> * @file: the file path of the source file that the expectation/assertion is in.
> @@ -41,7 +40,6 @@ enum kunit_assert_type {
> * format a string to a user reporting the failure.
> */
> struct kunit_assert {
> - struct kunit *test;
> enum kunit_assert_type type;
> int line;
> const char *file;
> @@ -60,14 +58,12 @@ struct kunit_assert {
>
> /**
> * KUNIT_INIT_ASSERT_STRUCT() - Initializer for a &struct kunit_assert.
> - * @kunit: The test case that this expectation/assertion is associated with.
> * @assert_type: The type (assertion or expectation) of this kunit_assert.
> * @fmt: The formatting function which builds a string out of this kunit_assert.
> *
> * The base initializer for a &struct kunit_assert.
> */
> -#define KUNIT_INIT_ASSERT_STRUCT(kunit, assert_type, fmt) { \
> - .test = kunit, \
> +#define KUNIT_INIT_ASSERT_STRUCT(assert_type, fmt) { \
> .type = assert_type, \
> .file = __FILE__, \
> .line = __LINE__, \
> @@ -96,15 +92,13 @@ void kunit_fail_assert_format(const struct kunit_assert *assert,
>
> /**
> * KUNIT_INIT_FAIL_ASSERT_STRUCT() - Initializer for &struct kunit_fail_assert.
> - * @test: The test case that this expectation/assertion is associated with.
> * @type: The type (assertion or expectation) of this kunit_assert.
> *
> * Initializes a &struct kunit_fail_assert. Intended to be used in
> * KUNIT_EXPECT_* and KUNIT_ASSERT_* macros.
> */
> -#define KUNIT_INIT_FAIL_ASSERT_STRUCT(test, type) { \
> - .assert = KUNIT_INIT_ASSERT_STRUCT(test, \
> - type, \
> +#define KUNIT_INIT_FAIL_ASSERT_STRUCT(type) { \
> + .assert = KUNIT_INIT_ASSERT_STRUCT(type, \
> kunit_fail_assert_format) \
> }
>
> @@ -129,7 +123,6 @@ void kunit_unary_assert_format(const struct kunit_assert *assert,
>
> /**
> * KUNIT_INIT_UNARY_ASSERT_STRUCT() - Initializes &struct kunit_unary_assert.
> - * @test: The test case that this expectation/assertion is associated with.
> * @type: The type (assertion or expectation) of this kunit_assert.
> * @cond: A string representation of the expression asserted true or false.
> * @expect_true: True if of type KUNIT_{EXPECT|ASSERT}_TRUE, false otherwise.
> @@ -137,9 +130,8 @@ void kunit_unary_assert_format(const struct kunit_assert *assert,
> * Initializes a &struct kunit_unary_assert. Intended to be used in
> * KUNIT_EXPECT_* and KUNIT_ASSERT_* macros.
> */
> -#define KUNIT_INIT_UNARY_ASSERT_STRUCT(test, type, cond, expect_true) { \
> - .assert = KUNIT_INIT_ASSERT_STRUCT(test, \
> - type, \
> +#define KUNIT_INIT_UNARY_ASSERT_STRUCT(type, cond, expect_true) { \
> + .assert = KUNIT_INIT_ASSERT_STRUCT(type, \
> kunit_unary_assert_format), \
> .condition = cond, \
> .expected_true = expect_true \
> @@ -167,7 +159,6 @@ void kunit_ptr_not_err_assert_format(const struct kunit_assert *assert,
> /**
> * KUNIT_INIT_PTR_NOT_ERR_ASSERT_STRUCT() - Initializes a
> * &struct kunit_ptr_not_err_assert.
> - * @test: The test case that this expectation/assertion is associated with.
> * @type: The type (assertion or expectation) of this kunit_assert.
> * @txt: A string representation of the expression passed to the expectation.
> * @val: The actual evaluated pointer value of the expression.
> @@ -175,9 +166,8 @@ void kunit_ptr_not_err_assert_format(const struct kunit_assert *assert,
> * Initializes a &struct kunit_ptr_not_err_assert. Intended to be used in
> * KUNIT_EXPECT_* and KUNIT_ASSERT_* macros.
> */
> -#define KUNIT_INIT_PTR_NOT_ERR_STRUCT(test, type, txt, val) { \
> - .assert = KUNIT_INIT_ASSERT_STRUCT(test, \
> - type, \
> +#define KUNIT_INIT_PTR_NOT_ERR_STRUCT(type, txt, val) { \
> + .assert = KUNIT_INIT_ASSERT_STRUCT(type, \
> kunit_ptr_not_err_assert_format), \
> .text = txt, \
> .value = val \
> @@ -212,7 +202,6 @@ void kunit_binary_assert_format(const struct kunit_assert *assert,
> /**
> * KUNIT_INIT_BINARY_ASSERT_STRUCT() - Initializes a
> * &struct kunit_binary_assert.
> - * @test: The test case that this expectation/assertion is associated with.
> * @type: The type (assertion or expectation) of this kunit_assert.
> * @op_str: A string representation of the comparison operator (e.g. "==").
> * @left_str: A string representation of the expression in the left slot.
> @@ -223,15 +212,13 @@ void kunit_binary_assert_format(const struct kunit_assert *assert,
> * Initializes a &struct kunit_binary_assert. Intended to be used in
> * KUNIT_EXPECT_* and KUNIT_ASSERT_* macros.
> */
> -#define KUNIT_INIT_BINARY_ASSERT_STRUCT(test, \
> - type, \
> +#define KUNIT_INIT_BINARY_ASSERT_STRUCT(type, \
> op_str, \
> left_str, \
> left_val, \
> right_str, \
> right_val) { \
> - .assert = KUNIT_INIT_ASSERT_STRUCT(test, \
> - type, \
> + .assert = KUNIT_INIT_ASSERT_STRUCT(type, \
> kunit_binary_assert_format), \
> .operation = op_str, \
> .left_text = left_str, \
> @@ -269,7 +256,6 @@ void kunit_binary_ptr_assert_format(const struct kunit_assert *assert,
> /**
> * KUNIT_INIT_BINARY_PTR_ASSERT_STRUCT() - Initializes a
> * &struct kunit_binary_ptr_assert.
> - * @test: The test case that this expectation/assertion is associated with.
> * @type: The type (assertion or expectation) of this kunit_assert.
> * @op_str: A string representation of the comparison operator (e.g. "==").
> * @left_str: A string representation of the expression in the left slot.
> @@ -280,15 +266,13 @@ void kunit_binary_ptr_assert_format(const struct kunit_assert *assert,
> * Initializes a &struct kunit_binary_ptr_assert. Intended to be used in
> * KUNIT_EXPECT_* and KUNIT_ASSERT_* macros.
> */
> -#define KUNIT_INIT_BINARY_PTR_ASSERT_STRUCT(test, \
> - type, \
> +#define KUNIT_INIT_BINARY_PTR_ASSERT_STRUCT(type, \
> op_str, \
> left_str, \
> left_val, \
> right_str, \
> right_val) { \
> - .assert = KUNIT_INIT_ASSERT_STRUCT(test, \
> - type, \
> + .assert = KUNIT_INIT_ASSERT_STRUCT(type, \
> kunit_binary_ptr_assert_format), \
> .operation = op_str, \
> .left_text = left_str, \
> @@ -326,7 +310,6 @@ void kunit_binary_str_assert_format(const struct kunit_assert *assert,
> /**
> * KUNIT_INIT_BINARY_STR_ASSERT_STRUCT() - Initializes a
> * &struct kunit_binary_str_assert.
> - * @test: The test case that this expectation/assertion is associated with.
> * @type: The type (assertion or expectation) of this kunit_assert.
> * @op_str: A string representation of the comparison operator (e.g. "==").
> * @left_str: A string representation of the expression in the left slot.
> @@ -337,15 +320,13 @@ void kunit_binary_str_assert_format(const struct kunit_assert *assert,
> * Initializes a &struct kunit_binary_str_assert. Intended to be used in
> * KUNIT_EXPECT_* and KUNIT_ASSERT_* macros.
> */
> -#define KUNIT_INIT_BINARY_STR_ASSERT_STRUCT(test, \
> - type, \
> +#define KUNIT_INIT_BINARY_STR_ASSERT_STRUCT(type, \
> op_str, \
> left_str, \
> left_val, \
> right_str, \
> right_val) { \
> - .assert = KUNIT_INIT_ASSERT_STRUCT(test, \
> - type, \
> + .assert = KUNIT_INIT_ASSERT_STRUCT(type, \
> kunit_binary_str_assert_format), \
> .operation = op_str, \
> .left_text = left_str, \
> diff --git a/include/kunit/test.h b/include/kunit/test.h
> index 690a28dfc795..ebd45593321e 100644
> --- a/include/kunit/test.h
> +++ b/include/kunit/test.h
> @@ -789,7 +789,7 @@ void kunit_failed_assertion(struct kunit *test,
> KUNIT_ASSERTION(test, \
> false, \
> kunit_fail_assert, \
> - KUNIT_INIT_FAIL_ASSERT_STRUCT(test, assert_type), \
> + KUNIT_INIT_FAIL_ASSERT_STRUCT(assert_type), \
> fmt, \
> ##__VA_ARGS__)
>
> @@ -819,8 +819,7 @@ void kunit_failed_assertion(struct kunit *test,
> KUNIT_ASSERTION(test, \
> !!(condition) == !!expected_true, \
> kunit_unary_assert, \
> - KUNIT_INIT_UNARY_ASSERT_STRUCT(test, \
> - assert_type, \
> + KUNIT_INIT_UNARY_ASSERT_STRUCT(assert_type, \
> #condition, \
> expected_true), \
> fmt, \
> @@ -878,8 +877,7 @@ do { \
> KUNIT_ASSERTION(test, \
> __left op __right, \
> assert_class, \
> - ASSERT_CLASS_INIT(test, \
> - assert_type, \
> + ASSERT_CLASS_INIT(assert_type, \
> #op, \
> #left, \
> __left, \
> @@ -1233,8 +1231,7 @@ do { \
> KUNIT_ASSERTION(test, \
> strcmp(__left, __right) op 0, \
> kunit_binary_str_assert, \
> - KUNIT_INIT_BINARY_STR_ASSERT_STRUCT(test, \
> - assert_type, \
> + KUNIT_INIT_BINARY_STR_ASSERT_STRUCT(assert_type, \
> #op, \
> #left, \
> __left, \
> @@ -1293,8 +1290,7 @@ do { \
> KUNIT_ASSERTION(test, \
> !IS_ERR_OR_NULL(__ptr), \
> kunit_ptr_not_err_assert, \
> - KUNIT_INIT_PTR_NOT_ERR_STRUCT(test, \
> - assert_type, \
> + KUNIT_INIT_PTR_NOT_ERR_STRUCT(assert_type, \
> #ptr, \
> __ptr), \
> fmt, \
> --
> 2.34.1.575.g55b058a8bb-goog
>

2022-01-11 06:51:54

by David Gow

[permalink] [raw]
Subject: Re: [PATCH 4/6] kunit: factor out kunit_base_assert_format() call into kunit_fail()

On Sat, Jan 8, 2022 at 9:23 AM Daniel Latypov <[email protected]> wrote:
>
> We call this function first thing for all the assertion `format()`
> functions.
> This is the part that prints the file and line number and assertion type
> (EXPECTATION, ASSERTION).
>
> Having it as part of the format functions lets us have the flexibility
> to not print that information (or print it differently) for new
> assertion types, but I think this we don't need that.
>
> And in the future, we'd like to consider factoring that data (file,
> line#, type) out of the kunit_assert struct and into a `static`
> variable, as Linus suggested [1], so we'd need to extract it anyways.
>
> [1] https://groups.google.com/g/kunit-dev/c/i3fZXgvBrfA/m/VULQg1z6BAAJ
>
> Signed-off-by: Daniel Latypov <[email protected]>
> ---

Looks good to me, thanks!

Reviewed-by: David Gow <[email protected]>


> lib/kunit/assert.c | 6 ------
> lib/kunit/test.c | 1 +
> 2 files changed, 1 insertion(+), 6 deletions(-)
>
> diff --git a/lib/kunit/assert.c b/lib/kunit/assert.c
> index b972bda61c0c..4d9a1295efc7 100644
> --- a/lib/kunit/assert.c
> +++ b/lib/kunit/assert.c
> @@ -40,7 +40,6 @@ EXPORT_SYMBOL_GPL(kunit_assert_print_msg);
> void kunit_fail_assert_format(const struct kunit_assert *assert,
> struct string_stream *stream)
> {
> - kunit_base_assert_format(assert, stream);
> string_stream_add(stream, "%pV", &assert->message);
> }
> EXPORT_SYMBOL_GPL(kunit_fail_assert_format);
> @@ -52,7 +51,6 @@ void kunit_unary_assert_format(const struct kunit_assert *assert,
>
> unary_assert = container_of(assert, struct kunit_unary_assert, assert);
>
> - kunit_base_assert_format(assert, stream);
> if (unary_assert->expected_true)
> string_stream_add(stream,
> KUNIT_SUBTEST_INDENT "Expected %s to be true, but is false\n",
> @@ -73,7 +71,6 @@ void kunit_ptr_not_err_assert_format(const struct kunit_assert *assert,
> ptr_assert = container_of(assert, struct kunit_ptr_not_err_assert,
> assert);
>
> - kunit_base_assert_format(assert, stream);
> if (!ptr_assert->value) {
> string_stream_add(stream,
> KUNIT_SUBTEST_INDENT "Expected %s is not null, but is\n",
> @@ -119,7 +116,6 @@ void kunit_binary_assert_format(const struct kunit_assert *assert,
> binary_assert = container_of(assert, struct kunit_binary_assert,
> assert);
>
> - kunit_base_assert_format(assert, stream);
> string_stream_add(stream,
> KUNIT_SUBTEST_INDENT "Expected %s %s %s, but\n",
> binary_assert->left_text,
> @@ -147,7 +143,6 @@ void kunit_binary_ptr_assert_format(const struct kunit_assert *assert,
> binary_assert = container_of(assert, struct kunit_binary_ptr_assert,
> assert);
>
> - kunit_base_assert_format(assert, stream);
> string_stream_add(stream,
> KUNIT_SUBTEST_INDENT "Expected %s %s %s, but\n",
> binary_assert->left_text,
> @@ -187,7 +182,6 @@ void kunit_binary_str_assert_format(const struct kunit_assert *assert,
> binary_assert = container_of(assert, struct kunit_binary_str_assert,
> assert);
>
> - kunit_base_assert_format(assert, stream);
> string_stream_add(stream,
> KUNIT_SUBTEST_INDENT "Expected %s %s %s, but\n",
> binary_assert->left_text,
> diff --git a/lib/kunit/test.c b/lib/kunit/test.c
> index 5ad671745483..735c1b67d843 100644
> --- a/lib/kunit/test.c
> +++ b/lib/kunit/test.c
> @@ -255,6 +255,7 @@ static void kunit_fail(struct kunit *test, struct kunit_assert *assert)
> return;
> }
>
> + kunit_base_assert_format(assert, stream);
> assert->format(assert, stream);
>
> kunit_print_string_stream(test, stream);
> --
> 2.34.1.575.g55b058a8bb-goog
>

2022-01-11 06:57:24

by David Gow

[permalink] [raw]
Subject: Re: [PATCH 5/6] kunit: split out part of kunit_assert into a static const

On Sat, Jan 8, 2022 at 9:23 AM Daniel Latypov <[email protected]> wrote:
>
> This is per Linus's suggestion in [1].
>
> The issue there is that every KUNIT_EXPECT/KUNIT_ASSERT puts a
> kunit_assert object onto the stack. Normally we rely on compilers to
> elide this, but when that doesn't work out, this blows up the stack
> usage of kunit test functions.
>
> We can move some data off the stack by making it static.
> This change introduces a new `struct kunit_loc` to hold the file and
> line number and then just passing assert_type (EXPECT or ASSERT) as an
> argument.

A part of me feels that logically, this struct is really
"kunit_assert_static_data" (the collection of everything we can make
static), rather than specifically reserved for location information,
but that's an uglier name. My only concern is that, if we do manage to
make more things static const, will we want to rename this struct?

>
> In [1], it was suggested to also move out the format string as well, but
> users could theoretically craft a format string at runtime, so we can't.
>
> This change leaves a copy of `assert_type` in kunit_assert for now
> because cleaning up all the macros to not pass it around is a bit more
> involved.
>
> Here's an example of the expanded code for KUNIT_FAIL():
> if (!(false)) {
> static const struct kunit_loc loc = { .file = ... };
> struct kunit_unary_assert __assertion = { .assert = { .type ... };
> kunit_failed_assertion(test, &loc, &__assertion.assert, ((void *)0));
> };
>
> [1] https://groups.google.com/g/kunit-dev/c/i3fZXgvBrfA/m/VULQg1z6BAAJ
>
> Signed-off-by: Daniel Latypov <[email protected]>
> Suggested-by: Linus Torvalds <[email protected]>
> ---

Nitpicky bikeshedding aside, this looks good to me.

Reviewed-by: David Gow <[email protected]>

Cheers,
-- David


> include/kunit/assert.h | 25 ++++++++++++++++---------
> include/kunit/test.h | 12 +++++++++++-
> lib/kunit/assert.c | 9 +++++----
> lib/kunit/test.c | 15 +++++++++------
> 4 files changed, 41 insertions(+), 20 deletions(-)
>
> diff --git a/include/kunit/assert.h b/include/kunit/assert.h
> index 3da6c792496c..4f91dbdb886a 100644
> --- a/include/kunit/assert.h
> +++ b/include/kunit/assert.h
> @@ -28,11 +28,21 @@ enum kunit_assert_type {
> KUNIT_EXPECTATION,
> };
>
> +/**
> + * struct kunit_loc - Identifies the source location of a line of code.
> + * @line: the line number in the file.
> + * @file: the file name.
> + */
> +struct kunit_loc {
> + int line;
> + const char *file;
> +};
> +
> +#define KUNIT_CURRENT_LOC { .file = __FILE__, .line = __LINE__ }
> +
> /**
> * struct kunit_assert - Data for printing a failed assertion or expectation.
> * @type: the type (either an expectation or an assertion) of this kunit_assert.
> - * @line: the source code line number that the expectation/assertion is at.
> - * @file: the file path of the source file that the expectation/assertion is in.
> * @message: an optional message to provide additional context.
> * @format: a function which formats the data in this kunit_assert to a string.
> *
> @@ -40,9 +50,7 @@ enum kunit_assert_type {
> * format a string to a user reporting the failure.
> */
> struct kunit_assert {
> - enum kunit_assert_type type;
> - int line;
> - const char *file;
> + enum kunit_assert_type type; // TODO([email protected]): delete this
> struct va_format message;
> void (*format)(const struct kunit_assert *assert,
> struct string_stream *stream);
> @@ -65,14 +73,13 @@ struct kunit_assert {
> */
> #define KUNIT_INIT_ASSERT_STRUCT(assert_type, fmt) { \
> .type = assert_type, \
> - .file = __FILE__, \
> - .line = __LINE__, \
> .message = KUNIT_INIT_VA_FMT_NULL, \
> .format = fmt \
> }
>
> -void kunit_base_assert_format(const struct kunit_assert *assert,
> - struct string_stream *stream);
> +void kunit_assert_prologue(const struct kunit_loc *loc,
> + enum kunit_assert_type type,
> + struct string_stream *stream);
>
> void kunit_assert_print_msg(const struct kunit_assert *assert,
> struct string_stream *stream);
> diff --git a/include/kunit/test.h b/include/kunit/test.h
> index ebd45593321e..6e201b45ada6 100644
> --- a/include/kunit/test.h
> +++ b/include/kunit/test.h
> @@ -771,13 +771,18 @@ void __printf(2, 3) kunit_log_append(char *log, const char *fmt, ...);
> #define KUNIT_SUCCEED(test) do {} while (0)
>
> void kunit_failed_assertion(struct kunit *test,
> + const struct kunit_loc *loc,
> + enum kunit_assert_type type,
> struct kunit_assert *assert,
> const char *fmt, ...);
>
> -#define KUNIT_ASSERTION(test, pass, assert_class, INITIALIZER, fmt, ...) do { \
> +#define KUNIT_ASSERTION(test, assert_type, pass, assert_class, INITIALIZER, fmt, ...) do { \
> if (!(pass)) { \
> + static const struct kunit_loc loc = KUNIT_CURRENT_LOC; \
> struct assert_class __assertion = INITIALIZER; \
> kunit_failed_assertion(test, \
> + &loc, \
> + assert_type, \
> &__assertion.assert, \
> fmt, \
> ##__VA_ARGS__); \
> @@ -787,6 +792,7 @@ void kunit_failed_assertion(struct kunit *test,
>
> #define KUNIT_FAIL_ASSERTION(test, assert_type, fmt, ...) \
> KUNIT_ASSERTION(test, \
> + assert_type, \
> false, \
> kunit_fail_assert, \
> KUNIT_INIT_FAIL_ASSERT_STRUCT(assert_type), \
> @@ -817,6 +823,7 @@ void kunit_failed_assertion(struct kunit *test,
> fmt, \
> ...) \
> KUNIT_ASSERTION(test, \
> + assert_type, \
> !!(condition) == !!expected_true, \
> kunit_unary_assert, \
> KUNIT_INIT_UNARY_ASSERT_STRUCT(assert_type, \
> @@ -875,6 +882,7 @@ do { \
> typeof(right) __right = (right); \
> \
> KUNIT_ASSERTION(test, \
> + assert_type, \
> __left op __right, \
> assert_class, \
> ASSERT_CLASS_INIT(assert_type, \
> @@ -1229,6 +1237,7 @@ do { \
> const char *__right = (right); \
> \
> KUNIT_ASSERTION(test, \
> + assert_type, \
> strcmp(__left, __right) op 0, \
> kunit_binary_str_assert, \
> KUNIT_INIT_BINARY_STR_ASSERT_STRUCT(assert_type, \
> @@ -1288,6 +1297,7 @@ do { \
> typeof(ptr) __ptr = (ptr); \
> \
> KUNIT_ASSERTION(test, \
> + assert_type, \
> !IS_ERR_OR_NULL(__ptr), \
> kunit_ptr_not_err_assert, \
> KUNIT_INIT_PTR_NOT_ERR_STRUCT(assert_type, \
> diff --git a/lib/kunit/assert.c b/lib/kunit/assert.c
> index 4d9a1295efc7..9f4492a8e24e 100644
> --- a/lib/kunit/assert.c
> +++ b/lib/kunit/assert.c
> @@ -10,12 +10,13 @@
>
> #include "string-stream.h"
>
> -void kunit_base_assert_format(const struct kunit_assert *assert,
> +void kunit_assert_prologue(const struct kunit_loc *loc,
> + enum kunit_assert_type type,
> struct string_stream *stream)
> {
> const char *expect_or_assert = NULL;
>
> - switch (assert->type) {
> + switch (type) {
> case KUNIT_EXPECTATION:
> expect_or_assert = "EXPECTATION";
> break;
> @@ -25,9 +26,9 @@ void kunit_base_assert_format(const struct kunit_assert *assert,
> }
>
> string_stream_add(stream, "%s FAILED at %s:%d\n",
> - expect_or_assert, assert->file, assert->line);
> + expect_or_assert, loc->file, loc->line);
> }
> -EXPORT_SYMBOL_GPL(kunit_base_assert_format);
> +EXPORT_SYMBOL_GPL(kunit_assert_prologue);
>
> void kunit_assert_print_msg(const struct kunit_assert *assert,
> struct string_stream *stream)
> diff --git a/lib/kunit/test.c b/lib/kunit/test.c
> index 735c1b67d843..3108ed0575d4 100644
> --- a/lib/kunit/test.c
> +++ b/lib/kunit/test.c
> @@ -240,7 +240,8 @@ static void kunit_print_string_stream(struct kunit *test,
> }
> }
>
> -static void kunit_fail(struct kunit *test, struct kunit_assert *assert)
> +static void kunit_fail(struct kunit *test, const struct kunit_loc *loc,
> + enum kunit_assert_type type, struct kunit_assert *assert)
> {
> struct string_stream *stream;
>
> @@ -250,12 +251,12 @@ static void kunit_fail(struct kunit *test, struct kunit_assert *assert)
> if (!stream) {
> WARN(true,
> "Could not allocate stream to print failed assertion in %s:%d\n",
> - assert->file,
> - assert->line);
> + loc->file,
> + loc->line);
> return;
> }
>
> - kunit_base_assert_format(assert, stream);
> + kunit_assert_prologue(loc, type, stream);
> assert->format(assert, stream);
>
> kunit_print_string_stream(test, stream);
> @@ -277,6 +278,8 @@ static void __noreturn kunit_abort(struct kunit *test)
> }
>
> void kunit_failed_assertion(struct kunit *test,
> + const struct kunit_loc *loc,
> + enum kunit_assert_type type,
> struct kunit_assert *assert,
> const char *fmt, ...)
> {
> @@ -286,11 +289,11 @@ void kunit_failed_assertion(struct kunit *test,
> assert->message.fmt = fmt;
> assert->message.va = &args;
>
> - kunit_fail(test, assert);
> + kunit_fail(test, loc, type, assert);
>
> va_end(args);
>
> - if (assert->type == KUNIT_ASSERTION)
> + if (type == KUNIT_ASSERTION)
> kunit_abort(test);
> }
> EXPORT_SYMBOL_GPL(kunit_failed_assertion);
> --
> 2.34.1.575.g55b058a8bb-goog
>

2022-01-11 06:57:29

by David Gow

[permalink] [raw]
Subject: Re: [PATCH 6/6] kunit: drop unused assert_type from kunit_assert and clean up macros

On Sat, Jan 8, 2022 at 9:23 AM Daniel Latypov <[email protected]> wrote:
>
> This field has been split out from kunit_assert to make the struct less
> heavy along with the filename and line number.
>
> This change drops the assert_type field and cleans up all the macros
> that were plumbing assert_type into kunit_assert.
>
> Signed-off-by: Daniel Latypov <[email protected]>
> ---

Looks good to me. Anything that simplifies these macros is a good
thing, in my opinion.

One minor formatting nitpick below.

Reviewed-by: David Gow <[email protected]>

-- David


> include/kunit/assert.h | 46 +++++++++++++-----------------------------
> include/kunit/test.h | 14 +++++--------
> 2 files changed, 19 insertions(+), 41 deletions(-)
>
> diff --git a/include/kunit/assert.h b/include/kunit/assert.h
> index 4f91dbdb886a..21299232c120 100644
> --- a/include/kunit/assert.h
> +++ b/include/kunit/assert.h
> @@ -42,7 +42,6 @@ struct kunit_loc {
>
> /**
> * struct kunit_assert - Data for printing a failed assertion or expectation.
> - * @type: the type (either an expectation or an assertion) of this kunit_assert.
> * @message: an optional message to provide additional context.
> * @format: a function which formats the data in this kunit_assert to a string.
> *
> @@ -50,7 +49,6 @@ struct kunit_loc {
> * format a string to a user reporting the failure.
> */
> struct kunit_assert {
> - enum kunit_assert_type type; // TODO([email protected]): delete this
> struct va_format message;
> void (*format)(const struct kunit_assert *assert,
> struct string_stream *stream);
> @@ -66,13 +64,11 @@ struct kunit_assert {
>
> /**
> * KUNIT_INIT_ASSERT_STRUCT() - Initializer for a &struct kunit_assert.
> - * @assert_type: The type (assertion or expectation) of this kunit_assert.
> * @fmt: The formatting function which builds a string out of this kunit_assert.
> *
> * The base initializer for a &struct kunit_assert.
> */
> -#define KUNIT_INIT_ASSERT_STRUCT(assert_type, fmt) { \
> - .type = assert_type, \
> +#define KUNIT_INIT_ASSERT_STRUCT(fmt) { \
> .message = KUNIT_INIT_VA_FMT_NULL, \
> .format = fmt \
> }
> @@ -98,15 +94,13 @@ void kunit_fail_assert_format(const struct kunit_assert *assert,
> struct string_stream *stream);
>
> /**
> - * KUNIT_INIT_FAIL_ASSERT_STRUCT() - Initializer for &struct kunit_fail_assert.
> - * @type: The type (assertion or expectation) of this kunit_assert.
> + * KUNIT_INIT_FAIL_ASSERT_STRUCT - Initializer for &struct kunit_fail_assert.
> *
> * Initializes a &struct kunit_fail_assert. Intended to be used in
> * KUNIT_EXPECT_* and KUNIT_ASSERT_* macros.
> */
> -#define KUNIT_INIT_FAIL_ASSERT_STRUCT(type) { \
> - .assert = KUNIT_INIT_ASSERT_STRUCT(type, \
> - kunit_fail_assert_format) \
> +#define KUNIT_INIT_FAIL_ASSERT_STRUCT { \
> + .assert = KUNIT_INIT_ASSERT_STRUCT(kunit_fail_assert_format) \
> }
>
> /**
> @@ -130,16 +124,14 @@ void kunit_unary_assert_format(const struct kunit_assert *assert,
>
> /**
> * KUNIT_INIT_UNARY_ASSERT_STRUCT() - Initializes &struct kunit_unary_assert.
> - * @type: The type (assertion or expectation) of this kunit_assert.
> * @cond: A string representation of the expression asserted true or false.
> * @expect_true: True if of type KUNIT_{EXPECT|ASSERT}_TRUE, false otherwise.
> *
> * Initializes a &struct kunit_unary_assert. Intended to be used in
> * KUNIT_EXPECT_* and KUNIT_ASSERT_* macros.
> */
> -#define KUNIT_INIT_UNARY_ASSERT_STRUCT(type, cond, expect_true) { \
> - .assert = KUNIT_INIT_ASSERT_STRUCT(type, \
> - kunit_unary_assert_format), \
> +#define KUNIT_INIT_UNARY_ASSERT_STRUCT(cond, expect_true) { \
> + .assert = KUNIT_INIT_ASSERT_STRUCT(kunit_unary_assert_format), \
> .condition = cond, \
> .expected_true = expect_true \
> }
> @@ -166,16 +158,14 @@ void kunit_ptr_not_err_assert_format(const struct kunit_assert *assert,
> /**
> * KUNIT_INIT_PTR_NOT_ERR_ASSERT_STRUCT() - Initializes a
> * &struct kunit_ptr_not_err_assert.
> - * @type: The type (assertion or expectation) of this kunit_assert.
> * @txt: A string representation of the expression passed to the expectation.
> * @val: The actual evaluated pointer value of the expression.
> *
> * Initializes a &struct kunit_ptr_not_err_assert. Intended to be used in
> * KUNIT_EXPECT_* and KUNIT_ASSERT_* macros.
> */
> -#define KUNIT_INIT_PTR_NOT_ERR_STRUCT(type, txt, val) { \
> - .assert = KUNIT_INIT_ASSERT_STRUCT(type, \
> - kunit_ptr_not_err_assert_format), \
> +#define KUNIT_INIT_PTR_NOT_ERR_STRUCT(txt, val) { \
> + .assert = KUNIT_INIT_ASSERT_STRUCT(kunit_ptr_not_err_assert_format), \
> .text = txt, \
> .value = val \
> }
> @@ -209,7 +199,6 @@ void kunit_binary_assert_format(const struct kunit_assert *assert,
> /**
> * KUNIT_INIT_BINARY_ASSERT_STRUCT() - Initializes a
> * &struct kunit_binary_assert.
> - * @type: The type (assertion or expectation) of this kunit_assert.
> * @op_str: A string representation of the comparison operator (e.g. "==").
> * @left_str: A string representation of the expression in the left slot.
> * @left_val: The actual evaluated value of the expression in the left slot.
> @@ -219,14 +208,12 @@ void kunit_binary_assert_format(const struct kunit_assert *assert,
> * Initializes a &struct kunit_binary_assert. Intended to be used in
> * KUNIT_EXPECT_* and KUNIT_ASSERT_* macros.
> */
> -#define KUNIT_INIT_BINARY_ASSERT_STRUCT(type, \
> - op_str, \
> +#define KUNIT_INIT_BINARY_ASSERT_STRUCT(op_str, \
> left_str, \
> left_val, \
> right_str, \
> right_val) { \
> - .assert = KUNIT_INIT_ASSERT_STRUCT(type, \
> - kunit_binary_assert_format), \
> + .assert = KUNIT_INIT_ASSERT_STRUCT(kunit_binary_assert_format), \
> .operation = op_str, \
> .left_text = left_str, \
> .left_value = left_val, \
> @@ -273,14 +260,12 @@ void kunit_binary_ptr_assert_format(const struct kunit_assert *assert,
> * Initializes a &struct kunit_binary_ptr_assert. Intended to be used in
> * KUNIT_EXPECT_* and KUNIT_ASSERT_* macros.
> */
> -#define KUNIT_INIT_BINARY_PTR_ASSERT_STRUCT(type, \
> - op_str, \
> +#define KUNIT_INIT_BINARY_PTR_ASSERT_STRUCT(op_str, \
> left_str, \
> left_val, \
> right_str, \
> right_val) { \
> - .assert = KUNIT_INIT_ASSERT_STRUCT(type, \
> - kunit_binary_ptr_assert_format), \
> + .assert = KUNIT_INIT_ASSERT_STRUCT(kunit_binary_ptr_assert_format), \
> .operation = op_str, \
> .left_text = left_str, \
> .left_value = left_val, \
> @@ -317,7 +302,6 @@ void kunit_binary_str_assert_format(const struct kunit_assert *assert,
> /**
> * KUNIT_INIT_BINARY_STR_ASSERT_STRUCT() - Initializes a
> * &struct kunit_binary_str_assert.
> - * @type: The type (assertion or expectation) of this kunit_assert.
> * @op_str: A string representation of the comparison operator (e.g. "==").
> * @left_str: A string representation of the expression in the left slot.
> * @left_val: The actual evaluated value of the expression in the left slot.
> @@ -327,14 +311,12 @@ void kunit_binary_str_assert_format(const struct kunit_assert *assert,
> * Initializes a &struct kunit_binary_str_assert. Intended to be used in
> * KUNIT_EXPECT_* and KUNIT_ASSERT_* macros.
> */
> -#define KUNIT_INIT_BINARY_STR_ASSERT_STRUCT(type, \
> - op_str, \
> +#define KUNIT_INIT_BINARY_STR_ASSERT_STRUCT(op_str, \
> left_str, \
> left_val, \
> right_str, \
> right_val) { \
> - .assert = KUNIT_INIT_ASSERT_STRUCT(type, \
> - kunit_binary_str_assert_format), \
> + .assert = KUNIT_INIT_ASSERT_STRUCT(kunit_binary_str_assert_format), \
> .operation = op_str, \
> .left_text = left_str, \
> .left_value = left_val, \
> diff --git a/include/kunit/test.h b/include/kunit/test.h
> index 6e201b45ada6..6f9074ec1995 100644
> --- a/include/kunit/test.h
> +++ b/include/kunit/test.h
> @@ -795,7 +795,7 @@ void kunit_failed_assertion(struct kunit *test,
> assert_type, \
> false, \
> kunit_fail_assert, \
> - KUNIT_INIT_FAIL_ASSERT_STRUCT(assert_type), \
> + KUNIT_INIT_FAIL_ASSERT_STRUCT, \

The '\' at the end of this line is misaligned. (The others seem fine,
but it's possible I missed one.)




> fmt, \
> ##__VA_ARGS__)
>
> @@ -826,8 +826,7 @@ void kunit_failed_assertion(struct kunit *test,
> assert_type, \
> !!(condition) == !!expected_true, \
> kunit_unary_assert, \
> - KUNIT_INIT_UNARY_ASSERT_STRUCT(assert_type, \
> - #condition, \
> + KUNIT_INIT_UNARY_ASSERT_STRUCT(#condition, \
> expected_true), \
> fmt, \
> ##__VA_ARGS__)
> @@ -885,8 +884,7 @@ do { \
> assert_type, \
> __left op __right, \
> assert_class, \
> - ASSERT_CLASS_INIT(assert_type, \
> - #op, \
> + ASSERT_CLASS_INIT(#op, \
> #left, \
> __left, \
> #right, \
> @@ -1240,8 +1238,7 @@ do { \
> assert_type, \
> strcmp(__left, __right) op 0, \
> kunit_binary_str_assert, \
> - KUNIT_INIT_BINARY_STR_ASSERT_STRUCT(assert_type, \
> - #op, \
> + KUNIT_INIT_BINARY_STR_ASSERT_STRUCT(#op, \
> #left, \
> __left, \
> #right, \
> @@ -1300,8 +1297,7 @@ do { \
> assert_type, \
> !IS_ERR_OR_NULL(__ptr), \
> kunit_ptr_not_err_assert, \
> - KUNIT_INIT_PTR_NOT_ERR_STRUCT(assert_type, \
> - #ptr, \
> + KUNIT_INIT_PTR_NOT_ERR_STRUCT(#ptr, \
> __ptr), \
> fmt, \
> ##__VA_ARGS__); \
> --
> 2.34.1.575.g55b058a8bb-goog
>

2022-01-11 17:08:03

by Daniel Latypov

[permalink] [raw]
Subject: Re: [PATCH 5/6] kunit: split out part of kunit_assert into a static const

On Mon, Jan 10, 2022 at 10:57 PM David Gow <[email protected]> wrote:
>
> On Sat, Jan 8, 2022 at 9:23 AM Daniel Latypov <[email protected]> wrote:
> >
> > This is per Linus's suggestion in [1].
> >
> > The issue there is that every KUNIT_EXPECT/KUNIT_ASSERT puts a
> > kunit_assert object onto the stack. Normally we rely on compilers to
> > elide this, but when that doesn't work out, this blows up the stack
> > usage of kunit test functions.
> >
> > We can move some data off the stack by making it static.
> > This change introduces a new `struct kunit_loc` to hold the file and
> > line number and then just passing assert_type (EXPECT or ASSERT) as an
> > argument.
>
> A part of me feels that logically, this struct is really
> "kunit_assert_static_data" (the collection of everything we can make
> static), rather than specifically reserved for location information,

That's exactly what I originally called it.
I was passing it around as
const struct kunit_assert_data *data,
and later
const struct kunit_assert_static_data *data,

But "data" isn't really a readable name.
Especially not when being plumbed through layers of macros as we'd
have to do to move more data into it, see below.

So I settled for dropping kunit_assert_type from the struct so we
could have a more explicit name (kunit_loc) for now.

> but that's an uglier name. My only concern is that, if we do manage to
> make more things static const, will we want to rename this struct?

As noted in the cover letter, the other stuff we'd want to move out
are specific to each assertion type, thus making that trickier.

We could have

struct kunit_assert_static_data {
struct kunit_loc loc;
enum kunit_assert_type type;
/* Optional fields: not all assertions will use these */
const char *left;
const char *right;
};

if we go down that path in the future.

I initially tried out going this way.
But it involved changing the macros a lot more, i.e. we pass around an
initializer for the assert struct right now, right? We'd need to also
pass around the kunit_assert_static_data initializer to allow the
flexibility to optionally set some of these fields now too.

Perhaps after trimming off a layer of indirection in the macros then
it won't be as unreadable...



>
> >
> > In [1], it was suggested to also move out the format string as well, but
> > users could theoretically craft a format string at runtime, so we can't.
> >
> > This change leaves a copy of `assert_type` in kunit_assert for now
> > because cleaning up all the macros to not pass it around is a bit more
> > involved.
> >
> > Here's an example of the expanded code for KUNIT_FAIL():
> > if (!(false)) {
> > static const struct kunit_loc loc = { .file = ... };
> > struct kunit_unary_assert __assertion = { .assert = { .type ... };
> > kunit_failed_assertion(test, &loc, &__assertion.assert, ((void *)0));
> > };
> >
> > [1] https://groups.google.com/g/kunit-dev/c/i3fZXgvBrfA/m/VULQg1z6BAAJ
> >
> > Signed-off-by: Daniel Latypov <[email protected]>
> > Suggested-by: Linus Torvalds <[email protected]>
> > ---
>
> Nitpicky bikeshedding aside, this looks good to me.
>
> Reviewed-by: David Gow <[email protected]>
>
> Cheers,
> -- David
>
>
> > include/kunit/assert.h | 25 ++++++++++++++++---------
> > include/kunit/test.h | 12 +++++++++++-
> > lib/kunit/assert.c | 9 +++++----
> > lib/kunit/test.c | 15 +++++++++------
> > 4 files changed, 41 insertions(+), 20 deletions(-)
> >
> > diff --git a/include/kunit/assert.h b/include/kunit/assert.h
> > index 3da6c792496c..4f91dbdb886a 100644
> > --- a/include/kunit/assert.h
> > +++ b/include/kunit/assert.h
> > @@ -28,11 +28,21 @@ enum kunit_assert_type {
> > KUNIT_EXPECTATION,
> > };
> >
> > +/**
> > + * struct kunit_loc - Identifies the source location of a line of code.
> > + * @line: the line number in the file.
> > + * @file: the file name.
> > + */
> > +struct kunit_loc {
> > + int line;
> > + const char *file;
> > +};
> > +
> > +#define KUNIT_CURRENT_LOC { .file = __FILE__, .line = __LINE__ }
> > +
> > /**
> > * struct kunit_assert - Data for printing a failed assertion or expectation.
> > * @type: the type (either an expectation or an assertion) of this kunit_assert.
> > - * @line: the source code line number that the expectation/assertion is at.
> > - * @file: the file path of the source file that the expectation/assertion is in.
> > * @message: an optional message to provide additional context.
> > * @format: a function which formats the data in this kunit_assert to a string.
> > *
> > @@ -40,9 +50,7 @@ enum kunit_assert_type {
> > * format a string to a user reporting the failure.
> > */
> > struct kunit_assert {
> > - enum kunit_assert_type type;
> > - int line;
> > - const char *file;
> > + enum kunit_assert_type type; // TODO([email protected]): delete this
> > struct va_format message;
> > void (*format)(const struct kunit_assert *assert,
> > struct string_stream *stream);
> > @@ -65,14 +73,13 @@ struct kunit_assert {
> > */
> > #define KUNIT_INIT_ASSERT_STRUCT(assert_type, fmt) { \
> > .type = assert_type, \
> > - .file = __FILE__, \
> > - .line = __LINE__, \
> > .message = KUNIT_INIT_VA_FMT_NULL, \
> > .format = fmt \
> > }
> >
> > -void kunit_base_assert_format(const struct kunit_assert *assert,
> > - struct string_stream *stream);
> > +void kunit_assert_prologue(const struct kunit_loc *loc,
> > + enum kunit_assert_type type,
> > + struct string_stream *stream);
> >
> > void kunit_assert_print_msg(const struct kunit_assert *assert,
> > struct string_stream *stream);
> > diff --git a/include/kunit/test.h b/include/kunit/test.h
> > index ebd45593321e..6e201b45ada6 100644
> > --- a/include/kunit/test.h
> > +++ b/include/kunit/test.h
> > @@ -771,13 +771,18 @@ void __printf(2, 3) kunit_log_append(char *log, const char *fmt, ...);
> > #define KUNIT_SUCCEED(test) do {} while (0)
> >
> > void kunit_failed_assertion(struct kunit *test,
> > + const struct kunit_loc *loc,
> > + enum kunit_assert_type type,
> > struct kunit_assert *assert,
> > const char *fmt, ...);
> >
> > -#define KUNIT_ASSERTION(test, pass, assert_class, INITIALIZER, fmt, ...) do { \
> > +#define KUNIT_ASSERTION(test, assert_type, pass, assert_class, INITIALIZER, fmt, ...) do { \
> > if (!(pass)) { \
> > + static const struct kunit_loc loc = KUNIT_CURRENT_LOC; \
> > struct assert_class __assertion = INITIALIZER; \
> > kunit_failed_assertion(test, \
> > + &loc, \
> > + assert_type, \
> > &__assertion.assert, \
> > fmt, \
> > ##__VA_ARGS__); \
> > @@ -787,6 +792,7 @@ void kunit_failed_assertion(struct kunit *test,
> >
> > #define KUNIT_FAIL_ASSERTION(test, assert_type, fmt, ...) \
> > KUNIT_ASSERTION(test, \
> > + assert_type, \
> > false, \
> > kunit_fail_assert, \
> > KUNIT_INIT_FAIL_ASSERT_STRUCT(assert_type), \
> > @@ -817,6 +823,7 @@ void kunit_failed_assertion(struct kunit *test,
> > fmt, \
> > ...) \
> > KUNIT_ASSERTION(test, \
> > + assert_type, \
> > !!(condition) == !!expected_true, \
> > kunit_unary_assert, \
> > KUNIT_INIT_UNARY_ASSERT_STRUCT(assert_type, \
> > @@ -875,6 +882,7 @@ do { \
> > typeof(right) __right = (right); \
> > \
> > KUNIT_ASSERTION(test, \
> > + assert_type, \
> > __left op __right, \
> > assert_class, \
> > ASSERT_CLASS_INIT(assert_type, \
> > @@ -1229,6 +1237,7 @@ do { \
> > const char *__right = (right); \
> > \
> > KUNIT_ASSERTION(test, \
> > + assert_type, \
> > strcmp(__left, __right) op 0, \
> > kunit_binary_str_assert, \
> > KUNIT_INIT_BINARY_STR_ASSERT_STRUCT(assert_type, \
> > @@ -1288,6 +1297,7 @@ do { \
> > typeof(ptr) __ptr = (ptr); \
> > \
> > KUNIT_ASSERTION(test, \
> > + assert_type, \
> > !IS_ERR_OR_NULL(__ptr), \
> > kunit_ptr_not_err_assert, \
> > KUNIT_INIT_PTR_NOT_ERR_STRUCT(assert_type, \
> > diff --git a/lib/kunit/assert.c b/lib/kunit/assert.c
> > index 4d9a1295efc7..9f4492a8e24e 100644
> > --- a/lib/kunit/assert.c
> > +++ b/lib/kunit/assert.c
> > @@ -10,12 +10,13 @@
> >
> > #include "string-stream.h"
> >
> > -void kunit_base_assert_format(const struct kunit_assert *assert,
> > +void kunit_assert_prologue(const struct kunit_loc *loc,
> > + enum kunit_assert_type type,
> > struct string_stream *stream)
> > {
> > const char *expect_or_assert = NULL;
> >
> > - switch (assert->type) {
> > + switch (type) {
> > case KUNIT_EXPECTATION:
> > expect_or_assert = "EXPECTATION";
> > break;
> > @@ -25,9 +26,9 @@ void kunit_base_assert_format(const struct kunit_assert *assert,
> > }
> >
> > string_stream_add(stream, "%s FAILED at %s:%d\n",
> > - expect_or_assert, assert->file, assert->line);
> > + expect_or_assert, loc->file, loc->line);
> > }
> > -EXPORT_SYMBOL_GPL(kunit_base_assert_format);
> > +EXPORT_SYMBOL_GPL(kunit_assert_prologue);
> >
> > void kunit_assert_print_msg(const struct kunit_assert *assert,
> > struct string_stream *stream)
> > diff --git a/lib/kunit/test.c b/lib/kunit/test.c
> > index 735c1b67d843..3108ed0575d4 100644
> > --- a/lib/kunit/test.c
> > +++ b/lib/kunit/test.c
> > @@ -240,7 +240,8 @@ static void kunit_print_string_stream(struct kunit *test,
> > }
> > }
> >
> > -static void kunit_fail(struct kunit *test, struct kunit_assert *assert)
> > +static void kunit_fail(struct kunit *test, const struct kunit_loc *loc,
> > + enum kunit_assert_type type, struct kunit_assert *assert)
> > {
> > struct string_stream *stream;
> >
> > @@ -250,12 +251,12 @@ static void kunit_fail(struct kunit *test, struct kunit_assert *assert)
> > if (!stream) {
> > WARN(true,
> > "Could not allocate stream to print failed assertion in %s:%d\n",
> > - assert->file,
> > - assert->line);
> > + loc->file,
> > + loc->line);
> > return;
> > }
> >
> > - kunit_base_assert_format(assert, stream);
> > + kunit_assert_prologue(loc, type, stream);
> > assert->format(assert, stream);
> >
> > kunit_print_string_stream(test, stream);
> > @@ -277,6 +278,8 @@ static void __noreturn kunit_abort(struct kunit *test)
> > }
> >
> > void kunit_failed_assertion(struct kunit *test,
> > + const struct kunit_loc *loc,
> > + enum kunit_assert_type type,
> > struct kunit_assert *assert,
> > const char *fmt, ...)
> > {
> > @@ -286,11 +289,11 @@ void kunit_failed_assertion(struct kunit *test,
> > assert->message.fmt = fmt;
> > assert->message.va = &args;
> >
> > - kunit_fail(test, assert);
> > + kunit_fail(test, loc, type, assert);
> >
> > va_end(args);
> >
> > - if (assert->type == KUNIT_ASSERTION)
> > + if (type == KUNIT_ASSERTION)
> > kunit_abort(test);
> > }
> > EXPORT_SYMBOL_GPL(kunit_failed_assertion);
> > --
> > 2.34.1.575.g55b058a8bb-goog
> >

2022-01-11 17:28:28

by Daniel Latypov

[permalink] [raw]
Subject: Re: [PATCH 1/6] kunit: add example test case showing off all the expect macros

On Mon, Jan 10, 2022 at 10:51 PM David Gow <[email protected]> wrote:
>
> On Sat, Jan 8, 2022 at 9:23 AM Daniel Latypov <[email protected]> wrote:
> >
> > Currently, these macros are only really documented near the bottom of
> > https://www.kernel.org/doc/html/latest/dev-tools/kunit/api/test.html#c.KUNIT_FAIL.
> >
> > E.g. it's likely someone might just not realize that
> > KUNIT_EXPECT_STREQ() exists and instead use KUNIT_EXPECT_FALSE(strcmp())
> > or similar.
> >
> > This can also serve as a basic smoketest that the KUnit assert machinery
> > still works for all the macros.
> >
> > Signed-off-by: Daniel Latypov <[email protected]>
> > ---
>
> I think this is a great idea. I will note that this definitely isn't a
> full test _of_ the assertion macros (in that it only exercises the
> success case), so keeping it as an example is probably best.
>
> A few possible ideas below, but I'm happy enough with this as-is regardless.

Applied the ideas locally.
It led to this diffstat
lib/kunit/kunit-example-test.c | 42 +++++++++++++++++++-----------------------
1 file changed, 19 insertions(+), 23 deletions(-)

So it's now a bit shorter and 8 of the added lines are new comments.

>
> Reviewed-by: David Gow <[email protected]>
>
> > lib/kunit/kunit-example-test.c | 46 ++++++++++++++++++++++++++++++++++
> > 1 file changed, 46 insertions(+)
> >
> > diff --git a/lib/kunit/kunit-example-test.c b/lib/kunit/kunit-example-test.c
> > index 51099b0ca29c..182a64c12541 100644
> > --- a/lib/kunit/kunit-example-test.c
> > +++ b/lib/kunit/kunit-example-test.c
> > @@ -69,6 +69,51 @@ static void example_mark_skipped_test(struct kunit *test)
> > /* This line should run */
> > kunit_info(test, "You should see this line.");
> > }
> > +
> > +/*
> > + * This test shows off all the KUNIT_EXPECT macros.
> > + */
> > +static void example_all_expect_macros_test(struct kunit *test)
> > +{
> > + KUNIT_EXPECT_TRUE(test, true);
> > + KUNIT_EXPECT_FALSE(test, false);
>
> _Maybe_ it's worth having a comment for each of these groups ('boolean
> assertions', 'integer assertions', 'pointer assertions', etc)?

Good idea, done.

>
> > +
> > + KUNIT_EXPECT_EQ(test, 1, 1);
> > + KUNIT_EXPECT_GE(test, 1, 1);
> > + KUNIT_EXPECT_LE(test, 1, 1);
> > + KUNIT_EXPECT_NE(test, 1, 0);
> > + KUNIT_EXPECT_GT(test, 1, 0);
> > + KUNIT_EXPECT_LT(test, 0, 1);
> > +
> > + KUNIT_EXPECT_NOT_ERR_OR_NULL(test, test);
> > + KUNIT_EXPECT_PTR_EQ(test, NULL, NULL);
> > + KUNIT_EXPECT_PTR_NE(test, test, NULL);
> > +
> > + KUNIT_EXPECT_STREQ(test, "hi", "hi");
> > + KUNIT_EXPECT_STRNEQ(test, "hi", "bye");
> > +
> > + /*
> > + * There are also _MSG variants of all of the above that let you include
> > + * additional text on failure.
> > + */
>
> There are also the ASSERT vs EXPECT variations. While it may be
> excessive to also include all of these, particularly in an example, it
> might be worth mentioning them in a comment somewhere?

I've gone ahead and added a section with one example

+ /*
+ * There are also ASSERT variants of all of the above that abort test
+ * execution if they fail. Useful for memory allocations, etc.
+ */
+ KUNIT_ASSERT_GT(test, sizeof(char), 0);
+


>
> Alternatively, if this is bloating the example too much, we could have
> only one example each of the ASSERT and _MSG variants.
>
> > + KUNIT_EXPECT_TRUE_MSG(test, true, "msg");
> > + KUNIT_EXPECT_FALSE_MSG(test, false, "msg");
>
> Part of me feels that a better message than "msg" would be nice to
> have here, but I can't think of a good one. Maybe (particularly for
> the less obvious integer/string/pointer macros below), having a
> description of what's being asserted?


I've gone ahead and added truncated this down to one example

+ KUNIT_EXPECT_GT_MSG(test, sizeof(int), 0, "Your ints are 0-bit?!");
+ KUNIT_ASSERT_GT_MSG(test, sizeof(int), 0, "Your ints are 0-bit?!");

>
>
>
> > +
> > + KUNIT_EXPECT_EQ_MSG(test, 1, 1, "msg");
> > + KUNIT_EXPECT_GE_MSG(test, 1, 1, "msg");
> > + KUNIT_EXPECT_LE_MSG(test, 1, 1, "msg");
> > + KUNIT_EXPECT_NE_MSG(test, 1, 0, "msg");
> > + KUNIT_EXPECT_GT_MSG(test, 1, 0, "msg");
> > + KUNIT_EXPECT_LT_MSG(test, 0, 1, "msg");
> > +
> > + KUNIT_EXPECT_NOT_ERR_OR_NULL_MSG(test, test, "msg");
> > + KUNIT_EXPECT_PTR_EQ_MSG(test, NULL, NULL, "msg");
> > + KUNIT_EXPECT_PTR_NE_MSG(test, test, NULL, "msg");
> > +
> > + KUNIT_EXPECT_STREQ_MSG(test, "hi", "hi", "msg");
> > + KUNIT_EXPECT_STRNEQ_MSG(test, "hi", "bye", "msg");
> > +}
> > +
> > /*
> > * Here we make a list of all the test cases we want to add to the test suite
> > * below.
> > @@ -83,6 +128,7 @@ static struct kunit_case example_test_cases[] = {
> > KUNIT_CASE(example_simple_test),
> > KUNIT_CASE(example_skip_test),
> > KUNIT_CASE(example_mark_skipped_test),
> > + KUNIT_CASE(example_all_expect_macros_test),
> > {}
> > };
> >
> > --
> > 2.34.1.575.g55b058a8bb-goog
> >

2022-01-11 18:34:28

by Daniel Latypov

[permalink] [raw]
Subject: Re: [PATCH 3/6] kunit: drop unused kunit* field in kunit_assert

On Mon, Jan 10, 2022 at 10:51 PM David Gow <[email protected]> wrote:
>
> On Sat, Jan 8, 2022 at 9:23 AM Daniel Latypov <[email protected]> wrote:
> >
> > The `struct kunit* test` field in kunit_assert is unused.
> > Note: we have access to `test` where we need it via the string_stream
> > object. I assume `test` in `kunit_assert` predates this and was leftover
> > after some refactoring.
>
> Note that I don't like the idea of accessing 'test' via the string
> stream in general, but we don't seem to ever actually do this (as far
> as I can tell). Maybe if we wanted to be super nitpicky, rewording the
> note to say "if we need it" rather than "where we need it" would be
> clearer.

Ah, I had meant "where we need it" == "where string_stream needs it,
as its the only user that doesn't get test passed as a parameter
everywhere".

Updated the wording to
Note: string_stream needs it, but it has its own `test` field.

>
> >
> > This patch removes the field and cleans up the macros to avoid
> > needlessly passing around `test`.
> >
> > Signed-off-by: Daniel Latypov <[email protected]>
> > ---
>
> Looks good, thanks!
>
> Reviewed-by: David Gow <[email protected]>
>
>
> > include/kunit/assert.h | 45 ++++++++++++------------------------------
> > include/kunit/test.h | 14 +++++--------
> > 2 files changed, 18 insertions(+), 41 deletions(-)
> >
> > diff --git a/include/kunit/assert.h b/include/kunit/assert.h
> > index ad889b539ab3..3da6c792496c 100644
> > --- a/include/kunit/assert.h
> > +++ b/include/kunit/assert.h
> > @@ -30,7 +30,6 @@ enum kunit_assert_type {
> >
> > /**
> > * struct kunit_assert - Data for printing a failed assertion or expectation.
> > - * @test: the test case this expectation/assertion is associated with.
> > * @type: the type (either an expectation or an assertion) of this kunit_assert.
> > * @line: the source code line number that the expectation/assertion is at.
> > * @file: the file path of the source file that the expectation/assertion is in.
> > @@ -41,7 +40,6 @@ enum kunit_assert_type {
> > * format a string to a user reporting the failure.
> > */
> > struct kunit_assert {
> > - struct kunit *test;
> > enum kunit_assert_type type;
> > int line;
> > const char *file;
> > @@ -60,14 +58,12 @@ struct kunit_assert {
> >
> > /**
> > * KUNIT_INIT_ASSERT_STRUCT() - Initializer for a &struct kunit_assert.
> > - * @kunit: The test case that this expectation/assertion is associated with.
> > * @assert_type: The type (assertion or expectation) of this kunit_assert.
> > * @fmt: The formatting function which builds a string out of this kunit_assert.
> > *
> > * The base initializer for a &struct kunit_assert.
> > */
> > -#define KUNIT_INIT_ASSERT_STRUCT(kunit, assert_type, fmt) { \
> > - .test = kunit, \
> > +#define KUNIT_INIT_ASSERT_STRUCT(assert_type, fmt) { \
> > .type = assert_type, \
> > .file = __FILE__, \
> > .line = __LINE__, \
> > @@ -96,15 +92,13 @@ void kunit_fail_assert_format(const struct kunit_assert *assert,
> >
> > /**
> > * KUNIT_INIT_FAIL_ASSERT_STRUCT() - Initializer for &struct kunit_fail_assert.
> > - * @test: The test case that this expectation/assertion is associated with.
> > * @type: The type (assertion or expectation) of this kunit_assert.
> > *
> > * Initializes a &struct kunit_fail_assert. Intended to be used in
> > * KUNIT_EXPECT_* and KUNIT_ASSERT_* macros.
> > */
> > -#define KUNIT_INIT_FAIL_ASSERT_STRUCT(test, type) { \
> > - .assert = KUNIT_INIT_ASSERT_STRUCT(test, \
> > - type, \
> > +#define KUNIT_INIT_FAIL_ASSERT_STRUCT(type) { \
> > + .assert = KUNIT_INIT_ASSERT_STRUCT(type, \
> > kunit_fail_assert_format) \
> > }
> >
> > @@ -129,7 +123,6 @@ void kunit_unary_assert_format(const struct kunit_assert *assert,
> >
> > /**
> > * KUNIT_INIT_UNARY_ASSERT_STRUCT() - Initializes &struct kunit_unary_assert.
> > - * @test: The test case that this expectation/assertion is associated with.
> > * @type: The type (assertion or expectation) of this kunit_assert.
> > * @cond: A string representation of the expression asserted true or false.
> > * @expect_true: True if of type KUNIT_{EXPECT|ASSERT}_TRUE, false otherwise.
> > @@ -137,9 +130,8 @@ void kunit_unary_assert_format(const struct kunit_assert *assert,
> > * Initializes a &struct kunit_unary_assert. Intended to be used in
> > * KUNIT_EXPECT_* and KUNIT_ASSERT_* macros.
> > */
> > -#define KUNIT_INIT_UNARY_ASSERT_STRUCT(test, type, cond, expect_true) { \
> > - .assert = KUNIT_INIT_ASSERT_STRUCT(test, \
> > - type, \
> > +#define KUNIT_INIT_UNARY_ASSERT_STRUCT(type, cond, expect_true) { \
> > + .assert = KUNIT_INIT_ASSERT_STRUCT(type, \
> > kunit_unary_assert_format), \
> > .condition = cond, \
> > .expected_true = expect_true \
> > @@ -167,7 +159,6 @@ void kunit_ptr_not_err_assert_format(const struct kunit_assert *assert,
> > /**
> > * KUNIT_INIT_PTR_NOT_ERR_ASSERT_STRUCT() - Initializes a
> > * &struct kunit_ptr_not_err_assert.
> > - * @test: The test case that this expectation/assertion is associated with.
> > * @type: The type (assertion or expectation) of this kunit_assert.
> > * @txt: A string representation of the expression passed to the expectation.
> > * @val: The actual evaluated pointer value of the expression.
> > @@ -175,9 +166,8 @@ void kunit_ptr_not_err_assert_format(const struct kunit_assert *assert,
> > * Initializes a &struct kunit_ptr_not_err_assert. Intended to be used in
> > * KUNIT_EXPECT_* and KUNIT_ASSERT_* macros.
> > */
> > -#define KUNIT_INIT_PTR_NOT_ERR_STRUCT(test, type, txt, val) { \
> > - .assert = KUNIT_INIT_ASSERT_STRUCT(test, \
> > - type, \
> > +#define KUNIT_INIT_PTR_NOT_ERR_STRUCT(type, txt, val) { \
> > + .assert = KUNIT_INIT_ASSERT_STRUCT(type, \
> > kunit_ptr_not_err_assert_format), \
> > .text = txt, \
> > .value = val \
> > @@ -212,7 +202,6 @@ void kunit_binary_assert_format(const struct kunit_assert *assert,
> > /**
> > * KUNIT_INIT_BINARY_ASSERT_STRUCT() - Initializes a
> > * &struct kunit_binary_assert.
> > - * @test: The test case that this expectation/assertion is associated with.
> > * @type: The type (assertion or expectation) of this kunit_assert.
> > * @op_str: A string representation of the comparison operator (e.g. "==").
> > * @left_str: A string representation of the expression in the left slot.
> > @@ -223,15 +212,13 @@ void kunit_binary_assert_format(const struct kunit_assert *assert,
> > * Initializes a &struct kunit_binary_assert. Intended to be used in
> > * KUNIT_EXPECT_* and KUNIT_ASSERT_* macros.
> > */
> > -#define KUNIT_INIT_BINARY_ASSERT_STRUCT(test, \
> > - type, \
> > +#define KUNIT_INIT_BINARY_ASSERT_STRUCT(type, \
> > op_str, \
> > left_str, \
> > left_val, \
> > right_str, \
> > right_val) { \
> > - .assert = KUNIT_INIT_ASSERT_STRUCT(test, \
> > - type, \
> > + .assert = KUNIT_INIT_ASSERT_STRUCT(type, \
> > kunit_binary_assert_format), \
> > .operation = op_str, \
> > .left_text = left_str, \
> > @@ -269,7 +256,6 @@ void kunit_binary_ptr_assert_format(const struct kunit_assert *assert,
> > /**
> > * KUNIT_INIT_BINARY_PTR_ASSERT_STRUCT() - Initializes a
> > * &struct kunit_binary_ptr_assert.
> > - * @test: The test case that this expectation/assertion is associated with.
> > * @type: The type (assertion or expectation) of this kunit_assert.
> > * @op_str: A string representation of the comparison operator (e.g. "==").
> > * @left_str: A string representation of the expression in the left slot.
> > @@ -280,15 +266,13 @@ void kunit_binary_ptr_assert_format(const struct kunit_assert *assert,
> > * Initializes a &struct kunit_binary_ptr_assert. Intended to be used in
> > * KUNIT_EXPECT_* and KUNIT_ASSERT_* macros.
> > */
> > -#define KUNIT_INIT_BINARY_PTR_ASSERT_STRUCT(test, \
> > - type, \
> > +#define KUNIT_INIT_BINARY_PTR_ASSERT_STRUCT(type, \
> > op_str, \
> > left_str, \
> > left_val, \
> > right_str, \
> > right_val) { \
> > - .assert = KUNIT_INIT_ASSERT_STRUCT(test, \
> > - type, \
> > + .assert = KUNIT_INIT_ASSERT_STRUCT(type, \
> > kunit_binary_ptr_assert_format), \
> > .operation = op_str, \
> > .left_text = left_str, \
> > @@ -326,7 +310,6 @@ void kunit_binary_str_assert_format(const struct kunit_assert *assert,
> > /**
> > * KUNIT_INIT_BINARY_STR_ASSERT_STRUCT() - Initializes a
> > * &struct kunit_binary_str_assert.
> > - * @test: The test case that this expectation/assertion is associated with.
> > * @type: The type (assertion or expectation) of this kunit_assert.
> > * @op_str: A string representation of the comparison operator (e.g. "==").
> > * @left_str: A string representation of the expression in the left slot.
> > @@ -337,15 +320,13 @@ void kunit_binary_str_assert_format(const struct kunit_assert *assert,
> > * Initializes a &struct kunit_binary_str_assert. Intended to be used in
> > * KUNIT_EXPECT_* and KUNIT_ASSERT_* macros.
> > */
> > -#define KUNIT_INIT_BINARY_STR_ASSERT_STRUCT(test, \
> > - type, \
> > +#define KUNIT_INIT_BINARY_STR_ASSERT_STRUCT(type, \
> > op_str, \
> > left_str, \
> > left_val, \
> > right_str, \
> > right_val) { \
> > - .assert = KUNIT_INIT_ASSERT_STRUCT(test, \
> > - type, \
> > + .assert = KUNIT_INIT_ASSERT_STRUCT(type, \
> > kunit_binary_str_assert_format), \
> > .operation = op_str, \
> > .left_text = left_str, \
> > diff --git a/include/kunit/test.h b/include/kunit/test.h
> > index 690a28dfc795..ebd45593321e 100644
> > --- a/include/kunit/test.h
> > +++ b/include/kunit/test.h
> > @@ -789,7 +789,7 @@ void kunit_failed_assertion(struct kunit *test,
> > KUNIT_ASSERTION(test, \
> > false, \
> > kunit_fail_assert, \
> > - KUNIT_INIT_FAIL_ASSERT_STRUCT(test, assert_type), \
> > + KUNIT_INIT_FAIL_ASSERT_STRUCT(assert_type), \
> > fmt, \
> > ##__VA_ARGS__)
> >
> > @@ -819,8 +819,7 @@ void kunit_failed_assertion(struct kunit *test,
> > KUNIT_ASSERTION(test, \
> > !!(condition) == !!expected_true, \
> > kunit_unary_assert, \
> > - KUNIT_INIT_UNARY_ASSERT_STRUCT(test, \
> > - assert_type, \
> > + KUNIT_INIT_UNARY_ASSERT_STRUCT(assert_type, \
> > #condition, \
> > expected_true), \
> > fmt, \
> > @@ -878,8 +877,7 @@ do { \
> > KUNIT_ASSERTION(test, \
> > __left op __right, \
> > assert_class, \
> > - ASSERT_CLASS_INIT(test, \
> > - assert_type, \
> > + ASSERT_CLASS_INIT(assert_type, \
> > #op, \
> > #left, \
> > __left, \
> > @@ -1233,8 +1231,7 @@ do { \
> > KUNIT_ASSERTION(test, \
> > strcmp(__left, __right) op 0, \
> > kunit_binary_str_assert, \
> > - KUNIT_INIT_BINARY_STR_ASSERT_STRUCT(test, \
> > - assert_type, \
> > + KUNIT_INIT_BINARY_STR_ASSERT_STRUCT(assert_type, \
> > #op, \
> > #left, \
> > __left, \
> > @@ -1293,8 +1290,7 @@ do { \
> > KUNIT_ASSERTION(test, \
> > !IS_ERR_OR_NULL(__ptr), \
> > kunit_ptr_not_err_assert, \
> > - KUNIT_INIT_PTR_NOT_ERR_STRUCT(test, \
> > - assert_type, \
> > + KUNIT_INIT_PTR_NOT_ERR_STRUCT(assert_type, \
> > #ptr, \
> > __ptr), \
> > fmt, \
> > --
> > 2.34.1.575.g55b058a8bb-goog
> >

2022-01-11 18:42:21

by Daniel Latypov

[permalink] [raw]
Subject: Re: [PATCH 2/6] kunit: move check if assertion passed into the macros

On Mon, Jan 10, 2022 at 10:51 PM David Gow <[email protected]> wrote:
>
> On Tue, Jan 11, 2022 at 6:33 AM Daniel Latypov <[email protected]> wrote:
> >
> > On Mon, Jan 10, 2022 at 2:21 PM Brendan Higgins
> > <[email protected]> wrote:
> > >
> > > On Fri, Jan 7, 2022 at 8:23 PM Daniel Latypov <[email protected]> wrote:
> > > >
> > > > Currently the code always calls kunit_do_assertion() even though it does
> > > > nothing when `pass` is true.
> > > >
> > > > This change moves the `if(!(pass))` check into the macro instead
> > > > and renames the function to kunit_failed_assertion().
> > > > I feel this a bit easier to read and understand.
> > > >
> > > > This has the potential upside of avoiding a function call that does
> > > > nothing most of the time (assuming your tests are passing) but comes
> > > > with the downside of generating a bit more code and branches.
> > > >
> > > > This also means we don't have to initialize structs that we don't need,
> > > > which will become a tiny bit more expensive if we switch over to using
> > > > static variables to try and reduce stack usage. (There's runtime code
> > > > to check if the variable has been initialized yet or not).
> > > >
> > > > Signed-off-by: Daniel Latypov <[email protected]>
> > >
> > > Tiny nit, see below. Otherwise:
> > >
> > > Reviewed-by: Brendan Higgins <[email protected]>
> > >
> > > > ---
> > > > include/kunit/test.h | 20 ++++++++++----------
> > > > lib/kunit/test.c | 13 ++++---------
> > > > 2 files changed, 14 insertions(+), 19 deletions(-)
> > > >
> > > > diff --git a/include/kunit/test.h b/include/kunit/test.h
> > > > index b26400731c02..690a28dfc795 100644
> > > > --- a/include/kunit/test.h
> > > > +++ b/include/kunit/test.h
> > > > @@ -770,18 +770,18 @@ void __printf(2, 3) kunit_log_append(char *log, const char *fmt, ...);
> > > > */
> > > > #define KUNIT_SUCCEED(test) do {} while (0)
> > > >
> > > > -void kunit_do_assertion(struct kunit *test,
> > > > - struct kunit_assert *assert,
> > > > - bool pass,
> > > > - const char *fmt, ...);
> > > > +void kunit_failed_assertion(struct kunit *test,
> > > > + struct kunit_assert *assert,
> > > > + const char *fmt, ...);
> > >
> > > Tiny nit: I think this should be kunit_fail_assertion. I think
> > > functions should be in the active tense, imperative mood since when
> > > you call a function you are telling it to do something.
> > >
> > > Also, do we need to worry about this getting confused with KUNIT_FAIL,
> > > or KUNIT_FAIL_ASSERTION:
> >
> > So do we want to try and pick a different name from
> > kunit_fail_assertion() to avoid confusion with the macro?
> > That's partly why I went with past tense.
> > Perhaps: "kunit_do_assertion() => kunit_do_failed_assertion()" instead?
>
> I'm not particularly picky about the name personally. But if I had to
> join the bikeshedding, I'd probably go with kunit_assertion_fail() or
> similar (kunit_assertion_failed works too, past-tense-wise.)
>
> But kunit_do_fail{,ed}_assertion() would work too.

I've gone ahead and locally renamed it to kunit_do_failed_assertion().
Talking offline, Brendan seemed ok with it, so we have 2 votes of
"it's good enough".

>
>
> >
> > Tangent: we have some similar confusing names, e.g. KUNIT_ASSERTION is
> > both the name of a macro and an enum (kunit_assert_type), and those
> > have the exact same case.
> >
> > >
> > > https://elixir.bootlin.com/linux/v5.16/source/include/kunit/test.h#L788
> > >
> > > ?
> > >
> > > > #define KUNIT_ASSERTION(test, pass, assert_class, INITIALIZER, fmt, ...) do { \
> > > > - struct assert_class __assertion = INITIALIZER; \
> > > > - kunit_do_assertion(test, \
> > > > - &__assertion.assert, \
> > > > - pass, \
> > > > - fmt, \
> > > > - ##__VA_ARGS__); \
> > > > + if (!(pass)) { \
> > > > + struct assert_class __assertion = INITIALIZER; \
> > > > + kunit_failed_assertion(test, \
> > > > + &__assertion.assert, \
> > > > + fmt, \
> > > > + ##__VA_ARGS__); \
> > > > + } \
> > > > } while (0)
> > > >
> > > >
> > > > diff --git a/lib/kunit/test.c b/lib/kunit/test.c
> > > > index c7ed4aabec04..5ad671745483 100644
> > > > --- a/lib/kunit/test.c
> > > > +++ b/lib/kunit/test.c
> > > > @@ -275,16 +275,11 @@ static void __noreturn kunit_abort(struct kunit *test)
> > > > WARN_ONCE(true, "Throw could not abort from test!\n");
> > > > }
> > > >
> > > > -void kunit_do_assertion(struct kunit *test,
> > > > - struct kunit_assert *assert,
> > > > - bool pass,
> > > > - const char *fmt, ...)
> > > > +void kunit_failed_assertion(struct kunit *test,
> > > > + struct kunit_assert *assert,
> > > > + const char *fmt, ...)
> > > > {
> > > > va_list args;
> > > > -
> > > > - if (pass)
> > > > - return;
> > > > -
> > > > va_start(args, fmt);
> > > >
> > > > assert->message.fmt = fmt;
> > > > @@ -297,7 +292,7 @@ void kunit_do_assertion(struct kunit *test,
> > > > if (assert->type == KUNIT_ASSERTION)
> > > > kunit_abort(test);
> > > > }
> > > > -EXPORT_SYMBOL_GPL(kunit_do_assertion);
> > > > +EXPORT_SYMBOL_GPL(kunit_failed_assertion);
> > > >
> > > > void kunit_init_test(struct kunit *test, const char *name, char *log)
> > > > {
> > > > --
> > > > 2.34.1.575.g55b058a8bb-goog
> > > >

2022-01-11 19:21:30

by Daniel Latypov

[permalink] [raw]
Subject: Re: [PATCH 6/6] kunit: drop unused assert_type from kunit_assert and clean up macros

On Mon, Jan 10, 2022 at 10:57 PM David Gow <[email protected]> wrote:
>
> On Sat, Jan 8, 2022 at 9:23 AM Daniel Latypov <[email protected]> wrote:
> >
> > This field has been split out from kunit_assert to make the struct less
> > heavy along with the filename and line number.
> >
> > This change drops the assert_type field and cleans up all the macros
> > that were plumbing assert_type into kunit_assert.
> >
> > Signed-off-by: Daniel Latypov <[email protected]>
> > ---
>
> Looks good to me. Anything that simplifies these macros is a good
> thing, in my opinion.
>
> One minor formatting nitpick below.
>
> Reviewed-by: David Gow <[email protected]>
>
> -- David
>
>
> > include/kunit/assert.h | 46 +++++++++++++-----------------------------
> > include/kunit/test.h | 14 +++++--------
> > 2 files changed, 19 insertions(+), 41 deletions(-)
> >
> > diff --git a/include/kunit/assert.h b/include/kunit/assert.h
> > index 4f91dbdb886a..21299232c120 100644
> > --- a/include/kunit/assert.h
> > +++ b/include/kunit/assert.h
> > @@ -42,7 +42,6 @@ struct kunit_loc {
> >
> > /**
> > * struct kunit_assert - Data for printing a failed assertion or expectation.
> > - * @type: the type (either an expectation or an assertion) of this kunit_assert.
> > * @message: an optional message to provide additional context.
> > * @format: a function which formats the data in this kunit_assert to a string.
> > *
> > @@ -50,7 +49,6 @@ struct kunit_loc {
> > * format a string to a user reporting the failure.
> > */
> > struct kunit_assert {
> > - enum kunit_assert_type type; // TODO([email protected]): delete this
> > struct va_format message;
> > void (*format)(const struct kunit_assert *assert,
> > struct string_stream *stream);
> > @@ -66,13 +64,11 @@ struct kunit_assert {
> >
> > /**
> > * KUNIT_INIT_ASSERT_STRUCT() - Initializer for a &struct kunit_assert.
> > - * @assert_type: The type (assertion or expectation) of this kunit_assert.
> > * @fmt: The formatting function which builds a string out of this kunit_assert.
> > *
> > * The base initializer for a &struct kunit_assert.
> > */
> > -#define KUNIT_INIT_ASSERT_STRUCT(assert_type, fmt) { \
> > - .type = assert_type, \
> > +#define KUNIT_INIT_ASSERT_STRUCT(fmt) { \
> > .message = KUNIT_INIT_VA_FMT_NULL, \
> > .format = fmt \
> > }
> > @@ -98,15 +94,13 @@ void kunit_fail_assert_format(const struct kunit_assert *assert,
> > struct string_stream *stream);
> >
> > /**
> > - * KUNIT_INIT_FAIL_ASSERT_STRUCT() - Initializer for &struct kunit_fail_assert.
> > - * @type: The type (assertion or expectation) of this kunit_assert.
> > + * KUNIT_INIT_FAIL_ASSERT_STRUCT - Initializer for &struct kunit_fail_assert.
> > *
> > * Initializes a &struct kunit_fail_assert. Intended to be used in
> > * KUNIT_EXPECT_* and KUNIT_ASSERT_* macros.
> > */
> > -#define KUNIT_INIT_FAIL_ASSERT_STRUCT(type) { \
> > - .assert = KUNIT_INIT_ASSERT_STRUCT(type, \
> > - kunit_fail_assert_format) \
> > +#define KUNIT_INIT_FAIL_ASSERT_STRUCT { \
> > + .assert = KUNIT_INIT_ASSERT_STRUCT(kunit_fail_assert_format) \
> > }
> >
> > /**
> > @@ -130,16 +124,14 @@ void kunit_unary_assert_format(const struct kunit_assert *assert,
> >
> > /**
> > * KUNIT_INIT_UNARY_ASSERT_STRUCT() - Initializes &struct kunit_unary_assert.
> > - * @type: The type (assertion or expectation) of this kunit_assert.
> > * @cond: A string representation of the expression asserted true or false.
> > * @expect_true: True if of type KUNIT_{EXPECT|ASSERT}_TRUE, false otherwise.
> > *
> > * Initializes a &struct kunit_unary_assert. Intended to be used in
> > * KUNIT_EXPECT_* and KUNIT_ASSERT_* macros.
> > */
> > -#define KUNIT_INIT_UNARY_ASSERT_STRUCT(type, cond, expect_true) { \
> > - .assert = KUNIT_INIT_ASSERT_STRUCT(type, \
> > - kunit_unary_assert_format), \
> > +#define KUNIT_INIT_UNARY_ASSERT_STRUCT(cond, expect_true) { \
> > + .assert = KUNIT_INIT_ASSERT_STRUCT(kunit_unary_assert_format), \
> > .condition = cond, \
> > .expected_true = expect_true \
> > }
> > @@ -166,16 +158,14 @@ void kunit_ptr_not_err_assert_format(const struct kunit_assert *assert,
> > /**
> > * KUNIT_INIT_PTR_NOT_ERR_ASSERT_STRUCT() - Initializes a
> > * &struct kunit_ptr_not_err_assert.
> > - * @type: The type (assertion or expectation) of this kunit_assert.
> > * @txt: A string representation of the expression passed to the expectation.
> > * @val: The actual evaluated pointer value of the expression.
> > *
> > * Initializes a &struct kunit_ptr_not_err_assert. Intended to be used in
> > * KUNIT_EXPECT_* and KUNIT_ASSERT_* macros.
> > */
> > -#define KUNIT_INIT_PTR_NOT_ERR_STRUCT(type, txt, val) { \
> > - .assert = KUNIT_INIT_ASSERT_STRUCT(type, \
> > - kunit_ptr_not_err_assert_format), \
> > +#define KUNIT_INIT_PTR_NOT_ERR_STRUCT(txt, val) { \
> > + .assert = KUNIT_INIT_ASSERT_STRUCT(kunit_ptr_not_err_assert_format), \
> > .text = txt, \
> > .value = val \
> > }
> > @@ -209,7 +199,6 @@ void kunit_binary_assert_format(const struct kunit_assert *assert,
> > /**
> > * KUNIT_INIT_BINARY_ASSERT_STRUCT() - Initializes a
> > * &struct kunit_binary_assert.
> > - * @type: The type (assertion or expectation) of this kunit_assert.
> > * @op_str: A string representation of the comparison operator (e.g. "==").
> > * @left_str: A string representation of the expression in the left slot.
> > * @left_val: The actual evaluated value of the expression in the left slot.
> > @@ -219,14 +208,12 @@ void kunit_binary_assert_format(const struct kunit_assert *assert,
> > * Initializes a &struct kunit_binary_assert. Intended to be used in
> > * KUNIT_EXPECT_* and KUNIT_ASSERT_* macros.
> > */
> > -#define KUNIT_INIT_BINARY_ASSERT_STRUCT(type, \
> > - op_str, \
> > +#define KUNIT_INIT_BINARY_ASSERT_STRUCT(op_str, \
> > left_str, \
> > left_val, \
> > right_str, \
> > right_val) { \
> > - .assert = KUNIT_INIT_ASSERT_STRUCT(type, \
> > - kunit_binary_assert_format), \
> > + .assert = KUNIT_INIT_ASSERT_STRUCT(kunit_binary_assert_format), \
> > .operation = op_str, \
> > .left_text = left_str, \
> > .left_value = left_val, \
> > @@ -273,14 +260,12 @@ void kunit_binary_ptr_assert_format(const struct kunit_assert *assert,
> > * Initializes a &struct kunit_binary_ptr_assert. Intended to be used in
> > * KUNIT_EXPECT_* and KUNIT_ASSERT_* macros.
> > */
> > -#define KUNIT_INIT_BINARY_PTR_ASSERT_STRUCT(type, \
> > - op_str, \
> > +#define KUNIT_INIT_BINARY_PTR_ASSERT_STRUCT(op_str, \
> > left_str, \
> > left_val, \
> > right_str, \
> > right_val) { \
> > - .assert = KUNIT_INIT_ASSERT_STRUCT(type, \
> > - kunit_binary_ptr_assert_format), \
> > + .assert = KUNIT_INIT_ASSERT_STRUCT(kunit_binary_ptr_assert_format), \
> > .operation = op_str, \
> > .left_text = left_str, \
> > .left_value = left_val, \
> > @@ -317,7 +302,6 @@ void kunit_binary_str_assert_format(const struct kunit_assert *assert,
> > /**
> > * KUNIT_INIT_BINARY_STR_ASSERT_STRUCT() - Initializes a
> > * &struct kunit_binary_str_assert.
> > - * @type: The type (assertion or expectation) of this kunit_assert.
> > * @op_str: A string representation of the comparison operator (e.g. "==").
> > * @left_str: A string representation of the expression in the left slot.
> > * @left_val: The actual evaluated value of the expression in the left slot.
> > @@ -327,14 +311,12 @@ void kunit_binary_str_assert_format(const struct kunit_assert *assert,
> > * Initializes a &struct kunit_binary_str_assert. Intended to be used in
> > * KUNIT_EXPECT_* and KUNIT_ASSERT_* macros.
> > */
> > -#define KUNIT_INIT_BINARY_STR_ASSERT_STRUCT(type, \
> > - op_str, \
> > +#define KUNIT_INIT_BINARY_STR_ASSERT_STRUCT(op_str, \
> > left_str, \
> > left_val, \
> > right_str, \
> > right_val) { \
> > - .assert = KUNIT_INIT_ASSERT_STRUCT(type, \
> > - kunit_binary_str_assert_format), \
> > + .assert = KUNIT_INIT_ASSERT_STRUCT(kunit_binary_str_assert_format), \
> > .operation = op_str, \
> > .left_text = left_str, \
> > .left_value = left_val, \
> > diff --git a/include/kunit/test.h b/include/kunit/test.h
> > index 6e201b45ada6..6f9074ec1995 100644
> > --- a/include/kunit/test.h
> > +++ b/include/kunit/test.h
> > @@ -795,7 +795,7 @@ void kunit_failed_assertion(struct kunit *test,
> > assert_type, \
> > false, \
> > kunit_fail_assert, \
> > - KUNIT_INIT_FAIL_ASSERT_STRUCT(assert_type), \
> > + KUNIT_INIT_FAIL_ASSERT_STRUCT, \
>
> The '\' at the end of this line is misaligned. (The others seem fine,
> but it's possible I missed one.)

Fixed, will include in v2.

>
>
>
>
> > fmt, \
> > ##__VA_ARGS__)
> >
> > @@ -826,8 +826,7 @@ void kunit_failed_assertion(struct kunit *test,
> > assert_type, \
> > !!(condition) == !!expected_true, \
> > kunit_unary_assert, \
> > - KUNIT_INIT_UNARY_ASSERT_STRUCT(assert_type, \
> > - #condition, \
> > + KUNIT_INIT_UNARY_ASSERT_STRUCT(#condition, \
> > expected_true), \
> > fmt, \
> > ##__VA_ARGS__)
> > @@ -885,8 +884,7 @@ do { \
> > assert_type, \
> > __left op __right, \
> > assert_class, \
> > - ASSERT_CLASS_INIT(assert_type, \
> > - #op, \
> > + ASSERT_CLASS_INIT(#op, \
> > #left, \
> > __left, \
> > #right, \
> > @@ -1240,8 +1238,7 @@ do { \
> > assert_type, \
> > strcmp(__left, __right) op 0, \
> > kunit_binary_str_assert, \
> > - KUNIT_INIT_BINARY_STR_ASSERT_STRUCT(assert_type, \
> > - #op, \
> > + KUNIT_INIT_BINARY_STR_ASSERT_STRUCT(#op, \
> > #left, \
> > __left, \
> > #right, \
> > @@ -1300,8 +1297,7 @@ do { \
> > assert_type, \
> > !IS_ERR_OR_NULL(__ptr), \
> > kunit_ptr_not_err_assert, \
> > - KUNIT_INIT_PTR_NOT_ERR_STRUCT(assert_type, \
> > - #ptr, \
> > + KUNIT_INIT_PTR_NOT_ERR_STRUCT(#ptr, \
> > __ptr), \
> > fmt, \
> > ##__VA_ARGS__); \
> > --
> > 2.34.1.575.g55b058a8bb-goog
> >