diff --git a/eng/Version.Details.xml b/eng/Version.Details.xml
index 4de6e29439..4cb798c0aa 100644
--- a/eng/Version.Details.xml
+++ b/eng/Version.Details.xml
@@ -13,21 +13,21 @@
https://github.com/dotnet/blazor
dd7fb4d3931d556458f62642c2edfc59f6295bfb
-
+
https://github.com/dotnet/aspnetcore-tooling
- 89579a87b9e8701a79e3584fa8bfd4e4efedcc7e
+ cc921d78edc850e3214917274d117bcc1450884b
-
+
https://github.com/dotnet/aspnetcore-tooling
- 89579a87b9e8701a79e3584fa8bfd4e4efedcc7e
+ cc921d78edc850e3214917274d117bcc1450884b
-
+
https://github.com/dotnet/aspnetcore-tooling
- 89579a87b9e8701a79e3584fa8bfd4e4efedcc7e
+ cc921d78edc850e3214917274d117bcc1450884b
-
+
https://github.com/dotnet/aspnetcore-tooling
- 89579a87b9e8701a79e3584fa8bfd4e4efedcc7e
+ cc921d78edc850e3214917274d117bcc1450884b
https://github.com/dotnet/efcore
@@ -57,165 +57,169 @@
https://github.com/dotnet/efcore
f1a6840fbc5c72a6cf2756dbdbcc65622434ebd7
-
+
https://github.com/dotnet/extensions
- 9a8d035b5f1c7264d8537d73a08cb812238699f1
+ 540b4e8f129a132749a60174464b8c8274bfed7d
-
+
https://github.com/dotnet/extensions
- 9a8d035b5f1c7264d8537d73a08cb812238699f1
+ 540b4e8f129a132749a60174464b8c8274bfed7d
-
+
https://github.com/dotnet/extensions
- 9a8d035b5f1c7264d8537d73a08cb812238699f1
+ 540b4e8f129a132749a60174464b8c8274bfed7d
-
+
https://github.com/dotnet/extensions
- 9a8d035b5f1c7264d8537d73a08cb812238699f1
+ 540b4e8f129a132749a60174464b8c8274bfed7d
-
+
https://github.com/dotnet/extensions
- 9a8d035b5f1c7264d8537d73a08cb812238699f1
+ 540b4e8f129a132749a60174464b8c8274bfed7d
-
+
https://github.com/dotnet/extensions
- 9a8d035b5f1c7264d8537d73a08cb812238699f1
+ 540b4e8f129a132749a60174464b8c8274bfed7d
-
+
https://github.com/dotnet/extensions
- 9a8d035b5f1c7264d8537d73a08cb812238699f1
+ 540b4e8f129a132749a60174464b8c8274bfed7d
-
+
https://github.com/dotnet/extensions
- 9a8d035b5f1c7264d8537d73a08cb812238699f1
+ 540b4e8f129a132749a60174464b8c8274bfed7d
-
+
https://github.com/dotnet/extensions
- 9a8d035b5f1c7264d8537d73a08cb812238699f1
+ 540b4e8f129a132749a60174464b8c8274bfed7d
-
+
https://github.com/dotnet/extensions
- 9a8d035b5f1c7264d8537d73a08cb812238699f1
+ 540b4e8f129a132749a60174464b8c8274bfed7d
-
+
https://github.com/dotnet/extensions
- 9a8d035b5f1c7264d8537d73a08cb812238699f1
+ 540b4e8f129a132749a60174464b8c8274bfed7d
-
+
https://github.com/dotnet/extensions
- 9a8d035b5f1c7264d8537d73a08cb812238699f1
+ 540b4e8f129a132749a60174464b8c8274bfed7d
-
+
https://github.com/dotnet/extensions
- 9a8d035b5f1c7264d8537d73a08cb812238699f1
+ 540b4e8f129a132749a60174464b8c8274bfed7d
-
+
https://github.com/dotnet/extensions
- 9a8d035b5f1c7264d8537d73a08cb812238699f1
+ 540b4e8f129a132749a60174464b8c8274bfed7d
-
+
https://github.com/dotnet/extensions
- 9a8d035b5f1c7264d8537d73a08cb812238699f1
+ 540b4e8f129a132749a60174464b8c8274bfed7d
-
+
https://github.com/dotnet/extensions
- 9a8d035b5f1c7264d8537d73a08cb812238699f1
+ 540b4e8f129a132749a60174464b8c8274bfed7d
-
+
https://github.com/dotnet/extensions
- 9a8d035b5f1c7264d8537d73a08cb812238699f1
+ 540b4e8f129a132749a60174464b8c8274bfed7d
-
+
https://github.com/dotnet/extensions
- 9a8d035b5f1c7264d8537d73a08cb812238699f1
+ 540b4e8f129a132749a60174464b8c8274bfed7d
-
+
https://github.com/dotnet/extensions
- 9a8d035b5f1c7264d8537d73a08cb812238699f1
+ 540b4e8f129a132749a60174464b8c8274bfed7d
-
+
https://github.com/dotnet/extensions
- 9a8d035b5f1c7264d8537d73a08cb812238699f1
+ 540b4e8f129a132749a60174464b8c8274bfed7d
-
+
https://github.com/dotnet/extensions
- 9a8d035b5f1c7264d8537d73a08cb812238699f1
+ 540b4e8f129a132749a60174464b8c8274bfed7d
-
+
https://github.com/dotnet/extensions
- 9a8d035b5f1c7264d8537d73a08cb812238699f1
+ 540b4e8f129a132749a60174464b8c8274bfed7d
-
+
https://github.com/dotnet/extensions
- 9a8d035b5f1c7264d8537d73a08cb812238699f1
+ 540b4e8f129a132749a60174464b8c8274bfed7d
-
+
https://github.com/dotnet/extensions
- 9a8d035b5f1c7264d8537d73a08cb812238699f1
+ 540b4e8f129a132749a60174464b8c8274bfed7d
-
+
https://github.com/dotnet/extensions
- 9a8d035b5f1c7264d8537d73a08cb812238699f1
+ 540b4e8f129a132749a60174464b8c8274bfed7d
-
+
https://github.com/dotnet/extensions
- 9a8d035b5f1c7264d8537d73a08cb812238699f1
+ 540b4e8f129a132749a60174464b8c8274bfed7d
-
+
https://github.com/dotnet/extensions
- 9a8d035b5f1c7264d8537d73a08cb812238699f1
+ 540b4e8f129a132749a60174464b8c8274bfed7d
-
+
https://github.com/dotnet/extensions
- 9a8d035b5f1c7264d8537d73a08cb812238699f1
+ 540b4e8f129a132749a60174464b8c8274bfed7d
-
+
https://github.com/dotnet/extensions
- 9a8d035b5f1c7264d8537d73a08cb812238699f1
+ 540b4e8f129a132749a60174464b8c8274bfed7d
-
+
https://github.com/dotnet/extensions
- 9a8d035b5f1c7264d8537d73a08cb812238699f1
+ 540b4e8f129a132749a60174464b8c8274bfed7d
-
+
https://github.com/dotnet/extensions
- 9a8d035b5f1c7264d8537d73a08cb812238699f1
+ 540b4e8f129a132749a60174464b8c8274bfed7d
-
+
https://github.com/dotnet/extensions
- 9a8d035b5f1c7264d8537d73a08cb812238699f1
+ 540b4e8f129a132749a60174464b8c8274bfed7d
-
+
https://github.com/dotnet/extensions
- 9a8d035b5f1c7264d8537d73a08cb812238699f1
+ 540b4e8f129a132749a60174464b8c8274bfed7d
-
+
https://github.com/dotnet/extensions
- 9a8d035b5f1c7264d8537d73a08cb812238699f1
+ 540b4e8f129a132749a60174464b8c8274bfed7d
-
+
https://github.com/dotnet/extensions
- 9a8d035b5f1c7264d8537d73a08cb812238699f1
+ 540b4e8f129a132749a60174464b8c8274bfed7d
-
+
https://github.com/dotnet/extensions
- 9a8d035b5f1c7264d8537d73a08cb812238699f1
+ 540b4e8f129a132749a60174464b8c8274bfed7d
-
+
https://github.com/dotnet/extensions
- 9a8d035b5f1c7264d8537d73a08cb812238699f1
+ 540b4e8f129a132749a60174464b8c8274bfed7d
-
+
https://github.com/dotnet/extensions
- 9a8d035b5f1c7264d8537d73a08cb812238699f1
+ 540b4e8f129a132749a60174464b8c8274bfed7d
-
+
https://github.com/dotnet/extensions
- 9a8d035b5f1c7264d8537d73a08cb812238699f1
+ 540b4e8f129a132749a60174464b8c8274bfed7d
-
+
https://github.com/dotnet/extensions
- 9a8d035b5f1c7264d8537d73a08cb812238699f1
+ 540b4e8f129a132749a60174464b8c8274bfed7d
+
+
+ https://github.com/dotnet/extensions
+ 540b4e8f129a132749a60174464b8c8274bfed7d
https://github.com/dotnet/runtime
@@ -328,9 +332,9 @@
https://github.com/dotnet/runtime
2f6e072c97738a56eeeb25f1fdb5dcc955649db0
-
+
https://github.com/dotnet/extensions
- 9a8d035b5f1c7264d8537d73a08cb812238699f1
+ 540b4e8f129a132749a60174464b8c8274bfed7d
https://github.com/dotnet/arcade
diff --git a/eng/Versions.props b/eng/Versions.props
index 179f752bcc..871b589673 100644
--- a/eng/Versions.props
+++ b/eng/Versions.props
@@ -97,47 +97,48 @@
3.2.0-preview1.20067.1
- 5.0.0-preview.3.20156.2
- 5.0.0-preview.3.20156.2
- 5.0.0-preview.3.20156.2
- 5.0.0-preview.3.20156.2
- 5.0.0-preview.3.20156.2
- 5.0.0-preview.3.20156.2
- 5.0.0-preview.3.20156.2
- 5.0.0-preview.3.20156.2
- 5.0.0-preview.3.20156.2
- 5.0.0-preview.3.20156.2
- 5.0.0-preview.3.20156.2
- 5.0.0-preview.3.20156.2
- 5.0.0-preview.3.20156.2
- 5.0.0-preview.3.20156.2
- 5.0.0-preview.3.20156.2
- 5.0.0-preview.3.20156.2
- 5.0.0-preview.3.20156.2
- 5.0.0-preview.3.20156.2
- 5.0.0-preview.3.20156.2
- 5.0.0-preview.3.20156.2
- 5.0.0-preview.3.20156.2
- 5.0.0-preview.3.20156.2
- 5.0.0-preview.3.20156.2
- 5.0.0-preview.3.20156.2
- 5.0.0-preview.3.20156.2
- 5.0.0-preview.3.20156.2
- 5.0.0-preview.3.20156.2
- 5.0.0-preview.3.20156.2
- 5.0.0-preview.3.20156.2
- 5.0.0-preview.3.20156.2
- 5.0.0-preview.3.20156.2
- 5.0.0-preview.3.20156.2
- 5.0.0-preview.3.20156.2
- 5.0.0-preview.3.20156.2
- 5.0.0-preview.3.20156.2
- 5.0.0-preview.3.20156.2
- 5.0.0-preview.3.20156.2
- 5.0.0-preview.3.20156.2
- 5.0.0-preview.3.20156.2
- 5.0.0-preview.3.20156.2
- 5.0.0-preview.3.20156.2
+ 5.0.0-preview.3.20156.3
+ 5.0.0-preview.3.20156.3
+ 5.0.0-preview.3.20156.3
+ 5.0.0-preview.3.20156.3
+ 5.0.0-preview.3.20156.3
+ 5.0.0-preview.3.20156.3
+ 5.0.0-preview.3.20156.3
+ 5.0.0-preview.3.20156.3
+ 5.0.0-preview.3.20156.3
+ 5.0.0-preview.3.20156.3
+ 5.0.0-preview.3.20156.3
+ 5.0.0-preview.3.20156.3
+ 5.0.0-preview.3.20156.3
+ 5.0.0-preview.3.20156.3
+ 5.0.0-preview.3.20156.3
+ 5.0.0-preview.3.20156.3
+ 5.0.0-preview.3.20156.3
+ 5.0.0-preview.3.20156.3
+ 5.0.0-preview.3.20156.3
+ 5.0.0-preview.3.20156.3
+ 5.0.0-preview.3.20156.3
+ 5.0.0-preview.3.20156.3
+ 5.0.0-preview.3.20156.3
+ 5.0.0-preview.3.20156.3
+ 5.0.0-preview.3.20156.3
+ 5.0.0-preview.3.20156.3
+ 5.0.0-preview.3.20156.3
+ 5.0.0-preview.3.20156.3
+ 5.0.0-preview.3.20156.3
+ 5.0.0-preview.3.20156.3
+ 5.0.0-preview.3.20156.3
+ 5.0.0-preview.3.20156.3
+ 5.0.0-preview.3.20156.3
+ 5.0.0-preview.3.20156.3
+ 5.0.0-preview.3.20156.3
+ 5.0.0-preview.3.20156.3
+ 5.0.0-preview.3.20156.3
+ 5.0.0-preview.3.20156.3
+ 5.0.0-preview.3.20156.3
+ 5.0.0-preview.3.20156.3
+ 5.0.0-preview.3.20156.3
+ 5.0.0-preview.3.20156.3
5.0.0-preview.3.20156.3
5.0.0-preview.3.20156.3
@@ -147,10 +148,10 @@
5.0.0-preview.3.20156.3
5.0.0-preview.3.20156.3
- 5.0.0-preview.3.20156.7
- 5.0.0-preview.3.20156.7
- 5.0.0-preview.3.20156.7
- 5.0.0-preview.3.20156.7
+ 5.0.0-preview.3.20157.1
+ 5.0.0-preview.3.20157.1
+ 5.0.0-preview.3.20157.1
+ 5.0.0-preview.3.20157.1
+ $(RepositoryRoot)
+ $(ASPNETCORE_TEST_LOG_DIR)
+ $(RepoRoot)artifacts\log\
+
+
+
+
+ true
+ false
+
+
+
+
+ <_Parameter1>Microsoft.AspNetCore.Testing.AspNetTestFramework
+ <_Parameter2>Microsoft.AspNetCore.Testing
+
+
+
+ <_Parameter1>$(PreserveExistingLogsInOutput)
+ <_Parameter2>$(TargetFramework)
+ <_Parameter3 Condition="'$(LoggingTestingDisableFileLogging)' != 'true'">$(LoggingTestingFileLoggingDirectory)
+
+
+
+
diff --git a/src/Testing/test/AssemblyTestLogTests.cs b/src/Testing/test/AssemblyTestLogTests.cs
new file mode 100644
index 0000000000..3db8ffc0ce
--- /dev/null
+++ b/src/Testing/test/AssemblyTestLogTests.cs
@@ -0,0 +1,221 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System;
+using System.IO;
+using System.Linq;
+using System.Reflection;
+using System.Runtime.CompilerServices;
+using System.Text.RegularExpressions;
+using System.Threading.Tasks;
+using Microsoft.AspNetCore.Testing;
+using Xunit;
+
+namespace Microsoft.Extensions.Logging.Testing.Tests
+{
+ public class AssemblyTestLogTests : LoggedTest
+ {
+ private static readonly Assembly ThisAssembly = typeof(AssemblyTestLogTests).GetTypeInfo().Assembly;
+ private static readonly string ThisAssemblyName = ThisAssembly.GetName().Name;
+ private static readonly string TFM = ThisAssembly.GetCustomAttributes().OfType().FirstOrDefault().TargetFramework;
+
+ [Fact]
+ public void FunctionalLogs_LogsPreservedFromNonQuarantinedTest()
+ {
+ }
+
+ [Fact]
+ [QuarantinedTest]
+ public void FunctionalLogs_LogsPreservedFromQuarantinedTest()
+ {
+ }
+
+ [Fact]
+ public void ForAssembly_ReturnsSameInstanceForSameAssembly()
+ {
+ Assert.Same(
+ AssemblyTestLog.ForAssembly(ThisAssembly),
+ AssemblyTestLog.ForAssembly(ThisAssembly));
+ }
+
+ [Fact]
+ public void TestLogWritesToITestOutputHelper()
+ {
+ var output = new TestTestOutputHelper();
+ var assemblyLog = AssemblyTestLog.Create(ThisAssemblyName, baseDirectory: null);
+
+ using (assemblyLog.StartTestLog(output, "NonExistant.Test.Class", out var loggerFactory))
+ {
+ var logger = loggerFactory.CreateLogger("TestLogger");
+ logger.LogInformation("Information!");
+
+ // Trace is disabled by default
+ logger.LogTrace("Trace!");
+ }
+
+ var testLogContent = MakeConsistent(output.Output);
+
+ Assert.Equal(
+@"[OFFSET] TestLifetime Information: Starting test TestLogWritesToITestOutputHelper at TIMESTAMP
+[OFFSET] TestLogger Information: Information!
+[OFFSET] TestLifetime Information: Finished test TestLogWritesToITestOutputHelper in DURATION
+", testLogContent, ignoreLineEndingDifferences: true);
+ }
+
+ [Fact]
+ public Task TestLogEscapesIllegalFileNames() =>
+ RunTestLogFunctionalTest((tempDir) =>
+ {
+ var illegalTestName = "T:e/s//t";
+ var escapedTestName = "T_e_s_t";
+ using (var testAssemblyLog = AssemblyTestLog.Create(ThisAssemblyName, baseDirectory: tempDir))
+ using (testAssemblyLog.StartTestLog(output: null, className: "FakeTestAssembly.FakeTestClass", loggerFactory: out var testLoggerFactory, minLogLevel: LogLevel.Trace, resolvedTestName: out var resolvedTestname, out var _, testName: illegalTestName))
+ {
+ Assert.Equal(escapedTestName, resolvedTestname);
+ }
+ });
+
+ [Fact]
+ public Task TestLogWritesToGlobalLogFile() =>
+ RunTestLogFunctionalTest((tempDir) =>
+ {
+ // Because this test writes to a file, it is a functional test and should be logged
+ // but it's also testing the test logging facility. So this is pretty meta ;)
+ var logger = LoggerFactory.CreateLogger("Test");
+
+ using (var testAssemblyLog = AssemblyTestLog.Create(ThisAssemblyName, tempDir))
+ {
+ logger.LogInformation("Created test log in {baseDirectory}", tempDir);
+
+ using (testAssemblyLog.StartTestLog(output: null, className: $"{ThisAssemblyName}.FakeTestClass", loggerFactory: out var testLoggerFactory, minLogLevel: LogLevel.Trace, testName: "FakeTestName"))
+ {
+ var testLogger = testLoggerFactory.CreateLogger("TestLogger");
+ testLogger.LogInformation("Information!");
+ testLogger.LogTrace("Trace!");
+ }
+ }
+
+ logger.LogInformation("Finished test log in {baseDirectory}", tempDir);
+
+ var globalLogPath = Path.Combine(tempDir, ThisAssemblyName, TFM, "global.log");
+ var testLog = Path.Combine(tempDir, ThisAssemblyName, TFM, "FakeTestClass", "FakeTestName.log");
+
+ Assert.True(File.Exists(globalLogPath), $"Expected global log file {globalLogPath} to exist");
+ Assert.True(File.Exists(testLog), $"Expected test log file {testLog} to exist");
+
+ var globalLogContent = MakeConsistent(File.ReadAllText(globalLogPath));
+ var testLogContent = MakeConsistent(File.ReadAllText(testLog));
+
+ Assert.Equal(
+@"[OFFSET] [GlobalTestLog] [Information] Global Test Logging initialized at TIMESTAMP. Configure the output directory via 'LoggingTestingFileLoggingDirectory' MSBuild property or set 'LoggingTestingDisableFileLogging' to 'true' to disable file logging.
+[OFFSET] [GlobalTestLog] [Information] Starting test FakeTestName
+[OFFSET] [GlobalTestLog] [Information] Finished test FakeTestName in DURATION
+", globalLogContent, ignoreLineEndingDifferences: true);
+ Assert.Equal(
+@"[OFFSET] [TestLifetime] [Information] Starting test FakeTestName at TIMESTAMP
+[OFFSET] [TestLogger] [Information] Information!
+[OFFSET] [TestLogger] [Verbose] Trace!
+[OFFSET] [TestLifetime] [Information] Finished test FakeTestName in DURATION
+", testLogContent, ignoreLineEndingDifferences: true);
+ });
+
+ [Fact]
+ public Task TestLogTruncatesTestNameToAvoidLongPaths() =>
+ RunTestLogFunctionalTest((tempDir) =>
+ {
+ var longTestName = new string('0', 50) + new string('1', 50) + new string('2', 50) + new string('3', 50) + new string('4', 50);
+ var logger = LoggerFactory.CreateLogger("Test");
+ using (var testAssemblyLog = AssemblyTestLog.Create(ThisAssemblyName, tempDir))
+ {
+ logger.LogInformation("Created test log in {baseDirectory}", tempDir);
+
+ using (testAssemblyLog.StartTestLog(output: null, className: $"{ThisAssemblyName}.FakeTestClass", loggerFactory: out var testLoggerFactory, minLogLevel: LogLevel.Trace, testName: longTestName))
+ {
+ testLoggerFactory.CreateLogger("TestLogger").LogInformation("Information!");
+ }
+ }
+ logger.LogInformation("Finished test log in {baseDirectory}", tempDir);
+
+ var testLogFiles = new DirectoryInfo(Path.Combine(tempDir, ThisAssemblyName, TFM, "FakeTestClass")).EnumerateFiles();
+ var testLog = Assert.Single(testLogFiles);
+ var testFileName = Path.GetFileNameWithoutExtension(testLog.Name);
+
+ // The first half of the file comes from the beginning of the test name passed to the logger
+ Assert.Equal(longTestName.Substring(0, testFileName.Length / 2), testFileName.Substring(0, testFileName.Length / 2));
+ // The last half of the file comes from the ending of the test name passed to the logger
+ Assert.Equal(longTestName.Substring(longTestName.Length - testFileName.Length / 2, testFileName.Length / 2), testFileName.Substring(testFileName.Length - testFileName.Length / 2, testFileName.Length / 2));
+ });
+
+ [Fact]
+ public Task TestLogEnumerateFilenamesToAvoidCollisions() =>
+ RunTestLogFunctionalTest((tempDir) =>
+ {
+ var logger = LoggerFactory.CreateLogger("Test");
+ using (var testAssemblyLog = AssemblyTestLog.Create(ThisAssemblyName, tempDir))
+ {
+ logger.LogInformation("Created test log in {baseDirectory}", tempDir);
+
+ for (var i = 0; i < 10; i++)
+ {
+ using (testAssemblyLog.StartTestLog(output: null, className: $"{ThisAssemblyName}.FakeTestClass", loggerFactory: out var testLoggerFactory, minLogLevel: LogLevel.Trace, testName: "FakeTestName"))
+ {
+ testLoggerFactory.CreateLogger("TestLogger").LogInformation("Information!");
+ }
+ }
+ }
+ logger.LogInformation("Finished test log in {baseDirectory}", tempDir);
+
+ // The first log file exists
+ Assert.True(File.Exists(Path.Combine(tempDir, ThisAssemblyName, TFM, "FakeTestClass", "FakeTestName.log")));
+
+ // Subsequent files exist
+ for (var i = 0; i < 9; i++)
+ {
+ Assert.True(File.Exists(Path.Combine(tempDir, ThisAssemblyName, TFM, "FakeTestClass", $"FakeTestName.{i}.log")));
+ }
+ });
+
+ private static readonly Regex TimestampRegex = new Regex(@"\d+-\d+-\d+T\d+:\d+:\d+");
+ private static readonly Regex TimestampOffsetRegex = new Regex(@"\d+\.\d+s");
+ private static readonly Regex DurationRegex = new Regex(@"[^ ]+s$");
+
+ private async Task RunTestLogFunctionalTest(Action action, [CallerMemberName] string testName = null)
+ {
+ var tempDir = Path.Combine(Path.GetTempPath(), $"TestLogging_{Guid.NewGuid().ToString("N")}");
+ try
+ {
+ action(tempDir);
+ }
+ finally
+ {
+ if (Directory.Exists(tempDir))
+ {
+ try
+ {
+ Directory.Delete(tempDir, recursive: true);
+ }
+ catch
+ {
+ await Task.Delay(100);
+ Directory.Delete(tempDir, recursive: true);
+ }
+ }
+ }
+ }
+
+ private static string MakeConsistent(string input)
+ {
+ return string.Join(Environment.NewLine, input.Split(new[] { Environment.NewLine }, StringSplitOptions.None)
+ .Select(line =>
+ {
+ var strippedPrefix = line.IndexOf("[") >= 0 ? line.Substring(line.IndexOf("[")) : line;
+
+ var strippedDuration = DurationRegex.Replace(strippedPrefix, "DURATION");
+ var strippedTimestamp = TimestampRegex.Replace(strippedDuration, "TIMESTAMP");
+ var strippedTimestampOffset = TimestampOffsetRegex.Replace(strippedTimestamp, "OFFSET");
+ return strippedTimestampOffset;
+ }));
+ }
+ }
+}
diff --git a/src/Testing/test/LoggedTestXunitTests.cs b/src/Testing/test/LoggedTestXunitTests.cs
new file mode 100644
index 0000000000..61d7802508
--- /dev/null
+++ b/src/Testing/test/LoggedTestXunitTests.cs
@@ -0,0 +1,193 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System.Linq;
+using System.Reflection;
+using Microsoft.AspNetCore.Testing;
+using Microsoft.Extensions.DependencyInjection;
+using Xunit;
+using Xunit.Abstractions;
+
+namespace Microsoft.Extensions.Logging.Testing.Tests
+{
+ [LogLevel(LogLevel.Debug)]
+ [ShortClassName]
+ public class LoggedTestXunitTests : TestLoggedTest
+ {
+ private readonly ITestOutputHelper _output;
+
+ public LoggedTestXunitTests(ITestOutputHelper output)
+ {
+ _output = output;
+ }
+
+ [Fact]
+ public void LoggedFactInitializesLoggedTestProperties()
+ {
+ Assert.NotNull(Logger);
+ Assert.NotNull(LoggerFactory);
+ Assert.NotNull(TestSink);
+ Assert.NotNull(TestOutputHelper);
+ }
+
+ [Theory]
+ [InlineData("Hello world")]
+ public void LoggedTheoryInitializesLoggedTestProperties(string argument)
+ {
+ Assert.NotNull(Logger);
+ Assert.NotNull(LoggerFactory);
+ Assert.NotNull(TestSink);
+ Assert.NotNull(TestOutputHelper);
+ // Use the test argument
+ Assert.NotNull(argument);
+ }
+
+ [ConditionalFact]
+ public void ConditionalLoggedFactGetsInitializedLoggerFactory()
+ {
+ Assert.NotNull(Logger);
+ Assert.NotNull(LoggerFactory);
+ Assert.NotNull(TestSink);
+ Assert.NotNull(TestOutputHelper);
+ }
+
+ [ConditionalTheory]
+ [InlineData("Hello world")]
+ public void LoggedConditionalTheoryInitializesLoggedTestProperties(string argument)
+ {
+ Assert.NotNull(Logger);
+ Assert.NotNull(LoggerFactory);
+ Assert.NotNull(TestSink);
+ Assert.NotNull(TestOutputHelper);
+ // Use the test argument
+ Assert.NotNull(argument);
+ }
+
+ [Fact]
+ [LogLevel(LogLevel.Information)]
+ public void LoggedFactFilteredByMethodLogLevel()
+ {
+ Logger.LogInformation("Information");
+ Logger.LogDebug("Debug");
+
+ var message = Assert.Single(TestSink.Writes);
+ Assert.Equal(LogLevel.Information, message.LogLevel);
+ Assert.Equal("Information", message.Formatter(message.State, null));
+ }
+
+ [Fact]
+ public void LoggedFactFilteredByClassLogLevel()
+ {
+ Logger.LogDebug("Debug");
+ Logger.LogTrace("Trace");
+
+ var message = Assert.Single(TestSink.Writes);
+ Assert.Equal(LogLevel.Debug, message.LogLevel);
+ Assert.Equal("Debug", message.Formatter(message.State, null));
+ }
+
+ [Theory]
+ [InlineData("Hello world")]
+ [LogLevel(LogLevel.Information)]
+ public void LoggedTheoryFilteredByLogLevel(string argument)
+ {
+ Logger.LogInformation("Information");
+ Logger.LogDebug("Debug");
+
+ var message = Assert.Single(TestSink.Writes);
+ Assert.Equal(LogLevel.Information, message.LogLevel);
+ Assert.Equal("Information", message.Formatter(message.State, null));
+
+ // Use the test argument
+ Assert.NotNull(argument);
+ }
+
+ [Fact]
+ public void AddTestLoggingUpdatedWhenLoggerFactoryIsSet()
+ {
+ var loggerFactory = new LoggerFactory();
+ var serviceCollection = new ServiceCollection();
+
+ LoggerFactory = loggerFactory;
+ AddTestLogging(serviceCollection);
+
+ Assert.Same(loggerFactory, serviceCollection.BuildServiceProvider().GetRequiredService());
+ }
+
+ [ConditionalTheory]
+ [EnvironmentVariableSkipCondition("ASPNETCORE_TEST_LOG_DIR", "")] // The test name is only generated when logging is enabled via the environment variable
+ [InlineData(null)]
+ public void LoggedTheoryNullArgumentsAreEscaped(string argument)
+ {
+ Assert.NotNull(LoggerFactory);
+ Assert.Equal($"{nameof(LoggedTheoryNullArgumentsAreEscaped)}_null", ResolvedTestMethodName);
+ // Use the test argument
+ Assert.Null(argument);
+ }
+
+ [Fact]
+ public void AdditionalSetupInvoked()
+ {
+ Assert.True(SetupInvoked);
+ }
+
+ [Fact]
+ public void MessageWrittenEventInvoked()
+ {
+ WriteContext context = null;
+ TestSink.MessageLogged += ctx => context = ctx;
+ Logger.LogInformation("Information");
+ Assert.Equal(TestSink.Writes.Single(), context);
+ }
+
+ [Fact]
+ public void ScopeStartedEventInvoked()
+ {
+ BeginScopeContext context = null;
+ TestSink.ScopeStarted += ctx => context = ctx;
+ using (Logger.BeginScope("Scope")) {}
+ Assert.Equal(TestSink.Scopes.Single(), context);
+ }
+ }
+
+ public class LoggedTestXunitLogLevelTests : LoggedTest
+ {
+ [Fact]
+ public void LoggedFactFilteredByAssemblyLogLevel()
+ {
+ Logger.LogTrace("Trace");
+
+ var message = Assert.Single(TestSink.Writes);
+ Assert.Equal(LogLevel.Trace, message.LogLevel);
+ Assert.Equal("Trace", message.Formatter(message.State, null));
+ }
+ }
+
+ public class LoggedTestXunitInitializationTests : TestLoggedTest
+ {
+ [Fact]
+ public void ITestOutputHelperInitializedByDefault()
+ {
+ Assert.True(ITestOutputHelperIsInitialized);
+ }
+ }
+
+ public class TestLoggedTest : LoggedTest
+ {
+ public bool SetupInvoked { get; private set; } = false;
+ public bool ITestOutputHelperIsInitialized { get; private set; } = false;
+
+ public override void Initialize(TestContext context, MethodInfo methodInfo, object[] testMethodArguments, ITestOutputHelper testOutputHelper)
+ {
+ base.Initialize(context, methodInfo, testMethodArguments, testOutputHelper);
+
+ try
+ {
+ TestOutputHelper.WriteLine("Test");
+ ITestOutputHelperIsInitialized = true;
+ } catch { }
+ SetupInvoked = true;
+ }
+ }
+}
diff --git a/src/Testing/test/Microsoft.AspNetCore.Testing.Tests.csproj b/src/Testing/test/Microsoft.AspNetCore.Testing.Tests.csproj
index 5a7366503d..a203e1289d 100644
--- a/src/Testing/test/Microsoft.AspNetCore.Testing.Tests.csproj
+++ b/src/Testing/test/Microsoft.AspNetCore.Testing.Tests.csproj
@@ -1,5 +1,4 @@
-
$(DefaultNetCoreTargetFramework);net472
@@ -18,7 +17,7 @@
-
+
diff --git a/src/Testing/test/Properties/AssemblyInfo.cs b/src/Testing/test/Properties/AssemblyInfo.cs
index d585b5ed95..417cd2d3fd 100644
--- a/src/Testing/test/Properties/AssemblyInfo.cs
+++ b/src/Testing/test/Properties/AssemblyInfo.cs
@@ -1,6 +1,8 @@
using Microsoft.AspNetCore.Testing;
+using Microsoft.Extensions.Logging;
+using Microsoft.Extensions.Logging.Testing;
using Xunit;
[assembly: Repeat(1)]
+[assembly: LogLevel(LogLevel.Trace)]
[assembly: AssemblyFixture(typeof(TestAssemblyFixture))]
-[assembly: TestFramework("Microsoft.AspNetCore.Testing.AspNetTestFramework", "Microsoft.AspNetCore.Testing")]
diff --git a/src/Testing/test/TestTestOutputHelper.cs b/src/Testing/test/TestTestOutputHelper.cs
new file mode 100644
index 0000000000..5a5f6aa85f
--- /dev/null
+++ b/src/Testing/test/TestTestOutputHelper.cs
@@ -0,0 +1,37 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System;
+using System.Text;
+using Xunit.Abstractions;
+
+namespace Microsoft.Extensions.Logging.Testing.Tests
+{
+ public class TestTestOutputHelper : ITestOutputHelper
+ {
+ private StringBuilder _output = new StringBuilder();
+
+ public bool Throw { get; set; }
+
+ public string Output => _output.ToString();
+
+ public void WriteLine(string message)
+ {
+ if (Throw)
+ {
+ throw new Exception("Boom!");
+ }
+ _output.AppendLine(message);
+ }
+
+ public void WriteLine(string format, params object[] args)
+ {
+ if (Throw)
+ {
+ throw new Exception("Boom!");
+ }
+ _output.AppendLine(string.Format(format, args));
+ }
+ }
+}
diff --git a/src/Tools/Microsoft.dotnet-openapi/test/OpenApiAddURLTests.cs b/src/Tools/Microsoft.dotnet-openapi/test/OpenApiAddURLTests.cs
index 0336cfdef2..d5fa7c9f50 100644
--- a/src/Tools/Microsoft.dotnet-openapi/test/OpenApiAddURLTests.cs
+++ b/src/Tools/Microsoft.dotnet-openapi/test/OpenApiAddURLTests.cs
@@ -17,7 +17,6 @@ namespace Microsoft.DotNet.OpenApi.Add.Tests
public OpenApiAddURLTests(ITestOutputHelper output) : base(output){ }
[Fact]
- [QuarantinedTest]
public async Task OpenApi_Add_Url_WithContentDisposition()
{
var project = CreateBasicProject(withOpenApi: false);
@@ -50,7 +49,6 @@ namespace Microsoft.DotNet.OpenApi.Add.Tests
}
[Fact]
- [QuarantinedTest]
public async Task OpenAPI_Add_Url_NoContentDisposition()
{
var project = CreateBasicProject(withOpenApi: false);
@@ -84,7 +82,6 @@ namespace Microsoft.DotNet.OpenApi.Add.Tests
}
[Fact]
- [QuarantinedTest]
public async Task OpenAPI_Add_Url_NoExtension_AssumesJson()
{
var project = CreateBasicProject(withOpenApi: false);
@@ -118,7 +115,6 @@ namespace Microsoft.DotNet.OpenApi.Add.Tests
}
[Fact]
- [QuarantinedTest]
public async Task OpenApi_Add_Url_NoSegment()
{
var project = CreateBasicProject(withOpenApi: false);
@@ -152,7 +148,6 @@ namespace Microsoft.DotNet.OpenApi.Add.Tests
}
[Fact]
- [QuarantinedTest]
public async Task OpenApi_Add_Url()
{
var project = CreateBasicProject(withOpenApi: false);
@@ -185,7 +180,6 @@ namespace Microsoft.DotNet.OpenApi.Add.Tests
}
[Fact]
- [QuarantinedTest]
public async Task OpenApi_Add_Url_SameName_UniqueFile()
{
var project = CreateBasicProject(withOpenApi: false);
@@ -246,7 +240,6 @@ namespace Microsoft.DotNet.OpenApi.Add.Tests
}
[Fact]
- [QuarantinedTest]
public async Task OpenApi_Add_Url_NSwagCSharp()
{
var project = CreateBasicProject(withOpenApi: false);
@@ -279,7 +272,6 @@ namespace Microsoft.DotNet.OpenApi.Add.Tests
}
[Fact]
- [QuarantinedTest]
public async Task OpenApi_Add_Url_NSwagTypeScript()
{
var project = CreateBasicProject(withOpenApi: false);
@@ -312,7 +304,6 @@ namespace Microsoft.DotNet.OpenApi.Add.Tests
}
[Fact]
- [QuarantinedTest]
public async Task OpenApi_Add_Url_OutputFile()
{
var project = CreateBasicProject(withOpenApi: false);
@@ -345,7 +336,6 @@ namespace Microsoft.DotNet.OpenApi.Add.Tests
}
[Fact]
- [QuarantinedTest]
public async Task OpenApi_Add_URL_FileAlreadyExists_Fail()
{
var project = CreateBasicProject(withOpenApi: false);
@@ -404,7 +394,6 @@ namespace Microsoft.DotNet.OpenApi.Add.Tests
}
[Fact]
- [QuarantinedTest]
public void OpenApi_Add_URL_MultipleTimes_OnlyOneReference()
{
var project = CreateBasicProject(withOpenApi: false);
@@ -431,7 +420,6 @@ namespace Microsoft.DotNet.OpenApi.Add.Tests
}
[Fact]
- [QuarantinedTest]
public async Task OpenAPi_Add_URL_InvalidUrl()
{
var project = CreateBasicProject(withOpenApi: false);
@@ -459,7 +447,6 @@ namespace Microsoft.DotNet.OpenApi.Add.Tests
Assert.False(File.Exists(jsonFile));
}
- [QuarantinedTest]
[Fact]
public void OpenApi_Add_URL_ActualResponse()
{