Skip to content

Commit

Permalink
Addressing issue with the generated code
Browse files Browse the repository at this point in the history
  • Loading branch information
Keboo committed Mar 19, 2024
1 parent 69a744c commit 90288ca
Show file tree
Hide file tree
Showing 7 changed files with 121 additions and 10 deletions.
4 changes: 4 additions & 0 deletions .editorconfig
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,10 @@ indent_style = space
[*.xml]
indent_size = 2

# MSBuild files
[*.{csproj,targets,props}]
indent_size = 2

# C# files
[*.cs]

Expand Down
15 changes: 12 additions & 3 deletions Moq.AutoMocker.Generators.Tests/CSharpSourceGeneratorVerifier.cs
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
using System.Collections.Immutable;
using System.Collections.Immutable;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Testing;
using Microsoft.CodeAnalysis.Testing.Verifiers;
using Microsoft.Extensions.Options;

namespace Moq.AutoMocker.Generators.Tests;

Expand All @@ -12,14 +13,22 @@ public static class CSharpSourceGeneratorVerifier<TSourceGenerator>
public class Test : CSharpSourceGeneratorTest<TSourceGenerator, MSTestVerifier>
{
public bool ReferenceAutoMocker { get; set; } = true;
public bool ReferenceOptionsAbstractions { get; set; }

protected override Project ApplyCompilationOptions(Project project)
{
if (ReferenceAutoMocker)
//project.AnalyzerOptions.WithAdditionalFiles();
if (ReferenceAutoMocker || ReferenceOptionsAbstractions)
{
string fullPath = Path.GetFullPath($"{AutoMock.AssemblyName}.dll");
project = project.AddMetadataReference(MetadataReference.CreateFromFile(fullPath));
}

if (ReferenceOptionsAbstractions)
{
project = project.AddMetadataReference(MetadataReference.CreateFromFile(typeof(IOptions<>).Assembly.Location));
}

return base.ApplyCompilationOptions(project);
}

Expand All @@ -34,7 +43,7 @@ protected override CompilationOptions CreateCompilationOptions()

private static ImmutableDictionary<string, ReportDiagnostic> GetNullableWarningsFromCompiler()
{
string[] args = { "/warnaserror:nullable" };
string[] args = ["/warnaserror:nullable"];
var commandLineArguments = CSharpCommandLineParser.Default.Parse(args, baseDirectory: Environment.CurrentDirectory, sdkDirectory: Environment.CurrentDirectory);
var nullableWarnings = commandLineArguments.CompilationOptions.SpecificDiagnosticOptions;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
</PropertyGroup>

<ItemGroup>
<PackageReference Include="Microsoft.Extensions.Options" />
<PackageReference Include="Microsoft.NET.Test.Sdk" />
<PackageReference Include="MSTest.TestAdapter" />
<PackageReference Include="MSTest.TestFramework" />
Expand Down
89 changes: 89 additions & 0 deletions Moq.AutoMocker.Generators.Tests/OptionsGeneratorTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
using Microsoft.CodeAnalysis.Text;
using System.Text;
using Microsoft.VisualStudio.TestTools.UnitTesting;

using VerifyCS = Moq.AutoMocker.Generators.Tests.CSharpSourceGeneratorVerifier<Moq.AutoMocker.Generators.OptionsExtensionSourceGenerator>;
using Microsoft.CodeAnalysis.Testing;


namespace Moq.AutoMocker.Generators.Tests;

[TestClass]
public class OptionsGeneratorTests
{
private const string ExpectedOptionsGeneratedFile = """
// Generated by Moq.AutoMocker.Generators.OptionsExtensionSourceGenerator.
// Licensed under the MIT License. See LICENSE in the project root for license information.
namespace Moq.AutoMock
{
using Microsoft.Extensions.Options;

/// <summary>
/// This class provides extension methods for interacting with IOptions in an <see cref="AutoMocker"/> instance.
/// </summary>
public static class AutoMockerOptionsExtensions
{
/// <summary>
/// This method sets up <see cref="AutoMocker"/> with various option related services for Microsoft's Option pattern, and allows their interception and manipulation in testing scenarios.
/// </summary>
/// <param name="mocker">The <see cref="AutoMocker"/> instance.</param>
/// <param name="configure">A delegate that can be used to configure an option instance of type TClass.</param>
/// <typeparam name="TClass">The type of Options being configured.</typeparam>
/// <returns>The same <see cref="AutoMocker"/> instance passed as parameter, allowing chained calls.</returns>
public static AutoMocker WithOptions<TClass>(this AutoMocker mocker, Action<TClass>? configure = null)
where TClass : class, new()
{
if (mocker == null)
{
throw new ArgumentNullException(nameof(mocker));
}

mocker.Use<IEnumerable<IConfigureOptions<TClass>>>(new[] { new ConfigureOptions<TClass>(configure) });
mocker.With<IOptionsMonitorCache<TClass>, OptionsCache<TClass>>();
mocker.With<IOptionsFactory<TClass>, OptionsFactory<TClass>>();
mocker.With<IOptionsMonitor<TClass>, OptionsMonitor<TClass>>();
mocker.With<IOptionsSnapshot<TClass>, OptionsManager<TClass>>();
TClass options = mocker.Get<IOptionsFactory<TClass>>().Create(string.Empty);
mocker.Use(Options.Create(options));
mocker.Use(options);
return mocker;
}
}
}
""";

[TestMethod]
public async Task WhenOptionsAbstractionAssemblyIsNotReferenced_NoGenerationOccurs()
{
await new VerifyCS.Test
{
TestCode = "",
ReferenceOptionsAbstractions = false

}.RunAsync();
}

[TestMethod]
public async Task WhenOptionsAbstractionAssemblyIsReferenced_ExtensionMethodIsGenerated()
{
await new VerifyCS.Test
{
CompilerDiagnostics = CompilerDiagnostics.None,
ReferenceOptionsAbstractions = true,
TestCode = "",
TestState =
{
GeneratedSources =
{
GetSourceFile(ExpectedOptionsGeneratedFile, "AutoMockerOptionsExtensions.cs")
}
}

}.RunAsync();
}

private static (string FileName, SourceText SourceText) GetSourceFile(string content, string fileName)
{
return (Path.Combine("Moq.AutoMocker.Generators", "Moq.AutoMocker.Generators.OptionsExtensionSourceGenerator", fileName), SourceText.From(content, Encoding.UTF8));
}
}
14 changes: 11 additions & 3 deletions Moq.AutoMocker.Generators/OptionsExtensionSourceGenerator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ public void Execute(GeneratorExecutionContext context)
return;
}

context.AddSource("AutoMocker.Options.cs", OptionsExtensionContent);
context.AddSource("AutoMockerOptionsExtensions.cs", OptionsExtensionContent);
}

