diff --git a/Sources/XCLogParser/logmanifest/LogManifest.swift b/Sources/XCLogParser/logmanifest/LogManifest.swift
index eafff2e..fa4e7fb 100644
--- a/Sources/XCLogParser/logmanifest/LogManifest.swift
+++ b/Sources/XCLogParser/logmanifest/LogManifest.swift
@@ -21,7 +21,7 @@ import Foundation
/// Parses a LogManifest.plist file.
/// That file has a list of the existing Xcode Logs inside a Derived Data's project directory
-public struct LogManifest {
+public struct LogManifest: Sendable {
public init() {}
@@ -32,6 +32,7 @@ public struct LogManifest {
return try parse(dictionary: logManifestDictionary, atPath: logManifestURL.path)
}
+ // swiftlint:disable function_body_length
public func parse(dictionary: NSDictionary, atPath path: String) throws -> [LogManifestEntry] {
guard let logs = dictionary["logs"] as? [String: [String: Any]] else {
throw LogError.invalidLogManifest("The file at \(path) is not a valid " +
@@ -53,9 +54,28 @@ public struct LogManifest {
}
let startDate = Date(timeIntervalSinceReferenceDate: timeStartedRecording)
let endDate = Date(timeIntervalSinceReferenceDate: timeStoppedRecording)
- let timestampStart = Int(startDate.timeIntervalSince1970.rounded())
- let timestampEnd = Int(endDate.timeIntervalSince1970.rounded())
-
+ let timestampStart = startDate.timeIntervalSince1970
+ let timestampEnd = endDate.timeIntervalSince1970
+
+ // Optionally extract statistics if available
+ let statistics: LogManifestEntryStatistics? = {
+ guard let primaryObservable = entry.value["primaryObservable"] as? [String: Any],
+ let totalNumberOfAnalyzerIssues = primaryObservable["totalNumberOfAnalyzerIssues"] as? Int,
+ let totalNumberOfErrors = primaryObservable["totalNumberOfErrors"] as? Int,
+ let totalNumberOfWarnings = primaryObservable["totalNumberOfWarnings"] as? Int,
+ let totalNumberOfTestFailures = primaryObservable["totalNumberOfTestFailures"] as? Int,
+ let highLevelStatus = primaryObservable["highLevelStatus"] as? String
+ else {
+ return nil
+ }
+ return LogManifestEntryStatistics(
+ totalNumberOfErrors: totalNumberOfErrors,
+ totalNumberOfAnalyzerIssues: totalNumberOfAnalyzerIssues,
+ highLevelStatus: highLevelStatus,
+ totalNumberOfTestFailures: totalNumberOfTestFailures,
+ totalNumberOfWarnings: totalNumberOfWarnings
+ )
+ }()
return LogManifestEntry(uniqueIdentifier: uniqueIdentifier,
title: title,
scheme: scheme,
@@ -63,7 +83,9 @@ public struct LogManifest {
timestampStart: timestampStart,
timestampEnd: timestampEnd,
duration: timestampEnd - timestampStart,
- type: type)
+ type: type,
+ statistics: statistics
+ )
}.sorted(by: { lhs, rhs -> Bool in
return lhs.timestampStart > rhs.timestampStart
})
diff --git a/Sources/XCLogParser/logmanifest/LogManifestModel.swift b/Sources/XCLogParser/logmanifest/LogManifestModel.swift
index 30194cf..c24f78d 100644
--- a/Sources/XCLogParser/logmanifest/LogManifestModel.swift
+++ b/Sources/XCLogParser/logmanifest/LogManifestModel.swift
@@ -19,7 +19,7 @@
import Foundation
-public enum LogManifestEntryType: String, Encodable {
+public enum LogManifestEntryType: String, Encodable, Sendable {
case xcode
case xcodebuild
@@ -37,18 +37,28 @@ public enum LogManifestEntryType: String, Encodable {
}
}
-public struct LogManifestEntry: Encodable {
+public struct LogManifestEntry: Encodable, Sendable {
public let uniqueIdentifier: String
public let title: String
public let scheme: String
public let fileName: String
- public let timestampStart: Int
- public let timestampEnd: Int
- public let duration: Int
+ public let timestampStart: TimeInterval
+ public let timestampEnd: TimeInterval
+ public let duration: Double
public let type: LogManifestEntryType
+ public let statistics: LogManifestEntryStatistics?
- public init(uniqueIdentifier: String, title: String, scheme: String, fileName: String,
- timestampStart: Int, timestampEnd: Int, duration: Int, type: LogManifestEntryType) {
+ public init(
+ uniqueIdentifier: String,
+ title: String,
+ scheme: String,
+ fileName: String,
+ timestampStart: TimeInterval,
+ timestampEnd: TimeInterval,
+ duration: Double,
+ type: LogManifestEntryType,
+ statistics: LogManifestEntryStatistics?
+ ) {
self.uniqueIdentifier = uniqueIdentifier
self.title = title
self.scheme = scheme
@@ -57,6 +67,29 @@ public struct LogManifestEntry: Encodable {
self.timestampEnd = timestampEnd
self.duration = duration
self.type = type
+ self.statistics = statistics
}
}
+
+public struct LogManifestEntryStatistics: Encodable, Sendable {
+ public let totalNumberOfErrors: Int
+ public let totalNumberOfAnalyzerIssues: Int
+ public let highLevelStatus: String
+ public let totalNumberOfTestFailures: Int
+ public let totalNumberOfWarnings: Int
+
+ public init(
+ totalNumberOfErrors: Int,
+ totalNumberOfAnalyzerIssues: Int,
+ highLevelStatus: String,
+ totalNumberOfTestFailures: Int,
+ totalNumberOfWarnings: Int
+ ) {
+ self.totalNumberOfErrors = totalNumberOfErrors
+ self.totalNumberOfAnalyzerIssues = totalNumberOfAnalyzerIssues
+ self.highLevelStatus = highLevelStatus
+ self.totalNumberOfTestFailures = totalNumberOfTestFailures
+ self.totalNumberOfWarnings = totalNumberOfWarnings
+ }
+}
diff --git a/Tests/XCLogParserTests/LogManifestTests.swift b/Tests/XCLogParserTests/LogManifestTests.swift
index bbccf04..710a55e 100644
--- a/Tests/XCLogParserTests/LogManifestTests.swift
+++ b/Tests/XCLogParserTests/LogManifestTests.swift
@@ -45,6 +45,14 @@ class LogManifestTests: XCTestCase {
highLevelStatus
W
+totalNumberOfAnalyzerIssues
+0
+totalNumberOfErrors
+0
+totalNumberOfTestFailures
+0
+totalNumberOfWarnings
+2
schemeIdentifier-containerName
MyApp
@@ -95,7 +103,13 @@ class LogManifestTests: XCTestCase {
"documentTypeString": "<nil>",
"domainType": "Xcode.IDEActivityLogDomainType.BuildLog",
"fileName": "599BC5A8-5E6A-4C16-A71E-A8D6301BAC07.xcactivitylog",
- "highLevelStatus": "E",
+ "primaryObservable": [
+ "highLevelStatus": "E",
+ "totalNumberOfErrors": 1,
+ "totalNumberOfAnalyzerIssues": 0,
+ "totalNumberOfTestFailures": 0,
+ "totalNumberOfWarnings": 2
+ ],
"schemeIdentifier-containerName": "MyApp project",
"schemeIdentifier-schemeName": "MyApp",
"schemeIdentifier-sharedScheme": 1,
@@ -109,7 +123,13 @@ class LogManifestTests: XCTestCase {
"documentTypeString": "<nil>",
"domainType": "Xcode.IDEActivityLogDomainType.BuildLog",
"fileName": "D1FEAFFA-2E88-4221-9CD2-AB607529381D.xcactivitylog",
- "highLevelStatus": "E",
+ "primaryObservable": [
+ "highLevelStatus": "E",
+ "totalNumberOfErrors": 1,
+ "totalNumberOfAnalyzerIssues": 0,
+ "totalNumberOfTestFailures": 0,
+ "totalNumberOfWarnings": 2
+ ],
"schemeIdentifier-containerName": "MyApp project",
"schemeIdentifier-schemeName": "MyApp",
"schemeIdentifier-sharedScheme": 1,
@@ -130,11 +150,7 @@ class LogManifestTests: XCTestCase {
let startDate = Date(timeIntervalSinceReferenceDate: firstStartedRecording)
let endDate = Date(timeIntervalSinceReferenceDate: firstStoppedRecording)
- let calendar = Calendar.current
- guard let expectedDuration = calendar.dateComponents([.second], from: startDate, to: endDate).second else {
- XCTFail("Error creating an expected duration field")
- return
- }
+ let expectedDuration = endDate.timeIntervalSince1970 - startDate.timeIntervalSince1970
XCTAssertEqual(expectedDuration, latestLog.duration)
}