Re: [PATCH v2 2/6] unit-tests: add for_test

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



On 22/07/2024 20:13, Kyle Lippincott wrote:
On Sat, Jul 20, 2024 at 11:22 PM René Scharfe <l.s.r@xxxxxx> wrote:

The macro TEST only allows defining a test that consists of a single
expression.  Add a new macro, for_test, which provides a way to define
unit tests that are made up of one or more statements.

for_test allows defining self-contained tests en bloc, a bit like
test_expect_success does for regular tests.  It acts like a for loop
that runs at most once; the test body is executed if test_skip_all()
had not been called before.

I can see based on this description where the name came from, but
without this context, it's not clear when reading a test what it
actually does. The name comes from an implementation detail, and is
not describing what it _does_, just _how_ it does it.

That's my feeling too. In fact I think the name "for_test" actually works against us by implicitly suggesting that "break" can be used to exit the test early when in fact that will not work because we'll skip the call to test__run_end()

+ * Run a test unless test_skip_all() has been called.  Acts like a for
+ * loop that runs at most once, with the test description between the
+ * parentheses and the test body as a statement or block after them.
+ * The description for each test should be unique.  E.g.:
+ *
+ *  for_test ("something else %d %d", arg1, arg2) {
+ *          prepare();
+ *          test_something_else(arg1, arg2);
+ *          cleanup();
+ *  }
+ */
+#define for_test(...)                                                  \
+       for (int for_test_running_ = test__run_begin() ?                \
+               (test__run_end(0, TEST_LOCATION(), __VA_ARGS__), 0) : 1;\
+            for_test_running_;                                         \
+            test__run_end(1, TEST_LOCATION(), __VA_ARGS__),            \
+               for_test_running_ = 0)

IMHO: this is borderline "too much magic" for my tastes.

Yes, compared to TEST_RUN() in v1 the magic level has jumped from Muggle to Dumbledore. Using a for loop is clever as it ensures test__run_end() is called at the end of the test without the need for separate TEST_END()[1] macro. The alternative is something like test_if_enabled()[2] which was discussed in the last round.

[1] https://lore.kernel.org/git/4f41f509-1d44-4476-92b0-9bb643f64576@xxxxxxxxx
[2] https://lore.kernel.org/git/xmqqa5iot01s.fsf@gitster.g

I think
having multiple test functions is generally easier to understand, and
the overhead isn't really relevant. It's not _as_ compact in the
source file, and requires that we have both the TEST statement and the
function (and forgetting the TEST statement means that we won't invoke
the function). If that is the main issue we're facing here, I wonder
if there's some other way of resolving that (such as unused function
detection via some compiler flags; even if it's not detected on all
platforms, getting at least one of the CI platforms should be
sufficient to prevent the issue [but we should target as many as
possible, so it's caught earlier than "I tried to send it to the
list"])

I also have a preference for function based tests but I do think something like for_test() can be useful in certain situations. For example in test-ctype.c where testing the ctype macros leads to a lot of repetition or a giant macro with a function based approach. If isalpha() and friends were functions instead we could write a single helper function which is passed the function under test together with the input data and expect result. Because they are macros that approach does not work.

Another example is where we are using a helper function with several inputs and we would prefer to write

for_test("test 1") {
	int input[] = ...
	int expect[] = ...

	test_helper(input, expect);

...

for_test("test 10") {
	int input[] = ...
	int expect[] = ...

	test_helper(input, expect);
}

rather then declaring all the input and expect variables up front with

	int input_1 = ...
	int input_2 = ...
	...
	int expect_1 = ...
	int expect_2 = ...

	TEST(test_helper(input_1, expect_1), "test 1");
	...
	TEST(test_helper(input_10, expect_10), "test 10");

If others agree that this is a good simplification for the people
reading the test code (and hopefully for the test author), I'm fine
with this going in (with a different name). I'm not trying to veto the
concept.

That's my feeling too.

Best Wishes

Phillip


+
  /*
   * Print a test plan, should be called before any tests. If the number
   * of tests is not known in advance test_done() will automatically
--
2.45.2







[Index of Archives]     [Linux Kernel Development]     [Gcc Help]     [IETF Annouce]     [DCCP]     [Netdev]     [Networking]     [Security]     [V4L]     [Bugtraq]     [Yosemite]     [MIPS Linux]     [ARM Linux]     [Linux Security]     [Linux RAID]     [Linux SCSI]     [Fedora Users]

  Powered by Linux