diff --git a/.editorconfig b/.editorconfig
new file mode 100644
index 0000000000..e06213f0b5
--- /dev/null
+++ b/.editorconfig
@@ -0,0 +1,436 @@
+# The default style generated by dotnet new editorconfig, with some minor changes.
+# All changes are annotated with `# not default`
+
+root = true
+
+# All files
+[*]
+# indent_size intentionally not specified in this section.
+indent_style = space
+insert_final_newline = true
+trim_trailing_whitespace = true
+
+# Shell scripts
+[*.sh]
+end_of_line = lf
+[*.{cmd,bat}]
+end_of_line = crlf
+
+# Xml files
+[*.xml]
+indent_size = 2
+
+# Markdown files
+[*.md]
+indent_size = 2
+trim_trailing_whitespace = false
+
+# PowerShell scripts
+[*.ps1]
+indent_size = 4
+
+# Visual Studio XML project files
+[*.{csproj,vcxproj,vcxproj.filters,proj,projitems,shproj}]
+indent_size = 2
+charset = utf-8
+
+# Visual Studio and .NET related XML config files
+[*.{props,targets,ruleset,config,nuspec,resx,vsixmanifest,vsct}]
+indent_size = 2
+
+# YAML files
+[*.{yml,yaml}]
+indent_size = 2
+
+# C# files
+[*.cs]
+
+# Do not set 'end_of_line = crlf' otherwise the rule IDE0055: Fix formatting will trigger on Linux/MAC OS.
+
+#### Core EditorConfig Options ####
+
+# Indentation and spacing
+indent_size = 4
+tab_width = 4
+
+# New line preferences
+insert_final_newline = false
+
+#### .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.
+
+# this. and Me. preferences
+dotnet_style_qualification_for_event = false:warning # not default, default is false:silent, helped with removing this. prefixes
+dotnet_style_qualification_for_field = false:warning # not default, same as above
+dotnet_style_qualification_for_method = false:warning # not default, same as above
+dotnet_style_qualification_for_property = false:warning # not default, same as above
+
+# Language keywords vs BCL types preferences
+dotnet_style_predefined_type_for_locals_parameters_members = true:silent
+dotnet_style_predefined_type_for_member_access = true:silent
+
+# Parentheses preferences
+dotnet_style_parentheses_in_arithmetic_binary_operators = always_for_clarity:silent
+dotnet_style_parentheses_in_other_binary_operators = always_for_clarity:silent
+dotnet_style_parentheses_in_other_operators = never_if_unnecessary:silent
+dotnet_style_parentheses_in_relational_binary_operators = always_for_clarity:silent
+
+# Modifier preferences
+dotnet_style_require_accessibility_modifiers = for_non_interface_members:silent
+
+# Expression-level preferences
+dotnet_style_coalesce_expression = true:suggestion
+dotnet_style_collection_initializer = true:suggestion
+dotnet_style_explicit_tuple_names = true:suggestion
+dotnet_style_null_propagation = true:suggestion
+dotnet_style_object_initializer = false:suggestion # not default, default is true, avoided because null references in object initializers are hard to diagnose from logs
+dotnet_style_operator_placement_when_wrapping = beginning_of_line
+dotnet_style_prefer_conditional_expression_over_assignment = true:suggestion
+dotnet_style_prefer_conditional_expression_over_return = true:suggestion
+dotnet_style_prefer_inferred_anonymous_type_member_names = true:suggestion
+dotnet_style_prefer_inferred_tuple_names = true:suggestion
+dotnet_style_prefer_is_null_check_over_reference_equality_method = true:suggestion
+dotnet_style_prefer_simplified_boolean_expressions = true:suggestion
+dotnet_style_prefer_simplified_interpolation = true:suggestion
+
+# IDE0074: Use compound assignment
+dotnet_style_prefer_compound_assignment = true:warning # not default, default is true:suggestion, increased severity to ensure it is used
+dotnet_diagnostic.IDE0074.severity = warning # not default, set in accordance to previous setting
+
+# IDE0032: Use auto property
+dotnet_style_prefer_auto_properties = true:warning # not default, default is true:suggestion, increased severity to ensure it is used
+dotnet_diagnostic.IDE0032.severity = warning # not default, set in accordance to previous setting
+
+# Field preferences
+dotnet_style_readonly_field = true:warning
+
+# Parameter preferences
+dotnet_code_quality_unused_parameters = all:suggestion
+
+# Suppression preferences
+dotnet_remove_unnecessary_suppression_exclusions = none
+
+#### C# Coding Conventions ####
+
+# var preferences
+csharp_style_var_elsewhere = false:silent # not default, turned off for now
+csharp_style_var_for_built_in_types = false:silent # not default, turned off for now
+csharp_style_var_when_type_is_apparent = false:silent # not default, turned off for now
+
+# Expression-bodied members
+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:suggestion
+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
+
+# Pattern matching preferences
+csharp_style_pattern_matching_over_as_with_null_check = true:suggestion
+csharp_style_pattern_matching_over_is_with_cast_check = true:suggestion
+csharp_style_prefer_not_pattern = true:suggestion
+csharp_style_prefer_pattern_matching = true:silent
+csharp_style_prefer_switch_expression = true:suggestion
+
+# Null-checking preferences
+csharp_style_conditional_delegate_call = true:suggestion
+
+# Modifier preferences
+csharp_prefer_static_local_function = true:warning
+csharp_preferred_modifier_order = public,private,protected,internal,static,extern,new,virtual,abstract,sealed,override,readonly,unsafe,volatile,async:silent
+
+# Code-block preferences
+csharp_prefer_braces = true:silent
+csharp_prefer_simple_using_statement = true:warning # not default, default is true:suggestion, increased severity to ensure it is used
+dotnet_diagnostic.IDE0063.severity = warning
+
+# Expression-level preferences
+csharp_prefer_simple_default_expression = true:suggestion
+csharp_style_deconstructed_variable_declaration = true:suggestion
+csharp_style_inlined_variable_declaration = true:suggestion
+csharp_style_prefer_local_over_anonymous_function = false:suggestion # not default, default is true, avoided because we use anonymous functions in multiple places and it does not make the code clearer
+csharp_style_prefer_index_operator = true:suggestion
+csharp_style_prefer_range_operator = true:suggestion
+csharp_style_throw_expression = true:suggestion
+csharp_style_unused_value_assignment_preference = discard_variable:suggestion
+csharp_style_unused_value_expression_statement_preference = discard_variable:silent
+
+# 'using' directive preferences
+csharp_using_directive_placement = outside_namespace:silent
+dotnet_diagnostic.IDE0065.severity = none
+
+# CA1822: Mark members as static
+dotnet_diagnostic.CA1822.severity = suggestion # TODO: increase severity and fix related entries
+
+# IDE0090: Use 'new(...)'
+dotnet_diagnostic.IDE0090.severity = warning # not default, increased severity to go with simpler declarations
+
+# IDE0005: Remove unnecessary import
+dotnet_diagnostic.IDE0005.severity = warning # not default, increased severity to ensure it is used
+
+#### C# Formatting Rules ####
+
+# IDE0055: Fix formatting
+dotnet_diagnostic.IDE0055.severity = warning
+
+# New line preferences
+csharp_new_line_before_catch = true
+csharp_new_line_before_else = true
+csharp_new_line_before_finally = true
+csharp_new_line_before_members_in_anonymous_types = true
+csharp_new_line_before_members_in_object_initializers = true
+csharp_new_line_before_open_brace = all
+csharp_new_line_between_query_expression_clauses = true
+
+# Indentation preferences
+csharp_indent_block_contents = true
+csharp_indent_braces = false
+csharp_indent_case_contents = true
+csharp_indent_case_contents_when_block = true
+csharp_indent_labels = one_less_than_current
+csharp_indent_switch_labels = true
+
+# Space preferences
+csharp_space_after_cast = false
+csharp_space_after_colon_in_inheritance_clause = true
+csharp_space_after_comma = true
+csharp_space_after_dot = false
+csharp_space_after_keywords_in_control_flow_statements = true
+csharp_space_after_semicolon_in_for_statement = true
+csharp_space_around_binary_operators = before_and_after
+csharp_space_around_declaration_statements = false
+csharp_space_before_colon_in_inheritance_clause = true
+csharp_space_before_comma = false
+csharp_space_before_dot = false
+csharp_space_before_open_square_brackets = false
+csharp_space_before_semicolon_in_for_statement = false
+csharp_space_between_empty_square_brackets = false
+csharp_space_between_method_call_empty_parameter_list_parentheses = false
+csharp_space_between_method_call_name_and_opening_parenthesis = false
+csharp_space_between_method_call_parameter_list_parentheses = false
+csharp_space_between_method_declaration_empty_parameter_list_parentheses = false
+csharp_space_between_method_declaration_name_and_open_parenthesis = false
+csharp_space_between_method_declaration_parameter_list_parentheses = false
+csharp_space_between_parentheses = false
+csharp_space_between_square_brackets = false
+
+# Wrapping preferences
+csharp_preserve_single_line_blocks = true
+csharp_preserve_single_line_statements = true
+
+#### Naming styles ####
+
+# not default, we have a lot of multiplatform code in compiler directives that defines methods, but keeps the bodies empty
+# IDE0052: Remove unused private members
+dotnet_diagnostic.IDE0051.severity = silent
+
+# not default, we have a lot of multiplatform code in compiler directives that defines methods, but keeps the bodies empty
+# IDE0052: Remove unread private members
+dotnet_diagnostic.IDE0052.severity = silent
+
+# IDE1006: Naming Styles
+dotnet_diagnostic.IDE1006.severity = warning
+dotnet_diagnostic.IDE0073.severity = warning
+csharp_style_prefer_method_group_conversion = true:silent
+csharp_style_prefer_null_check_over_type_check = true:suggestion
+csharp_style_implicit_object_creation_when_type_is_apparent = true:suggestion
+csharp_style_prefer_tuple_swap = true:suggestion
+
+# Naming rules
+
+dotnet_style_namespace_match_folder = true:suggestion
+csharp_style_namespace_declarations = file_scoped:warning # not default, default is block_scoped:silent, changed to follow modern preferences
+
+dotnet_naming_rule.types_and_namespaces_should_be_pascalcase.severity = suggestion
+dotnet_naming_rule.types_and_namespaces_should_be_pascalcase.symbols = types_and_namespaces
+dotnet_naming_rule.types_and_namespaces_should_be_pascalcase.style = pascalcase
+
+dotnet_naming_rule.interfaces_should_be_ipascalcase.severity = suggestion
+dotnet_naming_rule.interfaces_should_be_ipascalcase.symbols = interfaces
+dotnet_naming_rule.interfaces_should_be_ipascalcase.style = ipascalcase
+
+dotnet_naming_rule.type_parameters_should_be_tpascalcase.severity = suggestion
+dotnet_naming_rule.type_parameters_should_be_tpascalcase.symbols = type_parameters
+dotnet_naming_rule.type_parameters_should_be_tpascalcase.style = tpascalcase
+
+dotnet_naming_rule.methods_should_be_pascalcase.severity = suggestion
+dotnet_naming_rule.methods_should_be_pascalcase.symbols = methods
+dotnet_naming_rule.methods_should_be_pascalcase.style = pascalcase
+
+dotnet_naming_rule.properties_should_be_pascalcase.severity = suggestion
+dotnet_naming_rule.properties_should_be_pascalcase.symbols = properties
+dotnet_naming_rule.properties_should_be_pascalcase.style = pascalcase
+
+dotnet_naming_rule.events_should_be_pascalcase.severity = suggestion
+dotnet_naming_rule.events_should_be_pascalcase.symbols = events
+dotnet_naming_rule.events_should_be_pascalcase.style = pascalcase
+
+dotnet_naming_rule.local_variables_should_be_camelcase.severity = suggestion
+dotnet_naming_rule.local_variables_should_be_camelcase.symbols = local_variables
+dotnet_naming_rule.local_variables_should_be_camelcase.style = camelcase
+
+dotnet_naming_rule.local_constants_should_be_camelcase.severity = suggestion
+dotnet_naming_rule.local_constants_should_be_camelcase.symbols = local_constants
+dotnet_naming_rule.local_constants_should_be_camelcase.style = camelcase
+
+dotnet_naming_rule.parameters_should_be_camelcase.severity = suggestion
+dotnet_naming_rule.parameters_should_be_camelcase.symbols = parameters
+dotnet_naming_rule.parameters_should_be_camelcase.style = camelcase
+
+dotnet_naming_rule.public_fields_should_be_pascalcase.severity = suggestion
+dotnet_naming_rule.public_fields_should_be_pascalcase.symbols = public_fields
+dotnet_naming_rule.public_fields_should_be_pascalcase.style = pascalcase
+
+dotnet_naming_rule.private_fields_should_be__camelcase.severity = suggestion
+dotnet_naming_rule.private_fields_should_be__camelcase.symbols = private_fields
+dotnet_naming_rule.private_fields_should_be__camelcase.style = _camelcase
+
+dotnet_naming_rule.private_static_fields_should_be_s_camelcase.severity = suggestion
+dotnet_naming_rule.private_static_fields_should_be_s_camelcase.symbols = private_static_fields
+dotnet_naming_rule.private_static_fields_should_be_s_camelcase.style = s_camelcase
+
+dotnet_naming_rule.public_constant_fields_should_be_pascalcase.severity = suggestion
+dotnet_naming_rule.public_constant_fields_should_be_pascalcase.symbols = public_constant_fields
+dotnet_naming_rule.public_constant_fields_should_be_pascalcase.style = pascalcase
+
+dotnet_naming_rule.private_constant_fields_should_be_pascalcase.severity = suggestion
+dotnet_naming_rule.private_constant_fields_should_be_pascalcase.symbols = private_constant_fields
+dotnet_naming_rule.private_constant_fields_should_be_pascalcase.style = pascalcase
+
+dotnet_naming_rule.public_static_readonly_fields_should_be_pascalcase.severity = suggestion
+dotnet_naming_rule.public_static_readonly_fields_should_be_pascalcase.symbols = public_static_readonly_fields
+dotnet_naming_rule.public_static_readonly_fields_should_be_pascalcase.style = pascalcase
+
+dotnet_naming_rule.private_static_readonly_fields_should_be_pascalcase.severity = suggestion
+dotnet_naming_rule.private_static_readonly_fields_should_be_pascalcase.symbols = private_static_readonly_fields
+dotnet_naming_rule.private_static_readonly_fields_should_be_pascalcase.style = pascalcase
+
+dotnet_naming_rule.enums_should_be_pascalcase.severity = suggestion
+dotnet_naming_rule.enums_should_be_pascalcase.symbols = enums
+dotnet_naming_rule.enums_should_be_pascalcase.style = pascalcase
+
+dotnet_naming_rule.local_functions_should_be_pascalcase.severity = suggestion
+dotnet_naming_rule.local_functions_should_be_pascalcase.symbols = local_functions
+dotnet_naming_rule.local_functions_should_be_pascalcase.style = pascalcase
+
+dotnet_naming_rule.non_field_members_should_be_pascalcase.severity = suggestion
+dotnet_naming_rule.non_field_members_should_be_pascalcase.symbols = non_field_members
+dotnet_naming_rule.non_field_members_should_be_pascalcase.style = pascalcase
+
+# Symbol specifications
+
+dotnet_naming_symbols.interfaces.applicable_kinds = interface
+dotnet_naming_symbols.interfaces.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected
+dotnet_naming_symbols.interfaces.required_modifiers =
+
+dotnet_naming_symbols.enums.applicable_kinds = enum
+dotnet_naming_symbols.enums.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected
+dotnet_naming_symbols.enums.required_modifiers =
+
+dotnet_naming_symbols.events.applicable_kinds = event
+dotnet_naming_symbols.events.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected
+dotnet_naming_symbols.events.required_modifiers =
+
+dotnet_naming_symbols.methods.applicable_kinds = method
+dotnet_naming_symbols.methods.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected
+dotnet_naming_symbols.methods.required_modifiers =
+
+dotnet_naming_symbols.properties.applicable_kinds = property
+dotnet_naming_symbols.properties.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected
+dotnet_naming_symbols.properties.required_modifiers =
+
+dotnet_naming_symbols.public_fields.applicable_kinds = field
+dotnet_naming_symbols.public_fields.applicable_accessibilities = public, internal
+dotnet_naming_symbols.public_fields.required_modifiers =
+
+dotnet_naming_symbols.private_fields.applicable_kinds = field
+dotnet_naming_symbols.private_fields.applicable_accessibilities = private, protected, protected_internal, private_protected
+dotnet_naming_symbols.private_fields.required_modifiers =
+
+dotnet_naming_symbols.private_static_fields.applicable_kinds = field
+dotnet_naming_symbols.private_static_fields.applicable_accessibilities = private, protected, protected_internal, private_protected
+dotnet_naming_symbols.private_static_fields.required_modifiers = static
+
+dotnet_naming_symbols.types_and_namespaces.applicable_kinds = namespace, class, struct, interface, enum
+dotnet_naming_symbols.types_and_namespaces.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected
+dotnet_naming_symbols.types_and_namespaces.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 =
+
+dotnet_naming_symbols.type_parameters.applicable_kinds = namespace
+dotnet_naming_symbols.type_parameters.applicable_accessibilities = *
+dotnet_naming_symbols.type_parameters.required_modifiers =
+
+dotnet_naming_symbols.private_constant_fields.applicable_kinds = field
+dotnet_naming_symbols.private_constant_fields.applicable_accessibilities = private, protected, protected_internal, private_protected
+dotnet_naming_symbols.private_constant_fields.required_modifiers = const
+
+dotnet_naming_symbols.local_variables.applicable_kinds = local
+dotnet_naming_symbols.local_variables.applicable_accessibilities = local
+dotnet_naming_symbols.local_variables.required_modifiers =
+
+dotnet_naming_symbols.local_constants.applicable_kinds = local
+dotnet_naming_symbols.local_constants.applicable_accessibilities = local
+dotnet_naming_symbols.local_constants.required_modifiers = const
+
+dotnet_naming_symbols.parameters.applicable_kinds = parameter
+dotnet_naming_symbols.parameters.applicable_accessibilities = *
+dotnet_naming_symbols.parameters.required_modifiers =
+
+dotnet_naming_symbols.public_constant_fields.applicable_kinds = field
+dotnet_naming_symbols.public_constant_fields.applicable_accessibilities = public, internal
+dotnet_naming_symbols.public_constant_fields.required_modifiers = const
+
+dotnet_naming_symbols.public_static_readonly_fields.applicable_kinds = field
+dotnet_naming_symbols.public_static_readonly_fields.applicable_accessibilities = public, internal
+dotnet_naming_symbols.public_static_readonly_fields.required_modifiers = readonly, static
+
+dotnet_naming_symbols.private_static_readonly_fields.applicable_kinds = field
+dotnet_naming_symbols.private_static_readonly_fields.applicable_accessibilities = private, protected, protected_internal, private_protected
+dotnet_naming_symbols.private_static_readonly_fields.required_modifiers = readonly, static
+
+dotnet_naming_symbols.local_functions.applicable_kinds = local_function
+dotnet_naming_symbols.local_functions.applicable_accessibilities = *
+dotnet_naming_symbols.local_functions.required_modifiers =
+
+# Naming styles
+
+dotnet_naming_style.pascalcase.required_prefix =
+dotnet_naming_style.pascalcase.required_suffix =
+dotnet_naming_style.pascalcase.word_separator =
+dotnet_naming_style.pascalcase.capitalization = pascal_case
+
+dotnet_naming_style.ipascalcase.required_prefix = I
+dotnet_naming_style.ipascalcase.required_suffix =
+dotnet_naming_style.ipascalcase.word_separator =
+dotnet_naming_style.ipascalcase.capitalization = pascal_case
+
+dotnet_naming_style.tpascalcase.required_prefix = T
+dotnet_naming_style.tpascalcase.required_suffix =
+dotnet_naming_style.tpascalcase.word_separator =
+dotnet_naming_style.tpascalcase.capitalization = pascal_case
+
+dotnet_naming_style._camelcase.required_prefix = _
+dotnet_naming_style._camelcase.required_suffix =
+dotnet_naming_style._camelcase.word_separator =
+dotnet_naming_style._camelcase.capitalization = camel_case
+
+dotnet_naming_style.camelcase.required_prefix =
+dotnet_naming_style.camelcase.required_suffix =
+dotnet_naming_style.camelcase.word_separator =
+dotnet_naming_style.camelcase.capitalization = camel_case
+
+dotnet_naming_style.s_camelcase.required_prefix = s_
+dotnet_naming_style.s_camelcase.required_suffix =
+dotnet_naming_style.s_camelcase.word_separator =
+dotnet_naming_style.s_camelcase.capitalization = camel_case
diff --git a/TestPlatform.sln b/TestPlatform.sln
index f7e9b97ff0..61c4c661c9 100644
--- a/TestPlatform.sln
+++ b/TestPlatform.sln
@@ -97,6 +97,7 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "scripts", "scripts", "{EE49
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{E344E0A2-7715-4C7F-BAF7-D64EA94CB19B}"
ProjectSection(SolutionItems) = preProject
+ .editorconfig = .editorconfig
Nuget.config = Nuget.config
EndProjectSection
EndProject
diff --git a/eng/Versions.props b/eng/Versions.props
index e3c179fae5..15755f607b 100644
--- a/eng/Versions.props
+++ b/eng/Versions.props
@@ -33,7 +33,7 @@
2.1.0
4.1.0-1.21507.14
15.7.2
- 4.8.3
+ 4.16.1
5.3.0.1
2.3.0
9.0.1
diff --git a/playground/MSTest1/UnitTest1.cs b/playground/MSTest1/UnitTest1.cs
index a3303b5a99..9b925d47ee 100644
--- a/playground/MSTest1/UnitTest1.cs
+++ b/playground/MSTest1/UnitTest1.cs
@@ -1,13 +1,15 @@
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+
using Microsoft.VisualStudio.TestTools.UnitTesting;
-namespace MSTest1
+namespace MSTest1;
+
+[TestClass]
+public class UnitTest1
{
- [TestClass]
- public class UnitTest1
+ [TestMethod]
+ public void TestMethod1()
{
- [TestMethod]
- public void TestMethod1()
- {
- }
}
-}
+}
\ No newline at end of file
diff --git a/playground/TestPlatform.Playground/Program.cs b/playground/TestPlatform.Playground/Program.cs
index 37be2f96c3..edd8b95e21 100644
--- a/playground/TestPlatform.Playground/Program.cs
+++ b/playground/TestPlatform.Playground/Program.cs
@@ -1,8 +1,12 @@
-using Microsoft.TestPlatform.VsTestConsole.TranslationLayer;
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+
+using Microsoft.TestPlatform.VsTestConsole.TranslationLayer;
using Microsoft.VisualStudio.TestPlatform.ObjectModel;
using Microsoft.VisualStudio.TestPlatform.ObjectModel.Client;
using Microsoft.VisualStudio.TestPlatform.ObjectModel.Client.Interfaces;
using Microsoft.VisualStudio.TestPlatform.ObjectModel.Logging;
+
using System;
using System.Collections.Generic;
using System.Diagnostics;
@@ -11,123 +15,116 @@
using System.Reflection;
using System.Threading;
-namespace TestPlatform.Playground
+namespace TestPlatform.Playground;
+
+internal class Program
{
- internal class Program
+ static void Main(string[] args)
{
- static void Main(string[] args)
+ // This project references TranslationLayer, vstest.console, TestHostProvider, testhost and MSTest1 projects, to make sure
+ // we build all the dependencies of that are used to run tests via VSTestConsoleWrapper. It then copies the components from
+ // their original build locations, to $(TargetDir)\vstest.console directory, and it's subfolders to create an executable
+ // copy of TestPlatform that is similar to what we ship.
+ //
+ // The copying might trigger only on re-build, if you see outdated dependencies, Rebuild this project instead of just Build.
+ //
+ // Use this as playground for your debugging of end-to-end scenarios, it will automatically attach vstest.console and teshost
+ // sub-processes. It won't stop at entry-point automatically, don't forget to set your breakpoints, or remove VSTEST_DEBUG_NOBP
+ // from the environment variables of this project.
+
+ var thisAssemblyPath = Assembly.GetEntryAssembly().Location;
+ var here = Path.GetDirectoryName(thisAssemblyPath);
+ var playground = Path.GetFullPath(Path.Combine(here, "..", "..", "..", ".."));
+
+ var console = Path.Combine(here, "vstest.console", "vstest.console.exe");
+ var consoleOptions = new ConsoleParameters
{
- // This project references TranslationLayer, vstest.console, TestHostProvider, testhost and MSTest1 projects, to make sure
- // we build all the dependencies of that are used to run tests via VSTestConsoleWrapper. It then copies the components from
- // their original build locations, to $(TargetDir)\vstest.console directory, and it's subfolders to create an executable
- // copy of TestPlatform that is similar to what we ship.
- //
- // The copying might trigger only on re-build, if you see outdated dependencies, Rebuild this project instead of just Build.
- //
- // Use this as playground for your debugging of end-to-end scenarios, it will automatically attach vstest.console and teshost
- // sub-processes. It won't stop at entry-point automatically, don't forget to set your breakpoints, or remove VSTEST_DEBUG_NOBP
- // from the environment variables of this project.
-
- var thisAssemblyPath = Assembly.GetEntryAssembly().Location;
- var here = Path.GetDirectoryName(thisAssemblyPath);
- var playground = Path.GetFullPath(Path.Combine(here, "..", "..", "..", ".."));
-
- var console = Path.Combine(here, "vstest.console", "vstest.console.exe");
- var consoleOptions = new ConsoleParameters
- {
- LogFilePath = Path.Combine(here, "logs", "log.txt"),
- TraceLevel = TraceLevel.Verbose,
- };
-
- var r = new VsTestConsoleWrapper(console, consoleOptions);
-
- var sourceSettings = @"
+ LogFilePath = Path.Combine(here, "logs", "log.txt"),
+ TraceLevel = TraceLevel.Verbose,
+ };
+
+ var r = new VsTestConsoleWrapper(console, consoleOptions);
+
+ var sourceSettings = @"
true
";
- var sources = new[] {
- Path.Combine(playground, "MSTest1", "bin", "Debug", "net472", "MSTest1.dll")
- };
+ var sources = new[] {
+ Path.Combine(playground, "MSTest1", "bin", "Debug", "net472", "MSTest1.dll")
+ };
+
+ var options = new TestPlatformOptions();
+ r.RunTestsWithCustomTestHost(sources, sourceSettings, options, new TestRunHandler(), new DebuggerTestHostLauncher());
+ }
+
+ public class TestRunHandler : ITestRunEventsHandler
+ {
+
+ public TestRunHandler()
+ {
+ }
+
+ public void HandleLogMessage(TestMessageLevel level, string message)
+ {
+ Console.WriteLine($"[{level.ToString().ToUpper()}]: {message}");
+ }
- var options = new TestPlatformOptions();
- r.RunTestsWithCustomTestHost(sources, sourceSettings, options, new TestRunHandler(), new DebuggerTestHostLauncher());
+ public void HandleRawMessage(string rawMessage)
+ {
+ Console.WriteLine($"[MESSAGE]: { rawMessage}");
}
- public class TestRunHandler : ITestRunEventsHandler
+ public void HandleTestRunComplete(TestRunCompleteEventArgs testRunCompleteArgs, TestRunChangedEventArgs lastChunkArgs, ICollection runContextAttachments, ICollection executorUris)
{
+ Console.WriteLine($"[COMPLETE]: err: { testRunCompleteArgs.Error }, lastChunk: {WriteTests(lastChunkArgs?.NewTestResults)}");
+ }
+
+ public void HandleTestRunStatsChange(TestRunChangedEventArgs testRunChangedArgs)
+ {
+ Console.WriteLine($"[PROGRESS - NEW RESULTS]: {WriteTests(testRunChangedArgs.NewTestResults)}");
+ }
- public TestRunHandler()
- {
- }
-
- public void HandleLogMessage(TestMessageLevel level, string message)
- {
- Console.WriteLine($"[{level.ToString().ToUpper()}]: {message}");
- }
-
- public void HandleRawMessage(string rawMessage)
- {
- Console.WriteLine($"[MESSAGE]: { rawMessage}");
- }
-
- public void HandleTestRunComplete(TestRunCompleteEventArgs testRunCompleteArgs, TestRunChangedEventArgs lastChunkArgs, ICollection runContextAttachments, ICollection executorUris)
- {
- Console.WriteLine($"[COMPLETE]: err: { testRunCompleteArgs.Error }, lastChunk: {WriteTests(lastChunkArgs?.NewTestResults)}");
- }
-
- public void HandleTestRunStatsChange(TestRunChangedEventArgs testRunChangedArgs)
- {
- Console.WriteLine($"[PROGRESS - NEW RESULTS]: {WriteTests(testRunChangedArgs.NewTestResults)}");
- }
-
- public int LaunchProcessWithDebuggerAttached(TestProcessStartInfo testProcessStartInfo)
- {
- throw new NotImplementedException();
- }
-
- private string WriteTests(IEnumerable testResults)
- {
- return WriteTests(testResults?.Select(t => t.TestCase));
- }
-
- private string WriteTests(IEnumerable testCases)
- {
- if (testCases == null)
- {
- return null;
- }
-
- return "\t" + string.Join("\n\t", testCases.Select(r => r.DisplayName));
-
- }
+ public int LaunchProcessWithDebuggerAttached(TestProcessStartInfo testProcessStartInfo)
+ {
+ throw new NotImplementedException();
+ }
+
+ private string WriteTests(IEnumerable testResults)
+ {
+ return WriteTests(testResults?.Select(t => t.TestCase));
+ }
+
+ private string WriteTests(IEnumerable testCases)
+ {
+ return testCases == null ? null : "\t" + string.Join("\n\t", testCases.Select(r => r.DisplayName));
+ }
+ }
+
+ internal class DebuggerTestHostLauncher : ITestHostLauncher2
+ {
+ public bool IsDebug => true;
+
+ public bool AttachDebuggerToProcess(int pid)
+ {
+ return true;
+ }
+
+ public bool AttachDebuggerToProcess(int pid, CancellationToken cancellationToken)
+ {
+ return true;
+ }
+
+ public int LaunchTestHost(TestProcessStartInfo defaultTestHostStartInfo)
+ {
+ return 1;
}
- internal class DebuggerTestHostLauncher : ITestHostLauncher2
+ public int LaunchTestHost(TestProcessStartInfo defaultTestHostStartInfo, CancellationToken cancellationToken)
{
- public bool IsDebug => true;
-
- public bool AttachDebuggerToProcess(int pid)
- {
- return true;
- }
-
- public bool AttachDebuggerToProcess(int pid, CancellationToken cancellationToken)
- {
- return true;
- }
-
- public int LaunchTestHost(TestProcessStartInfo defaultTestHostStartInfo)
- {
- return 1;
- }
-
- public int LaunchTestHost(TestProcessStartInfo defaultTestHostStartInfo, CancellationToken cancellationToken)
- {
- return 1;
- }
+ return 1;
}
}
-}
+}
\ No newline at end of file
diff --git a/scripts/build/ExternalAssemblyVersions.cs b/scripts/build/ExternalAssemblyVersions.cs
index cfaa81efa9..6245a3ea3a 100644
--- a/scripts/build/ExternalAssemblyVersions.cs
+++ b/scripts/build/ExternalAssemblyVersions.cs
@@ -1,12 +1,14 @@
-namespace Microsoft.VisualStudio.TestPlatform
+// 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;
+
+internal class ExternalAssemblyVersions
{
- internal class ExternalAssemblyVersions
- {
- ///
- /// Refers to the versions of the assemblies retrieved from the
- /// Microsoft.QualityTools.Testing.Fakes.TestRunnerHarness package.
- /// The Package version can be found in "scripts\build\TestPlatform.Dependencies.props"
- ///
- internal const string MicrosoftFakesAssemblyVersion = "17.0.0.0";
- }
+ ///
+ /// Refers to the versions of the assemblies retrieved from the
+ /// Microsoft.QualityTools.Testing.Fakes.TestRunnerHarness package.
+ /// The Package version can be found in "scripts\build\TestPlatform.Dependencies.props"
+ ///
+ internal const string MicrosoftFakesAssemblyVersion = "17.0.0.0";
}
\ No newline at end of file
diff --git a/scripts/build/TestPlatform.Dependencies.props b/scripts/build/TestPlatform.Dependencies.props
index 5190258c1c..8f5fa9f8a4 100644
--- a/scripts/build/TestPlatform.Dependencies.props
+++ b/scripts/build/TestPlatform.Dependencies.props
@@ -31,7 +31,6 @@
5.11.0
5.0.0
9.0.1
- 4.8.3
17.1.0-preview-2-31925-026
diff --git a/scripts/build/TestPlatform.Settings.targets b/scripts/build/TestPlatform.Settings.targets
index 2a6a9b71bd..28e6a3ca99 100644
--- a/scripts/build/TestPlatform.Settings.targets
+++ b/scripts/build/TestPlatform.Settings.targets
@@ -7,6 +7,9 @@
readable when we read the file as xml, don't move it to a .props file, unless you change the build server process -->
17.2.0
preview
+ true
+ preview
+ true
-
-
-
- stylecop.json
-
-
-
-
-
- 1.0.1
- All
-
-
-
diff --git a/src/AttachVS/AttachVs.cs b/src/AttachVS/AttachVs.cs
index b4f4c3da53..2c47192174 100644
--- a/src/AttachVS/AttachVs.cs
+++ b/src/AttachVS/AttachVs.cs
@@ -1,4 +1,7 @@
-using System;
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+
+using System;
using System.Diagnostics;
using System.Linq;
using System.Reflection;
@@ -7,291 +10,289 @@
using System.Runtime.InteropServices.ComTypes;
using System.Threading;
-namespace Microsoft.TestPlatform.AttachVS
+namespace Microsoft.TestPlatform.AttachVS;
+
+internal class DebuggerUtility
{
- internal class DebuggerUtility
+ internal static bool AttachVSToProcess(int? pid, int? vsPid)
{
- internal static bool AttachVSToProcess(int? pid, int? vsPid)
+ try
{
- try
+ if (pid == null)
{
- if (pid == null)
- {
- Trace($"FAIL: Pid is null.");
- return false;
- }
- var process = Process.GetProcessById(pid.Value);
- Trace($"Starting with pid '{pid}({process?.ProcessName})', and vsPid '{vsPid}'");
- Trace($"Using pid: {pid} to get parent VS.");
- var vs = GetVsFromPid(Process.GetProcessById(vsPid ?? process.Id));
+ Trace($"FAIL: Pid is null.");
+ return false;
+ }
+ var process = Process.GetProcessById(pid.Value);
+ Trace($"Starting with pid '{pid}({process?.ProcessName})', and vsPid '{vsPid}'");
+ Trace($"Using pid: {pid} to get parent VS.");
+ var vs = GetVsFromPid(Process.GetProcessById(vsPid ?? process.Id));
- if (vs != null)
- {
- Trace($"Parent VS is {vs.ProcessName} ({vs.Id}).");
- AttachTo(process, vs);
- }
- else
+ if (vs != null)
+ {
+ Trace($"Parent VS is {vs.ProcessName} ({vs.Id}).");
+ AttachTo(process, vs);
+ }
+ else
+ {
+ Trace($"Parent VS not found, finding the first VS that started.");
+ var processes = Process.GetProcesses().Where(p => p.ProcessName == "devenv").Select(p =>
{
- Trace($"Parent VS not found, finding the first VS that started.");
- var processes = Process.GetProcesses().Where(p => p.ProcessName == "devenv").Select(p =>
+ try
{
- try
- {
- return new { Process = p, StartTime = p.StartTime, HasExited = p.HasExited };
- }
- catch
- {
- return null;
- }
- }).Where(p => p != null && !p.HasExited).OrderBy(p => p.StartTime).ToList();
+ return new { Process = p, p.StartTime, p.HasExited };
+ }
+ catch
+ {
+ return null;
+ }
+ }).Where(p => p != null && !p.HasExited).OrderBy(p => p.StartTime).ToList();
- var firstVs = processes.FirstOrDefault();
- Trace($"Found VS {firstVs.Process.Id}");
- AttachTo(process, firstVs.Process);
- }
- return true;
- }
- catch (Exception ex)
- {
- Trace($"ERROR: {ex}, {ex.StackTrace}");
- return false;
+ var firstVs = processes.FirstOrDefault();
+ Trace($"Found VS {firstVs.Process.Id}");
+ AttachTo(process, firstVs.Process);
}
+ return true;
+ }
+ catch (Exception ex)
+ {
+ Trace($"ERROR: {ex}, {ex.StackTrace}");
+ return false;
}
+ }
+
+ private static void AttachTo(Process process, Process vs)
+ {
+ var attached = AttachVs(vs, process.Id);
+ if (attached)
+ {
+ // You won't see this in DebugView++ because at this point VS is already attached and all the output goes into Debug window in VS.
+ Trace($"SUCCESS: Attached process: {process.ProcessName} ({process.Id})");
+ }
+ else
+ {
+ Trace($"FAIL: Could not attach process: {process.ProcessName} ({process.Id})");
+ }
+ }
- private static void AttachTo(Process process, Process vs)
+ private static bool AttachVs(Process vs, int pid)
+ {
+ IBindCtx bindCtx = null;
+ IRunningObjectTable runninObjectTable = null;
+ IEnumMoniker enumMoniker = null;
+ try
{
- var attached = AttachVs(vs, process.Id);
- if (attached)
+ var r = CreateBindCtx(0, out bindCtx);
+ Marshal.ThrowExceptionForHR(r);
+ if (bindCtx == null)
{
- // You won't see this in DebugView++ because at this point VS is already attached and all the output goes into Debug window in VS.
- Trace($"SUCCESS: Attached process: {process.ProcessName} ({process.Id})");
+ Trace($"BindCtx is null. Cannot attach VS.");
+ return false;
}
- else
+ bindCtx.GetRunningObjectTable(out runninObjectTable);
+ if (runninObjectTable == null)
{
- Trace($"FAIL: Could not attach process: {process.ProcessName} ({process.Id})");
+ Trace($"RunningObjectTable is null. Cannot attach VS.");
+ return false;
}
- }
- private static bool AttachVs(Process vs, int pid)
- {
- IBindCtx bindCtx = null;
- IRunningObjectTable runninObjectTable = null;
- IEnumMoniker enumMoniker = null;
- try
+ runninObjectTable.EnumRunning(out enumMoniker);
+ if (enumMoniker == null)
{
- var r = CreateBindCtx(0, out bindCtx);
- Marshal.ThrowExceptionForHR(r);
- if (bindCtx == null)
- {
- Trace($"BindCtx is null. Cannot attach VS.");
- return false;
- }
- bindCtx.GetRunningObjectTable(out runninObjectTable);
- if (runninObjectTable == null)
- {
- Trace($"RunningObjectTable is null. Cannot attach VS.");
- return false;
- }
+ Trace($"EnumMoniker is null. Cannot attach VS.");
+ return false;
+ }
- runninObjectTable.EnumRunning(out enumMoniker);
- if (enumMoniker == null)
- {
- Trace($"EnumMoniker is null. Cannot attach VS.");
- return false;
- }
+ var dteSuffix = ":" + vs.Id;
- var dteSuffix = ":" + vs.Id;
+ var moniker = new IMoniker[1];
+ while (enumMoniker.Next(1, moniker, IntPtr.Zero) == 0 && moniker[0] != null)
+ {
- var moniker = new IMoniker[1];
- while (enumMoniker.Next(1, moniker, IntPtr.Zero) == 0 && moniker[0] != null)
- {
- string dn;
+ moniker[0].GetDisplayName(bindCtx, null, out string dn);
- moniker[0].GetDisplayName(bindCtx, null, out dn);
+ if (dn.StartsWith("!VisualStudio.DTE.") && dn.EndsWith(dteSuffix))
+ {
+ object dbg, lps;
+ runninObjectTable.GetObject(moniker[0], out object dte);
- if (dn.StartsWith("!VisualStudio.DTE.") && dn.EndsWith(dteSuffix))
+ // The COM object can be busy, we retry few times, hoping that it won't be busy next time.
+ for (var i = 0; i < 10; i++)
{
- object dte, dbg, lps;
- runninObjectTable.GetObject(moniker[0], out dte);
-
- // The COM object can be busy, we retry few times, hoping that it won't be busy next time.
- for (var i = 0; i < 10; i++)
+ try
{
- try
+ dbg = dte.GetType().InvokeMember("Debugger", BindingFlags.GetProperty, null, dte, null);
+ lps = dbg.GetType().InvokeMember("LocalProcesses", BindingFlags.GetProperty, null, dbg, null);
+ var lpn = (System.Collections.IEnumerator)lps.GetType().InvokeMember("GetEnumerator", BindingFlags.InvokeMethod, null, lps, null);
+
+ while (lpn.MoveNext())
{
- dbg = dte.GetType().InvokeMember("Debugger", BindingFlags.GetProperty, null, dte, null);
- lps = dbg.GetType().InvokeMember("LocalProcesses", BindingFlags.GetProperty, null, dbg, null);
- var lpn = (System.Collections.IEnumerator)lps.GetType().InvokeMember("GetEnumerator", BindingFlags.InvokeMethod, null, lps, null);
+ var pn = Convert.ToInt32(lpn.Current.GetType().InvokeMember("ProcessID", BindingFlags.GetProperty, null, lpn.Current, null));
- while (lpn.MoveNext())
+ if (pn == pid)
{
- var pn = Convert.ToInt32(lpn.Current.GetType().InvokeMember("ProcessID", BindingFlags.GetProperty, null, lpn.Current, null));
-
- if (pn == pid)
- {
- lpn.Current.GetType().InvokeMember("Attach", BindingFlags.InvokeMethod, null, lpn.Current, null);
- return true;
- }
+ lpn.Current.GetType().InvokeMember("Attach", BindingFlags.InvokeMethod, null, lpn.Current, null);
+ return true;
}
}
- catch (COMException ex)
- {
- Trace($"ComException: Retrying in 250ms.\n{ex}");
- Thread.Sleep(250);
- }
}
- Marshal.ReleaseComObject(moniker[0]);
-
- break;
+ catch (COMException ex)
+ {
+ Trace($"ComException: Retrying in 250ms.\n{ex}");
+ Thread.Sleep(250);
+ }
}
-
Marshal.ReleaseComObject(moniker[0]);
+
+ break;
}
- return false;
+
+ Marshal.ReleaseComObject(moniker[0]);
}
- finally
+ return false;
+ }
+ finally
+ {
+ if (enumMoniker != null)
{
- if (enumMoniker != null)
+ try
{
- try
- {
- Marshal.ReleaseComObject(enumMoniker);
- }
- catch { }
+ Marshal.ReleaseComObject(enumMoniker);
}
- if (runninObjectTable != null)
+ catch { }
+ }
+ if (runninObjectTable != null)
+ {
+ try
{
- try
- {
- Marshal.ReleaseComObject(runninObjectTable);
- }
- catch { }
+ Marshal.ReleaseComObject(runninObjectTable);
}
- if (bindCtx != null)
+ catch { }
+ }
+ if (bindCtx != null)
+ {
+ try
{
- try
- {
- Marshal.ReleaseComObject(bindCtx);
- }
- catch { }
+ Marshal.ReleaseComObject(bindCtx);
}
+ catch { }
}
}
+ }
- private static Process GetVsFromPid(Process process)
+ private static Process GetVsFromPid(Process process)
+ {
+ var parent = process;
+ while (!IsVsOrNull(parent))
{
- var parent = process;
- while (!IsVsOrNull(parent))
- {
- parent = GetParentProcess(parent);
- }
+ parent = GetParentProcess(parent);
+ }
+
+ return parent;
+ }
- return parent;
+ private static bool IsVsOrNull(Process process)
+ {
+ if (process == null)
+ {
+ Trace("Parent process is null..");
+ return true;
}
- private static bool IsVsOrNull(Process process)
+ var isVs = process.ProcessName.Equals("devenv", StringComparison.InvariantCultureIgnoreCase);
+ if (isVs)
{
- if (process == null)
- {
- Trace("Parent process is null..");
- return true;
- }
+ Trace($"Process {process.ProcessName} ({process.Id}) is VS.");
+ }
+ else
+ {
+ Trace($"Process {process.ProcessName} ({process.Id}) is not VS.");
+ }
- var isVs = process.ProcessName.Equals("devenv", StringComparison.InvariantCultureIgnoreCase);
- if (isVs)
- {
- Trace($"Process {process.ProcessName} ({process.Id}) is VS.");
- }
- else
+ return isVs;
+ }
+
+ private static bool IsCorrectParent(Process currentProcess, Process parent)
+ {
+ try
+ {
+ // Parent needs to start before the child, otherwise it might be a different process
+ // that is just reusing the same PID.
+ if (parent.StartTime <= currentProcess.StartTime)
{
- Trace($"Process {process.ProcessName} ({process.Id}) is not VS.");
+ return true;
}
- return isVs;
+ Trace($"Process {parent.ProcessName} ({parent.Id}) is not a valid parent because it started after the current process.");
+ return false;
+ }
+ catch
+ {
+ // Access denied or process exited while we were holding the Process object.
+ return false;
}
+ }
- private static bool IsCorrectParent(Process currentProcess, Process parent)
+ private static Process GetParentProcess(Process process)
+ {
+ int id;
+ try
{
- try
- {
- // Parent needs to start before the child, otherwise it might be a different process
- // that is just reusing the same PID.
- if (parent.StartTime <= currentProcess.StartTime)
- {
- return true;
- }
+ var handle = process.Handle;
+ var res = NtQueryInformationProcess(handle, 0, out var pbi, Marshal.SizeOf(), out int size);
- Trace($"Process {parent.ProcessName} ({parent.Id}) is not a valid parent because it started after the current process.");
- return false;
- }
- catch
- {
- // Access denied or process exited while we were holding the Process object.
- return false;
- }
+ var p = res != 0 ? -1 : pbi.InheritedFromUniqueProcessId.ToInt32();
+
+ id = p;
+ }
+ catch
+ {
+ id = -1;
}
- private static Process GetParentProcess(Process process)
+ Process parent = null;
+ if (id != -1)
{
- var id = -1;
try
{
- var handle = process.Handle;
- var res = NtQueryInformationProcess(handle, 0, out var pbi, Marshal.SizeOf(), out int size);
-
- var p = res != 0 ? -1 : pbi.InheritedFromUniqueProcessId.ToInt32();
-
- id = p;
+ parent = Process.GetProcessById(id);
}
catch
{
- id = -1;
+ // throws when parent no longer runs
}
-
- Process parent = null;
- if (id != -1)
- {
- try
- {
- parent = Process.GetProcessById(id);
- }
- catch
- {
- // throws when parent no longer runs
- }
- }
-
- return IsCorrectParent(process, parent) ? parent : null;
}
- private static void Trace(string message, [CallerMemberName] string methodName = null)
- {
- System.Diagnostics.Trace.WriteLine($"[AttachVS]{methodName}: {message}");
- }
+ return IsCorrectParent(process, parent) ? parent : null;
+ }
- [StructLayout(LayoutKind.Sequential)]
- private struct PROCESS_BASIC_INFORMATION
- {
- public IntPtr ExitStatus;
- public IntPtr PebBaseAddress;
- public IntPtr AffinityMask;
- public IntPtr BasePriority;
- public IntPtr UniqueProcessId;
- public IntPtr InheritedFromUniqueProcessId;
- }
+ private static void Trace(string message, [CallerMemberName] string methodName = null)
+ {
+ System.Diagnostics.Trace.WriteLine($"[AttachVS]{methodName}: {message}");
+ }
- [DllImport("ntdll.dll", SetLastError = true)]
- private static extern int NtQueryInformationProcess(
- IntPtr processHandle,
- int processInformationClass,
- out PROCESS_BASIC_INFORMATION processInformation,
- int processInformationLength,
- out int returnLength);
+ [StructLayout(LayoutKind.Sequential)]
+ private struct PROCESS_BASIC_INFORMATION
+ {
+ public readonly IntPtr ExitStatus;
+ public readonly IntPtr PebBaseAddress;
+ public readonly IntPtr AffinityMask;
+ public readonly IntPtr BasePriority;
+ public readonly IntPtr UniqueProcessId;
+ public IntPtr InheritedFromUniqueProcessId;
+ }
- [DllImport("Kernel32")]
- private static extern uint GetTickCount();
+ [DllImport("ntdll.dll", SetLastError = true)]
+ private static extern int NtQueryInformationProcess(
+ IntPtr processHandle,
+ int processInformationClass,
+ out PROCESS_BASIC_INFORMATION processInformation,
+ int processInformationLength,
+ out int returnLength);
- [DllImport("ole32.dll")]
- private static extern int CreateBindCtx(uint reserved, out IBindCtx ppbc);
- }
-}
+ [DllImport("Kernel32")]
+ private static extern uint GetTickCount();
+
+ [DllImport("ole32.dll")]
+ private static extern int CreateBindCtx(uint reserved, out IBindCtx ppbc);
+}
\ No newline at end of file
diff --git a/src/AttachVS/Program.cs b/src/AttachVS/Program.cs
index da7c26c1d4..c7c85e4f96 100644
--- a/src/AttachVS/Program.cs
+++ b/src/AttachVS/Program.cs
@@ -1,31 +1,33 @@
-using System;
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+
+using System;
using System.Diagnostics;
using System.Linq;
-namespace Microsoft.TestPlatform.AttachVS
+namespace Microsoft.TestPlatform.AttachVS;
+
+internal class Program
{
- internal class Program
+ static void Main(string[] args)
{
- static void Main(string[] args)
- {
- Trace.Listeners.Add(new ConsoleTraceListener());
+ Trace.Listeners.Add(new ConsoleTraceListener());
- int? pid = ParsePid(args, position: 0);
- int? vsPid = ParsePid(args, position: 1);
+ int? pid = ParsePid(args, position: 0);
+ int? vsPid = ParsePid(args, position: 1);
- var exitCode = DebuggerUtility.AttachVSToProcess(pid, vsPid) ? 0 : 1;
- Environment.Exit(exitCode);
- }
+ var exitCode = DebuggerUtility.AttachVSToProcess(pid, vsPid) ? 0 : 1;
+ Environment.Exit(exitCode);
+ }
- private static int? ParsePid(string[] args, int position)
- {
- var id = args.Skip(position).Take(1).SingleOrDefault();
- int? pid = id == null
- ? null
- : int.TryParse(id, out var i)
- ? i
- : null;
- return pid;
- }
+ private static int? ParsePid(string[] args, int position)
+ {
+ var id = args.Skip(position).Take(1).SingleOrDefault();
+ int? pid = id == null
+ ? null
+ : int.TryParse(id, out var i)
+ ? i
+ : null;
+ return pid;
}
-}
+}
\ No newline at end of file
diff --git a/src/DataCollectors/DumpMinitool/Program.cs b/src/DataCollectors/DumpMinitool/Program.cs
index 815cf6bfa9..99f2d0e18c 100644
--- a/src/DataCollectors/DumpMinitool/Program.cs
+++ b/src/DataCollectors/DumpMinitool/Program.cs
@@ -1,173 +1,174 @@
-using Microsoft.Win32.SafeHandles;
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+
+using Microsoft.Win32.SafeHandles;
+
using System;
using System.Diagnostics;
+using System.Diagnostics.CodeAnalysis;
using System.IO;
using System.Runtime.InteropServices;
using System.Threading;
-namespace DumpMinitool
+namespace DumpMinitool;
+
+internal class Program
{
- internal class Program
+ static int Main(string[] args)
{
- static int Main(string[] args)
+ DebuggerBreakpoint.WaitForDebugger("VSTEST_DUMPTOOL_DEBUG");
+ Console.WriteLine($"Dump minitool: Started with arguments {string.Join(" ", args)}");
+ if (args?.Length != 6)
{
- DebuggerBreakpoint.WaitForDebugger("VSTEST_DUMPTOOL_DEBUG");
- Console.WriteLine($"Dump minitool: Started with arguments {string.Join(" ", args)}");
- if (args?.Length != 6)
- {
- Console.WriteLine($"There were { args?.Length ?? 0 } parameters. Provide exactly 6 parameters: --file --processId --dumpType ");
- return 2;
- }
+ Console.WriteLine($"There were { args?.Length ?? 0 } parameters. Provide exactly 6 parameters: --file --processId --dumpType ");
+ return 2;
+ }
- var outputFile = args[1];
- var processId = int.Parse(args[3]);
- var type = Enum.Parse(typeof(DumpTypeOption), args[5]);
+ var outputFile = args[1];
+ var processId = int.Parse(args[3]);
+ var type = Enum.Parse(typeof(DumpTypeOption), args[5]);
- Console.WriteLine($"Output file: '{outputFile}'");
- Console.WriteLine($"Process id: {processId}");
- Console.WriteLine($"Dump type: {type}");
+ Console.WriteLine($"Output file: '{outputFile}'");
+ Console.WriteLine($"Process id: {processId}");
+ Console.WriteLine($"Dump type: {type}");
- var process = Process.GetProcessById(processId);
+ var process = Process.GetProcessById(processId);
- using (var stream = new FileStream(outputFile, FileMode.Create, FileAccess.ReadWrite, FileShare.None))
- {
- NativeMethods.MINIDUMP_EXCEPTION_INFORMATION exceptionInfo = default;
+ using var stream = new FileStream(outputFile, FileMode.Create, FileAccess.ReadWrite, FileShare.None);
+ NativeMethods.MINIDUMP_EXCEPTION_INFORMATION exceptionInfo = default;
- NativeMethods.MINIDUMP_TYPE dumpType = NativeMethods.MINIDUMP_TYPE.MiniDumpNormal;
- switch (type)
+ NativeMethods.MINIDUMP_TYPE dumpType = NativeMethods.MINIDUMP_TYPE.MiniDumpNormal;
+ switch (type)
+ {
+ case DumpTypeOption.Full:
+ dumpType = NativeMethods.MINIDUMP_TYPE.MiniDumpWithFullMemory |
+ NativeMethods.MINIDUMP_TYPE.MiniDumpWithDataSegs |
+ NativeMethods.MINIDUMP_TYPE.MiniDumpWithHandleData |
+ NativeMethods.MINIDUMP_TYPE.MiniDumpWithUnloadedModules |
+ NativeMethods.MINIDUMP_TYPE.MiniDumpWithFullMemoryInfo |
+ NativeMethods.MINIDUMP_TYPE.MiniDumpWithThreadInfo |
+ NativeMethods.MINIDUMP_TYPE.MiniDumpWithTokenInformation;
+ break;
+ case DumpTypeOption.WithHeap:
+ dumpType = NativeMethods.MINIDUMP_TYPE.MiniDumpWithPrivateReadWriteMemory |
+ NativeMethods.MINIDUMP_TYPE.MiniDumpWithDataSegs |
+ NativeMethods.MINIDUMP_TYPE.MiniDumpWithHandleData |
+ NativeMethods.MINIDUMP_TYPE.MiniDumpWithUnloadedModules |
+ NativeMethods.MINIDUMP_TYPE.MiniDumpWithFullMemoryInfo |
+ NativeMethods.MINIDUMP_TYPE.MiniDumpWithThreadInfo |
+ NativeMethods.MINIDUMP_TYPE.MiniDumpWithTokenInformation;
+ break;
+ case DumpTypeOption.Mini:
+ dumpType = NativeMethods.MINIDUMP_TYPE.MiniDumpWithThreadInfo;
+ break;
+ }
+
+ // Retry the write dump on ERROR_PARTIAL_COPY
+ for (int i = 0; i < 5; i++)
+ {
+ // Dump the process!
+ if (NativeMethods.MiniDumpWriteDump(process.Handle, (uint)process.Id, stream.SafeFileHandle, dumpType, ref exceptionInfo, IntPtr.Zero, IntPtr.Zero))
+ {
+ Console.WriteLine("Dumped process.");
+ return 0;
+ }
+ else
+ {
+ int err = Marshal.GetHRForLastWin32Error();
+ if (err != NativeMethods.ERROR_PARTIAL_COPY)
{
- case DumpTypeOption.Full:
- dumpType = NativeMethods.MINIDUMP_TYPE.MiniDumpWithFullMemory |
- NativeMethods.MINIDUMP_TYPE.MiniDumpWithDataSegs |
- NativeMethods.MINIDUMP_TYPE.MiniDumpWithHandleData |
- NativeMethods.MINIDUMP_TYPE.MiniDumpWithUnloadedModules |
- NativeMethods.MINIDUMP_TYPE.MiniDumpWithFullMemoryInfo |
- NativeMethods.MINIDUMP_TYPE.MiniDumpWithThreadInfo |
- NativeMethods.MINIDUMP_TYPE.MiniDumpWithTokenInformation;
- break;
- case DumpTypeOption.WithHeap:
- dumpType = NativeMethods.MINIDUMP_TYPE.MiniDumpWithPrivateReadWriteMemory |
- NativeMethods.MINIDUMP_TYPE.MiniDumpWithDataSegs |
- NativeMethods.MINIDUMP_TYPE.MiniDumpWithHandleData |
- NativeMethods.MINIDUMP_TYPE.MiniDumpWithUnloadedModules |
- NativeMethods.MINIDUMP_TYPE.MiniDumpWithFullMemoryInfo |
- NativeMethods.MINIDUMP_TYPE.MiniDumpWithThreadInfo |
- NativeMethods.MINIDUMP_TYPE.MiniDumpWithTokenInformation;
- break;
- case DumpTypeOption.Mini:
- dumpType = NativeMethods.MINIDUMP_TYPE.MiniDumpWithThreadInfo;
- break;
+ Console.WriteLine($"Error dumping process {err}");
+ Marshal.ThrowExceptionForHR(err);
}
-
- // Retry the write dump on ERROR_PARTIAL_COPY
- for (int i = 0; i < 5; i++)
+ else
{
- // Dump the process!
- if (NativeMethods.MiniDumpWriteDump(process.Handle, (uint)process.Id, stream.SafeFileHandle, dumpType, ref exceptionInfo, IntPtr.Zero, IntPtr.Zero))
- {
- Console.WriteLine("Dumped process.");
- return 0;
- }
- else
- {
- int err = Marshal.GetHRForLastWin32Error();
- if (err != NativeMethods.ERROR_PARTIAL_COPY)
- {
- Console.WriteLine($"Error dumping process {err}");
- Marshal.ThrowExceptionForHR(err);
- }
- else
- {
- Console.WriteLine($"Error dumping process, was ERROR_PARTIAL_COPY, retrying.");
- }
- }
+ Console.WriteLine($"Error dumping process, was ERROR_PARTIAL_COPY, retrying.");
}
-
- Console.WriteLine($"Error dumping process after 5 retries.");
- return 1;
}
}
- private static class NativeMethods
- {
- public const int ERROR_PARTIAL_COPY = unchecked((int)0x8007012b);
+ Console.WriteLine($"Error dumping process after 5 retries.");
+ return 1;
+ }
- [DllImport("Dbghelp.dll", SetLastError = true)]
- public static extern bool MiniDumpWriteDump(IntPtr hProcess, uint ProcessId, SafeFileHandle hFile, MINIDUMP_TYPE DumpType, ref MINIDUMP_EXCEPTION_INFORMATION ExceptionParam, IntPtr UserStreamParam, IntPtr CallbackParam);
+ [SuppressMessage("Style", "IDE1006:Naming Styles", Justification = "Do not report for native methods")]
+ private static class NativeMethods
+ {
+ public const int ERROR_PARTIAL_COPY = unchecked((int)0x8007012b);
- [StructLayout(LayoutKind.Sequential, Pack = 4)]
- public struct MINIDUMP_EXCEPTION_INFORMATION
- {
- public uint ThreadId;
- public IntPtr ExceptionPointers;
- public int ClientPointers;
- }
+ [DllImport("Dbghelp.dll", SetLastError = true)]
+ public static extern bool MiniDumpWriteDump(IntPtr hProcess, uint ProcessId, SafeFileHandle hFile, MINIDUMP_TYPE dumpType, ref MINIDUMP_EXCEPTION_INFORMATION exceptionParam, IntPtr userStreamParam, IntPtr callbackParam);
- [Flags]
-#pragma warning disable SA1201 // Elements must appear in the correct order
- public enum MINIDUMP_TYPE : uint
-#pragma warning restore SA1201 // Elements must appear in the correct order
- {
- MiniDumpNormal = 0,
- MiniDumpWithDataSegs = 1 << 0,
- MiniDumpWithFullMemory = 1 << 1,
- MiniDumpWithHandleData = 1 << 2,
- MiniDumpFilterMemory = 1 << 3,
- MiniDumpScanMemory = 1 << 4,
- MiniDumpWithUnloadedModules = 1 << 5,
- MiniDumpWithIndirectlyReferencedMemory = 1 << 6,
- MiniDumpFilterModulePaths = 1 << 7,
- MiniDumpWithProcessThreadData = 1 << 8,
- MiniDumpWithPrivateReadWriteMemory = 1 << 9,
- MiniDumpWithoutOptionalData = 1 << 10,
- MiniDumpWithFullMemoryInfo = 1 << 11,
- MiniDumpWithThreadInfo = 1 << 12,
- MiniDumpWithCodeSegs = 1 << 13,
- MiniDumpWithoutAuxiliaryState = 1 << 14,
- MiniDumpWithFullAuxiliaryState = 1 << 15,
- MiniDumpWithPrivateWriteCopyMemory = 1 << 16,
- MiniDumpIgnoreInaccessibleMemory = 1 << 17,
- MiniDumpWithTokenInformation = 1 << 18,
- MiniDumpWithModuleHeaders = 1 << 19,
- MiniDumpFilterTriage = 1 << 20,
- MiniDumpWithAvxXStateContext = 1 << 21,
- MiniDumpWithIptTrace = 1 << 22,
- MiniDumpValidTypeFlags = (-1) ^ ((~1) << 22)
- }
+ [StructLayout(LayoutKind.Sequential, Pack = 4)]
+ public struct MINIDUMP_EXCEPTION_INFORMATION
+ {
+ public readonly uint ThreadId;
+ public readonly IntPtr ExceptionPointers;
+ public readonly int ClientPointers;
}
- }
- internal enum DumpTypeOption
- {
- Full,
- WithHeap,
- Mini,
+ [Flags]
+ public enum MINIDUMP_TYPE : uint
+ {
+ MiniDumpNormal = 0,
+ MiniDumpWithDataSegs = 1 << 0,
+ MiniDumpWithFullMemory = 1 << 1,
+ MiniDumpWithHandleData = 1 << 2,
+ MiniDumpFilterMemory = 1 << 3,
+ MiniDumpScanMemory = 1 << 4,
+ MiniDumpWithUnloadedModules = 1 << 5,
+ MiniDumpWithIndirectlyReferencedMemory = 1 << 6,
+ MiniDumpFilterModulePaths = 1 << 7,
+ MiniDumpWithProcessThreadData = 1 << 8,
+ MiniDumpWithPrivateReadWriteMemory = 1 << 9,
+ MiniDumpWithoutOptionalData = 1 << 10,
+ MiniDumpWithFullMemoryInfo = 1 << 11,
+ MiniDumpWithThreadInfo = 1 << 12,
+ MiniDumpWithCodeSegs = 1 << 13,
+ MiniDumpWithoutAuxiliaryState = 1 << 14,
+ MiniDumpWithFullAuxiliaryState = 1 << 15,
+ MiniDumpWithPrivateWriteCopyMemory = 1 << 16,
+ MiniDumpIgnoreInaccessibleMemory = 1 << 17,
+ MiniDumpWithTokenInformation = 1 << 18,
+ MiniDumpWithModuleHeaders = 1 << 19,
+ MiniDumpFilterTriage = 1 << 20,
+ MiniDumpWithAvxXStateContext = 1 << 21,
+ MiniDumpWithIptTrace = 1 << 22,
+ MiniDumpValidTypeFlags = (-1) ^ ((~1) << 22)
+ }
}
+}
- internal static class DebuggerBreakpoint
+internal enum DumpTypeOption
+{
+ Full,
+ WithHeap,
+ Mini,
+}
+
+internal static class DebuggerBreakpoint
+{
+ internal static void WaitForDebugger(string environmentVariable)
{
- internal static void WaitForDebugger(string environmentVariable)
+ if (string.IsNullOrWhiteSpace(environmentVariable))
{
- if (string.IsNullOrWhiteSpace(environmentVariable))
- {
- throw new ArgumentException($"'{nameof(environmentVariable)}' cannot be null or whitespace.", nameof(environmentVariable));
- }
+ throw new ArgumentException($"'{nameof(environmentVariable)}' cannot be null or whitespace.", nameof(environmentVariable));
+ }
- var debugEnabled = Environment.GetEnvironmentVariable(environmentVariable);
- if (!string.IsNullOrEmpty(debugEnabled) && debugEnabled.Equals("1", StringComparison.Ordinal))
- {
- Console.WriteLine("Waiting for debugger attach...");
+ var debugEnabled = Environment.GetEnvironmentVariable(environmentVariable);
+ if (!string.IsNullOrEmpty(debugEnabled) && debugEnabled.Equals("1", StringComparison.Ordinal))
+ {
+ Console.WriteLine("Waiting for debugger attach...");
- var currentProcess = Process.GetCurrentProcess();
- Console.WriteLine("Process Id: {0}, Name: {1}", currentProcess.Id, currentProcess.ProcessName);
+ var currentProcess = Process.GetCurrentProcess();
+ Console.WriteLine("Process Id: {0}, Name: {1}", currentProcess.Id, currentProcess.ProcessName);
- while (!Debugger.IsAttached)
- {
- Thread.Sleep(1000);
- }
-
- Debugger.Break();
+ while (!Debugger.IsAttached)
+ {
+ Thread.Sleep(1000);
}
+
+ Debugger.Break();
}
}
-}
+}
\ No newline at end of file
diff --git a/src/DataCollectors/Microsoft.TestPlatform.Extensions.EventLogCollector/CollectorNameValueConfigurationManager.cs b/src/DataCollectors/Microsoft.TestPlatform.Extensions.EventLogCollector/CollectorNameValueConfigurationManager.cs
index c4a9020f58..6884ea189b 100644
--- a/src/DataCollectors/Microsoft.TestPlatform.Extensions.EventLogCollector/CollectorNameValueConfigurationManager.cs
+++ b/src/DataCollectors/Microsoft.TestPlatform.Extensions.EventLogCollector/CollectorNameValueConfigurationManager.cs
@@ -1,129 +1,127 @@
// 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.TestPlatform.Extensions.EventLogCollector
+namespace Microsoft.TestPlatform.Extensions.EventLogCollector;
+
+using System.Collections.Generic;
+using System.Xml;
+
+using Microsoft.VisualStudio.TestPlatform.ObjectModel;
+
+///
+/// Utility class that collectors can use to read name/value configuration information from
+/// the XML element sent to them
+///
+internal class CollectorNameValueConfigurationManager
{
- using System.Collections.Generic;
- using System.Xml;
- using Microsoft.VisualStudio.TestPlatform.ObjectModel;
+ #region Private constants
+ // Configuration XML constants
+ private const string SettingNameAttributeName = "name";
- ///
- /// Utility class that collectors can use to read name/value configuration information from
- /// the XML element sent to them
- ///
- internal class CollectorNameValueConfigurationManager
- {
- #region Private constants
- // Configuration XML constants
- private const string SettingNameAttributeName = "name";
+ private const string SettingValueAttributeName = "value";
- private const string SettingValueAttributeName = "value";
+ #endregion
- #endregion
+ #region Private fields
- #region Private fields
+ ///
+ /// The name/value pairs loaded from the configuration XML element
+ ///
- ///
- /// The name/value pairs loaded from the configuration XML element
- ///
- private IDictionary nameValuePairs = new Dictionary();
+ #endregion
- #endregion
+ #region Constructor
- #region Constructor
+ ///
+ /// Initializes a new instance of the class.
+ /// Loads the configuration name/value information from the provided XML element into a dictionary
+ ///
+ ///
+ /// XML element containing the configuration
+ ///
+ public CollectorNameValueConfigurationManager(XmlElement configurationElement)
+ {
+ if (configurationElement == null)
+ {
+ // There is no configuration
+ return;
+ }
- ///
- /// Initializes a new instance of the class.
- /// Loads the configuration name/value information from the provided XML element into a dictionary
- ///
- ///
- /// XML element containing the configuration
- ///
- public CollectorNameValueConfigurationManager(XmlElement configurationElement)
+ // Iterate through top-level XML elements within the configuration element and store
+ // name/value information for elements that have name/value attributes.
+ foreach (XmlNode settingNode in configurationElement.ChildNodes)
{
- if (configurationElement == null)
+ // Skip all non-elements
+ if (settingNode is not XmlElement settingElement)
{
- // There is no configuration
- return;
+ continue;
}
- // Iterate through top-level XML elements within the configuration element and store
- // name/value information for elements that have name/value attributes.
- foreach (XmlNode settingNode in configurationElement.ChildNodes)
+ // Get the setting name
+ string settingName = settingElement.GetAttribute(SettingNameAttributeName);
+ if (string.IsNullOrWhiteSpace(settingName))
{
- // Skip all non-elements
- var settingElement = settingNode as XmlElement;
- if (settingElement == null)
+ if (EqtTrace.IsWarningEnabled)
{
- continue;
+ EqtTrace.Warning("Skipping configuration setting due to missing setting name");
}
- // Get the setting name
- string settingName = settingElement.GetAttribute(SettingNameAttributeName);
- if (string.IsNullOrWhiteSpace(settingName))
- {
- if (EqtTrace.IsWarningEnabled)
- {
- EqtTrace.Warning("Skipping configuration setting due to missing setting name");
- }
-
- continue;
- }
+ continue;
+ }
- // Get the setting value
- string settingValue = settingElement.GetAttribute(SettingValueAttributeName);
- if (string.IsNullOrWhiteSpace(settingValue))
+ // Get the setting value
+ string settingValue = settingElement.GetAttribute(SettingValueAttributeName);
+ if (string.IsNullOrWhiteSpace(settingValue))
+ {
+ if (EqtTrace.IsWarningEnabled)
{
- if (EqtTrace.IsWarningEnabled)
- {
- EqtTrace.Warning("Skipping configuration setting '{0}' due to missing value", settingName);
- }
-
- continue;
+ EqtTrace.Warning("Skipping configuration setting '{0}' due to missing value", settingName);
}
- // Save the name/value pair in the dictionary. Note that duplicate settings are
- // overwritten with the last occurrence's value.
- if (this.nameValuePairs.ContainsKey(settingName))
+ continue;
+ }
+
+ // Save the name/value pair in the dictionary. Note that duplicate settings are
+ // overwritten with the last occurrence's value.
+ if (NameValuePairs.ContainsKey(settingName))
+ {
+ if (EqtTrace.IsVerboseEnabled)
{
- if (EqtTrace.IsVerboseEnabled)
- {
- EqtTrace.Verbose(
- "Duplicate configuration setting found for '{0}'. Using the last setting.",
- settingName);
- }
+ EqtTrace.Verbose(
+ "Duplicate configuration setting found for '{0}'. Using the last setting.",
+ settingName);
}
-
- this.nameValuePairs[settingName] = settingValue;
}
+
+ NameValuePairs[settingName] = settingValue;
}
+ }
- #endregion
+ #endregion
- #region Public properties
+ #region Public properties
- internal IDictionary NameValuePairs => this.nameValuePairs;
+ internal IDictionary NameValuePairs { get; } = new Dictionary();
- ///
- /// Gets the value of the setting specified by name, or null if it was not found
- ///
- /// The setting name
- /// The setting value, or null if the setting was not found
- public string this[string name]
+ ///
+ /// Gets the value of the setting specified by name, or null if it was not found
+ ///
+ /// The setting name
+ /// The setting value, or null if the setting was not found
+ public string this[string name]
+ {
+ get
{
- get
+ if (name == null)
{
- if (name == null)
- {
- return null;
- }
-
- this.nameValuePairs.TryGetValue(name, out var settingValue);
- return settingValue;
+ return null;
}
- set => this.nameValuePairs[name] = value;
+ NameValuePairs.TryGetValue(name, out var settingValue);
+ return settingValue;
}
- #endregion
+
+ set => NameValuePairs[name] = value;
}
-}
+ #endregion
+}
\ No newline at end of file
diff --git a/src/DataCollectors/Microsoft.TestPlatform.Extensions.EventLogCollector/EventLogCollectorException.cs b/src/DataCollectors/Microsoft.TestPlatform.Extensions.EventLogCollector/EventLogCollectorException.cs
index 169a7a3442..36f89963d3 100644
--- a/src/DataCollectors/Microsoft.TestPlatform.Extensions.EventLogCollector/EventLogCollectorException.cs
+++ b/src/DataCollectors/Microsoft.TestPlatform.Extensions.EventLogCollector/EventLogCollectorException.cs
@@ -1,23 +1,22 @@
// 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.TestPlatform.Extensions.EventLogCollector
-{
- using System;
+namespace Microsoft.TestPlatform.Extensions.EventLogCollector;
+
+using System;
+///
+/// Private Exception class used for event log exceptions
+///
+internal class EventLogCollectorException : Exception
+{
///
- /// Private Exception class used for event log exceptions
+ /// Initializes a new instance of the class.
///
- internal class EventLogCollectorException : Exception
+ /// the localized exception message
+ /// the inner exception
+ public EventLogCollectorException(string localizedMessage, Exception innerException)
+ : base(localizedMessage, innerException)
{
- ///
- /// Initializes a new instance of the class.
- ///
- /// the localized exception message
- /// the inner exception
- public EventLogCollectorException(string localizedMessage, Exception innerException)
- : base(localizedMessage, innerException)
- {
- }
}
-}
+}
\ No newline at end of file
diff --git a/src/DataCollectors/Microsoft.TestPlatform.Extensions.EventLogCollector/EventLogConstants.cs b/src/DataCollectors/Microsoft.TestPlatform.Extensions.EventLogCollector/EventLogConstants.cs
index 1f74f8c00c..aa686ffeef 100644
--- a/src/DataCollectors/Microsoft.TestPlatform.Extensions.EventLogCollector/EventLogConstants.cs
+++ b/src/DataCollectors/Microsoft.TestPlatform.Extensions.EventLogCollector/EventLogConstants.cs
@@ -1,23 +1,22 @@
// 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.TestPlatform.Extensions.EventLogCollector
+namespace Microsoft.TestPlatform.Extensions.EventLogCollector;
+
+///
+/// Constants used by Event Log Data Collector.
+///
+internal static class EventLogConstants
{
- ///
- /// Constants used by Event Log Data Collector.
- ///
- internal static class EventLogConstants
- {
- // Supported configuration setting names
- public const string SettingEventLogs = "EventLogs";
- public const string SettingEventSources = "EventSources";
- public const string SettingEntryTypes = "EntryTypes";
- public const string SettingMaxEntries = "MaxEventLogEntriesToCollect";
+ // Supported configuration setting names
+ public const string SettingEventLogs = "EventLogs";
+ public const string SettingEventSources = "EventSources";
+ public const string SettingEntryTypes = "EntryTypes";
+ public const string SettingMaxEntries = "MaxEventLogEntriesToCollect";
- // default values
- public const int DefaultMaxEntries = 50000;
+ // default values
+ public const int DefaultMaxEntries = 50000;
- public const int TypeColumnMaxLength = 64;
- public const int SourceColumnMaxLength = 212;
- }
+ public const int TypeColumnMaxLength = 64;
+ public const int SourceColumnMaxLength = 212;
}
\ No newline at end of file
diff --git a/src/DataCollectors/Microsoft.TestPlatform.Extensions.EventLogCollector/EventLogContainer.cs b/src/DataCollectors/Microsoft.TestPlatform.Extensions.EventLogCollector/EventLogContainer.cs
index b8a98bc493..c7d6d28ef9 100644
--- a/src/DataCollectors/Microsoft.TestPlatform.Extensions.EventLogCollector/EventLogContainer.cs
+++ b/src/DataCollectors/Microsoft.TestPlatform.Extensions.EventLogCollector/EventLogContainer.cs
@@ -1,271 +1,251 @@
// 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.TestPlatform.Extensions.EventLogCollector
-{
- using System;
- using System.Collections.Generic;
- using System.Diagnostics;
- using System.Globalization;
-
- using Microsoft.VisualStudio.TestPlatform.ObjectModel;
- using Microsoft.VisualStudio.TestPlatform.ObjectModel.DataCollection;
-
- using Resource = Microsoft.TestPlatform.Extensions.EventLogCollector.Resources.Resources;
-
- ///
- /// The event log container.
- ///
- internal class EventLogContainer : IEventLogContainer
- {
- private ISet eventSources;
-
- private ISet entryTypes;
-
- private EventLog eventLog;
-
- private int nextEntryIndexToCollect;
-
- private int maxLogEntries;
+namespace Microsoft.TestPlatform.Extensions.EventLogCollector;
- private DataCollectionLogger dataCollectionLogger;
+using System;
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.Globalization;
- private DataCollectionContext dataCollectionContext;
+using Microsoft.VisualStudio.TestPlatform.ObjectModel;
+using Microsoft.VisualStudio.TestPlatform.ObjectModel.DataCollection;
- private bool limitReached;
+using Resource = Resources.Resources;
- private List eventLogEntries;
+///
+/// The event log container.
+///
+internal class EventLogContainer : IEventLogContainer
+{
+ private readonly ISet _eventSources;
- ///
- /// Keeps track of if we are disposed.
- ///
- private bool isDisposed;
+ private readonly ISet _entryTypes;
+ private readonly int _maxLogEntries;
- ///
- /// Initializes a new instance of the class.
- ///
- ///
- /// Event Log Name for which logs has to be collected.
- ///
- ///
- /// The event Sources.
- ///
- ///
- /// The entry Types.
- ///
- ///
- /// Max entries to store
- ///
- ///
- /// Data Collection Logger
- ///
- ///
- /// Data Collection Context
- ///
- public EventLogContainer(string eventLogName, ISet eventSources, ISet entryTypes, int maxLogEntries, DataCollectionLogger dataCollectionLogger, DataCollectionContext dataCollectionContext)
- {
- this.CreateEventLog(eventLogName);
- this.eventSources = eventSources;
- this.entryTypes = entryTypes;
- this.maxLogEntries = maxLogEntries;
- this.dataCollectionLogger = dataCollectionLogger;
- this.dataCollectionContext = dataCollectionContext;
+ private readonly DataCollectionLogger _dataCollectionLogger;
- this.eventLogEntries = new List();
- }
+ private readonly DataCollectionContext _dataCollectionContext;
- ///
- public List EventLogEntries => this.eventLogEntries;
+ ///
+ /// Keeps track of if we are disposed.
+ ///
+ private bool _isDisposed;
- ///
- public EventLog EventLog => this.eventLog;
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ ///
+ /// Event Log Name for which logs has to be collected.
+ ///
+ ///
+ /// The event Sources.
+ ///
+ ///
+ /// The entry Types.
+ ///
+ ///
+ /// Max entries to store
+ ///
+ ///
+ /// Data Collection Logger
+ ///
+ ///
+ /// Data Collection Context
+ ///
+ public EventLogContainer(string eventLogName, ISet eventSources, ISet entryTypes, int maxLogEntries, DataCollectionLogger dataCollectionLogger, DataCollectionContext dataCollectionContext)
+ {
+ CreateEventLog(eventLogName);
+ _eventSources = eventSources;
+ _entryTypes = entryTypes;
+ _maxLogEntries = maxLogEntries;
+ _dataCollectionLogger = dataCollectionLogger;
+ _dataCollectionContext = dataCollectionContext;
+
+ EventLogEntries = new List();
+ }
- internal int NextEntryIndexToCollect
- {
- get => this.nextEntryIndexToCollect;
+ ///
+ public List EventLogEntries { get; }
- set => this.nextEntryIndexToCollect = value;
- }
+ ///
+ public EventLog EventLog { get; private set; }
- ///
- /// Gets or sets a value indicating whether limit reached.
- ///
- internal bool LimitReached
- {
- get => this.limitReached;
+ internal int NextEntryIndexToCollect { get; set; }
- set => this.limitReached = value;
- }
+ ///
+ /// Gets or sets a value indicating whether limit reached.
+ ///
+ internal bool LimitReached { get; set; }
- public void Dispose()
- {
- this.Dispose(true);
+ public void Dispose()
+ {
+ Dispose(true);
- // Use SupressFinalize in case a subclass
- // of this type implements a finalizer.
- GC.SuppressFinalize(this);
- }
+ // Use SupressFinalize in case a subclass
+ // of this type implements a finalizer.
+ GC.SuppressFinalize(this);
+ }
- ///
- /// This is the event handler for the EntryWritten event of the System.Diagnostics.EventLog class.
- /// Note that the documentation for the EntryWritten event includes these remarks:
- /// "The system responds to WriteEntry only if the last write event occurred at least five seconds previously.
- /// This implies you will only receive one EntryWritten event notification within a five-second interval, even if more
- /// than one event log change occurs. If you insert a sufficiently long sleep interval (around 10 seconds) between calls
- /// to WriteEntry, no events will be lost. However, if write events occur more frequently, the most recent write events
- /// could be lost."
- /// This complicates this data collector because we don't want to sleep to wait for all events or lose the most recent events.
- /// To workaround, the implementation does several things:
- /// 1. We get the EventLog entries to collect from the EventLog.Entries collection and ignore the EntryWrittenEventArgs.
- /// 2. When event log collection ends for a data collection context, this method is called explicitly by the EventLogDataCollector
- /// passing null for EntryWrittenEventArgs (which is fine since the argument is ignored.
- /// 3. We keep track of which EventLogEntry object in the EventLog.Entries we still need to collect. We do this by inspecting
- /// the value of the EventLogEntry.Index property. The value of this property is an integer that is incremented for each entry
- /// that is written to the event log, but is reset to 0 if the entire event log is cleared.
- /// Another behavior of event logs that we need to account for is that if the event log reaches a size limit, older events are
- /// automatically deleted. In this case the collection EventLog.Entries contains only the entries remaining in the log,
- /// and the value of the EventLog.Entries[0].Index will not be 0; it will be the index of the oldest entry still in the log.
- /// For example, if the first 1000 entries written to an event log (since it was last completely cleared) are deleted because
- /// of the size limitation, then EventLog.Entries[0].Index would have a value of 1000 (this value is saved in the local variable
- /// "firstIndexInLog" in the method implementation. Similarly "mostRecentIndexInLog" is the index of the last entry written
- /// to the log at the time we examine it.
- ///
- /// Source
- /// The System.Diagnostics.EntryWrittenEventArgs object describing the entry that was written.
- public void OnEventLogEntryWritten(object source, EntryWrittenEventArgs e)
+ ///
+ /// This is the event handler for the EntryWritten event of the System.Diagnostics.EventLog class.
+ /// Note that the documentation for the EntryWritten event includes these remarks:
+ /// "The system responds to WriteEntry only if the last write event occurred at least five seconds previously.
+ /// This implies you will only receive one EntryWritten event notification within a five-second interval, even if more
+ /// than one event log change occurs. If you insert a sufficiently long sleep interval (around 10 seconds) between calls
+ /// to WriteEntry, no events will be lost. However, if write events occur more frequently, the most recent write events
+ /// could be lost."
+ /// This complicates this data collector because we don't want to sleep to wait for all events or lose the most recent events.
+ /// To workaround, the implementation does several things:
+ /// 1. We get the EventLog entries to collect from the EventLog.Entries collection and ignore the EntryWrittenEventArgs.
+ /// 2. When event log collection ends for a data collection context, this method is called explicitly by the EventLogDataCollector
+ /// passing null for EntryWrittenEventArgs (which is fine since the argument is ignored.
+ /// 3. We keep track of which EventLogEntry object in the EventLog.Entries we still need to collect. We do this by inspecting
+ /// the value of the EventLogEntry.Index property. The value of this property is an integer that is incremented for each entry
+ /// that is written to the event log, but is reset to 0 if the entire event log is cleared.
+ /// Another behavior of event logs that we need to account for is that if the event log reaches a size limit, older events are
+ /// automatically deleted. In this case the collection EventLog.Entries contains only the entries remaining in the log,
+ /// and the value of the EventLog.Entries[0].Index will not be 0; it will be the index of the oldest entry still in the log.
+ /// For example, if the first 1000 entries written to an event log (since it was last completely cleared) are deleted because
+ /// of the size limitation, then EventLog.Entries[0].Index would have a value of 1000 (this value is saved in the local variable
+ /// "firstIndexInLog" in the method implementation. Similarly "mostRecentIndexInLog" is the index of the last entry written
+ /// to the log at the time we examine it.
+ ///
+ /// Source
+ /// The System.Diagnostics.EntryWrittenEventArgs object describing the entry that was written.
+ public void OnEventLogEntryWritten(object source, EntryWrittenEventArgs e)
+ {
+ while (!LimitReached)
{
- while (!this.limitReached)
+ try
{
- try
+ lock (EventLogEntries)
{
- lock (this.eventLogEntries)
+ int currentCount = EventLog.Entries.Count;
+ if (currentCount == 0)
{
- int currentCount = this.eventLog.Entries.Count;
- if (currentCount == 0)
- {
- break;
- }
+ break;
+ }
- int firstIndexInLog = this.eventLog.Entries[0].Index;
- int mostRecentIndexInLog = this.eventLog.Entries[currentCount - 1].Index;
+ int firstIndexInLog = EventLog.Entries[0].Index;
+ int mostRecentIndexInLog = EventLog.Entries[currentCount - 1].Index;
- if (mostRecentIndexInLog == this.nextEntryIndexToCollect - 1)
- {
- // We've already collected the most recent entry in the log
- break;
- }
+ if (mostRecentIndexInLog == NextEntryIndexToCollect - 1)
+ {
+ // We've already collected the most recent entry in the log
+ break;
+ }
- if (mostRecentIndexInLog < this.nextEntryIndexToCollect - 1)
+ if (mostRecentIndexInLog < NextEntryIndexToCollect - 1)
+ {
+ if (EqtTrace.IsWarningEnabled)
{
- if (EqtTrace.IsWarningEnabled)
- {
- EqtTrace.Warning(
- string.Format(
- CultureInfo.InvariantCulture,
- "EventLogDataContainer: OnEventLogEntryWritten: Handling clearing of log (mostRecentIndexInLog < eventLogContainer.NextEntryIndex): firstIndexInLog: {0}:, mostRecentIndexInLog: {1}, NextEntryIndex: {2}",
- firstIndexInLog,
- mostRecentIndexInLog,
- this.nextEntryIndexToCollect));
- }
-
- // Send warning; event log must have been cleared.
- this.dataCollectionLogger.LogWarning(
- this.dataCollectionContext,
+ EqtTrace.Warning(
string.Format(
CultureInfo.InvariantCulture,
- Resource.EventsLostWarning,
- this.eventLog.Log));
-
- this.nextEntryIndexToCollect = 0;
- firstIndexInLog = 0;
+ "EventLogDataContainer: OnEventLogEntryWritten: Handling clearing of log (mostRecentIndexInLog < eventLogContainer.NextEntryIndex): firstIndexInLog: {0}:, mostRecentIndexInLog: {1}, NextEntryIndex: {2}",
+ firstIndexInLog,
+ mostRecentIndexInLog,
+ NextEntryIndexToCollect));
}
- for (;
- this.nextEntryIndexToCollect <= mostRecentIndexInLog;
- this.nextEntryIndexToCollect++)
- {
- int nextEntryIndexInCurrentLog = this.nextEntryIndexToCollect - firstIndexInLog;
- EventLogEntry nextEntry = this.eventLog.Entries[nextEntryIndexInCurrentLog];
+ // Send warning; event log must have been cleared.
+ _dataCollectionLogger.LogWarning(
+ _dataCollectionContext,
+ string.Format(
+ CultureInfo.InvariantCulture,
+ Resource.EventsLostWarning,
+ EventLog.Log));
- // If an explicit list of event sources was provided, only report log entries from those sources
- if (this.eventSources != null && this.eventSources.Count > 0)
- {
- if (!this.eventSources.Contains(nextEntry.Source))
- {
- continue;
- }
- }
+ NextEntryIndexToCollect = 0;
+ firstIndexInLog = 0;
+ }
- if (!this.entryTypes.Contains(nextEntry.EntryType))
+ for (;
+ NextEntryIndexToCollect <= mostRecentIndexInLog;
+ NextEntryIndexToCollect++)
+ {
+ int nextEntryIndexInCurrentLog = NextEntryIndexToCollect - firstIndexInLog;
+ EventLogEntry nextEntry = EventLog.Entries[nextEntryIndexInCurrentLog];
+
+ // If an explicit list of event sources was provided, only report log entries from those sources
+ if (_eventSources != null && _eventSources.Count > 0)
+ {
+ if (!_eventSources.Contains(nextEntry.Source))
{
continue;
}
+ }
- if (this.eventLogEntries.Count < this.maxLogEntries)
- {
- this.eventLogEntries.Add(nextEntry);
+ if (!_entryTypes.Contains(nextEntry.EntryType))
+ {
+ continue;
+ }
- if (EqtTrace.IsVerboseEnabled)
- {
- EqtTrace.Verbose(
- string.Format(
- CultureInfo.InvariantCulture,
- "EventLogDataContainer.OnEventLogEntryWritten() add event with Id {0} from position {1} in the current {2} log",
- nextEntry.Index,
- nextEntryIndexInCurrentLog,
- this.eventLog.Log));
- }
- }
- else
+ if (EventLogEntries.Count < _maxLogEntries)
+ {
+ EventLogEntries.Add(nextEntry);
+
+ if (EqtTrace.IsVerboseEnabled)
{
- this.LimitReached = true;
- break;
+ EqtTrace.Verbose(
+ string.Format(
+ CultureInfo.InvariantCulture,
+ "EventLogDataContainer.OnEventLogEntryWritten() add event with Id {0} from position {1} in the current {2} log",
+ nextEntry.Index,
+ nextEntryIndexInCurrentLog,
+ EventLog.Log));
}
}
+ else
+ {
+ LimitReached = true;
+ break;
+ }
}
}
- catch (Exception exception)
- {
- this.dataCollectionLogger.LogError(
- this.dataCollectionContext,
- string.Format(
- CultureInfo.InvariantCulture,
- Resource.EventsLostError,
- this.eventLog.Log,
- exception), exception);
- }
+ }
+ catch (Exception exception)
+ {
+ _dataCollectionLogger.LogError(
+ _dataCollectionContext,
+ string.Format(
+ CultureInfo.InvariantCulture,
+ Resource.EventsLostError,
+ EventLog.Log,
+ exception), exception);
}
}
+ }
- ///
- /// The dispose.
- ///
- ///
- /// The disposing.
- ///
- protected virtual void Dispose(bool disposing)
+ ///
+ /// The dispose.
+ ///
+ ///
+ /// The disposing.
+ ///
+ protected virtual void Dispose(bool disposing)
+ {
+ if (!_isDisposed)
{
- if (!this.isDisposed)
+ if (disposing)
{
- if (disposing)
- {
- this.eventLog.EnableRaisingEvents = false;
- this.eventLog.EntryWritten -= this.OnEventLogEntryWritten;
- this.eventLog.Dispose();
- }
-
- this.isDisposed = true;
+ EventLog.EnableRaisingEvents = false;
+ EventLog.EntryWritten -= OnEventLogEntryWritten;
+ EventLog.Dispose();
}
- }
- private void CreateEventLog(string eventLogName)
- {
- this.eventLog = new EventLog(eventLogName);
- this.eventLog.EnableRaisingEvents = true;
- this.eventLog.EntryWritten += this.OnEventLogEntryWritten;
- int currentCount = this.eventLog.Entries.Count;
- this.nextEntryIndexToCollect =
- (currentCount == 0) ? 0 : this.eventLog.Entries[currentCount - 1].Index + 1;
+ _isDisposed = true;
}
}
-}
+
+ private void CreateEventLog(string eventLogName)
+ {
+ EventLog = new EventLog(eventLogName);
+ EventLog.EnableRaisingEvents = true;
+ EventLog.EntryWritten += OnEventLogEntryWritten;
+ int currentCount = EventLog.Entries.Count;
+ NextEntryIndexToCollect =
+ (currentCount == 0) ? 0 : EventLog.Entries[currentCount - 1].Index + 1;
+ }
+}
\ No newline at end of file
diff --git a/src/DataCollectors/Microsoft.TestPlatform.Extensions.EventLogCollector/EventLogDataCollector.cs b/src/DataCollectors/Microsoft.TestPlatform.Extensions.EventLogCollector/EventLogDataCollector.cs
index 3b5639ea54..f7b02c99fd 100644
--- a/src/DataCollectors/Microsoft.TestPlatform.Extensions.EventLogCollector/EventLogDataCollector.cs
+++ b/src/DataCollectors/Microsoft.TestPlatform.Extensions.EventLogCollector/EventLogDataCollector.cs
@@ -1,681 +1,634 @@
// 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.TestPlatform.Extensions.EventLogCollector
+namespace Microsoft.TestPlatform.Extensions.EventLogCollector;
+
+using System;
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.Globalization;
+using System.IO;
+using System.Linq;
+using System.Xml;
+
+using Microsoft.VisualStudio.TestPlatform.ObjectModel;
+using Microsoft.VisualStudio.TestPlatform.ObjectModel.DataCollection;
+using Microsoft.VisualStudio.TestPlatform.Utilities.Helpers;
+using Microsoft.VisualStudio.TestPlatform.Utilities.Helpers.Interfaces;
+
+using Resource = Resources.Resources;
+
+///
+/// A data collector that collects event log data
+///
+[DataCollectorTypeUri(DefaultUri)]
+[DataCollectorFriendlyName("Event Log")]
+public class EventLogDataCollector : DataCollector
{
- using System;
- using System.Collections.Generic;
- using System.Diagnostics;
- using System.Globalization;
- using System.IO;
- using System.Linq;
- using System.Xml;
+ #region Constants
- using Microsoft.VisualStudio.TestPlatform.ObjectModel;
- using Microsoft.VisualStudio.TestPlatform.ObjectModel.DataCollection;
- using Microsoft.VisualStudio.TestPlatform.Utilities.Helpers;
- using Microsoft.VisualStudio.TestPlatform.Utilities.Helpers.Interfaces;
+ ///
+ /// The event log file name.
+ ///
+ private const string EventLogFileName = "Event Log";
+
+ ///
+ /// DataCollector URI.
+ ///
+ private const string DefaultUri = @"datacollector://Microsoft/EventLog/2.0";
+
+ #endregion
- using Resource = Resources.Resources;
+ #region Private fields
///
- /// A data collector that collects event log data
+ /// Event handler delegate for the SessionStart event
///
- [DataCollectorTypeUri(DefaultUri)]
- [DataCollectorFriendlyName("Event Log")]
- public class EventLogDataCollector : DataCollector
- {
- #region Constants
-
- ///
- /// The event log file name.
- ///
- private const string EventLogFileName = "Event Log";
-
- ///
- /// DataCollector URI.
- ///
- private const string DefaultUri = @"datacollector://Microsoft/EventLog/2.0";
-
- #endregion
-
- #region Private fields
-
- ///
- /// Event handler delegate for the SessionStart event
- ///
- private readonly EventHandler sessionStartEventHandler;
-
- ///
- /// Event handler delegate for the SessionEnd event
- ///
- private readonly EventHandler sessionEndEventHandler;
-
- ///
- /// Event handler delegate for the TestCaseStart event
- ///
- private readonly EventHandler testCaseStartEventHandler;
-
- ///
- /// Event handler delegate for the TestCaseEnd event
- ///
- private readonly EventHandler testCaseEndEventHandler;
-
- ///
- /// The event log directories.
- ///
- private readonly List eventLogDirectories;
-
- ///
- /// Object containing the execution events the data collector registers for
- ///
- private DataCollectionEvents events;
-
- ///
- /// The sink used by the data collector to send its data
- ///
- private DataCollectionSink dataSink;
-
- ///
- /// The data collector context.
- ///
- private DataCollectionContext dataCollectorContext;
-
- ///
- /// Used by the data collector to send warnings, errors, or other messages
- ///
- private DataCollectionLogger logger;
-
- ///
- /// The event log names.
- ///
- private ISet eventLogNames;
-
- ///
- /// The event sources.
- ///
- private ISet eventSources;
-
- ///
- /// The entry types.
- ///
- private ISet entryTypes;
-
- ///
- /// The max entries.
- ///
- private int maxEntries;
-
- ///
- /// The file helper.
- ///
- private IFileHelper fileHelper;
-
- ///
- /// The event log map.
- ///
- private IDictionary eventLogContainerMap = new Dictionary();
-
- #endregion
-
- #region Constructor
-
- ///
- /// Initializes a new instance of the class.
- ///
- public EventLogDataCollector()
- : this(new FileHelper())
- {
- }
+ private readonly EventHandler _sessionStartEventHandler;
- ///
- /// Initializes a new instance of the class.
- ///
- ///
- /// File Helper.
- ///
- internal EventLogDataCollector(IFileHelper fileHelper)
- {
- this.sessionStartEventHandler = this.OnSessionStart;
- this.sessionEndEventHandler = this.OnSessionEnd;
- this.testCaseStartEventHandler = this.OnTestCaseStart;
- this.testCaseEndEventHandler = this.OnTestCaseEnd;
-
- this.eventLogDirectories = new List();
- this.ContextMap = new Dictionary();
- this.fileHelper = fileHelper;
- }
+ ///
+ /// Event handler delegate for the SessionEnd event
+ ///
+ private readonly EventHandler _sessionEndEventHandler;
- #endregion
+ ///
+ /// Event handler delegate for the TestCaseStart event
+ ///
+ private readonly EventHandler _testCaseStartEventHandler;
- #region Internal Fields
+ ///
+ /// Event handler delegate for the TestCaseEnd event
+ ///
+ private readonly EventHandler _testCaseEndEventHandler;
- internal int MaxEntries
- {
- get
- {
- return this.maxEntries;
- }
- }
+ ///
+ /// The event log directories.
+ ///
+ private readonly List _eventLogDirectories;
- internal ISet EventSources
- {
- get
- {
- return this.eventSources;
- }
- }
+ ///
+ /// Object containing the execution events the data collector registers for
+ ///
+ private DataCollectionEvents _events;
- internal ISet EntryTypes
- {
- get
- {
- return this.entryTypes;
- }
- }
+ ///
+ /// The sink used by the data collector to send its data
+ ///
+ private DataCollectionSink _dataSink;
- internal ISet EventLogNames
- {
- get
- {
- return this.eventLogNames;
- }
- }
+ ///
+ /// The data collector context.
+ ///
+ private DataCollectionContext _dataCollectorContext;
- ///
- /// Gets the context data.
- ///
- internal Dictionary ContextMap { get; private set; }
-
- #endregion
-
- #region DataCollector Members
-
- ///
- /// Initializes the data collector
- ///
- ///
- /// The XML element containing configuration information for the data collector. Currently,
- /// this data collector does not have any configuration, so we ignore this parameter.
- ///
- ///
- /// Object containing the execution events the data collector registers for
- ///
- /// The sink used by the data collector to send its data
- ///
- /// Used by the data collector to send warnings, errors, or other messages
- ///
- /// Provides contextual information about the agent environment
- public override void Initialize(
- XmlElement configurationElement,
- DataCollectionEvents events,
- DataCollectionSink dataSink,
- DataCollectionLogger logger,
- DataCollectionEnvironmentContext dataCollectionEnvironmentContext)
- {
- ValidateArg.NotNull(events, nameof(events));
- ValidateArg.NotNull(dataSink, nameof(dataSink));
- ValidateArg.NotNull(logger, nameof(logger));
-
- this.events = events;
- this.dataSink = dataSink;
- this.logger = logger;
- this.dataCollectorContext = dataCollectionEnvironmentContext.SessionDataCollectionContext;
-
- // Load the configuration
- CollectorNameValueConfigurationManager nameValueSettings =
- new CollectorNameValueConfigurationManager(configurationElement);
-
- // Apply the configuration
- this.ConfigureEventSources(nameValueSettings);
- this.ConfigureEntryTypes(nameValueSettings);
- this.ConfigureMaxEntries(nameValueSettings);
- this.ConfigureEventLogNames(nameValueSettings);
-
- // Register for events
- events.SessionStart += this.sessionStartEventHandler;
- events.SessionEnd += this.sessionEndEventHandler;
- events.TestCaseStart += this.testCaseStartEventHandler;
- events.TestCaseEnd += this.testCaseEndEventHandler;
- }
+ ///
+ /// Used by the data collector to send warnings, errors, or other messages
+ ///
+ private DataCollectionLogger _logger;
- #endregion
-
- #region Internal
-
- ///
- /// The write event logs.
- ///
- ///
- /// The event log entries.
- ///
- ///
- /// Max Log Entries.
- ///
- ///
- /// The data collection context.
- ///
- ///
- /// The requested duration.
- ///
- ///
- /// The time request received.
- ///
- ///
- /// The .
- ///
- internal string WriteEventLogs(List eventLogEntries, int maxLogEntries, DataCollectionContext dataCollectionContext, TimeSpan requestedDuration, DateTime timeRequestReceived)
- {
- // Generate a unique but friendly Directory name in the temp directory
- string eventLogDirName = string.Format(
- CultureInfo.InvariantCulture,
- "{0}-{1}-{2:yyyy}{2:MM}{2:dd}-{2:HH}{2:mm}{2:ss}.{2:fff}",
- "Event Log",
- Environment.MachineName,
- DateTime.UtcNow);
+ ///
+ /// The file helper.
+ ///
+ private readonly IFileHelper _fileHelper;
- string eventLogDirPath = Path.Combine(Path.GetTempPath(), eventLogDirName);
+ ///
+ /// The event log map.
+ ///
+ private readonly IDictionary _eventLogContainerMap = new Dictionary();
- // Create the directory
- this.fileHelper.CreateDirectory(eventLogDirPath);
+ #endregion
- string eventLogBasePath = Path.Combine(eventLogDirPath, EventLogFileName);
- bool unusedFilenameFound = false;
+ #region Constructor
- string eventLogPath = eventLogBasePath + ".xml";
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ public EventLogDataCollector()
+ : this(new FileHelper())
+ {
+ }
- if (this.fileHelper.Exists(eventLogPath))
- {
- for (int i = 1; !unusedFilenameFound; i++)
- {
- eventLogPath = eventLogBasePath + "-" + i.ToString(CultureInfo.InvariantCulture) + ".xml";
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ ///
+ /// File Helper.
+ ///
+ internal EventLogDataCollector(IFileHelper fileHelper)
+ {
+ _sessionStartEventHandler = OnSessionStart;
+ _sessionEndEventHandler = OnSessionEnd;
+ _testCaseStartEventHandler = OnTestCaseStart;
+ _testCaseEndEventHandler = OnTestCaseEnd;
+
+ _eventLogDirectories = new List();
+ ContextMap = new Dictionary();
+ _fileHelper = fileHelper;
+ }
- if (!this.fileHelper.Exists(eventLogPath))
- {
- unusedFilenameFound = true;
- }
- }
- }
+ #endregion
- DateTime minDate = DateTime.MinValue;
+ #region Internal Fields
- // Limit entries to a certain time range if requested
- if (requestedDuration < TimeSpan.MaxValue)
- {
- try
- {
- minDate = timeRequestReceived - requestedDuration;
- }
- catch (ArgumentOutOfRangeException)
- {
- minDate = DateTime.MinValue;
- }
- }
+ internal int MaxEntries { get; private set; }
- Stopwatch stopwatch = new Stopwatch();
- stopwatch.Start();
- EventLogXmlWriter.WriteEventLogEntriesToXmlFile(
- eventLogPath,
- eventLogEntries.Where(
- entry => entry.TimeGenerated > minDate && entry.TimeGenerated < DateTime.MaxValue).OrderBy(x => x.TimeGenerated).Take(maxLogEntries).ToList(),
- this.fileHelper);
+ internal ISet EventSources { get; private set; }
- stopwatch.Stop();
+ internal ISet EntryTypes { get; private set; }
- if (EqtTrace.IsVerboseEnabled)
- {
- EqtTrace.Verbose(
- string.Format(
- CultureInfo.InvariantCulture,
- "EventLogDataContainer: Wrote {0} event log entries to file '{1}' in {2} seconds",
- eventLogEntries.Count,
- eventLogPath,
- stopwatch.Elapsed.TotalSeconds.ToString(CultureInfo.InvariantCulture)));
- }
+ internal ISet EventLogNames { get; private set; }
- // Write the event log file
- FileTransferInformation fileTransferInformation =
- new FileTransferInformation(dataCollectionContext, eventLogPath, true, this.fileHelper);
- this.dataSink.SendFileAsync(fileTransferInformation);
+ ///
+ /// Gets the context data.
+ ///
+ internal Dictionary ContextMap { get; private set; }
- if (EqtTrace.IsVerboseEnabled)
- {
- EqtTrace.Verbose(
- "EventLogDataContainer: Event log successfully sent for data collection context '{0}'.",
- dataCollectionContext.ToString());
- }
+ #endregion
- return eventLogPath;
- }
- #endregion
+ #region DataCollector Members
- #region IDisposable Members
+ ///
+ /// Initializes the data collector
+ ///
+ ///
+ /// The XML element containing configuration information for the data collector. Currently,
+ /// this data collector does not have any configuration, so we ignore this parameter.
+ ///
+ ///
+ /// Object containing the execution events the data collector registers for
+ ///
+ /// The sink used by the data collector to send its data
+ ///
+ /// Used by the data collector to send warnings, errors, or other messages
+ ///
+ /// Provides contextual information about the agent environment
+ public override void Initialize(
+ XmlElement configurationElement,
+ DataCollectionEvents events,
+ DataCollectionSink dataSink,
+ DataCollectionLogger logger,
+ DataCollectionEnvironmentContext dataCollectionEnvironmentContext)
+ {
+ ValidateArg.NotNull(events, nameof(events));
+ ValidateArg.NotNull(dataSink, nameof(dataSink));
+ ValidateArg.NotNull(logger, nameof(logger));
+
+ _events = events;
+ _dataSink = dataSink;
+ _logger = logger;
+ _dataCollectorContext = dataCollectionEnvironmentContext.SessionDataCollectionContext;
+
+ // Load the configuration
+ CollectorNameValueConfigurationManager nameValueSettings =
+ new(configurationElement);
+
+ // Apply the configuration
+ ConfigureEventSources(nameValueSettings);
+ ConfigureEntryTypes(nameValueSettings);
+ ConfigureMaxEntries(nameValueSettings);
+ ConfigureEventLogNames(nameValueSettings);
+
+ // Register for events
+ events.SessionStart += _sessionStartEventHandler;
+ events.SessionEnd += _sessionEndEventHandler;
+ events.TestCaseStart += _testCaseStartEventHandler;
+ events.TestCaseEnd += _testCaseEndEventHandler;
+ }
+
+ #endregion
- ///
- /// Cleans up resources allocated by the data collector
- ///
- /// Not used since this class does not have a finalizer.
- protected override void Dispose(bool disposing)
+ #region Internal
+
+ ///
+ /// The write event logs.
+ ///
+ ///
+ /// The event log entries.
+ ///
+ ///
+ /// Max Log Entries.
+ ///
+ ///
+ /// The data collection context.
+ ///
+ ///
+ /// The requested duration.
+ ///
+ ///
+ /// The time request received.
+ ///
+ ///
+ /// The .
+ ///
+ internal string WriteEventLogs(List eventLogEntries, int maxLogEntries, DataCollectionContext dataCollectionContext, TimeSpan requestedDuration, DateTime timeRequestReceived)
+ {
+ // Generate a unique but friendly Directory name in the temp directory
+ string eventLogDirName = string.Format(
+ CultureInfo.InvariantCulture,
+ "{0}-{1}-{2:yyyy}{2:MM}{2:dd}-{2:HH}{2:mm}{2:ss}.{2:fff}",
+ "Event Log",
+ Environment.MachineName,
+ DateTime.UtcNow);
+
+ string eventLogDirPath = Path.Combine(Path.GetTempPath(), eventLogDirName);
+
+ // Create the directory
+ _fileHelper.CreateDirectory(eventLogDirPath);
+
+ string eventLogBasePath = Path.Combine(eventLogDirPath, EventLogFileName);
+ bool unusedFilenameFound = false;
+
+ string eventLogPath = eventLogBasePath + ".xml";
+
+ if (_fileHelper.Exists(eventLogPath))
{
- // Unregister events
- this.events.SessionStart -= this.sessionStartEventHandler;
- this.events.SessionEnd -= this.sessionEndEventHandler;
- this.events.TestCaseStart -= this.testCaseStartEventHandler;
- this.events.TestCaseEnd -= this.testCaseEndEventHandler;
-
- // Unregister EventLogEntry Written.
- foreach (var eventLogContainer in this.eventLogContainerMap.Values)
+ for (int i = 1; !unusedFilenameFound; i++)
{
- eventLogContainer.Dispose();
- }
+ eventLogPath = eventLogBasePath + "-" + i.ToString(CultureInfo.InvariantCulture) + ".xml";
- // Delete all the temp event log directories
- this.RemoveTempEventLogDirs(this.eventLogDirectories);
+ if (!_fileHelper.Exists(eventLogPath))
+ {
+ unusedFilenameFound = true;
+ }
+ }
}
- #endregion
+ DateTime minDate = DateTime.MinValue;
- private static ISet ParseCommaSeparatedList(string commaSeparatedList)
+ // Limit entries to a certain time range if requested
+ if (requestedDuration < TimeSpan.MaxValue)
{
- ISet strings = new HashSet();
- string[] items = commaSeparatedList.Split(new char[] { ',' });
- foreach (string item in items)
+ try
+ {
+ minDate = timeRequestReceived - requestedDuration;
+ }
+ catch (ArgumentOutOfRangeException)
{
- strings.Add(item.Trim());
+ minDate = DateTime.MinValue;
}
+ }
+
+ Stopwatch stopwatch = new();
+ stopwatch.Start();
+ EventLogXmlWriter.WriteEventLogEntriesToXmlFile(
+ eventLogPath,
+ eventLogEntries.Where(
+ entry => entry.TimeGenerated > minDate && entry.TimeGenerated < DateTime.MaxValue).OrderBy(x => x.TimeGenerated).Take(maxLogEntries).ToList(),
+ _fileHelper);
+
+ stopwatch.Stop();
- return strings;
+ if (EqtTrace.IsVerboseEnabled)
+ {
+ EqtTrace.Verbose(
+ string.Format(
+ CultureInfo.InvariantCulture,
+ "EventLogDataContainer: Wrote {0} event log entries to file '{1}' in {2} seconds",
+ eventLogEntries.Count,
+ eventLogPath,
+ stopwatch.Elapsed.TotalSeconds.ToString(CultureInfo.InvariantCulture)));
}
- #region Event Handlers
+ // Write the event log file
+ FileTransferInformation fileTransferInformation =
+ new(dataCollectionContext, eventLogPath, true, _fileHelper);
+ _dataSink.SendFileAsync(fileTransferInformation);
- private void OnSessionStart(object sender, SessionStartEventArgs e)
+ if (EqtTrace.IsVerboseEnabled)
{
- ValidateArg.NotNull(e, "SessionStartEventArgs");
- ValidateArg.NotNull(e.Context, "SessionStartEventArgs.Context");
+ EqtTrace.Verbose(
+ "EventLogDataContainer: Event log successfully sent for data collection context '{0}'.",
+ dataCollectionContext.ToString());
+ }
- if (EqtTrace.IsVerboseEnabled)
- {
- EqtTrace.Verbose("EventLogDataCollector: SessionStart received");
- }
+ return eventLogPath;
+ }
+ #endregion
- this.StartCollectionForContext(e.Context, true);
+ #region IDisposable Members
+
+ ///
+ /// Cleans up resources allocated by the data collector
+ ///
+ /// Not used since this class does not have a finalizer.
+ protected override void Dispose(bool disposing)
+ {
+ // Unregister events
+ _events.SessionStart -= _sessionStartEventHandler;
+ _events.SessionEnd -= _sessionEndEventHandler;
+ _events.TestCaseStart -= _testCaseStartEventHandler;
+ _events.TestCaseEnd -= _testCaseEndEventHandler;
+
+ // Unregister EventLogEntry Written.
+ foreach (var eventLogContainer in _eventLogContainerMap.Values)
+ {
+ eventLogContainer.Dispose();
}
- private void OnSessionEnd(object sender, SessionEndEventArgs e)
+ // Delete all the temp event log directories
+ RemoveTempEventLogDirs(_eventLogDirectories);
+ }
+
+ #endregion
+
+ private static ISet ParseCommaSeparatedList(string commaSeparatedList)
+ {
+ ISet strings = new HashSet();
+ string[] items = commaSeparatedList.Split(new char[] { ',' });
+ foreach (string item in items)
{
- ValidateArg.NotNull(e, "SessionEndEventArgs");
- ValidateArg.NotNull(e.Context, "SessionEndEventArgs.Context");
+ strings.Add(item.Trim());
+ }
- if (EqtTrace.IsVerboseEnabled)
- {
- EqtTrace.Verbose("EventLogDataCollector: SessionEnd received");
- }
+ return strings;
+ }
+
+ #region Event Handlers
- this.WriteCollectedEventLogEntries(e.Context, true, TimeSpan.MaxValue, DateTime.UtcNow);
+ private void OnSessionStart(object sender, SessionStartEventArgs e)
+ {
+ ValidateArg.NotNull(e, "SessionStartEventArgs");
+ ValidateArg.NotNull(e.Context, "SessionStartEventArgs.Context");
+
+ if (EqtTrace.IsVerboseEnabled)
+ {
+ EqtTrace.Verbose("EventLogDataCollector: SessionStart received");
}
- private void OnTestCaseStart(object sender, TestCaseStartEventArgs e)
+ StartCollectionForContext(e.Context, true);
+ }
+
+ private void OnSessionEnd(object sender, SessionEndEventArgs e)
+ {
+ ValidateArg.NotNull(e, "SessionEndEventArgs");
+ ValidateArg.NotNull(e.Context, "SessionEndEventArgs.Context");
+
+ if (EqtTrace.IsVerboseEnabled)
{
- ValidateArg.NotNull(e, "TestCaseStartEventArgs");
- ValidateArg.NotNull(e.Context, "TestCaseStartEventArgs.Context");
+ EqtTrace.Verbose("EventLogDataCollector: SessionEnd received");
+ }
- if (!e.Context.HasTestCase)
- {
- Debug.Fail("Context is not for a test case");
- throw new ArgumentNullException("TestCaseStartEventArgs.Context.HasTestCase");
- }
+ WriteCollectedEventLogEntries(e.Context, true, TimeSpan.MaxValue, DateTime.UtcNow);
+ }
- if (EqtTrace.IsVerboseEnabled)
- {
- EqtTrace.Verbose("EventLogDataCollector: TestCaseStart received for test '{0}'.", e.TestCaseName);
- }
+ private void OnTestCaseStart(object sender, TestCaseStartEventArgs e)
+ {
+ ValidateArg.NotNull(e, "TestCaseStartEventArgs");
+ ValidateArg.NotNull(e.Context, "TestCaseStartEventArgs.Context");
- this.StartCollectionForContext(e.Context, false);
+ if (!e.Context.HasTestCase)
+ {
+ Debug.Fail("Context is not for a test case");
+ throw new ArgumentNullException("TestCaseStartEventArgs.Context.HasTestCase");
}
- private void OnTestCaseEnd(object sender, TestCaseEndEventArgs e)
+ if (EqtTrace.IsVerboseEnabled)
{
- ValidateArg.NotNull(e, "TestCaseEndEventArgs");
+ EqtTrace.Verbose("EventLogDataCollector: TestCaseStart received for test '{0}'.", e.TestCaseName);
+ }
- Debug.Assert(e.Context != null, "Context is null");
- Debug.Assert(e.Context.HasTestCase, "Context is not for a test case");
+ StartCollectionForContext(e.Context, false);
+ }
- if (EqtTrace.IsVerboseEnabled)
- {
- EqtTrace.Verbose(
- "EventLogDataCollector: TestCaseEnd received for test '{0}' with Test Outcome: {1}.",
- e.TestCaseName,
- e.TestOutcome);
- }
+ private void OnTestCaseEnd(object sender, TestCaseEndEventArgs e)
+ {
+ ValidateArg.NotNull(e, "TestCaseEndEventArgs");
+
+ Debug.Assert(e.Context != null, "Context is null");
+ Debug.Assert(e.Context.HasTestCase, "Context is not for a test case");
- this.WriteCollectedEventLogEntries(e.Context, false, TimeSpan.MaxValue, DateTime.UtcNow);
+ if (EqtTrace.IsVerboseEnabled)
+ {
+ EqtTrace.Verbose(
+ "EventLogDataCollector: TestCaseEnd received for test '{0}' with Test Outcome: {1}.",
+ e.TestCaseName,
+ e.TestOutcome);
}
- #endregion
+ WriteCollectedEventLogEntries(e.Context, false, TimeSpan.MaxValue, DateTime.UtcNow);
+ }
+
+ #endregion
- #region Private methods
+ #region Private methods
- private void RemoveTempEventLogDirs(List tempDirs)
+ private void RemoveTempEventLogDirs(List tempDirs)
+ {
+ if (tempDirs != null)
{
- if (tempDirs != null)
+ foreach (string dir in tempDirs)
{
- foreach (string dir in tempDirs)
- {
- // Delete only if the directory is empty
- this.fileHelper.DeleteEmptyDirectroy(dir);
- }
+ // Delete only if the directory is empty
+ _fileHelper.DeleteEmptyDirectroy(dir);
}
}
+ }
- private void StartCollectionForContext(DataCollectionContext dataCollectionContext, bool isSessionContext)
+ private void StartCollectionForContext(DataCollectionContext dataCollectionContext, bool isSessionContext)
+ {
+ lock (ContextMap)
{
- EventLogSessionContext eventLogSessionContext = null;
- lock (this.ContextMap)
- {
- eventLogSessionContext =
- new EventLogSessionContext(this.eventLogContainerMap);
- this.ContextMap.Add(dataCollectionContext, eventLogSessionContext);
- }
+ var eventLogSessionContext = new EventLogSessionContext(_eventLogContainerMap);
+ ContextMap.Add(dataCollectionContext, eventLogSessionContext);
}
+ }
- private void WriteCollectedEventLogEntries(
- DataCollectionContext dataCollectionContext,
- bool isSessionEnd,
- TimeSpan requestedDuration,
- DateTime timeRequestReceived)
- {
- var context = this.GetEventLogSessionContext(dataCollectionContext);
- context.CreateEventLogContainerEndIndexMap();
+ private void WriteCollectedEventLogEntries(
+ DataCollectionContext dataCollectionContext,
+ bool isSessionEnd,
+ TimeSpan requestedDuration,
+ DateTime timeRequestReceived)
+ {
+ var context = GetEventLogSessionContext(dataCollectionContext);
+ context.CreateEventLogContainerEndIndexMap();
- List eventLogEntries = new List();
- foreach (KeyValuePair kvp in this.eventLogContainerMap)
+ List eventLogEntries = new();
+ foreach (KeyValuePair kvp in _eventLogContainerMap)
+ {
+ try
{
- try
+ if (isSessionEnd)
{
- if (isSessionEnd)
- {
- kvp.Value.EventLog.EnableRaisingEvents = false;
- }
-
- for (int i = context.EventLogContainerStartIndexMap[kvp.Key]; i <= context.EventLogContainerEndIndexMap[kvp.Key]; i++)
- {
- eventLogEntries.Add(kvp.Value.EventLogEntries[i]);
- }
+ kvp.Value.EventLog.EnableRaisingEvents = false;
}
- catch (Exception e)
+
+ for (int i = context.EventLogContainerStartIndexMap[kvp.Key]; i <= context.EventLogContainerEndIndexMap[kvp.Key]; i++)
{
- this.logger.LogWarning(
- dataCollectionContext,
- string.Format(
- CultureInfo.InvariantCulture,
- Resource.CleanupException,
- kvp.Value.EventLog,
- e.ToString()));
+ eventLogEntries.Add(kvp.Value.EventLogEntries[i]);
}
}
+ catch (Exception e)
+ {
+ _logger.LogWarning(
+ dataCollectionContext,
+ string.Format(
+ CultureInfo.InvariantCulture,
+ Resource.CleanupException,
+ kvp.Value.EventLog,
+ e.ToString()));
+ }
+ }
- var fileName = this.WriteEventLogs(eventLogEntries, isSessionEnd ? int.MaxValue : this.maxEntries, dataCollectionContext, requestedDuration, timeRequestReceived);
+ var fileName = WriteEventLogs(eventLogEntries, isSessionEnd ? int.MaxValue : MaxEntries, dataCollectionContext, requestedDuration, timeRequestReceived);
- // Add the directory to the list
- this.eventLogDirectories.Add(Path.GetDirectoryName(fileName));
+ // Add the directory to the list
+ _eventLogDirectories.Add(Path.GetDirectoryName(fileName));
- lock (this.ContextMap)
- {
- this.ContextMap.Remove(dataCollectionContext);
- }
+ lock (ContextMap)
+ {
+ ContextMap.Remove(dataCollectionContext);
}
+ }
- private void ConfigureEventLogNames(CollectorNameValueConfigurationManager collectorNameValueConfigurationManager)
+ private void ConfigureEventLogNames(CollectorNameValueConfigurationManager collectorNameValueConfigurationManager)
+ {
+ EventLogNames = new HashSet();
+ string eventLogs = collectorNameValueConfigurationManager[EventLogConstants.SettingEventLogs];
+ if (eventLogs != null)
{
- this.eventLogNames = new HashSet();
- string eventLogs = collectorNameValueConfigurationManager[EventLogConstants.SettingEventLogs];
- if (eventLogs != null)
- {
- this.eventLogNames = ParseCommaSeparatedList(eventLogs);
- if (EqtTrace.IsVerboseEnabled)
- {
- EqtTrace.Verbose(
- "EventLogDataCollector configuration: " + EventLogConstants.SettingEventLogs + "=" + eventLogs);
- }
- }
- else
+ EventLogNames = ParseCommaSeparatedList(eventLogs);
+ if (EqtTrace.IsVerboseEnabled)
{
- // Default to collecting these standard logs
- this.eventLogNames.Add("System");
- this.eventLogNames.Add("Application");
+ EqtTrace.Verbose(
+ "EventLogDataCollector configuration: " + EventLogConstants.SettingEventLogs + "=" + eventLogs);
}
+ }
+ else
+ {
+ // Default to collecting these standard logs
+ EventLogNames.Add("System");
+ EventLogNames.Add("Application");
+ }
- foreach (string eventLogName in this.eventLogNames)
+ foreach (string eventLogName in EventLogNames)
+ {
+ try
{
- try
+ // Create an EventLog object and add it to the eventLogContext if one does not already exist
+ if (!_eventLogContainerMap.ContainsKey(eventLogName))
{
- // Create an EventLog object and add it to the eventLogContext if one does not already exist
- if (!this.eventLogContainerMap.ContainsKey(eventLogName))
- {
- IEventLogContainer eventLogContainer = new EventLogContainer(
- eventLogName,
- this.eventSources,
- this.entryTypes,
- int.MaxValue,
- this.logger,
- this.dataCollectorContext);
- this.eventLogContainerMap.Add(eventLogName, eventLogContainer);
- }
-
- if (EqtTrace.IsVerboseEnabled)
- {
- EqtTrace.Verbose(string.Format(
- CultureInfo.InvariantCulture,
- "EventLogDataCollector: Created EventSource '{0}'",
- eventLogName));
- }
+ IEventLogContainer eventLogContainer = new EventLogContainer(
+ eventLogName,
+ EventSources,
+ EntryTypes,
+ int.MaxValue,
+ _logger,
+ _dataCollectorContext);
+ _eventLogContainerMap.Add(eventLogName, eventLogContainer);
}
- catch (Exception ex)
+
+ if (EqtTrace.IsVerboseEnabled)
{
- this.logger.LogError(
- this.dataCollectorContext,
- new EventLogCollectorException(string.Format(CultureInfo.InvariantCulture, Resource.ReadError, eventLogName, Environment.MachineName), ex));
+ EqtTrace.Verbose(string.Format(
+ CultureInfo.InvariantCulture,
+ "EventLogDataCollector: Created EventSource '{0}'",
+ eventLogName));
}
}
+ catch (Exception ex)
+ {
+ _logger.LogError(
+ _dataCollectorContext,
+ new EventLogCollectorException(string.Format(CultureInfo.InvariantCulture, Resource.ReadError, eventLogName, Environment.MachineName), ex));
+ }
}
+ }
- private void ConfigureEventSources(CollectorNameValueConfigurationManager collectorNameValueConfigurationManager)
+ private void ConfigureEventSources(CollectorNameValueConfigurationManager collectorNameValueConfigurationManager)
+ {
+ string eventSourcesStr = collectorNameValueConfigurationManager[EventLogConstants.SettingEventSources];
+ if (!string.IsNullOrEmpty(eventSourcesStr))
{
- string eventSourcesStr = collectorNameValueConfigurationManager[EventLogConstants.SettingEventSources];
- if (!string.IsNullOrEmpty(eventSourcesStr))
+ EventSources = ParseCommaSeparatedList(eventSourcesStr);
+ if (EqtTrace.IsVerboseEnabled)
{
- this.eventSources = ParseCommaSeparatedList(eventSourcesStr);
- if (EqtTrace.IsVerboseEnabled)
- {
- EqtTrace.Verbose(
- "EventLogDataCollector configuration: " + EventLogConstants.SettingEventSources + "="
- + this.eventSources);
- }
+ EqtTrace.Verbose(
+ "EventLogDataCollector configuration: " + EventLogConstants.SettingEventSources + "="
+ + EventSources);
}
}
+ }
- private void ConfigureEntryTypes(CollectorNameValueConfigurationManager collectorNameValueConfigurationManager)
+ private void ConfigureEntryTypes(CollectorNameValueConfigurationManager collectorNameValueConfigurationManager)
+ {
+ EntryTypes = new HashSet();
+ string entryTypesStr = collectorNameValueConfigurationManager[EventLogConstants.SettingEntryTypes];
+ if (entryTypesStr != null)
{
- this.entryTypes = new HashSet();
- string entryTypesStr = collectorNameValueConfigurationManager[EventLogConstants.SettingEntryTypes];
- if (entryTypesStr != null)
+ foreach (string entryTypestring in ParseCommaSeparatedList(entryTypesStr))
{
- foreach (string entryTypestring in ParseCommaSeparatedList(entryTypesStr))
- {
- this.entryTypes.Add(
- (EventLogEntryType)Enum.Parse(typeof(EventLogEntryType), entryTypestring, true));
- }
-
- if (EqtTrace.IsVerboseEnabled)
- {
- EqtTrace.Verbose(
- "EventLogDataCollector configuration: " + EventLogConstants.SettingEntryTypes + "="
- + this.entryTypes);
- }
+ EntryTypes.Add(
+ (EventLogEntryType)Enum.Parse(typeof(EventLogEntryType), entryTypestring, true));
}
- else
+
+ if (EqtTrace.IsVerboseEnabled)
{
- this.entryTypes.Add(EventLogEntryType.Error);
- this.entryTypes.Add(EventLogEntryType.Warning);
- this.entryTypes.Add(EventLogEntryType.FailureAudit);
+ EqtTrace.Verbose(
+ "EventLogDataCollector configuration: " + EventLogConstants.SettingEntryTypes + "="
+ + EntryTypes);
}
}
+ else
+ {
+ EntryTypes.Add(EventLogEntryType.Error);
+ EntryTypes.Add(EventLogEntryType.Warning);
+ EntryTypes.Add(EventLogEntryType.FailureAudit);
+ }
+ }
- private void ConfigureMaxEntries(CollectorNameValueConfigurationManager collectorNameValueConfigurationManager)
+ private void ConfigureMaxEntries(CollectorNameValueConfigurationManager collectorNameValueConfigurationManager)
+ {
+ string maxEntriesstring = collectorNameValueConfigurationManager[EventLogConstants.SettingMaxEntries];
+ if (maxEntriesstring != null)
{
- string maxEntriesstring = collectorNameValueConfigurationManager[EventLogConstants.SettingMaxEntries];
- if (maxEntriesstring != null)
+ try
{
- try
- {
- this.maxEntries = int.Parse(maxEntriesstring, CultureInfo.InvariantCulture);
+ MaxEntries = int.Parse(maxEntriesstring, CultureInfo.InvariantCulture);
- // A negative or 0 value means no maximum
- if (this.maxEntries <= 0)
- {
- this.maxEntries = int.MaxValue;
- }
- }
- catch (FormatException)
+ // A negative or 0 value means no maximum
+ if (MaxEntries <= 0)
{
- this.maxEntries = EventLogConstants.DefaultMaxEntries;
- }
-
- if (EqtTrace.IsVerboseEnabled)
- {
- EqtTrace.Verbose(
- "EventLogDataCollector configuration: " + EventLogConstants.SettingMaxEntries + "="
- + this.maxEntries);
+ MaxEntries = int.MaxValue;
}
}
- else
+ catch (FormatException)
{
- this.maxEntries = EventLogConstants.DefaultMaxEntries;
+ MaxEntries = EventLogConstants.DefaultMaxEntries;
}
- }
- private EventLogSessionContext GetEventLogSessionContext(DataCollectionContext dataCollectionContext)
- {
- EventLogSessionContext eventLogSessionContext;
- bool eventLogContainerFound;
- lock (this.ContextMap)
+ if (EqtTrace.IsVerboseEnabled)
{
- eventLogContainerFound = this.ContextMap.TryGetValue(dataCollectionContext, out eventLogSessionContext);
+ EqtTrace.Verbose(
+ "EventLogDataCollector configuration: " + EventLogConstants.SettingMaxEntries + "="
+ + MaxEntries);
}
+ }
+ else
+ {
+ MaxEntries = EventLogConstants.DefaultMaxEntries;
+ }
+ }
- if (!eventLogContainerFound)
- {
- string msg = string.Format(
- CultureInfo.InvariantCulture,
- Resource.ContextNotFoundException,
- dataCollectionContext.ToString());
- throw new EventLogCollectorException(msg, null);
- }
+ private EventLogSessionContext GetEventLogSessionContext(DataCollectionContext dataCollectionContext)
+ {
+ EventLogSessionContext eventLogSessionContext;
+ bool eventLogContainerFound;
+ lock (ContextMap)
+ {
+ eventLogContainerFound = ContextMap.TryGetValue(dataCollectionContext, out eventLogSessionContext);
+ }
- return eventLogSessionContext;
+ if (!eventLogContainerFound)
+ {
+ string msg = string.Format(
+ CultureInfo.InvariantCulture,
+ Resource.ContextNotFoundException,
+ dataCollectionContext.ToString());
+ throw new EventLogCollectorException(msg, null);
}
- #endregion
+ return eventLogSessionContext;
}
-}
+
+ #endregion
+}
\ No newline at end of file
diff --git a/src/DataCollectors/Microsoft.TestPlatform.Extensions.EventLogCollector/EventLogSessionContext.cs b/src/DataCollectors/Microsoft.TestPlatform.Extensions.EventLogCollector/EventLogSessionContext.cs
index 059975eea7..6050336e84 100644
--- a/src/DataCollectors/Microsoft.TestPlatform.Extensions.EventLogCollector/EventLogSessionContext.cs
+++ b/src/DataCollectors/Microsoft.TestPlatform.Extensions.EventLogCollector/EventLogSessionContext.cs
@@ -1,65 +1,64 @@
// 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.TestPlatform.Extensions.EventLogCollector
+namespace Microsoft.TestPlatform.Extensions.EventLogCollector;
+
+using System.Collections.Generic;
+
+///
+/// Stores the start and end index for EventLogEntries corresponding to a data collection session.
+///
+internal class EventLogSessionContext
{
- using System.Collections.Generic;
+ private readonly IDictionary _eventLogContainerMap;
///
- /// Stores the start and end index for EventLogEntries corresponding to a data collection session.
+ /// Initializes a new instance of the class.
///
- internal class EventLogSessionContext
+ ///
+ /// Event Log container map.
+ ///
+ public EventLogSessionContext(IDictionary eventLogContainerMap)
{
- private IDictionary eventLogContainerMap;
+ _eventLogContainerMap = eventLogContainerMap;
+ CreateEventLogContainerStartIndexMap();
+ }
- ///
- /// Initializes a new instance of the class.
- ///
- ///
- /// Event Log container map.
- ///
- public EventLogSessionContext(IDictionary eventLogContainerMap)
- {
- this.eventLogContainerMap = eventLogContainerMap;
- this.CreateEventLogContainerStartIndexMap();
- }
+ ///
+ /// Gets the start index for EventLogs Entries.
+ ///
+ internal Dictionary EventLogContainerStartIndexMap { get; private set; }
- ///
- /// Gets the start index for EventLogs Entries.
- ///
- internal Dictionary EventLogContainerStartIndexMap { get; private set; }
+ ///
+ /// Gets the end index for EventLogs Entries
+ ///
+ internal Dictionary EventLogContainerEndIndexMap { get; private set; }
- ///
- /// Gets the end index for EventLogs Entries
- ///
- internal Dictionary EventLogContainerEndIndexMap { get; private set; }
+ ///
+ /// Creates the end index map for EventLogs Entries
+ ///
+ public void CreateEventLogContainerEndIndexMap()
+ {
+ EventLogContainerEndIndexMap = new Dictionary(_eventLogContainerMap.Count);
- ///
- /// Creates the end index map for EventLogs Entries
- ///
- public void CreateEventLogContainerEndIndexMap()
+ foreach (KeyValuePair kvp in _eventLogContainerMap)
{
- this.EventLogContainerEndIndexMap = new Dictionary(this.eventLogContainerMap.Count);
-
- foreach (KeyValuePair kvp in this.eventLogContainerMap)
- {
- kvp.Value.OnEventLogEntryWritten(kvp.Value.EventLog, null);
+ kvp.Value.OnEventLogEntryWritten(kvp.Value.EventLog, null);
- this.EventLogContainerEndIndexMap.Add(kvp.Key, kvp.Value.EventLogEntries.Count - 1);
- }
+ EventLogContainerEndIndexMap.Add(kvp.Key, kvp.Value.EventLogEntries.Count - 1);
}
+ }
- ///
- /// Creates the start index map for EventLogs Entries
- ///
- public void CreateEventLogContainerStartIndexMap()
- {
- this.EventLogContainerStartIndexMap = new Dictionary(this.eventLogContainerMap.Count);
+ ///
+ /// Creates the start index map for EventLogs Entries
+ ///
+ public void CreateEventLogContainerStartIndexMap()
+ {
+ EventLogContainerStartIndexMap = new Dictionary(_eventLogContainerMap.Count);
- foreach (KeyValuePair kvp in this.eventLogContainerMap)
- {
- this.EventLogContainerStartIndexMap.Add(kvp.Key, kvp.Value.EventLogEntries.Count);
- }
+ foreach (KeyValuePair kvp in _eventLogContainerMap)
+ {
+ EventLogContainerStartIndexMap.Add(kvp.Key, kvp.Value.EventLogEntries.Count);
}
}
}
\ No newline at end of file
diff --git a/src/DataCollectors/Microsoft.TestPlatform.Extensions.EventLogCollector/EventLogXmlWriter.cs b/src/DataCollectors/Microsoft.TestPlatform.Extensions.EventLogCollector/EventLogXmlWriter.cs
index 1446d31295..71dcf27841 100644
--- a/src/DataCollectors/Microsoft.TestPlatform.Extensions.EventLogCollector/EventLogXmlWriter.cs
+++ b/src/DataCollectors/Microsoft.TestPlatform.Extensions.EventLogCollector/EventLogXmlWriter.cs
@@ -1,89 +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.TestPlatform.Extensions.EventLogCollector
-{
- using System;
- using System.Collections.Generic;
- using System.Data;
- using System.Diagnostics;
- using System.Globalization;
- using System.IO;
- using System.Text;
+namespace Microsoft.TestPlatform.Extensions.EventLogCollector;
+
+using System;
+using System.Collections.Generic;
+using System.Data;
+using System.Diagnostics;
+using System.Globalization;
+using System.IO;
+using System.Text;
- using Microsoft.VisualStudio.TestPlatform.Utilities.Helpers.Interfaces;
+using Microsoft.VisualStudio.TestPlatform.Utilities.Helpers.Interfaces;
+
+///
+/// This class writes event log entries to an XML file in a format that can be retrieved into a DataSet
+///
+internal static class EventLogXmlWriter
+{
+ #region Public methods
///
- /// This class writes event log entries to an XML file in a format that can be retrieved into a DataSet
+ /// The write event log entries to xml file.
///
- internal static class EventLogXmlWriter
+ ///
+ /// The xml file path.
+ ///
+ ///
+ /// The event log entries.
+ ///
+ ///
+ /// The file Helper.
+ ///
+ public static void WriteEventLogEntriesToXmlFile(string xmlFilePath, List eventLogEntries, IFileHelper fileHelper)
{
- #region Public methods
+ using DataTable dataTable = new();
+ dataTable.Locale = CultureInfo.InvariantCulture;
- ///
- /// The write event log entries to xml file.
- ///
- ///
- /// The xml file path.
- ///
- ///
- /// The event log entries.
- ///
- ///
- /// The file Helper.
- ///
- public static void WriteEventLogEntriesToXmlFile(string xmlFilePath, List eventLogEntries, IFileHelper fileHelper)
- {
- using (DataTable dataTable = new DataTable())
- {
- dataTable.Locale = CultureInfo.InvariantCulture;
+ // The MaxLength of the Type and Source columns must be set to allow indices to be created on them
+ DataColumn typeColumn = new("Type", typeof(string));
+ typeColumn.MaxLength = EventLogConstants.TypeColumnMaxLength;
+ dataTable.Columns.Add(typeColumn);
- // The MaxLength of the Type and Source columns must be set to allow indices to be created on them
- DataColumn typeColumn = new DataColumn("Type", typeof(string));
- typeColumn.MaxLength = EventLogConstants.TypeColumnMaxLength;
- dataTable.Columns.Add(typeColumn);
+ dataTable.Columns.Add(new DataColumn("DateTime", typeof(DateTime)));
- dataTable.Columns.Add(new DataColumn("DateTime", typeof(DateTime)));
+ DataColumn sourceColumn = new("Source", typeof(string));
+ sourceColumn.MaxLength = EventLogConstants.SourceColumnMaxLength;
+ dataTable.Columns.Add(sourceColumn);
- DataColumn sourceColumn = new DataColumn("Source", typeof(string));
- sourceColumn.MaxLength = EventLogConstants.SourceColumnMaxLength;
- dataTable.Columns.Add(sourceColumn);
+ dataTable.Columns.Add(new DataColumn("Category", typeof(string)));
+ dataTable.Columns.Add(new DataColumn("EventID", typeof(long)));
+ dataTable.Columns.Add(new DataColumn("Description", typeof(string)));
+ dataTable.Columns.Add(new DataColumn("User", typeof(string)));
+ dataTable.Columns.Add(new DataColumn("Computer", typeof(string)));
+ dataTable.ExtendedProperties.Add("TimestampColumnName", "DateTime");
+ dataTable.ExtendedProperties.Add("IndexColumnNames", "Source,Type");
- dataTable.Columns.Add(new DataColumn("Category", typeof(string)));
- dataTable.Columns.Add(new DataColumn("EventID", typeof(long)));
- dataTable.Columns.Add(new DataColumn("Description", typeof(string)));
- dataTable.Columns.Add(new DataColumn("User", typeof(string)));
- dataTable.Columns.Add(new DataColumn("Computer", typeof(string)));
- dataTable.ExtendedProperties.Add("TimestampColumnName", "DateTime");
- dataTable.ExtendedProperties.Add("IndexColumnNames", "Source,Type");
-
- foreach (EventLogEntry entry in eventLogEntries)
- {
- DataRow row = dataTable.NewRow();
- row["Type"] = entry.EntryType.ToString();
- row["DateTime"] = entry.TimeGenerated;
- row["Source"] = entry.Source;
- row["Category"] = entry.Category;
- row["EventID"] = entry.InstanceId;
- row["Description"] = entry.Message;
- row["User"] = entry.UserName;
- row["Computer"] = entry.MachineName;
- dataTable.Rows.Add(row);
- }
+ foreach (EventLogEntry entry in eventLogEntries)
+ {
+ DataRow row = dataTable.NewRow();
+ row["Type"] = entry.EntryType.ToString();
+ row["DateTime"] = entry.TimeGenerated;
+ row["Source"] = entry.Source;
+ row["Category"] = entry.Category;
+ row["EventID"] = entry.InstanceId;
+ row["Description"] = entry.Message;
+ row["User"] = entry.UserName;
+ row["Computer"] = entry.MachineName;
+ dataTable.Rows.Add(row);
+ }
- DataSet dataSet = new DataSet();
- dataSet.Locale = CultureInfo.InvariantCulture;
- dataSet.Tables.Add(dataTable);
+ DataSet dataSet = new();
+ dataSet.Locale = CultureInfo.InvariantCulture;
+ dataSet.Tables.Add(dataTable);
- // Use UTF-16 encoding
- StringBuilder stringBuilder = new StringBuilder();
- using (StringWriter stringWriter = new StringWriter(stringBuilder))
- {
- dataSet.WriteXml(stringWriter, XmlWriteMode.WriteSchema);
- fileHelper.WriteAllTextToFile(xmlFilePath, stringBuilder.ToString());
- }
- }
- }
+ // Use UTF-16 encoding
+ StringBuilder stringBuilder = new();
+ using StringWriter stringWriter = new(stringBuilder);
+ dataSet.WriteXml(stringWriter, XmlWriteMode.WriteSchema);
+ fileHelper.WriteAllTextToFile(xmlFilePath, stringBuilder.ToString());
}
- #endregion
}
+#endregion
\ No newline at end of file
diff --git a/src/DataCollectors/Microsoft.TestPlatform.Extensions.EventLogCollector/IEventLogContainer.cs b/src/DataCollectors/Microsoft.TestPlatform.Extensions.EventLogCollector/IEventLogContainer.cs
index 71166a0576..fa1d544d96 100644
--- a/src/DataCollectors/Microsoft.TestPlatform.Extensions.EventLogCollector/IEventLogContainer.cs
+++ b/src/DataCollectors/Microsoft.TestPlatform.Extensions.EventLogCollector/IEventLogContainer.cs
@@ -1,36 +1,35 @@
// 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.TestPlatform.Extensions.EventLogCollector
-{
- using System;
- using System.Collections.Generic;
- using System.Diagnostics;
+namespace Microsoft.TestPlatform.Extensions.EventLogCollector;
+
+using System;
+using System.Collections.Generic;
+using System.Diagnostics;
+///
+/// Event log container interface
+///
+internal interface IEventLogContainer : IDisposable
+{
///
- /// Event log container interface
+ /// Gets the event log.
///
- internal interface IEventLogContainer : IDisposable
- {
- ///
- /// Gets the event log.
- ///
- EventLog EventLog { get; }
+ EventLog EventLog { get; }
- ///
- /// Gets the event log entries.
- ///
- List EventLogEntries { get; }
+ ///
+ /// Gets the event log entries.
+ ///
+ List EventLogEntries { get; }
- ///
- /// Event Handler for handling log entries.
- ///
- ///
- /// The source object that raised EventLog entry event.
- ///
- ///
- /// Contains data related to EventLog entry.
- ///
- void OnEventLogEntryWritten(object source, EntryWrittenEventArgs e);
- }
-}
+ ///
+ /// Event Handler for handling log entries.
+ ///
+ ///
+ /// The source object that raised EventLog entry event.
+ ///
+ ///
+ /// Contains data related to EventLog entry.
+ ///
+ void OnEventLogEntryWritten(object source, EntryWrittenEventArgs e);
+}
\ No newline at end of file
diff --git a/src/Microsoft.TestPlatform.AdapterUtilities/Helpers/ReflectionHelpers.MethodBase.cs b/src/Microsoft.TestPlatform.AdapterUtilities/Helpers/ReflectionHelpers.MethodBase.cs
index 0eaad1f4ed..8d3ce23fcb 100644
--- a/src/Microsoft.TestPlatform.AdapterUtilities/Helpers/ReflectionHelpers.MethodBase.cs
+++ b/src/Microsoft.TestPlatform.AdapterUtilities/Helpers/ReflectionHelpers.MethodBase.cs
@@ -1,56 +1,55 @@
// 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.TestPlatform.AdapterUtilities.Helpers
-{
- using System;
- using System.Reflection;
+namespace Microsoft.TestPlatform.AdapterUtilities.Helpers;
- internal static partial class ReflectionHelpers
- {
+using System;
+using System.Reflection;
+
+internal static partial class ReflectionHelpers
+{
#if NETSTANDARD1_0 || NETSTANDARD1_3 || WINDOWS_UWP
- private static readonly Type methodBase = typeof(MethodBase);
+ private static readonly Type MethodBase = typeof(MethodBase);
- private const string MemberTypePropertyName = "MemberType";
- private const string ReflectedTypePropertyName = "ReflectedType";
- private const string MethodHandlePropertyName = "MethodHandle";
+ private const string MemberTypePropertyName = "MemberType";
+ private const string ReflectedTypePropertyName = "ReflectedType";
+ private const string MethodHandlePropertyName = "MethodHandle";
- private static readonly PropertyInfo memberTypeProperty = methodBase.GetRuntimeProperty(MemberTypePropertyName);
- private static readonly PropertyInfo reflectedTypeProperty = methodBase.GetRuntimeProperty(ReflectedTypePropertyName);
- private static readonly PropertyInfo methodHandleProperty = methodBase.GetRuntimeProperty(MethodHandlePropertyName);
+ private static readonly PropertyInfo MemberTypeProperty = MethodBase.GetRuntimeProperty(MemberTypePropertyName);
+ private static readonly PropertyInfo ReflectedTypeProperty = MethodBase.GetRuntimeProperty(ReflectedTypePropertyName);
+ private static readonly PropertyInfo MethodHandleProperty = MethodBase.GetRuntimeProperty(MethodHandlePropertyName);
#endif
- internal static bool IsMethod(MethodBase method)
- {
+ internal static bool IsMethod(MethodBase method)
+ {
#if !NETSTANDARD1_0 && !NETSTANDARD1_3 && !WINDOWS_UWP
- return method.MemberType == MemberTypes.Method;
+ return method.MemberType == MemberTypes.Method;
#else
- AssertSupport(memberTypeProperty, MemberTypePropertyName, methodBase.FullName);
+ AssertSupport(MemberTypeProperty, MemberTypePropertyName, MethodBase.FullName);
- return (int)memberTypeProperty.GetValue(method) == 8;
+ return (int)MemberTypeProperty.GetValue(method) == 8;
#endif
- }
+ }
- internal static Type GetReflectedType(MethodBase method)
- {
+ internal static Type GetReflectedType(MethodBase method)
+ {
#if !NETSTANDARD1_0 && !NETSTANDARD1_3 && !WINDOWS_UWP
- return method.ReflectedType;
+ return method.ReflectedType;
#else
- AssertSupport(memberTypeProperty, ReflectedTypePropertyName, methodBase.FullName);
+ AssertSupport(MemberTypeProperty, ReflectedTypePropertyName, MethodBase.FullName);
- return reflectedTypeProperty.GetValue(method) as Type;
+ return ReflectedTypeProperty.GetValue(method) as Type;
#endif
- }
+ }
- internal static RuntimeMethodHandle GetMethodHandle(MethodBase method)
- {
+ internal static RuntimeMethodHandle GetMethodHandle(MethodBase method)
+ {
#if !NETSTANDARD1_0 && !NETSTANDARD1_3 && !WINDOWS_UWP
- return method.MethodHandle;
+ return method.MethodHandle;
#else
- AssertSupport(memberTypeProperty, MethodHandlePropertyName, methodBase.FullName);
+ AssertSupport(MemberTypeProperty, MethodHandlePropertyName, MethodBase.FullName);
- return (RuntimeMethodHandle)methodHandleProperty.GetValue(method);
+ return (RuntimeMethodHandle)MethodHandleProperty.GetValue(method);
#endif
- }
}
}
diff --git a/src/Microsoft.TestPlatform.AdapterUtilities/Helpers/ReflectionHelpers.Type.cs b/src/Microsoft.TestPlatform.AdapterUtilities/Helpers/ReflectionHelpers.Type.cs
index d8e8d23b5c..921c7a294b 100644
--- a/src/Microsoft.TestPlatform.AdapterUtilities/Helpers/ReflectionHelpers.Type.cs
+++ b/src/Microsoft.TestPlatform.AdapterUtilities/Helpers/ReflectionHelpers.Type.cs
@@ -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.TestPlatform.AdapterUtilities.Helpers
-{
- using System;
- using System.Reflection;
+namespace Microsoft.TestPlatform.AdapterUtilities.Helpers;
+
+using System;
+using System.Reflection;
- internal static partial class ReflectionHelpers
+internal static partial class ReflectionHelpers
+{
+ internal static bool IsGenericType(Type type)
{
- internal static bool IsGenericType(Type type)
- {
#if !NETSTANDARD1_0 && !NETSTANDARD1_3 && !WINDOWS_UWP
- return type.IsGenericType;
+ return type.IsGenericType;
#else
- return type.GetTypeInfo().IsGenericType;
+ return type.GetTypeInfo().IsGenericType;
#endif
- }
+ }
- internal static MethodBase GetDeclaringMethod(Type type)
- {
+ internal static MethodBase GetDeclaringMethod(Type type)
+ {
#if !NETSTANDARD1_0 && !NETSTANDARD1_3 && !WINDOWS_UWP
- return type.DeclaringMethod;
+ return type.DeclaringMethod;
#else
- return type.GetTypeInfo().DeclaringMethod;
+ return type.GetTypeInfo().DeclaringMethod;
#endif
- }
}
}
diff --git a/src/Microsoft.TestPlatform.AdapterUtilities/Helpers/ReflectionHelpers.cs b/src/Microsoft.TestPlatform.AdapterUtilities/Helpers/ReflectionHelpers.cs
index f3585d2617..8970ebb0b9 100644
--- a/src/Microsoft.TestPlatform.AdapterUtilities/Helpers/ReflectionHelpers.cs
+++ b/src/Microsoft.TestPlatform.AdapterUtilities/Helpers/ReflectionHelpers.cs
@@ -1,97 +1,96 @@
// 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.TestPlatform.AdapterUtilities.Helpers
-{
- using Microsoft.TestPlatform.AdapterUtilities.ManagedNameUtilities;
- using Microsoft.TestPlatform.AdapterUtilities.Resources;
+namespace Microsoft.TestPlatform.AdapterUtilities.Helpers;
+
+using Microsoft.TestPlatform.AdapterUtilities.ManagedNameUtilities;
+using Microsoft.TestPlatform.AdapterUtilities.Resources;
- using System;
- using System.Globalization;
- using System.Text;
+using System;
+using System.Globalization;
+using System.Text;
- internal static partial class ReflectionHelpers
+internal static partial class ReflectionHelpers
+{
+ private static void AssertSupport(T obj, string methodName, string className)
+ where T : class
{
- private static void AssertSupport(T obj, string methodName, string className)
- where T : class
+ if (obj == null)
{
- if (obj == null)
- {
- throw new NotImplementedException(string.Format(Resources.MethodNotImplementedOnPlatform, className, methodName));
- }
+ throw new NotImplementedException(string.Format(Resources.MethodNotImplementedOnPlatform, className, methodName));
}
+ }
- internal static string ParseEscapedString(string escapedString)
+ internal static string ParseEscapedString(string escapedString)
+ {
+ var stringBuilder = new StringBuilder();
+ var end = 0;
+ for (int i = 0; i < escapedString.Length; i++)
{
- var stringBuilder = new StringBuilder();
- var end = 0;
- for (int i = 0; i < escapedString.Length; i++)
+ if (escapedString[i] == '\'')
{
- if (escapedString[i] == '\'')
- {
- stringBuilder.Append(escapedString, end, i - end);
- end = i = ParseEscapedStringSegment(escapedString, i + 1, stringBuilder);
- }
- }
-
- if (stringBuilder.Length == 0)
- {
- return escapedString;
+ stringBuilder.Append(escapedString, end, i - end);
+ end = i = ParseEscapedStringSegment(escapedString, i + 1, stringBuilder);
}
+ }
- if (end != 0 && end < escapedString.Length)
- {
- stringBuilder.Append(escapedString, end, escapedString.Length - end);
- }
+ if (stringBuilder.Length == 0)
+ {
+ return escapedString;
+ }
- return stringBuilder.ToString();
+ if (end != 0 && end < escapedString.Length)
+ {
+ stringBuilder.Append(escapedString, end, escapedString.Length - end);
}
- // Unescapes a C# style escaped string.
- private static int ParseEscapedStringSegment(string escapedStringSegment, int pos, StringBuilder stringBuilder)
+ return stringBuilder.ToString();
+ }
+
+ // Unescapes a C# style escaped string.
+ private static int ParseEscapedStringSegment(string escapedStringSegment, int pos, StringBuilder stringBuilder)
+ {
+ for (int i = pos; i < escapedStringSegment.Length; i++)
{
- for (int i = pos; i < escapedStringSegment.Length; i++)
+ switch (escapedStringSegment[i])
{
- switch (escapedStringSegment[i])
- {
- case '\\':
- if (escapedStringSegment[i + 1] == 'u')
- {
- char c;
-
- try
- {
- var code = escapedStringSegment.Substring(i + 2, 4);
- c = (char)Convert.ToInt32(code, 16);
- }
- catch
- {
- throw new InvalidManagedNameException(
- string.Format(CultureInfo.CurrentCulture, Resources.ErrorInvalidSequenceAt, escapedStringSegment, i)
- );
- }
+ case '\\':
+ if (escapedStringSegment[i + 1] == 'u')
+ {
+ char c;
- stringBuilder.Append(c);
- i += 5;
+ try
+ {
+ var code = escapedStringSegment.Substring(i + 2, 4);
+ c = (char)Convert.ToInt32(code, 16);
}
- else
+ catch
{
- stringBuilder.Append(escapedStringSegment[++i]);
+ throw new InvalidManagedNameException(
+ string.Format(CultureInfo.CurrentCulture, Resources.ErrorInvalidSequenceAt, escapedStringSegment, i)
+ );
}
- break;
+ stringBuilder.Append(c);
+ i += 5;
+ }
+ else
+ {
+ stringBuilder.Append(escapedStringSegment[++i]);
+ }
- case '\'':
- return i + 1;
+ break;
- default:
- stringBuilder.Append(escapedStringSegment[i]);
- break;
- }
- }
+ case '\'':
+ return i + 1;
- string message = string.Format(CultureInfo.CurrentCulture, Resources.ErrorNoClosingQuote, escapedStringSegment);
- throw new InvalidManagedNameException(message);
+ default:
+ stringBuilder.Append(escapedStringSegment[i]);
+ break;
+ }
}
+
+ string message = string.Format(CultureInfo.CurrentCulture, Resources.ErrorNoClosingQuote, escapedStringSegment);
+ throw new InvalidManagedNameException(message);
}
}
diff --git a/src/Microsoft.TestPlatform.AdapterUtilities/HierarchyConstants.cs b/src/Microsoft.TestPlatform.AdapterUtilities/HierarchyConstants.cs
index 1546637c7c..0abe66a117 100644
--- a/src/Microsoft.TestPlatform.AdapterUtilities/HierarchyConstants.cs
+++ b/src/Microsoft.TestPlatform.AdapterUtilities/HierarchyConstants.cs
@@ -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.TestPlatform.AdapterUtilities
+namespace Microsoft.TestPlatform.AdapterUtilities;
+
+///
+/// Constants to help declare Hierarchy test property.
+///
+public static class HierarchyConstants
{
///
- /// Constants to help declare Hierarchy test property.
+ /// Label to use on Hierarchy test property.
///
- public static class HierarchyConstants
+ public const string HierarchyLabel = "Hierarchy";
+
+ ///
+ /// Property id to use on Hierarchy test property.
+ ///
+ public const string HierarchyPropertyId = "TestCase." + HierarchyLabel;
+
+ ///
+ /// Meanings of the indices in the Hierarchy array.
+ ///
+ public static class Levels
{
///
- /// Label to use on Hierarchy test property.
+ /// Total length of Hierarchy array.
///
- public const string HierarchyLabel = "Hierarchy";
+ public const int TotalLevelCount = 2;
///
- /// Property id to use on Hierarchy test property.
+ /// Index of the namespace element of the array.
///
- public const string HierarchyPropertyId = "TestCase." + HierarchyLabel;
+ public const int NamespaceIndex = 0;
///
- /// Meanings of the indices in the Hierarchy array.
+ /// Index of the class element of the array.
///
- public static class Levels
- {
- ///
- /// Total length of Hierarchy array.
- ///
- public const int TotalLevelCount = 2;
-
- ///
- /// Index of the namespace element of the array.
- ///
- public const int NamespaceIndex = 0;
-
- ///
- /// Index of the class element of the array.
- ///
- public const int ClassIndex = 1;
- }
+ public const int ClassIndex = 1;
}
-}
+}
\ No newline at end of file
diff --git a/src/Microsoft.TestPlatform.AdapterUtilities/ManagedNameConstants.cs b/src/Microsoft.TestPlatform.AdapterUtilities/ManagedNameConstants.cs
index 9415968b99..02e8387e6a 100644
--- a/src/Microsoft.TestPlatform.AdapterUtilities/ManagedNameConstants.cs
+++ b/src/Microsoft.TestPlatform.AdapterUtilities/ManagedNameConstants.cs
@@ -1,31 +1,30 @@
// 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.TestPlatform.AdapterUtilities
+namespace Microsoft.TestPlatform.AdapterUtilities;
+
+///
+/// Contants to help declare ManagedType and ManagedMethod test properties.
+///
+public static class ManagedNameConstants
{
///
- /// Constants to help declare ManagedType and ManagedMethod test properties.
+ /// Label to use on ManagedType test property.
///
- public static class ManagedNameConstants
- {
- ///
- /// Label to use on ManagedType test property.
- ///
- public const string ManagedTypeLabel = "ManagedType";
+ public const string ManagedTypeLabel = "ManagedType";
- ///
- /// Label to use on ManagedMethod test property.
- ///
- public const string ManagedMethodLabel = "ManagedMethod";
+ ///
+ /// Label to use on ManagedMethod test property.
+ ///
+ public const string ManagedMethodLabel = "ManagedMethod";
- ///
- /// Property id to use on ManagedType test property.
- ///
- public const string ManagedTypePropertyId = "TestCase." + ManagedTypeLabel;
+ ///
+ /// Property id to use on ManagedType test property.
+ ///
+ public const string ManagedTypePropertyId = "TestCase." + ManagedTypeLabel;
- ///
- /// Property id to use on ManagedMethod test property.
- ///
- public const string ManagedMethodPropertyId = "TestCase." + ManagedMethodLabel;
- }
-}
+ ///
+ /// Property id to use on ManagedMethod test property.
+ ///
+ public const string ManagedMethodPropertyId = "TestCase." + ManagedMethodLabel;
+}
\ No newline at end of file
diff --git a/src/Microsoft.TestPlatform.AdapterUtilities/ManagedNameUtilities/InvalidManagedNameException.cs b/src/Microsoft.TestPlatform.AdapterUtilities/ManagedNameUtilities/InvalidManagedNameException.cs
index 3cbbaf86f5..7bb5317ff4 100644
--- a/src/Microsoft.TestPlatform.AdapterUtilities/ManagedNameUtilities/InvalidManagedNameException.cs
+++ b/src/Microsoft.TestPlatform.AdapterUtilities/ManagedNameUtilities/InvalidManagedNameException.cs
@@ -1,27 +1,26 @@
// 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.TestPlatform.AdapterUtilities.ManagedNameUtilities
-{
- using System;
+namespace Microsoft.TestPlatform.AdapterUtilities.ManagedNameUtilities;
+
+using System;
#if !NETSTANDARD1_0 && !WINDOWS_UWP
- using System.Runtime.Serialization;
+using System.Runtime.Serialization;
#endif
#if !NETSTANDARD1_0 && !WINDOWS_UWP
- [Serializable]
+[Serializable]
#endif
- public class InvalidManagedNameException :
- Exception
+public class InvalidManagedNameException :
+ Exception
#if !NETSTANDARD1_0 && !WINDOWS_UWP
- , ISerializable
+ , ISerializable
#endif
- {
- public InvalidManagedNameException(string message) : base(message) { }
+{
+ public InvalidManagedNameException(string message) : base(message) { }
#if !NETSTANDARD1_0 && !WINDOWS_UWP
- protected InvalidManagedNameException(SerializationInfo info, StreamingContext context) : base(info, context) { }
+ protected InvalidManagedNameException(SerializationInfo info, StreamingContext context) : base(info, context) { }
#endif
- }
-}
\ No newline at end of file
+}
diff --git a/src/Microsoft.TestPlatform.AdapterUtilities/ManagedNameUtilities/ManagedNameHelper.Reflection.cs b/src/Microsoft.TestPlatform.AdapterUtilities/ManagedNameUtilities/ManagedNameHelper.Reflection.cs
index 5d37a39f80..cb7d4714f6 100644
--- a/src/Microsoft.TestPlatform.AdapterUtilities/ManagedNameUtilities/ManagedNameHelper.Reflection.cs
+++ b/src/Microsoft.TestPlatform.AdapterUtilities/ManagedNameUtilities/ManagedNameHelper.Reflection.cs
@@ -1,574 +1,572 @@
// 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.TestPlatform.AdapterUtilities.ManagedNameUtilities
-{
- using Microsoft.TestPlatform.AdapterUtilities.Resources;
- using Microsoft.TestPlatform.AdapterUtilities.Helpers;
+namespace Microsoft.TestPlatform.AdapterUtilities.ManagedNameUtilities;
+
+using Microsoft.TestPlatform.AdapterUtilities.Resources;
+using Microsoft.TestPlatform.AdapterUtilities.Helpers;
- using System;
- using System.Globalization;
- using System.Reflection;
- using System.Text;
+using System;
+using System.Globalization;
+using System.Reflection;
+using System.Text;
#if !NET20
- using System.Linq;
+using System.Linq;
#endif
- public static partial class ManagedNameHelper
+public static partial class ManagedNameHelper
+{
+ ///
+ /// Gets fully qualified managed type and method name from given instance.
+ ///
+ ///
+ /// A instance to get fully qualified managed type and method name.
+ ///
+ ///
+ /// When this method returns, contains the fully qualified managed type name of the .
+ /// This parameter is passed uninitialized; any value originally supplied in result will be overwritten.
+ /// The format is defined in the RFC.
+ ///
+ ///
+ /// When this method returns, contains the fully qualified managed method name of the .
+ /// This parameter is passed uninitialized; any value originally supplied in result will be overwritten.
+ /// The format is defined in the RFC.
+ ///
+ ///
+ /// is null.
+ ///
+ ///
+ /// must describe a method.
+ ///
+ ///
+ /// Required functionality on is missing on the current platform.
+ ///
+ ///
+ /// More information about and can be found in
+ /// the RFC.
+ ///
+ public static void GetManagedName(MethodBase method, out string managedTypeName, out string managedMethodName)
+ => GetManagedName(method, out managedTypeName, out managedMethodName, out _);
+
+ ///
+ /// Gets fully qualified managed type and method name from given instance.
+ ///
+ ///
+ /// A instance to get fully qualified managed type and method name.
+ ///
+ ///
+ /// When this method returns, contains the fully qualified managed type name of the .
+ /// This parameter is passed uninitialized; any value originally supplied in result will be overwritten.
+ /// The format is defined in the RFC.
+ ///
+ ///
+ /// When this method returns, contains the fully qualified managed method name of the .
+ /// This parameter is passed uninitialized; any value originally supplied in result will be overwritten.
+ /// The format is defined in the RFC.
+ ///
+ ///
+ /// When this method returns, contains the default test hierarchy values of the .
+ /// This parameter is passed uninitialized; any value originally supplied in result will be overwritten.
+ ///
+ ///
+ /// is null.
+ ///
+ ///
+ /// must describe a method.
+ ///
+ ///
+ /// Required functionality on is missing on the current platform.
+ ///
+ ///
+ /// More information about and can be found in
+ /// the RFC.
+ ///
+ public static void GetManagedName(MethodBase method, out string managedTypeName, out string managedMethodName, out string[] hierarchyValues)
{
- ///
- /// Gets fully qualified managed type and method name from given instance.
- ///
- ///
- /// A instance to get fully qualified managed type and method name.
- ///
- ///
- /// When this method returns, contains the fully qualified managed type name of the .
- /// This parameter is passed uninitialized; any value originally supplied in result will be overwritten.
- /// The format is defined in the RFC.
- ///
- ///
- /// When this method returns, contains the fully qualified managed method name of the .
- /// This parameter is passed uninitialized; any value originally supplied in result will be overwritten.
- /// The format is defined in the RFC.
- ///
- ///
- /// is null.
- ///
- ///
- /// must describe a method.
- ///
- ///
- /// Required functionality on is missing on the current platform.
- ///
- ///
- /// More information about and can be found in
- /// the RFC.
- ///
- public static void GetManagedName(MethodBase method, out string managedTypeName, out string managedMethodName)
- => GetManagedName(method, out managedTypeName, out managedMethodName, out _);
-
- ///
- /// Gets fully qualified managed type and method name from given instance.
- ///
- ///
- /// A instance to get fully qualified managed type and method name.
- ///
- ///
- /// When this method returns, contains the fully qualified managed type name of the .
- /// This parameter is passed uninitialized; any value originally supplied in result will be overwritten.
- /// The format is defined in the RFC.
- ///
- ///
- /// When this method returns, contains the fully qualified managed method name of the .
- /// This parameter is passed uninitialized; any value originally supplied in result will be overwritten.
- /// The format is defined in the RFC.
- ///
- ///
- /// When this method returns, contains the default test hierarchy values of the .
- /// This parameter is passed uninitialized; any value originally supplied in result will be overwritten.
- ///
- ///
- /// is null.
- ///
- ///
- /// must describe a method.
- ///
- ///
- /// Required functionality on is missing on the current platform.
- ///
- ///
- /// More information about and can be found in
- /// the RFC.
- ///
- public static void GetManagedName(MethodBase method, out string managedTypeName, out string managedMethodName, out string[] hierarchyValues)
- {
- if (method == null)
- {
- throw new ArgumentNullException(nameof(method));
- }
+ if (method == null)
+ {
+ throw new ArgumentNullException(nameof(method));
+ }
- if (!ReflectionHelpers.IsMethod(method))
- {
- throw new NotSupportedException(nameof(method));
- }
+ if (!ReflectionHelpers.IsMethod(method))
+ {
+ throw new NotSupportedException(nameof(method));
+ }
- var semanticType = ReflectionHelpers.GetReflectedType(method);
- if (ReflectionHelpers.IsGenericType(semanticType))
- {
- // The type might have some of its generic parameters specified, so make
- // sure we are working with the open form of the generic type.
- semanticType = semanticType.GetGenericTypeDefinition();
-
- // The method might have some of its parameters specified by the original closed type
- // declaration. Here we use the method handle (basically metadata token) to create
- // a new method reference using the open form of the reflected type. The intent is
- // to strip all generic type parameters.
- var methodHandle = ReflectionHelpers.GetMethodHandle(method);
- method = MethodBase.GetMethodFromHandle(methodHandle, semanticType.TypeHandle);
- }
+ var semanticType = ReflectionHelpers.GetReflectedType(method);
+ if (ReflectionHelpers.IsGenericType(semanticType))
+ {
+ // The type might have some of its generic parameters specified, so make
+ // sure we are working with the open form of the generic type.
+ semanticType = semanticType.GetGenericTypeDefinition();
+
+ // The method might have some of its parameters specified by the original closed type
+ // declaration. Here we use the method handle (basically metadata token) to create
+ // a new method reference using the open form of the reflected type. The intent is
+ // to strip all generic type parameters.
+ var methodHandle = ReflectionHelpers.GetMethodHandle(method);
+ method = MethodBase.GetMethodFromHandle(methodHandle, semanticType.TypeHandle);
+ }
- if (method.IsGenericMethod)
- {
- // If this method is generic, then convert to the generic method definition
- // so that we get the open generic type definitions for parameters.
- method = ((MethodInfo)method).GetGenericMethodDefinition();
- }
+ if (method.IsGenericMethod)
+ {
+ // If this method is generic, then convert to the generic method definition
+ // so that we get the open generic type definitions for parameters.
+ method = ((MethodInfo)method).GetGenericMethodDefinition();
+ }
- var typeBuilder = new StringBuilder();
- var methodBuilder = new StringBuilder();
+ var typeBuilder = new StringBuilder();
+ var methodBuilder = new StringBuilder();
- // Namespace and Type Name (with arity designation)
- var hierarchyPos = AppendTypeString(typeBuilder, semanticType, closedType: false);
+ // Namespace and Type Name (with arity designation)
+ var hierarchyPos = AppendTypeString(typeBuilder, semanticType, closedType: false);
- // Method Name with method arity
- var arity = method.GetGenericArguments().Length;
- AppendMethodString(methodBuilder, method.Name, arity);
- if (arity > 0)
- {
- methodBuilder.Append('`');
- methodBuilder.Append(arity);
- }
+ // Method Name with method arity
+ var arity = method.GetGenericArguments().Length;
+ AppendMethodString(methodBuilder, method.Name, arity);
+ if (arity > 0)
+ {
+ methodBuilder.Append('`');
+ methodBuilder.Append(arity);
+ }
- // Type Parameters
- var paramList = method.GetParameters();
- if (paramList.Length != 0)
+ // Type Parameters
+ var paramList = method.GetParameters();
+ if (paramList.Length != 0)
+ {
+ methodBuilder.Append('(');
+ foreach (var p in paramList)
{
- methodBuilder.Append('(');
- foreach (var p in paramList)
- {
- AppendTypeString(methodBuilder, p.ParameterType, closedType: true);
- methodBuilder.Append(',');
- }
- // Replace the last ',' with ')'
- methodBuilder[methodBuilder.Length - 1] = ')';
+ AppendTypeString(methodBuilder, p.ParameterType, closedType: true);
+ methodBuilder.Append(',');
}
+ // Replace the last ',' with ')'
+ methodBuilder[methodBuilder.Length - 1] = ')';
+ }
- managedTypeName = typeBuilder.ToString();
- managedMethodName = methodBuilder.ToString();
- hierarchyValues = new[] {
+ managedTypeName = typeBuilder.ToString();
+ managedMethodName = methodBuilder.ToString();
+ hierarchyValues = new[] {
managedTypeName.Substring(hierarchyPos[0], hierarchyPos[1] - hierarchyPos[0]),
managedTypeName.Substring(hierarchyPos[1] + 1, hierarchyPos[2] - hierarchyPos[1] - 1),
};
- }
+ }
+
+ ///
+ /// Gets the object with the specified
+ /// and in the instance.
+ ///
+ ///
+ /// An instance to search in.
+ ///
+ ///
+ /// The fully qualified managed name of the type.
+ /// The format is defined in the RFC.
+ ///
+ ///
+ /// The fully qualified managed name of the method.
+ /// The format is defined in the RFC.
+ ///
+ ///
+ /// A object that represents specified parameters, throws if null.
+ ///
+ ///
+ /// Values specified with and
+ /// does not correspond to a method in the instance, or malformed.
+ ///
+ ///
+ /// More information about and can be found in
+ /// the RFC.
+ ///
+ public static MethodBase GetMethod(Assembly assembly, string managedTypeName, string managedMethodName)
+ {
+ Type type;
- ///
- /// Gets the object with the specified
- /// and in the instance.
- ///
- ///
- /// An instance to search in.
- ///
- ///
- /// The fully qualified managed name of the type.
- /// The format is defined in the RFC.
- ///
- ///
- /// The fully qualified managed name of the method.
- /// The format is defined in the RFC.
- ///
- ///
- /// A object that represents specified parameters, throws if null.
- ///
- ///
- /// Values specified with and
- /// does not correspond to a method in the instance, or malformed.
- ///
- ///
- /// More information about and can be found in
- /// the RFC.
- ///
- public static MethodBase GetMethod(Assembly assembly, string managedTypeName, string managedMethodName)
- {
- Type type;
-
- var parsedManagedTypeName = ReflectionHelpers.ParseEscapedString(managedTypeName);
+ var parsedManagedTypeName = ReflectionHelpers.ParseEscapedString(managedTypeName);
#if !NETSTANDARD1_0 && !NETSTANDARD1_3 && !WINDOWS_UWP
- type = assembly.GetType(parsedManagedTypeName, throwOnError: false, ignoreCase: false);
+ type = assembly.GetType(parsedManagedTypeName, throwOnError: false, ignoreCase: false);
#else
- try
- {
- type = assembly.GetType(parsedManagedTypeName);
- }
- catch
- {
- type = null;
- }
+ try
+ {
+ type = assembly.GetType(parsedManagedTypeName);
+ }
+ catch
+ {
+ type = null;
+ }
#endif
- if (type == null)
- {
- string message = string.Format(CultureInfo.CurrentCulture, Resources.ErrorTypeNotFound, parsedManagedTypeName);
- throw new InvalidManagedNameException(message);
- }
+ if (type == null)
+ {
+ string message = string.Format(CultureInfo.CurrentCulture, Resources.ErrorTypeNotFound, parsedManagedTypeName);
+ throw new InvalidManagedNameException(message);
+ }
- MethodInfo method = null;
- ManagedNameParser.ParseManagedMethodName(managedMethodName, out var methodName, out var methodArity, out var parameterTypes);
+ MethodInfo method = null;
+ ManagedNameParser.ParseManagedMethodName(managedMethodName, out var methodName, out var methodArity, out var parameterTypes);
#if NET20 || NET35
- if (!IsNullOrWhiteSpace(methodName))
+ if (!IsNullOrWhiteSpace(methodName))
#else
- if (!string.IsNullOrWhiteSpace(methodName))
+ if (!string.IsNullOrWhiteSpace(methodName))
#endif
+ {
+ method = FindMethod(type, methodName, methodArity, parameterTypes);
+ }
+
+ if (method == null)
+ {
+ string message = string.Format(CultureInfo.CurrentCulture, Resources.ErrorMethodNotFound, methodName, managedTypeName);
+ throw new InvalidManagedNameException(message);
+ }
+
+ return method;
+ }
+
+ private static MethodInfo FindMethod(Type type, string methodName, int methodArity, string[] parameterTypes)
+ {
+ bool Filter(MemberInfo mbr, object param)
+ {
+ var method = mbr as MethodInfo;
+ if (method.Name != methodName || method.GetGenericArguments().Length != methodArity)
{
- method = FindMethod(type, methodName, methodArity, parameterTypes);
+ return false;
}
- if (method == null)
+ var paramList = method.GetParameters();
+ if (paramList.Length == 0 && parameterTypes == null)
{
- string message = string.Format(CultureInfo.CurrentCulture, Resources.ErrorMethodNotFound, methodName, managedTypeName);
- throw new InvalidManagedNameException(message);
+ return true;
}
-
- return method;
- }
-
- private static MethodInfo FindMethod(Type type, string methodName, int methodArity, string[] parameterTypes)
- {
- bool filter(MemberInfo mbr, object param)
+ else if (parameterTypes == null || paramList.Length != parameterTypes.Length)
{
- var method = mbr as MethodInfo;
- if (method.Name != methodName || method.GetGenericArguments().Length != methodArity)
- {
- return false;
- }
+ return false;
+ }
- var paramList = method.GetParameters();
- if (paramList.Length == 0 && parameterTypes == null)
- {
- return true;
- }
- else if (parameterTypes == null || paramList.Length != parameterTypes.Length)
+ for (int i = 0; i < paramList.Length; i++)
+ {
+ if (GetTypeString(paramList[i].ParameterType, closedType: true) != parameterTypes[i])
{
return false;
}
-
- for (int i = 0; i < paramList.Length; i++)
- {
- if (GetTypeString(paramList[i].ParameterType, closedType: true) != parameterTypes[i])
- {
- return false;
- }
- }
-
- return true;
}
- MemberInfo[] methods;
+ return true;
+ }
+
+ MemberInfo[] methods;
#if !NETSTANDARD1_0 && !NETSTANDARD1_3 && !WINDOWS_UWP
- var bindingFlags = BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Static;
- methods = type.FindMembers(MemberTypes.Method, bindingFlags, filter, null);
+ var bindingFlags = BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Static;
+ methods = type.FindMembers(MemberTypes.Method, bindingFlags, Filter, null);
#else
- methods = type.GetRuntimeMethods().Where(m => filter(m, null)).ToArray();
+ methods = type.GetRuntimeMethods().Where(m => Filter(m, null)).ToArray();
#endif
#if NET20
- return (MethodInfo)SingleOrDefault(methods);
+ return (MethodInfo)SingleOrDefault(methods);
#else
- return (MethodInfo)methods.SingleOrDefault();
+ return (MethodInfo)methods.SingleOrDefault();
#endif
- }
+ }
- private static int[] AppendTypeString(StringBuilder b, Type type, bool closedType)
- {
- int[] hierarchies = null;
+ private static int[] AppendTypeString(StringBuilder b, Type type, bool closedType)
+ {
+ int[] hierarchies = null;
- if (type.IsArray)
+ if (type.IsArray)
+ {
+ hierarchies = AppendTypeString(b, type.GetElementType(), closedType);
+ b.Append('[');
+ for (int i = 0; i < type.GetArrayRank() - 1; i++)
{
- hierarchies = AppendTypeString(b, type.GetElementType(), closedType);
- b.Append('[');
- for (int i = 0; i < type.GetArrayRank() - 1; i++)
- {
- b.Append(',');
- }
- b.Append(']');
+ b.Append(',');
}
- else if (type.IsGenericParameter)
+ b.Append(']');
+ }
+ else if (type.IsGenericParameter)
+ {
+ if (ReflectionHelpers.GetDeclaringMethod(type) != null)
{
- if (ReflectionHelpers.GetDeclaringMethod(type) != null)
- {
- b.Append('!');
- }
b.Append('!');
- b.Append(type.GenericParameterPosition);
}
- else
- {
- hierarchies = new int[3];
- hierarchies[0] = b.Length;
+ b.Append('!');
+ b.Append(type.GenericParameterPosition);
+ }
+ else
+ {
+ hierarchies = new int[3];
+ hierarchies[0] = b.Length;
- AppendNamespace(b, type.Namespace);
- hierarchies[1] = b.Length;
+ AppendNamespace(b, type.Namespace);
+ hierarchies[1] = b.Length;
- b.Append('.');
+ b.Append('.');
- AppendNestedTypeName(b, type);
- if (closedType)
- {
- AppendGenericTypeParameters(b, type);
- }
- hierarchies[2] = b.Length;
+ AppendNestedTypeName(b, type);
+ if (closedType)
+ {
+ AppendGenericTypeParameters(b, type);
}
-
- return hierarchies;
+ hierarchies[2] = b.Length;
}
- private static void AppendNamespace(StringBuilder b, string namespaceString)
- {
- int start = 0;
- bool shouldEscape = false;
+ return hierarchies;
+ }
- for (int i = 0; i <= namespaceString.Length; i++)
+ private static void AppendNamespace(StringBuilder b, string namespaceString)
+ {
+ int start = 0;
+ bool shouldEscape = false;
+
+ for (int i = 0; i <= namespaceString.Length; i++)
+ {
+ if (i == namespaceString.Length || namespaceString[i] == '.')
{
- if (i == namespaceString.Length || namespaceString[i] == '.')
+ if (start != 0)
{
- if (start != 0)
- {
- b.Append('.');
- }
-
- var part = namespaceString.Substring(start, i - start);
- if (shouldEscape)
- {
- NormalizeAndAppendString(b, part);
- shouldEscape = false;
- }
- else
- {
- b.Append(part);
- }
-
- start = i + 1;
- continue;
+ b.Append('.');
}
- shouldEscape = shouldEscape || NeedsEscaping(namespaceString[i], i - start);
+ var part = namespaceString.Substring(start, i - start);
+ if (shouldEscape)
+ {
+ NormalizeAndAppendString(b, part);
+ shouldEscape = false;
+ }
+ else
+ {
+ b.Append(part);
+ }
+
+ start = i + 1;
+ continue;
}
+
+ shouldEscape = shouldEscape || NeedsEscaping(namespaceString[i], i - start);
}
+ }
- private static void AppendMethodString(StringBuilder methodBuilder, string name, int methodArity)
+ private static void AppendMethodString(StringBuilder methodBuilder, string name, int methodArity)
+ {
+ var arityStart = name.LastIndexOf('`');
+ var arity = 0;
+ if (arityStart > 0)
{
- var arityStart = name.LastIndexOf('`');
- var arity = 0;
- if (arityStart > 0)
+ arityStart++;
+ var arityString = name.Substring(arityStart, name.Length - arityStart);
+ if (int.TryParse(arityString, out arity))
{
- arityStart++;
- var arityString = name.Substring(arityStart, name.Length - arityStart);
- if (int.TryParse(arityString, out arity))
+ if (arity == methodArity)
{
- if (arity == methodArity)
- {
- name = name.Substring(0, arityStart - 1);
- }
+ name = name.Substring(0, arityStart - 1);
}
}
+ }
- if (IsNormalized(name))
- {
- methodBuilder.Append(name);
- }
- else
- {
- NormalizeAndAppendString(methodBuilder, name);
- }
+ if (IsNormalized(name))
+ {
+ methodBuilder.Append(name);
+ }
+ else
+ {
+ NormalizeAndAppendString(methodBuilder, name);
+ }
- if (arity > 0 && methodArity == arity)
- {
- methodBuilder.Append($"`{arity}");
- }
+ if (arity > 0 && methodArity == arity)
+ {
+ methodBuilder.Append($"`{arity}");
}
+ }
- private static void NormalizeAndAppendString(StringBuilder b, string name)
+ private static void NormalizeAndAppendString(StringBuilder b, string name)
+ {
+ b.Append('\'');
+ for (int i = 0; i < name.Length; i++)
{
- b.Append('\'');
- for (int i = 0; i < name.Length; i++)
+ char c = name[i];
+ if (NeedsEscaping(c, i))
{
- char c = name[i];
- if (NeedsEscaping(c, i))
+ if (c == '\\' || c == '\'')
{
- if (c == '\\' || c == '\'')
- {
- // var encoded = Convert.ToString(((uint)c), 16);
- // b.Append("\\u");
- // b.Append('0', 4 - encoded.Length);
- // b.Append(encoded);
-
- b.Append('\\');
- b.Append(c);
- continue;
- }
- }
+ // var encoded = Convert.ToString(((uint)c), 16);
+ // b.Append("\\u");
+ // b.Append('0', 4 - encoded.Length);
+ // b.Append(encoded);
- b.Append(c);
+ b.Append('\\');
+ b.Append(c);
+ continue;
+ }
}
- b.Append('\'');
+
+ b.Append(c);
}
+ b.Append('\'');
+ }
- private static int AppendNestedTypeName(StringBuilder b, Type type)
+ private static int AppendNestedTypeName(StringBuilder b, Type type)
+ {
+ var outerArity = 0;
+ if (type.IsNested)
{
- var outerArity = 0;
- if (type.IsNested)
- {
- outerArity = AppendNestedTypeName(b, type.DeclaringType);
- b.Append('+');
- }
+ outerArity = AppendNestedTypeName(b, type.DeclaringType);
+ b.Append('+');
+ }
- var typeName = type.Name;
- var stars = 0;
- if (type.IsPointer)
+ var typeName = type.Name;
+ var stars = 0;
+ if (type.IsPointer)
+ {
+ for (int i = typeName.Length - 1; i > 0; i--)
{
- for (int i = typeName.Length - 1; i > 0; i--)
+ if (typeName[i] != '*')
{
- if (typeName[i] != '*')
- {
- stars = typeName.Length - i - 1;
- typeName = typeName.Substring(0, i + 1);
- break;
- }
+ stars = typeName.Length - i - 1;
+ typeName = typeName.Substring(0, i + 1);
+ break;
}
}
+ }
- var info = type.GetTypeInfo();
- var arity = !info.IsGenericType
- ? 0
- : info.GenericTypeParameters.Length > 0
- ? info.GenericTypeParameters.Length
- : info.GenericTypeArguments.Length;
+ var info = type.GetTypeInfo();
+ var arity = !info.IsGenericType
+ ? 0
+ : info.GenericTypeParameters.Length > 0
+ ? info.GenericTypeParameters.Length
+ : info.GenericTypeArguments.Length;
- AppendMethodString(b, typeName, arity - outerArity);
- b.Append('*', stars);
- return arity;
- }
+ AppendMethodString(b, typeName, arity - outerArity);
+ b.Append('*', stars);
+ return arity;
+ }
- private static void AppendGenericTypeParameters(StringBuilder b, Type type)
- {
- Type[] genargs;
+ private static void AppendGenericTypeParameters(StringBuilder b, Type type)
+ {
+ Type[] genargs;
#if !NETSTANDARD1_0 && !NETSTANDARD1_3 && !WINDOWS_UWP
- genargs = type.GetGenericArguments();
+ genargs = type.GetGenericArguments();
#else
- genargs = type.GetTypeInfo().GenericTypeArguments;
+ genargs = type.GetTypeInfo().GenericTypeArguments;
#endif
- if (genargs.Length != 0)
- {
- b.Append('<');
- foreach (var argType in genargs)
- {
- AppendTypeString(b, argType, closedType: true);
- b.Append(',');
- }
- // Replace the last ',' with '>'
- b[b.Length - 1] = '>';
- }
- }
-
- private static bool IsNormalized(string s)
+ if (genargs.Length != 0)
{
- for (int i = 0; i < s.Length; i++)
+ b.Append('<');
+ foreach (var argType in genargs)
{
- if (NeedsEscaping(s[i], i) && s[i] != '.')
- {
- return false;
- }
+ AppendTypeString(b, argType, closedType: true);
+ b.Append(',');
}
-
- return true;
+ // Replace the last ',' with '>'
+ b[b.Length - 1] = '>';
}
+ }
- private static bool NeedsEscaping(char c, int pos)
+ private static bool IsNormalized(string s)
+ {
+ for (int i = 0; i < s.Length; i++)
{
- if (pos == 0 && char.IsDigit(c))
- {
- return true;
- }
-
- if (c == '_'
- || char.IsLetterOrDigit(c) // Lu, Ll, Lt, Lm, Lo, or Nl
- )
+ if (NeedsEscaping(s[i], i) && s[i] != '.')
{
return false;
}
+ }
- var category = CharUnicodeInfo.GetUnicodeCategory(c);
- if (category == UnicodeCategory.NonSpacingMark // Mn
- || category == UnicodeCategory.SpacingCombiningMark // Mc
- || category == UnicodeCategory.ConnectorPunctuation // Pc
- || category == UnicodeCategory.Format) // Cf
- {
- return false;
- }
+ return true;
+ }
+ private static bool NeedsEscaping(char c, int pos)
+ {
+ if (pos == 0 && char.IsDigit(c))
+ {
return true;
}
- private static string GetTypeString(Type type, bool closedType)
+ if (c == '_'
+ || char.IsLetterOrDigit(c) // Lu, Ll, Lt, Lm, Lo, or Nl
+ )
+ {
+ return false;
+ }
+
+ var category = CharUnicodeInfo.GetUnicodeCategory(c);
+ if (category == UnicodeCategory.NonSpacingMark // Mn
+ || category == UnicodeCategory.SpacingCombiningMark // Mc
+ || category == UnicodeCategory.ConnectorPunctuation // Pc
+ || category == UnicodeCategory.Format) // Cf
{
- var builder = new StringBuilder();
- AppendTypeString(builder, type, closedType);
- return builder.ToString();
+ return false;
}
+ return true;
+ }
+
+ private static string GetTypeString(Type type, bool closedType)
+ {
+ var builder = new StringBuilder();
+ AppendTypeString(builder, type, closedType);
+ return builder.ToString();
+ }
+
#if NET20
+ // the method is mostly copied from
+ // https://github.com/dotnet/runtime/blob/c0840723b382bcfa67b35839af8572fcd38f1d13/src/libraries/System.Linq/src/System/Linq/Single.cs#L86
+ public static TSource SingleOrDefault(System.Collections.Generic.IEnumerable source)
+ {
+ if (source == null)
+ {
+ throw new ArgumentNullException(nameof(source));
+ }
- // the method is mostly copied from
- // https://github.com/dotnet/runtime/blob/c0840723b382bcfa67b35839af8572fcd38f1d13/src/libraries/System.Linq/src/System/Linq/Single.cs#L86
- public static TSource SingleOrDefault(System.Collections.Generic.IEnumerable source)
+ if (source is System.Collections.Generic.IList list)
{
- if (source == null)
+ switch (list.Count)
{
- throw new ArgumentNullException(nameof(source));
+ case 0:
+ return default;
+ case 1:
+ return list[0];
}
-
- if (source is System.Collections.Generic.IList list)
+ }
+ else
+ {
+ using (System.Collections.Generic.IEnumerator e = source.GetEnumerator())
{
- switch (list.Count)
+ if (!e.MoveNext())
{
- case 0:
- return default;
- case 1:
- return list[0];
+ return default;
}
- }
- else
- {
- using (System.Collections.Generic.IEnumerator e = source.GetEnumerator())
+
+ TSource result = e.Current;
+ if (!e.MoveNext())
{
- if (!e.MoveNext())
- {
- return default;
- }
-
- TSource result = e.Current;
- if (!e.MoveNext())
- {
- return result;
- }
+ return result;
}
}
-
- throw new InvalidOperationException("MoreThanOneElement");
}
+
+ throw new InvalidOperationException("MoreThanOneElement");
+ }
#endif
#if NET20 || NET35
- public static bool IsNullOrWhiteSpace(string value)
- {
- if (value is null) return true;
+ public static bool IsNullOrWhiteSpace(string value)
+ {
+ if (value is null) return true;
- for (int i = 0; i < value.Length; i++)
+ for (int i = 0; i < value.Length; i++)
+ {
+ if (!char.IsWhiteSpace(value[i]))
{
- if (!char.IsWhiteSpace(value[i]))
- {
- return false;
- }
+ return false;
}
-
- return true;
}
-#endif
+
+ return true;
}
+#endif
}
diff --git a/src/Microsoft.TestPlatform.AdapterUtilities/ManagedNameUtilities/ManagedNameParser.cs b/src/Microsoft.TestPlatform.AdapterUtilities/ManagedNameUtilities/ManagedNameParser.cs
index 96226f732e..e3a90e1759 100644
--- a/src/Microsoft.TestPlatform.AdapterUtilities/ManagedNameUtilities/ManagedNameParser.cs
+++ b/src/Microsoft.TestPlatform.AdapterUtilities/ManagedNameUtilities/ManagedNameParser.cs
@@ -1,267 +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.TestPlatform.AdapterUtilities.ManagedNameUtilities
+namespace Microsoft.TestPlatform.AdapterUtilities.ManagedNameUtilities;
+
+using Microsoft.TestPlatform.AdapterUtilities.Helpers;
+using Microsoft.TestPlatform.AdapterUtilities.Resources;
+
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.Globalization;
+
+public class ManagedNameParser
{
- using Microsoft.TestPlatform.AdapterUtilities.Helpers;
- using Microsoft.TestPlatform.AdapterUtilities.Resources;
+ ///
+ /// Parses a given fully qualified managed type name into its namespace and type name.
+ ///
+ ///
+ /// The fully qualified managed type name to parse.
+ /// The format is defined in the RFC.
+ ///
+ ///
+ /// When this method returns, contains the parsed namespace name of the .
+ /// This parameter is passed uninitialized; any value originally supplied in result will be overwritten.
+ ///
+ ///
+ /// When this method returns, contains the parsed type name of the .
+ /// This parameter is passed uninitialized; any value originally supplied in result will be overwritten.
+ ///
+ public static void ParseManagedTypeName(string managedTypeName, out string namespaceName, out string typeName)
+ {
+ int pos = managedTypeName.LastIndexOf('.');
+ if (pos == -1)
+ {
+ namespaceName = string.Empty;
+ typeName = managedTypeName;
+ }
+ else
+ {
+ namespaceName = managedTypeName.Substring(0, pos);
+ typeName = managedTypeName.Substring(pos + 1);
+ }
+ }
+
+ ///
+ /// Parses a given fully qualified managed method name into its name, arity and parameter types.
+ ///
+ ///
+ /// The fully qualified managed method name to parse.
+ /// The format is defined in the RFC.
+ ///
+ ///
+ /// When this method returns, contains the parsed method name of the .
+ /// This parameter is passed uninitialized; any value originally supplied in result will be overwritten.
+ ///
+ ///
+ /// When this method returns, contains the parsed arity of the .
+ /// This parameter is passed uninitialized; any value originally supplied in result will be overwritten.
+ ///
+ ///
+ /// When this method returns, contains the parsed parameter types of the .
+ /// If there are no parameter types in , is set to null.
+ /// This parameter is passed uninitialized; any value originally supplied in result will be overwritten.
+ ///
+ ///
+ /// Thrown if contains spaces, incomplete, or the arity isn't numeric.
+ ///
+ public static void ParseManagedMethodName(string managedMethodName, out string methodName, out int arity, out string[] parameterTypes)
+ {
+ int pos = ParseMethodName(managedMethodName, 0, out var escapedMethodName, out arity);
+ methodName = ReflectionHelpers.ParseEscapedString(escapedMethodName);
+ pos = ParseParameterTypeList(managedMethodName, pos, out parameterTypes);
+ if (pos != managedMethodName.Length)
+ {
+ string message = string.Format(CultureInfo.CurrentCulture, Resources.ErrorUnexpectedCharactersAtEnd, pos);
+ throw new InvalidManagedNameException(message);
+ }
+ }
- using System.Collections.Generic;
- using System.Diagnostics;
- using System.Globalization;
+ private static string Capture(string managedMethodName, int start, int end)
+ => managedMethodName.Substring(start, end - start);
- public class ManagedNameParser
+ private static int ParseMethodName(string managedMethodName, int start, out string methodName, out int arity)
{
- ///
- /// Parses a given fully qualified managed type name into its namespace and type name.
- ///
- ///
- /// The fully qualified managed type name to parse.
- /// The format is defined in the RFC.
- ///
- ///
- /// When this method returns, contains the parsed namespace name of the .
- /// This parameter is passed uninitialized; any value originally supplied in result will be overwritten.
- ///
- ///
- /// When this method returns, contains the parsed type name of the .
- /// This parameter is passed uninitialized; any value originally supplied in result will be overwritten.
- ///
- public static void ParseManagedTypeName(string managedTypeName, out string namespaceName, out string typeName)
+ var i = start;
+ var quoted = false;
+ for (; i < managedMethodName.Length; i++)
{
- int pos = managedTypeName.LastIndexOf('.');
- if (pos == -1)
+ var c = managedMethodName[i];
+ if (c == '\'' || quoted)
{
- namespaceName = string.Empty;
- typeName = managedTypeName;
+ quoted = c == '\'' ? !quoted : quoted;
+ continue;
}
- else
+
+ switch (c)
{
- namespaceName = managedTypeName.Substring(0, pos);
- typeName = managedTypeName.Substring(pos + 1);
+ case var w when char.IsWhiteSpace(w):
+ string message = string.Format(CultureInfo.CurrentCulture, Resources.ErrorWhitespaceNotValid, i);
+ throw new InvalidManagedNameException(message);
+
+ case '`':
+ methodName = Capture(managedMethodName, start, i);
+ return ParseArity(managedMethodName, i, out arity);
+
+ case '(':
+ methodName = Capture(managedMethodName, start, i);
+ arity = 0;
+ return i;
}
}
+ methodName = Capture(managedMethodName, start, i);
+ arity = 0;
+ return i;
+ }
- ///
- /// Parses a given fully qualified managed method name into its name, arity and parameter types.
- ///
- ///
- /// The fully qualified managed method name to parse.
- /// The format is defined in the RFC.
- ///
- ///
- /// When this method returns, contains the parsed method name of the .
- /// This parameter is passed uninitialized; any value originally supplied in result will be overwritten.
- ///
- ///
- /// When this method returns, contains the parsed arity of the .
- /// This parameter is passed uninitialized; any value originally supplied in result will be overwritten.
- ///
- ///
- /// When this method returns, contains the parsed parameter types of the .
- /// If there are no parameter types in , is set to null.
- /// This parameter is passed uninitialized; any value originally supplied in result will be overwritten.
- ///
- ///
- /// Thrown if contains spaces, incomplete, or the arity isn't numeric.
- ///
- public static void ParseManagedMethodName(string managedMethodName, out string methodName, out int arity, out string[] parameterTypes)
+ // parse arity in the form `nn where nn is an integer value.
+ private static int ParseArity(string managedMethodName, int start, out int arity)
+ {
+ Debug.Assert(managedMethodName[start] == '`');
+
+ int i = start + 1; // skip initial '`' char
+ for (; i < managedMethodName.Length; i++)
{
- int pos = ParseMethodName(managedMethodName, 0, out var escapedMethodName, out arity);
- methodName = ReflectionHelpers.ParseEscapedString(escapedMethodName);
- pos = ParseParameterTypeList(managedMethodName, pos, out parameterTypes);
- if (pos != managedMethodName.Length)
- {
- string message = string.Format(CultureInfo.CurrentCulture, Resources.ErrorUnexpectedCharactersAtEnd, pos);
- throw new InvalidManagedNameException(message);
- }
+ if (managedMethodName[i] == '(') break;
+ }
+ if (!int.TryParse(Capture(managedMethodName, start + 1, i), out arity))
+ {
+ string message = string.Format(CultureInfo.CurrentCulture, Resources.ErrorMethodArityMustBeNumeric);
+ throw new InvalidManagedNameException(message);
+ }
+ return i;
+ }
+
+ private static int ParseParameterTypeList(string managedMethodName, int start, out string[] parameterTypes)
+ {
+ parameterTypes = null;
+ if (start == managedMethodName.Length)
+ {
+ return start;
}
+ Debug.Assert(managedMethodName[start] == '(');
- private static string Capture(string managedMethodName, int start, int end)
- => managedMethodName.Substring(start, end - start);
+ var types = new List();
- private static int ParseMethodName(string managedMethodName, int start, out string methodName, out int arity)
+ int i = start + 1; // skip initial '(' char
+ for (; i < managedMethodName.Length; i++)
{
- var i = start;
- var quoted = false;
- for (; i < managedMethodName.Length; i++)
+ switch (managedMethodName[i])
{
- var c = managedMethodName[i];
- if (c == '\'' || quoted)
- {
- quoted = c == '\'' ? !quoted : quoted;
- continue;
- }
-
- switch (c)
- {
- case var w when char.IsWhiteSpace(w):
- string message = string.Format(CultureInfo.CurrentCulture, Resources.ErrorWhitespaceNotValid, i);
- throw new InvalidManagedNameException(message);
-
- case '`':
- methodName = Capture(managedMethodName, start, i);
- return ParseArity(managedMethodName, i, out arity);
-
- case '(':
- methodName = Capture(managedMethodName, start, i);
- arity = 0;
- return i;
- }
+ case ')':
+ if (types.Count != 0)
+ {
+ parameterTypes = types.ToArray();
+ }
+ return i + 1; // consume right parens
+
+ case ',':
+ break;
+
+ default:
+ i = ParseParameterType(managedMethodName, i, out var parameterType);
+ types.Add(parameterType);
+ break;
}
- methodName = Capture(managedMethodName, start, i);
- arity = 0;
- return i;
}
- // parse arity in the form `nn where nn is an integer value.
- private static int ParseArity(string managedMethodName, int start, out int arity)
- {
- arity = 0;
- Debug.Assert(managedMethodName[start] == '`');
+ string message = string.Format(CultureInfo.CurrentCulture, Resources.ErrorIncompleteManagedName);
+ throw new InvalidManagedNameException(message);
+ }
- int i = start + 1; // skip initial '`' char
- for (; i < managedMethodName.Length; i++)
+ private static int ParseParameterType(string managedMethodName, int start, out string parameterType)
+ {
+ parameterType = string.Empty;
+ var quoted = false;
+
+ int i;
+ for (i = start; i < managedMethodName.Length; i++)
+ {
+ if (managedMethodName[i] == '\'' || quoted)
{
- if (managedMethodName[i] == '(') break;
+ quoted = managedMethodName[i] == '\'' ? !quoted : quoted;
+ continue;
}
- if (!int.TryParse(Capture(managedMethodName, start + 1, i), out arity))
+
+ switch (managedMethodName[i])
{
- string message = string.Format(CultureInfo.CurrentCulture, Resources.ErrorMethodArityMustBeNumeric);
- throw new InvalidManagedNameException(message);
+ case '<':
+ i = ParseGenericBrackets(managedMethodName, i + 1);
+ break;
+
+ case '[':
+ i = ParseArrayBrackets(managedMethodName, i + 1);
+ break;
+
+ case ',':
+ case ')':
+ parameterType = Capture(managedMethodName, start, i);
+ return i - 1;
+
+ case var w when char.IsWhiteSpace(w):
+ string message = string.Format(CultureInfo.CurrentCulture, Resources.ErrorWhitespaceNotValid, i);
+ throw new InvalidManagedNameException(message);
}
- return i;
}
+ return i;
+ }
+
+ private static int ParseArrayBrackets(string managedMethodName, int start)
+ {
+ var quoted = false;
- private static int ParseParameterTypeList(string managedMethodName, int start, out string[] parameterTypes)
+ for (int i = start; i < managedMethodName.Length; i++)
{
- parameterTypes = null;
- if (start == managedMethodName.Length)
+ if (managedMethodName[i] == '\'' || quoted)
{
- return start;
+ quoted = managedMethodName[i] == '\'' ? !quoted : quoted;
+ continue;
}
- Debug.Assert(managedMethodName[start] == '(');
-
- var types = new List();
- int i = start + 1; // skip initial '(' char
- for (; i < managedMethodName.Length; i++)
+ switch (managedMethodName[i])
{
- switch (managedMethodName[i])
- {
- case ')':
- if (types.Count != 0)
- {
- parameterTypes = types.ToArray();
- }
- return i + 1; // consume right parens
-
- case ',':
- break;
-
- default:
- i = ParseParameterType(managedMethodName, i, out var parameterType);
- types.Add(parameterType);
- break;
- }
+ case ']':
+ return i;
+ case var w when char.IsWhiteSpace(w):
+ string msg = string.Format(CultureInfo.CurrentCulture, Resources.ErrorWhitespaceNotValid, i);
+ throw new InvalidManagedNameException(msg);
}
-
- string message = string.Format(CultureInfo.CurrentCulture, Resources.ErrorIncompleteManagedName);
- throw new InvalidManagedNameException(message);
}
- private static int ParseParameterType(string managedMethodName, int start, out string parameterType)
- {
- parameterType = string.Empty;
- var quoted = false;
+ string message = string.Format(CultureInfo.CurrentCulture, Resources.ErrorIncompleteManagedName);
+ throw new InvalidManagedNameException(message);
+ }
- int i = start;
- for (i = start; i < managedMethodName.Length; i++)
- {
- if (managedMethodName[i] == '\'' || quoted)
- {
- quoted = managedMethodName[i] == '\'' ? !quoted : quoted;
- continue;
- }
-
- switch (managedMethodName[i])
- {
- case '<':
- i = ParseGenericBrackets(managedMethodName, i + 1);
- break;
-
- case '[':
- i = ParseArrayBrackets(managedMethodName, i + 1);
- break;
-
- case ',':
- case ')':
- parameterType = Capture(managedMethodName, start, i);
- return i - 1;
-
- case var w when char.IsWhiteSpace(w):
- string message = string.Format(CultureInfo.CurrentCulture, Resources.ErrorWhitespaceNotValid, i);
- throw new InvalidManagedNameException(message);
- }
- }
- return i;
- }
+ private static int ParseGenericBrackets(string managedMethodName, int start)
+ {
+ var quoted = false;
- private static int ParseArrayBrackets(string managedMethodName, int start)
+ for (int i = start; i < managedMethodName.Length; i++)
{
- var quoted = false;
-
- for (int i = start; i < managedMethodName.Length; i++)
+ if (managedMethodName[i] == '\'' || quoted)
{
- if (managedMethodName[i] == '\'' || quoted)
- {
- quoted = managedMethodName[i] == '\'' ? !quoted : quoted;
- continue;
- }
-
- switch (managedMethodName[i])
- {
- case ']':
- return i;
- case var w when char.IsWhiteSpace(w):
- string msg = string.Format(CultureInfo.CurrentCulture, Resources.ErrorWhitespaceNotValid, i);
- throw new InvalidManagedNameException(msg);
- }
+ quoted = managedMethodName[i] == '\'' ? !quoted : quoted;
+ continue;
}
- string message = string.Format(CultureInfo.CurrentCulture, Resources.ErrorIncompleteManagedName);
- throw new InvalidManagedNameException(message);
- }
+ switch (managedMethodName[i])
+ {
+ case '<':
+ i = ParseGenericBrackets(managedMethodName, i + 1);
+ break;
- private static int ParseGenericBrackets(string managedMethodName, int start)
- {
- var quoted = false;
+ case '>':
+ return i;
- for (int i = start; i < managedMethodName.Length; i++)
- {
- if (managedMethodName[i] == '\'' || quoted)
- {
- quoted = managedMethodName[i] == '\'' ? !quoted : quoted;
- continue;
- }
-
- switch (managedMethodName[i])
- {
- case '<':
- i = ParseGenericBrackets(managedMethodName, i + 1);
- break;
-
- case '>':
- return i;
-
- case var w when char.IsWhiteSpace(w):
- string msg = string.Format(CultureInfo.CurrentCulture, Resources.ErrorWhitespaceNotValid, i);
- throw new InvalidManagedNameException(msg);
- }
+ case var w when char.IsWhiteSpace(w):
+ string msg = string.Format(CultureInfo.CurrentCulture, Resources.ErrorWhitespaceNotValid, i);
+ throw new InvalidManagedNameException(msg);
}
-
- string message = string.Format(CultureInfo.CurrentCulture, Resources.ErrorIncompleteManagedName);
- throw new InvalidManagedNameException(message);
}
+
+ string message = string.Format(CultureInfo.CurrentCulture, Resources.ErrorIncompleteManagedName);
+ throw new InvalidManagedNameException(message);
}
}
diff --git a/src/Microsoft.TestPlatform.AdapterUtilities/TestIdProvider.cs b/src/Microsoft.TestPlatform.AdapterUtilities/TestIdProvider.cs
index 7df8ce8cc2..89916480c8 100644
--- a/src/Microsoft.TestPlatform.AdapterUtilities/TestIdProvider.cs
+++ b/src/Microsoft.TestPlatform.AdapterUtilities/TestIdProvider.cs
@@ -1,382 +1,370 @@
// 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.TestPlatform.AdapterUtilities
+namespace Microsoft.TestPlatform.AdapterUtilities;
+
+using System;
+using System.Text;
+
+public class TestIdProvider
{
- using System;
- using System.Text;
+ internal const int BlockBits = 512;
+ internal const int DigestBits = 160;
+ internal const int BlockBytes = BlockBits / 8;
+ internal const int DigestBytes = DigestBits / 8;
+
+ private Guid _id = Guid.Empty;
+ private byte[] _hash = null;
+ private byte[] _lastBlock = new byte[BlockBytes];
+ private int _position = 0;
+
+ private readonly Sha1Implementation _hasher;
- public class TestIdProvider
+ public TestIdProvider()
{
- internal const int BlockBits = 512;
- internal const int DigestBits = 160;
- internal const int BlockBytes = BlockBits / 8;
- internal const int DigestBytes = DigestBits / 8;
+ _hasher = new Sha1Implementation();
+ }
- private Guid id = Guid.Empty;
- private byte[] hash = null;
- private byte[] lastBlock = new byte[BlockBytes];
- private int position = 0;
+ public void AppendString(string str)
+ {
+ if (_hash != null)
+ {
+ throw new InvalidOperationException();
+ }
+
+ var bytes = Encoding.Unicode.GetBytes(str);
+ var end = Math.Min(BlockBytes - _position, bytes.Length);
+
+ Buffer.BlockCopy(bytes, 0, _lastBlock, _position, end);
+
+ // Block length is not reached yet.
+ if (end + _position < BlockBytes)
+ {
+ _position += end;
+ return;
+ }
- private readonly Sha1Implementation hasher;
+ _hasher.ProcessBlock(_lastBlock, 0, _lastBlock.Length);
+ _position = 0;
- public TestIdProvider()
+ // We processed the entire string already
+ if (end == bytes.Length)
{
- hasher = new Sha1Implementation();
+ return;
}
- public void AppendString(string str)
+ int start = 0;
+ while (end < bytes.Length)
{
- if (hash != null)
+ start = end;
+ end += BlockBytes;
+ if (end > bytes.Length)
{
- throw new InvalidOperationException();
+ break;
}
- var bytes = Encoding.Unicode.GetBytes(str);
- var end = Math.Min(BlockBytes - position, bytes.Length);
+ _hasher.ProcessBlock(bytes, start, end - start);
+ }
- Buffer.BlockCopy(bytes, 0, lastBlock, position, end);
+ if (end > bytes.Length)
+ {
+ _position = bytes.Length - start;
+ Buffer.BlockCopy(bytes, start, _lastBlock, 0, _position);
+ }
+ }
- // Block length is not reached yet.
- if (end + position < BlockBytes)
- {
- position += end;
- return;
- }
+ public byte[] GetHash()
+ {
+ if (_hash != null)
+ {
+ return _hash;
+ }
- hasher.ProcessBlock(lastBlock, 0, lastBlock.Length);
- position = 0;
+ if (_position != 0)
+ {
+ _hasher.PadMessage(ref _lastBlock, _position);
+ _hasher.ProcessBlock(_lastBlock, 0, _lastBlock.Length);
+ }
+
+ _hash = _hasher.ProcessFinalBlock();
+
+ return _hash;
+ }
+
+ public Guid GetId()
+ {
+ if (_id != Guid.Empty)
+ {
+ return _id;
+ }
+
+ var toGuid = new byte[16];
+ Array.Copy(GetHash(), toGuid, 16);
+ _id = new Guid(toGuid);
+
+ return _id;
+ }
+
+ ///
+ /// SHA-1 Implementation as in https://tools.ietf.org/html/rfc3174
+ ///
+ ///
+ /// This implementation only works with messages with a length
+ /// that is a multiple of the size of 8-bits.
+ ///
+ internal class Sha1Implementation
+ {
+ /*
+ * Many of the variable, function and parameter names in this code
+ * were used because those were the names used in the publication.
+ *
+ * For more information please refer to https://tools.ietf.org/html/rfc3174.
+ */
- // We processed the entire string already
- if (end == bytes.Length)
+ private int _streamSize = 0;
+ private bool _messagePadded = false;
+
+ public Sha1Implementation()
+ {
+ Reset();
+ }
+
+ ///
+ /// A sequence of logical functions to be used in SHA-1.
+ /// Each f(t), 0 <= t <= 79, operates on three 32-bit words B, C, D and produces a 32-bit word as output.
+ ///
+ /// Function index. 0 <= t <= 79
+ /// Word B
+ /// Word C
+ /// Word D
+ ///
+ /// f(t;B,C,D) = (B AND C) OR ((NOT B) AND D) ( 0 <= t <= 19)
+ /// f(t;B,C,D) = B XOR C XOR D (20 <= t <= 39)
+ /// f(t;B,C,D) = (B AND C) OR (B AND D) OR (C AND D) (40 <= t <= 59)
+ /// f(t;B,C,D) = B XOR C XOR D (60 <= t <= 79)
+ ///
+ private static uint F(int t, uint b, uint c, uint d)
+ {
+ if (t >= 0 && t <= 19)
{
- return;
+ return (b & c) | (~b & d);
}
-
- int start = 0;
- while (end < bytes.Length)
+ else if ((t >= 20 && t <= 39) || (t >= 60 && t <= 79))
{
- start = end;
- end += BlockBytes;
- if (end > bytes.Length)
- {
- break;
- }
-
- hasher.ProcessBlock(bytes, start, end - start);
+ return b ^ c ^ d;
}
-
- if (end > bytes.Length)
+ else
{
- position = bytes.Length - start;
- Buffer.BlockCopy(bytes, start, lastBlock, 0, position);
+ return t >= 40 && t <= 59
+ ? (b & c) | (b & d) | (c & d)
+ : throw new ArgumentException("Argument out of bounds! 0 <= t < 80", nameof(t));
}
}
- public byte[] GetHash()
+ ///
+ /// Returns a constant word K(t) which is used in the SHA-1.
+ ///
+ /// Word index.
+ ///
+ /// K(t) = 0x5A827999 ( 0 <= t <= 19)
+ /// K(t) = 0x6ED9EBA1 (20 <= t <= 39)
+ /// K(t) = 0x8F1BBCDC (40 <= t <= 59)
+ /// K(t) = 0xCA62C1D6 (60 <= t <= 79)
+ ///
+ private static uint K(int t)
{
- if (hash != null)
+ if (t >= 0 && t <= 19)
{
- return hash;
+ return 0x5A827999u;
}
-
- if (position != 0)
+ else if (t >= 20 && t <= 39)
{
- hasher.PadMessage(ref lastBlock, position);
- hasher.ProcessBlock(lastBlock, 0, lastBlock.Length);
+ return 0x6ED9EBA1u;
}
+ else if (t >= 40 && t <= 59)
+ {
+ return 0x8F1BBCDCu;
+ }
+ else
+ {
+ return t >= 60 && t <= 79 ? 0xCA62C1D6u : throw new ArgumentException("Argument out of bounds! 0 <= t < 80", nameof(t));
+ }
+ }
- hash = hasher.ProcessFinalBlock();
-
- return hash;
+ ///
+ /// The circular left shift operation.
+ ///
+ /// An uint word.
+ /// 0 <= n < 32
+ /// S^n(X) = (X << n) OR (X >> 32-n)
+ private static uint S(uint x, byte n)
+ {
+ return n > 32 ? throw new ArgumentOutOfRangeException(nameof(n)) : (x << n) | (x >> (32 - n));
}
- public Guid GetId()
+ ///
+ /// Ensures that given bytes are in big endian notation.
+ ///
+ /// An array of bytes
+ private static void EnsureBigEndian(ref byte[] array)
{
- if (id != Guid.Empty)
+ if (BitConverter.IsLittleEndian)
{
- return id;
+ Array.Reverse(array);
}
+ }
- var toGuid = new byte[16];
- Array.Copy(GetHash(), toGuid, 16);
- id = new Guid(toGuid);
+ private readonly uint[] _h = new uint[5];
- return id;
+ private void Reset()
+ {
+ _streamSize = 0;
+ _messagePadded = false;
+
+ // as defined in https://tools.ietf.org/html/rfc3174#section-6.1
+ _h[0] = 0x67452301u;
+ _h[1] = 0xEFCDAB89u;
+ _h[2] = 0x98BADCFEu;
+ _h[3] = 0x10325476u;
+ _h[4] = 0xC3D2E1F0u;
}
- ///
- /// SHA-1 Implementation as in https://tools.ietf.org/html/rfc3174
- ///
- ///
- /// This implementation only works with messages with a length
- /// that is a multiple of the size of 8-bits.
- ///
- internal class Sha1Implementation
+ public byte[] ComputeHash(byte[] message)
{
- /*
- * Many of the variable, function and parameter names in this code
- * were used because those were the names used in the publication.
- *
- * For more information please refer to https://tools.ietf.org/html/rfc3174.
- */
+ Reset();
+ _streamSize = 0;
+ PadMessage(ref message);
- private int streamSize = 0;
- private bool messagePadded = false;
+ ProcessBlock(message, 0, message.Length);
- public Sha1Implementation()
- {
- Reset();
- }
+ return ProcessFinalBlock();
+ }
- ///
- /// A sequence of logical functions to be used in SHA-1.
- /// Each f(t), 0 <= t <= 79, operates on three 32-bit words B, C, D and produces a 32-bit word as output.
- ///
- /// Function index. 0 <= t <= 79
- /// Word B
- /// Word C
- /// Word D
- ///
- /// f(t;B,C,D) = (B AND C) OR ((NOT B) AND D) ( 0 <= t <= 19)
- /// f(t;B,C,D) = B XOR C XOR D (20 <= t <= 39)
- /// f(t;B,C,D) = (B AND C) OR (B AND D) OR (C AND D) (40 <= t <= 59)
- /// f(t;B,C,D) = B XOR C XOR D (60 <= t <= 79)
- ///
- private static uint F(int t, uint B, uint C, uint D)
+ private void ProcessMultipleBlocks(byte[] message)
+ {
+ var messageCount = message.Length / BlockBytes;
+ for (var i = 0; i < messageCount; i += 1)
{
- if (t >= 0 && t <= 19)
- {
- return (B & C) | (~B & D);
- }
- else if ((t >= 20 && t <= 39) || (t >= 60 && t <= 79))
- {
- return B ^ C ^ D;
- }
- else if (t >= 40 && t <= 59)
- {
- return (B & C) | (B & D) | (C & D);
- }
- else
- {
- throw new ArgumentException("Argument out of bounds! 0 <= t < 80", nameof(t));
- }
+ ProcessBlock(message, i * BlockBytes, BlockBytes);
}
+ }
- ///
- /// Returns a constant word K(t) which is used in the SHA-1.
- ///
- /// Word index.
- ///
- /// K(t) = 0x5A827999 ( 0 <= t <= 19)
- /// K(t) = 0x6ED9EBA1 (20 <= t <= 39)
- /// K(t) = 0x8F1BBCDC (40 <= t <= 59)
- /// K(t) = 0xCA62C1D6 (60 <= t <= 79)
- ///
- private static uint K(int t)
+ public byte[] ProcessFinalBlock()
+ {
+ if (!_messagePadded)
{
- if (t >= 0 && t <= 19)
- {
- return 0x5A827999u;
- }
- else if (t >= 20 && t <= 39)
- {
- return 0x6ED9EBA1u;
- }
- else if (t >= 40 && t <= 59)
- {
- return 0x8F1BBCDCu;
- }
- else if (t >= 60 && t <= 79)
- {
- return 0xCA62C1D6u;
- }
- else
- {
- throw new ArgumentException("Argument out of bounds! 0 <= t < 80", nameof(t));
- }
+ var pad = new byte[0];
+ PadMessage(ref pad, 0);
+ ProcessBlock(pad, 0, pad.Length);
}
- ///
- /// The circular left shift operation.
- ///
- /// An uint word.
- /// 0 <= n < 32
- /// S^n(X) = (X << n) OR (X >> 32-n)
- private static uint S(uint X, byte n)
+ var digest = new byte[DigestBytes];
+ for (int t = 0; t < _h.Length; t++)
{
- if (n > 32)
- {
- throw new ArgumentOutOfRangeException(nameof(n));
- }
+ var hi = BitConverter.GetBytes(_h[t]);
+ EnsureBigEndian(ref hi);
- return (X << n) | (X >> (32 - n));
- }
-
- ///
- /// Ensures that given bytes are in big endian notation.
- ///
- /// An array of bytes
- private static void EnsureBigEndian(ref byte[] array)
- {
- if (BitConverter.IsLittleEndian)
- {
- Array.Reverse(array);
- }
+ Buffer.BlockCopy(hi, 0, digest, t * hi.Length, hi.Length);
}
- private readonly uint[] H = new uint[5];
+ return digest;
+ }
- private void Reset()
+ public void PadMessage(ref byte[] message, int length = 0)
+ {
+ if (_messagePadded)
{
- streamSize = 0;
- messagePadded = false;
-
- // as defined in https://tools.ietf.org/html/rfc3174#section-6.1
- H[0] = 0x67452301u;
- H[1] = 0xEFCDAB89u;
- H[2] = 0x98BADCFEu;
- H[3] = 0x10325476u;
- H[4] = 0xC3D2E1F0u;
+ throw new InvalidOperationException();
}
- public byte[] ComputeHash(byte[] message)
+ if (length == 0)
{
- Reset();
- streamSize = 0;
- PadMessage(ref message);
-
- ProcessBlock(message, 0, message.Length);
-
- return ProcessFinalBlock();
+ length = message.Length;
}
-
- private void ProcessMultipleBlocks(byte[] message)
+ else
{
- var messageCount = message.Length / BlockBytes;
- for (var i = 0; i < messageCount; i += 1)
- {
- ProcessBlock(message, i * BlockBytes, BlockBytes);
- }
+ Array.Resize(ref message, length);
}
- public byte[] ProcessFinalBlock()
- {
- if (!messagePadded)
- {
- var pad = new byte[0];
- PadMessage(ref pad, 0);
- ProcessBlock(pad, 0, pad.Length);
- }
-
- var digest = new byte[DigestBytes];
- for (int t = 0; t < H.Length; t++)
- {
- var hi = BitConverter.GetBytes(H[t]);
- EnsureBigEndian(ref hi);
-
- Buffer.BlockCopy(hi, 0, digest, t * hi.Length, hi.Length);
- }
-
- return digest;
- }
+ _streamSize += length;
- public void PadMessage(ref byte[] message, int length = 0)
- {
- if (messagePadded)
- {
- throw new InvalidOperationException();
- }
+ var paddingBytes = BlockBytes - (length % BlockBytes);
- if (length == 0)
- {
- length = message.Length;
- }
- else
- {
- Array.Resize(ref message, length);
- }
+ // 64bit uint message size will be appended to end of the padding, making sure we have space for it.
+ if (paddingBytes <= 8)
+ paddingBytes += BlockBytes;
- streamSize += length;
+ var padding = new byte[paddingBytes];
+ padding[0] = 0b10000000;
- var paddingBytes = BlockBytes - (length % BlockBytes);
+ var messageBits = (ulong)_streamSize << 3;
+ var messageSize = BitConverter.GetBytes(messageBits);
+ EnsureBigEndian(ref messageSize);
- // 64bit uint message size will be appended to end of the padding, making sure we have space for it.
- if (paddingBytes <= 8)
- paddingBytes += BlockBytes;
+ Buffer.BlockCopy(messageSize, 0, padding, padding.Length - messageSize.Length, messageSize.Length);
- var padding = new byte[paddingBytes];
- padding[0] = 0b10000000;
+ Array.Resize(ref message, message.Length + padding.Length);
+ Buffer.BlockCopy(padding, 0, message, length, padding.Length);
- var messageBits = (ulong)streamSize << 3;
- var messageSize = BitConverter.GetBytes(messageBits);
- EnsureBigEndian(ref messageSize);
+ _messagePadded = true;
+ }
- Buffer.BlockCopy(messageSize, 0, padding, padding.Length - messageSize.Length, messageSize.Length);
+ public void ProcessBlock(byte[] message, int start, int length)
+ {
+ if (start + length > message.Length)
+ {
+ throw new ArgumentOutOfRangeException(nameof(length));
+ }
+ if (length % BlockBytes != 0)
+ {
+ throw new ArgumentException($"Invalid block size. Actual: {length}, Expected: Multiples of {BlockBytes}", nameof(length));
+ }
+ if (length != BlockBytes)
+ {
+ ProcessMultipleBlocks(message);
+ return;
+ }
+
+ _streamSize += BlockBytes;
+ var w = new uint[80];
- Array.Resize(ref message, message.Length + padding.Length);
- Buffer.BlockCopy(padding, 0, message, length, padding.Length);
+ // Get W(0) .. W(15)
+ for (int t = 0; t <= 15; t++)
+ {
+ var wordBytes = new byte[sizeof(uint)];
+ Buffer.BlockCopy(message, start + (t * sizeof(uint)), wordBytes, 0, sizeof(uint));
+ EnsureBigEndian(ref wordBytes);
+
+ w[t] = BitConverter.ToUInt32(wordBytes, 0);
+ }
- messagePadded = true;
+ // Calculate W(16) .. W(79)
+ for (int t = 16; t <= 79; t++)
+ {
+ w[t] = S(w[t - 3] ^ w[t - 8] ^ w[t - 14] ^ w[t - 16], 1);
}
- public void ProcessBlock(byte[] message, int start, int length)
+ uint a = _h[0],
+ b = _h[1],
+ c = _h[2],
+ d = _h[3],
+ e = _h[4];
+
+ for (int t = 0; t < 80; t++)
{
- if (start + length > message.Length)
- {
- throw new ArgumentOutOfRangeException(nameof(length));
- }
- if (length % BlockBytes != 0)
- {
- throw new ArgumentException($"Invalid block size. Actual: {length}, Expected: Multiples of {BlockBytes}", nameof(length));
- }
- if (length != BlockBytes)
- {
- ProcessMultipleBlocks(message);
- return;
- }
-
- streamSize += BlockBytes;
- var W = new uint[80];
-
- // Get W(0) .. W(15)
- for (int t = 0; t <= 15; t++)
- {
- var wordBytes = new byte[sizeof(uint)];
- Buffer.BlockCopy(message, start + (t * sizeof(uint)), wordBytes, 0, sizeof(uint));
- EnsureBigEndian(ref wordBytes);
-
- W[t] = BitConverter.ToUInt32(wordBytes, 0);
- }
-
- // Calculate W(16) .. W(79)
- for (int t = 16; t <= 79; t++)
- {
- W[t] = S(W[t - 3] ^ W[t - 8] ^ W[t - 14] ^ W[t - 16], 1);
- }
-
- uint A = H[0],
- B = H[1],
- C = H[2],
- D = H[3],
- E = H[4];
-
- for (int t = 0; t < 80; t++)
- {
- var temp = S(A, 5) + F(t, B, C, D) + E + W[t] + K(t);
- E = D;
- D = C;
- C = S(B, 30);
- B = A;
- A = temp;
- }
-
- H[0] += A;
- H[1] += B;
- H[2] += C;
- H[3] += D;
- H[4] += E;
+ var temp = S(a, 5) + F(t, b, c, d) + e + w[t] + K(t);
+ e = d;
+ d = c;
+ c = S(b, 30);
+ b = a;
+ a = temp;
}
+
+ _h[0] += a;
+ _h[1] += b;
+ _h[2] += c;
+ _h[3] += d;
+ _h[4] += e;
}
}
-}
+}
\ No newline at end of file
diff --git a/src/Microsoft.TestPlatform.Build/ArgumentEscaper.cs b/src/Microsoft.TestPlatform.Build/ArgumentEscaper.cs
index 86178efb95..42383db561 100644
--- a/src/Microsoft.TestPlatform.Build/ArgumentEscaper.cs
+++ b/src/Microsoft.TestPlatform.Build/ArgumentEscaper.cs
@@ -1,102 +1,100 @@
-using System;
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+
+using System;
using System.Text;
-namespace Microsoft.TestPlatform.Build.Utils
+namespace Microsoft.TestPlatform.Build.Utils;
+
+public static class ArgumentEscaper
{
- public static class ArgumentEscaper
+ ///
+ /// Undo the processing which took place to create string[] args in Main,
+ /// so that the next process will receive the same string[] args
+ ///
+ /// See here for more info:
+ /// http://blogs.msdn.com/b/twistylittlepassagesallalike/archive/2011/04/23/everyone-quotes-arguments-the-wrong-way.aspx
+ ///
+ ///
+ /// Return original string passed by client
+ public static string HandleEscapeSequenceInArgForProcessStart(string arg)
{
- ///
- /// Undo the processing which took place to create string[] args in Main,
- /// so that the next process will receive the same string[] args
- ///
- /// See here for more info:
- /// http://blogs.msdn.com/b/twistylittlepassagesallalike/archive/2011/04/23/everyone-quotes-arguments-the-wrong-way.aspx
- ///
- ///
- /// Return original string passed by client
- public static string HandleEscapeSequenceInArgForProcessStart(string arg)
+ var sb = new StringBuilder();
+
+ var needsQuotes = ShouldSurroundWithQuotes(arg);
+ var isQuoted = needsQuotes || IsSurroundedWithQuotes(arg);
+
+ if (needsQuotes)
{
- var sb = new StringBuilder();
+ sb.Append('\"');
+ }
- var needsQuotes = ShouldSurroundWithQuotes(arg);
- var isQuoted = needsQuotes || IsSurroundedWithQuotes(arg);
+ for (int i = 0; i < arg.Length; ++i)
+ {
+ var backslashCount = 0;
- if (needsQuotes)
+ // Consume All Backslashes
+ while (i < arg.Length && arg[i] == '\\')
{
- sb.Append('\"');
+ backslashCount++;
+ i++;
}
- for (int i = 0; i < arg.Length; ++i)
+ // Escape any backslashes at the end of the arg
+ // when the argument is also quoted.
+ // This ensures the outside quote is interpreted as
+ // an argument delimiter
+ if (i == arg.Length && isQuoted)
{
- var backslashCount = 0;
-
- // Consume All Backslashes
- while (i < arg.Length && arg[i] == '\\')
- {
- backslashCount++;
- i++;
- }
-
- // Escape any backslashes at the end of the arg
- // when the argument is also quoted.
- // This ensures the outside quote is interpreted as
- // an argument delimiter
- if (i == arg.Length && isQuoted)
- {
- sb.Append('\\', 2 * backslashCount);
- }
-
- // At then end of the arg, which isn't quoted,
- // just add the backslashes, no need to escape
- else if (i == arg.Length)
- {
- sb.Append('\\', backslashCount);
- }
-
- // Escape any preceding backslashes and the quote
- else if (arg[i] == '"')
- {
- sb.Append('\\', (2 * backslashCount) + 1);
- sb.Append('"');
- }
-
- // Output any consumed backslashes and the character
- else
- {
- sb.Append('\\', backslashCount);
- sb.Append(arg[i]);
- }
+ sb.Append('\\', 2 * backslashCount);
}
- if (needsQuotes)
+ // At then end of the arg, which isn't quoted,
+ // just add the backslashes, no need to escape
+ else if (i == arg.Length)
{
- sb.Append('\"');
+ sb.Append('\\', backslashCount);
}
- return sb.ToString();
- }
-
- internal static bool ShouldSurroundWithQuotes(string argument)
- {
- // Don't quote already quoted strings
- if (IsSurroundedWithQuotes(argument))
+ // Escape any preceding backslashes and the quote
+ else if (arg[i] == '"')
{
- return false;
+ sb.Append('\\', (2 * backslashCount) + 1);
+ sb.Append('"');
}
- // Only quote if whitespace exists in the string
- return ArgumentContainsWhitespace(argument);
+ // Output any consumed backslashes and the character
+ else
+ {
+ sb.Append('\\', backslashCount);
+ sb.Append(arg[i]);
+ }
}
- internal static bool IsSurroundedWithQuotes(string argument)
+ if (needsQuotes)
{
- return argument.StartsWith("\"", StringComparison.Ordinal) &&
- argument.EndsWith("\"", StringComparison.Ordinal);
+ sb.Append('\"');
}
- internal static bool ArgumentContainsWhitespace(string argument)
+ return sb.ToString();
+ }
+
+ internal static bool ShouldSurroundWithQuotes(string argument)
+ {
+ // Don't quote already quoted strings
+ if (IsSurroundedWithQuotes(argument))
{
- return argument.Contains(" ") || argument.Contains("\t") || argument.Contains("\n");
+ return false;
}
+
+ // Only quote if whitespace exists in the string
+ return ArgumentContainsWhitespace(argument);
}
-}
+
+ internal static bool IsSurroundedWithQuotes(string argument)
+ => argument.StartsWith("\"", StringComparison.Ordinal)
+ && argument.EndsWith("\"", StringComparison.Ordinal);
+
+ internal static bool ArgumentContainsWhitespace(string argument)
+ => argument.Contains(" ") || argument.Contains("\t") || argument.Contains("\n");
+}
\ No newline at end of file
diff --git a/src/Microsoft.TestPlatform.Build/Tasks/VSTestForwardingApp.cs b/src/Microsoft.TestPlatform.Build/Tasks/VSTestForwardingApp.cs
index 0131b222d5..921bfb900c 100644
--- a/src/Microsoft.TestPlatform.Build/Tasks/VSTestForwardingApp.cs
+++ b/src/Microsoft.TestPlatform.Build/Tasks/VSTestForwardingApp.cs
@@ -1,64 +1,63 @@
// 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.TestPlatform.Build.Tasks
+namespace Microsoft.TestPlatform.Build.Tasks;
+
+using Utils;
+
+using System;
+using System.Collections.Generic;
+using System.Diagnostics;
+
+using Trace;
+
+public class VSTestForwardingApp
{
- using Microsoft.TestPlatform.Build.Utils;
- using System;
- using System.Collections.Generic;
- using System.Diagnostics;
- using Trace;
+ private const string HostExe = "dotnet";
+ private readonly List _allArgs = new();
+ private int _activeProcessId;
- public class VSTestForwardingApp
+ public VSTestForwardingApp(string vsTestExePath, IEnumerable argsToForward)
{
- private const string hostExe = "dotnet";
- private readonly List allArgs = new List();
- private int activeProcessId;
+ _allArgs.Add("exec");
- public VSTestForwardingApp(string vsTestExePath, IEnumerable argsToForward)
- {
- this.allArgs.Add("exec");
-
- // Ensure that path to vstest.console is whitespace friendly. User may install
- // dotnet-cli to any folder containing whitespace (e.g. VS installs to program files).
- // Arguments are already whitespace friendly.
- this.allArgs.Add(ArgumentEscaper.HandleEscapeSequenceInArgForProcessStart(vsTestExePath));
- this.allArgs.AddRange(argsToForward);
- }
+ // Ensure that path to vstest.console is whitespace friendly. User may install
+ // dotnet-cli to any folder containing whitespace (e.g. VS installs to program files).
+ // Arguments are already whitespace friendly.
+ _allArgs.Add(ArgumentEscaper.HandleEscapeSequenceInArgForProcessStart(vsTestExePath));
+ _allArgs.AddRange(argsToForward);
+ }
- public int Execute()
+ public int Execute()
+ {
+ var processInfo = new ProcessStartInfo
{
- var processInfo = new ProcessStartInfo
- {
- FileName = hostExe,
- Arguments = string.Join(" ", this.allArgs),
- UseShellExecute = false,
- };
+ FileName = HostExe,
+ Arguments = string.Join(" ", _allArgs),
+ UseShellExecute = false,
+ };
- Tracing.Trace("VSTest: Starting vstest.console...");
- Tracing.Trace("VSTest: Arguments: " + processInfo.FileName + " " + processInfo.Arguments);
+ Tracing.Trace("VSTest: Starting vstest.console...");
+ Tracing.Trace("VSTest: Arguments: " + processInfo.FileName + " " + processInfo.Arguments);
- using (var activeProcess = new Process { StartInfo = processInfo })
- {
- activeProcess.Start();
- this.activeProcessId = activeProcess.Id;
+ using var activeProcess = new Process { StartInfo = processInfo };
+ activeProcess.Start();
+ _activeProcessId = activeProcess.Id;
- activeProcess.WaitForExit();
- Tracing.Trace("VSTest: Exit code: " + activeProcess.ExitCode);
- return activeProcess.ExitCode;
- }
- }
+ activeProcess.WaitForExit();
+ Tracing.Trace("VSTest: Exit code: " + activeProcess.ExitCode);
+ return activeProcess.ExitCode;
+ }
- public void Cancel()
+ public void Cancel()
+ {
+ try
+ {
+ Process.GetProcessById(_activeProcessId).Kill();
+ }
+ catch (ArgumentException ex)
{
- try
- {
- Process.GetProcessById(activeProcessId).Kill();
- }
- catch(ArgumentException ex)
- {
- Tracing.Trace(string.Format("VSTest: Killing process throws ArgumentException with the following message {0}. It may be that process is not running", ex));
- }
+ Tracing.Trace(string.Format("VSTest: Killing process throws ArgumentException with the following message {0}. It may be that process is not running", ex));
}
}
-}
+}
\ No newline at end of file
diff --git a/src/Microsoft.TestPlatform.Build/Tasks/VSTestLogsTask.cs b/src/Microsoft.TestPlatform.Build/Tasks/VSTestLogsTask.cs
index e50edf4b3e..466e4954c1 100644
--- a/src/Microsoft.TestPlatform.Build/Tasks/VSTestLogsTask.cs
+++ b/src/Microsoft.TestPlatform.Build/Tasks/VSTestLogsTask.cs
@@ -1,47 +1,48 @@
-// Copyright(c) Microsoft Corporation.All rights reserved.
+// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
-namespace Microsoft.TestPlatform.Build.Tasks
+namespace Microsoft.TestPlatform.Build.Tasks;
+
+using System;
+
+using Microsoft.Build.Framework;
+using Microsoft.Build.Utilities;
+
+using Resources;
+
+public class VSTestLogsTask : Task
{
- using System;
- using Microsoft.Build.Framework;
- using Microsoft.Build.Utilities;
- using Microsoft.TestPlatform.Build.Resources;
+ public string LogType
+ {
+ get;
+ set;
+ }
+
+ public string ProjectFilePath
+ {
+ get;
+ set;
+ }
- public class VSTestLogsTask : Task
+ public override bool Execute()
{
- public string LogType
+ if (string.Equals(LogType, "BuildStarted", StringComparison.OrdinalIgnoreCase))
{
- get;
- set;
+ Log.LogMessage(MessageImportance.Normal, Resources.BuildStarted);
}
-
- public string ProjectFilePath
+ else if (string.Equals(LogType, "BuildCompleted", StringComparison.OrdinalIgnoreCase))
{
- get;
- set;
+ Log.LogMessage(MessageImportance.Normal, Resources.BuildCompleted + Environment.NewLine);
}
-
- public override bool Execute()
+ else if (string.Equals(LogType, "NoIsTestProjectProperty", StringComparison.OrdinalIgnoreCase))
{
- if (string.Equals(LogType, "BuildStarted", StringComparison.OrdinalIgnoreCase))
- {
- Log.LogMessage(MessageImportance.Normal, Resources.BuildStarted);
- }
- else if (string.Equals(LogType, "BuildCompleted", StringComparison.OrdinalIgnoreCase))
- {
- Log.LogMessage(MessageImportance.Normal, Resources.BuildCompleted + Environment.NewLine);
- }
- else if (string.Equals(LogType, "NoIsTestProjectProperty", StringComparison.OrdinalIgnoreCase))
- {
- Log.LogMessage(MessageImportance.Low, Resources.NoIsTestProjectProperty);
- }
- else
- {
- return false;
- }
-
- return true;
+ Log.LogMessage(MessageImportance.Low, Resources.NoIsTestProjectProperty);
}
+ else
+ {
+ return false;
+ }
+
+ return true;
}
-}
+}
\ No newline at end of file
diff --git a/src/Microsoft.TestPlatform.Build/Tasks/VSTestTask.cs b/src/Microsoft.TestPlatform.Build/Tasks/VSTestTask.cs
index 2ded9cc953..aa3360ca57 100644
--- a/src/Microsoft.TestPlatform.Build/Tasks/VSTestTask.cs
+++ b/src/Microsoft.TestPlatform.Build/Tasks/VSTestTask.cs
@@ -1,420 +1,421 @@
// 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.TestPlatform.Build.Tasks
+namespace Microsoft.TestPlatform.Build.Tasks;
+
+using System;
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.Linq;
+using System.Threading;
+
+using Microsoft.Build.Framework;
+using Microsoft.Build.Utilities;
+using Resources;
+using Utils;
+
+using Trace;
+
+public class VSTestTask : Task, ICancelableTask
{
- using System;
- using System.Collections.Generic;
- using System.Diagnostics;
- using System.Linq;
- using System.Threading;
- using Microsoft.Build.Framework;
- using Microsoft.Build.Utilities;
- using Microsoft.TestPlatform.Build.Resources;
- using Microsoft.TestPlatform.Build.Utils;
- using Trace;
-
- public class VSTestTask : Task, ICancelableTask
+ // The process which is invoking vstest.console
+ private VSTestForwardingApp _vsTestForwardingApp;
+
+ private const string VsTestAppName = "vstest.console.dll";
+ private const string CodeCovergaeString = "Code Coverage";
+
+ public string TestFileFullPath
{
- // The process which is invoking vstest.console
- private VSTestForwardingApp vsTestForwardingApp;
+ get;
+ set;
+ }
- private const string vsTestAppName = "vstest.console.dll";
- private const string CodeCovergaeString = "Code Coverage";
+ public string VSTestSetting
+ {
+ get;
+ set;
+ }
- public string TestFileFullPath
- {
- get;
- set;
- }
+ public string[] VSTestTestAdapterPath
+ {
+ get;
+ set;
+ }
- public string VSTestSetting
- {
- get;
- set;
- }
+ public string VSTestFramework
+ {
+ get;
+ set;
+ }
- public string[] VSTestTestAdapterPath
- {
- get;
- set;
- }
+ public string VSTestPlatform
+ {
+ get;
+ set;
+ }
- public string VSTestFramework
- {
- get;
- set;
- }
+ public string VSTestTestCaseFilter
+ {
+ get;
+ set;
+ }
+ public string[] VSTestLogger
+ {
+ get;
+ set;
+ }
- public string VSTestPlatform
- {
- get;
- set;
- }
+ public string VSTestListTests
+ {
+ get;
+ set;
+ }
- public string VSTestTestCaseFilter
- {
- get;
- set;
- }
- public string[] VSTestLogger
- {
- get;
- set;
- }
+ public string VSTestDiag
+ {
+ get;
+ set;
+ }
- public string VSTestListTests
- {
- get;
- set;
- }
+ public string[] VSTestCLIRunSettings
+ {
+ get;
+ set;
+ }
- public string VSTestDiag
- {
- get;
- set;
- }
+ [Required]
+ public string VSTestConsolePath
+ {
+ get;
+ set;
+ }
- public string[] VSTestCLIRunSettings
- {
- get;
- set;
- }
+ public string VSTestResultsDirectory
+ {
+ get;
+ set;
+ }
- [Required]
- public string VSTestConsolePath
- {
- get;
- set;
- }
+ public string VSTestVerbosity
+ {
+ get;
+ set;
+ }
- public string VSTestResultsDirectory
- {
- get;
- set;
- }
+ public string[] VSTestCollect
+ {
+ get;
+ set;
+ }
- public string VSTestVerbosity
- {
- get;
- set;
- }
+ public string VSTestBlame
+ {
+ get;
+ set;
+ }
- public string[] VSTestCollect
- {
- get;
- set;
- }
+ public string VSTestBlameCrash
+ {
+ get;
+ set;
+ }
- public string VSTestBlame
- {
- get;
- set;
- }
+ public string VSTestBlameCrashDumpType
+ {
+ get;
+ set;
+ }
+
+ public string VSTestBlameCrashCollectAlways
+ {
+ get;
+ set;
+ }
+
+ public string VSTestBlameHang
+ {
+ get;
+ set;
+ }
+
+ public string VSTestBlameHangDumpType
+ {
+ get;
+ set;
+ }
+ public string VSTestBlameHangTimeout
+ {
+ get;
+ set;
+ }
+
+ public string VSTestTraceDataCollectorDirectoryPath
+ {
+ get;
+ set;
+ }
- public string VSTestBlameCrash
+ public string VSTestNoLogo
+ {
+ get;
+ set;
+ }
+
+ public override bool Execute()
+ {
+ var traceEnabledValue = Environment.GetEnvironmentVariable("VSTEST_BUILD_TRACE");
+ Tracing.traceEnabled = !string.IsNullOrEmpty(traceEnabledValue) && traceEnabledValue.Equals("1", StringComparison.OrdinalIgnoreCase);
+
+ var debugEnabled = Environment.GetEnvironmentVariable("VSTEST_BUILD_DEBUG");
+ if (!string.IsNullOrEmpty(debugEnabled) && debugEnabled.Equals("1", StringComparison.Ordinal))
{
- get;
- set;
+ Console.WriteLine("Waiting for debugger attach...");
+
+ var currentProcess = Process.GetCurrentProcess();
+ Console.WriteLine(string.Format("Process Id: {0}, Name: {1}", currentProcess.Id, currentProcess.ProcessName));
+
+ while (!Debugger.IsAttached)
+ {
+ Thread.Sleep(1000);
+ }
+
+ Debugger.Break();
}
- public string VSTestBlameCrashDumpType
+ // Avoid logging "Task returned false but did not log an error." on test failure, because we don't
+ // write MSBuild error. https://github.com/dotnet/msbuild/blob/51a1071f8871e0c93afbaf1b2ac2c9e59c7b6491/src/Framework/IBuildEngine7.cs#L12
+ var allowfailureWithoutError = BuildEngine.GetType().GetProperty("AllowFailureWithoutError");
+ allowfailureWithoutError?.SetValue(BuildEngine, true);
+
+ _vsTestForwardingApp = new VSTestForwardingApp(VSTestConsolePath, CreateArgument());
+ if (!string.IsNullOrEmpty(VSTestFramework))
{
- get;
- set;
+ Console.WriteLine(Resources.TestRunningSummary, TestFileFullPath, VSTestFramework);
}
- public string VSTestBlameCrashCollectAlways
+ return _vsTestForwardingApp.Execute() == 0;
+ }
+
+ public void Cancel()
+ {
+ Tracing.Trace("VSTest: Killing the process...");
+ _vsTestForwardingApp.Cancel();
+ }
+
+ internal IEnumerable CreateArgument()
+ {
+ var allArgs = AddArgs();
+
+ // VSTestCLIRunSettings should be last argument in allArgs as vstest.console ignore options after "--"(CLIRunSettings option).
+ AddCliRunSettingsArgs(allArgs);
+
+ return allArgs;
+ }
+
+ private void AddCliRunSettingsArgs(List allArgs)
+ {
+ if (VSTestCLIRunSettings != null && VSTestCLIRunSettings.Length > 0)
{
- get;
- set;
+ allArgs.Add("--");
+ foreach (var arg in VSTestCLIRunSettings)
+ {
+ allArgs.Add(ArgumentEscaper.HandleEscapeSequenceInArgForProcessStart(arg));
+ }
}
+ }
- public string VSTestBlameHang
+ private List AddArgs()
+ {
+ var isConsoleLoggerSpecifiedByUser = false;
+ var isCollectCodeCoverageEnabled = false;
+ var isRunSettingsEnabled = false;
+ var allArgs = new List();
+
+ // TODO log arguments in task
+ if (!string.IsNullOrEmpty(VSTestSetting))
{
- get;
- set;
+ isRunSettingsEnabled = true;
+ allArgs.Add("--settings:" + ArgumentEscaper.HandleEscapeSequenceInArgForProcessStart(VSTestSetting));
}
- public string VSTestBlameHangDumpType
+ if (VSTestTestAdapterPath != null && VSTestTestAdapterPath.Length > 0)
{
- get;
- set;
+ foreach (var arg in VSTestTestAdapterPath)
+ {
+ allArgs.Add("--testAdapterPath:" + ArgumentEscaper.HandleEscapeSequenceInArgForProcessStart(arg));
+ }
}
- public string VSTestBlameHangTimeout
+
+ if (!string.IsNullOrEmpty(VSTestFramework))
{
- get;
- set;
+ allArgs.Add("--framework:" + ArgumentEscaper.HandleEscapeSequenceInArgForProcessStart(VSTestFramework));
}
- public string VSTestTraceDataCollectorDirectoryPath
+ // vstest.console only support x86 and x64 for argument platform
+ if (!string.IsNullOrEmpty(VSTestPlatform) && !VSTestPlatform.Contains("AnyCPU"))
{
- get;
- set;
+ allArgs.Add("--platform:" + ArgumentEscaper.HandleEscapeSequenceInArgForProcessStart(VSTestPlatform));
}
- public string VSTestNoLogo
+ if (!string.IsNullOrEmpty(VSTestTestCaseFilter))
{
- get;
- set;
+ allArgs.Add("--testCaseFilter:" +
+ ArgumentEscaper.HandleEscapeSequenceInArgForProcessStart(VSTestTestCaseFilter));
}
- public override bool Execute()
+ if (VSTestLogger != null && VSTestLogger.Length > 0)
{
- var traceEnabledValue = Environment.GetEnvironmentVariable("VSTEST_BUILD_TRACE");
- Tracing.traceEnabled = !string.IsNullOrEmpty(traceEnabledValue) && traceEnabledValue.Equals("1", StringComparison.OrdinalIgnoreCase);
-
- var debugEnabled = Environment.GetEnvironmentVariable("VSTEST_BUILD_DEBUG");
- if (!string.IsNullOrEmpty(debugEnabled) && debugEnabled.Equals("1", StringComparison.Ordinal))
+ foreach (var arg in VSTestLogger)
{
- Console.WriteLine("Waiting for debugger attach...");
+ allArgs.Add("--logger:" + ArgumentEscaper.HandleEscapeSequenceInArgForProcessStart(arg));
- var currentProcess = Process.GetCurrentProcess();
- Console.WriteLine(string.Format("Process Id: {0}, Name: {1}", currentProcess.Id, currentProcess.ProcessName));
-
- while (!Debugger.IsAttached)
+ if (arg.StartsWith("console", StringComparison.OrdinalIgnoreCase))
{
- Thread.Sleep(1000);
+ isConsoleLoggerSpecifiedByUser = true;
}
-
- Debugger.Break();
- }
-
- // Avoid logging "Task returned false but did not log an error." on test failure, because we don't
- // write MSBuild error. https://github.com/dotnet/msbuild/blob/51a1071f8871e0c93afbaf1b2ac2c9e59c7b6491/src/Framework/IBuildEngine7.cs#L12
- var allowfailureWithoutError = BuildEngine.GetType().GetProperty("AllowFailureWithoutError");
- allowfailureWithoutError?.SetValue(BuildEngine, true);
-
- vsTestForwardingApp = new VSTestForwardingApp(this.VSTestConsolePath, this.CreateArgument());
- if (!string.IsNullOrEmpty(this.VSTestFramework))
- {
- Console.WriteLine(Resources.TestRunningSummary, this.TestFileFullPath, this.VSTestFramework);
}
-
- return vsTestForwardingApp.Execute() == 0;
}
- public void Cancel()
+ if (!string.IsNullOrEmpty(VSTestResultsDirectory))
{
- Tracing.Trace("VSTest: Killing the process...");
- vsTestForwardingApp.Cancel();
+ allArgs.Add("--resultsDirectory:" +
+ ArgumentEscaper.HandleEscapeSequenceInArgForProcessStart(VSTestResultsDirectory));
}
- internal IEnumerable CreateArgument()
+ if (!string.IsNullOrEmpty(VSTestListTests))
{
- var allArgs = this.AddArgs();
-
- // VSTestCLIRunSettings should be last argument in allArgs as vstest.console ignore options after "--"(CLIRunSettings option).
- this.AddCLIRunSettingsArgs(allArgs);
-
- return allArgs;
+ allArgs.Add("--listTests");
}
- private void AddCLIRunSettingsArgs(List allArgs)
+ if (!string.IsNullOrEmpty(VSTestDiag))
{
- if (this.VSTestCLIRunSettings != null && this.VSTestCLIRunSettings.Length > 0)
- {
- allArgs.Add("--");
- foreach (var arg in this.VSTestCLIRunSettings)
- {
- allArgs.Add(ArgumentEscaper.HandleEscapeSequenceInArgForProcessStart(arg));
- }
- }
+ allArgs.Add("--Diag:" + ArgumentEscaper.HandleEscapeSequenceInArgForProcessStart(VSTestDiag));
}
- private List AddArgs()
+ if (string.IsNullOrEmpty(TestFileFullPath))
+ {
+ Log.LogError("Test file path cannot be empty or null.");
+ }
+ else
{
- var isConsoleLoggerSpecifiedByUser = false;
- var isCollectCodeCoverageEnabled = false;
- var isRunSettingsEnabled = false;
- var allArgs = new List();
+ allArgs.Add(ArgumentEscaper.HandleEscapeSequenceInArgForProcessStart(TestFileFullPath));
+ }
- // TODO log arguments in task
- if (!string.IsNullOrEmpty(this.VSTestSetting))
- {
- isRunSettingsEnabled = true;
- allArgs.Add("--settings:" + ArgumentEscaper.HandleEscapeSequenceInArgForProcessStart(this.VSTestSetting));
- }
+ // Console logger was not specified by user, but verbosity was, hence add default console logger with verbosity as specified
+ if (!string.IsNullOrWhiteSpace(VSTestVerbosity) && !isConsoleLoggerSpecifiedByUser)
+ {
+ var normalTestLogging = new List() { "n", "normal", "d", "detailed", "diag", "diagnostic" };
+ var quietTestLogging = new List() { "q", "quiet" };
- if (this.VSTestTestAdapterPath != null && this.VSTestTestAdapterPath.Length > 0)
+ string vsTestVerbosity = "minimal";
+ if (normalTestLogging.Contains(VSTestVerbosity.ToLowerInvariant()))
{
- foreach (var arg in this.VSTestTestAdapterPath)
- {
- allArgs.Add("--testAdapterPath:" + ArgumentEscaper.HandleEscapeSequenceInArgForProcessStart(arg));
- }
+ vsTestVerbosity = "normal";
}
-
- if (!string.IsNullOrEmpty(this.VSTestFramework))
+ else if (quietTestLogging.Contains(VSTestVerbosity.ToLowerInvariant()))
{
- allArgs.Add("--framework:" + ArgumentEscaper.HandleEscapeSequenceInArgForProcessStart(this.VSTestFramework));
+ vsTestVerbosity = "quiet";
}
- // vstest.console only support x86 and x64 for argument platform
- if (!string.IsNullOrEmpty(this.VSTestPlatform) && !this.VSTestPlatform.Contains("AnyCPU"))
- {
- allArgs.Add("--platform:" + ArgumentEscaper.HandleEscapeSequenceInArgForProcessStart(this.VSTestPlatform));
- }
+ allArgs.Add("--logger:Console;Verbosity=" + vsTestVerbosity);
+ }
- if (!string.IsNullOrEmpty(this.VSTestTestCaseFilter))
- {
- allArgs.Add("--testCaseFilter:" +
- ArgumentEscaper.HandleEscapeSequenceInArgForProcessStart(this.VSTestTestCaseFilter));
- }
+ var blameCrash = !string.IsNullOrEmpty(VSTestBlameCrash);
+ var blameHang = !string.IsNullOrEmpty(VSTestBlameHang);
+ if (!string.IsNullOrEmpty(VSTestBlame) || blameCrash || blameHang)
+ {
+ var blameArgs = "--Blame";
- if (this.VSTestLogger != null && this.VSTestLogger.Length > 0)
+ var dumpArgs = new List();
+ if (blameCrash || blameHang)
{
- foreach (var arg in this.VSTestLogger)
+ if (blameCrash)
{
- allArgs.Add("--logger:" + ArgumentEscaper.HandleEscapeSequenceInArgForProcessStart(arg));
-
- if (arg.StartsWith("console", StringComparison.OrdinalIgnoreCase))
+ dumpArgs.Add("CollectDump");
+ if (!string.IsNullOrEmpty(VSTestBlameCrashCollectAlways))
{
- isConsoleLoggerSpecifiedByUser = true;
+ dumpArgs.Add($"CollectAlways={VSTestBlameCrashCollectAlways}");
}
- }
- }
-
- if (!string.IsNullOrEmpty(this.VSTestResultsDirectory))
- {
- allArgs.Add("--resultsDirectory:" +
- ArgumentEscaper.HandleEscapeSequenceInArgForProcessStart(this.VSTestResultsDirectory));
- }
-
- if (!string.IsNullOrEmpty(this.VSTestListTests))
- {
- allArgs.Add("--listTests");
- }
- if (!string.IsNullOrEmpty(this.VSTestDiag))
- {
- allArgs.Add("--Diag:" + ArgumentEscaper.HandleEscapeSequenceInArgForProcessStart(this.VSTestDiag));
- }
-
- if (string.IsNullOrEmpty(this.TestFileFullPath))
- {
- this.Log.LogError("Test file path cannot be empty or null.");
- }
- else
- {
- allArgs.Add(ArgumentEscaper.HandleEscapeSequenceInArgForProcessStart(this.TestFileFullPath));
- }
-
- // Console logger was not specified by user, but verbosity was, hence add default console logger with verbosity as specified
- if (!string.IsNullOrWhiteSpace(this.VSTestVerbosity) && !isConsoleLoggerSpecifiedByUser)
- {
- var normalTestLogging = new List() { "n", "normal", "d", "detailed", "diag", "diagnostic" };
- var quietTestLogging = new List() { "q", "quiet" };
-
- string vsTestVerbosity = "minimal";
- if (normalTestLogging.Contains(this.VSTestVerbosity.ToLowerInvariant()))
- {
- vsTestVerbosity = "normal";
- }
- else if (quietTestLogging.Contains(this.VSTestVerbosity.ToLowerInvariant()))
- {
- vsTestVerbosity = "quiet";
+ if (!string.IsNullOrEmpty(VSTestBlameCrashDumpType))
+ {
+ dumpArgs.Add($"DumpType={VSTestBlameCrashDumpType}");
+ }
}
- allArgs.Add("--logger:Console;Verbosity=" + vsTestVerbosity);
- }
-
- var blameCrash = !string.IsNullOrEmpty(this.VSTestBlameCrash);
- var blameHang = !string.IsNullOrEmpty(this.VSTestBlameHang);
- if (!string.IsNullOrEmpty(this.VSTestBlame) || blameCrash || blameHang)
- {
- var blameArgs = "--Blame";
-
- var dumpArgs = new List();
- if (blameCrash || blameHang)
+ if (blameHang)
{
- if (blameCrash)
- {
- dumpArgs.Add("CollectDump");
- if (!string.IsNullOrEmpty(this.VSTestBlameCrashCollectAlways))
- {
- dumpArgs.Add($"CollectAlways={this.VSTestBlameCrashCollectAlways}");
- }
-
- if (!string.IsNullOrEmpty(this.VSTestBlameCrashDumpType))
- {
- dumpArgs.Add($"DumpType={this.VSTestBlameCrashDumpType}");
- }
- }
+ dumpArgs.Add("CollectHangDump");
- if (blameHang)
+ if (!string.IsNullOrEmpty(VSTestBlameHangDumpType))
{
- dumpArgs.Add("CollectHangDump");
-
- if (!string.IsNullOrEmpty(this.VSTestBlameHangDumpType))
- {
- dumpArgs.Add($"HangDumpType={this.VSTestBlameHangDumpType}");
- }
-
- if (!string.IsNullOrEmpty(this.VSTestBlameHangTimeout))
- {
- dumpArgs.Add($"TestTimeout={this.VSTestBlameHangTimeout}");
- }
+ dumpArgs.Add($"HangDumpType={VSTestBlameHangDumpType}");
}
- if (dumpArgs.Any())
+ if (!string.IsNullOrEmpty(VSTestBlameHangTimeout))
{
- blameArgs += $":\"{string.Join(";", dumpArgs)}\"";
+ dumpArgs.Add($"TestTimeout={VSTestBlameHangTimeout}");
}
}
- allArgs.Add(blameArgs);
- }
-
- if (this.VSTestCollect != null && this.VSTestCollect.Length > 0)
- {
- foreach (var arg in this.VSTestCollect)
+ if (dumpArgs.Any())
{
- // For collecting code coverage, argument value can be either "Code Coverage" or "Code Coverage;a=b;c=d".
- // Split the argument with ';' and compare first token value.
- var tokens = arg.Split(';');
-
- if (arg.Equals(CodeCovergaeString, StringComparison.OrdinalIgnoreCase) ||
- tokens[0].Equals(CodeCovergaeString, StringComparison.OrdinalIgnoreCase))
- {
- isCollectCodeCoverageEnabled = true;
- }
-
- allArgs.Add("--collect:" + ArgumentEscaper.HandleEscapeSequenceInArgForProcessStart(arg));
+ blameArgs += $":\"{string.Join(";", dumpArgs)}\"";
}
}
- if (isCollectCodeCoverageEnabled || isRunSettingsEnabled)
+ allArgs.Add(blameArgs);
+ }
+
+ if (VSTestCollect != null && VSTestCollect.Length > 0)
+ {
+ foreach (var arg in VSTestCollect)
{
- // Pass TraceDataCollector path to vstest.console as TestAdapterPath if --collect "Code Coverage"
- // or --settings (User can enable code coverage from runsettings) option given.
- // Not parsing the runsettings for two reason:
- // 1. To keep no knowledge of runsettings structure in VSTestTask.
- // 2. Impact of adding adapter path always is minimal. (worst case: loads additional data collector assembly in datacollector process.)
- // This is required due to currently trace datacollector not ships with dotnet sdk, can be remove once we have
- // go code coverage x-plat.
- if (!string.IsNullOrEmpty(this.VSTestTraceDataCollectorDirectoryPath))
+ // For collecting code coverage, argument value can be either "Code Coverage" or "Code Coverage;a=b;c=d".
+ // Split the argument with ';' and compare first token value.
+ var tokens = arg.Split(';');
+
+ if (arg.Equals(CodeCovergaeString, StringComparison.OrdinalIgnoreCase) ||
+ tokens[0].Equals(CodeCovergaeString, StringComparison.OrdinalIgnoreCase))
{
- allArgs.Add("--testAdapterPath:" +
- ArgumentEscaper.HandleEscapeSequenceInArgForProcessStart(this
- .VSTestTraceDataCollectorDirectoryPath));
- }
- else
- {
- if (isCollectCodeCoverageEnabled)
- {
- // Not showing message in runsettings scenario, because we are not sure that code coverage is enabled.
- // User might be using older Microsoft.NET.Test.Sdk which don't have CodeCoverage infra.
- Console.WriteLine(Resources.UpdateTestSdkForCollectingCodeCoverage);
- }
+ isCollectCodeCoverageEnabled = true;
}
+
+ allArgs.Add("--collect:" + ArgumentEscaper.HandleEscapeSequenceInArgForProcessStart(arg));
}
+ }
- if (!string.IsNullOrWhiteSpace(this.VSTestNoLogo))
+ if (isCollectCodeCoverageEnabled || isRunSettingsEnabled)
+ {
+ // Pass TraceDataCollector path to vstest.console as TestAdapterPath if --collect "Code Coverage"
+ // or --settings (User can enable code coverage from runsettings) option given.
+ // Not parsing the runsettings for two reason:
+ // 1. To keep no knowledge of runsettings structure in VSTestTask.
+ // 2. Impact of adding adapter path always is minimal. (worst case: loads additional data collector assembly in datacollector process.)
+ // This is required due to currently trace datacollector not ships with dotnet sdk, can be remove once we have
+ // go code coverage x-plat.
+ if (!string.IsNullOrEmpty(VSTestTraceDataCollectorDirectoryPath))
+ {
+ allArgs.Add("--testAdapterPath:" +
+ ArgumentEscaper.HandleEscapeSequenceInArgForProcessStart(
+ VSTestTraceDataCollectorDirectoryPath));
+ }
+ else
{
- allArgs.Add("--nologo");
+ if (isCollectCodeCoverageEnabled)
+ {
+ // Not showing message in runsettings scenario, because we are not sure that code coverage is enabled.
+ // User might be using older Microsoft.NET.Test.Sdk which don't have CodeCoverage infra.
+ Console.WriteLine(Resources.UpdateTestSdkForCollectingCodeCoverage);
+ }
}
+ }
- return allArgs;
+ if (!string.IsNullOrWhiteSpace(VSTestNoLogo))
+ {
+ allArgs.Add("--nologo");
}
+
+ return allArgs;
}
-}
+}
\ No newline at end of file
diff --git a/src/Microsoft.TestPlatform.Build/Tracing.cs b/src/Microsoft.TestPlatform.Build/Tracing.cs
index 55d024c672..c64a77dffa 100644
--- a/src/Microsoft.TestPlatform.Build/Tracing.cs
+++ b/src/Microsoft.TestPlatform.Build/Tracing.cs
@@ -1,17 +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.
+
using System;
-namespace Microsoft.TestPlatform.Build.Trace
+namespace Microsoft.TestPlatform.Build.Trace;
+
+public static class Tracing
{
- public static class Tracing
+ [System.Diagnostics.CodeAnalysis.SuppressMessage("Style", "IDE1006:Naming Styles", Justification = "Part of the public API.")]
+ public static bool traceEnabled = false;
+
+ public static void Trace(string message)
{
- public static bool traceEnabled = false;
- public static void Trace(string message)
+ if (traceEnabled)
{
- if (traceEnabled)
- {
- Console.WriteLine(message);
- }
+ Console.WriteLine(message);
}
}
-}
+}
\ No newline at end of file
diff --git a/src/Microsoft.TestPlatform.Client/AttachmentsProcessing/TestRunAttachmentsProcessingEventsHandler.cs b/src/Microsoft.TestPlatform.Client/AttachmentsProcessing/TestRunAttachmentsProcessingEventsHandler.cs
index 6bceff7f24..fe68c17171 100644
--- a/src/Microsoft.TestPlatform.Client/AttachmentsProcessing/TestRunAttachmentsProcessingEventsHandler.cs
+++ b/src/Microsoft.TestPlatform.Client/AttachmentsProcessing/TestRunAttachmentsProcessingEventsHandler.cs
@@ -1,77 +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.Client.TestRunAttachmentsProcessing
+namespace Microsoft.VisualStudio.TestPlatform.Client.TestRunAttachmentsProcessing;
+
+using CommunicationUtilities.Interfaces;
+using Microsoft.VisualStudio.TestPlatform.CommunicationUtilities.ObjectModel;
+using ObjectModel;
+using Microsoft.VisualStudio.TestPlatform.ObjectModel.Client;
+using ObjectModel.Logging;
+
+using System.Collections.Generic;
+
+///
+/// The test run attachments processing events handler.
+///
+///
+public class TestRunAttachmentsProcessingEventsHandler : ITestRunAttachmentsProcessingEventsHandler
{
- using Microsoft.VisualStudio.TestPlatform.CommunicationUtilities.Interfaces;
- using Microsoft.VisualStudio.TestPlatform.CommunicationUtilities.ObjectModel;
- using Microsoft.VisualStudio.TestPlatform.ObjectModel;
- using Microsoft.VisualStudio.TestPlatform.ObjectModel.Client;
- using Microsoft.VisualStudio.TestPlatform.ObjectModel.Logging;
- using System.Collections.Generic;
+ private readonly ICommunicationManager _communicationManager;
///
- /// The test run attachments processing events handler.
+ /// Initializes a new instance of the class.
///
- ///
- public class TestRunAttachmentsProcessingEventsHandler : ITestRunAttachmentsProcessingEventsHandler
+ /// The communication manager.
+ public TestRunAttachmentsProcessingEventsHandler(ICommunicationManager communicationManager)
{
- private readonly ICommunicationManager communicationManager;
+ _communicationManager = communicationManager;
+ }
- ///
- /// Initializes a new instance of the class.
- ///
- /// The communication manager.
- public TestRunAttachmentsProcessingEventsHandler(ICommunicationManager communicationManager)
+ ///
+ public void HandleTestRunAttachmentsProcessingComplete(TestRunAttachmentsProcessingCompleteEventArgs attachmentsProcessingCompleteEventArgs, IEnumerable lastChunk)
+ {
+ if (EqtTrace.IsInfoEnabled)
{
- this.communicationManager = communicationManager;
+ EqtTrace.Info("Test run attachments processing completed.");
}
- ///
- public void HandleTestRunAttachmentsProcessingComplete(TestRunAttachmentsProcessingCompleteEventArgs attachmentsProcessingCompleteEventArgs, IEnumerable lastChunk)
+ var payload = new TestRunAttachmentsProcessingCompletePayload()
{
- if (EqtTrace.IsInfoEnabled)
- {
- EqtTrace.Info("Test run attachments processing completed.");
- }
-
- var payload = new TestRunAttachmentsProcessingCompletePayload()
- {
- AttachmentsProcessingCompleteEventArgs = attachmentsProcessingCompleteEventArgs,
- Attachments = lastChunk
- };
+ AttachmentsProcessingCompleteEventArgs = attachmentsProcessingCompleteEventArgs,
+ Attachments = lastChunk
+ };
- this.communicationManager.SendMessage(MessageType.TestRunAttachmentsProcessingComplete, payload);
- }
+ _communicationManager.SendMessage(MessageType.TestRunAttachmentsProcessingComplete, payload);
+ }
- ///
- public void HandleTestRunAttachmentsProcessingProgress(TestRunAttachmentsProcessingProgressEventArgs attachmentsProcessingProgressEventArgs)
+ ///
+ public void HandleTestRunAttachmentsProcessingProgress(TestRunAttachmentsProcessingProgressEventArgs attachmentsProcessingProgressEventArgs)
+ {
+ var payload = new TestRunAttachmentsProcessingProgressPayload()
{
- var payload = new TestRunAttachmentsProcessingProgressPayload()
- {
- AttachmentsProcessingProgressEventArgs = attachmentsProcessingProgressEventArgs,
- };
+ AttachmentsProcessingProgressEventArgs = attachmentsProcessingProgressEventArgs,
+ };
- this.communicationManager.SendMessage(MessageType.TestRunAttachmentsProcessingProgress, payload);
- }
+ _communicationManager.SendMessage(MessageType.TestRunAttachmentsProcessingProgress, payload);
+ }
- ///
- public void HandleProcessedAttachmentsChunk(IEnumerable attachments)
- {
- throw new System.NotImplementedException();
- }
+ ///
+ public void HandleProcessedAttachmentsChunk(IEnumerable attachments)
+ {
+ throw new System.NotImplementedException();
+ }
- ///
- public void HandleLogMessage(TestMessageLevel level, string message)
- {
- var testMessagePayload = new TestMessagePayload { MessageLevel = level, Message = message };
- this.communicationManager.SendMessage(MessageType.TestMessage, testMessagePayload);
- }
+ ///
+ public void HandleLogMessage(TestMessageLevel level, string message)
+ {
+ var testMessagePayload = new TestMessagePayload { MessageLevel = level, Message = message };
+ _communicationManager.SendMessage(MessageType.TestMessage, testMessagePayload);
+ }
- ///
- public void HandleRawMessage(string rawMessage)
- {
- // No-Op
- }
+ ///
+ public void HandleRawMessage(string rawMessage)
+ {
+ // No-Op
}
-}
+}
\ No newline at end of file
diff --git a/src/Microsoft.TestPlatform.Client/DesignMode/DesignModeClient.cs b/src/Microsoft.TestPlatform.Client/DesignMode/DesignModeClient.cs
index 58d8e9d2a5..d7dfa604ab 100644
--- a/src/Microsoft.TestPlatform.Client/DesignMode/DesignModeClient.cs
+++ b/src/Microsoft.TestPlatform.Client/DesignMode/DesignModeClient.cs
@@ -1,598 +1,591 @@
// 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.Client.DesignMode
+namespace Microsoft.VisualStudio.TestPlatform.Client.DesignMode;
+
+using System;
+using System.Collections.Generic;
+using System.Globalization;
+using System.Net;
+using System.Threading;
+using System.Threading.Tasks;
+
+using Client;
+using TestRunAttachmentsProcessing;
+using RequestHelper;
+using Common.Logging;
+using Microsoft.VisualStudio.TestPlatform.Common.Utilities;
+using CommunicationUtilities;
+using CommunicationUtilities.Interfaces;
+using Microsoft.VisualStudio.TestPlatform.CommunicationUtilities.ObjectModel;
+using CoreUtilities.Helpers;
+using CrossPlatEngine;
+using ObjectModel;
+using Microsoft.VisualStudio.TestPlatform.ObjectModel.Client;
+using Microsoft.VisualStudio.TestPlatform.ObjectModel.Client.Payloads;
+using ObjectModel.Logging;
+using PlatformAbstractions;
+using PlatformAbstractions.Interfaces;
+
+using CommunicationUtilitiesResources = CommunicationUtilities.Resources.Resources;
+
+///
+/// The design mode client.
+///
+public class DesignModeClient : IDesignModeClient
{
- using System;
- using System.Collections.Generic;
- using System.Globalization;
- using System.Net;
- using System.Threading;
- using System.Threading.Tasks;
-
- using Microsoft.VisualStudio.TestPlatform.Client;
- using Microsoft.VisualStudio.TestPlatform.Client.TestRunAttachmentsProcessing;
- using Microsoft.VisualStudio.TestPlatform.Client.RequestHelper;
- using Microsoft.VisualStudio.TestPlatform.Common.Logging;
- using Microsoft.VisualStudio.TestPlatform.Common.Utilities;
- using Microsoft.VisualStudio.TestPlatform.CommunicationUtilities;
- using Microsoft.VisualStudio.TestPlatform.CommunicationUtilities.Interfaces;
- using Microsoft.VisualStudio.TestPlatform.CommunicationUtilities.ObjectModel;
- using Microsoft.VisualStudio.TestPlatform.CoreUtilities.Helpers;
- using Microsoft.VisualStudio.TestPlatform.CrossPlatEngine;
- using Microsoft.VisualStudio.TestPlatform.ObjectModel;
- using Microsoft.VisualStudio.TestPlatform.ObjectModel.Client;
- using Microsoft.VisualStudio.TestPlatform.ObjectModel.Client.Payloads;
- using Microsoft.VisualStudio.TestPlatform.ObjectModel.Logging;
- using Microsoft.VisualStudio.TestPlatform.PlatformAbstractions;
- using Microsoft.VisualStudio.TestPlatform.PlatformAbstractions.Interfaces;
-
- using CommunicationUtilitiesResources = CommunicationUtilities.Resources.Resources;
+ private readonly ICommunicationManager _communicationManager;
+ private readonly IDataSerializer _dataSerializer;
+
+ private readonly ProtocolConfig _protocolConfig = ObjectModel.Constants.DefaultProtocolConfig;
+ private readonly IEnvironment _platformEnvironment;
+ private readonly TestSessionMessageLogger _testSessionMessageLogger;
+ private readonly object _lockObject = new();
+ [System.Diagnostics.CodeAnalysis.SuppressMessage("Style", "IDE1006:Naming Styles", Justification = "Part of the public API.")]
+ protected Action onCustomTestHostLaunchAckReceived;
+ [System.Diagnostics.CodeAnalysis.SuppressMessage("Style", "IDE1006:Naming Styles", Justification = "Part of the public API.")]
+ protected Action onAttachDebuggerAckRecieved;
///
- /// The design mode client.
+ /// Initializes a new instance of the class.
///
- public class DesignModeClient : IDesignModeClient
+ public DesignModeClient()
+ : this(new SocketCommunicationManager(), JsonDataSerializer.Instance, new PlatformEnvironment())
{
- private readonly ICommunicationManager communicationManager;
- private readonly IDataSerializer dataSerializer;
-
- private ProtocolConfig protocolConfig = ObjectModel.Constants.DefaultProtocolConfig;
- private IEnvironment platformEnvironment;
- private TestSessionMessageLogger testSessionMessageLogger;
- private object lockObject = new object();
-
- protected Action onCustomTestHostLaunchAckReceived;
- protected Action onAttachDebuggerAckRecieved;
-
- ///
- /// Initializes a new instance of the class.
- ///
- public DesignModeClient()
- : this(new SocketCommunicationManager(), JsonDataSerializer.Instance, new PlatformEnvironment())
- {
- }
+ }
- ///
- /// Initializes a new instance of the class.
- ///
- ///
- /// The communication manager.
- ///
- ///
- /// The data Serializer.
- ///
- ///
- /// The platform Environment
- ///
- internal DesignModeClient(ICommunicationManager communicationManager, IDataSerializer dataSerializer, IEnvironment platformEnvironment)
- {
- this.communicationManager = communicationManager;
- this.dataSerializer = dataSerializer;
- this.platformEnvironment = platformEnvironment;
- this.testSessionMessageLogger = TestSessionMessageLogger.Instance;
- this.testSessionMessageLogger.TestRunMessage += this.TestRunMessageHandler;
- }
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ ///
+ /// The communication manager.
+ ///
+ ///
+ /// The data Serializer.
+ ///
+ ///
+ /// The platform Environment
+ ///
+ internal DesignModeClient(ICommunicationManager communicationManager, IDataSerializer dataSerializer, IEnvironment platformEnvironment)
+ {
+ _communicationManager = communicationManager;
+ _dataSerializer = dataSerializer;
+ _platformEnvironment = platformEnvironment;
+ _testSessionMessageLogger = TestSessionMessageLogger.Instance;
+ _testSessionMessageLogger.TestRunMessage += TestRunMessageHandler;
+ }
- ///
- /// Property exposing the Instance
- ///
- public static IDesignModeClient Instance { get; private set; }
+ ///
+ /// Property exposing the Instance
+ ///
+ public static IDesignModeClient Instance { get; private set; }
- ///
- /// Initializes DesignMode
- ///
- public static void Initialize()
- {
- Instance = new DesignModeClient();
- }
+ ///
+ /// Initializes DesignMode
+ ///
+ public static void Initialize()
+ {
+ Instance = new DesignModeClient();
+ }
- ///
- /// Creates a client and waits for server to accept connection asynchronously
- ///
- ///
- /// Port number to connect
- ///
- ///
- /// The test Request Manager.
- ///
- public void ConnectToClientAndProcessRequests(int port, ITestRequestManager testRequestManager)
- {
- EqtTrace.Info("Trying to connect to server on port : {0}", port);
- this.communicationManager.SetupClientAsync(new IPEndPoint(IPAddress.Loopback, port));
+ ///
+ /// Creates a client and waits for server to accept connection asynchronously
+ ///
+ ///
+ /// Port number to connect
+ ///
+ ///
+ /// The test Request Manager.
+ ///
+ public void ConnectToClientAndProcessRequests(int port, ITestRequestManager testRequestManager)
+ {
+ EqtTrace.Info("Trying to connect to server on port : {0}", port);
+ _communicationManager.SetupClientAsync(new IPEndPoint(IPAddress.Loopback, port));
- var connectionTimeoutInSecs = EnvironmentHelper.GetConnectionTimeout();
+ var connectionTimeoutInSecs = EnvironmentHelper.GetConnectionTimeout();
- // Wait for the connection to the server and listen for requests.
- if (this.communicationManager.WaitForServerConnection(connectionTimeoutInSecs * 1000))
- {
- this.communicationManager.SendMessage(MessageType.SessionConnected);
- this.ProcessRequests(testRequestManager);
- }
- else
- {
- EqtTrace.Error("DesignModeClient : ConnectToClientAndProcessRequests : Client timed out while connecting to the server.");
- this.Dispose();
- throw new TimeoutException(
- string.Format(
- CultureInfo.CurrentUICulture,
- CommunicationUtilitiesResources.ConnectionTimeoutErrorMessage,
- CoreUtilities.Constants.VstestConsoleProcessName,
- "translation layer",
- connectionTimeoutInSecs,
- EnvironmentHelper.VstestConnectionTimeout)
- );
- }
+ // Wait for the connection to the server and listen for requests.
+ if (_communicationManager.WaitForServerConnection(connectionTimeoutInSecs * 1000))
+ {
+ _communicationManager.SendMessage(MessageType.SessionConnected);
+ ProcessRequests(testRequestManager);
}
-
- public void HandleParentProcessExit()
+ else
{
- // Dispose off the communications to end the session
- // this should end the "ProcessRequests" loop with an exception
- this.Dispose();
+ EqtTrace.Error("DesignModeClient : ConnectToClientAndProcessRequests : Client timed out while connecting to the server.");
+ Dispose();
+ throw new TimeoutException(
+ string.Format(
+ CultureInfo.CurrentUICulture,
+ CommunicationUtilitiesResources.ConnectionTimeoutErrorMessage,
+ CoreUtilities.Constants.VstestConsoleProcessName,
+ "translation layer",
+ connectionTimeoutInSecs,
+ EnvironmentHelper.VstestConnectionTimeout)
+ );
+ }
+ }
- EqtTrace.Info("DesignModeClient: Parent process exited, Exiting myself..");
+ public void HandleParentProcessExit()
+ {
+ // Dispose off the communications to end the session
+ // this should end the "ProcessRequests" loop with an exception
+ Dispose();
- this.platformEnvironment.Exit(1);
- }
+ EqtTrace.Info("DesignModeClient: Parent process exited, Exiting myself..");
- ///
- /// Process Requests from the IDE
- ///
- ///
- /// The test Request Manager.
- ///
- private void ProcessRequests(ITestRequestManager testRequestManager)
- {
- var isSessionEnd = false;
+ _platformEnvironment.Exit(1);
+ }
- do
- {
- try
- {
- var message = this.communicationManager.ReceiveMessage();
+ ///
+ /// Process Requests from the IDE
+ ///
+ ///
+ /// The test Request Manager.
+ ///
+ private void ProcessRequests(ITestRequestManager testRequestManager)
+ {
+ var isSessionEnd = false;
- if (EqtTrace.IsInfoEnabled)
- {
- EqtTrace.Info("DesignModeClient.ProcessRequests: Processing Message: {0}", message);
- }
+ do
+ {
+ try
+ {
+ var message = _communicationManager.ReceiveMessage();
- switch (message.MessageType)
- {
- case MessageType.VersionCheck:
- {
- var version = this.dataSerializer.DeserializePayload(message);
- this.protocolConfig.Version = Math.Min(version, this.protocolConfig.Version);
- this.communicationManager.SendMessage(MessageType.VersionCheck, this.protocolConfig.Version);
- break;
- }
-
- case MessageType.ExtensionsInitialize:
- {
- // Do not filter the Editor/IDE provided extensions by name
- var extensionPaths = this.communicationManager.DeserializePayload>(message);
- testRequestManager.InitializeExtensions(extensionPaths, skipExtensionFilters: true);
- break;
- }
-
- case MessageType.StartTestSession:
- {
- var testSessionPayload = this.communicationManager.DeserializePayload(message);
- this.StartTestSession(testSessionPayload, testRequestManager);
- break;
- }
-
- case MessageType.StopTestSession:
- {
- var testSessionInfo = this.communicationManager.DeserializePayload(message);
- this.StopTestSession(testSessionInfo);
- break;
- }
-
- case MessageType.StartDiscovery:
- {
- var discoveryPayload = this.dataSerializer.DeserializePayload(message);
- this.StartDiscovery(discoveryPayload, testRequestManager);
- break;
- }
-
- case MessageType.GetTestRunnerProcessStartInfoForRunAll:
- case MessageType.GetTestRunnerProcessStartInfoForRunSelected:
- {
- var testRunPayload =
- this.communicationManager.DeserializePayload(
- message);
- this.StartTestRun(testRunPayload, testRequestManager, shouldLaunchTesthost: true);
- break;
- }
-
- case MessageType.TestRunAllSourcesWithDefaultHost:
- case MessageType.TestRunSelectedTestCasesDefaultHost:
- {
- var testRunPayload =
- this.communicationManager.DeserializePayload(
- message);
- this.StartTestRun(testRunPayload, testRequestManager, shouldLaunchTesthost: false);
- break;
- }
-
- case MessageType.TestRunAttachmentsProcessingStart:
- {
- var testRunAttachmentsProcessingPayload =
- this.communicationManager.DeserializePayload(message);
- this.StartTestRunAttachmentsProcessing(testRunAttachmentsProcessingPayload, testRequestManager);
- break;
- }
-
- case MessageType.CancelDiscovery:
- {
- testRequestManager.CancelDiscovery();
- break;
- }
-
- case MessageType.CancelTestRun:
- {
- testRequestManager.CancelTestRun();
- break;
- }
-
- case MessageType.AbortTestRun:
- {
- testRequestManager.AbortTestRun();
- break;
- }
-
- case MessageType.TestRunAttachmentsProcessingCancel:
- {
- testRequestManager.CancelTestRunAttachmentsProcessing();
- break;
- }
-
- case MessageType.CustomTestHostLaunchCallback:
- {
- this.onCustomTestHostLaunchAckReceived?.Invoke(message);
- break;
- }
-
- case MessageType.EditorAttachDebuggerCallback:
- {
- this.onAttachDebuggerAckRecieved?.Invoke(message);
- break;
- }
-
- case MessageType.SessionEnd:
- {
- EqtTrace.Info("DesignModeClient: Session End message received from server. Closing the connection.");
- isSessionEnd = true;
- this.Dispose();
- break;
- }
-
- default:
- {
- EqtTrace.Info("DesignModeClient: Invalid Message received: {0}", message);
- break;
- }
- }
- }
- catch (Exception ex)
+ if (EqtTrace.IsInfoEnabled)
{
- EqtTrace.Error("DesignModeClient: Error processing request: {0}", ex);
- isSessionEnd = true;
- this.Dispose();
+ EqtTrace.Info("DesignModeClient.ProcessRequests: Processing Message: {0}", message);
}
- }
- while (!isSessionEnd);
- }
- ///
- /// Send a custom host launch message to IDE
- ///
- ///
- /// The test Process Start Info.
- ///
- ///
- /// The cancellation token.
- ///
- ///
- /// The .
- ///
- public int LaunchCustomHost(TestProcessStartInfo testProcessStartInfo, CancellationToken cancellationToken)
- {
- lock (this.lockObject)
- {
- var waitHandle = new AutoResetEvent(false);
- Message ackMessage = null;
- this.onCustomTestHostLaunchAckReceived = (ackRawMessage) =>
+ switch (message.MessageType)
{
- ackMessage = ackRawMessage;
- waitHandle.Set();
- };
+ case MessageType.VersionCheck:
+ {
+ var version = _dataSerializer.DeserializePayload(message);
+ _protocolConfig.Version = Math.Min(version, _protocolConfig.Version);
+ _communicationManager.SendMessage(MessageType.VersionCheck, _protocolConfig.Version);
+ break;
+ }
- this.communicationManager.SendMessage(MessageType.CustomTestHostLaunch, testProcessStartInfo);
+ case MessageType.ExtensionsInitialize:
+ {
+ // Do not filter the Editor/IDE provided extensions by name
+ var extensionPaths = _communicationManager.DeserializePayload>(message);
+ testRequestManager.InitializeExtensions(extensionPaths, skipExtensionFilters: true);
+ break;
+ }
- // LifeCycle of the TP through DesignModeClient is maintained by the IDEs or user-facing-clients like LUTs, who call TestPlatform
- // TP is handing over the control of launch to these IDEs and so, TP has to wait indefinite
- // Even if TP has a timeout here, there is no way TP can abort or stop the thread/task that is hung in IDE or LUT
- // Even if TP can abort the API somehow, TP is essentially putting IDEs or Clients in inconsistent state without having info on
- // Since the IDEs own user-UI-experience here, TP will let the custom host launch as much time as IDEs define it for their users
- WaitHandle.WaitAny(new WaitHandle[] { waitHandle, cancellationToken.WaitHandle });
+ case MessageType.StartTestSession:
+ {
+ var testSessionPayload = _communicationManager.DeserializePayload(message);
+ StartTestSession(testSessionPayload, testRequestManager);
+ break;
+ }
- cancellationToken.ThrowTestPlatformExceptionIfCancellationRequested();
+ case MessageType.StopTestSession:
+ {
+ var testSessionInfo = _communicationManager.DeserializePayload(message);
+ StopTestSession(testSessionInfo);
+ break;
+ }
- this.onCustomTestHostLaunchAckReceived = null;
+ case MessageType.StartDiscovery:
+ {
+ var discoveryPayload = _dataSerializer.DeserializePayload(message);
+ StartDiscovery(discoveryPayload, testRequestManager);
+ break;
+ }
- var ackPayload = this.dataSerializer.DeserializePayload(ackMessage);
+ case MessageType.GetTestRunnerProcessStartInfoForRunAll:
+ case MessageType.GetTestRunnerProcessStartInfoForRunSelected:
+ {
+ var testRunPayload =
+ _communicationManager.DeserializePayload(
+ message);
+ StartTestRun(testRunPayload, testRequestManager, shouldLaunchTesthost: true);
+ break;
+ }
+
+ case MessageType.TestRunAllSourcesWithDefaultHost:
+ case MessageType.TestRunSelectedTestCasesDefaultHost:
+ {
+ var testRunPayload =
+ _communicationManager.DeserializePayload(
+ message);
+ StartTestRun(testRunPayload, testRequestManager, shouldLaunchTesthost: false);
+ break;
+ }
+
+ case MessageType.TestRunAttachmentsProcessingStart:
+ {
+ var testRunAttachmentsProcessingPayload =
+ _communicationManager.DeserializePayload(message);
+ StartTestRunAttachmentsProcessing(testRunAttachmentsProcessingPayload, testRequestManager);
+ break;
+ }
- if (ackPayload.HostProcessId > 0)
- {
- return ackPayload.HostProcessId;
- }
- else
- {
- throw new TestPlatformException(ackPayload.ErrorMessage);
+ case MessageType.CancelDiscovery:
+ {
+ testRequestManager.CancelDiscovery();
+ break;
+ }
+
+ case MessageType.CancelTestRun:
+ {
+ testRequestManager.CancelTestRun();
+ break;
+ }
+
+ case MessageType.AbortTestRun:
+ {
+ testRequestManager.AbortTestRun();
+ break;
+ }
+
+ case MessageType.TestRunAttachmentsProcessingCancel:
+ {
+ testRequestManager.CancelTestRunAttachmentsProcessing();
+ break;
+ }
+
+ case MessageType.CustomTestHostLaunchCallback:
+ {
+ onCustomTestHostLaunchAckReceived?.Invoke(message);
+ break;
+ }
+
+ case MessageType.EditorAttachDebuggerCallback:
+ {
+ onAttachDebuggerAckRecieved?.Invoke(message);
+ break;
+ }
+
+ case MessageType.SessionEnd:
+ {
+ EqtTrace.Info("DesignModeClient: Session End message received from server. Closing the connection.");
+ isSessionEnd = true;
+ Dispose();
+ break;
+ }
+
+ default:
+ {
+ EqtTrace.Info("DesignModeClient: Invalid Message received: {0}", message);
+ break;
+ }
}
}
- }
-
- ///
- public bool AttachDebuggerToProcess(int pid, CancellationToken cancellationToken)
- {
- // If an attach request is issued but there is no support for attaching on the other
- // side of the communication channel, we simply return and let the caller know the
- // request failed.
- if (this.protocolConfig.Version < ObjectModel.Constants.MinimumProtocolVersionWithDebugSupport)
+ catch (Exception ex)
{
- return false;
+ EqtTrace.Error("DesignModeClient: Error processing request: {0}", ex);
+ isSessionEnd = true;
+ Dispose();
}
+ }
+ while (!isSessionEnd);
+ }
- lock (this.lockObject)
+ ///
+ /// Send a custom host launch message to IDE
+ ///
+ ///
+ /// The test Process Start Info.
+ ///
+ ///
+ /// The cancellation token.
+ ///
+ ///
+ /// The .
+ ///
+ public int LaunchCustomHost(TestProcessStartInfo testProcessStartInfo, CancellationToken cancellationToken)
+ {
+ lock (_lockObject)
+ {
+ var waitHandle = new AutoResetEvent(false);
+ Message ackMessage = null;
+ onCustomTestHostLaunchAckReceived = (ackRawMessage) =>
{
- var waitHandle = new AutoResetEvent(false);
- Message ackMessage = null;
- this.onAttachDebuggerAckRecieved = (ackRawMessage) =>
- {
- ackMessage = ackRawMessage;
- waitHandle.Set();
- };
+ ackMessage = ackRawMessage;
+ waitHandle.Set();
+ };
- this.communicationManager.SendMessage(MessageType.EditorAttachDebugger, pid);
+ _communicationManager.SendMessage(MessageType.CustomTestHostLaunch, testProcessStartInfo);
- WaitHandle.WaitAny(new WaitHandle[] { waitHandle, cancellationToken.WaitHandle });
+ // LifeCycle of the TP through DesignModeClient is maintained by the IDEs or user-facing-clients like LUTs, who call TestPlatform
+ // TP is handing over the control of launch to these IDEs and so, TP has to wait indefinite
+ // Even if TP has a timeout here, there is no way TP can abort or stop the thread/task that is hung in IDE or LUT
+ // Even if TP can abort the API somehow, TP is essentially putting IDEs or Clients in inconsistent state without having info on
+ // Since the IDEs own user-UI-experience here, TP will let the custom host launch as much time as IDEs define it for their users
+ WaitHandle.WaitAny(new WaitHandle[] { waitHandle, cancellationToken.WaitHandle });
- cancellationToken.ThrowTestPlatformExceptionIfCancellationRequested();
- this.onAttachDebuggerAckRecieved = null;
+ cancellationToken.ThrowTestPlatformExceptionIfCancellationRequested();
- var ackPayload = this.dataSerializer.DeserializePayload(ackMessage);
- if (!ackPayload.Attached)
- {
- EqtTrace.Warning(ackPayload.ErrorMessage);
- }
+ onCustomTestHostLaunchAckReceived = null;
- return ackPayload.Attached;
- }
- }
+ var ackPayload = _dataSerializer.DeserializePayload(ackMessage);
- ///
- /// Send the raw messages to IDE
- ///
- ///
- public void SendRawMessage(string rawMessage)
- {
- this.communicationManager.SendRawMessage(rawMessage);
+ return ackPayload.HostProcessId > 0 ? ackPayload.HostProcessId : throw new TestPlatformException(ackPayload.ErrorMessage);
}
+ }
- ///
- public void SendTestMessage(TestMessageLevel level, string message)
+ ///
+ public bool AttachDebuggerToProcess(int pid, CancellationToken cancellationToken)
+ {
+ // If an attach request is issued but there is no support for attaching on the other
+ // side of the communication channel, we simply return and let the caller know the
+ // request failed.
+ if (_protocolConfig.Version < ObjectModel.Constants.MinimumProtocolVersionWithDebugSupport)
{
- var payload = new TestMessagePayload { MessageLevel = level, Message = message };
- this.communicationManager.SendMessage(MessageType.TestMessage, payload);
+ return false;
}
- ///
- /// Sends the test session logger warning and error messages to IDE;
- ///
- ///
- ///
- public void TestRunMessageHandler(object sender, TestRunMessageEventArgs e)
+ lock (_lockObject)
{
- // save into trace log and send the message to the IDE
- //
- // there is a mismatch between log levels that VS uses and that TP
- // uses. In VS you can choose Trace level which will enable Test platform
- // logs on Verbose level. Below we report Errors and warnings always to the
- // IDE no matter what the level of VS logging is, but Info only when the Eqt trace
- // info level is enabled (so only when VS enables Trace logging)
- switch (e.Level)
+ var waitHandle = new AutoResetEvent(false);
+ Message ackMessage = null;
+ onAttachDebuggerAckRecieved = (ackRawMessage) =>
{
- case TestMessageLevel.Error:
- EqtTrace.Error(e.Message);
- SendTestMessage(e.Level, e.Message);
- break;
- case TestMessageLevel.Warning:
- EqtTrace.Warning(e.Message);
- SendTestMessage(e.Level, e.Message);
- break;
+ ackMessage = ackRawMessage;
+ waitHandle.Set();
+ };
- case TestMessageLevel.Informational:
- EqtTrace.Info(e.Message);
+ _communicationManager.SendMessage(MessageType.EditorAttachDebugger, pid);
- if (EqtTrace.IsInfoEnabled)
- SendTestMessage(e.Level, e.Message);
- break;
+ WaitHandle.WaitAny(new WaitHandle[] { waitHandle, cancellationToken.WaitHandle });
+ cancellationToken.ThrowTestPlatformExceptionIfCancellationRequested();
+ onAttachDebuggerAckRecieved = null;
- default:
- throw new NotSupportedException($"Test message level '{e.Level}' is not supported.");
+ var ackPayload = _dataSerializer.DeserializePayload(ackMessage);
+ if (!ackPayload.Attached)
+ {
+ EqtTrace.Warning(ackPayload.ErrorMessage);
}
+
+ return ackPayload.Attached;
}
+ }
- private void StartTestRun(TestRunRequestPayload testRunPayload, ITestRequestManager testRequestManager, bool shouldLaunchTesthost)
- {
- Task.Run(
- () =>
- {
- try
- {
- testRequestManager.ResetOptions();
-
- // We must avoid re-launching the test host if the test run payload already
- // contains test session info. Test session info being present is an indicative
- // of an already running test host spawned by a start test session call.
- var customLauncher =
- shouldLaunchTesthost && testRunPayload.TestSessionInfo == null
- ? DesignModeTestHostLauncherFactory.GetCustomHostLauncherForTestRun(
- this,
- testRunPayload.DebuggingEnabled)
- : null;
-
- testRequestManager.RunTests(testRunPayload, customLauncher, new DesignModeTestEventsRegistrar(this), this.protocolConfig);
- }
- catch (Exception ex)
- {
- EqtTrace.Error("DesignModeClient: Exception in StartTestRun: " + ex);
+ ///
+ /// Send the raw messages to IDE
+ ///
+ ///
+ public void SendRawMessage(string rawMessage)
+ {
+ _communicationManager.SendRawMessage(rawMessage);
+ }
- var testMessagePayload = new TestMessagePayload { MessageLevel = TestMessageLevel.Error, Message = ex.ToString() };
- this.communicationManager.SendMessage(MessageType.TestMessage, testMessagePayload);
- var runCompletePayload = new TestRunCompletePayload()
- {
- TestRunCompleteArgs = new TestRunCompleteEventArgs(null, false, true, ex, null, null, TimeSpan.MinValue),
- LastRunTests = null
- };
-
- // Send run complete to translation layer
- this.communicationManager.SendMessage(MessageType.ExecutionComplete, runCompletePayload);
- }
- });
- }
+ ///
+ public void SendTestMessage(TestMessageLevel level, string message)
+ {
+ var payload = new TestMessagePayload { MessageLevel = level, Message = message };
+ _communicationManager.SendMessage(MessageType.TestMessage, payload);
+ }
- private void StartDiscovery(DiscoveryRequestPayload discoveryRequestPayload, ITestRequestManager testRequestManager)
+ ///
+ /// Sends the test session logger warning and error messages to IDE;
+ ///
+ ///
+ ///
+ public void TestRunMessageHandler(object sender, TestRunMessageEventArgs e)
+ {
+ // save into trace log and send the message to the IDE
+ //
+ // there is a mismatch between log levels that VS uses and that TP
+ // uses. In VS you can choose Trace level which will enable Test platform
+ // logs on Verbose level. Below we report Errors and warnings always to the
+ // IDE no matter what the level of VS logging is, but Info only when the Eqt trace
+ // info level is enabled (so only when VS enables Trace logging)
+ switch (e.Level)
{
- Task.Run(
- () =>
- {
- try
- {
- testRequestManager.ResetOptions();
- testRequestManager.DiscoverTests(discoveryRequestPayload, new DesignModeTestEventsRegistrar(this), this.protocolConfig);
- }
- catch (Exception ex)
- {
- EqtTrace.Error("DesignModeClient: Exception in StartDiscovery: " + ex);
+ case TestMessageLevel.Error:
+ EqtTrace.Error(e.Message);
+ SendTestMessage(e.Level, e.Message);
+ break;
+ case TestMessageLevel.Warning:
+ EqtTrace.Warning(e.Message);
+ SendTestMessage(e.Level, e.Message);
+ break;
+
+ case TestMessageLevel.Informational:
+ EqtTrace.Info(e.Message);
+
+ if (EqtTrace.IsInfoEnabled)
+ SendTestMessage(e.Level, e.Message);
+ break;
- var testMessagePayload = new TestMessagePayload { MessageLevel = TestMessageLevel.Error, Message = ex.ToString() };
- this.communicationManager.SendMessage(MessageType.TestMessage, testMessagePayload);
- var payload = new DiscoveryCompletePayload()
- {
- IsAborted = true,
- LastDiscoveredTests = null,
- TotalTests = -1
- };
-
- // Send run complete to translation layer
- this.communicationManager.SendMessage(MessageType.DiscoveryComplete, payload);
- }
- });
+ default:
+ throw new NotSupportedException($"Test message level '{e.Level}' is not supported.");
}
+ }
- private void StartTestRunAttachmentsProcessing(TestRunAttachmentsProcessingPayload attachmentsProcessingPayload, ITestRequestManager testRequestManager)
- {
- Task.Run(
- () =>
+ private void StartTestRun(TestRunRequestPayload testRunPayload, ITestRequestManager testRequestManager, bool shouldLaunchTesthost)
+ {
+ Task.Run(
+ () =>
+ {
+ try
{
- try
- {
- testRequestManager.ProcessTestRunAttachments(attachmentsProcessingPayload, new TestRunAttachmentsProcessingEventsHandler(this.communicationManager), this.protocolConfig);
- }
- catch (Exception ex)
- {
- EqtTrace.Error("DesignModeClient: Exception in StartTestRunAttachmentsProcessing: " + ex);
-
- var testMessagePayload = new TestMessagePayload { MessageLevel = TestMessageLevel.Error, Message = ex.ToString() };
- this.communicationManager.SendMessage(MessageType.TestMessage, testMessagePayload);
+ testRequestManager.ResetOptions();
+
+ // We must avoid re-launching the test host if the test run payload already
+ // contains test session info. Test session info being present is an indicative
+ // of an already running test host spawned by a start test session call.
+ var customLauncher =
+ shouldLaunchTesthost && testRunPayload.TestSessionInfo == null
+ ? DesignModeTestHostLauncherFactory.GetCustomHostLauncherForTestRun(
+ this,
+ testRunPayload.DebuggingEnabled)
+ : null;
+
+ testRequestManager.RunTests(testRunPayload, customLauncher, new DesignModeTestEventsRegistrar(this), _protocolConfig);
+ }
+ catch (Exception ex)
+ {
+ EqtTrace.Error("DesignModeClient: Exception in StartTestRun: " + ex);
- var payload = new TestRunAttachmentsProcessingCompletePayload()
- {
- Attachments = null
- };
+ var testMessagePayload = new TestMessagePayload { MessageLevel = TestMessageLevel.Error, Message = ex.ToString() };
+ _communicationManager.SendMessage(MessageType.TestMessage, testMessagePayload);
+ var runCompletePayload = new TestRunCompletePayload()
+ {
+ TestRunCompleteArgs = new TestRunCompleteEventArgs(null, false, true, ex, null, null, TimeSpan.MinValue),
+ LastRunTests = null
+ };
- // Send run complete to translation layer
- this.communicationManager.SendMessage(MessageType.TestRunAttachmentsProcessingComplete, payload);
- }
- });
- }
+ // Send run complete to translation layer
+ _communicationManager.SendMessage(MessageType.ExecutionComplete, runCompletePayload);
+ }
+ });
+ }
- private void StartTestSession(StartTestSessionPayload payload, ITestRequestManager requestManager)
- {
- Task.Run(() =>
+ private void StartDiscovery(DiscoveryRequestPayload discoveryRequestPayload, ITestRequestManager testRequestManager)
+ {
+ Task.Run(
+ () =>
{
- var eventsHandler = new TestSessionEventsHandler(this.communicationManager);
-
try
{
- var customLauncher = payload.HasCustomHostLauncher
- ? DesignModeTestHostLauncherFactory.GetCustomHostLauncherForTestRun(this, payload.IsDebuggingEnabled)
- : null;
-
- requestManager.ResetOptions();
- requestManager.StartTestSession(payload, customLauncher, eventsHandler, this.protocolConfig);
+ testRequestManager.ResetOptions();
+ testRequestManager.DiscoverTests(discoveryRequestPayload, new DesignModeTestEventsRegistrar(this), _protocolConfig);
}
catch (Exception ex)
{
- EqtTrace.Error("DesignModeClient: Exception in StartTestSession: " + ex);
+ EqtTrace.Error("DesignModeClient: Exception in StartDiscovery: " + ex);
+
+ var testMessagePayload = new TestMessagePayload { MessageLevel = TestMessageLevel.Error, Message = ex.ToString() };
+ _communicationManager.SendMessage(MessageType.TestMessage, testMessagePayload);
+
+ var payload = new DiscoveryCompletePayload()
+ {
+ IsAborted = true,
+ LastDiscoveredTests = null,
+ TotalTests = -1
+ };
- eventsHandler.HandleLogMessage(TestMessageLevel.Error, ex.ToString());
- eventsHandler.HandleStartTestSessionComplete(null);
+ // Send run complete to translation layer
+ _communicationManager.SendMessage(MessageType.DiscoveryComplete, payload);
}
});
- }
+ }
- private void StopTestSession(TestSessionInfo testSessionInfo)
- {
- Task.Run(() =>
+ private void StartTestRunAttachmentsProcessing(TestRunAttachmentsProcessingPayload attachmentsProcessingPayload, ITestRequestManager testRequestManager)
+ {
+ Task.Run(
+ () =>
{
- var eventsHandler = new TestSessionEventsHandler(this.communicationManager);
-
try
{
- var stopped = TestSessionPool.Instance.KillSession(testSessionInfo);
-
- eventsHandler.HandleStopTestSessionComplete(testSessionInfo, stopped);
+ testRequestManager.ProcessTestRunAttachments(attachmentsProcessingPayload, new TestRunAttachmentsProcessingEventsHandler(_communicationManager), _protocolConfig);
}
catch (Exception ex)
{
- EqtTrace.Error("DesignModeClient: Exception in StopTestSession: " + ex);
+ EqtTrace.Error("DesignModeClient: Exception in StartTestRunAttachmentsProcessing: " + ex);
+
+ var testMessagePayload = new TestMessagePayload { MessageLevel = TestMessageLevel.Error, Message = ex.ToString() };
+ _communicationManager.SendMessage(MessageType.TestMessage, testMessagePayload);
+
+ var payload = new TestRunAttachmentsProcessingCompletePayload()
+ {
+ Attachments = null
+ };
- eventsHandler.HandleLogMessage(TestMessageLevel.Error, ex.ToString());
- eventsHandler.HandleStopTestSessionComplete(testSessionInfo, false);
+ // Send run complete to translation layer
+ _communicationManager.SendMessage(MessageType.TestRunAttachmentsProcessingComplete, payload);
}
});
- }
+ }
+
+ private void StartTestSession(StartTestSessionPayload payload, ITestRequestManager requestManager)
+ {
+ Task.Run(() =>
+ {
+ var eventsHandler = new TestSessionEventsHandler(_communicationManager);
+
+ try
+ {
+ var customLauncher = payload.HasCustomHostLauncher
+ ? DesignModeTestHostLauncherFactory.GetCustomHostLauncherForTestRun(this, payload.IsDebuggingEnabled)
+ : null;
- #region IDisposable Support
+ requestManager.ResetOptions();
+ requestManager.StartTestSession(payload, customLauncher, eventsHandler, _protocolConfig);
+ }
+ catch (Exception ex)
+ {
+ EqtTrace.Error("DesignModeClient: Exception in StartTestSession: " + ex);
- private bool disposedValue = false; // To detect redundant calls
+ eventsHandler.HandleLogMessage(TestMessageLevel.Error, ex.ToString());
+ eventsHandler.HandleStartTestSessionComplete(null);
+ }
+ });
+ }
- protected virtual void Dispose(bool disposing)
+ private void StopTestSession(TestSessionInfo testSessionInfo)
+ {
+ Task.Run(() =>
{
- if (!disposedValue)
+ var eventsHandler = new TestSessionEventsHandler(_communicationManager);
+
+ try
{
- if (disposing)
- {
- this.communicationManager?.StopClient();
- }
+ var stopped = TestSessionPool.Instance.KillSession(testSessionInfo);
- disposedValue = true;
+ eventsHandler.HandleStopTestSessionComplete(testSessionInfo, stopped);
}
- }
+ catch (Exception ex)
+ {
+ EqtTrace.Error("DesignModeClient: Exception in StopTestSession: " + ex);
+
+ eventsHandler.HandleLogMessage(TestMessageLevel.Error, ex.ToString());
+ eventsHandler.HandleStopTestSessionComplete(testSessionInfo, false);
+ }
+ });
+ }
+
+ #region IDisposable Support
- // This code added to correctly implement the disposable pattern.
- public void Dispose()
+ private bool _disposedValue = false; // To detect redundant calls
+
+ protected virtual void Dispose(bool disposing)
+ {
+ if (!_disposedValue)
{
- // Do not change this code. Put cleanup code in Dispose(bool disposing) above.
- Dispose(true);
+ if (disposing)
+ {
+ _communicationManager?.StopClient();
+ }
+
+ _disposedValue = true;
}
- #endregion
}
-}
+
+ // This code added to correctly implement the disposable pattern.
+ public void Dispose()
+ {
+ // Do not change this code. Put cleanup code in Dispose(bool disposing) above.
+ Dispose(true);
+ }
+ #endregion
+}
\ No newline at end of file
diff --git a/src/Microsoft.TestPlatform.Client/DesignMode/DesignModeTestEventsRegistrar.cs b/src/Microsoft.TestPlatform.Client/DesignMode/DesignModeTestEventsRegistrar.cs
index be33e0b96d..78a8164aef 100644
--- a/src/Microsoft.TestPlatform.Client/DesignMode/DesignModeTestEventsRegistrar.cs
+++ b/src/Microsoft.TestPlatform.Client/DesignMode/DesignModeTestEventsRegistrar.cs
@@ -1,66 +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.Client.DesignMode
+namespace Microsoft.VisualStudio.TestPlatform.Client.DesignMode;
+
+using Common.Interfaces;
+
+using Microsoft.VisualStudio.TestPlatform.ObjectModel.Client;
+
+using ObjectModel.Logging;
+
+///
+/// Registers the discovery and test run events for design mode flow
+///
+public class DesignModeTestEventsRegistrar : ITestDiscoveryEventsRegistrar, ITestRunEventsRegistrar
{
- using Microsoft.VisualStudio.TestPlatform.Common.Interfaces;
- using Microsoft.VisualStudio.TestPlatform.ObjectModel.Client;
- using Microsoft.VisualStudio.TestPlatform.ObjectModel.Logging;
+ private readonly IDesignModeClient _designModeClient;
+
+ public DesignModeTestEventsRegistrar(IDesignModeClient designModeClient)
+ {
+ _designModeClient = designModeClient;
+ }
+
+ #region ITestDiscoveryEventsRegistrar
+
+ public void RegisterDiscoveryEvents(IDiscoveryRequest discoveryRequest)
+ {
+ discoveryRequest.OnRawMessageReceived += OnRawMessageReceived;
+ }
+
+ public void UnregisterDiscoveryEvents(IDiscoveryRequest discoveryRequest)
+ {
+ discoveryRequest.OnRawMessageReceived -= OnRawMessageReceived;
+ }
+
+ #endregion
+
+ #region ITestRunEventsRegistrar
+
+ public void RegisterTestRunEvents(ITestRunRequest testRunRequest)
+ {
+ testRunRequest.OnRawMessageReceived += OnRawMessageReceived;
+ }
+
+ public void UnregisterTestRunEvents(ITestRunRequest testRunRequest)
+ {
+ testRunRequest.OnRawMessageReceived -= OnRawMessageReceived;
+ }
+
+ #endregion
///
- /// Registers the discovery and test run events for design mode flow
+ /// RawMessage received handler for getting rawmessages directly from the host
///
- public class DesignModeTestEventsRegistrar : ITestDiscoveryEventsRegistrar, ITestRunEventsRegistrar
+ ///
+ /// RawMessage from the testhost
+ private void OnRawMessageReceived(object sender, string rawMessage)
+ {
+ // Directly send the data to translation layer instead of de-serializing it here
+ _designModeClient.SendRawMessage(rawMessage);
+ }
+
+ public void LogWarning(string message)
{
- private IDesignModeClient designModeClient;
-
- public DesignModeTestEventsRegistrar(IDesignModeClient designModeClient)
- {
- this.designModeClient = designModeClient;
- }
-
- #region ITestDiscoveryEventsRegistrar
-
- public void RegisterDiscoveryEvents(IDiscoveryRequest discoveryRequest)
- {
- discoveryRequest.OnRawMessageReceived += OnRawMessageReceived;
- }
-
- public void UnregisterDiscoveryEvents(IDiscoveryRequest discoveryRequest)
- {
- discoveryRequest.OnRawMessageReceived -= OnRawMessageReceived;
- }
-
- #endregion
-
- #region ITestRunEventsRegistrar
-
- public void RegisterTestRunEvents(ITestRunRequest testRunRequest)
- {
- testRunRequest.OnRawMessageReceived += OnRawMessageReceived;
- }
-
- public void UnregisterTestRunEvents(ITestRunRequest testRunRequest)
- {
- testRunRequest.OnRawMessageReceived -= OnRawMessageReceived;
- }
-
- #endregion
-
- ///
- /// RawMessage received handler for getting rawmessages directly from the host
- ///
- ///
- /// RawMessage from the testhost
- private void OnRawMessageReceived(object sender, string rawMessage)
- {
- // Directly send the data to translation layer instead of de-serializing it here
- this.designModeClient.SendRawMessage(rawMessage);
- }
-
- public void LogWarning(string message)
- {
- this.designModeClient.SendTestMessage(TestMessageLevel.Warning, message);
- }
+ _designModeClient.SendTestMessage(TestMessageLevel.Warning, message);
}
-}
+}
\ No newline at end of file
diff --git a/src/Microsoft.TestPlatform.Client/DesignMode/DesignModeTestHostLauncher.cs b/src/Microsoft.TestPlatform.Client/DesignMode/DesignModeTestHostLauncher.cs
index 33c37b6995..387e35a6b3 100644
--- a/src/Microsoft.TestPlatform.Client/DesignMode/DesignModeTestHostLauncher.cs
+++ b/src/Microsoft.TestPlatform.Client/DesignMode/DesignModeTestHostLauncher.cs
@@ -1,67 +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.Client.DesignMode
+namespace Microsoft.VisualStudio.TestPlatform.Client.DesignMode;
+
+using System.Threading;
+
+using ObjectModel;
+using Microsoft.VisualStudio.TestPlatform.ObjectModel.Client.Interfaces;
+
+///
+/// DesignMode TestHost Launcher for hosting of test process
+///
+internal class DesignModeTestHostLauncher : ITestHostLauncher2
{
- using System.Threading;
- using Microsoft.VisualStudio.TestPlatform.ObjectModel;
- using Microsoft.VisualStudio.TestPlatform.ObjectModel.Client.Interfaces;
+ private readonly IDesignModeClient _designModeClient;
///
- /// DesignMode TestHost Launcher for hosting of test process
+ /// Initializes a new instance of the class.
///
- internal class DesignModeTestHostLauncher : ITestHostLauncher2
+ /// Design mode client instance.
+ public DesignModeTestHostLauncher(IDesignModeClient designModeClient)
{
- private readonly IDesignModeClient designModeClient;
-
- ///
- /// Initializes a new instance of the class.
- ///
- /// Design mode client instance.
- public DesignModeTestHostLauncher(IDesignModeClient designModeClient)
- {
- this.designModeClient = designModeClient;
- }
-
- ///
- public virtual bool IsDebug => false;
-
- ///
- public bool AttachDebuggerToProcess(int pid)
- {
- return this.designModeClient.AttachDebuggerToProcess(pid, CancellationToken.None);
- }
+ _designModeClient = designModeClient;
+ }
- ///
- public bool AttachDebuggerToProcess(int pid, CancellationToken cancellationToken)
- {
- return this.designModeClient.AttachDebuggerToProcess(pid, cancellationToken);
- }
+ ///
+ public virtual bool IsDebug => false;
- ///
- public int LaunchTestHost(TestProcessStartInfo defaultTestHostStartInfo)
- {
- return this.designModeClient.LaunchCustomHost(defaultTestHostStartInfo, CancellationToken.None);
- }
+ ///
+ public bool AttachDebuggerToProcess(int pid)
+ {
+ return _designModeClient.AttachDebuggerToProcess(pid, CancellationToken.None);
+ }
- ///
- public int LaunchTestHost(TestProcessStartInfo defaultTestHostStartInfo, CancellationToken cancellationToken)
- {
- return this.designModeClient.LaunchCustomHost(defaultTestHostStartInfo, cancellationToken);
- }
+ ///
+ public bool AttachDebuggerToProcess(int pid, CancellationToken cancellationToken)
+ {
+ return _designModeClient.AttachDebuggerToProcess(pid, cancellationToken);
}
- ///
- /// DesignMode Debug Launcher to use if debugging enabled
- ///
- internal class DesignModeDebugTestHostLauncher : DesignModeTestHostLauncher
+ ///
+ public int LaunchTestHost(TestProcessStartInfo defaultTestHostStartInfo)
{
- ///
- public DesignModeDebugTestHostLauncher(IDesignModeClient designModeClient) : base(designModeClient)
- {
- }
+ return _designModeClient.LaunchCustomHost(defaultTestHostStartInfo, CancellationToken.None);
+ }
- ///
- public override bool IsDebug => true;
+ ///
+ public int LaunchTestHost(TestProcessStartInfo defaultTestHostStartInfo, CancellationToken cancellationToken)
+ {
+ return _designModeClient.LaunchCustomHost(defaultTestHostStartInfo, cancellationToken);
}
}
+
+///
+/// DesignMode Debug Launcher to use if debugging enabled
+///
+internal class DesignModeDebugTestHostLauncher : DesignModeTestHostLauncher
+{
+ ///
+ public DesignModeDebugTestHostLauncher(IDesignModeClient designModeClient) : base(designModeClient)
+ {
+ }
+
+ ///
+ public override bool IsDebug => true;
+}
\ No newline at end of file
diff --git a/src/Microsoft.TestPlatform.Client/DesignMode/DesignModeTestHostLauncherFactory.cs b/src/Microsoft.TestPlatform.Client/DesignMode/DesignModeTestHostLauncherFactory.cs
index 5b92bc54ba..ea903e6a94 100644
--- a/src/Microsoft.TestPlatform.Client/DesignMode/DesignModeTestHostLauncherFactory.cs
+++ b/src/Microsoft.TestPlatform.Client/DesignMode/DesignModeTestHostLauncherFactory.cs
@@ -1,33 +1,22 @@
// 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.Client.DesignMode
+namespace Microsoft.VisualStudio.TestPlatform.Client.DesignMode;
+using Microsoft.VisualStudio.TestPlatform.ObjectModel.Client.Interfaces;
+
+///
+/// Factory for providing the design mode test host launchers
+///
+public static class DesignModeTestHostLauncherFactory
{
- using Microsoft.VisualStudio.TestPlatform.ObjectModel.Client;
- using Microsoft.VisualStudio.TestPlatform.ObjectModel.Client.Interfaces;
+ private static ITestHostLauncher s_defaultLauncher;
+ private static ITestHostLauncher s_debugLauncher;
- ///
- /// Factory for providing the design mode test host launchers
- ///
- public static class DesignModeTestHostLauncherFactory
+ public static ITestHostLauncher GetCustomHostLauncherForTestRun(IDesignModeClient designModeClient, bool debuggingEnabled)
{
- private static ITestHostLauncher defaultLauncher;
- private static ITestHostLauncher debugLauncher;
-
- public static ITestHostLauncher GetCustomHostLauncherForTestRun(IDesignModeClient designModeClient, bool debuggingEnabled)
- {
- ITestHostLauncher testHostLauncher = null;
-
- if (!debuggingEnabled)
- {
- testHostLauncher = defaultLauncher = defaultLauncher ?? new DesignModeTestHostLauncher(designModeClient);
- }
- else
- {
- testHostLauncher = debugLauncher = debugLauncher ?? new DesignModeDebugTestHostLauncher(designModeClient);
- }
-
- return testHostLauncher;
- }
+ ITestHostLauncher testHostLauncher = !debuggingEnabled
+ ? (s_defaultLauncher ??= new DesignModeTestHostLauncher(designModeClient))
+ : (s_debugLauncher ??= new DesignModeDebugTestHostLauncher(designModeClient));
+ return testHostLauncher;
}
-}
+}
\ No newline at end of file
diff --git a/src/Microsoft.TestPlatform.Client/DesignMode/IDesignModeClient.cs b/src/Microsoft.TestPlatform.Client/DesignMode/IDesignModeClient.cs
index 1b633c0a67..83a68a0acf 100644
--- a/src/Microsoft.TestPlatform.Client/DesignMode/IDesignModeClient.cs
+++ b/src/Microsoft.TestPlatform.Client/DesignMode/IDesignModeClient.cs
@@ -1,57 +1,57 @@
// 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.Client.DesignMode
+namespace Microsoft.VisualStudio.TestPlatform.Client.DesignMode;
+
+using System;
+using System.Threading;
+
+using RequestHelper;
+using ObjectModel;
+using ObjectModel.Logging;
+
+///
+/// The interface for design mode client.
+///
+public interface IDesignModeClient : IDisposable
{
- using System;
- using System.Threading;
- using Microsoft.VisualStudio.TestPlatform.Client.RequestHelper;
- using Microsoft.VisualStudio.TestPlatform.ObjectModel;
- using Microsoft.VisualStudio.TestPlatform.ObjectModel.Logging;
+ ///
+ /// Setups client based on port
+ ///
+ /// port number to connect
+ void ConnectToClientAndProcessRequests(int port, ITestRequestManager testRequestManager);
+
+ ///
+ /// Send a custom host launch message to IDE
+ ///
+ /// Default TestHost Start Info
+ /// The cancellation Token.
+ /// Process id of the launched test host.
+ int LaunchCustomHost(TestProcessStartInfo defaultTestHostStartInfo, CancellationToken cancellationToken);
+
+ ///
+ /// Attach debugger to an already running process.
+ ///
+ /// Process ID of the process to which the debugger should be attached.
+ /// The cancellation token.
+ /// if the debugger was successfully attached to the requested process, otherwise.
+ bool AttachDebuggerToProcess(int pid, CancellationToken cancellationToken);
+
+ ///
+ /// Handles parent process exit
+ ///
+ void HandleParentProcessExit();
+
+ ///
+ /// Send the raw messages to IDE
+ ///
+ ///
+ void SendRawMessage(string rawMessage);
///
- /// The interface for design mode client.
+ /// Send the test session messages to IDE
///
- public interface IDesignModeClient : IDisposable
- {
- ///
- /// Setups client based on port
- ///
- /// port number to connect
- void ConnectToClientAndProcessRequests(int port, ITestRequestManager testRequestManager);
-
- ///
- /// Send a custom host launch message to IDE
- ///
- /// Default TestHost Start Info
- /// The cancellation Token.
- /// Process id of the launched test host.
- int LaunchCustomHost(TestProcessStartInfo defaultTestHostStartInfo, CancellationToken cancellationToken);
-
- ///
- /// Attach debugger to an already running process.
- ///
- /// Process ID of the process to which the debugger should be attached.
- /// The cancellation token.
- /// if the debugger was successfully attached to the requested process, otherwise.
- bool AttachDebuggerToProcess(int pid, CancellationToken cancellationToken);
-
- ///
- /// Handles parent process exit
- ///
- void HandleParentProcessExit();
-
- ///
- /// Send the raw messages to IDE
- ///
- ///
- void SendRawMessage(string rawMessage);
-
- ///
- /// Send the test session messages to IDE
- ///
- /// Level for the message
- /// Actual message string
- void SendTestMessage(TestMessageLevel level, string message);
- }
-}
+ /// Level for the message
+ /// Actual message string
+ void SendTestMessage(TestMessageLevel level, string message);
+}
\ No newline at end of file
diff --git a/src/Microsoft.TestPlatform.Client/Discovery/DiscoveryRequest.cs b/src/Microsoft.TestPlatform.Client/Discovery/DiscoveryRequest.cs
index d63a2508e2..182584ee9a 100644
--- a/src/Microsoft.TestPlatform.Client/Discovery/DiscoveryRequest.cs
+++ b/src/Microsoft.TestPlatform.Client/Discovery/DiscoveryRequest.cs
@@ -1,568 +1,554 @@
// 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.Client.Discovery
+namespace Microsoft.VisualStudio.TestPlatform.Client.Discovery;
+
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Threading;
+
+using Common.Telemetry;
+using CommunicationUtilities;
+using CommunicationUtilities.Interfaces;
+using Microsoft.VisualStudio.TestPlatform.CommunicationUtilities.ObjectModel;
+using ObjectModel;
+using Microsoft.VisualStudio.TestPlatform.ObjectModel.Client;
+using ObjectModel.Engine;
+using ObjectModel.Logging;
+using Utilities;
+
+///
+/// The discovery request.
+///
+public sealed class DiscoveryRequest : IDiscoveryRequest, ITestDiscoveryEventsHandler2
{
- using System;
- using System.Collections.Generic;
- using System.Linq;
- using System.Threading;
-
- using Microsoft.VisualStudio.TestPlatform.Common.Telemetry;
- using Microsoft.VisualStudio.TestPlatform.CommunicationUtilities;
- using Microsoft.VisualStudio.TestPlatform.CommunicationUtilities.Interfaces;
- using Microsoft.VisualStudio.TestPlatform.CommunicationUtilities.ObjectModel;
- using Microsoft.VisualStudio.TestPlatform.ObjectModel;
- using Microsoft.VisualStudio.TestPlatform.ObjectModel.Client;
- using Microsoft.VisualStudio.TestPlatform.ObjectModel.Engine;
- using Microsoft.VisualStudio.TestPlatform.ObjectModel.Logging;
- using Microsoft.VisualStudio.TestPlatform.Utilities;
+ private readonly IDataSerializer _dataSerializer;
///
- /// The discovery request.
+ /// Initializes a new instance of the class.
///
- public sealed class DiscoveryRequest : IDiscoveryRequest, ITestDiscoveryEventsHandler2
+ /// The Request Data instance providing services and data for discovery
+ /// Discovery criterion.
+ /// Discovery manager instance.
+ internal DiscoveryRequest(IRequestData requestData, DiscoveryCriteria criteria, IProxyDiscoveryManager discoveryManager, ITestLoggerManager loggerManager)
+ : this(requestData, criteria, discoveryManager, loggerManager, JsonDataSerializer.Instance)
{
- private IDataSerializer dataSerializer;
-
- ///
- /// Initializes a new instance of the class.
- ///
- /// The Request Data instance providing services and data for discovery
- /// Discovery criterion.
- /// Discovery manager instance.
- internal DiscoveryRequest(IRequestData requestData, DiscoveryCriteria criteria, IProxyDiscoveryManager discoveryManager, ITestLoggerManager loggerManager)
- : this(requestData, criteria, discoveryManager, loggerManager, JsonDataSerializer.Instance)
- {
- }
+ }
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The Request Data instance providing services and data for discovery
+ /// Discovery criterion.
+ /// Discovery manager instance.
+ /// Data Serializer
+ internal DiscoveryRequest(
+ IRequestData requestData,
+ DiscoveryCriteria criteria,
+ IProxyDiscoveryManager discoveryManager,
+ ITestLoggerManager loggerManager,
+ IDataSerializer dataSerializer)
+ {
+ RequestData = requestData;
+ DiscoveryCriteria = criteria;
+ DiscoveryManager = discoveryManager;
+ LoggerManager = loggerManager;
+ _dataSerializer = dataSerializer;
+ }
- ///
- /// Initializes a new instance of the class.
- ///
- /// The Request Data instance providing services and data for discovery
- /// Discovery criterion.
- /// Discovery manager instance.
- /// Data Serializer
- internal DiscoveryRequest(
- IRequestData requestData,
- DiscoveryCriteria criteria,
- IProxyDiscoveryManager discoveryManager,
- ITestLoggerManager loggerManager,
- IDataSerializer dataSerializer)
+ ///
+ /// Start the discovery request
+ ///
+ public void DiscoverAsync()
+ {
+ if (EqtTrace.IsVerboseEnabled)
{
- this.requestData = requestData;
- this.DiscoveryCriteria = criteria;
- this.DiscoveryManager = discoveryManager;
- this.LoggerManager = loggerManager;
- this.dataSerializer = dataSerializer;
+ EqtTrace.Verbose("DiscoveryRequest.DiscoverAsync: Starting.");
}
- ///
- /// Start the discovery request
- ///
- public void DiscoverAsync()
+ lock (_syncObject)
{
- if (EqtTrace.IsVerboseEnabled)
+ if (_disposed)
{
- EqtTrace.Verbose("DiscoveryRequest.DiscoverAsync: Starting.");
+ throw new ObjectDisposedException("DiscoveryRequest");
}
- lock (this.syncObject)
- {
- if (this.disposed)
- {
- throw new ObjectDisposedException("DiscoveryRequest");
- }
-
- // Reset the discovery completion event
- this.discoveryCompleted.Reset();
+ // Reset the discovery completion event
+ _discoveryCompleted.Reset();
- this.discoveryInProgress = true;
- try
- {
- this.discoveryStartTime = DateTime.UtcNow;
+ DiscoveryInProgress = true;
+ try
+ {
+ _discoveryStartTime = DateTime.UtcNow;
- // Collecting Data Point Number of sources sent for discovery
- this.requestData.MetricsCollection.Add(TelemetryDataConstants.NumberOfSourcesSentForDiscovery, this.DiscoveryCriteria.Sources.Count());
+ // Collecting Data Point Number of sources sent for discovery
+ RequestData.MetricsCollection.Add(TelemetryDataConstants.NumberOfSourcesSentForDiscovery, DiscoveryCriteria.Sources.Count());
- // Invoke OnDiscoveryStart event
- var discoveryStartEvent = new DiscoveryStartEventArgs(this.DiscoveryCriteria);
- this.LoggerManager.HandleDiscoveryStart(discoveryStartEvent);
- this.OnDiscoveryStart.SafeInvoke(this, discoveryStartEvent, "DiscoveryRequest.DiscoveryStart");
+ // Invoke OnDiscoveryStart event
+ var discoveryStartEvent = new DiscoveryStartEventArgs(DiscoveryCriteria);
+ LoggerManager.HandleDiscoveryStart(discoveryStartEvent);
+ OnDiscoveryStart.SafeInvoke(this, discoveryStartEvent, "DiscoveryRequest.DiscoveryStart");
- this.DiscoveryManager.DiscoverTests(this.DiscoveryCriteria, this);
- }
- catch
- {
- this.discoveryInProgress = false;
- throw;
- }
+ DiscoveryManager.DiscoverTests(DiscoveryCriteria, this);
}
-
- if (EqtTrace.IsInfoEnabled)
+ catch
{
- EqtTrace.Info("DiscoveryRequest.DiscoverAsync: Started.");
+ DiscoveryInProgress = false;
+ throw;
}
}
- ///
- /// Aborts the test discovery.
- ///
- public void Abort()
+ if (EqtTrace.IsInfoEnabled)
{
- if (EqtTrace.IsVerboseEnabled)
- {
- EqtTrace.Verbose("DiscoveryRequest.Abort: Aborting.");
- }
-
- lock (this.syncObject)
- {
- if (this.disposed)
- {
- throw new ObjectDisposedException("DiscoveryRequest");
- }
-
- if (this.discoveryInProgress)
- {
- this.DiscoveryManager.Abort();
- }
- else
- {
- if (EqtTrace.IsInfoEnabled)
- {
- EqtTrace.Info("DiscoveryRequest.Abort: No operation to abort.");
- }
-
- return;
- }
- }
+ EqtTrace.Info("DiscoveryRequest.DiscoverAsync: Started.");
+ }
+ }
- if (EqtTrace.IsInfoEnabled)
- {
- EqtTrace.Info("DiscoveryRequest.Abort: Aborted.");
- }
+ ///
+ /// Aborts the test discovery.
+ ///
+ public void Abort()
+ {
+ if (EqtTrace.IsVerboseEnabled)
+ {
+ EqtTrace.Verbose("DiscoveryRequest.Abort: Aborting.");
}
- ///
- /// Wait for discovery completion
- ///
- /// The timeout.
- bool IRequest.WaitForCompletion(int timeout)
+ lock (_syncObject)
{
- if (EqtTrace.IsVerboseEnabled)
+ if (_disposed)
{
- EqtTrace.Verbose("DiscoveryRequest.WaitForCompletion: Waiting with timeout {0}.", timeout);
+ throw new ObjectDisposedException("DiscoveryRequest");
}
- if (this.disposed)
+ if (DiscoveryInProgress)
{
- throw new ObjectDisposedException("DiscoveryRequest");
+ DiscoveryManager.Abort();
}
-
- // This method is not synchronized as it can lead to dead-lock
- // (the discoveryCompletionEvent cannot be raised unless that lock is released)
- if (this.discoveryCompleted != null)
+ else
{
- return this.discoveryCompleted.WaitOne(timeout);
+ if (EqtTrace.IsInfoEnabled)
+ {
+ EqtTrace.Info("DiscoveryRequest.Abort: No operation to abort.");
+ }
+
+ return;
}
+ }
- return true;
+ if (EqtTrace.IsInfoEnabled)
+ {
+ EqtTrace.Info("DiscoveryRequest.Abort: Aborted.");
}
+ }
- ///
- /// Raised when the test discovery starts.
- ///
- public event EventHandler OnDiscoveryStart;
-
- ///
- /// Raised when the test discovery completes.
- ///
- public event EventHandler OnDiscoveryComplete;
-
- ///
- /// Raised when the message is received.
- ///
- /// TestRunMessageEventArgs should be renamed to more generic
- public event EventHandler OnDiscoveryMessage;
-
- ///
- /// Raised when new tests are discovered in this discovery request.
- ///
- public event EventHandler OnDiscoveredTests;
-
- ///
- /// Raised when a discovery event related message is received from host
- /// This is required if one wants to re-direct the message over the process boundary without any processing overhead
- /// All the discovery events should come as raw messages as well as proper serialized events like OnDiscoveredTests
- ///
- public event EventHandler OnRawMessageReceived;
-
- ///
- /// Specifies the discovery criterion
- ///
- public DiscoveryCriteria DiscoveryCriteria
+ ///
+ /// Wait for discovery completion
+ ///
+ /// The timeout.
+ bool IRequest.WaitForCompletion(int timeout)
+ {
+ if (EqtTrace.IsVerboseEnabled)
{
- get;
- private set;
+ EqtTrace.Verbose("DiscoveryRequest.WaitForCompletion: Waiting with timeout {0}.", timeout);
}
- ///
- /// Get the status for the discovery
- /// Returns true if discovery is in progress
- ///
- internal bool DiscoveryInProgress
+ if (_disposed)
{
- get { return this.discoveryInProgress; }
+ throw new ObjectDisposedException("DiscoveryRequest");
}
- ///
- /// Parent discovery manager
- ///
- internal IProxyDiscoveryManager DiscoveryManager { get; private set; }
+ // This method is not synchronized as it can lead to dead-lock
+ // (the discoveryCompletionEvent cannot be raised unless that lock is released)
+ return _discoveryCompleted == null || _discoveryCompleted.WaitOne(timeout);
+ }
+
+ ///
+ /// Raised when the test discovery starts.
+ ///
+ public event EventHandler OnDiscoveryStart;
+
+ ///
+ /// Raised when the test discovery completes.
+ ///
+ public event EventHandler OnDiscoveryComplete;
+
+ ///
+ /// Raised when the message is received.
+ ///
+ /// TestRunMessageEventArgs should be renamed to more generic
+ public event EventHandler OnDiscoveryMessage;
+
+ ///
+ /// Raised when new tests are discovered in this discovery request.
+ ///
+ public event EventHandler OnDiscoveredTests;
+
+ ///
+ /// Raised when a discovery event related message is received from host
+ /// This is required if one wants to re-direct the message over the process boundary without any processing overhead
+ /// All the discovery events should come as raw messages as well as proper serialized events like OnDiscoveredTests
+ ///
+ public event EventHandler OnRawMessageReceived;
+
+ ///
+ /// Specifies the discovery criterion
+ ///
+ public DiscoveryCriteria DiscoveryCriteria
+ {
+ get;
+ private set;
+ }
- ///
- /// Logger manager.
- ///
- internal ITestLoggerManager LoggerManager { get; private set; }
+ ///
+ /// Get the status for the discovery
+ /// Returns true if discovery is in progress
+ ///
+ internal bool DiscoveryInProgress { get; private set; }
- #region ITestDiscoveryEventsHandler2 Methods
+ ///
+ /// Parent discovery manager
+ ///
+ internal IProxyDiscoveryManager DiscoveryManager { get; private set; }
- ///
- public void HandleDiscoveryComplete(DiscoveryCompleteEventArgs discoveryCompleteEventArgs, IEnumerable lastChunk)
+ ///
+ /// Logger manager.
+ ///
+ internal ITestLoggerManager LoggerManager { get; private set; }
+
+ #region ITestDiscoveryEventsHandler2 Methods
+
+ ///
+ public void HandleDiscoveryComplete(DiscoveryCompleteEventArgs discoveryCompleteEventArgs, IEnumerable lastChunk)
+ {
+ if (EqtTrace.IsVerboseEnabled)
+ {
+ EqtTrace.Verbose("DiscoveryRequest.DiscoveryComplete: Starting. Aborted:{0}, TotalTests:{1}", discoveryCompleteEventArgs.IsAborted, discoveryCompleteEventArgs.TotalCount);
+ }
+
+ lock (_syncObject)
{
- if (EqtTrace.IsVerboseEnabled)
+ if (_disposed)
{
- EqtTrace.Verbose("DiscoveryRequest.DiscoveryComplete: Starting. Aborted:{0}, TotalTests:{1}", discoveryCompleteEventArgs.IsAborted, discoveryCompleteEventArgs.TotalCount);
+ if (EqtTrace.IsWarningEnabled)
+ {
+ EqtTrace.Warning("DiscoveryRequest.DiscoveryComplete: Ignoring as the object is disposed.");
+ }
+
+ return;
}
- lock (this.syncObject)
+ // If discovery event is already raised, ignore current one.
+ if (_discoveryCompleted.WaitOne(0))
{
- if (this.disposed)
+ if (EqtTrace.IsVerboseEnabled)
{
- if (EqtTrace.IsWarningEnabled)
- {
- EqtTrace.Warning("DiscoveryRequest.DiscoveryComplete: Ignoring as the object is disposed.");
- }
-
- return;
+ EqtTrace.Verbose("DiscoveryRequest.DiscoveryComplete:Ignoring duplicate DiscoveryComplete. Aborted:{0}, TotalTests:{1}", discoveryCompleteEventArgs.IsAborted, discoveryCompleteEventArgs.TotalCount);
}
- // If discovery event is already raised, ignore current one.
- if (this.discoveryCompleted.WaitOne(0))
- {
- if (EqtTrace.IsVerboseEnabled)
- {
- EqtTrace.Verbose("DiscoveryRequest.DiscoveryComplete:Ignoring duplicate DiscoveryComplete. Aborted:{0}, TotalTests:{1}", discoveryCompleteEventArgs.IsAborted, discoveryCompleteEventArgs.TotalCount);
- }
+ return;
+ }
- return;
- }
+ // Close the discovery session and terminate any test host processes. This operation should never
+ // throw.
+ DiscoveryManager?.Close();
- // Close the discovery session and terminate any test host processes. This operation should never
- // throw.
- this.DiscoveryManager?.Close();
+ try
+ {
+ // Raise onDiscoveredTests event if there are some tests in the last chunk.
+ // (We don't want to send the tests in the discovery complete event so that programming on top of
+ // RS client is easier i.e. user does not have to listen on discovery complete event.)
+ if (lastChunk != null && lastChunk.Any())
+ {
+ var discoveredTestsEvent = new DiscoveredTestsEventArgs(lastChunk);
+ LoggerManager.HandleDiscoveredTests(discoveredTestsEvent);
+ OnDiscoveredTests.SafeInvoke(this, discoveredTestsEvent, "DiscoveryRequest.DiscoveryComplete");
+ }
- try
+ LoggerManager.HandleDiscoveryComplete(discoveryCompleteEventArgs);
+ OnDiscoveryComplete.SafeInvoke(this, discoveryCompleteEventArgs, "DiscoveryRequest.DiscoveryComplete");
+ }
+ finally
+ {
+ // Notify the waiting handle that discovery is complete
+ if (_discoveryCompleted != null)
{
- // Raise onDiscoveredTests event if there are some tests in the last chunk.
- // (We don't want to send the tests in the discovery complete event so that programming on top of
- // RS client is easier i.e. user does not have to listen on discovery complete event.)
- if (lastChunk != null && lastChunk.Any())
+ _discoveryCompleted.Set();
+ if (EqtTrace.IsVerboseEnabled)
{
- var discoveredTestsEvent = new DiscoveredTestsEventArgs(lastChunk);
- this.LoggerManager.HandleDiscoveredTests(discoveredTestsEvent);
- this.OnDiscoveredTests.SafeInvoke(this, discoveredTestsEvent, "DiscoveryRequest.DiscoveryComplete");
+ EqtTrace.Verbose("DiscoveryRequest.DiscoveryComplete: Notified the discovery complete event.");
}
-
- this.LoggerManager.HandleDiscoveryComplete(discoveryCompleteEventArgs);
- this.OnDiscoveryComplete.SafeInvoke(this, discoveryCompleteEventArgs, "DiscoveryRequest.DiscoveryComplete");
}
- finally
+ else
{
- // Notify the waiting handle that discovery is complete
- if (this.discoveryCompleted != null)
- {
- this.discoveryCompleted.Set();
- if (EqtTrace.IsVerboseEnabled)
- {
- EqtTrace.Verbose("DiscoveryRequest.DiscoveryComplete: Notified the discovery complete event.");
- }
- }
- else
+ if (EqtTrace.IsWarningEnabled)
{
- if (EqtTrace.IsWarningEnabled)
- {
- EqtTrace.Warning("DiscoveryRequest.DiscoveryComplete: Discovery complete event was null.");
- }
+ EqtTrace.Warning("DiscoveryRequest.DiscoveryComplete: Discovery complete event was null.");
}
+ }
- this.discoveryInProgress = false;
- var discoveryFinalTimeTaken = DateTime.UtcNow - this.discoveryStartTime;
+ DiscoveryInProgress = false;
+ var discoveryFinalTimeTaken = DateTime.UtcNow - _discoveryStartTime;
- // Fill in the Metrics From Test Host Process
- var metrics = discoveryCompleteEventArgs.Metrics;
- if (metrics != null && metrics.Count != 0)
+ // Fill in the Metrics From Test Host Process
+ var metrics = discoveryCompleteEventArgs.Metrics;
+ if (metrics != null && metrics.Count != 0)
+ {
+ foreach (var metric in metrics)
{
- foreach (var metric in metrics)
- {
- this.requestData.MetricsCollection.Add(metric.Key, metric.Value);
- }
+ RequestData.MetricsCollection.Add(metric.Key, metric.Value);
}
-
- // Collecting Total Time Taken
- this.requestData.MetricsCollection.Add(
- TelemetryDataConstants.TimeTakenInSecForDiscovery, discoveryFinalTimeTaken.TotalSeconds);
}
- }
- if (EqtTrace.IsInfoEnabled)
- {
- EqtTrace.Info("DiscoveryRequest.DiscoveryComplete: Completed.");
+ // Collecting Total Time Taken
+ RequestData.MetricsCollection.Add(
+ TelemetryDataConstants.TimeTakenInSecForDiscovery, discoveryFinalTimeTaken.TotalSeconds);
}
}
- ///
- public void HandleDiscoveredTests(IEnumerable discoveredTestCases)
+ if (EqtTrace.IsInfoEnabled)
{
- if (EqtTrace.IsVerboseEnabled)
- {
- EqtTrace.Verbose("DiscoveryRequest.SendDiscoveredTests: Starting.");
- }
+ EqtTrace.Info("DiscoveryRequest.DiscoveryComplete: Completed.");
+ }
+ }
+
+ ///
+ public void HandleDiscoveredTests(IEnumerable discoveredTestCases)
+ {
+ if (EqtTrace.IsVerboseEnabled)
+ {
+ EqtTrace.Verbose("DiscoveryRequest.SendDiscoveredTests: Starting.");
+ }
- lock (this.syncObject)
+ lock (_syncObject)
+ {
+ if (_disposed)
{
- if (this.disposed)
+ if (EqtTrace.IsWarningEnabled)
{
- if (EqtTrace.IsWarningEnabled)
- {
- EqtTrace.Warning("DiscoveryRequest.SendDiscoveredTests: Ignoring as the object is disposed.");
- }
-
- return;
+ EqtTrace.Warning("DiscoveryRequest.SendDiscoveredTests: Ignoring as the object is disposed.");
}
- var discoveredTestsEvent = new DiscoveredTestsEventArgs(discoveredTestCases);
- this.LoggerManager.HandleDiscoveredTests(discoveredTestsEvent);
- this.OnDiscoveredTests.SafeInvoke(this, discoveredTestsEvent, "DiscoveryRequest.OnDiscoveredTests");
+ return;
}
- if (EqtTrace.IsInfoEnabled)
- {
- EqtTrace.Info("DiscoveryRequest.SendDiscoveredTests: Completed.");
- }
+ var discoveredTestsEvent = new DiscoveredTestsEventArgs(discoveredTestCases);
+ LoggerManager.HandleDiscoveredTests(discoveredTestsEvent);
+ OnDiscoveredTests.SafeInvoke(this, discoveredTestsEvent, "DiscoveryRequest.OnDiscoveredTests");
}
- ///
- /// Dispatch TestRunMessage event to listeners.
- ///
- /// Output level of the message being sent.
- /// Actual contents of the message
- public void HandleLogMessage(TestMessageLevel level, string message)
+ if (EqtTrace.IsInfoEnabled)
{
- if (EqtTrace.IsVerboseEnabled)
- {
- EqtTrace.Verbose("DiscoveryRequest.SendDiscoveryMessage: Starting.");
- }
+ EqtTrace.Info("DiscoveryRequest.SendDiscoveredTests: Completed.");
+ }
+ }
- lock (this.syncObject)
+ ///
+ /// Dispatch TestRunMessage event to listeners.
+ ///
+ /// Output level of the message being sent.
+ /// Actual contents of the message
+ public void HandleLogMessage(TestMessageLevel level, string message)
+ {
+ if (EqtTrace.IsVerboseEnabled)
+ {
+ EqtTrace.Verbose("DiscoveryRequest.SendDiscoveryMessage: Starting.");
+ }
+
+ lock (_syncObject)
+ {
+ if (_disposed)
{
- if (this.disposed)
+ if (EqtTrace.IsWarningEnabled)
{
- if (EqtTrace.IsWarningEnabled)
- {
- EqtTrace.Warning("DiscoveryRequest.SendDiscoveryMessage: Ignoring as the object is disposed.");
- }
-
- return;
+ EqtTrace.Warning("DiscoveryRequest.SendDiscoveryMessage: Ignoring as the object is disposed.");
}
- var testRunMessageEvent = new TestRunMessageEventArgs(level, message);
- this.LoggerManager.HandleDiscoveryMessage(testRunMessageEvent);
- this.OnDiscoveryMessage.SafeInvoke(this, testRunMessageEvent, "DiscoveryRequest.OnTestMessageRecieved");
+ return;
}
- if (EqtTrace.IsInfoEnabled)
- {
- EqtTrace.Info("DiscoveryRequest.SendDiscoveryMessage: Completed.");
- }
+ var testRunMessageEvent = new TestRunMessageEventArgs(level, message);
+ LoggerManager.HandleDiscoveryMessage(testRunMessageEvent);
+ OnDiscoveryMessage.SafeInvoke(this, testRunMessageEvent, "DiscoveryRequest.OnTestMessageRecieved");
}
- ///
- /// Handle Raw message directly from the host
- ///
- /// Raw message.
- public void HandleRawMessage(string rawMessage)
+ if (EqtTrace.IsInfoEnabled)
{
- // Note: Deserialize rawMessage only if required.
+ EqtTrace.Info("DiscoveryRequest.SendDiscoveryMessage: Completed.");
+ }
+ }
- var message = this.LoggerManager.LoggersInitialized || this.requestData.IsTelemetryOptedIn
- ? this.dataSerializer.DeserializeMessage(rawMessage)
- : null;
+ ///
+ /// Handle Raw message directly from the host
+ ///
+ /// Raw message.
+ public void HandleRawMessage(string rawMessage)
+ {
+ // Note: Deserialize rawMessage only if required.
- if (string.Equals(message?.MessageType, MessageType.DiscoveryComplete))
- {
- var discoveryCompletePayload = this.dataSerializer.DeserializePayload(message);
- rawMessage = UpdateRawMessageWithTelemetryInfo(discoveryCompletePayload, message) ?? rawMessage;
- HandleLoggerManagerDiscoveryComplete(discoveryCompletePayload);
- }
+ var message = LoggerManager.LoggersInitialized || RequestData.IsTelemetryOptedIn
+ ? _dataSerializer.DeserializeMessage(rawMessage)
+ : null;
- this.OnRawMessageReceived?.Invoke(this, rawMessage);
+ if (string.Equals(message?.MessageType, MessageType.DiscoveryComplete))
+ {
+ var discoveryCompletePayload = _dataSerializer.DeserializePayload(message);
+ rawMessage = UpdateRawMessageWithTelemetryInfo(discoveryCompletePayload, message) ?? rawMessage;
+ HandleLoggerManagerDiscoveryComplete(discoveryCompletePayload);
}
- ///
- /// Handles LoggerManager's DiscoveryComplete.
- ///
- /// Discovery complete payload.
- private void HandleLoggerManagerDiscoveryComplete(DiscoveryCompletePayload discoveryCompletePayload)
+ OnRawMessageReceived?.Invoke(this, rawMessage);
+ }
+
+ ///
+ /// Handles LoggerManager's DiscoveryComplete.
+ ///
+ /// Discovery complete payload.
+ private void HandleLoggerManagerDiscoveryComplete(DiscoveryCompletePayload discoveryCompletePayload)
+ {
+ if (LoggerManager.LoggersInitialized && discoveryCompletePayload != null)
{
- if (this.LoggerManager.LoggersInitialized && discoveryCompletePayload != null)
+ // Send last chunk to logger manager.
+ if (discoveryCompletePayload.LastDiscoveredTests != null)
{
- // Send last chunk to logger manager.
- if (discoveryCompletePayload.LastDiscoveredTests != null)
- {
- var discoveredTestsEventArgs = new DiscoveredTestsEventArgs(discoveryCompletePayload.LastDiscoveredTests);
- this.LoggerManager.HandleDiscoveredTests(discoveredTestsEventArgs);
- }
-
- // Send discovery complete to logger manager.
- var discoveryCompleteEventArgs = new DiscoveryCompleteEventArgs(discoveryCompletePayload.TotalTests, discoveryCompletePayload.IsAborted);
- discoveryCompleteEventArgs.Metrics = discoveryCompletePayload.Metrics;
- this.LoggerManager.HandleDiscoveryComplete(discoveryCompleteEventArgs);
+ var discoveredTestsEventArgs = new DiscoveredTestsEventArgs(discoveryCompletePayload.LastDiscoveredTests);
+ LoggerManager.HandleDiscoveredTests(discoveredTestsEventArgs);
}
+
+ // Send discovery complete to logger manager.
+ var discoveryCompleteEventArgs = new DiscoveryCompleteEventArgs(discoveryCompletePayload.TotalTests, discoveryCompletePayload.IsAborted);
+ discoveryCompleteEventArgs.Metrics = discoveryCompletePayload.Metrics;
+ LoggerManager.HandleDiscoveryComplete(discoveryCompleteEventArgs);
}
+ }
- ///
- /// Update raw message with telemetry info.
- ///
- /// Discovery complete payload.
- /// Message.
- /// Updated rawMessage.
- private string UpdateRawMessageWithTelemetryInfo(DiscoveryCompletePayload discoveryCompletePayload, Message message)
- {
- var rawMessage = default(string);
+ ///
+ /// Update raw message with telemetry info.
+ ///
+ /// Discovery complete payload.
+ /// Message.
+ /// Updated rawMessage.
+ private string UpdateRawMessageWithTelemetryInfo(DiscoveryCompletePayload discoveryCompletePayload, Message message)
+ {
+ var rawMessage = default(string);
- if (this.requestData.IsTelemetryOptedIn)
+ if (RequestData.IsTelemetryOptedIn)
+ {
+ if (discoveryCompletePayload != null)
{
- if (discoveryCompletePayload != null)
+ if (discoveryCompletePayload.Metrics == null)
{
- if (discoveryCompletePayload.Metrics == null)
- {
- discoveryCompletePayload.Metrics = this.requestData.MetricsCollection.Metrics;
- }
- else
+ discoveryCompletePayload.Metrics = RequestData.MetricsCollection.Metrics;
+ }
+ else
+ {
+ foreach (var kvp in RequestData.MetricsCollection.Metrics)
{
- foreach (var kvp in this.requestData.MetricsCollection.Metrics)
- {
- discoveryCompletePayload.Metrics[kvp.Key] = kvp.Value;
- }
+ discoveryCompletePayload.Metrics[kvp.Key] = kvp.Value;
}
-
- var discoveryFinalTimeTakenForDesignMode = DateTime.UtcNow - this.discoveryStartTime;
-
- // Collecting Total Time Taken
- discoveryCompletePayload.Metrics[TelemetryDataConstants.TimeTakenInSecForDiscovery] = discoveryFinalTimeTakenForDesignMode.TotalSeconds;
}
- if (message is VersionedMessage message1)
- {
- var version = message1.Version;
+ var discoveryFinalTimeTakenForDesignMode = DateTime.UtcNow - _discoveryStartTime;
- rawMessage = this.dataSerializer.SerializePayload(
- MessageType.DiscoveryComplete,
- discoveryCompletePayload,
- version);
- }
- else
- {
- rawMessage = this.dataSerializer.SerializePayload(
- MessageType.DiscoveryComplete,
- discoveryCompletePayload);
- }
+ // Collecting Total Time Taken
+ discoveryCompletePayload.Metrics[TelemetryDataConstants.TimeTakenInSecForDiscovery] = discoveryFinalTimeTakenForDesignMode.TotalSeconds;
}
- return rawMessage;
+ if (message is VersionedMessage message1)
+ {
+ var version = message1.Version;
+
+ rawMessage = _dataSerializer.SerializePayload(
+ MessageType.DiscoveryComplete,
+ discoveryCompletePayload,
+ version);
+ }
+ else
+ {
+ rawMessage = _dataSerializer.SerializePayload(
+ MessageType.DiscoveryComplete,
+ discoveryCompletePayload);
+ }
}
- #endregion
+ return rawMessage;
+ }
- #region IDisposable implementation
+ #endregion
- ///
- /// Performs application-defined tasks associated with freeing, releasing, or
- /// resetting unmanaged resources.
- ///
- public void Dispose()
- {
- this.Dispose(true);
+ #region IDisposable implementation
- GC.SuppressFinalize(this);
- }
+ ///
+ /// Performs application-defined tasks associated with freeing, releasing, or
+ /// resetting unmanaged resources.
+ ///
+ public void Dispose()
+ {
+ Dispose(true);
- private void Dispose(bool disposing)
+ GC.SuppressFinalize(this);
+ }
+
+ private void Dispose(bool disposing)
+ {
+ if (EqtTrace.IsVerboseEnabled)
{
- if (EqtTrace.IsVerboseEnabled)
- {
- EqtTrace.Verbose("DiscoveryRequest.Dispose: Starting.");
- }
+ EqtTrace.Verbose("DiscoveryRequest.Dispose: Starting.");
+ }
- lock (this.syncObject)
+ lock (_syncObject)
+ {
+ if (!_disposed)
{
- if (!this.disposed)
+ if (disposing)
{
- if (disposing)
+ if (_discoveryCompleted != null)
{
- if (this.discoveryCompleted != null)
- {
- this.discoveryCompleted.Dispose();
- }
+ _discoveryCompleted.Dispose();
}
-
- // Indicate that object has been disposed
- this.discoveryCompleted = null;
- this.disposed = true;
}
- }
- if (EqtTrace.IsInfoEnabled)
- {
- EqtTrace.Info("DiscoveryRequest.Dispose: Completed.");
+ // Indicate that object has been disposed
+ _discoveryCompleted = null;
+ _disposed = true;
}
}
- #endregion
+ if (EqtTrace.IsInfoEnabled)
+ {
+ EqtTrace.Info("DiscoveryRequest.Dispose: Completed.");
+ }
+ }
- #region privates fields
+ #endregion
- ///
- /// Request Data
- ///
- internal IRequestData requestData;
+ #region privates fields
- ///
- /// If this request has been disposed.
- ///
- private bool disposed = false;
+ ///
+ /// Request Data
+ ///
+ internal IRequestData RequestData;
- ///
- /// It get set when current discovery request is completed.
- ///
- private ManualResetEvent discoveryCompleted = new ManualResetEvent(false);
+ ///
+ /// If this request has been disposed.
+ ///
+ private bool _disposed = false;
- ///
- /// Sync object for various operations
- ///
- private object syncObject = new Object();
+ ///
+ /// It get set when current discovery request is completed.
+ ///
+ private ManualResetEvent _discoveryCompleted = new(false);
- ///
- /// Whether or not the test discovery is in progress.
- ///
- private bool discoveryInProgress;
+ ///
+ /// Sync object for various operations
+ ///
+ private readonly object _syncObject = new();
- ///
- /// Discovery Start Time
- ///
- private DateTime discoveryStartTime;
+ ///
+ /// Discovery Start Time
+ ///
+ private DateTime _discoveryStartTime;
- #endregion
- }
-}
+ #endregion
+}
\ No newline at end of file
diff --git a/src/Microsoft.TestPlatform.Client/Execution/TestRunRequest.cs b/src/Microsoft.TestPlatform.Client/Execution/TestRunRequest.cs
index e3efa01cb8..36697c47ea 100644
--- a/src/Microsoft.TestPlatform.Client/Execution/TestRunRequest.cs
+++ b/src/Microsoft.TestPlatform.Client/Execution/TestRunRequest.cs
@@ -1,698 +1,681 @@
// 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.Client.Execution
+namespace Microsoft.VisualStudio.TestPlatform.Client.Execution;
+
+using System;
+using System.Collections.Generic;
+using System.Collections.ObjectModel;
+using System.Diagnostics;
+using System.Linq;
+using System.Threading;
+
+using Common.Telemetry;
+using CommunicationUtilities;
+using CommunicationUtilities.Interfaces;
+using Microsoft.VisualStudio.TestPlatform.CommunicationUtilities.ObjectModel;
+using ObjectModel;
+using Microsoft.VisualStudio.TestPlatform.ObjectModel.Client;
+using Microsoft.VisualStudio.TestPlatform.ObjectModel.Client.Interfaces;
+using ObjectModel.Engine;
+using ObjectModel.Logging;
+using Microsoft.VisualStudio.TestPlatform.ObjectModel.Utilities;
+using Utilities;
+
+using ClientResources = Resources.Resources;
+
+public class TestRunRequest : ITestRunRequest, ITestRunEventsHandler2
{
- using System;
- using System.Collections.Generic;
- using System.Collections.ObjectModel;
- using System.Diagnostics;
- using System.Linq;
- using System.Threading;
-
- using Microsoft.VisualStudio.TestPlatform.Common.Telemetry;
- using Microsoft.VisualStudio.TestPlatform.CommunicationUtilities;
- using Microsoft.VisualStudio.TestPlatform.CommunicationUtilities.Interfaces;
- using Microsoft.VisualStudio.TestPlatform.CommunicationUtilities.ObjectModel;
- using Microsoft.VisualStudio.TestPlatform.ObjectModel;
- using Microsoft.VisualStudio.TestPlatform.ObjectModel.Client;
- using Microsoft.VisualStudio.TestPlatform.ObjectModel.Client.Interfaces;
- using Microsoft.VisualStudio.TestPlatform.ObjectModel.Engine;
- using Microsoft.VisualStudio.TestPlatform.ObjectModel.Logging;
- using Microsoft.VisualStudio.TestPlatform.ObjectModel.Utilities;
- using Microsoft.VisualStudio.TestPlatform.Utilities;
-
- using ClientResources = Microsoft.VisualStudio.TestPlatform.Client.Resources.Resources;
- using CommunicationObjectModel = Microsoft.VisualStudio.TestPlatform.CommunicationUtilities.ObjectModel;
-
- public class TestRunRequest : ITestRunRequest, ITestRunEventsHandler2
+ ///
+ /// Specifies whether the run is disposed or not
+ ///
+ private bool _disposed;
+
+ ///
+ /// Sync object for various operations
+ ///
+ private readonly object _syncObject = new();
+
+ ///
+ /// Sync object for cancel operation
+ ///
+ private readonly object _cancelSyncObject = new();
+
+ ///
+ /// The run completion event which will be signaled on completion of test run.
+ ///
+ private ManualResetEvent _runCompletionEvent = new(true);
+
+ ///
+ /// Tracks the time taken by each run request
+ ///
+ private Stopwatch _runRequestTimeTracker;
+
+ private readonly IDataSerializer _dataSerializer;
+
+ ///
+ /// Time out for run provided by client.
+ ///
+ private long _testSessionTimeout;
+
+ private Timer _timer;
+
+ ///
+ /// Execution Start Time
+ ///
+ private DateTime _executionStartTime;
+
+ ///
+ /// Request Data
+ ///
+ private readonly IRequestData _requestData;
+
+ internal TestRunRequest(IRequestData requestData, TestRunCriteria testRunCriteria, IProxyExecutionManager executionManager, ITestLoggerManager loggerManager) :
+ this(requestData, testRunCriteria, executionManager, loggerManager, JsonDataSerializer.Instance)
{
- ///
- /// The criteria/config for this test run request.
- ///
- internal TestRunCriteria testRunCriteria;
-
- ///
- /// Specifies whether the run is disposed or not
- ///
- private bool disposed;
-
- ///
- /// Sync object for various operations
- ///
- private object syncObject = new object();
-
- ///
- /// Sync object for cancel operation
- ///
- private object cancelSyncObject = new object();
-
- ///
- /// The run completion event which will be signaled on completion of test run.
- ///
- private ManualResetEvent runCompletionEvent = new ManualResetEvent(true);
-
- ///
- /// Tracks the time taken by each run request
- ///
- private Stopwatch runRequestTimeTracker;
-
- private IDataSerializer dataSerializer;
-
- ///
- /// Time out for run provided by client.
- ///
- private long testSessionTimeout;
-
- private Timer timer;
-
- ///
- /// Execution Start Time
- ///
- private DateTime executionStartTime;
-
- ///
- /// Request Data
- ///
- private IRequestData requestData;
-
- internal TestRunRequest(IRequestData requestData, TestRunCriteria testRunCriteria, IProxyExecutionManager executionManager, ITestLoggerManager loggerManager) :
- this(requestData, testRunCriteria, executionManager, loggerManager, JsonDataSerializer.Instance)
- {
- }
+ }
- internal TestRunRequest(IRequestData requestData, TestRunCriteria testRunCriteria, IProxyExecutionManager executionManager, ITestLoggerManager loggerManager, IDataSerializer dataSerializer)
+ internal TestRunRequest(IRequestData requestData, TestRunCriteria testRunCriteria, IProxyExecutionManager executionManager, ITestLoggerManager loggerManager, IDataSerializer dataSerializer)
+ {
+ Debug.Assert(testRunCriteria != null, "Test run criteria cannot be null");
+ Debug.Assert(executionManager != null, "ExecutionManager cannot be null");
+ Debug.Assert(requestData != null, "request Data is null");
+ Debug.Assert(loggerManager != null, "LoggerManager cannot be null");
+
+ if (EqtTrace.IsVerboseEnabled)
{
- Debug.Assert(testRunCriteria != null, "Test run criteria cannot be null");
- Debug.Assert(executionManager != null, "ExecutionManager cannot be null");
- Debug.Assert(requestData != null, "request Data is null");
- Debug.Assert(loggerManager != null, "LoggerManager cannot be null");
+ EqtTrace.Verbose("TestRunRequest.ExecuteAsync: Creating test run request.");
+ }
- if (EqtTrace.IsVerboseEnabled)
- {
- EqtTrace.Verbose("TestRunRequest.ExecuteAsync: Creating test run request.");
- }
+ TestRunCriteria = testRunCriteria;
+ ExecutionManager = executionManager;
+ LoggerManager = loggerManager;
+ State = TestRunState.Pending;
+ _dataSerializer = dataSerializer;
+ _requestData = requestData;
+ }
- this.testRunCriteria = testRunCriteria;
- this.ExecutionManager = executionManager;
- this.LoggerManager = loggerManager;
- this.State = TestRunState.Pending;
- this.dataSerializer = dataSerializer;
- this.requestData = requestData;
- }
+ #region ITestRunRequest
- #region ITestRunRequest
+ ///
+ /// Execute the test run asynchronously
+ ///
+ /// The process id of test host.
+ public int ExecuteAsync()
+ {
+ EqtTrace.Verbose("TestRunRequest.ExecuteAsync: Starting.");
- ///
- /// Execute the test run asynchronously
- ///
- /// The process id of test host.
- public int ExecuteAsync()
+ lock (_syncObject)
{
- EqtTrace.Verbose("TestRunRequest.ExecuteAsync: Starting.");
+ if (_disposed)
+ {
+ throw new ObjectDisposedException("testRunRequest");
+ }
- lock (this.syncObject)
+ if (State != TestRunState.Pending)
{
- if (this.disposed)
- {
- throw new ObjectDisposedException("testRunRequest");
- }
+ throw new InvalidOperationException(ClientResources.InvalidStateForExecution);
+ }
- if (this.State != TestRunState.Pending)
- {
- throw new InvalidOperationException(ClientResources.InvalidStateForExecution);
- }
+ _executionStartTime = DateTime.UtcNow;
- this.executionStartTime = DateTime.UtcNow;
+ // Collecting Number of sources Sent For Execution
+ var numberOfSources = (uint)(TestRunCriteria.Sources != null ? TestRunCriteria.Sources.Count() : 0);
+ _requestData.MetricsCollection.Add(TelemetryDataConstants.NumberOfSourcesSentForRun, numberOfSources);
- // Collecting Number of sources Sent For Execution
- var numberOfSources = (uint)(testRunCriteria.Sources != null ? testRunCriteria.Sources.Count() : 0);
- this.requestData.MetricsCollection.Add(TelemetryDataConstants.NumberOfSourcesSentForRun, numberOfSources);
+ if (EqtTrace.IsInfoEnabled)
+ {
+ EqtTrace.Info("TestRunRequest.ExecuteAsync: Starting run with settings:{0}", TestRunCriteria);
+ }
- if (EqtTrace.IsInfoEnabled)
- {
- EqtTrace.Info("TestRunRequest.ExecuteAsync: Starting run with settings:{0}", this.testRunCriteria);
- }
+ if (EqtTrace.IsVerboseEnabled)
+ {
+ // Waiting for warm up to be over.
+ EqtTrace.Verbose("TestRunRequest.ExecuteAsync: Wait for the first run request is over.");
+ }
- if (EqtTrace.IsVerboseEnabled)
- {
- // Waiting for warm up to be over.
- EqtTrace.Verbose("TestRunRequest.ExecuteAsync: Wait for the first run request is over.");
- }
+ State = TestRunState.InProgress;
- this.State = TestRunState.InProgress;
+ // Reset the run completion event
+ // (This needs to be done before queuing the test run because if the test run finishes fast then runCompletion event can
+ // remain in non-signaled state even though run is actually complete.
+ _runCompletionEvent.Reset();
- // Reset the run completion event
- // (This needs to be done before queuing the test run because if the test run finishes fast then runCompletion event can
- // remain in non-signaled state even though run is actually complete.
- this.runCompletionEvent.Reset();
+ try
+ {
+ var runConfiguration = XmlRunSettingsUtilities.GetRunConfigurationNode(TestRunCriteria.TestRunSettings);
+ _testSessionTimeout = runConfiguration.TestSessionTimeout;
- try
+ if (_testSessionTimeout > 0)
{
- var runConfiguration = XmlRunSettingsUtilities.GetRunConfigurationNode(this.TestRunCriteria.TestRunSettings);
- this.testSessionTimeout = runConfiguration.TestSessionTimeout;
-
- if (testSessionTimeout > 0)
+ if (EqtTrace.IsVerboseEnabled)
{
- if (EqtTrace.IsVerboseEnabled)
- {
- EqtTrace.Verbose(String.Format("TestRunRequest.ExecuteAsync: TestSessionTimeout is {0} milliseconds.", testSessionTimeout));
- }
-
- this.timer = new Timer(this.OnTestSessionTimeout, null, TimeSpan.FromMilliseconds(testSessionTimeout), TimeSpan.FromMilliseconds(0));
+ EqtTrace.Verbose(String.Format("TestRunRequest.ExecuteAsync: TestSessionTimeout is {0} milliseconds.", _testSessionTimeout));
}
- this.runRequestTimeTracker = new Stopwatch();
+ _timer = new Timer(OnTestSessionTimeout, null, TimeSpan.FromMilliseconds(_testSessionTimeout), TimeSpan.FromMilliseconds(0));
+ }
- // Start the stop watch for calculating the test run time taken overall
- this.runRequestTimeTracker.Start();
- var testRunStartEvent = new TestRunStartEventArgs(this.testRunCriteria);
- this.LoggerManager.HandleTestRunStart(testRunStartEvent);
- this.OnRunStart.SafeInvoke(this, testRunStartEvent, "TestRun.TestRunStart");
- int processId = this.ExecutionManager.StartTestRun(this.testRunCriteria, this);
+ _runRequestTimeTracker = new Stopwatch();
- if (EqtTrace.IsInfoEnabled)
- {
- EqtTrace.Info("TestRunRequest.ExecuteAsync: Started.");
- }
+ // Start the stop watch for calculating the test run time taken overall
+ _runRequestTimeTracker.Start();
+ var testRunStartEvent = new TestRunStartEventArgs(TestRunCriteria);
+ LoggerManager.HandleTestRunStart(testRunStartEvent);
+ OnRunStart.SafeInvoke(this, testRunStartEvent, "TestRun.TestRunStart");
+ int processId = ExecutionManager.StartTestRun(TestRunCriteria, this);
- return processId;
- }
- catch
+ if (EqtTrace.IsInfoEnabled)
{
- this.State = TestRunState.Pending;
- throw;
+ EqtTrace.Info("TestRunRequest.ExecuteAsync: Started.");
}
+
+ return processId;
+ }
+ catch
+ {
+ State = TestRunState.Pending;
+ throw;
}
}
+ }
- internal void OnTestSessionTimeout(object obj)
+ internal void OnTestSessionTimeout(object obj)
+ {
+ if (EqtTrace.IsVerboseEnabled)
{
- if (EqtTrace.IsVerboseEnabled)
- {
- EqtTrace.Verbose(string.Format("TestRunRequest.OnTestSessionTimeout: calling cancellation as test run exceeded testSessionTimeout {0} milliseconds", testSessionTimeout));
- }
+ EqtTrace.Verbose(string.Format("TestRunRequest.OnTestSessionTimeout: calling cancellation as test run exceeded testSessionTimeout {0} milliseconds", _testSessionTimeout));
+ }
+
+ string message = string.Format(ClientResources.TestSessionTimeoutMessage, _testSessionTimeout);
+ var testMessagePayload = new TestMessagePayload { MessageLevel = TestMessageLevel.Error, Message = message };
+ var rawMessage = _dataSerializer.SerializePayload(MessageType.TestMessage, testMessagePayload);
+
+ HandleLogMessage(TestMessageLevel.Error, message);
+ HandleRawMessage(rawMessage);
+ Abort();
+ }
- string message = string.Format(ClientResources.TestSessionTimeoutMessage, this.testSessionTimeout);
- var testMessagePayload = new TestMessagePayload { MessageLevel = TestMessageLevel.Error, Message = message };
- var rawMessage = this.dataSerializer.SerializePayload(MessageType.TestMessage, testMessagePayload);
+ ///
+ /// Wait for the run completion
+ ///
+ public bool WaitForCompletion(int timeout)
+ {
+ EqtTrace.Verbose("TestRunRequest.WaitForCompletion: Waiting with timeout {0}.", timeout);
- this.HandleLogMessage(TestMessageLevel.Error, message);
- this.HandleRawMessage(rawMessage);
- this.Abort();
+ if (_disposed)
+ {
+ throw new ObjectDisposedException("testRunRequest");
}
- ///
- /// Wait for the run completion
- ///
- public bool WaitForCompletion(int timeout)
+ if (State != TestRunState.InProgress
+ && !(State == TestRunState.Completed
+ || State == TestRunState.Canceled
+ || State == TestRunState.Aborted))
{
- EqtTrace.Verbose("TestRunRequest.WaitForCompletion: Waiting with timeout {0}.", timeout);
+ // If run is already terminated, then we should not throw an exception.
+ throw new InvalidOperationException(ClientResources.WaitForCompletionOperationIsNotAllowedWhenNoTestRunIsActive);
+ }
+
+ // This method is not synchronized as it can lead to dead-lock
+ // (the runCompletionEvent cannot be raised unless that lock is released)
+
+ // Wait for run completion (In case m_runCompletionEvent is closed, then waitOne will throw nice error)
+ return _runCompletionEvent == null || _runCompletionEvent.WaitOne(timeout);
+ }
- if (this.disposed)
+ ///
+ /// Cancel the test run asynchronously
+ ///
+ public void CancelAsync()
+ {
+ EqtTrace.Verbose("TestRunRequest.CancelAsync: Canceling.");
+
+ lock (_cancelSyncObject)
+ {
+ if (_disposed)
{
- throw new ObjectDisposedException("testRunRequest");
+ EqtTrace.Warning("Ignoring TestRunRequest.CancelAsync() as testRunRequest object has already been disposed.");
+ return;
}
- if (this.State != TestRunState.InProgress
- && !(this.State == TestRunState.Completed
- || this.State == TestRunState.Canceled
- || this.State == TestRunState.Aborted))
+ if (State != TestRunState.InProgress)
{
- // If run is already terminated, then we should not throw an exception.
- throw new InvalidOperationException(ClientResources.WaitForCompletionOperationIsNotAllowedWhenNoTestRunIsActive);
+ EqtTrace.Info("Ignoring TestRunRequest.CancelAsync(). No test run in progress.");
}
-
- // This method is not synchronized as it can lead to dead-lock
- // (the runCompletionEvent cannot be raised unless that lock is released)
-
- // Wait for run completion (In case m_runCompletionEvent is closed, then waitOne will throw nice error)
- if (this.runCompletionEvent != null)
+ else
{
- return this.runCompletionEvent.WaitOne(timeout);
+ // Inform the service about run cancellation
+ ExecutionManager.Cancel(this);
}
-
- return true;
}
- ///
- /// Cancel the test run asynchronously
- ///
- public void CancelAsync()
- {
- EqtTrace.Verbose("TestRunRequest.CancelAsync: Canceling.");
-
- lock (this.cancelSyncObject)
- {
- if (this.disposed)
- {
- EqtTrace.Warning("Ignoring TestRunRequest.CancelAsync() as testRunRequest object has already been disposed.");
- return;
- }
-
- if (this.State != TestRunState.InProgress)
- {
- EqtTrace.Info("Ignoring TestRunRequest.CancelAsync(). No test run in progress.");
- }
- else
- {
- // Inform the service about run cancellation
- this.ExecutionManager.Cancel(this);
- }
- }
+ EqtTrace.Info("TestRunRequest.CancelAsync: Canceled.");
+ }
- EqtTrace.Info("TestRunRequest.CancelAsync: Canceled.");
- }
+ ///
+ /// Aborts the test run execution process.
+ ///
+ public void Abort()
+ {
+ EqtTrace.Verbose("TestRunRequest.Abort: Aborting.");
- ///
- /// Aborts the test run execution process.
- ///
- public void Abort()
+ lock (_cancelSyncObject)
{
- EqtTrace.Verbose("TestRunRequest.Abort: Aborting.");
-
- lock (this.cancelSyncObject)
+ if (_disposed)
{
- if (this.disposed)
- {
- EqtTrace.Warning("Ignoring TestRunRequest.Abort() as testRunRequest object has already been disposed");
- return;
- }
-
- if (this.State != TestRunState.InProgress)
- {
- EqtTrace.Info("Ignoring TestRunRequest.Abort(). No test run in progress.");
- }
- else
- {
- this.ExecutionManager.Abort(this);
- }
+ EqtTrace.Warning("Ignoring TestRunRequest.Abort() as testRunRequest object has already been disposed");
+ return;
}
- EqtTrace.Info("TestRunRequest.Abort: Aborted.");
+ if (State != TestRunState.InProgress)
+ {
+ EqtTrace.Info("Ignoring TestRunRequest.Abort(). No test run in progress.");
+ }
+ else
+ {
+ ExecutionManager.Abort(this);
+ }
}
+ EqtTrace.Info("TestRunRequest.Abort: Aborted.");
+ }
- ///
- /// Specifies the test run criteria
- ///
- public ITestRunConfiguration TestRunConfiguration
- {
- get { return this.testRunCriteria; }
- }
- ///
- /// State of the test run
- ///
- public TestRunState State { get; private set; }
+ ///
+ /// Specifies the test run criteria
+ ///
+ public ITestRunConfiguration TestRunConfiguration
+ {
+ get { return TestRunCriteria; }
+ }
+
+ ///
+ /// State of the test run
+ ///
+ public TestRunState State { get; private set; }
- ///
- /// Raised when the test run statistics change.
- ///
- public event EventHandler OnRunStatsChange;
+ ///
+ /// Raised when the test run statistics change.
+ ///
+ public event EventHandler OnRunStatsChange;
- ///
- /// Raised when the test run starts.
- ///
- public event EventHandler OnRunStart;
+ ///
+ /// Raised when the test run starts.
+ ///
+ public event EventHandler OnRunStart;
- ///
- /// Raised when the test message is received.
- ///
- public event EventHandler TestRunMessage;
+ ///
+ /// Raised when the test message is received.
+ ///
+ public event EventHandler TestRunMessage;
- ///
- /// Raised when the test run completes.
- ///
- public event EventHandler OnRunCompletion;
+ ///
+ /// Raised when the test run completes.
+ ///
+ public event EventHandler OnRunCompletion;
- ///
- /// Raised when data collection message is received.
- ///
+ ///
+ /// Raised when data collection message is received.
+ ///
#pragma warning disable 67
- public event EventHandler DataCollectionMessage;
+ public event EventHandler DataCollectionMessage;
#pragma warning restore 67
- ///
- /// Raised when a test run event raw message is received from host
- /// This is required if one wants to re-direct the message over the process boundary without any processing overhead
- /// All the run events should come as raw messages as well as proper serialized events like OnRunStatsChange
- ///
- public event EventHandler OnRawMessageReceived;
-
- ///
- /// Parent execution manager
- ///
- internal IProxyExecutionManager ExecutionManager
- {
- get; private set;
- }
+ ///
+ /// Raised when a test run event raw message is received from host
+ /// This is required if one wants to re-direct the message over the process boundary without any processing overhead
+ /// All the run events should come as raw messages as well as proper serialized events like OnRunStatsChange
+ ///
+ public event EventHandler OnRawMessageReceived;
+
+ ///
+ /// Parent execution manager
+ ///
+ internal IProxyExecutionManager ExecutionManager
+ {
+ get; private set;
+ }
- ///
- /// Logger manager.
- ///
- internal ITestLoggerManager LoggerManager
- {
- get; private set;
- }
+ ///
+ /// Logger manager.
+ ///
+ internal ITestLoggerManager LoggerManager
+ {
+ get; private set;
+ }
- #endregion
+ #endregion
- #region IDisposable implementation
+ #region IDisposable implementation
- // Summary:
- // Performs application-defined tasks associated with freeing, releasing, or
- // resetting unmanaged resources.
- public void Dispose()
- {
- this.Dispose(true);
+ // Summary:
+ // Performs application-defined tasks associated with freeing, releasing, or
+ // resetting unmanaged resources.
+ public void Dispose()
+ {
+ Dispose(true);
- GC.SuppressFinalize(this);
- }
+ GC.SuppressFinalize(this);
+ }
+
+ #endregion
- #endregion
+ ///
+ /// The criteria/config for this test run request.
+ ///
+ public TestRunCriteria TestRunCriteria { get; internal set; }
- public TestRunCriteria TestRunCriteria
+ ///
+ /// Invoked when test run is complete
+ ///
+ public void HandleTestRunComplete(TestRunCompleteEventArgs runCompleteArgs, TestRunChangedEventArgs lastChunkArgs, ICollection runContextAttachments, ICollection executorUris)
+ {
+ if (runCompleteArgs == null)
{
- get { return this.testRunCriteria; }
+ throw new ArgumentNullException(nameof(runCompleteArgs));
}
- ///
- /// Invoked when test run is complete
- ///
- public void HandleTestRunComplete(TestRunCompleteEventArgs runCompleteArgs, TestRunChangedEventArgs lastChunkArgs, ICollection runContextAttachments, ICollection executorUris)
+ bool isAborted = runCompleteArgs.IsAborted;
+ bool isCanceled = runCompleteArgs.IsCanceled;
+
+ EqtTrace.Verbose("TestRunRequest:TestRunComplete: Starting. IsAborted:{0} IsCanceled:{1}.", isAborted, isCanceled);
+
+ lock (_syncObject)
{
- if (runCompleteArgs == null)
+ // If this object is disposed, don't do anything
+ if (_disposed)
{
- throw new ArgumentNullException(nameof(runCompleteArgs));
+ EqtTrace.Warning("TestRunRequest.TestRunComplete: Ignoring as the object is disposed.");
+ return;
}
- bool isAborted = runCompleteArgs.IsAborted;
- bool isCanceled = runCompleteArgs.IsCanceled;
+ if (_runCompletionEvent.WaitOne(0))
+ {
+ EqtTrace.Info("TestRunRequest:TestRunComplete:Ignoring duplicate event. IsAborted:{0} IsCanceled:{1}.", isAborted, isCanceled);
+ return;
+ }
- EqtTrace.Verbose("TestRunRequest:TestRunComplete: Starting. IsAborted:{0} IsCanceled:{1}.", isAborted, isCanceled);
+ // Disposing off the resources held by the execution manager so that the test host process can shut down.
+ ExecutionManager?.Close();
- lock (this.syncObject)
+ try
{
- // If this object is disposed, don't do anything
- if (this.disposed)
- {
- EqtTrace.Warning("TestRunRequest.TestRunComplete: Ignoring as the object is disposed.");
- return;
- }
+ _runRequestTimeTracker.Stop();
- if (this.runCompletionEvent.WaitOne(0))
+ if (lastChunkArgs != null)
{
- EqtTrace.Info("TestRunRequest:TestRunComplete:Ignoring duplicate event. IsAborted:{0} IsCanceled:{1}.", isAborted, isCanceled);
- return;
+ // Raised the changed event also
+ LoggerManager.HandleTestRunStatsChange(lastChunkArgs);
+ OnRunStatsChange.SafeInvoke(this, lastChunkArgs, "TestRun.RunStatsChanged");
}
- // Disposing off the resources held by the execution manager so that the test host process can shut down.
- this.ExecutionManager?.Close();
-
- try
+ TestRunCompleteEventArgs runCompletedEvent =
+ new(
+ runCompleteArgs.TestRunStatistics,
+ runCompleteArgs.IsCanceled,
+ runCompleteArgs.IsAborted,
+ runCompleteArgs.Error,
+ // This is required as TMI adapter is sending attachments as List which cannot be type casted to Collection.
+ runContextAttachments != null ? new Collection(runContextAttachments.ToList()) : null,
+ runCompleteArgs.InvokedDataCollectors,
+ _runRequestTimeTracker.Elapsed);
+
+ // Ignore the time sent (runCompleteArgs.ElapsedTimeInRunningTests)
+ // by either engines - as both calculate at different points
+ // If we use them, it would be an incorrect comparison between TAEF and Rocksteady
+ LoggerManager.HandleTestRunComplete(runCompletedEvent);
+ OnRunCompletion.SafeInvoke(this, runCompletedEvent, "TestRun.TestRunComplete");
+ }
+ finally
+ {
+ if (isCanceled)
{
- this.runRequestTimeTracker.Stop();
-
- if (lastChunkArgs != null)
- {
- // Raised the changed event also
- this.LoggerManager.HandleTestRunStatsChange(lastChunkArgs);
- this.OnRunStatsChange.SafeInvoke(this, lastChunkArgs, "TestRun.RunStatsChanged");
- }
-
- TestRunCompleteEventArgs runCompletedEvent =
- new TestRunCompleteEventArgs(
- runCompleteArgs.TestRunStatistics,
- runCompleteArgs.IsCanceled,
- runCompleteArgs.IsAborted,
- runCompleteArgs.Error,
- // This is required as TMI adapter is sending attachments as List which cannot be type casted to Collection.
- runContextAttachments != null ? new Collection(runContextAttachments.ToList()) : null,
- runCompleteArgs.InvokedDataCollectors,
- this.runRequestTimeTracker.Elapsed);
-
- // Ignore the time sent (runCompleteArgs.ElapsedTimeInRunningTests)
- // by either engines - as both calculate at different points
- // If we use them, it would be an incorrect comparison between TAEF and Rocksteady
- this.LoggerManager.HandleTestRunComplete(runCompletedEvent);
- this.OnRunCompletion.SafeInvoke(this, runCompletedEvent, "TestRun.TestRunComplete");
+ State = TestRunState.Canceled;
}
- finally
+ else
{
- if (isCanceled)
- {
- this.State = TestRunState.Canceled;
- }
- else if (isAborted)
- {
- this.State = TestRunState.Aborted;
- }
- else
- {
- this.State = TestRunState.Completed;
- }
+ State = isAborted ? TestRunState.Aborted : TestRunState.Completed;
+ }
- // Notify the waiting handle that run is complete
- this.runCompletionEvent.Set();
+ // Notify the waiting handle that run is complete
+ _runCompletionEvent.Set();
- var executionTotalTimeTaken = DateTime.UtcNow - this.executionStartTime;
+ var executionTotalTimeTaken = DateTime.UtcNow - _executionStartTime;
- // Fill in the time taken to complete the run
- this.requestData.MetricsCollection.Add(TelemetryDataConstants.TimeTakenInSecForRun, executionTotalTimeTaken.TotalSeconds);
+ // Fill in the time taken to complete the run
+ _requestData.MetricsCollection.Add(TelemetryDataConstants.TimeTakenInSecForRun, executionTotalTimeTaken.TotalSeconds);
- // Fill in the Metrics From Test Host Process
- var metrics = runCompleteArgs.Metrics;
- if (metrics != null && metrics.Count != 0)
+ // Fill in the Metrics From Test Host Process
+ var metrics = runCompleteArgs.Metrics;
+ if (metrics != null && metrics.Count != 0)
+ {
+ foreach (var metric in metrics)
{
- foreach (var metric in metrics)
- {
- this.requestData.MetricsCollection.Add(metric.Key, metric.Value);
- }
+ _requestData.MetricsCollection.Add(metric.Key, metric.Value);
}
}
-
- EqtTrace.Info("TestRunRequest:TestRunComplete: Completed.");
}
+
+ EqtTrace.Info("TestRunRequest:TestRunComplete: Completed.");
}
+ }
- ///
- /// Invoked when test run statistics change.
- ///
- [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1062:Validate arguments of public methods", MessageId = "1", Justification = "This is not an external event")]
- public virtual void HandleTestRunStatsChange(TestRunChangedEventArgs testRunChangedArgs)
+ ///
+ /// Invoked when test run statistics change.
+ ///
+ public virtual void HandleTestRunStatsChange(TestRunChangedEventArgs testRunChangedArgs)
+ {
+ if (testRunChangedArgs != null)
{
- if (testRunChangedArgs != null)
+ EqtTrace.Verbose("TestRunRequest:SendTestRunStatsChange: Starting.");
+ if (testRunChangedArgs.ActiveTests != null)
{
- EqtTrace.Verbose("TestRunRequest:SendTestRunStatsChange: Starting.");
- if (testRunChangedArgs.ActiveTests != null)
- {
- // Do verbose check to save performance in iterating test cases
- if (EqtTrace.IsVerboseEnabled)
- {
- foreach (TestCase testCase in testRunChangedArgs.ActiveTests)
- {
- EqtTrace.Verbose("InProgress is {0}", testCase.DisplayName);
- }
- }
- }
-
- lock (this.syncObject)
+ // Do verbose check to save performance in iterating test cases
+ if (EqtTrace.IsVerboseEnabled)
{
- // If this object is disposed, don't do anything
- if (this.disposed)
+ foreach (TestCase testCase in testRunChangedArgs.ActiveTests)
{
- EqtTrace.Warning("TestRunRequest.SendTestRunStatsChange: Ignoring as the object is disposed.");
- return;
+ EqtTrace.Verbose("InProgress is {0}", testCase.DisplayName);
}
-
- // TODO: Invoke this event in a separate thread.
- // For now, I am setting the ConcurrencyMode on the callback attribute to Multiple
- this.LoggerManager.HandleTestRunStatsChange(testRunChangedArgs);
- this.OnRunStatsChange.SafeInvoke(this, testRunChangedArgs, "TestRun.RunStatsChanged");
}
-
- EqtTrace.Info("TestRunRequest:SendTestRunStatsChange: Completed.");
}
- }
- ///
- /// Invoked when log messages are received
- ///
- public void HandleLogMessage(TestMessageLevel level, string message)
- {
- EqtTrace.Verbose("TestRunRequest:SendTestRunMessage: Starting.");
-
- lock (this.syncObject)
+ lock (_syncObject)
{
// If this object is disposed, don't do anything
- if (this.disposed)
+ if (_disposed)
{
- EqtTrace.Warning("TestRunRequest.SendTestRunMessage: Ignoring as the object is disposed.");
+ EqtTrace.Warning("TestRunRequest.SendTestRunStatsChange: Ignoring as the object is disposed.");
return;
}
- var testRunMessageEvent = new TestRunMessageEventArgs(level, message);
- this.LoggerManager.HandleTestRunMessage(testRunMessageEvent);
- this.TestRunMessage.SafeInvoke(this, testRunMessageEvent, "TestRun.LogMessages");
+ // TODO: Invoke this event in a separate thread.
+ // For now, I am setting the ConcurrencyMode on the callback attribute to Multiple
+ LoggerManager.HandleTestRunStatsChange(testRunChangedArgs);
+ OnRunStatsChange.SafeInvoke(this, testRunChangedArgs, "TestRun.RunStatsChanged");
}
- EqtTrace.Info("TestRunRequest:SendTestRunMessage: Completed.");
+ EqtTrace.Info("TestRunRequest:SendTestRunStatsChange: Completed.");
}
+ }
- ///
- /// Handle Raw message directly from the host
- ///
- ///
- public void HandleRawMessage(string rawMessage)
- {
- // Note: Deserialize rawMessage only if required.
-
- var message = this.LoggerManager.LoggersInitialized || this.requestData.IsTelemetryOptedIn ?
- this.dataSerializer.DeserializeMessage(rawMessage) : null;
+ ///
+ /// Invoked when log messages are received
+ ///
+ public void HandleLogMessage(TestMessageLevel level, string message)
+ {
+ EqtTrace.Verbose("TestRunRequest:SendTestRunMessage: Starting.");
- if (string.Equals(message?.MessageType, MessageType.ExecutionComplete))
+ lock (_syncObject)
+ {
+ // If this object is disposed, don't do anything
+ if (_disposed)
{
- var testRunCompletePayload = this.dataSerializer.DeserializePayload(message);
- rawMessage = UpdateRawMessageWithTelemetryInfo(testRunCompletePayload, message) ?? rawMessage;
- HandleLoggerManagerTestRunComplete(testRunCompletePayload);
+ EqtTrace.Warning("TestRunRequest.SendTestRunMessage: Ignoring as the object is disposed.");
+ return;
}
- this.OnRawMessageReceived?.Invoke(this, rawMessage);
+ var testRunMessageEvent = new TestRunMessageEventArgs(level, message);
+ LoggerManager.HandleTestRunMessage(testRunMessageEvent);
+ TestRunMessage.SafeInvoke(this, testRunMessageEvent, "TestRun.LogMessages");
}
- ///
- /// Handles LoggerManager's TestRunComplete.
- ///
- /// TestRun complete payload.
- private void HandleLoggerManagerTestRunComplete(TestRunCompletePayload testRunCompletePayload)
- {
- if (this.LoggerManager.LoggersInitialized && testRunCompletePayload != null)
- {
- // Send last chunk to logger manager.
- if (testRunCompletePayload.LastRunTests != null)
- {
- this.LoggerManager.HandleTestRunStatsChange(testRunCompletePayload.LastRunTests);
- }
+ EqtTrace.Info("TestRunRequest:SendTestRunMessage: Completed.");
+ }
- // Note: In HandleRawMessage attachments are considered from TestRunCompleteArgs, while in HandleTestRunComplete attachments are considered directly from testRunCompletePayload.
- // Ideally we should have attachmentSets at one place only.
- // Send test run complete to logger manager.
- TestRunCompleteEventArgs testRunCompleteArgs =
- new TestRunCompleteEventArgs(
- testRunCompletePayload.TestRunCompleteArgs.TestRunStatistics,
- testRunCompletePayload.TestRunCompleteArgs.IsCanceled,
- testRunCompletePayload.TestRunCompleteArgs.IsAborted,
- testRunCompletePayload.TestRunCompleteArgs.Error,
- testRunCompletePayload.TestRunCompleteArgs.AttachmentSets,
- testRunCompletePayload.TestRunCompleteArgs.InvokedDataCollectors,
- this.runRequestTimeTracker.Elapsed);
- this.LoggerManager.HandleTestRunComplete(testRunCompleteArgs);
- }
+ ///
+ /// Handle Raw message directly from the host
+ ///
+ ///
+ public void HandleRawMessage(string rawMessage)
+ {
+ // Note: Deserialize rawMessage only if required.
+
+ var message = LoggerManager.LoggersInitialized || _requestData.IsTelemetryOptedIn ?
+ _dataSerializer.DeserializeMessage(rawMessage) : null;
+
+ if (string.Equals(message?.MessageType, MessageType.ExecutionComplete))
+ {
+ var testRunCompletePayload = _dataSerializer.DeserializePayload(message);
+ rawMessage = UpdateRawMessageWithTelemetryInfo(testRunCompletePayload, message) ?? rawMessage;
+ HandleLoggerManagerTestRunComplete(testRunCompletePayload);
}
- ///
- /// Update raw message with telemetry info.
- ///
- /// Test run complete payload.
- /// Updated rawMessage.
- ///
- private string UpdateRawMessageWithTelemetryInfo(TestRunCompletePayload testRunCompletePayload, Message message)
+ OnRawMessageReceived?.Invoke(this, rawMessage);
+ }
+
+ ///
+ /// Handles LoggerManager's TestRunComplete.
+ ///
+ /// TestRun complete payload.
+ private void HandleLoggerManagerTestRunComplete(TestRunCompletePayload testRunCompletePayload)
+ {
+ if (LoggerManager.LoggersInitialized && testRunCompletePayload != null)
{
- var rawMessage = default(string);
- if (this.requestData.IsTelemetryOptedIn)
+ // Send last chunk to logger manager.
+ if (testRunCompletePayload.LastRunTests != null)
{
- if (testRunCompletePayload?.TestRunCompleteArgs != null)
- {
- if (testRunCompletePayload.TestRunCompleteArgs.Metrics == null)
- {
- testRunCompletePayload.TestRunCompleteArgs.Metrics = this.requestData.MetricsCollection.Metrics;
- }
- else
- {
- foreach (var kvp in this.requestData.MetricsCollection.Metrics)
- {
- testRunCompletePayload.TestRunCompleteArgs.Metrics[kvp.Key] = kvp.Value;
- }
- }
+ LoggerManager.HandleTestRunStatsChange(testRunCompletePayload.LastRunTests);
+ }
- // Fill in the time taken to complete the run
- var executionTotalTimeTakenForDesignMode = DateTime.UtcNow - this.executionStartTime;
- testRunCompletePayload.TestRunCompleteArgs.Metrics[TelemetryDataConstants.TimeTakenInSecForRun] = executionTotalTimeTakenForDesignMode.TotalSeconds;
- }
+ // Note: In HandleRawMessage attachments are considered from TestRunCompleteArgs, while in HandleTestRunComplete attachments are considered directly from testRunCompletePayload.
+ // Ideally we should have attachmentSets at one place only.
+ // Send test run complete to logger manager.
+ TestRunCompleteEventArgs testRunCompleteArgs =
+ new(
+ testRunCompletePayload.TestRunCompleteArgs.TestRunStatistics,
+ testRunCompletePayload.TestRunCompleteArgs.IsCanceled,
+ testRunCompletePayload.TestRunCompleteArgs.IsAborted,
+ testRunCompletePayload.TestRunCompleteArgs.Error,
+ testRunCompletePayload.TestRunCompleteArgs.AttachmentSets,
+ testRunCompletePayload.TestRunCompleteArgs.InvokedDataCollectors,
+ _runRequestTimeTracker.Elapsed);
+ LoggerManager.HandleTestRunComplete(testRunCompleteArgs);
+ }
+ }
- if (message is VersionedMessage message1)
+ ///
+ /// Update raw message with telemetry info.
+ ///
+ /// Test run complete payload.
+ /// Updated rawMessage.
+ ///
+ private string UpdateRawMessageWithTelemetryInfo(TestRunCompletePayload testRunCompletePayload, Message message)
+ {
+ var rawMessage = default(string);
+ if (_requestData.IsTelemetryOptedIn)
+ {
+ if (testRunCompletePayload?.TestRunCompleteArgs != null)
+ {
+ if (testRunCompletePayload.TestRunCompleteArgs.Metrics == null)
{
- var version = message1.Version;
-
- rawMessage = this.dataSerializer.SerializePayload(
- MessageType.ExecutionComplete,
- testRunCompletePayload,
- version);
+ testRunCompletePayload.TestRunCompleteArgs.Metrics = _requestData.MetricsCollection.Metrics;
}
else
{
- rawMessage = this.dataSerializer.SerializePayload(
- MessageType.ExecutionComplete,
- testRunCompletePayload);
+ foreach (var kvp in _requestData.MetricsCollection.Metrics)
+ {
+ testRunCompletePayload.TestRunCompleteArgs.Metrics[kvp.Key] = kvp.Value;
+ }
}
- }
- return rawMessage;
- }
+ // Fill in the time taken to complete the run
+ var executionTotalTimeTakenForDesignMode = DateTime.UtcNow - _executionStartTime;
+ testRunCompletePayload.TestRunCompleteArgs.Metrics[TelemetryDataConstants.TimeTakenInSecForRun] = executionTotalTimeTakenForDesignMode.TotalSeconds;
+ }
- ///
- /// Launch process with debugger attached
- ///
- ///
- /// processid
- public int LaunchProcessWithDebuggerAttached(TestProcessStartInfo testProcessStartInfo)
- {
- int processId = -1;
+ if (message is VersionedMessage message1)
+ {
+ var version = message1.Version;
- // Only launch while the test run is in progress and the launcher is a debug one
- if (this.State == TestRunState.InProgress && this.testRunCriteria.TestHostLauncher.IsDebug)
+ rawMessage = _dataSerializer.SerializePayload(
+ MessageType.ExecutionComplete,
+ testRunCompletePayload,
+ version);
+ }
+ else
{
- processId = this.testRunCriteria.TestHostLauncher.LaunchTestHost(testProcessStartInfo);
+ rawMessage = _dataSerializer.SerializePayload(
+ MessageType.ExecutionComplete,
+ testRunCompletePayload);
}
-
- return processId;
}
- ///
- public bool AttachDebuggerToProcess(int pid)
+ return rawMessage;
+ }
+
+ ///
+ /// Launch process with debugger attached
+ ///
+ ///
+ /// processid
+ public int LaunchProcessWithDebuggerAttached(TestProcessStartInfo testProcessStartInfo)
+ {
+ int processId = -1;
+
+ // Only launch while the test run is in progress and the launcher is a debug one
+ if (State == TestRunState.InProgress && TestRunCriteria.TestHostLauncher.IsDebug)
{
- return this.testRunCriteria.TestHostLauncher is ITestHostLauncher2 launcher
- && launcher.AttachDebuggerToProcess(pid);
+ processId = TestRunCriteria.TestHostLauncher.LaunchTestHost(testProcessStartInfo);
}
- ///
- /// Dispose the run
- ///
- ///
- protected virtual void Dispose(bool disposing)
- {
- EqtTrace.Verbose("TestRunRequest.Dispose: Starting.");
+ return processId;
+ }
+
+ ///
+ public bool AttachDebuggerToProcess(int pid)
+ {
+ return TestRunCriteria.TestHostLauncher is ITestHostLauncher2 launcher
+ && launcher.AttachDebuggerToProcess(pid);
+ }
- lock (this.syncObject)
+ ///
+ /// Dispose the run
+ ///
+ ///
+ protected virtual void Dispose(bool disposing)
+ {
+ EqtTrace.Verbose("TestRunRequest.Dispose: Starting.");
+
+ lock (_syncObject)
+ {
+ if (!_disposed)
{
- if (!this.disposed)
+ if (disposing)
{
- if (disposing)
- {
- this.runCompletionEvent?.Dispose();
- }
-
- // Indicate that object has been disposed
- this.runCompletionEvent = null;
- this.disposed = true;
+ _runCompletionEvent?.Dispose();
}
- }
- EqtTrace.Info("TestRunRequest.Dispose: Completed.");
+ // Indicate that object has been disposed
+ _runCompletionEvent = null;
+ _disposed = true;
+ }
}
+
+ EqtTrace.Info("TestRunRequest.Dispose: Completed.");
}
-}
+}
\ No newline at end of file
diff --git a/src/Microsoft.TestPlatform.Client/RequestHelper/ITestRequestManager.cs b/src/Microsoft.TestPlatform.Client/RequestHelper/ITestRequestManager.cs
index 714e1df9cb..224d4c22d8 100644
--- a/src/Microsoft.TestPlatform.Client/RequestHelper/ITestRequestManager.cs
+++ b/src/Microsoft.TestPlatform.Client/RequestHelper/ITestRequestManager.cs
@@ -1,110 +1,110 @@
// 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.Client.RequestHelper
-{
- using System;
- using System.Collections.Generic;
- using Microsoft.VisualStudio.TestPlatform.Client;
- using Microsoft.VisualStudio.TestPlatform.Common.Interfaces;
- using Microsoft.VisualStudio.TestPlatform.ObjectModel.Client;
- using Microsoft.VisualStudio.TestPlatform.ObjectModel.Client.Interfaces;
- using Microsoft.VisualStudio.TestPlatform.ObjectModel.Client.Payloads;
+namespace Microsoft.VisualStudio.TestPlatform.Client.RequestHelper;
+
+using System;
+using System.Collections.Generic;
+
+using Common.Interfaces;
+using Microsoft.VisualStudio.TestPlatform.ObjectModel.Client;
+using Microsoft.VisualStudio.TestPlatform.ObjectModel.Client.Interfaces;
+using Microsoft.VisualStudio.TestPlatform.ObjectModel.Client.Payloads;
+
+///
+/// Defines the contract for running various requests.
+///
+public interface ITestRequestManager : IDisposable
+{
///
- /// Defines the contract for running various requests.
+ /// Initializes the extensions while probing additional paths.
///
- public interface ITestRequestManager : IDisposable
- {
- ///
- /// Initializes the extensions while probing additional paths.
- ///
- ///
- /// Paths to additional extensions.
- /// Skip extension filtering by name if true.
- void InitializeExtensions(
- IEnumerable pathToAdditionalExtensions,
- bool skipExtensionFilters);
+ ///
+ /// Paths to additional extensions.
+ /// Skip extension filtering by name if true.
+ void InitializeExtensions(
+ IEnumerable pathToAdditionalExtensions,
+ bool skipExtensionFilters);
- ///
- /// Resets vstest.console.exe options.
- ///
- void ResetOptions();
+ ///
+ /// Resets vstest.console.exe options.
+ ///
+ void ResetOptions();
- ///
- /// Discovers tests given a list of sources and some run settings.
- ///
- ///
- /// Discovery payload.
- /// Discovery events registrar.
- /// Protocol related information.
- void DiscoverTests(
- DiscoveryRequestPayload discoveryPayload,
- ITestDiscoveryEventsRegistrar disoveryEventsRegistrar,
- ProtocolConfig protocolConfig);
+ ///
+ /// Discovers tests given a list of sources and some run settings.
+ ///
+ ///
+ /// Discovery payload.
+ /// Discovery events registrar.
+ /// Protocol related information.
+ void DiscoverTests(
+ DiscoveryRequestPayload discoveryPayload,
+ ITestDiscoveryEventsRegistrar disoveryEventsRegistrar,
+ ProtocolConfig protocolConfig);
- ///
- /// Runs tests given a list of sources and some run settings.
- ///
- ///
- /// Test run request payload.
- /// Custom test host launcher for the run.
- /// Run events registrar.
- /// Protocol related information.
- void RunTests(
- TestRunRequestPayload testRunRequestPayLoad,
- ITestHostLauncher customTestHostLauncher,
- ITestRunEventsRegistrar testRunEventsRegistrar,
- ProtocolConfig protocolConfig);
+ ///
+ /// Runs tests given a list of sources and some run settings.
+ ///
+ ///
+ /// Test run request payload.
+ /// Custom test host launcher for the run.
+ /// Run events registrar.
+ /// Protocol related information.
+ void RunTests(
+ TestRunRequestPayload testRunRequestPayLoad,
+ ITestHostLauncher customTestHostLauncher,
+ ITestRunEventsRegistrar testRunEventsRegistrar,
+ ProtocolConfig protocolConfig);
- ///
- /// Processes test run attachments.
- ///
- ///
- ///
- /// Test run attachments processing payload.
- ///
- ///
- /// Test run attachments processing events handler.
- ///
- /// Protocol related information.
- void ProcessTestRunAttachments(
- TestRunAttachmentsProcessingPayload testRunAttachmentsProcessingPayload,
- ITestRunAttachmentsProcessingEventsHandler testRunAttachmentsProcessingEventsHandler,
- ProtocolConfig protocolConfig);
+ ///
+ /// Processes test run attachments.
+ ///
+ ///
+ ///
+ /// Test run attachments processing payload.
+ ///
+ ///
+ /// Test run attachments processing events handler.
+ ///
+ /// Protocol related information.
+ void ProcessTestRunAttachments(
+ TestRunAttachmentsProcessingPayload testRunAttachmentsProcessingPayload,
+ ITestRunAttachmentsProcessingEventsHandler testRunAttachmentsProcessingEventsHandler,
+ ProtocolConfig protocolConfig);
- ///
- /// Starts a test session.
- ///
- ///
- /// The start test session payload.
- /// The custom test host launcher.
- /// The events handler.
- /// Protocol related information.
- void StartTestSession(
- StartTestSessionPayload payload,
- ITestHostLauncher testHostLauncher,
- ITestSessionEventsHandler eventsHandler,
- ProtocolConfig protocolConfig);
+ ///
+ /// Starts a test session.
+ ///
+ ///
+ /// The start test session payload.
+ /// The custom test host launcher.
+ /// The events handler.
+ /// Protocol related information.
+ void StartTestSession(
+ StartTestSessionPayload payload,
+ ITestHostLauncher testHostLauncher,
+ ITestSessionEventsHandler eventsHandler,
+ ProtocolConfig protocolConfig);
- ///
- /// Cancel the current test run request.
- ///
- void CancelTestRun();
+ ///
+ /// Cancel the current test run request.
+ ///
+ void CancelTestRun();
- ///
- /// Abort the current test run.
- ///
- void AbortTestRun();
+ ///
+ /// Abort the current test run.
+ ///
+ void AbortTestRun();
- ///
- /// Cancels the current discovery request.
- ///
- void CancelDiscovery();
+ ///
+ /// Cancels the current discovery request.
+ ///
+ void CancelDiscovery();
- ///
- /// Cancels the current test run attachments processing request.
- ///
- void CancelTestRunAttachmentsProcessing();
- }
-}
+ ///
+ /// Cancels the current test run attachments processing request.
+ ///
+ void CancelTestRunAttachmentsProcessing();
+}
\ No newline at end of file
diff --git a/src/Microsoft.TestPlatform.Client/TestPlatform.cs b/src/Microsoft.TestPlatform.Client/TestPlatform.cs
index bb7c62320d..3e3814b68c 100644
--- a/src/Microsoft.TestPlatform.Client/TestPlatform.cs
+++ b/src/Microsoft.TestPlatform.Client/TestPlatform.cs
@@ -1,339 +1,339 @@
// 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.Client
+namespace Microsoft.VisualStudio.TestPlatform.Client;
+
+using System;
+using System.Collections.Generic;
+using System.Globalization;
+using System.IO;
+using System.Linq;
+using System.Reflection;
+
+using Discovery;
+using Execution;
+using Common;
+using Common.ExtensionFramework;
+using Common.Hosting;
+using Common.Logging;
+using Microsoft.VisualStudio.TestPlatform.Common.Utilities;
+using CrossPlatEngine;
+using ObjectModel;
+using Microsoft.VisualStudio.TestPlatform.ObjectModel.Client;
+using ObjectModel.Engine;
+using ObjectModel.Host;
+using Microsoft.VisualStudio.TestPlatform.ObjectModel.Utilities;
+using PlatformAbstractions;
+using Utilities.Helpers;
+using Utilities.Helpers.Interfaces;
+
+using ClientResources = Resources.Resources;
+
+///
+/// Implementation for TestPlatform.
+///
+internal class TestPlatform : ITestPlatform
{
- using System;
- using System.Collections.Generic;
- using System.Globalization;
- using System.IO;
- using System.Linq;
- using System.Reflection;
-
- using Microsoft.VisualStudio.TestPlatform.Client.Discovery;
- using Microsoft.VisualStudio.TestPlatform.Client.Execution;
- using Microsoft.VisualStudio.TestPlatform.Common;
- using Microsoft.VisualStudio.TestPlatform.Common.ExtensionFramework;
- using Microsoft.VisualStudio.TestPlatform.Common.Hosting;
- using Microsoft.VisualStudio.TestPlatform.Common.Logging;
- using Microsoft.VisualStudio.TestPlatform.Common.Utilities;
- using Microsoft.VisualStudio.TestPlatform.CrossPlatEngine;
- using Microsoft.VisualStudio.TestPlatform.ObjectModel;
- using Microsoft.VisualStudio.TestPlatform.ObjectModel.Client;
- using Microsoft.VisualStudio.TestPlatform.ObjectModel.Engine;
- using Microsoft.VisualStudio.TestPlatform.ObjectModel.Host;
- using Microsoft.VisualStudio.TestPlatform.ObjectModel.Utilities;
- using Microsoft.VisualStudio.TestPlatform.PlatformAbstractions;
- using Microsoft.VisualStudio.TestPlatform.Utilities.Helpers;
- using Microsoft.VisualStudio.TestPlatform.Utilities.Helpers.Interfaces;
- using ClientResources = Resources.Resources;
+ private readonly TestRuntimeProviderManager _testHostProviderManager;
+
+ private readonly IFileHelper _fileHelper;
+
+ static TestPlatform()
+ {
+ // TODO: This is not the right way to force initialization of default extensions.
+ // Test runtime providers require this today. They're getting initialized even before
+ // test adapter paths are provided, which is incorrect.
+ AddExtensionAssembliesFromExtensionDirectory();
+ }
///
- /// Implementation for TestPlatform.
+ /// Initializes a new instance of the class.
///
- internal class TestPlatform : ITestPlatform
+ public TestPlatform()
+ : this(
+ new TestEngine(),
+ new FileHelper(),
+ TestRuntimeProviderManager.Instance)
{
- private readonly TestRuntimeProviderManager testHostProviderManager;
+ }
- private readonly IFileHelper fileHelper;
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ ///
+ /// The test engine.
+ /// The file helper.
+ /// The data.
+ protected TestPlatform(
+ ITestEngine testEngine,
+ IFileHelper filehelper,
+ TestRuntimeProviderManager testHostProviderManager)
+ {
+ TestEngine = testEngine;
+ _fileHelper = filehelper;
+ _testHostProviderManager = testHostProviderManager;
+ }
- static TestPlatform()
- {
- // TODO: This is not the right way to force initialization of default extensions.
- // Test runtime providers require this today. They're getting initialized even before
- // test adapter paths are provided, which is incorrect.
- AddExtensionAssembliesFromExtensionDirectory();
- }
+ ///
+ /// Gets or sets the test engine instance.
+ ///
+ private ITestEngine TestEngine { get; set; }
- ///
- /// Initializes a new instance of the class.
- ///
- public TestPlatform()
- : this(
- new TestEngine(),
- new FileHelper(),
- TestRuntimeProviderManager.Instance)
+ ///
+ public IDiscoveryRequest CreateDiscoveryRequest(
+ IRequestData requestData,
+ DiscoveryCriteria discoveryCriteria,
+ TestPlatformOptions options)
+ {
+ if (discoveryCriteria == null)
{
+ throw new ArgumentNullException(nameof(discoveryCriteria));
}
- ///
- /// Initializes a new instance of the class.
- ///
- ///
- /// The test engine.
- /// The file helper.
- /// The data.
- protected TestPlatform(
- ITestEngine testEngine,
- IFileHelper filehelper,
- TestRuntimeProviderManager testHostProviderManager)
+ // Update cache with Extension folder's files.
+ AddExtensionAssemblies(discoveryCriteria.RunSettings);
+
+ // Update extension assemblies from source when design mode is false.
+ var runConfiguration = XmlRunSettingsUtilities.GetRunConfigurationNode(discoveryCriteria.RunSettings);
+ if (!runConfiguration.DesignMode)
{
- this.TestEngine = testEngine;
- this.fileHelper = filehelper;
- this.testHostProviderManager = testHostProviderManager;
+ AddExtensionAssembliesFromSource(discoveryCriteria.Sources);
}
- ///
- /// Gets or sets the test engine instance.
- ///
- private ITestEngine TestEngine { get; set; }
+ // Initialize loggers.
+ var loggerManager = TestEngine.GetLoggerManager(requestData);
+ loggerManager.Initialize(discoveryCriteria.RunSettings);
- ///
- public IDiscoveryRequest CreateDiscoveryRequest(
- IRequestData requestData,
- DiscoveryCriteria discoveryCriteria,
- TestPlatformOptions options)
- {
- if (discoveryCriteria == null)
- {
- throw new ArgumentNullException(nameof(discoveryCriteria));
- }
+ var testHostManager = _testHostProviderManager.GetTestHostManagerByRunConfiguration(discoveryCriteria.RunSettings);
+ ThrowExceptionIfTestHostManagerIsNull(testHostManager, discoveryCriteria.RunSettings);
- // Update cache with Extension folder's files.
- this.AddExtensionAssemblies(discoveryCriteria.RunSettings);
-
- // Update extension assemblies from source when design mode is false.
- var runConfiguration = XmlRunSettingsUtilities.GetRunConfigurationNode(discoveryCriteria.RunSettings);
- if (!runConfiguration.DesignMode)
- {
- this.AddExtensionAssembliesFromSource(discoveryCriteria.Sources);
- }
+ testHostManager.Initialize(TestSessionMessageLogger.Instance, discoveryCriteria.RunSettings);
- // Initialize loggers.
- var loggerManager = this.TestEngine.GetLoggerManager(requestData);
- loggerManager.Initialize(discoveryCriteria.RunSettings);
+ var discoveryManager = TestEngine.GetDiscoveryManager(requestData, testHostManager, discoveryCriteria);
+ discoveryManager.Initialize(options?.SkipDefaultAdapters ?? false);
- var testHostManager = this.testHostProviderManager.GetTestHostManagerByRunConfiguration(discoveryCriteria.RunSettings);
- ThrowExceptionIfTestHostManagerIsNull(testHostManager, discoveryCriteria.RunSettings);
+ return new DiscoveryRequest(requestData, discoveryCriteria, discoveryManager, loggerManager);
+ }
- testHostManager.Initialize(TestSessionMessageLogger.Instance, discoveryCriteria.RunSettings);
+ ///
+ public ITestRunRequest CreateTestRunRequest(
+ IRequestData requestData,
+ TestRunCriteria testRunCriteria,
+ TestPlatformOptions options)
+ {
+ if (testRunCriteria == null)
+ {
+ throw new ArgumentNullException(nameof(testRunCriteria));
+ }
- var discoveryManager = this.TestEngine.GetDiscoveryManager(requestData, testHostManager, discoveryCriteria);
- discoveryManager.Initialize(options?.SkipDefaultAdapters ?? false);
+ AddExtensionAssemblies(testRunCriteria.TestRunSettings);
- return new DiscoveryRequest(requestData, discoveryCriteria, discoveryManager, loggerManager);
- }
+ var runConfiguration = XmlRunSettingsUtilities.GetRunConfigurationNode(testRunCriteria.TestRunSettings);
- ///
- public ITestRunRequest CreateTestRunRequest(
- IRequestData requestData,
- TestRunCriteria testRunCriteria,
- TestPlatformOptions options)
+ // Update extension assemblies from source when design mode is false.
+ if (!runConfiguration.DesignMode)
{
- if (testRunCriteria == null)
- {
- throw new ArgumentNullException(nameof(testRunCriteria));
- }
+ AddExtensionAssembliesFromSource(testRunCriteria);
+ }
- this.AddExtensionAssemblies(testRunCriteria.TestRunSettings);
+ // Initialize loggers.
+ var loggerManager = TestEngine.GetLoggerManager(requestData);
+ loggerManager.Initialize(testRunCriteria.TestRunSettings);
- var runConfiguration = XmlRunSettingsUtilities.GetRunConfigurationNode(testRunCriteria.TestRunSettings);
+ var testHostManager = _testHostProviderManager.GetTestHostManagerByRunConfiguration(testRunCriteria.TestRunSettings);
+ ThrowExceptionIfTestHostManagerIsNull(testHostManager, testRunCriteria.TestRunSettings);
- // Update extension assemblies from source when design mode is false.
- if (!runConfiguration.DesignMode)
- {
- this.AddExtensionAssembliesFromSource(testRunCriteria);
- }
+ testHostManager.Initialize(TestSessionMessageLogger.Instance, testRunCriteria.TestRunSettings);
- // Initialize loggers.
- var loggerManager = this.TestEngine.GetLoggerManager(requestData);
- loggerManager.Initialize(testRunCriteria.TestRunSettings);
+ // NOTE: The custom launcher should not be set when we have test session info available.
+ if (testRunCriteria.TestHostLauncher != null)
+ {
+ testHostManager.SetCustomLauncher(testRunCriteria.TestHostLauncher);
+ }
- var testHostManager = this.testHostProviderManager.GetTestHostManagerByRunConfiguration(testRunCriteria.TestRunSettings);
- ThrowExceptionIfTestHostManagerIsNull(testHostManager, testRunCriteria.TestRunSettings);
+ var executionManager = TestEngine.GetExecutionManager(requestData, testHostManager, testRunCriteria);
+ executionManager.Initialize(options?.SkipDefaultAdapters ?? false);
- testHostManager.Initialize(TestSessionMessageLogger.Instance, testRunCriteria.TestRunSettings);
+ return new TestRunRequest(requestData, testRunCriteria, executionManager, loggerManager);
+ }
- // NOTE: The custom launcher should not be set when we have test session info available.
- if (testRunCriteria.TestHostLauncher != null)
- {
- testHostManager.SetCustomLauncher(testRunCriteria.TestHostLauncher);
- }
+ ///
+ public bool StartTestSession(
+ IRequestData requestData,
+ StartTestSessionCriteria testSessionCriteria,
+ ITestSessionEventsHandler eventsHandler)
+ {
+ if (testSessionCriteria == null)
+ {
+ throw new ArgumentNullException(nameof(testSessionCriteria));
+ }
- var executionManager = this.TestEngine.GetExecutionManager(requestData, testHostManager, testRunCriteria);
- executionManager.Initialize(options?.SkipDefaultAdapters ?? false);
+ AddExtensionAssemblies(testSessionCriteria.RunSettings);
- return new TestRunRequest(requestData, testRunCriteria, executionManager, loggerManager);
+ var runConfiguration = XmlRunSettingsUtilities.GetRunConfigurationNode(testSessionCriteria.RunSettings);
+ if (!runConfiguration.DesignMode)
+ {
+ return false;
}
- ///
- public bool StartTestSession(
- IRequestData requestData,
- StartTestSessionCriteria testSessionCriteria,
- ITestSessionEventsHandler eventsHandler)
+ var testSessionManager = TestEngine.GetTestSessionManager(requestData, testSessionCriteria);
+ if (testSessionManager == null)
{
- if (testSessionCriteria == null)
- {
- throw new ArgumentNullException(nameof(testSessionCriteria));
- }
-
- this.AddExtensionAssemblies(testSessionCriteria.RunSettings);
-
- var runConfiguration = XmlRunSettingsUtilities.GetRunConfigurationNode(testSessionCriteria.RunSettings);
- if (!runConfiguration.DesignMode)
- {
- return false;
- }
+ // The test session manager is null because the combination of runsettings and
+ // sources tells us we should run in-process (i.e. in vstest.console). Because
+ // of this no session will be created because there's no testhost to be launched.
+ // Expecting a subsequent call to execute tests with the same set of parameters.
+ eventsHandler.HandleStartTestSessionComplete(null);
+ return false;
+ }
- var testSessionManager = this.TestEngine.GetTestSessionManager(requestData, testSessionCriteria);
- if (testSessionManager == null)
- {
- // The test session manager is null because the combination of runsettings and
- // sources tells us we should run in-process (i.e. in vstest.console). Because
- // of this no session will be created because there's no testhost to be launched.
- // Expecting a subsequent call to execute tests with the same set of parameters.
- eventsHandler.HandleStartTestSessionComplete(null);
- return false;
- }
+ return testSessionManager.StartSession(eventsHandler);
+ }
- return testSessionManager.StartSession(eventsHandler);
- }
+ ///
+ /// The dispose.
+ ///
+ public void Dispose()
+ {
+ throw new NotImplementedException();
+ }
- ///
- /// The dispose.
- ///
- public void Dispose()
- {
- throw new NotImplementedException();
- }
+ ///
+ public void UpdateExtensions(
+ IEnumerable pathToAdditionalExtensions,
+ bool skipExtensionFilters)
+ {
+ TestEngine.GetExtensionManager().UseAdditionalExtensions(pathToAdditionalExtensions, skipExtensionFilters);
+ }
- ///
- public void UpdateExtensions(
- IEnumerable pathToAdditionalExtensions,
- bool skipExtensionFilters)
- {
- this.TestEngine.GetExtensionManager().UseAdditionalExtensions(pathToAdditionalExtensions, skipExtensionFilters);
- }
+ ///
+ public void ClearExtensions()
+ {
+ TestEngine.GetExtensionManager().ClearExtensions();
+ }
- ///
- public void ClearExtensions()
+ private void ThrowExceptionIfTestHostManagerIsNull(
+ ITestRuntimeProvider testHostManager,
+ string settingsXml)
+ {
+ if (testHostManager == null)
{
- this.TestEngine.GetExtensionManager().ClearExtensions();
+ EqtTrace.Error("TestPlatform.CreateTestRunRequest: No suitable testHostProvider found for runsettings : {0}", settingsXml);
+ throw new TestPlatformException(string.Format(CultureInfo.CurrentCulture, ClientResources.NoTestHostProviderFound));
}
+ }
- private void ThrowExceptionIfTestHostManagerIsNull(
- ITestRuntimeProvider testHostManager,
- string settingsXml)
- {
- if (testHostManager == null)
- {
- EqtTrace.Error("TestPlatform.CreateTestRunRequest: No suitable testHostProvider found for runsettings : {0}", settingsXml);
- throw new TestPlatformException(string.Format(CultureInfo.CurrentCulture, ClientResources.NoTestHostProviderFound));
- }
- }
+ ///
+ /// Updates the test adapter paths provided through run settings to be used by the test
+ /// service.
+ ///
+ ///
+ /// The run settings.
+ private void AddExtensionAssemblies(string runSettings)
+ {
+ IEnumerable customTestAdaptersPaths = RunSettingsUtilities.GetTestAdaptersPaths(runSettings);
- ///
- /// Updates the test adapter paths provided through run settings to be used by the test
- /// service.
- ///
- ///
- /// The run settings.
- private void AddExtensionAssemblies(string runSettings)
+ if (customTestAdaptersPaths != null)
{
- IEnumerable customTestAdaptersPaths = RunSettingsUtilities.GetTestAdaptersPaths(runSettings);
-
- if (customTestAdaptersPaths != null)
+ foreach (string customTestAdaptersPath in customTestAdaptersPaths)
{
- foreach (string customTestAdaptersPath in customTestAdaptersPaths)
+ var adapterPath = Path.GetFullPath(Environment.ExpandEnvironmentVariables(customTestAdaptersPath));
+ if (!Directory.Exists(adapterPath))
{
- var adapterPath = Path.GetFullPath(Environment.ExpandEnvironmentVariables(customTestAdaptersPath));
- if (!Directory.Exists(adapterPath))
+ if (EqtTrace.IsWarningEnabled)
{
- if (EqtTrace.IsWarningEnabled)
- {
- EqtTrace.Warning(string.Format("AdapterPath Not Found:", adapterPath));
- }
-
- continue;
+ EqtTrace.Warning(string.Format("AdapterPath Not Found:", adapterPath));
}
- var extensionAssemblies = new List(
- this.fileHelper.EnumerateFiles(
- adapterPath,
- SearchOption.AllDirectories,
- TestPlatformConstants.TestAdapterEndsWithPattern,
- TestPlatformConstants.TestLoggerEndsWithPattern,
- TestPlatformConstants.DataCollectorEndsWithPattern,
- TestPlatformConstants.RunTimeEndsWithPattern));
+ continue;
+ }
- if (extensionAssemblies.Count > 0)
- {
- this.UpdateExtensions(extensionAssemblies, skipExtensionFilters: false);
- }
+ var extensionAssemblies = new List(
+ _fileHelper.EnumerateFiles(
+ adapterPath,
+ SearchOption.AllDirectories,
+ TestPlatformConstants.TestAdapterEndsWithPattern,
+ TestPlatformConstants.TestLoggerEndsWithPattern,
+ TestPlatformConstants.DataCollectorEndsWithPattern,
+ TestPlatformConstants.RunTimeEndsWithPattern));
+
+ if (extensionAssemblies.Count > 0)
+ {
+ UpdateExtensions(extensionAssemblies, skipExtensionFilters: false);
}
}
}
+ }
- ///
- /// Updates the extension assemblies from source directory.
- ///
- ///
- /// The test run criteria.
- private void AddExtensionAssembliesFromSource(TestRunCriteria testRunCriteria)
+ ///
+ /// Updates the extension assemblies from source directory.
+ ///
+ ///
+ /// The test run criteria.
+ private void AddExtensionAssembliesFromSource(TestRunCriteria testRunCriteria)
+ {
+ IEnumerable sources = testRunCriteria.Sources;
+ if (testRunCriteria.HasSpecificTests)
{
- IEnumerable sources = testRunCriteria.Sources;
- if (testRunCriteria.HasSpecificTests)
- {
- // If the test execution is with a test filter, group them by sources.
- sources = testRunCriteria.Tests.Select(tc => tc.Source).Distinct();
- }
-
- AddExtensionAssembliesFromSource(sources);
+ // If the test execution is with a test filter, group them by sources.
+ sources = testRunCriteria.Tests.Select(tc => tc.Source).Distinct();
}
- ///
- /// Updates the test logger paths from source directory.
- ///
- ///
- /// The list of sources.
- private void AddExtensionAssembliesFromSource(IEnumerable sources)
- {
- // Currently we support discovering loggers only from Source directory.
- var loggersToUpdate = new List();
+ AddExtensionAssembliesFromSource(sources);
+ }
- foreach (var source in sources)
- {
- var sourceDirectory = Path.GetDirectoryName(source);
- if (!string.IsNullOrEmpty(sourceDirectory)
- && this.fileHelper.DirectoryExists(sourceDirectory))
- {
- loggersToUpdate.AddRange(
- this.fileHelper.EnumerateFiles(
- sourceDirectory,
- SearchOption.TopDirectoryOnly,
- TestPlatformConstants.TestLoggerEndsWithPattern));
- }
- }
+ ///
+ /// Updates the test logger paths from source directory.
+ ///
+ ///
+ /// The list of sources.
+ private void AddExtensionAssembliesFromSource(IEnumerable sources)
+ {
+ // Currently we support discovering loggers only from Source directory.
+ var loggersToUpdate = new List();
- if (loggersToUpdate.Count > 0)
+ foreach (var source in sources)
+ {
+ var sourceDirectory = Path.GetDirectoryName(source);
+ if (!string.IsNullOrEmpty(sourceDirectory)
+ && _fileHelper.DirectoryExists(sourceDirectory))
{
- this.UpdateExtensions(loggersToUpdate, skipExtensionFilters: false);
+ loggersToUpdate.AddRange(
+ _fileHelper.EnumerateFiles(
+ sourceDirectory,
+ SearchOption.TopDirectoryOnly,
+ TestPlatformConstants.TestLoggerEndsWithPattern));
}
}
- ///
- /// Finds all test platform extensions from the `.\Extensions` directory. This is used to
- /// load the inbox extensions like TrxLogger and legacy test extensions like MSTest v1,
- /// MSTest C++, etc..
- ///
- private static void AddExtensionAssembliesFromExtensionDirectory()
+ if (loggersToUpdate.Count > 0)
{
- var fileHelper = new FileHelper();
- var extensionsFolder = Path.Combine(
- Path.GetDirectoryName(
- typeof(TestPlatform).GetTypeInfo().Assembly.GetAssemblyLocation()),
- "Extensions");
+ UpdateExtensions(loggersToUpdate, skipExtensionFilters: false);
+ }
+ }
- if (fileHelper.DirectoryExists(extensionsFolder))
- {
- var defaultExtensionPaths = fileHelper.EnumerateFiles(
- extensionsFolder,
- SearchOption.TopDirectoryOnly,
- ".dll",
- ".exe");
+ ///
+ /// Finds all test platform extensions from the `.\Extensions` directory. This is used to
+ /// load the inbox extensions like TrxLogger and legacy test extensions like MSTest v1,
+ /// MSTest C++, etc..
+ ///
+ private static void AddExtensionAssembliesFromExtensionDirectory()
+ {
+ var fileHelper = new FileHelper();
+ var extensionsFolder = Path.Combine(
+ Path.GetDirectoryName(
+ typeof(TestPlatform).GetTypeInfo().Assembly.GetAssemblyLocation()),
+ "Extensions");
- TestPluginCache.Instance.DefaultExtensionPaths = defaultExtensionPaths;
- }
+ if (fileHelper.DirectoryExists(extensionsFolder))
+ {
+ var defaultExtensionPaths = fileHelper.EnumerateFiles(
+ extensionsFolder,
+ SearchOption.TopDirectoryOnly,
+ ".dll",
+ ".exe");
+
+ TestPluginCache.Instance.DefaultExtensionPaths = defaultExtensionPaths;
}
}
-}
+}
\ No newline at end of file
diff --git a/src/Microsoft.TestPlatform.Client/TestPlatformFactory.cs b/src/Microsoft.TestPlatform.Client/TestPlatformFactory.cs
index 44ffa5e33f..b11ef880fa 100644
--- a/src/Microsoft.TestPlatform.Client/TestPlatformFactory.cs
+++ b/src/Microsoft.TestPlatform.Client/TestPlatformFactory.cs
@@ -1,24 +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.Client
+namespace Microsoft.VisualStudio.TestPlatform.Client;
+
+using Microsoft.VisualStudio.TestPlatform.ObjectModel.Client;
+
+///
+/// The factory class that provides an instance of the test platform.
+///
+public class TestPlatformFactory
{
- using Microsoft.VisualStudio.TestPlatform.ObjectModel.Client;
+ private static ITestPlatform s_testPlatform;
///
- /// The factory class that provides an instance of the test platform.
+ /// Gets an instance of the test platform.
///
- public class TestPlatformFactory
- {
- private static ITestPlatform testPlatform;
-
- ///
- /// Gets an instance of the test platform.
- ///
- /// The instance.
- public static ITestPlatform GetTestPlatform()
- {
- return testPlatform ?? (testPlatform = new TestPlatform());
- }
- }
-}
+ /// The instance.
+ public static ITestPlatform GetTestPlatform() => s_testPlatform ??= new TestPlatform();
+}
\ No newline at end of file
diff --git a/src/Microsoft.TestPlatform.Client/TestSession/TestSessionEventsHandler.cs b/src/Microsoft.TestPlatform.Client/TestSession/TestSessionEventsHandler.cs
index d5eaa5e6cc..f1004df645 100644
--- a/src/Microsoft.TestPlatform.Client/TestSession/TestSessionEventsHandler.cs
+++ b/src/Microsoft.TestPlatform.Client/TestSession/TestSessionEventsHandler.cs
@@ -1,72 +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.Client
+namespace Microsoft.VisualStudio.TestPlatform.Client;
+
+using CommunicationUtilities.Interfaces;
+
+using Microsoft.VisualStudio.TestPlatform.CommunicationUtilities.ObjectModel;
+using Microsoft.VisualStudio.TestPlatform.ObjectModel.Client;
+using Microsoft.VisualStudio.TestPlatform.ObjectModel.Client.Payloads;
+
+using ObjectModel.Logging;
+
+///
+/// Defines the way in which test session events should be handled.
+///
+internal class TestSessionEventsHandler : ITestSessionEventsHandler
{
- using Microsoft.VisualStudio.TestPlatform.CommunicationUtilities.Interfaces;
- using Microsoft.VisualStudio.TestPlatform.CommunicationUtilities.ObjectModel;
- using Microsoft.VisualStudio.TestPlatform.ObjectModel.Client;
- using Microsoft.VisualStudio.TestPlatform.ObjectModel.Client.Payloads;
- using Microsoft.VisualStudio.TestPlatform.ObjectModel.Logging;
+ private readonly ICommunicationManager _communicationManager;
///
- /// Defines the way in which test session events should be handled.
+ /// Creates an instance of the current class.
///
- internal class TestSessionEventsHandler : ITestSessionEventsHandler
+ ///
+ ///
+ /// The communication manager used for passing messages around.
+ ///
+ public TestSessionEventsHandler(ICommunicationManager communicationManager)
{
- private readonly ICommunicationManager communicationManager;
-
- ///
- /// Creates an instance of the current class.
- ///
- ///
- ///
- /// The communication manager used for passing messages around.
- ///
- public TestSessionEventsHandler(ICommunicationManager communicationManager)
- {
- this.communicationManager = communicationManager;
- }
+ _communicationManager = communicationManager;
+ }
- ///
- public void HandleStartTestSessionComplete(TestSessionInfo testSessionInfo)
+ ///
+ public void HandleStartTestSessionComplete(TestSessionInfo testSessionInfo)
+ {
+ var ackPayload = new StartTestSessionAckPayload()
{
- var ackPayload = new StartTestSessionAckPayload()
- {
- TestSessionInfo = testSessionInfo
- };
+ TestSessionInfo = testSessionInfo
+ };
- this.communicationManager.SendMessage(MessageType.StartTestSessionCallback, ackPayload);
- }
+ _communicationManager.SendMessage(MessageType.StartTestSessionCallback, ackPayload);
+ }
- ///
- public void HandleStopTestSessionComplete(TestSessionInfo testSessionInfo, bool stopped)
+ ///
+ public void HandleStopTestSessionComplete(TestSessionInfo testSessionInfo, bool stopped)
+ {
+ var ackPayload = new StopTestSessionAckPayload()
{
- var ackPayload = new StopTestSessionAckPayload()
- {
- TestSessionInfo = testSessionInfo,
- IsStopped = stopped
- };
+ TestSessionInfo = testSessionInfo,
+ IsStopped = stopped
+ };
- this.communicationManager.SendMessage(MessageType.StopTestSessionCallback, ackPayload);
- }
+ _communicationManager.SendMessage(MessageType.StopTestSessionCallback, ackPayload);
+ }
- ///
- public void HandleLogMessage(TestMessageLevel level, string message)
+ ///
+ public void HandleLogMessage(TestMessageLevel level, string message)
+ {
+ var messagePayload = new TestMessagePayload()
{
- var messagePayload = new TestMessagePayload()
- {
- MessageLevel = level,
- Message = message
- };
+ MessageLevel = level,
+ Message = message
+ };
- this.communicationManager.SendMessage(MessageType.TestMessage, messagePayload);
- }
+ _communicationManager.SendMessage(MessageType.TestMessage, messagePayload);
+ }
- ///
- public void HandleRawMessage(string rawMessage)
- {
- // No-op.
- }
+ ///
+ public void HandleRawMessage(string rawMessage)
+ {
+ // No-op.
}
-}
+}
\ No newline at end of file
diff --git a/src/Microsoft.TestPlatform.Common/Constants.cs b/src/Microsoft.TestPlatform.Common/Constants.cs
index 9f86b7f26b..921a80dcf1 100644
--- a/src/Microsoft.TestPlatform.Common/Constants.cs
+++ b/src/Microsoft.TestPlatform.Common/Constants.cs
@@ -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.Common
+namespace Microsoft.VisualStudio.TestPlatform.Common;
+
+///
+/// Defines the defaults used across different components.
+///
+public static class TestPlatformDefaults
{
///
- /// Defines the defaults used across different components.
+ /// string in the vstest.console.exe.config that specifies the bound on no of jobs in the job queue.
///
- public static class TestPlatformDefaults
- {
- ///
- /// string in the vstest.console.exe.config that specifies the bound on no of jobs in the job queue.
- ///
- public const string MaxNumberOfEventsLoggerEventQueueCanHold = "MaxNumberOfEventsLoggerEventQueueCanHold";
+ public const string MaxNumberOfEventsLoggerEventQueueCanHold = "MaxNumberOfEventsLoggerEventQueueCanHold";
- ///
- /// Default bound on the job queue.
- ///
- public const int DefaultMaxNumberOfEventsLoggerEventQueueCanHold = 500;
+ ///
+ /// Default bound on the job queue.
+ ///
+ public const int DefaultMaxNumberOfEventsLoggerEventQueueCanHold = 500;
- ///
- /// string in the vstest.console.exe.config that specifies the size bound on job queue.
- ///
- public const string MaxBytesLoggerEventQueueCanHold = "MaxBytesLoggerEventQueueCanHold";
+ ///
+ /// string in the vstest.console.exe.config that specifies the size bound on job queue.
+ ///
+ public const string MaxBytesLoggerEventQueueCanHold = "MaxBytesLoggerEventQueueCanHold";
- ///
- /// string in the rocksteady.exe.config that specifies whether or not we should try to keep memory usage by queue bounded.
- ///
- public const string EnableBoundsOnLoggerEventQueue = "EnableBoundsOnLoggerEventQueue";
+ ///
+ /// string in the rocksteady.exe.config that specifies whether or not we should try to keep memory usage by queue bounded.
+ ///
+ public const string EnableBoundsOnLoggerEventQueue = "EnableBoundsOnLoggerEventQueue";
- ///
- /// Default bound on the total size of all objects in the job queue. (25MB)
- ///
- public const int DefaultMaxBytesLoggerEventQueueCanHold = 25000000;
+ ///
+ /// Default bound on the total size of all objects in the job queue. (25MB)
+ ///
+ public const int DefaultMaxBytesLoggerEventQueueCanHold = 25000000;
- ///
- /// Default value of the boolean that determines whether or not job queue should be bounded.
- ///
- public const bool DefaultEnableBoundsOnLoggerEventQueue = true;
- }
+ ///
+ /// Default value of the boolean that determines whether or not job queue should be bounded.
+ ///
+ public const bool DefaultEnableBoundsOnLoggerEventQueue = true;
+}
+///
+/// Defines the constants used across different components.
+///
+public static class TestPlatformConstants
+{
///
- /// Defines the constants used across different components.
+ /// Pattern used to find the test adapters library using String.EndWith
///
- public static class TestPlatformConstants
- {
- ///
- /// Pattern used to find the test adapters library using String.EndWith
- ///
- public const string TestAdapterEndsWithPattern = @"TestAdapter.dll";
+ public const string TestAdapterEndsWithPattern = @"TestAdapter.dll";
- ///
- /// Pattern used to find the test loggers library using String.EndWith
- ///
- public const string TestLoggerEndsWithPattern = @"TestLogger.dll";
+ ///
+ /// Pattern used to find the test loggers library using String.EndWith
+ ///
+ public const string TestLoggerEndsWithPattern = @"TestLogger.dll";
- ///
- /// Pattern used to find the data collectors library using String.EndWith
- ///
- public const string DataCollectorEndsWithPattern = @"Collector.dll";
+ ///
+ /// Pattern used to find the data collectors library using String.EndWith
+ ///
+ public const string DataCollectorEndsWithPattern = @"Collector.dll";
- ///
- /// Pattern used to find the run time providers library using String.EndWith
- ///
- public const string RunTimeEndsWithPattern = @"RuntimeProvider.dll";
- }
-}
+ ///
+ /// Pattern used to find the run time providers library using String.EndWith
+ ///
+ public const string RunTimeEndsWithPattern = @"RuntimeProvider.dll";
+}
\ No newline at end of file
diff --git a/src/Microsoft.TestPlatform.Common/DataCollection/AfterTestRunEndResult.cs b/src/Microsoft.TestPlatform.Common/DataCollection/AfterTestRunEndResult.cs
index a81642ea30..d978238729 100644
--- a/src/Microsoft.TestPlatform.Common/DataCollection/AfterTestRunEndResult.cs
+++ b/src/Microsoft.TestPlatform.Common/DataCollection/AfterTestRunEndResult.cs
@@ -1,67 +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.Common.DataCollection
-{
- using Microsoft.VisualStudio.TestPlatform.ObjectModel;
- using System.Collections.Generic;
- using System.Collections.ObjectModel;
- using System.Runtime.Serialization;
+namespace Microsoft.VisualStudio.TestPlatform.Common.DataCollection;
- ///
- /// Payload object that is used to exchange data between datacollector process and runner process.
- ///
- [DataContract]
- public class AfterTestRunEndResult
+using ObjectModel;
+
+using System.Collections.Generic;
+using System.Collections.ObjectModel;
+using System.Runtime.Serialization;
+
+///
+/// Payload object that is used to exchange data between datacollector process and runner process.
+///
+[DataContract]
+public class AfterTestRunEndResult
+{
+ // We have more than one ctor for backward-compatibility reason but we don't want to add dependency on Newtosoft([JsonConstructor])
+ // We want to fallback to the non-public default constructor https://www.newtonsoft.com/json/help/html/T_Newtonsoft_Json_ConstructorHandling.htm during deserialization
+ private AfterTestRunEndResult()
{
- // We have more than one ctor for backward-compatibility reason but we don't want to add dependency on Newtonsoft([JsonConstructor])
- // We want to fallback to the non-public default constructor https://www.newtonsoft.com/json/help/html/T_Newtonsoft_Json_ConstructorHandling.htm during deserialization
- private AfterTestRunEndResult()
- {
- }
+ }
- ///
- /// Initializes a new instance of the class.
- ///
- ///
- /// The collection of attachment sets.
- ///
- ///
- /// The metrics.
- ///
- public AfterTestRunEndResult(Collection attachmentSets, IDictionary metrics)
- : this(attachmentSets, new Collection(), metrics)
- { }
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ ///
+ /// The collection of attachment sets.
+ ///
+ ///
+ /// The metrics.
+ ///
+ public AfterTestRunEndResult(Collection attachmentSets, IDictionary metrics)
+ : this(attachmentSets, new Collection(), metrics)
+ { }
- ///
- /// Initializes a new instance of the class.
- ///
- ///
- /// The collection of attachment sets.
- ///
- ///
- /// The collection of the DataCollectors invoked during test session
- ///
- ///
- /// The metrics.
- ///
- public AfterTestRunEndResult(Collection attachmentSets,
- Collection invokedDataCollectors,
- IDictionary metrics)
- {
- this.AttachmentSets = attachmentSets;
- this.InvokedDataCollectors = invokedDataCollectors;
- this.Metrics = metrics;
- }
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ ///
+ /// The collection of attachment sets.
+ ///
+ ///
+ /// The collection of the DataCollectors invoked during test session
+ ///
+ ///
+ /// The metrics.
+ ///
+ public AfterTestRunEndResult(Collection attachmentSets,
+ Collection invokedDataCollectors,
+ IDictionary metrics)
+ {
+ AttachmentSets = attachmentSets;
+ InvokedDataCollectors = invokedDataCollectors;
+ Metrics = metrics;
+ }
- [DataMember]
- public Collection AttachmentSets { get; private set; }
+ [DataMember]
+ public Collection AttachmentSets { get; private set; }
- [DataMember]
- public Collection InvokedDataCollectors { get; private set; }
+ [DataMember]
+ public Collection InvokedDataCollectors { get; private set; }
- [DataMember]
- public IDictionary Metrics { get; private set; }
- }
+ [DataMember]
+ public IDictionary Metrics { get; private set; }
}
\ No newline at end of file
diff --git a/src/Microsoft.TestPlatform.Common/DataCollection/BeforeTestRunStartResult.cs b/src/Microsoft.TestPlatform.Common/DataCollection/BeforeTestRunStartResult.cs
index a5b727f2fc..0e4ef10c1e 100644
--- a/src/Microsoft.TestPlatform.Common/DataCollection/BeforeTestRunStartResult.cs
+++ b/src/Microsoft.TestPlatform.Common/DataCollection/BeforeTestRunStartResult.cs
@@ -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.Common.DataCollection
-{
- using System.Collections.Generic;
- using System.Runtime.Serialization;
+namespace Microsoft.VisualStudio.TestPlatform.Common.DataCollection;
+
+using System.Collections.Generic;
+using System.Runtime.Serialization;
+///
+/// Payload object that is used to exchange data between datacollector process and runner process.
+///
+[DataContract]
+public class BeforeTestRunStartResult
+{
///
- /// Payload object that is used to exchange data between datacollector process and runner process.
+ /// Initializes a new instance of the class.
///
- [DataContract]
- public class BeforeTestRunStartResult
+ ///
+ /// The environment variables.
+ ///
+ ///
+ /// The data Collection Events Port.
+ ///
+ public BeforeTestRunStartResult(IDictionary environmentVariables, int dataCollectionEventsPort)
{
- ///
- /// Initializes a new instance of the class.
- ///
- ///
- /// The environment variables.
- ///
- ///
- /// The data Collection Events Port.
- ///
- public BeforeTestRunStartResult(IDictionary environmentVariables, int dataCollectionEventsPort)
- {
- this.EnvironmentVariables = environmentVariables;
- this.DataCollectionEventsPort = dataCollectionEventsPort;
- }
+ EnvironmentVariables = environmentVariables;
+ DataCollectionEventsPort = dataCollectionEventsPort;
+ }
- ///
- /// Gets the environment variable dictionary.
- ///
- [DataMember]
- public IDictionary EnvironmentVariables { get; private set; }
+ ///
+ /// Gets the environment variable dictionary.
+ ///
+ [DataMember]
+ public IDictionary EnvironmentVariables { get; private set; }
- ///
- /// Gets the data collection events port.
- ///
- [DataMember]
- public int DataCollectionEventsPort { get; private set; }
- }
+ ///
+ /// Gets the data collection events port.
+ ///
+ [DataMember]
+ public int DataCollectionEventsPort { get; private set; }
}
\ No newline at end of file
diff --git a/src/Microsoft.TestPlatform.Common/DataCollection/DataCollectionAttachmentManager.cs b/src/Microsoft.TestPlatform.Common/DataCollection/DataCollectionAttachmentManager.cs
index 50eaa48b6b..a952366b1d 100644
--- a/src/Microsoft.TestPlatform.Common/DataCollection/DataCollectionAttachmentManager.cs
+++ b/src/Microsoft.TestPlatform.Common/DataCollection/DataCollectionAttachmentManager.cs
@@ -1,387 +1,386 @@
// 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.Common.DataCollector
+namespace Microsoft.VisualStudio.TestPlatform.Common.DataCollector;
+
+using System;
+using System.Collections.Generic;
+using System.ComponentModel;
+using System.Diagnostics;
+using System.Globalization;
+using System.IO;
+using System.Linq;
+using System.Threading;
+using System.Threading.Tasks;
+
+using Interfaces;
+using ObjectModel;
+using Microsoft.VisualStudio.TestPlatform.ObjectModel.DataCollection;
+using Microsoft.VisualStudio.TestPlatform.ObjectModel.Logging;
+using Microsoft.VisualStudio.TestPlatform.Utilities;
+using Microsoft.VisualStudio.TestPlatform.Utilities.Helpers.Interfaces;
+
+///
+/// Manages file transfer from data collector to test runner service.
+///
+internal class DataCollectionAttachmentManager : IDataCollectionAttachmentManager
{
- using System;
- using System.Collections.Generic;
- using System.ComponentModel;
- using System.Diagnostics;
- using System.Globalization;
- using System.IO;
- using System.Linq;
- using System.Threading;
- using System.Threading.Tasks;
-
- using Microsoft.VisualStudio.TestPlatform.Common.DataCollector.Interfaces;
- using Microsoft.VisualStudio.TestPlatform.ObjectModel;
- using Microsoft.VisualStudio.TestPlatform.ObjectModel.DataCollection;
- using Microsoft.VisualStudio.TestPlatform.ObjectModel.Logging;
- using Microsoft.VisualStudio.TestPlatform.Utilities;
- using Microsoft.VisualStudio.TestPlatform.Utilities.Helpers.Interfaces;
+ private static readonly object AttachmentTaskLock = new();
+
+ #region Fields
///
- /// Manages file transfer from data collector to test runner service.
+ /// Default results directory to be used when user didn't specify.
///
- internal class DataCollectionAttachmentManager : IDataCollectionAttachmentManager
- {
- private static object attachmentTaskLock = new object();
+ private const string DefaultOutputDirectoryName = "TestResults";
- #region Fields
+ ///
+ /// Logger for data collection messages
+ ///
+ private IMessageSink _messageSink;
- ///
- /// Default results directory to be used when user didn't specify.
- ///
- private const string DefaultOutputDirectoryName = "TestResults";
+ ///
+ /// Attachment transfer tasks associated with a given datacollection context.
+ ///
+ private readonly Dictionary> _attachmentTasks;
- ///
- /// Logger for data collection messages
- ///
- private IMessageSink messageSink;
+ ///
+ /// Use to cancel attachment transfers if test run is canceled.
+ ///
+ private readonly CancellationTokenSource _cancellationTokenSource;
- ///
- /// Attachment transfer tasks associated with a given datacollection context.
- ///
- private Dictionary> attachmentTasks;
+ ///
+ /// File helper instance.
+ ///
+ private readonly IFileHelper _fileHelper;
- ///
- /// Use to cancel attachment transfers if test run is canceled.
- ///
- private CancellationTokenSource cancellationTokenSource;
+ #endregion
- ///
- /// File helper instance.
- ///
- private IFileHelper fileHelper;
+ #region Constructor
- #endregion
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ public DataCollectionAttachmentManager()
+ : this(new TestPlatform.Utilities.Helpers.FileHelper())
+ {
+ }
- #region Constructor
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// File helper instance.
+ protected DataCollectionAttachmentManager(IFileHelper fileHelper)
+ {
+ _fileHelper = fileHelper;
+ _cancellationTokenSource = new CancellationTokenSource();
+ _attachmentTasks = new Dictionary>();
+ AttachmentSets = new Dictionary>();
+ }
- ///
- /// Initializes a new instance of the class.
- ///
- public DataCollectionAttachmentManager()
- : this(new TestPlatform.Utilities.Helpers.FileHelper())
- {
- }
+ #endregion
- ///
- /// Initializes a new instance of the class.
- ///
- /// File helper instance.
- protected DataCollectionAttachmentManager(IFileHelper fileHelper)
- {
- this.fileHelper = fileHelper;
- this.cancellationTokenSource = new CancellationTokenSource();
- this.attachmentTasks = new Dictionary>();
- this.AttachmentSets = new Dictionary>();
- }
+ #region Properties
- #endregion
+ ///
+ /// Gets the session output directory.
+ ///
+ internal string SessionOutputDirectory { get; private set; }
- #region Properties
+ ///
+ /// Gets the attachment sets for the given datacollection context.
+ ///
+ internal Dictionary> AttachmentSets
+ {
+ get; private set;
+ }
+ #endregion
- ///
- /// Gets the session output directory.
- ///
- internal string SessionOutputDirectory { get; private set; }
+ #region public methods
- ///
- /// Gets the attachment sets for the given datacollection context.
- ///
- internal Dictionary> AttachmentSets
- {
- get; private set;
- }
- #endregion
+ ///
+ public void Initialize(SessionId id, string outputDirectory, IMessageSink messageSink)
+ {
+ ValidateArg.NotNull(id, nameof(id));
+ ValidateArg.NotNull(messageSink, nameof(messageSink));
- #region public methods
+ _messageSink = messageSink;
- ///
- public void Initialize(SessionId id, string outputDirectory, IMessageSink messageSink)
+ if (string.IsNullOrEmpty(outputDirectory))
{
- ValidateArg.NotNull(id, nameof(id));
- ValidateArg.NotNull(messageSink, nameof(messageSink));
-
- this.messageSink = messageSink;
-
- if (string.IsNullOrEmpty(outputDirectory))
- {
- this.SessionOutputDirectory = Path.Combine(Path.GetTempPath(), DefaultOutputDirectoryName, id.Id.ToString());
- }
- else
- {
- // Create a session specific directory under base output directory.
- var expandedOutputDirectory = Environment.ExpandEnvironmentVariables(outputDirectory);
- var absolutePath = Path.GetFullPath(expandedOutputDirectory);
- this.SessionOutputDirectory = Path.Combine(absolutePath, id.Id.ToString());
- }
-
- try
- {
- // Create the output directory if it doesn't exist.
- if (!Directory.Exists(this.SessionOutputDirectory))
- {
- Directory.CreateDirectory(this.SessionOutputDirectory);
- }
- }
- catch (UnauthorizedAccessException accessException)
- {
- string accessDeniedMessage = string.Format(CultureInfo.CurrentCulture, Resources.Resources.AccessDenied, accessException.Message);
- ConsoleOutput.Instance.Error(false, accessDeniedMessage);
- throw;
- }
-
+ SessionOutputDirectory = Path.Combine(Path.GetTempPath(), DefaultOutputDirectoryName, id.Id.ToString());
+ }
+ else
+ {
+ // Create a session specific directory under base output directory.
+ var expandedOutputDirectory = Environment.ExpandEnvironmentVariables(outputDirectory);
+ var absolutePath = Path.GetFullPath(expandedOutputDirectory);
+ SessionOutputDirectory = Path.Combine(absolutePath, id.Id.ToString());
}
- ///
- public List GetAttachments(DataCollectionContext dataCollectionContext)
+ try
{
- try
- {
- if (this.attachmentTasks.TryGetValue(dataCollectionContext, out var tasks))
- {
- Task.WhenAll(tasks.ToArray()).Wait();
- }
- }
- catch (Exception ex)
+ // Create the output directory if it doesn't exist.
+ if (!Directory.Exists(SessionOutputDirectory))
{
- EqtTrace.Error("DataCollectionAttachmentManager.GetAttachments: Fail to get attachments: {0} ", ex);
+ Directory.CreateDirectory(SessionOutputDirectory);
}
+ }
+ catch (UnauthorizedAccessException accessException)
+ {
+ string accessDeniedMessage = string.Format(CultureInfo.CurrentCulture, Resources.Resources.AccessDenied, accessException.Message);
+ ConsoleOutput.Instance.Error(false, accessDeniedMessage);
+ throw;
+ }
- List attachments = new List();
+ }
- if (this.AttachmentSets.TryGetValue(dataCollectionContext, out var uriAttachmentSetMap))
+ ///
+ public List GetAttachments(DataCollectionContext dataCollectionContext)
+ {
+ try
+ {
+ if (_attachmentTasks.TryGetValue(dataCollectionContext, out var tasks))
{
- attachments = uriAttachmentSetMap.Values.ToList();
- this.attachmentTasks.Remove(dataCollectionContext);
- this.AttachmentSets.Remove(dataCollectionContext);
+ Task.WhenAll(tasks.ToArray()).Wait();
}
-
- return attachments;
}
+ catch (Exception ex)
+ {
+ EqtTrace.Error("DataCollectionAttachmentManager.GetAttachments: Fail to get attachments: {0} ", ex);
+ }
+
+ List attachments = new();
- ///
- public void AddAttachment(FileTransferInformation fileTransferInfo, AsyncCompletedEventHandler sendFileCompletedCallback, Uri uri, string friendlyName)
+ if (AttachmentSets.TryGetValue(dataCollectionContext, out var uriAttachmentSetMap))
{
- ValidateArg.NotNull(fileTransferInfo, nameof(fileTransferInfo));
+ attachments = uriAttachmentSetMap.Values.ToList();
+ _attachmentTasks.Remove(dataCollectionContext);
+ AttachmentSets.Remove(dataCollectionContext);
+ }
- if (string.IsNullOrEmpty(this.SessionOutputDirectory))
- {
- if (EqtTrace.IsErrorEnabled)
- {
- EqtTrace.Error(
- "DataCollectionAttachmentManager.AddAttachment: Initialize not invoked.");
- }
+ return attachments;
+ }
- return;
- }
+ ///
+ public void AddAttachment(FileTransferInformation fileTransferInfo, AsyncCompletedEventHandler sendFileCompletedCallback, Uri uri, string friendlyName)
+ {
+ ValidateArg.NotNull(fileTransferInfo, nameof(fileTransferInfo));
- if (!this.AttachmentSets.ContainsKey(fileTransferInfo.Context))
+ if (string.IsNullOrEmpty(SessionOutputDirectory))
+ {
+ if (EqtTrace.IsErrorEnabled)
{
- var uriAttachmentSetMap = new Dictionary();
- this.AttachmentSets.Add(fileTransferInfo.Context, uriAttachmentSetMap);
- this.attachmentTasks.Add(fileTransferInfo.Context, new List());
+ EqtTrace.Error(
+ "DataCollectionAttachmentManager.AddAttachment: Initialize not invoked.");
}
- if (!this.AttachmentSets[fileTransferInfo.Context].ContainsKey(uri))
- {
- this.AttachmentSets[fileTransferInfo.Context].Add(uri, new AttachmentSet(uri, friendlyName));
- }
+ return;
+ }
- this.AddNewFileTransfer(fileTransferInfo, sendFileCompletedCallback, uri, friendlyName);
+ if (!AttachmentSets.ContainsKey(fileTransferInfo.Context))
+ {
+ var uriAttachmentSetMap = new Dictionary();
+ AttachmentSets.Add(fileTransferInfo.Context, uriAttachmentSetMap);
+ _attachmentTasks.Add(fileTransferInfo.Context, new List());
}
- ///
- public void Cancel()
+ if (!AttachmentSets[fileTransferInfo.Context].ContainsKey(uri))
{
- this.cancellationTokenSource.Cancel();
+ AttachmentSets[fileTransferInfo.Context].Add(uri, new AttachmentSet(uri, friendlyName));
}
- #endregion
+ AddNewFileTransfer(fileTransferInfo, sendFileCompletedCallback, uri, friendlyName);
+ }
- #region private methods
+ ///
+ public void Cancel()
+ {
+ _cancellationTokenSource.Cancel();
+ }
- ///
- /// Sanity checks on CopyRequestData
- ///
- ///
- /// The file Transfer Info.
- ///
- ///
- /// The local File Path.
- ///
- private static void Validate(FileTransferInformation fileTransferInfo, string localFilePath)
- {
- if (!File.Exists(fileTransferInfo.FileName))
- {
- throw new FileNotFoundException(
- string.Format(
- CultureInfo.CurrentCulture,
- "Could not find source file '{0}'.",
- fileTransferInfo.FileName));
- }
+ #endregion
- var directoryName = Path.GetDirectoryName(localFilePath);
+ #region private methods
- if (!Directory.Exists(directoryName))
- {
- Directory.CreateDirectory(directoryName);
- }
- else if (File.Exists(localFilePath))
- {
- File.Delete(localFilePath);
- }
+ ///
+ /// Sanity checks on CopyRequestData
+ ///
+ ///
+ /// The file Transfer Info.
+ ///
+ ///
+ /// The local File Path.
+ ///
+ private static void Validate(FileTransferInformation fileTransferInfo, string localFilePath)
+ {
+ if (!File.Exists(fileTransferInfo.FileName))
+ {
+ throw new FileNotFoundException(
+ string.Format(
+ CultureInfo.CurrentCulture,
+ "Could not find source file '{0}'.",
+ fileTransferInfo.FileName));
}
- ///
- /// Add a new file transfer (either copy/move) request.
- ///
- ///
- /// The file Transfer Info.
- ///
- ///
- /// The send File Completed Callback.
- ///
- ///
- /// The uri.
- ///
- ///
- /// The friendly Name.
- ///
- private void AddNewFileTransfer(FileTransferInformation fileTransferInfo, AsyncCompletedEventHandler sendFileCompletedCallback, Uri uri, string friendlyName)
+ var directoryName = Path.GetDirectoryName(localFilePath);
+
+ if (!Directory.Exists(directoryName))
{
- var context = fileTransferInfo.Context;
- Debug.Assert(
- context != null,
- "DataCollectionManager.AddNewFileTransfer: FileDataHeaderMessage with null context.");
-
- var testCaseId = fileTransferInfo.Context.HasTestCase
- ? fileTransferInfo.Context.TestExecId.Id.ToString()
- : string.Empty;
-
- var directoryPath = Path.Combine(
- this.SessionOutputDirectory,
- testCaseId);
- var localFilePath = Path.Combine(directoryPath, Path.GetFileName(fileTransferInfo.FileName));
-
- var task = Task.Factory.StartNew(
- () =>
- {
- Validate(fileTransferInfo, localFilePath);
+ Directory.CreateDirectory(directoryName);
+ }
+ else if (File.Exists(localFilePath))
+ {
+ File.Delete(localFilePath);
+ }
+ }
- if (this.cancellationTokenSource.Token.IsCancellationRequested)
- {
- this.cancellationTokenSource.Token.ThrowIfCancellationRequested();
- }
+ ///
+ /// Add a new file transfer (either copy/move) request.
+ ///
+ ///
+ /// The file Transfer Info.
+ ///
+ ///
+ /// The send File Completed Callback.
+ ///
+ ///
+ /// The uri.
+ ///
+ ///
+ /// The friendly Name.
+ ///
+ private void AddNewFileTransfer(FileTransferInformation fileTransferInfo, AsyncCompletedEventHandler sendFileCompletedCallback, Uri uri, string friendlyName)
+ {
+ var context = fileTransferInfo.Context;
+ Debug.Assert(
+ context != null,
+ "DataCollectionManager.AddNewFileTransfer: FileDataHeaderMessage with null context.");
+
+ var testCaseId = fileTransferInfo.Context.HasTestCase
+ ? fileTransferInfo.Context.TestExecId.Id.ToString()
+ : string.Empty;
+
+ var directoryPath = Path.Combine(
+ SessionOutputDirectory,
+ testCaseId);
+ var localFilePath = Path.Combine(directoryPath, Path.GetFileName(fileTransferInfo.FileName));
+
+ var task = Task.Factory.StartNew(
+ () =>
+ {
+ Validate(fileTransferInfo, localFilePath);
+
+ if (_cancellationTokenSource.Token.IsCancellationRequested)
+ {
+ _cancellationTokenSource.Token.ThrowIfCancellationRequested();
+ }
- try
+ try
+ {
+ if (fileTransferInfo.PerformCleanup)
{
- if (fileTransferInfo.PerformCleanup)
+ if (EqtTrace.IsInfoEnabled)
{
- if (EqtTrace.IsInfoEnabled)
- {
- EqtTrace.Info("DataCollectionAttachmentManager.AddNewFileTransfer : Moving file {0} to {1}", fileTransferInfo.FileName, localFilePath);
- }
+ EqtTrace.Info("DataCollectionAttachmentManager.AddNewFileTransfer : Moving file {0} to {1}", fileTransferInfo.FileName, localFilePath);
+ }
- this.fileHelper.MoveFile(fileTransferInfo.FileName, localFilePath);
+ _fileHelper.MoveFile(fileTransferInfo.FileName, localFilePath);
- if (EqtTrace.IsInfoEnabled)
- {
- EqtTrace.Info("DataCollectionAttachmentManager.AddNewFileTransfer : Moved file {0} to {1}", fileTransferInfo.FileName, localFilePath);
- }
+ if (EqtTrace.IsInfoEnabled)
+ {
+ EqtTrace.Info("DataCollectionAttachmentManager.AddNewFileTransfer : Moved file {0} to {1}", fileTransferInfo.FileName, localFilePath);
}
- else
+ }
+ else
+ {
+ if (EqtTrace.IsInfoEnabled)
{
- if (EqtTrace.IsInfoEnabled)
- {
- EqtTrace.Info("DataCollectionAttachmentManager.AddNewFileTransfer : Copying file {0} to {1}", fileTransferInfo.FileName, localFilePath);
- }
+ EqtTrace.Info("DataCollectionAttachmentManager.AddNewFileTransfer : Copying file {0} to {1}", fileTransferInfo.FileName, localFilePath);
+ }
- this.fileHelper.CopyFile(fileTransferInfo.FileName, localFilePath);
+ _fileHelper.CopyFile(fileTransferInfo.FileName, localFilePath);
- if (EqtTrace.IsInfoEnabled)
- {
- EqtTrace.Info("DataCollectionAttachmentManager.AddNewFileTransfer : Copied file {0} to {1}", fileTransferInfo.FileName, localFilePath);
- }
+ if (EqtTrace.IsInfoEnabled)
+ {
+ EqtTrace.Info("DataCollectionAttachmentManager.AddNewFileTransfer : Copied file {0} to {1}", fileTransferInfo.FileName, localFilePath);
}
}
- catch (Exception ex)
- {
- this.LogError(
- ex.ToString(),
- uri,
- friendlyName,
- Guid.Parse(testCaseId));
+ }
+ catch (Exception ex)
+ {
+ LogError(
+ ex.ToString(),
+ uri,
+ friendlyName,
+ Guid.Parse(testCaseId));
- throw;
- }
- },
- this.cancellationTokenSource.Token);
+ throw;
+ }
+ },
+ _cancellationTokenSource.Token);
- var continuationTask = task.ContinueWith(
- (t) =>
+ var continuationTask = task.ContinueWith(
+ (t) =>
+ {
+ try
{
- try
+ if (t.Exception == null)
{
- if (t.Exception == null)
+ lock (AttachmentTaskLock)
{
- lock (attachmentTaskLock)
- {
- this.AttachmentSets[fileTransferInfo.Context][uri].Attachments.Add(UriDataAttachment.CreateFrom(localFilePath, fileTransferInfo.Description));
- }
+ AttachmentSets[fileTransferInfo.Context][uri].Attachments.Add(UriDataAttachment.CreateFrom(localFilePath, fileTransferInfo.Description));
}
-
- sendFileCompletedCallback?.Invoke(this, new AsyncCompletedEventArgs(t.Exception, false, fileTransferInfo.UserToken));
}
- catch (Exception e)
+
+ sendFileCompletedCallback?.Invoke(this, new AsyncCompletedEventArgs(t.Exception, false, fileTransferInfo.UserToken));
+ }
+ catch (Exception e)
+ {
+ if (EqtTrace.IsErrorEnabled)
{
- if (EqtTrace.IsErrorEnabled)
- {
- EqtTrace.Error(
- "DataCollectionAttachmentManager.TriggerCallBack: Error occurred while raising the file transfer completed callback for {0}. Error: {1}",
- localFilePath,
- e.ToString());
- }
+ EqtTrace.Error(
+ "DataCollectionAttachmentManager.TriggerCallBack: Error occurred while raising the file transfer completed callback for {0}. Error: {1}",
+ localFilePath,
+ e.ToString());
}
- },
- this.cancellationTokenSource.Token);
+ }
+ },
+ _cancellationTokenSource.Token);
- this.attachmentTasks[fileTransferInfo.Context].Add(continuationTask);
- }
+ _attachmentTasks[fileTransferInfo.Context].Add(continuationTask);
+ }
- ///
- /// Logs an error message.
- ///
- ///
- /// The error message.
- ///
- ///
- /// The collector uri.
- ///
- ///
- /// The collector friendly name.
- ///
- ///
- /// Id of testCase if available, null otherwise.
- ///
- private void LogError(string errorMessage, Uri collectorUri, string collectorFriendlyName, Guid testCaseId)
+ ///
+ /// Logs an error message.
+ ///
+ ///
+ /// The error message.
+ ///
+ ///
+ /// The collector uri.
+ ///
+ ///
+ /// The collector friendly name.
+ ///
+ ///
+ /// Id of testCase if available, null otherwise.
+ ///
+ private void LogError(string errorMessage, Uri collectorUri, string collectorFriendlyName, Guid testCaseId)
+ {
+ var args = new DataCollectionMessageEventArgs(TestMessageLevel.Error, errorMessage)
{
- var args = new DataCollectionMessageEventArgs(TestMessageLevel.Error, errorMessage)
- {
- Uri = collectorUri,
- FriendlyName = collectorFriendlyName
- };
-
- if (!testCaseId.Equals(Guid.Empty))
- {
- args.TestCaseId = testCaseId;
- }
+ Uri = collectorUri,
+ FriendlyName = collectorFriendlyName
+ };
- this.messageSink.SendMessage(args);
+ if (!testCaseId.Equals(Guid.Empty))
+ {
+ args.TestCaseId = testCaseId;
}
- #endregion
+ _messageSink.SendMessage(args);
}
-}
+
+ #endregion
+}
\ No newline at end of file
diff --git a/src/Microsoft.TestPlatform.Common/DataCollection/DataCollectionEnvironmentVariable.cs b/src/Microsoft.TestPlatform.Common/DataCollection/DataCollectionEnvironmentVariable.cs
index bec2b004ca..0eb679ad9c 100644
--- a/src/Microsoft.TestPlatform.Common/DataCollection/DataCollectionEnvironmentVariable.cs
+++ b/src/Microsoft.TestPlatform.Common/DataCollection/DataCollectionEnvironmentVariable.cs
@@ -1,107 +1,106 @@
// 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.Common.DataCollector
+namespace Microsoft.VisualStudio.TestPlatform.Common.DataCollector;
+
+using System.Collections.Generic;
+
+using ObjectModel;
+
+///
+/// An environment variable requested to be set in the test execution environment by a data collector, including the
+/// friendly names of data collectors that requested it.
+/// This is needed to find list of environment variables needed for test run after eliminating the duplicate name and keys.
+/// For details check DataCollectionPluginManager.AddCollectorEnvironmentVariables() method.
+///
+internal class DataCollectionEnvironmentVariable
{
- using System.Collections.Generic;
+ #region Fields
+
+ ///
+ /// Variable name and requested value
+ ///
+ private readonly KeyValuePair _variable;
+
+ ///
+ /// Friendly names of data collectors that requested this environment variable
+ ///
+ private readonly List _dataCollectorsThatRequested;
+
+ #endregion
- using Microsoft.VisualStudio.TestPlatform.ObjectModel;
+ #region Constructors
///