[Boost.Test] Assertion changes in trunk? [WAS: Call for Review: Boost.Test documentation rewrite]
On Wed, Jan 22, 2014 at 11:25 AM, Alexander Lamaison
[...] As for as I'm aware, the only change is to the assertion macros, which move to a more natural syntax:
BOOST_CHECK_EQUAL(a, b) becomes BOOST_TEST(a == b) BOOST_CHECK_LT(a, b) becomes BOOST_TEST(a < b) ... etc. BOOST_REQUIRE_EQUAL(a, b) becomes BOOST_TEST_REQUIRE(a == b) ... etc. BOOST_WARN_EQUAL(a, b) becomes BOOST_TEST_WARN(a == b)
But don't we then miss better messages which actually show the values themselves when the assert fails? I do BOOST_CHECK(a == b) only when a and b lack the op<<, but otherwise greatly prefer BOOST_CHECK_EQUAL(a, b). And what's the point of going from BOOST_REQUIRE(expr) to BOOST_TEST_REQUIRE(expr), i.e. make it longer / more typing? What am I missing? --DD
Dominique Devienne
On Wed, Jan 22, 2014 at 11:25 AM, Alexander Lamaison
wrote: [...] As for as I'm aware, the only change is to the assertion macros, which move to a more natural syntax:
BOOST_CHECK_EQUAL(a, b) becomes BOOST_TEST(a == b) BOOST_CHECK_LT(a, b) becomes BOOST_TEST(a < b) ... etc. BOOST_REQUIRE_EQUAL(a, b) becomes BOOST_TEST_REQUIRE(a == b) ... etc. BOOST_WARN_EQUAL(a, b) becomes BOOST_TEST_WARN(a == b)
But don't we then miss better messages which actually show the values themselves when the assert fails? I do BOOST_CHECK(a == b) only when a and b lack the op<<, but otherwise greatly prefer BOOST_CHECK_EQUAL(a, b).
Actually, no. BOOST_CHECK(a <operator> b) expands to something like: bool x = (a <operator> b) BOOST_CHECK_IMPL(x, "a <operator> b"); int x = 1; int y = 2; BOOST_CHECK(x == y)
"Assertion failed (file:line): !(x == y)"
so you lose the information about a and b, and which operator was used to test their relationship. BOOST_TEST(a <operator> b), however, cleverly parses a, b and <operator> from the macro and does something like bool x = (a <operator> b) BOOST_CHECK_MESSAGE_IMPL(x, "a must be <operator> to b", a, b); int x = 1; int y = 2; BOOST_TEST(x == y)
"Assertion failed (file:line): "x must be == to y: 1 != 2"
Obviously, the above is pseudocode nonsense, but you get the idea.
And what's the point of going from BOOST_REQUIRE(expr) to BOOST_TEST_REQUIRE(expr), i.e. make it longer / more typing?
The macros had to change anyway because the behaviour was changing. The Boost preferred-practice is to prefix new macros with BOOST_ and the name of the library, so all new Boost.Test macros should start BOOST_TEST*. As 'CHECK' is the assertion level you should use by default, BOOST_TEST with no suffix gets the 'CHECK' behaviour. The 'REQUIRE' and 'WARN' behaviours are chosen by adding the suffix, as before. Alex -- Swish - Easy SFTP for Windows Explorer (http://www.swish-sftp.org)
On 22/01/14 11:52, Alexander Lamaison wrote:
BOOST_TEST(a <operator> b), however, cleverly parses a, b and <operator> from the macro and does something like
bool x = (a <operator> b) BOOST_CHECK_MESSAGE_IMPL(x, "a must be <operator> to b", a, b);
What sort of magic allows to parse a <operator> b, and how reliable is it in the real world?
On 22 January 2014 12:11, Mathias Gaunard
On 22/01/14 11:52, Alexander Lamaison wrote:
BOOST_TEST(a <operator> b), however, cleverly parses a, b and <operator> from the macro and does something like
bool x = (a <operator> b) BOOST_CHECK_MESSAGE_IMPL(x, "a must be <operator> to b", a, b);
What sort of magic allows to parse a <operator> b, and how reliable is it in the real world?
BTW, I haven't looked into Boost.Test code, but this reminds me expression capture and decomposition technique used in Catch library https://github.com/philsquared/Catch/blob/master/include/internal/catch_capt... (not sure if this is the only file involved) AFAICT, it works really nice in Catch. Best regards, -- Mateusz Łoskot, http://mateusz.loskot.net
On 22/01/2014 13:14, Mateusz Łoskot wrote:
BTW, I haven't looked into Boost.Test code, but this reminds me expression capture and decomposition technique used in Catch library https://github.com/philsquared/Catch/blob/master/include/internal/catch_capt... (not sure if this is the only file involved)
AFAICT, it works really nice in Catch.
Yes this upcoming feature in Boost.Test has been inspired by Catch and works the same way as far as I know. Regards, MAT.
Mathieu Champlon
On 22/01/2014 13:14, Mateusz Łoskot wrote:
BTW, I haven't looked into Boost.Test code, but this reminds me expression capture and decomposition technique used in Catch library
https://github.com/philsquared/Catch/blob/master/include/internal/catch_capt... re.hpp
(not sure if this is the only file involved)
AFAICT, it works really nice in Catch.
Yes this upcoming feature in Boost.Test has been inspired by Catch and works the same way as far as I know.
It was not inspired by Catch. It was inspired by Kevlin's video. And Boost.Test goes well beyond what Kevlin proposed. Gennadiy
On 22-01-2014 13:11, Mathias Gaunard wrote:
On 22/01/14 11:52, Alexander Lamaison wrote:
BOOST_TEST(a <operator> b), however, cleverly parses a, b and <operator> from the macro and does something like
bool x = (a <operator> b) BOOST_CHECK_MESSAGE_IMPL(x, "a must be <operator> to b", a, b);
What sort of magic allows to parse a <operator> b, and how reliable is it in the real world?
Completely reliable. You just bind the first object to some wrapper class object which provide the overloaded operators (via delegation). -Thorsten
On Wed, Jan 22, 2014 at 1:21 PM, Thorsten Ottosen < thorsten.ottosen@dezide.com> wrote:
On 22-01-2014 13:11, Mathias Gaunard wrote:
On 22/01/14 11:52, Alexander Lamaison wrote:
BOOST_TEST(a <operator> b), however, cleverly parses a, b and <operator>
from the macro and does something like
bool x = (a <operator> b) BOOST_CHECK_MESSAGE_IMPL(x, "a must be <operator> to b", a, b);
What sort of magic allows to parse a <operator> b, and how reliable is it in the real world?
Completely reliable. You just bind the first object to some wrapper class object which provide the overloaded operators (via delegation).
This seems to rely on an op->* operator, which has lower precedence than unary op* (dereference), and the macro INTERNAL_CATCH_TEST uses un-parenthesized Catch::ExpressionDecomposer()->*expr, so won't this break if I do MACRO(*lhs_ptr == *rhs_ptr)? I suppose MACRO((*lhs_ptr) == *rhs_ptr) might fix it, but the compile error for the obvious code might come as a surprise to many, no? I'm no C++ expert, so perhaps the above is nonsense, but I'd be interested in more details as to how this technique can be completely reliable, as stated by Thorsten. Thanks, --DD
On 22-01-2014 14:05, Dominique Devienne wrote:
On Wed, Jan 22, 2014 at 1:21 PM, Thorsten Ottosen < thorsten.ottosen@dezide.com> wrote:
Completely reliable. You just bind the first object to some wrapper class object which provide the overloaded operators (via delegation).
This seems to rely on an op->* operator, which has lower precedence than unary op* (dereference), and the macro INTERNAL_CATCH_TEST uses un-parenthesized Catch::ExpressionDecomposer()->*expr, so won't this break if I do MACRO(*lhs_ptr == *rhs_ptr)?
Hm. I haven't tried that. Isn't it sufficient that ->* has higher precedence than the comparison operators? -Thorsten
On Thu, Jan 23, 2014 at 11:53 AM, Thorsten Ottosen < thorsten.ottosen@dezide.com> wrote:
On 22-01-2014 14:05, Dominique Devienne wrote:
On Wed, Jan 22, 2014 at 1:21 PM, Thorsten Ottosen < thorsten.ottosen@dezide.com> wrote:
Completely reliable. You just bind the first object to some wrapper
class object which provide the overloaded operators (via delegation).
This seems to rely on an op->* operator, which has lower precedence than unary op* (dereference), and the macro INTERNAL_CATCH_TEST uses un-parenthesized Catch::ExpressionDecomposer()->*expr, so won't this break if I do MACRO(*lhs_ptr == *rhs_ptr)?
Hm. I haven't tried that. Isn't it sufficient that ->* has higher precedence than the comparison operators?
The precedence I was concerned about was not op== Thorsten, but unary op*. Apologies, my example is probably not what I meant. Here's another try: MACRO(lhs_ptr == rhs_ptr) expands to OTHER_MACRO(Catch::ExpressionDecomposer()->*lhs_ptr == rhs_ptr, ...) And since unary op* has higher precedence than op->*, I think that's equivalent to OTHER_MACRO(Catch::ExpressionDecomposer()->(*lhs_ptr) == rhs_ptr, ...) I.e. no longer Catch::ExpressionDecomposer::op->* and lhs, but Catch::ExpressionDecomposer::op-> and (*lhs). Thus compile error? Not tried, obviously... Perhaps it doesn't matter if the only goal of ExpressionDecomposer is to know what operator is used by the expression, and it doesn't care about the type the template param of its op->* becomes, but in this case it looks to me that the 'star' will associate to lhs, not to ->. Again, I'm no C++ expert. I'm just concerned over such trickery "just" to have a single macro, in Catch and more importantly here to Boost.Test. --DD
The precedence I was concerned about was not op== Thorsten, but unary op*.
Apologies, my example is probably not what I meant. Here's another try:
MACRO(lhs_ptr == rhs_ptr) expands to
OTHER_MACRO(Catch::ExpressionDecomposer()->*lhs_ptr == rhs_ptr, ...)
And since unary op* has higher precedence than op->*, I think that's equivalent to
OTHER_MACRO(Catch::ExpressionDecomposer()->(*lhs_ptr) == rhs_ptr, ...)
I.e. no longer Catch::ExpressionDecomposer::op->* and lhs, but Catch::ExpressionDecomposer::op-> and (*lhs). Thus compile error? Not tried, obviously...
Perhaps it doesn't matter if the only goal of ExpressionDecomposer is to know what operator is used by the expression, and it doesn't care about the type the template param of its op->* becomes, but in this case it looks to me that the 'star' will associate to lhs, not to ->. Again, I'm no C++ expert. I'm just concerned over such trickery "just" to have a single macro, in Catch and more importantly here to Boost.Test. --DD
The `->*` operator is one complete operator. In C++, the operators are decided during lexing before the preprocessor, and they don't change(with the exception of the `>>` operator in C++11). Futhermore, precedence decides the order of operators, not what the operators will become. Paul -- View this message in context: http://boost.2283326.n4.nabble.com/Boost-Test-Assertion-changes-in-trunk-WAS... Sent from the Boost - Dev mailing list archive at Nabble.com.
On Thu, Jan 23, 2014 at 3:35 PM, pfultz2
The `->*` operator is one complete operator. In C++, the operators are decided during lexing before the preprocessor, and they don't change(with the exception of the `>>` operator in C++11). Futhermore, precedence decides the order of operators, not what the operators will become.
Thanks Paul. With your help, and http://en.cppreference.com/w/cpp/language/translation_phases, I now get it. Glad to learn such trickery is safe. Best regards, --DD
Thorsten Ottosen
On 22-01-2014 13:11, Mathias Gaunard wrote:
On 22/01/14 11:52, Alexander Lamaison wrote:
BOOST_TEST(a <operator> b), however, cleverly parses a, b and <operator> from the macro and does something like
bool x = (a <operator> b) BOOST_CHECK_MESSAGE_IMPL(x, "a must be <operator> to b", a, b);
What sort of magic allows to parse a <operator> b, and how reliable is it in the real world?
Completely reliable. You just bind the first object to some wrapper class object which provide the overloaded operators (via delegation).
There are few rare examples where this tool won't work (Ihave few examples in my unit test). THe easiest work around is: BOOST_TEST(( expr )). Gennadiy
[Please do not mail me a copy of your followup]
Alexander Lamaison
As 'CHECK' is the assertion level you should use by default, [...]
I don't know why you would say that when it is contrary to accepted unit testing practices and the behavior of assertions in other testing frameworks in every other language otu there. For isntance, jUnit doesn't continue executing the test past the first failed assertion. Neither do unit testing frameworks in JavaScript or Python or PHP, etc. REQUIRE is the level you want by default, not CHECK. -- "The Direct3D Graphics Pipeline" free book http://tinyurl.com/d3d-pipeline The Computer Graphics Museum http://computergraphicsmuseum.org The Terminals Wiki http://terminals.classiccmp.org Legalize Adulthood! (my blog) http://legalizeadulthood.wordpress.com
legalize+jeeves@mail.xmission.com (Richard) writes:
[Please do not mail me a copy of your followup]
Alexander Lamaison
spake the secret code <868uu8nv96.fsf@doc.ic.ac.uk> thusly: As 'CHECK' is the assertion level you should use by default, [...]
I don't know why you would say that when it is contrary to accepted unit testing practices and the behavior of assertions in other testing frameworks in every other language otu there.
Tests cases should test one, and only one, logical concept each. Therefore, multiple assertions in the same tests case will all be testing the same concept. In other words, they are, together, constructing a report of the failure. Often this is possible using only one assertion, in which case CHECK and REQUIRE are equivalent, but sometimes more than one non-mutually-exlusive assertion is needed to give the full picture.
For instance, jUnit doesn't continue executing the test past the first failed assertion. Neither do unit testing frameworks in JavaScript or Python or PHP, etc.
Their loss ;) Seriously though, I find it helps to have multiple bits of data included a single test cases's failure report. It maximises my chances of getting the case to pass next time, which is important in C++ where there is compilation step in between to slow down the test-edit-test loop. Alex -- Swish - Easy SFTP for Windows Explorer (http://www.swish-sftp.org)
On 24/01/2014 23:58, Quoth Alexander Lamaison:
Tests cases should test one, and only one, logical concept each. Therefore, multiple assertions in the same tests case will all be testing the same concept. In other words, they are, together, constructing a report of the failure.
Often this is possible using only one assertion, in which case CHECK and REQUIRE are equivalent, but sometimes more than one non-mutually-exlusive assertion is needed to give the full picture.
It is most useful to have both. I generally prefer to write most test assertions as non-exclusive, but there are certain assertions that are best being exclusive -- most commonly because if that assertion is false, then future assertions and/or actions would result in crashes/exceptions that aren't interesting. (eg. you should REQUIRE that an output collection has the correct size [either == or >=] before trying to CHECK that individual elements have the expected values, because if the size is wrong then the value checks will just throw exceptions or cause UB; but if the size is right then it's useful to display all the expected vs. actual values instead of stopping at the first mismatch.) Although that particular case is even better if either the framework or custom code lets you do an == on the collection as a whole, so that you can display expected vs. actual contents in the case of a size mismatch as well.
On Jan 24, 2014, at 5:58 AM, Alexander Lamaison
legalize+jeeves@mail.xmission.com (Richard) writes:
Alexander Lamaison
spake the secret code <868uu8nv96.fsf@doc.ic.ac.uk> thusly: As 'CHECK' is the assertion level you should use by default, [...]
I don't know why you would say that when it is contrary to accepted unit testing practices and the behavior of assertions in other testing frameworks in every other language otu there.
Tests cases should test one, and only one, logical concept each. Therefore, multiple assertions in the same tests case will all be testing the same concept. In other words, they are, together, constructing a report of the failure.
Often this is possible using only one assertion, in which case CHECK and REQUIRE are equivalent, but sometimes more than one non-mutually-exlusive assertion is needed to give the full picture.
For instance, jUnit doesn't continue executing the test past the first failed assertion. Neither do unit testing frameworks in JavaScript or Python or PHP, etc.
Their loss ;)
Seriously though, I find it helps to have multiple bits of data included a single test cases's failure report. It maximises my chances of getting the case to pass next time, which is important in C++ where there is compilation step in between to slow down the test-edit-test loop.
I agree with most of what Alex has said. I have both assert and verify macros in my unit test library. The former aborts the test, via an exception, and the latter sets the failure flag but permits execution to continue. Otherwise, they work exactly alike in terms of reporting output. However, I wouldn't go so far as to say which should be the default macro to use. ___ Rob (Sent from my portable computation engine)
[Please do not mail me a copy of your followup] boost@lists.boost.org spake the secret code <867g9pr6id.fsf@doc.ic.ac.uk> thusly:
Tests cases should test one, and only one, logical concept each. Therefore, multiple assertions in the same tests case will all be testing the same concept. In other words, they are, together, constructing a report of the failure.
Often this is possible using only one assertion, in which case CHECK and REQUIRE are equivalent, but sometimes more than one non-mutually-exlusive assertion is needed to give the full picture.
OK, I can see this point of view. I suppose if you wrote your own custom assertion and it returned boost::test_tools::predicate_result to build up a detailed expectation failure message this would also be the case. The reason I objected is because too often I see people getting the wrong "take away message" and violating the assumption you stated in your first paragraph while accepting the pattern in your second paragraph. The end result is brittle tests the fail too often and give unit testing in general a bad reputation. -- "The Direct3D Graphics Pipeline" free book http://tinyurl.com/d3d-pipeline The Computer Graphics Museum http://computergraphicsmuseum.org The Terminals Wiki http://terminals.classiccmp.org Legalize Adulthood! (my blog) http://legalizeadulthood.wordpress.com
participants (11)
-
Alexander Lamaison
-
Dominique Devienne
-
Gavin Lambert
-
Gennadiy Rozental
-
legalize+jeeves@mail.xmission.com
-
Mateusz Łoskot
-
Mathias Gaunard
-
Mathieu Champlon
-
pfultz2
-
Rob Stewart
-
Thorsten Ottosen