[LLDB][SBProgress] Add a finalize method (#128966)

This patch adds a finalize method which destroys the underlying RAII
SBProgress. My primary motivation for this is so I can write better
tests that are non-flaky, but after discussing with @clayborg in my DAP
message improvement patch (#124648) this is probably an essential API
despite that I originally argued it wasn't.
This commit is contained in:
Jacob Lalonde
2025-03-03 14:01:29 -08:00
committed by GitHub
parent 8179bcfe56
commit 3ff6fb68d7
4 changed files with 58 additions and 29 deletions

View File

@@ -12,3 +12,14 @@ and will always send an initial progress update, updates when
Progress::Increment() is called, and also will make sure that a progress
completed update is reported even if the user doesn't explicitly cause one
to be sent.") lldb::SBProgress;
%feature("docstring",
"Finalize the SBProgress, which will cause a progress end event to be emitted. This
happens automatically when the SBProcess object is destroyed, but can be done explicitly
with Finalize to avoid having to rely on the language semantics for destruction.
Note once finalized, no further increments will be processed.") lldb::SBProgress::Finalize;
%feature("docstring",
"Increment the progress by a given number of units, optionally with a message. Not all progress events are guaraunteed
to be sent, but incrementing to the total will always guarauntee a progress end event being sent.") lldb::SBProcess::Increment;

View File

@@ -59,6 +59,11 @@ public:
void Increment(uint64_t amount, const char *description = nullptr);
/// Explicitly finalize an SBProgress, this can be used to terminate a
/// progress on command instead of waiting for a garbage collection or other
/// RAII to destroy the contained progress object.
void Finalize();
protected:
lldb_private::Progress &ref() const;

View File

@@ -40,10 +40,22 @@ SBProgress::~SBProgress() = default;
void SBProgress::Increment(uint64_t amount, const char *description) {
LLDB_INSTRUMENT_VA(amount, description);
if (!m_opaque_up)
return;
std::optional<std::string> description_opt;
if (description && description[0])
description_opt = description;
m_opaque_up->Increment(amount, std::move(description_opt));
}
void SBProgress::Finalize() {
// The lldb_private::Progress object is designed to be RAII and send the end
// progress event when it gets destroyed. So force our contained object to be
// destroyed and send the progress end event. Clearing this object also allows
// all other methods to quickly return without doing any work if they are
// called after this method.
m_opaque_up.reset();
}
lldb_private::Progress &SBProgress::ref() const { return *m_opaque_up; }

View File

@@ -5,35 +5,6 @@ from lldbsuite.test.lldbtest import *
class SBProgressTestCase(TestBase):
def test_with_external_bit_set(self):
"""Test SBProgress events are listened to when the external bit is set."""
progress = lldb.SBProgress("Test SBProgress", "Test progress", self.dbg)
listener = lldb.SBListener("Test listener")
broadcaster = self.dbg.GetBroadcaster()
broadcaster.AddListener(listener, lldb.eBroadcastBitExternalProgress)
event = lldb.SBEvent()
expected_string = "Test progress first increment"
progress.Increment(1, expected_string)
self.assertTrue(listener.PeekAtNextEvent(event))
stream = lldb.SBStream()
event.GetDescription(stream)
self.assertIn(expected_string, stream.GetData())
def test_without_external_bit_set(self):
"""Test SBProgress events are not listened to on the internal progress bit."""
progress = lldb.SBProgress("Test SBProgress", "Test progress", self.dbg)
listener = lldb.SBListener("Test listener")
broadcaster = self.dbg.GetBroadcaster()
broadcaster.AddListener(listener, lldb.eBroadcastBitProgress)
event = lldb.SBEvent()
expected_string = "Test progress first increment"
progress.Increment(1, expected_string)
self.assertFalse(listener.PeekAtNextEvent(event))
def test_with_external_bit_set(self):
"""Test SBProgress can handle null events."""
@@ -65,3 +36,33 @@ class SBProgressTestCase(TestBase):
stream = lldb.SBStream()
event.GetDescription(stream)
self.assertIn("Step 3", stream.GetData())
def test_progress_finalize_non_deterministic_progress(self):
"""Test SBProgress finalize sends the progressEnd event"""
progress = lldb.SBProgress("Test SBProgress", "Test finalize", self.dbg)
listener = lldb.SBListener("Test listener")
broadcaster = self.dbg.GetBroadcaster()
broadcaster.AddListener(listener, lldb.eBroadcastBitExternalProgressCategory)
event = lldb.SBEvent()
progress.Finalize()
self.assertTrue(listener.WaitForEvent(5, event))
stream = lldb.SBStream()
event.GetDescription(stream)
self.assertIn("type = end", stream.GetData())
def test_progress_finalize_deterministic_progress(self):
"""Test SBProgress finalize sends the progressEnd event"""
progress = lldb.SBProgress("Test SBProgress", "Test finalize", 13, self.dbg)
listener = lldb.SBListener("Test listener")
broadcaster = self.dbg.GetBroadcaster()
broadcaster.AddListener(listener, lldb.eBroadcastBitExternalProgressCategory)
event = lldb.SBEvent()
progress.Finalize()
self.assertTrue(listener.WaitForEvent(5, event))
stream = lldb.SBStream()
event.GetDescription(stream)
# Note even for progresses with a total, the total isn't
# sent in the end message.
self.assertIn("type = end", stream.GetData())