#include "Plugins/Platform/MacOSX/PlatformMacOSX.h"
#include "Plugins/Platform/MacOSX/PlatformRemoteMacOSX.h"
#include "TestingSupport/SubsystemRAII.h"
#include "TestingSupport/TestUtilities.h"
#include "lldb/Core/Debugger.h"
#include "lldb/Core/Progress.h"
#include "lldb/Host/FileSystem.h"
#include "lldb/Host/HostInfo.h"
#include "lldb/Utility/Listener.h"
#include "gtest/gtest.h"
#include <memory>
#include <mutex>
#include <thread>
using namespace lldb;
using namespace lldb_private;
static std::chrono::milliseconds TIMEOUT(500);
class ProgressReportTest : public ::testing::Test {
public:
ListenerSP CreateListenerFor(uint32_t bit) {
ArchSpec arch("x86_64-apple-macosx-");
Platform::SetHostPlatform(
PlatformRemoteMacOSX::CreateInstance(true, &arch));
m_debugger_sp = Debugger::CreateInstance();
Broadcaster &broadcaster = m_debugger_sp->GetBroadcaster();
m_listener_sp = Listener::MakeListener("progress-listener");
m_listener_sp->StartListeningForEvents(&broadcaster, bit);
return m_listener_sp;
}
protected:
void SetUp() override {
std::call_once(TestUtilities::g_debugger_initialize_flag,
[]() { Debugger::Initialize(nullptr); });
};
DebuggerSP m_debugger_sp;
ListenerSP m_listener_sp;
SubsystemRAII<FileSystem, HostInfo, PlatformMacOSX> subsystems;
};
TEST_F(ProgressReportTest, TestReportCreation) {
ListenerSP listener_sp = CreateListenerFor(lldb::eBroadcastBitProgress);
EventSP event_sp;
const ProgressEventData *data;
{
Progress progress1("Progress report 1", "Starting report 1");
Progress progress2("Progress report 2", "Starting report 2");
Progress progress3("Progress report 3", "Starting report 3");
}
ASSERT_TRUE(listener_sp->GetEvent(event_sp, TIMEOUT));
data = ProgressEventData::GetEventDataFromEvent(event_sp.get());
EXPECT_EQ(data->GetDetails(), "Starting report 1");
EXPECT_FALSE(data->IsFinite());
EXPECT_FALSE(data->GetCompleted());
EXPECT_EQ(data->GetTotal(), Progress::kNonDeterministicTotal);
EXPECT_EQ(data->GetMessage(), "Progress report 1: Starting report 1");
ASSERT_TRUE(listener_sp->GetEvent(event_sp, TIMEOUT));
data = ProgressEventData::GetEventDataFromEvent(event_sp.get());
EXPECT_EQ(data->GetDetails(), "Starting report 2");
EXPECT_FALSE(data->IsFinite());
EXPECT_FALSE(data->GetCompleted());
EXPECT_EQ(data->GetTotal(), Progress::kNonDeterministicTotal);
EXPECT_EQ(data->GetMessage(), "Progress report 2: Starting report 2");
ASSERT_TRUE(listener_sp->GetEvent(event_sp, TIMEOUT));
data = ProgressEventData::GetEventDataFromEvent(event_sp.get());
EXPECT_EQ(data->GetDetails(), "Starting report 3");
EXPECT_FALSE(data->IsFinite());
EXPECT_FALSE(data->GetCompleted());
EXPECT_EQ(data->GetTotal(), Progress::kNonDeterministicTotal);
EXPECT_EQ(data->GetMessage(), "Progress report 3: Starting report 3");
ASSERT_TRUE(listener_sp->GetEvent(event_sp, TIMEOUT));
data = ProgressEventData::GetEventDataFromEvent(event_sp.get());
EXPECT_EQ(data->GetTitle(), "Progress report 3");
EXPECT_TRUE(data->GetCompleted());
EXPECT_FALSE(data->IsFinite());
EXPECT_EQ(data->GetMessage(), "Progress report 3: Starting report 3");
ASSERT_TRUE(listener_sp->GetEvent(event_sp, TIMEOUT));
data = ProgressEventData::GetEventDataFromEvent(event_sp.get());
EXPECT_EQ(data->GetTitle(), "Progress report 2");
EXPECT_TRUE(data->GetCompleted());
EXPECT_FALSE(data->IsFinite());
EXPECT_EQ(data->GetMessage(), "Progress report 2: Starting report 2");
ASSERT_TRUE(listener_sp->GetEvent(event_sp, TIMEOUT));
data = ProgressEventData::GetEventDataFromEvent(event_sp.get());
EXPECT_EQ(data->GetTitle(), "Progress report 1");
EXPECT_TRUE(data->GetCompleted());
EXPECT_FALSE(data->IsFinite());
EXPECT_EQ(data->GetMessage(), "Progress report 1: Starting report 1");
}
TEST_F(ProgressReportTest, TestReportDestructionWithPartialProgress) {
ListenerSP listener_sp = CreateListenerFor(lldb::eBroadcastBitProgress);
EventSP event_sp;
const ProgressEventData *data;
{
Progress progress("Finite progress", "Report 1", 100);
progress.Increment(3);
}
ASSERT_TRUE(listener_sp->GetEvent(event_sp, TIMEOUT));
data = ProgressEventData::GetEventDataFromEvent(event_sp.get());
EXPECT_EQ(data->GetDetails(), "Report 1");
EXPECT_TRUE(data->IsFinite());
EXPECT_EQ(data->GetCompleted(), (uint64_t)0);
EXPECT_EQ(data->GetTotal(), (uint64_t)100);
EXPECT_EQ(data->GetMessage(), "Finite progress: Report 1");
ASSERT_TRUE(listener_sp->GetEvent(event_sp, TIMEOUT));
data = ProgressEventData::GetEventDataFromEvent(event_sp.get());
EXPECT_EQ(data->GetDetails(), "Report 1");
EXPECT_TRUE(data->IsFinite());
EXPECT_EQ(data->GetCompleted(), (uint64_t)3);
EXPECT_EQ(data->GetTotal(), (uint64_t)100);
EXPECT_EQ(data->GetMessage(), "Finite progress: Report 1");
ASSERT_TRUE(listener_sp->GetEvent(event_sp, TIMEOUT));
data = ProgressEventData::GetEventDataFromEvent(event_sp.get());
EXPECT_EQ(data->GetDetails(), "Report 1");
EXPECT_TRUE(data->IsFinite());
EXPECT_EQ(data->GetCompleted(), (uint64_t)100);
EXPECT_EQ(data->GetTotal(), (uint64_t)100);
EXPECT_EQ(data->GetMessage(), "Finite progress: Report 1");
{
Progress progress("Infinite progress", "Report 2");
progress.Increment(3);
}
ASSERT_TRUE(listener_sp->GetEvent(event_sp, TIMEOUT));
data = ProgressEventData::GetEventDataFromEvent(event_sp.get());
EXPECT_EQ(data->GetDetails(), "Report 2");
EXPECT_FALSE(data->IsFinite());
EXPECT_EQ(data->GetCompleted(), (uint64_t)0);
EXPECT_EQ(data->GetTotal(), Progress::kNonDeterministicTotal);
EXPECT_EQ(data->GetMessage(), "Infinite progress: Report 2");
ASSERT_TRUE(listener_sp->GetEvent(event_sp, TIMEOUT));
data = ProgressEventData::GetEventDataFromEvent(event_sp.get());
EXPECT_EQ(data->GetDetails(), "Report 2");
EXPECT_FALSE(data->IsFinite());
EXPECT_EQ(data->GetCompleted(), (uint64_t)3);
EXPECT_EQ(data->GetTotal(), Progress::kNonDeterministicTotal);
EXPECT_EQ(data->GetMessage(), "Infinite progress: Report 2");
ASSERT_TRUE(listener_sp->GetEvent(event_sp, TIMEOUT));
data = ProgressEventData::GetEventDataFromEvent(event_sp.get());
EXPECT_EQ(data->GetDetails(), "Report 2");
EXPECT_FALSE(data->IsFinite());
EXPECT_EQ(data->GetCompleted(), Progress::kNonDeterministicTotal);
EXPECT_EQ(data->GetTotal(), Progress::kNonDeterministicTotal);
EXPECT_EQ(data->GetMessage(), "Infinite progress: Report 2");
}
TEST_F(ProgressReportTest, TestFiniteOverflow) {
ListenerSP listener_sp = CreateListenerFor(lldb::eBroadcastBitProgress);
EventSP event_sp;
const ProgressEventData *data;
{
Progress progress("Finite progress", "Report 1", 10);
progress.Increment(11);
progress.Increment(47);
}
ASSERT_TRUE(listener_sp->GetEvent(event_sp, TIMEOUT));
data = ProgressEventData::GetEventDataFromEvent(event_sp.get());
EXPECT_TRUE(data->IsFinite());
EXPECT_EQ(data->GetCompleted(), 0U);
EXPECT_EQ(data->GetTotal(), 10U);
ASSERT_TRUE(listener_sp->GetEvent(event_sp, TIMEOUT));
data = ProgressEventData::GetEventDataFromEvent(event_sp.get());
EXPECT_TRUE(data->IsFinite());
EXPECT_EQ(data->GetCompleted(), 10U);
EXPECT_EQ(data->GetTotal(), 10U);
ASSERT_FALSE(listener_sp->GetEvent(event_sp, TIMEOUT));
}
TEST_F(ProgressReportTest, TestNonDeterministicOverflow) {
ListenerSP listener_sp = CreateListenerFor(lldb::eBroadcastBitProgress);
EventSP event_sp;
const ProgressEventData *data;
constexpr uint64_t max_minus_1 = std::numeric_limits<uint64_t>::max() - 1;
{
Progress progress("Non deterministic progress", "Report 1");
progress.Increment(max_minus_1);
progress.Increment(max_minus_1);
}
ASSERT_TRUE(listener_sp->GetEvent(event_sp, TIMEOUT));
data = ProgressEventData::GetEventDataFromEvent(event_sp.get());
EXPECT_FALSE(data->IsFinite());
EXPECT_EQ(data->GetCompleted(), 0U);
EXPECT_EQ(data->GetTotal(), Progress::kNonDeterministicTotal);
ASSERT_TRUE(listener_sp->GetEvent(event_sp, TIMEOUT));
data = ProgressEventData::GetEventDataFromEvent(event_sp.get());
EXPECT_FALSE(data->IsFinite());
EXPECT_EQ(data->GetCompleted(), max_minus_1);
EXPECT_EQ(data->GetTotal(), Progress::kNonDeterministicTotal);
ASSERT_TRUE(listener_sp->GetEvent(event_sp, TIMEOUT));
data = ProgressEventData::GetEventDataFromEvent(event_sp.get());
EXPECT_FALSE(data->IsFinite());
EXPECT_EQ(data->GetCompleted(), Progress::kNonDeterministicTotal);
EXPECT_EQ(data->GetTotal(), Progress::kNonDeterministicTotal);
ASSERT_FALSE(listener_sp->GetEvent(event_sp, TIMEOUT));
}
TEST_F(ProgressReportTest, TestMinimumReportTime) {
ListenerSP listener_sp = CreateListenerFor(lldb::eBroadcastBitProgress);
EventSP event_sp;
const ProgressEventData *data;
{
Progress progress("Finite progress", "Report 1", 20,
m_debugger_sp.get(),
std::chrono::seconds(1));
for (int i = 0; i < 10; ++i)
progress.Increment();
std::this_thread::sleep_for(std::chrono::seconds(1));
for (int i = 0; i < 10; ++i)
progress.Increment();
}
ASSERT_TRUE(listener_sp->GetEvent(event_sp, TIMEOUT));
data = ProgressEventData::GetEventDataFromEvent(event_sp.get());
EXPECT_TRUE(data->IsFinite());
EXPECT_EQ(data->GetCompleted(), 0U);
EXPECT_EQ(data->GetTotal(), 20U);
ASSERT_TRUE(listener_sp->GetEvent(event_sp, TIMEOUT));
data = ProgressEventData::GetEventDataFromEvent(event_sp.get());
EXPECT_TRUE(data->IsFinite());
EXPECT_EQ(data->GetCompleted(), 11U);
EXPECT_EQ(data->GetTotal(), 20U);
ASSERT_TRUE(listener_sp->GetEvent(event_sp, TIMEOUT));
data = ProgressEventData::GetEventDataFromEvent(event_sp.get());
EXPECT_TRUE(data->IsFinite());
EXPECT_EQ(data->GetCompleted(), 20U);
EXPECT_EQ(data->GetTotal(), 20U);
ASSERT_FALSE(listener_sp->GetEvent(event_sp, TIMEOUT));
}
TEST_F(ProgressReportTest, TestExternalReportCreation) {
ListenerSP listener_sp =
CreateListenerFor(lldb::eBroadcastBitExternalProgress);
EventSP event_sp;
const ProgressEventData *data;
{
Progress progress1("Progress report 1", "Starting report 1",
std::nullopt, nullptr,
std::chrono::seconds(0),
Progress::Origin::eExternal);
Progress progress2("Progress report 2", "Starting report 2",
std::nullopt, nullptr,
std::chrono::seconds(0),
Progress::Origin::eExternal);
Progress progress3("Progress report 3", "Starting report 3",
std::nullopt, nullptr,
std::chrono::seconds(0),
Progress::Origin::eExternal);
}
ASSERT_TRUE(listener_sp->GetEvent(event_sp, TIMEOUT));
data = ProgressEventData::GetEventDataFromEvent(event_sp.get());
EXPECT_EQ(data->GetDetails(), "Starting report 1");
EXPECT_FALSE(data->IsFinite());
EXPECT_FALSE(data->GetCompleted());
EXPECT_EQ(data->GetTotal(), Progress::kNonDeterministicTotal);
EXPECT_EQ(data->GetMessage(), "Progress report 1: Starting report 1");
ASSERT_TRUE(listener_sp->GetEvent(event_sp, TIMEOUT));
data = ProgressEventData::GetEventDataFromEvent(event_sp.get());
EXPECT_EQ(data->GetDetails(), "Starting report 2");
EXPECT_FALSE(data->IsFinite());
EXPECT_FALSE(data->GetCompleted());
EXPECT_EQ(data->GetTotal(), Progress::kNonDeterministicTotal);
EXPECT_EQ(data->GetMessage(), "Progress report 2: Starting report 2");
ASSERT_TRUE(listener_sp->GetEvent(event_sp, TIMEOUT));
data = ProgressEventData::GetEventDataFromEvent(event_sp.get());
EXPECT_EQ(data->GetDetails(), "Starting report 3");
EXPECT_FALSE(data->IsFinite());
EXPECT_FALSE(data->GetCompleted());
EXPECT_EQ(data->GetTotal(), Progress::kNonDeterministicTotal);
EXPECT_EQ(data->GetMessage(), "Progress report 3: Starting report 3");
ASSERT_TRUE(listener_sp->GetEvent(event_sp, TIMEOUT));
data = ProgressEventData::GetEventDataFromEvent(event_sp.get());
EXPECT_EQ(data->GetTitle(), "Progress report 3");
EXPECT_TRUE(data->GetCompleted());
EXPECT_FALSE(data->IsFinite());
EXPECT_EQ(data->GetMessage(), "Progress report 3: Starting report 3");
ASSERT_TRUE(listener_sp->GetEvent(event_sp, TIMEOUT));
data = ProgressEventData::GetEventDataFromEvent(event_sp.get());
EXPECT_EQ(data->GetTitle(), "Progress report 2");
EXPECT_TRUE(data->GetCompleted());
EXPECT_FALSE(data->IsFinite());
EXPECT_EQ(data->GetMessage(), "Progress report 2: Starting report 2");
ASSERT_TRUE(listener_sp->GetEvent(event_sp, TIMEOUT));
data = ProgressEventData::GetEventDataFromEvent(event_sp.get());
EXPECT_EQ(data->GetTitle(), "Progress report 1");
EXPECT_TRUE(data->GetCompleted());
EXPECT_FALSE(data->IsFinite());
EXPECT_EQ(data->GetMessage(), "Progress report 1: Starting report 1");
}
TEST_F(ProgressReportTest, TestExternalReportNotReceived) {
ListenerSP listener_sp = CreateListenerFor(lldb::eBroadcastBitProgress);
EventSP event_sp;
{
Progress progress1("External Progress report 1",
"Starting external report 1",
std::nullopt, nullptr,
std::chrono::seconds(0),
Progress::Origin::eExternal);
}
ASSERT_FALSE(listener_sp->GetEvent(event_sp, TIMEOUT));
}