I depend on hardware that may or may not respond. As a consequence I frequently end up writing functions with timeouts. System time is a known source for brittle unit tests so injecting a controlled and stable time seems like a good idea for testing.
I wonder if there are any facilities in std::chrono that help with that. The alternative I see is to write a wrapper around the system time and depend on that adapter.
Here is a minimal example of how a wrapper could look like.
#pragma once
#include <memory>
#include <chrono>
#include <thread>
#include <iostream>
using std::chrono::system_clock;
using std::chrono::milliseconds;
using std::shared_ptr;
using std::make_shared;
class Wrapped_Clock
{
public:
virtual system_clock::time_point Now() { return system_clock::now(); }
virtual void Sleep(milliseconds ms) { std::this_thread::sleep_for(ms); }
};
class Mock_Clock : public Wrapped_Clock
{
private:
system_clock::time_point now;
public:
Mock_Clock() : now(system_clock::now()){}
~Mock_Clock() {}
system_clock::time_point Now() { return now; }
void Sleep(milliseconds ms) { }
};
class CanTimeOut
{
private:
shared_ptr<Wrapped_Clock> sclock;
public:
CanTimeOut(shared_ptr<Wrapped_Clock> sclock = make_shared<Wrapped_Clock>()) : sclock(sclock) {}
~CanTimeOut() {}
milliseconds TimeoutAction(milliseconds maxtime)
{
using std::chrono::duration_cast;
int x = 0;
system_clock::time_point start = sclock->Now();
system_clock::time_point timeout = sclock->Now() + maxtime;
while (timeout > sclock->Now() && x != 2000)
{
sclock->Sleep(milliseconds(1));
++x;
}
milliseconds elapsed = duration_cast<milliseconds>(sclock->Now() - start);
return elapsed;
}
};
#define EXPECT_GE(left, right, test) \
{ if (!(left >= right)) { \
std::cout << #test << " " << "!(" << left << " >= " << right << ")" << std::endl; \
} }
#define EXPECT_EQ(expected, actual, test) \
{ if (!(expected == actual)) { \
std::cout << #test << " " << "!(" << expected << " == " << actual << ")" << std::endl; \
} }
void TestWithSystemClock()
{
CanTimeOut cto;
long long timeout = 1000;
milliseconds actual = cto.TimeoutAction(milliseconds(timeout));
EXPECT_GE(actual.count(), timeout, TestWithSystemClock);
}
void TestWithMockClock()
{
CanTimeOut cto(make_shared<Mock_Clock>());
milliseconds actual = cto.TimeoutAction(milliseconds(1000));
EXPECT_EQ(0, actual.count(), TestWithMockClock);
}
int main()
{
TestWithSystemClock();
TestWithMockClock();
}
How much of this can be replaced with functionality from std::chrone?
Edit 1:
- "What exactly are you testing?"
I am controlling time as a test condition to change the behaviour of method calls that depend on time. The Test illustrates that mocking the time and controlling behaviour as a concept works and shows my understanding of it. The point of the minimal example is to show my understanding of mocking time to make it easier to show the differences to the
std::
facilities. - "spend ~10 words saying what the tests should contrast." The one Test always times out. The other Test shows no passage of time. A third Test that controls an exact and non zero passage of time was not included.
- "Besides, sleep has nothing to do with the clock. It's not a chrono feature." I needed it to ensure that the one test never loops more than a certain amount before timing out, this simulates some action that takes time and can time out. On the other hand I wanted to build in a shortcut so the second test does not waste time waiting. It would be ok to not mock Sleep as well but the test would take 2 seconds. I recognize the point that Sleep is not a chrono feature and therefor misleading.