Skip to content

Commit

Permalink
Make tests pass in their timeout, and don't use watchdog as for tests…
Browse files Browse the repository at this point in the history
… like TimeTravel where we manipulate the clock it won't work.
  • Loading branch information
TrentHouliston committed Aug 18, 2024
1 parent aa44740 commit 1f6ec75
Show file tree
Hide file tree
Showing 8 changed files with 51 additions and 16 deletions.
42 changes: 37 additions & 5 deletions tests/test_util/TestBase.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@

namespace test_util {

template <typename BaseClass, int timeout = 1000>
template <typename BaseClass>
class TestBase : public NUClear::Reactor {
public:
/**
Expand All @@ -46,21 +46,53 @@ class TestBase : public NUClear::Reactor {
template <int i>
struct Step {};

explicit TestBase(std::unique_ptr<NUClear::Environment> environment, const bool& shutdown_on_idle = true)
/**
* Emit this struct to fail the test
*/
struct Fail {
explicit Fail(std::string message) : message(std::move(message)) {}
std::string message;
};

explicit TestBase(std::unique_ptr<NUClear::Environment> environment,
const bool& shutdown_on_idle = true,
const std::chrono::milliseconds& timeout = std::chrono::milliseconds(1000))
: Reactor(std::move(environment)) {

// Shutdown if the system is idle
if (shutdown_on_idle) {
on<Idle<>>().then([this] { powerplant.shutdown(); });
}

on<Shutdown>().then([this] {
std::lock_guard<std::mutex> lock(timeout_mutex);
clean_shutdown = true;
timeout_cv.notify_all();
});

// Timeout if the test doesn't complete in time
on<Watchdog<BaseClass, timeout, std::chrono::milliseconds>, MainThread>().then([this] {
powerplant.shutdown();
INFO("Test timed out");
// Typically we would use a watchdog, however it is subject to Time Travel
// So instead spawn a thread that will wait for the timeout and then fail the test and shut down
on<Always>().then([this, timeout] {
std::unique_lock<std::mutex> lock(timeout_mutex);
timeout_cv.wait_for(lock, timeout);
if (!clean_shutdown) {
powerplant.shutdown();
emit<Scope::DIRECT>(std::make_unique<Fail>("Test timed out"));
}
});

on<Trigger<Fail>, MainThread>().then([this](const Fail& f) {
INFO(f.message);
CHECK(false);
powerplant.shutdown();
});
}

private:
std::mutex timeout_mutex;
std::condition_variable timeout_cv;
bool clean_shutdown = false;
};

} // namespace test_util
Expand Down
2 changes: 1 addition & 1 deletion tests/tests/api/TimeTravelFrozen.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ constexpr std::chrono::milliseconds SHUTDOWN_TIME = std::chrono::milliseconds(12

struct WaitForShutdown {};

class TestReactor : public test_util::TestBase<TestReactor, 5000> {
class TestReactor : public test_util::TestBase<TestReactor> {
public:
TestReactor(std::unique_ptr<NUClear::Environment> environment) : TestBase(std::move(environment), false) {

Expand Down
5 changes: 3 additions & 2 deletions tests/tests/dsl/Every.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -33,10 +33,11 @@ std::vector<NUClear::clock::time_point> every_times; // NOLINT(cppcoreguideli
std::vector<NUClear::clock::time_point> per_times; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables)
std::vector<NUClear::clock::time_point> dynamic_times; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables)

class TestReactor : public test_util::TestBase<TestReactor, 20000> {
class TestReactor : public test_util::TestBase<TestReactor> {

public:
TestReactor(std::unique_ptr<NUClear::Environment> environment) : TestBase(std::move(environment), false) {
TestReactor(std::unique_ptr<NUClear::Environment> environment)
: TestBase(std::move(environment), false, std::chrono::milliseconds(10000)) {

// Trigger on 3 different types of every
on<Every<1000, Per<std::chrono::seconds>>>().then([]() { every_times.push_back(NUClear::clock::now()); });
Expand Down
2 changes: 1 addition & 1 deletion tests/tests/dsl/Idle.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ struct SimpleMessage {

constexpr int time_step = 50;

class TestReactor : public test_util::TestBase<TestReactor, 10000> {
class TestReactor : public test_util::TestBase<TestReactor> {
public:
template <int N>
struct CustomPool {
Expand Down
2 changes: 1 addition & 1 deletion tests/tests/dsl/IdleSync.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ namespace {
/// A vector of events that have happened
std::vector<std::string> events; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables)

class TestReactor : public test_util::TestBase<TestReactor, 1000> {
class TestReactor : public test_util::TestBase<TestReactor> {
public:
TestReactor(std::unique_ptr<NUClear::Environment> environment) : TestBase(std::move(environment), false) {

Expand Down
5 changes: 3 additions & 2 deletions tests/tests/dsl/TCP.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ struct TestConnection {

struct Finished {};

class TestReactor : public test_util::TestBase<TestReactor, 2000> {
class TestReactor : public test_util::TestBase<TestReactor> {
public:
void handle_data(const std::string& name, const IO::Event& event) {
// We have data to read
Expand All @@ -78,7 +78,8 @@ class TestReactor : public test_util::TestBase<TestReactor, 2000> {
}
}

TestReactor(std::unique_ptr<NUClear::Environment> environment) : TestBase(std::move(environment), false) {
TestReactor(std::unique_ptr<NUClear::Environment> environment)
: TestBase(std::move(environment), false, std::chrono::milliseconds(2000)) {

for (const auto& t : active_tests) {
switch (t) {
Expand Down
4 changes: 2 additions & 2 deletions tests/tests/dsl/Watchdog.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -35,10 +35,10 @@ std::vector<std::string> events; // NOLINT(cppcoreguidelines-avoid-non-const-gl
template <int I>
struct Flag {};

class TestReactor : public test_util::TestBase<TestReactor, 10000> {
class TestReactor : public test_util::TestBase<TestReactor> {
public:
TestReactor(std::unique_ptr<NUClear::Environment> environment)
: TestBase(std::move(environment), false), start(NUClear::clock::now()) {
: TestBase(std::move(environment), false, std::chrono::milliseconds(10000)), start(NUClear::clock::now()) {

on<Watchdog<Flag<1>, 50, std::chrono::milliseconds>>().then([this] {
events.push_back("Watchdog 1 triggered @ " + floored_time());
Expand Down
5 changes: 3 additions & 2 deletions tests/tests/dsl/emit/Delay.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -51,9 +51,10 @@ struct TargetTimeMessage {

struct FinishTest {};

class TestReactor : public test_util::TestBase<TestReactor, 2000> {
class TestReactor : public test_util::TestBase<TestReactor> {
public:
TestReactor(std::unique_ptr<NUClear::Environment> environment) : TestBase(std::move(environment), false) {
TestReactor(std::unique_ptr<NUClear::Environment> environment)
: TestBase(std::move(environment), false, std::chrono::milliseconds(2000)) {

// Measure when messages were sent and received and print those values
on<Trigger<DelayedMessage>>().then([](const DelayedMessage& m) {
Expand Down

0 comments on commit 1f6ec75

Please sign in to comment.