Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
using EasySourceGenerators.Generators.DataBuilding;
using EasySourceGenerators.Generators.IncrementalGenerators;

namespace EasySourceGenerators.GeneratorTests;

[TestFixture]
public class BodyGenerationDataExtractorTests
{
[Test]
public void Extract_WithCompileTimeConstants_BodyReturningConstant_InvokesWithConstants()
{
DataMethodBodyGenerator generator = new DataMethodBodyGenerator(
new BodyGenerationData(
ReturnType: typeof(string),
ParametersTypes: [],
CompileTimeConstants: 42,
ReturnConstantValueFactory: (Func<int, string>)(constants => $"value_{constants}")));

FluentBodyResult result = BodyGenerationDataExtractor.Extract(generator, false);

Assert.That(result.ReturnValue, Is.EqualTo("value_42"));
Assert.That(result.IsVoid, Is.False);
}

[Test]
public void Extract_WithCompileTimeConstants_RuntimeBodyNoArgs_InvokesWithConstants()
{
DataMethodBodyGenerator generator = new DataMethodBodyGenerator(
new BodyGenerationData(
ReturnType: typeof(string),
ParametersTypes: [],
CompileTimeConstants: 10,
RuntimeDelegateBody: (Func<int, string>)(constants => $"body_{constants}")));

FluentBodyResult result = BodyGenerationDataExtractor.Extract(generator, false);

Assert.That(result.ReturnValue, Is.EqualTo("body_10"));
Assert.That(result.IsVoid, Is.False);
}

[Test]
public void Extract_WithCompileTimeConstants_RuntimeBodyWithAdditionalParams_ReturnsNullValue()
{
DataMethodBodyGenerator generator = new DataMethodBodyGenerator(
new BodyGenerationData(
ReturnType: typeof(string),
ParametersTypes: [typeof(int)],
CompileTimeConstants: 10,
RuntimeDelegateBody: (Func<int, int, string>)((constants, param) => $"{constants}_{param}")));

FluentBodyResult result = BodyGenerationDataExtractor.Extract(generator, false);

Assert.That(result.ReturnValue, Is.Null);
Assert.That(result.IsVoid, Is.False);
Comment on lines +42 to +54
Copy link

Copilot AI Apr 2, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This test currently asserts that a constants-aware delegate with additional method parameters yields ReturnValue == null. That behavior will cause the generator to emit a default/empty implementation without diagnostics (since HasDelegateBody is also forced false when constants exist). Consider changing the expected behavior to either trigger syntax extraction (HasDelegateBody=true) or to fail with a clear diagnostic so this scenario can’t silently succeed.

Copilot uses AI. Check for mistakes.
}

[Test]
public void Extract_WithoutConstants_BodyReturningConstant_InvokesWithoutArgs()
{
DataMethodBodyGenerator generator = new DataMethodBodyGenerator(
new BodyGenerationData(
ReturnType: typeof(int),
ParametersTypes: [],
ReturnConstantValueFactory: (Func<int>)(() => 99)));

FluentBodyResult result = BodyGenerationDataExtractor.Extract(generator, false);

Assert.That(result.ReturnValue, Is.EqualTo("99"));
}

[Test]
public void Extract_WithoutConstants_RuntimeBodyNoParams_InvokesDirectly()
{
DataMethodBodyGenerator generator = new DataMethodBodyGenerator(
new BodyGenerationData(
ReturnType: typeof(string),
ParametersTypes: [],
RuntimeDelegateBody: (Func<string>)(() => "hello")));

FluentBodyResult result = BodyGenerationDataExtractor.Extract(generator, false);

Assert.That(result.ReturnValue, Is.EqualTo("hello"));
}

[Test]
public void Extract_VoidReturnType_WithConstants_RuntimeBody_InvokesWithConstants()
{
string captured = "";
DataMethodBodyGenerator generator = new DataMethodBodyGenerator(
new BodyGenerationData(
ReturnType: typeof(void),
ParametersTypes: [],
CompileTimeConstants: "test",
RuntimeDelegateBody: (Action<string>)(constants => { captured = constants; })));

FluentBodyResult result = BodyGenerationDataExtractor.Extract(generator, true);

Assert.That(result.IsVoid, Is.True);
Assert.That(captured, Is.EqualTo("test"));
}

[Test]
public void Extract_NullBodyGenerationData_ReturnsNullReturnValue()
{
DataMethodBodyGenerator generator = new DataMethodBodyGenerator(
new BodyGenerationData(ReturnType: typeof(string)));

FluentBodyResult result = BodyGenerationDataExtractor.Extract(generator, false);

Assert.That(result.ReturnValue, Is.Null);
}

[Test]
public void Extract_WithCompileTimeConstants_ConstantFactoryTakesPriority()
{
DataMethodBodyGenerator generator = new DataMethodBodyGenerator(
new BodyGenerationData(
ReturnType: typeof(string),
ParametersTypes: [],
CompileTimeConstants: 5,
ReturnConstantValueFactory: (Func<int, string>)(constants => $"factory_{constants}"),
RuntimeDelegateBody: (Func<int, string>)(constants => $"body_{constants}")));

FluentBodyResult result = BodyGenerationDataExtractor.Extract(generator, false);

Assert.That(result.ReturnValue, Is.EqualTo("factory_5"));
}
}
194 changes: 194 additions & 0 deletions EasySourceGenerators.GeneratorTests/MethodBodyBuilderTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -189,4 +189,198 @@ public void FullFluentChain_UseProvidedBody_ProducesCorrectData()
object? bodyValue = generator.Data.RuntimeDelegateBody!.DynamicInvoke();
Assert.That(bodyValue, Is.EqualTo(42));
}

