Skip to content

Commit

Permalink
Simplified unit test code with c++20
Browse files Browse the repository at this point in the history
  • Loading branch information
mmd-osm committed Mar 5, 2025
1 parent 623509a commit cbee216
Show file tree
Hide file tree
Showing 5 changed files with 63 additions and 125 deletions.
21 changes: 8 additions & 13 deletions include/cgimap/output_formatter.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,8 @@ struct element_info {
bool visible_,
std::optional<osm_redaction_id_t> redaction_ = {});

bool operator==(const element_info &other) const = default;

// Standard meanings
osm_nwr_id_t id = 0;
osm_nwr_id_t version = 0;
Expand Down Expand Up @@ -98,6 +100,8 @@ struct changeset_info {
size_t num_changes_,
size_t comments_count_);

bool operator==(const changeset_info &other) const = default;

// returns true if the changeset is "open" at a particular
// point in time.
//
Expand All @@ -111,7 +115,8 @@ struct changeset_info {
// closed explicitly with a closing time, or close implicitly
// an hour after the last update to the changeset. closed_at
// should have an ISO 8601 format: YYYY-MM-DDTHH:MM:SSZ
std::string created_at, closed_at;
std::string created_at;
std::string closed_at;
// anonymous objects don't have UIDs or display names
std::optional<osm_user_id_t> uid;
std::optional<std::string> display_name;
Expand Down Expand Up @@ -141,13 +146,7 @@ struct changeset_comment_info {
std::string created_at,
std::string author_display_name);

constexpr bool operator==(const changeset_comment_info &other) const {
return ((id == other.id) &&
(author_id == other.author_id) &&
(body == other.body) &&
(created_at == other.created_at) &&
(author_display_name == other.author_display_name));
}
bool operator==(const changeset_comment_info &other) const = default;
};

