├── .editorconfig ├── .github └── workflows │ └── build-and-publish.yml ├── .gitignore ├── CHANGELOG.md ├── Directory.Build.props ├── Generators.Tests ├── Expected │ ├── BlobContainerClientMock.cs │ └── LoggerMock.cs ├── Generators.Tests.csproj └── MockGeneratorTests.cs ├── Generators ├── Generators.csproj ├── Internal │ ├── CodeWriter.cs │ ├── DefaultConstraintCandidateCollector.cs │ ├── Exceptions.cs │ ├── GeneratorCache.cs │ ├── GeneratorLog.cs │ ├── Indents.cs │ ├── KnownTypes.cs │ ├── MockClassGenerator.cs │ ├── MockMemberGenerator.cs │ ├── MockTargetModelFactory.cs │ ├── Models │ │ ├── MockTarget.cs │ │ ├── MockTargetMember.cs │ │ ├── MockTargetMethodRunDelegateType.cs │ │ └── MockTargetParameter.cs │ └── NamedTypeSymbolCacheKeyEqualityComparer.cs └── MockGenerator.cs ├── LICENSE.txt ├── README.md ├── SourceMock.sln ├── Tests.Interfaces ├── AbstractClass.cs ├── Disposable.cs ├── IEmptyInterface.cs ├── IExcludedInterface.cs ├── IInheritedInteface.cs ├── IInternalInterface.cs ├── IMockable.cs ├── IMockable2.cs ├── INeedsCollectionDefaults.cs ├── INeedsGenericConstraints.cs ├── INeedsGenerics.cs ├── INeedsOtherDefaults.cs ├── INeedsParameterModifiers.cs └── Tests.Interfaces.csproj ├── Tests ├── ArgumentTests.cs ├── DefaultValueTests.cs ├── ExceptionSetupTests.cs ├── Generated │ └── SourceMock.Generators │ │ └── SourceMock.Generators.MockGenerator │ │ ├── global__Azure_Storage_Blobs_BlobContainerClient.cs │ │ ├── global__SourceMock_Tests_Interfaces_AbstractClass.cs │ │ ├── global__SourceMock_Tests_Interfaces_Disposable.cs │ │ ├── global__SourceMock_Tests_Interfaces_IEmptyInterface.cs │ │ ├── global__SourceMock_Tests_Interfaces_IInheritedInteface.cs │ │ ├── global__SourceMock_Tests_Interfaces_IInternalInterface.cs │ │ ├── global__SourceMock_Tests_Interfaces_IMockable.cs │ │ ├── global__SourceMock_Tests_Interfaces_IMockable2.cs │ │ ├── global__SourceMock_Tests_Interfaces_INeedsCollectionDefaults.cs │ │ ├── global__SourceMock_Tests_Interfaces_INeedsGenericConstraints.cs │ │ ├── global__SourceMock_Tests_Interfaces_INeedsGenericConstraints_TNotNull__TClass__TNullableClass__TStruct__TUnmanaged_.cs │ │ ├── global__SourceMock_Tests_Interfaces_INeedsGenerics.cs │ │ ├── global__SourceMock_Tests_Interfaces_INeedsGenerics_U_.cs │ │ ├── global__SourceMock_Tests_Interfaces_INeedsOtherDefaults.cs │ │ ├── global__SourceMock_Tests_Interfaces_INeedsParameterModifiers.cs │ │ └── global__System_Net_WebSockets_WebSocket.cs ├── MockManifest.cs ├── ReturnValueTests.cs ├── RunsCallbackTests.cs ├── Tests.csproj └── VerificationTests.cs └── [Main] ├── GenerateMocksForAssemblyOfAttribute.cs ├── GenerateMocksForTypesAttribute.cs ├── IMock.cs ├── Interfaces ├── IMockMethodSetup.cs ├── IMockPropertyCalls.cs ├── IMockPropertySetup.cs ├── IMockSettablePropertyCalls.cs ├── IMockSettablePropertySetup.cs ├── IMockSetupReturns.cs ├── IMockSetupRuns.cs └── IMockSetupThrows.cs ├── Internal ├── DefaultValue.cs ├── IMockArgumentMatcher.cs ├── IMockMethodSetupInternal.cs ├── MockArgumentMatcher.cs ├── MockCall.cs ├── MockMethodHandler.cs ├── MockMethodSetup.cs ├── MockPropertyCalls.cs ├── MockPropertyHandler.cs ├── MockPropertySetup.cs └── VoidReturn.cs ├── MockExtensions.cs ├── NoArguments.cs └── [Main].csproj /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | charset = utf-8 5 | indent_style = space 6 | indent_size = 4 7 | end_of_line = crlf 8 | trim_trailing_whitespace = true 9 | insert_final_newline = false 10 | 11 | [*.md] 12 | trim_trailing_whitespace = false 13 | 14 | [*.{html,config,csproj,targets,props,yml}] 15 | indent_size = 2 16 | 17 | [{package,appsettings*}.json] 18 | indent_size = 2 19 | 20 | [*.cs] 21 | csharp_new_line_before_open_brace = none 22 | csharp_style_var_for_built_in_types = true:warning 23 | csharp_style_var_when_type_is_apparent = true:warning 24 | csharp_style_var_elsewhere = true:warning 25 | 26 | # IDE0044: Add readonly modifier 27 | dotnet_style_readonly_field = true:warning 28 | 29 | # IDE0005: Using directive is unnecessary. 30 | dotnet_diagnostic.IDE0005.severity = warning 31 | 32 | # IDE0090: 'new' expression can be simplified. 33 | dotnet_diagnostic.IDE0090.severity = warning 34 | 35 | # Performance Sensitive Analyzers 36 | dotnet_diagnostic.HAA0501.severity = warning 37 | dotnet_diagnostic.HAA0502.severity = warning 38 | dotnet_diagnostic.HAA0503.severity = warning -------------------------------------------------------------------------------- /.github/workflows/build-and-publish.yml: -------------------------------------------------------------------------------- 1 | name: 'Build and Publish' 2 | on: [push, pull_request] 3 | 4 | jobs: 5 | build-and-publish: 6 | name: 'Build and Publish' 7 | runs-on: windows-latest 8 | steps: 9 | - uses: actions/checkout@v2 10 | - uses: actions/setup-dotnet@v1 11 | with: 12 | dotnet-version: 5.0.x 13 | 14 | - run: dotnet build --configuration Release 15 | - run: dotnet test --no-build --configuration Release 16 | - run: dotnet pack --no-build --output . --configuration Release 17 | 18 | - run: dotnet nuget push SourceMock.*.nupkg --source https://api.nuget.org/v3/index.json --api-key ${{secrets.NUGET_API_KEY}} --skip-duplicate 19 | if: github.ref == 'refs/heads/main' 20 | 21 | - uses: actions/upload-artifact@v2 22 | with: 23 | name: Package 24 | path: SourceMock.*.nupkg -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .vs/ 2 | bin/ 3 | obj/ 4 | /Generators/Logs/ 5 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | ## [0.12.0] - 2021-11-13 4 | 5 | ### Fixed 6 | - Fix class mock generation failure when base class has any fields 7 | - Fix class mock generation failure when base class has internal members but no InternalsVisibleTo 8 | 9 | ## [0.11.0] - 2021-10-21 10 | 11 | ### Fixed 12 | - Fix mock generation bug in generic types referenced through GenerateMocksForTypes 13 | 14 | ## [0.10.0] - 2021-06-21 15 | 16 | ### Changed 17 | - All generated mocks are now internal 18 | 19 | ## [0.9.1] - 2021-06-21 20 | 21 | ### Fixed 22 | - Fix class mocks with implicit interface implementations 23 | - Fix class mocks with non-virtual properties 24 | 25 | ## [0.9.0] - 2021-05-03 26 | 27 | ### Added 28 | - Initial version -------------------------------------------------------------------------------- /Directory.Build.props: -------------------------------------------------------------------------------- 1 | 2 | 3 | 9.0 4 | enable 5 | true 6 | CS1591 7 | 8 | -------------------------------------------------------------------------------- /Generators.Tests/Expected/LoggerMock.cs: -------------------------------------------------------------------------------- 1 | #nullable enable 2 | namespace Microsoft.Extensions.Logging.Mocks { 3 | internal class LoggerMock : global::Microsoft.Extensions.Logging.ILogger, ILoggerSetup, ILoggerCalls, SourceMock.IMock> { 4 | public ILoggerSetup Setup => this; 5 | public ILoggerCalls Calls => this; 6 | 7 | private readonly SourceMock.Internal.MockMethodHandler _logHandler = new(); 8 | SourceMock.Interfaces.IMockMethodSetup.LogAction> ILoggerSetup.Log(SourceMock.Internal.MockArgumentMatcher logLevel, SourceMock.Internal.MockArgumentMatcher eventId, SourceMock.Internal.MockArgumentMatcher state, SourceMock.Internal.MockArgumentMatcher exception, SourceMock.Internal.MockArgumentMatcher> formatter) => _logHandler.Setup.LogAction, SourceMock.Internal.VoidReturn>(new[] { typeof(TState) }, new SourceMock.Internal.IMockArgumentMatcher[] { logLevel, eventId, state, exception, formatter }); 9 | public void Log(global::Microsoft.Extensions.Logging.LogLevel logLevel, global::Microsoft.Extensions.Logging.EventId eventId, TState state, global::System.Exception exception, global::System.Func formatter) => _logHandler.Call.LogAction, SourceMock.Internal.VoidReturn>(new[] { typeof(TState) }, new object?[] { logLevel, eventId, state, exception, formatter }); 10 | System.Collections.Generic.IReadOnlyList<(global::Microsoft.Extensions.Logging.LogLevel logLevel, global::Microsoft.Extensions.Logging.EventId eventId, TState state, global::System.Exception exception, global::System.Func formatter)> ILoggerCalls.Log(SourceMock.Internal.MockArgumentMatcher logLevel, SourceMock.Internal.MockArgumentMatcher eventId, SourceMock.Internal.MockArgumentMatcher state, SourceMock.Internal.MockArgumentMatcher exception, SourceMock.Internal.MockArgumentMatcher> formatter) => _logHandler.Calls(new[] { typeof(TState) }, new SourceMock.Internal.IMockArgumentMatcher[] { logLevel, eventId, state, exception, formatter }, args => ((global::Microsoft.Extensions.Logging.LogLevel)args[0]!, (global::Microsoft.Extensions.Logging.EventId)args[1]!, (TState)args[2]!, (global::System.Exception)args[3]!, (global::System.Func)args[4]!)); 11 | 12 | private readonly SourceMock.Internal.MockMethodHandler _isEnabledHandler = new(); 13 | SourceMock.Interfaces.IMockMethodSetup, bool> ILoggerSetup.IsEnabled(SourceMock.Internal.MockArgumentMatcher logLevel) => _isEnabledHandler.Setup, bool>(null, new SourceMock.Internal.IMockArgumentMatcher[] { logLevel }); 14 | public bool IsEnabled(global::Microsoft.Extensions.Logging.LogLevel logLevel) => _isEnabledHandler.Call, bool>(null, new object?[] { logLevel }); 15 | System.Collections.Generic.IReadOnlyList ILoggerCalls.IsEnabled(SourceMock.Internal.MockArgumentMatcher logLevel) => _isEnabledHandler.Calls(null, new SourceMock.Internal.IMockArgumentMatcher[] { logLevel }, args => ((global::Microsoft.Extensions.Logging.LogLevel)args[0]!)); 16 | 17 | private readonly SourceMock.Internal.MockMethodHandler _beginScopeHandler = new(); 18 | SourceMock.Interfaces.IMockMethodSetup.BeginScopeFunc, global::System.IDisposable> ILoggerSetup.BeginScope(SourceMock.Internal.MockArgumentMatcher state) => _beginScopeHandler.Setup.BeginScopeFunc, global::System.IDisposable>(new[] { typeof(TState) }, new SourceMock.Internal.IMockArgumentMatcher[] { state }); 19 | public global::System.IDisposable BeginScope(TState state) => _beginScopeHandler.Call.BeginScopeFunc, global::System.IDisposable>(new[] { typeof(TState) }, new object?[] { state }); 20 | System.Collections.Generic.IReadOnlyList ILoggerCalls.BeginScope(SourceMock.Internal.MockArgumentMatcher state) => _beginScopeHandler.Calls(new[] { typeof(TState) }, new SourceMock.Internal.IMockArgumentMatcher[] { state }, args => ((TState)args[0]!)); 21 | } 22 | 23 | internal static class LoggerDelegates { 24 | public delegate void LogAction(global::Microsoft.Extensions.Logging.LogLevel logLevel, global::Microsoft.Extensions.Logging.EventId eventId, TState state, global::System.Exception exception, global::System.Func formatter); 25 | public delegate global::System.IDisposable BeginScopeFunc(TState state); 26 | } 27 | 28 | internal interface ILoggerSetup { 29 | SourceMock.Interfaces.IMockMethodSetup.LogAction> Log(SourceMock.Internal.MockArgumentMatcher logLevel = default, SourceMock.Internal.MockArgumentMatcher eventId = default, SourceMock.Internal.MockArgumentMatcher state = default, SourceMock.Internal.MockArgumentMatcher exception = default, SourceMock.Internal.MockArgumentMatcher> formatter = default); 30 | SourceMock.Interfaces.IMockMethodSetup, bool> IsEnabled(SourceMock.Internal.MockArgumentMatcher logLevel = default); 31 | SourceMock.Interfaces.IMockMethodSetup.BeginScopeFunc, global::System.IDisposable> BeginScope(SourceMock.Internal.MockArgumentMatcher state = default); 32 | } 33 | 34 | internal interface ILoggerCalls { 35 | System.Collections.Generic.IReadOnlyList<(global::Microsoft.Extensions.Logging.LogLevel logLevel, global::Microsoft.Extensions.Logging.EventId eventId, TState state, global::System.Exception exception, global::System.Func formatter)> Log(SourceMock.Internal.MockArgumentMatcher logLevel = default, SourceMock.Internal.MockArgumentMatcher eventId = default, SourceMock.Internal.MockArgumentMatcher state = default, SourceMock.Internal.MockArgumentMatcher exception = default, SourceMock.Internal.MockArgumentMatcher> formatter = default); 36 | System.Collections.Generic.IReadOnlyList IsEnabled(SourceMock.Internal.MockArgumentMatcher logLevel = default); 37 | System.Collections.Generic.IReadOnlyList BeginScope(SourceMock.Internal.MockArgumentMatcher state = default); 38 | } 39 | } -------------------------------------------------------------------------------- /Generators.Tests/Generators.Tests.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | net5.0 4 | false 5 | SourceMock.Generators.Tests 6 | SourceMock.Generators.Tests 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | PreserveNewest 26 | 27 | 28 | -------------------------------------------------------------------------------- /Generators.Tests/MockGeneratorTests.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.IO; 4 | using System.Linq; 5 | using System.Reflection; 6 | using Azure.Storage.Blobs; 7 | using Microsoft.CodeAnalysis; 8 | using Microsoft.CodeAnalysis.CSharp; 9 | using Microsoft.Extensions.Logging; 10 | using Xunit; 11 | using Xunit.Abstractions; 12 | 13 | namespace SourceMock.Generators.Tests { 14 | public class MockGeneratorTests { 15 | private readonly ITestOutputHelper _output; 16 | 17 | public MockGeneratorTests(ITestOutputHelper output) { 18 | _output = output; 19 | } 20 | 21 | [Theory] 22 | [InlineData(typeof(ILogger<>), "LoggerMock.cs")] 23 | [InlineData(typeof(BlobContainerClient), "BlobContainerClientMock.cs")] 24 | public void Generator_GeneratesExpectedMock(Type targetType, string expectedTextFileName) { 25 | // Arrange 26 | var targetTypeFullName = targetType.IsGenericType 27 | // Remove `1 at the end. Would not work with more than 9 generic arguments. 28 | ? targetType.FullName!.Remove(targetType.FullName.Length - 2) + "<>" 29 | : targetType.FullName!; 30 | var originalCompilation = CreateCompilationFromText( 31 | $@"[assembly: SourceMock.GenerateMocksForTypes(new[] {{ 32 | typeof({targetTypeFullName}) 33 | }})]", 34 | targetType.Assembly 35 | ); 36 | 37 | var generator = new MockGenerator(); 38 | var driver = (GeneratorDriver)CSharpGeneratorDriver.Create(generator); 39 | 40 | // Act 41 | driver = driver.RunGeneratorsAndUpdateCompilation(originalCompilation, out _, out var diagnostics); 42 | 43 | // Assert 44 | Assert.Empty(diagnostics); 45 | var tree = Assert.Single(driver.GetRunResult().GeneratedTrees); 46 | var text = tree.ToString(); 47 | _output.WriteLine(text); 48 | Assert.Equal(GetExpectedText(expectedTextFileName), text); 49 | } 50 | 51 | private CSharpCompilation CreateCompilationFromText(string source, Assembly referenceAssembly) { 52 | var initialReferenceAssemblies = new[] { 53 | typeof(GenerateMocksForTypesAttribute).Assembly, 54 | referenceAssembly 55 | }; 56 | var referenceAssemblies = initialReferenceAssemblies.Concat( 57 | initialReferenceAssemblies.SelectMany(GetReferencesRecursive) 58 | ).Distinct(); 59 | 60 | var compilation = CSharpCompilation.Create("_", 61 | new[] { CSharpSyntaxTree.ParseText(source) }, 62 | referenceAssemblies.Select(a => MetadataReference.CreateFromFile(a.Location)), 63 | new CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary) 64 | ); 65 | 66 | Assert.Empty(compilation.GetDiagnostics()); 67 | 68 | return compilation; 69 | } 70 | 71 | private IEnumerable GetReferencesRecursive(Assembly assembly) { 72 | foreach (var reference in assembly.GetReferencedAssemblies()) { 73 | var referencedAssembly = Assembly.Load(reference); 74 | yield return referencedAssembly; 75 | foreach (var nestedReference in GetReferencesRecursive(referencedAssembly)) { 76 | yield return nestedReference; 77 | } 78 | } 79 | } 80 | 81 | private string GetExpectedText(string name) { 82 | return File.ReadAllText(Path.Combine(AppContext.BaseDirectory, "Expected", name)); 83 | } 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /Generators/Generators.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | netstandard2.0 5 | false 6 | SourceMock.Generators 7 | SourceMock.Generators 8 | true 9 | 10 | 11 | 12 | Microsoft.CodeAnalysis.PerformanceSensitiveAnalyzers 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | all 23 | runtime; build; native; contentfiles; analyzers 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | -------------------------------------------------------------------------------- /Generators/Internal/CodeWriter.cs: -------------------------------------------------------------------------------- 1 | using System.Text; 2 | using Roslyn.Utilities; 3 | 4 | namespace SourceMock.Generators.Internal { 5 | internal class CodeWriter { 6 | private readonly StringBuilder _builder; 7 | 8 | [PerformanceSensitive("")] 9 | public CodeWriter() { 10 | #pragma warning disable HAA0502 // Explicit allocation -- unavoidable for now, can be pooled later 11 | _builder = new StringBuilder(); 12 | #pragma warning restore HAA0502 13 | } 14 | 15 | [PerformanceSensitive("")] 16 | public CodeWriter Write(string value) { 17 | _builder.Append(value); 18 | return this; 19 | } 20 | 21 | [PerformanceSensitive("")] 22 | public CodeWriter Write(string part1, string part2) { 23 | _builder 24 | .Append(part1) 25 | .Append(part2); 26 | return this; 27 | } 28 | 29 | [PerformanceSensitive("")] 30 | public CodeWriter Write(string part1, string part2, string part3) { 31 | _builder 32 | .Append(part1) 33 | .Append(part2) 34 | .Append(part3); 35 | return this; 36 | } 37 | 38 | [PerformanceSensitive("")] 39 | public CodeWriter Write(string part1, string part2, string part3, string part4) { 40 | _builder 41 | .Append(part1) 42 | .Append(part2) 43 | .Append(part3) 44 | .Append(part4); 45 | return this; 46 | } 47 | 48 | [PerformanceSensitive("")] 49 | internal CodeWriter Write(string part1, string part2, string part3, string part4, string part5) { 50 | _builder 51 | .Append(part1) 52 | .Append(part2) 53 | .Append(part3) 54 | .Append(part4) 55 | .Append(part5); 56 | return this; 57 | } 58 | 59 | [PerformanceSensitive("")] 60 | public CodeWriter Write(string part1, string part2, string part3, string part4, string part5, string part6) { 61 | _builder 62 | .Append(part1) 63 | .Append(part2) 64 | .Append(part3) 65 | .Append(part4) 66 | .Append(part5) 67 | .Append(part6); 68 | return this; 69 | } 70 | 71 | [PerformanceSensitive("")] 72 | public CodeWriter Write(string part1, string part2, string part3, string part4, string part5, string part6, string part7) { 73 | _builder 74 | .Append(part1) 75 | .Append(part2) 76 | .Append(part3) 77 | .Append(part4) 78 | .Append(part5) 79 | .Append(part6) 80 | .Append(part7); 81 | return this; 82 | } 83 | 84 | [PerformanceSensitive("")] 85 | public CodeWriter Write((string part1, string part2, string part3, string part4) parts) { 86 | _builder 87 | .Append(parts.part1) 88 | .Append(parts.part2) 89 | .Append(parts.part3) 90 | .Append(parts.part4); 91 | return this; 92 | } 93 | 94 | [PerformanceSensitive("")] 95 | public CodeWriter WriteLine() { 96 | _builder.AppendLine(); 97 | return this; 98 | } 99 | 100 | [PerformanceSensitive("")] 101 | public CodeWriter WriteLine(string line) { 102 | _builder.AppendLine(line); 103 | return this; 104 | } 105 | 106 | [PerformanceSensitive("")] 107 | public CodeWriter WriteLine(string part1, string part2) { 108 | _builder 109 | .Append(part1) 110 | .AppendLine(part2); 111 | return this; 112 | } 113 | 114 | [PerformanceSensitive("")] 115 | public CodeWriter WriteLine(string part1, string part2, string part3) { 116 | _builder 117 | .Append(part1) 118 | .Append(part2) 119 | .AppendLine(part3); 120 | return this; 121 | } 122 | 123 | [PerformanceSensitive("")] 124 | public CodeWriter WriteLine(string part1, string part2, string part3, string part4) { 125 | _builder 126 | .Append(part1) 127 | .Append(part2) 128 | .Append(part3) 129 | .AppendLine(part4); 130 | return this; 131 | } 132 | 133 | [PerformanceSensitive("")] 134 | public CodeWriter WriteLine(string part1, string part2, string part3, string part4, string part5) { 135 | _builder 136 | .Append(part1) 137 | .Append(part2) 138 | .Append(part3) 139 | .Append(part4) 140 | .AppendLine(part5); 141 | return this; 142 | } 143 | 144 | [PerformanceSensitive("")] 145 | public CodeWriter WriteLine(string part1, string part2, string part3, string part4, string part5, string part6) { 146 | _builder 147 | .Append(part1) 148 | .Append(part2) 149 | .Append(part3) 150 | .Append(part4) 151 | .Append(part5) 152 | .AppendLine(part6); 153 | return this; 154 | } 155 | 156 | [PerformanceSensitive("")] 157 | public CodeWriter WriteLine(string part1, string part2, string part3, string part4, string part5, string part6, string part7) { 158 | _builder 159 | .Append(part1) 160 | .Append(part2) 161 | .Append(part3) 162 | .Append(part4) 163 | .Append(part5) 164 | .Append(part6) 165 | .AppendLine(part7); 166 | return this; 167 | } 168 | 169 | [PerformanceSensitive("")] 170 | public CodeWriter WriteLine(string part1, string part2, string part3, string part4, string part5, string part6, string part7, string part8) { 171 | _builder 172 | .Append(part1) 173 | .Append(part2) 174 | .Append(part3) 175 | .Append(part4) 176 | .Append(part5) 177 | .Append(part6) 178 | .Append(part7) 179 | .AppendLine(part8); 180 | return this; 181 | } 182 | 183 | [PerformanceSensitive("")] 184 | public CodeWriter WriteGeneric(string genericTypeName, string genericArgumentName) { 185 | _builder 186 | .Append(genericTypeName) 187 | .Append("<") 188 | .Append(genericArgumentName) 189 | .Append(">"); 190 | 191 | return this; 192 | } 193 | 194 | [PerformanceSensitive("")] 195 | public CodeWriter WriteGeneric(string genericTypeName, string genericArgumentName1, string genericArgumentName2) { 196 | _builder 197 | .Append(genericTypeName) 198 | .Append("<") 199 | .Append(genericArgumentName1) 200 | .Append(", ") 201 | .Append(genericArgumentName2) 202 | .Append(">"); 203 | 204 | return this; 205 | } 206 | 207 | public CodeWriter WriteIfNotNull(string? value) { 208 | _builder.Append(value); 209 | return this; 210 | } 211 | 212 | public CodeWriter WriteIfNotNull(string? value, (string part1, string part2) ifNotNull, string ifNull) { 213 | if (value != null) { 214 | _builder 215 | .Append(value) 216 | .Append(ifNotNull.part1) 217 | .Append(ifNotNull.part2); 218 | } 219 | else { 220 | _builder.Append(ifNull); 221 | } 222 | 223 | return this; 224 | } 225 | 226 | public CodeWriter WriteIfNotNull(string? value, (string part1, string part2, string part3) ifNotNull, string ifNull) { 227 | if (value != null) { 228 | _builder 229 | .Append(value) 230 | .Append(ifNotNull.part1) 231 | .Append(ifNotNull.part2) 232 | .Append(ifNotNull.part3); 233 | } 234 | else { 235 | _builder.Append(ifNull); 236 | } 237 | 238 | return this; 239 | } 240 | 241 | [PerformanceSensitive("")] 242 | public CodeWriter Append(CodeWriter other) { 243 | _builder.Append(other._builder); 244 | return this; 245 | } 246 | 247 | public int CurrentLength => _builder.Length; 248 | 249 | public override string ToString() => _builder.ToString(); 250 | } 251 | } 252 | -------------------------------------------------------------------------------- /Generators/Internal/DefaultConstraintCandidateCollector.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using Microsoft.CodeAnalysis; 3 | using Roslyn.Utilities; 4 | 5 | namespace SourceMock.Generators.Internal { 6 | // https://github.com/dotnet/csharplang/discussions/4638#discussioncomment-594388 7 | public class DefaultConstraintCandidateCollector : SymbolVisitor { 8 | private NullableAnnotation? _lastAnnotation; 9 | private readonly HashSet _results = new(); 10 | 11 | public ISet Collect(ISymbol symbol) { 12 | _results.Clear(); 13 | Visit(symbol); 14 | return _results; 15 | } 16 | 17 | public override void VisitMethod(IMethodSymbol symbol) { 18 | _lastAnnotation = symbol.ReturnNullableAnnotation; 19 | Visit(symbol.ReturnType); 20 | 21 | foreach (var parameter in symbol.Parameters) { 22 | _lastAnnotation = parameter.NullableAnnotation; 23 | Visit(parameter.Type); 24 | } 25 | } 26 | 27 | [PerformanceSensitive("")] 28 | public override void VisitArrayType(IArrayTypeSymbol symbol) { 29 | _lastAnnotation = symbol.ElementNullableAnnotation; 30 | Visit(symbol.ElementType); 31 | } 32 | 33 | [PerformanceSensitive("")] 34 | public override void VisitNamedType(INamedTypeSymbol symbol) { 35 | if (!symbol.IsGenericType) 36 | return; 37 | 38 | var arguments = symbol.TypeArguments; 39 | for (var i = 0; i < arguments.Length; i++) { 40 | _lastAnnotation = symbol.TypeArgumentNullableAnnotations[0]; 41 | Visit(arguments[i]); 42 | } 43 | } 44 | 45 | [PerformanceSensitive("")] 46 | public override void VisitTypeParameter(ITypeParameterSymbol symbol) { 47 | if (_lastAnnotation != NullableAnnotation.Annotated) 48 | return; 49 | 50 | if (symbol.HasValueTypeConstraint || symbol.HasReferenceTypeConstraint) 51 | return; // we'll copy existing constraints anyways 52 | 53 | _results.Add(symbol); 54 | } 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /Generators/Internal/Exceptions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Microsoft.CodeAnalysis; 3 | 4 | namespace SourceMock.Generators.Internal { 5 | internal static class Exceptions { 6 | 7 | // Having this as a separate method removes need to suppress allocation warnings each time in exceptional situations 8 | public static NotSupportedException NotSupported(string message) => new(message); 9 | 10 | public static NotSupportedException MemberNotSupported(ISymbol symbol) => Exceptions.NotSupported( 11 | $"{symbol.Name} has an unsupported member symbol type ({symbol.GetType()})" 12 | ); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /Generators/Internal/GeneratorCache.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Concurrent; 3 | using System.Collections.Generic; 4 | using System.Threading; 5 | using System.Threading.Tasks; 6 | 7 | namespace SourceMock.Generators.Internal { 8 | // Note: SourceGenerator documentation explicitly does NOT recommend caching. 9 | // https://github.com/dotnet/roslyn/blob/main/docs/features/source-generators.cookbook.md#participate-in-the-ide-experience 10 | // However I can't in good conscience release something that regenerates tons of files on each cursor move. 11 | internal class GeneratorCache: IDisposable { 12 | private static readonly TimeSpan Expiry = TimeSpan.FromMinutes(10); 13 | 14 | private readonly string _name; 15 | private readonly ConcurrentDictionary _dictionary; 16 | private readonly CancellationTokenSource _cleanupCancellationSource = new(); 17 | #pragma warning disable IDE0052 // Remove unread private members 18 | private readonly Task _cleanupTask; 19 | #pragma warning restore IDE0052 // Remove unread private members 20 | 21 | public GeneratorCache(string name, IEqualityComparer? comparer = null) { 22 | _dictionary = new(comparer ?? EqualityComparer.Default); 23 | _cleanupTask = Task.Run(CleanupAsync); 24 | _name = name; 25 | } 26 | 27 | public bool TryGetValue(TKey key, out TValue? value) { 28 | var found = _dictionary.TryGetValue(key, out var entry); 29 | if (!found) { 30 | value = default; 31 | return false; 32 | } 33 | 34 | value = entry.Value; 35 | entry.LastAccessUtc = DateTime.UtcNow; 36 | return found; 37 | } 38 | 39 | public void TryAdd(TKey key, TValue value) { 40 | _dictionary.TryAdd(key, new Entry(value, DateTime.UtcNow)); 41 | } 42 | 43 | private async Task CleanupAsync() { 44 | while (!_cleanupCancellationSource.IsCancellationRequested) { 45 | await Task.Delay(Expiry, _cleanupCancellationSource.Token).ConfigureAwait(false); 46 | 47 | GeneratorLog.Log($"Starting cache cleanup for {_name}. Size: {_dictionary.Count}"); 48 | var cutoff = DateTime.UtcNow - Expiry; 49 | var keysToRemove = new List(); 50 | foreach (var pair in _dictionary) { 51 | if (pair.Value.LastAccessUtc < cutoff) 52 | keysToRemove.Add(pair.Key); 53 | } 54 | 55 | foreach (var key in keysToRemove) { 56 | _dictionary.TryRemove(key, out _); 57 | } 58 | GeneratorLog.Log($"Finished cache cleanup for {_name}. Size: {_dictionary.Count}"); 59 | } 60 | } 61 | 62 | private class Entry { 63 | public Entry(TValue value, DateTime lastAccessUtc) { 64 | Value = value; 65 | LastAccessUtc = lastAccessUtc; 66 | } 67 | 68 | public TValue Value { get; } 69 | // Thread safety does not matter for this one -- 70 | // if we set it from multiple threads, the times 71 | // will be close enough for it not to matter who 72 | // wins. 73 | public DateTime LastAccessUtc { get; set; } 74 | } 75 | 76 | public void Dispose() { 77 | _cleanupCancellationSource.Cancel(); 78 | _cleanupCancellationSource.Dispose(); 79 | } 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /Generators/Internal/GeneratorLog.cs: -------------------------------------------------------------------------------- 1 | using System.Diagnostics; 2 | using Roslyn.Utilities; 3 | using System.Threading; 4 | using System.Runtime.CompilerServices; 5 | #if DEBUG 6 | using System; 7 | using System.Collections.Concurrent; 8 | using System.IO; 9 | using System.Threading.Tasks; 10 | #endif 11 | 12 | namespace SourceMock.Generators.Internal { 13 | internal static class GeneratorLog { 14 | #if DEBUG 15 | private static readonly DateTime _start; 16 | private static readonly Stopwatch _stopwatch; 17 | 18 | private static readonly CancellationTokenSource _logTaskCancellationSource; 19 | private static readonly Task? _logTask; 20 | private static readonly ConcurrentQueue<(DateTime date, string message)> _entries = new(); 21 | 22 | static GeneratorLog() { 23 | static string GetLogPath([CallerFilePath] string path = "") { 24 | using var process = Process.GetCurrentProcess(); 25 | var now = DateTime.Now; 26 | var fileName = $"{Path.GetFileName(process.MainModule.FileName)} ({process.Id}) {now:HH_mm_ss.ffffff}.log"; 27 | return Path.Combine(Path.GetDirectoryName(path), "..", "Logs", $"{now:MMM dd}", fileName); 28 | } 29 | 30 | // Note that the time will drift over time (https://nima-ara-blog.azurewebsites.net/high-resolution-clock-in-net/), 31 | // but for debug scenarios that's totally fine 32 | _start = DateTime.Now; 33 | _stopwatch = Stopwatch.StartNew(); 34 | 35 | var logPath = GetLogPath(); 36 | Directory.CreateDirectory(Path.GetDirectoryName(logPath)); 37 | 38 | _logTaskCancellationSource = new CancellationTokenSource(); 39 | _logTask = Task.Run(async () => { 40 | while (!_logTaskCancellationSource.IsCancellationRequested) { 41 | if (!_entries.IsEmpty) { 42 | using var streamWriter = new StreamWriter(logPath, append: true); 43 | while (_entries.TryDequeue(out var entry)) { 44 | streamWriter.WriteLine($"[{entry.date:HH.mm.ss.fff}] {entry.message}"); 45 | } 46 | } 47 | await Task.Delay(100, _logTaskCancellationSource.Token).ConfigureAwait(false); 48 | } 49 | }); 50 | 51 | Log($"Starting log from {typeof(GeneratorLog).Assembly.Location}"); 52 | AppDomain.CurrentDomain.ProcessExit += (_, _) => { 53 | Log("Stopping log on process exit"); 54 | SpinWait.SpinUntil(() => _entries.IsEmpty, 500); 55 | _logTaskCancellationSource.Cancel(); 56 | SpinWait.SpinUntil(() => _logTask.IsCompleted, 500); 57 | }; 58 | } 59 | #endif 60 | 61 | [Conditional("DEBUG")] 62 | [PerformanceSensitive("")] 63 | public static void Log(string message) { 64 | #if DEBUG 65 | _entries.Enqueue((_start.AddTicks(_stopwatch.ElapsedTicks), message)); 66 | #endif 67 | } 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /Generators/Internal/Indents.cs: -------------------------------------------------------------------------------- 1 | namespace SourceMock.Generators.Internal { 2 | internal static class Indents { 3 | public const string Type = " "; 4 | public const string Member = Type + " "; 5 | public const string MemberBody = Member + " "; 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /Generators/Internal/KnownTypes.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.CodeAnalysis; 2 | 3 | namespace SourceMock.Generators.Internal { 4 | internal static class KnownTypes { 5 | public static class GenerateMocksForAssemblyOfAttribute { 6 | public const string Name = "GenerateMocksForAssemblyOfAttribute"; 7 | public const string NameWithoutAttribute = "GenerateMocksForAssemblyOf"; 8 | 9 | public static bool NamespaceMatches(INamespaceSymbol? @namespace) => @namespace is { 10 | Name: "SourceMock", ContainingNamespace: { IsGlobalNamespace: true } 11 | }; 12 | 13 | public static class NamedParameters { 14 | public const string ExcludeRegex = "ExcludeRegex"; 15 | } 16 | } 17 | 18 | public static class GenerateMocksForTypesAttribute { 19 | public const string Name = "GenerateMocksForTypesAttribute"; 20 | public const string NameWithoutAttribute = "GenerateMocksForTypes"; 21 | 22 | public static bool NamespaceMatches(INamespaceSymbol? @namespace) => @namespace is { 23 | Name: "SourceMock", ContainingNamespace: { IsGlobalNamespace: true } 24 | }; 25 | } 26 | 27 | public static class IMock { 28 | public const string FullName = "SourceMock.IMock"; 29 | } 30 | 31 | public static class IMockMethodSetup { 32 | public const string FullName = "SourceMock.Interfaces.IMockMethodSetup"; 33 | } 34 | 35 | public static class IMockArgumentMatcher { 36 | public const string FullName = "SourceMock.Internal.IMockArgumentMatcher"; 37 | } 38 | 39 | public static class MockMethodHandler { 40 | public const string FullName = "SourceMock.Internal.MockMethodHandler"; 41 | } 42 | 43 | public static class IMockPropertySetup { 44 | public const string FullName = "SourceMock.Interfaces.IMockPropertySetup"; 45 | } 46 | 47 | public static class IMockSettablePropertySetup { 48 | public const string FullName = "SourceMock.Interfaces.IMockSettablePropertySetup"; 49 | } 50 | 51 | public static class IMockPropertyCalls { 52 | public const string FullName = "SourceMock.Interfaces.IMockPropertyCalls"; 53 | } 54 | 55 | public static class IMockSettablePropertyCalls { 56 | public const string FullName = "SourceMock.Interfaces.IMockSettablePropertyCalls"; 57 | } 58 | 59 | public static class MockPropertyHandler { 60 | public const string FullName = "SourceMock.Internal.MockPropertyHandler"; 61 | } 62 | 63 | public static class MockArgumentMatcher { 64 | public const string FullName = "SourceMock.Internal.MockArgumentMatcher"; 65 | } 66 | 67 | public static class NoArguments { 68 | public const string FullName = "SourceMock.NoArguments"; 69 | } 70 | 71 | public static class VoidReturn { 72 | public const string FullName = "SourceMock.Internal.VoidReturn"; 73 | } 74 | 75 | public static class IReadOnlyList { 76 | public const string FullName = "System.Collections.Generic.IReadOnlyList"; 77 | } 78 | 79 | public static class Func { 80 | public const string FullName = "System.Func"; 81 | } 82 | 83 | public static class Action { 84 | public const string FullName = "System.Action"; 85 | } 86 | } 87 | } 88 | -------------------------------------------------------------------------------- /Generators/Internal/MockClassGenerator.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Microsoft.CodeAnalysis; 3 | using Roslyn.Utilities; 4 | using SourceMock.Generators.Internal.Models; 5 | 6 | namespace SourceMock.Generators.Internal { 7 | internal class MockClassGenerator { 8 | private static readonly SymbolDisplayFormat TargetTypeNamespaceDisplayFormat = SymbolDisplayFormat.FullyQualifiedFormat.WithGlobalNamespaceStyle( 9 | SymbolDisplayGlobalNamespaceStyle.OmittedAsContaining 10 | ); 11 | 12 | private readonly MockTargetModelFactory _modelFactory; 13 | private readonly MockMemberGenerator _mockMemberGenerator = new(); 14 | 15 | public MockClassGenerator(MockTargetModelFactory modelFactory) { 16 | _modelFactory = modelFactory; 17 | } 18 | 19 | [PerformanceSensitive("")] 20 | public string Generate(MockTarget target, IAssemblySymbol currentAssembly) { 21 | var targetTypeNamespace = target.Type.ContainingNamespace.ToDisplayString(TargetTypeNamespaceDisplayFormat); 22 | var mockBaseName = GenerateMockBaseName(target.Type.Name); 23 | var typeParameters = GenerateTypeParametersAsString(target); 24 | var mockClassName = mockBaseName + "Mock" + typeParameters; 25 | var setupInterfaceName = "I" + mockBaseName + "Setup" + typeParameters; 26 | var callsInterfaceName = "I" + mockBaseName + "Calls" + typeParameters; 27 | var customDelegatesClassName = mockBaseName + "Delegates" + typeParameters; 28 | 29 | #pragma warning disable HAA0502 // Explicit allocation -- unavoidable for now, can be pooled later 30 | var mainWriter = new CodeWriter() 31 | #pragma warning restore HAA0502 32 | .WriteLine("#nullable enable") 33 | .WriteLine("namespace ", targetTypeNamespace, ".Mocks {"); 34 | 35 | mainWriter 36 | .Write(Indents.Type, "internal class ", mockClassName, " : ") 37 | .Write(target.FullTypeName, ", ", setupInterfaceName, ", ", callsInterfaceName, ", ") 38 | .WriteGeneric(KnownTypes.IMock.FullName, target.FullTypeName) 39 | .WriteIfNotNull(target.GenericParameterConstraints, (Environment.NewLine, Indents.Type, "{"), " {") 40 | .WriteLine() 41 | .WriteLine(Indents.Member, "public ", setupInterfaceName, " Setup => this;") 42 | .WriteLine(Indents.Member, "public ", callsInterfaceName, " Calls => this;"); 43 | 44 | #pragma warning disable HAA0502 // Explicit allocation -- unavoidable for now, can be pooled later 45 | var customDelegatesClassWriter = new CodeWriter() 46 | #pragma warning restore HAA0502 47 | .WriteLine(Indents.Type, "internal static class ", customDelegatesClassName, " {"); 48 | var customDelegatesEmptyLength = customDelegatesClassWriter.CurrentLength; 49 | 50 | #pragma warning disable HAA0502 // Explicit allocation -- unavoidable for now, can be pooled later 51 | var setupInterfaceWriter = new CodeWriter() 52 | #pragma warning restore HAA0502 53 | .WriteLine(Indents.Type, "internal interface ", setupInterfaceName, " {"); 54 | 55 | #pragma warning disable HAA0502 // Explicit allocation -- unavoidable for now, can be pooled later 56 | var callsInterfaceWriter = new CodeWriter() 57 | #pragma warning restore HAA0502 58 | .WriteLine(Indents.Type, "internal interface ", callsInterfaceName, " {"); 59 | 60 | #pragma warning disable HAA0401 // Possible allocation of reference type enumerator - TODO 61 | foreach (var member in _modelFactory.GetMockTargetMembers(target, customDelegatesClassName, currentAssembly)) { 62 | #pragma warning restore HAA0401 63 | mainWriter.WriteLine(); 64 | _mockMemberGenerator.WriteMemberMocks( 65 | mainWriter, 66 | customDelegatesClassWriter, 67 | setupInterfaceWriter, 68 | setupInterfaceName, 69 | callsInterfaceWriter, 70 | callsInterfaceName, 71 | member, 72 | currentAssembly 73 | ).WriteLine(); 74 | } 75 | 76 | mainWriter.WriteLine(Indents.Type, "}"); 77 | 78 | if (customDelegatesClassWriter.CurrentLength != customDelegatesEmptyLength) { 79 | customDelegatesClassWriter.Write(Indents.Type, "}"); 80 | mainWriter 81 | .WriteLine() 82 | .Append(customDelegatesClassWriter) 83 | .WriteLine(); 84 | } 85 | 86 | setupInterfaceWriter.Write(Indents.Type, "}"); 87 | mainWriter 88 | .WriteLine() 89 | .Append(setupInterfaceWriter) 90 | .WriteLine(); 91 | 92 | callsInterfaceWriter.Write(Indents.Type, "}"); 93 | mainWriter 94 | .WriteLine() 95 | .Append(callsInterfaceWriter) 96 | .WriteLine(); 97 | 98 | mainWriter.Write("}"); 99 | return mainWriter.ToString(); 100 | } 101 | 102 | [PerformanceSensitive("")] 103 | private string GenerateMockBaseName(string targetName) { 104 | if (targetName.Length < 3) 105 | return targetName; 106 | 107 | var canRemoveI = targetName[0] == 'I' 108 | && char.IsUpper(targetName[1]) 109 | && char.IsLower(targetName[2]); 110 | 111 | return canRemoveI ? targetName.Substring(1) : targetName; 112 | } 113 | 114 | [PerformanceSensitive("")] 115 | private string GenerateTypeParametersAsString(MockTarget target) { 116 | var parameters = target.Type.TypeParameters; 117 | if (parameters.IsEmpty) 118 | return ""; 119 | 120 | #pragma warning disable HAA0502 // Explicit allocation -- unavoidable for now, can be pooled later 121 | var writer = new CodeWriter(); 122 | #pragma warning restore HAA0502 123 | writer.Write("<"); 124 | var index = 0; 125 | foreach (var parameter in parameters) { 126 | if (index > 0) 127 | writer.Write(", "); 128 | writer.Write(parameter.Name); 129 | index += 1; 130 | } 131 | writer.Write(">"); 132 | return writer.ToString(); 133 | } 134 | } 135 | } 136 | -------------------------------------------------------------------------------- /Generators/Internal/MockMemberGenerator.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Immutable; 3 | using Microsoft.CodeAnalysis; 4 | using Roslyn.Utilities; 5 | using SourceMock.Generators.Internal.Models; 6 | 7 | namespace SourceMock.Generators.Internal { 8 | internal class MockMemberGenerator { 9 | private readonly DefaultConstraintCandidateCollector _defaultConstraintCollector = new(); 10 | 11 | [PerformanceSensitive("")] 12 | public CodeWriter WriteMemberMocks( 13 | CodeWriter mockWriter, 14 | CodeWriter customDelegatesClassWriter, 15 | CodeWriter setupInterfaceWriter, 16 | string setupInterfaceName, 17 | CodeWriter callsInterfaceWriter, 18 | string callsInterfaceName, 19 | in MockTargetMember member, 20 | IAssemblySymbol currentAssembly 21 | ) { 22 | WriteHandlerField(mockWriter, member).WriteLine(); 23 | if (member.MethodRunDelegateType is { IsCustom: true } runDelegate) 24 | WriteCustomRunDelegate(customDelegatesClassWriter, member, runDelegate).WriteLine(); 25 | WriteSetupInterfaceMember(setupInterfaceWriter, member).WriteLine(); 26 | WriteSetupMemberImplementation(mockWriter, setupInterfaceName, member).WriteLine(); 27 | WriteMemberImplementation(mockWriter, member, currentAssembly).WriteLine(); 28 | WriteCallsInterfaceMember(callsInterfaceWriter, member).WriteLine(); 29 | WriteCallsMemberImplementation(mockWriter, callsInterfaceName, member); 30 | return mockWriter; 31 | } 32 | 33 | [PerformanceSensitive("")] 34 | private CodeWriter WriteHandlerField(CodeWriter writer, in MockTargetMember member) { 35 | writer.Write(Indents.Member, "private readonly "); 36 | switch (member.Symbol) { 37 | case IMethodSymbol: 38 | writer.Write(KnownTypes.MockMethodHandler.FullName, " ", member.HandlerFieldName, " = new()"); 39 | break; 40 | 41 | case IPropertySymbol property: 42 | writer 43 | .WriteGeneric(KnownTypes.MockPropertyHandler.FullName, member.HandlerGenericParameterFullName) 44 | .Write(" ", member.HandlerFieldName) 45 | .Write(" = new(", property.SetMethod != null ? "true" : "false", ")"); 46 | break; 47 | 48 | default: 49 | throw Exceptions.MemberNotSupported(member.Symbol); 50 | } 51 | return writer.Write(";"); 52 | } 53 | 54 | [PerformanceSensitive("")] 55 | private CodeWriter WriteCustomRunDelegate(CodeWriter writer, in MockTargetMember member, MockTargetMethodRunDelegateType runDelegate) { 56 | writer.Write(Indents.Member, "public delegate ", member.TypeFullName, " ", runDelegate.CustomNameWithGenericParameters!, "("); 57 | WriteMethodImplementationParametersIfAny(writer, member, out _); 58 | return writer.Write(");"); 59 | } 60 | 61 | [PerformanceSensitive("")] 62 | private CodeWriter WriteSetupInterfaceMember(CodeWriter writer, in MockTargetMember member) { 63 | writer.Write(Indents.Member); 64 | WriteSetupMemberType(writer, member); 65 | writer.Write(" "); 66 | WriteSetupOrCallsInterfaceMemberNameAndParameters(writer, member); 67 | return writer; 68 | } 69 | 70 | [PerformanceSensitive("")] 71 | private CodeWriter WriteSetupMemberImplementation(CodeWriter writer, string setupInterfaceName, in MockTargetMember member) { 72 | writer.Write(Indents.Member); 73 | 74 | WriteSetupMemberType(writer, member); 75 | writer.Write(" ", setupInterfaceName, ".", member.Name); 76 | if (member.Symbol is IMethodSymbol) { 77 | WriteMethodGenericParametersIfAny(writer, member); 78 | writer.Write("("); 79 | WriteSetupOrCallsMemberParameters(writer, member, appendDefaultValue: false); 80 | writer.Write(")"); 81 | WriteExplicitImplementationDefaultConstraintsIfAny(writer, member); 82 | } 83 | 84 | writer.Write(" => "); 85 | 86 | writer.Write(member.HandlerFieldName, ".Setup"); 87 | if (member.Symbol is IMethodSymbol) { 88 | writer.Write("<", member.MethodRunDelegateType!.Value.FullName, ", ", member.HandlerGenericParameterFullName, ">("); 89 | WriteCommonMethodHandlerArguments(writer, member, KnownTypes.IMockArgumentMatcher.FullName); 90 | writer.Write(")"); 91 | } 92 | else { 93 | writer.Write("()"); 94 | } 95 | 96 | return writer.Write(";"); 97 | } 98 | 99 | [PerformanceSensitive("")] 100 | private CodeWriter WriteSetupMemberType(CodeWriter writer, in MockTargetMember member) { 101 | var setupTypeFullName = member.Symbol switch { 102 | IMethodSymbol => KnownTypes.IMockMethodSetup.FullName, 103 | IPropertySymbol property => property.SetMethod != null 104 | ? KnownTypes.IMockSettablePropertySetup.FullName 105 | : KnownTypes.IMockPropertySetup.FullName, 106 | var s => throw Exceptions.MemberNotSupported(s) 107 | }; 108 | 109 | if (member.Symbol is IPropertySymbol) 110 | return writer.WriteGeneric(setupTypeFullName, member.TypeFullName); 111 | 112 | if (member.IsVoidMethod) 113 | return writer.WriteGeneric(setupTypeFullName, member.MethodRunDelegateType!.Value.FullName); 114 | 115 | return writer.WriteGeneric(setupTypeFullName, member.MethodRunDelegateType!.Value.FullName, member.TypeFullName); 116 | } 117 | 118 | [PerformanceSensitive("")] 119 | private CodeWriter WriteMemberImplementation(CodeWriter writer, in MockTargetMember member, IAssemblySymbol currentAssembly) { 120 | writer.Write(Indents.Member); 121 | if (member.Symbol.ContainingType.TypeKind == TypeKind.Interface) { 122 | writer.Write("public "); 123 | } 124 | else { 125 | var currentAssemblyHasInternalAccess = member.Symbol.ContainingAssembly.GivesAccessTo(currentAssembly); 126 | writer.Write(GetAccessibility(member.Symbol.DeclaredAccessibility, currentAssemblyHasInternalAccess), " override "); 127 | } 128 | writer.Write(member.TypeFullName, " ", member.Name); 129 | 130 | switch (member.Symbol) { 131 | case IMethodSymbol: 132 | WriteMethodGenericParametersIfAny(writer, member); 133 | writer.Write("("); 134 | WriteMethodImplementationParametersIfAny(writer, member, out var hasOutParameters); 135 | writer.Write(")"); 136 | writer.WriteIfNotNull(member.GenericParameterConstraints, (Environment.NewLine, Indents.Member), " "); 137 | WriteMethodImplementationBody(writer, hasOutParameters, member); 138 | break; 139 | 140 | case IPropertySymbol property: 141 | if (property.SetMethod != null) { 142 | writer.WriteLine(" {"); 143 | writer.Write(Indents.MemberBody, "get => ", member.HandlerFieldName, ".GetterHandler"); 144 | WriteMemberImplementationHandlerCall( 145 | writer, member, 146 | (KnownTypes.Func.FullName, "<", member.TypeFullName, ">"), 147 | parametersOverride: ImmutableArray.Empty 148 | ); 149 | writer.WriteLine(";"); 150 | writer.Write(Indents.MemberBody, "set => ", member.HandlerFieldName, ".SetterHandler"); 151 | WriteMemberImplementationHandlerCall( 152 | writer, member, 153 | (KnownTypes.Action.FullName, "<", member.TypeFullName, ">") 154 | ); 155 | writer.WriteLine(";"); 156 | writer.Write(Indents.Member, "}"); 157 | } 158 | else { 159 | writer.Write(" => ", member.HandlerFieldName, ".GetterHandler"); 160 | WriteMemberImplementationHandlerCall( 161 | writer, member, 162 | (KnownTypes.Func.FullName, "<", member.TypeFullName, ">") 163 | ); 164 | writer.Write(";"); 165 | } 166 | break; 167 | 168 | default: 169 | throw Exceptions.MemberNotSupported(member.Symbol); 170 | } 171 | 172 | return writer; 173 | } 174 | 175 | [PerformanceSensitive("")] 176 | private string GetAccessibility(Accessibility accessibility, bool currentAssemblylHasInternalAccess) => accessibility switch { 177 | Accessibility.Public => "public", 178 | Accessibility.Protected => "protected", 179 | Accessibility.ProtectedOrInternal => currentAssemblylHasInternalAccess ? "protected internal" : "protected", 180 | Accessibility.ProtectedAndInternal => "private protected", 181 | #pragma warning disable HAA0601 // Boxing -- OK in exceptional case 182 | _ => throw Exceptions.NotSupported($"Unexpected accessibility: {accessibility}") 183 | #pragma warning restore HAA0601 184 | }; 185 | 186 | [PerformanceSensitive("")] 187 | private CodeWriter WriteMethodImplementationParametersIfAny(CodeWriter writer, MockTargetMember member, out bool hasOutParameters) { 188 | hasOutParameters = false; 189 | foreach (var parameter in member.Parameters) { 190 | if (parameter.Index > 0) 191 | writer.Write(", "); 192 | if (GetRefModifier(parameter.RefKind) is {} modifier) 193 | writer.Write(modifier, " "); 194 | writer.Write(parameter.FullTypeName, " ", parameter.Name); 195 | hasOutParameters = hasOutParameters || (parameter.RefKind == RefKind.Out); 196 | } 197 | return writer; 198 | } 199 | 200 | [PerformanceSensitive("")] 201 | private string? GetRefModifier(RefKind refKind) => refKind switch { 202 | RefKind.None => null, 203 | RefKind.Ref => "ref", 204 | RefKind.In => "in", 205 | RefKind.Out => "out", 206 | #pragma warning disable HAA0601 // Boxing -- OK in exceptional case 207 | _ => throw Exceptions.NotSupported($"Unsupported parameter ref kind: {refKind}") 208 | #pragma warning restore HAA0601 209 | }; 210 | 211 | [PerformanceSensitive("")] 212 | private CodeWriter WriteMethodImplementationBody(CodeWriter writer, bool hasOutParameters, in MockTargetMember member) { 213 | if (hasOutParameters) { 214 | writer 215 | .WriteLine("{") 216 | .Write(Indents.MemberBody, "var arguments = "); 217 | WriteCommonArgumentsArray(writer, "object?", member.Parameters).WriteLine(";"); 218 | writer 219 | .Write(Indents.MemberBody, "var result = ", member.HandlerFieldName); 220 | WriteMemberImplementationHandlerCall(writer, member, argumentsArrayOverride: "arguments") 221 | .WriteLine(";"); 222 | foreach (var parameter in member.Parameters) { 223 | if (parameter.RefKind is not RefKind.Out or RefKind.Ref) 224 | continue; 225 | writer.Write(Indents.MemberBody, parameter.Name, " = (", parameter.FullTypeName, ")arguments[", parameter.Index.ToString(), "]"); 226 | if (!parameter.FullTypeName.EndsWith("?")) 227 | writer.Write("!"); 228 | writer.WriteLine(";"); 229 | } 230 | writer.WriteLine(Indents.MemberBody, "return result;"); 231 | return writer.Write(Indents.Member, "}"); 232 | } 233 | 234 | writer.Write("=> ", member.HandlerFieldName); 235 | WriteMemberImplementationHandlerCall(writer, member); 236 | return writer.Write(";"); 237 | } 238 | 239 | [PerformanceSensitive("")] 240 | private CodeWriter WriteMemberImplementationHandlerCall( 241 | CodeWriter writer, 242 | in MockTargetMember member, 243 | (string part1, string part2, string part3, string part4)? runDelegateFullNameOverride = null, 244 | string? argumentsArrayOverride = null, 245 | ImmutableArray? parametersOverride = null 246 | ) { 247 | writer.Write(".Call<"); 248 | if (runDelegateFullNameOverride != null) { 249 | writer.Write(runDelegateFullNameOverride.Value); 250 | } 251 | else { 252 | writer.Write(member.MethodRunDelegateType!.Value.FullName); 253 | } 254 | writer.Write(", ", member.HandlerGenericParameterFullName, ">("); 255 | WriteCommonMethodHandlerArguments(writer, member, "object?", argumentsArrayOverride, parametersOverride ?? member.Parameters); 256 | return writer.Write(")"); 257 | } 258 | 259 | [PerformanceSensitive("")] 260 | private CodeWriter WriteCallsInterfaceMember(CodeWriter writer, in MockTargetMember member) { 261 | writer.Write(Indents.Member); 262 | WriteCallsMemberType(writer, member); 263 | writer.Write(" "); 264 | return WriteSetupOrCallsInterfaceMemberNameAndParameters(writer, member); 265 | } 266 | 267 | [PerformanceSensitive("")] 268 | private CodeWriter WriteCallsMemberImplementation(CodeWriter writer, string callsInterfaceName, in MockTargetMember member) { 269 | writer.Write(Indents.Member); 270 | WriteCallsMemberType(writer, member); 271 | writer.Write(" ", callsInterfaceName, ".", member.Name); 272 | if (member.Symbol is IMethodSymbol) { 273 | WriteMethodGenericParametersIfAny(writer, member); 274 | writer.Write("("); 275 | WriteSetupOrCallsMemberParameters(writer, member, appendDefaultValue: false); 276 | writer.Write(")"); 277 | WriteExplicitImplementationDefaultConstraintsIfAny(writer, member); 278 | } 279 | writer.Write(" => ", member.HandlerFieldName, ".Calls("); 280 | if (member.Symbol is IMethodSymbol) { 281 | WriteCommonMethodHandlerArguments(writer, member, KnownTypes.IMockArgumentMatcher.FullName).Write(", "); 282 | var parameters = member.Parameters; 283 | if (!parameters.IsEmpty) { 284 | writer.Write("args => ("); 285 | foreach (var parameter in parameters) { 286 | if (parameter.Index > 0) 287 | writer.Write(", "); 288 | writer.Write("(", parameter.FullTypeName, ")args[", parameter.Index.ToString(), "]"); 289 | if (!parameter.FullTypeName.EndsWith("?")) 290 | writer.Write("!"); 291 | } 292 | writer.Write(")"); 293 | } 294 | else { 295 | writer.Write("_ => ", KnownTypes.NoArguments.FullName, ".Value"); 296 | } 297 | } 298 | return writer.Write(");"); 299 | } 300 | 301 | [PerformanceSensitive("")] 302 | private CodeWriter WriteCallsMemberType(CodeWriter writer, in MockTargetMember member) { 303 | if (member.Symbol is IPropertySymbol property) { 304 | var callsTypeFullName = property.SetMethod != null 305 | ? KnownTypes.IMockSettablePropertyCalls.FullName 306 | : KnownTypes.IMockPropertyCalls.FullName; 307 | return writer.WriteGeneric(callsTypeFullName, member.TypeFullName); 308 | } 309 | 310 | writer.Write(KnownTypes.IReadOnlyList.FullName, "<"); 311 | 312 | var parameters = member.Parameters; 313 | if (parameters.Length > 1) { 314 | writer.Write("("); 315 | foreach (var parameter in parameters) { 316 | if (parameter.Index > 0) 317 | writer.Write(", "); 318 | writer.Write(parameter.FullTypeName, " ", parameter.Name); 319 | } 320 | writer.Write(")"); 321 | } 322 | else if (parameters.Length == 1) { 323 | writer.Write(parameters[0].FullTypeName); 324 | } 325 | else { 326 | writer.Write(KnownTypes.NoArguments.FullName); 327 | } 328 | 329 | return writer.Write(">"); 330 | } 331 | 332 | [PerformanceSensitive("")] 333 | private CodeWriter WriteSetupOrCallsInterfaceMemberNameAndParameters(CodeWriter writer, in MockTargetMember member) { 334 | writer.Write(member.Name); 335 | switch (member.Symbol) { 336 | case IMethodSymbol: 337 | WriteMethodGenericParametersIfAny(writer, member); 338 | writer.Write("("); 339 | WriteSetupOrCallsMemberParameters(writer, member, appendDefaultValue: true); 340 | writer.Write(")"); 341 | writer.WriteIfNotNull(member.GenericParameterConstraints); 342 | writer.Write(";"); 343 | break; 344 | case IPropertySymbol: 345 | writer.Write(" { get; }"); 346 | break; 347 | default: 348 | throw Exceptions.MemberNotSupported(member.Symbol); 349 | } 350 | return writer; 351 | } 352 | 353 | [PerformanceSensitive("")] 354 | private CodeWriter WriteSetupOrCallsMemberParameters(CodeWriter writer, in MockTargetMember member, bool appendDefaultValue) { 355 | foreach (var parameter in member.Parameters) { 356 | if (parameter.Index > 0) 357 | writer.Write(", "); 358 | writer 359 | .WriteGeneric(KnownTypes.MockArgumentMatcher.FullName, parameter.FullTypeName) 360 | .Write(" ", parameter.Name); 361 | if (appendDefaultValue) 362 | writer.Write(" = default"); 363 | } 364 | return writer; 365 | } 366 | 367 | [PerformanceSensitive("")] 368 | private CodeWriter WriteCommonMethodHandlerArguments( 369 | CodeWriter writer, 370 | in MockTargetMember member, 371 | string argumentTypeFullName, 372 | string? argumentsArrayOverride = null, 373 | ImmutableArray? parametersOverride = null 374 | ) { 375 | var parameters = parametersOverride ?? member.Parameters; 376 | 377 | var genericParameters = member.GenericParameters; 378 | if (!genericParameters.IsEmpty) { 379 | writer.Write("new[] { "); 380 | for (var i = 0; i < genericParameters.Length; i++) { 381 | if (i > 0) 382 | writer.Write(", "); 383 | writer.Write("typeof(", genericParameters[i].Name, ")"); 384 | } 385 | writer.Write(" }"); 386 | } 387 | else { 388 | writer.Write("null"); 389 | } 390 | writer.Write(", "); 391 | 392 | if (argumentsArrayOverride != null) 393 | return writer.Write(argumentsArrayOverride); 394 | 395 | return WriteCommonArgumentsArray(writer, argumentTypeFullName, parameters); 396 | } 397 | 398 | [PerformanceSensitive("")] 399 | private CodeWriter WriteCommonArgumentsArray(CodeWriter writer, string argumentTypeFullName, ImmutableArray parameters) { 400 | if (parameters.IsEmpty) 401 | return writer.Write("null"); 402 | 403 | writer.Write("new ", argumentTypeFullName, "[] { "); 404 | foreach (var parameter in parameters) { 405 | if (parameter.Index > 0) 406 | writer.Write(", "); 407 | 408 | if (parameter.RefKind == RefKind.Out && argumentTypeFullName == "object?") { 409 | writer.Write("default(", parameter.FullTypeName, ")"); 410 | continue; 411 | } 412 | 413 | writer.Write(parameter.Name); 414 | } 415 | return writer.Write(" }"); 416 | } 417 | 418 | [PerformanceSensitive("")] 419 | private CodeWriter WriteMethodGenericParametersIfAny(CodeWriter writer, in MockTargetMember member) { 420 | var genericParameters = member.GenericParameters; 421 | if (genericParameters.IsEmpty) 422 | return writer; 423 | writer.Write("<"); 424 | for (var i = 0; i < genericParameters.Length; i++) { 425 | if (i > 0) 426 | writer.Write(", "); 427 | writer.Write(genericParameters[i].Name); 428 | } 429 | return writer.Write(">"); 430 | } 431 | 432 | [PerformanceSensitive("")] 433 | private CodeWriter WriteExplicitImplementationDefaultConstraintsIfAny(CodeWriter writer, in MockTargetMember member) { 434 | if (member.GenericParameters.Length == 0) 435 | return writer; 436 | 437 | var parametersNeedingDefault = _defaultConstraintCollector.Collect(member.Symbol); 438 | if (parametersNeedingDefault.Count == 0) 439 | return writer; 440 | 441 | #pragma warning disable HAA0401 // Possible allocation of reference type enumerator - TODO 442 | foreach (var parameter in parametersNeedingDefault) { 443 | #pragma warning restore HAA0401 444 | writer.Write(" where ", parameter.Name, ": default"); 445 | } 446 | return writer; 447 | } 448 | } 449 | } 450 | -------------------------------------------------------------------------------- /Generators/Internal/MockTargetModelFactory.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Collections.Immutable; 3 | using System.Linq; 4 | using Microsoft.CodeAnalysis; 5 | using Roslyn.Utilities; 6 | using SourceMock.Generators.Internal.Models; 7 | 8 | namespace SourceMock.Generators.Internal { 9 | internal class MockTargetModelFactory { 10 | private static readonly SymbolDisplayFormat TargetTypeDisplayFormat = SymbolDisplayFormat.FullyQualifiedFormat 11 | .WithMiscellaneousOptions(SymbolDisplayFormat.FullyQualifiedFormat.MiscellaneousOptions | SymbolDisplayMiscellaneousOptions.IncludeNullableReferenceTypeModifier); 12 | 13 | public MockTarget GetMockTarget(INamedTypeSymbol type) { 14 | var fullName = GetFullTypeName(type, NullableAnnotation.None); 15 | return new MockTarget(type, fullName, GetGenericConstraintsAsCode(Indents.Member, type.TypeParameters)); 16 | } 17 | 18 | [PerformanceSensitive("")] 19 | public IEnumerable GetMockTargetMembers(MockTarget target, string customDelegatesClassName, IAssemblySymbol currentAssembly) { 20 | #pragma warning disable HAA0502 // Explicit allocation -- unavoidable for now, can be pooled later (or removed if we handle them differently) 21 | var lastOverloadIds = new Dictionary(); 22 | #pragma warning restore HAA0502 23 | 24 | foreach (var member in target.Type.GetMembers()) { 25 | if (!lastOverloadIds.TryGetValue(member.Name, out var lastOverloadId)) 26 | lastOverloadId = 0; 27 | 28 | var overloadId = lastOverloadId + 1; 29 | if (GetMockTargetMember(member, overloadId, customDelegatesClassName, currentAssembly) is not {} discovered) 30 | continue; 31 | 32 | lastOverloadIds[member.Name] = overloadId; 33 | yield return discovered; 34 | } 35 | 36 | if (target.Type.TypeKind != TypeKind.Interface) 37 | yield break; 38 | 39 | foreach (var @interface in target.Type.AllInterfaces) { 40 | foreach (var member in @interface.GetMembers()) { 41 | if (lastOverloadIds.ContainsKey(member.Name)) 42 | throw Exceptions.NotSupported($"Type member {@interface.Name}.{member.Name} is hidden or overloaded by another type member. This is not yet supported."); 43 | 44 | if (GetMockTargetMember(member, 1, customDelegatesClassName, currentAssembly) is not {} discovered) 45 | continue; 46 | 47 | lastOverloadIds[member.Name] = 1; 48 | yield return discovered; 49 | } 50 | } 51 | } 52 | 53 | [PerformanceSensitive("")] 54 | private MockTargetMember? GetMockTargetMember(ISymbol member, int overloadId, string customDelegatesClassName, IAssemblySymbol currentAssembly) => member switch { 55 | IMethodSymbol method => GetMockTargetMethod(method, overloadId, customDelegatesClassName, currentAssembly), 56 | IPropertySymbol property => GetMockTargetProperty(property, overloadId, currentAssembly), 57 | IFieldSymbol => null, 58 | ITypeSymbol => null, 59 | _ => throw Exceptions.MemberNotSupported(member) 60 | }; 61 | 62 | [PerformanceSensitive("")] 63 | private MockTargetMember? GetMockTargetMethod(IMethodSymbol method, int overloadId, string customDelegatesClassName, IAssemblySymbol currentAssembly) { 64 | if (method.MethodKind != MethodKind.Ordinary) 65 | return null; 66 | 67 | if (!IsVirtual(method)) 68 | return null; 69 | 70 | if (!IsVisible(method, currentAssembly)) 71 | return null; 72 | 73 | var parameters = ConvertParametersFromSymbols(method.Parameters); 74 | var returnTypeFullName = GetFullTypeName(method.ReturnType, method.ReturnNullableAnnotation); 75 | 76 | return new( 77 | method, method.Name, method.ReturnType, 78 | returnTypeFullName, 79 | method.TypeParameters, 80 | GetGenericConstraintsAsCode(Indents.MemberBody, method.TypeParameters), 81 | parameters, 82 | GetHandlerFieldName(method.Name, overloadId), 83 | GetRunDelegateType(method, parameters, returnTypeFullName, overloadId, customDelegatesClassName) 84 | ); 85 | } 86 | 87 | [PerformanceSensitive("")] 88 | private MockTargetMember? GetMockTargetProperty(IPropertySymbol property, int overloadId, IAssemblySymbol currentAssembly) { 89 | if (!IsVirtual(property)) 90 | return null; 91 | 92 | if (!IsVisible(property, currentAssembly)) 93 | return null; 94 | 95 | return new( 96 | property, property.Name, property.Type, 97 | GetFullTypeName(property.Type, property.NullableAnnotation), 98 | ImmutableArray.Empty, 99 | genericParameterConstraints: null, 100 | property.SetMethod == null 101 | ? ImmutableArray.Empty 102 | : ConvertParametersFromSymbols(property.SetMethod.Parameters), 103 | GetHandlerFieldName(property.Name, overloadId), 104 | methodRunDelegateType: null 105 | ); 106 | } 107 | 108 | [PerformanceSensitive("")] 109 | private bool IsVirtual(ISymbol symbol) 110 | => symbol.ContainingType.TypeKind == TypeKind.Interface 111 | || symbol.IsAbstract 112 | || symbol.IsVirtual; 113 | 114 | [PerformanceSensitive("")] 115 | private bool IsVisible(ISymbol symbol, IAssemblySymbol currentAssembly) 116 | => symbol.ContainingType.TypeKind == TypeKind.Interface 117 | || symbol.DeclaredAccessibility is Accessibility.Public or Accessibility.ProtectedOrInternal 118 | || symbol.ContainingAssembly.GivesAccessTo(currentAssembly) && (symbol.DeclaredAccessibility is Accessibility.Internal or Accessibility.ProtectedAndInternal); 119 | 120 | [PerformanceSensitive("")] 121 | private string GetFullTypeName(ITypeSymbol type, NullableAnnotation nullableAnnotation) { 122 | var nullableFlowState = nullableAnnotation switch { 123 | NullableAnnotation.Annotated => NullableFlowState.MaybeNull, 124 | NullableAnnotation.NotAnnotated => NullableFlowState.NotNull, 125 | _ => NullableFlowState.None 126 | }; 127 | return type.ToDisplayString(nullableFlowState, TargetTypeDisplayFormat); 128 | } 129 | 130 | [PerformanceSensitive("")] 131 | private ImmutableArray ConvertParametersFromSymbols(ImmutableArray parameters) { 132 | if (parameters.Length == 0) 133 | return ImmutableArray.Empty; 134 | 135 | var builder = ImmutableArray.CreateBuilder(parameters.Length); 136 | for (var i = 0; i < parameters.Length; i++) { 137 | var parameter = parameters[i]; 138 | builder.Add(new MockTargetParameter( 139 | parameter.Name, GetFullTypeName(parameter.Type, parameter.NullableAnnotation), parameter.RefKind, i 140 | )); 141 | } 142 | return builder.MoveToImmutable(); 143 | } 144 | 145 | [PerformanceSensitive("")] 146 | private string GetHandlerFieldName(string memberName, int overloadId) { 147 | #pragma warning disable HAA0601 // Boxing - unavoidable for now, will revisit later 148 | return $"_{char.ToLowerInvariant(memberName[0])}{memberName.Substring(1)}{(overloadId > 1 ? overloadId.ToString() : "")}Handler"; 149 | #pragma warning restore HAA0601 150 | } 151 | 152 | [PerformanceSensitive("")] 153 | private MockTargetMethodRunDelegateType GetRunDelegateType( 154 | IMethodSymbol method, 155 | ImmutableArray parameters, 156 | string returnTypeFullName, 157 | int overloadId, 158 | string customDelegatesClassName 159 | ) { 160 | var needsCustomDelegate = method.IsGenericMethod 161 | || parameters.Length > 16 162 | || parameters.Any(static p => p.RefKind is not RefKind.None or RefKind.In); 163 | if (needsCustomDelegate) { 164 | var suffix = method.ReturnsVoid ? "Action" : "Func"; 165 | var nameWithGenericParameters = method.Name + (overloadId > 1 ? overloadId.ToString() : "") + suffix; 166 | if (method.IsGenericMethod) 167 | nameWithGenericParameters += "<" + string.Join(", ", method.TypeParameters.Select(static t => t.Name)) + ">"; 168 | 169 | return MockTargetMethodRunDelegateType.Custom(nameWithGenericParameters, customDelegatesClassName + "." + nameWithGenericParameters); 170 | } 171 | 172 | if (method.ReturnsVoid) { 173 | if (!parameters.IsEmpty) 174 | return new($"{KnownTypes.Action.FullName}<{string.Join(",", parameters.Select(static x => x.FullTypeName))}>"); 175 | 176 | return new(KnownTypes.Action.FullName); 177 | } 178 | 179 | if (!parameters.IsEmpty) 180 | return new($"{KnownTypes.Func.FullName}<{string.Join(",", parameters.Select(static x => x.FullTypeName))}, {returnTypeFullName}>"); 181 | 182 | return new($"{KnownTypes.Func.FullName}<{returnTypeFullName}>"); 183 | } 184 | 185 | 186 | [PerformanceSensitive("")] 187 | private string? GetGenericConstraintsAsCode(string indent, ImmutableArray parameters) { 188 | var writer = (CodeWriter?)null; 189 | foreach (var parameter in parameters) { 190 | writer = WriteGenericConstraints(writer, indent, parameter); 191 | } 192 | return writer?.ToString(); 193 | } 194 | 195 | [PerformanceSensitive("")] 196 | private CodeWriter? WriteGenericConstraints(CodeWriter? writer, string indent, ITypeParameterSymbol parameter) { 197 | var parameterStarted = false; 198 | void WriteConstraint(string constraint) { 199 | if (!parameterStarted) { 200 | #pragma warning disable HAA0502 // Explicit new reference type allocation - currently unavoidable 201 | writer ??= new CodeWriter(); 202 | #pragma warning restore HAA0502 // Explicit new reference type allocation 203 | writer 204 | .WriteLine() 205 | .Write(indent, "where ", parameter.Name, ": "); 206 | parameterStarted = true; 207 | } 208 | else { 209 | writer!.Write(", "); 210 | } 211 | writer.Write(constraint); 212 | } 213 | 214 | if (parameter.HasReferenceTypeConstraint) { 215 | WriteConstraint("class"); 216 | if (parameter.ReferenceTypeConstraintNullableAnnotation == NullableAnnotation.Annotated) 217 | writer!.Write("?"); 218 | } 219 | // seems like unmanaged adds implict struct constraint 220 | if (parameter.HasValueTypeConstraint && !parameter.HasUnmanagedTypeConstraint) 221 | WriteConstraint("struct"); 222 | if (parameter.HasNotNullConstraint) 223 | WriteConstraint("notnull"); 224 | if (parameter.HasUnmanagedTypeConstraint) 225 | WriteConstraint("unmanaged"); 226 | 227 | for (var i = 0; i < parameter.ConstraintTypes.Length; i++) { 228 | var fullTypeName = GetFullTypeName(parameter.ConstraintTypes[i], parameter.ConstraintNullableAnnotations[i]); 229 | WriteConstraint(fullTypeName); 230 | } 231 | 232 | if (parameter.HasConstructorConstraint) 233 | WriteConstraint("new()"); 234 | 235 | return writer; 236 | } 237 | } 238 | } 239 | -------------------------------------------------------------------------------- /Generators/Internal/Models/MockTarget.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.CodeAnalysis; 2 | using Roslyn.Utilities; 3 | 4 | namespace SourceMock.Generators.Internal.Models { 5 | internal readonly struct MockTarget { 6 | [PerformanceSensitive("")] 7 | public MockTarget( 8 | INamedTypeSymbol targetType, 9 | string targetTypeQualifiedName, 10 | string? genericParameterConstraints 11 | ) 12 | { 13 | Type = targetType; 14 | FullTypeName = targetTypeQualifiedName; 15 | GenericParameterConstraints = genericParameterConstraints; 16 | } 17 | 18 | public INamedTypeSymbol Type { get; } 19 | public string FullTypeName { get; } 20 | public string? GenericParameterConstraints { get; } 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /Generators/Internal/Models/MockTargetMember.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Immutable; 2 | using Microsoft.CodeAnalysis; 3 | using Roslyn.Utilities; 4 | 5 | namespace SourceMock.Generators.Internal.Models { 6 | internal readonly struct MockTargetMember { 7 | [PerformanceSensitive("")] 8 | public MockTargetMember( 9 | ISymbol symbol, 10 | string name, 11 | ITypeSymbol type, 12 | string typeFullName, 13 | ImmutableArray genericParameters, 14 | string? genericParameterConstraints, 15 | ImmutableArray parameters, 16 | string handlerFieldName, 17 | MockTargetMethodRunDelegateType? methodRunDelegateType 18 | ) { 19 | Symbol = symbol; 20 | Name = name; 21 | Type = type; 22 | TypeFullName = typeFullName; 23 | GenericParameters = genericParameters; 24 | GenericParameterConstraints = genericParameterConstraints; 25 | Parameters = parameters; 26 | HandlerFieldName = handlerFieldName; 27 | MethodRunDelegateType = methodRunDelegateType; 28 | } 29 | 30 | public ISymbol Symbol { get; } 31 | public string Name { get; } 32 | public ITypeSymbol Type { get; } 33 | public string TypeFullName { get; } 34 | public ImmutableArray GenericParameters { get; } 35 | public string? GenericParameterConstraints { get; } 36 | public ImmutableArray Parameters { get; } 37 | public string HandlerFieldName { get; } 38 | public MockTargetMethodRunDelegateType? MethodRunDelegateType { get; } 39 | 40 | [PerformanceSensitive("")] 41 | public bool IsVoidMethod => Symbol is IMethodSymbol && Type.SpecialType == SpecialType.System_Void; 42 | [PerformanceSensitive("")] 43 | public string HandlerGenericParameterFullName => !IsVoidMethod ? TypeFullName : KnownTypes.VoidReturn.FullName; 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /Generators/Internal/Models/MockTargetMethodRunDelegateType.cs: -------------------------------------------------------------------------------- 1 | namespace SourceMock.Generators.Internal.Models { 2 | internal readonly struct MockTargetMethodRunDelegateType { 3 | public MockTargetMethodRunDelegateType(string fullName) { 4 | FullName = fullName; 5 | CustomNameWithGenericParameters = null; 6 | } 7 | 8 | private MockTargetMethodRunDelegateType(string fullName, string customNameWithGenericParameters) { 9 | FullName = fullName; 10 | CustomNameWithGenericParameters = customNameWithGenericParameters; 11 | } 12 | 13 | public static MockTargetMethodRunDelegateType Custom(string customNameWithGenericParameters, string fullName) { 14 | return new(fullName, customNameWithGenericParameters); 15 | } 16 | 17 | public string FullName { get; } 18 | public string? CustomNameWithGenericParameters { get; } 19 | public bool IsCustom => CustomNameWithGenericParameters != null; 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /Generators/Internal/Models/MockTargetParameter.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.CodeAnalysis; 2 | 3 | namespace SourceMock.Generators.Internal.Models { 4 | internal readonly struct MockTargetParameter { 5 | public MockTargetParameter(string name, string typeFullName, RefKind refKind, int index) { 6 | Name = name; 7 | FullTypeName = typeFullName; 8 | RefKind = refKind; 9 | Index = index; 10 | } 11 | 12 | public string Name { get; } 13 | public string FullTypeName { get; } 14 | public RefKind RefKind { get; } 15 | public int Index { get; } 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /Generators/Internal/NamedTypeSymbolCacheKeyEqualityComparer.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Linq; 3 | using Microsoft.CodeAnalysis; 4 | 5 | namespace SourceMock.Generators.Internal { 6 | internal class NamedTypeSymbolCacheKeyEqualityComparer : IEqualityComparer { 7 | public static NamedTypeSymbolCacheKeyEqualityComparer Default { get; } = new NamedTypeSymbolCacheKeyEqualityComparer(); 8 | 9 | public bool Equals(INamedTypeSymbol x, INamedTypeSymbol y) { 10 | if (ReferenceEquals(x, y)) 11 | return true; 12 | if (SymbolEqualityComparer.Default.Equals(x, y)) 13 | return true; 14 | if (x.DeclaringSyntaxReferences.Length == 0) 15 | return false; // non-syntax types will only be cached on assembly level 16 | 17 | return x.DeclaringSyntaxReferences.SequenceEqual( 18 | y.DeclaringSyntaxReferences, static (x, y) => x == y 19 | ); 20 | } 21 | 22 | public int GetHashCode(INamedTypeSymbol obj) { 23 | if (obj.DeclaringSyntaxReferences.Length == 0) 24 | return obj.GetHashCode(); 25 | 26 | var hashCode = 0; 27 | foreach (var reference in obj.DeclaringSyntaxReferences) { 28 | hashCode ^= reference.GetHashCode(); 29 | } 30 | return hashCode; 31 | } 32 | } 33 | } -------------------------------------------------------------------------------- /Generators/MockGenerator.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Immutable; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Text.RegularExpressions; 6 | using Microsoft.CodeAnalysis; 7 | using Microsoft.CodeAnalysis.Text; 8 | using Roslyn.Utilities; 9 | using SourceMock.Generators.Internal; 10 | using SourceMock.Generators.Internal.Models; 11 | 12 | namespace SourceMock.Generators { 13 | [Generator] 14 | internal class MockGenerator : ISourceGenerator, IDisposable { 15 | private static class DiagnosticDescriptors { 16 | #pragma warning disable RS2008 // Enable analyzer release tracking 17 | public static readonly DiagnosticDescriptor SingleMockFailedToGenerate = new( 18 | "SourceMock001", "Failed to generate a single mock", "Failed to generate mock for {0}: {1}", 19 | "Generation", DiagnosticSeverity.Warning, isEnabledByDefault: true 20 | ); 21 | public static readonly DiagnosticDescriptor RegexPatternFailedToParse = new( 22 | "SourceMock002", "Regex pattern is not a valid regex", "Regex pattern \"{0}\" cannot be parsed: {1}", 23 | "Generation", DiagnosticSeverity.Error, isEnabledByDefault: true 24 | ); 25 | #pragma warning restore RS2008 // Enable analyzer release tracking 26 | } 27 | 28 | private readonly GeneratorCache<(IAssemblySymbol assembly, string? excludePattern), ImmutableArray<(string name, SourceText source)>> _mockedAssemblyCache = new("MockedAssemblyCache"); 29 | private readonly GeneratorCache _mockedTypeCache = new("MockedTypeCache", NamedTypeSymbolCacheKeyEqualityComparer.Default); 30 | private readonly MockTargetModelFactory _modelFactory = new(); 31 | private readonly MockClassGenerator _classGenerator; 32 | 33 | public MockGenerator() { 34 | GeneratorLog.Log("MockGenerator constructor"); 35 | _classGenerator = new(_modelFactory); 36 | } 37 | 38 | public void Initialize(GeneratorInitializationContext context) { 39 | } 40 | 41 | [PerformanceSensitive("")] 42 | public void Execute(GeneratorExecutionContext context) { 43 | GeneratorLog.Log("MockGenerator.Execute started"); 44 | try { 45 | GeneratorLog.Log("Get attributes started"); 46 | var attributes = context.Compilation.Assembly.GetAttributes(); 47 | GeneratorLog.Log("Get attributes finished"); 48 | foreach (var attribute in attributes) { 49 | ProcessAssemblyAttribute(attribute, context); 50 | } 51 | } 52 | catch (Exception ex) { 53 | GeneratorLog.Log("MockGenerator.Execute failed: " + ex); 54 | throw; 55 | } 56 | GeneratorLog.Log("MockGenerator.Execute finished"); 57 | } 58 | 59 | private void ProcessAssemblyAttribute(AttributeData attribute, in GeneratorExecutionContext context) { 60 | if (attribute.AttributeClass is not {} attributeClass) 61 | return; 62 | 63 | switch (attributeClass.Name) { 64 | case KnownTypes.GenerateMocksForAssemblyOfAttribute.Name: 65 | if (!KnownTypes.GenerateMocksForAssemblyOfAttribute.NamespaceMatches(attributeClass.ContainingNamespace)) 66 | return; 67 | ProcessGenerateMocksForAssemblyAttribute(attribute, context); 68 | break; 69 | 70 | case KnownTypes.GenerateMocksForTypesAttribute.Name: 71 | if (!KnownTypes.GenerateMocksForTypesAttribute.NamespaceMatches(attributeClass.ContainingNamespace)) 72 | return; 73 | ProcessGenerateMocksForTypesAttribute(attribute, context); 74 | break; 75 | } 76 | } 77 | 78 | [PerformanceSensitive("")] 79 | private void ProcessGenerateMocksForAssemblyAttribute(AttributeData attribute, GeneratorExecutionContext context) { 80 | // intermediate code state? just in case 81 | if (attribute.ConstructorArguments.ElementAtOrDefault(0).Value is not INamedTypeSymbol anyTypeInAssembly) 82 | return; 83 | 84 | string? excludePattern = null; 85 | foreach (var named in attribute.NamedArguments) { 86 | if (named.Key == KnownTypes.GenerateMocksForAssemblyOfAttribute.NamedParameters.ExcludeRegex) 87 | excludePattern = named.Value.Value as string; 88 | } 89 | 90 | GenerateMocksForAssembly(anyTypeInAssembly.ContainingAssembly, excludePattern, attribute.ApplicationSyntaxReference, context); 91 | } 92 | 93 | [PerformanceSensitive("")] 94 | private void ProcessGenerateMocksForTypesAttribute(AttributeData attribute, GeneratorExecutionContext context) { 95 | if (attribute.ConstructorArguments.ElementAtOrDefault(0) is not { Kind: TypedConstantKind.Array, Values: var typeConstants }) 96 | return; 97 | 98 | foreach (var typeConstant in typeConstants) { 99 | if (typeConstant is not { Value: INamedTypeSymbol type }) 100 | continue; 101 | if (type.IsUnboundGenericType) 102 | type = type.ConstructedFrom; 103 | 104 | GenerateMockForType(_modelFactory.GetMockTarget(type), assemblyCacheBuilder: null, context); 105 | } 106 | } 107 | 108 | [PerformanceSensitive("")] 109 | private void GenerateMocksForAssembly(IAssemblySymbol assembly, string? excludePattern, SyntaxReference? errorSyntaxReference, in GeneratorExecutionContext context) { 110 | if (_mockedAssemblyCache.TryGetValue((assembly, excludePattern), out var sources)) { 111 | GeneratorLog.Log("Using cached mocks for assembly " + assembly.Name); 112 | foreach (var (name, source) in sources) { 113 | context.AddSource(name, source); 114 | } 115 | return; 116 | } 117 | 118 | GeneratorLog.Log("Generating mocks for assembly " + assembly.Name); 119 | 120 | Regex? excludeRegex; 121 | try { 122 | #pragma warning disable HAA0502 // Explicit new reference type allocation -- No alternative at the moment (cache/pool later?) 123 | excludeRegex = excludePattern != null ? new Regex(excludePattern) : null; 124 | #pragma warning restore HAA0502 125 | } 126 | catch (ArgumentException ex) { 127 | #pragma warning disable HAA0101 // Array allocation for params parameter -- Exceptional case: OK to allocate 128 | context.ReportDiagnostic(Diagnostic.Create( 129 | DiagnosticDescriptors.RegexPatternFailedToParse, 130 | errorSyntaxReference?.SyntaxTree.GetLocation(errorSyntaxReference.Span), 131 | excludePattern, ex.Message 132 | )); 133 | #pragma warning restore HAA0101 // Array allocation for params parameter 134 | return; 135 | } 136 | 137 | var assemblyCacheBuilder = ImmutableArray.CreateBuilder<(string, SourceText)>(); 138 | GenerateMocksForNamespace(assembly.GlobalNamespace, excludeRegex, assemblyCacheBuilder, context); 139 | _mockedAssemblyCache.TryAdd((assembly, excludePattern), assemblyCacheBuilder.ToImmutable()); 140 | } 141 | 142 | [PerformanceSensitive("")] 143 | private void GenerateMocksForNamespace( 144 | INamespaceSymbol parent, 145 | Regex? excludeRegex, 146 | ImmutableArray<(string, SourceText)>.Builder assemblyCacheBuilder, 147 | in GeneratorExecutionContext context 148 | ) { 149 | #pragma warning disable HAA0401 // Possible allocation of reference type enumerator -- TODO to revisit later 150 | foreach (var member in parent.GetMembers()) { 151 | #pragma warning restore HAA0401 152 | switch (member) { 153 | case INamedTypeSymbol type: 154 | var target = _modelFactory.GetMockTarget(type); 155 | if (!ShouldIncludeInMocksForAssembly(target, excludeRegex, context)) 156 | continue; 157 | GenerateMockForType(target, assemblyCacheBuilder, context); 158 | break; 159 | 160 | case INamespaceSymbol nested: 161 | GenerateMocksForNamespace(nested, excludeRegex, assemblyCacheBuilder, context); 162 | break; 163 | } 164 | } 165 | } 166 | 167 | [PerformanceSensitive("")] 168 | private bool ShouldIncludeInMocksForAssembly(MockTarget target, Regex? excludeRegex, in GeneratorExecutionContext context) { 169 | var type = target.Type; 170 | if (type.TypeKind != TypeKind.Interface) 171 | return false; 172 | 173 | if (type.DeclaredAccessibility != Accessibility.Public) { 174 | var isVisibleInternal = type.DeclaredAccessibility == Accessibility.Internal 175 | && type.ContainingAssembly.GivesAccessTo(context.Compilation.Assembly); 176 | if (!isVisibleInternal) 177 | return false; 178 | } 179 | 180 | if (excludeRegex != null && excludeRegex.IsMatch(target.FullTypeName)) 181 | return false; 182 | 183 | return true; 184 | } 185 | 186 | [PerformanceSensitive("")] 187 | private void GenerateMockForType( 188 | MockTarget target, 189 | ImmutableArray<(string, SourceText)>.Builder? assemblyCacheBuilder, 190 | in GeneratorExecutionContext context 191 | ) { 192 | if (_mockedTypeCache.TryGetValue(target.Type, out var cached)) { 193 | GeneratorLog.Log("Using cached mock for type " + target.FullTypeName); 194 | context.AddSource(cached.name, cached.source); 195 | assemblyCacheBuilder?.Add(cached); 196 | return; 197 | } 198 | 199 | GeneratorLog.Log("Generating mock for type " + target.FullTypeName); 200 | string mockContent; 201 | try { 202 | mockContent = _classGenerator.Generate(target, context.Compilation.Assembly); 203 | } 204 | catch (Exception ex) { 205 | #pragma warning disable HAA0101 // Array allocation for params parameter 206 | // Exceptional case: OK to allocate 207 | context.ReportDiagnostic(Diagnostic.Create( 208 | DiagnosticDescriptors.SingleMockFailedToGenerate, location: null, target.FullTypeName, ex.ToString() 209 | )); 210 | #pragma warning restore HAA0101 // Array allocation for params parameter 211 | return; 212 | } 213 | 214 | var mockFileName = Regex.Replace(target.FullTypeName, @"[^\w\d]", "_") + ".cs"; 215 | var mockSource = SourceText.From(mockContent, Encoding.UTF8); 216 | context.AddSource(mockFileName, mockSource); 217 | _mockedTypeCache.TryAdd(target.Type, (mockFileName, mockSource)); 218 | assemblyCacheBuilder?.Add((mockFileName, mockSource)); 219 | } 220 | 221 | public void Dispose() { 222 | _mockedAssemblyCache.Dispose(); 223 | _mockedTypeCache.Dispose(); 224 | } 225 | } 226 | } -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | Copyright (c) 2021-2022, Andrey Shchekin 2 | All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without 5 | modification, are permitted provided that the following conditions are met: 6 | 7 | * Redistributions of source code must retain the above copyright notice, this 8 | list of conditions and the following disclaimer. 9 | 10 | * Redistributions in binary form must reproduce the above copyright notice, 11 | this list of conditions and the following disclaimer in the documentation 12 | and/or other materials provided with the distribution. 13 | 14 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 15 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 17 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 18 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 20 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 21 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 22 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 23 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Overview 2 | 3 | ![](https://img.shields.io/nuget/v/SourceMock?style=for-the-badge) 4 | 5 | SourceMock is a C# mocking framework based on source generators. 6 | 7 | This allows for a nicer interface than reflection based frameworks, for example: 8 | ```csharp 9 | mock.Setup.Parse().Returns(3); 10 | ``` 11 | instead of 12 | ```csharp 13 | mock.Setup(x => x.Parse(It.IsAny())).Return(3) 14 | ``` 15 | 16 | # Cookbook 17 | 18 | All examples assume the following interface: 19 | ```csharp 20 | namespace Parsing { 21 | interface IParser { int Parse(string value); } 22 | } 23 | ``` 24 | 25 | ## Set up a simple mock 26 | 27 | ```csharp 28 | [assembly: GenerateMocksForAssemblyOf(typeof(IParser))] 29 | 30 | using Parsing.Mocks; 31 | 32 | var parser = new ParserMock(); 33 | 34 | parser.Setup.Parse().Returns(1); 35 | 36 | Assert.Equal(1, parser.Parse()); 37 | ``` 38 | 39 | ## Verify calls 40 | 41 | ```csharp 42 | [assembly: GenerateMocksForAssemblyOf(typeof(IParser))] 43 | 44 | using Parsing.Mocks; 45 | 46 | var parser = new ParserMock(); 47 | 48 | parser.Parse("1"); 49 | parser.Parse("2"); 50 | 51 | Assert.Equal(new[] { "1", "2" }, parser.Calls.Parse()); 52 | ``` 53 | 54 | ## Set up a callback 55 | 56 | ```csharp 57 | [assembly: GenerateMocksForAssemblyOf(typeof(IParser))] 58 | 59 | using Parsing.Mocks; 60 | 61 | var parser = new ParserMock(); 62 | 63 | parser.Setup.Parse().Runs(s => int.Parse(s)); 64 | 65 | Assert.Equal(1, parser.Parse("1")); 66 | ``` 67 | 68 | # Limitations 69 | 70 | ## By Design 71 | 72 | ### Strict Mocks 73 | 74 | SourceMock does not provide strict mocks — this is by design. 75 | I believe that verifying setups blurs the line between Arrange and Assert and decreases test readability. 76 | 77 | Instead, assert `.Calls` at the end of the test to confirm the expected calls. 78 | 79 | ## Not Yet 80 | 81 | These are not _intentionally_ excluded, just not yet supported: 82 | 1. Class mocks: custom constructors, calling base methods, mocking protected members 83 | 2. Custom default values 84 | 3. Custom parameter matchers 85 | 4. Setting up output values for ref and out parameters 86 | 5. Chained setups 87 | 6. Anything more advanced than the above 88 | 89 | # Kudos 90 | 91 | This framework borrows a lot of its design from [Moq](https://github.com/moq), which is one of the best .NET libraries overall. 92 | -------------------------------------------------------------------------------- /SourceMock.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio Version 16 4 | VisualStudioVersion = 16.0.30804.86 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "[Main]", "[Main]\[Main].csproj", "{FFE646A4-7C02-4BF9-A5E4-BD331D02A73C}" 7 | EndProject 8 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Tests", "Tests\Tests.csproj", "{E69446D0-2931-4428-AB16-4CF904A61134}" 9 | EndProject 10 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{18512360-554B-48A7-94AD-2CA72C06EFB8}" 11 | ProjectSection(SolutionItems) = preProject 12 | .editorconfig = .editorconfig 13 | CHANGELOG.md = CHANGELOG.md 14 | Directory.Build.props = Directory.Build.props 15 | README.md = README.md 16 | EndProjectSection 17 | EndProject 18 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Generators", "Generators\Generators.csproj", "{46146400-5A8E-4019-8BB4-B1BF9CA63C3A}" 19 | EndProject 20 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Tests.Interfaces", "Tests.Interfaces\Tests.Interfaces.csproj", "{2ADB146B-C267-4772-A324-D2E3E22A4CDD}" 21 | EndProject 22 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Generators.Tests", "Generators.Tests\Generators.Tests.csproj", "{E26138CB-E9BC-4E23-926E-C7FAA8BDDE8F}" 23 | EndProject 24 | Global 25 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 26 | Debug|Any CPU = Debug|Any CPU 27 | Release|Any CPU = Release|Any CPU 28 | EndGlobalSection 29 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 30 | {FFE646A4-7C02-4BF9-A5E4-BD331D02A73C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 31 | {FFE646A4-7C02-4BF9-A5E4-BD331D02A73C}.Debug|Any CPU.Build.0 = Debug|Any CPU 32 | {FFE646A4-7C02-4BF9-A5E4-BD331D02A73C}.Release|Any CPU.ActiveCfg = Release|Any CPU 33 | {FFE646A4-7C02-4BF9-A5E4-BD331D02A73C}.Release|Any CPU.Build.0 = Release|Any CPU 34 | {E69446D0-2931-4428-AB16-4CF904A61134}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 35 | {E69446D0-2931-4428-AB16-4CF904A61134}.Debug|Any CPU.Build.0 = Debug|Any CPU 36 | {E69446D0-2931-4428-AB16-4CF904A61134}.Release|Any CPU.ActiveCfg = Release|Any CPU 37 | {E69446D0-2931-4428-AB16-4CF904A61134}.Release|Any CPU.Build.0 = Release|Any CPU 38 | {46146400-5A8E-4019-8BB4-B1BF9CA63C3A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 39 | {46146400-5A8E-4019-8BB4-B1BF9CA63C3A}.Debug|Any CPU.Build.0 = Debug|Any CPU 40 | {46146400-5A8E-4019-8BB4-B1BF9CA63C3A}.Release|Any CPU.ActiveCfg = Release|Any CPU 41 | {46146400-5A8E-4019-8BB4-B1BF9CA63C3A}.Release|Any CPU.Build.0 = Release|Any CPU 42 | {2ADB146B-C267-4772-A324-D2E3E22A4CDD}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 43 | {2ADB146B-C267-4772-A324-D2E3E22A4CDD}.Debug|Any CPU.Build.0 = Debug|Any CPU 44 | {2ADB146B-C267-4772-A324-D2E3E22A4CDD}.Release|Any CPU.ActiveCfg = Release|Any CPU 45 | {2ADB146B-C267-4772-A324-D2E3E22A4CDD}.Release|Any CPU.Build.0 = Release|Any CPU 46 | {E26138CB-E9BC-4E23-926E-C7FAA8BDDE8F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 47 | {E26138CB-E9BC-4E23-926E-C7FAA8BDDE8F}.Debug|Any CPU.Build.0 = Debug|Any CPU 48 | {E26138CB-E9BC-4E23-926E-C7FAA8BDDE8F}.Release|Any CPU.ActiveCfg = Release|Any CPU 49 | {E26138CB-E9BC-4E23-926E-C7FAA8BDDE8F}.Release|Any CPU.Build.0 = Release|Any CPU 50 | EndGlobalSection 51 | GlobalSection(SolutionProperties) = preSolution 52 | HideSolutionNode = FALSE 53 | EndGlobalSection 54 | GlobalSection(ExtensibilityGlobals) = postSolution 55 | SolutionGuid = {1116CF99-62CD-4830-A075-B997B3A6B6C1} 56 | EndGlobalSection 57 | EndGlobal 58 | -------------------------------------------------------------------------------- /Tests.Interfaces/AbstractClass.cs: -------------------------------------------------------------------------------- 1 | namespace SourceMock.Tests.Interfaces { 2 | public abstract class AbstractClass { 3 | private readonly string? _getStringDefault; 4 | 5 | public AbstractClass(string getStringDefault) { 6 | _getStringDefault = getStringDefault; 7 | } 8 | 9 | protected AbstractClass() { 10 | } 11 | 12 | public void NonVirtual() {} 13 | private void Private() {} 14 | protected virtual void Protected() {} 15 | protected internal virtual void ProtectedInternal() {} 16 | private protected virtual void PrivateProtected() {} 17 | 18 | public abstract int Get(); 19 | public virtual string? GetString() => _getStringDefault; 20 | 21 | public virtual int VirtualProperty => 1; 22 | public int NonVirtualProperty => 1; 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /Tests.Interfaces/Disposable.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace SourceMock.Tests.Interfaces { 4 | public class Disposable : IDisposable { 5 | public virtual void Dispose() { 6 | } 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /Tests.Interfaces/IEmptyInterface.cs: -------------------------------------------------------------------------------- 1 | namespace SourceMock.Tests.Interfaces { 2 | public interface IEmptyInterface { 3 | } 4 | } -------------------------------------------------------------------------------- /Tests.Interfaces/IExcludedInterface.cs: -------------------------------------------------------------------------------- 1 | namespace SourceMock.Tests.Interfaces { 2 | public interface IExcludedInterface { 3 | } 4 | } 5 | -------------------------------------------------------------------------------- /Tests.Interfaces/IInheritedInteface.cs: -------------------------------------------------------------------------------- 1 | namespace SourceMock.Tests.Interfaces { 2 | interface IInheritedInteface : IMockable2, IEmptyInterface { 3 | void Method(); 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /Tests.Interfaces/IInternalInterface.cs: -------------------------------------------------------------------------------- 1 | namespace SourceMock.Tests.Interfaces { 2 | internal interface IInternalInterface { 3 | } 4 | } 5 | -------------------------------------------------------------------------------- /Tests.Interfaces/IMockable.cs: -------------------------------------------------------------------------------- 1 | using System.Threading.Tasks; 2 | 3 | namespace SourceMock.Tests.Interfaces { 4 | public interface IMockable { 5 | int GetInt32(); 6 | int? GetInt32Nullable(); 7 | string GetString(); 8 | string? GetStringNullable(); 9 | IMockable2 GetMockable2(); 10 | Task GetStringAsync(); 11 | 12 | int ParseToInt32(string? value); 13 | bool TestInterface(IEmptyInterface value); 14 | 15 | double Divide(double value1, double value2); 16 | 17 | int Sum(int value1, int value2); 18 | int Sum(int value1, int value2, int value3); 19 | 20 | void Execute(); 21 | void Execute(IEmptyInterface value); 22 | 23 | int Count { get; } 24 | string Name { get; set; } 25 | } 26 | } -------------------------------------------------------------------------------- /Tests.Interfaces/IMockable2.cs: -------------------------------------------------------------------------------- 1 | namespace SourceMock.Tests.Interfaces { 2 | public interface IMockable2 { 3 | string GetString(); 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /Tests.Interfaces/INeedsCollectionDefaults.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Collections.Immutable; 3 | 4 | namespace SourceMock.Tests.Interfaces { 5 | public interface INeedsCollectionDefaults { 6 | int[] GetArray(); 7 | List GetList(); 8 | ImmutableList GetImmutableList(); 9 | ImmutableArray GetImmutableArray(); 10 | IEnumerable GetIEnumerable(); 11 | IList GetIList(); 12 | ICollection GetICollection(); 13 | IReadOnlyCollection GetIReadOnlyCollection(); 14 | IReadOnlyList GetIReadOnlyList(); 15 | IImmutableList GetIImmutableList(); 16 | 17 | Dictionary GetDictionary(); 18 | ImmutableDictionary GetImmutableDictionary(); 19 | IDictionary GetIDictionary(); 20 | IReadOnlyDictionary GetIReadOnlyDictionary(); 21 | IImmutableDictionary GetIImmutableDictionary(); 22 | 23 | HashSet GetHashSet(); 24 | ImmutableHashSet GetImmutableHashSet(); 25 | ISet GetISet(); 26 | IReadOnlySet GetIReadOnlySet(); 27 | IImmutableSet GetIImmutableSet(); 28 | } 29 | } -------------------------------------------------------------------------------- /Tests.Interfaces/INeedsGenericConstraints.cs: -------------------------------------------------------------------------------- 1 | namespace SourceMock.Tests.Interfaces { 2 | public interface INeedsGenericConstraints { 3 | void Method() 4 | where TNotNull : notnull, IMockable, new() 5 | where TClassNotNull : class, IMockable, new() 6 | where TNullableClass : class?, IMockable, new() 7 | where TStruct : struct, IMockable 8 | where TUnmanaged : unmanaged, IMockable; 9 | } 10 | 11 | public interface INeedsGenericConstraints 12 | where TNotNull : notnull, IMockable, new() 13 | where TClass : class, IMockable, new() 14 | where TNullableClass : class?, IMockable, new() 15 | where TStruct : struct, IMockable 16 | where TUnmanaged : unmanaged, IMockable 17 | { 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /Tests.Interfaces/INeedsGenerics.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | 3 | namespace SourceMock.Tests.Interfaces { 4 | public interface INeedsGenerics { 5 | T Parse(string value); 6 | T Get(); 7 | void SetOptional(T? value); 8 | IEnumerable GetAll(); 9 | } 10 | 11 | public interface INeedsGenerics { 12 | U Get(); 13 | T Convert(U value); 14 | T Cast(U value) 15 | where T: U; 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /Tests.Interfaces/INeedsOtherDefaults.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Threading.Tasks; 3 | 4 | namespace SourceMock.Tests.Interfaces { 5 | public interface INeedsOtherDefaults { 6 | Task ExecuteAsync(); 7 | Task GetStringAsync(); 8 | Task> GetListAsync(); 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /Tests.Interfaces/INeedsParameterModifiers.cs: -------------------------------------------------------------------------------- 1 | namespace SourceMock.Tests.Interfaces { 2 | public interface INeedsParameterModifiers { 3 | int TestIn(in int value); 4 | int TestRef(ref int value); 5 | int TestOut(out int value); 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /Tests.Interfaces/Tests.Interfaces.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net5.0 5 | false 6 | SourceMock.Tests.Interfaces 7 | SourceMock.Tests.Interfaces 8 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /Tests/ArgumentTests.cs: -------------------------------------------------------------------------------- 1 | using Xunit; 2 | using SourceMock.Tests.Interfaces.Mocks; 3 | using SourceMock.Tests.Interfaces; 4 | 5 | namespace SourceMock.Tests { 6 | public class ArgumentTests { 7 | [Fact] 8 | public void SingleArgument() { 9 | var mock = new MockableMock(); 10 | 11 | mock.Setup.ParseToInt32("1").Returns(1); 12 | mock.Setup.ParseToInt32("2").Returns(2); 13 | 14 | Assert.Equal(2, mock.ParseToInt32("2")); 15 | } 16 | 17 | [Fact] 18 | public void SingleArgument_NullValue() { 19 | var mock = new MockableMock(); 20 | 21 | mock.Setup.ParseToInt32(null).Returns(1); 22 | 23 | Assert.Equal(1, mock.ParseToInt32(null)); 24 | } 25 | 26 | [Fact] 27 | public void SingleArgument_Interface() { 28 | var mock = new MockableMock(); 29 | var argument = new EmptyClass(); 30 | 31 | mock.Setup.TestInterface(argument).Returns(true); 32 | 33 | Assert.True(mock.TestInterface(argument)); 34 | } 35 | 36 | [Fact] 37 | public void SingleArgument_Optional() { 38 | var mock = new MockableMock(); 39 | 40 | mock.Setup.ParseToInt32().Returns(1); 41 | 42 | Assert.Equal(1, mock.ParseToInt32("x")); 43 | } 44 | 45 | [Fact] 46 | public void MultipleArguments() { 47 | var mock = new MockableMock(); 48 | 49 | mock.Setup.Sum(1, 2).Returns(3); 50 | 51 | Assert.Equal(3, mock.Sum(1, 2)); 52 | } 53 | 54 | [Fact] 55 | public void InArgument() { 56 | var mock = new NeedsParameterModifiersMock(); 57 | 58 | mock.Setup.TestIn(1).Returns(2); 59 | 60 | Assert.Equal(2, mock.TestIn(1)); 61 | } 62 | 63 | [Fact] 64 | public void RefArgument() { 65 | var mock = new NeedsParameterModifiersMock(); 66 | 67 | mock.Setup.TestRef(1).Returns(2); 68 | 69 | var x = 1; 70 | Assert.Equal(2, mock.TestRef(ref x)); 71 | } 72 | 73 | [Fact] 74 | public void OutArgument() { 75 | var mock = new NeedsParameterModifiersMock(); 76 | 77 | mock.Setup.TestOut().Returns(2); 78 | 79 | int x; 80 | Assert.Equal(2, mock.TestOut(out x)); 81 | } 82 | 83 | [Fact] 84 | public void Generic() { 85 | var mock = new NeedsGenericsMock(); 86 | 87 | mock.Setup.Get().Returns(1); 88 | mock.Setup.Get().Returns(2); 89 | mock.Setup.Get().Returns(3); 90 | 91 | Assert.Equal(2, mock.Get()); 92 | } 93 | 94 | private class EmptyClass : IEmptyInterface {} 95 | } 96 | } -------------------------------------------------------------------------------- /Tests/DefaultValueTests.cs: -------------------------------------------------------------------------------- 1 | using System.Collections; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Reflection; 5 | using System.Threading.Tasks; 6 | using SourceMock.Tests.Interfaces; 7 | using SourceMock.Tests.Interfaces.Mocks; 8 | using Xunit; 9 | 10 | namespace SourceMock.Tests { 11 | public class DefaultValueTests { 12 | [Theory] 13 | [MemberData(nameof(AllCollectionMethods))] 14 | public void Collection(MethodInfo method) { 15 | var mock = new NeedsCollectionDefaultsMock(); 16 | 17 | var result = method.Invoke(mock, null); 18 | 19 | Assert.NotNull(result); 20 | Assert.Empty((IEnumerable)result!); 21 | } 22 | 23 | [Fact] 24 | public void Task_NonGeneric() { 25 | var mock = new NeedsOtherDefaultsMock(); 26 | 27 | var task = mock.ExecuteAsync(); 28 | 29 | Assert.Same(Task.CompletedTask, task); 30 | } 31 | 32 | [Fact] 33 | public async Task Task_Generic() { 34 | var mock = new NeedsOtherDefaultsMock(); 35 | 36 | var task = mock.GetStringAsync(); 37 | 38 | Assert.NotNull(task); 39 | Assert.Null(await task); 40 | } 41 | 42 | [Fact] 43 | public async Task Task_Collection() { 44 | var mock = new NeedsOtherDefaultsMock(); 45 | 46 | var task = mock.GetListAsync(); 47 | 48 | Assert.NotNull(task); 49 | Assert.Empty(await task); 50 | } 51 | 52 | public static IEnumerable AllCollectionMethods { get; } = typeof(INeedsCollectionDefaults) 53 | .GetMethods() 54 | .Select(m => new object[] { m }) 55 | .ToList(); 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /Tests/ExceptionSetupTests.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using SourceMock.Tests.Interfaces.Mocks; 3 | using Xunit; 4 | 5 | namespace SourceMock.Tests { 6 | public class ExceptionSetupTests { 7 | [Fact] 8 | public void Simple() { 9 | var mock = new MockableMock(); 10 | 11 | mock.Setup.GetInt32().Throws(new Exception()); 12 | 13 | Assert.Throws(() => mock.GetInt32()); 14 | } 15 | 16 | [Fact] 17 | public void Generic() { 18 | var mock = new MockableMock(); 19 | 20 | mock.Setup.GetInt32().Throws(); 21 | 22 | Assert.Throws(() => mock.GetInt32()); 23 | } 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /Tests/Generated/SourceMock.Generators/SourceMock.Generators.MockGenerator/global__SourceMock_Tests_Interfaces_AbstractClass.cs: -------------------------------------------------------------------------------- 1 | #nullable enable 2 | namespace SourceMock.Tests.Interfaces.Mocks { 3 | internal class AbstractClassMock : global::SourceMock.Tests.Interfaces.AbstractClass, IAbstractClassSetup, IAbstractClassCalls, SourceMock.IMock { 4 | public IAbstractClassSetup Setup => this; 5 | public IAbstractClassCalls Calls => this; 6 | 7 | private readonly SourceMock.Internal.MockMethodHandler _protectedInternalHandler = new(); 8 | SourceMock.Interfaces.IMockMethodSetup IAbstractClassSetup.ProtectedInternal() => _protectedInternalHandler.Setup(null, null); 9 | protected internal override void ProtectedInternal() => _protectedInternalHandler.Call(null, null); 10 | System.Collections.Generic.IReadOnlyList IAbstractClassCalls.ProtectedInternal() => _protectedInternalHandler.Calls(null, null, _ => SourceMock.NoArguments.Value); 11 | 12 | private readonly SourceMock.Internal.MockMethodHandler _privateProtectedHandler = new(); 13 | SourceMock.Interfaces.IMockMethodSetup IAbstractClassSetup.PrivateProtected() => _privateProtectedHandler.Setup(null, null); 14 | private protected override void PrivateProtected() => _privateProtectedHandler.Call(null, null); 15 | System.Collections.Generic.IReadOnlyList IAbstractClassCalls.PrivateProtected() => _privateProtectedHandler.Calls(null, null, _ => SourceMock.NoArguments.Value); 16 | 17 | private readonly SourceMock.Internal.MockMethodHandler _getHandler = new(); 18 | SourceMock.Interfaces.IMockMethodSetup, int> IAbstractClassSetup.Get() => _getHandler.Setup, int>(null, null); 19 | public override int Get() => _getHandler.Call, int>(null, null); 20 | System.Collections.Generic.IReadOnlyList IAbstractClassCalls.Get() => _getHandler.Calls(null, null, _ => SourceMock.NoArguments.Value); 21 | 22 | private readonly SourceMock.Internal.MockMethodHandler _getStringHandler = new(); 23 | SourceMock.Interfaces.IMockMethodSetup, string?> IAbstractClassSetup.GetString() => _getStringHandler.Setup, string?>(null, null); 24 | public override string? GetString() => _getStringHandler.Call, string?>(null, null); 25 | System.Collections.Generic.IReadOnlyList IAbstractClassCalls.GetString() => _getStringHandler.Calls(null, null, _ => SourceMock.NoArguments.Value); 26 | 27 | private readonly SourceMock.Internal.MockPropertyHandler _virtualPropertyHandler = new(false); 28 | SourceMock.Interfaces.IMockPropertySetup IAbstractClassSetup.VirtualProperty => _virtualPropertyHandler.Setup(); 29 | public override int VirtualProperty => _virtualPropertyHandler.GetterHandler.Call, int>(null, null); 30 | SourceMock.Interfaces.IMockPropertyCalls IAbstractClassCalls.VirtualProperty => _virtualPropertyHandler.Calls(); 31 | } 32 | 33 | internal interface IAbstractClassSetup { 34 | SourceMock.Interfaces.IMockMethodSetup ProtectedInternal(); 35 | SourceMock.Interfaces.IMockMethodSetup PrivateProtected(); 36 | SourceMock.Interfaces.IMockMethodSetup, int> Get(); 37 | SourceMock.Interfaces.IMockMethodSetup, string?> GetString(); 38 | SourceMock.Interfaces.IMockPropertySetup VirtualProperty { get; } 39 | } 40 | 41 | internal interface IAbstractClassCalls { 42 | System.Collections.Generic.IReadOnlyList ProtectedInternal(); 43 | System.Collections.Generic.IReadOnlyList PrivateProtected(); 44 | System.Collections.Generic.IReadOnlyList Get(); 45 | System.Collections.Generic.IReadOnlyList GetString(); 46 | SourceMock.Interfaces.IMockPropertyCalls VirtualProperty { get; } 47 | } 48 | } -------------------------------------------------------------------------------- /Tests/Generated/SourceMock.Generators/SourceMock.Generators.MockGenerator/global__SourceMock_Tests_Interfaces_Disposable.cs: -------------------------------------------------------------------------------- 1 | #nullable enable 2 | namespace SourceMock.Tests.Interfaces.Mocks { 3 | internal class DisposableMock : global::SourceMock.Tests.Interfaces.Disposable, IDisposableSetup, IDisposableCalls, SourceMock.IMock { 4 | public IDisposableSetup Setup => this; 5 | public IDisposableCalls Calls => this; 6 | 7 | private readonly SourceMock.Internal.MockMethodHandler _disposeHandler = new(); 8 | SourceMock.Interfaces.IMockMethodSetup IDisposableSetup.Dispose() => _disposeHandler.Setup(null, null); 9 | public override void Dispose() => _disposeHandler.Call(null, null); 10 | System.Collections.Generic.IReadOnlyList IDisposableCalls.Dispose() => _disposeHandler.Calls(null, null, _ => SourceMock.NoArguments.Value); 11 | } 12 | 13 | internal interface IDisposableSetup { 14 | SourceMock.Interfaces.IMockMethodSetup Dispose(); 15 | } 16 | 17 | internal interface IDisposableCalls { 18 | System.Collections.Generic.IReadOnlyList Dispose(); 19 | } 20 | } -------------------------------------------------------------------------------- /Tests/Generated/SourceMock.Generators/SourceMock.Generators.MockGenerator/global__SourceMock_Tests_Interfaces_IEmptyInterface.cs: -------------------------------------------------------------------------------- 1 | #nullable enable 2 | namespace SourceMock.Tests.Interfaces.Mocks { 3 | internal class EmptyInterfaceMock : global::SourceMock.Tests.Interfaces.IEmptyInterface, IEmptyInterfaceSetup, IEmptyInterfaceCalls, SourceMock.IMock { 4 | public IEmptyInterfaceSetup Setup => this; 5 | public IEmptyInterfaceCalls Calls => this; 6 | } 7 | 8 | internal interface IEmptyInterfaceSetup { 9 | } 10 | 11 | internal interface IEmptyInterfaceCalls { 12 | } 13 | } -------------------------------------------------------------------------------- /Tests/Generated/SourceMock.Generators/SourceMock.Generators.MockGenerator/global__SourceMock_Tests_Interfaces_IInheritedInteface.cs: -------------------------------------------------------------------------------- 1 | #nullable enable 2 | namespace SourceMock.Tests.Interfaces.Mocks { 3 | internal class InheritedIntefaceMock : global::SourceMock.Tests.Interfaces.IInheritedInteface, IInheritedIntefaceSetup, IInheritedIntefaceCalls, SourceMock.IMock { 4 | public IInheritedIntefaceSetup Setup => this; 5 | public IInheritedIntefaceCalls Calls => this; 6 | 7 | private readonly SourceMock.Internal.MockMethodHandler _methodHandler = new(); 8 | SourceMock.Interfaces.IMockMethodSetup IInheritedIntefaceSetup.Method() => _methodHandler.Setup(null, null); 9 | public void Method() => _methodHandler.Call(null, null); 10 | System.Collections.Generic.IReadOnlyList IInheritedIntefaceCalls.Method() => _methodHandler.Calls(null, null, _ => SourceMock.NoArguments.Value); 11 | 12 | private readonly SourceMock.Internal.MockMethodHandler _getStringHandler = new(); 13 | SourceMock.Interfaces.IMockMethodSetup, string> IInheritedIntefaceSetup.GetString() => _getStringHandler.Setup, string>(null, null); 14 | public string GetString() => _getStringHandler.Call, string>(null, null); 15 | System.Collections.Generic.IReadOnlyList IInheritedIntefaceCalls.GetString() => _getStringHandler.Calls(null, null, _ => SourceMock.NoArguments.Value); 16 | } 17 | 18 | internal interface IInheritedIntefaceSetup { 19 | SourceMock.Interfaces.IMockMethodSetup Method(); 20 | SourceMock.Interfaces.IMockMethodSetup, string> GetString(); 21 | } 22 | 23 | internal interface IInheritedIntefaceCalls { 24 | System.Collections.Generic.IReadOnlyList Method(); 25 | System.Collections.Generic.IReadOnlyList GetString(); 26 | } 27 | } -------------------------------------------------------------------------------- /Tests/Generated/SourceMock.Generators/SourceMock.Generators.MockGenerator/global__SourceMock_Tests_Interfaces_IInternalInterface.cs: -------------------------------------------------------------------------------- 1 | #nullable enable 2 | namespace SourceMock.Tests.Interfaces.Mocks { 3 | internal class InternalInterfaceMock : global::SourceMock.Tests.Interfaces.IInternalInterface, IInternalInterfaceSetup, IInternalInterfaceCalls, SourceMock.IMock { 4 | public IInternalInterfaceSetup Setup => this; 5 | public IInternalInterfaceCalls Calls => this; 6 | } 7 | 8 | internal interface IInternalInterfaceSetup { 9 | } 10 | 11 | internal interface IInternalInterfaceCalls { 12 | } 13 | } -------------------------------------------------------------------------------- /Tests/Generated/SourceMock.Generators/SourceMock.Generators.MockGenerator/global__SourceMock_Tests_Interfaces_IMockable.cs: -------------------------------------------------------------------------------- 1 | #nullable enable 2 | namespace SourceMock.Tests.Interfaces.Mocks { 3 | internal class MockableMock : global::SourceMock.Tests.Interfaces.IMockable, IMockableSetup, IMockableCalls, SourceMock.IMock { 4 | public IMockableSetup Setup => this; 5 | public IMockableCalls Calls => this; 6 | 7 | private readonly SourceMock.Internal.MockMethodHandler _getInt32Handler = new(); 8 | SourceMock.Interfaces.IMockMethodSetup, int> IMockableSetup.GetInt32() => _getInt32Handler.Setup, int>(null, null); 9 | public int GetInt32() => _getInt32Handler.Call, int>(null, null); 10 | System.Collections.Generic.IReadOnlyList IMockableCalls.GetInt32() => _getInt32Handler.Calls(null, null, _ => SourceMock.NoArguments.Value); 11 | 12 | private readonly SourceMock.Internal.MockMethodHandler _getInt32NullableHandler = new(); 13 | SourceMock.Interfaces.IMockMethodSetup, int?> IMockableSetup.GetInt32Nullable() => _getInt32NullableHandler.Setup, int?>(null, null); 14 | public int? GetInt32Nullable() => _getInt32NullableHandler.Call, int?>(null, null); 15 | System.Collections.Generic.IReadOnlyList IMockableCalls.GetInt32Nullable() => _getInt32NullableHandler.Calls(null, null, _ => SourceMock.NoArguments.Value); 16 | 17 | private readonly SourceMock.Internal.MockMethodHandler _getStringHandler = new(); 18 | SourceMock.Interfaces.IMockMethodSetup, string> IMockableSetup.GetString() => _getStringHandler.Setup, string>(null, null); 19 | public string GetString() => _getStringHandler.Call, string>(null, null); 20 | System.Collections.Generic.IReadOnlyList IMockableCalls.GetString() => _getStringHandler.Calls(null, null, _ => SourceMock.NoArguments.Value); 21 | 22 | private readonly SourceMock.Internal.MockMethodHandler _getStringNullableHandler = new(); 23 | SourceMock.Interfaces.IMockMethodSetup, string?> IMockableSetup.GetStringNullable() => _getStringNullableHandler.Setup, string?>(null, null); 24 | public string? GetStringNullable() => _getStringNullableHandler.Call, string?>(null, null); 25 | System.Collections.Generic.IReadOnlyList IMockableCalls.GetStringNullable() => _getStringNullableHandler.Calls(null, null, _ => SourceMock.NoArguments.Value); 26 | 27 | private readonly SourceMock.Internal.MockMethodHandler _getMockable2Handler = new(); 28 | SourceMock.Interfaces.IMockMethodSetup, global::SourceMock.Tests.Interfaces.IMockable2> IMockableSetup.GetMockable2() => _getMockable2Handler.Setup, global::SourceMock.Tests.Interfaces.IMockable2>(null, null); 29 | public global::SourceMock.Tests.Interfaces.IMockable2 GetMockable2() => _getMockable2Handler.Call, global::SourceMock.Tests.Interfaces.IMockable2>(null, null); 30 | System.Collections.Generic.IReadOnlyList IMockableCalls.GetMockable2() => _getMockable2Handler.Calls(null, null, _ => SourceMock.NoArguments.Value); 31 | 32 | private readonly SourceMock.Internal.MockMethodHandler _getStringAsyncHandler = new(); 33 | SourceMock.Interfaces.IMockMethodSetup>, global::System.Threading.Tasks.Task> IMockableSetup.GetStringAsync() => _getStringAsyncHandler.Setup>, global::System.Threading.Tasks.Task>(null, null); 34 | public global::System.Threading.Tasks.Task GetStringAsync() => _getStringAsyncHandler.Call>, global::System.Threading.Tasks.Task>(null, null); 35 | System.Collections.Generic.IReadOnlyList IMockableCalls.GetStringAsync() => _getStringAsyncHandler.Calls(null, null, _ => SourceMock.NoArguments.Value); 36 | 37 | private readonly SourceMock.Internal.MockMethodHandler _parseToInt32Handler = new(); 38 | SourceMock.Interfaces.IMockMethodSetup, int> IMockableSetup.ParseToInt32(SourceMock.Internal.MockArgumentMatcher value) => _parseToInt32Handler.Setup, int>(null, new SourceMock.Internal.IMockArgumentMatcher[] { value }); 39 | public int ParseToInt32(string? value) => _parseToInt32Handler.Call, int>(null, new object?[] { value }); 40 | System.Collections.Generic.IReadOnlyList IMockableCalls.ParseToInt32(SourceMock.Internal.MockArgumentMatcher value) => _parseToInt32Handler.Calls(null, new SourceMock.Internal.IMockArgumentMatcher[] { value }, args => ((string?)args[0])); 41 | 42 | private readonly SourceMock.Internal.MockMethodHandler _testInterfaceHandler = new(); 43 | SourceMock.Interfaces.IMockMethodSetup, bool> IMockableSetup.TestInterface(SourceMock.Internal.MockArgumentMatcher value) => _testInterfaceHandler.Setup, bool>(null, new SourceMock.Internal.IMockArgumentMatcher[] { value }); 44 | public bool TestInterface(global::SourceMock.Tests.Interfaces.IEmptyInterface value) => _testInterfaceHandler.Call, bool>(null, new object?[] { value }); 45 | System.Collections.Generic.IReadOnlyList IMockableCalls.TestInterface(SourceMock.Internal.MockArgumentMatcher value) => _testInterfaceHandler.Calls(null, new SourceMock.Internal.IMockArgumentMatcher[] { value }, args => ((global::SourceMock.Tests.Interfaces.IEmptyInterface)args[0]!)); 46 | 47 | private readonly SourceMock.Internal.MockMethodHandler _divideHandler = new(); 48 | SourceMock.Interfaces.IMockMethodSetup, double> IMockableSetup.Divide(SourceMock.Internal.MockArgumentMatcher value1, SourceMock.Internal.MockArgumentMatcher value2) => _divideHandler.Setup, double>(null, new SourceMock.Internal.IMockArgumentMatcher[] { value1, value2 }); 49 | public double Divide(double value1, double value2) => _divideHandler.Call, double>(null, new object?[] { value1, value2 }); 50 | System.Collections.Generic.IReadOnlyList<(double value1, double value2)> IMockableCalls.Divide(SourceMock.Internal.MockArgumentMatcher value1, SourceMock.Internal.MockArgumentMatcher value2) => _divideHandler.Calls(null, new SourceMock.Internal.IMockArgumentMatcher[] { value1, value2 }, args => ((double)args[0]!, (double)args[1]!)); 51 | 52 | private readonly SourceMock.Internal.MockMethodHandler _sumHandler = new(); 53 | SourceMock.Interfaces.IMockMethodSetup, int> IMockableSetup.Sum(SourceMock.Internal.MockArgumentMatcher value1, SourceMock.Internal.MockArgumentMatcher value2) => _sumHandler.Setup, int>(null, new SourceMock.Internal.IMockArgumentMatcher[] { value1, value2 }); 54 | public int Sum(int value1, int value2) => _sumHandler.Call, int>(null, new object?[] { value1, value2 }); 55 | System.Collections.Generic.IReadOnlyList<(int value1, int value2)> IMockableCalls.Sum(SourceMock.Internal.MockArgumentMatcher value1, SourceMock.Internal.MockArgumentMatcher value2) => _sumHandler.Calls(null, new SourceMock.Internal.IMockArgumentMatcher[] { value1, value2 }, args => ((int)args[0]!, (int)args[1]!)); 56 | 57 | private readonly SourceMock.Internal.MockMethodHandler _sum2Handler = new(); 58 | SourceMock.Interfaces.IMockMethodSetup, int> IMockableSetup.Sum(SourceMock.Internal.MockArgumentMatcher value1, SourceMock.Internal.MockArgumentMatcher value2, SourceMock.Internal.MockArgumentMatcher value3) => _sum2Handler.Setup, int>(null, new SourceMock.Internal.IMockArgumentMatcher[] { value1, value2, value3 }); 59 | public int Sum(int value1, int value2, int value3) => _sum2Handler.Call, int>(null, new object?[] { value1, value2, value3 }); 60 | System.Collections.Generic.IReadOnlyList<(int value1, int value2, int value3)> IMockableCalls.Sum(SourceMock.Internal.MockArgumentMatcher value1, SourceMock.Internal.MockArgumentMatcher value2, SourceMock.Internal.MockArgumentMatcher value3) => _sum2Handler.Calls(null, new SourceMock.Internal.IMockArgumentMatcher[] { value1, value2, value3 }, args => ((int)args[0]!, (int)args[1]!, (int)args[2]!)); 61 | 62 | private readonly SourceMock.Internal.MockMethodHandler _executeHandler = new(); 63 | SourceMock.Interfaces.IMockMethodSetup IMockableSetup.Execute() => _executeHandler.Setup(null, null); 64 | public void Execute() => _executeHandler.Call(null, null); 65 | System.Collections.Generic.IReadOnlyList IMockableCalls.Execute() => _executeHandler.Calls(null, null, _ => SourceMock.NoArguments.Value); 66 | 67 | private readonly SourceMock.Internal.MockMethodHandler _execute2Handler = new(); 68 | SourceMock.Interfaces.IMockMethodSetup> IMockableSetup.Execute(SourceMock.Internal.MockArgumentMatcher value) => _execute2Handler.Setup, SourceMock.Internal.VoidReturn>(null, new SourceMock.Internal.IMockArgumentMatcher[] { value }); 69 | public void Execute(global::SourceMock.Tests.Interfaces.IEmptyInterface value) => _execute2Handler.Call, SourceMock.Internal.VoidReturn>(null, new object?[] { value }); 70 | System.Collections.Generic.IReadOnlyList IMockableCalls.Execute(SourceMock.Internal.MockArgumentMatcher value) => _execute2Handler.Calls(null, new SourceMock.Internal.IMockArgumentMatcher[] { value }, args => ((global::SourceMock.Tests.Interfaces.IEmptyInterface)args[0]!)); 71 | 72 | private readonly SourceMock.Internal.MockPropertyHandler _countHandler = new(false); 73 | SourceMock.Interfaces.IMockPropertySetup IMockableSetup.Count => _countHandler.Setup(); 74 | public int Count => _countHandler.GetterHandler.Call, int>(null, null); 75 | SourceMock.Interfaces.IMockPropertyCalls IMockableCalls.Count => _countHandler.Calls(); 76 | 77 | private readonly SourceMock.Internal.MockPropertyHandler _nameHandler = new(true); 78 | SourceMock.Interfaces.IMockSettablePropertySetup IMockableSetup.Name => _nameHandler.Setup(); 79 | public string Name { 80 | get => _nameHandler.GetterHandler.Call, string>(null, null); 81 | set => _nameHandler.SetterHandler.Call, string>(null, new object?[] { value }); 82 | } 83 | SourceMock.Interfaces.IMockSettablePropertyCalls IMockableCalls.Name => _nameHandler.Calls(); 84 | } 85 | 86 | internal interface IMockableSetup { 87 | SourceMock.Interfaces.IMockMethodSetup, int> GetInt32(); 88 | SourceMock.Interfaces.IMockMethodSetup, int?> GetInt32Nullable(); 89 | SourceMock.Interfaces.IMockMethodSetup, string> GetString(); 90 | SourceMock.Interfaces.IMockMethodSetup, string?> GetStringNullable(); 91 | SourceMock.Interfaces.IMockMethodSetup, global::SourceMock.Tests.Interfaces.IMockable2> GetMockable2(); 92 | SourceMock.Interfaces.IMockMethodSetup>, global::System.Threading.Tasks.Task> GetStringAsync(); 93 | SourceMock.Interfaces.IMockMethodSetup, int> ParseToInt32(SourceMock.Internal.MockArgumentMatcher value = default); 94 | SourceMock.Interfaces.IMockMethodSetup, bool> TestInterface(SourceMock.Internal.MockArgumentMatcher value = default); 95 | SourceMock.Interfaces.IMockMethodSetup, double> Divide(SourceMock.Internal.MockArgumentMatcher value1 = default, SourceMock.Internal.MockArgumentMatcher value2 = default); 96 | SourceMock.Interfaces.IMockMethodSetup, int> Sum(SourceMock.Internal.MockArgumentMatcher value1 = default, SourceMock.Internal.MockArgumentMatcher value2 = default); 97 | SourceMock.Interfaces.IMockMethodSetup, int> Sum(SourceMock.Internal.MockArgumentMatcher value1 = default, SourceMock.Internal.MockArgumentMatcher value2 = default, SourceMock.Internal.MockArgumentMatcher value3 = default); 98 | SourceMock.Interfaces.IMockMethodSetup Execute(); 99 | SourceMock.Interfaces.IMockMethodSetup> Execute(SourceMock.Internal.MockArgumentMatcher value = default); 100 | SourceMock.Interfaces.IMockPropertySetup Count { get; } 101 | SourceMock.Interfaces.IMockSettablePropertySetup Name { get; } 102 | } 103 | 104 | internal interface IMockableCalls { 105 | System.Collections.Generic.IReadOnlyList GetInt32(); 106 | System.Collections.Generic.IReadOnlyList GetInt32Nullable(); 107 | System.Collections.Generic.IReadOnlyList GetString(); 108 | System.Collections.Generic.IReadOnlyList GetStringNullable(); 109 | System.Collections.Generic.IReadOnlyList GetMockable2(); 110 | System.Collections.Generic.IReadOnlyList GetStringAsync(); 111 | System.Collections.Generic.IReadOnlyList ParseToInt32(SourceMock.Internal.MockArgumentMatcher value = default); 112 | System.Collections.Generic.IReadOnlyList TestInterface(SourceMock.Internal.MockArgumentMatcher value = default); 113 | System.Collections.Generic.IReadOnlyList<(double value1, double value2)> Divide(SourceMock.Internal.MockArgumentMatcher value1 = default, SourceMock.Internal.MockArgumentMatcher value2 = default); 114 | System.Collections.Generic.IReadOnlyList<(int value1, int value2)> Sum(SourceMock.Internal.MockArgumentMatcher value1 = default, SourceMock.Internal.MockArgumentMatcher value2 = default); 115 | System.Collections.Generic.IReadOnlyList<(int value1, int value2, int value3)> Sum(SourceMock.Internal.MockArgumentMatcher value1 = default, SourceMock.Internal.MockArgumentMatcher value2 = default, SourceMock.Internal.MockArgumentMatcher value3 = default); 116 | System.Collections.Generic.IReadOnlyList Execute(); 117 | System.Collections.Generic.IReadOnlyList Execute(SourceMock.Internal.MockArgumentMatcher value = default); 118 | SourceMock.Interfaces.IMockPropertyCalls Count { get; } 119 | SourceMock.Interfaces.IMockSettablePropertyCalls Name { get; } 120 | } 121 | } -------------------------------------------------------------------------------- /Tests/Generated/SourceMock.Generators/SourceMock.Generators.MockGenerator/global__SourceMock_Tests_Interfaces_IMockable2.cs: -------------------------------------------------------------------------------- 1 | #nullable enable 2 | namespace SourceMock.Tests.Interfaces.Mocks { 3 | internal class Mockable2Mock : global::SourceMock.Tests.Interfaces.IMockable2, IMockable2Setup, IMockable2Calls, SourceMock.IMock { 4 | public IMockable2Setup Setup => this; 5 | public IMockable2Calls Calls => this; 6 | 7 | private readonly SourceMock.Internal.MockMethodHandler _getStringHandler = new(); 8 | SourceMock.Interfaces.IMockMethodSetup, string> IMockable2Setup.GetString() => _getStringHandler.Setup, string>(null, null); 9 | public string GetString() => _getStringHandler.Call, string>(null, null); 10 | System.Collections.Generic.IReadOnlyList IMockable2Calls.GetString() => _getStringHandler.Calls(null, null, _ => SourceMock.NoArguments.Value); 11 | } 12 | 13 | internal interface IMockable2Setup { 14 | SourceMock.Interfaces.IMockMethodSetup, string> GetString(); 15 | } 16 | 17 | internal interface IMockable2Calls { 18 | System.Collections.Generic.IReadOnlyList GetString(); 19 | } 20 | } -------------------------------------------------------------------------------- /Tests/Generated/SourceMock.Generators/SourceMock.Generators.MockGenerator/global__SourceMock_Tests_Interfaces_INeedsGenericConstraints.cs: -------------------------------------------------------------------------------- 1 | #nullable enable 2 | namespace SourceMock.Tests.Interfaces.Mocks { 3 | internal class NeedsGenericConstraintsMock : global::SourceMock.Tests.Interfaces.INeedsGenericConstraints, INeedsGenericConstraintsSetup, INeedsGenericConstraintsCalls, SourceMock.IMock { 4 | public INeedsGenericConstraintsSetup Setup => this; 5 | public INeedsGenericConstraintsCalls Calls => this; 6 | 7 | private readonly SourceMock.Internal.MockMethodHandler _methodHandler = new(); 8 | SourceMock.Interfaces.IMockMethodSetup> INeedsGenericConstraintsSetup.Method() => _methodHandler.Setup, SourceMock.Internal.VoidReturn>(new[] { typeof(TNotNull), typeof(TClassNotNull), typeof(TNullableClass), typeof(TStruct), typeof(TUnmanaged) }, null); 9 | public void Method() 10 | where TNotNull: notnull, global::SourceMock.Tests.Interfaces.IMockable, new() 11 | where TClassNotNull: class, global::SourceMock.Tests.Interfaces.IMockable, new() 12 | where TNullableClass: class?, global::SourceMock.Tests.Interfaces.IMockable, new() 13 | where TStruct: struct, global::SourceMock.Tests.Interfaces.IMockable 14 | where TUnmanaged: unmanaged, global::SourceMock.Tests.Interfaces.IMockable 15 | => _methodHandler.Call, SourceMock.Internal.VoidReturn>(new[] { typeof(TNotNull), typeof(TClassNotNull), typeof(TNullableClass), typeof(TStruct), typeof(TUnmanaged) }, null); 16 | System.Collections.Generic.IReadOnlyList INeedsGenericConstraintsCalls.Method() => _methodHandler.Calls(new[] { typeof(TNotNull), typeof(TClassNotNull), typeof(TNullableClass), typeof(TStruct), typeof(TUnmanaged) }, null, _ => SourceMock.NoArguments.Value); 17 | } 18 | 19 | internal static class NeedsGenericConstraintsDelegates { 20 | public delegate void MethodAction(); 21 | } 22 | 23 | internal interface INeedsGenericConstraintsSetup { 24 | SourceMock.Interfaces.IMockMethodSetup> Method() 25 | where TNotNull: notnull, global::SourceMock.Tests.Interfaces.IMockable, new() 26 | where TClassNotNull: class, global::SourceMock.Tests.Interfaces.IMockable, new() 27 | where TNullableClass: class?, global::SourceMock.Tests.Interfaces.IMockable, new() 28 | where TStruct: struct, global::SourceMock.Tests.Interfaces.IMockable 29 | where TUnmanaged: unmanaged, global::SourceMock.Tests.Interfaces.IMockable; 30 | } 31 | 32 | internal interface INeedsGenericConstraintsCalls { 33 | System.Collections.Generic.IReadOnlyList Method() 34 | where TNotNull: notnull, global::SourceMock.Tests.Interfaces.IMockable, new() 35 | where TClassNotNull: class, global::SourceMock.Tests.Interfaces.IMockable, new() 36 | where TNullableClass: class?, global::SourceMock.Tests.Interfaces.IMockable, new() 37 | where TStruct: struct, global::SourceMock.Tests.Interfaces.IMockable 38 | where TUnmanaged: unmanaged, global::SourceMock.Tests.Interfaces.IMockable; 39 | } 40 | } -------------------------------------------------------------------------------- /Tests/Generated/SourceMock.Generators/SourceMock.Generators.MockGenerator/global__SourceMock_Tests_Interfaces_INeedsGenericConstraints_TNotNull__TClass__TNullableClass__TStruct__TUnmanaged_.cs: -------------------------------------------------------------------------------- 1 | #nullable enable 2 | namespace SourceMock.Tests.Interfaces.Mocks { 3 | internal class NeedsGenericConstraintsMock : global::SourceMock.Tests.Interfaces.INeedsGenericConstraints, INeedsGenericConstraintsSetup, INeedsGenericConstraintsCalls, SourceMock.IMock> 4 | where TNotNull: notnull, global::SourceMock.Tests.Interfaces.IMockable, new() 5 | where TClass: class, global::SourceMock.Tests.Interfaces.IMockable, new() 6 | where TNullableClass: class?, global::SourceMock.Tests.Interfaces.IMockable, new() 7 | where TStruct: struct, global::SourceMock.Tests.Interfaces.IMockable 8 | where TUnmanaged: unmanaged, global::SourceMock.Tests.Interfaces.IMockable 9 | { 10 | public INeedsGenericConstraintsSetup Setup => this; 11 | public INeedsGenericConstraintsCalls Calls => this; 12 | } 13 | 14 | internal interface INeedsGenericConstraintsSetup { 15 | } 16 | 17 | internal interface INeedsGenericConstraintsCalls { 18 | } 19 | } -------------------------------------------------------------------------------- /Tests/Generated/SourceMock.Generators/SourceMock.Generators.MockGenerator/global__SourceMock_Tests_Interfaces_INeedsGenerics.cs: -------------------------------------------------------------------------------- 1 | #nullable enable 2 | namespace SourceMock.Tests.Interfaces.Mocks { 3 | internal class NeedsGenericsMock : global::SourceMock.Tests.Interfaces.INeedsGenerics, INeedsGenericsSetup, INeedsGenericsCalls, SourceMock.IMock { 4 | public INeedsGenericsSetup Setup => this; 5 | public INeedsGenericsCalls Calls => this; 6 | 7 | private readonly SourceMock.Internal.MockMethodHandler _parseHandler = new(); 8 | SourceMock.Interfaces.IMockMethodSetup, T> INeedsGenericsSetup.Parse(SourceMock.Internal.MockArgumentMatcher value) => _parseHandler.Setup, T>(new[] { typeof(T) }, new SourceMock.Internal.IMockArgumentMatcher[] { value }); 9 | public T Parse(string value) => _parseHandler.Call, T>(new[] { typeof(T) }, new object?[] { value }); 10 | System.Collections.Generic.IReadOnlyList INeedsGenericsCalls.Parse(SourceMock.Internal.MockArgumentMatcher value) => _parseHandler.Calls(new[] { typeof(T) }, new SourceMock.Internal.IMockArgumentMatcher[] { value }, args => ((string)args[0]!)); 11 | 12 | private readonly SourceMock.Internal.MockMethodHandler _getHandler = new(); 13 | SourceMock.Interfaces.IMockMethodSetup, T> INeedsGenericsSetup.Get() => _getHandler.Setup, T>(new[] { typeof(T) }, null); 14 | public T Get() => _getHandler.Call, T>(new[] { typeof(T) }, null); 15 | System.Collections.Generic.IReadOnlyList INeedsGenericsCalls.Get() => _getHandler.Calls(new[] { typeof(T) }, null, _ => SourceMock.NoArguments.Value); 16 | 17 | private readonly SourceMock.Internal.MockMethodHandler _setOptionalHandler = new(); 18 | SourceMock.Interfaces.IMockMethodSetup> INeedsGenericsSetup.SetOptional(SourceMock.Internal.MockArgumentMatcher value) where T: default => _setOptionalHandler.Setup, SourceMock.Internal.VoidReturn>(new[] { typeof(T) }, new SourceMock.Internal.IMockArgumentMatcher[] { value }); 19 | public void SetOptional(T? value) => _setOptionalHandler.Call, SourceMock.Internal.VoidReturn>(new[] { typeof(T) }, new object?[] { value }); 20 | System.Collections.Generic.IReadOnlyList INeedsGenericsCalls.SetOptional(SourceMock.Internal.MockArgumentMatcher value) where T: default => _setOptionalHandler.Calls(new[] { typeof(T) }, new SourceMock.Internal.IMockArgumentMatcher[] { value }, args => ((T?)args[0])); 21 | 22 | private readonly SourceMock.Internal.MockMethodHandler _getAllHandler = new(); 23 | SourceMock.Interfaces.IMockMethodSetup, global::System.Collections.Generic.IEnumerable> INeedsGenericsSetup.GetAll() where T: default => _getAllHandler.Setup, global::System.Collections.Generic.IEnumerable>(new[] { typeof(T) }, null); 24 | public global::System.Collections.Generic.IEnumerable GetAll() => _getAllHandler.Call, global::System.Collections.Generic.IEnumerable>(new[] { typeof(T) }, null); 25 | System.Collections.Generic.IReadOnlyList INeedsGenericsCalls.GetAll() where T: default => _getAllHandler.Calls(new[] { typeof(T) }, null, _ => SourceMock.NoArguments.Value); 26 | } 27 | 28 | internal static class NeedsGenericsDelegates { 29 | public delegate T ParseFunc(string value); 30 | public delegate T GetFunc(); 31 | public delegate void SetOptionalAction(T? value); 32 | public delegate global::System.Collections.Generic.IEnumerable GetAllFunc(); 33 | } 34 | 35 | internal interface INeedsGenericsSetup { 36 | SourceMock.Interfaces.IMockMethodSetup, T> Parse(SourceMock.Internal.MockArgumentMatcher value = default); 37 | SourceMock.Interfaces.IMockMethodSetup, T> Get(); 38 | SourceMock.Interfaces.IMockMethodSetup> SetOptional(SourceMock.Internal.MockArgumentMatcher value = default); 39 | SourceMock.Interfaces.IMockMethodSetup, global::System.Collections.Generic.IEnumerable> GetAll(); 40 | } 41 | 42 | internal interface INeedsGenericsCalls { 43 | System.Collections.Generic.IReadOnlyList Parse(SourceMock.Internal.MockArgumentMatcher value = default); 44 | System.Collections.Generic.IReadOnlyList Get(); 45 | System.Collections.Generic.IReadOnlyList SetOptional(SourceMock.Internal.MockArgumentMatcher value = default); 46 | System.Collections.Generic.IReadOnlyList GetAll(); 47 | } 48 | } -------------------------------------------------------------------------------- /Tests/Generated/SourceMock.Generators/SourceMock.Generators.MockGenerator/global__SourceMock_Tests_Interfaces_INeedsGenerics_U_.cs: -------------------------------------------------------------------------------- 1 | #nullable enable 2 | namespace SourceMock.Tests.Interfaces.Mocks { 3 | internal class NeedsGenericsMock : global::SourceMock.Tests.Interfaces.INeedsGenerics, INeedsGenericsSetup, INeedsGenericsCalls, SourceMock.IMock> { 4 | public INeedsGenericsSetup Setup => this; 5 | public INeedsGenericsCalls Calls => this; 6 | 7 | private readonly SourceMock.Internal.MockMethodHandler _getHandler = new(); 8 | SourceMock.Interfaces.IMockMethodSetup, U> INeedsGenericsSetup.Get() => _getHandler.Setup, U>(null, null); 9 | public U Get() => _getHandler.Call, U>(null, null); 10 | System.Collections.Generic.IReadOnlyList INeedsGenericsCalls.Get() => _getHandler.Calls(null, null, _ => SourceMock.NoArguments.Value); 11 | 12 | private readonly SourceMock.Internal.MockMethodHandler _convertHandler = new(); 13 | SourceMock.Interfaces.IMockMethodSetup.ConvertFunc, T> INeedsGenericsSetup.Convert(SourceMock.Internal.MockArgumentMatcher value) => _convertHandler.Setup.ConvertFunc, T>(new[] { typeof(T) }, new SourceMock.Internal.IMockArgumentMatcher[] { value }); 14 | public T Convert(U value) => _convertHandler.Call.ConvertFunc, T>(new[] { typeof(T) }, new object?[] { value }); 15 | System.Collections.Generic.IReadOnlyList INeedsGenericsCalls.Convert(SourceMock.Internal.MockArgumentMatcher value) => _convertHandler.Calls(new[] { typeof(T) }, new SourceMock.Internal.IMockArgumentMatcher[] { value }, args => ((U)args[0]!)); 16 | 17 | private readonly SourceMock.Internal.MockMethodHandler _castHandler = new(); 18 | SourceMock.Interfaces.IMockMethodSetup.CastFunc, T> INeedsGenericsSetup.Cast(SourceMock.Internal.MockArgumentMatcher value) => _castHandler.Setup.CastFunc, T>(new[] { typeof(T) }, new SourceMock.Internal.IMockArgumentMatcher[] { value }); 19 | public T Cast(U value) 20 | where T: U 21 | => _castHandler.Call.CastFunc, T>(new[] { typeof(T) }, new object?[] { value }); 22 | System.Collections.Generic.IReadOnlyList INeedsGenericsCalls.Cast(SourceMock.Internal.MockArgumentMatcher value) => _castHandler.Calls(new[] { typeof(T) }, new SourceMock.Internal.IMockArgumentMatcher[] { value }, args => ((U)args[0]!)); 23 | } 24 | 25 | internal static class NeedsGenericsDelegates { 26 | public delegate T ConvertFunc(U value); 27 | public delegate T CastFunc(U value); 28 | } 29 | 30 | internal interface INeedsGenericsSetup { 31 | SourceMock.Interfaces.IMockMethodSetup, U> Get(); 32 | SourceMock.Interfaces.IMockMethodSetup.ConvertFunc, T> Convert(SourceMock.Internal.MockArgumentMatcher value = default); 33 | SourceMock.Interfaces.IMockMethodSetup.CastFunc, T> Cast(SourceMock.Internal.MockArgumentMatcher value = default) 34 | where T: U; 35 | } 36 | 37 | internal interface INeedsGenericsCalls { 38 | System.Collections.Generic.IReadOnlyList Get(); 39 | System.Collections.Generic.IReadOnlyList Convert(SourceMock.Internal.MockArgumentMatcher value = default); 40 | System.Collections.Generic.IReadOnlyList Cast(SourceMock.Internal.MockArgumentMatcher value = default) 41 | where T: U; 42 | } 43 | } -------------------------------------------------------------------------------- /Tests/Generated/SourceMock.Generators/SourceMock.Generators.MockGenerator/global__SourceMock_Tests_Interfaces_INeedsOtherDefaults.cs: -------------------------------------------------------------------------------- 1 | #nullable enable 2 | namespace SourceMock.Tests.Interfaces.Mocks { 3 | internal class NeedsOtherDefaultsMock : global::SourceMock.Tests.Interfaces.INeedsOtherDefaults, INeedsOtherDefaultsSetup, INeedsOtherDefaultsCalls, SourceMock.IMock { 4 | public INeedsOtherDefaultsSetup Setup => this; 5 | public INeedsOtherDefaultsCalls Calls => this; 6 | 7 | private readonly SourceMock.Internal.MockMethodHandler _executeAsyncHandler = new(); 8 | SourceMock.Interfaces.IMockMethodSetup, global::System.Threading.Tasks.Task> INeedsOtherDefaultsSetup.ExecuteAsync() => _executeAsyncHandler.Setup, global::System.Threading.Tasks.Task>(null, null); 9 | public global::System.Threading.Tasks.Task ExecuteAsync() => _executeAsyncHandler.Call, global::System.Threading.Tasks.Task>(null, null); 10 | System.Collections.Generic.IReadOnlyList INeedsOtherDefaultsCalls.ExecuteAsync() => _executeAsyncHandler.Calls(null, null, _ => SourceMock.NoArguments.Value); 11 | 12 | private readonly SourceMock.Internal.MockMethodHandler _getStringAsyncHandler = new(); 13 | SourceMock.Interfaces.IMockMethodSetup>, global::System.Threading.Tasks.Task> INeedsOtherDefaultsSetup.GetStringAsync() => _getStringAsyncHandler.Setup>, global::System.Threading.Tasks.Task>(null, null); 14 | public global::System.Threading.Tasks.Task GetStringAsync() => _getStringAsyncHandler.Call>, global::System.Threading.Tasks.Task>(null, null); 15 | System.Collections.Generic.IReadOnlyList INeedsOtherDefaultsCalls.GetStringAsync() => _getStringAsyncHandler.Calls(null, null, _ => SourceMock.NoArguments.Value); 16 | 17 | private readonly SourceMock.Internal.MockMethodHandler _getListAsyncHandler = new(); 18 | SourceMock.Interfaces.IMockMethodSetup>>, global::System.Threading.Tasks.Task>> INeedsOtherDefaultsSetup.GetListAsync() => _getListAsyncHandler.Setup>>, global::System.Threading.Tasks.Task>>(null, null); 19 | public global::System.Threading.Tasks.Task> GetListAsync() => _getListAsyncHandler.Call>>, global::System.Threading.Tasks.Task>>(null, null); 20 | System.Collections.Generic.IReadOnlyList INeedsOtherDefaultsCalls.GetListAsync() => _getListAsyncHandler.Calls(null, null, _ => SourceMock.NoArguments.Value); 21 | } 22 | 23 | internal interface INeedsOtherDefaultsSetup { 24 | SourceMock.Interfaces.IMockMethodSetup, global::System.Threading.Tasks.Task> ExecuteAsync(); 25 | SourceMock.Interfaces.IMockMethodSetup>, global::System.Threading.Tasks.Task> GetStringAsync(); 26 | SourceMock.Interfaces.IMockMethodSetup>>, global::System.Threading.Tasks.Task>> GetListAsync(); 27 | } 28 | 29 | internal interface INeedsOtherDefaultsCalls { 30 | System.Collections.Generic.IReadOnlyList ExecuteAsync(); 31 | System.Collections.Generic.IReadOnlyList GetStringAsync(); 32 | System.Collections.Generic.IReadOnlyList GetListAsync(); 33 | } 34 | } -------------------------------------------------------------------------------- /Tests/Generated/SourceMock.Generators/SourceMock.Generators.MockGenerator/global__SourceMock_Tests_Interfaces_INeedsParameterModifiers.cs: -------------------------------------------------------------------------------- 1 | #nullable enable 2 | namespace SourceMock.Tests.Interfaces.Mocks { 3 | internal class NeedsParameterModifiersMock : global::SourceMock.Tests.Interfaces.INeedsParameterModifiers, INeedsParameterModifiersSetup, INeedsParameterModifiersCalls, SourceMock.IMock { 4 | public INeedsParameterModifiersSetup Setup => this; 5 | public INeedsParameterModifiersCalls Calls => this; 6 | 7 | private readonly SourceMock.Internal.MockMethodHandler _testInHandler = new(); 8 | SourceMock.Interfaces.IMockMethodSetup INeedsParameterModifiersSetup.TestIn(SourceMock.Internal.MockArgumentMatcher value) => _testInHandler.Setup(null, new SourceMock.Internal.IMockArgumentMatcher[] { value }); 9 | public int TestIn(in int value) => _testInHandler.Call(null, new object?[] { value }); 10 | System.Collections.Generic.IReadOnlyList INeedsParameterModifiersCalls.TestIn(SourceMock.Internal.MockArgumentMatcher value) => _testInHandler.Calls(null, new SourceMock.Internal.IMockArgumentMatcher[] { value }, args => ((int)args[0]!)); 11 | 12 | private readonly SourceMock.Internal.MockMethodHandler _testRefHandler = new(); 13 | SourceMock.Interfaces.IMockMethodSetup INeedsParameterModifiersSetup.TestRef(SourceMock.Internal.MockArgumentMatcher value) => _testRefHandler.Setup(null, new SourceMock.Internal.IMockArgumentMatcher[] { value }); 14 | public int TestRef(ref int value) => _testRefHandler.Call(null, new object?[] { value }); 15 | System.Collections.Generic.IReadOnlyList INeedsParameterModifiersCalls.TestRef(SourceMock.Internal.MockArgumentMatcher value) => _testRefHandler.Calls(null, new SourceMock.Internal.IMockArgumentMatcher[] { value }, args => ((int)args[0]!)); 16 | 17 | private readonly SourceMock.Internal.MockMethodHandler _testOutHandler = new(); 18 | SourceMock.Interfaces.IMockMethodSetup INeedsParameterModifiersSetup.TestOut(SourceMock.Internal.MockArgumentMatcher value) => _testOutHandler.Setup(null, new SourceMock.Internal.IMockArgumentMatcher[] { value }); 19 | public int TestOut(out int value) { 20 | var arguments = new object?[] { default(int) }; 21 | var result = _testOutHandler.Call(null, arguments); 22 | value = (int)arguments[0]!; 23 | return result; 24 | } 25 | System.Collections.Generic.IReadOnlyList INeedsParameterModifiersCalls.TestOut(SourceMock.Internal.MockArgumentMatcher value) => _testOutHandler.Calls(null, new SourceMock.Internal.IMockArgumentMatcher[] { value }, args => ((int)args[0]!)); 26 | } 27 | 28 | internal static class NeedsParameterModifiersDelegates { 29 | public delegate int TestInFunc(in int value); 30 | public delegate int TestRefFunc(ref int value); 31 | public delegate int TestOutFunc(out int value); 32 | } 33 | 34 | internal interface INeedsParameterModifiersSetup { 35 | SourceMock.Interfaces.IMockMethodSetup TestIn(SourceMock.Internal.MockArgumentMatcher value = default); 36 | SourceMock.Interfaces.IMockMethodSetup TestRef(SourceMock.Internal.MockArgumentMatcher value = default); 37 | SourceMock.Interfaces.IMockMethodSetup TestOut(SourceMock.Internal.MockArgumentMatcher value = default); 38 | } 39 | 40 | internal interface INeedsParameterModifiersCalls { 41 | System.Collections.Generic.IReadOnlyList TestIn(SourceMock.Internal.MockArgumentMatcher value = default); 42 | System.Collections.Generic.IReadOnlyList TestRef(SourceMock.Internal.MockArgumentMatcher value = default); 43 | System.Collections.Generic.IReadOnlyList TestOut(SourceMock.Internal.MockArgumentMatcher value = default); 44 | } 45 | } -------------------------------------------------------------------------------- /Tests/MockManifest.cs: -------------------------------------------------------------------------------- 1 | using System.Net.WebSockets; 2 | using Azure.Storage.Blobs; 3 | using SourceMock; 4 | using SourceMock.Tests.Interfaces; 5 | 6 | [assembly: GenerateMocksForAssemblyOf(typeof(IMockable), ExcludeRegex = "ExcludedInterface")] 7 | [assembly: GenerateMocksForTypes( 8 | typeof(AbstractClass), 9 | typeof(Disposable), 10 | typeof(WebSocket), 11 | typeof(BlobContainerClient) 12 | )] -------------------------------------------------------------------------------- /Tests/ReturnValueTests.cs: -------------------------------------------------------------------------------- 1 | using Xunit; 2 | using SourceMock.Tests.Interfaces.Mocks; 3 | using System.Threading.Tasks; 4 | 5 | namespace SourceMock.Tests { 6 | public class ReturnValueTests { 7 | [Fact] 8 | public void ValueType() { 9 | var mock = new MockableMock(); 10 | 11 | mock.Setup.GetInt32().Returns(3); 12 | 13 | Assert.Equal(3, mock.GetInt32()); 14 | } 15 | 16 | [Fact] 17 | public void NullableValueType() { 18 | var mock = new MockableMock(); 19 | 20 | mock.Setup.GetInt32Nullable().Returns(null); 21 | 22 | Assert.Null(mock.GetInt32Nullable()); 23 | } 24 | 25 | [Fact] 26 | public void ReferenceType() { 27 | var mock = new MockableMock(); 28 | 29 | mock.Setup.GetString().Returns("a"); 30 | 31 | Assert.Equal("a", mock.GetString()); 32 | } 33 | 34 | [Fact] 35 | public void NullableReferenceType() { 36 | var mock = new MockableMock(); 37 | 38 | mock.Setup.GetStringNullable().Returns(null); 39 | 40 | Assert.Null(mock.GetStringNullable()); 41 | } 42 | 43 | [Fact] 44 | public void Property() { 45 | var mock = new MockableMock(); 46 | 47 | mock.Setup.Count.Returns(10); 48 | 49 | Assert.Equal(10, mock.Count); 50 | } 51 | 52 | [Fact] 53 | public void Generic() { 54 | var mock = new NeedsGenericsMock(); 55 | 56 | mock.Setup.Parse("2+2").Returns(5); 57 | 58 | Assert.Equal(5, mock.Parse("2+2")); 59 | } 60 | 61 | [Fact] 62 | public void Generic_ContainingInterface() { 63 | var mock = new NeedsGenericsMock(); 64 | 65 | mock.Setup.Get().Returns(5); 66 | 67 | Assert.Equal(5, mock.Get()); 68 | } 69 | 70 | [Fact] 71 | public async Task Task() { 72 | var mock = new MockableMock(); 73 | 74 | mock.Setup.GetStringAsync().ReturnsAsync("a"); 75 | 76 | Assert.Equal("a", await mock.GetStringAsync()); 77 | } 78 | } 79 | } -------------------------------------------------------------------------------- /Tests/RunsCallbackTests.cs: -------------------------------------------------------------------------------- 1 | using Xunit; 2 | using SourceMock.Tests.Interfaces.Mocks; 3 | using SourceMock.Tests.Interfaces; 4 | 5 | namespace SourceMock.Tests { 6 | public class RunsCallbackTests { 7 | [Fact] 8 | public void MethodArgument_ReturnValue() { 9 | var mock = new MockableMock(); 10 | 11 | mock.Setup.ParseToInt32("1") 12 | .Runs(s => s == "1" ? 1 : default); 13 | 14 | var result = mock.ParseToInt32("1"); 15 | 16 | Assert.Equal(1, result); 17 | } 18 | 19 | [Fact] 20 | public void MethodNoArgument_ReturnValue() { 21 | var mock = new MockableMock(); 22 | mock.Setup.GetInt32().Runs(() => 3); 23 | 24 | var result = mock.GetInt32(); 25 | 26 | Assert.Equal(3, result); 27 | } 28 | 29 | [Fact] 30 | public void Method_Void() { 31 | IEmptyInterface? runsArgument = null; 32 | var argument = new EmptyClass(); 33 | 34 | var mock = new MockableMock(); 35 | mock.Setup.Execute(default) 36 | .Runs(argument => runsArgument = argument); 37 | 38 | mock.Execute(argument); 39 | 40 | Assert.Equal(argument, runsArgument); 41 | } 42 | 43 | [Fact] 44 | public void Method_Out() { 45 | var mock = new NeedsParameterModifiersMock(); 46 | mock.Setup.TestOut().Runs((out int x) => x = 5); 47 | 48 | mock.TestOut(out var result); 49 | 50 | Assert.Equal(5, result); 51 | } 52 | 53 | [Fact] 54 | public void Method_Generic() { 55 | var mock = new NeedsGenericsMock(); 56 | mock.Setup.Convert().Runs(x => x.ToString()); 57 | 58 | var result = mock.Convert(5); 59 | 60 | Assert.Equal("5", result); 61 | } 62 | 63 | [Fact] 64 | public void Property_ReturnValue() { 65 | var mock = new MockableMock(); 66 | mock.Setup.Count.get.Runs(() => 5); 67 | 68 | var result = mock.Count; 69 | 70 | Assert.Equal(5, result); 71 | } 72 | 73 | private class EmptyClass : IEmptyInterface { } 74 | } 75 | } -------------------------------------------------------------------------------- /Tests/Tests.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | net5.0 4 | false 5 | SourceMock.Tests 6 | SourceMock.Tests 7 | 8 | true 9 | Generated 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | -------------------------------------------------------------------------------- /Tests/VerificationTests.cs: -------------------------------------------------------------------------------- 1 | using Xunit; 2 | using SourceMock.Tests.Interfaces.Mocks; 3 | 4 | namespace SourceMock.Tests { 5 | public class VerificationTests { 6 | [Fact] 7 | public void NoArguments() { 8 | var mock = new MockableMock(); 9 | 10 | mock.GetInt32(); 11 | 12 | Assert.Equal(1, mock.Calls.GetInt32().Count); 13 | } 14 | 15 | [Fact] 16 | public void OneArgument() { 17 | var mock = new MockableMock(); 18 | 19 | mock.ParseToInt32("x"); 20 | 21 | Assert.Equal(new[] { "x" }, mock.Calls.ParseToInt32()); 22 | } 23 | 24 | [Fact] 25 | public void MultipleArguments() { 26 | var mock = new MockableMock(); 27 | 28 | mock.Divide(4, 2); 29 | 30 | Assert.Equal(new[] { (4d, 2d) }, mock.Calls.Divide()); 31 | } 32 | 33 | [Fact] 34 | public void MultipleArguments_Filtered() { 35 | var mock = new MockableMock(); 36 | 37 | mock.Divide(4, 2); 38 | mock.Divide(1, 5); 39 | 40 | Assert.Single(mock.Calls.Divide(4, 2)); 41 | } 42 | 43 | [Fact] 44 | public void Overloaded() { 45 | var mock = new MockableMock(); 46 | 47 | mock.Sum(1, 2); 48 | 49 | Assert.Equal(new[] { (1, 2) }, mock.Calls.Sum(default, default)); 50 | } 51 | 52 | [Fact] 53 | public void Property_Get() { 54 | var mock = new MockableMock(); 55 | 56 | var _ = mock.Count; 57 | 58 | Assert.Single(mock.Calls.Count.get); 59 | } 60 | 61 | [Fact] 62 | public void Property_Set() { 63 | var mock = new MockableMock(); 64 | 65 | mock.Name = "test"; 66 | 67 | Assert.Equal(new[] { "test" }, mock.Calls.Name.set()); 68 | } 69 | 70 | [Fact] 71 | public void Property_Get_Virtual() { 72 | var mock = new AbstractClassMock(); 73 | 74 | var _ = mock.VirtualProperty; 75 | 76 | Assert.Single(mock.Calls.VirtualProperty.get); 77 | } 78 | 79 | [Fact] 80 | public void InArgument() { 81 | var mock = new NeedsParameterModifiersMock(); 82 | 83 | mock.TestIn(5); 84 | 85 | Assert.Equal(new[] { 5 }, mock.Calls.TestIn()); 86 | } 87 | 88 | [Fact] 89 | public void RefArgument() { 90 | var mock = new NeedsParameterModifiersMock(); 91 | 92 | var x = 5; 93 | mock.TestRef(ref x); 94 | 95 | Assert.Equal(new[] { 5 }, mock.Calls.TestRef()); 96 | } 97 | 98 | [Fact] 99 | public void ImplicitInterfaceImplementation() { 100 | var mock = new DisposableMock(); 101 | 102 | mock.Dispose(); 103 | 104 | Assert.Equal(1, mock.Calls.Dispose().Count); 105 | } 106 | } 107 | } -------------------------------------------------------------------------------- /[Main]/GenerateMocksForAssemblyOfAttribute.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Text.RegularExpressions; 3 | 4 | namespace SourceMock { 5 | /// 6 | /// Requests mock generation for all interfaces in the specified assembly. 7 | /// 8 | /// 9 | /// When this attribute is used in a test project, SourceMock will generate 10 | /// mocks for all interfaces in the target assembly, and include those into 11 | /// the test project. 12 | /// 13 | [AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)] 14 | public class GenerateMocksForAssemblyOfAttribute : Attribute { 15 | /// 16 | /// Initializes a new instance of the class. 17 | /// 18 | /// 19 | /// Any type in the target assembly. 20 | /// 21 | public GenerateMocksForAssemblyOfAttribute(Type anyTypeInTargetAssembly) { 22 | AnyTypeInTargetAssembly = anyTypeInTargetAssembly; 23 | } 24 | 25 | internal Type AnyTypeInTargetAssembly { get; } 26 | 27 | /// 28 | /// Pattern of type names to exclude from mock generation. 29 | /// 30 | /// 31 | /// Pattern is based on syntax. 32 | /// 33 | public string? ExcludeRegex { get; set; } 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /[Main]/GenerateMocksForTypesAttribute.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace SourceMock { 4 | /// 5 | /// Requests mock generation for specified classes or interfaces. 6 | /// 7 | [AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)] 8 | public class GenerateMocksForTypesAttribute : Attribute { 9 | /// 10 | /// Initializes a new instance of the class. 11 | /// 12 | /// Types to generate mocks for. 13 | public GenerateMocksForTypesAttribute(params Type[] types) { 14 | Types = types; 15 | } 16 | 17 | internal Type[] Types { get; } 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /[Main]/IMock.cs: -------------------------------------------------------------------------------- 1 | namespace SourceMock { 2 | /// Represents a generated mock class. 3 | public interface IMock { 4 | } 5 | 6 | /// Represents a generated mock class. 7 | /// The mocked type. 8 | public interface IMock : IMock { 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /[Main]/Interfaces/IMockMethodSetup.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace SourceMock.Interfaces { 4 | /// 5 | /// Provides a way to set up behavior for a mocked method. 6 | /// 7 | /// The type of callback delegate passed into . 8 | public interface IMockMethodSetup : IMockSetupThrows, IMockSetupRuns 9 | where TRun : Delegate 10 | { 11 | } 12 | 13 | /// 14 | /// Provides a way to set up behavior for a mocked method that returns a value. 15 | /// 16 | /// The method return type. 17 | /// The type of callback delegate passed into . 18 | public interface IMockMethodSetup : IMockMethodSetup, IMockSetupReturns 19 | where TRun: Delegate 20 | { 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /[Main]/Interfaces/IMockPropertyCalls.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Diagnostics.CodeAnalysis; 3 | 4 | namespace SourceMock.Interfaces { 5 | /// 6 | /// Provides a way to retrieve calls made to a mocked property that has a getter. 7 | /// 8 | /// Property type. 9 | public interface IMockPropertyCalls { 10 | /// 11 | /// Provides a way to retrieve calls made to the mocked getter. 12 | /// 13 | /// 14 | /// SourceMock does not currently support parametrized properties, 15 | /// so getter calls always use . 16 | /// 17 | [SuppressMessage("Style", "IDE1006:Naming Styles", Justification = "Aiming to match C# syntax")] 18 | IReadOnlyList get { get; } 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /[Main]/Interfaces/IMockPropertySetup.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace SourceMock.Interfaces { 4 | /// 5 | /// Provides a way to set up behavior for a mocked property that has a getter. 6 | /// 7 | public interface IMockPropertySetup : IMockSetupReturns { 8 | /// 9 | /// Provides a way to to set up behavior for the mocked getter. 10 | /// 11 | public IMockMethodSetup, T> get { get; } 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /[Main]/Interfaces/IMockSettablePropertyCalls.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Diagnostics.CodeAnalysis; 3 | using SourceMock.Internal; 4 | 5 | namespace SourceMock.Interfaces { 6 | /// 7 | /// Provides a way to retrieve calls made to a specific mocked property that has both getter and setter. 8 | /// 9 | /// Property type. 10 | public interface IMockSettablePropertyCalls : IMockPropertyCalls { 11 | /// 12 | /// Provides a way to retrieve assignments made to the mocked setter. 13 | /// 14 | /// The optional value to filter the assignments 15 | /// 16 | /// Either all assignments made; or assignments that match 17 | /// if value is specified. 18 | /// 19 | /// 20 | /// SourceMock does not currently support parametrized properties, 21 | /// so setter calls always have one parameter of type . 22 | /// 23 | [SuppressMessage("Style", "IDE1006:Naming Styles", Justification = "Aiming to match C# syntax")] 24 | IReadOnlyList set(MockArgumentMatcher value = default); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /[Main]/Interfaces/IMockSettablePropertySetup.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using SourceMock.Internal; 3 | 4 | namespace SourceMock.Interfaces { 5 | /// 6 | /// Provides a way to set up behavior for a mocked property that has both getter and setter. 7 | /// 8 | public interface IMockSettablePropertySetup : IMockPropertySetup { 9 | /// 10 | /// Provides a way to to set up behavior for the mocked setter. 11 | /// 12 | /// The property value to set up behavior for. 13 | /// 14 | /// An object to set up the setter behavior 15 | /// when assigned the given . 16 | /// 17 | IMockMethodSetup> set(MockArgumentMatcher value = default); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /[Main]/Interfaces/IMockSetupReturns.cs: -------------------------------------------------------------------------------- 1 | namespace SourceMock.Interfaces { 2 | /// 3 | /// Provides a way to configure a return value for a mocked method or property. 4 | /// 5 | /// The type of the return value 6 | public interface IMockSetupReturns { 7 | /// 8 | /// Configures mock to return the specified value when called. 9 | /// 10 | /// The value to return. 11 | void Returns(TReturn value); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /[Main]/Interfaces/IMockSetupRuns.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace SourceMock.Interfaces { 4 | /// 5 | /// Provides a way to configure custom behavior for a mocked method. 6 | /// 7 | /// The type of the callback delegate passed into . 8 | public interface IMockSetupRuns 9 | where TRun: Delegate 10 | { 11 | /// 12 | /// Configures mocked method to run a specific callback delegate. 13 | /// 14 | /// The callback delegate to run. 15 | void Runs(TRun run); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /[Main]/Interfaces/IMockSetupThrows.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace SourceMock.Interfaces { 4 | /// 5 | /// Provides a way to configure an exception to be thrown by a mocked method. 6 | /// 7 | public interface IMockSetupThrows { 8 | /// 9 | /// Configures mocked method to throw the specified exception when called. 10 | /// 11 | /// The to throw. 12 | void Throws(Exception exception); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /[Main]/Internal/DefaultValue.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Collections.Immutable; 4 | using System.Threading.Tasks; 5 | 6 | namespace SourceMock.Internal { 7 | internal static class DefaultValue { 8 | private static readonly IDictionary MutableCollectionTypes = MapCollectionInterfacesToClassTypes( 9 | typeof(List<>), typeof(Dictionary<,>), typeof(HashSet<>) 10 | ); 11 | private static readonly IDictionary ImmutableCollectionTypes = MapCollectionInterfacesToClassTypes( 12 | typeof(ImmutableList<>), typeof(ImmutableDictionary<,>), typeof(ImmutableHashSet<>) 13 | ); 14 | 15 | public static T? Get() { 16 | return (T?)GetStandard(typeof(T)); 17 | } 18 | 19 | private static object? Get(Type type) { 20 | return GetStandard(type); 21 | } 22 | 23 | private static object? GetStandard(Type type) { 24 | if (type.IsValueType) { 25 | if (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(ImmutableArray<>)) 26 | return type.GetField(nameof(ImmutableArray.Empty)).GetValue(null); 27 | 28 | return Activator.CreateInstance(type); 29 | } 30 | 31 | if (type == typeof(Task)) 32 | return Task.CompletedTask; 33 | 34 | if (type.IsArray) 35 | return Array.CreateInstance(type.GetElementType(), 0); 36 | 37 | if (type.IsGenericType) { 38 | var definition = type.GetGenericTypeDefinition(); 39 | var arguments = type.GetGenericArguments(); 40 | 41 | // TODO: some refelction caching 42 | if (definition == typeof(Task<>)) { 43 | return ((Func>)Task.FromResult) 44 | .Method 45 | .GetGenericMethodDefinition() 46 | .MakeGenericMethod(arguments) 47 | .Invoke(null, new[] { Get(arguments[0]) }); 48 | } 49 | 50 | if (MutableCollectionTypes.TryGetValue(definition, out var classType)) 51 | return Activator.CreateInstance(classType.MakeGenericType(arguments)); 52 | if (ImmutableCollectionTypes.TryGetValue(definition, out classType)) 53 | return classType.MakeGenericType(arguments).GetField("Empty").GetValue(null); 54 | } 55 | 56 | return null; 57 | } 58 | 59 | private static IDictionary MapCollectionInterfacesToClassTypes(params Type[] classTypes) { 60 | var results = new Dictionary(); 61 | foreach (var classType in classTypes) { 62 | var classTypeDefinition = classType.GetGenericTypeDefinition(); 63 | results.Add(classTypeDefinition, classTypeDefinition); 64 | 65 | var classTypeArgumentCount = classType.GetGenericArguments().Length; 66 | foreach (var interfaceType in classType.GetInterfaces()) { 67 | if (!interfaceType.IsGenericType) 68 | continue; 69 | 70 | var interfaceTypeDefinition = interfaceType.GetGenericTypeDefinition(); 71 | if (results.ContainsKey(interfaceTypeDefinition)) 72 | continue; 73 | 74 | if (interfaceType.GetGenericArguments().Length != classTypeArgumentCount) 75 | continue; 76 | 77 | results.Add(interfaceTypeDefinition, classTypeDefinition); 78 | } 79 | } 80 | return results; 81 | } 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /[Main]/Internal/IMockArgumentMatcher.cs: -------------------------------------------------------------------------------- 1 | namespace SourceMock.Internal { 2 | /// This type supports generated code and is not intended to be used directly. 3 | public interface IMockArgumentMatcher { 4 | /// This method supports generated code and is not intended to be used directly. 5 | bool Matches(object? argument); 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /[Main]/Internal/IMockMethodSetupInternal.cs: -------------------------------------------------------------------------------- 1 | namespace SourceMock.Internal { 2 | internal interface IMockCallMatcher 3 | { 4 | bool Matches(MockCall call); 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /[Main]/Internal/MockArgumentMatcher.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace SourceMock.Internal { 4 | /// This type supports generated code and is not intended to be used directly. 5 | public readonly struct MockArgumentMatcher: IMockArgumentMatcher { 6 | private readonly Func? _matches; 7 | 8 | private MockArgumentMatcher(Func matches) { 9 | _matches = matches; 10 | } 11 | 12 | bool IMockArgumentMatcher.Matches(object? argument) => _matches == null || (argument switch { 13 | T typed => _matches(typed), 14 | null => _matches((T)((object?)null)!), 15 | _ => false 16 | }); 17 | 18 | /// This operator supports generated code and is not intended to be used directly. 19 | public static implicit operator MockArgumentMatcher(T value) => new(v => Equals(v, value)); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /[Main]/Internal/MockCall.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | 4 | namespace SourceMock.Internal { 5 | internal readonly struct MockCall { 6 | public MockCall(IReadOnlyList genericArguments, IReadOnlyList arguments) { 7 | GenericArguments = genericArguments; 8 | Arguments = arguments; 9 | } 10 | 11 | public IReadOnlyList GenericArguments { get; } 12 | public IReadOnlyList Arguments { get; } 13 | 14 | public bool Matches(IReadOnlyList genericArguments, IReadOnlyList arguments) { 15 | for (var i = 0; i < genericArguments.Count; i++) { 16 | if (!Equals(genericArguments[i], GenericArguments[i])) 17 | return false; 18 | } 19 | for (var i = 0; i < arguments.Count; i++) { 20 | if (!arguments[i].Matches(Arguments[i])) 21 | return false; 22 | } 23 | return true; 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /[Main]/Internal/MockMethodHandler.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using SourceMock.Interfaces; 5 | 6 | namespace SourceMock.Internal { 7 | /// This type supports generated code and is not intended to be used directly. 8 | public class MockMethodHandler { 9 | private readonly IList _setups = new List(); 10 | private readonly IList _calls = new List(); 11 | 12 | /// This method supports generated code and is not intended to be used directly. 13 | public IMockMethodSetup Setup(IReadOnlyList? genericArguments, IReadOnlyList? arguments) where TRun : Delegate { 14 | genericArguments ??= Array.Empty(); 15 | arguments ??= Array.Empty(); 16 | var setup = new MockMethodSetup(genericArguments, arguments); 17 | _setups.Add(setup); 18 | 19 | return setup; 20 | } 21 | 22 | /// This method supports generated code and is not intended to be used directly. 23 | // Important: arguments must be an array here to ensure that DynamicInvoke in setup.Execute returns out arguments in the same array 24 | public TReturn Call(IReadOnlyList? genericArguments, object?[]? arguments) where TRun : Delegate { 25 | genericArguments ??= Array.Empty(); 26 | arguments ??= Array.Empty(); 27 | var call = new MockCall(genericArguments, arguments); 28 | _calls.Add(call); 29 | 30 | // setups added later take priority 31 | var setup = _setups.LastOrDefault(s => s.Matches(call)); 32 | return (setup != null ? ((MockMethodSetup)setup).Execute(arguments) : DefaultValue.Get())!; 33 | } 34 | 35 | /// This method supports generated code and is not intended to be used directly. 36 | public IReadOnlyList Calls( 37 | IReadOnlyList? genericArguments, 38 | IReadOnlyList? arguments, 39 | Func, T> convertResult 40 | ) { 41 | genericArguments ??= Array.Empty(); 42 | arguments ??= Array.Empty(); 43 | return _calls 44 | .Where(c => c.Matches(genericArguments, arguments)) 45 | .Select(c => convertResult(c.Arguments)) 46 | .Cast() 47 | .ToList(); 48 | } 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /[Main]/Internal/MockMethodSetup.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using SourceMock.Interfaces; 4 | 5 | namespace SourceMock.Internal { 6 | internal class MockMethodSetup : IMockMethodSetup, IMockCallMatcher 7 | where TRun : Delegate 8 | { 9 | private readonly IReadOnlyList _genericArguments; 10 | private readonly IReadOnlyList _arguments; 11 | private bool _hasReturnValue; 12 | private TReturn? _returnValue; 13 | private Exception? _exception; 14 | private TRun? _callback; 15 | 16 | public MockMethodSetup(IReadOnlyList genericArguments, IReadOnlyList arguments) { 17 | _genericArguments = genericArguments; 18 | _arguments = arguments; 19 | } 20 | 21 | public void Returns(TReturn value) { 22 | _hasReturnValue = true; 23 | _returnValue = value; 24 | } 25 | 26 | public void Throws(Exception exception) { 27 | _exception = exception; 28 | } 29 | 30 | public void Runs(TRun callback) { 31 | _callback = callback; 32 | } 33 | 34 | public bool Matches(MockCall call) => call.Matches(_genericArguments, _arguments); 35 | 36 | public TReturn? Execute(object?[] arguments) { 37 | if (_callback != null) 38 | return (TReturn?)_callback.DynamicInvoke(arguments); 39 | 40 | if (_exception != null) 41 | throw _exception; 42 | 43 | if (!_hasReturnValue) 44 | return DefaultValue.Get(); 45 | 46 | return _returnValue; 47 | } 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /[Main]/Internal/MockPropertyCalls.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using SourceMock.Interfaces; 3 | 4 | namespace SourceMock.Internal { 5 | internal class MockPropertyCalls : IMockSettablePropertyCalls { 6 | private readonly MockPropertyHandler _handler; 7 | 8 | public MockPropertyCalls(MockPropertyHandler handler) { 9 | _handler = handler; 10 | } 11 | 12 | public IReadOnlyList get => _handler.GetterHandler.Calls(null, null, _ => NoArguments.Value); 13 | 14 | public IReadOnlyList set(MockArgumentMatcher value = default) => _handler.SetterHandler.Calls(null, new IMockArgumentMatcher[] { value }, args => (T)args[0]!); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /[Main]/Internal/MockPropertyHandler.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using SourceMock.Interfaces; 3 | 4 | namespace SourceMock.Internal { 5 | /// This type supports generated code and is not intended to be used directly. 6 | public class MockPropertyHandler { 7 | private readonly MockMethodHandler? _setterHandler; 8 | 9 | /// This constructor supports generated code and is not intended to be used directly. 10 | public MockPropertyHandler(bool isSettable) { 11 | GetterHandler = new MockMethodHandler(); 12 | _setterHandler = isSettable ? new MockMethodHandler() : null; 13 | } 14 | 15 | /// This property supports generated code and is not intended to be used directly. 16 | public MockMethodHandler GetterHandler { get; } 17 | /// This property supports generated code and is not intended to be used directly. 18 | public MockMethodHandler SetterHandler => _setterHandler 19 | ?? throw new InvalidOperationException("Attempted to set up setter for property with no setter."); 20 | 21 | /// This methpd supports generated code and is not intended to be used directly. 22 | public IMockSettablePropertySetup Setup() => new MockPropertySetup(this); 23 | /// This methpd supports generated code and is not intended to be used directly. 24 | public IMockSettablePropertyCalls Calls() => new MockPropertyCalls(this); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /[Main]/Internal/MockPropertySetup.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using SourceMock.Interfaces; 3 | 4 | namespace SourceMock.Internal { 5 | internal class MockPropertySetup : IMockSettablePropertySetup { 6 | private readonly MockPropertyHandler _handler; 7 | 8 | public MockPropertySetup(MockPropertyHandler handler) { 9 | _handler = handler; 10 | } 11 | 12 | public IMockMethodSetup, T> get => _handler.GetterHandler.Setup, T>(null, null); 13 | 14 | public IMockMethodSetup> set(MockArgumentMatcher value = default) => ( 15 | _handler.SetterHandler ?? throw new InvalidOperationException("Attempted to set up setter for property with no setter.") 16 | ).Setup, VoidReturn>(null, new IMockArgumentMatcher[] { value }); 17 | 18 | public void Returns(T value) => get.Returns(value); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /[Main]/Internal/VoidReturn.cs: -------------------------------------------------------------------------------- 1 | namespace SourceMock.Internal { 2 | /// This type supports generated code and is not intended to be used directly. 3 | public class VoidReturn { 4 | private VoidReturn() {} 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /[Main]/MockExtensions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Threading.Tasks; 3 | using SourceMock.Interfaces; 4 | 5 | /// 6 | /// Provides a set of extension methods for SourceMock interfaces. 7 | /// 8 | public static class MockExtensions { 9 | /// 10 | /// Configures mocked method to throw the specified exception when called. 11 | /// 12 | /// The specific type of to throw. 13 | /// The method to configure. 14 | public static void Throws(this IMockSetupThrows setup) 15 | where TException : Exception, new() 16 | { 17 | setup.Throws(new Exception()); 18 | } 19 | 20 | /// 21 | /// Configures mocked method to return the specified value, wrapped in a . 22 | /// 23 | /// The method to configure. 24 | /// The value to return. 25 | public static void ReturnsAsync(this IMockSetupReturns> setup, T value) { 26 | setup.Returns(Task.FromResult(value)); 27 | } 28 | } -------------------------------------------------------------------------------- /[Main]/NoArguments.cs: -------------------------------------------------------------------------------- 1 | namespace SourceMock { 2 | /// 3 | /// Represents argument list for methods that have no arguments. 4 | /// 5 | public class NoArguments { 6 | private NoArguments() {} 7 | /// 8 | /// Represents the singleton instance of class. No other instances exist. 9 | /// 10 | public static NoArguments Value { get; } = new NoArguments(); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /[Main]/[Main].csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | netstandard2.1; net471 4 | SourceMock 5 | SourceMock 6 | true 7 | 8 | 9 | 0.12.0 10 | Mocking framework that uses Roslyn source generators for extra flexibility. 11 | Andrey Shchekin 12 | https://github.com/ashmind/SourceMock 13 | git 14 | https://github.com/ashmind/SourceMock.git 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | --------------------------------------------------------------------------------