Skip to content

Commit

Permalink
Delete Match Method (#329)
Browse files Browse the repository at this point in the history
Delete the `match` method which first checks whether a given `String`
input matches against a specified regular expression. Instead, attempt
to extract the capture groups from a given regular expression. If the
`captureGroups` method returns `nil`, it implies a failed match.
Otherwise, a successful match is implied by a non-nil return value.
  • Loading branch information
cpisciotta authored Nov 9, 2024
1 parent ecb7a62 commit 8f738c5
Show file tree
Hide file tree
Showing 4 changed files with 48 additions and 59 deletions.
57 changes: 25 additions & 32 deletions Sources/XcbeautifyLib/JunitReporter.swift
Original file line number Diff line number Diff line change
Expand Up @@ -23,81 +23,74 @@ package final class JunitReporter {
// Remove any preceding or excessive spaces
let line = line.trimmingCharacters(in: .whitespacesAndNewlines)

if FailingTestCaptureGroup.regex.match(string: line) {
guard let testCase = generateFailingTest(line: line) else { return }
if let groups = FailingTestCaptureGroup.regex.captureGroups(for: line) {
guard let testCase = generateFailingTest(groups: groups) else { return }
components.append(.failingTest(testCase))
} else if RestartingTestCaptureGroup.regex.match(string: line) {
guard let testCase = generateRestartingTest(line: line) else { return }
} else if let groups = RestartingTestCaptureGroup.regex.captureGroups(for: line) {
guard let testCase = generateRestartingTest(groups: groups, line: line) else { return }
components.append(.failingTest(testCase))
} else if TestCasePassedCaptureGroup.regex.match(string: line) {
guard let testCase = generatePassingTest(line: line) else { return }
} else if let groups = TestCasePassedCaptureGroup.regex.captureGroups(for: line) {
guard let testCase = generatePassingTest(groups: groups) else { return }
components.append(.testCasePassed(testCase))
} else if TestCaseSkippedCaptureGroup.regex.match(string: line) {
guard let testCase = generateSkippedTest(line: line) else { return }
} else if let groups = TestCaseSkippedCaptureGroup.regex.captureGroups(for: line) {
guard let testCase = generateSkippedTest(groups: groups) else { return }
components.append(.skippedTest(testCase))
} else if TestSuiteStartCaptureGroup.regex.match(string: line) {
guard let testStart = generateSuiteStart(line: line) else { return }
} else if let groups = TestSuiteStartCaptureGroup.regex.captureGroups(for: line) {
guard let testStart = generateSuiteStart(groups: groups) else { return }
components.append(.testSuiteStart(testStart))
} else if ParallelTestCaseFailedCaptureGroup.regex.match(string: line) {
guard let testCase = generateParallelFailingTest(line: line) else { return }
} else if let groups = ParallelTestCaseFailedCaptureGroup.regex.captureGroups(for: line) {
guard let testCase = generateParallelFailingTest(groups: groups) else { return }
parallelComponents.append(.failingTest(testCase))
} else if ParallelTestCasePassedCaptureGroup.regex.match(string: line) {
guard let testCase = generatePassingParallelTest(line: line) else { return }
} else if let groups = ParallelTestCasePassedCaptureGroup.regex.captureGroups(for: line) {
guard let testCase = generatePassingParallelTest(groups: groups) else { return }
parallelComponents.append(.testCasePassed(testCase))
} else if ParallelTestCaseSkippedCaptureGroup.regex.match(string: line) {
guard let testCase = generateSkippedParallelTest(line: line) else { return }
} else if let groups = ParallelTestCaseSkippedCaptureGroup.regex.captureGroups(for: line) {
guard let testCase = generateSkippedParallelTest(groups: groups) else { return }
parallelComponents.append(.testCasePassed(testCase))
} else {
// Not needed for generating a junit report
return
}
}

private func generateFailingTest(line: String) -> TestCase? {
let groups = FailingTestCaptureGroup.regex.captureGroups(for: line)
private func generateFailingTest(groups: [String]) -> TestCase? {
guard let group = FailingTestCaptureGroup(groups: groups) else { return nil }
return TestCase(classname: group.testSuite, name: group.testCase, time: nil, failure: .init(message: "\(group.file) - \(group.reason)"))
}

private func generateRestartingTest(line: String) -> TestCase? {
let groups = RestartingTestCaptureGroup.regex.captureGroups(for: line)
// TODO: Delete `line` parameter
private func generateRestartingTest(groups: [String], line: String) -> TestCase? {
guard let group = RestartingTestCaptureGroup(groups: groups) else { return nil }
return TestCase(classname: group.testSuite, name: group.testCase, time: nil, failure: .init(message: line))
}

private func generateParallelFailingTest(line: String) -> TestCase? {
private func generateParallelFailingTest(groups: [String]) -> TestCase? {
// Parallel tests do not provide meaningful failure messages
let groups = ParallelTestCaseFailedCaptureGroup.regex.captureGroups(for: line)
guard let group = ParallelTestCaseFailedCaptureGroup(groups: groups) else { return nil }
return TestCase(classname: group.suite, name: group.testCase, time: nil, failure: .init(message: "Parallel test failed"))
}

private func generatePassingTest(line: String) -> TestCase? {
let groups = TestCasePassedCaptureGroup.regex.captureGroups(for: line)
private func generatePassingTest(groups: [String]) -> TestCase? {
guard let group = TestCasePassedCaptureGroup(groups: groups) else { return nil }
return TestCase(classname: group.suite, name: group.testCase, time: group.time)
}

private func generateSkippedTest(line: String) -> TestCase? {
let groups = TestCaseSkippedCaptureGroup.regex.captureGroups(for: line)
private func generateSkippedTest(groups: [String]) -> TestCase? {
guard let group = TestCaseSkippedCaptureGroup(groups: groups) else { return nil }
return TestCase(classname: group.suite, name: group.testCase, time: group.time, skipped: .init(message: nil))
}

private func generatePassingParallelTest(line: String) -> TestCase? {
let groups = ParallelTestCasePassedCaptureGroup.regex.captureGroups(for: line)
private func generatePassingParallelTest(groups: [String]) -> TestCase? {
guard let group = ParallelTestCasePassedCaptureGroup(groups: groups) else { return nil }
return TestCase(classname: group.suite, name: group.testCase, time: group.time)
}

private func generateSkippedParallelTest(line: String) -> TestCase? {
let groups = ParallelTestCaseSkippedCaptureGroup.regex.captureGroups(for: line)
private func generateSkippedParallelTest(groups: [String]) -> TestCase? {
guard let group = ParallelTestCaseSkippedCaptureGroup(groups: groups) else { return nil }
return TestCase(classname: group.suite, name: group.testCase, time: group.time, skipped: .init(message: nil))
}

private func generateSuiteStart(line: String) -> String? {
let groups = TestSuiteStartCaptureGroup.regex.captureGroups(for: line)
private func generateSuiteStart(groups: [String]) -> String? {
guard let group = TestSuiteStartCaptureGroup(groups: groups) else { return nil }
return group.testSuiteName
}
Expand Down
27 changes: 11 additions & 16 deletions Sources/XcbeautifyLib/Parser.swift
Original file line number Diff line number Diff line change
Expand Up @@ -134,25 +134,20 @@ package final class Parser {
return nil
}

// Find first parser that can parse specified string
guard let idx = captureGroupTypes.firstIndex(where: { $0.regex.match(string: line) }) else {
return nil
}
for (index, captureGroupType) in captureGroupTypes.enumerated() {
guard let groups = captureGroupType.regex.captureGroups(for: line) else { continue }

guard let captureGroupType = captureGroupTypes[safe: idx] else {
assertionFailure()
return nil
}
guard let captureGroup = captureGroupType.init(groups: groups) else {
assertionFailure()
return nil
}

let groups: [String] = captureGroupType.regex.captureGroups(for: line)
guard let captureGroup = captureGroupType.init(groups: groups) else {
assertionFailure()
return nil
}
// Move found parser to the top, so next time it will be checked first
captureGroupTypes.insert(captureGroupTypes.remove(at: index), at: 0)

// Move found parser to the top, so next time it will be checked first
captureGroupTypes.insert(captureGroupTypes.remove(at: idx), at: 0)
return captureGroup
}

return captureGroup
return nil
}
}
15 changes: 8 additions & 7 deletions Sources/XcbeautifyLib/XCRegex.swift
Original file line number Diff line number Diff line change
Expand Up @@ -15,14 +15,15 @@ package final class XCRegex: @unchecked Sendable {
self.pattern = pattern
}

func match(string: String) -> Bool {
let fullRange = NSRange(string.startIndex..., in: string)
return regex?.rangeOfFirstMatch(in: string, range: fullRange).location != NSNotFound
}
func captureGroups(for line: String) -> [String]? {
assert(regex != nil)

func captureGroups(for line: String) -> [String] {
let matches = regex?.matches(in: line, range: NSRange(location: 0, length: line.utf16.count))
guard let match = matches?.first else { return [] }
guard
let matches = regex?.matches(in: line, range: NSRange(location: 0, length: line.utf16.count)),
let match = matches.first
else {
return nil
}

let lastRangeIndex = match.numberOfRanges - 1
guard lastRangeIndex >= 1 else { return [] }
Expand Down
8 changes: 4 additions & 4 deletions Tests/XcbeautifyLibTests/CaptureGroupTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -18,22 +18,22 @@ final class CaptureGroupTests: XCTestCase {
]

for input in inputs {
XCTAssertTrue(SwiftCompilingCaptureGroup.regex.match(string: input))
XCTAssertNotNil(SwiftCompilingCaptureGroup.regex.captureGroups(for: input))
}
}

func testMatchCompilationResults() {
let input = #"/* com.apple.actool.compilation-results */"#
XCTAssertTrue(CompilationResultCaptureGroup.regex.match(string: input))
XCTAssertNotNil(CompilationResultCaptureGroup.regex.captureGroups(for: input))
}

func testMatchSwiftDriverJobDiscoveryEmittingModule() {
let input = #"SwiftDriverJobDiscovery normal arm64 Emitting module for Widgets (in target 'Widgets' from project 'Backyard Birds')"#
XCTAssertTrue(SwiftDriverJobDiscoveryEmittingModuleCaptureGroup.regex.match(string: input))
XCTAssertNotNil(SwiftDriverJobDiscoveryEmittingModuleCaptureGroup.regex.captureGroups(for: input))
}

func testMkDirCaptureGroup() throws {
let input = "MkDir /Backyard-Birds/Build/Products/Debug/Widgets.appex/Contents (in target \'Widgets\' from project \'Backyard Birds\')"
XCTAssertTrue(MkDirCaptureGroup.regex.match(string: input))
XCTAssertNotNil(MkDirCaptureGroup.regex.captureGroups(for: input))
}
}

0 comments on commit 8f738c5

Please sign in to comment.