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

Fully support additional projects #680

Merged
merged 10 commits into from
Dec 23, 2020
2 changes: 1 addition & 1 deletion RoslynSDK.ruleset
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,6 @@
</Rules>
<Rules AnalyzerId="Roslyn.Diagnostics.Analyzers" RuleNamespace="Roslyn.Diagnostics.Analyzers">
<Rule Id="RS1019" Action="None" /> <!-- DiagnosticId must be unique across analyzers: Many tests use similar analyzer instances -->
<Rule Id="RS1022" Action="None" /> <!-- AD0001: https://github.com/dotnet/roslyn-analyzers/issues/1803 -->
<Rule Id="RS2008" Action="None" /> <!-- Enable analyzer release tracking: only used in this repository for testing -->
</Rules>
</RuleSet>
2 changes: 1 addition & 1 deletion eng/Versions.props
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@
<DiffPlexVersion>1.4.4</DiffPlexVersion>
<!-- Testing -->
<MicrosoftCodeAnalysis2PrimaryTestVersion>2.6.1</MicrosoftCodeAnalysis2PrimaryTestVersion>
<MicrosoftCodeAnalysis3PrimaryTestVersion>3.3.1</MicrosoftCodeAnalysis3PrimaryTestVersion>
<MicrosoftCodeAnalysis3PrimaryTestVersion>3.8.0</MicrosoftCodeAnalysis3PrimaryTestVersion>
<MicrosoftCodeAnalysisTestingVersion>1.0.1-beta1.20374.2</MicrosoftCodeAnalysisTestingVersion>
<XunitCombinatorialVersion>1.2.7</XunitCombinatorialVersion>
<VsixTestingXunitVersion>0.1.49-beta</VsixTestingXunitVersion>
Expand Down

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,15 @@ namespace Microsoft.CodeAnalysis.Testing
{
internal static class DictionaryExtensions
{
public static void AddRange<TKey, TValue>(this IDictionary<TKey, TValue> dictionary, IEnumerable<KeyValuePair<TKey, TValue>> items)
where TKey : notnull
{
foreach (var (key, value) in items)
{
dictionary.Add(key, value);
}
}

// Copied from ConcurrentDictionary since IDictionary doesn't have this useful method
public static TValue GetOrAdd<TKey, TValue>(this IDictionary<TKey, TValue> dictionary, TKey key, Func<TKey, TValue> function)
where TKey : notnull
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.

using System.Collections.Immutable;
using Microsoft.CodeAnalysis.Text;

namespace Microsoft.CodeAnalysis.Testing.Model
{
/// <summary>
/// Represents an evaluated <see cref="ProjectState"/>.
/// </summary>
public sealed class EvaluatedProjectState
{
public EvaluatedProjectState(ProjectState state, ReferenceAssemblies defaultReferenceAssemblies)
: this(
state.Name,
state.AssemblyName,
state.Language,
state.ReferenceAssemblies ?? defaultReferenceAssemblies,
state.OutputKind ?? OutputKind.DynamicallyLinkedLibrary,
state.DocumentationMode ?? DocumentationMode.Diagnose,
state.Sources.ToImmutableArray(),
state.AdditionalFiles.ToImmutableArray(),
state.AdditionalProjectReferences.ToImmutableArray(),
state.AdditionalReferences.ToImmutableArray())
{
}

private EvaluatedProjectState(
string name,
string assemblyName,
string language,
ReferenceAssemblies referenceAssemblies,
OutputKind outputKind,
DocumentationMode documentationMode,
ImmutableArray<(string filename, SourceText content)> sources,
ImmutableArray<(string filename, SourceText content)> additionalFiles,
ImmutableArray<string> additionalProjectReferences,
ImmutableArray<MetadataReference> additionalReferences)
{
Name = name;
AssemblyName = assemblyName;
Language = language;
ReferenceAssemblies = referenceAssemblies;
OutputKind = outputKind;
DocumentationMode = documentationMode;
Sources = sources;
AdditionalFiles = additionalFiles;
AdditionalProjectReferences = additionalProjectReferences;
AdditionalReferences = additionalReferences;
}

public string Name { get; }

public string AssemblyName { get; }

public string Language { get; }

public ReferenceAssemblies ReferenceAssemblies { get; }

public OutputKind OutputKind { get; }

public DocumentationMode DocumentationMode { get; }

public ImmutableArray<(string filename, SourceText content)> Sources { get; }

public ImmutableArray<(string filename, SourceText content)> AdditionalFiles { get; }

public ImmutableArray<string> AdditionalProjectReferences { get; }

public ImmutableArray<MetadataReference> AdditionalReferences { get; }

public EvaluatedProjectState WithSources(ImmutableArray<(string filename, SourceText content)> sources)
{
if (sources == Sources)
{
return this;
}

return With(sources: sources);
}

private EvaluatedProjectState With(
Optional<string> name = default,
Optional<string> assemblyName = default,
Optional<string> language = default,
Optional<ReferenceAssemblies> referenceAssemblies = default,
Optional<OutputKind> outputKind = default,
Optional<DocumentationMode> documentationMode = default,
Optional<ImmutableArray<(string filename, SourceText content)>> sources = default,
Optional<ImmutableArray<(string filename, SourceText content)>> additionalFiles = default,
Optional<ImmutableArray<string>> additionalProjectReferences = default,
Optional<ImmutableArray<MetadataReference>> additionalReferences = default)
{
return new EvaluatedProjectState(
GetValueOrDefault(name, Name),
GetValueOrDefault(assemblyName, AssemblyName),
GetValueOrDefault(language, Language),
GetValueOrDefault(referenceAssemblies, ReferenceAssemblies),
GetValueOrDefault(outputKind, OutputKind),
GetValueOrDefault(documentationMode, DocumentationMode),
GetValueOrDefault(sources, Sources),
GetValueOrDefault(additionalFiles, AdditionalFiles),
GetValueOrDefault(additionalProjectReferences, AdditionalProjectReferences),
GetValueOrDefault(additionalReferences, AdditionalReferences));
}

private static T GetValueOrDefault<T>(Optional<T> optionalValue, T defaultValue)
{
return optionalValue.HasValue ? optionalValue.Value : defaultValue;
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.

using System;
using System.Collections.Generic;

namespace Microsoft.CodeAnalysis.Testing
{
public class ProjectCollection : Dictionary<string, ProjectState>
{
private readonly string _defaultLanguage;
private readonly string _defaultExtension;

public ProjectCollection(string defaultLanguage, string defaultExtension)
{
_defaultLanguage = defaultLanguage;
_defaultExtension = defaultExtension;
}

public new ProjectState this[string projectName]
{
get
{
if (TryGetValue(projectName, out var project))
{
return project;
}

return this[projectName, _defaultLanguage];
}
}

public ProjectState this[string projectName, string language]
{
get
{
var project = this.GetOrAdd(projectName, () => new ProjectState(projectName, _defaultLanguage, $"/{projectName}/Test", _defaultExtension));
if (project.Language != language)
{
throw new InvalidOperationException();
}

return project;
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,34 +2,77 @@
// 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 Microsoft.CodeAnalysis.Text;

namespace Microsoft.CodeAnalysis.Testing
{
public abstract class ProjectState
public class ProjectState
{
private readonly string _defaultPrefix;
private readonly string _defaultExtension;

protected ProjectState(string name, string defaultPrefix, string defaultExtension)
public ProjectState(string name, string language, string defaultPrefix, string defaultExtension)
{
Name = name;
_defaultPrefix = defaultPrefix;
_defaultExtension = defaultExtension;
Language = language;
DefaultPrefix = defaultPrefix;
DefaultExtension = defaultExtension;

Sources = new SourceFileList(defaultPrefix, defaultExtension);
}

internal ProjectState(ProjectState sourceState)
{
Name = sourceState.Name;
Language = sourceState.Language;
ReferenceAssemblies = sourceState.ReferenceAssemblies;
OutputKind = sourceState.OutputKind;
DocumentationMode = sourceState.DocumentationMode;
DefaultPrefix = sourceState.DefaultPrefix;
DefaultExtension = sourceState.DefaultExtension;
Sources = new SourceFileList(DefaultPrefix, DefaultExtension);

Sources.AddRange(sourceState.Sources);
AdditionalFiles.AddRange(sourceState.AdditionalFiles);
AdditionalFilesFactories.AddRange(sourceState.AdditionalFilesFactories);
AdditionalProjectReferences.AddRange(sourceState.AdditionalProjectReferences);
}

public string Name { get; }

public string AssemblyName => Name;

public abstract string Language { get; }
public string Language { get; }

/// <summary>
/// Gets or sets the reference assemblies to use for the project.
/// </summary>
/// <value>
/// A <see cref="Testing.ReferenceAssemblies"/> instance to use specific reference assemblies; otherwise,
/// <see langword="null"/> to inherit the reference assemblies from
/// <see cref="AnalyzerTest{TVerifier}.ReferenceAssemblies"/>.
/// </value>
public ReferenceAssemblies? ReferenceAssemblies { get; set; }

public OutputKind? OutputKind { get; set; }

public DocumentationMode? DocumentationMode { get; set; }

/// <summary>
/// Gets the set of source files for analyzer or code fix testing. Files may be added to this list using one of
/// the <see cref="SourceFileList.Add(string)"/> methods.
/// </summary>
public SourceFileList Sources { get; }

public SourceFileCollection AdditionalFiles { get; } = new SourceFileCollection();

public List<Func<IEnumerable<(string filename, SourceText content)>>> AdditionalFilesFactories { get; } = new List<Func<IEnumerable<(string filename, SourceText content)>>>();

public List<string> AdditionalProjectReferences { get; } = new List<string>();

public MetadataReferenceCollection AdditionalReferences { get; } = new MetadataReferenceCollection();

private protected string DefaultPrefix { get; }

private protected string DefaultExtension { get; }
}
}
Loading