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 | |
22 | using namespace lldb; |
23 | using namespace lldb_private; |
24 | |
25 | static std::chrono::milliseconds TIMEOUT(500); |
26 | |
27 | class ProgressReportTest : public ::testing::Test { |
28 | public: |
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 | |
47 | protected: |
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 | |
63 | TEST_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 | |
136 | TEST_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 | |
174 | TEST_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 | |
215 | TEST_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 | |