Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
Prev Previous commit
test(mocks): split [Obsolete] named-args coverage for net472 (#5626)
  • Loading branch information
JohnVerheij committed Apr 19, 2026
commit 87611033ffe035ac4ace693ab278b1ac5a18a481
41 changes: 34 additions & 7 deletions TUnit.Mocks.SourceGenerator.Tests/MockGeneratorTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1240,13 +1240,6 @@ public interface IDialogService
// must round-trip through the generated attribute literal verbatim.
[Obsolete("Use \"NewMethod\" in C:\\New\\Path")]
string? WithTrickyChars();

// C# 10+ named arguments on [Obsolete] (DiagnosticId, UrlFormat) must be
// preserved on the generated forward so consumers using
// `#pragma warning disable CUSTOM001` continue to suppress the mock's
// call site, not just the source interface's.
[Obsolete("Replaced", DiagnosticId = "CUSTOM001", UrlFormat = "https://example.test/{0}")]
string? WithDiagnosticId();
}

public abstract class BaseDialog
Expand All @@ -1270,6 +1263,40 @@ void M()
return VerifyGeneratorOutput(source);
}

#if NET6_0_OR_GREATER
[Test]
public Task Interface_With_Obsolete_DiagnosticId_NamedArgs()
{
// C# 10+ DiagnosticId and UrlFormat named arguments on [Obsolete] were added in
// .NET 5; they don't exist on .NET Framework 4.7.2's mscorlib. This test is gated
// on net6+ so the test compilation can resolve the named arguments against a
// mscorlib that has them. The generator's named-arg preservation path is exercised
// here to keep the regression guard for the bot review #2 fix on supported TFMs.
var source = """
using System;
using TUnit.Mocks;

public interface IDeprecatedApi
{
[Obsolete("Replaced", DiagnosticId = "CUSTOM001", UrlFormat = "https://example.test/{0}")]
string? WithDiagnosticId();
}

public class TestUsage
{
void M()
{
var mock = Mock.Of<IDeprecatedApi>();
}
}
""";

AssertGeneratedCodeHasNoObsoleteWarnings(source);
AssertGeneratedCodeHasNoNullableWarnings(source);
return VerifyGeneratorOutput(source);
}
#endif

