The functions in cql_assertions.hh are very convenient, but have one frustrating drawback: When you have many of those assertions in one test, it's very hard to know *which* of the similar assertions failed. The problem is that an error often looks like this: unknown location(0): fatal error: in "test_many_columns": std::runtime_error: Expected 2 row(s) but got 0 tests/cql_assertions.cc(131): last checkpoint Which of the many similar checks in "test_many_columns" failed? Note the unhelpful "unknown location" and also the "last checkpoint" points to code in cql_assertions.cc, not in the actual test, so it is useless. The root cause of these problems is that the Boost macros use the C preprocessor __FILE__ and __LINE__, which in actual C++ functions like is_rows() remembers its location, instead of the caller. Fixing this will not be simple. But this patch has a much simpler solution - fixing the "last checkpoint". What ruins the last checkpoint is the use of BOOST_REQUIRE inside the cql_assertions.cc is_rows() - when that succeeds, it records the location inside cql_assertions.cc (!) as the last success. If we just replace BOOST_REQUIRE by our own test (just like in the rest of the cql_assertions.cc code), this code will not override the last checkpoint. The user can see the last real successful BOOST_REQUIRE, or use BOOST_TEST_PASSPOINT() to set his own checkpoints between different parts of the same test. After this patch, and with adding BOOST_TEST_PASSPOINT() calls between different parts of my test, the failure above now looks like: unknown location(0): fatal error: in "test_many_columns": std::runtime_error: Expected 2 row(s) but got 0 tests/secondary_index_test.cc(299): last checkpoint The "last checkpoint" now shows me exactly where my failing check was. Signed-off-by: Nadav Har'El <nyh@scylladb.com> Message-Id: <20180501152638.26238-1-nyh@scylladb.com>
140 lines
4.6 KiB
C++
140 lines
4.6 KiB
C++
|
|
/*
|
|
* Copyright (C) 2015 ScyllaDB
|
|
*/
|
|
|
|
/*
|
|
* This file is part of Scylla.
|
|
*
|
|
* Scylla is free software: you can redistribute it and/or modify
|
|
* it under the terms of the GNU Affero General Public License as published by
|
|
* the Free Software Foundation, either version 3 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* Scylla is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with Scylla. If not, see <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
#include <boost/test/unit_test.hpp>
|
|
#include <boost/range/adaptor/transformed.hpp>
|
|
#include "cql_assertions.hh"
|
|
#include "transport/messages/result_message.hh"
|
|
#include "to_string.hh"
|
|
#include "bytes.hh"
|
|
|
|
static inline void fail(sstring msg) {
|
|
throw std::runtime_error(msg);
|
|
}
|
|
|
|
rows_assertions::rows_assertions(shared_ptr<cql_transport::messages::result_message::rows> rows)
|
|
: _rows(rows)
|
|
{ }
|
|
|
|
rows_assertions
|
|
rows_assertions::with_size(size_t size) {
|
|
auto row_count = _rows->rs().size();
|
|
if (row_count != size) {
|
|
fail(sprint("Expected %d row(s) but got %d", size, row_count));
|
|
}
|
|
return {*this};
|
|
}
|
|
|
|
rows_assertions
|
|
rows_assertions::is_empty() {
|
|
auto row_count = _rows->rs().size();
|
|
if (row_count != 0) {
|
|
auto&& first_row = *_rows->rs().rows().begin();
|
|
fail(sprint("Expected no rows, but got %d. First row: %s", row_count, to_string(first_row)));
|
|
}
|
|
return {*this};
|
|
}
|
|
|
|
rows_assertions
|
|
rows_assertions::is_not_empty() {
|
|
auto row_count = _rows->rs().size();
|
|
if (row_count == 0) {
|
|
fail("Expected some rows, but was result was empty");
|
|
}
|
|
return {*this};
|
|
}
|
|
|
|
rows_assertions
|
|
rows_assertions::with_row(std::initializer_list<bytes_opt> values) {
|
|
std::vector<bytes_opt> expected_row(values);
|
|
for (auto&& row : _rows->rs().rows()) {
|
|
if (row == expected_row) {
|
|
return {*this};
|
|
}
|
|
}
|
|
fail(sprint("Expected row not found: %s not in %s\n", to_string(expected_row), _rows));
|
|
return {*this};
|
|
}
|
|
|
|
// Verifies that the result has the following rows and only that rows, in that order.
|
|
rows_assertions
|
|
rows_assertions::with_rows(std::initializer_list<std::initializer_list<bytes_opt>> rows) {
|
|
auto actual_i = _rows->rs().rows().begin();
|
|
auto actual_end = _rows->rs().rows().end();
|
|
int row_nr = 0;
|
|
for (auto&& row : rows) {
|
|
if (actual_i == actual_end) {
|
|
fail(sprint("Expected more rows (%d), got %d", rows.size(), _rows->rs().size()));
|
|
}
|
|
auto& actual = *actual_i;
|
|
if (!std::equal(
|
|
std::begin(row), std::end(row),
|
|
std::begin(actual), std::end(actual))) {
|
|
fail(sprint("row %d differs, expected %s got %s", row_nr, to_string(row), to_string(actual)));
|
|
}
|
|
++actual_i;
|
|
++row_nr;
|
|
}
|
|
if (actual_i != actual_end) {
|
|
fail(sprint("Expected less rows (%d), got %d. Next row is: %s", rows.size(), _rows->rs().size(),
|
|
to_string(*actual_i)));
|
|
}
|
|
return {*this};
|
|
}
|
|
|
|
// Verifies that the result has the following rows and only those rows.
|
|
rows_assertions
|
|
rows_assertions::with_rows_ignore_order(std::initializer_list<std::initializer_list<bytes_opt>> rows) {
|
|
auto& actual = _rows->rs().rows();
|
|
for (auto&& expected : rows) {
|
|
auto found = std::find_if(std::begin(actual), std::end(actual), [&] (auto&& row) {
|
|
return std::equal(
|
|
std::begin(row), std::end(row),
|
|
std::begin(expected), std::end(expected));
|
|
});
|
|
if (found == std::end(actual)) {
|
|
fail(sprint("row %s not found in result set (%s)", to_string(expected),
|
|
::join(", ", actual | boost::adaptors::transformed([] (auto& r) { return to_string(r); }))));
|
|
}
|
|
}
|
|
if (_rows->rs().size() != rows.size()) {
|
|
fail(sprint("Expected more rows (%d), got %d", _rows->rs().size(), rows.size()));
|
|
}
|
|
return {*this};
|
|
}
|
|
|
|
result_msg_assertions::result_msg_assertions(shared_ptr<cql_transport::messages::result_message> msg)
|
|
: _msg(msg)
|
|
{ }
|
|
|
|
rows_assertions result_msg_assertions::is_rows() {
|
|
auto rows = dynamic_pointer_cast<cql_transport::messages::result_message::rows>(_msg);
|
|
if (!rows) {
|
|
fail("Expected rows in result set");
|
|
}
|
|
return rows_assertions(rows);
|
|
}
|
|
|
|
result_msg_assertions assert_that(shared_ptr<cql_transport::messages::result_message> msg) {
|
|
return result_msg_assertions(msg);
|
|
}
|