Files
scylladb/test/lib/cql_assertions.cc
Kefu Chai c37f4e5252 treewide: use fmt::join() when appropriate
now that fmtlib provides fmt::join(). see
https://fmt.dev/latest/api.html#_CPPv4I0EN3fmt4joinE9join_viewIN6detail10iterator_tI5RangeEEN6detail10sentinel_tI5RangeEEERR5Range11string_view
there is not need to revent the wheel. so in this change, the homebrew
join() is replaced with fmt::join().

as fmt::join() returns an join_view(), this could improve the
performance under certain circumstances where the fully materialized
string is not needed.

please note, the goal of this change is to use fmt::join(), and this
change does not intend to improve the performance of existing
implementation based on "operator<<" unless the new implementation is
much more complicated. we will address the unnecessarily materialized
strings in a follow-up commit.

some noteworthy things related to this change:

* unlike the existing `join()`, `fmt::join()` returns a view. so we
  have to materialize the view if what we expect is a `sstring`
* `fmt::format()` does not accept a view, so we cannot pass the
  return value of `fmt::join()` to `fmt::format()`
* fmtlib does not format a typed pointer, i.e., it does not format,
  for instance, a `const std::string*`. but operator<<() always print
  a typed pointer. so if we want to format a typed pointer, we either
  need to cast the pointer to `void*` or use `fmt::ptr()`.
* fmtlib is not able to pick up the overload of
  `operator<<(std::ostream& os, const column_definition* cd)`, so we
  have to use a wrapper class of `maybe_column_definition` for printing
  a pointer to `column_definition`. since the overload is only used
  by the two overloads of
  `statement_restrictions::add_single_column_parition_key_restriction()`,
  the operator<< for `const column_definition*` is dropped.

Signed-off-by: Kefu Chai <kefu.chai@scylladb.com>
2023-03-16 20:34:18 +08:00

239 lines
8.2 KiB
C++

