diff --git a/.github/workflows/actions.yml b/.github/workflows/actions.yml
new file mode 100644
index 0000000..d0184b8
--- /dev/null
+++ b/.github/workflows/actions.yml
@@ -0,0 +1,37 @@
+name: Actions
+
+on:
+ pull_request:
+ branches:
+ - main
+
+jobs:
+
+ bb_checks:
+ name: BB Checks
+ uses: BinaryBirds/github-workflows/.github/workflows/extra_soundness.yml@main
+ with:
+ local_swift_dependencies_check_enabled : true
+
+ swiftlang_checks:
+ name: Swiftlang Checks
+ uses: swiftlang/github-workflows/.github/workflows/soundness.yml@main
+ with:
+ license_header_check_project_name: "Testify"
+ format_check_enabled : true
+ broken_symlink_check_enabled : true
+ unacceptable_language_check_enabled : true
+ api_breakage_check_enabled : false
+ docs_check_enabled : false
+ license_header_check_enabled : false
+ shell_check_enabled : false
+ yamllint_check_enabled : false
+ python_lint_check_enabled : false
+
+ swiftlang_tests:
+ name: Swiftlang Tests
+ uses: swiftlang/github-workflows/.github/workflows/swift_package_test.yml@main
+ with:
+ enable_windows_checks : false
+ linux_build_command: "swift test --parallel"
+ linux_exclude_swift_versions: "[{\"swift_version\": \"5.8\"}, {\"swift_version\": \"5.10\"}, {\"swift_version\": \"nightly\"}, {\"swift_version\": \"nightly-main\"}, {\"swift_version\": \"nightly-6.0\"}]"
\ No newline at end of file
diff --git a/.gitignore b/.gitignore
index 40b1b4f..dd3cae7 100644
--- a/.gitignore
+++ b/.gitignore
@@ -3,3 +3,4 @@
/.build
/Packages
/*.xcodeproj
+.vscode/launch.json
diff --git a/.swift-format b/.swift-format
new file mode 100644
index 0000000..58d21f0
--- /dev/null
+++ b/.swift-format
@@ -0,0 +1,64 @@
+{
+ "version": 1,
+ "lineLength": 80,
+ "maximumBlankLines": 1,
+ "fileScopedDeclarationPrivacy": {
+ "accessLevel": "private"
+ },
+ "tabWidth": 4,
+ "indentation": {
+ "spaces": 4
+ },
+ "indentConditionalCompilationBlocks": false,
+ "indentSwitchCaseLabels": false,
+ "lineBreakAroundMultilineExpressionChainComponents": true,
+ "lineBreakBeforeControlFlowKeywords": true,
+ "lineBreakBeforeEachArgument": true,
+ "lineBreakBeforeEachGenericRequirement": true,
+ "prioritizeKeepingFunctionOutputTogether": false,
+ "respectsExistingLineBreaks": true,
+ "spacesAroundRangeFormationOperators": false,
+ "multiElementCollectionTrailingCommas": true,
+ "rules": {
+ "AllPublicDeclarationsHaveDocumentation": false,
+ "AlwaysUseLiteralForEmptyCollectionInit": true,
+ "AlwaysUseLowerCamelCase": true,
+ "AmbiguousTrailingClosureOverload": true,
+ "BeginDocumentationCommentWithOneLineSummary": true,
+ "DoNotUseSemicolons": true,
+ "DontRepeatTypeInStaticProperties": true,
+ "FileScopedDeclarationPrivacy": true,
+ "FullyIndirectEnum": true,
+ "GroupNumericLiterals": true,
+ "IdentifiersMustBeASCII": true,
+ "NeverForceUnwrap": false,
+ "NeverUseForceTry": true,
+ "NeverUseImplicitlyUnwrappedOptionals": true,
+ "NoAccessLevelOnExtensionDeclaration": true,
+ "NoAssignmentInExpressions": true,
+ "NoBlockComments": true,
+ "NoCasesWithOnlyFallthrough": true,
+ "NoEmptyTrailingClosureParentheses": true,
+ "NoLabelsInCasePatterns": true,
+ "NoLeadingUnderscores": true,
+ "NoParensAroundConditions": true,
+ "NoPlaygroundLiterals": true,
+ "NoVoidReturnOnFunctionSignature": true,
+ "OmitExplicitReturns": true,
+ "OneCasePerLine": true,
+ "OneVariableDeclarationPerLine": true,
+ "OnlyOneTrailingClosureArgument": true,
+ "OrderedImports": true,
+ "ReplaceForEachWithForLoop": true,
+ "ReturnVoidInsteadOfEmptyTuple": true,
+ "TypeNamesShouldBeCapitalized": true,
+ "UseEarlyExits": true,
+ "UseLetInEveryBoundCaseVariable": true,
+ "UseShorthandTypeNames": true,
+ "UseSingleLinePropertyGetter": true,
+ "UseSynthesizedInitializer": true,
+ "UseTripleSlashForDocumentationComments": true,
+ "UseWhereClausesInForLoops": true,
+ "ValidateDocumentationComments": true
+ }
+}
diff --git a/.swiftformatignore b/.swiftformatignore
new file mode 100644
index 0000000..90955ec
--- /dev/null
+++ b/.swiftformatignore
@@ -0,0 +1,2 @@
+Package.swift
+Package@swift-5.9.swift
\ No newline at end of file
diff --git a/.unacceptablelanguageignore b/.unacceptablelanguageignore
new file mode 100644
index 0000000..045b5ba
--- /dev/null
+++ b/.unacceptablelanguageignore
@@ -0,0 +1,10 @@
+Tests/TestifySDKTests/Resources/Assets/json/Kitura.json
+Tests/TestifySDKTests/Resources/Assets/json/Kitura-coverage.json
+Tests/TestifySDKTests/Resources/Assets/json/PromiseFailure.json
+Tests/TestifySDKTests/Resources/Assets/json/Shell-coverage.json
+Tests/TestifySDKTests/Resources/Assets/json/ShellOutFailure.json
+Tests/TestifySDKTests/Resources/Assets/tests/Kitura-coverage.tests
+Tests/TestifySDKTests/Resources/Assets/tests/PromiseFailure.tests
+Tests/TestifySDKTests/Resources/Assets/tests/ShellOutFailure.tests
+Tests/TestifySDKTests/Resources/Assets/xml/Kitura.xml
+Tests/TestifySDKTests/Resources/Assets/tests/Kitura.tests
\ No newline at end of file
diff --git a/Makefile b/Makefile
index 00749f2..356a5ae 100644
--- a/Makefile
+++ b/Makefile
@@ -1,3 +1,19 @@
+SHELL=/bin/bash
+
+baseUrl = https://raw.githubusercontent.com/BinaryBirds/github-workflows/refs/heads/main/scripts
+
+test:
+ swift test --parallel
+
+language:
+ curl -s $(baseUrl)/check-unacceptable-language.sh | bash
+
+lint:
+ curl -s $(baseUrl)/run-swift-format.sh | bash
+
+format:
+ curl -s $(baseUrl)/run-swift-format.sh | bash -s -- --fix
+
release:
swift package update && swift build -c release
diff --git a/README.md b/README.md
index 7603e3a..c4fa7b7 100755
--- a/README.md
+++ b/README.md
@@ -35,7 +35,7 @@ In your project folder run:
You can just use the [Swift Package Manager](https://theswiftdev.com/2017/11/09/swift-package-manager-tutorial/) as usual:
```swift
-.package(url: "https://github.com/binarybirds/testify", from: "1.1.2"),
+.package(url: "https://github.com/binarybirds/testify", from: "1.2.0"),
```
⚠️ Don't forget to add "Testify" to your target as a dependency!
diff --git a/Sources/TestifySDK/Codable/RawTestResultDecoder.swift b/Sources/TestifySDK/Codable/RawTestResultDecoder.swift
index e89d338..3d654b4 100644
--- a/Sources/TestifySDK/Codable/RawTestResultDecoder.swift
+++ b/Sources/TestifySDK/Codable/RawTestResultDecoder.swift
@@ -1,5 +1,5 @@
//
-// RawTestResultParser.swift
+// RawTestResultDecoder.swift
// Testify
//
// Created by Tibor Bodecs on 2023. 02. 12..
@@ -7,46 +7,10 @@
import Foundation
-private extension String {
-
- func match(_ pattern: String) -> String? {
- guard let regex = try? NSRegularExpression(pattern: pattern)
- else { return nil }
- let matches = regex.matches(
- in: self,
- range: .init(location: 0, length: count)
- )
- guard let match = matches.first,
- let range = Range(match.range, in: self)
- else { return nil }
- return String(self[range])
- }
-
- var matchedTestName: String? {
- guard let match = match("(\\'.+\\')") else {
- return nil
- }
- return String(match.dropFirst().dropLast())
- }
-
- var matchedDate: String? {
- match("(\\d{4}-\\d{2}-\\d{2}\\s\\d{2}\\:\\d{2}\\:\\d{2}\\.\\d{3})")
- }
-
- var matchedSeconds: String? {
- match("(\\d+\\.\\d+)")
- }
-
- var matchedUnexpected: String? {
- guard let dropFirst = match("\\((\\d+)")?.dropFirst() else { return nil }
- return String(dropFirst)
- }
-}
-
public struct RawTestResultDecoder {
-
+
private let dateFormatter: DateFormatter
-
+
public init() {
self.dateFormatter = .init()
self.dateFormatter.dateFormat = "yyyy-MM-dd HH:mm:ss.SSS"
@@ -57,47 +21,32 @@ public struct RawTestResultDecoder {
var currentCaseName: String?
var testCaseOutput: String = ""
var gatherTestCaseOutput = false
-
+
let lines = input.split(separator: "\n").map({ String($0) })
for (index, line) in lines.enumerated() {
// start or end test suite
if line.contains("Test Suite") {
- if line.contains("started") {
- guard let name = line.matchedTestName else { continue }
-
- guard let matchedDate = line.matchedDate,
- let date = dateFormatter.date(from: matchedDate)
- else { continue }
-
- suites.append(
- TestSuite(
- name: name,
- startDate: date,
- endDate: date,
- unexpected: 0,
- outcome: .failure
- )
- )
- continue;
- }
- else {
+ guard line.contains("started") else {
guard var suite = suites.last else { continue }
suites = Array(suites.dropLast())
-
- suite.outcome = line.contains("passed") ? .success : .failure
+
+ suite.outcome =
+ line.contains("passed") ? .success : .failure
if let matchedDate = line.matchedDate,
- let date = dateFormatter.date(from: matchedDate) {
+ let date = dateFormatter.date(from: matchedDate)
+ {
suite.endDate = date
}
- if index+1 < lines.count {
- let nextLine = lines[index+1]
+ if index + 1 < lines.count {
+ let nextLine = lines[index + 1]
if nextLine.contains("Executed"),
- let matchedUnexpected = nextLine.matchedUnexpected,
- let unexpected = UInt(matchedUnexpected) {
+ let matchedUnexpected = nextLine.matchedUnexpected,
+ let unexpected = UInt(matchedUnexpected)
+ {
suite.unexpected = unexpected
}
}
-
+
if suites.isEmpty {
suites.append(suite)
}
@@ -108,39 +57,64 @@ public struct RawTestResultDecoder {
suites.append(parentSuite)
}
}
- continue;
+ continue
}
+ guard let name = line.matchedTestName else { continue }
+
+ guard let matchedDate = line.matchedDate,
+ let date = dateFormatter.date(from: matchedDate)
+ else { continue }
+
+ suites.append(
+ TestSuite(
+ name: name,
+ startDate: date,
+ endDate: date,
+ unexpected: 0,
+ outcome: .failure
+ )
+ )
+ continue
}
if line.contains("Test Case") {
- if line.contains("started") {
- testCaseOutput = ""
- gatherTestCaseOutput = true
- currentCaseName = line.matchedTestName
- continue;
- }
- else {
+ guard line.contains("started") else {
gatherTestCaseOutput = false
guard var suite = suites.last else { continue }
suites = Array(suites.dropLast())
- let outcome: Outcome = line.contains("passed") || line.contains("measured") ? .success : .failure
-
+ let outcome: Outcome =
+ line.contains("passed") || line.contains("measured")
+ ? .success : .failure
+
var failureInfo: FailureInfo? = nil
if outcome == .failure, !testCaseOutput.isEmpty {
let outputSplit = testCaseOutput.split(separator: ":")
let file = String(outputSplit[0])
- if outputSplit.count >= 2, let line = Int(outputSplit[1]) {
- let reason = String(outputSplit.dropFirst(4)
- .joined(separator: ":")
- .trimmingCharacters(in: CharacterSet(charactersIn: "-").union(.whitespaces)))
- failureInfo = FailureInfo(file: file, line: line, reason: reason)
+ if outputSplit.count >= 2,
+ let line = Int(outputSplit[1])
+ {
+ let reason = String(
+ outputSplit.dropFirst(4)
+ .joined(separator: ":")
+ .trimmingCharacters(
+ in: CharacterSet(charactersIn: "-")
+ .union(.whitespaces)
+ )
+ )
+ failureInfo = FailureInfo(
+ file: file,
+ line: line,
+ reason: reason
+ )
}
}
-
- if let _currentCaseName = currentCaseName,
+
+ if let notNullcurrentCaseName = currentCaseName,
let matchedSeconds = line.matchedSeconds,
- let duration = TimeInterval(matchedSeconds) {
-
- let caseName = _currentCaseName.dropFirst(2).dropLast()
+ let duration = TimeInterval(matchedSeconds)
+ {
+
+ let caseName = notNullcurrentCaseName.dropFirst(2)
+ .dropLast()
let firstSplit = caseName.split(separator: ".")
let secondSplit = firstSplit[1].split(separator: " ")
let testCase = TestCase(
@@ -155,8 +129,12 @@ public struct RawTestResultDecoder {
}
suites.append(suite)
currentCaseName = nil
- continue;
+ continue
}
+ testCaseOutput = ""
+ gatherTestCaseOutput = true
+ currentCaseName = line.matchedTestName
+ continue
}
if gatherTestCaseOutput {
testCaseOutput += line
@@ -165,3 +143,41 @@ public struct RawTestResultDecoder {
return suites.first
}
}
+
+extension String {
+
+ fileprivate func match(_ pattern: String) -> String? {
+ guard let regex = try? NSRegularExpression(pattern: pattern)
+ else { return nil }
+ let matches = regex.matches(
+ in: self,
+ range: .init(location: 0, length: count)
+ )
+ guard let match = matches.first,
+ let range = Range(match.range, in: self)
+ else { return nil }
+ return String(self[range])
+ }
+
+ fileprivate var matchedTestName: String? {
+ guard let match = match("(\\'.+\\')") else {
+ return nil
+ }
+ return String(match.dropFirst().dropLast())
+ }
+
+ fileprivate var matchedDate: String? {
+ match("(\\d{4}-\\d{2}-\\d{2}\\s\\d{2}\\:\\d{2}\\:\\d{2}\\.\\d{3})")
+ }
+
+ fileprivate var matchedSeconds: String? {
+ match("(\\d+\\.\\d+)")
+ }
+
+ fileprivate var matchedUnexpected: String? {
+ guard let dropFirst = match("\\((\\d+)")?.dropFirst() else {
+ return nil
+ }
+ return String(dropFirst)
+ }
+}
diff --git a/Sources/TestifySDK/Codable/TestResultGitHubFlavoredMarkdownEncoder.swift b/Sources/TestifySDK/Codable/TestResultGitHubFlavoredMarkdownEncoder.swift
index 5558e7b..25140fc 100644
--- a/Sources/TestifySDK/Codable/TestResultGitHubFlavoredMarkdownEncoder.swift
+++ b/Sources/TestifySDK/Codable/TestResultGitHubFlavoredMarkdownEncoder.swift
@@ -1,11 +1,18 @@
+//
+// TestResultGitHubFlavoredMarkdownEncoder.swift
+// Testify
+//
+// Created by kanstantsin-bucha
+//
+
import Foundation
public struct TestResultGitHubFlavoredMarkdownEncoder: TestResultEncoder {
-
+
public init() {
-
+
}
-
+
public func encode(_ input: TestSuite) throws -> String {
var totalTestsCount = 0
var totalTime: Double = 0
@@ -19,39 +26,46 @@ public struct TestResultGitHubFlavoredMarkdownEncoder: TestResultEncoder {
let name = suite.name
let count = suite.cases.count
let time = suite.cases.reduce(0) { $0 + $1.duration }
- let successCount = suite.cases.reduce(0) { $0 + ($1.outcome == .success ? 1 : 0) }
- let failureCount = suite.cases.reduce(0) { $0 + ($1.outcome == .failure ? 1 : 0) }
-
+ let successCount = suite.cases.reduce(0) {
+ $0 + ($1.outcome == .success ? 1 : 0)
+ }
+ let failureCount = suite.cases.reduce(0) {
+ $0 + ($1.outcome == .failure ? 1 : 0)
+ }
+
totalTestsCount += count
totalTime += time
totalSucceedCount += successCount
totalFailedCount += totalFailedCount
-
+
result += "\n"
let suiteResult = count == successCount ? "✅" : "❌"
- result += " \(suiteResult) \(name): \(count) tests were completed in \(timeString(time)) with \(successCount) passed, \(failureCount) failed.
\n"
-
+ result +=
+ " \(suiteResult) \(name): \(count) tests were completed in \(timeString(time)) with \(successCount) passed, \(failureCount) failed.
\n"
+
for testCase in suite.cases {
let name = testCase.testName
let testResult = testCase.outcome == .success ? "✅" : "❌"
let time = timeString(testCase.duration)
result += "| \(testResult) \(time) | \(name)
\n"
}
-
+
result += "
\n"
result += " \n"
}
-
+
let testsRunResult = totalTestsCount == totalSucceedCount ? "✅" : "❌"
- var testsResult = "# \(testsRunResult) \(totalTestsCount) tests were completed in \(timeString(totalTime))"
+ var testsResult =
+ "# \(testsRunResult) \(totalTestsCount) tests were completed in \(timeString(totalTime))"
testsResult += "\n \n"
- testsResult += "\(totalSucceedCount) tests passed, \(totalFailedCount) test failed.\n"
+ testsResult +=
+ "\(totalSucceedCount) tests passed, \(totalFailedCount) test failed.\n"
testsResult += "\n----\n"
testsResult += result
-
+
return testsResult
}
-
+
private func timeString(_ sec: Double) -> String {
"\(String(format: "%.2f", sec))s"
}
diff --git a/Sources/TestifySDK/Codable/TestResultJSONEncoder.swift b/Sources/TestifySDK/Codable/TestResultJSONEncoder.swift
index f15f88a..36ea6bb 100644
--- a/Sources/TestifySDK/Codable/TestResultJSONEncoder.swift
+++ b/Sources/TestifySDK/Codable/TestResultJSONEncoder.swift
@@ -1,6 +1,6 @@
//
-// File.swift
-//
+// TestResultJSONEncoder.swift
+// Testify
//
// Created by Tibor Bodecs on 2023. 02. 12..
//
@@ -8,7 +8,7 @@
import Foundation
struct TestResultJSONEncoder: TestResultEncoder {
-
+
func encode(_ suite: TestSuite) throws -> String {
let encoder = JSONEncoder()
let data = try encoder.encode(suite)
diff --git a/Sources/TestifySDK/Codable/TestResultJunitEncoder.swift b/Sources/TestifySDK/Codable/TestResultJunitEncoder.swift
index 4362c73..7af6a27 100644
--- a/Sources/TestifySDK/Codable/TestResultJunitEncoder.swift
+++ b/Sources/TestifySDK/Codable/TestResultJunitEncoder.swift
@@ -1,6 +1,6 @@
//
-// File.swift
-//
+// TestResultJunitEncoder.swift
+// Testify
//
// Created by Lengyel Gábor on 2023. 02. 15..
//
@@ -8,29 +8,32 @@
import Foundation
public struct TestResultJunitEncoder: TestResultEncoder {
-
+
public init() {
-
+
}
-
+
public func encode(_ input: TestSuite) throws -> String {
var restOfResult = ""
var allTests = 0
var allTimes = 0.0
var allFails = 0
-
+
let suites: [TestSuite] = input.children.reduce([]) { $0 + $1.children }
for suite in suites {
let start = suite.startDate
let name = suite.name
let tests = suite.cases.count
let time = suite.cases.reduce(0) { $0 + $1.duration }
- let failureCount = suite.cases.reduce(0) { $0 + ($1.outcome == .failure ? 1 : 0) }
+ let failureCount = suite.cases.reduce(0) {
+ $0 + ($1.outcome == .failure ? 1 : 0)
+ }
allTests += tests
allTimes += time
allFails += failureCount
- restOfResult += "\n"
-
+ restOfResult +=
+ "\n"
+
for testCase in suite.cases {
let name = testCase.testName
let className = testCase.className
@@ -38,18 +41,22 @@ public struct TestResultJunitEncoder: TestResultEncoder {
let time = testCase.duration
let failLine = testCase.failureInfo?.line
let failReason = testCase.failureInfo?.reason
-
- restOfResult += "\n"
- } else {
- if (testCase.failureInfo != nil) {
+ }
+ else {
+ if testCase.failureInfo != nil {
restOfResult += ">\n"
- restOfResult += "\n"
+ restOfResult +=
+ "\n"
restOfResult += "\(failReason ?? "")\n"
restOfResult += "\n"
restOfResult += "\n"
- } else {
+ }
+ else {
restOfResult += "/>\n"
}
}
@@ -57,12 +64,13 @@ public struct TestResultJunitEncoder: TestResultEncoder {
restOfResult += "\n"
}
restOfResult += "\n"
-
+
let formatter = DateFormatter()
formatter.dateFormat = "yyyyMMdd_HHmmss"
var startResult = "\n\n"
- startResult += "\n"
-
+ startResult +=
+ "\n"
+
return startResult + restOfResult
}
}
diff --git a/Sources/TestifySDK/Codable/TestResultMarkdownEncoder.swift b/Sources/TestifySDK/Codable/TestResultMarkdownEncoder.swift
index 5713464..7db0d90 100644
--- a/Sources/TestifySDK/Codable/TestResultMarkdownEncoder.swift
+++ b/Sources/TestifySDK/Codable/TestResultMarkdownEncoder.swift
@@ -1,6 +1,6 @@
//
-// File.swift
-//
+// TestResultMarkdownEncoder.swift
+// Testify
//
// Created by Tibor Bodecs on 2023. 02. 12..
//
@@ -8,11 +8,11 @@
import Foundation
public struct TestResultMarkdownEncoder: TestResultEncoder {
-
+
public init() {
-
+
}
-
+
public func encode(_ input: TestSuite) throws -> String {
var result = ""
@@ -22,20 +22,25 @@ public struct TestResultMarkdownEncoder: TestResultEncoder {
let name = suite.name
let count = suite.cases.count
let time = suite.cases.reduce(0) { $0 + $1.duration }
- let successCount = suite.cases.reduce(0) { $0 + ($1.outcome == .success ? 1 : 0) }
- let failureCount = suite.cases.reduce(0) { $0 + ($1.outcome == .failure ? 1 : 0) }
-
- result += "\(name): \(count) tests were completed in \(time) with \(successCount) passed, \(failureCount) failed.\n\n"
+ let successCount = suite.cases.reduce(0) {
+ $0 + ($1.outcome == .success ? 1 : 0)
+ }
+ let failureCount = suite.cases.reduce(0) {
+ $0 + ($1.outcome == .failure ? 1 : 0)
+ }
+
+ result +=
+ "\(name): \(count) tests were completed in \(time) with \(successCount) passed, \(failureCount) failed.\n\n"
result += "| Test case | Result | Time |\n"
result += "| :--- | ---: | ---: |\n"
-
+
for testCase in suite.cases {
let name = testCase.testName
let testResult = testCase.outcome == .success ? "✅" : "❌"
let time = "\(testCase.duration)s ⌛️"
result += "| \(name) | \(testResult) | \(time) |\n"
}
-
+
}
return result
diff --git a/Sources/TestifySDK/Models/FailureInfo.swift b/Sources/TestifySDK/Models/FailureInfo.swift
index d0eab1d..3fc00b0 100644
--- a/Sources/TestifySDK/Models/FailureInfo.swift
+++ b/Sources/TestifySDK/Models/FailureInfo.swift
@@ -11,7 +11,7 @@ public struct FailureInfo: Codable {
public let file: String
public let line: Int
public let reason: String
-
+
public init(
file: String,
line: Int,
diff --git a/Sources/TestifySDK/Models/OutputFormat.swift b/Sources/TestifySDK/Models/OutputFormat.swift
index 45dc358..f597cc5 100644
--- a/Sources/TestifySDK/Models/OutputFormat.swift
+++ b/Sources/TestifySDK/Models/OutputFormat.swift
@@ -1,13 +1,13 @@
//
-// File.swift
-//
+// OutputFormat.swift
+// Testify
//
// Created by Lengyel Gábor on 2023. 02. 17..
//
import Foundation
-public enum OutputFormat : String, CaseIterable, Sendable {
+public enum OutputFormat: String, CaseIterable, Sendable {
case json
case junit
case md
diff --git a/Sources/TestifySDK/Models/TestCase.swift b/Sources/TestifySDK/Models/TestCase.swift
index 234877a..8cd04a8 100644
--- a/Sources/TestifySDK/Models/TestCase.swift
+++ b/Sources/TestifySDK/Models/TestCase.swift
@@ -14,7 +14,7 @@ public struct TestCase: Codable {
public let duration: TimeInterval
public let outcome: Outcome
public let failureInfo: FailureInfo?
-
+
public init(
moduleName: String,
className: String,
@@ -30,5 +30,5 @@ public struct TestCase: Codable {
self.outcome = outcome
self.failureInfo = failureInfo
}
-
+
}
diff --git a/Sources/TestifySDK/Models/TestSuite.swift b/Sources/TestifySDK/Models/TestSuite.swift
index c48cfb5..23097e3 100644
--- a/Sources/TestifySDK/Models/TestSuite.swift
+++ b/Sources/TestifySDK/Models/TestSuite.swift
@@ -7,10 +7,10 @@
import Foundation
-public extension TestSuite {
- static func parse(_ input: String) -> TestSuite? {
+extension TestSuite {
+ public static func parse(_ input: String) -> TestSuite? {
let decoder = RawTestResultDecoder()
- let suite = try! decoder.decode(input)
+ let suite = try? decoder.decode(input)
return suite
}
}
@@ -23,7 +23,7 @@ public struct TestSuite: Codable {
public var outcome: Outcome
public var cases: [TestCase]
public var children: [TestSuite]
-
+
public init(
name: String,
startDate: Date,
diff --git a/Sources/testify/main.swift b/Sources/testify/main.swift
index bf80a66..bfcf5a9 100644
--- a/Sources/testify/main.swift
+++ b/Sources/testify/main.swift
@@ -10,14 +10,17 @@ import TestifySDK
let args = CommandLine.arguments
var outputFormat: OutputFormat = .json
-if (args.count >= 2) {
+if args.count >= 2 {
if let format = OutputFormat(rawValue: args[1]) {
outputFormat = format
- } else {
+ }
+ else {
let formats = OutputFormat.allCases
- .map { "'\($0)'"}
+ .map { "'\($0)'" }
.joined(separator: ", ")
- fatalError("Error: Unknown output format. Available formats: \(formats)")
+ fatalError(
+ "Error: Unknown output format. Available formats: \(formats)"
+ )
}
}
@@ -26,7 +29,7 @@ var input: String = ""
repeat {
data = FileHandle.standardInput.availableData
input += String(decoding: data, as: UTF8.self)
-} while (data.count > 0)
+} while data.count > 0
let decoder = RawTestResultDecoder()
guard let suite = try decoder.decode(input) else {
@@ -40,19 +43,19 @@ case .json:
let jsonData = try encoder.encode(suite)
print("\n", String(data: jsonData, encoding: .utf8)!, "\n")
print("\n", String(decoding: data, as: UTF8.self), "\n")
-
+
case .junit:
let encoder = TestResultJunitEncoder()
- let junitData = try! encoder.encode(suite)
+ let junitData = try encoder.encode(suite)
print(junitData)
-
+
case .md:
let encoder = TestResultMarkdownEncoder()
- let mdData = try! encoder.encode(suite)
+ let mdData = try encoder.encode(suite)
print(mdData)
-
+
case .gfm:
let encoder = TestResultGitHubFlavoredMarkdownEncoder()
- let mdData = try! encoder.encode(suite)
+ let mdData = try encoder.encode(suite)
print(mdData)
}
diff --git a/Tests/TestifySDKTests/Bundle+Extensions.swift b/Tests/TestifySDKTests/Bundle+Extensions.swift
index 5837b78..b4b1a3a 100644
--- a/Tests/TestifySDKTests/Bundle+Extensions.swift
+++ b/Tests/TestifySDKTests/Bundle+Extensions.swift
@@ -8,17 +8,23 @@
import Foundation
extension Bundle {
-
+
enum BundleCustomError: Error {
case fileNotFound(file: String, extension: String)
}
-
- func getURL(for file: String, withExtension extension: String) throws -> URL {
- guard let url = Bundle.module.url(
- forResource: file,
- withExtension: `extension`
- ) else {
- throw BundleCustomError.fileNotFound(file: file, extension: `extension`)
+
+ func getURL(for file: String, withExtension extension: String) throws -> URL
+ {
+ guard
+ let url = Bundle.module.url(
+ forResource: file,
+ withExtension: `extension`
+ )
+ else {
+ throw BundleCustomError.fileNotFound(
+ file: file,
+ extension: `extension`
+ )
}
return url
}
diff --git a/Tests/TestifySDKTests/TestifyJunitTests.swift b/Tests/TestifySDKTests/TestifyJunitTests.swift
index fa4ff2a..5d85fa1 100644
--- a/Tests/TestifySDKTests/TestifyJunitTests.swift
+++ b/Tests/TestifySDKTests/TestifyJunitTests.swift
@@ -1,9 +1,14 @@
-import XCTest
import Foundation
+import XCTest
+
@testable import TestifySDK
+#if canImport(FoundationXML)
+import FoundationXML
+#endif
+
final class TestifyJunitTests: XCTestCase {
-
+
func testTests() throws {
let testFiles = [
"PromiseUnexpectedFailure",
@@ -17,32 +22,43 @@ final class TestifyJunitTests: XCTestCase {
]
let decoder = RawTestResultDecoder()
-
+
for file in testFiles {
-
- let testUrl = try Bundle.module.getURL(for: file, withExtension: "tests")
- let xmlUrl = try Bundle.module.getURL(for: file, withExtension: "xml")
+
+ let testUrl = try Bundle.module.getURL(
+ for: file,
+ withExtension: "tests"
+ )
+ let xmlUrl = try Bundle.module.getURL(
+ for: file,
+ withExtension: "xml"
+ )
let testData = try Data(contentsOf: testUrl)
- guard let testOutput = String(data: testData, encoding: .utf8) else {
+ guard let testOutput = String(data: testData, encoding: .utf8)
+ else {
return XCTFail("Could not decode test data.")
}
-
+
let suite = try decoder.decode(testOutput)
let xmlData = try Data(contentsOf: xmlUrl)
let xmlParser = XMLParser(data: xmlData)
let parser = SuiteXmlParser()
xmlParser.delegate = parser
xmlParser.parse()
-
+
if let suite {
- XCTAssertTrue(self.checkNumbers(suite, parser), "Invalid numbers count for \(file).")
- } else {
+ XCTAssertTrue(
+ self.checkNumbers(suite, parser),
+ "Invalid numbers count for \(file)."
+ )
+ }
+ else {
XCTFail("Could not parse XML for \(file).")
}
}
}
-
+
//@TODO: is there a better way to check this ?
func checkNumbers(_ suite: TestSuite, _ parser: SuiteXmlParser) -> Bool {
let suites: [TestSuite] = suite.children.reduce([]) { $0 + $1.children }
@@ -50,35 +66,38 @@ final class TestifyJunitTests: XCTestCase {
var allFails = 0
for suite in suites {
let tests = suite.cases.count
- let failureCount = suite.cases.reduce(0) { $0 + ($1.outcome == .failure ? 1 : 0) }
+ let failureCount = suite.cases.reduce(0) {
+ $0 + ($1.outcome == .failure ? 1 : 0)
+ }
allTests += tests
allFails += failureCount
}
- return suites.count == parser.suites &&
- allTests == parser.tests &&
- allFails == parser.fails
+ return suites.count == parser.suites && allTests == parser.tests
+ && allFails == parser.fails
}
-
- class SuiteXmlParser : NSObject, XMLParserDelegate {
+
+ class SuiteXmlParser: NSObject, XMLParserDelegate {
var suites = 0
var tests = 0
var fails = 0
-
+
func parser(
_ parser: XMLParser,
didStartElement elementName: String,
namespaceURI: String?,
qualifiedName qName: String?,
- attributes attributeDict: [String : String] = [:]
+ attributes attributeDict: [String: String] = [:]
) {
- if (elementName=="testsuite") {
+ if elementName == "testsuite" {
suites += 1
- } else if (elementName=="testcase") {
+ }
+ else if elementName == "testcase" {
tests += 1
- } else if (elementName=="failure") {
+ }
+ else if elementName == "failure" {
fails += 1
}
}
}
-
+
}
diff --git a/Tests/TestifySDKTests/TestifyMarkdownTests.swift b/Tests/TestifySDKTests/TestifyMarkdownTests.swift
index 048f1d7..848cc20 100644
--- a/Tests/TestifySDKTests/TestifyMarkdownTests.swift
+++ b/Tests/TestifySDKTests/TestifyMarkdownTests.swift
@@ -1,4 +1,5 @@
import XCTest
+
@testable import TestifySDK
final class TestifyMarkdownTests: XCTestCase {
@@ -17,64 +18,81 @@ final class TestifyMarkdownTests: XCTestCase {
]
let decoder = RawTestResultDecoder()
-
+
for file in testFiles {
- let testUrl = try Bundle.module.getURL(for: file, withExtension: "tests")
+ let testUrl = try Bundle.module.getURL(
+ for: file,
+ withExtension: "tests"
+ )
let mdUrl = try Bundle.module.getURL(for: file, withExtension: "md")
let testData = try Data(contentsOf: testUrl)
- guard let testOutput = String(data: testData, encoding: .utf8) else {
+ guard let testOutput = String(data: testData, encoding: .utf8)
+ else {
return XCTFail("Could not decode test data.")
}
let suite = try decoder.decode(testOutput)
-
+
let resultData = try Data(contentsOf: mdUrl)
- guard let testMdOutput = String(data: resultData, encoding: .utf8) else {
+ guard let testMdOutput = String(data: resultData, encoding: .utf8)
+ else {
return XCTFail("Could not decode test data.")
}
-
- let filtered = testMdOutput.split(separator: "\n").map({ String($0) }).filter {
- $0.contains("tests were completed") &&
- $0.contains("passed") &&
- $0.contains("failed")
- }
-
+
+ let filtered = testMdOutput.split(separator: "\n")
+ .map({ String($0) })
+ .filter {
+ $0.contains("tests were completed") && $0.contains("passed")
+ && $0.contains("failed")
+ }
+
if let suite {
- XCTAssertTrue(self.checkNumbers(suite, filtered), "Invalid numbers count for \(file).")
- } else {
+ XCTAssertTrue(
+ self.checkNumbers(suite, filtered),
+ "Invalid numbers count for \(file)."
+ )
+ }
+ else {
XCTFail("Not found suite for \(file).")
}
-
+
}
}
-
+
//@TODO: is there a better way to check this ?
func checkNumbers(_ suite: TestSuite, _ filtered: [String]) -> Bool {
var mdAllTests = 0
var mdAllFails = 0
for item in filtered {
-
- let tests = Int(item.components(separatedBy: " tests were completed in ").filter { $0.contains(": ") }[0]
- .components(separatedBy: ": ")[1])
- let fails = Int(item.components(separatedBy: " passed, ").filter { $0.contains("failed.") }[0]
- .components(separatedBy: " failed.")[0])
+
+ let tests = Int(
+ item.components(separatedBy: " tests were completed in ")
+ .filter { $0.contains(": ") }[0]
+ .components(separatedBy: ": ")[1]
+ )
+ let fails = Int(
+ item.components(separatedBy: " passed, ")
+ .filter { $0.contains("failed.") }[0]
+ .components(separatedBy: " failed.")[0]
+ )
mdAllTests += tests ?? 0
mdAllFails += fails ?? 0
}
-
+
let suites: [TestSuite] = suite.children.reduce([]) { $0 + $1.children }
var allTests = 0
var allFails = 0
for suite in suites {
let tests = suite.cases.count
- let failureCount = suite.cases.reduce(0) { $0 + ($1.outcome == .failure ? 1 : 0) }
+ let failureCount = suite.cases.reduce(0) {
+ $0 + ($1.outcome == .failure ? 1 : 0)
+ }
allTests += tests
allFails += failureCount
}
-
- return suites.count == filtered.count &&
- allTests == mdAllTests &&
- allFails == mdAllFails
+
+ return suites.count == filtered.count && allTests == mdAllTests
+ && allFails == mdAllFails
}
-
+
}
diff --git a/Tests/TestifySDKTests/TestifySDKTests.swift b/Tests/TestifySDKTests/TestifySDKTests.swift
index d892077..3032f02 100644
--- a/Tests/TestifySDKTests/TestifySDKTests.swift
+++ b/Tests/TestifySDKTests/TestifySDKTests.swift
@@ -1,4 +1,5 @@
import XCTest
+
@testable import TestifySDK
final class TestifyTests: XCTestCase {
@@ -17,36 +18,51 @@ final class TestifyTests: XCTestCase {
]
let decoder = RawTestResultDecoder()
-
+
for file in testFiles {
- let testUrl = try Bundle.module.getURL(for: file, withExtension: "tests")
- let jsonUrl = try Bundle.module.getURL(for: file, withExtension: "json")
+ let testUrl = try Bundle.module.getURL(
+ for: file,
+ withExtension: "tests"
+ )
+ let jsonUrl = try Bundle.module.getURL(
+ for: file,
+ withExtension: "json"
+ )
let testData = try Data(contentsOf: testUrl)
- guard let testOutput = String(data: testData, encoding: .utf8) else {
+ guard let testOutput = String(data: testData, encoding: .utf8)
+ else {
return XCTFail("Could not decode test data.")
}
let resultData = try Data(contentsOf: jsonUrl)
let suite = try decoder.decode(testOutput)
-
+
let decoder = JSONDecoder()
decoder.dateDecodingStrategy = .secondsSince1970
- let expectation = try decoder.decode(TestSuite.self, from: resultData)
+ let expectation = try decoder.decode(
+ TestSuite.self,
+ from: resultData
+ )
if let suite {
- XCTAssertTrue(self.checkIsEqual(suite, expectation), "Invalid case count for \(file).")
- } else {
+ XCTAssertTrue(
+ self.checkIsEqual(suite, expectation),
+ "Invalid case count for \(file)."
+ )
+ }
+ else {
XCTFail("no test suite found")
}
}
}
//@TODO: proper equation checking, for now I'm ok with this... ¯\_(ツ)_/¯
- func checkIsEqual(_ testSuite1: TestSuite, _ testSuite2: TestSuite) -> Bool {
- return testSuite1.name == testSuite2.name &&
- testSuite1.outcome == testSuite2.outcome &&
- testSuite1.cases.count == testSuite2.cases.count &&
- testSuite1.children.count == testSuite2.children.count
+ func checkIsEqual(_ testSuite1: TestSuite, _ testSuite2: TestSuite) -> Bool
+ {
+ testSuite1.name == testSuite2.name
+ && testSuite1.outcome == testSuite2.outcome
+ && testSuite1.cases.count == testSuite2.cases.count
+ && testSuite1.children.count == testSuite2.children.count
}
-
+
}