// Copyright (c) 2026 AlphaSierraPapa for the SharpDevelop Team // // 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. using AwesomeAssertions; using ICSharpCode.ILSpyX.Search; using ILSpy.Search; using NUnit.Framework; namespace ICSharpCode.ILSpy.Tests.Search; /// /// Unit tests for the prefix-DSL parser exposed by . /// The parser is the data-input side of the search pipeline — it converts the user's /// freeform query string into a whose Mode / MemberSearchKind / /// InAssembly / InNamespace / Keywords / RegEx / FullNameSearch / OmitGenerics drive the /// strategy. Tests pin the prefixes advertised by the SearchPane watermark. /// [TestFixture] public class SearchPrefixParsingTests { static SearchRequest Parse(string input, SearchMode defaultMode = SearchMode.TypeAndMember) => RunningSearch.ParseInput(input, defaultMode); [Test] public void Plain_Term_Falls_Through_To_The_Picker_Mode() { var r = Parse("Enumerable"); r.Mode.Should().Be(SearchMode.TypeAndMember); r.Keywords.Should().BeEquivalentTo("Enumerable"); } [Test] public void T_Prefix_Switches_To_Type_Mode() { var r = Parse("t:String"); r.Mode.Should().Be(SearchMode.Type); r.Keywords.Should().BeEquivalentTo("String"); } [Test] public void M_Prefix_Switches_To_Member_Mode() { var r = Parse("m:ToString"); r.Mode.Should().Be(SearchMode.Member); r.Keywords.Should().BeEquivalentTo("ToString"); } [Test] public void Md_Prefix_Switches_To_Method_Mode() { var r = Parse("md:Run"); r.Mode.Should().Be(SearchMode.Method); r.Keywords.Should().BeEquivalentTo("Run"); } [Test] public void F_Prefix_Switches_To_Field_Mode() { var r = Parse("f:value"); r.Mode.Should().Be(SearchMode.Field); r.Keywords.Should().BeEquivalentTo("value"); } [Test] public void P_Prefix_Switches_To_Property_Mode() { var r = Parse("p:Length"); r.Mode.Should().Be(SearchMode.Property); r.Keywords.Should().BeEquivalentTo("Length"); } [Test] public void E_Prefix_Switches_To_Event_Mode() { var r = Parse("e:Click"); r.Mode.Should().Be(SearchMode.Event); r.Keywords.Should().BeEquivalentTo("Click"); } [Test] public void C_Prefix_Switches_To_Literal_Mode() { var r = Parse("c:42"); r.Mode.Should().Be(SearchMode.Literal); r.Keywords.Should().BeEquivalentTo("42"); } [Test] public void N_Prefix_Switches_To_Namespace_Mode() { var r = Parse("n:System"); r.Mode.Should().Be(SearchMode.Namespace); r.Keywords.Should().BeEquivalentTo("System"); } [Test] public void A_Prefix_Switches_To_Assembly_Mode_With_NameOrFileName_Kind() { var r = Parse("a:mscorlib"); r.Mode.Should().Be(SearchMode.Assembly); r.AssemblySearchKind.Should().Be(AssemblySearchKind.NameOrFileName); r.Keywords.Should().BeEquivalentTo("mscorlib"); } [Test] public void Af_Prefix_Switches_To_Assembly_Mode_With_FilePath_Kind() { var r = Parse("af:foo.dll"); r.Mode.Should().Be(SearchMode.Assembly); r.AssemblySearchKind.Should().Be(AssemblySearchKind.FilePath); r.Keywords.Should().BeEquivalentTo("foo.dll"); } [Test] public void An_Prefix_Switches_To_Assembly_Mode_With_FullName_Kind() { var r = Parse("an:System.Linq"); r.Mode.Should().Be(SearchMode.Assembly); r.AssemblySearchKind.Should().Be(AssemblySearchKind.FullName); r.Keywords.Should().BeEquivalentTo("System.Linq"); } [Test] public void At_Prefix_Switches_To_Token_Mode() { // @ token prefix is special: single character, no colon. var r = Parse("@0x06000001"); r.Mode.Should().Be(SearchMode.Token); r.Keywords.Should().BeEquivalentTo("0x06000001"); } [Test] public void InAssembly_Prefix_Sets_The_Filter() { var r = Parse("inassembly:mscorlib Foo"); r.InAssembly.Should().Be("mscorlib"); r.Keywords.Should().BeEquivalentTo("mscorlib", "Foo"); } [Test] public void InNamespace_Prefix_Sets_The_Filter() { var r = Parse("innamespace:System.Linq Enumerable"); r.InNamespace.Should().Be("System.Linq"); r.Keywords.Should().BeEquivalentTo("System.Linq", "Enumerable"); } [Test] public void Regex_Term_Wrapped_In_Slashes_Populates_RegEx() { var r = Parse("/^Enumerable$/"); r.RegEx.Should().NotBeNull(); r.RegEx!.IsMatch("Enumerable").Should().BeTrue(); r.RegEx.IsMatch("EnumerableExtensions").Should().BeFalse(); } [Test] public void FullNameSearch_Enabled_When_Term_Contains_Dot() { // A term like System.Linq.Enumerable signals the user wants a full-name match. var r = Parse("System.Linq.Enumerable"); r.FullNameSearch.Should().BeTrue(); } [Test] public void FullNameSearch_Off_For_Plain_Identifier() { var r = Parse("Enumerable"); r.FullNameSearch.Should().BeFalse(); } [Test] public void OmitGenerics_True_When_Term_Has_No_Angle_Or_Backtick() { var r = Parse("List"); r.OmitGenerics.Should().BeTrue(); } [Test] public void OmitGenerics_False_When_Term_Has_Generic_Angle() { var r = Parse("List"); r.OmitGenerics.Should().BeFalse(); } [Test] public void Empty_Input_Yields_Zero_Keywords() { var r = Parse(" "); r.Keywords.Should().BeEmpty(); } [Test] public void Quoted_Term_Survives_Whitespace_Split() { var r = Parse("\"hello world\""); r.Keywords.Should().BeEquivalentTo("hello world"); } [Test] public void Match_Operators_Are_Passed_Through_To_Keywords() { // =, +, -, ~ are per-keyword prefixes interpreted by AbstractSearchStrategy.IsMatch // — the parser only has to deliver them in the keyword string untouched. var r = Parse("=Foo +Bar -Baz"); r.Keywords.Should().BeEquivalentTo("=Foo", "+Bar", "-Baz"); } }