/*
* Copyright (C) 2015-present ScyllaDB
*/
/*
* SPDX-License-Identifier: AGPL-3.0-or-later
*/
#include <boost/test/unit_test.hpp>
#include <boost/range/adaptor/transformed.hpp>
#include "test/lib/cql_assertions.hh"
#include "transport/messages/result_message.hh"
#include "utils/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) {
const auto& rs = _rows->rs().result_set();
auto row_count = rs.size();
if (row_count != size) {
fail(format("Expected {:d} row(s) but got {:d}", size, row_count));
}
return {*this};
}
rows_assertions
rows_assertions::is_empty() {
const auto& rs = _rows->rs().result_set();
auto row_count = rs.size();
if (row_count != 0) {
auto&& first_row = *rs.rows().begin();
fail(format("Expected no rows, but got {:d}. First row: {}", row_count, to_string(first_row)));
}
return {*this};
}
rows_assertions
rows_assertions::is_not_empty() {
const auto& rs = _rows->rs().result_set();
auto row_count = rs.size();
if (row_count == 0) {
fail("Expected some rows, but was result was empty");
}
return {*this};
}
rows_assertions
rows_assertions::rows_assertions::is_null() {
const auto& rs = _rows->rs().result_set();
for (auto&& row : rs.rows()) {
for (const bytes_opt& v : row) {
if (v) {
fail(format("Expected null values. Found: {}\n", v));
}
}
}
return {*this};
}
rows_assertions
rows_assertions::rows_assertions::is_not_null() {
const auto& rs = _rows->rs().result_set();
for (auto&& row : rs.rows()) {
for (const bytes_opt& v : row) {
if (!v) {
fail(format("Expected non-null values. {}\n", to_string(row)));
}
}
}
return is_not_empty();
}
rows_assertions
rows_assertions::with_column_types(std::initializer_list<data_type> column_types) {
auto meta = _rows->rs().result_set().get_metadata();
const auto& columns = meta.get_names();
if (column_types.size() != columns.size()) {
fail(format("Expected {:d} columns, got {:d}", column_types.size(), meta.column_count()));
}
auto expected_it = column_types.begin();
auto actual_it = columns.begin();
for (int i = 0; i < (int)columns.size(); i++) {
const auto& expected_type = *expected_it++;
const auto& actual_spec = *actual_it++;
if (expected_type != actual_spec->type) {
fail(format("Column {:d}: expected type {}, got {}", i, expected_type->name(), actual_spec->type->name()));
}
}
return {*this};
}
rows_assertions
rows_assertions::with_row(std::initializer_list<bytes_opt> values) {
const auto& rs = _rows->rs().result_set();
std::vector<bytes_opt> expected_row(values);
for (auto&& row : rs.rows()) {
if (row == expected_row) {
return {*this};
}
}
fail(format("Expected row not found: {} not in {}\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::vector<std::vector<bytes_opt>> rows) {
const auto& rs = _rows->rs().result_set();
auto actual_i = rs.rows().begin();
auto actual_end = rs.rows().end();
int row_nr = 0;
for (auto&& row : rows) {
if (actual_i == actual_end) {
fail(format("Expected more rows ({:d}), got {:d}", rows.size(), rs.size()));
}
auto& actual = *actual_i;
if (!std::equal(
std::begin(row), std::end(row),
std::begin(actual), std::end(actual))) {
fail(format("row {:d} differs, expected {} got {}", row_nr, to_string(row), to_string(actual)));
}
++actual_i;
++row_nr;
}
if (actual_i != actual_end) {
fail(format("Expected less rows ({:d}), got {:d}. Next row is: {}", rows.size(), 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::vector<std::vector<bytes_opt>> rows) {
const auto& rs = _rows->rs().result_set();
auto& actual = 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(format("row {} not found in result set ({})", to_string(expected),
fmt::join(actual | boost::adaptors::transformed([] (auto& r) { return to_string(r); }), ", ")));
}
}
if (rs.size() != rows.size()) {
fail(format("Expected different number of rows ({:d}), got {:d}", rows.size(), rs.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);
}
rows_assertions rows_assertions::with_serialized_columns_count(size_t columns_count) {
size_t serialized_column_count = _rows->rs().get_metadata().column_count();
if (serialized_column_count != columns_count) {
fail(format("Expected {:d} serialized columns(s) but got {:d}", columns_count, serialized_column_count));
}
return {*this};
}
shared_ptr<cql_transport::messages::result_message> cquery_nofail(
cql_test_env& env, sstring_view query, std::unique_ptr<cql3::query_options>&& qo, const std::source_location& loc) {
try {
if (qo) {
return env.execute_cql(query, std::move(qo)).get0();
} else {
return env.execute_cql(query).get0();
}
} catch (...) {
BOOST_FAIL(format("query '{}' failed: {}\n{}:{}: originally from here",
query, std::current_exception(), loc.file_name(), loc.line()));
}
return shared_ptr<cql_transport::messages::result_message>(nullptr);
}
void require_rows(cql_test_env& e,
sstring_view qstr,
const std::vector<std::vector<bytes_opt>>& expected,
const std::source_location& loc) {
try {
assert_that(cquery_nofail(e, qstr, nullptr, loc)).is_rows().with_rows_ignore_order(expected);
}
catch (const std::exception& e) {
BOOST_FAIL(format("query '{}' failed: {}\n{}:{}: originally from here",
qstr, e.what(), loc.file_name(), loc.line()));
}
}
void eventually_require_rows(cql_test_env& e, sstring_view qstr, const std::vector<std::vector<bytes_opt>>& expected,
const std::source_location& loc) {
try {
eventually([&] {
assert_that(cquery_nofail(e, qstr, nullptr, loc)).is_rows().with_rows_ignore_order(expected);
});
} catch (const std::exception& e) {
BOOST_FAIL(format("query '{}' failed: {}\n{}:{}: originally from here",
qstr, e.what(), loc.file_name(), loc.line()));
}
}
void require_rows(cql_test_env& e,
cql3::prepared_cache_key_type id,
const std::vector<cql3::raw_value>& values,
const std::vector<std::vector<bytes_opt>>& expected,
const std::source_location& loc) {
try {
assert_that(e.execute_prepared(id, values).get0()).is_rows().with_rows_ignore_order(expected);
} catch (const std::exception& e) {
BOOST_FAIL(format("execute_prepared failed: {}\n{}:{}: originally from here",
e.what(), loc.file_name(), loc.line()));
}
}