Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Improve support for TestWorkspace with source generated files #58455

Merged
merged 2 commits into from
Dec 23, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 17 additions & 7 deletions src/Compilers/Test/Core/SourceGeneration/TestGenerators.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,8 @@
// See the LICENSE file in the project root for more information.

using System;
using System.IO;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using Microsoft.CodeAnalysis;
Expand All @@ -13,18 +14,27 @@ namespace Roslyn.Test.Utilities.TestGenerators
{
internal class SingleFileTestGenerator : ISourceGenerator
{
private readonly string _content;
private readonly string _hintName;
private readonly List<(string content, string hintName)> _sources = new();

public SingleFileTestGenerator(string content, string hintName = "generatedFile")
public SingleFileTestGenerator()
{
_content = content;
_hintName = hintName;
}

public SingleFileTestGenerator(string content, string? hintName = null)
{
AddSource(content, hintName);
}

public void AddSource(string content, string? hintName = null)
{
hintName ??= "generatedFile" + (_sources.Any() ? (_sources.Count + 1).ToString() : "");
_sources.Add((content, hintName));
}

public void Execute(GeneratorExecutionContext context)
{
context.AddSource(this._hintName, SourceText.From(_content, Encoding.UTF8));
foreach (var (content, hintName) in _sources)
context.AddSource(hintName, SourceText.From(content, Encoding.UTF8));
}

public void Initialize(GeneratorInitializationContext context)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -440,7 +440,7 @@ public async Task CrefCompletionSpeculatesOutsideTrivia()
class C
{
}";
using var workspace = TestWorkspace.Create(LanguageNames.CSharp, new CSharpCompilationOptions(OutputKind.ConsoleApplication), new CSharpParseOptions(), new[] { text }, ExportProvider);
using var workspace = TestWorkspace.Create(LanguageNames.CSharp, new CSharpCompilationOptions(OutputKind.ConsoleApplication), new CSharpParseOptions(), new[] { text }, exportProvider: ExportProvider);
var called = false;

var hostDocument = workspace.DocumentWithCursor;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2777,7 +2777,7 @@ static void Main(string[] args)
override $$
}
}";
using var workspace = TestWorkspace.Create(LanguageNames.CSharp, new CSharpCompilationOptions(OutputKind.ConsoleApplication), new CSharpParseOptions(), new[] { text }, ExportProvider);
using var workspace = TestWorkspace.Create(LanguageNames.CSharp, new CSharpCompilationOptions(OutputKind.ConsoleApplication), new CSharpParseOptions(), new[] { text }, exportProvider: ExportProvider);
var provider = new OverrideCompletionProvider();
var testDocument = workspace.Documents.Single();
var document = workspace.CurrentSolution.GetRequiredDocument(testDocument.Id);
Expand Down
31 changes: 31 additions & 0 deletions src/EditorFeatures/CSharpTest/NavigateTo/NavigateToTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
Expand Down Expand Up @@ -1473,6 +1474,36 @@ public class C
},
await _aggregator.GetItemsAsync("C"));
}

[Fact]
public async Task DoIncludeSymbolsFromMultipleSourceGeneratedFiles()
{
using var workspace = TestWorkspace.CreateCSharp(
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can the xml syntax cannot support this scenario?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

all the other tests in this file use the xml syntax so I would prefer if we kept things consistent

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

➡️ I needed one test to use the new code so we can see it's working. Both work; but the XML form is clumsier. Eventually it would be nice to provide an object model similar to Microsoft.CodeAnalysis.Testing.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

but the XML form is clumsier

Is this a style question or are there bugs that we don't find by testing with one or the other?

I can agree that testing via XML is much nicer in VB and would be fine if we used a different teat api surface area in C# vs VB. However, without some additional benefit beyond ergonomics I can't say I think it would be worth the exercise.

Copy link
Member Author

@sharwell sharwell Dec 22, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It doesn't have a schema, so there's no real completion support. I'm not a fan of the reliance on copy/pasting from some other existing test which occurs even when writing VB code.

I'm not sure whether we validate the complete contents of the XML markup (e.g. if an attribute is misspelled but still valid XML, do we fail?).

files: Array.Empty<string>(),
sourceGeneratedFiles: new[]
{
@"
public partial class C
{
}",
@"
public partial class C
{
}",
},
composition: EditorTestCompositions.EditorFeatures);

_provider = new NavigateToItemProvider(workspace, AsynchronousOperationListenerProvider.NullListener, workspace.GetService<IThreadingContext>());
_aggregator = new NavigateToTestAggregator(_provider);

VerifyNavigateToResultItems(
new()
{
new NavigateToItem("C", NavigateToItemKind.Class, "csharp", null, null, s_emptyExactPatternMatch, null),
new NavigateToItem("C", NavigateToItemKind.Class, "csharp", null, null, s_emptyExactPatternMatch, null),
},
await _aggregator.GetItemsAsync("C"));
}
}
}
#pragma warning restore CS0618 // MatchKind is obsolete
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ public async Task Test_FadingSpans()
{ LanguageNames.CSharp, ImmutableArray.Create<DiagnosticAnalyzer>(analyzer) }
};

