public async Task RequestingHighlightsForWrongNodeShouldProduceNoHighlights(DataSet dataSet) { // local function bool IsWrongNode(SyntaxBase node) => !(node is ISymbolReference) && !(node is ITopLevelNamedDeclarationSyntax) && !(node is Token); var uri = DocumentUri.From($"/{dataSet.Name}"); using var client = await IntegrationTestHelper.StartServerWithTextAsync(dataSet.Bicep, uri); var compilation = dataSet.CopyFilesAndCreateCompilation(TestContext, out _); var lineStarts = compilation.SyntaxTreeGrouping.EntryPoint.LineStarts; var wrongNodes = SyntaxAggregator.Aggregate( compilation.SyntaxTreeGrouping.EntryPoint.ProgramSyntax, new List<SyntaxBase>(), (accumulated, node) => { if (IsWrongNode(node) && !(node is ProgramSyntax)) { accumulated.Add(node); } return accumulated; }, accumulated => accumulated, (accumulated, node) => IsWrongNode(node)); foreach (var syntax in wrongNodes) { var highlights = await client.RequestDocumentHighlight(new DocumentHighlightParams { TextDocument = new TextDocumentIdentifier(uri), Position = IntegrationTestHelper.GetPosition(lineStarts, syntax) }); highlights.Should().BeNull(); } }
public async Task FindReferencesWithDeclarationsShouldProduceCorrectResults(DataSet dataSet) { var uri = DocumentUri.From($"/{dataSet.Name}"); using var client = await IntegrationTestHelper.StartServerWithText(dataSet.Bicep, uri); var compilation = new Compilation(TestResourceTypeProvider.Create(), SyntaxFactory.CreateFromText(dataSet.Bicep)); var symbolTable = compilation.ReconstructSymbolTable(); var lineStarts = TextCoordinateConverter.GetLineStarts(dataSet.Bicep); var symbolToSyntaxLookup = symbolTable .Where(pair => pair.Value.Kind != SymbolKind.Error) .ToLookup(pair => pair.Value, pair => pair.Key); foreach (var(syntax, symbol) in symbolTable.Where(s => s.Value.Kind != SymbolKind.Error)) { var locations = await client.RequestReferences(new ReferenceParams { TextDocument = new TextDocumentIdentifier(uri), Context = new ReferenceContext { IncludeDeclaration = true }, Position = PositionHelper.GetPosition(lineStarts, syntax.Span.Position) }); // all URIs should be the same in the results locations.Select(r => r.Uri).Should().AllBeEquivalentTo(uri); // calculate expected ranges var expectedRanges = symbolToSyntaxLookup[symbol] .Select(node => PositionHelper.GetNameRange(lineStarts, node)); // ranges should match what we got from our own symbol table locations.Select(l => l.Range).Should().BeEquivalentTo(expectedRanges); } }
private async Task <BicepTelemetryEvent> ResolveCompletionAsync(string text, string prefix, Position position) { var fileSystemDict = new Dictionary <Uri, string>(); var telemetryReceived = new TaskCompletionSource <BicepTelemetryEvent>(); var client = await IntegrationTestHelper.StartServerWithClientConnectionAsync( TestContext, options => { options.OnTelemetryEvent <BicepTelemetryEvent>(telemetry => telemetryReceived.SetResult(telemetry)); }, resourceTypeProvider : AzResourceTypeProvider.CreateWithAzTypes(), fileResolver : new InMemoryFileResolver(fileSystemDict)); var mainUri = DocumentUri.FromFileSystemPath("/main.bicep"); fileSystemDict[mainUri.ToUri()] = text; client.TextDocument.DidOpenTextDocument(TextDocumentParamHelper.CreateDidOpenDocumentParams(mainUri, fileSystemDict[mainUri.ToUri()], 1)); var completions = await client.RequestCompletion(new CompletionParams { TextDocument = new TextDocumentIdentifier(mainUri), Position = position, }); CompletionItem completionItem = completions.Where(x => x.Kind == CompletionItemKind.Snippet && x.Label == prefix).First(); Command? command = completionItem.Command; JArray? arguments = command !.Arguments; BicepTelemetryEvent?telemetryEvent = arguments !.First().ToObject <BicepTelemetryEvent>(); await client.ResolveCompletion(completionItem); await client.Workspace.ExecuteCommand(command); return(await IntegrationTestHelper.WithTimeoutAsync(telemetryReceived.Task)); }
public async Task NonFunctionCallSyntaxShouldProvideNoSignatureHelp(DataSet dataSet) { var uri = DocumentUri.From($"/{dataSet.Name}"); using var client = await IntegrationTestHelper.StartServerWithTextAsync(dataSet.Bicep, uri); var compilation = dataSet.CopyFilesAndCreateCompilation(TestContext, out _); var tree = compilation.SyntaxTreeGrouping.EntryPoint; var nonFunctions = SyntaxAggregator.Aggregate( tree.ProgramSyntax, new List <SyntaxBase>(), (accumulated, current) => { if (current is not FunctionCallSyntaxBase) { accumulated.Add(current); } return(accumulated); }, accumulated => accumulated, // requesting signature help on non-function nodes that are placed inside function call nodes will produce signature help // since we don't want that, stop the visitor from visiting inner nodes when a function call is encountered (accumulated, current) => current is not FunctionCallSyntaxBase); foreach (var nonFunction in nonFunctions) { using (new AssertionScope().WithVisualCursor(tree, nonFunction.Span.ToZeroLengthSpan())) { var position = PositionHelper.GetPosition(tree.LineStarts, nonFunction.Span.Position); var signatureHelp = await RequestSignatureHelp(client, position, uri); signatureHelp.Should().BeNull(); } } }
public async Task Build_command_should_generate_template_with_symbolic_names_if_enabled() { var diagnosticsListener = new MultipleMessageListener <PublishDiagnosticsParams>(); var features = BicepTestConstants.CreateFeaturesProvider( TestContext, symbolicNameCodegenEnabled: true, assemblyFileVersion: BicepTestConstants.DevAssemblyFileVersion); var client = await IntegrationTestHelper.StartServerWithClientConnectionAsync( this.TestContext, options => options.OnPublishDiagnostics(diagnosticsParams => diagnosticsListener.AddMessage(diagnosticsParams)), new LanguageServer.Server.CreationOptions( NamespaceProvider: BuiltInTestTypes.Create(), Features: features)); var outputDirectory = FileHelper.SaveEmbeddedResourcesWithPathPrefix( TestContext, typeof(ExamplesTests).Assembly, "Bicep.Core.Samples/Resources_CRLF"); var bicepFilePath = Path.Combine(outputDirectory, "main.bicep"); var expectedJson = File.ReadAllText(Path.Combine(outputDirectory, "main.symbolicnames.json")); client.TextDocument.DidOpenTextDocument(TextDocumentParamHelper.CreateDidOpenDocumentParamsFromFile(bicepFilePath, 1)); await diagnosticsListener.WaitNext(); await client.Workspace.ExecuteCommand(new Command { Name = "build", Arguments = new JArray { bicepFilePath, } }); var buildCommandOutput = File.ReadAllText(Path.ChangeExtension(bicepFilePath, ".json")); buildCommandOutput.Should().BeEquivalentToIgnoringNewlines(expectedJson); }
public static async Task <LanguageServerHelper> StartServerWithTextAsync(TestContext testContext, string text, DocumentUri documentUri, Action <LanguageClientOptions>?onClientOptions = null, Server.CreationOptions?creationOptions = null) { var diagnosticsPublished = new TaskCompletionSource <PublishDiagnosticsParams>(); creationOptions ??= new Server.CreationOptions(); creationOptions = creationOptions with { FileResolver = creationOptions.FileResolver ?? new InMemoryFileResolver(new Dictionary <Uri, string> { [documentUri.ToUri()] = text, }), ModuleRestoreScheduler = creationOptions.ModuleRestoreScheduler ?? BicepTestConstants.ModuleRestoreScheduler }; var helper = await LanguageServerHelper.StartServerWithClientConnectionAsync( testContext, options => { onClientOptions?.Invoke(options); options.OnPublishDiagnostics(p => { testContext.WriteLine($"Received {p.Diagnostics.Count()} diagnostic(s)."); diagnosticsPublished.SetResult(p); }); }, creationOptions); // send open document notification helper.Client.DidOpenTextDocument(TextDocumentParamHelper.CreateDidOpenDocumentParams(documentUri, text, 0)); testContext.WriteLine($"Opened file {documentUri}."); // notifications don't produce responses, // but our server should send us diagnostics when it receives the notification await IntegrationTestHelper.WithTimeoutAsync(diagnosticsPublished.Task); return(helper); }
public async Task RequestDeploymentGraphShouldReturnDeploymentGraph() { var diagnosticsListener = new MultipleMessageListener <PublishDiagnosticsParams>(); var fileSystemDict = new Dictionary <Uri, string>(); var client = await IntegrationTestHelper.StartServerWithClientConnectionAsync( this.TestContext, options => options.OnPublishDiagnostics(diagnosticsParams => diagnosticsListener.AddMessage(diagnosticsParams)), BuiltInTestTypes.Create(), new InMemoryFileResolver(fileSystemDict)); var mainUri = DocumentUri.FromFileSystemPath("/main.bicep"); fileSystemDict[mainUri.ToUri()] = @" resource res1 'Test.Rp/basicTests@2020-01-01' = { name: 'res1' } resource res2 'Test.Rp/readWriteTests@2020-01-01' = { name: 'res2' properites: { readwrite: mod1.outputs.output1 } } resource unknownRes = { } module mod1 './modules/module1.bicep' = { name: 'mod1' } module mod2 './modules/module2.bicep' = { name: 'mod2' } module nonExistingMod './path/to/nonExistingModule.bicep' = { } "; var module1Uri = DocumentUri.FromFileSystemPath("/modules/module1.bicep"); fileSystemDict[module1Uri.ToUri()] = @" resource res3 'Test.Rp/basicTests@2020-01-01' = { name: 'res3' } output output1 int = 123 "; var module2Uri = DocumentUri.FromFileSystemPath("/modules/module2.bicep"); fileSystemDict[module2Uri.ToUri()] = @" resource res4 'Test.Rp/basicTests@2020-01-01' = { name: 'res4' } module nestedMod './nestedModules/nestedModule.bicep' = [for x in []: { name: 'nestedMod' dependsOn: [ res4 ] }] "; var nestedModuleUri = DocumentUri.FromFileSystemPath("/modules/nestedModules/nestedModule.bicep"); fileSystemDict[nestedModuleUri.ToUri()] = @" resource res5 'Test.Rp/basicTests@2020-01-01' = { name: 'res5' } "; client.TextDocument.DidOpenTextDocument(TextDocumentParamHelper.CreateDidOpenDocumentParams(mainUri, fileSystemDict[mainUri.ToUri()], 1)); await diagnosticsListener.WaitNext(); var deploymentGraph = await client.SendRequest(new BicepDeploymentGraphParams(new TextDocumentIdentifier(mainUri)), default); deploymentGraph.Should().NotBeNull(); deploymentGraph !.Nodes.Should().Equal( new BicepDeploymentGraphNode("mod1", "<module>", false, CreateTextRange(15, 0, 17, 1), true, false, Path.GetFullPath(module1Uri.GetFileSystemPath())), new BicepDeploymentGraphNode("mod1::res3", "Test.Rp/basicTests", false, CreateTextRange(1, 0, 3, 1), false, false, Path.GetFullPath(module1Uri.GetFileSystemPath())), new BicepDeploymentGraphNode("mod2", "<module>", false, CreateTextRange(19, 0, 21, 1), true, false, Path.GetFullPath(module2Uri.GetFileSystemPath())), new BicepDeploymentGraphNode("mod2::nestedMod", "<module>", true, CreateTextRange(5, 0, 9, 1), true, false, Path.GetFullPath(nestedModuleUri.GetFileSystemPath())), new BicepDeploymentGraphNode("mod2::nestedMod::res5", "Test.Rp/basicTests", false, CreateTextRange(1, 0, 3, 1), false, false, Path.GetFullPath(nestedModuleUri.GetFileSystemPath())), new BicepDeploymentGraphNode("mod2::res4", "Test.Rp/basicTests", false, CreateTextRange(1, 0, 3, 1), false, false, Path.GetFullPath(module2Uri.GetFileSystemPath())), new BicepDeploymentGraphNode("nonExistingMod", "<module>", false, CreateTextRange(23, 0, 24, 1), false, true, Path.GetFullPath("/path/to/nonExistingModule.bicep")), new BicepDeploymentGraphNode("res1", "Test.Rp/basicTests", false, CreateTextRange(1, 0, 3, 1), false, false, Path.GetFullPath(mainUri.GetFileSystemPath())), new BicepDeploymentGraphNode("res2", "Test.Rp/readWriteTests", false, CreateTextRange(5, 0, 10, 1), false, true, Path.GetFullPath(mainUri.GetFileSystemPath()))); deploymentGraph !.Edges.Should().Equal( new BicepDeploymentGraphEdge("mod2::nestedMod", "mod2::res4"), new BicepDeploymentGraphEdge("res2", "mod1")); deploymentGraph !.ErrorCount.Should().Be(7); }
public async Task HoveringOverSymbolReferencesAndDeclarationsShouldProduceHovers(DataSet dataSet) { var(compilation, _, fileUri) = await dataSet.SetupPrerequisitesAndCreateCompilation(TestContext); var uri = DocumentUri.From(fileUri); var client = await IntegrationTestHelper.StartServerWithTextAsync(this.TestContext, dataSet.Bicep, uri, creationOptions : new LanguageServer.Server.CreationOptions(NamespaceProvider: BicepTestConstants.NamespaceProvider, FileResolver: BicepTestConstants.FileResolver)); var symbolTable = compilation.ReconstructSymbolTable(); var lineStarts = compilation.SourceFileGrouping.EntryPoint.LineStarts; var symbolReferences = SyntaxAggregator.Aggregate( compilation.SourceFileGrouping.EntryPoint.ProgramSyntax, new List <SyntaxBase>(), (accumulated, node) => { if (node is ISymbolReference || node is ITopLevelNamedDeclarationSyntax) { accumulated.Add(node); } return(accumulated); }, accumulated => accumulated); foreach (var symbolReference in symbolReferences) { // by default, request a hover on the first character of the syntax, but for certain syntaxes, this doesn't make sense. // for example on an instance function call 'az.resourceGroup()', it only makes sense to request a hover on the 3rd character. var nodeForHover = symbolReference switch { ITopLevelDeclarationSyntax d => d.Keyword, ResourceAccessSyntax r => r.ResourceName, FunctionCallSyntaxBase f => f.Name, _ => symbolReference, }; var hover = await client.RequestHover(new HoverParams { TextDocument = new TextDocumentIdentifier(uri), Position = TextCoordinateConverter.GetPosition(lineStarts, nodeForHover.Span.Position) }); // fancy method to give us some annotated source code to look at if any assertions fail :) using (new AssertionScope().WithVisualCursor(compilation.SourceFileGrouping.EntryPoint, nodeForHover.Span.ToZeroLengthSpan())) { if (!symbolTable.TryGetValue(symbolReference, out var symbol)) { if (symbolReference is InstanceFunctionCallSyntax && compilation.GetEntrypointSemanticModel().GetSymbolInfo(symbolReference) is FunctionSymbol ifcSymbol) { ValidateHover(hover, ifcSymbol); break; } // symbol ref not bound to a symbol hover.Should().BeNull(); continue; } switch (symbol !.Kind) {
public async Task RequestDocumentSymbol_should_return_full_symbol_list() { var documentUri = DocumentUri.From("/template.bicep"); var diagsReceived = new TaskCompletionSource <PublishDiagnosticsParams>(); var client = await IntegrationTestHelper.StartServerWithClientConnection(options => { options.OnPublishDiagnostics(diags => { diagsReceived.SetResult(diags); }); }); // client opens the document client.TextDocument.DidOpenTextDocument(new DidOpenTextDocumentParams { TextDocument = new TextDocumentItem { LanguageId = "bicep", Version = 0, Uri = documentUri, Text = @" param myParam string = 'test' resource myRes 'myRp/provider@2019-01-01' = { name = 'test' } output myOutput string = 'myOutput' ", }, }); // client requests symbols var symbols = await client.TextDocument.RequestDocumentSymbol(new DocumentSymbolParams { TextDocument = new TextDocumentIdentifier { Uri = documentUri, }, }); symbols.Should().SatisfyRespectively( x => { x.DocumentSymbol.Name.Should().Be("myParam"); x.DocumentSymbol.Kind.Should().Be(SymbolKind.Field); x.DocumentSymbol.Range.Should().HaveRange((1, 0), (2, 0)); }, x => { x.DocumentSymbol.Name.Should().Be("myRes"); x.DocumentSymbol.Kind.Should().Be(SymbolKind.Object); x.DocumentSymbol.Range.Should().HaveRange((2, 0), (5, 0)); }, x => { x.DocumentSymbol.Name.Should().Be("myOutput"); x.DocumentSymbol.Kind.Should().Be(SymbolKind.Interface); x.DocumentSymbol.Range.Should().HaveRange((5, 2), (6, 0)); } ); // client deletes the output and renames the resource client.TextDocument.DidChangeTextDocument(new DidChangeTextDocumentParams { TextDocument = new VersionedTextDocumentIdentifier { Version = 1, Uri = documentUri, }, ContentChanges = new Container <TextDocumentContentChangeEvent>( // for now we're sending the full document every time, nothing fancy.. new TextDocumentContentChangeEvent { Text = @" param myParam string = 'test' resource myRenamedRes 'myRp/provider@2019-01-01' = { name = 'test' } ", } ) }); // client requests symbols symbols = await client.TextDocument.RequestDocumentSymbol(new DocumentSymbolParams { TextDocument = new TextDocumentIdentifier { Uri = documentUri, }, }); symbols.Should().SatisfyRespectively( x => { x.DocumentSymbol.Name.Should().Be("myParam"); x.DocumentSymbol.Kind.Should().Be(SymbolKind.Field); x.DocumentSymbol.Range.Should().HaveRange((1, 0), (2, 0)); }, x => { x.DocumentSymbol.Name.Should().Be("myRenamedRes"); x.DocumentSymbol.Kind.Should().Be(SymbolKind.Object); x.DocumentSymbol.Range.Should().HaveRange((2, 0), (5, 0)); } ); }
public async Task RequestDocumentSymbol_should_return_full_symbol_list() { var documentUri = DocumentUri.From("/template.bicep"); var diagsReceived = new TaskCompletionSource <PublishDiagnosticsParams>(); var client = await IntegrationTestHelper.StartServerWithClientConnectionAsync(options => { options.OnPublishDiagnostics(diags => { diagsReceived.SetResult(diags); }); }); // client opens the document client.TextDocument.DidOpenTextDocument(TextDocumentParamHelper.CreateDidOpenDocumentParams(documentUri, @" param myParam string = 'test' resource myRes 'myRp/provider@2019-01-01' = { name: 'test' } module myMod './module.bicep' = { name: 'test' } output myOutput string = 'myOutput' ", 0)); // client requests symbols var symbols = await client.TextDocument.RequestDocumentSymbol(new DocumentSymbolParams { TextDocument = new TextDocumentIdentifier { Uri = documentUri, }, }); symbols.Should().SatisfyRespectively( x => { x.DocumentSymbol !.Name.Should().Be("myParam"); x.DocumentSymbol.Kind.Should().Be(SymbolKind.Field); }, x => { x.DocumentSymbol !.Name.Should().Be("myRes"); x.DocumentSymbol.Kind.Should().Be(SymbolKind.Object); }, x => { x.DocumentSymbol !.Name.Should().Be("myMod"); x.DocumentSymbol.Kind.Should().Be(SymbolKind.Module); }, x => { x.DocumentSymbol !.Name.Should().Be("myOutput"); x.DocumentSymbol.Kind.Should().Be(SymbolKind.Interface); } ); // client deletes the output and renames the resource client.TextDocument.DidChangeTextDocument(TextDocumentParamHelper.CreateDidChangeTextDocumentParams(documentUri, @" param myParam string = 'test' resource myRenamedRes 'myRp/provider@2019-01-01' = { name: 'test' } module myMod './module.bicep' = { name: 'test' } ", 1)); // client requests symbols symbols = await client.TextDocument.RequestDocumentSymbol(new DocumentSymbolParams { TextDocument = new TextDocumentIdentifier { Uri = documentUri, }, }); symbols.Should().SatisfyRespectively( x => { x.DocumentSymbol !.Name.Should().Be("myParam"); x.DocumentSymbol.Kind.Should().Be(SymbolKind.Field); }, x => { x.DocumentSymbol !.Name.Should().Be("myRenamedRes"); x.DocumentSymbol.Kind.Should().Be(SymbolKind.Object); }, x => { x.DocumentSymbol !.Name.Should().Be("myMod"); x.DocumentSymbol.Kind.Should().Be(SymbolKind.Module); } ); }
public async Task VerifyResourceBodyCompletionWithDiscriminatedObjectTypeContainsRequiredPropertiesSnippet() { string text = @"resource deploymentScripts 'Microsoft.Resources/deploymentScripts@2020-10-01'="; var syntaxTree = SyntaxTree.Create(new Uri("file:///main.bicep"), text); using var client = await IntegrationTestHelper.StartServerWithTextAsync(text, syntaxTree.FileUri, resourceTypeProvider : TypeProvider); var completions = await client.RequestCompletion(new CompletionParams { TextDocument = new TextDocumentIdentifier(syntaxTree.FileUri), Position = TextCoordinateConverter.GetPosition(syntaxTree.LineStarts, text.Length), }); completions.Should().SatisfyRespectively( c => { c.Label.Should().Be("{}"); }, c => { c.InsertTextFormat.Should().Be(InsertTextFormat.Snippet); c.Label.Should().Be("required-properties-AzureCLI"); c.Detail.Should().Be("Required properties"); c.TextEdit?.NewText?.Should().BeEquivalentToIgnoringNewlines(@"{ name: $1 location: $2 kind: 'AzureCLI' properties: { azCliVersion: $3 retentionInterval: $4 } $0 }"); }, c => { c.Label.Should().Be("required-properties-AzurePowerShell"); c.Detail.Should().Be("Required properties"); c.TextEdit?.NewText?.Should().BeEquivalentToIgnoringNewlines(@"{ name: $1 location: $2 kind: 'AzurePowerShell' properties: { azPowerShellVersion: $3 retentionInterval: $4 } $0 }"); }, c => { c.Label.Should().Be("if"); }, c => { c.Label.Should().Be("for"); }, c => { c.Label.Should().Be("for-indexed"); }, c => { c.Label.Should().Be("for-filtered"); }); }
public async Task DidOpenTextDocument_should_trigger_PublishDiagnostics() { var documentUri = DocumentUri.From("/template.bicep"); var diagsReceived = new TaskCompletionSource <PublishDiagnosticsParams>(); var client = await IntegrationTestHelper.StartServerWithClientConnectionAsync(options => { options.OnPublishDiagnostics(diags => { diagsReceived.SetResult(diags); }); }); // open document client.TextDocument.DidOpenTextDocument(TextDocumentParamHelper.CreateDidOpenDocumentParams(documentUri, @" param myParam string = 2 resource myRes 'invalidFormat' = { } randomToken ", 1)); var response = await IntegrationTestHelper.WithTimeoutAsync(diagsReceived.Task); response.Diagnostics.Should().SatisfyRespectively( d => { d.Range.Should().HaveRange((1, 23), (1, 24)); d.Should().HaveCodeAndSeverity("BCP027", DiagnosticSeverity.Error); }, d => { d.Range.Should().HaveRange((2, 15), (2, 30)); d.Should().HaveCodeAndSeverity("BCP029", DiagnosticSeverity.Error); }, d => { d.Range.Should().HaveRange((5, 0), (5, 11)); d.Should().HaveCodeAndSeverity("BCP007", DiagnosticSeverity.Error); } ); // change document diagsReceived = new TaskCompletionSource <PublishDiagnosticsParams>(); client.TextDocument.DidChangeTextDocument(TextDocumentParamHelper.CreateDidChangeTextDocumentParams(documentUri, @" param myParam string = 'fixed!' resource myRes 'invalidFormat' = { } randomToken ", 2)); response = await IntegrationTestHelper.WithTimeoutAsync(diagsReceived.Task); response.Diagnostics.Should().SatisfyRespectively( d => { d.Range.Should().HaveRange((2, 15), (2, 30)); d.Should().HaveCodeAndSeverity("BCP029", DiagnosticSeverity.Error); }, d => { d.Range.Should().HaveRange((5, 0), (5, 11)); d.Should().HaveCodeAndSeverity("BCP007", DiagnosticSeverity.Error); } ); // close document diagsReceived = new TaskCompletionSource <PublishDiagnosticsParams>(); client.TextDocument.DidCloseTextDocument(TextDocumentParamHelper.CreateDidCloseTextDocumentParams(documentUri, 3)); response = await IntegrationTestHelper.WithTimeoutAsync(diagsReceived.Task); response.Diagnostics.Should().BeEmpty(); }
public async Task DidOpenTextDocument_should_trigger_PublishDiagnostics() { var documentUri = DocumentUri.From("/template.bicep"); var diagsReceived = new TaskCompletionSource <PublishDiagnosticsParams>(); var client = await IntegrationTestHelper.StartServerWithClientConnectionAsync(this.TestContext, options => { options.OnPublishDiagnostics(diags => { diagsReceived.SetResult(diags); }); }); // open document client.TextDocument.DidOpenTextDocument(TextDocumentParamHelper.CreateDidOpenDocumentParams(documentUri, @" param myParam string = 2 resource myRes 'invalidFormat' = { } randomToken ", 1)); var response = await IntegrationTestHelper.WithTimeoutAsync(diagsReceived.Task); response.Diagnostics.Should().SatisfyRespectively( d => { d.Range.Should().HaveRange((1, 6), (1, 13)); // note documentation pretty printing moves Uri to code for output d.Should().HaveCodeAndSeverity(new NoUnusedParametersRule().Uri !.AbsoluteUri, DiagnosticSeverity.Warning); }, d => { d.Range.Should().HaveRange((1, 23), (1, 24)); d.Should().HaveCodeAndSeverity("BCP027", DiagnosticSeverity.Error); }, d => { d.Range.Should().HaveRange((2, 15), (2, 30)); d.Should().HaveCodeAndSeverity("BCP029", DiagnosticSeverity.Error); }, d => { d.Range.Should().HaveRange((5, 0), (5, 11)); d.Should().HaveCodeAndSeverity("BCP007", DiagnosticSeverity.Error); } ); // change document diagsReceived = new TaskCompletionSource <PublishDiagnosticsParams>(); client.TextDocument.DidChangeTextDocument(TextDocumentParamHelper.CreateDidChangeTextDocumentParams(documentUri, @" param myParam string = 'fixed!' resource myRes 'invalidFormat' = { } randomToken ", 2)); response = await IntegrationTestHelper.WithTimeoutAsync(diagsReceived.Task); response.Diagnostics.Should().SatisfyRespectively( d => { d.Range.Should().HaveRange((1, 6), (1, 13)); // documentation provided with linter sets code to uri for pretty link print outs d.Should().HaveCodeAndSeverity(new NoUnusedParametersRule().Uri !.AbsoluteUri, DiagnosticSeverity.Warning); }, d => { d.Range.Should().HaveRange((2, 15), (2, 30)); d.Should().HaveCodeAndSeverity("BCP029", DiagnosticSeverity.Error); }, d => { d.Range.Should().HaveRange((5, 0), (5, 11)); d.Should().HaveCodeAndSeverity("BCP007", DiagnosticSeverity.Error); } ); // close document diagsReceived = new TaskCompletionSource <PublishDiagnosticsParams>(); client.TextDocument.DidCloseTextDocument(TextDocumentParamHelper.CreateDidCloseTextDocumentParams(documentUri, 3)); response = await IntegrationTestHelper.WithTimeoutAsync(diagsReceived.Task); response.Diagnostics.Should().BeEmpty(); }
public async Task DidOpenTextDocument_should_trigger_PublishDiagnostics() { var documentUri = DocumentUri.From("/template.bicep"); var diagsReceived = new TaskCompletionSource <PublishDiagnosticsParams>(); var client = await IntegrationTestHelper.StartServerWithClientConnection(options => { options.OnPublishDiagnostics(diags => { diagsReceived.SetResult(diags); }); }); client.TextDocument.DidOpenTextDocument(new DidOpenTextDocumentParams { TextDocument = new TextDocumentItem { LanguageId = "bicep", Version = 1, Uri = documentUri, Text = @" param myParam string = 2 resource myRes 'invalidFormat' = { } randomToken ", }, }); var response = await IntegrationTestHelper.WithTimeout(diagsReceived.Task); response.Diagnostics.Should().SatisfyRespectively( d => { d.Range.Should().HaveRange((1, 23), (1, 24)); d.Should().HaveCodeAndSeverity("BCP027", DiagnosticSeverity.Error); }, d => { d.Range.Should().HaveRange((2, 15), (2, 30)); d.Should().HaveCodeAndSeverity("BCP029", DiagnosticSeverity.Error); }, d => { d.Range.Should().HaveRange((5, 0), (5, 11)); d.Should().HaveCodeAndSeverity("BCP007", DiagnosticSeverity.Error); } ); diagsReceived = new TaskCompletionSource <PublishDiagnosticsParams>(); client.TextDocument.DidChangeTextDocument(new DidChangeTextDocumentParams { TextDocument = new VersionedTextDocumentIdentifier { Version = 2, Uri = documentUri, }, ContentChanges = new Container <TextDocumentContentChangeEvent>( new TextDocumentContentChangeEvent { Text = @" param myParam string = 'fixed!' resource myRes 'invalidFormat' = { } randomToken ", } ), }); response = await IntegrationTestHelper.WithTimeout(diagsReceived.Task); response.Diagnostics.Should().SatisfyRespectively( d => { d.Range.Should().HaveRange((2, 15), (2, 30)); d.Should().HaveCodeAndSeverity("BCP029", DiagnosticSeverity.Error); }, d => { d.Range.Should().HaveRange((5, 0), (5, 11)); d.Should().HaveCodeAndSeverity("BCP007", DiagnosticSeverity.Error); } ); }