Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Wire up support for selftests #102

Merged
merged 10 commits into from
Oct 21, 2024
Merged

Wire up support for selftests #102

merged 10 commits into from
Oct 21, 2024

Conversation

pks-t
Copy link
Member

@pks-t pks-t commented Sep 6, 2024

Depends on CMake support added via #100.

@pks-t pks-t force-pushed the pks-selftests branch 30 times, most recently from eb1eb99 to ad46565 Compare September 7, 2024 05:51
@pks-t pks-t force-pushed the pks-selftests branch 2 times, most recently from 26ec063 to a041ee9 Compare September 7, 2024 07:30
@pks-t pks-t changed the title WIP: Wire up support for selftests Wire up support for selftests Sep 19, 2024
@pks-t pks-t force-pushed the pks-selftests branch 3 times, most recently from 2dd6ec9 to e088b4d Compare September 20, 2024 04:20
@pks-t
Copy link
Member Author

pks-t commented Sep 20, 2024

@ethomson This should be ready to review, now that #100 has landed.

Copy link
Member

@ethomson ethomson left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Generally I think this makes sense - I just had a few notes. The "how do we deal with the pointer test" I think is an interesting question. We could just accept the UB and move on, assuming that nobody's going to build clar on an AS/400. But I wonder if there's something that do that's more useful (or if I misunderstood the invalid pointer rule).

clar.c Outdated
@@ -211,7 +211,7 @@ static void clar_abort(const char *msg, ...)
va_start(argp, msg);
clar_print_onabortv(msg, argp);
va_end(argp);
exit(-1);
exit(1);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think that probably we should define exit codes then be consistent. I don't think that this is something that we need to address in this PR, just something for us to consider.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Most of this has been superseded with the introduction of clar_abort() anyway, which does provide some consistency here. So there's only two cases left, and here we could think about specific error codes in a future MR indeed.

@@ -25,4 +25,9 @@ if(CMAKE_PROJECT_NAME STREQUAL PROJECT_NAME)
if(BUILD_TESTING)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is if(BUILD_TESTING) but this is option(BUILD_TEST ... above.

I noticed because we introduce the option(BUILD_EXAMPLE ... below. I think we should have the options specified in the same place, and that we probably want BUILD_TEST, not BUILD_TESTING. (I would actually prefer BUILD_TESTS and BUILD_EXAMPLES, as plural, but I wouldn't block over that.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@ethomson BUILD_TESTING is actually set up via include(CTest), so it is what CMake itself declares to be the official option. So personally I'd agree that BUILD_TEST is saner, but CMake disagrees with both of us :/

But yeah, you're right, we still have the old BUILD_TESTS in here, which has in fact been introduced via f255c0b (ci: convert to use CMake, 2024-09-05). Let me drop that.

const char *actual = "expected";
cl_assert_equal_p(actual, actual); /* pointers to same object */
cl_assert_equal_p(&actual, actual);
void *p1 = (void *)0x1, *p2 = (void *)0x2;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think that technically this is UB. http://www.cs.man.ac.uk/~pjj/cs211/c_rationale/node12.html

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I guess you refer to the following quote, right?

Since pointers and integers are now considered incommensurate, the only integer that can be safely converted to a pointer is the constant 0. The result of converting any other integer to a pointer is machine dependent.

And the following makes clear that even the comparison of invalid pointers is UB:

Regardless how an invalid pointer is created, any use of it yields undefined behavior. Even assignment, comparison with a null pointer constant, or comparison with itself, might on some systems result in an exception.

One alternative would be to use intptr_t instead, but that's an optional extension to C99, only. So I'm inclined to just leave this as-is, as it works just fine in practice on most systems, unless you have a better idea.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yep, I think that's the pragmatic approach. It is after all, the tests for the tests, so a little questionably UB behavior probably won't hurt too bad.

test/main.c Outdated
#endif
{
if (argc < 2) {
fprintf(stderr, "USAGE: %s <SELFTEST_SUITE_EXECUTABLE> <OPTIONS>\n",
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we make this not shout?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sure :)

We explicitly declare a `BUILD_TESTS` option. This option is unused as
we use `BUILD_TESTING` instead, which is what the CTest module brings
with it.

Drop the `BUILD_TESTS` option.
When raising an error we print the current file, line and function where
the error was raised. This is of course quite helpful to users, but it
stands in the way of testing the clar because they make the output of
executables dependent on the developer's setup.

Add the ability to override these with fixed constants via a new
`CLAR_SELFTEST` macro.
We use `exit(-1)` in two places. Exit codes should be in the range of
[0, 255] though, so the resulting behaviour is not defined and depends
on the platform. Fix this by using `exit(1)` instead.
The "%p" formatter has platform-specific behaviour. On Windows for
example it prints as a zero-padded integer, whereas on glibc systems it
prints as non-zero-padded integer with "0x" prepended.

Address this by instead using PRIxPTR and casting the pointers to the
`uintptr_t` type.
The examples are closely coupled with our selftests, which makes it hard
to iterate on both. Split them out into their own standalone directory
as a prerequisite for wiring up proper testing of the clar.
We are about to wire up proper testing of clar via CI. This will be done
by executing the "clar_test" binary we already have in another clar test
binary. This requires us to move the current "clar_test" into its own
subdirectory such that "generate.py" can then generate the "clar.suite"
for each of these two test binaries independently.
The global test counter has been added as a demonstration for how it is
possible to modify global resources. Now that we have moved examples
into their own directory though it no longer serves that purpose.

In fact, it now stands in our way as we are about to introduce testing
of the clar itself with various different options. But as we assert that
the counter is "8" after all tests have ran, we wouldn't be able to
exclude some tests from running.

Drop the counter altogether to prepare for the change.
The output for the test that exercise `cl_assert_equal_p()` is
indeterministic because we compare on-stack addresses, which naturally
change between executions. Adapt the test to instead use fixed values.
While we have had the "clar_test" binary for a very long time already,
it was more for demonstration purposes than for real testing of the
clar. It could serve as sort of a smoke test if the developer remembered
to execute it, but that's about it.

In the preceding commits we have moved the "clar_test" into a separate
directory, renamed it to "selftest_suite" and made its output
deterministic. We can thus now wire up proper testing of the clar by
exercising the "selftest_suite" binary via a new "selftest" binary.
These tests run the "selftest_test" binary with various different
arguments and check both its return code as well as its output.
Wire up support for testing in our CI now that we have proper self
tests.
@pks-t
Copy link
Member Author

pks-t commented Oct 21, 2024

Rebased and addressed feedback.

@ethomson
Copy link
Member

Thanks!

@ethomson ethomson merged commit 15580f9 into clar-test:main Oct 21, 2024
5 checks passed
@pks-t pks-t deleted the pks-selftests branch October 21, 2024 08:57
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants