Skip to content

[lldb-dap] Add unit test for ColumnDescriptor, BreakpointMode and Breakpoint #139627

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
May 13, 2025

Conversation

JDevlieghere
Copy link
Member

Implement fromJSON for ColumnDescriptor, BreakpointMode and Breakpoint and use it to implement the corresponding unit tests.

…akpoint

Implement fromJSON for ColumnDescriptor, BreakpointMode and Breakpoint
and use it to implement the corresponding unit tests.
@llvmbot
Copy link
Member

llvmbot commented May 12, 2025

@llvm/pr-subscribers-lldb

Author: Jonas Devlieghere (JDevlieghere)

Changes

Implement fromJSON for ColumnDescriptor, BreakpointMode and Breakpoint and use it to implement the corresponding unit tests.


Full diff: https://github.com/llvm/llvm-project/pull/139627.diff

3 Files Affected:

  • (modified) lldb/tools/lldb-dap/Protocol/ProtocolTypes.cpp (+96)
  • (modified) lldb/tools/lldb-dap/Protocol/ProtocolTypes.h (+8)
  • (modified) lldb/unittests/DAP/ProtocolTypesTest.cpp (+72)
diff --git a/lldb/tools/lldb-dap/Protocol/ProtocolTypes.cpp b/lldb/tools/lldb-dap/Protocol/ProtocolTypes.cpp
index c9cab350f9f12..7c2f4b20f4956 100644
--- a/lldb/tools/lldb-dap/Protocol/ProtocolTypes.cpp
+++ b/lldb/tools/lldb-dap/Protocol/ProtocolTypes.cpp
@@ -94,6 +94,28 @@ json::Value toJSON(const ExceptionBreakpointsFilter &EBF) {
   return result;
 }
 
