From c6ac019a4e4bcf381baf14f1676ce9fd825a8db4 Mon Sep 17 00:00:00 2001 From: Juster Zhu Date: Tue, 21 Oct 2025 23:03:11 +0800 Subject: [PATCH] fix: PathPicker's File Filtering Function malfunctions (#786) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * fix: PathPicker's File Filtering Function malfunctions Regular expression validation for adding parameters; the sample reference content is as follows: [Name,Pattern] → True [123,.exe,.pdb] → True [All] → True [ImageAll] → True [11,*.txt] → True *.123 → False *.txt → False .png → False [*.txt] → False [.txt] → False [a] → True [a,b] → True [a,b,c] → True [a,*] → True [a,.] → True [*] → False [.] → False [a,] → False [,a] → False [] → False [a,b,] → False [a,,b] → False [a*b] → False [a.b] → False * Add PathPicker regex validation and headless tests Introduces regex-based validation for the PathPicker FileFilter property, supporting both NETSTANDARD2_0 and NET8_0 with conditional compilation. Adds headless UI tests and supporting test views to verify correct and incorrect filter patterns. * Refactor PathPicker tests and update accessibility Moved PathPicker unit tests from HeadlessTest.Ursa to Test.Ursa, removing UI-dependent test files and replacing them with direct unit tests for ParseFileTypes. Updated PathPicker.cs to make ParseFileTypes internal for improved testability and adjusted regex method accessibility. Updated project file to remove references to deleted test files. * Update HeadlessTest.Ursa.csproj * Update src/Ursa/Controls/PathPicker/PathPicker.cs Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --------- Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- src/Ursa/Controls/PathPicker/PathPicker.cs | 31 ++++++++++-- .../HeadlessTest.Ursa.csproj | 1 - .../PathPickerTests/PathPickerTests.cs | 47 +++++++++++++++++++ 3 files changed, 74 insertions(+), 5 deletions(-) create mode 100644 tests/Test.Ursa/PathPickerTests/PathPickerTests.cs diff --git a/src/Ursa/Controls/PathPicker/PathPicker.cs b/src/Ursa/Controls/PathPicker/PathPicker.cs index 2da25f0..07f75a8 100644 --- a/src/Ursa/Controls/PathPicker/PathPicker.cs +++ b/src/Ursa/Controls/PathPicker/PathPicker.cs @@ -1,4 +1,5 @@ using System.Text; +using System.Text.RegularExpressions; using System.Windows.Input; using Avalonia; using Avalonia.Controls; @@ -15,9 +16,21 @@ namespace Ursa.Controls; [TemplatePart(Name = PART_Button, Type = typeof(Button))] [PseudoClasses(PseudoClassName.PC_Empty)] -public class PathPicker : TemplatedControl +public partial class PathPicker : TemplatedControl { + private const string PatternsRegexString = @"^\[(?:[^*.,]+|([^*.,]+(,[^,]+)+))\]$"; + +#if NETSTANDARD2_0 + private static readonly Regex _patternsRegex = new (PatternsRegexString); +#endif + +#if NET8_0 + [GeneratedRegex(PatternsRegexString)] + private static partial Regex GetPatternsRegex(); +#endif + public const string PART_Button = "PART_Button"; + public static readonly StyledProperty SuggestedStartPathProperty = AvaloniaProperty.Register( nameof(SuggestedStartPath), string.Empty); @@ -181,7 +194,6 @@ public class PathPicker : TemplatedControl Button.ClickEvent.AddHandler(LaunchPicker, _button); } - private static string RemoveNewLine(string str) { return str.Replace("\r", "") @@ -220,9 +232,20 @@ public class PathPicker : TemplatedControl /** * [ParseFilePickerTypeStr][ParseFilePickerTypeStr]... */ - private static IReadOnlyList? ParseFileTypes(string str) + internal static IReadOnlyList? ParseFileTypes(string str) { - if (string.IsNullOrWhiteSpace(str)) return null; + if (string.IsNullOrWhiteSpace(str)) + return null; + +#if NETSTANDARD2_0 + if (!_patternsRegex.IsMatch(str)) +#endif + +#if NET8_0 + if (!GetPatternsRegex().IsMatch(str)) +#endif + throw new ArgumentException($"{nameof(str)} Invalid parameter, please refer to the following content: [Name, Pattern], such as [123, .exe, .pdb] or [All][ImageAll][11, *.txt]"); + string[] separatedStrings = ["[", "][", "]"]; var list = RemoveNewLine(str) .Replace(" ", string.Empty) diff --git a/tests/HeadlessTest.Ursa/HeadlessTest.Ursa.csproj b/tests/HeadlessTest.Ursa/HeadlessTest.Ursa.csproj index 9b7f2da..24a152f 100644 --- a/tests/HeadlessTest.Ursa/HeadlessTest.Ursa.csproj +++ b/tests/HeadlessTest.Ursa/HeadlessTest.Ursa.csproj @@ -28,5 +28,4 @@ - diff --git a/tests/Test.Ursa/PathPickerTests/PathPickerTests.cs b/tests/Test.Ursa/PathPickerTests/PathPickerTests.cs new file mode 100644 index 0000000..c17641a --- /dev/null +++ b/tests/Test.Ursa/PathPickerTests/PathPickerTests.cs @@ -0,0 +1,47 @@ +using Ursa.Controls; + +namespace Test.Ursa.PathPickerTests; + +public class PathPickerTests +{ + [Theory] + [InlineData(".123")] + [InlineData(".txt")] + [InlineData(".png")] + [InlineData("[.txt]")] + [InlineData("[]")] + [InlineData("[.]")] + [InlineData("[a,]")] + [InlineData("[,a]")] + [InlineData("[a,b,]")] + [InlineData("[a,,b]")] + [InlineData("[a*b]")] + [InlineData("[a.b]")] + public void ParseFileTypes_InvalidInputs_ThrowsArgumentException(string input) + { + var exception = Assert.Throws(() => PathPicker.ParseFileTypes(input)); + Assert.Contains("Invalid parameter, please refer to the following content", exception.Message); + } + + [Theory] + [InlineData("[Name,Pattern]")] + [InlineData("[123,.exe,.pdb]")] + [InlineData("[All]")] + [InlineData("[ImageAll]")] + [InlineData("[11,.txt]")] + [InlineData("[a]")] + [InlineData("[a,b]")] + [InlineData("[a,b,c]")] + [InlineData("[a,.]")] + public void ParseFileTypes_ValidInputs_DoesNotThrow(string input) + { + try + { + PathPicker.ParseFileTypes(input); + } + catch (Exception ex) + { + Assert.Fail($"It is expected not to throw an exception, but actually throws one :{ex.Message}"); + } + } +} \ No newline at end of file