Thread: expressive test macros (was: Report test_atomic_ops() failuresconsistently, via macros)
expressive test macros (was: Report test_atomic_ops() failuresconsistently, via macros)
From
Andres Freund
Date:
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. https://git.postgresql.org/gitweb/?p=postgresql.git;a=commitdiff;h=639feb92e1186e1de495d1bfdb191869cb56450a ... +#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
Re: expressive test macros (was: Report test_atomic_ops() failuresconsistently, via macros)
From
Noah Misch
Date:
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.
Re: expressive test macros (was: Report test_atomic_ops() failures consistently, via macros)
From
Tom Lane
Date:
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
Re: expressive test macros (was: Report test_atomic_ops() failuresconsistently, via macros)
From
Peter Eisentraut
Date:
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
Re: expressive test macros (was: Report test_atomic_ops() failuresconsistently, via macros)
From
Peter Eisentraut
Date:
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
Re: expressive test macros (was: Report test_atomic_ops() failuresconsistently, via macros)
From
Andres Freund
Date:
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
Re: expressive test macros (was: Report test_atomic_ops() failures consistently, via macros)
From
Tom Lane
Date:
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
Re: expressive test macros (was: Report test_atomic_ops() failuresconsistently, via macros)
From
Andres Freund
Date:
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
Re: expressive test macros (was: Report test_atomic_ops() failuresconsistently, via macros)
From
Noah Misch
Date:
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).
Re: expressive test macros (was: Report test_atomic_ops() failuresconsistently, via macros)
From
Peter Eisentraut
Date:
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