Skip to content
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
31 changes: 31 additions & 0 deletions src/FluentAssertions.Analyzers.TestUtils/CsProjectArguments.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
using System;
using Microsoft.CodeAnalysis;

public class CsProjectArguments
{
public TargetFramework TargetFramework { get; set; } = TargetFramework.Net6_0;
public string[] Sources { get; set; }
public PackageReference[] PackageReferences { get; set; } = Array.Empty<PackageReference>();
public string Language { get; set; } = LanguageNames.CSharp;
}

public static class CsProjectArgumentsExtensions
{
public static TCsProjectArguments WithTargetFramework<TCsProjectArguments>(this TCsProjectArguments arguments, TargetFramework targetFramework) where TCsProjectArguments : CsProjectArguments
{
arguments.TargetFramework = targetFramework;
return arguments;
}

public static TCsProjectArguments WithSources<TCsProjectArguments>(this TCsProjectArguments arguments, params string[] sources) where TCsProjectArguments : CsProjectArguments
{
arguments.Sources = sources;
return arguments;
}

public static TCsProjectArguments WithPackageReferences<TCsProjectArguments>(this TCsProjectArguments arguments, params PackageReference[] packageReferences) where TCsProjectArguments : CsProjectArguments
{
arguments.PackageReferences = packageReferences;
return arguments;
}
}
72 changes: 23 additions & 49 deletions src/FluentAssertions.Analyzers.TestUtils/CsProjectGenerator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -59,12 +59,6 @@ string GetSystemAssemblyPathByName(string assemblyName)
return System.IO.Path.Combine(root, assemblyName);
}
}
// based on http://code.fitness/post/2017/02/using-csharpscript-with-netstandard.html
public static string GetSystemAssemblyPathByName(string assemblyName)
{
var root = System.IO.Path.GetDirectoryName(typeof(object).Assembly.Location);
return System.IO.Path.Combine(root, assemblyName);
}

private static readonly ImmutableArray<MetadataReference> References;

Expand All @@ -73,40 +67,7 @@ public static string GetSystemAssemblyPathByName(string assemblyName)
private static readonly string VisualBasicDefaultExt = "vb";
private static readonly string TestProjectName = "TestProject";

/// <summary>
/// Given an array of strings as sources and a language, turn them into a project and return the documents and spans of it.
/// </summary>
/// <param name="sources">Classes in the form of strings</param>
/// <param name="language">The language the source code is in</param>
/// <returns>A Tuple containing the Documents produced from the sources and their TextSpans if relevant</returns>
public static Document[] GetDocuments(string[] sources, string language)
{
if (language != LanguageNames.CSharp && language != LanguageNames.VisualBasic)
{
throw new ArgumentException("Unsupported Language");
}

var project = CreateProject(sources, language);
var documents = project.Documents.ToArray();

if (sources.Length != documents.Length)
{
throw new SystemException("Amount of sources did not match amount of Documents created");
}

return documents;
}

/// <summary>
/// Create a Document from a string through creating a project that contains it.
/// </summary>
/// <param name="source">Classes in the form of a string</param>
/// <param name="language">The language the source code is in</param>
/// <returns>A Document created from the source string</returns>
public static Document CreateDocument(string source, string language = LanguageNames.CSharp)
{
return CreateProject(new[] { source }, language).Documents.First();
}
public static Document CreateDocument(CsProjectArguments arguments) => CreateProject(arguments).Documents.First();

/// <summary>
/// Create a project using the inputted strings as sources.
Expand All @@ -115,27 +76,40 @@ public static Document CreateDocument(string source, string language = LanguageN
/// <param name="language">The language the source code is in</param>
/// <returns>A Project created out of the Documents created from the source strings</returns>
public static Project CreateProject(string[] sources, string language = LanguageNames.CSharp)
{
var arguments = new CsProjectArguments
{
Language = language,
Sources = sources,
TargetFramework = TargetFramework.Net6_0,
};
return CreateProject(arguments).AddMetadataReferences(References);
}

public static Project CreateProject(CsProjectArguments arguments)
{
string fileNamePrefix = DefaultFilePathPrefix;
string fileExt = language == LanguageNames.CSharp ? CSharpDefaultFileExt : VisualBasicDefaultExt;
string fileExt = arguments.Language is LanguageNames.CSharp ? CSharpDefaultFileExt : VisualBasicDefaultExt;

var projectId = ProjectId.CreateNewId(debugName: TestProjectName);

var solution = new AdhocWorkspace()
.CurrentSolution
.AddProject(projectId, TestProjectName, TestProjectName, language)
.AddMetadataReferences(projectId, References);
.AddProject(projectId, TestProjectName, TestProjectName, arguments.Language);
foreach (var package in arguments.PackageReferences)
{
solution = solution.AddPackageReference(projectId, package);
}

solution = solution.AddTargetFrameworkReference(projectId, arguments.TargetFramework);

int count = 0;
foreach (var source in sources)
for (int i = 0; i < arguments.Sources.Length; i++)
{
var newFileName = fileNamePrefix + count + "." + fileExt;
var newFileName = fileNamePrefix + i + "." + fileExt;
var documentId = DocumentId.CreateNewId(projectId, debugName: newFileName);
solution = solution.AddDocument(documentId, newFileName, SourceText.From(source));
count++;
solution = solution.AddDocument(documentId, newFileName, SourceText.From(arguments.Sources[i]));
}
return solution.GetProject(projectId);
}
}
}

