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
7 changes: 6 additions & 1 deletion src/GalaxyCheck.Tests.V3/DomainGen.cs
Original file line number Diff line number Diff line change
Expand Up @@ -35,12 +35,17 @@ from testFunction in TestFunctions.Nullary()
public static Stable.IGen<int> Size() =>
Stable.Gen.Int32().Between(GalaxyCheck.Gens.Parameters.Size.Zero.Value, GalaxyCheck.Gens.Parameters.Size.Max.Value);

public static Stable.IGen<int> Seed() => Stable.Gen.Int32();
public static Stable.IGen<int> Seed() => Stable.Gen.Int32().NoShrink();

public static Stable.IGen<int?> SeedWaypoint() => Seed().OrNullStruct();

public static Stable.IGen<Property> ToProperty(this Stable.IGen<IGen<object>> metaGen) =>
from gen in metaGen
from testFunction in TestFunctions.Unary<object>()
select new Property((IGen<Property.Test<object>>)Property.ForAll<object>(gen, testFunction));

public class SeedAttribute : Stable.GenAttribute
{
public override Stable.IGen Value => Seed();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ static void Test(int seed, int size)
var filteredGen = baseGen.Where(testFunction);

// Act
var sample = filteredGen.Sample(args => args with { Seed = seed, Size = size });
var sample = filteredGen.SampleTraversal(args => args with { Seed = seed, Size = size });

// Assert
sample.Should().OnlyContain(x => testFunction(x));
Expand All @@ -35,7 +35,7 @@ static void Test(int seed, int size)
var filteredGen = baseGen.Where(testFunction);

// Act
var act = () => filteredGen.Sample(args => args with { Seed = seed, Size = size });
var act = () => filteredGen.SampleTraversal(args => args with { Seed = seed, Size = size });

// Assert
act.Should().NotThrow<Exceptions.GenExhaustionException>();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ public void ItCanGenerateTheType(Type type)
var gen = Gen.Create(type);

// Act
var sample = gen.Sample();
var sample = gen.SampleTraversal();

// Assert
sample.Should().AllBeAssignableTo(type);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
namespace GalaxyCheck.Tests.Features.Generators.Reflection.ObjectGraph;

public class AboutErrorReproduction
{
[Stable.Property]
[InlineData(typeof(ExampleObjects.UnconstructableClass), "")]
[InlineData(typeof(ClassWithNestedUnconstructableClass), " at path '$.Property'")]
[InlineData(typeof(ClassWithNestedNullableUnconstructableClass), " at path '$.Property'")]
[InlineData(typeof(List<ExampleObjects.UnconstructableClass>), " at path '$.[*]'")]
public Stable.Property WhenTypeIsUnresolvable(Type type, string expectedPathDescription)
{
return Stable.Property.ForAll(DomainGen.Seed(), seed =>
{
// Arrange
var gen = Gen.Create(type);

// Act
var action = () => gen.Sample(args => args with { Seed = seed });

// Assert
action
.Should()
.Throw<Exceptions.GenErrorException>()
.WithGenErrorMessage($"could not resolve type '*'{expectedPathDescription}");
});
}

private class ClassWithNestedUnconstructableClass
{
public ExampleObjects.UnconstructableClass Property { get; set; } = null!;
}

private class ClassWithNestedNullableUnconstructableClass
{
public ExampleObjects.UnconstructableClass? Property { get; set; } = null!;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
namespace GalaxyCheck.Tests.Features.Generators.Reflection.ObjectGraph;

public static class ExampleObjects
{
public class ClassWithFailingConstructor
{
private static readonly Exception _exception = new("Constructor failed");

public ClassWithFailingConstructor()
{
throw _exception;
}
}

public class ClassWithNestedFailingConstructor
{
public ClassWithFailingConstructor Property { get; set; } = null!;
}

public class ClassWithArrayOfFailingConstructors
{
public ClassWithFailingConstructor[] Property { get; set; } = null!;
}

public class ClassWithArrayOfNestedFailingConstructors
{
public ClassWithNestedFailingConstructor[] Property { get; set; } = null!;
}

public class ClassWithFailingConstructorAsConstructorParameter
{
public ClassWithFailingConstructor Property { get; set; }

public ClassWithFailingConstructorAsConstructorParameter(ClassWithFailingConstructor property)
{
Property = property;
}
}

public class ClassWithNestedFailingConstructorAsConstructorParameter
{
public ClassWithNestedFailingConstructor Property { get; set; }

public ClassWithNestedFailingConstructorAsConstructorParameter(ClassWithNestedFailingConstructor property)
{
Property = property;
}
}

public class ClassWithOneProperty
{
public int Property { get; set; }
}

public class ClassWithTwoProperties
{
public int Property1 { get; set; }
public int Property2 { get; set; }
}

public class ClassWithOneNestedProperty
{
public ClassWithOneProperty Property { get; set; } = null!;
}

public abstract class UnconstructableClass
{
}
}
6 changes: 1 addition & 5 deletions src/GalaxyCheck.Tests.V3/GalaxyCheck.Tests.V3.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@

<ItemGroup>
<PackageReference Include="FluentAssertions" Version="6.11.0" />
<PackageReference Include="GalaxyCheck.Stable.Xunit" Version="0.0.0-5085494672" />
<PackageReference Include="GalaxyCheck.Stable.Xunit" Version="0.0.0-5131115847" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.3.2" />
<PackageReference Include="Snapshooter.Xunit" Version="0.5.8" />
<PackageReference Include="xunit" Version="2.4.2" />
Expand All @@ -31,8 +31,4 @@
<ProjectReference Include="..\GalaxyCheck\GalaxyCheck.csproj" />
</ItemGroup>

<ItemGroup>
<Folder Include="IntegrationTests" />
</ItemGroup>

</Project>
18 changes: 16 additions & 2 deletions src/GalaxyCheck.Tests.V3/TestProxy.cs
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ public static CheckResult<object> Check(this PropertyProxy propertyProxy, Func<C
/// Samples a generator by traversing the example space. This is useful to ensure that not only the produced value satisfies some invariant, but
/// also all shrunk values.
/// </summary>
public static IReadOnlyCollection<T> Sample<T>(this IGen<T> gen, Func<SampleGenArgs, SampleGenArgs>? configure = null)
public static IReadOnlyCollection<T> SampleTraversal<T>(this IGen<T> gen, Func<SampleGenArgs, SampleGenArgs>? configure = null)
{
var args = SampleGenArgs.Default;
if (configure != null)
Expand All @@ -84,12 +84,26 @@ public static IReadOnlyCollection<T> Sample<T>(this IGen<T> gen, Func<SampleGenA
}

return gen.Advanced
.SampleOneExampleSpace(seed: args.Seed, size: args.Size)
.SampleOneExampleSpace(seed: args.Seed, size: args.Size ?? 100)
.Traverse()
.Take(args.MaxSampleSize)
.ToList();
}

public static IReadOnlyCollection<T> Sample<T>(this IGen<T> gen, Func<SampleGenArgs, SampleGenArgs>? configure = null)
{
var args = SampleGenArgs.Default;
if (configure != null)
{
args = configure(args);
}

return gen
.Sample(seed: args.Seed, size: args.Size ?? 100)
.Take(args.MaxSampleSize)
.ToList();
}

public record CheckPropertyArgs(int Seed, int? Size, string? Replay)
{
public static CheckPropertyArgs Default { get; } = new(0, null, null);
Expand Down
13 changes: 13 additions & 0 deletions src/GalaxyCheck.Tests.V3/TestUtility/AssertionExtensions.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
using FluentAssertions.Specialized;

namespace GalaxyCheck.Tests.TestUtility;

public static class AssertionExtensions
{
public static ExceptionAssertions<Exceptions.GenErrorException> WithGenErrorMessage(
this ExceptionAssertions<Exceptions.GenErrorException> assertions,
string messageBody)
{
return assertions.WithMessage("Error during generation: " + messageBody);
}
}
2 changes: 1 addition & 1 deletion src/GalaxyCheck/Gens/FactoryGen.cs
Original file line number Diff line number Diff line change
Expand Up @@ -229,7 +229,7 @@ protected override IGen<T> Get
var context = ReflectedGenHandlerContext.Create(typeof(T), NullabilityInfo);

return ReflectedGenBuilder
.Build(typeof(T), RegisteredGensByType, MemberOverrides, Error, context)
.Build(typeof(T), RegisteredGensByType, MemberOverrides, context)
.Cast<T>();
}
}
Expand Down
6 changes: 3 additions & 3 deletions src/GalaxyCheck/Gens/ReflectedGenHelpers/ErrorFactory.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
namespace GalaxyCheck.Gens.ReflectedGenHelpers
using System;

namespace GalaxyCheck.Gens.ReflectedGenHelpers
{
internal delegate IGen ErrorFactory(string message);

internal delegate IGen ContextualErrorFactory(string message, ReflectedGenHandlerContext context);
}
24 changes: 12 additions & 12 deletions src/GalaxyCheck/Gens/ReflectedGenHelpers/ReflectedGenBuilder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,30 +11,30 @@ public static IGen Build(
Type type,
IReadOnlyDictionary<Type, Func<IGen>> registeredGensByType,
IReadOnlyList<ReflectedGenMemberOverride> memberOverrides,
ErrorFactory errorFactory,
ReflectedGenHandlerContext context)
{
ContextualErrorFactory contextualErrorFactory = (message, context) =>
{
var suffix = context.Members.Count() == 1 ? "" : $" at path '{context.MemberPath}'";
return errorFactory(message + suffix);
};

var genFactoriesByPriority = new List<IReflectedGenHandler>
{
new MemberOverrideReflectedGenHandler(memberOverrides),
new NullableGenHandler(),
new RegistryReflectedGenHandler(registeredGensByType, contextualErrorFactory),
new RegistryReflectedGenHandler(registeredGensByType),
new CollectionReflectedGenHandler(),
new ArrayReflectedGenHandler(),
new EnumReflectedGenHandler(),
new DefaultConstructorReflectedGenHandler(contextualErrorFactory),
new NonDefaultConstructorReflectedGenHandler(contextualErrorFactory),
new DefaultConstructorReflectedGenHandler(),
new NonDefaultConstructorReflectedGenHandler(),
};

var compositeReflectedGenFactory = new CompositeReflectedGenHandler(genFactoriesByPriority, contextualErrorFactory);
var compositeReflectedGenFactory = new CompositeReflectedGenHandler(genFactoriesByPriority);

return compositeReflectedGenFactory.CreateGen(type, context);
try
{
return compositeReflectedGenFactory.CreateGen(type, context);
}
catch (Exception ex)
{
return Gen.Advanced.Error(type, $"{ex.Message} \n {ex.StackTrace}");
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -36,5 +36,17 @@ public static int CalculateStableSeed(this ReflectedGenHandlerContext context) =
return acc + curr;
}
});

public static IGen<T> Error<T>(this ReflectedGenHandlerContext context, string message)
{
var suffix = context.Members.Count == 1 ? "" : $" at path '{context.MemberPath}'";
return Gen.Advanced.Error<T>(message + suffix);
}

public static IGen Error(this ReflectedGenHandlerContext context, Type type, string message)
{
var suffix = context.Members.Count == 1 ? "" : $" at path '{context.MemberPath}'";
return Gen.Advanced.Error(type, message + suffix);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,10 @@ namespace GalaxyCheck.Gens.ReflectedGenHelpers.ReflectedGenHandlers
internal class CompositeReflectedGenHandler : IReflectedGenHandler
{
private readonly IReadOnlyList<IReflectedGenHandler> _genHandlersByPriority;
private readonly ContextualErrorFactory _errorFactory;

public CompositeReflectedGenHandler(
IReadOnlyList<IReflectedGenHandler> genHandlersByPriority,
ContextualErrorFactory errorFactory)
public CompositeReflectedGenHandler(IReadOnlyList<IReflectedGenHandler> genHandlersByPriority)
{
_genHandlersByPriority = genHandlersByPriority;
_errorFactory = errorFactory;
}

public bool CanHandleGen(Type type, ReflectedGenHandlerContext context) =>
Expand All @@ -24,7 +20,7 @@ public IGen CreateGen(IReflectedGenHandler innerHandler, Type type, ReflectedGen
{
if (context.TypeHistory.Skip(1).Any(t => t == type))
{
return _errorFactory($"detected circular reference on type '{type}'", context);
return context.Error(type, $"detected circular reference on type '{type}'");
}

var gen = _genHandlersByPriority
Expand All @@ -34,7 +30,7 @@ public IGen CreateGen(IReflectedGenHandler innerHandler, Type type, ReflectedGen

if (gen == null)
{
return _errorFactory($"could not resolve type '{type}'", context);
return context.Error(type, $"could not resolve type '{type}'");
}

return gen;
Expand Down
Loading