struct member_info {
Expand All @@ -158,11 +157,7 @@ struct member_info {
member_info() = default;
member_info(element_type type_, osm_nwr_id_t ref_, std::string role_);

constexpr bool operator==(const member_info &other) const {
return ((type == other.type) &&
(ref == other.ref) &&
(role == other.role));
}
bool operator==(const member_info &other) const = default;
};

using nodes_t = std::vector<osm_nwr_id_t>;
Expand Down
48 changes: 24 additions & 24 deletions test/test_core.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
#include "cgimap/output_buffer.hpp"
#include "cgimap/request_helpers.hpp"
#include "cgimap/time.hpp"
#include "cgimap/util.hpp"
#include "staticxml.hpp"

#include <boost/program_options.hpp>
Expand All @@ -23,6 +24,7 @@
#include <filesystem>
#include <fstream>
#include <vector>
#include <ranges>
#include <sstream>

#include "test_request.hpp"
Expand All @@ -43,32 +45,32 @@ std::map<std::string, std::string> read_headers(std::istream &in,
// allow comments in lines which begin immediately with #. this shouldn't
// conflict with any headers, as although http headers technically can start
// with #, i'm pretty sure we're not using any which do.
if ((line.size() > 0) && (line[0] == '#')) {
if (line.starts_with('#')) {
continue;
}

al::erase_all(line, "\r");
std::erase(line, '\r');

if (!in.good()) {
throw std::runtime_error("Test file ends before separator.");
}
if (line == separator) {
break;
}

boost::iterator_range<std::string::iterator> result =
al::find_first(line, ":");
if (!result) {
throw std::runtime_error(
"Test file header doesn't match expected format.");
}

std::string key(line.begin(), result.begin());
std::string val(result.end(), line.end());
// Split HTTP header "Request-Method: GET" into "Request-Method" and value "GET"
std::string_view line_view(line);

al::trim(key);
al::trim(val);
auto pos = line_view.find(':');

headers.insert(std::make_pair(key, val));
if (pos != std::string_view::npos) {
auto key = std::string(trim(line_view.substr(0, pos)));
auto value = std::string(trim(line_view.substr(pos + 1)));
headers.try_emplace(key, value);
}
else {
throw std::runtime_error("Test file header doesn't match expected format.");
}
}

return headers;
Expand All @@ -78,20 +80,18 @@ std::map<std::string, std::string> read_headers(std::istream &in,
* take the test file and use it to set up the request headers.
*/
void setup_request_headers(test_request &req, std::istream &in) {
using dict = std::map<std::string, std::string>;
dict headers = read_headers(in, "---");
auto headers = read_headers(in, "---");

for (const dict::value_type &val : headers) {
std::string key(val.first);

al::to_upper(key);
al::replace_all(key, "-", "_");
for (auto const & [k, v] : headers) {
auto key = k;
std::ranges::transform(key, key.begin(), [](unsigned char c) {
return (c == '-' ? '_' : std::toupper(c));
});

if (key == "DATE") {
req.set_current_time(parse_time(val.second));

req.set_current_time(parse_time(v));
} else {
req.set_header(key, val.second);
req.set_header(key, v);
}
}

Expand Down
10 changes: 5 additions & 5 deletions test/test_database.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -155,8 +155,8 @@ void test_database::testcase_ended() {
txn_owner_readwrite.reset();
}

void test_database::run(
const std::function<void(test_database&)> &func) {
template <typename Func>
void test_database::run(Func func) {

try {
// clear out database before using it!
Expand All @@ -170,8 +170,8 @@ void test_database::run(
testcase_ended();
}

void test_database::run_update(
const std::function<void(test_database&)> &func) {
template <typename Func>
void test_database::run_update(Func func) {

try {
// clear out database before using it!
Expand All @@ -184,7 +184,7 @@ void test_database::run_update(
testcase_ended();
}

test_database::setup_error::setup_error(std::string str)
test_database::setup_error::setup_error(const std::string& str)
: m_str(str) {
}

Expand Down
8 changes: 5 additions & 3 deletions test/test_database.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ struct test_database {
// allow the test to be skipped, as people might not have or want an
// apidb database set up on their local machines.
struct setup_error : public std::exception {
explicit setup_error(std::string fmt);
explicit setup_error(const std::string& fmt);
~setup_error() noexcept override = default;
const char *what() const noexcept override;

Expand All @@ -59,11 +59,13 @@ struct test_database {
// writeable and readonly data selection available from the
// test_database's get_data_selection() call. the func should
// do its own testing - the run method here is just plumbing.
void run(const std::function<void(test_database&)> &func);
template <typename Func>
void run(Func func);

// run a database update test in write mode. test will be
// executed exactly once only.
void run_update(const std::function<void(test_database&)> &func);
template <typename Func>
void run_update(Func func);

// return a data selection factory pointing at the current database
[[nodiscard]] std::shared_ptr<data_selection::factory> get_data_selection_factory() const;
Expand Down
101 changes: 21 additions & 80 deletions test/test_formatter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -14,40 +14,18 @@
#include <ctime>
#include <fstream>
#include <iomanip>
#include <ranges>


namespace {

bool equal_tags(const tags_t &a, const tags_t &b) {
using vec_tags_t = std::vector<std::pair<std::string, std::string> >;
if (a.size() != b.size()) { return false; }
vec_tags_t sorted_a(a.begin(), a.end());
vec_tags_t sorted_b(b.begin(), b.end());
std::sort(sorted_a.begin(), sorted_a.end());
std::sort(sorted_b.begin(), sorted_b.end());
return std::equal(sorted_a.begin(), sorted_a.end(), sorted_b.begin());
}

bool equal_nodes(const nodes_t &a, const nodes_t &b) {
if (a.size() != b.size()) { return false; }
std::vector<osm_nwr_id_t> a_vec(a.begin(), a.end());
std::vector<osm_nwr_id_t> b_vec(b.begin(), b.end());
return std::equal(a_vec.begin(), a_vec.end(), b_vec.begin());
}

bool equal_members(const members_t &a, const members_t &b) {
if (a.size() != b.size()) { return false; }
std::vector<member_info> a_vec(a.begin(), a.end());
std::vector<member_info> b_vec(b.begin(), b.end());
const size_t n = a_vec.size();
for (size_t i = 0; i < n; ++i) {
if ((a_vec[i].type != b_vec[i].type)
|| (a_vec[i].ref != b_vec[i].ref)
|| (a_vec[i].role != b_vec[i].role)) {
return false;
}
}
return true;
auto sorted_a = a;
auto sorted_b = b;
std::ranges::sort(sorted_a);
std::ranges::sort(sorted_b);
return (sorted_a == sorted_b);
}

} // anonymous namespace
Expand All @@ -57,35 +35,20 @@ test_formatter::node_t::node_t(const element_info &elem_, double lon_, double la
: elem(elem_), lon(lon_), lat(lat_), tags(tags_) {}

bool test_formatter::node_t::operator==(const node_t &other) const {
#define CMP(sym) { if ((sym) != other. sym) { return false; } }
CMP(elem.id)
CMP(elem.version)
CMP(elem.changeset)
CMP(elem.timestamp)
CMP(elem.uid)
CMP(elem.display_name)
CMP(elem.visible)
CMP(lon)
CMP(lat)
#undef CMP
return equal_tags(tags, other.tags);
return elem == other.elem &&
lon == other.lon &&
lat == other.lat &&
equal_tags(tags, other.tags);
}

test_formatter::way_t::way_t(const element_info &elem_, const nodes_t &nodes_,
const tags_t &tags_)
: elem(elem_), nodes(nodes_), tags(tags_) {}

bool test_formatter::way_t::operator==(const way_t &other) const {
#define CMP(sym) { if ((sym) != other. sym) { return false; } }
CMP(elem.id)
CMP(elem.version)
CMP(elem.changeset)
CMP(elem.timestamp)
CMP(elem.uid)
CMP(elem.display_name)
CMP(elem.visible)
#undef CMP
return equal_tags(tags, other.tags) && equal_nodes(nodes, other.nodes);
return elem == other.elem &&
nodes == other.nodes &&
equal_tags(tags, other.tags);
}

test_formatter::relation_t::relation_t(const element_info &elem_,
Expand All @@ -94,16 +57,9 @@ test_formatter::relation_t::relation_t(const element_info &elem_,
: elem(elem_), members(members_), tags(tags_) {}

bool test_formatter::relation_t::operator==(const relation_t &other) const {
#define CMP(sym) { if ((sym) != other. sym) { return false; } }
CMP(elem.id)
CMP(elem.version)
CMP(elem.changeset)
CMP(elem.timestamp)
CMP(elem.uid)
CMP(elem.display_name)
CMP(elem.visible)
#undef CMP
return equal_tags(tags, other.tags) && equal_members(members, other.members);
return elem == other.elem &&
members == other.members &&
equal_tags(tags, other.tags);
}

test_formatter::changeset_t::changeset_t(const changeset_info &info,
Expand All @@ -119,26 +75,11 @@ test_formatter::changeset_t::changeset_t(const changeset_info &info,
}

bool test_formatter::changeset_t::operator==(const changeset_t &other) const {
#define CMP(sym) { if (!(sym == other.sym)) { return false; } }
CMP(m_info.id)
CMP(m_info.created_at)
CMP(m_info.closed_at)
CMP(m_info.uid)
CMP(m_info.display_name)
CMP(m_info.bounding_box)
CMP(m_info.num_changes)
CMP(m_info.comments_count)
CMP(m_tags.size())
CMP(m_include_comments)
if (m_include_comments) {
CMP(m_comments.size())
if (!std::equal(m_comments.begin(), m_comments.end(), other.m_comments.begin())) {
return false;
}
}
CMP(m_time)
#undef CMP
return std::equal(m_tags.begin(), m_tags.end(), other.m_tags.begin());
return equal_tags(other.m_tags, m_tags) &&
m_include_comments == other.m_include_comments &&
m_info == other.m_info &&
m_time == other.m_time &&
(!m_include_comments || m_comments == other.m_comments);
}

mime::type test_formatter::mime_type() const {
Expand Down

0 comments on commit cbee216

Please sign in to comment.