[Test]
public Task Interface_FluentUI_Shape_Nullable_Warnings()
{
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
// <auto-generated/>
#nullable enable

namespace TUnit.Mocks.Generated
{
public sealed class IDeprecatedApiMock : global::TUnit.Mocks.Mock<global::IDeprecatedApi>, global::IDeprecatedApi
{
[global::System.ComponentModel.EditorBrowsable(global::System.ComponentModel.EditorBrowsableState.Never)]
internal IDeprecatedApiMock(global::IDeprecatedApi mockObject, global::TUnit.Mocks.MockEngine<global::IDeprecatedApi> engine)
: base(mockObject, engine) { }

[global::System.Obsolete("Replaced", DiagnosticId = "CUSTOM001", UrlFormat = "https://example.test/{0}")]
string? global::IDeprecatedApi.WithDiagnosticId() => Object.WithDiagnosticId();
}
}


// ===== FILE SEPARATOR =====

// <auto-generated/>
#nullable enable

namespace TUnit.Mocks.Generated
{
file sealed class IDeprecatedApiMockImpl : global::IDeprecatedApi, global::TUnit.Mocks.IRaisable, global::TUnit.Mocks.IMockObject
{
private readonly global::TUnit.Mocks.MockEngine<global::IDeprecatedApi> _engine;

[global::System.ComponentModel.EditorBrowsable(global::System.ComponentModel.EditorBrowsableState.Never)]
global::TUnit.Mocks.IMock? global::TUnit.Mocks.IMockObject.MockWrapper { get; set; }

internal IDeprecatedApiMockImpl(global::TUnit.Mocks.MockEngine<global::IDeprecatedApi> engine)
{
_engine = engine;
}

[global::System.Obsolete("Replaced", DiagnosticId = "CUSTOM001", UrlFormat = "https://example.test/{0}")]
public string? WithDiagnosticId()
{
return _engine.HandleCallWithReturn<string?>(0, "WithDiagnosticId", global::System.Array.Empty<object?>(), default);
}

[global::System.ComponentModel.EditorBrowsable(global::System.ComponentModel.EditorBrowsableState.Never)]
public void RaiseEvent(string eventName, object? args)
{
throw new global::System.InvalidOperationException($"No event named '{eventName}' exists on this mock.");
}
}

internal static class IDeprecatedApiMockFactory
{
[global::System.Runtime.CompilerServices.ModuleInitializer]
internal static void Register()
{
global::TUnit.Mocks.MockRegistry.RegisterFactory<global::IDeprecatedApi>(Create);
}

internal static global::TUnit.Mocks.Mock<global::IDeprecatedApi> CreateAutoMock(global::TUnit.Mocks.MockBehavior behavior)
{
var engine = new global::TUnit.Mocks.MockEngine<global::IDeprecatedApi>(behavior);
var impl = new IDeprecatedApiMockImpl(engine);
engine.Raisable = impl;
var mock = new IDeprecatedApiMock(impl, engine);
return mock;
}

internal static global::TUnit.Mocks.Mock<global::IDeprecatedApi> Create(global::TUnit.Mocks.MockBehavior behavior, object[] constructorArgs)
{
if (constructorArgs.Length > 0) throw new global::System.ArgumentException($"Interface mock 'global::IDeprecatedApi' does not support constructor arguments, but {constructorArgs.Length} were provided.");
var engine = new global::TUnit.Mocks.MockEngine<global::IDeprecatedApi>(behavior);
var impl = new IDeprecatedApiMockImpl(engine);
engine.Raisable = impl;
var mock = new IDeprecatedApiMock(impl, engine);
return mock;
}
}
}


// ===== FILE SEPARATOR =====

// <auto-generated/>
#nullable enable

namespace TUnit.Mocks.Generated
{
public static class IDeprecatedApi_MockMemberExtensions
{
public static global::TUnit.Mocks.MockMethodCall<string?> WithDiagnosticId(this global::TUnit.Mocks.Mock<global::IDeprecatedApi> mock)
{
var matchers = global::System.Array.Empty<global::TUnit.Mocks.Arguments.IArgumentMatcher>();
return new global::TUnit.Mocks.MockMethodCall<string?>(global::TUnit.Mocks.MockRegistry.GetEngine(mock), 0, "WithDiagnosticId", matchers);
}
}
}


// ===== FILE SEPARATOR =====

// <auto-generated/>
#nullable enable

namespace TUnit.Mocks
{
public static class IDeprecatedApi_MockStaticExtension
{
extension(global::IDeprecatedApi _)
{
public static global::TUnit.Mocks.Generated.IDeprecatedApiMock Mock(global::TUnit.Mocks.MockBehavior behavior = global::TUnit.Mocks.MockBehavior.Loose)
{
return (global::TUnit.Mocks.Generated.IDeprecatedApiMock)global::TUnit.Mocks.Generated.IDeprecatedApiMockFactory.CreateAutoMock(behavior);
}
}
}
}


// ===== FILE SEPARATOR =====

// <auto-generated/>
#nullable enable

namespace TUnit.Mocks.Generated;
Original file line number Diff line number Diff line change
Expand Up @@ -199,9 +199,6 @@ namespace TUnit.Mocks.Generated
[global::System.Obsolete("Use \"NewMethod\" in C:\\New\\Path")]
string? global::IDialogService.WithTrickyChars() => Object.WithTrickyChars();

[global::System.Obsolete("Replaced", DiagnosticId = "CUSTOM001", UrlFormat = "https://example.test/{0}")]
string? global::IDialogService.WithDiagnosticId() => Object.WithDiagnosticId();

string? global::IDialogService.Greeting { [global::System.Obsolete] get => Object.Greeting; [global::System.Obsolete] set => Object.Greeting = value; }

string? global::IDialogService.Headline { [global::System.Obsolete] get => Object.Headline; set => Object.Headline = value; }
Expand Down Expand Up @@ -295,12 +292,6 @@ namespace TUnit.Mocks.Generated
return _engine.HandleCallWithReturn<string?>(8, "WithTrickyChars", global::System.Array.Empty<object?>(), default);
}

[global::System.Obsolete("Replaced", DiagnosticId = "CUSTOM001", UrlFormat = "https://example.test/{0}")]
public string? WithDiagnosticId()
{
return _engine.HandleCallWithReturn<string?>(9, "WithDiagnosticId", global::System.Array.Empty<object?>(), default);
}

public string? Greeting
{
[global::System.Obsolete]
Expand Down Expand Up @@ -440,12 +431,6 @@ namespace TUnit.Mocks.Generated
return new IDialogService_WithTrickyChars_M8_MockCall(global::TUnit.Mocks.MockRegistry.GetEngine(mock), 8, "WithTrickyChars", matchers);
}

public static IDialogService_WithDiagnosticId_M9_MockCall WithDiagnosticId(this global::TUnit.Mocks.Mock<global::IDialogService> mock)
{
var matchers = global::System.Array.Empty<global::TUnit.Mocks.Arguments.IArgumentMatcher>();
return new IDialogService_WithDiagnosticId_M9_MockCall(global::TUnit.Mocks.MockRegistry.GetEngine(mock), 9, "WithDiagnosticId", matchers);
}

