Skip to content

Commit 7efae41

Browse files
committed
Finalize implementation and add crash detection support.
1 parent 90b56a8 commit 7efae41

20 files changed

+272
-108
lines changed

test/EFCore.Jet.FunctionalTests/EFCore.Jet.FunctionalTests.csproj

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,11 @@
88
</PropertyGroup>
99

1010
<ItemGroup>
11-
<Compile Include="..\Shared\**\*.cs" />
11+
<Compile Include="..\Shared\**\*.cs">
12+
<Link>%(RecursiveDir)%(Filename)%(Extension)</Link>
13+
</Compile>
1214
</ItemGroup>
13-
15+
1416
<ItemGroup>
1517
<PackageReference Include="MSTest.TestFramework" />
1618
<PackageReference Include="xunit.core" />

test/EFCore.Jet.FunctionalTests/Properties/AssemblyInfo.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,4 +15,4 @@
1515

1616
#endif
1717

18-
[assembly: TestFramework("EntityFrameworkCore.Jet.FunctionalTests.TestUtilities.Xunit.JetXunitTestFramework", "EntityFrameworkCore.Jet.FunctionalTests")]
18+
[assembly: TestFramework("EntityFrameworkCore.Jet.FunctionalTests.TestUtilities.Xunit." + nameof(JetXunitTestFramework), "EntityFrameworkCore.Jet.FunctionalTests")]

test/EFCore.Jet.FunctionalTests/TestUtilities/Xunit/JetXunitTestCaseRunner.cs

Lines changed: 0 additions & 74 deletions
This file was deleted.

test/EFCore.Jet.FunctionalTests/TestUtilities/Xunit/JetXunitTestFramework.cs

Lines changed: 0 additions & 14 deletions
This file was deleted.

test/EFCore.Jet.Tests/EFCore.Jet.Tests.csproj

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,6 @@
77
<Platforms>AnyCPU;x86</Platforms>
88
</PropertyGroup>
99

