Add Language Server Tests Back In (#1576)

This commit is contained in:
Scott Carda 2022-11-29 20:09:03 -08:00 коммит произвёл GitHub
Родитель 16c5e86de2
Коммит 82e9304d61
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 4AEE18F83AFDEB23
22 изменённых файлов: 347 добавлений и 161 удалений

Просмотреть файл

@ -1,7 +1,7 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 16
VisualStudioVersion = 16.0.28809.33
# Visual Studio Version 17
VisualStudioVersion = 17.4.33103.184
MinimumVisualStudioVersion = 10.0.40219.1
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "CompilationManager", "src\QsCompiler\CompilationManager\CompilationManager.csproj", "{8990670B-B9D2-4485-AA5B-34A301A11CD3}"
EndProject
@ -73,6 +73,8 @@ Project("{6EC3EE1D-3C4E-46DD-8F32-0CC8E7565705}") = "App", "src\QsFmt\App\App.fs
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Telemetry", "src\Telemetry\Library\Telemetry.csproj", "{2A562128-2FD0-47CF-B457-DDF3C19CCDAC}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Tests.LanguageServer", "src\QsCompiler\Tests.LanguageServer\Tests.LanguageServer.csproj", "{A9E3087B-F21A-45D4-A0A8-DF1DB0043233}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@ -431,6 +433,18 @@ Global
{2A562128-2FD0-47CF-B457-DDF3C19CCDAC}.Release|x64.Build.0 = Release|Any CPU
{2A562128-2FD0-47CF-B457-DDF3C19CCDAC}.Release|x86.ActiveCfg = Release|Any CPU
{2A562128-2FD0-47CF-B457-DDF3C19CCDAC}.Release|x86.Build.0 = Release|Any CPU
{A9E3087B-F21A-45D4-A0A8-DF1DB0043233}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{A9E3087B-F21A-45D4-A0A8-DF1DB0043233}.Debug|Any CPU.Build.0 = Debug|Any CPU
{A9E3087B-F21A-45D4-A0A8-DF1DB0043233}.Debug|x64.ActiveCfg = Debug|Any CPU
{A9E3087B-F21A-45D4-A0A8-DF1DB0043233}.Debug|x64.Build.0 = Debug|Any CPU
{A9E3087B-F21A-45D4-A0A8-DF1DB0043233}.Debug|x86.ActiveCfg = Debug|Any CPU
{A9E3087B-F21A-45D4-A0A8-DF1DB0043233}.Debug|x86.Build.0 = Debug|Any CPU
{A9E3087B-F21A-45D4-A0A8-DF1DB0043233}.Release|Any CPU.ActiveCfg = Release|Any CPU
{A9E3087B-F21A-45D4-A0A8-DF1DB0043233}.Release|Any CPU.Build.0 = Release|Any CPU
{A9E3087B-F21A-45D4-A0A8-DF1DB0043233}.Release|x64.ActiveCfg = Release|Any CPU
{A9E3087B-F21A-45D4-A0A8-DF1DB0043233}.Release|x64.Build.0 = Release|Any CPU
{A9E3087B-F21A-45D4-A0A8-DF1DB0043233}.Release|x86.ActiveCfg = Release|Any CPU
{A9E3087B-F21A-45D4-A0A8-DF1DB0043233}.Release|x86.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@ -454,6 +468,7 @@ Global
{4D1F507F-7382-4F5B-9923-58398A565D8B} = {755A6971-AD80-4519-A64E-0333B89C1E9D}
{AD350766-EE10-4DE3-A834-129D026487FB} = {755A6971-AD80-4519-A64E-0333B89C1E9D}
{2A562128-2FD0-47CF-B457-DDF3C19CCDAC} = {755A6971-AD80-4519-A64E-0333B89C1E9D}
{A9E3087B-F21A-45D4-A0A8-DF1DB0043233} = {B4A9484D-31FC-4A27-9E26-4C8DE3E02D77}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {B921C36B-4574-4025-8FE3-E5BD2D3D2B81}

Просмотреть файл

@ -99,7 +99,7 @@ namespace Microsoft.Quantum.QsCompiler.CompilationBuilder
/// </remarks>
public bool QueueForExecution<T>(Func<T> execute, [MaybeNull] out T result)
{
T res = default(T);
T res = default;
var succeeded = true;
this.QueueForExecutionAsync(() =>
{

Просмотреть файл

@ -110,7 +110,7 @@ namespace Microsoft.Quantum.QsCompiler.CompilationBuilder
? path ?? string.Empty
: string.Empty;
private ImmutableDictionary<string, string?> BuildProperties { get; }
internal ImmutableDictionary<string, string?> BuildProperties { get; }
public static ProjectProperties Empty =>
new ProjectProperties(ImmutableDictionary<string, string?>.Empty);
@ -123,7 +123,7 @@ namespace Microsoft.Quantum.QsCompiler.CompilationBuilder
{
public delegate bool Loader(Uri projectFile, [NotNullWhen(true)] out ProjectInformation? projectInfo);
internal ProjectProperties Properties { get; }
public ProjectProperties Properties { get; }
public ImmutableArray<string> SourceFiles { get; }
@ -309,6 +309,16 @@ namespace Microsoft.Quantum.QsCompiler.CompilationBuilder
.ToImmutableHashSet();
}
/// <summary>
/// Retrieves the currently set project information that is used to load the project.
/// </summary>
internal ProjectInformation GetProjectInformation() =>
new ProjectInformation(
sourceFiles: this.specifiedSourceFiles.Select(uri => uri.AbsolutePath),
projectReferences: this.specifiedProjectReferences.Select(uri => uri.AbsolutePath),
references: this.specifiedReferences.Select(uri => uri.AbsolutePath),
buildProperties: this.Properties.BuildProperties);
/// <summary>
/// If the project is not yet loaded, loads all specified source file, dll references and project references
/// using <paramref name="projectOutputPaths"/> to resolve the dll output paths for project references.
@ -1441,14 +1451,8 @@ namespace Microsoft.Quantum.QsCompiler.CompilationBuilder
/// This method waits for all currently running or queued tasks to finish
/// before getting the file content.
/// </remarks>
public string[]? FileContentInMemory(TextDocumentIdentifier textDocument)
{
if (textDocument?.Uri == null)
{
return null;
}
this.load.QueueForExecution(
public string[]? FileContentInMemory(TextDocumentIdentifier textDocument) =>
textDocument?.Uri != null && this.load.QueueForExecution(
() =>
{
// NOTE: the call below prevents any consolidating of the processing queues
@ -1456,9 +1460,28 @@ namespace Microsoft.Quantum.QsCompiler.CompilationBuilder
var manager = this.Manager(textDocument.Uri);
return manager?.FileContentInMemory(textDocument);
},
out var content);
return content;
}
out var content)
? content : null;
/// <summary>
/// Returns the project information stored for the project defined by
/// the given <paramref name="projectFile"/>, if it is listed as a
/// project with this manager.
/// </summary>
/// <remarks>
/// Returns null if the given project file is null or the project is not
/// registered with this manager.
/// <para/>
/// This method waits for all currently running or queued tasks to finish
/// before getting the file content.
/// </remarks>
public ProjectInformation? GetProjectInformation(TextDocumentIdentifier projectFile) =>
projectFile?.Uri != null && this.load.QueueForExecution(
() => this.projects.TryGetValue(projectFile.Uri, out Project project)
? project.GetProjectInformation()
: null,
out var info)
? info : null;
/* static routines related to loading the content needed for compilation */

Просмотреть файл

@ -13,6 +13,7 @@ namespace Microsoft.Quantum.QsLanguageServer
// commands for diagnostic purposes
internal const string FileContentInMemory = "qsLanguageServer/fileContentInMemory";
internal const string FileDiagnostics = "qsLanguageServer/fileDiagnostics";
internal const string ProjectInformation = "qsLanguageServer/projectInformation";
}
public class ProtocolError

Просмотреть файл

@ -34,8 +34,7 @@ namespace Microsoft.Quantum.QsLanguageServer
/// needed to determine if the reality of a source file that has changed on disk is indeed given by the content on disk,
/// or whether its current state as it is in the editor needs to be preserved
/// </summary>
private readonly ConcurrentDictionary<Uri, FileContentManager> openFiles =
new ConcurrentDictionary<Uri, FileContentManager>();
private readonly ConcurrentDictionary<Uri, FileContentManager> openFiles = new();
private FileContentManager? GetOpenFile(Uri key) => this.openFiles.TryGetValue(key, out var file) ? file : null;
@ -203,7 +202,7 @@ namespace Microsoft.Quantum.QsLanguageServer
/// and publishes suitable diagnostics for it.
/// </summary>
public Task LoadProjectsAsync(IEnumerable<Uri> projects) =>
this.projectLoader is object ?
this.projectLoader is not null ?
this.projects.LoadProjectsAsync(projects, this.QsProjectLoader, this.GetOpenFile) :
Task.CompletedTask;
@ -212,7 +211,7 @@ namespace Microsoft.Quantum.QsLanguageServer
/// updates that project in the list of tracked projects or adds it if needed, and publishes suitable diagnostics for it.
/// </summary>
public Task ProjectDidChangeOnDiskAsync(Uri project) =>
this.projectLoader is object ?
this.projectLoader is not null ?
this.projects.ProjectChangedOnDiskAsync(project, this.QsProjectLoader, this.GetOpenFile) :
Task.CompletedTask;
@ -545,7 +544,48 @@ namespace Microsoft.Quantum.QsLanguageServer
internal Diagnostic[]? FileDiagnostics(TextDocumentIdentifier textDocument)
{
var allDiagnostics = this.projects.GetDiagnostics(textDocument?.Uri);
return allDiagnostics?.Count() == 1 ? allDiagnostics.Single().Diagnostics : null; // count is > 1 if the given uri corresponds to a project file
return allDiagnostics?.Length == 1 ? allDiagnostics.Single().Diagnostics : null; // count is > 1 if the given uri corresponds to a project file
}
/// <summary>
/// Waits for all currently running or queued tasks to finish before getting the project information.
/// -> Method to be used for testing/diagnostic purposes only!
/// </summary>
internal string? ProjectInformation(TextDocumentIdentifier textDocument)
{
var projectInfo = this.projects.GetProjectInformation(textDocument);
if (projectInfo is null)
{
return null;
}
var stringWriter = new StringWriter();
var writer = System.Xml.XmlWriter.Create(stringWriter);
void WriteElementGroup(string groupName, IEnumerable<string> paths)
{
writer.WriteStartElement(groupName);
foreach (var path in paths)
{
writer.WriteStartElement("File");
writer.WriteAttributeString("Path", path);
writer.WriteEndElement();
}
writer.WriteEndElement();
}
writer.WriteStartDocument();
writer.WriteStartElement("ProjectInfo");
writer.WriteAttributeString("OutputPath", projectInfo.Properties.DllOutputPath);
writer.WriteAttributeString("TargetCapability", projectInfo.Properties.TargetCapability.Name);
writer.WriteAttributeString("ProcessorArchitecture", projectInfo.Properties.ProcessorArchitecture);
WriteElementGroup("Sources", projectInfo.SourceFiles);
WriteElementGroup("ProjectReferences", projectInfo.ProjectReferences);
WriteElementGroup("References", projectInfo.References);
writer.WriteEndElement();
writer.WriteEndDocument();
writer.Flush();
return stringWriter.ToString();
}
}
}

Просмотреть файл

@ -7,7 +7,6 @@ using System.Collections.Generic;
using System.Collections.Immutable;
using System.IO;
using System.Linq;
using System.Security.Permissions;
using System.Threading.Tasks;
using Microsoft.Quantum.QsCompiler;
using Microsoft.Quantum.QsCompiler.CompilationBuilder;
@ -19,8 +18,8 @@ namespace Microsoft.Quantum.QsLanguageServer
/// <summary>
/// This class provides a basic file watcher for the LSP that sends notifications on watched files
/// when they change on disk (i.e. are added, removed, or edited).
/// The implemeneted mechanism is far from perfect and will fail in some cases -
/// in particular I expect it to fail (silently) for autogenerated file system edits generated in rapid succession,
/// The implemented mechanism is far from perfect and will fail in some cases -
/// in particular I expect it to fail (silently) for auto-generated file system edits generated in rapid succession,
/// especially in combination with large batch edits in the file system.
/// However, all in all splitting out the logic to generate notifications for individual files here (even if it is less than perfect),
/// seems better and overall less error prone than having to deal with less abstraction on the server side.
@ -28,7 +27,7 @@ namespace Microsoft.Quantum.QsLanguageServer
/// </summary>
internal class FileWatcher
{
private readonly ConcurrentBag<System.IO.FileSystemWatcher> watchers;
private readonly ConcurrentBag<FileSystemWatcher> watchers;
private readonly Action<Exception> onException;
private void OnBufferOverflow(object sender, ErrorEventArgs e)
@ -61,7 +60,7 @@ namespace Microsoft.Quantum.QsLanguageServer
public FileWatcher(Action<Exception> onException)
{
this.onException = onException;
this.watchers = new ConcurrentBag<System.IO.FileSystemWatcher>();
this.watchers = new ConcurrentBag<FileSystemWatcher>();
this.watchedDirectories = new Dictionary<Uri, ImmutableHashSet<string>>();
this.processing = new ProcessingQueue(this.onException, "error in file system watcher");
this.globPatterns = new ConcurrentDictionary<Uri, IEnumerable<string>>();
@ -71,9 +70,9 @@ namespace Microsoft.Quantum.QsLanguageServer
/// Returns a file system watcher for the given folder and pattern, with the proper event handlers added.
/// IMPORTANT: The returned watcher is disabled and needs to be enabled by setting EnableRaisingEvents to true.
/// </summary>
private System.IO.FileSystemWatcher GetWatcher(string folder, string pattern, NotifyFilters notifyOn)
private FileSystemWatcher GetWatcher(string folder, string pattern, NotifyFilters notifyOn)
{
var watcher = new System.IO.FileSystemWatcher
var watcher = new FileSystemWatcher
{
NotifyFilter = notifyOn,
Filter = pattern,
@ -206,7 +205,7 @@ namespace Microsoft.Quantum.QsLanguageServer
public void OnCreated(object source, FileSystemEventArgs e)
{
var directories = new Dictionary<Uri, ImmutableHashSet<string>>();
if (source is System.IO.FileSystemWatcher watcher &&
if (source is FileSystemWatcher watcher &&
this.globPatterns.TryGetValue(new Uri(watcher.Path), out var globPatterns))
{
var maxNrTries = 10; // copied directories need some time until they are on disk -> todo: better solution?

Просмотреть файл

@ -761,6 +761,7 @@ namespace Microsoft.Quantum.QsLanguageServer
this.rpc.InvokeWithParameterObjectAsync<ApplyWorkspaceEditResponse>(Methods.WorkspaceApplyEditName, edit)) :
param.Command == CommandIds.FileContentInMemory ? CastAndExecute<TextDocumentIdentifier>(this.editorState.FileContentInMemory) :
param.Command == CommandIds.FileDiagnostics ? CastAndExecute<TextDocumentIdentifier>(this.editorState.FileDiagnostics) :
param.Command == CommandIds.ProjectInformation ? CastAndExecute<TextDocumentIdentifier>(this.editorState.ProjectInformation) :
null;
}
catch

Просмотреть файл

@ -37,12 +37,19 @@ namespace Microsoft.Quantum.QsLanguageServer
HelpText = "Port to use for TCP/IP connections.")]
public int Port { get; set; }
[Option(
"unnamed",
Required = false,
SetName = ConnectionViaPipe,
HelpText = "Connect via anonymous pipes.")]
internal bool UseAnonymousPipes { get; set; }
[Option(
'w',
"writer",
Required = true,
SetName = ConnectionViaPipe,
HelpText = "Named pipe to write to.")]
HelpText = "Name of handle of the pipe to write to.")]
public string? WriterPipeName { get; set; }
[Option(
@ -50,7 +57,7 @@ namespace Microsoft.Quantum.QsLanguageServer
"reader",
Required = true,
SetName = ConnectionViaPipe,
HelpText = "Named pipe to read from.")]
HelpText = "Name of handle of the pipe to read from.")]
public string? ReaderPipeName { get; set; }
[Option(
@ -133,7 +140,7 @@ namespace Microsoft.Quantum.QsLanguageServer
server = options.UseStdInOut
? ConnectViaStdInOut(options.LogFile)
: options.ReaderPipeName != null && options.WriterPipeName != null
? ConnectViaNamedPipe(options.WriterPipeName, options.ReaderPipeName, options.LogFile)
? ConnectViaPipes(options.WriterPipeName, options.ReaderPipeName, options.UseAnonymousPipes, options.LogFile)
: ConnectViaSocket(port: options.Port, logFile: options.LogFile);
}
catch (Exception ex)
@ -181,7 +188,12 @@ namespace Microsoft.Quantum.QsLanguageServer
return new QsLanguageServer(Console.OpenStandardOutput(), Console.OpenStandardInput());
}
internal static QsLanguageServer ConnectViaNamedPipe(string writerName, string readerName, string? logFile = null)
internal static QsLanguageServer ConnectViaPipes(string writer, string reader, bool useAnonymousPipes, string? logFile = null) =>
useAnonymousPipes
? ConnectViaAnonymousPipes(writer, reader, logFile)
: ConnectViaNamedPipes(writer, reader, logFile);
internal static QsLanguageServer ConnectViaNamedPipes(string writerName, string readerName, string? logFile = null)
{
Log($"Connecting via named pipe. {Environment.NewLine}ReaderPipe: \"{readerName}\" {Environment.NewLine}WriterPipe: \"{writerName}\"", logFile, stdout: true);
var writerPipe = new NamedPipeClientStream(writerName);
@ -202,6 +214,20 @@ namespace Microsoft.Quantum.QsLanguageServer
return new QsLanguageServer(writerPipe, readerPipe);
}
internal static QsLanguageServer ConnectViaAnonymousPipes(string writerHandle, string readerHandle, string? logFile = null)
{
Log($"Connecting via anonymous pipe.", logFile, stdout: true);
var writerPipe = new AnonymousPipeClientStream(PipeDirection.Out, writerHandle);
var readerPipe = new AnonymousPipeClientStream(PipeDirection.In, readerHandle);
if (!writerPipe.IsConnected || !readerPipe.IsConnected)
{
Log($"[ERROR] Connection failed.", logFile);
}
return new QsLanguageServer(writerPipe, readerPipe);
}
internal static QsLanguageServer ConnectViaSocket(string hostname = "localhost", int port = 8008, string? logFile = null)
{
Log($"Connecting via socket. {Environment.NewLine}Port number: {port}", logFile, stdout: true);

Просмотреть файл

@ -51,7 +51,7 @@ namespace Microsoft.Quantum.QsLanguageServer
}
private static readonly Regex TargetFrameworkMoniker =
new Regex(@"(netstandard[1-9]\.[0-9])|(netcoreapp[1-9]\.[0-9])|(net[1-9][0-9][0-9]?)|(net[1-9]\.[0-9])");
new(@"(netstandard[1-9]\.[0-9])|(netcoreapp[1-9]\.[0-9])|(net[1-9][0-9][0-9]?)|(net[1-9]\.[0-9])");
private readonly ImmutableArray<string> supportedQsFrameworks =
ImmutableArray.Create("netstandard2.", "netcoreapp2.", "netcoreapp3.", "net6.");

