Skip to content

Commit 3d0a166

Browse files
committed
🌍 #385 Allow properties to be generated from InlineData/MemberData
1 parent 138a78d commit 3d0a166

File tree

74 files changed

+838
-270
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

74 files changed

+838
-270
lines changed

GalaxyCheck.sln.DotSettings

+2-1
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,3 @@
11
<wpf:ResourceDictionary xml:space="preserve" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:s="clr-namespace:System;assembly=mscorlib" xmlns:ss="urn:shemas-jetbrains-com:settings-storage-xaml" xmlns:wpf="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
2-
<s:Boolean x:Key="/Default/UserDictionary/Words/=Nullary/@EntryIndexedValue">True</s:Boolean></wpf:ResourceDictionary>
2+
<s:Boolean x:Key="/Default/UserDictionary/Words/=Nullary/@EntryIndexedValue">True</s:Boolean>
3+
<s:Boolean x:Key="/Default/UserDictionary/Words/=Snapshooter/@EntryIndexedValue">True</s:Boolean></wpf:ResourceDictionary>

src/GalaxyCheck.Tests.V3/TestUtility/DummyTestFunctions.cs

+1-1
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,6 @@ public static class Size
1313
size => size.Value > value.Value;
1414

1515
public static Func<GalaxyCheck.Gens.Parameters.Size, bool> Top50thPercentile() =>
16-
IsGreaterThan(new GalaxyCheck.Gens.Parameters.Size(GalaxyCheck.Gens.Parameters.Size.Max.Value / 2));
16+
IsGreaterThan(new GalaxyCheck.Gens.Parameters.Size(Gens.Parameters.Size.Max.Value / 2));
1717
}
1818
}

src/GalaxyCheck.Xunit.Tests/GalaxyCheck.Xunit.Tests.csproj

+5
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
<PackageReference Include="FluentAssertions" Version="5.10.3" />
1919
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.2.0" />
2020
<PackageReference Include="Moq" Version="4.18.1" />
21+
<PackageReference Include="Snapshooter.Xunit" Version="0.5.8" />
2122
<PackageReference Include="xunit" Version="2.4.1" />
2223
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.5">
2324
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
@@ -33,4 +34,8 @@
3334
<ProjectReference Include="..\GalaxyCheck.Xunit\GalaxyCheck.Xunit.csproj" />
3435
</ItemGroup>
3536

37+
<ItemGroup>
38+
<Folder Include="IntegrationTests\__snapshots__" />
39+
</ItemGroup>
40+
3641
</Project>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
using System.Runtime.InteropServices;
2+
using Snapshooter;
3+
using Snapshooter.Xunit;
4+
using Xunit;
5+
6+
namespace Tests.IntegrationTests;
7+
8+
public abstract class BaseTestSuiteTests<Fixture> : IClassFixture<Fixture>
9+
where Fixture : TestSuiteFixture
10+
{
11+
private readonly Fixture _fixture;
12+
13+
protected BaseTestSuiteTests(Fixture fixture)
14+
{
15+
_fixture = fixture;
16+
}
17+
18+
[Fact]
19+
public void TestNameSnapshots()
20+
{
21+
Snapshot.Match(_fixture.TestNameToTestResult.Keys, SnapshotNameExtension.Create(_fixture.TestSuiteName));
22+
}
23+
24+
[Fact]
25+
public void TestOutputSnapshots()
26+
{
27+
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows) is false)
28+
{
29+
// TODO: Cross-platform support for this behaviour - I think this might be fixed in .NET 7
30+
return;
31+
}
32+
33+
foreach (var (testName, testOutput) in _fixture.TestNameToTestResult)
34+
{
35+
Snapshot.Match(testOutput, SnapshotNameExtension.Create(_fixture.TestSuiteName, testName));
36+
}
37+
}
38+
}

src/GalaxyCheck.Xunit.Tests/IntegrationTests/GenSnapshotTests.Snapshooter.cs

