Skip to content

Commit

Permalink
Merge pull request #8
Browse files Browse the repository at this point in the history
- Swift 6 update
- remove force unwraps
- fix out-of-memory issues
- update unit tests
  • Loading branch information
tib authored Nov 5, 2024
2 parents d6de543 + b1abbac commit ced1af6
Show file tree
Hide file tree
Showing 47 changed files with 369 additions and 93 deletions.
5 changes: 4 additions & 1 deletion Package.swift
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// swift-tools-version:5.7
// swift-tools-version:6.0
import PackageDescription

let package = Package(
Expand Down Expand Up @@ -31,6 +31,9 @@ let package = Package(
name: "TestifySDKTests",
dependencies: [
.target(name: "TestifySDK"),
],
resources: [
.process("Resources"),
]
),
]
Expand Down
40 changes: 40 additions & 0 deletions [email protected]
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
// swift-tools-version:5.9
import PackageDescription

let package = Package(
name: "testify",
platforms: [
.macOS(.v10_15),
],
products: [
.executable(name: "testify", targets: ["testify"]),
.library(name: "TestifySDK", targets: ["TestifySDK"])
],
targets: [
.executableTarget(
name: "testify",
dependencies: [
.target(name: "TestifySDK")
]
),

// MARK: - targets

.target(
name: "TestifySDK",
dependencies: []
),

// MARK: - test targets

.testTarget(
name: "TestifySDKTests",
dependencies: [
.target(name: "TestifySDK"),
],
resources: [
.process("Resources"),
]
),
]
)
97 changes: 55 additions & 42 deletions Sources/TestifySDK/Codable/RawTestResultDecoder.swift
Original file line number Diff line number Diff line change
Expand Up @@ -10,17 +10,15 @@ import Foundation
private extension String {

func match(_ pattern: String) -> String? {
let regex = try! NSRegularExpression(pattern: pattern)
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
}
guard let match = matches.first,
let range = Range(match.range, in: self)
else { return nil }
return String(self[range])
}

Expand All @@ -40,7 +38,8 @@ private extension String {
}

var matchedUnexpected: String? {
String(match("\\((\\d+)")!.dropFirst())
guard let dropFirst = match("\\((\\d+)")?.dropFirst() else { return nil }
return String(dropFirst)
}
}

Expand All @@ -53,19 +52,22 @@ public struct RawTestResultDecoder {
self.dateFormatter.dateFormat = "yyyy-MM-dd HH:mm:ss.SSS"
}

