Convert to file-scoped namespaces (#1197)

This commit is contained in:
Amaury Levé 2022-08-30 09:31:32 +02:00 коммит произвёл GitHub
Родитель 993fe848a2
Коммит f84ea2b842
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 4AEE18F83AFDEB23
393 изменённых файлов: 53461 добавлений и 53747 удалений

Просмотреть файл

@ -47,3 +47,107 @@ indent_size = 4
tab_width = 4
insert_final_newline = true
trim_trailing_whitespace = true
[*.{cs,vb}]
end_of_line = crlf
#### .NET Coding Conventions ####
# Organize usings
dotnet_separate_import_directive_groups = true
dotnet_sort_system_directives_first = true
# License header
file_header_template = Copyright (c) Microsoft Corporation. All rights reserved.\nLicensed under the MIT license. See LICENSE file in the project root for full license information.
# Styling conventions
csharp_indent_labels = one_less_than_current
csharp_prefer_braces = true:silent
csharp_prefer_simple_default_expression = true:suggestion
csharp_prefer_simple_using_statement = true:suggestion
csharp_style_deconstructed_variable_declaration = true:suggestion
csharp_style_expression_bodied_accessors = true:silent
csharp_style_expression_bodied_constructors = false:silent
csharp_style_expression_bodied_indexers = true:silent
csharp_style_expression_bodied_lambdas = true:silent
csharp_style_expression_bodied_local_functions = false:silent
csharp_style_expression_bodied_methods = false:silent
csharp_style_expression_bodied_operators = false:silent
csharp_style_expression_bodied_properties = true:silent
csharp_style_implicit_object_creation_when_type_is_apparent = true:suggestion
csharp_style_inlined_variable_declaration = true:suggestion
csharp_style_namespace_declarations = file_scoped:silent
csharp_style_namespace_declarations = file_scoped:warning
csharp_style_prefer_index_operator = true:suggestion
csharp_style_prefer_local_over_anonymous_function = true:suggestion
csharp_style_prefer_method_group_conversion = true:silent
csharp_style_prefer_null_check_over_type_check = true:suggestion
csharp_style_prefer_range_operator = true:suggestion
csharp_style_prefer_top_level_statements = true:silent
csharp_style_prefer_tuple_swap = true:suggestion
csharp_style_prefer_utf8_string_literals = true:suggestion
csharp_style_throw_expression = true:suggestion
csharp_style_unused_value_assignment_preference = discard_variable:suggestion
csharp_using_directive_placement = outside_namespace:silent
dotnet_style_operator_placement_when_wrapping = beginning_of_line
dotnet_style_coalesce_expression = true:suggestion
dotnet_style_null_propagation = true:suggestion
dotnet_style_prefer_is_null_check_over_reference_equality_method = true:suggestion
dotnet_style_prefer_auto_properties = true:silent
dotnet_style_object_initializer = true:suggestion
dotnet_style_collection_initializer = true:suggestion
dotnet_style_prefer_simplified_boolean_expressions = true:suggestion
dotnet_style_prefer_conditional_expression_over_assignment = true:silent
dotnet_style_prefer_conditional_expression_over_return = true:silent
dotnet_style_explicit_tuple_names = true:suggestion
dotnet_style_prefer_inferred_tuple_names = true:suggestion
dotnet_style_prefer_inferred_anonymous_type_member_names = true:suggestion
dotnet_style_prefer_compound_assignment = true:suggestion
dotnet_style_prefer_simplified_interpolation = true:suggestion
dotnet_style_namespace_match_folder = true:suggestion
#### Naming styles ####
# Naming rules
dotnet_naming_rule.interface_should_be_begins_with_i.severity = suggestion
dotnet_naming_rule.interface_should_be_begins_with_i.symbols = interface
dotnet_naming_rule.interface_should_be_begins_with_i.style = begins_with_i
dotnet_naming_rule.types_should_be_pascal_case.severity = suggestion
dotnet_naming_rule.types_should_be_pascal_case.symbols = types
dotnet_naming_rule.types_should_be_pascal_case.style = pascal_case
dotnet_naming_rule.non_field_members_should_be_pascal_case.severity = suggestion
dotnet_naming_rule.non_field_members_should_be_pascal_case.symbols = non_field_members
dotnet_naming_rule.non_field_members_should_be_pascal_case.style = pascal_case
# Symbol specifications
dotnet_naming_symbols.interface.applicable_kinds = interface
dotnet_naming_symbols.interface.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected
dotnet_naming_symbols.interface.required_modifiers =
dotnet_naming_symbols.types.applicable_kinds = class, struct, interface, enum
dotnet_naming_symbols.types.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected
dotnet_naming_symbols.types.required_modifiers =
dotnet_naming_symbols.non_field_members.applicable_kinds = property, event, method
dotnet_naming_symbols.non_field_members.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected
dotnet_naming_symbols.non_field_members.required_modifiers =
# Naming styles
dotnet_naming_style.begins_with_i.required_prefix = I
dotnet_naming_style.begins_with_i.required_suffix =
dotnet_naming_style.begins_with_i.word_separator =
dotnet_naming_style.begins_with_i.capitalization = pascal_case
dotnet_naming_style.pascal_case.required_prefix =
dotnet_naming_style.pascal_case.required_suffix =
dotnet_naming_style.pascal_case.word_separator =
dotnet_naming_style.pascal_case.capitalization = pascal_case

Просмотреть файл

@ -2,4 +2,8 @@
<Project>
<Import Project="scripts\build\TestFx.props" />
<Import Project="eng\TFMConfiguration.props" />
<PropertyGroup>
<LangVersion>Latest</LangVersion>
</PropertyGroup>
</Project>

Просмотреть файл

@ -1,46 +1,45 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
namespace MSTest.Extensibility.Samples
namespace MSTest.Extensibility.Samples;
using Microsoft.VisualStudio.TestTools.UnitTesting;
public static class AssertEx
{
using Microsoft.VisualStudio.TestTools.UnitTesting;
private static AssertIs assertis;
public static class AssertEx
/// <summary>
/// A simple assert extension to validate if an object is of a given type.
/// </summary>
/// <typeparam name="T">The type to check.</typeparam>
/// <param name="assert">Assert class.</param>
/// <param name="obj">The object.</param>
/// <returns>True if object is of the given type.</returns>
/// <exception cref="AssertFailedException">If object is not of the given type.</exception>
public static bool IsOfType<T>(this Assert assert, object obj)
{
private static AssertIs assertis;
/// <summary>
/// A simple assert extension to validate if an object is of a given type.
/// </summary>
/// <typeparam name="T">The type to check.</typeparam>
/// <param name="assert">Assert class.</param>
/// <param name="obj">The object.</param>
/// <returns>True if object is of the given type.</returns>
/// <exception cref="AssertFailedException">If object is not of the given type.</exception>
public static bool IsOfType<T>(this Assert assert, object obj)
if (obj is T)
{
if (obj is T)
{
return true;
}
throw new AssertFailedException(
string.Format(
"Expected object of type {0} but found object of type {1}",
typeof(T),
obj ?? obj.GetType()));
return true;
}
/// <summary>
/// A chain/grouping of assert statements.
/// </summary>
/// <param name="assert">The Assert class.</param>
/// <returns>The class that contains the assert methods for this grouping.</returns>
public static AssertIs Is(this Assert assert)
{
assertis ??= new AssertIs();
throw new AssertFailedException(
string.Format(
"Expected object of type {0} but found object of type {1}",
typeof(T),
obj ?? obj.GetType()));
}
return assertis;
}
/// <summary>
/// A chain/grouping of assert statements.
/// </summary>
/// <param name="assert">The Assert class.</param>
/// <returns>The class that contains the assert methods for this grouping.</returns>
public static AssertIs Is(this Assert assert)
{
assertis ??= new AssertIs();
return assertis;
}
}

Просмотреть файл

@ -1,46 +1,45 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
namespace MSTest.Extensibility.Samples
namespace MSTest.Extensibility.Samples;
using Microsoft.VisualStudio.TestTools.UnitTesting;
/// <summary>
/// A grouping of Assert statements with similar functionality.
/// </summary>
public class AssertIs
{
using Microsoft.VisualStudio.TestTools.UnitTesting;
/// <summary>
/// Determines if the 'divisor' is actually a divisor of 'number'.
/// </summary>
/// <param name="number">A number.</param>
/// <param name="divisor">Its proclaimed divisor.</param>
/// <returns>True if it is a divisor</returns>
/// <exception cref="AssertFailedException">If it is not a divisor.</exception>
public bool Divisor(int number, int divisor)
{
if (number % divisor == 0)
{
return true;
}
throw new AssertFailedException(string.Format("{0} is not a divisor of {1}", divisor, number));
}
/// <summary>
/// A grouping of Assert statements with similar functionality.
/// Determines if a number is positive.
/// </summary>
public class AssertIs
/// <param name="number">The number.</param>
/// <returns>True if it is positive.</returns>
/// <exception cref="AssertFailedException">If the number is not positive.</exception>
public bool Positive(int number)
{
/// <summary>
/// Determines if the 'divisor' is actually a divisor of 'number'.
/// </summary>
/// <param name="number">A number.</param>
/// <param name="divisor">Its proclaimed divisor.</param>
/// <returns>True if it is a divisor</returns>
/// <exception cref="AssertFailedException">If it is not a divisor.</exception>
public bool Divisor(int number, int divisor)
if (number > 0)
{
if (number % divisor == 0)
{
return true;
}
throw new AssertFailedException(string.Format("{0} is not a divisor of {1}", divisor, number));
return true;
}
/// <summary>
/// Determines if a number is positive.
/// </summary>
/// <param name="number">The number.</param>
/// <returns>True if it is positive.</returns>
/// <exception cref="AssertFailedException">If the number is not positive.</exception>
public bool Positive(int number)
{
if (number > 0)
{
return true;
}
throw new AssertFailedException(string.Format("{0} is not positive", number));
}
throw new AssertFailedException(string.Format("{0} is not positive", number));
}
}

Просмотреть файл

@ -4,7 +4,6 @@
<PropertyGroup>
<TargetFrameworks>net462</TargetFrameworks>
<TreatWarningsAsErrors>True</TreatWarningsAsErrors>
<LangVersion>Latest</LangVersion>
</PropertyGroup>
<PropertyGroup>

Просмотреть файл

@ -1,137 +1,136 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
namespace Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter
{
using System;
using System.Collections.Generic;
namespace Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter;
using Microsoft.VisualStudio.TestPlatform.ObjectModel;
using System;
using System.Collections.Generic;
using Microsoft.VisualStudio.TestPlatform.ObjectModel;
/// <summary>
/// Constants used throughout.
/// </summary>
internal static class Constants
{
/// <summary>
/// Uri of the MSTest executor.
/// </summary>
internal const string ExecutorUriString = "executor://MSTestAdapter/v2";
/// <summary>
/// Constants used throughout.
/// The name of test run parameters node in the runsettings.
/// </summary>
internal static class Constants
{
/// <summary>
/// Uri of the MSTest executor.
/// </summary>
internal const string ExecutorUriString = "executor://MSTestAdapter/v2";
internal const string TestRunParametersName = "TestRunParameters";
/// <summary>
/// The name of test run parameters node in the runsettings.
/// </summary>
internal const string TestRunParametersName = "TestRunParameters";
/// <summary>
/// The executor uri for this adapter.
/// </summary>
internal static readonly Uri ExecutorUri = new(ExecutorUriString);
/// <summary>
/// The executor uri for this adapter.
/// </summary>
internal static readonly Uri ExecutorUri = new(ExecutorUriString);
#region Test Property registration
internal static readonly TestProperty DescriptionProperty = TestProperty.Register("Description", DescriptionLabel, typeof(string), TestPropertyAttributes.Hidden, typeof(TestCase));
#region Test Property registration
internal static readonly TestProperty DescriptionProperty = TestProperty.Register("Description", DescriptionLabel, typeof(string), TestPropertyAttributes.Hidden, typeof(TestCase));
internal static readonly TestProperty WorkItemIdsProperty = TestProperty.Register("WorkItemIds", WorkItemIdsLabel, typeof(string[]), TestPropertyAttributes.Hidden, typeof(TestCase));
internal static readonly TestProperty WorkItemIdsProperty = TestProperty.Register("WorkItemIds", WorkItemIdsLabel, typeof(string[]), TestPropertyAttributes.Hidden, typeof(TestCase));
internal static readonly TestProperty CssIterationProperty = TestProperty.Register("CssIteration", CssIterationLabel, typeof(string), TestPropertyAttributes.Hidden, typeof(TestCase));
internal static readonly TestProperty CssIterationProperty = TestProperty.Register("CssIteration", CssIterationLabel, typeof(string), TestPropertyAttributes.Hidden, typeof(TestCase));
internal static readonly TestProperty CssProjectStructureProperty = TestProperty.Register("CssProjectStructure", CssProjectStructureLabel, typeof(string), TestPropertyAttributes.Hidden, typeof(TestCase));
internal static readonly TestProperty CssProjectStructureProperty = TestProperty.Register("CssProjectStructure", CssProjectStructureLabel, typeof(string), TestPropertyAttributes.Hidden, typeof(TestCase));
internal static readonly TestProperty TestClassNameProperty = TestProperty.Register("MSTestDiscoverer.TestClassName", TestClassNameLabel, typeof(string), TestPropertyAttributes.Hidden, typeof(TestCase));
internal static readonly TestProperty TestClassNameProperty = TestProperty.Register("MSTestDiscoverer.TestClassName", TestClassNameLabel, typeof(string), TestPropertyAttributes.Hidden, typeof(TestCase));
internal static readonly TestProperty DeclaringClassNameProperty = TestProperty.Register("MSTestDiscoverer.DeclaringClassName", DeclaringClassNameLabel, typeof(string), TestPropertyAttributes.Hidden, typeof(TestCase));
internal static readonly TestProperty DeclaringClassNameProperty = TestProperty.Register("MSTestDiscoverer.DeclaringClassName", DeclaringClassNameLabel, typeof(string), TestPropertyAttributes.Hidden, typeof(TestCase));
internal static readonly TestProperty AsyncTestProperty = TestProperty.Register("MSTestDiscoverer.IsAsync", IsAsyncLabel, typeof(bool), TestPropertyAttributes.Hidden, typeof(TestCase));
internal static readonly TestProperty AsyncTestProperty = TestProperty.Register("MSTestDiscoverer.IsAsync", IsAsyncLabel, typeof(bool), TestPropertyAttributes.Hidden, typeof(TestCase));
#pragma warning disable CS0618 // Type or member is obsolete
internal static readonly TestProperty TestCategoryProperty = TestProperty.Register("MSTestDiscoverer.TestCategory", TestCategoryLabel, typeof(string[]), TestPropertyAttributes.Hidden | TestPropertyAttributes.Trait, typeof(TestCase));
internal static readonly TestProperty TestCategoryProperty = TestProperty.Register("MSTestDiscoverer.TestCategory", TestCategoryLabel, typeof(string[]), TestPropertyAttributes.Hidden | TestPropertyAttributes.Trait, typeof(TestCase));
#pragma warning restore CS0618 // Type or member is obsolete
internal static readonly TestProperty PriorityProperty = TestProperty.Register("MSTestDiscoverer.Priority", PriorityLabel, typeof(int), TestPropertyAttributes.Hidden, typeof(TestCase));
internal static readonly TestProperty PriorityProperty = TestProperty.Register("MSTestDiscoverer.Priority", PriorityLabel, typeof(int), TestPropertyAttributes.Hidden, typeof(TestCase));
internal static readonly TestProperty DeploymentItemsProperty = TestProperty.Register("MSTestDiscoverer.DeploymentItems", DeploymentItemsLabel, typeof(KeyValuePair<string, string>[]), TestPropertyAttributes.Hidden, typeof(TestCase));
internal static readonly TestProperty DeploymentItemsProperty = TestProperty.Register("MSTestDiscoverer.DeploymentItems", DeploymentItemsLabel, typeof(KeyValuePair<string, string>[]), TestPropertyAttributes.Hidden, typeof(TestCase));
internal static readonly TestProperty DoNotParallelizeProperty = TestProperty.Register("MSTestDiscoverer.DoNotParallelize", DoNotParallelizeLabel, typeof(bool), TestPropertyAttributes.Hidden, typeof(TestCase));
internal static readonly TestProperty DoNotParallelizeProperty = TestProperty.Register("MSTestDiscoverer.DoNotParallelize", DoNotParallelizeLabel, typeof(bool), TestPropertyAttributes.Hidden, typeof(TestCase));
internal static readonly TestProperty ExecutionIdProperty = TestProperty.Register("ExecutionId", ExecutionIdLabel, typeof(Guid), TestPropertyAttributes.Hidden, typeof(TestResult));
internal static readonly TestProperty ExecutionIdProperty = TestProperty.Register("ExecutionId", ExecutionIdLabel, typeof(Guid), TestPropertyAttributes.Hidden, typeof(TestResult));
internal static readonly TestProperty ParentExecIdProperty = TestProperty.Register("ParentExecId", ParentExecIdLabel, typeof(Guid), TestPropertyAttributes.Hidden, typeof(TestResult));
internal static readonly TestProperty ParentExecIdProperty = TestProperty.Register("ParentExecId", ParentExecIdLabel, typeof(Guid), TestPropertyAttributes.Hidden, typeof(TestResult));
internal static readonly TestProperty InnerResultsCountProperty = TestProperty.Register("InnerResultsCount", InnerResultsCountLabel, typeof(int), TestPropertyAttributes.Hidden, typeof(TestResult));
internal static readonly TestProperty InnerResultsCountProperty = TestProperty.Register("InnerResultsCount", InnerResultsCountLabel, typeof(int), TestPropertyAttributes.Hidden, typeof(TestResult));
internal static readonly TestProperty TestRunIdProperty = TestProperty.Register(TestRunId, TestRunId, typeof(int), TestPropertyAttributes.Hidden, typeof(TestCase));
internal static readonly TestProperty TestRunIdProperty = TestProperty.Register(TestRunId, TestRunId, typeof(int), TestPropertyAttributes.Hidden, typeof(TestCase));
internal static readonly TestProperty TestPlanIdProperty = TestProperty.Register(TestPlanId, TestPlanId, typeof(int), TestPropertyAttributes.Hidden, typeof(TestCase));
internal static readonly TestProperty TestPlanIdProperty = TestProperty.Register(TestPlanId, TestPlanId, typeof(int), TestPropertyAttributes.Hidden, typeof(TestCase));
internal static readonly TestProperty TestCaseIdProperty = TestProperty.Register(TestCaseId, TestCaseId, typeof(int), TestPropertyAttributes.Hidden, typeof(TestCase));
internal static readonly TestProperty TestCaseIdProperty = TestProperty.Register(TestCaseId, TestCaseId, typeof(int), TestPropertyAttributes.Hidden, typeof(TestCase));
internal static readonly TestProperty TestPointIdProperty = TestProperty.Register(TestPointId, TestPointId, typeof(int), TestPropertyAttributes.Hidden, typeof(TestCase));
internal static readonly TestProperty TestPointIdProperty = TestProperty.Register(TestPointId, TestPointId, typeof(int), TestPropertyAttributes.Hidden, typeof(TestCase));
internal static readonly TestProperty TestConfigurationIdProperty = TestProperty.Register(TestConfigurationId, TestConfigurationId, typeof(int), TestPropertyAttributes.Hidden, typeof(TestCase));
internal static readonly TestProperty TestConfigurationIdProperty = TestProperty.Register(TestConfigurationId, TestConfigurationId, typeof(int), TestPropertyAttributes.Hidden, typeof(TestCase));
internal static readonly TestProperty TestConfigurationNameProperty = TestProperty.Register(TestConfigurationName, TestConfigurationName, typeof(string), TestPropertyAttributes.Hidden, typeof(TestCase));
internal static readonly TestProperty TestConfigurationNameProperty = TestProperty.Register(TestConfigurationName, TestConfigurationName, typeof(string), TestPropertyAttributes.Hidden, typeof(TestCase));
internal static readonly TestProperty IsInLabEnvironmentProperty = TestProperty.Register(IsInLabEnvironment, IsInLabEnvironment, typeof(bool), TestPropertyAttributes.Hidden, typeof(TestCase));
internal static readonly TestProperty IsInLabEnvironmentProperty = TestProperty.Register(IsInLabEnvironment, IsInLabEnvironment, typeof(bool), TestPropertyAttributes.Hidden, typeof(TestCase));
internal static readonly TestProperty BuildConfigurationIdProperty = TestProperty.Register(BuildConfigurationId, BuildConfigurationId, typeof(int), TestPropertyAttributes.Hidden, typeof(TestCase));
internal static readonly TestProperty BuildConfigurationIdProperty = TestProperty.Register(BuildConfigurationId, BuildConfigurationId, typeof(int), TestPropertyAttributes.Hidden, typeof(TestCase));
internal static readonly TestProperty BuildDirectoryProperty = TestProperty.Register(BuildDirectory, BuildDirectory, typeof(string), TestPropertyAttributes.Hidden, typeof(TestCase));
internal static readonly TestProperty BuildDirectoryProperty = TestProperty.Register(BuildDirectory, BuildDirectory, typeof(string), TestPropertyAttributes.Hidden, typeof(TestCase));
internal static readonly TestProperty BuildFlavorProperty = TestProperty.Register(BuildFlavor, BuildFlavor, typeof(string), TestPropertyAttributes.Hidden, typeof(TestCase));
internal static readonly TestProperty BuildFlavorProperty = TestProperty.Register(BuildFlavor, BuildFlavor, typeof(string), TestPropertyAttributes.Hidden, typeof(TestCase));
internal static readonly TestProperty BuildNumberProperty = TestProperty.Register(BuildNumber, BuildNumber, typeof(string), TestPropertyAttributes.Hidden, typeof(TestCase));
internal static readonly TestProperty BuildNumberProperty = TestProperty.Register(BuildNumber, BuildNumber, typeof(string), TestPropertyAttributes.Hidden, typeof(TestCase));
internal static readonly TestProperty BuildPlatformProperty = TestProperty.Register(BuildPlatform, BuildPlatform, typeof(string), TestPropertyAttributes.Hidden, typeof(TestCase));
internal static readonly TestProperty BuildPlatformProperty = TestProperty.Register(BuildPlatform, BuildPlatform, typeof(string), TestPropertyAttributes.Hidden, typeof(TestCase));
internal static readonly TestProperty BuildUriProperty = TestProperty.Register(BuildUri, BuildUri, typeof(string), TestPropertyAttributes.Hidden, typeof(TestCase));
internal static readonly TestProperty BuildUriProperty = TestProperty.Register(BuildUri, BuildUri, typeof(string), TestPropertyAttributes.Hidden, typeof(TestCase));
internal static readonly TestProperty TfsServerCollectionUrlProperty = TestProperty.Register(TfsServerCollectionUrl, TfsServerCollectionUrl, typeof(string), TestPropertyAttributes.Hidden, typeof(TestCase));
internal static readonly TestProperty TfsServerCollectionUrlProperty = TestProperty.Register(TfsServerCollectionUrl, TfsServerCollectionUrl, typeof(string), TestPropertyAttributes.Hidden, typeof(TestCase));
internal static readonly TestProperty TfsTeamProjectProperty = TestProperty.Register(TfsTeamProject, TfsTeamProject, typeof(string), TestPropertyAttributes.Hidden, typeof(TestCase));
internal static readonly TestProperty TfsTeamProjectProperty = TestProperty.Register(TfsTeamProject, TfsTeamProject, typeof(string), TestPropertyAttributes.Hidden, typeof(TestCase));
internal static readonly TestProperty TestDynamicDataTypeProperty = TestProperty.Register("MSTest.DynamicDataType", "DynamicDataType", typeof(int), TestPropertyAttributes.Hidden, typeof(TestCase));
internal static readonly TestProperty TestDynamicDataTypeProperty = TestProperty.Register("MSTest.DynamicDataType", "DynamicDataType", typeof(int), TestPropertyAttributes.Hidden, typeof(TestCase));
internal static readonly TestProperty TestDynamicDataProperty = TestProperty.Register("MSTest.DynamicData", "DynamicData", typeof(string[]), TestPropertyAttributes.Hidden, typeof(TestCase));
#endregion
internal static readonly TestProperty TestDynamicDataProperty = TestProperty.Register("MSTest.DynamicData", "DynamicData", typeof(string[]), TestPropertyAttributes.Hidden, typeof(TestCase));
#endregion
#region Private Constants
#region Private Constants
/// <summary>
/// These are the Test properties used by the adapter, which essentially correspond
/// to attributes on tests, and may be available in command line/TeamBuild to filter tests.
/// These Property names should not be localized.
/// </summary>
private const string TestClassNameLabel = "ClassName";
private const string DeclaringClassNameLabel = "DeclaringClassName";
private const string IsAsyncLabel = "IsAsync";
private const string TestCategoryLabel = "TestCategory";
private const string PriorityLabel = "Priority";
private const string DeploymentItemsLabel = "DeploymentItems";
private const string DoNotParallelizeLabel = "DoNotParallelize";
private const string ExecutionIdLabel = "ExecutionId";
private const string ParentExecIdLabel = "ParentExecId";
private const string InnerResultsCountLabel = "InnerResultsCount";
private const string DescriptionLabel = "Description";
private const string CssIterationLabel = "CssIteration";
private const string CssProjectStructureLabel = "CssProjectStructure";
private const string WorkItemIdsLabel = "WorkItemIds";
/// <summary>
/// These are the Test properties used by the adapter, which essentially correspond
/// to attributes on tests, and may be available in command line/TeamBuild to filter tests.
/// These Property names should not be localized.
/// </summary>
private const string TestClassNameLabel = "ClassName";
private const string DeclaringClassNameLabel = "DeclaringClassName";
private const string IsAsyncLabel = "IsAsync";
private const string TestCategoryLabel = "TestCategory";
private const string PriorityLabel = "Priority";
private const string DeploymentItemsLabel = "DeploymentItems";
private const string DoNotParallelizeLabel = "DoNotParallelize";
private const string ExecutionIdLabel = "ExecutionId";
private const string ParentExecIdLabel = "ParentExecId";
private const string InnerResultsCountLabel = "InnerResultsCount";
private const string DescriptionLabel = "Description";
private const string CssIterationLabel = "CssIteration";
private const string CssProjectStructureLabel = "CssProjectStructure";
private const string WorkItemIdsLabel = "WorkItemIds";
private const string TestRunId = "__Tfs_TestRunId__";
private const string TestPlanId = "__Tfs_TestPlanId__";
private const string TestCaseId = "__Tfs_TestCaseId__";
private const string TestPointId = "__Tfs_TestPointId__";
private const string TestConfigurationId = "__Tfs_TestConfigurationId__";
private const string TestConfigurationName = "__Tfs_TestConfigurationName__";
private const string IsInLabEnvironment = "__Tfs_IsInLabEnvironment__";
private const string BuildConfigurationId = "__Tfs_BuildConfigurationId__";
private const string BuildDirectory = "__Tfs_BuildDirectory__";
private const string BuildFlavor = "__Tfs_BuildFlavor__";
private const string BuildNumber = "__Tfs_BuildNumber__";
private const string BuildPlatform = "__Tfs_BuildPlatform__";
private const string BuildUri = "__Tfs_BuildUri__";
private const string TfsServerCollectionUrl = "__Tfs_TfsServerCollectionUrl__";
private const string TfsTeamProject = "__Tfs_TeamProject__";
private const string TestRunId = "__Tfs_TestRunId__";
private const string TestPlanId = "__Tfs_TestPlanId__";
private const string TestCaseId = "__Tfs_TestCaseId__";
private const string TestPointId = "__Tfs_TestPointId__";
private const string TestConfigurationId = "__Tfs_TestConfigurationId__";
private const string TestConfigurationName = "__Tfs_TestConfigurationName__";
private const string IsInLabEnvironment = "__Tfs_IsInLabEnvironment__";
private const string BuildConfigurationId = "__Tfs_BuildConfigurationId__";
private const string BuildDirectory = "__Tfs_BuildDirectory__";
private const string BuildFlavor = "__Tfs_BuildFlavor__";
private const string BuildNumber = "__Tfs_BuildNumber__";
private const string BuildPlatform = "__Tfs_BuildPlatform__";
private const string BuildUri = "__Tfs_BuildUri__";
private const string TfsServerCollectionUrl = "__Tfs_TfsServerCollectionUrl__";
private const string TfsTeamProject = "__Tfs_TeamProject__";
#endregion
}
#endregion
}

Просмотреть файл

@ -1,417 +1,416 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
namespace Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.Discovery
namespace Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.Discovery;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.Linq;
using System.Reflection;
using System.Runtime.Serialization;
using System.Security;
using System.Text;
using Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.Execution;
using Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.Helpers;
using Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.ObjectModel;
using Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices;
using Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.Interface;
using UTF = Microsoft.VisualStudio.TestTools.UnitTesting;
/// <summary>
/// Enumerates through all types in the assembly in search of valid test methods.
/// </summary>
internal class AssemblyEnumerator : MarshalByRefObject
{
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.Linq;
using System.Reflection;
using System.Runtime.Serialization;
using System.Security;
using System.Text;
/// <summary>
/// Helper for reflection API's.
/// </summary>
private static readonly ReflectHelper ReflectHelper = ReflectHelper.Instance;
using Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.Execution;
using Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.Helpers;
using Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.ObjectModel;
using Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices;
using Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.Interface;
/// <summary>
/// Type cache
/// </summary>
private readonly TypeCache typeCache = new(ReflectHelper);
using UTF = Microsoft.VisualStudio.TestTools.UnitTesting;
/// <summary>
/// Initializes a new instance of the <see cref="AssemblyEnumerator"/> class.
/// </summary>
public AssemblyEnumerator()
{
}
/// <summary>
/// Initializes a new instance of the <see cref="AssemblyEnumerator"/> class.
/// </summary>
/// <param name="settings">The settings for the session.</param>
/// <remarks>Use this constructor when creating this object in a new app domain so the settings for this app domain are set.</remarks>
public AssemblyEnumerator(MSTestSettings settings)
{
// Populate the settings into the domain(Desktop workflow) performing discovery.
// This would just be resettings the settings to itself in non desktop workflows.
MSTestSettings.PopulateSettings(settings);
}
/// <summary>
/// Gets or sets the run settings to use for current discovery session.
/// </summary>
public string RunSettingsXml { get; set; }
/// <summary>
/// Returns object to be used for controlling lifetime, null means infinite lifetime.
/// </summary>
/// <returns>
/// The <see cref="object"/>.
/// </returns>
[SecurityCritical]
#if NET5_0_OR_GREATER
[Obsolete]
#endif
public override object InitializeLifetimeService()
{
return null;
}
/// <summary>
/// Enumerates through all types in the assembly in search of valid test methods.
/// </summary>
internal class AssemblyEnumerator : MarshalByRefObject
/// <param name="assemblyFileName">The assembly file name.</param>
/// <param name="warnings">Contains warnings if any, that need to be passed back to the caller.</param>
/// <returns>A collection of Test Elements.</returns>
internal ICollection<UnitTestElement> EnumerateAssembly(string assemblyFileName, out ICollection<string> warnings)
{
/// <summary>
/// Helper for reflection API's.
/// </summary>
private static readonly ReflectHelper ReflectHelper = ReflectHelper.Instance;
Debug.Assert(!string.IsNullOrWhiteSpace(assemblyFileName), "Invalid assembly file name.");
/// <summary>
/// Type cache
/// </summary>
private readonly TypeCache typeCache = new(ReflectHelper);
var runSettingsXml = this.RunSettingsXml;
var warningMessages = new List<string>();
var tests = new List<UnitTestElement>();
/// <summary>
/// Initializes a new instance of the <see cref="AssemblyEnumerator"/> class.
/// </summary>
public AssemblyEnumerator()
var assembly = PlatformServiceProvider.Instance.FileOperations.LoadAssembly(assemblyFileName, isReflectionOnly: false);
var types = this.GetTypes(assembly, assemblyFileName, warningMessages);
var discoverInternals = assembly.GetCustomAttribute<UTF.DiscoverInternalsAttribute>() != null;
var testDataSourceDiscovery = assembly.GetCustomAttribute<UTF.TestDataSourceDiscoveryAttribute>()?.DiscoveryOption ?? UTF.TestDataSourceDiscoveryOption.DuringDiscovery;
foreach (var type in types)
{
}
/// <summary>
/// Initializes a new instance of the <see cref="AssemblyEnumerator"/> class.
/// </summary>
/// <param name="settings">The settings for the session.</param>
/// <remarks>Use this constructor when creating this object in a new app domain so the settings for this app domain are set.</remarks>
public AssemblyEnumerator(MSTestSettings settings)
{
// Populate the settings into the domain(Desktop workflow) performing discovery.
// This would just be resettings the settings to itself in non desktop workflows.
MSTestSettings.PopulateSettings(settings);
}
/// <summary>
/// Gets or sets the run settings to use for current discovery session.
/// </summary>
public string RunSettingsXml { get; set; }
/// <summary>
/// Returns object to be used for controlling lifetime, null means infinite lifetime.
/// </summary>
/// <returns>
/// The <see cref="object"/>.
/// </returns>
[SecurityCritical]
#if NET5_0_OR_GREATER
[Obsolete]
#endif
public override object InitializeLifetimeService()
{
return null;
}
/// <summary>
/// Enumerates through all types in the assembly in search of valid test methods.
/// </summary>
/// <param name="assemblyFileName">The assembly file name.</param>
/// <param name="warnings">Contains warnings if any, that need to be passed back to the caller.</param>
/// <returns>A collection of Test Elements.</returns>
internal ICollection<UnitTestElement> EnumerateAssembly(string assemblyFileName, out ICollection<string> warnings)
{
Debug.Assert(!string.IsNullOrWhiteSpace(assemblyFileName), "Invalid assembly file name.");
var runSettingsXml = this.RunSettingsXml;
var warningMessages = new List<string>();
var tests = new List<UnitTestElement>();
var assembly = PlatformServiceProvider.Instance.FileOperations.LoadAssembly(assemblyFileName, isReflectionOnly: false);
var types = this.GetTypes(assembly, assemblyFileName, warningMessages);
var discoverInternals = assembly.GetCustomAttribute<UTF.DiscoverInternalsAttribute>() != null;
var testDataSourceDiscovery = assembly.GetCustomAttribute<UTF.TestDataSourceDiscoveryAttribute>()?.DiscoveryOption ?? UTF.TestDataSourceDiscoveryOption.DuringDiscovery;
foreach (var type in types)
if (type == null)
{
if (type == null)
{
continue;
}
var testsInType = this.DiscoverTestsInType(assemblyFileName, runSettingsXml, assembly, type, warningMessages, discoverInternals, testDataSourceDiscovery);
tests.AddRange(testsInType);
continue;
}
warnings = warningMessages;
return tests;
var testsInType = this.DiscoverTestsInType(assemblyFileName, runSettingsXml, assembly, type, warningMessages, discoverInternals, testDataSourceDiscovery);
tests.AddRange(testsInType);
}
/// <summary>
/// Gets the types defined in an assembly.
/// </summary>
/// <param name="assembly">The reflected assembly.</param>
/// <param name="assemblyFileName">The file name of the assembly.</param>
/// <param name="warningMessages">Contains warnings if any, that need to be passed back to the caller.</param>
/// <returns>Gets the types defined in the provided assembly.</returns>
internal Type[] GetTypes(Assembly assembly, string assemblyFileName, ICollection<string> warningMessages)
warnings = warningMessages;
return tests;
}
/// <summary>
/// Gets the types defined in an assembly.
/// </summary>
/// <param name="assembly">The reflected assembly.</param>
/// <param name="assemblyFileName">The file name of the assembly.</param>
/// <param name="warningMessages">Contains warnings if any, that need to be passed back to the caller.</param>
/// <returns>Gets the types defined in the provided assembly.</returns>
internal Type[] GetTypes(Assembly assembly, string assemblyFileName, ICollection<string> warningMessages)
{
var types = new List<Type>();
try
{
var types = new List<Type>();
try
{
types.AddRange(assembly.DefinedTypes.Select(typeinfo => typeinfo.AsType()));
}
catch (ReflectionTypeLoadException ex)
{
PlatformServiceProvider.Instance.AdapterTraceLogger.LogWarning($"MSTestExecutor.TryGetTests: {Resource.TestAssembly_AssemblyDiscoveryFailure}", assemblyFileName, ex);
PlatformServiceProvider.Instance.AdapterTraceLogger.LogWarning(Resource.ExceptionsThrown);
if (ex.LoaderExceptions != null)
{
// If not able to load all type, log a warning and continue with loaded types.
var message = string.Format(CultureInfo.CurrentCulture, Resource.TypeLoadFailed, assemblyFileName, this.GetLoadExceptionDetails(ex));
warningMessages?.Add(message);
foreach (var loaderEx in ex.LoaderExceptions)
{
PlatformServiceProvider.Instance.AdapterTraceLogger.LogWarning("{0}", loaderEx);
}
}
return ex.Types;
}
return types.ToArray();
types.AddRange(assembly.DefinedTypes.Select(typeinfo => typeinfo.AsType()));
}
/// <summary>
/// Formats load exception as multi-line string, each line contains load error message.
/// </summary>
/// <param name="ex">The exception.</param>
/// <returns>Returns loader exceptions as a multi-line string.</returns>
internal string GetLoadExceptionDetails(ReflectionTypeLoadException ex)
catch (ReflectionTypeLoadException ex)
{
Debug.Assert(ex != null, "exception should not be null.");
var map = new Dictionary<string, object>(StringComparer.OrdinalIgnoreCase); // Exception -> null.
var errorDetails = new StringBuilder();
PlatformServiceProvider.Instance.AdapterTraceLogger.LogWarning($"MSTestExecutor.TryGetTests: {Resource.TestAssembly_AssemblyDiscoveryFailure}", assemblyFileName, ex);
PlatformServiceProvider.Instance.AdapterTraceLogger.LogWarning(Resource.ExceptionsThrown);
if (ex.LoaderExceptions != null)
{
// Loader exceptions can contain duplicates, leave only unique exceptions.
foreach (var loaderException in ex.LoaderExceptions)
// If not able to load all type, log a warning and continue with loaded types.
var message = string.Format(CultureInfo.CurrentCulture, Resource.TypeLoadFailed, assemblyFileName, this.GetLoadExceptionDetails(ex));
warningMessages?.Add(message);
foreach (var loaderEx in ex.LoaderExceptions)
{
Debug.Assert(loaderException != null, "loader exception should not be null.");
var line = string.Format(CultureInfo.CurrentCulture, Resource.EnumeratorLoadTypeErrorFormat, loaderException.GetType(), loaderException.Message);
if (!map.ContainsKey(line))
{
map.Add(line, null);
errorDetails.AppendLine(line);
}
PlatformServiceProvider.Instance.AdapterTraceLogger.LogWarning("{0}", loaderEx);
}
}
else
return ex.Types;
}
return types.ToArray();
}
/// <summary>
/// Formats load exception as multi-line string, each line contains load error message.
/// </summary>
/// <param name="ex">The exception.</param>
/// <returns>Returns loader exceptions as a multi-line string.</returns>
internal string GetLoadExceptionDetails(ReflectionTypeLoadException ex)
{
Debug.Assert(ex != null, "exception should not be null.");
var map = new Dictionary<string, object>(StringComparer.OrdinalIgnoreCase); // Exception -> null.
var errorDetails = new StringBuilder();
if (ex.LoaderExceptions != null)
{
// Loader exceptions can contain duplicates, leave only unique exceptions.
foreach (var loaderException in ex.LoaderExceptions)
{
errorDetails.AppendLine(ex.Message);
Debug.Assert(loaderException != null, "loader exception should not be null.");
var line = string.Format(CultureInfo.CurrentCulture, Resource.EnumeratorLoadTypeErrorFormat, loaderException.GetType(), loaderException.Message);
if (!map.ContainsKey(line))
{
map.Add(line, null);
errorDetails.AppendLine(line);
}
}
}
else
{
errorDetails.AppendLine(ex.Message);
}
return errorDetails.ToString();
}
/// <summary>
/// Returns an instance of the <see cref="TypeEnumerator"/> class.
/// </summary>
/// <param name="type">The type to enumerate.</param>
/// <param name="assemblyFileName">The reflected assembly name.</param>
/// <param name="discoverInternals">True to discover test classes which are declared internal in
/// addition to test classes which are declared public.</param>
/// <returns>a TypeEnumerator instance.</returns>
internal virtual TypeEnumerator GetTypeEnumerator(Type type, string assemblyFileName, bool discoverInternals = false)
{
var typeValidator = new TypeValidator(ReflectHelper, discoverInternals);
var testMethodValidator = new TestMethodValidator(ReflectHelper, discoverInternals);
return new TypeEnumerator(type, assemblyFileName, ReflectHelper, typeValidator, testMethodValidator);
}
private IEnumerable<UnitTestElement> DiscoverTestsInType(string assemblyFileName, string runSettingsXml, Assembly assembly, Type type, List<string> warningMessages, bool discoverInternals = false, UTF.TestDataSourceDiscoveryOption discoveryOption = UTF.TestDataSourceDiscoveryOption.DuringExecution)
{
var sourceLevelParameters = PlatformServiceProvider.Instance.SettingsProvider.GetProperties(assemblyFileName);
sourceLevelParameters = RunSettingsUtilities.GetTestRunParameters(runSettingsXml)?.ConcatWithOverwrites(sourceLevelParameters)
?? sourceLevelParameters
?? new Dictionary<string, object>();
string typeFullName = null;
var tests = new List<UnitTestElement>();
try
{
typeFullName = type.FullName;
var testTypeEnumerator = this.GetTypeEnumerator(type, assemblyFileName, discoverInternals);
var unitTestCases = testTypeEnumerator.Enumerate(out var warningsFromTypeEnumerator);
var typeIgnored = ReflectHelper.IsAttributeDefined(type, typeof(UTF.IgnoreAttribute), false);
if (warningsFromTypeEnumerator != null)
{
warningMessages.AddRange(warningsFromTypeEnumerator);
}
return errorDetails.ToString();
}
/// <summary>
/// Returns an instance of the <see cref="TypeEnumerator"/> class.
/// </summary>
/// <param name="type">The type to enumerate.</param>
/// <param name="assemblyFileName">The reflected assembly name.</param>
/// <param name="discoverInternals">True to discover test classes which are declared internal in
/// addition to test classes which are declared public.</param>
/// <returns>a TypeEnumerator instance.</returns>
internal virtual TypeEnumerator GetTypeEnumerator(Type type, string assemblyFileName, bool discoverInternals = false)
{
var typeValidator = new TypeValidator(ReflectHelper, discoverInternals);
var testMethodValidator = new TestMethodValidator(ReflectHelper, discoverInternals);
return new TypeEnumerator(type, assemblyFileName, ReflectHelper, typeValidator, testMethodValidator);
}
private IEnumerable<UnitTestElement> DiscoverTestsInType(string assemblyFileName, string runSettingsXml, Assembly assembly, Type type, List<string> warningMessages, bool discoverInternals = false, UTF.TestDataSourceDiscoveryOption discoveryOption = UTF.TestDataSourceDiscoveryOption.DuringExecution)
{
var sourceLevelParameters = PlatformServiceProvider.Instance.SettingsProvider.GetProperties(assemblyFileName);
sourceLevelParameters = RunSettingsUtilities.GetTestRunParameters(runSettingsXml)?.ConcatWithOverwrites(sourceLevelParameters)
?? sourceLevelParameters
?? new Dictionary<string, object>();
string typeFullName = null;
var tests = new List<UnitTestElement>();
try
if (unitTestCases != null)
{
typeFullName = type.FullName;
var testTypeEnumerator = this.GetTypeEnumerator(type, assemblyFileName, discoverInternals);
var unitTestCases = testTypeEnumerator.Enumerate(out var warningsFromTypeEnumerator);
var typeIgnored = ReflectHelper.IsAttributeDefined(type, typeof(UTF.IgnoreAttribute), false);
if (warningsFromTypeEnumerator != null)
foreach (var test in unitTestCases)
{
warningMessages.AddRange(warningsFromTypeEnumerator);
}
if (unitTestCases != null)
{
foreach (var test in unitTestCases)
if (discoveryOption == UTF.TestDataSourceDiscoveryOption.DuringDiscovery)
{
if (discoveryOption == UTF.TestDataSourceDiscoveryOption.DuringDiscovery)
if (this.DynamicDataAttached(sourceLevelParameters, assembly, test, tests))
{
if (this.DynamicDataAttached(sourceLevelParameters, assembly, test, tests))
{
continue;
}
continue;
}
tests.Add(test);
}
}
}
catch (Exception exception)
{
// If we fail to discover type from a class, then don't abort the discovery
// Move to the next type.
string message = string.Format(CultureInfo.CurrentCulture, Resource.CouldNotInspectTypeDuringDiscovery, typeFullName, assemblyFileName, exception.Message);
PlatformServiceProvider.Instance.AdapterTraceLogger.LogInfo($"AssemblyEnumerator: {message}");
warningMessages.Add(message);
}
return tests;
}
private bool DynamicDataAttached(IDictionary<string, object> sourceLevelParameters, Assembly assembly, UnitTestElement test, List<UnitTestElement> tests)
{
// It should always be `true`, but if any part of the chain is obsolete; it might not contain those.
// Since we depend on those properties, if they don't exist, we bail out early.
if (!test.TestMethod.HasManagedMethodAndTypeProperties)
{
return false;
}
using var writer = new ThreadSafeStringWriter(CultureInfo.InvariantCulture, "all");
var testMethod = test.TestMethod;
var testContext = PlatformServiceProvider.Instance.GetTestContext(testMethod, writer, sourceLevelParameters);
var testMethodInfo = this.typeCache.GetTestMethodInfo(testMethod, testContext, MSTestSettings.CurrentSettings.CaptureDebugTraces);
if (testMethodInfo == null)
{
return false;
}
return /* DataSourceAttribute discovery is disabled for now, since we cannot serialize DataRow values.
this.TryProcessDataSource(test, testMethodInfo, testContext, tests) || */
this.TryProcessTestDataSourceTests(test, testMethodInfo, tests);
}
private bool TryProcessDataSource(UnitTestElement test, TestMethodInfo testMethodInfo, ITestContext testContext, List<UnitTestElement> tests)
{
var dataSourceAttributes = ReflectHelper.GetAttributes<UTF.DataSourceAttribute>(testMethodInfo.MethodInfo, false);
if (dataSourceAttributes == null)
{
return false;
}
if (dataSourceAttributes.Length > 1)
{
var message = string.Format(CultureInfo.CurrentCulture, Resource.CannotEnumerateDataSourceAttribute_MoreThenOneDefined, test.TestMethod.ManagedTypeName, test.TestMethod.ManagedMethodName, dataSourceAttributes.Length);
PlatformServiceProvider.Instance.AdapterTraceLogger.LogInfo($"DynamicDataEnumarator: {message}");
throw new InvalidOperationException(message);
}
// dataSourceAttributes.Length == 1
try
{
return this.ProcessDataSourceTests(test, testMethodInfo, testContext, tests);
}
catch (Exception ex)
{
var message = string.Format(CultureInfo.CurrentCulture, Resource.CannotEnumerateDataSourceAttribute, test.TestMethod.ManagedTypeName, test.TestMethod.ManagedMethodName, ex);
PlatformServiceProvider.Instance.AdapterTraceLogger.LogInfo($"DynamicDataEnumarator: {message}");
return false;
}
}
private bool ProcessDataSourceTests(UnitTestElement test, TestMethodInfo testMethodInfo, ITestContext testContext, List<UnitTestElement> tests)
{
var dataRows = PlatformServiceProvider.Instance.TestDataSource.GetData(testMethodInfo, testContext);
if (dataRows == null || !dataRows.Any())
{
return false;
}
try
{
int rowIndex = 0;
foreach (var dataRow in dataRows)
{
// TODO: Test serialization
rowIndex++;
var displayName = string.Format(CultureInfo.CurrentCulture, Resource.DataDrivenResultDisplayName, test.DisplayName, rowIndex);
var discoveredTest = test.Clone();
discoveredTest.DisplayName = displayName;
discoveredTest.TestMethod.DataType = DynamicDataType.DataSourceAttribute;
discoveredTest.TestMethod.SerializedData = DataSerializationHelper.Serialize(new[] { (object)rowIndex });
tests.Add(discoveredTest);
}
return true;
}
finally
{
testContext.SetDataConnection(null);
testContext.SetDataRow(null);
}
}
private bool TryProcessTestDataSourceTests(UnitTestElement test, TestMethodInfo testMethodInfo, List<UnitTestElement> tests)
{
var methodInfo = testMethodInfo.MethodInfo;
var testDataSources = ReflectHelper.GetAttributes<Attribute>(methodInfo, false)?.Where(a => a is UTF.ITestDataSource).OfType<UTF.ITestDataSource>().ToArray();
if (testDataSources == null || testDataSources.Length == 0)
{
return false;
}
try
{
return this.ProcessTestDataSourceTests(test, (MethodInfo)methodInfo, testDataSources, tests);
}
catch (Exception ex)
{
var message = string.Format(CultureInfo.CurrentCulture, Resource.CannotEnumerateIDataSourceAttribute, test.TestMethod.ManagedTypeName, test.TestMethod.ManagedMethodName, ex);
PlatformServiceProvider.Instance.AdapterTraceLogger.LogInfo($"DynamicDataEnumarator: {message}");
return false;
}
}
private bool ProcessTestDataSourceTests(UnitTestElement test, MethodInfo methodInfo, UTF.ITestDataSource[] testDataSources, List<UnitTestElement> tests)
{
foreach (var dataSource in testDataSources)
{
var data = dataSource.GetData(methodInfo);
var discoveredTests = new Dictionary<string, UnitTestElement>();
var discoveredIndex = new Dictionary<string, int>();
var serializationFailed = false;
var index = 0;
foreach (var d in data)
{
var discoveredTest = test.Clone();
discoveredTest.DisplayName = dataSource.GetDisplayName(methodInfo, d) ?? discoveredTest.DisplayName;
// if we have a duplicate test name don't expand the test, bail out.
if (discoveredTests.ContainsKey(discoveredTest.DisplayName))
{
var firstSeen = discoveredIndex[discoveredTest.DisplayName];
var warning = string.Format(CultureInfo.CurrentCulture, Resource.CannotExpandIDataSourceAttribute_DuplicateDisplayName, firstSeen, index, discoveredTest.DisplayName);
warning = string.Format(CultureInfo.CurrentUICulture, Resource.CannotExpandIDataSourceAttribute, test.TestMethod.ManagedTypeName, test.TestMethod.ManagedMethodName, warning);
PlatformServiceProvider.Instance.AdapterTraceLogger.LogWarning($"DynamicDataEnumarator: {warning}");
serializationFailed = true;
break;
}
try
{
discoveredTest.TestMethod.SerializedData = DataSerializationHelper.Serialize(d);
discoveredTest.TestMethod.DataType = DynamicDataType.ITestDataSource;
}
catch (SerializationException)
{
var firstSeen = discoveredIndex[discoveredTest.DisplayName];
var warning = string.Format(CultureInfo.CurrentCulture, Resource.CannotExpandIDataSourceAttribute_CannotSerialize, index, discoveredTest.DisplayName);
warning = string.Format(CultureInfo.CurrentUICulture, Resource.CannotExpandIDataSourceAttribute, test.TestMethod.ManagedTypeName, test.TestMethod.ManagedMethodName, warning);
PlatformServiceProvider.Instance.AdapterTraceLogger.LogWarning($"DynamicDataEnumarator: {warning}");
serializationFailed = true;
break;
}
discoveredTests[discoveredTest.DisplayName] = discoveredTest;
discoveredIndex[discoveredTest.DisplayName] = index++;
}
// Serialization failed for the type, bail out.
if (serializationFailed)
{
tests.Add(test);
break;
}
}
}
catch (Exception exception)
{
// If we fail to discover type from a class, then don't abort the discovery
// Move to the next type.
string message = string.Format(CultureInfo.CurrentCulture, Resource.CouldNotInspectTypeDuringDiscovery, typeFullName, assemblyFileName, exception.Message);
PlatformServiceProvider.Instance.AdapterTraceLogger.LogInfo($"AssemblyEnumerator: {message}");
warningMessages.Add(message);
}
tests.AddRange(discoveredTests.Values);
return tests;
}
private bool DynamicDataAttached(IDictionary<string, object> sourceLevelParameters, Assembly assembly, UnitTestElement test, List<UnitTestElement> tests)
{
// It should always be `true`, but if any part of the chain is obsolete; it might not contain those.
// Since we depend on those properties, if they don't exist, we bail out early.
if (!test.TestMethod.HasManagedMethodAndTypeProperties)
{
return false;
}
using var writer = new ThreadSafeStringWriter(CultureInfo.InvariantCulture, "all");
var testMethod = test.TestMethod;
var testContext = PlatformServiceProvider.Instance.GetTestContext(testMethod, writer, sourceLevelParameters);
var testMethodInfo = this.typeCache.GetTestMethodInfo(testMethod, testContext, MSTestSettings.CurrentSettings.CaptureDebugTraces);
if (testMethodInfo == null)
{
return false;
}
return /* DataSourceAttribute discovery is disabled for now, since we cannot serialize DataRow values.
this.TryProcessDataSource(test, testMethodInfo, testContext, tests) || */
this.TryProcessTestDataSourceTests(test, testMethodInfo, tests);
}
private bool TryProcessDataSource(UnitTestElement test, TestMethodInfo testMethodInfo, ITestContext testContext, List<UnitTestElement> tests)
{
var dataSourceAttributes = ReflectHelper.GetAttributes<UTF.DataSourceAttribute>(testMethodInfo.MethodInfo, false);
if (dataSourceAttributes == null)
{
return false;
}
if (dataSourceAttributes.Length > 1)
{
var message = string.Format(CultureInfo.CurrentCulture, Resource.CannotEnumerateDataSourceAttribute_MoreThenOneDefined, test.TestMethod.ManagedTypeName, test.TestMethod.ManagedMethodName, dataSourceAttributes.Length);
PlatformServiceProvider.Instance.AdapterTraceLogger.LogInfo($"DynamicDataEnumarator: {message}");
throw new InvalidOperationException(message);
}
// dataSourceAttributes.Length == 1
try
{
return this.ProcessDataSourceTests(test, testMethodInfo, testContext, tests);
}
catch (Exception ex)
{
var message = string.Format(CultureInfo.CurrentCulture, Resource.CannotEnumerateDataSourceAttribute, test.TestMethod.ManagedTypeName, test.TestMethod.ManagedMethodName, ex);
PlatformServiceProvider.Instance.AdapterTraceLogger.LogInfo($"DynamicDataEnumarator: {message}");
return false;
}
}
private bool ProcessDataSourceTests(UnitTestElement test, TestMethodInfo testMethodInfo, ITestContext testContext, List<UnitTestElement> tests)
{
var dataRows = PlatformServiceProvider.Instance.TestDataSource.GetData(testMethodInfo, testContext);
if (dataRows == null || !dataRows.Any())
{
return false;
}
try
{
int rowIndex = 0;
foreach (var dataRow in dataRows)
{
// TODO: Test serialization
rowIndex++;
var displayName = string.Format(CultureInfo.CurrentCulture, Resource.DataDrivenResultDisplayName, test.DisplayName, rowIndex);
var discoveredTest = test.Clone();
discoveredTest.DisplayName = displayName;
discoveredTest.TestMethod.DataType = DynamicDataType.DataSourceAttribute;
discoveredTest.TestMethod.SerializedData = DataSerializationHelper.Serialize(new[] { (object)rowIndex });
tests.Add(discoveredTest);
}
return true;
}
finally
{
testContext.SetDataConnection(null);
testContext.SetDataRow(null);
}
}
private bool TryProcessTestDataSourceTests(UnitTestElement test, TestMethodInfo testMethodInfo, List<UnitTestElement> tests)
{
var methodInfo = testMethodInfo.MethodInfo;
var testDataSources = ReflectHelper.GetAttributes<Attribute>(methodInfo, false)?.Where(a => a is UTF.ITestDataSource).OfType<UTF.ITestDataSource>().ToArray();
if (testDataSources == null || testDataSources.Length == 0)
{
return false;
}
try
{
return this.ProcessTestDataSourceTests(test, (MethodInfo)methodInfo, testDataSources, tests);
}
catch (Exception ex)
{
var message = string.Format(CultureInfo.CurrentCulture, Resource.CannotEnumerateIDataSourceAttribute, test.TestMethod.ManagedTypeName, test.TestMethod.ManagedMethodName, ex);
PlatformServiceProvider.Instance.AdapterTraceLogger.LogInfo($"DynamicDataEnumarator: {message}");
return false;
}
}
private bool ProcessTestDataSourceTests(UnitTestElement test, MethodInfo methodInfo, UTF.ITestDataSource[] testDataSources, List<UnitTestElement> tests)
{
foreach (var dataSource in testDataSources)
{
var data = dataSource.GetData(methodInfo);
var discoveredTests = new Dictionary<string, UnitTestElement>();
var discoveredIndex = new Dictionary<string, int>();
var serializationFailed = false;
var index = 0;
foreach (var d in data)
{
var discoveredTest = test.Clone();
discoveredTest.DisplayName = dataSource.GetDisplayName(methodInfo, d) ?? discoveredTest.DisplayName;
// if we have a duplicate test name don't expand the test, bail out.
if (discoveredTests.ContainsKey(discoveredTest.DisplayName))
{
var firstSeen = discoveredIndex[discoveredTest.DisplayName];
var warning = string.Format(CultureInfo.CurrentCulture, Resource.CannotExpandIDataSourceAttribute_DuplicateDisplayName, firstSeen, index, discoveredTest.DisplayName);
warning = string.Format(CultureInfo.CurrentUICulture, Resource.CannotExpandIDataSourceAttribute, test.TestMethod.ManagedTypeName, test.TestMethod.ManagedMethodName, warning);
PlatformServiceProvider.Instance.AdapterTraceLogger.LogWarning($"DynamicDataEnumarator: {warning}");
serializationFailed = true;
break;
}
try
{
discoveredTest.TestMethod.SerializedData = DataSerializationHelper.Serialize(d);
discoveredTest.TestMethod.DataType = DynamicDataType.ITestDataSource;
}
catch (SerializationException)
{
var firstSeen = discoveredIndex[discoveredTest.DisplayName];
var warning = string.Format(CultureInfo.CurrentCulture, Resource.CannotExpandIDataSourceAttribute_CannotSerialize, index, discoveredTest.DisplayName);
warning = string.Format(CultureInfo.CurrentUICulture, Resource.CannotExpandIDataSourceAttribute, test.TestMethod.ManagedTypeName, test.TestMethod.ManagedMethodName, warning);
PlatformServiceProvider.Instance.AdapterTraceLogger.LogWarning($"DynamicDataEnumarator: {warning}");
serializationFailed = true;
break;
}
discoveredTests[discoveredTest.DisplayName] = discoveredTest;
discoveredIndex[discoveredTest.DisplayName] = index++;
}
// Serialization failed for the type, bail out.
if (serializationFailed)
{
tests.Add(test);
break;
}
tests.AddRange(discoveredTests.Values);
}
return true;
}
}

Просмотреть файл

@ -1,123 +1,122 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
namespace Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.Discovery
{
using System;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Reflection;
namespace Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.Discovery;
using Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.ObjectModel;
using Microsoft.VisualStudio.TestPlatform.ObjectModel.Adapter;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using System;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Reflection;
using Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.ObjectModel;
using Microsoft.VisualStudio.TestPlatform.ObjectModel.Adapter;
using Microsoft.VisualStudio.TestTools.UnitTesting;
/// <summary>
/// Enumerates through an assembly to get a set of test methods.
/// </summary>
internal class AssemblyEnumeratorWrapper
{
/// <summary>
/// Assembly name for UTF
/// </summary>
private static readonly AssemblyName UnitTestFrameworkAssemblyName = typeof(TestMethodAttribute).GetTypeInfo().Assembly.GetName();
/// <summary>
/// Enumerates through an assembly to get a set of test methods.
/// Gets test elements from an assembly.
/// </summary>
internal class AssemblyEnumeratorWrapper
/// <param name="assemblyFileName"> The assembly file name. </param>
/// <param name="runSettings"> The run Settings. </param>
/// <param name="warnings"> Contains warnings if any, that need to be passed back to the caller. </param>
/// <returns> A collection of test elements. </returns>
internal ICollection<UnitTestElement> GetTests(string assemblyFileName, IRunSettings runSettings, out ICollection<string> warnings)
{
/// <summary>
/// Assembly name for UTF
/// </summary>
private static readonly AssemblyName UnitTestFrameworkAssemblyName = typeof(TestMethodAttribute).GetTypeInfo().Assembly.GetName();
warnings = new List<string>();
/// <summary>
/// Gets test elements from an assembly.
/// </summary>
/// <param name="assemblyFileName"> The assembly file name. </param>
/// <param name="runSettings"> The run Settings. </param>
/// <param name="warnings"> Contains warnings if any, that need to be passed back to the caller. </param>
/// <returns> A collection of test elements. </returns>
internal ICollection<UnitTestElement> GetTests(string assemblyFileName, IRunSettings runSettings, out ICollection<string> warnings)
if (string.IsNullOrEmpty(assemblyFileName))
{
warnings = new List<string>();
if (string.IsNullOrEmpty(assemblyFileName))
{
return null;
}
var fullFilePath = PlatformServiceProvider.Instance.FileOperations.GetFullFilePath(assemblyFileName);
try
{
if (!PlatformServiceProvider.Instance.FileOperations.DoesFileExist(fullFilePath))
{
var message = string.Format(CultureInfo.CurrentCulture, Resource.TestAssembly_FileDoesNotExist, fullFilePath);
throw new FileNotFoundException(message);
}
if (!PlatformServiceProvider.Instance.TestSource.IsAssemblyReferenced(UnitTestFrameworkAssemblyName, fullFilePath))
{
return null;
}
// Load the assembly in isolation if required.
return this.GetTestsInIsolation(fullFilePath, runSettings, out warnings);
}
catch (FileNotFoundException ex)
{
var message = string.Format(CultureInfo.CurrentCulture, Resource.TestAssembly_AssemblyDiscoveryFailure, fullFilePath, ex.Message);
PlatformServiceProvider.Instance.AdapterTraceLogger.LogWarning($"{nameof(AssemblyEnumeratorWrapper)}.{nameof(this.GetTests)}: {Resource.TestAssembly_AssemblyDiscoveryFailure}", fullFilePath, ex);
warnings.Add(message);
return null;
}
catch (ReflectionTypeLoadException ex)
{
var message = string.Format(CultureInfo.CurrentCulture, Resource.TestAssembly_AssemblyDiscoveryFailure, fullFilePath, ex.Message);
PlatformServiceProvider.Instance.AdapterTraceLogger.LogWarning($"{nameof(AssemblyEnumeratorWrapper)}.{nameof(this.GetTests)}: {Resource.TestAssembly_AssemblyDiscoveryFailure}", fullFilePath, ex);
PlatformServiceProvider.Instance.AdapterTraceLogger.LogWarning(Resource.ExceptionsThrown);
warnings.Add(message);
if (ex.LoaderExceptions != null)
{
foreach (var loaderEx in ex.LoaderExceptions)
{
PlatformServiceProvider.Instance.AdapterTraceLogger.LogWarning("{0}", loaderEx);
}
}
return null;
}
catch (BadImageFormatException)
{
// Ignore BadImageFormatException when loading native dll in managed adapter.
return null;
}
catch (Exception ex)
{
// Catch all exceptions, if discoverer fails to load the dll then let caller continue with other sources.
// Discover test doesn't work if there is a managed C++ project in solution
// Assembly.Load() fails to load the managed cpp executable, with FileLoadException. It can load the dll
// successfully though. This is known CLR issue.
PlatformServiceProvider.Instance.AdapterTraceLogger.LogWarning($"{nameof(AssemblyEnumeratorWrapper)}.{nameof(this.GetTests)}: {Resource.TestAssembly_AssemblyDiscoveryFailure}", fullFilePath, ex);
var message = ex is FileNotFoundException fileNotFoundEx ? fileNotFoundEx.Message : string.Format(CultureInfo.CurrentCulture, Resource.TestAssembly_AssemblyDiscoveryFailure, fullFilePath, ex.Message);
warnings.Add(message);
return null;
}
return null;
}
private ICollection<UnitTestElement> GetTestsInIsolation(string fullFilePath, IRunSettings runSettings, out ICollection<string> warnings)
var fullFilePath = PlatformServiceProvider.Instance.FileOperations.GetFullFilePath(assemblyFileName);
try
{
using var isolationHost = PlatformServiceProvider.Instance.CreateTestSourceHost(fullFilePath, runSettings, frameworkHandle: null);
// Create an instance of a type defined in adapter so that adapter gets loaded in the child app domain
var assemblyEnumerator = isolationHost.CreateInstanceForType(typeof(AssemblyEnumerator), new object[] { MSTestSettings.CurrentSettings }) as AssemblyEnumerator;
// This might not be supported if an older version of "PlatformServices" (Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices) assembly is already loaded into the App Domain.
try
if (!PlatformServiceProvider.Instance.FileOperations.DoesFileExist(fullFilePath))
{
assemblyEnumerator.RunSettingsXml = runSettings?.SettingsXml;
}
catch
{
PlatformServiceProvider.Instance.AdapterTraceLogger.LogWarning(Resource.OlderTFMVersionFound);
var message = string.Format(CultureInfo.CurrentCulture, Resource.TestAssembly_FileDoesNotExist, fullFilePath);
throw new FileNotFoundException(message);
}
return assemblyEnumerator.EnumerateAssembly(fullFilePath, out warnings);
if (!PlatformServiceProvider.Instance.TestSource.IsAssemblyReferenced(UnitTestFrameworkAssemblyName, fullFilePath))
{
return null;
}
// Load the assembly in isolation if required.
return this.GetTestsInIsolation(fullFilePath, runSettings, out warnings);
}
catch (FileNotFoundException ex)
{
var message = string.Format(CultureInfo.CurrentCulture, Resource.TestAssembly_AssemblyDiscoveryFailure, fullFilePath, ex.Message);
PlatformServiceProvider.Instance.AdapterTraceLogger.LogWarning($"{nameof(AssemblyEnumeratorWrapper)}.{nameof(this.GetTests)}: {Resource.TestAssembly_AssemblyDiscoveryFailure}", fullFilePath, ex);
warnings.Add(message);
return null;
}
catch (ReflectionTypeLoadException ex)
{
var message = string.Format(CultureInfo.CurrentCulture, Resource.TestAssembly_AssemblyDiscoveryFailure, fullFilePath, ex.Message);
PlatformServiceProvider.Instance.AdapterTraceLogger.LogWarning($"{nameof(AssemblyEnumeratorWrapper)}.{nameof(this.GetTests)}: {Resource.TestAssembly_AssemblyDiscoveryFailure}", fullFilePath, ex);
PlatformServiceProvider.Instance.AdapterTraceLogger.LogWarning(Resource.ExceptionsThrown);
warnings.Add(message);
if (ex.LoaderExceptions != null)
{
foreach (var loaderEx in ex.LoaderExceptions)
{
PlatformServiceProvider.Instance.AdapterTraceLogger.LogWarning("{0}", loaderEx);
}
}
return null;
}
catch (BadImageFormatException)
{
// Ignore BadImageFormatException when loading native dll in managed adapter.
return null;
}
catch (Exception ex)
{
// Catch all exceptions, if discoverer fails to load the dll then let caller continue with other sources.
// Discover test doesn't work if there is a managed C++ project in solution
// Assembly.Load() fails to load the managed cpp executable, with FileLoadException. It can load the dll
// successfully though. This is known CLR issue.
PlatformServiceProvider.Instance.AdapterTraceLogger.LogWarning($"{nameof(AssemblyEnumeratorWrapper)}.{nameof(this.GetTests)}: {Resource.TestAssembly_AssemblyDiscoveryFailure}", fullFilePath, ex);
var message = ex is FileNotFoundException fileNotFoundEx ? fileNotFoundEx.Message : string.Format(CultureInfo.CurrentCulture, Resource.TestAssembly_AssemblyDiscoveryFailure, fullFilePath, ex.Message);
warnings.Add(message);
return null;
}
}
private ICollection<UnitTestElement> GetTestsInIsolation(string fullFilePath, IRunSettings runSettings, out ICollection<string> warnings)
{
using var isolationHost = PlatformServiceProvider.Instance.CreateTestSourceHost(fullFilePath, runSettings, frameworkHandle: null);
// Create an instance of a type defined in adapter so that adapter gets loaded in the child app domain
var assemblyEnumerator = isolationHost.CreateInstanceForType(typeof(AssemblyEnumerator), new object[] { MSTestSettings.CurrentSettings }) as AssemblyEnumerator;
// This might not be supported if an older version of "PlatformServices" (Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices) assembly is already loaded into the App Domain.
try
{
assemblyEnumerator.RunSettingsXml = runSettings?.SettingsXml;
}
catch
{
PlatformServiceProvider.Instance.AdapterTraceLogger.LogWarning(Resource.OlderTFMVersionFound);
}
return assemblyEnumerator.EnumerateAssembly(fullFilePath, out warnings);
}
}

Просмотреть файл

@ -1,87 +1,86 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
namespace Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.Discovery
{
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Reflection;
namespace Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.Discovery;
using Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.Extensions;
using Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.Helpers;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Reflection;
using Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.Extensions;
using Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.Helpers;
using Microsoft.VisualStudio.TestTools.UnitTesting;
/// <summary>
/// Determines if a method is a valid test method.
/// </summary>
internal class TestMethodValidator
{
private readonly ReflectHelper reflectHelper;
private readonly bool discoverInternals;
/// <summary>
/// Initializes a new instance of the <see cref="TestMethodValidator"/> class.
/// </summary>
/// <param name="reflectHelper">An instance to reflection helper for type information.</param>
internal TestMethodValidator(ReflectHelper reflectHelper)
: this(reflectHelper, false)
{
}
/// <summary>
/// Initializes a new instance of the <see cref="TestMethodValidator"/> class.
/// </summary>
/// <param name="reflectHelper">An instance to reflection helper for type information.</param>
/// <param name="discoverInternals">True to discover methods which are declared internal in addition to methods
/// which are declared public.</param>
internal TestMethodValidator(ReflectHelper reflectHelper, bool discoverInternals)
{
this.reflectHelper = reflectHelper;
this.discoverInternals = discoverInternals;
}
/// <summary>
/// Determines if a method is a valid test method.
/// </summary>
internal class TestMethodValidator
/// <param name="testMethodInfo"> The reflected method. </param>
/// <param name="type"> The reflected type. </param>
/// <param name="warnings"> Contains warnings if any, that need to be passed back to the caller. </param>
/// <returns> Return true if a method is a valid test method. </returns>
internal virtual bool IsValidTestMethod(MethodInfo testMethodInfo, Type type, ICollection<string> warnings)
{
private readonly ReflectHelper reflectHelper;
private readonly bool discoverInternals;
/// <summary>
/// Initializes a new instance of the <see cref="TestMethodValidator"/> class.
/// </summary>
/// <param name="reflectHelper">An instance to reflection helper for type information.</param>
internal TestMethodValidator(ReflectHelper reflectHelper)
: this(reflectHelper, false)
if (!this.reflectHelper.IsAttributeDefined(testMethodInfo, typeof(TestMethodAttribute), false)
&& !this.reflectHelper.HasAttributeDerivedFrom(testMethodInfo, typeof(TestMethodAttribute), false))
{
return false;
}
/// <summary>
/// Initializes a new instance of the <see cref="TestMethodValidator"/> class.
/// </summary>
/// <param name="reflectHelper">An instance to reflection helper for type information.</param>
/// <param name="discoverInternals">True to discover methods which are declared internal in addition to methods
/// which are declared public.</param>
internal TestMethodValidator(ReflectHelper reflectHelper, bool discoverInternals)
// Generic method Definitions are not valid.
if (testMethodInfo.IsGenericMethodDefinition)
{
this.reflectHelper = reflectHelper;
this.discoverInternals = discoverInternals;
var message = string.Format(CultureInfo.CurrentCulture, Resource.UTA_ErrorGenericTestMethod, testMethodInfo.DeclaringType.FullName, testMethodInfo.Name);
warnings.Add(message);
return false;
}
/// <summary>
/// Determines if a method is a valid test method.
/// </summary>
/// <param name="testMethodInfo"> The reflected method. </param>
/// <param name="type"> The reflected type. </param>
/// <param name="warnings"> Contains warnings if any, that need to be passed back to the caller. </param>
/// <returns> Return true if a method is a valid test method. </returns>
internal virtual bool IsValidTestMethod(MethodInfo testMethodInfo, Type type, ICollection<string> warnings)
var isAccessible = testMethodInfo.IsPublic
|| (this.discoverInternals && testMethodInfo.IsAssembly);
// Todo: Decide whether parameter count matters.
// The isGenericMethod check below id to verify that there are no closed generic methods slipping through.
// Closed generic methods being GenericMethod<int> and open being GenericMethod<T>.
var isValidTestMethod = isAccessible && !testMethodInfo.IsAbstract && !testMethodInfo.IsStatic
&& !testMethodInfo.IsGenericMethod
&& testMethodInfo.IsVoidOrTaskReturnType();
if (!isValidTestMethod)
{
if (!this.reflectHelper.IsAttributeDefined(testMethodInfo, typeof(TestMethodAttribute), false)
&& !this.reflectHelper.HasAttributeDerivedFrom(testMethodInfo, typeof(TestMethodAttribute), false))
{
return false;
}
// Generic method Definitions are not valid.
if (testMethodInfo.IsGenericMethodDefinition)
{
var message = string.Format(CultureInfo.CurrentCulture, Resource.UTA_ErrorGenericTestMethod, testMethodInfo.DeclaringType.FullName, testMethodInfo.Name);
warnings.Add(message);
return false;
}
var isAccessible = testMethodInfo.IsPublic
|| (this.discoverInternals && testMethodInfo.IsAssembly);
// Todo: Decide whether parameter count matters.
// The isGenericMethod check below id to verify that there are no closed generic methods slipping through.
// Closed generic methods being GenericMethod<int> and open being GenericMethod<T>.
var isValidTestMethod = isAccessible && !testMethodInfo.IsAbstract && !testMethodInfo.IsStatic
&& !testMethodInfo.IsGenericMethod
&& testMethodInfo.IsVoidOrTaskReturnType();
if (!isValidTestMethod)
{
var message = string.Format(CultureInfo.CurrentCulture, Resource.UTA_ErrorIncorrectTestMethodSignature, type.FullName, testMethodInfo.Name);
warnings.Add(message);
return false;
}
return true;
var message = string.Format(CultureInfo.CurrentCulture, Resource.UTA_ErrorIncorrectTestMethodSignature, type.FullName, testMethodInfo.Name);
warnings.Add(message);
return false;
}
return true;
}
}

Просмотреть файл

@ -1,210 +1,209 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
namespace Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.Discovery
{
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Diagnostics;
using System.Linq;
using System.Reflection;
using System.Threading.Tasks;
namespace Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.Discovery;
using Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.Extensions;
using Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.Helpers;
using Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.ObjectModel;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Diagnostics;
using System.Linq;
using System.Reflection;
using System.Threading.Tasks;
using Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.Extensions;
using Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.Helpers;
using Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.ObjectModel;
using Microsoft.VisualStudio.TestTools.UnitTesting;
/// <summary>
/// Enumerates through the type looking for Valid Test Methods to execute.
/// </summary>
internal class TypeEnumerator
{
private readonly Type type;
private readonly string assemblyName;
private readonly TypeValidator typeValidator;
private readonly TestMethodValidator testMethodValidator;
private readonly ReflectHelper reflectHelper;
/// <summary>
/// Enumerates through the type looking for Valid Test Methods to execute.
/// Initializes a new instance of the <see cref="TypeEnumerator"/> class.
/// </summary>
internal class TypeEnumerator
/// <param name="type"> The reflected type. </param>
/// <param name="assemblyName"> The name of the assembly being reflected. </param>
/// <param name="reflectHelper"> An instance to reflection helper for type information. </param>
/// <param name="typeValidator"> The validator for test classes. </param>
/// <param name="testMethodValidator"> The validator for test methods. </param>
internal TypeEnumerator(Type type, string assemblyName, ReflectHelper reflectHelper, TypeValidator typeValidator, TestMethodValidator testMethodValidator)
{
private readonly Type type;
private readonly string assemblyName;
private readonly TypeValidator typeValidator;
private readonly TestMethodValidator testMethodValidator;
private readonly ReflectHelper reflectHelper;
this.type = type;
this.assemblyName = assemblyName;
this.reflectHelper = reflectHelper;
this.typeValidator = typeValidator;
this.testMethodValidator = testMethodValidator;
}
/// <summary>
/// Initializes a new instance of the <see cref="TypeEnumerator"/> class.
/// </summary>
/// <param name="type"> The reflected type. </param>
/// <param name="assemblyName"> The name of the assembly being reflected. </param>
/// <param name="reflectHelper"> An instance to reflection helper for type information. </param>
/// <param name="typeValidator"> The validator for test classes. </param>
/// <param name="testMethodValidator"> The validator for test methods. </param>
internal TypeEnumerator(Type type, string assemblyName, ReflectHelper reflectHelper, TypeValidator typeValidator, TestMethodValidator testMethodValidator)
/// <summary>
/// Walk through all methods in the type, and find out the test methods
/// </summary>
/// <param name="warnings"> Contains warnings if any, that need to be passed back to the caller. </param>
/// <returns> list of test cases.</returns>
internal virtual ICollection<UnitTestElement> Enumerate(out ICollection<string> warnings)
{
warnings = new Collection<string>();
if (!this.typeValidator.IsValidTestClass(this.type, warnings))
{
this.type = type;
this.assemblyName = assemblyName;
this.reflectHelper = reflectHelper;
this.typeValidator = typeValidator;
this.testMethodValidator = testMethodValidator;
return null;
}
/// <summary>
/// Walk through all methods in the type, and find out the test methods
/// </summary>
/// <param name="warnings"> Contains warnings if any, that need to be passed back to the caller. </param>
/// <returns> list of test cases.</returns>
internal virtual ICollection<UnitTestElement> Enumerate(out ICollection<string> warnings)
{
warnings = new Collection<string>();
// If test class is valid, then get the tests
return this.GetTests(warnings);
}
if (!this.typeValidator.IsValidTestClass(this.type, warnings))
/// <summary>
/// Gets a list of valid tests in a type.
/// </summary>
/// <param name="warnings"> Contains warnings if any, that need to be passed back to the caller. </param>
/// <returns> List of Valid Tests. </returns>
internal Collection<UnitTestElement> GetTests(ICollection<string> warnings)
{
bool foundDuplicateTests = false;
var foundTests = new HashSet<string>();
var tests = new Collection<UnitTestElement>();
// Test class is already valid. Verify methods.
foreach (var method in this.type.GetRuntimeMethods())
{
var isMethodDeclaredInTestTypeAssembly = this.reflectHelper.IsMethodDeclaredInSameAssemblyAsType(method, this.type);
var enableMethodsFromOtherAssemblies = MSTestSettings.CurrentSettings.EnableBaseClassTestMethodsFromOtherAssemblies;
if (!isMethodDeclaredInTestTypeAssembly && !enableMethodsFromOtherAssemblies)
{
return null;
continue;
}
// If test class is valid, then get the tests
return this.GetTests(warnings);
if (this.testMethodValidator.IsValidTestMethod(method, this.type, warnings))
{
foundDuplicateTests = foundDuplicateTests || !foundTests.Add(method.Name);
tests.Add(this.GetTestFromMethod(method, isMethodDeclaredInTestTypeAssembly, warnings));
}
}
/// <summary>
/// Gets a list of valid tests in a type.
/// </summary>
/// <param name="warnings"> Contains warnings if any, that need to be passed back to the caller. </param>
/// <returns> List of Valid Tests. </returns>
internal Collection<UnitTestElement> GetTests(ICollection<string> warnings)
if (!foundDuplicateTests)
{
bool foundDuplicateTests = false;
var foundTests = new HashSet<string>();
var tests = new Collection<UnitTestElement>();
// Test class is already valid. Verify methods.
foreach (var method in this.type.GetRuntimeMethods())
{
var isMethodDeclaredInTestTypeAssembly = this.reflectHelper.IsMethodDeclaredInSameAssemblyAsType(method, this.type);
var enableMethodsFromOtherAssemblies = MSTestSettings.CurrentSettings.EnableBaseClassTestMethodsFromOtherAssemblies;
if (!isMethodDeclaredInTestTypeAssembly && !enableMethodsFromOtherAssemblies)
{
continue;
}
if (this.testMethodValidator.IsValidTestMethod(method, this.type, warnings))
{
foundDuplicateTests = foundDuplicateTests || !foundTests.Add(method.Name);
tests.Add(this.GetTestFromMethod(method, isMethodDeclaredInTestTypeAssembly, warnings));
}
}
if (!foundDuplicateTests)
{
return tests;
}
// Remove duplicate test methods by taking the first one of each name
// that is declared closest to the test class in the hierarchy.
var inheritanceDepths = new Dictionary<string, int>();
var currentType = this.type;
int currentDepth = 0;
while (currentType != null)
{
inheritanceDepths[currentType.FullName] = currentDepth;
++currentDepth;
currentType = currentType.GetTypeInfo().BaseType;
}
return new Collection<UnitTestElement>(
tests.GroupBy(
t => t.TestMethod.Name,
(_, elements) =>
elements.OrderBy(t => inheritanceDepths[t.TestMethod.DeclaringClassFullName ?? t.TestMethod.FullClassName]).First())
.ToList());
return tests;
}
/// <summary>
/// Gets a UnitTestElement from a MethodInfo object filling it up with appropriate values.
/// </summary>
/// <param name="method">The reflected method.</param>
/// <param name="isDeclaredInTestTypeAssembly">True if the reflected method is declared in the same assembly as the current type.</param>
/// <param name="warnings">Contains warnings if any, that need to be passed back to the caller.</param>
/// <returns> Returns a UnitTestElement.</returns>
internal UnitTestElement GetTestFromMethod(MethodInfo method, bool isDeclaredInTestTypeAssembly, ICollection<string> warnings)
// Remove duplicate test methods by taking the first one of each name
// that is declared closest to the test class in the hierarchy.
var inheritanceDepths = new Dictionary<string, int>();
var currentType = this.type;
int currentDepth = 0;
while (currentType != null)
{
// null if the current instance represents a generic type parameter.
Debug.Assert(this.type.AssemblyQualifiedName != null, "AssemblyQualifiedName for method is null.");
// This allows void returning async test method to be valid test method. Though they will be executed similar to non-async test method.
var isAsync = ReflectHelper.MatchReturnType(method, typeof(Task));
var testMethod = new TestMethod(method, method.Name, this.type.FullName, this.assemblyName, isAsync);
if (!method.DeclaringType.FullName.Equals(this.type.FullName))
{
testMethod.DeclaringClassFullName = method.DeclaringType.FullName;
}
if (!isDeclaredInTestTypeAssembly)
{
testMethod.DeclaringAssemblyName =
PlatformServiceProvider.Instance.FileOperations.GetAssemblyPath(
method.DeclaringType.GetTypeInfo().Assembly);
}
var testElement = new UnitTestElement(testMethod);
// Get compiler generated type name for async test method (either void returning or task returning).
var asyncTypeName = method.GetAsyncTypeName();
testElement.AsyncTypeName = asyncTypeName;
testElement.TestCategory = this.reflectHelper.GetCategories(method, this.type);
testElement.DoNotParallelize = this.reflectHelper.IsDoNotParallelizeSet(method, this.type);
var traits = this.reflectHelper.GetTestPropertiesAsTraits(method);
var ownerTrait = this.reflectHelper.GetTestOwnerAsTraits(method);
if (ownerTrait != null)
{
traits = traits.Concat(new[] { ownerTrait });
}
testElement.Priority = this.reflectHelper.GetPriority(method);
var priorityTrait = this.reflectHelper.GetTestPriorityAsTraits(testElement.Priority);
if (priorityTrait != null)
{
traits = traits.Concat(new[] { priorityTrait });
}
testElement.Traits = traits.ToArray();
if (this.reflectHelper.GetCustomAttribute(method, typeof(CssIterationAttribute)) is CssIterationAttribute cssIteration)
{
testElement.CssIteration = cssIteration.CssIteration;
}
if (this.reflectHelper.GetCustomAttribute(method, typeof(CssProjectStructureAttribute)) is CssProjectStructureAttribute cssProjectStructure)
{
testElement.CssProjectStructure = cssProjectStructure.CssProjectStructure;
}
if (this.reflectHelper.GetCustomAttribute(method, typeof(DescriptionAttribute)) is DescriptionAttribute descriptionAttribute)
{
testElement.Description = descriptionAttribute.Description;
}
var workItemAttributes = this.reflectHelper.GetCustomAttributes(method, typeof(WorkItemAttribute)).Cast<WorkItemAttribute>().ToArray();
if (workItemAttributes.Any())
{
testElement.WorkItemIds = workItemAttributes.Select(x => x.Id.ToString()).ToArray();
}
testElement.Ignored = this.reflectHelper.IsAttributeDefined(method, typeof(IgnoreAttribute), false);
// Get Deployment items if any.
testElement.DeploymentItems = PlatformServiceProvider.Instance.TestDeployment.GetDeploymentItems(method, this.type, warnings);
// get DisplayName from TestMethodAttribute
var testMethodAttribute = this.reflectHelper.GetCustomAttribute(method, typeof(TestMethodAttribute)) as TestMethodAttribute;
testElement.DisplayName = testMethodAttribute?.DisplayName ?? method.Name;
return testElement;
inheritanceDepths[currentType.FullName] = currentDepth;
++currentDepth;
currentType = currentType.GetTypeInfo().BaseType;
}
return new Collection<UnitTestElement>(
tests.GroupBy(
t => t.TestMethod.Name,
(_, elements) =>
elements.OrderBy(t => inheritanceDepths[t.TestMethod.DeclaringClassFullName ?? t.TestMethod.FullClassName]).First())
.ToList());
}
/// <summary>
/// Gets a UnitTestElement from a MethodInfo object filling it up with appropriate values.
/// </summary>
/// <param name="method">The reflected method.</param>
/// <param name="isDeclaredInTestTypeAssembly">True if the reflected method is declared in the same assembly as the current type.</param>
/// <param name="warnings">Contains warnings if any, that need to be passed back to the caller.</param>
/// <returns> Returns a UnitTestElement.</returns>
internal UnitTestElement GetTestFromMethod(MethodInfo method, bool isDeclaredInTestTypeAssembly, ICollection<string> warnings)
{
// null if the current instance represents a generic type parameter.
Debug.Assert(this.type.AssemblyQualifiedName != null, "AssemblyQualifiedName for method is null.");
// This allows void returning async test method to be valid test method. Though they will be executed similar to non-async test method.
var isAsync = ReflectHelper.MatchReturnType(method, typeof(Task));
var testMethod = new TestMethod(method, method.Name, this.type.FullName, this.assemblyName, isAsync);
if (!method.DeclaringType.FullName.Equals(this.type.FullName))
{
testMethod.DeclaringClassFullName = method.DeclaringType.FullName;
}
if (!isDeclaredInTestTypeAssembly)
{
testMethod.DeclaringAssemblyName =
PlatformServiceProvider.Instance.FileOperations.GetAssemblyPath(
method.DeclaringType.GetTypeInfo().Assembly);
}
var testElement = new UnitTestElement(testMethod);
// Get compiler generated type name for async test method (either void returning or task returning).
var asyncTypeName = method.GetAsyncTypeName();
testElement.AsyncTypeName = asyncTypeName;
testElement.TestCategory = this.reflectHelper.GetCategories(method, this.type);
testElement.DoNotParallelize = this.reflectHelper.IsDoNotParallelizeSet(method, this.type);
var traits = this.reflectHelper.GetTestPropertiesAsTraits(method);
var ownerTrait = this.reflectHelper.GetTestOwnerAsTraits(method);
if (ownerTrait != null)
{
traits = traits.Concat(new[] { ownerTrait });
}
testElement.Priority = this.reflectHelper.GetPriority(method);
var priorityTrait = this.reflectHelper.GetTestPriorityAsTraits(testElement.Priority);
if (priorityTrait != null)
{
traits = traits.Concat(new[] { priorityTrait });
}
testElement.Traits = traits.ToArray();
if (this.reflectHelper.GetCustomAttribute(method, typeof(CssIterationAttribute)) is CssIterationAttribute cssIteration)
{
testElement.CssIteration = cssIteration.CssIteration;
}
if (this.reflectHelper.GetCustomAttribute(method, typeof(CssProjectStructureAttribute)) is CssProjectStructureAttribute cssProjectStructure)
{
testElement.CssProjectStructure = cssProjectStructure.CssProjectStructure;
}
if (this.reflectHelper.GetCustomAttribute(method, typeof(DescriptionAttribute)) is DescriptionAttribute descriptionAttribute)
{
testElement.Description = descriptionAttribute.Description;
}
var workItemAttributes = this.reflectHelper.GetCustomAttributes(method, typeof(WorkItemAttribute)).Cast<WorkItemAttribute>().ToArray();
if (workItemAttributes.Any())
{
testElement.WorkItemIds = workItemAttributes.Select(x => x.Id.ToString()).ToArray();
}
testElement.Ignored = this.reflectHelper.IsAttributeDefined(method, typeof(IgnoreAttribute), false);
// Get Deployment items if any.
testElement.DeploymentItems = PlatformServiceProvider.Instance.TestDeployment.GetDeploymentItems(method, this.type, warnings);
// get DisplayName from TestMethodAttribute
var testMethodAttribute = this.reflectHelper.GetCustomAttribute(method, typeof(TestMethodAttribute)) as TestMethodAttribute;
testElement.DisplayName = testMethodAttribute?.DisplayName ?? method.Name;
return testElement;
}
}

Просмотреть файл

@ -1,215 +1,214 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
namespace Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.Discovery
{
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.Reflection;
namespace Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.Discovery;
using Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.Helpers;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.Reflection;
using Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.Helpers;
using Microsoft.VisualStudio.TestTools.UnitTesting;
/// <summary>
/// Determines whether a type is a valid test class for this adapter.
/// </summary>
internal class TypeValidator
{
// Setting this to a string representation instead of a typeof(TestContext).FullName
// since the later would require a load of the Test Framework extension assembly at this point.
private const string TestContextFullName = "Microsoft.VisualStudio.TestTools.UnitTesting.TestContext";
private readonly ReflectHelper reflectHelper;
private readonly bool discoverInternals;
/// <summary>
/// Determines whether a type is a valid test class for this adapter.
/// Initializes a new instance of the <see cref="TypeValidator"/> class.
/// </summary>
internal class TypeValidator
/// <param name="reflectHelper">An instance to reflection helper for type information.</param>
internal TypeValidator(ReflectHelper reflectHelper)
: this(reflectHelper, false)
{
// Setting this to a string representation instead of a typeof(TestContext).FullName
// since the later would require a load of the Test Framework extension assembly at this point.
private const string TestContextFullName = "Microsoft.VisualStudio.TestTools.UnitTesting.TestContext";
private readonly ReflectHelper reflectHelper;
private readonly bool discoverInternals;
}
/// <summary>
/// Initializes a new instance of the <see cref="TypeValidator"/> class.
/// </summary>
/// <param name="reflectHelper">An instance to reflection helper for type information.</param>
internal TypeValidator(ReflectHelper reflectHelper)
: this(reflectHelper, false)
/// <summary>
/// Initializes a new instance of the <see cref="TypeValidator"/> class.
/// </summary>
/// <param name="reflectHelper">An instance to reflection helper for type information.</param>
/// <param name="discoverInternals">True to discover test classes which are declared internal in
/// addition to test classes which are declared public.</param>
internal TypeValidator(ReflectHelper reflectHelper, bool discoverInternals)
{
this.reflectHelper = reflectHelper;
this.discoverInternals = discoverInternals;
}
/// <summary>
/// Determines if a type is a valid test class for this adapter.
/// </summary>
/// <param name="type">The reflected type.</param>
/// <param name="warnings">Contains warnings if any, that need to be passed back to the caller.</param>
/// <returns>Return true if it is a valid test class.</returns>
internal virtual bool IsValidTestClass(Type type, ICollection<string> warnings)
{
if (type.GetTypeInfo().IsClass &&
(this.reflectHelper.IsAttributeDefined(type, typeof(TestClassAttribute), false) ||
this.reflectHelper.HasAttributeDerivedFrom(type, typeof(TestClassAttribute), false)))
{
}
/// <summary>
/// Initializes a new instance of the <see cref="TypeValidator"/> class.
/// </summary>
/// <param name="reflectHelper">An instance to reflection helper for type information.</param>
/// <param name="discoverInternals">True to discover test classes which are declared internal in
/// addition to test classes which are declared public.</param>
internal TypeValidator(ReflectHelper reflectHelper, bool discoverInternals)
{
this.reflectHelper = reflectHelper;
this.discoverInternals = discoverInternals;
}
/// <summary>
/// Determines if a type is a valid test class for this adapter.
/// </summary>
/// <param name="type">The reflected type.</param>
/// <param name="warnings">Contains warnings if any, that need to be passed back to the caller.</param>
/// <returns>Return true if it is a valid test class.</returns>
internal virtual bool IsValidTestClass(Type type, ICollection<string> warnings)
{
if (type.GetTypeInfo().IsClass &&
(this.reflectHelper.IsAttributeDefined(type, typeof(TestClassAttribute), false) ||
this.reflectHelper.HasAttributeDerivedFrom(type, typeof(TestClassAttribute), false)))
// inaccessible class
if (!this.TypeHasValidAccessibility(type.GetTypeInfo(), this.discoverInternals))
{
// inaccessible class
if (!this.TypeHasValidAccessibility(type.GetTypeInfo(), this.discoverInternals))
{
var warning = string.Format(CultureInfo.CurrentCulture, Resource.UTA_ErrorNonPublicTestClass, type.FullName);
warnings.Add(warning);
return false;
}
// Generic class
if (type.GetTypeInfo().IsGenericTypeDefinition && !type.GetTypeInfo().IsAbstract)
{
// In IDE generic classes that are not abstract are treated as not runnable. Keep consistence.
var warning = string.Format(CultureInfo.CurrentCulture, Resource.UTA_ErrorNonPublicTestClass, type.FullName);
warnings.Add(warning);
return false;
}
// Class is not valid if the testContext property is incorrect
if (!this.HasCorrectTestContextSignature(type))
{
var warning = string.Format(CultureInfo.CurrentCulture, Resource.UTA_ErrorInValidTestContextSignature, type.FullName);
warnings.Add(warning);
return false;
}
// Abstract test classes can be base classes for derived test classes.
// There is no way to see if there are derived test classes.
// Thus if a test class is abstract, just ignore all test methods from it
// (they will be visible in derived classes). No warnings (such as test method, deployment item,
// etc attribute is defined on the class) will be generated for this class:
// What we do is:
// - report the class as "not valid" test class. This will cause to skip enumerating tests from it.
// - Do not generate warnings/do not create NOT RUNNABLE tests.
if (type.GetTypeInfo().IsAbstract)
{
return false;
}
return true;
}
return false;
}
/// <summary>
/// Determines if the type has a valid TestContext property definition.
/// </summary>
/// <param name="type">The reflected type.</param>
/// <returns>Returns true if type has a valid TestContext property definition.</returns>
internal bool HasCorrectTestContextSignature(Type type)
{
Debug.Assert(type != null, "HasCorrectTestContextSignature type is null");
var propertyInfoEnumerable = type.GetTypeInfo().DeclaredProperties;
var propertyInfo = new List<PropertyInfo>();
foreach (var pinfo in propertyInfoEnumerable)
{
// PropertyType.FullName can be null if the property is a generic type.
if (TestContextFullName.Equals(pinfo.PropertyType.FullName, StringComparison.Ordinal))
{
propertyInfo.Add(pinfo);
}
}
if (propertyInfo.Count == 0)
{
return true;
}
foreach (var pinfo in propertyInfo)
{
var setInfo = pinfo.SetMethod;
if (setInfo == null)
{
// we have a getter, but not a setter.
return false;
}
if (setInfo.IsPrivate || setInfo.IsStatic || setInfo.IsAbstract)
{
return false;
}
}
return true;
}
internal bool TypeHasValidAccessibility(TypeInfo type, bool discoverInternals)
{
if (type.IsVisible)
{
// The type is public or a public nested class of entirely public container classes.
return true;
}
if (!discoverInternals)
{
// The type is not externally visible and internal test classes are not to be discovered.
var warning = string.Format(CultureInfo.CurrentCulture, Resource.UTA_ErrorNonPublicTestClass, type.FullName);
warnings.Add(warning);
return false;
}
// Either the type is not public or it is a nested class and itself or one of its containers is not public.
if (type.IsNested)
// Generic class
if (type.GetTypeInfo().IsGenericTypeDefinition && !type.GetTypeInfo().IsAbstract)
{
// Assembly is CLR term for internal visibility:
// Private == private,
// FamilyANDAssembly == private protected,
// Assembly == internal,
// Family == protected,
// FamilyORAssembly == protected internal,
// Public == public.
// So this reads IsNestedInternal || IsNestedPublic:
var isNestedPublicOrInternal = type.IsNestedAssembly || type.IsNestedPublic;
if (!isNestedPublicOrInternal)
{
// This type is nested, but is not public or internal.
return false;
}
// The type itself is nested and is public, or internal, but could be in hierarchy of types
// where some of the parent types is private (or other modifier that is not public and is not internal)
// if we looked for just public types we could just look at IsVisible, but internal type nested in internal type
// is not Visible, so we need to check all the parents and make sure they are all either public or internal.
var parentsArePublicOrInternal = true;
var declaringType = type.DeclaringType;
while (declaringType != null && parentsArePublicOrInternal)
{
var declaringTypeIsPublicOrInternal =
// Declaring type is non-nested type, and we are looking for internal or public, which are the only
// two valid options that non-nested type can be.
!declaringType.IsNested
// Or the type is nested internal, or nested public type, but not any other
// like nested protected internal type, or nested private type.
|| declaringType.GetTypeInfo().IsNestedAssembly || declaringType.GetTypeInfo().IsNestedPublic;
if (!declaringTypeIsPublicOrInternal)
{
parentsArePublicOrInternal = false;
break;
}
declaringType = declaringType.DeclaringType;
}
return parentsArePublicOrInternal;
// In IDE generic classes that are not abstract are treated as not runnable. Keep consistence.
var warning = string.Format(CultureInfo.CurrentCulture, Resource.UTA_ErrorNonPublicTestClass, type.FullName);
warnings.Add(warning);
return false;
}
// Class is not valid if the testContext property is incorrect
if (!this.HasCorrectTestContextSignature(type))
{
var warning = string.Format(CultureInfo.CurrentCulture, Resource.UTA_ErrorInValidTestContextSignature, type.FullName);
warnings.Add(warning);
return false;
}
// Abstract test classes can be base classes for derived test classes.
// There is no way to see if there are derived test classes.
// Thus if a test class is abstract, just ignore all test methods from it
// (they will be visible in derived classes). No warnings (such as test method, deployment item,
// etc attribute is defined on the class) will be generated for this class:
// What we do is:
// - report the class as "not valid" test class. This will cause to skip enumerating tests from it.
// - Do not generate warnings/do not create NOT RUNNABLE tests.
if (type.GetTypeInfo().IsAbstract)
{
return false;
}
// The type is not public and is not nested. Non-nested types can be only public or internal
// so this type must be internal.
return true;
}
return false;
}
/// <summary>
/// Determines if the type has a valid TestContext property definition.
/// </summary>
/// <param name="type">The reflected type.</param>
/// <returns>Returns true if type has a valid TestContext property definition.</returns>
internal bool HasCorrectTestContextSignature(Type type)
{
Debug.Assert(type != null, "HasCorrectTestContextSignature type is null");
var propertyInfoEnumerable = type.GetTypeInfo().DeclaredProperties;
var propertyInfo = new List<PropertyInfo>();
foreach (var pinfo in propertyInfoEnumerable)
{
// PropertyType.FullName can be null if the property is a generic type.
if (TestContextFullName.Equals(pinfo.PropertyType.FullName, StringComparison.Ordinal))
{
propertyInfo.Add(pinfo);
}
}
if (propertyInfo.Count == 0)
{
return true;
}
foreach (var pinfo in propertyInfo)
{
var setInfo = pinfo.SetMethod;
if (setInfo == null)
{
// we have a getter, but not a setter.
return false;
}
if (setInfo.IsPrivate || setInfo.IsStatic || setInfo.IsAbstract)
{
return false;
}
}
return true;
}
internal bool TypeHasValidAccessibility(TypeInfo type, bool discoverInternals)
{
if (type.IsVisible)
{
// The type is public or a public nested class of entirely public container classes.
return true;
}
if (!discoverInternals)
{
// The type is not externally visible and internal test classes are not to be discovered.
return false;
}
// Either the type is not public or it is a nested class and itself or one of its containers is not public.
if (type.IsNested)
{
// Assembly is CLR term for internal visibility:
// Private == private,
// FamilyANDAssembly == private protected,
// Assembly == internal,
// Family == protected,
// FamilyORAssembly == protected internal,
// Public == public.
// So this reads IsNestedInternal || IsNestedPublic:
var isNestedPublicOrInternal = type.IsNestedAssembly || type.IsNestedPublic;
if (!isNestedPublicOrInternal)
{
// This type is nested, but is not public or internal.
return false;
}
// The type itself is nested and is public, or internal, but could be in hierarchy of types
// where some of the parent types is private (or other modifier that is not public and is not internal)
// if we looked for just public types we could just look at IsVisible, but internal type nested in internal type
// is not Visible, so we need to check all the parents and make sure they are all either public or internal.
var parentsArePublicOrInternal = true;
var declaringType = type.DeclaringType;
while (declaringType != null && parentsArePublicOrInternal)
{
var declaringTypeIsPublicOrInternal =
// Declaring type is non-nested type, and we are looking for internal or public, which are the only
// two valid options that non-nested type can be.
!declaringType.IsNested
// Or the type is nested internal, or nested public type, but not any other
// like nested protected internal type, or nested private type.
|| declaringType.GetTypeInfo().IsNestedAssembly || declaringType.GetTypeInfo().IsNestedPublic;
if (!declaringTypeIsPublicOrInternal)
{
parentsArePublicOrInternal = false;
break;
}
declaringType = declaringType.DeclaringType;
}
return parentsArePublicOrInternal;
}
// The type is not public and is not nested. Non-nested types can be only public or internal
// so this type must be internal.
return true;
}
}

Просмотреть файл

@ -1,169 +1,168 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
namespace Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter
namespace Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter;
using System.Collections.Generic;
using System.Globalization;
using Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.Discovery;
using Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.ObjectModel;
using Microsoft.VisualStudio.TestPlatform.ObjectModel;
using Microsoft.VisualStudio.TestPlatform.ObjectModel.Adapter;
using Microsoft.VisualStudio.TestPlatform.ObjectModel.Logging;
internal class UnitTestDiscoverer
{
using System.Collections.Generic;
using System.Globalization;
using Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.Discovery;
using Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.ObjectModel;
using Microsoft.VisualStudio.TestPlatform.ObjectModel;
using Microsoft.VisualStudio.TestPlatform.ObjectModel.Adapter;
using Microsoft.VisualStudio.TestPlatform.ObjectModel.Logging;
private readonly AssemblyEnumeratorWrapper assemblyEnumeratorWrapper;
internal class UnitTestDiscoverer
internal UnitTestDiscoverer()
{
private readonly AssemblyEnumeratorWrapper assemblyEnumeratorWrapper;
this.assemblyEnumeratorWrapper = new AssemblyEnumeratorWrapper();
this.TestMethodFilter = new TestMethodFilter();
}
internal UnitTestDiscoverer()
/// <summary>
/// Gets or sets method filter for filtering tests
/// </summary>
private TestMethodFilter TestMethodFilter { get; set; }
/// <summary>
/// Discovers the tests available from the provided sources.
/// </summary>
/// <param name="sources"> The sources. </param>
/// <param name="logger"> The logger. </param>
/// <param name="discoverySink"> The discovery Sink. </param>
/// <param name="discoveryContext"> The discovery context. </param>
internal void DiscoverTests(
IEnumerable<string> sources,
IMessageLogger logger,
ITestCaseDiscoverySink discoverySink,
IDiscoveryContext discoveryContext)
{
foreach (var source in sources)
{
this.assemblyEnumeratorWrapper = new AssemblyEnumeratorWrapper();
this.TestMethodFilter = new TestMethodFilter();
this.DiscoverTestsInSource(source, logger, discoverySink, discoveryContext);
}
}
/// <summary>
/// Get the tests from the parameter source
/// </summary>
/// <param name="source"> The source. </param>
/// <param name="logger"> The logger. </param>
/// <param name="discoverySink"> The discovery Sink. </param>
/// <param name="discoveryContext"> The discovery context. </param>
internal virtual void DiscoverTestsInSource(
string source,
IMessageLogger logger,
ITestCaseDiscoverySink discoverySink,
IDiscoveryContext discoveryContext)
{
var testElements = this.assemblyEnumeratorWrapper.GetTests(source, discoveryContext?.RunSettings, out var warnings);
// log the warnings
foreach (var warning in warnings)
{
PlatformServiceProvider.Instance.AdapterTraceLogger.LogInfo(
"MSTestDiscoverer: Warning during discovery from {0}. {1} ",
source,
warning);
var message = string.Format(CultureInfo.CurrentCulture, Resource.DiscoveryWarning, source, warning);
logger.SendMessage(TestMessageLevel.Warning, message);
}
/// <summary>
/// Gets or sets method filter for filtering tests
/// </summary>
private TestMethodFilter TestMethodFilter { get; set; }
/// <summary>
/// Discovers the tests available from the provided sources.
/// </summary>
/// <param name="sources"> The sources. </param>
/// <param name="logger"> The logger. </param>
/// <param name="discoverySink"> The discovery Sink. </param>
/// <param name="discoveryContext"> The discovery context. </param>
internal void DiscoverTests(
IEnumerable<string> sources,
IMessageLogger logger,
ITestCaseDiscoverySink discoverySink,
IDiscoveryContext discoveryContext)
// No tests found => nothing to do
if (testElements == null || testElements.Count == 0)
{
foreach (var source in sources)
{
this.DiscoverTestsInSource(source, logger, discoverySink, discoveryContext);
}
return;
}
/// <summary>
/// Get the tests from the parameter source
/// </summary>
/// <param name="source"> The source. </param>
/// <param name="logger"> The logger. </param>
/// <param name="discoverySink"> The discovery Sink. </param>
/// <param name="discoveryContext"> The discovery context. </param>
internal virtual void DiscoverTestsInSource(
string source,
IMessageLogger logger,
ITestCaseDiscoverySink discoverySink,
IDiscoveryContext discoveryContext)
{
var testElements = this.assemblyEnumeratorWrapper.GetTests(source, discoveryContext?.RunSettings, out var warnings);
PlatformServiceProvider.Instance.AdapterTraceLogger.LogInfo(
"MSTestDiscoverer: Found {0} tests from source {1}",
testElements.Count,
source);
// log the warnings
foreach (var warning in warnings)
this.SendTestCases(source, testElements, discoverySink, discoveryContext, logger);
}
internal void SendTestCases(string source, IEnumerable<UnitTestElement> testElements, ITestCaseDiscoverySink discoverySink, IDiscoveryContext discoveryContext, IMessageLogger logger)
{
var shouldCollectSourceInformation = MSTestSettings.RunConfigurationSettings.CollectSourceInformation;
var navigationSessions = new Dictionary<string, object>();
try
{
if (shouldCollectSourceInformation)
{
PlatformServiceProvider.Instance.AdapterTraceLogger.LogInfo(
"MSTestDiscoverer: Warning during discovery from {0}. {1} ",
source,
warning);
var message = string.Format(CultureInfo.CurrentCulture, Resource.DiscoveryWarning, source, warning);
logger.SendMessage(TestMessageLevel.Warning, message);
navigationSessions.Add(source, PlatformServiceProvider.Instance.FileOperations.CreateNavigationSession(source));
}
// No tests found => nothing to do
if (testElements == null || testElements.Count == 0)
// Get filter expression and skip discovery in case filter expression has parsing error.
ITestCaseFilterExpression filterExpression = this.TestMethodFilter.GetFilterExpression(discoveryContext, logger, out var filterHasError);
if (filterHasError)
{
return;
}
PlatformServiceProvider.Instance.AdapterTraceLogger.LogInfo(
"MSTestDiscoverer: Found {0} tests from source {1}",
testElements.Count,
source);
this.SendTestCases(source, testElements, discoverySink, discoveryContext, logger);
}
internal void SendTestCases(string source, IEnumerable<UnitTestElement> testElements, ITestCaseDiscoverySink discoverySink, IDiscoveryContext discoveryContext, IMessageLogger logger)
{
var shouldCollectSourceInformation = MSTestSettings.RunConfigurationSettings.CollectSourceInformation;
var navigationSessions = new Dictionary<string, object>();
try
foreach (var testElement in testElements)
{
var testCase = testElement.ToTestCase();
// Filter tests based on test case filters
if (filterExpression != null && filterExpression.MatchTestCase(testCase, (p) => this.TestMethodFilter.PropertyValueProvider(testCase, p)) == false)
{
continue;
}
if (shouldCollectSourceInformation)
{
navigationSessions.Add(source, PlatformServiceProvider.Instance.FileOperations.CreateNavigationSession(source));
}
string testSource = testElement.TestMethod.DeclaringAssemblyName ?? source;
// Get filter expression and skip discovery in case filter expression has parsing error.
ITestCaseFilterExpression filterExpression = this.TestMethodFilter.GetFilterExpression(discoveryContext, logger, out var filterHasError);
if (filterHasError)
{
return;
}
foreach (var testElement in testElements)
{
var testCase = testElement.ToTestCase();
// Filter tests based on test case filters
if (filterExpression != null && filterExpression.MatchTestCase(testCase, (p) => this.TestMethodFilter.PropertyValueProvider(testCase, p)) == false)
if (!navigationSessions.TryGetValue(testSource, out var testNavigationSession))
{
continue;
testNavigationSession = PlatformServiceProvider.Instance.FileOperations.CreateNavigationSession(testSource);
navigationSessions.Add(testSource, testNavigationSession);
}
if (shouldCollectSourceInformation)
if (testNavigationSession != null)
{
string testSource = testElement.TestMethod.DeclaringAssemblyName ?? source;
var className = testElement.TestMethod.DeclaringClassFullName
?? testElement.TestMethod.FullClassName;
if (!navigationSessions.TryGetValue(testSource, out var testNavigationSession))
var methodName = testElement.TestMethod.Name;
// If it is async test method use compiler generated type and method name for navigation data.
if (!string.IsNullOrEmpty(testElement.AsyncTypeName))
{
testNavigationSession = PlatformServiceProvider.Instance.FileOperations.CreateNavigationSession(testSource);
navigationSessions.Add(testSource, testNavigationSession);
className = testElement.AsyncTypeName;
// compiler generated method name is "MoveNext".
methodName = "MoveNext";
}
if (testNavigationSession != null)
PlatformServiceProvider.Instance.FileOperations.GetNavigationData(
testNavigationSession,
className,
methodName,
out var minLineNumber,
out var fileName);
if (!string.IsNullOrEmpty(fileName))
{
var className = testElement.TestMethod.DeclaringClassFullName
?? testElement.TestMethod.FullClassName;
var methodName = testElement.TestMethod.Name;
// If it is async test method use compiler generated type and method name for navigation data.
if (!string.IsNullOrEmpty(testElement.AsyncTypeName))
{
className = testElement.AsyncTypeName;
// compiler generated method name is "MoveNext".
methodName = "MoveNext";
}
PlatformServiceProvider.Instance.FileOperations.GetNavigationData(
testNavigationSession,
className,
methodName,
out var minLineNumber,
out var fileName);
if (!string.IsNullOrEmpty(fileName))
{
testCase.LineNumber = minLineNumber;
testCase.CodeFilePath = fileName;
}
testCase.LineNumber = minLineNumber;
testCase.CodeFilePath = fileName;
}
}
discoverySink.SendTestCase(testCase);
}
discoverySink.SendTestCase(testCase);
}
finally
}
finally
{
foreach (object navigationSession in navigationSessions.Values)
{
foreach (object navigationSession in navigationSessions.Values)
{
PlatformServiceProvider.Instance.FileOperations.DisposeNavigationSession(navigationSession);
}
PlatformServiceProvider.Instance.FileOperations.DisposeNavigationSession(navigationSession);
}
}
}

Просмотреть файл

@ -1,184 +1,183 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
namespace Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.Execution
namespace Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.Execution;
using System;
using System.Globalization;
using System.IO;
using System.Threading;
using Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices;
using Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.Interface;
using Microsoft.VisualStudio.TestTools.UnitTesting.Logging;
/// <summary>
/// Listens for log messages and Debug.WriteLine
/// Note that this class is not thread-safe and thus should only be used when unit tests are being run serially.
/// </summary>
public class LogMessageListener : IDisposable
{
using System;
using System.Globalization;
using System.IO;
using System.Threading;
using Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices;
using Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.Interface;
using Microsoft.VisualStudio.TestTools.UnitTesting.Logging;
private static object traceLock = new();
private static int listenerCount;
private static ThreadSafeStringWriter redirectedDebugTrace;
/// <summary>
/// Listens for log messages and Debug.WriteLine
/// Note that this class is not thread-safe and thus should only be used when unit tests are being run serially.
/// Trace listener to capture Trace.WriteLines in the test cases
/// </summary>
public class LogMessageListener : IDisposable
private static ITraceListener traceListener;
private readonly ThreadSafeStringWriter redirectedStandardOutput;
private readonly ThreadSafeStringWriter redirectedStandardError;
private readonly bool captureDebugTraces;
/// <summary>
/// Trace listener Manager to perform operation on tracelistener objects.
/// </summary>
private ITraceListenerManager traceListenerManager;
private bool isDisposed;
/// <summary>
/// Initializes a new instance of the <see cref="LogMessageListener"/> class.
/// </summary>
/// <param name="captureDebugTraces">Captures debug traces if true.</param>
public LogMessageListener(bool captureDebugTraces)
{
private static object traceLock = new();
private static int listenerCount;
private static ThreadSafeStringWriter redirectedDebugTrace;
this.captureDebugTraces = captureDebugTraces;
/// <summary>
/// Trace listener to capture Trace.WriteLines in the test cases
/// </summary>
private static ITraceListener traceListener;
private readonly ThreadSafeStringWriter redirectedStandardOutput;
private readonly ThreadSafeStringWriter redirectedStandardError;
private readonly bool captureDebugTraces;
// Cache the original output/error streams and replace it with the own stream.
this.redirectedStandardOutput = new ThreadSafeStringWriter(CultureInfo.InvariantCulture, "out");
this.redirectedStandardError = new ThreadSafeStringWriter(CultureInfo.InvariantCulture, "err");
/// <summary>
/// Trace listener Manager to perform operation on tracelistener objects.
/// </summary>
private ITraceListenerManager traceListenerManager;
private bool isDisposed;
Logger.OnLogMessage += this.redirectedStandardOutput.WriteLine;
/// <summary>
/// Initializes a new instance of the <see cref="LogMessageListener"/> class.
/// </summary>
/// <param name="captureDebugTraces">Captures debug traces if true.</param>
public LogMessageListener(bool captureDebugTraces)
if (this.captureDebugTraces)
{
this.captureDebugTraces = captureDebugTraces;
// This is awkward, it has a side-effect of setting up Console output redirection, but the naming is suggesting that we are
// just getting TraceListener manager.
this.traceListenerManager = PlatformServiceProvider.Instance.GetTraceListenerManager(this.redirectedStandardOutput, this.redirectedStandardError);
// Cache the original output/error streams and replace it with the own stream.
this.redirectedStandardOutput = new ThreadSafeStringWriter(CultureInfo.InvariantCulture, "out");
this.redirectedStandardError = new ThreadSafeStringWriter(CultureInfo.InvariantCulture, "err");
// The Debug listener uses Debug.WriteLine and Debug.Write to write the messages, which end up written into Trace.Listeners.
// These listeners are static and hence shared across the whole process. We need to capture Debug output only for the current
// test, which was historically done by registering a listener in constructor of this class, and by removing the listener on Dispose.
// The newly created listener replaced previously registered listener, which was remembered, and put back on dispose.
//
// This works well as long as there are no tests running in parallel. But as soon as there are tests running in parallel. Then all the
// debug output of all tests will be output into the test that was most recently created (because it registered the listener most recently).
//
// To prevent mixing of outputs, the ThreadSafeStringWriter was re-implemented for net46 and newer to leverage AsyncLocal, which allows the writer to
// write only to the output of the current test. This leaves the LogMessageListener with only one task. Make sure that a trace listener is registered
// as long as there is any active test. This is still done by constructor and Dispose, but instead of replacing the listener every time, we use listenerCount
// to only add the listener when there is none, and remove it when we are the last one to dispose.
//
// This would break the behavior for net451, but that functionality was moved further into ThreadSafeStringWriter.
lock (traceLock)
{
if (listenerCount == 0)
{
redirectedDebugTrace = new ThreadSafeStringWriter(CultureInfo.InvariantCulture, "trace");
traceListener = PlatformServiceProvider.Instance.GetTraceListener(redirectedDebugTrace);
this.traceListenerManager.Add(traceListener);
}
Logger.OnLogMessage += this.redirectedStandardOutput.WriteLine;
listenerCount++;
}
}
}
~LogMessageListener()
{
this.Dispose(false);
}
/// <summary>
/// Gets logger output
/// </summary>
public string StandardOutput => this.redirectedStandardOutput.ToString();
/// <summary>
/// Gets 'Error' Output from the redirected stream
/// </summary>
public string StandardError => this.redirectedStandardError.ToString();
/// <summary>
/// Gets 'Trace' Output from the redirected stream
/// </summary>
public string DebugTrace
{
get
{
return redirectedDebugTrace?.ToString();
}
}
public string GetAndClearStandardOutput()
{
var output = this.redirectedStandardOutput.ToStringAndClear();
return output;
}
public string GetAndClearStandardError()
{
var output = this.redirectedStandardError.ToStringAndClear();
return output;
}
public string GetAndClearDebugTrace()
{
if (redirectedDebugTrace == null)
{
return null;
}
var output = redirectedDebugTrace.ToStringAndClear();
return output;
}
public void Dispose()
{
this.Dispose(true);
GC.SuppressFinalize(this);
}
private void Dispose(bool disposing)
{
if (disposing && !this.isDisposed)
{
this.isDisposed = true;
Logger.OnLogMessage -= this.redirectedStandardOutput.WriteLine;
Logger.OnLogMessage -= this.redirectedStandardError.WriteLine;
this.redirectedStandardOutput.Dispose();
this.redirectedStandardError.Dispose();
if (this.captureDebugTraces)
{
// This is awkward, it has a side-effect of setting up Console output redirection, but the naming is suggesting that we are
// just getting TraceListener manager.
this.traceListenerManager = PlatformServiceProvider.Instance.GetTraceListenerManager(this.redirectedStandardOutput, this.redirectedStandardError);
// The Debug listener uses Debug.WriteLine and Debug.Write to write the messages, which end up written into Trace.Listeners.
// These listeners are static and hence shared across the whole process. We need to capture Debug output only for the current
// test, which was historically done by registering a listener in constructor of this class, and by removing the listener on Dispose.
// The newly created listener replaced previously registered listener, which was remembered, and put back on dispose.
//
// This works well as long as there are no tests running in parallel. But as soon as there are tests running in parallel. Then all the
// debug output of all tests will be output into the test that was most recently created (because it registered the listener most recently).
//
// To prevent mixing of outputs, the ThreadSafeStringWriter was re-implemented for net46 and newer to leverage AsyncLocal, which allows the writer to
// write only to the output of the current test. This leaves the LogMessageListener with only one task. Make sure that a trace listener is registered
// as long as there is any active test. This is still done by constructor and Dispose, but instead of replacing the listener every time, we use listenerCount
// to only add the listener when there is none, and remove it when we are the last one to dispose.
//
// This would break the behavior for net451, but that functionality was moved further into ThreadSafeStringWriter.
lock (traceLock)
{
if (listenerCount == 0)
if (listenerCount == 1)
{
redirectedDebugTrace = new ThreadSafeStringWriter(CultureInfo.InvariantCulture, "trace");
traceListener = PlatformServiceProvider.Instance.GetTraceListener(redirectedDebugTrace);
this.traceListenerManager.Add(traceListener);
}
listenerCount++;
}
}
}
~LogMessageListener()
{
this.Dispose(false);
}
/// <summary>
/// Gets logger output
/// </summary>
public string StandardOutput => this.redirectedStandardOutput.ToString();
/// <summary>
/// Gets 'Error' Output from the redirected stream
/// </summary>
public string StandardError => this.redirectedStandardError.ToString();
/// <summary>
/// Gets 'Trace' Output from the redirected stream
/// </summary>
public string DebugTrace
{
get
{
return redirectedDebugTrace?.ToString();
}
}
public string GetAndClearStandardOutput()
{
var output = this.redirectedStandardOutput.ToStringAndClear();
return output;
}
public string GetAndClearStandardError()
{
var output = this.redirectedStandardError.ToStringAndClear();
return output;
}
public string GetAndClearDebugTrace()
{
if (redirectedDebugTrace == null)
{
return null;
}
var output = redirectedDebugTrace.ToStringAndClear();
return output;
}
public void Dispose()
{
this.Dispose(true);
GC.SuppressFinalize(this);
}
private void Dispose(bool disposing)
{
if (disposing && !this.isDisposed)
{
this.isDisposed = true;
Logger.OnLogMessage -= this.redirectedStandardOutput.WriteLine;
Logger.OnLogMessage -= this.redirectedStandardError.WriteLine;
this.redirectedStandardOutput.Dispose();
this.redirectedStandardError.Dispose();
if (this.captureDebugTraces)
{
lock (traceLock)
{
if (listenerCount == 1)
try
{
try
{
if (traceListener != null)
{
this.traceListenerManager.Remove(traceListener);
}
}
catch (Exception e)
{
// Catch all exceptions since Dispose should not throw.
PlatformServiceProvider.Instance.AdapterTraceLogger.LogError("ConsoleOutputRedirector.Dispose threw exception: {0}", e);
}
if (traceListener != null)
{
// Dispose trace manager and listeners
this.traceListenerManager.Dispose(traceListener);
this.traceListenerManager = null;
traceListener = null;
this.traceListenerManager.Remove(traceListener);
}
}
catch (Exception e)
{
// Catch all exceptions since Dispose should not throw.
PlatformServiceProvider.Instance.AdapterTraceLogger.LogError("ConsoleOutputRedirector.Dispose threw exception: {0}", e);
}
listenerCount--;
if (traceListener != null)
{
// Dispose trace manager and listeners
this.traceListenerManager.Dispose(traceListener);
this.traceListenerManager = null;
traceListener = null;
}
}
listenerCount--;
}
}
}

Просмотреть файл

@ -1,35 +1,34 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
namespace Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.Execution
namespace Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.Execution;
using System;
using System.Collections.Generic;
/// <summary>
/// Result of the run cleanup operation
/// </summary>
[Serializable]
internal class RunCleanupResult
{
using System;
using System.Collections.Generic;
/// <summary>
/// Gets or sets the standard out of the cleanup methods
/// </summary>
internal string StandardOut { get; set; }
/// <summary>
/// Result of the run cleanup operation
/// Gets or sets the standard error of the cleanup methods
/// </summary>
[Serializable]
internal class RunCleanupResult
{
/// <summary>
/// Gets or sets the standard out of the cleanup methods
/// </summary>
internal string StandardOut { get; set; }
internal string StandardError { get; set; }
/// <summary>
/// Gets or sets the standard error of the cleanup methods
/// </summary>
internal string StandardError { get; set; }
/// <summary>
/// Gets or sets the Debug trace of the cleanup methods.
/// </summary>
internal string DebugTrace { get; set; }
/// <summary>
/// Gets or sets the Debug trace of the cleanup methods.
/// </summary>
internal string DebugTrace { get; set; }
/// <summary>
/// Gets or sets the Warnings from the RunCleanup method
/// </summary>
internal IList<string> Warnings { get; set; }
}
/// <summary>
/// Gets or sets the Warnings from the RunCleanup method
/// </summary>
internal IList<string> Warnings { get; set; }
}

Просмотреть файл

@ -1,243 +1,242 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
namespace Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.Execution
{
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.Text;
using System.Text.RegularExpressions;
namespace Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.Execution;
using Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.ObjectModel;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.Text;
using System.Text.RegularExpressions;
using Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.ObjectModel;
/// <summary>
/// Provides helper methods to parse stack trace.
/// </summary>
internal static class StackTraceHelper
{
/// <summary>
/// Type that need to be excluded.
/// </summary>
private static List<string> typesToBeExcluded = new() { typeof(Microsoft.VisualStudio.TestTools.UnitTesting.Assert).Namespace, typeof(MSTestExecutor).Namespace };
/// <summary>
/// Provides helper methods to parse stack trace.
/// Gets the types whose methods should be ignored in the reported call stacks.
/// This is used to remove our stack that the user will not care about.
/// </summary>
internal static class StackTraceHelper
private static List<string> TypeToBeExcluded
{
/// <summary>
/// Type that need to be excluded.
/// </summary>
private static List<string> typesToBeExcluded = new() { typeof(Microsoft.VisualStudio.TestTools.UnitTesting.Assert).Namespace, typeof(MSTestExecutor).Namespace };
/// <summary>
/// Gets the types whose methods should be ignored in the reported call stacks.
/// This is used to remove our stack that the user will not care about.
/// </summary>
private static List<string> TypeToBeExcluded
get
{
get
{
return typesToBeExcluded;
}
}
/// <summary>
/// Gets the stack trace for an exception, including all stack traces for inner
/// exceptions.
/// </summary>
/// <param name="ex">
/// The exception.
/// </param>
/// <returns>
/// The <see cref="StackTraceInformation"/> for the provided exception.
/// </returns>
internal static StackTraceInformation GetStackTraceInformation(Exception ex)
{
Debug.Assert(ex != null, "exception should not be null.");
Stack<string> stackTraces = new();
for (Exception curException = ex;
curException != null;
curException = curException.InnerException)
{
// TODO:Fix the shadow stack-trace used in Private Object
// (Look-in Assertion.cs in the UnitTestFramework assembly)
// Sometimes the stack trace can be null, but the inner stack trace
// contains information. We are not interested in null stack traces
// so we simply ignore this case
try
{
if (curException.StackTrace != null)
{
stackTraces.Push(curException.StackTrace);
}
}
catch (Exception e)
{
// curException.StackTrace can throw exception, Although MSDN doc doesn't say that.
try
{
// try to get stack trace
if (e.StackTrace != null)
{
stackTraces.Push(e.StackTrace);
}
}
catch (Exception)
{
PlatformServiceProvider.Instance.AdapterTraceLogger.LogError(
"StackTraceHelper.GetStackTraceInformation: Failed to get stack trace info.");
}
}
}
StringBuilder result = new();
bool first = true;
while (stackTraces.Count != 0)
{
result.AppendFormat(
CultureInfo.CurrentCulture,
"{0} {1}{2}",
first ? string.Empty : (Resource.UTA_EndOfInnerExceptionTrace + Environment.NewLine),
stackTraces.Pop(),
Environment.NewLine);
first = false;
}
return CreateStackTraceInformation(ex, true, result.ToString());
}
/// <summary>
/// Removes all stack frames that refer to Microsoft.VisualStudio.TestTools.UnitTesting.Assertion
/// </summary>
/// <param name="stackTrace">
/// The stack Trace.
/// </param>
/// <returns>
/// The trimmed stack trace removing traces of the framework and adapter from the stack.
/// </returns>
internal static string TrimStackTrace(string stackTrace)
{
Debug.Assert(!string.IsNullOrEmpty(stackTrace), "stack trace should be non-empty.");
StringBuilder result = new(stackTrace.Length);
string[] stackFrames = Regex.Split(stackTrace, Environment.NewLine);
foreach (string stackFrame in stackFrames)
{
if (string.IsNullOrEmpty(stackFrame))
{
continue;
}
// Add the frame to the result if it does not refer to
// the assertion class in the test framework
bool hasReference = HasReferenceToUTF(stackFrame);
if (!hasReference)
{
result.Append(stackFrame);
result.Append(Environment.NewLine);
}
}
return result.ToString();
}
/// <summary>
/// Gets the exception messages, including the messages for all inner exceptions
/// recursively
/// </summary>
/// <param name="ex">
/// The exception.
/// </param>
/// <returns>
/// The aggregated exception message that considers inner exceptions.
/// </returns>
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Justification = "Requirement is to handle all kinds of user exceptions and message appropriately.")]
internal static string GetExceptionMessage(Exception ex)
{
Debug.Assert(ex != null, "exception should not be null.");
StringBuilder result = new();
bool first = true;
for (Exception curException = ex;
curException != null;
curException = curException.InnerException)
{
// Get the exception message. Need to check for errors because the Message property
// may have been overridden by the exception type in user code.
string msg;
try
{
msg = curException.Message;
}
catch
{
msg = string.Format(CultureInfo.CurrentCulture, Resource.UTF_FailedToGetExceptionMessage, curException.GetType());
}
result.AppendFormat(
CultureInfo.CurrentCulture,
"{0}{1}: {2}",
first ? string.Empty : " ---> ",
curException.GetType(),
msg);
first = false;
}
return result.ToString();
}
/// <summary>
/// Create stack trace information
/// </summary>
/// <param name="ex">
/// The exception.
/// </param>
/// <param name="checkInnerExceptions">
/// Whether the inner exception needs to be checked too.
/// </param>
/// <param name="stackTraceString">
/// The stack Trace String.
/// </param>
/// <returns>
/// The <see cref="StackTraceInformation"/>.
/// </returns>
internal static StackTraceInformation CreateStackTraceInformation(
Exception ex,
bool checkInnerExceptions,
string stackTraceString)
{
if (checkInnerExceptions && ex.InnerException != null)
{
return CreateStackTraceInformation(ex.InnerException, checkInnerExceptions, stackTraceString);
}
var stackTrace = StackTraceHelper.TrimStackTrace(stackTraceString);
if (!string.IsNullOrEmpty(stackTrace))
{
return new StackTraceInformation(stackTrace, null, 0, 0);
}
return null;
}
/// <summary>
/// Returns whether the parameter stackFrame has reference to UTF
/// </summary>
/// <param name="stackFrame">
/// The stack Frame.
/// </param>
/// <returns>
/// True if the framework or the adapter methods are in the stack frame.
/// </returns>
internal static bool HasReferenceToUTF(string stackFrame)
{
foreach (var type in TypeToBeExcluded)
{
if (stackFrame.IndexOf(type, StringComparison.Ordinal) > -1)
{
return true;
}
}
return false;
return typesToBeExcluded;
}
}
/// <summary>
/// Gets the stack trace for an exception, including all stack traces for inner
/// exceptions.
/// </summary>
/// <param name="ex">
/// The exception.
/// </param>
/// <returns>
/// The <see cref="StackTraceInformation"/> for the provided exception.
/// </returns>
internal static StackTraceInformation GetStackTraceInformation(Exception ex)
{
Debug.Assert(ex != null, "exception should not be null.");
Stack<string> stackTraces = new();
for (Exception curException = ex;
curException != null;
curException = curException.InnerException)
{
// TODO:Fix the shadow stack-trace used in Private Object
// (Look-in Assertion.cs in the UnitTestFramework assembly)
// Sometimes the stack trace can be null, but the inner stack trace
// contains information. We are not interested in null stack traces
// so we simply ignore this case
try
{
if (curException.StackTrace != null)
{
stackTraces.Push(curException.StackTrace);
}
}
catch (Exception e)
{
// curException.StackTrace can throw exception, Although MSDN doc doesn't say that.
try
{
// try to get stack trace
if (e.StackTrace != null)
{
stackTraces.Push(e.StackTrace);
}
}
catch (Exception)
{
PlatformServiceProvider.Instance.AdapterTraceLogger.LogError(
"StackTraceHelper.GetStackTraceInformation: Failed to get stack trace info.");
}
}
}
StringBuilder result = new();
bool first = true;
while (stackTraces.Count != 0)
{
result.AppendFormat(
CultureInfo.CurrentCulture,
"{0} {1}{2}",
first ? string.Empty : (Resource.UTA_EndOfInnerExceptionTrace + Environment.NewLine),
stackTraces.Pop(),
Environment.NewLine);
first = false;
}
return CreateStackTraceInformation(ex, true, result.ToString());
}
/// <summary>
/// Removes all stack frames that refer to Microsoft.VisualStudio.TestTools.UnitTesting.Assertion
/// </summary>
/// <param name="stackTrace">
/// The stack Trace.
/// </param>
/// <returns>
/// The trimmed stack trace removing traces of the framework and adapter from the stack.
/// </returns>
internal static string TrimStackTrace(string stackTrace)
{
Debug.Assert(!string.IsNullOrEmpty(stackTrace), "stack trace should be non-empty.");
StringBuilder result = new(stackTrace.Length);
string[] stackFrames = Regex.Split(stackTrace, Environment.NewLine);
foreach (string stackFrame in stackFrames)
{
if (string.IsNullOrEmpty(stackFrame))
{
continue;
}
// Add the frame to the result if it does not refer to
// the assertion class in the test framework
bool hasReference = HasReferenceToUTF(stackFrame);
if (!hasReference)
{
result.Append(stackFrame);
result.Append(Environment.NewLine);
}
}
return result.ToString();
}
/// <summary>
/// Gets the exception messages, including the messages for all inner exceptions
/// recursively
/// </summary>
/// <param name="ex">
/// The exception.
/// </param>
/// <returns>
/// The aggregated exception message that considers inner exceptions.
/// </returns>
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Justification = "Requirement is to handle all kinds of user exceptions and message appropriately.")]
internal static string GetExceptionMessage(Exception ex)
{
Debug.Assert(ex != null, "exception should not be null.");
StringBuilder result = new();
bool first = true;
for (Exception curException = ex;
curException != null;
curException = curException.InnerException)
{
// Get the exception message. Need to check for errors because the Message property
// may have been overridden by the exception type in user code.
string msg;
try
{
msg = curException.Message;
}
catch
{
msg = string.Format(CultureInfo.CurrentCulture, Resource.UTF_FailedToGetExceptionMessage, curException.GetType());
}
result.AppendFormat(
CultureInfo.CurrentCulture,
"{0}{1}: {2}",
first ? string.Empty : " ---> ",
curException.GetType(),
msg);
first = false;
}
return result.ToString();
}
/// <summary>
/// Create stack trace information
/// </summary>
/// <param name="ex">
/// The exception.
/// </param>
/// <param name="checkInnerExceptions">
/// Whether the inner exception needs to be checked too.
/// </param>
/// <param name="stackTraceString">
/// The stack Trace String.
/// </param>
/// <returns>
/// The <see cref="StackTraceInformation"/>.
/// </returns>
internal static StackTraceInformation CreateStackTraceInformation(
Exception ex,
bool checkInnerExceptions,
string stackTraceString)
{
if (checkInnerExceptions && ex.InnerException != null)
{
return CreateStackTraceInformation(ex.InnerException, checkInnerExceptions, stackTraceString);
}
var stackTrace = StackTraceHelper.TrimStackTrace(stackTraceString);
if (!string.IsNullOrEmpty(stackTrace))
{
return new StackTraceInformation(stackTrace, null, 0, 0);
}
return null;
}
/// <summary>
/// Returns whether the parameter stackFrame has reference to UTF
/// </summary>
/// <param name="stackFrame">
/// The stack Frame.
/// </param>
/// <returns>
/// True if the framework or the adapter methods are in the stack frame.
/// </returns>
internal static bool HasReferenceToUTF(string stackFrame)
{
foreach (var type in TypeToBeExcluded)
{
if (stackFrame.IndexOf(type, StringComparison.Ordinal) > -1)
{
return true;
}
}
return false;
}
}

Просмотреть файл

@ -1,52 +1,51 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
namespace Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.Execution
namespace Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.Execution;
using System.Collections.Generic;
using TestPlatformObjectModel = Microsoft.VisualStudio.TestPlatform.ObjectModel;
/// <summary>
/// Reads and parses the TcmTestProperties in order to populate them in TestRunParameters.
/// </summary>
internal static class TcmTestPropertiesProvider
{
using System.Collections.Generic;
using TestPlatformObjectModel = Microsoft.VisualStudio.TestPlatform.ObjectModel;
/// <summary>
/// Reads and parses the TcmTestProperties in order to populate them in TestRunParameters.
/// Gets tcm properties from test case.
/// </summary>
internal static class TcmTestPropertiesProvider
/// <param name="testCase">Test case.</param>
/// <returns>Tcm properties.</returns>
public static IDictionary<TestPlatformObjectModel.TestProperty, object> GetTcmProperties(TestPlatformObjectModel.TestCase testCase)
{
/// <summary>
/// Gets tcm properties from test case.
/// </summary>
/// <param name="testCase">Test case.</param>
/// <returns>Tcm properties.</returns>
public static IDictionary<TestPlatformObjectModel.TestProperty, object> GetTcmProperties(TestPlatformObjectModel.TestCase testCase)
var tcmProperties = new Dictionary<TestPlatformObjectModel.TestProperty, object>();
// Return empty properties when testCase is null or when test case id is zero.
if (testCase == null ||
testCase.GetPropertyValue<int>(Constants.TestCaseIdProperty, default) == 0)
{
var tcmProperties = new Dictionary<TestPlatformObjectModel.TestProperty, object>();
// Return empty properties when testCase is null or when test case id is zero.
if (testCase == null ||
testCase.GetPropertyValue<int>(Constants.TestCaseIdProperty, default) == 0)
{
return tcmProperties;
}
// Step 1: Add common properties.
tcmProperties[Constants.TestRunIdProperty] = testCase.GetPropertyValue<int>(Constants.TestRunIdProperty, default);
tcmProperties[Constants.TestPlanIdProperty] = testCase.GetPropertyValue<int>(Constants.TestPlanIdProperty, default);
tcmProperties[Constants.BuildConfigurationIdProperty] = testCase.GetPropertyValue<int>(Constants.BuildConfigurationIdProperty, default);
tcmProperties[Constants.BuildDirectoryProperty] = testCase.GetPropertyValue<string>(Constants.BuildDirectoryProperty, default);
tcmProperties[Constants.BuildFlavorProperty] = testCase.GetPropertyValue<string>(Constants.BuildFlavorProperty, default);
tcmProperties[Constants.BuildNumberProperty] = testCase.GetPropertyValue<string>(Constants.BuildNumberProperty, default);
tcmProperties[Constants.BuildPlatformProperty] = testCase.GetPropertyValue<string>(Constants.BuildPlatformProperty, default);
tcmProperties[Constants.BuildUriProperty] = testCase.GetPropertyValue<string>(Constants.BuildUriProperty, default);
tcmProperties[Constants.TfsServerCollectionUrlProperty] = testCase.GetPropertyValue<string>(Constants.TfsServerCollectionUrlProperty, default);
tcmProperties[Constants.TfsTeamProjectProperty] = testCase.GetPropertyValue<string>(Constants.TfsTeamProjectProperty, default);
tcmProperties[Constants.IsInLabEnvironmentProperty] = testCase.GetPropertyValue<bool>(Constants.IsInLabEnvironmentProperty, default);
// Step 2: Add test case specific properties.
tcmProperties[Constants.TestCaseIdProperty] = testCase.GetPropertyValue<int>(Constants.TestCaseIdProperty, default);
tcmProperties[Constants.TestConfigurationIdProperty] = testCase.GetPropertyValue<int>(Constants.TestConfigurationIdProperty, default);
tcmProperties[Constants.TestConfigurationNameProperty] = testCase.GetPropertyValue<string>(Constants.TestConfigurationNameProperty, default);
tcmProperties[Constants.TestPointIdProperty] = testCase.GetPropertyValue<int>(Constants.TestPointIdProperty, default);
return tcmProperties;
}
// Step 1: Add common properties.
tcmProperties[Constants.TestRunIdProperty] = testCase.GetPropertyValue<int>(Constants.TestRunIdProperty, default);
tcmProperties[Constants.TestPlanIdProperty] = testCase.GetPropertyValue<int>(Constants.TestPlanIdProperty, default);
tcmProperties[Constants.BuildConfigurationIdProperty] = testCase.GetPropertyValue<int>(Constants.BuildConfigurationIdProperty, default);
tcmProperties[Constants.BuildDirectoryProperty] = testCase.GetPropertyValue<string>(Constants.BuildDirectoryProperty, default);
tcmProperties[Constants.BuildFlavorProperty] = testCase.GetPropertyValue<string>(Constants.BuildFlavorProperty, default);
tcmProperties[Constants.BuildNumberProperty] = testCase.GetPropertyValue<string>(Constants.BuildNumberProperty, default);
tcmProperties[Constants.BuildPlatformProperty] = testCase.GetPropertyValue<string>(Constants.BuildPlatformProperty, default);
tcmProperties[Constants.BuildUriProperty] = testCase.GetPropertyValue<string>(Constants.BuildUriProperty, default);
tcmProperties[Constants.TfsServerCollectionUrlProperty] = testCase.GetPropertyValue<string>(Constants.TfsServerCollectionUrlProperty, default);
tcmProperties[Constants.TfsTeamProjectProperty] = testCase.GetPropertyValue<string>(Constants.TfsTeamProjectProperty, default);
tcmProperties[Constants.IsInLabEnvironmentProperty] = testCase.GetPropertyValue<bool>(Constants.IsInLabEnvironmentProperty, default);
// Step 2: Add test case specific properties.
tcmProperties[Constants.TestCaseIdProperty] = testCase.GetPropertyValue<int>(Constants.TestCaseIdProperty, default);
tcmProperties[Constants.TestConfigurationIdProperty] = testCase.GetPropertyValue<int>(Constants.TestConfigurationIdProperty, default);
tcmProperties[Constants.TestConfigurationNameProperty] = testCase.GetPropertyValue<string>(Constants.TestConfigurationNameProperty, default);
tcmProperties[Constants.TestPointIdProperty] = testCase.GetPropertyValue<int>(Constants.TestPointIdProperty, default);
return tcmProperties;
}
}

Просмотреть файл

@ -1,242 +1,241 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
namespace Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.Execution
namespace Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.Execution;
using System;
using System.Diagnostics.CodeAnalysis;
using System.Globalization;
using System.Reflection;
using Extensions;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using ObjectModel;
using UnitTestOutcome = Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.ObjectModel.UnitTestOutcome;
/// <summary>
/// Defines TestAssembly Info object
/// </summary>
public class TestAssemblyInfo
{
using System;
using System.Diagnostics.CodeAnalysis;
using System.Globalization;
using System.Reflection;
private MethodInfo assemblyCleanupMethod;
using Extensions;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using ObjectModel;
using UnitTestOutcome = Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.ObjectModel.UnitTestOutcome;
private MethodInfo assemblyInitializeMethod;
private readonly object assemblyInfoExecuteSyncObject;
/// <summary>
/// Defines TestAssembly Info object
/// Initializes a new instance of the <see cref="TestAssemblyInfo"/> class.
/// </summary>
public class TestAssemblyInfo
/// <param name="assembly">Sets the <see cref="Assembly"/> this class is representing. </param>
internal TestAssemblyInfo(Assembly assembly)
{
private MethodInfo assemblyCleanupMethod;
this.assemblyInfoExecuteSyncObject = new object();
this.Assembly = assembly;
}
private MethodInfo assemblyInitializeMethod;
private readonly object assemblyInfoExecuteSyncObject;
/// <summary>
/// Initializes a new instance of the <see cref="TestAssemblyInfo"/> class.
/// </summary>
/// <param name="assembly">Sets the <see cref="Assembly"/> this class is representing. </param>
internal TestAssemblyInfo(Assembly assembly)
/// <summary>
/// Gets <c>AssemblyInitialize</c> method for the assembly.
/// </summary>
public MethodInfo AssemblyInitializeMethod
{
get
{
this.assemblyInfoExecuteSyncObject = new object();
this.Assembly = assembly;
return this.assemblyInitializeMethod;
}
/// <summary>
/// Gets <c>AssemblyInitialize</c> method for the assembly.
/// </summary>
public MethodInfo AssemblyInitializeMethod
internal set
{
get
if (this.assemblyInitializeMethod != null)
{
return this.assemblyInitializeMethod;
var message = string.Format(CultureInfo.CurrentCulture, Resource.UTA_ErrorMultiAssemblyInit, this.assemblyInitializeMethod.DeclaringType.FullName);
throw new TypeInspectionException(message);
}
internal set
{
if (this.assemblyInitializeMethod != null)
{
var message = string.Format(CultureInfo.CurrentCulture, Resource.UTA_ErrorMultiAssemblyInit, this.assemblyInitializeMethod.DeclaringType.FullName);
throw new TypeInspectionException(message);
}
this.assemblyInitializeMethod = value;
}
}
this.assemblyInitializeMethod = value;
}
/// <summary>
/// Gets <c>AssemblyCleanup</c> method for the assembly.
/// </summary>
public MethodInfo AssemblyCleanupMethod
{
get
{
return this.assemblyCleanupMethod;
}
/// <summary>
/// Gets <c>AssemblyCleanup</c> method for the assembly.
/// </summary>
public MethodInfo AssemblyCleanupMethod
internal set
{
get
if (this.assemblyCleanupMethod != null)
{
return this.assemblyCleanupMethod;
string message = string.Format(CultureInfo.CurrentCulture, Resource.UTA_ErrorMultiAssemblyClean, this.assemblyCleanupMethod.DeclaringType.FullName);
throw new TypeInspectionException(message);
}
internal set
{
if (this.assemblyCleanupMethod != null)
{
string message = string.Format(CultureInfo.CurrentCulture, Resource.UTA_ErrorMultiAssemblyClean, this.assemblyCleanupMethod.DeclaringType.FullName);
throw new TypeInspectionException(message);
}
this.assemblyCleanupMethod = value;
}
this.assemblyCleanupMethod = value;
}
}
/// <summary>
/// Gets a value indicating whether <c>AssemblyInitialize</c> has been executed.
/// </summary>
public bool IsAssemblyInitializeExecuted { get; internal set; }
/// <summary>
/// Gets a value indicating whether <c>AssemblyInitialize</c> has been executed.
/// </summary>
public bool IsAssemblyInitializeExecuted { get; internal set; }
/// <summary>
/// Gets the assembly initialization exception.
/// </summary>
public Exception AssemblyInitializationException { get; internal set; }
/// <summary>
/// Gets the assembly initialization exception.
/// </summary>
public Exception AssemblyInitializationException { get; internal set; }
/// <summary>
/// Gets a value indicating whether this assembly has an executable <c>AssemblyCleanup</c> method.
/// </summary>
public bool HasExecutableCleanupMethod
{
get
{
// If no assembly cleanup, then continue with the next one.
if (this.AssemblyCleanupMethod == null)
{
return false;
}
return true;
}
}
/// <summary>
/// Gets the <see cref="Assembly"/> this class represents.
/// </summary>
internal Assembly Assembly { get; }
/// <summary>
/// Runs assembly initialize method.
/// </summary>
/// <param name="testContext"> The test context. </param>
/// <exception cref="TestFailedException"> Throws a test failed exception if the initialization method throws an exception. </exception>
[SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Justification = "Requirement is to handle all kinds of user exceptions and message appropriately.")]
public void RunAssemblyInitialize(TestContext testContext)
{
// No assembly initialize => nothing to do.
if (this.AssemblyInitializeMethod == null)
{
return;
}
if (testContext == null)
{
throw new NullReferenceException(Resource.TestContextIsNull);
}
// If assembly initialization is not done, then do it.
if (!this.IsAssemblyInitializeExecuted)
{
// Acquiring a lock is usually a costly operation which does not need to be
// performed every time if the assembly init is already executed.
lock (this.assemblyInfoExecuteSyncObject)
{
// Perform a check again.
if (!this.IsAssemblyInitializeExecuted)
{
try
{
this.AssemblyInitializeMethod.InvokeAsSynchronousTask(null, testContext);
}
catch (Exception ex)
{
this.AssemblyInitializationException = ex;
}
finally
{
this.IsAssemblyInitializeExecuted = true;
}
}
}
}
// If assemblyInitialization was successful, then don't do anything
if (this.AssemblyInitializationException == null)
{
return;
}
// Cache and return an already created TestFailedException.
if (this.AssemblyInitializationException is TestFailedException)
{
throw this.AssemblyInitializationException;
}
var realException = this.AssemblyInitializationException.InnerException ?? this.AssemblyInitializationException;
var outcome = realException is AssertInconclusiveException ? UnitTestOutcome.Inconclusive : UnitTestOutcome.Failed;
// Do not use StackTraceHelper.GetExceptionMessage(realException) as it prefixes the message with the exception type name.
var exceptionMessage = realException.TryGetMessage();
var errorMessage = string.Format(
CultureInfo.CurrentCulture,
Resource.UTA_AssemblyInitMethodThrows,
this.AssemblyInitializeMethod.DeclaringType.FullName,
this.AssemblyInitializeMethod.Name,
realException.GetType().ToString(),
exceptionMessage);
var exceptionStackTraceInfo = StackTraceHelper.GetStackTraceInformation(realException);
var testFailedException = new TestFailedException(outcome, errorMessage, exceptionStackTraceInfo, realException);
this.AssemblyInitializationException = testFailedException;
throw testFailedException;
}
/// <summary>
/// Run assembly cleanup methods
/// </summary>
/// <returns>
/// Any exception that can be thrown as part of a assembly cleanup as warning messages.
/// </returns>
[SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Justification = "Requirement is to handle all kinds of user exceptions and message appropriately.")]
public string RunAssemblyCleanup()
/// <summary>
/// Gets a value indicating whether this assembly has an executable <c>AssemblyCleanup</c> method.
/// </summary>
public bool HasExecutableCleanupMethod
{
get
{
// If no assembly cleanup, then continue with the next one.
if (this.AssemblyCleanupMethod == null)
{
return null;
return false;
}
return true;
}
}
/// <summary>
/// Gets the <see cref="Assembly"/> this class represents.
/// </summary>
internal Assembly Assembly { get; }
/// <summary>
/// Runs assembly initialize method.
/// </summary>
/// <param name="testContext"> The test context. </param>
/// <exception cref="TestFailedException"> Throws a test failed exception if the initialization method throws an exception. </exception>
[SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Justification = "Requirement is to handle all kinds of user exceptions and message appropriately.")]
public void RunAssemblyInitialize(TestContext testContext)
{
// No assembly initialize => nothing to do.
if (this.AssemblyInitializeMethod == null)
{
return;
}
if (testContext == null)
{
throw new NullReferenceException(Resource.TestContextIsNull);
}
// If assembly initialization is not done, then do it.
if (!this.IsAssemblyInitializeExecuted)
{
// Acquiring a lock is usually a costly operation which does not need to be
// performed every time if the assembly init is already executed.
lock (this.assemblyInfoExecuteSyncObject)
{
try
// Perform a check again.
if (!this.IsAssemblyInitializeExecuted)
{
this.AssemblyCleanupMethod.InvokeAsSynchronousTask(null);
return null;
try
{
this.AssemblyInitializeMethod.InvokeAsSynchronousTask(null, testContext);
}
catch (Exception ex)
{
this.AssemblyInitializationException = ex;
}
finally
{
this.IsAssemblyInitializeExecuted = true;
}
}
catch (Exception ex)
}
}
// If assemblyInitialization was successful, then don't do anything
if (this.AssemblyInitializationException == null)
{
return;
}
// Cache and return an already created TestFailedException.
if (this.AssemblyInitializationException is TestFailedException)
{
throw this.AssemblyInitializationException;
}
var realException = this.AssemblyInitializationException.InnerException ?? this.AssemblyInitializationException;
var outcome = realException is AssertInconclusiveException ? UnitTestOutcome.Inconclusive : UnitTestOutcome.Failed;
// Do not use StackTraceHelper.GetExceptionMessage(realException) as it prefixes the message with the exception type name.
var exceptionMessage = realException.TryGetMessage();
var errorMessage = string.Format(
CultureInfo.CurrentCulture,
Resource.UTA_AssemblyInitMethodThrows,
this.AssemblyInitializeMethod.DeclaringType.FullName,
this.AssemblyInitializeMethod.Name,
realException.GetType().ToString(),
exceptionMessage);
var exceptionStackTraceInfo = StackTraceHelper.GetStackTraceInformation(realException);
var testFailedException = new TestFailedException(outcome, errorMessage, exceptionStackTraceInfo, realException);
this.AssemblyInitializationException = testFailedException;
throw testFailedException;
}
/// <summary>
/// Run assembly cleanup methods
/// </summary>
/// <returns>
/// Any exception that can be thrown as part of a assembly cleanup as warning messages.
/// </returns>
[SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Justification = "Requirement is to handle all kinds of user exceptions and message appropriately.")]
public string RunAssemblyCleanup()
{
if (this.AssemblyCleanupMethod == null)
{
return null;
}
lock (this.assemblyInfoExecuteSyncObject)
{
try
{
this.AssemblyCleanupMethod.InvokeAsSynchronousTask(null);
return null;
}
catch (Exception ex)
{
var realException = ex.InnerException ?? ex;
string errorMessage;
// special case AssertFailedException to trim off part of the stack trace
if (realException is AssertFailedException ||
realException is AssertInconclusiveException)
{
var realException = ex.InnerException ?? ex;
string errorMessage;
// special case AssertFailedException to trim off part of the stack trace
if (realException is AssertFailedException ||
realException is AssertInconclusiveException)
{
errorMessage = realException.Message;
}
else
{
errorMessage = StackTraceHelper.GetExceptionMessage(realException);
}
return string.Format(
CultureInfo.CurrentCulture,
Resource.UTA_AssemblyCleanupMethodWasUnsuccesful,
this.AssemblyCleanupMethod.DeclaringType.Name,
this.AssemblyCleanupMethod.Name,
errorMessage,
StackTraceHelper.GetStackTraceInformation(realException)?.ErrorStackTrace);
errorMessage = realException.Message;
}
else
{
errorMessage = StackTraceHelper.GetExceptionMessage(realException);
}
return string.Format(
CultureInfo.CurrentCulture,
Resource.UTA_AssemblyCleanupMethodWasUnsuccesful,
this.AssemblyCleanupMethod.DeclaringType.Name,
this.AssemblyCleanupMethod.Name,
errorMessage,
StackTraceHelper.GetStackTraceInformation(realException)?.ErrorStackTrace);
}
}
}

Просмотреть файл

@ -1,72 +1,71 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
namespace Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.Execution
namespace Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.Execution;
using System;
using System.Security;
using Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.Helpers;
using Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.ObjectModel;
internal class TestAssemblySettingsProvider : MarshalByRefObject
{
using System;
using System.Security;
private readonly ReflectHelper reflectHelper;
using Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.Helpers;
using Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.ObjectModel;
internal class TestAssemblySettingsProvider : MarshalByRefObject
public TestAssemblySettingsProvider()
: this(ReflectHelper.Instance)
{
private readonly ReflectHelper reflectHelper;
}
public TestAssemblySettingsProvider()
: this(ReflectHelper.Instance)
{
}
internal TestAssemblySettingsProvider(ReflectHelper reflectHelper)
{
this.reflectHelper = reflectHelper;
}
internal TestAssemblySettingsProvider(ReflectHelper reflectHelper)
{
this.reflectHelper = reflectHelper;
}
/// <summary>
/// Returns object to be used for controlling lifetime, null means infinite lifetime.
/// </summary>
/// <returns>
/// The <see cref="object"/>.
/// </returns>
[SecurityCritical]
/// <summary>
/// Returns object to be used for controlling lifetime, null means infinite lifetime.
/// </summary>
/// <returns>
/// The <see cref="object"/>.
/// </returns>
[SecurityCritical]
#if NET5_0_OR_GREATER
[Obsolete]
[Obsolete]
#endif
public override object InitializeLifetimeService()
public override object InitializeLifetimeService()
{
return null;
}
internal TestAssemblySettings GetSettings(string source)
{
var testAssemblySettings = new TestAssemblySettings();
// Load the source.
var testAssembly = PlatformServiceProvider.Instance.FileOperations.LoadAssembly(source, isReflectionOnly: false);
var parallelizeAttribute = this.reflectHelper.GetParallelizeAttribute(testAssembly);
if (parallelizeAttribute != null)
{
return null;
testAssemblySettings.Workers = parallelizeAttribute.Workers;
testAssemblySettings.Scope = parallelizeAttribute.Scope;
if (testAssemblySettings.Workers == 0)
{
testAssemblySettings.Workers = Environment.ProcessorCount;
}
}
internal TestAssemblySettings GetSettings(string source)
testAssemblySettings.CanParallelizeAssembly = !this.reflectHelper.IsDoNotParallelizeSet(testAssembly);
var classCleanupSequencingAttribute = this.reflectHelper.GetClassCleanupAttribute(testAssembly);
if (classCleanupSequencingAttribute != null)
{
var testAssemblySettings = new TestAssemblySettings();
// Load the source.
var testAssembly = PlatformServiceProvider.Instance.FileOperations.LoadAssembly(source, isReflectionOnly: false);
var parallelizeAttribute = this.reflectHelper.GetParallelizeAttribute(testAssembly);
if (parallelizeAttribute != null)
{
testAssemblySettings.Workers = parallelizeAttribute.Workers;
testAssemblySettings.Scope = parallelizeAttribute.Scope;
if (testAssemblySettings.Workers == 0)
{
testAssemblySettings.Workers = Environment.ProcessorCount;
}
}
testAssemblySettings.CanParallelizeAssembly = !this.reflectHelper.IsDoNotParallelizeSet(testAssembly);
var classCleanupSequencingAttribute = this.reflectHelper.GetClassCleanupAttribute(testAssembly);
if (classCleanupSequencingAttribute != null)
{
testAssemblySettings.ClassCleanupLifecycle = classCleanupSequencingAttribute.CleanupBehavior;
}
return testAssemblySettings;
testAssemblySettings.ClassCleanupLifecycle = classCleanupSequencingAttribute.CleanupBehavior;
}
return testAssemblySettings;
}
}

Просмотреть файл

@ -1,42 +1,41 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
namespace Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.Execution
{
using System.Collections.Generic;
using System.Collections.ObjectModel;
namespace Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.Execution;
using Microsoft.VisualStudio.TestPlatform.ObjectModel;
using Microsoft.VisualStudio.TestPlatform.ObjectModel.Adapter;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using Microsoft.VisualStudio.TestPlatform.ObjectModel;
using Microsoft.VisualStudio.TestPlatform.ObjectModel.Adapter;
/// <summary>
/// The test case discovery sink.
/// </summary>
internal class TestCaseDiscoverySink : ITestCaseDiscoverySink
{
/// <summary>
/// Initializes a new instance of the <see cref="TestCaseDiscoverySink"/> class.
/// </summary>
public TestCaseDiscoverySink()
{
this.Tests = new Collection<TestCase>();
}
/// <summary>
/// The test case discovery sink.
/// Gets the tests.
/// </summary>
internal class TestCaseDiscoverySink : ITestCaseDiscoverySink
public ICollection<TestCase> Tests { get; private set; }
/// <summary>
/// Sends the test case.
/// </summary>
/// <param name="discoveredTest"> The discovered test. </param>
public void SendTestCase(TestCase discoveredTest)
{
/// <summary>
/// Initializes a new instance of the <see cref="TestCaseDiscoverySink"/> class.
/// </summary>
public TestCaseDiscoverySink()
if (discoveredTest != null)
{
this.Tests = new Collection<TestCase>();
}
/// <summary>
/// Gets the tests.
/// </summary>
public ICollection<TestCase> Tests { get; private set; }
/// <summary>
/// Sends the test case.
/// </summary>
/// <param name="discoveredTest"> The discovered test. </param>
public void SendTestCase(TestCase discoveredTest)
{
if (discoveredTest != null)
{
this.Tests.Add(discoveredTest);
}
this.Tests.Add(discoveredTest);
}
}
}

Просмотреть файл

@ -1,429 +1,428 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
namespace Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.Execution
namespace Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.Execution;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Globalization;
using System.Linq;
using System.Reflection;
using Extensions;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using ObjectModel;
using ObjectModelUnitTestOutcome = ObjectModel.UnitTestOutcome;
/// <summary>
/// Defines the TestClassInfo object
/// </summary>
public class TestClassInfo
{
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Globalization;
using System.Linq;
using System.Reflection;
using Extensions;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using ObjectModel;
using ObjectModelUnitTestOutcome = ObjectModel.UnitTestOutcome;
private readonly object testClassExecuteSyncObject;
private MethodInfo classCleanupMethod;
private MethodInfo classInitializeMethod;
private MethodInfo testCleanupMethod;
private MethodInfo testInitializeMethod;
/// <summary>
/// Defines the TestClassInfo object
/// Initializes a new instance of the <see cref="TestClassInfo"/> class.
/// </summary>
public class TestClassInfo
/// <param name="type">Underlying test class type.</param>
/// <param name="constructor">Constructor for the test class.</param>
/// <param name="testContextProperty">Reference to the <see cref="TestContext"/> property in test class.</param>
/// <param name="classAttribute">Test class attribute.</param>
/// <param name="parent">Parent assembly info.</param>
internal TestClassInfo(
Type type,
ConstructorInfo constructor,
PropertyInfo testContextProperty,
TestClassAttribute classAttribute,
TestAssemblyInfo parent)
{
private readonly object testClassExecuteSyncObject;
private MethodInfo classCleanupMethod;
private MethodInfo classInitializeMethod;
private MethodInfo testCleanupMethod;
private MethodInfo testInitializeMethod;
Debug.Assert(type != null, "Type should not be null");
Debug.Assert(constructor != null, "Constructor should not be null");
Debug.Assert(parent != null, "Parent should not be null");
Debug.Assert(classAttribute != null, "ClassAtribute should not be null");
/// <summary>
/// Initializes a new instance of the <see cref="TestClassInfo"/> class.
/// </summary>
/// <param name="type">Underlying test class type.</param>
/// <param name="constructor">Constructor for the test class.</param>
/// <param name="testContextProperty">Reference to the <see cref="TestContext"/> property in test class.</param>
/// <param name="classAttribute">Test class attribute.</param>
/// <param name="parent">Parent assembly info.</param>
internal TestClassInfo(
Type type,
ConstructorInfo constructor,
PropertyInfo testContextProperty,
TestClassAttribute classAttribute,
TestAssemblyInfo parent)
this.ClassType = type;
this.Constructor = constructor;
this.TestContextProperty = testContextProperty;
this.BaseClassCleanupMethodsStack = new Stack<MethodInfo>();
this.BaseClassInitAndCleanupMethods = new Queue<Tuple<MethodInfo, MethodInfo>>();
this.BaseTestInitializeMethodsQueue = new Queue<MethodInfo>();
this.BaseTestCleanupMethodsQueue = new Queue<MethodInfo>();
this.Parent = parent;
this.ClassAttribute = classAttribute;
this.testClassExecuteSyncObject = new object();
}
/// <summary>
/// Gets the class attribute.
/// </summary>
public TestClassAttribute ClassAttribute { get; private set; }
/// <summary>
/// Gets the class type.
/// </summary>
public Type ClassType { get; private set; }
/// <summary>
/// Gets the constructor.
/// </summary>
public ConstructorInfo Constructor { get; private set; }
/// <summary>
/// Gets the test context property.
/// </summary>
public PropertyInfo TestContextProperty { get; private set; }
/// <summary>
/// Gets the parent <see cref="TestAssemblyInfo"/>.
/// </summary>
public TestAssemblyInfo Parent { get; private set; }
/// <summary>
/// Gets the class initialize method.
/// </summary>
public MethodInfo ClassInitializeMethod
{
get
{
Debug.Assert(type != null, "Type should not be null");
Debug.Assert(constructor != null, "Constructor should not be null");
Debug.Assert(parent != null, "Parent should not be null");
Debug.Assert(classAttribute != null, "ClassAtribute should not be null");
this.ClassType = type;
this.Constructor = constructor;
this.TestContextProperty = testContextProperty;
this.BaseClassCleanupMethodsStack = new Stack<MethodInfo>();
this.BaseClassInitAndCleanupMethods = new Queue<Tuple<MethodInfo, MethodInfo>>();
this.BaseTestInitializeMethodsQueue = new Queue<MethodInfo>();
this.BaseTestCleanupMethodsQueue = new Queue<MethodInfo>();
this.Parent = parent;
this.ClassAttribute = classAttribute;
this.testClassExecuteSyncObject = new object();
return this.classInitializeMethod;
}
/// <summary>
/// Gets the class attribute.
/// </summary>
public TestClassAttribute ClassAttribute { get; private set; }
/// <summary>
/// Gets the class type.
/// </summary>
public Type ClassType { get; private set; }
/// <summary>
/// Gets the constructor.
/// </summary>
public ConstructorInfo Constructor { get; private set; }
/// <summary>
/// Gets the test context property.
/// </summary>
public PropertyInfo TestContextProperty { get; private set; }
/// <summary>
/// Gets the parent <see cref="TestAssemblyInfo"/>.
/// </summary>
public TestAssemblyInfo Parent { get; private set; }
/// <summary>
/// Gets the class initialize method.
/// </summary>
public MethodInfo ClassInitializeMethod
internal set
{
get
if (this.classInitializeMethod != null)
{
return this.classInitializeMethod;
var message = string.Format(CultureInfo.CurrentCulture, Resource.UTA_ErrorMultiClassInit, this.ClassType.FullName);
throw new TypeInspectionException(message);
}
internal set
{
if (this.classInitializeMethod != null)
{
var message = string.Format(CultureInfo.CurrentCulture, Resource.UTA_ErrorMultiClassInit, this.ClassType.FullName);
throw new TypeInspectionException(message);
}
this.classInitializeMethod = value;
}
}
/// <summary>
/// Gets a value indicating whether class initialize has executed.
/// </summary>
public bool IsClassInitializeExecuted { get; internal set; }
/// <summary>
/// Gets a value indicating whether class cleanup has executed.
/// </summary>
public bool IsClassCleanupExecuted { get; internal set; }
/// <summary>
/// Gets a stack of class cleanup methods to be executed.
/// </summary>
public Stack<MethodInfo> BaseClassCleanupMethodsStack { get; internal set; }
/// <summary>
/// Gets the exception thrown during <see cref="ClassInitializeAttribute"/> method invocation.
/// </summary>
public Exception ClassInitializationException { get; internal set; }
/// <summary>
/// Gets the exception thrown during <see cref="ClassCleanupAttribute"/> method invocation.
/// </summary>
public Exception ClassCleanupException { get; internal set; }
/// <summary>
/// Gets the class cleanup method.
/// </summary>
public MethodInfo ClassCleanupMethod
{
get
{
return this.classCleanupMethod;
}
internal set
{
if (this.classCleanupMethod != null)
{
var message = string.Format(CultureInfo.CurrentCulture, Resource.UTA_ErrorMultiClassClean, this.ClassType.FullName);
throw new TypeInspectionException(message);
}
this.classCleanupMethod = value;
}
}
/// <summary>
/// Gets a value indicating whether this class info has a executable cleanup method.
/// </summary>
public bool HasExecutableCleanupMethod
{
get
{
if (this.BaseClassCleanupMethodsStack.Any())
{
// If any base cleanups were pushed to the stack we need to run them
return true;
}
// If no class cleanup, then continue with the next one.
if (this.ClassCleanupMethod == null)
{
return false;
}
return true;
}
}
/// <summary>
/// Gets a tuples' queue of class initialize/cleanup methods to call for this type.
/// </summary>
public Queue<Tuple<MethodInfo, MethodInfo>> BaseClassInitAndCleanupMethods { get; private set; }
/// <summary>
/// Gets the test initialize method.
/// </summary>
public MethodInfo TestInitializeMethod
{
get
{
return this.testInitializeMethod;
}
internal set
{
if (this.testInitializeMethod != null)
{
var message = string.Format(CultureInfo.CurrentCulture, Resource.UTA_ErrorMultiInit, this.ClassType.FullName);
throw new TypeInspectionException(message);
}
this.testInitializeMethod = value;
}
}
/// <summary>
/// Gets the test cleanup method.
/// </summary>
public MethodInfo TestCleanupMethod
{
get
{
return this.testCleanupMethod;
}
internal set
{
if (this.testCleanupMethod != null)
{
var message = string.Format(CultureInfo.CurrentCulture, Resource.UTA_ErrorMultiClean, this.ClassType.FullName);
throw new TypeInspectionException(message);
}
this.testCleanupMethod = value;
}
}
/// <summary>
/// Gets a queue of test initialize methods to call for this type.
/// </summary>
public Queue<MethodInfo> BaseTestInitializeMethodsQueue { get; private set; }
/// <summary>
/// Gets a queue of test cleanup methods to call for this type.
/// </summary>
public Queue<MethodInfo> BaseTestCleanupMethodsQueue { get; private set; }
/// <summary>
/// Runs the class initialize method.
/// </summary>
/// <param name="testContext"> The test context. </param>
/// <exception cref="TestFailedException"> Throws a test failed exception if the initialization method throws an exception. </exception>
[SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Justification = "Requirement is to handle all kinds of user exceptions and message appropriately.")]
public void RunClassInitialize(TestContext testContext)
{
// If no class initialize and no base class initialize, return
if (this.ClassInitializeMethod is null && !this.BaseClassInitAndCleanupMethods.Any(p => p.Item1 != null))
{
return;
}
if (testContext == null)
{
throw new NullReferenceException(Resource.TestContextIsNull);
}
MethodInfo initializeMethod = null;
string failedClassInitializeMethodName = string.Empty;
// If class initialization is not done, then do it.
if (!this.IsClassInitializeExecuted)
{
// Acquiring a lock is usually a costly operation which does not need to be
// performed every time if the class init is already executed.
lock (this.testClassExecuteSyncObject)
{
// Perform a check again.
if (!this.IsClassInitializeExecuted)
{
try
{
// ClassInitialize methods for base classes are called in reverse order of discovery
// Base -> Child TestClass
var baseClassInitializeStack = new Stack<Tuple<MethodInfo, MethodInfo>>(
this.BaseClassInitAndCleanupMethods.Where(p => p.Item1 != null));
while (baseClassInitializeStack.Count > 0)
{
var baseInitCleanupMethods = baseClassInitializeStack.Pop();
initializeMethod = baseInitCleanupMethods.Item1;
initializeMethod?.InvokeAsSynchronousTask(null, testContext);
if (baseInitCleanupMethods.Item2 != null)
{
this.BaseClassCleanupMethodsStack.Push(baseInitCleanupMethods.Item2);
}
}
initializeMethod = null;
if (this.classInitializeMethod != null)
{
this.ClassInitializeMethod.InvokeAsSynchronousTask(null, testContext);
}
}
catch (Exception ex)
{
this.ClassInitializationException = ex;
failedClassInitializeMethodName = initializeMethod?.Name ?? this.ClassInitializeMethod.Name;
}
finally
{
this.IsClassInitializeExecuted = true;
}
}
}
}
// If classInitialization was successful, then don't do anything
if (this.ClassInitializationException == null)
{
return;
}
if (this.ClassInitializationException is TestFailedException)
{
throw this.ClassInitializationException;
}
// Fail the current test if it was a failure.
var realException = this.ClassInitializationException.InnerException ?? this.ClassInitializationException;
var outcome = realException is AssertInconclusiveException ? ObjectModelUnitTestOutcome.Inconclusive : ObjectModelUnitTestOutcome.Failed;
// Do not use StackTraceHelper.GetExceptionMessage(realException) as it prefixes the message with the exception type name.
var exceptionMessage = realException.TryGetMessage();
var errorMessage = string.Format(
CultureInfo.CurrentCulture,
Resource.UTA_ClassInitMethodThrows,
this.ClassType.FullName,
failedClassInitializeMethodName,
realException.GetType().ToString(),
exceptionMessage);
var exceptionStackTraceInfo = StackTraceHelper.GetStackTraceInformation(realException);
var testFailedException = new TestFailedException(outcome, errorMessage, exceptionStackTraceInfo, realException);
this.ClassInitializationException = testFailedException;
throw testFailedException;
}
/// <summary>
/// Run class cleanup methods
/// </summary>
/// <param name="classCleanupLifecycle">The current lifecyle position that ClassCleanup is executing from</param>
/// <returns>
/// Any exception that can be thrown as part of a class cleanup as warning messages.
/// </returns>
public string RunClassCleanup(ClassCleanupBehavior classCleanupLifecycle = ClassCleanupBehavior.EndOfAssembly)
{
if (this.ClassCleanupMethod is null && this.BaseClassInitAndCleanupMethods.All(p => p.Item2 == null))
{
return null;
}
if (!this.IsClassCleanupExecuted)
{
lock (this.testClassExecuteSyncObject)
{
if (this.IsClassCleanupExecuted)
{
return null;
}
if (this.IsClassInitializeExecuted || this.ClassInitializeMethod is null)
{
MethodInfo classCleanupMethod = null;
try
{
classCleanupMethod = this.ClassCleanupMethod;
classCleanupMethod?.InvokeAsSynchronousTask(null);
var baseClassCleanupQueue = new Queue<MethodInfo>(this.BaseClassCleanupMethodsStack);
while (baseClassCleanupQueue.Count > 0)
{
classCleanupMethod = baseClassCleanupQueue.Dequeue();
classCleanupMethod?.InvokeAsSynchronousTask(null);
}
this.IsClassCleanupExecuted = true;
return null;
}
catch (Exception exception)
{
var realException = exception.InnerException ?? exception;
this.ClassCleanupException = realException;
string errorMessage;
// special case AssertFailedException to trim off part of the stack trace
if (realException is AssertFailedException ||
realException is AssertInconclusiveException)
{
errorMessage = realException.Message;
}
else
{
errorMessage = StackTraceHelper.GetExceptionMessage(realException);
}
var exceptionStackTraceInfo = realException.TryGetStackTraceInformation();
errorMessage = string.Format(
CultureInfo.CurrentCulture,
Resource.UTA_ClassCleanupMethodWasUnsuccesful,
classCleanupMethod.DeclaringType.Name,
classCleanupMethod.Name,
errorMessage,
exceptionStackTraceInfo?.ErrorStackTrace);
if (classCleanupLifecycle == ClassCleanupBehavior.EndOfClass)
{
var testFailedException = new TestFailedException(ObjectModelUnitTestOutcome.Failed, errorMessage, exceptionStackTraceInfo);
this.ClassCleanupException = testFailedException;
throw testFailedException;
}
return errorMessage;
}
}
}
}
return null;
this.classInitializeMethod = value;
}
}
/// <summary>
/// Gets a value indicating whether class initialize has executed.
/// </summary>
public bool IsClassInitializeExecuted { get; internal set; }
/// <summary>
/// Gets a value indicating whether class cleanup has executed.
/// </summary>
public bool IsClassCleanupExecuted { get; internal set; }
/// <summary>
/// Gets a stack of class cleanup methods to be executed.
/// </summary>
public Stack<MethodInfo> BaseClassCleanupMethodsStack { get; internal set; }
/// <summary>
/// Gets the exception thrown during <see cref="ClassInitializeAttribute"/> method invocation.
/// </summary>
public Exception ClassInitializationException { get; internal set; }
/// <summary>
/// Gets the exception thrown during <see cref="ClassCleanupAttribute"/> method invocation.
/// </summary>
public Exception ClassCleanupException { get; internal set; }
/// <summary>
/// Gets the class cleanup method.
/// </summary>
public MethodInfo ClassCleanupMethod
{
get
{
return this.classCleanupMethod;
}
internal set
{
if (this.classCleanupMethod != null)
{
var message = string.Format(CultureInfo.CurrentCulture, Resource.UTA_ErrorMultiClassClean, this.ClassType.FullName);
throw new TypeInspectionException(message);
}
this.classCleanupMethod = value;
}
}
/// <summary>
/// Gets a value indicating whether this class info has a executable cleanup method.
/// </summary>
public bool HasExecutableCleanupMethod
{
get
{
if (this.BaseClassCleanupMethodsStack.Any())
{
// If any base cleanups were pushed to the stack we need to run them
return true;
}
// If no class cleanup, then continue with the next one.
if (this.ClassCleanupMethod == null)
{
return false;
}
return true;
}
}
/// <summary>
/// Gets a tuples' queue of class initialize/cleanup methods to call for this type.
/// </summary>
public Queue<Tuple<MethodInfo, MethodInfo>> BaseClassInitAndCleanupMethods { get; private set; }
/// <summary>
/// Gets the test initialize method.
/// </summary>
public MethodInfo TestInitializeMethod
{
get
{
return this.testInitializeMethod;
}
internal set
{
if (this.testInitializeMethod != null)
{
var message = string.Format(CultureInfo.CurrentCulture, Resource.UTA_ErrorMultiInit, this.ClassType.FullName);
throw new TypeInspectionException(message);
}
this.testInitializeMethod = value;
}
}
/// <summary>
/// Gets the test cleanup method.
/// </summary>
public MethodInfo TestCleanupMethod
{
get
{
return this.testCleanupMethod;
}
internal set
{
if (this.testCleanupMethod != null)
{
var message = string.Format(CultureInfo.CurrentCulture, Resource.UTA_ErrorMultiClean, this.ClassType.FullName);
throw new TypeInspectionException(message);
}
this.testCleanupMethod = value;
}
}
/// <summary>
/// Gets a queue of test initialize methods to call for this type.
/// </summary>
public Queue<MethodInfo> BaseTestInitializeMethodsQueue { get; private set; }
/// <summary>
/// Gets a queue of test cleanup methods to call for this type.
/// </summary>
public Queue<MethodInfo> BaseTestCleanupMethodsQueue { get; private set; }
/// <summary>
/// Runs the class initialize method.
/// </summary>
/// <param name="testContext"> The test context. </param>
/// <exception cref="TestFailedException"> Throws a test failed exception if the initialization method throws an exception. </exception>
[SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Justification = "Requirement is to handle all kinds of user exceptions and message appropriately.")]
public void RunClassInitialize(TestContext testContext)
{
// If no class initialize and no base class initialize, return
if (this.ClassInitializeMethod is null && !this.BaseClassInitAndCleanupMethods.Any(p => p.Item1 != null))
{
return;
}
if (testContext == null)
{
throw new NullReferenceException(Resource.TestContextIsNull);
}
MethodInfo initializeMethod = null;
string failedClassInitializeMethodName = string.Empty;
// If class initialization is not done, then do it.
if (!this.IsClassInitializeExecuted)
{
// Acquiring a lock is usually a costly operation which does not need to be
// performed every time if the class init is already executed.
lock (this.testClassExecuteSyncObject)
{
// Perform a check again.
if (!this.IsClassInitializeExecuted)
{
try
{
// ClassInitialize methods for base classes are called in reverse order of discovery
// Base -> Child TestClass
var baseClassInitializeStack = new Stack<Tuple<MethodInfo, MethodInfo>>(
this.BaseClassInitAndCleanupMethods.Where(p => p.Item1 != null));
while (baseClassInitializeStack.Count > 0)
{
var baseInitCleanupMethods = baseClassInitializeStack.Pop();
initializeMethod = baseInitCleanupMethods.Item1;
initializeMethod?.InvokeAsSynchronousTask(null, testContext);
if (baseInitCleanupMethods.Item2 != null)
{
this.BaseClassCleanupMethodsStack.Push(baseInitCleanupMethods.Item2);
}
}
initializeMethod = null;
if (this.classInitializeMethod != null)
{
this.ClassInitializeMethod.InvokeAsSynchronousTask(null, testContext);
}
}
catch (Exception ex)
{
this.ClassInitializationException = ex;
failedClassInitializeMethodName = initializeMethod?.Name ?? this.ClassInitializeMethod.Name;
}
finally
{
this.IsClassInitializeExecuted = true;
}
}
}
}
// If classInitialization was successful, then don't do anything
if (this.ClassInitializationException == null)
{
return;
}
if (this.ClassInitializationException is TestFailedException)
{
throw this.ClassInitializationException;
}
// Fail the current test if it was a failure.
var realException = this.ClassInitializationException.InnerException ?? this.ClassInitializationException;
var outcome = realException is AssertInconclusiveException ? ObjectModelUnitTestOutcome.Inconclusive : ObjectModelUnitTestOutcome.Failed;
// Do not use StackTraceHelper.GetExceptionMessage(realException) as it prefixes the message with the exception type name.
var exceptionMessage = realException.TryGetMessage();
var errorMessage = string.Format(
CultureInfo.CurrentCulture,
Resource.UTA_ClassInitMethodThrows,
this.ClassType.FullName,
failedClassInitializeMethodName,
realException.GetType().ToString(),
exceptionMessage);
var exceptionStackTraceInfo = StackTraceHelper.GetStackTraceInformation(realException);
var testFailedException = new TestFailedException(outcome, errorMessage, exceptionStackTraceInfo, realException);
this.ClassInitializationException = testFailedException;
throw testFailedException;
}
/// <summary>
/// Run class cleanup methods
/// </summary>
/// <param name="classCleanupLifecycle">The current lifecyle position that ClassCleanup is executing from</param>
/// <returns>
/// Any exception that can be thrown as part of a class cleanup as warning messages.
/// </returns>
public string RunClassCleanup(ClassCleanupBehavior classCleanupLifecycle = ClassCleanupBehavior.EndOfAssembly)
{
if (this.ClassCleanupMethod is null && this.BaseClassInitAndCleanupMethods.All(p => p.Item2 == null))
{
return null;
}
if (!this.IsClassCleanupExecuted)
{
lock (this.testClassExecuteSyncObject)
{
if (this.IsClassCleanupExecuted)
{
return null;
}
if (this.IsClassInitializeExecuted || this.ClassInitializeMethod is null)
{
MethodInfo classCleanupMethod = null;
try
{
classCleanupMethod = this.ClassCleanupMethod;
classCleanupMethod?.InvokeAsSynchronousTask(null);
var baseClassCleanupQueue = new Queue<MethodInfo>(this.BaseClassCleanupMethodsStack);
while (baseClassCleanupQueue.Count > 0)
{
classCleanupMethod = baseClassCleanupQueue.Dequeue();
classCleanupMethod?.InvokeAsSynchronousTask(null);
}
this.IsClassCleanupExecuted = true;
return null;
}
catch (Exception exception)
{
var realException = exception.InnerException ?? exception;
this.ClassCleanupException = realException;
string errorMessage;
// special case AssertFailedException to trim off part of the stack trace
if (realException is AssertFailedException ||
realException is AssertInconclusiveException)
{
errorMessage = realException.Message;
}
else
{
errorMessage = StackTraceHelper.GetExceptionMessage(realException);
}
var exceptionStackTraceInfo = realException.TryGetStackTraceInformation();
errorMessage = string.Format(
CultureInfo.CurrentCulture,
Resource.UTA_ClassCleanupMethodWasUnsuccesful,
classCleanupMethod.DeclaringType.Name,
classCleanupMethod.Name,
errorMessage,
exceptionStackTraceInfo?.ErrorStackTrace);
if (classCleanupLifecycle == ClassCleanupBehavior.EndOfClass)
{
var testFailedException = new TestFailedException(ObjectModelUnitTestOutcome.Failed, errorMessage, exceptionStackTraceInfo);
this.ClassCleanupException = testFailedException;
throw testFailedException;
}
return errorMessage;
}
}
}
}
return null;
}
}

Просмотреть файл

@ -1,517 +1,516 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
namespace Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.Execution
{
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
namespace Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.Execution;
using Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.Extensions;
using Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.Helpers;
using Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.ObjectModel;
using Microsoft.VisualStudio.TestPlatform.ObjectModel;
using Microsoft.VisualStudio.TestPlatform.ObjectModel.Adapter;
using Microsoft.VisualStudio.TestPlatform.ObjectModel.Logging;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.Extensions;
using Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.Helpers;
using Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.ObjectModel;
using Microsoft.VisualStudio.TestPlatform.ObjectModel;
using Microsoft.VisualStudio.TestPlatform.ObjectModel.Adapter;
using Microsoft.VisualStudio.TestPlatform.ObjectModel.Logging;
using Microsoft.VisualStudio.TestTools.UnitTesting;
/// <summary>
/// Class responsible for execution of tests at assembly level and sending tests via framework handle
/// </summary>
public class TestExecutionManager
{
/// <summary>
/// Specifies whether the test run is canceled or not
/// </summary>
private TestRunCancellationToken cancellationToken;
/// <summary>
/// Class responsible for execution of tests at assembly level and sending tests via framework handle
/// Dictionary for test run parameters
/// </summary>
public class TestExecutionManager
private readonly IDictionary<string, object> sessionParameters;
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1810:InitializeReferenceTypeStaticFieldsInline", Justification = "Need to over-write the keys in dictionary.")]
public TestExecutionManager()
{
/// <summary>
/// Specifies whether the test run is canceled or not
/// </summary>
private TestRunCancellationToken cancellationToken;
this.TestMethodFilter = new TestMethodFilter();
this.sessionParameters = new Dictionary<string, object>();
}
/// <summary>
/// Dictionary for test run parameters
/// </summary>
private readonly IDictionary<string, object> sessionParameters;
/// <summary>
/// Gets or sets method filter for filtering tests
/// </summary>
private TestMethodFilter TestMethodFilter { get; set; }
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1810:InitializeReferenceTypeStaticFieldsInline", Justification = "Need to over-write the keys in dictionary.")]
public TestExecutionManager()
/// <summary>
/// Gets or sets a value indicating whether any test executed has failed.
/// </summary>
private bool HasAnyTestFailed { get; set; }
/// <summary>
/// Runs the tests.
/// </summary>
/// <param name="tests">Tests to be run.</param>
/// <param name="runContext">Context to use when executing the tests.</param>
/// <param name="frameworkHandle">Handle to the framework to record results and to do framework operations.</param>
/// <param name="runCancellationToken">Test run cancellation token</param>
public void RunTests(IEnumerable<TestCase> tests, IRunContext runContext, IFrameworkHandle frameworkHandle, TestRunCancellationToken runCancellationToken)
{
Debug.Assert(tests != null, "tests");
Debug.Assert(runContext != null, "runContext");
Debug.Assert(frameworkHandle != null, "frameworkHandle");
Debug.Assert(runCancellationToken != null, "runCancellationToken");
this.cancellationToken = runCancellationToken;
var isDeploymentDone = PlatformServiceProvider.Instance.TestDeployment.Deploy(tests, runContext, frameworkHandle);
// Placing this after deployment since we need information post deployment that we pass in as properties.
this.CacheSessionParameters(runContext, frameworkHandle);
// Execute the tests
this.ExecuteTests(tests, runContext, frameworkHandle, isDeploymentDone);
if (!this.HasAnyTestFailed)
{
this.TestMethodFilter = new TestMethodFilter();
this.sessionParameters = new Dictionary<string, object>();
PlatformServiceProvider.Instance.TestDeployment.Cleanup();
}
}
public void RunTests(IEnumerable<string> sources, IRunContext runContext, IFrameworkHandle frameworkHandle, TestRunCancellationToken cancellationToken)
{
this.cancellationToken = cancellationToken;
var discoverySink = new TestCaseDiscoverySink();
var tests = new List<TestCase>();
// deploy everything first.
foreach (var source in sources)
{
if (this.cancellationToken.Canceled)
{
break;
}
var logger = (IMessageLogger)frameworkHandle;
// discover the tests
this.GetUnitTestDiscoverer().DiscoverTestsInSource(source, logger, discoverySink, runContext);
tests.AddRange(discoverySink.Tests);
// Clear discoverSinksTests so that it just stores test for one source at one point of time
discoverySink.Tests.Clear();
}
/// <summary>
/// Gets or sets method filter for filtering tests
/// </summary>
private TestMethodFilter TestMethodFilter { get; set; }
bool isDeploymentDone = PlatformServiceProvider.Instance.TestDeployment.Deploy(tests, runContext, frameworkHandle);
/// <summary>
/// Gets or sets a value indicating whether any test executed has failed.
/// </summary>
private bool HasAnyTestFailed { get; set; }
// Placing this after deployment since we need information post deployment that we pass in as properties.
this.CacheSessionParameters(runContext, frameworkHandle);
/// <summary>
/// Runs the tests.
/// </summary>
/// <param name="tests">Tests to be run.</param>
/// <param name="runContext">Context to use when executing the tests.</param>
/// <param name="frameworkHandle">Handle to the framework to record results and to do framework operations.</param>
/// <param name="runCancellationToken">Test run cancellation token</param>
public void RunTests(IEnumerable<TestCase> tests, IRunContext runContext, IFrameworkHandle frameworkHandle, TestRunCancellationToken runCancellationToken)
// Run tests.
this.ExecuteTests(tests, runContext, frameworkHandle, isDeploymentDone);
if (!this.HasAnyTestFailed)
{
Debug.Assert(tests != null, "tests");
Debug.Assert(runContext != null, "runContext");
Debug.Assert(frameworkHandle != null, "frameworkHandle");
Debug.Assert(runCancellationToken != null, "runCancellationToken");
PlatformServiceProvider.Instance.TestDeployment.Cleanup();
}
}
this.cancellationToken = runCancellationToken;
/// <summary>
/// Execute the parameter tests
/// </summary>
/// <param name="tests">Tests to execute.</param>
/// <param name="runContext">The run context.</param>
/// <param name="frameworkHandle">Handle to record test start/end/results.</param>
/// <param name="isDeploymentDone">Indicates if deployment is done.</param>
internal virtual void ExecuteTests(IEnumerable<TestCase> tests, IRunContext runContext, IFrameworkHandle frameworkHandle, bool isDeploymentDone)
{
var testsBySource = from test in tests
group test by test.Source into testGroup
select new { Source = testGroup.Key, Tests = testGroup };
var isDeploymentDone = PlatformServiceProvider.Instance.TestDeployment.Deploy(tests, runContext, frameworkHandle);
foreach (var group in testsBySource)
{
this.ExecuteTestsInSource(group.Tests, runContext, frameworkHandle, group.Source, isDeploymentDone);
}
}
// Placing this after deployment since we need information post deployment that we pass in as properties.
this.CacheSessionParameters(runContext, frameworkHandle);
internal virtual UnitTestDiscoverer GetUnitTestDiscoverer()
{
return new UnitTestDiscoverer();
}
// Execute the tests
this.ExecuteTests(tests, runContext, frameworkHandle, isDeploymentDone);
if (!this.HasAnyTestFailed)
{
PlatformServiceProvider.Instance.TestDeployment.Cleanup();
}
internal void SendTestResults(TestCase test, UnitTestResult[] unitTestResults, DateTimeOffset startTime, DateTimeOffset endTime, ITestExecutionRecorder testExecutionRecorder)
{
if (!(unitTestResults?.Length > 0))
{
return;
}
public void RunTests(IEnumerable<string> sources, IRunContext runContext, IFrameworkHandle frameworkHandle, TestRunCancellationToken cancellationToken)
foreach (var unitTestResult in unitTestResults)
{
this.cancellationToken = cancellationToken;
var discoverySink = new TestCaseDiscoverySink();
var tests = new List<TestCase>();
// deploy everything first.
foreach (var source in sources)
if (test == null)
{
if (this.cancellationToken.Canceled)
{
break;
}
var logger = (IMessageLogger)frameworkHandle;
// discover the tests
this.GetUnitTestDiscoverer().DiscoverTestsInSource(source, logger, discoverySink, runContext);
tests.AddRange(discoverySink.Tests);
// Clear discoverSinksTests so that it just stores test for one source at one point of time
discoverySink.Tests.Clear();
continue;
}
bool isDeploymentDone = PlatformServiceProvider.Instance.TestDeployment.Deploy(tests, runContext, frameworkHandle);
var testResult = unitTestResult.ToTestResult(test, startTime, endTime, MSTestSettings.CurrentSettings);
// Placing this after deployment since we need information post deployment that we pass in as properties.
this.CacheSessionParameters(runContext, frameworkHandle);
// Run tests.
this.ExecuteTests(tests, runContext, frameworkHandle, isDeploymentDone);
if (!this.HasAnyTestFailed)
if (unitTestResult.DatarowIndex >= 0)
{
PlatformServiceProvider.Instance.TestDeployment.Cleanup();
}
}
/// <summary>
/// Execute the parameter tests
/// </summary>
/// <param name="tests">Tests to execute.</param>
/// <param name="runContext">The run context.</param>
/// <param name="frameworkHandle">Handle to record test start/end/results.</param>
/// <param name="isDeploymentDone">Indicates if deployment is done.</param>
internal virtual void ExecuteTests(IEnumerable<TestCase> tests, IRunContext runContext, IFrameworkHandle frameworkHandle, bool isDeploymentDone)
{
var testsBySource = from test in tests
group test by test.Source into testGroup
select new { Source = testGroup.Key, Tests = testGroup };
foreach (var group in testsBySource)
{
this.ExecuteTestsInSource(group.Tests, runContext, frameworkHandle, group.Source, isDeploymentDone);
}
}
internal virtual UnitTestDiscoverer GetUnitTestDiscoverer()
{
return new UnitTestDiscoverer();
}
internal void SendTestResults(TestCase test, UnitTestResult[] unitTestResults, DateTimeOffset startTime, DateTimeOffset endTime, ITestExecutionRecorder testExecutionRecorder)
{
if (!(unitTestResults?.Length > 0))
{
return;
testResult.DisplayName = string.Format(CultureInfo.CurrentCulture, Resource.DataDrivenResultDisplayName, test.DisplayName, unitTestResult.DatarowIndex);
}
foreach (var unitTestResult in unitTestResults)
testExecutionRecorder.RecordEnd(test, testResult.Outcome);
if (testResult.Outcome == TestOutcome.Failed)
{
if (test == null)
{
continue;
}
var testResult = unitTestResult.ToTestResult(test, startTime, endTime, MSTestSettings.CurrentSettings);
if (unitTestResult.DatarowIndex >= 0)
{
testResult.DisplayName = string.Format(CultureInfo.CurrentCulture, Resource.DataDrivenResultDisplayName, test.DisplayName, unitTestResult.DatarowIndex);
}
testExecutionRecorder.RecordEnd(test, testResult.Outcome);
if (testResult.Outcome == TestOutcome.Failed)
{
PlatformServiceProvider.Instance.AdapterTraceLogger.LogInfo("MSTestExecutor:Test {0} failed. ErrorMessage:{1}, ErrorStackTrace:{2}.", testResult.TestCase.FullyQualifiedName, testResult.ErrorMessage, testResult.ErrorStackTrace);
this.HasAnyTestFailed = true;
}
try
{
testExecutionRecorder.RecordResult(testResult);
}
catch (TestCanceledException)
{
// Ignore this exception
}
PlatformServiceProvider.Instance.AdapterTraceLogger.LogInfo("MSTestExecutor:Test {0} failed. ErrorMessage:{1}, ErrorStackTrace:{2}.", testResult.TestCase.FullyQualifiedName, testResult.ErrorMessage, testResult.ErrorStackTrace);
this.HasAnyTestFailed = true;
}
}
private static bool MatchTestFilter(ITestCaseFilterExpression filterExpression, TestCase test, TestMethodFilter testMethodFilter)
{
if (filterExpression != null && filterExpression.MatchTestCase(test, p => testMethodFilter.PropertyValueProvider(test, p)) == false)
{
// Skip test if not fitting filter criteria.
return false;
}
return true;
}
/// <summary>
/// Execute the parameter tests present in parameter source
/// </summary>
/// <param name="tests">Tests to execute.</param>
/// <param name="runContext">The run context.</param>
/// <param name="frameworkHandle">Handle to record test start/end/results.</param>
/// <param name="source">The test container for the tests.</param>
/// <param name="isDeploymentDone">Indicates if deployment is done.</param>
private void ExecuteTestsInSource(IEnumerable<TestCase> tests, IRunContext runContext, IFrameworkHandle frameworkHandle, string source, bool isDeploymentDone)
{
Debug.Assert(!string.IsNullOrEmpty(source), "Source cannot be empty");
if (isDeploymentDone)
{
source = Path.Combine(PlatformServiceProvider.Instance.TestDeployment.GetDeploymentDirectory(), Path.GetFileName(source));
}
using var isolationHost = PlatformServiceProvider.Instance.CreateTestSourceHost(source, runContext?.RunSettings, frameworkHandle);
// Create an instance of a type defined in adapter so that adapter gets loaded in the child app domain
var testRunner = isolationHost.CreateInstanceForType(
typeof(UnitTestRunner),
new object[] { MSTestSettings.CurrentSettings }) as UnitTestRunner;
PlatformServiceProvider.Instance.AdapterTraceLogger.LogInfo("Created unit-test runner {0}", source);
// Default test set is filtered tests based on user provided filter criteria
ICollection<TestCase> testsToRun = new TestCase[0];
var filterExpression = this.TestMethodFilter.GetFilterExpression(runContext, frameworkHandle, out var filterHasError);
if (filterHasError)
{
// Bail out without processing everything else below.
return;
}
testsToRun = tests.Where(t => MatchTestFilter(filterExpression, t, this.TestMethodFilter)).ToArray();
// this is done so that appropriate values of test context properties are set at source level
// and are merged with session level parameters
var sourceLevelParameters = PlatformServiceProvider.Instance.SettingsProvider.GetProperties(source);
if (this.sessionParameters != null && this.sessionParameters.Count > 0)
{
sourceLevelParameters = this.sessionParameters.ConcatWithOverwrites(sourceLevelParameters);
}
TestAssemblySettingsProvider sourceSettingsProvider = null;
try
{
sourceSettingsProvider = isolationHost.CreateInstanceForType(
typeof(TestAssemblySettingsProvider),
null) as TestAssemblySettingsProvider;
testExecutionRecorder.RecordResult(testResult);
}
catch (Exception ex)
catch (TestCanceledException)
{
PlatformServiceProvider.Instance.AdapterTraceLogger.LogInfo("Could not create TestAssemblySettingsProvider instance in child app-domain", ex);
// Ignore this exception
}
}
}
var sourceSettings = (sourceSettingsProvider != null) ? sourceSettingsProvider.GetSettings(source) : new TestAssemblySettings();
var parallelWorkers = sourceSettings.Workers;
var parallelScope = sourceSettings.Scope;
this.InitializeClassCleanupManager(source, testRunner, testsToRun, sourceSettings);
private static bool MatchTestFilter(ITestCaseFilterExpression filterExpression, TestCase test, TestMethodFilter testMethodFilter)
{
if (filterExpression != null && filterExpression.MatchTestCase(test, p => testMethodFilter.PropertyValueProvider(test, p)) == false)
{
// Skip test if not fitting filter criteria.
return false;
}
if (MSTestSettings.CurrentSettings.ParallelizationWorkers.HasValue)
return true;
}
/// <summary>
/// Execute the parameter tests present in parameter source
/// </summary>
/// <param name="tests">Tests to execute.</param>
/// <param name="runContext">The run context.</param>
/// <param name="frameworkHandle">Handle to record test start/end/results.</param>
/// <param name="source">The test container for the tests.</param>
/// <param name="isDeploymentDone">Indicates if deployment is done.</param>
private void ExecuteTestsInSource(IEnumerable<TestCase> tests, IRunContext runContext, IFrameworkHandle frameworkHandle, string source, bool isDeploymentDone)
{
Debug.Assert(!string.IsNullOrEmpty(source), "Source cannot be empty");
if (isDeploymentDone)
{
source = Path.Combine(PlatformServiceProvider.Instance.TestDeployment.GetDeploymentDirectory(), Path.GetFileName(source));
}
using var isolationHost = PlatformServiceProvider.Instance.CreateTestSourceHost(source, runContext?.RunSettings, frameworkHandle);
// Create an instance of a type defined in adapter so that adapter gets loaded in the child app domain
var testRunner = isolationHost.CreateInstanceForType(
typeof(UnitTestRunner),
new object[] { MSTestSettings.CurrentSettings }) as UnitTestRunner;
PlatformServiceProvider.Instance.AdapterTraceLogger.LogInfo("Created unit-test runner {0}", source);
// Default test set is filtered tests based on user provided filter criteria
ICollection<TestCase> testsToRun = new TestCase[0];
var filterExpression = this.TestMethodFilter.GetFilterExpression(runContext, frameworkHandle, out var filterHasError);
if (filterHasError)
{
// Bail out without processing everything else below.
return;
}
testsToRun = tests.Where(t => MatchTestFilter(filterExpression, t, this.TestMethodFilter)).ToArray();
// this is done so that appropriate values of test context properties are set at source level
// and are merged with session level parameters
var sourceLevelParameters = PlatformServiceProvider.Instance.SettingsProvider.GetProperties(source);
if (this.sessionParameters != null && this.sessionParameters.Count > 0)
{
sourceLevelParameters = this.sessionParameters.ConcatWithOverwrites(sourceLevelParameters);
}
TestAssemblySettingsProvider sourceSettingsProvider = null;
try
{
sourceSettingsProvider = isolationHost.CreateInstanceForType(
typeof(TestAssemblySettingsProvider),
null) as TestAssemblySettingsProvider;
}
catch (Exception ex)
{
PlatformServiceProvider.Instance.AdapterTraceLogger.LogInfo("Could not create TestAssemblySettingsProvider instance in child app-domain", ex);
}
var sourceSettings = (sourceSettingsProvider != null) ? sourceSettingsProvider.GetSettings(source) : new TestAssemblySettings();
var parallelWorkers = sourceSettings.Workers;
var parallelScope = sourceSettings.Scope;
this.InitializeClassCleanupManager(source, testRunner, testsToRun, sourceSettings);
if (MSTestSettings.CurrentSettings.ParallelizationWorkers.HasValue)
{
// The runsettings value takes precedence over an assembly level setting. Reset the level.
parallelWorkers = MSTestSettings.CurrentSettings.ParallelizationWorkers.Value;
}
if (MSTestSettings.CurrentSettings.ParallelizationScope.HasValue)
{
// The runsettings value takes precedence over an assembly level setting. Reset the level.
parallelScope = MSTestSettings.CurrentSettings.ParallelizationScope.Value;
}
if (!MSTestSettings.CurrentSettings.DisableParallelization && sourceSettings.CanParallelizeAssembly && parallelWorkers > 0)
{
// Parallelization is enabled. Let's do further classification for sets.
var logger = (IMessageLogger)frameworkHandle;
logger.SendMessage(
TestMessageLevel.Informational,
string.Format(CultureInfo.CurrentCulture, Resource.TestParallelizationBanner, source, parallelWorkers, parallelScope));
// Create test sets for execution, we can execute them in parallel based on parallel settings
IEnumerable<IGrouping<bool, TestCase>> testsets = Enumerable.Empty<IGrouping<bool, TestCase>>();
// Parallel and not parallel sets.
testsets = testsToRun.GroupBy(t => t.GetPropertyValue<bool>(TestAdapter.Constants.DoNotParallelizeProperty, false));
var parallelizableTestSet = testsets.FirstOrDefault(g => g.Key == false);
var nonparallelizableTestSet = testsets.FirstOrDefault(g => g.Key == true);
if (parallelizableTestSet != null)
{
// The runsettings value takes precedence over an assembly level setting. Reset the level.
parallelWorkers = MSTestSettings.CurrentSettings.ParallelizationWorkers.Value;
}
ConcurrentQueue<IEnumerable<TestCase>> queue = null;
if (MSTestSettings.CurrentSettings.ParallelizationScope.HasValue)
{
// The runsettings value takes precedence over an assembly level setting. Reset the level.
parallelScope = MSTestSettings.CurrentSettings.ParallelizationScope.Value;
}
if (!MSTestSettings.CurrentSettings.DisableParallelization && sourceSettings.CanParallelizeAssembly && parallelWorkers > 0)
{
// Parallelization is enabled. Let's do further classification for sets.
var logger = (IMessageLogger)frameworkHandle;
logger.SendMessage(
TestMessageLevel.Informational,
string.Format(CultureInfo.CurrentCulture, Resource.TestParallelizationBanner, source, parallelWorkers, parallelScope));
// Create test sets for execution, we can execute them in parallel based on parallel settings
IEnumerable<IGrouping<bool, TestCase>> testsets = Enumerable.Empty<IGrouping<bool, TestCase>>();
// Parallel and not parallel sets.
testsets = testsToRun.GroupBy(t => t.GetPropertyValue<bool>(TestAdapter.Constants.DoNotParallelizeProperty, false));
var parallelizableTestSet = testsets.FirstOrDefault(g => g.Key == false);
var nonparallelizableTestSet = testsets.FirstOrDefault(g => g.Key == true);
if (parallelizableTestSet != null)
// Chunk the sets into further groups based on parallel level
switch (parallelScope)
{
ConcurrentQueue<IEnumerable<TestCase>> queue = null;
case ExecutionScope.MethodLevel:
queue = new ConcurrentQueue<IEnumerable<TestCase>>(parallelizableTestSet.Select(t => new[] { t }));
break;
// Chunk the sets into further groups based on parallel level
switch (parallelScope)
{
case ExecutionScope.MethodLevel:
queue = new ConcurrentQueue<IEnumerable<TestCase>>(parallelizableTestSet.Select(t => new[] { t }));
break;
case ExecutionScope.ClassLevel:
queue = new ConcurrentQueue<IEnumerable<TestCase>>(parallelizableTestSet.GroupBy(t => t.GetPropertyValue(TestAdapter.Constants.TestClassNameProperty) as string));
break;
}
var tasks = new List<Task>();
for (int i = 0; i < parallelWorkers; i++)
{
tasks.Add(Task.Factory.StartNew(
() =>
{
while (!queue.IsEmpty)
{
if (this.cancellationToken != null && this.cancellationToken.Canceled)
{
// if a cancellation has been requested, do not queue any more test runs.
break;
}
if (queue.TryDequeue(out IEnumerable<TestCase> testSet))
{
this.ExecuteTestsWithTestRunner(testSet, runContext, frameworkHandle, source, sourceLevelParameters, testRunner);
}
}
},
CancellationToken.None,
TaskCreationOptions.LongRunning,
TaskScheduler.Default));
}
Task.WaitAll(tasks.ToArray());
case ExecutionScope.ClassLevel:
queue = new ConcurrentQueue<IEnumerable<TestCase>>(parallelizableTestSet.GroupBy(t => t.GetPropertyValue(TestAdapter.Constants.TestClassNameProperty) as string));
break;
}
// Queue the non parallel set
if (nonparallelizableTestSet != null)
var tasks = new List<Task>();
for (int i = 0; i < parallelWorkers; i++)
{
this.ExecuteTestsWithTestRunner(nonparallelizableTestSet, runContext, frameworkHandle, source, sourceLevelParameters, testRunner);
}
}
else
{
this.ExecuteTestsWithTestRunner(testsToRun, runContext, frameworkHandle, source, sourceLevelParameters, testRunner);
}
this.RunCleanup(frameworkHandle, testRunner);
PlatformServiceProvider.Instance.AdapterTraceLogger.LogInfo("Executed tests belonging to source {0}", source);
}
private void InitializeClassCleanupManager(string source, UnitTestRunner testRunner, ICollection<TestCase> testsToRun, TestAssemblySettings sourceSettings)
{
try
{
var unitTestElements = testsToRun.Select(e => e.ToUnitTestElement(source)).ToArray();
testRunner.InitializeClassCleanupManager(unitTestElements, (int)sourceSettings.ClassCleanupLifecycle);
}
catch (Exception ex)
{
// source might not support this if it's legacy make sure it's supported by checking for the type
if (ex.GetType().FullName != "System.Runtime.Remoting.RemotingException")
{
throw;
}
}
}
private void ExecuteTestsWithTestRunner(
IEnumerable<TestCase> tests,
IRunContext runContext,
ITestExecutionRecorder testExecutionRecorder,
string source,
IDictionary<string, object> sourceLevelParameters,
UnitTestRunner testRunner)
{
foreach (var currentTest in tests)
{
if (this.cancellationToken != null && this.cancellationToken.Canceled)
{
break;
}
var unitTestElement = currentTest.ToUnitTestElement(source);
testExecutionRecorder.RecordStart(currentTest);
var startTime = DateTimeOffset.Now;
PlatformServiceProvider.Instance.AdapterTraceLogger.LogInfo("Executing test {0}", unitTestElement.TestMethod.Name);
// Run single test passing test context properties to it.
var tcmProperties = TcmTestPropertiesProvider.GetTcmProperties(currentTest);
var testContextProperties = this.GetTestContextProperties(tcmProperties, sourceLevelParameters);
var unitTestResult = testRunner.RunSingleTest(unitTestElement.TestMethod, testContextProperties);
PlatformServiceProvider.Instance.AdapterTraceLogger.LogInfo("Executed test {0}", unitTestElement.TestMethod.Name);
var endTime = DateTimeOffset.Now;
this.SendTestResults(currentTest, unitTestResult, startTime, endTime, testExecutionRecorder);
}
}
/// <summary>
/// Get test context properties.
/// </summary>
/// <param name="tcmProperties">Tcm properties.</param>
/// <param name="sourceLevelParameters">Source level parameters.</param>
/// <returns>Test context properties.</returns>
private IDictionary<string, object> GetTestContextProperties(IDictionary<TestProperty, object> tcmProperties, IDictionary<string, object> sourceLevelParameters)
{
var testContextProperties = new Dictionary<string, object>();
// Add tcm properties.
foreach (var propertyPair in tcmProperties)
{
testContextProperties[propertyPair.Key.Id] = propertyPair.Value;
}
// Add source level parameters.
foreach (var propertyPair in sourceLevelParameters)
{
testContextProperties[propertyPair.Key] = propertyPair.Value;
}
return testContextProperties;
}
private void RunCleanup(
ITestExecutionRecorder testExecutionRecorder,
UnitTestRunner testRunner)
{
// All cleanups (Class and Assembly) run at the end of test execution. Failures in these cleanup
// methods will be reported as Warnings.
PlatformServiceProvider.Instance.AdapterTraceLogger.LogInfo("Executing cleanup methods.");
var cleanupResult = testRunner.RunCleanup();
PlatformServiceProvider.Instance.AdapterTraceLogger.LogInfo("Executed cleanup methods.");
if (cleanupResult != null)
{
// Do not attach the standard output and error messages to any test result. It is not
// guaranteed that a test method of same class would have run last. We will end up
// adding stdout to test method of another class.
this.LogCleanupResult(testExecutionRecorder, cleanupResult);
}
}
[SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Justification = "Requirement is to handle errors in user specified run parameters")]
private void CacheSessionParameters(IRunContext runContext, ITestExecutionRecorder testExecutionRecorder)
{
if (!string.IsNullOrEmpty(runContext?.RunSettings?.SettingsXml))
{
try
{
var testRunParameters = RunSettingsUtilities.GetTestRunParameters(runContext.RunSettings.SettingsXml);
if (testRunParameters != null)
{
// Clear sessionParameters to prevent key collisions of test run parameters in case
// "Keep Test Execution Engine Alive" is selected in VS.
this.sessionParameters.Clear();
foreach (var kvp in testRunParameters)
tasks.Add(Task.Factory.StartNew(
() =>
{
this.sessionParameters.Add(kvp);
}
}
}
catch (Exception ex)
{
testExecutionRecorder.SendMessage(TestMessageLevel.Error, ex.Message);
while (!queue.IsEmpty)
{
if (this.cancellationToken != null && this.cancellationToken.Canceled)
{
// if a cancellation has been requested, do not queue any more test runs.
break;
}
if (queue.TryDequeue(out IEnumerable<TestCase> testSet))
{
this.ExecuteTestsWithTestRunner(testSet, runContext, frameworkHandle, source, sourceLevelParameters, testRunner);
}
}
},
CancellationToken.None,
TaskCreationOptions.LongRunning,
TaskScheduler.Default));
}
Task.WaitAll(tasks.ToArray());
}
// Queue the non parallel set
if (nonparallelizableTestSet != null)
{
this.ExecuteTestsWithTestRunner(nonparallelizableTestSet, runContext, frameworkHandle, source, sourceLevelParameters, testRunner);
}
}
/// <summary>
/// Log the parameter warnings on the parameter logger
/// </summary>
/// <param name="testExecutionRecorder">Handle to record test start/end/results/messages.</param>
/// <param name="result">Result of the run operation.</param>
private void LogCleanupResult(ITestExecutionRecorder testExecutionRecorder, RunCleanupResult result)
else
{
Debug.Assert(testExecutionRecorder != null, "Logger should not be null");
this.ExecuteTestsWithTestRunner(testsToRun, runContext, frameworkHandle, source, sourceLevelParameters, testRunner);
}
if (!string.IsNullOrWhiteSpace(result.StandardOut))
this.RunCleanup(frameworkHandle, testRunner);
PlatformServiceProvider.Instance.AdapterTraceLogger.LogInfo("Executed tests belonging to source {0}", source);
}
private void InitializeClassCleanupManager(string source, UnitTestRunner testRunner, ICollection<TestCase> testsToRun, TestAssemblySettings sourceSettings)
{
try
{
var unitTestElements = testsToRun.Select(e => e.ToUnitTestElement(source)).ToArray();
testRunner.InitializeClassCleanupManager(unitTestElements, (int)sourceSettings.ClassCleanupLifecycle);
}
catch (Exception ex)
{
// source might not support this if it's legacy make sure it's supported by checking for the type
if (ex.GetType().FullName != "System.Runtime.Remoting.RemotingException")
{
testExecutionRecorder.SendMessage(TestMessageLevel.Informational, result.StandardOut);
throw;
}
}
}
private void ExecuteTestsWithTestRunner(
IEnumerable<TestCase> tests,
IRunContext runContext,
ITestExecutionRecorder testExecutionRecorder,
string source,
IDictionary<string, object> sourceLevelParameters,
UnitTestRunner testRunner)
{
foreach (var currentTest in tests)
{
if (this.cancellationToken != null && this.cancellationToken.Canceled)
{
break;
}
if (!string.IsNullOrWhiteSpace(result.DebugTrace))
{
testExecutionRecorder.SendMessage(TestMessageLevel.Informational, result.DebugTrace);
}
var unitTestElement = currentTest.ToUnitTestElement(source);
if (!string.IsNullOrWhiteSpace(result.StandardError))
{
testExecutionRecorder.SendMessage(
MSTestSettings.CurrentSettings.TreatClassAndAssemblyCleanupWarningsAsErrors ? TestMessageLevel.Error : TestMessageLevel.Warning,
result.StandardError);
}
testExecutionRecorder.RecordStart(currentTest);
if (result.Warnings != null)
var startTime = DateTimeOffset.Now;
PlatformServiceProvider.Instance.AdapterTraceLogger.LogInfo("Executing test {0}", unitTestElement.TestMethod.Name);
// Run single test passing test context properties to it.
var tcmProperties = TcmTestPropertiesProvider.GetTcmProperties(currentTest);
var testContextProperties = this.GetTestContextProperties(tcmProperties, sourceLevelParameters);
var unitTestResult = testRunner.RunSingleTest(unitTestElement.TestMethod, testContextProperties);
PlatformServiceProvider.Instance.AdapterTraceLogger.LogInfo("Executed test {0}", unitTestElement.TestMethod.Name);
var endTime = DateTimeOffset.Now;
this.SendTestResults(currentTest, unitTestResult, startTime, endTime, testExecutionRecorder);
}
}
/// <summary>
/// Get test context properties.
/// </summary>
/// <param name="tcmProperties">Tcm properties.</param>
/// <param name="sourceLevelParameters">Source level parameters.</param>
/// <returns>Test context properties.</returns>
private IDictionary<string, object> GetTestContextProperties(IDictionary<TestProperty, object> tcmProperties, IDictionary<string, object> sourceLevelParameters)
{
var testContextProperties = new Dictionary<string, object>();
// Add tcm properties.
foreach (var propertyPair in tcmProperties)
{
testContextProperties[propertyPair.Key.Id] = propertyPair.Value;
}
// Add source level parameters.
foreach (var propertyPair in sourceLevelParameters)
{
testContextProperties[propertyPair.Key] = propertyPair.Value;
}
return testContextProperties;
}
private void RunCleanup(
ITestExecutionRecorder testExecutionRecorder,
UnitTestRunner testRunner)
{
// All cleanups (Class and Assembly) run at the end of test execution. Failures in these cleanup
// methods will be reported as Warnings.
PlatformServiceProvider.Instance.AdapterTraceLogger.LogInfo("Executing cleanup methods.");
var cleanupResult = testRunner.RunCleanup();
PlatformServiceProvider.Instance.AdapterTraceLogger.LogInfo("Executed cleanup methods.");
if (cleanupResult != null)
{
// Do not attach the standard output and error messages to any test result. It is not
// guaranteed that a test method of same class would have run last. We will end up
// adding stdout to test method of another class.
this.LogCleanupResult(testExecutionRecorder, cleanupResult);
}
}
[SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Justification = "Requirement is to handle errors in user specified run parameters")]
private void CacheSessionParameters(IRunContext runContext, ITestExecutionRecorder testExecutionRecorder)
{
if (!string.IsNullOrEmpty(runContext?.RunSettings?.SettingsXml))
{
try
{
foreach (string warning in result.Warnings)
var testRunParameters = RunSettingsUtilities.GetTestRunParameters(runContext.RunSettings.SettingsXml);
if (testRunParameters != null)
{
if (!string.IsNullOrWhiteSpace(warning))
// Clear sessionParameters to prevent key collisions of test run parameters in case
// "Keep Test Execution Engine Alive" is selected in VS.
this.sessionParameters.Clear();
foreach (var kvp in testRunParameters)
{
testExecutionRecorder.SendMessage(
MSTestSettings.CurrentSettings.TreatClassAndAssemblyCleanupWarningsAsErrors ? TestMessageLevel.Error : TestMessageLevel.Warning,
warning);
this.sessionParameters.Add(kvp);
}
}
}
catch (Exception ex)
{
testExecutionRecorder.SendMessage(TestMessageLevel.Error, ex.Message);
}
}
}
/// <summary>
/// Log the parameter warnings on the parameter logger
/// </summary>
/// <param name="testExecutionRecorder">Handle to record test start/end/results/messages.</param>
/// <param name="result">Result of the run operation.</param>
private void LogCleanupResult(ITestExecutionRecorder testExecutionRecorder, RunCleanupResult result)
{
Debug.Assert(testExecutionRecorder != null, "Logger should not be null");
if (!string.IsNullOrWhiteSpace(result.StandardOut))
{
testExecutionRecorder.SendMessage(TestMessageLevel.Informational, result.StandardOut);
}
if (!string.IsNullOrWhiteSpace(result.DebugTrace))
{
testExecutionRecorder.SendMessage(TestMessageLevel.Informational, result.DebugTrace);
}
if (!string.IsNullOrWhiteSpace(result.StandardError))
{
testExecutionRecorder.SendMessage(
MSTestSettings.CurrentSettings.TreatClassAndAssemblyCleanupWarningsAsErrors ? TestMessageLevel.Error : TestMessageLevel.Warning,
result.StandardError);
}
if (result.Warnings != null)
{
foreach (string warning in result.Warnings)
{
if (!string.IsNullOrWhiteSpace(warning))
{
testExecutionRecorder.SendMessage(
MSTestSettings.CurrentSettings.TreatClassAndAssemblyCleanupWarningsAsErrors ? TestMessageLevel.Error : TestMessageLevel.Warning,
warning);
}
}
}
}
}

Разница между файлами не показана из-за своего большого размера Загрузить разницу

Просмотреть файл

@ -1,451 +1,450 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
namespace Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.Execution
namespace Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.Execution;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Globalization;
using System.Linq;
using System.Reflection;
using Extensions;
using Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.Helpers;
using Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.ObjectModel;
using Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.Interface;
using UTF = Microsoft.VisualStudio.TestTools.UnitTesting;
/// <summary>
/// This class is responsible to running tests and converting framework TestResults to adapter TestResults.
/// </summary>
internal class TestMethodRunner
{
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Globalization;
using System.Linq;
using System.Reflection;
using Extensions;
using Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.Helpers;
using Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.ObjectModel;
using Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.Interface;
using UTF = Microsoft.VisualStudio.TestTools.UnitTesting;
/// <summary>
/// Test context which needs to be passed to the various methods of the test
/// </summary>
private readonly ITestContext testContext;
/// <summary>
/// This class is responsible to running tests and converting framework TestResults to adapter TestResults.
/// TestMethod that needs to be executed.
/// </summary>
internal class TestMethodRunner
private readonly TestMethod test;
/// <summary>
/// TestMethod referred by the above test element
/// </summary>
private readonly TestMethodInfo testMethodInfo;
/// <summary>
/// Specifies whether debug traces should be captured or not
/// </summary>
private readonly bool captureDebugTraces;
/// <summary>
/// Helper for reflection API's.
/// </summary>
private readonly ReflectHelper reflectHelper;
/// <summary>
/// Initializes a new instance of the <see cref="TestMethodRunner"/> class.
/// </summary>
/// <param name="testMethodInfo">
/// The test method info.
/// </param>
/// <param name="testMethod">
/// The test method.
/// </param>
/// <param name="testContext">
/// The test context.
/// </param>
/// <param name="captureDebugTraces">
/// The capture debug traces.
/// </param>
public TestMethodRunner(TestMethodInfo testMethodInfo, TestMethod testMethod, ITestContext testContext, bool captureDebugTraces)
: this(testMethodInfo, testMethod, testContext, captureDebugTraces, ReflectHelper.Instance)
{
/// <summary>
/// Test context which needs to be passed to the various methods of the test
/// </summary>
private readonly ITestContext testContext;
}
/// <summary>
/// TestMethod that needs to be executed.
/// </summary>
private readonly TestMethod test;
/// <summary>
/// Initializes a new instance of the <see cref="TestMethodRunner"/> class.
/// </summary>
/// <param name="testMethodInfo">
/// The test method info.
/// </param>
/// <param name="testMethod">
/// The test method.
/// </param>
/// <param name="testContext">
/// The test context.
/// </param>
/// <param name="captureDebugTraces">
/// The capture debug traces.
/// </param>
/// <param name="reflectHelper">
/// The reflect Helper object.
/// </param>
public TestMethodRunner(TestMethodInfo testMethodInfo, TestMethod testMethod, ITestContext testContext, bool captureDebugTraces, ReflectHelper reflectHelper)
{
Debug.Assert(testMethodInfo != null, "testMethodInfo should not be null");
Debug.Assert(testMethod != null, "testMethod should not be null");
Debug.Assert(testContext != null, "testContext should not be null");
/// <summary>
/// TestMethod referred by the above test element
/// </summary>
private readonly TestMethodInfo testMethodInfo;
this.testMethodInfo = testMethodInfo;
this.test = testMethod;
this.testContext = testContext;
this.captureDebugTraces = captureDebugTraces;
this.reflectHelper = reflectHelper;
}
/// <summary>
/// Specifies whether debug traces should be captured or not
/// </summary>
private readonly bool captureDebugTraces;
/// <summary>
/// Executes a test
/// </summary>
/// <returns>The test results.</returns>
internal UnitTestResult[] Execute()
{
string initLogs = string.Empty;
string initTrace = string.Empty;
string initErrorLogs = string.Empty;
string inittestContextMessages = string.Empty;
/// <summary>
/// Helper for reflection API's.
/// </summary>
private readonly ReflectHelper reflectHelper;
UnitTestResult[] result = null;
/// <summary>
/// Initializes a new instance of the <see cref="TestMethodRunner"/> class.
/// </summary>
/// <param name="testMethodInfo">
/// The test method info.
/// </param>
/// <param name="testMethod">
/// The test method.
/// </param>
/// <param name="testContext">
/// The test context.
/// </param>
/// <param name="captureDebugTraces">
/// The capture debug traces.
/// </param>
public TestMethodRunner(TestMethodInfo testMethodInfo, TestMethod testMethod, ITestContext testContext, bool captureDebugTraces)
: this(testMethodInfo, testMethod, testContext, captureDebugTraces, ReflectHelper.Instance)
try
{
using (LogMessageListener logListener = new(this.captureDebugTraces))
{
try
{
// Run the assembly and class Initialize methods if required.
// Assembly or class initialize can throw exceptions in which case we need to ensure that we fail the test.
this.testMethodInfo.Parent.Parent.RunAssemblyInitialize(this.testContext.Context);
this.testMethodInfo.Parent.RunClassInitialize(this.testContext.Context);
}
finally
{
initLogs = logListener.GetAndClearStandardOutput();
initTrace = logListener.GetAndClearDebugTrace();
initErrorLogs = logListener.GetAndClearStandardError();
inittestContextMessages = this.testContext.GetAndClearDiagnosticMessages();
}
}
// Listening to log messages when running the test method with its Test Initialize and cleanup later on in the stack.
// This allows us to differentiate logging when data driven methods are used.
result = this.RunTestMethod();
}
catch (TestFailedException ex)
{
result = new[] { new UnitTestResult(ex) };
}
catch (Exception ex)
{
if (result == null || result.Length == 0)
{
result = new[] { new UnitTestResult() };
}
var newResult = new UnitTestResult(new TestFailedException(UnitTestOutcome.Error, ex.TryGetMessage(), ex.TryGetStackTraceInformation()))
{
StandardOut = result[result.Length - 1].StandardOut,
StandardError = result[result.Length - 1].StandardError,
DebugTrace = result[result.Length - 1].DebugTrace,
TestContextMessages = result[result.Length - 1].TestContextMessages,
Duration = result[result.Length - 1].Duration
};
result[result.Length - 1] = newResult;
}
finally
{
var firstResult = result[0];
firstResult.StandardOut = initLogs + firstResult.StandardOut;
firstResult.StandardError = initErrorLogs + firstResult.StandardError;
firstResult.DebugTrace = initTrace + firstResult.DebugTrace;
firstResult.TestContextMessages = inittestContextMessages + firstResult.TestContextMessages;
}
/// <summary>
/// Initializes a new instance of the <see cref="TestMethodRunner"/> class.
/// </summary>
/// <param name="testMethodInfo">
/// The test method info.
/// </param>
/// <param name="testMethod">
/// The test method.
/// </param>
/// <param name="testContext">
/// The test context.
/// </param>
/// <param name="captureDebugTraces">
/// The capture debug traces.
/// </param>
/// <param name="reflectHelper">
/// The reflect Helper object.
/// </param>
public TestMethodRunner(TestMethodInfo testMethodInfo, TestMethod testMethod, ITestContext testContext, bool captureDebugTraces, ReflectHelper reflectHelper)
{
Debug.Assert(testMethodInfo != null, "testMethodInfo should not be null");
Debug.Assert(testMethod != null, "testMethod should not be null");
Debug.Assert(testContext != null, "testContext should not be null");
return result;
}
this.testMethodInfo = testMethodInfo;
this.test = testMethod;
this.testContext = testContext;
this.captureDebugTraces = captureDebugTraces;
this.reflectHelper = reflectHelper;
/// <summary>
/// Runs the test method
/// </summary>
/// <returns>The test results.</returns>
[SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Justification = "Requirement is to handle all kinds of user exceptions and message appropriately.")]
internal UnitTestResult[] RunTestMethod()
{
Debug.Assert(this.test != null, "Test should not be null.");
Debug.Assert(this.testMethodInfo.TestMethod != null, "Test method should not be null.");
List<UTF.TestResult> results = new();
var isDataDriven = false;
if (this.testMethodInfo.TestMethodOptions.Executor != null)
{
if (this.test.DataType == DynamicDataType.ITestDataSource)
{
var data = DataSerializationHelper.Deserialize(this.test.SerializedData);
var testResults = this.ExecuteTestWithDataSource(null, data);
results.AddRange(testResults);
}
else if (this.ExecuteDataSourceBasedTests(results))
{
isDataDriven = true;
}
else
{
var testResults = this.ExecuteTest(this.testMethodInfo);
foreach (var testResult in testResults)
{
if (string.IsNullOrWhiteSpace(testResult.DisplayName))
{
testResult.DisplayName = this.test.DisplayName;
}
}
results.AddRange(testResults);
}
}
else
{
PlatformServiceProvider.Instance.AdapterTraceLogger.LogError(
"Not able to get executor for method {0}.{1}",
this.testMethodInfo.TestClassName,
this.testMethodInfo.TestMethodName);
}
/// <summary>
/// Executes a test
/// </summary>
/// <returns>The test results.</returns>
internal UnitTestResult[] Execute()
{
string initLogs = string.Empty;
string initTrace = string.Empty;
string initErrorLogs = string.Empty;
string inittestContextMessages = string.Empty;
// Get aggregate outcome.
var aggregateOutcome = this.GetAggregateOutcome(results);
this.testContext.SetOutcome(aggregateOutcome);
UnitTestResult[] result = null;
// Set a result in case no result is present.
if (!results.Any())
{
results.Add(new UTF.TestResult() { Outcome = aggregateOutcome, TestFailureException = new TestFailedException(UnitTestOutcome.Error, Resource.UTA_NoTestResult) });
}
// In case of data driven, set parent info in results.
if (isDataDriven)
{
results = this.UpdateResultsWithParentInfo(results, Guid.NewGuid());
}
return results.ToArray().ToUnitTestResults();
}
private bool ExecuteDataSourceBasedTests(List<UTF.TestResult> results)
{
var isDataDriven = false;
UTF.DataSourceAttribute[] dataSourceAttribute = this.testMethodInfo.GetAttributes<UTF.DataSourceAttribute>(false);
if (dataSourceAttribute != null && dataSourceAttribute.Length == 1)
{
isDataDriven = true;
Stopwatch watch = new();
watch.Start();
try
{
using (LogMessageListener logListener = new(this.captureDebugTraces))
IEnumerable<object> dataRows = PlatformServiceProvider.Instance.TestDataSource.GetData(this.testMethodInfo, this.testContext);
if (dataRows == null)
{
try
{
// Run the assembly and class Initialize methods if required.
// Assembly or class initialize can throw exceptions in which case we need to ensure that we fail the test.
this.testMethodInfo.Parent.Parent.RunAssemblyInitialize(this.testContext.Context);
this.testMethodInfo.Parent.RunClassInitialize(this.testContext.Context);
}
finally
{
initLogs = logListener.GetAndClearStandardOutput();
initTrace = logListener.GetAndClearDebugTrace();
initErrorLogs = logListener.GetAndClearStandardError();
inittestContextMessages = this.testContext.GetAndClearDiagnosticMessages();
}
}
// Listening to log messages when running the test method with its Test Initialize and cleanup later on in the stack.
// This allows us to differentiate logging when data driven methods are used.
result = this.RunTestMethod();
}
catch (TestFailedException ex)
{
result = new[] { new UnitTestResult(ex) };
}
catch (Exception ex)
{
if (result == null || result.Length == 0)
{
result = new[] { new UnitTestResult() };
}
var newResult = new UnitTestResult(new TestFailedException(UnitTestOutcome.Error, ex.TryGetMessage(), ex.TryGetStackTraceInformation()))
{
StandardOut = result[result.Length - 1].StandardOut,
StandardError = result[result.Length - 1].StandardError,
DebugTrace = result[result.Length - 1].DebugTrace,
TestContextMessages = result[result.Length - 1].TestContextMessages,
Duration = result[result.Length - 1].Duration
};
result[result.Length - 1] = newResult;
}
finally
{
var firstResult = result[0];
firstResult.StandardOut = initLogs + firstResult.StandardOut;
firstResult.StandardError = initErrorLogs + firstResult.StandardError;
firstResult.DebugTrace = initTrace + firstResult.DebugTrace;
firstResult.TestContextMessages = inittestContextMessages + firstResult.TestContextMessages;
}
return result;
}
/// <summary>
/// Runs the test method
/// </summary>
/// <returns>The test results.</returns>
[SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Justification = "Requirement is to handle all kinds of user exceptions and message appropriately.")]
internal UnitTestResult[] RunTestMethod()
{
Debug.Assert(this.test != null, "Test should not be null.");
Debug.Assert(this.testMethodInfo.TestMethod != null, "Test method should not be null.");
List<UTF.TestResult> results = new();
var isDataDriven = false;
if (this.testMethodInfo.TestMethodOptions.Executor != null)
{
if (this.test.DataType == DynamicDataType.ITestDataSource)
{
var data = DataSerializationHelper.Deserialize(this.test.SerializedData);
var testResults = this.ExecuteTestWithDataSource(null, data);
results.AddRange(testResults);
}
else if (this.ExecuteDataSourceBasedTests(results))
{
isDataDriven = true;
watch.Stop();
var inconclusiveResult = new UTF.TestResult();
inconclusiveResult.Outcome = UTF.UnitTestOutcome.Inconclusive;
inconclusiveResult.Duration = watch.Elapsed;
results.Add(inconclusiveResult);
}
else
{
var testResults = this.ExecuteTest(this.testMethodInfo);
foreach (var testResult in testResults)
try
{
if (string.IsNullOrWhiteSpace(testResult.DisplayName))
int rowIndex = 0;
foreach (object dataRow in dataRows)
{
testResult.DisplayName = this.test.DisplayName;
UTF.TestResult[] testResults = this.ExecuteTestWithDataRow(dataRow, rowIndex++);
results.AddRange(testResults);
}
}
results.AddRange(testResults);
}
}
else
{
PlatformServiceProvider.Instance.AdapterTraceLogger.LogError(
"Not able to get executor for method {0}.{1}",
this.testMethodInfo.TestClassName,
this.testMethodInfo.TestMethodName);
}
// Get aggregate outcome.
var aggregateOutcome = this.GetAggregateOutcome(results);
this.testContext.SetOutcome(aggregateOutcome);
// Set a result in case no result is present.
if (!results.Any())
{
results.Add(new UTF.TestResult() { Outcome = aggregateOutcome, TestFailureException = new TestFailedException(UnitTestOutcome.Error, Resource.UTA_NoTestResult) });
}
// In case of data driven, set parent info in results.
if (isDataDriven)
{
results = this.UpdateResultsWithParentInfo(results, Guid.NewGuid());
}
return results.ToArray().ToUnitTestResults();
}
private bool ExecuteDataSourceBasedTests(List<UTF.TestResult> results)
{
var isDataDriven = false;
UTF.DataSourceAttribute[] dataSourceAttribute = this.testMethodInfo.GetAttributes<UTF.DataSourceAttribute>(false);
if (dataSourceAttribute != null && dataSourceAttribute.Length == 1)
{
isDataDriven = true;
Stopwatch watch = new();
watch.Start();
try
{
IEnumerable<object> dataRows = PlatformServiceProvider.Instance.TestDataSource.GetData(this.testMethodInfo, this.testContext);
if (dataRows == null)
finally
{
watch.Stop();
var inconclusiveResult = new UTF.TestResult();
inconclusiveResult.Outcome = UTF.UnitTestOutcome.Inconclusive;
inconclusiveResult.Duration = watch.Elapsed;
results.Add(inconclusiveResult);
}
else
{
try
{
int rowIndex = 0;
foreach (object dataRow in dataRows)
{
UTF.TestResult[] testResults = this.ExecuteTestWithDataRow(dataRow, rowIndex++);
results.AddRange(testResults);
}
}
finally
{
this.testContext.SetDataConnection(null);
this.testContext.SetDataRow(null);
}
this.testContext.SetDataConnection(null);
this.testContext.SetDataRow(null);
}
}
catch (Exception ex)
{
watch.Stop();
var failedResult = new UTF.TestResult();
failedResult.Outcome = UTF.UnitTestOutcome.Error;
failedResult.TestFailureException = ex;
failedResult.Duration = watch.Elapsed;
results.Add(failedResult);
}
}
else
{
UTF.ITestDataSource[] testDataSources = this.testMethodInfo.GetAttributes<Attribute>(false)?.Where(a => a is UTF.ITestDataSource).OfType<UTF.ITestDataSource>().ToArray();
if (testDataSources != null && testDataSources.Length > 0)
{
isDataDriven = true;
foreach (var testDataSource in testDataSources)
{
foreach (var data in testDataSource.GetData(this.testMethodInfo.MethodInfo))
{
try
{
var testResults = this.ExecuteTestWithDataSource(testDataSource, data);
results.AddRange(testResults);
}
finally
{
this.testMethodInfo.SetArguments(null);
}
}
}
}
}
return isDataDriven;
}
private UTF.TestResult[] ExecuteTestWithDataSource(UTF.ITestDataSource testDataSource, object[] data)
{
var stopwatch = Stopwatch.StartNew();
this.testMethodInfo.SetArguments(data);
var testResults = this.ExecuteTest(this.testMethodInfo);
stopwatch.Stop();
var hasDisplayName = !string.IsNullOrWhiteSpace(this.test.DisplayName);
foreach (var testResult in testResults)
{
if (testResult.Duration == TimeSpan.Zero)
{
testResult.Duration = stopwatch.Elapsed;
}
var displayName = this.test.Name;
if (testDataSource != null)
{
displayName = testDataSource.GetDisplayName(this.testMethodInfo.MethodInfo, data);
}
else if (hasDisplayName)
{
displayName = this.test.DisplayName;
}
testResult.DisplayName = displayName;
}
return testResults;
}
private UTF.TestResult[] ExecuteTestWithDataRow(object dataRow, int rowIndex)
{
var displayName = string.Format(CultureInfo.CurrentCulture, Resource.DataDrivenResultDisplayName, this.test.DisplayName, rowIndex);
Stopwatch stopwatch = null;
UTF.TestResult[] testResults = null;
try
{
stopwatch = Stopwatch.StartNew();
this.testContext.SetDataRow(dataRow);
testResults = this.ExecuteTest(this.testMethodInfo);
}
finally
{
stopwatch?.Stop();
this.testContext.SetDataRow(null);
}
foreach (var testResult in testResults)
{
testResult.DisplayName = displayName;
testResult.DatarowIndex = rowIndex;
testResult.Duration = stopwatch.Elapsed;
}
return testResults;
}
private UTF.TestResult[] ExecuteTest(TestMethodInfo testMethodInfo)
{
try
{
return this.testMethodInfo.TestMethodOptions.Executor.Execute(testMethodInfo);
}
catch (Exception ex)
{
return new[]
watch.Stop();
var failedResult = new UTF.TestResult();
failedResult.Outcome = UTF.UnitTestOutcome.Error;
failedResult.TestFailureException = ex;
failedResult.Duration = watch.Elapsed;
results.Add(failedResult);
}
}
else
{
UTF.ITestDataSource[] testDataSources = this.testMethodInfo.GetAttributes<Attribute>(false)?.Where(a => a is UTF.ITestDataSource).OfType<UTF.ITestDataSource>().ToArray();
if (testDataSources != null && testDataSources.Length > 0)
{
isDataDriven = true;
foreach (var testDataSource in testDataSources)
{
new UTF.TestResult()
foreach (var data in testDataSource.GetData(this.testMethodInfo.MethodInfo))
{
TestFailureException = new Exception(string.Format(CultureInfo.CurrentCulture, Resource.UTA_ExecuteThrewException, ex?.Message, ex?.StackTrace), ex)
try
{
var testResults = this.ExecuteTestWithDataSource(testDataSource, data);
results.AddRange(testResults);
}
finally
{
this.testMethodInfo.SetArguments(null);
}
}
};
}
}
}
/// <summary>
/// Gets aggregate outcome.
/// </summary>
/// <param name="results">Results.</param>
/// <returns>Aggregate outcome.</returns>
private UTF.UnitTestOutcome GetAggregateOutcome(List<UTF.TestResult> results)
return isDataDriven;
}
private UTF.TestResult[] ExecuteTestWithDataSource(UTF.ITestDataSource testDataSource, object[] data)
{
var stopwatch = Stopwatch.StartNew();
this.testMethodInfo.SetArguments(data);
var testResults = this.ExecuteTest(this.testMethodInfo);
stopwatch.Stop();
var hasDisplayName = !string.IsNullOrWhiteSpace(this.test.DisplayName);
foreach (var testResult in testResults)
{
// In case results are not present, set outcome as unknown.
if (!results.Any())
if (testResult.Duration == TimeSpan.Zero)
{
return UTF.UnitTestOutcome.Unknown;
testResult.Duration = stopwatch.Elapsed;
}
// Get aggregate outcome.
var aggregateOutcome = results[0].Outcome;
foreach (var result in results)
var displayName = this.test.Name;
if (testDataSource != null)
{
aggregateOutcome = UnitTestOutcomeExtensions.GetMoreImportantOutcome(aggregateOutcome, result.Outcome);
displayName = testDataSource.GetDisplayName(this.testMethodInfo.MethodInfo, data);
}
else if (hasDisplayName)
{
displayName = this.test.DisplayName;
}
return aggregateOutcome;
testResult.DisplayName = displayName;
}
/// <summary>
/// Updates given results with parent info if results are greater than 1.
/// Add parent results as first result in updated result.
/// </summary>
/// <param name="results">Results.</param>
/// <param name="executionId">Current execution id.</param>
/// <returns>Updated results which contains parent result as first result. All other results contains parent result info.</returns>
private List<UTF.TestResult> UpdateResultsWithParentInfo(List<UTF.TestResult> results, Guid executionId)
return testResults;
}
private UTF.TestResult[] ExecuteTestWithDataRow(object dataRow, int rowIndex)
{
var displayName = string.Format(CultureInfo.CurrentCulture, Resource.DataDrivenResultDisplayName, this.test.DisplayName, rowIndex);
Stopwatch stopwatch = null;
UTF.TestResult[] testResults = null;
try
{
// Return results in case there are no results.
if (!results.Any())
stopwatch = Stopwatch.StartNew();
this.testContext.SetDataRow(dataRow);
testResults = this.ExecuteTest(this.testMethodInfo);
}
finally
{
stopwatch?.Stop();
this.testContext.SetDataRow(null);
}
foreach (var testResult in testResults)
{
testResult.DisplayName = displayName;
testResult.DatarowIndex = rowIndex;
testResult.Duration = stopwatch.Elapsed;
}
return testResults;
}
private UTF.TestResult[] ExecuteTest(TestMethodInfo testMethodInfo)
{
try
{
return this.testMethodInfo.TestMethodOptions.Executor.Execute(testMethodInfo);
}
catch (Exception ex)
{
return new[]
{
return results;
}
// UpdatedResults contain parent result at first position and remaining results has parent info updated.
var updatedResults = new List<UTF.TestResult>();
foreach (var result in results)
{
result.ExecutionId = Guid.NewGuid();
result.ParentExecId = executionId;
updatedResults.Add(result);
}
return updatedResults;
new UTF.TestResult()
{
TestFailureException = new Exception(string.Format(CultureInfo.CurrentCulture, Resource.UTA_ExecuteThrewException, ex?.Message, ex?.StackTrace), ex)
}
};
}
}
/// <summary>
/// Gets aggregate outcome.
/// </summary>
/// <param name="results">Results.</param>
/// <returns>Aggregate outcome.</returns>
private UTF.UnitTestOutcome GetAggregateOutcome(List<UTF.TestResult> results)
{
// In case results are not present, set outcome as unknown.
if (!results.Any())
{
return UTF.UnitTestOutcome.Unknown;
}
// Get aggregate outcome.
var aggregateOutcome = results[0].Outcome;
foreach (var result in results)
{
aggregateOutcome = UnitTestOutcomeExtensions.GetMoreImportantOutcome(aggregateOutcome, result.Outcome);
}
return aggregateOutcome;
}
/// <summary>
/// Updates given results with parent info if results are greater than 1.
/// Add parent results as first result in updated result.
/// </summary>
/// <param name="results">Results.</param>
/// <param name="executionId">Current execution id.</param>
/// <returns>Updated results which contains parent result as first result. All other results contains parent result info.</returns>
private List<UTF.TestResult> UpdateResultsWithParentInfo(List<UTF.TestResult> results, Guid executionId)
{
// Return results in case there are no results.
if (!results.Any())
{
return results;
}
// UpdatedResults contain parent result at first position and remaining results has parent info updated.
var updatedResults = new List<UTF.TestResult>();
foreach (var result in results)
{
result.ExecutionId = Guid.NewGuid();
result.ParentExecId = executionId;
updatedResults.Add(result);
}
return updatedResults;
}
}

Просмотреть файл

@ -1,74 +1,73 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
namespace Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter
namespace Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter;
using System;
using System.Diagnostics;
using Microsoft.VisualStudio.TestPlatform.ObjectModel;
/// <summary>
/// Cancellation token supporting cancellation of a test run.
/// </summary>
public class TestRunCancellationToken
{
using System;
using System.Diagnostics;
using Microsoft.VisualStudio.TestPlatform.ObjectModel;
/// <summary>
/// Stores whether the test run is canceled or not.
/// </summary>
private bool canceled;
/// <summary>
/// Cancellation token supporting cancellation of a test run.
/// Callback to be invoked when canceled.
/// </summary>
public class TestRunCancellationToken
private Action registeredCallback;
/// <summary>
/// Gets a value indicating whether the test run is canceled.
/// </summary>
public bool Canceled
{
/// <summary>
/// Stores whether the test run is canceled or not.
/// </summary>
private bool canceled;
/// <summary>
/// Callback to be invoked when canceled.
/// </summary>
private Action registeredCallback;
/// <summary>
/// Gets a value indicating whether the test run is canceled.
/// </summary>
public bool Canceled
get
{
get
return this.canceled;
}
private set
{
this.canceled = value;
if (this.canceled)
{
return this.canceled;
this.registeredCallback?.Invoke();
}
private set
{
this.canceled = value;
if (this.canceled)
{
this.registeredCallback?.Invoke();
}
}
}
/// <summary>
/// Cancels the execution of a test run.
/// </summary>
public void Cancel()
{
this.Canceled = true;
}
/// <summary>
/// Registers a callback method to be invoked when canceled.
/// </summary>
/// <param name="callback">Callback delegate for handling cancellation.</param>
public void Register(Action callback)
{
ValidateArg.NotNull(callback, "callback");
Debug.Assert(this.registeredCallback == null, "Callback delegate is already registered, use a new cancellationToken");
this.registeredCallback = callback;
}
/// <summary>
/// Unregister the callback method.
/// </summary>
public void Unregister()
{
this.registeredCallback = null;
}
}
/// <summary>
/// Cancels the execution of a test run.
/// </summary>
public void Cancel()
{
this.Canceled = true;
}
/// <summary>
/// Registers a callback method to be invoked when canceled.
/// </summary>
/// <param name="callback">Callback delegate for handling cancellation.</param>
public void Register(Action callback)
{
ValidateArg.NotNull(callback, "callback");
Debug.Assert(this.registeredCallback == null, "Callback delegate is already registered, use a new cancellationToken");
this.registeredCallback = callback;
}
/// <summary>
/// Unregister the callback method.
/// </summary>
public void Unregister()
{
this.registeredCallback = null;
}
}

Разница между файлами не показана из-за своего большого размера Загрузить разницу

Просмотреть файл

@ -1,375 +1,374 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
namespace Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.Execution
{
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.Linq;
using System.Reflection;
using System.Security;
namespace Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.Execution;
using Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.Extensions;
using Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.Helpers;
using Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.ObjectModel;
using Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using TPOM = Microsoft.VisualStudio.TestPlatform.ObjectModel;
using UTF = Microsoft.VisualStudio.TestTools.UnitTesting;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.Linq;
using System.Reflection;
using System.Security;
using Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.Extensions;
using Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.Helpers;
using Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.ObjectModel;
using Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using TPOM = Microsoft.VisualStudio.TestPlatform.ObjectModel;
using UTF = Microsoft.VisualStudio.TestTools.UnitTesting;
/// <summary>
/// The runner that runs a single unit test. Also manages the assembly and class cleanup methods at the end of the run.
/// </summary>
internal class UnitTestRunner : MarshalByRefObject
{
/// <summary>
/// Type cache
/// </summary>
private readonly TypeCache typeCache;
/// <summary>
/// The runner that runs a single unit test. Also manages the assembly and class cleanup methods at the end of the run.
/// Reflect helper
/// </summary>
internal class UnitTestRunner : MarshalByRefObject
private readonly ReflectHelper reflectHelper;
/// <summary>
/// Class cleanup manager
/// </summary>
private ClassCleanupManager classCleanupManager;
/// <summary>
/// Initializes a new instance of the <see cref="UnitTestRunner"/> class.
/// </summary>
/// <param name="settings"> Specifies adapter settings that need to be instantiated in the domain running these tests. </param>
public UnitTestRunner(MSTestSettings settings)
: this(settings, ReflectHelper.Instance)
{
/// <summary>
/// Type cache
/// </summary>
private readonly TypeCache typeCache;
}
/// <summary>
/// Reflect helper
/// </summary>
private readonly ReflectHelper reflectHelper;
/// <summary>
/// Initializes a new instance of the <see cref="UnitTestRunner"/> class.
/// </summary>
/// <param name="settings"> Specifies adapter settings. </param>
/// <param name="reflectHelper"> The reflect Helper. </param>
internal UnitTestRunner(MSTestSettings settings, ReflectHelper reflectHelper)
{
this.reflectHelper = reflectHelper;
this.typeCache = new TypeCache(reflectHelper);
/// <summary>
/// Class cleanup manager
/// </summary>
private ClassCleanupManager classCleanupManager;
// Populate the settings into the domain(Desktop workflow) performing discovery.
// This would just be resettings the settings to itself in non desktop workflows.
MSTestSettings.PopulateSettings(settings);
}
/// <summary>
/// Initializes a new instance of the <see cref="UnitTestRunner"/> class.
/// </summary>
/// <param name="settings"> Specifies adapter settings that need to be instantiated in the domain running these tests. </param>
public UnitTestRunner(MSTestSettings settings)
: this(settings, ReflectHelper.Instance)
{
}
/// <summary>
/// Initializes a new instance of the <see cref="UnitTestRunner"/> class.
/// </summary>
/// <param name="settings"> Specifies adapter settings. </param>
/// <param name="reflectHelper"> The reflect Helper. </param>
internal UnitTestRunner(MSTestSettings settings, ReflectHelper reflectHelper)
{
this.reflectHelper = reflectHelper;
this.typeCache = new TypeCache(reflectHelper);
// Populate the settings into the domain(Desktop workflow) performing discovery.
// This would just be resettings the settings to itself in non desktop workflows.
MSTestSettings.PopulateSettings(settings);
}
/// <summary>
/// Returns object to be used for controlling lifetime, null means infinite lifetime.
/// </summary>
/// <returns>
/// The <see cref="object"/>.
/// </returns>
[SecurityCritical]
/// <summary>
/// Returns object to be used for controlling lifetime, null means infinite lifetime.
/// </summary>
/// <returns>
/// The <see cref="object"/>.
/// </returns>
[SecurityCritical]
#if NET5_0_OR_GREATER
[Obsolete]
[Obsolete]
#endif
public override object InitializeLifetimeService()
public override object InitializeLifetimeService()
{
return null;
}
/// <summary>
/// Initialized the class cleanup manager for the unit test runner. Note, this can run over process-isolation,
/// and all inputs must be serializable from host process.
/// </summary>
/// <param name="testsToRun">the list of tests that will be run in this execution</param>
/// <param name="classCleanupLifecycle">The assembly level class cleanup lifecycle</param>
internal void InitializeClassCleanupManager(ICollection<UnitTestElement> testsToRun, int? classCleanupLifecycle)
{
// We can't transport the enum across AppDomain boundaries because of backwards and forwards compatibility.
// So we're converting here if we can, or falling back to the default.
var lifecycle = ClassCleanupBehavior.EndOfAssembly;
if (classCleanupLifecycle != null && Enum.IsDefined(typeof(ClassCleanupBehavior), classCleanupLifecycle))
{
lifecycle = (ClassCleanupBehavior)classCleanupLifecycle;
}
this.classCleanupManager = new ClassCleanupManager(
testsToRun,
MSTestSettings.CurrentSettings.ClassCleanupLifecycle,
lifecycle,
this.reflectHelper);
}
/// <summary>
/// Runs a single test.
/// </summary>
/// <param name="testMethod"> The test Method. </param>
/// <param name="testContextProperties"> The test context properties. </param>
/// <returns> The <see cref="UnitTestResult"/>. </returns>
internal UnitTestResult[] RunSingleTest(TestMethod testMethod, IDictionary<string, object> testContextProperties)
{
if (testMethod == null)
{
throw new ArgumentNullException(nameof(testMethod));
}
try
{
using var writer = new ThreadSafeStringWriter(CultureInfo.InvariantCulture, "context");
var properties = new Dictionary<string, object>(testContextProperties);
var testContext = PlatformServiceProvider.Instance.GetTestContext(testMethod, writer, properties);
testContext.SetOutcome(TestTools.UnitTesting.UnitTestOutcome.InProgress);
// Get the testMethod
var testMethodInfo = this.typeCache.GetTestMethodInfo(
testMethod,
testContext,
MSTestSettings.CurrentSettings.CaptureDebugTraces);
if (this.classCleanupManager == null && testMethodInfo != null && testMethodInfo.Parent.HasExecutableCleanupMethod)
{
PlatformServiceProvider.Instance.AdapterTraceLogger.LogWarning(Resource.OlderTFMVersionFoundClassCleanup);
}
if (!this.IsTestMethodRunnable(testMethod, testMethodInfo, out var notRunnableResult))
{
bool shouldRunClassCleanup = false;
this.classCleanupManager?.MarkTestComplete(testMethodInfo, testMethod, out shouldRunClassCleanup);
if (shouldRunClassCleanup)
{
testMethodInfo.Parent.RunClassCleanup(ClassCleanupBehavior.EndOfClass);
}
return notRunnableResult;
}
var result = new TestMethodRunner(testMethodInfo, testMethod, testContext, MSTestSettings.CurrentSettings.CaptureDebugTraces, this.reflectHelper).Execute();
this.RunClassCleanupIfEndOfClass(testMethodInfo, testMethod, result);
return result;
}
catch (TypeInspectionException ex)
{
// Catch any exception thrown while inspecting the test method and return failure.
return new UnitTestResult[] { new UnitTestResult(ObjectModel.UnitTestOutcome.Failed, ex.Message) };
}
}
/// <summary>
/// Runs the class cleanup method.
/// It returns any error information during the execution of the cleanup method
/// </summary>
/// <returns> The <see cref="RunCleanupResult"/>. </returns>
internal RunCleanupResult RunCleanup()
{
// No cleanup methods to execute, then return.
var assemblyInfoCache = this.typeCache.AssemblyInfoListWithExecutableCleanupMethods;
var classInfoCache = this.typeCache.ClassInfoListWithExecutableCleanupMethods;
if (!assemblyInfoCache.Any() && !classInfoCache.Any())
{
return null;
}
/// <summary>
/// Initialized the class cleanup manager for the unit test runner. Note, this can run over process-isolation,
/// and all inputs must be serializable from host process.
/// </summary>
/// <param name="testsToRun">the list of tests that will be run in this execution</param>
/// <param name="classCleanupLifecycle">The assembly level class cleanup lifecycle</param>
internal void InitializeClassCleanupManager(ICollection<UnitTestElement> testsToRun, int? classCleanupLifecycle)
{
// We can't transport the enum across AppDomain boundaries because of backwards and forwards compatibility.
// So we're converting here if we can, or falling back to the default.
var lifecycle = ClassCleanupBehavior.EndOfAssembly;
if (classCleanupLifecycle != null && Enum.IsDefined(typeof(ClassCleanupBehavior), classCleanupLifecycle))
{
lifecycle = (ClassCleanupBehavior)classCleanupLifecycle;
}
var result = new RunCleanupResult { Warnings = new List<string>() };
this.classCleanupManager = new ClassCleanupManager(
testsToRun,
MSTestSettings.CurrentSettings.ClassCleanupLifecycle,
lifecycle,
this.reflectHelper);
using (var redirector = new LogMessageListener(MSTestSettings.CurrentSettings.CaptureDebugTraces))
{
try
{
this.RunClassCleanupMethods(classInfoCache, result.Warnings);
this.RunAssemblyCleanup(assemblyInfoCache, result.Warnings);
}
finally
{
// Replacing the null character with a string.replace should work.
// If this does not work for a specific dotnet version a custom function doing the same needs to be put in place.
result.StandardOut = redirector.GetAndClearStandardOutput()?.Replace("\0", "\\0");
result.StandardError = redirector.GetAndClearStandardError()?.Replace("\0", "\\0");
result.DebugTrace = redirector.GetAndClearDebugTrace()?.Replace("\0", "\\0");
}
}
/// <summary>
/// Runs a single test.
/// </summary>
/// <param name="testMethod"> The test Method. </param>
/// <param name="testContextProperties"> The test context properties. </param>
/// <returns> The <see cref="UnitTestResult"/>. </returns>
internal UnitTestResult[] RunSingleTest(TestMethod testMethod, IDictionary<string, object> testContextProperties)
return result;
}
private void RunClassCleanupIfEndOfClass(TestMethodInfo testMethodInfo, TestMethod testMethod, UnitTestResult[] results)
{
bool shouldRunClassCleanup = false;
this.classCleanupManager?.MarkTestComplete(testMethodInfo, testMethod, out shouldRunClassCleanup);
if (shouldRunClassCleanup)
{
if (testMethod == null)
{
throw new ArgumentNullException(nameof(testMethod));
}
string cleanupLogs = string.Empty;
string cleanupTrace = string.Empty;
string cleanupErrorLogs = string.Empty;
try
{
using var writer = new ThreadSafeStringWriter(CultureInfo.InvariantCulture, "context");
var properties = new Dictionary<string, object>(testContextProperties);
var testContext = PlatformServiceProvider.Instance.GetTestContext(testMethod, writer, properties);
testContext.SetOutcome(TestTools.UnitTesting.UnitTestOutcome.InProgress);
// Get the testMethod
var testMethodInfo = this.typeCache.GetTestMethodInfo(
testMethod,
testContext,
MSTestSettings.CurrentSettings.CaptureDebugTraces);
if (this.classCleanupManager == null && testMethodInfo != null && testMethodInfo.Parent.HasExecutableCleanupMethod)
{
PlatformServiceProvider.Instance.AdapterTraceLogger.LogWarning(Resource.OlderTFMVersionFoundClassCleanup);
}
if (!this.IsTestMethodRunnable(testMethod, testMethodInfo, out var notRunnableResult))
{
bool shouldRunClassCleanup = false;
this.classCleanupManager?.MarkTestComplete(testMethodInfo, testMethod, out shouldRunClassCleanup);
if (shouldRunClassCleanup)
{
testMethodInfo.Parent.RunClassCleanup(ClassCleanupBehavior.EndOfClass);
}
return notRunnableResult;
}
var result = new TestMethodRunner(testMethodInfo, testMethod, testContext, MSTestSettings.CurrentSettings.CaptureDebugTraces, this.reflectHelper).Execute();
this.RunClassCleanupIfEndOfClass(testMethodInfo, testMethod, result);
return result;
}
catch (TypeInspectionException ex)
{
// Catch any exception thrown while inspecting the test method and return failure.
return new UnitTestResult[] { new UnitTestResult(ObjectModel.UnitTestOutcome.Failed, ex.Message) };
}
}
/// <summary>
/// Runs the class cleanup method.
/// It returns any error information during the execution of the cleanup method
/// </summary>
/// <returns> The <see cref="RunCleanupResult"/>. </returns>
internal RunCleanupResult RunCleanup()
{
// No cleanup methods to execute, then return.
var assemblyInfoCache = this.typeCache.AssemblyInfoListWithExecutableCleanupMethods;
var classInfoCache = this.typeCache.ClassInfoListWithExecutableCleanupMethods;
if (!assemblyInfoCache.Any() && !classInfoCache.Any())
{
return null;
}
var result = new RunCleanupResult { Warnings = new List<string>() };
using (var redirector = new LogMessageListener(MSTestSettings.CurrentSettings.CaptureDebugTraces))
{
using LogMessageListener logListener =
new(MSTestSettings.CurrentSettings.CaptureDebugTraces);
try
{
this.RunClassCleanupMethods(classInfoCache, result.Warnings);
this.RunAssemblyCleanup(assemblyInfoCache, result.Warnings);
// Class cleanup can throw exceptions in which case we need to ensure that we fail the test.
testMethodInfo.Parent.RunClassCleanup(ClassCleanupBehavior.EndOfClass);
}
finally
{
// Replacing the null character with a string.replace should work.
// If this does not work for a specific dotnet version a custom function doing the same needs to be put in place.
result.StandardOut = redirector.GetAndClearStandardOutput()?.Replace("\0", "\\0");
result.StandardError = redirector.GetAndClearStandardError()?.Replace("\0", "\\0");
result.DebugTrace = redirector.GetAndClearDebugTrace()?.Replace("\0", "\\0");
cleanupLogs = logListener.StandardOutput;
cleanupTrace = logListener.DebugTrace;
cleanupErrorLogs = logListener.StandardError;
var lastResult = results[results.Length - 1];
lastResult.StandardOut += cleanupLogs;
lastResult.StandardError += cleanupErrorLogs;
lastResult.DebugTrace += cleanupTrace;
}
}
return result;
}
private void RunClassCleanupIfEndOfClass(TestMethodInfo testMethodInfo, TestMethod testMethod, UnitTestResult[] results)
{
bool shouldRunClassCleanup = false;
this.classCleanupManager?.MarkTestComplete(testMethodInfo, testMethod, out shouldRunClassCleanup);
if (shouldRunClassCleanup)
catch (Exception e)
{
string cleanupLogs = string.Empty;
string cleanupTrace = string.Empty;
string cleanupErrorLogs = string.Empty;
results[results.Length - 1].Outcome = ObjectModel.UnitTestOutcome.Failed;
results[results.Length - 1].ErrorMessage = e.Message;
results[results.Length - 1].ErrorStackTrace = e.StackTrace;
}
}
}
try
/// <summary>
/// Whether the given testMethod is runnable
/// </summary>
/// <param name="testMethod">The testMethod</param>
/// <param name="testMethodInfo">The testMethodInfo</param>
/// <param name="notRunnableResult">The results to return if the test method is not runnable</param>
/// <returns>whether the given testMethod is runnable</returns>
private bool IsTestMethodRunnable(
TestMethod testMethod,
TestMethodInfo testMethodInfo,
out UnitTestResult[] notRunnableResult)
{
// If the specified TestMethod could not be found, return a NotFound result.
if (testMethodInfo == null)
{
{
notRunnableResult = new UnitTestResult[]
{
using LogMessageListener logListener =
new(MSTestSettings.CurrentSettings.CaptureDebugTraces);
try
{
// Class cleanup can throw exceptions in which case we need to ensure that we fail the test.
testMethodInfo.Parent.RunClassCleanup(ClassCleanupBehavior.EndOfClass);
}
finally
{
cleanupLogs = logListener.StandardOutput;
cleanupTrace = logListener.DebugTrace;
cleanupErrorLogs = logListener.StandardError;
var lastResult = results[results.Length - 1];
lastResult.StandardOut += cleanupLogs;
lastResult.StandardError += cleanupErrorLogs;
lastResult.DebugTrace += cleanupTrace;
}
}
catch (Exception e)
{
results[results.Length - 1].Outcome = ObjectModel.UnitTestOutcome.Failed;
results[results.Length - 1].ErrorMessage = e.Message;
results[results.Length - 1].ErrorStackTrace = e.StackTrace;
}
new UnitTestResult(
ObjectModel.UnitTestOutcome.NotFound,
string.Format(CultureInfo.CurrentCulture, Resource.TestNotFound, testMethod.Name))
};
return false;
}
}
/// <summary>
/// Whether the given testMethod is runnable
/// </summary>
/// <param name="testMethod">The testMethod</param>
/// <param name="testMethodInfo">The testMethodInfo</param>
/// <param name="notRunnableResult">The results to return if the test method is not runnable</param>
/// <returns>whether the given testMethod is runnable</returns>
private bool IsTestMethodRunnable(
TestMethod testMethod,
TestMethodInfo testMethodInfo,
out UnitTestResult[] notRunnableResult)
// If test cannot be executed, then bail out.
if (!testMethodInfo.IsRunnable)
{
// If the specified TestMethod could not be found, return a NotFound result.
if (testMethodInfo == null)
{
{
notRunnableResult = new UnitTestResult[]
{
new UnitTestResult(
ObjectModel.UnitTestOutcome.NotFound,
string.Format(CultureInfo.CurrentCulture, Resource.TestNotFound, testMethod.Name))
};
return false;
}
}
// If test cannot be executed, then bail out.
if (!testMethodInfo.IsRunnable)
{
{
notRunnableResult = new UnitTestResult[]
{ new UnitTestResult(ObjectModel.UnitTestOutcome.NotRunnable, testMethodInfo.NotRunnableReason) };
return false;
}
}
string ignoreMessage = null;
var isIgnoreAttributeOnClass =
this.reflectHelper.IsAttributeDefined(testMethodInfo.Parent.ClassType, typeof(UTF.IgnoreAttribute), false);
var isIgnoreAttributeOnMethod =
this.reflectHelper.IsAttributeDefined(testMethodInfo.TestMethod, typeof(UTF.IgnoreAttribute), false);
if (isIgnoreAttributeOnClass)
{
ignoreMessage = this.reflectHelper.GetIgnoreMessage(testMethodInfo.Parent.ClassType.GetTypeInfo());
}
if (string.IsNullOrEmpty(ignoreMessage) && isIgnoreAttributeOnMethod)
{
ignoreMessage = this.reflectHelper.GetIgnoreMessage(testMethodInfo.TestMethod);
}
if (isIgnoreAttributeOnClass || isIgnoreAttributeOnMethod)
{
{
notRunnableResult = new[] { new UnitTestResult(ObjectModel.UnitTestOutcome.Ignored, ignoreMessage) };
return false;
}
}
notRunnableResult = null;
return true;
}
/// <summary>
/// Run assembly cleanup methods
/// </summary>
/// <param name="assemblyInfoCache"> The assembly Info Cache. </param>
/// <param name="warnings"> The warnings. </param>
private void RunAssemblyCleanup(IEnumerable<TestAssemblyInfo> assemblyInfoCache, IList<string> warnings)
{
foreach (var assemblyInfo in assemblyInfoCache)
{
Debug.Assert(assemblyInfo.HasExecutableCleanupMethod, "HasExecutableCleanupMethod should be true.");
var warning = assemblyInfo.RunAssemblyCleanup();
if (warning != null)
{
warnings.Add(warning);
}
notRunnableResult = new UnitTestResult[]
{ new UnitTestResult(ObjectModel.UnitTestOutcome.NotRunnable, testMethodInfo.NotRunnableReason) };
return false;
}
}
/// <summary>
/// Run class cleanup methods
/// </summary>
/// <param name="classInfoCache"> The class Info Cache. </param>
/// <param name="warnings"> The warnings. </param>
private void RunClassCleanupMethods(IEnumerable<TestClassInfo> classInfoCache, IList<string> warnings)
{
foreach (var classInfo in classInfoCache)
{
Debug.Assert(classInfo.HasExecutableCleanupMethod, "HasExecutableCleanupMethod should be true.");
string ignoreMessage = null;
var isIgnoreAttributeOnClass =
this.reflectHelper.IsAttributeDefined(testMethodInfo.Parent.ClassType, typeof(UTF.IgnoreAttribute), false);
var isIgnoreAttributeOnMethod =
this.reflectHelper.IsAttributeDefined(testMethodInfo.TestMethod, typeof(UTF.IgnoreAttribute), false);
var warning = classInfo.RunClassCleanup();
if (warning != null)
{
warnings.Add(warning);
}
if (isIgnoreAttributeOnClass)
{
ignoreMessage = this.reflectHelper.GetIgnoreMessage(testMethodInfo.Parent.ClassType.GetTypeInfo());
}
if (string.IsNullOrEmpty(ignoreMessage) && isIgnoreAttributeOnMethod)
{
ignoreMessage = this.reflectHelper.GetIgnoreMessage(testMethodInfo.TestMethod);
}
if (isIgnoreAttributeOnClass || isIgnoreAttributeOnMethod)
{
{
notRunnableResult = new[] { new UnitTestResult(ObjectModel.UnitTestOutcome.Ignored, ignoreMessage) };
return false;
}
}
private class ClassCleanupManager
notRunnableResult = null;
return true;
}
/// <summary>
/// Run assembly cleanup methods
/// </summary>
/// <param name="assemblyInfoCache"> The assembly Info Cache. </param>
/// <param name="warnings"> The warnings. </param>
private void RunAssemblyCleanup(IEnumerable<TestAssemblyInfo> assemblyInfoCache, IList<string> warnings)
{
foreach (var assemblyInfo in assemblyInfoCache)
{
private readonly ClassCleanupBehavior? lifecycleFromMsTest;
private readonly ClassCleanupBehavior lifecycleFromAssembly;
private readonly ReflectHelper reflectHelper;
private readonly Dictionary<string, HashSet<string>> remainingTestsByClass;
Debug.Assert(assemblyInfo.HasExecutableCleanupMethod, "HasExecutableCleanupMethod should be true.");
public ClassCleanupManager(
IEnumerable<UnitTestElement> testsToRun,
ClassCleanupBehavior? lifecycleFromMsTest,
ClassCleanupBehavior lifecycleFromAssembly,
ReflectHelper reflectHelper = null)
var warning = assemblyInfo.RunAssemblyCleanup();
if (warning != null)
{
this.remainingTestsByClass = testsToRun.GroupBy(t => t.TestMethod.FullClassName)
.ToDictionary(
g => g.Key,
g => new HashSet<string>(g.Select(t => t.DisplayName)));
this.lifecycleFromMsTest = lifecycleFromMsTest;
this.lifecycleFromAssembly = lifecycleFromAssembly;
this.reflectHelper = reflectHelper ?? new ReflectHelper();
warnings.Add(warning);
}
}
}
public void MarkTestComplete(TestMethodInfo testMethodInfo, TestMethod testMethod, out bool shouldCleanup)
/// <summary>
/// Run class cleanup methods
/// </summary>
/// <param name="classInfoCache"> The class Info Cache. </param>
/// <param name="warnings"> The warnings. </param>
private void RunClassCleanupMethods(IEnumerable<TestClassInfo> classInfoCache, IList<string> warnings)
{
foreach (var classInfo in classInfoCache)
{
Debug.Assert(classInfo.HasExecutableCleanupMethod, "HasExecutableCleanupMethod should be true.");
var warning = classInfo.RunClassCleanup();
if (warning != null)
{
shouldCleanup = false;
var testsByClass = this.remainingTestsByClass[testMethodInfo.TestClassName];
lock (testsByClass)
{
testsByClass.Remove(testMethod.DisplayName);
if (testsByClass.Count == 0 && testMethodInfo.Parent.HasExecutableCleanupMethod)
{
var cleanupLifecycle = this.reflectHelper.GetClassCleanupBehavior(testMethodInfo.Parent)
?? this.lifecycleFromMsTest
?? this.lifecycleFromAssembly;
warnings.Add(warning);
}
}
}
shouldCleanup = cleanupLifecycle == ClassCleanupBehavior.EndOfClass;
}
private class ClassCleanupManager
{
private readonly ClassCleanupBehavior? lifecycleFromMsTest;
private readonly ClassCleanupBehavior lifecycleFromAssembly;
private readonly ReflectHelper reflectHelper;
private readonly Dictionary<string, HashSet<string>> remainingTestsByClass;
public ClassCleanupManager(
IEnumerable<UnitTestElement> testsToRun,
ClassCleanupBehavior? lifecycleFromMsTest,
ClassCleanupBehavior lifecycleFromAssembly,
ReflectHelper reflectHelper = null)
{
this.remainingTestsByClass = testsToRun.GroupBy(t => t.TestMethod.FullClassName)
.ToDictionary(
g => g.Key,
g => new HashSet<string>(g.Select(t => t.DisplayName)));
this.lifecycleFromMsTest = lifecycleFromMsTest;
this.lifecycleFromAssembly = lifecycleFromAssembly;
this.reflectHelper = reflectHelper ?? new ReflectHelper();
}
public void MarkTestComplete(TestMethodInfo testMethodInfo, TestMethod testMethod, out bool shouldCleanup)
{
shouldCleanup = false;
var testsByClass = this.remainingTestsByClass[testMethodInfo.TestClassName];
lock (testsByClass)
{
testsByClass.Remove(testMethod.DisplayName);
if (testsByClass.Count == 0 && testMethodInfo.Parent.HasExecutableCleanupMethod)
{
var cleanupLifecycle = this.reflectHelper.GetClassCleanupBehavior(testMethodInfo.Parent)
?? this.lifecycleFromMsTest
?? this.lifecycleFromAssembly;
shouldCleanup = cleanupLifecycle == ClassCleanupBehavior.EndOfClass;
}
}
}

Просмотреть файл

@ -1,89 +1,88 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
namespace Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.Extensions
namespace Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.Extensions;
using System;
using System.Globalization;
using System.Text;
using Execution;
using Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.ObjectModel;
using UTF = Microsoft.VisualStudio.TestTools.UnitTesting;
/// <summary>
/// Extensions to <see cref="Exception"/> type.
/// </summary>
internal static class ExceptionExtensions
{
using System;
using System.Globalization;
using System.Text;
using Execution;
using Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.ObjectModel;
using UTF = Microsoft.VisualStudio.TestTools.UnitTesting;
/// <summary>
/// Get the InnerException if available, else return the current Exception.
/// </summary>
/// <param name="exception">The exception.</param>
/// <returns>
/// An <see cref="Exception"/> instance.
/// </returns>
internal static Exception GetInnerExceptionOrDefault(this Exception exception)
{
return exception?.InnerException ?? exception;
}
/// <summary>
/// Extensions to <see cref="Exception"/> type.
/// Get the exception message if available, empty otherwise.
/// </summary>
internal static class ExceptionExtensions
/// <param name="exception">An <see cref="Exception"/> object</param>
/// <returns>Exception message</returns>
internal static string TryGetMessage(this Exception exception)
{
/// <summary>
/// Get the InnerException if available, else return the current Exception.
/// </summary>
/// <param name="exception">The exception.</param>
/// <returns>
/// An <see cref="Exception"/> instance.
/// </returns>
internal static Exception GetInnerExceptionOrDefault(this Exception exception)
if (exception == null)
{
return exception?.InnerException ?? exception;
return string.Format(CultureInfo.CurrentCulture, Resource.UTF_FailedToGetExceptionMessage, "null");
}
/// <summary>
/// Get the exception message if available, empty otherwise.
/// </summary>
/// <param name="exception">An <see cref="Exception"/> object</param>
/// <returns>Exception message</returns>
internal static string TryGetMessage(this Exception exception)
{
if (exception == null)
{
return string.Format(CultureInfo.CurrentCulture, Resource.UTF_FailedToGetExceptionMessage, "null");
}
// It is safe to retrieve an exception message, it should not throw in any case.
return exception.Message ?? string.Empty;
}
// It is safe to retrieve an exception message, it should not throw in any case.
return exception.Message ?? string.Empty;
/// <summary>
/// Gets the <see cref="StackTraceInformation"/> for an exception.
/// </summary>
/// <param name="exception">An <see cref="Exception"/> instance.</param>
/// <returns>StackTraceInformation for the exception</returns>
internal static StackTraceInformation TryGetStackTraceInformation(this Exception exception)
{
if (!string.IsNullOrEmpty(exception?.StackTrace))
{
return StackTraceHelper.CreateStackTraceInformation(exception, false, exception.StackTrace);
}
/// <summary>
/// Gets the <see cref="StackTraceInformation"/> for an exception.
/// </summary>
/// <param name="exception">An <see cref="Exception"/> instance.</param>
/// <returns>StackTraceInformation for the exception</returns>
internal static StackTraceInformation TryGetStackTraceInformation(this Exception exception)
{
if (!string.IsNullOrEmpty(exception?.StackTrace))
{
return StackTraceHelper.CreateStackTraceInformation(exception, false, exception.StackTrace);
}
return null;
}
return null;
/// <summary>
/// Checks whether exception is an Assert exception
/// </summary>
/// <param name="exception">An <see cref="Exception"/> instance.</param>
/// <param name="outcome"> Framework's Outcome depending on type of assertion.</param>
/// <param name="exceptionMessage">Exception message.</param>
/// <param name="exceptionStackTrace">StackTraceInformation for the exception</param>
/// <returns>True, if Assert exception. False, otherwise.</returns>
internal static bool TryGetUnitTestAssertException(this Exception exception, out UTF.UnitTestOutcome outcome, out string exceptionMessage, out StackTraceInformation exceptionStackTrace)
{
if (exception is UTF.UnitTestAssertException)
{
outcome = exception is UTF.AssertInconclusiveException ?
UTF.UnitTestOutcome.Inconclusive : UTF.UnitTestOutcome.Failed;
exceptionMessage = exception.TryGetMessage();
exceptionStackTrace = exception.TryGetStackTraceInformation();
return true;
}
/// <summary>
/// Checks whether exception is an Assert exception
/// </summary>
/// <param name="exception">An <see cref="Exception"/> instance.</param>
/// <param name="outcome"> Framework's Outcome depending on type of assertion.</param>
/// <param name="exceptionMessage">Exception message.</param>
/// <param name="exceptionStackTrace">StackTraceInformation for the exception</param>
/// <returns>True, if Assert exception. False, otherwise.</returns>
internal static bool TryGetUnitTestAssertException(this Exception exception, out UTF.UnitTestOutcome outcome, out string exceptionMessage, out StackTraceInformation exceptionStackTrace)
else
{
if (exception is UTF.UnitTestAssertException)
{
outcome = exception is UTF.AssertInconclusiveException ?
UTF.UnitTestOutcome.Inconclusive : UTF.UnitTestOutcome.Failed;
exceptionMessage = exception.TryGetMessage();
exceptionStackTrace = exception.TryGetStackTraceInformation();
return true;
}
else
{
outcome = UTF.UnitTestOutcome.Failed;
exceptionMessage = null;
exceptionStackTrace = null;
return false;
}
outcome = UTF.UnitTestOutcome.Failed;
exceptionMessage = null;
exceptionStackTrace = null;
return false;
}
}
}

Просмотреть файл

@ -1,164 +1,163 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
namespace Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.Extensions
namespace Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.Extensions;
using System.Diagnostics;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Threading.Tasks;
using Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.Helpers;
using Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.ObjectModel;
using Microsoft.VisualStudio.TestTools.UnitTesting;
internal static class MethodInfoExtensions
{
using System.Diagnostics;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Threading.Tasks;
using Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.Helpers;
using Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.ObjectModel;
using Microsoft.VisualStudio.TestTools.UnitTesting;
internal static class MethodInfoExtensions
/// <summary>
/// Verifies that the class initialize has the correct signature
/// </summary>
/// <param name="method">The method to verify.</param>
/// <returns>True if the method has the right Assembly/Class initialize signature.</returns>
internal static bool HasCorrectClassOrAssemblyInitializeSignature(this MethodInfo method)
{
/// <summary>
/// Verifies that the class initialize has the correct signature
/// </summary>
/// <param name="method">The method to verify.</param>
/// <returns>True if the method has the right Assembly/Class initialize signature.</returns>
internal static bool HasCorrectClassOrAssemblyInitializeSignature(this MethodInfo method)
Debug.Assert(method != null, "method should not be null.");
ParameterInfo[] parameters = method.GetParameters();
return
method.IsStatic &&
method.IsPublic &&
(parameters.Length == 1) &&
parameters[0].ParameterType == typeof(TestContext) &&
method.IsVoidOrTaskReturnType();
}
/// <summary>
/// Verifies that the class cleanup has the correct signature
/// </summary>
/// <param name="method">The method to verify.</param>
/// <returns>True if the method has the right Assembly/Class cleanup signature.</returns>
internal static bool HasCorrectClassOrAssemblyCleanupSignature(this MethodInfo method)
{
Debug.Assert(method != null, "method should not be null.");
return
method.IsStatic &&
method.IsPublic &&
(method.GetParameters().Length == 0) &&
method.IsVoidOrTaskReturnType();
}
/// <summary>
/// Verifies that the test Initialize/cleanup has the correct signature
/// </summary>
/// <param name="method">The method to verify.</param>
/// <returns>True if the method has the right test init/cleanup signature.</returns>
internal static bool HasCorrectTestInitializeOrCleanupSignature(this MethodInfo method)
{
Debug.Assert(method != null, "method should not be null.");
return
!method.IsStatic &&
method.IsPublic &&
(method.GetParameters().Length == 0) &&
method.IsVoidOrTaskReturnType();
}
/// <summary>
/// Verifies that the test method has the correct signature
/// </summary>
/// <param name="method">The method to verify.</param>
/// <param name="ignoreParameterLength">Indicates whether parameter length is to be ignored.</param>
/// <param name="discoverInternals">True if internal test classes and test methods should be discovered in
/// addition to public test classes and methods.</param>
/// <returns>True if the method has the right test method signature.</returns>
internal static bool HasCorrectTestMethodSignature(this MethodInfo method, bool ignoreParameterLength, bool discoverInternals = false)
{
Debug.Assert(method != null, "method should not be null.");
return
!method.IsAbstract &&
!method.IsStatic &&
!method.IsGenericMethod &&
(method.IsPublic || (discoverInternals && method.IsAssembly)) &&
(method.GetParameters().Length == 0 || ignoreParameterLength) &&
method.IsVoidOrTaskReturnType(); // Match return type Task for async methods only. Else return type void.
}
/// <summary>
/// Checks whether test method has correct Timeout attribute.
/// </summary>
/// <param name="method">The method to verify.</param>
/// <returns>True if the method has the right test timeout signature.</returns>
internal static bool HasCorrectTimeout(this MethodInfo method)
{
Debug.Assert(method != null, "method should not be null.");
// There should be one and only one TimeoutAttribute.
var attributes = ReflectHelper.GetCustomAttributes(method, typeof(TimeoutAttribute), false);
if (attributes?.Length != 1)
{
Debug.Assert(method != null, "method should not be null.");
ParameterInfo[] parameters = method.GetParameters();
return
method.IsStatic &&
method.IsPublic &&
(parameters.Length == 1) &&
parameters[0].ParameterType == typeof(TestContext) &&
method.IsVoidOrTaskReturnType();
return false;
}
/// <summary>
/// Verifies that the class cleanup has the correct signature
/// </summary>
/// <param name="method">The method to verify.</param>
/// <returns>True if the method has the right Assembly/Class cleanup signature.</returns>
internal static bool HasCorrectClassOrAssemblyCleanupSignature(this MethodInfo method)
{
Debug.Assert(method != null, "method should not be null.");
// Timeout cannot be less than 0.
var attribute = attributes[0] as TimeoutAttribute;
return
method.IsStatic &&
method.IsPublic &&
(method.GetParameters().Length == 0) &&
method.IsVoidOrTaskReturnType();
return !(attribute?.Timeout < 0);
}
/// <summary>
/// Check is return type is void for non async and Task for async methods.
/// </summary>
/// <param name="method">The method to verify.</param>
/// <returns>True if the method has a void/task return type..</returns>
internal static bool IsVoidOrTaskReturnType(this MethodInfo method)
{
return ReflectHelper.MatchReturnType(method, typeof(Task))
|| (ReflectHelper.MatchReturnType(method, typeof(void)) && method.GetAsyncTypeName() == null);
}
/// <summary>
/// For async methods compiler generates different type and method.
/// Gets the compiler generated type name for given async test method.
/// </summary>
/// <param name="method">The method to verify.</param>
/// <returns>Compiler generated type name for given async test method..</returns>
internal static string GetAsyncTypeName(this MethodInfo method)
{
var asyncStateMachineAttribute = ReflectHelper.GetCustomAttributes(method, typeof(AsyncStateMachineAttribute), false).FirstOrDefault() as AsyncStateMachineAttribute;
return asyncStateMachineAttribute?.StateMachineType?.FullName;
}
/// <summary>
/// Invoke a <see cref="MethodInfo"/> as a synchronous <see cref="Task"/>.
/// </summary>
/// <param name="methodInfo">
/// <see cref="MethodInfo"/> instance.
/// </param>
/// <param name="classInstance">
/// Instance of the on which methodInfo is invoked.
/// </param>
/// <param name="parameters">
/// Arguments for the methodInfo invoke.
/// </param>
internal static void InvokeAsSynchronousTask(this MethodInfo methodInfo, object classInstance, params object[] parameters)
{
var methodParameters = methodInfo.GetParameters();
// check if testmethod expected parameter values but no testdata was provided,
// throw error with appropriate message.
if (methodParameters != null && methodParameters.Length > 0 && parameters == null)
{
throw new TestFailedException(ObjectModel.UnitTestOutcome.Error, Resource.UTA_TestMethodExpectedParameters);
}
/// <summary>
/// Verifies that the test Initialize/cleanup has the correct signature
/// </summary>
/// <param name="method">The method to verify.</param>
/// <returns>True if the method has the right test init/cleanup signature.</returns>
internal static bool HasCorrectTestInitializeOrCleanupSignature(this MethodInfo method)
{
Debug.Assert(method != null, "method should not be null.");
var task = methodInfo.Invoke(classInstance, parameters) as Task;
return
!method.IsStatic &&
method.IsPublic &&
(method.GetParameters().Length == 0) &&
method.IsVoidOrTaskReturnType();
}
/// <summary>
/// Verifies that the test method has the correct signature
/// </summary>
/// <param name="method">The method to verify.</param>
/// <param name="ignoreParameterLength">Indicates whether parameter length is to be ignored.</param>
/// <param name="discoverInternals">True if internal test classes and test methods should be discovered in
/// addition to public test classes and methods.</param>
/// <returns>True if the method has the right test method signature.</returns>
internal static bool HasCorrectTestMethodSignature(this MethodInfo method, bool ignoreParameterLength, bool discoverInternals = false)
{
Debug.Assert(method != null, "method should not be null.");
return
!method.IsAbstract &&
!method.IsStatic &&
!method.IsGenericMethod &&
(method.IsPublic || (discoverInternals && method.IsAssembly)) &&
(method.GetParameters().Length == 0 || ignoreParameterLength) &&
method.IsVoidOrTaskReturnType(); // Match return type Task for async methods only. Else return type void.
}
/// <summary>
/// Checks whether test method has correct Timeout attribute.
/// </summary>
/// <param name="method">The method to verify.</param>
/// <returns>True if the method has the right test timeout signature.</returns>
internal static bool HasCorrectTimeout(this MethodInfo method)
{
Debug.Assert(method != null, "method should not be null.");
// There should be one and only one TimeoutAttribute.
var attributes = ReflectHelper.GetCustomAttributes(method, typeof(TimeoutAttribute), false);
if (attributes?.Length != 1)
{
return false;
}
// Timeout cannot be less than 0.
var attribute = attributes[0] as TimeoutAttribute;
return !(attribute?.Timeout < 0);
}
/// <summary>
/// Check is return type is void for non async and Task for async methods.
/// </summary>
/// <param name="method">The method to verify.</param>
/// <returns>True if the method has a void/task return type..</returns>
internal static bool IsVoidOrTaskReturnType(this MethodInfo method)
{
return ReflectHelper.MatchReturnType(method, typeof(Task))
|| (ReflectHelper.MatchReturnType(method, typeof(void)) && method.GetAsyncTypeName() == null);
}
/// <summary>
/// For async methods compiler generates different type and method.
/// Gets the compiler generated type name for given async test method.
/// </summary>
/// <param name="method">The method to verify.</param>
/// <returns>Compiler generated type name for given async test method..</returns>
internal static string GetAsyncTypeName(this MethodInfo method)
{
var asyncStateMachineAttribute = ReflectHelper.GetCustomAttributes(method, typeof(AsyncStateMachineAttribute), false).FirstOrDefault() as AsyncStateMachineAttribute;
return asyncStateMachineAttribute?.StateMachineType?.FullName;
}
/// <summary>
/// Invoke a <see cref="MethodInfo"/> as a synchronous <see cref="Task"/>.
/// </summary>
/// <param name="methodInfo">
/// <see cref="MethodInfo"/> instance.
/// </param>
/// <param name="classInstance">
/// Instance of the on which methodInfo is invoked.
/// </param>
/// <param name="parameters">
/// Arguments for the methodInfo invoke.
/// </param>
internal static void InvokeAsSynchronousTask(this MethodInfo methodInfo, object classInstance, params object[] parameters)
{
var methodParameters = methodInfo.GetParameters();
// check if testmethod expected parameter values but no testdata was provided,
// throw error with appropriate message.
if (methodParameters != null && methodParameters.Length > 0 && parameters == null)
{
throw new TestFailedException(ObjectModel.UnitTestOutcome.Error, Resource.UTA_TestMethodExpectedParameters);
}
var task = methodInfo.Invoke(classInstance, parameters) as Task;
// If methodInfo is an Async method, wait for returned task
task?.GetAwaiter().GetResult();
}
// If methodInfo is an Async method, wait for returned task
task?.GetAwaiter().GetResult();
}
}

Просмотреть файл

@ -1,168 +1,167 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
namespace Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.Extensions
namespace Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.Extensions;
using System.Collections.Generic;
using System.Linq;
using Microsoft.TestPlatform.AdapterUtilities;
using Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.ObjectModel;
using Microsoft.VisualStudio.TestPlatform.ObjectModel;
using Constants = Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.Constants;
/// <summary>
/// Extension Methods for TestCase Class
/// </summary>
internal static class TestCaseExtensions
{
using System.Collections.Generic;
using System.Linq;
internal static readonly TestProperty ManagedTypeProperty = TestProperty.Register(
id: ManagedNameConstants.ManagedTypePropertyId,
label: ManagedNameConstants.ManagedTypeLabel,
category: string.Empty,
description: string.Empty,
valueType: typeof(string),
validateValueCallback: o => !string.IsNullOrWhiteSpace(o as string),
attributes: TestPropertyAttributes.Hidden,
owner: typeof(TestCase));
using Microsoft.TestPlatform.AdapterUtilities;
using Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.ObjectModel;
using Microsoft.VisualStudio.TestPlatform.ObjectModel;
internal static readonly TestProperty ManagedMethodProperty = TestProperty.Register(
id: ManagedNameConstants.ManagedMethodPropertyId,
label: ManagedNameConstants.ManagedMethodLabel,
category: string.Empty,
description: string.Empty,
valueType: typeof(string),
validateValueCallback: o => !string.IsNullOrWhiteSpace(o as string),
attributes: TestPropertyAttributes.Hidden,
owner: typeof(TestCase));
using Constants = Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.Constants;
internal static readonly TestProperty HierarchyProperty = TestProperty.Register(
id: HierarchyConstants.HierarchyPropertyId,
label: HierarchyConstants.HierarchyLabel,
category: string.Empty,
description: string.Empty,
valueType: typeof(string[]),
validateValueCallback: null,
attributes: TestPropertyAttributes.Immutable,
owner: typeof(TestCase));
/// <summary>
/// Extension Methods for TestCase Class
/// The test name
/// </summary>
internal static class TestCaseExtensions
/// <param name="testCase"> The test case. </param>
/// <param name="testClassName"> The test case's class name. </param>
/// <returns> The test name, without the class name, if provided. </returns>
internal static string GetTestName(this TestCase testCase, string testClassName)
{
internal static readonly TestProperty ManagedTypeProperty = TestProperty.Register(
id: ManagedNameConstants.ManagedTypePropertyId,
label: ManagedNameConstants.ManagedTypeLabel,
category: string.Empty,
description: string.Empty,
valueType: typeof(string),
validateValueCallback: o => !string.IsNullOrWhiteSpace(o as string),
attributes: TestPropertyAttributes.Hidden,
owner: typeof(TestCase));
var fullyQualifiedName = testCase.FullyQualifiedName;
internal static readonly TestProperty ManagedMethodProperty = TestProperty.Register(
id: ManagedNameConstants.ManagedMethodPropertyId,
label: ManagedNameConstants.ManagedMethodLabel,
category: string.Empty,
description: string.Empty,
valueType: typeof(string),
validateValueCallback: o => !string.IsNullOrWhiteSpace(o as string),
attributes: TestPropertyAttributes.Hidden,
owner: typeof(TestCase));
// Not using Replace because there can be multiple instances of that string.
var name = fullyQualifiedName.StartsWith($"{testClassName}.")
? fullyQualifiedName.Remove(0, $"{testClassName}.".Length)
: fullyQualifiedName;
internal static readonly TestProperty HierarchyProperty = TestProperty.Register(
id: HierarchyConstants.HierarchyPropertyId,
label: HierarchyConstants.HierarchyLabel,
category: string.Empty,
description: string.Empty,
valueType: typeof(string[]),
validateValueCallback: null,
attributes: TestPropertyAttributes.Immutable,
owner: typeof(TestCase));
/// <summary>
/// The test name
/// </summary>
/// <param name="testCase"> The test case. </param>
/// <param name="testClassName"> The test case's class name. </param>
/// <returns> The test name, without the class name, if provided. </returns>
internal static string GetTestName(this TestCase testCase, string testClassName)
{
var fullyQualifiedName = testCase.FullyQualifiedName;
// Not using Replace because there can be multiple instances of that string.
var name = fullyQualifiedName.StartsWith($"{testClassName}.")
? fullyQualifiedName.Remove(0, $"{testClassName}.".Length)
: fullyQualifiedName;
return name;
}
/// <summary>
/// The to unit test element.
/// </summary>
/// <param name="testCase"> The test case. </param>
/// <param name="source"> The source. If deployed this is the full path of the source in the deployment directory. </param>
/// <returns> The converted <see cref="UnitTestElement"/>. </returns>
internal static UnitTestElement ToUnitTestElement(this TestCase testCase, string source)
{
var isAsync = (testCase.GetPropertyValue(Constants.AsyncTestProperty) as bool?) ?? false;
var testClassName = testCase.GetPropertyValue(Constants.TestClassNameProperty) as string;
var name = testCase.GetTestName(testClassName);
TestMethod testMethod;
if (testCase.ContainsManagedMethodAndType())
{
testMethod = new TestMethod(testCase.GetManagedType(), testCase.GetManagedMethod(), testCase.GetHierarchy(), name, testClassName, source, isAsync);
}
else
{
testMethod = new TestMethod(name, testClassName, source, isAsync);
}
var dataType = (DynamicDataType)testCase.GetPropertyValue(Constants.TestDynamicDataTypeProperty, (int)DynamicDataType.None);
if (dataType != DynamicDataType.None)
{
var data = testCase.GetPropertyValue<string[]>(Constants.TestDynamicDataProperty, null);
testMethod.DataType = dataType;
testMethod.SerializedData = data;
}
testMethod.DisplayName = testCase.DisplayName;
if (testCase.GetPropertyValue(Constants.DeclaringClassNameProperty) is string declaringClassName && declaringClassName != testClassName)
{
testMethod.DeclaringClassFullName = declaringClassName;
}
UnitTestElement testElement = new(testMethod)
{
IsAsync = isAsync,
TestCategory = testCase.GetPropertyValue(Constants.TestCategoryProperty) as string[],
Priority = testCase.GetPropertyValue(Constants.PriorityProperty) as int?,
DisplayName = testCase.DisplayName
};
if (testCase.Traits.Any())
{
testElement.Traits = testCase.Traits.ToArray();
}
var cssIteration = testCase.GetPropertyValue<string>(Constants.CssIterationProperty, null);
if (!string.IsNullOrWhiteSpace(cssIteration))
{
testElement.CssIteration = cssIteration;
}
var cssProjectStructure = testCase.GetPropertyValue<string>(Constants.CssProjectStructureProperty, null);
if (!string.IsNullOrWhiteSpace(cssIteration))
{
testElement.CssProjectStructure = cssProjectStructure;
}
var description = testCase.GetPropertyValue<string>(Constants.DescriptionProperty, null);
if (!string.IsNullOrWhiteSpace(description))
{
testElement.Description = description;
}
var workItemIds = testCase.GetPropertyValue<string[]>(Constants.WorkItemIdsProperty, null);
if (workItemIds != null && workItemIds.Length > 0)
{
testElement.WorkItemIds = workItemIds;
}
var deploymentItems = testCase.GetPropertyValue<KeyValuePair<string, string>[]>(Constants.DeploymentItemsProperty, null);
if (deploymentItems != null && deploymentItems.Length > 0)
{
testElement.DeploymentItems = deploymentItems;
}
testElement.DoNotParallelize = testCase.GetPropertyValue(Constants.DoNotParallelizeProperty, false);
return testElement;
}
internal static string GetManagedType(this TestCase testCase) => testCase.GetPropertyValue<string>(ManagedTypeProperty, null);
internal static void SetManagedType(this TestCase testCase, string value) => testCase.SetPropertyValue<string>(ManagedTypeProperty, value);
internal static string GetManagedMethod(this TestCase testCase) => testCase.GetPropertyValue<string>(ManagedMethodProperty, null);
internal static void SetManagedMethod(this TestCase testCase, string value) => testCase.SetPropertyValue<string>(ManagedMethodProperty, value);
internal static bool ContainsManagedMethodAndType(this TestCase testCase) => !string.IsNullOrWhiteSpace(testCase.GetManagedMethod()) && !string.IsNullOrWhiteSpace(testCase.GetManagedType());
internal static string[] GetHierarchy(this TestCase testCase) => testCase.GetPropertyValue<string[]>(HierarchyProperty, null);
internal static void SetHierarchy(this TestCase testCase, params string[] value) => testCase.SetPropertyValue<string[]>(HierarchyProperty, value);
return name;
}
/// <summary>
/// The to unit test element.
/// </summary>
/// <param name="testCase"> The test case. </param>
/// <param name="source"> The source. If deployed this is the full path of the source in the deployment directory. </param>
/// <returns> The converted <see cref="UnitTestElement"/>. </returns>
internal static UnitTestElement ToUnitTestElement(this TestCase testCase, string source)
{
var isAsync = (testCase.GetPropertyValue(Constants.AsyncTestProperty) as bool?) ?? false;
var testClassName = testCase.GetPropertyValue(Constants.TestClassNameProperty) as string;
var name = testCase.GetTestName(testClassName);
TestMethod testMethod;
if (testCase.ContainsManagedMethodAndType())
{
testMethod = new TestMethod(testCase.GetManagedType(), testCase.GetManagedMethod(), testCase.GetHierarchy(), name, testClassName, source, isAsync);
}
else
{
testMethod = new TestMethod(name, testClassName, source, isAsync);
}
var dataType = (DynamicDataType)testCase.GetPropertyValue(Constants.TestDynamicDataTypeProperty, (int)DynamicDataType.None);
if (dataType != DynamicDataType.None)
{
var data = testCase.GetPropertyValue<string[]>(Constants.TestDynamicDataProperty, null);
testMethod.DataType = dataType;
testMethod.SerializedData = data;
}
testMethod.DisplayName = testCase.DisplayName;
if (testCase.GetPropertyValue(Constants.DeclaringClassNameProperty) is string declaringClassName && declaringClassName != testClassName)
{
testMethod.DeclaringClassFullName = declaringClassName;
}
UnitTestElement testElement = new(testMethod)
{
IsAsync = isAsync,
TestCategory = testCase.GetPropertyValue(Constants.TestCategoryProperty) as string[],
Priority = testCase.GetPropertyValue(Constants.PriorityProperty) as int?,
DisplayName = testCase.DisplayName
};
if (testCase.Traits.Any())
{
testElement.Traits = testCase.Traits.ToArray();
}
var cssIteration = testCase.GetPropertyValue<string>(Constants.CssIterationProperty, null);
if (!string.IsNullOrWhiteSpace(cssIteration))
{
testElement.CssIteration = cssIteration;
}
var cssProjectStructure = testCase.GetPropertyValue<string>(Constants.CssProjectStructureProperty, null);
if (!string.IsNullOrWhiteSpace(cssIteration))
{
testElement.CssProjectStructure = cssProjectStructure;
}
var description = testCase.GetPropertyValue<string>(Constants.DescriptionProperty, null);
if (!string.IsNullOrWhiteSpace(description))
{
testElement.Description = description;
}
var workItemIds = testCase.GetPropertyValue<string[]>(Constants.WorkItemIdsProperty, null);
if (workItemIds != null && workItemIds.Length > 0)
{
testElement.WorkItemIds = workItemIds;
}
var deploymentItems = testCase.GetPropertyValue<KeyValuePair<string, string>[]>(Constants.DeploymentItemsProperty, null);
if (deploymentItems != null && deploymentItems.Length > 0)
{
testElement.DeploymentItems = deploymentItems;
}
testElement.DoNotParallelize = testCase.GetPropertyValue(Constants.DoNotParallelizeProperty, false);
return testElement;
}
internal static string GetManagedType(this TestCase testCase) => testCase.GetPropertyValue<string>(ManagedTypeProperty, null);
internal static void SetManagedType(this TestCase testCase, string value) => testCase.SetPropertyValue<string>(ManagedTypeProperty, value);
internal static string GetManagedMethod(this TestCase testCase) => testCase.GetPropertyValue<string>(ManagedMethodProperty, null);
internal static void SetManagedMethod(this TestCase testCase, string value) => testCase.SetPropertyValue<string>(ManagedMethodProperty, value);
internal static bool ContainsManagedMethodAndType(this TestCase testCase) => !string.IsNullOrWhiteSpace(testCase.GetManagedMethod()) && !string.IsNullOrWhiteSpace(testCase.GetManagedType());
internal static string[] GetHierarchy(this TestCase testCase) => testCase.GetPropertyValue<string[]>(HierarchyProperty, null);
internal static void SetHierarchy(this TestCase testCase, params string[] value) => testCase.SetPropertyValue<string[]>(HierarchyProperty, value);
}

Просмотреть файл

@ -1,24 +1,23 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
namespace Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.Extensions
namespace Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.Extensions;
using Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.Interface;
internal static class TestContextExtensions
{
using Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.Interface;
internal static class TestContextExtensions
/// <summary>
/// Returns diagnostic messages written to test context and clears from this instance.
/// </summary>
/// <param name="testContext">The test context instance.</param>
/// <returns>The diagnostic messages.</returns>
internal static string GetAndClearDiagnosticMessages(this ITestContext testContext)
{
/// <summary>
/// Returns diagnostic messages written to test context and clears from this instance.
/// </summary>
/// <param name="testContext">The test context instance.</param>
/// <returns>The diagnostic messages.</returns>
internal static string GetAndClearDiagnosticMessages(this ITestContext testContext)
{
var messages = testContext.GetDiagnosticMessages();
var messages = testContext.GetDiagnosticMessages();
testContext.ClearDiagnosticMessages();
testContext.ClearDiagnosticMessages();
return messages;
}
return messages;
}
}

Просмотреть файл

@ -1,57 +1,56 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
namespace Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.Extensions
namespace Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.Extensions;
using Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.ObjectModel;
using UTF = Microsoft.VisualStudio.TestTools.UnitTesting;
public static class TestResultExtensions
{
using Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.ObjectModel;
using UTF = Microsoft.VisualStudio.TestTools.UnitTesting;
public static class TestResultExtensions
/// <summary>
/// Converts the test framework's TestResult objects array to a serializable UnitTestResult objects array.
/// </summary>
/// <param name="testResults">The test framework's TestResult object array.</param>
/// <returns>The serializable UnitTestResult object array.</returns>
public static UnitTestResult[] ToUnitTestResults(this UTF.TestResult[] testResults)
{
/// <summary>
/// Converts the test framework's TestResult objects array to a serializable UnitTestResult objects array.
/// </summary>
/// <param name="testResults">The test framework's TestResult object array.</param>
/// <returns>The serializable UnitTestResult object array.</returns>
public static UnitTestResult[] ToUnitTestResults(this UTF.TestResult[] testResults)
UnitTestResult[] unitTestResults = new UnitTestResult[testResults.Length];
for (int i = 0; i < testResults.Length; ++i)
{
UnitTestResult[] unitTestResults = new UnitTestResult[testResults.Length];
UnitTestResult unitTestResult = null;
UnitTestOutcome outcome = testResults[i].Outcome.ToUnitTestOutcome();
for (int i = 0; i < testResults.Length; ++i)
if (testResults[i].TestFailureException != null)
{
UnitTestResult unitTestResult = null;
UnitTestOutcome outcome = testResults[i].Outcome.ToUnitTestOutcome();
if (testResults[i].TestFailureException != null)
{
unitTestResult =
new UnitTestResult(
new TestFailedException(
outcome,
testResults[i].TestFailureException.TryGetMessage(),
testResults[i].TestFailureException is TestFailedException testException ? testException.StackTraceInformation : testResults[i].TestFailureException.TryGetStackTraceInformation()));
}
else
{
unitTestResult = new UnitTestResult { Outcome = outcome };
}
unitTestResult.StandardOut = testResults[i].LogOutput;
unitTestResult.StandardError = testResults[i].LogError;
unitTestResult.DebugTrace = testResults[i].DebugTrace;
unitTestResult.TestContextMessages = testResults[i].TestContextMessages;
unitTestResult.Duration = testResults[i].Duration;
unitTestResult.DisplayName = testResults[i].DisplayName;
unitTestResult.DatarowIndex = testResults[i].DatarowIndex;
unitTestResult.ResultFiles = testResults[i].ResultFiles;
unitTestResult.ExecutionId = testResults[i].ExecutionId;
unitTestResult.ParentExecId = testResults[i].ParentExecId;
unitTestResult.InnerResultsCount = testResults[i].InnerResultsCount;
unitTestResults[i] = unitTestResult;
unitTestResult =
new UnitTestResult(
new TestFailedException(
outcome,
testResults[i].TestFailureException.TryGetMessage(),
testResults[i].TestFailureException is TestFailedException testException ? testException.StackTraceInformation : testResults[i].TestFailureException.TryGetStackTraceInformation()));
}
else
{
unitTestResult = new UnitTestResult { Outcome = outcome };
}
return unitTestResults;
unitTestResult.StandardOut = testResults[i].LogOutput;
unitTestResult.StandardError = testResults[i].LogError;
unitTestResult.DebugTrace = testResults[i].DebugTrace;
unitTestResult.TestContextMessages = testResults[i].TestContextMessages;
unitTestResult.Duration = testResults[i].Duration;
unitTestResult.DisplayName = testResults[i].DisplayName;
unitTestResult.DatarowIndex = testResults[i].DatarowIndex;
unitTestResult.ResultFiles = testResults[i].ResultFiles;
unitTestResult.ExecutionId = testResults[i].ExecutionId;
unitTestResult.ParentExecId = testResults[i].ParentExecId;
unitTestResult.InnerResultsCount = testResults[i].InnerResultsCount;
unitTestResults[i] = unitTestResult;
}
return unitTestResults;
}
}

Просмотреть файл

@ -1,45 +1,44 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
namespace Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.Extensions
namespace Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.Extensions;
using Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.ObjectModel;
using UTF = Microsoft.VisualStudio.TestTools.UnitTesting;
public static class UnitTestOutcomeExtensions
{
using Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.ObjectModel;
using UTF = Microsoft.VisualStudio.TestTools.UnitTesting;
public static class UnitTestOutcomeExtensions
/// <summary>
/// Converts the test framework's UnitTestOutcome object to adapter's UnitTestOutcome object.
/// </summary>
/// <param name="frameworkTestOutcome">The test framework's UnitTestOutcome object.</param>
/// <returns>The adapter's UnitTestOutcome object.</returns>
public static UnitTestOutcome ToUnitTestOutcome(this UTF.UnitTestOutcome frameworkTestOutcome)
{
/// <summary>
/// Converts the test framework's UnitTestOutcome object to adapter's UnitTestOutcome object.
/// </summary>
/// <param name="frameworkTestOutcome">The test framework's UnitTestOutcome object.</param>
/// <returns>The adapter's UnitTestOutcome object.</returns>
public static UnitTestOutcome ToUnitTestOutcome(this UTF.UnitTestOutcome frameworkTestOutcome)
UnitTestOutcome outcome = frameworkTestOutcome switch
{
UnitTestOutcome outcome = frameworkTestOutcome switch
{
UTF.UnitTestOutcome.Failed => UnitTestOutcome.Failed,
UTF.UnitTestOutcome.Inconclusive => UnitTestOutcome.Inconclusive,
UTF.UnitTestOutcome.InProgress => UnitTestOutcome.InProgress,
UTF.UnitTestOutcome.Passed => UnitTestOutcome.Passed,
UTF.UnitTestOutcome.Timeout => UnitTestOutcome.Timeout,
UTF.UnitTestOutcome.NotRunnable => UnitTestOutcome.NotRunnable,
_ => UnitTestOutcome.Error,
};
return outcome;
}
UTF.UnitTestOutcome.Failed => UnitTestOutcome.Failed,
UTF.UnitTestOutcome.Inconclusive => UnitTestOutcome.Inconclusive,
UTF.UnitTestOutcome.InProgress => UnitTestOutcome.InProgress,
UTF.UnitTestOutcome.Passed => UnitTestOutcome.Passed,
UTF.UnitTestOutcome.Timeout => UnitTestOutcome.Timeout,
UTF.UnitTestOutcome.NotRunnable => UnitTestOutcome.NotRunnable,
_ => UnitTestOutcome.Error,
};
return outcome;
}
/// <summary>
/// Returns more important outcome of two.
/// </summary>
/// <param name="outcome1"> First outcome that needs to be compared. </param>
/// <param name="outcome2"> Second outcome that needs to be compared. </param>
/// <returns> Outcome which has higher importance.</returns>
internal static UTF.UnitTestOutcome GetMoreImportantOutcome(this UTF.UnitTestOutcome outcome1, UTF.UnitTestOutcome outcome2)
{
var unitTestOutcome1 = outcome1.ToUnitTestOutcome();
var unitTestOutcome2 = outcome2.ToUnitTestOutcome();
return unitTestOutcome1 < unitTestOutcome2 ? outcome1 : outcome2;
}
/// <summary>
/// Returns more important outcome of two.
/// </summary>
/// <param name="outcome1"> First outcome that needs to be compared. </param>
/// <param name="outcome2"> Second outcome that needs to be compared. </param>
/// <returns> Outcome which has higher importance.</returns>
internal static UTF.UnitTestOutcome GetMoreImportantOutcome(this UTF.UnitTestOutcome outcome1, UTF.UnitTestOutcome outcome2)
{
var unitTestOutcome1 = outcome1.ToUnitTestOutcome();
var unitTestOutcome2 = outcome2.ToUnitTestOutcome();
return unitTestOutcome1 < unitTestOutcome2 ? outcome1 : outcome2;
}
}

Просмотреть файл

@ -1,116 +1,115 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
namespace Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.Helpers
namespace Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.Helpers;
using System;
using System.Collections.Concurrent;
using System.IO;
using System.Runtime.Serialization.Json;
using System.Text;
internal static class DataSerializationHelper
{
using System;
using System.Collections.Concurrent;
using System.IO;
using System.Runtime.Serialization.Json;
using System.Text;
internal static class DataSerializationHelper
private static readonly ConcurrentDictionary<string, DataContractJsonSerializer> SerializerCache = new();
private static readonly DataContractJsonSerializerSettings SerializerSettings = new()
{
private static readonly ConcurrentDictionary<string, DataContractJsonSerializer> SerializerCache = new();
private static readonly DataContractJsonSerializerSettings SerializerSettings = new()
UseSimpleDictionaryFormat = true,
EmitTypeInformation = System.Runtime.Serialization.EmitTypeInformation.Always,
DateTimeFormat = new System.Runtime.Serialization.DateTimeFormat("O", System.Globalization.CultureInfo.InvariantCulture)
};
/// <summary>
/// Serializes the date in such a way that won't throw exceptions during deserialization in Test Platform.
/// The result can be deserialized using <see cref="Deserialize(string[])"/> method.
/// </summary>
/// <param name="data">Data array to serialize.</param>
/// <returns>Serialized array.</returns>
public static string[] Serialize(object[] data)
{
if (data == null)
{
UseSimpleDictionaryFormat = true,
EmitTypeInformation = System.Runtime.Serialization.EmitTypeInformation.Always,
DateTimeFormat = new System.Runtime.Serialization.DateTimeFormat("O", System.Globalization.CultureInfo.InvariantCulture)
};
/// <summary>
/// Serializes the date in such a way that won't throw exceptions during deserialization in Test Platform.
/// The result can be deserialized using <see cref="Deserialize(string[])"/> method.
/// </summary>
/// <param name="data">Data array to serialize.</param>
/// <returns>Serialized array.</returns>
public static string[] Serialize(object[] data)
{
if (data == null)
{
return null;
}
var serializedData = new string[data.Length * 2];
for (int i = 0; i < data.Length; i++)
{
var typeIndex = i * 2;
var dataIndex = typeIndex + 1;
if (data[i] == null)
{
serializedData[typeIndex] = null;
serializedData[dataIndex] = null;
continue;
}
var type = data[i].GetType();
var typeName = type.AssemblyQualifiedName;
serializedData[typeIndex] = typeName;
var serializer = GetSerializer(type);
using var memoryStream = new MemoryStream();
serializer.WriteObject(memoryStream, data[i]);
var serializerData = memoryStream.ToArray();
serializedData[dataIndex] = Encoding.UTF8.GetString(serializerData, 0, serializerData.Length);
}
return serializedData;
return null;
}
/// <summary>
/// Deserializes the data serialized by <see cref="Serialize(object[])" /> method.
/// </summary>
/// <param name="serializedData">Serialized data array to deserialize.</param>
/// <returns>Deserialized array.</returns>
public static object[] Deserialize(string[] serializedData)
var serializedData = new string[data.Length * 2];
for (int i = 0; i < data.Length; i++)
{
if (serializedData == null || serializedData.Length % 2 != 0)
var typeIndex = i * 2;
var dataIndex = typeIndex + 1;
if (data[i] == null)
{
return null;
serializedData[typeIndex] = null;
serializedData[dataIndex] = null;
continue;
}
var length = serializedData.Length / 2;
var data = new object[length];
var type = data[i].GetType();
var typeName = type.AssemblyQualifiedName;
for (int i = 0; i < length; i++)
serializedData[typeIndex] = typeName;
var serializer = GetSerializer(type);
using var memoryStream = new MemoryStream();
serializer.WriteObject(memoryStream, data[i]);
var serializerData = memoryStream.ToArray();
serializedData[dataIndex] = Encoding.UTF8.GetString(serializerData, 0, serializerData.Length);
}
return serializedData;
}
/// <summary>
/// Deserializes the data serialized by <see cref="Serialize(object[])" /> method.
/// </summary>
/// <param name="serializedData">Serialized data array to deserialize.</param>
/// <returns>Deserialized array.</returns>
public static object[] Deserialize(string[] serializedData)
{
if (serializedData == null || serializedData.Length % 2 != 0)
{
return null;
}
var length = serializedData.Length / 2;
var data = new object[length];
for (int i = 0; i < length; i++)
{
var typeIndex = i * 2;
var assemblyQualifiedName = serializedData[typeIndex];
var serializedValue = serializedData[typeIndex + 1];
if (serializedValue == null || assemblyQualifiedName == null)
{
var typeIndex = i * 2;
var assemblyQualifiedName = serializedData[typeIndex];
var serializedValue = serializedData[typeIndex + 1];
if (serializedValue == null || assemblyQualifiedName == null)
{
data[i] = null;
continue;
}
var serializer = GetSerializer(assemblyQualifiedName);
var serialzedDataBytes = Encoding.UTF8.GetBytes(serializedValue);
using var memoryStream = new MemoryStream(serialzedDataBytes);
data[i] = serializer.ReadObject(memoryStream);
data[i] = null;
continue;
}
return data;
var serializer = GetSerializer(assemblyQualifiedName);
var serialzedDataBytes = Encoding.UTF8.GetBytes(serializedValue);
using var memoryStream = new MemoryStream(serialzedDataBytes);
data[i] = serializer.ReadObject(memoryStream);
}
private static DataContractJsonSerializer GetSerializer(string assemblyQualifiedName)
{
return SerializerCache.GetOrAdd(
assemblyQualifiedName,
_ => new DataContractJsonSerializer(Type.GetType(assemblyQualifiedName) ?? typeof(object), SerializerSettings));
}
return data;
}
private static DataContractJsonSerializer GetSerializer(Type type)
{
return SerializerCache.GetOrAdd(
type.AssemblyQualifiedName,
_ => new DataContractJsonSerializer(type, SerializerSettings));
}
private static DataContractJsonSerializer GetSerializer(string assemblyQualifiedName)
{
return SerializerCache.GetOrAdd(
assemblyQualifiedName,
_ => new DataContractJsonSerializer(Type.GetType(assemblyQualifiedName) ?? typeof(object), SerializerSettings));
}
private static DataContractJsonSerializer GetSerializer(Type type)
{
return SerializerCache.GetOrAdd(
type.AssemblyQualifiedName,
_ => new DataContractJsonSerializer(type, SerializerSettings));
}
}

Просмотреть файл

@ -1,61 +1,60 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
namespace Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.Helpers
namespace Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.Helpers;
using System;
using System.Collections.Generic;
using System.Linq;
internal static class DictionaryHelper
{
using System;
using System.Collections.Generic;
using System.Linq;
internal static class DictionaryHelper
public static IDictionary<TKey, TValue> ConcatWithOverwrites<TKey, TValue>(
this IDictionary<TKey, TValue> source,
IDictionary<TKey, TValue> overwrite,
string sourceFriendlyName = "source",
string overwriteFriendlyName = "overwrite")
where TKey : IEquatable<TKey>
{
public static IDictionary<TKey, TValue> ConcatWithOverwrites<TKey, TValue>(
this IDictionary<TKey, TValue> source,
IDictionary<TKey, TValue> overwrite,
string sourceFriendlyName = "source",
string overwriteFriendlyName = "overwrite")
where TKey : IEquatable<TKey>
if ((source == null || source?.Count == 0) && (overwrite == null || overwrite?.Count == 0))
{
if ((source == null || source?.Count == 0) && (overwrite == null || overwrite?.Count == 0))
{
PlatformServiceProvider.Instance.AdapterTraceLogger.LogInfo("DictionaryHelper.ConcatWithOverwrites: Both {0} and {1} dictionaries are null or empty, returning empty dictionary.", sourceFriendlyName, overwriteFriendlyName);
return new Dictionary<TKey, TValue>();
}
if (overwrite == null || overwrite?.Count == 0)
{
PlatformServiceProvider.Instance.AdapterTraceLogger.LogInfo("DictionaryHelper.ConcatWithOverwrites: The {0} is null or empty, returning the {1} dictionary.", overwriteFriendlyName, sourceFriendlyName);
return source.ToDictionary(p => p.Key, p => p.Value);
}
if (source == null || source?.Count == 0)
{
PlatformServiceProvider.Instance.AdapterTraceLogger.LogInfo("DictionaryHelper.ConcatWithOverwrites: The {0} is null or empty, returning the {1} dictionary.", sourceFriendlyName, overwriteFriendlyName);
return overwrite.ToDictionary(p => p.Key, p => p.Value);
}
PlatformServiceProvider.Instance.AdapterTraceLogger.LogInfo("DictionaryHelper.ConcatWithOverwrites: The {0} has {1} keys. And {2} has {3} keys. Merging them.", sourceFriendlyName, source.Count, overwriteFriendlyName, overwrite.Count);
var destination = source.ToDictionary(p => p.Key, p => p.Value);
PlatformServiceProvider.Instance.AdapterTraceLogger.LogInfo("DictionaryHelper.ConcatWithOverwrites: Taking all keys from {0}: {1}.", sourceFriendlyName, string.Join(", ", source.Keys));
var overwrites = new List<TKey>();
foreach (var k in overwrite.Keys)
{
if (destination.ContainsKey(k))
{
PlatformServiceProvider.Instance.AdapterTraceLogger.LogInfo("DictionaryHelper.ConcatWithOverwrites: The {0} already contains key {1}. Overwriting it with value from {2}.", sourceFriendlyName, k, overwriteFriendlyName);
destination[k] = overwrite[k];
overwrites.Add(k);
}
else
{
PlatformServiceProvider.Instance.AdapterTraceLogger.LogInfo("DictionaryHelper.ConcatWithOverwrites: The {0} does not contain key {1}. Adding it from {2}.", sourceFriendlyName, k, overwriteFriendlyName);
destination.Add(k, overwrite[k]);
}
}
PlatformServiceProvider.Instance.AdapterTraceLogger.LogInfo("DictionaryHelper.ConcatWithOverwrites: Merging done: Resulting dictionary has keys {0}, overwrites {1}.", string.Join(", ", destination.Keys), string.Join(", ", overwrites));
return destination;
PlatformServiceProvider.Instance.AdapterTraceLogger.LogInfo("DictionaryHelper.ConcatWithOverwrites: Both {0} and {1} dictionaries are null or empty, returning empty dictionary.", sourceFriendlyName, overwriteFriendlyName);
return new Dictionary<TKey, TValue>();
}
if (overwrite == null || overwrite?.Count == 0)
{
PlatformServiceProvider.Instance.AdapterTraceLogger.LogInfo("DictionaryHelper.ConcatWithOverwrites: The {0} is null or empty, returning the {1} dictionary.", overwriteFriendlyName, sourceFriendlyName);
return source.ToDictionary(p => p.Key, p => p.Value);
}
if (source == null || source?.Count == 0)
{
PlatformServiceProvider.Instance.AdapterTraceLogger.LogInfo("DictionaryHelper.ConcatWithOverwrites: The {0} is null or empty, returning the {1} dictionary.", sourceFriendlyName, overwriteFriendlyName);
return overwrite.ToDictionary(p => p.Key, p => p.Value);
}
PlatformServiceProvider.Instance.AdapterTraceLogger.LogInfo("DictionaryHelper.ConcatWithOverwrites: The {0} has {1} keys. And {2} has {3} keys. Merging them.", sourceFriendlyName, source.Count, overwriteFriendlyName, overwrite.Count);
var destination = source.ToDictionary(p => p.Key, p => p.Value);
PlatformServiceProvider.Instance.AdapterTraceLogger.LogInfo("DictionaryHelper.ConcatWithOverwrites: Taking all keys from {0}: {1}.", sourceFriendlyName, string.Join(", ", source.Keys));
var overwrites = new List<TKey>();
foreach (var k in overwrite.Keys)
{
if (destination.ContainsKey(k))
{
PlatformServiceProvider.Instance.AdapterTraceLogger.LogInfo("DictionaryHelper.ConcatWithOverwrites: The {0} already contains key {1}. Overwriting it with value from {2}.", sourceFriendlyName, k, overwriteFriendlyName);
destination[k] = overwrite[k];
overwrites.Add(k);
}
else
{
PlatformServiceProvider.Instance.AdapterTraceLogger.LogInfo("DictionaryHelper.ConcatWithOverwrites: The {0} does not contain key {1}. Adding it from {2}.", sourceFriendlyName, k, overwriteFriendlyName);
destination.Add(k, overwrite[k]);
}
}
PlatformServiceProvider.Instance.AdapterTraceLogger.LogInfo("DictionaryHelper.ConcatWithOverwrites: Merging done: Resulting dictionary has keys {0}, overwrites {1}.", string.Join(", ", destination.Keys), string.Join(", ", overwrites));
return destination;
}
}

Разница между файлами не показана из-за своего большого размера Загрузить разницу

Просмотреть файл

@ -1,103 +1,102 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
namespace Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.Helpers
namespace Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.Helpers;
using System;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Xml;
using Microsoft.VisualStudio.TestPlatform.ObjectModel.Utilities;
using TestPlatform.ObjectModel;
internal class RunSettingsUtilities
{
using System;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Xml;
using Microsoft.VisualStudio.TestPlatform.ObjectModel.Utilities;
using TestPlatform.ObjectModel;
internal class RunSettingsUtilities
/// <summary>
/// Gets the settings to be used while creating XmlReader for runsettings.
/// </summary>
internal static XmlReaderSettings ReaderSettings
{
/// <summary>
/// Gets the settings to be used while creating XmlReader for runsettings.
/// </summary>
internal static XmlReaderSettings ReaderSettings
get
{
get
{
var settings = new XmlReaderSettings();
settings.IgnoreComments = true;
settings.IgnoreWhitespace = true;
return settings;
}
}
/// <summary>
/// Gets the set of user defined test run parameters from settings xml as key value pairs.
/// </summary>
/// <param name="settingsXml">The runsettings xml.</param>
/// <returns>The test run parameters.</returns>
/// <remarks>If there is no test run parameters section defined in the settingsxml a blank dictionary is returned.</remarks>
internal static Dictionary<string, object> GetTestRunParameters(string settingsXml)
{
var nodeValue = GetNodeValue(settingsXml, TestAdapter.Constants.TestRunParametersName, TestRunParameters.FromXml);
if (nodeValue == default(Dictionary<string, object>))
{
// Return default.
nodeValue = new Dictionary<string, object>();
}
return nodeValue;
}
/// <summary>
/// Throws if the node has an attribute.
/// </summary>
/// <param name="reader"> The reader. </param>
/// <exception cref="SettingsException"> Thrown if the node has an attribute. </exception>
internal static void ThrowOnHasAttributes(XmlReader reader)
{
if (reader.HasAttributes)
{
reader.MoveToNextAttribute();
throw new SettingsException(
string.Format(
CultureInfo.CurrentCulture,
Resource.InvalidSettingsXmlAttribute,
TestPlatform.ObjectModel.Constants.RunConfigurationSettingsName,
reader.Name));
}
}
private static T GetNodeValue<T>(string settingsXml, string nodeName, Func<XmlReader, T> nodeParser)
{
if (string.IsNullOrWhiteSpace(settingsXml))
{
return default;
}
// use XmlReader to avoid loading of the plugins in client code (mainly from VS).
using (StringReader stringReader = new(settingsXml))
{
XmlReader reader = XmlReader.Create(stringReader, ReaderSettings);
// read to the fist child
XmlReaderUtilities.ReadToRootNode(reader);
reader.ReadToNextElement();
// Read till we reach nodeName element or reach EOF
while (!string.Equals(reader.Name, nodeName, StringComparison.OrdinalIgnoreCase)
&&
!reader.EOF)
{
reader.SkipToNextElement();
}
if (!reader.EOF)
{
// read nodeName element.
return nodeParser(reader);
}
}
return default;
var settings = new XmlReaderSettings();
settings.IgnoreComments = true;
settings.IgnoreWhitespace = true;
return settings;
}
}
/// <summary>
/// Gets the set of user defined test run parameters from settings xml as key value pairs.
/// </summary>
/// <param name="settingsXml">The runsettings xml.</param>
/// <returns>The test run parameters.</returns>
/// <remarks>If there is no test run parameters section defined in the settingsxml a blank dictionary is returned.</remarks>
internal static Dictionary<string, object> GetTestRunParameters(string settingsXml)
{
var nodeValue = GetNodeValue(settingsXml, TestAdapter.Constants.TestRunParametersName, TestRunParameters.FromXml);
if (nodeValue == default(Dictionary<string, object>))
{
// Return default.
nodeValue = new Dictionary<string, object>();
}
return nodeValue;
}
/// <summary>
/// Throws if the node has an attribute.
/// </summary>
/// <param name="reader"> The reader. </param>
/// <exception cref="SettingsException"> Thrown if the node has an attribute. </exception>
internal static void ThrowOnHasAttributes(XmlReader reader)
{
if (reader.HasAttributes)
{
reader.MoveToNextAttribute();
throw new SettingsException(
string.Format(
CultureInfo.CurrentCulture,
Resource.InvalidSettingsXmlAttribute,
TestPlatform.ObjectModel.Constants.RunConfigurationSettingsName,
reader.Name));
}
}
private static T GetNodeValue<T>(string settingsXml, string nodeName, Func<XmlReader, T> nodeParser)
{
if (string.IsNullOrWhiteSpace(settingsXml))
{
return default;
}
// use XmlReader to avoid loading of the plugins in client code (mainly from VS).
using (StringReader stringReader = new(settingsXml))
{
XmlReader reader = XmlReader.Create(stringReader, ReaderSettings);
// read to the fist child
XmlReaderUtilities.ReadToRootNode(reader);
reader.ReadToNextElement();
// Read till we reach nodeName element or reach EOF
while (!string.Equals(reader.Name, nodeName, StringComparison.OrdinalIgnoreCase)
&&
!reader.EOF)
{
reader.SkipToNextElement();
}
if (!reader.EOF)
{
// read nodeName element.
return nodeParser(reader);
}
}
return default;
}
}

Просмотреть файл

@ -1,67 +1,66 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
namespace Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.Helpers
namespace Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.Helpers;
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Xml;
using Microsoft.VisualStudio.TestPlatform.ObjectModel;
internal static class TestRunParameters
{
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Xml;
using Microsoft.VisualStudio.TestPlatform.ObjectModel;
internal static class TestRunParameters
internal static Dictionary<string, object> FromXml(XmlReader reader)
{
internal static Dictionary<string, object> FromXml(XmlReader reader)
var testParameters = new Dictionary<string, object>();
if (!reader.IsEmptyElement)
{
var testParameters = new Dictionary<string, object>();
RunSettingsUtilities.ThrowOnHasAttributes(reader);
reader.Read();
if (!reader.IsEmptyElement)
while (reader.NodeType == XmlNodeType.Element)
{
RunSettingsUtilities.ThrowOnHasAttributes(reader);
reader.Read();
while (reader.NodeType == XmlNodeType.Element)
var elementName = reader.Name;
switch (elementName)
{
var elementName = reader.Name;
switch (elementName)
{
case "Parameter":
string paramName = null;
string paramValue = null;
for (var attIndex = 0; attIndex < reader.AttributeCount; attIndex++)
case "Parameter":
string paramName = null;
string paramValue = null;
for (var attIndex = 0; attIndex < reader.AttributeCount; attIndex++)
{
reader.MoveToAttribute(attIndex);
if (string.Equals(reader.Name, "Name", StringComparison.OrdinalIgnoreCase))
{
reader.MoveToAttribute(attIndex);
if (string.Equals(reader.Name, "Name", StringComparison.OrdinalIgnoreCase))
{
paramName = reader.Value;
}
else if (string.Equals(reader.Name, "Value", StringComparison.OrdinalIgnoreCase))
{
paramValue = reader.Value;
}
paramName = reader.Value;
}
if (paramName != null && paramValue != null)
else if (string.Equals(reader.Name, "Value", StringComparison.OrdinalIgnoreCase))
{
testParameters[paramName] = paramValue;
paramValue = reader.Value;
}
}
break;
default:
throw new SettingsException(
string.Format(
CultureInfo.CurrentCulture,
Resource.InvalidSettingsXmlElement,
TestAdapter.Constants.TestRunParametersName,
reader.Name));
}
if (paramName != null && paramValue != null)
{
testParameters[paramName] = paramValue;
}
reader.Read();
break;
default:
throw new SettingsException(
string.Format(
CultureInfo.CurrentCulture,
Resource.InvalidSettingsXmlElement,
TestAdapter.Constants.TestRunParametersName,
reader.Name));
}
}
return testParameters;
reader.Read();
}
}
return testParameters;
}
}

Просмотреть файл

@ -1,62 +1,61 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
namespace Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.Helpers
namespace Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.Helpers;
using Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.ObjectModel;
using Microsoft.VisualStudio.TestPlatform.ObjectModel;
using UTF = Microsoft.VisualStudio.TestTools.UnitTesting;
internal static class UnitTestOutcomeHelper
{
using Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.ObjectModel;
using Microsoft.VisualStudio.TestPlatform.ObjectModel;
using UTF = Microsoft.VisualStudio.TestTools.UnitTesting;
internal static class UnitTestOutcomeHelper
/// <summary>
/// Converts the parameter unitTestOutcome to testOutcome
/// </summary>
/// <param name="unitTestOutcome"> The unit Test Outcome. </param>
/// <param name="currentSettings">Current MSTest settings</param>
/// <returns>The Test platforms outcome.</returns>
internal static TestOutcome ToTestOutcome(UnitTestOutcome unitTestOutcome, MSTestSettings currentSettings)
{
/// <summary>
/// Converts the parameter unitTestOutcome to testOutcome
/// </summary>
/// <param name="unitTestOutcome"> The unit Test Outcome. </param>
/// <param name="currentSettings">Current MSTest settings</param>
/// <returns>The Test platforms outcome.</returns>
internal static TestOutcome ToTestOutcome(UnitTestOutcome unitTestOutcome, MSTestSettings currentSettings)
switch (unitTestOutcome)
{
switch (unitTestOutcome)
{
case UnitTestOutcome.Passed:
return TestOutcome.Passed;
case UnitTestOutcome.Passed:
return TestOutcome.Passed;
case UnitTestOutcome.Failed:
case UnitTestOutcome.Error:
case UnitTestOutcome.Timeout:
return TestOutcome.Failed;
case UnitTestOutcome.Failed:
case UnitTestOutcome.Error:
case UnitTestOutcome.Timeout:
return TestOutcome.Failed;
case UnitTestOutcome.NotRunnable:
case UnitTestOutcome.NotRunnable:
{
if (currentSettings.MapNotRunnableToFailed)
{
if (currentSettings.MapNotRunnableToFailed)
{
return TestOutcome.Failed;
}
return TestOutcome.None;
return TestOutcome.Failed;
}
case UnitTestOutcome.Ignored:
return TestOutcome.Skipped;
case UnitTestOutcome.Inconclusive:
{
if (currentSettings.MapInconclusiveToFailed)
{
return TestOutcome.Failed;
}
return TestOutcome.Skipped;
}
case UnitTestOutcome.NotFound:
return TestOutcome.NotFound;
default:
return TestOutcome.None;
}
}
case UnitTestOutcome.Ignored:
return TestOutcome.Skipped;
case UnitTestOutcome.Inconclusive:
{
if (currentSettings.MapInconclusiveToFailed)
{
return TestOutcome.Failed;
}
return TestOutcome.Skipped;
}
case UnitTestOutcome.NotFound:
return TestOutcome.NotFound;
default:
return TestOutcome.None;
}
}
}

Просмотреть файл

@ -1,124 +1,123 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
namespace Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter
{
using System.Collections.Generic;
using System.IO;
namespace Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter;
using Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.Interface;
using Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.Interface.ObjectModel;
using System.Collections.Generic;
using System.IO;
using Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.Interface;
using Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.Interface.ObjectModel;
/// <summary>
/// The definition of a PlatformServiceProvider with a hook to all the services.
/// </summary>
internal interface IPlatformServiceProvider
{
/// <summary>
/// Gets an instance to the platform service validator for test sources.
/// </summary>
ITestSource TestSource { get; }
/// <summary>
/// The definition of a PlatformServiceProvider with a hook to all the services.
/// Gets an instance to the platform service to data drive a test.
/// </summary>
internal interface IPlatformServiceProvider
{
/// <summary>
/// Gets an instance to the platform service validator for test sources.
/// </summary>
ITestSource TestSource { get; }
ITestDataSource TestDataSource { get; }
/// <summary>
/// Gets an instance to the platform service to data drive a test.
/// </summary>
ITestDataSource TestDataSource { get; }
/// <summary>
/// Gets an instance to the platform service for file operations.
/// </summary>
IFileOperations FileOperations { get; }
/// <summary>
/// Gets an instance to the platform service for file operations.
/// </summary>
IFileOperations FileOperations { get; }
/// <summary>
/// Gets an instance to the platform service for trace logging.
/// </summary>
IAdapterTraceLogger AdapterTraceLogger { get; }
/// <summary>
/// Gets an instance to the platform service for trace logging.
/// </summary>
IAdapterTraceLogger AdapterTraceLogger { get; }
/// <summary>
/// Gets an instance of the test deployment service.
/// </summary>
ITestDeployment TestDeployment { get; }
/// <summary>
/// Gets an instance of the test deployment service.
/// </summary>
ITestDeployment TestDeployment { get; }
/// <summary>
/// Gets an instance to the platform service for a Settings Provider.
/// </summary>
ISettingsProvider SettingsProvider { get; }
/// <summary>
/// Gets an instance to the platform service for a Settings Provider.
/// </summary>
ISettingsProvider SettingsProvider { get; }
/// <summary>
/// Gets an instance to the platform service for thread operations.
/// </summary>
IThreadOperations ThreadOperations { get; }
/// <summary>
/// Gets an instance to the platform service for thread operations.
/// </summary>
IThreadOperations ThreadOperations { get; }
/// <summary>
/// Gets an instance to the platform service for reflection operations specific to a platform.
/// </summary>
IReflectionOperations ReflectionOperations { get; }
/// <summary>
/// Gets an instance to the platform service for reflection operations specific to a platform.
/// </summary>
IReflectionOperations ReflectionOperations { get; }
/// <summary>
/// Creates an instance to the platform service for a test source host.
/// </summary>
/// <param name="source">
/// The source.
/// </param>
/// <param name="runSettings">
/// The run Settings for the session.
/// </param>
/// <param name="frameworkHandle">
/// The handle to the test platform.
/// </param>
/// <returns>
/// Returns the host for the source provided.
/// </returns>
ITestSourceHost CreateTestSourceHost(
string source,
TestPlatform.ObjectModel.Adapter.IRunSettings runSettings,
TestPlatform.ObjectModel.Adapter.IFrameworkHandle frameworkHandle);
/// <summary>
/// Creates an instance to the platform service for a test source host.
/// </summary>
/// <param name="source">
/// The source.
/// </param>
/// <param name="runSettings">
/// The run Settings for the session.
/// </param>
/// <param name="frameworkHandle">
/// The handle to the test platform.
/// </param>
/// <returns>
/// Returns the host for the source provided.
/// </returns>
ITestSourceHost CreateTestSourceHost(
string source,
TestPlatform.ObjectModel.Adapter.IRunSettings runSettings,
TestPlatform.ObjectModel.Adapter.IFrameworkHandle frameworkHandle);
/// <summary>
/// Gets an instance to the platform service listener who monitors trace and debug output
/// on provided text writer.
/// </summary>
/// <param name="textWriter">
/// The text Writer.
/// </param>
/// <returns>
/// The <see cref="ITraceListener"/>.
/// </returns>
ITraceListener GetTraceListener(TextWriter textWriter);
/// <summary>
/// Gets an instance to the platform service listener who monitors trace and debug output
/// on provided text writer.
/// </summary>
/// <param name="textWriter">
/// The text Writer.
/// </param>
/// <returns>
/// The <see cref="ITraceListener"/>.
/// </returns>
ITraceListener GetTraceListener(TextWriter textWriter);
/// <summary>
/// Gets an instance to the platform service trace-listener manager which updates the output/error streams
/// with redirected streams and performs operations on the listener provided as argument.
/// </summary>
/// <param name="outputWriter">
/// The redirected output stream writer.
/// </param>
/// <param name="errorWriter">
/// The redirected error stream writer.
/// </param>
/// <returns>
/// The manager for trace listeners.
/// </returns>
ITraceListenerManager GetTraceListenerManager(TextWriter outputWriter, TextWriter errorWriter);
/// <summary>
/// Gets an instance to the platform service trace-listener manager which updates the output/error streams
/// with redirected streams and performs operations on the listener provided as argument.
/// </summary>
/// <param name="outputWriter">
/// The redirected output stream writer.
/// </param>
/// <param name="errorWriter">
/// The redirected error stream writer.
/// </param>
/// <returns>
/// The manager for trace listeners.
/// </returns>
ITraceListenerManager GetTraceListenerManager(TextWriter outputWriter, TextWriter errorWriter);
/// <summary>
/// Gets the TestContext object for a platform.
/// </summary>
/// <param name="testMethod">
/// The test method.
/// </param>
/// <param name="writer">
/// The writer instance for logging.
/// </param>
/// <param name="properties">
/// The default set of properties the test context needs to be filled with.
/// </param>
/// <returns>
/// The <see cref="ITestContext"/> instance.
/// </returns>
/// <remarks>
/// This was required for compatibility reasons since the TestContext object that the V1 adapter had for desktop is not .Net Core compliant.
/// </remarks>
ITestContext GetTestContext(ITestMethod testMethod, StringWriter writer, IDictionary<string, object> properties);
}
/// <summary>
/// Gets the TestContext object for a platform.
/// </summary>
/// <param name="testMethod">
/// The test method.
/// </param>
/// <param name="writer">
/// The writer instance for logging.
/// </param>
/// <param name="properties">
/// The default set of properties the test context needs to be filled with.
/// </param>
/// <returns>
/// The <see cref="ITestContext"/> instance.
/// </returns>
/// <remarks>
/// This was required for compatibility reasons since the TestContext object that the V1 adapter had for desktop is not .Net Core compliant.
/// </remarks>
ITestContext GetTestContext(ITestMethod testMethod, StringWriter writer, IDictionary<string, object> properties);
}

Просмотреть файл

@ -4,7 +4,6 @@
<PropertyGroup>
<TargetFrameworks>netstandard2.0;$(NetCoreAppCurrent);$(NetCoreAppMinimum);$(NetFrameworkMinimum)</TargetFrameworks>
<TreatWarningsAsErrors>True</TreatWarningsAsErrors>
<LangVersion>Latest</LangVersion>
</PropertyGroup>
<PropertyGroup>
@ -63,4 +62,4 @@
<Output TaskParameter="DestinationFiles" ItemName="FileWrites" />
</Copy>
</Target>
</Project>
</Project>

Просмотреть файл

@ -1,86 +1,85 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
namespace Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter
namespace Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.ObjectModel;
using Microsoft.VisualStudio.TestPlatform.ObjectModel;
using Microsoft.VisualStudio.TestPlatform.ObjectModel.Adapter;
using Microsoft.VisualStudio.TestPlatform.ObjectModel.Logging;
/// <summary>
/// Contains the discovery logic for this adapter.
/// </summary>
[DefaultExecutorUri(TestAdapter.Constants.ExecutorUriString)]
[FileExtension(".xap")]
[FileExtension(".appx")]
[FileExtension(".dll")]
[FileExtension(".exe")]
public class MSTestDiscoverer : ITestDiscoverer
{
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.ObjectModel;
using Microsoft.VisualStudio.TestPlatform.ObjectModel;
using Microsoft.VisualStudio.TestPlatform.ObjectModel.Adapter;
using Microsoft.VisualStudio.TestPlatform.ObjectModel.Logging;
/// <summary>
/// Discovers the tests available from the provided source. Not supported for .xap source.
/// </summary>
/// <param name="sources">Collection of test containers.</param>
/// <param name="discoveryContext">Context in which discovery is being performed.</param>
/// <param name="logger">Logger used to log messages.</param>
/// <param name="discoverySink">Used to send testcases and discovery related events back to Discoverer manager.</param>
[System.Security.SecurityCritical]
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1062:Validate arguments of public methods", MessageId = "0", Justification ="Discovery context can be null.")]
public void DiscoverTests(
IEnumerable<string> sources,
IDiscoveryContext discoveryContext,
IMessageLogger logger,
ITestCaseDiscoverySink discoverySink)
{
ValidateArg.NotNull(sources, "sources");
ValidateArg.NotNull(logger, "logger");
ValidateArg.NotNull(discoverySink, "discoverySink");
if (!this.AreValidSources(sources))
{
throw new NotSupportedException(Resource.SourcesNotSupported);
}
// Populate the runsettings.
try
{
MSTestSettings.PopulateSettings(discoveryContext);
}
catch (AdapterSettingsException ex)
{
logger.SendMessage(TestMessageLevel.Error, ex.Message);
return;
}
// Scenarios that include testsettings or forcing a run via the legacy adapter are currently not supported in MSTestAdapter.
if (MSTestSettings.IsLegacyScenario(logger))
{
return;
}
new UnitTestDiscoverer().DiscoverTests(sources, logger, discoverySink, discoveryContext);
}
/// <summary>
/// Contains the discovery logic for this adapter.
/// Verifies if the sources are valid for the target platform.
/// </summary>
[DefaultExecutorUri(TestAdapter.Constants.ExecutorUriString)]
[FileExtension(".xap")]
[FileExtension(".appx")]
[FileExtension(".dll")]
[FileExtension(".exe")]
public class MSTestDiscoverer : ITestDiscoverer
/// <param name="sources">The test sources</param>
/// <remarks>Sources cannot be null.</remarks>
/// <returns>True if the source has a valid extension for the current platform.</returns>
internal bool AreValidSources(IEnumerable<string> sources)
{
/// <summary>
/// Discovers the tests available from the provided source. Not supported for .xap source.
/// </summary>
/// <param name="sources">Collection of test containers.</param>
/// <param name="discoveryContext">Context in which discovery is being performed.</param>
/// <param name="logger">Logger used to log messages.</param>
/// <param name="discoverySink">Used to send testcases and discovery related events back to Discoverer manager.</param>
[System.Security.SecurityCritical]
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1062:Validate arguments of public methods", MessageId = "0", Justification ="Discovery context can be null.")]
public void DiscoverTests(
IEnumerable<string> sources,
IDiscoveryContext discoveryContext,
IMessageLogger logger,
ITestCaseDiscoverySink discoverySink)
{
ValidateArg.NotNull(sources, "sources");
ValidateArg.NotNull(logger, "logger");
ValidateArg.NotNull(discoverySink, "discoverySink");
if (!this.AreValidSources(sources))
{
throw new NotSupportedException(Resource.SourcesNotSupported);
}
// Populate the runsettings.
try
{
MSTestSettings.PopulateSettings(discoveryContext);
}
catch (AdapterSettingsException ex)
{
logger.SendMessage(TestMessageLevel.Error, ex.Message);
return;
}
// Scenarios that include testsettings or forcing a run via the legacy adapter are currently not supported in MSTestAdapter.
if (MSTestSettings.IsLegacyScenario(logger))
{
return;
}
new UnitTestDiscoverer().DiscoverTests(sources, logger, discoverySink, discoveryContext);
}
/// <summary>
/// Verifies if the sources are valid for the target platform.
/// </summary>
/// <param name="sources">The test sources</param>
/// <remarks>Sources cannot be null.</remarks>
/// <returns>True if the source has a valid extension for the current platform.</returns>
internal bool AreValidSources(IEnumerable<string> sources)
{
// ValidSourceExtensions is always expected to return a non-null list.
return
sources.Any(
source =>
PlatformServiceProvider.Instance.TestSource.ValidSourceExtensions.Any(
extension =>
string.Equals(Path.GetExtension(source), extension, StringComparison.OrdinalIgnoreCase)));
}
// ValidSourceExtensions is always expected to return a non-null list.
return
sources.Any(
source =>
PlatformServiceProvider.Instance.TestSource.ValidSourceExtensions.Any(
extension =>
string.Equals(Path.GetExtension(source), extension, StringComparison.OrdinalIgnoreCase)));
}
}

Просмотреть файл

@ -1,119 +1,118 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
namespace Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter
namespace Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter;
using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.Execution;
using Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.ObjectModel;
using Microsoft.VisualStudio.TestPlatform.ObjectModel;
using Microsoft.VisualStudio.TestPlatform.ObjectModel.Adapter;
using Microsoft.VisualStudio.TestPlatform.ObjectModel.Logging;
/// <summary>
/// Contains the execution logic for this adapter.
/// </summary>
[ExtensionUri(TestAdapter.Constants.ExecutorUriString)]
public class MSTestExecutor : ITestExecutor
{
using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.Execution;
using Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.ObjectModel;
using Microsoft.VisualStudio.TestPlatform.ObjectModel;
using Microsoft.VisualStudio.TestPlatform.ObjectModel.Adapter;
using Microsoft.VisualStudio.TestPlatform.ObjectModel.Logging;
/// <summary>
/// Token for canceling the test run.
/// </summary>
private TestRunCancellationToken cancellationToken = null;
/// <summary>
/// Contains the execution logic for this adapter.
/// Initializes a new instance of the <see cref="MSTestExecutor"/> class.
/// </summary>
[ExtensionUri(TestAdapter.Constants.ExecutorUriString)]
public class MSTestExecutor : ITestExecutor
public MSTestExecutor()
{
/// <summary>
/// Token for canceling the test run.
/// </summary>
private TestRunCancellationToken cancellationToken = null;
this.TestExecutionManager = new TestExecutionManager();
this.MSTestDiscoverer = new MSTestDiscoverer();
}
/// <summary>
/// Initializes a new instance of the <see cref="MSTestExecutor"/> class.
/// </summary>
public MSTestExecutor()
/// <summary>
/// Gets or sets the ms test execution manager.
/// </summary>
public TestExecutionManager TestExecutionManager { get; protected set; }
/// <summary>
/// Gets discoverer used for validating the sources.
/// </summary>
private MSTestDiscoverer MSTestDiscoverer { get; }
public void RunTests(IEnumerable<TestCase> tests, IRunContext runContext, IFrameworkHandle frameworkHandle)
{
PlatformServiceProvider.Instance.AdapterTraceLogger.LogInfo("MSTestExecutor.RunTests: Running tests from testcases.");
ValidateArg.NotNull(frameworkHandle, "frameworkHandle");
ValidateArg.NotNullOrEmpty(tests, "tests");
if (!this.MSTestDiscoverer.AreValidSources(from test in tests select test.Source))
{
this.TestExecutionManager = new TestExecutionManager();
this.MSTestDiscoverer = new MSTestDiscoverer();
throw new NotSupportedException();
}
/// <summary>
/// Gets or sets the ms test execution manager.
/// </summary>
public TestExecutionManager TestExecutionManager { get; protected set; }
/// <summary>
/// Gets discoverer used for validating the sources.
/// </summary>
private MSTestDiscoverer MSTestDiscoverer { get; }
public void RunTests(IEnumerable<TestCase> tests, IRunContext runContext, IFrameworkHandle frameworkHandle)
// Populate the runsettings.
try
{
PlatformServiceProvider.Instance.AdapterTraceLogger.LogInfo("MSTestExecutor.RunTests: Running tests from testcases.");
ValidateArg.NotNull(frameworkHandle, "frameworkHandle");
ValidateArg.NotNullOrEmpty(tests, "tests");
if (!this.MSTestDiscoverer.AreValidSources(from test in tests select test.Source))
{
throw new NotSupportedException();
}
// Populate the runsettings.
try
{
MSTestSettings.PopulateSettings(runContext);
}
catch (AdapterSettingsException ex)
{
frameworkHandle.SendMessage(TestMessageLevel.Error, ex.Message);
return;
}
// Scenarios that include testsettings or forcing a run via the legacy adapter are currently not supported in MSTestAdapter.
if (MSTestSettings.IsLegacyScenario(frameworkHandle))
{
return;
}
this.cancellationToken = new TestRunCancellationToken();
this.TestExecutionManager.RunTests(tests, runContext, frameworkHandle, this.cancellationToken);
this.cancellationToken = null;
MSTestSettings.PopulateSettings(runContext);
}
catch (AdapterSettingsException ex)
{
frameworkHandle.SendMessage(TestMessageLevel.Error, ex.Message);
return;
}
public void RunTests(IEnumerable<string> sources, IRunContext runContext, IFrameworkHandle frameworkHandle)
// Scenarios that include testsettings or forcing a run via the legacy adapter are currently not supported in MSTestAdapter.
if (MSTestSettings.IsLegacyScenario(frameworkHandle))
{
PlatformServiceProvider.Instance.AdapterTraceLogger.LogInfo("MSTestExecutor.RunTests: Running tests from sources.");
ValidateArg.NotNull(frameworkHandle, "frameworkHandle");
ValidateArg.NotNullOrEmpty(sources, "sources");
if (!this.MSTestDiscoverer.AreValidSources(sources))
{
throw new NotSupportedException();
}
// Populate the runsettings.
try
{
MSTestSettings.PopulateSettings(runContext);
}
catch (AdapterSettingsException ex)
{
frameworkHandle.SendMessage(TestMessageLevel.Error, ex.Message);
return;
}
// Scenarios that include testsettings or forcing a run via the legacy adapter are currently not supported in MSTestAdapter.
if (MSTestSettings.IsLegacyScenario(frameworkHandle))
{
return;
}
sources = PlatformServiceProvider.Instance.TestSource.GetTestSources(sources);
this.cancellationToken = new TestRunCancellationToken();
this.TestExecutionManager.RunTests(sources, runContext, frameworkHandle, this.cancellationToken);
this.cancellationToken = null;
return;
}
public void Cancel()
this.cancellationToken = new TestRunCancellationToken();
this.TestExecutionManager.RunTests(tests, runContext, frameworkHandle, this.cancellationToken);
this.cancellationToken = null;
}
public void RunTests(IEnumerable<string> sources, IRunContext runContext, IFrameworkHandle frameworkHandle)
{
PlatformServiceProvider.Instance.AdapterTraceLogger.LogInfo("MSTestExecutor.RunTests: Running tests from sources.");
ValidateArg.NotNull(frameworkHandle, "frameworkHandle");
ValidateArg.NotNullOrEmpty(sources, "sources");
if (!this.MSTestDiscoverer.AreValidSources(sources))
{
this.cancellationToken?.Cancel();
throw new NotSupportedException();
}
// Populate the runsettings.
try
{
MSTestSettings.PopulateSettings(runContext);
}
catch (AdapterSettingsException ex)
{
frameworkHandle.SendMessage(TestMessageLevel.Error, ex.Message);
return;
}
// Scenarios that include testsettings or forcing a run via the legacy adapter are currently not supported in MSTestAdapter.
if (MSTestSettings.IsLegacyScenario(frameworkHandle))
{
return;
}
sources = PlatformServiceProvider.Instance.TestSource.GetTestSources(sources);
this.cancellationToken = new TestRunCancellationToken();
this.TestExecutionManager.RunTests(sources, runContext, frameworkHandle, this.cancellationToken);
this.cancellationToken = null;
}
public void Cancel()
{
this.cancellationToken?.Cancel();
}
}

Разница между файлами не показана из-за своего большого размера Загрузить разницу

Просмотреть файл

@ -1,15 +1,14 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
namespace Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.ObjectModel
{
using System;
namespace Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.ObjectModel;
internal class AdapterSettingsException : Exception
using System;
internal class AdapterSettingsException : Exception
{
internal AdapterSettingsException(string message)
: base(message)
{
internal AdapterSettingsException(string message)
: base(message)
{
}
}
}

Просмотреть файл

@ -1,12 +1,11 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
namespace Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.ObjectModel
namespace Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.ObjectModel;
internal enum DynamicDataType : int
{
internal enum DynamicDataType : int
{
None = 0,
DataSourceAttribute = 1,
ITestDataSource = 2
}
None = 0,
DataSourceAttribute = 1,
ITestDataSource = 2
}

Просмотреть файл

@ -1,49 +1,48 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
namespace Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.ObjectModel
namespace Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.ObjectModel;
using System;
using System.Diagnostics;
[Serializable]
internal class StackTraceInformation
{
using System;
using System.Diagnostics;
[Serializable]
internal class StackTraceInformation
public StackTraceInformation(string stackTrace)
: this(stackTrace, null, 0, 0)
{
public StackTraceInformation(string stackTrace)
: this(stackTrace, null, 0, 0)
{
}
public StackTraceInformation(string stackTrace, string filePath, int lineNumber, int columnNumber)
{
Debug.Assert(!string.IsNullOrEmpty(stackTrace), "StackTrace message should not be empty");
Debug.Assert(lineNumber >= 0, "Line number should be greater than or equal to 0");
Debug.Assert(columnNumber >= 0, "Column number should be greater than or equal to 0");
this.ErrorStackTrace = stackTrace;
this.ErrorFilePath = filePath;
this.ErrorLineNumber = lineNumber;
this.ErrorColumnNumber = columnNumber;
}
/// <summary>
/// Gets stack Trace associated with the test failure
/// </summary>
public string ErrorStackTrace { get; private set; }
/// <summary>
/// Gets source code FilePath where the error occurred
/// </summary>
public string ErrorFilePath { get; private set; }
/// <summary>
/// Gets line number in the source code file where the error occurred.
/// </summary>
public int ErrorLineNumber { get; private set; }
/// <summary>
/// Gets column number in the source code file where the error occurred.
/// </summary>
public int ErrorColumnNumber { get; private set; }
}
public StackTraceInformation(string stackTrace, string filePath, int lineNumber, int columnNumber)
{
Debug.Assert(!string.IsNullOrEmpty(stackTrace), "StackTrace message should not be empty");
Debug.Assert(lineNumber >= 0, "Line number should be greater than or equal to 0");
Debug.Assert(columnNumber >= 0, "Column number should be greater than or equal to 0");
this.ErrorStackTrace = stackTrace;
this.ErrorFilePath = filePath;
this.ErrorLineNumber = lineNumber;
this.ErrorColumnNumber = columnNumber;
}
/// <summary>
/// Gets stack Trace associated with the test failure
/// </summary>
public string ErrorStackTrace { get; private set; }
/// <summary>
/// Gets source code FilePath where the error occurred
/// </summary>
public string ErrorFilePath { get; private set; }
/// <summary>
/// Gets line number in the source code file where the error occurred.
/// </summary>
public int ErrorLineNumber { get; private set; }
/// <summary>
/// Gets column number in the source code file where the error occurred.
/// </summary>
public int ErrorColumnNumber { get; private set; }
}

Просмотреть файл

@ -1,37 +1,36 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
namespace Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.ObjectModel
namespace Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.ObjectModel;
using System;
using Microsoft.VisualStudio.TestTools.UnitTesting;
[Serializable]
internal class TestAssemblySettings
{
using System;
using Microsoft.VisualStudio.TestTools.UnitTesting;
[Serializable]
internal class TestAssemblySettings
public TestAssemblySettings()
{
public TestAssemblySettings()
{
this.Workers = -1;
}
/// <summary>
/// Gets or sets the parallelization level.
/// </summary>
internal int Workers { get; set; }
/// <summary>
/// Gets or sets the mode of parallelization.
/// </summary>
internal ExecutionScope Scope { get; set; }
/// <summary>
/// Gets or sets a value indicating whether the assembly can be parallelized.
/// </summary>
internal bool CanParallelizeAssembly { get; set; }
/// <summary>
/// Gets or sets the class cleanup lifecycle timing.
/// </summary>
internal ClassCleanupBehavior ClassCleanupLifecycle { get; set; }
this.Workers = -1;
}
/// <summary>
/// Gets or sets the parallelization level.
/// </summary>
internal int Workers { get; set; }
/// <summary>
/// Gets or sets the mode of parallelization.
/// </summary>
internal ExecutionScope Scope { get; set; }
/// <summary>
/// Gets or sets a value indicating whether the assembly can be parallelized.
/// </summary>
internal bool CanParallelizeAssembly { get; set; }
/// <summary>
/// Gets or sets the class cleanup lifecycle timing.
/// </summary>
internal ClassCleanupBehavior ClassCleanupLifecycle { get; set; }
}

Просмотреть файл

@ -1,49 +1,48 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
namespace Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.ObjectModel
namespace Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.ObjectModel;
using System;
using System.Diagnostics;
/// <summary>
/// Internal class to indicate Test Execution failure
/// </summary>
[Serializable]
internal class TestFailedException : Exception
{
using System;
using System.Diagnostics;
public TestFailedException(UnitTestOutcome outcome, string errorMessage)
: this(outcome, errorMessage, null, null)
{
}
public TestFailedException(UnitTestOutcome outcome, string errorMessage, StackTraceInformation stackTraceInformation)
: this(outcome, errorMessage, stackTraceInformation, null)
{
}
public TestFailedException(UnitTestOutcome outcome, string errorMessage, Exception realException)
: this(outcome, errorMessage, null, realException)
{
}
public TestFailedException(UnitTestOutcome outcome, string errorMessage, StackTraceInformation stackTraceInformation, Exception realException)
: base(errorMessage, realException)
{
Debug.Assert(!string.IsNullOrEmpty(errorMessage), "ErrorMessage should not be empty");
this.Outcome = outcome;
this.StackTraceInformation = stackTraceInformation;
}
/// <summary>
/// Internal class to indicate Test Execution failure
/// Gets stack trace information associated with the test failure
/// </summary>
[Serializable]
internal class TestFailedException : Exception
{
public TestFailedException(UnitTestOutcome outcome, string errorMessage)
: this(outcome, errorMessage, null, null)
{
}
public StackTraceInformation StackTraceInformation { get; private set; }
public TestFailedException(UnitTestOutcome outcome, string errorMessage, StackTraceInformation stackTraceInformation)
: this(outcome, errorMessage, stackTraceInformation, null)
{
}
public TestFailedException(UnitTestOutcome outcome, string errorMessage, Exception realException)
: this(outcome, errorMessage, null, realException)
{
}
public TestFailedException(UnitTestOutcome outcome, string errorMessage, StackTraceInformation stackTraceInformation, Exception realException)
: base(errorMessage, realException)
{
Debug.Assert(!string.IsNullOrEmpty(errorMessage), "ErrorMessage should not be empty");
this.Outcome = outcome;
this.StackTraceInformation = stackTraceInformation;
}
/// <summary>
/// Gets stack trace information associated with the test failure
/// </summary>
public StackTraceInformation StackTraceInformation { get; private set; }
/// <summary>
/// Gets outcome of the test case
/// </summary>
public UnitTestOutcome Outcome { get; private set; }
}
/// <summary>
/// Gets outcome of the test case
/// </summary>
public UnitTestOutcome Outcome { get; private set; }
}

Просмотреть файл

@ -1,167 +1,166 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
namespace Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.ObjectModel
namespace Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.ObjectModel;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Diagnostics;
using System.Reflection;
using Microsoft.TestPlatform.AdapterUtilities;
using Microsoft.TestPlatform.AdapterUtilities.ManagedNameUtilities;
using MSTestAdapter.PlatformServices.Interface.ObjectModel;
/// <summary>
/// TestMethod contains information about a unit test method that needs to be executed
/// </summary>
[Serializable]
public sealed class TestMethod : ITestMethod
{
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Diagnostics;
using System.Reflection;
/// <summary>
/// Number of elements in <see cref="Hierarchy"/>.
/// </summary>
public const int TotalHierarchyLevels = HierarchyConstants.Levels.TotalLevelCount;
using Microsoft.TestPlatform.AdapterUtilities;
using Microsoft.TestPlatform.AdapterUtilities.ManagedNameUtilities;
#region Fields
private IReadOnlyCollection<string> hierarchy;
private string declaringClassFullName = null;
private string declaringAssemblyName = null;
#endregion
using MSTestAdapter.PlatformServices.Interface.ObjectModel;
public TestMethod(string name, string fullClassName, string assemblyName, bool isAsync)
{
if (string.IsNullOrEmpty(assemblyName))
{
throw new ArgumentNullException(nameof(assemblyName));
}
Debug.Assert(!string.IsNullOrEmpty(name), "TestName cannot be empty");
Debug.Assert(!string.IsNullOrEmpty(fullClassName), "Full className cannot be empty");
this.Name = name;
this.FullClassName = fullClassName;
this.AssemblyName = assemblyName;
this.IsAsync = isAsync;
var hierarchy = new string[HierarchyConstants.Levels.TotalLevelCount];
hierarchy[HierarchyConstants.Levels.ContainerIndex] = null;
hierarchy[HierarchyConstants.Levels.NamespaceIndex] = fullClassName;
hierarchy[HierarchyConstants.Levels.ClassIndex] = name;
hierarchy[HierarchyConstants.Levels.TestGroupIndex] = name;
this.hierarchy = new ReadOnlyCollection<string>(hierarchy);
}
internal TestMethod(MethodBase method, string name, string fullClassName, string assemblyName, bool isAsync)
: this(name, fullClassName, assemblyName, isAsync)
{
if (method == null)
{
throw new ArgumentNullException(nameof(method));
}
ManagedNameHelper.GetManagedName(method, out var managedType, out var managedMethod, out var hierarchyValues);
hierarchyValues[HierarchyConstants.Levels.ContainerIndex] = null; // This one will be set by test windows to current test project name.
this.ManagedTypeName = managedType;
this.ManagedMethodName = managedMethod;
this.hierarchy = new ReadOnlyCollection<string>(hierarchyValues);
}
internal TestMethod(string managedTypeName, string managedMethodName, string[] hierarchyValues, string name, string fullClassName, string assemblyName, bool isAsync)
: this(name, fullClassName, assemblyName, isAsync)
{
this.ManagedTypeName = managedTypeName;
this.ManagedMethodName = managedMethodName;
this.hierarchy = new ReadOnlyCollection<string>(hierarchyValues);
}
/// <inheritdoc />
public string Name { get; }
/// <inheritdoc />
public string FullClassName { get; }
/// <summary>
/// TestMethod contains information about a unit test method that needs to be executed
/// Gets or sets the declaring assembly full name. This will be used while getting navigation data.
/// This will be null if AssemblyName is same as DeclaringAssemblyName.
/// Reason to set to null in the above case is to minimize the transfer of data across appdomains and not have a performance hit.
/// </summary>
[Serializable]
public sealed class TestMethod : ITestMethod
public string DeclaringAssemblyName
{
/// <summary>
/// Number of elements in <see cref="Hierarchy"/>.
/// </summary>
public const int TotalHierarchyLevels = HierarchyConstants.Levels.TotalLevelCount;
#region Fields
private IReadOnlyCollection<string> hierarchy;
private string declaringClassFullName = null;
private string declaringAssemblyName = null;
#endregion
public TestMethod(string name, string fullClassName, string assemblyName, bool isAsync)
get
{
if (string.IsNullOrEmpty(assemblyName))
{
throw new ArgumentNullException(nameof(assemblyName));
}
Debug.Assert(!string.IsNullOrEmpty(name), "TestName cannot be empty");
Debug.Assert(!string.IsNullOrEmpty(fullClassName), "Full className cannot be empty");
this.Name = name;
this.FullClassName = fullClassName;
this.AssemblyName = assemblyName;
this.IsAsync = isAsync;
var hierarchy = new string[HierarchyConstants.Levels.TotalLevelCount];
hierarchy[HierarchyConstants.Levels.ContainerIndex] = null;
hierarchy[HierarchyConstants.Levels.NamespaceIndex] = fullClassName;
hierarchy[HierarchyConstants.Levels.ClassIndex] = name;
hierarchy[HierarchyConstants.Levels.TestGroupIndex] = name;
this.hierarchy = new ReadOnlyCollection<string>(hierarchy);
return this.declaringAssemblyName;
}
internal TestMethod(MethodBase method, string name, string fullClassName, string assemblyName, bool isAsync)
: this(name, fullClassName, assemblyName, isAsync)
set
{
if (method == null)
{
throw new ArgumentNullException(nameof(method));
}
ManagedNameHelper.GetManagedName(method, out var managedType, out var managedMethod, out var hierarchyValues);
hierarchyValues[HierarchyConstants.Levels.ContainerIndex] = null; // This one will be set by test windows to current test project name.
this.ManagedTypeName = managedType;
this.ManagedMethodName = managedMethod;
this.hierarchy = new ReadOnlyCollection<string>(hierarchyValues);
Debug.Assert(value != this.AssemblyName, "DeclaringAssemblyName should not be the same as AssemblyName.");
this.declaringAssemblyName = value;
}
internal TestMethod(string managedTypeName, string managedMethodName, string[] hierarchyValues, string name, string fullClassName, string assemblyName, bool isAsync)
: this(name, fullClassName, assemblyName, isAsync)
{
this.ManagedTypeName = managedTypeName;
this.ManagedMethodName = managedMethodName;
this.hierarchy = new ReadOnlyCollection<string>(hierarchyValues);
}
/// <inheritdoc />
public string Name { get; }
/// <inheritdoc />
public string FullClassName { get; }
/// <summary>
/// Gets or sets the declaring assembly full name. This will be used while getting navigation data.
/// This will be null if AssemblyName is same as DeclaringAssemblyName.
/// Reason to set to null in the above case is to minimize the transfer of data across appdomains and not have a performance hit.
/// </summary>
public string DeclaringAssemblyName
{
get
{
return this.declaringAssemblyName;
}
set
{
Debug.Assert(value != this.AssemblyName, "DeclaringAssemblyName should not be the same as AssemblyName.");
this.declaringAssemblyName = value;
}
}
/// <summary>
/// Gets or sets the declaring class full name.
/// This will be used to resolve overloads and while getting navigation data.
/// This will be null if FullClassName is same as DeclaringClassFullName.
/// Reason to set to null in the above case is to minimize the transfer of data across appdomains and not have a perf hit.
/// </summary>
public string DeclaringClassFullName
{
get
{
return this.declaringClassFullName;
}
set
{
Debug.Assert(value != this.FullClassName, "DeclaringClassFullName should not be the same as FullClassName.");
this.declaringClassFullName = value;
}
}
/// <inheritdoc />
public string AssemblyName { get; private set; }
/// <inheritdoc />
public bool IsAsync { get; private set; }
/// <inheritdoc />
public string ManagedTypeName { get; }
/// <inheritdoc />
public string ManagedMethodName { get; }
/// <inheritdoc />
public bool HasManagedMethodAndTypeProperties => !string.IsNullOrWhiteSpace(this.ManagedTypeName) && !string.IsNullOrWhiteSpace(this.ManagedMethodName);
/// <inheritdoc />
public IReadOnlyCollection<string> Hierarchy => this.hierarchy;
/// <summary>
/// Gets or sets type of dynamic data if any
/// </summary>
internal DynamicDataType DataType { get; set; }
/// <summary>
/// Gets or sets the serialized data
/// </summary>
internal string[] SerializedData { get; set; }
/// <summary>
/// Gets or sets the test group set during discovery
/// </summary>
internal string TestGroup { get; set; }
/// <summary>
/// Gets or sets the display name set during discovery
/// </summary>
internal string DisplayName { get; set; }
internal TestMethod Clone() => this.MemberwiseClone() as TestMethod;
}
/// <summary>
/// Gets or sets the declaring class full name.
/// This will be used to resolve overloads and while getting navigation data.
/// This will be null if FullClassName is same as DeclaringClassFullName.
/// Reason to set to null in the above case is to minimize the transfer of data across appdomains and not have a perf hit.
/// </summary>
public string DeclaringClassFullName
{
get
{
return this.declaringClassFullName;
}
set
{
Debug.Assert(value != this.FullClassName, "DeclaringClassFullName should not be the same as FullClassName.");
this.declaringClassFullName = value;
}
}
/// <inheritdoc />
public string AssemblyName { get; private set; }
/// <inheritdoc />
public bool IsAsync { get; private set; }
/// <inheritdoc />
public string ManagedTypeName { get; }
/// <inheritdoc />
public string ManagedMethodName { get; }
/// <inheritdoc />
public bool HasManagedMethodAndTypeProperties => !string.IsNullOrWhiteSpace(this.ManagedTypeName) && !string.IsNullOrWhiteSpace(this.ManagedMethodName);
/// <inheritdoc />
public IReadOnlyCollection<string> Hierarchy => this.hierarchy;
/// <summary>
/// Gets or sets type of dynamic data if any
/// </summary>
internal DynamicDataType DataType { get; set; }
/// <summary>
/// Gets or sets the serialized data
/// </summary>
internal string[] SerializedData { get; set; }
/// <summary>
/// Gets or sets the test group set during discovery
/// </summary>
internal string TestGroup { get; set; }
/// <summary>
/// Gets or sets the display name set during discovery
/// </summary>
internal string DisplayName { get; set; }
internal TestMethod Clone() => this.MemberwiseClone() as TestMethod;
}

Просмотреть файл

@ -1,39 +1,38 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
namespace Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.ObjectModel
namespace Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.ObjectModel;
using Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.Interface;
using Microsoft.VisualStudio.TestTools.UnitTesting;
/// <summary>
/// A facade service for options passed to a test method.
/// </summary>
internal class TestMethodOptions
{
using Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.Interface;
using Microsoft.VisualStudio.TestTools.UnitTesting;
/// <summary>
/// Gets or sets the timeout specified for a test method.
/// </summary>
internal int Timeout { get; set; }
/// <summary>
/// A facade service for options passed to a test method.
/// Gets or sets the ExpectedException attribute adorned on a test method.
/// </summary>
internal class TestMethodOptions
{
/// <summary>
/// Gets or sets the timeout specified for a test method.
/// </summary>
internal int Timeout { get; set; }
internal ExpectedExceptionBaseAttribute ExpectedException { get; set; }
/// <summary>
/// Gets or sets the ExpectedException attribute adorned on a test method.
/// </summary>
internal ExpectedExceptionBaseAttribute ExpectedException { get; set; }
/// <summary>
/// Gets or sets the testcontext passed into the test method.
/// </summary>
internal ITestContext TestContext { get; set; }
/// <summary>
/// Gets or sets the testcontext passed into the test method.
/// </summary>
internal ITestContext TestContext { get; set; }
/// <summary>
/// Gets or sets a value indicating whether debug traces should be captured when running the test.
/// </summary>
internal bool CaptureDebugTraces { get; set; }
/// <summary>
/// Gets or sets a value indicating whether debug traces should be captured when running the test.
/// </summary>
internal bool CaptureDebugTraces { get; set; }
/// <summary>
/// Gets or sets the test method executor that invokes the test.
/// </summary>
internal TestMethodAttribute Executor { get; set; }
}
/// <summary>
/// Gets or sets the test method executor that invokes the test.
/// </summary>
internal TestMethodAttribute Executor { get; set; }
}

Просмотреть файл

@ -1,29 +1,28 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
namespace Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.ObjectModel
namespace Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.ObjectModel;
using System;
/// <summary>
/// Internal class to indicate type inspection failure
/// </summary>
[Serializable]
internal class TypeInspectionException : Exception
{
using System;
/// <summary>
/// Internal class to indicate type inspection failure
/// </summary>
[Serializable]
internal class TypeInspectionException : Exception
public TypeInspectionException()
: base()
{
public TypeInspectionException()
: base()
{
}
}
public TypeInspectionException(string message)
: base(message)
{
}
public TypeInspectionException(string message)
: base(message)
{
}
public TypeInspectionException(string message, Exception innerException)
: base(message, innerException)
{
}
public TypeInspectionException(string message, Exception innerException)
: base(message, innerException)
{
}
}

Просмотреть файл

@ -1,272 +1,271 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
namespace Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.ObjectModel
{
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Linq;
namespace Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.ObjectModel;
using Microsoft.TestPlatform.AdapterUtilities;
using Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.Extensions;
using Microsoft.VisualStudio.TestPlatform.ObjectModel;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Linq;
using Microsoft.TestPlatform.AdapterUtilities;
using Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.Extensions;
using Microsoft.VisualStudio.TestPlatform.ObjectModel;
/// <summary>
/// The unit test element.
/// </summary>
[Serializable]
[DebuggerDisplay("{GetDisplayName()} ({TestMethod.ManagedTypeName})")]
internal class UnitTestElement
{
/// <summary>
/// Initializes a new instance of the <see cref="UnitTestElement"/> class.
/// </summary>
/// <param name="testMethod"> The test method. </param>
/// <exception cref="ArgumentNullException"> Thrown when method is null. </exception>
public UnitTestElement(TestMethod testMethod)
{
if (testMethod == null)
{
throw new ArgumentNullException(nameof(testMethod));
}
Debug.Assert(testMethod.FullClassName != null, "Full className cannot be empty");
this.TestMethod = testMethod;
}
/// <summary>
/// The unit test element.
/// Gets the test method which should be executed as part of this test case
/// </summary>
[Serializable]
[DebuggerDisplay("{GetDisplayName()} ({TestMethod.ManagedTypeName})")]
internal class UnitTestElement
public TestMethod TestMethod { get; private set; }
/// <summary>
/// Gets or sets a value indicating whether the unit test should be ignored at run-time
/// </summary>
public bool Ignored { get; set; }
/// <summary>
/// Gets or sets a value indicating whether it is a async test
/// </summary>
public bool IsAsync { get; set; }
/// <summary>
/// Gets or sets the test categories for test method.
/// </summary>
public string[] TestCategory { get; set; }
/// <summary>
/// Gets or sets the traits for test method.
/// </summary>
public Trait[] Traits { get; set; }
/// <summary>
/// Gets or sets the priority of the test method, if any.
/// </summary>
public int? Priority { get; set; }
/// <summary>
/// Gets or sets a value indicating whether this test method should not execute in parallel.
/// </summary>
public bool DoNotParallelize { get; set; }
/// <summary>
/// Gets or sets the deployment items for the test method.
/// </summary>
public KeyValuePair<string, string>[] DeploymentItems { get; set; }
/// <summary>
/// Gets or sets the DisplayName
/// </summary>
public string DisplayName { get; set; }
/// <summary>
/// Gets or sets the compiler generated type name for async test method.
/// </summary>
internal string AsyncTypeName { get; set; }
/// <summary>
/// Gets or sets the Css Iteration for the test method.
/// </summary>
internal string CssIteration { get; set; }
/// <summary>
/// Gets or sets the Css Project Structure for the test method.
/// </summary>
internal string CssProjectStructure { get; set; }
/// <summary>
/// Gets or sets the Description for the test method.
/// </summary>
internal string Description { get; set; }
/// <summary>
/// Gets or sets the Work Item Ids for the test method.
/// </summary>
internal string[] WorkItemIds { get; set; }
internal UnitTestElement Clone()
{
/// <summary>
/// Initializes a new instance of the <see cref="UnitTestElement"/> class.
/// </summary>
/// <param name="testMethod"> The test method. </param>
/// <exception cref="ArgumentNullException"> Thrown when method is null. </exception>
public UnitTestElement(TestMethod testMethod)
var clone = this.MemberwiseClone() as UnitTestElement;
if (this.TestMethod != null)
{
if (testMethod == null)
{
throw new ArgumentNullException(nameof(testMethod));
}
Debug.Assert(testMethod.FullClassName != null, "Full className cannot be empty");
this.TestMethod = testMethod;
clone.TestMethod = this.TestMethod.Clone();
}
/// <summary>
/// Gets the test method which should be executed as part of this test case
/// </summary>
public TestMethod TestMethod { get; private set; }
return clone;
}
/// <summary>
/// Gets or sets a value indicating whether the unit test should be ignored at run-time
/// </summary>
public bool Ignored { get; set; }
/// <summary>
/// Convert the UnitTestElement instance to an Object Model testCase instance.
/// </summary>
/// <returns> An instance of <see cref="TestCase"/>. </returns>
internal TestCase ToTestCase()
{
// This causes compatibility problems with older runners.
// string fullName = this.TestMethod.HasManagedMethodAndTypeProperties
// ? string.Format(CultureInfo.InvariantCulture, "{0}.{1}", this.TestMethod.ManagedTypeName, this.TestMethod.ManagedMethodName)
// : string.Format(CultureInfo.InvariantCulture, "{0}.{1}", this.TestMethod.FullClassName, this.TestMethod.Name);
var fullName = string.Format(CultureInfo.InvariantCulture, "{0}.{1}", this.TestMethod.FullClassName, this.TestMethod.Name);
/// <summary>
/// Gets or sets a value indicating whether it is a async test
/// </summary>
public bool IsAsync { get; set; }
TestCase testCase = new(fullName, TestAdapter.Constants.ExecutorUri, this.TestMethod.AssemblyName);
testCase.DisplayName = this.GetDisplayName();
/// <summary>
/// Gets or sets the test categories for test method.
/// </summary>
public string[] TestCategory { get; set; }
/// <summary>
/// Gets or sets the traits for test method.
/// </summary>
public Trait[] Traits { get; set; }
/// <summary>
/// Gets or sets the priority of the test method, if any.
/// </summary>
public int? Priority { get; set; }
/// <summary>
/// Gets or sets a value indicating whether this test method should not execute in parallel.
/// </summary>
public bool DoNotParallelize { get; set; }
/// <summary>
/// Gets or sets the deployment items for the test method.
/// </summary>
public KeyValuePair<string, string>[] DeploymentItems { get; set; }
/// <summary>
/// Gets or sets the DisplayName
/// </summary>
public string DisplayName { get; set; }
/// <summary>
/// Gets or sets the compiler generated type name for async test method.
/// </summary>
internal string AsyncTypeName { get; set; }
/// <summary>
/// Gets or sets the Css Iteration for the test method.
/// </summary>
internal string CssIteration { get; set; }
/// <summary>
/// Gets or sets the Css Project Structure for the test method.
/// </summary>
internal string CssProjectStructure { get; set; }
/// <summary>
/// Gets or sets the Description for the test method.
/// </summary>
internal string Description { get; set; }
/// <summary>
/// Gets or sets the Work Item Ids for the test method.
/// </summary>
internal string[] WorkItemIds { get; set; }
internal UnitTestElement Clone()
if (this.TestMethod.HasManagedMethodAndTypeProperties)
{
var clone = this.MemberwiseClone() as UnitTestElement;
if (this.TestMethod != null)
{
clone.TestMethod = this.TestMethod.Clone();
}
return clone;
testCase.SetPropertyValue(TestCaseExtensions.ManagedTypeProperty, this.TestMethod.ManagedTypeName);
testCase.SetPropertyValue(TestCaseExtensions.ManagedMethodProperty, this.TestMethod.ManagedMethodName);
testCase.SetPropertyValue(TestAdapter.Constants.TestClassNameProperty, this.TestMethod.ManagedTypeName);
}
else
{
testCase.SetPropertyValue(TestAdapter.Constants.TestClassNameProperty, this.TestMethod.FullClassName);
}
/// <summary>
/// Convert the UnitTestElement instance to an Object Model testCase instance.
/// </summary>
/// <returns> An instance of <see cref="TestCase"/>. </returns>
internal TestCase ToTestCase()
var hierarchy = this.TestMethod.Hierarchy;
if (hierarchy != null && hierarchy.Count > 0)
{
testCase.SetHierarchy(hierarchy.ToArray());
}
// Set declaring type if present so the correct method info can be retrieved
if (this.TestMethod.DeclaringClassFullName != null)
{
testCase.SetPropertyValue(TestAdapter.Constants.DeclaringClassNameProperty, this.TestMethod.DeclaringClassFullName);
}
// Many of the tests will not be async, so there is no point in sending extra data
if (this.IsAsync)
{
testCase.SetPropertyValue(TestAdapter.Constants.AsyncTestProperty, this.IsAsync);
}
// Set only if some test category is present
if (this.TestCategory != null && this.TestCategory.Length > 0)
{
testCase.SetPropertyValue(TestAdapter.Constants.TestCategoryProperty, this.TestCategory);
}
// Set priority if present
if (this.Priority != null)
{
testCase.SetPropertyValue(TestAdapter.Constants.PriorityProperty, this.Priority.Value);
}
if (this.Traits != null)
{
testCase.Traits.AddRange(this.Traits);
}
if (!string.IsNullOrEmpty(this.CssIteration))
{
testCase.SetPropertyValue(TestAdapter.Constants.CssIterationProperty, this.CssIteration);
}
if (!string.IsNullOrEmpty(this.CssProjectStructure))
{
testCase.SetPropertyValue(TestAdapter.Constants.CssProjectStructureProperty, this.CssProjectStructure);
}
if (!string.IsNullOrEmpty(this.Description))
{
testCase.SetPropertyValue(TestAdapter.Constants.DescriptionProperty, this.Description);
}
if (this.WorkItemIds != null)
{
testCase.SetPropertyValue(TestAdapter.Constants.WorkItemIdsProperty, this.WorkItemIds);
}
// The list of items to deploy before running this test.
if (this.DeploymentItems != null && this.DeploymentItems.Length > 0)
{
testCase.SetPropertyValue(TestAdapter.Constants.DeploymentItemsProperty, this.DeploymentItems);
}
// Set the Do not parallelize state if present
if (this.DoNotParallelize)
{
testCase.SetPropertyValue(TestAdapter.Constants.DoNotParallelizeProperty, this.DoNotParallelize);
}
// Store resolved data if any
if (this.TestMethod.DataType != DynamicDataType.None)
{
var data = this.TestMethod.SerializedData;
testCase.SetPropertyValue(TestAdapter.Constants.TestDynamicDataTypeProperty, (int)this.TestMethod.DataType);
testCase.SetPropertyValue(TestAdapter.Constants.TestDynamicDataProperty, data);
}
string fileName = testCase.Source;
try
{
fileName = Path.GetFileName(fileName);
}
catch
{
}
var idProvider = new TestIdProvider();
idProvider.AppendString(testCase.ExecutorUri?.ToString());
idProvider.AppendString(fileName);
if (this.TestMethod.HasManagedMethodAndTypeProperties)
{
idProvider.AppendString(this.TestMethod.ManagedTypeName);
idProvider.AppendString(this.TestMethod.ManagedMethodName);
}
else
{
idProvider.AppendString(testCase.FullyQualifiedName);
}
if (this.TestMethod.DataType != DynamicDataType.None)
{
idProvider.AppendString(testCase.DisplayName);
}
testCase.Id = idProvider.GetId();
return testCase;
}
private string GetDisplayName()
{
if (string.IsNullOrWhiteSpace(this.DisplayName))
{
return this.TestMethod.Name;
// This causes compatibility problems with older runners.
// string fullName = this.TestMethod.HasManagedMethodAndTypeProperties
// ? string.Format(CultureInfo.InvariantCulture, "{0}.{1}", this.TestMethod.ManagedTypeName, this.TestMethod.ManagedMethodName)
// : string.Format(CultureInfo.InvariantCulture, "{0}.{1}", this.TestMethod.FullClassName, this.TestMethod.Name);
var fullName = string.Format(CultureInfo.InvariantCulture, "{0}.{1}", this.TestMethod.FullClassName, this.TestMethod.Name);
TestCase testCase = new(fullName, TestAdapter.Constants.ExecutorUri, this.TestMethod.AssemblyName);
testCase.DisplayName = this.GetDisplayName();
if (this.TestMethod.HasManagedMethodAndTypeProperties)
{
testCase.SetPropertyValue(TestCaseExtensions.ManagedTypeProperty, this.TestMethod.ManagedTypeName);
testCase.SetPropertyValue(TestCaseExtensions.ManagedMethodProperty, this.TestMethod.ManagedMethodName);
testCase.SetPropertyValue(TestAdapter.Constants.TestClassNameProperty, this.TestMethod.ManagedTypeName);
}
else
{
testCase.SetPropertyValue(TestAdapter.Constants.TestClassNameProperty, this.TestMethod.FullClassName);
}
var hierarchy = this.TestMethod.Hierarchy;
if (hierarchy != null && hierarchy.Count > 0)
{
testCase.SetHierarchy(hierarchy.ToArray());
}
// Set declaring type if present so the correct method info can be retrieved
if (this.TestMethod.DeclaringClassFullName != null)
{
testCase.SetPropertyValue(TestAdapter.Constants.DeclaringClassNameProperty, this.TestMethod.DeclaringClassFullName);
}
// Many of the tests will not be async, so there is no point in sending extra data
if (this.IsAsync)
{
testCase.SetPropertyValue(TestAdapter.Constants.AsyncTestProperty, this.IsAsync);
}
// Set only if some test category is present
if (this.TestCategory != null && this.TestCategory.Length > 0)
{
testCase.SetPropertyValue(TestAdapter.Constants.TestCategoryProperty, this.TestCategory);
}
// Set priority if present
if (this.Priority != null)
{
testCase.SetPropertyValue(TestAdapter.Constants.PriorityProperty, this.Priority.Value);
}
if (this.Traits != null)
{
testCase.Traits.AddRange(this.Traits);
}
if (!string.IsNullOrEmpty(this.CssIteration))
{
testCase.SetPropertyValue(TestAdapter.Constants.CssIterationProperty, this.CssIteration);
}
if (!string.IsNullOrEmpty(this.CssProjectStructure))
{
testCase.SetPropertyValue(TestAdapter.Constants.CssProjectStructureProperty, this.CssProjectStructure);
}
if (!string.IsNullOrEmpty(this.Description))
{
testCase.SetPropertyValue(TestAdapter.Constants.DescriptionProperty, this.Description);
}
if (this.WorkItemIds != null)
{
testCase.SetPropertyValue(TestAdapter.Constants.WorkItemIdsProperty, this.WorkItemIds);
}
// The list of items to deploy before running this test.
if (this.DeploymentItems != null && this.DeploymentItems.Length > 0)
{
testCase.SetPropertyValue(TestAdapter.Constants.DeploymentItemsProperty, this.DeploymentItems);
}
// Set the Do not parallelize state if present
if (this.DoNotParallelize)
{
testCase.SetPropertyValue(TestAdapter.Constants.DoNotParallelizeProperty, this.DoNotParallelize);
}
// Store resolved data if any
if (this.TestMethod.DataType != DynamicDataType.None)
{
var data = this.TestMethod.SerializedData;
testCase.SetPropertyValue(TestAdapter.Constants.TestDynamicDataTypeProperty, (int)this.TestMethod.DataType);
testCase.SetPropertyValue(TestAdapter.Constants.TestDynamicDataProperty, data);
}
string fileName = testCase.Source;
try
{
fileName = Path.GetFileName(fileName);
}
catch
{
}
var idProvider = new TestIdProvider();
idProvider.AppendString(testCase.ExecutorUri?.ToString());
idProvider.AppendString(fileName);
if (this.TestMethod.HasManagedMethodAndTypeProperties)
{
idProvider.AppendString(this.TestMethod.ManagedTypeName);
idProvider.AppendString(this.TestMethod.ManagedMethodName);
}
else
{
idProvider.AppendString(testCase.FullyQualifiedName);
}
if (this.TestMethod.DataType != DynamicDataType.None)
{
idProvider.AppendString(testCase.DisplayName);
}
testCase.Id = idProvider.GetId();
return testCase;
// return string.IsNullOrWhiteSpace(this.TestMethod.ManagedMethodName)
// ? this.TestMethod.Name
// : this.TestMethod.ManagedMethodName;
}
private string GetDisplayName()
else
{
if (string.IsNullOrWhiteSpace(this.DisplayName))
{
return this.TestMethod.Name;
// This causes compatibility problems with older runners.
// return string.IsNullOrWhiteSpace(this.TestMethod.ManagedMethodName)
// ? this.TestMethod.Name
// : this.TestMethod.ManagedMethodName;
}
else
{
return this.DisplayName;
}
return this.DisplayName;
}
}
}

Просмотреть файл

@ -1,59 +1,58 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
namespace Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.ObjectModel
namespace Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.ObjectModel;
/// <summary>
/// Outcome of a test
/// </summary>
public enum UnitTestOutcome : int
{
/// <summary>
/// Outcome of a test
/// There was a system error while we were trying to execute a test.
/// </summary>
public enum UnitTestOutcome : int
{
/// <summary>
/// There was a system error while we were trying to execute a test.
/// </summary>
Error,
Error,
/// <summary>
/// Test was executed, but there were issues.
/// Issues may involve exceptions or failed assertions.
/// </summary>
Failed,
/// <summary>
/// Test was executed, but there were issues.
/// Issues may involve exceptions or failed assertions.
/// </summary>
Failed,
/// <summary>
/// The test timed out
/// </summary>
Timeout,
/// <summary>
/// The test timed out
/// </summary>
Timeout,
/// <summary>
/// Test has completed, but we can't say if it passed or failed.
/// (Used in Assert.InConclusive scenario)
/// </summary>
Inconclusive,
/// <summary>
/// Test has completed, but we can't say if it passed or failed.
/// (Used in Assert.InConclusive scenario)
/// </summary>
Inconclusive,
/// <summary>
/// Test had it chance for been executed but was not, as Ignore == true.
/// </summary>
Ignored,
/// <summary>
/// Test had it chance for been executed but was not, as Ignore == true.
/// </summary>
Ignored,
/// <summary>
/// Test cannot be executed.
/// </summary>
NotRunnable,
/// <summary>
/// Test cannot be executed.
/// </summary>
NotRunnable,
/// <summary>
/// Test was executed w/o any issues.
/// </summary>
Passed,
/// <summary>
/// Test was executed w/o any issues.
/// </summary>
Passed,
/// <summary>
/// The specific test cannot be found.
/// </summary>
NotFound,
/// <summary>
/// The specific test cannot be found.
/// </summary>
NotFound,
/// <summary>
/// When test is handed over to runner for execution, it goes into progress state.
/// It is added so that the right status can be set in TestContext.
/// </summary>
InProgress,
}
/// <summary>
/// When test is handed over to runner for execution, it goes into progress state.
/// It is added so that the right status can be set in TestContext.
/// </summary>
InProgress,
}

Просмотреть файл

@ -1,214 +1,213 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
namespace Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.ObjectModel
namespace Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.ObjectModel;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.Helpers;
using Microsoft.VisualStudio.TestPlatform.ObjectModel;
using Constants = Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.Constants;
[Serializable]
[DebuggerDisplay("{DisplayName} ({Outcome})")]
public class UnitTestResult
{
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.Helpers;
using Microsoft.VisualStudio.TestPlatform.ObjectModel;
using Constants = Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.Constants;
[Serializable]
[DebuggerDisplay("{DisplayName} ({Outcome})")]
public class UnitTestResult
/// <summary>
/// Initializes a new instance of the <see cref="UnitTestResult"/> class.
/// </summary>
internal UnitTestResult()
{
/// <summary>
/// Initializes a new instance of the <see cref="UnitTestResult"/> class.
/// </summary>
internal UnitTestResult()
this.DatarowIndex = -1;
}
/// <summary>
/// Initializes a new instance of the <see cref="UnitTestResult"/> class.
/// </summary>
/// <param name="testFailedException"> The test failed exception. </param>
internal UnitTestResult(TestFailedException testFailedException)
: this()
{
this.Outcome = testFailedException.Outcome;
this.ErrorMessage = testFailedException.Message;
if (testFailedException.StackTraceInformation != null)
{
this.DatarowIndex = -1;
}
/// <summary>
/// Initializes a new instance of the <see cref="UnitTestResult"/> class.
/// </summary>
/// <param name="testFailedException"> The test failed exception. </param>
internal UnitTestResult(TestFailedException testFailedException)
: this()
{
this.Outcome = testFailedException.Outcome;
this.ErrorMessage = testFailedException.Message;
if (testFailedException.StackTraceInformation != null)
{
this.ErrorStackTrace = testFailedException.StackTraceInformation.ErrorStackTrace;
this.ErrorLineNumber = testFailedException.StackTraceInformation.ErrorLineNumber;
this.ErrorFilePath = testFailedException.StackTraceInformation.ErrorFilePath;
this.ErrorColumnNumber = testFailedException.StackTraceInformation.ErrorColumnNumber;
}
}
/// <summary>
/// Initializes a new instance of the <see cref="UnitTestResult"/> class.
/// </summary>
/// <param name="outcome"> The outcome. </param>
/// <param name="errorMessage"> The error message. </param>
internal UnitTestResult(UnitTestOutcome outcome, string errorMessage)
: this()
{
this.Outcome = outcome;
this.ErrorMessage = errorMessage;
}
/// <summary>
/// Gets the display name for the result
/// </summary>
public string DisplayName { get; internal set; }
/// <summary>
/// Gets the outcome of the result
/// </summary>
public UnitTestOutcome Outcome { get; internal set; }
/// <summary>
/// Gets the errorMessage of the result
/// </summary>
public string ErrorMessage { get; internal set; }
/// <summary>
/// Gets the stackTrace of the result
/// </summary>
public string ErrorStackTrace { get; internal set; }
/// <summary>
/// Gets the execution id of the result
/// </summary>
public Guid ExecutionId { get; internal set; }
/// <summary>
/// Gets the parent execution id of the result
/// </summary>
public Guid ParentExecId { get; internal set; }
/// <summary>
/// Gets the inner results count of the result
/// </summary>
public int InnerResultsCount { get; internal set; }
/// <summary>
/// Gets the duration of the result
/// </summary>
public TimeSpan Duration { get; internal set; }
/// <summary>
/// Gets the standard output of the result
/// </summary>
public string StandardOut { get; internal set; }
/// <summary>
/// Gets the Standard Error of the result
/// </summary>
public string StandardError { get; internal set; }
/// <summary>
/// Gets the debug trace of the result
/// </summary>
public string DebugTrace { get; internal set; }
/// <summary>
/// Gets additional information messages generated by TestContext.WriteLine.
/// </summary>
public string TestContextMessages { get; internal set; }
/// <summary>
/// Gets the source code FilePath where the error was thrown.
/// </summary>
public string ErrorFilePath { get; internal set; }
/// <summary>
/// Gets the line number in the source code file where the error was thrown.
/// </summary>
public int ErrorLineNumber { get; private set; }
/// <summary>
/// Gets the column number in the source code file where the error was thrown.
/// </summary>
public int ErrorColumnNumber { get; private set; }
/// <summary>
/// Gets data row index in data source. Set only for results of individual
/// run of data row of a data driven test.
/// </summary>
public int DatarowIndex { get; internal set; }
/// <summary>
/// Gets the result files attached by the test.
/// </summary>
public IList<string> ResultFiles { get; internal set; }
/// <summary>
/// Convert parameter unitTestResult to testResult
/// </summary>
/// <param name="testCase"> The test Case. </param>
/// <param name="startTime"> The start Time. </param>
/// <param name="endTime"> The end Time. </param>
/// <param name="currentSettings">Current MSTest settings.</param>
/// <returns> The <see cref="TestResult"/>. </returns>
internal TestResult ToTestResult(TestCase testCase, DateTimeOffset startTime, DateTimeOffset endTime, MSTestSettings currentSettings)
{
Debug.Assert(testCase != null, "testCase");
var testResult = new TestResult(testCase)
{
DisplayName = this.DisplayName,
Duration = this.Duration,
ErrorMessage = this.ErrorMessage,
ErrorStackTrace = this.ErrorStackTrace,
Outcome = UnitTestOutcomeHelper.ToTestOutcome(this.Outcome, currentSettings),
StartTime = startTime,
EndTime = endTime
};
testResult.SetPropertyValue<Guid>(Constants.ExecutionIdProperty, this.ExecutionId);
testResult.SetPropertyValue<Guid>(Constants.ParentExecIdProperty, this.ParentExecId);
testResult.SetPropertyValue<int>(Constants.InnerResultsCountProperty, this.InnerResultsCount);
if (!string.IsNullOrEmpty(this.StandardOut))
{
TestResultMessage message = new(TestResultMessage.StandardOutCategory, this.StandardOut);
testResult.Messages.Add(message);
}
if (!string.IsNullOrEmpty(this.StandardError))
{
TestResultMessage message = new(TestResultMessage.StandardErrorCategory, this.StandardError);
testResult.Messages.Add(message);
}
if (!string.IsNullOrEmpty(this.DebugTrace))
{
string debugTraceMessagesinStdOut = string.Format(CultureInfo.InvariantCulture, "\n\n{0}\n{1}", Resource.DebugTraceBanner, this.DebugTrace);
TestResultMessage debugTraceMessage = new(TestResultMessage.StandardOutCategory, debugTraceMessagesinStdOut);
testResult.Messages.Add(debugTraceMessage);
}
if (!string.IsNullOrEmpty(this.TestContextMessages))
{
string testContextMessagesInStdOut = string.Format(CultureInfo.InvariantCulture, "\n\n{0}\n{1}", Resource.TestContextMessageBanner, this.TestContextMessages);
TestResultMessage testContextMessage = new(TestResultMessage.StandardOutCategory, testContextMessagesInStdOut);
testResult.Messages.Add(testContextMessage);
}
if (this.ResultFiles != null && this.ResultFiles.Count > 0)
{
AttachmentSet attachmentSet = new(Constants.ExecutorUri, Resource.AttachmentSetDisplayName);
foreach (var resultFile in this.ResultFiles)
{
string pathToResultFile = PlatformServiceProvider.Instance.FileOperations.GetFullFilePath(resultFile);
UriDataAttachment attachment = new(new Uri(pathToResultFile), resultFile);
attachmentSet.Attachments.Add(attachment);
}
testResult.Attachments.Add(attachmentSet);
}
return testResult;
this.ErrorStackTrace = testFailedException.StackTraceInformation.ErrorStackTrace;
this.ErrorLineNumber = testFailedException.StackTraceInformation.ErrorLineNumber;
this.ErrorFilePath = testFailedException.StackTraceInformation.ErrorFilePath;
this.ErrorColumnNumber = testFailedException.StackTraceInformation.ErrorColumnNumber;
}
}
/// <summary>
/// Initializes a new instance of the <see cref="UnitTestResult"/> class.
/// </summary>
/// <param name="outcome"> The outcome. </param>
/// <param name="errorMessage"> The error message. </param>
internal UnitTestResult(UnitTestOutcome outcome, string errorMessage)
: this()
{
this.Outcome = outcome;
this.ErrorMessage = errorMessage;
}
/// <summary>
/// Gets the display name for the result
/// </summary>
public string DisplayName { get; internal set; }
/// <summary>
/// Gets the outcome of the result
/// </summary>
public UnitTestOutcome Outcome { get; internal set; }
/// <summary>
/// Gets the errorMessage of the result
/// </summary>
public string ErrorMessage { get; internal set; }
/// <summary>
/// Gets the stackTrace of the result
/// </summary>
public string ErrorStackTrace { get; internal set; }
/// <summary>
/// Gets the execution id of the result
/// </summary>
public Guid ExecutionId { get; internal set; }
/// <summary>
/// Gets the parent execution id of the result
/// </summary>
public Guid ParentExecId { get; internal set; }
/// <summary>
/// Gets the inner results count of the result
/// </summary>
public int InnerResultsCount { get; internal set; }
/// <summary>
/// Gets the duration of the result
/// </summary>
public TimeSpan Duration { get; internal set; }
/// <summary>
/// Gets the standard output of the result
/// </summary>
public string StandardOut { get; internal set; }
/// <summary>
/// Gets the Standard Error of the result
/// </summary>
public string StandardError { get; internal set; }
/// <summary>
/// Gets the debug trace of the result
/// </summary>
public string DebugTrace { get; internal set; }
/// <summary>
/// Gets additional information messages generated by TestContext.WriteLine.
/// </summary>
public string TestContextMessages { get; internal set; }
/// <summary>
/// Gets the source code FilePath where the error was thrown.
/// </summary>
public string ErrorFilePath { get; internal set; }
/// <summary>
/// Gets the line number in the source code file where the error was thrown.
/// </summary>
public int ErrorLineNumber { get; private set; }
/// <summary>
/// Gets the column number in the source code file where the error was thrown.
/// </summary>
public int ErrorColumnNumber { get; private set; }
/// <summary>
/// Gets data row index in data source. Set only for results of individual
/// run of data row of a data driven test.
/// </summary>
public int DatarowIndex { get; internal set; }
/// <summary>
/// Gets the result files attached by the test.
/// </summary>
public IList<string> ResultFiles { get; internal set; }
/// <summary>
/// Convert parameter unitTestResult to testResult
/// </summary>
/// <param name="testCase"> The test Case. </param>
/// <param name="startTime"> The start Time. </param>
/// <param name="endTime"> The end Time. </param>
/// <param name="currentSettings">Current MSTest settings.</param>
/// <returns> The <see cref="TestResult"/>. </returns>
internal TestResult ToTestResult(TestCase testCase, DateTimeOffset startTime, DateTimeOffset endTime, MSTestSettings currentSettings)
{
Debug.Assert(testCase != null, "testCase");
var testResult = new TestResult(testCase)
{
DisplayName = this.DisplayName,
Duration = this.Duration,
ErrorMessage = this.ErrorMessage,
ErrorStackTrace = this.ErrorStackTrace,
Outcome = UnitTestOutcomeHelper.ToTestOutcome(this.Outcome, currentSettings),
StartTime = startTime,
EndTime = endTime
};
testResult.SetPropertyValue<Guid>(Constants.ExecutionIdProperty, this.ExecutionId);
testResult.SetPropertyValue<Guid>(Constants.ParentExecIdProperty, this.ParentExecId);
testResult.SetPropertyValue<int>(Constants.InnerResultsCountProperty, this.InnerResultsCount);
if (!string.IsNullOrEmpty(this.StandardOut))
{
TestResultMessage message = new(TestResultMessage.StandardOutCategory, this.StandardOut);
testResult.Messages.Add(message);
}
if (!string.IsNullOrEmpty(this.StandardError))
{
TestResultMessage message = new(TestResultMessage.StandardErrorCategory, this.StandardError);
testResult.Messages.Add(message);
}
if (!string.IsNullOrEmpty(this.DebugTrace))
{
string debugTraceMessagesinStdOut = string.Format(CultureInfo.InvariantCulture, "\n\n{0}\n{1}", Resource.DebugTraceBanner, this.DebugTrace);
TestResultMessage debugTraceMessage = new(TestResultMessage.StandardOutCategory, debugTraceMessagesinStdOut);
testResult.Messages.Add(debugTraceMessage);
}
if (!string.IsNullOrEmpty(this.TestContextMessages))
{
string testContextMessagesInStdOut = string.Format(CultureInfo.InvariantCulture, "\n\n{0}\n{1}", Resource.TestContextMessageBanner, this.TestContextMessages);
TestResultMessage testContextMessage = new(TestResultMessage.StandardOutCategory, testContextMessagesInStdOut);
testResult.Messages.Add(testContextMessage);
}
if (this.ResultFiles != null && this.ResultFiles.Count > 0)
{
AttachmentSet attachmentSet = new(Constants.ExecutorUri, Resource.AttachmentSetDisplayName);
foreach (var resultFile in this.ResultFiles)
{
string pathToResultFile = PlatformServiceProvider.Instance.FileOperations.GetFullFilePath(resultFile);
UriDataAttachment attachment = new(new Uri(pathToResultFile), resultFile);
attachmentSet.Attachments.Add(attachment);
}
testResult.Attachments.Add(attachmentSet);
}
return testResult;
}
}

Просмотреть файл

@ -1,221 +1,220 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
namespace Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter
{
using System.Collections.Generic;
using System.IO;
namespace Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter;
using Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices;
using Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.Interface;
using Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.Interface.ObjectModel;
using System.Collections.Generic;
using System.IO;
using Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices;
using Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.Interface;
using Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.Interface.ObjectModel;
/// <summary>
/// The main service provider class that exposes all the platform services available.
/// </summary>
internal class PlatformServiceProvider : IPlatformServiceProvider
{
private static IPlatformServiceProvider instance;
private ITestSource testSource;
private IFileOperations fileOperations;
private IAdapterTraceLogger traceLogger;
private ITestDeployment testDeployment;
private ISettingsProvider settingsProvider;
private ITestDataSource testDataSource;
private IThreadOperations threadOperations;
private IReflectionOperations reflectionOperations;
/// <summary>
/// The main service provider class that exposes all the platform services available.
/// Initializes a new instance of the <see cref="PlatformServiceProvider"/> class - a singleton.
/// </summary>
internal class PlatformServiceProvider : IPlatformServiceProvider
private PlatformServiceProvider()
{
private static IPlatformServiceProvider instance;
private ITestSource testSource;
private IFileOperations fileOperations;
private IAdapterTraceLogger traceLogger;
private ITestDeployment testDeployment;
private ISettingsProvider settingsProvider;
private ITestDataSource testDataSource;
private IThreadOperations threadOperations;
private IReflectionOperations reflectionOperations;
}
/// <summary>
/// Initializes a new instance of the <see cref="PlatformServiceProvider"/> class - a singleton.
/// </summary>
private PlatformServiceProvider()
/// <summary>
/// Gets an instance to the platform service validator for test sources.
/// </summary>
public ITestSource TestSource
{
get
{
}
/// <summary>
/// Gets an instance to the platform service validator for test sources.
/// </summary>
public ITestSource TestSource
{
get
{
return this.testSource ??= new TestSource();
}
}
/// <summary>
/// Gets an instance to the platform service validator for data sources for tests.
/// </summary>
public ITestDataSource TestDataSource
{
get
{
return this.testDataSource ??= new TestDataSource();
}
}
/// <summary>
/// Gets an instance to the platform service for file operations.
/// </summary>
public IFileOperations FileOperations
{
get
{
return this.fileOperations ??= new FileOperations();
}
}
/// <summary>
/// Gets an instance to the platform service for trace logging.
/// </summary>
public IAdapterTraceLogger AdapterTraceLogger
{
get
{
return this.traceLogger ??= new AdapterTraceLogger();
}
}
/// <summary>
/// Gets an instance of the test deployment service.
/// </summary>
public ITestDeployment TestDeployment
{
get
{
return this.testDeployment ??= new TestDeployment();
}
}
/// <summary>
/// Gets an instance to the platform service for a Settings Provider.
/// </summary>
public ISettingsProvider SettingsProvider
{
get
{
return this.settingsProvider ??= new MSTestSettingsProvider();
}
}
/// <summary>
/// Gets an instance to the platform service for thread operations.
/// </summary>
public IThreadOperations ThreadOperations
{
get
{
return this.threadOperations ??= new ThreadOperations();
}
}
/// <summary>
/// Gets an instance to the platform service for reflection operations specific to a platform.
/// </summary>
public IReflectionOperations ReflectionOperations
{
get
{
return this.reflectionOperations ??= new ReflectionOperations();
}
}
/// <summary>
/// Gets or sets the instance for the platform service.
/// </summary>
internal static IPlatformServiceProvider Instance
{
get
{
return instance ??= new PlatformServiceProvider();
}
set
{
instance = value;
}
}
/// <summary>
/// Creates an instance to the platform service for a test source host.
/// </summary>
/// <param name="source">
/// The source.
/// </param>
/// <param name="runSettings">
/// The run Settings for the session.
/// </param>
/// <param name="frameworkHandle">
/// The handle to the test platform.
/// </param>
/// <returns>
/// Returns the host for the source provided.
/// </returns>
public ITestSourceHost CreateTestSourceHost(
string source,
TestPlatform.ObjectModel.Adapter.IRunSettings runSettings,
TestPlatform.ObjectModel.Adapter.IFrameworkHandle frameworkHandle)
{
var testSourceHost = new TestSourceHost(source, runSettings, frameworkHandle);
testSourceHost.SetupHost();
return testSourceHost;
}
/// <summary>
/// Gets an instance to the platform service listener who monitors trace and debug output
/// on provided text writer.
/// </summary>
/// <param name="textWriter">
/// The text Writer.
/// </param>
/// <returns>
/// The <see cref="ITraceListener"/>.
/// </returns>
public ITraceListener GetTraceListener(TextWriter textWriter)
{
return new TraceListenerWrapper(textWriter);
}
/// <summary>
/// Gets an instance to the platform service trace-listener manager which updates the output/error streams
/// with redirected streams and performs operations on the listener provided as argument.
/// </summary>
/// <param name="outputWriter">
/// The redirected output stream writer.
/// </param>
/// <param name="errorWriter">
/// The redirected error stream writer.
/// </param>
/// <returns>
/// The manager for trace listeners.
/// </returns>
public ITraceListenerManager GetTraceListenerManager(TextWriter outputWriter, TextWriter errorWriter)
{
return new TraceListenerManager(outputWriter, errorWriter);
}
/// <summary>
/// Gets the TestContext object for a platform.
/// </summary>
/// <param name="testMethod">
/// The test method.
/// </param>
/// <param name="writer">
/// The writer instance for logging.
/// </param>
/// <param name="properties">
/// The default set of properties the test context needs to be filled with.
/// </param>
/// <returns>
/// The <see cref="ITestContext"/> instance.
/// </returns>
/// <remarks>
/// This was required for compatibility reasons since the TestContext object that the V1 adapter had for desktop is not .Net Core compliant.
/// </remarks>
public ITestContext GetTestContext(ITestMethod testMethod, StringWriter writer, IDictionary<string, object> properties)
{
return new TestContextImplementation(testMethod, writer, properties);
return this.testSource ??= new TestSource();
}
}
/// <summary>
/// Gets an instance to the platform service validator for data sources for tests.
/// </summary>
public ITestDataSource TestDataSource
{
get
{
return this.testDataSource ??= new TestDataSource();
}
}
/// <summary>
/// Gets an instance to the platform service for file operations.
/// </summary>
public IFileOperations FileOperations
{
get
{
return this.fileOperations ??= new FileOperations();
}
}
/// <summary>
/// Gets an instance to the platform service for trace logging.
/// </summary>
public IAdapterTraceLogger AdapterTraceLogger
{
get
{
return this.traceLogger ??= new AdapterTraceLogger();
}
}
/// <summary>
/// Gets an instance of the test deployment service.
/// </summary>
public ITestDeployment TestDeployment
{
get
{
return this.testDeployment ??= new TestDeployment();
}
}
/// <summary>
/// Gets an instance to the platform service for a Settings Provider.
/// </summary>
public ISettingsProvider SettingsProvider
{
get
{
return this.settingsProvider ??= new MSTestSettingsProvider();
}
}
/// <summary>
/// Gets an instance to the platform service for thread operations.
/// </summary>
public IThreadOperations ThreadOperations
{
get
{
return this.threadOperations ??= new ThreadOperations();
}
}
/// <summary>
/// Gets an instance to the platform service for reflection operations specific to a platform.
/// </summary>
public IReflectionOperations ReflectionOperations
{
get
{
return this.reflectionOperations ??= new ReflectionOperations();
}
}
/// <summary>
/// Gets or sets the instance for the platform service.
/// </summary>
internal static IPlatformServiceProvider Instance
{
get
{
return instance ??= new PlatformServiceProvider();
}
set
{
instance = value;
}
}
/// <summary>
/// Creates an instance to the platform service for a test source host.
/// </summary>
/// <param name="source">
/// The source.
/// </param>
/// <param name="runSettings">
/// The run Settings for the session.
/// </param>
/// <param name="frameworkHandle">
/// The handle to the test platform.
/// </param>
/// <returns>
/// Returns the host for the source provided.
/// </returns>
public ITestSourceHost CreateTestSourceHost(
string source,
TestPlatform.ObjectModel.Adapter.IRunSettings runSettings,
TestPlatform.ObjectModel.Adapter.IFrameworkHandle frameworkHandle)
{
var testSourceHost = new TestSourceHost(source, runSettings, frameworkHandle);
testSourceHost.SetupHost();
return testSourceHost;
}
/// <summary>
/// Gets an instance to the platform service listener who monitors trace and debug output
/// on provided text writer.
/// </summary>
/// <param name="textWriter">
/// The text Writer.
/// </param>
/// <returns>
/// The <see cref="ITraceListener"/>.
/// </returns>
public ITraceListener GetTraceListener(TextWriter textWriter)
{
return new TraceListenerWrapper(textWriter);
}
/// <summary>
/// Gets an instance to the platform service trace-listener manager which updates the output/error streams
/// with redirected streams and performs operations on the listener provided as argument.
/// </summary>
/// <param name="outputWriter">
/// The redirected output stream writer.
/// </param>
/// <param name="errorWriter">
/// The redirected error stream writer.
/// </param>
/// <returns>
/// The manager for trace listeners.
/// </returns>
public ITraceListenerManager GetTraceListenerManager(TextWriter outputWriter, TextWriter errorWriter)
{
return new TraceListenerManager(outputWriter, errorWriter);
}
/// <summary>
/// Gets the TestContext object for a platform.
/// </summary>
/// <param name="testMethod">
/// The test method.
/// </param>
/// <param name="writer">
/// The writer instance for logging.
/// </param>
/// <param name="properties">
/// The default set of properties the test context needs to be filled with.
/// </param>
/// <returns>
/// The <see cref="ITestContext"/> instance.
/// </returns>
/// <remarks>
/// This was required for compatibility reasons since the TestContext object that the V1 adapter had for desktop is not .Net Core compliant.
/// </remarks>
public ITestContext GetTestContext(ITestMethod testMethod, StringWriter writer, IDictionary<string, object> properties)
{
return new TestContextImplementation(testMethod, writer, properties);
}
}

Просмотреть файл

@ -1,21 +1,20 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
namespace Microsoft.VisualStudio.TestPlatform
namespace Microsoft.VisualStudio.TestPlatform;
using System;
/// <summary>
/// Custom Attribute to specify the exact types which should be loaded from assembly
/// </summary>
[AttributeUsage(AttributeTargets.Assembly, Inherited = false, AllowMultiple = false)]
internal sealed class TestExtensionTypesAttribute : Attribute
{
using System;
/// <summary>
/// Custom Attribute to specify the exact types which should be loaded from assembly
/// </summary>
[AttributeUsage(AttributeTargets.Assembly, Inherited = false, AllowMultiple = false)]
internal sealed class TestExtensionTypesAttribute : Attribute
public TestExtensionTypesAttribute(params Type[] types)
{
public TestExtensionTypesAttribute(params Type[] types)
{
this.Types = types;
}
public Type[] Types { get; }
this.Types = types;
}
public Type[] Types { get; }
}

Просмотреть файл

@ -1,148 +1,147 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
namespace Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter
namespace Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter;
using System;
using System.IO;
using System.Xml;
using Microsoft.VisualStudio.TestPlatform.ObjectModel;
using Microsoft.VisualStudio.TestPlatform.ObjectModel.Adapter;
using Microsoft.VisualStudio.TestPlatform.ObjectModel.Utilities;
public class RunConfigurationSettings
{
using System;
using System.IO;
using System.Xml;
/// <summary>
/// The settings name.
/// </summary>
public const string SettingsName = "RunConfiguration";
using Microsoft.VisualStudio.TestPlatform.ObjectModel;
using Microsoft.VisualStudio.TestPlatform.ObjectModel.Adapter;
using Microsoft.VisualStudio.TestPlatform.ObjectModel.Utilities;
public class RunConfigurationSettings
/// <summary>
/// Initializes a new instance of the <see cref="RunConfigurationSettings"/> class.
/// </summary>
public RunConfigurationSettings()
{
/// <summary>
/// The settings name.
/// </summary>
public const string SettingsName = "RunConfiguration";
this.CollectSourceInformation = true;
}
/// <summary>
/// Initializes a new instance of the <see cref="RunConfigurationSettings"/> class.
/// </summary>
public RunConfigurationSettings()
/// <summary>
/// Gets a value indicating whether source information needs to be collected or not.
/// </summary>
public bool CollectSourceInformation { get; private set; }
/// <summary>
/// Populate adapter settings from the context
/// </summary>
/// <param name="context">
/// The discovery context that contains the runsettings.
/// </param>
/// <returns>Populated RunConfigurationSettings from the discovery context.</returns>
public static RunConfigurationSettings PopulateSettings(IDiscoveryContext context)
{
if (context == null || context.RunSettings == null || string.IsNullOrEmpty(context.RunSettings.SettingsXml))
{
this.CollectSourceInformation = true;
}
/// <summary>
/// Gets a value indicating whether source information needs to be collected or not.
/// </summary>
public bool CollectSourceInformation { get; private set; }
/// <summary>
/// Populate adapter settings from the context
/// </summary>
/// <param name="context">
/// The discovery context that contains the runsettings.
/// </param>
/// <returns>Populated RunConfigurationSettings from the discovery context.</returns>
public static RunConfigurationSettings PopulateSettings(IDiscoveryContext context)
{
if (context == null || context.RunSettings == null || string.IsNullOrEmpty(context.RunSettings.SettingsXml))
{
// This will contain default configuration settings
return new RunConfigurationSettings();
}
var settings = GetSettings(context.RunSettings.SettingsXml, SettingsName);
if (settings != null)
{
return settings;
}
// This will contain default configuration settings
return new RunConfigurationSettings();
}
/// <summary>
/// Gets the configuration settings from the xml.
/// </summary>
/// <param name="runsettingsXml"> The xml with the settings passed from the test platform. </param>
/// <param name="settingName"> The name of the settings to fetch.</param>
/// <returns> The settings if found. Null otherwise. </returns>
internal static RunConfigurationSettings GetSettings(string runsettingsXml, string settingName)
var settings = GetSettings(context.RunSettings.SettingsXml, SettingsName);
if (settings != null)
{
using (var stringReader = new StringReader(runsettingsXml))
{
XmlReader reader = XmlReader.Create(stringReader, XmlRunSettingsUtilities.ReaderSettings);
// read to the fist child
XmlReaderUtilities.ReadToRootNode(reader);
reader.ReadToNextElement();
// Read till we reach nodeName element or reach EOF
while (!string.Equals(reader.Name, settingName, StringComparison.OrdinalIgnoreCase)
&&
!reader.EOF)
{
reader.SkipToNextElement();
}
if (!reader.EOF)
{
// read nodeName element.
return ToSettings(reader.ReadSubtree());
}
}
return null;
}
/// <summary>
/// Convert the parameter xml to TestSettings
/// </summary>
/// <param name="reader">Reader to load the settings from.</param>
/// <returns>An instance of the <see cref="MSTestSettings"/> class</returns>
private static RunConfigurationSettings ToSettings(XmlReader reader)
{
ValidateArg.NotNull<XmlReader>(reader, "reader");
// Expected format of the xml is: -
//
// <Runsettings>
// <RunConfiguration>
// <CollectSourceInformation>true</CollectSourceInformation>
// </RunConfiguration>
// </Runsettings>
RunConfigurationSettings settings = new();
// Read the first element in the section
reader.ReadToNextElement();
if (!reader.IsEmptyElement)
{
reader.Read();
while (reader.NodeType == XmlNodeType.Element)
{
string elementName = reader.Name.ToUpperInvariant();
switch (elementName)
{
case "COLLECTSOURCEINFORMATION":
{
if (bool.TryParse(reader.ReadInnerXml(), out var result))
{
settings.CollectSourceInformation = result;
PlatformServiceProvider.Instance.AdapterTraceLogger.LogInfo(
"CollectSourceInformation value Found : {0} ",
result);
}
break;
}
default:
{
reader.SkipToNextElement();
break;
}
}
}
}
return settings;
}
return new RunConfigurationSettings();
}
/// <summary>
/// Gets the configuration settings from the xml.
/// </summary>
/// <param name="runsettingsXml"> The xml with the settings passed from the test platform. </param>
/// <param name="settingName"> The name of the settings to fetch.</param>
/// <returns> The settings if found. Null otherwise. </returns>
internal static RunConfigurationSettings GetSettings(string runsettingsXml, string settingName)
{
using (var stringReader = new StringReader(runsettingsXml))
{
XmlReader reader = XmlReader.Create(stringReader, XmlRunSettingsUtilities.ReaderSettings);
// read to the fist child
XmlReaderUtilities.ReadToRootNode(reader);
reader.ReadToNextElement();
// Read till we reach nodeName element or reach EOF
while (!string.Equals(reader.Name, settingName, StringComparison.OrdinalIgnoreCase)
&&
!reader.EOF)
{
reader.SkipToNextElement();
}
if (!reader.EOF)
{
// read nodeName element.
return ToSettings(reader.ReadSubtree());
}
}
return null;
}
/// <summary>
/// Convert the parameter xml to TestSettings
/// </summary>
/// <param name="reader">Reader to load the settings from.</param>
/// <returns>An instance of the <see cref="MSTestSettings"/> class</returns>
private static RunConfigurationSettings ToSettings(XmlReader reader)
{
ValidateArg.NotNull<XmlReader>(reader, "reader");
// Expected format of the xml is: -
//
// <Runsettings>
// <RunConfiguration>
// <CollectSourceInformation>true</CollectSourceInformation>
// </RunConfiguration>
// </Runsettings>
RunConfigurationSettings settings = new();
// Read the first element in the section
reader.ReadToNextElement();
if (!reader.IsEmptyElement)
{
reader.Read();
while (reader.NodeType == XmlNodeType.Element)
{
string elementName = reader.Name.ToUpperInvariant();
switch (elementName)
{
case "COLLECTSOURCEINFORMATION":
{
if (bool.TryParse(reader.ReadInnerXml(), out var result))
{
settings.CollectSourceInformation = result;
PlatformServiceProvider.Instance.AdapterTraceLogger.LogInfo(
"CollectSourceInformation value Found : {0} ",
result);
}
break;
}
default:
{
reader.SkipToNextElement();
break;
}
}
}
}
return settings;
}
}

Просмотреть файл

@ -1,136 +1,135 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
namespace Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter
namespace Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Reflection;
using Microsoft.VisualStudio.TestPlatform.ObjectModel;
using Microsoft.VisualStudio.TestPlatform.ObjectModel.Adapter;
using Microsoft.VisualStudio.TestPlatform.ObjectModel.Logging;
internal class TestMethodFilter
{
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Reflection;
using Microsoft.VisualStudio.TestPlatform.ObjectModel;
using Microsoft.VisualStudio.TestPlatform.ObjectModel.Adapter;
using Microsoft.VisualStudio.TestPlatform.ObjectModel.Logging;
/// <summary>
/// Supported properties for filtering
/// </summary>
private readonly Dictionary<string, TestProperty> supportedProperties;
internal class TestMethodFilter
internal TestMethodFilter()
{
/// <summary>
/// Supported properties for filtering
/// </summary>
private readonly Dictionary<string, TestProperty> supportedProperties;
internal TestMethodFilter()
this.supportedProperties = new Dictionary<string, TestProperty>(StringComparer.OrdinalIgnoreCase)
{
this.supportedProperties = new Dictionary<string, TestProperty>(StringComparer.OrdinalIgnoreCase)
{
[Constants.TestCategoryProperty.Label] = Constants.TestCategoryProperty,
[Constants.PriorityProperty.Label] = Constants.PriorityProperty,
[TestCaseProperties.FullyQualifiedName.Label] = TestCaseProperties.FullyQualifiedName,
[TestCaseProperties.DisplayName.Label] = TestCaseProperties.DisplayName,
[Constants.TestClassNameProperty.Label] = Constants.TestClassNameProperty
};
}
[Constants.TestCategoryProperty.Label] = Constants.TestCategoryProperty,
[Constants.PriorityProperty.Label] = Constants.PriorityProperty,
[TestCaseProperties.FullyQualifiedName.Label] = TestCaseProperties.FullyQualifiedName,
[TestCaseProperties.DisplayName.Label] = TestCaseProperties.DisplayName,
[Constants.TestClassNameProperty.Label] = Constants.TestClassNameProperty
};
}
/// <summary>
/// Returns ITestCaseFilterExpression for TestProperties supported by adapter.
/// </summary>
/// <param name="context">The current context of the run.</param>
/// <param name="logger">Handler to report test messages/start/end and results.</param>
/// <param name="filterHasError">Indicates that the filter is unsupported/has an error.</param>
/// <returns>A filter expression.</returns>
internal ITestCaseFilterExpression GetFilterExpression(IDiscoveryContext context, IMessageLogger logger, out bool filterHasError)
{
filterHasError = false;
ITestCaseFilterExpression filter = null;
if (context != null)
{
try
{
filter = (context is IRunContext) ? this.GetTestCaseFilterFromRunContext(context as IRunContext) : this.GetTestCaseFilterFromDiscoveryContext(context, logger);
}
catch (TestPlatformFormatException ex)
{
filterHasError = true;
logger.SendMessage(TestMessageLevel.Error, ex.Message);
}
}
return filter;
}
/// <summary>
/// Provides TestProperty for property name 'propertyName' as used in filter.
/// </summary>
/// <param name="propertyName">The property name.</param>
/// <returns>a TestProperty instance.</returns>
internal TestProperty PropertyProvider(string propertyName)
{
this.supportedProperties.TryGetValue(propertyName, out var testProperty);
Debug.Assert(testProperty != null, "Invalid property queried");
return testProperty;
}
/// <summary>
/// Provides value of TestProperty corresponding to property name 'propertyName' as used in filter.
/// </summary>
/// <param name="currentTest">The current test case.</param>
/// <param name="propertyName">Property name.</param>
/// <returns>The property value.</returns>
internal object PropertyValueProvider(TestCase currentTest, string propertyName)
{
if (currentTest != null && propertyName != null)
{
if (this.supportedProperties.TryGetValue(propertyName, out var testProperty))
{
// Test case might not have defined this property. In that case GetPropertyValue()
// would return default value. For filtering, if property is not defined return null.
if (currentTest.Properties.Contains(testProperty))
{
return currentTest.GetPropertyValue(testProperty);
}
}
}
return null;
}
/// <summary>
/// Gets filter expression from run context.
/// </summary>
/// <param name="context">Run context</param>
/// <returns>Filter expression.</returns>
private ITestCaseFilterExpression GetTestCaseFilterFromRunContext(IRunContext context)
{
return context.GetTestCaseFilter(this.supportedProperties.Keys, this.PropertyProvider);
}
/// <summary>
/// Gets filter expression from discovery context.
/// </summary>
/// <param name="context">Discovery context</param>
/// <param name="logger">The logger to log exception messages too.</param>
/// <returns>Filter expression.</returns>
private ITestCaseFilterExpression GetTestCaseFilterFromDiscoveryContext(IDiscoveryContext context, IMessageLogger logger)
/// <summary>
/// Returns ITestCaseFilterExpression for TestProperties supported by adapter.
/// </summary>
/// <param name="context">The current context of the run.</param>
/// <param name="logger">Handler to report test messages/start/end and results.</param>
/// <param name="filterHasError">Indicates that the filter is unsupported/has an error.</param>
/// <returns>A filter expression.</returns>
internal ITestCaseFilterExpression GetFilterExpression(IDiscoveryContext context, IMessageLogger logger, out bool filterHasError)
{
filterHasError = false;
ITestCaseFilterExpression filter = null;
if (context != null)
{
try
{
// GetTestCaseFilter is present in DiscoveryContext but not in IDiscoveryContext interface.
MethodInfo methodGetTestCaseFilter = context.GetType().GetRuntimeMethod("GetTestCaseFilter", new[] { typeof(IEnumerable<string>), typeof(Func<string, TestProperty>) });
return (ITestCaseFilterExpression)methodGetTestCaseFilter?.Invoke(context, new object[] { this.supportedProperties.Keys, (Func<string, TestProperty>)this.PropertyProvider });
filter = (context is IRunContext) ? this.GetTestCaseFilterFromRunContext(context as IRunContext) : this.GetTestCaseFilterFromDiscoveryContext(context, logger);
}
catch (Exception ex)
catch (TestPlatformFormatException ex)
{
// In case of UWP .Net Native Tool Chain compilation. Invoking methods via Reflection doesn't work, hence discovery always fails.
// Hence throwing exception only if it is of type TargetInvocationException(i.e. Method got invoked but something went wrong in GetTestCaseFilter Method)
if (ex is TargetInvocationException)
{
throw ex.InnerException;
}
filterHasError = true;
logger.SendMessage(TestMessageLevel.Error, ex.Message);
}
}
logger.SendMessage(TestMessageLevel.Warning, ex.Message);
return filter;
}
/// <summary>
/// Provides TestProperty for property name 'propertyName' as used in filter.
/// </summary>
/// <param name="propertyName">The property name.</param>
/// <returns>a TestProperty instance.</returns>
internal TestProperty PropertyProvider(string propertyName)
{
this.supportedProperties.TryGetValue(propertyName, out var testProperty);
Debug.Assert(testProperty != null, "Invalid property queried");
return testProperty;
}
/// <summary>
/// Provides value of TestProperty corresponding to property name 'propertyName' as used in filter.
/// </summary>
/// <param name="currentTest">The current test case.</param>
/// <param name="propertyName">Property name.</param>
/// <returns>The property value.</returns>
internal object PropertyValueProvider(TestCase currentTest, string propertyName)
{
if (currentTest != null && propertyName != null)
{
if (this.supportedProperties.TryGetValue(propertyName, out var testProperty))
{
// Test case might not have defined this property. In that case GetPropertyValue()
// would return default value. For filtering, if property is not defined return null.
if (currentTest.Properties.Contains(testProperty))
{
return currentTest.GetPropertyValue(testProperty);
}
}
}
return null;
}
/// <summary>
/// Gets filter expression from run context.
/// </summary>
/// <param name="context">Run context</param>
/// <returns>Filter expression.</returns>
private ITestCaseFilterExpression GetTestCaseFilterFromRunContext(IRunContext context)
{
return context.GetTestCaseFilter(this.supportedProperties.Keys, this.PropertyProvider);
}
/// <summary>
/// Gets filter expression from discovery context.
/// </summary>
/// <param name="context">Discovery context</param>
/// <param name="logger">The logger to log exception messages too.</param>
/// <returns>Filter expression.</returns>
private ITestCaseFilterExpression GetTestCaseFilterFromDiscoveryContext(IDiscoveryContext context, IMessageLogger logger)
{
try
{
// GetTestCaseFilter is present in DiscoveryContext but not in IDiscoveryContext interface.
MethodInfo methodGetTestCaseFilter = context.GetType().GetRuntimeMethod("GetTestCaseFilter", new[] { typeof(IEnumerable<string>), typeof(Func<string, TestProperty>) });
return (ITestCaseFilterExpression)methodGetTestCaseFilter?.Invoke(context, new object[] { this.supportedProperties.Keys, (Func<string, TestProperty>)this.PropertyProvider });
}
catch (Exception ex)
{
// In case of UWP .Net Native Tool Chain compilation. Invoking methods via Reflection doesn't work, hence discovery always fails.
// Hence throwing exception only if it is of type TargetInvocationException(i.e. Method got invoked but something went wrong in GetTestCaseFilter Method)
if (ex is TargetInvocationException)
{
throw ex.InnerException;
}
return null;
logger.SendMessage(TestMessageLevel.Warning, ex.Message);
}
return null;
}
}

Разница между файлами не показана из-за своего большого размера Загрузить разницу

Просмотреть файл

@ -1,44 +1,43 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
namespace Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices
namespace Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices;
using System.Collections.Generic;
using Microsoft.VisualStudio.TestPlatform.ObjectModel;
internal class Constants
{
using System.Collections.Generic;
/// <summary>
/// Constants for detecting .net framework.
/// </summary>
public const string TargetFrameworkAttributeFullName = "System.Runtime.Versioning.TargetFrameworkAttribute";
using Microsoft.VisualStudio.TestPlatform.ObjectModel;
public const string DotNetFrameWorkStringPrefix = ".NETFramework,Version=";
internal class Constants
{
/// <summary>
/// Constants for detecting .net framework.
/// </summary>
public const string TargetFrameworkAttributeFullName = "System.Runtime.Versioning.TargetFrameworkAttribute";
public const string TargetFrameworkName = "TargetFrameworkName";
public const string DotNetFrameWorkStringPrefix = ".NETFramework,Version=";
/// <summary>
/// Constants for MSTest in Portable Mode
/// </summary>
public const string PortableVsTestLocation = "PortableVsTestLocation";
public const string TargetFrameworkName = "TargetFrameworkName";
public const string PublicAssemblies = "PublicAssemblies";
/// <summary>
/// Constants for MSTest in Portable Mode
/// </summary>
public const string PortableVsTestLocation = "PortableVsTestLocation";
public const string PrivateAssemblies = "PrivateAssemblies";
public const string PublicAssemblies = "PublicAssemblies";
public static readonly TestProperty DeploymentItemsProperty = TestProperty.Register("MSTestDiscoverer.DeploymentItems", DeploymentItemsLabel, typeof(KeyValuePair<string, string>[]), TestPropertyAttributes.Hidden, typeof(TestCase));
public const string PrivateAssemblies = "PrivateAssemblies";
internal const string DllExtension = ".dll";
internal const string ExeExtension = ".exe";
internal const string PhoneAppxPackageExtension = ".appx";
public static readonly TestProperty DeploymentItemsProperty = TestProperty.Register("MSTestDiscoverer.DeploymentItems", DeploymentItemsLabel, typeof(KeyValuePair<string, string>[]), TestPropertyAttributes.Hidden, typeof(TestCase));
// These are tied to a specific VS version. Can be changed to have a list of supported version instead.
internal const string VisualStudioRootRegKey32ForDev14 = @"SOFTWARE\Microsoft\VisualStudio\" + VisualStudioVersion;
internal const string VisualStudioRootRegKey64ForDev14 = @"SOFTWARE\Wow6432Node\Microsoft\VisualStudio\" + VisualStudioVersion;
internal const string DllExtension = ".dll";
internal const string ExeExtension = ".exe";
internal const string PhoneAppxPackageExtension = ".appx";
internal const string VisualStudioVersion = "14.0";
// These are tied to a specific VS version. Can be changed to have a list of supported version instead.
internal const string VisualStudioRootRegKey32ForDev14 = @"SOFTWARE\Microsoft\VisualStudio\" + VisualStudioVersion;
internal const string VisualStudioRootRegKey64ForDev14 = @"SOFTWARE\Wow6432Node\Microsoft\VisualStudio\" + VisualStudioVersion;
internal const string VisualStudioVersion = "14.0";
private const string DeploymentItemsLabel = "DeploymentItems";
}
private const string DeploymentItemsLabel = "DeploymentItems";
}

Просмотреть файл

@ -1,168 +1,167 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
namespace Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.Data
namespace Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.Data;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Data;
using System.Data.OleDb;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Globalization;
using System.IO;
using System.Text;
using Microsoft.VisualStudio.TestPlatform.ObjectModel;
/// <summary>
/// Utility classes to access databases, and to handle quoted strings etc for comma separated value files.
/// </summary>
internal sealed class CsvDataConnection : TestDataConnection
{
using System;
using System.Collections;
using System.Collections.Generic;
using System.Data;
using System.Data.OleDb;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Globalization;
using System.IO;
using System.Text;
using Microsoft.VisualStudio.TestPlatform.ObjectModel;
// Template used to map from a filename to a DB connection string
private const string CsvConnectionTemplate = "Provider=Microsoft.Jet.OLEDB.4.0;Data Source={0};Persist Security Info=False;Extended Properties=\"text;HDR=YES;FMT=Delimited\"";
private const string CsvConnectionTemplate64 = "Provider=Microsoft.Ace.OLEDB.12.0;Data Source={0};Persist Security Info=False;Extended Properties=\"text;HDR=YES;FMT=Delimited\"";
/// <summary>
/// Utility classes to access databases, and to handle quoted strings etc for comma separated value files.
/// </summary>
internal sealed class CsvDataConnection : TestDataConnection
private readonly string fileName;
public CsvDataConnection(string fileName, List<string> dataFolders)
: base(dataFolders)
{
// Template used to map from a filename to a DB connection string
private const string CsvConnectionTemplate = "Provider=Microsoft.Jet.OLEDB.4.0;Data Source={0};Persist Security Info=False;Extended Properties=\"text;HDR=YES;FMT=Delimited\"";
private const string CsvConnectionTemplate64 = "Provider=Microsoft.Ace.OLEDB.12.0;Data Source={0};Persist Security Info=False;Extended Properties=\"text;HDR=YES;FMT=Delimited\"";
Debug.Assert(!string.IsNullOrEmpty(fileName), "fileName");
this.fileName = fileName;
}
private readonly string fileName;
public CsvDataConnection(string fileName, List<string> dataFolders)
: base(dataFolders)
private string TableName
{
get
{
Debug.Assert(!string.IsNullOrEmpty(fileName), "fileName");
this.fileName = fileName;
// Only one table based on the name of the file, with dots converted to # signs
return Path.GetFileName(this.fileName).Replace('.', '#');
}
}
private string TableName
public override List<string> GetDataTablesAndViews()
{
List<string> tableNames = new(1);
tableNames.Add(this.TableName);
return tableNames;
}
[SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Justification = "Requirement is to handle all kinds of user exceptions and message appropriately.")]
public override List<string> GetColumns(string tableName)
{
// Somewhat heavy, this could be improved, right now I simply
// read the table in then check the columns...
try
{
get
DataTable table = this.ReadTable(tableName, null);
if (table != null)
{
// Only one table based on the name of the file, with dots converted to # signs
return Path.GetFileName(this.fileName).Replace('.', '#');
}
}
public override List<string> GetDataTablesAndViews()
{
List<string> tableNames = new(1);
tableNames.Add(this.TableName);
return tableNames;
}
[SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Justification = "Requirement is to handle all kinds of user exceptions and message appropriately.")]
public override List<string> GetColumns(string tableName)
{
// Somewhat heavy, this could be improved, right now I simply
// read the table in then check the columns...
try
{
DataTable table = this.ReadTable(tableName, null);
if (table != null)
List<string> columnNames = new();
foreach (DataColumn column in table.Columns)
{
List<string> columnNames = new();
foreach (DataColumn column in table.Columns)
{
columnNames.Add(column.ColumnName);
}
return columnNames;
}
}
catch (Exception exception)
{
EqtTrace.ErrorIf(EqtTrace.IsErrorEnabled, exception.Message + " for CSV data source " + this.fileName);
}
return null;
}
[SuppressMessage("Microsoft.Reliability", "CA2000:Dispose objects before losing scope", Justification = "Untested. Leaving as-is.")]
[SuppressMessage("Microsoft.Security", "CA2100:Review SQL queries for security", Justification = "Not passed in from user.")]
public DataTable ReadTable(string tableName, IEnumerable columns, int maxRows)
{
// We specifically use OleDb to read a CSV file...
WriteDiagnostics("ReadTable: {0}", tableName);
WriteDiagnostics("Current Directory: {0}", Directory.GetCurrentDirectory());
// We better work with a full path, if nothing else, errors become easier to report
string fullPath = this.FixPath(this.fileName) ?? Path.GetFullPath(this.fileName);
// We can map simplified CSVs to an OLEDB/Text connection, then proceed as normal
using OleDbConnection connection = new();
using OleDbDataAdapter dataAdapter = new();
using OleDbCommandBuilder commandBuilder = new();
using OleDbCommand command = new();
// We have to use the name of the folder which contains the CSV file in the connection string
// If target platform is x64, then use CsvConnectionTemplate64 connection string.
if (IntPtr.Size == 8)
{
connection.ConnectionString = string.Format(CultureInfo.InvariantCulture, CsvConnectionTemplate64, Path.GetDirectoryName(fullPath));
}
else
{
connection.ConnectionString = string.Format(CultureInfo.InvariantCulture, CsvConnectionTemplate, Path.GetDirectoryName(fullPath));
}
WriteDiagnostics("Connection String: {0}", connection.ConnectionString);
// We have to open the connection now, before we try to quote
// the table name, otherwise QuoteIdentifier fails (for OleDb, go figure!)
// The connection will get closed when we dispose of it
connection.Open();
string quotedTableName = commandBuilder.QuoteIdentifier(tableName, connection);
command.Connection = connection;
string topClause;
if (maxRows >= 0)
{
topClause = string.Format(CultureInfo.InvariantCulture, " top {0}", maxRows.ToString(NumberFormatInfo.InvariantInfo));
}
else
{
topClause = string.Empty;
}
string columnsClause;
if (columns != null)
{
StringBuilder builder = new();
foreach (string columnName in columns)
{
if (builder.Length > 0)
{
builder.Append(',');
}
builder.Append(commandBuilder.QuoteIdentifier(columnName, connection));
columnNames.Add(column.ColumnName);
}
columnsClause = builder.ToString();
if (columnsClause.Length == 0)
{
columnsClause = "*";
}
return columnNames;
}
else
}
catch (Exception exception)
{
EqtTrace.ErrorIf(EqtTrace.IsErrorEnabled, exception.Message + " for CSV data source " + this.fileName);
}
return null;
}
[SuppressMessage("Microsoft.Reliability", "CA2000:Dispose objects before losing scope", Justification = "Untested. Leaving as-is.")]
[SuppressMessage("Microsoft.Security", "CA2100:Review SQL queries for security", Justification = "Not passed in from user.")]
public DataTable ReadTable(string tableName, IEnumerable columns, int maxRows)
{
// We specifically use OleDb to read a CSV file...
WriteDiagnostics("ReadTable: {0}", tableName);
WriteDiagnostics("Current Directory: {0}", Directory.GetCurrentDirectory());
// We better work with a full path, if nothing else, errors become easier to report
string fullPath = this.FixPath(this.fileName) ?? Path.GetFullPath(this.fileName);
// We can map simplified CSVs to an OLEDB/Text connection, then proceed as normal
using OleDbConnection connection = new();
using OleDbDataAdapter dataAdapter = new();
using OleDbCommandBuilder commandBuilder = new();
using OleDbCommand command = new();
// We have to use the name of the folder which contains the CSV file in the connection string
// If target platform is x64, then use CsvConnectionTemplate64 connection string.
if (IntPtr.Size == 8)
{
connection.ConnectionString = string.Format(CultureInfo.InvariantCulture, CsvConnectionTemplate64, Path.GetDirectoryName(fullPath));
}
else
{
connection.ConnectionString = string.Format(CultureInfo.InvariantCulture, CsvConnectionTemplate, Path.GetDirectoryName(fullPath));
}
WriteDiagnostics("Connection String: {0}", connection.ConnectionString);
// We have to open the connection now, before we try to quote
// the table name, otherwise QuoteIdentifier fails (for OleDb, go figure!)
// The connection will get closed when we dispose of it
connection.Open();
string quotedTableName = commandBuilder.QuoteIdentifier(tableName, connection);
command.Connection = connection;
string topClause;
if (maxRows >= 0)
{
topClause = string.Format(CultureInfo.InvariantCulture, " top {0}", maxRows.ToString(NumberFormatInfo.InvariantInfo));
}
else
{
topClause = string.Empty;
}
string columnsClause;
if (columns != null)
{
StringBuilder builder = new();
foreach (string columnName in columns)
{
if (builder.Length > 0)
{
builder.Append(',');
}
builder.Append(commandBuilder.QuoteIdentifier(columnName, connection));
}
columnsClause = builder.ToString();
if (columnsClause.Length == 0)
{
columnsClause = "*";
}
command.CommandText = string.Format(CultureInfo.InvariantCulture, "select {0} {1} from {2}", topClause, columnsClause, quotedTableName);
WriteDiagnostics("Query: " + command.CommandText);
dataAdapter.SelectCommand = command;
DataTable table = new();
table.Locale = CultureInfo.InvariantCulture;
dataAdapter.Fill(table);
return table;
}
public override DataTable ReadTable(string tableName, IEnumerable columns)
else
{
return this.ReadTable(tableName, columns, -1);
columnsClause = "*";
}
command.CommandText = string.Format(CultureInfo.InvariantCulture, "select {0} {1} from {2}", topClause, columnsClause, quotedTableName);
WriteDiagnostics("Query: " + command.CommandText);
dataAdapter.SelectCommand = command;
DataTable table = new();
table.Locale = CultureInfo.InvariantCulture;
dataAdapter.Fill(table);
return table;
}
public override DataTable ReadTable(string tableName, IEnumerable columns)
{
return this.ReadTable(tableName, columns, -1);
}
}

Просмотреть файл

@ -1,121 +1,120 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
namespace Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.Data
namespace Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.Data;
using System;
using System.Collections.Generic;
using System.Data.Odbc;
using System.Diagnostics;
/// <summary>
/// Utility classes to access databases, and to handle quoted strings etc for ODBC.
/// </summary>
internal sealed class OdbcDataConnection : TestDataConnectionSql
{
using System;
using System.Collections.Generic;
using System.Data.Odbc;
using System.Diagnostics;
private readonly bool isMSSql;
public OdbcDataConnection(string invariantProviderName, string connectionString, List<string> dataFolders)
: base(invariantProviderName, FixConnectionString(connectionString, dataFolders), dataFolders)
{
// Need open connection to get Connection.Driver.
Debug.Assert(this.IsOpen(), "The connection must be open!");
this.isMSSql = this.Connection != null && IsMSSql(this.Connection.Driver);
}
public new OdbcCommandBuilder CommandBuilder
{
get { return (OdbcCommandBuilder)base.CommandBuilder; }
}
public new OdbcConnection Connection
{
get { return (OdbcConnection)base.Connection; }
}
/// <summary>
/// Utility classes to access databases, and to handle quoted strings etc for ODBC.
/// This is overridden because we need manually get quote literals, OleDb does not fill those automatically.
/// </summary>
internal sealed class OdbcDataConnection : TestDataConnectionSql
public override void GetQuoteLiterals()
{
private readonly bool isMSSql;
this.GetQuoteLiteralsHelper();
}
public OdbcDataConnection(string invariantProviderName, string connectionString, List<string> dataFolders)
: base(invariantProviderName, FixConnectionString(connectionString, dataFolders), dataFolders)
public override string GetDefaultSchema()
{
if (this.isMSSql)
{
// Need open connection to get Connection.Driver.
Debug.Assert(this.IsOpen(), "The connection must be open!");
this.isMSSql = this.Connection != null && IsMSSql(this.Connection.Driver);
return this.GetDefaultSchemaMSSql();
}
public new OdbcCommandBuilder CommandBuilder
return base.GetDefaultSchema();
}
protected override SchemaMetaData[] GetSchemaMetaData()
{
// The following may fail for Oracle ODBC, need to test that...
SchemaMetaData data1 = new()
{
get { return (OdbcCommandBuilder)base.CommandBuilder; }
SchemaTable = "Tables",
SchemaColumn = "TABLE_SCHEM",
NameColumn = "TABLE_NAME",
TableTypeColumn = "TABLE_TYPE",
ValidTableTypes = new string[] { "TABLE", "SYSTEM TABLE" },
InvalidSchemas = null
};
SchemaMetaData data2 = new()
{
SchemaTable = "Views",
SchemaColumn = "TABLE_SCHEM",
NameColumn = "TABLE_NAME",
TableTypeColumn = "TABLE_TYPE",
ValidTableTypes = new string[] { "VIEW" },
InvalidSchemas = new string[] { "sys", "INFORMATION_SCHEMA" }
};
return new SchemaMetaData[] { data1, data2 };
}
protected override string QuoteIdentifier(string identifier)
{
Debug.Assert(!string.IsNullOrEmpty(identifier), "identifier");
return this.CommandBuilder.QuoteIdentifier(identifier, this.Connection); // Must pass connection.
}
protected override string UnquoteIdentifier(string identifier)
{
Debug.Assert(!string.IsNullOrEmpty(identifier), "identifier");
return this.CommandBuilder.UnquoteIdentifier(identifier, this.Connection); // Must pass connection.
}
// Need to fix up excel connections
private static string FixConnectionString(string connectionString, List<string> dataFolders)
{
OdbcConnectionStringBuilder builder = new(connectionString);
// only fix this for excel
if (!string.Equals(builder.Dsn, "Excel Files"))
{
return connectionString;
}
public new OdbcConnection Connection
{
get { return (OdbcConnection)base.Connection; }
}
string fileName = builder["dbq"] as string;
/// <summary>
/// This is overridden because we need manually get quote literals, OleDb does not fill those automatically.
/// </summary>
public override void GetQuoteLiterals()
if (string.IsNullOrEmpty(fileName))
{
this.GetQuoteLiteralsHelper();
return connectionString;
}
public override string GetDefaultSchema()
else
{
if (this.isMSSql)
// Fix-up magic file paths
string fixedFilePath = FixPath(fileName, dataFolders);
if (fixedFilePath != null)
{
return this.GetDefaultSchemaMSSql();
builder["dbq"] = fixedFilePath;
}
return base.GetDefaultSchema();
}
protected override SchemaMetaData[] GetSchemaMetaData()
{
// The following may fail for Oracle ODBC, need to test that...
SchemaMetaData data1 = new()
{
SchemaTable = "Tables",
SchemaColumn = "TABLE_SCHEM",
NameColumn = "TABLE_NAME",
TableTypeColumn = "TABLE_TYPE",
ValidTableTypes = new string[] { "TABLE", "SYSTEM TABLE" },
InvalidSchemas = null
};
SchemaMetaData data2 = new()
{
SchemaTable = "Views",
SchemaColumn = "TABLE_SCHEM",
NameColumn = "TABLE_NAME",
TableTypeColumn = "TABLE_TYPE",
ValidTableTypes = new string[] { "VIEW" },
InvalidSchemas = new string[] { "sys", "INFORMATION_SCHEMA" }
};
return new SchemaMetaData[] { data1, data2 };
}
protected override string QuoteIdentifier(string identifier)
{
Debug.Assert(!string.IsNullOrEmpty(identifier), "identifier");
return this.CommandBuilder.QuoteIdentifier(identifier, this.Connection); // Must pass connection.
}
protected override string UnquoteIdentifier(string identifier)
{
Debug.Assert(!string.IsNullOrEmpty(identifier), "identifier");
return this.CommandBuilder.UnquoteIdentifier(identifier, this.Connection); // Must pass connection.
}
// Need to fix up excel connections
private static string FixConnectionString(string connectionString, List<string> dataFolders)
{
OdbcConnectionStringBuilder builder = new(connectionString);
// only fix this for excel
if (!string.Equals(builder.Dsn, "Excel Files"))
{
return connectionString;
}
string fileName = builder["dbq"] as string;
if (string.IsNullOrEmpty(fileName))
{
return connectionString;
}
else
{
// Fix-up magic file paths
string fixedFilePath = FixPath(fileName, dataFolders);
if (fixedFilePath != null)
{
builder["dbq"] = fixedFilePath;
}
return builder.ConnectionString;
}
return builder.ConnectionString;
}
}
}

Просмотреть файл

@ -1,109 +1,108 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
namespace Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.Data
namespace Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.Data;
using System.Collections.Generic;
using System.Data.OleDb;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
/// <summary>
/// Utility classes to access databases, and to handle quoted strings etc for OLE DB.
/// </summary>
[SuppressMessage("Microsoft.Naming", "CA1706", Justification = "OleDb instead of Oledb to match System.Data.OleDb")]
internal sealed class OleDataConnection : TestDataConnectionSql
{
using System.Collections.Generic;
using System.Data.OleDb;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
private readonly bool isMSSql;
public OleDataConnection(string invariantProviderName, string connectionString, List<string> dataFolders)
: base(invariantProviderName, FixConnectionString(connectionString, dataFolders), dataFolders)
{
// Need open connection to get Connection.Provider.
Debug.Assert(this.IsOpen(), "The connection must be open!");
// Fill m_isMSSql.
this.isMSSql = this.Connection != null && IsMSSql(this.Connection.Provider);
}
public new OleDbCommandBuilder CommandBuilder
{
get { return (OleDbCommandBuilder)base.CommandBuilder; }
}
public new OleDbConnection Connection
{
get { return (OleDbConnection)base.Connection; }
}
/// <summary>
/// Utility classes to access databases, and to handle quoted strings etc for OLE DB.
/// This is overridden because we need manually get quote literals, OleDb does not fill those automatically.
/// </summary>
[SuppressMessage("Microsoft.Naming", "CA1706", Justification = "OleDb instead of Oledb to match System.Data.OleDb")]
internal sealed class OleDataConnection : TestDataConnectionSql
public override void GetQuoteLiterals()
{
private readonly bool isMSSql;
this.GetQuoteLiteralsHelper();
}
public OleDataConnection(string invariantProviderName, string connectionString, List<string> dataFolders)
: base(invariantProviderName, FixConnectionString(connectionString, dataFolders), dataFolders)
public override string GetDefaultSchema()
{
if (this.isMSSql)
{
// Need open connection to get Connection.Provider.
Debug.Assert(this.IsOpen(), "The connection must be open!");
// Fill m_isMSSql.
this.isMSSql = this.Connection != null && IsMSSql(this.Connection.Provider);
return this.GetDefaultSchemaMSSql();
}
public new OleDbCommandBuilder CommandBuilder
{
get { return (OleDbCommandBuilder)base.CommandBuilder; }
}
return base.GetDefaultSchema();
}
public new OleDbConnection Connection
protected override SchemaMetaData[] GetSchemaMetaData()
{
// Note, in older iterations of the code there seemed to be
// cases when we also need to look in the "views" table
// but I do not see that in my test cases
SchemaMetaData data = new()
{
get { return (OleDbConnection)base.Connection; }
}
SchemaTable = "Tables",
SchemaColumn = "TABLE_SCHEMA",
NameColumn = "TABLE_NAME",
TableTypeColumn = "TABLE_TYPE",
ValidTableTypes = new string[] { "VIEW", "TABLE" },
InvalidSchemas = null
};
return new SchemaMetaData[] { data };
}
/// <summary>
/// This is overridden because we need manually get quote literals, OleDb does not fill those automatically.
/// </summary>
public override void GetQuoteLiterals()
{
this.GetQuoteLiteralsHelper();
}
protected override string QuoteIdentifier(string identifier)
{
Debug.Assert(!string.IsNullOrEmpty(identifier), "identifier");
return this.CommandBuilder.QuoteIdentifier(identifier, this.Connection);
}
public override string GetDefaultSchema()
protected override string UnquoteIdentifier(string identifier)
{
Debug.Assert(!string.IsNullOrEmpty(identifier), "identifier");
return this.CommandBuilder.UnquoteIdentifier(identifier, this.Connection);
}
private static string FixConnectionString(string connectionString, List<string> dataFolders)
{
OleDbConnectionStringBuilder oleDbBuilder = new(connectionString);
string fileName = oleDbBuilder.DataSource;
if (string.IsNullOrEmpty(fileName))
{
if (this.isMSSql)
return connectionString;
}
else
{
// Fix-up magic file paths
string fixedFilePath = FixPath(fileName, dataFolders);
if (fixedFilePath != null)
{
return this.GetDefaultSchemaMSSql();
oleDbBuilder.DataSource = fixedFilePath;
}
return base.GetDefaultSchema();
}
protected override SchemaMetaData[] GetSchemaMetaData()
{
// Note, in older iterations of the code there seemed to be
// cases when we also need to look in the "views" table
// but I do not see that in my test cases
SchemaMetaData data = new()
{
SchemaTable = "Tables",
SchemaColumn = "TABLE_SCHEMA",
NameColumn = "TABLE_NAME",
TableTypeColumn = "TABLE_TYPE",
ValidTableTypes = new string[] { "VIEW", "TABLE" },
InvalidSchemas = null
};
return new SchemaMetaData[] { data };
}
protected override string QuoteIdentifier(string identifier)
{
Debug.Assert(!string.IsNullOrEmpty(identifier), "identifier");
return this.CommandBuilder.QuoteIdentifier(identifier, this.Connection);
}
protected override string UnquoteIdentifier(string identifier)
{
Debug.Assert(!string.IsNullOrEmpty(identifier), "identifier");
return this.CommandBuilder.UnquoteIdentifier(identifier, this.Connection);
}
private static string FixConnectionString(string connectionString, List<string> dataFolders)
{
OleDbConnectionStringBuilder oleDbBuilder = new(connectionString);
string fileName = oleDbBuilder.DataSource;
if (string.IsNullOrEmpty(fileName))
{
return connectionString;
}
else
{
// Fix-up magic file paths
string fixedFilePath = FixPath(fileName, dataFolders);
if (fixedFilePath != null)
{
oleDbBuilder.DataSource = fixedFilePath;
}
return oleDbBuilder.ConnectionString;
}
return oleDbBuilder.ConnectionString;
}
}
}

Просмотреть файл

@ -1,73 +1,72 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
namespace Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.Data
namespace Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.Data;
using System.Collections.Generic;
using System.Data.SqlClient;
/// <summary>
/// Utility classes to access databases, and to handle quoted strings etc for SQL Server.
/// </summary>
internal sealed class SqlDataConnection : TestDataConnectionSql
{
using System.Collections.Generic;
using System.Data.SqlClient;
public SqlDataConnection(string invariantProviderName, string connectionString, List<string> dataFolders)
: base(invariantProviderName, FixConnectionString(connectionString, dataFolders), dataFolders)
{
}
/// <summary>
/// Utility classes to access databases, and to handle quoted strings etc for SQL Server.
/// Returns default database schema.
/// this.Connection must be already opened.
/// </summary>
internal sealed class SqlDataConnection : TestDataConnectionSql
/// <returns>The default database schema.</returns>
public override string GetDefaultSchema()
{
public SqlDataConnection(string invariantProviderName, string connectionString, List<string> dataFolders)
: base(invariantProviderName, FixConnectionString(connectionString, dataFolders), dataFolders)
{
}
return this.GetDefaultSchemaMSSql();
}
/// <summary>
/// Returns default database schema.
/// this.Connection must be already opened.
/// </summary>
/// <returns>The default database schema.</returns>
public override string GetDefaultSchema()
protected override SchemaMetaData[] GetSchemaMetaData()
{
SchemaMetaData data = new()
{
return this.GetDefaultSchemaMSSql();
}
SchemaTable = "Tables",
SchemaColumn = "TABLE_SCHEMA",
NameColumn = "TABLE_NAME",
TableTypeColumn = "TABLE_TYPE",
ValidTableTypes = new string[] { "VIEW", "BASE TABLE" },
InvalidSchemas = null
};
return new SchemaMetaData[] { data };
}
protected override SchemaMetaData[] GetSchemaMetaData()
private static string FixConnectionString(string connectionString, List<string> dataFolders)
{
SqlConnectionStringBuilder sqlBuilder = new(connectionString);
string attachedFile = sqlBuilder.AttachDBFilename;
if (string.IsNullOrEmpty(attachedFile))
{
SchemaMetaData data = new()
// No file, so no need to rewrite the connection string
return connectionString;
}
else
{
// Force pooling off for SQL when there is a file involved
// Without this, after the connection is closed, an exclusive lock persists
// for a long time, preventing us from moving files around
sqlBuilder.Pooling = false;
// Fix-up magic file paths
string fixedFilePath = FixPath(attachedFile, dataFolders);
if (fixedFilePath != null)
{
SchemaTable = "Tables",
SchemaColumn = "TABLE_SCHEMA",
NameColumn = "TABLE_NAME",
TableTypeColumn = "TABLE_TYPE",
ValidTableTypes = new string[] { "VIEW", "BASE TABLE" },
InvalidSchemas = null
};
return new SchemaMetaData[] { data };
}
private static string FixConnectionString(string connectionString, List<string> dataFolders)
{
SqlConnectionStringBuilder sqlBuilder = new(connectionString);
string attachedFile = sqlBuilder.AttachDBFilename;
if (string.IsNullOrEmpty(attachedFile))
{
// No file, so no need to rewrite the connection string
return connectionString;
sqlBuilder.AttachDBFilename = fixedFilePath;
}
else
{
// Force pooling off for SQL when there is a file involved
// Without this, after the connection is closed, an exclusive lock persists
// for a long time, preventing us from moving files around
sqlBuilder.Pooling = false;
// Fix-up magic file paths
string fixedFilePath = FixPath(attachedFile, dataFolders);
if (fixedFilePath != null)
{
sqlBuilder.AttachDBFilename = fixedFilePath;
}
// Return modified connection string
return sqlBuilder.ConnectionString;
}
// Return modified connection string
return sqlBuilder.ConnectionString;
}
}
}

Просмотреть файл

@ -1,168 +1,167 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
namespace Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.Data
namespace Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.Data;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Data;
using System.Data.Common;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Security;
/// <summary>
/// This used to be "DataUtility", a helper class to handle quoted strings etc for different
/// data providers but the purpose has been expanded to be a general abstraction over a
/// connection, including the ability to read data and metadata (tables and columns)
/// </summary>
internal abstract class TestDataConnection : IDisposable
{
using System;
using System.Collections;
using System.Collections.Generic;
using System.Data;
using System.Data.Common;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Security;
internal const string ConnectionDirectoryKey = "|DataDirectory|\\";
private static bool? extendedDiagnosticsEnabled;
// List of places to look for files when substituting |DataDirectory|
private readonly List<string> dataFolders;
internal protected TestDataConnection(List<string> dataFolders)
{
this.dataFolders = dataFolders;
}
/// <summary>
/// This used to be "DataUtility", a helper class to handle quoted strings etc for different
/// data providers but the purpose has been expanded to be a general abstraction over a
/// connection, including the ability to read data and metadata (tables and columns)
/// Gets the connection.
/// </summary>
internal abstract class TestDataConnection : IDisposable
/// <remarks>This will only return non-null for true DB based connections (TestDataConnectionSql)</remarks>
public virtual DbConnection Connection
{
internal const string ConnectionDirectoryKey = "|DataDirectory|\\";
get { return null; }
}
private static bool? extendedDiagnosticsEnabled;
// List of places to look for files when substituting |DataDirectory|
private readonly List<string> dataFolders;
internal protected TestDataConnection(List<string> dataFolders)
private static bool ExtendedDiagnosticsEnabled
{
get
{
this.dataFolders = dataFolders;
}
/// <summary>
/// Gets the connection.
/// </summary>
/// <remarks>This will only return non-null for true DB based connections (TestDataConnectionSql)</remarks>
public virtual DbConnection Connection
{
get { return null; }
}
private static bool ExtendedDiagnosticsEnabled
{
get
if (!extendedDiagnosticsEnabled.HasValue)
{
if (!extendedDiagnosticsEnabled.HasValue)
// We use an environment variable so that we can enable this extended
// diagnostic trace
try
{
// We use an environment variable so that we can enable this extended
// diagnostic trace
try
{
string value = Environment.GetEnvironmentVariable("VSTS_DIAGNOSTICS");
extendedDiagnosticsEnabled = (value != null) && value.Contains("TestDataConnection");
}
catch (SecurityException)
{
extendedDiagnosticsEnabled = false;
}
string value = Environment.GetEnvironmentVariable("VSTS_DIAGNOSTICS");
extendedDiagnosticsEnabled = (value != null) && value.Contains("TestDataConnection");
}
return extendedDiagnosticsEnabled.Value;
}
}
/// <summary>
/// Get a list of tables and views for this connection. Filters out "system" tables
/// </summary>
/// <returns>List of names or null if error</returns>
public abstract List<string> GetDataTablesAndViews();
/// <summary>
/// Given a table name, return a list of column names
/// </summary>
/// <param name="tableName">The name of the table.</param>
/// <returns>List of names or null if error</returns>
public abstract List<string> GetColumns(string tableName);
/// <summary>
/// Read the content of a table or view into memory
/// Try to limit to columns specified, if columns is null, read all columns
/// </summary>
/// <param name="tableName">Minimally quoted table name</param>
/// <param name="columns">Array of columns</param>
/// <returns>Data table or null if error</returns>
public abstract DataTable ReadTable(string tableName, IEnumerable columns);
// It is critical that is class be disposed of properly, otherwise
// data connections may be left open. In general it is best to use create instances
// in a "using"
public virtual void Dispose()
{
GC.SuppressFinalize(this);
}
internal static bool PathNeedsFixup(string path)
{
if (!string.IsNullOrEmpty(path))
{
if (path.StartsWith(ConnectionDirectoryKey, StringComparison.Ordinal))
catch (SecurityException)
{
return true;
extendedDiagnosticsEnabled = false;
}
}
return false;
}
// Only use this if "PathNeedsFixup" returns true
internal static string GetRelativePart(string path)
{
Debug.Assert(PathNeedsFixup(path), "Incorrect path.");
return path.Substring(ConnectionDirectoryKey.Length);
}
// Check a string to see if it has our magic prefix
// and if it does, assume what follows is a relative
// path, which we then convert by making it a full path
// otherwise return null
internal static string FixPath(string path, List<string> foldersToCheck)
{
if (PathNeedsFixup(path))
{
string relPath = GetRelativePart(path);
// First bet, relative to the current directory
string fullPath = Path.GetFullPath(relPath);
if (File.Exists(fullPath))
{
return fullPath;
}
// Second bet, any on our folders foldersToCheck list
if (foldersToCheck != null)
{
foreach (string folder in foldersToCheck)
{
fullPath = Path.GetFullPath(Path.Combine(folder, relPath));
if (File.Exists(fullPath))
{
return fullPath;
}
}
}
// Finally assume the file ended up directly in the current directory.
return Path.GetFullPath(Path.GetFileName(relPath));
}
return null;
}
[Conditional("DEBUG")]
protected internal static void WriteDiagnostics(string formatString, params object[] parameters)
{
if (ExtendedDiagnosticsEnabled)
{
Debug.WriteLine("TestDataConnection: " + string.Format(CultureInfo.InvariantCulture, formatString, parameters));
}
}
protected string FixPath(string path)
{
return FixPath(path, this.dataFolders);
return extendedDiagnosticsEnabled.Value;
}
}
/// <summary>
/// Get a list of tables and views for this connection. Filters out "system" tables
/// </summary>
/// <returns>List of names or null if error</returns>
public abstract List<string> GetDataTablesAndViews();
/// <summary>
/// Given a table name, return a list of column names
/// </summary>
/// <param name="tableName">The name of the table.</param>
/// <returns>List of names or null if error</returns>
public abstract List<string> GetColumns(string tableName);
/// <summary>
/// Read the content of a table or view into memory
/// Try to limit to columns specified, if columns is null, read all columns
/// </summary>
/// <param name="tableName">Minimally quoted table name</param>
/// <param name="columns">Array of columns</param>
/// <returns>Data table or null if error</returns>
public abstract DataTable ReadTable(string tableName, IEnumerable columns);
// It is critical that is class be disposed of properly, otherwise
// data connections may be left open. In general it is best to use create instances
// in a "using"
public virtual void Dispose()
{
GC.SuppressFinalize(this);
}
internal static bool PathNeedsFixup(string path)
{
if (!string.IsNullOrEmpty(path))
{
if (path.StartsWith(ConnectionDirectoryKey, StringComparison.Ordinal))
{
return true;
}
}
return false;
}
// Only use this if "PathNeedsFixup" returns true
internal static string GetRelativePart(string path)
{
Debug.Assert(PathNeedsFixup(path), "Incorrect path.");
return path.Substring(ConnectionDirectoryKey.Length);
}
// Check a string to see if it has our magic prefix
// and if it does, assume what follows is a relative
// path, which we then convert by making it a full path
// otherwise return null
internal static string FixPath(string path, List<string> foldersToCheck)
{
if (PathNeedsFixup(path))
{
string relPath = GetRelativePart(path);
// First bet, relative to the current directory
string fullPath = Path.GetFullPath(relPath);
if (File.Exists(fullPath))
{
return fullPath;
}
// Second bet, any on our folders foldersToCheck list
if (foldersToCheck != null)
{
foreach (string folder in foldersToCheck)
{
fullPath = Path.GetFullPath(Path.Combine(folder, relPath));
if (File.Exists(fullPath))
{
return fullPath;
}
}
}
// Finally assume the file ended up directly in the current directory.
return Path.GetFullPath(Path.GetFileName(relPath));
}
return null;
}
[Conditional("DEBUG")]
protected internal static void WriteDiagnostics(string formatString, params object[] parameters)
{
if (ExtendedDiagnosticsEnabled)
{
Debug.WriteLine("TestDataConnection: " + string.Format(CultureInfo.InvariantCulture, formatString, parameters));
}
}
protected string FixPath(string path)
{
return FixPath(path, this.dataFolders);
}
}

Просмотреть файл

@ -1,85 +1,84 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
namespace Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.Data
namespace Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.Data;
using System;
using System.Collections.Generic;
using System.Diagnostics;
/// <summary>
/// Defines a class that creates TestDataConnection instances to connect to data sources.
/// </summary>
internal class TestDataConnectionFactory
{
using System;
using System.Collections.Generic;
using System.Diagnostics;
// These are not "real" providers, but are recognized by the test runtime
private const string CsvProvider = "Microsoft.VisualStudio.TestTools.DataSource.CSV";
private const string XmlProvider = "Microsoft.VisualStudio.TestTools.DataSource.XML";
/// <summary>
/// Defines a class that creates TestDataConnection instances to connect to data sources.
/// Test Specific Providers: maps provider name to provider factory that we lookup prior to using (by default) SqlTestDataConnection.
/// Notes
/// - the key (provider name is case-sensitive).
/// - other providers can be registered using RegisterProvider (per app domain).
/// </summary>
internal class TestDataConnectionFactory
private static Dictionary<string, TestDataConnectionFactory> specializedProviders = new()
{
// These are not "real" providers, but are recognized by the test runtime
private const string CsvProvider = "Microsoft.VisualStudio.TestTools.DataSource.CSV";
private const string XmlProvider = "Microsoft.VisualStudio.TestTools.DataSource.XML";
// The XML case is quite unlike all others, as there is no real DB connection at all!
{ XmlProvider, new XmlTestDataConnectionFactory() },
/// <summary>
/// Test Specific Providers: maps provider name to provider factory that we lookup prior to using (by default) SqlTestDataConnection.
/// Notes
/// - the key (provider name is case-sensitive).
/// - other providers can be registered using RegisterProvider (per app domain).
/// </summary>
private static Dictionary<string, TestDataConnectionFactory> specializedProviders = new()
// The CSV case does use a database connection, but it is hidden, and schema
// queries are highly specialized
{ CsvProvider, new CsvTestDataConnectionFactory() },
};
/// <summary>
/// Construct a wrapper for a database connection, what is actually returned indirectly depends
/// on the invariantProviderName, and the specific call knows how to deal with database variations
/// </summary>
/// <param name="invariantProviderName">The provider name.</param>
/// <param name="connectionString">The connection string.</param>
/// <param name="dataFolders">null, or a list of locations to check when fixing up connection string</param>
/// <returns>The TestDataConnection instance.</returns>
public virtual TestDataConnection Create(string invariantProviderName, string connectionString, List<string> dataFolders)
{
Debug.Assert(!string.IsNullOrEmpty(invariantProviderName), "invariantProviderName");
Debug.Assert(!string.IsNullOrEmpty(connectionString), "connectionString");
TestDataConnection.WriteDiagnostics("Create {0}, {1}", invariantProviderName, connectionString);
// Most, but not all, connections are actually database based,
// here we look for special cases
if (specializedProviders.TryGetValue(invariantProviderName, out var factory))
{
// The XML case is quite unlike all others, as there is no real DB connection at all!
{ XmlProvider, new XmlTestDataConnectionFactory() },
// The CSV case does use a database connection, but it is hidden, and schema
// queries are highly specialized
{ CsvProvider, new CsvTestDataConnectionFactory() },
};
/// <summary>
/// Construct a wrapper for a database connection, what is actually returned indirectly depends
/// on the invariantProviderName, and the specific call knows how to deal with database variations
/// </summary>
/// <param name="invariantProviderName">The provider name.</param>
/// <param name="connectionString">The connection string.</param>
/// <param name="dataFolders">null, or a list of locations to check when fixing up connection string</param>
/// <returns>The TestDataConnection instance.</returns>
public virtual TestDataConnection Create(string invariantProviderName, string connectionString, List<string> dataFolders)
{
Debug.Assert(!string.IsNullOrEmpty(invariantProviderName), "invariantProviderName");
Debug.Assert(!string.IsNullOrEmpty(connectionString), "connectionString");
TestDataConnection.WriteDiagnostics("Create {0}, {1}", invariantProviderName, connectionString);
// Most, but not all, connections are actually database based,
// here we look for special cases
if (specializedProviders.TryGetValue(invariantProviderName, out var factory))
{
Debug.Assert(factory != null, "factory");
return factory.Create(invariantProviderName, connectionString, dataFolders);
}
else
{
// Default is to use a conventional SQL based connection, this create method in turn
// handles variations between DB based implementations
return TestDataConnectionSql.Create(invariantProviderName, connectionString, dataFolders);
}
Debug.Assert(factory != null, "factory");
return factory.Create(invariantProviderName, connectionString, dataFolders);
}
#region TestDataConnectionFactories
private class XmlTestDataConnectionFactory : TestDataConnectionFactory
else
{
public override TestDataConnection Create(string invariantProviderName, string connectionString, List<string> dataFolders)
{
return new XmlDataConnection(connectionString, dataFolders);
}
// Default is to use a conventional SQL based connection, this create method in turn
// handles variations between DB based implementations
return TestDataConnectionSql.Create(invariantProviderName, connectionString, dataFolders);
}
private class CsvTestDataConnectionFactory : TestDataConnectionFactory
{
public override TestDataConnection Create(string invariantProviderName, string connectionString, List<string> dataFolders)
{
return new CsvDataConnection(connectionString, dataFolders);
}
}
#endregion TestDataConnectionFactories
}
#region TestDataConnectionFactories
private class XmlTestDataConnectionFactory : TestDataConnectionFactory
{
public override TestDataConnection Create(string invariantProviderName, string connectionString, List<string> dataFolders)
{
return new XmlDataConnection(connectionString, dataFolders);
}
}
private class CsvTestDataConnectionFactory : TestDataConnectionFactory
{
public override TestDataConnection Create(string invariantProviderName, string connectionString, List<string> dataFolders)
{
return new CsvDataConnection(connectionString, dataFolders);
}
}
#endregion TestDataConnectionFactories
}

Разница между файлами не показана из-за своего большого размера Загрузить разницу

Просмотреть файл

@ -1,129 +1,128 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
namespace Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.Data
namespace Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.Data;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Data;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Globalization;
using System.IO;
using System.Security;
using System.Xml;
using Microsoft.VisualStudio.TestPlatform.ObjectModel;
/// <summary>
/// Utility classes to access databases, and to handle quoted strings etc for XML data.
/// </summary>
internal sealed class XmlDataConnection : TestDataConnection
{
using System;
using System.Collections;
using System.Collections.Generic;
using System.Data;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Globalization;
using System.IO;
using System.Security;
using System.Xml;
using Microsoft.VisualStudio.TestPlatform.ObjectModel;
private string fileName;
/// <summary>
/// Utility classes to access databases, and to handle quoted strings etc for XML data.
/// </summary>
internal sealed class XmlDataConnection : TestDataConnection
public XmlDataConnection(string fileName, List<string> dataFolders)
: base(dataFolders)
{
private string fileName;
Debug.Assert(!string.IsNullOrEmpty(fileName), "fileName");
this.fileName = fileName;
}
public XmlDataConnection(string fileName, List<string> dataFolders)
: base(dataFolders)
public override List<string> GetDataTablesAndViews()
{
DataSet dataSet = this.LoadDataSet(true);
if (dataSet != null)
{
Debug.Assert(!string.IsNullOrEmpty(fileName), "fileName");
this.fileName = fileName;
List<string> tableNames = new();
int tableCount = dataSet.Tables.Count;
for (int i = 0; i < tableCount; i++)
{
DataTable table = dataSet.Tables[i];
tableNames.Add(table.TableName);
}
return tableNames;
}
public override List<string> GetDataTablesAndViews()
else
{
DataSet dataSet = this.LoadDataSet(true);
if (dataSet != null)
{
List<string> tableNames = new();
int tableCount = dataSet.Tables.Count;
for (int i = 0; i < tableCount; i++)
{
DataTable table = dataSet.Tables[i];
tableNames.Add(table.TableName);
}
return tableNames;
}
else
{
return null;
}
}
public override List<string> GetColumns(string tableName)
{
DataSet dataSet = this.LoadDataSet(true);
if (dataSet != null)
{
DataTable table = dataSet.Tables[tableName];
if (table != null)
{
List<string> columnNames = new();
foreach (DataColumn column in table.Columns)
{
// Only show "normal" columns, we try to hide derived columns used as part
// of the support for relations
if (column.ColumnMapping != MappingType.Hidden)
{
columnNames.Add(column.ColumnName);
}
}
return columnNames;
}
}
return null;
}
public override DataTable ReadTable(string tableName, IEnumerable columns)
{
// Reading XML is very simple...
// We do not ask it to just load a specific table, or specific columns
// so there is inefficiency since we will reload the entire file
// once for every table in it. Oh well. Reading XML is pretty quick
// compared to other forms of data source
DataSet ds = this.LoadDataSet(false);
return ds != null ? ds.Tables[tableName] : null;
}
[SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Justification = "Requirement is to handle all kinds of user exceptions and message appropriately.")]
[SuppressMessage("Microsoft.Reliability", "CA2000:Dispose objects before losing scope", Justification = "Un-tested. Preserving behavior.")]
private DataSet LoadDataSet(bool schemaOnly)
{
try
{
DataSet dataSet = new();
dataSet.Locale = CultureInfo.CurrentCulture;
string path = this.FixPath(this.fileName) ?? Path.GetFullPath(this.fileName);
if (schemaOnly)
{
dataSet.ReadXmlSchema(path);
}
else
{
dataSet.ReadXml(path);
}
return dataSet;
}
catch (SecurityException securityException)
{
EqtTrace.ErrorIf(EqtTrace.IsErrorEnabled, securityException.Message + " for XML data source " + this.fileName);
}
catch (XmlException xmlException)
{
EqtTrace.ErrorIf(EqtTrace.IsErrorEnabled, xmlException.Message + " for XML data source " + this.fileName);
}
catch (Exception exception)
{
// Yes, we get other exceptions too!
EqtTrace.ErrorIf(EqtTrace.IsErrorEnabled, exception.Message + " for XML data source " + this.fileName);
}
return null;
}
}
public override List<string> GetColumns(string tableName)
{
DataSet dataSet = this.LoadDataSet(true);
if (dataSet != null)
{
DataTable table = dataSet.Tables[tableName];
if (table != null)
{
List<string> columnNames = new();
foreach (DataColumn column in table.Columns)
{
// Only show "normal" columns, we try to hide derived columns used as part
// of the support for relations
if (column.ColumnMapping != MappingType.Hidden)
{
columnNames.Add(column.ColumnName);
}
}
return columnNames;
}
}
return null;
}
public override DataTable ReadTable(string tableName, IEnumerable columns)
{
// Reading XML is very simple...
// We do not ask it to just load a specific table, or specific columns
// so there is inefficiency since we will reload the entire file
// once for every table in it. Oh well. Reading XML is pretty quick
// compared to other forms of data source
DataSet ds = this.LoadDataSet(false);
return ds != null ? ds.Tables[tableName] : null;
}
[SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Justification = "Requirement is to handle all kinds of user exceptions and message appropriately.")]
[SuppressMessage("Microsoft.Reliability", "CA2000:Dispose objects before losing scope", Justification = "Un-tested. Preserving behavior.")]
private DataSet LoadDataSet(bool schemaOnly)
{
try
{
DataSet dataSet = new();
dataSet.Locale = CultureInfo.CurrentCulture;
string path = this.FixPath(this.fileName) ?? Path.GetFullPath(this.fileName);
if (schemaOnly)
{
dataSet.ReadXmlSchema(path);
}
else
{
dataSet.ReadXml(path);
}
return dataSet;
}
catch (SecurityException securityException)
{
EqtTrace.ErrorIf(EqtTrace.IsErrorEnabled, securityException.Message + " for XML data source " + this.fileName);
}
catch (XmlException xmlException)
{
EqtTrace.ErrorIf(EqtTrace.IsErrorEnabled, xmlException.Message + " for XML data source " + this.fileName);
}
catch (Exception exception)
{
// Yes, we get other exceptions too!
EqtTrace.ErrorIf(EqtTrace.IsErrorEnabled, exception.Message + " for XML data source " + this.fileName);
}
return null;
}
}

Просмотреть файл

@ -1,266 +1,265 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
namespace Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.Deployment
namespace Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.Deployment;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Reflection;
using Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.Utilities;
using Microsoft.VisualStudio.TestPlatform.ObjectModel;
/// <summary>
/// Utility function for Assembly related info
/// The caller is supposed to create AppDomain and create instance of given class in there.
/// </summary>
internal class AssemblyLoadWorker : MarshalByRefObject
{
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Reflection;
using Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.Utilities;
using Microsoft.VisualStudio.TestPlatform.ObjectModel;
private readonly IAssemblyUtility assemblyUtility;
public AssemblyLoadWorker()
: this(new AssemblyUtility())
{
}
internal AssemblyLoadWorker(IAssemblyUtility assemblyUtility)
{
this.assemblyUtility = assemblyUtility;
}
/// <summary>
/// Utility function for Assembly related info
/// The caller is supposed to create AppDomain and create instance of given class in there.
/// Returns the full path to the dependent assemblies of the parameter managed assembly recursively.
/// It does not report GAC assemblies.
/// </summary>
internal class AssemblyLoadWorker : MarshalByRefObject
/// <param name="assemblyPath"> Path to the assembly file to load from. </param>
/// <param name="warnings"> The warnings. </param>
/// <returns> Full path to dependent assemblies. </returns>
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Justification = "Requirement is to handle all kinds of user exceptions and message appropriately.")]
public string[] GetFullPathToDependentAssemblies(string assemblyPath, out IList<string> warnings)
{
private readonly IAssemblyUtility assemblyUtility;
Debug.Assert(!string.IsNullOrEmpty(assemblyPath), "assemblyPath");
public AssemblyLoadWorker()
: this(new AssemblyUtility())
warnings = new List<string>();
Assembly assembly = null;
try
{
EqtTrace.Verbose($"AssemblyLoadWorker.GetFullPathToDependentAssemblies: Reflection loading {assemblyPath}.");
// First time we load in LoadFromContext to avoid issues.
assembly = this.assemblyUtility.ReflectionOnlyLoadFrom(assemblyPath);
}
catch (Exception ex)
{
EqtTrace.Error($"AssemblyLoadWorker.GetFullPathToDependentAssemblies: Reflection loading of {assemblyPath} failed:");
EqtTrace.Error(ex);
warnings.Add(ex.Message);
return new string[0]; // Otherwise just return no dependencies.
}
internal AssemblyLoadWorker(IAssemblyUtility assemblyUtility)
{
this.assemblyUtility = assemblyUtility;
}
Debug.Assert(assembly != null, "assembly");
/// <summary>
/// Returns the full path to the dependent assemblies of the parameter managed assembly recursively.
/// It does not report GAC assemblies.
/// </summary>
/// <param name="assemblyPath"> Path to the assembly file to load from. </param>
/// <param name="warnings"> The warnings. </param>
/// <returns> Full path to dependent assemblies. </returns>
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Justification = "Requirement is to handle all kinds of user exceptions and message appropriately.")]
public string[] GetFullPathToDependentAssemblies(string assemblyPath, out IList<string> warnings)
{
Debug.Assert(!string.IsNullOrEmpty(assemblyPath), "assemblyPath");
List<string> result = new();
HashSet<string> visitedAssemblies = new();
warnings = new List<string>();
Assembly assembly = null;
visitedAssemblies.Add(assembly.FullName);
this.ProcessChildren(assembly, result, visitedAssemblies, warnings);
return result.ToArray();
}
/// <summary>
/// initialize the lifetime service.
/// </summary>
/// <returns> The <see cref="object"/>. </returns>
public override object InitializeLifetimeService()
{
// Infinite.
return null;
}
/// <summary>
/// Get the target dotNet framework string for the assembly
/// </summary>
/// <param name="path">Path of the assembly file</param>
/// <returns> String representation of the target dotNet framework e.g. .NETFramework,Version=v4.0 </returns>
internal string GetTargetFrameworkVersionStringFromPath(string path)
{
if (File.Exists(path))
{
try
{
EqtTrace.Verbose($"AssemblyLoadWorker.GetFullPathToDependentAssemblies: Reflection loading {assemblyPath}.");
// First time we load in LoadFromContext to avoid issues.
assembly = this.assemblyUtility.ReflectionOnlyLoadFrom(assemblyPath);
Assembly a = this.assemblyUtility.ReflectionOnlyLoadFrom(path);
return this.GetTargetFrameworkStringFromAssembly(a);
}
catch (BadImageFormatException)
{
if (EqtTrace.IsErrorEnabled)
{
EqtTrace.Error("AssemblyHelper:GetTargetFrameworkVersionString() caught BadImageFormatException. Falling to native binary.");
}
}
catch (Exception ex)
{
EqtTrace.Error($"AssemblyLoadWorker.GetFullPathToDependentAssemblies: Reflection loading of {assemblyPath} failed:");
EqtTrace.Error(ex);
warnings.Add(ex.Message);
return new string[0]; // Otherwise just return no dependencies.
}
Debug.Assert(assembly != null, "assembly");
List<string> result = new();
HashSet<string> visitedAssemblies = new();
visitedAssemblies.Add(assembly.FullName);
this.ProcessChildren(assembly, result, visitedAssemblies, warnings);
return result.ToArray();
}
/// <summary>
/// initialize the lifetime service.
/// </summary>
/// <returns> The <see cref="object"/>. </returns>
public override object InitializeLifetimeService()
{
// Infinite.
return null;
}
/// <summary>
/// Get the target dotNet framework string for the assembly
/// </summary>
/// <param name="path">Path of the assembly file</param>
/// <returns> String representation of the target dotNet framework e.g. .NETFramework,Version=v4.0 </returns>
internal string GetTargetFrameworkVersionStringFromPath(string path)
{
if (File.Exists(path))
{
try
if (EqtTrace.IsErrorEnabled)
{
Assembly a = this.assemblyUtility.ReflectionOnlyLoadFrom(path);
return this.GetTargetFrameworkStringFromAssembly(a);
}
catch (BadImageFormatException)
{
if (EqtTrace.IsErrorEnabled)
{
EqtTrace.Error("AssemblyHelper:GetTargetFrameworkVersionString() caught BadImageFormatException. Falling to native binary.");
}
}
catch (Exception ex)
{
if (EqtTrace.IsErrorEnabled)
{
EqtTrace.Error("AssemblyHelper:GetTargetFrameworkVersionString() Returning default. Unhandled exception: {0}.", ex);
}
}
}
return string.Empty;
}
/// <summary>
/// Get the target dot net framework string for the assembly
/// </summary>
/// <param name="assembly">Assembly from which target framework has to find</param>
/// <returns>String representation of the target dot net framework e.g. .NETFramework,Version=v4.0 </returns>
private string GetTargetFrameworkStringFromAssembly(Assembly assembly)
{
string dotNetVersion = string.Empty;
foreach (CustomAttributeData data in CustomAttributeData.GetCustomAttributes(assembly))
{
if (data?.NamedArguments?.Count > 0)
{
var declaringType = data.NamedArguments[0].MemberInfo.DeclaringType;
if (declaringType != null)
{
string attributeName = declaringType.FullName;
if (string.Equals(
attributeName,
PlatformServices.Constants.TargetFrameworkAttributeFullName,
StringComparison.OrdinalIgnoreCase))
{
dotNetVersion = data.ConstructorArguments[0].Value.ToString();
break;
}
}
}
}
return dotNetVersion;
}
/// <summary>
/// Processes references, modules, satellites.
/// Fills parameter results.
/// </summary>
/// <param name="assembly"> The assembly. </param>
/// <param name="result"> The result. </param>
/// <param name="visitedAssemblies"> The visited Assemblies. </param>
/// <param name="warnings"> The warnings. </param>
private void ProcessChildren(Assembly assembly, IList<string> result, ISet<string> visitedAssemblies, IList<string> warnings)
{
Debug.Assert(assembly != null, "assembly");
EqtTrace.Verbose($"AssemblyLoadWorker.GetFullPathToDependentAssemblies: Processing assembly {assembly.FullName}.");
foreach (AssemblyName reference in assembly.GetReferencedAssemblies())
{
this.GetDependentAssembliesInternal(reference.FullName, result, visitedAssemblies, warnings);
}
// Take care of .netmodule's.
var modules = new Module[0];
try
{
EqtTrace.Verbose($"AssemblyLoadWorker.GetFullPathToDependentAssemblies: Getting modules of {assembly.FullName}.");
modules = assembly.GetModules();
}
catch (FileNotFoundException e)
{
string warning = string.Format(CultureInfo.CurrentCulture, Resource.MissingDeploymentDependency, e.FileName, e.Message);
warnings.Add(warning);
return;
}
// Assembly.GetModules() returns all modules including main one.
if (modules.Length > 1)
{
// The modules must be in the same directory as assembly that references them.
foreach (Module m in modules)
{
// Module.Name ~ MyModule.netmodule. Module.FullyQualifiedName ~ C:\dir\MyModule.netmodule.
string shortName = m.Name;
// Note that "MyModule" may contain dots:
int dotIndex = shortName.LastIndexOf('.');
if (dotIndex > 0)
{
shortName = shortName.Substring(0, dotIndex);
}
if (string.Equals(shortName, assembly.GetName().Name, StringComparison.OrdinalIgnoreCase))
{
// This is main assembly module.
continue;
}
if (!visitedAssemblies.Add(m.Name))
{
// The assembly was already in the set, meaning that we already visited it.
continue;
}
if (!File.Exists(m.FullyQualifiedName))
{
string warning = string.Format(CultureInfo.CurrentCulture, Resource.MissingDeploymentDependencyWithoutReason, m.FullyQualifiedName);
warnings.Add(warning);
continue;
}
result.Add(m.FullyQualifiedName);
EqtTrace.Error("AssemblyHelper:GetTargetFrameworkVersionString() Returning default. Unhandled exception: {0}.", ex);
}
}
}
/// <summary>
/// Loads in Load Context. Fills private members.
/// </summary>
/// <param name="assemblyString"> Full or partial assembly name passed to Assembly.Load. </param>
/// <param name="result"> The result. </param>
/// <param name="visitedAssemblies"> The visited Assemblies. </param>
/// <param name="warnings"> The warnings. </param>
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Justification = "Requirement is to handle all kinds of user exceptions and message appropriately.")]
private void GetDependentAssembliesInternal(string assemblyString, IList<string> result, ISet<string> visitedAssemblies, IList<string> warnings)
return string.Empty;
}
/// <summary>
/// Get the target dot net framework string for the assembly
/// </summary>
/// <param name="assembly">Assembly from which target framework has to find</param>
/// <returns>String representation of the target dot net framework e.g. .NETFramework,Version=v4.0 </returns>
private string GetTargetFrameworkStringFromAssembly(Assembly assembly)
{
string dotNetVersion = string.Empty;
foreach (CustomAttributeData data in CustomAttributeData.GetCustomAttributes(assembly))
{
Debug.Assert(!string.IsNullOrEmpty(assemblyString), "assemblyString");
if (!visitedAssemblies.Add(assemblyString))
if (data?.NamedArguments?.Count > 0)
{
// The assembly was already in the hashset, so we already visited it.
return;
var declaringType = data.NamedArguments[0].MemberInfo.DeclaringType;
if (declaringType != null)
{
string attributeName = declaringType.FullName;
if (string.Equals(
attributeName,
PlatformServices.Constants.TargetFrameworkAttributeFullName,
StringComparison.OrdinalIgnoreCase))
{
dotNetVersion = data.ConstructorArguments[0].Value.ToString();
break;
}
}
}
}
Assembly assembly = null;
try
return dotNetVersion;
}
/// <summary>
/// Processes references, modules, satellites.
/// Fills parameter results.
/// </summary>
/// <param name="assembly"> The assembly. </param>
/// <param name="result"> The result. </param>
/// <param name="visitedAssemblies"> The visited Assemblies. </param>
/// <param name="warnings"> The warnings. </param>
private void ProcessChildren(Assembly assembly, IList<string> result, ISet<string> visitedAssemblies, IList<string> warnings)
{
Debug.Assert(assembly != null, "assembly");
EqtTrace.Verbose($"AssemblyLoadWorker.GetFullPathToDependentAssemblies: Processing assembly {assembly.FullName}.");
foreach (AssemblyName reference in assembly.GetReferencedAssemblies())
{
this.GetDependentAssembliesInternal(reference.FullName, result, visitedAssemblies, warnings);
}
// Take care of .netmodule's.
var modules = new Module[0];
try
{
EqtTrace.Verbose($"AssemblyLoadWorker.GetFullPathToDependentAssemblies: Getting modules of {assembly.FullName}.");
modules = assembly.GetModules();
}
catch (FileNotFoundException e)
{
string warning = string.Format(CultureInfo.CurrentCulture, Resource.MissingDeploymentDependency, e.FileName, e.Message);
warnings.Add(warning);
return;
}
// Assembly.GetModules() returns all modules including main one.
if (modules.Length > 1)
{
// The modules must be in the same directory as assembly that references them.
foreach (Module m in modules)
{
EqtTrace.Verbose($"AssemblyLoadWorker.GetDependentAssembliesInternal: Reflection loading {assemblyString}.");
// Module.Name ~ MyModule.netmodule. Module.FullyQualifiedName ~ C:\dir\MyModule.netmodule.
string shortName = m.Name;
string postPolicyAssembly = AppDomain.CurrentDomain.ApplyPolicy(assemblyString);
Debug.Assert(!string.IsNullOrEmpty(postPolicyAssembly), "postPolicyAssembly");
// Note that "MyModule" may contain dots:
int dotIndex = shortName.LastIndexOf('.');
if (dotIndex > 0)
{
shortName = shortName.Substring(0, dotIndex);
}
assembly = this.assemblyUtility.ReflectionOnlyLoad(postPolicyAssembly);
visitedAssemblies.Add(assembly.FullName); // Just in case.
if (string.Equals(shortName, assembly.GetName().Name, StringComparison.OrdinalIgnoreCase))
{
// This is main assembly module.
continue;
}
if (!visitedAssemblies.Add(m.Name))
{
// The assembly was already in the set, meaning that we already visited it.
continue;
}
if (!File.Exists(m.FullyQualifiedName))
{
string warning = string.Format(CultureInfo.CurrentCulture, Resource.MissingDeploymentDependencyWithoutReason, m.FullyQualifiedName);
warnings.Add(warning);
continue;
}
result.Add(m.FullyQualifiedName);
}
catch (Exception ex)
{
EqtTrace.Error($"AssemblyLoadWorker.GetDependentAssembliesInternal: Reflection loading {assemblyString} failed:.");
EqtTrace.Error(ex);
string warning = string.Format(CultureInfo.CurrentCulture, Resource.MissingDeploymentDependency, assemblyString, ex.Message);
warnings.Add(warning);
return;
}
EqtTrace.Verbose($"AssemblyLoadWorker.GetDependentAssembliesInternal: Assembly {assemblyString} was added as dependency.");
result.Add(assembly.Location);
this.ProcessChildren(assembly, result, visitedAssemblies, warnings);
}
}
/// <summary>
/// Loads in Load Context. Fills private members.
/// </summary>
/// <param name="assemblyString"> Full or partial assembly name passed to Assembly.Load. </param>
/// <param name="result"> The result. </param>
/// <param name="visitedAssemblies"> The visited Assemblies. </param>
/// <param name="warnings"> The warnings. </param>
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Justification = "Requirement is to handle all kinds of user exceptions and message appropriately.")]
private void GetDependentAssembliesInternal(string assemblyString, IList<string> result, ISet<string> visitedAssemblies, IList<string> warnings)
{
Debug.Assert(!string.IsNullOrEmpty(assemblyString), "assemblyString");
if (!visitedAssemblies.Add(assemblyString))
{
// The assembly was already in the hashset, so we already visited it.
return;
}
Assembly assembly = null;
try
{
EqtTrace.Verbose($"AssemblyLoadWorker.GetDependentAssembliesInternal: Reflection loading {assemblyString}.");
string postPolicyAssembly = AppDomain.CurrentDomain.ApplyPolicy(assemblyString);
Debug.Assert(!string.IsNullOrEmpty(postPolicyAssembly), "postPolicyAssembly");
assembly = this.assemblyUtility.ReflectionOnlyLoad(postPolicyAssembly);
visitedAssemblies.Add(assembly.FullName); // Just in case.
}
catch (Exception ex)
{
EqtTrace.Error($"AssemblyLoadWorker.GetDependentAssembliesInternal: Reflection loading {assemblyString} failed:.");
EqtTrace.Error(ex);
string warning = string.Format(CultureInfo.CurrentCulture, Resource.MissingDeploymentDependency, assemblyString, ex.Message);
warnings.Add(warning);
return;
}
EqtTrace.Verbose($"AssemblyLoadWorker.GetDependentAssembliesInternal: Assembly {assemblyString} was added as dependency.");
result.Add(assembly.Location);
this.ProcessChildren(assembly, result, visitedAssemblies, warnings);
}
}

Просмотреть файл

@ -1,78 +1,77 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
namespace Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.Deployment
namespace Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.Deployment;
using System;
using System.Diagnostics;
using System.IO;
/// <summary>
/// The test run directories.
/// </summary>
[Serializable]
#pragma warning disable SA1649 // File name must match first type name
public class TestRunDirectories
#pragma warning restore SA1649 // File name must match first type name
{
using System;
using System.Diagnostics;
using System.IO;
/// <summary>
/// The default deployment root directory. We do not want to localize it.
/// </summary>
internal const string DefaultDeploymentRootDirectory = "TestResults";
/// <summary>
/// The test run directories.
/// The deployment in directory suffix.
/// </summary>
[Serializable]
#pragma warning disable SA1649 // File name must match first type name
public class TestRunDirectories
#pragma warning restore SA1649 // File name must match first type name
internal const string DeploymentInDirectorySuffix = "In";
/// <summary>
/// The deployment out directory suffix.
/// </summary>
internal const string DeploymentOutDirectorySuffix = "Out";
public TestRunDirectories(string rootDirectory)
{
/// <summary>
/// The default deployment root directory. We do not want to localize it.
/// </summary>
internal const string DefaultDeploymentRootDirectory = "TestResults";
Debug.Assert(!string.IsNullOrEmpty(rootDirectory), "rootDirectory");
/// <summary>
/// The deployment in directory suffix.
/// </summary>
internal const string DeploymentInDirectorySuffix = "In";
this.RootDeploymentDirectory = rootDirectory;
}
/// <summary>
/// The deployment out directory suffix.
/// </summary>
internal const string DeploymentOutDirectorySuffix = "Out";
/// <summary>
/// Gets or sets the root deployment directory
/// </summary>
public string RootDeploymentDirectory { get; set; }
public TestRunDirectories(string rootDirectory)
/// <summary>
/// Gets the In directory
/// </summary>
public string InDirectory
{
get
{
Debug.Assert(!string.IsNullOrEmpty(rootDirectory), "rootDirectory");
this.RootDeploymentDirectory = rootDirectory;
return Path.Combine(this.RootDeploymentDirectory, DeploymentInDirectorySuffix);
}
}
/// <summary>
/// Gets or sets the root deployment directory
/// </summary>
public string RootDeploymentDirectory { get; set; }
/// <summary>
/// Gets the In directory
/// </summary>
public string InDirectory
/// <summary>
/// Gets the Out directory
/// </summary>
public string OutDirectory
{
get
{
get
{
return Path.Combine(this.RootDeploymentDirectory, DeploymentInDirectorySuffix);
}
return Path.Combine(this.RootDeploymentDirectory, DeploymentOutDirectorySuffix);
}
}
/// <summary>
/// Gets the Out directory
/// </summary>
public string OutDirectory
/// <summary>
/// Gets In\MachineName directory
/// </summary>
public string InMachineNameDirectory
{
get
{
get
{
return Path.Combine(this.RootDeploymentDirectory, DeploymentOutDirectorySuffix);
}
}
/// <summary>
/// Gets In\MachineName directory
/// </summary>
public string InMachineNameDirectory
{
get
{
return Path.Combine(Path.Combine(this.RootDeploymentDirectory, DeploymentInDirectorySuffix), Environment.MachineName);
}
return Path.Combine(Path.Combine(this.RootDeploymentDirectory, DeploymentInDirectorySuffix), Environment.MachineName);
}
}
}

Просмотреть файл

@ -4,7 +4,6 @@
<PropertyGroup>
<TargetFrameworks>$(NetFrameworkMinimum)</TargetFrameworks>
<TreatWarningsAsErrors>True</TreatWarningsAsErrors>
<LangVersion>Latest</LangVersion>
</PropertyGroup>
<PropertyGroup>

Просмотреть файл

@ -1,62 +1,61 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
namespace Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices
{
using Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.Interface;
using Microsoft.VisualStudio.TestPlatform.ObjectModel;
namespace Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices;
using Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.Interface;
using Microsoft.VisualStudio.TestPlatform.ObjectModel;
#pragma warning disable SA1649 // SA1649FileNameMustMatchTypeName
/// <summary>
/// A service to log any trace messages from the adapter that would be shown in *.TpTrace files.
/// </summary>
public class AdapterTraceLogger : IAdapterTraceLogger
{
/// <summary>
/// A service to log any trace messages from the adapter that would be shown in *.TpTrace files.
/// Log an error in a given format.
/// </summary>
public class AdapterTraceLogger : IAdapterTraceLogger
/// <param name="format"> The format. </param>
/// <param name="args"> The args. </param>
public void LogError(string format, params object[] args)
{
/// <summary>
/// Log an error in a given format.
/// </summary>
/// <param name="format"> The format. </param>
/// <param name="args"> The args. </param>
public void LogError(string format, params object[] args)
if (EqtTrace.IsErrorEnabled)
{
if (EqtTrace.IsErrorEnabled)
{
EqtTrace.Error(this.PrependAdapterName(format), args);
}
}
/// <summary>
/// Log a warning in a given format.
/// </summary>
/// <param name="format"> The format. </param>
/// <param name="args"> The args. </param>
public void LogWarning(string format, params object[] args)
{
if (EqtTrace.IsWarningEnabled)
{
EqtTrace.Warning(this.PrependAdapterName(format), args);
}
}
/// <summary>
/// Log an information message in a given format.
/// </summary>
/// <param name="format"> The format. </param>
/// <param name="args"> The args. </param>
public void LogInfo(string format, params object[] args)
{
if (EqtTrace.IsInfoEnabled)
{
EqtTrace.Info(this.PrependAdapterName(format), args);
}
}
private string PrependAdapterName(string format)
{
return $"MSTest - {format}";
EqtTrace.Error(this.PrependAdapterName(format), args);
}
}
#pragma warning restore SA1649 // SA1649FileNameMustMatchTypeName
/// <summary>
/// Log a warning in a given format.
/// </summary>
/// <param name="format"> The format. </param>
/// <param name="args"> The args. </param>
public void LogWarning(string format, params object[] args)
{
if (EqtTrace.IsWarningEnabled)
{
EqtTrace.Warning(this.PrependAdapterName(format), args);
}
}
/// <summary>
/// Log an information message in a given format.
/// </summary>
/// <param name="format"> The format. </param>
/// <param name="args"> The args. </param>
public void LogInfo(string format, params object[] args)
{
if (EqtTrace.IsInfoEnabled)
{
EqtTrace.Info(this.PrependAdapterName(format), args);
}
}
private string PrependAdapterName(string format)
{
return $"MSTest - {format}";
}
}
#pragma warning restore SA1649 // SA1649FileNameMustMatchTypeName

Просмотреть файл

@ -1,134 +1,133 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
namespace Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices
namespace Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices;
using System;
using System.IO;
using System.Reflection;
using Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.Interface;
using Microsoft.VisualStudio.TestPlatform.ObjectModel;
/// <summary>
/// This service is responsible for any file based operations.
/// </summary>
public class FileOperations : IFileOperations
{
using System;
using System.IO;
using System.Reflection;
using Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.Interface;
using Microsoft.VisualStudio.TestPlatform.ObjectModel;
/// <summary>
/// This service is responsible for any file based operations.
/// Loads an assembly into the current context.
/// </summary>
public class FileOperations : IFileOperations
/// <param name="assemblyFileName">The name of the assembly.</param>
/// <param name="isReflectionOnly">
/// Indicates whether this should be a reflection only load.
/// </param>
/// <returns>A handle to the loaded assembly.</returns>
public Assembly LoadAssembly(string assemblyFileName, bool isReflectionOnly)
{
/// <summary>
/// Loads an assembly into the current context.
/// </summary>
/// <param name="assemblyFileName">The name of the assembly.</param>
/// <param name="isReflectionOnly">
/// Indicates whether this should be a reflection only load.
/// </param>
/// <returns>A handle to the loaded assembly.</returns>
public Assembly LoadAssembly(string assemblyFileName, bool isReflectionOnly)
if (isReflectionOnly)
{
if (isReflectionOnly)
{
return Assembly.ReflectionOnlyLoadFrom(assemblyFileName);
}
else
{
return Assembly.LoadFrom(assemblyFileName);
}
return Assembly.ReflectionOnlyLoadFrom(assemblyFileName);
}
/// <summary>
/// Gets the path to the .DLL of the assembly.
/// </summary>
/// <param name="assembly">The assembly.</param>
/// <returns>Path to the .DLL of the assembly.</returns>
public string GetAssemblyPath(Assembly assembly)
else
{
return assembly.Location;
}
/// <summary>
/// Verify if a file exists in the current context.
/// </summary>
/// <param name="assemblyFileName"> The assembly file name. </param>
/// <returns> true if the file exists. </returns>
public bool DoesFileExist(string assemblyFileName)
{
return (SafeInvoke<bool>(() => File.Exists(assemblyFileName)) as bool?) ?? false;
}
/// <summary>
/// Creates a Navigation session for the source file.
/// This is used to get file path and line number information for its components.
/// </summary>
/// <param name="source"> The source file. </param>
/// <returns> A Navigation session instance for the current platform. </returns>
public object CreateNavigationSession(string source)
{
var messageFormatOnException =
string.Join("MSTestDiscoverer:DiaSession: Could not create diaSession for source:", source, ". Reason:{0}");
return SafeInvoke<DiaSession>(() => new DiaSession(source), messageFormatOnException) as DiaSession;
}
/// <summary>
/// Gets the navigation data for a navigation session.
/// </summary>
/// <param name="navigationSession"> The navigation session. </param>
/// <param name="className"> The class name. </param>
/// <param name="methodName"> The method name. </param>
/// <param name="minLineNumber"> The min line number. </param>
/// <param name="fileName"> The file name. </param>
public void GetNavigationData(object navigationSession, string className, string methodName, out int minLineNumber, out string fileName)
{
fileName = null;
minLineNumber = -1;
var diasession = navigationSession as DiaSession;
var navigationData = diasession?.GetNavigationData(className, methodName);
if (navigationData != null)
{
minLineNumber = navigationData.MinLineNumber;
fileName = navigationData.FileName;
}
}
/// <summary>
/// Disposes the navigation session instance.
/// </summary>
/// <param name="navigationSession"> The navigation session. </param>
public void DisposeNavigationSession(object navigationSession)
{
var diasession = navigationSession as DiaSession;
diasession?.Dispose();
}
/// <summary>
/// Gets the full file path of an assembly file.
/// </summary>
/// <param name="assemblyFileName"> The file name. </param>
/// <returns> The full file path. </returns>
public string GetFullFilePath(string assemblyFileName)
{
return (SafeInvoke<string>(() => Path.GetFullPath(assemblyFileName)) as string) ?? assemblyFileName;
}
private static object SafeInvoke<T>(Func<T> action, string messageFormatOnException = null)
{
try
{
return action.Invoke();
}
catch (Exception exception)
{
if (string.IsNullOrEmpty(messageFormatOnException))
{
messageFormatOnException = "{0}";
}
EqtTrace.ErrorIf(EqtTrace.IsErrorEnabled, messageFormatOnException, exception.Message);
}
return null;
return Assembly.LoadFrom(assemblyFileName);
}
}
#pragma warning restore SA1649 // SA1649FileNameMustMatchTypeName
/// <summary>
/// Gets the path to the .DLL of the assembly.
/// </summary>
/// <param name="assembly">The assembly.</param>
/// <returns>Path to the .DLL of the assembly.</returns>
public string GetAssemblyPath(Assembly assembly)
{
return assembly.Location;
}
/// <summary>
/// Verify if a file exists in the current context.
/// </summary>
/// <param name="assemblyFileName"> The assembly file name. </param>
/// <returns> true if the file exists. </returns>
public bool DoesFileExist(string assemblyFileName)
{
return (SafeInvoke<bool>(() => File.Exists(assemblyFileName)) as bool?) ?? false;
}
/// <summary>
/// Creates a Navigation session for the source file.
/// This is used to get file path and line number information for its components.
/// </summary>
/// <param name="source"> The source file. </param>
/// <returns> A Navigation session instance for the current platform. </returns>
public object CreateNavigationSession(string source)
{
var messageFormatOnException =
string.Join("MSTestDiscoverer:DiaSession: Could not create diaSession for source:", source, ". Reason:{0}");
return SafeInvoke<DiaSession>(() => new DiaSession(source), messageFormatOnException) as DiaSession;
}
/// <summary>
/// Gets the navigation data for a navigation session.
/// </summary>
/// <param name="navigationSession"> The navigation session. </param>
/// <param name="className"> The class name. </param>
/// <param name="methodName"> The method name. </param>
/// <param name="minLineNumber"> The min line number. </param>
/// <param name="fileName"> The file name. </param>
public void GetNavigationData(object navigationSession, string className, string methodName, out int minLineNumber, out string fileName)
{
fileName = null;
minLineNumber = -1;
var diasession = navigationSession as DiaSession;
var navigationData = diasession?.GetNavigationData(className, methodName);
if (navigationData != null)
{
minLineNumber = navigationData.MinLineNumber;
fileName = navigationData.FileName;
}
}
/// <summary>
/// Disposes the navigation session instance.
/// </summary>
/// <param name="navigationSession"> The navigation session. </param>
public void DisposeNavigationSession(object navigationSession)
{
var diasession = navigationSession as DiaSession;
diasession?.Dispose();
}
/// <summary>
/// Gets the full file path of an assembly file.
/// </summary>
/// <param name="assemblyFileName"> The file name. </param>
/// <returns> The full file path. </returns>
public string GetFullFilePath(string assemblyFileName)
{
return (SafeInvoke<string>(() => Path.GetFullPath(assemblyFileName)) as string) ?? assemblyFileName;
}
private static object SafeInvoke<T>(Func<T> action, string messageFormatOnException = null)
{
try
{
return action.Invoke();
}
catch (Exception exception)
{
if (string.IsNullOrEmpty(messageFormatOnException))
{
messageFormatOnException = "{0}";
}
EqtTrace.ErrorIf(EqtTrace.IsErrorEnabled, messageFormatOnException, exception.Message);
}
return null;
}
}
#pragma warning restore SA1649 // SA1649FileNameMustMatchTypeName

Просмотреть файл

@ -1,68 +1,67 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
namespace Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices
{
using System;
using System.Reflection;
namespace Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices;
using Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.Interface;
using Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.Utilities;
using System;
using System.Reflection;
using Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.Interface;
using Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.Utilities;
/// <summary>
/// This service is responsible for platform specific reflection operations.
/// </summary>
/// <remarks>
/// The test platform triggers discovery of test assets built for all architectures including ARM on desktop. In such cases we would need to load
/// these sources in a reflection only context. Since Reflection-Only context currently is primarily prevalent in .Net Framework only, this service is required
/// so that some operations like fetching attributes in a reflection only context can be performed.
/// </remarks>
public class ReflectionOperations : IReflectionOperations
{
private readonly ReflectionUtility reflectionUtility;
/// <summary>
/// This service is responsible for platform specific reflection operations.
/// Initializes a new instance of the <see cref="ReflectionOperations"/> class.
/// </summary>
/// <remarks>
/// The test platform triggers discovery of test assets built for all architectures including ARM on desktop. In such cases we would need to load
/// these sources in a reflection only context. Since Reflection-Only context currently is primarily prevalent in .Net Framework only, this service is required
/// so that some operations like fetching attributes in a reflection only context can be performed.
/// </remarks>
public class ReflectionOperations : IReflectionOperations
public ReflectionOperations()
{
private readonly ReflectionUtility reflectionUtility;
/// <summary>
/// Initializes a new instance of the <see cref="ReflectionOperations"/> class.
/// </summary>
public ReflectionOperations()
{
this.reflectionUtility = new ReflectionUtility();
}
/// <summary>
/// Gets all the custom attributes adorned on a member.
/// </summary>
/// <param name="memberInfo"> The member. </param>
/// <param name="inherit"> True to inspect the ancestors of element; otherwise, false. </param>
/// <returns> The list of attributes on the member. Empty list if none found. </returns>
public object[] GetCustomAttributes(MemberInfo memberInfo, bool inherit)
{
return this.reflectionUtility.GetCustomAttributes(memberInfo, inherit);
}
/// <summary>
/// Gets all the custom attributes of a given type adorned on a member.
/// </summary>
/// <param name="memberInfo"> The member info. </param>
/// <param name="type"> The attribute type. </param>
/// <param name="inherit"> True to inspect the ancestors of element; otherwise, false. </param>
/// <returns> The list of attributes on the member. Empty list if none found. </returns>
public object[] GetCustomAttributes(MemberInfo memberInfo, Type type, bool inherit)
{
return this.reflectionUtility.GetCustomAttributes(memberInfo, type, inherit);
}
/// <summary>
/// Gets all the custom attributes of a given type on an assembly.
/// </summary>
/// <param name="assembly"> The assembly. </param>
/// <param name="type"> The attribute type. </param>
/// <returns> The list of attributes of the given type on the member. Empty list if none found. </returns>
public object[] GetCustomAttributes(Assembly assembly, Type type)
{
return this.reflectionUtility.GetCustomAttributes(assembly, type);
}
this.reflectionUtility = new ReflectionUtility();
}
#pragma warning restore SA1649 // SA1649FileNameMustMatchTypeName
/// <summary>
/// Gets all the custom attributes adorned on a member.
/// </summary>
/// <param name="memberInfo"> The member. </param>
/// <param name="inherit"> True to inspect the ancestors of element; otherwise, false. </param>
/// <returns> The list of attributes on the member. Empty list if none found. </returns>
public object[] GetCustomAttributes(MemberInfo memberInfo, bool inherit)
{
return this.reflectionUtility.GetCustomAttributes(memberInfo, inherit);
}
/// <summary>
/// Gets all the custom attributes of a given type adorned on a member.
/// </summary>
/// <param name="memberInfo"> The member info. </param>
/// <param name="type"> The attribute type. </param>
/// <param name="inherit"> True to inspect the ancestors of element; otherwise, false. </param>
/// <returns> The list of attributes on the member. Empty list if none found. </returns>
public object[] GetCustomAttributes(MemberInfo memberInfo, Type type, bool inherit)
{
return this.reflectionUtility.GetCustomAttributes(memberInfo, type, inherit);
}
/// <summary>
/// Gets all the custom attributes of a given type on an assembly.
/// </summary>
/// <param name="assembly"> The assembly. </param>
/// <param name="type"> The attribute type. </param>
/// <returns> The list of attributes of the given type on the member. Empty list if none found. </returns>
public object[] GetCustomAttributes(Assembly assembly, Type type)
{
return this.reflectionUtility.GetCustomAttributes(assembly, type);
}
}
#pragma warning restore SA1649 // SA1649FileNameMustMatchTypeName

Просмотреть файл

@ -1,506 +1,505 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
namespace Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices
namespace Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Data;
using System.Data.Common;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Threading;
using Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.Interface;
using Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.Interface.ObjectModel;
using UTF = Microsoft.VisualStudio.TestTools.UnitTesting;
/// <summary>
/// Internal implementation of TestContext exposed to the user.
/// The virtual string properties of the TestContext are retrieved from the property dictionary
/// like GetProperty&lt;string&gt;("TestName") or GetProperty&lt;string&gt;("FullyQualifiedTestClassName");
/// </summary>
public class TestContextImplementation : UTF.TestContext, ITestContext
{
using System;
using System.Collections;
using System.Collections.Generic;
using System.Data;
using System.Data.Common;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Threading;
using Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.Interface;
using Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.Interface.ObjectModel;
using UTF = Microsoft.VisualStudio.TestTools.UnitTesting;
/// <summary>
/// List of result files associated with the test
/// </summary>
private readonly IList<string> testResultFiles;
/// <summary>
/// Internal implementation of TestContext exposed to the user.
/// The virtual string properties of the TestContext are retrieved from the property dictionary
/// like GetProperty&lt;string&gt;("TestName") or GetProperty&lt;string&gt;("FullyQualifiedTestClassName");
/// Properties
/// </summary>
public class TestContextImplementation : UTF.TestContext, ITestContext
private IDictionary<string, object> properties;
/// <summary>
/// Unit test outcome
/// </summary>
private UTF.UnitTestOutcome outcome;
/// <summary>
/// Writer on which the messages given by the user should be written
/// </summary>
private readonly ThreadSafeStringWriter threadSafeStringWriter;
/// <summary>
/// Specifies whether the writer is disposed or not
/// </summary>
private bool stringWriterDisposed = false;
/// <summary>
/// Test Method
/// </summary>
private readonly ITestMethod testMethod;
/// <summary>
/// DB connection for test context
/// </summary>
private DbConnection dbConnection;
/// <summary>
/// Data row for TestContext
/// </summary>
private DataRow dataRow;
/// <summary>
/// Initializes a new instance of the <see cref="TestContextImplementation"/> class.
/// </summary>
/// <param name="testMethod">The test method.</param>
/// <param name="stringWriter">The writer where diagnostic messages are written to.</param>
/// <param name="properties">Properties/configuration passed in.</param>
public TestContextImplementation(ITestMethod testMethod, StringWriter stringWriter, IDictionary<string, object> properties)
{
/// <summary>
/// List of result files associated with the test
/// </summary>
private readonly IList<string> testResultFiles;
Debug.Assert(testMethod != null, "TestMethod is not null");
Debug.Assert(stringWriter != null, "StringWriter is not null");
Debug.Assert(properties != null, "properties is not null");
/// <summary>
/// Properties
/// </summary>
private IDictionary<string, object> properties;
this.testMethod = testMethod;
/// <summary>
/// Unit test outcome
/// </summary>
private UTF.UnitTestOutcome outcome;
// Cannot get this type in constructor directly, because all signatures for all platforms need to be the same.
this.threadSafeStringWriter = (ThreadSafeStringWriter)stringWriter;
this.properties = new Dictionary<string, object>(properties);
this.CancellationTokenSource = new CancellationTokenSource();
this.InitializeProperties();
/// <summary>
/// Writer on which the messages given by the user should be written
/// </summary>
private readonly ThreadSafeStringWriter threadSafeStringWriter;
this.testResultFiles = new List<string>();
}
/// <summary>
/// Specifies whether the writer is disposed or not
/// </summary>
private bool stringWriterDisposed = false;
#region TestContext impl
/// <summary>
/// Test Method
/// </summary>
private readonly ITestMethod testMethod;
/// <summary>
/// DB connection for test context
/// </summary>
private DbConnection dbConnection;
/// <summary>
/// Data row for TestContext
/// </summary>
private DataRow dataRow;
/// <summary>
/// Initializes a new instance of the <see cref="TestContextImplementation"/> class.
/// </summary>
/// <param name="testMethod">The test method.</param>
/// <param name="stringWriter">The writer where diagnostic messages are written to.</param>
/// <param name="properties">Properties/configuration passed in.</param>
public TestContextImplementation(ITestMethod testMethod, StringWriter stringWriter, IDictionary<string, object> properties)
/// <inheritdoc/>
public override UTF.UnitTestOutcome CurrentTestOutcome
{
get
{
Debug.Assert(testMethod != null, "TestMethod is not null");
Debug.Assert(stringWriter != null, "StringWriter is not null");
Debug.Assert(properties != null, "properties is not null");
this.testMethod = testMethod;
// Cannot get this type in constructor directly, because all signatures for all platforms need to be the same.
this.threadSafeStringWriter = (ThreadSafeStringWriter)stringWriter;
this.properties = new Dictionary<string, object>(properties);
this.CancellationTokenSource = new CancellationTokenSource();
this.InitializeProperties();
this.testResultFiles = new List<string>();
}
#region TestContext impl
/// <inheritdoc/>
public override UTF.UnitTestOutcome CurrentTestOutcome
{
get
{
return this.outcome;
}
}
/// <inheritdoc/>
public override DbConnection DataConnection
{
get
{
return this.dbConnection;
}
}
/// <inheritdoc/>
public override DataRow DataRow
{
get
{
return this.dataRow;
}
}
/// <inheritdoc/>
public override IDictionary Properties
{
get
{
return this.properties as IDictionary;
}
}
/// <inheritdoc/>
public override string TestRunDirectory
{
get
{
return this.GetStringPropertyValue(TestContextPropertyStrings.TestRunDirectory);
}
}
/// <inheritdoc/>
public override string DeploymentDirectory
{
get
{
return this.GetStringPropertyValue(TestContextPropertyStrings.DeploymentDirectory);
}
}
/// <inheritdoc/>
public override string ResultsDirectory
{
get
{
return this.GetStringPropertyValue(TestContextPropertyStrings.ResultsDirectory);
}
}
/// <inheritdoc/>
public override string TestRunResultsDirectory
{
get
{
return this.GetStringPropertyValue(TestContextPropertyStrings.TestRunResultsDirectory);
}
}
/// <inheritdoc/>
[SuppressMessage("Microsoft.Naming", "CA1702:CompoundWordsShouldBeCasedCorrectly", Justification = "TestResultsDirectory is what we need.")]
public override string TestResultsDirectory
{
get
{
// In MSTest, it is actually "In\697105f7-004f-42e8-bccf-eb024870d3e9\User1", but
// we are setting it to "In" only because MSTest does not create this directory.
return this.GetStringPropertyValue(TestContextPropertyStrings.TestResultsDirectory);
}
}
/// <inheritdoc/>
public override string TestDir
{
get
{
return this.GetStringPropertyValue(TestContextPropertyStrings.TestDir);
}
}
/// <inheritdoc/>
public override string TestDeploymentDir
{
get
{
return this.GetStringPropertyValue(TestContextPropertyStrings.TestDeploymentDir);
}
}
/// <inheritdoc/>
public override string TestLogsDir
{
get
{
return this.GetStringPropertyValue(TestContextPropertyStrings.TestLogsDir);
}
}
/// <inheritdoc/>
public override string FullyQualifiedTestClassName
{
get
{
return this.GetStringPropertyValue(TestContextPropertyStrings.FullyQualifiedTestClassName);
}
}
/// <inheritdoc/>
public override string ManagedType
{
get
{
return this.GetStringPropertyValue(TestContextPropertyStrings.ManagedType);
}
}
/// <inheritdoc/>
public override string ManagedMethod
{
get
{
return this.GetStringPropertyValue(TestContextPropertyStrings.ManagedMethod);
}
}
/// <inheritdoc/>
public override string TestName
{
get
{
return this.GetStringPropertyValue(TestContextPropertyStrings.TestName);
}
}
public UTF.TestContext Context
{
get
{
return this as UTF.TestContext;
}
}
/// <inheritdoc/>
public override void AddResultFile(string fileName)
{
if (string.IsNullOrEmpty(fileName))
{
throw new ArgumentException(Resource.Common_CannotBeNullOrEmpty, nameof(fileName));
}
this.testResultFiles.Add(Path.GetFullPath(fileName));
}
/// <inheritdoc/>
public override void BeginTimer(string timerName)
{
throw new NotSupportedException();
}
/// <inheritdoc/>
public override void EndTimer(string timerName)
{
throw new NotSupportedException();
}
/// <summary>
/// When overridden in a derived class, used to write trace messages while the
/// test is running.
/// </summary>
/// <param name="message">The formatted string that contains the trace message.</param>
public override void Write(string message)
{
if (this.stringWriterDisposed)
{
return;
}
try
{
var msg = message?.Replace("\0", "\\0");
this.threadSafeStringWriter.Write(msg);
}
catch (ObjectDisposedException)
{
this.stringWriterDisposed = true;
}
}
/// <summary>
/// When overridden in a derived class, used to write trace messages while the
/// test is running.
/// </summary>
/// <param name="format">The string that contains the trace message.</param>
/// <param name="args">Arguments to add to the trace message.</param>
public override void Write(string format, params object[] args)
{
if (this.stringWriterDisposed)
{
return;
}
try
{
string message = string.Format(CultureInfo.CurrentCulture, format?.Replace("\0", "\\0"), args);
this.threadSafeStringWriter.Write(message);
}
catch (ObjectDisposedException)
{
this.stringWriterDisposed = true;
}
}
/// <summary>
/// When overridden in a derived class, used to write trace messages while the
/// test is running.
/// </summary>
/// <param name="message">The formatted string that contains the trace message.</param>
public override void WriteLine(string message)
{
if (this.stringWriterDisposed)
{
return;
}
try
{
var msg = message?.Replace("\0", "\\0");
this.threadSafeStringWriter.WriteLine(msg);
}
catch (ObjectDisposedException)
{
this.stringWriterDisposed = true;
}
}
/// <summary>
/// When overridden in a derived class, used to write trace messages while the
/// test is running.
/// </summary>
/// <param name="format">The string that contains the trace message.</param>
/// <param name="args">Arguments to add to the trace message.</param>
public override void WriteLine(string format, params object[] args)
{
if (this.stringWriterDisposed)
{
return;
}
try
{
string message = string.Format(CultureInfo.CurrentCulture, format?.Replace("\0", "\\0"), args);
this.threadSafeStringWriter.WriteLine(message);
}
catch (ObjectDisposedException)
{
this.stringWriterDisposed = true;
}
}
/// <summary>
/// Set the unit-test outcome
/// </summary>
/// <param name="outcome">The test outcome.</param>
public void SetOutcome(UTF.UnitTestOutcome outcome)
{
this.outcome = ToUTF(outcome);
}
/// <summary>
/// Set data row for particular run of TestMethod.
/// </summary>
/// <param name="dataRow">data row.</param>
public void SetDataRow(object dataRow)
{
this.dataRow = dataRow as DataRow;
}
/// <summary>
/// Set connection for TestContext
/// </summary>
/// <param name="dbConnection">db Connection.</param>
public void SetDataConnection(object dbConnection)
{
this.dbConnection = dbConnection as DbConnection;
}
/// <summary>
/// Returns whether property with parameter name is present or not
/// </summary>
/// <param name="propertyName">The property name.</param>
/// <param name="propertyValue">The property value.</param>
/// <returns>True if found.</returns>
public bool TryGetPropertyValue(string propertyName, out object propertyValue)
{
if (this.properties == null)
{
propertyValue = null;
return false;
}
return this.properties.TryGetValue(propertyName, out propertyValue);
}
/// <summary>
/// Adds the parameter name/value pair to property bag
/// </summary>
/// <param name="propertyName">The property name.</param>
/// <param name="propertyValue">The property value.</param>
public void AddProperty(string propertyName, string propertyValue)
{
this.properties ??= new Dictionary<string, object>();
this.properties.Add(propertyName, propertyValue);
}
/// <summary>
/// Result files attached
/// </summary>
/// <returns>Results files generated in run.</returns>
public IList<string> GetResultFiles()
{
if (!this.testResultFiles.Any())
{
return null;
}
List<string> results = this.testResultFiles.ToList();
// clear the result files to handle data driven tests
this.testResultFiles.Clear();
return results;
}
/// <summary>
/// Gets messages from the testContext writeLines
/// </summary>
/// <returns>The test context messages added so far.</returns>
public string GetDiagnosticMessages()
{
return this.threadSafeStringWriter.ToString();
}
/// <summary>
/// Clears the previous testContext writeline messages.
/// </summary>
public void ClearDiagnosticMessages()
{
this.threadSafeStringWriter.ToStringAndClear();
}
#endregion
/// <summary>
/// Converts the parameter outcome to UTF outcome
/// </summary>
/// <param name="outcome">The UTF outcome.</param>
/// <returns>test outcome</returns>
private static UTF.UnitTestOutcome ToUTF(UTF.UnitTestOutcome outcome)
{
switch (outcome)
{
case UTF.UnitTestOutcome.Error:
case UTF.UnitTestOutcome.Failed:
case UTF.UnitTestOutcome.Inconclusive:
case UTF.UnitTestOutcome.Passed:
case UTF.UnitTestOutcome.Timeout:
case UTF.UnitTestOutcome.InProgress:
return outcome;
default:
Debug.Fail("Unknown outcome " + outcome);
return UTF.UnitTestOutcome.Unknown;
}
}
/// <summary>
/// Helper to safely fetch a property value.
/// </summary>
/// <param name="propertyName">Property Name</param>
/// <returns>Property value</returns>
private string GetStringPropertyValue(string propertyName)
{
this.properties.TryGetValue(propertyName, out var propertyValue);
return propertyValue as string;
}
/// <summary>
/// Helper to initialize the properties.
/// </summary>
private void InitializeProperties()
{
this.properties[TestContextPropertyStrings.FullyQualifiedTestClassName] = this.testMethod.FullClassName;
this.properties[TestContextPropertyStrings.ManagedType] = this.testMethod.ManagedTypeName;
this.properties[TestContextPropertyStrings.ManagedMethod] = this.testMethod.ManagedMethodName;
this.properties[TestContextPropertyStrings.TestName] = this.testMethod.Name;
return this.outcome;
}
}
/// <inheritdoc/>
public override DbConnection DataConnection
{
get
{
return this.dbConnection;
}
}
/// <inheritdoc/>
public override DataRow DataRow
{
get
{
return this.dataRow;
}
}
/// <inheritdoc/>
public override IDictionary Properties
{
get
{
return this.properties as IDictionary;
}
}
/// <inheritdoc/>
public override string TestRunDirectory
{
get
{
return this.GetStringPropertyValue(TestContextPropertyStrings.TestRunDirectory);
}
}
/// <inheritdoc/>
public override string DeploymentDirectory
{
get
{
return this.GetStringPropertyValue(TestContextPropertyStrings.DeploymentDirectory);
}
}
/// <inheritdoc/>
public override string ResultsDirectory
{
get
{
return this.GetStringPropertyValue(TestContextPropertyStrings.ResultsDirectory);
}
}
/// <inheritdoc/>
public override string TestRunResultsDirectory
{
get
{
return this.GetStringPropertyValue(TestContextPropertyStrings.TestRunResultsDirectory);
}
}
/// <inheritdoc/>
[SuppressMessage("Microsoft.Naming", "CA1702:CompoundWordsShouldBeCasedCorrectly", Justification = "TestResultsDirectory is what we need.")]
public override string TestResultsDirectory
{
get
{
// In MSTest, it is actually "In\697105f7-004f-42e8-bccf-eb024870d3e9\User1", but
// we are setting it to "In" only because MSTest does not create this directory.
return this.GetStringPropertyValue(TestContextPropertyStrings.TestResultsDirectory);
}
}
/// <inheritdoc/>
public override string TestDir
{
get
{
return this.GetStringPropertyValue(TestContextPropertyStrings.TestDir);
}
}
/// <inheritdoc/>
public override string TestDeploymentDir
{
get
{
return this.GetStringPropertyValue(TestContextPropertyStrings.TestDeploymentDir);
}
}
/// <inheritdoc/>
public override string TestLogsDir
{
get
{
return this.GetStringPropertyValue(TestContextPropertyStrings.TestLogsDir);
}
}
/// <inheritdoc/>
public override string FullyQualifiedTestClassName
{
get
{
return this.GetStringPropertyValue(TestContextPropertyStrings.FullyQualifiedTestClassName);
}
}
/// <inheritdoc/>
public override string ManagedType
{
get
{
return this.GetStringPropertyValue(TestContextPropertyStrings.ManagedType);
}
}
/// <inheritdoc/>
public override string ManagedMethod
{
get
{
return this.GetStringPropertyValue(TestContextPropertyStrings.ManagedMethod);
}
}
/// <inheritdoc/>
public override string TestName
{
get
{
return this.GetStringPropertyValue(TestContextPropertyStrings.TestName);
}
}
public UTF.TestContext Context
{
get
{
return this as UTF.TestContext;
}
}
/// <inheritdoc/>
public override void AddResultFile(string fileName)
{
if (string.IsNullOrEmpty(fileName))
{
throw new ArgumentException(Resource.Common_CannotBeNullOrEmpty, nameof(fileName));
}
this.testResultFiles.Add(Path.GetFullPath(fileName));
}
/// <inheritdoc/>
public override void BeginTimer(string timerName)
{
throw new NotSupportedException();
}
/// <inheritdoc/>
public override void EndTimer(string timerName)
{
throw new NotSupportedException();
}
/// <summary>
/// When overridden in a derived class, used to write trace messages while the
/// test is running.
/// </summary>
/// <param name="message">The formatted string that contains the trace message.</param>
public override void Write(string message)
{
if (this.stringWriterDisposed)
{
return;
}
try
{
var msg = message?.Replace("\0", "\\0");
this.threadSafeStringWriter.Write(msg);
}
catch (ObjectDisposedException)
{
this.stringWriterDisposed = true;
}
}
/// <summary>
/// When overridden in a derived class, used to write trace messages while the
/// test is running.
/// </summary>
/// <param name="format">The string that contains the trace message.</param>
/// <param name="args">Arguments to add to the trace message.</param>
public override void Write(string format, params object[] args)
{
if (this.stringWriterDisposed)
{
return;
}
try
{
string message = string.Format(CultureInfo.CurrentCulture, format?.Replace("\0", "\\0"), args);
this.threadSafeStringWriter.Write(message);
}
catch (ObjectDisposedException)
{
this.stringWriterDisposed = true;
}
}
/// <summary>
/// When overridden in a derived class, used to write trace messages while the
/// test is running.
/// </summary>
/// <param name="message">The formatted string that contains the trace message.</param>
public override void WriteLine(string message)
{
if (this.stringWriterDisposed)
{
return;
}
try
{
var msg = message?.Replace("\0", "\\0");
this.threadSafeStringWriter.WriteLine(msg);
}
catch (ObjectDisposedException)
{
this.stringWriterDisposed = true;
}
}
/// <summary>
/// When overridden in a derived class, used to write trace messages while the
/// test is running.
/// </summary>
/// <param name="format">The string that contains the trace message.</param>
/// <param name="args">Arguments to add to the trace message.</param>
public override void WriteLine(string format, params object[] args)
{
if (this.stringWriterDisposed)
{
return;
}
try
{
string message = string.Format(CultureInfo.CurrentCulture, format?.Replace("\0", "\\0"), args);
this.threadSafeStringWriter.WriteLine(message);
}
catch (ObjectDisposedException)
{
this.stringWriterDisposed = true;
}
}
/// <summary>
/// Set the unit-test outcome
/// </summary>
/// <param name="outcome">The test outcome.</param>
public void SetOutcome(UTF.UnitTestOutcome outcome)
{
this.outcome = ToUTF(outcome);
}
/// <summary>
/// Set data row for particular run of TestMethod.
/// </summary>
/// <param name="dataRow">data row.</param>
public void SetDataRow(object dataRow)
{
this.dataRow = dataRow as DataRow;
}
/// <summary>
/// Set connection for TestContext
/// </summary>
/// <param name="dbConnection">db Connection.</param>
public void SetDataConnection(object dbConnection)
{
this.dbConnection = dbConnection as DbConnection;
}
/// <summary>
/// Returns whether property with parameter name is present or not
/// </summary>
/// <param name="propertyName">The property name.</param>
/// <param name="propertyValue">The property value.</param>
/// <returns>True if found.</returns>
public bool TryGetPropertyValue(string propertyName, out object propertyValue)
{
if (this.properties == null)
{
propertyValue = null;
return false;
}
return this.properties.TryGetValue(propertyName, out propertyValue);
}
/// <summary>
/// Adds the parameter name/value pair to property bag
/// </summary>
/// <param name="propertyName">The property name.</param>
/// <param name="propertyValue">The property value.</param>
public void AddProperty(string propertyName, string propertyValue)
{
this.properties ??= new Dictionary<string, object>();
this.properties.Add(propertyName, propertyValue);
}
/// <summary>
/// Result files attached
/// </summary>
/// <returns>Results files generated in run.</returns>
public IList<string> GetResultFiles()
{
if (!this.testResultFiles.Any())
{
return null;
}
List<string> results = this.testResultFiles.ToList();
// clear the result files to handle data driven tests
this.testResultFiles.Clear();
return results;
}
/// <summary>
/// Gets messages from the testContext writeLines
/// </summary>
/// <returns>The test context messages added so far.</returns>
public string GetDiagnosticMessages()
{
return this.threadSafeStringWriter.ToString();
}
/// <summary>
/// Clears the previous testContext writeline messages.
/// </summary>
public void ClearDiagnosticMessages()
{
this.threadSafeStringWriter.ToStringAndClear();
}
#endregion
/// <summary>
/// Converts the parameter outcome to UTF outcome
/// </summary>
/// <param name="outcome">The UTF outcome.</param>
/// <returns>test outcome</returns>
private static UTF.UnitTestOutcome ToUTF(UTF.UnitTestOutcome outcome)
{
switch (outcome)
{
case UTF.UnitTestOutcome.Error:
case UTF.UnitTestOutcome.Failed:
case UTF.UnitTestOutcome.Inconclusive:
case UTF.UnitTestOutcome.Passed:
case UTF.UnitTestOutcome.Timeout:
case UTF.UnitTestOutcome.InProgress:
return outcome;
default:
Debug.Fail("Unknown outcome " + outcome);
return UTF.UnitTestOutcome.Unknown;
}
}
/// <summary>
/// Helper to safely fetch a property value.
/// </summary>
/// <param name="propertyName">Property Name</param>
/// <returns>Property value</returns>
private string GetStringPropertyValue(string propertyName)
{
this.properties.TryGetValue(propertyName, out var propertyValue);
return propertyValue as string;
}
/// <summary>
/// Helper to initialize the properties.
/// </summary>
private void InitializeProperties()
{
this.properties[TestContextPropertyStrings.FullyQualifiedTestClassName] = this.testMethod.FullClassName;
this.properties[TestContextPropertyStrings.ManagedType] = this.testMethod.ManagedTypeName;
this.properties[TestContextPropertyStrings.ManagedMethod] = this.testMethod.ManagedMethodName;
this.properties[TestContextPropertyStrings.TestName] = this.testMethod.Name;
}
}

Просмотреть файл

@ -1,145 +1,144 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
namespace Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices
{
using System;
using System.Collections.Generic;
using System.Configuration;
using System.Data;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.Data;
using Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.Extensions;
using Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.Interface;
namespace Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices;
using UTF = Microsoft.VisualStudio.TestTools.UnitTesting;
using System;
using System.Collections.Generic;
using System.Configuration;
using System.Data;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.Data;
using Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.Extensions;
using Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.Interface;
using UTF = Microsoft.VisualStudio.TestTools.UnitTesting;
#pragma warning disable SA1649 // SA1649FileNameMustMatchTypeName
/// <summary>
/// The platform service that provides values from data source when data driven tests are run.
/// </summary>
/// <remarks>
/// NOTE NOTE NOTE: This platform service refers to the inbox UTF extension assembly for UTF.ITestMethod which can only be loadable inside of the app domain that discovers/runs
/// the tests since it can only be found at the test output directory. DO NOT call into this platform service outside of the appdomain context if you do not want to hit
/// a ReflectionTypeLoadException.
/// </remarks>
public class TestDataSource : ITestDataSource
/// <summary>
/// The platform service that provides values from data source when data driven tests are run.
/// </summary>
/// <remarks>
/// NOTE NOTE NOTE: This platform service refers to the inbox UTF extension assembly for UTF.ITestMethod which can only be loadable inside of the app domain that discovers/runs
/// the tests since it can only be found at the test output directory. DO NOT call into this platform service outside of the appdomain context if you do not want to hit
/// a ReflectionTypeLoadException.
/// </remarks>
public class TestDataSource : ITestDataSource
{
public IEnumerable<object> GetData(UTF.ITestMethod testMethodInfo, ITestContext testContext)
{
public IEnumerable<object> GetData(UTF.ITestMethod testMethodInfo, ITestContext testContext)
// Figure out where (as well as the current directory) we could look for data files
// for unit tests this means looking at the location of the test itself
List<string> dataFolders = new();
dataFolders.Add(Path.GetDirectoryName(new Uri(testMethodInfo.MethodInfo.Module.Assembly.CodeBase).LocalPath));
List<UTF.TestResult> dataRowResults = new();
// Connect to data source.
TestDataConnectionFactory factory = new();
string providerNameInvariant;
string connectionString;
string tableName;
UTF.DataAccessMethod dataAccessMethod;
try
{
// Figure out where (as well as the current directory) we could look for data files
// for unit tests this means looking at the location of the test itself
List<string> dataFolders = new();
dataFolders.Add(Path.GetDirectoryName(new Uri(testMethodInfo.MethodInfo.Module.Assembly.CodeBase).LocalPath));
List<UTF.TestResult> dataRowResults = new();
// Connect to data source.
TestDataConnectionFactory factory = new();
string providerNameInvariant;
string connectionString;
string tableName;
UTF.DataAccessMethod dataAccessMethod;
try
{
this.GetConnectionProperties(testMethodInfo.GetAttributes<UTF.DataSourceAttribute>(false)[0], out providerNameInvariant, out connectionString, out tableName, out dataAccessMethod);
}
catch (Exception ex)
{
throw ex;
}
try
{
using TestDataConnection connection = factory.Create(providerNameInvariant, connectionString, dataFolders);
DataTable table = connection.ReadTable(tableName, null);
DataRow[] rows = table.Select();
Debug.Assert(rows != null, "rows should not be null.");
// check for row length is 0
if (rows.Length == 0)
{
return null;
}
IEnumerable<int> permutation = this.GetPermutation(dataAccessMethod, rows.Length);
object[] rowsAfterPermutation = new object[rows.Length];
int index = 0;
foreach (int rowIndex in permutation)
{
rowsAfterPermutation[index++] = rows[rowIndex];
}
testContext.SetDataConnection(connection.Connection);
return rowsAfterPermutation;
}
catch (Exception ex)
{
string message = ExceptionExtensions.GetExceptionMessage(ex);
throw new Exception(string.Format(CultureInfo.CurrentCulture, Resource.UTA_ErrorDataConnectionFailed, ex.Message), ex);
}
this.GetConnectionProperties(testMethodInfo.GetAttributes<UTF.DataSourceAttribute>(false)[0], out providerNameInvariant, out connectionString, out tableName, out dataAccessMethod);
}
catch (Exception ex)
{
throw ex;
}
/// <summary>
/// Get permutations for data row access
/// </summary>
/// <param name="dataAccessMethod">The data access method.</param>
/// <param name="length">Number of permutations.</param>
/// <returns>Permutations.</returns>
private IEnumerable<int> GetPermutation(UTF.DataAccessMethod dataAccessMethod, int length)
try
{
switch (dataAccessMethod)
using TestDataConnection connection = factory.Create(providerNameInvariant, connectionString, dataFolders);
DataTable table = connection.ReadTable(tableName, null);
DataRow[] rows = table.Select();
Debug.Assert(rows != null, "rows should not be null.");
// check for row length is 0
if (rows.Length == 0)
{
case UTF.DataAccessMethod.Sequential:
return new SequentialIntPermutation(length);
case UTF.DataAccessMethod.Random:
return new RandomIntPermutation(length);
default:
Debug.Fail("Unknown DataAccessMehtod: " + dataAccessMethod);
return new SequentialIntPermutation(length);
return null;
}
IEnumerable<int> permutation = this.GetPermutation(dataAccessMethod, rows.Length);
object[] rowsAfterPermutation = new object[rows.Length];
int index = 0;
foreach (int rowIndex in permutation)
{
rowsAfterPermutation[index++] = rows[rowIndex];
}
testContext.SetDataConnection(connection.Connection);
return rowsAfterPermutation;
}
/// <summary>
/// Get connection property based on DataSourceAttribute. If its in config file then read it from config.
/// </summary>
/// <param name="dataSourceAttribute">The dataSourceAttribute.</param>
/// <param name="providerNameInvariant">The provider name.</param>
/// <param name="connectionString">The connection string.</param>
/// <param name="tableName">The table name.</param>
/// <param name="dataAccessMethod">The data access method.</param>
private void GetConnectionProperties(UTF.DataSourceAttribute dataSourceAttribute, out string providerNameInvariant, out string connectionString, out string tableName, out UTF.DataAccessMethod dataAccessMethod)
catch (Exception ex)
{
if (string.IsNullOrEmpty(dataSourceAttribute.DataSourceSettingName) == false)
{
UTF.DataSourceElement elem = UTF.TestConfiguration.ConfigurationSection.DataSources[dataSourceAttribute.DataSourceSettingName];
if (elem == null)
{
throw new Exception(string.Format(CultureInfo.CurrentCulture, Resource.UTA_DataSourceConfigurationSectionMissing, dataSourceAttribute.DataSourceSettingName));
}
providerNameInvariant = ConfigurationManager.ConnectionStrings[elem.ConnectionString].ProviderName;
connectionString = ConfigurationManager.ConnectionStrings[elem.ConnectionString].ConnectionString;
tableName = elem.DataTableName;
dataAccessMethod = (UTF.DataAccessMethod)Enum.Parse(typeof(UTF.DataAccessMethod), elem.DataAccessMethod);
}
else
{
providerNameInvariant = dataSourceAttribute.ProviderInvariantName;
connectionString = dataSourceAttribute.ConnectionString;
tableName = dataSourceAttribute.TableName;
dataAccessMethod = dataSourceAttribute.DataAccessMethod;
}
string message = ExceptionExtensions.GetExceptionMessage(ex);
throw new Exception(string.Format(CultureInfo.CurrentCulture, Resource.UTA_ErrorDataConnectionFailed, ex.Message), ex);
}
}
#pragma warning restore SA1649 // SA1649FileNameMustMatchTypeName
/// <summary>
/// Get permutations for data row access
/// </summary>
/// <param name="dataAccessMethod">The data access method.</param>
/// <param name="length">Number of permutations.</param>
/// <returns>Permutations.</returns>
private IEnumerable<int> GetPermutation(UTF.DataAccessMethod dataAccessMethod, int length)
{
switch (dataAccessMethod)
{
case UTF.DataAccessMethod.Sequential:
return new SequentialIntPermutation(length);
case UTF.DataAccessMethod.Random:
return new RandomIntPermutation(length);
default:
Debug.Fail("Unknown DataAccessMehtod: " + dataAccessMethod);
return new SequentialIntPermutation(length);
}
}
/// <summary>
/// Get connection property based on DataSourceAttribute. If its in config file then read it from config.
/// </summary>
/// <param name="dataSourceAttribute">The dataSourceAttribute.</param>
/// <param name="providerNameInvariant">The provider name.</param>
/// <param name="connectionString">The connection string.</param>
/// <param name="tableName">The table name.</param>
/// <param name="dataAccessMethod">The data access method.</param>
private void GetConnectionProperties(UTF.DataSourceAttribute dataSourceAttribute, out string providerNameInvariant, out string connectionString, out string tableName, out UTF.DataAccessMethod dataAccessMethod)
{
if (string.IsNullOrEmpty(dataSourceAttribute.DataSourceSettingName) == false)
{
UTF.DataSourceElement elem = UTF.TestConfiguration.ConfigurationSection.DataSources[dataSourceAttribute.DataSourceSettingName];
if (elem == null)
{
throw new Exception(string.Format(CultureInfo.CurrentCulture, Resource.UTA_DataSourceConfigurationSectionMissing, dataSourceAttribute.DataSourceSettingName));
}
providerNameInvariant = ConfigurationManager.ConnectionStrings[elem.ConnectionString].ProviderName;
connectionString = ConfigurationManager.ConnectionStrings[elem.ConnectionString].ConnectionString;
tableName = elem.DataTableName;
dataAccessMethod = (UTF.DataAccessMethod)Enum.Parse(typeof(UTF.DataAccessMethod), elem.DataAccessMethod);
}
else
{
providerNameInvariant = dataSourceAttribute.ProviderInvariantName;
connectionString = dataSourceAttribute.ConnectionString;
tableName = dataSourceAttribute.TableName;
dataAccessMethod = dataSourceAttribute.DataAccessMethod;
}
}
}
#pragma warning restore SA1649 // SA1649FileNameMustMatchTypeName

Просмотреть файл

@ -1,70 +1,69 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
namespace Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices
{
using System.Collections.Generic;
using System.Reflection;
namespace Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices;
using Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.Interface;
using Microsoft.VisualStudio.TestPlatform.ObjectModel.Utilities;
using System.Collections.Generic;
using System.Reflection;
using Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.Interface;
using Microsoft.VisualStudio.TestPlatform.ObjectModel.Utilities;
#pragma warning disable SA1649 // SA1649FileNameMustMatchTypeName
/// <summary>
/// This platform service is responsible for any data or operations to validate
/// the test sources provided to the adapter.
/// </summary>
public class TestSource : ITestSource
{
/// <summary>
/// This platform service is responsible for any data or operations to validate
/// the test sources provided to the adapter.
/// Gets the set of valid extensions for sources targeting this platform.
/// </summary>
public class TestSource : ITestSource
public IEnumerable<string> ValidSourceExtensions
{
/// <summary>
/// Gets the set of valid extensions for sources targeting this platform.
/// </summary>
public IEnumerable<string> ValidSourceExtensions
get
{
get
{
// Since desktop Platform service would also discover other platform tests on desktop,
// this extension list needs to be updated with all platforms supported file extensions.
return new List<string>
{
Constants.DllExtension,
Constants.PhoneAppxPackageExtension,
Constants.ExeExtension
};
}
}
/// <summary>
/// Verifies if the assembly provided is referenced by the source.
/// </summary>
/// <param name="assemblyName"> The assembly name. </param>
/// <param name="source"> The source. </param>
/// <returns> True if the assembly is referenced. </returns>
public bool IsAssemblyReferenced(AssemblyName assemblyName, string source)
{
// This loads the dll in a different app domain. We can optimize this to load in the current domain since this code could be run in a new app domain anyway.
bool? utfReference = AssemblyHelper.DoesReferencesAssembly(source, assemblyName);
// If no reference to UTF don't run discovery. Take conservative approach. If not able to find proceed with discovery.
if (utfReference.HasValue && utfReference.Value == false)
{
return false;
}
return true;
}
/// <summary>
/// Gets the set of sources (dll's/exe's) that contain tests. If a source is a package(appx), return the file(dll/exe) that contains tests from it.
/// </summary>
/// <param name="sources"> Sources given to the adapter. </param>
/// <returns> Sources that contains tests. <see cref="IEnumerable{T}"/>. </returns>
public IEnumerable<string> GetTestSources(IEnumerable<string> sources)
{
return sources;
// Since desktop Platform service would also discover other platform tests on desktop,
// this extension list needs to be updated with all platforms supported file extensions.
return new List<string>
{
Constants.DllExtension,
Constants.PhoneAppxPackageExtension,
Constants.ExeExtension
};
}
}
#pragma warning restore SA1649 // SA1649FileNameMustMatchTypeName
/// <summary>
/// Verifies if the assembly provided is referenced by the source.
/// </summary>
/// <param name="assemblyName"> The assembly name. </param>
/// <param name="source"> The source. </param>
/// <returns> True if the assembly is referenced. </returns>
public bool IsAssemblyReferenced(AssemblyName assemblyName, string source)
{
// This loads the dll in a different app domain. We can optimize this to load in the current domain since this code could be run in a new app domain anyway.
bool? utfReference = AssemblyHelper.DoesReferencesAssembly(source, assemblyName);
// If no reference to UTF don't run discovery. Take conservative approach. If not able to find proceed with discovery.
if (utfReference.HasValue && utfReference.Value == false)
{
return false;
}
return true;
}
/// <summary>
/// Gets the set of sources (dll's/exe's) that contain tests. If a source is a package(appx), return the file(dll/exe) that contains tests from it.
/// </summary>
/// <param name="sources"> Sources given to the adapter. </param>
/// <returns> Sources that contains tests. <see cref="IEnumerable{T}"/>. </returns>
public IEnumerable<string> GetTestSources(IEnumerable<string> sources)
{
return sources;
}
}
#pragma warning restore SA1649 // SA1649FileNameMustMatchTypeName

Просмотреть файл

@ -1,370 +1,369 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
namespace Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices
{
using System;
using System.Collections.Generic;
using System.IO;
using System.Reflection;
namespace Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices;
using Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.Interface;
using Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.Utilities;
using Microsoft.VisualStudio.TestPlatform.ObjectModel;
using Microsoft.VisualStudio.TestPlatform.ObjectModel.Adapter;
using Microsoft.VisualStudio.TestPlatform.ObjectModel.Utilities;
using System;
using System.Collections.Generic;
using System.IO;
using System.Reflection;
using Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.Interface;
using Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.Utilities;
using Microsoft.VisualStudio.TestPlatform.ObjectModel;
using Microsoft.VisualStudio.TestPlatform.ObjectModel.Adapter;
using Microsoft.VisualStudio.TestPlatform.ObjectModel.Utilities;
/// <summary>
/// A host that loads the test source.This can be in isolation for desktop using an AppDomain or just loading the source in the current context.
/// </summary>
public class TestSourceHost : ITestSourceHost
{
/// <summary>
/// Child AppDomain used to discover/execute tests
/// </summary>
private AppDomain domain;
/// <summary>
/// A host that loads the test source.This can be in isolation for desktop using an AppDomain or just loading the source in the current context.
/// Assembly resolver used in the current app-domain
/// </summary>
public class TestSourceHost : ITestSourceHost
private AssemblyResolver parentDomainAssemblyResolver;
/// <summary>
/// Assembly resolver used in the new child app-domain created for discovery/execution
/// </summary>
private AssemblyResolver childDomainAssemblyResolver;
/// <summary>
/// Determines whether child-appdomain needs to be created based on DisableAppDomain Flag set in runsettings
/// </summary>
private readonly bool isAppDomainCreationDisabled;
private readonly string sourceFileName;
private readonly IRunSettings runSettings;
private readonly IFrameworkHandle frameworkHandle;
private string currentDirectory = null;
private readonly IAppDomain appDomain;
private string targetFrameworkVersion;
/// <summary>
/// Initializes a new instance of the <see cref="TestSourceHost"/> class.
/// </summary>
/// <param name="sourceFileName"> The source file name. </param>
/// <param name="runSettings"> The run-settings provided for this session. </param>
/// <param name="frameworkHandle"> The handle to the test platform. </param>
public TestSourceHost(string sourceFileName, IRunSettings runSettings, IFrameworkHandle frameworkHandle)
: this(sourceFileName, runSettings, frameworkHandle, new AppDomainWrapper())
{
/// <summary>
/// Child AppDomain used to discover/execute tests
/// </summary>
private AppDomain domain;
}
/// <summary>
/// Assembly resolver used in the current app-domain
/// </summary>
private AssemblyResolver parentDomainAssemblyResolver;
internal TestSourceHost(string sourceFileName, IRunSettings runSettings, IFrameworkHandle frameworkHandle, IAppDomain appDomain)
{
this.sourceFileName = sourceFileName;
this.runSettings = runSettings;
this.frameworkHandle = frameworkHandle;
this.appDomain = appDomain;
/// <summary>
/// Assembly resolver used in the new child app-domain created for discovery/execution
/// </summary>
private AssemblyResolver childDomainAssemblyResolver;
// Set the environment context.
this.SetContext(sourceFileName);
/// <summary>
/// Determines whether child-appdomain needs to be created based on DisableAppDomain Flag set in runsettings
/// </summary>
private readonly bool isAppDomainCreationDisabled;
// Set isAppDomainCreationDisabled flag
this.isAppDomainCreationDisabled = (this.runSettings != null) && MSTestAdapterSettings.IsAppDomainCreationDisabled(this.runSettings.SettingsXml);
}
private readonly string sourceFileName;
private readonly IRunSettings runSettings;
private readonly IFrameworkHandle frameworkHandle;
private string currentDirectory = null;
private readonly IAppDomain appDomain;
private string targetFrameworkVersion;
/// <summary>
/// Initializes a new instance of the <see cref="TestSourceHost"/> class.
/// </summary>
/// <param name="sourceFileName"> The source file name. </param>
/// <param name="runSettings"> The run-settings provided for this session. </param>
/// <param name="frameworkHandle"> The handle to the test platform. </param>
public TestSourceHost(string sourceFileName, IRunSettings runSettings, IFrameworkHandle frameworkHandle)
: this(sourceFileName, runSettings, frameworkHandle, new AppDomainWrapper())
internal AppDomain AppDomain
{
get
{
}
internal TestSourceHost(string sourceFileName, IRunSettings runSettings, IFrameworkHandle frameworkHandle, IAppDomain appDomain)
{
this.sourceFileName = sourceFileName;
this.runSettings = runSettings;
this.frameworkHandle = frameworkHandle;
this.appDomain = appDomain;
// Set the environment context.
this.SetContext(sourceFileName);
// Set isAppDomainCreationDisabled flag
this.isAppDomainCreationDisabled = (this.runSettings != null) && MSTestAdapterSettings.IsAppDomainCreationDisabled(this.runSettings.SettingsXml);
}
internal AppDomain AppDomain
{
get
{
return this.domain;
}
}
/// <summary>
/// Setup the isolation host.
/// </summary>
public void SetupHost()
{
List<string> resolutionPaths = this.GetResolutionPaths(this.sourceFileName, VSInstallationUtilities.IsCurrentProcessRunningInPortableMode());
if (EqtTrace.IsInfoEnabled)
{
EqtTrace.Info("DesktopTestSourceHost.SetupHost(): Creating assembly resolver with resolution paths {0}.", string.Join(",", resolutionPaths.ToArray()));
}
// Case when DisableAppDomain setting is present in runsettings and no child-appdomain needs to be created
if (this.isAppDomainCreationDisabled)
{
this.parentDomainAssemblyResolver = new AssemblyResolver(resolutionPaths);
this.AddSearchDirectoriesSpecifiedInRunSettingsToAssemblyResolver(this.parentDomainAssemblyResolver, Path.GetDirectoryName(this.sourceFileName));
}
// Create child-appdomain and set assembly resolver on it
else
{
// Setup app-domain
var appDomainSetup = new AppDomainSetup();
this.targetFrameworkVersion = this.GetTargetFrameworkVersionString(this.sourceFileName);
AppDomainUtilities.SetAppDomainFrameworkVersionBasedOnTestSource(appDomainSetup, this.targetFrameworkVersion);
appDomainSetup.ApplicationBase = this.GetAppBaseAsPerPlatform();
var configFile = this.GetConfigFileForTestSource(this.sourceFileName);
AppDomainUtilities.SetConfigurationFile(appDomainSetup, configFile);
EqtTrace.Info("DesktopTestSourceHost.SetupHost(): Creating app-domain for source {0} with application base path {1}.", this.sourceFileName, appDomainSetup.ApplicationBase);
string domainName = string.Format("TestSourceHost: Enumerating source ({0})", this.sourceFileName);
this.domain = this.appDomain.CreateDomain(domainName, null, appDomainSetup);
// Load objectModel before creating assembly resolver otherwise in 3.5 process, we run into a recursive assembly resolution
// which is trigged by AppContainerUtilities.AttachEventToResolveWinmd method.
EqtTrace.SetupRemoteEqtTraceListeners(this.domain);
// Add an assembly resolver in the child app-domain...
Type assemblyResolverType = typeof(AssemblyResolver);
EqtTrace.Info("DesktopTestSourceHost.SetupHost(): assemblyenumerator location: {0} , fullname: {1} ", assemblyResolverType.Assembly.Location, assemblyResolverType.FullName);
var resolver = AppDomainUtilities.CreateInstance(
this.domain,
assemblyResolverType,
new object[] { resolutionPaths });
EqtTrace.Info(
"DesktopTestSourceHost.SetupHost(): resolver type: {0} , resolve type assembly: {1} ",
resolver.GetType().FullName,
resolver.GetType().Assembly.Location);
this.childDomainAssemblyResolver = (AssemblyResolver)resolver;
this.AddSearchDirectoriesSpecifiedInRunSettingsToAssemblyResolver(this.childDomainAssemblyResolver, Path.GetDirectoryName(this.sourceFileName));
}
}
/// <summary>
/// Creates an instance of a given type in the test source host.
/// </summary>
/// <param name="type"> The type that needs to be created in the host. </param>
/// <param name="args">The arguments to pass to the constructor.
/// This array of arguments must match in number, order, and type the parameters of the constructor to invoke.
/// Pass in null for a constructor with no arguments.
/// </param>
/// <returns> An instance of the type created in the host. </returns>
/// <remarks> If a type is to be created in isolation then it needs to be a MarshalByRefObject. </remarks>
public object CreateInstanceForType(Type type, object[] args)
{
// Honor DisableAppDomain setting if it is present in runsettings
if (this.isAppDomainCreationDisabled)
{
return Activator.CreateInstance(type, args);
}
return AppDomainUtilities.CreateInstance(this.domain, type, args);
}
/// <summary>
/// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
/// </summary>
public void Dispose()
{
if (this.parentDomainAssemblyResolver != null)
{
this.parentDomainAssemblyResolver.Dispose();
this.parentDomainAssemblyResolver = null;
}
if (this.childDomainAssemblyResolver != null)
{
this.childDomainAssemblyResolver.Dispose();
this.childDomainAssemblyResolver = null;
}
if (this.domain != null)
{
try
{
this.appDomain.Unload(this.domain);
}
catch (Exception exception)
{
// This happens usually when a test spawns off a thread and fails to clean it up.
EqtTrace.Error("DesktopTestSourceHost.Dispose(): The app domain running tests could not be unloaded. Exception: {0}", exception);
if (this.frameworkHandle != null)
{
// Let the test platform know that it should tear down the test host process
// since we have issues in unloading appdomain. We do so to avoid any assembly locking issues.
this.frameworkHandle.EnableShutdownAfterTestRun = true;
EqtTrace.Verbose("DesktopTestSourceHost.Dispose(): Notifying the test platform that the test host process should be shut down because the app domain running tests could not be unloaded successfully.");
}
}
this.domain = null;
}
this.ResetContext();
GC.SuppressFinalize(this);
}
/// <summary>
/// Gets child-domain's appbase to point to appropriate location.
/// </summary>
/// <returns>Appbase path that should be set for child appdomain</returns>
internal string GetAppBaseAsPerPlatform()
{
// The below logic of preferential setting the appdomains appbase is needed because:
// 1. We set this to the location of the test source if it is built for Full CLR -> Ideally this needs to be done in all situations.
// 2. We set this to the location where the current adapter is being picked up from for UWP and .Net Core scenarios -> This needs to be
// different especially for UWP because we use the desktop adapter(from %temp%\VisualStudioTestExplorerExtensions) itself for test discovery
// in IDE scenarios. If the app base is set to the test source location, discovery will not work because we drop the
// UWP platform service assembly at the test source location and since CLR starts looking for assemblies from the app base location,
// there would be a mismatch of platform service assemblies during discovery.
if (this.targetFrameworkVersion.Contains(PlatformServices.Constants.DotNetFrameWorkStringPrefix))
{
return Path.GetDirectoryName(this.sourceFileName) ?? Path.GetDirectoryName(typeof(TestSourceHost).Assembly.Location);
}
else
{
return Path.GetDirectoryName(typeof(TestSourceHost).Assembly.Location);
}
}
/// <summary>
/// Gets the probing paths to load the test assembly dependencies.
/// </summary>
/// <param name="sourceFileName">
/// The source File Name.
/// </param>
/// <param name="isPortableMode">
/// True if running in portable mode else false.
/// </param>
/// <returns>
/// A list of path.
/// </returns>
internal virtual List<string> GetResolutionPaths(string sourceFileName, bool isPortableMode)
{
List<string> resolutionPaths = new();
// Add path of test assembly in resolution path. Mostly will be used for resolving winmd.
resolutionPaths.Add(Path.GetDirectoryName(sourceFileName));
if (!isPortableMode)
{
EqtTrace.Info("DesktopTestSourceHost.GetResolutionPaths(): Not running in portable mode");
string pathToPublicAssemblies = VSInstallationUtilities.PathToPublicAssemblies;
if (!StringUtilities.IsNullOrWhiteSpace(pathToPublicAssemblies))
{
resolutionPaths.Add(pathToPublicAssemblies);
}
string pathToPrivateAssemblies = VSInstallationUtilities.PathToPrivateAssemblies;
if (!StringUtilities.IsNullOrWhiteSpace(pathToPrivateAssemblies))
{
resolutionPaths.Add(pathToPrivateAssemblies);
}
}
// Adding adapter folder to resolution paths
if (!resolutionPaths.Contains(Path.GetDirectoryName(typeof(TestSourceHost).Assembly.Location)))
{
resolutionPaths.Add(Path.GetDirectoryName(typeof(TestSourceHost).Assembly.Location));
}
// Adding TestPlatform folder to resolution paths
if (!resolutionPaths.Contains(Path.GetDirectoryName(typeof(AssemblyHelper).Assembly.Location)))
{
resolutionPaths.Add(Path.GetDirectoryName(typeof(AssemblyHelper).Assembly.Location));
}
return resolutionPaths;
}
internal virtual string GetTargetFrameworkVersionString(string sourceFileName)
{
return AppDomainUtilities.GetTargetFrameworkVersionString(sourceFileName);
}
private string GetConfigFileForTestSource(string sourceFileName)
{
return new DeploymentUtility().GetConfigFile(sourceFileName);
}
/// <summary>
/// Sets context required for running tests.
/// </summary>
/// <param name="source">
/// source parameter used for setting context
/// </param>
private void SetContext(string source)
{
if (string.IsNullOrEmpty(source))
{
return;
}
Exception setWorkingDirectoryException = null;
this.currentDirectory = Environment.CurrentDirectory;
try
{
Environment.CurrentDirectory = Path.GetDirectoryName(source);
EqtTrace.Info("MSTestExecutor: Changed the working directory to {0}", Environment.CurrentDirectory);
}
catch (IOException ex)
{
setWorkingDirectoryException = ex;
}
catch (System.Security.SecurityException ex)
{
setWorkingDirectoryException = ex;
}
if (setWorkingDirectoryException != null)
{
EqtTrace.Error("MSTestExecutor.SetWorkingDirectory: Failed to set the working directory to '{0}'. {1}", Path.GetDirectoryName(source), setWorkingDirectoryException);
}
}
/// <summary>
/// Resets the context as it was before calling SetContext()
/// </summary>
private void ResetContext()
{
if (!string.IsNullOrEmpty(this.currentDirectory))
{
Environment.CurrentDirectory = this.currentDirectory;
}
}
private void AddSearchDirectoriesSpecifiedInRunSettingsToAssemblyResolver(AssemblyResolver assemblyResolver, string baseDirectory)
{
// Check if user specified any adapter settings
MSTestAdapterSettings adapterSettings = MSTestSettingsProvider.Settings;
if (adapterSettings != null)
{
try
{
var additionalSearchDirectories = adapterSettings.GetDirectoryListWithRecursiveProperty(baseDirectory);
if (additionalSearchDirectories?.Count > 0)
{
assemblyResolver.AddSearchDirectoriesFromRunSetting(additionalSearchDirectories);
}
}
catch (Exception exception)
{
EqtTrace.Error(
"DesktopTestSourceHost.AddSearchDirectoriesSpecifiedInRunSettingsToAssemblyResolver(): Exception hit while trying to set assembly resolver for domain. Exception : {0} \n Message : {1}",
exception,
exception.Message);
}
}
return this.domain;
}
}
#pragma warning restore SA1649 // SA1649FileNameMustMatchTypeName
/// <summary>
/// Setup the isolation host.
/// </summary>
public void SetupHost()
{
List<string> resolutionPaths = this.GetResolutionPaths(this.sourceFileName, VSInstallationUtilities.IsCurrentProcessRunningInPortableMode());
if (EqtTrace.IsInfoEnabled)
{
EqtTrace.Info("DesktopTestSourceHost.SetupHost(): Creating assembly resolver with resolution paths {0}.", string.Join(",", resolutionPaths.ToArray()));
}
// Case when DisableAppDomain setting is present in runsettings and no child-appdomain needs to be created
if (this.isAppDomainCreationDisabled)
{
this.parentDomainAssemblyResolver = new AssemblyResolver(resolutionPaths);
this.AddSearchDirectoriesSpecifiedInRunSettingsToAssemblyResolver(this.parentDomainAssemblyResolver, Path.GetDirectoryName(this.sourceFileName));
}
// Create child-appdomain and set assembly resolver on it
else
{
// Setup app-domain
var appDomainSetup = new AppDomainSetup();
this.targetFrameworkVersion = this.GetTargetFrameworkVersionString(this.sourceFileName);
AppDomainUtilities.SetAppDomainFrameworkVersionBasedOnTestSource(appDomainSetup, this.targetFrameworkVersion);
appDomainSetup.ApplicationBase = this.GetAppBaseAsPerPlatform();
var configFile = this.GetConfigFileForTestSource(this.sourceFileName);
AppDomainUtilities.SetConfigurationFile(appDomainSetup, configFile);
EqtTrace.Info("DesktopTestSourceHost.SetupHost(): Creating app-domain for source {0} with application base path {1}.", this.sourceFileName, appDomainSetup.ApplicationBase);
string domainName = string.Format("TestSourceHost: Enumerating source ({0})", this.sourceFileName);
this.domain = this.appDomain.CreateDomain(domainName, null, appDomainSetup);
// Load objectModel before creating assembly resolver otherwise in 3.5 process, we run into a recursive assembly resolution
// which is trigged by AppContainerUtilities.AttachEventToResolveWinmd method.
EqtTrace.SetupRemoteEqtTraceListeners(this.domain);
// Add an assembly resolver in the child app-domain...
Type assemblyResolverType = typeof(AssemblyResolver);
EqtTrace.Info("DesktopTestSourceHost.SetupHost(): assemblyenumerator location: {0} , fullname: {1} ", assemblyResolverType.Assembly.Location, assemblyResolverType.FullName);
var resolver = AppDomainUtilities.CreateInstance(
this.domain,
assemblyResolverType,
new object[] { resolutionPaths });
EqtTrace.Info(
"DesktopTestSourceHost.SetupHost(): resolver type: {0} , resolve type assembly: {1} ",
resolver.GetType().FullName,
resolver.GetType().Assembly.Location);
this.childDomainAssemblyResolver = (AssemblyResolver)resolver;
this.AddSearchDirectoriesSpecifiedInRunSettingsToAssemblyResolver(this.childDomainAssemblyResolver, Path.GetDirectoryName(this.sourceFileName));
}
}
/// <summary>
/// Creates an instance of a given type in the test source host.
/// </summary>
/// <param name="type"> The type that needs to be created in the host. </param>
/// <param name="args">The arguments to pass to the constructor.
/// This array of arguments must match in number, order, and type the parameters of the constructor to invoke.
/// Pass in null for a constructor with no arguments.
/// </param>
/// <returns> An instance of the type created in the host. </returns>
/// <remarks> If a type is to be created in isolation then it needs to be a MarshalByRefObject. </remarks>
public object CreateInstanceForType(Type type, object[] args)
{
// Honor DisableAppDomain setting if it is present in runsettings
if (this.isAppDomainCreationDisabled)
{
return Activator.CreateInstance(type, args);
}
return AppDomainUtilities.CreateInstance(this.domain, type, args);
}
/// <summary>
/// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
/// </summary>
public void Dispose()
{
if (this.parentDomainAssemblyResolver != null)
{
this.parentDomainAssemblyResolver.Dispose();
this.parentDomainAssemblyResolver = null;
}
if (this.childDomainAssemblyResolver != null)
{
this.childDomainAssemblyResolver.Dispose();
this.childDomainAssemblyResolver = null;
}
if (this.domain != null)
{
try
{
this.appDomain.Unload(this.domain);
}
catch (Exception exception)
{
// This happens usually when a test spawns off a thread and fails to clean it up.
EqtTrace.Error("DesktopTestSourceHost.Dispose(): The app domain running tests could not be unloaded. Exception: {0}", exception);
if (this.frameworkHandle != null)
{
// Let the test platform know that it should tear down the test host process
// since we have issues in unloading appdomain. We do so to avoid any assembly locking issues.
this.frameworkHandle.EnableShutdownAfterTestRun = true;
EqtTrace.Verbose("DesktopTestSourceHost.Dispose(): Notifying the test platform that the test host process should be shut down because the app domain running tests could not be unloaded successfully.");
}
}
this.domain = null;
}
this.ResetContext();
GC.SuppressFinalize(this);
}
/// <summary>
/// Gets child-domain's appbase to point to appropriate location.
/// </summary>
/// <returns>Appbase path that should be set for child appdomain</returns>
internal string GetAppBaseAsPerPlatform()
{
// The below logic of preferential setting the appdomains appbase is needed because:
// 1. We set this to the location of the test source if it is built for Full CLR -> Ideally this needs to be done in all situations.
// 2. We set this to the location where the current adapter is being picked up from for UWP and .Net Core scenarios -> This needs to be
// different especially for UWP because we use the desktop adapter(from %temp%\VisualStudioTestExplorerExtensions) itself for test discovery
// in IDE scenarios. If the app base is set to the test source location, discovery will not work because we drop the
// UWP platform service assembly at the test source location and since CLR starts looking for assemblies from the app base location,
// there would be a mismatch of platform service assemblies during discovery.
if (this.targetFrameworkVersion.Contains(PlatformServices.Constants.DotNetFrameWorkStringPrefix))
{
return Path.GetDirectoryName(this.sourceFileName) ?? Path.GetDirectoryName(typeof(TestSourceHost).Assembly.Location);
}
else
{
return Path.GetDirectoryName(typeof(TestSourceHost).Assembly.Location);
}
}
/// <summary>
/// Gets the probing paths to load the test assembly dependencies.
/// </summary>
/// <param name="sourceFileName">
/// The source File Name.
/// </param>
/// <param name="isPortableMode">
/// True if running in portable mode else false.
/// </param>
/// <returns>
/// A list of path.
/// </returns>
internal virtual List<string> GetResolutionPaths(string sourceFileName, bool isPortableMode)
{
List<string> resolutionPaths = new();
// Add path of test assembly in resolution path. Mostly will be used for resolving winmd.
resolutionPaths.Add(Path.GetDirectoryName(sourceFileName));
if (!isPortableMode)
{
EqtTrace.Info("DesktopTestSourceHost.GetResolutionPaths(): Not running in portable mode");
string pathToPublicAssemblies = VSInstallationUtilities.PathToPublicAssemblies;
if (!StringUtilities.IsNullOrWhiteSpace(pathToPublicAssemblies))
{
resolutionPaths.Add(pathToPublicAssemblies);
}
string pathToPrivateAssemblies = VSInstallationUtilities.PathToPrivateAssemblies;
if (!StringUtilities.IsNullOrWhiteSpace(pathToPrivateAssemblies))
{
resolutionPaths.Add(pathToPrivateAssemblies);
}
}
// Adding adapter folder to resolution paths
if (!resolutionPaths.Contains(Path.GetDirectoryName(typeof(TestSourceHost).Assembly.Location)))
{
resolutionPaths.Add(Path.GetDirectoryName(typeof(TestSourceHost).Assembly.Location));
}
// Adding TestPlatform folder to resolution paths
if (!resolutionPaths.Contains(Path.GetDirectoryName(typeof(AssemblyHelper).Assembly.Location)))
{
resolutionPaths.Add(Path.GetDirectoryName(typeof(AssemblyHelper).Assembly.Location));
}
return resolutionPaths;
}
internal virtual string GetTargetFrameworkVersionString(string sourceFileName)
{
return AppDomainUtilities.GetTargetFrameworkVersionString(sourceFileName);
}
private string GetConfigFileForTestSource(string sourceFileName)
{
return new DeploymentUtility().GetConfigFile(sourceFileName);
}
/// <summary>
/// Sets context required for running tests.
/// </summary>
/// <param name="source">
/// source parameter used for setting context
/// </param>
private void SetContext(string source)
{
if (string.IsNullOrEmpty(source))
{
return;
}
Exception setWorkingDirectoryException = null;
this.currentDirectory = Environment.CurrentDirectory;
try
{
Environment.CurrentDirectory = Path.GetDirectoryName(source);
EqtTrace.Info("MSTestExecutor: Changed the working directory to {0}", Environment.CurrentDirectory);
}
catch (IOException ex)
{
setWorkingDirectoryException = ex;
}
catch (System.Security.SecurityException ex)
{
setWorkingDirectoryException = ex;
}
if (setWorkingDirectoryException != null)
{
EqtTrace.Error("MSTestExecutor.SetWorkingDirectory: Failed to set the working directory to '{0}'. {1}", Path.GetDirectoryName(source), setWorkingDirectoryException);
}
}
/// <summary>
/// Resets the context as it was before calling SetContext()
/// </summary>
private void ResetContext()
{
if (!string.IsNullOrEmpty(this.currentDirectory))
{
Environment.CurrentDirectory = this.currentDirectory;
}
}
private void AddSearchDirectoriesSpecifiedInRunSettingsToAssemblyResolver(AssemblyResolver assemblyResolver, string baseDirectory)
{
// Check if user specified any adapter settings
MSTestAdapterSettings adapterSettings = MSTestSettingsProvider.Settings;
if (adapterSettings != null)
{
try
{
var additionalSearchDirectories = adapterSettings.GetDirectoryListWithRecursiveProperty(baseDirectory);
if (additionalSearchDirectories?.Count > 0)
{
assemblyResolver.AddSearchDirectoriesFromRunSetting(additionalSearchDirectories);
}
}
catch (Exception exception)
{
EqtTrace.Error(
"DesktopTestSourceHost.AddSearchDirectoriesSpecifiedInRunSettingsToAssemblyResolver(): Exception hit while trying to set assembly resolver for domain. Exception : {0} \n Message : {1}",
exception,
exception.Message);
}
}
}
}
#pragma warning restore SA1649 // SA1649FileNameMustMatchTypeName

Просмотреть файл

@ -1,118 +1,117 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
namespace Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices
{
using System;
using System.Reflection;
using System.Threading;
namespace Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices;
using Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.Interface;
using System;
using System.Reflection;
using System.Threading;
using Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.Interface;
#pragma warning disable SA1649 // SA1649FileNameMustMatchTypeName
/// <summary>
/// This service is responsible for any Async operations specific to a platform.
/// </summary>
public class ThreadOperations : IThreadOperations
{
/// <summary>
/// This service is responsible for any Async operations specific to a platform.
/// Execute the given action synchronously on a background thread in the given timeout.
/// </summary>
public class ThreadOperations : IThreadOperations
/// <param name="action">The action to execute.</param>
/// <param name="timeout">Timeout for the specified action in milliseconds.</param>
/// <param name="cancelToken">Token to cancel the execution</param>
/// <returns>Returns true if the action executed before the timeout. returns false otherwise.</returns>
public bool Execute(Action action, int timeout, CancellationToken cancelToken)
{
/// <summary>
/// Execute the given action synchronously on a background thread in the given timeout.
/// </summary>
/// <param name="action">The action to execute.</param>
/// <param name="timeout">Timeout for the specified action in milliseconds.</param>
/// <param name="cancelToken">Token to cancel the execution</param>
/// <returns>Returns true if the action executed before the timeout. returns false otherwise.</returns>
public bool Execute(Action action, int timeout, CancellationToken cancelToken)
bool executionAborted = false;
Thread executionThread = new(new ThreadStart(action))
{
bool executionAborted = false;
Thread executionThread = new(new ThreadStart(action))
{
IsBackground = true,
Name = "MSTestAdapter Thread"
};
IsBackground = true,
Name = "MSTestAdapter Thread"
};
executionThread.SetApartmentState(Thread.CurrentThread.GetApartmentState());
executionThread.Start();
cancelToken.Register(() =>
{
executionAborted = true;
AbortThread(executionThread);
});
executionThread.SetApartmentState(Thread.CurrentThread.GetApartmentState());
executionThread.Start();
cancelToken.Register(() =>
{
executionAborted = true;
AbortThread(executionThread);
});
if (JoinThread(timeout, executionThread))
if (JoinThread(timeout, executionThread))
{
if (executionAborted)
{
if (executionAborted)
{
return false;
}
// Successfully completed
return true;
}
else if (executionAborted)
{
// Execution aborted due to user choice
return false;
}
else
{
// Timed out
AbortThread(executionThread);
return false;
}
// Successfully completed
return true;
}
/// <summary>
/// Execute an action with handling for Thread Aborts (if possible) so the main thread of the adapter does not die.
/// </summary>
/// <param name="action"> The action to execute. </param>
public void ExecuteWithAbortSafety(Action action)
else if (executionAborted)
{
try
{
action.Invoke();
}
catch (ThreadAbortException exception)
{
Thread.ResetAbort();
// Throwing an exception so that the test is marked as failed.
// This is a TargetInvocation exception because we want just the ThreadAbort exception to be shown to the user and not something we create here.
// TargetInvocation exceptions are stripped off by the test failure handler surfacing the actual exception.
throw new TargetInvocationException(exception);
}
}
private static bool JoinThread(int timeout, Thread executionThread)
{
try
{
return executionThread.Join(timeout);
}
catch (ThreadStateException)
{
// Join was called on a thread not started
}
// Execution aborted due to user choice
return false;
}
private static void AbortThread(Thread executionThread)
else
{
try
{
// Abort test thread after timeout.
executionThread.Abort();
}
catch (ThreadStateException)
{
// Catch and discard ThreadStateException. If Abort is called on a thread that has been suspended,
// a ThreadStateException is thrown in the thread that called Abort,
// and AbortRequested is added to the ThreadState property of the thread being aborted.
// A ThreadAbortException is not thrown in the suspended thread until Resume is called.
}
// Timed out
AbortThread(executionThread);
return false;
}
}
/// <summary>
/// Execute an action with handling for Thread Aborts (if possible) so the main thread of the adapter does not die.
/// </summary>
/// <param name="action"> The action to execute. </param>
public void ExecuteWithAbortSafety(Action action)
{
try
{
action.Invoke();
}
catch (ThreadAbortException exception)
{
Thread.ResetAbort();
// Throwing an exception so that the test is marked as failed.
// This is a TargetInvocation exception because we want just the ThreadAbort exception to be shown to the user and not something we create here.
// TargetInvocation exceptions are stripped off by the test failure handler surfacing the actual exception.
throw new TargetInvocationException(exception);
}
}
private static bool JoinThread(int timeout, Thread executionThread)
{
try
{
return executionThread.Join(timeout);
}
catch (ThreadStateException)
{
// Join was called on a thread not started
}
return false;
}
private static void AbortThread(Thread executionThread)
{
try
{
// Abort test thread after timeout.
executionThread.Abort();
}
catch (ThreadStateException)
{
// Catch and discard ThreadStateException. If Abort is called on a thread that has been suspended,
// a ThreadStateException is thrown in the thread that called Abort,
// and AbortRequested is added to the ThreadState property of the thread being aborted.
// A ThreadAbortException is not thrown in the suspended thread until Resume is called.
}
}
#pragma warning restore SA1649 // SA1649FileNameMustMatchTypeName
}
#pragma warning restore SA1649 // SA1649FileNameMustMatchTypeName

Просмотреть файл

@ -1,249 +1,248 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
namespace Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.Utilities
{
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.IO;
using System.Reflection;
namespace Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.Utilities;
using Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.Deployment;
using Microsoft.VisualStudio.TestPlatform.ObjectModel;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.IO;
using System.Reflection;
using Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.Deployment;
using Microsoft.VisualStudio.TestPlatform.ObjectModel;
/// <summary>
/// Utilities for AppDomain
/// </summary>
internal static class AppDomainUtilities
{
private const string ObjectModelVersionBuiltAgainst = "11.0.0.0";
private static Version defaultVersion = new();
private static Version version45 = new("4.5");
private static XmlUtilities xmlUtilities = null;
/// <summary>
/// Utilities for AppDomain
/// Gets or sets the Xml Utilities instance.
/// </summary>
internal static class AppDomainUtilities
internal static XmlUtilities XmlUtilities
{
private const string ObjectModelVersionBuiltAgainst = "11.0.0.0";
private static Version defaultVersion = new();
private static Version version45 = new("4.5");
private static XmlUtilities xmlUtilities = null;
/// <summary>
/// Gets or sets the Xml Utilities instance.
/// </summary>
internal static XmlUtilities XmlUtilities
get
{
get
{
xmlUtilities ??= new XmlUtilities();
xmlUtilities ??= new XmlUtilities();
return xmlUtilities;
}
set
{
xmlUtilities = value;
}
return xmlUtilities;
}
/// <summary>
/// Set the target framework for app domain setup if target framework of dll is > 4.5
/// </summary>
/// <param name="setup">AppdomainSetup for app domain creation</param>
/// <param name="frameworkVersionString">The target framework version of the test source.</param>
[SuppressMessage("StyleCop.CSharp.DocumentationRules", "SA1650:ElementDocumentationMustBeSpelledCorrectly", Justification = "Reviewed. Suppression is OK here.")]
[SuppressMessage("StyleCop.CSharp.NamingRules", "SA1305:FieldNamesMustNotUseHungarianNotation", Justification = "Reviewed. Suppression is OK here.")]
internal static void SetAppDomainFrameworkVersionBasedOnTestSource(AppDomainSetup setup, string frameworkVersionString)
set
{
if (GetTargetFrameworkVersionFromVersionString(frameworkVersionString).CompareTo(version45) > 0)
{
PropertyInfo pInfo = typeof(AppDomainSetup).GetProperty(PlatformServices.Constants.TargetFrameworkName);
if (pInfo != null)
{
pInfo.SetValue(setup, frameworkVersionString, null);
}
}
}
/// <summary>
/// Get target framework version string from the given dll
/// </summary>
/// <param name="testSourcePath">
/// The path of the dll
/// </param>
/// <returns>
/// Framework string
/// TODO: Need to add components/E2E tests to cover these scenarios.
/// </returns>
[SuppressMessage("StyleCop.CSharp.DocumentationRules", "SA1650:ElementDocumentationMustBeSpelledCorrectly", Justification = "Reviewed. Suppression is OK here.")]
internal static string GetTargetFrameworkVersionString(string testSourcePath)
{
AppDomainSetup appDomainSetup = new();
appDomainSetup.LoaderOptimization = LoaderOptimization.MultiDomainHost;
AppDomainUtilities.SetConfigurationFile(appDomainSetup, new DeploymentUtility().GetConfigFile(testSourcePath));
if (File.Exists(testSourcePath))
{
AppDomain appDomain = null;
try
{
appDomain = AppDomain.CreateDomain("Framework Version String Domain", null, appDomainSetup);
// Wire the eqttrace logs in this domain to the current domain.
EqtTrace.SetupRemoteEqtTraceListeners(appDomain);
// Add an assembly resolver to resolve ObjectModel or any Test Platform dependencies.
// Not moving to IMetaDataImport APIs because the time taken for this operation is <20 ms and
// IMetaDataImport needs COM registration which is not a guarantee in Dev15.
var assemblyResolverType = typeof(AssemblyResolver);
var resolutionPaths = new List<string> { Path.GetDirectoryName(typeof(TestCase).Assembly.Location) };
resolutionPaths.Add(Path.GetDirectoryName(testSourcePath));
AppDomainUtilities.CreateInstance(
appDomain,
assemblyResolverType,
new object[] { resolutionPaths });
var assemblyLoadWorker =
(AssemblyLoadWorker)AppDomainUtilities.CreateInstance(
appDomain,
typeof(AssemblyLoadWorker),
null);
return assemblyLoadWorker.GetTargetFrameworkVersionStringFromPath(testSourcePath);
}
catch (Exception exception)
{
if (EqtTrace.IsErrorEnabled)
{
EqtTrace.Error(exception);
}
}
finally
{
if (appDomain != null)
{
AppDomain.Unload(appDomain);
}
}
}
return string.Empty;
}
/// <summary>
/// Set configuration file on the parameter appDomain.
/// </summary>
/// <param name="appDomainSetup"> The app Domain Setup. </param>
/// <param name="testSourceConfigFile"> The test Source Config File. </param>
[SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Justification = "Requirement is to handle all kinds of user exceptions and message appropriately.")]
internal static void SetConfigurationFile(AppDomainSetup appDomainSetup, string testSourceConfigFile)
{
if (!string.IsNullOrEmpty(testSourceConfigFile))
{
if (EqtTrace.IsInfoEnabled)
{
EqtTrace.Info("UnitTestAdapter: Using configuration file {0} to setup appdomain for test source {1}.", testSourceConfigFile, Path.GetFileNameWithoutExtension(testSourceConfigFile));
}
appDomainSetup.ConfigurationFile = Path.GetFullPath(testSourceConfigFile);
try
{
// Add redirection of the built 11.0 Object Model assembly to the current version if that is not 11.0
var currentVersionOfObjectModel = typeof(TestCase).Assembly.GetName().Version.ToString();
if (!string.Equals(currentVersionOfObjectModel, ObjectModelVersionBuiltAgainst))
{
var assemblyName = typeof(TestCase).Assembly.GetName();
var configurationBytes =
XmlUtilities.AddAssemblyRedirection(
testSourceConfigFile,
assemblyName,
ObjectModelVersionBuiltAgainst,
assemblyName.Version.ToString());
appDomainSetup.SetConfigurationBytes(configurationBytes);
}
}
catch (Exception ex)
{
if (EqtTrace.IsErrorEnabled)
{
EqtTrace.Error("Exception hit while adding binding redirects to test source config file. Exception : {0}", ex);
}
}
}
else
{
// Use the current domains configuration setting.
appDomainSetup.ConfigurationFile = AppDomain.CurrentDomain.SetupInformation.ConfigurationFile;
}
}
internal static object CreateInstance(AppDomain appDomain, Type type, object[] arguments)
{
Debug.Assert(appDomain != null, "appDomain is null");
Debug.Assert(type != null, "type is null");
var typeAssemblyLocation = type.Assembly.Location;
var fullFilePath = typeAssemblyLocation == null ? null : Path.Combine(appDomain.SetupInformation.ApplicationBase, Path.GetFileName(typeAssemblyLocation));
if (fullFilePath == null || File.Exists(fullFilePath))
{
// If the assembly exists in the app base directory, load it from there itself.
// Even if it does not exist, Create the type in the default Load Context and let the CLR resolve the assembly path.
// This would load the assembly in the Default Load context.
return appDomain.CreateInstanceAndUnwrap(
type.Assembly.FullName,
type.FullName,
false,
BindingFlags.Default,
null,
arguments,
null,
null);
}
else
{
// This means that the file is not present in the app base directory. Load it from Path instead.
// NOTE: We expect that all types that we are creating from here are types we know the location for.
// This would load the assembly in the Load-From context.
// While the above if condition is satisfied for most common cases, there could be a case where the adapter dlls
// do not get copied over to where the test assembly is, in which case we load them from where the parent AppDomain is picking them up from.
return appDomain.CreateInstanceFromAndUnwrap(
typeAssemblyLocation,
type.FullName,
false,
BindingFlags.Default,
null,
arguments,
null,
null);
}
}
/// <summary>
/// Get the Version for the target framework version string
/// </summary>
/// <param name="version">Target framework string</param>
/// <returns>Framework Version</returns>
internal static Version GetTargetFrameworkVersionFromVersionString(string version)
{
try
{
if (version.Length > PlatformServices.Constants.DotNetFrameWorkStringPrefix.Length + 1)
{
string versionPart = version.Substring(PlatformServices.Constants.DotNetFrameWorkStringPrefix.Length + 1);
return new Version(versionPart);
}
}
catch (FormatException ex)
{
// if the version is ".NETPortable,Version=v4.5,Profile=Profile259", then above code will throw exception.
EqtTrace.Warning(string.Format("AppDomainUtilities.GetTargetFrameworkVersionFromVersionString: Could not create version object from version string '{0}' due to error '{1}':", version, ex.Message));
}
return defaultVersion;
xmlUtilities = value;
}
}
/// <summary>
/// Set the target framework for app domain setup if target framework of dll is > 4.5
/// </summary>
/// <param name="setup">AppdomainSetup for app domain creation</param>
/// <param name="frameworkVersionString">The target framework version of the test source.</param>
[SuppressMessage("StyleCop.CSharp.DocumentationRules", "SA1650:ElementDocumentationMustBeSpelledCorrectly", Justification = "Reviewed. Suppression is OK here.")]
[SuppressMessage("StyleCop.CSharp.NamingRules", "SA1305:FieldNamesMustNotUseHungarianNotation", Justification = "Reviewed. Suppression is OK here.")]
internal static void SetAppDomainFrameworkVersionBasedOnTestSource(AppDomainSetup setup, string frameworkVersionString)
{
if (GetTargetFrameworkVersionFromVersionString(frameworkVersionString).CompareTo(version45) > 0)
{
PropertyInfo pInfo = typeof(AppDomainSetup).GetProperty(PlatformServices.Constants.TargetFrameworkName);
if (pInfo != null)
{
pInfo.SetValue(setup, frameworkVersionString, null);
}
}
}
/// <summary>
/// Get target framework version string from the given dll
/// </summary>
/// <param name="testSourcePath">
/// The path of the dll
/// </param>
/// <returns>
/// Framework string
/// TODO: Need to add components/E2E tests to cover these scenarios.
/// </returns>
[SuppressMessage("StyleCop.CSharp.DocumentationRules", "SA1650:ElementDocumentationMustBeSpelledCorrectly", Justification = "Reviewed. Suppression is OK here.")]
internal static string GetTargetFrameworkVersionString(string testSourcePath)
{
AppDomainSetup appDomainSetup = new();
appDomainSetup.LoaderOptimization = LoaderOptimization.MultiDomainHost;
AppDomainUtilities.SetConfigurationFile(appDomainSetup, new DeploymentUtility().GetConfigFile(testSourcePath));
if (File.Exists(testSourcePath))
{
AppDomain appDomain = null;
try
{
appDomain = AppDomain.CreateDomain("Framework Version String Domain", null, appDomainSetup);
// Wire the eqttrace logs in this domain to the current domain.
EqtTrace.SetupRemoteEqtTraceListeners(appDomain);
// Add an assembly resolver to resolve ObjectModel or any Test Platform dependencies.
// Not moving to IMetaDataImport APIs because the time taken for this operation is <20 ms and
// IMetaDataImport needs COM registration which is not a guarantee in Dev15.
var assemblyResolverType = typeof(AssemblyResolver);
var resolutionPaths = new List<string> { Path.GetDirectoryName(typeof(TestCase).Assembly.Location) };
resolutionPaths.Add(Path.GetDirectoryName(testSourcePath));
AppDomainUtilities.CreateInstance(
appDomain,
assemblyResolverType,
new object[] { resolutionPaths });
var assemblyLoadWorker =
(AssemblyLoadWorker)AppDomainUtilities.CreateInstance(
appDomain,
typeof(AssemblyLoadWorker),
null);
return assemblyLoadWorker.GetTargetFrameworkVersionStringFromPath(testSourcePath);
}
catch (Exception exception)
{
if (EqtTrace.IsErrorEnabled)
{
EqtTrace.Error(exception);
}
}
finally
{
if (appDomain != null)
{
AppDomain.Unload(appDomain);
}
}
}
return string.Empty;
}
/// <summary>
/// Set configuration file on the parameter appDomain.
/// </summary>
/// <param name="appDomainSetup"> The app Domain Setup. </param>
/// <param name="testSourceConfigFile"> The test Source Config File. </param>
[SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Justification = "Requirement is to handle all kinds of user exceptions and message appropriately.")]
internal static void SetConfigurationFile(AppDomainSetup appDomainSetup, string testSourceConfigFile)
{
if (!string.IsNullOrEmpty(testSourceConfigFile))
{
if (EqtTrace.IsInfoEnabled)
{
EqtTrace.Info("UnitTestAdapter: Using configuration file {0} to setup appdomain for test source {1}.", testSourceConfigFile, Path.GetFileNameWithoutExtension(testSourceConfigFile));
}
appDomainSetup.ConfigurationFile = Path.GetFullPath(testSourceConfigFile);
try
{
// Add redirection of the built 11.0 Object Model assembly to the current version if that is not 11.0
var currentVersionOfObjectModel = typeof(TestCase).Assembly.GetName().Version.ToString();
if (!string.Equals(currentVersionOfObjectModel, ObjectModelVersionBuiltAgainst))
{
var assemblyName = typeof(TestCase).Assembly.GetName();
var configurationBytes =
XmlUtilities.AddAssemblyRedirection(
testSourceConfigFile,
assemblyName,
ObjectModelVersionBuiltAgainst,
assemblyName.Version.ToString());
appDomainSetup.SetConfigurationBytes(configurationBytes);
}
}
catch (Exception ex)
{
if (EqtTrace.IsErrorEnabled)
{
EqtTrace.Error("Exception hit while adding binding redirects to test source config file. Exception : {0}", ex);
}
}
}
else
{
// Use the current domains configuration setting.
appDomainSetup.ConfigurationFile = AppDomain.CurrentDomain.SetupInformation.ConfigurationFile;
}
}
internal static object CreateInstance(AppDomain appDomain, Type type, object[] arguments)
{
Debug.Assert(appDomain != null, "appDomain is null");
Debug.Assert(type != null, "type is null");
var typeAssemblyLocation = type.Assembly.Location;
var fullFilePath = typeAssemblyLocation == null ? null : Path.Combine(appDomain.SetupInformation.ApplicationBase, Path.GetFileName(typeAssemblyLocation));
if (fullFilePath == null || File.Exists(fullFilePath))
{
// If the assembly exists in the app base directory, load it from there itself.
// Even if it does not exist, Create the type in the default Load Context and let the CLR resolve the assembly path.
// This would load the assembly in the Default Load context.
return appDomain.CreateInstanceAndUnwrap(
type.Assembly.FullName,
type.FullName,
false,
BindingFlags.Default,
null,
arguments,
null,
null);
}
else
{
// This means that the file is not present in the app base directory. Load it from Path instead.
// NOTE: We expect that all types that we are creating from here are types we know the location for.
// This would load the assembly in the Load-From context.
// While the above if condition is satisfied for most common cases, there could be a case where the adapter dlls
// do not get copied over to where the test assembly is, in which case we load them from where the parent AppDomain is picking them up from.
return appDomain.CreateInstanceFromAndUnwrap(
typeAssemblyLocation,
type.FullName,
false,
BindingFlags.Default,
null,
arguments,
null,
null);
}
}
/// <summary>
/// Get the Version for the target framework version string
/// </summary>
/// <param name="version">Target framework string</param>
/// <returns>Framework Version</returns>
internal static Version GetTargetFrameworkVersionFromVersionString(string version)
{
try
{
if (version.Length > PlatformServices.Constants.DotNetFrameWorkStringPrefix.Length + 1)
{
string versionPart = version.Substring(PlatformServices.Constants.DotNetFrameWorkStringPrefix.Length + 1);
return new Version(versionPart);
}
}
catch (FormatException ex)
{
// if the version is ".NETPortable,Version=v4.5,Profile=Profile259", then above code will throw exception.
EqtTrace.Warning(string.Format("AppDomainUtilities.GetTargetFrameworkVersionFromVersionString: Could not create version object from version string '{0}' due to error '{1}':", version, ex.Message));
}
return defaultVersion;
}
}

Просмотреть файл

@ -1,24 +1,23 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
namespace Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices
namespace Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices;
using System;
using System.Security.Policy;
/// <summary>
/// Abstraction over the AppDomain APIs.
/// </summary>
internal class AppDomainWrapper : IAppDomain
{
using System;
using System.Security.Policy;
/// <summary>
/// Abstraction over the AppDomain APIs.
/// </summary>
internal class AppDomainWrapper : IAppDomain
public AppDomain CreateDomain(string friendlyName, Evidence securityInfo, AppDomainSetup info)
{
public AppDomain CreateDomain(string friendlyName, Evidence securityInfo, AppDomainSetup info)
{
return AppDomain.CreateDomain(friendlyName, securityInfo, info);
}
return AppDomain.CreateDomain(friendlyName, securityInfo, info);
}
public void Unload(AppDomain appDomain)
{
AppDomain.Unload(appDomain);
}
public void Unload(AppDomain appDomain)
{
AppDomain.Unload(appDomain);
}
}

Просмотреть файл

@ -1,279 +1,278 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
namespace Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.Utilities
{
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Reflection;
namespace Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.Utilities;
using Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.Deployment;
using Microsoft.VisualStudio.TestPlatform.ObjectModel;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Reflection;
using Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.Deployment;
using Microsoft.VisualStudio.TestPlatform.ObjectModel;
/// <summary>
/// Utility for assembly specific functionality.
/// </summary>
internal class AssemblyUtility : IAssemblyUtility
{
private static Dictionary<string, object> cultures;
private readonly string[] assemblyExtensions = new string[] { ".dll", ".exe" };
/// <summary>
/// Utility for assembly specific functionality.
/// Gets all supported culture names in Keys. The Values are always null.
/// </summary>
internal class AssemblyUtility : IAssemblyUtility
private static Dictionary<string, object> Cultures
{
private static Dictionary<string, object> cultures;
private readonly string[] assemblyExtensions = new string[] { ".dll", ".exe" };
/// <summary>
/// Gets all supported culture names in Keys. The Values are always null.
/// </summary>
private static Dictionary<string, object> Cultures
get
{
get
if (cultures == null)
{
if (cultures == null)
cultures = new Dictionary<string, object>(StringComparer.OrdinalIgnoreCase);
foreach (var info in CultureInfo.GetCultures(CultureTypes.AllCultures))
{
cultures = new Dictionary<string, object>(StringComparer.OrdinalIgnoreCase);
foreach (var info in CultureInfo.GetCultures(CultureTypes.AllCultures))
{
cultures.Add(info.Name, null);
}
}
return cultures;
}
}
/// <summary>
/// Loads an assembly into the reflection-only context, given its path.
/// </summary>
/// <param name="assemblyPath">The path of the file that contains the manifest of the assembly.</param>
/// <returns>The loaded assembly.</returns>
public Assembly ReflectionOnlyLoadFrom(string assemblyPath)
{
return Assembly.ReflectionOnlyLoadFrom(assemblyPath);
}
/// <summary>
/// Loads an assembly into the reflection-only context, given its display name.
/// </summary>
/// <param name="assemblyString">The display name of the assembly, as returned by the System.Reflection.AssemblyName.FullName property.</param>
/// <returns>The loaded assembly.</returns>
public Assembly ReflectionOnlyLoad(string assemblyString)
{
return Assembly.ReflectionOnlyLoad(assemblyString);
}
/// <summary>
/// Whether file extension is an assembly file extension.
/// Returns true for .exe and .dll, otherwise false.
/// </summary>
/// <param name="extensionWithLeadingDot"> Extension containing leading dot, e.g. ".exe". </param>
/// <remarks> Path.GetExtension() returns extension with leading dot. </remarks>
/// <returns> True if this is an assembly extension. </returns>
internal bool IsAssemblyExtension(string extensionWithLeadingDot)
{
foreach (var realExtension in this.assemblyExtensions)
{
if (string.Equals(extensionWithLeadingDot, realExtension, StringComparison.OrdinalIgnoreCase))
{
return true;
cultures.Add(info.Name, null);
}
}
return false;
}
/// <summary>
/// Determines whether given file is managed assembly. Does not load the assembly. Does not check file extension.
/// Performance: takes ~0.1 seconds on 2x CPU P4.
/// </summary>
/// <param name="path"> The path to the assembly. </param>
/// <returns> True if managed assembly. </returns>
internal bool IsAssembly(string path)
{
Debug.Assert(!string.IsNullOrEmpty(path), "path");
try
{
// AssemblyName.GetAssemblyName: causes the file to be opened and closed, but the assembly is not added to this domain.
// Also if there are dependencies, they are never loaded.
AssemblyName.GetAssemblyName(path);
return true;
}
catch (FileLoadException)
{
// This is an executable image but not an assembly.
}
catch (BadImageFormatException)
{
// Happens when file is not a DLL/EXE, etc.
}
// If file cannot be found we will throw.
// If there's anything else like SecurityException - we just pass exception through.
return false;
}
/// <summary>
/// Returns satellite assemblies. Returns full canonicalized paths.
/// If the file is not an assembly returns empty list.
/// </summary>
/// <param name="assemblyPath"> The assembly to get satellites for. </param>
/// <returns> List of satellite assemblies. </returns>
internal virtual List<string> GetSatelliteAssemblies(string assemblyPath)
{
if (!this.IsAssemblyExtension(Path.GetExtension(assemblyPath)) || !this.IsAssembly(assemblyPath))
{
EqtTrace.ErrorIf(
EqtTrace.IsErrorEnabled,
"AssemblyUtilities.GetSatelliteAssemblies: the specified file '{0}' is not managed assembly.",
assemblyPath);
Debug.Fail("AssemblyUtilities.GetSatelliteAssemblies: the file '" + assemblyPath + "' is not an assembly.");
// If e.g. this is unmanaged dll, we don't care about the satellites.
return new List<string>();
}
assemblyPath = Path.GetFullPath(assemblyPath);
var assemblyDir = Path.GetDirectoryName(assemblyPath);
var satellites = new List<string>();
// Directory.Exists for 266 dirs takes 9ms while Path.GetDirectories can take up to 80ms on 10k dirs.
foreach (string dir in Cultures.Keys)
{
var dirPath = Path.Combine(assemblyDir, dir);
if (!Directory.Exists(dirPath))
{
continue;
}
// Check if the satellite exists in this dir.
// We check filenames like: MyAssembly.dll -> MyAssembly.resources.dll.
// Surprisingly, but both DLL and EXE are found by resource manager.
foreach (var extension in this.assemblyExtensions)
{
// extension contains leading dot.
string satellite = Path.ChangeExtension(Path.GetFileName(assemblyPath), "resources" + extension);
string satellitePath = Path.Combine(assemblyDir, Path.Combine(dir, satellite));
// We don't use Assembly.LoadFrom/Assembly.GetSatelliteAssebmlies because this is rather slow
// (1620ms for 266 cultures when directories do not exist).
if (File.Exists(satellitePath))
{
// If the satellite found is not a managed assembly we do not report it as a reference.
if (!this.IsAssembly(satellitePath))
{
EqtTrace.ErrorIf(
EqtTrace.IsErrorEnabled,
"AssemblyUtilities.GetSatelliteAssemblies: found assembly '{0}' installed as satellite but it's not managed assembly.",
satellitePath);
continue;
}
// If both .exe and .dll exist we return both silently.
satellites.Add(satellitePath);
}
}
}
return satellites;
}
/// <summary>
/// Returns the dependent assemblies of the parameter assembly.
/// </summary>
/// <param name="assemblyPath"> Path to assembly to get dependencies for. </param>
/// <param name="configFile"> Config file to use while trying to resolve dependencies. </param>
/// <param name="warnings"> The warnings. </param>
/// <returns> The <see cref="T:string[]"/>. </returns>
internal virtual string[] GetFullPathToDependentAssemblies(string assemblyPath, string configFile, out IList<string> warnings)
{
Debug.Assert(!string.IsNullOrEmpty(assemblyPath), "assemblyPath");
EqtTrace.InfoIf(EqtTrace.IsInfoEnabled, "AssemblyDependencyFinder.GetDependentAssemblies: start.");
AppDomainSetup setupInfo = new();
var dllDirectory = Path.GetDirectoryName(Path.GetFullPath(assemblyPath));
setupInfo.ApplicationBase = dllDirectory;
Debug.Assert(string.IsNullOrEmpty(configFile) || File.Exists(configFile), "Config file is specified but does not exist: {0}", configFile);
AppDomainUtilities.SetConfigurationFile(setupInfo, configFile);
EqtTrace.InfoIf(EqtTrace.IsInfoEnabled, "AssemblyDependencyFinder.GetDependentAssemblies: Using config file: '{0}'.", setupInfo.ConfigurationFile);
setupInfo.LoaderOptimization = LoaderOptimization.MultiDomainHost;
AppDomain appDomain = null;
try
{
appDomain = AppDomain.CreateDomain("Dependency finder domain", null, setupInfo);
if (EqtTrace.IsInfoEnabled)
{
EqtTrace.Info("AssemblyDependencyFinder.GetDependentAssemblies: Created AppDomain.");
}
var assemblyResolverType = typeof(AssemblyResolver);
EqtTrace.SetupRemoteEqtTraceListeners(appDomain);
// This has to be LoadFrom, otherwise we will have to use AssemblyResolver to find self.
using AssemblyResolver resolver =
(AssemblyResolver)AppDomainUtilities.CreateInstance(
appDomain,
assemblyResolverType,
new object[] { this.GetResolutionPaths() });
// This has to be Load, otherwise Serialization of argument types will not work correctly.
AssemblyLoadWorker worker =
(AssemblyLoadWorker)AppDomainUtilities.CreateInstance(appDomain, typeof(AssemblyLoadWorker), null);
EqtTrace.InfoIf(EqtTrace.IsInfoEnabled, "AssemblyDependencyFinder.GetDependentAssemblies: loaded the worker.");
var allDependencies = worker.GetFullPathToDependentAssemblies(assemblyPath, out warnings);
var dependenciesFromDllDirectory = new List<string>();
var dllDirectoryUppercase = dllDirectory.ToUpperInvariant();
foreach (var dependency in allDependencies)
{
if (dependency.ToUpperInvariant().Contains(dllDirectoryUppercase))
{
dependenciesFromDllDirectory.Add(dependency);
}
}
return dependenciesFromDllDirectory.ToArray();
}
finally
{
if (appDomain != null)
{
EqtTrace.InfoIf(EqtTrace.IsInfoEnabled, "AssemblyDependencyFinder.GetDependentAssemblies: unloading AppDomain...");
AppDomain.Unload(appDomain);
EqtTrace.InfoIf(EqtTrace.IsInfoEnabled, "AssemblyDependencyFinder.GetDependentAssemblies: unloading AppDomain succeeded.");
}
}
}
/// <summary>
/// Gets the resolution paths for app domain creation.
/// </summary>
/// <returns> The <see cref="IList{T}"/> of resolution paths. </returns>
internal IList<string> GetResolutionPaths()
{
// Use dictionary to ensure we get a list of unique paths, but keep a list as the
// dictionary does not guarantee order.
Dictionary<string, object> resolutionPathsDictionary = new(StringComparer.OrdinalIgnoreCase);
List<string> resolutionPaths = new();
// Add the path of the currently executing assembly (use Uri(CodeBase).LocalPath as Location can be on shadow dir).
string currentlyExecutingAssembly = Path.GetDirectoryName(Path.GetFullPath(new Uri(Assembly.GetExecutingAssembly().CodeBase).LocalPath));
resolutionPaths.Add(currentlyExecutingAssembly);
resolutionPathsDictionary[currentlyExecutingAssembly] = null;
// Add the application base for this domain.
if (!resolutionPathsDictionary.ContainsKey(AppDomain.CurrentDomain.BaseDirectory))
{
resolutionPaths.Add(AppDomain.CurrentDomain.BaseDirectory);
resolutionPathsDictionary[AppDomain.CurrentDomain.BaseDirectory] = null;
}
return resolutionPaths;
return cultures;
}
}
/// <summary>
/// Loads an assembly into the reflection-only context, given its path.
/// </summary>
/// <param name="assemblyPath">The path of the file that contains the manifest of the assembly.</param>
/// <returns>The loaded assembly.</returns>
public Assembly ReflectionOnlyLoadFrom(string assemblyPath)
{
return Assembly.ReflectionOnlyLoadFrom(assemblyPath);
}
/// <summary>
/// Loads an assembly into the reflection-only context, given its display name.
/// </summary>
/// <param name="assemblyString">The display name of the assembly, as returned by the System.Reflection.AssemblyName.FullName property.</param>
/// <returns>The loaded assembly.</returns>
public Assembly ReflectionOnlyLoad(string assemblyString)
{
return Assembly.ReflectionOnlyLoad(assemblyString);
}
/// <summary>
/// Whether file extension is an assembly file extension.
/// Returns true for .exe and .dll, otherwise false.
/// </summary>
/// <param name="extensionWithLeadingDot"> Extension containing leading dot, e.g. ".exe". </param>
/// <remarks> Path.GetExtension() returns extension with leading dot. </remarks>
/// <returns> True if this is an assembly extension. </returns>
internal bool IsAssemblyExtension(string extensionWithLeadingDot)
{
foreach (var realExtension in this.assemblyExtensions)
{
if (string.Equals(extensionWithLeadingDot, realExtension, StringComparison.OrdinalIgnoreCase))
{
return true;
}
}
return false;
}
/// <summary>
/// Determines whether given file is managed assembly. Does not load the assembly. Does not check file extension.
/// Performance: takes ~0.1 seconds on 2x CPU P4.
/// </summary>
/// <param name="path"> The path to the assembly. </param>
/// <returns> True if managed assembly. </returns>
internal bool IsAssembly(string path)
{
Debug.Assert(!string.IsNullOrEmpty(path), "path");
try
{
// AssemblyName.GetAssemblyName: causes the file to be opened and closed, but the assembly is not added to this domain.
// Also if there are dependencies, they are never loaded.
AssemblyName.GetAssemblyName(path);
return true;
}
catch (FileLoadException)
{
// This is an executable image but not an assembly.
}
catch (BadImageFormatException)
{
// Happens when file is not a DLL/EXE, etc.
}
// If file cannot be found we will throw.
// If there's anything else like SecurityException - we just pass exception through.
return false;
}
/// <summary>
/// Returns satellite assemblies. Returns full canonicalized paths.
/// If the file is not an assembly returns empty list.
/// </summary>
/// <param name="assemblyPath"> The assembly to get satellites for. </param>
/// <returns> List of satellite assemblies. </returns>
internal virtual List<string> GetSatelliteAssemblies(string assemblyPath)
{
if (!this.IsAssemblyExtension(Path.GetExtension(assemblyPath)) || !this.IsAssembly(assemblyPath))
{
EqtTrace.ErrorIf(
EqtTrace.IsErrorEnabled,
"AssemblyUtilities.GetSatelliteAssemblies: the specified file '{0}' is not managed assembly.",
assemblyPath);
Debug.Fail("AssemblyUtilities.GetSatelliteAssemblies: the file '" + assemblyPath + "' is not an assembly.");
// If e.g. this is unmanaged dll, we don't care about the satellites.
return new List<string>();
}
assemblyPath = Path.GetFullPath(assemblyPath);
var assemblyDir = Path.GetDirectoryName(assemblyPath);
var satellites = new List<string>();
// Directory.Exists for 266 dirs takes 9ms while Path.GetDirectories can take up to 80ms on 10k dirs.
foreach (string dir in Cultures.Keys)
{
var dirPath = Path.Combine(assemblyDir, dir);
if (!Directory.Exists(dirPath))
{
continue;
}
// Check if the satellite exists in this dir.
// We check filenames like: MyAssembly.dll -> MyAssembly.resources.dll.
// Surprisingly, but both DLL and EXE are found by resource manager.
foreach (var extension in this.assemblyExtensions)
{
// extension contains leading dot.
string satellite = Path.ChangeExtension(Path.GetFileName(assemblyPath), "resources" + extension);
string satellitePath = Path.Combine(assemblyDir, Path.Combine(dir, satellite));
// We don't use Assembly.LoadFrom/Assembly.GetSatelliteAssebmlies because this is rather slow
// (1620ms for 266 cultures when directories do not exist).
if (File.Exists(satellitePath))
{
// If the satellite found is not a managed assembly we do not report it as a reference.
if (!this.IsAssembly(satellitePath))
{
EqtTrace.ErrorIf(
EqtTrace.IsErrorEnabled,
"AssemblyUtilities.GetSatelliteAssemblies: found assembly '{0}' installed as satellite but it's not managed assembly.",
satellitePath);
continue;
}
// If both .exe and .dll exist we return both silently.
satellites.Add(satellitePath);
}
}
}
return satellites;
}
/// <summary>
/// Returns the dependent assemblies of the parameter assembly.
/// </summary>
/// <param name="assemblyPath"> Path to assembly to get dependencies for. </param>
/// <param name="configFile"> Config file to use while trying to resolve dependencies. </param>
/// <param name="warnings"> The warnings. </param>
/// <returns> The <see cref="T:string[]"/>. </returns>
internal virtual string[] GetFullPathToDependentAssemblies(string assemblyPath, string configFile, out IList<string> warnings)
{
Debug.Assert(!string.IsNullOrEmpty(assemblyPath), "assemblyPath");
EqtTrace.InfoIf(EqtTrace.IsInfoEnabled, "AssemblyDependencyFinder.GetDependentAssemblies: start.");
AppDomainSetup setupInfo = new();
var dllDirectory = Path.GetDirectoryName(Path.GetFullPath(assemblyPath));
setupInfo.ApplicationBase = dllDirectory;
Debug.Assert(string.IsNullOrEmpty(configFile) || File.Exists(configFile), "Config file is specified but does not exist: {0}", configFile);
AppDomainUtilities.SetConfigurationFile(setupInfo, configFile);
EqtTrace.InfoIf(EqtTrace.IsInfoEnabled, "AssemblyDependencyFinder.GetDependentAssemblies: Using config file: '{0}'.", setupInfo.ConfigurationFile);
setupInfo.LoaderOptimization = LoaderOptimization.MultiDomainHost;
AppDomain appDomain = null;
try
{
appDomain = AppDomain.CreateDomain("Dependency finder domain", null, setupInfo);
if (EqtTrace.IsInfoEnabled)
{
EqtTrace.Info("AssemblyDependencyFinder.GetDependentAssemblies: Created AppDomain.");
}
var assemblyResolverType = typeof(AssemblyResolver);
EqtTrace.SetupRemoteEqtTraceListeners(appDomain);
// This has to be LoadFrom, otherwise we will have to use AssemblyResolver to find self.
using AssemblyResolver resolver =
(AssemblyResolver)AppDomainUtilities.CreateInstance(
appDomain,
assemblyResolverType,
new object[] { this.GetResolutionPaths() });
// This has to be Load, otherwise Serialization of argument types will not work correctly.
AssemblyLoadWorker worker =
(AssemblyLoadWorker)AppDomainUtilities.CreateInstance(appDomain, typeof(AssemblyLoadWorker), null);
EqtTrace.InfoIf(EqtTrace.IsInfoEnabled, "AssemblyDependencyFinder.GetDependentAssemblies: loaded the worker.");
var allDependencies = worker.GetFullPathToDependentAssemblies(assemblyPath, out warnings);
var dependenciesFromDllDirectory = new List<string>();
var dllDirectoryUppercase = dllDirectory.ToUpperInvariant();
foreach (var dependency in allDependencies)
{
if (dependency.ToUpperInvariant().Contains(dllDirectoryUppercase))
{
dependenciesFromDllDirectory.Add(dependency);
}
}
return dependenciesFromDllDirectory.ToArray();
}
finally
{
if (appDomain != null)
{
EqtTrace.InfoIf(EqtTrace.IsInfoEnabled, "AssemblyDependencyFinder.GetDependentAssemblies: unloading AppDomain...");
AppDomain.Unload(appDomain);
EqtTrace.InfoIf(EqtTrace.IsInfoEnabled, "AssemblyDependencyFinder.GetDependentAssemblies: unloading AppDomain succeeded.");
}
}
}
/// <summary>
/// Gets the resolution paths for app domain creation.
/// </summary>
/// <returns> The <see cref="IList{T}"/> of resolution paths. </returns>
internal IList<string> GetResolutionPaths()
{
// Use dictionary to ensure we get a list of unique paths, but keep a list as the
// dictionary does not guarantee order.
Dictionary<string, object> resolutionPathsDictionary = new(StringComparer.OrdinalIgnoreCase);
List<string> resolutionPaths = new();
// Add the path of the currently executing assembly (use Uri(CodeBase).LocalPath as Location can be on shadow dir).
string currentlyExecutingAssembly = Path.GetDirectoryName(Path.GetFullPath(new Uri(Assembly.GetExecutingAssembly().CodeBase).LocalPath));
resolutionPaths.Add(currentlyExecutingAssembly);
resolutionPathsDictionary[currentlyExecutingAssembly] = null;
// Add the application base for this domain.
if (!resolutionPathsDictionary.ContainsKey(AppDomain.CurrentDomain.BaseDirectory))
{
resolutionPaths.Add(AppDomain.CurrentDomain.BaseDirectory);
resolutionPathsDictionary[AppDomain.CurrentDomain.BaseDirectory] = null;
}
return resolutionPaths;
}
}

Просмотреть файл

@ -1,249 +1,248 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
namespace Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.Utilities
namespace Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.Utilities;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Security;
using Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.Deployment;
using Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.Extensions;
using Microsoft.VisualStudio.TestPlatform.ObjectModel;
internal class DeploymentUtility : DeploymentUtilityBase
{
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Security;
using Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.Deployment;
using Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.Extensions;
using Microsoft.VisualStudio.TestPlatform.ObjectModel;
internal class DeploymentUtility : DeploymentUtilityBase
public DeploymentUtility()
: base()
{
public DeploymentUtility()
: base()
{
}
}
public DeploymentUtility(DeploymentItemUtility deploymentItemUtility, AssemblyUtility assemblyUtility, FileUtility fileUtility)
: base(deploymentItemUtility, assemblyUtility, fileUtility)
{
}
public DeploymentUtility(DeploymentItemUtility deploymentItemUtility, AssemblyUtility assemblyUtility, FileUtility fileUtility)
: base(deploymentItemUtility, assemblyUtility, fileUtility)
{
}
public override void AddDeploymentItemsBasedOnMsTestSetting(string testSource, IList<DeploymentItem> deploymentItems, List<string> warnings)
public override void AddDeploymentItemsBasedOnMsTestSetting(string testSource, IList<DeploymentItem> deploymentItems, List<string> warnings)
{
if (MSTestSettingsProvider.Settings.DeployTestSourceDependencies)
{
if (MSTestSettingsProvider.Settings.DeployTestSourceDependencies)
EqtTrace.Info("Adding the references and satellite assemblies to the deployment items list");
// Get the referenced assemblies.
this.ProcessNewStorage(testSource, deploymentItems, warnings);
// Get the satellite assemblies
var satelliteItems = this.GetSatellites(deploymentItems, testSource, warnings);
foreach (var satelliteItem in satelliteItems)
{
EqtTrace.Info("Adding the references and satellite assemblies to the deployment items list");
// Get the referenced assemblies.
this.ProcessNewStorage(testSource, deploymentItems, warnings);
// Get the satellite assemblies
var satelliteItems = this.GetSatellites(deploymentItems, testSource, warnings);
foreach (var satelliteItem in satelliteItems)
{
this.DeploymentItemUtility.AddDeploymentItem(deploymentItems, satelliteItem);
}
}
else
{
EqtTrace.Info("Adding the test source directory to the deployment items list");
this.DeploymentItemUtility.AddDeploymentItem(deploymentItems, new DeploymentItem(Path.GetDirectoryName(testSource)));
this.DeploymentItemUtility.AddDeploymentItem(deploymentItems, satelliteItem);
}
}
/// <summary>
/// Get root deployment directory
/// </summary>
/// <param name="baseDirectory">The base directory.</param>
/// <returns>Root deployment directory.</returns>
public override string GetRootDeploymentDirectory(string baseDirectory)
else
{
string dateTimeSufix = DateTime.Now.ToString("yyyyMMddTHHmmss", DateTimeFormatInfo.InvariantInfo);
string directoryName = string.Format(CultureInfo.InvariantCulture, Resource.TestRunName, DeploymentFolderPrefix, Environment.UserName, dateTimeSufix);
directoryName = this.FileUtility.ReplaceInvalidFileNameCharacters(directoryName);
EqtTrace.Info("Adding the test source directory to the deployment items list");
this.DeploymentItemUtility.AddDeploymentItem(deploymentItems, new DeploymentItem(Path.GetDirectoryName(testSource)));
}
}
return this.FileUtility.GetNextIterationDirectoryName(baseDirectory, directoryName);
/// <summary>
/// Get root deployment directory
/// </summary>
/// <param name="baseDirectory">The base directory.</param>
/// <returns>Root deployment directory.</returns>
public override string GetRootDeploymentDirectory(string baseDirectory)
{
string dateTimeSufix = DateTime.Now.ToString("yyyyMMddTHHmmss", DateTimeFormatInfo.InvariantInfo);
string directoryName = string.Format(CultureInfo.InvariantCulture, Resource.TestRunName, DeploymentFolderPrefix, Environment.UserName, dateTimeSufix);
directoryName = this.FileUtility.ReplaceInvalidFileNameCharacters(directoryName);
return this.FileUtility.GetNextIterationDirectoryName(baseDirectory, directoryName);
}
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Justification = "Requirement is to handle all kinds of user exceptions and message appropriately.")]
protected void ProcessNewStorage(string testSource, IList<DeploymentItem> deploymentItems, IList<string> warnings)
{
// Add deployment items and process .config files only for storages we have not processed before.
if (!this.DeploymentItemUtility.IsValidDeploymentItem(testSource, string.Empty, out var errorMessage))
{
warnings.Add(errorMessage);
return;
}
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Justification = "Requirement is to handle all kinds of user exceptions and message appropriately.")]
protected void ProcessNewStorage(string testSource, IList<DeploymentItem> deploymentItems, IList<string> warnings)
this.DeploymentItemUtility.AddDeploymentItem(deploymentItems, new DeploymentItem(testSource, string.Empty, DeploymentItemOriginType.TestStorage));
// Deploy .config file if exists, only for assemblies, i.e. DLL and EXE.
// First check <TestStorage>.config, then if not found check for App.Config
// and deploy AppConfig to <TestStorage>.config.
if (this.AssemblyUtility.IsAssemblyExtension(Path.GetExtension(testSource)))
{
// Add deployment items and process .config files only for storages we have not processed before.
if (!this.DeploymentItemUtility.IsValidDeploymentItem(testSource, string.Empty, out var errorMessage))
var configFile = this.AddTestSourceConfigFileIfExists(testSource, deploymentItems);
// Deal with test dependencies: update dependencyDeploymentItems and missingDependentAssemblies.
try
{
warnings.Add(errorMessage);
return;
// We look for dependent assemblies only for DLL and EXE's.
this.AddDependencies(testSource, configFile, deploymentItems, warnings);
}
this.DeploymentItemUtility.AddDeploymentItem(deploymentItems, new DeploymentItem(testSource, string.Empty, DeploymentItemOriginType.TestStorage));
// Deploy .config file if exists, only for assemblies, i.e. DLL and EXE.
// First check <TestStorage>.config, then if not found check for App.Config
// and deploy AppConfig to <TestStorage>.config.
if (this.AssemblyUtility.IsAssemblyExtension(Path.GetExtension(testSource)))
catch (Exception e)
{
var configFile = this.AddTestSourceConfigFileIfExists(testSource, deploymentItems);
// Deal with test dependencies: update dependencyDeploymentItems and missingDependentAssemblies.
try
{
// We look for dependent assemblies only for DLL and EXE's.
this.AddDependencies(testSource, configFile, deploymentItems, warnings);
}
catch (Exception e)
{
string warning = string.Format(CultureInfo.CurrentCulture, Resource.DeploymentErrorFailedToDeployDependencies, testSource, e);
warnings.Add(warning);
}
string warning = string.Format(CultureInfo.CurrentCulture, Resource.DeploymentErrorFailedToDeployDependencies, testSource, e);
warnings.Add(warning);
}
}
}
protected override void AddDependenciesOfDeploymentItem(string deploymentItemFile, IList<string> filesToDeploy, IList<string> warnings)
protected override void AddDependenciesOfDeploymentItem(string deploymentItemFile, IList<string> filesToDeploy, IList<string> warnings)
{
var dependencies = new List<DeploymentItem>();
this.AddDependencies(deploymentItemFile, null, dependencies, warnings);
foreach (var dependencyItem in dependencies)
{
var dependencies = new List<DeploymentItem>();
Debug.Assert(Path.IsPathRooted(dependencyItem.SourcePath), "Path of the dependency " + dependencyItem.SourcePath + " is not rooted.");
this.AddDependencies(deploymentItemFile, null, dependencies, warnings);
foreach (var dependencyItem in dependencies)
{
Debug.Assert(Path.IsPathRooted(dependencyItem.SourcePath), "Path of the dependency " + dependencyItem.SourcePath + " is not rooted.");
// Add dependencies to filesToDeploy.
filesToDeploy.Add(dependencyItem.SourcePath);
}
// Add dependencies to filesToDeploy.
filesToDeploy.Add(dependencyItem.SourcePath);
}
}
protected IEnumerable<DeploymentItem> GetSatellites(IEnumerable<DeploymentItem> deploymentItems, string testSource, IList<string> warnings)
protected IEnumerable<DeploymentItem> GetSatellites(IEnumerable<DeploymentItem> deploymentItems, string testSource, IList<string> warnings)
{
List<DeploymentItem> satellites = new();
foreach (DeploymentItem item in deploymentItems)
{
List<DeploymentItem> satellites = new();
foreach (DeploymentItem item in deploymentItems)
// We do not care about deployment items which are directories because in that case we deploy all files underneath anyway.
string path = null;
try
{
// We do not care about deployment items which are directories because in that case we deploy all files underneath anyway.
string path = null;
try
{
path = this.GetFullPathToDeploymentItemSource(item.SourcePath, testSource);
path = Path.GetFullPath(path);
path = this.GetFullPathToDeploymentItemSource(item.SourcePath, testSource);
path = Path.GetFullPath(path);
if (string.IsNullOrEmpty(path) || !this.AssemblyUtility.IsAssemblyExtension(Path.GetExtension(path))
|| !this.FileUtility.DoesFileExist(path) || !this.AssemblyUtility.IsAssembly(path))
{
continue;
}
}
catch (ArgumentException ex)
if (string.IsNullOrEmpty(path) || !this.AssemblyUtility.IsAssemblyExtension(Path.GetExtension(path))
|| !this.FileUtility.DoesFileExist(path) || !this.AssemblyUtility.IsAssembly(path))
{
EqtTrace.WarningIf(EqtTrace.IsWarningEnabled, "DeploymentManager.GetSatellites: {0}", ex);
}
catch (SecurityException ex)
{
EqtTrace.WarningIf(EqtTrace.IsWarningEnabled, "DeploymentManager.GetSatellites: {0}", ex);
}
catch (IOException ex)
{
// This covers PathTooLongException.
EqtTrace.WarningIf(EqtTrace.IsWarningEnabled, "DeploymentManager.GetSatellites: {0}", ex);
}
catch (NotSupportedException ex)
{
EqtTrace.WarningIf(EqtTrace.IsWarningEnabled, "DeploymentManager.GetSatellites: {0}", ex);
continue;
}
}
catch (ArgumentException ex)
{
EqtTrace.WarningIf(EqtTrace.IsWarningEnabled, "DeploymentManager.GetSatellites: {0}", ex);
}
catch (SecurityException ex)
{
EqtTrace.WarningIf(EqtTrace.IsWarningEnabled, "DeploymentManager.GetSatellites: {0}", ex);
}
catch (IOException ex)
{
// This covers PathTooLongException.
EqtTrace.WarningIf(EqtTrace.IsWarningEnabled, "DeploymentManager.GetSatellites: {0}", ex);
}
catch (NotSupportedException ex)
{
EqtTrace.WarningIf(EqtTrace.IsWarningEnabled, "DeploymentManager.GetSatellites: {0}", ex);
}
// Note: now Path operations with itemPath should not result in any exceptions.
// path is already canonicalized.
// Note: now Path operations with itemPath should not result in any exceptions.
// path is already canonicalized.
// If we cannot access satellite due to security, etc, we report warning.
try
// If we cannot access satellite due to security, etc, we report warning.
try
{
string itemDir = Path.GetDirectoryName(path).TrimEnd(
new char[] { Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar });
List<string> itemSatellites = this.AssemblyUtility.GetSatelliteAssemblies(path);
foreach (string satellite in itemSatellites)
{
string itemDir = Path.GetDirectoryName(path).TrimEnd(
Debug.Assert(!string.IsNullOrEmpty(satellite), "DeploymentManager.DoDeployment: got empty satellite!");
Debug.Assert(
satellite.IndexOf(itemDir, StringComparison.OrdinalIgnoreCase) == 0,
"DeploymentManager.DoDeployment: Got satellite that does not start with original item path");
string satelliteDir = Path.GetDirectoryName(satellite).TrimEnd(
new char[] { Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar });
List<string> itemSatellites = this.AssemblyUtility.GetSatelliteAssemblies(path);
foreach (string satellite in itemSatellites)
{
Debug.Assert(!string.IsNullOrEmpty(satellite), "DeploymentManager.DoDeployment: got empty satellite!");
Debug.Assert(
satellite.IndexOf(itemDir, StringComparison.OrdinalIgnoreCase) == 0,
"DeploymentManager.DoDeployment: Got satellite that does not start with original item path");
string satelliteDir = Path.GetDirectoryName(satellite).TrimEnd(
new char[] { Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar });
Debug.Assert(!string.IsNullOrEmpty(satelliteDir), "DeploymentManager.DoDeployment: got empty satellite dir!");
Debug.Assert(satelliteDir.Length > itemDir.Length + 1, "DeploymentManager.DoDeployment: wrong satellite dir length!");
Debug.Assert(!string.IsNullOrEmpty(satelliteDir), "DeploymentManager.DoDeployment: got empty satellite dir!");
Debug.Assert(satelliteDir.Length > itemDir.Length + 1, "DeploymentManager.DoDeployment: wrong satellite dir length!");
string localeDir = satelliteDir.Substring(itemDir.Length + 1);
Debug.Assert(!string.IsNullOrEmpty(localeDir), "DeploymentManager.DoDeployment: got empty dir name for satellite dir!");
string localeDir = satelliteDir.Substring(itemDir.Length + 1);
Debug.Assert(!string.IsNullOrEmpty(localeDir), "DeploymentManager.DoDeployment: got empty dir name for satellite dir!");
string relativeOutputDir = Path.Combine(item.RelativeOutputDirectory, localeDir);
string relativeOutputDir = Path.Combine(item.RelativeOutputDirectory, localeDir);
// Now finally add the item!
DeploymentItem satelliteItem = new(satellite, relativeOutputDir, DeploymentItemOriginType.Satellite);
this.DeploymentItemUtility.AddDeploymentItem(satellites, satelliteItem);
}
}
catch (ArgumentException ex)
{
EqtTrace.WarningIf(EqtTrace.IsWarningEnabled, "DeploymentManager.GetSatellites: {0}", ex);
string warning = string.Format(CultureInfo.CurrentCulture, Resource.DeploymentErrorGettingSatellite, item, ex.GetType(), ex.GetExceptionMessage());
warnings.Add(warning);
}
catch (SecurityException ex)
{
EqtTrace.WarningIf(EqtTrace.IsWarningEnabled, "DeploymentManager.GetSatellites: {0}", ex);
string warning = string.Format(CultureInfo.CurrentCulture, Resource.DeploymentErrorGettingSatellite, item, ex.GetType(), ex.GetExceptionMessage());
warnings.Add(warning);
}
catch (IOException ex)
{
// This covers PathTooLongException.
EqtTrace.WarningIf(EqtTrace.IsWarningEnabled, "DeploymentManager.GetSatellites: {0}", ex);
string warning = string.Format(CultureInfo.CurrentCulture, Resource.DeploymentErrorGettingSatellite, item, ex.GetType(), ex.GetExceptionMessage());
warnings.Add(warning);
// Now finally add the item!
DeploymentItem satelliteItem = new(satellite, relativeOutputDir, DeploymentItemOriginType.Satellite);
this.DeploymentItemUtility.AddDeploymentItem(satellites, satelliteItem);
}
}
return satellites;
catch (ArgumentException ex)
{
EqtTrace.WarningIf(EqtTrace.IsWarningEnabled, "DeploymentManager.GetSatellites: {0}", ex);
string warning = string.Format(CultureInfo.CurrentCulture, Resource.DeploymentErrorGettingSatellite, item, ex.GetType(), ex.GetExceptionMessage());
warnings.Add(warning);
}
catch (SecurityException ex)
{
EqtTrace.WarningIf(EqtTrace.IsWarningEnabled, "DeploymentManager.GetSatellites: {0}", ex);
string warning = string.Format(CultureInfo.CurrentCulture, Resource.DeploymentErrorGettingSatellite, item, ex.GetType(), ex.GetExceptionMessage());
warnings.Add(warning);
}
catch (IOException ex)
{
// This covers PathTooLongException.
EqtTrace.WarningIf(EqtTrace.IsWarningEnabled, "DeploymentManager.GetSatellites: {0}", ex);
string warning = string.Format(CultureInfo.CurrentCulture, Resource.DeploymentErrorGettingSatellite, item, ex.GetType(), ex.GetExceptionMessage());
warnings.Add(warning);
}
}
/// <summary>
/// Process test storage and add dependent assemblies to dependencyDeploymentItems.
/// </summary>
/// <param name="testSource">The test source.</param>
/// <param name="configFile">The config file.</param>
/// <param name="deploymentItems">Deployment items.</param>
/// <param name="warnings">Warnings.</param>
private void AddDependencies(string testSource, string configFile, IList<DeploymentItem> deploymentItems, IList<string> warnings)
return satellites;
}
/// <summary>
/// Process test storage and add dependent assemblies to dependencyDeploymentItems.
/// </summary>
/// <param name="testSource">The test source.</param>
/// <param name="configFile">The config file.</param>
/// <param name="deploymentItems">Deployment items.</param>
/// <param name="warnings">Warnings.</param>
private void AddDependencies(string testSource, string configFile, IList<DeploymentItem> deploymentItems, IList<string> warnings)
{
Debug.Assert(!string.IsNullOrEmpty(testSource), "testSource should not be null or empty.");
// config file can be null.
Debug.Assert(deploymentItems != null, "deploymentItems should not be null.");
Debug.Assert(Path.IsPathRooted(testSource), "path should be rooted.");
var sw = Stopwatch.StartNew();
// Note: if this is not an assembly we simply return empty array, also:
// we do recursive search and report missing.
string[] references = this.AssemblyUtility.GetFullPathToDependentAssemblies(testSource, configFile, out var warningList);
if (warningList != null && warningList.Count > 0)
{
Debug.Assert(!string.IsNullOrEmpty(testSource), "testSource should not be null or empty.");
warnings = warnings.Concat(warningList).ToList();
}
// config file can be null.
Debug.Assert(deploymentItems != null, "deploymentItems should not be null.");
Debug.Assert(Path.IsPathRooted(testSource), "path should be rooted.");
if (EqtTrace.IsInfoEnabled)
{
EqtTrace.Info("DeploymentManager: Source:{0} has following references", testSource);
EqtTrace.Info("DeploymentManager: Resolving dependencies took {0} ms", sw.ElapsedMilliseconds);
}
var sw = Stopwatch.StartNew();
// Note: if this is not an assembly we simply return empty array, also:
// we do recursive search and report missing.
string[] references = this.AssemblyUtility.GetFullPathToDependentAssemblies(testSource, configFile, out var warningList);
if (warningList != null && warningList.Count > 0)
{
warnings = warnings.Concat(warningList).ToList();
}
foreach (string reference in references)
{
DeploymentItem deploymentItem = new(reference, string.Empty, DeploymentItemOriginType.Dependency);
this.DeploymentItemUtility.AddDeploymentItem(deploymentItems, deploymentItem);
if (EqtTrace.IsInfoEnabled)
{
EqtTrace.Info("DeploymentManager: Source:{0} has following references", testSource);
EqtTrace.Info("DeploymentManager: Resolving dependencies took {0} ms", sw.ElapsedMilliseconds);
}
foreach (string reference in references)
{
DeploymentItem deploymentItem = new(reference, string.Empty, DeploymentItemOriginType.Dependency);
this.DeploymentItemUtility.AddDeploymentItem(deploymentItems, deploymentItem);
if (EqtTrace.IsInfoEnabled)
{
EqtTrace.Info("DeploymentManager: Reference:{0} ", reference);
}
EqtTrace.Info("DeploymentManager: Reference:{0} ", reference);
}
}
}

Просмотреть файл

@ -1,310 +1,309 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
namespace Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.Utilities
namespace Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.Utilities;
using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
/// <summary>
/// Utility for reflection API's
/// </summary>
internal class ReflectionUtility
{
using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
/// <summary>
/// Gets the custom attributes of the provided type on a memberInfo
/// </summary>
/// <param name="attributeProvider"> The member to reflect on. </param>
/// <param name="type"> The attribute type. </param>
/// <returns> The vale of the custom attribute. </returns>
internal virtual object[] GetCustomAttributes(MemberInfo attributeProvider, Type type)
{
return this.GetCustomAttributes(attributeProvider, type, true);
}
/// <summary>
/// Utility for reflection API's
/// Gets all the custom attributes adorned on a member.
/// </summary>
internal class ReflectionUtility
/// <param name="memberInfo"> The member. </param>
/// <param name="inherit"> True to inspect the ancestors of element; otherwise, false. </param>
/// <returns> The list of attributes on the member. Empty list if none found. </returns>
internal object[] GetCustomAttributes(MemberInfo memberInfo, bool inherit)
{
/// <summary>
/// Gets the custom attributes of the provided type on a memberInfo
/// </summary>
/// <param name="attributeProvider"> The member to reflect on. </param>
/// <param name="type"> The attribute type. </param>
/// <returns> The vale of the custom attribute. </returns>
internal virtual object[] GetCustomAttributes(MemberInfo attributeProvider, Type type)
return this.GetCustomAttributes(memberInfo, type: null, inherit: inherit);
}
/// <summary>
/// Get custom attributes on a member for both normal and reflection only load.
/// </summary>
/// <param name="memberInfo">Member for which attributes needs to be retrieved.</param>
/// <param name="type">Type of attribute to retrieve.</param>
/// <param name="inherit">If inherited type of attribute.</param>
/// <returns>All attributes of give type on member.</returns>
internal object[] GetCustomAttributes(MemberInfo memberInfo, Type type, bool inherit)
{
if (memberInfo == null)
{
return this.GetCustomAttributes(attributeProvider, type, true);
return null;
}
/// <summary>
/// Gets all the custom attributes adorned on a member.
/// </summary>
/// <param name="memberInfo"> The member. </param>
/// <param name="inherit"> True to inspect the ancestors of element; otherwise, false. </param>
/// <returns> The list of attributes on the member. Empty list if none found. </returns>
internal object[] GetCustomAttributes(MemberInfo memberInfo, bool inherit)
bool shouldGetAllAttributes = type == null;
if (!this.IsReflectionOnlyLoad(memberInfo))
{
return this.GetCustomAttributes(memberInfo, type: null, inherit: inherit);
}
/// <summary>
/// Get custom attributes on a member for both normal and reflection only load.
/// </summary>
/// <param name="memberInfo">Member for which attributes needs to be retrieved.</param>
/// <param name="type">Type of attribute to retrieve.</param>
/// <param name="inherit">If inherited type of attribute.</param>
/// <returns>All attributes of give type on member.</returns>
internal object[] GetCustomAttributes(MemberInfo memberInfo, Type type, bool inherit)
{
if (memberInfo == null)
if (shouldGetAllAttributes)
{
return null;
}
bool shouldGetAllAttributes = type == null;
if (!this.IsReflectionOnlyLoad(memberInfo))
{
if (shouldGetAllAttributes)
{
return memberInfo.GetCustomAttributes(inherit);
}
else
{
return memberInfo.GetCustomAttributes(type, inherit);
}
return memberInfo.GetCustomAttributes(inherit);
}
else
{
List<object> nonUniqueAttributes = new();
Dictionary<string, object> uniqueAttributes = new();
var inheritanceThreshold = 10;
var inheritanceLevel = 0;
if (inherit && memberInfo.MemberType == MemberTypes.TypeInfo)
{
// This code is based on the code for fetching CustomAttributes in System.Reflection.CustomAttribute(RuntimeType type, RuntimeType caType, bool inherit)
var tempTypeInfo = memberInfo as TypeInfo;
do
{
var attributes = CustomAttributeData.GetCustomAttributes(tempTypeInfo);
this.AddNewAttributes(
attributes,
shouldGetAllAttributes,
type,
uniqueAttributes,
nonUniqueAttributes);
tempTypeInfo = tempTypeInfo.BaseType?.GetTypeInfo();
inheritanceLevel++;
}
while (tempTypeInfo != null && tempTypeInfo != typeof(object).GetTypeInfo()
&& inheritanceLevel < inheritanceThreshold);
}
else if (inherit && memberInfo.MemberType == MemberTypes.Method)
{
// This code is based on the code for fetching CustomAttributes in System.Reflection.CustomAttribute(RuntimeMethodInfo method, RuntimeType caType, bool inherit).
var tempMethodInfo = memberInfo as MethodInfo;
do
{
var attributes = CustomAttributeData.GetCustomAttributes(tempMethodInfo);
this.AddNewAttributes(
attributes,
shouldGetAllAttributes,
type,
uniqueAttributes,
nonUniqueAttributes);
var baseDefinition = tempMethodInfo.GetBaseDefinition();
if (baseDefinition != null)
{
if (string.Equals(
string.Concat(tempMethodInfo.DeclaringType.FullName, tempMethodInfo.Name),
string.Concat(baseDefinition.DeclaringType.FullName, baseDefinition.Name)))
{
break;
}
}
tempMethodInfo = baseDefinition;
inheritanceLevel++;
}
while (tempMethodInfo != null && inheritanceLevel < inheritanceThreshold);
}
else
{
// Ideally we should not be reaching here. We only query for attributes on types/methods currently.
// Return the attributes that CustomAttributeData returns in this cases not considering inheritance.
var firstLevelAttributes =
CustomAttributeData.GetCustomAttributes(memberInfo);
this.AddNewAttributes(firstLevelAttributes, shouldGetAllAttributes, type, uniqueAttributes, nonUniqueAttributes);
}
nonUniqueAttributes.AddRange(uniqueAttributes.Values);
return nonUniqueAttributes.ToArray();
return memberInfo.GetCustomAttributes(type, inherit);
}
}
internal object[] GetCustomAttributes(Assembly assembly, Type type)
else
{
if (assembly.ReflectionOnly)
List<object> nonUniqueAttributes = new();
Dictionary<string, object> uniqueAttributes = new();
var inheritanceThreshold = 10;
var inheritanceLevel = 0;
if (inherit && memberInfo.MemberType == MemberTypes.TypeInfo)
{
List<CustomAttributeData> customAttributes = new();
customAttributes.AddRange(CustomAttributeData.GetCustomAttributes(assembly));
// This code is based on the code for fetching CustomAttributes in System.Reflection.CustomAttribute(RuntimeType type, RuntimeType caType, bool inherit)
var tempTypeInfo = memberInfo as TypeInfo;
List<object> attributesArray = new();
foreach (var attribute in customAttributes)
do
{
if (this.IsTypeInheriting(attribute.Constructor.DeclaringType, type)
|| attribute.Constructor.DeclaringType.AssemblyQualifiedName.Equals(
type.AssemblyQualifiedName))
var attributes = CustomAttributeData.GetCustomAttributes(tempTypeInfo);
this.AddNewAttributes(
attributes,
shouldGetAllAttributes,
type,
uniqueAttributes,
nonUniqueAttributes);
tempTypeInfo = tempTypeInfo.BaseType?.GetTypeInfo();
inheritanceLevel++;
}
while (tempTypeInfo != null && tempTypeInfo != typeof(object).GetTypeInfo()
&& inheritanceLevel < inheritanceThreshold);
}
else if (inherit && memberInfo.MemberType == MemberTypes.Method)
{
// This code is based on the code for fetching CustomAttributes in System.Reflection.CustomAttribute(RuntimeMethodInfo method, RuntimeType caType, bool inherit).
var tempMethodInfo = memberInfo as MethodInfo;
do
{
var attributes = CustomAttributeData.GetCustomAttributes(tempMethodInfo);
this.AddNewAttributes(
attributes,
shouldGetAllAttributes,
type,
uniqueAttributes,
nonUniqueAttributes);
var baseDefinition = tempMethodInfo.GetBaseDefinition();
if (baseDefinition != null)
{
Attribute attributeInstance = CreateAttributeInstance(attribute);
if (attributeInstance != null)
if (string.Equals(
string.Concat(tempMethodInfo.DeclaringType.FullName, tempMethodInfo.Name),
string.Concat(baseDefinition.DeclaringType.FullName, baseDefinition.Name)))
{
attributesArray.Add(attributeInstance);
break;
}
}
}
return attributesArray.ToArray();
tempMethodInfo = baseDefinition;
inheritanceLevel++;
}
while (tempMethodInfo != null && inheritanceLevel < inheritanceThreshold);
}
else
{
return assembly.GetCustomAttributes(type).ToArray();
// Ideally we should not be reaching here. We only query for attributes on types/methods currently.
// Return the attributes that CustomAttributeData returns in this cases not considering inheritance.
var firstLevelAttributes =
CustomAttributeData.GetCustomAttributes(memberInfo);
this.AddNewAttributes(firstLevelAttributes, shouldGetAllAttributes, type, uniqueAttributes, nonUniqueAttributes);
}
nonUniqueAttributes.AddRange(uniqueAttributes.Values);
return nonUniqueAttributes.ToArray();
}
}
/// <summary>
/// Create instance of the attribute for reflection only load.
/// </summary>
/// <param name="attributeData">The attribute data.</param>
/// <returns>An attribute.</returns>
private static Attribute CreateAttributeInstance(CustomAttributeData attributeData)
internal object[] GetCustomAttributes(Assembly assembly, Type type)
{
if (assembly.ReflectionOnly)
{
object attribute = null;
try
List<CustomAttributeData> customAttributes = new();
customAttributes.AddRange(CustomAttributeData.GetCustomAttributes(assembly));
List<object> attributesArray = new();
foreach (var attribute in customAttributes)
{
// Create instance of attribute. For some case, constructor param is returned as ReadOnlyCollection
// instead of array. So convert it to array else constructor invoke will fail.
Type attributeType = Type.GetType(attributeData.Constructor.DeclaringType.AssemblyQualifiedName);
List<Type> constructorParameters = new();
List<object> constructorArguments = new();
foreach (var parameter in attributeData.ConstructorArguments)
if (this.IsTypeInheriting(attribute.Constructor.DeclaringType, type)
|| attribute.Constructor.DeclaringType.AssemblyQualifiedName.Equals(
type.AssemblyQualifiedName))
{
Type parameterType = Type.GetType(parameter.ArgumentType.AssemblyQualifiedName);
constructorParameters.Add(parameterType);
if (parameterType.IsArray)
Attribute attributeInstance = CreateAttributeInstance(attribute);
if (attributeInstance != null)
{
if (parameter.Value is IEnumerable enumerable)
{
ArrayList list = new();
foreach (var item in enumerable)
{
if (item is CustomAttributeTypedArgument argument)
{
list.Add(argument.Value);
}
else
{
list.Add(item);
}
}
attributesArray.Add(attributeInstance);
}
}
}
constructorArguments.Add(list.ToArray(parameterType.GetElementType()));
}
else
return attributesArray.ToArray();
}
else
{
return assembly.GetCustomAttributes(type).ToArray();
}
}
/// <summary>
/// Create instance of the attribute for reflection only load.
/// </summary>
/// <param name="attributeData">The attribute data.</param>
/// <returns>An attribute.</returns>
private static Attribute CreateAttributeInstance(CustomAttributeData attributeData)
{
object attribute = null;
try
{
// Create instance of attribute. For some case, constructor param is returned as ReadOnlyCollection
// instead of array. So convert it to array else constructor invoke will fail.
Type attributeType = Type.GetType(attributeData.Constructor.DeclaringType.AssemblyQualifiedName);
List<Type> constructorParameters = new();
List<object> constructorArguments = new();
foreach (var parameter in attributeData.ConstructorArguments)
{
Type parameterType = Type.GetType(parameter.ArgumentType.AssemblyQualifiedName);
constructorParameters.Add(parameterType);
if (parameterType.IsArray)
{
if (parameter.Value is IEnumerable enumerable)
{
ArrayList list = new();
foreach (var item in enumerable)
{
constructorArguments.Add(parameter.Value);
if (item is CustomAttributeTypedArgument argument)
{
list.Add(argument.Value);
}
else
{
list.Add(item);
}
}
constructorArguments.Add(list.ToArray(parameterType.GetElementType()));
}
else
{
constructorArguments.Add(parameter.Value);
}
}
ConstructorInfo constructor = attributeType.GetConstructor(constructorParameters.ToArray());
attribute = constructor.Invoke(constructorArguments.ToArray());
foreach (var namedArgument in attributeData.NamedArguments)
else
{
attributeType.GetProperty(namedArgument.MemberInfo.Name).SetValue(attribute, namedArgument.TypedValue.Value, null);
constructorArguments.Add(parameter.Value);
}
}
// If not able to create instance of attribute ignore attribute. (May happen for custom user defined attributes).
catch (BadImageFormatException)
{
}
catch (FileLoadException)
{
}
catch (TypeLoadException)
{
}
ConstructorInfo constructor = attributeType.GetConstructor(constructorParameters.ToArray());
attribute = constructor.Invoke(constructorArguments.ToArray());
return attribute as Attribute;
foreach (var namedArgument in attributeData.NamedArguments)
{
attributeType.GetProperty(namedArgument.MemberInfo.Name).SetValue(attribute, namedArgument.TypedValue.Value, null);
}
}
private void AddNewAttributes(
IList<CustomAttributeData> customAttributes,
bool shouldGetAllAttributes,
Type type,
Dictionary<string, object> uniqueAttributes,
List<object> nonUniqueAttributes)
// If not able to create instance of attribute ignore attribute. (May happen for custom user defined attributes).
catch (BadImageFormatException)
{
foreach (var attribute in customAttributes)
}
catch (FileLoadException)
{
}
catch (TypeLoadException)
{
}
return attribute as Attribute;
}
private void AddNewAttributes(
IList<CustomAttributeData> customAttributes,
bool shouldGetAllAttributes,
Type type,
Dictionary<string, object> uniqueAttributes,
List<object> nonUniqueAttributes)
{
foreach (var attribute in customAttributes)
{
if (shouldGetAllAttributes
|| (this.IsTypeInheriting(attribute.Constructor.DeclaringType, type)
|| attribute.Constructor.DeclaringType.AssemblyQualifiedName.Equals(
type.AssemblyQualifiedName)))
{
if (shouldGetAllAttributes
|| (this.IsTypeInheriting(attribute.Constructor.DeclaringType, type)
|| attribute.Constructor.DeclaringType.AssemblyQualifiedName.Equals(
type.AssemblyQualifiedName)))
Attribute attributeInstance = CreateAttributeInstance(attribute);
if (attributeInstance != null)
{
Attribute attributeInstance = CreateAttributeInstance(attribute);
if (attributeInstance != null)
if (this.GetCustomAttributes(
attributeInstance.GetType().GetTypeInfo(),
typeof(AttributeUsageAttribute),
true).FirstOrDefault() is AttributeUsageAttribute attributeUsageAttribute && !attributeUsageAttribute.AllowMultiple)
{
if (this.GetCustomAttributes(
attributeInstance.GetType().GetTypeInfo(),
typeof(AttributeUsageAttribute),
true).FirstOrDefault() is AttributeUsageAttribute attributeUsageAttribute && !attributeUsageAttribute.AllowMultiple)
if (!uniqueAttributes.ContainsKey(attributeInstance.GetType().FullName))
{
if (!uniqueAttributes.ContainsKey(attributeInstance.GetType().FullName))
{
uniqueAttributes.Add(attributeInstance.GetType().FullName, attributeInstance);
}
}
else
{
nonUniqueAttributes.Add(attributeInstance);
uniqueAttributes.Add(attributeInstance.GetType().FullName, attributeInstance);
}
}
else
{
nonUniqueAttributes.Add(attributeInstance);
}
}
}
}
}
/// <summary>
/// Check whether the member is loaded in a reflection only context.
/// </summary>
/// <param name="memberInfo"> The member Info. </param>
/// <returns> True if the member is loaded in a reflection only context. </returns>
private bool IsReflectionOnlyLoad(MemberInfo memberInfo)
/// <summary>
/// Check whether the member is loaded in a reflection only context.
/// </summary>
/// <param name="memberInfo"> The member Info. </param>
/// <returns> True if the member is loaded in a reflection only context. </returns>
private bool IsReflectionOnlyLoad(MemberInfo memberInfo)
{
if (memberInfo != null)
{
if (memberInfo != null)
{
return memberInfo.Module.Assembly.ReflectionOnly;
}
return false;
return memberInfo.Module.Assembly.ReflectionOnly;
}
private bool IsTypeInheriting(Type type1, Type type2)
{
while (type1 != null)
{
if (type1.AssemblyQualifiedName.Equals(type2.AssemblyQualifiedName))
{
return true;
}
return false;
}
type1 = type1.GetTypeInfo().BaseType;
private bool IsTypeInheriting(Type type1, Type type2)
{
while (type1 != null)
{
if (type1.AssemblyQualifiedName.Equals(type2.AssemblyQualifiedName))
{
return true;
}
return false;
type1 = type1.GetTypeInfo().BaseType;
}
return false;
}
}

Просмотреть файл

@ -1,29 +1,28 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
namespace Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices
namespace Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices;
using System;
using System.Security.Policy;
/// <summary>
/// This interface is an abstraction over the AppDomain APIs
/// </summary>
internal interface IAppDomain
{
using System;
using System.Security.Policy;
/// <summary>
/// Unloads the specified application domain.
/// </summary>
/// <param name="appDomain">An application domain to unload.</param>
void Unload(AppDomain appDomain);
/// <summary>
/// This interface is an abstraction over the AppDomain APIs
/// Creates a new application domain using the specified name, evidence, and application domain setup information.
/// </summary>
internal interface IAppDomain
{
/// <summary>
/// Unloads the specified application domain.
/// </summary>
/// <param name="appDomain">An application domain to unload.</param>
void Unload(AppDomain appDomain);
/// <summary>
/// Creates a new application domain using the specified name, evidence, and application domain setup information.
/// </summary>
/// <param name="friendlyName">The friendly name of the domain.</param>
/// <param name="securityInfo">Evidence that establishes the identity of the code that runs in the application domain. Pass null to use the evidence of the current application domain.</param>
/// <param name="info">An object that contains application domain initialization information.</param>
/// <returns>The newly created application domain.</returns>
AppDomain CreateDomain(string friendlyName, Evidence securityInfo, AppDomainSetup info);
}
/// <param name="friendlyName">The friendly name of the domain.</param>
/// <param name="securityInfo">Evidence that establishes the identity of the code that runs in the application domain. Pass null to use the evidence of the current application domain.</param>
/// <param name="info">An object that contains application domain initialization information.</param>
/// <returns>The newly created application domain.</returns>
AppDomain CreateDomain(string friendlyName, Evidence securityInfo, AppDomainSetup info);
}

Просмотреть файл

@ -1,24 +1,23 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
namespace Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.Utilities
namespace Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.Utilities;
using System.Reflection;
internal interface IAssemblyUtility
{
using System.Reflection;
/// <summary>
/// Loads an assembly into the reflection-only context, given its path.
/// </summary>
/// <param name="assemblyPath">The path of the file that contains the manifest of the assembly.</param>
/// <returns>The loaded assembly.</returns>
Assembly ReflectionOnlyLoadFrom(string assemblyPath);
internal interface IAssemblyUtility
{
/// <summary>
/// Loads an assembly into the reflection-only context, given its path.
/// </summary>
/// <param name="assemblyPath">The path of the file that contains the manifest of the assembly.</param>
/// <returns>The loaded assembly.</returns>
Assembly ReflectionOnlyLoadFrom(string assemblyPath);
/// <summary>
/// Loads an assembly into the reflection-only context, given its display name.
/// </summary>
/// <param name="assemblyString">The display name of the assembly, as returned by the System.Reflection.AssemblyName.FullName property.</param>
/// <returns>The loaded assembly.</returns>
Assembly ReflectionOnlyLoad(string assemblyString);
}
/// <summary>
/// Loads an assembly into the reflection-only context, given its display name.
/// </summary>
/// <param name="assemblyString">The display name of the assembly, as returned by the System.Reflection.AssemblyName.FullName property.</param>
/// <returns>The loaded assembly.</returns>
Assembly ReflectionOnlyLoad(string assemblyString);
}

Просмотреть файл

@ -1,54 +1,53 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
namespace Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices
namespace Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices;
using System;
using System.Collections;
using System.Collections.Generic;
/// <summary>
/// Permutation of integers from 0 to (numberOfObjects - 1), in random order and in the end all values are returned.
/// Used to get random permutation for data row access in data driven test.
/// </summary>
internal class RandomIntPermutation : IEnumerable<int>
{
using System;
using System.Collections;
using System.Collections.Generic;
private int[] objects;
/// <summary>
/// Permutation of integers from 0 to (numberOfObjects - 1), in random order and in the end all values are returned.
/// Used to get random permutation for data row access in data driven test.
/// </summary>
internal class RandomIntPermutation : IEnumerable<int>
public RandomIntPermutation(int numberOfObjects)
{
private int[] objects;
public RandomIntPermutation(int numberOfObjects)
if (numberOfObjects < 0)
{
if (numberOfObjects < 0)
{
throw new ArgumentException(Resource.WrongNumberOfObjects, nameof(numberOfObjects));
}
this.objects = new int[numberOfObjects];
for (int i = 0; i < numberOfObjects; ++i)
{
this.objects[i] = i;
}
Random random = new();
for (int last = this.objects.Length - 1; last > 0; --last)
{
// Swap last and at random position which can be last in which case we don't swap.
int position = random.Next(last); // 0 .. last - 1
(this.objects[position], this.objects[last]) = (this.objects[last], this.objects[position]);
}
throw new ArgumentException(Resource.WrongNumberOfObjects, nameof(numberOfObjects));
}
public IEnumerator<int> GetEnumerator()
this.objects = new int[numberOfObjects];
for (int i = 0; i < numberOfObjects; ++i)
{
// Iterate over created permutation, do not change it.
for (int i = 0; i < this.objects.Length; ++i)
{
yield return this.objects[i];
}
this.objects[i] = i;
}
IEnumerator IEnumerable.GetEnumerator()
Random random = new();
for (int last = this.objects.Length - 1; last > 0; --last)
{
return this.GetEnumerator();
// Swap last and at random position which can be last in which case we don't swap.
int position = random.Next(last); // 0 .. last - 1
(this.objects[position], this.objects[last]) = (this.objects[last], this.objects[position]);
}
}
public IEnumerator<int> GetEnumerator()
{
// Iterate over created permutation, do not change it.
for (int i = 0; i < this.objects.Length; ++i)
{
yield return this.objects[i];
}
}
IEnumerator IEnumerable.GetEnumerator()
{
return this.GetEnumerator();
}
}

Просмотреть файл

@ -1,41 +1,40 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
namespace Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices
namespace Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices;
using System;
using System.Collections;
using System.Collections.Generic;
/// <summary>
/// Permutation of integers from 0 to (numberOfObjects - 1) returned by increment of 1.
/// Used to get sequential permutation for data row access in data driven test.
/// </summary>
internal class SequentialIntPermutation : IEnumerable<int>
{
using System;
using System.Collections;
using System.Collections.Generic;
private readonly int numberOfObjects;
/// <summary>
/// Permutation of integers from 0 to (numberOfObjects - 1) returned by increment of 1.
/// Used to get sequential permutation for data row access in data driven test.
/// </summary>
internal class SequentialIntPermutation : IEnumerable<int>
public SequentialIntPermutation(int numberOfObjects)
{
private readonly int numberOfObjects;
public SequentialIntPermutation(int numberOfObjects)
if (numberOfObjects < 0)
{
if (numberOfObjects < 0)
{
throw new ArgumentException(Resource.WrongNumberOfObjects, nameof(numberOfObjects));
}
this.numberOfObjects = numberOfObjects;
throw new ArgumentException(Resource.WrongNumberOfObjects, nameof(numberOfObjects));
}
public IEnumerator<int> GetEnumerator()
{
for (int i = 0; i < this.numberOfObjects; ++i)
{
yield return i;
}
}
this.numberOfObjects = numberOfObjects;
}
IEnumerator IEnumerable.GetEnumerator()
public IEnumerator<int> GetEnumerator()
{
for (int i = 0; i < this.numberOfObjects; ++i)
{
return this.GetEnumerator();
yield return i;
}
}
IEnumerator IEnumerable.GetEnumerator()
{
return this.GetEnumerator();
}
}

Просмотреть файл

@ -1,262 +1,261 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
namespace Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.Utilities
namespace Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.Utilities;
using System;
using System.Diagnostics;
using System.IO;
using System.Runtime.InteropServices;
using static System.String;
public static class VSInstallationUtilities
{
using System;
using System.Diagnostics;
using System.IO;
using System.Runtime.InteropServices;
using static System.String;
/// <summary>
/// Public assemblies directory name
/// </summary>
private const string PublicAssembliesDirectoryName = "PublicAssemblies";
public static class VSInstallationUtilities
/// <summary>
/// Folder name of private assemblies
/// </summary>
private const string PrivateAssembliesFolderName = "PrivateAssemblies";
/// <summary>
/// The manifest file name to determine if it is running in portable mode
/// </summary>
private const string PortableVsTestManifestFilename = "Portable.VsTest.Manifest";
private static string vsInstallPath = null;
private static bool vsInstallPathEvaluated = false;
/// <summary>
/// Gets the visual studio installation path on the local machine.
/// </summary>
/// <returns>VS install path</returns>
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Justification = "Need to ignore failures to read the registry settings")]
public static string VSInstallPath
{
/// <summary>
/// Public assemblies directory name
/// </summary>
private const string PublicAssembliesDirectoryName = "PublicAssemblies";
/// <summary>
/// Folder name of private assemblies
/// </summary>
private const string PrivateAssembliesFolderName = "PrivateAssemblies";
/// <summary>
/// The manifest file name to determine if it is running in portable mode
/// </summary>
private const string PortableVsTestManifestFilename = "Portable.VsTest.Manifest";
private static string vsInstallPath = null;
private static bool vsInstallPathEvaluated = false;
/// <summary>
/// Gets the visual studio installation path on the local machine.
/// </summary>
/// <returns>VS install path</returns>
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Justification = "Need to ignore failures to read the registry settings")]
public static string VSInstallPath
get
{
get
if (!vsInstallPathEvaluated)
{
if (!vsInstallPathEvaluated)
try
{
try
{
vsInstallPath = null;
vsInstallPath = null;
// Use the Setup API to find the installation folder for currently running VS instance.
if (new SetupConfiguration() is ISetupConfiguration setupConfiguration)
{
var currentConfiguration = setupConfiguration.GetInstanceForCurrentProcess();
var currentInstallationPath = currentConfiguration.GetInstallationPath();
vsInstallPath = Path.Combine(currentInstallationPath, @"Common7\IDE");
}
}
catch
// Use the Setup API to find the installation folder for currently running VS instance.
if (new SetupConfiguration() is ISetupConfiguration setupConfiguration)
{
// SetupConfiguration won't work if VS is not installed or VS is pre-vs2017 version.
// So ignore all exception from it.
}
finally
{
vsInstallPathEvaluated = true;
var currentConfiguration = setupConfiguration.GetInstanceForCurrentProcess();
var currentInstallationPath = currentConfiguration.GetInstallationPath();
vsInstallPath = Path.Combine(currentInstallationPath, @"Common7\IDE");
}
}
return vsInstallPath;
}
}
/// <summary>
/// Gets path to public assemblies.
///
/// Returns null if VS is not installed on this machine.
/// </summary>
public static string PathToPublicAssemblies => GetFullPath(PublicAssembliesDirectoryName);
/// <summary>
/// Gets path to private assemblies.
///
/// Returns null if VS is not installed on this machine.
/// </summary>
public static string PathToPrivateAssemblies => GetFullPath(PrivateAssembliesFolderName);
/// <summary>
/// Is Current process running in Portable Mode
/// </summary>
/// <returns>True, if portable mode; false, otherwise</returns>
public static bool IsCurrentProcessRunningInPortableMode()
{
return IsProcessRunningInPortableMode(Process.GetCurrentProcess().MainModule.FileName);
}
/// <summary>
/// Is the EXE specified running in Portable Mode
/// </summary>
/// <param name="exeName">EXE name.</param>
/// <returns>True, if portable mode; false, otherwise</returns>
public static bool IsProcessRunningInPortableMode(string exeName)
{
// Get the directory of the exe
var exeDir = Path.GetDirectoryName(exeName);
if (!string.IsNullOrEmpty(exeDir))
{
return File.Exists(Path.Combine(exeDir, PortableVsTestManifestFilename));
catch
{
// SetupConfiguration won't work if VS is not installed or VS is pre-vs2017 version.
// So ignore all exception from it.
}
finally
{
vsInstallPathEvaluated = true;
}
}
return false;
}
private static string GetFullPath(string folderName)
{
var vsInstallDir = VSInstallPath;
return IsNullOrWhiteSpace(vsInstallDir?.Trim()) ? null : Path.Combine(vsInstallDir, folderName);
}
/// <summary>
/// Information about an instance of a product.
/// </summary>
[Guid("B41463C3-8866-43B5-BC33-2B0676F7F42E")]
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
#pragma warning disable SA1201 // Elements must appear in the correct order
public interface ISetupInstance
#pragma warning restore SA1201 // Elements must appear in the correct order
{
/// <summary>
/// Gets the instance identifier (should match the name of the parent instance directory).
/// </summary>
/// <returns>The instance identifier.</returns>
[return: MarshalAs(UnmanagedType.BStr)]
string GetInstanceId();
/// <summary>
/// Gets the local date and time when the installation was originally installed.
/// </summary>
/// <returns>The local date and time when the installation was originally installed.</returns>
[return: MarshalAs(UnmanagedType.Struct)]
System.Runtime.InteropServices.ComTypes.FILETIME GetInstallDate();
/// <summary>
/// Gets the unique name of the installation, often indicating the branch and other information used for telemetry.
/// </summary>
/// <returns>The unique name of the installation, often indicating the branch and other information used for telemetry.</returns>
[return: MarshalAs(UnmanagedType.BStr)]
string GetInstallationName();
/// <summary>
/// Gets the path to the installation root of the product.
/// </summary>
/// <returns>The path to the installation root of the product.</returns>
[return: MarshalAs(UnmanagedType.BStr)]
string GetInstallationPath();
/// <summary>
/// Gets the version of the product installed in this instance.
/// </summary>
/// <returns>The version of the product installed in this instance.</returns>
[return: MarshalAs(UnmanagedType.BStr)]
string GetInstallationVersion();
/// <summary>
/// Gets the display name (title) of the product installed in this instance.
/// </summary>
/// <param name="lcid">The LCID for the display name.</param>
/// <returns>The display name (title) of the product installed in this instance.</returns>
[return: MarshalAs(UnmanagedType.BStr)]
string GetDisplayName([In, MarshalAs(UnmanagedType.U4)] int lcid);
/// <summary>
/// Gets the description of the product installed in this instance.
/// </summary>
/// <param name="lcid">The LCID for the description.</param>
/// <returns>The description of the product installed in this instance.</returns>
[return: MarshalAs(UnmanagedType.BStr)]
string GetDescription([In, MarshalAs(UnmanagedType.U4)] int lcid);
/// <summary>
/// Resolves the optional relative path to the root path of the instance.
/// </summary>
/// <param name="relativePath">A relative path within the instance to resolve, or NULL to get the root path.</param>
/// <returns>The full path to the optional relative path within the instance. If the relative path is NULL, the root path will always terminate in a backslash.</returns>
[return: MarshalAs(UnmanagedType.BStr)]
string ResolvePath([In, MarshalAs(UnmanagedType.LPWStr)] string relativePath);
}
/// <summary>
/// A enumerator of installed <see cref="ISetupInstance"/> objects.
/// </summary>
[Guid("6380BCFF-41D3-4B2E-8B2E-BF8A6810C848")]
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
public interface IEnumSetupInstances
{
/// <summary>
/// Retrieves the next set of product instances in the enumeration sequence.
/// </summary>
/// <param name="celt">The number of product instances to retrieve.</param>
/// <param name="rgelt">A pointer to an array of <see cref="ISetupInstance"/>.</param>
/// <param name="pceltFetched">A pointer to the number of product instances retrieved. If celt is 1 this parameter may be NULL.</param>
void Next(
[In] int celt,
[Out, MarshalAs(UnmanagedType.Interface)] out ISetupInstance rgelt,
[Out] out int pceltFetched);
/// <summary>
/// Skips the next set of product instances in the enumeration sequence.
/// </summary>
/// <param name="celt">The number of product instances to skip.</param>
void Skip([In, MarshalAs(UnmanagedType.U4)] int celt);
/// <summary>
/// Resets the enumeration sequence to the beginning.
/// </summary>
void Reset();
/// <summary>
/// Creates a new enumeration object in the same state as the current enumeration object: the new object points to the same place in the enumeration sequence.
/// </summary>
/// <returns>A pointer to a pointer to a new <see cref="IEnumSetupInstances"/> interface. If the method fails, this parameter is undefined.</returns>
[return: MarshalAs(UnmanagedType.Interface)]
IEnumSetupInstances Clone();
}
/// <summary>
/// Gets information about product instances set up on the machine.
/// </summary>
[Guid("42843719-DB4C-46C2-8E7C-64F1816EFD5B")]
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
public interface ISetupConfiguration
{
/// <summary>
/// Enumerates all product instances installed.
/// </summary>
/// <returns>An enumeration of installed product instances.</returns>
[return: MarshalAs(UnmanagedType.Interface)]
IEnumSetupInstances EnumInstances();
/// <summary>
/// Gets the instance for the current process path.
/// </summary>
/// <returns>The instance for the current process path.</returns>
[return: MarshalAs(UnmanagedType.Interface)]
ISetupInstance GetInstanceForCurrentProcess();
/// <summary>
/// Gets the instance for the given path.
/// </summary>
/// <param name="wzPath">Path used to determine instance</param>
/// <returns>The instance for the given path.</returns>
[return: MarshalAs(UnmanagedType.Interface)]
ISetupInstance GetInstanceForPath([In, MarshalAs(UnmanagedType.LPWStr)] string wzPath);
}
/// <summary>
/// CoClass that implements <see cref="ISetupConfiguration"/>.
/// </summary>
[ComImport]
[Guid("177F0C4A-1CD3-4DE7-A32C-71DBBB9FA36D")]
public class SetupConfiguration
{
return vsInstallPath;
}
}
/// <summary>
/// Gets path to public assemblies.
///
/// Returns null if VS is not installed on this machine.
/// </summary>
public static string PathToPublicAssemblies => GetFullPath(PublicAssembliesDirectoryName);
/// <summary>
/// Gets path to private assemblies.
///
/// Returns null if VS is not installed on this machine.
/// </summary>
public static string PathToPrivateAssemblies => GetFullPath(PrivateAssembliesFolderName);
/// <summary>
/// Is Current process running in Portable Mode
/// </summary>
/// <returns>True, if portable mode; false, otherwise</returns>
public static bool IsCurrentProcessRunningInPortableMode()
{
return IsProcessRunningInPortableMode(Process.GetCurrentProcess().MainModule.FileName);
}
/// <summary>
/// Is the EXE specified running in Portable Mode
/// </summary>
/// <param name="exeName">EXE name.</param>
/// <returns>True, if portable mode; false, otherwise</returns>
public static bool IsProcessRunningInPortableMode(string exeName)
{
// Get the directory of the exe
var exeDir = Path.GetDirectoryName(exeName);
if (!string.IsNullOrEmpty(exeDir))
{
return File.Exists(Path.Combine(exeDir, PortableVsTestManifestFilename));
}
return false;
}
private static string GetFullPath(string folderName)
{
var vsInstallDir = VSInstallPath;
return IsNullOrWhiteSpace(vsInstallDir?.Trim()) ? null : Path.Combine(vsInstallDir, folderName);
}
/// <summary>
/// Information about an instance of a product.
/// </summary>
[Guid("B41463C3-8866-43B5-BC33-2B0676F7F42E")]
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
#pragma warning disable SA1201 // Elements must appear in the correct order
public interface ISetupInstance
#pragma warning restore SA1201 // Elements must appear in the correct order
{
/// <summary>
/// Gets the instance identifier (should match the name of the parent instance directory).
/// </summary>
/// <returns>The instance identifier.</returns>
[return: MarshalAs(UnmanagedType.BStr)]
string GetInstanceId();
/// <summary>
/// Gets the local date and time when the installation was originally installed.
/// </summary>
/// <returns>The local date and time when the installation was originally installed.</returns>
[return: MarshalAs(UnmanagedType.Struct)]
System.Runtime.InteropServices.ComTypes.FILETIME GetInstallDate();
/// <summary>
/// Gets the unique name of the installation, often indicating the branch and other information used for telemetry.
/// </summary>
/// <returns>The unique name of the installation, often indicating the branch and other information used for telemetry.</returns>
[return: MarshalAs(UnmanagedType.BStr)]
string GetInstallationName();
/// <summary>
/// Gets the path to the installation root of the product.
/// </summary>
/// <returns>The path to the installation root of the product.</returns>
[return: MarshalAs(UnmanagedType.BStr)]
string GetInstallationPath();
/// <summary>
/// Gets the version of the product installed in this instance.
/// </summary>
/// <returns>The version of the product installed in this instance.</returns>
[return: MarshalAs(UnmanagedType.BStr)]
string GetInstallationVersion();
/// <summary>
/// Gets the display name (title) of the product installed in this instance.
/// </summary>
/// <param name="lcid">The LCID for the display name.</param>
/// <returns>The display name (title) of the product installed in this instance.</returns>
[return: MarshalAs(UnmanagedType.BStr)]
string GetDisplayName([In, MarshalAs(UnmanagedType.U4)] int lcid);
/// <summary>
/// Gets the description of the product installed in this instance.
/// </summary>
/// <param name="lcid">The LCID for the description.</param>
/// <returns>The description of the product installed in this instance.</returns>
[return: MarshalAs(UnmanagedType.BStr)]
string GetDescription([In, MarshalAs(UnmanagedType.U4)] int lcid);
/// <summary>
/// Resolves the optional relative path to the root path of the instance.
/// </summary>
/// <param name="relativePath">A relative path within the instance to resolve, or NULL to get the root path.</param>
/// <returns>The full path to the optional relative path within the instance. If the relative path is NULL, the root path will always terminate in a backslash.</returns>
[return: MarshalAs(UnmanagedType.BStr)]
string ResolvePath([In, MarshalAs(UnmanagedType.LPWStr)] string relativePath);
}
/// <summary>
/// A enumerator of installed <see cref="ISetupInstance"/> objects.
/// </summary>
[Guid("6380BCFF-41D3-4B2E-8B2E-BF8A6810C848")]
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
public interface IEnumSetupInstances
{
/// <summary>
/// Retrieves the next set of product instances in the enumeration sequence.
/// </summary>
/// <param name="celt">The number of product instances to retrieve.</param>
/// <param name="rgelt">A pointer to an array of <see cref="ISetupInstance"/>.</param>
/// <param name="pceltFetched">A pointer to the number of product instances retrieved. If celt is 1 this parameter may be NULL.</param>
void Next(
[In] int celt,
[Out, MarshalAs(UnmanagedType.Interface)] out ISetupInstance rgelt,
[Out] out int pceltFetched);
/// <summary>
/// Skips the next set of product instances in the enumeration sequence.
/// </summary>
/// <param name="celt">The number of product instances to skip.</param>
void Skip([In, MarshalAs(UnmanagedType.U4)] int celt);
/// <summary>
/// Resets the enumeration sequence to the beginning.
/// </summary>
void Reset();
/// <summary>
/// Creates a new enumeration object in the same state as the current enumeration object: the new object points to the same place in the enumeration sequence.
/// </summary>
/// <returns>A pointer to a pointer to a new <see cref="IEnumSetupInstances"/> interface. If the method fails, this parameter is undefined.</returns>
[return: MarshalAs(UnmanagedType.Interface)]
IEnumSetupInstances Clone();
}
/// <summary>
/// Gets information about product instances set up on the machine.
/// </summary>
[Guid("42843719-DB4C-46C2-8E7C-64F1816EFD5B")]
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
public interface ISetupConfiguration
{
/// <summary>
/// Enumerates all product instances installed.
/// </summary>
/// <returns>An enumeration of installed product instances.</returns>
[return: MarshalAs(UnmanagedType.Interface)]
IEnumSetupInstances EnumInstances();
/// <summary>
/// Gets the instance for the current process path.
/// </summary>
/// <returns>The instance for the current process path.</returns>
[return: MarshalAs(UnmanagedType.Interface)]
ISetupInstance GetInstanceForCurrentProcess();
/// <summary>
/// Gets the instance for the given path.
/// </summary>
/// <param name="wzPath">Path used to determine instance</param>
/// <returns>The instance for the given path.</returns>
[return: MarshalAs(UnmanagedType.Interface)]
ISetupInstance GetInstanceForPath([In, MarshalAs(UnmanagedType.LPWStr)] string wzPath);
}
/// <summary>
/// CoClass that implements <see cref="ISetupConfiguration"/>.
/// </summary>
[ComImport]
[Guid("177F0C4A-1CD3-4DE7-A32C-71DBBB9FA36D")]
public class SetupConfiguration
{
}
}

Просмотреть файл

@ -1,154 +1,153 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
namespace Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.Utilities
namespace Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.Utilities;
using System;
using System.Diagnostics;
using System.IO;
using System.Reflection;
using System.Text;
using System.Xml;
internal class XmlUtilities
{
using System;
using System.Diagnostics;
using System.IO;
using System.Reflection;
using System.Text;
using System.Xml;
private const string XmlNamespace = "urn:schemas-microsoft-com:asm.v1";
internal class XmlUtilities
/// <summary>
/// Adds assembly redirection and converts the resulting config file to a byte array.
/// </summary>
/// <param name="configFile"> The config File. </param>
/// <param name="assemblyName">The assembly name.</param>
/// <param name="oldVersion">The old version.</param>
/// <param name="newVersion">The new version.</param>
/// <returns> A byte array of the config file with the redirections added. </returns>
internal byte[] AddAssemblyRedirection(string configFile, AssemblyName assemblyName, string oldVersion, string newVersion)
{
private const string XmlNamespace = "urn:schemas-microsoft-com:asm.v1";
var doc = this.GetXmlDocument(configFile);
/// <summary>
/// Adds assembly redirection and converts the resulting config file to a byte array.
/// </summary>
/// <param name="configFile"> The config File. </param>
/// <param name="assemblyName">The assembly name.</param>
/// <param name="oldVersion">The old version.</param>
/// <param name="newVersion">The new version.</param>
/// <returns> A byte array of the config file with the redirections added. </returns>
internal byte[] AddAssemblyRedirection(string configFile, AssemblyName assemblyName, string oldVersion, string newVersion)
var configurationElement = FindOrCreateElement(doc, doc, "configuration");
var assemblyBindingSection = FindOrCreateAssemblyBindingSection(doc, configurationElement);
AddAssemblyBindingRedirect(doc, assemblyBindingSection, assemblyName, oldVersion, newVersion);
using var ms = new MemoryStream();
doc.Save(ms);
return ms.ToArray();
}
/// <summary>
/// Gets the Xml document from the config file. This is virtual for unit testing.
/// </summary>
/// <param name="configFile">The config file.</param>
/// <returns>An XmlDocument.</returns>
internal virtual XmlDocument GetXmlDocument(string configFile)
{
var doc = new XmlDocument();
if (!string.IsNullOrEmpty(configFile?.Trim()))
{
var doc = this.GetXmlDocument(configFile);
var configurationElement = FindOrCreateElement(doc, doc, "configuration");
var assemblyBindingSection = FindOrCreateAssemblyBindingSection(doc, configurationElement);
AddAssemblyBindingRedirect(doc, assemblyBindingSection, assemblyName, oldVersion, newVersion);
using var ms = new MemoryStream();
doc.Save(ms);
return ms.ToArray();
using var xmlReader = new XmlTextReader(configFile);
xmlReader.DtdProcessing = DtdProcessing.Prohibit;
xmlReader.XmlResolver = null;
doc.Load(xmlReader);
}
/// <summary>
/// Gets the Xml document from the config file. This is virtual for unit testing.
/// </summary>
/// <param name="configFile">The config file.</param>
/// <returns>An XmlDocument.</returns>
internal virtual XmlDocument GetXmlDocument(string configFile)
return doc;
}
private static XmlElement FindOrCreateElement(XmlDocument doc, XmlNode parent, string name)
{
var ret = parent[name];
if (ret != null)
{
var doc = new XmlDocument();
if (!string.IsNullOrEmpty(configFile?.Trim()))
{
using var xmlReader = new XmlTextReader(configFile);
xmlReader.DtdProcessing = DtdProcessing.Prohibit;
xmlReader.XmlResolver = null;
doc.Load(xmlReader);
}
return doc;
}
private static XmlElement FindOrCreateElement(XmlDocument doc, XmlNode parent, string name)
{
var ret = parent[name];
if (ret != null)
{
return ret;
}
ret = doc.CreateElement(name, parent.NamespaceURI);
parent.AppendChild(ret);
return ret;
}
private static XmlElement FindOrCreateAssemblyBindingSection(XmlDocument doc, XmlElement configurationElement)
ret = doc.CreateElement(name, parent.NamespaceURI);
parent.AppendChild(ret);
return ret;
}
private static XmlElement FindOrCreateAssemblyBindingSection(XmlDocument doc, XmlElement configurationElement)
{
// Each section must be created with the xmlns specified so that
// we don't end up with xmlns="" on each element.
// Find or create the runtime section (this one should not have an xmlns on it).
var runtimeSection = FindOrCreateElement(doc, configurationElement, "runtime");
// Use the assemblyBinding section if it exists; otherwise, create one.
var assemblyBindingSection = runtimeSection["assemblyBinding"];
if (assemblyBindingSection != null)
{
// Each section must be created with the xmlns specified so that
// we don't end up with xmlns="" on each element.
// Find or create the runtime section (this one should not have an xmlns on it).
var runtimeSection = FindOrCreateElement(doc, configurationElement, "runtime");
// Use the assemblyBinding section if it exists; otherwise, create one.
var assemblyBindingSection = runtimeSection["assemblyBinding"];
if (assemblyBindingSection != null)
{
return assemblyBindingSection;
}
assemblyBindingSection = doc.CreateElement("assemblyBinding", XmlNamespace);
runtimeSection.AppendChild(assemblyBindingSection);
return assemblyBindingSection;
}
/// <summary>
/// Add an assembly binding redirect entry to the config file.
/// </summary>
/// <param name="doc"> The doc. </param>
/// <param name="assemblyBindingSection"> The assembly Binding Section. </param>
/// <param name="assemblyName"> The assembly Name. </param>
/// <param name="fromVersion"> The from Version. </param>
/// <param name="toVersion"> The to Version. </param>
private static void AddAssemblyBindingRedirect(
XmlDocument doc,
XmlElement assemblyBindingSection,
AssemblyName assemblyName,
string fromVersion,
string toVersion)
assemblyBindingSection = doc.CreateElement("assemblyBinding", XmlNamespace);
runtimeSection.AppendChild(assemblyBindingSection);
return assemblyBindingSection;
}
/// <summary>
/// Add an assembly binding redirect entry to the config file.
/// </summary>
/// <param name="doc"> The doc. </param>
/// <param name="assemblyBindingSection"> The assembly Binding Section. </param>
/// <param name="assemblyName"> The assembly Name. </param>
/// <param name="fromVersion"> The from Version. </param>
/// <param name="toVersion"> The to Version. </param>
private static void AddAssemblyBindingRedirect(
XmlDocument doc,
XmlElement assemblyBindingSection,
AssemblyName assemblyName,
string fromVersion,
string toVersion)
{
Debug.Assert(assemblyName != null, "assemblyName should not be null.");
if (assemblyName == null)
{
Debug.Assert(assemblyName != null, "assemblyName should not be null.");
if (assemblyName == null)
{
throw new ArgumentNullException(nameof(assemblyName));
}
// Convert the public key token into a string.
StringBuilder publicKeyTokenString = null;
var publicKeyToken = assemblyName.GetPublicKeyToken();
if (publicKeyToken != null)
{
publicKeyTokenString = new StringBuilder(publicKeyToken.GetLength(0) * 2);
for (var i = 0; i < publicKeyToken.GetLength(0); i++)
{
publicKeyTokenString.AppendFormat(
System.Globalization.CultureInfo.InvariantCulture,
"{0:x2}",
new object[] { publicKeyToken[i] });
}
}
// Get the culture as a string.
var cultureString = assemblyName.CultureInfo.ToString();
if (string.IsNullOrEmpty(cultureString))
{
cultureString = "neutral";
}
// Add the dependentAssembly section.
var dependentAssemblySection = doc.CreateElement("dependentAssembly", XmlNamespace);
assemblyBindingSection.AppendChild(dependentAssemblySection);
// Add the assemblyIdentity element.
var assemblyIdentityElement = doc.CreateElement("assemblyIdentity", XmlNamespace);
assemblyIdentityElement.SetAttribute("name", assemblyName.Name);
if (publicKeyTokenString != null)
{
assemblyIdentityElement.SetAttribute("publicKeyToken", publicKeyTokenString.ToString());
}
assemblyIdentityElement.SetAttribute("culture", cultureString);
dependentAssemblySection.AppendChild(assemblyIdentityElement);
var bindingRedirectElement = doc.CreateElement("bindingRedirect", XmlNamespace);
bindingRedirectElement.SetAttribute("oldVersion", fromVersion);
bindingRedirectElement.SetAttribute("newVersion", toVersion);
dependentAssemblySection.AppendChild(bindingRedirectElement);
throw new ArgumentNullException(nameof(assemblyName));
}
// Convert the public key token into a string.
StringBuilder publicKeyTokenString = null;
var publicKeyToken = assemblyName.GetPublicKeyToken();
if (publicKeyToken != null)
{
publicKeyTokenString = new StringBuilder(publicKeyToken.GetLength(0) * 2);
for (var i = 0; i < publicKeyToken.GetLength(0); i++)
{
publicKeyTokenString.AppendFormat(
System.Globalization.CultureInfo.InvariantCulture,
"{0:x2}",
new object[] { publicKeyToken[i] });
}
}
// Get the culture as a string.
var cultureString = assemblyName.CultureInfo.ToString();
if (string.IsNullOrEmpty(cultureString))
{
cultureString = "neutral";
}
// Add the dependentAssembly section.
var dependentAssemblySection = doc.CreateElement("dependentAssembly", XmlNamespace);
assemblyBindingSection.AppendChild(dependentAssemblySection);
// Add the assemblyIdentity element.
var assemblyIdentityElement = doc.CreateElement("assemblyIdentity", XmlNamespace);
assemblyIdentityElement.SetAttribute("name", assemblyName.Name);
if (publicKeyTokenString != null)
{
assemblyIdentityElement.SetAttribute("publicKeyToken", publicKeyTokenString.ToString());
}
assemblyIdentityElement.SetAttribute("culture", cultureString);
dependentAssemblySection.AppendChild(assemblyIdentityElement);
var bindingRedirectElement = doc.CreateElement("bindingRedirect", XmlNamespace);
bindingRedirectElement.SetAttribute("oldVersion", fromVersion);
bindingRedirectElement.SetAttribute("newVersion", toVersion);
dependentAssemblySection.AppendChild(bindingRedirectElement);
}
}

Просмотреть файл

@ -1,32 +1,31 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
namespace Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.Interface
namespace Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.Interface;
/// <summary>
/// A service to log any trace messages from the adapter that would be shown in *.TpTrace files.
/// </summary>
public interface IAdapterTraceLogger
{
/// <summary>
/// A service to log any trace messages from the adapter that would be shown in *.TpTrace files.
/// Log an error in a given format.
/// </summary>
public interface IAdapterTraceLogger
{
/// <summary>
/// Log an error in a given format.
/// </summary>
/// <param name="format"> The format. </param>
/// <param name="args"> The args. </param>
void LogError(string format, params object[] args);
/// <param name="format"> The format. </param>
/// <param name="args"> The args. </param>
void LogError(string format, params object[] args);
/// <summary>
/// Log a warning in a given format.
/// </summary>
/// <param name="format"> The format. </param>
/// <param name="args"> The args. </param>
void LogWarning(string format, params object[] args);
/// <summary>
/// Log a warning in a given format.
/// </summary>
/// <param name="format"> The format. </param>
/// <param name="args"> The args. </param>
void LogWarning(string format, params object[] args);
/// <summary>
/// Log an information message in a given format.
/// </summary>
/// <param name="format"> The format. </param>
/// <param name="args"> The args. </param>
void LogInfo(string format, params object[] args);
}
/// <summary>
/// Log an information message in a given format.
/// </summary>
/// <param name="format"> The format. </param>
/// <param name="args"> The args. </param>
void LogInfo(string format, params object[] args);
}

Просмотреть файл

@ -1,87 +1,86 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
namespace Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.Interface
namespace Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.Interface;
using System.Reflection;
/// <summary>
/// This service is responsible for any file based operations.
/// </summary>
public interface IFileOperations
{
using System.Reflection;
/// <summary>
/// Loads an assembly into the current context.
/// </summary>
/// <param name="assemblyName">
/// The name of the assembly.
/// </param>
/// <param name="isReflectionOnly">
/// Indicates whether this should be a reflection only load.
/// </param>
/// <returns>
/// A handle to the loaded assembly.
/// </returns>
/// <remarks>
/// A platform can choose how it wants the assembly loaded. (ReflectionOnlyLoad/Load etc).
/// </remarks>
Assembly LoadAssembly(string assemblyName, bool isReflectionOnly);
/// <summary>
/// This service is responsible for any file based operations.
/// Gets the path to the .DLL of the assembly.
/// </summary>
public interface IFileOperations
{
/// <summary>
/// Loads an assembly into the current context.
/// </summary>
/// <param name="assemblyName">
/// The name of the assembly.
/// </param>
/// <param name="isReflectionOnly">
/// Indicates whether this should be a reflection only load.
/// </param>
/// <returns>
/// A handle to the loaded assembly.
/// </returns>
/// <remarks>
/// A platform can choose how it wants the assembly loaded. (ReflectionOnlyLoad/Load etc).
/// </remarks>
Assembly LoadAssembly(string assemblyName, bool isReflectionOnly);
/// <param name="assembly">The assembly.</param>
/// <returns>Path to the .DLL of the assembly.</returns>
string GetAssemblyPath(Assembly assembly);
/// <summary>
/// Gets the path to the .DLL of the assembly.
/// </summary>
/// <param name="assembly">The assembly.</param>
/// <returns>Path to the .DLL of the assembly.</returns>
string GetAssemblyPath(Assembly assembly);
/// <summary>
/// Verify if a file exists in the current context.
/// </summary>
/// <param name="assemblyFileName"> The assembly file name. </param>
/// <returns> true if the file exists. </returns>
bool DoesFileExist(string assemblyFileName);
/// <summary>
/// Verify if a file exists in the current context.
/// </summary>
/// <param name="assemblyFileName"> The assembly file name. </param>
/// <returns> true if the file exists. </returns>
bool DoesFileExist(string assemblyFileName);
/// <summary>
/// Creates a Navigation session for the source file.
/// This is used to get file path and line number information for its components.
/// </summary>
/// <param name="source"> The source file. </param>
/// <returns> A Navigation session instance for the current platform. </returns>
/// <remarks>
/// Unfortunately we cannot use INavigationSession introduced in Object Model in Dev14 update-2 because
/// the adapter needs to work with older VS versions as well where this new type would not be defined resulting in a type not found exception.
/// </remarks>
object CreateNavigationSession(string source);
/// <summary>
/// Creates a Navigation session for the source file.
/// This is used to get file path and line number information for its components.
/// </summary>
/// <param name="source"> The source file. </param>
/// <returns> A Navigation session instance for the current platform. </returns>
/// <remarks>
/// Unfortunately we cannot use INavigationSession introduced in Object Model in Dev14 update-2 because
/// the adapter needs to work with older VS versions as well where this new type would not be defined resulting in a type not found exception.
/// </remarks>
object CreateNavigationSession(string source);
/// <summary>
/// Gets the navigation data for a navigation session.
/// </summary>
/// <param name="navigationSession"> The navigation session. </param>
/// <param name="className"> The class name. </param>
/// <param name="methodName"> The method name. </param>
/// <param name="minLineNumber"> The min line number. </param>
/// <param name="fileName"> The file name. </param>
/// <remarks>
/// Unfortunately we cannot use INavigationSession introduced in Object Model in Dev14 update-2 because
/// the adapter needs to work with older VS versions as well where this new type would not be defined resulting in a type not found exception.
/// </remarks>
void GetNavigationData(object navigationSession, string className, string methodName, out int minLineNumber, out string fileName);
/// <summary>
/// Gets the navigation data for a navigation session.
/// </summary>
/// <param name="navigationSession"> The navigation session. </param>
/// <param name="className"> The class name. </param>
/// <param name="methodName"> The method name. </param>
/// <param name="minLineNumber"> The min line number. </param>
/// <param name="fileName"> The file name. </param>
/// <remarks>
/// Unfortunately we cannot use INavigationSession introduced in Object Model in Dev14 update-2 because
/// the adapter needs to work with older VS versions as well where this new type would not be defined resulting in a type not found exception.
/// </remarks>
void GetNavigationData(object navigationSession, string className, string methodName, out int minLineNumber, out string fileName);
/// <summary>
/// Disposes the navigation session instance.
/// </summary>
/// <param name="navigationSession"> The navigation session. </param>
/// <remarks>
/// Unfortunately we cannot use INavigationSession introduced in Object Model in Dev14 update-2 because
/// the adapter needs to work with older VS versions as well where this new type would not be defined resulting in a type not found exception.
/// </remarks>
void DisposeNavigationSession(object navigationSession);
/// <summary>
/// Disposes the navigation session instance.
/// </summary>
/// <param name="navigationSession"> The navigation session. </param>
/// <remarks>
/// Unfortunately we cannot use INavigationSession introduced in Object Model in Dev14 update-2 because
/// the adapter needs to work with older VS versions as well where this new type would not be defined resulting in a type not found exception.
/// </remarks>
void DisposeNavigationSession(object navigationSession);
/// <summary>
/// Gets the full file path of an assembly file.
/// </summary>
/// <param name="assemblyFileName"> The file name. </param>
/// <returns> The full file path. </returns>
string GetFullFilePath(string assemblyFileName);
}
/// <summary>
/// Gets the full file path of an assembly file.
/// </summary>
/// <param name="assemblyFileName"> The file name. </param>
/// <returns> The full file path. </returns>
string GetFullFilePath(string assemblyFileName);
}

Просмотреть файл

@ -1,39 +1,38 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
namespace Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.Interface
namespace Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.Interface;
using System;
using System.Reflection;
/// <summary>
/// This service is responsible for platform specific reflection operations.
/// </summary>
public interface IReflectionOperations
{
using System;
using System.Reflection;
/// <summary>
/// Gets all the custom attributes adorned on a member.
/// </summary>
/// <param name="memberInfo"> The member. </param>
/// <param name="inherit"> True to inspect the ancestors of element; otherwise, false. </param>
/// <returns> The list of attributes on the member. Empty list if none found. </returns>
object[] GetCustomAttributes(MemberInfo memberInfo, bool inherit);
/// <summary>
/// This service is responsible for platform specific reflection operations.
/// Gets all the custom attributes of a given type adorned on a member.
/// </summary>
public interface IReflectionOperations
{
/// <summary>
/// Gets all the custom attributes adorned on a member.
/// </summary>
/// <param name="memberInfo"> The member. </param>
/// <param name="inherit"> True to inspect the ancestors of element; otherwise, false. </param>
/// <returns> The list of attributes on the member. Empty list if none found. </returns>
object[] GetCustomAttributes(MemberInfo memberInfo, bool inherit);
/// <param name="memberInfo"> The member info. </param>
/// <param name="type"> The attribute type. </param>
/// <param name="inherit"> True to inspect the ancestors of element; otherwise, false. </param>
/// <returns> The list of attributes on the member. Empty list if none found. </returns>
object[] GetCustomAttributes(MemberInfo memberInfo, Type type, bool inherit);
/// <summary>
/// Gets all the custom attributes of a given type adorned on a member.
/// </summary>
/// <param name="memberInfo"> The member info. </param>
/// <param name="type"> The attribute type. </param>
/// <param name="inherit"> True to inspect the ancestors of element; otherwise, false. </param>
/// <returns> The list of attributes on the member. Empty list if none found. </returns>
object[] GetCustomAttributes(MemberInfo memberInfo, Type type, bool inherit);
/// <summary>
/// Gets all the custom attributes of a given type on an assembly.
/// </summary>
/// <param name="assembly"> The assembly. </param>
/// <param name="type"> The attribute type. </param>
/// <returns> The list of attributes of the given type on the member. Empty list if none found. </returns>
object[] GetCustomAttributes(Assembly assembly, Type type);
}
/// <summary>
/// Gets all the custom attributes of a given type on an assembly.
/// </summary>
/// <param name="assembly"> The assembly. </param>
/// <param name="type"> The attribute type. </param>
/// <returns> The list of attributes of the given type on the member. Empty list if none found. </returns>
object[] GetCustomAttributes(Assembly assembly, Type type);
}

Просмотреть файл

@ -1,32 +1,31 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
namespace Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.Interface
namespace Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.Interface;
using System.Collections.Generic;
using System.Xml;
/// <summary>
/// To read settings from the runsettings xml for the corresponding platform service.
/// </summary>
public interface ISettingsProvider
{
using System.Collections.Generic;
using System.Xml;
/// <summary>
/// Load settings from the xml reader instance which are specific
/// for the corresponding platform service.
/// </summary>
/// <param name="reader">
/// Reader that can be used to read current node and all its descendants,
/// to load the settings from.</param>
void Load(XmlReader reader);
/// <summary>
/// To read settings from the runsettings xml for the corresponding platform service.
/// The set of properties/settings specific to the platform, that will be surfaced to the user through the test context.
/// </summary>
public interface ISettingsProvider
{
/// <summary>
/// Load settings from the xml reader instance which are specific
/// for the corresponding platform service.
/// </summary>
/// <param name="reader">
/// Reader that can be used to read current node and all its descendants,
/// to load the settings from.</param>
void Load(XmlReader reader);
/// <summary>
/// The set of properties/settings specific to the platform, that will be surfaced to the user through the test context.
/// </summary>
/// <param name="source">
/// source is used to find application base directory used for setting test context properties.
/// </param>
/// <returns>Properties specific to the platform.</returns>
IDictionary<string, object> GetProperties(string source);
}
/// <param name="source">
/// source is used to find application base directory used for setting test context properties.
/// </param>
/// <returns>Properties specific to the platform.</returns>
IDictionary<string, object> GetProperties(string source);
}

Просмотреть файл

@ -1,71 +1,70 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
namespace Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.Interface
namespace Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.Interface;
using System.Collections.Generic;
using Microsoft.VisualStudio.TestTools.UnitTesting;
/// <summary>
/// Operations on the TestContext object that is implemented differently for each platform.
/// </summary>
public interface ITestContext
{
using System.Collections.Generic;
using Microsoft.VisualStudio.TestTools.UnitTesting;
/// <summary>
/// Gets the inner test context object.
/// </summary>
TestContext Context { get; }
/// <summary>
/// Operations on the TestContext object that is implemented differently for each platform.
/// Returns whether property with parameter name is present.
/// </summary>
public interface ITestContext
{
/// <summary>
/// Gets the inner test context object.
/// </summary>
TestContext Context { get; }
/// <param name="propertyName"> The property Name. </param>
/// <param name="propertyValue"> The property Value. </param>
/// <returns> True if the property is present. </returns>
bool TryGetPropertyValue(string propertyName, out object propertyValue);
/// <summary>
/// Returns whether property with parameter name is present.
/// </summary>
/// <param name="propertyName"> The property Name. </param>
/// <param name="propertyValue"> The property Value. </param>
/// <returns> True if the property is present. </returns>
bool TryGetPropertyValue(string propertyName, out object propertyValue);
/// <summary>
/// Adds the parameter name/value pair to property bag.
/// </summary>
/// <param name="propertyName"> The property Name. </param>
/// <param name="propertyValue"> The property Value. </param>
void AddProperty(string propertyName, string propertyValue);
/// <summary>
/// Adds the parameter name/value pair to property bag.
/// </summary>
/// <param name="propertyName"> The property Name. </param>
/// <param name="propertyValue"> The property Value. </param>
void AddProperty(string propertyName, string propertyValue);
/// <summary>
/// Sets the outcome of a Test Method in the TestContext.
/// </summary>
/// <param name="outcome"> The outcome. </param>
void SetOutcome(UnitTestOutcome outcome);
/// <summary>
/// Sets the outcome of a Test Method in the TestContext.
/// </summary>
/// <param name="outcome"> The outcome. </param>
void SetOutcome(UnitTestOutcome outcome);
/// <summary>
/// Set data row for particular run of TestMethod.
/// </summary>
/// <param name="dataRow">data row</param>
void SetDataRow(object dataRow);
/// <summary>
/// Set data row for particular run of TestMethod.
/// </summary>
/// <param name="dataRow">data row</param>
void SetDataRow(object dataRow);
/// <summary>
/// Set connection for TestContext
/// </summary>
/// <param name="dbConnection">db Connection.</param>
void SetDataConnection(object dbConnection);
/// <summary>
/// Set connection for TestContext
/// </summary>
/// <param name="dbConnection">db Connection.</param>
void SetDataConnection(object dbConnection);
/// <summary>
/// Gets the attached Result files
/// </summary>
/// <returns>
/// The list of result files.
/// </returns>
IList<string> GetResultFiles();
/// <summary>
/// Gets the attached Result files
/// </summary>
/// <returns>
/// The list of result files.
/// </returns>
IList<string> GetResultFiles();
/// <summary>
/// Gets the diagnostics messages written to TestContext.WriteLine()
/// </summary>
/// <returns>The test context messages added so far.</returns>
string GetDiagnosticMessages();
/// <summary>
/// Gets the diagnostics messages written to TestContext.WriteLine()
/// </summary>
/// <returns>The test context messages added so far.</returns>
string GetDiagnosticMessages();
/// <summary>
/// Clears the previous testContext writeline messages.
/// </summary>
void ClearDiagnosticMessages();
}
/// <summary>
/// Clears the previous testContext writeline messages.
/// </summary>
void ClearDiagnosticMessages();
}

Просмотреть файл

@ -1,28 +1,27 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
namespace Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.Interface
{
using System.Collections.Generic;
using UTF = Microsoft.VisualStudio.TestTools.UnitTesting;
namespace Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.Interface;
using System.Collections.Generic;
using UTF = Microsoft.VisualStudio.TestTools.UnitTesting;
/// <summary>
/// Interface that provides values from data source when data driven tests are run.
/// </summary>
public interface ITestDataSource
{
/// <summary>
/// Interface that provides values from data source when data driven tests are run.
/// Gets the test data from custom test data source and sets dbconnection in testContext object.
/// </summary>
public interface ITestDataSource
{
/// <summary>
/// Gets the test data from custom test data source and sets dbconnection in testContext object.
/// </summary>
/// <param name="testMethodInfo">
/// The info of test method.
/// </param>
/// <param name="testContext">
/// Test Context object
/// </param>
/// <returns>
/// Test data for calling test method.
/// </returns>
IEnumerable<object> GetData(UTF.ITestMethod testMethodInfo, ITestContext testContext);
}
/// <param name="testMethodInfo">
/// The info of test method.
/// </param>
/// <param name="testContext">
/// Test Context object
/// </param>
/// <returns>
/// Test data for calling test method.
/// </returns>
IEnumerable<object> GetData(UTF.ITestMethod testMethodInfo, ITestContext testContext);
}

Просмотреть файл

@ -1,47 +1,46 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
namespace Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.Interface
{
using System;
using System.Collections.Generic;
using System.Reflection;
namespace Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.Interface;
using Microsoft.VisualStudio.TestPlatform.ObjectModel;
using Microsoft.VisualStudio.TestPlatform.ObjectModel.Adapter;
using System;
using System.Collections.Generic;
using System.Reflection;
using Microsoft.VisualStudio.TestPlatform.ObjectModel;
using Microsoft.VisualStudio.TestPlatform.ObjectModel.Adapter;
/// <summary>
/// The TestDeployment interface.
/// </summary>
public interface ITestDeployment
{
/// <summary>
/// Deploy deployment items for the specified test cases.
/// </summary>
/// <param name="testCases"> The test cases. </param>
/// <param name="runContext"> The run context. </param>
/// <param name="frameworkHandle"> The framework handle. </param>
/// <returns> True if deployment is done. </returns>
bool Deploy(IEnumerable<TestCase> testCases, IRunContext runContext, IFrameworkHandle frameworkHandle);
/// <summary>
/// The TestDeployment interface.
/// Gets the set of deployment items on a method and its corresponding class.
/// </summary>
public interface ITestDeployment
{
/// <summary>
/// Deploy deployment items for the specified test cases.
/// </summary>
/// <param name="testCases"> The test cases. </param>
/// <param name="runContext"> The run context. </param>
/// <param name="frameworkHandle"> The framework handle. </param>
/// <returns> True if deployment is done. </returns>
bool Deploy(IEnumerable<TestCase> testCases, IRunContext runContext, IFrameworkHandle frameworkHandle);
/// <param name="method"> The method. </param>
/// <param name="type"> The type. </param>
/// <param name="warnings"> The warnings. </param>
/// <returns> A KeyValuePair of deployment items. </returns>
KeyValuePair<string, string>[] GetDeploymentItems(MethodInfo method, Type type, ICollection<string> warnings);
/// <summary>
/// Gets the set of deployment items on a method and its corresponding class.
/// </summary>
/// <param name="method"> The method. </param>
/// <param name="type"> The type. </param>
/// <param name="warnings"> The warnings. </param>
/// <returns> A KeyValuePair of deployment items. </returns>
KeyValuePair<string, string>[] GetDeploymentItems(MethodInfo method, Type type, ICollection<string> warnings);
/// <summary>
/// Cleanup deployment item directories.
/// </summary>
void Cleanup();
/// <summary>
/// Cleanup deployment item directories.
/// </summary>
void Cleanup();
/// <summary>
/// Gets the deployment output directory where the source file along with all its dependencies is dropped.
/// </summary>
/// <returns> The deployment output directory. </returns>
string GetDeploymentDirectory();
}
/// <summary>
/// Gets the deployment output directory where the source file along with all its dependencies is dropped.
/// </summary>
/// <returns> The deployment output directory. </returns>
string GetDeploymentDirectory();
}

Просмотреть файл

@ -1,35 +1,34 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
namespace Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.Interface
namespace Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.Interface;
using System.Collections.Generic;
using System.Reflection;
/// <summary>
/// This platform service is responsible for any data or operations to validate
/// the test sources provided to the adapter.
/// </summary>
public interface ITestSource
{
using System.Collections.Generic;
using System.Reflection;
/// <summary>
/// Gets the set of valid extensions for sources targeting this platform.
/// </summary>
IEnumerable<string> ValidSourceExtensions { get; }
/// <summary>
/// This platform service is responsible for any data or operations to validate
/// the test sources provided to the adapter.
/// Verifies if the assembly provided is referenced by the source.
/// </summary>
public interface ITestSource
{
/// <summary>
/// Gets the set of valid extensions for sources targeting this platform.
/// </summary>
IEnumerable<string> ValidSourceExtensions { get; }
/// <param name="assemblyName"> The assembly name. </param>
/// <param name="source"> The source. </param>
/// <returns> True if the assembly is referenced. </returns>
bool IsAssemblyReferenced(AssemblyName assemblyName, string source);
/// <summary>
/// Verifies if the assembly provided is referenced by the source.
/// </summary>
/// <param name="assemblyName"> The assembly name. </param>
/// <param name="source"> The source. </param>
/// <returns> True if the assembly is referenced. </returns>
bool IsAssemblyReferenced(AssemblyName assemblyName, string source);
/// <summary>
/// Gets the set of sources (dll's/exe's) that contain tests. If a source is a package(appx), return the file(dll/exe) that contains tests from it.
/// </summary>
/// <param name="sources"> Sources given to the adapter. </param>
/// <returns> Sources that contains tests. </returns>
IEnumerable<string> GetTestSources(IEnumerable<string> sources);
}
/// <summary>
/// Gets the set of sources (dll's/exe's) that contain tests. If a source is a package(appx), return the file(dll/exe) that contains tests from it.
/// </summary>
/// <param name="sources"> Sources given to the adapter. </param>
/// <returns> Sources that contains tests. </returns>
IEnumerable<string> GetTestSources(IEnumerable<string> sources);
}

Просмотреть файл

@ -1,30 +1,29 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
namespace Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.Interface
namespace Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.Interface;
using System;
/// <summary>
/// A host that loads the test source.This can be in isolation for desktop using an AppDomain or just loading the source in the current context.
/// </summary>
public interface ITestSourceHost : IDisposable
{
using System;
/// <summary>
/// Sets up the isolation host.
/// </summary>
void SetupHost();
/// <summary>
/// A host that loads the test source.This can be in isolation for desktop using an AppDomain or just loading the source in the current context.
/// Creates an instance of a given type in the test source host.
/// </summary>
public interface ITestSourceHost : IDisposable
{
/// <summary>
/// Sets up the isolation host.
/// </summary>
void SetupHost();
/// <summary>
/// Creates an instance of a given type in the test source host.
/// </summary>
/// <param name="type"> The type that needs to be created in the host. </param>
/// <param name="args">The arguments to pass to the constructor.
/// This array of arguments must match in number, order, and type the parameters of the constructor to invoke.
/// Pass in null for a constructor with no arguments.
/// </param>
/// <returns> An instance of the type created in the host. </returns>
/// <remarks> If a type is to be created in isolation then it needs to be a MarshalByRefObject. </remarks>
object CreateInstanceForType(Type type, object[] args);
}
/// <param name="type"> The type that needs to be created in the host. </param>
/// <param name="args">The arguments to pass to the constructor.
/// This array of arguments must match in number, order, and type the parameters of the constructor to invoke.
/// Pass in null for a constructor with no arguments.
/// </param>
/// <returns> An instance of the type created in the host. </returns>
/// <remarks> If a type is to be created in isolation then it needs to be a MarshalByRefObject. </remarks>
object CreateInstanceForType(Type type, object[] args);
}

Просмотреть файл

@ -1,29 +1,28 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
namespace Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.Interface
namespace Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.Interface;
using System;
using System.Threading;
/// <summary>
/// This service is responsible for any thread operations specific to a platform.
/// </summary>
public interface IThreadOperations
{
using System;
using System.Threading;
/// <summary>
/// Execute the given action synchronously on a background thread in the given timeout.
/// </summary>
/// <param name="action">The action to execute.</param>
/// <param name="timeout">Timeout for the specified action in milliseconds.</param>
/// <param name="cancelToken">Token to cancel the execution</param>
/// <returns>Returns true if the action executed before the timeout. returns false otherwise.</returns>
bool Execute(Action action, int timeout, CancellationToken cancelToken);
/// <summary>
/// This service is responsible for any thread operations specific to a platform.
/// Execute an action with handling for Thread Aborts (if possible) so the main thread of the adapter does not die.
/// </summary>
public interface IThreadOperations
{
/// <summary>
/// Execute the given action synchronously on a background thread in the given timeout.
/// </summary>
/// <param name="action">The action to execute.</param>
/// <param name="timeout">Timeout for the specified action in milliseconds.</param>
/// <param name="cancelToken">Token to cancel the execution</param>
/// <returns>Returns true if the action executed before the timeout. returns false otherwise.</returns>
bool Execute(Action action, int timeout, CancellationToken cancelToken);
/// <summary>
/// Execute an action with handling for Thread Aborts (if possible) so the main thread of the adapter does not die.
/// </summary>
/// <param name="action"> The action to execute. </param>
void ExecuteWithAbortSafety(Action action);
}
/// <param name="action"> The action to execute. </param>
void ExecuteWithAbortSafety(Action action);
}

Некоторые файлы не были показаны из-за слишком большого количества измененных файлов Показать больше