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
22using namespace lldb;
23using namespace lldb_private;
24
25static std::chrono::milliseconds TIMEOUT(500);
26
27class ProgressReportTest : public ::testing::Test {
28public:
29 ListenerSP CreateListenerFor(uint32_t bit) {
30 // Set up the debugger, make sure that was done properly.
31 ArchSpec arch("x86_64-apple-macosx-");
32 Platform::SetHostPlatform(
33 PlatformRemoteMacOSX::CreateInstance(force: true, arch: &arch));
34
35 m_debugger_sp = Debugger::CreateInstance();
36
37 // Get the debugger's broadcaster.
38 Broadcaster &broadcaster = m_debugger_sp->GetBroadcaster();
39
40 // Create a listener, make sure it can receive events and that it's
41 // listening to the correct broadcast bit.
42 m_listener_sp = Listener::MakeListener(name: "progress-listener");
43 m_listener_sp->StartListeningForEvents(broadcaster: &broadcaster, event_mask: bit);
44 return m_listener_sp;
45 }
46
47protected:
48 // The debugger's initialization function can't be called with no arguments
49 // so calling it using SubsystemRAII will cause the test build to fail as
50 // SubsystemRAII will call Initialize with no arguments. As such we set it up
51 // here the usual way.
52 void SetUp() override {
53 std::call_once(once&: TestUtilities::g_debugger_initialize_flag,
54 f: []() { Debugger::Initialize(load_plugin_callback: nullptr); });
55 };
56
57 DebuggerSP m_debugger_sp;
58 ListenerSP m_listener_sp;
59 SubsystemRAII<FileSystem, HostInfo, PlatformMacOSX, ProgressManager>
60 subsystems;
61};
62
63TEST_F(ProgressReportTest, TestReportCreation) {
64 ListenerSP listener_sp = CreateListenerFor(bit: Debugger::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, TestProgressManager) {
137 ListenerSP listener_sp =
138 CreateListenerFor(bit: Debugger::eBroadcastBitProgressCategory);
139 EventSP event_sp;
140 const ProgressEventData *data;
141
142 // Create three progress events with the same category then try to pop 2
143 // events from the queue in a row before the progress reports are destroyed.
144 // Since only 1 event should've been broadcast for this category, the second
145 // GetEvent() call should return false.
146 {
147 Progress progress1("Progress report 1", "Starting report 1");
148 Progress progress2("Progress report 1", "Starting report 2");
149 Progress progress3("Progress report 1", "Starting report 3");
150 ASSERT_TRUE(listener_sp->GetEvent(event_sp, TIMEOUT));
151 ASSERT_FALSE(listener_sp->GetEvent(event_sp, TIMEOUT));
152 }
153
154 data = ProgressEventData::GetEventDataFromEvent(event_ptr: event_sp.get());
155
156 EXPECT_EQ(data->GetDetails(), "");
157 EXPECT_FALSE(data->IsFinite());
158 EXPECT_FALSE(data->GetCompleted());
159 EXPECT_EQ(data->GetTotal(), Progress::kNonDeterministicTotal);
160 EXPECT_EQ(data->GetMessage(), "Progress report 1");
161
162 // Pop another event from the queue, this should be the event for the final
163 // report for this category.
164 ASSERT_TRUE(listener_sp->GetEvent(event_sp, TIMEOUT));
165 data = ProgressEventData::GetEventDataFromEvent(event_ptr: event_sp.get());
166
167 EXPECT_EQ(data->GetDetails(), "");
168 EXPECT_FALSE(data->IsFinite());
169 EXPECT_TRUE(data->GetCompleted());
170 EXPECT_EQ(data->GetTotal(), Progress::kNonDeterministicTotal);
171 EXPECT_EQ(data->GetMessage(), "Progress report 1");
172}
173
174TEST_F(ProgressReportTest, TestOverlappingEvents) {
175 ListenerSP listener_sp =
176 CreateListenerFor(bit: Debugger::eBroadcastBitProgressCategory);
177 EventSP event_sp;
178 const ProgressEventData *data;
179
180 // Create two progress reports of the same category that overlap with each
181 // other. Here we want to ensure that the ID broadcasted for the initial and
182 // final reports for this category are the same.
183 std::unique_ptr<Progress> overlap_progress1 =
184 std::make_unique<Progress>(args: "Overlapping report 1", args: "Starting report 1");
185 std::unique_ptr<Progress> overlap_progress2 =
186 std::make_unique<Progress>(args: "Overlapping report 1", args: "Starting report 2");
187 overlap_progress1.reset();
188
189 ASSERT_TRUE(listener_sp->GetEvent(event_sp, TIMEOUT));
190 data = ProgressEventData::GetEventDataFromEvent(event_ptr: event_sp.get());
191 // Get the ID used in the first report for this category.
192 uint64_t expected_progress_id = data->GetID();
193
194 EXPECT_EQ(data->GetDetails(), "");
195 EXPECT_FALSE(data->IsFinite());
196 EXPECT_FALSE(data->GetCompleted());
197 EXPECT_EQ(data->GetTotal(), Progress::kNonDeterministicTotal);
198 EXPECT_EQ(data->GetMessage(), "Overlapping report 1");
199
200 overlap_progress2.reset();
201
202 ASSERT_TRUE(listener_sp->GetEvent(event_sp, TIMEOUT));
203 data = ProgressEventData::GetEventDataFromEvent(event_ptr: event_sp.get());
204
205 EXPECT_EQ(data->GetDetails(), "");
206 EXPECT_FALSE(data->IsFinite());
207 EXPECT_TRUE(data->GetCompleted());
208 EXPECT_EQ(data->GetTotal(), Progress::kNonDeterministicTotal);
209 EXPECT_EQ(data->GetMessage(), "Overlapping report 1");
210 // The progress ID for the final report should be the same as that for the
211 // initial report.
212 EXPECT_EQ(data->GetID(), expected_progress_id);
213}
214
215TEST_F(ProgressReportTest, TestProgressManagerDisjointReports) {
216 ListenerSP listener_sp =
217 CreateListenerFor(bit: Debugger::eBroadcastBitProgressCategory);
218 EventSP event_sp;
219 const ProgressEventData *data;
220 uint64_t expected_progress_id;
221
222 { Progress progress("Coalesced report 1", "Starting report 1"); }
223 { Progress progress("Coalesced report 1", "Starting report 2"); }
224 { Progress progress("Coalesced report 1", "Starting report 3"); }
225
226 ASSERT_TRUE(listener_sp->GetEvent(event_sp, TIMEOUT));
227 data = ProgressEventData::GetEventDataFromEvent(event_ptr: event_sp.get());
228 expected_progress_id = data->GetID();
229
230 EXPECT_EQ(data->GetDetails(), "");
231 EXPECT_FALSE(data->IsFinite());
232 EXPECT_FALSE(data->GetCompleted());
233 EXPECT_EQ(data->GetTotal(), Progress::kNonDeterministicTotal);
234 EXPECT_EQ(data->GetMessage(), "Coalesced report 1");
235
236 ASSERT_TRUE(listener_sp->GetEvent(event_sp, TIMEOUT));
237 data = ProgressEventData::GetEventDataFromEvent(event_ptr: event_sp.get());
238
239 EXPECT_EQ(data->GetID(), expected_progress_id);
240 EXPECT_EQ(data->GetDetails(), "");
241 EXPECT_FALSE(data->IsFinite());
242 EXPECT_TRUE(data->GetCompleted());
243 EXPECT_EQ(data->GetTotal(), Progress::kNonDeterministicTotal);
244 EXPECT_EQ(data->GetMessage(), "Coalesced report 1");
245
246 ASSERT_FALSE(listener_sp->GetEvent(event_sp, TIMEOUT));
247}
248

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