@@ -622,51 +622,56 @@ public func configurationForEntryPoint(from args: __CommandLineArguments_v0) thr
622622
623623 // Filtering
624624 var filters = [ Configuration . TestFilter] ( )
625- func testFilter( forRegularExpressions regexes: [ String ] ? , label: String , membership: Configuration . TestFilter . Membership ) throws -> Configuration . TestFilter {
626- guard let regexes, !regexes. isEmpty else {
627- // Return early if empty, even though the `reduce` logic below can handle
628- // this case, in order to avoid the `#available` guard.
629- return . unfiltered
625+ func testFilters( forOptionArguments optionArguments: [ String ] ? , label: String , membership: Configuration . TestFilter . Membership ) throws -> [ Configuration . TestFilter ] {
626+
627+ // Filters will come in two flavors: those with `tag:` as a prefix, and
628+ // those without. We split them into two collections, taking care to handle
629+ // an escaped colon, treating it as a pseudo-operator.
630+ let tagPrefix = " tag: "
631+ let escapedTagPrefix = #"tag\:"#
632+ var tags = [ Tag] ( )
633+ var regexes = [ String] ( )
634+
635+ // Loop through all the option arguments, separating tags from regex filters
636+ for var optionArg in optionArguments ?? [ ] {
637+ if optionArg. hasPrefix ( tagPrefix) {
638+ // Running into the `tag:` prefix means we should strip it and use the
639+ // actual tag name the user has provided
640+ let tagStringWithoutPrefix = String ( optionArg. dropFirst ( tagPrefix. count) )
641+ tags. append ( Tag ( userProvidedStringValue: tagStringWithoutPrefix) )
642+ } else {
643+ // If we run into the escaped tag prefix, the user has indicated they
644+ // want to us to treat it as a regex filter. We need to to unescape it
645+ // before adding it as a regex filter
646+ if optionArg. hasPrefix ( escapedTagPrefix) {
647+ optionArg. replaceSubrange ( escapedTagPrefix. startIndex..< escapedTagPrefix. endIndex, with: tagPrefix)
648+ }
649+ regexes. append ( optionArg)
650+ }
630651 }
631652
632- guard #available( _regexAPI, * ) else {
633- throw _EntryPointError. featureUnavailable ( " The ` \( label) ' option is not supported on this OS version. " )
653+ // If we didn't find any tags, the tagFilter should be .unfiltered,
654+ // otherwise we construct it with the provided tags
655+ let tagFilter : Configuration . TestFilter = switch ( membership, tags. isEmpty) {
656+ case ( _, true ) : . unfiltered
657+ case ( . including, false ) : Configuration . TestFilter ( includingAnyOf: tags)
658+ case ( . excluding, false ) : Configuration . TestFilter ( excludingAnyOf: tags)
634659 }
635- return try Configuration . TestFilter ( membership: membership, matchingAnyOf: regexes)
636- }
637-
638- // Extract any filters or skips without the `tag:` prefix; those will be treated as normal regexes.
639- let tagPrefix = " tag: "
640- let escapedTagPrefix = " tag \\ : "
641- var nonTagFilterRegexes : [ String ] = [ ]
642- var nonTagSkipRegexes : [ String ] = [ ]
643660
644- for var filter in args. filter ?? [ ] {
645- if filter. hasPrefix ( tagPrefix) {
646- filters. append ( Configuration . TestFilter ( includingAnyOf: [ Tag ( userProvidedStringValue: String ( filter. dropFirst ( 4 ) ) ) ] ) )
647- } else {
648- // If we run into the escaped tag prefix, we need to to remove the escape character before adding it as a regex filter
649- if filter. hasPrefix ( escapedTagPrefix) {
650- filter. replaceSubrange ( escapedTagPrefix. startIndex..< escapedTagPrefix. endIndex, with: tagPrefix)
651- }
652- nonTagFilterRegexes. append ( filter)
661+ guard !regexes. isEmpty else {
662+ // Return early with just the tag filter, even though the `reduce` logic
663+ // below can handle this case, in order to avoid the `#available` guard.
664+ return [ tagFilter]
653665 }
654- }
655666
656- for var skip in args. skip ?? [ ] {
657- if skip. hasPrefix ( tagPrefix) {
658- filters. append ( Configuration . TestFilter ( includingAnyOf: [ Tag ( userProvidedStringValue: String ( skip. dropFirst ( 4 ) ) ) ] ) )
659- } else {
660- // If we run into the escaped tag prefix, we need to to remove the escape character before adding it as a regex filter
661- if skip. hasPrefix ( escapedTagPrefix) {
662- skip. replaceSubrange ( escapedTagPrefix. startIndex..< escapedTagPrefix. endIndex, with: tagPrefix)
663- }
664- nonTagSkipRegexes. append ( skip)
667+ guard #available( _regexAPI, * ) else {
668+ throw _EntryPointError. featureUnavailable ( " The ` \( label) ' option is not supported on this OS version when used as a regular expression. Consider filtering/skipping by tag using the 'tag:' prefix. " )
665669 }
670+ return [ try Configuration . TestFilter ( membership: membership, matchingAnyOf: regexes) , tagFilter]
666671 }
667672
668- filters. append ( try testFilter ( forRegularExpressions : nonTagFilterRegexes , label: " --filter " , membership: . including) )
669- filters. append ( try testFilter ( forRegularExpressions : nonTagSkipRegexes , label: " --skip " , membership: . excluding) )
673+ filters += try testFilters ( forOptionArguments : args . filter , label: " --filter " , membership: . including)
674+ filters += try testFilters ( forOptionArguments : args . skip , label: " --skip " , membership: . excluding)
670675
671676 configuration. testFilter = filters. reduce ( . unfiltered) { $0. combining ( with: $1) }
672677 if args. includeHiddenTests == true {
0 commit comments