diff --git a/.github/workflows/unittest.yml b/.github/workflows/unittest.yml index 6c8f8a5..1dd678d 100644 --- a/.github/workflows/unittest.yml +++ b/.github/workflows/unittest.yml @@ -13,7 +13,7 @@ concurrency: cancel-in-progress: true env: - SCHEME: "DBXCResultParser-Package" + SCHEME: "DBXCResultParser" DESTINATION: "platform=OS X" jobs: diff --git a/Package.resolved b/Package.resolved new file mode 100644 index 0000000..b1ff0a0 --- /dev/null +++ b/Package.resolved @@ -0,0 +1,14 @@ +{ + "pins" : [ + { + "identity" : "swift-argument-parser", + "kind" : "remoteSourceControl", + "location" : "https://github.com/apple/swift-argument-parser.git", + "state" : { + "revision" : "c8ed701b513cf5177118a175d85fbbbcd707ab41", + "version" : "1.3.0" + } + } + ], + "version" : 2 +} diff --git a/Package.swift b/Package.swift index 538d041..03859bb 100644 --- a/Package.swift +++ b/Package.swift @@ -1,4 +1,4 @@ -// swift-tools-version:5.3 +// swift-tools-version:5.8 // The swift-tools-version declares the minimum version of Swift required to build this package. import PackageDescription @@ -12,26 +12,23 @@ let package = Package( platforms: [ .macOS(.v10_15) ], - products: [ - // Products define the executables and libraries a package produces, and make them visible to other packages. - .library( - name: packageName, - targets: [packageName] - ), - .library( - name: packageTestHelpersName, - targets: [packageTestHelpersName] - ), - ], dependencies: [ - + .package( + url: "https://github.com/apple/swift-argument-parser.git", + .upToNextMajor(from: "1.0.0") + ), ], targets: [ // Targets are the basic building blocks of a package. A target can define a module or a test suite. // Targets can depend on other targets in this package, and on products in packages this package depends on. - .target( + .executableTarget( name: packageName, - dependencies: [] + dependencies: [ + .product( + name: "ArgumentParser", + package: "swift-argument-parser" + ) + ] ), .target( name: packageTestHelpersName, @@ -46,7 +43,7 @@ let package = Package( .init(stringLiteral: packageTestHelpersName) ], resources: [ - .process("Resources/DBXCResultParser.xcresult") + .copy("Resources/DBXCResultParser.xcresult") ] ), ] diff --git a/README.md b/README.md index 8c6c278..d4452c9 100644 --- a/README.md +++ b/README.md @@ -10,8 +10,23 @@ The `DBXCReportModel` package provides a Swift module for parsing `.xcresult` fi - Calculates total and average test durations, as well as combined test statuses. - Supports identifying slow tests based on average duration. - Includes utility functions for filtering tests based on status. +- Can be executed as a command-line tool to generate test reports directly from the terminal. -## Installation +## Command-Line Tool Usage + +The package includes a command-line tool that can be executed to generate test reports. Here is an example of how to run it: + +```bash +swift run DBXCResultParser --xcresult-path path/to/tests.xcresult +``` + +The available options are: +- `--xcresult-path`: Specifies the path to the `.xcresult` file. +- `--format`: Determines the output format (`list` or `count`). +- `--locale`: Sets the locale for number and measurement formatting (e.g., "en-GB"). +- `--include`: Filters the test results to include only certain statuses (e.g., `failure,skipped`). + +## Usage To use `DBXCReportModel` in your Swift package, add it to the dependencies for your `Package.swift` file: @@ -19,7 +34,7 @@ To use `DBXCReportModel` in your Swift package, add it to the dependencies for y let package = Package( name: "YourPackageName", dependencies: [ - .package(url: "https://github.com/dodobrands/DBXCResultParser", from: "1.0.0") + .package(url: "https://github.com/dodobrands/DBXCResultParser", .upToNextMajor(from: "3.0.0")) ], targets: [ .target( @@ -30,8 +45,6 @@ let package = Package( ) ``` -## Usage - To parse an `.xcresult` file and access the report data, initialize a `DBXCReportModel` with the path to the `.xcresult` file: ```swift @@ -71,7 +84,7 @@ The `DBXCTextFormatter` class provides a way to format the data from a `DBXCRepo ### Usage -To format your test report data, create an instance of `DBXCTextFormatter` with the desired output format and optionally a locale for number and measurement formatting: +To format your test report data, create an instance of `DBXCTextFormatter`: ```swift import DBXCReportModel @@ -79,19 +92,18 @@ import DBXCReportModel // Assuming you have already created a `DBXCReportModel` instance as `reportModel` let reportModel: DBXCReportModel = ... -// Create a text formatter for detailed list output -let listFormatter = DBXCTextFormatter(format: .list) +// Create a text formatter +let formatter = DBXCTextFormatter() -// Create a text formatter for summary count output with a specific locale -let countFormatter = DBXCTextFormatter(format: .count, locale: Locale(identifier: "en_US")) +// Set the desired output format and locale +formatter.format = .list +formatter.locale = Locale(identifier: "en_US") // Format the report data into a string -let detailedListOutput = listFormatter.format(reportModel) -let summaryCountOutput = countFormatter.format(reportModel) +let formattedOutput = formatter.format(reportModel) -// Print the formatted outputs -print("Detailed List Output:\n\(detailedListOutput)") -print("Summary Count Output:\n\(summaryCountOutput)") +// Print the formatted output +print("Formatted Output:\n\(formattedOutput)") ``` The `format` method can also take an array of `DBXCReportModel.Module.File.RepeatableTest.Test.Status` to filter which test results are included in the output. By default, it includes all test statuses. @@ -121,10 +133,12 @@ FileB.swift ### Customizing Number and Measurement Formatting -The `DBXCTextFormatter` allows you to specify a locale when initializing it. This locale is used to format numbers and measurements according to the provided locale's conventions. +The `DBXCTextFormatter` allows you to specify a locale when setting the property. This locale is used to format numbers and measurements according to the provided locale's conventions. ```swift -let formatter = DBXCTextFormatter(format: .count, locale: Locale(identifier: "fr_FR")) +let formatter = DBXCTextFormatter() +formatter.format = .count +formatter.locale = Locale(identifier: "fr_FR") let output = formatter.format(reportModel) print(output) // Will output numbers and durations formatted in French ``` diff --git a/Sources/DBXCResultParser/Formatters/DBXCTextFormatter.swift b/Sources/DBXCResultParser/Formatters/DBXCTextFormatter.swift index 5fc8583..189b873 100644 --- a/Sources/DBXCResultParser/Formatters/DBXCTextFormatter.swift +++ b/Sources/DBXCResultParser/Formatters/DBXCTextFormatter.swift @@ -6,33 +6,39 @@ // import Foundation +import ArgumentParser extension DBXCTextFormatter { /// Output format options - public enum Format { + public enum Format: String, Decodable { case list // Outputs detailed list of test results case count // // Outputs a summary count of test results } } -public class DBXCTextFormatter { - /// The format style to use for output - public let format: Format +@main +public class DBXCTextFormatter: ParsableCommand { + public required init() { } + + @Option(help: "Path to .xcresult") + public var xcresultPath: String + + @Option(help: "Result format") + public var format: Format = .list /// /// The locale to use for formatting numbers and measurements - public let locale: Locale? + @Option(help: "Locale to use for numbers and measurements formatting (default: system)") + public var locale: Locale? - /// Initializes a new text formatter with the specified format and locale. - /// - /// - Parameters: - /// - format: The output format to use. - /// - locale: The locale for number and measurement formatting. Defaults to `nil`. - public init( - format: Format, - locale: Locale? = nil - ) { - self.format = format - self.locale = locale + @Option(help: "Test statutes to include in report, comma separated") + public var include: String = DBXCReportModel.Module.File.RepeatableTest.Test.Status.allCases.map { $0.rawValue }.joined(separator: ",") + + public func run() throws { + let xcresultPath = URL(fileURLWithPath: xcresultPath) + let report = try DBXCReportModel(xcresultPath: xcresultPath) + let includeTestResults = include.split(separator: ",").compactMap { DBXCReportModel.Module.File.RepeatableTest.Test.Status(rawValue: String($0)) } + let result = format(report, testResults: includeTestResults) + print(result) } /// Formats the test report data into a string based on the specified format. @@ -138,3 +144,15 @@ extension NumberFormatter { return formatter } } + +extension Locale: ExpressibleByArgument { + public init?(argument: String) { + self.init(identifier: argument) + } +} + +extension DBXCTextFormatter.Format: ExpressibleByArgument { + public init?(argument: String) { + self.init(rawValue: argument) + } +} diff --git a/Sources/DBXCResultParser/Models/DBXCReportModel.swift b/Sources/DBXCResultParser/Models/DBXCReportModel.swift index 3e4fe7b..d14b229 100644 --- a/Sources/DBXCResultParser/Models/DBXCReportModel.swift +++ b/Sources/DBXCResultParser/Models/DBXCReportModel.swift @@ -124,7 +124,7 @@ extension DBXCReportModel.Module.File.RepeatableTest { } extension DBXCReportModel.Module.File.RepeatableTest.Test { - public enum Status: Equatable, CaseIterable { + public enum Status: String, Equatable, CaseIterable { case success case failure case expectedFailure diff --git a/Tests/DBXCResultParserTests/DBXCTextFormatterTests.swift b/Tests/DBXCResultParserTests/DBXCTextFormatterTests.swift index 2643340..f7b83e7 100644 --- a/Tests/DBXCResultParserTests/DBXCTextFormatterTests.swift +++ b/Tests/DBXCResultParserTests/DBXCTextFormatterTests.swift @@ -15,7 +15,9 @@ final class DBXCTextFormatterTests: XCTestCase { } func test_testResult_any_list() { - let formatter = DBXCTextFormatter(format: .list, locale: locale) + let formatter = DBXCTextFormatter() + formatter.format = .list + formatter.locale = locale let result = formatter.format(.genericReport) XCTAssertEqual(result, @@ -38,7 +40,9 @@ NotificationsSetupServiceTests } func test_testResult_success_list() { - let formatter = DBXCTextFormatter(format: .list, locale: locale) + let formatter = DBXCTextFormatter() + formatter.format = .list + formatter.locale = locale let result = formatter.format(.genericReport, testResults: [.success]) XCTAssertEqual(result, @@ -52,14 +56,18 @@ NetworkSpec } func test_testResult_any_count() { - let formatter = DBXCTextFormatter(format: .count, locale: locale) + let formatter = DBXCTextFormatter() + formatter.format = .count + formatter.locale = locale let result = formatter.format(.genericReport) XCTAssertEqual(result, "7 (0 sec)") } func test_testResult_failure_count() { - let formatter = DBXCTextFormatter(format: .count, locale: locale) + let formatter = DBXCTextFormatter() + formatter.format = .count + formatter.locale = locale let result = formatter.format(.genericReport, testResults: [.failure]) XCTAssertEqual(result, "3 (0 sec)")