public void Initialize(GeneratorInitializationContext context)
Expand All @@ -32,23 +32,31 @@ private static bool ReferencesOptions(IEnumerable<AssemblyIdentity> assemblies)

private const string OptionsExtensionContent =
"""
// Generated by Moq.AutoMocker.Generators.OptionsExtensionSourceGenerator.
// Licensed under the MIT License. See LICENSE in the project root for license information.
namespace Moq.AutoMock
{
using Microsoft.Extensions.Options;

/// <summary>
/// This class provides extension methods for interacting with IOptions in an <see cref="AutoMocker"/> instance.
/// </summary>
public static class AutoMockerOptionsExtensions
{
/// <summary>
/// This method sets up <see cref="AutoMocker"/> with various option related services for Microsoft's Option pattern, and allows their interception and manipulation in testing scenarios.
/// </summary>
/// <param name="mocker">The <see cref="AutoMocker"/> instance</param>
/// <param name="mocker">The <see cref="AutoMocker"/> instance.</param>
/// <param name="configure">A delegate that can be used to configure an option instance of type TClass.</param>
/// <typeparam name="TClass">The type of Options being configured.</typeparam>
/// <returns>The same <see cref="AutoMocker"/> instance passed as parameter, allowing chained calls.</returns>
public static AutoMocker WithOptions<TClass>(this AutoMocker mocker, Action<TClass>? configure = null)
where TClass : class, new()
{
if (mocker == null) throw new ArgumentNullException(nameof(mocker));
if (mocker == null)
{
throw new ArgumentNullException(nameof(mocker));
}

mocker.Use<IEnumerable<IConfigureOptions<TClass>>>(new[] { new ConfigureOptions<TClass>(configure) });
mocker.With<IOptionsMonitorCache<TClass>, OptionsCache<TClass>>();
Expand Down
2 changes: 1 addition & 1 deletion Moq.AutoMocker.Generators/TargetTestingFramework.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,6 @@ public enum TargetTestingFramework
{
Unknown,
MSTest,
Xunit,
XUnit,
NUnit
}
6 changes: 3 additions & 3 deletions Moq.AutoMocker.Generators/UnitTestSourceGenerator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ public void Execute(GeneratorExecutionContext context)
case TargetTestingFramework.MSTest:
builder.AppendLine(" [global::Microsoft.VisualStudio.TestTools.UnitTesting.TestMethod]");
break;
case TargetTestingFramework.Xunit:
case TargetTestingFramework.XUnit:
builder.AppendLine(" [global::Xunit.Fact]");
break;
case TargetTestingFramework.NUnit:
Expand Down Expand Up @@ -97,7 +97,7 @@ public void Execute(GeneratorExecutionContext context)
builder.AppendLine($" System.ArgumentNullException ex = global::Microsoft.VisualStudio.TestTools.UnitTesting.Assert.ThrowsException<System.ArgumentNullException>(() => {constructorInvocation});");
builder.AppendLine($" global::Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AreEqual(\"{test.NullParameterName}\", ex.ParamName);");
break;
case TargetTestingFramework.Xunit:
case TargetTestingFramework.XUnit:
builder.AppendLine($" System.ArgumentNullException ex = global::Xunit.Assert.Throws<System.ArgumentNullException>(() => {constructorInvocation});");
builder.AppendLine($" global::Xunit.Assert.Equal(\"{test.NullParameterName}\", ex.ParamName);");
break;
Expand Down Expand Up @@ -154,7 +154,7 @@ private static TargetTestingFramework GetTestingFramework(IEnumerable<AssemblyId
}
if (assembly.Name.StartsWith("xunit."))
{
return TargetTestingFramework.Xunit;
return TargetTestingFramework.XUnit;
}
}
return TargetTestingFramework.Unknown;
Expand Down

0 comments on commit 90288ca

Please sign in to comment.