blob: 96eeb5cb82a0e51e6927d747752bff474f8d6eda [file] [log] [blame]
John Park33858a32018-09-28 23:05:48 -07001#include "aos/util/phased_loop.h"
Brian Silvermandcaa3f72015-11-29 05:32:08 +00002
3#include "gtest/gtest.h"
4
Brian Silverman8babd8f2020-06-23 16:38:50 -07005#include "aos/time/time.h"
Brian Silvermandcaa3f72015-11-29 05:32:08 +00006
7namespace aos {
8namespace time {
9namespace testing {
10
Austin Schuh8aec1ed2016-05-01 13:29:20 -070011using ::std::chrono::milliseconds;
Brian Silverman8babd8f2020-06-23 16:38:50 -070012using ::std::chrono::nanoseconds;
Austin Schuh8aec1ed2016-05-01 13:29:20 -070013
Alex Perrycb7da4b2019-08-28 19:35:56 -070014typedef ::testing::Test PhasedLoopTest;
Brian Silvermandcaa3f72015-11-29 05:32:08 +000015typedef PhasedLoopTest PhasedLoopDeathTest;
16
Austin Schuh8aec1ed2016-05-01 13:29:20 -070017monotonic_clock::time_point InMs(int ms) {
18 return monotonic_clock::time_point(::std::chrono::milliseconds(ms));
19}
20
Brian Silvermandcaa3f72015-11-29 05:32:08 +000021TEST_F(PhasedLoopTest, Reset) {
22 {
Austin Schuhd32b3622019-06-23 18:49:06 -070023 PhasedLoop loop(milliseconds(100), monotonic_clock::epoch(),
24 milliseconds(0));
Brian Silvermandcaa3f72015-11-29 05:32:08 +000025
Austin Schuh8aec1ed2016-05-01 13:29:20 -070026 loop.Reset(monotonic_clock::epoch());
27 EXPECT_EQ(InMs(0), loop.sleep_time());
28 EXPECT_EQ(1, loop.Iterate(monotonic_clock::epoch()));
29 EXPECT_EQ(InMs(100), loop.sleep_time());
Brian Silvermandcaa3f72015-11-29 05:32:08 +000030
Austin Schuh8aec1ed2016-05-01 13:29:20 -070031 loop.Reset(InMs(99));
32 EXPECT_EQ(InMs(0), loop.sleep_time());
33 EXPECT_EQ(1, loop.Iterate(InMs(99)));
34 EXPECT_EQ(InMs(100), loop.sleep_time());
Brian Silvermandcaa3f72015-11-29 05:32:08 +000035
Austin Schuh8aec1ed2016-05-01 13:29:20 -070036 loop.Reset(InMs(100));
37 EXPECT_EQ(InMs(100), loop.sleep_time());
38 EXPECT_EQ(1, loop.Iterate(InMs(199)));
39 EXPECT_EQ(InMs(200), loop.sleep_time());
Brian Silvermandcaa3f72015-11-29 05:32:08 +000040
Austin Schuh8aec1ed2016-05-01 13:29:20 -070041 loop.Reset(InMs(101));
42 EXPECT_EQ(InMs(100), loop.sleep_time());
43 EXPECT_EQ(1, loop.Iterate(InMs(101)));
44 EXPECT_EQ(InMs(200), loop.sleep_time());
Brian Silvermandcaa3f72015-11-29 05:32:08 +000045 }
46 {
Austin Schuhd32b3622019-06-23 18:49:06 -070047 PhasedLoop loop(milliseconds(100), monotonic_clock::epoch(),
48 milliseconds(1));
Austin Schuh8aec1ed2016-05-01 13:29:20 -070049 loop.Reset(monotonic_clock::epoch());
50 EXPECT_EQ(InMs(-99), loop.sleep_time());
51 EXPECT_EQ(1, loop.Iterate(monotonic_clock::epoch()));
52 EXPECT_EQ(InMs(1), loop.sleep_time());
Brian Silvermandcaa3f72015-11-29 05:32:08 +000053 }
54 {
Austin Schuhd32b3622019-06-23 18:49:06 -070055 PhasedLoop loop(milliseconds(100), monotonic_clock::epoch(),
56 milliseconds(99));
Brian Silvermandcaa3f72015-11-29 05:32:08 +000057
Austin Schuh8aec1ed2016-05-01 13:29:20 -070058 loop.Reset(monotonic_clock::epoch());
59 EXPECT_EQ(InMs(-1), loop.sleep_time());
60 EXPECT_EQ(1, loop.Iterate(monotonic_clock::epoch()));
61 EXPECT_EQ(InMs(99), loop.sleep_time());
Brian Silvermandcaa3f72015-11-29 05:32:08 +000062
Austin Schuh8aec1ed2016-05-01 13:29:20 -070063 loop.Reset(InMs(98));
64 EXPECT_EQ(InMs(-1), loop.sleep_time());
65 EXPECT_EQ(1, loop.Iterate(InMs(98)));
66 EXPECT_EQ(InMs(99), loop.sleep_time());
Brian Silvermandcaa3f72015-11-29 05:32:08 +000067
Austin Schuh8aec1ed2016-05-01 13:29:20 -070068 loop.Reset(InMs(99));
69 EXPECT_EQ(InMs(99), loop.sleep_time());
70 EXPECT_EQ(1, loop.Iterate(InMs(99)));
71 EXPECT_EQ(InMs(199), loop.sleep_time());
Brian Silvermandcaa3f72015-11-29 05:32:08 +000072
Austin Schuh8aec1ed2016-05-01 13:29:20 -070073 loop.Reset(InMs(100));
74 EXPECT_EQ(InMs(99), loop.sleep_time());
75 EXPECT_EQ(1, loop.Iterate(InMs(100)));
76 EXPECT_EQ(InMs(199), loop.sleep_time());
Brian Silvermandcaa3f72015-11-29 05:32:08 +000077 }
78}
79
80TEST_F(PhasedLoopTest, Iterate) {
81 {
Austin Schuhd32b3622019-06-23 18:49:06 -070082 PhasedLoop loop(milliseconds(100), monotonic_clock::epoch(),
83 milliseconds(99));
Austin Schuh8aec1ed2016-05-01 13:29:20 -070084 loop.Reset(monotonic_clock::epoch());
85 EXPECT_EQ(1, loop.Iterate(monotonic_clock::epoch()));
86 EXPECT_EQ(InMs(99), loop.sleep_time());
87 EXPECT_EQ(1, loop.Iterate(InMs(100)));
88 EXPECT_EQ(InMs(199), loop.sleep_time());
89 EXPECT_EQ(0, loop.Iterate(InMs(100)));
90 EXPECT_EQ(InMs(199), loop.sleep_time());
91 EXPECT_EQ(0, loop.Iterate(InMs(101)));
92 EXPECT_EQ(InMs(199), loop.sleep_time());
93 EXPECT_EQ(0, loop.Iterate(InMs(198)));
94 EXPECT_EQ(InMs(199), loop.sleep_time());
95 EXPECT_EQ(1, loop.Iterate(InMs(199)));
96 EXPECT_EQ(InMs(299), loop.sleep_time());
97 EXPECT_EQ(1, loop.Iterate(InMs(300)));
98 EXPECT_EQ(InMs(399), loop.sleep_time());
99 EXPECT_EQ(3, loop.Iterate(InMs(600)));
100 EXPECT_EQ(InMs(699), loop.sleep_time());
Brian Silvermandcaa3f72015-11-29 05:32:08 +0000101 }
102 {
Austin Schuhd32b3622019-06-23 18:49:06 -0700103 PhasedLoop loop(milliseconds(100), monotonic_clock::epoch(),
104 milliseconds(1));
Austin Schuh8aec1ed2016-05-01 13:29:20 -0700105 loop.Reset(monotonic_clock::epoch());
106 EXPECT_EQ(1, loop.Iterate(monotonic_clock::epoch()));
107 EXPECT_EQ(InMs(1), loop.sleep_time());
108 EXPECT_EQ(1, loop.Iterate(InMs(100)));
109 EXPECT_EQ(InMs(101), loop.sleep_time());
110 EXPECT_EQ(0, loop.Iterate(InMs(100)));
111 EXPECT_EQ(InMs(101), loop.sleep_time());
112 EXPECT_EQ(1, loop.Iterate(InMs(103)));
113 EXPECT_EQ(InMs(201), loop.sleep_time());
114 EXPECT_EQ(0, loop.Iterate(InMs(198)));
115 EXPECT_EQ(InMs(201), loop.sleep_time());
116 EXPECT_EQ(0, loop.Iterate(InMs(200)));
117 EXPECT_EQ(InMs(201), loop.sleep_time());
118 EXPECT_EQ(1, loop.Iterate(InMs(201)));
119 EXPECT_EQ(InMs(301), loop.sleep_time());
120 EXPECT_EQ(3, loop.Iterate(InMs(600)));
121 EXPECT_EQ(InMs(601), loop.sleep_time());
Brian Silvermandcaa3f72015-11-29 05:32:08 +0000122 }
123}
124
125// Makes sure that everything works correctly when crossing zero.
126// This seems like a rare case at first, but starting from zero needs to
127// work, which means negatives should too.
128TEST_F(PhasedLoopTest, CrossingZero) {
Austin Schuhd32b3622019-06-23 18:49:06 -0700129 PhasedLoop loop(milliseconds(100), monotonic_clock::epoch(), milliseconds(1));
Austin Schuh8aec1ed2016-05-01 13:29:20 -0700130 loop.Reset(InMs(-1000));
131 EXPECT_EQ(InMs(-1099), loop.sleep_time());
132 EXPECT_EQ(9, loop.Iterate(InMs(-250)));
133 EXPECT_EQ(InMs(-199), loop.sleep_time());
134 EXPECT_EQ(1, loop.Iterate(InMs(-199)));
135 EXPECT_EQ(InMs(-99), loop.sleep_time());
136 EXPECT_EQ(1, loop.Iterate(InMs(-90)));
137 EXPECT_EQ(InMs(1), loop.sleep_time());
138 EXPECT_EQ(0, loop.Iterate(InMs(0)));
139 EXPECT_EQ(InMs(1), loop.sleep_time());
140 EXPECT_EQ(1, loop.Iterate(InMs(1)));
141 EXPECT_EQ(InMs(101), loop.sleep_time());
Brian Silvermandcaa3f72015-11-29 05:32:08 +0000142
Austin Schuh8aec1ed2016-05-01 13:29:20 -0700143 EXPECT_EQ(0, loop.Iterate(InMs(2)));
144 EXPECT_EQ(InMs(101), loop.sleep_time());
Brian Silvermandcaa3f72015-11-29 05:32:08 +0000145
Austin Schuh8aec1ed2016-05-01 13:29:20 -0700146 EXPECT_EQ(-2, loop.Iterate(InMs(-101)));
147 EXPECT_EQ(InMs(-99), loop.sleep_time());
148 EXPECT_EQ(1, loop.Iterate(InMs(-99)));
149 EXPECT_EQ(InMs(1), loop.sleep_time());
Brian Silvermandcaa3f72015-11-29 05:32:08 +0000150
Austin Schuh8aec1ed2016-05-01 13:29:20 -0700151 EXPECT_EQ(0, loop.Iterate(InMs(-99)));
152 EXPECT_EQ(InMs(1), loop.sleep_time());
Brian Silvermandcaa3f72015-11-29 05:32:08 +0000153}
154
Austin Schuh5d4b0982017-04-08 14:36:08 -0700155// Tests OffsetFromIntervalAndTime for various edge conditions.
156TEST_F(PhasedLoopTest, OffsetFromIntervalAndTimeTest) {
Austin Schuhd32b3622019-06-23 18:49:06 -0700157 PhasedLoop loop(milliseconds(1000), monotonic_clock::epoch(),
158 milliseconds(300));
Austin Schuh5d4b0982017-04-08 14:36:08 -0700159
160 EXPECT_EQ(milliseconds(1),
161 loop.OffsetFromIntervalAndTime(milliseconds(1000), InMs(1001)));
162
163 EXPECT_EQ(milliseconds(0),
164 loop.OffsetFromIntervalAndTime(milliseconds(1000), InMs(1000)));
165
166 EXPECT_EQ(milliseconds(0),
167 loop.OffsetFromIntervalAndTime(milliseconds(1000), InMs(0)));
168
169 EXPECT_EQ(milliseconds(999),
170 loop.OffsetFromIntervalAndTime(milliseconds(1000), InMs(-1)));
171
172 EXPECT_EQ(milliseconds(7),
173 loop.OffsetFromIntervalAndTime(milliseconds(1000), InMs(19115007)));
174
175 EXPECT_EQ(milliseconds(7), loop.OffsetFromIntervalAndTime(milliseconds(1000),
176 InMs(-19115993)));
177}
178
Brian Silvermandcaa3f72015-11-29 05:32:08 +0000179// Tests that passing invalid values to the constructor dies correctly.
180TEST_F(PhasedLoopDeathTest, InvalidValues) {
Austin Schuhd32b3622019-06-23 18:49:06 -0700181 EXPECT_DEATH(
182 PhasedLoop(milliseconds(1), monotonic_clock::epoch(), milliseconds(2)),
Austin Schuhf257f3c2019-10-27 21:00:43 -0700183 ".*offset < interval.*");
Austin Schuhd32b3622019-06-23 18:49:06 -0700184 EXPECT_DEATH(
185 PhasedLoop(milliseconds(1), monotonic_clock::epoch(), milliseconds(1)),
Austin Schuhf257f3c2019-10-27 21:00:43 -0700186 ".*offset < interval.*");
Austin Schuhd32b3622019-06-23 18:49:06 -0700187 EXPECT_DEATH(
188 PhasedLoop(milliseconds(1), monotonic_clock::epoch(), milliseconds(-1)),
Austin Schuhf257f3c2019-10-27 21:00:43 -0700189 ".*offset >= monotonic_clock::duration\\(0\\).*");
Austin Schuhd32b3622019-06-23 18:49:06 -0700190 EXPECT_DEATH(
191 PhasedLoop(milliseconds(0), monotonic_clock::epoch(), milliseconds(0)),
Austin Schuhf257f3c2019-10-27 21:00:43 -0700192 ".*interval > monotonic_clock::duration\\(0\\).*");
Brian Silvermandcaa3f72015-11-29 05:32:08 +0000193}
194
Brian Silverman8babd8f2020-06-23 16:38:50 -0700195// Tests that every single value within two intervals of 0 works.
196// This is good at finding edge cases in the rounding.
197TEST_F(PhasedLoopTest, SweepingZero) {
198 for (int i = -30; i < -20; ++i) {
199 PhasedLoop loop(nanoseconds(20),
200 monotonic_clock::epoch() - nanoseconds(30));
201 EXPECT_EQ(1, loop.Iterate(monotonic_clock::epoch() + nanoseconds(i)));
202 }
203 for (int i = -20; i < 0; ++i) {
204 PhasedLoop loop(nanoseconds(20),
205 monotonic_clock::epoch() - nanoseconds(30));
206 EXPECT_EQ(2, loop.Iterate(monotonic_clock::epoch() + nanoseconds(i)));
207 }
208 for (int i = 0; i < 20; ++i) {
209 PhasedLoop loop(nanoseconds(20),
210 monotonic_clock::epoch() - nanoseconds(30));
211 EXPECT_EQ(3, loop.Iterate(monotonic_clock::epoch() + nanoseconds(i)));
212 }
213 for (int i = 20; i < 30; ++i) {
214 PhasedLoop loop(nanoseconds(20),
215 monotonic_clock::epoch() - nanoseconds(30));
216 EXPECT_EQ(4, loop.Iterate(monotonic_clock::epoch() + nanoseconds(i)));
217 }
218}
219
Milind Upadhyay42589bb2021-05-19 20:05:16 -0700220// Tests that the phased loop is correctly adjusting when the offset is
221// decremented multiple times.
222TEST_F(PhasedLoopTest, DecrementingOffset) {
223 constexpr int kCount = 5;
224 constexpr int kIterations = 10;
225 const auto kOffset = milliseconds(400);
226 const auto kInterval = milliseconds(1000);
227 const auto kAllIterationsInterval = kInterval * kIterations;
228
229 PhasedLoop loop(kInterval, monotonic_clock::epoch(), kOffset);
230 auto last_time = monotonic_clock::epoch() + kOffset + (kInterval * 3);
231 ASSERT_EQ(5, loop.Iterate(last_time));
232 for (int i = 1; i < kCount; i++) {
233 const auto offset = kOffset - milliseconds(i);
234 loop.set_interval_and_offset(kInterval, offset);
235 const auto next_time = last_time - milliseconds(1) + kAllIterationsInterval;
236 EXPECT_EQ(kIterations, loop.Iterate(next_time));
237 last_time = next_time;
238 }
239}
240
241// Tests that the phased loop is correctly adjusting when the offset is
242// changed to 0.
243TEST_F(PhasedLoopTest, ChangingOffset) {
244 const auto kOffset = milliseconds(900);
245 const auto kInterval = milliseconds(1000);
246 PhasedLoop loop(kInterval, monotonic_clock::epoch(), kOffset);
247 const auto last_time = monotonic_clock::epoch() + kOffset + (kInterval * 3);
248 ASSERT_EQ(5, loop.Iterate(last_time));
249 loop.set_interval_and_offset(kInterval, milliseconds(0));
250 EXPECT_EQ(4, loop.Iterate((last_time - kOffset) + (kInterval * 4)));
251}
252
Brian Silvermandcaa3f72015-11-29 05:32:08 +0000253} // namespace testing
254} // namespace time
255} // namespace aos