Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Change the CI test units to be configurable using defines #106

Merged
merged 10 commits into from
Apr 30, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .github/workflows/macos.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,8 @@ jobs:
ninjaVersion: 1.11.1

- name: Configure CMake
env:
CXXFLAGS: -DNUCLEAR_TEST_TIME_UNIT_DEN=10
run: |
cmake -E make_directory build
cmake -S . -B build \
Expand Down
11 changes: 7 additions & 4 deletions src/extension/network/NUClearNetwork.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -93,8 +93,8 @@ namespace extension {
inline void measure_round_trip(std::chrono::steady_clock::duration time) {

// Make our measurement into a float seconds type
const std::chrono::duration<float, std::ratio<1>> m =
std::chrono::duration_cast<std::chrono::duration<float, std::ratio<1>>>(time);
const std::chrono::duration<float> m =
std::chrono::duration_cast<std::chrono::duration<float>>(time);

// Alias variables
const auto& Q = round_trip_kf.process_noise;
Expand All @@ -111,7 +111,7 @@ namespace extension {

// Put result into our variable
round_trip_time = std::chrono::duration_cast<std::chrono::steady_clock::duration>(
std::chrono::duration<float, std::ratio<1>>(X));
std::chrono::duration<float>(X));
}
};

Expand All @@ -130,7 +130,10 @@ namespace extension {
* @param target who we are sending to (blank means everyone)
* @param reliable if the delivery of the data should be ensured
*/
void send(const uint64_t& hash, const std::vector<uint8_t>& payload, const std::string& target, bool reliable);
void send(const uint64_t& hash,
const std::vector<uint8_t>& payload,
const std::string& target,
bool reliable);

/**
* @brief Set the callback to use when a data packet is completed
Expand Down
46 changes: 21 additions & 25 deletions tests/dsl/emit/Delay.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -23,16 +23,17 @@
#include <catch.hpp>
#include <nuclear>

#include "../../test_util/TestBase.hpp"
#include "test_util/TestBase.hpp"
#include "test_util/TimeUnit.hpp"

// Anonymous namespace to keep everything file local
namespace {

using TimeUnit = test_util::TimeUnit;

/// @brief Events that occur during the test
std::vector<std::string> events; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables)

/// @brief Test units are the time units the test is performed in
using TestUnits = std::chrono::duration<int64_t, std::ratio<1, 20>>;
/// @brief Perform this many different time points for the test
constexpr int test_loops = 5;

Expand All @@ -50,23 +51,23 @@ struct TargetTimeMessage {

struct FinishTest {};

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

// Measure when messages were sent and received and print those values
on<Trigger<DelayedMessage>>().then([](const DelayedMessage& m) {
auto true_delta = std::chrono::duration_cast<TestUnits>(NUClear::clock::now() - m.time);
auto delta = std::chrono::duration_cast<TestUnits>(m.delay);
auto true_delta = test_util::round_to_test_units(NUClear::clock::now() - m.time);
auto delta = test_util::round_to_test_units(m.delay);

// Print the debug message
events.push_back("delayed " + std::to_string(true_delta.count()) + " received "
+ std::to_string(delta.count()));
});

on<Trigger<TargetTimeMessage>>().then([](const TargetTimeMessage& m) {
auto true_delta = std::chrono::duration_cast<TestUnits>(NUClear::clock::now() - m.time);
auto delta = std::chrono::duration_cast<TestUnits>(m.target - m.time);
auto true_delta = test_util::round_to_test_units(NUClear::clock::now() - m.time);
auto delta = test_util::round_to_test_units(m.target - m.time);

// Print the debug message
events.push_back("at_time " + std::to_string(true_delta.count()) + " received "
Expand All @@ -78,25 +79,20 @@ class TestReactor : public test_util::TestBase<TestReactor> {
powerplant.shutdown();
});


on<Startup>().then([this] {
// Get our jump size in milliseconds
const int jump_unit = (TestUnits::period::num * 1000) / TestUnits::period::den;
// Delay with consistent jumps
on<Trigger<Step<1>>>().then([this] {
// Interleave absolute and relative events
for (int i = 0; i < test_loops; ++i) {
auto delay = std::chrono::milliseconds(jump_unit * i);
auto delay = TimeUnit(i * 2);
emit<Scope::DELAY>(std::make_unique<DelayedMessage>(delay), delay);
}

// Target time with consistent jumps that interleave the first set
for (int i = 0; i < test_loops; ++i) {
auto target = NUClear::clock::now() + std::chrono::milliseconds(jump_unit / 2 + jump_unit * i);
auto target = NUClear::clock::now() + TimeUnit((i * 2) + 1);
emit<Scope::DELAY>(std::make_unique<TargetTimeMessage>(target), target);
}

// Emit a shutdown one time unit after
emit<Scope::DELAY>(std::make_unique<FinishTest>(), std::chrono::milliseconds(jump_unit * (test_loops + 1)));
emit<Scope::DELAY>(std::make_unique<FinishTest>(), TimeUnit((test_loops + 1) * 2));
});

on<Startup>().then([this] { emit(std::make_unique<Step<1>>()); });
}
};
} // namespace
Expand All @@ -110,15 +106,15 @@ TEST_CASE("Testing the delay emit", "[api][emit][delay]") {

const std::vector<std::string> expected = {
"delayed 0 received 0",
"at_time 0 received 0",
"delayed 1 received 1",
"at_time 1 received 1",
"delayed 2 received 2",
"at_time 2 received 2",
"delayed 3 received 3",
"at_time 3 received 3",
"delayed 4 received 4",
"at_time 4 received 4",
"at_time 5 received 5",
"delayed 6 received 6",
"at_time 7 received 7",
"delayed 8 received 8",
"at_time 9 received 9",
"Finished",
};

Expand Down
40 changes: 18 additions & 22 deletions tests/individual/TimeTravel.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,12 @@
#include <nuclear>

#include "test_util/TestBase.hpp"
#include "test_util/TimeUnit.hpp"

namespace {

using TestUnits = std::chrono::duration<int64_t, std::ratio<1, 25>>;
using TimeUnit = test_util::TimeUnit;

constexpr int64_t EVENT_1_TIME = 4;
constexpr int64_t EVENT_2_TIME = 8;

Expand Down Expand Up @@ -36,7 +38,7 @@ class TestReactor : public test_util::TestBase<TestReactor, 5000> {
results.events[0] = Results::TimePair{NUClear::clock::now(), std::chrono::steady_clock::now()};
return false;
},
NUClear::clock::time_point(TestUnits(EVENT_1_TIME)),
NUClear::clock::time_point(TimeUnit(EVENT_1_TIME)),
1));

// Emit a chrono task to run at time EVENT_2_TIME, and shutdown
Expand All @@ -46,7 +48,7 @@ class TestReactor : public test_util::TestBase<TestReactor, 5000> {
powerplant.shutdown();
return false;
},
NUClear::clock::time_point(TestUnits(EVENT_2_TIME)),
NUClear::clock::time_point(TimeUnit(EVENT_2_TIME)),
2));

// Time travel!
Expand Down Expand Up @@ -86,7 +88,7 @@ TEST_CASE("Test time travel correctly changes the time for non zero rtf", "[time
const double rtf = GENERATE(0.5, 1.0, 2.0);
CAPTURE(action, adjustment, rtf);
reactor.action = action;
reactor.adjustment = TestUnits(adjustment);
reactor.adjustment = TimeUnit(adjustment);
reactor.rtf = rtf;

// Start the powerplant
Expand All @@ -110,32 +112,26 @@ TEST_CASE("Test time travel correctly changes the time for non zero rtf", "[time
default: throw std::runtime_error("Unknown action");
}

std::array<TestUnits, 2> expected_nuclear = {TestUnits(expected[0]), TestUnits(expected[1])};
std::array<TestUnits, 2> expected_steady = {TestUnits(std::lround(double(expected[0]) / rtf)),
TestUnits(std::lround(double(expected[1]) / rtf))};
std::array<TimeUnit, 2> expected_nuclear = {TimeUnit(expected[0]), TimeUnit(expected[1])};
std::array<TimeUnit, 2> expected_steady = {TimeUnit(std::lround(double(expected[0]) / rtf)),
TimeUnit(std::lround(double(expected[1]) / rtf))};

const auto& r = reactor.results;
const auto& n_start = reactor.results.start.nuclear;
const auto& s_start = reactor.results.start.steady;

auto round_to_test_units = [](const auto& duration) {
const double d = std::chrono::duration_cast<std::chrono::duration<double>>(duration).count();
const double t = (TestUnits::period::den * d) / TestUnits::period::num;
return TestUnits(std::lround(t));
};

std::array<TestUnits, 2> actual_nuclear = {
round_to_test_units(r.events[0].nuclear - n_start),
round_to_test_units(r.events[1].nuclear - n_start),
std::array<TimeUnit, 2> actual_nuclear = {
test_util::round_to_test_units(r.events[0].nuclear - n_start),
test_util::round_to_test_units(r.events[1].nuclear - n_start),
};
std::array<TestUnits, 2> actual_steady = {
round_to_test_units(r.events[0].steady - s_start),
round_to_test_units(r.events[1].steady - s_start),
std::array<TimeUnit, 2> actual_steady = {
test_util::round_to_test_units(r.events[0].steady - s_start),
test_util::round_to_test_units(r.events[1].steady - s_start),
};

const TestUnits actual_adjustment(round_to_test_units(r.start.nuclear - r.zero.nuclear));
const TestUnits expected_adjustment(std::min(adjustment, action == Action::NEAREST ? EVENT_1_TIME : adjustment));
CHECK(round_to_test_units(r.zero.nuclear.time_since_epoch()) == TestUnits(0));
const TimeUnit actual_adjustment(test_util::round_to_test_units(r.start.nuclear - r.zero.nuclear));
const TimeUnit expected_adjustment(std::min(adjustment, action == Action::NEAREST ? EVENT_1_TIME : adjustment));
CHECK(test_util::round_to_test_units(r.zero.nuclear.time_since_epoch()) == TimeUnit(0));
CHECK(expected_nuclear[0] == actual_nuclear[0]);
CHECK(expected_nuclear[1] == actual_nuclear[1]);
CHECK(expected_steady[0] == actual_steady[0]);
Expand Down
65 changes: 65 additions & 0 deletions tests/test_util/TimeUnit.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
/*
* MIT License
*
* Copyright (c) 2023 NUClear Contributors
*
* This file is part of the NUClear codebase.
* See https://github.com/Fastcode/NUClear for further info.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated
* documentation files (the "Software"), to deal in the Software without restriction, including without limitation the
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all copies or substantial portions of the
* Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
* WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
* OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/

#ifndef TEST_UTIL_TIME_UNIT_HPP
#define TEST_UTIL_TIME_UNIT_HPP

#ifndef NUCLEAR_TEST_TIME_UNIT_NUM
// NOLINTNEXTLINE(cppcoreguidelines-macro-usage)
#define NUCLEAR_TEST_TIME_UNIT_NUM 1
#endif
#ifndef NUCLEAR_TEST_TIME_UNIT_DEN
// NOLINTNEXTLINE(cppcoreguidelines-macro-usage)
#define NUCLEAR_TEST_TIME_UNIT_DEN 20
#endif

#include <chrono>

namespace test_util {

/**
* Units that time based tests should use to measure time.
* This can be changed so that slower systems (such as CI) can run the tests with a larger time unit since they can be
* slow and fail.
* To change from the default define NUCLEAR_TEST_TIME_UNIT_NUM and NUCLEAR_TEST_TIME_UNIT_DEN to the desired time unit.
*/
using TimeUnit = std::chrono::duration<int64_t, std::ratio<NUCLEAR_TEST_TIME_UNIT_NUM, NUCLEAR_TEST_TIME_UNIT_DEN>>;

/**
* Rounds the given duration to the nearest TimeUnit.
*
* @tparam T The type of the duration.
*
* @param duration The duration to be rounded.
*
* @return The rounded duration in TimeUnit.
*/
template <typename T>
TimeUnit round_to_test_units(const T& duration) {
const double d = std::chrono::duration_cast<std::chrono::duration<double>>(duration).count();
const double t = (TimeUnit::period::den * d) / TimeUnit::period::num;
return TimeUnit(std::lround(t));
}

} // namespace test_util

#endif // TEST_UTIL_TIME_UNIT_HPP
Loading