Refactor xUnit extensibility
Adds our own hook for before/after logic that's more usable, called `ITestMethodLifecycle`. This provides access to a context object including the information about the test and the output helper. This can be implemented by attributes or by the class itself. The goal (and result) of this, is that we have a single *test executor* extensibility point that provides all of the features we need. We should use this everywhere we need features xUnit doesn't have. Adding a new extensibility point (`ITestMethodLifecycle`) allows us to do this without turning all of these features into a giant monolith. --- Also updated our existing extensibility to use this new hook. I did as much cleanup as a could to remove duplication from logging and keep it loosly coupled. I didn't want to tease this apart completely because the scope of this PR is already pretty large.
This commit is contained in:
parent
31b0a53a9f
commit
0e7ff1e6ec
|
|
@ -9,6 +9,7 @@ using System.Linq;
|
|||
using System.Reflection;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Text;
|
||||
using Microsoft.AspNetCore.Testing;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Serilog;
|
||||
using Serilog.Core;
|
||||
|
|
@ -23,14 +24,6 @@ namespace Microsoft.Extensions.Logging.Testing
|
|||
private static readonly string MaxPathLengthEnvironmentVariableName = "ASPNETCORE_TEST_LOG_MAXPATH";
|
||||
private static readonly string LogFileExtension = ".log";
|
||||
private static readonly int MaxPathLength = GetMaxPathLength();
|
||||
private static char[] InvalidFileChars = new char[]
|
||||
{
|
||||
'\"', '<', '>', '|', '\0',
|
||||
(char)1, (char)2, (char)3, (char)4, (char)5, (char)6, (char)7, (char)8, (char)9, (char)10,
|
||||
(char)11, (char)12, (char)13, (char)14, (char)15, (char)16, (char)17, (char)18, (char)19, (char)20,
|
||||
(char)21, (char)22, (char)23, (char)24, (char)25, (char)26, (char)27, (char)28, (char)29, (char)30,
|
||||
(char)31, ':', '*', '?', '\\', '/', ' ', (char)127
|
||||
};
|
||||
|
||||
private static readonly object _lock = new object();
|
||||
private static readonly Dictionary<Assembly, AssemblyTestLog> _logs = new Dictionary<Assembly, AssemblyTestLog>();
|
||||
|
|
@ -113,8 +106,8 @@ namespace Microsoft.Extensions.Logging.Testing
|
|||
SerilogLoggerProvider serilogLoggerProvider = null;
|
||||
if (!string.IsNullOrEmpty(_baseDirectory))
|
||||
{
|
||||
logOutputDirectory = Path.Combine(GetAssemblyBaseDirectory(_baseDirectory, _assembly), className);
|
||||
testName = RemoveIllegalFileChars(testName);
|
||||
logOutputDirectory = Path.Combine(_baseDirectory, className);
|
||||
testName = TestFileOutputContext.RemoveIllegalFileChars(testName);
|
||||
|
||||
if (logOutputDirectory.Length + testName.Length + LogFileExtension.Length >= MaxPathLength)
|
||||
{
|
||||
|
|
@ -184,10 +177,10 @@ namespace Microsoft.Extensions.Logging.Testing
|
|||
{
|
||||
var logStart = DateTimeOffset.UtcNow;
|
||||
SerilogLoggerProvider serilogLoggerProvider = null;
|
||||
var globalLogDirectory = GetAssemblyBaseDirectory(baseDirectory, assembly);
|
||||
if (!string.IsNullOrEmpty(globalLogDirectory))
|
||||
if (!string.IsNullOrEmpty(baseDirectory))
|
||||
{
|
||||
var globalLogFileName = Path.Combine(globalLogDirectory, "global.log");
|
||||
baseDirectory = TestFileOutputContext.GetAssemblyBaseDirectory(assembly, baseDirectory);
|
||||
var globalLogFileName = Path.Combine(baseDirectory, "global.log");
|
||||
serilogLoggerProvider = ConfigureFileLogging(globalLogFileName, logStart);
|
||||
}
|
||||
|
||||
|
|
@ -222,31 +215,26 @@ namespace Microsoft.Extensions.Logging.Testing
|
|||
{
|
||||
if (!_logs.TryGetValue(assembly, out var log))
|
||||
{
|
||||
var baseDirectory = GetFileLoggerAttribute(assembly).BaseDirectory;
|
||||
var baseDirectory = TestFileOutputContext.GetOutputDirectory(assembly);
|
||||
|
||||
log = Create(assembly, baseDirectory);
|
||||
_logs[assembly] = log;
|
||||
|
||||
// Try to clear previous logs
|
||||
var assemblyBaseDirectory = GetAssemblyBaseDirectory(baseDirectory, assembly);
|
||||
if (Directory.Exists(assemblyBaseDirectory))
|
||||
// Try to clear previous logs, continue if it fails.
|
||||
var assemblyBaseDirectory = TestFileOutputContext.GetAssemblyBaseDirectory(assembly);
|
||||
if (!string.IsNullOrEmpty(assemblyBaseDirectory))
|
||||
{
|
||||
try
|
||||
{
|
||||
Directory.Delete(assemblyBaseDirectory, recursive: true);
|
||||
}
|
||||
catch {}
|
||||
catch { }
|
||||
}
|
||||
}
|
||||
return log;
|
||||
}
|
||||
}
|
||||
|
||||
private static string GetAssemblyBaseDirectory(string baseDirectory, Assembly assembly)
|
||||
=> string.IsNullOrEmpty(baseDirectory)
|
||||
? string.Empty
|
||||
: Path.Combine(baseDirectory, assembly.GetName().Name, GetFileLoggerAttribute(assembly).TFM);
|
||||
|
||||
private static TestFrameworkFileLoggerAttribute GetFileLoggerAttribute(Assembly assembly)
|
||||
=> assembly.GetCustomAttribute<TestFrameworkFileLoggerAttribute>()
|
||||
?? throw new InvalidOperationException($"No {nameof(TestFrameworkFileLoggerAttribute)} found on the assembly {assembly.GetName().Name}. "
|
||||
|
|
@ -275,27 +263,6 @@ namespace Microsoft.Extensions.Logging.Testing
|
|||
return new SerilogLoggerProvider(serilogger, dispose: true);
|
||||
}
|
||||
|
||||
private static string RemoveIllegalFileChars(string s)
|
||||
{
|
||||
var sb = new StringBuilder();
|
||||
|
||||
foreach (var c in s)
|
||||
{
|
||||
if (InvalidFileChars.Contains(c))
|
||||
{
|
||||
if (sb.Length > 0 && sb[sb.Length - 1] != '_')
|
||||
{
|
||||
sb.Append('_');
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
sb.Append(c);
|
||||
}
|
||||
}
|
||||
return sb.ToString();
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
(_serviceProvider as IDisposable)?.Dispose();
|
||||
|
|
|
|||
|
|
@ -2,6 +2,11 @@
|
|||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Testing;
|
||||
|
||||
namespace Microsoft.Extensions.Logging.Testing
|
||||
{
|
||||
|
|
@ -12,7 +17,23 @@ namespace Microsoft.Extensions.Logging.Testing
|
|||
/// This currently only works in Windows environments
|
||||
/// </remarks>
|
||||
[AttributeUsage(AttributeTargets.Method, AllowMultiple = false)]
|
||||
public class CollectDumpAttribute : Attribute
|
||||
public class CollectDumpAttribute : Attribute, ITestMethodLifecycle
|
||||
{
|
||||
public Task OnTestStartAsync(TestContext context, CancellationToken cancellationToken)
|
||||
{
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
public Task OnTestEndAsync(TestContext context, Exception exception, CancellationToken cancellationToken)
|
||||
{
|
||||
if (exception != null)
|
||||
{
|
||||
var path = Path.Combine(context.FileOutput.TestClassOutputDirectory, context.FileOutput.GetUniqueFileName(context.FileOutput.TestName, ".dmp"));
|
||||
var process = Process.GetCurrentProcess();
|
||||
DumpCollector.Collect(process, path);
|
||||
}
|
||||
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@
|
|||
|
||||
using System;
|
||||
using System.Reflection;
|
||||
using Microsoft.AspNetCore.Testing;
|
||||
using Xunit.Abstractions;
|
||||
|
||||
namespace Microsoft.Extensions.Logging.Testing
|
||||
|
|
@ -18,6 +19,6 @@ namespace Microsoft.Extensions.Logging.Testing
|
|||
// For back compat
|
||||
IDisposable StartLog(out ILoggerFactory loggerFactory, LogLevel minLogLevel, string testName);
|
||||
|
||||
void Initialize(MethodInfo methodInfo, object[] testMethodArguments, ITestOutputHelper testOutputHelper);
|
||||
void Initialize(TestContext context, MethodInfo methodInfo, object[] testMethodArguments, ITestOutputHelper testOutputHelper);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@
|
|||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System.Reflection;
|
||||
using Microsoft.AspNetCore.Testing;
|
||||
using Xunit.Abstractions;
|
||||
|
||||
namespace Microsoft.Extensions.Logging.Testing
|
||||
|
|
@ -13,9 +14,9 @@ namespace Microsoft.Extensions.Logging.Testing
|
|||
|
||||
public ITestSink TestSink { get; set; }
|
||||
|
||||
public override void Initialize(MethodInfo methodInfo, object[] testMethodArguments, ITestOutputHelper testOutputHelper)
|
||||
public override void Initialize(TestContext context, MethodInfo methodInfo, object[] testMethodArguments, ITestOutputHelper testOutputHelper)
|
||||
{
|
||||
base.Initialize(methodInfo, testMethodArguments, testOutputHelper);
|
||||
base.Initialize(context, methodInfo, testMethodArguments, testOutputHelper);
|
||||
|
||||
TestSink = new TestSink();
|
||||
LoggerFactory.AddProvider(new TestLoggerProvider(TestSink));
|
||||
|
|
|
|||
|
|
@ -6,12 +6,16 @@ using System.Linq;
|
|||
using System.Reflection;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.ExceptionServices;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Testing;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Serilog;
|
||||
using Xunit.Abstractions;
|
||||
|
||||
namespace Microsoft.Extensions.Logging.Testing
|
||||
{
|
||||
public class LoggedTestBase : ILoggedTest
|
||||
public class LoggedTestBase : ILoggedTest, ITestMethodLifecycle
|
||||
{
|
||||
private ExceptionDispatchInfo _initializationException;
|
||||
|
||||
|
|
@ -23,11 +27,11 @@ namespace Microsoft.Extensions.Logging.Testing
|
|||
TestOutputHelper = output;
|
||||
}
|
||||
|
||||
protected TestContext Context { get; private set; }
|
||||
|
||||
// Internal for testing
|
||||
internal string ResolvedTestClassName { get; set; }
|
||||
|
||||
internal RepeatContext RepeatContext { get; set; }
|
||||
|
||||
public string ResolvedLogOutputDirectory { get; set; }
|
||||
|
||||
public string ResolvedTestMethodName { get; set; }
|
||||
|
|
@ -49,7 +53,7 @@ namespace Microsoft.Extensions.Logging.Testing
|
|||
return AssemblyTestLog.ForAssembly(GetType().GetTypeInfo().Assembly).StartTestLog(TestOutputHelper, GetType().FullName, out loggerFactory, minLogLevel, testName);
|
||||
}
|
||||
|
||||
public virtual void Initialize(MethodInfo methodInfo, object[] testMethodArguments, ITestOutputHelper testOutputHelper)
|
||||
public virtual void Initialize(TestContext context, MethodInfo methodInfo, object[] testMethodArguments, ITestOutputHelper testOutputHelper)
|
||||
{
|
||||
try
|
||||
{
|
||||
|
|
@ -59,25 +63,22 @@ namespace Microsoft.Extensions.Logging.Testing
|
|||
var logLevelAttribute = methodInfo.GetCustomAttribute<LogLevelAttribute>()
|
||||
?? methodInfo.DeclaringType.GetCustomAttribute<LogLevelAttribute>()
|
||||
?? methodInfo.DeclaringType.Assembly.GetCustomAttribute<LogLevelAttribute>();
|
||||
var testName = testMethodArguments.Aggregate(methodInfo.Name, (a, b) => $"{a}-{(b ?? "null")}");
|
||||
|
||||
var useShortClassName = methodInfo.DeclaringType.GetCustomAttribute<ShortClassNameAttribute>()
|
||||
?? methodInfo.DeclaringType.Assembly.GetCustomAttribute<ShortClassNameAttribute>();
|
||||
// internal for testing
|
||||
ResolvedTestClassName = useShortClassName == null ? classType.FullName : classType.Name;
|
||||
ResolvedTestClassName = context.FileOutput.TestClassName;
|
||||
|
||||
_testLog = AssemblyTestLog
|
||||
.ForAssembly(classType.GetTypeInfo().Assembly)
|
||||
.StartTestLog(
|
||||
TestOutputHelper,
|
||||
ResolvedTestClassName,
|
||||
context.FileOutput.TestClassName,
|
||||
out var loggerFactory,
|
||||
logLevelAttribute?.LogLevel ?? LogLevel.Debug,
|
||||
out var resolvedTestName,
|
||||
out var logOutputDirectory,
|
||||
testName);
|
||||
out var logDirectory,
|
||||
context.FileOutput.TestName);
|
||||
|
||||
ResolvedLogOutputDirectory = logOutputDirectory;
|
||||
ResolvedLogOutputDirectory = logDirectory;
|
||||
ResolvedTestMethodName = resolvedTestName;
|
||||
|
||||
LoggerFactory = loggerFactory;
|
||||
|
|
@ -91,7 +92,7 @@ namespace Microsoft.Extensions.Logging.Testing
|
|||
|
||||
public virtual void Dispose()
|
||||
{
|
||||
if(_testLog == null)
|
||||
if (_testLog == null)
|
||||
{
|
||||
// It seems like sometimes the MSBuild goop that adds the test framework can end up in a bad state and not actually add it
|
||||
// Not sure yet why that happens but the exception isn't clear so I'm adding this error so we can detect it better.
|
||||
|
|
@ -102,5 +103,19 @@ namespace Microsoft.Extensions.Logging.Testing
|
|||
_initializationException?.Throw();
|
||||
_testLog.Dispose();
|
||||
}
|
||||
|
||||
Task ITestMethodLifecycle.OnTestStartAsync(TestContext context, CancellationToken cancellationToken)
|
||||
{
|
||||
|
||||
Context = context;
|
||||
|
||||
Initialize(context, context.TestMethod, context.MethodArguments, context.Output);
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
Task ITestMethodLifecycle.OnTestEndAsync(TestContext context, Exception exception, CancellationToken cancellationToken)
|
||||
{
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,12 +0,0 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
namespace Microsoft.Extensions.Logging.Testing
|
||||
{
|
||||
public class RepeatContext
|
||||
{
|
||||
internal int Limit { get; set; }
|
||||
|
||||
internal int CurrentIteration { get; set; }
|
||||
}
|
||||
}
|
||||
|
|
@ -1,12 +0,0 @@
|
|||
// Copyright(c) .NET Foundation.All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System;
|
||||
|
||||
namespace Microsoft.Extensions.Logging.Testing
|
||||
{
|
||||
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Assembly, AllowMultiple = false)]
|
||||
public class ShortClassNameAttribute : Attribute
|
||||
{
|
||||
}
|
||||
}
|
||||
|
|
@ -1,20 +1,17 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System;
|
||||
using Microsoft.AspNetCore.Testing;
|
||||
|
||||
namespace Microsoft.Extensions.Logging.Testing
|
||||
{
|
||||
[AttributeUsage(AttributeTargets.Assembly, AllowMultiple = false)]
|
||||
public class TestFrameworkFileLoggerAttribute : Attribute
|
||||
public class TestFrameworkFileLoggerAttribute : TestOutputDirectoryAttribute
|
||||
{
|
||||
public TestFrameworkFileLoggerAttribute(string tfm, string baseDirectory = null)
|
||||
: base(tfm, baseDirectory)
|
||||
{
|
||||
TFM = tfm;
|
||||
BaseDirectory = baseDirectory;
|
||||
}
|
||||
|
||||
public string TFM { get; }
|
||||
public string BaseDirectory { get; }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,28 +0,0 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using Microsoft.AspNetCore.Testing;
|
||||
using Xunit.Abstractions;
|
||||
using Xunit.Sdk;
|
||||
|
||||
namespace Microsoft.Extensions.Logging.Testing
|
||||
{
|
||||
public class LoggedConditionalFactDiscoverer : LoggedFactDiscoverer
|
||||
{
|
||||
private readonly IMessageSink _diagnosticMessageSink;
|
||||
|
||||
public LoggedConditionalFactDiscoverer(IMessageSink diagnosticMessageSink) : base(diagnosticMessageSink)
|
||||
{
|
||||
_diagnosticMessageSink = diagnosticMessageSink;
|
||||
}
|
||||
|
||||
protected override IXunitTestCase CreateTestCase(ITestFrameworkDiscoveryOptions discoveryOptions, ITestMethod testMethod, IAttributeInfo factAttribute)
|
||||
{
|
||||
var skipReason = testMethod.EvaluateSkipConditions();
|
||||
return skipReason != null
|
||||
? new SkippedTestCase(skipReason, _diagnosticMessageSink, discoveryOptions.MethodDisplayOrDefault(), TestMethodDisplayOptions.None, testMethod)
|
||||
: base.CreateTestCase(discoveryOptions, testMethod, factAttribute);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
|
@ -1,55 +0,0 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System.Collections.Generic;
|
||||
using Microsoft.AspNetCore.Testing;
|
||||
using Xunit.Abstractions;
|
||||
using Xunit.Sdk;
|
||||
|
||||
namespace Microsoft.Extensions.Logging.Testing
|
||||
{
|
||||
public class LoggedConditionalTheoryDiscoverer : LoggedTheoryDiscoverer
|
||||
{
|
||||
public LoggedConditionalTheoryDiscoverer(IMessageSink diagnosticMessageSink)
|
||||
: base(diagnosticMessageSink)
|
||||
{
|
||||
}
|
||||
|
||||
protected override IEnumerable<IXunitTestCase> CreateTestCasesForTheory(
|
||||
ITestFrameworkDiscoveryOptions discoveryOptions,
|
||||
ITestMethod testMethod,
|
||||
IAttributeInfo theoryAttribute)
|
||||
{
|
||||
var skipReason = testMethod.EvaluateSkipConditions();
|
||||
return skipReason != null
|
||||
? new[] { new SkippedTestCase(skipReason, DiagnosticMessageSink, discoveryOptions.MethodDisplayOrDefault(), TestMethodDisplayOptions.None, testMethod) }
|
||||
: base.CreateTestCasesForTheory(discoveryOptions, testMethod, theoryAttribute);
|
||||
}
|
||||
|
||||
protected override IEnumerable<IXunitTestCase> CreateTestCasesForDataRow(
|
||||
ITestFrameworkDiscoveryOptions discoveryOptions,
|
||||
ITestMethod testMethod, IAttributeInfo theoryAttribute,
|
||||
object[] dataRow)
|
||||
{
|
||||
var skipReason = testMethod.EvaluateSkipConditions();
|
||||
if (skipReason == null && dataRow?.Length > 0)
|
||||
{
|
||||
var obj = dataRow[0];
|
||||
if (obj != null)
|
||||
{
|
||||
var type = obj.GetType();
|
||||
var property = type.GetProperty("Skip");
|
||||
if (property != null && property.PropertyType.Equals(typeof(string)))
|
||||
{
|
||||
skipReason = property.GetValue(obj) as string;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return skipReason != null
|
||||
? base.CreateTestCasesForSkippedDataRow(discoveryOptions, testMethod, theoryAttribute, dataRow, skipReason)
|
||||
: base.CreateTestCasesForDataRow(discoveryOptions, testMethod, theoryAttribute, dataRow);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
|
@ -1,18 +0,0 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using Xunit.Abstractions;
|
||||
using Xunit.Sdk;
|
||||
|
||||
namespace Microsoft.Extensions.Logging.Testing
|
||||
{
|
||||
public class LoggedFactDiscoverer : FactDiscoverer
|
||||
{
|
||||
public LoggedFactDiscoverer(IMessageSink diagnosticMessageSink) : base(diagnosticMessageSink)
|
||||
{
|
||||
}
|
||||
|
||||
protected override IXunitTestCase CreateTestCase(ITestFrameworkDiscoveryOptions discoveryOptions, ITestMethod testMethod, IAttributeInfo factAttribute)
|
||||
=> new LoggedTestCase(DiagnosticMessageSink, discoveryOptions.MethodDisplayOrDefault(), TestMethodDisplayOptions.None, testMethod);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,31 +0,0 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System.Collections.Generic;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Xunit.Abstractions;
|
||||
using Xunit.Sdk;
|
||||
|
||||
namespace Microsoft.Extensions.Logging.Testing
|
||||
{
|
||||
public class LoggedTestAssemblyRunner : XunitTestAssemblyRunner
|
||||
{
|
||||
public LoggedTestAssemblyRunner(
|
||||
ITestAssembly testAssembly,
|
||||
IEnumerable<IXunitTestCase> testCases,
|
||||
IMessageSink diagnosticMessageSink,
|
||||
IMessageSink executionMessageSink,
|
||||
ITestFrameworkExecutionOptions executionOptions)
|
||||
: base(testAssembly, testCases, diagnosticMessageSink, executionMessageSink, executionOptions)
|
||||
{
|
||||
}
|
||||
|
||||
protected override Task<RunSummary> RunTestCollectionAsync(
|
||||
IMessageBus messageBus,
|
||||
ITestCollection testCollection,
|
||||
IEnumerable<IXunitTestCase> testCases,
|
||||
CancellationTokenSource cancellationTokenSource)
|
||||
=> new LoggedTestCollectionRunner(testCollection, testCases, DiagnosticMessageSink, messageBus, TestCaseOrderer, new ExceptionAggregator(Aggregator), cancellationTokenSource).RunAsync();
|
||||
}
|
||||
}
|
||||
|
|
@ -1,37 +0,0 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Xunit.Abstractions;
|
||||
using Xunit.Sdk;
|
||||
|
||||
namespace Microsoft.Extensions.Logging.Testing
|
||||
{
|
||||
public class LoggedTestCase : XunitTestCase
|
||||
{
|
||||
[Obsolete("Called by the de-serializer; should only be called by deriving classes for de-serialization purposes")]
|
||||
public LoggedTestCase() : base()
|
||||
{
|
||||
}
|
||||
|
||||
public LoggedTestCase(
|
||||
IMessageSink diagnosticMessageSink,
|
||||
TestMethodDisplay defaultMethodDisplay,
|
||||
TestMethodDisplayOptions defaultMethodDisplayOptions,
|
||||
ITestMethod testMethod,
|
||||
object[] testMethodArguments = null)
|
||||
: base(diagnosticMessageSink, defaultMethodDisplay, defaultMethodDisplayOptions, testMethod, testMethodArguments)
|
||||
{
|
||||
}
|
||||
|
||||
public override Task<RunSummary> RunAsync(
|
||||
IMessageSink diagnosticMessageSink,
|
||||
IMessageBus messageBus,
|
||||
object[] constructorArguments,
|
||||
ExceptionAggregator aggregator,
|
||||
CancellationTokenSource cancellationTokenSource)
|
||||
=> new LoggedTestCaseRunner(this, DisplayName, SkipReason, constructorArguments, TestMethodArguments, messageBus, aggregator, cancellationTokenSource).RunAsync();
|
||||
}
|
||||
}
|
||||
|
|
@ -1,42 +0,0 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Reflection;
|
||||
using System.Threading;
|
||||
using Xunit.Abstractions;
|
||||
using Xunit.Sdk;
|
||||
|
||||
namespace Microsoft.Extensions.Logging.Testing
|
||||
{
|
||||
public class LoggedTestCaseRunner : XunitTestCaseRunner
|
||||
{
|
||||
public LoggedTestCaseRunner(
|
||||
IXunitTestCase testCase,
|
||||
string displayName,
|
||||
string skipReason,
|
||||
object[] constructorArguments,
|
||||
object[] testMethodArguments,
|
||||
IMessageBus messageBus,
|
||||
ExceptionAggregator aggregator,
|
||||
CancellationTokenSource cancellationTokenSource)
|
||||
: base(testCase, displayName, skipReason, constructorArguments, testMethodArguments, messageBus, aggregator, cancellationTokenSource)
|
||||
{
|
||||
}
|
||||
|
||||
protected override XunitTestRunner CreateTestRunner(
|
||||
ITest test,
|
||||
IMessageBus messageBus,
|
||||
Type testClass,
|
||||
object[] constructorArguments,
|
||||
MethodInfo testMethod,
|
||||
object[] testMethodArguments,
|
||||
string skipReason,
|
||||
IReadOnlyList<BeforeAfterTestAttribute> beforeAfterAttributes,
|
||||
ExceptionAggregator aggregator,
|
||||
CancellationTokenSource cancellationTokenSource)
|
||||
=> new LoggedTestRunner(test, messageBus, testClass, constructorArguments, testMethod, testMethodArguments,
|
||||
skipReason, beforeAfterAttributes, new ExceptionAggregator(aggregator), cancellationTokenSource);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,36 +0,0 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Xunit.Abstractions;
|
||||
using Xunit.Sdk;
|
||||
|
||||
namespace Microsoft.Extensions.Logging.Testing
|
||||
{
|
||||
public class LoggedTestClassRunner : XunitTestClassRunner
|
||||
{
|
||||
public LoggedTestClassRunner(
|
||||
ITestClass testClass,
|
||||
IReflectionTypeInfo @class,
|
||||
IEnumerable<IXunitTestCase> testCases,
|
||||
IMessageSink diagnosticMessageSink,
|
||||
IMessageBus messageBus,
|
||||
ITestCaseOrderer testCaseOrderer,
|
||||
ExceptionAggregator aggregator,
|
||||
CancellationTokenSource cancellationTokenSource,
|
||||
IDictionary<Type, object> collectionFixtureMappings)
|
||||
: base(testClass, @class, testCases, diagnosticMessageSink, messageBus, testCaseOrderer, aggregator, cancellationTokenSource, collectionFixtureMappings)
|
||||
{
|
||||
}
|
||||
|
||||
protected override Task<RunSummary> RunTestMethodAsync(
|
||||
ITestMethod testMethod,
|
||||
IReflectionMethodInfo method,
|
||||
IEnumerable<IXunitTestCase> testCases,
|
||||
object[] constructorArguments)
|
||||
=> new LoggedTestMethodRunner(testMethod, Class, method, testCases, DiagnosticMessageSink, MessageBus, new ExceptionAggregator(Aggregator), CancellationTokenSource, constructorArguments).RunAsync();
|
||||
}
|
||||
}
|
||||
|
|
@ -1,33 +0,0 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System.Collections.Generic;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Xunit.Abstractions;
|
||||
using Xunit.Sdk;
|
||||
|
||||
namespace Microsoft.Extensions.Logging.Testing
|
||||
{
|
||||
public class LoggedTestCollectionRunner : XunitTestCollectionRunner
|
||||
{
|
||||
private readonly IMessageSink _diagnosticMessageSink;
|
||||
|
||||
public LoggedTestCollectionRunner(
|
||||
ITestCollection testCollection,
|
||||
IEnumerable<IXunitTestCase> testCases,
|
||||
IMessageSink diagnosticMessageSink,
|
||||
IMessageBus messageBus,
|
||||
ITestCaseOrderer testCaseOrderer,
|
||||
ExceptionAggregator aggregator,
|
||||
CancellationTokenSource cancellationTokenSource)
|
||||
: base(testCollection, testCases, diagnosticMessageSink, messageBus, testCaseOrderer, aggregator, cancellationTokenSource)
|
||||
{
|
||||
// Base class doesn't expose this, so capture it here.
|
||||
_diagnosticMessageSink = diagnosticMessageSink;
|
||||
}
|
||||
|
||||
protected override Task<RunSummary> RunTestClassAsync(ITestClass testClass, IReflectionTypeInfo @class, IEnumerable<IXunitTestCase> testCases)
|
||||
=> new LoggedTestClassRunner(testClass, @class, testCases, _diagnosticMessageSink, MessageBus, TestCaseOrderer, new ExceptionAggregator(Aggregator), CancellationTokenSource, CollectionFixtureMappings).RunAsync();
|
||||
}
|
||||
}
|
||||
|
|
@ -1,26 +0,0 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System.Reflection;
|
||||
using Xunit.Abstractions;
|
||||
using Xunit.Sdk;
|
||||
|
||||
namespace Microsoft.Extensions.Logging.Testing
|
||||
{
|
||||
public class LoggedTestFramework : XunitTestFramework
|
||||
{
|
||||
public LoggedTestFramework(IMessageSink messageSink) : base(messageSink)
|
||||
{
|
||||
}
|
||||
|
||||
protected override ITestFrameworkDiscoverer CreateDiscoverer(IAssemblyInfo assemblyInfo)
|
||||
{
|
||||
return new LoggedTestFrameworkDiscoverer(assemblyInfo, SourceInformationProvider, DiagnosticMessageSink);
|
||||
}
|
||||
|
||||
protected override ITestFrameworkExecutor CreateExecutor(AssemblyName assemblyName)
|
||||
{
|
||||
return new LoggedTestFrameworkExecutor(assemblyName, SourceInformationProvider, DiagnosticMessageSink);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,80 +0,0 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Microsoft.AspNetCore.Testing;
|
||||
using Xunit;
|
||||
using Xunit.Abstractions;
|
||||
using Xunit.Sdk;
|
||||
|
||||
namespace Microsoft.Extensions.Logging.Testing
|
||||
{
|
||||
public class LoggedTestFrameworkDiscoverer : XunitTestFrameworkDiscoverer
|
||||
{
|
||||
private IDictionary<Type, IXunitTestCaseDiscoverer> Discoverers { get; }
|
||||
|
||||
public LoggedTestFrameworkDiscoverer(
|
||||
IAssemblyInfo assemblyInfo,
|
||||
ISourceInformationProvider sourceProvider,
|
||||
IMessageSink diagnosticMessageSink,
|
||||
IXunitTestCollectionFactory collectionFactory = null)
|
||||
: base(assemblyInfo, sourceProvider, diagnosticMessageSink, collectionFactory)
|
||||
{
|
||||
Discoverers = new Dictionary<Type, IXunitTestCaseDiscoverer>()
|
||||
{
|
||||
{ typeof(ConditionalTheoryAttribute), new LoggedConditionalTheoryDiscoverer(diagnosticMessageSink) },
|
||||
{ typeof(ConditionalFactAttribute), new LoggedConditionalFactDiscoverer(diagnosticMessageSink) },
|
||||
{ typeof(TheoryAttribute), new LoggedTheoryDiscoverer(diagnosticMessageSink) },
|
||||
{ typeof(FactAttribute), new LoggedFactDiscoverer(diagnosticMessageSink) }
|
||||
};
|
||||
}
|
||||
|
||||
protected override bool FindTestsForMethod(
|
||||
ITestMethod testMethod,
|
||||
bool includeSourceInformation,
|
||||
IMessageBus messageBus,
|
||||
ITestFrameworkDiscoveryOptions discoveryOptions)
|
||||
{
|
||||
if (typeof(ILoggedTest).IsAssignableFrom(testMethod.TestClass.Class.ToRuntimeType()))
|
||||
{
|
||||
var factAttributes = testMethod.Method.GetCustomAttributes(typeof(FactAttribute));
|
||||
if (factAttributes.Count() > 1)
|
||||
{
|
||||
var message = $"Test method '{testMethod.TestClass.Class.Name}.{testMethod.Method.Name}' has multiple [Fact]-derived attributes";
|
||||
var testCase = new ExecutionErrorTestCase(DiagnosticMessageSink, TestMethodDisplay.ClassAndMethod, TestMethodDisplayOptions.None, testMethod, message);
|
||||
return ReportDiscoveredTestCase(testCase, includeSourceInformation, messageBus);
|
||||
}
|
||||
|
||||
var factAttribute = factAttributes.FirstOrDefault();
|
||||
if (factAttribute == null)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
var factAttributeType = (factAttribute as IReflectionAttributeInfo)?.Attribute.GetType();
|
||||
if (!Discoverers.TryGetValue(factAttributeType, out var discoverer))
|
||||
{
|
||||
return base.FindTestsForMethod(testMethod, includeSourceInformation, messageBus, discoveryOptions);
|
||||
}
|
||||
else
|
||||
{
|
||||
foreach (var testCase in discoverer.Discover(discoveryOptions, testMethod, factAttribute))
|
||||
{
|
||||
if (!ReportDiscoveredTestCase(testCase, includeSourceInformation, messageBus))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
return base.FindTestsForMethod(testMethod, includeSourceInformation, messageBus, discoveryOptions);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,26 +0,0 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System.Collections.Generic;
|
||||
using System.Reflection;
|
||||
using Xunit.Abstractions;
|
||||
using Xunit.Sdk;
|
||||
|
||||
namespace Microsoft.Extensions.Logging.Testing
|
||||
{
|
||||
public class LoggedTestFrameworkExecutor : XunitTestFrameworkExecutor
|
||||
{
|
||||
public LoggedTestFrameworkExecutor(AssemblyName assemblyName, ISourceInformationProvider sourceInformationProvider, IMessageSink diagnosticMessageSink)
|
||||
: base(assemblyName, sourceInformationProvider, diagnosticMessageSink)
|
||||
{
|
||||
}
|
||||
|
||||
protected override async void RunTestCases(IEnumerable<IXunitTestCase> testCases, IMessageSink executionMessageSink, ITestFrameworkExecutionOptions executionOptions)
|
||||
{
|
||||
using (var assemblyRunner = new LoggedTestAssemblyRunner(TestAssembly, testCases, DiagnosticMessageSink, executionMessageSink, executionOptions))
|
||||
{
|
||||
await assemblyRunner.RunAsync();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,80 +0,0 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Threading;
|
||||
using Xunit.Abstractions;
|
||||
using Xunit.Sdk;
|
||||
|
||||
namespace Microsoft.Extensions.Logging.Testing
|
||||
{
|
||||
public class LoggedTestInvoker : XunitTestInvoker
|
||||
{
|
||||
private readonly ITestOutputHelper _output;
|
||||
private readonly RepeatContext _repeatContext;
|
||||
private readonly bool _collectDumpOnFailure;
|
||||
|
||||
public LoggedTestInvoker(
|
||||
ITest test,
|
||||
IMessageBus messageBus,
|
||||
Type testClass,
|
||||
object[] constructorArguments,
|
||||
MethodInfo testMethod,
|
||||
object[] testMethodArguments,
|
||||
IReadOnlyList<BeforeAfterTestAttribute> beforeAfterAttributes,
|
||||
ExceptionAggregator aggregator,
|
||||
CancellationTokenSource cancellationTokenSource,
|
||||
ITestOutputHelper output,
|
||||
RepeatContext repeatContext,
|
||||
bool collectDumpOnFailure)
|
||||
: base(test, messageBus, testClass, constructorArguments, testMethod, testMethodArguments, beforeAfterAttributes, aggregator, cancellationTokenSource)
|
||||
{
|
||||
_output = output;
|
||||
_repeatContext = repeatContext;
|
||||
_collectDumpOnFailure = collectDumpOnFailure;
|
||||
}
|
||||
|
||||
protected override object CreateTestClass()
|
||||
{
|
||||
var testClass = base.CreateTestClass();
|
||||
|
||||
(testClass as ILoggedTest).Initialize(
|
||||
TestMethod,
|
||||
TestMethodArguments,
|
||||
_output ?? ConstructorArguments.SingleOrDefault(a => typeof(ITestOutputHelper).IsAssignableFrom(a.GetType())) as ITestOutputHelper);
|
||||
|
||||
if (testClass is LoggedTestBase loggedTestBase)
|
||||
{
|
||||
// Used for testing
|
||||
loggedTestBase.RepeatContext = _repeatContext;
|
||||
}
|
||||
|
||||
return testClass;
|
||||
}
|
||||
|
||||
protected override object CallTestMethod(object testClassInstance)
|
||||
{
|
||||
try
|
||||
{
|
||||
return base.CallTestMethod(testClassInstance);
|
||||
}
|
||||
catch
|
||||
{
|
||||
if (_collectDumpOnFailure && testClassInstance is LoggedTestBase loggedTestBase)
|
||||
{
|
||||
var path = Path.Combine(loggedTestBase.ResolvedLogOutputDirectory, loggedTestBase.ResolvedTestMethodName + ".dmp");
|
||||
var process = Process.GetCurrentProcess();
|
||||
|
||||
DumpCollector.Collect(process, path);
|
||||
}
|
||||
|
||||
throw;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,36 +0,0 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System.Collections.Generic;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Xunit.Abstractions;
|
||||
using Xunit.Sdk;
|
||||
|
||||
namespace Microsoft.Extensions.Logging.Testing
|
||||
{
|
||||
public class LoggedTestMethodRunner : XunitTestMethodRunner
|
||||
{
|
||||
private IMessageSink DiagnosticMessageSink { get; }
|
||||
private object[] ConstructorArguments { get; }
|
||||
|
||||
public LoggedTestMethodRunner(
|
||||
ITestMethod testMethod,
|
||||
IReflectionTypeInfo @class,
|
||||
IReflectionMethodInfo method,
|
||||
IEnumerable<IXunitTestCase> testCases,
|
||||
IMessageSink diagnosticMessageSink,
|
||||
IMessageBus messageBus,
|
||||
ExceptionAggregator aggregator,
|
||||
CancellationTokenSource cancellationTokenSource,
|
||||
object[] constructorArguments)
|
||||
: base(testMethod, @class, method, testCases, diagnosticMessageSink, messageBus, aggregator, cancellationTokenSource, constructorArguments)
|
||||
{
|
||||
DiagnosticMessageSink = diagnosticMessageSink;
|
||||
ConstructorArguments = constructorArguments;
|
||||
}
|
||||
|
||||
protected override Task<RunSummary> RunTestCaseAsync(IXunitTestCase testCase)
|
||||
=> testCase.RunAsync(DiagnosticMessageSink, MessageBus, ConstructorArguments, new ExceptionAggregator(Aggregator), CancellationTokenSource);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,114 +0,0 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Testing;
|
||||
using Xunit.Abstractions;
|
||||
using Xunit.Sdk;
|
||||
|
||||
namespace Microsoft.Extensions.Logging.Testing
|
||||
{
|
||||
public class LoggedTestRunner : XunitTestRunner
|
||||
{
|
||||
public LoggedTestRunner(
|
||||
ITest test,
|
||||
IMessageBus messageBus,
|
||||
Type testClass,
|
||||
object[] constructorArguments,
|
||||
MethodInfo testMethod, object[]
|
||||
testMethodArguments, string skipReason,
|
||||
IReadOnlyList<BeforeAfterTestAttribute> beforeAfterAttributes,
|
||||
ExceptionAggregator aggregator,
|
||||
CancellationTokenSource cancellationTokenSource)
|
||||
: base(test, messageBus, testClass, constructorArguments, testMethod, testMethodArguments, skipReason, beforeAfterAttributes, aggregator, cancellationTokenSource)
|
||||
{
|
||||
}
|
||||
|
||||
protected async override Task<Tuple<decimal, string>> InvokeTestAsync(ExceptionAggregator aggregator)
|
||||
{
|
||||
var testOutputHelper = ConstructorArguments.SingleOrDefault(a => typeof(TestOutputHelper).IsAssignableFrom(a.GetType())) as TestOutputHelper
|
||||
?? new TestOutputHelper();
|
||||
testOutputHelper.Initialize(MessageBus, Test);
|
||||
|
||||
var executionTime = await InvokeTestMethodAsync(aggregator, testOutputHelper);
|
||||
|
||||
var output = testOutputHelper.Output;
|
||||
testOutputHelper.Uninitialize();
|
||||
|
||||
return Tuple.Create(executionTime, output);
|
||||
}
|
||||
|
||||
protected override Task<decimal> InvokeTestMethodAsync(ExceptionAggregator aggregator)
|
||||
=> InvokeTestMethodAsync(aggregator, null);
|
||||
|
||||
private async Task<decimal> InvokeTestMethodAsync(ExceptionAggregator aggregator, ITestOutputHelper output)
|
||||
{
|
||||
var collectDump = TestMethod.GetCustomAttribute<CollectDumpAttribute>() != null;
|
||||
var repeatAttribute = GetRepeatAttribute(TestMethod);
|
||||
|
||||
if (!typeof(LoggedTestBase).IsAssignableFrom(TestClass) || repeatAttribute == null)
|
||||
{
|
||||
return await new LoggedTestInvoker(Test, MessageBus, TestClass, ConstructorArguments, TestMethod, TestMethodArguments, BeforeAfterAttributes, aggregator, CancellationTokenSource, output, null, collectDump).RunAsync();
|
||||
}
|
||||
|
||||
return await RunRepeatTestInvoker(aggregator, output, collectDump, repeatAttribute);
|
||||
}
|
||||
|
||||
private async Task<decimal> RunRepeatTestInvoker(ExceptionAggregator aggregator, ITestOutputHelper output, bool collectDump, RepeatAttribute repeatAttribute)
|
||||
{
|
||||
var repeatContext = new RepeatContext
|
||||
{
|
||||
Limit = repeatAttribute.RunCount
|
||||
};
|
||||
|
||||
var timeTaken = 0.0M;
|
||||
var testLogger = new LoggedTestInvoker(
|
||||
Test,
|
||||
MessageBus,
|
||||
TestClass,
|
||||
ConstructorArguments,
|
||||
TestMethod,
|
||||
TestMethodArguments,
|
||||
BeforeAfterAttributes,
|
||||
aggregator,
|
||||
CancellationTokenSource,
|
||||
output,
|
||||
repeatContext,
|
||||
collectDump);
|
||||
|
||||
for (repeatContext.CurrentIteration = 0; repeatContext.CurrentIteration < repeatContext.Limit; repeatContext.CurrentIteration++)
|
||||
{
|
||||
timeTaken = await testLogger.RunAsync();
|
||||
if (aggregator.HasExceptions)
|
||||
{
|
||||
return timeTaken;
|
||||
}
|
||||
}
|
||||
|
||||
return timeTaken;
|
||||
}
|
||||
|
||||
private RepeatAttribute GetRepeatAttribute(MethodInfo methodInfo)
|
||||
{
|
||||
var attributeCandidate = methodInfo.GetCustomAttribute<RepeatAttribute>();
|
||||
if (attributeCandidate != null)
|
||||
{
|
||||
return attributeCandidate;
|
||||
}
|
||||
|
||||
attributeCandidate = methodInfo.DeclaringType.GetCustomAttribute<RepeatAttribute>();
|
||||
if (attributeCandidate != null)
|
||||
{
|
||||
return attributeCandidate;
|
||||
}
|
||||
|
||||
return methodInfo.DeclaringType.Assembly.GetCustomAttribute<RepeatAttribute>();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,29 +0,0 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System.Collections.Generic;
|
||||
using Xunit.Abstractions;
|
||||
using Xunit.Sdk;
|
||||
|
||||
namespace Microsoft.Extensions.Logging.Testing
|
||||
{
|
||||
public class LoggedTheoryDiscoverer : TheoryDiscoverer
|
||||
{
|
||||
public LoggedTheoryDiscoverer(IMessageSink diagnosticMessageSink) : base(diagnosticMessageSink)
|
||||
{
|
||||
}
|
||||
|
||||
protected override IEnumerable<IXunitTestCase> CreateTestCasesForDataRow(
|
||||
ITestFrameworkDiscoveryOptions discoveryOptions,
|
||||
ITestMethod testMethod,
|
||||
IAttributeInfo theoryAttribute,
|
||||
object[] dataRow)
|
||||
=> new[] { new LoggedTestCase(DiagnosticMessageSink, discoveryOptions.MethodDisplayOrDefault(), TestMethodDisplayOptions.None, testMethod, dataRow) };
|
||||
|
||||
protected override IEnumerable<IXunitTestCase> CreateTestCasesForTheory(
|
||||
ITestFrameworkDiscoveryOptions discoveryOptions,
|
||||
ITestMethod testMethod,
|
||||
IAttributeInfo theoryAttribute)
|
||||
=> new[] { new LoggedTheoryTestCase(DiagnosticMessageSink, discoveryOptions.MethodDisplayOrDefault(), TestMethodDisplayOptions.None, testMethod) };
|
||||
}
|
||||
}
|
||||
|
|
@ -1,36 +0,0 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Xunit.Abstractions;
|
||||
using Xunit.Sdk;
|
||||
|
||||
namespace Microsoft.Extensions.Logging.Testing
|
||||
{
|
||||
public class LoggedTheoryTestCase : XunitTheoryTestCase
|
||||
{
|
||||
[Obsolete("Called by the de-serializer; should only be called by deriving classes for de-serialization purposes")]
|
||||
public LoggedTheoryTestCase() : base()
|
||||
{
|
||||
}
|
||||
|
||||
public LoggedTheoryTestCase(
|
||||
IMessageSink diagnosticMessageSink,
|
||||
TestMethodDisplay defaultMethodDisplay,
|
||||
TestMethodDisplayOptions defaultMethodDisplayOptions,
|
||||
ITestMethod testMethod)
|
||||
: base(diagnosticMessageSink, defaultMethodDisplay, defaultMethodDisplayOptions, testMethod)
|
||||
{
|
||||
}
|
||||
|
||||
public override Task<RunSummary> RunAsync(
|
||||
IMessageSink diagnosticMessageSink,
|
||||
IMessageBus messageBus,
|
||||
object[] constructorArguments,
|
||||
ExceptionAggregator aggregator,
|
||||
CancellationTokenSource cancellationTokenSource)
|
||||
=> new LoggedTheoryTestCaseRunner(this, DisplayName, SkipReason, constructorArguments, diagnosticMessageSink, messageBus, aggregator, cancellationTokenSource).RunAsync();
|
||||
}
|
||||
}
|
||||
|
|
@ -1,41 +0,0 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Reflection;
|
||||
using System.Threading;
|
||||
using Xunit.Abstractions;
|
||||
using Xunit.Sdk;
|
||||
|
||||
namespace Microsoft.Extensions.Logging.Testing
|
||||
{
|
||||
public class LoggedTheoryTestCaseRunner : XunitTheoryTestCaseRunner
|
||||
{
|
||||
public LoggedTheoryTestCaseRunner(
|
||||
IXunitTestCase testCase,
|
||||
string displayName,
|
||||
string skipReason,
|
||||
object[] constructorArguments,
|
||||
IMessageSink diagnosticMessageSink,
|
||||
IMessageBus messageBus,
|
||||
ExceptionAggregator aggregator,
|
||||
CancellationTokenSource cancellationTokenSource)
|
||||
: base(testCase, displayName, skipReason, constructorArguments, diagnosticMessageSink, messageBus, aggregator, cancellationTokenSource)
|
||||
{
|
||||
}
|
||||
|
||||
protected override XunitTestRunner CreateTestRunner(
|
||||
ITest test,
|
||||
IMessageBus messageBus,
|
||||
Type testClass,
|
||||
object[] constructorArguments,
|
||||
MethodInfo testMethod,
|
||||
object[] testMethodArguments,
|
||||
string skipReason,
|
||||
IReadOnlyList<BeforeAfterTestAttribute> beforeAfterAttributes,
|
||||
ExceptionAggregator aggregator,
|
||||
CancellationTokenSource cancellationTokenSource)
|
||||
=> new LoggedTestRunner(test, messageBus, testClass, constructorArguments, testMethod, testMethodArguments, skipReason, beforeAfterAttributes, new ExceptionAggregator(aggregator), cancellationTokenSource);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,28 +0,0 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System;
|
||||
using System.ComponentModel;
|
||||
|
||||
namespace Microsoft.Extensions.Logging.Testing
|
||||
{
|
||||
/// <summary>
|
||||
/// Runs a test multiple times to stress flaky tests that are believed to be fixed.
|
||||
/// This can be used on an assembly, class, or method name.
|
||||
/// Requires using <see cref="LoggedTest"/> to run.
|
||||
/// </summary>
|
||||
[EditorBrowsable(EditorBrowsableState.Never)]
|
||||
[AttributeUsage(AttributeTargets.Method | AttributeTargets.Class | AttributeTargets.Assembly, AllowMultiple = false)]
|
||||
public class RepeatAttribute : Attribute
|
||||
{
|
||||
public RepeatAttribute(int runCount = 10)
|
||||
{
|
||||
RunCount = runCount;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The number of times to run a test.
|
||||
/// </summary>
|
||||
public int RunCount { get; }
|
||||
}
|
||||
}
|
||||
|
|
@ -11,8 +11,8 @@
|
|||
Condition="'$(GenerateLoggingTestingAssemblyAttributes)' != 'false'">
|
||||
<ItemGroup>
|
||||
<AssemblyAttribute Include="Xunit.TestFramework">
|
||||
<_Parameter1>Microsoft.Extensions.Logging.Testing.LoggedTestFramework</_Parameter1>
|
||||
<_Parameter2>Microsoft.Extensions.Logging.Testing</_Parameter2>
|
||||
<_Parameter1>Microsoft.AspNetCore.Testing.AspNetTestFramework</_Parameter1>
|
||||
<_Parameter2>Microsoft.AspNetCore.Testing</_Parameter2>
|
||||
</AssemblyAttribute>
|
||||
|
||||
<AssemblyAttribute Include="Microsoft.Extensions.Logging.Testing.TestFrameworkFileLoggerAttribute">
|
||||
|
|
|
|||
|
|
@ -18,12 +18,6 @@ namespace Microsoft.Extensions.Logging.Testing.Tests
|
|||
private static readonly string ThisAssemblyName = ThisAssembly.GetName().Name;
|
||||
private static readonly string TFM = new DirectoryInfo(AppContext.BaseDirectory).Name;
|
||||
|
||||
[Fact]
|
||||
public void FullClassNameUsedWhenShortClassNameAttributeNotSpecified()
|
||||
{
|
||||
Assert.Equal(GetType().FullName, ResolvedTestClassName);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ForAssembly_ReturnsSameInstanceForSameAssembly()
|
||||
{
|
||||
|
|
@ -57,7 +51,7 @@ namespace Microsoft.Extensions.Logging.Testing.Tests
|
|||
}
|
||||
|
||||
[Fact]
|
||||
private Task TestLogEscapesIllegalFileNames() =>
|
||||
public Task TestLogEscapesIllegalFileNames() =>
|
||||
RunTestLogFunctionalTest((tempDir) =>
|
||||
{
|
||||
var illegalTestName = "T:e/s//t";
|
||||
|
|
|
|||
|
|
@ -1,43 +0,0 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using Xunit;
|
||||
|
||||
namespace Microsoft.Extensions.Logging.Testing.Tests
|
||||
{
|
||||
[Repeat]
|
||||
public class LoggedTestXunitRepeatTests : LoggedTest
|
||||
{
|
||||
public static int _runCount = 0;
|
||||
|
||||
[Fact]
|
||||
[Repeat(5)]
|
||||
public void RepeatLimitIsSetCorrectly()
|
||||
{
|
||||
Assert.Equal(5, RepeatContext.Limit);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
[Repeat(5)]
|
||||
public void RepeatRunsTestSpecifiedNumberOfTimes()
|
||||
{
|
||||
Assert.Equal(RepeatContext.CurrentIteration, _runCount);
|
||||
_runCount++;
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void RepeatCanBeSetOnClass()
|
||||
{
|
||||
Assert.Equal(10, RepeatContext.Limit);
|
||||
}
|
||||
}
|
||||
|
||||
public class LoggedTestXunitRepeatAssemblyTests : LoggedTest
|
||||
{
|
||||
[Fact]
|
||||
public void RepeatCanBeSetOnAssembly()
|
||||
{
|
||||
Assert.Equal(1, RepeatContext.Limit);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -21,18 +21,6 @@ namespace Microsoft.Extensions.Logging.Testing.Tests
|
|||
_output = output;
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ShortClassNameUsedWhenShortClassNameAttributeSpecified()
|
||||
{
|
||||
Assert.Equal(GetType().Name, ResolvedTestClassName);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void LoggedTestTestOutputHelperSameInstanceAsInjectedConstructorArg()
|
||||
{
|
||||
Assert.Same(_output, TestOutputHelper);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void LoggedFactInitializesLoggedTestProperties()
|
||||
{
|
||||
|
|
@ -189,9 +177,9 @@ namespace Microsoft.Extensions.Logging.Testing.Tests
|
|||
public bool SetupInvoked { get; private set; } = false;
|
||||
public bool ITestOutputHelperIsInitialized { get; private set; } = false;
|
||||
|
||||
public override void Initialize(MethodInfo methodInfo, object[] testMethodArguments, ITestOutputHelper testOutputHelper)
|
||||
public override void Initialize(TestContext context, MethodInfo methodInfo, object[] testMethodArguments, ITestOutputHelper testOutputHelper)
|
||||
{
|
||||
base.Initialize(methodInfo, testMethodArguments, testOutputHelper);
|
||||
base.Initialize(context, methodInfo, testMethodArguments, testOutputHelper);
|
||||
|
||||
try
|
||||
{
|
||||
|
|
|
|||
|
|
@ -2,4 +2,3 @@ using Microsoft.Extensions.Logging;
|
|||
using Microsoft.Extensions.Logging.Testing;
|
||||
|
||||
[assembly: LogLevel(LogLevel.Trace)]
|
||||
[assembly: Repeat(1)]
|
||||
|
|
|
|||
Loading…
Reference in New Issue