using var workspace = TestWorkspace.CreateCSharp(new string[] { "class A { }", "class E { }" }, CSharpParseOptions.Default, composition: SquiggleUtilities.CompositionWithSolutionCrawler);
using var workspace = TestWorkspace.CreateCSharp(new string[] { "class A { }", "class E { }" }, parseOptions: CSharpParseOptions.Default, composition: SquiggleUtilities.CompositionWithSolutionCrawler);
using var wrapper = new DiagnosticTaggerWrapper<DiagnosticsClassificationTaggerProvider, ClassificationTag>(workspace, analyzerMap);
var tagger = wrapper.TaggerProvider.CreateTagger<ClassificationTag>(workspace.Documents.First().GetTextBuffer());
using var disposable = tagger as IDisposable;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ public async Task Test_TagSourceDiffer()
{ LanguageNames.CSharp, ImmutableArray.Create<DiagnosticAnalyzer>(analyzer) }
};

using var workspace = TestWorkspace.CreateCSharp(new string[] { "class A { }", "class E { }" }, CSharpParseOptions.Default);
using var workspace = TestWorkspace.CreateCSharp(new string[] { "class A { }", "class E { }" }, parseOptions: CSharpParseOptions.Default);
using var wrapper = new DiagnosticTaggerWrapper<DiagnosticsSquiggleTaggerProvider, IErrorTag>(workspace, analyzerMap);
var tagger = wrapper.TaggerProvider.CreateTagger<IErrorTag>(workspace.Documents.First().GetTextBuffer());
using var disposable = tagger as IDisposable;
Expand Down Expand Up @@ -69,7 +69,7 @@ public async Task Test_TagSourceDiffer()
[WpfFact, Trait(Traits.Feature, Traits.Features.Diagnostics)]
public async Task MultipleTaggersAndDispose()
{
using var workspace = TestWorkspace.CreateCSharp(new string[] { "class A {" }, CSharpParseOptions.Default);
using var workspace = TestWorkspace.CreateCSharp(new string[] { "class A {" }, parseOptions: CSharpParseOptions.Default);
using var wrapper = new DiagnosticTaggerWrapper<DiagnosticsSquiggleTaggerProvider, IErrorTag>(workspace);
// Make two taggers.
var tagger1 = wrapper.TaggerProvider.CreateTagger<IErrorTag>(workspace.Documents.First().GetTextBuffer());
Expand All @@ -89,7 +89,7 @@ public async Task MultipleTaggersAndDispose()
[WpfFact, Trait(Traits.Feature, Traits.Features.Diagnostics)]
public async Task TaggerProviderCreatedAfterInitialDiagnosticsReported()
{
using var workspace = TestWorkspace.CreateCSharp(new string[] { "class C {" }, CSharpParseOptions.Default);
using var workspace = TestWorkspace.CreateCSharp(new string[] { "class C {" }, parseOptions: CSharpParseOptions.Default);
using var wrapper = new DiagnosticTaggerWrapper<DiagnosticsSquiggleTaggerProvider, IErrorTag>(workspace, analyzerMap: null, createTaggerProvider: false);
// First, make sure all diagnostics have been reported.
await wrapper.WaitForTags();
Expand Down Expand Up @@ -119,7 +119,7 @@ public async Task TestWithMockDiagnosticService_TaggerProviderCreatedBeforeIniti

using var workspace = TestWorkspace.CreateCSharp(
new string[] { "class A { }" },
CSharpParseOptions.Default,
parseOptions: CSharpParseOptions.Default,
composition: s_compositionWithMockDiagnosticService);

var listenerProvider = workspace.ExportProvider.GetExportedValue<IAsynchronousOperationListenerProvider>();
Expand Down Expand Up @@ -156,7 +156,7 @@ public async Task TestWithMockDiagnosticService_TaggerProviderCreatedAfterInitia

using var workspace = TestWorkspace.CreateCSharp(
new string[] { "class A { }" },
CSharpParseOptions.Default,
parseOptions: CSharpParseOptions.Default,
composition: s_compositionWithMockDiagnosticService);

var listenerProvider = workspace.ExportProvider.GetExportedValue<IAsynchronousOperationListenerProvider>();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

#nullable disable

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
Expand Down Expand Up @@ -128,6 +129,7 @@ internal static TestWorkspace Create(
CompilationOptions compilationOptions,
ParseOptions parseOptions,
string[] files,
string[] sourceGeneratedFiles = null,
ExportProvider exportProvider = null,
TestComposition composition = null,
string[] metadataReferences = null,
Expand All @@ -137,7 +139,7 @@ internal static TestWorkspace Create(
bool openDocuments = false,
IDocumentServiceProvider documentServiceProvider = null)
{
var workspaceElement = CreateWorkspaceElement(language, compilationOptions, parseOptions, files, metadataReferences, extension, commonReferences);
var workspaceElement = CreateWorkspaceElement(language, compilationOptions, parseOptions, files, sourceGeneratedFiles, metadataReferences, extension, commonReferences);
return Create(workspaceElement, openDocuments, exportProvider, composition, workspaceKind, documentServiceProvider);
}

Expand Down Expand Up @@ -193,19 +195,20 @@ public static TestWorkspace CreateCSharp(
string[] metadataReferences = null,
bool openDocuments = false)
{
return CreateCSharp(new[] { file }, parseOptions, compilationOptions, exportProvider, composition, metadataReferences, openDocuments);
return CreateCSharp(new[] { file }, Array.Empty<string>(), parseOptions, compilationOptions, exportProvider, composition, metadataReferences, openDocuments);
}

public static TestWorkspace CreateCSharp(
string[] files,
string[] sourceGeneratedFiles = null,
ParseOptions parseOptions = null,
CompilationOptions compilationOptions = null,
ExportProvider exportProvider = null,
TestComposition composition = null,
string[] metadataReferences = null,
bool openDocuments = false)
{
return Create(LanguageNames.CSharp, compilationOptions, parseOptions, files, exportProvider, composition, metadataReferences, openDocuments: openDocuments);
return Create(LanguageNames.CSharp, compilationOptions, parseOptions, files, sourceGeneratedFiles, exportProvider, composition, metadataReferences, openDocuments: openDocuments);
}

public static TestWorkspace CreateCSharp2(
Expand All @@ -230,19 +233,20 @@ public static TestWorkspace CreateVisualBasic(
string[] metadataReferences = null,
bool openDocuments = false)
{
return CreateVisualBasic(new[] { file }, parseOptions, compilationOptions, exportProvider, composition, metadataReferences, openDocuments);
return CreateVisualBasic(new[] { file }, Array.Empty<string>(), parseOptions, compilationOptions, exportProvider, composition, metadataReferences, openDocuments);
}

public static TestWorkspace CreateVisualBasic(
string[] files,
string[] sourceGeneratedFiles = null,
ParseOptions parseOptions = null,
CompilationOptions compilationOptions = null,
ExportProvider exportProvider = null,
TestComposition composition = null,
string[] metadataReferences = null,
bool openDocuments = false)
{
return Create(LanguageNames.VisualBasic, compilationOptions, parseOptions, files, exportProvider, composition, metadataReferences, openDocuments: openDocuments);
return Create(LanguageNames.VisualBasic, compilationOptions, parseOptions, files, sourceGeneratedFiles, exportProvider, composition, metadataReferences, openDocuments: openDocuments);
}

/// <param name="files">Can pass in multiple file contents with individual source kind: files will be named test1.vb, test2.vbx, etc.</param>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@
using System.IO;
using System.Linq;
using System.Runtime.CompilerServices;
using System.ServiceModel.Description;
using System.Threading;
using System.Xml.Linq;
using Microsoft.CodeAnalysis.CSharp;
Expand All @@ -29,7 +28,6 @@
using Microsoft.CodeAnalysis.UnitTests;
using Microsoft.CodeAnalysis.VisualBasic;
using Microsoft.VisualStudio.Composition;
using Microsoft.VisualStudio.Text;
using Roslyn.Test.Utilities;
using Roslyn.Test.Utilities.TestGenerators;
using Roslyn.Utilities;
Expand Down Expand Up @@ -103,6 +101,7 @@ internal void InitializeDocuments(
compilationOptions,
parseOptions,
files,
sourceGeneratedFiles: Array.Empty<string>(),
metadataReferences,
extension,
commonReferences);
Expand Down Expand Up @@ -338,8 +337,15 @@ private static TestHostProject CreateProject(
documents.Add(document);
}

SingleFileTestGenerator testGenerator = null;
foreach (var sourceGeneratedDocumentElement in projectElement.Elements(DocumentFromSourceGeneratorElementName))
{
if (testGenerator is null)
{
testGenerator = new SingleFileTestGenerator();
analyzers.Add(new TestGeneratorReference(testGenerator));
}

var name = GetFileName(workspace, sourceGeneratedDocumentElement, ref documentId);

var markupCode = sourceGeneratedDocumentElement.NormalizedValue();
Expand All @@ -350,7 +356,7 @@ private static TestHostProject CreateProject(
var document = new TestHostDocument(exportProvider, languageServices, code, name, documentFilePath, cursorPosition, spans, isSourceGenerated: true);
documents.Add(document);

analyzers.Add(new TestGeneratorReference(new SingleFileTestGenerator(code, name)));
testGenerator.AddSource(code, name);
}

var additionalDocuments = new List<TestHostDocument>();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,10 @@

#nullable disable

using System;
using System.Collections.Generic;
using System.Linq;
using System.Xml.Linq;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.Text;
using Microsoft.CodeAnalysis.VisualBasic;
using Roslyn.Utilities;

Expand All @@ -22,23 +20,31 @@ internal static XElement CreateWorkspaceElement(
CompilationOptions compilationOptions = null,
ParseOptions parseOptions = null,
string[] files = null,
string[] sourceGeneratedFiles = null,
string[] metadataReferences = null,
string extension = null,
bool commonReferences = true)
{
var documentElements = new List<XElement>();

var index = 0;
extension ??= (language == LanguageNames.CSharp) ? CSharpExtension : VisualBasicExtension;
if (files != null)
{
var index = 0;
extension ??= (language == LanguageNames.CSharp) ? CSharpExtension : VisualBasicExtension;

foreach (var file in files)
{
documentElements.Add(CreateDocumentElement(file, GetDefaultTestSourceDocumentName(index++, extension), parseOptions));
}
}

if (sourceGeneratedFiles != null)
{
foreach (var file in sourceGeneratedFiles)
{
documentElements.Add(CreateDocumentFromSourceGeneratorElement(file, GetDefaultTestSourceDocumentName(index++, extension), parseOptions));
}
}

if (metadataReferences != null)
{
foreach (var reference in metadataReferences)
Expand Down Expand Up @@ -171,6 +177,14 @@ protected static XElement CreateDocumentElement(string code, string filePath, Pa
code.Replace("\r\n", "\n"));
}

protected static XElement CreateDocumentFromSourceGeneratorElement(string code, string hintName, ParseOptions parseOptions = null)
{
return new XElement(DocumentFromSourceGeneratorElementName,
new XAttribute(FilePathAttributeName, hintName),
CreateParseOptionsElement(parseOptions),
code.Replace("\r\n", "\n"));
}

private static XElement CreateParseOptionsElement(ParseOptions parseOptions)
{
return parseOptions == null
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -414,7 +414,7 @@ Class C
End Sub
End Class]]></a>.Value.NormalizeLineEndings()

Using workspace = TestWorkspace.Create(LanguageNames.VisualBasic, New VisualBasicCompilationOptions(OutputKind.ConsoleApplication), New VisualBasicParseOptions(), {text}, ExportProvider)
Using workspace = TestWorkspace.Create(LanguageNames.VisualBasic, New VisualBasicCompilationOptions(OutputKind.ConsoleApplication), New VisualBasicParseOptions(), {text}, exportProvider:=ExportProvider)
Dim called = False

Dim hostDocument = workspace.DocumentWithCursor
Expand Down