From 38d7045685d4904d013ea4990a3aa7f5e78309cf Mon Sep 17 00:00:00 2001 From: Dan Goodliffe Date: Sat, 22 Oct 2022 17:57:47 +0100 Subject: Add magic support to printing/parsing/validating enumerations --- test/Jamfile.jam | 2 ++ test/enumDetailsData.hpp | 20 +++++++++++ test/test-enumDetails.cpp | 50 ++++++++++++++++++++++++++ test/test-static-enumDetails.cpp | 78 ++++++++++++++++++++++++++++++++++++++++ 4 files changed, 150 insertions(+) create mode 100644 test/enumDetailsData.hpp create mode 100644 test/test-enumDetails.cpp create mode 100644 test/test-static-enumDetails.cpp (limited to 'test') diff --git a/test/Jamfile.jam b/test/Jamfile.jam index afe58c8..7606ae4 100644 --- a/test/Jamfile.jam +++ b/test/Jamfile.jam @@ -29,3 +29,5 @@ run test-geo.cpp ; run test-network.cpp ; run test-persistence.cpp : -- : [ sequence.insertion-sort [ glob fixtures/json/*.json fixtures/json/bad/*.json ] ] ; run test-text.cpp ; +run test-enumDetails.cpp ; +compile test-static-enumDetails.cpp ; diff --git a/test/enumDetailsData.hpp b/test/enumDetailsData.hpp new file mode 100644 index 0000000..0e98af5 --- /dev/null +++ b/test/enumDetailsData.hpp @@ -0,0 +1,20 @@ +#pragma once +#include + +enum GlobalUnscoped { aa, b, c }; +enum class GlobalScoped { aa, b, c }; +namespace ns { + enum Unscoped { aa, b, c }; + enum class Scoped { aa, b, c }; +} +namespace test1 { + enum class DefaultDense { a, bee, ci, de }; +} +namespace test2 { + enum class NumberedSparse { a = 0, bee = 3, ci = -20, de = 100 }; +} + +template<> struct EnumValueCollection { + // Any ordered integer_sequence which includes all enumeration values + using Vs = std::integer_sequence; +}; diff --git a/test/test-enumDetails.cpp b/test/test-enumDetails.cpp new file mode 100644 index 0000000..c803cf8 --- /dev/null +++ b/test/test-enumDetails.cpp @@ -0,0 +1,50 @@ +#define BOOST_TEST_MODULE test_enumDetails + +#include "enumDetailsData.hpp" +#include +#include +#include +#include + +constexpr std::array INVALID_NAMES {"", "missing", "GlobalScoped::aa", "GlobalScoped", "ns::aa", "a", "bb"}; +constexpr std::array VALID_NAMES {"aa", "b", "c"}; +template constexpr std::array VALID_VALUES {E::aa, E::b, E::c}; +// Not a template, else Boost test framework throws printing the context +constexpr std::array INVALID_VALUES {-1, 3, 20}; + +#define TESTS_FOR_TYPE(TYPE) \ + BOOST_DATA_TEST_CASE(invalid_check_##TYPE, INVALID_VALUES, in) \ + { \ + BOOST_CHECK(!EnumDetails::is_valid(static_cast(in))); \ + } \ + BOOST_DATA_TEST_CASE(invalid_parse_##TYPE, INVALID_NAMES, in) \ + { \ + BOOST_CHECK(!EnumDetails::parse(in).has_value()); \ + } \ + BOOST_DATA_TEST_CASE(invalid_to_string_##TYPE, INVALID_VALUES, in) \ + { \ + BOOST_CHECK(!EnumDetails::to_string(static_cast(in)).has_value()); \ + } \ + BOOST_DATA_TEST_CASE(valid_check_##TYPE, VALID_VALUES, in) \ + { \ + BOOST_CHECK(EnumDetails::is_valid(in)); \ + } \ + BOOST_DATA_TEST_CASE(valid_parse_##TYPE, VALID_NAMES ^ VALID_VALUES, in, out) \ + { \ + const auto v = EnumDetails::parse(in); \ + BOOST_REQUIRE(v.has_value()); \ + BOOST_CHECK_EQUAL(v.value(), out); \ + } \ + BOOST_DATA_TEST_CASE(valid_to_string_##TYPE, VALID_VALUES ^ VALID_NAMES, in, out) \ + { \ + const auto v = EnumDetails::to_string(in); \ + BOOST_CHECK(v.has_value()); \ + BOOST_CHECK_EQUAL(v.value(), out); \ + } + +TESTS_FOR_TYPE(GlobalScoped) +TESTS_FOR_TYPE(GlobalUnscoped) +using ns_unscoped = ns::Unscoped; +using ns_scoped = ns::Scoped; +TESTS_FOR_TYPE(ns_unscoped) +TESTS_FOR_TYPE(ns_scoped) diff --git a/test/test-static-enumDetails.cpp b/test/test-static-enumDetails.cpp new file mode 100644 index 0000000..03c2203 --- /dev/null +++ b/test/test-static-enumDetails.cpp @@ -0,0 +1,78 @@ +#define ENUM_PROBE +#include "enumDetailsData.hpp" +#include + +// Test type name +static_assert(EnumTypeDetails::typeName == "GlobalUnscoped"); +static_assert(EnumTypeDetails::typeName == "GlobalScoped"); +static_assert(EnumTypeDetails::typeName == "ns::Unscoped"); +static_assert(EnumTypeDetails::typeName == "ns::Scoped"); + +static_assert(EnumValueDetails::valueName == "aa"); +static_assert(EnumValueDetails::valueName == "aa"); +static_assert(EnumValueDetails::valueName == "aa"); +static_assert(EnumValueDetails::valueName == "aa"); + +namespace test1 { + static_assert(EnumValueDetails::valid); + static_assert(EnumValueDetails::valid); + static_assert(EnumValueDetails(0)>::valid); + static_assert(EnumValueDetails(3)>::valid); + static_assert(!EnumValueDetails(-1)>::valid); + static_assert(!EnumValueDetails(4)>::valid); + static_assert(EnumValueDetails::valueName == "a"); + static_assert(EnumValueDetails::valueName == "de"); + using ED_DD = EnumDetails; + static_assert(EnumValueCollection::Vs::size() == 256); + static_assert(ED_DD::valid_flags.size() == 256); + static_assert(ED_DD::values.size() == 4); + static_assert(std::is_sorted(ED_DD::values.begin(), ED_DD::values.end())); + static_assert(ED_DD::values.at(0) == DefaultDense::a); + static_assert(ED_DD::values.at(3) == DefaultDense::de); + static_assert(ED_DD::names.at(0) == "a"); + static_assert(ED_DD::names.at(3) == "de"); + + static_assert(ED_DD::is_valid(DefaultDense::a)); + static_assert(ED_DD::is_valid(DefaultDense::de)); + static_assert(!ED_DD::is_valid(DefaultDense(-1))); + static_assert(!ED_DD::parse("").has_value()); + static_assert(!ED_DD::parse("nonsense").has_value()); + static_assert(ED_DD::parse("bee").value() == DefaultDense::bee); + static_assert(ED_DD::parse("ci").value() == DefaultDense::ci); + static_assert(ED_DD::to_string(DefaultDense::de).value() == "de"); + static_assert(!ED_DD::to_string(static_cast(10)).has_value()); +} + +namespace test2 { + static_assert(EnumValueDetails::valid); + static_assert(EnumValueDetails(0)>::valid); + static_assert(EnumValueDetails(3)>::valid); + static_assert(EnumValueDetails(-20)>::valid); + static_assert(EnumValueDetails(100)>::valid); + static_assert(!EnumValueDetails(2)>::valid); + static_assert(EnumValueDetails::valueName == "a"); + static_assert(EnumValueDetails::valueName == "de"); + using ED_NS = EnumDetails; + static_assert(EnumValueCollection::Vs::size() == 7); + static_assert(ED_NS::values.size() == 4); + static_assert(ED_NS::valid_flags.size() == 7); + static_assert(std::is_sorted(ED_NS::values.begin(), ED_NS::values.end())); + static_assert(ED_NS::values.at(0) == NumberedSparse::ci); + static_assert(ED_NS::values.at(1) == NumberedSparse::a); + static_assert(ED_NS::values.at(2) == NumberedSparse::bee); + static_assert(ED_NS::values.at(3) == NumberedSparse::de); + static_assert(ED_NS::names.at(0) == "ci"); + static_assert(ED_NS::names.at(1) == "a"); + static_assert(ED_NS::names.at(2) == "bee"); + static_assert(ED_NS::names.at(3) == "de"); + + static_assert(ED_NS::is_valid(NumberedSparse::a)); + static_assert(ED_NS::is_valid(NumberedSparse::de)); + static_assert(!ED_NS::is_valid(NumberedSparse(-1))); + static_assert(!ED_NS::parse("").has_value()); + static_assert(!ED_NS::parse("nonsense").has_value()); + static_assert(ED_NS::parse("bee").value() == NumberedSparse::bee); + static_assert(ED_NS::parse("ci").value() == NumberedSparse::ci); + static_assert(ED_NS::to_string(NumberedSparse::ci).value() == "ci"); + static_assert(!ED_NS::to_string(static_cast(10)).has_value()); +} -- cgit v1.2.3