Skip to content

Commit eab4679

Browse files
committed
Update the testFilters to contain all the logic for processing test filters regardless of type, regex or tag
1 parent 4071138 commit eab4679

1 file changed

Lines changed: 41 additions & 36 deletions

File tree

Sources/Testing/ABI/EntryPoints/EntryPoint.swift

Lines changed: 41 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -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

Comments
 (0)