Files
clang-p2996/lldb/unittests/ValueObject/DumpValueObjectOptionsTests.cpp
Jonas Devlieghere b852fb1ec5 [lldb] Move ValueObject into its own library (NFC) (#113393)
ValueObject is part of lldbCore for historical reasons, but conceptually
it deserves to be its own library. This does introduce a (link-time) circular
dependency between lldbCore and lldbValueObject, which is unfortunate
but probably unavoidable because so many things in LLDB rely on
ValueObject. We already have cycles and these libraries are never built
as dylibs so while this doesn't improve the situation, it also doesn't
make things worse.

The header includes were updated with the following command:

```
find . -type f -exec sed -i.bak "s%include \"lldb/Core/ValueObject%include \"lldb/ValueObject/ValueObject%" '{}' \;
```
2024-10-24 20:20:48 -07:00

198 lines
8.0 KiB
C++

//===-- DumpValueObjectOptionsTests.cpp -----------------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
#include "Plugins/Platform/Linux/PlatformLinux.h"
#include "Plugins/ScriptInterpreter/None/ScriptInterpreterNone.h"
#include "Plugins/TypeSystem/Clang/TypeSystemClang.h"
#include "TestingSupport/SubsystemRAII.h"
#include "TestingSupport/Symbol/ClangTestUtils.h"
#include "lldb/Core/Debugger.h"
#include "lldb/DataFormatters/DumpValueObjectOptions.h"
#include "lldb/ValueObject/ValueObject.h"
#include "lldb/ValueObject/ValueObjectConstResult.h"
#include "gtest/gtest.h"
#include <type_traits>
using namespace lldb;
using namespace lldb_private;
struct MockProcess : Process {
MockProcess(lldb::TargetSP target_sp, lldb::ListenerSP listener_sp)
: Process(target_sp, listener_sp) {}
llvm::StringRef GetPluginName() override { return "mock process"; }
bool CanDebug(lldb::TargetSP target, bool plugin_specified_by_name) override {
return false;
};
Status DoDestroy() override { return {}; }
void RefreshStateAfterStop() override {}
bool DoUpdateThreadList(ThreadList &old_thread_list,
ThreadList &new_thread_list) override {
return false;
};
size_t DoReadMemory(lldb::addr_t vm_addr, void *buf, size_t size,
Status &error) override {
// No need to read memory in these tests.
return size;
}
};
class ValueObjectMockProcessTest : public ::testing::Test {
public:
void SetUp() override {
ArchSpec arch("i386-pc-linux");
Platform::SetHostPlatform(
platform_linux::PlatformLinux::CreateInstance(true, &arch));
m_debugger_sp = Debugger::CreateInstance();
ASSERT_TRUE(m_debugger_sp);
m_debugger_sp->GetTargetList().CreateTarget(*m_debugger_sp, "", arch,
eLoadDependentsNo,
m_platform_sp, m_target_sp);
ASSERT_TRUE(m_target_sp);
ASSERT_TRUE(m_target_sp->GetArchitecture().IsValid());
ASSERT_TRUE(m_platform_sp);
m_listener_sp = Listener::MakeListener("dummy");
m_process_sp = std::make_shared<MockProcess>(m_target_sp, m_listener_sp);
ASSERT_TRUE(m_process_sp);
m_exe_ctx = ExecutionContext(m_process_sp);
m_holder = std::make_unique<clang_utils::TypeSystemClangHolder>("test");
m_type_system = m_holder->GetAST();
}
template <typename UnderlyingType>
void TestDumpEnum(
const std::vector<std::pair<const char *, UnderlyingType>> enumerators,
const std::vector<std::tuple<UnderlyingType, DumpValueObjectOptions,
const char *>> &tests) {
CompilerType enum_type = MakeEnumType(enumerators);
StreamString strm;
ConstString var_name("test_var");
ByteOrder endian = endian::InlHostByteOrder();
ExecutionContextScope *exe_scope = m_exe_ctx.GetBestExecutionContextScope();
for (auto [value, options, expected] : tests) {
DataExtractor data_extractor{&value, sizeof(value), endian, 4};
auto valobj_sp = ValueObjectConstResult::Create(exe_scope, enum_type,
var_name, data_extractor);
if (llvm::Error error = valobj_sp->Dump(strm, options))
llvm::consumeError(std::move(error));
ASSERT_STREQ(strm.GetString().str().c_str(), expected);
strm.Clear();
}
}
template <typename UnderlyingType>
CompilerType MakeEnumType(
const std::vector<std::pair<const char *, UnderlyingType>> enumerators) {
CompilerType int_type = m_type_system->GetBuiltinTypeForEncodingAndBitSize(
std::is_same<UnderlyingType, int>::value ? lldb::eEncodingSint
: lldb::eEncodingUint,
32);
CompilerType enum_type = m_type_system->CreateEnumerationType(
"TestEnum", m_type_system->GetTranslationUnitDecl(),
OptionalClangModuleID(), Declaration(), int_type, false);
m_type_system->StartTagDeclarationDefinition(enum_type);
Declaration decl;
for (auto [name, value] : enumerators)
m_type_system->AddEnumerationValueToEnumerationType(enum_type, decl, name,
value, 32);
m_type_system->CompleteTagDeclarationDefinition(enum_type);
return enum_type;
}
ExecutionContext m_exe_ctx;
TypeSystemClang *m_type_system;
private:
SubsystemRAII<FileSystem, HostInfo, platform_linux::PlatformLinux,
ScriptInterpreterNone>
m_subsystems;
std::unique_ptr<clang_utils::TypeSystemClangHolder> m_holder;
lldb::DebuggerSP m_debugger_sp;
lldb::TargetSP m_target_sp;
lldb::PlatformSP m_platform_sp;
lldb::ListenerSP m_listener_sp;
lldb::ProcessSP m_process_sp;
};
TEST_F(ValueObjectMockProcessTest, EmptyEnum) {
// All values of an empty enum should be shown as plain numbers.
TestDumpEnum<unsigned>({}, {{0, {}, "(TestEnum) test_var = 0\n"},
{1, {}, "(TestEnum) test_var = 1\n"},
{2, {}, "(TestEnum) test_var = 2\n"}});
TestDumpEnum<int>({}, {{-2, {}, "(TestEnum) test_var = -2\n"},
{-1, {}, "(TestEnum) test_var = -1\n"},
{0, {}, "(TestEnum) test_var = 0\n"},
{1, {}, "(TestEnum) test_var = 1\n"},
{2, {}, "(TestEnum) test_var = 2\n"}});
}
TEST_F(ValueObjectMockProcessTest, Enum) {
// This is not a bitfield-like enum, so values are printed as decimal by
// default. Also we only show the enumerator name if the value is an
// exact match.
TestDumpEnum<unsigned>(
{{"test_2", 2}, {"test_3", 3}},
{{0, {}, "(TestEnum) test_var = 0\n"},
{1, {}, "(TestEnum) test_var = 1\n"},
{2, {}, "(TestEnum) test_var = test_2\n"},
{3, {}, "(TestEnum) test_var = test_3\n"},
{4, {}, "(TestEnum) test_var = 4\n"},
{5, {}, "(TestEnum) test_var = 5\n"},
{1, DumpValueObjectOptions().SetHideRootName(true), "(TestEnum) 1\n"},
{1, DumpValueObjectOptions().SetHideRootType(true), "test_var = 1\n"},
{1, DumpValueObjectOptions().SetHideRootName(true).SetHideRootType(true),
"1\n"},
{1, DumpValueObjectOptions().SetHideName(true), "(TestEnum) 1\n"},
{1, DumpValueObjectOptions().SetHideValue(true),
"(TestEnum) test_var =\n"},
{1, DumpValueObjectOptions().SetHideName(true).SetHideValue(true),
"(TestEnum) \n"}});
}
TEST_F(ValueObjectMockProcessTest, BitFieldLikeEnum) {
// These enumerators set individual bits in the value, as if it were a flag
// set. lldb treats this as a "bitfield like enum". This means we show values
// as hex, and values without exact matches are shown as a combination of
// enumerators and any remaining value left over.
TestDumpEnum<unsigned>(
{{"test_2", 2}, {"test_4", 4}},
{
{0, {}, "(TestEnum) test_var = 0x0\n"},
{1, {}, "(TestEnum) test_var = 0x1\n"},
{2, {}, "(TestEnum) test_var = test_2\n"},
{4, {}, "(TestEnum) test_var = test_4\n"},
{6, {}, "(TestEnum) test_var = test_2 | test_4\n"},
{7, {}, "(TestEnum) test_var = test_2 | test_4 | 0x1\n"},
{8, {}, "(TestEnum) test_var = 0x8\n"},
{1, DumpValueObjectOptions().SetHideRootName(true),
"(TestEnum) 0x1\n"},
{1, DumpValueObjectOptions().SetHideRootType(true),
"test_var = 0x1\n"},
{1,
DumpValueObjectOptions().SetHideRootName(true).SetHideRootType(true),
"0x1\n"},
{1, DumpValueObjectOptions().SetHideName(true), "(TestEnum) 0x1\n"},
{1, DumpValueObjectOptions().SetHideValue(true),
"(TestEnum) test_var =\n"},
{1, DumpValueObjectOptions().SetHideName(true).SetHideValue(true),
"(TestEnum) \n"},
});
}