mirror of
https://gitlab.kitware.com/vtk/vtk-m
synced 2024-09-19 02:25:42 +00:00
Merge topic 'better-test-macros'
899b93ec2 Allow variable arguments to VTKM_TEST_ASSERT Acked-by: Kitware Robot <kwrobot@kitware.com> Acked-by: Robert Maynard <robert.maynard@kitware.com> Merge-request: !1429
This commit is contained in:
commit
6543942568
69
docs/changelog/variable-test-assert-arguments.md
Normal file
69
docs/changelog/variable-test-assert-arguments.md
Normal file
@ -0,0 +1,69 @@
|
||||
# Allow variable arguments to `VTKM_TEST_ASSERT`
|
||||
|
||||
The `VTKM_TEST_ASSERT` macro is a very useful tool for performing checks
|
||||
in tests. However, it is rather annoying to have to always specify a
|
||||
message for the assert. Often the failure is self evident from the
|
||||
condition (which is already printed out), and specifying a message is
|
||||
both repetative and annoying.
|
||||
|
||||
Also, it is often equally annoying to print out additional information
|
||||
in the case of an assertion failure. In that case, you have to either
|
||||
attach a debugger or add a printf, see the problem, and remove the
|
||||
printf.
|
||||
|
||||
This change solves both of these problems. `VTKM_TEST_ASSERT` now takes a
|
||||
condition and a variable number of message arguments. If no message
|
||||
arguments are given, then a default message (along with the condition)
|
||||
are output. If multiple message arguments are given, they are appended
|
||||
together in the result. The messages do not have to be strings. Any
|
||||
object that can be sent to a stream will be printed correctly. This
|
||||
allows you to print out the values that caused the issue.
|
||||
|
||||
So the old behavior of `VTKM_TEST_ASSERT` still works. So you can have a
|
||||
statement like
|
||||
|
||||
```cpp
|
||||
VTKM_TEST_ASSERT(array.GetNumberOfValues() != 0, "Array is empty");
|
||||
```
|
||||
|
||||
As before, if this assertion failed, you would get the following error
|
||||
message.
|
||||
|
||||
```
|
||||
Array is empty (array.GetNumberOfValues() != 0)
|
||||
```
|
||||
|
||||
However, in the statement above, you may feel that it is self evident that
|
||||
`array.GetNumberOfValues() == 0` means the array is empty and you have to
|
||||
type this into your test, like, 20 times. You can save yourself some work
|
||||
by dropping the message.
|
||||
|
||||
```cpp
|
||||
VTKM_TEST_ASSERT(array.GetNumberOfValues() != 0);
|
||||
```
|
||||
|
||||
In this case if the assertion fails, you will get a message like this.
|
||||
|
||||
```
|
||||
Test assertion failed (array.GetNumberOfValues() != 0)
|
||||
```
|
||||
|
||||
But perhaps you have the opposite problem. Perhaps you need to output more
|
||||
information. Let's say that you expected a particular operation to half the
|
||||
length of an array. If the operation fails, it could be helpful to know how
|
||||
big the array actually is. You can now actually output that on failure by
|
||||
adding more message arguments.
|
||||
|
||||
```cpp
|
||||
VTKM_TEST_ARRAY(outarray.GetNumberOfValues() == inarrayGetNumberOfValues()/2,
|
||||
"Expected array size ",
|
||||
inarrayGetNumberOfValues()/2,
|
||||
" but got ",
|
||||
outarray.GetNumberOfValues());
|
||||
```
|
||||
|
||||
In this case, if the test failed, you might get an error like this.
|
||||
|
||||
```
|
||||
Expected array size 5 but got 6 (outarray.GetNumberOfValues() == inarrayGetNumberOfValues()/2)
|
||||
```
|
@ -51,21 +51,33 @@
|
||||
#endif
|
||||
#endif
|
||||
|
||||
/// \def VTKM_TEST_ASSERT(condition, message)
|
||||
/// \def VTKM_STRINGIFY_FIRST(...)
|
||||
///
|
||||
/// A utility macro that takes 1 or more arguments and converts it into the C string version
|
||||
/// of the first argument.
|
||||
|
||||
#define VTKM_STRINGIFY_FIRST(...) VTKM_EXPAND(VTK_M_STRINGIFY_FIRST_IMPL(__VA_ARGS__, dummy))
|
||||
#define VTK_M_STRINGIFY_FIRST_IMPL(first, ...) #first
|
||||
#define VTKM_EXPAND(x) x
|
||||
|
||||
/// \def VTKM_TEST_ASSERT(condition, messages..)
|
||||
///
|
||||
/// Asserts a condition for a test to pass. A passing condition is when \a
|
||||
/// condition resolves to true. If \a condition is false, then the test is
|
||||
/// aborted and failure is returned.
|
||||
/// aborted and failure is returned. If one or more message arguments are
|
||||
/// given, they are printed out by concatinating them. If no messages are
|
||||
/// given, a generic message is given. In any case, the condition that failed
|
||||
/// is written out.
|
||||
|
||||
#define VTKM_TEST_ASSERT(condition, message) \
|
||||
::vtkm::testing::Testing::Assert(condition, __FILE__, __LINE__, message, #condition)
|
||||
#define VTKM_TEST_ASSERT(...) \
|
||||
::vtkm::testing::Testing::Assert( \
|
||||
VTKM_STRINGIFY_FIRST(__VA_ARGS__), __FILE__, __LINE__, __VA_ARGS__)
|
||||
|
||||
/// \def VTKM_TEST_FAIL(message)
|
||||
/// \def VTKM_TEST_FAIL(messages..)
|
||||
///
|
||||
/// Causes a test to fail with the given \a message.
|
||||
/// Causes a test to fail with the given \a messages. At least one argument must be given.
|
||||
|
||||
#define VTKM_TEST_FAIL(message) \
|
||||
throw ::vtkm::testing::Testing::TestFailure(__FILE__, __LINE__, message)
|
||||
#define VTKM_TEST_FAIL(...) ::vtkm::testing::Testing::TestFail(__FILE__, __LINE__, __VA_ARGS__)
|
||||
|
||||
namespace vtkm
|
||||
{
|
||||
@ -169,40 +181,67 @@ public:
|
||||
class TestFailure
|
||||
{
|
||||
public:
|
||||
VTKM_CONT TestFailure(const std::string& file, vtkm::Id line, const std::string& message)
|
||||
: File(file)
|
||||
, Line(line)
|
||||
, Message(message)
|
||||
{
|
||||
}
|
||||
|
||||
VTKM_CONT TestFailure(const std::string& file,
|
||||
vtkm::Id line,
|
||||
const std::string& message,
|
||||
const std::string& condition)
|
||||
template <typename... Ts>
|
||||
VTKM_CONT TestFailure(const std::string& file, vtkm::Id line, Ts&&... messages)
|
||||
: File(file)
|
||||
, Line(line)
|
||||
{
|
||||
this->Message.append(message);
|
||||
this->Message.append(" (");
|
||||
this->Message.append(condition);
|
||||
this->Message.append(")");
|
||||
std::stringstream messageStream;
|
||||
this->AppendMessages(messageStream, std::forward<Ts>(messages)...);
|
||||
this->Message = messageStream.str();
|
||||
}
|
||||
|
||||
VTKM_CONT const std::string& GetFile() const { return this->File; }
|
||||
VTKM_CONT vtkm::Id GetLine() const { return this->Line; }
|
||||
VTKM_CONT const std::string& GetMessage() const { return this->Message; }
|
||||
private:
|
||||
template <typename T1>
|
||||
VTKM_CONT void AppendMessages(std::stringstream& messageStream, T1&& m1)
|
||||
{
|
||||
messageStream << m1;
|
||||
}
|
||||
template <typename T1, typename T2>
|
||||
VTKM_CONT void AppendMessages(std::stringstream& messageStream, T1&& m1, T2&& m2)
|
||||
{
|
||||
messageStream << m1 << m2;
|
||||
}
|
||||
template <typename T1, typename T2, typename T3>
|
||||
VTKM_CONT void AppendMessages(std::stringstream& messageStream, T1&& m1, T2&& m2, T3&& m3)
|
||||
{
|
||||
messageStream << m1 << m2 << m3;
|
||||
}
|
||||
template <typename T1, typename T2, typename T3, typename T4>
|
||||
VTKM_CONT void AppendMessages(std::stringstream& messageStream,
|
||||
T1&& m1,
|
||||
T2&& m2,
|
||||
T3&& m3,
|
||||
T4&& m4)
|
||||
{
|
||||
messageStream << m1 << m2 << m3 << m4;
|
||||
}
|
||||
template <typename T1, typename T2, typename T3, typename T4, typename... Ts>
|
||||
VTKM_CONT void AppendMessages(std::stringstream& messageStream,
|
||||
T1&& m1,
|
||||
T2&& m2,
|
||||
T3&& m3,
|
||||
T4&& m4,
|
||||
Ts&&... ms)
|
||||
{
|
||||
messageStream << m1 << m2 << m3 << m4;
|
||||
this->AppendMessages(messageStream, std::forward<Ts>(ms)...);
|
||||
}
|
||||
|
||||
std::string File;
|
||||
vtkm::Id Line;
|
||||
std::string Message;
|
||||
};
|
||||
|
||||
static VTKM_CONT void Assert(bool condition,
|
||||
template <typename... Ts>
|
||||
static VTKM_CONT void Assert(const std::string& conditionString,
|
||||
const std::string& file,
|
||||
vtkm::Id line,
|
||||
const std::string& message,
|
||||
const std::string& conditionString)
|
||||
bool condition,
|
||||
Ts&&... messages)
|
||||
{
|
||||
if (condition)
|
||||
{
|
||||
@ -210,10 +249,24 @@ public:
|
||||
}
|
||||
else
|
||||
{
|
||||
throw TestFailure(file, line, message, conditionString);
|
||||
throw TestFailure(file, line, std::forward<Ts>(messages)..., " (", conditionString, ")");
|
||||
}
|
||||
}
|
||||
|
||||
static VTKM_CONT void Assert(const std::string& conditionString,
|
||||
const std::string& file,
|
||||
vtkm::Id line,
|
||||
bool condition)
|
||||
{
|
||||
Assert(conditionString, file, line, condition, "Test assertion failed");
|
||||
}
|
||||
|
||||
template <typename... Ts>
|
||||
static VTKM_CONT void TestFail(const std::string& file, vtkm::Id line, Ts&&... messages)
|
||||
{
|
||||
throw TestFailure(file, line, std::forward<Ts>(messages)...);
|
||||
}
|
||||
|
||||
#ifndef VTKM_TESTING_IN_CONT
|
||||
/// Calls the test function \a function with no arguments. Catches any errors
|
||||
/// generated by VTKM_TEST_ASSERT or VTKM_TEST_FAIL, reports the error, and
|
||||
|
@ -31,14 +31,33 @@ void Fail()
|
||||
VTKM_TEST_FAIL("I expect this error.");
|
||||
}
|
||||
|
||||
void Fail2()
|
||||
{
|
||||
vtkm::Id num = 5;
|
||||
VTKM_TEST_FAIL("I can provide a number: ", num);
|
||||
}
|
||||
|
||||
void BadAssert()
|
||||
{
|
||||
VTKM_TEST_ASSERT(0 == 1, "I expect this error.");
|
||||
}
|
||||
|
||||
void BadAssert2()
|
||||
{
|
||||
vtkm::Id num1 = 0;
|
||||
vtkm::Id num2 = 1;
|
||||
VTKM_TEST_ASSERT(num1 == num2, "num 1 is ", num1, "; num 2 is ", num2);
|
||||
}
|
||||
|
||||
void BadAssert3()
|
||||
{
|
||||
VTKM_TEST_ASSERT(0 == 1);
|
||||
}
|
||||
|
||||
void GoodAssert()
|
||||
{
|
||||
VTKM_TEST_ASSERT(1 == 1, "Always true.");
|
||||
VTKM_TEST_ASSERT(1 == 1);
|
||||
}
|
||||
|
||||
void TestTestEqual()
|
||||
@ -65,11 +84,29 @@ int UnitTestTesting(int, char* [])
|
||||
return 1;
|
||||
}
|
||||
std::cout << "This call should fail." << std::endl;
|
||||
if (vtkm::testing::Testing::Run(Fail2) == 0)
|
||||
{
|
||||
std::cout << "Did not get expected fail!" << std::endl;
|
||||
return 1;
|
||||
}
|
||||
std::cout << "This call should fail." << std::endl;
|
||||
if (vtkm::testing::Testing::Run(BadAssert) == 0)
|
||||
{
|
||||
std::cout << "Did not get expected fail!" << std::endl;
|
||||
return 1;
|
||||
}
|
||||
std::cout << "This call should fail." << std::endl;
|
||||
if (vtkm::testing::Testing::Run(BadAssert2) == 0)
|
||||
{
|
||||
std::cout << "Did not get expected fail!" << std::endl;
|
||||
return 1;
|
||||
}
|
||||
std::cout << "This call should fail." << std::endl;
|
||||
if (vtkm::testing::Testing::Run(BadAssert3) == 0)
|
||||
{
|
||||
std::cout << "Did not get expected fail!" << std::endl;
|
||||
return 1;
|
||||
}
|
||||
|
||||
std::cout << "This call should pass." << std::endl;
|
||||
// This is what your main function typically looks like.
|
||||
|
Loading…
Reference in New Issue
Block a user