[Test]
public void WithCompileTimeConstants_WithParam_ReturnsStage5WithConstants()
{
DataMethodBodyBuilderStage4<int, string> stage4 = new DataMethodBodyBuilderStage4<int, string>(
new BodyGenerationData(ReturnType: typeof(string), ParametersTypes: [typeof(int)]));

IMethodBodyBuilderStage5WithConstants<int, string, int> result = stage4.WithCompileTimeConstants(() => 42);

Assert.That(result, Is.TypeOf<DataMethodBodyBuilderStage5WithConstants<int, string, int>>());
DataMethodBodyBuilderStage5WithConstants<int, string, int> stage5 = (DataMethodBodyBuilderStage5WithConstants<int, string, int>)result;
Assert.That(stage5.Data.CompileTimeConstants, Is.EqualTo(42));
}

[Test]
public void WithCompileTimeConstants_NoArg_ReturnsStage5NoArgWithConstants()
{
DataMethodBodyBuilderStage4NoArg<string> stage4 = new DataMethodBodyBuilderStage4NoArg<string>(
new BodyGenerationData(ReturnType: typeof(string), ParametersTypes: []));

IMethodBodyBuilderStage5NoArgWithConstants<string, int> result = stage4.WithCompileTimeConstants(() => 99);

Assert.That(result, Is.TypeOf<DataMethodBodyBuilderStage5NoArgWithConstants<string, int>>());
DataMethodBodyBuilderStage5NoArgWithConstants<string, int> stage5 = (DataMethodBodyBuilderStage5NoArgWithConstants<string, int>)result;
Assert.That(stage5.Data.CompileTimeConstants, Is.EqualTo(99));
}

[Test]
public void WithCompileTimeConstants_ReturnVoid_ReturnsStage5ReturnVoidWithConstants()
{
DataMethodBodyBuilderStage4ReturnVoid<int> stage4 = new DataMethodBodyBuilderStage4ReturnVoid<int>(
new BodyGenerationData(ReturnType: typeof(void), ParametersTypes: [typeof(int)]));

IMethodBodyBuilderStage5ReturnVoidWithConstants<int, string> result = stage4.WithCompileTimeConstants(() => "test");

Assert.That(result, Is.TypeOf<DataMethodBodyBuilderStage5ReturnVoidWithConstants<int, string>>());
DataMethodBodyBuilderStage5ReturnVoidWithConstants<int, string> stage5 = (DataMethodBodyBuilderStage5ReturnVoidWithConstants<int, string>)result;
Assert.That(stage5.Data.CompileTimeConstants, Is.EqualTo("test"));
}

[Test]
public void WithCompileTimeConstants_ReturnVoidNoArg_ReturnsStage5ReturnVoidNoArgWithConstants()
{
DataMethodBodyBuilderStage4ReturnVoidNoArg stage4 = new DataMethodBodyBuilderStage4ReturnVoidNoArg(
new BodyGenerationData(ReturnType: typeof(void), ParametersTypes: []));

IMethodBodyBuilderStage5ReturnVoidNoArgWithConstants<int> result = stage4.WithCompileTimeConstants(() => 7);

Assert.That(result, Is.TypeOf<DataMethodBodyBuilderStage5ReturnVoidNoArgWithConstants<int>>());
DataMethodBodyBuilderStage5ReturnVoidNoArgWithConstants<int> stage5 = (DataMethodBodyBuilderStage5ReturnVoidNoArgWithConstants<int>)result;
Assert.That(stage5.Data.CompileTimeConstants, Is.EqualTo(7));
}

