Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -7,33 +7,61 @@

namespace Microsoft.VisualStudio.TestTools.UnitTesting;

/// <summary>
/// Provides a bridge for logging messages using an <see cref="ILogger"/> instance.
/// </summary>
/// <remarks>
/// This class adapts logging calls to the <see cref="ILogger"/> interface, allowing messages to be
/// logged at various levels such as Error, Information, Debug, and Warning. It checks if the respective log level is
/// enabled before logging the message.
/// </remarks>
[SuppressMessage("ApiDesign", "RS0030:Do not use banned APIs", Justification = "MTP logger bridge")]
// Type is serializable to support serialization through AppDomains but ILogger is not so we handle it being null
// when we are inside the AppDomain.
// TODO: We should either not support AppDomains at all or make a marshaling version that would send the message and
// enum to the outside AppDomain instance that would then log it.
[Serializable]
internal sealed class BridgedTraceLogger : IAdapterTraceLogger
{
private readonly ILogger _logger;
[NonSerialized]
private readonly ILogger? _logger;

// This constructor is used when the logger is not available, e.g., in AppDomains.
public BridgedTraceLogger()
=> _logger = null;

public BridgedTraceLogger(ILogger logger)
=> _logger = logger;
=> _logger = logger ?? throw new ArgumentNullException(nameof(logger));

public bool IsInfoEnabled => _logger?.IsEnabled(LogLevel.Information) ?? false;

public void LogError(string format, params object?[] args)
{
if (_logger.IsEnabled(LogLevel.Error))
if (_logger?.IsEnabled(LogLevel.Error) == true)
{
_logger.LogError(string.Format(CultureInfo.CurrentCulture, format, args));
}
}

public void LogInfo(string format, params object?[] args)
{
if (_logger.IsEnabled(LogLevel.Information))
if (_logger?.IsEnabled(LogLevel.Information) == true)
{
_logger.LogInformation(string.Format(CultureInfo.CurrentCulture, format, args));
}
}

public void LogVerbose(string format, params object?[] args)
{
if (_logger?.IsEnabled(LogLevel.Debug) == true)
{
_logger.LogDebug(string.Format(CultureInfo.CurrentCulture, format, args));
}
}

public void LogWarning(string format, params object?[] args)
{
if (_logger.IsEnabled(LogLevel.Warning))
if (_logger?.IsEnabled(LogLevel.Warning) == true)
{
_logger.LogWarning(string.Format(CultureInfo.CurrentCulture, format, args));
}
Expand Down
127 changes: 18 additions & 109 deletions src/Adapter/MSTestAdapter.PlatformServices/AssemblyResolver.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
using System.Security.Permissions;
#endif

using Microsoft.VisualStudio.TestPlatform.ObjectModel;
using Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.Interface;
using Microsoft.VisualStudio.TestTools.UnitTesting;

namespace Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices;
Expand Down Expand Up @@ -83,7 +83,7 @@ class AssemblyResolver :
/// lock for the loaded assemblies cache.
/// </summary>
private readonly Lock _syncLock = new();

private readonly IAdapterTraceLogger _logger;
private static List<string>? s_currentlyLoading;
private bool _disposed;

Expand All @@ -93,16 +93,18 @@ class AssemblyResolver :
/// <param name="directories">
/// A list of directories for resolution path.
/// </param>
/// <param name="logger">The logger.</param>
/// <remarks>
/// If there are additional paths where a recursive search is required
/// call AddSearchDirectoryFromRunSetting method with that list.
/// </remarks>
public AssemblyResolver(IList<string> directories)
public AssemblyResolver(IList<string> directories, IAdapterTraceLogger logger)
{
Guard.NotNullOrEmpty(directories);

_searchDirectories = [.. directories];
_directoryList = new Queue<RecursiveDirectoryPath>();
_logger = logger;

AppDomain.CurrentDomain.AssemblyResolve += new ResolveEventHandler(OnResolve);
#if NETFRAMEWORK
Expand Down Expand Up @@ -342,18 +344,10 @@ protected virtual
}
catch (Exception ex)
{
SafeLog(
SafeLog(name, () => _logger.LogInfo(
"MSTest.AssemblyResolver.OnResolve: Failed to create assemblyName '{0}'. Reason: {1} ",
name,
() =>
{
if (EqtTrace.IsInfoEnabled)
{
EqtTrace.Info(
"MSTest.AssemblyResolver.OnResolve: Failed to create assemblyName '{0}'. Reason: {1} ",
name,
ex);
}
});
ex));

return null;
}
Expand All @@ -367,15 +361,7 @@ protected virtual
continue;
}

SafeLog(
name,
() =>
{
if (EqtTrace.IsVerboseEnabled)
{
EqtTrace.Verbose("MSTest.AssemblyResolver.OnResolve: Searching assembly '{0}' in the directory '{1}'", requestedName.Name, dir);
}
});
SafeLog(name, () => _logger.LogVerbose("MSTest.AssemblyResolver.OnResolve: Searching assembly '{0}' in the directory '{1}'", requestedName.Name, dir));

foreach (string extension in new string[] { ".dll", ".exe" })
{
Expand All @@ -389,15 +375,7 @@ protected virtual
// the ResourceHelper's currentlyLoading stack to null if an exception occurs.
if (s_currentlyLoading != null && s_currentlyLoading.Count > 0 && s_currentlyLoading.LastIndexOf(assemblyPath) != -1)
{
SafeLog(
name,
() =>
{
if (EqtTrace.IsInfoEnabled)
{
EqtTrace.Info("MSTest.AssemblyResolver.OnResolve: Assembly '{0}' is searching for itself recursively '{1}', returning as not found.", name, assemblyPath);
}
});
SafeLog(name, () => _logger.LogInfo("MSTest.AssemblyResolver.OnResolve: Assembly '{0}' is searching for itself recursively '{1}', returning as not found.", name, assemblyPath));
_resolvedAssemblies[name] = null;
return null;
}
Expand Down Expand Up @@ -497,27 +475,9 @@ private void WindowsRuntimeMetadataReflectionOnlyNamespaceResolve(object sender,
return null;
}

SafeLog(
args.Name,
() =>
{
if (EqtTrace.IsInfoEnabled)
{
EqtTrace.Info("MSTest.AssemblyResolver.OnResolve: Resolving assembly '{0}'", args.Name);
}
});

SafeLog(args.Name, () => _logger.LogInfo("MSTest.AssemblyResolver.OnResolve: Resolving assembly '{0}'", args.Name));
string assemblyNameToLoad = AppDomain.CurrentDomain.ApplyPolicy(args.Name);

SafeLog(
assemblyNameToLoad,
() =>
{
if (EqtTrace.IsInfoEnabled)
{
EqtTrace.Info("MSTest.AssemblyResolver.OnResolve: Resolving assembly after applying policy '{0}'", assemblyNameToLoad);
}
});
SafeLog(assemblyNameToLoad, () => _logger.LogInfo("MSTest.AssemblyResolver.OnResolve: Resolving assembly after applying policy '{0}'", assemblyNameToLoad));

lock (_syncLock)
{
Expand Down Expand Up @@ -561,17 +521,7 @@ private void WindowsRuntimeMetadataReflectionOnlyNamespaceResolve(object sender,
else
{
// generate warning that path does not exist.
SafeLog(
assemblyNameToLoad,
() =>
{
if (EqtTrace.IsWarningEnabled)
{
EqtTrace.Warning(
"MSTest.AssemblyResolver.OnResolve: the directory '{0}', does not exist",
currentNode.DirectoryPath);
}
});
SafeLog(assemblyNameToLoad, () => _logger.LogWarning("MSTest.AssemblyResolver.OnResolve: the directory '{0}', does not exist", currentNode.DirectoryPath));
}
}

Expand Down Expand Up @@ -616,15 +566,7 @@ private void WindowsRuntimeMetadataReflectionOnlyNamespaceResolve(object sender,
}
catch (Exception ex)
{
SafeLog(
args.Name,
() =>
{
if (EqtTrace.IsInfoEnabled)
{
EqtTrace.Info("MSTest.AssemblyResolver.OnResolve: Failed to load assembly '{0}'. Reason: {1}", assemblyNameToLoad, ex);
}
});
SafeLog(args.Name, () => _logger.LogInfo("MSTest.AssemblyResolver.OnResolve: Failed to load assembly '{0}'. Reason: {1}", assemblyNameToLoad, ex));
}

return assembly;
Expand All @@ -645,15 +587,7 @@ private bool TryLoadFromCache(string assemblyName, bool isReflectionOnly, out As
: _resolvedAssemblies.TryGetValue(assemblyName, out assembly);
if (isFoundInCache)
{
SafeLog(
assemblyName,
() =>
{
if (EqtTrace.IsInfoEnabled)
{
EqtTrace.Info("MSTest.AssemblyResolver.OnResolve: Resolved '{0}'", assemblyName);
}
});
SafeLog(assemblyName, () => _logger.LogInfo("MSTest.AssemblyResolver.OnResolve: Resolved '{0}'", assemblyName));
return true;
}

Expand Down Expand Up @@ -721,29 +655,12 @@ private static void SafeLog(string? assemblyName, Action loggerAction)
_resolvedAssemblies[assemblyName] = assembly;
}

SafeLog(
assemblyName,
() =>
{
if (EqtTrace.IsInfoEnabled)
{
EqtTrace.Info("MSTest.AssemblyResolver.OnResolve: Resolved assembly '{0}'", assemblyName);
}
});

SafeLog(assemblyName, () => _logger.LogInfo("MSTest.AssemblyResolver.OnResolve: Resolved assembly '{0}'", assemblyName));
return assembly;
}
catch (FileLoadException ex)
{
SafeLog(
assemblyName,
() =>
{
if (EqtTrace.IsInfoEnabled)
{
EqtTrace.Info("MSTest.AssemblyResolver.OnResolve: Failed to load assembly '{0}'. Reason:{1} ", assemblyName, ex);
}
});
SafeLog(assemblyName, () => _logger.LogInfo("MSTest.AssemblyResolver.OnResolve: Failed to load assembly '{0}'. Reason:{1} ", assemblyName, ex));

// Re-throw FileLoadException, because this exception means that the assembly
// was found, but could not be loaded. This will allow us to report a more
Expand All @@ -753,15 +670,7 @@ private static void SafeLog(string? assemblyName, Action loggerAction)
catch (Exception ex)
{
// For all other exceptions, try the next extension.
SafeLog(
assemblyName,
() =>
{
if (EqtTrace.IsInfoEnabled)
{
EqtTrace.Info("MSTest.AssemblyResolver.OnResolve: Failed to load assembly '{0}'. Reason:{1} ", assemblyName, ex);
}
});
SafeLog(assemblyName, () => _logger.LogInfo("MSTest.AssemblyResolver.OnResolve: Failed to load assembly '{0}'. Reason:{1} ", assemblyName, ex));
}

return null;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
using System.Data;
using System.Data.OleDb;

using Microsoft.VisualStudio.TestPlatform.ObjectModel;
using Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.Interface;
using Microsoft.VisualStudio.TestTools.UnitTesting;

namespace Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.Data;
Expand All @@ -22,8 +22,8 @@ internal sealed class CsvDataConnection : TestDataConnection

private readonly string _fileName;

public CsvDataConnection(string fileName, List<string> dataFolders)
: base(dataFolders)
public CsvDataConnection(string fileName, List<string> dataFolders, IAdapterTraceLogger logger)
: base(dataFolders, logger)
{
DebugEx.Assert(!StringEx.IsNullOrEmpty(fileName), "fileName");
_fileName = fileName;
Expand Down Expand Up @@ -63,7 +63,7 @@ public override List<string> GetDataTablesAndViews()
}
catch (Exception exception)
{
EqtTrace.ErrorIf(EqtTrace.IsErrorEnabled, exception.Message + " for CSV data source " + _fileName);
Logger.LogError(exception.Message + " for CSV data source " + _fileName);
}

return null;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@

using System.Data.Odbc;

using Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.Interface;
using Microsoft.VisualStudio.TestTools.UnitTesting;

namespace Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.Data;
Expand All @@ -16,8 +17,8 @@ internal sealed class OdbcDataConnection : TestDataConnectionSql
{
private readonly bool _isMSSql;

public OdbcDataConnection(string invariantProviderName, string connectionString, List<string> dataFolders)
: base(invariantProviderName, FixConnectionString(connectionString, dataFolders), dataFolders)
public OdbcDataConnection(string invariantProviderName, string connectionString, List<string> dataFolders, IAdapterTraceLogger logger)
: base(invariantProviderName, FixConnectionString(connectionString, dataFolders), dataFolders, logger)
{
// Need open connection to get Connection.Driver.
DebugEx.Assert(IsOpen(), "The connection must be open!");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@

using System.Data.OleDb;

using Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.Interface;
using Microsoft.VisualStudio.TestTools.UnitTesting;

namespace Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.Data;
Expand All @@ -16,8 +17,8 @@ internal sealed class OleDataConnection : TestDataConnectionSql
{
private readonly bool _isMSSql;

public OleDataConnection(string invariantProviderName, string connectionString, List<string> dataFolders)
: base(invariantProviderName, FixConnectionString(connectionString, dataFolders), dataFolders)
public OleDataConnection(string invariantProviderName, string connectionString, List<string> dataFolders, IAdapterTraceLogger logger)
: base(invariantProviderName, FixConnectionString(connectionString, dataFolders), dataFolders, logger)
{
// Need open connection to get Connection.Provider.
DebugEx.Assert(IsOpen(), "The connection must be open!");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@

using System.Data.SqlClient;

using Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.Interface;
using Microsoft.VisualStudio.TestTools.UnitTesting;

namespace Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.Data;
Expand All @@ -14,8 +15,8 @@ namespace Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.Dat
/// </summary>
internal sealed class SqlDataConnection : TestDataConnectionSql
{
public SqlDataConnection(string invariantProviderName, string connectionString, List<string> dataFolders)
: base(invariantProviderName, FixConnectionString(connectionString, dataFolders), dataFolders)
public SqlDataConnection(string invariantProviderName, string connectionString, List<string> dataFolders, IAdapterTraceLogger logger)
: base(invariantProviderName, FixConnectionString(connectionString, dataFolders), dataFolders, logger)
{
}

Expand Down
Loading
Loading