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
23using namespace lldb;
24using namespace lldb_private;
25
26static std::chrono::milliseconds TIMEOUT(500);
27
28class ProgressReportTest : public ::testing::Test {
29public:
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
48protected:
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
63TEST_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
136TEST_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
211TEST_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
239TEST_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
274TEST_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
315TEST_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
398TEST_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

source code of lldb/unittests/Core/ProgressReportTest.cpp