public func decode(_ input: String) throws -> TestSuite {
public func decode(_ input: String) throws -> TestSuite? {
var suites: [TestSuite] = []
var currentCaseName: String?
var testCaseOutput: 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") {
let name = line.matchedTestName!
let date = dateFormatter.date(from: line.matchedDate!)!
guard let name = line.matchedTestName else { continue }

guard let matchedDate = line.matchedDate,
let date = dateFormatter.date(from: matchedDate)
else { continue }

suites.append(
TestSuite(
Expand All @@ -79,27 +81,32 @@ public struct RawTestResultDecoder {
continue;
}
else {
var suite = suites.last!
guard var suite = suites.last else { continue }
suites = Array(suites.dropLast())

suite.outcome = line.contains("passed") ? .success : .failure
suite.endDate = dateFormatter.date(from: line.matchedDate!)!

if let matchedDate = line.matchedDate,
let date = dateFormatter.date(from: matchedDate) {
suite.endDate = date
}
if index+1 < lines.count {
let nextLine = lines[index+1]
if nextLine.contains("Executed") {
suite.unexpected = UInt(nextLine.matchedUnexpected!)!
if nextLine.contains("Executed"),
let matchedUnexpected = nextLine.matchedUnexpected,
let unexpected = UInt(matchedUnexpected) {
suite.unexpected = unexpected
}
}

if suites.isEmpty {
suites.append(suite)
}
else {
var parentSuite = suites.last!
suites = Array(suites.dropLast())
parentSuite.children.append(suite)
suites.append(parentSuite)
if var parentSuite = suites.last {
suites = Array(suites.dropLast())
parentSuite.children.append(suite)
suites.append(parentSuite)
}
}
continue;
}
Expand All @@ -113,33 +120,39 @@ public struct RawTestResultDecoder {
}
else {
gatherTestCaseOutput = false
var suite = suites.last!
guard var suite = suites.last else { continue }
suites = Array(suites.dropLast())
let outcome: Outcome = line.contains("passed") ? .success : .failure
let caseName = currentCaseName!.dropFirst(2).dropLast()
let firstSplit = caseName.split(separator: ".")
let secondSplit = firstSplit[1].split(separator: " ")
let outcome: Outcome = line.contains("passed") || line.contains("measured") ? .success : .failure

var failureInfo: FailureInfo? = nil
if outcome == .failure {
if outcome == .failure, !testCaseOutput.isEmpty {
let outputSplit = testCaseOutput.split(separator: ":")
let file = String(outputSplit[0])
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)
}
}

let testCase = TestCase(
moduleName: String(firstSplit[0]),
className: String(secondSplit[0]),
testName: String(secondSplit[1]),
duration: TimeInterval(line.matchedSeconds!)!,
outcome: outcome,
failureInfo: failureInfo
)
suite.cases.append(testCase)
if let _currentCaseName = currentCaseName,
let matchedSeconds = line.matchedSeconds,
let duration = TimeInterval(matchedSeconds) {

let caseName = _currentCaseName.dropFirst(2).dropLast()
let firstSplit = caseName.split(separator: ".")
let secondSplit = firstSplit[1].split(separator: " ")
let testCase = TestCase(
moduleName: String(firstSplit[0]),
className: String(secondSplit[0]),
testName: String(secondSplit[1]),
duration: duration,
outcome: outcome,
failureInfo: failureInfo
)
suite.cases.append(testCase)
}
suites.append(suite)
currentCaseName = nil
continue;
Expand All @@ -149,6 +162,6 @@ public struct RawTestResultDecoder {
testCaseOutput += line
}
}
return suites.first!
return suites.first
}
}
8 changes: 4 additions & 4 deletions Sources/TestifySDK/Codable/TestResultCodable.swift
Original file line number Diff line number Diff line change
Expand Up @@ -7,18 +7,18 @@

import Foundation

public enum TestResultEncoderError: Error {
public enum TestResultEncoderError: Error, Sendable {
case unknown
}

public enum TestResultDecoderError: Error {
public enum TestResultDecoderError: Error, Sendable {
case unknown
}

public protocol TestResultEncoder {
public protocol TestResultEncoder: Sendable {
func encode(_: TestSuite) throws -> String
}

public protocol TestResultDecoder {
public protocol TestResultDecoder: Sendable {
func decode(_: String) throws -> TestSuite
}
2 changes: 1 addition & 1 deletion Sources/TestifySDK/Models/Outcome.swift
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@

import Foundation

public enum Outcome: String, Codable {
public enum Outcome: String, Codable, Sendable {
case success
case failure
}
2 changes: 1 addition & 1 deletion Sources/TestifySDK/Models/OutputFormat.swift
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@

import Foundation

public enum OutputFormat : String, CaseIterable {
public enum OutputFormat : String, CaseIterable, Sendable {
case json
case junit
case md
Expand Down
2 changes: 0 additions & 2 deletions Sources/TestifySDK/Models/TestSuite.swift
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,11 @@
import Foundation

public extension TestSuite {

static func parse(_ input: String) -> TestSuite? {
let decoder = RawTestResultDecoder()
let suite = try! decoder.decode(input)
return suite
}

}

public struct TestSuite: Codable {
Expand Down
9 changes: 6 additions & 3 deletions Sources/testify/main.swift
Original file line number Diff line number Diff line change
Expand Up @@ -25,18 +25,21 @@ var data: Data
var input: String = ""
repeat {
data = FileHandle.standardInput.availableData
input += String(data: data, encoding: .utf8)!
input += String(decoding: data, as: UTF8.self)
} while (data.count > 0)

let decoder = RawTestResultDecoder()
let suite = try decoder.decode(input)
guard let suite = try decoder.decode(input) else {
fatalError("Error: Invalid test result data.")
}

switch outputFormat {
case .json:
let encoder = JSONEncoder()
encoder.outputFormatting = .prettyPrinted
let jsonData = try! encoder.encode(suite)
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()
Expand Down
25 changes: 25 additions & 0 deletions Tests/TestifySDKTests/Bundle+Extensions.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
//
// Bundle+Extensions.swift
// testify
//
// Created by Amine BENSALAH on 09/10/2024.
//

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`)
}
return url
}
}
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
Loading

0 comments on commit ced1af6

Please sign in to comment.