Просмотреть файл

@ -2,7 +2,7 @@
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>netcoreapp3.1</TargetFramework>
<TargetFramework>net6.0</TargetFramework>
<ExecutionTarget>ionq.qpu</ExecutionTarget>
</PropertyGroup>

Просмотреть файл

@ -6,9 +6,9 @@ using Microsoft.Build.Locator;
namespace Microsoft.Quantum.QsLanguageServer.Testing
{
internal static class VisualStudioInstanceWrapper
internal static class MsBuildDefaults
{
public static Lazy<VisualStudioInstance> LazyVisualStudioInstance { get; }
public static Lazy<VisualStudioInstance> LazyRegistration { get; }
= new Lazy<VisualStudioInstance>(MSBuildLocator.RegisterDefaults);
}
}

Просмотреть файл

@ -2,8 +2,11 @@
// Licensed under the MIT License.
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Threading.Tasks;
using Microsoft.Quantum.QsCompiler;
using Microsoft.Quantum.QsCompiler.CompilationBuilder;
using Microsoft.Quantum.QsCompiler.ReservedKeywords;
@ -12,25 +15,73 @@ using Microsoft.VisualStudio.TestTools.UnitTesting;
namespace Microsoft.Quantum.QsLanguageServer.Testing
{
[TestClass]
public class ProjectLoaderTests
public partial class BasicFunctionality
{
private static string ProjectFileName(string project) =>
Path.Combine("TestProjects", project, $"{project}.csproj");
internal static Uri ProjectUri(string project) =>
new Uri(Path.GetFullPath(ProjectFileName(project)));
new(Path.GetFullPath(ProjectFileName(project)));
internal static (Uri, ProjectInformation?) Context(string project)
internal async Task<(Uri, ProjectInformation?)> GetProjectInformationAsync(string project)
{
var uri = ProjectUri(project);
return (uri, CompilationContext.Load(uri));
}
var projDir = Path.GetDirectoryName(uri.AbsolutePath) ?? "";
[TestInitialize]
public void RunMSBuildLocator()
{
_ = VisualStudioInstanceWrapper.LazyVisualStudioInstance.Value;
var initParams = TestUtils.GetInitializeParams();
initParams.RootUri = new Uri(projDir);
await this.rpc.NotifyWithParameterObjectAsync(Methods.Initialize.Name, initParams);
var projectInfo = await this.GetProjectInformationAsync(uri);
if (projectInfo is null)
{
return (uri, null);
}
var stringReader = new StringReader(projectInfo);
var reader = System.Xml.XmlReader.Create(stringReader);
void ReadElementGroup(string groupName, out List<string> paths)
{
paths = new List<string>();
if (!reader.IsStartElement(groupName))
{
reader.ReadToNextSibling(groupName);
}
reader.ReadStartElement(groupName);
while (reader.IsStartElement("File"))
{
paths.Add(reader.GetAttribute("Path") ?? "");
reader.ReadToNextSibling("File");
}
}
var outputPath = reader.IsStartElement("ProjectInfo")
? reader.GetAttribute("OutputPath")
: null;
var targetCapability = reader.GetAttribute("TargetCapability");
var processorArch = reader.GetAttribute("ProcessorArchitecture");
reader.ReadStartElement("ProjectInfo");
ReadElementGroup("Sources", out var sources);
ReadElementGroup("ProjectReferences", out var projectRefs);
ReadElementGroup("References", out var references);
reader.ReadEndElement();
var projectProperties = new Dictionary<string, string?>
{
{ MSBuildProperties.TargetPath, outputPath },
{ MSBuildProperties.ResolvedTargetCapability, targetCapability },
{ MSBuildProperties.ResolvedProcessorArchitecture, processorArch },
};
var infos = new ProjectInformation(
sourceFiles: sources,
projectReferences: projectRefs,
references: references,
projectProperties);
return (uri, infos);
}
[TestMethod]
@ -99,7 +150,7 @@ namespace Microsoft.Quantum.QsLanguageServer.Testing
}
[TestMethod]
public void LoadNonQSharpProjects()
public async Task LoadNonQSharpProjectsAsync()
{
var invalidProjects = new string[]
{
@ -110,16 +161,16 @@ namespace Microsoft.Quantum.QsLanguageServer.Testing
foreach (var project in invalidProjects)
{
var (_, context) = Context(project);
var (_, context) = await this.GetProjectInformationAsync(project);
Assert.IsNull(context);
}
}
[TestMethod]
public void LoadOutdatedQSharpProject()
public async Task LoadUnsupportedQSharpProjectAsync()
{
var (projectFile, context) = Context("test9");
var projDir = Path.GetDirectoryName(projectFile.LocalPath) ?? "";
var (projectFile, context) = await this.GetProjectInformationAsync("test9");
var projDir = Path.GetDirectoryName(projectFile.AbsolutePath) ?? "";
Assert.IsNotNull(context);
Assert.AreEqual("test9.dll", Path.GetFileName(context!.Properties.DllOutputPath));
Assert.IsTrue((Path.GetDirectoryName(context.Properties.DllOutputPath) ?? "").StartsWith(projDir));
@ -132,14 +183,16 @@ namespace Microsoft.Quantum.QsLanguageServer.Testing
Assert.IsTrue(context.UsesIntrinsics());
Assert.IsTrue(context.UsesCanon());
Assert.IsFalse(context.UsesXunitHelper());
CollectionAssert.AreEquivalent(qsFiles, context.SourceFiles.ToArray());
var expected = qsFiles.Select(Path.GetFullPath).Select(p => new Uri(p).AbsolutePath).ToArray();
CollectionAssert.AreEquivalent(expected, context.SourceFiles.ToArray());
}
[TestMethod]
public void LoadQSharpCoreLibraries()
public async Task LoadUnsupportedQSharpCoreLibrariesAsync()
{
var (projectFile, context) = Context("test3");
var projDir = Path.GetDirectoryName(projectFile.LocalPath) ?? "";
var (projectFile, context) = await this.GetProjectInformationAsync("test3");
var projDir = Path.GetDirectoryName(projectFile.AbsolutePath) ?? "";
Assert.IsNotNull(context);
Assert.AreEqual("test3.dll", Path.GetFileName(context!.Properties.DllOutputPath));
Assert.IsTrue((Path.GetDirectoryName(context.Properties.DllOutputPath) ?? "").StartsWith(projDir));
@ -155,15 +208,21 @@ namespace Microsoft.Quantum.QsLanguageServer.Testing
Assert.IsTrue(context.UsesIntrinsics());
Assert.IsTrue(context.UsesCanon());
Assert.IsFalse(context.UsesXunitHelper());
CollectionAssert.AreEquivalent(qsFiles, context.SourceFiles.ToArray());
(projectFile, context) = Context("test12");
projDir = Path.GetDirectoryName(projectFile.LocalPath) ?? "";
var expected = qsFiles.Select(Path.GetFullPath).Select(p => new Uri(p).AbsolutePath).ToArray();
CollectionAssert.AreEquivalent(expected, context.SourceFiles.ToArray());
}
[TestMethod]
public async Task LoadQSharpCoreLibrariesAsync()
{
var (projectFile, context) = await this.GetProjectInformationAsync("test12");
var projDir = Path.GetDirectoryName(projectFile.AbsolutePath) ?? "";
Assert.IsNotNull(context);
Assert.AreEqual("test12.dll", Path.GetFileName(context!.Properties.DllOutputPath));
Assert.IsTrue((Path.GetDirectoryName(context.Properties.DllOutputPath) ?? "").StartsWith(projDir));
qsFiles = new string[]
var qsFiles = new string[]
{
Path.Combine(projDir, "format", "Unformatted.qs"),
Path.Combine(projDir, "Operation12a.qs"),
@ -176,14 +235,16 @@ namespace Microsoft.Quantum.QsLanguageServer.Testing
Assert.IsFalse(context.UsesIntrinsics());
Assert.IsTrue(context.UsesCanon());
Assert.IsFalse(context.UsesXunitHelper());
CollectionAssert.AreEquivalent(qsFiles, context.SourceFiles.ToArray());
var expected = qsFiles.Select(Path.GetFullPath).Select(p => new Uri(p).AbsolutePath).ToArray();
CollectionAssert.AreEquivalent(expected, context.SourceFiles.ToArray());
}
[TestMethod]
public void LoadQSharpFrameworkLibrary()
public async Task LoadQSharpFrameworkLibraryAsync()
{
var (projectFile, context) = Context("test7");
var projDir = Path.GetDirectoryName(projectFile.LocalPath) ?? "";
var (projectFile, context) = await this.GetProjectInformationAsync("test7");
var projDir = Path.GetDirectoryName(projectFile.AbsolutePath) ?? "";
Assert.IsNotNull(context);
Assert.AreEqual("test7.dll", Path.GetFileName(context!.Properties.DllOutputPath));
Assert.IsTrue((Path.GetDirectoryName(context.Properties.DllOutputPath) ?? "").StartsWith(projDir));
@ -196,14 +257,16 @@ namespace Microsoft.Quantum.QsLanguageServer.Testing
Assert.IsTrue(context.UsesIntrinsics());
Assert.IsTrue(context.UsesCanon());
Assert.IsFalse(context.UsesXunitHelper());
CollectionAssert.AreEquivalent(qsFiles, context.SourceFiles.ToArray());
var expected = qsFiles.Select(Path.GetFullPath).Select(p => new Uri(p).AbsolutePath).ToArray();
CollectionAssert.AreEquivalent(expected, context.SourceFiles.ToArray());
}
[TestMethod]
public void LoadQSharpConsoleApps()
public async Task LoadUnsupportedQSharpConsoleAppAsync()
{
var (projectFile, context) = Context("test4");
var projDir = Path.GetDirectoryName(projectFile.LocalPath) ?? "";
var (projectFile, context) = await this.GetProjectInformationAsync("test4");
var projDir = Path.GetDirectoryName(projectFile.AbsolutePath) ?? "";
Assert.IsNotNull(context);
Assert.AreEqual("test4.dll", Path.GetFileName(context!.Properties.DllOutputPath));
Assert.IsTrue((Path.GetDirectoryName(context.Properties.DllOutputPath) ?? "").StartsWith(projDir));
@ -217,44 +280,58 @@ namespace Microsoft.Quantum.QsLanguageServer.Testing
Assert.IsTrue(context.UsesCanon());
Assert.IsFalse(context.UsesXunitHelper());
Assert.IsTrue(context.UsesProject("test3.csproj"));
CollectionAssert.AreEquivalent(qsFiles, context.SourceFiles.ToArray());
(projectFile, context) = Context("test10");
projDir = Path.GetDirectoryName(projectFile.LocalPath) ?? "";
var expected = qsFiles.Select(Path.GetFullPath).Select(p => new Uri(p).AbsolutePath).ToArray();
CollectionAssert.AreEquivalent(expected, context.SourceFiles.ToArray());
}
[TestMethod]
public async Task LoadOutdatedQSharpConsoleAppAsync()
{
var (projectFile, context) = await this.GetProjectInformationAsync("test10");
var projDir = Path.GetDirectoryName(projectFile.AbsolutePath) ?? "";
Assert.IsNotNull(context);
Assert.AreEqual("test10.dll", Path.GetFileName(context!.Properties.DllOutputPath));
Assert.IsTrue((Path.GetDirectoryName(context.Properties.DllOutputPath) ?? "").StartsWith(projDir));
qsFiles = new string[]
var qsFiles = new string[]
{
Path.Combine(projDir, "Operation10.qs"),
};
Assert.IsTrue(context.UsesIntrinsics());
Assert.IsTrue(context.UsesCanon());
CollectionAssert.AreEquivalent(qsFiles, context.SourceFiles.ToArray());
(projectFile, context) = Context("test11");
projDir = Path.GetDirectoryName(projectFile.LocalPath) ?? "";
var expected = qsFiles.Select(Path.GetFullPath).Select(p => new Uri(p).AbsolutePath).ToArray();
CollectionAssert.AreEquivalent(expected, context.SourceFiles.ToArray());
}
[TestMethod]
public async Task LoadQSharpConsoleAppAsync()
{
var (projectFile, context) = await this.GetProjectInformationAsync("test11");
var projDir = Path.GetDirectoryName(projectFile.AbsolutePath) ?? "";
Assert.IsNotNull(context);
Assert.AreEqual("test11.dll", Path.GetFileName(context!.Properties.DllOutputPath));
Assert.IsTrue((Path.GetDirectoryName(context.Properties.DllOutputPath) ?? "").StartsWith(projDir));
qsFiles = new string[]
var qsFiles = new string[]
{
Path.Combine(projDir, "Operation11.qs"),
};
Assert.IsTrue(context.UsesIntrinsics());
Assert.IsTrue(context.UsesCanon());
CollectionAssert.AreEquivalent(qsFiles, context.SourceFiles.ToArray());
var expected = qsFiles.Select(Path.GetFullPath).Select(p => new Uri(p).AbsolutePath).ToArray();
CollectionAssert.AreEquivalent(expected, context.SourceFiles.ToArray());
}
[TestMethod]
public void LoadTargetedQSharpExecutable()
public async Task LoadTargetedQSharpExecutableAsync()
{
var (projectFile, context) = Context("test17");
var projDir = Path.GetDirectoryName(projectFile.LocalPath) ?? "";
var (projectFile, context) = await this.GetProjectInformationAsync("test17");
var projDir = Path.GetDirectoryName(projectFile.AbsolutePath) ?? "";
Assert.IsNotNull(context);
Assert.AreEqual("test17.dll", Path.GetFileName(context!.Properties.DllOutputPath));
Assert.IsTrue((Path.GetDirectoryName(context.Properties.DllOutputPath) ?? "").StartsWith(projDir));
@ -272,14 +349,16 @@ namespace Microsoft.Quantum.QsLanguageServer.Testing
Assert.IsFalse(context.UsesDll("Microsoft.Quantum.Type3.Core.dll"));
Assert.IsTrue(context.UsesCanon());
Assert.IsFalse(context.UsesXunitHelper());
CollectionAssert.AreEquivalent(qsFiles, context.SourceFiles.ToArray());
var expected = qsFiles.Select(Path.GetFullPath).Select(p => new Uri(p).AbsolutePath).ToArray();
CollectionAssert.AreEquivalent(expected, context.SourceFiles.ToArray());
}
[TestMethod]
public void LoadQSharpUnitTest()
public async Task LoadQSharpUnitTestAsync()
{
var (projectFile, context) = Context("test5");
var projDir = Path.GetDirectoryName(projectFile.LocalPath) ?? "";
var (projectFile, context) = await this.GetProjectInformationAsync("test5");
var projDir = Path.GetDirectoryName(projectFile.AbsolutePath) ?? "";
Assert.IsNotNull(context);
Assert.AreEqual("test5.dll", Path.GetFileName(context!.Properties.DllOutputPath));
Assert.IsTrue((Path.GetDirectoryName(context.Properties.DllOutputPath) ?? "").StartsWith(projDir));
@ -296,14 +375,16 @@ namespace Microsoft.Quantum.QsLanguageServer.Testing
Assert.IsTrue(context.UsesXunitHelper());
Assert.IsTrue(context.UsesProject("test3.csproj"));
Assert.IsTrue(context.UsesProject("test4.csproj"));
CollectionAssert.AreEquivalent(qsFiles, context.SourceFiles.ToArray());
var expected = qsFiles.Select(Path.GetFullPath).Select(p => new Uri(p).AbsolutePath).ToArray();
CollectionAssert.AreEquivalent(expected, context.SourceFiles.ToArray());
}
[TestMethod]
public void LoadQSharpMultiFrameworkLibrary()
public async Task LoadQSharpMultiFrameworkLibraryAsync()
{
var (projectFile, context) = Context("test6");
var projDir = Path.GetDirectoryName(projectFile.LocalPath) ?? "";
var (projectFile, context) = await this.GetProjectInformationAsync("test6");
var projDir = Path.GetDirectoryName(projectFile.AbsolutePath) ?? "";
Assert.IsNotNull(context);
Assert.AreEqual("test6.dll", Path.GetFileName(context!.Properties.DllOutputPath));
Assert.IsTrue((Path.GetDirectoryName(context.Properties.DllOutputPath) ?? "").StartsWith(projDir));
@ -319,7 +400,9 @@ namespace Microsoft.Quantum.QsLanguageServer.Testing
Assert.IsTrue(context.UsesCanon());
Assert.IsFalse(context.UsesXunitHelper());
Assert.IsTrue(context.UsesProject("test3.csproj"));
CollectionAssert.AreEquivalent(qsFiles, context.SourceFiles.ToArray());
var expected = qsFiles.Select(Path.GetFullPath).Select(p => new Uri(p).AbsolutePath).ToArray();
CollectionAssert.AreEquivalent(expected, context.SourceFiles.ToArray());
}
}
@ -329,10 +412,7 @@ namespace Microsoft.Quantum.QsLanguageServer.Testing
Console.WriteLine($"[{level}]: {msg}");
internal static EditorState Editor =>
new EditorState(new ProjectLoader(LogOutput), null, null, null, null);
internal static ProjectInformation? Load(Uri projectFile) =>
Editor.QsProjectLoader(projectFile, out var loaded) ? loaded : null;
new(new ProjectLoader(LogOutput), null, null, null, null);
internal static bool UsesDll(this ProjectInformation info, string dll) => info.References.Any(r => r.EndsWith(dll));

Просмотреть файл

@ -8,7 +8,6 @@ using System.Linq;
using Microsoft.Quantum.QsCompiler.TextProcessing;
using Microsoft.VisualStudio.LanguageServer.Protocol;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Builder = Microsoft.Quantum.QsCompiler.CompilationBuilder.Utils;
namespace Microsoft.Quantum.QsLanguageServer.Testing
{
@ -48,12 +47,12 @@ namespace Microsoft.Quantum.QsLanguageServer.Testing
return current;
}
private static string[] allKeywords = Keywords.ReservedKeywords.ToArray();
private static readonly string[] AllKeywords = Keywords.ReservedKeywords.ToArray();
internal string[] GetRandomLines(int nrLines, bool withLanguageKeywords = true)
{
var lines = new string[nrLines];
string GetKeyword() => $" {allKeywords[this.rnd.Next(0, allKeywords.Length)]} ";
string GetKeyword() => $" {AllKeywords[this.rnd.Next(0, AllKeywords.Length)]} ";
for (var nr = 0; nr < nrLines; ++nr)
{
lines[nr] = this.GetRandomLine();
@ -70,7 +69,7 @@ namespace Microsoft.Quantum.QsLanguageServer.Testing
{
var filename = Path.Combine(TestInputDirectory, Path.GetRandomFileName()) + (withQsExtension ? ".qs" : "z"); // guarantee .qs extension exists or not
var content = this.GetRandomLines(nrLines, withLanguageKeywords);
using (StreamWriter sw = new StreamWriter(filename))
using (StreamWriter sw = new(filename))
{
foreach (var line in content)
{

Просмотреть файл

@ -3,6 +3,7 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.IO.Pipes;
using System.Runtime.InteropServices;
@ -22,9 +23,9 @@ namespace Microsoft.Quantum.QsLanguageServer.Testing
private Connection? connection;
private JsonRpc rpc = null!; // Initialized in SetupServerConnectionAsync.
private readonly RandomInput inputGenerator = new RandomInput();
private readonly Stack<PublishDiagnosticParams> receivedDiagnostics = new Stack<PublishDiagnosticParams>();
private readonly ManualResetEvent projectLoaded = new ManualResetEvent(false);
private readonly RandomInput inputGenerator = new();
private readonly Stack<PublishDiagnosticParams> receivedDiagnostics = new();
private readonly ManualResetEvent projectLoaded = new(false);
public Task<string[]> GetFileContentInMemoryAsync(string filename) =>
this.rpc.InvokeWithParameterObjectAsync<string[]>(
@ -36,6 +37,11 @@ namespace Microsoft.Quantum.QsLanguageServer.Testing
Methods.WorkspaceExecuteCommand.Name,
TestUtils.ServerCommand(CommandIds.FileDiagnostics, filename == null ? new TextDocumentIdentifier { Uri = new Uri("file://unknown") } : TestUtils.GetTextDocumentIdentifier(filename)));
public Task<string> GetProjectInformationAsync(Uri projectFile) =>
this.rpc.InvokeWithParameterObjectAsync<string>(
Methods.WorkspaceExecuteCommand.Name,
TestUtils.ServerCommand(CommandIds.ProjectInformation, new TextDocumentIdentifier() { Uri = projectFile }));
public Task SetupAsync()
{
var initParams = TestUtils.GetInitializeParams();
@ -55,9 +61,10 @@ namespace Microsoft.Quantum.QsLanguageServer.Testing
[TestInitialize]
public async Task SetupServerConnectionAsync()
{
// Need to run MSBuildLocator for some tests like UpdateProjectFileAsync
_ = VisualStudioInstanceWrapper.LazyVisualStudioInstance.Value;
// Need to run MSBuildLocator for some tests.
_ = MsBuildDefaults.LazyRegistration.Value;
var logFile = Path.GetTempFileName();
Directory.CreateDirectory(RandomInput.TestInputDirectory);
var outputDir = new DirectoryInfo(RandomInput.TestInputDirectory);
foreach (var file in outputDir.GetFiles())
@ -73,18 +80,20 @@ namespace Microsoft.Quantum.QsLanguageServer.Testing
var readerPipe = new NamedPipeServerStream(serverWriterPipe, PipeDirection.InOut, 4, PipeTransmissionMode.Message, PipeOptions.Asynchronous, 256, 256);
var writerPipe = new NamedPipeServerStream(serverReaderPipe, PipeDirection.InOut, 4, PipeTransmissionMode.Message, PipeOptions.Asynchronous, 256, 256);
var server = Server.ConnectViaNamedPipe(serverWriterPipe, serverReaderPipe);
Server.ConnectViaNamedPipes(serverWriterPipe, serverReaderPipe);
await readerPipe.WaitForConnectionAsync().ConfigureAwait(true);
await writerPipe.WaitForConnectionAsync().ConfigureAwait(true);
this.connection = new Connection(readerPipe, writerPipe);
}
else
{
var readerPipe = new System.IO.Pipelines.Pipe();
var writerPipe = new System.IO.Pipelines.Pipe();
var readerPipe = new AnonymousPipeServerStream(PipeDirection.In, HandleInheritability.Inheritable);
var writerPipe = new AnonymousPipeServerStream(PipeDirection.Out, HandleInheritability.Inheritable);
var server = new QsLanguageServer(sender: writerPipe.Writer.AsStream(), reader: readerPipe.Reader.AsStream());
this.connection = new Connection(writerPipe.Reader.AsStream(), readerPipe.Writer.AsStream());
Server.ConnectViaAnonymousPipes(readerPipe.GetClientHandleAsString(), writerPipe.GetClientHandleAsString());
this.connection = new Connection(readerPipe, writerPipe);
}
this.rpc = new JsonRpc(this.connection.Writer, this.connection.Reader, this)

Просмотреть файл

@ -17,10 +17,8 @@ namespace Microsoft.Quantum.QsLanguageServer.Testing
{
internal static class TestUtils
{
internal static Uri GetUri(string filename)
{
return new Uri(Path.GetFullPath(filename));
}
internal static Uri GetUri(string filename) =>
new(Path.GetFullPath(filename));
internal static List<string> GetContent(string filename)
{
@ -38,14 +36,11 @@ namespace Microsoft.Quantum.QsLanguageServer.Testing
return content;
}
internal static TextDocumentIdentifier GetTextDocumentIdentifier(string filename)
{
return new TextDocumentIdentifier { Uri = GetUri(filename) };
}
internal static TextDocumentIdentifier GetTextDocumentIdentifier(string filename) =>
new() { Uri = GetUri(filename) };
internal static InitializeParams GetInitializeParams()
{
return new InitializeParams
internal static InitializeParams GetInitializeParams() =>
new()
{
ProcessId = -1,
InitializationOptions = null,
@ -56,7 +51,6 @@ namespace Microsoft.Quantum.QsLanguageServer.Testing
Experimental = new object(),
},
};
}
internal static DidOpenTextDocumentParams GetOpenFileParams(string filename)
{
@ -90,7 +84,7 @@ namespace Microsoft.Quantum.QsLanguageServer.Testing
}
internal static ExecuteCommandParams ServerCommand(string command, params object[] args) =>
new ExecuteCommandParams { Command = command, Arguments = args };
new() { Command = command, Arguments = args };
// does not modify range
internal static int GetRangeLength(Range range, IReadOnlyList<string> content)
@ -118,14 +112,14 @@ namespace Microsoft.Quantum.QsLanguageServer.Testing
}
Assert.IsTrue(IsValidRange(change.Range) && change.Text != null);
Assert.IsTrue(change.Range.End.Line < content.Count());
Assert.IsTrue(change.Range.End.Line < content.Count);
Assert.IsTrue(change.Range.Start.Character <= content[change.Range.Start.Line].Length);
Assert.IsTrue(change.Range.End.Character <= content[change.Range.End.Line].Length);
var (startLine, startChar) = (change.Range.Start.Line, change.Range.Start.Character);
var (endLine, endChar) = (change.Range.End.Line, change.Range.End.Character);
var newText = string.Concat(content[startLine].Substring(0, startChar), change.Text, content[endLine].Substring(endChar));
var newText = string.Concat(content[startLine][..startChar], change.Text, content[endLine][endChar..]);
if (startLine > 0)
{
newText = content[--startLine] + newText;
@ -133,11 +127,11 @@ namespace Microsoft.Quantum.QsLanguageServer.Testing
if (endLine + 1 < content.Count)
{
newText = newText + content[++endLine];
newText += content[++endLine];
}
var lineChanges = Builder.SplitLines(newText);
if (lineChanges.Length == 0 || (endLine + 1 == content.Count() && Builder.EndOfLine.Match(lineChanges.Last()).Success))
if (lineChanges.Length == 0 || (endLine + 1 == content.Count && Builder.EndOfLine.Match(lineChanges.Last()).Success))
{
lineChanges = lineChanges.Concat(new string[] { string.Empty }).ToArray();
}
@ -153,8 +147,8 @@ namespace Microsoft.Quantum.QsLanguageServer.Testing
static bool IsBeforeOrEqual(Position a, Position b) =>
a.Line < b.Line || (a.Line == b.Line && a.Character <= b.Character);
return !(range?.Start is null) &&
!(range.End is null) &&
return range?.Start is not null &&
range.End is not null &&
IsValidPosition(range.Start) &&
IsValidPosition(range.End) &&
IsBeforeOrEqual(range.Start, range.End);
@ -207,7 +201,7 @@ namespace Microsoft.Quantum.QsLanguageServer.Testing
/// <returns>The project manager for the fully loaded project.</returns>
internal static async Task<ProjectManager> LoadProjectAsync(this Uri projectFile, SendTelemetryHandler? telemetryHandler = null)
{
ManualResetEvent eventSignal = new ManualResetEvent(false);
ManualResetEvent eventSignal = new(false);
void CheckForLoadingCompleted(string msg, MessageType messageType)
{
Console.WriteLine($"[{messageType}]: {msg}");

Просмотреть файл

@ -14,13 +14,13 @@
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.11.0" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.2.0" />
<PackageReference Include="Microsoft.VisualStudio.LanguageServer.Client" Version="17.2.2105" />
<PackageReference Include="MSTest.TestAdapter" Version="2.2.10" />
<PackageReference Include="MSTest.TestFramework" Version="2.2.10" />
<PackageReference Include="System.IO.Pipelines" Version="6.0.1" />
<PackageReference Include="System.IO.Pipes" Version="4.3.0" />
<PackageReference Include="NuGet.Frameworks" Version="6.2.1-rc.7" />
<PackageReference Include="NuGet.Frameworks" Version="6.2.2" />
</ItemGroup>
<ItemGroup>
@ -32,21 +32,21 @@
<ItemGroup>
<Content Include="..\TestProjects\**\*proj">
<Link>TestProjects\%(RecursiveDir)%(Filename)%(Extension)</Link>
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</Content>
<Content Include="..\TestProjects\**\*.qs">
<Link>TestProjects\%(RecursiveDir)%(Filename)%(Extension)</Link>
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</Content>
</ItemGroup>
<Target Name="PublishQsFmt" BeforeTargets="BeforeCompile">
<Target Name="PublishTools" BeforeTargets="BeforeCompile">
<Message Importance="high" Text="Publishing QsFmt"/>
<PropertyGroup>
<QsFmtPublishPath>$(OutputPath)\qsfmt\</QsFmtPublishPath>
<QsFmtPublishCommand>dotnet publish "$(EnlistmentRoot)src/QsFmt/App/App.fsproj" -o "$(QsFmtPublishPath)"</QsFmtPublishCommand>
</PropertyGroup>
<MakeDir Directories="$(OutputPath)\qsfmt" />
<MakeDir Directories="$(QsFmtPublishPath)" />
<Exec Command="$(QsFmtPublishCommand)" IgnoreExitCode="false" ContinueOnError="ErrorAndContinue">
<Output TaskParameter="ExitCode" PropertyName="QsFmtPublishExitCode" />
</Exec>

Просмотреть файл

@ -32,8 +32,8 @@ namespace Microsoft.Quantum.QsLanguageServer.Testing
public async Task ShutdownAsync()
{
// the shutdown request should *not* result in the server exiting, and calling multiple times is fine
_ = await this.rpc.InvokeAsync<object>(Methods.Shutdown.Name);
var response = await this.rpc.InvokeAsync<object>(Methods.Shutdown.Name);
response = await this.rpc.InvokeAsync<object>(Methods.Shutdown.Name);
Assert.IsNull(response);
Assert.IsNotNull(this.rpc);
}
@ -132,7 +132,7 @@ namespace Microsoft.Quantum.QsLanguageServer.Testing
await this.rpc.InvokeWithParameterObjectAsync<Task>(Methods.TextDocumentDidClose.Name, TestUtils.GetCloseFileParams(filename));
// verify that a file can be closed immediately after it was opened
var openTask = this.rpc.InvokeWithParameterObjectAsync<Task>(Methods.TextDocumentDidOpen.Name, TestUtils.GetOpenFileParams(filename));
_ = this.rpc.InvokeWithParameterObjectAsync<Task>(Methods.TextDocumentDidOpen.Name, TestUtils.GetOpenFileParams(filename));
await this.rpc.InvokeWithParameterObjectAsync<Task>(Methods.TextDocumentDidClose.Name, TestUtils.GetCloseFileParams(filename));
}
@ -145,7 +145,7 @@ namespace Microsoft.Quantum.QsLanguageServer.Testing
await this.SetupAsync();
// verify that safe notification can be sent immediately after sending the open notification (even if the latter has not yet finished processing)
var openFileTask = this.rpc.InvokeWithParameterObjectAsync<Task>(Methods.TextDocumentDidOpen.Name, TestUtils.GetOpenFileParams(filename));
_ = this.rpc.InvokeWithParameterObjectAsync<Task>(Methods.TextDocumentDidOpen.Name, TestUtils.GetOpenFileParams(filename));
await this.rpc.InvokeWithParameterObjectAsync<Task>(Methods.TextDocumentDidSave.Name, TestUtils.GetSaveFileParams(filename, content));
// check that the file content is indeed updated on save, according to the passed parameter
@ -169,13 +169,13 @@ namespace Microsoft.Quantum.QsLanguageServer.Testing
// and the file can be closed and no diagnostics are left even if some changes are still queued for processing
var edits = this.inputGenerator.MakeRandomEdits(50, ref content, fileSize, false);
Task[] processing = new Task[edits.Length];
var openFileTask = this.rpc.InvokeWithParameterObjectAsync<Task>(Methods.TextDocumentDidOpen.Name, TestUtils.GetOpenFileParams(filename));
_ = this.rpc.InvokeWithParameterObjectAsync<Task>(Methods.TextDocumentDidOpen.Name, TestUtils.GetOpenFileParams(filename));
for (var i = 0; i < edits.Length; ++i)
{
processing[i] = this.rpc.InvokeWithParameterObjectAsync<Task>(Methods.TextDocumentDidChange.Name, TestUtils.GetChangedFileParams(filename, new[] { edits[i] }));
}
var closeFileTask = this.rpc.InvokeWithParameterObjectAsync<Task>(Methods.TextDocumentDidClose.Name, TestUtils.GetCloseFileParams(filename));
_ = this.rpc.InvokeWithParameterObjectAsync<Task>(Methods.TextDocumentDidClose.Name, TestUtils.GetCloseFileParams(filename));
var finalDiagnostics = await this.GetFileDiagnosticsAsync(filename);
// check that the file is no longer present in the default manager after closing (final diagnostics are null), and
@ -210,14 +210,14 @@ namespace Microsoft.Quantum.QsLanguageServer.Testing
var expected = Builder.JoinLines(expectedContent.ToArray());
var got = Builder.JoinLines(trackedContent);
Assert.IsNotNull(trackedContent);
Assert.AreEqual(expectedContent.Count(), trackedContent.Count(), $"expected: \n{expected} \ngot: \n{got}");
Assert.AreEqual(expectedContent.Count, trackedContent.Length, $"expected: \n{expected} \ngot: \n{got}");
Assert.AreEqual(expected, got);
// check whether a single array of changes is processed correctly
var edits = this.inputGenerator.MakeRandomEdits(50, ref expectedContent, fileSize, false);
await this.rpc.InvokeWithParameterObjectAsync<Task>(Methods.TextDocumentDidChange.Name, TestUtils.GetChangedFileParams(filename, edits));
trackedContent = await this.GetFileContentInMemoryAsync(filename);
Assert.AreEqual(expectedContent.Count(), trackedContent.Count());
Assert.AreEqual(expectedContent.Count, trackedContent.Length);
Assert.AreEqual(Builder.JoinLines(expectedContent.ToArray()), Builder.JoinLines(trackedContent));
// check if changes are also processed correctly if many changes (array of length one) are given in rapid succession
@ -240,7 +240,7 @@ namespace Microsoft.Quantum.QsLanguageServer.Testing
expected = Builder.JoinLines(expectedContent.ToArray());
got = Builder.JoinLines(trackedContent);
Assert.AreEqual(expectedContent.Count(), trackedContent.Count(), $"expected: \n{expected} \ngot: \n{got}");
Assert.AreEqual(expectedContent.Count, trackedContent.Length, $"expected: \n{expected} \ngot: \n{got}");
Assert.AreEqual(expected, got);
}
}
@ -281,7 +281,7 @@ namespace Microsoft.Quantum.QsLanguageServer.Testing
var expected = Builder.JoinLines(expectedContent.ToArray());
var got = Builder.JoinLines(trackedContent);
Assert.IsNotNull(trackedContent);
Assert.AreEqual(expectedContent.Count(), trackedContent.Count(), $"expected: \n{expected} \ngot: \n{got}");
Assert.AreEqual(expectedContent.Count, trackedContent.Length, $"expected: \n{expected} \ngot: \n{got}");
Assert.AreEqual(expected, got);
}
}
@ -289,7 +289,7 @@ namespace Microsoft.Quantum.QsLanguageServer.Testing
[TestMethod]
public async Task TargetPackageIntrinsicsAsync()
{
var projectFile = ProjectLoaderTests.ProjectUri("test17");
var projectFile = ProjectUri("test17");
var projDir = Path.GetDirectoryName(projectFile.AbsolutePath) ?? "";
var programFile = Path.Combine(projDir, "MeasureBell.qs");
@ -311,7 +311,7 @@ namespace Microsoft.Quantum.QsLanguageServer.Testing
[TestMethod]
public async Task UpdateProjectFileAsync()
{
var projectFile = ProjectLoaderTests.ProjectUri("test14");
var projectFile = ProjectUri("test14");
var projDir = Path.GetDirectoryName(projectFile.AbsolutePath) ?? "";
var programFile = Path.Combine(projDir, "Teleport.qs");
var projectFileContent = XDocument.Load(projectFile.AbsolutePath);
@ -363,7 +363,7 @@ namespace Microsoft.Quantum.QsLanguageServer.Testing
{
async Task RunFormattingTestAsync(string projectName)
{
var projectFile = ProjectLoaderTests.ProjectUri(projectName);
var projectFile = ProjectUri(projectName);
var projDir = Path.GetDirectoryName(projectFile.LocalPath) ?? "";
var telemetryEvents = new List<(string, int)>();
var projectManager = await projectFile.LoadProjectAsync(
@ -381,7 +381,7 @@ namespace Microsoft.Quantum.QsLanguageServer.Testing
Assert.IsNotNull(edits);
Assert.AreEqual(1, edits!.Length);
Assert.AreEqual(expectedContent, edits[0].NewText);
Assert.AreEqual(1, telemetryEvents.Count());
Assert.AreEqual(1, telemetryEvents.Count);
Assert.AreEqual("formatting", telemetryEvents[0].Item1);
Assert.AreEqual(edits.Length, telemetryEvents[0].Item2);
}
@ -393,7 +393,7 @@ namespace Microsoft.Quantum.QsLanguageServer.Testing
[TestMethod]
public async Task TestLambdaHoverInfoAsync()
{
var projectFile = ProjectLoaderTests.ProjectUri("test16");
var projectFile = ProjectUri("test16");
var projectManager = await LoadProjectFileAsync(projectFile);
var lambdaFile = new Uri(projectFile, "Lambda.qs");
@ -463,7 +463,7 @@ namespace Microsoft.Quantum.QsLanguageServer.Testing
[TestMethod]
public async Task TestLambdaReferencesAsync()
{
var projectFile = ProjectLoaderTests.ProjectUri("test16");
var projectFile = ProjectUri("test16");
var projectManager = await LoadProjectFileAsync(projectFile);
var lambdaFile = new Uri(projectFile, "Lambda.qs");

Просмотреть файл

@ -43,6 +43,6 @@
<PackageReference Include="Microsoft.Build.Locator" Version="1.4.1" />
<PackageReference Include="Microsoft.Build.Utilities.Core" Version="16.9.0" ExcludeAssets="runtime" />
<PackageReference Include="NuGet.ProjectModel" Version="6.2.1" />
<PackageReference Include="NuGet.Frameworks" Version="6.2.1" />
<PackageReference Include="NuGet.Frameworks" Version="6.2.2" />
</ItemGroup>
</Project>

Просмотреть файл

@ -6,9 +6,9 @@
</PropertyGroup>
<ItemGroup>
<Content Include="$(SolutionDir)src\QsCompiler\Tests.Compiler\TestCases\**\*.qs">
<Content Include="$(MSBuildThisFileDirectory)..\..\QsCompiler\Tests.Compiler\TestCases\**\*.qs">
<Link>TestCases\%(FileName)%(Extension)</Link>
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</Content>
<Compile Include="Tests.fs" />
</ItemGroup>

Просмотреть файл

@ -32,7 +32,7 @@ namespace Microsoft.Quantum.Telemetry.Commands
{
var isPii = this.Args.PiiProperties.ContainsKey(property.Key);
if (!logEventCommand.Args.Properties.TryGetValue(property.Key, out var value)
|| !object.Equals(value, property.Value)
|| !Equals(value, property.Value)
|| (isPii && !logEventCommand.Args.PiiProperties.ContainsKey(property.Key)))
{
return false;

Просмотреть файл

@ -17,7 +17,7 @@ namespace Microsoft.Quantum.Telemetry.Commands
public override bool Equals(object? obj) =>
(obj is SetContextCommand setContextCommand)
&& object.Equals(this.Args, setContextCommand.Args);
&& Equals(this.Args, setContextCommand.Args);
public override int GetHashCode() =>
this.CommandType.GetHashCode() ^ this.Args.GetHashCode();
@ -52,10 +52,10 @@ namespace Microsoft.Quantum.Telemetry.Commands
{
if (obj is SetContextArgs setContextArgs)
{
return object.Equals(setContextArgs.IsPii, this.IsPii)
&& object.Equals(setContextArgs.Name, this.Name)
&& object.Equals(setContextArgs.PropertyType, this.PropertyType)
&& object.Equals(setContextArgs.Value, this.Value);
return Equals(setContextArgs.IsPii, this.IsPii)
&& Equals(setContextArgs.Name, this.Name)
&& Equals(setContextArgs.PropertyType, this.PropertyType)
&& Equals(setContextArgs.Value, this.Value);
}
return false;

Просмотреть файл

@ -20,8 +20,7 @@ namespace Microsoft.Quantum.Telemetry
internal static PiiKind ToPiiKind(this bool isPii) =>
isPii ? PiiKind.GenericData : PiiKind.None;
private static Dictionary<TelemetryPropertyType, Action<ILogger, string, object, PiiKind>> setContextActions =
new Dictionary<TelemetryPropertyType, Action<ILogger, string, object, PiiKind>>()
private static readonly Dictionary<TelemetryPropertyType, Action<ILogger, string, object, PiiKind>> SetContextActions = new()
{
{ TelemetryPropertyType.Boolean, (logger, name, value, piiKind) => logger.SetContext(name, (bool)value, piiKind) },
{ TelemetryPropertyType.DateTime, (logger, name, value, piiKind) => logger.SetContext(name, (DateTime)value, piiKind) },
@ -32,7 +31,7 @@ namespace Microsoft.Quantum.Telemetry
};
internal static void SetContext(this ILogger logger, string name, object value, TelemetryPropertyType propertyType, bool isPii) =>
setContextActions[propertyType](logger, name, value, isPii.ToPiiKind());
SetContextActions[propertyType](logger, name, value, isPii.ToPiiKind());
private static Dictionary<TelemetryPropertyType, Action<EventProperties, string, object, PiiKind>> setPropertyMethods =
new Dictionary<TelemetryPropertyType, Action<EventProperties, string, object, PiiKind>>()
@ -55,7 +54,7 @@ namespace Microsoft.Quantum.Telemetry
{
if (value != null)
{
var convertedValue = TypeConversionHelper.ConvertValue(value, serializeJson);
var convertedValue = ConvertValue(value, serializeJson);
if (convertedValue != null)
{
eventProperties.SetProperty(name, convertedValue.Item2, convertedValue.Item1, isPii.ToPiiKind());