Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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