Skip to content

Commit 596fdc7

Browse files
authored
test: cover proxy generator edge cases (#417)
1 parent 32413fc commit 596fdc7

2 files changed

Lines changed: 127 additions & 2 deletions

File tree

src/PatternKit.Generators/ProxyGenerator.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -883,7 +883,7 @@ private void GenerateProxyMethod(StringBuilder sb, MemberInfo member, ContractIn
883883
// Note: ref-returning methods cannot be async
884884
bool useAsync = contractInfo.HasAsyncMembers &&
885885
config.InterceptorMode != ProxyInterceptorMode.None &&
886-
(member.IsAsync || member.HasCancellationToken) &&
886+
member.IsAsync &&
887887
!member.ReturnsByRef &&
888888
!member.ReturnsByRefReadonly;
889889
var asyncModifier = useAsync ? "async " : "";
@@ -1035,7 +1035,7 @@ private void GenerateInterceptedDelegation(StringBuilder sb, MemberInfo member,
10351035
sb.AppendLine();
10361036

10371037
// Use async or sync based on detection and configuration
1038-
bool useAsync = contractInfo.HasAsyncMembers && (member.IsAsync || member.HasCancellationToken);
1038+
bool useAsync = contractInfo.HasAsyncMembers && member.IsAsync;
10391039

10401040
if (useAsync)
10411041
{

test/PatternKit.Generators.Tests/ProxyGeneratorTests.cs

Lines changed: 125 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1226,6 +1226,8 @@ public void GenerateProxy_AsyncPipelineWithVoidTasksAndRefs_CoversAsyncEmission(
12261226
public partial interface Worker
12271227
{
12281228
void Copy(ref int source, out int destination, in bool enabled);
1229+
void Cancelable(CancellationToken cancellationToken = default);
1230+
int CountWithCancellation(ref int source, out int destination, in bool enabled, CancellationToken cancellationToken = default);
12291231
Task SaveAsync(int id, CancellationToken ct = default);
12301232
ValueTask FlushAsync(CancellationToken ct = default);
12311233
Task<int> CountAsync(CancellationToken ct = default);
@@ -1245,6 +1247,8 @@ public partial interface Worker
12451247
ScenarioExpect.Contains("for (int __i = 0; __i < _interceptors!.Count; __i++)", proxySource);
12461248
ScenarioExpect.Contains("for (int __i = _interceptors!.Count - 1; __i >= 0; __i--)", proxySource);
12471249
ScenarioExpect.Contains("_inner.Copy(ref source, out destination, in enabled);", proxySource);
1250+
ScenarioExpect.Contains("_inner.Cancelable(cancellationToken);", proxySource);
1251+
ScenarioExpect.Contains("var __result = _inner.CountWithCancellation(ref source, out destination, in enabled, cancellationToken);", proxySource);
12481252
ScenarioExpect.Contains("await __task.ConfigureAwait(false);", proxySource);
12491253
ScenarioExpect.Contains("var __result = await __task.ConfigureAwait(false);", proxySource);
12501254
ScenarioExpect.Contains("return default!;", proxySource);
@@ -1306,6 +1310,7 @@ public abstract partial class WorkerContract
13061310
public static string StaticValue => "ignored";
13071311
13081312
public abstract void Copy(ref int source, out int destination, in bool enabled);
1313+
public abstract int CopyAndCount(ref int source, out int destination, in bool enabled);
13091314
public abstract int Calculate(int value);
13101315
public virtual string Virtual(string value) => value;
13111316
public string Concrete(string value) => value;
@@ -1328,6 +1333,7 @@ public abstract partial class WorkerContract
13281333
ScenarioExpect.Contains("set => _inner.Name = value;", proxySource);
13291334
ScenarioExpect.Contains("Version", proxySource);
13301335
ScenarioExpect.Contains("_inner.Copy(ref source, out destination, in enabled);", proxySource);
1336+
ScenarioExpect.Contains("return _inner.CopyAndCount(ref source, out destination, in enabled);", proxySource);
13311337
ScenarioExpect.Contains("return _inner.Calculate(value);", proxySource);
13321338
ScenarioExpect.Contains("return _inner.Virtual(value);", proxySource);
13331339
ScenarioExpect.DoesNotContain("Concrete", proxySource);
@@ -1362,4 +1368,123 @@ public partial interface IEmptyProxy
13621368
ScenarioExpect.All(result.Results, r => ScenarioExpect.Empty(r.Diagnostics));
13631369
ScenarioExpect.Empty(result.Results.SelectMany(r => r.GeneratedSources));
13641370
}
1371+
1372+
[Scenario("GenerateProxy GenerateAsyncTrue EmitsAsyncInterceptorsFromExplicitOption")]
1373+
[Fact]
1374+
public void GenerateProxy_GenerateAsyncTrue_EmitsAsyncInterceptorsFromExplicitOption()
1375+
{
1376+
const string source = """
1377+
using PatternKit.Generators.Proxy;
1378+
using System.Threading;
1379+
using System.Threading.Tasks;
1380+
1381+
namespace TestNamespace;
1382+
1383+
[GenerateProxy(GenerateAsync = true)]
1384+
public partial interface IExplicitAsyncProxy
1385+
{
1386+
Task<string> LoadAsync(CancellationToken cancellationToken = default);
1387+
string Poll(CancellationToken cancellationToken = default);
1388+
}
1389+
""";
1390+
1391+
var comp = RoslynTestHelpers.CreateCompilation(source, nameof(GenerateProxy_GenerateAsyncTrue_EmitsAsyncInterceptorsFromExplicitOption));
1392+
var gen = new ProxyGenerator();
1393+
_ = RoslynTestHelpers.Run(comp, gen, out var result, out _);
1394+
1395+
ScenarioExpect.All(result.Results, r => ScenarioExpect.Empty(r.Diagnostics));
1396+
1397+
var interceptorSource = result.Results
1398+
.SelectMany(r => r.GeneratedSources)
1399+
.First(gs => gs.HintName == "TestNamespace_IExplicitAsyncProxy.Proxy.Interceptor.g.cs")
1400+
.SourceText.ToString();
1401+
1402+
ScenarioExpect.Contains("BeforeAsync", interceptorSource);
1403+
ScenarioExpect.Contains("void Before(MethodContext context)", interceptorSource);
1404+
}
1405+
1406+
[Scenario("GenerateProxy InternalProtectedInternalMembers GenerateAccessibleOverrides")]
1407+
[Fact]
1408+
public void GenerateProxy_InternalProtectedInternalMembers_GenerateAccessibleOverrides()
1409+
{
1410+
const string source = """
1411+
using PatternKit.Generators.Proxy;
1412+
1413+
namespace TestNamespace;
1414+
1415+
[GenerateProxy]
1416+
internal abstract partial class InternalContract
1417+
{
1418+
protected internal abstract string Name { get; set; }
1419+
protected internal abstract int Count(ref int source, out int destination, in bool enabled);
1420+
}
1421+
""";
1422+
1423+
var comp = RoslynTestHelpers.CreateCompilation(source, nameof(GenerateProxy_InternalProtectedInternalMembers_GenerateAccessibleOverrides));
1424+
var gen = new ProxyGenerator();
1425+
_ = RoslynTestHelpers.Run(comp, gen, out var result, out var updated);
1426+
1427+
ScenarioExpect.All(result.Results, r => ScenarioExpect.Empty(r.Diagnostics));
1428+
1429+
var proxySource = result.Results
1430+
.SelectMany(r => r.GeneratedSources)
1431+
.Single(gs => gs.HintName == "TestNamespace_InternalContract.Proxy.g.cs")
1432+
.SourceText.ToString();
1433+
1434+
ScenarioExpect.Contains("internal sealed partial class InternalContractProxy", proxySource);
1435+
ScenarioExpect.Contains("protected internal override string Name", proxySource);
1436+
ScenarioExpect.Contains("protected internal override int Count(ref int source, out int destination, in bool enabled)", proxySource);
1437+
ScenarioExpect.Contains("return _inner.Count(ref source, out destination, in enabled);", proxySource);
1438+
1439+
var emit = updated.Emit(Stream.Null);
1440+
ScenarioExpect.True(emit.Success, string.Join("\n", emit.Diagnostics));
1441+
}
1442+
1443+
[Scenario("GenerateProxy Defaults CoverNullableEnumAndCharLiteralBranches")]
1444+
[Fact]
1445+
public void GenerateProxy_Defaults_CoverNullableEnumAndCharLiteralBranches()
1446+
{
1447+
const string source = """
1448+
using PatternKit.Generators.Proxy;
1449+
1450+
namespace TestNamespace;
1451+
1452+
public enum Mode { None = 0, Known = 1 }
1453+
1454+
[GenerateProxy(InterceptorMode = ProxyInterceptorMode.None)]
1455+
public partial interface IDefaultLiteralProxy
1456+
{
1457+
string Format(
1458+
int? optional = null,
1459+
Mode missing = (Mode)99,
1460+
char slash = '\\',
1461+
char carriageReturn = '\r',
1462+
char tab = '\t',
1463+
char zero = '\0',
1464+
char plain = 'x');
1465+
}
1466+
""";
1467+
1468+
var comp = RoslynTestHelpers.CreateCompilation(source, nameof(GenerateProxy_Defaults_CoverNullableEnumAndCharLiteralBranches));
1469+
var gen = new ProxyGenerator();
1470+
_ = RoslynTestHelpers.Run(comp, gen, out var result, out var updated);
1471+
1472+
ScenarioExpect.All(result.Results, r => ScenarioExpect.Empty(r.Diagnostics));
1473+
1474+
var proxySource = result.Results
1475+
.SelectMany(r => r.GeneratedSources)
1476+
.Single(gs => gs.HintName == "TestNamespace_IDefaultLiteralProxy.Proxy.g.cs")
1477+
.SourceText.ToString();
1478+
1479+
ScenarioExpect.Contains("int? optional = null", proxySource);
1480+
ScenarioExpect.Contains("Mode missing = (global::TestNamespace.Mode)99", proxySource);
1481+
ScenarioExpect.Contains("char slash = '\\\\'", proxySource);
1482+
ScenarioExpect.Contains("char carriageReturn = '\\r'", proxySource);
1483+
ScenarioExpect.Contains("char tab = '\\t'", proxySource);
1484+
ScenarioExpect.Contains("char zero = '\\0'", proxySource);
1485+
ScenarioExpect.Contains("char plain = 'x'", proxySource);
1486+
1487+
var emit = updated.Emit(Stream.Null);
1488+
ScenarioExpect.True(emit.Success, string.Join("\n", emit.Diagnostics));
1489+
}
13651490
}

0 commit comments

Comments
 (0)