Skip to content
This repository was archived by the owner on Dec 13, 2018. It is now read-only.

Commit 1b8e402

Browse files
author
John Luo
committed
Implement file logging integration with xunit
- Add filename handling for assembly test log
1 parent 4ba2478 commit 1b8e402

32 files changed

+979
-113
lines changed

.appveyor.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ environment:
1212
global:
1313
DOTNET_SKIP_FIRST_TIME_EXPERIENCE: true
1414
DOTNET_CLI_TELEMETRY_OPTOUT: 1
15+
ASPNETCORE_TEST_LOG_DIR: "$APPVEYOR_BUILD_FOLDER\\artifacts\\logs"
1516
test: 'off'
1617
deploy: 'off'
1718
os: Visual Studio 2017

.travis.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ env:
55
global:
66
- DOTNET_SKIP_FIRST_TIME_EXPERIENCE: true
77
- DOTNET_CLI_TELEMETRY_OPTOUT: 1
8+
- ASPNETCORE_TEST_LOG_DIR: "$APPVEYOR_BUILD_FOLDER\\artifacts\\logs"
89
mono: none
910
os:
1011
- linux

NuGetPackageVerifier.json

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,15 @@
22
"Default": {
33
"rules": [
44
"DefaultCompositeRule"
5-
]
5+
],
6+
"packages": {
7+
"Microsoft.Extensions.Logging.Testing": {
8+
"Exclusions": {
9+
"BUILD_ITEMS_FRAMEWORK": {
10+
"*": "Props file intentionally targets any framework since the content is the same for both netstandard2.0 and net461."
11+
}
12+
}
13+
}
14+
}
615
}
716
}

build/dependencies.props

