Skip to content

Commit 740e950

Browse files
committed
cleanup & better test cases
cleanup remoe & typo update comment
1 parent c60741b commit 740e950

File tree

4 files changed

+74
-69
lines changed

4 files changed

+74
-69
lines changed

selfdrive/ui/replay/replay.cc

+17-20
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
#include <QApplication>
44
#include <QDebug>
5+
56
#include "cereal/services.h"
67
#include "selfdrive/camerad/cameras/camera_common.h"
78
#include "selfdrive/common/timing.h"
@@ -25,7 +26,7 @@ inline void precise_nano_sleep(long sleep_ns) {
2526

2627
Replay::Replay(QString route, QStringList allow, QStringList block, SubMaster *sm_, bool dcam, bool ecam, QObject *parent)
2728
: sm(sm_), load_dcam(dcam), load_ecam(ecam), QObject(parent) {
28-
std::vector<const char*> s;
29+
std::vector<const char *> s;
2930
for (const auto &it : services) {
3031
if ((allow.size() == 0 || allow.contains(it.name)) &&
3132
!block.contains(it.name)) {
@@ -62,14 +63,13 @@ bool Replay::load() {
6263

6364
void Replay::start(int seconds) {
6465
seekTo(seconds);
65-
qDebug() << "start from" << seconds;
6666
// start stream thread
6767
thread = new QThread;
6868
QObject::connect(thread, &QThread::started, [=]() { stream(); });
6969
thread->start();
7070
}
7171

72-
void Replay::updateEvents(const std::function<bool()>& lambda) {
72+
void Replay::updateEvents(const std::function<bool()> &lambda) {
7373
// set updating_events to true to force stream thread relase the lock and wait for evnets_udpated.
7474
updating_events_ = true;
7575
{
@@ -83,24 +83,24 @@ void Replay::updateEvents(const std::function<bool()>& lambda) {
8383
void Replay::seekTo(int seconds, bool relative) {
8484
if (segments_.empty()) return;
8585

86-
// cannot do seek while queueSegment is running, the queueSegment will wake up the stream thread,
87-
// and change the current_segment_ to a value different from seek in the event loop.
88-
std::unique_lock lk(queue_lock_);
89-
86+
bool segment_loaded = false;
9087
updateEvents([&]() {
9188
if (relative) {
9289
seconds += ((cur_mono_time_ - route_start_ts_) * 1e-9);
9390
}
91+
qInfo() << "seeking to" << seconds;
92+
9493
cur_mono_time_ = route_start_ts_ + std::clamp(seconds, 0, (int)segments_.size() * 60) * 1e9;
9594
current_segment_ = std::min(seconds / 60, (int)segments_.size() - 1);
96-
// always emit segmentChanged. the current_segment_ may not correct when seeking cross boundary or invalid segments.
97-
// e.g. seekTo 60s(segment 1), but segment 0 is end at 60.021, or segment 1 is invalid.
98-
emit segmentChanged();
99-
100-
qInfo() << "seeking to " << seconds;
101-
// return true if segment is already loaded
102-
return segments_[current_segment_] && segments_[current_segment_]->isLoaded();
95+
segment_loaded = std::find(segments_merged_.begin(), segments_merged_.end(), current_segment_) != segments_merged_.end();
96+
return segment_loaded;
10397
});
98+
99+
if (!segment_loaded) {
100+
// always emit segmentChanged if segment is not loaded.
101+
// the current_segment_ may not valid when seeking cross boundary or seeking to an invalid segment.
102+
emit segmentChanged();
103+
}
104104
}
105105

106106
void Replay::pause(bool pause) {
@@ -119,7 +119,6 @@ void Replay::setCurrentSegment(int n) {
119119

120120
// maintain the segment window
121121
void Replay::queueSegment() {
122-
std::unique_lock lk(queue_lock_);
123122
// fetch segments forward
124123
int cur_seg = current_segment_.load();
125124
int end_idx = cur_seg;
@@ -173,20 +172,18 @@ void Replay::mergeSegments(int cur_seg, int end_idx) {
173172
auto it = std::find_if(new_events->begin(), new_events->end(), [=](auto e) { return e->which == cereal::Event::Which::INIT_DATA; });
174173
if (it != new_events->end()) {
175174
route_start_ts_ = (*it)->mono_time;
176-
// cur_mono_time_ may be set by seek when route_start_ts_ = 0
175+
// cur_mono_time_ is set by seekTo int start() before get route_start_ts_
177176
cur_mono_time_ += route_start_ts_;
178177
}
179178
}
180179

181180
events_ = new_events;
182181
segments_merged_ = segments_need_merge;
183-
return segments_[cur_seg] && segments_[cur_seg]->isLoaded();
182+
return true;
184183
});
185184
delete prev_events;
186185
} else {
187-
updateEvents([&]() {
188-
return segments_[cur_seg] && segments_[cur_seg]->isLoaded();
189-
});
186+
updateEvents([]() { return true; });
190187
}
191188

192189
// free segments out of current semgnt window.

selfdrive/ui/replay/replay.h

-1
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,6 @@ protected slots:
3838
QThread *thread;
3939

4040
// logs
41-
std::mutex queue_lock_;
4241
std::mutex stream_lock_;
4342
std::condition_variable stream_cv_;
4443
std::atomic<bool> updating_events_ = false;

selfdrive/ui/replay/route.h

+3-4
Original file line numberDiff line numberDiff line change
@@ -30,10 +30,12 @@ class Route {
3030
inline int size() const { return segments_.size(); }
3131
inline SegmentFile &at(int n) { return segments_[n]; }
3232

33+
// public for unit tests
34+
std::vector<SegmentFile> segments_;
35+
3336
protected:
3437
bool loadFromJson(const QString &json);
3538
QString route_;
36-
std::vector<SegmentFile> segments_;
3739
};
3840

3941
class Segment : public QObject {
@@ -48,9 +50,6 @@ class Segment : public QObject {
4850
std::unique_ptr<LogReader> log;
4951
std::unique_ptr<FrameReader> frames[MAX_CAMERAS] = {};
5052

51-
// public for unit tests
52-
bool loaded_ = false, valid_ = false;
53-
5453
signals:
5554
void loadFinished();
5655

selfdrive/ui/replay/tests/test_replay.cc

+54-44
Original file line numberDiff line numberDiff line change
@@ -99,70 +99,80 @@ TEST_CASE("Segment") {
9999

100100
// helper class for unit tests
101101
class TestReplay : public Replay {
102-
public:
102+
public:
103103
TestReplay(const QString &route) : Replay(route, {}, {}) {}
104104
void test_seek();
105105

106-
protected:
107-
void testSeekTo(int seek_to, int invalid_segment = -1);
106+
protected:
107+
void testSeekTo(int seek_to, const std::set<int> &invalid_segments = {});
108108
};
109109

110-
void TestReplay::testSeekTo(int seek_to, int invalid_segment) {
110+
void TestReplay::testSeekTo(int seek_to, const std::set<int> &invalid_segments) {
111111
seekTo(seek_to);
112112

113113
// wait for seek finish
114-
std::unique_lock lk(stream_lock_);
115-
stream_cv_.wait(lk, [=]() { return events_updated_ == true; });
116-
events_updated_ = false;
117-
118-
// verify result
119-
INFO("seek to [" << seek_to << "s segment " << seek_to / 60 << "]");
120-
REQUIRE(!events_->empty());
121-
REQUIRE(is_events_ordered(*events_));
122-
REQUIRE(uint64_t(route_start_ts_ + seek_to * 1e9) == cur_mono_time_);
123-
124-
Event cur_event(cereal::Event::Which::INIT_DATA, cur_mono_time_);
125-
auto eit = std::upper_bound(events_->begin(), events_->end(), &cur_event, Event::lessThan());
126-
127-
REQUIRE(eit != events_->end());
128-
const int seek_to_segment = seek_to / 60;
129-
const int event_seconds = ((*eit)->mono_time - route_start_ts_) / 1e9;
130-
current_segment_ = event_seconds / 60;
131-
INFO("event [" << event_seconds << "s segment " << current_segment_ << "]");
132-
REQUIRE(event_seconds >= seek_to);
133-
if (seek_to_segment != invalid_segment) {
134-
REQUIRE(current_segment_ == seek_to_segment); // in the same segment
135-
REQUIRE(event_seconds == seek_to); // at the same time
136-
} else {
137-
// has invalid_segment
138-
if (current_segment_ == seek_to_segment) {
139-
// seek cross-boundary. e.g. seek_to 60s(segment 1), but segment 0 end at 60.021.
140-
REQUIRE(event_seconds == seek_to);
114+
while (true) {
115+
std::unique_lock lk(stream_lock_);
116+
stream_cv_.wait(lk, [=]() { return events_updated_ == true; });
117+
events_updated_ = false;
118+
119+
// verify result
120+
REQUIRE(uint64_t(route_start_ts_ + seek_to * 1e9) == cur_mono_time_);
121+
122+
Event cur_event(cereal::Event::Which::INIT_DATA, cur_mono_time_);
123+
auto eit = std::upper_bound(events_->begin(), events_->end(), &cur_event, Event::lessThan());
124+
if (eit == events_->end()) {
125+
qDebug() << "waiting for events...";
126+
continue;
127+
}
128+
129+
INFO("seek to [" << seek_to << "s segment " << seek_to / 60 << "]");
130+
REQUIRE(!events_->empty());
131+
REQUIRE(is_events_ordered(*events_));
132+
133+
REQUIRE(eit != events_->end());
134+
const int seek_to_segment = seek_to / 60;
135+
const int event_seconds = ((*eit)->mono_time - route_start_ts_) / 1e9;
136+
current_segment_ = event_seconds / 60;
137+
INFO("event [" << event_seconds << "s segment " << current_segment_ << "]");
138+
REQUIRE(event_seconds >= seek_to);
139+
if (invalid_segments.find(seek_to_segment) == invalid_segments.end()) {
140+
REQUIRE(current_segment_ == seek_to_segment); // in the same segment
141+
REQUIRE(event_seconds == seek_to); // at the same time
141142
} else {
142-
REQUIRE(current_segment_ == seek_to_segment + 1);
143+
if (current_segment_ == seek_to_segment) {
144+
// seek cross-boundary. e.g. seek_to 60s(segment 1), but segment 0 end at 60.021 and segemnt 1 is invalid.
145+
REQUIRE(event_seconds == seek_to);
146+
} else {
147+
REQUIRE(current_segment_ > seek_to_segment);
148+
REQUIRE(invalid_segments.find(current_segment_) == invalid_segments.end());
149+
}
143150
}
151+
break;
144152
}
145153
}
146154

147155
void TestReplay::test_seek() {
148156
QEventLoop loop;
149157

150158
std::thread thread = std::thread([&]() {
151-
// random seek 500 times in one segment
152-
for (int i = 0; i < 500; ++i) {
159+
const int loop_count = 100;
160+
// random seek in one segment
161+
for (int i = 0; i < loop_count; ++i) {
153162
testSeekTo(random_int(0, 60));
154163
}
155-
// random seek 500 times in 3 segments (this test if fast)
156-
for (int i = 0; i < 500; ++i) {
157-
testSeekTo(random_int(0, 60 * 3 - 1));
164+
// random seek in 3 segments
165+
for (int i = 0; i < loop_count; ++i) {
166+
testSeekTo(random_int(0, 60 * 3));
158167
}
159-
160-
// random seek 500 times in 3 segments.segment 1 is invalid.
161-
// invalid segmen 1
162-
segments_[1]->valid_ = segments_[1]->loaded_ = false;
163-
queueSegment();
164-
for (int i = 0; i < 500; ++i) {
165-
testSeekTo(random_int(0, 60 * 3 - 1), 1);
168+
// random seek in invalid segments
169+
std::set<int> invalid_segments{5, 6, 7, 9};
170+
for (int i : invalid_segments) {
171+
route_->segments_[i].rlog = route_->segments_[i].qlog = "";
172+
route_->segments_[i].road_cam = route_->segments_[i].qcamera = "";
173+
}
174+
for (int i = 0; i < loop_count; ++i) {
175+
testSeekTo(random_int(4 * 60, 60 * 10), invalid_segments);
166176
}
167177
loop.quit();
168178
});

0 commit comments

Comments
 (0)