Skip to content

Commit fe14610

Browse files
committed
Refactor googletest helpers: board-aware I2C begin, remove TP param, add SPI test base
1 parent 6c56736 commit fe14610

2 files changed

Lines changed: 201 additions & 135 deletions

File tree

src/googletest/test_helper.hpp

Lines changed: 89 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -5,81 +5,120 @@
55
*/
66
/*!
77
@file test_helper.hpp
8-
@brief Helper for testing UnitComponent
8+
@brief Helper for testing UnitComponent periodic measurements
99
@note Depends on GoogleTest
1010
*/
1111
#ifndef M5_UNIT_COMPONENT_GOOGLETEST_HELPER_HPP
1212
#define M5_UNIT_COMPONENT_GOOGLETEST_HELPER_HPP
1313

1414
#include <M5Utility.hpp>
15-
#include <thread>
16-
#include <cassert>
15+
#include <algorithm>
16+
#include <cstdint>
17+
#include <numeric>
18+
#include <vector>
1719

1820
namespace m5 {
1921
namespace unit {
2022
namespace googletest {
2123

24+
/*!
25+
@struct PeriodicMeasurementResult
26+
@brief Result of periodic measurement data collection with statistical analysis
27+
*/
28+
struct PeriodicMeasurementResult {
29+
std::vector<uint32_t> intervals; //!< All inter-update intervals (ms)
30+
uint32_t update_count{}; //!< Number of successful updates
31+
uint32_t expected_interval{}; //!< Expected interval from unit->interval()
32+
bool timed_out{}; //!< True if timeout before collecting all samples
33+
34+
//! @brief Average of intervals (ms)
35+
uint32_t average() const
36+
{
37+
if (intervals.empty()) {
38+
return 0;
39+
}
40+
uint64_t sum = std::accumulate(intervals.begin(), intervals.end(), uint64_t{0});
41+
return static_cast<uint32_t>(sum / intervals.size());
42+
}
43+
44+
//! @brief Median of intervals (ms), robust to outliers
45+
uint32_t median() const
46+
{
47+
if (intervals.empty()) {
48+
return 0;
49+
}
50+
auto sorted = intervals;
51+
std::sort(sorted.begin(), sorted.end());
52+
auto n = sorted.size();
53+
return (n % 2) ? sorted[n / 2] : (sorted[n / 2 - 1] + sorted[n / 2]) / 2;
54+
}
55+
56+
//! @brief Minimum interval (ms)
57+
uint32_t min_interval() const
58+
{
59+
if (intervals.empty()) {
60+
return 0;
61+
}
62+
return *std::min_element(intervals.begin(), intervals.end());
63+
}
64+
65+
//! @brief Maximum interval (ms)
66+
uint32_t max_interval() const
67+
{
68+
if (intervals.empty()) {
69+
return 0;
70+
}
71+
return *std::max_element(intervals.begin(), intervals.end());
72+
}
73+
};
74+
75+
/*!
76+
@brief Collect periodic measurement data from a unit
77+
@tparam U m5::unit::Component-derived class
78+
@param unit Unit instance to collect measurements from
79+
@param times Number of update cycles to collect
80+
@param timeout_duration Timeout in ms (0 = auto-calculate as interval * (times + 1))
81+
@param callback Optional callback invoked after each successful update
82+
@return PeriodicMeasurementResult containing collected intervals and statistics
83+
*/
2284
template <class U>
23-
uint32_t test_periodic_measurement(U* unit, const uint32_t times, const uint32_t tolerance,
24-
const uint32_t timeout_duration, void (*callback)(U*), const bool skip_after_test)
85+
PeriodicMeasurementResult collect_periodic_measurements(U* unit, const uint32_t times = 8,
86+
const uint32_t timeout_duration = 0,
87+
void (*callback)(U*) = nullptr)
2588
{
2689
static_assert(std::is_base_of<m5::unit::Component, U>::value, "U must be derived from Component");
2790

28-
auto interval = unit->interval();
29-
decltype(interval) avg{}, avgCnt{};
30-
uint32_t cnt{times};
31-
auto prev = unit->updatedMillis();
32-
auto timeout_at = m5::utility::millis() + timeout_duration;
33-
while (cnt && m5::utility::millis() <= timeout_at) {
91+
PeriodicMeasurementResult result;
92+
result.expected_interval = unit->interval();
93+
if (times > 1) {
94+
result.intervals.reserve(times - 1);
95+
}
96+
97+
auto actual_timeout = timeout_duration ? timeout_duration : result.expected_interval * (times + 1);
98+
auto timeout_at = m5::utility::millis() + actual_timeout;
99+
uint32_t remaining = times;
100+
decltype(unit->updatedMillis()) prev{};
101+
bool first_update = true;
102+
103+
while (remaining && m5::utility::millis() <= timeout_at) {
34104
unit->update();
35105
if (unit->updated()) {
36-
--cnt;
106+
++result.update_count;
107+
--remaining;
37108
auto um = unit->updatedMillis();
38-
if (prev) {
39-
auto duration = um - prev;
40-
++avgCnt;
41-
avg += duration;
42-
// M5_LOGI("dur:%ld", duration);
43-
// EXPECT_LE(duration, interval + 1);
109+
if (!first_update) {
110+
result.intervals.push_back(um - prev);
44111
}
45-
prev = um;
112+
first_update = false;
113+
prev = um;
46114
if (callback) {
47115
callback(unit);
48116
}
49117
}
50-
std::this_thread::yield();
51-
}
52-
53-
// M5_LOGI("AVG:%u avgCnt:%u", avg, avgCnt);
54-
55-
if (!skip_after_test) {
56-
EXPECT_EQ(cnt, 0U);
57-
EXPECT_EQ(avgCnt, times - 1);
58-
if (avgCnt) {
59-
avg /= avgCnt;
60-
EXPECT_LE(avg, decltype(interval)(interval + tolerance));
61-
}
62-
return avg;
63118
}
64-
return 0U;
65-
}
66119

67-
template <class U>
68-
uint32_t test_periodic_measurement(U* unit, const uint32_t times = 8, const uint32_t tolerance = 1,
69-
void (*callback)(U*) = nullptr, const bool skip_after_test = false)
70-
{
71-
static_assert(std::is_base_of<m5::unit::Component, U>::value, "U must be derived from Component");
72-
auto timeout_duration = (unit->interval() * 2) * times;
73-
return test_periodic_measurement(unit, times, tolerance, timeout_duration, callback, skip_after_test);
74-
}
75-
76-
template <class U>
77-
uint32_t test_periodic_measurement(U* unit, const uint32_t times = 8, void (*callback)(U*) = nullptr,
78-
const bool skip_after_test = false)
79-
{
80-
static_assert(std::is_base_of<m5::unit::Component, U>::value, "U must be derived from Component");
81-
auto timeout_duration = (unit->interval() * 2) * times;
82-
return test_periodic_measurement(unit, times, 1, timeout_duration, callback, skip_after_test);
120+
result.timed_out = (remaining > 0);
121+
return result;
83122
}
84123

85124
} // namespace googletest

0 commit comments

Comments
 (0)