#include "Protocol/ProtocolTypes.h"
#include "Protocol/ProtocolEvents.h"
#include "Protocol/ProtocolRequests.h"
#include "TestingSupport/TestUtilities.h"
#include "llvm/ADT/StringRef.h"
#include "llvm/Support/JSON.h"
#include "llvm/Testing/Support/Error.h"
#include "gmock/gmock.h"
#include "gtest/gtest.h"
#include <optional>
using namespace llvm;
using namespace lldb;
using namespace lldb_dap;
using namespace lldb_dap::protocol;
using lldb_private::roundtripJSON;
using llvm::json::parse;
using llvm::json::Value;
static std::string pp(const json::Value &E) {
return formatv("{0:2}", E).str();
}
TEST(ProtocolTypesTest, ExceptionBreakpointsFilter) {
ExceptionBreakpointsFilter filter;
filter.filter = "testFilter";
filter.label = "Test Filter";
filter.description = "This is a test filter";
filter.defaultState = true;
filter.supportsCondition = true;
filter.conditionDescription = "Condition for test filter";
llvm::Expected<ExceptionBreakpointsFilter> deserialized_filter =
roundtripJSON(filter);
ASSERT_THAT_EXPECTED(deserialized_filter, llvm::Succeeded());
EXPECT_EQ(filter.filter, deserialized_filter->filter);
EXPECT_EQ(filter.label, deserialized_filter->label);
EXPECT_EQ(filter.description, deserialized_filter->description);
EXPECT_EQ(filter.defaultState, deserialized_filter->defaultState);
EXPECT_EQ(filter.supportsCondition, deserialized_filter->supportsCondition);
EXPECT_EQ(filter.conditionDescription,
deserialized_filter->conditionDescription);
}
TEST(ProtocolTypesTest, Source) {
Source source;
source.name = "testName";
source.path = "/path/to/source";
source.sourceReference = 12345;
source.presentationHint = Source::eSourcePresentationHintEmphasize;
llvm::Expected<Source> deserialized_source = roundtripJSON(source);
ASSERT_THAT_EXPECTED(deserialized_source, llvm::Succeeded());
EXPECT_EQ(source.name, deserialized_source->name);
EXPECT_EQ(source.path, deserialized_source->path);
EXPECT_EQ(source.sourceReference, deserialized_source->sourceReference);
EXPECT_EQ(source.presentationHint, deserialized_source->presentationHint);
}
TEST(ProtocolTypesTest, ColumnDescriptor) {
ColumnDescriptor column;
column.attributeName = "moduleName";
column.label = "Module Name";
column.format = "uppercase";
column.type = eColumnTypeString;
column.width = 20;
llvm::Expected<ColumnDescriptor> deserialized_column = roundtripJSON(column);
ASSERT_THAT_EXPECTED(deserialized_column, llvm::Succeeded());
EXPECT_EQ(column.attributeName, deserialized_column->attributeName);
EXPECT_EQ(column.label, deserialized_column->label);
EXPECT_EQ(column.format, deserialized_column->format);
EXPECT_EQ(column.type, deserialized_column->type);
EXPECT_EQ(column.width, deserialized_column->width);
}
TEST(ProtocolTypesTest, BreakpointMode) {
BreakpointMode mode;
mode.mode = "testMode";
mode.label = "Test Mode";
mode.description = "This is a test mode";
mode.appliesTo = {eBreakpointModeApplicabilitySource,
eBreakpointModeApplicabilityException};
llvm::Expected<BreakpointMode> deserialized_mode = roundtripJSON(mode);
ASSERT_THAT_EXPECTED(deserialized_mode, llvm::Succeeded());
EXPECT_EQ(mode.mode, deserialized_mode->mode);
EXPECT_EQ(mode.label, deserialized_mode->label);
EXPECT_EQ(mode.description, deserialized_mode->description);
EXPECT_EQ(mode.appliesTo, deserialized_mode->appliesTo);
}
TEST(ProtocolTypesTest, Breakpoint) {
Breakpoint breakpoint;
breakpoint.id = 42;
breakpoint.verified = true;
breakpoint.message = "Breakpoint set successfully";
breakpoint.source =
Source{"test.cpp", "/path/to/test.cpp", 123,
Source::eSourcePresentationHintNormal, std::nullopt};
breakpoint.line = 10;
breakpoint.column = 5;
breakpoint.endLine = 15;
breakpoint.endColumn = 10;
breakpoint.instructionReference = "0x12345678";
breakpoint.offset = 4;
breakpoint.reason = BreakpointReason::eBreakpointReasonPending;
llvm::Expected<Breakpoint> deserialized_breakpoint =
roundtripJSON(breakpoint);
ASSERT_THAT_EXPECTED(deserialized_breakpoint, llvm::Succeeded());
EXPECT_EQ(breakpoint.id, deserialized_breakpoint->id);
EXPECT_EQ(breakpoint.verified, deserialized_breakpoint->verified);
EXPECT_EQ(breakpoint.message, deserialized_breakpoint->message);
EXPECT_EQ(breakpoint.source->name, deserialized_breakpoint->source->name);
EXPECT_EQ(breakpoint.source->path, deserialized_breakpoint->source->path);
EXPECT_EQ(breakpoint.source->sourceReference,
deserialized_breakpoint->source->sourceReference);
EXPECT_EQ(breakpoint.source->presentationHint,
deserialized_breakpoint->source->presentationHint);
EXPECT_EQ(breakpoint.line, deserialized_breakpoint->line);
EXPECT_EQ(breakpoint.column, deserialized_breakpoint->column);
EXPECT_EQ(breakpoint.endLine, deserialized_breakpoint->endLine);
EXPECT_EQ(breakpoint.endColumn, deserialized_breakpoint->endColumn);
EXPECT_EQ(breakpoint.instructionReference,
deserialized_breakpoint->instructionReference);
EXPECT_EQ(breakpoint.offset, deserialized_breakpoint->offset);
EXPECT_EQ(breakpoint.reason, deserialized_breakpoint->reason);
}
TEST(ProtocolTypesTest, SourceBreakpoint) {
SourceBreakpoint source_breakpoint;
source_breakpoint.line = 42;
source_breakpoint.column = 5;
source_breakpoint.condition = "x > 10";
source_breakpoint.hitCondition = "5";
source_breakpoint.logMessage = "Breakpoint hit at line 42";
source_breakpoint.mode = "hardware";
llvm::Expected<SourceBreakpoint> deserialized_source_breakpoint =
roundtripJSON(source_breakpoint);
ASSERT_THAT_EXPECTED(deserialized_source_breakpoint, llvm::Succeeded());
EXPECT_EQ(source_breakpoint.line, deserialized_source_breakpoint->line);
EXPECT_EQ(source_breakpoint.column, deserialized_source_breakpoint->column);
EXPECT_EQ(source_breakpoint.condition,
deserialized_source_breakpoint->condition);
EXPECT_EQ(source_breakpoint.hitCondition,
deserialized_source_breakpoint->hitCondition);
EXPECT_EQ(source_breakpoint.logMessage,
deserialized_source_breakpoint->logMessage);
EXPECT_EQ(source_breakpoint.mode, deserialized_source_breakpoint->mode);
}
TEST(ProtocolTypesTest, FunctionBreakpoint) {
FunctionBreakpoint function_breakpoint;
function_breakpoint.name = "myFunction";
function_breakpoint.condition = "x == 0";
function_breakpoint.hitCondition = "3";
llvm::Expected<FunctionBreakpoint> deserialized_function_breakpoint =
roundtripJSON(function_breakpoint);
ASSERT_THAT_EXPECTED(deserialized_function_breakpoint, llvm::Succeeded());
EXPECT_EQ(function_breakpoint.name, deserialized_function_breakpoint->name);
EXPECT_EQ(function_breakpoint.condition,
deserialized_function_breakpoint->condition);
EXPECT_EQ(function_breakpoint.hitCondition,
deserialized_function_breakpoint->hitCondition);
}
TEST(ProtocolTypesTest, DataBreakpoint) {
DataBreakpoint data_breakpoint_info;
data_breakpoint_info.dataId = "variable1";
data_breakpoint_info.accessType = eDataBreakpointAccessTypeReadWrite;
data_breakpoint_info.condition = "x > 100";
data_breakpoint_info.hitCondition = "10";
llvm::Expected<DataBreakpoint> deserialized_data_breakpoint_info =
roundtripJSON(data_breakpoint_info);
ASSERT_THAT_EXPECTED(deserialized_data_breakpoint_info, llvm::Succeeded());
EXPECT_EQ(data_breakpoint_info.dataId,
deserialized_data_breakpoint_info->dataId);
EXPECT_EQ(data_breakpoint_info.accessType,
deserialized_data_breakpoint_info->accessType);
EXPECT_EQ(data_breakpoint_info.condition,
deserialized_data_breakpoint_info->condition);
EXPECT_EQ(data_breakpoint_info.hitCondition,
deserialized_data_breakpoint_info->hitCondition);
}
TEST(ProtocolTypesTest, Capabilities) {
Capabilities capabilities;
capabilities.supportedFeatures.insert(eAdapterFeatureANSIStyling);
capabilities.supportedFeatures.insert(
eAdapterFeatureBreakpointLocationsRequest);
capabilities.exceptionBreakpointFilters = {
{{"filter1", "Filter 1", "Description 1", true, true, "Condition 1"},
{"filter2", "Filter 2", "Description 2", false, false, "Condition 2"}}};
capabilities.completionTriggerCharacters = {".", "->"};
capabilities.additionalModuleColumns = {
{"moduleName", "Module Name", "uppercase", eColumnTypeString, 20}};
capabilities.supportedChecksumAlgorithms = {eChecksumAlgorithmMD5,
eChecksumAlgorithmSHA256};
capabilities.breakpointModes = {{"hardware",
"Hardware Breakpoint",
"Description",
{eBreakpointModeApplicabilitySource}}};
capabilities.lldbExtVersion = "1.0.0";
llvm::Expected<Capabilities> deserialized_capabilities =
roundtripJSON(capabilities);
ASSERT_THAT_EXPECTED(deserialized_capabilities, llvm::Succeeded());
EXPECT_EQ(capabilities.supportedFeatures,
deserialized_capabilities->supportedFeatures);
EXPECT_EQ(capabilities.exceptionBreakpointFilters.size(),
deserialized_capabilities->exceptionBreakpointFilters.size());
for (size_t i = 0; i < capabilities.exceptionBreakpointFilters.size(); ++i) {
const auto &original = capabilities.exceptionBreakpointFilters.at(i);
const auto &deserialized =
deserialized_capabilities->exceptionBreakpointFilters.at(i);
EXPECT_EQ(original.filter, deserialized.filter);
EXPECT_EQ(original.label, deserialized.label);
EXPECT_EQ(original.description, deserialized.description);
EXPECT_EQ(original.defaultState, deserialized.defaultState);
EXPECT_EQ(original.supportsCondition, deserialized.supportsCondition);
EXPECT_EQ(original.conditionDescription, deserialized.conditionDescription);
}
EXPECT_EQ(capabilities.completionTriggerCharacters,
deserialized_capabilities->completionTriggerCharacters);
EXPECT_EQ(capabilities.additionalModuleColumns.size(),
deserialized_capabilities->additionalModuleColumns.size());
for (size_t i = 0; i < capabilities.additionalModuleColumns.size(); ++i) {
const auto &original = capabilities.additionalModuleColumns.at(i);
const auto &deserialized =
deserialized_capabilities->additionalModuleColumns.at(i);
EXPECT_EQ(original.attributeName, deserialized.attributeName);
EXPECT_EQ(original.label, deserialized.label);
EXPECT_EQ(original.format, deserialized.format);
EXPECT_EQ(original.type, deserialized.type);
EXPECT_EQ(original.width, deserialized.width);
}
EXPECT_EQ(capabilities.supportedChecksumAlgorithms,
deserialized_capabilities->supportedChecksumAlgorithms);
EXPECT_EQ(capabilities.breakpointModes.size(),
deserialized_capabilities->breakpointModes.size());
for (size_t i = 0; i < capabilities.breakpointModes.size(); ++i) {
const auto &original = capabilities.breakpointModes.at(i);
const auto &deserialized = deserialized_capabilities->breakpointModes.at(i);
EXPECT_EQ(original.mode, deserialized.mode);
EXPECT_EQ(original.label, deserialized.label);
EXPECT_EQ(original.description, deserialized.description);
EXPECT_EQ(original.appliesTo, deserialized.appliesTo);
}
EXPECT_EQ(capabilities.lldbExtVersion,
deserialized_capabilities->lldbExtVersion);
}
TEST(ProtocolTypesTest, Scope) {
Scope scope;
scope.name = "Locals";
scope.presentationHint = Scope::eScopePresentationHintLocals;
scope.variablesReference = 1;
scope.namedVariables = 2;
scope.indexedVariables = std::nullopt;
scope.expensive = false;
scope.line = 2;
scope.column = 3;
scope.endLine = 10;
scope.endColumn = 20;
Source source;
source.name = "testName";
source.path = "/path/to/source";
source.sourceReference = 12345;
source.presentationHint = Source::eSourcePresentationHintNormal;
scope.source = source;
llvm::Expected<Scope> deserialized_scope = roundtripJSON(scope);
ASSERT_THAT_EXPECTED(deserialized_scope, llvm::Succeeded());
EXPECT_EQ(scope.name, deserialized_scope->name);
EXPECT_EQ(scope.presentationHint, deserialized_scope->presentationHint);
EXPECT_EQ(scope.variablesReference, deserialized_scope->variablesReference);
EXPECT_EQ(scope.namedVariables, deserialized_scope->namedVariables);
EXPECT_EQ(scope.indexedVariables, deserialized_scope->indexedVariables);
EXPECT_EQ(scope.expensive, deserialized_scope->expensive);
EXPECT_EQ(scope.line, deserialized_scope->line);
EXPECT_EQ(scope.column, deserialized_scope->column);
EXPECT_EQ(scope.endLine, deserialized_scope->endLine);
EXPECT_EQ(scope.endColumn, deserialized_scope->endColumn);
EXPECT_THAT(deserialized_scope->source.has_value(), true);
const Source &deserialized_source = deserialized_scope->source.value();
EXPECT_EQ(source.path, deserialized_source.path);
EXPECT_EQ(source.sourceReference, deserialized_source.sourceReference);
EXPECT_EQ(source.presentationHint, deserialized_source.presentationHint);
}
TEST(ProtocolTypesTest, PresentationHint) {
std::vector<std::pair<Source::PresentationHint, llvm::StringRef>> test_cases =
{{Source::eSourcePresentationHintNormal, "normal"},
{Source::eSourcePresentationHintEmphasize, "emphasize"},
{Source::eSourcePresentationHintDeemphasize, "deemphasize"}};
for (const auto &test_case : test_cases) {
llvm::json::Value serialized = toJSON(test_case.first);
ASSERT_EQ(serialized.kind(), llvm::json::Value::Kind::String);
EXPECT_EQ(serialized.getAsString(), test_case.second);
Source::PresentationHint deserialized;
llvm::json::Path::Root root;
ASSERT_TRUE(fromJSON(serialized, deserialized, root))
<< llvm::toString(root.getError());
EXPECT_EQ(deserialized, test_case.first);
}
llvm::json::Value invalid_value = "invalid_hint";
Source::PresentationHint deserialized_invalid;
llvm::json::Path::Root root;
EXPECT_FALSE(fromJSON(invalid_value, deserialized_invalid, root));
}
TEST(ProtocolTypesTest, SteppingGranularity) {
std::vector<std::pair<SteppingGranularity, llvm::StringRef>> test_cases = {
{eSteppingGranularityStatement, "statement"},
{eSteppingGranularityLine, "line"},
{eSteppingGranularityInstruction, "instruction"}};
for (const auto &test_case : test_cases) {
llvm::json::Value serialized = toJSON(test_case.first);
ASSERT_EQ(serialized.kind(), llvm::json::Value::Kind::String);
EXPECT_EQ(serialized.getAsString(), test_case.second);
SteppingGranularity deserialized;
llvm::json::Path::Root root;
ASSERT_TRUE(fromJSON(serialized, deserialized, root))
<< llvm::toString(root.getError());
EXPECT_EQ(deserialized, test_case.first);
}
llvm::json::Value invalid_value = "invalid_granularity";
SteppingGranularity deserialized_invalid;
llvm::json::Path::Root root;
EXPECT_FALSE(fromJSON(invalid_value, deserialized_invalid, root));
}
TEST(ProtocolTypesTest, BreakpointReason) {
std::vector<std::pair<BreakpointReason, llvm::StringRef>> test_cases = {
{BreakpointReason::eBreakpointReasonPending, "pending"},
{BreakpointReason::eBreakpointReasonFailed, "failed"}};
for (const auto &test_case : test_cases) {
llvm::json::Value serialized = toJSON(test_case.first);
ASSERT_EQ(serialized.kind(), llvm::json::Value::Kind::String);
EXPECT_EQ(serialized.getAsString(), test_case.second);
BreakpointReason deserialized;
llvm::json::Path::Root root;
ASSERT_TRUE(fromJSON(serialized, deserialized, root))
<< llvm::toString(root.getError());
EXPECT_EQ(deserialized, test_case.first);
}
llvm::json::Value invalid_value = "invalid_reason";
BreakpointReason deserialized_invalid;
llvm::json::Path::Root root;
EXPECT_FALSE(fromJSON(invalid_value, deserialized_invalid, root));
}
TEST(ProtocolTypesTest, DataBreakpointAccessType) {
std::vector<std::pair<DataBreakpointAccessType, llvm::StringRef>> test_cases =
{{eDataBreakpointAccessTypeRead, "read"},
{eDataBreakpointAccessTypeWrite, "write"},
{eDataBreakpointAccessTypeReadWrite, "readWrite"}};
for (const auto &test_case : test_cases) {
llvm::json::Value serialized = toJSON(test_case.first);
ASSERT_EQ(serialized.kind(), llvm::json::Value::Kind::String);
EXPECT_EQ(serialized.getAsString(), test_case.second);
DataBreakpointAccessType deserialized;
llvm::json::Path::Root root;
ASSERT_TRUE(fromJSON(serialized, deserialized, root))
<< llvm::toString(root.getError());
EXPECT_EQ(deserialized, test_case.first);
}
llvm::json::Value invalid_value = "invalid_access_type";
DataBreakpointAccessType deserialized_invalid;
llvm::json::Path::Root root;
EXPECT_FALSE(fromJSON(invalid_value, deserialized_invalid, root));
}
TEST(ProtocolTypesTest, ColumnType) {
std::vector<std::pair<ColumnType, llvm::StringRef>> test_cases = {
{eColumnTypeString, "string"},
{eColumnTypeNumber, "number"},
{eColumnTypeBoolean, "boolean"},
{eColumnTypeTimestamp, "unixTimestampUTC"}};
for (const auto &test_case : test_cases) {
llvm::json::Value serialized = toJSON(test_case.first);
ASSERT_EQ(serialized.kind(), llvm::json::Value::Kind::String);
EXPECT_EQ(serialized.getAsString(), test_case.second);
ColumnType deserialized;
llvm::json::Path::Root root;
ASSERT_TRUE(fromJSON(serialized, deserialized, root))
<< llvm::toString(root.getError());
EXPECT_EQ(deserialized, test_case.first);
}
llvm::json::Value invalid_value = "invalid_column_type";
ColumnType deserialized_invalid;
llvm::json::Path::Root root;
EXPECT_FALSE(fromJSON(invalid_value, deserialized_invalid, root));
}
TEST(ProtocolTypesTest, BreakpointModeApplicability) {
std::vector<std::pair<BreakpointModeApplicability, llvm::StringRef>>
test_cases = {{eBreakpointModeApplicabilitySource, "source"},
{eBreakpointModeApplicabilityException, "exception"},
{eBreakpointModeApplicabilityData, "data"},
{eBreakpointModeApplicabilityInstruction, "instruction"}};
for (const auto &test_case : test_cases) {
llvm::json::Value serialized = toJSON(test_case.first);
ASSERT_EQ(serialized.kind(), llvm::json::Value::Kind::String);
EXPECT_EQ(serialized.getAsString(), test_case.second);
BreakpointModeApplicability deserialized;
llvm::json::Path::Root root;
ASSERT_TRUE(fromJSON(serialized, deserialized, root))
<< llvm::toString(root.getError());
EXPECT_EQ(deserialized, test_case.first);
}
llvm::json::Value invalid_value = "invalid_applicability";
BreakpointModeApplicability deserialized_invalid;
llvm::json::Path::Root root;
EXPECT_FALSE(fromJSON(invalid_value, deserialized_invalid, root));
}
TEST(ProtocolTypesTest, ChecksumAlgorithm) {
std::vector<std::pair<ChecksumAlgorithm, llvm::StringRef>> test_cases = {
{eChecksumAlgorithmMD5, "MD5"},
{eChecksumAlgorithmSHA1, "SHA1"},
{eChecksumAlgorithmSHA256, "SHA256"},
{eChecksumAlgorithmTimestamp, "timestamp"}};
for (const auto &test_case : test_cases) {
llvm::json::Value serialized = toJSON(test_case.first);
ASSERT_EQ(serialized.kind(), llvm::json::Value::Kind::String);
EXPECT_EQ(serialized.getAsString(), test_case.second);
ChecksumAlgorithm deserialized;
llvm::json::Path::Root root;
ASSERT_TRUE(fromJSON(serialized, deserialized, root))
<< llvm::toString(root.getError());
EXPECT_EQ(deserialized, test_case.first);
}
llvm::json::Value invalid_value = "invalid_algorithm";
ChecksumAlgorithm deserialized_invalid;
llvm::json::Path::Root root;
EXPECT_FALSE(fromJSON(invalid_value, deserialized_invalid, root));
}
TEST(ProtocolTypesTest, DisassembledInstructionPresentationHint) {
std::vector<
std::pair<DisassembledInstruction::PresentationHint, llvm::StringRef>>
test_cases = {{DisassembledInstruction::
eDisassembledInstructionPresentationHintNormal,
"normal"},
{DisassembledInstruction::
eDisassembledInstructionPresentationHintInvalid,
"invalid"}};
for (const auto &test_case : test_cases) {
llvm::json::Value serialized = toJSON(test_case.first);
ASSERT_EQ(serialized.kind(), llvm::json::Value::Kind::String);
EXPECT_EQ(serialized.getAsString(), test_case.second);
DisassembledInstruction::PresentationHint deserialized;
llvm::json::Path::Root root;
ASSERT_TRUE(fromJSON(serialized, deserialized, root))
<< llvm::toString(root.getError());
EXPECT_EQ(deserialized, test_case.first);
}
llvm::json::Value invalid_value = "invalid_hint";
DisassembledInstruction::PresentationHint deserialized_invalid;
llvm::json::Path::Root root;
EXPECT_FALSE(fromJSON(invalid_value, deserialized_invalid, root));
}
TEST(ProtocolTypesTest, DisassembledInstruction) {
DisassembledInstruction instruction;
instruction.address = 0x12345678;
instruction.instructionBytes = "0F 1F 00";
instruction.instruction = "mov eax, ebx";
instruction.symbol = "main";
instruction.location =
Source{"test.cpp", "/path/to/test.cpp", 123,
Source::eSourcePresentationHintNormal, std::nullopt};
instruction.line = 10;
instruction.column = 5;
instruction.endLine = 15;
instruction.endColumn = 10;
instruction.presentationHint =
DisassembledInstruction::eDisassembledInstructionPresentationHintNormal;
StringLiteral json = R"({
"address": "0x12345678",
"column": 5,
"endColumn": 10,
"endLine": 15,
"instruction": "mov eax, ebx",
"instructionBytes": "0F 1F 00",
"line": 10,
"location": {
"name": "test.cpp",
"path": "/path/to/test.cpp",
"presentationHint": "normal",
"sourceReference": 123
},
"presentationHint": "normal",
"symbol": "main"
})";
EXPECT_EQ(json, pp(instruction));
EXPECT_THAT_EXPECTED(parse<DisassembledInstruction>(json),
HasValue(Value(instruction)));
EXPECT_THAT_EXPECTED(
parse<DisassembledInstruction>(R"({"address":1})",
"disassemblyInstruction"),
FailedWithMessage("expected string at disassemblyInstruction.address"));
EXPECT_THAT_EXPECTED(
parse<DisassembledInstruction>(R"({"address":"-1"})",
"disassemblyInstruction"),
FailedWithMessage(
"malformed memory reference at disassemblyInstruction.address"));
EXPECT_THAT_EXPECTED(
parse<DisassembledInstruction>(
R"({"address":"0xfffffffffffffffffffffffffff"})",
"disassemblyInstruction"),
FailedWithMessage(
"malformed memory reference at disassemblyInstruction.address"));
}
TEST(ProtocolTypesTest, Thread) {
const Thread thread{1, "thr1"};
const StringRef json = R"({
"id": 1,
"name": "thr1"
})";
EXPECT_EQ(json, pp(thread));
EXPECT_THAT_EXPECTED(parse<Thread>(json), HasValue(Value(thread)));
EXPECT_THAT_EXPECTED(parse<Thread>(R"({"id":1})", "thread"),
FailedWithMessage("missing value at thread.name"));
EXPECT_THAT_EXPECTED(parse<Thread>(R"({"id":"one"})", "thread"),
FailedWithMessage("expected uint64_t at thread.id"));
EXPECT_THAT_EXPECTED(parse<Thread>(R"({"id":1,"name":false})", "thread"),
FailedWithMessage("expected string at thread.name"));
}
TEST(ProtocolTypesTest, ThreadResponseBody) {
const ThreadsResponseBody body{{{1, "thr1"}, {2, "thr2"}}};
const StringRef json = R"({
"threads": [
{
"id": 1,
"name": "thr1"
},
{
"id": 2,
"name": "thr2"
}
]
})";
EXPECT_EQ(json, pp(body));
}
TEST(ProtocolTypesTest, CapabilitiesEventBody) {
Capabilities capabilities;
capabilities.supportedFeatures = {
eAdapterFeatureANSIStyling,
eAdapterFeatureBreakpointLocationsRequest,
};
CapabilitiesEventBody body;
body.capabilities = capabilities;
StringRef json = R"({
"capabilities": {
"supportsANSIStyling": true,
"supportsBreakpointLocationsRequest": true
}
})";
EXPECT_EQ(json, pp(body));
}
TEST(ProtocolTypesTest, ExceptionFilterOptions) {
EXPECT_THAT_EXPECTED(parse<ExceptionFilterOptions>(R"({"filterId":"id"})"),
HasValue(Value(ExceptionFilterOptions{
"id", "", ""})));
EXPECT_THAT_EXPECTED(
parse<ExceptionFilterOptions>(R"({"filterId":"id","condition":"1+2"})"),
HasValue(Value(ExceptionFilterOptions{
"id", "1+2", ""})));
EXPECT_THAT_EXPECTED(
parse<ExceptionFilterOptions>(
R"({"filterId":"id","condition":"1+2","mode":"m"})"),
HasValue(Value(ExceptionFilterOptions{
"id", "1+2", "m"})));
EXPECT_THAT_EXPECTED(
parse<ExceptionFilterOptions>(R"({})", "exceptionFilterOptions"),
FailedWithMessage("missing value at exceptionFilterOptions.filterId"));
EXPECT_THAT_EXPECTED(
parse<ExceptionFilterOptions>(R"({"filterId":"id","condition":42})",
"exceptionFilterOptions"),
FailedWithMessage("expected string at exceptionFilterOptions.condition"));
EXPECT_THAT_EXPECTED(
parse<ExceptionFilterOptions>(R"({"filterId":"id","mode":42})",
"exceptionFilterOptions"),
FailedWithMessage("expected string at exceptionFilterOptions.mode"));
}
TEST(ProtocolTypesTest, SetExceptionBreakpointsArguments) {
EXPECT_THAT_EXPECTED(
parse<SetExceptionBreakpointsArguments>(R"({"filters":[]})"),
HasValue(testing::FieldsAre(testing::IsEmpty(),
testing::IsEmpty())));
EXPECT_THAT_EXPECTED(
parse<SetExceptionBreakpointsArguments>(R"({"filters":["abc"]})"),
HasValue(testing::FieldsAre(std::vector<std::string>{"abc"},
testing::IsEmpty())));
EXPECT_THAT_EXPECTED(
parse<SetExceptionBreakpointsArguments>(
R"({"filters":[],"filterOptions":[{"filterId":"abc"}]})"),
HasValue(testing::FieldsAre(
testing::IsEmpty(),
testing::Contains(testing::FieldsAre(
"abc", "", "")))));
EXPECT_THAT_EXPECTED(parse<SetExceptionBreakpointsArguments>(R"({})"),
FailedWithMessage("missing value at (root).filters"));
EXPECT_THAT_EXPECTED(
parse<SetExceptionBreakpointsArguments>(R"({"filters":false})"),
FailedWithMessage("expected array at (root).filters"));
}
TEST(ProtocolTypesTest, SetExceptionBreakpointsResponseBody) {
SetExceptionBreakpointsResponseBody body;
Breakpoint bp;
bp.id = 12, bp.verified = true;
body.breakpoints = {bp};
EXPECT_EQ(R"({
"breakpoints": [
{
"id": 12,
"verified": true
}
]
})",
pp(body));
}
TEST(ProtocolTypesTest, StepInTarget) {
StepInTarget target;
target.id = 230;
target.label = "the_function_name";
target.line = 2;
target.column = 320;
target.endLine = 32;
target.endColumn = 23;
llvm::Expected<StepInTarget> deserialized_target = roundtripJSON(target);
ASSERT_THAT_EXPECTED(deserialized_target, llvm::Succeeded());
EXPECT_EQ(target.id, deserialized_target->id);
EXPECT_EQ(target.label, deserialized_target->label);
EXPECT_EQ(target.line, deserialized_target->line);
EXPECT_EQ(target.column, deserialized_target->column);
EXPECT_EQ(target.endLine, deserialized_target->endLine);
EXPECT_EQ(target.endColumn, deserialized_target->endColumn);
}
TEST(ProtocolTypesTest, ReadMemoryArguments) {
ReadMemoryArguments args;
args.count = 20;
args.memoryReference = 43962;
args.offset = 0;
llvm::Expected<ReadMemoryArguments> expected =
parse<ReadMemoryArguments>(R"({"memoryReference":"-4000", "count": 20})");
ASSERT_THAT_EXPECTED(expected, llvm::Failed());
expected = parse<ReadMemoryArguments>(
R"({"memoryReference":"0xabba", "count": 20})");
ASSERT_THAT_EXPECTED(expected, llvm::Succeeded());
EXPECT_EQ(args.count, expected->count);
EXPECT_EQ(args.memoryReference, expected->memoryReference);
EXPECT_EQ(args.offset, expected->offset);
}
TEST(ProtocolTypesTest, ReadMemoryResponseBody) {
ReadMemoryResponseBody response;
response.address = 0xdeadbeef;
const std::string data_str = "hello world!";
std::transform(data_str.begin(), data_str.end(),
std::back_inserter(response.data),
[](char letter) { return std::byte(letter); });
response.unreadableBytes = 1;
Expected<Value> expected = json::parse(
R"({ "address": "0xDEADBEEF", "data": "aGVsbG8gd29ybGQh", "unreadableBytes": 1})");
ASSERT_THAT_EXPECTED(expected, llvm::Succeeded());
EXPECT_EQ(pp(*expected), pp(response));
}
TEST(ProtocolTypesTest, Modules) {
Module module;
module.id = "AC805E8E-B6A4-CD92-4B05-5CFA7CE24AE8-8926C776";
module.name = "libm.so.6";
module.path = "/some/path/to/libm.so.6";
module.isOptimized = true;
module.isUserCode = true;
module.version = "0.0.1";
module.symbolStatus = "Symbol not found.";
module.symbolFilePath = "/some/file/path/to/the/symbol/module";
module.dateTimeStamp = "2020-12-09T16:09:53+00:00";
module.addressRange = "0xcafeface";
module.debugInfoSizeBytes = 1572864;
Expected<json::Value> expected = json::parse(
R"({
"id" : "AC805E8E-B6A4-CD92-4B05-5CFA7CE24AE8-8926C776",
"name": "libm.so.6",
"path": "/some/path/to/libm.so.6",
"isOptimized": true,
"isUserCode": true,
"version": "0.0.1",
"symbolStatus": "Symbol not found.",
"symbolFilePath": "/some/file/path/to/the/symbol/module",
"dateTimeStamp": "2020-12-09T16:09:53+00:00",
"addressRange": "0xcafeface",
"debugInfoSize": "1.5MB" })");
ASSERT_THAT_EXPECTED(expected, llvm::Succeeded());
EXPECT_EQ(pp(*expected), pp(module));
module.path.clear();
module.isOptimized = false;
module.isUserCode = false;
module.version.clear();
module.symbolStatus.clear();
module.symbolFilePath.clear();
module.dateTimeStamp.clear();
module.addressRange.clear();
module.debugInfoSizeBytes = 0;
EXPECT_NE(pp(*expected), pp(module));
Expected<json::Value> expected_no_opt = json::parse(
R"({
"id" : "AC805E8E-B6A4-CD92-4B05-5CFA7CE24AE8-8926C776",
"name": "libm.so.6"})");
ASSERT_THAT_EXPECTED(expected_no_opt, llvm::Succeeded());
EXPECT_EQ(pp(*expected_no_opt), pp(module));
}
TEST(ProtocolTypesTest, ModulesArguments) {
ModulesArguments args;
llvm::Expected<ModulesArguments> expected = parse<ModulesArguments>(R"({})");
ASSERT_THAT_EXPECTED(expected, llvm::Succeeded());
EXPECT_EQ(args.startModule, expected->startModule);
EXPECT_EQ(args.moduleCount, expected->moduleCount);
args.startModule = 1;
args.moduleCount = 2;
llvm::Expected<ModulesArguments> expected_no_default =
parse<ModulesArguments>(R"({ "startModule": 1, "moduleCount": 2})");
ASSERT_THAT_EXPECTED(expected_no_default, llvm::Succeeded());
EXPECT_EQ(args.startModule, expected_no_default->startModule);
EXPECT_EQ(args.moduleCount, expected_no_default->moduleCount);
}
TEST(ProtocolTypesTest, ModulesResponseBody) {
ModulesResponseBody response;
Module module1;
module1.id = "first id";
module1.name = "first name";
Module module2;
module2.id = "second id";
module2.name = "second name";
response.modules = {std::move(module1), std::move(module2)};
response.totalModules = 2;
Expected<json::Value> expected = json::parse(
R"({
"modules": [
{ "id": "first id", "name": "first name"},
{ "id": "second id", "name": "second name"}
],
"totalModules": 2 })");
ASSERT_THAT_EXPECTED(expected, llvm::Succeeded());
EXPECT_EQ(pp(*expected), pp(response));
}
TEST(ProtocolTypesTest, VariablePresentationHint) {
VariablePresentationHint hint;
hint.kind = "kind";
hint.attributes = {"a", "b", "c"};
hint.visibility = "public";
hint.lazy = true;
const StringRef json = R"({
"attributes": [
"a",
"b",
"c"
],
"kind": "kind",
"lazy": true,
"visibility": "public"
})";
EXPECT_EQ(pp(Value(hint)), json);
EXPECT_THAT_EXPECTED(json::parse(json), HasValue(Value(hint)));
}
TEST(ProtocolTypesTest, Variable) {
Variable var;
var.name = "var1";
var.variablesReference = 42;
var.value = "value";
var.type = "type";
VariablePresentationHint hint;
hint.kind = "kind";
var.presentationHint = std::move(hint);
var.evaluateName = "my_name";
var.namedVariables = 7;
var.indexedVariables = 7;
var.memoryReference = 291u;
var.declarationLocationReference = 24;
var.valueLocationReference = 100;
const StringRef json = R"({
"declarationLocationReference": 24,
"evaluateName": "my_name",
"indexedVariables": 7,
"memoryReference": "0x123",
"name": "var1",
"namedVariables": 7,
"presentationHint": {
"kind": "kind"
},
"type": "type",
"value": "value",
"valueLocationReference": 100,
"variablesReference": 42
})";
EXPECT_EQ(pp(Value(var)), json);
EXPECT_THAT_EXPECTED(json::parse(json), HasValue(Value(var)));
}
TEST(ProtocolTypesTest, VariablesArguments) {
llvm::Expected<VariablesArguments> expected = parse<VariablesArguments>(R"({
"variablesReference": 42,
"filter": "indexed",
"start": 10,
"count": 5,
"format": {
"hex": true
}
})");
ASSERT_THAT_EXPECTED(expected, llvm::Succeeded());
EXPECT_EQ(expected->variablesReference, 42u);
EXPECT_EQ(expected->filter, VariablesArguments::eVariablesFilterIndexed);
EXPECT_EQ(expected->start, 10u);
EXPECT_EQ(expected->count, 5u);
EXPECT_EQ(expected->format->hex, true);
EXPECT_THAT_EXPECTED(
parse<VariablesArguments>(R"({})"),
FailedWithMessage("missing value at (root).variablesReference"));
EXPECT_THAT_EXPECTED(
parse<VariablesArguments>(
R"({"variablesReference": 42, "filter": "my-filter"})"),
FailedWithMessage(
"unexpected value, expected 'named' or 'indexed' at (root).filter"));
}
TEST(ProtocolTypesTest, VariablesResponseBody) {
Variable var1;
var1.name = "var1";
var1.variablesReference = 42;
var1.value = "<var1-value>";
Variable var2;
var2.name = "var2";
var2.variablesReference = 3;
var2.value = "<var2-value>";
VariablesResponseBody response{{var1, var2}};
Expected<json::Value> expected = json::parse(R"({
"variables": [
{
"name": "var1",
"value": "<var1-value>",
"variablesReference": 42
},
{
"name": "var2",
"value": "<var2-value>",
"variablesReference": 3
}
]
})");
ASSERT_THAT_EXPECTED(expected, llvm::Succeeded());
EXPECT_EQ(pp(*expected), pp(response));
}
TEST(ProtocolTypesTest, CompletionItem) {
CompletionItem item;
item.label = "label";
item.text = "text";
item.sortText = "sortText";
item.detail = "detail";
item.type = eCompletionItemTypeConstructor;
item.start = 1;
item.length = 3;
item.selectionStart = 4;
item.selectionLength = 8;
const StringRef json = R"({
"detail": "detail",
"label": "label",
"length": 3,
"selectionLength": 8,
"selectionStart": 4,
"sortText": "sortText",
"start": 1,
"text": "text",
"type": "constructor"
})";
EXPECT_EQ(pp(Value(item)), json);
EXPECT_THAT_EXPECTED(json::parse(json), HasValue(Value(item)));
}
TEST(ProtocolTypesTest, CompletionsArguments) {
llvm::Expected<CompletionsArguments> expected =
parse<CompletionsArguments>(R"({
"column": 8,
"frameId": 7,
"line": 9,
"text": "abc"
})");
ASSERT_THAT_EXPECTED(expected, llvm::Succeeded());
EXPECT_EQ(expected->frameId, 7u);
EXPECT_EQ(expected->text, "abc");
EXPECT_EQ(expected->column, 8);
EXPECT_EQ(expected->line, 9);
EXPECT_THAT_EXPECTED(parse<CompletionsArguments>(R"({})"),
FailedWithMessage("missing value at (root).text"));
EXPECT_THAT_EXPECTED(parse<CompletionsArguments>(R"({"text":"abc"})"),
FailedWithMessage("missing value at (root).column"));
}
TEST(ProtocolTypesTest, CompletionsResponseBody) {
CompletionItem item;
item.label = "label";
item.text = "text";
item.detail = "detail";
CompletionsResponseBody response{{item}};
Expected<json::Value> expected = json::parse(R"({
"targets": [
{
"detail": "detail",
"label": "label",
"text": "text"
}
]
})");
ASSERT_THAT_EXPECTED(expected, llvm::Succeeded());
EXPECT_EQ(pp(*expected), pp(response));
}
TEST(ProtocolTypesTest, InvalidatedEventBody) {
InvalidatedEventBody body;
body.areas = {InvalidatedEventBody::eAreaStacks,
InvalidatedEventBody::eAreaThreads};
body.stackFrameId = 1;
body.threadId = 20;
Expected<json::Value> expected = json::parse(R"({
"areas": [
"stacks",
"threads"
],
"stackFrameId": 1,
"threadId": 20
})");
ASSERT_THAT_EXPECTED(expected, llvm::Succeeded());
EXPECT_EQ(pp(*expected), pp(body));
}
TEST(ProtocolTypesTest, MemoryEventBody) {
MemoryEventBody body;
body.memoryReference = 12345;
body.offset = 0;
body.count = 4;
StringRef json = R"({
"count": 4,
"memoryReference": "0x3039",
"offset": 0
})";
EXPECT_EQ(json, pp(body));
}
TEST(ProtocolTypesTest, DataBreakpointInfoArguments) {
llvm::Expected<DataBreakpointInfoArguments> expected =
parse<DataBreakpointInfoArguments>(R"({
"name": "data",
"variablesReference": 8,
"frameId": 9,
"bytes": 10,
"asAddress": false,
"mode": "source"
})");
ASSERT_THAT_EXPECTED(expected, llvm::Succeeded());
EXPECT_EQ(expected->name, "data");
EXPECT_EQ(expected->variablesReference, 8);
EXPECT_EQ(expected->frameId, 9u);
EXPECT_EQ(expected->bytes, 10);
EXPECT_EQ(expected->asAddress, false);
EXPECT_EQ(expected->mode, "source");
EXPECT_THAT_EXPECTED(parse<DataBreakpointInfoArguments>(R"({})"),
FailedWithMessage("missing value at (root).name"));
EXPECT_THAT_EXPECTED(parse<DataBreakpointInfoArguments>(R"({"name":"data"})"),
llvm::Succeeded());
}
TEST(ProtocolTypesTest, ExceptionBreakMode) {
const std::vector<std::pair<ExceptionBreakMode, llvm::StringRef>> test_cases =
{{ExceptionBreakMode::eExceptionBreakModeAlways, "always"},
{ExceptionBreakMode::eExceptionBreakModeNever, "never"},
{ExceptionBreakMode::eExceptionBreakModeUnhandled, "unhandled"},
{ExceptionBreakMode::eExceptionBreakModeUserUnhandled, "userUnhandled"}};
for (const auto [value, expected] : test_cases) {
json::Value const serialized = toJSON(value);
ASSERT_EQ(serialized.kind(), llvm::json::Value::Kind::String);
EXPECT_EQ(serialized.getAsString(), expected);
}
}
TEST(ProtocolTypesTest, ExceptionDetails) {
ExceptionDetails details;
Expected<json::Value> expected = parse(R"({})");
ASSERT_THAT_EXPECTED(expected, llvm::Succeeded());
EXPECT_EQ(pp(*expected), pp(details));
details.message = "SIGABRT exception";
details.typeName = "signal";
details.fullTypeName = "SIGABRT";
details.evaluateName = "process handle SIGABRT";
details.stackTrace = "some stacktrace";
ExceptionDetails inner_details;
inner_details.message = "inner message";
details.innerException = {std::move(inner_details)};
Expected<json::Value> expected_opt = parse(R"({
"message": "SIGABRT exception",
"typeName": "signal",
"fullTypeName": "SIGABRT",
"evaluateName": "process handle SIGABRT",
"stackTrace": "some stacktrace",
"innerException": [{
"message": "inner message"
}]
})");
ASSERT_THAT_EXPECTED(expected_opt, llvm::Succeeded());
EXPECT_EQ(pp(*expected_opt), pp(details));
}