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

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
using FluentAssertions;
using GalaxyCheck;
using NebulaCheck;
using System.Linq;
using Property = NebulaCheck.Property;
using Test = NebulaCheck.Test;

namespace Tests.V2.GenTests.ReflectedGenTests
{
public class AboutGeneratingStructs
{
private struct EmptyStruct
{
}

[Property]
public NebulaCheck.IGen<Test> ItCanGenerateAnEmptyStruct() =>
from value in DomainGen.Any()
from seed in DomainGen.Seed()
from size in DomainGen.Size()
select Property.ForThese(() =>
{
var gen = GalaxyCheck.Gen.Create<EmptyStruct>();

var instance = gen.SampleOne(seed: seed, size: size);

instance.Should().NotBeNull();
});

private struct StructWithOneProperty
{
public int Property { get; set; }

}

[Property]
public NebulaCheck.IGen<Test> ItCanGenerateAStructWithOneProperty() =>
from seed in DomainGen.Seed()
from size in DomainGen.Size()
select Property.ForThese(() =>
{
var gen = GalaxyCheck.Gen
.Factory()
.RegisterType(GalaxyCheck.Gen.Int32().Where(x => x != 0))
.Create<StructWithOneProperty>();

var instance = gen.SampleOne(seed: seed, size: size);

instance.Should().NotBeNull();
instance.Property.Should().NotBe(0);
});

private struct StructWithTwoProperties
{
public int Property1 { get; set; }
public int Property2 { get; set; }
}

[Property]
public NebulaCheck.IGen<Test> ItCanGenerateAStructWithTwoProperties() =>
from seed in DomainGen.Seed()
from size in DomainGen.Size()
select Property.ForThese(() =>
{
var gen = GalaxyCheck.Gen
.Factory()
.RegisterType(GalaxyCheck.Gen.Int32().Where(x => x != 0))
.Create<StructWithTwoProperties>();

var instance = gen.SampleOne(seed: 0);

instance.Should().NotBeNull();
instance.Property1.Should().NotBe(0);
instance.Property2.Should().NotBe(0);
});


private struct StructWithOneConstructorArgument
{
public StructWithOneConstructorArgument(int property)
{
Property = property;
UsedConstructor = true;
}

public int Property { get; set; }

public bool UsedConstructor { get; set; }
}

[Property]
public NebulaCheck.IGen<Test> ItCanGenerateAStructWithOneConstructorArgument() =>
from seed in DomainGen.Seed()
from size in DomainGen.Size()
select Property.ForThese(() =>
{
var gen = GalaxyCheck.Gen
.Factory()
.RegisterType(GalaxyCheck.Gen.Int32().Where(x => x != 0))
.Create<StructWithOneConstructorArgument>();

var instance = gen.SampleOne(seed: seed, size: size);

instance.Should().NotBeNull();
instance.Property.Should().NotBe(0);
instance.UsedConstructor.Should().BeTrue();
});
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,15 +14,7 @@ public class AboutOverridingMembers
{
private record RecordWithOneProperty(object Property);

private class ClassWithOneConstructorArgument
{
public object Property { get; }

public ClassWithOneConstructorArgument(object property)
{
Property = property;
}
}

[Property]
public NebulaCheck.IGen<Test> ItCanOverrideAnExternalInitProperty() =>
Expand Down
10 changes: 10 additions & 0 deletions src/GalaxyCheck.Tests/RendererTests/AboutUnaryExamples.cs
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,7 @@ public void TupleExamples(object value, string expectedRendering)
{ new Action(() => { }), "System.Action" },
{ Operations.Create, "Create" },
{ new FaultyRecord(), "<Rendering failed>" },
{ new Struct { A = 1, B = 2, C = 3 }, "{ A = 1, B = 2, C = 3 }" }
};

[Theory]
Expand Down Expand Up @@ -142,5 +143,14 @@ public enum Operations
Create,
Read
}

public struct Struct
{
public int A { get; set; }

public int B { get; set; }

public int C { get; set; }
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,20 @@ public DefaultConstructorReflectedGenHandler(ContextualErrorFactory errorFactory
_errorFactory = errorFactory;
}

public bool CanHandleGen(Type type, ReflectedGenHandlerContext context) =>
type.GetConstructors().Any(constructor => constructor.GetParameters().Any() == false);
public bool CanHandleGen(Type type, ReflectedGenHandlerContext context)
{
if (IsStruct(type))
{
// Structs have slightly different constructor semantics.
return type.GetConstructors().Any() == false;
}
else
{
return type.GetConstructors().Any(constructor => constructor.GetParameters().Any() == false);
}
}

private static bool IsStruct(Type type) => type.IsValueType && !type.IsPrimitive && !type.IsEnum;

public IGen CreateGen(IReflectedGenHandler innerHandler, Type type, ReflectedGenHandlerContext context)
{
Expand All @@ -36,10 +48,10 @@ private static IGen<T> CreateGenGeneric<T>(IReflectedGenHandler innerHandler, Re
Gen.Zip(CreateSetFieldActionGens(innerHandler, typeof(T), context)))
.SelectMany((x) =>
{
T instance;
object instance;
try
{
instance = (T)Activator.CreateInstance(typeof(T))!;
instance = Activator.CreateInstance(typeof(T))!;
}
catch (TargetInvocationException ex)
{
Expand All @@ -52,7 +64,7 @@ private static IGen<T> CreateGenGeneric<T>(IReflectedGenHandler innerHandler, Re
{
try
{
setPropertyAction(instance);
setPropertyAction(ref instance);
}
catch (TargetInvocationException ex)
{
Expand All @@ -64,14 +76,16 @@ private static IGen<T> CreateGenGeneric<T>(IReflectedGenHandler innerHandler, Re

foreach (var setFieldAction in x.Item2)
{
setFieldAction(instance);
setFieldAction(ref instance);
}

return Gen.Constant(instance);
return Gen.Constant((T)instance);
});
}

private delegate void SetMemberAction(ref object instance);

private static IEnumerable<IGen<Action<object>>> CreateSetPropertyActionGens(IReflectedGenHandler innerHandler, Type type, ReflectedGenHandlerContext parentContext)
private static IEnumerable<IGen<SetMemberAction>> CreateSetPropertyActionGens(IReflectedGenHandler innerHandler, Type type, ReflectedGenHandlerContext parentContext)
{
var nullabilityInfoContext = new NullabilityInfoContext();
return type
Expand All @@ -84,11 +98,11 @@ private static IEnumerable<IGen<Action<object>>> CreateSetPropertyActionGens(IRe
.CreateGen(property.PropertyType, context)
.Cast<object>()
.Advanced.ReferenceRngWaypoint(rngWaypoint => rngWaypoint.Influence(context.CalculateStableSeed()))
.Select((Func<object?, Action<object>>)(value => obj => property.SetValue(obj, value)));
.Select((Func<object?, SetMemberAction>)(value => (ref object obj) => property.SetValue(obj, value)));
});
}

private static IEnumerable<IGen<Action<object>>> CreateSetFieldActionGens(IReflectedGenHandler innerHandler, Type type, ReflectedGenHandlerContext parentContext)
private static IEnumerable<IGen<SetMemberAction>> CreateSetFieldActionGens(IReflectedGenHandler innerHandler, Type type, ReflectedGenHandlerContext parentContext)
{
var nullabilityInfoContext = new NullabilityInfoContext();
return type
Expand All @@ -101,7 +115,7 @@ private static IEnumerable<IGen<Action<object>>> CreateSetFieldActionGens(IRefle
.CreateGen(field.FieldType, context)
.Cast<object>()
.Advanced.ReferenceRngWaypoint(rngWaypoint => rngWaypoint.Influence(context.CalculateStableSeed()))
.Select((Func<object?, Action<object>>)(value => obj => field.SetValue(obj, value)));
.Select((Func<object?, SetMemberAction>)(value => (ref object obj) => field.SetValue(obj, value)));
});
}
}
Expand Down