Skip to content

Commit

Permalink
feat: Add handling for Swift Testing Only Parallelization (#871)
Browse files Browse the repository at this point in the history
* add handling for swiftTestingOnly as introduced in Xcode 16

* delete dead links from References

* fix formatting

* more formatting

* rename Parallelization to Parallelizable to adhere to xcode naming

* rename Parallelization to Parallelizable to adhere to xcode naming

* add init as deprecated, updated extension and removed redundant unit test

* rename Parallelizable type to TestParallelization and make both types available in TestableReference with deprecation warning to maintain backwards caompatability

* reintroduce parallelizable from xmlElement

* fix tests using old type

* use computed var to best handle backwards compat with updated tests

* address lint warnings

* Fix linting issues

* Fix linting issue

---------

Co-authored-by: Pedro <[email protected]>
  • Loading branch information
kelvinharron and pepicrft authored Dec 3, 2024
1 parent 4e84f41 commit b907392
Show file tree
Hide file tree
Showing 4 changed files with 168 additions and 11 deletions.
2 changes: 0 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -108,14 +108,12 @@ object.
## References 📚

- [Xcode Project File Format](http://www.monobjc.net/xcode-project-file-format.html)
- [A brief look at the Xcode project format](http://danwright.info/blog/2010/10/xcode-pbxproject-files/)
- [pbexplorer](https://github.com/mjmsmith/pbxplorer)
- [pbxproj identifiers](https://pewpewthespells.com/blog/pbxproj_identifiers.html)
- [mob-pbxproj](https://github.com/kronenthaler/mod-pbxproj)
- [Xcodeproj](https://github.com/CocoaPods/Xcodeproj)
- [Nanaimo](https://github.com/CocoaPods/Nanaimo)
- [Facebook Buck](https://buckbuild.com/javadoc/com/facebook/buck/apple/xcode/xcodeproj/package-summary.html)
- [Swift Package Manager - Xcodeproj](https://github.com/apple/swift-package-manager/tree/main/Sources/Xcodeproj)

## Contributing

Expand Down
12 changes: 12 additions & 0 deletions Sources/XcodeProj/Scheme/XCScheme+TestParallelization.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import Foundation

public extension XCScheme {
/// With the introduction of Swift Testing and Xcode 16, you can now choose to run your tests
// in parallel across either the full suite of tests in a target with `.all`, just those created
// under Swift Testing with `.swiftTestingOnly`, or run them serially with the `.none` option.
enum TestParallelization: String {
case all
case swiftTestingOnly
case none
}
}
48 changes: 44 additions & 4 deletions Sources/XcodeProj/Scheme/XCScheme+TestableReference.swift
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,13 @@ public extension XCScheme {
// MARK: - Attributes

public var skipped: Bool
public var parallelizable: Bool
@available(*, deprecated, message: "Please use parallelization property instead")
public var parallelizable: Bool {
get { parallelization == .swiftTestingOnly }
set { parallelization = newValue ? .swiftTestingOnly : .none }
}

public var parallelization: TestParallelization
public var randomExecutionOrdering: Bool
public var useTestSelectionWhitelist: Bool?
public var buildableReference: BuildableReference
Expand All @@ -16,6 +22,25 @@ public extension XCScheme {

// MARK: - Init

public init(skipped: Bool,
parallelization: TestParallelization = .none,
randomExecutionOrdering: Bool = false,
buildableReference: BuildableReference,
locationScenarioReference: LocationScenarioReference? = nil,
skippedTests: [TestItem] = [],
selectedTests: [TestItem] = [],
useTestSelectionWhitelist: Bool? = nil) {
self.skipped = skipped
self.parallelization = parallelization
self.randomExecutionOrdering = randomExecutionOrdering
self.buildableReference = buildableReference
self.locationScenarioReference = locationScenarioReference
self.useTestSelectionWhitelist = useTestSelectionWhitelist
self.selectedTests = selectedTests
self.skippedTests = skippedTests
}

@available(*, deprecated, message: "Use init with Parallelization argument instead")
public init(skipped: Bool,
parallelizable: Bool = false,
randomExecutionOrdering: Bool = false,
Expand All @@ -25,7 +50,7 @@ public extension XCScheme {
selectedTests: [TestItem] = [],
useTestSelectionWhitelist: Bool? = nil) {
self.skipped = skipped
self.parallelizable = parallelizable
parallelization = parallelizable ? .swiftTestingOnly : .none
self.randomExecutionOrdering = randomExecutionOrdering
self.buildableReference = buildableReference
self.locationScenarioReference = locationScenarioReference
Expand All @@ -36,7 +61,13 @@ public extension XCScheme {

init(element: AEXMLElement) throws {
skipped = element.attributes["skipped"] == "YES"
parallelizable = element.attributes["parallelizable"] == "YES"

if let parallelizableValue = element.attributes["parallelizable"] {
parallelization = parallelizableValue == "YES" ? .all : .none
} else {
parallelization = .swiftTestingOnly
}

useTestSelectionWhitelist = element.attributes["useTestSelectionWhitelist"] == "YES"
randomExecutionOrdering = element.attributes["testExecutionOrdering"] == "random"
buildableReference = try BuildableReference(element: element["BuildableReference"])
Expand All @@ -63,7 +94,16 @@ public extension XCScheme {

func xmlElement() -> AEXMLElement {
var attributes: [String: String] = ["skipped": skipped.xmlString]
attributes["parallelizable"] = parallelizable ? parallelizable.xmlString : nil

switch parallelization {
case .all:
attributes["parallelizable"] = "YES"
case .none:
attributes["parallelizable"] = "NO"
case .swiftTestingOnly:
break // SwiftTesting is inferred by the lack of a value
}

if let useTestSelectionWhitelist {
attributes["useTestSelectionWhitelist"] = useTestSelectionWhitelist.xmlString
}
Expand Down
117 changes: 112 additions & 5 deletions Tests/XcodeProjTests/Scheme/XCSchemeTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ final class XCSchemeIntegrationTests: XCTestCase {
func test_write_testableReferenceDefaultAttributesValuesAreOmitted() {
let reference = XCScheme.TestableReference(
skipped: false,
parallelizable: false,
parallelization: .swiftTestingOnly,
randomExecutionOrdering: false,
buildableReference: XCScheme.BuildableReference(
referencedContainer: "",
Expand All @@ -85,10 +85,10 @@ final class XCSchemeIntegrationTests: XCTestCase {
XCTAssertNil(subject.attributes["useTestSelectionWhitelist"])
}

func test_write_testableReferenceAttributesValues() {
func test_write_testableReferenceAttributesValues_allParallelizable() {
let reference = XCScheme.TestableReference(
skipped: false,
parallelizable: true,
parallelization: .all,
randomExecutionOrdering: true,
buildableReference: XCScheme.BuildableReference(
referencedContainer: "",
Expand All @@ -107,11 +107,117 @@ final class XCSchemeIntegrationTests: XCTestCase {
XCTAssertEqual(subject.attributes["testExecutionOrdering"], "random")
}

func test_write_testableReferenceAttributesValues_noneParallelizable() {
let reference = XCScheme.TestableReference(
skipped: false,
parallelization: .none,
randomExecutionOrdering: true,
buildableReference: XCScheme.BuildableReference(
referencedContainer: "",
blueprint: PBXObject(),
buildableName: "",
blueprintName: ""
),
skippedTests: [],
selectedTests: [],
useTestSelectionWhitelist: true
)
let subject = reference.xmlElement()
XCTAssertEqual(subject.attributes["skipped"], "NO")
XCTAssertEqual(subject.attributes["parallelizable"], "NO")
XCTAssertEqual(subject.attributes["useTestSelectionWhitelist"], "YES")
XCTAssertEqual(subject.attributes["testExecutionOrdering"], "random")
}

func test_write_testableReferenceAttributesValues_trueParallelizable() {
let reference = XCScheme.TestableReference(
skipped: false,
parallelizable: true,
randomExecutionOrdering: true,
buildableReference: XCScheme.BuildableReference(
referencedContainer: "",
blueprint: PBXObject(),
buildableName: "",
blueprintName: ""
),
skippedTests: [],
selectedTests: [],
useTestSelectionWhitelist: true
)
let subject = reference.xmlElement()
XCTAssertEqual(subject.attributes["skipped"], "NO")
XCTAssertEqual(subject.attributes["parallelizable"], nil)
XCTAssertEqual(subject.attributes["useTestSelectionWhitelist"], "YES")
XCTAssertEqual(subject.attributes["testExecutionOrdering"], "random")
}

func test_write_testableReferenceAttributesValues_falseParallelizable() {
let reference = XCScheme.TestableReference(
skipped: false,
parallelizable: false,
randomExecutionOrdering: true,
buildableReference: XCScheme.BuildableReference(
referencedContainer: "",
blueprint: PBXObject(),
buildableName: "",
blueprintName: ""
),
skippedTests: [],
selectedTests: [],
useTestSelectionWhitelist: true
)
let subject = reference.xmlElement()
XCTAssertEqual(subject.attributes["skipped"], "NO")
XCTAssertEqual(subject.attributes["parallelizable"], "NO")
XCTAssertEqual(subject.attributes["useTestSelectionWhitelist"], "YES")
XCTAssertEqual(subject.attributes["testExecutionOrdering"], "random")
}

func test_computed_parallelizable_testableReference_false() {
let reference = XCScheme.TestableReference(
skipped: false,
parallelizable: false,
randomExecutionOrdering: true,
buildableReference: XCScheme.BuildableReference(
referencedContainer: "",
blueprint: PBXObject(),
buildableName: "",
blueprintName: ""
),
skippedTests: [],
selectedTests: [],
useTestSelectionWhitelist: true
)

XCTAssertEqual(reference.parallelizable, false)
XCTAssertEqual(reference.parallelization, .none)
}

func test_computed_parallelizable_testableReference_true() {
let reference = XCScheme.TestableReference(
skipped: false,
parallelizable: true,
randomExecutionOrdering: true,
buildableReference: XCScheme.BuildableReference(
referencedContainer: "",
blueprint: PBXObject(),
buildableName: "",
blueprintName: ""
),
skippedTests: [],
selectedTests: [],
useTestSelectionWhitelist: true
)

XCTAssertEqual(reference.parallelizable, true)
XCTAssertEqual(reference.parallelization, .swiftTestingOnly)
}

func test_write_testableReferenceSelectedTests() {
// Given
let reference = XCScheme.TestableReference(
skipped: false,
parallelizable: true,
parallelization: .all,
randomExecutionOrdering: true,
buildableReference: XCScheme.BuildableReference(
referencedContainer: "",
Expand Down Expand Up @@ -397,7 +503,8 @@ final class XCSchemeIntegrationTests: XCTestCase {
XCTAssertEqual(scheme.testAction?.codeCoverageEnabled, true)
XCTAssertEqual(scheme.testAction?.onlyGenerateCoverageForSpecifiedTargets, true)
XCTAssertEqual(scheme.testAction?.testables.first?.skipped, false)
XCTAssertEqual(scheme.testAction?.testables.first?.parallelizable, false)
XCTAssertEqual(scheme.testAction?.testables.first?.parallelizable, true)
XCTAssertEqual(scheme.testAction?.testables.first?.parallelization, .swiftTestingOnly)
XCTAssertEqual(scheme.testAction?.testables.first?.randomExecutionOrdering, false)
XCTAssertEqual(scheme.testAction?.testables.first?.useTestSelectionWhitelist, false)
XCTAssertEqual(scheme.testAction?.testables.first?.buildableReference.buildableIdentifier, "primary")
Expand Down

0 comments on commit b907392

Please sign in to comment.