Skip to content
This repository was archived by the owner on Jun 30, 2023. It is now read-only.

Commit 5acedb2

Browse files
committed
Unify naming conventions for runtime lookup
Code generation support for generics introduced the `Of..` name to append the generic types being mocked (i.e. `IEnumerableOfStringMock`). This was not reflected in the run-time naming classes so some tests were failing. In addition, there was quite some duplication between MockNaming and StuntNaming, which is now unified with the former delegating to the latter all behavior and just providing different DefaultNamespace and DefaultSuffix values. The new implementation is more aligned with the NamingConvention used for codegen now.
1 parent 2e4f6e8 commit 5acedb2

File tree

9 files changed

+116
-71
lines changed

9 files changed

+116
-71
lines changed

src/Moq/Moq.CodeAnalysis/MockNamingConvention.cs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10,13 +10,13 @@ namespace Moq
1010
public class MockNamingConvention : NamingConvention
1111
{
1212
/// <summary>
13-
/// Gets the generated code namespace, which is <see cref="MockNaming.Namespace"/>.
13+
/// Gets the generated code namespace, which is <see cref="MockNaming.DefaultNamespace"/>.
1414
/// </summary>
15-
public override string Namespace => MockNaming.Namespace;
15+
public override string Namespace => MockNaming.DefaultNamespace;
1616

1717
/// <summary>
18-
/// Gets the generated type names suffix, which is <see cref="MockNaming.NameSuffix"/>.
18+
/// Gets the generated type names suffix, which is <see cref="MockNaming.DefaultSuffix"/>.
1919
/// </summary>
20-
public override string NameSuffix => MockNaming.NameSuffix;
20+
public override string NameSuffix => MockNaming.DefaultSuffix;
2121
}
2222
}

src/Moq/Moq.Sdk/MockNaming.cs

Lines changed: 30 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,5 @@
11
using System;
2-
using System.Collections.Generic;
3-
using System.Linq;
4-
using System.Text;
2+
using Stunts;
53

64
namespace Moq.Sdk
75
{
@@ -11,48 +9,46 @@ namespace Moq.Sdk
119
public static class MockNaming
1210
{
1311
/// <summary>
14-
/// The namespace where generated mocks are declared.
12+
/// The default namespace where generated mocks are declared.
1513
/// </summary>
16-
public const string Namespace = "Mocks";
14+
public const string DefaultNamespace = "Mocks";
1715

1816
/// <summary>
19-
/// The suffix added to mock type names.
17+
/// The default suffix added to mock type names.
2018
/// </summary>
21-
public const string NameSuffix = "Mock";
19+
public const string DefaultSuffix = "Mock";
2220

2321
/// <summary>
24-
/// Gets the runtime mock name from its base type and implemented interfaces.
22+
/// Gets the runtime mock name from its base type and optional additional
23+
/// interfaces, using the <see cref="DefaultSuffix"/>.
2524
/// </summary>
26-
public static string GetName(Type baseType, Type[] implementedInterfaces)
27-
{
28-
var builder = new StringBuilder();
29-
AddNames(builder, new[] { baseType });
30-
AddNames(builder, implementedInterfaces.OrderBy(x => x.Name));
31-
return builder.Append(NameSuffix).ToString();
32-
}
25+
public static string GetName(Type baseType, Type[] additionalInterfaces)
26+
=> GetName(DefaultSuffix, baseType, additionalInterfaces);
27+
28+
/// <summary>
29+
/// Gets the runtime mock name from its base type and optional additional interfaces
30+
/// and the given <paramref name="suffix"/> appended to the type name.
31+
/// </summary>
32+
public static string GetName(string suffix, Type baseType, Type[] additionalInterfaces)
33+
=> StuntNaming.GetName(suffix, baseType, additionalInterfaces);
34+
35+
/// <summary>
36+
/// Gets the runtime mock full name from its base type and optional additional interfaces,
37+
/// using the <see cref="DefaultNamespace"/> and <see cref="DefaultSuffix"/>.
38+
/// </summary>
39+
public static string GetFullName(Type baseType, params Type[] additionalInterfaces)
40+
=> GetFullName(DefaultNamespace, DefaultSuffix, baseType, additionalInterfaces);
3341

3442
/// <summary>
3543
/// Gets the runtime mock full name from its base type and implemented interfaces.
3644
/// </summary>
37-
public static string GetFullName(Type baseType, Type[] implementedInterfaces)
38-
=> Namespace + "." + GetName(baseType, implementedInterfaces);
45+
public static string GetFullName(string @namespace, Type baseType, params Type[] additionalInterfaces)
46+
=> GetFullName(@namespace, DefaultSuffix, baseType, additionalInterfaces);
3947

40-
static void AddNames(StringBuilder builder, IEnumerable<Type> symbols)
41-
{
42-
foreach (var symbol in symbols)
43-
{
44-
if (!symbol.IsGenericType)
45-
{
46-
builder.Append(symbol.Name);
47-
}
48-
else
49-
{
50-
// Remove the arity of the generic type
51-
builder.Append(symbol.Name.Substring(0, symbol.Name.IndexOf('`')));
52-
builder.Append("Of");
53-
AddNames(builder, symbol.GenericTypeArguments);
54-
}
55-
}
56-
}
48+
/// <summary>
49+
/// Gets the runtime mock full name from its base type and implemented interfaces.
50+
/// </summary>
51+
public static string GetFullName(string @namespace, string suffix, Type baseType, params Type[] additionalInterfaces)
52+
=> StuntNaming.GetFullName(@namespace, suffix, baseType, additionalInterfaces);
5753
}
5854
}

