Skip to content

Commit

Permalink
Arrays of XCResultValueTypes do not parse correctly (#52)
Browse files Browse the repository at this point in the history
Change Description: This change addresses an issue where arrays (such as [Double] in the performance metrics object) were not properly being decoded. This was due to the need to decode into the intermediate type of XCResultValueType in order to get to the true values underneath.

Test Plan/Testing Performed: Added the xcresult that @OdNairy gave in #51. Added some explicit tests to double-check that the values returned match what we would expect. Also did a quick test if we removed one of the enum cases like "CPU Time" that the metric type enum would properly handle new metric identifier values from new xcresult versions.
  • Loading branch information
abotkin-cpi authored Oct 4, 2020
1 parent 7918fc6 commit 61bfe78
Show file tree
Hide file tree
Showing 46 changed files with 220 additions and 0 deletions.
40 changes: 40 additions & 0 deletions Sources/XCParseCore/ActionTestPerformanceMetricSummary.swift
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,40 @@

import Foundation


public enum ActionTestPerformanceMetric : ExpressibleByStringLiteral {
case ClockMonotonicTime
case CPUCycles
case CPUInstructionsRetired
case CPUTime
case DiskLogicalWrites
case MemoryPhysical
case MemoryPhysicalPeak
case Unknown(name: String)

public init(stringLiteral name: String) {
switch name {
case "com.apple.dt.XCTMetric_Clock.time.monotonic":
self = .ClockMonotonicTime
case "com.apple.dt.XCTMetric_CPU.cycles":
self = .CPUCycles
case "com.apple.dt.XCTMetric_CPU.instructions_retired":
self = .CPUInstructionsRetired
case "com.apple.dt.XCTMetric_CPU.time":
self = .CPUTime
case "com.apple.dt.XCTMetric_Disk.logical.writes":
self = .DiskLogicalWrites
case "com.apple.dt.XCTMetric_Memory.physical":
self = .MemoryPhysical
case "com.apple.dt.XCTMetric_Memory.physical-peak":
self = .MemoryPhysicalPeak
default:
self = .Unknown(name: name)
}
}
}


open class ActionTestPerformanceMetricSummary : Codable {
public let displayName: String
public let unitOfMeasurement: String
Expand All @@ -20,6 +54,12 @@ open class ActionTestPerformanceMetricSummary : Codable {
public let maxRegression: Double?
public let maxStandardDeviation: Double?

// Derived
public var metricType : ActionTestPerformanceMetric {
let identifierString = identifier ?? "Identifier Missing"
return ActionTestPerformanceMetric(stringLiteral: identifierString)
}

enum ActionTestPerformanceMetricSummaryCodingKeys: String, CodingKey {
case displayName
case unitOfMeasurement
Expand Down
5 changes: 5 additions & 0 deletions Sources/XCParseCore/XCPResultDecoding.swift
Original file line number Diff line number Diff line change
Expand Up @@ -350,6 +350,11 @@ extension KeyedDecodingContainer {
let resultObj = try container.decode(XCResultObject.self)
if let type = resultObj.type.getType() as? T.Type {
list.append(try tmpContainer.decode(type))
} else if let type = resultObj.type.getType() as? XCResultValueType.Type {
let decodedValue = try tmpContainer.decode(type)
if let value = decodedValue.getValue() as? T {
list.append(value)
}
}
}
return list
Expand Down

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Binary file not shown.
Binary file not shown.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Binary file not shown.
Binary file not shown.
Binary file not shown.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Binary file not shown.
Binary file not shown.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
29 changes: 29 additions & 0 deletions Tests/Resources/testMetrics.xcresult/Info.plist

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

140 changes: 140 additions & 0 deletions Tests/xcparseTests/xcparseTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,9 @@ final class xcparseTests: XCTestCase {
("testDivideAttachmentsWithUTIFlags",testDivideAttachmentsWithUTIFlags),
("testAttachmentsHEIC",testAttachmentsHEIC),
("testAttachmentsMissingInput",testAttachmentsMissingInput),
("testPerfomanceMetrics",testPerformanceMetricsCategorySearch),
("testPerformanceMetricsSearchAlongTheRoad",testPerformanceMetricsSearchAlongTheRoad),
("testPerformanceMetricsUsualSearch",testPerformanceMetricsUsualSearch),
]

func runAndWaitForXCParseProcess() throws {
Expand All @@ -64,6 +67,143 @@ final class xcparseTests: XCTestCase {
super.tearDown()
}

// MARK: - Metrics -

func testPerformanceMetricsCategorySearch() throws {
let file = try Resource(name: "testMetrics", type: "xcresult")
let xcresultPath = file.url.path

var xcresult = XCResult(path: xcresultPath)
guard let invocationRecord = xcresult.invocationRecord else { return }

guard let action = invocationRecord.actions.filter({ $0.actionResult.testsRef != nil }).first else { return }

guard let testPlanRunSummaries: ActionTestPlanRunSummaries = action.actionResult.testsRef!.modelFromReference(withXCResult: xcresult) else { return }

let testableSummaries = testPlanRunSummaries.summaries[0].testableSummaries

let testSummaries = testableSummaries[0].flattenedTestSummaryMap(withXCResult: xcresult).map({ $0.testSummary })
let categorySearch = testSummaries.filter {
guard let testName = $0.name else {
return false
}

return testName == "testCategorySearch()"
}

for testSummary in categorySearch {
for metric in testSummary.performanceMetrics {
let perfMetricMeasurements = metric.measurements

switch metric.metricType {
case .ClockMonotonicTime:
XCTAssertEqual(perfMetricMeasurements, [6.109e-06, 6.273000000000001e-06, 6.314e-06, 4.633e-06, 6.15e-06])
break
case .CPUCycles:
XCTAssertEqual(perfMetricMeasurements, [1965.018, 2366.559, 2923.601, 2114.557, 3463.443])
break
case .CPUInstructionsRetired:
XCTAssertEqual(perfMetricMeasurements, [3262.079, 3518.519, 3657.059, 3185.146, 4164.798])
break
case .CPUTime:
XCTAssertEqual(perfMetricMeasurements, [0.001053659, 0.00140999, 0.0016860430000000001, 0.0012279090000000001, 0.002028352])
break
case .DiskLogicalWrites:
XCTAssertEqual(perfMetricMeasurements, [0.0, 0.0, 49.152, 0.0, 0.0])
break
case .MemoryPhysical:
XCTAssertEqual(perfMetricMeasurements, [32.768, 81.92, 98.304, 32.768, 32.768])
break
case .MemoryPhysicalPeak:
XCTAssertEqual(perfMetricMeasurements, [0.0, 0.0, 0.0, 0.0, 0.0])
break
default:
break
}
}
}
}

func testPerformanceMetricsSearchAlongTheRoad() throws {
let file = try Resource(name: "testMetrics", type: "xcresult")
let xcresultPath = file.url.path

var xcresult = XCResult(path: xcresultPath)
guard let invocationRecord = xcresult.invocationRecord else { return }

guard let action = invocationRecord.actions.filter({ $0.actionResult.testsRef != nil }).first else { return }

guard let testPlanRunSummaries: ActionTestPlanRunSummaries = action.actionResult.testsRef!.modelFromReference(withXCResult: xcresult) else { return }

let testableSummaries = testPlanRunSummaries.summaries[0].testableSummaries

let testSummaries = testableSummaries[0].flattenedTestSummaryMap(withXCResult: xcresult).map({ $0.testSummary })
let categorySearch = testSummaries.filter {
guard let testName = $0.name else {
return false
}

return testName == "testSearchAlongTheRoad()"
}

for testSummary in categorySearch {
for metric in testSummary.performanceMetrics {
let perfMetricMeasurements = metric.measurements

switch metric.metricType {
case .MemoryPhysical:
XCTAssertEqual(perfMetricMeasurements, [0.0, 16.384, 0.0, 0.0, 0.0])
break
case .MemoryPhysicalPeak:
XCTAssertEqual(perfMetricMeasurements, [0.0, 0.0, 0.0, 0.0, 0.0])
break
default:
break
}
}
}
}

func testPerformanceMetricsUsualSearch() throws {
let file = try Resource(name: "testMetrics", type: "xcresult")
let xcresultPath = file.url.path

var xcresult = XCResult(path: xcresultPath)
guard let invocationRecord = xcresult.invocationRecord else { return }

guard let action = invocationRecord.actions.filter({ $0.actionResult.testsRef != nil }).first else { return }

guard let testPlanRunSummaries: ActionTestPlanRunSummaries = action.actionResult.testsRef!.modelFromReference(withXCResult: xcresult) else { return }

let testableSummaries = testPlanRunSummaries.summaries[0].testableSummaries

let testSummaries = testableSummaries[0].flattenedTestSummaryMap(withXCResult: xcresult).map({ $0.testSummary })
let categorySearch = testSummaries.filter {
guard let testName = $0.name else {
return false
}

return testName == "testUsualSearch()"
}

for testSummary in categorySearch {
for metric in testSummary.performanceMetrics {
let perfMetricMeasurements = metric.measurements

switch metric.metricType {
case .MemoryPhysical:
XCTAssertEqual(perfMetricMeasurements, [0.0, 16.384, 147.456, 32.768, 65.536])
break
case .MemoryPhysicalPeak:
XCTAssertEqual(perfMetricMeasurements, [0.0, 0.0, 0.0, 0.0, 0.0])
break
default:
break
}
}
}
}

// MARK: - Command - Screenshots

func testScreenshots() throws {
Expand Down

0 comments on commit 61bfe78

Please sign in to comment.