[Test]
public void Stage5WithConstants_UseProvidedBody_SetsRuntimeDelegateBody()
{
DataMethodBodyBuilderStage5WithConstants<int, string, int> stage5 = new DataMethodBodyBuilderStage5WithConstants<int, string, int>(
new BodyGenerationData(ReturnType: typeof(string), ParametersTypes: [typeof(int)], CompileTimeConstants: 42));

IMethodBodyGenerator result = stage5.UseProvidedBody((constants, param) => $"{constants}_{param}");

Assert.That(result, Is.TypeOf<DataMethodBodyGenerator>());
DataMethodBodyGenerator generator = (DataMethodBodyGenerator)result;
Assert.That(generator.Data.RuntimeDelegateBody, Is.Not.Null);
Assert.That(generator.Data.CompileTimeConstants, Is.EqualTo(42));
}

[Test]
public void Stage5WithConstants_BodyReturningConstant_SetsReturnConstantValueFactory()
{
DataMethodBodyBuilderStage5WithConstants<int, string, int> stage5 = new DataMethodBodyBuilderStage5WithConstants<int, string, int>(
new BodyGenerationData(ReturnType: typeof(string), ParametersTypes: [typeof(int)], CompileTimeConstants: 42));

IMethodBodyGenerator result = stage5.BodyReturningConstant(constants => $"value_{constants}");

Assert.That(result, Is.TypeOf<DataMethodBodyGenerator>());
DataMethodBodyGenerator generator = (DataMethodBodyGenerator)result;
Assert.That(generator.Data.ReturnConstantValueFactory, Is.Not.Null);
Assert.That(generator.Data.CompileTimeConstants, Is.EqualTo(42));
}

[Test]
public void Stage5NoArgWithConstants_UseProvidedBody_SetsRuntimeDelegateBody()
{
DataMethodBodyBuilderStage5NoArgWithConstants<string, int> stage5 = new DataMethodBodyBuilderStage5NoArgWithConstants<string, int>(
new BodyGenerationData(ReturnType: typeof(string), ParametersTypes: [], CompileTimeConstants: 10));

IMethodBodyGenerator result = stage5.UseProvidedBody(constants => $"value_{constants}");

Assert.That(result, Is.TypeOf<DataMethodBodyGenerator>());
DataMethodBodyGenerator generator = (DataMethodBodyGenerator)result;
Assert.That(generator.Data.RuntimeDelegateBody, Is.Not.Null);
Assert.That(generator.Data.CompileTimeConstants, Is.EqualTo(10));
}

[Test]
public void Stage5NoArgWithConstants_BodyReturningConstant_SetsReturnConstantValueFactory()
{
DataMethodBodyBuilderStage5NoArgWithConstants<string, int> stage5 = new DataMethodBodyBuilderStage5NoArgWithConstants<string, int>(
new BodyGenerationData(ReturnType: typeof(string), ParametersTypes: [], CompileTimeConstants: 10));

IMethodBodyGenerator result = stage5.BodyReturningConstant(constants => $"const_{constants}");

Assert.That(result, Is.TypeOf<DataMethodBodyGenerator>());
DataMethodBodyGenerator generator = (DataMethodBodyGenerator)result;
Assert.That(generator.Data.ReturnConstantValueFactory, Is.Not.Null);
Assert.That(generator.Data.CompileTimeConstants, Is.EqualTo(10));
}

[Test]
public void Stage5ReturnVoidWithConstants_UseProvidedBody_SetsRuntimeDelegateBody()
{
DataMethodBodyBuilderStage5ReturnVoidWithConstants<int, string> stage5 = new DataMethodBodyBuilderStage5ReturnVoidWithConstants<int, string>(
new BodyGenerationData(ReturnType: typeof(void), ParametersTypes: [typeof(int)], CompileTimeConstants: "ctx"));

IMethodBodyGenerator result = stage5.UseProvidedBody((constants, param) => { });

Assert.That(result, Is.TypeOf<DataMethodBodyGenerator>());
DataMethodBodyGenerator generator = (DataMethodBodyGenerator)result;
Assert.That(generator.Data.RuntimeDelegateBody, Is.Not.Null);
Assert.That(generator.Data.CompileTimeConstants, Is.EqualTo("ctx"));
}