15 changes: 15 additions & 0 deletions src/FluentAssertions.Analyzers.TestUtils/PackageReference.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
public class PackageReference
{
public string Name { get; }
public string Version { get; }
public string Path { get; }

public PackageReference(string name, string version, string path) => (Name, Version, Path) = (name, version, path);

public static PackageReference FluentAssertions_6_12_0 { get; } = FluentAssertions("6.12.0");

public static PackageReference MSTestTestFramework_3_1_1 { get; } = new("MSTest.TestFramework", "3.1.1", "lib/netstandard2.0/");
public static PackageReference XunitAssert_2_5_1 { get; } = new("xunit.assert", "2.5.1", "lib/netstandard1.1/");

public static PackageReference FluentAssertions(string version) => new("FluentAssertions", version, "lib/netstandard2.0/");
}
55 changes: 55 additions & 0 deletions src/FluentAssertions.Analyzers.TestUtils/SolutionExtensions.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
using System;
using System.IO;
using System.IO.Compression;
using System.Net.Http;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis;

public static class SolutionExtensions
{
private static readonly string NugetPackagesPath = Environment.GetEnvironmentVariable("NUGET_PACKAGES")
?? (OperatingSystem.IsWindows() ? Environment.ExpandEnvironmentVariables("%userprofile%\\.nuget\\packages") : "~/.nuget/packages");

private static readonly HttpClient HttpClient = new HttpClient();

public static Solution AddPackageReference(this Solution solution, ProjectId projectId, PackageReference package)
{
DownloadPackageAsync(package.Name, package.Version).GetAwaiter().GetResult();

var packagePath = Path.Combine(NugetPackagesPath, package.Name, package.Version, package.Path);
foreach (var dll in Directory.GetFiles(packagePath, "*.dll"))
{
solution = solution.AddMetadataReference(projectId, MetadataReference.CreateFromFile(dll));
}

return solution;
}

public static Solution AddTargetFrameworkReference(this Solution solution, ProjectId projectId, TargetFramework targetFramework)
{
return targetFramework switch
{
TargetFramework.NetStandard2_0 => solution.AddPackageReference(projectId, new("NETStandard.Library", "2.0.3", "build/netstandard2.0/ref/")),
TargetFramework.NetStandard2_1 => solution.AddPackageReference(projectId, new("NETStandard.Library.Ref", "2.1.0", "ref/netstandard2.1/ref/")),
TargetFramework.Net6_0 => solution.AddPackageReference(projectId, new("Microsoft.NETCore.App.Ref", "6.0.25", "ref/net6.0/")),
TargetFramework.Net7_0 => solution.AddPackageReference(projectId, new("Microsoft.NETCore.App.Ref", "7.0.14", "ref/net7.0/")),
TargetFramework.Net8_0 => solution.AddPackageReference(projectId, new("Microsoft.NETCore.App.Ref", "8.0.0", "ref/net8.0/")),
_ => throw new ArgumentOutOfRangeException(nameof(targetFramework), targetFramework, "Unknown target framework"),
};
}

private static async Task DownloadPackageAsync(string packageId, string version)
{
var packagePath = Path.Combine(NugetPackagesPath, packageId, version);
if (Directory.Exists(packagePath))
{
return;
}

await using var stream = await HttpClient.GetStreamAsync(new Uri($"https://www.nuget.org/api/v2/package/{packageId}/{version}")).ConfigureAwait(false);
using var zip = new ZipArchive(stream, ZipArchiveMode.Read);

Directory.CreateDirectory(packagePath);
zip.ExtractToDirectory(packagePath, overwriteFiles: true);
}
}
8 changes: 8 additions & 0 deletions src/FluentAssertions.Analyzers.TestUtils/TargetFramework.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
public enum TargetFramework
{
NetStandard2_0,
NetStandard2_1,
Net6_0,
Net7_0,
Net8_0,
}
34 changes: 34 additions & 0 deletions src/FluentAssertions.Analyzers.Tests/CodeFixVerifierArguments.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
using System.Collections.Generic;
using Microsoft.CodeAnalysis.CodeFixes;
using Microsoft.CodeAnalysis.Diagnostics;

namespace FluentAssertions.Analyzers.Tests;

public class CodeFixVerifierArguments : CsProjectArguments
{
public List<string> FixedSources { get; } = new();

public List<DiagnosticAnalyzer> DiagnosticAnalyzers { get; } = new();

public List<CodeFixProvider> CodeFixProviders { get; } = new();

public CodeFixVerifierArguments() { }

public CodeFixVerifierArguments WithDiagnosticAnalyzer<TDiagnosticAnalyzer>() where TDiagnosticAnalyzer : DiagnosticAnalyzer, new()
{
DiagnosticAnalyzers.Add(new TDiagnosticAnalyzer());
return this;
}

public CodeFixVerifierArguments WithCodeFixProvider<TCodeFixProvider>() where TCodeFixProvider : CodeFixProvider, new()
{
CodeFixProviders.Add(new TCodeFixProvider());
return this;
}

public CodeFixVerifierArguments WithFixedSources(params string[] fixedSources)
{
FixedSources.AddRange(fixedSources);
return this;
}
}
Loading