diff --git a/src/GalaxyCheck.Tests/PropertyTests/AboutPreconditions/AboutPreconditions.cs b/src/GalaxyCheck.Tests/PropertyTests/AboutPreconditions/AboutPreconditions.cs index b2af32d1..9c6d646d 100644 --- a/src/GalaxyCheck.Tests/PropertyTests/AboutPreconditions/AboutPreconditions.cs +++ b/src/GalaxyCheck.Tests/PropertyTests/AboutPreconditions/AboutPreconditions.cs @@ -2,7 +2,6 @@ using GalaxyCheck; using System; using System.Linq; -using System.Reflection; using FluentAssertions; using Xunit; using static Tests.V2.DomainGenAttributes; @@ -58,36 +57,5 @@ public void IfThePreconditionPasses_WhenThePropertyIsSampled_AllTheValuesPassThe result.Should().OnlyContain(x => pred(x)); } - - private GalaxyCheck.Property NestedPrecondition(int x) - { - Func pred = x => x % 2 == 0; - - GalaxyCheck.Property.Precondition(pred(x)); - - return GalaxyCheck.Gen.Int32().ForAll(_ => pred(x).Should().BeTrue()); - } - - [Fact] - public void ForNestedProperties_IfThePreconditionPasses_ThenTheEquivalentAssertPasses() - { - var property = GalaxyCheck.Property.Reflect(GetMethod(nameof(NestedPrecondition)), this); - - var result = property.Check(seed: 0); - - result.Counterexample.Should().BeNull(); - } - - private static MethodInfo GetMethod(string name) - { - var methodInfo = typeof(AboutPreconditions).GetMethod(name, BindingFlags.NonPublic | BindingFlags.Instance); - - if (methodInfo == null) - { - throw new Exception("Unable to locate method"); - } - - return methodInfo; - } } } diff --git a/src/GalaxyCheck.Tests/PropertyTests/ReflectedPropertyTests/AboutMethodInfoInput.cs b/src/GalaxyCheck.Tests/PropertyTests/ReflectedPropertyTests/AboutMethodInfoInput.cs index 3e637c44..6388b8f7 100644 --- a/src/GalaxyCheck.Tests/PropertyTests/ReflectedPropertyTests/AboutMethodInfoInput.cs +++ b/src/GalaxyCheck.Tests/PropertyTests/ReflectedPropertyTests/AboutMethodInfoInput.cs @@ -6,23 +6,18 @@ namespace Tests.V2.PropertyTests.ReflectedPropertyTests { - /// - /// TODO: - /// - Use different gen types as parameters when supported as parameters. Otherwise we are vulnerable to parameter - /// ordering bugs. - /// public class AboutMethodInfoInput { [Fact] public void AVoidMethodInfoReceivesInputLikeForAll() { var gen0 = Gen.Int32(); - var gen1 = Gen.Int32(); + var gen1 = Gen.String(); var forAllProperty = Property.ForAll(gen0, gen1, (x, y) => { }); var forAllPropertyInput = forAllProperty.Select(x => new object[] { x.Input.Item1, x.Input.Item2 }); - Action f = (x, y) => { }; + Action f = (x, y) => { }; var methodInfoProperty = Property.Reflect(f.Method, null); var methodInfoPropertyInput = methodInfoProperty.Select(x => x.Input); @@ -33,48 +28,16 @@ public void AVoidMethodInfoReceivesInputLikeForAll() public void ABooleanReturningMethodInfoReceivesInputLikeForAll() { var gen0 = Gen.Int32(); - var gen1 = Gen.Int32(); + var gen1 = Gen.String(); var forAllProperty = Property.ForAll(gen0, gen1, (x, y) => true); var forAllPropertyInput = forAllProperty.Select(x => new object[] { x.Input.Item1, x.Input.Item2 }); - Func f = (x, y) => true; + Func f = (x, y) => true; var methodInfoProperty = Property.Reflect(f.Method, null); var methodInfoPropertyInput = methodInfoProperty.Select(x => x.Input); GenAssert.Equal(forAllPropertyInput, methodInfoPropertyInput, 0, 10); } - - private Property ARecursiveProperty(int x) - { - return Gen.Int32().ForAll(y => true); - } - - [Fact] - public void APropertyReturningMethodInfoReceivesAConcatenationOfInput() - { - var gen0 = Gen.Int32(); - var gen1 = Gen.Int32(); - - var forAllProperty = Property.ForAll(gen0, gen1, (x, y) => true); - var forAllPropertyInput = forAllProperty.Select(x => new object[] { x.Input.Item1, x.Input.Item2 }); - - var methodInfoProperty = Property.Reflect(GetMethod(nameof(ARecursiveProperty)), this); - var methodInfoPropertyInput = methodInfoProperty.Select(x => x.PresentedInput); - - GenAssert.Equal(forAllPropertyInput, methodInfoPropertyInput, 0, 1); - } - - private static MethodInfo GetMethod(string name) - { - var methodInfo = typeof(AboutMethodInfoInput).GetMethod(name, BindingFlags.NonPublic | BindingFlags.Instance); - - if (methodInfo == null) - { - throw new Exception("Unable to locate method"); - } - - return methodInfo; - } } } diff --git a/src/GalaxyCheck.Tests/PropertyTests/ReflectedPropertyTests/AboutMethodInfoOutput.cs b/src/GalaxyCheck.Tests/PropertyTests/ReflectedPropertyTests/AboutMethodInfoOutput.cs index 0d8926f5..13b1aa93 100644 --- a/src/GalaxyCheck.Tests/PropertyTests/ReflectedPropertyTests/AboutMethodInfoOutput.cs +++ b/src/GalaxyCheck.Tests/PropertyTests/ReflectedPropertyTests/AboutMethodInfoOutput.cs @@ -60,13 +60,13 @@ public void ABooleanMethodInfoCanBeFalsified() checkResult.Falsified.Should().BeTrue(); } - private Property AnInfallibleNestedPropertyFunction(int x) => Gen.Constant(null).ForAll(_ => true); - private Property AnInfallibleNestedPropertyFunctionWithVaryingTypes(int x) => Gen.Constant(true).ForAll(_ => true); + private Property AnInfalliblePropertyFunction() => Gen.Constant(null).ForAll(_ => true); + private Property AnInfalliblePropertyFunctionWithVaryingTypes() => Gen.Constant(true).ForAll(_ => true); [Theory] - [InlineData(nameof(AnInfallibleNestedPropertyFunction))] - [InlineData(nameof(AnInfallibleNestedPropertyFunctionWithVaryingTypes))] - public void ANestedPropertyMethodInfoCanBeUnfalsifiable(string methodName) + [InlineData(nameof(AnInfalliblePropertyFunction))] + [InlineData(nameof(AnInfalliblePropertyFunctionWithVaryingTypes))] + public void APropertyMethodInfoCanBeUnfalsifiable(string methodName) { var property = Property.Reflect(GetMethod(methodName), this); @@ -75,12 +75,12 @@ public void ANestedPropertyMethodInfoCanBeUnfalsifiable(string methodName) checkResult.Falsified.Should().BeFalse(); } - private Property AFallibleNestedPropertyFunction(int x) => Gen.Int32().ForAll(x => x < 100); + private Property AFalliblePropertyFunction() => Gen.Int32().ForAll(x => x < 100); [Fact] - public void ANestedPropertyMethodInfoCanBeFalsified() + public void APropertyMethodInfoCanBeFalsified() { - var property = Property.Reflect(GetMethod(nameof(AFallibleNestedPropertyFunction)), this); + var property = Property.Reflect(GetMethod(nameof(AFalliblePropertyFunction)), this); var checkResult = property.Check(); diff --git a/src/GalaxyCheck.Tests/RunnerTests/PrintTests/Snapshots.cs b/src/GalaxyCheck.Tests/RunnerTests/PrintTests/Snapshots.cs index c582fe0e..2389fdb2 100644 --- a/src/GalaxyCheck.Tests/RunnerTests/PrintTests/Snapshots.cs +++ b/src/GalaxyCheck.Tests/RunnerTests/PrintTests/Snapshots.cs @@ -9,108 +9,114 @@ namespace Tests.V2.RunnerTests.PrintTests { public class Snapshots { - [Fact] - public void PrintInt32Gen() + public class PrintGen { - var gen = Gen.Int32(); + [Fact] + public void PrintInt32Gen() + { + var gen = Gen.Int32(); - var print = PrintAndCollect(gen.Cast()); + var print = PrintAndCollect(gen.Cast()); - Snapshot.Match(print); - } + Snapshot.Match(print); + } - [Fact] - public void PrintInt32GenFiltered() - { - var gen = Gen.Int32().Where(x => x % 2 == 0); + [Fact] + public void PrintInt32GenFiltered() + { + var gen = Gen.Int32().Where(x => x % 2 == 0); - var print = PrintAndCollect(gen.Cast()); + var print = PrintAndCollect(gen.Cast()); - Snapshot.Match(print); - } + Snapshot.Match(print); + } - [Fact] - public void PrintListOfInt32Gen() - { - var gen = Gen.Int32().ListOf(); + [Fact] + public void PrintListOfInt32Gen() + { + var gen = Gen.Int32().ListOf(); - var print = PrintAndCollect(gen); + var print = PrintAndCollect(gen); - Snapshot.Match(print); - } + Snapshot.Match(print); + } - [Fact] - public void PrintNullaryProperty() - { - var property = Property.Nullary(() => false); + private static string PrintAndCollect(IGen gen) + { + var log = new List(); - var print = PrintAndCollect(property); + gen.Advanced.Print(seed: 0, stdout: log.Add); - Snapshot.Match(print); + return string.Join(Environment.NewLine, log); + } } - [Fact] - public void PrintUnaryProperty() + public class PrintProperty { - var property = Gen.Int32().ForAll((_) => false); + [Fact] + public void PrintNullaryProperty() + { + var property = Property.Nullary(() => false); - var print = PrintAndCollect(property); + var print = PrintAndCollect(property); - Snapshot.Match(print); - } + Snapshot.Match(print); + } - [Fact] - public void PrintBinaryProperty() - { - var property = - from x in Gen.Int32().LessThan(int.MaxValue) - from y in Gen.Int32().GreaterThan(x) - select Property.ForThese(() => false); + [Fact] + public void PrintUnaryProperty() + { + var property = Gen.Int32().ForAll((_) => false); - var print = PrintAndCollect(property); + var print = PrintAndCollect(property); - Snapshot.Match(print); - } + Snapshot.Match(print); + } - [Fact] - public void PrintUnaryListProperty() - { - var property = Gen.Int32().ListOf().ForAll((_) => false); + [Fact] + public void PrintBinaryProperty() + { + var property = new Property( + from x in Gen.Int32().LessThan(int.MaxValue) + from y in Gen.Int32().GreaterThan(x) + select Property.ForThese(() => false)); - var print = PrintAndCollect(property); + var print = PrintAndCollect(property); - Snapshot.Match(print); - } + Snapshot.Match(print); + } - [Fact] - public void PrintBinaryListProperty() - { - var property = - from xs in Gen.Int32().ListOf() - from ys in Gen.Int32().ListOf().WithCountGreaterThan(xs.Count) - select Property.ForThese(() => false); + [Fact] + public void PrintUnaryListProperty() + { + var property = Gen.Int32().ListOf().ForAll((_) => false); - var print = PrintAndCollect(property); + var print = PrintAndCollect(property); - Snapshot.Match(print); - } + Snapshot.Match(print); + } - private static string PrintAndCollect(IGen gen) - { - var log = new List(); + [Fact] + public void PrintBinaryListProperty() + { + var property = new Property( + from xs in Gen.Int32().ListOf() + from ys in Gen.Int32().ListOf().WithCountGreaterThan(xs.Count) + select Property.ForThese(() => false)); - gen.Advanced.Print(seed: 0, stdout: log.Add); + var print = PrintAndCollect(property); - return string.Join(Environment.NewLine, log); - } + Snapshot.Match(print); + } - private static string PrintAndCollect(IGen> gen) - { - var log = new List(); + private static string PrintAndCollect(Property property) + { + var log = new List(); - gen.Advanced.Print(seed: 0, stdout: log.Add); + property.Print(seed: 0, stdout: log.Add); - return string.Join(Environment.NewLine, log); - } + return string.Join(Environment.NewLine, log); + } + } } } diff --git a/src/GalaxyCheck.Tests/RunnerTests/PrintTests/__snapshots__/Snapshots.PrintInt32Gen.snap b/src/GalaxyCheck.Tests/RunnerTests/PrintTests/__snapshots__/PrintGen.PrintInt32Gen.snap similarity index 100% rename from src/GalaxyCheck.Tests/RunnerTests/PrintTests/__snapshots__/Snapshots.PrintInt32Gen.snap rename to src/GalaxyCheck.Tests/RunnerTests/PrintTests/__snapshots__/PrintGen.PrintInt32Gen.snap diff --git a/src/GalaxyCheck.Tests/RunnerTests/PrintTests/__snapshots__/Snapshots.PrintInt32GenFiltered.snap b/src/GalaxyCheck.Tests/RunnerTests/PrintTests/__snapshots__/PrintGen.PrintInt32GenFiltered.snap similarity index 100% rename from src/GalaxyCheck.Tests/RunnerTests/PrintTests/__snapshots__/Snapshots.PrintInt32GenFiltered.snap rename to src/GalaxyCheck.Tests/RunnerTests/PrintTests/__snapshots__/PrintGen.PrintInt32GenFiltered.snap diff --git a/src/GalaxyCheck.Tests/RunnerTests/PrintTests/__snapshots__/Snapshots.PrintListOfInt32Gen.snap b/src/GalaxyCheck.Tests/RunnerTests/PrintTests/__snapshots__/PrintGen.PrintListOfInt32Gen.snap similarity index 100% rename from src/GalaxyCheck.Tests/RunnerTests/PrintTests/__snapshots__/Snapshots.PrintListOfInt32Gen.snap rename to src/GalaxyCheck.Tests/RunnerTests/PrintTests/__snapshots__/PrintGen.PrintListOfInt32Gen.snap diff --git a/src/GalaxyCheck.Tests/RunnerTests/PrintTests/__snapshots__/Snapshots.PrintBinaryListProperty.snap b/src/GalaxyCheck.Tests/RunnerTests/PrintTests/__snapshots__/PrintProperty.PrintBinaryListProperty.snap similarity index 100% rename from src/GalaxyCheck.Tests/RunnerTests/PrintTests/__snapshots__/Snapshots.PrintBinaryListProperty.snap rename to src/GalaxyCheck.Tests/RunnerTests/PrintTests/__snapshots__/PrintProperty.PrintBinaryListProperty.snap diff --git a/src/GalaxyCheck.Tests/RunnerTests/PrintTests/__snapshots__/Snapshots.PrintBinaryProperty.snap b/src/GalaxyCheck.Tests/RunnerTests/PrintTests/__snapshots__/PrintProperty.PrintBinaryProperty.snap similarity index 100% rename from src/GalaxyCheck.Tests/RunnerTests/PrintTests/__snapshots__/Snapshots.PrintBinaryProperty.snap rename to src/GalaxyCheck.Tests/RunnerTests/PrintTests/__snapshots__/PrintProperty.PrintBinaryProperty.snap diff --git a/src/GalaxyCheck.Tests/RunnerTests/PrintTests/__snapshots__/Snapshots.PrintNullaryProperty.snap b/src/GalaxyCheck.Tests/RunnerTests/PrintTests/__snapshots__/PrintProperty.PrintNullaryProperty.snap similarity index 100% rename from src/GalaxyCheck.Tests/RunnerTests/PrintTests/__snapshots__/Snapshots.PrintNullaryProperty.snap rename to src/GalaxyCheck.Tests/RunnerTests/PrintTests/__snapshots__/PrintProperty.PrintNullaryProperty.snap diff --git a/src/GalaxyCheck.Tests/RunnerTests/PrintTests/__snapshots__/Snapshots.PrintUnaryListProperty.snap b/src/GalaxyCheck.Tests/RunnerTests/PrintTests/__snapshots__/PrintProperty.PrintUnaryListProperty.snap similarity index 100% rename from src/GalaxyCheck.Tests/RunnerTests/PrintTests/__snapshots__/Snapshots.PrintUnaryListProperty.snap rename to src/GalaxyCheck.Tests/RunnerTests/PrintTests/__snapshots__/PrintProperty.PrintUnaryListProperty.snap diff --git a/src/GalaxyCheck.Tests/RunnerTests/PrintTests/__snapshots__/Snapshots.PrintUnaryProperty.snap b/src/GalaxyCheck.Tests/RunnerTests/PrintTests/__snapshots__/PrintProperty.PrintUnaryProperty.snap similarity index 100% rename from src/GalaxyCheck.Tests/RunnerTests/PrintTests/__snapshots__/Snapshots.PrintUnaryProperty.snap rename to src/GalaxyCheck.Tests/RunnerTests/PrintTests/__snapshots__/PrintProperty.PrintUnaryProperty.snap diff --git a/src/GalaxyCheck.Xunit.Tests/IntegrationSample/Tests.cs b/src/GalaxyCheck.Xunit.Tests/IntegrationSample/Tests.cs index bebb5533..bb46d862 100644 --- a/src/GalaxyCheck.Xunit.Tests/IntegrationSample/Tests.cs +++ b/src/GalaxyCheck.Xunit.Tests/IntegrationSample/Tests.cs @@ -61,14 +61,14 @@ public void FallibleBooleanProperty([Between(0, 100)] int x) } [Property] - public Property InfallibleNestedProperty([Between(0, 100)] int x) + public Property InfallibleNestedProperty() { AnnounceTestInvocation(nameof(InfallibleNestedProperty)); return Gen.Int32().Between(0, 100).ForAll(y => { }); } [Property] - public Property FallibleNestedProperty([Between(0, 100)] int x) + public Property FallibleNestedProperty() { AnnounceTestInvocation(nameof(FallibleNestedProperty)); return Gen.Int32().Between(0, 100).ForAll(y => { throw new Exception("Failed!"); }); @@ -97,12 +97,17 @@ public void PropertyWithGenFromMemberGen([MemberGen(nameof(EvenInt32))] int x) } [Sample] - public IGen Sample() => + public IGen SamplePureProperty() => from a in Gen.Int32().Between(0, 100) select Property.ForThese(() => { }); + [Sample] + public void SampleVoidProperty([Between(0, 100)] int x) + { + } + private void AnnounceTestInvocation(string testName, params object[] injectedValues) { _testOutputHelper.WriteLine(JsonConvert.SerializeObject(new diff --git a/src/GalaxyCheck.Xunit.Tests/IntegrationTests.cs b/src/GalaxyCheck.Xunit.Tests/IntegrationTests.cs index 85ae5a15..8d28c40d 100644 --- a/src/GalaxyCheck.Xunit.Tests/IntegrationTests.cs +++ b/src/GalaxyCheck.Xunit.Tests/IntegrationTests.cs @@ -105,9 +105,18 @@ public void PropertyWithGenFromMemberGen() } [Fact] - public void Sample() + public void SamplePureProperty() { - var testResult = _fixture.FindTestResult(nameof(Sample)); + var testResult = _fixture.FindTestResult(nameof(SamplePureProperty)); + + testResult.Outcome.Should().Be("Failed"); + testResult.Message.Should().StartWith("GalaxyCheck.SampleException : Test case failed to prevent false-positives."); + } + + [Fact] + public void SampleVoidProperty() + { + var testResult = _fixture.FindTestResult(nameof(SampleVoidProperty)); testResult.Outcome.Should().Be("Failed"); testResult.Message.Should().StartWith("GalaxyCheck.SampleException : Test case failed to prevent false-positives."); @@ -115,7 +124,7 @@ public void Sample() public record Invocation(object[] InjectedParameters); - public record TestResult(string TestName, string Outcome, string? Message, ImmutableList Invocations); + public record TestResult(string TestName, string Outcome, string Message, ImmutableList Invocations); public class TestSuiteFixture : IAsyncLifetime { @@ -168,7 +177,7 @@ where desc.Name.LocalName.Contains("Message") return new TestResult( attributes.Single(a => a.Name.LocalName.Contains("testName")).Value.Split('.').Last(), attributes.Single(a => a.Name.LocalName.Contains("outcome")).Value, - message, + message!, new List().ToImmutableList()); } } diff --git a/src/GalaxyCheck.Xunit/Internal/DefaultPropertyFactory.cs b/src/GalaxyCheck.Xunit/Internal/DefaultPropertyFactory.cs index f15c02ce..8e985c02 100644 --- a/src/GalaxyCheck.Xunit/Internal/DefaultPropertyFactory.cs +++ b/src/GalaxyCheck.Xunit/Internal/DefaultPropertyFactory.cs @@ -6,13 +6,10 @@ namespace GalaxyCheck.Xunit.Internal { internal class DefaultPropertyFactory : IPropertyFactory { - public IGen> CreateProperty( + public Property CreateProperty( MethodInfo methodInfo, object? target, IGenFactory? genFactory, - IReadOnlyDictionary customGens) - { - return Property.Reflect(methodInfo, target, genFactory, customGens); - } + IReadOnlyDictionary customGens) => Property.Reflect(methodInfo, target, genFactory, customGens); } } diff --git a/src/GalaxyCheck.Xunit/Internal/IPropertyFactory.cs b/src/GalaxyCheck.Xunit/Internal/IPropertyFactory.cs index a1e44b46..d2fa14b7 100644 --- a/src/GalaxyCheck.Xunit/Internal/IPropertyFactory.cs +++ b/src/GalaxyCheck.Xunit/Internal/IPropertyFactory.cs @@ -6,7 +6,7 @@ namespace GalaxyCheck.Xunit.Internal { public interface IPropertyFactory { - IGen> CreateProperty( + Property CreateProperty( MethodInfo methodInfo, object? target, IGenFactory? genFactory, diff --git a/src/GalaxyCheck.Xunit/Internal/IPropertyRunner.cs b/src/GalaxyCheck.Xunit/Internal/IPropertyRunner.cs index 220c793b..ace8cc1d 100644 --- a/src/GalaxyCheck.Xunit/Internal/IPropertyRunner.cs +++ b/src/GalaxyCheck.Xunit/Internal/IPropertyRunner.cs @@ -4,7 +4,7 @@ namespace GalaxyCheck.Xunit.Internal { internal record PropertyRunParameters( - IGen> Property, + Property Property, int Iterations, int ShrinkLimit, string? Replay, diff --git a/src/GalaxyCheck.Xunit/Internal/PropertyAssertRunner.cs b/src/GalaxyCheck.Xunit/Internal/PropertyAssertRunner.cs index f3d88bb4..d116ae0a 100644 --- a/src/GalaxyCheck.Xunit/Internal/PropertyAssertRunner.cs +++ b/src/GalaxyCheck.Xunit/Internal/PropertyAssertRunner.cs @@ -7,7 +7,7 @@ internal class PropertyAssertRunner : IPropertyRunner { public void Run(PropertyRunParameters parameters, ITestOutputHelper testOutputHelper) { - parameters.Property.Select(test => test.Cast()).Assert( + parameters.Property.Assert( replay: parameters.Replay, seed: parameters.Seed, size: parameters.Size, diff --git a/src/GalaxyCheck.Xunit/Internal/PropertySampleRunner.cs b/src/GalaxyCheck.Xunit/Internal/PropertySampleRunner.cs index a068273b..2089c8e3 100644 --- a/src/GalaxyCheck.Xunit/Internal/PropertySampleRunner.cs +++ b/src/GalaxyCheck.Xunit/Internal/PropertySampleRunner.cs @@ -10,7 +10,7 @@ public void Run(PropertyRunParameters parameters, ITestOutputHelper testOutputHe { var log = new List(); - parameters.Property.Advanced.Print( + parameters.Property.Print( stdout: log.Add, seed: parameters.Seed, size: parameters.Size, diff --git a/src/GalaxyCheck/Properties/LinqProperties.cs b/src/GalaxyCheck/Properties/LinqProperties.cs index 4cd8a5f6..01f16c1f 100644 --- a/src/GalaxyCheck/Properties/LinqProperties.cs +++ b/src/GalaxyCheck/Properties/LinqProperties.cs @@ -5,11 +5,11 @@ namespace GalaxyCheck { public partial class Property { - public static Test ForThese(Func func) => TestFactory.Create( + public static Test ForThese(Func func) => TestFactory.Create( null!, () => func(), null); - public static Test ForThese(Action func) => ForThese(func.AsTrueFunc()); + public static Test ForThese(Action func) => ForThese(func.AsTrueFunc()); } } diff --git a/src/GalaxyCheck/Properties/Reflect.cs b/src/GalaxyCheck/Properties/Reflect.cs index b6168c90..047a29b2 100644 --- a/src/GalaxyCheck/Properties/Reflect.cs +++ b/src/GalaxyCheck/Properties/Reflect.cs @@ -10,7 +10,7 @@ namespace GalaxyCheck { public partial class Property { - private delegate IGen> ReflectedPropertyHandler( + private delegate Property ReflectedPropertyHandler( MethodInfo methodInfo, object? target, IGenFactory? genFactory, @@ -18,7 +18,7 @@ private delegate IGen> ReflectedPropertyHandler( public static ImmutableList SupportedReturnTypes => MethodPropertyHandlers.Keys.ToImmutableList(); - public static IGen> Reflect( + public static Property Reflect( MethodInfo methodInfo, object? target, IGenFactory? genFactory = null, @@ -27,7 +27,7 @@ public static IGen> Reflect( if (!SupportedReturnTypes.Contains(methodInfo.ReturnType)) { var supportedReturnTypesFormatted = string.Join(", ", SupportedReturnTypes); - var message = $"Return type {methodInfo.ReturnType} is not supported by GalaxyCheck.Xunit. Please use one of: {supportedReturnTypesFormatted}"; + var message = $"Return type {methodInfo.ReturnType} is not supported by GalaxyCheck. Please use one of: {supportedReturnTypesFormatted}"; throw new Exception(message); } @@ -39,13 +39,13 @@ public static IGen> Reflect( { { typeof(void), ToVoidProperty }, { typeof(bool), ToBooleanProperty }, - { typeof(Property), ToNestedProperty }, - { typeof(IGen), ToPureProperty } + { typeof(Property), ToReturnedProperty }, + { typeof(IGen), ToPureProperty }, }.ToImmutableDictionary(); private static ReflectedPropertyHandler ToVoidProperty => (methodInfo, target, genFactory, customGens) => { - return Gen + return new Property(Gen .Parameters(methodInfo, genFactory, customGens) .ForAll(parameters => { @@ -61,12 +61,12 @@ public static IGen> Reflect( .Select(test => TestFactory.Create( test.Input, test.Output, - test.Input)); + test.Input))); }; private static ReflectedPropertyHandler ToBooleanProperty => (methodInfo, target, genFactory, customGens) => { - return Gen + return new Property(Gen .Parameters(methodInfo, genFactory, customGens) .ForAll(parameters => { @@ -82,24 +82,19 @@ public static IGen> Reflect( .Select(test => TestFactory.Create( test.Input, test.Output, - test.Input)); + test.Input))); }; - private static ReflectedPropertyHandler ToNestedProperty => (methodInfo, target, genFactory, customGens) => - from parameters in Gen.Parameters(methodInfo, genFactory, customGens) - let property = InvokeNestedProperty(methodInfo, target, parameters) - where property != null - from test in property - select TestFactory.Create( - test.Input, - test.Output, - Enumerable.Concat(parameters, test.PresentedInput!).ToArray()); - - private static ReflectedPropertyHandler ToPureProperty => (methodInfo, target, _, _) => + private static ReflectedPropertyHandler ToReturnedProperty => (methodInfo, target, genFactory, customGens) => { + if (methodInfo.GetParameters().Any()) + { + throw new Exception($"Parameters are not support for methods returning properties. Violating signature: \"{methodInfo}\""); + } + try { - return ((IGen)methodInfo.Invoke(target, new object[] { })!).Select(test => test.Cast()); + return (Property)methodInfo.Invoke(target, new object[] {})!; } catch (TargetInvocationException ex) { @@ -107,21 +102,16 @@ select TestFactory.Create( } }; - private static Property? InvokeNestedProperty(MethodInfo methodInfo, object? target, object[] parameters) + private static ReflectedPropertyHandler ToPureProperty => (methodInfo, target, _, _) => { try { - return (Property)methodInfo.Invoke(target, parameters)!; + return new Property((IGen)methodInfo.Invoke(target, new object[] { })!); } catch (TargetInvocationException ex) { - if (ex.InnerException!.GetType() == typeof(PropertyPreconditionException)) - { - return null; - } - - throw ex.InnerException; + throw ex.InnerException!; } - } + }; } } diff --git a/src/GalaxyCheck/Properties/Test.cs b/src/GalaxyCheck/Properties/Test.cs index 10ed39fc..bfc2fc06 100644 --- a/src/GalaxyCheck/Properties/Test.cs +++ b/src/GalaxyCheck/Properties/Test.cs @@ -1,40 +1,24 @@ using GalaxyCheck.Properties; using System; +using System.Collections.Generic; namespace GalaxyCheck { #pragma warning disable IDE1006 // Naming Styles - public interface Test + public interface Test : Test #pragma warning restore IDE1006 // Naming Styles { - object? Input { get; } - - Lazy Output { get; } - - object?[]? PresentedInput { get; } - -#pragma warning disable IDE1006 // Naming Styles - public interface TestOutput -#pragma warning restore IDE1006 // Naming Styles - { - TestResult Result { get; } - - Exception? Exception { get; } - } - - public enum TestResult - { - Succeeded = 1, - Failed = 2, - FailedPrecondition = 3 - } } #pragma warning disable IDE1006 // Naming Styles - public interface Test : Test + public interface Test #pragma warning restore IDE1006 // Naming Styles { - new T Input { get; } + T Input { get; } + + Lazy Output { get; } + + IReadOnlyList? PresentedInput { get; } } public static partial class Extensions diff --git a/src/GalaxyCheck/Properties/TestFactory.cs b/src/GalaxyCheck/Properties/TestFactory.cs index 8f2c3b43..ac4ab766 100644 --- a/src/GalaxyCheck/Properties/TestFactory.cs +++ b/src/GalaxyCheck/Properties/TestFactory.cs @@ -1,51 +1,49 @@ using System; +using System.Collections.Generic; namespace GalaxyCheck.Properties { internal static class TestFactory { - private record TestOutputImpl(Test.TestResult Result, Exception? Exception) : Test.TestOutput; + private record TestOutputImpl(TestResult Result, Exception? Exception) : TestOutput; - private record TestImpl(T Input, Lazy Output, object?[]? PresentedInput) : Test - { - object? Test.Input => Input; - } + private record TestImpl(T Input, Lazy Output, IReadOnlyList? PresentedInput) : Test; - private record TestImpl(object? Input, Lazy Output, object?[]? PresentedInput) : Test; + private record TestImpl(object? Input, Lazy Output, IReadOnlyList? PresentedInput) : Test; - public static Test Create(T input, Lazy output, object?[]? presentedInput) + public static Test Create(T input, Lazy output, IReadOnlyList? presentedInput) { return new TestImpl(input, output, presentedInput); } - public static Test Create(object[] input, Lazy output, object?[]? presentedInput) + public static Test Create(object[] input, Lazy output, IReadOnlyList? presentedInput) { return new TestImpl(input, output, presentedInput); } - public static Test Create(T input, Func generateOutput, object?[]? presentedInput) + public static Test Create(T input, Func generateOutput, IReadOnlyList? presentedInput) { return new TestImpl(input, AnalyzeBooleanOutput(generateOutput), presentedInput); } - public static Test Create(object[] input, Func generateOutput, object?[]? presentedInput) + public static Test Create(object[] input, Func generateOutput, IReadOnlyList? presentedInput) { return new TestImpl(input, AnalyzeBooleanOutput(generateOutput), presentedInput); } - private static Lazy AnalyzeBooleanOutput(Func generateOutput) => new Lazy(() => + private static Lazy AnalyzeBooleanOutput(Func generateOutput) => new Lazy(() => { try { - return new TestOutputImpl(generateOutput() ? Test.TestResult.Succeeded : Test.TestResult.Failed, null); + return new TestOutputImpl(generateOutput() ? TestResult.Succeeded : TestResult.Failed, null); } catch (Property.PropertyPreconditionException) { - return new TestOutputImpl(Test.TestResult.FailedPrecondition, null); + return new TestOutputImpl(TestResult.FailedPrecondition, null); } catch (Exception ex) { - return new TestOutputImpl(Test.TestResult.Failed, ex); + return new TestOutputImpl(TestResult.Failed, ex); } }); } diff --git a/src/GalaxyCheck/Properties/TestOutput.cs b/src/GalaxyCheck/Properties/TestOutput.cs new file mode 100644 index 00000000..c7d911a7 --- /dev/null +++ b/src/GalaxyCheck/Properties/TestOutput.cs @@ -0,0 +1,13 @@ +using System; + +namespace GalaxyCheck +{ +#pragma warning disable IDE1006 // Naming Styles + public interface TestOutput +#pragma warning restore IDE1006 // Naming Styles + { + TestResult Result { get; } + + Exception? Exception { get; } + } +} diff --git a/src/GalaxyCheck/Properties/TestResult.cs b/src/GalaxyCheck/Properties/TestResult.cs new file mode 100644 index 00000000..ec432a67 --- /dev/null +++ b/src/GalaxyCheck/Properties/TestResult.cs @@ -0,0 +1,9 @@ +namespace GalaxyCheck +{ + public enum TestResult + { + Succeeded = 1, + Failed = 2, + FailedPrecondition = 3 + } +} diff --git a/src/GalaxyCheck/Property.cs b/src/GalaxyCheck/Property.cs index e4cfcf9b..1061f4da 100644 --- a/src/GalaxyCheck/Property.cs +++ b/src/GalaxyCheck/Property.cs @@ -1,20 +1,16 @@ using GalaxyCheck.Properties; -using System; namespace GalaxyCheck { - public partial class Property : IGen + public partial class Property : Property { - private readonly IGen _gen; - - public Property(IGen gen) + public Property(IGen> gen) : base(gen) { - _gen = gen; } - public IGenAdvanced Advanced => _gen.Advanced; - - IGenAdvanced IGen.Advanced => Advanced; + public Property(IGen gen) : this(gen.Select(t => t.Cast())) + { + } } public class Property : IGen> @@ -34,4 +30,4 @@ public Property(IGen> gen) from test in property select TestFactory.Create(new object[] { test.Input }, test.Output, test.PresentedInput)); } -} \ No newline at end of file +} diff --git a/src/GalaxyCheck/Runners/Check.cs b/src/GalaxyCheck/Runners/Check.cs index 232ccbbc..d594bf34 100644 --- a/src/GalaxyCheck/Runners/Check.cs +++ b/src/GalaxyCheck/Runners/Check.cs @@ -206,7 +206,7 @@ public CheckResult( public record CheckIteration( T Value, - object?[] PresentationalValue, + IReadOnlyList PresentationalValue, IExampleSpace ExampleSpace, GenParameters Parameters, IEnumerable Path, @@ -221,7 +221,7 @@ public record Counterexample( IEnumerable ReplayPath, string Replay, Exception? Exception, - object?[] PresentationalValue); + IReadOnlyList PresentationalValue); public enum TerminationReason { diff --git a/src/GalaxyCheck/Runners/Check/Automata/AnalyzeExplorationForCheck.cs b/src/GalaxyCheck/Runners/Check/Automata/AnalyzeExplorationForCheck.cs index d345d431..24fca491 100644 --- a/src/GalaxyCheck/Runners/Check/Automata/AnalyzeExplorationForCheck.cs +++ b/src/GalaxyCheck/Runners/Check/Automata/AnalyzeExplorationForCheck.cs @@ -9,9 +9,9 @@ public static AnalyzeExploration> Impl() => (example) => var testOutput = example.Value.Output.Value; return testOutput.Result switch { - Test.TestResult.Succeeded => ExplorationOutcome.Success(), - Test.TestResult.FailedPrecondition => ExplorationOutcome.Discard(), - Test.TestResult.Failed => ExplorationOutcome.Fail(testOutput.Exception), + TestResult.Succeeded => ExplorationOutcome.Success(), + TestResult.FailedPrecondition => ExplorationOutcome.Discard(), + TestResult.Failed => ExplorationOutcome.Fail(testOutput.Exception), _ => throw new Exception("Fatal: Unhandled case") }; }; diff --git a/src/GalaxyCheck/Runners/Check/Automata/CheckStateContext.cs b/src/GalaxyCheck/Runners/Check/Automata/CheckStateContext.cs index 89ad85f1..c15b2f36 100644 --- a/src/GalaxyCheck/Runners/Check/Automata/CheckStateContext.cs +++ b/src/GalaxyCheck/Runners/Check/Automata/CheckStateContext.cs @@ -134,12 +134,12 @@ internal record CounterexampleContext( public T Value => ExampleSpace.Current.Value; - public object?[] PresentationalValue + public IReadOnlyList PresentationalValue { get { var test = TestExampleSpace.Current.Value; - var arity = test.PresentedInput?.Length; + var arity = test.PresentedInput?.Count; return arity > 0 ? test.PresentedInput! : PresentationInferrer.InferValue(ExampleSpaceHistory); } } diff --git a/src/GalaxyCheck/Runners/Check/Automata/PresentationInferrer.cs b/src/GalaxyCheck/Runners/Check/Automata/PresentationInferrer.cs index 51338543..6f1c40f2 100644 --- a/src/GalaxyCheck/Runners/Check/Automata/PresentationInferrer.cs +++ b/src/GalaxyCheck/Runners/Check/Automata/PresentationInferrer.cs @@ -9,7 +9,7 @@ namespace GalaxyCheck.Runners.Check.Automata { internal static class PresentationInferrer { - public static object?[] InferValue(IEnumerable?>> exampleSpaceHistory) + public static IReadOnlyList InferValue(IEnumerable?>> exampleSpaceHistory) { return InferExampleSpace(exampleSpaceHistory)?.Current.Value ?? new object?[] { }; } @@ -30,12 +30,17 @@ from lazyExs in tail let exs = lazyExs.Value where exs != null where exs.Current.Id.HashCode == lastHashCode - where exs.Current.Value == null || exs.Current.Value is not Test + where exs.Current.Value == null || IsTest(exs.Current.Value) == false select exs; return presentationalExampleSpaces.FirstOrDefault()?.Map(UnwrapBinding); } + private static bool IsTest(object obj) + { + return obj.GetType().GetInterfaces().Any(t => t.IsGenericType && t.GetGenericTypeDefinition() == typeof(Test<>)); + } + private static object?[] UnwrapBinding(object? obj) { static object?[] UnwrapBindingRec(object? obj) diff --git a/src/GalaxyCheck/Runners/ExampleRenderer.cs b/src/GalaxyCheck/Runners/ExampleRenderer.cs index 0c734bb6..49719923 100644 --- a/src/GalaxyCheck/Runners/ExampleRenderer.cs +++ b/src/GalaxyCheck/Runners/ExampleRenderer.cs @@ -8,7 +8,7 @@ namespace GalaxyCheck.Runners { public static class ExampleRenderer { - public static IEnumerable Render(object?[] example) => example.Length switch + public static IEnumerable Render(IReadOnlyList example) => example.Count switch { 0 => new string[] { "(no value)" }, 1 => new string[] { RenderValue(example.Single()) }, diff --git a/src/GalaxyCheck/Runners/PresentationalSample.cs b/src/GalaxyCheck/Runners/PresentationalSample.cs index 648fa736..e76c2103 100644 --- a/src/GalaxyCheck/Runners/PresentationalSample.cs +++ b/src/GalaxyCheck/Runners/PresentationalSample.cs @@ -1,5 +1,5 @@ using GalaxyCheck.Properties; -using GalaxyCheck.Runners.Check; +using System.Collections.Generic; using System.Linq; namespace GalaxyCheck @@ -8,13 +8,13 @@ namespace GalaxyCheck public static partial class Extensions { - public static SampleWithMetricsResult SamplePresentationalWithMetrics( - this IGenAdvanced> advanced, + public static SampleWithMetricsResult> SamplePresentationalWithMetrics( + this Property property, int? iterations = null, int? seed = null, - int? size = null) => PresentationalSampleHelpers.RunPresentationalValueSample(advanced, iterations: iterations, seed: seed, size: size); + int? size = null) => PresentationalSampleHelpers.RunPresentationalValueSample(property, iterations: iterations, seed: seed, size: size); - public static SampleWithMetricsResult SamplePresentationalWithMetrics( + public static SampleWithMetricsResult> SamplePresentationalWithMetrics( this IGenAdvanced advanced, int? iterations = null, int? seed = null, @@ -26,30 +26,29 @@ namespace GalaxyCheck.Runners.Sample { internal static class PresentationalSampleHelpers { - internal static SampleWithMetricsResult RunPresentationalValueSample( - IGenAdvanced> advanced, + internal static SampleWithMetricsResult> RunPresentationalValueSample( + Property property, int? iterations, int? seed, int? size) { - var property = advanced - .AsGen() + var propertyWithoutFailures = property .Where(TestMeetsPrecondition) .Select(MuteTestFailure); - var checkResult = property.Check(iterations: iterations, seed: seed, size: size); + var checkResult = propertyWithoutFailures.Check(iterations: iterations, seed: seed, size: size); var values = checkResult.Checks .Select(check => check.PresentationalValue) .ToList(); - return new SampleWithMetricsResult( + return new SampleWithMetricsResult>( values, checkResult.Discards, checkResult.RandomnessConsumption); } - internal static SampleWithMetricsResult RunPresentationalValueSample( + internal static SampleWithMetricsResult> RunPresentationalValueSample( IGenAdvanced advanced, int? iterations, int? seed, @@ -63,14 +62,14 @@ internal static class PresentationalSampleHelpers .Select(check => check.PresentationalValue) .ToList(); - return new SampleWithMetricsResult( + return new SampleWithMetricsResult>( values, checkResult.Discards, checkResult.RandomnessConsumption); } private static bool TestMeetsPrecondition(Test test) => - test.Output.Value.Result != Test.TestResult.FailedPrecondition; + test.Output.Value.Result != TestResult.FailedPrecondition; private static Test MuteTestFailure(Test test) => TestFactory.Create(test.Input, () => true, test.PresentedInput); diff --git a/src/GalaxyCheck/Runners/Print.cs b/src/GalaxyCheck/Runners/Print.cs index f232a2a1..3ceace31 100644 --- a/src/GalaxyCheck/Runners/Print.cs +++ b/src/GalaxyCheck/Runners/Print.cs @@ -1,5 +1,4 @@ using GalaxyCheck.Runners.Print; -using GalaxyCheck.Runners.Sample; using System; using System.Collections.Generic; using System.Linq; @@ -8,14 +7,14 @@ namespace GalaxyCheck { public static partial class Extensions { - public static void Print( - this IGenAdvanced> advanced, + public static void Print( + this Property property, int? iterations = null, int? seed = null, int? size = null, Action? stdout = null) { - var sample = advanced.SamplePresentationalWithMetrics( + var sample = property.SamplePresentationalWithMetrics( iterations: iterations, seed: seed, size: size); @@ -45,7 +44,7 @@ namespace GalaxyCheck.Runners.Print internal static class PrintHelpers { internal static void Print( - IReadOnlyList presentationalValues, + IReadOnlyList> presentationalValues, int discards, Action? stdout) { @@ -84,4 +83,4 @@ private static IEnumerable FormatMultiaryLines(int sampleIndex, List> RunExampleSpaceSample< private static bool TestMeetsPrecondition(Test test) { // Evaluate the test by accessing the lazily-evaluated `Output` - return test.Output.Value.Result != Test.TestResult.FailedPrecondition; + return test.Output.Value.Result != TestResult.FailedPrecondition; } private static Test MuteTestFailure(Test test)