Skip to content
Merged
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
@@ -1,12 +1,11 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.

#if NETFRAMEWORK

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

namespace Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.Utilities;
Expand All @@ -18,9 +17,22 @@ internal static class AppDomainUtilities
{
private const string ObjectModelVersionBuiltAgainst = "11.0.0.0";

private const string ObjectModelAssemblyName = "Microsoft.VisualStudio.TestPlatform.ObjectModel";

private static readonly Version DefaultVersion = new();
private static readonly Version Version45 = new("4.5");

/// <summary>
/// Resolves the loaded VSTest object-model assembly by simple name, so this AppDomain-wiring code does not
/// need a compile-time reference to it. By the time these methods run (test source host setup during
/// discovery/execution) the adapter has already loaded the object model into the current (parent) domain,
/// so its identity — including any binding redirect in effect — matches what a direct type reference resolved to.
/// </summary>
/// <returns>The object-model assembly.</returns>
private static Assembly GetObjectModelAssembly()
=> AppDomain.CurrentDomain.GetAssemblies().FirstOrDefault(a => string.Equals(a.GetName().Name, ObjectModelAssemblyName, StringComparison.Ordinal))
?? Assembly.Load(ObjectModelAssemblyName);

/// <summary>
/// Gets or sets the Xml Utilities instance.
/// </summary>
Expand Down Expand Up @@ -90,7 +102,7 @@ internal static string GetTargetFrameworkVersionString(string testSourcePath)

var resolutionPaths = new List<string>
{
Path.GetDirectoryName(typeof(TestCase).Assembly.Location),
Path.GetDirectoryName(GetObjectModelAssembly().Location),
Path.GetDirectoryName(testSourcePath),
};

Expand Down Expand Up @@ -157,10 +169,10 @@ internal static void SetConfigurationFile(AppDomainSetup appDomainSetup, string?
try
{
// Add redirection of the built 11.0 Object Model assembly to the current version if that is not 11.0
string currentVersionOfObjectModel = typeof(TestCase).Assembly.GetName().Version.ToString();
string currentVersionOfObjectModel = GetObjectModelAssembly().GetName().Version.ToString();
if (!string.Equals(currentVersionOfObjectModel, ObjectModelVersionBuiltAgainst, StringComparison.Ordinal))
{
AssemblyName assemblyName = typeof(TestCase).Assembly.GetName();
AssemblyName assemblyName = GetObjectModelAssembly().GetName();
Comment on lines +172 to +175

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[NIT] Performance / Code Structure

GetObjectModelAssembly() is called twice within the same try-block. Unlike the old typeof(TestCase).Assembly (a JIT-inlined runtime constant), each call to GetObjectModelAssembly() allocates a LINQ query over AppDomain.CurrentDomain.GetAssemblies(). The practical cost is negligible in a setup path, but the logic can be simplified to a single call:

// Capture once — avoids two linear scans of GetAssemblies()
AssemblyName assemblyName = GetObjectModelAssembly().GetName();
string currentVersionOfObjectModel = assemblyName.Version!.ToString();
if (!string.Equals(currentVersionOfObjectModel, ObjectModelVersionBuiltAgainst, StringComparison.Ordinal))
{
    byte[] configurationBytes =
        XmlUtilities.AddAssemblyRedirection(
            testSourceConfigFile,
            assemblyName,
            ObjectModelVersionBuiltAgainst,
            assemblyName.Version!.ToString());
    appDomainSetup.SetConfigurationBytes(configurationBytes);
}

This also surfaces the Version! null-annotation more explicitly — AssemblyName.Version is nullable, though it will never be null for the VSTest ObjectModel in practice.

byte[] configurationBytes =
XmlUtilities.AddAssemblyRedirection(
testSourceConfigFile,
Expand Down
Loading