Skip to content
Open
Show file tree
Hide file tree
Changes from 12 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
27 changes: 21 additions & 6 deletions Sources/XCLogParser/logmanifest/LogManifest.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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() {}

Expand All @@ -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 " +
Expand All @@ -46,24 +47,38 @@ public struct LogManifest {
let timeStartedRecording = entry.value["timeStartedRecording"] as? Double,
let timeStoppedRecording = entry.value["timeStoppedRecording"] as? Double,
let className = entry.value["className"] as? String,
let type = LogManifestEntryType.buildFromClassName(className)
let type = LogManifestEntryType.buildFromClassName(className),
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 {
throw LogError.invalidLogManifest("The file at \(path) is not a valid " +
Comment thread
AvdLee marked this conversation as resolved.
"LogManifest file.")
}
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
let statistics = 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