1 | //===-- ProgressReportTest.cpp --------------------------------------------===// |
2 | // |
3 | // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. |
4 | // See https://llvm.org/LICENSE.txt for license information. |
5 | // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
6 | // |
7 | //===----------------------------------------------------------------------===// |
8 | |
9 | #include "Plugins/Platform/MacOSX/PlatformMacOSX.h" |
10 | #include "Plugins/Platform/MacOSX/PlatformRemoteMacOSX.h" |
11 | #include "TestingSupport/SubsystemRAII.h" |
12 | #include "TestingSupport/TestUtilities.h" |
13 | #include "lldb/Core/Debugger.h" |
14 | #include "lldb/Core/Progress.h" |
15 | #include "lldb/Host/FileSystem.h" |
16 | #include "lldb/Host/HostInfo.h" |
17 | #include "lldb/Utility/Listener.h" |
18 | #include "gtest/gtest.h" |
19 | #include <memory> |
20 | #include <mutex> |
21 | #include <thread> |
22 | |
23 | using namespace lldb; |
24 | using namespace lldb_private; |
25 | |
26 | static std::chrono::milliseconds TIMEOUT(500); |
27 | |
28 | class ProgressReportTest : public ::testing::Test { |
29 | public: |
30 | ListenerSP CreateListenerFor(uint32_t bit) { |
31 | // Set up the debugger, make sure that was done properly. |
32 | ArchSpec arch("x86_64-apple-macosx-" ); |
33 | Platform::SetHostPlatform( |
34 | PlatformRemoteMacOSX::CreateInstance(force: true, arch: &arch)); |
35 | |
36 | m_debugger_sp = Debugger::CreateInstance(); |
37 | |
38 | // Get the debugger's broadcaster. |
39 | Broadcaster &broadcaster = m_debugger_sp->GetBroadcaster(); |
40 | |
41 | // Create a listener, make sure it can receive events and that it's |
42 | // listening to the correct broadcast bit. |
43 | m_listener_sp = Listener::MakeListener(name: "progress-listener" ); |
44 | m_listener_sp->StartListeningForEvents(broadcaster: &broadcaster, event_mask: bit); |
45 | return m_listener_sp; |
46 | } |
47 | |
48 | protected: |
49 | // The debugger's initialization function can't be called with no arguments |
50 | // so calling it using SubsystemRAII will cause the test build to fail as |
51 | // SubsystemRAII will call Initialize with no arguments. As such we set it up |
52 | // here the usual way. |
53 | void SetUp() override { |
54 | std::call_once(once&: TestUtilities::g_debugger_initialize_flag, |
55 | f: []() { Debugger::Initialize(load_plugin_callback: nullptr); }); |
56 | }; |
57 | |
58 | DebuggerSP m_debugger_sp; |
59 | ListenerSP m_listener_sp; |
60 | SubsystemRAII<FileSystem, HostInfo, PlatformMacOSX> subsystems; |
61 | }; |
62 | |
63 | TEST_F(ProgressReportTest, TestReportCreation) { |
64 | ListenerSP listener_sp = CreateListenerFor(bit: lldb::eBroadcastBitProgress); |
65 | EventSP event_sp; |
66 | const ProgressEventData *data; |
67 | |
68 | // Scope this for RAII on the progress objects. |
69 | // Create progress reports and check that their respective events for having |
70 | // started and ended are broadcasted. |
71 | { |
72 | Progress progress1("Progress report 1" , "Starting report 1" ); |
73 | Progress progress2("Progress report 2" , "Starting report 2" ); |
74 | Progress progress3("Progress report 3" , "Starting report 3" ); |
75 | } |
76 | |
77 | // Start popping events from the queue, they should have been recevied |
78 | // in this order: |
79 | // Starting progress: 1, 2, 3 |
80 | // Ending progress: 3, 2, 1 |
81 | ASSERT_TRUE(listener_sp->GetEvent(event_sp, TIMEOUT)); |
82 | data = ProgressEventData::GetEventDataFromEvent(event_ptr: event_sp.get()); |
83 | |
84 | EXPECT_EQ(data->GetDetails(), "Starting report 1" ); |
85 | EXPECT_FALSE(data->IsFinite()); |
86 | EXPECT_FALSE(data->GetCompleted()); |
87 | EXPECT_EQ(data->GetTotal(), Progress::kNonDeterministicTotal); |
88 | EXPECT_EQ(data->GetMessage(), "Progress report 1: Starting report 1" ); |
89 | |
90 | ASSERT_TRUE(listener_sp->GetEvent(event_sp, TIMEOUT)); |
91 | data = ProgressEventData::GetEventDataFromEvent(event_ptr: event_sp.get()); |
92 | |
93 | EXPECT_EQ(data->GetDetails(), "Starting report 2" ); |
94 | EXPECT_FALSE(data->IsFinite()); |
95 | EXPECT_FALSE(data->GetCompleted()); |
96 | EXPECT_EQ(data->GetTotal(), Progress::kNonDeterministicTotal); |
97 | EXPECT_EQ(data->GetMessage(), "Progress report 2: Starting report 2" ); |
98 | |
99 | ASSERT_TRUE(listener_sp->GetEvent(event_sp, TIMEOUT)); |
100 | data = ProgressEventData::GetEventDataFromEvent(event_ptr: event_sp.get()); |
101 | |
102 | EXPECT_EQ(data->GetDetails(), "Starting report 3" ); |
103 | EXPECT_FALSE(data->IsFinite()); |
104 | EXPECT_FALSE(data->GetCompleted()); |
105 | EXPECT_EQ(data->GetTotal(), Progress::kNonDeterministicTotal); |
106 | EXPECT_EQ(data->GetMessage(), "Progress report 3: Starting report 3" ); |
107 | |
108 | // Progress report objects should be destroyed at this point so |
109 | // get each report from the queue and check that they've been |
110 | // destroyed in reverse order. |
111 | ASSERT_TRUE(listener_sp->GetEvent(event_sp, TIMEOUT)); |
112 | data = ProgressEventData::GetEventDataFromEvent(event_ptr: event_sp.get()); |
113 | |
114 | EXPECT_EQ(data->GetTitle(), "Progress report 3" ); |
115 | EXPECT_TRUE(data->GetCompleted()); |
116 | EXPECT_FALSE(data->IsFinite()); |
117 | EXPECT_EQ(data->GetMessage(), "Progress report 3: Starting report 3" ); |
118 | |
119 | ASSERT_TRUE(listener_sp->GetEvent(event_sp, TIMEOUT)); |
120 | data = ProgressEventData::GetEventDataFromEvent(event_ptr: event_sp.get()); |
121 | |
122 | EXPECT_EQ(data->GetTitle(), "Progress report 2" ); |
123 | EXPECT_TRUE(data->GetCompleted()); |
124 | EXPECT_FALSE(data->IsFinite()); |
125 | EXPECT_EQ(data->GetMessage(), "Progress report 2: Starting report 2" ); |
126 | |
127 | ASSERT_TRUE(listener_sp->GetEvent(event_sp, TIMEOUT)); |
128 | data = ProgressEventData::GetEventDataFromEvent(event_ptr: event_sp.get()); |
129 | |
130 | EXPECT_EQ(data->GetTitle(), "Progress report 1" ); |
131 | EXPECT_TRUE(data->GetCompleted()); |
132 | EXPECT_FALSE(data->IsFinite()); |
133 | EXPECT_EQ(data->GetMessage(), "Progress report 1: Starting report 1" ); |
134 | } |
135 | |
136 | TEST_F(ProgressReportTest, TestReportDestructionWithPartialProgress) { |
137 | ListenerSP listener_sp = CreateListenerFor(bit: lldb::eBroadcastBitProgress); |
138 | EventSP event_sp; |
139 | const ProgressEventData *data; |
140 | |
141 | // Create a finite progress report and only increment to a non-completed |
142 | // state before destruction. |
143 | { |
144 | Progress progress("Finite progress" , "Report 1" , 100); |
145 | progress.Increment(amount: 3); |
146 | } |
147 | |
148 | // Verify that the progress in the events are: |
149 | // 1. At construction: 0 out of 100 |
150 | // 2. At increment: 3 out of 100 |
151 | // 3. At destruction: 100 out of 100 |
152 | ASSERT_TRUE(listener_sp->GetEvent(event_sp, TIMEOUT)); |
153 | data = ProgressEventData::GetEventDataFromEvent(event_ptr: event_sp.get()); |
154 | EXPECT_EQ(data->GetDetails(), "Report 1" ); |
155 | EXPECT_TRUE(data->IsFinite()); |
156 | EXPECT_EQ(data->GetCompleted(), (uint64_t)0); |
157 | EXPECT_EQ(data->GetTotal(), (uint64_t)100); |
158 | EXPECT_EQ(data->GetMessage(), "Finite progress: Report 1" ); |
159 | |
160 | ASSERT_TRUE(listener_sp->GetEvent(event_sp, TIMEOUT)); |
161 | data = ProgressEventData::GetEventDataFromEvent(event_ptr: event_sp.get()); |
162 | EXPECT_EQ(data->GetDetails(), "Report 1" ); |
163 | EXPECT_TRUE(data->IsFinite()); |
164 | EXPECT_EQ(data->GetCompleted(), (uint64_t)3); |
165 | EXPECT_EQ(data->GetTotal(), (uint64_t)100); |
166 | EXPECT_EQ(data->GetMessage(), "Finite progress: Report 1" ); |
167 | |
168 | ASSERT_TRUE(listener_sp->GetEvent(event_sp, TIMEOUT)); |
169 | data = ProgressEventData::GetEventDataFromEvent(event_ptr: event_sp.get()); |
170 | EXPECT_EQ(data->GetDetails(), "Report 1" ); |
171 | EXPECT_TRUE(data->IsFinite()); |
172 | EXPECT_EQ(data->GetCompleted(), (uint64_t)100); |
173 | EXPECT_EQ(data->GetTotal(), (uint64_t)100); |
174 | EXPECT_EQ(data->GetMessage(), "Finite progress: Report 1" ); |
175 | |
176 | // Create an infinite progress report and increment by some amount. |
177 | { |
178 | Progress progress("Infinite progress" , "Report 2" ); |
179 | progress.Increment(amount: 3); |
180 | } |
181 | |
182 | // Verify that the progress in the events are: |
183 | // 1. At construction: 0 |
184 | // 2. At increment: 3 |
185 | // 3. At destruction: Progress::kNonDeterministicTotal |
186 | ASSERT_TRUE(listener_sp->GetEvent(event_sp, TIMEOUT)); |
187 | data = ProgressEventData::GetEventDataFromEvent(event_ptr: event_sp.get()); |
188 | EXPECT_EQ(data->GetDetails(), "Report 2" ); |
189 | EXPECT_FALSE(data->IsFinite()); |
190 | EXPECT_EQ(data->GetCompleted(), (uint64_t)0); |
191 | EXPECT_EQ(data->GetTotal(), Progress::kNonDeterministicTotal); |
192 | EXPECT_EQ(data->GetMessage(), "Infinite progress: Report 2" ); |
193 | |
194 | ASSERT_TRUE(listener_sp->GetEvent(event_sp, TIMEOUT)); |
195 | data = ProgressEventData::GetEventDataFromEvent(event_ptr: event_sp.get()); |
196 | EXPECT_EQ(data->GetDetails(), "Report 2" ); |
197 | EXPECT_FALSE(data->IsFinite()); |
198 | EXPECT_EQ(data->GetCompleted(), (uint64_t)3); |
199 | EXPECT_EQ(data->GetTotal(), Progress::kNonDeterministicTotal); |
200 | EXPECT_EQ(data->GetMessage(), "Infinite progress: Report 2" ); |
201 | |
202 | ASSERT_TRUE(listener_sp->GetEvent(event_sp, TIMEOUT)); |
203 | data = ProgressEventData::GetEventDataFromEvent(event_ptr: event_sp.get()); |
204 | EXPECT_EQ(data->GetDetails(), "Report 2" ); |
205 | EXPECT_FALSE(data->IsFinite()); |
206 | EXPECT_EQ(data->GetCompleted(), Progress::kNonDeterministicTotal); |
207 | EXPECT_EQ(data->GetTotal(), Progress::kNonDeterministicTotal); |
208 | EXPECT_EQ(data->GetMessage(), "Infinite progress: Report 2" ); |
209 | } |
210 | |
211 | TEST_F(ProgressReportTest, TestFiniteOverflow) { |
212 | ListenerSP listener_sp = CreateListenerFor(bit: lldb::eBroadcastBitProgress); |
213 | EventSP event_sp; |
214 | const ProgressEventData *data; |
215 | |
216 | // Increment the report beyond its limit and make sure we only get one |
217 | // completed event. |
218 | { |
219 | Progress progress("Finite progress" , "Report 1" , 10); |
220 | progress.Increment(amount: 11); |
221 | progress.Increment(amount: 47); |
222 | } |
223 | |
224 | ASSERT_TRUE(listener_sp->GetEvent(event_sp, TIMEOUT)); |
225 | data = ProgressEventData::GetEventDataFromEvent(event_ptr: event_sp.get()); |
226 | EXPECT_TRUE(data->IsFinite()); |
227 | EXPECT_EQ(data->GetCompleted(), 0U); |
228 | EXPECT_EQ(data->GetTotal(), 10U); |
229 | |
230 | ASSERT_TRUE(listener_sp->GetEvent(event_sp, TIMEOUT)); |
231 | data = ProgressEventData::GetEventDataFromEvent(event_ptr: event_sp.get()); |
232 | EXPECT_TRUE(data->IsFinite()); |
233 | EXPECT_EQ(data->GetCompleted(), 10U); |
234 | EXPECT_EQ(data->GetTotal(), 10U); |
235 | |
236 | ASSERT_FALSE(listener_sp->GetEvent(event_sp, TIMEOUT)); |
237 | } |
238 | |
239 | TEST_F(ProgressReportTest, TestNonDeterministicOverflow) { |
240 | ListenerSP listener_sp = CreateListenerFor(bit: lldb::eBroadcastBitProgress); |
241 | EventSP event_sp; |
242 | const ProgressEventData *data; |
243 | constexpr uint64_t max_minus_1 = std::numeric_limits<uint64_t>::max() - 1; |
244 | |
245 | // Increment the report beyond its limit and make sure we only get one |
246 | // completed event. The event which overflows the counter should be ignored. |
247 | { |
248 | Progress progress("Non deterministic progress" , "Report 1" ); |
249 | progress.Increment(amount: max_minus_1); |
250 | progress.Increment(amount: max_minus_1); |
251 | } |
252 | |
253 | ASSERT_TRUE(listener_sp->GetEvent(event_sp, TIMEOUT)); |
254 | data = ProgressEventData::GetEventDataFromEvent(event_ptr: event_sp.get()); |
255 | EXPECT_FALSE(data->IsFinite()); |
256 | EXPECT_EQ(data->GetCompleted(), 0U); |
257 | EXPECT_EQ(data->GetTotal(), Progress::kNonDeterministicTotal); |
258 | |
259 | ASSERT_TRUE(listener_sp->GetEvent(event_sp, TIMEOUT)); |
260 | data = ProgressEventData::GetEventDataFromEvent(event_ptr: event_sp.get()); |
261 | EXPECT_FALSE(data->IsFinite()); |
262 | EXPECT_EQ(data->GetCompleted(), max_minus_1); |
263 | EXPECT_EQ(data->GetTotal(), Progress::kNonDeterministicTotal); |
264 | |
265 | ASSERT_TRUE(listener_sp->GetEvent(event_sp, TIMEOUT)); |
266 | data = ProgressEventData::GetEventDataFromEvent(event_ptr: event_sp.get()); |
267 | EXPECT_FALSE(data->IsFinite()); |
268 | EXPECT_EQ(data->GetCompleted(), Progress::kNonDeterministicTotal); |
269 | EXPECT_EQ(data->GetTotal(), Progress::kNonDeterministicTotal); |
270 | |
271 | ASSERT_FALSE(listener_sp->GetEvent(event_sp, TIMEOUT)); |
272 | } |
273 | |
274 | TEST_F(ProgressReportTest, TestMinimumReportTime) { |
275 | ListenerSP listener_sp = CreateListenerFor(bit: lldb::eBroadcastBitProgress); |
276 | EventSP event_sp; |
277 | const ProgressEventData *data; |
278 | |
279 | { |
280 | Progress progress("Finite progress" , "Report 1" , /*total=*/20, |
281 | m_debugger_sp.get(), |
282 | /*minimum_report_time=*/std::chrono::seconds(1)); |
283 | // Send 10 events in quick succession. These should not generate any events. |
284 | for (int i = 0; i < 10; ++i) |
285 | progress.Increment(); |
286 | |
287 | // Sleep, then send 10 more. This should generate one event for the first |
288 | // increment, and then another for completion. |
289 | std::this_thread::sleep_for(rtime: std::chrono::seconds(1)); |
290 | for (int i = 0; i < 10; ++i) |
291 | progress.Increment(); |
292 | } |
293 | |
294 | ASSERT_TRUE(listener_sp->GetEvent(event_sp, TIMEOUT)); |
295 | data = ProgressEventData::GetEventDataFromEvent(event_ptr: event_sp.get()); |
296 | EXPECT_TRUE(data->IsFinite()); |
297 | EXPECT_EQ(data->GetCompleted(), 0U); |
298 | EXPECT_EQ(data->GetTotal(), 20U); |
299 | |
300 | ASSERT_TRUE(listener_sp->GetEvent(event_sp, TIMEOUT)); |
301 | data = ProgressEventData::GetEventDataFromEvent(event_ptr: event_sp.get()); |
302 | EXPECT_TRUE(data->IsFinite()); |
303 | EXPECT_EQ(data->GetCompleted(), 11U); |
304 | EXPECT_EQ(data->GetTotal(), 20U); |
305 | |
306 | ASSERT_TRUE(listener_sp->GetEvent(event_sp, TIMEOUT)); |
307 | data = ProgressEventData::GetEventDataFromEvent(event_ptr: event_sp.get()); |
308 | EXPECT_TRUE(data->IsFinite()); |
309 | EXPECT_EQ(data->GetCompleted(), 20U); |
310 | EXPECT_EQ(data->GetTotal(), 20U); |
311 | |
312 | ASSERT_FALSE(listener_sp->GetEvent(event_sp, TIMEOUT)); |
313 | } |
314 | |
315 | TEST_F(ProgressReportTest, TestExternalReportCreation) { |
316 | ListenerSP listener_sp = |
317 | CreateListenerFor(bit: lldb::eBroadcastBitExternalProgress); |
318 | EventSP event_sp; |
319 | const ProgressEventData *data; |
320 | |
321 | // Scope this for RAII on the progress objects. |
322 | // Create progress reports and check that their respective events for having |
323 | // started and ended are broadcasted. |
324 | { |
325 | Progress progress1("Progress report 1" , "Starting report 1" , |
326 | /*total=*/std::nullopt, /*debugger=*/nullptr, |
327 | /*minimum_report_time=*/std::chrono::seconds(0), |
328 | Progress::Origin::eExternal); |
329 | Progress progress2("Progress report 2" , "Starting report 2" , |
330 | /*total=*/std::nullopt, /*debugger=*/nullptr, |
331 | /*minimum_report_time=*/std::chrono::seconds(0), |
332 | Progress::Origin::eExternal); |
333 | Progress progress3("Progress report 3" , "Starting report 3" , |
334 | /*total=*/std::nullopt, /*debugger=*/nullptr, |
335 | /*minimum_report_time=*/std::chrono::seconds(0), |
336 | Progress::Origin::eExternal); |
337 | } |
338 | |
339 | // Start popping events from the queue, they should have been recevied |
340 | // in this order: |
341 | // Starting progress: 1, 2, 3 |
342 | // Ending progress: 3, 2, 1 |
343 | ASSERT_TRUE(listener_sp->GetEvent(event_sp, TIMEOUT)); |
344 | data = ProgressEventData::GetEventDataFromEvent(event_ptr: event_sp.get()); |
345 | |
346 | EXPECT_EQ(data->GetDetails(), "Starting report 1" ); |
347 | EXPECT_FALSE(data->IsFinite()); |
348 | EXPECT_FALSE(data->GetCompleted()); |
349 | EXPECT_EQ(data->GetTotal(), Progress::kNonDeterministicTotal); |
350 | EXPECT_EQ(data->GetMessage(), "Progress report 1: Starting report 1" ); |
351 | |
352 | ASSERT_TRUE(listener_sp->GetEvent(event_sp, TIMEOUT)); |
353 | data = ProgressEventData::GetEventDataFromEvent(event_ptr: event_sp.get()); |
354 | |
355 | EXPECT_EQ(data->GetDetails(), "Starting report 2" ); |
356 | EXPECT_FALSE(data->IsFinite()); |
357 | EXPECT_FALSE(data->GetCompleted()); |
358 | EXPECT_EQ(data->GetTotal(), Progress::kNonDeterministicTotal); |
359 | EXPECT_EQ(data->GetMessage(), "Progress report 2: Starting report 2" ); |
360 | |
361 | ASSERT_TRUE(listener_sp->GetEvent(event_sp, TIMEOUT)); |
362 | data = ProgressEventData::GetEventDataFromEvent(event_ptr: event_sp.get()); |
363 | |
364 | EXPECT_EQ(data->GetDetails(), "Starting report 3" ); |
365 | EXPECT_FALSE(data->IsFinite()); |
366 | EXPECT_FALSE(data->GetCompleted()); |
367 | EXPECT_EQ(data->GetTotal(), Progress::kNonDeterministicTotal); |
368 | EXPECT_EQ(data->GetMessage(), "Progress report 3: Starting report 3" ); |
369 | |
370 | // Progress report objects should be destroyed at this point so |
371 | // get each report from the queue and check that they've been |
372 | // destroyed in reverse order. |
373 | ASSERT_TRUE(listener_sp->GetEvent(event_sp, TIMEOUT)); |
374 | data = ProgressEventData::GetEventDataFromEvent(event_ptr: event_sp.get()); |
375 | |
376 | EXPECT_EQ(data->GetTitle(), "Progress report 3" ); |
377 | EXPECT_TRUE(data->GetCompleted()); |
378 | EXPECT_FALSE(data->IsFinite()); |
379 | EXPECT_EQ(data->GetMessage(), "Progress report 3: Starting report 3" ); |
380 | |
381 | ASSERT_TRUE(listener_sp->GetEvent(event_sp, TIMEOUT)); |
382 | data = ProgressEventData::GetEventDataFromEvent(event_ptr: event_sp.get()); |
383 | |
384 | EXPECT_EQ(data->GetTitle(), "Progress report 2" ); |
385 | EXPECT_TRUE(data->GetCompleted()); |
386 | EXPECT_FALSE(data->IsFinite()); |
387 | EXPECT_EQ(data->GetMessage(), "Progress report 2: Starting report 2" ); |
388 | |
389 | ASSERT_TRUE(listener_sp->GetEvent(event_sp, TIMEOUT)); |
390 | data = ProgressEventData::GetEventDataFromEvent(event_ptr: event_sp.get()); |
391 | |
392 | EXPECT_EQ(data->GetTitle(), "Progress report 1" ); |
393 | EXPECT_TRUE(data->GetCompleted()); |
394 | EXPECT_FALSE(data->IsFinite()); |
395 | EXPECT_EQ(data->GetMessage(), "Progress report 1: Starting report 1" ); |
396 | } |
397 | |
398 | TEST_F(ProgressReportTest, TestExternalReportNotReceived) { |
399 | ListenerSP listener_sp = CreateListenerFor(bit: lldb::eBroadcastBitProgress); |
400 | EventSP event_sp; |
401 | |
402 | // Scope this for RAII on the progress objects. |
403 | // Create progress reports and check that their respective events for having |
404 | // started and ended are broadcasted. |
405 | { |
406 | Progress progress1("External Progress report 1" , |
407 | "Starting external report 1" , |
408 | /*total=*/std::nullopt, /*debugger=*/nullptr, |
409 | /*minimum_report_time=*/std::chrono::seconds(0), |
410 | Progress::Origin::eExternal); |
411 | } |
412 | |
413 | ASSERT_FALSE(listener_sp->GetEvent(event_sp, TIMEOUT)); |
414 | } |
415 | |