-34
This file was deleted.
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,6 @@
1-
using FluentAssertions;
2-
using Xunit;
3-
4-
namespace Tests.IntegrationTests
1+
namespace Tests.IntegrationTests
52
{
6-
public class PropertyTests : IClassFixture<PropertyTests.Fixture>
3+
public class PropertyTests : BaseTestSuiteTests<PropertyTests.Fixture>
74
{
85
public class Fixture : TestSuiteFixture
96
{
@@ -12,55 +9,8 @@ public Fixture() : base("IntegrationSample")
129
}
1310
}
1411

15-
private readonly Fixture _fixture;
16-
17-
public PropertyTests(Fixture fixture)
18-
{
19-
_fixture = fixture;
20-
}
21-
22-
[Theory]
23-
[InlineData("InfallibleVoidProperty")]
24-
[InlineData("InfallibleVoidPropertyAsync")]
25-
[InlineData("InfallibleBooleanProperty")]
26-
[InlineData("InfallibleBooleanPropertyAsync")]
27-
[InlineData("InfallibleReturnedProperty")]
28-
[InlineData("InfallibleReturnedPropertyAsync")]
29-
[InlineData("PropertyWithGenFromGenFactory")]
30-
[InlineData("PropertyWithGenFromGenFactoryAsync")]
31-
[InlineData("PropertyWithGenFromMemberGen")]
32-
[InlineData("PropertyWithGenFromMemberGenAsync")]
33-
public void PassingProperties(string testName)
34-
{
35-
var testResult = _fixture.FindTestResult(testName);
36-
37-
testResult.Outcome.Should().Be("Passed");
38-
}
39-
40-
[Theory]
41-
[InlineData("FallibleVoidProperty")]
42-
[InlineData("FallibleVoidPropertyAsync")]
43-
[InlineData("FallibleBooleanProperty")]
44-
[InlineData("FallibleBooleanPropertyAsync")]
45-
[InlineData("FallibleReturnedProperty")]
46-
[InlineData("FallibleReturnedPropertyAsync")]
47-
public void FailingProperties(string testName)
12+
public PropertyTests(Fixture fixture) : base(fixture)
4813
{
49-
var testResult = _fixture.FindTestResult(testName);
50-
51-
testResult.Outcome.Should().Be("Failed");
52-
testResult.Message.Should().StartWith("GalaxyCheck.Runners.PropertyFailedException");
53-
}
54-
55-
[Theory]
56-
[InlineData("SampleVoidProperty")]
57-
[InlineData("SampleVoidPropertyAsync")]
58-
public void SampledProperties(string testName)
59-
{
60-
var testResult = _fixture.FindTestResult(testName);
61-
62-
testResult.Outcome.Should().Be("Failed");
63-
testResult.Message.Should().StartWith("GalaxyCheck.SampleException : Test case failed to prevent false-positives.");
6414
}
6515
}
6616
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
namespace Tests.IntegrationTests
2+
{
3+
public class SnapshooterGenSnapshotTests : BaseTestSuiteTests<SnapshooterGenSnapshotTests.Fixture>
4+
{
5+
public class Fixture : TestSuiteFixture
6+
{
7+
public Fixture() : base("IntegrationSample_GenSnapshot_Snapshooter")
8+
{
9+
}
10+
}
11+
12+
public SnapshooterGenSnapshotTests(Fixture fixture) : base(fixture)
13+
{
14+
}
15+
}
16+
}

src/GalaxyCheck.Xunit.Tests/IntegrationTests/TestSuiteFixture.cs

