Example #1
0
        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();
            }
        }
Example #2
0
        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);
            }
        }
Example #3
0
        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));
        }
Example #4
0
        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();
                }
            }
        }
Example #5
0
        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);
        }
Example #6
0
        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);
        }
Example #7
0
        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);
        }
Example #8
0
        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);
            }
                );
        }
Example #11
0
        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();
        }
Example #13
0
        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);
            }
                );
        }