pgsql: Report test_atomic_ops() failures consistently, via macros.
Report test_atomic_ops() failures consistently, via macros.
This prints the unexpected value in more failure cases, and it removes
forty-eight hand-maintained error messages. Back-patch to 9.5, which
introduced these tests.
Reviewed (in an earlier version) by Andres Freund.
Discussion: /messages/by-id/20190915160021.GA24376@alvherre.pgsql
Branch
------
master
Details
-------
https://git.postgresql.org/pg/commitdiff/e800bd7414df3ce8170761e5b75b13e83f576988
Modified Files
--------------
src/test/regress/regress.c | 227 ++++++++++++++++-----------------------------
1 file changed, 81 insertions(+), 146 deletions(-)
Hi,
On 2019-10-05 17:08:38 +0000, Noah Misch wrote:
Report test_atomic_ops() failures consistently, via macros.
This prints the unexpected value in more failure cases, and it removes
forty-eight hand-maintained error messages. Back-patch to 9.5, which
introduced these tests.
Thanks for these, that's a nice improvement.
...
+#define EXPECT_EQ_U32(result_expr, expected_expr) \
+ do { \
+ uint32 result = (result_expr); \
+ uint32 expected = (expected_expr); \
+ if (result != expected) \
+ elog(ERROR, \
+ "%s yielded %u, expected %s in file \"%s\" line %u", \
+ #result_expr, result, #expected_expr, __FILE__, __LINE__); \
+ } while (0)
...
I wonder if we should put these (and a few more, for other types), into
a more general place. I would like to have them for writing both tests
like regress.c:test_atomic_ops(), and for writing assertions that
actually display useful error messages. For the former it makes sense
to ERROR out, for the latter they ought to abort, as currently.
Seems like putting ASSERT_{EQ,LT,...}_{U32,S32,...} (or Assert_Eq_...,
but that'd imo look weirder than the inconsistency) into c.h would make
sense, and EXPECT_ somewhere in common/pg_test.h or such?
Greetings,
Andres Freund
On Sat, Oct 05, 2019 at 12:07:29PM -0700, Andres Freund wrote:
+#define EXPECT_EQ_U32(result_expr, expected_expr) \ + do { \ + uint32 result = (result_expr); \ + uint32 expected = (expected_expr); \ + if (result != expected) \ + elog(ERROR, \ + "%s yielded %u, expected %s in file \"%s\" line %u", \ + #result_expr, result, #expected_expr, __FILE__, __LINE__); \ + } while (0) ...I wonder if we should put these (and a few more, for other types), into
a more general place. I would like to have them for writing both tests
like regress.c:test_atomic_ops(), and for writing assertions that
actually display useful error messages. For the former it makes sense
to ERROR out, for the latter they ought to abort, as currently.Seems like putting ASSERT_{EQ,LT,...}_{U32,S32,...} (or Assert_Eq_...,
but that'd imo look weirder than the inconsistency) into c.h would make
sense, and EXPECT_ somewhere in common/pg_test.h or such?
Sounds reasonable. For broader use, I would include the expected value, not
just expected_expr:
elog(ERROR, \
"%s yielded %u, expected %s (%u) in file \"%s\" line %u", \
#result_expr, result, #expected_expr, expected, __FILE__, __LINE__); \
I didn't do that for the atomics tests, where expected_expr is always trivial.
The codebase has plenty of Assert(x == y) where either of x or y could have
the surprising value.
Andres Freund <andres@anarazel.de> writes:
On 2019-10-05 17:08:38 +0000, Noah Misch wrote:
Report test_atomic_ops() failures consistently, via macros.
I wonder if we should put these (and a few more, for other types), into
a more general place. I would like to have them for writing both tests
like regress.c:test_atomic_ops(), and for writing assertions that
actually display useful error messages. For the former it makes sense
to ERROR out, for the latter they ought to abort, as currently.
IMO, anything named like "assert" ought to act like Assert does now,
ie (1) it's a no-op in a non-assert build and (2) you get an abort()
on failure. No strong opinions about what the test-and-elog variant
should be called -- but it seems like we might have some difficulty
agreeing on what the appropriate error level is for that. If it's
morally like an Assert except we want it on all the time, should
it be PANIC? What will happen in frontend code?
Seems like putting ASSERT_{EQ,LT,...}_{U32,S32,...} (or Assert_Eq_...,
but that'd imo look weirder than the inconsistency) into c.h would make
sense, and EXPECT_ somewhere in common/pg_test.h or such?
I'd just put them all in c.h. I see no reason why a new header
is helpful.
regards, tom lane
On 2019-10-06 04:20, Noah Misch wrote:
Seems like putting ASSERT_{EQ,LT,...}_{U32,S32,...} (or Assert_Eq_...,
but that'd imo look weirder than the inconsistency) into c.h would make
sense, and EXPECT_ somewhere in common/pg_test.h or such?Sounds reasonable. For broader use, I would include the expected value, not
just expected_expr:elog(ERROR, \
"%s yielded %u, expected %s (%u) in file \"%s\" line %u", \
#result_expr, result, #expected_expr, expected, __FILE__, __LINE__); \I didn't do that for the atomics tests, where expected_expr is always trivial.
The codebase has plenty of Assert(x == y) where either of x or y could have
the surprising value.
I've been meaning to propose some JUnit-style more-specific Assert
variants such as AssertEquals for this reason. But as Tom writes
nearby, it should be a straight wrapper around Assert, not elog. So
these need to be named separately.
Btw., JUnit uses the ordering convention assertEquals(expected, actual),
whereas Perl Test::More uses is(actual, expected). Let's make sure we
pick something and stick with it.
--
Peter Eisentraut http://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services
On 2019-10-07 19:57, Tom Lane wrote:
I'd just put them all in c.h. I see no reason why a new header
is helpful.
Assert stuff is already in there, but surely stuff that calls elog()
doesn't belong in there?
--
Peter Eisentraut http://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services
Hi,
On 2019-10-07 13:57:41 -0400, Tom Lane wrote:
Andres Freund <andres@anarazel.de> writes:
On 2019-10-05 17:08:38 +0000, Noah Misch wrote:
Report test_atomic_ops() failures consistently, via macros.
I wonder if we should put these (and a few more, for other types), into
a more general place. I would like to have them for writing both tests
like regress.c:test_atomic_ops(), and for writing assertions that
actually display useful error messages. For the former it makes sense
to ERROR out, for the latter they ought to abort, as currently.IMO, anything named like "assert" ought to act like Assert does now,
ie (1) it's a no-op in a non-assert build and (2) you get an abort()
on failure.
No disagreement at all.
No strong opinions about what the test-and-elog variant
should be called -- but it seems like we might have some difficulty
agreeing on what the appropriate error level is for that. If it's
morally like an Assert except we want it on all the time, should
it be PANIC?
Perhaps it ought to just take elevel as a parameter? Could even be
useful for debugging...
What will happen in frontend code?
Hm. Map to pg_log_*, and abort() if it's an erroring elevel?
Seems like putting ASSERT_{EQ,LT,...}_{U32,S32,...} (or Assert_Eq_...,
but that'd imo look weirder than the inconsistency) into c.h would make
sense, and EXPECT_ somewhere in common/pg_test.h or such?I'd just put them all in c.h. I see no reason why a new header
is helpful.
WFM.
Greetings,
Andres Freund
Peter Eisentraut <peter.eisentraut@2ndquadrant.com> writes:
On 2019-10-07 19:57, Tom Lane wrote:
I'd just put them all in c.h. I see no reason why a new header
is helpful.
Assert stuff is already in there, but surely stuff that calls elog()
doesn't belong in there?
True, though I had the impression that Andres wanted to propose things
that would work in either frontend or backend, presumably with different
implementations. You could argue it either way as to whether to have
that in c.h (with an #ifdef) or separately in postgres.h and
postgres_fe.h.
regards, tom lane
Hi,
On 2019-10-07 21:58:08 +0200, Peter Eisentraut wrote:
On 2019-10-07 19:57, Tom Lane wrote:
I'd just put them all in c.h. I see no reason why a new header
is helpful.Assert stuff is already in there, but surely stuff that calls elog()
doesn't belong in there?
Make it call an ExpectFailure() (or similar) function that takes the
various parameters (expression, file, line, severity, format string,
args) as an argument? And that's implemented in terms of elog() in the
backend, and pg_log* + abort() (when appropriate) in the frontend?
Greetings,
Andres Freund
On Mon, Oct 07, 2019 at 09:56:20PM +0200, Peter Eisentraut wrote:
On 2019-10-06 04:20, Noah Misch wrote:
elog(ERROR, \
"%s yielded %u, expected %s (%u) in file \"%s\" line %u", \
#result_expr, result, #expected_expr, expected, __FILE__, __LINE__); \
I've been meaning to propose some JUnit-style more-specific Assert
variants such as AssertEquals for this reason. But as Tom writes
nearby, it should be a straight wrapper around Assert, not elog. So
these need to be named separately.
Agreed.
Btw., JUnit uses the ordering convention assertEquals(expected, actual),
whereas Perl Test::More uses is(actual, expected). Let's make sure we
pick something and stick with it.
Since we write "if (actual == expected)", I prefer f(actual, expected). CUnit
uses CU_ASSERT_EQUAL(actual, expected).
On 2019-10-08 06:59, Noah Misch wrote:
Btw., JUnit uses the ordering convention assertEquals(expected, actual),
whereas Perl Test::More uses is(actual, expected). Let's make sure we
pick something and stick with it.Since we write "if (actual == expected)", I prefer f(actual, expected). CUnit
uses CU_ASSERT_EQUAL(actual, expected).
Yes, that seems to be the dominating order outside of JUnit.
--
Peter Eisentraut http://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services