Skip to content
Closed
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
40 changes: 31 additions & 9 deletions Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -17,37 +17,59 @@ let products: [PackageDescription.Product] = [
)
]

let flags: [PackageDescription.SwiftSetting] = [.enableExperimentalFeature("StrictConcurrency")]

let targets: [PackageDescription.Target] = [
.executableTarget(
name: "xcode-selective-test",
dependencies: ["SelectiveTestingCore",
.product(name: "ArgumentParser", package: "swift-argument-parser")]
.product(name: "ArgumentParser", package: "swift-argument-parser")],
swiftSettings: flags
),
.target(name: "SelectiveTestingCore",
dependencies: ["DependencyCalculator",
"TestConfigurator",
"Git",
"PathKit",
"Yams",
.product(name: "ArgumentParser", package: "swift-argument-parser")]),
.product(name: "ArgumentParser", package: "swift-argument-parser")],
swiftSettings: flags,
),
.target(name: "DependencyCalculator",
dependencies: ["Workspace", "PathKit", "Git", .product(name: "Logging", package: "swift-log")]),
dependencies: ["Workspace", "PathKit", "Git", .product(name: "Logging", package: "swift-log")],
swiftSettings: flags,
),
.target(name: "TestConfigurator",
dependencies: ["Workspace", "PathKit", .product(name: "Logging", package: "swift-log")]),
dependencies: [
"Workspace",
"PathKit",
.product(name: "Logging", package: "swift-log"),
.product(name: "ArgumentParser", package: "swift-argument-parser")
],
swiftSettings: flags,
),
.target(name: "Workspace",
dependencies: ["XcodeProj", .product(name: "Logging", package: "swift-log")]),
dependencies: ["XcodeProj", .product(name: "Logging", package: "swift-log")],
swiftSettings: flags,
),
.target(name: "Git",
dependencies: ["SelectiveTestShell", "PathKit", .product(name: "Logging", package: "swift-log")]),
.target(name: "SelectiveTestShell"),
dependencies: ["SelectiveTestShell", "PathKit", .product(name: "Logging", package: "swift-log")],
swiftSettings: flags,
),
.target(name: "SelectiveTestShell",
swiftSettings: flags,
),
.testTarget(
name: "SelectiveTestingTests",
dependencies: ["xcode-selective-test", "PathKit"],
resources: [.copy("ExampleProject")]
resources: [.copy("ExampleProject")],
swiftSettings: flags
),
.testTarget(
name: "DependencyCalculatorTests",
dependencies: ["DependencyCalculator", "Workspace", "PathKit", "SelectiveTestingCore"],
resources: [.copy("ExamplePackages")]
resources: [.copy("ExamplePackages")],
swiftSettings: flags
),
.plugin(
name: "SelectiveTestingPlugin",
Expand Down
12 changes: 8 additions & 4 deletions Plugins/SelectiveTestingPlugin/SelectiveTestingPlugin.swift
Original file line number Diff line number Diff line change
Expand Up @@ -48,19 +48,23 @@ struct SelectiveTestingPlugin: CommandPlugin {
}

if !toolArguments.contains(where: { $0 == "--test-plan" }) {
let testPlans = context.xcodeProject.filePaths.filter {
$0.extension == "xctestplan"
let allFiles = context.xcodeProject.targets.reduce([]) { partialResult, target in
partialResult + target.inputFiles
}

let testPlans = allFiles.filter {
$0.url.pathExtension == "xctestplan"
}

if !testPlans.isEmpty {
if testPlans.count == 1 {
print("Using \(testPlans[0].string) test plan")
print("Using \(testPlans[0].url.path()) test plan")
} else {
print("Using \(testPlans.count) test plans")
}

for testPlan in testPlans {
toolArguments.append(contentsOf: ["--test-plan", testPlan.string])
toolArguments.append(contentsOf: ["--test-plan", testPlan.url.path()])
}
}
}
Expand Down
3 changes: 1 addition & 2 deletions Sources/SelectiveTestingCore/SelectiveTestingTool.swift
Original file line number Diff line number Diff line change
Expand Up @@ -37,9 +37,8 @@ public final class SelectiveTestingTool {
dryRun: Bool = false,
verbose: Bool = false) throws
{
let suppliedBasePath = basePath.map { Path($0) }
var configCandidates: [Path] = []
if let suppliedBasePath {
if let suppliedBasePath = basePath.map({ Path($0) }) {
let baseDirectory: Path
if let ext = suppliedBasePath.extension,
ext == "xcworkspace" || ext == "xcodeproj" {
Expand Down
6 changes: 3 additions & 3 deletions Sources/Workspace/Target.swift
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
//

import Foundation
import PathKit
@preconcurrency import PathKit
import XcodeProj

extension PBXNativeTarget {
Expand All @@ -17,8 +17,8 @@ extension PBXNativeTarget {
}
}

public struct TargetIdentity: Hashable {
public enum TargetType {
public struct TargetIdentity: Hashable, Sendable {
public enum TargetType: Sendable {
case project
case package
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ struct DependencyCalculatorTests {

@Test
func graphIntegrity_submodule() async throws {
// given
let (depsGraph, mainApp, module, submodule, mainAppTests, moduleTests, submoduleTests) = depStructure()

let files = Set([Path("/folder/submodule/file.swift")])
Expand All @@ -47,13 +48,16 @@ struct DependencyCalculatorTests {
dependencyStructure: depsGraph,
candidateTestPlan: nil)

// when
let affected = graph.affectedTargets(changedFiles: files)

// then
#expect(affected == Set([mainApp, mainAppTests, module, moduleTests, submodule, submoduleTests]))
}

@Test
func graphIntegrity_mainApp() async throws {
// given
let (depsGraph, mainApp, _, _, mainAppTests, _, _) = depStructure()

let files = Set([Path("/folder/submodule/file.swift")])
Expand All @@ -63,13 +67,16 @@ struct DependencyCalculatorTests {
dependencyStructure: depsGraph,
candidateTestPlan: nil)

// when
let affected = graph.affectedTargets(changedFiles: files)

// then
#expect(affected == Set([mainApp, mainAppTests]))
}

@Test
func graphIntegrity_module() async throws {
// given
let (depsGraph, mainApp, module, _, mainAppTests, moduleTests, _) = depStructure()

let files = Set([Path("/folder/submodule/file.swift")])
Expand All @@ -79,8 +86,10 @@ struct DependencyCalculatorTests {
dependencyStructure: depsGraph,
candidateTestPlan: nil)

// when
let affected = graph.affectedTargets(changedFiles: files)

// then
#expect(affected == Set([module, moduleTests, mainApp, mainAppTests]))
}
}
9 changes: 9 additions & 0 deletions Tests/DependencyCalculatorTests/PackageMetadataTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -12,13 +12,16 @@ import Testing
struct PackageMetadataTests {
@Test
func packageMetadataParsing_Simple() throws {
// given
guard let exampleInBundle = Bundle.module.path(forResource: "ExamplePackages", ofType: "") else {
fatalError("Missing ExamplePackages in TestBundle")
}

// when
let basePath = Path(exampleInBundle) + "Simple"
let metadata = try PackageTargetMetadata.parse(at: basePath)

// then
#expect(metadata.count == 2)
let first = metadata[0]
#expect(first.name == "ExampleSubpackage")
Expand Down Expand Up @@ -51,13 +54,16 @@ struct PackageMetadataTests {

@Test
func packageMetadataParsing_ExamplePackage() throws {
// given
guard let exampleInBundle = Bundle.module.path(forResource: "ExamplePackages", ofType: "") else {
fatalError("Missing ExamplePackages in TestBundle")
}

// when
let basePath = Path(exampleInBundle) + "CrossDependency"
let metadata = try PackageTargetMetadata.parse(at: basePath)

// then
#expect(metadata.count == 10)
let first = metadata[0]
#expect(first.name == "SelectiveTesting")
Expand All @@ -82,13 +88,16 @@ struct PackageMetadataTests {

@Test
func packageAndWorkspace() throws {
// given
guard let exampleInBundle = Bundle.module.path(forResource: "ExamplePackages", ofType: "") else {
fatalError("Missing ExamplePackages in TestBundle")
}

// when
let basePath = Path(exampleInBundle) + "PackageAndWorkspace"
let metadata = try PackageTargetMetadata.parse(at: basePath)

// then
#expect(metadata.count == 2)
let first = metadata[0]
#expect(first.name == "APackage")
Expand Down
Loading
Loading