1 | //===-- ProtocolTypesTest.cpp -----------------------------------*- C++ -*-===// |
2 | // |
3 | // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. |
4 | // See https://llvm.org/LICENSE.txt for license information. |
5 | // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
6 | // |
7 | //===----------------------------------------------------------------------===// |
8 | |
9 | #include "Protocol/ProtocolTypes.h" |
10 | #include "Protocol/ProtocolEvents.h" |
11 | #include "Protocol/ProtocolRequests.h" |
12 | #include "llvm/ADT/StringRef.h" |
13 | #include "llvm/Support/JSON.h" |
14 | #include "llvm/Testing/Support/Error.h" |
15 | #include "gmock/gmock.h" |
16 | #include "gtest/gtest.h" |
17 | #include <optional> |
18 | |
19 | using namespace llvm; |
20 | using namespace lldb; |
21 | using namespace lldb_dap; |
22 | using namespace lldb_dap::protocol; |
23 | using llvm::json::parse; |
24 | using llvm::json::Value; |
25 | |
26 | /// Returns a pretty printed json string of a `llvm::json::Value`. |
27 | static std::string pp(const json::Value &E) { |
28 | return formatv(Fmt: "{0:2}" , Vals: E).str(); |
29 | } |
30 | |
31 | template <typename T> static llvm::Expected<T> roundtrip(const T &input) { |
32 | llvm::json::Value value = toJSON(input); |
33 | llvm::json::Path::Root root; |
34 | T output; |
35 | if (!fromJSON(value, output, root)) |
36 | return root.getError(); |
37 | return output; |
38 | } |
39 | |
40 | TEST(ProtocolTypesTest, ExceptionBreakpointsFilter) { |
41 | ExceptionBreakpointsFilter filter; |
42 | filter.filter = "testFilter" ; |
43 | filter.label = "Test Filter" ; |
44 | filter.description = "This is a test filter" ; |
45 | filter.defaultState = true; |
46 | filter.supportsCondition = true; |
47 | filter.conditionDescription = "Condition for test filter" ; |
48 | |
49 | llvm::Expected<ExceptionBreakpointsFilter> deserialized_filter = |
50 | roundtrip(input: filter); |
51 | ASSERT_THAT_EXPECTED(deserialized_filter, llvm::Succeeded()); |
52 | |
53 | EXPECT_EQ(filter.filter, deserialized_filter->filter); |
54 | EXPECT_EQ(filter.label, deserialized_filter->label); |
55 | EXPECT_EQ(filter.description, deserialized_filter->description); |
56 | EXPECT_EQ(filter.defaultState, deserialized_filter->defaultState); |
57 | EXPECT_EQ(filter.supportsCondition, deserialized_filter->supportsCondition); |
58 | EXPECT_EQ(filter.conditionDescription, |
59 | deserialized_filter->conditionDescription); |
60 | } |
61 | |
62 | TEST(ProtocolTypesTest, Source) { |
63 | Source source; |
64 | source.name = "testName" ; |
65 | source.path = "/path/to/source" ; |
66 | source.sourceReference = 12345; |
67 | source.presentationHint = Source::eSourcePresentationHintEmphasize; |
68 | |
69 | llvm::Expected<Source> deserialized_source = roundtrip(input: source); |
70 | ASSERT_THAT_EXPECTED(deserialized_source, llvm::Succeeded()); |
71 | |
72 | EXPECT_EQ(source.name, deserialized_source->name); |
73 | EXPECT_EQ(source.path, deserialized_source->path); |
74 | EXPECT_EQ(source.sourceReference, deserialized_source->sourceReference); |
75 | EXPECT_EQ(source.presentationHint, deserialized_source->presentationHint); |
76 | } |
77 | |
78 | TEST(ProtocolTypesTest, ColumnDescriptor) { |
79 | ColumnDescriptor column; |
80 | column.attributeName = "moduleName" ; |
81 | column.label = "Module Name" ; |
82 | column.format = "uppercase" ; |
83 | column.type = eColumnTypeString; |
84 | column.width = 20; |
85 | |
86 | llvm::Expected<ColumnDescriptor> deserialized_column = roundtrip(input: column); |
87 | ASSERT_THAT_EXPECTED(deserialized_column, llvm::Succeeded()); |
88 | |
89 | EXPECT_EQ(column.attributeName, deserialized_column->attributeName); |
90 | EXPECT_EQ(column.label, deserialized_column->label); |
91 | EXPECT_EQ(column.format, deserialized_column->format); |
92 | EXPECT_EQ(column.type, deserialized_column->type); |
93 | EXPECT_EQ(column.width, deserialized_column->width); |
94 | } |
95 | |
96 | TEST(ProtocolTypesTest, BreakpointMode) { |
97 | BreakpointMode mode; |
98 | mode.mode = "testMode" ; |
99 | mode.label = "Test Mode" ; |
100 | mode.description = "This is a test mode" ; |
101 | mode.appliesTo = {eBreakpointModeApplicabilitySource, |
102 | eBreakpointModeApplicabilityException}; |
103 | |
104 | llvm::Expected<BreakpointMode> deserialized_mode = roundtrip(input: mode); |
105 | ASSERT_THAT_EXPECTED(deserialized_mode, llvm::Succeeded()); |
106 | |
107 | EXPECT_EQ(mode.mode, deserialized_mode->mode); |
108 | EXPECT_EQ(mode.label, deserialized_mode->label); |
109 | EXPECT_EQ(mode.description, deserialized_mode->description); |
110 | EXPECT_EQ(mode.appliesTo, deserialized_mode->appliesTo); |
111 | } |
112 | |
113 | TEST(ProtocolTypesTest, Breakpoint) { |
114 | Breakpoint breakpoint; |
115 | breakpoint.id = 42; |
116 | breakpoint.verified = true; |
117 | breakpoint.message = "Breakpoint set successfully" ; |
118 | breakpoint.source = Source{.name: "test.cpp" , .path: "/path/to/test.cpp" , .sourceReference: 123, |
119 | .presentationHint: Source::eSourcePresentationHintNormal}; |
120 | breakpoint.line = 10; |
121 | breakpoint.column = 5; |
122 | breakpoint.endLine = 15; |
123 | breakpoint.endColumn = 10; |
124 | breakpoint.instructionReference = "0x12345678" ; |
125 | breakpoint.offset = 4; |
126 | breakpoint.reason = BreakpointReason::eBreakpointReasonPending; |
127 | |
128 | llvm::Expected<Breakpoint> deserialized_breakpoint = roundtrip(input: breakpoint); |
129 | ASSERT_THAT_EXPECTED(deserialized_breakpoint, llvm::Succeeded()); |
130 | |
131 | EXPECT_EQ(breakpoint.id, deserialized_breakpoint->id); |
132 | EXPECT_EQ(breakpoint.verified, deserialized_breakpoint->verified); |
133 | EXPECT_EQ(breakpoint.message, deserialized_breakpoint->message); |
134 | EXPECT_EQ(breakpoint.source->name, deserialized_breakpoint->source->name); |
135 | EXPECT_EQ(breakpoint.source->path, deserialized_breakpoint->source->path); |
136 | EXPECT_EQ(breakpoint.source->sourceReference, |
137 | deserialized_breakpoint->source->sourceReference); |
138 | EXPECT_EQ(breakpoint.source->presentationHint, |
139 | deserialized_breakpoint->source->presentationHint); |
140 | EXPECT_EQ(breakpoint.line, deserialized_breakpoint->line); |
141 | EXPECT_EQ(breakpoint.column, deserialized_breakpoint->column); |
142 | EXPECT_EQ(breakpoint.endLine, deserialized_breakpoint->endLine); |
143 | EXPECT_EQ(breakpoint.endColumn, deserialized_breakpoint->endColumn); |
144 | EXPECT_EQ(breakpoint.instructionReference, |
145 | deserialized_breakpoint->instructionReference); |
146 | EXPECT_EQ(breakpoint.offset, deserialized_breakpoint->offset); |
147 | EXPECT_EQ(breakpoint.reason, deserialized_breakpoint->reason); |
148 | } |
149 | |
150 | TEST(ProtocolTypesTest, SourceBreakpoint) { |
151 | SourceBreakpoint source_breakpoint; |
152 | source_breakpoint.line = 42; |
153 | source_breakpoint.column = 5; |
154 | source_breakpoint.condition = "x > 10" ; |
155 | source_breakpoint.hitCondition = "5" ; |
156 | source_breakpoint.logMessage = "Breakpoint hit at line 42" ; |
157 | source_breakpoint.mode = "hardware" ; |
158 | |
159 | llvm::Expected<SourceBreakpoint> deserialized_source_breakpoint = |
160 | roundtrip(input: source_breakpoint); |
161 | ASSERT_THAT_EXPECTED(deserialized_source_breakpoint, llvm::Succeeded()); |
162 | |
163 | EXPECT_EQ(source_breakpoint.line, deserialized_source_breakpoint->line); |
164 | EXPECT_EQ(source_breakpoint.column, deserialized_source_breakpoint->column); |
165 | EXPECT_EQ(source_breakpoint.condition, |
166 | deserialized_source_breakpoint->condition); |
167 | EXPECT_EQ(source_breakpoint.hitCondition, |
168 | deserialized_source_breakpoint->hitCondition); |
169 | EXPECT_EQ(source_breakpoint.logMessage, |
170 | deserialized_source_breakpoint->logMessage); |
171 | EXPECT_EQ(source_breakpoint.mode, deserialized_source_breakpoint->mode); |
172 | } |
173 | |
174 | TEST(ProtocolTypesTest, FunctionBreakpoint) { |
175 | FunctionBreakpoint function_breakpoint; |
176 | function_breakpoint.name = "myFunction" ; |
177 | function_breakpoint.condition = "x == 0" ; |
178 | function_breakpoint.hitCondition = "3" ; |
179 | |
180 | llvm::Expected<FunctionBreakpoint> deserialized_function_breakpoint = |
181 | roundtrip(input: function_breakpoint); |
182 | ASSERT_THAT_EXPECTED(deserialized_function_breakpoint, llvm::Succeeded()); |
183 | |
184 | EXPECT_EQ(function_breakpoint.name, deserialized_function_breakpoint->name); |
185 | EXPECT_EQ(function_breakpoint.condition, |
186 | deserialized_function_breakpoint->condition); |
187 | EXPECT_EQ(function_breakpoint.hitCondition, |
188 | deserialized_function_breakpoint->hitCondition); |
189 | } |
190 | |
191 | TEST(ProtocolTypesTest, DataBreakpoint) { |
192 | DataBreakpoint data_breakpoint_info; |
193 | data_breakpoint_info.dataId = "variable1" ; |
194 | data_breakpoint_info.accessType = eDataBreakpointAccessTypeReadWrite; |
195 | data_breakpoint_info.condition = "x > 100" ; |
196 | data_breakpoint_info.hitCondition = "10" ; |
197 | |
198 | llvm::Expected<DataBreakpoint> deserialized_data_breakpoint_info = |
199 | roundtrip(input: data_breakpoint_info); |
200 | ASSERT_THAT_EXPECTED(deserialized_data_breakpoint_info, llvm::Succeeded()); |
201 | |
202 | EXPECT_EQ(data_breakpoint_info.dataId, |
203 | deserialized_data_breakpoint_info->dataId); |
204 | EXPECT_EQ(data_breakpoint_info.accessType, |
205 | deserialized_data_breakpoint_info->accessType); |
206 | EXPECT_EQ(data_breakpoint_info.condition, |
207 | deserialized_data_breakpoint_info->condition); |
208 | EXPECT_EQ(data_breakpoint_info.hitCondition, |
209 | deserialized_data_breakpoint_info->hitCondition); |
210 | } |
211 | |
212 | TEST(ProtocolTypesTest, Capabilities) { |
213 | Capabilities capabilities; |
214 | |
215 | // Populate supported features. |
216 | capabilities.supportedFeatures.insert(V: eAdapterFeatureANSIStyling); |
217 | capabilities.supportedFeatures.insert( |
218 | V: eAdapterFeatureBreakpointLocationsRequest); |
219 | |
220 | // Populate optional fields. |
221 | capabilities.exceptionBreakpointFilters = { |
222 | {{.filter: "filter1" , .label: "Filter 1" , .description: "Description 1" , .defaultState: true, .supportsCondition: true, .conditionDescription: "Condition 1" }, |
223 | {.filter: "filter2" , .label: "Filter 2" , .description: "Description 2" , .defaultState: false, .supportsCondition: false, .conditionDescription: "Condition 2" }}}; |
224 | |
225 | capabilities.completionTriggerCharacters = {"." , "->" }; |
226 | capabilities.additionalModuleColumns = { |
227 | {.attributeName: "moduleName" , .label: "Module Name" , .format: "uppercase" , .type: eColumnTypeString, .width: 20}}; |
228 | capabilities.supportedChecksumAlgorithms = {eChecksumAlgorithmMD5, |
229 | eChecksumAlgorithmSHA256}; |
230 | capabilities.breakpointModes = {{.mode: "hardware" , |
231 | .label: "Hardware Breakpoint" , |
232 | .description: "Description" , |
233 | .appliesTo: {eBreakpointModeApplicabilitySource}}}; |
234 | capabilities.lldbExtVersion = "1.0.0" ; |
235 | |
236 | // Perform roundtrip serialization and deserialization. |
237 | llvm::Expected<Capabilities> deserialized_capabilities = |
238 | roundtrip(input: capabilities); |
239 | ASSERT_THAT_EXPECTED(deserialized_capabilities, llvm::Succeeded()); |
240 | |
241 | // Verify supported features. |
242 | EXPECT_EQ(capabilities.supportedFeatures, |
243 | deserialized_capabilities->supportedFeatures); |
244 | |
245 | // Verify exception breakpoint filters. |
246 | ASSERT_TRUE( |
247 | deserialized_capabilities->exceptionBreakpointFilters.has_value()); |
248 | EXPECT_EQ(capabilities.exceptionBreakpointFilters->size(), |
249 | deserialized_capabilities->exceptionBreakpointFilters->size()); |
250 | for (size_t i = 0; i < capabilities.exceptionBreakpointFilters->size(); ++i) { |
251 | const auto &original = capabilities.exceptionBreakpointFilters->at(n: i); |
252 | const auto &deserialized = |
253 | deserialized_capabilities->exceptionBreakpointFilters->at(n: i); |
254 | EXPECT_EQ(original.filter, deserialized.filter); |
255 | EXPECT_EQ(original.label, deserialized.label); |
256 | EXPECT_EQ(original.description, deserialized.description); |
257 | EXPECT_EQ(original.defaultState, deserialized.defaultState); |
258 | EXPECT_EQ(original.supportsCondition, deserialized.supportsCondition); |
259 | EXPECT_EQ(original.conditionDescription, deserialized.conditionDescription); |
260 | } |
261 | |
262 | // Verify completion trigger characters. |
263 | ASSERT_TRUE( |
264 | deserialized_capabilities->completionTriggerCharacters.has_value()); |
265 | EXPECT_EQ(capabilities.completionTriggerCharacters, |
266 | deserialized_capabilities->completionTriggerCharacters); |
267 | |
268 | // Verify additional module columns. |
269 | ASSERT_TRUE(deserialized_capabilities->additionalModuleColumns.has_value()); |
270 | EXPECT_EQ(capabilities.additionalModuleColumns->size(), |
271 | deserialized_capabilities->additionalModuleColumns->size()); |
272 | for (size_t i = 0; i < capabilities.additionalModuleColumns->size(); ++i) { |
273 | const auto &original = capabilities.additionalModuleColumns->at(n: i); |
274 | const auto &deserialized = |
275 | deserialized_capabilities->additionalModuleColumns->at(n: i); |
276 | EXPECT_EQ(original.attributeName, deserialized.attributeName); |
277 | EXPECT_EQ(original.label, deserialized.label); |
278 | EXPECT_EQ(original.format, deserialized.format); |
279 | EXPECT_EQ(original.type, deserialized.type); |
280 | EXPECT_EQ(original.width, deserialized.width); |
281 | } |
282 | |
283 | // Verify supported checksum algorithms. |
284 | ASSERT_TRUE( |
285 | deserialized_capabilities->supportedChecksumAlgorithms.has_value()); |
286 | EXPECT_EQ(capabilities.supportedChecksumAlgorithms, |
287 | deserialized_capabilities->supportedChecksumAlgorithms); |
288 | |
289 | // Verify breakpoint modes. |
290 | ASSERT_TRUE(deserialized_capabilities->breakpointModes.has_value()); |
291 | EXPECT_EQ(capabilities.breakpointModes->size(), |
292 | deserialized_capabilities->breakpointModes->size()); |
293 | for (size_t i = 0; i < capabilities.breakpointModes->size(); ++i) { |
294 | const auto &original = capabilities.breakpointModes->at(n: i); |
295 | const auto &deserialized = |
296 | deserialized_capabilities->breakpointModes->at(n: i); |
297 | EXPECT_EQ(original.mode, deserialized.mode); |
298 | EXPECT_EQ(original.label, deserialized.label); |
299 | EXPECT_EQ(original.description, deserialized.description); |
300 | EXPECT_EQ(original.appliesTo, deserialized.appliesTo); |
301 | } |
302 | |
303 | // Verify lldb extension version. |
304 | ASSERT_TRUE(deserialized_capabilities->lldbExtVersion.has_value()); |
305 | EXPECT_EQ(capabilities.lldbExtVersion, |
306 | deserialized_capabilities->lldbExtVersion); |
307 | } |
308 | |
309 | TEST(ProtocolTypesTest, Scope) { |
310 | Scope scope; |
311 | scope.name = "Locals" ; |
312 | scope.presentationHint = Scope::eScopePresentationHintLocals; |
313 | scope.variablesReference = 1; |
314 | scope.namedVariables = 2; |
315 | scope.indexedVariables = std::nullopt; |
316 | scope.expensive = false; |
317 | scope.line = 2; |
318 | scope.column = 3; |
319 | scope.endLine = 10; |
320 | scope.endColumn = 20; |
321 | |
322 | Source source; |
323 | source.name = "testName" ; |
324 | source.path = "/path/to/source" ; |
325 | source.sourceReference = 12345; |
326 | source.presentationHint = Source::eSourcePresentationHintNormal; |
327 | scope.source = source; |
328 | |
329 | llvm::Expected<Scope> deserialized_scope = roundtrip(input: scope); |
330 | ASSERT_THAT_EXPECTED(deserialized_scope, llvm::Succeeded()); |
331 | EXPECT_EQ(scope.name, deserialized_scope->name); |
332 | EXPECT_EQ(scope.presentationHint, deserialized_scope->presentationHint); |
333 | EXPECT_EQ(scope.variablesReference, deserialized_scope->variablesReference); |
334 | EXPECT_EQ(scope.namedVariables, deserialized_scope->namedVariables); |
335 | EXPECT_EQ(scope.indexedVariables, deserialized_scope->indexedVariables); |
336 | EXPECT_EQ(scope.expensive, deserialized_scope->expensive); |
337 | EXPECT_EQ(scope.line, deserialized_scope->line); |
338 | EXPECT_EQ(scope.column, deserialized_scope->column); |
339 | EXPECT_EQ(scope.endLine, deserialized_scope->endLine); |
340 | EXPECT_EQ(scope.endColumn, deserialized_scope->endColumn); |
341 | |
342 | EXPECT_THAT(deserialized_scope->source.has_value(), true); |
343 | const Source &deserialized_source = deserialized_scope->source.value(); |
344 | |
345 | EXPECT_EQ(source.path, deserialized_source.path); |
346 | EXPECT_EQ(source.sourceReference, deserialized_source.sourceReference); |
347 | EXPECT_EQ(source.presentationHint, deserialized_source.presentationHint); |
348 | } |
349 | |
350 | TEST(ProtocolTypesTest, PresentationHint) { |
351 | // Test all PresentationHint values. |
352 | std::vector<std::pair<Source::PresentationHint, llvm::StringRef>> test_cases = |
353 | {{Source::eSourcePresentationHintNormal, "normal" }, |
354 | {Source::eSourcePresentationHintEmphasize, "emphasize" }, |
355 | {Source::eSourcePresentationHintDeemphasize, "deemphasize" }}; |
356 | |
357 | for (const auto &test_case : test_cases) { |
358 | // Serialize the PresentationHint to JSON. |
359 | llvm::json::Value serialized = toJSON(test_case.first); |
360 | ASSERT_EQ(serialized.kind(), llvm::json::Value::Kind::String); |
361 | EXPECT_EQ(serialized.getAsString(), test_case.second); |
362 | |
363 | // Deserialize the JSON back to PresentationHint. |
364 | Source::PresentationHint deserialized; |
365 | llvm::json::Path::Root root; |
366 | ASSERT_TRUE(fromJSON(serialized, deserialized, root)) |
367 | << llvm::toString(E: root.getError()); |
368 | EXPECT_EQ(deserialized, test_case.first); |
369 | } |
370 | |
371 | // Test invalid value. |
372 | llvm::json::Value invalid_value = "invalid_hint" ; |
373 | Source::PresentationHint deserialized_invalid; |
374 | llvm::json::Path::Root root; |
375 | EXPECT_FALSE(fromJSON(invalid_value, deserialized_invalid, root)); |
376 | } |
377 | |
378 | TEST(ProtocolTypesTest, SteppingGranularity) { |
379 | // Test all SteppingGranularity values. |
380 | std::vector<std::pair<SteppingGranularity, llvm::StringRef>> test_cases = { |
381 | {eSteppingGranularityStatement, "statement" }, |
382 | {eSteppingGranularityLine, "line" }, |
383 | {eSteppingGranularityInstruction, "instruction" }}; |
384 | |
385 | for (const auto &test_case : test_cases) { |
386 | // Serialize the SteppingGranularity to JSON. |
387 | llvm::json::Value serialized = toJSON(test_case.first); |
388 | ASSERT_EQ(serialized.kind(), llvm::json::Value::Kind::String); |
389 | EXPECT_EQ(serialized.getAsString(), test_case.second); |
390 | |
391 | // Deserialize the JSON back to SteppingGranularity. |
392 | SteppingGranularity deserialized; |
393 | llvm::json::Path::Root root; |
394 | ASSERT_TRUE(fromJSON(serialized, deserialized, root)) |
395 | << llvm::toString(E: root.getError()); |
396 | EXPECT_EQ(deserialized, test_case.first); |
397 | } |
398 | |
399 | // Test invalid value. |
400 | llvm::json::Value invalid_value = "invalid_granularity" ; |
401 | SteppingGranularity deserialized_invalid; |
402 | llvm::json::Path::Root root; |
403 | EXPECT_FALSE(fromJSON(invalid_value, deserialized_invalid, root)); |
404 | } |
405 | |
406 | TEST(ProtocolTypesTest, BreakpointReason) { |
407 | // Test all BreakpointReason values. |
408 | std::vector<std::pair<BreakpointReason, llvm::StringRef>> test_cases = { |
409 | {BreakpointReason::eBreakpointReasonPending, "pending" }, |
410 | {BreakpointReason::eBreakpointReasonFailed, "failed" }}; |
411 | |
412 | for (const auto &test_case : test_cases) { |
413 | // Serialize the BreakpointReason to JSON. |
414 | llvm::json::Value serialized = toJSON(test_case.first); |
415 | ASSERT_EQ(serialized.kind(), llvm::json::Value::Kind::String); |
416 | EXPECT_EQ(serialized.getAsString(), test_case.second); |
417 | |
418 | // Deserialize the JSON back to BreakpointReason. |
419 | BreakpointReason deserialized; |
420 | llvm::json::Path::Root root; |
421 | ASSERT_TRUE(fromJSON(serialized, deserialized, root)) |
422 | << llvm::toString(E: root.getError()); |
423 | EXPECT_EQ(deserialized, test_case.first); |
424 | } |
425 | |
426 | // Test invalid value. |
427 | llvm::json::Value invalid_value = "invalid_reason" ; |
428 | BreakpointReason deserialized_invalid; |
429 | llvm::json::Path::Root root; |
430 | EXPECT_FALSE(fromJSON(invalid_value, deserialized_invalid, root)); |
431 | } |
432 | |
433 | TEST(ProtocolTypesTest, DataBreakpointAccessType) { |
434 | // Test all DataBreakpointAccessType values. |
435 | std::vector<std::pair<DataBreakpointAccessType, llvm::StringRef>> test_cases = |
436 | {{eDataBreakpointAccessTypeRead, "read" }, |
437 | {eDataBreakpointAccessTypeWrite, "write" }, |
438 | {eDataBreakpointAccessTypeReadWrite, "readWrite" }}; |
439 | |
440 | for (const auto &test_case : test_cases) { |
441 | // Serialize the DataBreakpointAccessType to JSON. |
442 | llvm::json::Value serialized = toJSON(test_case.first); |
443 | ASSERT_EQ(serialized.kind(), llvm::json::Value::Kind::String); |
444 | EXPECT_EQ(serialized.getAsString(), test_case.second); |
445 | |
446 | // Deserialize the JSON back to DataBreakpointAccessType. |
447 | DataBreakpointAccessType deserialized; |
448 | llvm::json::Path::Root root; |
449 | ASSERT_TRUE(fromJSON(serialized, deserialized, root)) |
450 | << llvm::toString(E: root.getError()); |
451 | EXPECT_EQ(deserialized, test_case.first); |
452 | } |
453 | |
454 | // Test invalid value |
455 | llvm::json::Value invalid_value = "invalid_access_type" ; |
456 | DataBreakpointAccessType deserialized_invalid; |
457 | llvm::json::Path::Root root; |
458 | EXPECT_FALSE(fromJSON(invalid_value, deserialized_invalid, root)); |
459 | } |
460 | |
461 | TEST(ProtocolTypesTest, ColumnType) { |
462 | // Test all ColumnType values. |
463 | std::vector<std::pair<ColumnType, llvm::StringRef>> test_cases = { |
464 | {eColumnTypeString, "string" }, |
465 | {eColumnTypeNumber, "number" }, |
466 | {eColumnTypeBoolean, "boolean" }, |
467 | {eColumnTypeTimestamp, "unixTimestampUTC" }}; |
468 | |
469 | for (const auto &test_case : test_cases) { |
470 | // Serialize the ColumnType to JSON. |
471 | llvm::json::Value serialized = toJSON(test_case.first); |
472 | ASSERT_EQ(serialized.kind(), llvm::json::Value::Kind::String); |
473 | EXPECT_EQ(serialized.getAsString(), test_case.second); |
474 | |
475 | // Deserialize the JSON back to ColumnType. |
476 | ColumnType deserialized; |
477 | llvm::json::Path::Root root; |
478 | ASSERT_TRUE(fromJSON(serialized, deserialized, root)) |
479 | << llvm::toString(E: root.getError()); |
480 | EXPECT_EQ(deserialized, test_case.first); |
481 | } |
482 | |
483 | // Test invalid value. |
484 | llvm::json::Value invalid_value = "invalid_column_type" ; |
485 | ColumnType deserialized_invalid; |
486 | llvm::json::Path::Root root; |
487 | EXPECT_FALSE(fromJSON(invalid_value, deserialized_invalid, root)); |
488 | } |
489 | |
490 | TEST(ProtocolTypesTest, BreakpointModeApplicability) { |
491 | // Test all BreakpointModeApplicability values. |
492 | std::vector<std::pair<BreakpointModeApplicability, llvm::StringRef>> |
493 | test_cases = {{eBreakpointModeApplicabilitySource, "source" }, |
494 | {eBreakpointModeApplicabilityException, "exception" }, |
495 | {eBreakpointModeApplicabilityData, "data" }, |
496 | {eBreakpointModeApplicabilityInstruction, "instruction" }}; |
497 | |
498 | for (const auto &test_case : test_cases) { |
499 | // Serialize the BreakpointModeApplicability to JSON. |
500 | llvm::json::Value serialized = toJSON(test_case.first); |
501 | ASSERT_EQ(serialized.kind(), llvm::json::Value::Kind::String); |
502 | EXPECT_EQ(serialized.getAsString(), test_case.second); |
503 | |
504 | // Deserialize the JSON back to BreakpointModeApplicability. |
505 | BreakpointModeApplicability deserialized; |
506 | llvm::json::Path::Root root; |
507 | ASSERT_TRUE(fromJSON(serialized, deserialized, root)) |
508 | << llvm::toString(E: root.getError()); |
509 | EXPECT_EQ(deserialized, test_case.first); |
510 | } |
511 | |
512 | // Test invalid value. |
513 | llvm::json::Value invalid_value = "invalid_applicability" ; |
514 | BreakpointModeApplicability deserialized_invalid; |
515 | llvm::json::Path::Root root; |
516 | EXPECT_FALSE(fromJSON(invalid_value, deserialized_invalid, root)); |
517 | } |
518 | |
519 | TEST(ProtocolTypesTest, ChecksumAlgorithm) { |
520 | // Test all ChecksumAlgorithm values. |
521 | std::vector<std::pair<ChecksumAlgorithm, llvm::StringRef>> test_cases = { |
522 | {eChecksumAlgorithmMD5, "MD5" }, |
523 | {eChecksumAlgorithmSHA1, "SHA1" }, |
524 | {eChecksumAlgorithmSHA256, "SHA256" }, |
525 | {eChecksumAlgorithmTimestamp, "timestamp" }}; |
526 | |
527 | for (const auto &test_case : test_cases) { |
528 | // Serialize the ChecksumAlgorithm to JSON. |
529 | llvm::json::Value serialized = toJSON(test_case.first); |
530 | ASSERT_EQ(serialized.kind(), llvm::json::Value::Kind::String); |
531 | EXPECT_EQ(serialized.getAsString(), test_case.second); |
532 | |
533 | // Deserialize the JSON back to ChecksumAlgorithm. |
534 | ChecksumAlgorithm deserialized; |
535 | llvm::json::Path::Root root; |
536 | ASSERT_TRUE(fromJSON(serialized, deserialized, root)) |
537 | << llvm::toString(E: root.getError()); |
538 | EXPECT_EQ(deserialized, test_case.first); |
539 | } |
540 | |
541 | // Test invalid value. |
542 | llvm::json::Value invalid_value = "invalid_algorithm" ; |
543 | ChecksumAlgorithm deserialized_invalid; |
544 | llvm::json::Path::Root root; |
545 | EXPECT_FALSE(fromJSON(invalid_value, deserialized_invalid, root)); |
546 | } |
547 | |
548 | TEST(ProtocolTypesTest, DisassembledInstructionPresentationHint) { |
549 | // Test all PresentationHint values. |
550 | std::vector< |
551 | std::pair<DisassembledInstruction::PresentationHint, llvm::StringRef>> |
552 | test_cases = {{DisassembledInstruction:: |
553 | eDisassembledInstructionPresentationHintNormal, |
554 | "normal" }, |
555 | {DisassembledInstruction:: |
556 | eDisassembledInstructionPresentationHintInvalid, |
557 | "invalid" }}; |
558 | |
559 | for (const auto &test_case : test_cases) { |
560 | // Serialize the PresentationHint to JSON. |
561 | llvm::json::Value serialized = toJSON(test_case.first); |
562 | ASSERT_EQ(serialized.kind(), llvm::json::Value::Kind::String); |
563 | EXPECT_EQ(serialized.getAsString(), test_case.second); |
564 | |
565 | // Deserialize the JSON back to PresentationHint. |
566 | DisassembledInstruction::PresentationHint deserialized; |
567 | llvm::json::Path::Root root; |
568 | ASSERT_TRUE(fromJSON(serialized, deserialized, root)) |
569 | << llvm::toString(E: root.getError()); |
570 | EXPECT_EQ(deserialized, test_case.first); |
571 | } |
572 | |
573 | // Test invalid value. |
574 | llvm::json::Value invalid_value = "invalid_hint" ; |
575 | DisassembledInstruction::PresentationHint deserialized_invalid; |
576 | llvm::json::Path::Root root; |
577 | EXPECT_FALSE(fromJSON(invalid_value, deserialized_invalid, root)); |
578 | } |
579 | |
580 | TEST(ProtocolTypesTest, DisassembledInstruction) { |
581 | DisassembledInstruction instruction; |
582 | instruction.address = 0x12345678; |
583 | instruction.instructionBytes = "0F 1F 00" ; |
584 | instruction.instruction = "mov eax, ebx" ; |
585 | instruction.symbol = "main" ; |
586 | instruction.location = Source{.name: "test.cpp" , .path: "/path/to/test.cpp" , .sourceReference: 123, |
587 | .presentationHint: Source::eSourcePresentationHintNormal}; |
588 | instruction.line = 10; |
589 | instruction.column = 5; |
590 | instruction.endLine = 15; |
591 | instruction.endColumn = 10; |
592 | instruction.presentationHint = |
593 | DisassembledInstruction::eDisassembledInstructionPresentationHintNormal; |
594 | |
595 | StringLiteral json = R"({ |
596 | "address": "0x12345678", |
597 | "column": 5, |
598 | "endColumn": 10, |
599 | "endLine": 15, |
600 | "instruction": "mov eax, ebx", |
601 | "instructionBytes": "0F 1F 00", |
602 | "line": 10, |
603 | "location": { |
604 | "name": "test.cpp", |
605 | "path": "/path/to/test.cpp", |
606 | "presentationHint": "normal", |
607 | "sourceReference": 123 |
608 | }, |
609 | "presentationHint": "normal", |
610 | "symbol": "main" |
611 | })" ; |
612 | |
613 | // Validate toJSON |
614 | EXPECT_EQ(json, pp(instruction)); |
615 | |
616 | // Validate fromJSON |
617 | EXPECT_THAT_EXPECTED(parse<DisassembledInstruction>(json), |
618 | HasValue(Value(instruction))); |
619 | // Validate parsing errors |
620 | EXPECT_THAT_EXPECTED( |
621 | parse<DisassembledInstruction>(R"({"address":1})" , |
622 | "disassemblyInstruction" ), |
623 | FailedWithMessage("expected string at disassemblyInstruction.address" )); |
624 | EXPECT_THAT_EXPECTED(parse<DisassembledInstruction>(R"({"address":"-1"})" , |
625 | "disassemblyInstruction" ), |
626 | FailedWithMessage("expected string encoded uint64_t at " |
627 | "disassemblyInstruction.address" )); |
628 | EXPECT_THAT_EXPECTED(parse<DisassembledInstruction>( |
629 | R"({"address":"0xfffffffffffffffffffffffffff"})" , |
630 | "disassemblyInstruction" ), |
631 | FailedWithMessage("expected string encoded uint64_t at " |
632 | "disassemblyInstruction.address" )); |
633 | } |
634 | |
635 | TEST(ProtocolTypesTest, Thread) { |
636 | const Thread thread{.id: 1, .name: "thr1" }; |
637 | const StringRef json = R"({ |
638 | "id": 1, |
639 | "name": "thr1" |
640 | })" ; |
641 | // Validate toJSON |
642 | EXPECT_EQ(json, pp(thread)); |
643 | // Validate fromJSON |
644 | EXPECT_THAT_EXPECTED(parse<Thread>(json), HasValue(Value(thread))); |
645 | // Validate parsing errors |
646 | EXPECT_THAT_EXPECTED(parse<Thread>(R"({"id":1})" , "thread" ), |
647 | FailedWithMessage("missing value at thread.name" )); |
648 | EXPECT_THAT_EXPECTED(parse<Thread>(R"({"id":"one"})" , "thread" ), |
649 | FailedWithMessage("expected uint64_t at thread.id" )); |
650 | EXPECT_THAT_EXPECTED(parse<Thread>(R"({"id":1,"name":false})" , "thread" ), |
651 | FailedWithMessage("expected string at thread.name" )); |
652 | } |
653 | |
654 | TEST(ProtocolTypesTest, ThreadResponseBody) { |
655 | const ThreadsResponseBody body{.threads: {{.id: 1, .name: "thr1" }, {.id: 2, .name: "thr2" }}}; |
656 | const StringRef json = R"({ |
657 | "threads": [ |
658 | { |
659 | "id": 1, |
660 | "name": "thr1" |
661 | }, |
662 | { |
663 | "id": 2, |
664 | "name": "thr2" |
665 | } |
666 | ] |
667 | })" ; |
668 | // Validate toJSON |
669 | EXPECT_EQ(json, pp(body)); |
670 | } |
671 | |
672 | TEST(ProtocolTypesTest, CapabilitiesEventBody) { |
673 | Capabilities capabilities; |
674 | capabilities.supportedFeatures = { |
675 | eAdapterFeatureANSIStyling, |
676 | eAdapterFeatureBreakpointLocationsRequest, |
677 | }; |
678 | CapabilitiesEventBody body; |
679 | body.capabilities = capabilities; |
680 | StringRef json = R"({ |
681 | "capabilities": { |
682 | "supportsANSIStyling": true, |
683 | "supportsBreakpointLocationsRequest": true |
684 | } |
685 | })" ; |
686 | // Validate toJSON |
687 | EXPECT_EQ(json, pp(body)); |
688 | } |
689 | |