From 87735d1703b47646659b0dee19593ed27b63925f Mon Sep 17 00:00:00 2001 From: Kanushka Gayan Date: Tue, 17 Feb 2026 23:39:40 +0530 Subject: [PATCH 1/2] Add diagnostic range to flow model --- .../core/DiagnosticHandler.java | 3 +- .../core/model/Diagnostics.java | 16 +++- .../extension/FlowModelDiagnosticsTest.java | 85 ++++++++++++++++- .../extension/ModelGeneratorTest.java | 91 ++++++++++++++++++- 4 files changed, 187 insertions(+), 8 deletions(-) diff --git a/flow-model-generator/modules/flow-model-generator-core/src/main/java/io/ballerina/flowmodelgenerator/core/DiagnosticHandler.java b/flow-model-generator/modules/flow-model-generator-core/src/main/java/io/ballerina/flowmodelgenerator/core/DiagnosticHandler.java index d11b9cad65..1e71dcfa92 100644 --- a/flow-model-generator/modules/flow-model-generator-core/src/main/java/io/ballerina/flowmodelgenerator/core/DiagnosticHandler.java +++ b/flow-model-generator/modules/flow-model-generator-core/src/main/java/io/ballerina/flowmodelgenerator/core/DiagnosticHandler.java @@ -138,7 +138,8 @@ private void next() { private void addDiagnostic(DiagnosticCapable builder) { builder.diagnostics() - .diagnostic(currentDiagnostic.diagnosticInfo().severity(), currentDiagnostic.message()); + .diagnostic(currentDiagnostic.diagnosticInfo().severity(), currentDiagnostic.message(), + currentDiagnostic.location().lineRange()); } private static boolean hasDiagnosticPassed(LinePosition nodeStartLine, LinePosition diagnosticEndLine, diff --git a/flow-model-generator/modules/flow-model-generator-core/src/main/java/io/ballerina/flowmodelgenerator/core/model/Diagnostics.java b/flow-model-generator/modules/flow-model-generator-core/src/main/java/io/ballerina/flowmodelgenerator/core/model/Diagnostics.java index 9633b5c56d..31ec207e19 100644 --- a/flow-model-generator/modules/flow-model-generator-core/src/main/java/io/ballerina/flowmodelgenerator/core/model/Diagnostics.java +++ b/flow-model-generator/modules/flow-model-generator-core/src/main/java/io/ballerina/flowmodelgenerator/core/model/Diagnostics.java @@ -19,6 +19,7 @@ package io.ballerina.flowmodelgenerator.core.model; import io.ballerina.tools.diagnostics.DiagnosticSeverity; +import io.ballerina.tools.text.LineRange; import java.util.ArrayList; import java.util.List; @@ -33,13 +34,19 @@ public record Diagnostics(boolean hasDiagnostics, List diagnostics) { /** - * Represents diagnostic information with severity and message. + * Represents diagnostic information with severity, message and range. * * @param severity severity of the diagnostic * @param message message of the diagnostic + * @param range source line range of the diagnostic * @since 1.0.0 */ - public record Info(DiagnosticSeverity severity, String message) { } + public record Info(DiagnosticSeverity severity, String message, LineRange range) { + + public Info(DiagnosticSeverity severity, String message) { + this(severity, message, null); + } + } public static class Builder extends FacetedBuilder { @@ -67,6 +74,11 @@ public Builder diagnostic(DiagnosticSeverity severity, String message) { return this; } + public Builder diagnostic(DiagnosticSeverity severity, String message, LineRange range) { + this.diagnostics.add(new Info(severity, message, range)); + return this; + } + public Diagnostics build() { return new Diagnostics(hasDiagnostics || !diagnostics.isEmpty(), diagnostics.isEmpty() ? null : diagnostics); diff --git a/flow-model-generator/modules/flow-model-generator-ls-extension/src/test/java/io/ballerina/flowmodelgenerator/extension/FlowModelDiagnosticsTest.java b/flow-model-generator/modules/flow-model-generator-ls-extension/src/test/java/io/ballerina/flowmodelgenerator/extension/FlowModelDiagnosticsTest.java index cc9f70c90e..dedf4c6d70 100644 --- a/flow-model-generator/modules/flow-model-generator-ls-extension/src/test/java/io/ballerina/flowmodelgenerator/extension/FlowModelDiagnosticsTest.java +++ b/flow-model-generator/modules/flow-model-generator-ls-extension/src/test/java/io/ballerina/flowmodelgenerator/extension/FlowModelDiagnosticsTest.java @@ -18,6 +18,7 @@ package io.ballerina.flowmodelgenerator.extension; +import com.google.gson.JsonArray; import com.google.gson.JsonElement; import com.google.gson.JsonObject; import io.ballerina.flowmodelgenerator.extension.request.FlowModelSourceGeneratorRequest; @@ -49,11 +50,16 @@ public void test(Path config) throws IOException { JsonElement flowNode = getResponse(request).get("flowNode"); notifyDidClose(sourcePath); - if (!flowNode.equals(testConfig.output())) { + assertDiagnosticsIncludeRange(flowNode, "flowNode"); + JsonElement normalizedActualFlowNode = flowNode.deepCopy(); + stripDiagnosticRanges(normalizedActualFlowNode); + JsonElement normalizedExpectedFlowNode = testConfig.output().deepCopy(); + stripDiagnosticRanges(normalizedExpectedFlowNode); + if (!normalizedActualFlowNode.equals(normalizedExpectedFlowNode)) { TestConfig updateConfig = new TestConfig(testConfig.source(), testConfig.description(), testConfig.flowNode(), flowNode); // updateConfig(configJsonPath, updateConfig); - compareJsonElements(flowNode, testConfig.output()); + compareJsonElements(normalizedActualFlowNode, normalizedExpectedFlowNode); Assert.fail(String.format("Failed test: '%s' (%s)", testConfig.description(), configJsonPath)); } } @@ -77,7 +83,13 @@ public void testMultipleRequests() throws IOException, InterruptedException { FlowModelSourceGeneratorRequest finalReq = new FlowModelSourceGeneratorRequest(sourcePath, flowNode); JsonObject response = getResponse(finalReq); notifyDidClose(sourcePath); - Assert.assertEquals(response.get("flowNode"), testConfig.output()); + JsonElement actualFlowNode = response.get("flowNode"); + assertDiagnosticsIncludeRange(actualFlowNode, "flowNode"); + JsonElement normalizedActualFlowNode = actualFlowNode.deepCopy(); + stripDiagnosticRanges(normalizedActualFlowNode); + JsonElement normalizedExpectedFlowNode = testConfig.output().deepCopy(); + stripDiagnosticRanges(normalizedExpectedFlowNode); + Assert.assertEquals(normalizedActualFlowNode, normalizedExpectedFlowNode); } @Override @@ -101,4 +113,71 @@ public String description() { return description == null ? "" : description; } } + + private static void stripDiagnosticRanges(JsonElement jsonElement) { + if (jsonElement == null || jsonElement.isJsonNull()) { + return; + } + if (jsonElement.isJsonArray()) { + JsonArray jsonArray = jsonElement.getAsJsonArray(); + for (JsonElement element : jsonArray) { + stripDiagnosticRanges(element); + } + return; + } + if (!jsonElement.isJsonObject()) { + return; + } + JsonObject jsonObject = jsonElement.getAsJsonObject(); + if (jsonObject.has("diagnostics")) { + JsonElement diagnosticsElement = jsonObject.get("diagnostics"); + if (diagnosticsElement.isJsonObject()) { + JsonArray diagnosticsArray = diagnosticsElement.getAsJsonObject().getAsJsonArray("diagnostics"); + if (diagnosticsArray != null) { + for (JsonElement diagnosticElement : diagnosticsArray) { + if (diagnosticElement.isJsonObject()) { + diagnosticElement.getAsJsonObject().remove("range"); + } + } + } + } + } + for (String key : jsonObject.keySet()) { + stripDiagnosticRanges(jsonObject.get(key)); + } + } + + private static void assertDiagnosticsIncludeRange(JsonElement jsonElement, String path) { + if (jsonElement == null || jsonElement.isJsonNull()) { + return; + } + if (jsonElement.isJsonArray()) { + JsonArray jsonArray = jsonElement.getAsJsonArray(); + for (int i = 0; i < jsonArray.size(); i++) { + assertDiagnosticsIncludeRange(jsonArray.get(i), path + "[" + i + "]"); + } + return; + } + if (!jsonElement.isJsonObject()) { + return; + } + JsonObject jsonObject = jsonElement.getAsJsonObject(); + if (jsonObject.has("diagnostics")) { + JsonElement diagnosticsElement = jsonObject.get("diagnostics"); + if (diagnosticsElement.isJsonObject()) { + JsonArray diagnosticsArray = diagnosticsElement.getAsJsonObject().getAsJsonArray("diagnostics"); + if (diagnosticsArray != null) { + for (int i = 0; i < diagnosticsArray.size(); i++) { + JsonElement diagnosticElement = diagnosticsArray.get(i); + Assert.assertTrue(diagnosticElement.isJsonObject() + && diagnosticElement.getAsJsonObject().has("range"), + String.format("Expected diagnostic range at %s.diagnostics.diagnostics[%d]", path, i)); + } + } + } + } + for (String key : jsonObject.keySet()) { + assertDiagnosticsIncludeRange(jsonObject.get(key), path + "." + key); + } + } } diff --git a/flow-model-generator/modules/flow-model-generator-ls-extension/src/test/java/io/ballerina/flowmodelgenerator/extension/ModelGeneratorTest.java b/flow-model-generator/modules/flow-model-generator-ls-extension/src/test/java/io/ballerina/flowmodelgenerator/extension/ModelGeneratorTest.java index 2a3934028d..fd4252d28e 100644 --- a/flow-model-generator/modules/flow-model-generator-ls-extension/src/test/java/io/ballerina/flowmodelgenerator/extension/ModelGeneratorTest.java +++ b/flow-model-generator/modules/flow-model-generator-ls-extension/src/test/java/io/ballerina/flowmodelgenerator/extension/ModelGeneratorTest.java @@ -18,6 +18,8 @@ package io.ballerina.flowmodelgenerator.extension; +import com.google.gson.JsonArray; +import com.google.gson.JsonElement; import com.google.gson.JsonObject; import com.google.gson.JsonPrimitive; import io.ballerina.flowmodelgenerator.extension.request.FlowModelGeneratorRequest; @@ -54,17 +56,34 @@ public void test(Path config) throws IOException { boolean fileNameEquality = testFileName != null && balFileName.equals(testFileName.getAsString()); JsonObject modifiedDiagram = jsonModel.deepCopy(); modifiedDiagram.addProperty("fileName", balFileName); + assertDiagnosticsIncludeRange(modifiedDiagram, "diagram"); + JsonObject normalizedActualDiagram = modifiedDiagram.deepCopy(); + stripDiagnosticRanges(normalizedActualDiagram); + JsonObject normalizedExpectedDiagram = testConfig.diagram().deepCopy(); + stripDiagnosticRanges(normalizedExpectedDiagram); - boolean flowEquality = modifiedDiagram.equals(testConfig.diagram()); + boolean flowEquality = normalizedActualDiagram.equals(normalizedExpectedDiagram); if (!fileNameEquality || !flowEquality) { TestConfig updatedConfig = new TestConfig(testConfig.start(), testConfig.end(), testConfig.source(), testConfig.description(), modifiedDiagram); // updateConfig(configJsonPath, updatedConfig); - compareJsonElements(modifiedDiagram, testConfig.diagram()); + compareJsonElements(normalizedActualDiagram, normalizedExpectedDiagram); Assert.fail(String.format("Failed test: '%s' (%s)", testConfig.description(), configJsonPath)); } } + @Test + public void testDiagnosticRangesInFlowModel() throws IOException { + Path configJsonPath = configDir.resolve("diagnostics1.json"); + TestConfig testConfig = gson.fromJson(Files.newBufferedReader(configJsonPath), TestConfig.class); + + FlowModelGeneratorRequest request = new FlowModelGeneratorRequest( + getSourcePath(testConfig.source()), testConfig.start(), testConfig.end()); + JsonObject jsonModel = getResponseAndCloseFile(request, testConfig.source()).getAsJsonObject("flowModel"); + + assertDiagnosticsIncludeRange(jsonModel, "diagram"); + } + @Override protected String[] skipList() { return new String[]{ @@ -107,4 +126,72 @@ public String description() { return description == null ? "" : description; } } + + private static void stripDiagnosticRanges(JsonElement jsonElement) { + if (jsonElement == null || jsonElement.isJsonNull()) { + return; + } + if (jsonElement.isJsonArray()) { + JsonArray jsonArray = jsonElement.getAsJsonArray(); + for (JsonElement element : jsonArray) { + stripDiagnosticRanges(element); + } + return; + } + if (!jsonElement.isJsonObject()) { + return; + } + JsonObject jsonObject = jsonElement.getAsJsonObject(); + if (jsonObject.has("diagnostics")) { + JsonElement diagnosticsElement = jsonObject.get("diagnostics"); + if (diagnosticsElement.isJsonObject()) { + JsonObject diagnosticsObject = diagnosticsElement.getAsJsonObject(); + JsonArray diagnosticsArray = diagnosticsObject.getAsJsonArray("diagnostics"); + if (diagnosticsArray != null) { + for (JsonElement diagnosticElement : diagnosticsArray) { + if (diagnosticElement.isJsonObject()) { + diagnosticElement.getAsJsonObject().remove("range"); + } + } + } + } + } + for (String key : jsonObject.keySet()) { + stripDiagnosticRanges(jsonObject.get(key)); + } + } + + private static void assertDiagnosticsIncludeRange(JsonElement jsonElement, String path) { + if (jsonElement == null || jsonElement.isJsonNull()) { + return; + } + if (jsonElement.isJsonArray()) { + JsonArray jsonArray = jsonElement.getAsJsonArray(); + for (int i = 0; i < jsonArray.size(); i++) { + assertDiagnosticsIncludeRange(jsonArray.get(i), path + "[" + i + "]"); + } + return; + } + if (!jsonElement.isJsonObject()) { + return; + } + JsonObject jsonObject = jsonElement.getAsJsonObject(); + if (jsonObject.has("diagnostics")) { + JsonElement diagnosticsElement = jsonObject.get("diagnostics"); + if (diagnosticsElement.isJsonObject()) { + JsonArray diagnosticsArray = diagnosticsElement.getAsJsonObject().getAsJsonArray("diagnostics"); + if (diagnosticsArray != null) { + for (int i = 0; i < diagnosticsArray.size(); i++) { + JsonElement diagnosticElement = diagnosticsArray.get(i); + Assert.assertTrue(diagnosticElement.isJsonObject() + && diagnosticElement.getAsJsonObject().has("range"), + String.format("Expected diagnostic range at %s.diagnostics.diagnostics[%d]", path, i)); + } + } + } + } + for (String key : jsonObject.keySet()) { + assertDiagnosticsIncludeRange(jsonObject.get(key), path + "." + key); + } + } } From cc5db9f94c7d6b576a70581d562e34f68ed1f6df Mon Sep 17 00:00:00 2001 From: Kanushka Gayan Date: Mon, 23 Feb 2026 14:07:56 +0530 Subject: [PATCH 2/2] Handle diagnostic range in search and agents flow model tests --- .../extension/SearchNodesTest.java | 78 ++++++++++++++++++- .../agentsmanager/FlowNodeGeneratorTest.java | 78 ++++++++++++++++++- 2 files changed, 152 insertions(+), 4 deletions(-) diff --git a/flow-model-generator/modules/flow-model-generator-ls-extension/src/test/java/io/ballerina/flowmodelgenerator/extension/SearchNodesTest.java b/flow-model-generator/modules/flow-model-generator-ls-extension/src/test/java/io/ballerina/flowmodelgenerator/extension/SearchNodesTest.java index 25cb96d49e..8b9adeffbc 100644 --- a/flow-model-generator/modules/flow-model-generator-ls-extension/src/test/java/io/ballerina/flowmodelgenerator/extension/SearchNodesTest.java +++ b/flow-model-generator/modules/flow-model-generator-ls-extension/src/test/java/io/ballerina/flowmodelgenerator/extension/SearchNodesTest.java @@ -19,6 +19,8 @@ package io.ballerina.flowmodelgenerator.extension; import com.google.gson.JsonArray; +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; import io.ballerina.flowmodelgenerator.extension.request.SearchNodesRequest; import io.ballerina.modelgenerator.commons.AbstractLSTest; import io.ballerina.tools.text.LinePosition; @@ -50,13 +52,18 @@ public void test(Path config) throws IOException { ); JsonArray jsonModel = getResponseAndCloseFile(request, testConfig.source()).getAsJsonArray("output"); + assertDiagnosticsIncludeRange(jsonModel, "output"); + JsonArray normalizedActual = jsonModel.deepCopy(); + stripDiagnosticRanges(normalizedActual); + JsonArray normalizedExpected = testConfig.output().deepCopy(); + stripDiagnosticRanges(normalizedExpected); - if (!jsonModel.equals(testConfig.output())) { + if (!normalizedActual.equals(normalizedExpected)) { TestConfig updateConfig = new TestConfig(testConfig.source(), testConfig.description(), testConfig.position(), testConfig.queryMap(), jsonModel); // updateConfig(configJsonPath, updateConfig); - compareJsonElements(jsonModel, testConfig.output()); + compareJsonElements(normalizedActual, normalizedExpected); Assert.fail(String.format("Failed test: '%s' (%s)", testConfig.description(), configJsonPath)); } } @@ -99,4 +106,71 @@ public String description() { return description == null ? "" : description; } } + + private static void stripDiagnosticRanges(JsonElement jsonElement) { + if (jsonElement == null || jsonElement.isJsonNull()) { + return; + } + if (jsonElement.isJsonArray()) { + JsonArray jsonArray = jsonElement.getAsJsonArray(); + for (JsonElement element : jsonArray) { + stripDiagnosticRanges(element); + } + return; + } + if (!jsonElement.isJsonObject()) { + return; + } + JsonObject jsonObject = jsonElement.getAsJsonObject(); + if (jsonObject.has("diagnostics")) { + JsonElement diagnosticsElement = jsonObject.get("diagnostics"); + if (diagnosticsElement.isJsonObject()) { + JsonArray diagnosticsArray = diagnosticsElement.getAsJsonObject().getAsJsonArray("diagnostics"); + if (diagnosticsArray != null) { + for (JsonElement diagnosticElement : diagnosticsArray) { + if (diagnosticElement.isJsonObject()) { + diagnosticElement.getAsJsonObject().remove("range"); + } + } + } + } + } + for (String key : jsonObject.keySet()) { + stripDiagnosticRanges(jsonObject.get(key)); + } + } + + private static void assertDiagnosticsIncludeRange(JsonElement jsonElement, String path) { + if (jsonElement == null || jsonElement.isJsonNull()) { + return; + } + if (jsonElement.isJsonArray()) { + JsonArray jsonArray = jsonElement.getAsJsonArray(); + for (int i = 0; i < jsonArray.size(); i++) { + assertDiagnosticsIncludeRange(jsonArray.get(i), path + "[" + i + "]"); + } + return; + } + if (!jsonElement.isJsonObject()) { + return; + } + JsonObject jsonObject = jsonElement.getAsJsonObject(); + if (jsonObject.has("diagnostics")) { + JsonElement diagnosticsElement = jsonObject.get("diagnostics"); + if (diagnosticsElement.isJsonObject()) { + JsonArray diagnosticsArray = diagnosticsElement.getAsJsonObject().getAsJsonArray("diagnostics"); + if (diagnosticsArray != null) { + for (int i = 0; i < diagnosticsArray.size(); i++) { + JsonElement diagnosticElement = diagnosticsArray.get(i); + Assert.assertTrue(diagnosticElement.isJsonObject() + && diagnosticElement.getAsJsonObject().has("range"), + String.format("Expected diagnostic range at %s.diagnostics.diagnostics[%d]", path, i)); + } + } + } + } + for (String key : jsonObject.keySet()) { + assertDiagnosticsIncludeRange(jsonObject.get(key), path + "." + key); + } + } } diff --git a/flow-model-generator/modules/flow-model-generator-ls-extension/src/test/java/io/ballerina/flowmodelgenerator/extension/agentsmanager/FlowNodeGeneratorTest.java b/flow-model-generator/modules/flow-model-generator-ls-extension/src/test/java/io/ballerina/flowmodelgenerator/extension/agentsmanager/FlowNodeGeneratorTest.java index 0f3500c086..bf1f8853ba 100644 --- a/flow-model-generator/modules/flow-model-generator-ls-extension/src/test/java/io/ballerina/flowmodelgenerator/extension/agentsmanager/FlowNodeGeneratorTest.java +++ b/flow-model-generator/modules/flow-model-generator-ls-extension/src/test/java/io/ballerina/flowmodelgenerator/extension/agentsmanager/FlowNodeGeneratorTest.java @@ -18,6 +18,8 @@ package io.ballerina.flowmodelgenerator.extension.agentsmanager; +import com.google.gson.JsonArray; +import com.google.gson.JsonElement; import com.google.gson.JsonObject; import com.google.gson.JsonPrimitive; import io.ballerina.flowmodelgenerator.extension.request.FlowModelGeneratorRequest; @@ -76,13 +78,18 @@ public void test(Path config) throws IOException { boolean fileNameEquality = testFileName != null && balFileName.equals(testFileName.getAsString()); JsonObject modifiedDiagram = jsonModel.deepCopy(); modifiedDiagram.addProperty("fileName", balFileName); + assertDiagnosticsIncludeRange(modifiedDiagram, "diagram"); + JsonObject normalizedActualDiagram = modifiedDiagram.deepCopy(); + stripDiagnosticRanges(normalizedActualDiagram); + JsonObject normalizedExpectedDiagram = testConfig.diagram().deepCopy(); + stripDiagnosticRanges(normalizedExpectedDiagram); - boolean flowEquality = modifiedDiagram.equals(testConfig.diagram()); + boolean flowEquality = normalizedActualDiagram.equals(normalizedExpectedDiagram); if (!fileNameEquality || !flowEquality) { TestConfig updatedConfig = new TestConfig(testConfig.start(), testConfig.end(), testConfig.source(), testConfig.description(), modifiedDiagram); // updateConfig(configJsonPath, updatedConfig); - compareJsonElements(modifiedDiagram, testConfig.diagram()); + compareJsonElements(normalizedActualDiagram, normalizedExpectedDiagram); Assert.fail(String.format("Failed test: '%s' (%s)", testConfig.description(), configJsonPath)); } } @@ -129,4 +136,71 @@ public String description() { return description == null ? "" : description; } } + + private static void stripDiagnosticRanges(JsonElement jsonElement) { + if (jsonElement == null || jsonElement.isJsonNull()) { + return; + } + if (jsonElement.isJsonArray()) { + JsonArray jsonArray = jsonElement.getAsJsonArray(); + for (JsonElement element : jsonArray) { + stripDiagnosticRanges(element); + } + return; + } + if (!jsonElement.isJsonObject()) { + return; + } + JsonObject jsonObject = jsonElement.getAsJsonObject(); + if (jsonObject.has("diagnostics")) { + JsonElement diagnosticsElement = jsonObject.get("diagnostics"); + if (diagnosticsElement.isJsonObject()) { + JsonArray diagnosticsArray = diagnosticsElement.getAsJsonObject().getAsJsonArray("diagnostics"); + if (diagnosticsArray != null) { + for (JsonElement diagnosticElement : diagnosticsArray) { + if (diagnosticElement.isJsonObject()) { + diagnosticElement.getAsJsonObject().remove("range"); + } + } + } + } + } + for (String key : jsonObject.keySet()) { + stripDiagnosticRanges(jsonObject.get(key)); + } + } + + private static void assertDiagnosticsIncludeRange(JsonElement jsonElement, String path) { + if (jsonElement == null || jsonElement.isJsonNull()) { + return; + } + if (jsonElement.isJsonArray()) { + JsonArray jsonArray = jsonElement.getAsJsonArray(); + for (int i = 0; i < jsonArray.size(); i++) { + assertDiagnosticsIncludeRange(jsonArray.get(i), path + "[" + i + "]"); + } + return; + } + if (!jsonElement.isJsonObject()) { + return; + } + JsonObject jsonObject = jsonElement.getAsJsonObject(); + if (jsonObject.has("diagnostics")) { + JsonElement diagnosticsElement = jsonObject.get("diagnostics"); + if (diagnosticsElement.isJsonObject()) { + JsonArray diagnosticsArray = diagnosticsElement.getAsJsonObject().getAsJsonArray("diagnostics"); + if (diagnosticsArray != null) { + for (int i = 0; i < diagnosticsArray.size(); i++) { + JsonElement diagnosticElement = diagnosticsArray.get(i); + Assert.assertTrue(diagnosticElement.isJsonObject() + && diagnosticElement.getAsJsonObject().has("range"), + String.format("Expected diagnostic range at %s.diagnostics.diagnostics[%d]", path, i)); + } + } + } + } + for (String key : jsonObject.keySet()) { + assertDiagnosticsIncludeRange(jsonObject.get(key), path + "." + key); + } + } }