diff --git a/Sources/XcbeautifyLib/CaptureGroups.swift b/Sources/XcbeautifyLib/CaptureGroups.swift index 503afb24..3a022fd9 100644 --- a/Sources/XcbeautifyLib/CaptureGroups.swift +++ b/Sources/XcbeautifyLib/CaptureGroups.swift @@ -1,6 +1,8 @@ import Foundation -protocol CaptureGroup { } +public protocol CaptureGroup { + static var outputType: OutputType { get } +} protocol ErrorCaptureGroup: CaptureGroup { var wholeError: String { get } @@ -30,59 +32,83 @@ protocol ExecutedCaptureGroup: CaptureGroup { var wallClockTimeInSeconds: Double { get } } -struct EmptyCaptureGroup: CaptureGroup { } +struct EmptyCaptureGroup: CaptureGroup { + static let outputType: OutputType = .undefined +} struct AnalyzeCaptureGroup: CaptureGroup { + static let outputType: OutputType = .task + let filePath: String let fileName: String let target: String } struct BuildTargetCaptureGroup: TargetCaptureGroup { + static let outputType: OutputType = .task + let target: String let project: String let configuration: String } struct AggregateTargetCaptureGroup: TargetCaptureGroup { + static let outputType: OutputType = .task + let target: String let project: String let configuration: String } struct AnalyzeTargetCaptureGroup: TargetCaptureGroup { + static let outputType: OutputType = .task + let target: String let project: String let configuration: String } /// Nothing returned here for now -struct CheckDependenciesCaptureGroup: CaptureGroup { } +struct CheckDependenciesCaptureGroup: CaptureGroup { + static let outputType: OutputType = .task + } struct ShellCommandCaptureGroup: CaptureGroup { + static let outputType: OutputType = .task + let commandPath: String let arguments: String } struct CleanRemoveCaptureGroup: CaptureGroup { + static let outputType: OutputType = .task + let directory: String } struct CleanTargetCaptureGroup: TargetCaptureGroup { + static let outputType: OutputType = .task + let target: String let project: String let configuration: String } struct CodesignCaptureGroup: CaptureGroup { + static let outputType: OutputType = .task + let file: String } struct CodesignFrameworkCaptureGroup: CaptureGroup { + static let outputType: OutputType = .task + let frameworkPath: String } struct CompileCaptureGroup: CompileFileCaptureGroup { + static let outputType: OutputType = .task + #if !os(Linux) let filePath: String #endif @@ -91,44 +117,60 @@ struct CompileCaptureGroup: CompileFileCaptureGroup { } struct CompileCommandCaptureGroup: CaptureGroup { + static let outputType: OutputType = .task + let compilerCommand: String let filePath: String } struct CompileXibCaptureGroup: CompileFileCaptureGroup { + static let outputType: OutputType = .task + let filePath: String let filename: String let target: String } struct CompileStoryboardCaptureGroup: CompileFileCaptureGroup { + static let outputType: OutputType = .task + let filePath: String let filename: String let target: String } struct CopyHeaderCaptureGroup: CopyCaptureGroup { + static let outputType: OutputType = .task + let file: String let targetFile: String let target: String } struct CopyPlistCaptureGroup: CopyCaptureGroup { + static let outputType: OutputType = .task + let file: String let target: String } struct CopyStringsCaptureGroup: CopyCaptureGroup { + static let outputType: OutputType = .task + let file: String let target: String } struct CpresourceCaptureGroup: CopyCaptureGroup { + static let outputType: OutputType = .task + let file: String let target: String } struct ExecutedWithoutSkippedCaptureGroup: ExecutedCaptureGroup { + static let outputType: OutputType = .undefined // FIXME + let numberOfTests: Int let numberOfSkipped = 0 let numberOfFailures: Int @@ -137,6 +179,8 @@ struct ExecutedWithoutSkippedCaptureGroup: ExecutedCaptureGroup { } struct ExecutedWithSkippedCaptureGroup: ExecutedCaptureGroup { + static let outputType: OutputType = .undefined // FIXME + let numberOfTests: Int let numberOfSkipped: Int let numberOfFailures: Int @@ -145,6 +189,8 @@ struct ExecutedWithSkippedCaptureGroup: ExecutedCaptureGroup { } struct FailingTestCaptureGroup: CaptureGroup { + static let outputType: OutputType = .error + let file: String let testSuite: String let testCase: String @@ -152,33 +198,47 @@ struct FailingTestCaptureGroup: CaptureGroup { } struct UIFailingTestCaptureGroup: CaptureGroup { + static let outputType: OutputType = .error + let file: String let reason: String } struct RestartingTestCaptureGroup: CaptureGroup { + static let outputType: OutputType = .test + let testSuiteAndTestCase: String let testSuite: String let testCase: String } -struct GenerateCoverageDataCaptureGroup: CaptureGroup { } +struct GenerateCoverageDataCaptureGroup: CaptureGroup { + static let outputType: OutputType = .task + } struct GeneratedCoverageReportCaptureGroup: CaptureGroup { + static let outputType: OutputType = .task + let coverageReportFilePath: String } struct GenerateDSYMCaptureGroup: CaptureGroup { + static let outputType: OutputType = .task + let dsym: String let target: String } struct LibtoolCaptureGroup: CaptureGroup { + static let outputType: OutputType = .task + let fileName: String let target: String } struct LinkingCaptureGroup: CaptureGroup { + static let outputType: OutputType = .task + #if !os(Linux) let binaryFilename: String #endif @@ -186,22 +246,30 @@ struct LinkingCaptureGroup: CaptureGroup { } struct TestCasePassedCaptureGroup: CaptureGroup { + static let outputType: OutputType = .testCase + let suite: String let testCase: String let time: String } struct TestCaseStartedCaptureGroup: CaptureGroup { + static let outputType: OutputType = .testCase + let suite: String let testCase: String } struct TestCasePendingCaptureGroup: CaptureGroup { + static let outputType: OutputType = .testCase + let suite: String let testCase: String } struct TestCaseMeasuredCaptureGroup: CaptureGroup { + static let outputType: OutputType = .testCase + let suite: String let testCase: String let name: String @@ -211,6 +279,8 @@ struct TestCaseMeasuredCaptureGroup: CaptureGroup { } struct ParallelTestCasePassedCaptureGroup: CaptureGroup { + static let outputType: OutputType = .testCase + let suite: String let testCase: String let device: String @@ -218,12 +288,16 @@ struct ParallelTestCasePassedCaptureGroup: CaptureGroup { } struct ParallelTestCaseAppKitPassedCaptureGroup: CaptureGroup { + static let outputType: OutputType = .testCase + let suite: String let testCase: String let time: String } struct ParallelTestCaseFailedCaptureGroup: CaptureGroup { + static let outputType: OutputType = .error + let suite: String let testCase: String let device: String @@ -231,209 +305,310 @@ struct ParallelTestCaseFailedCaptureGroup: CaptureGroup { } struct ParallelTestingStartedCaptureGroup: CaptureGroup { + static let outputType: OutputType = .test + let device: String } struct ParallelTestingPassedCaptureGroup: CaptureGroup { + static let outputType: OutputType = .test + let device: String } struct ParallelTestingFailedCaptureGroup: CaptureGroup { + static let outputType: OutputType = .nonContextualError + let device: String } struct ParallelTestSuiteStartedCaptureGroup: CaptureGroup { + static let outputType: OutputType = .test + let suite: String let device: String } struct PhaseSuccessCaptureGroup: CaptureGroup { + static let outputType: OutputType = .result + let phase: String } struct PhaseScriptExecutionCaptureGroup: CaptureGroup { + static let outputType: OutputType = .task + let phaseName: String let target: String } struct ProcessPchCaptureGroup: CaptureGroup { + static let outputType: OutputType = .task + let file: String let buildTarget: String } struct ProcessPchCommandCaptureGroup: CaptureGroup { + static let outputType: OutputType = .task + let filePath: String } struct PreprocessCaptureGroup: CaptureGroup { + static let outputType: OutputType = .task + let file: String } struct PbxcpCaptureGroup: CopyCaptureGroup { + static let outputType: OutputType = .task + let file: String let targetFile: String let target: String } struct ProcessInfoPlistCaptureGroup: CaptureGroup { + static let outputType: OutputType = .task + let filePath: String let filename: String let target: String? // Xcode 10+ } struct TestsRunCompletionCaptureGroup: CaptureGroup { + static let outputType: OutputType = .test + let suite: String let result: String let time: String } struct TestSuiteStartedCaptureGroup: CaptureGroup { + static let outputType: OutputType = .test + let suite: String let time: String } struct TestSuiteStartCaptureGroup: CaptureGroup { + static let outputType: OutputType = .test + let testSuiteName: String } -struct TestSuiteAllTestsPassedCaptureGroup: CaptureGroup { } +struct TestSuiteAllTestsPassedCaptureGroup: CaptureGroup { + static let outputType: OutputType = .result // FIXME + + } -struct TestSuiteAllTestsFailedCaptureGroup: CaptureGroup { } +struct TestSuiteAllTestsFailedCaptureGroup: CaptureGroup { + static let outputType: OutputType = .result // FIXME + } struct TIFFutilCaptureGroup: CaptureGroup { + static let outputType: OutputType = .task + let filename: String } struct TouchCaptureGroup: CaptureGroup { + static let outputType: OutputType = .task + let filename: String let target: String } struct WriteFileCaptureGroup: CaptureGroup { + static let outputType: OutputType = .task + let filePath: String } -struct WriteAuxiliaryFilesCaptureGroup: CaptureGroup { } +struct WriteAuxiliaryFilesCaptureGroup: CaptureGroup { + static let outputType: OutputType = .task + } struct CompileWarningCaptureGroup: CaptureGroup { + static let outputType: OutputType = .warning + let filePath: String let filename: String let reason: String } struct LDWarningCaptureGroup: CaptureGroup { + static let outputType: OutputType = .warning + let ldPrefix: String let warningMessage: String } struct GenericWarningCaptureGroup: CaptureGroup { + static let outputType: OutputType = .warning + let wholeWarning: String } struct WillNotBeCodeSignedCaptureGroup: CaptureGroup { + static let outputType: OutputType = .warning + let wholeWarning: String } struct DuplicateLocalizedStringKeyCaptureGroup: CaptureGroup { + static let outputType: OutputType = .warning + let warningMessage: String } struct ClangErrorCaptureGroup: ErrorCaptureGroup { + static let outputType: OutputType = .error + let wholeError: String } struct CheckDependenciesErrorsCaptureGroup: ErrorCaptureGroup { + static let outputType: OutputType = .error + let wholeError: String } struct ProvisioningProfileRequiredCaptureGroup: ErrorCaptureGroup { + static let outputType: OutputType = .error + let wholeError: String } struct NoCertificateCaptureGroup: ErrorCaptureGroup { + static let outputType: OutputType = .warning + let wholeError: String } struct CompileErrorCaptureGroup: CaptureGroup { + static let outputType: OutputType = .error + let filePath: String let isFatalError: String let reason: String } struct CursorCaptureGroup: CaptureGroup { + static let outputType: OutputType = .warning + let cursor: String } struct FatalErrorCaptureGroup: ErrorCaptureGroup { + static let outputType: OutputType = .error + let wholeError: String } struct FileMissingErrorCaptureGroup: CaptureGroup { + static let outputType: OutputType = .error + let reason: String let filePath: String } struct LDErrorCaptureGroup: ErrorCaptureGroup { + static let outputType: OutputType = .error + let wholeError: String } struct LinkerDuplicateSymbolsLocationCaptureGroup: CaptureGroup { + static let outputType: OutputType = .error + let wholeError: String } struct LinkerDuplicateSymbolsCaptureGroup: CaptureGroup { + static let outputType: OutputType = .error + let reason: String } struct LinkerUndefinedSymbolLocationCaptureGroup: CaptureGroup { + static let outputType: OutputType = .error + let symbolLocation: String } struct LinkerUndefinedSymbolsCaptureGroup: CaptureGroup { + static let outputType: OutputType = .error + let reason: String } struct PodsErrorCaptureGroup: ErrorCaptureGroup { + static let outputType: OutputType = .error + let wholeError: String } struct SymbolReferencedFromCaptureGroup: CaptureGroup { + static let outputType: OutputType = .error + let reference: String } struct ModuleIncludesErrorCaptureGroup: ErrorCaptureGroup { + static let outputType: OutputType = .error + let wholeError: String } struct UndefinedSymbolLocationCaptureGroup: CaptureGroup { + static let outputType: OutputType = .warning + let target: String let filename: String } struct PackageFetchingCaptureGroup: CaptureGroup { + static let outputType: OutputType = .task + let source: String } struct PackageUpdatingCaptureGroup: CaptureGroup { + static let outputType: OutputType = .task + let source: String } struct PackageCheckingOutCaptureGroup: CaptureGroup { + static let outputType: OutputType = .task + let version: String let package: String } -struct PackageGraphResolvingStartCaptureGroup: CaptureGroup { } +struct PackageGraphResolvingStartCaptureGroup: CaptureGroup { + static let outputType: OutputType = .task + + } + +struct PackageGraphResolvingEndedCaptureGroup: CaptureGroup { + static let outputType: OutputType = .task -struct PackageGraphResolvingEndedCaptureGroup: CaptureGroup { } + } struct PackageGraphResolvedItemCaptureGroup: CaptureGroup { + static let outputType: OutputType = .task + let packageName: String let packageURL: String let packageVersion: String } struct XcodebuildErrorCaptureGroup: ErrorCaptureGroup { + static let outputType: OutputType = .error + let wholeError: String } diff --git a/Sources/XcbeautifyLib/Parser.swift b/Sources/XcbeautifyLib/Parser.swift index bce920c7..1c06b191 100644 --- a/Sources/XcbeautifyLib/Parser.swift +++ b/Sources/XcbeautifyLib/Parser.swift @@ -1,17 +1,11 @@ public class Parser { - - private let colored: Bool - private let renderer: OutputRendering - private let additionalLines: () -> String? - private(set) var summary: TestSummary? = nil + public private(set) var summary: TestSummary? = nil private(set) var needToRecordSummary = false - public var preserveUnbeautifiedLines = false - public var outputType: OutputType = OutputType.undefined private lazy var innerParsers: [InnerParser] = [ @@ -100,26 +94,13 @@ public class Parser { // MARK: - Init public init( - colored: Bool = true, - renderer: Renderer, - preserveUnbeautifiedLines: Bool = false, additionalLines: @escaping () -> (String?) ) { - self.colored = colored - - switch renderer { - case .terminal: - self.renderer = TerminalRenderer(colored: colored) - case .gitHubActions: - self.renderer = GitHubActionsRenderer() - } - - self.preserveUnbeautifiedLines = preserveUnbeautifiedLines self.additionalLines = additionalLines } - public func parse(line: String) -> String? { - + public func parse(line: String) -> CaptureGroup? { + // Find first parser that can parse specified string guard let idx = innerParsers.firstIndex(where: { $0.regex.match(string: line)}) else { @@ -127,13 +108,13 @@ public class Parser { if Regex.executedWithoutSkipped.match(string: line) { outputType = OutputType.task - parseSummary(line: line, colored: colored, skipped: false) + parseSummary(line: line, skipped: false) return nil } if Regex.executedWithSkipped.match(string: line) { outputType = OutputType.task - parseSummary(line: line, colored: colored, skipped: true) + parseSummary(line: line, skipped: true) return nil } @@ -149,7 +130,7 @@ public class Parser { // Nothing found? outputType = OutputType.undefined - return preserveUnbeautifiedLines ? line : nil + return nil } let parser = innerParsers[idx] @@ -160,17 +141,12 @@ public class Parser { // Move found parser to the top, so next time it will be checked first innerParsers.insert(innerParsers.remove(at: idx), at: 0) - return result.value - } - - public func formattedSummary() -> String? { - guard let summary = summary else { return nil } - return renderer.format(testSummary: summary) + return result.captureGroup } // MARK: Private - private func parseSummary(line: String, colored: Bool, skipped: Bool) { + private func parseSummary(line: String, skipped: Bool) { guard needToRecordSummary else { return } defer { needToRecordSummary = false } @@ -188,8 +164,6 @@ public class Parser { private func innerParser(_ regex: Regex, outputType: OutputType) -> InnerParser { return InnerParser( - additionalLines: additionalLines, - renderer: renderer, regex: regex, outputType: outputType ) @@ -199,22 +173,16 @@ public class Parser { fileprivate struct Result { let outputType: OutputType - let value: String? + let captureGroup: CaptureGroup } - let additionalLines: () -> String? - let renderer: OutputRendering let regex: Regex let outputType: OutputType func parse(line: String) -> Result { return Result( outputType: outputType, - value: renderer.beautify( - line: line, - pattern: regex.pattern, - additionalLines: additionalLines - ) + captureGroup: line.captureGroup(with: regex.pattern) ) } } diff --git a/Sources/XcbeautifyLib/Renderers/OutputRendering.swift b/Sources/XcbeautifyLib/Renderers/OutputRendering.swift index 1da1c797..9e0b5c6e 100644 --- a/Sources/XcbeautifyLib/Renderers/OutputRendering.swift +++ b/Sources/XcbeautifyLib/Renderers/OutputRendering.swift @@ -3,8 +3,6 @@ import Foundation protocol OutputRendering { var colored: Bool { get } - func beautify(line: String, pattern: Pattern, additionalLines: @escaping () -> (String?)) -> String? - func format(testSummary: TestSummary) -> String func format(line: String, command: String, pattern: Pattern, arguments: String) -> String? @@ -75,190 +73,6 @@ protocol OutputRendering { func formatWriteFile(group: WriteFileCaptureGroup) -> String? } -extension OutputRendering { - func beautify( - line: String, - pattern: Pattern, - additionalLines: @escaping () -> (String?) - ) -> String? { - let group: CaptureGroup = line.captureGroup(with: pattern) - - switch (pattern, group) { - case (.aggregateTarget, let group as AggregateTargetCaptureGroup): - return formatTargetCommand(command: "Aggregate", group: group) - case (.analyze, let group as AnalyzeCaptureGroup): - return formatAnalyze(group: group) - case (.analyzeTarget, let group as AnalyzeTargetCaptureGroup): - return formatTargetCommand(command: "Analyze", group: group) - case (.buildTarget, let group as BuildTargetCaptureGroup): - return formatTargetCommand(command: "Build", group: group) - case (.checkDependencies, _ as CheckDependenciesCaptureGroup): - return format(line: line, command: "Check Dependencies", pattern: .checkDependencies, arguments: "") - case (.checkDependenciesErrors, let group as CheckDependenciesErrorsCaptureGroup): - return formatError(group: group) - case (.clangError, let group as ClangErrorCaptureGroup): - return formatError(group: group) - case (.cleanRemove, let group as CleanRemoveCaptureGroup): - return formatCleanRemove(group: group) - case (.cleanTarget, let group as CleanTargetCaptureGroup): - return formatTargetCommand(command: "Clean", group: group) - case (.codesign, let group as CodesignCaptureGroup): - return formatCodeSign(group: group) - case (.codesignFramework, let group as CodesignFrameworkCaptureGroup): - return formatCodeSignFramework(group: group) - case (.compile, let group as CompileCaptureGroup): - return formatCompile(group: group) - case (.compileCommand, let group as CompileCommandCaptureGroup): - return formatCompileCommand(group: group) - case (.compileError, let group as CompileErrorCaptureGroup): - return formatCompileError(group: group, additionalLines: additionalLines) - case (.compileStoryboard, let group as CompileStoryboardCaptureGroup): - return formatCompile(group: group) - case (.compileWarning, let group as CompileWarningCaptureGroup): - return formatCompileWarning(group: group, additionalLines: additionalLines) - case (.compileXib, let group as CompileXibCaptureGroup): - return formatCompile(group: group) - case (.copyHeader, let group as CopyHeaderCaptureGroup): - return formatCopy(group: group) - case (.copyPlist, let group as CopyPlistCaptureGroup): - return formatCopy(group: group) - case (.copyStrings, let group as CopyStringsCaptureGroup): - return formatCopy(group: group) - case (.cpresource, let group as CpresourceCaptureGroup): - return formatCopy(group: group) - case (.cursor, let group as CursorCaptureGroup): - return formatCursor(group: group) - case (.duplicateLocalizedStringKey, let group as DuplicateLocalizedStringKeyCaptureGroup): - return formatDuplicateLocalizedStringKey(group: group) - case (.executedWithoutSkipped, let group as ExecutedWithoutSkippedCaptureGroup): - return formatExecutedWithoutSkipped(group: group) - case (.executedWithSkipped, let group as ExecutedWithSkippedCaptureGroup): - return formatExecutedWithSkipped(group: group) - case (.failingTest, let group as FailingTestCaptureGroup): - return formatFailingTest(group: group) - case (.fatalError, let group as FatalErrorCaptureGroup): - return formatError(group: group) - case (.fileMissingError, let group as FileMissingErrorCaptureGroup): - return formatFileMissingError(group: group) - case (.generateCoverageData, let group as GenerateCoverageDataCaptureGroup): - return formatGenerateCoverageData(group: group) - case (.generatedCoverageReport, let group as GeneratedCoverageReportCaptureGroup): - return formatCoverageReport(group: group) - case (.generateDsym, let group as GenerateDSYMCaptureGroup): - return formatGenerateDsym(group: group) - case (.genericWarning, let group as GenericWarningCaptureGroup): - return formatWarning(group: group) - case (.ldError, let group as LDErrorCaptureGroup): - return formatError(group: group) - case (.ldWarning, let group as LDWarningCaptureGroup): - return formatLdWarning(group: group) - case (.libtool, let group as LibtoolCaptureGroup): - return formatLibtool(group: group) - case (.linkerDuplicateSymbols, let group as LinkerDuplicateSymbolsCaptureGroup): - return formatLinkerDuplicateSymbolsError(group: group) - case (.linkerDuplicateSymbolsLocation, let group as LinkerDuplicateSymbolsLocationCaptureGroup): - return formatLinkerDuplicateSymbolsLocation(group: group) - case (.linkerUndefinedSymbolLocation, let group as LinkerUndefinedSymbolLocationCaptureGroup): - return formatLinkerUndefinedSymbolLocation(group: group) - case (.linkerUndefinedSymbols, let group as LinkerUndefinedSymbolsCaptureGroup): - return formatLinkerUndefinedSymbolsError(group: group) - case (.linking, let group as LinkingCaptureGroup): - return formatLinking(group: group) - case (.moduleIncludesError, let group as ModuleIncludesErrorCaptureGroup): - return formatError(group: group) - case (.noCertificate, let group as NoCertificateCaptureGroup): - return formatError(group: group) - case (.packageCheckingOut, let group as PackageCheckingOutCaptureGroup): - return formatPackageCheckingOut(group: group) - case (.packageFetching, let group as PackageFetchingCaptureGroup): - return formatPackageFetching(group: group) - case (.packageGraphResolvedItem, let group as PackageGraphResolvedItemCaptureGroup): - return formatPackageItem(group: group) - case (.packageGraphResolvingEnded, _ as PackageGraphResolvingEndedCaptureGroup): - return formatPackageEnd() - case (.packageGraphResolvingStart, _ as PackageGraphResolvingStartCaptureGroup): - return formatPackageStart() - case (.packageUpdating, let group as PackageUpdatingCaptureGroup): - return formatPackageUpdating(group: group) - case (.parallelTestCaseAppKitPassed, let group as ParallelTestCaseAppKitPassedCaptureGroup): - return formatParallelTestCaseAppKitPassed(group: group) - case (.parallelTestCaseFailed, let group as ParallelTestCaseFailedCaptureGroup): - return formatParallelTestCaseFailed(group: group) - case (.parallelTestCasePassed, let group as ParallelTestCasePassedCaptureGroup): - return formatParallelTestCasePassed(group: group) - case (.parallelTestingFailed, let group as ParallelTestingFailedCaptureGroup): - return formatParallelTestingFailed(line: line, group: group) - case (.parallelTestingPassed, let group as ParallelTestingPassedCaptureGroup): - return formatParallelTestingPassed(line: line, group: group) - case (.parallelTestingStarted, let group as ParallelTestingStartedCaptureGroup): - return formatParallelTestingStarted(line: line, group: group) - case (.parallelTestSuiteStarted, let group as ParallelTestSuiteStartedCaptureGroup): - return formatParallelTestSuiteStarted(group: group) - case (.pbxcp, let group as PbxcpCaptureGroup): - return formatCopy(group: group) - case (.phaseScriptExecution, let group as PhaseScriptExecutionCaptureGroup): - return formatPhaseScriptExecution(group: group) - case (.phaseSuccess, let group as PhaseSuccessCaptureGroup): - return formatPhaseSuccess(group: group) - case (.podsError, let group as PodsErrorCaptureGroup): - return formatError(group: group) - case (.preprocess, _ as PreprocessCaptureGroup): - return format(line: line, command: "Preprocessing", pattern: pattern, arguments: "$1") - case (.processInfoPlist, let group as ProcessInfoPlistCaptureGroup): - return formatProcessInfoPlist(group: group) - case (.processPch, let group as ProcessPchCaptureGroup): - return formatProcessPch(group: group) - case (.processPchCommand, let group as ProcessPchCommandCaptureGroup): - return formatProcessPchCommand(group: group) - case (.provisioningProfileRequired, let group as ProvisioningProfileRequiredCaptureGroup): - return formatError(group: group) - case (.restartingTest, let group as RestartingTestCaptureGroup): - return formatRestartingTest(line: line, group: group) - case (.shellCommand, let group as ShellCommandCaptureGroup): - return formatShellCommand(group: group) - case (.symbolReferencedFrom, _ as SymbolReferencedFromCaptureGroup): - return formatCompleteError(line: line) - case (.testCaseMeasured, let group as TestCaseMeasuredCaptureGroup): - return formatTestCaseMeasured(group: group) - case (.testCasePassed, let group as TestCasePassedCaptureGroup): - return formatTestCasePassed(group: group) - case (.testCasePending, let group as TestCasePendingCaptureGroup): - return formatTestCasePending(group: group) - case (.testCaseStarted, let group as TestCaseStartedCaptureGroup): - return formatTestCasesStarted(group: group) - case (.testsRunCompletion, let group as TestsRunCompletionCaptureGroup): - return formatTestsRunCompletion(group: group) - case (.testSuiteAllTestsFailed, let group as TestSuiteAllTestsFailedCaptureGroup): - return formatTestSuiteAllTestsFailed(group: group) - case (.testSuiteAllTestsPassed, let group as TestSuiteAllTestsPassedCaptureGroup): - return formatTestSuiteAllTestsPassed(group: group) - case (.testSuiteStart, let group as TestSuiteStartCaptureGroup): - return formatTestSuiteStart(group: group) - case (.testSuiteStarted, let group as TestSuiteStartedCaptureGroup): - return formatTestSuiteStarted(group: group) - case (.tiffutil, let group as TIFFutilCaptureGroup): - return formatTIFFUtil(group: group) - case (.touch, let group as TouchCaptureGroup): - return formatTouch(group: group) - case (.uiFailingTest, let group as UIFailingTestCaptureGroup): - return formatUIFailingTest(group: group) - case (.undefinedSymbolLocation, _ as UndefinedSymbolLocationCaptureGroup): - return formatCompleteWarning(line: line) - case (.willNotBeCodeSigned, let group as WillNotBeCodeSignedCaptureGroup): - return formatWillNotBeCodesignWarning(group: group) - case (.writeAuxiliaryFiles, let group as WriteAuxiliaryFilesCaptureGroup): - return formatWriteAuxiliaryFiles(group: group) - case (.writeFile, let group as WriteFileCaptureGroup): - return formatWriteFile(group: group) - case (.xcodebuildError, let group as XcodebuildErrorCaptureGroup): - return formatError(group: group) - case (_, _): - assertionFailure() - return nil - } - } -} - extension OutputRendering { func format(line: String, command: String, pattern: Pattern, arguments: String) -> String? { diff --git a/Sources/XcbeautifyLib/TestSummary.swift b/Sources/XcbeautifyLib/TestSummary.swift index 53c6c142..a2d1c948 100644 --- a/Sources/XcbeautifyLib/TestSummary.swift +++ b/Sources/XcbeautifyLib/TestSummary.swift @@ -1,4 +1,4 @@ -struct TestSummary { +public struct TestSummary { let testsCount: Int let skippedCount: Int let failuresCount: Int diff --git a/Sources/XcbeautifyLib/XCFormatter.swift b/Sources/XcbeautifyLib/XCFormatter.swift new file mode 100644 index 00000000..35be0619 --- /dev/null +++ b/Sources/XcbeautifyLib/XCFormatter.swift @@ -0,0 +1,205 @@ +import Foundation + +public struct XCFormatter { + private let renderer: OutputRendering + private let colored: Bool + private let additionalLines: () -> String? + + public init( + renderer: Renderer, + colored: Bool, + additionalLines: @escaping () -> String? + ) { + switch renderer { + case .terminal: + self.renderer = TerminalRenderer(colored: colored) + case .gitHubActions: + self.renderer = GitHubActionsRenderer() + } + self.colored = colored + self.additionalLines = additionalLines + } + + public func beautify( + captureGroup: CaptureGroup, + line: String + ) -> String? { + switch captureGroup { + case let group as AggregateTargetCaptureGroup: + return renderer.formatTargetCommand(command: "Aggregate", group: group) + case let group as AnalyzeCaptureGroup: + return renderer.formatAnalyze(group: group) + case let group as AnalyzeTargetCaptureGroup: + return renderer.formatTargetCommand(command: "Analyze", group: group) + case let group as BuildTargetCaptureGroup: + return renderer.formatTargetCommand(command: "Build", group: group) + case _ as CheckDependenciesCaptureGroup: + return renderer.format(line: line, command: "Check Dependencies", pattern: .checkDependencies, arguments: "") + case let group as CheckDependenciesErrorsCaptureGroup: + return renderer.formatError(group: group) + case let group as ClangErrorCaptureGroup: + return renderer.formatError(group: group) + case let group as CleanRemoveCaptureGroup: + return renderer.formatCleanRemove(group: group) + case let group as CleanTargetCaptureGroup: + return renderer.formatTargetCommand(command: "Clean", group: group) + case let group as CodesignCaptureGroup: + return renderer.formatCodeSign(group: group) + case let group as CodesignFrameworkCaptureGroup: + return renderer.formatCodeSignFramework(group: group) + case let group as CompileCaptureGroup: + return renderer.formatCompile(group: group) + case let group as CompileCommandCaptureGroup: + return renderer.formatCompileCommand(group: group) + case let group as CompileErrorCaptureGroup: + return renderer.formatCompileError(group: group, additionalLines: additionalLines) + case let group as CompileStoryboardCaptureGroup: + return renderer.formatCompile(group: group) + case let group as CompileWarningCaptureGroup: + return renderer.formatCompileWarning(group: group, additionalLines: additionalLines) + case let group as CompileXibCaptureGroup: + return renderer.formatCompile(group: group) + case let group as CopyHeaderCaptureGroup: + return renderer.formatCopy(group: group) + case let group as CopyPlistCaptureGroup: + return renderer.formatCopy(group: group) + case let group as CopyStringsCaptureGroup: + return renderer.formatCopy(group: group) + case let group as CpresourceCaptureGroup: + return renderer.formatCopy(group: group) + case let group as CursorCaptureGroup: + return renderer.formatCursor(group: group) + case let group as DuplicateLocalizedStringKeyCaptureGroup: + return renderer.formatDuplicateLocalizedStringKey(group: group) + case let group as ExecutedWithoutSkippedCaptureGroup: + return renderer.formatExecutedWithoutSkipped(group: group) + case let group as ExecutedWithSkippedCaptureGroup: + return renderer.formatExecutedWithSkipped(group: group) + case let group as FailingTestCaptureGroup: + return renderer.formatFailingTest(group: group) + case let group as FatalErrorCaptureGroup: + return renderer.formatError(group: group) + case let group as FileMissingErrorCaptureGroup: + return renderer.formatFileMissingError(group: group) + case let group as GenerateCoverageDataCaptureGroup: + return renderer.formatGenerateCoverageData(group: group) + case let group as GeneratedCoverageReportCaptureGroup: + return renderer.formatCoverageReport(group: group) + case let group as GenerateDSYMCaptureGroup: + return renderer.formatGenerateDsym(group: group) + case let group as GenericWarningCaptureGroup: + return renderer.formatWarning(group: group) + case let group as LDErrorCaptureGroup: + return renderer.formatError(group: group) + case let group as LDWarningCaptureGroup: + return renderer.formatLdWarning(group: group) + case let group as LibtoolCaptureGroup: + return renderer.formatLibtool(group: group) + case let group as LinkerDuplicateSymbolsCaptureGroup: + return renderer.formatLinkerDuplicateSymbolsError(group: group) + case let group as LinkerDuplicateSymbolsLocationCaptureGroup: + return renderer.formatLinkerDuplicateSymbolsLocation(group: group) + case let group as LinkerUndefinedSymbolLocationCaptureGroup: + return renderer.formatLinkerUndefinedSymbolLocation(group: group) + case let group as LinkerUndefinedSymbolsCaptureGroup: + return renderer.formatLinkerUndefinedSymbolsError(group: group) + case let group as LinkingCaptureGroup: + return renderer.formatLinking(group: group) + case let group as ModuleIncludesErrorCaptureGroup: + return renderer.formatError(group: group) + case let group as NoCertificateCaptureGroup: + return renderer.formatError(group: group) + case let group as PackageCheckingOutCaptureGroup: + return renderer.formatPackageCheckingOut(group: group) + case let group as PackageFetchingCaptureGroup: + return renderer.formatPackageFetching(group: group) + case let group as PackageGraphResolvedItemCaptureGroup: + return renderer.formatPackageItem(group: group) + case _ as PackageGraphResolvingEndedCaptureGroup: + return renderer.formatPackageEnd() + case _ as PackageGraphResolvingStartCaptureGroup: + return renderer.formatPackageStart() + case let group as PackageUpdatingCaptureGroup: + return renderer.formatPackageUpdating(group: group) + case let group as ParallelTestCaseAppKitPassedCaptureGroup: + return renderer.formatParallelTestCaseAppKitPassed(group: group) + case let group as ParallelTestCaseFailedCaptureGroup: + return renderer.formatParallelTestCaseFailed(group: group) + case let group as ParallelTestCasePassedCaptureGroup: + return renderer.formatParallelTestCasePassed(group: group) + case let group as ParallelTestingFailedCaptureGroup: + return renderer.formatParallelTestingFailed(line: line, group: group) + case let group as ParallelTestingPassedCaptureGroup: + return renderer.formatParallelTestingPassed(line: line, group: group) + case let group as ParallelTestingStartedCaptureGroup: + return renderer.formatParallelTestingStarted(line: line, group: group) + case let group as ParallelTestSuiteStartedCaptureGroup: + return renderer.formatParallelTestSuiteStarted(group: group) + case let group as PbxcpCaptureGroup: + return renderer.formatCopy(group: group) + case let group as PhaseScriptExecutionCaptureGroup: + return renderer.formatPhaseScriptExecution(group: group) + case let group as PhaseSuccessCaptureGroup: + return renderer.formatPhaseSuccess(group: group) + case let group as PodsErrorCaptureGroup: + return renderer.formatError(group: group) + case _ as PreprocessCaptureGroup: + return renderer.format(line: line, command: "Preprocessing", pattern: .preprocess, arguments: "$1") + case let group as ProcessInfoPlistCaptureGroup: + return renderer.formatProcessInfoPlist(group: group) + case let group as ProcessPchCaptureGroup: + return renderer.formatProcessPch(group: group) + case let group as ProcessPchCommandCaptureGroup: + return renderer.formatProcessPchCommand(group: group) + case let group as ProvisioningProfileRequiredCaptureGroup: + return renderer.formatError(group: group) + case let group as RestartingTestCaptureGroup: + return renderer.formatRestartingTest(line: line, group: group) + case let group as ShellCommandCaptureGroup: + return renderer.formatShellCommand(group: group) + case _ as SymbolReferencedFromCaptureGroup: + return renderer.formatCompleteError(line: line) + case let group as TestCaseMeasuredCaptureGroup: + return renderer.formatTestCaseMeasured(group: group) + case let group as TestCasePassedCaptureGroup: + return renderer.formatTestCasePassed(group: group) + case let group as TestCasePendingCaptureGroup: + return renderer.formatTestCasePending(group: group) + case let group as TestCaseStartedCaptureGroup: + return renderer.formatTestCasesStarted(group: group) + case let group as TestsRunCompletionCaptureGroup: + return renderer.formatTestsRunCompletion(group: group) + case let group as TestSuiteAllTestsFailedCaptureGroup: + return renderer.formatTestSuiteAllTestsFailed(group: group) + case let group as TestSuiteAllTestsPassedCaptureGroup: + return renderer.formatTestSuiteAllTestsPassed(group: group) + case let group as TestSuiteStartCaptureGroup: + return renderer.formatTestSuiteStart(group: group) + case let group as TestSuiteStartedCaptureGroup: + return renderer.formatTestSuiteStarted(group: group) + case let group as TIFFutilCaptureGroup: + return renderer.formatTIFFUtil(group: group) + case let group as TouchCaptureGroup: + return renderer.formatTouch(group: group) + case let group as UIFailingTestCaptureGroup: + return renderer.formatUIFailingTest(group: group) + case _ as UndefinedSymbolLocationCaptureGroup: + return renderer.formatCompleteWarning(line: line) + case let group as WillNotBeCodeSignedCaptureGroup: + return renderer.formatWillNotBeCodesignWarning(group: group) + case let group as WriteAuxiliaryFilesCaptureGroup: + return renderer.formatWriteAuxiliaryFiles(group: group) + case let group as WriteFileCaptureGroup: + return renderer.formatWriteFile(group: group) + case let group as XcodebuildErrorCaptureGroup: + return renderer.formatError(group: group) + default: + assertionFailure() + return nil + } + } + + public func formatTestSummary(testSummary: TestSummary) -> String { + renderer.format(testSummary: testSummary) + } +} diff --git a/Sources/xcbeautify/Xcbeautify.swift b/Sources/xcbeautify/Xcbeautify.swift index 764fb82e..06786170 100644 --- a/Sources/xcbeautify/Xcbeautify.swift +++ b/Sources/xcbeautify/Xcbeautify.swift @@ -50,18 +50,23 @@ struct Xcbeautify: ParsableCommand { } let parser = Parser( - colored: !disableColoredOutput, + additionalLines: { readLine() } + ) + + let formatter = XCFormatter( renderer: renderer, - preserveUnbeautifiedLines: preserveUnbeautified, + colored: !disableColoredOutput, additionalLines: { readLine() } ) while let line = readLine() { - guard let formatted = parser.parse(line: line) else { continue } + guard let captureGroup = parser.parse(line: line) else { continue } + guard let formatted = formatter.beautify(captureGroup: captureGroup, line: line) else { continue } output.write(parser.outputType, formatted) } - if let formattedSummary = parser.formattedSummary() { + if let summary = parser.summary { + let formattedSummary = formatter.formatTestSummary(testSummary: summary) output.write(.result, formattedSummary) } diff --git a/Tests/XcbeautifyLibTests/RendererTests/GitHubActionsRendererTests.swift b/Tests/XcbeautifyLibTests/RendererTests/GitHubActionsRendererTests.swift index c0662c72..272d86a9 100644 --- a/Tests/XcbeautifyLibTests/RendererTests/GitHubActionsRendererTests.swift +++ b/Tests/XcbeautifyLibTests/RendererTests/GitHubActionsRendererTests.swift @@ -3,14 +3,17 @@ import XCTest final class GitHubActionsRendererTests: XCTestCase { var parser: Parser! + var formatter: XCFormatter! override func setUpWithError() throws { try super.setUpWithError() - parser = Parser(colored: false, renderer: .gitHubActions, additionalLines: { nil } ) + parser = Parser(additionalLines: { nil } ) + formatter = XCFormatter(renderer: .gitHubActions, colored: false, additionalLines: { nil }) } private func logFormatted(_ string: String) -> String? { - return parser.parse(line: string) + guard let captureGroup = parser.parse(line: string) else { return nil } + return formatter.beautify(captureGroup: captureGroup, line: string) } func testAggregateTarget() { diff --git a/Tests/XcbeautifyLibTests/RendererTests/TerminalRendererTests.swift b/Tests/XcbeautifyLibTests/RendererTests/TerminalRendererTests.swift index 70c19b78..dc638556 100644 --- a/Tests/XcbeautifyLibTests/RendererTests/TerminalRendererTests.swift +++ b/Tests/XcbeautifyLibTests/RendererTests/TerminalRendererTests.swift @@ -3,14 +3,17 @@ import XCTest final class TerminalRendererTests: XCTestCase { var parser: Parser! + var formatter: XCFormatter! override func setUpWithError() throws { try super.setUpWithError() - parser = Parser(colored: false, renderer: .terminal, additionalLines: { nil } ) + parser = Parser(additionalLines: { nil } ) + formatter = XCFormatter(renderer: .terminal, colored: false, additionalLines: { nil }) } private func noColoredFormatted(_ string: String) -> String? { - return parser.parse(line: string) + guard let captureGroup = parser.parse(line: string) else { return nil } + return formatter.beautify(captureGroup: captureGroup, line: string) } func testAggregateTarget() {