diff --git a/configure.py b/configure.py index 575ea2aee2..c5c9383fa4 100755 --- a/configure.py +++ b/configure.py @@ -509,6 +509,7 @@ urchin_core = (['database.cc', urchin_tests_dependencies = urchin_core + http + api + [ 'tests/urchin/cql_test_env.cc', 'tests/urchin/cql_assertions.cc', + 'tests/urchin/result_set_assertions.cc', ] deps = { diff --git a/tests/urchin/result_set_assertions.cc b/tests/urchin/result_set_assertions.cc new file mode 100644 index 0000000000..1bdebf2efc --- /dev/null +++ b/tests/urchin/result_set_assertions.cc @@ -0,0 +1,73 @@ +/* + * Copyright 2015 Cloudius Systems + */ + +#include + +#include "result_set_assertions.hh" +#include "to_string.hh" + +static inline +sstring to_sstring(const bytes& b) { + return sstring(b.begin(), b.end()); +} + +bool +row_assertion::matches(const query::result_set_row& row) const { + for (auto&& column_and_value : _expected_values) { + auto&& name = column_and_value.first; + auto&& value = column_and_value.second; + + // FIXME: result_set_row works on sstring column names instead of more general "bytes". + auto ss_name = to_sstring(name); + + if (!row.has(ss_name)) { + return false; + } + const data_value& val = row.get_data_value(ss_name); + if (val != data_value(boost::any(value), val.type())) { + return false; + } + } + return true; +} + +sstring +row_assertion::describe(schema_ptr schema) const { + return "{" + ::join(", ", _expected_values | boost::adaptors::transformed([&schema] (auto&& e) { + auto&& name = e.first; + auto&& value = e.second; + const column_definition* def = schema->get_column_definition(name); + if (!def) { + BOOST_FAIL(sprint("Schema is missing column definition for '%s'", name)); + } + return sprint("%s=\"%s\"", to_sstring(name), def->type->to_string(def->type->decompose(value))); + })) + "}"; +} + +const result_set_assertions& +result_set_assertions::has(const row_assertion& ra) const { + for (auto&& row : _rs.rows()) { + if (ra.matches(row)) { + return *this; + } + } + BOOST_FAIL(sprint("Row %s not found in %s", ra.describe(_rs.schema()), _rs)); + return *this; +} + +const result_set_assertions& +result_set_assertions::has_only(const row_assertion& ra) const { + BOOST_REQUIRE(_rs.rows().size() == 1); + auto& row = _rs.rows()[0]; + if (!ra.matches(row)) { + BOOST_FAIL(sprint("Expected %s but got %s", ra.describe(_rs.schema()), row)); + } + return *this; +} + +const result_set_assertions& +result_set_assertions::is_empty() const { + BOOST_REQUIRE_EQUAL(_rs.rows().size(), 0); + return *this; +} diff --git a/tests/urchin/result_set_assertions.hh b/tests/urchin/result_set_assertions.hh new file mode 100644 index 0000000000..6d04cb4685 --- /dev/null +++ b/tests/urchin/result_set_assertions.hh @@ -0,0 +1,51 @@ +/* + * Copyright 2015 Cloudius Systems + */ + +#pragma once + +#include + +#include "query-result-set.hh" + +// +// Contains assertions for query::result_set objects +// +// Example use: +// +// assert_that(rs) +// .has(a_row().with_column("column_name", "value")); +// + +class row_assertion { + std::map _expected_values; +public: + row_assertion& with_column(bytes name, boost::any value) { + _expected_values.emplace(name, value); + return *this; + } +private: + friend class result_set_assertions; + bool matches(const query::result_set_row& row) const; + sstring describe(schema_ptr s) const; +}; + +inline +row_assertion a_row() { + return {}; +} + +class result_set_assertions { + const query::result_set& _rs; +public: + result_set_assertions(const query::result_set& rs) : _rs(rs) { } + const result_set_assertions& has(const row_assertion& ra) const; + const result_set_assertions& has_only(const row_assertion& ra) const; + const result_set_assertions& is_empty() const; +}; + +// Make rs live as long as the returned assertion object is used +inline +result_set_assertions assert_that(const query::result_set& rs) { + return { rs }; +}