diff --git a/src/Workspaces/MSBuildWorkspaceLoader.cs b/src/Workspaces/MSBuildWorkspaceLoader.cs index 82dcce883f..fa91d1124b 100644 --- a/src/Workspaces/MSBuildWorkspaceLoader.cs +++ b/src/Workspaces/MSBuildWorkspaceLoader.cs @@ -13,6 +13,8 @@ namespace Microsoft.CodeAnalysis.Tools.Workspaces { internal static class MSBuildWorkspaceLoader { + private static readonly SemaphoreSlim s_guard = new SemaphoreSlim(1, 1); + public static async Task LoadAsync( string solutionOrProjectPath, WorkspaceType workspaceType, @@ -29,35 +31,45 @@ internal static class MSBuildWorkspaceLoader { "AlwaysCompileMarkupFilesInSeparateDomain", bool.FalseString }, }; - var workspace = MSBuildWorkspace.Create(properties); + MSBuildWorkspace workspace; - Build.Framework.ILogger? binlog = null; - if (createBinaryLog) + await s_guard.WaitAsync(); + try { - binlog = new Build.Logging.BinaryLogger() + workspace = MSBuildWorkspace.Create(properties); + + Build.Framework.ILogger? binlog = null; + if (createBinaryLog) { - Parameters = Path.Combine(Environment.CurrentDirectory, "formatDiagnosticLog.binlog"), - Verbosity = Build.Framework.LoggerVerbosity.Diagnostic, - }; - } + binlog = new Build.Logging.BinaryLogger() + { + Parameters = Path.Combine(Environment.CurrentDirectory, "formatDiagnosticLog.binlog"), + Verbosity = Build.Framework.LoggerVerbosity.Diagnostic, + }; + } - if (workspaceType == WorkspaceType.Solution) - { - await workspace.OpenSolutionAsync(solutionOrProjectPath, msbuildLogger: binlog, cancellationToken: cancellationToken).ConfigureAwait(false); - } - else - { - try + if (workspaceType == WorkspaceType.Solution) { - await workspace.OpenProjectAsync(solutionOrProjectPath, msbuildLogger: binlog, cancellationToken: cancellationToken).ConfigureAwait(false); + await workspace.OpenSolutionAsync(solutionOrProjectPath, msbuildLogger: binlog, cancellationToken: cancellationToken).ConfigureAwait(false); } - catch (InvalidOperationException) + else { - logger.LogError(Resources.Could_not_format_0_Format_currently_supports_only_CSharp_and_Visual_Basic_projects, solutionOrProjectPath); - workspace.Dispose(); - return null; + try + { + await workspace.OpenProjectAsync(solutionOrProjectPath, msbuildLogger: binlog, cancellationToken: cancellationToken).ConfigureAwait(false); + } + catch (InvalidOperationException) + { + logger.LogError(Resources.Could_not_format_0_Format_currently_supports_only_CSharp_and_Visual_Basic_projects, solutionOrProjectPath); + workspace.Dispose(); + return null; + } } } + finally + { + s_guard.Release(); + } LogWorkspaceDiagnostics(logger, logWorkspaceWarnings, workspace.Diagnostics); diff --git a/tests/Analyzers/CodeStyleAnalyzerFormatterTests.cs b/tests/Analyzers/CodeStyleAnalyzerFormatterTests.cs index 9cd2bdc2a0..1dbf7d6020 100644 --- a/tests/Analyzers/CodeStyleAnalyzerFormatterTests.cs +++ b/tests/Analyzers/CodeStyleAnalyzerFormatterTests.cs @@ -6,6 +6,7 @@ using Microsoft.CodeAnalysis.Tools.Formatters; using Microsoft.CodeAnalysis.Tools.Tests.Formatters; using Xunit; +using Xunit.Abstractions; namespace Microsoft.CodeAnalysis.Tools.Tests.Analyzers { @@ -13,6 +14,11 @@ public class CodeStyleAnalyzerFormatterTests : CSharpFormatterTests { private protected override ICodeFormatter Formatter => AnalyzerFormatter.CodeStyleFormatter; + public CodeStyleAnalyzerFormatterTests(ITestOutputHelper output) + { + TestOutputHelper = output; + } + [Fact] public async Task TestUseVarCodeStyle_AppliesWhenNotUsingVar() { diff --git a/tests/Analyzers/ThirdPartyAnalyzerFormatterTests.cs b/tests/Analyzers/ThirdPartyAnalyzerFormatterTests.cs new file mode 100644 index 0000000000..7893f46d03 --- /dev/null +++ b/tests/Analyzers/ThirdPartyAnalyzerFormatterTests.cs @@ -0,0 +1,122 @@ +// Copyright (c) Microsoft. All Rights Reserved. Licensed under the MIT license. See License.txt in the project root for license information. + +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.CodeAnalysis.Diagnostics; +using Microsoft.CodeAnalysis.Tools.Analyzers; +using Microsoft.CodeAnalysis.Tools.Formatters; +using Microsoft.CodeAnalysis.Tools.Tests.Formatters; +using Microsoft.CodeAnalysis.Tools.Tests.Utilities; +using Microsoft.CodeAnalysis.Tools.Workspaces; +using Xunit; +using Xunit.Abstractions; + +namespace Microsoft.CodeAnalysis.Tools.Tests.Analyzers +{ + public class ThirdPartyAnalyzerFormatterTests : CSharpFormatterTests, IAsyncLifetime + { + private static readonly string s_analyzerProjectFilePath = Path.Combine("for_analyzer_formatter", "analyzer_project", "analyzer_project.csproj"); + + private protected override ICodeFormatter Formatter => AnalyzerFormatter.ThirdPartyFormatter; + + private Project _analyzerReferencesProject; + + public ThirdPartyAnalyzerFormatterTests(ITestOutputHelper output) + { + TestOutputHelper = output; + } + + public async Task InitializeAsync() + { + var logger = new TestLogger(); + + try + { + // Restore the Analyzer packages that have been added to `for_analyzer_formatter/analyzer_project/analyzer_project.csproj` + var exitCode = await NuGetHelper.PerformRestore(s_analyzerProjectFilePath, TestOutputHelper); + Assert.Equal(0, exitCode); + + // Load the analyzer_project into a MSBuildWorkspace. + var workspacePath = Path.Combine(TestProjectsPathHelper.GetProjectsDirectory(), s_analyzerProjectFilePath); + + MSBuildRegistrar.RegisterInstance(logger); + var analyzerWorkspace = await MSBuildWorkspaceLoader.LoadAsync(workspacePath, WorkspaceType.Project, createBinaryLog: false, logWorkspaceWarnings: true, logger, CancellationToken.None); + + // From this project we can get valid AnalyzerReferences to add to our test project. + _analyzerReferencesProject = analyzerWorkspace.CurrentSolution.Projects.Single(); + } + catch + { + TestOutputHelper.WriteLine(logger.GetLog()); + throw; + } + } + + public Task DisposeAsync() + { + _analyzerReferencesProject = null; + + return Task.CompletedTask; + } + + private IEnumerable GetAnalyzerReferences(string prefix) + => _analyzerReferencesProject.AnalyzerReferences.Where(reference => reference.Display.StartsWith(prefix)); + + [Fact] + public async Task TestStyleCopBlankLineFixer_RemovesUnnecessaryBlankLines() + { + var analyzerReferences = GetAnalyzerReferences("StyleCop"); + + var testCode = @" +class C +{ + + void M() + + { + + object obj = new object(); + + + int count = 5; + + } + +} +"; + + var expectedCode = @" +class C +{ + void M() + { + object obj = new object(); + + int count = 5; + } +} +"; + + var editorConfig = new Dictionary() + { + // Turn off all diagnostics analyzers + ["dotnet_analyzer_diagnostic.severity"] = "none", + + // Two or more consecutive blank lines: Remove down to one blank line. SA1507 + ["dotnet_diagnostic.SA1507.severity"] = "error", + + // Blank line immediately before or after a { line: remove it. SA1505, SA1509 + ["dotnet_diagnostic.SA1505.severity"] = "error", + ["dotnet_diagnostic.SA1509.severity"] = "error", + + // Blank line immediately before a } line: remove it. SA1508 + ["dotnet_diagnostic.SA1508.severity"] = "error", + }; + + await AssertCodeChangedAsync(testCode, expectedCode, editorConfig, fixCategory: FixCategory.Analyzers, analyzerReferences: analyzerReferences); + } + } +} diff --git a/tests/CodeFormatterTests.cs b/tests/CodeFormatterTests.cs index d28fd083f6..5964ced988 100644 --- a/tests/CodeFormatterTests.cs +++ b/tests/CodeFormatterTests.cs @@ -412,7 +412,7 @@ await TestFormatWorkspaceAsync( [Fact] public async Task NoFilesFormattedInCodeStyleSolution_WhenNotFixingCodeStyle() { - var restoreExitCode = await PerformNuGetRestore(s_codeStyleSolutionFilePath); + var restoreExitCode = await NuGetHelper.PerformRestore(s_codeStyleSolutionFilePath, _output); Assert.Equal(0, restoreExitCode); await TestFormatWorkspaceAsync( @@ -429,7 +429,7 @@ await TestFormatWorkspaceAsync( [Fact] public async Task NoFilesFormattedInCodeStyleSolution_WhenFixingCodeStyleErrors() { - var restoreExitCode = await PerformNuGetRestore(s_codeStyleSolutionFilePath); + var restoreExitCode = await NuGetHelper.PerformRestore(s_codeStyleSolutionFilePath, _output); Assert.Equal(0, restoreExitCode); await TestFormatWorkspaceAsync( @@ -447,7 +447,7 @@ await TestFormatWorkspaceAsync( [Fact] public async Task FilesFormattedInCodeStyleSolution_WhenFixingCodeStyleWarnings() { - var restoreExitCode = await PerformNuGetRestore(s_codeStyleSolutionFilePath); + var restoreExitCode = await NuGetHelper.PerformRestore(s_codeStyleSolutionFilePath, _output); Assert.Equal(0, restoreExitCode); await TestFormatWorkspaceAsync( @@ -465,7 +465,7 @@ await TestFormatWorkspaceAsync( [Fact] public async Task NoFilesFormattedInAnalyzersSolution_WhenNotFixingAnalyzers() { - var restoreExitCode = await PerformNuGetRestore(s_analyzersSolutionFilePath); + var restoreExitCode = await NuGetHelper.PerformRestore(s_analyzersSolutionFilePath, _output); Assert.Equal(0, restoreExitCode); await TestFormatWorkspaceAsync( @@ -482,7 +482,7 @@ await TestFormatWorkspaceAsync( [Fact] public async Task FilesFormattedInAnalyzersSolution_WhenFixingAnalyzerErrors() { - var restoreExitCode = await PerformNuGetRestore(s_analyzersSolutionFilePath); + var restoreExitCode = await NuGetHelper.PerformRestore(s_analyzersSolutionFilePath, _output); Assert.Equal(0, restoreExitCode); await TestFormatWorkspaceAsync( @@ -497,18 +497,6 @@ await TestFormatWorkspaceAsync( analyzerSeverity: DiagnosticSeverity.Error); } - internal async Task PerformNuGetRestore(string workspaceFilePath) - { - var workspacePath = Path.Combine(TestProjectsPathHelper.GetProjectsDirectory(), workspaceFilePath); - - var processInfo = ProcessRunner.CreateProcess("dotnet", $"restore \"{workspacePath}\"", captureOutput: true, displayWindow: false); - var restoreResult = await processInfo.Result; - - _output.WriteLine(string.Join(Environment.NewLine, restoreResult.OutputLines)); - - return restoreResult.ExitCode; - } - internal async Task TestFormatWorkspaceAsync( string workspaceFilePath, string[] include, @@ -561,11 +549,18 @@ internal async Task TestFormatWorkspaceAsync( Environment.CurrentDirectory = currentDirectory; var log = logger.GetLog(); - _output.WriteLine(log); - Assert.Equal(expectedExitCode, formatResult.ExitCode); - Assert.Equal(expectedFilesFormatted, formatResult.FilesFormatted); - Assert.Equal(expectedFileCount, formatResult.FileCount); + try + { + Assert.Equal(expectedExitCode, formatResult.ExitCode); + Assert.Equal(expectedFilesFormatted, formatResult.FilesFormatted); + Assert.Equal(expectedFileCount, formatResult.FileCount); + } + catch + { + _output.WriteLine(log); + throw; + } return log; } diff --git a/tests/Formatters/AbstractFormatterTests.cs b/tests/Formatters/AbstractFormatterTests.cs index bc5551fa79..49a0f49f82 100644 --- a/tests/Formatters/AbstractFormatterTests.cs +++ b/tests/Formatters/AbstractFormatterTests.cs @@ -8,6 +8,7 @@ using System.Text; using System.Threading; using System.Threading.Tasks; +using Microsoft.CodeAnalysis.Diagnostics; using Microsoft.CodeAnalysis.Host.Mef; using Microsoft.CodeAnalysis.Testing; using Microsoft.CodeAnalysis.Text; @@ -17,6 +18,9 @@ using Microsoft.Extensions.Logging; using Microsoft.VisualStudio.Composition; using Xunit; +using Xunit.Abstractions; + +#nullable enable namespace Microsoft.CodeAnalysis.Tools.Tests.Formatters { @@ -62,6 +66,8 @@ static AbstractFormatterTest() private protected abstract ICodeFormatter Formatter { get; } + protected ITestOutputHelper? TestOutputHelper { get; set; } + protected AbstractFormatterTest() { TestState = new SolutionState(DefaultFilePathPrefix, DefaultFileExt); @@ -72,8 +78,6 @@ protected AbstractFormatterTest() /// public abstract string Language { get; } - private static TestLogger Logger => new TestLogger(); - public SolutionState TestState { get; } private protected string ToEditorConfig(IReadOnlyDictionary editorConfig) => $@"root = true @@ -85,45 +89,48 @@ private protected string ToEditorConfig(IReadOnlyDictionary edit private protected Task AssertCodeUnchangedAsync( string code, IReadOnlyDictionary editorConfig, - Encoding encoding = null, + Encoding? encoding = null, FixCategory fixCategory = FixCategory.Whitespace, + IEnumerable? analyzerReferences = null, DiagnosticSeverity codeStyleSeverity = DiagnosticSeverity.Error, DiagnosticSeverity analyzerSeverity = DiagnosticSeverity.Error) { - return AssertCodeChangedAsync(code, code, ToEditorConfig(editorConfig), encoding, fixCategory, codeStyleSeverity, analyzerSeverity); + return AssertCodeChangedAsync(code, code, ToEditorConfig(editorConfig), encoding, fixCategory, analyzerReferences, codeStyleSeverity, analyzerSeverity); } private protected Task AssertCodeChangedAsync( string testCode, string expectedCode, IReadOnlyDictionary editorConfig, - Encoding encoding = null, + Encoding? encoding = null, FixCategory fixCategory = FixCategory.Whitespace, + IEnumerable? analyzerReferences = null, DiagnosticSeverity codeStyleSeverity = DiagnosticSeverity.Error, DiagnosticSeverity analyzerSeverity = DiagnosticSeverity.Error) { - return AssertCodeChangedAsync(testCode, expectedCode, ToEditorConfig(editorConfig), encoding, fixCategory, codeStyleSeverity, analyzerSeverity); + return AssertCodeChangedAsync(testCode, expectedCode, ToEditorConfig(editorConfig), encoding, fixCategory, analyzerReferences, codeStyleSeverity, analyzerSeverity); } private protected async Task AssertCodeChangedAsync( string testCode, string expectedCode, string editorConfig, - Encoding encoding = null, + Encoding? encoding = null, FixCategory fixCategory = FixCategory.Whitespace, + IEnumerable? analyzerReferences = null, DiagnosticSeverity codeStyleSeverity = DiagnosticSeverity.Error, DiagnosticSeverity analyzerSeverity = DiagnosticSeverity.Error) { var text = SourceText.From(testCode, encoding ?? Encoding.UTF8); TestState.Sources.Add(text); - var solution = await GetSolutionAsync(TestState.Sources.ToArray(), TestState.AdditionalFiles.ToArray(), TestState.AdditionalReferences.ToArray(), editorConfig); + var solution = await GetSolutionAsync(TestState.Sources.ToArray(), TestState.AdditionalFiles.ToArray(), TestState.AdditionalReferences.ToArray(), editorConfig, analyzerReferences); var project = solution.Projects.Single(); var document = project.Documents.Single(); - var fileMatcher = SourceFileMatcher.CreateMatcher(new[] { document.FilePath }, exclude: Array.Empty()); + var fileMatcher = SourceFileMatcher.CreateMatcher(new[] { document.FilePath! }, exclude: Array.Empty()); var formatOptions = new FormatOptions( - workspaceFilePath: project.FilePath, + workspaceFilePath: project.FilePath!, workspaceType: WorkspaceType.Solution, logLevel: LogLevel.Trace, fixCategory, @@ -137,11 +144,21 @@ private protected async Task AssertCodeChangedAsync( var pathsToFormat = GetOnlyFileToFormat(solution); - var formattedSolution = await Formatter.FormatAsync(solution, pathsToFormat, formatOptions, Logger, new List(), default); + var logger = new TestLogger(); + + var formattedSolution = await Formatter.FormatAsync(solution, pathsToFormat, formatOptions, logger, new List(), default); var formattedDocument = GetOnlyDocument(formattedSolution); var formattedText = await formattedDocument.GetTextAsync(); - Assert.Equal(expectedCode, formattedText.ToString()); + try + { + Assert.Equal(expectedCode, formattedText.ToString()); + } + catch + { + TestOutputHelper?.WriteLine(logger.GetLog()); + throw; + } return formattedText; } @@ -178,9 +195,9 @@ private protected async Task AssertCodeChangedAsync( /// Additional metadata references to include in the project. /// The .editorconfig to apply to this solution. /// A solution containing a project with the specified sources and additional files. - private protected async Task GetSolutionAsync((string filename, SourceText content)[] sources, (string filename, SourceText content)[] additionalFiles, MetadataReference[] additionalMetadataReferences, IReadOnlyDictionary editorConfig) + private protected async Task GetSolutionAsync((string filename, SourceText content)[] sources, (string filename, SourceText content)[] additionalFiles, MetadataReference[] additionalMetadataReferences, IReadOnlyDictionary editorConfig, IEnumerable? analyzerReferences = null) { - return await GetSolutionAsync(sources, additionalFiles, additionalMetadataReferences, ToEditorConfig(editorConfig)); + return await GetSolutionAsync(sources, additionalFiles, additionalMetadataReferences, ToEditorConfig(editorConfig), analyzerReferences); } /// @@ -192,9 +209,10 @@ private protected async Task GetSolutionAsync((string filename, Source /// Additional metadata references to include in the project. /// The .editorconfig to apply to this solution. /// A solution containing a project with the specified sources and additional files. - private protected async Task GetSolutionAsync((string filename, SourceText content)[] sources, (string filename, SourceText content)[] additionalFiles, MetadataReference[] additionalMetadataReferences, string editorConfig) + private protected async Task GetSolutionAsync((string filename, SourceText content)[] sources, (string filename, SourceText content)[] additionalFiles, MetadataReference[] additionalMetadataReferences, string editorConfig, IEnumerable? analyzerReferences = null) { - var project = await CreateProjectAsync(sources, additionalFiles, additionalMetadataReferences, Language, SourceText.From(editorConfig, Encoding.UTF8)); + analyzerReferences ??= Enumerable.Empty(); + var project = await CreateProjectAsync(sources, additionalFiles, additionalMetadataReferences, analyzerReferences, Language, SourceText.From(editorConfig, Encoding.UTF8)); return project.Solution; } @@ -213,10 +231,10 @@ private protected async Task GetSolutionAsync((string filename, Source /// The .editorconfig to apply to this project. /// A created out of the s created from the source /// strings. - protected async Task CreateProjectAsync((string filename, SourceText content)[] sources, (string filename, SourceText content)[] additionalFiles, MetadataReference[] additionalMetadataReferences, string language, SourceText editorConfigText) + protected async Task CreateProjectAsync((string filename, SourceText content)[] sources, (string filename, SourceText content)[] additionalFiles, MetadataReference[] additionalMetadataReferences, IEnumerable analyzerReferences, string language, SourceText editorConfigText) { language ??= Language; - return await CreateProjectImplAsync(sources, additionalFiles, additionalMetadataReferences, language, editorConfigText); + return await CreateProjectImplAsync(sources, additionalFiles, additionalMetadataReferences, analyzerReferences, language, editorConfigText); } /// @@ -230,12 +248,12 @@ protected async Task CreateProjectAsync((string filename, SourceText co /// The .editorconfig to apply to this project. /// A created out of the s created from the source /// strings. - protected virtual async Task CreateProjectImplAsync((string filename, SourceText content)[] sources, (string filename, SourceText content)[] additionalFiles, MetadataReference[] additionalMetadataReferences, string language, SourceText editorConfigText) + protected virtual async Task CreateProjectImplAsync((string filename, SourceText content)[] sources, (string filename, SourceText content)[] additionalFiles, MetadataReference[] additionalMetadataReferences, IEnumerable analyzerReferences, string language, SourceText editorConfigText) { var projectId = ProjectId.CreateNewId(debugName: DefaultTestProjectName); - var solution = await CreateSolutionAsync(projectId, language, editorConfigText); - - solution = solution.AddMetadataReferences(projectId, additionalMetadataReferences); + var solution = (await CreateSolutionAsync(projectId, language, editorConfigText)) + .AddAnalyzerReferences(projectId, analyzerReferences) + .AddMetadataReferences(projectId, additionalMetadataReferences); for (var i = 0; i < sources.Length; i++) { @@ -251,7 +269,7 @@ protected virtual async Task CreateProjectImplAsync((string filename, S solution = solution.AddAdditionalDocument(documentId, newFileName, source); } - return solution.GetProject(projectId); + return solution.GetProject(projectId)!; } /// diff --git a/tests/Formatters/CharsetFormatterTests.cs b/tests/Formatters/CharsetFormatterTests.cs index 96bb2ff0c2..4585df758c 100644 --- a/tests/Formatters/CharsetFormatterTests.cs +++ b/tests/Formatters/CharsetFormatterTests.cs @@ -5,6 +5,7 @@ using System.Threading.Tasks; using Microsoft.CodeAnalysis.Tools.Formatters; using Xunit; +using Xunit.Abstractions; namespace Microsoft.CodeAnalysis.Tools.Tests.Formatters { @@ -12,6 +13,11 @@ public class CharsetFormatterTests : CSharpFormatterTests { private protected override ICodeFormatter Formatter => new CharsetFormatter(); + public CharsetFormatterTests(ITestOutputHelper output) + { + TestOutputHelper = output; + } + [Theory] [InlineData("latin1", "utf-8")] [InlineData("latin1", "utf-8-bom")] diff --git a/tests/Formatters/EndOfLineFormatterTests.cs b/tests/Formatters/EndOfLineFormatterTests.cs index df06638c1f..9420350049 100644 --- a/tests/Formatters/EndOfLineFormatterTests.cs +++ b/tests/Formatters/EndOfLineFormatterTests.cs @@ -4,6 +4,7 @@ using System.Threading.Tasks; using Microsoft.CodeAnalysis.Tools.Formatters; using Xunit; +using Xunit.Abstractions; namespace Microsoft.CodeAnalysis.Tools.Tests.Formatters { @@ -11,6 +12,11 @@ public class EndOfLineFormatterTests : CSharpFormatterTests { private protected override ICodeFormatter Formatter => new EndOfLineFormatter(); + public EndOfLineFormatterTests(ITestOutputHelper output) + { + TestOutputHelper = output; + } + [Theory] [InlineData("\n", "\n", "lf")] [InlineData("\r\n", "\n", "lf")] diff --git a/tests/Formatters/FinalNewlineFormatterTests.cs b/tests/Formatters/FinalNewlineFormatterTests.cs index a305c545d0..c085db6157 100644 --- a/tests/Formatters/FinalNewlineFormatterTests.cs +++ b/tests/Formatters/FinalNewlineFormatterTests.cs @@ -4,6 +4,7 @@ using System.Threading.Tasks; using Microsoft.CodeAnalysis.Tools.Formatters; using Xunit; +using Xunit.Abstractions; namespace Microsoft.CodeAnalysis.Tools.Tests.Formatters { @@ -11,6 +12,11 @@ public class FinalNewlineFormatterTests : CSharpFormatterTests { private protected override ICodeFormatter Formatter => new FinalNewlineFormatter(); + public FinalNewlineFormatterTests(ITestOutputHelper output) + { + TestOutputHelper = output; + } + [Fact] public async Task WhenFinalNewlineUnspecified_AndFinalNewlineMissing_NoChange() { diff --git a/tests/Formatters/FormattedFilesTests.cs b/tests/Formatters/FormattedFilesTests.cs index eec4c2482a..ddf267a836 100644 --- a/tests/Formatters/FormattedFilesTests.cs +++ b/tests/Formatters/FormattedFilesTests.cs @@ -11,6 +11,7 @@ using Microsoft.CodeAnalysis.Tools.Utilities; using Microsoft.Extensions.Logging; using Xunit; +using Xunit.Abstractions; namespace Microsoft.CodeAnalysis.Tools.Tests.Formatters { @@ -24,6 +25,11 @@ public class FormattedFilesTests : CSharpFormatterTests ["end_of_line"] = "lf", }; + public FormattedFilesTests(ITestOutputHelper output) + { + TestOutputHelper = output; + } + [Fact] public async Task ReturnsItem_WhenFileFormatted() { diff --git a/tests/Formatters/OrganizeImportsFormatterTests.cs b/tests/Formatters/OrganizeImportsFormatterTests.cs index b9a83dba4c..407ee14484 100644 --- a/tests/Formatters/OrganizeImportsFormatterTests.cs +++ b/tests/Formatters/OrganizeImportsFormatterTests.cs @@ -5,6 +5,7 @@ using System.Threading.Tasks; using Microsoft.CodeAnalysis.Tools.Formatters; using Xunit; +using Xunit.Abstractions; namespace Microsoft.CodeAnalysis.Tools.Tests.Formatters { @@ -12,6 +13,11 @@ public class OrganizeImportsFormatterTests : CSharpFormatterTests { private protected override ICodeFormatter Formatter => new OrganizeImportsFormatter(); + public OrganizeImportsFormatterTests(ITestOutputHelper output) + { + TestOutputHelper = output; + } + [Fact] public async Task WhenOptionsDisabled_AndImportsNotSorted_ImportsSorted() { diff --git a/tests/Formatters/UnnecessaryImportsFormatterTests.cs b/tests/Formatters/UnnecessaryImportsFormatterTests.cs index 2a69c43d46..66b33e088c 100644 --- a/tests/Formatters/UnnecessaryImportsFormatterTests.cs +++ b/tests/Formatters/UnnecessaryImportsFormatterTests.cs @@ -5,6 +5,7 @@ using Microsoft.CodeAnalysis.Diagnostics; using Microsoft.CodeAnalysis.Tools.Formatters; using Xunit; +using Xunit.Abstractions; namespace Microsoft.CodeAnalysis.Tools.Tests.Formatters { @@ -17,6 +18,11 @@ public class UnnecessaryImportsFormatterTests : CSharpFormatterTests private protected override ICodeFormatter Formatter => new UnnecessaryImportsFormatter(); + public UnnecessaryImportsFormatterTests(ITestOutputHelper output) + { + TestOutputHelper = output; + } + [Fact] public async Task WhenNotFixingCodeSyle_AndHasUnusedImports_NoChange() { diff --git a/tests/Utilities/NuGetHelper.cs b/tests/Utilities/NuGetHelper.cs new file mode 100644 index 0000000000..8e6817ccba --- /dev/null +++ b/tests/Utilities/NuGetHelper.cs @@ -0,0 +1,27 @@ +// Copyright (c) Microsoft. All Rights Reserved. Licensed under the MIT license. See License.txt in the project root for license information. + +using System; +using System.IO; +using System.Threading.Tasks; +using Microsoft.CodeAnalysis.Tools.Utilities; +using Xunit.Abstractions; + +#nullable enable + +namespace Microsoft.CodeAnalysis.Tools.Tests.Utilities +{ + internal static class NuGetHelper + { + public static async Task PerformRestore(string workspaceFilePath, ITestOutputHelper output) + { + var workspacePath = Path.Combine(TestProjectsPathHelper.GetProjectsDirectory(), workspaceFilePath); + + var processInfo = ProcessRunner.CreateProcess("dotnet", $"restore \"{workspacePath}\"", captureOutput: true, displayWindow: false); + var restoreResult = await processInfo.Result; + + output.WriteLine(string.Join(Environment.NewLine, restoreResult.OutputLines)); + + return restoreResult.ExitCode; + } + } +} diff --git a/tests/projects/for_analyzer_formatter/analyzer_project/analyzer_project.csproj b/tests/projects/for_analyzer_formatter/analyzer_project/analyzer_project.csproj new file mode 100644 index 0000000000..5f0d40f60c --- /dev/null +++ b/tests/projects/for_analyzer_formatter/analyzer_project/analyzer_project.csproj @@ -0,0 +1,14 @@ + + + + netstandard2.0 + + + + + all + runtime; build; native; contentfiles; analyzers + + + +