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
32 changes: 32 additions & 0 deletions TUnit.Mocks.SourceGenerator.Tests/MockGeneratorTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1347,6 +1347,38 @@ void M()
return VerifyGeneratorOutput(source);
}

[Test]
public Task SelfEquatable_Generates_EqualsOf_GetHashCodeOf_ToStringOf()
{
// Regression for #5675: self-referential IEquatable<T> together with
// overrides of GetHashCode/ToString must produce disambiguated extension
// helpers (EqualsOf / GetHashCodeOf / ToStringOf) on the generated mock,
// because C# overload resolution always prefers the inherited object
// instance methods over an extension named the same.
var source = """
using System;
using TUnit.Mocks;

public class SelfEquatableSnapshot : IEquatable<SelfEquatableSnapshot>
{
public virtual bool Equals(SelfEquatableSnapshot? other) => ReferenceEquals(this, other);
public override bool Equals(object? obj) => obj is SelfEquatableSnapshot s && Equals(s);
public override int GetHashCode() => 0;
public override string ToString() => "base";
}

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

return VerifyGeneratorOutput(source);
}

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

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

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

internal SelfEquatableSnapshotMockImpl(global::TUnit.Mocks.MockEngine<global::SelfEquatableSnapshot> engine) : base()
{
_engine = engine;
}

public override bool Equals(global::SelfEquatableSnapshot? other)
{
if (_engine.TryHandleCallWithReturn<bool, global::SelfEquatableSnapshot?>(0, "Equals", other, default, out var __result))
{
return __result;
}
return base.Equals(other);
}

public override bool Equals(object? obj)
{
if (_engine.TryHandleCallWithReturn<bool, object?>(1, "Equals", obj, default, out var __result))
{
return __result;
}
return base.Equals(obj);
}

public override int GetHashCode()
{
if (_engine.TryHandleCallWithReturn<int>(2, "GetHashCode", global::System.Array.Empty<object?>(), default, out var __result))
{
return __result;
}
return base.GetHashCode();
}

public override string ToString()
{
if (_engine.TryHandleCallWithReturn<string>(3, "ToString", global::System.Array.Empty<object?>(), "", out var __result))
{
return __result;
}
return base.ToString();
}

[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.");
}
}

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

private static global::TUnit.Mocks.Mock<global::SelfEquatableSnapshot> Create(global::TUnit.Mocks.MockBehavior behavior, object[] constructorArgs)
{
var engine = new global::TUnit.Mocks.MockEngine<global::SelfEquatableSnapshot>(behavior);
var impl = new SelfEquatableSnapshotMockImpl(engine);
engine.Raisable = impl;
var mock = new global::TUnit.Mocks.Mock<global::SelfEquatableSnapshot>(impl, engine);
return mock;
}
}
}


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

// <auto-generated/>
#nullable enable

namespace TUnit.Mocks.Generated
{
public static class SelfEquatableSnapshot_MockMemberExtensions
{
public static SelfEquatableSnapshot_Equals_M0_MockCall EqualsOf(this global::TUnit.Mocks.Mock<global::SelfEquatableSnapshot> mock, global::TUnit.Mocks.Arguments.Arg<global::SelfEquatableSnapshot?> other)
{
var matchers = new global::TUnit.Mocks.Arguments.IArgumentMatcher[] { other.Matcher };
return new SelfEquatableSnapshot_Equals_M0_MockCall(global::TUnit.Mocks.MockRegistry.GetEngine(mock), 0, "Equals", matchers);
}

public static SelfEquatableSnapshot_Equals_M0_MockCall EqualsOf(this global::TUnit.Mocks.Mock<global::SelfEquatableSnapshot> mock, global::System.Func<global::SelfEquatableSnapshot?, bool> other)
{
global::TUnit.Mocks.Arguments.Arg<global::SelfEquatableSnapshot?> __fa_other = other;
var matchers = new global::TUnit.Mocks.Arguments.IArgumentMatcher[] { __fa_other.Matcher };
return new SelfEquatableSnapshot_Equals_M0_MockCall(global::TUnit.Mocks.MockRegistry.GetEngine(mock), 0, "Equals", matchers);
}

public static SelfEquatableSnapshot_Equals_M1_MockCall EqualsOf(this global::TUnit.Mocks.Mock<global::SelfEquatableSnapshot> mock, global::TUnit.Mocks.Arguments.Arg<object?> obj)
{
var matchers = new global::TUnit.Mocks.Arguments.IArgumentMatcher[] { obj.Matcher };
return new SelfEquatableSnapshot_Equals_M1_MockCall(global::TUnit.Mocks.MockRegistry.GetEngine(mock), 1, "Equals", matchers);
}

public static SelfEquatableSnapshot_Equals_M1_MockCall EqualsOf(this global::TUnit.Mocks.Mock<global::SelfEquatableSnapshot> mock, global::System.Func<object?, bool> obj)
{
global::TUnit.Mocks.Arguments.Arg<object?> __fa_obj = obj;
var matchers = new global::TUnit.Mocks.Arguments.IArgumentMatcher[] { __fa_obj.Matcher };
return new SelfEquatableSnapshot_Equals_M1_MockCall(global::TUnit.Mocks.MockRegistry.GetEngine(mock), 1, "Equals", matchers);
}

public static global::TUnit.Mocks.MockMethodCall<int> GetHashCodeOf(this global::TUnit.Mocks.Mock<global::SelfEquatableSnapshot> mock)
{
var matchers = global::System.Array.Empty<global::TUnit.Mocks.Arguments.IArgumentMatcher>();
return new global::TUnit.Mocks.MockMethodCall<int>(global::TUnit.Mocks.MockRegistry.GetEngine(mock), 2, "GetHashCode", matchers);
}

public static global::TUnit.Mocks.MockMethodCall<string> ToStringOf(this global::TUnit.Mocks.Mock<global::SelfEquatableSnapshot> 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), 3, "ToString", matchers);
}
}

[global::System.ComponentModel.EditorBrowsable(global::System.ComponentModel.EditorBrowsableState.Never)]
public sealed class SelfEquatableSnapshot_Equals_M0_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<bool>? _builder;

internal SelfEquatableSnapshot_Equals_M0_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<bool> EnsureSetup()
{
var existing = global::System.Threading.Volatile.Read(ref _builder);
if (existing is not null) return existing;
return EnsureSetupSlow();
}

[global::System.Runtime.CompilerServices.MethodImpl(global::System.Runtime.CompilerServices.MethodImplOptions.NoInlining)]
private global::TUnit.Mocks.Setup.MethodSetupBuilder<bool> EnsureSetupSlow()
{
var setup = new global::TUnit.Mocks.Setup.MethodSetup(_memberId, _matchers, _memberName);
var fresh = new global::TUnit.Mocks.Setup.MethodSetupBuilder<bool>(setup);
var prev = global::System.Threading.Interlocked.CompareExchange(ref _builder, fresh, null);
if (prev is not null) return prev;
// AddSetup runs only on the CAS winner. Setup is sequential in practice,
// so a concurrent loser observing the builder before registration is benign.
_engine.AddSetup(setup);
return fresh;
}

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

/// <summary>Configure a typed computed return value using the actual method parameters.</summary>
public SelfEquatableSnapshot_Equals_M0_MockCall Returns(global::System.Func<global::SelfEquatableSnapshot?, bool> factory)
{
EnsureSetup().Returns(args => factory((global::SelfEquatableSnapshot?)args[0]));
return this;
}

/// <summary>Execute a typed callback using the actual method parameters.</summary>
public SelfEquatableSnapshot_Equals_M0_MockCall Callback(global::System.Action<global::SelfEquatableSnapshot?> callback)
{
EnsureSetup().Callback(callback);
return this;
}

/// <summary>Configure a typed computed exception using the actual method parameters.</summary>
public SelfEquatableSnapshot_Equals_M0_MockCall Throws(global::System.Func<global::SelfEquatableSnapshot?, global::System.Exception> exceptionFactory)
{
EnsureSetup().Throws(args => exceptionFactory((global::SelfEquatableSnapshot?)args[0]));
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);
}

[global::System.ComponentModel.EditorBrowsable(global::System.ComponentModel.EditorBrowsableState.Never)]
public sealed class SelfEquatableSnapshot_Equals_M1_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<bool>? _builder;

internal SelfEquatableSnapshot_Equals_M1_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<bool> EnsureSetup()
{
var existing = global::System.Threading.Volatile.Read(ref _builder);
if (existing is not null) return existing;
return EnsureSetupSlow();
}

[global::System.Runtime.CompilerServices.MethodImpl(global::System.Runtime.CompilerServices.MethodImplOptions.NoInlining)]
private global::TUnit.Mocks.Setup.MethodSetupBuilder<bool> EnsureSetupSlow()
{
var setup = new global::TUnit.Mocks.Setup.MethodSetup(_memberId, _matchers, _memberName);
var fresh = new global::TUnit.Mocks.Setup.MethodSetupBuilder<bool>(setup);
var prev = global::System.Threading.Interlocked.CompareExchange(ref _builder, fresh, null);
if (prev is not null) return prev;
// AddSetup runs only on the CAS winner. Setup is sequential in practice,
// so a concurrent loser observing the builder before registration is benign.
_engine.AddSetup(setup);
return fresh;
}

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

/// <summary>Configure a typed computed return value using the actual method parameters.</summary>
public SelfEquatableSnapshot_Equals_M1_MockCall Returns(global::System.Func<object?, bool> factory)
{
EnsureSetup().Returns(args => factory((object?)args[0]));
return this;
}

/// <summary>Execute a typed callback using the actual method parameters.</summary>
public SelfEquatableSnapshot_Equals_M1_MockCall Callback(global::System.Action<object?> callback)
{
EnsureSetup().Callback(callback);
return this;
}

/// <summary>Configure a typed computed exception using the actual method parameters.</summary>
public SelfEquatableSnapshot_Equals_M1_MockCall Throws(global::System.Func<object?, global::System.Exception> exceptionFactory)
{
EnsureSetup().Throws(args => exceptionFactory((object?)args[0]));
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);
}
}


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

// <auto-generated/>
#nullable enable

namespace TUnit.Mocks
{
public static class SelfEquatableSnapshot_MockStaticExtension
{
extension(global::SelfEquatableSnapshot _)
{
public static global::TUnit.Mocks.Mock<global::SelfEquatableSnapshot> Mock(global::TUnit.Mocks.MockBehavior behavior = global::TUnit.Mocks.MockBehavior.Loose)
{
return global::TUnit.Mocks.Mock.Of<global::SelfEquatableSnapshot>(behavior);
}
}
}
}


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

// <auto-generated/>
#nullable enable

namespace TUnit.Mocks.Generated;
Loading
Loading