From eb6abb020e895c917fb34a7f052b67b57910c2d6 Mon Sep 17 00:00:00 2001 From: Dima Korolev Date: Sun, 1 Sep 2019 10:41:56 -0700 Subject: [PATCH] Made it possible to "unit test" code from within implementations. --- benchmark_base64.cc | 45 +++++++++++++++++++++++++------- ensure_implementations_compile.h | 7 ++++- implementations.h | 9 +++++++ 3 files changed, 51 insertions(+), 10 deletions(-) diff --git a/benchmark_base64.cc b/benchmark_base64.cc index f4f43be..d96b34f 100644 --- a/benchmark_base64.cc +++ b/benchmark_base64.cc @@ -31,6 +31,33 @@ DEFINE_uint64(n, 0, "The number of blocks to benchmark on, keep at zero to deriv DEFINE_double(gb, ndebug ? 0.1 : 0.01, "If `--n` is not set, the number of (unencoded) gigabytes to run the test on."); DEFINE_bool(log, false, "Output extra details, to stderr. By default, MB/s will be printed, to stdout."); +template +struct EXPECT_EQ_IMPL final { + static void DoIt(LHS&& lhs, RHS&& rhs) { + if (lhs != rhs) { + std::cerr << "\b\b\bFail.\nDetails: '" << lhs << "' != '" << rhs << "'.\n"; + std::exit(-1); + } + } +}; + +template +struct EXPECT_EQ_IMPL final { + static void DoIt(std::string const& lhs, std::string const& rhs) { + if (lhs != rhs) { + std::cerr << "\b\b\bFail.\nDetails: '" << lhs << "' != '" << rhs << "'.\n"; + std::exit(-1); + } + } +}; + +template +inline void EXPECT_EQ(LHS&& lhs, RHS&& rhs) { + EXPECT_EQ_IMPL::value || current::strings::is_string_type::value, + current::decay, + current::decay>::DoIt(std::forward(lhs), std::forward(rhs)); +} + namespace implementations { #define BASE64_PERFTEST_EXPAND_MACRO_IMPL(x) x @@ -45,14 +72,19 @@ struct Code; // The canonical implementation keeps a member `std::string tmp` as the stateful storage. // This is to enable performance tests against other implementations, that are not using `std::string`-s. +struct HasInit { + virtual ~HasInit() = default; + virtual void Init() const {} +}; + #define IMPLEMENTATION(name) \ static constexpr int index_##name = BASE64_PERFTEST_EXPAND_MACRO(__COUNTER__) - counter_begin; \ template <> \ - struct Name { \ + struct Name final { \ static char const* HumanReadableName() { return #name; } \ }; \ template <> \ - struct Code + struct Code final : HasInit #include "implementations.h" @@ -63,17 +95,12 @@ static constexpr int total = BASE64_PERFTEST_EXPAND_MACRO(__COUNTER__) - counter template void Run(size_t n, std::vector const& input, std::vector const& golden, size_t total_bytes) { IMPL impl; + impl.Init(); if (FLAGS_log) { std::cerr << "Testing correctness: ..."; } - auto const EXPECT_EQ = [](std::string const& lhs, std::string const& rhs) { - if (lhs != rhs) { - std::cerr << "\b\b\bFail.\nDetails: '" << lhs << "' != '" << rhs << "'.\n"; - std::exit(-1); - } - }; auto const Base64Encode = [&impl](std::string const& original) { return impl.DoEncode(original); }; auto const Base64Decode = [&impl](std::string const& encoded) { return impl.DoDecode(encoded); }; @@ -157,7 +184,7 @@ void Run(size_t n, std::vector const& input, std::vector names; std::map< std::string, diff --git a/ensure_implementations_compile.h b/ensure_implementations_compile.h index d341bb4..70abf41 100644 --- a/ensure_implementations_compile.h +++ b/ensure_implementations_compile.h @@ -11,5 +11,10 @@ #include "current/bricks/util/singleton.h" #ifndef IMPLEMENTATION -#define IMPLEMENTATION(x) struct unused_implementation_##x +struct HasInitStub { + virtual ~HasInitStub() = default; + virtual void Init() const {} +}; +#define EXPECT_EQ(...) +#define IMPLEMENTATION(x) struct unused_implementation_##x final : HasInitStub #endif diff --git a/implementations.h b/implementations.h index 735f24f..808be1d 100644 --- a/implementations.h +++ b/implementations.h @@ -3,6 +3,15 @@ #include "ensure_implementations_compile.h" IMPLEMENTATION(vanilla) { + void Init() const override { + EXPECT_EQ(4, 2 + 2); + std::string const foo = "foo"; + std::string const bar = "bar"; + EXPECT_EQ("foobar", foo + bar); + // EXPECT_EQ(5, 2 * 2); // This would fail. + // EXPECT_EQ("foo", "bar"); // This would fail. + } + std::string placeholder; current::strings::Chunk DoEncode(current::strings::Chunk chunk) { placeholder = current::Base64Encode(chunk.c_str(), chunk.length());