[Test]
public void Stage5ReturnVoidNoArgWithConstants_UseProvidedBody_SetsRuntimeDelegateBody()
{
DataMethodBodyBuilderStage5ReturnVoidNoArgWithConstants<string> stage5 = new DataMethodBodyBuilderStage5ReturnVoidNoArgWithConstants<string>(
new BodyGenerationData(ReturnType: typeof(void), ParametersTypes: [], CompileTimeConstants: "ctx"));

IMethodBodyGenerator result = stage5.UseProvidedBody(constants => { });

Assert.That(result, Is.TypeOf<DataMethodBodyGenerator>());
DataMethodBodyGenerator generator = (DataMethodBodyGenerator)result;
Assert.That(generator.Data.RuntimeDelegateBody, Is.Not.Null);
Assert.That(generator.Data.CompileTimeConstants, Is.EqualTo("ctx"));
}

[Test]
public void FullFluentChain_WithConstants_NoArg_BodyReturningConstant_ProducesCorrectData()
{
DataGeneratorsFactory factory = new DataGeneratorsFactory();

IMethodBodyGenerator result = factory.StartFluentApiBuilderForBody()
.ForMethod()
.WithReturnType<string>()
.WithNoParameters()
.WithCompileTimeConstants(() => 42)
.BodyReturningConstant(constants => $"value_{constants}");

Assert.That(result, Is.TypeOf<DataMethodBodyGenerator>());
DataMethodBodyGenerator generator = (DataMethodBodyGenerator)result;
Assert.That(generator.Data.ReturnType, Is.EqualTo(typeof(string)));
Assert.That(generator.Data.ParametersTypes, Is.Empty);
Assert.That(generator.Data.CompileTimeConstants, Is.EqualTo(42));
Assert.That(generator.Data.ReturnConstantValueFactory, Is.Not.Null);
object? constantValue = generator.Data.ReturnConstantValueFactory!.DynamicInvoke(42);
Assert.That(constantValue, Is.EqualTo("value_42"));
}

[Test]
public void FullFluentChain_WithConstants_WithParam_UseProvidedBody_ProducesCorrectData()
{
DataGeneratorsFactory factory = new DataGeneratorsFactory();

IMethodBodyGenerator result = factory.StartFluentApiBuilderForBody()
.ForMethod()
.WithReturnType<int>()
.WithParameter<int>()
.WithCompileTimeConstants(() => new { Offset = 100 })
.UseProvidedBody((constants, param) => constants.Offset + param);

Assert.That(result, Is.TypeOf<DataMethodBodyGenerator>());
DataMethodBodyGenerator generator = (DataMethodBodyGenerator)result;
Assert.That(generator.Data.ReturnType, Is.EqualTo(typeof(int)));
Assert.That(generator.Data.ParametersTypes, Is.EqualTo(new[] { typeof(int) }));
Assert.That(generator.Data.CompileTimeConstants, Is.Not.Null);
Assert.That(generator.Data.RuntimeDelegateBody, Is.Not.Null);
Copy link

Copilot AI Apr 2, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This test validates that the builder stores CompileTimeConstants and RuntimeDelegateBody, but it doesn’t assert that the generator can actually produce a correct method body for the stage5 (constants, param) UseProvidedBody form. Adding an integration-style assertion (e.g., run the execution/runtime extraction path and ensure it doesn’t fall back to a default return value) would prevent regressions where constants-aware bodies are silently ignored.

Suggested change
Assert.That(generator.Data.RuntimeDelegateBody, Is.Not.Null);
Assert.That(generator.Data.RuntimeDelegateBody, Is.Not.Null);
// Execute the runtime body with the stored constants and a sample parameter
object? constants = generator.Data.CompileTimeConstants;
object? runtimeResult = generator.Data.RuntimeDelegateBody!.DynamicInvoke(constants, 23);
Assert.That(runtimeResult, Is.EqualTo(123));

Copilot uses AI. Check for mistakes.
}

[Test]
public void WithCompileTimeConstants_FactoryIsInvokedImmediately()
{
int invocationCount = 0;
DataMethodBodyBuilderStage4NoArg<string> stage4 = new DataMethodBodyBuilderStage4NoArg<string>(
new BodyGenerationData(ReturnType: typeof(string), ParametersTypes: []));

stage4.WithCompileTimeConstants(() =>
{
invocationCount++;
return 42;
});

Assert.That(invocationCount, Is.EqualTo(1));
}
}
Loading
Loading