10-
<ItemGroup>
11-
<Compile Include="..\Shared\**\*.cs" />
12-
</ItemGroup>
13-
1410
<ItemGroup>
1511
<None Update="config.json">
1612
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
using System;
2+
3+
namespace EntityFrameworkCore.Jet.FunctionalTests.TestUtilities;
4+
5+
[Flags]
6+
public enum AccessProviderTypeVariation
7+
{
8+
None = 0,
9+
X86 = 1 << 0,
10+
X64 = 1 << 1,
11+
Odbc = 1 << 2,
12+
OleDb = 1 << 3,
13+
All = -1,
14+
}
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
using System;
2+
using System.Linq;
3+
using System.Threading.Tasks;
4+
using Microsoft.EntityFrameworkCore.TestUtilities.Xunit;
5+
6+
namespace EntityFrameworkCore.Jet.FunctionalTests.TestUtilities;
7+
8+
/// <summary>
9+
/// Marks a test method or class that is known to crash the test runner.
10+
/// </summary>
11+
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = true)]
12+
public class TestRunnerCrashAttribute : Attribute, ITestCondition
13+
{
14+
public const string DefaultSkipReason = "The test is known to crash the test runner.";
15+
16+
protected AccessProviderTypeVariation[] AccessProviderTypeVariations { get; }
17+
18+
public TestRunnerCrashAttribute(params AccessProviderTypeVariation[] accessProviderTypeVariations)
19+
{
20+
AccessProviderTypeVariations = accessProviderTypeVariations.Length > 0
21+
? accessProviderTypeVariations
22+
: new[] { AccessProviderTypeVariation.All };
23+
}
24+
25+
public virtual ValueTask<bool> IsMetAsync()
26+
{
27+
// Implement and enable if we want to filter tests by specific runtime scenarios.
28+
var currentVariation = AccessProviderTypeVariation.All; // AppConfig.AccessProviderTypeVariation;
29+
var isMet = AccessProviderTypeVariations.Any(v => v.HasFlag(currentVariation));
30+
31+
if (!isMet && string.IsNullOrEmpty(Skip))
32+
{
33+
Skip = DefaultSkipReason;
34+
}
35+
36+
return new ValueTask<bool>(isMet);
37+
}
38+
39+
public virtual string SkipReason
40+
=> Skip;
41+
42+
public virtual string Skip { get; set; }
43+
}
Lines changed: 135 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,135 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.IO;
4+
using System.Reflection;
5+
using System.Threading;
6+
using System.Threading.Tasks;
7+
using Xunit.Abstractions;
8+
using Xunit.Sdk;
9+
10+
namespace EntityFrameworkCore.Jet.FunctionalTests.TestUtilities.Xunit;
11+
12+
public class JetXunitTestCaseRunner : XunitTestCaseRunner
13+
{
14+
public const string TestRunnerCrashCacheDirectory = "TestRunnerCrashCache";
15+
public const string AutoSkipPrefix = "[AutoSkip]";
16+
public const string AutoSkipTestRunnerCrashingTestsEnvironmentVariableName = "EFCoreJet_AutoSkipTestRunnerCrashingTests";
17+
18+
public virtual bool EnableAutoSkipTestsKnownToCrashTestRunner
19+
=> (Environment.GetEnvironmentVariable(AutoSkipTestRunnerCrashingTestsEnvironmentVariableName)?.ToLowerInvariant() ?? "true") != "false";
20+
21+
public JetXunitTestCaseRunner(IXunitTestCase testCase,
22+
string displayName,
23+
string skipReason,
24+
object[] constructorArguments,
25+
object[] testMethodArguments,
26+
IMessageBus messageBus,
27+
ExceptionAggregator aggregator,
28+
CancellationTokenSource cancellationTokenSource)
29+
: base(
30+
testCase,
31+
displayName,
32+
skipReason,
33+
constructorArguments,
34+
testMethodArguments,
35+
messageBus,
36+
aggregator,
37+
cancellationTokenSource)
38+
{
39+
}
40+
41+
protected override XunitTestRunner CreateTestRunner(ITest test,
42+
IMessageBus messageBus,
43+
Type testClass,
44+
object[] constructorArguments,
45+
MethodInfo testMethod,
46+
object[] testMethodArguments,
47+
string skipReason,
48+
IReadOnlyList<BeforeAfterTestAttribute> beforeAfterAttributes,
49+
ExceptionAggregator aggregator,
50+
CancellationTokenSource cancellationTokenSource)
51+
=> new JetXunitTestRunner(
52+
test,
53+
messageBus,
54+
testClass,
55+
constructorArguments,
56+
testMethod,
57+
testMethodArguments,
58+
skipReason,
59+
beforeAfterAttributes,
60+
new ExceptionAggregator(aggregator),
61+
cancellationTokenSource);
62+
63+
/// <remarks>
64+
/// `TestRunner&lt;TTestCase&gt;.RunAsync()` is not virtual, so we need to override this method here to call our own
65+
/// `JetXunitTestRunner.RunAsync()` implementation.
66+
/// </remarks>>
67+
protected override async Task<RunSummary> RunTestAsync()
68+
{
69+
if (EnableAutoSkipTestsKnownToCrashTestRunner)
70+
{
71+
AutoSkipTestsKnownToCrashTestRunner();
72+
}
73+
74+
return await RunWithCrashDetection(
75+
() => ((JetXunitTestRunner)CreateTestRunner(
76+
CreateTest(TestCase, DisplayName),
77+
MessageBus,
78+
TestClass,
79+
ConstructorArguments,
80+
TestMethod,
81+
TestMethodArguments,
82+
SkipReason,
83+
BeforeAfterAttributes,
84+
Aggregator,
85+
CancellationTokenSource))
86+
.RunAsync());
87+
}
88+
89+
protected virtual async Task<RunSummary> RunWithCrashDetection(Func<Task<RunSummary>> func)
90+
{
91+
Directory.CreateDirectory(TestRunnerCrashCacheDirectory);
92+
93+
var filePath = Path.Combine(TestRunnerCrashCacheDirectory, $"{DateTime.UtcNow:yyyyMMdd'_'HHmmss.fffffff}_{(Environment.Is64BitProcess ? "x64" : "x86")}_{Guid.NewGuid()}.txt");
94+
var contents = $"{TestCase.TestMethod.TestClass.Class.Name}\t{TestCase.TestMethod.Method.Name}";
95+
await File.WriteAllTextAsync(filePath, contents);
96+
97+
var result = await func();
98+
99+
File.Delete(filePath);
100+
101+
return result;
102+
}
103+
104+
protected virtual void AutoSkipTestsKnownToCrashTestRunner()
105+
{
106+
if (IsTestKnownToCrashTestRunner(TestCase))
107+
{
108+
SkipReason = $"{AutoSkipPrefix} {TestRunnerCrashAttribute.DefaultSkipReason}";
109+
}
110+
}
111+
112+
protected virtual bool IsTestKnownToCrashTestRunner(ITestCase testCase)
113+
{
114+
if (File.Exists(JetXunitTestFramework.TestsKnownToCrashTestRunnerFilePath))
115+
{
116+
foreach (var line in File.ReadLines(JetXunitTestFramework.TestsKnownToCrashTestRunnerFilePath))
117+
{
118+
var parts = line.Split('\t');
119+
if (parts.Length >= 2)
120+
{
121+
var testClass = parts[^2];
122+
var testMethod = parts[^1];
123+
124+
if (testClass == testCase.TestMethod.TestClass.Class.Name &&
125+
testMethod == testCase.TestMethod.Method.Name)
126+
{
127+
return true;
128+
}
129+
}
130+
}
131+
}
132+
133+
return false;
134+
}
135+
}
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
using System;
2+
using System.IO;
3+
using Xunit.Abstractions;
4+
using Xunit.Sdk;
5+
6+
namespace EntityFrameworkCore.Jet.FunctionalTests.TestUtilities.Xunit;
7+
8+
public class JetXunitTestFramework : XunitTestFramework
9+
{
10+
public const string TestsKnownToCrashTestRunnerFilePath = "./../../../TestsKnownToCrashTestRunner.txt";
11+
public const string DetectCrashesOfPreviousRunsEnvironmentVariableName = "EFCoreJet_DetectCrashesOfPreviousRuns";
12+
13+
public virtual bool EnableDetectCrashesOfPreviousRuns
14+
=> Environment.GetEnvironmentVariable(DetectCrashesOfPreviousRunsEnvironmentVariableName)?.ToLowerInvariant() == "true";
15+
16+
public JetXunitTestFramework(IMessageSink messageSink) : base(messageSink)
17+
{
18+
}
19+
20+
protected override ITestFrameworkDiscoverer CreateDiscoverer(IAssemblyInfo assemblyInfo)
21+
{
22+
if (EnableDetectCrashesOfPreviousRuns)
23+
{
24+
DetectCrashesOfPreviousRuns();
25+
}
26+
27+
return new JetXunitTestFrameworkDiscoverer(assemblyInfo, SourceInformationProvider, DiagnosticMessageSink);
28+
}
29+
30+
protected virtual void DetectCrashesOfPreviousRuns()
31+
{
32+
if (!Directory.Exists(JetXunitTestCaseRunner.TestRunnerCrashCacheDirectory))
33+
return;
34+
35+
foreach (var filePath in Directory.EnumerateFiles(JetXunitTestCaseRunner.TestRunnerCrashCacheDirectory, "*.txt"))
36+
{
37+
var contents = File.ReadAllText(filePath);
38+
contents = $"{string.Join('\t', Path.GetFileNameWithoutExtension(filePath).Split('_'))}\t{contents}\n";
39+
File.AppendAllText(TestsKnownToCrashTestRunnerFilePath, contents);
40+
41+
File.Delete(filePath);
42+
}
43+
}
44+
}
Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -27,9 +27,8 @@ public JetXunitTestFrameworkDiscoverer(
2727
}
2828

2929
protected override bool IsValidTestClass(ITypeInfo type)
30-
=> base.IsValidTestClass(type) /* &&
31-
IsTestConditionMet<SupportedServerVersionConditionAttribute>(type) &&
32-
IsTestConditionMet<SupportedServerVersionLessThanConditionAttribute>(type)*/;
30+
=> base.IsValidTestClass(type) &&
31+
IsTestConditionMet<TestRunnerCrashAttribute>(type);
3332

3433
protected virtual bool IsTestConditionMet<TType>(ITypeInfo type) where TType : ITestCondition
3534
=> GetTestConditions<TType>(type).Aggregate(true, (current, next) => current && next.IsMetAsync().Result);

0 commit comments

Comments
 (0)