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() {