forked from envoyproxy/envoy
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathtest_time_system.h
134 lines (111 loc) · 5.14 KB
/
test_time_system.h
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
#pragma once
#include "envoy/event/timer.h"
#include "common/common/assert.h"
#include "common/common/thread.h"
#include "test/test_common/global.h"
namespace Envoy {
namespace Event {
class TestTimeSystem;
// Adds sleep() and waitFor() interfaces to Event::TimeSystem.
class TestTimeSystem : public Event::TimeSystem {
public:
~TestTimeSystem() override = default;
/**
* Advances time forward by the specified duration, running any timers
* along the way that have been scheduled to fire.
*
* @param duration The amount of time to sleep.
*/
virtual void sleep(const Duration& duration) PURE;
template <class D> void sleep(const D& duration) {
sleep(std::chrono::duration_cast<Duration>(duration));
}
/**
* Waits for the specified duration to expire, or for a condvar to
* be notified, whichever comes first.
*
* @param mutex A mutex which must be held before calling this function.
* @param condvar The condition to wait on.
* @param duration The maximum amount of time to wait.
* @return Thread::CondVar::WaitStatus whether the condition timed out or not.
*/
virtual Thread::CondVar::WaitStatus
waitFor(Thread::MutexBasicLockable& mutex, Thread::CondVar& condvar,
const Duration& duration) noexcept EXCLUSIVE_LOCKS_REQUIRED(mutex) PURE;
template <class D>
Thread::CondVar::WaitStatus waitFor(Thread::MutexBasicLockable& mutex, Thread::CondVar& condvar,
const D& duration) noexcept EXCLUSIVE_LOCKS_REQUIRED(mutex) {
return waitFor(mutex, condvar, std::chrono::duration_cast<Duration>(duration));
}
};
// There should only be one instance of any time-system resident in a test
// process at once. This helper class is used with Test::Global to help enforce
// that with an ASSERT. Each time-system derivation should have a helper
// implementation which is referenced from a delegate (see
// DelegatingTestTimeSystemBase). In each delegate, a SingletonTimeSystemHelper
// should be instantiated via Test::Global<SingletonTimeSystemHelper>. Only one
// instance of SingletonTimeSystemHelper per process, at a time. When all
// references to the delegates are destructed, the singleton will be destroyed
// as well, so each test-method will get a fresh start.
class SingletonTimeSystemHelper {
public:
SingletonTimeSystemHelper() : time_system_(nullptr) {}
using MakeTimeSystemFn = std::function<std::unique_ptr<TestTimeSystem>()>;
/**
* Returns a singleton time-system, creating a default one of there's not
* one already. This method is thread-safe.
*
* @return the time system.
*/
TestTimeSystem& timeSystem(const MakeTimeSystemFn& make_time_system);
private:
std::unique_ptr<TestTimeSystem> time_system_ GUARDED_BY(mutex_);
Thread::MutexBasicLockable mutex_;
};
// Implements the TestTimeSystem interface, delegating implementation of all
// methods to a TestTimeSystem reference supplied by a timeSystem() method in a
// subclass.
template <class TimeSystemVariant> class DelegatingTestTimeSystemBase : public TestTimeSystem {
public:
void sleep(const Duration& duration) override { timeSystem().sleep(duration); }
Thread::CondVar::WaitStatus
waitFor(Thread::MutexBasicLockable& mutex, Thread::CondVar& condvar,
const Duration& duration) noexcept EXCLUSIVE_LOCKS_REQUIRED(mutex) override {
return timeSystem().waitFor(mutex, condvar, duration);
}
SchedulerPtr createScheduler(Scheduler& base_scheduler) override {
return timeSystem().createScheduler(base_scheduler);
}
SystemTime systemTime() override { return timeSystem().systemTime(); }
MonotonicTime monotonicTime() override { return timeSystem().monotonicTime(); }
TimeSystemVariant& operator*() { return timeSystem(); }
virtual TimeSystemVariant& timeSystem() PURE;
};
// Wraps a concrete time-system in a delegate that ensures there is only one
// time-system of any variant resident in a process at a time. Attempts to
// instantiate multiple instances of the same type of time-system will simply
// reference the same shared delegate, which will be deleted when the last one
// goes out of scope. Attempts to instantiate different types of type-systems
// will result in a RELEASE_ASSERT. See the testcases in
// test_time_system_test.cc to understand the allowable sequences.
template <class TimeSystemVariant>
class DelegatingTestTimeSystem : public DelegatingTestTimeSystemBase<TimeSystemVariant> {
public:
DelegatingTestTimeSystem() : time_system_(initTimeSystem()) {}
TimeSystemVariant& timeSystem() override { return time_system_; }
private:
TimeSystemVariant& initTimeSystem() {
auto make_time_system = []() -> std::unique_ptr<TestTimeSystem> {
return std::make_unique<TimeSystemVariant>();
};
auto time_system = dynamic_cast<TimeSystemVariant*>(&singleton_->timeSystem(make_time_system));
RELEASE_ASSERT(time_system,
"Two different types of time-systems allocated. If deriving from "
"Event::TestUsingSimulatedTime make sure it is the first base class.");
return *time_system;
}
Test::Global<SingletonTimeSystemHelper> singleton_;
TimeSystemVariant& time_system_;
};
} // namespace Event
} // namespace Envoy