File: VirtualProjectXmlProviderTests.cs
Web Access
Project: src\src\LanguageServer\Microsoft.CodeAnalysis.LanguageServer.UnitTests\Microsoft.CodeAnalysis.LanguageServer.UnitTests.csproj (Microsoft.CodeAnalysis.LanguageServer.UnitTests)
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
 
// Uncomment this to test run-api locally.
// Eventually when a new enough SDK is adopted in-repo we can remove this
//#define RoslynTestRunApi
 
using System.Text;
using System.Text.RegularExpressions;
using Microsoft.CodeAnalysis.LanguageServer.FileBasedPrograms;
using Microsoft.Extensions.Logging;
using Roslyn.LanguageServer.Protocol;
using Roslyn.Test.Utilities;
using Xunit.Abstractions;
 
namespace Microsoft.CodeAnalysis.LanguageServer.UnitTests;
 
/// <summary>
/// Goal of these tests:
/// - Ensure that the various request/response forms work as expected in basic scenarios.
/// - Ensure that various properties on the response are populated in a reasonable way.
/// Non-goals:
/// - Thorough behavioral testing.
/// - Testing of more intricate behaviors which are subject to change.
/// </summary>
public sealed class VirtualProjectXmlProviderTests : AbstractLanguageServerHostTests
{
    public VirtualProjectXmlProviderTests(ITestOutputHelper testOutputHelper) : base(testOutputHelper)
    {
    }
 
    private class EnableRunApiTests : ExecutionCondition
    {
        public override bool ShouldSkip =>
#if RoslynTestRunApi
            false;
#else
            true;
#endif
 
        public override string SkipReason => $"Compilation symbol 'RoslynTestRunApi' is not defined.";
    }
 
    private async Task<VirtualProjectXmlProvider> GetProjectXmlProviderAsync()
    {
        var (exportProvider, _) = await LanguageServerTestComposition.CreateExportProviderAsync(
            LoggerFactory, includeDevKitComponents: false, MefCacheDirectory.Path, extensionPaths: null);
        return exportProvider.GetExportedValue<VirtualProjectXmlProvider>();
    }
 
    [Fact]
    public async Task GetProjectXml_FileBasedProgram_SdkTooOld_01()
    {
        var projectProvider = await GetProjectXmlProviderAsync();
 
        var tempDir = TempRoot.CreateDirectory();
        var appFile = tempDir.CreateFile("app.cs");
        await appFile.WriteAllTextAsync("""
            Console.WriteLine("Hello, world!");
            """);
 
        var globalJsonFile = tempDir.CreateFile("global.json");
        globalJsonFile.WriteAllBytes(Encoding.UTF8.GetBytes("""
            {
              "sdk": {
                "version": "9.0.105"
              }
            }
            """));
 
        var contentNullable = await projectProvider.GetVirtualProjectContentAsync(appFile.Path, CancellationToken.None);
        Assert.Null(contentNullable);
    }
 
    [ConditionalFact(typeof(EnableRunApiTests))]
    public async Task GetProjectXml_FileBasedProgram_01()
    {
        var projectProvider = await GetProjectXmlProviderAsync();
 
        var tempDir = TempRoot.CreateDirectory();
        var appFile = tempDir.CreateFile("app.cs");
        await appFile.WriteAllTextAsync("""
            Console.WriteLine("Hello, world!");
            """);
 
        var globalJsonFile = tempDir.CreateFile("global.json");
        await globalJsonFile.WriteAllTextAsync("""
            {
              "sdk": {
                "version": "10.0.100-preview.5.25265.12"
              }
            }
            """);
 
        var contentNullable = await projectProvider.GetVirtualProjectContentAsync(appFile.Path, CancellationToken.None);
        var content = contentNullable.Value;
        var virtualProjectXml = content.VirtualProjectXml;
        LoggerFactory.CreateLogger<VirtualProjectXmlProviderTests>().LogTrace(virtualProjectXml);
 
        Assert.Contains("<TargetFramework>net10.0</TargetFramework>", virtualProjectXml);
        Assert.Contains("<ArtifactsPath>", virtualProjectXml);
        Assert.Empty(content.Diagnostics);
    }
 
    [ConditionalFact(typeof(EnableRunApiTests))]
    public async Task GetProjectXml_NonFileBasedProgram_01()
    {
        var projectProvider = await GetProjectXmlProviderAsync();
 
        var tempDir = TempRoot.CreateDirectory();
        var appFile = tempDir.CreateFile("app.cs");
        await appFile.WriteAllTextAsync("""
            public class C
            {
            }
            """);
 
        var globalJsonFile = tempDir.CreateFile("global.json");
        await globalJsonFile.WriteAllTextAsync("""
            {
              "sdk": {
                "version": "10.0.100-preview.5.25265.12"
              }
            }
            """);
 
        var contentNullable = await projectProvider.GetVirtualProjectContentAsync(appFile.Path, CancellationToken.None);
        var content = contentNullable.Value;
        LoggerFactory.CreateLogger<VirtualProjectXmlProviderTests>().LogTrace(content.VirtualProjectXml);
 
        Assert.Contains("<TargetFramework>net10.0</TargetFramework>", content.VirtualProjectXml);
        Assert.Contains("<ArtifactsPath>", content.VirtualProjectXml);
        Assert.Empty(content.Diagnostics);
    }
 
    [ConditionalFact(typeof(EnableRunApiTests))]
    public async Task GetProjectXml_BadPath_01()
    {
        var projectProvider = await GetProjectXmlProviderAsync();
 
        var tempDir = TempRoot.CreateDirectory();
 
        var globalJsonFile = tempDir.CreateFile("global.json");
        await globalJsonFile.WriteAllTextAsync("""
            {
              "sdk": {
                "version": "10.0.100-preview.5.25265.12"
              }
            }
            """);
 
        var content = await projectProvider.GetVirtualProjectContentAsync(Path.Combine(tempDir.Path, "BAD"), CancellationToken.None);
        Assert.Null(content);
    }
 
    [ConditionalFact(typeof(EnableRunApiTests))]
    public async Task GetProjectXml_BadDirective_01()
    {
        var projectProvider = await GetProjectXmlProviderAsync();
 
        var tempDir = TempRoot.CreateDirectory();
        var appFile = tempDir.CreateFile("app.cs");
        await appFile.WriteAllTextAsync("""
            #:package Newtonsoft.Json@13.0.3
            #:BAD
            Console.WriteLine("Hello, world!");
            """);
 
        var globalJsonFile = tempDir.CreateFile("global.json");
        await globalJsonFile.WriteAllTextAsync("""
            {
              "sdk": {
                "version": "10.0.100-preview.5.25265.12"
              }
            }
            """);
 
        var contentNullable = await projectProvider.GetVirtualProjectContentAsync(appFile.Path, CancellationToken.None);
        var content = contentNullable.Value;
        var diagnostic = content.Diagnostics.Single();
        Assert.Contains("Unrecognized directive 'BAD'", diagnostic.Message);
        Assert.Equal(appFile.Path, diagnostic.Location.Path);
 
        // LinePositionSpan is not deserializing properly.
        // Address when implementing editor squiggles. https://github.com/dotnet/roslyn/issues/78688
        Assert.Equal("(0,0)-(0,0)", diagnostic.Location.Span.ToString());
    }
}