Skip to content
32 changes: 27 additions & 5 deletions Sources/XCLogParser/logmanifest/LogManifest.swift
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@

/// 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() {}

Expand All @@ -32,6 +32,7 @@
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 " +
Expand All @@ -53,17 +54,38 @@
}
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

Check failure on line 59 in Sources/XCLogParser/logmanifest/LogManifest.swift

View workflow job for this annotation

GitHub Actions / SwiftLint

Trailing Whitespace Violation: Lines should not have trailing whitespace. (trailing_whitespace)
// 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,
fileName: fileName,
timestampStart: timestampStart,
timestampEnd: timestampEnd,
duration: timestampEnd - timestampStart,
type: type)
type: type,
statistics: statistics
)
}.sorted(by: { lhs, rhs -> Bool in
return lhs.timestampStart > rhs.timestampStart
})
Expand Down
47 changes: 40 additions & 7 deletions Sources/XCLogParser/logmanifest/LogManifestModel.swift
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@

import Foundation

public enum LogManifestEntryType: String, Encodable {
public enum LogManifestEntryType: String, Encodable, Sendable {
case xcode
case xcodebuild

Expand All @@ -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
Expand All @@ -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
}
}
30 changes: 23 additions & 7 deletions Tests/XCLogParserTests/LogManifestTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,14 @@ class LogManifestTests: XCTestCase {
<dict>
<key>highLevelStatus</key>
<string>W</string>
<key>totalNumberOfAnalyzerIssues</key>
<integer>0</integer>
<key>totalNumberOfErrors</key>
<integer>0</integer>
<key>totalNumberOfTestFailures</key>
<integer>0</integer>
<key>totalNumberOfWarnings</key>
<integer>2</integer>
</dict>
<key>schemeIdentifier-containerName</key>
<string>MyApp</string>
Expand Down Expand Up @@ -95,7 +103,13 @@ class LogManifestTests: XCTestCase {
"documentTypeString": "&lt;nil&gt;",
"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,
Expand All @@ -109,7 +123,13 @@ class LogManifestTests: XCTestCase {
"documentTypeString": "&lt;nil&gt;",
"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,
Expand All @@ -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)
}

Expand Down
Loading