//===-- ProtocolRequestsTest.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 "Protocol/ProtocolRequests.h" #include "Protocol/ProtocolTypes.h" #include "TestingSupport/TestUtilities.h" #include "llvm/Testing/Support/Error.h" #include #include using namespace llvm; using namespace lldb_dap::protocol; using namespace lldb_dap; using lldb_private::PrettyPrint; using llvm::json::parse; TEST(ProtocolRequestsTest, ExceptionInfoArguments) { llvm::Expected expected = parse(R"({ "threadId": 3434 })"); ASSERT_THAT_EXPECTED(expected, llvm::Succeeded()); EXPECT_EQ(expected->threadId, 3434U); // Check required keys; EXPECT_THAT_EXPECTED(parse(R"({})"), FailedWithMessage("missing value at (root).threadId")); EXPECT_THAT_EXPECTED(parse(R"({"id": 10})"), FailedWithMessage("missing value at (root).threadId")); } TEST(ProtocolRequestsTest, ExceptionInfoResponseBody) { ExceptionInfoResponseBody body; body.exceptionId = "signal"; body.breakMode = eExceptionBreakModeAlways; // Check required keys. Expected expected = parse( R"({ "exceptionId": "signal", "breakMode": "always" })"); ASSERT_THAT_EXPECTED(expected, llvm::Succeeded()); EXPECT_EQ(PrettyPrint(*expected), PrettyPrint(body)); // Check optional keys. body.description = "SIGNAL SIGWINCH"; body.breakMode = eExceptionBreakModeNever; body.details = ExceptionDetails{}; body.details->message = "some message"; Expected expected_opt = parse( R"({ "exceptionId": "signal", "description": "SIGNAL SIGWINCH", "breakMode": "never", "details": { "message": "some message" } })"); ASSERT_THAT_EXPECTED(expected_opt, llvm::Succeeded()); EXPECT_EQ(PrettyPrint(*expected_opt), PrettyPrint(body)); } TEST(ProtocolRequestsTest, EvaluateArguments) { llvm::Expected expected = parse(R"({ "expression": "hello world", "context": "repl" })"); ASSERT_THAT_EXPECTED(expected, llvm::Succeeded()); EXPECT_EQ(expected->expression, "hello world"); EXPECT_EQ(expected->context, eEvaluateContextRepl); // Check required keys. EXPECT_THAT_EXPECTED(parse(R"({})"), FailedWithMessage("missing value at (root).expression")); } TEST(ProtocolRequestsTest, EvaluateResponseBody) { EvaluateResponseBody body; body.result = "hello world"; body.variablesReference = var_ref_t(7); // Check required keys. Expected expected = parse(R"({ "result": "hello world", "variablesReference": 7 })"); ASSERT_THAT_EXPECTED(expected, llvm::Succeeded()); EXPECT_EQ(PrettyPrint(*expected), PrettyPrint(body)); // Check optional keys. body.result = "'abc'"; body.type = "string"; body.variablesReference = var_ref_t(42); body.namedVariables = 1; body.indexedVariables = 2; body.memoryReference = "0x123"; body.valueLocationReference = 22; Expected expected_opt = parse(R"({ "result": "'abc'", "type": "string", "variablesReference": 42, "namedVariables": 1, "indexedVariables": 2, "memoryReference": "0x123", "valueLocationReference": 22 })"); ASSERT_THAT_EXPECTED(expected_opt, llvm::Succeeded()); EXPECT_EQ(PrettyPrint(*expected_opt), PrettyPrint(body)); } TEST(ProtocolRequestsTest, InitializeRequestArguments) { llvm::Expected expected = parse(R"({"adapterID": "myid"})"); ASSERT_THAT_EXPECTED(expected, llvm::Succeeded()); EXPECT_EQ(expected->adapterID, "myid"); // Check optional keys. expected = parse(R"({ "adapterID": "myid", "clientID": "myclientid", "clientName": "lldb-dap-unit-tests", "locale": "en-US", "linesStartAt1": true, "columnsStartAt1": true, "pathFormat": "uri", "supportsVariableType": true, "supportsVariablePaging": true, "supportsRunInTerminalRequest": true, "supportsMemoryReferences": true, "supportsProgressReporting": true, "supportsInvalidatedEvent": true, "supportsMemoryEvent": true, "supportsArgsCanBeInterpretedByShell": true, "supportsStartDebuggingRequest": true, "supportsANSIStyling": true })"); ASSERT_THAT_EXPECTED(expected, llvm::Succeeded()); EXPECT_EQ(expected->adapterID, "myid"); EXPECT_EQ(expected->clientID, "myclientid"); EXPECT_EQ(expected->clientName, "lldb-dap-unit-tests"); EXPECT_EQ(expected->locale, "en-US"); EXPECT_EQ(expected->linesStartAt1, true); EXPECT_EQ(expected->columnsStartAt1, true); EXPECT_EQ(expected->pathFormat, ePathFormatURI); EXPECT_EQ(expected->supportedFeatures.contains(eClientFeatureVariableType), true); EXPECT_EQ( expected->supportedFeatures.contains(eClientFeatureRunInTerminalRequest), true); EXPECT_EQ( expected->supportedFeatures.contains(eClientFeatureMemoryReferences), true); EXPECT_EQ( expected->supportedFeatures.contains(eClientFeatureProgressReporting), true); EXPECT_EQ( expected->supportedFeatures.contains(eClientFeatureInvalidatedEvent), true); EXPECT_EQ(expected->supportedFeatures.contains(eClientFeatureMemoryEvent), true); EXPECT_EQ(expected->supportedFeatures.contains( eClientFeatureArgsCanBeInterpretedByShell), true); EXPECT_EQ( expected->supportedFeatures.contains(eClientFeatureStartDebuggingRequest), true); EXPECT_EQ(expected->supportedFeatures.contains(eClientFeatureANSIStyling), true); // Check required keys. EXPECT_THAT_EXPECTED(parse(R"({})"), FailedWithMessage("missing value at (root).adapterID")); } TEST(ProtocolRequestsTest, PauseRequestArguments) { llvm::Expected expected = parse(R"({"threadId": 123})"); ASSERT_THAT_EXPECTED(expected, llvm::Succeeded()); EXPECT_EQ(expected->threadId, 123U); // Check required keys. EXPECT_THAT_EXPECTED(parse(R"({})"), FailedWithMessage("missing value at (root).threadId")); } TEST(ProtocolRequestsTest, LocationsArguments) { llvm::Expected expected = parse(R"({"locationReference": 123})"); ASSERT_THAT_EXPECTED(expected, llvm::Succeeded()); EXPECT_EQ(expected->locationReference, 123U); // Check required keys. EXPECT_THAT_EXPECTED( parse(R"({})"), FailedWithMessage("missing value at (root).locationReference")); } TEST(ProtocolRequestsTest, LocationsResponseBody) { LocationsResponseBody body; body.source.sourceReference = 123; body.source.name = "test.cpp"; body.line = 42; // Check required keys. Expected expected = parse(R"({ "source": { "sourceReference": 123, "name": "test.cpp" }, "line": 42 })"); ASSERT_THAT_EXPECTED(expected, llvm::Succeeded()); EXPECT_EQ(PrettyPrint(*expected), PrettyPrint(body)); // Check optional keys. body.column = 2; body.endLine = 43; body.endColumn = 4; expected = parse(R"({ "source": { "sourceReference": 123, "name": "test.cpp" }, "line": 42, "column": 2, "endLine": 43, "endColumn": 4 })"); ASSERT_THAT_EXPECTED(expected, llvm::Succeeded()); EXPECT_EQ(PrettyPrint(*expected), PrettyPrint(body)); } TEST(ProtocolRequestsTest, CompileUnitsArguments) { llvm::Expected expected = parse(R"({"moduleId": "42"})"); ASSERT_THAT_EXPECTED(expected, llvm::Succeeded()); EXPECT_EQ(expected->moduleId, "42"); // Check required keys. EXPECT_THAT_EXPECTED(parse(R"({})"), FailedWithMessage("missing value at (root).moduleId")); } TEST(ProtocolRequestsTest, CompileUnitsResponseBody) { CompileUnitsResponseBody body; body.compileUnits = {{"main.cpp"}, {"util.cpp"}}; // Check required keys. Expected expected = parse(R"({ "compileUnits": [ { "compileUnitPath": "main.cpp" }, { "compileUnitPath": "util.cpp" } ] })"); ASSERT_THAT_EXPECTED(expected, llvm::Succeeded()); EXPECT_EQ(PrettyPrint(*expected), PrettyPrint(body)); } TEST(ProtocolRequestsTest, TestGetTargetBreakpointsResponseBody) { Breakpoint breakpoint1; breakpoint1.id = 1; breakpoint1.verified = true; Breakpoint breakpoint2; breakpoint2.id = 2; breakpoint2.verified = false; breakpoint2.message = "Failed to set breakpoint"; TestGetTargetBreakpointsResponseBody body; body.breakpoints = {breakpoint1, breakpoint2}; // Check required keys. Expected expected = parse(R"({ "breakpoints": [ { "id": 1, "verified": true }, { "id": 2, "verified": false, "message": "Failed to set breakpoint" } ] })"); ASSERT_THAT_EXPECTED(expected, llvm::Succeeded()); EXPECT_EQ(PrettyPrint(*expected), PrettyPrint(body)); } TEST(ProtocolRequestsTest, RestartArguments) { llvm::Expected expected = parse(R"({})"); ASSERT_THAT_EXPECTED(expected, llvm::Succeeded()); EXPECT_TRUE(std::holds_alternative(expected->arguments)); // Check missed keys. expected = parse(R"({ "arguments": {} })"); EXPECT_THAT_EXPECTED(expected, FailedWithMessage("failed to parse arguments, expected " "`launch` or `attach` arguments")); // Check launch arguments. expected = parse(R"({ "arguments": { "program": "main.exe", "cwd": "/home/root" } })"); ASSERT_THAT_EXPECTED(expected, llvm::Succeeded()); const LaunchRequestArguments *launch_args = std::get_if(&expected->arguments); EXPECT_NE(launch_args, nullptr); EXPECT_EQ(launch_args->configuration.program, "main.exe"); EXPECT_EQ(launch_args->cwd, "/home/root"); // Check attach arguments. expected = parse(R"({ "arguments": { "pid": 123 } })"); ASSERT_THAT_EXPECTED(expected, llvm::Succeeded()); const AttachRequestArguments *attach_args = std::get_if(&expected->arguments); EXPECT_NE(attach_args, nullptr); EXPECT_EQ(attach_args->pid, 123U); } TEST(ProtocolRequestsTest, StackTraceArguments) { llvm::Expected expected = parse(R"({ "threadId": 42, "startFrame": 1, "levels": 10, "format": { "parameters": true, "line": true } })"); ASSERT_THAT_EXPECTED(expected, llvm::Succeeded()); EXPECT_EQ(expected->threadId, 42U); EXPECT_EQ(expected->startFrame, 1U); EXPECT_EQ(expected->levels, 10U); EXPECT_EQ(expected->format->parameters, true); EXPECT_EQ(expected->format->line, true); // Check required keys. EXPECT_THAT_EXPECTED(parse(R"({})"), FailedWithMessage("missing value at (root).threadId")); } TEST(ProtocolRequestsTest, StackTraceResponseBody) { StackFrame frame1; frame1.id = 1; frame1.name = "main"; frame1.source = Source{}; frame1.source->name = "main.cpp"; frame1.source->sourceReference = 123; frame1.line = 23; frame1.column = 1; StackFrame frame2; frame2.id = 2; frame2.name = "test"; frame2.presentationHint = StackFrame::ePresentationHintLabel; StackTraceResponseBody body; body.stackFrames = {frame1, frame2}; body.totalFrames = 2; // Check required keys. Expected expected = parse(R"({ "stackFrames": [ { "id": 1, "name": "main", "source": { "name": "main.cpp", "sourceReference": 123 }, "line": 23, "column": 1 }, { "id": 2, "name": "test", "line": 0, "column": 0, "presentationHint": "label" } ], "totalFrames": 2 })"); ASSERT_THAT_EXPECTED(expected, llvm::Succeeded()); EXPECT_EQ(PrettyPrint(*expected), PrettyPrint(body)); } TEST(ProtocolRequestsTest, SetVariableArguments) { llvm::Expected expected = parse(R"({ "variablesReference": 42, "name": "test", "value": "12345" })"); ASSERT_THAT_EXPECTED(expected, llvm::Succeeded()); EXPECT_EQ(expected->variablesReference.AsUInt32(), 42U); EXPECT_EQ(expected->name, "test"); EXPECT_EQ(expected->value, "12345"); EXPECT_EQ(expected->format, std::nullopt); // Check required keys. EXPECT_THAT_EXPECTED( parse(R"({})"), FailedWithMessage("missing value at (root).variablesReference")); } TEST(ProtocolRequestsTest, CancelRequestArguments) { llvm::Expected expected = parse(R"({ "requestId": 42, "progressId": "abc" })"); ASSERT_THAT_EXPECTED(expected, llvm::Succeeded()); EXPECT_EQ(expected->requestId, 42U); EXPECT_EQ(expected->progressId, "abc"); expected = parse(R"({ "requestId": 42 })"); ASSERT_THAT_EXPECTED(expected, llvm::Succeeded()); EXPECT_EQ(expected->requestId, 42U); EXPECT_TRUE(expected->progressId.empty()); expected = parse(R"({ "progressId": "abc" })"); ASSERT_THAT_EXPECTED(expected, llvm::Succeeded()); EXPECT_EQ(expected->requestId, 0u); EXPECT_EQ(expected->progressId, "abc"); // Check an empty message, all keys are optional. EXPECT_THAT_EXPECTED(parse(R"({})"), Succeeded()); }