src/Stunts/Stunts.CodeAnalysis/NamingConvention.cs

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
using System.Collections.Generic;
22
using System.Linq;
33
using System.Text;
4-
using System.Xml;
54
using Microsoft.CodeAnalysis;
65

76
namespace Stunts
@@ -14,12 +13,12 @@ public class NamingConvention
1413
/// <summary>
1514
/// The namespace of the generated code.
1615
/// </summary>
17-
public virtual string Namespace => StuntNaming.Namespace;
16+
public virtual string Namespace => StuntNaming.DefaultNamespace;
1817

1918
/// <summary>
2019
/// Suffix appended to the type name, i.e. <c>IFooStunt</c>.
2120
/// </summary>
22-
public virtual string NameSuffix => StuntNaming.NameSuffix;
21+
public virtual string NameSuffix => StuntNaming.DefaultSuffix;
2322

2423
/// <summary>
2524
/// The type name to generate for the given (optional) base type and implemented interfaces.

src/Stunts/Stunts.Sdk/StuntGenerator.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -140,6 +140,8 @@ public async Task<Document> GenerateDocumentAsync(Project project, ITypeSymbol[]
140140
#if DEBUG
141141
// Update the persisted temp file in debug builds.
142142
File.WriteAllText(filePath, code);
143+
if (Debugger.IsAttached)
144+
Process.Start(filePath);
143145
#endif
144146

145147
return document;

src/Stunts/Stunts.Tests/StuntFactoryTests.cs

Lines changed: 3 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,5 @@
11
using System;
2-
using System.Collections.Generic;
3-
using System.Linq;
42
using System.Reflection;
5-
using System.Text;
6-
using System.Threading.Tasks;
73
using Xunit;
84

95
namespace Stunts.Tests
@@ -19,27 +15,26 @@ public void CreateStuntFromCallingAssemblyWithNamingConvention()
1915
new[] { typeof(IDisposable) },
2016
Array.Empty<object>());
2117

22-
Assert.IsType<IStuntFactoryIDisposableStunt>(stunt);
18+
Assert.IsType<IDisposableIStuntFactoryStunt>(stunt);
2319
}
2420

2521
[Fact]
2622
public void CanReplaceDefaultFactory()
2723
{
2824
var existing = StuntFactory.Default;
29-
var factory = new IStuntFactoryIDisposableStunt();
25+
var factory = new IDisposableIStuntFactoryStunt();
3026
StuntFactory.Default = factory;
3127

3228
Assert.Same(factory, StuntFactory.Default);
3329

3430
StuntFactory.Default = existing;
3531
}
36-
3732
}
3833
}
3934