Lines changed: 17 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -4,31 +4,32 @@
44
</PropertyGroup>
55
<PropertyGroup Label="Package Versions">
66
<InternalAspNetCoreSdkPackageVersion>2.1.0-preview3-17004</InternalAspNetCoreSdkPackageVersion>
7-
<MicrosoftAspNetCoreTestingPackageVersion>2.1.0-preview3-32176</MicrosoftAspNetCoreTestingPackageVersion>
7+
<MicrosoftAspNetCoreTestingPackageVersion>2.1.0-preview3-32192</MicrosoftAspNetCoreTestingPackageVersion>
88
<MicrosoftCodeAnalysisCSharpWorkspacesPackageVersion>2.6.1</MicrosoftCodeAnalysisCSharpWorkspacesPackageVersion>
9-
<MicrosoftExtensionsConfigurationAbstractionsPackageVersion>2.1.0-preview3-32176</MicrosoftExtensionsConfigurationAbstractionsPackageVersion>
10-
<MicrosoftExtensionsConfigurationBinderPackageVersion>2.1.0-preview3-32176</MicrosoftExtensionsConfigurationBinderPackageVersion>
11-
<MicrosoftExtensionsConfigurationEnvironmentVariablesPackageVersion>2.1.0-preview3-32176</MicrosoftExtensionsConfigurationEnvironmentVariablesPackageVersion>
12-
<MicrosoftExtensionsConfigurationFileExtensionsPackageVersion>2.1.0-preview3-32176</MicrosoftExtensionsConfigurationFileExtensionsPackageVersion>
13-
<MicrosoftExtensionsConfigurationJsonPackageVersion>2.1.0-preview3-32176</MicrosoftExtensionsConfigurationJsonPackageVersion>
14-
<MicrosoftExtensionsDependencyInjectionAbstractionsPackageVersion>2.1.0-preview3-32176</MicrosoftExtensionsDependencyInjectionAbstractionsPackageVersion>
15-
<MicrosoftExtensionsDependencyInjectionPackageVersion>2.1.0-preview3-32176</MicrosoftExtensionsDependencyInjectionPackageVersion>
16-
<MicrosoftExtensionsDependencyModelPackageVersion>2.1.0-preview2-26403-06</MicrosoftExtensionsDependencyModelPackageVersion>
17-
<MicrosoftExtensionsFileProvidersPhysicalPackageVersion>2.1.0-preview3-32176</MicrosoftExtensionsFileProvidersPhysicalPackageVersion>
18-
<MicrosoftExtensionsOptionsConfigurationExtensionsPackageVersion>2.1.0-preview3-32176</MicrosoftExtensionsOptionsConfigurationExtensionsPackageVersion>
19-
<MicrosoftExtensionsOptionsPackageVersion>2.1.0-preview3-32176</MicrosoftExtensionsOptionsPackageVersion>
9+
<MicrosoftExtensionsConfigurationAbstractionsPackageVersion>2.1.0-preview3-32192</MicrosoftExtensionsConfigurationAbstractionsPackageVersion>
10+
<MicrosoftExtensionsConfigurationBinderPackageVersion>2.1.0-preview3-32192</MicrosoftExtensionsConfigurationBinderPackageVersion>
11+
<MicrosoftExtensionsConfigurationEnvironmentVariablesPackageVersion>2.1.0-preview3-32192</MicrosoftExtensionsConfigurationEnvironmentVariablesPackageVersion>
12+
<MicrosoftExtensionsConfigurationFileExtensionsPackageVersion>2.1.0-preview3-32192</MicrosoftExtensionsConfigurationFileExtensionsPackageVersion>
13+
<MicrosoftExtensionsConfigurationJsonPackageVersion>2.1.0-preview3-32192</MicrosoftExtensionsConfigurationJsonPackageVersion>
14+
<MicrosoftExtensionsDependencyInjectionAbstractionsPackageVersion>2.1.0-preview3-32192</MicrosoftExtensionsDependencyInjectionAbstractionsPackageVersion>
15+
<MicrosoftExtensionsDependencyInjectionPackageVersion>2.1.0-preview3-32192</MicrosoftExtensionsDependencyInjectionPackageVersion>
16+
<MicrosoftExtensionsDependencyModelPackageVersion>2.1.0-preview2-26406-04</MicrosoftExtensionsDependencyModelPackageVersion>
17+
<MicrosoftExtensionsFileProvidersPhysicalPackageVersion>2.1.0-preview3-32192</MicrosoftExtensionsFileProvidersPhysicalPackageVersion>
18+
<MicrosoftExtensionsOptionsConfigurationExtensionsPackageVersion>2.1.0-preview3-32192</MicrosoftExtensionsOptionsConfigurationExtensionsPackageVersion>
19+
<MicrosoftExtensionsOptionsPackageVersion>2.1.0-preview3-32192</MicrosoftExtensionsOptionsPackageVersion>
2020
<MicrosoftNETCoreApp20PackageVersion>2.0.0</MicrosoftNETCoreApp20PackageVersion>
21-
<MicrosoftNETCoreApp21PackageVersion>2.1.0-preview2-26403-06</MicrosoftNETCoreApp21PackageVersion>
21+
<MicrosoftNETCoreApp21PackageVersion>2.1.0-preview2-26406-04</MicrosoftNETCoreApp21PackageVersion>
2222
<MicrosoftNETTestSdkPackageVersion>15.6.1</MicrosoftNETTestSdkPackageVersion>
2323
<MoqPackageVersion>4.7.49</MoqPackageVersion>
2424
<NewtonsoftJsonPackageVersion>11.0.2</NewtonsoftJsonPackageVersion>
2525
<SerilogExtensionsLoggingPackageVersion>1.4.0</SerilogExtensionsLoggingPackageVersion>
2626
<SerilogSinksFilePackageVersion>3.2.0</SerilogSinksFilePackageVersion>
27-
<SystemDiagnosticsEventLogPackageVersion>4.5.0-preview2-26403-05</SystemDiagnosticsEventLogPackageVersion>
28-
<SystemReflectionMetadataPackageVersion>1.6.0-preview2-26403-05</SystemReflectionMetadataPackageVersion>
29-
<SystemValueTuplePackageVersion>4.5.0-preview2-26403-05</SystemValueTuplePackageVersion>
27+
<SystemDiagnosticsEventLogPackageVersion>4.5.0-preview2-26406-04</SystemDiagnosticsEventLogPackageVersion>
28+
<SystemReflectionMetadataPackageVersion>1.6.0-preview2-26406-04</SystemReflectionMetadataPackageVersion>
29+
<SystemValueTuplePackageVersion>4.5.0-preview2-26406-04</SystemValueTuplePackageVersion>
3030
<XunitAbstractionsPackageVersion>2.0.1</XunitAbstractionsPackageVersion>
3131
<XunitAssertPackageVersion>2.3.1</XunitAssertPackageVersion>
32+
<XunitExtensibilityExecutionPackageVersion>2.3.1</XunitExtensibilityExecutionPackageVersion>
3233
<XunitPackageVersion>2.3.1</XunitPackageVersion>
3334
<XunitRunnerVisualStudioPackageVersion>2.4.0-beta.1.build3945</XunitRunnerVisualStudioPackageVersion>
3435
</PropertyGroup>