extension(global::TUnit.Mocks.Mock<global::IDialogService> mock)
{
public global::TUnit.Mocks.PropertyMockCall<string?> Greeting
Expand Down Expand Up @@ -608,68 +593,6 @@ namespace TUnit.Mocks.Generated
/// <inheritdoc />
public void WasNeverCalled(string? message) => _engine.CreateVerification(_memberId, _memberName, _matchers).WasNeverCalled(message);
}

[global::System.ComponentModel.EditorBrowsable(global::System.ComponentModel.EditorBrowsableState.Never)]
public sealed class IDialogService_WithDiagnosticId_M9_MockCall : global::TUnit.Mocks.Verification.ICallVerification
{
private readonly global::TUnit.Mocks.IMockEngineAccess _engine;
private readonly int _memberId;
private readonly string _memberName;
private readonly global::TUnit.Mocks.Arguments.IArgumentMatcher[] _matchers;
private global::TUnit.Mocks.Setup.MethodSetupBuilder<string?>? _builder;
private bool _builderInitialized;
private object? _builderLock;

internal IDialogService_WithDiagnosticId_M9_MockCall(global::TUnit.Mocks.IMockEngineAccess engine, int memberId, string memberName, global::TUnit.Mocks.Arguments.IArgumentMatcher[] matchers)
{
_engine = engine;
_memberId = memberId;
_memberName = memberName;
_matchers = matchers;
}

private global::TUnit.Mocks.Setup.MethodSetupBuilder<string?> EnsureSetup() =>
global::System.Threading.LazyInitializer.EnsureInitialized(ref _builder, ref _builderInitialized, ref _builderLock, () =>
{
var setup = new global::TUnit.Mocks.Setup.MethodSetup(_memberId, _matchers, _memberName);
_engine.AddSetup(setup);
return new global::TUnit.Mocks.Setup.MethodSetupBuilder<string?>(setup);
})!;

/// <inheritdoc />
public IDialogService_WithDiagnosticId_M9_MockCall Returns(string? value) { EnsureSetup().Returns(value); return this; }
/// <inheritdoc />
public IDialogService_WithDiagnosticId_M9_MockCall Returns(global::System.Func<string?> factory) { EnsureSetup().Returns(factory); return this; }
/// <inheritdoc />
public IDialogService_WithDiagnosticId_M9_MockCall ReturnsSequentially(params string?[] values) { EnsureSetup().ReturnsSequentially(values); return this; }
/// <inheritdoc />
public IDialogService_WithDiagnosticId_M9_MockCall Throws<TException>() where TException : global::System.Exception, new() { EnsureSetup().Throws<TException>(); return this; }
/// <inheritdoc />
public IDialogService_WithDiagnosticId_M9_MockCall Throws(global::System.Exception exception) { EnsureSetup().Throws(exception); return this; }
/// <inheritdoc />
public IDialogService_WithDiagnosticId_M9_MockCall Callback(global::System.Action callback) { EnsureSetup().Callback(callback); return this; }
/// <inheritdoc />
public IDialogService_WithDiagnosticId_M9_MockCall TransitionsTo(string stateName) { EnsureSetup().TransitionsTo(stateName); return this; }
/// <inheritdoc />
public IDialogService_WithDiagnosticId_M9_MockCall Then() { EnsureSetup().Then(); return this; }

/// <summary>Auto-raise the Opened event when this method is called.</summary>
public IDialogService_WithDiagnosticId_M9_MockCall RaisesOpened(string? e) { EnsureSetup().Raises("Opened", (object?)e); return this; }

// ICallVerification
/// <inheritdoc />
public void WasCalled() => _engine.CreateVerification(_memberId, _memberName, _matchers).WasCalled();
/// <inheritdoc />
public void WasCalled(global::TUnit.Mocks.Times times) => _engine.CreateVerification(_memberId, _memberName, _matchers).WasCalled(times);
/// <inheritdoc />
public void WasCalled(global::TUnit.Mocks.Times times, string? message) => _engine.CreateVerification(_memberId, _memberName, _matchers).WasCalled(times, message);
/// <inheritdoc />
public void WasCalled(string? message) => _engine.CreateVerification(_memberId, _memberName, _matchers).WasCalled(message);
/// <inheritdoc />
public void WasNeverCalled() => _engine.CreateVerification(_memberId, _memberName, _matchers).WasNeverCalled();
/// <inheritdoc />
public void WasNeverCalled(string? message) => _engine.CreateVerification(_memberId, _memberName, _matchers).WasNeverCalled(message);
}
}


Expand Down
Loading