Skip to content

Commit 1178772

Browse files
kpschoedelpull[bot]
authored andcommitted
Add unit tests for SystemTimer.h (#12742)
#### Problem Only the higher-level `System::Layer` timer operations had tests; the utility classes in `system/SystemTimer.h` had no unit tests, and a recent refactor (#12628) introduced a bug that a proper unit test would have caught. Fixes #12729 Add unit tests for SystemTimer.h #### Change overview What's in this PR - Add tests covering `TimerData`, `TimeList`, and `TimerPool`. - Changed these helpers to take a `Timestamp` rather than a `Timeout`. - Fixed `TimerList::Remove(Node*)` to allow an empty list or null argument (matching its description). #### Testing Quis custodiet ipsos custodes?
1 parent 15f5a8e commit 1178772

File tree

5 files changed

+185
-29
lines changed

5 files changed

+185
-29
lines changed

src/system/SystemLayerImplLwIP.cpp

+2-2
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ CHIP_ERROR LayerImplLwIP::StartTimer(Clock::Timeout delay, TimerCompleteCallback
5656

5757
CancelTimer(onComplete, appState);
5858

59-
TimerList::Node * timer = mTimerPool.Create(*this, delay, onComplete, appState);
59+
TimerList::Node * timer = mTimerPool.Create(*this, SystemClock().GetMonotonicTimestamp() + delay, onComplete, appState);
6060
VerifyOrReturnError(timer != nullptr, CHIP_ERROR_NO_MEMORY);
6161

6262
if (mTimerList.Add(timer) == timer)
@@ -87,7 +87,7 @@ CHIP_ERROR LayerImplLwIP::ScheduleWork(TimerCompleteCallback onComplete, void *
8787
{
8888
VerifyOrReturnError(mLayerState.IsInitialized(), CHIP_ERROR_INCORRECT_STATE);
8989

90-
TimerList::Node * timer = mTimerPool.Create(*this, System::Clock::kZero, onComplete, appState);
90+
TimerList::Node * timer = mTimerPool.Create(*this, SystemClock().GetMonotonicTimestamp(), onComplete, appState);
9191
VerifyOrReturnError(timer != nullptr, CHIP_ERROR_NO_MEMORY);
9292

9393
return ScheduleLambda([this, timer] { this->mTimerPool.Invoke(timer); });

src/system/SystemLayerImplSelect.cpp

+2-2
Original file line numberDiff line numberDiff line change
@@ -124,7 +124,7 @@ CHIP_ERROR LayerImplSelect::StartTimer(Clock::Timeout delay, TimerCompleteCallba
124124

125125
CancelTimer(onComplete, appState);
126126

127-
TimerList::Node * timer = mTimerPool.Create(*this, delay, onComplete, appState);
127+
TimerList::Node * timer = mTimerPool.Create(*this, SystemClock().GetMonotonicTimestamp() + delay, onComplete, appState);
128128
VerifyOrReturnError(timer != nullptr, CHIP_ERROR_NO_MEMORY);
129129

130130
#if CHIP_SYSTEM_CONFIG_USE_DISPATCH
@@ -186,7 +186,7 @@ CHIP_ERROR LayerImplSelect::ScheduleWork(TimerCompleteCallback onComplete, void
186186

187187
CancelTimer(onComplete, appState);
188188

189-
TimerList::Node * timer = mTimerPool.Create(*this, Clock::kZero, onComplete, appState);
189+
TimerList::Node * timer = mTimerPool.Create(*this, SystemClock().GetMonotonicTimestamp(), onComplete, appState);
190190
VerifyOrReturnError(timer != nullptr, CHIP_ERROR_NO_MEMORY);
191191

192192
#if CHIP_SYSTEM_CONFIG_USE_DISPATCH

src/system/SystemTimer.cpp

+18-17
Original file line numberDiff line numberDiff line change
@@ -67,29 +67,30 @@ TimerList::Node * TimerList::Add(TimerList::Node * add)
6767

6868
TimerList::Node * TimerList::Remove(TimerList::Node * remove)
6969
{
70-
VerifyOrDie(mEarliestTimer != nullptr);
71-
72-
if (remove == mEarliestTimer)
73-
{
74-
mEarliestTimer = remove->mNextTimer;
75-
}
76-
else
70+
if (mEarliestTimer != nullptr && remove != nullptr)
7771
{
78-
TimerList::Node * lTimer = mEarliestTimer;
79-
80-
while (lTimer->mNextTimer)
72+
if (remove == mEarliestTimer)
73+
{
74+
mEarliestTimer = remove->mNextTimer;
75+
}
76+
else
8177
{
82-
if (remove == lTimer->mNextTimer)
78+
TimerList::Node * lTimer = mEarliestTimer;
79+
80+
while (lTimer->mNextTimer)
8381
{
84-
lTimer->mNextTimer = remove->mNextTimer;
85-
break;
86-
}
82+
if (remove == lTimer->mNextTimer)
83+
{
84+
lTimer->mNextTimer = remove->mNextTimer;
85+
break;
86+
}
8787

88-
lTimer = lTimer->mNextTimer;
88+
lTimer = lTimer->mNextTimer;
89+
}
8990
}
90-
}
9191

92-
remove->mNextTimer = nullptr;
92+
remove->mNextTimer = nullptr;
93+
}
9394
return mEarliestTimer;
9495
}
9596

src/system/SystemTimer.h

+10-8
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ namespace chip {
4444
namespace System {
4545

4646
class Layer;
47+
class TestTimer;
4748

4849
/**
4950
* Basic Timer information: time and callback.
@@ -57,7 +58,7 @@ class DLL_EXPORT TimerData
5758
Callback(Layer & systemLayer, TimerCompleteCallback onComplete, void * appState) :
5859
mSystemLayer(&systemLayer), mOnComplete(onComplete), mAppState(appState)
5960
{}
60-
void Invoke() { mOnComplete(mSystemLayer, mAppState); }
61+
void Invoke() const { mOnComplete(mSystemLayer, mAppState); }
6162
const TimerCompleteCallback & GetOnComplete() const { return mOnComplete; }
6263
void * GetAppState() const { return mAppState; }
6364
Layer * GetSystemLayer() const { return mSystemLayer; }
@@ -68,8 +69,8 @@ class DLL_EXPORT TimerData
6869
void * mAppState;
6970
};
7071

71-
TimerData(Layer & systemLayer, System::Clock::Timeout delay, TimerCompleteCallback onComplete, void * appState) :
72-
mAwakenTime(SystemClock().GetMonotonicTimestamp() + delay), mCallback(systemLayer, onComplete, appState)
72+
TimerData(Layer & systemLayer, System::Clock::Timestamp awakenTime, TimerCompleteCallback onComplete, void * appState) :
73+
mAwakenTime(awakenTime), mCallback(systemLayer, onComplete, appState)
7374
{}
7475
~TimerData() = default;
7576

@@ -106,8 +107,8 @@ class TimerList
106107
class Node : public TimerData
107108
{
108109
public:
109-
Node(Layer & systemLayer, System::Clock::Timeout delay, TimerCompleteCallback onComplete, void * appState) :
110-
TimerData(systemLayer, delay, onComplete, appState), mNextTimer(nullptr)
110+
Node(Layer & systemLayer, System::Clock::Timestamp awakenTime, TimerCompleteCallback onComplete, void * appState) :
111+
TimerData(systemLayer, awakenTime, onComplete, appState), mNextTimer(nullptr)
111112
{}
112113
Node * mNextTimer;
113114
};
@@ -160,7 +161,7 @@ class TimerList
160161
/**
161162
* Test whether there are any timers.
162163
*/
163-
bool Empty() const { return mEarliestTimer != nullptr; }
164+
bool Empty() const { return mEarliestTimer == nullptr; }
164165

165166
/**
166167
* Remove and return all timers that expire before the given time @a t.
@@ -188,9 +189,9 @@ class TimerPool
188189
/**
189190
* Create a new timer from the pool.
190191
*/
191-
Timer * Create(Layer & systemLayer, System::Clock::Timeout delay, TimerCompleteCallback onComplete, void * appState)
192+
Timer * Create(Layer & systemLayer, System::Clock::Timestamp awakenTime, TimerCompleteCallback onComplete, void * appState)
192193
{
193-
Timer * timer = mTimerPool.CreateObject(systemLayer, delay, onComplete, appState);
194+
Timer * timer = mTimerPool.CreateObject(systemLayer, awakenTime, onComplete, appState);
194195
SYSTEM_STATS_INCREMENT(Stats::kSystemLayer_NumTimers);
195196
return timer;
196197
}
@@ -224,6 +225,7 @@ class TimerPool
224225
}
225226

226227
private:
228+
friend class TestTimer;
227229
ObjectPool<Timer, CHIP_SYSTEM_CONFIG_NUM_TIMERS> mTimerPool;
228230
};
229231

src/system/tests/TestSystemTimer.cpp

+153
Original file line numberDiff line numberDiff line change
@@ -168,6 +168,158 @@ static void CheckStarvation(nlTestSuite * inSuite, void * aContext)
168168
ServiceEvents(lSys);
169169
}
170170

171+
// Test the implementation helper classes TimerPool, TimerList, and TimerData.
172+
namespace chip {
173+
namespace System {
174+
class TestTimer
175+
{
176+
public:
177+
static void CheckTimerPool(nlTestSuite * inSuite, void * aContext);
178+
};
179+
} // namespace System
180+
} // namespace chip
181+
182+
void chip::System::TestTimer::CheckTimerPool(nlTestSuite * inSuite, void * aContext)
183+
{
184+
TestContext & testContext = *static_cast<TestContext *>(aContext);
185+
Layer & systemLayer = *testContext.mLayer;
186+
nlTestSuite * const suite = testContext.mTestSuite;
187+
188+
using Timer = TimerList::Node;
189+
struct TestState
190+
{
191+
int count = 0;
192+
static void Increment(Layer * layer, void * state) { ++static_cast<TestState *>(state)->count; }
193+
static void Reset(Layer * layer, void * state) { static_cast<TestState *>(state)->count = 0; }
194+
};
195+
TestState testState;
196+
197+
using namespace Clock::Literals;
198+
struct
199+
{
200+
Clock::Timestamp awakenTime;
201+
TimerCompleteCallback onComplete;
202+
Timer * timer;
203+
} testTimer[] = {
204+
{ 111_ms, TestState::Increment }, // 0
205+
{ 100_ms, TestState::Increment }, // 1
206+
{ 202_ms, TestState::Reset }, // 2
207+
{ 303_ms, TestState::Increment }, // 3
208+
};
209+
210+
TimerPool<Timer> pool;
211+
NL_TEST_ASSERT(suite, pool.mTimerPool.Allocated() == 0);
212+
SYSTEM_STATS_RESET(Stats::kSystemLayer_NumTimers);
213+
214+
// Test TimerPool::Create() and TimerData accessors.
215+
216+
for (auto & timer : testTimer)
217+
{
218+
timer.timer = pool.Create(systemLayer, timer.awakenTime, timer.onComplete, &testState);
219+
}
220+
#if CHIP_SYSTEM_CONFIG_PROVIDE_STATISTICS
221+
NL_TEST_ASSERT(suite, Stats::GetResourcesInUse()[Stats::kSystemLayer_NumTimers] == 4);
222+
#endif // CHIP_SYSTEM_CONFIG_PROVIDE_STATISTICS
223+
224+
for (auto & timer : testTimer)
225+
{
226+
NL_TEST_ASSERT(suite, timer.timer != nullptr);
227+
NL_TEST_ASSERT(suite, timer.timer->AwakenTime() == timer.awakenTime);
228+
NL_TEST_ASSERT(suite, timer.timer->GetCallback().GetOnComplete() == timer.onComplete);
229+
NL_TEST_ASSERT(suite, timer.timer->GetCallback().GetAppState() == &testState);
230+
NL_TEST_ASSERT(suite, timer.timer->GetCallback().GetSystemLayer() == &systemLayer);
231+
}
232+
233+
// Test TimerList operations.
234+
235+
TimerList list;
236+
NL_TEST_ASSERT(suite, list.Remove(nullptr) == nullptr);
237+
NL_TEST_ASSERT(suite, list.Remove(nullptr, nullptr) == nullptr);
238+
NL_TEST_ASSERT(suite, list.PopEarliest() == nullptr);
239+
NL_TEST_ASSERT(suite, list.PopIfEarlier(500_ms) == nullptr);
240+
NL_TEST_ASSERT(suite, list.Earliest() == nullptr);
241+
NL_TEST_ASSERT(suite, list.Empty());
242+
243+
Timer * earliest = list.Add(testTimer[0].timer); // list: () → (0) returns: 0
244+
NL_TEST_ASSERT(suite, earliest == testTimer[0].timer);
245+
NL_TEST_ASSERT(suite, list.PopIfEarlier(10_ms) == nullptr);
246+
NL_TEST_ASSERT(suite, list.Earliest() == testTimer[0].timer);
247+
NL_TEST_ASSERT(suite, !list.Empty());
248+
249+
earliest = list.Add(testTimer[1].timer); // list: (0) → (1 0) returns: 1
250+
NL_TEST_ASSERT(suite, earliest == testTimer[1].timer);
251+
NL_TEST_ASSERT(suite, list.Earliest() == testTimer[1].timer);
252+
253+
earliest = list.Add(testTimer[2].timer); // list: (1 0) → (1 0 2) returns: 1
254+
NL_TEST_ASSERT(suite, earliest == testTimer[1].timer);
255+
NL_TEST_ASSERT(suite, list.Earliest() == testTimer[1].timer);
256+
257+
earliest = list.Add(testTimer[3].timer); // list: (1 0 2) → (1 0 2 3) returns: 1
258+
NL_TEST_ASSERT(suite, earliest == testTimer[1].timer);
259+
NL_TEST_ASSERT(suite, list.Earliest() == testTimer[1].timer);
260+
261+
earliest = list.Remove(earliest); // list: (1 0 2 3) → (0 2 3) returns: 0
262+
NL_TEST_ASSERT(suite, earliest == testTimer[0].timer);
263+
NL_TEST_ASSERT(suite, list.Earliest() == testTimer[0].timer);
264+
265+
earliest = list.Remove(TestState::Reset, &testState); // list: (0 2 3) → (0 3) returns: 2
266+
NL_TEST_ASSERT(suite, earliest == testTimer[2].timer);
267+
NL_TEST_ASSERT(suite, list.Earliest() == testTimer[0].timer);
268+
269+
earliest = list.PopEarliest(); // list: (0 3) → (3) returns: 0
270+
NL_TEST_ASSERT(suite, earliest == testTimer[0].timer);
271+
NL_TEST_ASSERT(suite, list.Earliest() == testTimer[3].timer);
272+
273+
earliest = list.PopIfEarlier(10_ms); // list: (3) → (3) returns: nullptr
274+
NL_TEST_ASSERT(suite, earliest == nullptr);
275+
276+
earliest = list.PopIfEarlier(500_ms); // list: (3) → () returns: 3
277+
NL_TEST_ASSERT(suite, earliest == testTimer[3].timer);
278+
NL_TEST_ASSERT(suite, list.Empty());
279+
280+
earliest = list.Add(testTimer[3].timer); // list: () → (3) returns: 3
281+
list.Clear(); // list: (3) → ()
282+
NL_TEST_ASSERT(suite, earliest == testTimer[3].timer);
283+
NL_TEST_ASSERT(suite, list.Empty());
284+
285+
for (auto & timer : testTimer)
286+
{
287+
list.Add(timer.timer);
288+
}
289+
TimerList early = list.ExtractEarlier(200_ms); // list: (1 0 2 3) → (2 3) returns: (1 0)
290+
NL_TEST_ASSERT(suite, list.PopEarliest() == testTimer[2].timer);
291+
NL_TEST_ASSERT(suite, list.PopEarliest() == testTimer[3].timer);
292+
NL_TEST_ASSERT(suite, list.PopEarliest() == nullptr);
293+
NL_TEST_ASSERT(suite, early.PopEarliest() == testTimer[1].timer);
294+
NL_TEST_ASSERT(suite, early.PopEarliest() == testTimer[0].timer);
295+
NL_TEST_ASSERT(suite, early.PopEarliest() == nullptr);
296+
297+
// Test TimerPool::Invoke()
298+
NL_TEST_ASSERT(suite, testState.count == 0);
299+
pool.Invoke(testTimer[0].timer);
300+
testTimer[0].timer = nullptr;
301+
NL_TEST_ASSERT(suite, testState.count == 1);
302+
NL_TEST_ASSERT(suite, pool.mTimerPool.Allocated() == 3);
303+
#if CHIP_SYSTEM_CONFIG_PROVIDE_STATISTICS
304+
NL_TEST_ASSERT(suite, Stats::GetResourcesInUse()[Stats::kSystemLayer_NumTimers] == 3);
305+
#endif // CHIP_SYSTEM_CONFIG_PROVIDE_STATISTICS
306+
307+
// Test TimerPool::Release()
308+
pool.Release(testTimer[1].timer);
309+
testTimer[1].timer = nullptr;
310+
NL_TEST_ASSERT(suite, testState.count == 1);
311+
NL_TEST_ASSERT(suite, pool.mTimerPool.Allocated() == 2);
312+
#if CHIP_SYSTEM_CONFIG_PROVIDE_STATISTICS
313+
NL_TEST_ASSERT(suite, Stats::GetResourcesInUse()[Stats::kSystemLayer_NumTimers] == 2);
314+
#endif // CHIP_SYSTEM_CONFIG_PROVIDE_STATISTICS
315+
316+
pool.ReleaseAll();
317+
NL_TEST_ASSERT(suite, pool.mTimerPool.Allocated() == 0);
318+
#if CHIP_SYSTEM_CONFIG_PROVIDE_STATISTICS
319+
NL_TEST_ASSERT(suite, Stats::GetResourcesInUse()[Stats::kSystemLayer_NumTimers] == 0);
320+
#endif // CHIP_SYSTEM_CONFIG_PROVIDE_STATISTICS
321+
}
322+
171323
// Test Suite
172324

173325
/**
@@ -178,6 +330,7 @@ static const nlTest sTests[] =
178330
{
179331
NL_TEST_DEF("Timer::TestOverflow", CheckOverflow),
180332
NL_TEST_DEF("Timer::TestTimerStarvation", CheckStarvation),
333+
NL_TEST_DEF("Timer::TestTimerPool", chip::System::TestTimer::CheckTimerPool),
181334
NL_TEST_SENTINEL()
182335
};
183336
// clang-format on

0 commit comments

Comments
 (0)