From 17556d013e08f06f0f02a8fc60422706df835dbe Mon Sep 17 00:00:00 2001 From: shuelsmeier <42076445+shuelsmeier@users.noreply.github.com> Date: Fri, 29 Jan 2021 13:54:06 +0100 Subject: [PATCH] Initial commit --- .editorconfig | 983 +++++++++++++++ .gitignore | 350 ++++++ CODE_OF_CONDUCT.md | 6 + CONTRIBUTING.md | 3 + .../Comparer/ChangelogComparer.cs | 133 ++ .../ChangelogSectionCollectionComparer.cs | 96 ++ .../Comparer/ChangelogSectionComparer.cs | 113 ++ .../ChangelogSectionUnreleasedComparer.cs | 115 ++ .../ChangelogSubSectionCollectionComparer.cs | 96 ++ .../Comparer/ChangelogSubSectionComparer.cs | 95 ++ ...angelogSubSectionItemCollectionComparer.cs | 96 ++ .../ChangelogSubSectionItemComparer.cs | 88 ++ .../Comparer/ResultComparer.cs | 100 ++ .../Extensions/AssertExtensions.cs | 239 ++++ .../KeepAChangelogParser.Tests.csproj | 42 + .../Properties/.gitkeep | 0 .../ChangelogResultComparerFactory.cs | 29 + .../TheChangelogContainsADescription.cs | 72 ++ ...asedSectionAndOneSubSectionWithAllTypes.cs | 283 +++++ ...dSectionAndOneSubSectionWithInvalidType.cs | 75 ++ ...ctionAndOneSubSectionWithInvalidVersion.cs | 75 ++ ...dSectionAndOneSubSectionWithMissingDash.cs | 75 ++ ...dSectionAndOneSubSectionWithMissingDate.cs | 75 ++ ...SectionAndOneSubSectionWithMissingSpace.cs | 75 ++ ...ctionAndOneSubSectionWithMissingVersion.cs | 75 ++ ...onAndOneSubSectionWithTwoIdenticalTypes.cs | 79 ++ ...ionAnUnreleasedSectionAndTwoSubSections.cs | 250 ++++ ...tainsADescriptionAndAnUnreleasedSection.cs | 112 ++ ...ptionAndAnUnreleasedSectionWithAllTypes.cs | 237 ++++ ...nAndAnUnreleasedSectionWithInvalidTitle.cs | 69 ++ ...onAndAnUnreleasedSectionWithInvalidType.cs | 69 ++ ...dAnUnreleasedSectionWithItemMissingDash.cs | 69 ++ ...AnUnreleasedSectionWithItemMissingSpace.cs | 69 ++ ...iptionAndAnUnreleasedSectionWithNoTitle.cs | 69 ++ ...riptionAndAnUnreleasedSectionWithNoType.cs | 69 ++ ...asedSectionWithTitleMissingCloseBracket.cs | 69 ++ ...easedSectionWithTitleMissingOpenBracket.cs | 69 ++ ...nUnreleasedSectionWithTwoIdenticalTypes.cs | 73 ++ .../TheChangelogContainsNothing.cs | 60 + ...logContainsNothingWithInvalidHeadingOne.cs | 56 + .../TheChangelogContainsNothingWithNoTitle.cs | 56 + KeepAChangelogParser.sln | 42 + KeepAChangelogParser/ChangelogParser.cs | 1068 +++++++++++++++++ .../Contracts/IChangelogTokenizer.cs | 16 + KeepAChangelogParser/IChangelogParser.cs | 16 + .../KeepAChangelogParser.csproj | 62 + .../KeepAChangelogParser.nuspec | 24 + KeepAChangelogParser/Models/Changelog.cs | 17 + .../Models/ChangelogSection.cs | 15 + .../Models/ChangelogSectionCollection.cs | 11 + .../Models/ChangelogSectionUnreleased.cs | 13 + .../Models/ChangelogSubSection.cs | 13 + .../Models/ChangelogSubSectionCollection.cs | 11 + .../Models/ChangelogSubSectionItem.cs | 11 + .../ChangelogSubSectionItemCollection.cs | 11 + .../Models/ChangelogSubSectionType.cs | 14 + KeepAChangelogParser/Models/ChangelogToken.cs | 61 + .../Models/ChangelogTokenDefinition.cs | 28 + .../Models/ChangelogTokenMatch.cs | 21 + .../Models/ChangelogTokenType.cs | 22 + KeepAChangelogParser/Properties/.gitkeep | 0 KeepAChangelogParser/PublicAPI.Shipped.txt | 48 + KeepAChangelogParser/PublicAPI.Unshipped.txt | 1 + .../Services/ChangelogTokenizer.cs | 163 +++ KeepAChangelogParser/key.snk | Bin 0 -> 596 bytes LICENSE | 21 + README.md | 24 + azure-pipelines.yml | 46 + 68 files changed, 6643 insertions(+) create mode 100644 .editorconfig create mode 100644 .gitignore create mode 100644 CODE_OF_CONDUCT.md create mode 100644 CONTRIBUTING.md create mode 100644 KeepAChangelogParser.Tests/Comparer/ChangelogComparer.cs create mode 100644 KeepAChangelogParser.Tests/Comparer/ChangelogSectionCollectionComparer.cs create mode 100644 KeepAChangelogParser.Tests/Comparer/ChangelogSectionComparer.cs create mode 100644 KeepAChangelogParser.Tests/Comparer/ChangelogSectionUnreleasedComparer.cs create mode 100644 KeepAChangelogParser.Tests/Comparer/ChangelogSubSectionCollectionComparer.cs create mode 100644 KeepAChangelogParser.Tests/Comparer/ChangelogSubSectionComparer.cs create mode 100644 KeepAChangelogParser.Tests/Comparer/ChangelogSubSectionItemCollectionComparer.cs create mode 100644 KeepAChangelogParser.Tests/Comparer/ChangelogSubSectionItemComparer.cs create mode 100644 KeepAChangelogParser.Tests/Comparer/ResultComparer.cs create mode 100644 KeepAChangelogParser.Tests/Extensions/AssertExtensions.cs create mode 100644 KeepAChangelogParser.Tests/KeepAChangelogParser.Tests.csproj create mode 100644 KeepAChangelogParser.Tests/Properties/.gitkeep create mode 100644 KeepAChangelogParser.Tests/Services/ChangelogResultComparerFactory.cs create mode 100644 KeepAChangelogParser.Tests/TheChangelogContainsADescription.cs create mode 100644 KeepAChangelogParser.Tests/TheChangelogContainsADescriptionAnUnreleasedSectionAndOneSubSectionWithAllTypes.cs create mode 100644 KeepAChangelogParser.Tests/TheChangelogContainsADescriptionAnUnreleasedSectionAndOneSubSectionWithInvalidType.cs create mode 100644 KeepAChangelogParser.Tests/TheChangelogContainsADescriptionAnUnreleasedSectionAndOneSubSectionWithInvalidVersion.cs create mode 100644 KeepAChangelogParser.Tests/TheChangelogContainsADescriptionAnUnreleasedSectionAndOneSubSectionWithMissingDash.cs create mode 100644 KeepAChangelogParser.Tests/TheChangelogContainsADescriptionAnUnreleasedSectionAndOneSubSectionWithMissingDate.cs create mode 100644 KeepAChangelogParser.Tests/TheChangelogContainsADescriptionAnUnreleasedSectionAndOneSubSectionWithMissingSpace.cs create mode 100644 KeepAChangelogParser.Tests/TheChangelogContainsADescriptionAnUnreleasedSectionAndOneSubSectionWithMissingVersion.cs create mode 100644 KeepAChangelogParser.Tests/TheChangelogContainsADescriptionAnUnreleasedSectionAndOneSubSectionWithTwoIdenticalTypes.cs create mode 100644 KeepAChangelogParser.Tests/TheChangelogContainsADescriptionAnUnreleasedSectionAndTwoSubSections.cs create mode 100644 KeepAChangelogParser.Tests/TheChangelogContainsADescriptionAndAnUnreleasedSection.cs create mode 100644 KeepAChangelogParser.Tests/TheChangelogContainsADescriptionAndAnUnreleasedSectionWithAllTypes.cs create mode 100644 KeepAChangelogParser.Tests/TheChangelogContainsADescriptionAndAnUnreleasedSectionWithInvalidTitle.cs create mode 100644 KeepAChangelogParser.Tests/TheChangelogContainsADescriptionAndAnUnreleasedSectionWithInvalidType.cs create mode 100644 KeepAChangelogParser.Tests/TheChangelogContainsADescriptionAndAnUnreleasedSectionWithItemMissingDash.cs create mode 100644 KeepAChangelogParser.Tests/TheChangelogContainsADescriptionAndAnUnreleasedSectionWithItemMissingSpace.cs create mode 100644 KeepAChangelogParser.Tests/TheChangelogContainsADescriptionAndAnUnreleasedSectionWithNoTitle.cs create mode 100644 KeepAChangelogParser.Tests/TheChangelogContainsADescriptionAndAnUnreleasedSectionWithNoType.cs create mode 100644 KeepAChangelogParser.Tests/TheChangelogContainsADescriptionAndAnUnreleasedSectionWithTitleMissingCloseBracket.cs create mode 100644 KeepAChangelogParser.Tests/TheChangelogContainsADescriptionAndAnUnreleasedSectionWithTitleMissingOpenBracket.cs create mode 100644 KeepAChangelogParser.Tests/TheChangelogContainsADescriptionAndAnUnreleasedSectionWithTwoIdenticalTypes.cs create mode 100644 KeepAChangelogParser.Tests/TheChangelogContainsNothing.cs create mode 100644 KeepAChangelogParser.Tests/TheChangelogContainsNothingWithInvalidHeadingOne.cs create mode 100644 KeepAChangelogParser.Tests/TheChangelogContainsNothingWithNoTitle.cs create mode 100644 KeepAChangelogParser.sln create mode 100644 KeepAChangelogParser/ChangelogParser.cs create mode 100644 KeepAChangelogParser/Contracts/IChangelogTokenizer.cs create mode 100644 KeepAChangelogParser/IChangelogParser.cs create mode 100644 KeepAChangelogParser/KeepAChangelogParser.csproj create mode 100644 KeepAChangelogParser/KeepAChangelogParser.nuspec create mode 100644 KeepAChangelogParser/Models/Changelog.cs create mode 100644 KeepAChangelogParser/Models/ChangelogSection.cs create mode 100644 KeepAChangelogParser/Models/ChangelogSectionCollection.cs create mode 100644 KeepAChangelogParser/Models/ChangelogSectionUnreleased.cs create mode 100644 KeepAChangelogParser/Models/ChangelogSubSection.cs create mode 100644 KeepAChangelogParser/Models/ChangelogSubSectionCollection.cs create mode 100644 KeepAChangelogParser/Models/ChangelogSubSectionItem.cs create mode 100644 KeepAChangelogParser/Models/ChangelogSubSectionItemCollection.cs create mode 100644 KeepAChangelogParser/Models/ChangelogSubSectionType.cs create mode 100644 KeepAChangelogParser/Models/ChangelogToken.cs create mode 100644 KeepAChangelogParser/Models/ChangelogTokenDefinition.cs create mode 100644 KeepAChangelogParser/Models/ChangelogTokenMatch.cs create mode 100644 KeepAChangelogParser/Models/ChangelogTokenType.cs create mode 100644 KeepAChangelogParser/Properties/.gitkeep create mode 100644 KeepAChangelogParser/PublicAPI.Shipped.txt create mode 100644 KeepAChangelogParser/PublicAPI.Unshipped.txt create mode 100644 KeepAChangelogParser/Services/ChangelogTokenizer.cs create mode 100644 KeepAChangelogParser/key.snk create mode 100644 LICENSE create mode 100644 README.md create mode 100644 azure-pipelines.yml diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..4089e5c --- /dev/null +++ b/.editorconfig @@ -0,0 +1,983 @@ +# Remove the line below if you want to inherit .editorconfig settings from higher directories +root = true + +# C# files +[*.cs] + +#### Core EditorConfig Options #### + +# Indentation and spacing +indent_size = 2 +indent_style = space +tab_width = 2 + +# New line preferences +end_of_line = crlf +insert_final_newline = false + +#### .NET Coding Conventions #### + +# Organize usings +dotnet_separate_import_directive_groups = false +dotnet_sort_system_directives_first = false +file_header_template = unset + +# this. and Me. preferences +dotnet_style_qualification_for_event = true:error +dotnet_style_qualification_for_field = true:error +dotnet_style_qualification_for_method = true:error +dotnet_style_qualification_for_property = true:error + +# 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:error +dotnet_style_parentheses_in_other_binary_operators = always_for_clarity:error +dotnet_style_parentheses_in_other_operators = always_for_clarity:error +dotnet_style_parentheses_in_relational_binary_operators = always_for_clarity:error + +# 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 = true:suggestion +dotnet_style_operator_placement_when_wrapping = beginning_of_line +dotnet_style_prefer_auto_properties = true:silent +dotnet_style_prefer_compound_assignment = true:suggestion +dotnet_style_prefer_conditional_expression_over_assignment = true:silent +dotnet_style_prefer_conditional_expression_over_return = true:silent +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 + +# Field preferences +dotnet_style_readonly_field = true:error + +# 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 +csharp_style_var_for_built_in_types = false:silent +csharp_style_var_when_type_is_apparent = false:silent + +# 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:silent +csharp_style_expression_bodied_local_functions = false:silent +csharp_style_expression_bodied_methods = false:silent +csharp_style_expression_bodied_operators = false:silent +csharp_style_expression_bodied_properties = true:silent + +# 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:suggestion +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:suggestion + +# 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_pattern_local_over_anonymous_function = true:suggestion +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 + +#### C# Formatting Rules #### + +# 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 #### + +# Naming rules + +dotnet_naming_rule.interface_should_be_begins_with_i.severity = suggestion +dotnet_naming_rule.interface_should_be_begins_with_i.symbols = interface +dotnet_naming_rule.interface_should_be_begins_with_i.style = begins_with_i + +dotnet_naming_rule.private_property_should_be_camel_case.severity = suggestion +dotnet_naming_rule.private_property_should_be_camel_case.symbols = private_property +dotnet_naming_rule.private_property_should_be_camel_case.style = camel_case + +dotnet_naming_rule.private_enum_should_be_pascal_case.severity = suggestion +dotnet_naming_rule.private_enum_should_be_pascal_case.symbols = private_enum +dotnet_naming_rule.private_enum_should_be_pascal_case.style = pascal_case + +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.private_method_should_be_camel_case.severity = suggestion +dotnet_naming_rule.private_method_should_be_camel_case.symbols = private_method +dotnet_naming_rule.private_method_should_be_camel_case.style = camel_case + +dotnet_naming_rule.private_static_method_should_be_camel_case.severity = suggestion +dotnet_naming_rule.private_static_method_should_be_camel_case.symbols = private_static_method +dotnet_naming_rule.private_static_method_should_be_camel_case.style = camel_case + +dotnet_naming_rule.non_field_members_should_be_pascal_case.severity = suggestion +dotnet_naming_rule.non_field_members_should_be_pascal_case.symbols = non_field_members +dotnet_naming_rule.non_field_members_should_be_pascal_case.style = pascal_case + +# Symbol specifications + +dotnet_naming_symbols.interface.applicable_kinds = interface +dotnet_naming_symbols.interface.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected +dotnet_naming_symbols.interface.required_modifiers = + +dotnet_naming_symbols.private_property.applicable_kinds = property +dotnet_naming_symbols.private_property.applicable_accessibilities = private +dotnet_naming_symbols.private_property.required_modifiers = + +dotnet_naming_symbols.private_method.applicable_kinds = method +dotnet_naming_symbols.private_method.applicable_accessibilities = private +dotnet_naming_symbols.private_method.required_modifiers = + +dotnet_naming_symbols.private_enum.applicable_kinds = enum +dotnet_naming_symbols.private_enum.applicable_accessibilities = private +dotnet_naming_symbols.private_enum.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, protected, protected_internal, private_protected +dotnet_naming_symbols.non_field_members.required_modifiers = + +dotnet_naming_symbols.private_static_method.applicable_kinds = method +dotnet_naming_symbols.private_static_method.applicable_accessibilities = private +dotnet_naming_symbols.private_static_method.required_modifiers = static + +# Naming styles + +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 + +dotnet_naming_style.camel_case.required_prefix = +dotnet_naming_style.camel_case.required_suffix = +dotnet_naming_style.camel_case.word_separator = +dotnet_naming_style.camel_case.capitalization = camel_case + +#### Code Analysis Rules #### + +# CA1000: Do not declare static members on generic types +dotnet_diagnostic.CA1000.severity = silent + +# CA1001: Types that own disposable fields should be disposable +dotnet_diagnostic.CA1001.severity = silent + +# CA1002: Do not expose generic lists +dotnet_diagnostic.CA1002.severity = warning + +# CA1003: Use generic event handler instances +dotnet_diagnostic.CA1003.severity = warning + +# CA1005: Avoid excessive parameters on generic types +dotnet_diagnostic.CA1005.severity = warning + +# CA1008: Enums should have zero value +dotnet_diagnostic.CA1008.severity = warning + +# CA1010: Generic interface should also be implemented +dotnet_diagnostic.CA1010.severity = silent + +# CA1012: Abstract types should not have public constructors +dotnet_diagnostic.CA1012.severity = warning + +# CA1014: Mark assemblies with CLSCompliant +dotnet_diagnostic.CA1014.severity = warning + +# CA1016: Mark assemblies with assembly version +dotnet_diagnostic.CA1016.severity = suggestion + +# CA1017: Mark assemblies with ComVisible +dotnet_diagnostic.CA1017.severity = warning + +# CA1018: Mark attributes with AttributeUsageAttribute +dotnet_diagnostic.CA1018.severity = suggestion + +# CA1019: Define accessors for attribute arguments +dotnet_diagnostic.CA1019.severity = warning + +# CA1021: Avoid out parameters +dotnet_diagnostic.CA1021.severity = warning + +# CA1024: Use properties where appropriate +dotnet_diagnostic.CA1024.severity = warning + +# CA1027: Mark enums with FlagsAttribute +dotnet_diagnostic.CA1027.severity = warning + +# CA1028: Enum Storage should be Int32 +dotnet_diagnostic.CA1028.severity = warning + +# CA1030: Use events where appropriate +dotnet_diagnostic.CA1030.severity = warning + +# CA1031: Do not catch general exception types +dotnet_diagnostic.CA1031.severity = warning + +# CA1032: Implement standard exception constructors +dotnet_diagnostic.CA1032.severity = warning + +# CA1033: Interface methods should be callable by child types +dotnet_diagnostic.CA1033.severity = warning + +# CA1034: Nested types should not be visible +dotnet_diagnostic.CA1034.severity = warning + +# CA1036: Override methods on comparable types +dotnet_diagnostic.CA1036.severity = silent + +# CA1040: Avoid empty interfaces +dotnet_diagnostic.CA1040.severity = warning + +# CA1041: Provide ObsoleteAttribute message +dotnet_diagnostic.CA1041.severity = suggestion + +# CA1043: Use Integral Or String Argument For Indexers +dotnet_diagnostic.CA1043.severity = warning + +# CA1044: Properties should not be write only +dotnet_diagnostic.CA1044.severity = warning + +# CA1045: Do not pass types by reference +dotnet_diagnostic.CA1045.severity = warning + +# CA1046: Do not overload equality operator on reference types +dotnet_diagnostic.CA1046.severity = warning + +# CA1047: Do not declare protected member in sealed type +dotnet_diagnostic.CA1047.severity = suggestion + +# CA1050: Declare types in namespaces +dotnet_diagnostic.CA1050.severity = suggestion + +# CA1051: Do not declare visible instance fields +dotnet_diagnostic.CA1051.severity = silent + +# CA1052: Static holder types should be Static or NotInheritable +dotnet_diagnostic.CA1052.severity = warning + +# CA1054: URI-like parameters should not be strings +dotnet_diagnostic.CA1054.severity = warning + +# CA1055: URI-like return values should not be strings +dotnet_diagnostic.CA1055.severity = warning + +# CA1056: URI-like properties should not be strings +dotnet_diagnostic.CA1056.severity = warning + +# CA1058: Types should not extend certain base types +dotnet_diagnostic.CA1058.severity = warning + +# CA1060: Move pinvokes to native methods class +dotnet_diagnostic.CA1060.severity = warning + +# CA1061: Do not hide base class methods +dotnet_diagnostic.CA1061.severity = suggestion + +# CA1062: Validate arguments of public methods +dotnet_diagnostic.CA1062.severity = warning + +# CA1063: Implement IDisposable Correctly +dotnet_diagnostic.CA1063.severity = warning + +# CA1064: Exceptions should be public +dotnet_diagnostic.CA1064.severity = warning + +# CA1065: Do not raise exceptions in unexpected locations +dotnet_diagnostic.CA1065.severity = warning + +# CA1066: Implement IEquatable when overriding Object.Equals +dotnet_diagnostic.CA1066.severity = warning + +# CA1067: Override Object.Equals(object) when implementing IEquatable +dotnet_diagnostic.CA1067.severity = suggestion + +# CA1068: CancellationToken parameters must come last +dotnet_diagnostic.CA1068.severity = suggestion + +# CA1069: Enums values should not be duplicated +dotnet_diagnostic.CA1069.severity = suggestion + +# CA1070: Do not declare event fields as virtual +dotnet_diagnostic.CA1070.severity = suggestion + +# CA1200: Avoid using cref tags with a prefix +dotnet_diagnostic.CA1200.severity = silent + +# CA1303: Do not pass literals as localized parameters +dotnet_diagnostic.CA1303.severity = warning + +# CA1304: Specify CultureInfo +dotnet_diagnostic.CA1304.severity = silent + +# CA1305: Specify IFormatProvider +dotnet_diagnostic.CA1305.severity = silent + +# CA1307: Specify StringComparison for clarity +dotnet_diagnostic.CA1307.severity = warning + +# CA1308: Normalize strings to uppercase +dotnet_diagnostic.CA1308.severity = warning + +# CA1309: Use ordinal string comparison +dotnet_diagnostic.CA1309.severity = silent + +# CA1310: Specify StringComparison for correctness +dotnet_diagnostic.CA1310.severity = silent + +# CA1401: P/Invokes should not be visible +dotnet_diagnostic.CA1401.severity = suggestion + +# CA1416: Validate platform compatibility +dotnet_diagnostic.CA1416.severity = warning + +# CA1417: Do not use 'OutAttribute' on string parameters for P/Invokes +dotnet_diagnostic.CA1417.severity = warning + +# CA1501: Avoid excessive inheritance +dotnet_diagnostic.CA1501.severity = warning + +# CA1502: Avoid excessive complexity +dotnet_diagnostic.CA1502.severity = warning + +# CA1505: Avoid unmaintainable code +dotnet_diagnostic.CA1505.severity = warning + +# CA1506: Avoid excessive class coupling +dotnet_diagnostic.CA1506.severity = warning + +# CA1507: Use nameof to express symbol names +dotnet_diagnostic.CA1507.severity = suggestion + +# CA1508: Avoid dead conditional code +dotnet_diagnostic.CA1508.severity = warning + +# CA1509: Invalid entry in code metrics rule specification file +dotnet_diagnostic.CA1509.severity = warning + +# CA1700: Do not name enum values 'Reserved' +dotnet_diagnostic.CA1700.severity = warning + +# CA1707: Identifiers should not contain underscores +dotnet_diagnostic.CA1707.severity = silent + +# CA1708: Identifiers should differ by more than case +dotnet_diagnostic.CA1708.severity = silent + +# CA1710: Identifiers should have correct suffix +dotnet_diagnostic.CA1710.severity = silent + +# CA1711: Identifiers should not have incorrect suffix +dotnet_diagnostic.CA1711.severity = silent + +# CA1712: Do not prefix enum values with type name +dotnet_diagnostic.CA1712.severity = silent + +# CA1713: Events should not have 'Before' or 'After' prefix +dotnet_diagnostic.CA1713.severity = warning + +# CA1715: Identifiers should have correct prefix +dotnet_diagnostic.CA1715.severity = silent + +# CA1716: Identifiers should not match keywords +dotnet_diagnostic.CA1716.severity = silent + +# CA1720: Identifier contains type name +dotnet_diagnostic.CA1720.severity = silent + +# CA1721: Property names should not match get methods +dotnet_diagnostic.CA1721.severity = warning + +# CA1724: Type names should not match namespaces +dotnet_diagnostic.CA1724.severity = warning + +# CA1725: Parameter names should match base declaration +dotnet_diagnostic.CA1725.severity = silent + +# CA1801: Review unused parameters +dotnet_diagnostic.CA1801.severity = warning + +# CA1802: Use literals where appropriate +dotnet_diagnostic.CA1802.severity = warning + +# CA1805: Do not initialize unnecessarily +dotnet_diagnostic.CA1805.severity = silent + +# CA1806: Do not ignore method results +dotnet_diagnostic.CA1806.severity = suggestion + +# CA1810: Initialize reference type static fields inline +dotnet_diagnostic.CA1810.severity = warning + +# CA1812: Avoid uninstantiated internal classes +dotnet_diagnostic.CA1812.severity = warning + +# CA1813: Avoid unsealed attributes +dotnet_diagnostic.CA1813.severity = warning + +# CA1814: Prefer jagged arrays over multidimensional +dotnet_diagnostic.CA1814.severity = warning + +# CA1815: Override equals and operator equals on value types +dotnet_diagnostic.CA1815.severity = warning + +# CA1816: Dispose methods should call SuppressFinalize +dotnet_diagnostic.CA1816.severity = suggestion + +# CA1819: Properties should not return arrays +dotnet_diagnostic.CA1819.severity = warning + +# CA1820: Test for empty strings using string length +dotnet_diagnostic.CA1820.severity = warning + +# CA1821: Remove empty Finalizers +dotnet_diagnostic.CA1821.severity = suggestion + +# CA1822: Mark members as static +dotnet_diagnostic.CA1822.severity = suggestion + +# CA1823: Avoid unused private fields +dotnet_diagnostic.CA1823.severity = warning + +# CA1824: Mark assemblies with NeutralResourcesLanguageAttribute +dotnet_diagnostic.CA1824.severity = suggestion + +# CA1825: Avoid zero-length array allocations +dotnet_diagnostic.CA1825.severity = suggestion + +# CA1826: Do not use Enumerable methods on indexable collections +dotnet_diagnostic.CA1826.severity = suggestion + +# CA1827: Do not use Count() or LongCount() when Any() can be used +dotnet_diagnostic.CA1827.severity = suggestion + +# CA1828: Do not use CountAsync() or LongCountAsync() when AnyAsync() can be used +dotnet_diagnostic.CA1828.severity = suggestion + +# CA1829: Use Length/Count property instead of Count() when available +dotnet_diagnostic.CA1829.severity = suggestion + +# CA1830: Prefer strongly-typed Append and Insert method overloads on StringBuilder +dotnet_diagnostic.CA1830.severity = suggestion + +# CA1831: Use AsSpan or AsMemory instead of Range-based indexers when appropriate +dotnet_diagnostic.CA1831.severity = warning + +# CA1832: Use AsSpan or AsMemory instead of Range-based indexers when appropriate +dotnet_diagnostic.CA1832.severity = suggestion + +# CA1833: Use AsSpan or AsMemory instead of Range-based indexers when appropriate +dotnet_diagnostic.CA1833.severity = suggestion + +# CA1834: Consider using 'StringBuilder.Append(char)' when applicable +dotnet_diagnostic.CA1834.severity = suggestion + +# CA1835: Prefer the 'Memory'-based overloads for 'ReadAsync' and 'WriteAsync' +dotnet_diagnostic.CA1835.severity = suggestion + +# CA1836: Prefer IsEmpty over Count +dotnet_diagnostic.CA1836.severity = suggestion + +# CA1837: Use 'Environment.ProcessId' +dotnet_diagnostic.CA1837.severity = suggestion + +# CA1838: Avoid 'StringBuilder' parameters for P/Invokes +dotnet_diagnostic.CA1838.severity = silent + +# CA2000: Dispose objects before losing scope +dotnet_diagnostic.CA2000.severity = warning + +# CA2002: Do not lock on objects with weak identity +dotnet_diagnostic.CA2002.severity = warning + +# CA2007: Consider calling ConfigureAwait on the awaited task +dotnet_diagnostic.CA2007.severity = warning + +# CA2008: Do not create tasks without passing a TaskScheduler +dotnet_diagnostic.CA2008.severity = warning + +# CA2009: Do not call ToImmutableCollection on an ImmutableCollection value +dotnet_diagnostic.CA2009.severity = suggestion + +# CA2011: Avoid infinite recursion +dotnet_diagnostic.CA2011.severity = suggestion + +# CA2012: Use ValueTasks correctly +dotnet_diagnostic.CA2012.severity = suggestion + +# CA2013: Do not use ReferenceEquals with value types +dotnet_diagnostic.CA2013.severity = warning + +# CA2014: Do not use stackalloc in loops +dotnet_diagnostic.CA2014.severity = warning + +# CA2015: Do not define finalizers for types derived from MemoryManager +dotnet_diagnostic.CA2015.severity = warning + +# CA2016: Forward the 'CancellationToken' parameter to methods +dotnet_diagnostic.CA2016.severity = suggestion + +# CA2100: Review SQL queries for security vulnerabilities +dotnet_diagnostic.CA2100.severity = warning + +# CA2101: Specify marshaling for P/Invoke string arguments +dotnet_diagnostic.CA2101.severity = suggestion + +# CA2109: Review visible event handlers +dotnet_diagnostic.CA2109.severity = warning + +# CA2119: Seal methods that satisfy private interfaces +dotnet_diagnostic.CA2119.severity = warning + +# CA2153: Do Not Catch Corrupted State Exceptions +dotnet_diagnostic.CA2153.severity = warning + +# CA2200: Rethrow to preserve stack details +dotnet_diagnostic.CA2200.severity = warning + +# CA2201: Do not raise reserved exception types +dotnet_diagnostic.CA2201.severity = silent + +# CA2207: Initialize value type static fields inline +dotnet_diagnostic.CA2207.severity = warning + +# CA2208: Instantiate argument exceptions correctly +dotnet_diagnostic.CA2208.severity = suggestion + +# CA2211: Non-constant fields should not be visible +dotnet_diagnostic.CA2211.severity = suggestion + +# CA2213: Disposable fields should be disposed +dotnet_diagnostic.CA2213.severity = warning + +# CA2214: Do not call overridable methods in constructors +dotnet_diagnostic.CA2214.severity = warning + +# CA2215: Dispose methods should call base class dispose +dotnet_diagnostic.CA2215.severity = silent + +# CA2216: Disposable types should declare finalizer +dotnet_diagnostic.CA2216.severity = warning + +# CA2217: Do not mark enums with FlagsAttribute +dotnet_diagnostic.CA2217.severity = warning + +# CA2218: Override GetHashCode on overriding Equals +dotnet_diagnostic.CA2218.severity = suggestion + +# CA2219: Do not raise exceptions in finally clauses +dotnet_diagnostic.CA2219.severity = suggestion + +# CA2224: Override Equals on overloading operator equals +dotnet_diagnostic.CA2224.severity = suggestion + +# CA2225: Operator overloads have named alternates +dotnet_diagnostic.CA2225.severity = warning + +# CA2226: Operators should have symmetrical overloads +dotnet_diagnostic.CA2226.severity = warning + +# CA2227: Collection properties should be read only +dotnet_diagnostic.CA2227.severity = warning + +# CA2229: Implement serialization constructors +dotnet_diagnostic.CA2229.severity = silent + +# CA2231: Overload operator equals on overriding value type Equals +dotnet_diagnostic.CA2231.severity = suggestion + +# CA2234: Pass system uri objects instead of strings +dotnet_diagnostic.CA2234.severity = warning + +# CA2235: Mark all non-serializable fields +dotnet_diagnostic.CA2235.severity = warning + +# CA2237: Mark ISerializable types with serializable +dotnet_diagnostic.CA2237.severity = warning + +# CA2241: Provide correct arguments to formatting methods +dotnet_diagnostic.CA2241.severity = suggestion + +# CA2242: Test for NaN correctly +dotnet_diagnostic.CA2242.severity = suggestion + +# CA2243: Attribute string literals should parse correctly +dotnet_diagnostic.CA2243.severity = warning + +# CA2244: Do not duplicate indexed element initializations +dotnet_diagnostic.CA2244.severity = suggestion + +# CA2245: Do not assign a property to itself +dotnet_diagnostic.CA2245.severity = suggestion + +# CA2246: Assigning symbol and its member in the same statement +dotnet_diagnostic.CA2246.severity = suggestion + +# CA2247: Argument passed to TaskCompletionSource constructor should be TaskCreationOptions enum instead of TaskContinuationOptions enum +dotnet_diagnostic.CA2247.severity = warning + +# CA2248: Provide correct 'enum' argument to 'Enum.HasFlag' +dotnet_diagnostic.CA2248.severity = suggestion + +# CA2249: Consider using 'string.Contains' instead of 'string.IndexOf' +dotnet_diagnostic.CA2249.severity = suggestion + +# CA2300: Do not use insecure deserializer BinaryFormatter +dotnet_diagnostic.CA2300.severity = warning + +# CA2301: Do not call BinaryFormatter.Deserialize without first setting BinaryFormatter.Binder +dotnet_diagnostic.CA2301.severity = warning + +# CA2302: Ensure BinaryFormatter.Binder is set before calling BinaryFormatter.Deserialize +dotnet_diagnostic.CA2302.severity = warning + +# CA2305: Do not use insecure deserializer LosFormatter +dotnet_diagnostic.CA2305.severity = warning + +# CA2310: Do not use insecure deserializer NetDataContractSerializer +dotnet_diagnostic.CA2310.severity = warning + +# CA2311: Do not deserialize without first setting NetDataContractSerializer.Binder +dotnet_diagnostic.CA2311.severity = warning + +# CA2312: Ensure NetDataContractSerializer.Binder is set before deserializing +dotnet_diagnostic.CA2312.severity = warning + +# CA2315: Do not use insecure deserializer ObjectStateFormatter +dotnet_diagnostic.CA2315.severity = warning + +# CA2321: Do not deserialize with JavaScriptSerializer using a SimpleTypeResolver +dotnet_diagnostic.CA2321.severity = warning + +# CA2322: Ensure JavaScriptSerializer is not initialized with SimpleTypeResolver before deserializing +dotnet_diagnostic.CA2322.severity = warning + +# CA2326: Do not use TypeNameHandling values other than None +dotnet_diagnostic.CA2326.severity = warning + +# CA2327: Do not use insecure JsonSerializerSettings +dotnet_diagnostic.CA2327.severity = warning + +# CA2328: Ensure that JsonSerializerSettings are secure +dotnet_diagnostic.CA2328.severity = warning + +# CA2329: Do not deserialize with JsonSerializer using an insecure configuration +dotnet_diagnostic.CA2329.severity = warning + +# CA2330: Ensure that JsonSerializer has a secure configuration when deserializing +dotnet_diagnostic.CA2330.severity = warning + +# CA2350: Do not use DataTable.ReadXml() with untrusted data +dotnet_diagnostic.CA2350.severity = warning + +# CA2351: Do not use DataSet.ReadXml() with untrusted data +dotnet_diagnostic.CA2351.severity = warning + +# CA2352: Unsafe DataSet or DataTable in serializable type can be vulnerable to remote code execution attacks +dotnet_diagnostic.CA2352.severity = warning + +# CA2353: Unsafe DataSet or DataTable in serializable type +dotnet_diagnostic.CA2353.severity = warning + +# CA2354: Unsafe DataSet or DataTable in deserialized object graph can be vulnerable to remote code execution attacks +dotnet_diagnostic.CA2354.severity = warning + +# CA2355: Unsafe DataSet or DataTable type found in deserializable object graph +dotnet_diagnostic.CA2355.severity = warning + +# CA2356: Unsafe DataSet or DataTable type in web deserializable object graph +dotnet_diagnostic.CA2356.severity = warning + +# CA2361: Ensure auto-generated class containing DataSet.ReadXml() is not used with untrusted data +dotnet_diagnostic.CA2361.severity = warning + +# CA2362: Unsafe DataSet or DataTable in auto-generated serializable type can be vulnerable to remote code execution attacks +dotnet_diagnostic.CA2362.severity = warning + +# CA3001: Review code for SQL injection vulnerabilities +dotnet_diagnostic.CA3001.severity = warning + +# CA3002: Review code for XSS vulnerabilities +dotnet_diagnostic.CA3002.severity = warning + +# CA3003: Review code for file path injection vulnerabilities +dotnet_diagnostic.CA3003.severity = warning + +# CA3004: Review code for information disclosure vulnerabilities +dotnet_diagnostic.CA3004.severity = warning + +# CA3005: Review code for LDAP injection vulnerabilities +dotnet_diagnostic.CA3005.severity = warning + +# CA3006: Review code for process command injection vulnerabilities +dotnet_diagnostic.CA3006.severity = warning + +# CA3007: Review code for open redirect vulnerabilities +dotnet_diagnostic.CA3007.severity = warning + +# CA3008: Review code for XPath injection vulnerabilities +dotnet_diagnostic.CA3008.severity = warning + +# CA3009: Review code for XML injection vulnerabilities +dotnet_diagnostic.CA3009.severity = warning + +# CA3010: Review code for XAML injection vulnerabilities +dotnet_diagnostic.CA3010.severity = warning + +# CA3011: Review code for DLL injection vulnerabilities +dotnet_diagnostic.CA3011.severity = warning + +# CA3012: Review code for regex injection vulnerabilities +dotnet_diagnostic.CA3012.severity = warning + +# CA3061: Do Not Add Schema By URL +dotnet_diagnostic.CA3061.severity = silent + +# CA3075: Insecure DTD processing in XML +dotnet_diagnostic.CA3075.severity = silent + +# CA3076: Insecure XSLT script processing. +dotnet_diagnostic.CA3076.severity = silent + +# CA3077: Insecure Processing in API Design, XmlDocument and XmlTextReader +dotnet_diagnostic.CA3077.severity = silent + +# CA3147: Mark Verb Handlers With Validate Antiforgery Token +dotnet_diagnostic.CA3147.severity = silent + +# CA5350: Do Not Use Weak Cryptographic Algorithms +dotnet_diagnostic.CA5350.severity = silent + +# CA5351: Do Not Use Broken Cryptographic Algorithms +dotnet_diagnostic.CA5351.severity = silent + +# CA5358: Review cipher mode usage with cryptography experts +dotnet_diagnostic.CA5358.severity = warning + +# CA5359: Do Not Disable Certificate Validation +dotnet_diagnostic.CA5359.severity = silent + +# CA5360: Do Not Call Dangerous Methods In Deserialization +dotnet_diagnostic.CA5360.severity = silent + +# CA5361: Do Not Disable SChannel Use of Strong Crypto +dotnet_diagnostic.CA5361.severity = warning + +# CA5362: Potential reference cycle in deserialized object graph +dotnet_diagnostic.CA5362.severity = warning + +# CA5363: Do Not Disable Request Validation +dotnet_diagnostic.CA5363.severity = silent + +# CA5364: Do Not Use Deprecated Security Protocols +dotnet_diagnostic.CA5364.severity = silent + +# CA5365: Do Not Disable HTTP Header Checking +dotnet_diagnostic.CA5365.severity = silent + +# CA5366: Use XmlReader for 'DataSet.ReadXml()' +dotnet_diagnostic.CA5366.severity = silent + +# CA5367: Do Not Serialize Types With Pointer Fields +dotnet_diagnostic.CA5367.severity = warning + +# CA5368: Set ViewStateUserKey For Classes Derived From Page +dotnet_diagnostic.CA5368.severity = silent + +# CA5369: Use XmlReader for 'XmlSerializer.Deserialize()' +dotnet_diagnostic.CA5369.severity = silent + +# CA5370: Use XmlReader for XmlValidatingReader constructor +dotnet_diagnostic.CA5370.severity = silent + +# CA5371: Use XmlReader for 'XmlSchema.Read()' +dotnet_diagnostic.CA5371.severity = silent + +# CA5372: Use XmlReader for XPathDocument constructor +dotnet_diagnostic.CA5372.severity = silent + +# CA5373: Do not use obsolete key derivation function +dotnet_diagnostic.CA5373.severity = silent + +# CA5374: Do Not Use XslTransform +dotnet_diagnostic.CA5374.severity = silent + +# CA5375: Do Not Use Account Shared Access Signature +dotnet_diagnostic.CA5375.severity = warning + +# CA5376: Use SharedAccessProtocol HttpsOnly +dotnet_diagnostic.CA5376.severity = warning + +# CA5377: Use Container Level Access Policy +dotnet_diagnostic.CA5377.severity = warning + +# CA5378: Do not disable ServicePointManagerSecurityProtocols +dotnet_diagnostic.CA5378.severity = warning + +# CA5379: Ensure Key Derivation Function algorithm is sufficiently strong +dotnet_diagnostic.CA5379.severity = silent + +# CA5380: Do Not Add Certificates To Root Store +dotnet_diagnostic.CA5380.severity = warning + +# CA5381: Ensure Certificates Are Not Added To Root Store +dotnet_diagnostic.CA5381.severity = warning + +# CA5382: Use Secure Cookies In ASP.NET Core +dotnet_diagnostic.CA5382.severity = warning + +# CA5383: Ensure Use Secure Cookies In ASP.NET Core +dotnet_diagnostic.CA5383.severity = warning + +# CA5384: Do Not Use Digital Signature Algorithm (DSA) +dotnet_diagnostic.CA5384.severity = silent + +# CA5385: Use Rivest–Shamir–Adleman (RSA) Algorithm With Sufficient Key Size +dotnet_diagnostic.CA5385.severity = silent + +# CA5386: Avoid hardcoding SecurityProtocolType value +dotnet_diagnostic.CA5386.severity = warning + +# CA5387: Do Not Use Weak Key Derivation Function With Insufficient Iteration Count +dotnet_diagnostic.CA5387.severity = warning + +# CA5388: Ensure Sufficient Iteration Count When Using Weak Key Derivation Function +dotnet_diagnostic.CA5388.severity = warning + +# CA5389: Do Not Add Archive Item's Path To The Target File System Path +dotnet_diagnostic.CA5389.severity = warning + +# CA5390: Do not hard-code encryption key +dotnet_diagnostic.CA5390.severity = warning + +# CA5391: Use antiforgery tokens in ASP.NET Core MVC controllers +dotnet_diagnostic.CA5391.severity = warning + +# CA5392: Use DefaultDllImportSearchPaths attribute for P/Invokes +dotnet_diagnostic.CA5392.severity = warning + +# CA5393: Do not use unsafe DllImportSearchPath value +dotnet_diagnostic.CA5393.severity = warning + +# CA5394: Do not use insecure randomness +dotnet_diagnostic.CA5394.severity = warning + +# CA5395: Miss HttpVerb attribute for action methods +dotnet_diagnostic.CA5395.severity = warning + +# CA5396: Set HttpOnly to true for HttpCookie +dotnet_diagnostic.CA5396.severity = warning + +# CA5397: Do not use deprecated SslProtocols values +dotnet_diagnostic.CA5397.severity = silent + +# CA5398: Avoid hardcoded SslProtocols values +dotnet_diagnostic.CA5398.severity = warning + +# CA5399: HttpClients should enable certificate revocation list checks +dotnet_diagnostic.CA5399.severity = warning + +# CA5400: Ensure HttpClient certificate revocation list check is not disabled +dotnet_diagnostic.CA5400.severity = warning + +# CA5401: Do not use CreateEncryptor with non-default IV +dotnet_diagnostic.CA5401.severity = warning + +# CA5402: Use CreateEncryptor with the default IV +dotnet_diagnostic.CA5402.severity = warning + +# CA5403: Do not hard-code certificate +dotnet_diagnostic.CA5403.severity = warning + +# IL3000: Avoid using accessing Assembly file path when publishing as a single-file +dotnet_diagnostic.IL3000.severity = warning + +# IL3001: Avoid using accessing Assembly file path when publishing as a single-file +dotnet_diagnostic.IL3001.severity = warning \ No newline at end of file diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..dfcfd56 --- /dev/null +++ b/.gitignore @@ -0,0 +1,350 @@ +## Ignore Visual Studio temporary files, build results, and +## files generated by popular Visual Studio add-ons. +## +## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore + +# User-specific files +*.rsuser +*.suo +*.user +*.userosscache +*.sln.docstates + +# User-specific files (MonoDevelop/Xamarin Studio) +*.userprefs + +# Mono auto generated files +mono_crash.* + +# Build results +[Dd]ebug/ +[Dd]ebugPublic/ +[Rr]elease/ +[Rr]eleases/ +x64/ +x86/ +[Aa][Rr][Mm]/ +[Aa][Rr][Mm]64/ +bld/ +[Bb]in/ +[Oo]bj/ +[Ll]og/ +[Ll]ogs/ + +# Visual Studio 2015/2017 cache/options directory +.vs/ +# Uncomment if you have tasks that create the project's static files in wwwroot +#wwwroot/ + +# Visual Studio 2017 auto generated files +Generated\ Files/ + +# MSTest test Results +[Tt]est[Rr]esult*/ +[Bb]uild[Ll]og.* + +# NUnit +*.VisualState.xml +TestResult.xml +nunit-*.xml + +# Build Results of an ATL Project +[Dd]ebugPS/ +[Rr]eleasePS/ +dlldata.c + +# Benchmark Results +BenchmarkDotNet.Artifacts/ + +# .NET Core +project.lock.json +project.fragment.lock.json +artifacts/ + +# StyleCop +StyleCopReport.xml + +# Files built by Visual Studio +*_i.c +*_p.c +*_h.h +*.ilk +*.meta +*.obj +*.iobj +*.pch +*.pdb +*.ipdb +*.pgc +*.pgd +*.rsp +*.sbr +*.tlb +*.tli +*.tlh +*.tmp +*.tmp_proj +*_wpftmp.csproj +*.log +*.vspscc +*.vssscc +.builds +*.pidb +*.svclog +*.scc + +# Chutzpah Test files +_Chutzpah* + +# Visual C++ cache files +ipch/ +*.aps +*.ncb +*.opendb +*.opensdf +*.sdf +*.cachefile +*.VC.db +*.VC.VC.opendb + +# Visual Studio profiler +*.psess +*.vsp +*.vspx +*.sap + +# Visual Studio Trace Files +*.e2e + +# TFS 2012 Local Workspace +$tf/ + +# Guidance Automation Toolkit +*.gpState + +# ReSharper is a .NET coding add-in +_ReSharper*/ +*.[Rr]e[Ss]harper +*.DotSettings.user + +# TeamCity is a build add-in +_TeamCity* + +# DotCover is a Code Coverage Tool +*.dotCover + +# AxoCover is a Code Coverage Tool +.axoCover/* +!.axoCover/settings.json + +# Visual Studio code coverage results +*.coverage +*.coveragexml + +# NCrunch +_NCrunch_* +.*crunch*.local.xml +nCrunchTemp_* + +# MightyMoose +*.mm.* +AutoTest.Net/ + +# Web workbench (sass) +.sass-cache/ + +# Installshield output folder +[Ee]xpress/ + +# DocProject is a documentation generator add-in +DocProject/buildhelp/ +DocProject/Help/*.HxT +DocProject/Help/*.HxC +DocProject/Help/*.hhc +DocProject/Help/*.hhk +DocProject/Help/*.hhp +DocProject/Help/Html2 +DocProject/Help/html + +# Click-Once directory +publish/ + +# Publish Web Output +*.[Pp]ublish.xml +*.azurePubxml +# Note: Comment the next line if you want to checkin your web deploy settings, +# but database connection strings (with potential passwords) will be unencrypted +*.pubxml +*.publishproj + +# Microsoft Azure Web App publish settings. Comment the next line if you want to +# checkin your Azure Web App publish settings, but sensitive information contained +# in these scripts will be unencrypted +PublishScripts/ + +# NuGet Packages +*.nupkg +# NuGet Symbol Packages +*.snupkg +# The packages folder can be ignored because of Package Restore +**/[Pp]ackages/* +# except build/, which is used as an MSBuild target. +!**/[Pp]ackages/build/ +# Uncomment if necessary however generally it will be regenerated when needed +#!**/[Pp]ackages/repositories.config +# NuGet v3's project.json files produces more ignorable files +*.nuget.props +*.nuget.targets + +# Microsoft Azure Build Output +csx/ +*.build.csdef + +# Microsoft Azure Emulator +ecf/ +rcf/ + +# Windows Store app package directories and files +AppPackages/ +BundleArtifacts/ +Package.StoreAssociation.xml +_pkginfo.txt +*.appx +*.appxbundle +*.appxupload + +# Visual Studio cache files +# files ending in .cache can be ignored +*.[Cc]ache +# but keep track of directories ending in .cache +!?*.[Cc]ache/ + +# Others +ClientBin/ +~$* +*~ +*.dbmdl +*.dbproj.schemaview +*.jfm +*.pfx +*.publishsettings +orleans.codegen.cs + +# Including strong name files can present a security risk +# (https://github.com/github/gitignore/pull/2483#issue-259490424) +#*.snk + +# Since there are multiple workflows, uncomment next line to ignore bower_components +# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) +#bower_components/ + +# RIA/Silverlight projects +Generated_Code/ + +# Backup & report files from converting an old project file +# to a newer Visual Studio version. Backup files are not needed, +# because we have git ;-) +_UpgradeReport_Files/ +Backup*/ +UpgradeLog*.XML +UpgradeLog*.htm +ServiceFabricBackup/ +*.rptproj.bak + +# SQL Server files +*.mdf +*.ldf +*.ndf + +# Business Intelligence projects +*.rdl.data +*.bim.layout +*.bim_*.settings +*.rptproj.rsuser +*- [Bb]ackup.rdl +*- [Bb]ackup ([0-9]).rdl +*- [Bb]ackup ([0-9][0-9]).rdl + +# Microsoft Fakes +FakesAssemblies/ + +# GhostDoc plugin setting file +*.GhostDoc.xml + +# Node.js Tools for Visual Studio +.ntvs_analysis.dat +node_modules/ + +# Visual Studio 6 build log +*.plg + +# Visual Studio 6 workspace options file +*.opt + +# Visual Studio 6 auto-generated workspace file (contains which files were open etc.) +*.vbw + +# Visual Studio LightSwitch build output +**/*.HTMLClient/GeneratedArtifacts +**/*.DesktopClient/GeneratedArtifacts +**/*.DesktopClient/ModelManifest.xml +**/*.Server/GeneratedArtifacts +**/*.Server/ModelManifest.xml +_Pvt_Extensions + +# Paket dependency manager +.paket/paket.exe +paket-files/ + +# FAKE - F# Make +.fake/ + +# CodeRush personal settings +.cr/personal + +# Python Tools for Visual Studio (PTVS) +__pycache__/ +*.pyc + +# Cake - Uncomment if you are using it +# tools/** +# !tools/packages.config + +# Tabs Studio +*.tss + +# Telerik's JustMock configuration file +*.jmconfig + +# BizTalk build output +*.btp.cs +*.btm.cs +*.odx.cs +*.xsd.cs + +# OpenCover UI analysis results +OpenCover/ + +# Azure Stream Analytics local run output +ASALocalRun/ + +# MSBuild Binary and Structured Log +*.binlog + +# NVidia Nsight GPU debugger configuration file +*.nvuser + +# MFractors (Xamarin productivity tool) working folder +.mfractor/ + +# Local History for Visual Studio +.localhistory/ + +# BeatPulse healthcheck temp database +healthchecksdb + +# Backup folder for Package Reference Convert tool in Visual Studio 2017 +MigrationBackup/ + +# Ionide (cross platform F# VS Code tools) working folder +.ionide/ diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md new file mode 100644 index 0000000..5ae910e --- /dev/null +++ b/CODE_OF_CONDUCT.md @@ -0,0 +1,6 @@ +## Code of Conduct + +This project has adopted the code of conduct defined by the Contributor Covenant +to clarify expected behavior in our community. + +For more information, see the [.NET Foundation Code of Conduct](https://dotnetfoundation.org/code-of-conduct). \ No newline at end of file diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 0000000..142e991 --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,3 @@ +# Contributing + +Contributions are highly welcome, however, except for very small changes, kindly file an issue and let's have a discussion before you open a pull request. \ No newline at end of file diff --git a/KeepAChangelogParser.Tests/Comparer/ChangelogComparer.cs b/KeepAChangelogParser.Tests/Comparer/ChangelogComparer.cs new file mode 100644 index 0000000..b883abb --- /dev/null +++ b/KeepAChangelogParser.Tests/Comparer/ChangelogComparer.cs @@ -0,0 +1,133 @@ +using KeepAChangelogParser.Models; +using System; +using System.Collections.Generic; +using System.Globalization; + +namespace KeepAChangelogParser.Tests.Comparer +{ + + public class ChangelogComparer : + IComparer + { + + private readonly IComparer changelogSectionCollectionComparer; + private readonly IComparer changelogSectionUnreleasedComparer; + + public ChangelogComparer( + IComparer changelogSectionCollectionComparer, + IComparer changelogSectionUnreleasedComparer + ) + { + this.changelogSectionCollectionComparer = changelogSectionCollectionComparer; + this.changelogSectionUnreleasedComparer = changelogSectionUnreleasedComparer; + } + + public int Compare( + object? x, + object? y + ) + { + if (!(x is Changelog)) + { + string message = + string.Format( + CultureInfo.CurrentCulture, + "Required input {0} is not a recognized {1}.", + nameof(y), typeof(Changelog)); + + throw new ArgumentException(message, nameof(x)); + } + + if (!(y is Changelog)) + { + string message = + string.Format( + CultureInfo.CurrentCulture, + "Required input {0} is not a recognized {1}.", + nameof(y), typeof(Changelog)); + + throw new ArgumentException(message, nameof(y)); + } + + int result = + this.Compare( + (Changelog)x, + (Changelog)y); + + return result; + } + + public int Compare( + Changelog? x, + Changelog? y + ) + { + if (x == null && y != null) { return 1; } + if (x != null && y == null) { return -1; } + + if (x != null && y != null) + { + return this.compareChangelog(x, y); + } + + return 0; + } + + private int compareChangelog( + Changelog x, + Changelog y + ) + { + int result; + + if ((result = compare(x.MarkdownText, y.MarkdownText)) != 0) { return result; } + if ((result = compare(x.MarkdownTitle, y.MarkdownTitle)) != 0) { return result; } + if ((result = this.compare(x.SectionCollection, y.SectionCollection)) != 0) { return result; } + if ((result = this.compare(x.SectionUnreleased, y.SectionUnreleased)) != 0) { return result; } + + return result; + } + + private static int compare( + string x, + string y + ) + { + return string.Compare(x, y, StringComparison.Ordinal); + } + + private int compare( + ChangelogSectionCollection? x, + ChangelogSectionCollection? y + ) + { + if (x == null && y != null) { return 1; } + if (x != null && y == null) { return -1; } + + if (x != null && y != null) + { + return this.changelogSectionCollectionComparer.Compare(x, y); + } + + return 0; + } + + private int compare( + ChangelogSectionUnreleased? x, + ChangelogSectionUnreleased? y + ) + { + if (x == null && y != null) { return 1; } + if (x != null && y == null) { return -1; } + + if (x != null && y != null) + { + return this.changelogSectionUnreleasedComparer.Compare(x, y); + } + + return 0; + } + + } + +} diff --git a/KeepAChangelogParser.Tests/Comparer/ChangelogSectionCollectionComparer.cs b/KeepAChangelogParser.Tests/Comparer/ChangelogSectionCollectionComparer.cs new file mode 100644 index 0000000..efeda5f --- /dev/null +++ b/KeepAChangelogParser.Tests/Comparer/ChangelogSectionCollectionComparer.cs @@ -0,0 +1,96 @@ +using KeepAChangelogParser.Models; +using System; +using System.Collections; +using System.Collections.Generic; +using System.Globalization; + +namespace KeepAChangelogParser.Tests.Comparer +{ + + public class ChangelogSectionCollectionComparer : + IComparer, + IComparer + { + + private readonly IComparer changelogSectionComparer; + + public ChangelogSectionCollectionComparer( + IComparer changelogSectionComparer + ) + { + this.changelogSectionComparer = changelogSectionComparer; + } + + public int Compare( + object? x, + object? y + ) + { + if (!(x is ChangelogSectionCollection)) + { + string message = + string.Format( + CultureInfo.CurrentCulture, + "Required input {0} is not a recognized {1}.", + nameof(y), typeof(ChangelogSectionCollection)); + + throw new ArgumentException(message, nameof(x)); + } + + if (!(y is ChangelogSectionCollection)) + { + string message = + string.Format( + CultureInfo.CurrentCulture, + "Required input {0} is not a recognized {1}.", + nameof(y), typeof(ChangelogSectionCollection)); + + throw new ArgumentException(message, nameof(y)); + } + + int result = + this.Compare( + (ChangelogSectionCollection)x, + (ChangelogSectionCollection)y); + + return result; + } + + public int Compare( + ChangelogSectionCollection? x, + ChangelogSectionCollection? y + ) + { + if (x == null && y != null) { return 1; } + if (x != null && y == null) { return -1; } + + if (x != null && y != null) + { + if (x.Count < y.Count) { return 1; } + if (x.Count > y.Count) { return -1; } + + for (int i = 0; i < x.Count; i++) + { + int result = + this.compareChangelogSectionCollection( + x[i], + y[i]); + + if (result != 0) { return result; } + } + } + + return 0; + } + + private int compareChangelogSectionCollection( + ChangelogSection x, + ChangelogSection y + ) + { + return this.changelogSectionComparer.Compare(x, y); + } + + } + +} diff --git a/KeepAChangelogParser.Tests/Comparer/ChangelogSectionComparer.cs b/KeepAChangelogParser.Tests/Comparer/ChangelogSectionComparer.cs new file mode 100644 index 0000000..7c494c8 --- /dev/null +++ b/KeepAChangelogParser.Tests/Comparer/ChangelogSectionComparer.cs @@ -0,0 +1,113 @@ +using KeepAChangelogParser.Models; +using System; +using System.Collections.Generic; +using System.Globalization; + +namespace KeepAChangelogParser.Tests.Comparer +{ + + public class ChangelogSectionComparer : + IComparer + { + + private readonly IComparer changelogSubSectionCollectionComparer; + + public ChangelogSectionComparer( + IComparer changelogSubSectionCollectionComparer + ) + { + this.changelogSubSectionCollectionComparer = changelogSubSectionCollectionComparer; + } + + public int Compare( + object? x, + object? y + ) + { + if (!(x is ChangelogSection)) + { + string message = + string.Format( + CultureInfo.CurrentCulture, + "Required input {0} is not a recognized {1}.", + nameof(y), typeof(ChangelogSection)); + + throw new ArgumentException(message, nameof(x)); + } + + if (!(y is ChangelogSection)) + { + string message = + string.Format( + CultureInfo.CurrentCulture, + "Required input {0} is not a recognized {1}.", + nameof(y), typeof(ChangelogSection)); + + throw new ArgumentException(message, nameof(y)); + } + + int result = + this.Compare( + (ChangelogSection)x, + (ChangelogSection)y); + + return result; + } + + public int Compare( + ChangelogSection? x, + ChangelogSection? y + ) + { + if (x == null && y != null) { return 1; } + if (x != null && y == null) { return -1; } + + if (x != null && y != null) + { + return this.compareChangelogSection(x, y); + } + + return 0; + } + + private int compareChangelogSection( + ChangelogSection x, + ChangelogSection y + ) + { + int result; + + if ((result = compare(x.MarkdownDate, y.MarkdownDate)) != 0) { return result; } + if ((result = compare(x.MarkdownVersion, y.MarkdownVersion)) != 0) { return result; } + if ((result = this.compare(x.SubSectionCollection, y.SubSectionCollection)) != 0) { return result; } + + return result; + } + + private static int compare( + string x, + string y + ) + { + return string.Compare(x, y, StringComparison.Ordinal); + } + + private int compare( + ChangelogSubSectionCollection? x, + ChangelogSubSectionCollection? y + ) + { + if (x == null && y != null) { return 1; } + if (x != null && y == null) { return -1; } + + if (x != null && y != null) + { + return this.changelogSubSectionCollectionComparer.Compare(x, y); + } + + return 0; + } + + } + +} diff --git a/KeepAChangelogParser.Tests/Comparer/ChangelogSectionUnreleasedComparer.cs b/KeepAChangelogParser.Tests/Comparer/ChangelogSectionUnreleasedComparer.cs new file mode 100644 index 0000000..8010861 --- /dev/null +++ b/KeepAChangelogParser.Tests/Comparer/ChangelogSectionUnreleasedComparer.cs @@ -0,0 +1,115 @@ +using KeepAChangelogParser.Models; +using System; +using System.Collections; +using System.Collections.Generic; +using System.Globalization; + +namespace KeepAChangelogParser.Tests.Comparer +{ + + public class ChangelogSectionUnreleasedComparer : + IComparer, + IComparer + + { + + private readonly IComparer changelogSubSectionCollectionComparer; + + public ChangelogSectionUnreleasedComparer( + IComparer changelogSubSectionCollectionComparer + ) + { + this.changelogSubSectionCollectionComparer = changelogSubSectionCollectionComparer; + } + + public int Compare( + object? x, + object? y + ) + { + if (!(x is ChangelogSectionUnreleased)) + { + string message = + string.Format( + CultureInfo.CurrentCulture, + "Required input {0} is not a recognized {1}.", + nameof(y), typeof(ChangelogSectionUnreleased)); + + throw new ArgumentException(message, nameof(x)); + } + + if (!(y is ChangelogSectionUnreleased)) + { + string message = + string.Format( + CultureInfo.CurrentCulture, + "Required input {0} is not a recognized {1}.", + nameof(y), typeof(ChangelogSectionUnreleased)); + + throw new ArgumentException(message, nameof(y)); + } + + int result = + this.Compare( + (ChangelogSectionUnreleased)x, + (ChangelogSectionUnreleased)y); + + return result; + } + + public int Compare( + ChangelogSectionUnreleased? x, + ChangelogSectionUnreleased? y + ) + { + if (x == null && y != null) { return 1; } + if (x != null && y == null) { return -1; } + + if (x != null && y != null) + { + return this.compareChangelogSectionUnreleased(x, y); + } + + return 0; + } + + private int compareChangelogSectionUnreleased( + ChangelogSectionUnreleased x, + ChangelogSectionUnreleased y + ) + { + int result; + + if ((result = compare(x.MarkdownTitle, y.MarkdownTitle)) != 0) { return result; } + if ((result = this.compare(x.SubSectionCollection, y.SubSectionCollection)) != 0) { return result; } + + return result; + } + + private static int compare( + string x, + string y + ) + { + return string.Compare(x, y, StringComparison.Ordinal); + } + + private int compare( + ChangelogSubSectionCollection? x, + ChangelogSubSectionCollection? y + ) + { + if (x == null && y != null) { return 1; } + if (x != null && y == null) { return -1; } + + if (x != null && y != null) + { + return this.changelogSubSectionCollectionComparer.Compare(x, y); + } + + return 0; + } + + } + +} diff --git a/KeepAChangelogParser.Tests/Comparer/ChangelogSubSectionCollectionComparer.cs b/KeepAChangelogParser.Tests/Comparer/ChangelogSubSectionCollectionComparer.cs new file mode 100644 index 0000000..479cffe --- /dev/null +++ b/KeepAChangelogParser.Tests/Comparer/ChangelogSubSectionCollectionComparer.cs @@ -0,0 +1,96 @@ +using KeepAChangelogParser.Models; +using System; +using System.Collections; +using System.Collections.Generic; +using System.Globalization; + +namespace KeepAChangelogParser.Tests.Comparer +{ + + public class ChangelogSubSectionCollectionComparer : + IComparer, + IComparer + { + + private readonly IComparer changelogSubSectionComparer; + + public ChangelogSubSectionCollectionComparer( + IComparer changelogSubSectionComparer + ) + { + this.changelogSubSectionComparer = changelogSubSectionComparer; + } + + public int Compare( + object? x, + object? y + ) + { + if (!(x is ChangelogSubSectionCollection)) + { + string message = + string.Format( + CultureInfo.CurrentCulture, + "Required input {0} is not a recognized {1}.", + nameof(y), typeof(ChangelogSubSectionCollection)); + + throw new ArgumentException(message, nameof(x)); + } + + if (!(y is ChangelogSubSectionCollection)) + { + string message = + string.Format( + CultureInfo.CurrentCulture, + "Required input {0} is not a recognized {1}.", + nameof(y), typeof(ChangelogSubSectionCollection)); + + throw new ArgumentException(message, nameof(y)); + } + + int result = + this.Compare( + (ChangelogSubSectionCollection)x, + (ChangelogSubSectionCollection)y); + + return result; + } + + public int Compare( + ChangelogSubSectionCollection? x, + ChangelogSubSectionCollection? y + ) + { + if (x == null && y != null) { return 1; } + if (x != null && y == null) { return -1; } + + if (x != null && y != null) + { + if (x.Count < y.Count) { return 1; } + if (x.Count > y.Count) { return -1; } + + for (int i = 0; i < x.Count; i++) + { + int result = + this.compareChangelogSubSectionCollection( + x[i], + y[i]); + + if (result != 0) { return result; } + } + } + + return 0; + } + + private int compareChangelogSubSectionCollection( + ChangelogSubSection x, + ChangelogSubSection y + ) + { + return this.changelogSubSectionComparer.Compare(x, y); + } + + } + +} diff --git a/KeepAChangelogParser.Tests/Comparer/ChangelogSubSectionComparer.cs b/KeepAChangelogParser.Tests/Comparer/ChangelogSubSectionComparer.cs new file mode 100644 index 0000000..bd442a4 --- /dev/null +++ b/KeepAChangelogParser.Tests/Comparer/ChangelogSubSectionComparer.cs @@ -0,0 +1,95 @@ +using KeepAChangelogParser.Models; +using System; +using System.Collections.Generic; +using System.Globalization; + +namespace KeepAChangelogParser.Tests.Comparer +{ + + public class ChangelogSubSectionComparer : + IComparer + { + + private readonly IComparer changelogSubSectionItemCollectionComparer; + + public ChangelogSubSectionComparer( + IComparer changelogSubSectionItemCollectionComparer + ) + { + this.changelogSubSectionItemCollectionComparer = changelogSubSectionItemCollectionComparer; + } + + public int Compare( + object? x, + object? y + ) + { + if (!(x is ChangelogSubSection)) + { + string message = + string.Format( + CultureInfo.CurrentCulture, + "Required input {0} is not a recognized {1}.", + nameof(y), typeof(ChangelogSubSection)); + + throw new ArgumentException(message, nameof(x)); + } + + if (!(y is ChangelogSubSection)) + { + string message = + string.Format( + CultureInfo.CurrentCulture, + "Required input {0} is not a recognized {1}.", + nameof(y), typeof(ChangelogSubSection)); + + throw new ArgumentException(message, nameof(y)); + } + + int result = + this.Compare( + (ChangelogSubSection)x, + (ChangelogSubSection)y); + + return result; + } + + public int Compare( + ChangelogSubSection? x, + ChangelogSubSection? y + ) + { + if (x == null && y != null) { return 1; } + if (x != null && y == null) { return -1; } + + if (x != null && y != null) + { + return this.compareChangelogSubSection(x, y); + } + + return 0; + } + + private int compareChangelogSubSection( + ChangelogSubSection x, + ChangelogSubSection y + ) + { + int result; + + if ((result = this.compare(x.ItemCollection, y.ItemCollection)) != 0) { return result; } + + return result; + } + + private int compare( + ChangelogSubSectionItemCollection x, + ChangelogSubSectionItemCollection y + ) + { + return this.changelogSubSectionItemCollectionComparer.Compare(x, y); + } + + } + +} diff --git a/KeepAChangelogParser.Tests/Comparer/ChangelogSubSectionItemCollectionComparer.cs b/KeepAChangelogParser.Tests/Comparer/ChangelogSubSectionItemCollectionComparer.cs new file mode 100644 index 0000000..983bd40 --- /dev/null +++ b/KeepAChangelogParser.Tests/Comparer/ChangelogSubSectionItemCollectionComparer.cs @@ -0,0 +1,96 @@ +using KeepAChangelogParser.Models; +using System; +using System.Collections; +using System.Collections.Generic; +using System.Globalization; + +namespace KeepAChangelogParser.Tests.Comparer +{ + + public class ChangelogSubSectionItemCollectionComparer : + IComparer, + IComparer + { + + private readonly IComparer changelogSubSectionItemComparer; + + public ChangelogSubSectionItemCollectionComparer( + IComparer changelogSubSectionItemComparer + ) + { + this.changelogSubSectionItemComparer = changelogSubSectionItemComparer; + } + + public int Compare( + object? x, + object? y + ) + { + if (!(x is ChangelogSubSectionItemCollection)) + { + string message = + string.Format( + CultureInfo.CurrentCulture, + "Required input {0} is not a recognized {1}.", + nameof(y), typeof(ChangelogSubSectionItemCollection)); + + throw new ArgumentException(message, nameof(x)); + } + + if (!(y is ChangelogSubSectionItemCollection)) + { + string message = + string.Format( + CultureInfo.CurrentCulture, + "Required input {0} is not a recognized {1}.", + nameof(y), typeof(ChangelogSubSectionItemCollection)); + + throw new ArgumentException(message, nameof(y)); + } + + int result = + this.Compare( + (ChangelogSubSectionItemCollection)x, + (ChangelogSubSectionItemCollection)y); + + return result; + } + + public int Compare( + ChangelogSubSectionItemCollection? x, + ChangelogSubSectionItemCollection? y + ) + { + if (x == null && y != null) { return 1; } + if (x != null && y == null) { return -1; } + + if (x != null && y != null) + { + if (x.Count < y.Count) { return 1; } + if (x.Count > y.Count) { return -1; } + + for (int i = 0; i < x.Count; i++) + { + int result = + this.compareChangelogSubSectionItemCollection( + x[i], + y[i]); + + if (result != 0) { return result; } + } + } + + return 0; + } + + private int compareChangelogSubSectionItemCollection( + ChangelogSubSectionItem x, + ChangelogSubSectionItem y + ) + { + return this.changelogSubSectionItemComparer.Compare(x, y); + } + + } + +} diff --git a/KeepAChangelogParser.Tests/Comparer/ChangelogSubSectionItemComparer.cs b/KeepAChangelogParser.Tests/Comparer/ChangelogSubSectionItemComparer.cs new file mode 100644 index 0000000..4405a29 --- /dev/null +++ b/KeepAChangelogParser.Tests/Comparer/ChangelogSubSectionItemComparer.cs @@ -0,0 +1,88 @@ +using KeepAChangelogParser.Models; +using System; +using System.Collections; +using System.Collections.Generic; +using System.Globalization; + +namespace KeepAChangelogParser.Tests.Comparer +{ + + public class ChangelogSubSectionItemComparer : + IComparer, + IComparer + { + + public int Compare( + object? x, + object? y + ) + { + if (!(x is ChangelogSubSectionItem)) + { + string message = + string.Format( + CultureInfo.CurrentCulture, + "Required input {0} is not a recognized {1}.", + nameof(y), typeof(ChangelogSubSectionItem)); + + throw new ArgumentException(message, nameof(x)); + } + + if (!(y is ChangelogSubSectionItem)) + { + string message = + string.Format( + CultureInfo.CurrentCulture, + "Required input {0} is not a recognized {1}.", + nameof(y), typeof(ChangelogSubSectionItem)); + + throw new ArgumentException(message, nameof(y)); + } + + int result = + this.Compare( + (ChangelogSubSectionItem)x, + (ChangelogSubSectionItem)y); + + return result; + } + + public int Compare( + ChangelogSubSectionItem? x, + ChangelogSubSectionItem? y + ) + { + if (x == null && y != null) { return 1; } + if (x != null && y == null) { return -1; } + + if (x != null && y != null) + { + return compareChangelogSubSectionItem(x, y); + } + + return 0; + } + + private static int compareChangelogSubSectionItem( + ChangelogSubSectionItem x, + ChangelogSubSectionItem y + ) + { + int result; + + if ((result = compare(x.MarkdownText, y.MarkdownText)) != 0) { return result; } + + return result; + } + + private static int compare( + string x, + string y + ) + { + return string.Compare(x, y, StringComparison.Ordinal); + } + + } + +} diff --git a/KeepAChangelogParser.Tests/Comparer/ResultComparer.cs b/KeepAChangelogParser.Tests/Comparer/ResultComparer.cs new file mode 100644 index 0000000..71a25a1 --- /dev/null +++ b/KeepAChangelogParser.Tests/Comparer/ResultComparer.cs @@ -0,0 +1,100 @@ +using CSharpFunctionalExtensions; +using System; +using System.Collections; +using System.Collections.Generic; +using System.Globalization; + +namespace KeepAChangelogParser.Tests.Comparer +{ + + public class ResultComparer : + IComparer, + IComparer> + { + + private readonly IComparer comparer; + + public ResultComparer( + IComparer comparer + ) + { + this.comparer = comparer; + } + + public int Compare( + object? x, + object? y + ) + { + if (!(x is Result)) + { + string message = + string.Format( + CultureInfo.CurrentCulture, + "Required input {0} is not a recognized {1}.", + nameof(y), typeof(Result)); + + throw new ArgumentException(message, nameof(x)); + } + + if (!(y is Result)) + { + string message = + string.Format( + CultureInfo.CurrentCulture, + "Required input {0} is not a recognized {1}.", + nameof(y), typeof(Result)); + + throw new ArgumentException(message, nameof(y)); + } + + int result = + this.Compare( + (Result)x, + (Result)y); + + return result; + } + + public int Compare( + Result x, + Result y + ) + { + int result; + + if ((result = compare(x.IsSuccess, y.IsSuccess)) != 0) { return result; } + + if (x.IsSuccess) + { + if ((result = this.comparer.Compare(x.Value, y.Value)) != 0) { return result; } + } + + if (x.IsFailure) + { + if ((result = compare(x.Error, y.Error)) != 0) { return result; } + } + + return result; + } + + private static int compare( + string x, + string y + ) + { + return string.Compare(x, y, StringComparison.Ordinal); + } + + private static int compare( + bool x, + bool y + ) + { + if (x != y) { return 1; } + + return 0; + } + } + +} \ No newline at end of file diff --git a/KeepAChangelogParser.Tests/Extensions/AssertExtensions.cs b/KeepAChangelogParser.Tests/Extensions/AssertExtensions.cs new file mode 100644 index 0000000..d77018e --- /dev/null +++ b/KeepAChangelogParser.Tests/Extensions/AssertExtensions.cs @@ -0,0 +1,239 @@ +using Ardalis.GuardClauses; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using Newtonsoft.Json; +using System; +using System.Collections; +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +using System.IO; +using System.Linq; +using System.Text; + +namespace KeepAChangelogParser.Tests.Extensions +{ + + public static class AssertExtensions + { + [SuppressMessage( + "Style", + "IDE0060:Nicht verwendete Parameter entfernen", + Justification = "Suppress message for necessary assert extension parameter.")] + public static void AreEqual( + this Assert assert, + T expected, + T actual, + IComparer comparer + ) + { + Guard.Against.Null(comparer, nameof(comparer)); + + int compareResult = + comparer.Compare( + expected, + actual); + + if (compareResult == 0) + { + return; + } + + List expectedJsonStringCollection = serializeToJson(expected); + List actualJsonStringCollection = serializeToJson(actual); + + int expectedMaxStringLength = + expectedJsonStringCollection. + Max(x => x.Length); + + int actualMaxStringLength = + actualJsonStringCollection. + Max(x => x.Length); + + int chunkSize = 80; + + bool isCutLength = + expectedMaxStringLength > chunkSize + || actualMaxStringLength > chunkSize; + + if (isCutLength) + { + expectedMaxStringLength = + expectedMaxStringLength > chunkSize ? + chunkSize : + expectedMaxStringLength; + + actualMaxStringLength = + actualMaxStringLength > chunkSize ? + chunkSize : + actualMaxStringLength; + + List? expectedJsonStringCollectionToCut = + expectedJsonStringCollection. + Select((x, index) => new CutElement() { Element = x, Index = index }). + Where(x => x.Element.Length > chunkSize). + ToList(); + + for (int index1 = 0; index1 < expectedJsonStringCollectionToCut.Count; index1++) + { + CutElement? x = expectedJsonStringCollectionToCut[index1]; + + IEnumerable lines = + Enumerable. + Range(0, (x.Element.Length / chunkSize) + 1). + Select(i => x.Element. + Substring( + i * chunkSize, + x.Element.Length - (chunkSize * i) >= chunkSize ? chunkSize : x.Element.Length - (chunkSize * i))); + + int indexToAdd = + lines.Count(); + + for (int index2 = index1 + 1; index2 < expectedJsonStringCollectionToCut.Count; index2++) + { + expectedJsonStringCollectionToCut[index2].Index += indexToAdd - 1; + } + + expectedJsonStringCollection. + RemoveAt(x.Index); + + expectedJsonStringCollection. + InsertRange(x.Index, lines); + + if (x.Index < actualJsonStringCollection.Count) + { + Enumerable. + Range(0, lines.Count() - 1). + ToList(). + ForEach(y => actualJsonStringCollection.Insert(x.Index + 1, "")); + } + } + + var actualJsonStringCollectionToCut = + actualJsonStringCollection. + Select((x, index) => new { Element = x, Index = index }). + Where(x => x.Element.Length > chunkSize). + ToList(); + + foreach (var x in actualJsonStringCollectionToCut) + { + List lines = + Enumerable. + Range(0, (x.Element.Length / chunkSize) + 1). + Select(i => x.Element. + Substring( + i * chunkSize, + x.Element.Length - (chunkSize * i) >= chunkSize ? chunkSize : x.Element.Length - (chunkSize * i))). + ToList(); + + actualJsonStringCollection. + RemoveAt(x.Index); + + actualJsonStringCollection. + Insert(x.Index, lines[0]); + + for (int i = 1; i < lines.Count; i++) + { + if (string.IsNullOrEmpty(actualJsonStringCollection[x.Index + i])) + { + actualJsonStringCollection. + RemoveAt(x.Index + i); + } + else + { + if (x.Index < expectedJsonStringCollection.Count) + { + expectedJsonStringCollection. + Insert(x.Index + i, ""); + } + } + + actualJsonStringCollection. + Insert(x.Index + i, lines[i]); + } + } + } + + int maxJsonTringCollectionCount = + expectedJsonStringCollection.Count > actualJsonStringCollection.Count ? + expectedJsonStringCollection.Count : + actualJsonStringCollection.Count; + + string message = Environment.NewLine; + + string expectedHeader = + "Expected". + PadRight(expectedMaxStringLength, ' '); + + string expectedLine = + "--------". + PadRight(expectedMaxStringLength, '-'); + + string actualLine = + "------". + PadRight(actualMaxStringLength, '-'); + + message += $" | {expectedHeader} | Actual{Environment.NewLine}"; + message += $"----+-{expectedLine}-+-{actualLine}-{Environment.NewLine}"; + + for (int index = 0; index < maxJsonTringCollectionCount; index++) + { + string expectedJsonStringLine = + index < expectedJsonStringCollection.Count ? + expectedJsonStringCollection[index] : + string.Empty; + + string actualJsonStringLine = + index < actualJsonStringCollection.Count ? + actualJsonStringCollection[index] : + string.Empty; + + bool isDifferent = + !string.Equals( + expectedJsonStringLine, + actualJsonStringLine, + StringComparison.Ordinal); + + message += + isDifferent ? + $"--> | {expectedJsonStringLine.PadRight(expectedMaxStringLength, ' ')} | {actualJsonStringLine}{Environment.NewLine}" : + $" | {expectedJsonStringLine.PadRight(expectedMaxStringLength, ' ')} | {actualJsonStringLine}{Environment.NewLine}"; + } + + Assert.IsTrue(false, message); + } + + private static List serializeToJson( + T t + ) + { + JsonSerializer jsonSerializer = JsonSerializer.Create( + new JsonSerializerSettings() + { + Formatting = Formatting.Indented + }); + + StringBuilder stringBuilder = new StringBuilder(); + + using StringWriter stringWriter = new StringWriter(stringBuilder); + + using JsonTextWriter jsonTextWriter = new JsonTextWriter(stringWriter); + + jsonSerializer.Serialize(jsonTextWriter, t); + + List JsonStringCollection = + stringBuilder. + ToString(). + Split(Environment.NewLine). + ToList(); + + return JsonStringCollection; + } + + private class CutElement + { + public string Element { get; set; } = string.Empty; + public int Index { get; set; } = 0; + } + + } + +} diff --git a/KeepAChangelogParser.Tests/KeepAChangelogParser.Tests.csproj b/KeepAChangelogParser.Tests/KeepAChangelogParser.Tests.csproj new file mode 100644 index 0000000..edf80a1 --- /dev/null +++ b/KeepAChangelogParser.Tests/KeepAChangelogParser.Tests.csproj @@ -0,0 +1,42 @@ + + + + net5.0 + enable + Library + + + + DEBUG;TRACE + + + + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + + + + + + + + + + + + + + + + + + + diff --git a/KeepAChangelogParser.Tests/Properties/.gitkeep b/KeepAChangelogParser.Tests/Properties/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/KeepAChangelogParser.Tests/Services/ChangelogResultComparerFactory.cs b/KeepAChangelogParser.Tests/Services/ChangelogResultComparerFactory.cs new file mode 100644 index 0000000..66cdedf --- /dev/null +++ b/KeepAChangelogParser.Tests/Services/ChangelogResultComparerFactory.cs @@ -0,0 +1,29 @@ +using KeepAChangelogParser.Models; +using KeepAChangelogParser.Tests.Comparer; + +namespace KeepAChangelogParser.Tests.Services +{ + + public static class ChangelogResultComparerFactory + { + + public static ResultComparer Create() + { + ChangelogSubSectionCollectionComparer changelogSubSectionCollectionComparer = + new ChangelogSubSectionCollectionComparer( + new ChangelogSubSectionComparer( + new ChangelogSubSectionItemCollectionComparer( + new ChangelogSubSectionItemComparer()))); + + return new ResultComparer( + new ChangelogComparer( + new ChangelogSectionCollectionComparer( + new ChangelogSectionComparer( + changelogSubSectionCollectionComparer)), + new ChangelogSectionUnreleasedComparer( + changelogSubSectionCollectionComparer))); + } + + } + +} diff --git a/KeepAChangelogParser.Tests/TheChangelogContainsADescription.cs b/KeepAChangelogParser.Tests/TheChangelogContainsADescription.cs new file mode 100644 index 0000000..aa93c57 --- /dev/null +++ b/KeepAChangelogParser.Tests/TheChangelogContainsADescription.cs @@ -0,0 +1,72 @@ +using CSharpFunctionalExtensions; +using KeepAChangelogParser.Models; +using KeepAChangelogParser.Tests.Extensions; +using KeepAChangelogParser.Tests.Services; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using System; + +namespace KeepAChangelogParser.Tests +{ + + [TestClass] + public class TheChangelogContainsADescription + { + + [TestMethod] + public void WhenTheChangelogContainsADescription_ThenTheResultIsCorrect() + { + // Arrange + string textToParse = + createTextToParse(); + + IChangelogParser changelogParser = + new ChangelogParser(); + + // Act + Result actualParseResult = + changelogParser.Parse( + textToParse); + + // Assert + Result expectedParseResult = + createExpectedParseResult(); + + Assert.That.AreEqual( + actualParseResult, + expectedParseResult, + ChangelogResultComparerFactory.Create()); + } + + private static string createTextToParse() + { + return + "# Changelog" + Environment.NewLine + + "" + Environment.NewLine + + "All notable changes to this project will be documented in this file." + Environment.NewLine + + "" + Environment.NewLine + + "The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/)," + Environment.NewLine + + "and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html)." + Environment.NewLine + + "" + Environment.NewLine; + } + + private static Result createExpectedParseResult() + { + Result expectedParseResult = + Result.Success( + new Changelog() + { + MarkdownTitle = + "Changelog", + MarkdownText = + "All notable changes to this project will be documented in this file." + Environment.NewLine + + "" + Environment.NewLine + + "The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/)," + Environment.NewLine + + "and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).", + }); + + return expectedParseResult; + } + + } + +} diff --git a/KeepAChangelogParser.Tests/TheChangelogContainsADescriptionAnUnreleasedSectionAndOneSubSectionWithAllTypes.cs b/KeepAChangelogParser.Tests/TheChangelogContainsADescriptionAnUnreleasedSectionAndOneSubSectionWithAllTypes.cs new file mode 100644 index 0000000..a93899b --- /dev/null +++ b/KeepAChangelogParser.Tests/TheChangelogContainsADescriptionAnUnreleasedSectionAndOneSubSectionWithAllTypes.cs @@ -0,0 +1,283 @@ +using CSharpFunctionalExtensions; +using KeepAChangelogParser.Models; +using KeepAChangelogParser.Tests.Extensions; +using KeepAChangelogParser.Tests.Services; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using System; +using System.Collections.Generic; + +namespace KeepAChangelogParser.Tests +{ + + [TestClass] + public class TheChangelogContainsADescriptionAnUnreleasedSectionAndOneSubSectionWithAllTypes + { + + [TestMethod] + public void WhenTheChangelogContainsADescriptionAnUnreleasedSectionAndOneSubSectionWithAllTypes_ThenTheResultIsCorrect() + { + // Arrange + string textToParse = + createTextToParse(); + + IChangelogParser changelogParser = + new ChangelogParser(); + + // Act + Result actualParseResult = + changelogParser.Parse( + textToParse); + + // Assert + Result expectedParseResult = + createExpectedParseResult(); + + Assert.That.AreEqual( + expectedParseResult, + actualParseResult, + ChangelogResultComparerFactory.Create()); + } + + private static string createTextToParse() + { + return + "# Changelog" + Environment.NewLine + + "" + Environment.NewLine + + "All notable changes to this project will be documented in this file." + Environment.NewLine + + "" + Environment.NewLine + + "The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/)," + Environment.NewLine + + "and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html)." + Environment.NewLine + + "" + Environment.NewLine + + "## [Unreleased]" + Environment.NewLine + + "" + Environment.NewLine + + "### Added" + Environment.NewLine + + "- New visual identity by[@tylerfortune8](https://github.com/tylerfortune8)." + Environment.NewLine + + "- Version navigation." + Environment.NewLine + + "" + Environment.NewLine + + "## [1.0.0] - 2017-06-20" + Environment.NewLine + + "" + Environment.NewLine + + "### Added" + Environment.NewLine + + "- New visual identity by[@tylerfortune8](https://github.com/tylerfortune8)." + Environment.NewLine + + "- Version navigation." + Environment.NewLine + + "" + Environment.NewLine + + "### Changed" + Environment.NewLine + + "- New visual identity by[@tylerfortune8](https://github.com/tylerfortune8)." + Environment.NewLine + + "- Version navigation." + Environment.NewLine + + "" + Environment.NewLine + + "### Deprecated" + Environment.NewLine + + "- New visual identity by[@tylerfortune8](https://github.com/tylerfortune8)." + Environment.NewLine + + "- Version navigation." + Environment.NewLine + + "" + Environment.NewLine + + "### Removed" + Environment.NewLine + + "- New visual identity by[@tylerfortune8](https://github.com/tylerfortune8)." + Environment.NewLine + + "- Version navigation." + Environment.NewLine + + "" + Environment.NewLine + + "### Fixed" + Environment.NewLine + + "- New visual identity by[@tylerfortune8](https://github.com/tylerfortune8)." + Environment.NewLine + + "- Version navigation." + Environment.NewLine + + "" + Environment.NewLine + + "### Security" + Environment.NewLine + + "- New visual identity by[@tylerfortune8](https://github.com/tylerfortune8)." + Environment.NewLine + + "- Version navigation." + + "" + Environment.NewLine; + } + + private static Result createExpectedParseResult() + { + Result expectedParseResult = + Result.Success( + new Changelog() + { + MarkdownTitle = + "Changelog", + MarkdownText = + "All notable changes to this project will be documented in this file." + Environment.NewLine + + "" + Environment.NewLine + + "The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/)," + Environment.NewLine + + "and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).", + }); + + expectedParseResult.Value. + SectionUnreleased. + MarkdownTitle = "Unreleased"; + + expectedParseResult.Value. + SectionUnreleased. + SubSectionCollection. + AddRange( + new List() + { + new ChangelogSubSection() + { + Type = ChangelogSubSectionType.Added, + }, + }); + + expectedParseResult.Value. + SectionUnreleased. + SubSectionCollection[0]. + ItemCollection. + AddRange( + new List() + { + new ChangelogSubSectionItem() + { + MarkdownText = "New visual identity by[@tylerfortune8](https://github.com/tylerfortune8)." + }, + new ChangelogSubSectionItem() + { + MarkdownText = "Version navigation." + }, + }); + + expectedParseResult.Value. + SectionCollection. + AddRange( + new List() + { + new ChangelogSection() + { + MarkdownVersion = "1.0.0", + MarkdownDate = "2017-06-20" + }, + }); + + expectedParseResult.Value. + SectionCollection[0]. + SubSectionCollection. + AddRange( + new List() + { + new ChangelogSubSection() + { + Type = ChangelogSubSectionType.Added, + }, + new ChangelogSubSection() + { + Type = ChangelogSubSectionType.Changed, + }, + new ChangelogSubSection() + { + Type = ChangelogSubSectionType.Deprecated, + }, + new ChangelogSubSection() + { + Type = ChangelogSubSectionType.Removed, + }, + new ChangelogSubSection() + { + Type = ChangelogSubSectionType.Fixed, + }, + new ChangelogSubSection() + { + Type = ChangelogSubSectionType.Security, + }, + }); + + expectedParseResult.Value. + SectionCollection[0]. + SubSectionCollection[0]. + ItemCollection. + AddRange( + new List() + { + new ChangelogSubSectionItem() + { + MarkdownText = "New visual identity by[@tylerfortune8](https://github.com/tylerfortune8)." + }, + new ChangelogSubSectionItem() + { + MarkdownText = "Version navigation." + }, + }); + + expectedParseResult.Value. + SectionCollection[0]. + SubSectionCollection[1]. + ItemCollection. + AddRange( + new List() + { + new ChangelogSubSectionItem() + { + MarkdownText = "New visual identity by[@tylerfortune8](https://github.com/tylerfortune8)." + }, + new ChangelogSubSectionItem() + { + MarkdownText = "Version navigation." + }, + }); + + expectedParseResult.Value. + SectionCollection[0]. + SubSectionCollection[2]. + ItemCollection. + AddRange( + new List() + { + new ChangelogSubSectionItem() + { + MarkdownText = "New visual identity by[@tylerfortune8](https://github.com/tylerfortune8)." + }, + new ChangelogSubSectionItem() + { + MarkdownText = "Version navigation." + }, + }); + + expectedParseResult.Value. + SectionCollection[0]. + SubSectionCollection[3]. + ItemCollection. + AddRange( + new List() + { + new ChangelogSubSectionItem() + { + MarkdownText = "New visual identity by[@tylerfortune8](https://github.com/tylerfortune8)." + }, + new ChangelogSubSectionItem() + { + MarkdownText = "Version navigation." + }, + }); + + expectedParseResult.Value. + SectionCollection[0]. + SubSectionCollection[4]. + ItemCollection. + AddRange( + new List() + { + new ChangelogSubSectionItem() + { + MarkdownText = "New visual identity by[@tylerfortune8](https://github.com/tylerfortune8)." + }, + new ChangelogSubSectionItem() + { + MarkdownText = "Version navigation." + }, + }); + + expectedParseResult.Value. + SectionCollection[0]. + SubSectionCollection[5]. + ItemCollection. + AddRange( + new List() + { + new ChangelogSubSectionItem() + { + MarkdownText = "New visual identity by[@tylerfortune8](https://github.com/tylerfortune8)." + }, + new ChangelogSubSectionItem() + { + MarkdownText = "Version navigation." + }, + }); + return expectedParseResult; + } + + } + +} diff --git a/KeepAChangelogParser.Tests/TheChangelogContainsADescriptionAnUnreleasedSectionAndOneSubSectionWithInvalidType.cs b/KeepAChangelogParser.Tests/TheChangelogContainsADescriptionAnUnreleasedSectionAndOneSubSectionWithInvalidType.cs new file mode 100644 index 0000000..636f846 --- /dev/null +++ b/KeepAChangelogParser.Tests/TheChangelogContainsADescriptionAnUnreleasedSectionAndOneSubSectionWithInvalidType.cs @@ -0,0 +1,75 @@ +using CSharpFunctionalExtensions; +using KeepAChangelogParser.Models; +using KeepAChangelogParser.Tests.Extensions; +using KeepAChangelogParser.Tests.Services; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using System; + +namespace KeepAChangelogParser.Tests +{ + + [TestClass] + public class TheChangelogContainsADescriptionAnUnreleasedSectionAndOneSubSectionWithInvalidType + { + + [TestMethod] + public void WhenTheChangelogContainsADescriptionAnUnreleasedSectionAndOneSubSectionWithInvalidType_ThenTheResultIsFailureInvalidSubsectionType() + { + // Arrange + string textToParse = + createTextToParse(); + + IChangelogParser changelogParser = + new ChangelogParser(); + + // Act + Result actualParseResult = + changelogParser.Parse( + textToParse); + + // Assert + Result expectedParseResult = + createExpectedParseResult(); + + Assert.That.AreEqual( + expectedParseResult, + actualParseResult, + ChangelogResultComparerFactory.Create()); + } + + private static string createTextToParse() + { + return + /* 01 */ "# Changelog" + Environment.NewLine + + /* 02 */ "" + Environment.NewLine + + /* 03 */ "All notable changes to this project will be documented in this file." + Environment.NewLine + + /* 04 */ "" + Environment.NewLine + + /* 05 */ "The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/)," + Environment.NewLine + + /* 06 */ "and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html)." + Environment.NewLine + + /* 07 */ "" + Environment.NewLine + + /* 08 */ "## [Unreleased]" + Environment.NewLine + + /* 09 */ "" + Environment.NewLine + + /* 10 */ "### Added" + Environment.NewLine + + /* 11 */ "- New visual identity by[@tylerfortune8](https://github.com/tylerfortune8)." + Environment.NewLine + + /* 12 */ "- Version navigation." + Environment.NewLine + + /* 13 */ "" + Environment.NewLine + + /* 14 */ "## [1.0.0] - 2017-06-20" + Environment.NewLine + + /* 15 */ "" + Environment.NewLine + + /* 16 */ "### Add" + Environment.NewLine + + /* 17 */ "- New visual identity by[@tylerfortune8](https://github.com/tylerfortune8)." + Environment.NewLine + + /* 18 */ "- Version navigation." + Environment.NewLine + + /* 19 */ "" + Environment.NewLine; + } + + private static Result createExpectedParseResult() + { + Result expectedParseResult = + Result.Failure( + "Invalid subsection type. Error parsing text in line 16 / index 5."); + + return expectedParseResult; + } + + } + +} diff --git a/KeepAChangelogParser.Tests/TheChangelogContainsADescriptionAnUnreleasedSectionAndOneSubSectionWithInvalidVersion.cs b/KeepAChangelogParser.Tests/TheChangelogContainsADescriptionAnUnreleasedSectionAndOneSubSectionWithInvalidVersion.cs new file mode 100644 index 0000000..952ff69 --- /dev/null +++ b/KeepAChangelogParser.Tests/TheChangelogContainsADescriptionAnUnreleasedSectionAndOneSubSectionWithInvalidVersion.cs @@ -0,0 +1,75 @@ +using CSharpFunctionalExtensions; +using KeepAChangelogParser.Models; +using KeepAChangelogParser.Tests.Extensions; +using KeepAChangelogParser.Tests.Services; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using System; + +namespace KeepAChangelogParser.Tests +{ + + [TestClass] + public class TheChangelogContainsADescriptionAnUnreleasedSectionAndOneSubSectionWithInvalidVersion + { + + [TestMethod] + public void WhenTheChangelogContainsADescriptionAnUnreleasedSectionAndOneSubSectionWithInvalidVersion_ThenTheResultIsFailureInvalidVersion() + { + // Arrange + string textToParse = + createTextToParse(); + + IChangelogParser changelogParser = + new ChangelogParser(); + + // Act + Result actualParseResult = + changelogParser.Parse( + textToParse); + + // Assert + Result expectedParseResult = + createExpectedParseResult(); + + Assert.That.AreEqual( + expectedParseResult, + actualParseResult, + ChangelogResultComparerFactory.Create()); + } + + private static string createTextToParse() + { + return + /* 01 */ "# Changelog" + Environment.NewLine + + /* 02 */ "" + Environment.NewLine + + /* 03 */ "All notable changes to this project will be documented in this file." + Environment.NewLine + + /* 04 */ "" + Environment.NewLine + + /* 05 */ "The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/)," + Environment.NewLine + + /* 06 */ "and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html)." + Environment.NewLine + + /* 07 */ "" + Environment.NewLine + + /* 08 */ "## [Unreleased]" + Environment.NewLine + + /* 09 */ "" + Environment.NewLine + + /* 10 */ "### Added" + Environment.NewLine + + /* 11 */ "- New visual identity by[@tylerfortune8](https://github.com/tylerfortune8)." + Environment.NewLine + + /* 12 */ "- Version navigation." + Environment.NewLine + + /* 13 */ "" + Environment.NewLine + + /* 14 */ "## [1.0] - 2017-06-20" + Environment.NewLine + + /* 15 */ "" + Environment.NewLine + + /* 16 */ "### Added" + Environment.NewLine + + /* 17 */ "- New visual identity by[@tylerfortune8](https://github.com/tylerfortune8)." + Environment.NewLine + + /* 18 */ "- Version navigation." + Environment.NewLine + + /* 19 */ "" + Environment.NewLine; + } + + private static Result createExpectedParseResult() + { + Result expectedParseResult = + Result.Failure( + "Invalid version. Error parsing text in line 14 / index 5."); + + return expectedParseResult; + } + + } + +} diff --git a/KeepAChangelogParser.Tests/TheChangelogContainsADescriptionAnUnreleasedSectionAndOneSubSectionWithMissingDash.cs b/KeepAChangelogParser.Tests/TheChangelogContainsADescriptionAnUnreleasedSectionAndOneSubSectionWithMissingDash.cs new file mode 100644 index 0000000..e3b732e --- /dev/null +++ b/KeepAChangelogParser.Tests/TheChangelogContainsADescriptionAnUnreleasedSectionAndOneSubSectionWithMissingDash.cs @@ -0,0 +1,75 @@ +using CSharpFunctionalExtensions; +using KeepAChangelogParser.Models; +using KeepAChangelogParser.Tests.Extensions; +using KeepAChangelogParser.Tests.Services; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using System; + +namespace KeepAChangelogParser.Tests +{ + + [TestClass] + public class TheChangelogContainsADescriptionAnUnreleasedSectionAndOneSubSectionWithMissingDash + { + + [TestMethod] + public void WhenTheChangelogContainsADescriptionAnUnreleasedSectionAndOneSubSectionWithMissingDash_ThenTheResultIsFailureNoDash() + { + // Arrange + string textToParse = + createTextToParse(); + + IChangelogParser changelogParser = + new ChangelogParser(); + + // Act + Result actualParseResult = + changelogParser.Parse( + textToParse); + + // Assert + Result expectedParseResult = + createExpectedParseResult(); + + Assert.That.AreEqual( + expectedParseResult, + actualParseResult, + ChangelogResultComparerFactory.Create()); + } + + private static string createTextToParse() + { + return + /* 01 */ "# Changelog" + Environment.NewLine + + /* 02 */ "" + Environment.NewLine + + /* 03 */ "All notable changes to this project will be documented in this file." + Environment.NewLine + + /* 04 */ "" + Environment.NewLine + + /* 05 */ "The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/)," + Environment.NewLine + + /* 06 */ "and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html)." + Environment.NewLine + + /* 07 */ "" + Environment.NewLine + + /* 08 */ "## [Unreleased]" + Environment.NewLine + + /* 09 */ "" + Environment.NewLine + + /* 10 */ "### Added" + Environment.NewLine + + /* 11 */ "- New visual identity by[@tylerfortune8](https://github.com/tylerfortune8)." + Environment.NewLine + + /* 12 */ "- Version navigation." + Environment.NewLine + + /* 13 */ "" + Environment.NewLine + + /* 14 */ "## [1.0.0] - 2017-06-20" + Environment.NewLine + + /* 15 */ "" + Environment.NewLine + + /* 16 */ "### Added" + Environment.NewLine + + /* 17 */ " New visual identity by[@tylerfortune8](https://github.com/tylerfortune8)." + Environment.NewLine + + /* 18 */ "- Version navigation." + Environment.NewLine + + /* 19 */ "" + Environment.NewLine; + } + + private static Result createExpectedParseResult() + { + Result expectedParseResult = + Result.Failure( + "No dash. Error parsing text in line 17 / index 1."); + + return expectedParseResult; + } + + } + +} diff --git a/KeepAChangelogParser.Tests/TheChangelogContainsADescriptionAnUnreleasedSectionAndOneSubSectionWithMissingDate.cs b/KeepAChangelogParser.Tests/TheChangelogContainsADescriptionAnUnreleasedSectionAndOneSubSectionWithMissingDate.cs new file mode 100644 index 0000000..b1ffb4f --- /dev/null +++ b/KeepAChangelogParser.Tests/TheChangelogContainsADescriptionAnUnreleasedSectionAndOneSubSectionWithMissingDate.cs @@ -0,0 +1,75 @@ +using CSharpFunctionalExtensions; +using KeepAChangelogParser.Models; +using KeepAChangelogParser.Tests.Extensions; +using KeepAChangelogParser.Tests.Services; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using System; + +namespace KeepAChangelogParser.Tests +{ + + [TestClass] + public class TheChangelogContainsADescriptionAnUnreleasedSectionAndOneSubSectionWithMissingDate + { + + [TestMethod] + public void WhenTheChangelogContainsADescriptionAnUnreleasedSectionAndOneSubSectionWithMissingDate_ThenTheResultIsFailureNodate() + { + // Arrange + string textToParse = + createTextToParse(); + + IChangelogParser changelogParser = + new ChangelogParser(); + + // Act + Result actualParseResult = + changelogParser.Parse( + textToParse); + + // Assert + Result expectedParseResult = + createExpectedParseResult(); + + Assert.That.AreEqual( + expectedParseResult, + actualParseResult, + ChangelogResultComparerFactory.Create()); + } + + private static string createTextToParse() + { + return + /* 01 */ "# Changelog" + Environment.NewLine + + /* 02 */ "" + Environment.NewLine + + /* 03 */ "All notable changes to this project will be documented in this file." + Environment.NewLine + + /* 04 */ "" + Environment.NewLine + + /* 05 */ "The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/)," + Environment.NewLine + + /* 06 */ "and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html)." + Environment.NewLine + + /* 07 */ "" + Environment.NewLine + + /* 08 */ "## [Unreleased]" + Environment.NewLine + + /* 09 */ "" + Environment.NewLine + + /* 10 */ "### Added" + Environment.NewLine + + /* 11 */ "- New visual identity by[@tylerfortune8](https://github.com/tylerfortune8)." + Environment.NewLine + + /* 12 */ "- Version navigation." + Environment.NewLine + + /* 13 */ "" + Environment.NewLine + + /* 14 */ "## [1.0.0] - " + Environment.NewLine + + /* 15 */ "" + Environment.NewLine + + /* 16 */ "### Added" + Environment.NewLine + + /* 17 */ "- New visual identity by[@tylerfortune8](https://github.com/tylerfortune8)." + Environment.NewLine + + /* 18 */ "- Version navigation." + Environment.NewLine + + /* 19 */ "" + Environment.NewLine; + } + + private static Result createExpectedParseResult() + { + Result expectedParseResult = + Result.Failure( + "No date. Error parsing text in line 15 / index 0."); + + return expectedParseResult; + } + + } + +} diff --git a/KeepAChangelogParser.Tests/TheChangelogContainsADescriptionAnUnreleasedSectionAndOneSubSectionWithMissingSpace.cs b/KeepAChangelogParser.Tests/TheChangelogContainsADescriptionAnUnreleasedSectionAndOneSubSectionWithMissingSpace.cs new file mode 100644 index 0000000..f50827c --- /dev/null +++ b/KeepAChangelogParser.Tests/TheChangelogContainsADescriptionAnUnreleasedSectionAndOneSubSectionWithMissingSpace.cs @@ -0,0 +1,75 @@ +using CSharpFunctionalExtensions; +using KeepAChangelogParser.Models; +using KeepAChangelogParser.Tests.Extensions; +using KeepAChangelogParser.Tests.Services; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using System; + +namespace KeepAChangelogParser.Tests +{ + + [TestClass] + public class TheChangelogContainsADescriptionAnUnreleasedSectionAndOneSubSectionWithMissingSpace + { + + [TestMethod] + public void WhenTheChangelogContainsADescriptionAnUnreleasedSectionAndOneSubSectionWithMissingSpace_ThenTheResultIsFailureNoSpace() + { + // Arrange + string textToParse = + createTextToParse(); + + IChangelogParser changelogParser = + new ChangelogParser(); + + // Act + Result actualParseResult = + changelogParser.Parse( + textToParse); + + // Assert + Result expectedParseResult = + createExpectedParseResult(); + + Assert.That.AreEqual( + expectedParseResult, + actualParseResult, + ChangelogResultComparerFactory.Create()); + } + + private static string createTextToParse() + { + return + /* 01 */ "# Changelog" + Environment.NewLine + + /* 02 */ "" + Environment.NewLine + + /* 03 */ "All notable changes to this project will be documented in this file." + Environment.NewLine + + /* 04 */ "" + Environment.NewLine + + /* 05 */ "The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/)," + Environment.NewLine + + /* 06 */ "and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html)." + Environment.NewLine + + /* 07 */ "" + Environment.NewLine + + /* 08 */ "## [Unreleased]" + Environment.NewLine + + /* 09 */ "" + Environment.NewLine + + /* 10 */ "### Added" + Environment.NewLine + + /* 11 */ "- New visual identity by[@tylerfortune8](https://github.com/tylerfortune8)." + Environment.NewLine + + /* 12 */ "- Version navigation." + Environment.NewLine + + /* 13 */ "" + Environment.NewLine + + /* 14 */ "## [1.0.0] - 2017-06-20" + Environment.NewLine + + /* 15 */ "" + Environment.NewLine + + /* 16 */ "### Added" + Environment.NewLine + + /* 17 */ "-New visual identity by[@tylerfortune8](https://github.com/tylerfortune8)." + Environment.NewLine + + /* 18 */ "- Version navigation." + Environment.NewLine + + /* 19 */ "" + Environment.NewLine; + } + + private static Result createExpectedParseResult() + { + Result expectedParseResult = + Result.Failure( + "No space. Error parsing text in line 17 / index 2."); + + return expectedParseResult; + } + + } + +} diff --git a/KeepAChangelogParser.Tests/TheChangelogContainsADescriptionAnUnreleasedSectionAndOneSubSectionWithMissingVersion.cs b/KeepAChangelogParser.Tests/TheChangelogContainsADescriptionAnUnreleasedSectionAndOneSubSectionWithMissingVersion.cs new file mode 100644 index 0000000..2cf7a5e --- /dev/null +++ b/KeepAChangelogParser.Tests/TheChangelogContainsADescriptionAnUnreleasedSectionAndOneSubSectionWithMissingVersion.cs @@ -0,0 +1,75 @@ +using CSharpFunctionalExtensions; +using KeepAChangelogParser.Models; +using KeepAChangelogParser.Tests.Extensions; +using KeepAChangelogParser.Tests.Services; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using System; + +namespace KeepAChangelogParser.Tests +{ + + [TestClass] + public class TheChangelogContainsADescriptionAnUnreleasedSectionAndOneSubSectionWithMissingVersion + { + + [TestMethod] + public void WhenTheChangelogContainsADescriptionAnUnreleasedSectionAndOneSubSectionWithMissingVersion_ThenTheResultIsFailureInvalidVersion() + { + // Arrange + string textToParse = + createTextToParse(); + + IChangelogParser changelogParser = + new ChangelogParser(); + + // Act + Result actualParseResult = + changelogParser.Parse( + textToParse); + + // Assert + Result expectedParseResult = + createExpectedParseResult(); + + Assert.That.AreEqual( + expectedParseResult, + actualParseResult, + ChangelogResultComparerFactory.Create()); + } + + private static string createTextToParse() + { + return + /* 01 */ "# Changelog" + Environment.NewLine + + /* 02 */ "" + Environment.NewLine + + /* 03 */ "All notable changes to this project will be documented in this file." + Environment.NewLine + + /* 04 */ "" + Environment.NewLine + + /* 05 */ "The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/)," + Environment.NewLine + + /* 06 */ "and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html)." + Environment.NewLine + + /* 07 */ "" + Environment.NewLine + + /* 08 */ "## [Unreleased]" + Environment.NewLine + + /* 09 */ "" + Environment.NewLine + + /* 10 */ "### Added" + Environment.NewLine + + /* 11 */ "- New visual identity by[@tylerfortune8](https://github.com/tylerfortune8)." + Environment.NewLine + + /* 12 */ "- Version navigation." + Environment.NewLine + + /* 13 */ "" + Environment.NewLine + + /* 14 */ "## [] - 2017-06-20" + Environment.NewLine + + /* 15 */ "" + Environment.NewLine + + /* 16 */ "### Added" + Environment.NewLine + + /* 17 */ "- New visual identity by[@tylerfortune8](https://github.com/tylerfortune8)." + Environment.NewLine + + /* 18 */ "- Version navigation." + Environment.NewLine + + /* 19 */ "" + Environment.NewLine; + } + + private static Result createExpectedParseResult() + { + Result expectedParseResult = + Result.Failure( + "Invalid version. Error parsing text in line 14 / index 5."); + + return expectedParseResult; + } + + } + +} diff --git a/KeepAChangelogParser.Tests/TheChangelogContainsADescriptionAnUnreleasedSectionAndOneSubSectionWithTwoIdenticalTypes.cs b/KeepAChangelogParser.Tests/TheChangelogContainsADescriptionAnUnreleasedSectionAndOneSubSectionWithTwoIdenticalTypes.cs new file mode 100644 index 0000000..f7aa8ac --- /dev/null +++ b/KeepAChangelogParser.Tests/TheChangelogContainsADescriptionAnUnreleasedSectionAndOneSubSectionWithTwoIdenticalTypes.cs @@ -0,0 +1,79 @@ +using CSharpFunctionalExtensions; +using KeepAChangelogParser.Models; +using KeepAChangelogParser.Tests.Extensions; +using KeepAChangelogParser.Tests.Services; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using System; + +namespace KeepAChangelogParser.Tests +{ + + [TestClass] + public class TheChangelogContainsADescriptionAnUnreleasedSectionAndOneSubSectionWithTwoIdenticalTypes + { + + [TestMethod] + public void WhenTheChangelogContainsADescriptionAnUnreleasedSectionAndOneSubSectionWithTwoIdenticalTypes_ThenTheResultIsFailureSubsectionTypeAlreadyExists() + { + // Arrange + string textToParse = + createTextToParse(); + + IChangelogParser changelogParser = + new ChangelogParser(); + + // Act + Result actualParseResult = + changelogParser.Parse( + textToParse); + + // Assert + Result expectedParseResult = + createExpectedParseResult(); + + Assert.That.AreEqual( + expectedParseResult, + actualParseResult, + ChangelogResultComparerFactory.Create()); + } + + private static string createTextToParse() + { + return + /* 01 */ "# Changelog" + Environment.NewLine + + /* 02 */ "" + Environment.NewLine + + /* 03 */ "All notable changes to this project will be documented in this file." + Environment.NewLine + + /* 04 */ "" + Environment.NewLine + + /* 05 */ "The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/)," + Environment.NewLine + + /* 06 */ "and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html)." + Environment.NewLine + + /* 07 */ "" + Environment.NewLine + + /* 08 */ "## [Unreleased]" + Environment.NewLine + + /* 09 */ "" + Environment.NewLine + + /* 10 */ "### Added" + Environment.NewLine + + /* 11 */ "- New visual identity by[@tylerfortune8](https://github.com/tylerfortune8)." + Environment.NewLine + + /* 12 */ "- Version navigation." + Environment.NewLine + + /* 13 */ "" + Environment.NewLine + + /* 14 */ "## [1.0.0] - 2017-06-20" + Environment.NewLine + + /* 15 */ "" + Environment.NewLine + + /* 16 */ "### Added" + Environment.NewLine + + /* 17 */ "- New visual identity by[@tylerfortune8](https://github.com/tylerfortune8)." + Environment.NewLine + + /* 18 */ "- Version navigation." + Environment.NewLine + + /* 19 */ "" + Environment.NewLine + + /* 20 */ "### Added" + Environment.NewLine + + /* 21 */ "- New visual identity by[@tylerfortune8](https://github.com/tylerfortune8)." + Environment.NewLine + + /* 22 */ "- Version navigation." + Environment.NewLine + + /* 23 */ "" + Environment.NewLine; + } + + private static Result createExpectedParseResult() + { + Result expectedParseResult = + Result.Failure( + "Subsection type already exists. Error parsing text in line 20 / index 5."); + + return expectedParseResult; + } + + } + +} diff --git a/KeepAChangelogParser.Tests/TheChangelogContainsADescriptionAnUnreleasedSectionAndTwoSubSections.cs b/KeepAChangelogParser.Tests/TheChangelogContainsADescriptionAnUnreleasedSectionAndTwoSubSections.cs new file mode 100644 index 0000000..3eb3df7 --- /dev/null +++ b/KeepAChangelogParser.Tests/TheChangelogContainsADescriptionAnUnreleasedSectionAndTwoSubSections.cs @@ -0,0 +1,250 @@ +using CSharpFunctionalExtensions; +using KeepAChangelogParser.Models; +using KeepAChangelogParser.Tests.Extensions; +using KeepAChangelogParser.Tests.Services; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using System; +using System.Collections.Generic; + +namespace KeepAChangelogParser.Tests +{ + + [TestClass] + public class TheChangelogContainsADescriptionAnUnreleasedSectionAndTwoSubSections + { + + [TestMethod] + public void WhenTheChangelogContainsADescriptionAnUnreleasedSectionAndTwoSubSections_ThenTheResultIsCorrect() + { + // Arrange + string textToParse = + createTextToParse(); + + IChangelogParser changelogParser = + new ChangelogParser(); + + // Act + Result actualParseResult = + changelogParser.Parse( + textToParse); + + // Assert + Result expectedParseResult = + createExpectedParseResult(); + + Assert.That.AreEqual( + expectedParseResult, + actualParseResult, + ChangelogResultComparerFactory.Create()); + } + + private static string createTextToParse() + { + return + "# Changelog" + Environment.NewLine + + "" + Environment.NewLine + + "All notable changes to this project will be documented in this file." + Environment.NewLine + + "" + Environment.NewLine + + "The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/)," + Environment.NewLine + + "and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html)." + Environment.NewLine + + "" + Environment.NewLine + + "## [Unreleased]" + Environment.NewLine + + "" + Environment.NewLine + + "### Added" + Environment.NewLine + + "- New visual identity by[@tylerfortune8](https://github.com/tylerfortune8)." + Environment.NewLine + + "- Version navigation." + Environment.NewLine + + "" + Environment.NewLine + + "## [1.1.0] - 2017-07-01" + Environment.NewLine + + "" + Environment.NewLine + + "### Added" + Environment.NewLine + + "- New visual identity by[@tylerfortune8](https://github.com/tylerfortune8)." + Environment.NewLine + + "- Version navigation." + Environment.NewLine + + "" + Environment.NewLine + + "### Changed" + Environment.NewLine + + "- New visual identity by[@tylerfortune8](https://github.com/tylerfortune8)." + Environment.NewLine + + "- Version navigation." + Environment.NewLine + + "" + Environment.NewLine + + "## [1.0.0] - 2017-06-20" + Environment.NewLine + + "" + Environment.NewLine + + "### Added" + Environment.NewLine + + "- New visual identity by[@tylerfortune8](https://github.com/tylerfortune8)." + Environment.NewLine + + "- Version navigation." + Environment.NewLine + + "" + Environment.NewLine + + "### Changed" + Environment.NewLine + + "- New visual identity by[@tylerfortune8](https://github.com/tylerfortune8)." + Environment.NewLine + + "- Version navigation." + Environment.NewLine + + "" + Environment.NewLine; + } + + private static Result createExpectedParseResult() + { + Result expectedParseResult = + Result.Success( + new Changelog() + { + MarkdownTitle = + "Changelog", + MarkdownText = + "All notable changes to this project will be documented in this file." + Environment.NewLine + + "" + Environment.NewLine + + "The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/)," + Environment.NewLine + + "and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).", + }); + + expectedParseResult.Value. + SectionUnreleased. + MarkdownTitle = "Unreleased"; + + expectedParseResult.Value. + SectionUnreleased. + SubSectionCollection. + AddRange( + new List() + { + new ChangelogSubSection() + { + Type = ChangelogSubSectionType.Added, + }, + }); + + expectedParseResult.Value. + SectionUnreleased. + SubSectionCollection[0]. + ItemCollection. + AddRange( + new List() + { + new ChangelogSubSectionItem() + { + MarkdownText = "New visual identity by[@tylerfortune8](https://github.com/tylerfortune8)." + }, + new ChangelogSubSectionItem() + { + MarkdownText = "Version navigation." + }, + }); + + expectedParseResult.Value. + SectionCollection. + AddRange( + new List() + { + new ChangelogSection() + { + MarkdownVersion = "1.1.0", + MarkdownDate = "2017-07-01" + }, + new ChangelogSection() + { + MarkdownVersion = "1.0.0", + MarkdownDate = "2017-06-20" + }, + }); + + expectedParseResult.Value. + SectionCollection[0]. + SubSectionCollection. + AddRange( + new List() + { + new ChangelogSubSection() + { + Type = ChangelogSubSectionType.Added, + }, + new ChangelogSubSection() + { + Type = ChangelogSubSectionType.Changed, + }, + }); + + expectedParseResult.Value. + SectionCollection[0]. + SubSectionCollection[0]. + ItemCollection. + AddRange( + new List() + { + new ChangelogSubSectionItem() + { + MarkdownText = "New visual identity by[@tylerfortune8](https://github.com/tylerfortune8)." + }, + new ChangelogSubSectionItem() + { + MarkdownText = "Version navigation." + }, + }); + + expectedParseResult.Value. + SectionCollection[0]. + SubSectionCollection[1]. + ItemCollection. + AddRange( + new List() + { + new ChangelogSubSectionItem() + { + MarkdownText = "New visual identity by[@tylerfortune8](https://github.com/tylerfortune8)." + }, + new ChangelogSubSectionItem() + { + MarkdownText = "Version navigation." + }, + }); + + + expectedParseResult.Value. + SectionCollection[1]. + SubSectionCollection. + AddRange( + new List() + { + new ChangelogSubSection() + { + Type = ChangelogSubSectionType.Added, + }, + new ChangelogSubSection() + { + Type = ChangelogSubSectionType.Changed, + }, + }); + + expectedParseResult.Value. + SectionCollection[1]. + SubSectionCollection[0]. + ItemCollection. + AddRange( + new List() + { + new ChangelogSubSectionItem() + { + MarkdownText = "New visual identity by[@tylerfortune8](https://github.com/tylerfortune8)." + }, + new ChangelogSubSectionItem() + { + MarkdownText = "Version navigation." + }, + }); + + expectedParseResult.Value. + SectionCollection[1]. + SubSectionCollection[1]. + ItemCollection. + AddRange( + new List() + { + new ChangelogSubSectionItem() + { + MarkdownText = "New visual identity by[@tylerfortune8](https://github.com/tylerfortune8)." + }, + new ChangelogSubSectionItem() + { + MarkdownText = "Version navigation." + }, + }); + + return expectedParseResult; + } + + } + +} diff --git a/KeepAChangelogParser.Tests/TheChangelogContainsADescriptionAndAnUnreleasedSection.cs b/KeepAChangelogParser.Tests/TheChangelogContainsADescriptionAndAnUnreleasedSection.cs new file mode 100644 index 0000000..9c6bbaf --- /dev/null +++ b/KeepAChangelogParser.Tests/TheChangelogContainsADescriptionAndAnUnreleasedSection.cs @@ -0,0 +1,112 @@ +using CSharpFunctionalExtensions; +using KeepAChangelogParser.Models; +using KeepAChangelogParser.Tests.Extensions; +using KeepAChangelogParser.Tests.Services; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using System; +using System.Collections.Generic; + +namespace KeepAChangelogParser.Tests +{ + + [TestClass] + public class TheChangelogContainsADescriptionAndAnUnreleasedSection + { + + [TestMethod] + public void WhenTheChangelogContainsADescriptionAndAnUnreleasedSection_ThenTheResultIsCorrect() + { + // Arrange + string textToParse = + createTextToParse(); + + IChangelogParser changelogParser = + new ChangelogParser(); + + // Act + Result actualParseResult = + changelogParser.Parse( + textToParse); + + // Assert + Result expectedParseResult = + createExpectedParseResult(); + + Assert.That.AreEqual( + expectedParseResult, + actualParseResult, + ChangelogResultComparerFactory.Create()); + } + + private static string createTextToParse() + { + return + "# Changelog" + Environment.NewLine + + "" + Environment.NewLine + + "All notable changes to this project will be documented in this file." + Environment.NewLine + + "" + Environment.NewLine + + "The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/)," + Environment.NewLine + + "and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html)." + Environment.NewLine + + "" + Environment.NewLine + + "## [Unreleased]" + Environment.NewLine + + "" + Environment.NewLine + + "### Added" + Environment.NewLine + + "- New visual identity by[@tylerfortune8](https://github.com/tylerfortune8)." + Environment.NewLine + + "- Version navigation." + Environment.NewLine + + "" + Environment.NewLine; + } + + private static Result createExpectedParseResult() + { + Result expectedParseResult = + Result.Success( + new Changelog() + { + MarkdownTitle = + "Changelog", + MarkdownText = + "All notable changes to this project will be documented in this file." + Environment.NewLine + + "" + Environment.NewLine + + "The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/)," + Environment.NewLine + + "and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).", + }); + + expectedParseResult.Value. + SectionUnreleased. + MarkdownTitle = "Unreleased"; + + expectedParseResult.Value. + SectionUnreleased. + SubSectionCollection. + AddRange( + new List() + { + new ChangelogSubSection() + { + Type = ChangelogSubSectionType.Added, + }, + }); + + expectedParseResult.Value. + SectionUnreleased. + SubSectionCollection[0]. + ItemCollection. + AddRange( + new List() + { + new ChangelogSubSectionItem() + { + MarkdownText = "New visual identity by[@tylerfortune8](https://github.com/tylerfortune8)." + }, + new ChangelogSubSectionItem() + { + MarkdownText = "Version navigation." + }, + }); + + return expectedParseResult; + } + + } + +} diff --git a/KeepAChangelogParser.Tests/TheChangelogContainsADescriptionAndAnUnreleasedSectionWithAllTypes.cs b/KeepAChangelogParser.Tests/TheChangelogContainsADescriptionAndAnUnreleasedSectionWithAllTypes.cs new file mode 100644 index 0000000..229833b --- /dev/null +++ b/KeepAChangelogParser.Tests/TheChangelogContainsADescriptionAndAnUnreleasedSectionWithAllTypes.cs @@ -0,0 +1,237 @@ +using CSharpFunctionalExtensions; +using KeepAChangelogParser.Models; +using KeepAChangelogParser.Tests.Extensions; +using KeepAChangelogParser.Tests.Services; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using System; +using System.Collections.Generic; + +namespace KeepAChangelogParser.Tests +{ + + [TestClass] + public class TheChangelogContainsADescriptionAndAnUnreleasedSectionWithAllTypes + { + + [TestMethod] + public void WhenTheChangelogContainsADescriptionAndAnUnreleasedSectionWithAllTypes_ThenTheResultIsCorrect() + { + // Arrange + string textToParse = + createTextToParse(); + + IChangelogParser changelogParser = + new ChangelogParser(); + + // Act + Result actualParseResult = + changelogParser.Parse( + textToParse); + + // Assert + Result expectedParseResult = + createExpectedParseResult(); + + Assert.That.AreEqual( + expectedParseResult, + actualParseResult, + ChangelogResultComparerFactory.Create()); + } + + private static string createTextToParse() + { + return + "# Changelog" + Environment.NewLine + + "" + Environment.NewLine + + "All notable changes to this project will be documented in this file." + Environment.NewLine + + "" + Environment.NewLine + + "The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/)," + Environment.NewLine + + "and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html)." + Environment.NewLine + + "" + Environment.NewLine + + "## [Unreleased]" + Environment.NewLine + + "" + Environment.NewLine + + "### Added" + Environment.NewLine + + "- New visual identity by[@tylerfortune8](https://github.com/tylerfortune8)." + Environment.NewLine + + "- Version navigation." + Environment.NewLine + + "" + Environment.NewLine + + "### Changed" + Environment.NewLine + + "- New visual identity by[@tylerfortune8](https://github.com/tylerfortune8)." + Environment.NewLine + + "- Version navigation." + Environment.NewLine + + "" + Environment.NewLine + + "### Deprecated" + Environment.NewLine + + "- New visual identity by[@tylerfortune8](https://github.com/tylerfortune8)." + Environment.NewLine + + "- Version navigation." + Environment.NewLine + + "" + Environment.NewLine + + "### Removed" + Environment.NewLine + + "- New visual identity by[@tylerfortune8](https://github.com/tylerfortune8)." + Environment.NewLine + + "- Version navigation." + Environment.NewLine + + "" + Environment.NewLine + + "### Fixed" + Environment.NewLine + + "- New visual identity by[@tylerfortune8](https://github.com/tylerfortune8)." + Environment.NewLine + + "- Version navigation." + Environment.NewLine + + "" + Environment.NewLine + + "### Security" + Environment.NewLine + + "- New visual identity by[@tylerfortune8](https://github.com/tylerfortune8)." + Environment.NewLine + + "- Version navigation." + + "" + Environment.NewLine; + } + + private static Result createExpectedParseResult() + { + Result expectedParseResult = + Result.Success( + new Changelog() + { + MarkdownTitle = + "Changelog", + MarkdownText = + "All notable changes to this project will be documented in this file." + Environment.NewLine + + "" + Environment.NewLine + + "The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/)," + Environment.NewLine + + "and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).", + }); + + expectedParseResult.Value. + SectionUnreleased. + MarkdownTitle = "Unreleased"; + + expectedParseResult.Value. + SectionUnreleased. + SubSectionCollection. + AddRange( + new List() + { + new ChangelogSubSection() + { + Type = ChangelogSubSectionType.Added, + }, + new ChangelogSubSection() + { + Type = ChangelogSubSectionType.Changed, + }, + new ChangelogSubSection() + { + Type = ChangelogSubSectionType.Deprecated, + }, + new ChangelogSubSection() + { + Type = ChangelogSubSectionType.Removed, + }, + new ChangelogSubSection() + { + Type = ChangelogSubSectionType.Fixed, + }, + new ChangelogSubSection() + { + Type = ChangelogSubSectionType.Security, + }, + }); + + expectedParseResult.Value. + SectionUnreleased. + SubSectionCollection[0]. + ItemCollection. + AddRange( + new List() + { + new ChangelogSubSectionItem() + { + MarkdownText = "New visual identity by[@tylerfortune8](https://github.com/tylerfortune8)." + }, + new ChangelogSubSectionItem() + { + MarkdownText = "Version navigation." + }, + }); + + expectedParseResult.Value. + SectionUnreleased. + SubSectionCollection[1]. + ItemCollection. + AddRange( + new List() + { + new ChangelogSubSectionItem() + { + MarkdownText = "New visual identity by[@tylerfortune8](https://github.com/tylerfortune8)." + }, + new ChangelogSubSectionItem() + { + MarkdownText = "Version navigation." + }, + }); + + expectedParseResult.Value. + SectionUnreleased. + SubSectionCollection[2]. + ItemCollection. + AddRange( + new List() + { + new ChangelogSubSectionItem() + { + MarkdownText = "New visual identity by[@tylerfortune8](https://github.com/tylerfortune8)." + }, + new ChangelogSubSectionItem() + { + MarkdownText = "Version navigation." + }, + }); + + expectedParseResult.Value. + SectionUnreleased. + SubSectionCollection[3]. + ItemCollection. + AddRange( + new List() + { + new ChangelogSubSectionItem() + { + MarkdownText = "New visual identity by[@tylerfortune8](https://github.com/tylerfortune8)." + }, + new ChangelogSubSectionItem() + { + MarkdownText = "Version navigation." + }, + }); + + expectedParseResult.Value. + SectionUnreleased. + SubSectionCollection[4]. + ItemCollection. + AddRange( + new List() + { + new ChangelogSubSectionItem() + { + MarkdownText = "New visual identity by[@tylerfortune8](https://github.com/tylerfortune8)." + }, + new ChangelogSubSectionItem() + { + MarkdownText = "Version navigation." + }, + }); + + expectedParseResult.Value. + SectionUnreleased. + SubSectionCollection[5]. + ItemCollection. + AddRange( + new List() + { + new ChangelogSubSectionItem() + { + MarkdownText = "New visual identity by[@tylerfortune8](https://github.com/tylerfortune8)." + }, + new ChangelogSubSectionItem() + { + MarkdownText = "Version navigation." + }, + }); + + return expectedParseResult; + } + + } + +} diff --git a/KeepAChangelogParser.Tests/TheChangelogContainsADescriptionAndAnUnreleasedSectionWithInvalidTitle.cs b/KeepAChangelogParser.Tests/TheChangelogContainsADescriptionAndAnUnreleasedSectionWithInvalidTitle.cs new file mode 100644 index 0000000..96af0c6 --- /dev/null +++ b/KeepAChangelogParser.Tests/TheChangelogContainsADescriptionAndAnUnreleasedSectionWithInvalidTitle.cs @@ -0,0 +1,69 @@ +using CSharpFunctionalExtensions; +using KeepAChangelogParser.Models; +using KeepAChangelogParser.Tests.Extensions; +using KeepAChangelogParser.Tests.Services; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using System; + +namespace KeepAChangelogParser.Tests +{ + + [TestClass] + public class TheChangelogContainsADescriptionAndAnUnreleasedSectionWithInvalidTitle + { + + [TestMethod] + public void WhenTheChangelogContainsADescriptionAndAnUnreleasedSectionWithInvalidTitle_ThenTheResultIsFailureInvalidUnreleasedTitle() + { + // Arrange + string textToParse = + createTextToParse(); + + IChangelogParser changelogParser = + new ChangelogParser(); + + // Act + Result actualParseResult = + changelogParser.Parse( + textToParse); + + // Assert + Result expectedParseResult = + createExpectedParseResult(); + + Assert.That.AreEqual( + expectedParseResult, + actualParseResult, + ChangelogResultComparerFactory.Create()); + } + + private static string createTextToParse() + { + return + /* 01 */ "# Changelog" + Environment.NewLine + + /* 02 */ "" + Environment.NewLine + + /* 03 */ "All notable changes to this project will be documented in this file." + Environment.NewLine + + /* 04 */ "" + Environment.NewLine + + /* 05 */ "The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/)," + Environment.NewLine + + /* 06 */ "and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html)." + Environment.NewLine + + /* 07 */ "" + Environment.NewLine + + /* 08 */ "## [Unrelease]" + Environment.NewLine + + /* 09 */ "" + Environment.NewLine + + /* 10 */ "### Added" + Environment.NewLine + + /* 11 */ "- New visual identity by[@tylerfortune8](https://github.com/tylerfortune8)." + Environment.NewLine + + /* 12 */ "- Version navigation." + Environment.NewLine + + /* 13 */ "" + Environment.NewLine; + } + + private static Result createExpectedParseResult() + { + Result expectedParseResult = + Result.Failure( + "Invalid unreleased title. Error parsing text in line 8 / index 5."); + + return expectedParseResult; + } + + } + +} diff --git a/KeepAChangelogParser.Tests/TheChangelogContainsADescriptionAndAnUnreleasedSectionWithInvalidType.cs b/KeepAChangelogParser.Tests/TheChangelogContainsADescriptionAndAnUnreleasedSectionWithInvalidType.cs new file mode 100644 index 0000000..42ab925 --- /dev/null +++ b/KeepAChangelogParser.Tests/TheChangelogContainsADescriptionAndAnUnreleasedSectionWithInvalidType.cs @@ -0,0 +1,69 @@ +using CSharpFunctionalExtensions; +using KeepAChangelogParser.Models; +using KeepAChangelogParser.Tests.Extensions; +using KeepAChangelogParser.Tests.Services; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using System; + +namespace KeepAChangelogParser.Tests +{ + + [TestClass] + public class TheChangelogContainsADescriptionAndAnUnreleasedSectionWithInvalidType + { + + [TestMethod] + public void WhenTheChangelogContainsADescriptionAndAnUnreleasedSectionWithInvalidType_ThenTheResultIsFailureInvalidSubsectionType() + { + // Arrange + string textToParse = + createTextToParse(); + + IChangelogParser changelogParser = + new ChangelogParser(); + + // Act + Result actualParseResult = + changelogParser.Parse( + textToParse); + + // Assert + Result expectedParseResult = + createExpectedParseResult(); + + Assert.That.AreEqual( + expectedParseResult, + actualParseResult, + ChangelogResultComparerFactory.Create()); + } + + private static string createTextToParse() + { + return + /* 01 */ "# Changelog" + Environment.NewLine + + /* 02 */ "" + Environment.NewLine + + /* 03 */ "All notable changes to this project will be documented in this file." + Environment.NewLine + + /* 04 */ "" + Environment.NewLine + + /* 05 */ "The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/)," + Environment.NewLine + + /* 06 */ "and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html)." + Environment.NewLine + + /* 07 */ "" + Environment.NewLine + + /* 08 */ "## [Unreleased]" + Environment.NewLine + + /* 09 */ "" + Environment.NewLine + + /* 10 */ "### Add" + Environment.NewLine + + /* 11 */ "- New visual identity by[@tylerfortune8](https://github.com/tylerfortune8)." + Environment.NewLine + + /* 12 */ "- Version navigation." + Environment.NewLine + + /* 13 */ "" + Environment.NewLine; + } + + private static Result createExpectedParseResult() + { + Result expectedParseResult = + Result.Failure( + "Invalid subsection type. Error parsing text in line 10 / index 5."); + + return expectedParseResult; + } + + } + +} diff --git a/KeepAChangelogParser.Tests/TheChangelogContainsADescriptionAndAnUnreleasedSectionWithItemMissingDash.cs b/KeepAChangelogParser.Tests/TheChangelogContainsADescriptionAndAnUnreleasedSectionWithItemMissingDash.cs new file mode 100644 index 0000000..f6c5be3 --- /dev/null +++ b/KeepAChangelogParser.Tests/TheChangelogContainsADescriptionAndAnUnreleasedSectionWithItemMissingDash.cs @@ -0,0 +1,69 @@ +using CSharpFunctionalExtensions; +using KeepAChangelogParser.Models; +using KeepAChangelogParser.Tests.Extensions; +using KeepAChangelogParser.Tests.Services; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using System; + +namespace KeepAChangelogParser.Tests +{ + + [TestClass] + public class TheChangelogContainsADescriptionAndAnUnreleasedSectionWithItemMissingDash + { + + [TestMethod] + public void WhenTheChangelogContainsADescriptionAndAnUnreleasedSectionWithItemMissingDash_ThenTheResultIsFailureNoDash() + { + // Arrange + string textToParse = + createTextToParse(); + + IChangelogParser changelogParser = + new ChangelogParser(); + + // Act + Result actualParseResult = + changelogParser.Parse( + textToParse); + + // Assert + Result expectedParseResult = + createExpectedParseResult(); + + Assert.That.AreEqual( + expectedParseResult, + actualParseResult, + ChangelogResultComparerFactory.Create()); + } + + private static string createTextToParse() + { + return + /* 01 */ "# Changelog" + Environment.NewLine + + /* 02 */ "" + Environment.NewLine + + /* 03 */ "All notable changes to this project will be documented in this file." + Environment.NewLine + + /* 04 */ "" + Environment.NewLine + + /* 05 */ "The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/)," + Environment.NewLine + + /* 06 */ "and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html)." + Environment.NewLine + + /* 07 */ "" + Environment.NewLine + + /* 08 */ "## [Unreleased]" + Environment.NewLine + + /* 09 */ "" + Environment.NewLine + + /* 10 */ "### Added" + Environment.NewLine + + /* 11 */ "- New visual identity by[@tylerfortune8](https://github.com/tylerfortune8)." + Environment.NewLine + + /* 12 */ " Version navigation." + Environment.NewLine + + /* 13 */ "" + Environment.NewLine; + } + + private static Result createExpectedParseResult() + { + Result expectedParseResult = + Result.Failure( + "No dash. Error parsing text in line 12 / index 1."); + + return expectedParseResult; + } + + } + +} diff --git a/KeepAChangelogParser.Tests/TheChangelogContainsADescriptionAndAnUnreleasedSectionWithItemMissingSpace.cs b/KeepAChangelogParser.Tests/TheChangelogContainsADescriptionAndAnUnreleasedSectionWithItemMissingSpace.cs new file mode 100644 index 0000000..6586967 --- /dev/null +++ b/KeepAChangelogParser.Tests/TheChangelogContainsADescriptionAndAnUnreleasedSectionWithItemMissingSpace.cs @@ -0,0 +1,69 @@ +using CSharpFunctionalExtensions; +using KeepAChangelogParser.Models; +using KeepAChangelogParser.Tests.Extensions; +using KeepAChangelogParser.Tests.Services; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using System; + +namespace KeepAChangelogParser.Tests +{ + + [TestClass] + public class TheChangelogContainsADescriptionAndAnUnreleasedSectionWithItemMissingSpace + { + + [TestMethod] + public void WhenTheChangelogContainsADescriptionAndAnUnreleasedSectionWithItemMissingSpace_ThenTheResultIsFailureNoSpace() + { + // Arrange + string textToParse = + createTextToParse(); + + IChangelogParser changelogParser = + new ChangelogParser(); + + // Act + Result actualParseResult = + changelogParser.Parse( + textToParse); + + // Assert + Result expectedParseResult = + createExpectedParseResult(); + + Assert.That.AreEqual( + expectedParseResult, + actualParseResult, + ChangelogResultComparerFactory.Create()); + } + + private static string createTextToParse() + { + return + /* 01 */ "# Changelog" + Environment.NewLine + + /* 02 */ "" + Environment.NewLine + + /* 03 */ "All notable changes to this project will be documented in this file." + Environment.NewLine + + /* 04 */ "" + Environment.NewLine + + /* 05 */ "The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/)," + Environment.NewLine + + /* 06 */ "and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html)." + Environment.NewLine + + /* 07 */ "" + Environment.NewLine + + /* 08 */ "## [Unreleased]" + Environment.NewLine + + /* 09 */ "" + Environment.NewLine + + /* 10 */ "### Added" + Environment.NewLine + + /* 11 */ "- New visual identity by[@tylerfortune8](https://github.com/tylerfortune8)." + Environment.NewLine + + /* 12 */ "-Version navigation." + Environment.NewLine + + /* 13 */ "" + Environment.NewLine; + } + + private static Result createExpectedParseResult() + { + Result expectedParseResult = + Result.Failure( + "No space. Error parsing text in line 12 / index 2."); + + return expectedParseResult; + } + + } + +} diff --git a/KeepAChangelogParser.Tests/TheChangelogContainsADescriptionAndAnUnreleasedSectionWithNoTitle.cs b/KeepAChangelogParser.Tests/TheChangelogContainsADescriptionAndAnUnreleasedSectionWithNoTitle.cs new file mode 100644 index 0000000..99c36e7 --- /dev/null +++ b/KeepAChangelogParser.Tests/TheChangelogContainsADescriptionAndAnUnreleasedSectionWithNoTitle.cs @@ -0,0 +1,69 @@ +using CSharpFunctionalExtensions; +using KeepAChangelogParser.Models; +using KeepAChangelogParser.Tests.Extensions; +using KeepAChangelogParser.Tests.Services; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using System; + +namespace KeepAChangelogParser.Tests +{ + + [TestClass] + public class TheChangelogContainsADescriptionAndAnUnreleasedSectionWithNoTitle + { + + [TestMethod] + public void WhenTheChangelogContainsADescriptionAndAnUnreleasedSectionWithNoTitle_ThenTheResultIsFailureInvalidText() + { + // Arrange + string textToParse = + createTextToParse(); + + IChangelogParser changelogParser = + new ChangelogParser(); + + // Act + Result actualParseResult = + changelogParser.Parse( + textToParse); + + // Assert + Result expectedParseResult = + createExpectedParseResult(); + + Assert.That.AreEqual( + expectedParseResult, + actualParseResult, + ChangelogResultComparerFactory.Create()); + } + + private static string createTextToParse() + { + return + "# Changelog" + Environment.NewLine + + "" + Environment.NewLine + + "All notable changes to this project will be documented in this file." + Environment.NewLine + + "" + Environment.NewLine + + "The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/)," + Environment.NewLine + + "and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html)." + Environment.NewLine + + "" + Environment.NewLine + + "## []" + Environment.NewLine + + "" + Environment.NewLine + + "### Added" + Environment.NewLine + + "- New visual identity by[@tylerfortune8](https://github.com/tylerfortune8)." + Environment.NewLine + + "- Version navigation." + Environment.NewLine + + "" + Environment.NewLine; + } + + private static Result createExpectedParseResult() + { + Result expectedParseResult = + Result.Failure( + "Invalid text. Error parsing text in line 8 / index 5."); + + return expectedParseResult; + } + + } + +} diff --git a/KeepAChangelogParser.Tests/TheChangelogContainsADescriptionAndAnUnreleasedSectionWithNoType.cs b/KeepAChangelogParser.Tests/TheChangelogContainsADescriptionAndAnUnreleasedSectionWithNoType.cs new file mode 100644 index 0000000..99292d4 --- /dev/null +++ b/KeepAChangelogParser.Tests/TheChangelogContainsADescriptionAndAnUnreleasedSectionWithNoType.cs @@ -0,0 +1,69 @@ +using CSharpFunctionalExtensions; +using KeepAChangelogParser.Models; +using KeepAChangelogParser.Tests.Extensions; +using KeepAChangelogParser.Tests.Services; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using System; + +namespace KeepAChangelogParser.Tests +{ + + [TestClass] + public class TheChangelogContainsADescriptionAndAnUnreleasedSectionWithNoType + { + + [TestMethod] + public void WhenTheChangelogContainsADescriptionAndAnUnreleasedSectionWithNoType_ThenTheResultIsFailureInvalidSubsectionType() + { + // Arrange + string textToParse = + createTextToParse(); + + IChangelogParser changelogParser = + new ChangelogParser(); + + // Act + Result actualParseResult = + changelogParser.Parse( + textToParse); + + // Assert + Result expectedParseResult = + createExpectedParseResult(); + + Assert.That.AreEqual( + expectedParseResult, + actualParseResult, + ChangelogResultComparerFactory.Create()); + } + + private static string createTextToParse() + { + return + /* 01 */ "# Changelog" + Environment.NewLine + + /* 02 */ "" + Environment.NewLine + + /* 03 */ "All notable changes to this project will be documented in this file." + Environment.NewLine + + /* 04 */ "" + Environment.NewLine + + /* 05 */ "The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/)," + Environment.NewLine + + /* 06 */ "and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html)." + Environment.NewLine + + /* 07 */ "" + Environment.NewLine + + /* 08 */ "## [Unreleased]" + Environment.NewLine + + /* 09 */ "" + Environment.NewLine + + /* 10 */ "### " + Environment.NewLine + + /* 11 */ "- New visual identity by[@tylerfortune8](https://github.com/tylerfortune8)." + Environment.NewLine + + /* 12 */ "- Version navigation." + Environment.NewLine + + /* 13 */ "" + Environment.NewLine; + } + + private static Result createExpectedParseResult() + { + Result expectedParseResult = + Result.Failure( + "Invalid title. Error parsing text in line 11 / index 75."); + + return expectedParseResult; + } + + } + +} diff --git a/KeepAChangelogParser.Tests/TheChangelogContainsADescriptionAndAnUnreleasedSectionWithTitleMissingCloseBracket.cs b/KeepAChangelogParser.Tests/TheChangelogContainsADescriptionAndAnUnreleasedSectionWithTitleMissingCloseBracket.cs new file mode 100644 index 0000000..d0c39cc --- /dev/null +++ b/KeepAChangelogParser.Tests/TheChangelogContainsADescriptionAndAnUnreleasedSectionWithTitleMissingCloseBracket.cs @@ -0,0 +1,69 @@ +using CSharpFunctionalExtensions; +using KeepAChangelogParser.Models; +using KeepAChangelogParser.Tests.Extensions; +using KeepAChangelogParser.Tests.Services; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using System; + +namespace KeepAChangelogParser.Tests +{ + + [TestClass] + public class TheChangelogContainsADescriptionAndAnUnreleasedSectionWithTitleMissingCloseBracket + { + + [TestMethod] + public void WhenTheChangelogContainsADescriptionAndAnUnreleasedSectionWithTitleMissingCloseBracket_ThenTheResultIsFailureNoCloseSquareBracket() + { + // Arrange + string textToParse = + createTextToParse(); + + IChangelogParser changelogParser = + new ChangelogParser(); + + // Act + Result actualParseResult = + changelogParser.Parse( + textToParse); + + // Assert + Result expectedParseResult = + createExpectedParseResult(); + + Assert.That.AreEqual( + expectedParseResult, + actualParseResult, + ChangelogResultComparerFactory.Create()); + } + + private static string createTextToParse() + { + return + /* 01 */ "# Changelog" + Environment.NewLine + + /* 02 */ "" + Environment.NewLine + + /* 03 */ "All notable changes to this project will be documented in this file." + Environment.NewLine + + /* 04 */ "" + Environment.NewLine + + /* 05 */ "The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/)," + Environment.NewLine + + /* 06 */ "and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html)." + Environment.NewLine + + /* 07 */ "" + Environment.NewLine + + /* 08 */ "## [Unreleased" + Environment.NewLine + + /* 09 */ "" + Environment.NewLine + + /* 10 */ "### Added" + Environment.NewLine + + /* 11 */ "- New visual identity by[@tylerfortune8](https://github.com/tylerfortune8)." + Environment.NewLine + + /* 12 */ "- Version navigation." + Environment.NewLine + + /* 13 */ "" + Environment.NewLine; + } + + private static Result createExpectedParseResult() + { + Result expectedParseResult = + Result.Failure( + "No close square bracket. Error parsing text in line 9 / index 0."); + + return expectedParseResult; + } + + } + +} diff --git a/KeepAChangelogParser.Tests/TheChangelogContainsADescriptionAndAnUnreleasedSectionWithTitleMissingOpenBracket.cs b/KeepAChangelogParser.Tests/TheChangelogContainsADescriptionAndAnUnreleasedSectionWithTitleMissingOpenBracket.cs new file mode 100644 index 0000000..ccaccd8 --- /dev/null +++ b/KeepAChangelogParser.Tests/TheChangelogContainsADescriptionAndAnUnreleasedSectionWithTitleMissingOpenBracket.cs @@ -0,0 +1,69 @@ +using CSharpFunctionalExtensions; +using KeepAChangelogParser.Models; +using KeepAChangelogParser.Tests.Extensions; +using KeepAChangelogParser.Tests.Services; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using System; + +namespace KeepAChangelogParser.Tests +{ + + [TestClass] + public class TheChangelogContainsADescriptionAndAnUnreleasedSectionWithTitleMissingOpenBracket + { + + [TestMethod] + public void WhenTheChangelogContainsADescriptionAndAnUnreleasedSectionWithTitleMissingOpenBracket_ThenTheResultIsFailureNoOpenSquareBracket() + { + // Arrange + string textToParse = + createTextToParse(); + + IChangelogParser changelogParser = + new ChangelogParser(); + + // Act + Result actualParseResult = + changelogParser.Parse( + textToParse); + + // Assert + Result expectedParseResult = + createExpectedParseResult(); + + Assert.That.AreEqual( + expectedParseResult, + actualParseResult, + ChangelogResultComparerFactory.Create()); + } + + private static string createTextToParse() + { + return + /* 01 */ "# Changelog" + Environment.NewLine + + /* 02 */ "" + Environment.NewLine + + /* 03 */ "All notable changes to this project will be documented in this file." + Environment.NewLine + + /* 04 */ "" + Environment.NewLine + + /* 05 */ "The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/)," + Environment.NewLine + + /* 06 */ "and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html)." + Environment.NewLine + + /* 07 */ "" + Environment.NewLine + + /* 08 */ "## Unreleased]" + Environment.NewLine + + /* 09 */ "" + Environment.NewLine + + /* 10 */ "### Added" + Environment.NewLine + + /* 11 */ "- New visual identity by[@tylerfortune8](https://github.com/tylerfortune8)." + Environment.NewLine + + /* 12 */ "- Version navigation." + Environment.NewLine + + /* 13 */ "" + Environment.NewLine; + } + + private static Result createExpectedParseResult() + { + Result expectedParseResult = + Result.Failure( + "No open square bracket. Error parsing text in line 8 / index 4."); + + return expectedParseResult; + } + + } + +} diff --git a/KeepAChangelogParser.Tests/TheChangelogContainsADescriptionAndAnUnreleasedSectionWithTwoIdenticalTypes.cs b/KeepAChangelogParser.Tests/TheChangelogContainsADescriptionAndAnUnreleasedSectionWithTwoIdenticalTypes.cs new file mode 100644 index 0000000..761d51d --- /dev/null +++ b/KeepAChangelogParser.Tests/TheChangelogContainsADescriptionAndAnUnreleasedSectionWithTwoIdenticalTypes.cs @@ -0,0 +1,73 @@ +using CSharpFunctionalExtensions; +using KeepAChangelogParser.Models; +using KeepAChangelogParser.Tests.Extensions; +using KeepAChangelogParser.Tests.Services; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using System; + +namespace KeepAChangelogParser.Tests +{ + + [TestClass] + public class TheChangelogContainsADescriptionAndAnUnreleasedSectionWithTwoIdenticalTypes + { + + [TestMethod] + public void WhenTheChangelogContainsADescriptionAndAnUnreleasedSectionWithTwoIdenticalTypes_ThenTheResultIsFailureSubsectionTypeAlreadyExists() + { + // Arrange + string textToParse = + createTextToParse(); + + IChangelogParser changelogParser = + new ChangelogParser(); + + // Act + Result actualParseResult = + changelogParser.Parse( + textToParse); + + // Assert + Result expectedParseResult = + createExpectedParseResult(); + + Assert.That.AreEqual( + expectedParseResult, + actualParseResult, + ChangelogResultComparerFactory.Create()); + } + + private static string createTextToParse() + { + return + /* 01 */ "# Changelog" + Environment.NewLine + + /* 02 */ "" + Environment.NewLine + + /* 03 */ "All notable changes to this project will be documented in this file." + Environment.NewLine + + /* 04 */ "" + Environment.NewLine + + /* 05 */ "The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/)," + Environment.NewLine + + /* 06 */ "and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html)." + Environment.NewLine + + /* 07 */ "" + Environment.NewLine + + /* 08 */ "## [Unreleased]" + Environment.NewLine + + /* 09 */ "" + Environment.NewLine + + /* 10 */ "### Added" + Environment.NewLine + + /* 11 */ "- New visual identity by[@tylerfortune8](https://github.com/tylerfortune8)." + Environment.NewLine + + /* 12 */ "- Version navigation." + Environment.NewLine + + /* 13 */ "" + Environment.NewLine + + /* 14 */ "### Added" + Environment.NewLine + + /* 15 */ "- New visual identity by[@tylerfortune8](https://github.com/tylerfortune8)." + Environment.NewLine + + /* 16 */ "- Version navigation." + Environment.NewLine + + /* 17 */ "" + Environment.NewLine; + } + + private static Result createExpectedParseResult() + { + Result expectedParseResult = + Result.Failure( + "Subsection type already exists. Error parsing text in line 14 / index 5."); + + return expectedParseResult; + } + + } + +} diff --git a/KeepAChangelogParser.Tests/TheChangelogContainsNothing.cs b/KeepAChangelogParser.Tests/TheChangelogContainsNothing.cs new file mode 100644 index 0000000..fead257 --- /dev/null +++ b/KeepAChangelogParser.Tests/TheChangelogContainsNothing.cs @@ -0,0 +1,60 @@ +using CSharpFunctionalExtensions; +using KeepAChangelogParser.Models; +using KeepAChangelogParser.Tests.Extensions; +using KeepAChangelogParser.Tests.Services; +using Microsoft.VisualStudio.TestTools.UnitTesting; + +namespace KeepAChangelogParser.Tests +{ + + [TestClass] + public class TheChangelogContainsNothing + { + + [TestMethod] + public void WhenTheChangelogContainsNothing_ThenTheResultIsCorrect() + { + // Arrange + string textToParse = + createTextToParse(); + + IChangelogParser changelogParser = + new ChangelogParser(); + + // Act + Result actualParseResult = + changelogParser.Parse( + textToParse); + + // Assert + Result expectedParseResult = + createExpectedParseResult(); + + Assert.That.AreEqual( + expectedParseResult, + actualParseResult, + ChangelogResultComparerFactory.Create()); + } + + private static string createTextToParse() + { + return + "# Changelog"; + } + + private static Result createExpectedParseResult() + { + Result expectedParseResult = + Result.Success( + new Changelog() + { + MarkdownTitle = + "Changelog", + }); + + return expectedParseResult; + } + + } + +} diff --git a/KeepAChangelogParser.Tests/TheChangelogContainsNothingWithInvalidHeadingOne.cs b/KeepAChangelogParser.Tests/TheChangelogContainsNothingWithInvalidHeadingOne.cs new file mode 100644 index 0000000..0c67b71 --- /dev/null +++ b/KeepAChangelogParser.Tests/TheChangelogContainsNothingWithInvalidHeadingOne.cs @@ -0,0 +1,56 @@ +using CSharpFunctionalExtensions; +using KeepAChangelogParser.Models; +using KeepAChangelogParser.Tests.Extensions; +using KeepAChangelogParser.Tests.Services; +using Microsoft.VisualStudio.TestTools.UnitTesting; + +namespace KeepAChangelogParser.Tests +{ + + [TestClass] + public class TheChangelogContainsNothingWithInvalidHeadingOne + { + + [TestMethod] + public void WhenTheChangelogContainsNothingWithInvalidHeadingOne_ThenTheResultIsFailure() + { + // Arrange + string textToParse = + createTextToParse(); + + IChangelogParser changelogParser = + new ChangelogParser(); + + // Act + Result actualParseResult = + changelogParser.Parse( + textToParse); + + // Assert + Result expectedParseResult = + createExpectedParseResult(); + + Assert.That.AreEqual( + expectedParseResult, + actualParseResult, + ChangelogResultComparerFactory.Create()); + } + + private static string createTextToParse() + { + return + /* 1 */ "Changelog"; + } + + private static Result createExpectedParseResult() + { + Result expectedParseResult = + Result.Failure( + "No level one heading. Error parsing text in line 1 / index 1."); + + return expectedParseResult; + } + + } + +} diff --git a/KeepAChangelogParser.Tests/TheChangelogContainsNothingWithNoTitle.cs b/KeepAChangelogParser.Tests/TheChangelogContainsNothingWithNoTitle.cs new file mode 100644 index 0000000..7e9d6e1 --- /dev/null +++ b/KeepAChangelogParser.Tests/TheChangelogContainsNothingWithNoTitle.cs @@ -0,0 +1,56 @@ +using CSharpFunctionalExtensions; +using KeepAChangelogParser.Models; +using KeepAChangelogParser.Tests.Extensions; +using KeepAChangelogParser.Tests.Services; +using Microsoft.VisualStudio.TestTools.UnitTesting; + +namespace KeepAChangelogParser.Tests +{ + + [TestClass] + public class TheChangelogContainsNothingWithNoTitle + { + + [TestMethod] + public void WhenTheChangelogContainsNothingWithNoTitle_ThenTheResultIsFailure() + { + // Arrange + string textToParse = + createTextToParse(); + + IChangelogParser changelogParser = + new ChangelogParser(); + + // Act + Result actualParseResult = + changelogParser.Parse( + textToParse); + + // Assert + Result expectedParseResult = + createExpectedParseResult(); + + Assert.That.AreEqual( + expected: expectedParseResult, + actual: actualParseResult, + comparer: ChangelogResultComparerFactory.Create()); + } + + private static string createTextToParse() + { + return + /* 1 */ "# "; + } + + private static Result createExpectedParseResult() + { + Result expectedParseResult = + Result.Failure( + "Invalid title. Error parsing text in line 1 / index 2."); + + return expectedParseResult; + } + + } + +} diff --git a/KeepAChangelogParser.sln b/KeepAChangelogParser.sln new file mode 100644 index 0000000..7188a5b --- /dev/null +++ b/KeepAChangelogParser.sln @@ -0,0 +1,42 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 16 +VisualStudioVersion = 16.0.30907.101 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "KeepAChangelogParser", "KeepAChangelogParser\KeepAChangelogParser.csproj", "{10BB7C8D-7F0F-4618-A2B3-73CB414F4392}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "KeepAChangelogParser.Tests", "KeepAChangelogParser.Tests\KeepAChangelogParser.Tests.csproj", "{2DB91937-0CF7-43D0-A4BB-6B561B88B917}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{2382311E-54D1-4AD0-B252-38D1B391CF76}" + ProjectSection(SolutionItems) = preProject + .editorconfig = .editorconfig + .gitignore = .gitignore + README.md = README.md + EndProjectSection +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release_Signed|Any CPU = Release_Signed|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {10BB7C8D-7F0F-4618-A2B3-73CB414F4392}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {10BB7C8D-7F0F-4618-A2B3-73CB414F4392}.Debug|Any CPU.Build.0 = Debug|Any CPU + {10BB7C8D-7F0F-4618-A2B3-73CB414F4392}.Release_Signed|Any CPU.ActiveCfg = Release_Signed|Any CPU + {10BB7C8D-7F0F-4618-A2B3-73CB414F4392}.Release_Signed|Any CPU.Build.0 = Release_Signed|Any CPU + {10BB7C8D-7F0F-4618-A2B3-73CB414F4392}.Release|Any CPU.ActiveCfg = Release|Any CPU + {10BB7C8D-7F0F-4618-A2B3-73CB414F4392}.Release|Any CPU.Build.0 = Release|Any CPU + {2DB91937-0CF7-43D0-A4BB-6B561B88B917}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {2DB91937-0CF7-43D0-A4BB-6B561B88B917}.Debug|Any CPU.Build.0 = Debug|Any CPU + {2DB91937-0CF7-43D0-A4BB-6B561B88B917}.Release_Signed|Any CPU.ActiveCfg = Release|Any CPU + {2DB91937-0CF7-43D0-A4BB-6B561B88B917}.Release|Any CPU.ActiveCfg = Release|Any CPU + {2DB91937-0CF7-43D0-A4BB-6B561B88B917}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {5FE20180-9CAC-4377-8761-36CCE96E9E6E} + EndGlobalSection +EndGlobal diff --git a/KeepAChangelogParser/ChangelogParser.cs b/KeepAChangelogParser/ChangelogParser.cs new file mode 100644 index 0000000..ac5d293 --- /dev/null +++ b/KeepAChangelogParser/ChangelogParser.cs @@ -0,0 +1,1068 @@ +using CSharpFunctionalExtensions; +using KeepAChangelogParser.Contracts; +using KeepAChangelogParser.Models; +using KeepAChangelogParser.Services; +using System; +using System.Collections.Generic; +using System.Linq; + +namespace KeepAChangelogParser +{ + + public class ChangelogParser : + IChangelogParser + { + + public Result Parse( + string text + ) + { + Result changelogResult = + Result.Success(new Changelog()); + + IChangelogTokenizer changelogTokenizer = + new ChangelogTokenizer(); + + IEnumerable tokenCollection = + changelogTokenizer.Tokenize(text); + + Stack tokenStack = + new Stack(tokenCollection.Reverse()); + + changelogResult = parseHeadingOne(changelogResult, tokenStack); + changelogResult = parseSpace(changelogResult, tokenStack); + changelogResult = parseTitle(changelogResult, tokenStack); + changelogResult = parseNewLine(changelogResult, tokenStack); + + while (isHeadingOneTextOrNewLine(changelogResult, tokenStack)) + { + if (isText(tokenStack)) + { + changelogResult = parseText(changelogResult, tokenStack); + } + + changelogResult = parseTextNewLine(changelogResult, tokenStack); + } + + changelogResult = trimText(changelogResult); + + while (isHeadingTwo(changelogResult, tokenStack)) + { + changelogResult = parseHeadingTwo(changelogResult, tokenStack); + changelogResult = parseSpace(changelogResult, tokenStack); + changelogResult = parseOpenSquareBracket(changelogResult, tokenStack); + + if (isTitle(changelogResult, tokenStack) && + isEmptySectionUnreleased(changelogResult) && + isEmptySectionCollection(changelogResult)) + { + changelogResult = parseUnreleased(changelogResult, tokenStack); + changelogResult = parseCloseSquareBracket(changelogResult, tokenStack); + changelogResult = parseNewLine(changelogResult, tokenStack); + + while (isNewLine(changelogResult, tokenStack)) + { + changelogResult = parseNewLine(changelogResult, tokenStack); + } + + while (isHeadingThree(changelogResult, tokenStack)) + { + changelogResult = parseUnreleasedHeadingThree(changelogResult, tokenStack); + changelogResult = parseSpace(changelogResult, tokenStack); + changelogResult = parseUnreleasedTitle(changelogResult, tokenStack); + changelogResult = parseNewLine(changelogResult, tokenStack); + + while (isHeadingThreeUnreleasedTextOrNewLine(changelogResult, tokenStack)) + { + if (isText(tokenStack)) + { + changelogResult = parseDash(changelogResult, tokenStack); + changelogResult = parseSpace(changelogResult, tokenStack); + changelogResult = parseUnreleasedText(changelogResult, tokenStack); + } + + changelogResult = parseListTextNewLine(changelogResult, tokenStack); + } + } + } + else + { + changelogResult = parseVersion(changelogResult, tokenStack); + changelogResult = parseCloseSquareBracket(changelogResult, tokenStack); + changelogResult = parseSpace(changelogResult, tokenStack); + changelogResult = parseDash(changelogResult, tokenStack); + changelogResult = parseSpace(changelogResult, tokenStack); + changelogResult = parseDate(changelogResult, tokenStack); + changelogResult = parseNewLine(changelogResult, tokenStack); + + while (isNewLine(changelogResult, tokenStack)) + { + changelogResult = parseNewLine(changelogResult, tokenStack); + } + + while (isHeadingThree(changelogResult, tokenStack)) + { + changelogResult = parseHeadingThree(changelogResult, tokenStack); + changelogResult = parseSpace(changelogResult, tokenStack); + changelogResult = parseTitle(changelogResult, tokenStack); + changelogResult = parseNewLine(changelogResult, tokenStack); + + while (isHeadingThreeTextOrNewLine(changelogResult, tokenStack)) + { + if (isText(tokenStack)) + { + changelogResult = parseDash(changelogResult, tokenStack); + changelogResult = parseSpace(changelogResult, tokenStack); + changelogResult = parseText(changelogResult, tokenStack); + } + + changelogResult = parseListTextNewLine(changelogResult, tokenStack); + } + } + } + } + + changelogResult = parseSequenceTerminator(changelogResult, tokenStack); + + if (changelogResult.IsFailure) + { + return Result.Failure(changelogResult.Error); + } + + return Result.Success(changelogResult.Value); + } + + private static Result addTokenValueToText( + Result changelogResult, + ChangelogToken token + ) + { + int sectionCollectionCount = + changelogResult.Value. + SectionCollection.Count; + + if (sectionCollectionCount == 0) + { + changelogResult.Value. + MarkdownText += token.Value; + + return changelogResult; + } + + int subSectionCollectionCount = + changelogResult.Value. + SectionCollection[sectionCollectionCount - 1]. + SubSectionCollection.Count; + + int subSectionItemCollectionCount = + changelogResult.Value. + SectionCollection[sectionCollectionCount - 1]. + SubSectionCollection[subSectionCollectionCount - 1]. + ItemCollection.Count; + + changelogResult.Value. + SectionCollection[sectionCollectionCount - 1]. + SubSectionCollection[subSectionCollectionCount - 1]. + ItemCollection[subSectionItemCollectionCount - 1]. + MarkdownText += token.Value; + + return changelogResult; + } + + private static Result addTokenValueToTitleOrSetType( + Result changelogResult, + ChangelogToken token + ) + { + int sectionCollectionCount = + changelogResult.Value. + SectionCollection.Count; + + if (sectionCollectionCount == 0) + { + changelogResult.Value. + MarkdownTitle += token.Value; + + return changelogResult; + } + + int subSectionCollectionCount = + changelogResult.Value. + SectionCollection[sectionCollectionCount - 1]. + SubSectionCollection.Count; + + if (!Enum.TryParse(token.Value, out ChangelogSubSectionType subSectionType)) + { + return Result.Failure( + $"Invalid subsection type. Error parsing text in line {token.LineNumber} / index {token.Index}."); + } + + for (int index = 0; index < changelogResult.Value.SectionCollection[sectionCollectionCount - 1].SubSectionCollection.Count - 1; index++) + { + if (changelogResult.Value.SectionCollection[sectionCollectionCount - 1].SubSectionCollection[index].Type == subSectionType) + { + return Result.Failure( + $"Subsection type already exists. Error parsing text in line {token.LineNumber} / index {token.Index}."); + } + } + + changelogResult.Value. + SectionCollection[sectionCollectionCount - 1]. + SubSectionCollection[subSectionCollectionCount - 1]. + Type = subSectionType; + + return changelogResult; + } + + private static Result addTokenValueToUnreleasedText( + Result changelogResult, + ChangelogToken token + ) + { + int subSectionUnreleasedCollectionCount = + changelogResult.Value. + SectionUnreleased. + SubSectionCollection.Count; + + int subSectionUnreleasedItemCollectionCount = + changelogResult.Value. + SectionUnreleased. + SubSectionCollection[subSectionUnreleasedCollectionCount - 1]. + ItemCollection.Count; + + changelogResult.Value. + SectionUnreleased. + SubSectionCollection[subSectionUnreleasedCollectionCount - 1]. + ItemCollection[subSectionUnreleasedItemCollectionCount - 1]. + MarkdownText += token.Value; + + return changelogResult; + } + + private static bool isHeadingOneTextOrNewLine( + Result changelogResult, + Stack tokenStack + ) + { + if (changelogResult.IsFailure) { return false; } + + ChangelogToken token = tokenStack.Peek(); + + switch (token.Type) + { + case ChangelogTokenType.CloseParenthesis: + case ChangelogTokenType.CloseSquareBracket: + case ChangelogTokenType.Dash: + case ChangelogTokenType.OpenParenthesis: + case ChangelogTokenType.OpenSquareBracket: + case ChangelogTokenType.Text: + case ChangelogTokenType.NewLine: + { + return true; + } + default: + { + return false; + } + } + } + + private static bool isHeadingThree( + Result changelogResult, + Stack tokenStack + ) + { + if (changelogResult.IsFailure) { return false; } + + ChangelogToken token = tokenStack.Peek(); + + switch (token.Type) + { + case ChangelogTokenType.HeadingThree: + { + return true; + } + default: + { + return false; + } + } + } + + private static bool isHeadingThreeTextOrNewLine( + Result changelogResult, + Stack tokenStack + ) + { + if (changelogResult.IsFailure) { return false; } + + ChangelogToken token = tokenStack.Peek(); + + switch (token.Type) + { + case ChangelogTokenType.Dash: + { + int sectionCollectionCount = + changelogResult.Value. + SectionCollection.Count; + + int subSectionCollectionCount = + changelogResult.Value. + SectionCollection[sectionCollectionCount - 1]. + SubSectionCollection.Count; + + changelogResult.Value. + SectionCollection[sectionCollectionCount - 1]. + SubSectionCollection[subSectionCollectionCount - 1]. + ItemCollection. + Add(new ChangelogSubSectionItem()); + + return true; + } + + case ChangelogTokenType.CloseParenthesis: + case ChangelogTokenType.CloseSquareBracket: + case ChangelogTokenType.OpenParenthesis: + case ChangelogTokenType.OpenSquareBracket: + case ChangelogTokenType.Text: + case ChangelogTokenType.Space: + case ChangelogTokenType.NewLine: + { + return true; + } + default: + { + return false; + } + } + } + + private static bool isHeadingThreeUnreleasedTextOrNewLine( + Result changelogResult, + Stack tokenStack + ) + { + if (changelogResult.IsFailure) { return false; } + + ChangelogToken token = tokenStack.Peek(); + + switch (token.Type) + { + case ChangelogTokenType.Dash: + { + int subSectionCollectionCount = + changelogResult.Value. + SectionUnreleased. + SubSectionCollection.Count; + + changelogResult.Value. + SectionUnreleased. + SubSectionCollection[subSectionCollectionCount - 1]. + ItemCollection. + Add(new ChangelogSubSectionItem()); + + return true; + } + + case ChangelogTokenType.CloseParenthesis: + case ChangelogTokenType.CloseSquareBracket: + case ChangelogTokenType.OpenParenthesis: + case ChangelogTokenType.OpenSquareBracket: + case ChangelogTokenType.Space: + case ChangelogTokenType.Text: + case ChangelogTokenType.NewLine: + { + return true; + } + default: + { + return false; + } + } + } + + private static bool isHeadingTwo( + Result changelogResult, + Stack tokenStack + ) + { + if (changelogResult.IsFailure) { return false; } + + ChangelogToken token = tokenStack.Peek(); + + switch (token.Type) + { + case ChangelogTokenType.HeadingTwo: + { + return true; + } + default: + { + return false; + } + } + } + + private static bool isNewLine( + Result changelogResult, + Stack tokenStack + ) + { + if (changelogResult.IsFailure) { return false; } + + ChangelogToken token = tokenStack.Peek(); + + switch (token.Type) + { + case ChangelogTokenType.NewLine: + { + return true; + } + default: + { + return false; + } + } + } + + private static bool isText( + Stack tokenStack + ) + { + ChangelogToken token = tokenStack.Peek(); + + switch (token.Type) + { + case ChangelogTokenType.CloseParenthesis: + case ChangelogTokenType.CloseSquareBracket: + case ChangelogTokenType.Dash: + case ChangelogTokenType.OpenParenthesis: + case ChangelogTokenType.OpenSquareBracket: + case ChangelogTokenType.Space: + case ChangelogTokenType.Text: + { + return true; + } + default: + { + return false; + } + } + } + + private static bool isTitle( + Result changelogResult, + Stack tokenStack + ) + { + if (changelogResult.IsFailure) { return false; } + + ChangelogToken token = tokenStack.Peek(); + + switch (token.Type) + { + case ChangelogTokenType.CloseParenthesis: + case ChangelogTokenType.CloseSquareBracket: + case ChangelogTokenType.Dash: + case ChangelogTokenType.OpenParenthesis: + case ChangelogTokenType.OpenSquareBracket: + case ChangelogTokenType.Space: + case ChangelogTokenType.Text: + { + return true; + } + + default: + { + return false; + } + } + } + + private static bool isEmptySectionUnreleased( + Result changelogResult + ) + { + if (changelogResult.IsFailure) { return false; } + + return string.IsNullOrEmpty( + changelogResult.Value.SectionUnreleased.MarkdownTitle); + } + + private static bool isEmptySectionCollection( + Result changelogResult + ) + { + if (changelogResult.IsFailure) { return false; } + + return changelogResult.Value.SectionCollection.Count == 0; + } + + private static Result parseCloseSquareBracket( + Result changelogResult, + Stack tokenStack + ) + { + if (changelogResult.IsFailure) { return changelogResult; } + + ChangelogToken token = tokenStack.Pop(); + + if (token.Type != ChangelogTokenType.CloseSquareBracket) + { + return Result.Failure( + $"No close square bracket. Error parsing text in line {token.LineNumber} / index {token.Index}."); + } + + return changelogResult; + } + + private static Result parseDash( + Result changelogResult, + Stack tokenStack + ) + { + if (changelogResult.IsFailure) { return changelogResult; } + + ChangelogToken token = tokenStack.Pop(); + + switch (token.Type) + { + case ChangelogTokenType.Dash: + { + } + break; + + default: + { + return Result.Failure( + $"No dash. Error parsing text in line {token.LineNumber} / index {token.Index}."); + } + } + + return changelogResult; + } + + private static Result parseDate( + Result changelogResult, + Stack tokenStack + ) + { + if (changelogResult.IsFailure) { return changelogResult; } + + ChangelogToken token = tokenStack.Pop(); + + switch (token.Type) + { + case ChangelogTokenType.Date: + { + int sectionCollectionCount = + changelogResult.Value. + SectionCollection.Count; + + changelogResult.Value. + SectionCollection[sectionCollectionCount - 1]. + MarkdownDate = token.Value; + } + break; + + default: + { + return Result.Failure( + $"No date. Error parsing text in line {token.LineNumber} / index {token.Index}."); + } + } + + return changelogResult; + } + + private static Result parseHeadingOne( + Result changelogResult, + Stack tokenStack + ) + { + ChangelogToken token = tokenStack.Pop(); + + if (token.Type != ChangelogTokenType.HeadingOne) + { + return Result.Failure( + $"No level one heading. Error parsing text in line {token.LineNumber} / index {token.Index}."); + } + + return changelogResult; + } + + private static Result parseHeadingThree( + Result changelogResult, + Stack tokenStack + ) + { + if (changelogResult.IsFailure) { return changelogResult; } + + tokenStack.Pop(); + + int sectionCollectionCount = + changelogResult.Value. + SectionCollection.Count; + + changelogResult.Value. + SectionCollection[sectionCollectionCount - 1]. + SubSectionCollection. + Add(new ChangelogSubSection()); + + return changelogResult; + } + + private static Result parseHeadingTwo( + Result changelogResult, + Stack tokenStack + ) + { + tokenStack.Pop(); + + return changelogResult; + } + + private static Result parseListTextNewLine( + Result changelogResult, + Stack tokenStack + ) + { + if (changelogResult.IsFailure) { return changelogResult; } + + ChangelogToken token = tokenStack.Pop(); + + if (token.Type != ChangelogTokenType.NewLine) + { + return Result.Failure( + $"No new line. Error parsing text in line {token.LineNumber} / index {token.Index}."); + } + + return changelogResult; + } + + private static Result parseNewLine( + Result changelogResult, + Stack tokenStack + ) + { + if (changelogResult.IsFailure) { return changelogResult; } + + ChangelogToken token = tokenStack.Pop(); + + if (token.Type != ChangelogTokenType.NewLine) + { + return Result.Failure( + $"No new line. Error parsing text in line {token.LineNumber} / index {token.Index}."); + } + + return changelogResult; + } + + private static Result parseOpenSquareBracket( + Result changelogResult, + Stack tokenStack + ) + { + if (changelogResult.IsFailure) { return changelogResult; } + + ChangelogToken token = tokenStack.Pop(); + + if (token.Type != ChangelogTokenType.OpenSquareBracket) + { + return Result.Failure( + $"No open square bracket. Error parsing text in line {token.LineNumber} / index {token.Index}."); + } + + return changelogResult; + } + + private static Result parseSequenceTerminator( + Result changelogResult, + Stack tokenStack + ) + { + if (changelogResult.IsFailure) { return changelogResult; } + + ChangelogToken token = tokenStack.Pop(); + + if (token.Type != ChangelogTokenType.SequenceTerminator) + { + return Result.Failure( + $"No sequence terminator. Error parsing text in line {token.LineNumber} / index {token.Index}."); + } + + return changelogResult; + } + + private static Result parseSpace( + Result changelogResult, + Stack tokenStack + ) + { + if (changelogResult.IsFailure) { return changelogResult; } + + ChangelogToken token = tokenStack.Pop(); + + if (token.Type != ChangelogTokenType.Space) + { + return Result.Failure( + $"No space. Error parsing text in line {token.LineNumber} / index {token.Index}."); + } + + return changelogResult; + } + + private static Result parseText( + Result changelogResult, + Stack tokenStack + ) + { + if (changelogResult.IsFailure) { return changelogResult; } + + bool first = true; + + while (true) + { + ChangelogToken token = tokenStack.Peek(); + + if (first == false && token.Type == ChangelogTokenType.NewLine) { break; } + if (first == false && token.Type == ChangelogTokenType.SequenceTerminator) { break; } + + first = false; + + token = tokenStack.Pop(); + + switch (token.Type) + { + case ChangelogTokenType.CloseParenthesis: + case ChangelogTokenType.CloseSquareBracket: + case ChangelogTokenType.Dash: + case ChangelogTokenType.OpenParenthesis: + case ChangelogTokenType.OpenSquareBracket: + case ChangelogTokenType.Space: + case ChangelogTokenType.Text: + { + changelogResult = + addTokenValueToText( + changelogResult, + token); + + if (changelogResult.IsFailure) { return changelogResult; } + } + break; + + default: + { + return Result.Failure( + $"Invalid text. Error parsing text in line {token.LineNumber} / index {token.Index}."); + } + } + } + + return changelogResult; + } + + private static Result parseTextNewLine( + Result changelogResult, + Stack tokenStack + ) + { + if (changelogResult.IsFailure) { return changelogResult; } + + ChangelogToken token = tokenStack.Pop(); + + if (token.Type != ChangelogTokenType.NewLine) + { + return Result.Failure( + $"No new line. Error parsing text in line {token.LineNumber} / index {token.Index}."); + } + + return addTokenValueToText( + changelogResult, + token); + } + + private static Result parseTitle( + Result changelogResult, + Stack tokenStack + ) + { + if (changelogResult.IsFailure) { return changelogResult; } + + bool first = true; + + while (true) + { + ChangelogToken token = tokenStack.Peek(); + + if (first == false && token.Type == ChangelogTokenType.NewLine) { break; } + + first = false; + + token = tokenStack.Pop(); + + switch (token.Type) + { + case ChangelogTokenType.CloseParenthesis: + case ChangelogTokenType.CloseSquareBracket: + case ChangelogTokenType.Dash: + case ChangelogTokenType.OpenParenthesis: + case ChangelogTokenType.OpenSquareBracket: + case ChangelogTokenType.Space: + case ChangelogTokenType.Text: + { + changelogResult = + addTokenValueToTitleOrSetType( + changelogResult, + token); + + if (changelogResult.IsFailure) { return changelogResult; } + } + break; + + default: + { + return Result.Failure( + $"Invalid title. Error parsing text in line {token.LineNumber} / index {token.Index}."); + } + } + } + + return changelogResult; + } + + private static Result parseUnreleased( + Result changelogResult, + Stack tokenStack + ) + { + if (changelogResult.IsFailure) { return changelogResult; } + + ChangelogToken token = tokenStack.Pop(); + + switch (token.Type) + { + case ChangelogTokenType.Text: + { + if (!string.Equals(token.Value, "Unreleased", StringComparison.Ordinal)) + { + return Result.Failure( + $"Invalid unreleased title. Error parsing text in line {token.LineNumber} / index {token.Index}."); + } + + changelogResult.Value.SectionUnreleased.MarkdownTitle = token.Value; + } + break; + + default: + { + return Result.Failure( + $"Invalid text. Error parsing text in line {token.LineNumber} / index {token.Index}."); + } + } + + return changelogResult; + } + + private static Result parseUnreleasedHeadingThree( + Result changelogResult, + Stack tokenStack + ) + { + if (changelogResult.IsFailure) { return changelogResult; } + + tokenStack.Pop(); + + changelogResult.Value. + SectionUnreleased. + SubSectionCollection. + Add(new ChangelogSubSection()); + + return changelogResult; + } + + private static Result parseUnreleasedText( + Result changelogResult, + Stack tokenStack + ) + { + if (changelogResult.IsFailure) { return changelogResult; } + + bool first = true; + + while (true) + { + ChangelogToken token = tokenStack.Peek(); + + if (first == false && token.Type == ChangelogTokenType.NewLine) { break; } + if (first == false && token.Type == ChangelogTokenType.SequenceTerminator) { break; } + + first = false; + + token = tokenStack.Pop(); + + switch (token.Type) + { + case ChangelogTokenType.CloseParenthesis: + case ChangelogTokenType.CloseSquareBracket: + case ChangelogTokenType.Dash: + case ChangelogTokenType.OpenParenthesis: + case ChangelogTokenType.OpenSquareBracket: + case ChangelogTokenType.Space: + case ChangelogTokenType.Text: + { + changelogResult = + addTokenValueToUnreleasedText( + changelogResult, + token); + + if (changelogResult.IsFailure) { return changelogResult; } + } + break; + + default: + { + return Result.Failure( + $"Invalid text. Error parsing text in line {token.LineNumber} / index {token.Index}."); + } + } + } + + return changelogResult; + } + + private static Result parseUnreleasedTitle( + Result changelogResult, + Stack tokenStack + ) + { + if (changelogResult.IsFailure) { return changelogResult; } + + bool first = true; + + while (true) + { + ChangelogToken token = tokenStack.Peek(); + + if (first == false && token.Type == ChangelogTokenType.NewLine) { break; } + + first = false; + + token = tokenStack.Pop(); + + switch (token.Type) + { + case ChangelogTokenType.CloseParenthesis: + case ChangelogTokenType.CloseSquareBracket: + case ChangelogTokenType.Dash: + case ChangelogTokenType.OpenParenthesis: + case ChangelogTokenType.OpenSquareBracket: + case ChangelogTokenType.Space: + case ChangelogTokenType.Text: + { + changelogResult = + setUnreleasedType( + changelogResult, + token); + + if (changelogResult.IsFailure) { return changelogResult; } + } + break; + + default: + { + return Result.Failure( + $"Invalid title. Error parsing text in line {token.LineNumber} / index {token.Index}."); + } + } + } + + return changelogResult; + } + + private static Result parseVersion( + Result changelogResult, + Stack tokenStack + ) + { + if (changelogResult.IsFailure) { return changelogResult; } + + ChangelogToken token = tokenStack.Pop(); + + switch (token.Type) + { + case ChangelogTokenType.Version: + { + changelogResult.Value. + SectionCollection. + Add(new ChangelogSection()); + + int sectionCollectionCount = + changelogResult.Value. + SectionCollection.Count; + + changelogResult.Value. + SectionCollection[sectionCollectionCount - 1]. + MarkdownVersion = token.Value; + } + break; + + default: + { + return Result.Failure( + $"Invalid version. Error parsing text in line {token.LineNumber} / index {token.Index}."); + } + } + + return changelogResult; + } + + private static Result setUnreleasedType( + Result changelogResult, + ChangelogToken token + ) + { + int subSectionUnreleasedCollectionCount = + changelogResult.Value. + SectionUnreleased. + SubSectionCollection.Count; + + if (!Enum.TryParse(token.Value, out ChangelogSubSectionType subSectionType)) + { + return Result.Failure( + $"Invalid subsection type. Error parsing text in line {token.LineNumber} / index {token.Index}."); + } + + for (int index = 0; index < changelogResult.Value.SectionUnreleased.SubSectionCollection.Count - 1; index++) + { + if (changelogResult.Value.SectionUnreleased.SubSectionCollection[index].Type == subSectionType) + { + return Result.Failure( + $"Subsection type already exists. Error parsing text in line {token.LineNumber} / index {token.Index}."); + } + } + + changelogResult.Value. + SectionUnreleased. + SubSectionCollection[subSectionUnreleasedCollectionCount - 1]. + Type = subSectionType; + + return changelogResult; + } + + private static Result trimText( + Result changelogResult + ) + { + if (changelogResult.IsFailure) { return changelogResult; } + + changelogResult.Value.MarkdownText = + changelogResult.Value.MarkdownText. + Trim(new char[] { '\r', '\n' }); + + return changelogResult; + } + + } + +} diff --git a/KeepAChangelogParser/Contracts/IChangelogTokenizer.cs b/KeepAChangelogParser/Contracts/IChangelogTokenizer.cs new file mode 100644 index 0000000..c5d4072 --- /dev/null +++ b/KeepAChangelogParser/Contracts/IChangelogTokenizer.cs @@ -0,0 +1,16 @@ +using KeepAChangelogParser.Models; +using System.Collections.Generic; + +namespace KeepAChangelogParser.Contracts +{ + + internal interface IChangelogTokenizer + { + + IEnumerable Tokenize( + string text + ); + + } + +} diff --git a/KeepAChangelogParser/IChangelogParser.cs b/KeepAChangelogParser/IChangelogParser.cs new file mode 100644 index 0000000..0a3cd72 --- /dev/null +++ b/KeepAChangelogParser/IChangelogParser.cs @@ -0,0 +1,16 @@ +using CSharpFunctionalExtensions; +using KeepAChangelogParser.Models; + +namespace KeepAChangelogParser +{ + + public interface IChangelogParser + { + + Result Parse( + string text + ); + + } + +} diff --git a/KeepAChangelogParser/KeepAChangelogParser.csproj b/KeepAChangelogParser/KeepAChangelogParser.csproj new file mode 100644 index 0000000..5cdb24c --- /dev/null +++ b/KeepAChangelogParser/KeepAChangelogParser.csproj @@ -0,0 +1,62 @@ + + + + Steven Huelsmeier + Copyright © Steven Huelsmeier 2020-2021 + Parser Library for KeepAChangeLog + KeepAChangeLogParser + KeepAChangeLogParser + en-US + 1.0.0.0 + net452;netcoreapp3.1;net5.0 + true + KeepAChangeLogParser + KeepAChangeLogParser.Signed + MIT + https://github.com/shuelsmeier/keepachangelogparser + https://github.com/shuelsmeier/keepachangelogparser/blob/main/CHANGELOG.md + false + KeepAChangeLog Parser + + true + true + snupkg + + true + true + Debug;Release;Release_Signed + latest + true + 9 + enable + + + + DEBUG;TRACE + + + + key.snk + + + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + + + + + + diff --git a/KeepAChangelogParser/KeepAChangelogParser.nuspec b/KeepAChangelogParser/KeepAChangelogParser.nuspec new file mode 100644 index 0000000..8ab60e4 --- /dev/null +++ b/KeepAChangelogParser/KeepAChangelogParser.nuspec @@ -0,0 +1,24 @@ + + + + KeepAChangelogParser + 1.0.0 + KeepAChangelogParser + Steven Huelsmeier + false + MIT + http://project_url_here_or_delete_this_line/ + + Parser for keep a changelog changelogs. + Initial Release. + $copyright$ + keep a changelog, parser + + + + + + + + + \ No newline at end of file diff --git a/KeepAChangelogParser/Models/Changelog.cs b/KeepAChangelogParser/Models/Changelog.cs new file mode 100644 index 0000000..b0c9c74 --- /dev/null +++ b/KeepAChangelogParser/Models/Changelog.cs @@ -0,0 +1,17 @@ +namespace KeepAChangelogParser.Models +{ + + public class Changelog + { + + public string MarkdownTitle { get; set; } = string.Empty; + + public string MarkdownText { get; set; } = string.Empty; + + public ChangelogSectionUnreleased SectionUnreleased { get; } = new ChangelogSectionUnreleased(); + + public ChangelogSectionCollection SectionCollection { get; } = new ChangelogSectionCollection(); + + } + +} diff --git a/KeepAChangelogParser/Models/ChangelogSection.cs b/KeepAChangelogParser/Models/ChangelogSection.cs new file mode 100644 index 0000000..9c12ff6 --- /dev/null +++ b/KeepAChangelogParser/Models/ChangelogSection.cs @@ -0,0 +1,15 @@ +namespace KeepAChangelogParser.Models +{ + + public class ChangelogSection + { + + public string MarkdownVersion { get; set; } = string.Empty; + + public string MarkdownDate { get; set; } = string.Empty; + + public ChangelogSubSectionCollection SubSectionCollection { get; } = new ChangelogSubSectionCollection(); + + } + +} \ No newline at end of file diff --git a/KeepAChangelogParser/Models/ChangelogSectionCollection.cs b/KeepAChangelogParser/Models/ChangelogSectionCollection.cs new file mode 100644 index 0000000..46c2c7b --- /dev/null +++ b/KeepAChangelogParser/Models/ChangelogSectionCollection.cs @@ -0,0 +1,11 @@ +using System.Collections.Generic; + +namespace KeepAChangelogParser.Models +{ + + public class ChangelogSectionCollection : + List + { + } + +} \ No newline at end of file diff --git a/KeepAChangelogParser/Models/ChangelogSectionUnreleased.cs b/KeepAChangelogParser/Models/ChangelogSectionUnreleased.cs new file mode 100644 index 0000000..ea23de5 --- /dev/null +++ b/KeepAChangelogParser/Models/ChangelogSectionUnreleased.cs @@ -0,0 +1,13 @@ +namespace KeepAChangelogParser.Models +{ + + public class ChangelogSectionUnreleased + { + + public string MarkdownTitle { get; set; } = string.Empty; + + public ChangelogSubSectionCollection SubSectionCollection { get; } = new ChangelogSubSectionCollection(); + + } + +} diff --git a/KeepAChangelogParser/Models/ChangelogSubSection.cs b/KeepAChangelogParser/Models/ChangelogSubSection.cs new file mode 100644 index 0000000..5680de3 --- /dev/null +++ b/KeepAChangelogParser/Models/ChangelogSubSection.cs @@ -0,0 +1,13 @@ +namespace KeepAChangelogParser.Models +{ + + public class ChangelogSubSection + { + + public ChangelogSubSectionType Type { get; set; } + + public ChangelogSubSectionItemCollection ItemCollection { get; } = new ChangelogSubSectionItemCollection(); + + } + +} diff --git a/KeepAChangelogParser/Models/ChangelogSubSectionCollection.cs b/KeepAChangelogParser/Models/ChangelogSubSectionCollection.cs new file mode 100644 index 0000000..3e1a694 --- /dev/null +++ b/KeepAChangelogParser/Models/ChangelogSubSectionCollection.cs @@ -0,0 +1,11 @@ +using System.Collections.Generic; + +namespace KeepAChangelogParser.Models +{ + + public class ChangelogSubSectionCollection : + List + { + } + +} \ No newline at end of file diff --git a/KeepAChangelogParser/Models/ChangelogSubSectionItem.cs b/KeepAChangelogParser/Models/ChangelogSubSectionItem.cs new file mode 100644 index 0000000..2f24d59 --- /dev/null +++ b/KeepAChangelogParser/Models/ChangelogSubSectionItem.cs @@ -0,0 +1,11 @@ +namespace KeepAChangelogParser.Models +{ + + public class ChangelogSubSectionItem + { + + public string MarkdownText { get; set; } = string.Empty; + + } + +} diff --git a/KeepAChangelogParser/Models/ChangelogSubSectionItemCollection.cs b/KeepAChangelogParser/Models/ChangelogSubSectionItemCollection.cs new file mode 100644 index 0000000..cdd0683 --- /dev/null +++ b/KeepAChangelogParser/Models/ChangelogSubSectionItemCollection.cs @@ -0,0 +1,11 @@ +using System.Collections.Generic; + +namespace KeepAChangelogParser.Models +{ + + public class ChangelogSubSectionItemCollection : + List + { + } + +} diff --git a/KeepAChangelogParser/Models/ChangelogSubSectionType.cs b/KeepAChangelogParser/Models/ChangelogSubSectionType.cs new file mode 100644 index 0000000..1451fa6 --- /dev/null +++ b/KeepAChangelogParser/Models/ChangelogSubSectionType.cs @@ -0,0 +1,14 @@ +namespace KeepAChangelogParser.Models +{ + + public enum ChangelogSubSectionType + { + Added, // for new features + Changed, // for changes in existing functionality + Deprecated, // for soon-to-be removed features + Removed, // for now removed features + Fixed, // for any bug fixes + Security, // in case of vulnerabilities + } + +} diff --git a/KeepAChangelogParser/Models/ChangelogToken.cs b/KeepAChangelogParser/Models/ChangelogToken.cs new file mode 100644 index 0000000..1da4701 --- /dev/null +++ b/KeepAChangelogParser/Models/ChangelogToken.cs @@ -0,0 +1,61 @@ +using System.Diagnostics; + +namespace KeepAChangelogParser.Models +{ + + [DebuggerDisplay("{debuggerDisplay,nq}")] + internal class ChangelogToken + { + + public ChangelogTokenType Type { get; } + + public int LineNumber { get; } + + public int Index { get; } + + public string Value { get; } + + public ChangelogToken( + ChangelogTokenType type + ) + { + this.Type = type; + this.Value = string.Empty; + this.LineNumber = -1; + this.Index = -1; + } + + public ChangelogToken( + ChangelogTokenType type, + string value, + int lineNumber, + int index + ) + { + this.Type = type; + this.Value = value; + this.LineNumber = lineNumber; + this.Index = index; + } + + private string debuggerDisplay + { + get + { + string debuggerDisplay = string.Empty; + + debuggerDisplay += $"\"{this.Type}\""; + debuggerDisplay += " | "; + debuggerDisplay += $"\"{this.LineNumber}\""; + debuggerDisplay += " | "; + debuggerDisplay += $"\"{this.Index}\""; + debuggerDisplay += " | "; + debuggerDisplay += $"\"{this.Value}\""; + + return debuggerDisplay; + } + } + + } + +} diff --git a/KeepAChangelogParser/Models/ChangelogTokenDefinition.cs b/KeepAChangelogParser/Models/ChangelogTokenDefinition.cs new file mode 100644 index 0000000..823f8b3 --- /dev/null +++ b/KeepAChangelogParser/Models/ChangelogTokenDefinition.cs @@ -0,0 +1,28 @@ +using System.Text.RegularExpressions; + +namespace KeepAChangelogParser.Models +{ + + internal class ChangelogTokenDefinition + { + + public Regex Regex { get; } + + public ChangelogTokenType Type { get; } + + public int Precedence { get; } + + public ChangelogTokenDefinition( + ChangelogTokenType type, + string regexPattern, + int precedence + ) + { + this.Type = type; + this.Regex = new Regex(regexPattern, RegexOptions.IgnoreCase | RegexOptions.Compiled); + this.Precedence = precedence; + } + + } + +} diff --git a/KeepAChangelogParser/Models/ChangelogTokenMatch.cs b/KeepAChangelogParser/Models/ChangelogTokenMatch.cs new file mode 100644 index 0000000..1c90653 --- /dev/null +++ b/KeepAChangelogParser/Models/ChangelogTokenMatch.cs @@ -0,0 +1,21 @@ +namespace KeepAChangelogParser.Models +{ + + internal class ChangelogTokenMatch + { + + public int LineNumber { get; set; } + + public ChangelogTokenType Type { get; set; } + + public string Value { get; set; } = string.Empty; + + public int StartIndex { get; set; } + + public int EndIndex { get; set; } + + public int Precedence { get; set; } + + } + +} diff --git a/KeepAChangelogParser/Models/ChangelogTokenType.cs b/KeepAChangelogParser/Models/ChangelogTokenType.cs new file mode 100644 index 0000000..c5ef889 --- /dev/null +++ b/KeepAChangelogParser/Models/ChangelogTokenType.cs @@ -0,0 +1,22 @@ +namespace KeepAChangelogParser.Models +{ + + internal enum ChangelogTokenType + { + CloseParenthesis, + CloseSquareBracket, + Dash, + Date, + HeadingOne, + HeadingTwo, + HeadingThree, + NewLine, + OpenParenthesis, + OpenSquareBracket, + SequenceTerminator, + Space, + Text, + Version + } + +} diff --git a/KeepAChangelogParser/Properties/.gitkeep b/KeepAChangelogParser/Properties/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/KeepAChangelogParser/PublicAPI.Shipped.txt b/KeepAChangelogParser/PublicAPI.Shipped.txt new file mode 100644 index 0000000..547edb5 --- /dev/null +++ b/KeepAChangelogParser/PublicAPI.Shipped.txt @@ -0,0 +1,48 @@ +#nullable enable +KeepAChangelogParser.ChangelogParser +KeepAChangelogParser.ChangelogParser.ChangelogParser() -> void +KeepAChangelogParser.ChangelogParser.Parse(string! text) -> CSharpFunctionalExtensions.Result +KeepAChangelogParser.IChangelogParser +KeepAChangelogParser.IChangelogParser.Parse(string! text) -> CSharpFunctionalExtensions.Result +KeepAChangelogParser.Models.Changelog +KeepAChangelogParser.Models.Changelog.Changelog() -> void +KeepAChangelogParser.Models.Changelog.MarkdownText.get -> string! +KeepAChangelogParser.Models.Changelog.MarkdownText.set -> void +KeepAChangelogParser.Models.Changelog.MarkdownTitle.get -> string! +KeepAChangelogParser.Models.Changelog.MarkdownTitle.set -> void +KeepAChangelogParser.Models.Changelog.SectionCollection.get -> KeepAChangelogParser.Models.ChangelogSectionCollection! +KeepAChangelogParser.Models.Changelog.SectionUnreleased.get -> KeepAChangelogParser.Models.ChangelogSectionUnreleased! +KeepAChangelogParser.Models.ChangelogSection +KeepAChangelogParser.Models.ChangelogSection.ChangelogSection() -> void +KeepAChangelogParser.Models.ChangelogSection.MarkdownDate.get -> string! +KeepAChangelogParser.Models.ChangelogSection.MarkdownDate.set -> void +KeepAChangelogParser.Models.ChangelogSection.MarkdownVersion.get -> string! +KeepAChangelogParser.Models.ChangelogSection.MarkdownVersion.set -> void +KeepAChangelogParser.Models.ChangelogSection.SubSectionCollection.get -> KeepAChangelogParser.Models.ChangelogSubSectionCollection! +KeepAChangelogParser.Models.ChangelogSectionCollection +KeepAChangelogParser.Models.ChangelogSectionCollection.ChangelogSectionCollection() -> void +KeepAChangelogParser.Models.ChangelogSectionUnreleased +KeepAChangelogParser.Models.ChangelogSectionUnreleased.ChangelogSectionUnreleased() -> void +KeepAChangelogParser.Models.ChangelogSectionUnreleased.MarkdownTitle.get -> string! +KeepAChangelogParser.Models.ChangelogSectionUnreleased.MarkdownTitle.set -> void +KeepAChangelogParser.Models.ChangelogSectionUnreleased.SubSectionCollection.get -> KeepAChangelogParser.Models.ChangelogSubSectionCollection! +KeepAChangelogParser.Models.ChangelogSubSection +KeepAChangelogParser.Models.ChangelogSubSection.ChangelogSubSection() -> void +KeepAChangelogParser.Models.ChangelogSubSection.ItemCollection.get -> KeepAChangelogParser.Models.ChangelogSubSectionItemCollection! +KeepAChangelogParser.Models.ChangelogSubSection.Type.get -> KeepAChangelogParser.Models.ChangelogSubSectionType +KeepAChangelogParser.Models.ChangelogSubSection.Type.set -> void +KeepAChangelogParser.Models.ChangelogSubSectionCollection +KeepAChangelogParser.Models.ChangelogSubSectionCollection.ChangelogSubSectionCollection() -> void +KeepAChangelogParser.Models.ChangelogSubSectionItem +KeepAChangelogParser.Models.ChangelogSubSectionItem.ChangelogSubSectionItem() -> void +KeepAChangelogParser.Models.ChangelogSubSectionItem.MarkdownText.get -> string! +KeepAChangelogParser.Models.ChangelogSubSectionItem.MarkdownText.set -> void +KeepAChangelogParser.Models.ChangelogSubSectionItemCollection +KeepAChangelogParser.Models.ChangelogSubSectionItemCollection.ChangelogSubSectionItemCollection() -> void +KeepAChangelogParser.Models.ChangelogSubSectionType +KeepAChangelogParser.Models.ChangelogSubSectionType.Added = 0 -> KeepAChangelogParser.Models.ChangelogSubSectionType +KeepAChangelogParser.Models.ChangelogSubSectionType.Changed = 1 -> KeepAChangelogParser.Models.ChangelogSubSectionType +KeepAChangelogParser.Models.ChangelogSubSectionType.Deprecated = 2 -> KeepAChangelogParser.Models.ChangelogSubSectionType +KeepAChangelogParser.Models.ChangelogSubSectionType.Fixed = 4 -> KeepAChangelogParser.Models.ChangelogSubSectionType +KeepAChangelogParser.Models.ChangelogSubSectionType.Removed = 3 -> KeepAChangelogParser.Models.ChangelogSubSectionType +KeepAChangelogParser.Models.ChangelogSubSectionType.Security = 5 -> KeepAChangelogParser.Models.ChangelogSubSectionType diff --git a/KeepAChangelogParser/PublicAPI.Unshipped.txt b/KeepAChangelogParser/PublicAPI.Unshipped.txt new file mode 100644 index 0000000..5f28270 --- /dev/null +++ b/KeepAChangelogParser/PublicAPI.Unshipped.txt @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/KeepAChangelogParser/Services/ChangelogTokenizer.cs b/KeepAChangelogParser/Services/ChangelogTokenizer.cs new file mode 100644 index 0000000..17b4942 --- /dev/null +++ b/KeepAChangelogParser/Services/ChangelogTokenizer.cs @@ -0,0 +1,163 @@ +using KeepAChangelogParser.Contracts; +using KeepAChangelogParser.Models; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text.RegularExpressions; + +namespace KeepAChangelogParser.Services +{ + + internal class ChangelogTokenizer : + IChangelogTokenizer + { + + private readonly List tokenDefinitionCollection = + new List() + { + new ChangelogTokenDefinition(ChangelogTokenType.CloseParenthesis, "\\)", 1), + new ChangelogTokenDefinition(ChangelogTokenType.CloseSquareBracket, "\\]", 1), + new ChangelogTokenDefinition(ChangelogTokenType.Dash, "-", 1), + new ChangelogTokenDefinition(ChangelogTokenType.Date, "\\d\\d\\d\\d-\\d\\d-\\d\\d", 1), + new ChangelogTokenDefinition(ChangelogTokenType.HeadingOne, "#", 3), + new ChangelogTokenDefinition(ChangelogTokenType.HeadingTwo, "##", 2), + new ChangelogTokenDefinition(ChangelogTokenType.HeadingThree, "###", 1), + new ChangelogTokenDefinition(ChangelogTokenType.OpenParenthesis, "\\(", 1), + new ChangelogTokenDefinition(ChangelogTokenType.OpenSquareBracket, "\\[", 1), + new ChangelogTokenDefinition(ChangelogTokenType.Space, " ", 1), + new ChangelogTokenDefinition(ChangelogTokenType.Text, "[^\\[|^\\]|^\\(|^\\)]+", 99), + new ChangelogTokenDefinition(ChangelogTokenType.Version, "(0|[1-9]\\d*)\\.(0|[1-9]\\d*)\\.(0|[1-9]\\d*)(?:-((?:0|[1-9]\\d*|\\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\\.(?:0|[1-9]\\d*|\\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\\+([0-9a-zA-Z-]+(?:\\.[0-9a-zA-Z-]+)*))?", 1), + }; + + public IEnumerable Tokenize( + string text + ) + { + if (string.IsNullOrEmpty(text)) + { + return new List(); + } + + List tokenCollection = + new List(); + + List lineCollection = + text. + Split(new[] { Environment.NewLine }, StringSplitOptions.None). + ToList(); + + for (int lineNumber = 1; lineNumber <= lineCollection.Count; lineNumber++) + { + if (lineNumber > 1) + { + ChangelogToken token = + new ChangelogToken( + ChangelogTokenType.NewLine, + Environment.NewLine, + lineNumber, + lineCollection[lineNumber - 1].Length); + + tokenCollection. + Add(token); + } + + int startIndex = 0; + + while (startIndex != lineCollection[lineNumber - 1].Length) + { + List tokenMatchCollection = + this.findTokenMatches( + lineNumber, + lineCollection[lineNumber - 1], + startIndex); + + List> tokenMatchByStartIndexCollection = + tokenMatchCollection. + GroupBy(x => x.StartIndex). + OrderBy(x => x.Key). + ToList(); + + ChangelogTokenMatch tokenMatch = + tokenMatchByStartIndexCollection[0]. + OrderBy(x => x.Precedence). + First(); + + ChangelogToken token = + new ChangelogToken( + tokenMatch.Type, + tokenMatch.Value, + lineNumber, + tokenMatch.StartIndex + 1); + + tokenCollection. + Add(token); + + startIndex = + tokenMatch.EndIndex; + } + } + + tokenCollection.Add( + new ChangelogToken( + ChangelogTokenType.NewLine, + Environment.NewLine, + lineCollection.Count, + lineCollection[lineCollection.Count - 1].Length)); + + tokenCollection.Add( + new ChangelogToken( + ChangelogTokenType.SequenceTerminator)); + + return tokenCollection; + } + + private static IEnumerable findMatches( + ChangelogTokenDefinition tokenDefinition, + int lineNumber, + string inputString, + int startIndex + ) + { + MatchCollection matches = + tokenDefinition.Regex. + Matches( + inputString, + startIndex); + + for (int i = 0; i < matches.Count; i++) + { + yield return new ChangelogTokenMatch() + { + LineNumber = lineNumber, + StartIndex = matches[i].Index, + EndIndex = matches[i].Index + matches[i].Length, + Type = tokenDefinition.Type, + Value = matches[i].Value, + Precedence = tokenDefinition.Precedence + }; + } + } + + private List findTokenMatches( + int lineNumber, + string text, + int startIndex + ) + { + List tokenMatchCollection = + new List(); + + foreach (ChangelogTokenDefinition tokenDefinition in this.tokenDefinitionCollection) + { + tokenMatchCollection. + AddRange( + findMatches(tokenDefinition, lineNumber, text, startIndex). + ToList()); + } + + return tokenMatchCollection; + } + + } + +} diff --git a/KeepAChangelogParser/key.snk b/KeepAChangelogParser/key.snk new file mode 100644 index 0000000000000000000000000000000000000000..bd7375755698faba882513861644ca6a5d04847f GIT binary patch literal 596 zcmV-a0;~N80ssI2Bme+XQ$aES1ONa50097La3h(H59Sw^dd0$;9K#7$@tD?$<~A0@ zUbFKcWfhPY;v3lvhM(0sNw4kk2FIit;R0>XWdQmQpZ-*PfSY)~XV>Vpb8a1xPI3 z*nshB$tyq~Y+~;3VK;vc(?5Q8`&^7Pxhu%~2n;iWjk9#L%4Qn6${(4}d&&Wy!t}v` z>6Kzn2+M#j9H^Ws%#14JQ2Hef0)6hoXT`)XdLr(tgy+n67p2-gOO>HIgVf(NOsI;Z z&ybB##UQuB6xIIv>`gzWPN?6&Zmc6Wt?!r^eLkQoi_( zsgnz3VM8?e@>r$Tqcv~A)TA+9F;TVeptDyhw4wXzL}1(0eS6G~xPY4IxphoQ7Z z+x{?2r6f0)<5E*TX%$ZDfaNP9B3Ch~l3tsE)p)%X#Yccd#_9xQQSs^YFBCisi{`w2 idz!Pvcq_F!wq~g5hVK5FO;y3WzUn}xuAXq|5Ql^r+$cH# literal 0 HcmV?d00001 diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..cf2d2be --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2020 Steven Hülsmeier + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..d154e3c --- /dev/null +++ b/README.md @@ -0,0 +1,24 @@ +# keepachangelogparser + +[![Build Status](https://dev.azure.com/nakrul0789/KeepAChangelogParser/_apis/build/status/shuelsmeier.keepachangelogparser?branchName=main)](https://dev.azure.com/nakrul0789/KeepAChangelogParser/_build/latest?definitionId=1&branchName=main) [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://github.com/shuelsmeier/keepachangelogparser/blob/master/LICENSE) + +keepachangelogparser is a parser for changelogs defined by the [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) format. + +## Issues & Contributions + +If you find a bug or have a feature request, please report them at this repository's issues section. See the [CONTRIBUTING GUIDE](CONTRIBUTING.md) for details on building and contributing to this project. + +## Maintainer + +Author and owner +* [Steven Huelsmeier](https://github.com/shuelsmeier) + +## Code of Conduct + +This project has adopted the code of conduct defined by the Contributor Covenant to clarify expected behavior in our community. + +For more information, see the [.NET Foundation Code of Conduct](https://dotnetfoundation.org/code-of-conduct). + +## License + +This project is licensed under the MIT license. See the [LICENSE](LICENSE) file for more info. diff --git a/azure-pipelines.yml b/azure-pipelines.yml new file mode 100644 index 0000000..9e53c52 --- /dev/null +++ b/azure-pipelines.yml @@ -0,0 +1,46 @@ +# .NET Desktop +# Build and run tests for .NET Desktop or Windows classic desktop solutions. +# Add steps that publish symbols, save build artifacts, and more: +# https://docs.microsoft.com/azure/devops/pipelines/apps/windows/dot-net + +trigger: +- main + +pool: + vmImage: 'windows-latest' + +variables: + solution: '**/*.sln' + buildPlatform: 'Any CPU' + buildConfiguration: 'Debug' + +steps: +- task: NuGetToolInstaller@1 + +- task: NuGetCommand@2 + inputs: + restoreSolution: '$(solution)' + +- task: VSBuild@1 + inputs: + solution: '$(solution)' + platform: '$(buildPlatform)' + configuration: '$(buildConfiguration)' + +- task: VisualStudioTestPlatformInstaller@1 + inputs: + packageFeedSelector: 'nugetOrg' + versionSelector: 'latestStable' + +- task: VSTest@2 + inputs: + testSelector: 'testAssemblies' + testAssemblyVer2: + '.\KeepAChangelogParser.Tests\bin\Debug\net5.0\KeepAChangelogParser.Tests.dll' + #'**\KeepAChangelogParser.Tests.dll' + vstestLocationMethod: 'version' + vsTestVersion: 'toolsInstaller' + runInParallel: true + codeCoverageEnabled: true + platform: '$(buildPlatform)' + configuration: '$(buildConfiguration)'