Skip to content

Commit

Permalink
try refactoring SUITE_
Browse files Browse the repository at this point in the history
  • Loading branch information
bkietz committed Nov 13, 2024
1 parent 6323ea8 commit 360663f
Show file tree
Hide file tree
Showing 4 changed files with 68 additions and 64 deletions.
39 changes: 21 additions & 18 deletions cmake_modules/test_.cxx
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,6 @@ std::vector<std::any> parameters;
struct Info {
char const *file;
int line;
char const *suite_name;
char const *test_name;
};

Expand All @@ -55,33 +54,41 @@ static void body(void const *p) {
Test::body(*static_cast<Parameter const *>(p));
}

struct DefaultSuiteState {
void setup() {}
void teardown() {}
};
export DefaultSuiteState suite_(...) { return {}; }

export template <typename S>
struct Registrar {
using SuiteState = std::conditional_t<Complete<S>, S, int>;

static SuiteState *const suite_state() {
alignas(SuiteState) static char storage[sizeof(SuiteState)];
return std::launder(reinterpret_cast<SuiteState *>(&storage));
}

struct Fixture : testing::Test {
static void SetUpTestSuite() { new (suite_state()) SuiteState{}; }
static void TearDownTestSuite() { suite_state()->~SuiteState(); }
static void SetUpTestSuite() { S{}.setup(); }
static void TearDownTestSuite() { S{}.teardown(); }
void TestBody() override { _body(_parameter); }

Body *_body;
void const *_parameter;
void TestBody() override { _body(_parameter); }

template <typename Test, typename Parameter>
Fixture(Test *, Parameter const *p) : _body{&body<Test, Parameter>}, _parameter{p} {}
};

void register_one(auto *test, Info info, auto parameter, int i = -1,
std::string type_name = "") {
static_assert(std::is_empty_v<S>);
constexpr bool HAS_PARAMETER =
not std::is_same_v<decltype(parameter), std::nullptr_t>;

auto [file, line, suite_name, test_name] = info;
auto [file, line, test_name] = info;

std::string suite_name{file};
if (auto i = suite_name.find_last_of("\\/"); i != std::string::npos) {
suite_name = suite_name.substr(i + 1);
}
if (auto i = suite_name.find_first_of('.'); i != std::string::npos) {
suite_name = suite_name.substr(0, i);
}

char const *type_param = nullptr;
char const *value_param = nullptr;
Expand All @@ -99,8 +106,8 @@ struct Registrar {
name += "/" + PrintToString(*parameter);
value_param = name.c_str() + old_size + 1;
}
testing::RegisterTest(suite_name, name.c_str(), type_param, value_param, file, line,
[test, parameter] {
testing::RegisterTest(suite_name.c_str(), name.c_str(), type_param, value_param, file,
line, [test, parameter] {
if constexpr (HAS_PARAMETER) {
return new Fixture{test, parameter};
} else {
Expand Down Expand Up @@ -430,7 +437,3 @@ struct Matcher {
void DescribeTo(std::ostream *os) const { describe(*os); }
void DescribeNegationTo(std::ostream *os) const { describe_negation(*os); }
};

export struct DontTerminateIfDestructionThrows {
~DontTerminateIfDestructionThrows() noexcept(false) {}
};
68 changes: 30 additions & 38 deletions cmake_modules/test_.hxx
Original file line number Diff line number Diff line change
Expand Up @@ -48,19 +48,16 @@
/// and incorporated into the test case’s total name along with case_name and the
/// suite’s name to make it accessible to
/// :gtest:`filtering <advanced.html#running-a-subset-of-the-tests>`.
#define TEST_(case_name, ...) \
namespace SUITE_NAME { \
struct case_name : Registrar<struct SuiteState> { \
case_name() { \
register_(this, {__FILE__, __LINE__, GTEST_STRINGIFY_(SUITE_NAME), \
#case_name} __VA_OPT__(, ) __VA_ARGS__); \
} \
template <typename Parameter> \
static void body(Parameter const &parameter); \
} case_name; \
} \
template <typename Parameter> \
void SUITE_NAME::case_name::body(Parameter const &parameter)
#define TEST_(case_name, ...) \
struct case_name : Registrar<decltype(::suite_(nullptr))> { \
case_name() { \
register_(this, {__FILE__, __LINE__, #case_name} __VA_OPT__(, ) __VA_ARGS__); \
} \
template <typename Parameter> \
static void body(Parameter const &parameter); \
} case_name; \
template <typename Parameter> \
void case_name::body(Parameter const &parameter)

///.. c:macro:: EXPECT_(condition...)
///
Expand Down Expand Up @@ -110,44 +107,39 @@
(::expect_helper::Begin{} <= __VA_ARGS__, ::expect_helper::End{#__VA_ARGS__}) \
}

/// Define state/resources available during a suite's execution.
/// Define shared suite resources.
///
/// Defines a ``struct`` which will be constructed once before
/// any cases in the suite are run and destroyed when no more
/// cases from the suite will run. (Constructed/destroyed in
/// Defines an empty ``struct`` whose ``setup()`` and ``teardown()`` member functions will
/// be invoked once before any cases in the suite are run and once after no more cases from
/// the suite will run, respectively. (These correspond
// clang-format off
/// :gtest:`SetUpTestSuite/TearDownTestSuite <advanced.html#sharing-resources-between-tests-in-the-same-test-suite>`
/// :gtest:`SetUpTestSuite/TearDownTestSuite. <advanced.html#sharing-resources-between-tests-in-the-same-test-suite>`
// clang-format on
/// respectively.)
/// )
///
/// This should be used if a resource is too expensive to set up in each test case
/// and management of the shared resource might fail. ``SUITE_`` functions have access
/// to :c:macro:`EXPECT_`, so any failures in setup or teardown can be exposed as
/// a failure of the test suite.
///
/// .. code-block::
///
/// ServerHandle server_handle;
///
/// SUITE_ {
/// SuiteState() {
/// void setup() {
/// server_handle.connect_to("localhost", 7890);
/// EXPECT_(server_handle.is_connected());
/// }
/// ~SuiteState() {
/// void teardown() {
/// server_handle.orderly_shutdown();
/// EXPECT_(not server_handle.is_connected());
/// }
/// ServerHandle server_handle;
/// };
///
/// This may be omitted, in which case no state will be shared.
/// If it is provided it must precede all :expr:`TEST_(...)`
/// If it is provided it must precede all :c:macro:`TEST_`
/// definitions (this is checked at runtime).
///
/// A pointer to the constructed state ``struct`` is accessible
/// in test bodies by calling :expr:`suite_state()`.
///
/// .. code-block::
///
/// TEST_(address) {
/// EXPECT_(suite_state()->server_handle.address() == "localhost:7890");
/// }
#define SUITE_ \
namespace SUITE_NAME { \
struct SuiteState; \
} \
struct SUITE_NAME::SuiteState : DontTerminateIfDestructionThrows
#define SUITE_ \
struct SuiteState; \
SuiteState suite_(void *); \
struct SuiteState
21 changes: 17 additions & 4 deletions project.test.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -196,12 +196,12 @@ glob benchmark:
unit testing:
- write: basics.cxx
contents: |
#include <coroutine>
#include <string>
import test_;
SUITE_ { std::string yo = "yo"; };
TEST_(DISABLED_empty) {}
TEST_(DISABLED_failing) {
EXPECT_(false);
}
TEST_(basic) {
int three = 3, five = 5;
EXPECT_(three != five);
Expand All @@ -213,7 +213,6 @@ unit testing:
int *ptr = &three;
if (not EXPECT_(ptr != nullptr)) return;
EXPECT_(*ptr == three);
EXPECT_(suite_state()->yo == "yo");
EXPECT_("hello world" >>= HasSubstr("llo"));
}
TEST_(custom_matcher) {
Expand All @@ -237,6 +236,20 @@ unit testing:
- cmake --install .build --prefix ../usr --config Debug
- exists: .build/Debug/test_.basics
- does not exist: ../usr/bin/test_.basics
- write: suite_management.cxx
contents: |
#include <string>
import test_;
std::string str;
SUITE_ {
void setup() { str = "yo"; }
void teardown() { str = ""; }
};
TEST_(str_is_yo) { EXPECT_(str == "yo"); }
- cmake --build .build --config Debug
- ctest --test-dir .build --output-on-failure -C Debug


disabling unit testing:
Expand Down
4 changes: 0 additions & 4 deletions testing.rst
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,6 @@ specify resources which should be shared across the suite.

module test_;

SUITE_ { std::string yo = "yo"; };

TEST_(basic) {
int three = 3, five = 5;
EXPECT_(three == five);
Expand All @@ -47,8 +45,6 @@ specify resources which should be shared across the suite.
// A lambda can hook expectation failure and add more context
};

EXPECT_(suite_state()->yo == "yo");

// GMock's matchers are available
EXPECT_("hello world" >>= HasSubstr("llo"));
}
Expand Down

0 comments on commit 360663f

Please sign in to comment.