diff --git a/.build/.build.csproj b/.build/.build.csproj
index 67d554b7ad..7dff94fa91 100644
--- a/.build/.build.csproj
+++ b/.build/.build.csproj
@@ -2,7 +2,7 @@
Exe
net6.0
-
+
False
CS0649;CS0169
@@ -12,6 +12,7 @@
+
@@ -19,4 +20,4 @@
-
\ No newline at end of file
+
diff --git a/.build/Build.CI.cs b/.build/Build.CI.cs
index bfd360b36b..7de0234da0 100644
--- a/.build/Build.CI.cs
+++ b/.build/Build.CI.cs
@@ -31,6 +31,7 @@ internal class LocalConstants
"ci-ignore",
GitHubActionsImage.WindowsLatest,
GitHubActionsImage.UbuntuLatest,
+ AutoGenerate = false,
On = new[] { GitHubActionsTrigger.Push },
OnPushTags = new[] { "v*" },
OnPushBranches = new[] { "master", "main", "next" },
@@ -42,6 +43,7 @@ internal class LocalConstants
GitHubActionsImage.MacOsLatest,
GitHubActionsImage.WindowsLatest,
GitHubActionsImage.UbuntuLatest,
+ AutoGenerate = false,
On = new[] { GitHubActionsTrigger.Push },
OnPushTags = new[] { "v*" },
OnPushBranches = new[] { "master", "main", "next" },
@@ -64,7 +66,7 @@ internal class LocalConstants
[PrintCIEnvironment]
[UploadLogs]
[TitleEvents]
-public partial class Solution
+public partial class NukeSolution
{
public static RocketSurgeonGitHubActionsConfiguration CiIgnoreMiddleware(
RocketSurgeonGitHubActionsConfiguration configuration
diff --git a/.build/Build.Metadata.cs b/.build/Build.Metadata.cs
new file mode 100644
index 0000000000..c999d6cbff
--- /dev/null
+++ b/.build/Build.Metadata.cs
@@ -0,0 +1,220 @@
+using System.Collections.Immutable;
+using System.Text;
+using System.Xml.Linq;
+using NuGet.LibraryModel;
+using NuGet.ProjectModel;
+using NuGet.Versioning;
+using Nuke.Common;
+using Nuke.Common.ProjectModel;
+using Nuke.Common.Utilities;
+
+public partial class NukeSolution : IParseGeneratorMetadata
+{
+}
+
+/*
+
+
+
+ <_rsgPackageReferenceList>@(PackageReference)
+ <_rsgPackageVersionList>@(PackageVersion)
+
+
+
+
+
+
+
+
+ <_rsgPackageReferenceList>@(PackageReference)
+ <_rsgPackageVersionList>@(PackageVersion)
+
+
+
+
+
+
+
+
+
+ */
+
+
+public interface IParseGeneratorMetadata : IHaveSolution, IHaveOutputLogs, IHaveBuildTarget, IHaveRestoreTarget, IComprehendSources, IHaveGitVersion,
+ IComprehendTests, IHaveTestTarget, IHavePackTarget
+{
+ public Target LoadProjectData => _ =>
+ _
+ .After(Restore)
+ .DependentFor(Pack)
+ .Executes(
+ () =>
+ {
+ var projects = new List();
+
+ projects.Add(new GeneratorItem("Rocket.Surgery.Extensions.Testing", ImmutableArray.Empty));
+ var lockFileFormat = new LockFileFormat();
+ foreach (var project in Solution
+ .AllProjects
+ .Where(z => z.GetProperty("IsMagicProject") == true))
+ {
+ var lockFile = lockFileFormat.Read(project.Directory / "obj" / "project.assets.json")!;
+ var results = lockFile.PackageSpec.TargetFrameworks
+ .SelectMany(
+ z => z.Dependencies
+ .Where(
+ z => !z.AutoReferenced
+ && ( z.IncludeType & LibraryIncludeFlags.Compile ) != 0
+ && z.ReferenceType == LibraryDependencyReferenceType.Direct
+ ),
+ (information, dependency) => ( target: information.TargetAlias, dependency )
+ )
+ .GroupBy(z => z.dependency.Name)
+ .Where(z => z.Count() == lockFile.PackageSpec.TargetFrameworks.Count)
+ .Select(z => z.First().dependency)
+ .Select(
+ z => new PackageReferenceItem(
+ z.Name,
+ lockFile.Libraries.First(x => x.Name == z.Name).Version
+ )
+ )
+ .ToList();
+
+
+ projects.Add(new GeneratorItem(lockFile.PackageSpec.Name, results.ToImmutableArray()));
+ if (lockFile.PackageSpec.Name == "Rocket.Surgery.Extensions.Testing.XUnit")
+ {
+ projects.Add(
+ new GeneratorItem(
+ lockFile.PackageSpec.Name,
+ results
+ .Select(z => z with { Name = z.Name.Replace("xunit.abstractions", "xunit") })
+ .ToImmutableArray()
+ )
+ );
+ }
+ }
+
+ var targetsDoc = new XDocument();
+ var implicitPackageReferencesTarget = new XElement("Target");
+ var implicitCentralPackageVersionsTarget = new XElement("Target");
+ var xProperties = new XElement("PropertyGroup");
+ {
+ var xProject = new XElement("Project");
+ var propertyGroup = XElement.Parse(
+ @"<_rsgPackageReferenceList>@(PackageReference)<_rsgPackageVersionList>@(PackageVersion)"
+ );
+
+ implicitPackageReferencesTarget.SetAttributeValue("Name", "AddRsgImplicitPackageReferences");
+ implicitPackageReferencesTarget.SetAttributeValue("BeforeTargets", "CollectPackageReferences");
+ implicitPackageReferencesTarget.SetAttributeValue(
+ "Condition", "'$(ManagePackageVersionsCentrally)' == 'true' and '$(ImplicitPackageReferences)' == 'true'"
+ );
+ implicitPackageReferencesTarget.Add(propertyGroup.Clone());
+
+ implicitCentralPackageVersionsTarget.SetAttributeValue("Name", "AddRsgImplicitCentralPackageVersions");
+ implicitCentralPackageVersionsTarget.SetAttributeValue("BeforeTargets", "CollectCentralPackageVersions");
+ implicitCentralPackageVersionsTarget.SetAttributeValue("AfterTargets", "CollectPackageReferences");
+ implicitCentralPackageVersionsTarget.SetAttributeValue(
+ "Condition", "'$(ManagePackageVersionsCentrally)' == 'true' and '$(ImplicitPackageReferences)' == 'true'"
+ );
+ implicitCentralPackageVersionsTarget.Add(propertyGroup.Clone());
+
+
+ var implicitTestingReferenceItemGroup = new XElement("ItemGroup");
+ implicitTestingReferenceItemGroup.SetAttributeValue(
+ "Condition", "'$(ManagePackageVersionsCentrally)' == 'true' and '$(ImplicitPackageReferences)' == 'true'"
+ );
+
+ var defaultPackageReference = new XElement("PackageReference");
+ defaultPackageReference.SetAttributeValue("Include", "Rocket.Surgery.Extensions.Testing");
+ implicitTestingReferenceItemGroup.Add(defaultPackageReference);
+ xProject.Add(implicitTestingReferenceItemGroup);
+
+ xProject.Add(implicitPackageReferencesTarget);
+ xProject.Add(implicitCentralPackageVersionsTarget);
+ targetsDoc.Add(xProject);
+ }
+ var propsDoc = new XDocument();
+ {
+ var xProject = new XElement("Project");
+ propsDoc.Add(xProject);
+ xProject.Add(xProperties);
+ {
+ var prop = new XElement("ImplicitPackageReferences");
+ xProperties.Add(prop);
+ prop.SetValue("true");
+ prop.SetAttributeValue("Condition", "'$(ImplicitPackageReferences)' == ''");
+ }
+ {
+ var prop = new XElement("ImplicitPackageReferenceWarning");
+ xProperties.Add(prop);
+ prop.SetValue("true");
+ prop.SetAttributeValue("Condition", "'$(ImplicitPackageReferenceWarning)' == ''");
+ }
+ }
+
+ var addedItems = new HashSet();
+ foreach (var project in projects)
+ {
+ var version = GitVersion.FullSemVer;
+
+ var conditionPropertyName = $"ImplicitPackageReference{project.AssemblyName.Replace(".", "")}";
+ if (!addedItems.Contains(project.AssemblyName))
+ {
+ var enabledProperty = new XElement(conditionPropertyName);
+ enabledProperty.SetValue("true");
+ enabledProperty.SetAttributeValue("Condition", $"'$({conditionPropertyName})' == ''");
+ xProperties.Add(enabledProperty);
+ }
+
+ var packageReferenceItemGroup = new XElement("ItemGroup");
+ var conditionBuilder = new StringBuilder();
+ conditionBuilder
+ .Append("'$(").Append(conditionPropertyName).Append(")' == 'true' ");
+ if (project.PackageReferences.Length > 0)
+ {
+ conditionBuilder
+ .Append("and ")
+ .AppendJoin(" and ", project.PackageReferences.Select(z => $"$(_rsgPackageReferenceList.Contains('{z.Name}'))"));
+ }
+
+ packageReferenceItemGroup.SetAttributeValue(
+ "Condition", conditionBuilder + $" and !$(_rsgPackageReferenceList.Contains('{project.AssemblyName}'))"
+ );
+
+ var packageReference = new XElement("PackageReference");
+ packageReferenceItemGroup.Add(packageReference);
+ packageReference.SetAttributeValue("Include", project.AssemblyName);
+ implicitPackageReferencesTarget.Add(packageReferenceItemGroup);
+
+ conditionBuilder.Append(" and !$(_rsgPackageVersionList.Contains('").Append(project.AssemblyName).Append("'))");
+
+ var packageVersionWarning = new XElement("Warning");
+ packageVersionWarning.SetAttributeValue("Condition", $"'$(ImplicitPackageReferenceWarning)' == 'true' and {conditionBuilder}");
+ packageVersionWarning.SetAttributeValue(
+ "Text",
+ $"PackageReference to {project.AssemblyName} has been added implicitly using default version {version}. Add or disable this warning with false. Use false to disable all implicit package references or <{conditionPropertyName}>false{conditionPropertyName}> to disable only this implicit reference."
+ );
+ var packageVersionItemGroup = new XElement("ItemGroup");
+ packageVersionItemGroup.SetAttributeValue("Condition", conditionBuilder);
+ var packageVersion = new XElement("PackageVersion");
+ packageVersion.SetAttributeValue("Include", project.AssemblyName);
+ packageVersion.SetAttributeValue("Version", version);
+ packageVersionItemGroup.Add(packageVersion);
+ implicitCentralPackageVersionsTarget.Add(packageVersionWarning);
+ implicitCentralPackageVersionsTarget.Add(packageVersionItemGroup);
+ addedItems.Add(project.AssemblyName);
+ }
+
+ // src/Testing/build/Rocket.Surgery.Extensions.Testing.props
+
+ propsDoc.Save(SourceDirectory / "Testing" / "Sdk" / "ImplicitPackageReferences.props");
+ targetsDoc.Save(SourceDirectory / "Testing" / "Sdk" / "ImplicitPackageReferences.targets");
+ }
+ );
+}
+
+public record GeneratorItem(string AssemblyName, ImmutableArray PackageReferences);
+
+public record PackageReferenceItem(string Name, NuGetVersion Version);
diff --git a/.build/Build.cs b/.build/Build.cs
index 110e14460d..b00a19bcda 100644
--- a/.build/Build.cs
+++ b/.build/Build.cs
@@ -6,68 +6,67 @@
using Nuke.Common.Tools.DotNet;
using Nuke.Common.Tools.GitVersion;
using Nuke.Common.Tools.MSBuild;
-using Rocket.Surgery.Nuke;
using Rocket.Surgery.Nuke.DotNetCore;
[PublicAPI]
[CheckBuildProjectConfigurations]
[UnsetVisualStudioEnvironmentVariables]
[PackageIcon("https://raw.githubusercontent.com/RocketSurgeonsGuild/graphics/master/png/social-square-thrust-rounded.png")]
-[EnsureGitHooks(GitHook.PreCommit)]
+//[EnsureGitHooks(GitHook.PreCommit)]
[EnsureReadmeIsUpdated("Readme.md")]
[DotNetVerbosityMapping]
[MSBuildVerbosityMapping]
[NuGetVerbosityMapping]
[ShutdownDotNetAfterServerBuild]
-public partial class Solution : NukeBuild,
- ICanRestoreWithDotNetCore,
- ICanBuildWithDotNetCore,
- ICanTestWithDotNetCore,
- ICanPackWithDotNetCore,
- IHaveDataCollector,
- ICanClean,
- ICanUpdateReadme,
- IGenerateCodeCoverageReport,
- IGenerateCodeCoverageSummary,
- IGenerateCodeCoverageBadges,
- IHaveConfiguration,
- ICanLint
+public partial class NukeSolution : NukeBuild,
+ ICanRestoreWithDotNetCore,
+ ICanBuildWithDotNetCore,
+ ICanTestWithDotNetCore,
+ ICanPackWithDotNetCore,
+ IHaveDataCollector,
+ ICanClean,
+ ICanUpdateReadme,
+ IGenerateCodeCoverageReport,
+ IGenerateCodeCoverageSummary,
+ IGenerateCodeCoverageBadges,
+ IHaveConfiguration
+
{
///
- /// Support plugins are available for:
- /// - JetBrains ReSharper https://nuke.build/resharper
- /// - JetBrains Rider https://nuke.build/rider
- /// - Microsoft VisualStudio https://nuke.build/visualstudio
- /// - Microsoft VSCode https://nuke.build/vscode
+ /// Support plugins are available for:
+ /// - JetBrains ReSharper https://nuke.build/resharper
+ /// - JetBrains Rider https://nuke.build/rider
+ /// - Microsoft VisualStudio https://nuke.build/visualstudio
+ /// - Microsoft VSCode https://nuke.build/vscode
///
- public static int Main() => Execute(x => x.Default);
+ public static int Main()
+ {
+ return Execute(x => x.Default);
+ }
- [OptionalGitRepository]
- public GitRepository? GitRepository { get; }
+ [OptionalGitRepository] public GitRepository? GitRepository { get; }
private Target Default => _ => _
- .DependsOn(Restore)
- .DependsOn(Build)
- .DependsOn(Test)
- .DependsOn(Pack);
+ .DependsOn(Restore)
+ .DependsOn(Build)
+ .DependsOn(Test)
+ .DependsOn(Pack);
public Target Build => _ => _.Inherit(x => x.CoreBuild);
public Target Pack => _ => _.Inherit(x => x.CorePack)
- .DependsOn(Clean);
+ .DependsOn(Clean)
+ .After(Test);
- [ComputedGitVersion]
- public GitVersion GitVersion { get; } = null!;
+ [ComputedGitVersion] public GitVersion GitVersion { get; } = null!;
public Target Clean => _ => _.Inherit(x => x.Clean);
- public Target Lint => _ => _.Inherit(x => x.Lint);
public Target Restore => _ => _.Inherit(x => x.CoreRestore);
public Target Test => _ => _.Inherit(x => x.CoreTest);
public Target BuildVersion => _ => _.Inherit(x => x.BuildVersion)
- .Before(Default)
- .Before(Clean);
+ .Before(Default)
+ .Before(Clean);
- [Parameter("Configuration to build")]
- public Configuration Configuration { get; } = IsLocalBuild ? Configuration.Debug : Configuration.Release;
-}
\ No newline at end of file
+ [Parameter("Configuration to build")] public Configuration Configuration { get; } = IsLocalBuild ? Configuration.Debug : Configuration.Release;
+}
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index 7948bb4113..0112380b7d 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -84,10 +84,6 @@ jobs:
uses: actions/setup-dotnet@v1.9.0
with:
dotnet-version: '3.1.x'
- - name: 🔨 Use .NET Core 5.0 SDK
- uses: actions/setup-dotnet@v1.9.0
- with:
- dotnet-version: '5.0.x'
- name: 🔨 Use .NET Core 6.0 SDK
uses: actions/setup-dotnet@v1.9.0
with:
@@ -98,6 +94,9 @@ jobs:
- name: 🎁 Restore
run: |
dotnet nuke Restore --skip
+ - name: Load Project Data
+ run: |
+ dotnet nuke LoadProjectData --skip
- name: ⚙ Build
run: |
dotnet nuke Build --skip
diff --git a/.gitignore b/.gitignore
index 8cbd569933..ec198b31be 100644
--- a/.gitignore
+++ b/.gitignore
@@ -210,4 +210,5 @@ codealike.json
.idea/
# mac OS
-.DS_STORE
\ No newline at end of file
+.DS_STORE
+/test/Testing.Tests/Fixtures/Directory.Packages.props
diff --git a/.husky/pre-commit b/.husky/pre-commit
index c32e97ffee..104008d081 100755
--- a/.husky/pre-commit
+++ b/.husky/pre-commit
@@ -2,3 +2,7 @@
. "$(dirname "$0")/_/husky.sh"
npx lint-staged -d -r
+dotnet nuke --generate-configuration GitHubActions_ci --host GitHubActions
+git add .github/workflows/ci.yml
+dotnet nuke --generate-configuration GitHubActions_ci-ignore --host GitHubActions
+git add .github/workflows/ci-ignore.yml
diff --git a/.nuke/build.schema.json b/.nuke/build.schema.json
index 6932e74056..63845730ae 100644
--- a/.nuke/build.schema.json
+++ b/.nuke/build.schema.json
@@ -94,7 +94,7 @@
"GenerateCodeCoverageReportCobertura",
"GenerateCodeCoverageSummary",
"GenerateReadme",
- "Lint",
+ "LoadProjectData",
"Pack",
"Restore",
"Test",
@@ -131,7 +131,7 @@
"GenerateCodeCoverageReportCobertura",
"GenerateCodeCoverageSummary",
"GenerateReadme",
- "Lint",
+ "LoadProjectData",
"Pack",
"Restore",
"Test",
diff --git a/Directory.Packages.props b/Directory.Packages.props
index a5d699a14a..bd30d9b81b 100644
--- a/Directory.Packages.props
+++ b/Directory.Packages.props
@@ -1,4 +1,4 @@
-
+
@@ -15,9 +15,13 @@
-
+
+
+
+
+
@@ -51,5 +55,8 @@
-
-
\ No newline at end of file
+
+
diff --git a/Readme.md b/Readme.md
index 305fface1c..c628ce0eba 100644
--- a/Readme.md
+++ b/Readme.md
@@ -19,6 +19,7 @@
| Package | NuGet |
| ------- | ----- |
| Rocket.Surgery.Extensions.Testing | [![nuget-version-ingbk+ngdt+w-badge]![nuget-downloads-ingbk+ngdt+w-badge]][nuget-ingbk+ngdt+w] |
+| Rocket.Surgery.Extensions.Testing.Analyzers | [![nuget-version-is2d/pp3l2nq-badge]![nuget-downloads-is2d/pp3l2nq-badge]][nuget-is2d/pp3l2nq] |
| Rocket.Surgery.Extensions.Testing.Coverlet | [![nuget-version-2jdbmqdcrhfg-badge]![nuget-downloads-2jdbmqdcrhfg-badge]][nuget-2jdbmqdcrhfg] |
| Rocket.Surgery.Extensions.Testing.FakeItEasy | [![nuget-version-6rnnzg4ixtvq-badge]![nuget-downloads-6rnnzg4ixtvq-badge]][nuget-6rnnzg4ixtvq] |
| Rocket.Surgery.Extensions.Testing.Fixtures | [![nuget-version-xegxxxxh/pzg-badge]![nuget-downloads-xegxxxxh/pzg-badge]][nuget-xegxxxxh/pzg] |
@@ -44,6 +45,9 @@ TBD
[nuget-ingbk+ngdt+w]: https://www.nuget.org/packages/Rocket.Surgery.Extensions.Testing/
[nuget-version-ingbk+ngdt+w-badge]: https://img.shields.io/nuget/v/Rocket.Surgery.Extensions.Testing.svg?color=004880&logo=nuget&style=flat-square "NuGet Version"
[nuget-downloads-ingbk+ngdt+w-badge]: https://img.shields.io/nuget/dt/Rocket.Surgery.Extensions.Testing.svg?color=004880&logo=nuget&style=flat-square "NuGet Downloads"
+[nuget-is2d/pp3l2nq]: https://www.nuget.org/packages/Rocket.Surgery.Extensions.Testing.Analyzers/
+[nuget-version-is2d/pp3l2nq-badge]: https://img.shields.io/nuget/v/Rocket.Surgery.Extensions.Testing.Analyzers.svg?color=004880&logo=nuget&style=flat-square "NuGet Version"
+[nuget-downloads-is2d/pp3l2nq-badge]: https://img.shields.io/nuget/dt/Rocket.Surgery.Extensions.Testing.Analyzers.svg?color=004880&logo=nuget&style=flat-square "NuGet Downloads"
[nuget-2jdbmqdcrhfg]: https://www.nuget.org/packages/Rocket.Surgery.Extensions.Testing.Coverlet/
[nuget-version-2jdbmqdcrhfg-badge]: https://img.shields.io/nuget/v/Rocket.Surgery.Extensions.Testing.Coverlet.svg?color=004880&logo=nuget&style=flat-square "NuGet Version"
[nuget-downloads-2jdbmqdcrhfg-badge]: https://img.shields.io/nuget/dt/Rocket.Surgery.Extensions.Testing.Coverlet.svg?color=004880&logo=nuget&style=flat-square "NuGet Downloads"
diff --git a/Testing.sln b/Testing.sln
index f82bd1b71e..79f852b7c8 100644
--- a/Testing.sln
+++ b/Testing.sln
@@ -39,6 +39,10 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Rocket.Surgery.Extensions.T
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Rocket.Surgery.Extensions.Testing.XUnit", "src\Testing.XUnit\Rocket.Surgery.Extensions.Testing.XUnit.csproj", "{88A55AFE-EDA6-410C-B576-8B960688D113}"
EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Rocket.Surgery.Extensions.Testing.Analyzers", "src\Analyzers\Rocket.Surgery.Extensions.Testing.Analyzers.csproj", "{CAB5BF83-775D-4457-ABC6-F9A74E230E96}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Analyzers.Tests", "test\Analyzers.Tests\Analyzers.Tests.csproj", "{DFB78690-0AC5-4B48-9A07-5D321CA4A865}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -167,6 +171,30 @@ Global
{88A55AFE-EDA6-410C-B576-8B960688D113}.Release|x64.Build.0 = Release|Any CPU
{88A55AFE-EDA6-410C-B576-8B960688D113}.Release|x86.ActiveCfg = Release|Any CPU
{88A55AFE-EDA6-410C-B576-8B960688D113}.Release|x86.Build.0 = Release|Any CPU
+ {CAB5BF83-775D-4457-ABC6-F9A74E230E96}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {CAB5BF83-775D-4457-ABC6-F9A74E230E96}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {CAB5BF83-775D-4457-ABC6-F9A74E230E96}.Debug|x64.ActiveCfg = Debug|Any CPU
+ {CAB5BF83-775D-4457-ABC6-F9A74E230E96}.Debug|x64.Build.0 = Debug|Any CPU
+ {CAB5BF83-775D-4457-ABC6-F9A74E230E96}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {CAB5BF83-775D-4457-ABC6-F9A74E230E96}.Debug|x86.Build.0 = Debug|Any CPU
+ {CAB5BF83-775D-4457-ABC6-F9A74E230E96}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {CAB5BF83-775D-4457-ABC6-F9A74E230E96}.Release|Any CPU.Build.0 = Release|Any CPU
+ {CAB5BF83-775D-4457-ABC6-F9A74E230E96}.Release|x64.ActiveCfg = Release|Any CPU
+ {CAB5BF83-775D-4457-ABC6-F9A74E230E96}.Release|x64.Build.0 = Release|Any CPU
+ {CAB5BF83-775D-4457-ABC6-F9A74E230E96}.Release|x86.ActiveCfg = Release|Any CPU
+ {CAB5BF83-775D-4457-ABC6-F9A74E230E96}.Release|x86.Build.0 = Release|Any CPU
+ {DFB78690-0AC5-4B48-9A07-5D321CA4A865}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {DFB78690-0AC5-4B48-9A07-5D321CA4A865}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {DFB78690-0AC5-4B48-9A07-5D321CA4A865}.Debug|x64.ActiveCfg = Debug|Any CPU
+ {DFB78690-0AC5-4B48-9A07-5D321CA4A865}.Debug|x64.Build.0 = Debug|Any CPU
+ {DFB78690-0AC5-4B48-9A07-5D321CA4A865}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {DFB78690-0AC5-4B48-9A07-5D321CA4A865}.Debug|x86.Build.0 = Debug|Any CPU
+ {DFB78690-0AC5-4B48-9A07-5D321CA4A865}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {DFB78690-0AC5-4B48-9A07-5D321CA4A865}.Release|Any CPU.Build.0 = Release|Any CPU
+ {DFB78690-0AC5-4B48-9A07-5D321CA4A865}.Release|x64.ActiveCfg = Release|Any CPU
+ {DFB78690-0AC5-4B48-9A07-5D321CA4A865}.Release|x64.Build.0 = Release|Any CPU
+ {DFB78690-0AC5-4B48-9A07-5D321CA4A865}.Release|x86.ActiveCfg = Release|Any CPU
+ {DFB78690-0AC5-4B48-9A07-5D321CA4A865}.Release|x86.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@@ -182,6 +210,8 @@ Global
{961EE399-F31D-40A9-9559-F9568BCF5088} = {8FFDF555-DB50-45F9-9A2D-6410F39151C3}
{AFEE7BD7-6EAE-41F7-B2F6-0B3EB04F5D4D} = {8FFDF555-DB50-45F9-9A2D-6410F39151C3}
{88A55AFE-EDA6-410C-B576-8B960688D113} = {8FFDF555-DB50-45F9-9A2D-6410F39151C3}
+ {CAB5BF83-775D-4457-ABC6-F9A74E230E96} = {8FFDF555-DB50-45F9-9A2D-6410F39151C3}
+ {DFB78690-0AC5-4B48-9A07-5D321CA4A865} = {DF33E0FB-9790-4654-B60F-8AB22E0CC3D1}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {439897C2-CCBD-44FE-B2DC-A3E4670ADA59}
diff --git a/src/Analyzers/MutableGenerator.cs b/src/Analyzers/MutableGenerator.cs
new file mode 100644
index 0000000000..f856bf0c10
--- /dev/null
+++ b/src/Analyzers/MutableGenerator.cs
@@ -0,0 +1,36 @@
+using Microsoft.CodeAnalysis;
+using Microsoft.CodeAnalysis.CSharp;
+
+namespace Rocket.Surgery.Extensions.Testing.Analyzers
+{
+ ///
+ /// Generator to convert an immutable type into a mutable one
+ ///
+ [Generator]
+ public class MutableGenerator : ISourceGenerator
+ {
+ ///
+ public void Initialize(GeneratorInitializationContext context)
+ {
+ context.RegisterForSyntaxNotifications(() => new SyntaxReceiver());
+ }
+
+ ///
+ public void Execute(GeneratorExecutionContext context)
+ {
+ if (!( context.SyntaxReceiver is SyntaxReceiver syntaxReceiver ))
+ {
+ return;
+ }
+
+ var compliation = ( context.Compilation as CSharpCompilation )!;
+ }
+
+ private class SyntaxReceiver : ISyntaxReceiver
+ {
+ public void OnVisitSyntaxNode(SyntaxNode syntaxNode)
+ {
+ }
+ }
+ }
+}
diff --git a/src/Analyzers/Rocket.Surgery.Extensions.Testing.Analyzers.csproj b/src/Analyzers/Rocket.Surgery.Extensions.Testing.Analyzers.csproj
new file mode 100644
index 0000000000..61f57eb1aa
--- /dev/null
+++ b/src/Analyzers/Rocket.Surgery.Extensions.Testing.Analyzers.csproj
@@ -0,0 +1,13 @@
+
+
+ netstandard2.0
+ true
+
+
+
+
+
+
+
+
+
diff --git a/src/Testing.FakeItEasy/Rocket.Surgery.Extensions.Testing.FakeItEasy.csproj b/src/Testing.FakeItEasy/Rocket.Surgery.Extensions.Testing.FakeItEasy.csproj
index 1303f8d35a..8494e4a9da 100644
--- a/src/Testing.FakeItEasy/Rocket.Surgery.Extensions.Testing.FakeItEasy.csproj
+++ b/src/Testing.FakeItEasy/Rocket.Surgery.Extensions.Testing.FakeItEasy.csproj
@@ -3,11 +3,12 @@
netstandard2.1;netstandard2.0;net6.0
false
Rocket.Surgery.Extensions.Testing
+ true
-
+
diff --git a/src/Testing.Moq/Rocket.Surgery.Extensions.Testing.Moq.csproj b/src/Testing.Moq/Rocket.Surgery.Extensions.Testing.Moq.csproj
index 7111131d28..fe5b00a6a7 100644
--- a/src/Testing.Moq/Rocket.Surgery.Extensions.Testing.Moq.csproj
+++ b/src/Testing.Moq/Rocket.Surgery.Extensions.Testing.Moq.csproj
@@ -3,6 +3,7 @@
netstandard2.1;netstandard2.0;net6.0
false
Rocket.Surgery.Extensions.Testing
+ true
diff --git a/src/Testing.NSubstitute/Rocket.Surgery.Extensions.Testing.NSubstitute.csproj b/src/Testing.NSubstitute/Rocket.Surgery.Extensions.Testing.NSubstitute.csproj
index e48d22ae47..d93c7f353f 100644
--- a/src/Testing.NSubstitute/Rocket.Surgery.Extensions.Testing.NSubstitute.csproj
+++ b/src/Testing.NSubstitute/Rocket.Surgery.Extensions.Testing.NSubstitute.csproj
@@ -4,11 +4,12 @@
netstandard2.1;netstandard2.0;net6.0
false
Rocket.Surgery.Extensions.Testing
+ true
-
+
diff --git a/src/Testing.XUnit/Rocket.Surgery.Extensions.Testing.XUnit.csproj b/src/Testing.XUnit/Rocket.Surgery.Extensions.Testing.XUnit.csproj
index 05e3710f25..9a455b7cea 100644
--- a/src/Testing.XUnit/Rocket.Surgery.Extensions.Testing.XUnit.csproj
+++ b/src/Testing.XUnit/Rocket.Surgery.Extensions.Testing.XUnit.csproj
@@ -3,6 +3,7 @@
netstandard2.1;netstandard2.0;net6.0
false
Rocket.Surgery.Extensions.Testing
+ true
diff --git a/src/Testing.XUnit/XUnitExtensions.cs b/src/Testing.XUnit/XUnitExtensions.cs
index 29e7ff2f17..9b5ffd07c4 100644
--- a/src/Testing.XUnit/XUnitExtensions.cs
+++ b/src/Testing.XUnit/XUnitExtensions.cs
@@ -2,7 +2,7 @@
using Xunit.Abstractions;
// ReSharper disable once CheckNamespace
-namespace xunit;
+namespace Xunit;
///
/// Helpful XUnit Extensions
@@ -20,4 +20,4 @@ public static ITest GetTest(this ITestOutputHelper output)
var testMember = type.GetField("test", BindingFlags.Instance | BindingFlags.NonPublic)!;
return (ITest)testMember.GetValue(output)!;
}
-}
\ No newline at end of file
+}
diff --git a/src/Testing/LoggerTest.cs b/src/Testing/LoggerTest.cs
index 3a1d07e873..d049e3255a 100644
--- a/src/Testing/LoggerTest.cs
+++ b/src/Testing/LoggerTest.cs
@@ -48,6 +48,8 @@ public abstract class LoggerTest : IDisposable
///
protected DiagnosticSource DiagnosticSource => _values.Value.diagnosticSource;
+ protected ITestOutputHelper TestOutputHelper { get; }
+
///
/// The
///
@@ -108,6 +110,7 @@ protected LoggerTest(
)
{
Disposables = new CompositeDisposable();
+ TestOutputHelper = outputHelper;
_values =
new Lazy<(IMsftLogger logger, ILoggerFactory loggerFactory, ISeriLogger serilogLogger, DiagnosticSource diagnosticSource, IObservable
diff --git a/src/Testing/Rocket.Surgery.Extensions.Testing.csproj b/src/Testing/Rocket.Surgery.Extensions.Testing.csproj
index 5adb781ecc..df00ccb17d 100644
--- a/src/Testing/Rocket.Surgery.Extensions.Testing.csproj
+++ b/src/Testing/Rocket.Surgery.Extensions.Testing.csproj
@@ -21,11 +21,18 @@
-
-
+
+
+
diff --git a/src/Testing/Sdk/ImplicitPackageReferences.props b/src/Testing/Sdk/ImplicitPackageReferences.props
new file mode 100644
index 0000000000..d8eedf282a
--- /dev/null
+++ b/src/Testing/Sdk/ImplicitPackageReferences.props
@@ -0,0 +1,22 @@
+
+
+
+ true
+ true
+ true
+ true
+ true
+ true
+ true
+
+
diff --git a/src/Testing/Sdk/ImplicitPackageReferences.targets b/src/Testing/Sdk/ImplicitPackageReferences.targets
new file mode 100644
index 0000000000..892be2d379
--- /dev/null
+++ b/src/Testing/Sdk/ImplicitPackageReferences.targets
@@ -0,0 +1,111 @@
+
+
+
+
+
+
+
+ <_rsgPackageReferenceList>@(PackageReference)
+ <_rsgPackageVersionList>@(PackageVersion)
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ <_rsgPackageReferenceList>@(PackageReference)
+ <_rsgPackageVersionList>@(PackageVersion)
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/Testing/Sdk/Sdk.props b/src/Testing/Sdk/Sdk.props
new file mode 100644
index 0000000000..8961ec6bb5
--- /dev/null
+++ b/src/Testing/Sdk/Sdk.props
@@ -0,0 +1,3 @@
+
+
+
diff --git a/src/Testing/Sdk/Sdk.targets b/src/Testing/Sdk/Sdk.targets
new file mode 100644
index 0000000000..34b28848d5
--- /dev/null
+++ b/src/Testing/Sdk/Sdk.targets
@@ -0,0 +1,3 @@
+
+
+
diff --git a/src/Testing/build/Rocket.Surgery.Extensions.Testing.props b/src/Testing/build/Rocket.Surgery.Extensions.Testing.props
index 542ab9da6b..5d204031f1 100644
--- a/src/Testing/build/Rocket.Surgery.Extensions.Testing.props
+++ b/src/Testing/build/Rocket.Surgery.Extensions.Testing.props
@@ -1,7 +1,11 @@
- [Bogus*]*,[Autofac*]*,[FakeItEasy*]*,[Moq*]*,[FluentValidation*]*,[Ben.Demystifier*]*,[Humanizer*]*,[xunit*]*,[Microsoft.*]*,[XunitXml*]*,[coverlet.*]*,[System.*]*,[*]JetBrains.Annotations*
- [Bogus*]*,[Autofac*]*,[FakeItEasy*]*,[Moq*]*,[FluentValidation*]*,[Ben.Demystifier*]*,[Humanizer*]*,[xunit*]*,[Microsoft.*]*,[XunitXml*]*,[coverlet.*]*,[System.*]*,[*]JetBrains.Annotations*,$(Exclude)
+ [Bogus*]*,[Autofac*]*,[FakeItEasy*]*,[Moq*]*,[FluentValidation*]*,[Ben.Demystifier*]*,[Humanizer*]*,[xunit*]*,[Microsoft.*]*,[XunitXml*]*,[coverlet.*]*,[System.*]*,[*]JetBrains.Annotations*
+ [Bogus*]*,[Autofac*]*,[FakeItEasy*]*,[Moq*]*,[FluentValidation*]*,[Ben.Demystifier*]*,[Humanizer*]*,[xunit*]*,[Microsoft.*]*,[XunitXml*]*,[coverlet.*]*,[System.*]*,[*]JetBrains.Annotations*,$(Exclude)
diff --git a/src/Testing/build/Rocket.Surgery.Extensions.Testing.targets b/src/Testing/build/Rocket.Surgery.Extensions.Testing.targets
index a139622d8f..f75adf7e4d 100644
--- a/src/Testing/build/Rocket.Surgery.Extensions.Testing.targets
+++ b/src/Testing/build/Rocket.Surgery.Extensions.Testing.targets
@@ -1,7 +1,2 @@
-
-
- PreserveNewest
-
-
diff --git a/test/Analyzers.Tests/Analyzers.Tests.csproj b/test/Analyzers.Tests/Analyzers.Tests.csproj
new file mode 100644
index 0000000000..27abccf247
--- /dev/null
+++ b/test/Analyzers.Tests/Analyzers.Tests.csproj
@@ -0,0 +1,16 @@
+
+
+ net6.0
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/test/Analyzers.Tests/Helpers/CollectibleTestAssemblyLoadContext.cs b/test/Analyzers.Tests/Helpers/CollectibleTestAssemblyLoadContext.cs
new file mode 100644
index 0000000000..4af85bca76
--- /dev/null
+++ b/test/Analyzers.Tests/Helpers/CollectibleTestAssemblyLoadContext.cs
@@ -0,0 +1,19 @@
+using System.Reflection;
+using System.Runtime.Loader;
+
+namespace Analyzers.Tests.Helpers;
+
+internal class CollectibleTestAssemblyLoadContext : AssemblyLoadContext, IDisposable
+{
+ protected override Assembly? Load(AssemblyName assemblyName)
+ {
+ return null;
+ }
+
+ public void Dispose()
+ {
+#if NETCOREAPP3_1
+ Unload();
+#endif
+ }
+}
diff --git a/test/Analyzers.Tests/Helpers/GenerationHelpers.cs b/test/Analyzers.Tests/Helpers/GenerationHelpers.cs
new file mode 100644
index 0000000000..b1e807bc4a
--- /dev/null
+++ b/test/Analyzers.Tests/Helpers/GenerationHelpers.cs
@@ -0,0 +1,57 @@
+using Microsoft.CodeAnalysis;
+using Microsoft.CodeAnalysis.CSharp;
+using Microsoft.CodeAnalysis.Text;
+
+namespace Analyzers.Tests.Helpers;
+
+public static class GenerationHelpers
+{
+ internal const string CrLf = "\r\n";
+ internal const string Lf = "\n";
+ internal const string DefaultFilePathPrefix = "Test";
+ internal const string CSharpDefaultFileExt = "cs";
+ internal const string TestProjectName = "TestProject";
+
+ public static Project CreateProject(
+ string projectName,
+ IEnumerable metadataReferences,
+ params SourceText[] sources
+ )
+ {
+ var projectId = ProjectId.CreateNewId(projectName);
+ var solution = new AdhocWorkspace()
+ .CurrentSolution
+ .AddProject(projectId, projectName, projectName, LanguageNames.CSharp)
+ .WithProjectCompilationOptions(
+ projectId,
+ new CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary)
+ )
+ .WithProjectParseOptions(
+ projectId,
+ new CSharpParseOptions(preprocessorSymbols: new[] { "SOMETHING_ACTIVE" })
+ )
+ .AddMetadataReferences(projectId, metadataReferences);
+
+ var count = 0;
+ foreach (var source in sources)
+ {
+ var newFileName = DefaultFilePathPrefix + count + "." + CSharpDefaultFileExt;
+ var documentId = DocumentId.CreateNewId(projectId, newFileName);
+ solution = solution.AddDocument(documentId, newFileName, source);
+ count++;
+ }
+
+ var project = solution.GetProject(projectId);
+ if (project is null)
+ {
+ throw new InvalidOperationException($"The ad hoc workspace does not contain a project with the id {projectId.Id}");
+ }
+
+ return project;
+ }
+
+ public static Project CreateProject(IEnumerable metadataReferences, params SourceText[] sources)
+ {
+ return CreateProject(TestProjectName, metadataReferences, sources);
+ }
+}
diff --git a/test/Analyzers.Tests/Helpers/GenerationTestResult.cs b/test/Analyzers.Tests/Helpers/GenerationTestResult.cs
new file mode 100644
index 0000000000..76ebf559d6
--- /dev/null
+++ b/test/Analyzers.Tests/Helpers/GenerationTestResult.cs
@@ -0,0 +1,61 @@
+using System.Collections.Immutable;
+using FluentAssertions;
+using Microsoft.CodeAnalysis;
+using Microsoft.CodeAnalysis.CSharp;
+using Microsoft.Extensions.Logging;
+using Xunit;
+using Xunit.Sdk;
+
+namespace Analyzers.Tests.Helpers;
+
+public record GenerationTestResult(
+ CSharpCompilation Compilation,
+ ImmutableArray Diagnostics,
+ ImmutableArray SyntaxTrees,
+ ILogger Logger
+)
+{
+ public void EnsureDiagnosticSeverity(DiagnosticSeverity severity = DiagnosticSeverity.Warning)
+ {
+ Diagnostics.Where(x => x.Severity >= severity).Should().HaveCount(0);
+ }
+
+ public void AssertGeneratedAsExpected(string expectedValue, params string[] expectedValues)
+ {
+ // normalize line endings to just LF
+ var generatedText = SyntaxTrees.Select(z => NormalizeToLf(z.GetText().ToString()).Trim()).ToArray();
+ // and append preamble to the expected
+ var expectedText = new[] { expectedValue }.Concat(expectedValues).Select(z => NormalizeToLf(z).Trim()).ToArray();
+
+ generatedText.Should().HaveCount(expectedText.Length);
+ foreach (var (generated, expectedTxt) in generatedText.Zip(expectedText, (generated, expected) => ( generated, expected )))
+ {
+ try
+ {
+ generated.Should().Be(expectedTxt);
+ }
+ catch
+ {
+ try
+ {
+ Assert.Equal(generated, expectedTxt);
+ }
+ catch (EqualException e2)
+ {
+ Logger.LogCritical(e2.Message);
+ }
+ catch
+ {
+ // ignore
+ }
+
+ throw;
+ }
+ }
+ }
+
+ public static string NormalizeToLf(string input)
+ {
+ return input.Replace(GenerationHelpers.CrLf, GenerationHelpers.Lf);
+ }
+}
diff --git a/test/Analyzers.Tests/Helpers/GenerationTestResults.cs b/test/Analyzers.Tests/Helpers/GenerationTestResults.cs
new file mode 100644
index 0000000000..7c68a7a18a
--- /dev/null
+++ b/test/Analyzers.Tests/Helpers/GenerationTestResults.cs
@@ -0,0 +1,84 @@
+using System.Collections.Immutable;
+using System.Diagnostics.CodeAnalysis;
+using System.Reflection;
+using Microsoft.CodeAnalysis;
+using Microsoft.CodeAnalysis.CSharp;
+using Xunit;
+
+namespace Analyzers.Tests.Helpers;
+
+public record GenerationTestResults(
+ CSharpCompilation InputCompilation,
+ ImmutableArray InputDiagnostics,
+ ImmutableArray InputSyntaxTrees,
+ ImmutableDictionary Results,
+ CSharpCompilation FinalCompilation,
+ ImmutableArray FinalDiagnostics,
+ Assembly? Assembly
+)
+{
+ public bool TryGetResult(Type type, [NotNullWhen(true)] out GenerationTestResult? result)
+ {
+ return Results.TryGetValue(type, out result);
+ }
+
+ public bool TryGetResult([NotNullWhen(true)] out GenerationTestResult? result)
+ where T : new()
+ {
+ return Results.TryGetValue(typeof(T), out result);
+ }
+
+ public void EnsureDiagnosticSeverity(DiagnosticSeverity severity = DiagnosticSeverity.Warning)
+ {
+ Assert.Empty(InputDiagnostics.Where(x => x.Severity >= severity));
+ foreach (var result in Results.Values)
+ {
+ result.EnsureDiagnosticSeverity(severity);
+ }
+ }
+
+ public void AssertGeneratedAsExpected(string expectedValue, params string[] expectedValues)
+ where T : new()
+ {
+ if (!TryGetResult(out var result))
+ {
+ Assert.NotNull(result);
+ return;
+ }
+
+ result.AssertGeneratedAsExpected(expectedValue);
+ }
+
+ public void AssertCompilationWasSuccessful()
+ {
+ Assert.Empty(
+ InputDiagnostics
+ .Where(z => !z.GetMessage().Contains("does not contain a definition for"))
+ .Where(z => !z.GetMessage().Contains("Assuming assembly reference"))
+ .Where(x => x.Severity >= DiagnosticSeverity.Warning)
+ );
+ foreach (var result in Results.Values)
+ {
+ result.EnsureDiagnosticSeverity();
+ }
+ }
+
+ public void AssertGenerationWasSuccessful()
+ {
+ foreach (var item in Results.Values)
+ {
+ Assert.NotNull(item.Compilation);
+ item.EnsureDiagnosticSeverity();
+ }
+ }
+
+ public static implicit operator CSharpCompilation(GenerationTestResults results)
+ {
+ return results.FinalCompilation;
+ }
+
+ public static implicit operator Assembly?(GenerationTestResults results)
+ {
+ return results.Assembly;
+ }
+}
diff --git a/test/Analyzers.Tests/Helpers/GeneratorTest.cs b/test/Analyzers.Tests/Helpers/GeneratorTest.cs
new file mode 100644
index 0000000000..52e8050fc1
--- /dev/null
+++ b/test/Analyzers.Tests/Helpers/GeneratorTest.cs
@@ -0,0 +1,370 @@
+using System.Collections.Immutable;
+using System.Reflection;
+using System.Runtime.Loader;
+using System.Text;
+using Castle.Core;
+using Microsoft.CodeAnalysis;
+using Microsoft.CodeAnalysis.CSharp;
+using Microsoft.CodeAnalysis.Diagnostics;
+using Microsoft.CodeAnalysis.Emit;
+using Microsoft.CodeAnalysis.Text;
+using Microsoft.Extensions.DependencyInjection;
+using Microsoft.Extensions.Logging;
+using Rocket.Surgery.Extensions.Testing;
+using Xunit;
+using Xunit.Abstractions;
+
+namespace Analyzers.Tests.Helpers;
+
+public static class Helpers
+{
+ public static Assembly EmitInto(this CSharpCompilation compilation, AssemblyLoadContext context, string? outputName = null)
+ {
+ using var stream = new MemoryStream();
+ var emitResult = compilation.Emit(stream, options: new EmitOptions(outputNameOverride: outputName));
+ if (!emitResult.Success)
+ {
+ Assert.Empty(emitResult.Diagnostics);
+ }
+
+ var data = stream.ToArray();
+
+ using var assemblyStream = new MemoryStream(data);
+ return context.LoadFromStream(assemblyStream);
+ }
+
+ public static MetadataReference CreateMetadataReference(this CSharpCompilation compilation)
+ {
+ using var stream = new MemoryStream();
+ var emitResult = compilation.Emit(stream, options: new EmitOptions(outputNameOverride: compilation.AssemblyName));
+ if (!emitResult.Success)
+ {
+ Assert.Empty(emitResult.Diagnostics);
+ }
+
+ var data = stream.ToArray();
+
+ using var assemblyStream = new MemoryStream(data);
+ return MetadataReference.CreateFromStream(assemblyStream, MetadataReferenceProperties.Assembly);
+ }
+}
+
+public abstract class GeneratorTest : GeneratorTester
+{
+ protected GeneratorTest(ITestOutputHelper testOutputHelper, LogLevel minLevel) : base(
+ GenerationHelpers.TestProjectName,
+ new CollectibleTestAssemblyLoadContext(),
+ testOutputHelper,
+ minLevel
+ )
+ {
+ Disposables.Add(( AssemblyLoadContext as IDisposable )!);
+ }
+}
+
+public class GeneratorTester : LoggerTest
+{
+ private readonly string _projectName;
+ protected AssemblyLoadContext AssemblyLoadContext { get; }
+ private readonly HashSet _metadataReferences = new(ReferenceEqualityComparer.Instance);
+ private readonly HashSet _generators = new();
+ private readonly List _sources = new();
+ private GenerationTestResults? _lastResult;
+ private readonly HashSet _ignoredFilePaths = new();
+ private readonly OptionsProvider _optionsProvider = new OptionsProvider();
+
+ public GeneratorTester(
+ string projectName, AssemblyLoadContext assemblyLoadContext, ITestOutputHelper testOutputHelper, LogLevel minLevel = LogLevel.Trace
+ ) : base(
+ testOutputHelper,
+ minLevel
+ )
+ {
+ _projectName = projectName;
+ AssemblyLoadContext = assemblyLoadContext;
+ AddReferences(
+ "mscorlib.dll",
+ "netstandard.dll",
+ "System.dll",
+ "System.Core.dll",
+#if NETCOREAPP
+ "System.Private.CoreLib.dll",
+#endif
+ "System.Runtime.dll"
+ );
+
+ AddReferences(
+ typeof(ActivatorUtilities).Assembly,
+ typeof(IServiceProvider).Assembly,
+ typeof(IServiceCollection).Assembly,
+ typeof(ServiceCollection).Assembly
+ );
+ }
+
+ public GeneratorTester AddCompilationReference(params CSharpCompilation[] additionalCompilations)
+ {
+ AddReferences(additionalCompilations.Select(z => z.CreateMetadataReference()).ToArray());
+ return this;
+ }
+
+ public GeneratorTester IgnoreOutputFile(string path)
+ {
+ _ignoredFilePaths.Add(path);
+ return this;
+ }
+
+ public GeneratorTester WithGenerator(Type type)
+ {
+ _generators.Add(type);
+ return this;
+ }
+
+ public GeneratorTester WithGenerator()
+ where T : new()
+ {
+ _generators.Add(typeof(T));
+ return this;
+ }
+
+ public GeneratorTester AddReferences(params string[] coreAssemblyNames)
+ {
+ // this "core assemblies hack" is from https://stackoverflow.com/a/47196516/4418060
+ var coreAssemblyPath = Path.GetDirectoryName(typeof(object).Assembly.Location)!;
+
+ foreach (var name in coreAssemblyNames)
+ {
+ _metadataReferences.Add(MetadataReference.CreateFromFile(Path.Combine(coreAssemblyPath, name)));
+ }
+
+ return this;
+ }
+
+ public GeneratorTester AddReferences(params MetadataReference[] references)
+ {
+ foreach (var metadataReference in references)
+ {
+ _metadataReferences.Add(metadataReference);
+ }
+
+ return this;
+ }
+
+ public GeneratorTester AddReferences(params Type[] references)
+ {
+ foreach (var type in references)
+ {
+ _metadataReferences.Add(MetadataReference.CreateFromFile(type.Assembly.Location));
+ }
+
+ return this;
+ }
+
+ public GeneratorTester AddReferences(params Assembly[] references)
+ {
+ foreach (var type in references)
+ {
+ _metadataReferences.Add(MetadataReference.CreateFromFile(type.Location));
+ }
+
+ return this;
+ }
+
+ public GeneratorTester AddSources(params SourceText[] additionalSources)
+ {
+ _sources.AddRange(additionalSources);
+ return this;
+ }
+
+ public GeneratorTester AddSources(params string[] additionalSources)
+ {
+ _sources.AddRange(additionalSources.Select(s => SourceText.From(s, Encoding.UTF8)));
+ return this;
+ }
+
+ public GeneratorTester AddGlobalOption(string key, string value)
+ {
+ _optionsProvider.AddGlobalOption(key, value);
+ return this;
+ }
+
+ public GeneratorTester AddOption(SyntaxTree tree, string value)
+ {
+ _optionsProvider.AddOption(tree, value);
+ return this;
+ }
+
+ public GeneratorTester AddOption(AdditionalText key, string value)
+ {
+ _optionsProvider.AddOption(key, value);
+ return this;
+ }
+
+ public CSharpCompilation Compile()
+ {
+ return GenerateAsync().ConfigureAwait(false).GetAwaiter().GetResult().FinalCompilation;
+ }
+
+ private Assembly? Emit(CSharpCompilation compilation, string? outputName = null)
+ {
+ using var stream = new MemoryStream();
+ var emitResult = compilation.Emit(
+ stream,
+ options: new EmitOptions(outputNameOverride: outputName)
+ );
+ if (!emitResult.Success)
+ {
+ return null;
+ }
+
+ var data = stream.ToArray();
+
+ using var assemblyStream = new MemoryStream(data);
+ return AssemblyLoadContext.LoadFromStream(assemblyStream);
+ }
+
+ public async Task GenerateAsync()
+ {
+ Logger.LogInformation("Starting Generation for {SourceCount}", _sources.Count);
+ if (Logger.IsEnabled(LogLevel.Trace))
+ {
+ Logger.LogTrace("--- References --- {Count}", _sources.Count);
+ foreach (var reference in _metadataReferences)
+ Logger.LogTrace(" Reference: {Name}", reference.Display);
+ }
+
+ var project = GenerationHelpers.CreateProject(_projectName, _metadataReferences, _sources.ToArray());
+
+ var compilation = (CSharpCompilation?)await project.GetCompilationAsync().ConfigureAwait(false);
+ if (compilation is null)
+ {
+ throw new InvalidOperationException("Could not compile the sources");
+ }
+
+ var diagnostics = compilation.GetDiagnostics();
+ if (Logger.IsEnabled(LogLevel.Trace) && diagnostics is { Length: > 0 })
+ {
+ Logger.LogTrace("--- Input Diagnostics --- {Count}", _sources.Count);
+ foreach (var d in diagnostics)
+ Logger.LogTrace(" Reference: {Name}", d.ToString());
+ }
+
+ var results = new GenerationTestResults(
+ compilation,
+ diagnostics,
+ compilation.SyntaxTrees,
+ ImmutableDictionary.Empty,
+ null!,
+ ImmutableArray.Empty,
+ null!
+ );
+
+ var builder = ImmutableDictionary.Empty.ToBuilder();
+
+ var inputCompilation = compilation;
+
+ foreach (var generatorType in _generators)
+ {
+ Logger.LogInformation("--- {Generator} ---", generatorType.FullName);
+ var generator = ( Activator.CreateInstance(generatorType) as IIncrementalGenerator )!;
+ var driver = CSharpGeneratorDriver.Create(generator).WithUpdatedParseOptions(new CSharpParseOptions());
+
+ driver.RunGeneratorsAndUpdateCompilation(compilation, out var outputCompilation, out diagnostics);
+
+ if (Logger.IsEnabled(LogLevel.Trace) && diagnostics is { Length: > 0 })
+ {
+ Logger.LogTrace("--- Diagnostics --- {Count}", _sources.Count);
+ foreach (var d in diagnostics)
+ Logger.LogTrace(" Reference: {Name}", d.ToString());
+ }
+
+ var trees = outputCompilation.SyntaxTrees
+ .Except(compilation.SyntaxTrees)
+ .ToImmutableArray();
+ if (Logger.IsEnabled(LogLevel.Trace) && trees is { Length: > 0 })
+ {
+ Logger.LogTrace("--- Syntax Trees --- {Count}", _sources.Count);
+ foreach (var t in trees)
+ {
+ Logger.LogTrace(" FilePath: {Name}", t.FilePath);
+ Logger.LogTrace(" Source:\n{Name}", ( await t.GetTextAsync().ConfigureAwait(false) ).ToString());
+ }
+ }
+
+ inputCompilation = inputCompilation.AddSyntaxTrees(trees);
+
+ builder.Add(
+ generatorType,
+ new GenerationTestResult(
+ ( outputCompilation as CSharpCompilation )!,
+ diagnostics,
+ trees.Where(z => !_ignoredFilePaths.Any(x => z.FilePath.Contains(x))).ToImmutableArray(),
+ Logger
+ )
+ );
+ }
+
+ results = results with
+ {
+ FinalCompilation = inputCompilation,
+ FinalDiagnostics = inputCompilation.GetDiagnostics(),
+ Assembly = Emit(inputCompilation)
+ };
+
+ return _lastResult = results with { Results = builder.ToImmutable() };
+ }
+
+ private class OptionsProvider : AnalyzerConfigOptionsProvider
+ {
+ // ReSharper disable once CollectionNeverQueried.Local
+ private readonly Dictionary _options = new();
+ private readonly Dictionary _globalOptions;
+
+ public OptionsProvider()
+ {
+ _globalOptions = new();
+ GlobalOptions = new Options(_globalOptions);
+ }
+
+ public override AnalyzerConfigOptions GetOptions(SyntaxTree tree)
+ {
+ throw new NotImplementedException();
+ }
+
+ public override AnalyzerConfigOptions GetOptions(AdditionalText textFile)
+ {
+ throw new NotImplementedException();
+ }
+
+ public override AnalyzerConfigOptions GlobalOptions { get; }
+
+ public void AddOption(SyntaxTree tree, string value)
+ {
+ _options.Add(tree.FilePath, value);
+ }
+
+ public void AddOption(AdditionalText tree, string value)
+ {
+ _options.Add(tree.Path, value);
+ }
+
+ public void AddGlobalOption(string key, string value)
+ {
+ _globalOptions.Add(key, value);
+ }
+ }
+
+ private class Options : AnalyzerConfigOptions
+ {
+ private readonly IReadOnlyDictionary _options;
+
+ public Options(IReadOnlyDictionary options)
+ {
+ _options = options;
+ }
+
+ public override bool TryGetValue(string key, out string value)
+ {
+ return _options.TryGetValue(key, out value!);
+ }
+ }
+}
diff --git a/test/Analyzers.Tests/ViewModelGeneratorTests.cs b/test/Analyzers.Tests/ViewModelGeneratorTests.cs
new file mode 100644
index 0000000000..a5c52abde7
--- /dev/null
+++ b/test/Analyzers.Tests/ViewModelGeneratorTests.cs
@@ -0,0 +1,46 @@
+//using FluentAssertions;
+//using JetBrains.Annotations;
+//using Microsoft.Extensions.Logging;
+//using Analyzers.Tests.Helpers;
+//using Rocket.Surgery.Extensions.Testing.Analyzers;
+//using Xunit;
+//using Xunit.Abstractions;
+//
+//namespace Analyzers.Tests
+//{
+// public class InheritFromGeneratorTests : GeneratorTest
+// {
+// public InheritFromGeneratorTests([NotNull] ITestOutputHelper testOutputHelper) : base(testOutputHelper, LogLevel.Trace)
+// {
+// WithGenerator();
+// }
+//
+// [Fact]
+// public async Task Should_Add_Sources_For_XUnit()
+// {
+// AddReferences(typeof(FactAttribute), typeof(Xunit.Abstractions.ITest));
+//
+// var result = await GenerateAsync();
+// result.TryGetResult(out var output).Should().BeTrue();
+// result.EnsureDiagnosticSeverity();
+// output.SyntaxTrees.Should().HaveCount(2);
+// output.SyntaxTrees.Should().Contain(z => z.FilePath.EndsWith("Marker_Rocket.Surgery.Extensions.Testing.XUnit.cs"))
+// .And.Contain(z => z.FilePath.EndsWith("Rocket.Surgery.Extensions.Testing.XUnit_XUnitExtensions.cs"));
+// }
+//
+// [Fact]
+// public async Task Should_Add_Sources_For_XUnit_Once()
+// {
+// AddReferences(typeof(FactAttribute), typeof(Xunit.Abstractions.ITest));
+// AddCompilationReference(new GeneratorTester("TestA", AssemblyLoadContext, TestOutputHelper).AddReferences(typeof(FactAttribute), typeof(Xunit.Abstractions.ITest)).WithGenerator().Compile());
+//
+// var result = await GenerateAsync();
+// result.TryGetResult(out var output).Should().BeTrue();
+// result.EnsureDiagnosticSeverity();
+// output.SyntaxTrees.Should().HaveCount(0);
+// }
+//
+// }
+//}
+
+
diff --git a/test/Directory.Build.props b/test/Directory.Build.props
index 0a5dc14ebc..18604500ef 100644
--- a/test/Directory.Build.props
+++ b/test/Directory.Build.props
@@ -1,7 +1,19 @@
-
+
+ [Bogus*]*,[Autofac*]*,[FakeItEasy*]*,[Moq*]*,[FluentValidation*]*,[Ben.Demystifier*]*,[Humanizer*]*,[xunit*]*,[Microsoft.*]*,[XunitXml*]*,[coverlet.*]*,[System.*]*,[*]JetBrains.Annotations*
+ [Bogus*]*,[Autofac*]*,[FakeItEasy*]*,[Moq*]*,[FluentValidation*]*,[Ben.Demystifier*]*,[Humanizer*]*,[xunit*]*,[Microsoft.*]*,[XunitXml*]*,[coverlet.*]*,[System.*]*,[*]JetBrains.Annotations*,$(Exclude)
+
+
+
+ false
+ true
+
true
true
diff --git a/test/Directory.Build.targets b/test/Directory.Build.targets
index 7e160b5442..e2529eda15 100644
--- a/test/Directory.Build.targets
+++ b/test/Directory.Build.targets
@@ -12,7 +12,6 @@
-
diff --git a/test/Testing.Tests/Fixtures/Includes_XUnit_Implictly/FakeXUnit.csproj b/test/Testing.Tests/Fixtures/Includes_XUnit_Implictly/FakeXUnit.csproj
new file mode 100644
index 0000000000..e2acc5d106
--- /dev/null
+++ b/test/Testing.Tests/Fixtures/Includes_XUnit_Implictly/FakeXUnit.csproj
@@ -0,0 +1,15 @@
+
+
+
+ net6.0
+ enable
+ enable
+ false
+
+
+
+
+
+
+
+
diff --git a/test/Testing.Tests/Fixtures/NuGet.config b/test/Testing.Tests/Fixtures/NuGet.config
new file mode 100644
index 0000000000..ba215df6f8
--- /dev/null
+++ b/test/Testing.Tests/Fixtures/NuGet.config
@@ -0,0 +1,17 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/test/Testing.Tests/Rocket.Surgery.Extensions.Testing.Tests.csproj b/test/Testing.Tests/Rocket.Surgery.Extensions.Testing.Tests.csproj
index 814109224f..94bacab2b1 100644
--- a/test/Testing.Tests/Rocket.Surgery.Extensions.Testing.Tests.csproj
+++ b/test/Testing.Tests/Rocket.Surgery.Extensions.Testing.Tests.csproj
@@ -4,10 +4,15 @@
+
+
+
+
+
diff --git a/test/Testing.Tests/XUnitExtensionsTests.cs b/test/Testing.Tests/XUnitExtensionsTests.cs
index 7196c4f44c..f456f4c80f 100644
--- a/test/Testing.Tests/XUnitExtensionsTests.cs
+++ b/test/Testing.Tests/XUnitExtensionsTests.cs
@@ -1,5 +1,4 @@
using FluentAssertions;
-using xunit;
using Xunit;
using Xunit.Abstractions;
@@ -21,4 +20,4 @@ public void GetTestTest()
test.Should().NotBeNull();
test.DisplayName.Should().EndWith("GetTestTest");
}
-}
\ No newline at end of file
+}