Короче говоря
Создайте один foo::list
и один std::list
, а затем сравните их при выполнении операций над ними. На самом деле единственное отличие от обычного модульного теста состоит в том, что у вас есть два контейнера, и вместо непосредственного использования REQUIRE()
для каждой операции с типом, который вы тестируете, вы выполняете операцию с типом, который вы тестируете, и эталонным типом, а затем сравниваетеих. Для этого мы предполагаем, что std::list
или что-то без ошибок. Затем мы используем его в качестве нашей контрольной точки для , а не для . Другими словами, если операция завершается успешно с std::list
и выполняется с foo::list
, и они сравниваются равными, операция завершается успешно.
Пример
Вы знаете, что такое подмножество операцийчто вы можете использовать для сравнения состояния, а я нет, так что вот фиктивная функция сравнения
template <class T, class U>
bool compare_types(const T &t, const U &u)
{
bool equivalent = true;
//Generic comparisons here, like iterating over the elements to compare their values.
//Of course update equal or just return false if the comparison fails.
//If your types are not containers, perform whatever is needed to test equivalent state.
return equivalent;
}
Как указывал Jarod42, это может стать более забавным и более общим, особенно если Op f
нижелямбда (C ++ 14 необходим для общих лямбда-выражений):
template <class ValueT, class RefT, class TestT, class Op>
bool compare_op_with_value(RefT &t, TestT &u, Op f, const ValueT &value)
{
if (!compare_types(t, u))
return false;
f(t, value);
f(u, value);
return compare_types(t, u);
}
Ваша функция может возвращать значение:
template <class ValueT, class RefT, class TestT, class Op>
bool compare_op_with_ret(RefT &t, TestT &u, Op f)
{
if (!compare_types(t, u))
return false;
ValueT ret1 = f(t);
ValueT ret2 = f(u);
return ret1 == ret2 && compare_types(t, u);
}
... и т. д. для разыменованных типов возврата и т. д. Вам нужно будет написать новую функцию сравнения для каждого вида теста, но это довольно тривиально. Вам нужно будет добавить еще один параметр шаблона для возвращаемых типов, которые не совпадают (например, итератор).
Затем вам понадобится ваш тестовый пример (я добавил в std::vector
как foo::list
для описания). ..
TEMPLATE_TEST_CASE("StdFooCompare", "[list]", int)
{
using std_type = std::list<TestType>;
using foo_type = std::vector<TestType>;
auto initializer = {0,1,2,3,4};
std_type list1 = initializer;
foo_type list2 = initializer;
//testing insertion, using auto since insert() returns iterators
auto insert_f = [](auto list, TestType value) -> auto {
return list.insert(list.begin(), value);
};
REQUIRE(compare_op_with_value(list1, list2, insert_f, -1));
//testing front(), being explicit about the return type rather than using auto
auto front_f = [](auto list) -> TestType & {
return list.front();
};
REQUIRE(compare_op_with_ret<TestType>(list1, list2, front_f));
//continue testing along these lines
}
Я мог бы потратить еще пару часов на это, но я надеюсь, что вы поняли. Я потратил больше времени на это.
Примечание:На самом деле я не запускал этот код, поэтому рассмотрите весь этот псевдокод, чтобы донести идею, например, я мог пропустить точку с запятой или что-то подобное.