1+ #if ! UNO_RUNTIMETESTS_DISABLE_UI
2+
3+ #nullable enable
4+
5+ using System ;
6+ using System . Collections . Generic ;
7+ using System . Data ;
8+ using System . Linq ;
9+ using System . Reflection ;
10+ using System . Text ;
11+ using Microsoft . VisualStudio . TestTools . UnitTesting ;
12+ using static System . StringComparison ;
13+
14+ namespace Uno . UI . RuntimeTests ;
15+
16+ partial class UnitTestsControl
17+ {
18+ private static IEnumerable < MethodInfo > FilterTests ( UnitTestClassInfo testClassInfo , string ? query )
19+ {
20+ var tests = testClassInfo . Tests ? . AsEnumerable ( ) ?? Array . Empty < MethodInfo > ( ) ;
21+ foreach ( var filter in SearchPredicate . ParseQuery ( query ) )
22+ {
23+ // chain filters with AND logic
24+ tests = tests . Where ( x =>
25+ filter . Exclusion ^ // use xor to flip the result based on Exclusion
26+ filter . Tag ? . ToLowerInvariant ( ) switch
27+ {
28+ "class" => MatchClassName ( x , filter . Text ) ,
29+ "displayname" => MatchDisplayName ( x , filter . Text ) ,
30+ "method" => MatchMethodName ( x , filter . Text ) ,
31+
32+ _ => MatchClassName ( x , filter . Text ) || MatchMethodName ( x , filter . Text ) ,
33+ }
34+ ) ;
35+ }
36+
37+ bool MatchClassName ( MethodInfo x , string value ) => x . DeclaringType ? . Name . Contains ( value , InvariantCultureIgnoreCase ) ?? false ;
38+ bool MatchMethodName ( MethodInfo x , string value ) => x . Name . Contains ( value , InvariantCultureIgnoreCase ) ;
39+ bool MatchDisplayName ( MethodInfo x , string value ) =>
40+ // fixme: since we are returning MethodInfo for match, there is no way to specify
41+ // which of the [DataRow] or which row within [DynamicData] without refactoring.
42+ // fixme: support [DynamicData]
43+ x . GetCustomAttributes < DataRowAttribute > ( ) . Any ( y => y . DisplayName . Contains ( value , InvariantCultureIgnoreCase ) ) ;
44+
45+ return tests ;
46+ }
47+
48+ public record SearchPredicate ( string Raw , string Text , bool Exclusion , string ? Tag = null )
49+ {
50+ private static readonly IReadOnlyDictionary < string , string > TagAliases =
51+ new Dictionary < string , string > ( StringComparer . InvariantCultureIgnoreCase )
52+ {
53+ [ "c" ] = "class" ,
54+ [ "m" ] = "method" ,
55+ [ "d" ] = "displayname" ,
56+ } ;
57+
58+ public static SearchPredicate [ ] ParseQuery ( string ? query )
59+ {
60+ if ( string . IsNullOrWhiteSpace ( query ) ) return Array . Empty < SearchPredicate > ( ) ;
61+
62+ return query ! . Split ( ' ' , StringSplitOptions . RemoveEmptyEntries )
63+ . Select ( Parse )
64+ . OfType < SearchPredicate > ( ) // trim null
65+ . Where ( x => x . Text . Length > 0 ) // ignore empty tag query eg: "c:"
66+ . ToArray ( ) ;
67+ }
68+
69+ public static SearchPredicate ? Parse ( string criteria )
70+ {
71+ if ( string . IsNullOrWhiteSpace ( criteria ) ) return null ;
72+
73+ var raw = criteria . Trim ( ) ;
74+ var text = raw ;
75+ if ( text . StartsWith ( '-' ) is var exclusion && exclusion )
76+ {
77+ text = text . Substring ( 1 ) ;
78+ }
79+ var tag = default ( string ? ) ;
80+ if ( text . Split ( ':' , 2 ) is { Length : 2 } tagParts )
81+ {
82+ tag = TagAliases . TryGetValue ( tagParts [ 0 ] , out var alias ) ? alias : tagParts [ 0 ] ;
83+ text = tagParts [ 1 ] ;
84+ }
85+
86+ return new ( raw , text , exclusion , tag ) ;
87+ }
88+ }
89+ }
90+
91+ #endif
0 commit comments