+60-28
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
using System.Diagnostics;
55
using System.IO;
66
using System.Linq;
7+
using System.Threading;
78
using System.Threading.Tasks;
89
using System.Xml.Linq;
910
using Xunit;
@@ -12,54 +13,76 @@ namespace Tests.IntegrationTests
1213
{
1314
public record Invocation(object[] InjectedParameters);
1415

15-
public record TestResult(string TestName, string Outcome, string Message, ImmutableList<Invocation> Invocations);
16+
public record TestResult(string TestName, string Outcome, string Message, string Output);
1617

1718
public abstract class TestSuiteFixture : IAsyncLifetime
1819
{
20+
private static readonly SemaphoreSlim Lock = new(1, 1);
21+
1922
private readonly string _currentDirectory = Directory.GetCurrentDirectory();
2023
private readonly string _testReportFileName;
21-
private readonly string _testSuiteName;
2224

2325
public TestSuiteFixture(string testSuiteName)
2426
{
27+
TestSuiteName = testSuiteName;
2528
_testReportFileName = Path.Combine(_currentDirectory, $"test_result_{testSuiteName}_{DateTime.UtcNow.Ticks}.trx");
26-
_testSuiteName = testSuiteName;
2729
}
2830

31+
public string TestSuiteName { get; }
32+
2933
public ImmutableList<TestResult> TestResults { get; private set; } = default!;
3034

35+
public IReadOnlyDictionary<string, string> TestNameToTestResult { get; private set; } = default!;
36+
37+
public string TestReport { get; private set; } = default!;
38+
3139
public async Task InitializeAsync()
3240
{
33-
var currentProjectDir = Enumerable
34-
.Range(0, 3)
35-
.Aggregate(new DirectoryInfo(_currentDirectory), (acc, _) => acc.Parent!);
36-
var integrationProjectDir = Path.Combine(currentProjectDir.FullName, "samples", _testSuiteName);
41+
await Lock.WaitAsync();
3742

38-
var testProcess = Process.Start(new ProcessStartInfo
43+
try
3944
{
40-
FileName = "dotnet",
41-
Arguments = $"test {integrationProjectDir} --logger \"trx;LogFileName={_testReportFileName}\"",
42-
RedirectStandardOutput = true,
43-
RedirectStandardError = true,
44-
});
45-
46-
var standardOutput = await testProcess!.StandardOutput.ReadToEndAsync();
47-
var standardError = await testProcess!.StandardError.ReadToEndAsync();
48-
49-
var testReport = File.ReadAllText(_testReportFileName);
50-
51-
var testResults =
52-
from descendent in XElement.Parse(testReport).Descendants()
53-
where descendent.Name.LocalName == "UnitTestResult"
54-
select ElementToTestResult(descendent);
55-
56-
TestResults = testResults.ToImmutableList();
45+
var currentProjectDir = Enumerable
46+
.Range(0, 3)
47+
.Aggregate(new DirectoryInfo(_currentDirectory), (acc, _) => acc.Parent!);
48+
var integrationProjectDir = Path.Combine(currentProjectDir.FullName, "samples", TestSuiteName);
49+
50+
var testProcess = Process.Start(new ProcessStartInfo
51+
{
52+
FileName = "dotnet",
53+
Arguments = $"test {integrationProjectDir} --logger \"trx;LogFileName={_testReportFileName}\"",
54+
RedirectStandardOutput = true,
55+
RedirectStandardError = true,
56+
});
57+
58+
var standardOutput = await testProcess!.StandardOutput.ReadToEndAsync();
59+
var standardError = await testProcess!.StandardError.ReadToEndAsync();
60+
61+
TestReport = await File.ReadAllTextAsync(_testReportFileName);
62+
63+
var testResults =
64+
from descendent in XElement.Parse(TestReport).Descendants()
65+
where descendent.Name.LocalName == "UnitTestResult"
66+
select ElementToTestResult(descendent);
67+
68+
TestResults = testResults.ToImmutableList();
69+
70+
TestNameToTestResult = TestResults
71+
.OrderBy(x => x.TestName)
72+
.GroupBy(x => x.TestName.Contains('(') ? x.TestName[..x.TestName.IndexOf('(')] : x.TestName)
73+
.SelectMany(g => g.Select((x, i) => (TestName: $"{g.Key}_{i}", TestResult: x)))
74+
.ToDictionary(
75+
x => x.TestName,
76+
x => string.Join("\n", $"Outcome: {x.TestResult.Outcome}", "", "Output:", "", x.TestResult.Output));
77+
}
78+
finally
79+
{
80+
Lock.Release();
81+
}
5782
}
5883

5984
public Task DisposeAsync() => Task.CompletedTask;
6085

61-
public TestResult FindTestResult(string testName) => TestResults.Single(x => x.TestName == testName);
62-
6386
private static TestResult ElementToTestResult(XElement element)
6487
{
6588
var attributes = element.Attributes();
@@ -69,11 +92,20 @@ from desc in element.Descendants()
6992
where desc.Name.LocalName.Contains("Message")
7093
select desc.Value).SingleOrDefault();
7194

95+
var output = (
96+
from desc in element.Descendants()
97+
where desc.Name.LocalName.Contains("Output")
98+
select desc.Value).SingleOrDefault();
99+
if (output?.Contains(" at ") is true)
100+
{
101+
output = output[..output.IndexOf(" at ")]; // Scrub the stacktrace with local paths 😅
102+
}
103+
72104
return new TestResult(
73105
attributes.Single(a => a.Name.LocalName.Contains("testName")).Value.Split('.').Last(),
74106
attributes.Single(a => a.Name.LocalName.Contains("outcome")).Value,
75107
message!,
76-
new List<Invocation>().ToImmutableList());
108+
output!);
77109
}
78110
}
79111
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
[
2+
"FallibleBooleanProperty_0",
3+
"FallibleBooleanPropertyAsync_0",
4+
"FalliblePropertyWithControlData_0",
5+
"FalliblePropertyWithControlData_1",
6+
"FalliblePropertyWithControlDataAsync_0",
7+
"FalliblePropertyWithControlDataAsync_1",
8+
"FallibleReturnedProperty_0",
9+
"FallibleReturnedPropertyAsync_0",
10+
"FallibleVoidProperty_0",
11+
"FallibleVoidPropertyAsync_0",
12+
"InfallibleBooleanProperty_0",
13+
"InfallibleBooleanPropertyAsync_0",
14+
"InfalliblePropertyWithControlData_0",
15+
"InfalliblePropertyWithControlData_1",
16+
"InfalliblePropertyWithControlDataAsync_0",
17+
"InfalliblePropertyWithControlDataAsync_1",
18+
"InfallibleReturnedProperty_0",
19+
"InfallibleReturnedPropertyAsync_0",
20+
"InfallibleVoidProperty_0",
21+
"InfallibleVoidPropertyAsync_0",
22+
"PropertyWithGenFromGenFactory_0",
23+
"PropertyWithGenFromGenFactoryAsync_0",
24+
"PropertyWithGenFromMemberGen_0",
25+
"PropertyWithGenFromMemberGenAsync_0",
26+
"SampleVoidProperty_0",
27+
"SampleVoidPropertyAsync_0"
28+
]
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
[
2+
"ObjectOutput_0",
3+
"TaskOutput_0",
4+
"TypedOutput_0",
5+
"ValueTaskOuput_0"
6+
]
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
Outcome: Failed
2+
3+
Output:
4+
5+
{"testName":"FallibleBooleanPropertyAsync","injectedValues":[0]}GalaxyCheck.Runners.PropertyFailedException :
6+
7+
Falsified after 1 test (0 shrinks)
8+
Reproduction:
9+
[Replay("H4sIAAAAAAAACjMx0jPQAwCnieL5BQAAAA==")]
10+
Counterexample: [0]
11+
12+
---- Failed!
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
Outcome: Failed
2+
3+
Output:
4+
5+
{"testName":"FallibleBooleanProperty","injectedValues":[0]}GalaxyCheck.Runners.PropertyFailedException :
6+
7+
Falsified after 1 test (0 shrinks)
8+
Reproduction:
9+
[Replay("H4sIAAAAAAAACjMx0jPQAwCnieL5BQAAAA==")]
10+
Counterexample: [0]
11+
12+
---- Failed!

0 commit comments

Comments
 (0)