4035
namespace Stunts
4136
{
42-
public class IStuntFactoryIDisposableStunt : IStuntFactory, IDisposable
37+
public class IDisposableIStuntFactoryStunt : IStuntFactory, IDisposable
4338
{
4439
public object CreateStunt(Assembly stuntsAssembly, Type baseType, Type[] implementedInterfaces, object[] construtorArguments)
4540
=> throw new NotImplementedException();

src/Stunts/Stunts.Tests/StuntGeneratorTests.cs

Lines changed: 6 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ public async Task CanGenerateStuntForInterface(string language, bool trace = fal
3535
var generator = new StuntGenerator();
3636
var compilation = await CreateStunt(generator, language, typeof(IFoo), trace);
3737
var assembly = compilation.Emit();
38-
var type = assembly.GetExportedTypes().FirstOrDefault();
38+
var type = assembly.GetType(StuntNaming.GetFullName(typeof(IFoo)), true);
3939

4040
Assert.NotNull(type);
4141

@@ -63,9 +63,7 @@ public async Task CanGenerateStuntForClass(string language, bool trace = false)
6363
var generator = new StuntGenerator();
6464
var compilation = await CreateStunt(generator, language, typeof(Foo), trace);
6565
var assembly = compilation.Emit();
66-
var type = assembly.GetExportedTypes().FirstOrDefault();
67-
68-
Assert.NotNull(type);
66+
var type = assembly.GetType(StuntNaming.GetFullName(typeof(Foo)), true);
6967

7068
var instance = Activator.CreateInstance(type);
7169

@@ -91,8 +89,8 @@ public async Task GeneratedNameContainsAdditionalInterfaceInName(string language
9189
{
9290
var compilation = await CreateStunt(new StuntGenerator(), language, new[] { typeof(INotifyPropertyChanged), typeof(IDisposable) }, trace);
9391
var assembly = compilation.Emit();
94-
var type = assembly.GetExportedTypes().FirstOrDefault();
95-
92+
var type = assembly.GetType(StuntNaming.GetFullName(typeof(INotifyPropertyChanged), typeof(IDisposable)), true);
93+
9694
Assert.NotNull(type);
9795
Assert.True(typeof(IDisposable).IsAssignableFrom(type));
9896
Assert.True(type.FullName.Contains(nameof(IDisposable)),
@@ -106,7 +104,7 @@ public async Task GeneratedInterfaceHasCompilerGeneratedAttribute(string languag
106104
{
107105
var compilation = await CreateStunt(new StuntGenerator(), language, typeof(ICalculator), trace);
108106
var assembly = compilation.Emit();
109-
var type = assembly.GetExportedTypes().FirstOrDefault();
107+
var type = assembly.GetType(StuntNaming.GetFullName(typeof(ICalculator)), true);
110108

111109
Assert.NotNull(type);
112110

@@ -148,7 +146,7 @@ public async Task GeneratedTypeOverridesVirtualObjectMembers(string language, bo
148146
{
149147
var compilation = await CreateStunt(new StuntGenerator(), language, new[] { typeof(INotifyPropertyChanged), typeof(IDisposable) }, trace);
150148
var assembly = compilation.Emit();
151-
var type = assembly.GetExportedTypes().FirstOrDefault();
149+
var type = assembly.GetType(StuntNaming.GetFullName(typeof(INotifyPropertyChanged), typeof(IDisposable)), true);
152150

153151
Assert.NotNull(type);
154152

src/Stunts/Stunts.Tests/StuntNamingTests.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,12 +6,12 @@ namespace Stunts.Tests
66
public class StuntNamingTests
77
{
88
[Theory]
9-
[InlineData("StuntFactoryIDisposableIServiceProvider" + StuntNaming.NameSuffix, typeof(StuntFactory), typeof(IServiceProvider), typeof(IDisposable))]
9+
[InlineData("StuntFactoryIDisposableIServiceProvider" + StuntNaming.DefaultSuffix, typeof(StuntFactory), typeof(IServiceProvider), typeof(IDisposable))]
1010
public void GetNameOrdersTypes(string expectedName, Type baseType, params Type[] implementedInterfaces)
1111
=> Assert.Equal(expectedName, StuntNaming.GetName(baseType, implementedInterfaces));
1212

1313
[Theory]
14-
[InlineData(StuntNaming.Namespace + ".StuntFactoryIDisposableIServiceProvider" + StuntNaming.NameSuffix, typeof(StuntFactory), typeof(IServiceProvider), typeof(IDisposable))]
14+
[InlineData(StuntNaming.DefaultNamespace + ".StuntFactoryIDisposableIServiceProvider" + StuntNaming.DefaultSuffix, typeof(StuntFactory), typeof(IServiceProvider), typeof(IDisposable))]
1515
public void GetFullNameOrdersTypes(string expectedName, Type baseType, params Type[] implementedInterfaces)
1616
=> Assert.Equal(expectedName, StuntNaming.GetFullName(baseType, implementedInterfaces));
1717
}

src/Stunts/Stunts/StuntNaming.cs

Lines changed: 66 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
using System;
22
using System.Collections.Generic;
33
using System.Linq;
4+
using System.Text;
45

56
namespace Stunts
67
{
@@ -10,29 +11,83 @@ namespace Stunts
1011
public static class StuntNaming
1112
{
1213
/// <summary>
13-
/// The namespace where generated stunts are declared.
14+
/// The default namespace where generated stunts are declared.
1415
/// </summary>
15-
public const string Namespace = "Stunts";
16+
public const string DefaultNamespace = "Stunts";
1617

1718
/// <summary>
18-
/// The suffix added to stunt type names.
19+
/// The default suffix added to stunt type names.
1920
/// </summary>
20-
public const string NameSuffix = "Stunt";
21+
public const string DefaultSuffix = "Stunt";
2122

2223
/// <summary>
23-
/// Gets the runtime stunt name from its base type and implemented interfaces.
24+
/// Gets the runtime stunt name from its base type and optional additional
25+
/// interfaces, using the <see cref="DefaultSuffix"/>.
2426
/// </summary>
25-
public static string GetName(Type baseType, Type[] implementedInterfaces)
26-
{
27-
Array.Sort(implementedInterfaces, Comparer<Type>.Create((x, y) => x.Name.CompareTo(y.Name)));
27+
public static string GetName(Type baseType, params Type[] additionalInterfaces)
28+
=> GetName(DefaultSuffix, baseType, additionalInterfaces);
2829

29-
return baseType.Name + string.Join("", implementedInterfaces.Select(x => x.Name)) + NameSuffix;
30+
/// <summary>
31+
/// Gets the runtime stunt name from its base type and optional additional interfaces
32+
/// and the given <paramref name="suffix"/> appended to the type name.
33+
/// </summary>
34+
public static string GetName(string suffix, Type baseType, params Type[] additionalInterfaces)
35+
{
36+
if (baseType.IsClass)
37+
{
38+
return new StringBuilder()
39+
.AddName(baseType)
40+
.AddNames(additionalInterfaces.OrderBy(x => x.Name, StringComparer.Ordinal))
41+
.Append(suffix)
42+
.ToString();
43+
}
44+
else
45+
{
46+
return new StringBuilder()
47+
.AddNames(new[] { baseType }
48+
.Concat(additionalInterfaces)
49+
.OrderBy(x => x.Name, StringComparer.Ordinal))
50+
.Append(suffix)
51+
.ToString();
52+
}
3053
}
3154

55+
/// <summary>
56+
/// Gets the runtime stunt full name from its base type and optional additional interfaces,
57+
/// using the <see cref="DefaultNamespace"/> and <see cref="DefaultSuffix"/>.
58+
/// </summary>
59+
public static string GetFullName(Type baseType, params Type[] additionalInterfaces)
60+
=> GetFullName(DefaultNamespace, DefaultSuffix, baseType, additionalInterfaces);
61+
3262
/// <summary>
3363
/// Gets the runtime stunt full name from its base type and implemented interfaces.
3464
/// </summary>
35-
public static string GetFullName(Type baseType, Type[] implementedInterfaces)
36-
=> Namespace + "." + GetName(baseType, implementedInterfaces);
65+
public static string GetFullName(string @namespace, Type baseType, params Type[] additionalInterfaces)
66+
=> GetFullName(@namespace, DefaultSuffix, baseType, additionalInterfaces);
67+
68+
/// <summary>
69+
/// Gets the runtime stunt full name from its base type and implemented interfaces.
70+
/// </summary>
71+
public static string GetFullName(string @namespace, string suffix, Type baseType, params Type[] additionalInterfaces)
72+
=> @namespace + "." + GetName(suffix, baseType, additionalInterfaces);
73+
}
74+
75+
internal static class StringBuilderExtensions
76+
{
77+
public static StringBuilder AddNames(this StringBuilder builder, IEnumerable<Type> types)
78+
{
79+
foreach (var type in types)
80+
{
81+
builder.AddName(type);
82+
if (type.IsConstructedGenericType)
83+
{
84+
builder.Append("Of").AddNames(type.GenericTypeArguments);
85+
}
86+
}
87+
return builder;
88+
}
89+
90+
public static StringBuilder AddName(this StringBuilder builder, Type type)
91+
=> type.IsGenericType ? builder.Append(type.Name.Substring(0, type.Name.IndexOf('`'))) : builder.Append(type.Name);
3792
}
3893
}

src/Stunts/Stunts/Stunts.csproj

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
<Sdk Name="Microsoft.Build.CentralPackageVersions" />
33

44
<PropertyGroup>
5-
<TargetFramework>netstandard1.3</TargetFramework>
5+
<TargetFramework>netstandard2.0</TargetFramework>
66

77
<!-- Avoid restore graph errors since the main packaging project also has implicit PackageId=Stunts -->
88
<PackageId>Stunts.Core</PackageId>

0 commit comments

Comments
 (0)