src/Microsoft.Extensions.Logging.Testing/AssemblyTestLog.cs

Lines changed: 33 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,11 @@
55
using System.Collections.Generic;
66
using System.Diagnostics;
77
using System.IO;
8+
using System.Linq;
89
using System.Reflection;
910
using System.Runtime.CompilerServices;
1011
using System.Runtime.InteropServices;
12+
using System.Text;
1113
using Microsoft.Extensions.DependencyInjection;
1214
using Serilog;
1315
using Serilog.Extensions.Logging;
@@ -20,6 +22,14 @@ public class AssemblyTestLog : IDisposable
2022
public static readonly string OutputDirectoryEnvironmentVariableName = "ASPNETCORE_TEST_LOG_DIR";
2123
private static readonly string LogFileExtension = ".log";
2224
private static readonly int MaxPathLength = 245;
25+
private static char[] InvalidFileChars = new char[]
26+
{
27+
'\"', '<', '>', '|', '\0',
28+
(char)1, (char)2, (char)3, (char)4, (char)5, (char)6, (char)7, (char)8, (char)9, (char)10,
29+
(char)11, (char)12, (char)13, (char)14, (char)15, (char)16, (char)17, (char)18, (char)19, (char)20,
30+
(char)21, (char)22, (char)23, (char)24, (char)25, (char)26, (char)27, (char)28, (char)29, (char)30,
31+
(char)31, ':', '*', '?', '\\', '/', ' ', (char)127
32+
};
2333

