diff --git a/MyNUnit/Attributes/.editorconfig b/MyNUnit/Attributes/.editorconfig new file mode 100644 index 0000000..3582506 --- /dev/null +++ b/MyNUnit/Attributes/.editorconfig @@ -0,0 +1,249 @@ +# Удалите строку ниже, если вы хотите наследовать параметры .editorconfig из каталогов, расположенных выше в иерархии +root = true + +# Файлы C# +[*.cs] + +dotnet_diagnostic.SA0001.severity = none + +#### Основные параметры EditorConfig #### + +# Отступы и интервалы +indent_size = 4 +indent_style = space +tab_width = 4 + +# Предпочтения для новых строк +end_of_line = crlf +insert_final_newline = false + +#### Действия кода .NET #### + +# Члены типа +dotnet_hide_advanced_members = false +dotnet_member_insertion_location = with_other_members_of_the_same_kind +dotnet_property_generation_behavior = prefer_throwing_properties + +# Поиск символов +dotnet_search_reference_assemblies = true + +#### Рекомендации по написанию кода .NET #### + +# Упорядочение Using +dotnet_separate_import_directive_groups = false +dotnet_sort_system_directives_first = false +file_header_template = unset + +# Предпочтения для this. и Me. +dotnet_style_qualification_for_event = false +dotnet_style_qualification_for_field = false +dotnet_style_qualification_for_method = false +dotnet_style_qualification_for_property = false + +# Параметры использования ключевых слов языка и типов BCL +dotnet_style_predefined_type_for_locals_parameters_members = true +dotnet_style_predefined_type_for_member_access = true + +# Предпочтения для скобок +dotnet_style_parentheses_in_arithmetic_binary_operators = always_for_clarity +dotnet_style_parentheses_in_other_binary_operators = always_for_clarity +dotnet_style_parentheses_in_other_operators = never_if_unnecessary +dotnet_style_parentheses_in_relational_binary_operators = always_for_clarity + +# Предпочтения модификатора +dotnet_style_require_accessibility_modifiers = for_non_interface_members + +# Выражения уровень предпочтения +dotnet_prefer_system_hash_code = true +dotnet_style_coalesce_expression = true +dotnet_style_collection_initializer = true +dotnet_style_explicit_tuple_names = true +dotnet_style_namespace_match_folder = true +dotnet_style_null_propagation = true +dotnet_style_object_initializer = true +dotnet_style_operator_placement_when_wrapping = beginning_of_line +dotnet_style_prefer_auto_properties = true +dotnet_style_prefer_collection_expression = when_types_loosely_match +dotnet_style_prefer_compound_assignment = true +dotnet_style_prefer_conditional_expression_over_assignment = true +dotnet_style_prefer_conditional_expression_over_return = true +dotnet_style_prefer_foreach_explicit_cast_in_source = when_strongly_typed +dotnet_style_prefer_inferred_anonymous_type_member_names = true +dotnet_style_prefer_inferred_tuple_names = true +dotnet_style_prefer_is_null_check_over_reference_equality_method = true +dotnet_style_prefer_simplified_boolean_expressions = true +dotnet_style_prefer_simplified_interpolation = true + +# Предпочтения для полей +dotnet_style_readonly_field = true + +# Настройки параметров +dotnet_code_quality_unused_parameters = all + +# Параметры подавления +dotnet_remove_unnecessary_suppression_exclusions = none + +# Предпочтения для новых строк +dotnet_style_allow_multiple_blank_lines_experimental = true +dotnet_style_allow_statement_immediately_after_block_experimental = true + +#### Рекомендации по написанию кода C# #### + +# Предпочтения var +csharp_style_var_elsewhere = false +csharp_style_var_for_built_in_types = false +csharp_style_var_when_type_is_apparent = false + +# Члены, заданные выражениями +csharp_style_expression_bodied_accessors = true +csharp_style_expression_bodied_constructors = false +csharp_style_expression_bodied_indexers = true +csharp_style_expression_bodied_lambdas = true +csharp_style_expression_bodied_local_functions = false +csharp_style_expression_bodied_methods = false +csharp_style_expression_bodied_operators = false +csharp_style_expression_bodied_properties = true + +# Настройки соответствия шаблонов +csharp_style_pattern_matching_over_as_with_null_check = true +csharp_style_pattern_matching_over_is_with_cast_check = true +csharp_style_prefer_extended_property_pattern = true +csharp_style_prefer_not_pattern = true +csharp_style_prefer_pattern_matching = true +csharp_style_prefer_switch_expression = true + +# Настройки проверки на null +csharp_style_conditional_delegate_call = true + +# Предпочтения модификатора +csharp_prefer_static_anonymous_function = true +csharp_prefer_static_local_function = true +csharp_preferred_modifier_order = public,private,protected,internal,file,static,extern,new,virtual,abstract,sealed,override,readonly,unsafe,required,volatile,async +csharp_style_prefer_readonly_struct = true +csharp_style_prefer_readonly_struct_member = true + +# Предпочтения для блоков кода +csharp_prefer_braces = true +csharp_prefer_simple_using_statement = true +csharp_prefer_system_threading_lock = true +csharp_style_namespace_declarations = block_scoped +csharp_style_prefer_method_group_conversion = true +csharp_style_prefer_primary_constructors = true +csharp_style_prefer_simple_property_accessors = true +csharp_style_prefer_top_level_statements = true + +# Выражения уровень предпочтения +csharp_prefer_simple_default_expression = true +csharp_style_deconstructed_variable_declaration = true +csharp_style_implicit_object_creation_when_type_is_apparent = true +csharp_style_inlined_variable_declaration = true +csharp_style_prefer_implicitly_typed_lambda_expression = true +csharp_style_prefer_index_operator = true +csharp_style_prefer_local_over_anonymous_function = true +csharp_style_prefer_null_check_over_type_check = true +csharp_style_prefer_range_operator = true +csharp_style_prefer_tuple_swap = true +csharp_style_prefer_unbound_generic_type_in_nameof = true +csharp_style_prefer_utf8_string_literals = true +csharp_style_throw_expression = true +csharp_style_unused_value_assignment_preference = discard_variable +csharp_style_unused_value_expression_statement_preference = discard_variable + +# предпочтения для директивы using +csharp_using_directive_placement = outside_namespace + +# Предпочтения для новых строк +csharp_style_allow_blank_line_after_colon_in_constructor_initializer_experimental = true +csharp_style_allow_blank_line_after_token_in_arrow_expression_clause_experimental = true +csharp_style_allow_blank_line_after_token_in_conditional_expression_experimental = true +csharp_style_allow_blank_lines_between_consecutive_braces_experimental = true +csharp_style_allow_embedded_statements_on_same_line_experimental = true + +#### Правила форматирования C# #### + +# Предпочтения для новых строк +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 + +# Параметры отступов +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 + +# Предпочтения для интервалов +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 + +# Параметры переноса +csharp_preserve_single_line_blocks = true +csharp_preserve_single_line_statements = true + +#### Стили именования #### + +# Правила именования + +dotnet_naming_rule.interface_should_be_begins_with_i.severity = suggestion +dotnet_naming_rule.interface_should_be_begins_with_i.symbols = interface +dotnet_naming_rule.interface_should_be_begins_with_i.style = begins_with_i + +dotnet_naming_rule.types_should_be_pascal_case.severity = suggestion +dotnet_naming_rule.types_should_be_pascal_case.symbols = types +dotnet_naming_rule.types_should_be_pascal_case.style = pascal_case + +dotnet_naming_rule.non_field_members_should_be_pascal_case.severity = suggestion +dotnet_naming_rule.non_field_members_should_be_pascal_case.symbols = non_field_members +dotnet_naming_rule.non_field_members_should_be_pascal_case.style = pascal_case + +# Спецификации символов + +dotnet_naming_symbols.interface.applicable_kinds = interface +dotnet_naming_symbols.interface.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected +dotnet_naming_symbols.interface.required_modifiers = + +dotnet_naming_symbols.types.applicable_kinds = class, struct, interface, enum +dotnet_naming_symbols.types.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected +dotnet_naming_symbols.types.required_modifiers = + +dotnet_naming_symbols.non_field_members.applicable_kinds = property, event, method +dotnet_naming_symbols.non_field_members.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected +dotnet_naming_symbols.non_field_members.required_modifiers = + +# Стили именования + +dotnet_naming_style.pascal_case.required_prefix = +dotnet_naming_style.pascal_case.required_suffix = +dotnet_naming_style.pascal_case.word_separator = +dotnet_naming_style.pascal_case.capitalization = pascal_case + +dotnet_naming_style.begins_with_i.required_prefix = I +dotnet_naming_style.begins_with_i.required_suffix = +dotnet_naming_style.begins_with_i.word_separator = +dotnet_naming_style.begins_with_i.capitalization = pascal_case diff --git a/MyNUnit/Attributes/AfterAttribute.cs b/MyNUnit/Attributes/AfterAttribute.cs new file mode 100644 index 0000000..0f09aad --- /dev/null +++ b/MyNUnit/Attributes/AfterAttribute.cs @@ -0,0 +1,13 @@ +// +// Copyright (c) Kalinin Andrew. All rights reserved. +// + +namespace Attributes; + +/// +/// An attribute that marks the method to be executed after each test method. +/// +[AttributeUsage(AttributeTargets.Method)] +public class AfterAttribute : Attribute +{ +} \ No newline at end of file diff --git a/MyNUnit/Attributes/AfterClassAttribute.cs b/MyNUnit/Attributes/AfterClassAttribute.cs new file mode 100644 index 0000000..50fe5a1 --- /dev/null +++ b/MyNUnit/Attributes/AfterClassAttribute.cs @@ -0,0 +1,13 @@ +// +// Copyright (c) Kalinin Andrew. All rights reserved. +// + +namespace Attributes; + +/// +/// An attribute that marks the method to be executed once after completing all tests in the class. +/// +[AttributeUsage(AttributeTargets.Method)] +public class AfterClassAttribute : Attribute +{ +} \ No newline at end of file diff --git a/MyNUnit/Attributes/Attributes.csproj b/MyNUnit/Attributes/Attributes.csproj new file mode 100644 index 0000000..0da7f57 --- /dev/null +++ b/MyNUnit/Attributes/Attributes.csproj @@ -0,0 +1,33 @@ + + + + Library + net9.0 + enable + enable + + + + + + + + + + + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + + + + + + + + + + diff --git a/MyNUnit/Attributes/BeforeAttribute.cs b/MyNUnit/Attributes/BeforeAttribute.cs new file mode 100644 index 0000000..bfaf24b --- /dev/null +++ b/MyNUnit/Attributes/BeforeAttribute.cs @@ -0,0 +1,13 @@ +// +// Copyright (c) Kalinin Andrew. All rights reserved. +// + +namespace Attributes; + +/// +/// An attribute that marks the method to be executed before each test method. +/// +[AttributeUsage(AttributeTargets.Method)] +public class BeforeAttribute : Attribute +{ +} \ No newline at end of file diff --git a/MyNUnit/Attributes/BeforeClassAttribute.cs b/MyNUnit/Attributes/BeforeClassAttribute.cs new file mode 100644 index 0000000..210e2e8 --- /dev/null +++ b/MyNUnit/Attributes/BeforeClassAttribute.cs @@ -0,0 +1,13 @@ +// +// Copyright (c) Kalinin Andrew. All rights reserved. +// + +namespace Attributes; + +/// +/// An attribute that marks the method that must be executed once before starting all tests in the class. +/// +[AttributeUsage(AttributeTargets.Method)] +public class BeforeClassAttribute : Attribute +{ +} \ No newline at end of file diff --git a/MyNUnit/Attributes/TestAttribute.cs b/MyNUnit/Attributes/TestAttribute.cs new file mode 100644 index 0000000..d18a25c --- /dev/null +++ b/MyNUnit/Attributes/TestAttribute.cs @@ -0,0 +1,22 @@ +// +// Copyright (c) Kalinin Andrew. All rights reserved. +// + +namespace Attributes; + +/// +/// An attribute that marks a method as a test method. +/// +[AttributeUsage(AttributeTargets.Method)] +public class TestAttribute : Attribute +{ + /// + /// Gets or sets expected exception. + /// + public Type? Expected { get; set; } + + /// + /// Gets or sets the reason for ignoring. + /// + public string? Ignore { get; set; } +} \ No newline at end of file diff --git a/MyNUnit/Attributes/stylecop.json b/MyNUnit/Attributes/stylecop.json new file mode 100644 index 0000000..c7ed419 --- /dev/null +++ b/MyNUnit/Attributes/stylecop.json @@ -0,0 +1,8 @@ +{ + "$schema": "https://raw.githubusercontent.com/DotNetAnalyzers/StyleCopAnalyzers/master/StyleCop.Analyzers/StyleCop.Analyzers/Settings/stylecop.schema.json", + "settings": { + "documentationRules": { + "companyName": "Kalinin Andrew" + } + } +} diff --git a/MyNUnit/MyNUnit.Tests/.editorconfig b/MyNUnit/MyNUnit.Tests/.editorconfig new file mode 100644 index 0000000..c3685c2 --- /dev/null +++ b/MyNUnit/MyNUnit.Tests/.editorconfig @@ -0,0 +1,257 @@ +# Удалите строку ниже, если вы хотите наследовать параметры .editorconfig из каталогов, расположенных выше в иерархии +root = true + +# Файлы C# +[*.cs] + +# Default severity for analyzer diagnostics with category 'StyleCop.CSharp.DocumentationRules' +dotnet_analyzer_diagnostic.category-StyleCop.CSharp.DocumentationRules.severity = none + +dotnet_diagnostic.SA0001.severity = none + +dotnet_diagnostic.SA1407.severity = none + +#### Основные параметры EditorConfig #### + +# Отступы и интервалы +indent_size = 4 +indent_style = space +tab_width = 4 + +# Предпочтения для новых строк +end_of_line = crlf +insert_final_newline = false + +#### Действия кода .NET #### + +# Члены типа +dotnet_hide_advanced_members = false +dotnet_member_insertion_location = with_other_members_of_the_same_kind +dotnet_property_generation_behavior = prefer_throwing_properties + +# Поиск символов +dotnet_search_reference_assemblies = true + +#### Рекомендации по написанию кода .NET #### + +# Упорядочение Using +dotnet_separate_import_directive_groups = false +dotnet_sort_system_directives_first = false +file_header_template = unset + +# Предпочтения для this. и Me. +dotnet_style_qualification_for_event = false +dotnet_style_qualification_for_field = false +dotnet_style_qualification_for_method = false +dotnet_style_qualification_for_property = false + +# Параметры использования ключевых слов языка и типов BCL +dotnet_style_predefined_type_for_locals_parameters_members = true +dotnet_style_predefined_type_for_member_access = true + +# Предпочтения для скобок +dotnet_style_parentheses_in_arithmetic_binary_operators = always_for_clarity +dotnet_style_parentheses_in_other_binary_operators = always_for_clarity +dotnet_style_parentheses_in_other_operators = never_if_unnecessary +dotnet_style_parentheses_in_relational_binary_operators = always_for_clarity + +# Предпочтения модификатора +dotnet_style_require_accessibility_modifiers = for_non_interface_members + +# Выражения уровень предпочтения +dotnet_prefer_system_hash_code = true +dotnet_style_coalesce_expression = true +dotnet_style_collection_initializer = true +dotnet_style_explicit_tuple_names = true +dotnet_style_namespace_match_folder = true +dotnet_style_null_propagation = true +dotnet_style_object_initializer = true +dotnet_style_operator_placement_when_wrapping = beginning_of_line +dotnet_style_prefer_auto_properties = true +dotnet_style_prefer_collection_expression = when_types_loosely_match +dotnet_style_prefer_compound_assignment = true +dotnet_style_prefer_conditional_expression_over_assignment = true +dotnet_style_prefer_conditional_expression_over_return = true +dotnet_style_prefer_foreach_explicit_cast_in_source = when_strongly_typed +dotnet_style_prefer_inferred_anonymous_type_member_names = true +dotnet_style_prefer_inferred_tuple_names = true +dotnet_style_prefer_is_null_check_over_reference_equality_method = true +dotnet_style_prefer_simplified_boolean_expressions = true +dotnet_style_prefer_simplified_interpolation = true + +# Предпочтения для полей +dotnet_style_readonly_field = true + +# Настройки параметров +dotnet_code_quality_unused_parameters = all + +# Параметры подавления +dotnet_remove_unnecessary_suppression_exclusions = none + +# Предпочтения для новых строк +dotnet_style_allow_multiple_blank_lines_experimental = true +dotnet_style_allow_statement_immediately_after_block_experimental = true + +#### Рекомендации по написанию кода C# #### + +# Предпочтения var +csharp_style_var_elsewhere = false +csharp_style_var_for_built_in_types = false +csharp_style_var_when_type_is_apparent = false + +# Члены, заданные выражениями +csharp_style_expression_bodied_accessors = true +csharp_style_expression_bodied_constructors = false +csharp_style_expression_bodied_indexers = true +csharp_style_expression_bodied_lambdas = true +csharp_style_expression_bodied_local_functions = false +csharp_style_expression_bodied_methods = false +csharp_style_expression_bodied_operators = false +csharp_style_expression_bodied_properties = true + +# Настройки соответствия шаблонов +csharp_style_pattern_matching_over_as_with_null_check = true +csharp_style_pattern_matching_over_is_with_cast_check = true +csharp_style_prefer_extended_property_pattern = true +csharp_style_prefer_not_pattern = true +csharp_style_prefer_pattern_matching = true +csharp_style_prefer_switch_expression = true + +# Настройки проверки на null +csharp_style_conditional_delegate_call = true + +# Предпочтения модификатора +csharp_prefer_static_anonymous_function = true +csharp_prefer_static_local_function = true +csharp_preferred_modifier_order = public,private,protected,internal,file,static,extern,new,virtual,abstract,sealed,override,readonly,unsafe,required,volatile,async +csharp_style_prefer_readonly_struct = true +csharp_style_prefer_readonly_struct_member = true + +# Предпочтения для блоков кода +csharp_prefer_braces = true +csharp_prefer_simple_using_statement = true +csharp_prefer_system_threading_lock = true +csharp_style_namespace_declarations = block_scoped +csharp_style_prefer_method_group_conversion = true +csharp_style_prefer_primary_constructors = true +csharp_style_prefer_simple_property_accessors = true +csharp_style_prefer_top_level_statements = true + +# Выражения уровень предпочтения +csharp_prefer_simple_default_expression = true +csharp_style_deconstructed_variable_declaration = true +csharp_style_implicit_object_creation_when_type_is_apparent = true +csharp_style_inlined_variable_declaration = true +csharp_style_prefer_implicitly_typed_lambda_expression = true +csharp_style_prefer_index_operator = true +csharp_style_prefer_local_over_anonymous_function = true +csharp_style_prefer_null_check_over_type_check = true +csharp_style_prefer_range_operator = true +csharp_style_prefer_tuple_swap = true +csharp_style_prefer_unbound_generic_type_in_nameof = true +csharp_style_prefer_utf8_string_literals = true +csharp_style_throw_expression = true +csharp_style_unused_value_assignment_preference = discard_variable +csharp_style_unused_value_expression_statement_preference = discard_variable + +# предпочтения для директивы using +csharp_using_directive_placement = outside_namespace + +# Предпочтения для новых строк +csharp_style_allow_blank_line_after_colon_in_constructor_initializer_experimental = true +csharp_style_allow_blank_line_after_token_in_arrow_expression_clause_experimental = true +csharp_style_allow_blank_line_after_token_in_conditional_expression_experimental = true +csharp_style_allow_blank_lines_between_consecutive_braces_experimental = true +csharp_style_allow_embedded_statements_on_same_line_experimental = true + +#### Правила форматирования C# #### + +# Предпочтения для новых строк +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 + +# Параметры отступов +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 + +# Предпочтения для интервалов +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 + +# Параметры переноса +csharp_preserve_single_line_blocks = true +csharp_preserve_single_line_statements = true + +#### Стили именования #### + +# Правила именования + +dotnet_naming_rule.interface_should_be_begins_with_i.severity = suggestion +dotnet_naming_rule.interface_should_be_begins_with_i.symbols = interface +dotnet_naming_rule.interface_should_be_begins_with_i.style = begins_with_i + +dotnet_naming_rule.types_should_be_pascal_case.severity = suggestion +dotnet_naming_rule.types_should_be_pascal_case.symbols = types +dotnet_naming_rule.types_should_be_pascal_case.style = pascal_case + +dotnet_naming_rule.non_field_members_should_be_pascal_case.severity = suggestion +dotnet_naming_rule.non_field_members_should_be_pascal_case.symbols = non_field_members +dotnet_naming_rule.non_field_members_should_be_pascal_case.style = pascal_case + +# Спецификации символов + +dotnet_naming_symbols.interface.applicable_kinds = interface +dotnet_naming_symbols.interface.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected +dotnet_naming_symbols.interface.required_modifiers = + +dotnet_naming_symbols.types.applicable_kinds = class, struct, interface, enum +dotnet_naming_symbols.types.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected +dotnet_naming_symbols.types.required_modifiers = + +dotnet_naming_symbols.non_field_members.applicable_kinds = property, event, method +dotnet_naming_symbols.non_field_members.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected +dotnet_naming_symbols.non_field_members.required_modifiers = + +# Стили именования + +dotnet_naming_style.pascal_case.required_prefix = +dotnet_naming_style.pascal_case.required_suffix = +dotnet_naming_style.pascal_case.word_separator = +dotnet_naming_style.pascal_case.capitalization = pascal_case + +dotnet_naming_style.begins_with_i.required_prefix = I +dotnet_naming_style.begins_with_i.required_suffix = +dotnet_naming_style.begins_with_i.word_separator = +dotnet_naming_style.begins_with_i.capitalization = pascal_case + +# SA1401: Fields should be private +dotnet_diagnostic.SA1401.severity = none diff --git a/MyNUnit/MyNUnit.Tests/ExpectedExceptionClass.cs b/MyNUnit/MyNUnit.Tests/ExpectedExceptionClass.cs new file mode 100644 index 0000000..adf5dfd --- /dev/null +++ b/MyNUnit/MyNUnit.Tests/ExpectedExceptionClass.cs @@ -0,0 +1,27 @@ +namespace MyNUnit.Tests; + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using MyTestAttribute = Attributes.TestAttribute; + +public class ExceptionTest +{ + [MyTest(Expected = typeof(ArgumentException))] + public void ThrowsExpectedException() + { + throw new ArgumentException(); + } + + [MyTest(Expected = typeof(ArithmeticException))] + public void ThrowsDifferentException() + { + throw new ArgumentNullException(); + } + + [MyTest(Expected = typeof(ArithmeticException))] + public void DoesntThrowException() + { + } +} \ No newline at end of file diff --git a/MyNUnit/MyNUnit.Tests/LifecycleHookClass.cs b/MyNUnit/MyNUnit.Tests/LifecycleHookClass.cs new file mode 100644 index 0000000..7cc6e40 --- /dev/null +++ b/MyNUnit/MyNUnit.Tests/LifecycleHookClass.cs @@ -0,0 +1,36 @@ +namespace MyNUnit.Tests; + +using Attributes; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using MyAfterAttribute = Attributes.AfterAttribute; +using MyAfterClassAttribute = Attributes.AfterClassAttribute; +using MyBeforeAttribute = Attributes.BeforeAttribute; +using MyBeforeClassAttribute = Attributes.BeforeClassAttribute; +using MyTestAttribute = Attributes.TestAttribute; + +public class LifecycleTest +{ + public static List Loger = new List(); + + [MyBeforeClass] + public static void BeforeClass() => Loger.Add("BeforeClass"); + + [MyAfterClass] + public static void AfterClass() => Loger.Add("AfterClass"); + + [MyBefore] + public static void Before() => Loger.Add("Before"); + + [MyAfter] + public static void After() => Loger.Add("After"); + + [MyTest] + public static void FirstTest() => Loger.Add("Test1"); + + [MyTest] + public static void SecondTest() => Loger.Add("Test2"); +} \ No newline at end of file diff --git a/MyNUnit/MyNUnit.Tests/MyNUnit.Tests.csproj b/MyNUnit/MyNUnit.Tests/MyNUnit.Tests.csproj new file mode 100644 index 0000000..13f2bbd --- /dev/null +++ b/MyNUnit/MyNUnit.Tests/MyNUnit.Tests.csproj @@ -0,0 +1,44 @@ + + + + net9.0 + latest + enable + enable + false + + + + + + + + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/MyNUnit/MyNUnit.Tests/PassFailIgnoreScenarios.cs b/MyNUnit/MyNUnit.Tests/PassFailIgnoreScenarios.cs new file mode 100644 index 0000000..2a00a9c --- /dev/null +++ b/MyNUnit/MyNUnit.Tests/PassFailIgnoreScenarios.cs @@ -0,0 +1,23 @@ +// +// Copyright (c) Kalinin Andrew. All rights reserved. +// + +namespace MyNUnit.Tests; + +using MyTestAttribute = Attributes.TestAttribute; + +public class PassFailIgnoreScenarios +{ + [MyTest] + public void PassingTest() + { + } + + [MyTest] + public void FailingTest() => throw new Exception("aaa"); + + [MyTest(Ignore = "qqq")] + public void IgnoredTest() + { + } +} \ No newline at end of file diff --git a/MyNUnit/MyNUnit.Tests/TestsForMyNUnit.cs b/MyNUnit/MyNUnit.Tests/TestsForMyNUnit.cs new file mode 100644 index 0000000..5895709 --- /dev/null +++ b/MyNUnit/MyNUnit.Tests/TestsForMyNUnit.cs @@ -0,0 +1,75 @@ +// +// Copyright (c) Kalinin Andrew. All rights reserved. +// + +namespace MyNUnit.Tests; + +using NUnit.Framework; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; +using System.Text; +using System.Threading.Tasks; +using static MyNUnit.Tests.LifecycleTest; + +public class TestsForMyNUnit +{ + private TestRunner? runner; + private string? path; + + [OneTimeSetUp] + public void Setup() + { + this.runner = new TestRunner(); + this.path = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location); + } + + [SetUp] + public void ResetStat() + { + Loger.Clear(); + } + + [Test] + public void BasicTests_ShouldIdentifySpecificity() + { + var result = this.runner!.RunTest(this.path!); + var classResult = result.Where(x => x.ClassName == nameof(PassFailIgnoreScenarios)); + Assert.That(classResult.Count(), Is.EqualTo(3)); + + var passing = classResult.First(x => x.MethodName == nameof(PassFailIgnoreScenarios.PassingTest)); + Assert.That(passing.IsSuccess, Is.True); + + var failed = classResult.First(x => x.MethodName == nameof(PassFailIgnoreScenarios.FailingTest)); + Assert.That(failed.IsSuccess, Is.False); + + var ignored = classResult.First(x => x.MethodName == nameof(PassFailIgnoreScenarios.IgnoredTest)); + Assert.That(ignored.IsIgnored, Is.True); + } + + [Test] + public void ExceptionTests_ShouldWasCorrectVersus() + { + var result = this.runner!.RunTest(this.path!); + var classResult = result.Where(x => x.ClassName == nameof(ExceptionTest)); + Assert.That(classResult.Count(), Is.EqualTo(3)); + + var success = classResult.First(x => x.MethodName == nameof(ExceptionTest.ThrowsExpectedException)); + Assert.That(success.IsSuccess, Is.True); + + var wrong = classResult.First(x => x.MethodName == nameof(ExceptionTest.ThrowsDifferentException)); + Assert.That(wrong.IsSuccess, Is.False); + + var noException = classResult.First(x => x.MethodName == nameof(ExceptionTest.DoesntThrowException)); + Assert.That(noException.IsSuccess, Is.False); + } + + [Test] + public void LifecycleTests_ShouldRunAllMethodsInCorrectCount() + { + this.runner!.RunTest(this.path!); + + var expectedLog = new List { "BeforeClass", "Before", "Test1", "After", "AfterClass", "BeforeClass", "Before", "Test2", "After", "AfterClass" }; + } +} \ No newline at end of file diff --git a/MyNUnit/MyNUnit.Tests/stylecop.json b/MyNUnit/MyNUnit.Tests/stylecop.json new file mode 100644 index 0000000..c7ed419 --- /dev/null +++ b/MyNUnit/MyNUnit.Tests/stylecop.json @@ -0,0 +1,8 @@ +{ + "$schema": "https://raw.githubusercontent.com/DotNetAnalyzers/StyleCopAnalyzers/master/StyleCop.Analyzers/StyleCop.Analyzers/Settings/stylecop.schema.json", + "settings": { + "documentationRules": { + "companyName": "Kalinin Andrew" + } + } +} diff --git a/MyNUnit/MyNUnit.slnx b/MyNUnit/MyNUnit.slnx new file mode 100644 index 0000000..ef1c5e5 --- /dev/null +++ b/MyNUnit/MyNUnit.slnx @@ -0,0 +1,5 @@ + + + + + diff --git a/MyNUnit/MyNUnit/.editorconfig b/MyNUnit/MyNUnit/.editorconfig new file mode 100644 index 0000000..81efc3c --- /dev/null +++ b/MyNUnit/MyNUnit/.editorconfig @@ -0,0 +1,248 @@ +# Удалите строку ниже, если вы хотите наследовать параметры .editorconfig из каталогов, расположенных выше в иерархии +root = true + +# Файлы C# +[*.cs] +dotnet_diagnostic.SA0001.severity = none + +#### Основные параметры EditorConfig #### + +# Отступы и интервалы +indent_size = 4 +indent_style = space +tab_width = 4 + +# Предпочтения для новых строк +end_of_line = crlf +insert_final_newline = false + +#### Действия кода .NET #### + +# Члены типа +dotnet_hide_advanced_members = false +dotnet_member_insertion_location = with_other_members_of_the_same_kind +dotnet_property_generation_behavior = prefer_throwing_properties + +# Поиск символов +dotnet_search_reference_assemblies = true + +#### Рекомендации по написанию кода .NET #### + +# Упорядочение Using +dotnet_separate_import_directive_groups = false +dotnet_sort_system_directives_first = false +file_header_template = unset + +# Предпочтения для this. и Me. +dotnet_style_qualification_for_event = false +dotnet_style_qualification_for_field = false +dotnet_style_qualification_for_method = false +dotnet_style_qualification_for_property = false + +# Параметры использования ключевых слов языка и типов BCL +dotnet_style_predefined_type_for_locals_parameters_members = true +dotnet_style_predefined_type_for_member_access = true + +# Предпочтения для скобок +dotnet_style_parentheses_in_arithmetic_binary_operators = always_for_clarity +dotnet_style_parentheses_in_other_binary_operators = always_for_clarity +dotnet_style_parentheses_in_other_operators = never_if_unnecessary +dotnet_style_parentheses_in_relational_binary_operators = always_for_clarity + +# Предпочтения модификатора +dotnet_style_require_accessibility_modifiers = for_non_interface_members + +# Выражения уровень предпочтения +dotnet_prefer_system_hash_code = true +dotnet_style_coalesce_expression = true +dotnet_style_collection_initializer = true +dotnet_style_explicit_tuple_names = true +dotnet_style_namespace_match_folder = true +dotnet_style_null_propagation = true +dotnet_style_object_initializer = true +dotnet_style_operator_placement_when_wrapping = beginning_of_line +dotnet_style_prefer_auto_properties = true +dotnet_style_prefer_collection_expression = when_types_loosely_match +dotnet_style_prefer_compound_assignment = true +dotnet_style_prefer_conditional_expression_over_assignment = true +dotnet_style_prefer_conditional_expression_over_return = true +dotnet_style_prefer_foreach_explicit_cast_in_source = when_strongly_typed +dotnet_style_prefer_inferred_anonymous_type_member_names = true +dotnet_style_prefer_inferred_tuple_names = true +dotnet_style_prefer_is_null_check_over_reference_equality_method = true +dotnet_style_prefer_simplified_boolean_expressions = true +dotnet_style_prefer_simplified_interpolation = true + +# Предпочтения для полей +dotnet_style_readonly_field = true + +# Настройки параметров +dotnet_code_quality_unused_parameters = all + +# Параметры подавления +dotnet_remove_unnecessary_suppression_exclusions = none + +# Предпочтения для новых строк +dotnet_style_allow_multiple_blank_lines_experimental = true +dotnet_style_allow_statement_immediately_after_block_experimental = true + +#### Рекомендации по написанию кода C# #### + +# Предпочтения var +csharp_style_var_elsewhere = false +csharp_style_var_for_built_in_types = false +csharp_style_var_when_type_is_apparent = false + +# Члены, заданные выражениями +csharp_style_expression_bodied_accessors = true +csharp_style_expression_bodied_constructors = false +csharp_style_expression_bodied_indexers = true +csharp_style_expression_bodied_lambdas = true +csharp_style_expression_bodied_local_functions = false +csharp_style_expression_bodied_methods = false +csharp_style_expression_bodied_operators = false +csharp_style_expression_bodied_properties = true + +# Настройки соответствия шаблонов +csharp_style_pattern_matching_over_as_with_null_check = true +csharp_style_pattern_matching_over_is_with_cast_check = true +csharp_style_prefer_extended_property_pattern = true +csharp_style_prefer_not_pattern = true +csharp_style_prefer_pattern_matching = true +csharp_style_prefer_switch_expression = true + +# Настройки проверки на null +csharp_style_conditional_delegate_call = true + +# Предпочтения модификатора +csharp_prefer_static_anonymous_function = true +csharp_prefer_static_local_function = true +csharp_preferred_modifier_order = public,private,protected,internal,file,static,extern,new,virtual,abstract,sealed,override,readonly,unsafe,required,volatile,async +csharp_style_prefer_readonly_struct = true +csharp_style_prefer_readonly_struct_member = true + +# Предпочтения для блоков кода +csharp_prefer_braces = true +csharp_prefer_simple_using_statement = true +csharp_prefer_system_threading_lock = true +csharp_style_namespace_declarations = block_scoped +csharp_style_prefer_method_group_conversion = true +csharp_style_prefer_primary_constructors = true +csharp_style_prefer_simple_property_accessors = true +csharp_style_prefer_top_level_statements = true + +# Выражения уровень предпочтения +csharp_prefer_simple_default_expression = true +csharp_style_deconstructed_variable_declaration = true +csharp_style_implicit_object_creation_when_type_is_apparent = true +csharp_style_inlined_variable_declaration = true +csharp_style_prefer_implicitly_typed_lambda_expression = true +csharp_style_prefer_index_operator = true +csharp_style_prefer_local_over_anonymous_function = true +csharp_style_prefer_null_check_over_type_check = true +csharp_style_prefer_range_operator = true +csharp_style_prefer_tuple_swap = true +csharp_style_prefer_unbound_generic_type_in_nameof = true +csharp_style_prefer_utf8_string_literals = true +csharp_style_throw_expression = true +csharp_style_unused_value_assignment_preference = discard_variable +csharp_style_unused_value_expression_statement_preference = discard_variable + +# предпочтения для директивы using +csharp_using_directive_placement = outside_namespace + +# Предпочтения для новых строк +csharp_style_allow_blank_line_after_colon_in_constructor_initializer_experimental = true +csharp_style_allow_blank_line_after_token_in_arrow_expression_clause_experimental = true +csharp_style_allow_blank_line_after_token_in_conditional_expression_experimental = true +csharp_style_allow_blank_lines_between_consecutive_braces_experimental = true +csharp_style_allow_embedded_statements_on_same_line_experimental = true + +#### Правила форматирования C# #### + +# Предпочтения для новых строк +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 + +# Параметры отступов +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 + +# Предпочтения для интервалов +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 + +# Параметры переноса +csharp_preserve_single_line_blocks = true +csharp_preserve_single_line_statements = true + +#### Стили именования #### + +# Правила именования + +dotnet_naming_rule.interface_should_be_begins_with_i.severity = suggestion +dotnet_naming_rule.interface_should_be_begins_with_i.symbols = interface +dotnet_naming_rule.interface_should_be_begins_with_i.style = begins_with_i + +dotnet_naming_rule.types_should_be_pascal_case.severity = suggestion +dotnet_naming_rule.types_should_be_pascal_case.symbols = types +dotnet_naming_rule.types_should_be_pascal_case.style = pascal_case + +dotnet_naming_rule.non_field_members_should_be_pascal_case.severity = suggestion +dotnet_naming_rule.non_field_members_should_be_pascal_case.symbols = non_field_members +dotnet_naming_rule.non_field_members_should_be_pascal_case.style = pascal_case + +# Спецификации символов + +dotnet_naming_symbols.interface.applicable_kinds = interface +dotnet_naming_symbols.interface.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected +dotnet_naming_symbols.interface.required_modifiers = + +dotnet_naming_symbols.types.applicable_kinds = class, struct, interface, enum +dotnet_naming_symbols.types.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected +dotnet_naming_symbols.types.required_modifiers = + +dotnet_naming_symbols.non_field_members.applicable_kinds = property, event, method +dotnet_naming_symbols.non_field_members.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected +dotnet_naming_symbols.non_field_members.required_modifiers = + +# Стили именования + +dotnet_naming_style.pascal_case.required_prefix = +dotnet_naming_style.pascal_case.required_suffix = +dotnet_naming_style.pascal_case.word_separator = +dotnet_naming_style.pascal_case.capitalization = pascal_case + +dotnet_naming_style.begins_with_i.required_prefix = I +dotnet_naming_style.begins_with_i.required_suffix = +dotnet_naming_style.begins_with_i.word_separator = +dotnet_naming_style.begins_with_i.capitalization = pascal_case diff --git a/MyNUnit/MyNUnit/Assert.cs b/MyNUnit/MyNUnit/Assert.cs new file mode 100644 index 0000000..4ca2667 --- /dev/null +++ b/MyNUnit/MyNUnit/Assert.cs @@ -0,0 +1,69 @@ +// +// Copyright (c) Kalinin Andrew. All rights reserved. +// + +namespace MyNUnit; + +using System.Runtime.CompilerServices; + +/// +/// Provides a set of static methods for verifying conditions in tests. +/// +public static class Assert +{ + /// + /// Verifies that two values are equal. + /// + /// The type of values to compare. + /// The expected value. + /// The actual value. + /// Thrown when values are not equal. + public static void AreEqual(T expected, T real) + { + if (!Equals(expected, real)) + { + throw new Exception($"Ожидалось: {expected}, получилось {real}"); + } + } + + /// + /// Verifies that a condition is true. + /// + /// The condition to verify. + /// Thrown when condition is false. + public static void IsTrue(bool condition) + { + if (!condition) + { + throw new Exception("Условие должно быть истинным"); + } + } + + /// + /// Verifies that an object is not null. + /// + /// The object to verify. + /// The expression being verified (auto-captured by compiler). + /// Thrown when object is null. + public static void IsNotNull(object? obj, [CallerArgumentExpression(nameof(obj))] string? expression = null) + { + if (obj == null) + { + throw new Exception($"{expression} ожидался не null"); + } + } + + /// + /// Verifies that an object is null. + /// + /// The object to verify. + /// The expression being verified (auto-captured by compiler). + /// Thrown when object is not null. + public static void IsNull(object? obj, [CallerArgumentExpression(nameof(obj))] string? expression = null) + { + if (obj != null) + { + throw new Exception($"{expression} ожидался null"); + } + } +} \ No newline at end of file diff --git a/MyNUnit/MyNUnit/MyNUnit.csproj b/MyNUnit/MyNUnit/MyNUnit.csproj new file mode 100644 index 0000000..4219ae0 --- /dev/null +++ b/MyNUnit/MyNUnit/MyNUnit.csproj @@ -0,0 +1,42 @@ + + + + Exe + net9.0 + enable + enable + + + + + + + + + + + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + + + + + + + + + + + + + + + + + + + diff --git a/MyNUnit/MyNUnit/Program.cs b/MyNUnit/MyNUnit/Program.cs new file mode 100644 index 0000000..9816a32 --- /dev/null +++ b/MyNUnit/MyNUnit/Program.cs @@ -0,0 +1,21 @@ +// +// Copyright (c) Kalinin Andrew. All rights reserved. +// + +using MyNUnit; + +if (args.Length == 0) +{ + Console.WriteLine("Укажите путь"); + return; +} + +string path = args[0]; +if (!Directory.Exists(path)) +{ + Console.WriteLine($"Папка {path} не найдена"); + return; +} + +var runner = new TestRunner(); +runner.RunTest(path); \ No newline at end of file diff --git a/MyNUnit/MyNUnit/TestResult.cs b/MyNUnit/MyNUnit/TestResult.cs new file mode 100644 index 0000000..6672e18 --- /dev/null +++ b/MyNUnit/MyNUnit/TestResult.cs @@ -0,0 +1,52 @@ +// +// Copyright (c) Kalinin Andrew. All rights reserved. +// + +namespace MyNUnit; + +/// +/// Represents a data structure for storing the complete result of a single test method. +/// +public class TestResult +{ + /// + /// Gets or sets the name of the assembly containing the test. + /// + public string? AssemblyName { get; set; } + + /// + /// Gets or sets the name of the test class containing the test method. + /// + public string? ClassName { get; set; } + + /// + /// Gets or sets the name of the test method itself. + /// + public string? MethodName { get; set; } + + /// + /// Gets or sets a value indicating whether gets or sets a flag indicating whether the test passed successfully (true). + /// If the test failed or was ignored, this value is false. + /// + public bool IsSuccess { get; set; } + + /// + /// Gets or sets a value indicating whether gets or sets a flag indicating whether the test was ignored. + /// + public bool IsIgnored { get; set; } + + /// + /// Gets or sets the reason why the test was ignored. + /// + public string? IgnoreReason { get; set; } + + /// + /// Gets or sets the error message or exception details if the test failed. + /// + public string? ErrorMessage { get; set; } + + /// + /// Gets or sets the time taken to execute the test. + /// + public TimeSpan TestTime { get; set; } +} \ No newline at end of file diff --git a/MyNUnit/MyNUnit/TestRunner.cs b/MyNUnit/MyNUnit/TestRunner.cs new file mode 100644 index 0000000..58b49b2 --- /dev/null +++ b/MyNUnit/MyNUnit/TestRunner.cs @@ -0,0 +1,222 @@ +// +// Copyright (c) Kalinin Andrew. All rights reserved. +// + +namespace MyNUnit; + +using Attributes; +using System.Collections.Concurrent; +using System.Diagnostics; +using System.Reflection; + +/// +/// The main class responsible for detecting, executing, and delivering tests. +/// +public class TestRunner +{ + /// + /// Scans the specified path for the DLL, downloads the assemblies, and runs all the detected tests. + /// + /// The path to the directory to search for assemblies. + /// List of results. + public List RunTest(string path) + { + var dlls = Directory.GetFiles(path, "*.dll", SearchOption.AllDirectories); + var results = new ConcurrentBag(); + + Parallel.ForEach(dlls, (dllPath) => + { + try + { + var assembly = Assembly.LoadFrom(dllPath); + var assemblyName = Path.GetFileName(dllPath); + var testClasses = assembly.GetTypes().Where(x => x.GetMethods().Any(y => y.GetCustomAttribute() != null)); + + Parallel.ForEach(testClasses, (testClass) => + { + this.RunTestsInClass(testClass, results, assemblyName); + }); + } + catch (Exception exception) + { + Console.WriteLine($"{exception.Message}"); + } + }); + + this.Print(results); + return results.ToList(); + } + + private bool RunStaticMethods(Type type, Type attributeType, out string? error) + { + var methods = type.GetMethods(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static).Where(x => x.GetCustomAttribute(attributeType) != null); + + foreach (var method in methods) + { + try + { + method.Invoke(null, null); + } + catch (Exception exception) + { + error = exception.Message; + return false; + } + } + + error = null; + return true; + } + + private void RunSingleTest(Type testClass, MethodInfo methodInfo, ConcurrentBag results, string assemblyName) + { + var attribute = methodInfo.GetCustomAttribute()!; + var result = new TestResult + { + AssemblyName = assemblyName, + ClassName = testClass.Name, + MethodName = methodInfo.Name, + }; + + if (!string.IsNullOrEmpty(attribute.Ignore)) + { + result.IsIgnored = true; + result.IgnoreReason = attribute.Ignore; + result.TestTime = TimeSpan.Zero; + results.Add(result); + return; + } + + var stopwatch = Stopwatch.StartNew(); + object? instance = null; + + try + { + instance = Activator.CreateInstance(testClass); + this.RunBeforeAndAfterMethods(testClass, instance!, typeof(BeforeAttribute)); + + try + { + methodInfo.Invoke(instance, null); + if (attribute.Expected != null) + { + result.IsSuccess = false; + result.ErrorMessage = $"Ожидаемое({attribute.Expected.Name}) исключение не выбросилось"; + } + else + { + result.IsSuccess = true; + } + } + catch (TargetInvocationException targetInvocationException) + { + var ex = targetInvocationException.InnerException; + if (attribute.Expected != null && attribute.Expected.IsInstanceOfType(ex) && ex != null) + { + result.IsSuccess = true; + } + else + { + result.IsSuccess = false; + result.ErrorMessage = $"{ex?.GetType().Name ?? "Unknown exception"}: {ex?.Message ?? "No details"}"; + } + } + + this.RunBeforeAndAfterMethods(testClass, instance!, typeof(AfterAttribute)); + } + catch (Exception ex) + { + result.IsSuccess = false; + result.ErrorMessage = result.ErrorMessage ?? $" {ex.Message}"; + } + finally + { + stopwatch.Stop(); + result.TestTime = stopwatch.Elapsed; + results.Add(result); + } + } + + private void RunBeforeAndAfterMethods(Type type, object instance, Type attributeType) + { + var methods = type.GetMethods(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance).Where(x => x.GetCustomAttribute(attributeType) != null); + + foreach (var method in methods) + { + try + { + method.Invoke(instance, null); + } + catch (TargetInvocationException) + { + throw; + } + } + } + + private void RunTestsInClass(Type testClass, ConcurrentBag results, string assemblyName) + { + if (!this.RunStaticMethods(testClass, typeof(BeforeClassAttribute), out string? beforeClassError)) + { + this.FailAllTests(testClass, results, $"{beforeClassError}", assemblyName); + return; + } + + var testMethods = testClass.GetMethods().Where(x => x.GetCustomAttribute() != null); + + foreach (var testMethod in testMethods) + { + this.RunSingleTest(testClass, testMethod, results, assemblyName); + } + + this.RunStaticMethods(testClass, typeof(AfterClassAttribute), out _); + } + + private void FailAllTests(Type testClass, ConcurrentBag results, string reason, string assemblyName) + { + var methods = testClass.GetMethods().Where(x => x.GetCustomAttribute() != null); + foreach (var method in methods) + { + results.Add(new TestResult + { + AssemblyName = assemblyName, + ClassName = testClass.Name, + MethodName = method.Name, + IsSuccess = false, + ErrorMessage = reason, + TestTime = TimeSpan.Zero, + }); + } + } + + private void Print(ConcurrentBag results) + { + Console.WriteLine("\nРезультаты тестирования"); + var passed = results.Where(x => x.IsSuccess).OrderBy(x => x.ClassName).ThenBy(x => x.MethodName).ToList(); + var failed = results.Where(x => !x.IsSuccess && !x.IsIgnored).OrderBy(x => x.ClassName).ThenBy(x => x.MethodName).ToList(); + var ignored = results.Where(x => x.IsIgnored).OrderBy(x => x.ClassName).ThenBy(x => x.MethodName).ToList(); + + Console.ForegroundColor = ConsoleColor.Green; + Console.WriteLine($"Успешно {passed.Count}"); + foreach (var result in passed) + { + Console.WriteLine($"{result.ClassName}.{result.MethodName} ({result.TestTime.TotalMilliseconds:F2} мс)"); + } + + Console.ForegroundColor = ConsoleColor.Red; + Console.WriteLine($"\nПровалено {failed.Count}"); + foreach (var result in failed) + { + Console.WriteLine($"{result.ClassName}.{result.MethodName} ({result.TestTime.TotalMilliseconds:F2} мс) \nОШИБКА: {result.ErrorMessage}\n"); + } + + Console.ForegroundColor = ConsoleColor.Yellow; + Console.WriteLine($"Проигнорировано {ignored.Count}"); + foreach (var result in ignored) + { + Console.WriteLine($"Пропущен {result.ClassName}.{result.MethodName} по причине {result.IgnoreReason} ({result.TestTime.TotalMilliseconds:F2} мс)"); + } + + Console.ResetColor(); + } +} \ No newline at end of file diff --git a/MyNUnit/MyNUnit/stylecop.json b/MyNUnit/MyNUnit/stylecop.json new file mode 100644 index 0000000..c7ed419 --- /dev/null +++ b/MyNUnit/MyNUnit/stylecop.json @@ -0,0 +1,8 @@ +{ + "$schema": "https://raw.githubusercontent.com/DotNetAnalyzers/StyleCopAnalyzers/master/StyleCop.Analyzers/StyleCop.Analyzers/Settings/stylecop.schema.json", + "settings": { + "documentationRules": { + "companyName": "Kalinin Andrew" + } + } +}