+bool fromJSON(const json::Value &Params, ColumnType &CT, json::Path P) {
+  auto rawColumnType = Params.getAsString();
+  if (!rawColumnType) {
+    P.report("expected a string");
+    return false;
+  }
+  std::optional<ColumnType> columnType =
+      StringSwitch<std::optional<ColumnType>>(*rawColumnType)
+          .Case("string", eColumnTypeString)
+          .Case("number", eColumnTypeNumber)
+          .Case("boolean", eColumnTypeBoolean)
+          .Case("unixTimestampUTC ", eColumnTypeTimestamp)
+          .Default(std::nullopt);
+  if (!columnType) {
+    P.report("unexpected value, expected 'string', 'number',  'boolean', or "
+             "'unixTimestampUTC'");
+    return false;
+  }
+  CT = *columnType;
+  return true;
+}
+
 json::Value toJSON(const ColumnType &T) {
   switch (T) {
   case eColumnTypeString:
@@ -108,6 +130,14 @@ json::Value toJSON(const ColumnType &T) {
   llvm_unreachable("unhandled column type.");
 }
 
+bool fromJSON(const llvm::json::Value &Params, ColumnDescriptor &CD,
+              llvm::json::Path P) {
+  llvm::json::ObjectMapper O(Params, P);
+  return O && O.map("attributeName", CD.attributeName) &&
+         O.map("label", CD.label) && O.mapOptional("format", CD.format) &&
+         O.mapOptional("type", CD.type) && O.mapOptional("width", CD.width);
+}
+
 json::Value toJSON(const ColumnDescriptor &CD) {
   json::Object result{{"attributeName", CD.attributeName}, {"label", CD.label}};
 
@@ -149,6 +179,30 @@ json::Value toJSON(const BreakpointModeApplicability &BMA) {
   llvm_unreachable("unhandled breakpoint mode applicability.");
 }
 
+bool fromJSON(const llvm::json::Value &Params, BreakpointModeApplicability &BMA,
+              llvm::json::Path P) {
+  auto rawApplicability = Params.getAsString();
+  if (!rawApplicability) {
+    P.report("expected a string");
+    return false;
+  }
+  std::optional<BreakpointModeApplicability> applicability =
+      llvm::StringSwitch<std::optional<BreakpointModeApplicability>>(
+          *rawApplicability)
+          .Case("source", eBreakpointModeApplicabilitySource)
+          .Case("exception", eBreakpointModeApplicabilityException)
+          .Case("data", eBreakpointModeApplicabilityData)
+          .Case("instruction", eBreakpointModeApplicabilityInstruction)
+          .Default(std::nullopt);
+  if (!applicability) {
+    P.report("unexpected value, expected 'source', 'exception', 'data', or "
+             "'instruction'");
+    return false;
+  }
+  BMA = *applicability;
+  return true;
+}
+
 json::Value toJSON(const BreakpointMode &BM) {
   json::Object result{
       {"mode", BM.mode},
@@ -162,6 +216,14 @@ json::Value toJSON(const BreakpointMode &BM) {
   return result;
 }
 
+bool fromJSON(const llvm::json::Value &Params, BreakpointMode &BM,
+              llvm::json::Path P) {
+  llvm::json::ObjectMapper O(Params, P);
+  return O && O.map("mode", BM.mode) && O.map("label", BM.label) &&
+         O.mapOptional("description", BM.description) &&
+         O.map("appliesTo", BM.appliesTo);
+}
+
 static llvm::StringLiteral ToString(AdapterFeature feature) {
   switch (feature) {
   case eAdapterFeatureANSIStyling:
@@ -320,6 +382,26 @@ llvm::json::Value toJSON(const BreakpointReason &BR) {
   llvm_unreachable("unhandled breakpoint reason.");
 }
 
+bool fromJSON(const llvm::json::Value &Params, BreakpointReason &BR,
+              llvm::json::Path P) {
+  auto rawReason = Params.getAsString();
+  if (!rawReason) {
+    P.report("expected a string");
+    return false;
+  }
+  std::optional<BreakpointReason> reason =
+      llvm::StringSwitch<std::optional<BreakpointReason>>(*rawReason)
+          .Case("pending", BreakpointReason::eBreakpointReasonPending)
+          .Case("failed", BreakpointReason::eBreakpointReasonFailed)
+          .Default(std::nullopt);
+  if (!reason) {
+    P.report("unexpected value, expected 'pending' or 'failed'");
+    return false;
+  }
+  BR = *reason;
+  return true;
+}
+
 json::Value toJSON(const Breakpoint &BP) {
   json::Object result{{"verified", BP.verified}};
 
@@ -348,6 +430,20 @@ json::Value toJSON(const Breakpoint &BP) {
   return result;
 }
 
+bool fromJSON(const llvm::json::Value &Params, Breakpoint &BP,
+              llvm::json::Path P) {
+  llvm::json::ObjectMapper O(Params, P);
+  return O && O.mapOptional("id", BP.id) && O.map("verified", BP.verified) &&
+         O.mapOptional("message", BP.message) &&
+         O.mapOptional("source", BP.source) && O.mapOptional("line", BP.line) &&
+         O.mapOptional("column", BP.column) &&
+         O.mapOptional("endLine", BP.endLine) &&
+         O.mapOptional("endColumn", BP.endColumn) &&
+         O.mapOptional("instructionReference", BP.instructionReference) &&
+         O.mapOptional("offset", BP.offset) &&
+         O.mapOptional("reason", BP.reason);
+}
+
 bool fromJSON(const llvm::json::Value &Params, SourceBreakpoint &SB,
               llvm::json::Path P) {
   json::ObjectMapper O(Params, P);
diff --git a/lldb/tools/lldb-dap/Protocol/ProtocolTypes.h b/lldb/tools/lldb-dap/Protocol/ProtocolTypes.h
index d1e86b0897675..cab188637acd5 100644
--- a/lldb/tools/lldb-dap/Protocol/ProtocolTypes.h
+++ b/lldb/tools/lldb-dap/Protocol/ProtocolTypes.h
@@ -65,6 +65,8 @@ enum ColumnType : unsigned {
   eColumnTypeBoolean,
   eColumnTypeTimestamp
 };
+bool fromJSON(const llvm::json::Value &, ColumnType &, llvm::json::Path);
+llvm::json::Value toJSON(const ColumnType &);
 
 /// A ColumnDescriptor specifies what module attribute to show in a column of
 /// the modules view, how to format it, and what the column’s label should be.
@@ -89,6 +91,7 @@ struct ColumnDescriptor {
   /// Width of this column in characters (hint only).
   std::optional<int> width;
 };
+bool fromJSON(const llvm::json::Value &, ColumnDescriptor &, llvm::json::Path);
 llvm::json::Value toJSON(const ColumnDescriptor &);
 
 /// Names of checksum algorithms that may be supported by a debug adapter.
@@ -114,6 +117,8 @@ enum BreakpointModeApplicability : unsigned {
   /// In `InstructionBreakpoint`'s.
   eBreakpointModeApplicabilityInstruction
 };
+bool fromJSON(const llvm::json::Value &, BreakpointModeApplicability &,
+              llvm::json::Path);
 llvm::json::Value toJSON(const BreakpointModeApplicability &);
 
 /// A `BreakpointMode` is provided as a option when setting breakpoints on
@@ -133,6 +138,7 @@ struct BreakpointMode {
   /// Describes one or more type of breakpoint this mode applies to.
   std::vector<BreakpointModeApplicability> appliesTo;
 };
+bool fromJSON(const llvm::json::Value &, BreakpointMode &, llvm::json::Path);
 llvm::json::Value toJSON(const BreakpointMode &);
 
 /// Debug Adapter Features flags supported by lldb-dap.
@@ -364,6 +370,7 @@ enum class BreakpointReason : unsigned {
   /// adapter does not believe it can be verified without intervention.
   eBreakpointReasonFailed,
 };
+bool fromJSON(const llvm::json::Value &, BreakpointReason &, llvm::json::Path);
 llvm::json::Value toJSON(const BreakpointReason &);
 
 /// Information about a breakpoint created in `setBreakpoints`,
@@ -415,6 +422,7 @@ struct Breakpoint {
   /// should omit this property.
   std::optional<BreakpointReason> reason;
 };
+bool fromJSON(const llvm::json::Value &, Breakpoint &, llvm::json::Path);
 llvm::json::Value toJSON(const Breakpoint &);
 
 /// Properties of a breakpoint or logpoint passed to the `setBreakpoints`
diff --git a/lldb/unittests/DAP/ProtocolTypesTest.cpp b/lldb/unittests/DAP/ProtocolTypesTest.cpp
index fa46816ca4a10..56b21f18fa7cd 100644
--- a/lldb/unittests/DAP/ProtocolTypesTest.cpp
+++ b/lldb/unittests/DAP/ProtocolTypesTest.cpp
@@ -60,3 +60,75 @@ TEST(ProtocolTypesTest, Source) {
   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 = roundtrip(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 = roundtrip(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, ePresentationHintNormal};
+  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 = roundtrip(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);
+}

@JDevlieghere
Copy link
Member Author

JDevlieghere commented May 13, 2025

low-priority ping (I'm doing this in the background and dealing with stacked PRs is a nuisance)

Copy link
Contributor

@ashgti ashgti left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM!

@JDevlieghere JDevlieghere merged commit 8193294 into llvm:main May 13, 2025
13 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants