From 1507c1e6f4b23e8401f1ab28667c85e8111532b2 Mon Sep 17 00:00:00 2001 From: Arthur van de Vondervoort Date: Mon, 4 May 2026 19:49:45 +0200 Subject: [PATCH] fix(FC0004): use AZ Dev Tools-compatible permission sorting Replace OrdinalIgnoreCase string comparison with natural/alphanumeric sort that matches AZ Dev Tools' PermissionComparer behavior: - table/tabledata types sort first, grouped by name - Remaining types sort alphabetically by type keyword - Object names use natural sort (numeric chunks as integers) - Text chunks compared with spaces stripped and InvariantCultureIgnoreCase This fixes false positives on code formatted by AZ Dev Tools' Sort Permissions action. Closes #245 Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- ...rmission-declaration-order.instructions.md | 44 +++++-- .../Permissions/NaturalNameComparer.cs | 108 ++++++++++++++++++ .../Permissions/PermissionSyntaxHelper.cs | 67 ++++++++--- .../HasDiagnostic/UnsortedMixedTypes.al | 6 +- .../HasDiagnostic/UnsortedNumeric.al | 20 ++++ .../HasDiagnostic/UnsortedTableNotFirst.al | 20 ++++ .../HasFix/ReorderMixedTypes/current.al | 6 +- .../HasFix/ReorderMixedTypes/expected.al | 6 +- .../NoDiagnostic/AlreadySorted.al | 6 +- .../NoDiagnostic/DotsBeforeLetters.al | 15 +++ .../NoDiagnostic/NaturalNumericSort.al | 20 ++++ .../NoDiagnostic/TableTypesFirst.al | 26 +++++ .../PermissionDeclarationOrder.cs | 5 + 13 files changed, 306 insertions(+), 43 deletions(-) create mode 100644 src/ALCops.Common/Permissions/NaturalNameComparer.cs create mode 100644 src/ALCops.FormattingCop.Test/Rules/PermissionDeclarationOrder/HasDiagnostic/UnsortedNumeric.al create mode 100644 src/ALCops.FormattingCop.Test/Rules/PermissionDeclarationOrder/HasDiagnostic/UnsortedTableNotFirst.al create mode 100644 src/ALCops.FormattingCop.Test/Rules/PermissionDeclarationOrder/NoDiagnostic/DotsBeforeLetters.al create mode 100644 src/ALCops.FormattingCop.Test/Rules/PermissionDeclarationOrder/NoDiagnostic/NaturalNumericSort.al create mode 100644 src/ALCops.FormattingCop.Test/Rules/PermissionDeclarationOrder/NoDiagnostic/TableTypesFirst.al diff --git a/.github/instructions/fc0004-permission-declaration-order.instructions.md b/.github/instructions/fc0004-permission-declaration-order.instructions.md index 7fa499d..221f337 100644 --- a/.github/instructions/fc0004-permission-declaration-order.instructions.md +++ b/.github/instructions/fc0004-permission-declaration-order.instructions.md @@ -22,7 +22,8 @@ Detects `Permissions` property entries that are not sorted alphabetically by (ty ``` src/ALCops.Common/ └── Permissions/ - └── PermissionSyntaxHelper.cs # Shared sort logic, multi-line builder + ├── NaturalNameComparer.cs # Natural/alphanumeric name comparison (AZ-compatible) + └── PermissionSyntaxHelper.cs # Shared sort logic, multi-line builder src/ALCops.FormattingCop/ ├── Analyzers/ @@ -33,11 +34,21 @@ src/ALCops.FormattingCop/ ### Sort order -Entries are sorted by two keys: -1. **Type keyword** (alphabetical, case-insensitive): `codeunit` < `page` < `query` < `record` < `report` < `table` < `tabledata` < `xmlport` -2. **Object name** (alphabetical, case-insensitive) within the same type +Entries are sorted using AZ Dev Tools-compatible ordering (matches `PermissionComparer` from al-code-outline): -Uses `StringComparison.OrdinalIgnoreCase` for both comparisons. Note that ordinal comparison means `(` < `+` < `0` < `A`, which may differ from culture-specific ordering. +1. **Table types first**: `table` and `tabledata` entries are grouped before all other types + - Within table types, entries are sorted by **object name** first (natural/alphanumeric comparison) + - For the same object name, `table` sorts before `tabledata` +2. **Remaining types alphabetically**: `codeunit` < `page` < `query` < `report` < `xmlport` + - Within the same type, sorted by **object name** (natural/alphanumeric comparison) + +**Name comparison** uses `NaturalNameComparer` (natural/alphanumeric sort): +- Splits names into text and numeric chunks +- Text chunks: spaces stripped, compared with `StringComparison.InvariantCultureIgnoreCase` +- Numeric chunks: compared as integers (`"Item 2"` < `"Item 10"`) +- Tiebreaker: shorter string first + +Uses `StringComparison.OrdinalIgnoreCase` for type keyword comparison only (fixed ASCII vocabulary). ### Analysis flow @@ -59,7 +70,11 @@ Uses `StringComparison.OrdinalIgnoreCase` for both comparisons. Note that ordina | `RegisterCompilationAction` | Same pattern as AC0032; iterates all objects in one pass | | One diagnostic per object, not per entry | The fix reorders the entire list; per-entry diagnostics would be noise | | Diagnostic on `PropertySyntax` node | Ensures CodeFix can find the node via `FindNode` + ancestor/descendant traversal | -| Type+name sorting applied globally (affects AC0031/AC0032) | `ArePermissionsSorted` was refactored from name-only to type+name; consistent behavior across all permission rules | +| AZ Dev Tools-compatible sort order | Prevents false positives on code formatted by AZ Dev Tools' "Sort Permissions" | +| table/tabledata first, rest alphabetical | Matches AZ's fixed priority map; more future-proof for new permission types | +| Natural/alphanumeric name comparison | Matches AZ's `AlphanumComparatorFast`; handles numeric suffixes correctly | +| `InvariantCultureIgnoreCase` for names | Handles accented chars and punctuation weight; matches AZ behavior | +| Space stripping in text chunks | Matches AZ behavior; prevents spaces from affecting sort order | | CodeFix always outputs multi-line for 2+ entries | Consistent formatting; single-line permissions are hard to scan | | Single entry stays single-line | No formatting benefit from multi-line with one entry | | Permission chars casing preserved | Each entry keeps its original `r`/`R`/`rimd`/`RIMD`/`X` casing | @@ -93,13 +108,18 @@ var propertySyntax = node as PropertySyntax ## Test coverage -**HasDiagnostic (4 cases):** UnsortedTabledata, UnsortedMixedTypes, UnsortedSingleType, UnsortedCodeunit. -**NoDiagnostic (4 cases):** AlreadySorted, SingleEntry, NoPermissionsProperty, SortedTabledata. +**HasDiagnostic (6 cases):** UnsortedTabledata, UnsortedMixedTypes, UnsortedSingleType, UnsortedCodeunit, UnsortedNumeric, UnsortedTableNotFirst. +**NoDiagnostic (7 cases):** AlreadySorted, SingleEntry, NoPermissionsProperty, SortedTabledata, DotsBeforeLetters, NaturalNumericSort, TableTypesFirst. **HasFix (4 cases):** ReorderTabledata, ReorderMixedTypes, SingleLineToMultiLine, PreserveCasing. ## Refactoring impact -The `PermissionSyntaxHelper.ArePermissionsSorted` method was changed from name-only to type+name comparison. This affects AC0031's `FindInsertionIndex` (used by its CodeFix to decide alphabetical vs append insertion). The behavioral change is: -- A list sorted by name but not by type is now detected as "unsorted" -- AC0031's CodeFix will append (instead of inserting alphabetically) for such lists -- This is the correct behavior since type+name is the canonical sort order +The `PermissionSyntaxHelper` sort logic was changed from simple `OrdinalIgnoreCase` to AZ Dev Tools-compatible ordering. This affects: +- **FC0004**: Uses the new sort order for detection and code fix +- **AC0031's `FindInsertionIndex`**: Uses `ComparePermissionEntries` for insertion point calculation +- **AC0032**: Uses `ArePermissionsSorted` to determine if a list is sorted + +The behavioral changes: +- table/tabledata types now sort before other types (previously alphabetical: `codeunit` came first) +- Names use natural/alphanumeric comparison (previously ordinal: "Item 10" came before "Item 2") +- Spaces in names are ignored during comparison (previously significant) diff --git a/src/ALCops.Common/Permissions/NaturalNameComparer.cs b/src/ALCops.Common/Permissions/NaturalNameComparer.cs new file mode 100644 index 0000000..eba1707 --- /dev/null +++ b/src/ALCops.Common/Permissions/NaturalNameComparer.cs @@ -0,0 +1,108 @@ +namespace ALCops.Common.Permissions; + +/// +/// Natural/alphanumeric string comparer for AL object names. +/// Splits strings into text and numeric chunks: +/// - Text chunks are compared with spaces stripped and InvariantCultureIgnoreCase. +/// - Numeric chunks are compared as integers. +/// - Shorter string wins as tiebreaker when all chunks are equal. +/// Functionally equivalent to AZ AL Dev Tools' AlphanumComparatorFast. +/// +public static class NaturalNameComparer +{ + /// + /// Compares two object names using natural/alphanumeric sorting. + /// Null or empty strings sort after non-empty strings. + /// + public static int Compare(string? x, string? y) + { + bool xEmpty = string.IsNullOrWhiteSpace(x); + bool yEmpty = string.IsNullOrWhiteSpace(y); + + if (xEmpty && yEmpty) + return 0; + if (xEmpty) + return 1; + if (yEmpty) + return -1; + + int len1 = x!.Length; + int len2 = y!.Length; + int marker1 = 0; + int marker2 = 0; + + while (marker1 < len1 && marker2 < len2) + { + char ch1 = x[marker1]; + char ch2 = y[marker2]; + + // Collect chunk from x + int chunkStart1 = marker1; + bool isDigit1 = char.IsDigit(ch1); + while (marker1 < len1 && char.IsDigit(x[marker1]) == isDigit1) + marker1++; + + // Collect chunk from y + int chunkStart2 = marker2; + bool isDigit2 = char.IsDigit(ch2); + while (marker2 < len2 && char.IsDigit(y[marker2]) == isDigit2) + marker2++; + + int result; + if (isDigit1 && isDigit2) + { + // Both numeric: compare as integers + long num1 = ParseLong(x, chunkStart1, marker1); + long num2 = ParseLong(y, chunkStart2, marker2); + result = num1.CompareTo(num2); + } + else + { + // At least one is text: compare with spaces stripped, InvariantCultureIgnoreCase + var str1 = StripSpaces(x, chunkStart1, marker1); + var str2 = StripSpaces(y, chunkStart2, marker2); + result = string.Compare(str1, str2, StringComparison.InvariantCultureIgnoreCase); + } + + if (result != 0) + return result; + } + + return len1 - len2; + } + + private static long ParseLong(string s, int start, int end) + { + long result = 0; + for (int i = start; i < end; i++) + result = result * 10 + (s[i] - '0'); + return result; + } + + private static string StripSpaces(string s, int start, int end) + { + // Fast path: if no spaces, return substring directly + bool hasSpace = false; + for (int i = start; i < end; i++) + { + if (s[i] == ' ') + { + hasSpace = true; + break; + } + } + + if (!hasSpace) + return s.Substring(start, end - start); + + var chars = new char[end - start]; + int count = 0; + for (int i = start; i < end; i++) + { + if (s[i] != ' ') + chars[count++] = s[i]; + } + + return new string(chars, 0, count); + } +} diff --git a/src/ALCops.Common/Permissions/PermissionSyntaxHelper.cs b/src/ALCops.Common/Permissions/PermissionSyntaxHelper.cs index da65d0b..8b96c4a 100644 --- a/src/ALCops.Common/Permissions/PermissionSyntaxHelper.cs +++ b/src/ALCops.Common/Permissions/PermissionSyntaxHelper.cs @@ -13,9 +13,9 @@ public static class PermissionSyntaxHelper private const string CanonicalOrder = MethodOperationMap.CanonicalOrder; /// - /// Checks whether the permission entries are sorted alphabetically by - /// (type keyword, object name), both case-insensitive. - /// Returns true if 0 or 1 entries (trivially sorted). + /// Checks whether the permission entries are sorted using AZ Dev Tools-compatible ordering: + /// table/tabledata types first (grouped by name), remaining types alphabetically by type, then by name. + /// Names use natural/alphanumeric comparison. Returns true if 0 or 1 entries (trivially sorted). /// public static bool ArePermissionsSorted(SeparatedSyntaxList permissions) { @@ -33,11 +33,8 @@ public static bool ArePermissionsSorted(SeparatedSyntaxList pe if (previousType is not null && previousName is not null) { - int typeCompare = string.Compare(previousType, type, StringComparison.OrdinalIgnoreCase); - if (typeCompare > 0) - return false; - if (typeCompare == 0 && - string.Compare(previousName, name, StringComparison.OrdinalIgnoreCase) > 0) + int cmp = ComparePermissionEntries(previousType, previousName, type, name); + if (cmp > 0) return false; } @@ -127,7 +124,7 @@ public static PermissionSyntax CreatePermissionSyntax(string tableName, string p /// /// Finds the insertion index for a new entry in a sorted permission list, - /// comparing by (type keyword, object name). The new entry type defaults to "tabledata" + /// using AZ Dev Tools-compatible ordering. The new entry type defaults to "tabledata" /// since AC0031 only inserts tabledata entries. /// If not sorted, returns the count (append). /// @@ -144,10 +141,8 @@ public static int FindInsertionIndex(SeparatedSyntaxList permi if (entryType is null || entryName is null) continue; - int typeCompare = string.Compare(newType, entryType, StringComparison.OrdinalIgnoreCase); - if (typeCompare < 0) - return i; - if (typeCompare == 0 && string.Compare(tableName, entryName, StringComparison.OrdinalIgnoreCase) < 0) + int cmp = ComparePermissionEntries(newType, tableName, entryType, entryName); + if (cmp < 0) return i; } @@ -317,7 +312,8 @@ private static bool HasNewlineTrivia(SyntaxTriviaList triviaList) } /// - /// Sorts the permission entries by (type keyword, object name), both case-insensitive. + /// Sorts the permission entries using AZ Dev Tools-compatible ordering: + /// table/tabledata first (grouped by name), remaining types alphabetically. /// Returns a new list with the entries in sorted order, stripping leading trivia from /// all entries (callers are responsible for applying formatting). /// @@ -331,18 +327,51 @@ public static List GetSortedPermissions(SeparatedSyntaxList + /// Compares two permission entries using AZ Dev Tools-compatible ordering: + /// - table/tabledata types sort first, grouped by name (table before tabledata for same name) + /// - remaining types sort alphabetically by type keyword (OrdinalIgnoreCase) + /// - within the same non-table type, sort by name using natural/alphanumeric comparison + /// + public static int ComparePermissionEntries(string typeA, string nameA, string typeB, string nameB) + { + bool aIsTable = IsTableType(typeA); + bool bIsTable = IsTableType(typeB); + + if (aIsTable && bIsTable) + { + // Both table types: sort by name first, then type (table before tabledata) + int nameCompare = NaturalNameComparer.Compare(nameA, nameB); + if (nameCompare != 0) + return nameCompare; + return string.Compare(typeA, typeB, StringComparison.OrdinalIgnoreCase); + } + + if (aIsTable != bIsTable) + { + // Table types come first + return aIsTable ? -1 : 1; + } + + // Both non-table: sort by type alphabetically, then by name + int typeCompare = string.Compare(typeA, typeB, StringComparison.OrdinalIgnoreCase); + if (typeCompare != 0) + return typeCompare; + return NaturalNameComparer.Compare(nameA, nameB); + } + + private static bool IsTableType(string type) => + string.Equals(type, "table", StringComparison.OrdinalIgnoreCase) || + string.Equals(type, "tabledata", StringComparison.OrdinalIgnoreCase); + /// /// Builds a multi-line PermissionPropertyValueSyntax from an ordered list of entries. /// The first entry has no leading trivia (it shares the line with "Permissions = "). diff --git a/src/ALCops.FormattingCop.Test/Rules/PermissionDeclarationOrder/HasDiagnostic/UnsortedMixedTypes.al b/src/ALCops.FormattingCop.Test/Rules/PermissionDeclarationOrder/HasDiagnostic/UnsortedMixedTypes.al index 365cc57..11e914f 100644 --- a/src/ALCops.FormattingCop.Test/Rules/PermissionDeclarationOrder/HasDiagnostic/UnsortedMixedTypes.al +++ b/src/ALCops.FormattingCop.Test/Rules/PermissionDeclarationOrder/HasDiagnostic/UnsortedMixedTypes.al @@ -24,8 +24,8 @@ permissionset 50100 "My Permission Set" Assignable = false; Access = Public; - [|Permissions = tabledata "My Table" = R, - codeunit "My Codeunit" = X, + [|Permissions = codeunit "My Codeunit" = X, + report "My Report" = X, page "My Page" = X, - report "My Report" = X|]; + tabledata "My Table" = R|]; } diff --git a/src/ALCops.FormattingCop.Test/Rules/PermissionDeclarationOrder/HasDiagnostic/UnsortedNumeric.al b/src/ALCops.FormattingCop.Test/Rules/PermissionDeclarationOrder/HasDiagnostic/UnsortedNumeric.al new file mode 100644 index 0000000..a41a719 --- /dev/null +++ b/src/ALCops.FormattingCop.Test/Rules/PermissionDeclarationOrder/HasDiagnostic/UnsortedNumeric.al @@ -0,0 +1,20 @@ +codeunit 50100 "Item 10" +{ +} + +codeunit 50101 "Item 2" +{ +} + +codeunit 50102 "Item 1" +{ +} + +permissionset 50100 "My Permission Set" +{ + Assignable = true; + [|Permissions = + codeunit "Item 1" = X, + codeunit "Item 10" = X, + codeunit "Item 2" = X|]; +} diff --git a/src/ALCops.FormattingCop.Test/Rules/PermissionDeclarationOrder/HasDiagnostic/UnsortedTableNotFirst.al b/src/ALCops.FormattingCop.Test/Rules/PermissionDeclarationOrder/HasDiagnostic/UnsortedTableNotFirst.al new file mode 100644 index 0000000..6c9bb6e --- /dev/null +++ b/src/ALCops.FormattingCop.Test/Rules/PermissionDeclarationOrder/HasDiagnostic/UnsortedTableNotFirst.al @@ -0,0 +1,20 @@ +table 50100 "My Table" +{ + Caption = '', Locked = true; + fields + { + field(1; MyField; Integer) { } + } +} + +codeunit 50100 "My Codeunit" +{ +} + +permissionset 50100 "My Permission Set" +{ + Assignable = true; + [|Permissions = + codeunit "My Codeunit" = X, + tabledata "My Table" = R|]; +} diff --git a/src/ALCops.FormattingCop.Test/Rules/PermissionDeclarationOrder/HasFix/ReorderMixedTypes/current.al b/src/ALCops.FormattingCop.Test/Rules/PermissionDeclarationOrder/HasFix/ReorderMixedTypes/current.al index 365cc57..11e914f 100644 --- a/src/ALCops.FormattingCop.Test/Rules/PermissionDeclarationOrder/HasFix/ReorderMixedTypes/current.al +++ b/src/ALCops.FormattingCop.Test/Rules/PermissionDeclarationOrder/HasFix/ReorderMixedTypes/current.al @@ -24,8 +24,8 @@ permissionset 50100 "My Permission Set" Assignable = false; Access = Public; - [|Permissions = tabledata "My Table" = R, - codeunit "My Codeunit" = X, + [|Permissions = codeunit "My Codeunit" = X, + report "My Report" = X, page "My Page" = X, - report "My Report" = X|]; + tabledata "My Table" = R|]; } diff --git a/src/ALCops.FormattingCop.Test/Rules/PermissionDeclarationOrder/HasFix/ReorderMixedTypes/expected.al b/src/ALCops.FormattingCop.Test/Rules/PermissionDeclarationOrder/HasFix/ReorderMixedTypes/expected.al index 3de3a7f..e91ff28 100644 --- a/src/ALCops.FormattingCop.Test/Rules/PermissionDeclarationOrder/HasFix/ReorderMixedTypes/expected.al +++ b/src/ALCops.FormattingCop.Test/Rules/PermissionDeclarationOrder/HasFix/ReorderMixedTypes/expected.al @@ -24,8 +24,8 @@ permissionset 50100 "My Permission Set" Assignable = false; Access = Public; - Permissions = codeunit "My Codeunit" = X, + Permissions = tabledata "My Table" = R, + codeunit "My Codeunit" = X, page "My Page" = X, - report "My Report" = X, - tabledata "My Table" = R; + report "My Report" = X; } diff --git a/src/ALCops.FormattingCop.Test/Rules/PermissionDeclarationOrder/NoDiagnostic/AlreadySorted.al b/src/ALCops.FormattingCop.Test/Rules/PermissionDeclarationOrder/NoDiagnostic/AlreadySorted.al index 302c53d..565ebce 100644 --- a/src/ALCops.FormattingCop.Test/Rules/PermissionDeclarationOrder/NoDiagnostic/AlreadySorted.al +++ b/src/ALCops.FormattingCop.Test/Rules/PermissionDeclarationOrder/NoDiagnostic/AlreadySorted.al @@ -24,8 +24,8 @@ permissionset 50100 "My Permission Set" Assignable = false; Access = Public; - Permissions = codeunit "My Codeunit" = X, + Permissions = tabledata Alpha = R, + codeunit "My Codeunit" = X, page "My Page" = X, - report "My Report" = X, - tabledata Alpha = R; + report "My Report" = X; } diff --git a/src/ALCops.FormattingCop.Test/Rules/PermissionDeclarationOrder/NoDiagnostic/DotsBeforeLetters.al b/src/ALCops.FormattingCop.Test/Rules/PermissionDeclarationOrder/NoDiagnostic/DotsBeforeLetters.al new file mode 100644 index 0000000..5589491 --- /dev/null +++ b/src/ALCops.FormattingCop.Test/Rules/PermissionDeclarationOrder/NoDiagnostic/DotsBeforeLetters.al @@ -0,0 +1,15 @@ +codeunit 50100 "Post. Appr. Setup Wiz Hlp" +{ +} + +codeunit 50101 "Post Inv. Appr. Setup Hlp" +{ +} + +[||]permissionset 50100 GeneratedPermission +{ + Assignable = true; + Permissions = + codeunit "Post. Appr. Setup Wiz Hlp" = X, + codeunit "Post Inv. Appr. Setup Hlp" = X; +} diff --git a/src/ALCops.FormattingCop.Test/Rules/PermissionDeclarationOrder/NoDiagnostic/NaturalNumericSort.al b/src/ALCops.FormattingCop.Test/Rules/PermissionDeclarationOrder/NoDiagnostic/NaturalNumericSort.al new file mode 100644 index 0000000..dcaa9fe --- /dev/null +++ b/src/ALCops.FormattingCop.Test/Rules/PermissionDeclarationOrder/NoDiagnostic/NaturalNumericSort.al @@ -0,0 +1,20 @@ +codeunit 50100 "Item 2" +{ +} + +codeunit 50101 "Item 10" +{ +} + +codeunit 50102 "Item 1" +{ +} + +[||]permissionset 50100 "My Permission Set" +{ + Assignable = true; + Permissions = + codeunit "Item 1" = X, + codeunit "Item 2" = X, + codeunit "Item 10" = X; +} diff --git a/src/ALCops.FormattingCop.Test/Rules/PermissionDeclarationOrder/NoDiagnostic/TableTypesFirst.al b/src/ALCops.FormattingCop.Test/Rules/PermissionDeclarationOrder/NoDiagnostic/TableTypesFirst.al new file mode 100644 index 0000000..0b630db --- /dev/null +++ b/src/ALCops.FormattingCop.Test/Rules/PermissionDeclarationOrder/NoDiagnostic/TableTypesFirst.al @@ -0,0 +1,26 @@ +[||]table 50100 "My Table" +{ + Caption = '', Locked = true; + fields + { + field(1; MyField; Integer) { } + } +} + +codeunit 50100 "My Codeunit" +{ +} + +page 50100 "My Page" +{ +} + +permissionset 50100 "My Permission Set" +{ + Assignable = true; + Permissions = + table "My Table" = X, + tabledata "My Table" = R, + codeunit "My Codeunit" = X, + page "My Page" = X; +} diff --git a/src/ALCops.FormattingCop.Test/Rules/PermissionDeclarationOrder/PermissionDeclarationOrder.cs b/src/ALCops.FormattingCop.Test/Rules/PermissionDeclarationOrder/PermissionDeclarationOrder.cs index 17d5b67..b360615 100644 --- a/src/ALCops.FormattingCop.Test/Rules/PermissionDeclarationOrder/PermissionDeclarationOrder.cs +++ b/src/ALCops.FormattingCop.Test/Rules/PermissionDeclarationOrder/PermissionDeclarationOrder.cs @@ -25,6 +25,8 @@ public void Setup() [TestCase("UnsortedMixedTypes")] [TestCase("UnsortedSingleType")] [TestCase("UnsortedCodeunit")] + [TestCase("UnsortedNumeric")] + [TestCase("UnsortedTableNotFirst")] public async Task HasDiagnostic(string testCase) { var code = await File.ReadAllTextAsync(Path.Combine(_testCasePath, nameof(HasDiagnostic), $"{testCase}.al")) @@ -38,6 +40,9 @@ public async Task HasDiagnostic(string testCase) [TestCase("SingleEntry")] [TestCase("NoPermissionsProperty")] [TestCase("SortedTabledata")] + [TestCase("DotsBeforeLetters")] + [TestCase("NaturalNumericSort")] + [TestCase("TableTypesFirst")] public async Task NoDiagnostic(string testCase) { var code = await File.ReadAllTextAsync(Path.Combine(_testCasePath, nameof(NoDiagnostic), $"{testCase}.al"))