2434
private static readonly object _lock = new object();
2535
private static readonly Dictionary<Assembly, AssemblyTestLog> _logs = new Dictionary<Assembly, AssemblyTestLog>();
@@ -42,9 +52,12 @@ private AssemblyTestLog(ILoggerFactory globalLoggerFactory, ILogger globalLogger
4252
public IDisposable StartTestLog(ITestOutputHelper output, string className, out ILoggerFactory loggerFactory, [CallerMemberName] string testName = null) =>
4353
StartTestLog(output, className, out loggerFactory, LogLevel.Debug, testName);
4454

45-
public IDisposable StartTestLog(ITestOutputHelper output, string className, out ILoggerFactory loggerFactory, LogLevel minLogLevel, [CallerMemberName] string testName = null)
55+
public IDisposable StartTestLog(ITestOutputHelper output, string className, out ILoggerFactory loggerFactory, LogLevel minLogLevel, [CallerMemberName] string testName = null) =>
56+
StartTestLog(output, className, out loggerFactory, minLogLevel, out var _, testName);
57+
58+
internal IDisposable StartTestLog(ITestOutputHelper output, string className, out ILoggerFactory loggerFactory, LogLevel minLogLevel, out string resolvedTestName, [CallerMemberName] string testName = null)
4659
{
47-
var serviceProvider = CreateLoggerServices(output, className, minLogLevel, testName);
60+
var serviceProvider = CreateLoggerServices(output, className, minLogLevel, out resolvedTestName, testName);
4861
var factory = serviceProvider.GetRequiredService<ILoggerFactory>();
4962
loggerFactory = factory;
5063
var logger = loggerFactory.CreateLogger("TestLifetime");
@@ -72,11 +85,13 @@ public ILoggerFactory CreateLoggerFactory(ITestOutputHelper output, string class
7285

7386
public ILoggerFactory CreateLoggerFactory(ITestOutputHelper output, string className, LogLevel minLogLevel, [CallerMemberName] string testName = null)
7487
{
75-
return CreateLoggerServices(output, className, minLogLevel, testName).GetRequiredService<ILoggerFactory>();
88+
return CreateLoggerServices(output, className, minLogLevel, out var _, testName).GetRequiredService<ILoggerFactory>();
7689
}
7790

78-
public IServiceProvider CreateLoggerServices(ITestOutputHelper output, string className, LogLevel minLogLevel, [CallerMemberName] string testName = null)
91+
public IServiceProvider CreateLoggerServices(ITestOutputHelper output, string className, LogLevel minLogLevel, out string normalizedTestName, [CallerMemberName] string testName = null)
7992
{
93+
normalizedTestName = string.Empty;
94+
8095
// Try to shorten the class name using the assembly name
8196
if (className.StartsWith(_assemblyName + "."))
8297
{
@@ -87,6 +102,7 @@ public IServiceProvider CreateLoggerServices(ITestOutputHelper output, string cl
87102
if (!string.IsNullOrEmpty(_baseDirectory))
88103
{
89104
var testOutputDirectory = Path.Combine(GetAssemblyBaseDirectory(_assemblyName, _baseDirectory), className);
105+
testName = RemoveIllegalFileChars(testName);
90106

91107
if (testOutputDirectory.Length + testName.Length + LogFileExtension.Length >= MaxPathLength)
92108
{
@@ -118,11 +134,13 @@ public IServiceProvider CreateLoggerServices(ITestOutputHelper output, string cl
118134
if (!File.Exists(testOutputFile))
119135
{
120136
_globalLogger.LogWarning($"To resolve log file collision, the enumerated file {testOutputFile} will be used.");
137+
testName = $"{testName}.{i}";
121138
break;
122139
}
123140
}
124141
}
125142

143+
normalizedTestName = testName;
126144
serilogLoggerProvider = ConfigureFileLogging(testOutputFile);
127145
}
128146

@@ -234,6 +252,17 @@ private static SerilogLoggerProvider ConfigureFileLogging(string fileName)
234252
return new SerilogLoggerProvider(serilogger, dispose: true);
235253
}
236254

255+
private static string RemoveIllegalFileChars(string s)
256+
{
257+
var sb = new StringBuilder();
258+
259+
foreach (var c in s)
260+
{
261+
sb.Append(InvalidFileChars.Contains(c) ? '_' : c);
262+
}
263+
return sb.ToString();
264+
}
265+
237266
public void Dispose()
238267
{
239268
(_serviceProvider as IDisposable)?.Dispose();

src/Microsoft.Extensions.Logging.Testing/LoggedTest.cs

Lines changed: 30 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,24 +4,50 @@
44
using System;
55
using System.Reflection;
66
using System.Runtime.CompilerServices;
7+
using Microsoft.Extensions.DependencyInjection;
78
using Xunit.Abstractions;
89

910
namespace Microsoft.Extensions.Logging.Testing
1011
{
1112
public abstract class LoggedTest
1213
{
13-
private readonly ITestOutputHelper _output;
14+
private ILoggerFactory _loggerFactory;
1415

15-
public LoggedTest(ITestOutputHelper output)
16+
// Obsolete but keeping for back compat
17+
public LoggedTest(ITestOutputHelper output = null)
1618
{
17-
_output = output;
19+
TestOutputHelper = output;
1820
}
1921

22+
// Internal for testing
23+
internal string TestMethodTestName { get; set; }
24+
25+
public ILogger Logger { get; set; }
26+
27+
public ILoggerFactory LoggerFactory
28+
{
29+
get
30+
{
31+
return _loggerFactory;
32+
}
33+
set
34+
{
35+
_loggerFactory = value;
36+
AddTestLogging = services => services.AddSingleton(_loggerFactory);
37+
}
38+
}
39+
40+
public ITestOutputHelper TestOutputHelper { get; set; }
41+
42+
public ITestSink TestSink { get; set; }
43+
44+
public Action<IServiceCollection> AddTestLogging { get; private set; } = services => { };
45+
2046
public IDisposable StartLog(out ILoggerFactory loggerFactory, [CallerMemberName] string testName = null) => StartLog(out loggerFactory, LogLevel.Information, testName);
2147

2248
public IDisposable StartLog(out ILoggerFactory loggerFactory, LogLevel minLogLevel, [CallerMemberName] string testName = null)
2349
{
24-
return AssemblyTestLog.ForAssembly(GetType().GetTypeInfo().Assembly).StartTestLog(_output, GetType().FullName, out loggerFactory, minLogLevel, testName);
50+
return AssemblyTestLog.ForAssembly(GetType().GetTypeInfo().Assembly).StartTestLog(TestOutputHelper, GetType().FullName, out loggerFactory, minLogLevel, testName);
2551
}
2652
}
2753
}

src/Microsoft.Extensions.Logging.Testing/Microsoft.Extensions.Logging.Testing.csproj

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
<PropertyGroup>
44
<Description>Helpers for writing tests that use Microsoft.Extensions.Logging. Contains null implementations of the abstractions that do nothing, as well as test implementations that are observable.</Description>
5-
<TargetFramework>netstandard2.0</TargetFramework>
5+
<TargetFrameworks>netstandard2.0;net461</TargetFrameworks>
66
<NoWarn>$(NoWarn);CS1591</NoWarn>
77
<PackageTags>$(PackageTags);testing</PackageTags>
88
<EnableApiCheck>false</EnableApiCheck>
@@ -14,11 +14,18 @@
1414
</ItemGroup>
1515

1616
<ItemGroup>
17+
<PackageReference Include="Microsoft.AspNetCore.Testing" Version="$(MicrosoftAspNetCoreTestingPackageVersion)" />
1718
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="$(MicrosoftExtensionsDependencyInjectionPackageVersion)" />
1819
<PackageReference Include="xunit.abstractions" Version="$(XunitAbstractionsPackageVersion)" />
20+
<PackageReference Include="xunit.extensibility.execution" Version="$(XunitExtensibilityExecutionPackageVersion)" />
1921
<PackageReference Include="xunit.assert" Version="$(XunitAssertPackageVersion)" />
2022
<PackageReference Include="Serilog.Extensions.Logging" Version="$(SerilogExtensionsLoggingPackageVersion)" />
2123
<PackageReference Include="Serilog.Sinks.File" Version="$(SerilogSinksFilePackageVersion)" />
2224
</ItemGroup>
2325

26+
<ItemGroup>
27+
<Folder Include="Xunit\Shared\" />
28+
29+
<Content Include="build\**\*.props" PackagePath="%(Identity)" />
30+
</ItemGroup>
2431
</Project>
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
// Copyright (c) .NET Foundation. All rights reserved.
2+
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
3+
4+
using System.Runtime.CompilerServices;
5+
6+
[assembly: InternalsVisibleTo("Microsoft.Extensions.Logging.Testing.Tests, PublicKey=0024000004800000940000000602000000240000525341310004000001000100f33a29044fa9d740c9b3213a93e57c84b472c84e0b8a0e1ae48e67a9f8f6de9d5f7f3d52ac23e48ac51801f1dc950abe901da34d2a9e3baadb141a17c77ef3c565dd5ee5054b91cf63bb3c6ab83f72ab3aafe93d0fc3c2348b764fafb0b1c0733de51459aeab46580384bf9d74c4e28164b7cde247f891ba07891c9d872ad2bb")]
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
// Copyright (c) .NET Foundation. All rights reserved.
2+
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
3+
4+
namespace Microsoft.Extensions.Logging.Testing
5+
{
6+
public class TestLoggerProvider : ILoggerProvider
7+
{
8+
private readonly ITestSink _sink;
9+
10+
public TestLoggerProvider(ITestSink sink)
11+
{
12+
_sink = sink;
13+
}
14+
15+
public ILogger CreateLogger(string categoryName)
16+
{
17+
return new TestLogger(categoryName, _sink, enabled: true);
18+
}
19+
20+
public void Dispose()
21+
{
22+
}
23+
}
24+
}
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
// Copyright (c) .NET Foundation. All rights reserved.
2+
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
3+
4+
using System;
5+
6+
namespace Microsoft.Extensions.Logging.Testing
7+
{
8+
[AttributeUsage(AttributeTargets.Method, AllowMultiple = false)]
9+
public class LogLevelAttribute : Attribute
10+
{
11+
public LogLevelAttribute(LogLevel logLevel)
12+
{
13+
LogLevel = logLevel;
14+
}
15+
16+
public LogLevel LogLevel { get; }
17+
}
18+
}

0 commit comments

Comments
 (0)