Move the auto-generation of schemes to a mapper (#1357)

* Add empty AutogeneratedSchemesGraphMapper

* Don't generate the schemes at generation time

* Implement AutogeneratedSchemesGraphMapper

* Update CHANGELOG

* Remove unnecessary method

* Turn the graph mapper into a project mapper

* Address some comments

Co-authored-by: Pedro Piñera <pedro@ppinera.es>
This commit is contained in:
Pedro Piñera Buendía 2020-06-03 17:41:36 +02:00 committed by GitHub
parent c07df2be86
commit 5dc8b7ab92
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
22 changed files with 312 additions and 224 deletions

View File

@ -11,6 +11,7 @@ Please, check out guidelines: https://keepachangelog.com/en/1.0.0/
### Changed
- Upgrade XcodeProj to 7.11.0 [#1398](https://github.com/tuist/tuist/pull/1398) by [@pepibumur](https://github.com/pepibumur)
- Move the auto-generation of schemes to a model mapper [#1357](https://github.com/tuist/tuist/pull/1357) by [@pepibumur](https://github.com/pepibumur)
## 1.9.0 - Speedy Gonzales

View File

@ -5,7 +5,7 @@ public protocol ProjectMapping {
}
public class SequentialProjectMapper: ProjectMapping {
private let mappers: [ProjectMapping]
let mappers: [ProjectMapping]
public init(mappers: [ProjectMapping]) {
self.mappers = mappers

View File

@ -11,7 +11,6 @@ public struct Project: Equatable, CustomStringConvertible {
lhs.targets == rhs.targets &&
lhs.packages == rhs.packages &&
lhs.schemes == rhs.schemes &&
lhs.autogenerateSchemes == rhs.autogenerateSchemes &&
lhs.settings == rhs.settings &&
lhs.filesGroup == rhs.filesGroup &&
lhs.additionalFiles == rhs.additionalFiles
@ -40,9 +39,6 @@ public struct Project: Equatable, CustomStringConvertible {
/// Project schemes
public var schemes: [Scheme]
/// Auto generate default schemes
public var autogenerateSchemes: Bool
/// Project settings.
public var settings: Settings
@ -67,15 +63,14 @@ public struct Project: Equatable, CustomStringConvertible {
/// *(Those won't be included in any build phases)*
public init(path: AbsolutePath,
name: String,
organizationName: String? = nil,
fileName: String? = nil,
organizationName: String?,
fileName: String?,
settings: Settings,
filesGroup: ProjectGroup,
targets: [Target] = [],
packages: [Package] = [],
schemes: [Scheme] = [],
autogenerateSchemes: Bool = true,
additionalFiles: [FileElement] = []) {
targets: [Target],
packages: [Package],
schemes: [Scheme],
additionalFiles: [FileElement]) {
self.path = path
self.name = name
self.organizationName = organizationName
@ -83,7 +78,6 @@ public struct Project: Equatable, CustomStringConvertible {
self.targets = targets
self.packages = packages
self.schemes = schemes
self.autogenerateSchemes = autogenerateSchemes
self.settings = settings
self.filesGroup = filesGroup
self.additionalFiles = additionalFiles
@ -144,7 +138,28 @@ public struct Project: Equatable, CustomStringConvertible {
targets: targets,
packages: packages,
schemes: schemes,
autogenerateSchemes: autogenerateSchemes,
additionalFiles: additionalFiles)
}
/// Returns a copy of the project with the given schemes set.
/// - Parameter schemes: Schemes to be set to the copy.
public func with(schemes: [Scheme]) -> Project {
Project(path: path,
name: name,
organizationName: organizationName,
fileName: fileName,
settings: settings,
filesGroup: filesGroup,
targets: targets,
packages: packages,
schemes: schemes,
additionalFiles: additionalFiles)
}
/// Returns the name of the default configuration.
public var defaultDebugBuildConfigurationName: String {
let debugConfiguration = settings.defaultDebugBuildConfiguration()
let buildConfiguration = debugConfiguration ?? settings.configurations.keys.first
return buildConfiguration?.name ?? BuildConfiguration.debug.name
}
}

View File

@ -12,7 +12,6 @@ public extension Project {
targets: [Target] = [Target.test()],
packages: [Package] = [],
schemes: [Scheme] = [],
autogenerateSchemes: Bool = true,
additionalFiles: [FileElement] = []) -> Project {
Project(path: path,
name: name,
@ -23,29 +22,28 @@ public extension Project {
targets: targets,
packages: packages,
schemes: schemes,
autogenerateSchemes: autogenerateSchemes,
additionalFiles: additionalFiles)
}
static func empty(path: AbsolutePath = AbsolutePath("/test/"),
name: String = "Project",
organizationName: String? = nil,
fileName: String? = nil,
settings: Settings = .default,
filesGroup: ProjectGroup = .group(name: "Project"),
targets: [Target] = [],
packages: [Package] = [],
schemes: [Scheme] = [],
autogenerateSchemes: Bool = true,
additionalFiles: [FileElement] = []) -> Project {
Project(path: path,
name: name,
organizationName: organizationName,
fileName: fileName,
settings: settings,
filesGroup: filesGroup,
targets: targets,
packages: packages,
schemes: schemes,
autogenerateSchemes: autogenerateSchemes,
additionalFiles: additionalFiles)
}
}

View File

@ -55,29 +55,12 @@ final class SchemesGenerator: SchemesGenerating {
func generateProjectSchemes(project: Project,
generatedProject: GeneratedProject,
graph: Graph) throws -> [SchemeDescriptor] {
let customSchemes: [SchemeDescriptor] = try project.schemes.map { scheme in
try project.schemes.map { scheme in
try generateScheme(scheme: scheme,
path: project.path,
graph: graph,
generatedProjects: [project.path: generatedProject])
}
guard project.autogenerateSchemes else {
return customSchemes
}
let buildConfiguration = defaultDebugBuildConfigurationName(in: project)
let userDefinedSchemes = Set(project.schemes.map(\.name))
let defaultSchemeTargets = project.targets.filter { !userDefinedSchemes.contains($0.name) }
let defaultSchemes: [SchemeDescriptor] = try defaultSchemeTargets.map { target in
let scheme = createDefaultScheme(target: target, project: project, buildConfiguration: buildConfiguration, graph: graph)
return try generateScheme(scheme: scheme,
path: project.path,
graph: graph,
generatedProjects: [project.path: generatedProject])
}
return customSchemes + defaultSchemes
}
/// Wipes shared and user schemes at a workspace or project path. This is needed
@ -94,37 +77,6 @@ final class SchemesGenerator: SchemesGenerating {
if fileHandler.exists(sharedPath) { try fileHandler.delete(sharedPath) }
}
func createDefaultScheme(target: Target, project: Project, buildConfiguration: String, graph: Graph) -> Scheme {
let targetReference = TargetReference(projectPath: project.path, name: target.name)
let testTargets: [TestableTarget]
if target.product.testsBundle {
testTargets = [TestableTarget(target: targetReference)]
} else {
testTargets = graph.testTargetsDependingOn(path: project.path, name: target.name)
.map { TargetReference(projectPath: $0.project.path, name: $0.target.name) }
.map { TestableTarget(target: $0) }
}
return Scheme(name: target.name,
shared: true,
buildAction: BuildAction(targets: [targetReference]),
testAction: TestAction(targets: testTargets,
arguments: nil,
configurationName: buildConfiguration,
coverage: false,
codeCoverageTargets: [],
preActions: [],
postActions: [],
diagnosticsOptions: Set()),
runAction: RunAction(configurationName: buildConfiguration,
executable: targetReference,
filePath: nil,
arguments: Arguments(environment: target.environment),
diagnosticsOptions: Set()))
}
/// Generate schemes for a project or workspace.
///
/// - Parameters:
@ -325,7 +277,7 @@ final class SchemesGenerator: SchemesGenerating {
pathRunnable = XCScheme.PathRunnable(filePath: filePath.pathString)
} else {
guard let targetNode = graph.target(path: target.projectPath, name: target.name) else { return nil }
defaultBuildConfiguration = defaultDebugBuildConfigurationName(in: targetNode.project)
defaultBuildConfiguration = targetNode.project.defaultDebugBuildConfigurationName
guard let buildableReference = try createBuildableReference(targetReference: target,
graph: graph,
rootPath: rootPath,
@ -426,7 +378,7 @@ final class SchemesGenerator: SchemesGenerating {
guard let target = try defaultTargetReference(scheme: scheme),
let targetNode = graph.target(path: target.projectPath, name: target.name) else { return nil }
let buildConfiguration = scheme.analyzeAction?.configurationName ?? defaultDebugBuildConfigurationName(in: targetNode.project)
let buildConfiguration = scheme.analyzeAction?.configurationName ?? targetNode.project.defaultDebugBuildConfigurationName
return XCScheme.AnalyzeAction(buildConfiguration: buildConfiguration)
}
@ -611,13 +563,6 @@ final class SchemesGenerator: SchemesGenerating {
}.sorted { $0.variable < $1.variable }
}
private func defaultDebugBuildConfigurationName(in project: Project) -> String {
let debugConfiguration = project.settings.defaultDebugBuildConfiguration()
let buildConfiguration = debugConfiguration ?? project.settings.configurations.keys.first
return buildConfiguration?.name ?? BuildConfiguration.debug.name
}
/// Returns the scheme buildable reference for a given target.
///
/// - Parameters:

View File

@ -0,0 +1,64 @@
import Foundation
import TuistCore
/// A project mapper that auto-generates schemes for each of the targets of the graph
/// if the user hasn't already defined schemes for those.
public class AutogeneratedSchemesProjectMapper: ProjectMapping {
// MARK: - Init
public init() {}
// MARK: - GraphMapping
public func map(project: Project) throws -> (Project, [SideEffectDescriptor]) {
let schemeNames = Set(project.schemes.map { $0.name })
let schemes = project.schemes
let autogeneratedSchemes = project.targets.compactMap { (target: Target) -> Scheme? in
let scheme = self.createDefaultScheme(target: target,
project: project,
buildConfiguration: project.defaultDebugBuildConfigurationName)
// The user has already defined a scheme with that name.
if schemeNames.contains(scheme.name) { return nil }
return scheme
}
return (project.with(schemes: schemes + autogeneratedSchemes), [])
}
// MARK: - Private
private func createDefaultScheme(target: Target, project: Project, buildConfiguration: String) -> Scheme {
let targetReference = TargetReference(projectPath: project.path, name: target.name)
let testTargets: [TestableTarget]
if target.product.testsBundle {
testTargets = [TestableTarget(target: targetReference)]
} else {
// The test targets that are dependant on the given target.
testTargets = project.targets
.filter { $0.product.testsBundle && $0.dependencies.contains(.target(name: target.name)) }
.sorted(by: { $0.name < $1.name })
.map { TargetReference(projectPath: project.path, name: $0.name) }
.map { TestableTarget(target: $0) }
}
return Scheme(name: target.name,
shared: true,
buildAction: BuildAction(targets: [targetReference]),
testAction: TestAction(targets: testTargets,
arguments: nil,
configurationName: buildConfiguration,
coverage: false,
codeCoverageTargets: [],
preActions: [],
postActions: [],
diagnosticsOptions: Set()),
runAction: RunAction(configurationName: buildConfiguration,
executable: targetReference,
filePath: nil,
arguments: Arguments(environment: target.environment),
diagnosticsOptions: Set()))
}
}

View File

@ -2,6 +2,7 @@ import Foundation
import TuistCache
import TuistCloud
import TuistCore
import TuistGenerator
/// It defines an interface for providing the mappers to be used for a specific configuration.
protocol GraphMapperProviding {

View File

@ -0,0 +1,23 @@
import Foundation
import TuistCore
import TuistGenerator
/// It defines an interface for providing the project mappers to be used for a specific configuration.
protocol ProjectMapperProviding {
/// Returns a list of mappers to be used for a specific configuration.
/// - Parameter config: Project's configuration.
func mapper(config: Config) -> ProjectMapping
}
class ProjectMapperProvider: ProjectMapperProviding {
func mapper(config: Config) -> ProjectMapping {
var mappers: [ProjectMapping] = []
// Auto-generation of schemes
if !config.generationOptions.contains(.disableAutogeneratedSchemes) {
mappers.append(AutogeneratedSchemesProjectMapper())
}
return SequentialProjectMapper(mappers: mappers)
}
}

View File

@ -78,10 +78,14 @@ final class ProjectEditorMapper: ProjectEditorMapping {
// Project
let project = Project(path: sourceRootPath,
name: "Manifests",
organizationName: nil,
fileName: nil,
settings: projectSettings,
filesGroup: .group(name: "Manifests"),
targets: targets,
schemes: [scheme])
packages: [],
schemes: [scheme],
additionalFiles: [])
// Graph
var dependencies: [TargetNode] = []

View File

@ -24,11 +24,12 @@ class ProjectGenerator: ProjectGenerating {
private let modelLoader: GeneratorModelLoading
private let graphLoader: GraphLoading
private let sideEffectDescriptorExecutor: SideEffectDescriptorExecuting
private let projectMapper: ProjectMapping
private let graphMapperProvider: GraphMapperProviding
private let projectMapperProvider: ProjectMapperProviding
private let manifestLoader: ManifestLoading
init(graphMapperProvider: GraphMapperProviding = GraphMapperProvider(useCache: false),
projectMapperProvider: ProjectMapperProviding = ProjectMapperProvider(),
manifestLoaderFactory: ManifestLoaderFactory = ManifestLoaderFactory()) {
let manifestLoader = manifestLoaderFactory.createManifestLoader()
recursiveManifestLoader = RecursiveManifestLoader(manifestLoader: manifestLoader)
@ -39,7 +40,7 @@ class ProjectGenerator: ProjectGenerating {
sideEffectDescriptorExecutor = SideEffectDescriptorExecutor()
self.modelLoader = modelLoader
self.graphMapperProvider = graphMapperProvider
projectMapper = SequentialProjectMapper(mappers: [])
self.projectMapperProvider = projectMapperProvider
self.manifestLoader = manifestLoader
}
@ -154,10 +155,14 @@ class ProjectGenerator: ProjectGenerating {
manifestLinter.lint(project: $0.value)
}.printAndThrowIfNeeded()
// Load config
let config = try graphLoader.loadConfig(path: path)
// Convert to models
let models = try convert(manifests: manifests)
// Apply any registered model mappers
let projectMapper = projectMapperProvider.mapper(config: config)
let updatedModels = try models.map(projectMapper.map)
let updatedProjects = updatedModels.map(\.0)
let modelMapperSideEffects = updatedModels.flatMap { $0.1 }
@ -168,7 +173,6 @@ class ProjectGenerator: ProjectGenerating {
let (graph, project) = try cachedGraphLoader.loadProject(path: path)
// Apply graph mappers
let config = try graphLoader.loadConfig(path: graph.entryPath)
let (updatedGraph, graphMapperSideEffects) = try graphMapperProvider.mapper(config: config).map(graph: graph)
return (project, updatedGraph, modelMapperSideEffects + graphMapperSideEffects)
@ -183,10 +187,14 @@ class ProjectGenerator: ProjectGenerating {
manifestLinter.lint(project: $0.value)
}.printAndThrowIfNeeded()
// Load config
let config = try graphLoader.loadConfig(path: path)
// Convert to models
let models = try convert(manifests: manifests)
// Apply model mappers
let projectMapper = projectMapperProvider.mapper(config: config)
let updatedModels = try models.projects.map(projectMapper.map)
let updatedProjects = updatedModels.map(\.0)
let modelMapperSideEffects = updatedModels.flatMap { $0.1 }
@ -197,7 +205,6 @@ class ProjectGenerator: ProjectGenerating {
let (graph, workspace) = try cachedGraphLoader.loadWorkspace(path: path)
// Apply graph mappers
let config = try graphLoader.loadConfig(path: graph.entryPath)
let (updatedGraph, graphMapperSideEffects) = try graphMapperProvider.mapper(config: config).map(graph: graph)
return (workspace, updatedGraph, modelMapperSideEffects + graphMapperSideEffects)

View File

@ -102,10 +102,6 @@ extension GeneratorModelLoader {
enrichedModel = enrichedModel.replacing(organizationName: organizationName)
}
if config.generationOptions.contains(.disableAutogeneratedSchemes) {
enrichedModel = enrichedModel.replacing(autogenerateSchemes: false)
}
return enrichedModel
}

View File

@ -21,6 +21,7 @@ extension TuistCore.Project {
return Project(path: generatorPaths.manifestDirectory,
name: name,
organizationName: organizationName,
fileName: nil,
settings: settings ?? .default,
filesGroup: .group(name: "Project"),
targets: targets,
@ -39,7 +40,6 @@ extension TuistCore.Project {
targets: targets + [target],
packages: packages,
schemes: schemes,
autogenerateSchemes: autogenerateSchemes,
additionalFiles: additionalFiles)
}
@ -53,7 +53,6 @@ extension TuistCore.Project {
targets: targets,
packages: packages,
schemes: schemes,
autogenerateSchemes: autogenerateSchemes,
additionalFiles: additionalFiles)
}
@ -67,21 +66,6 @@ extension TuistCore.Project {
targets: targets,
packages: packages,
schemes: schemes,
autogenerateSchemes: autogenerateSchemes,
additionalFiles: additionalFiles)
}
func replacing(autogenerateSchemes: Bool) -> TuistCore.Project {
Project(path: path,
name: name,
organizationName: organizationName,
fileName: fileName,
settings: settings,
filesGroup: filesGroup,
targets: targets,
packages: packages,
schemes: schemes,
autogenerateSchemes: autogenerateSchemes,
additionalFiles: additionalFiles)
}
}

View File

@ -13,6 +13,10 @@ public extension XCTestCase {
// MARK: - XCTAssertions
func XCTAssertEmpty<T: Collection>(_ collection: T, file: StaticString = #file, line: UInt = #line) {
XCTAssertEqual(collection.count, 0, "Expected to be empty but it has \(collection.count) elements", file: file, line: line)
}
// swiftlint:disable large_tuple
func XCTAssertEqualPairs<T: Equatable>(_ subjects: [(T, T, Bool)], file: StaticString = #file, line: UInt = #line) {
subjects.forEach {

View File

@ -48,4 +48,27 @@ final class ProjectTests: XCTestCase {
XCTAssertEqual(project.fileName, "SomeProjectName")
XCTAssertEqual(project.name, "SomeProjectName")
}
func test_defaultDebugBuildConfigurationName_when_defaultDebugConfigExists() {
// Given
let project = Project.test(settings: Settings.test())
// When
let got = project.defaultDebugBuildConfigurationName
// Then
XCTAssertEqual(got, "Debug")
}
func test_defaultDebugBuildConfigurationName_when_defaultDebugConfigDoesntExist() {
// Given
let settings = Settings.test(base: [:], configurations: [.debug("Test"): Configuration.test()])
let project = Project.test(settings: settings)
// When
let got = project.defaultDebugBuildConfigurationName
// Then
XCTAssertEqual(got, "Test")
}
}

View File

@ -23,6 +23,8 @@ final class ProjectGroupsTests: XCTestCase {
sourceRootPath = AbsolutePath("/test/")
project = Project(path: path,
name: "Project",
organizationName: nil,
fileName: nil,
settings: .default,
filesGroup: .group(name: "Project"),
targets: [
@ -30,7 +32,8 @@ final class ProjectGroupsTests: XCTestCase {
.test(),
],
packages: [],
schemes: [])
schemes: [],
additionalFiles: [])
pbxproj = PBXProj()
}

View File

@ -17,68 +17,6 @@ final class SchemesGeneratorTests: XCTestCase {
subject = SchemesGenerator()
}
// MARK: - Scheme Generation
func test_defaultGeneratedScheme_RegularTarget() throws {
// Given
let target = Target.test(name: "App", product: .app)
let testTarget1 = Target.test(name: "AppTests1", product: .unitTests)
let testTarget2 = Target.test(name: "AppTests2", product: .unitTests)
let testTarget3 = Target.test(name: "AppTests3", product: .unitTests)
let testTargets = [testTarget1, testTarget2, testTarget3]
let project = Project.test(targets: [target] + testTargets)
let graph = Graph.create(dependencies: [(project: project, target: target, dependencies: []),
(project: project, target: testTarget1, dependencies: [target]),
(project: project, target: testTarget2, dependencies: [target]),
(project: project, target: testTarget3, dependencies: [target])])
// When
let got = subject.createDefaultScheme(target: target, project: project, buildConfiguration: "Debug", graph: graph)
// Then
let result = try XCTUnwrap(got)
XCTAssertEqual(result.name, target.name)
XCTAssertTrue(result.shared)
let buildAction = try XCTUnwrap(result.buildAction)
let targetReference = TargetReference(projectPath: project.path, name: target.name)
XCTAssertEqual(buildAction.targets, [targetReference])
let testAction = try XCTUnwrap(result.testAction)
let testableTargests = testTargets
.map { TargetReference(projectPath: project.path, name: $0.name) }
.map { TestableTarget(target: $0) }
XCTAssertEqual(testAction.targets, testableTargests)
}
func test_defaultGeneratedScheme_TestTarget() throws {
// Given
let target = Target.test(name: "App", product: .app)
let testTarget = Target.test(name: "AppTests", product: .unitTests)
let project = Project.test(targets: [target, testTarget])
let graph = Graph.create(dependencies: [(project: project, target: target, dependencies: []),
(project: project, target: testTarget, dependencies: [target])])
// When
let got = subject.createDefaultScheme(target: testTarget, project: project, buildConfiguration: "Debug", graph: graph)
// Then
let result = try XCTUnwrap(got)
XCTAssertEqual(result.name, testTarget.name)
XCTAssertTrue(result.shared)
let buildAction = try XCTUnwrap(result.buildAction)
let targetReference = TargetReference(projectPath: project.path, name: testTarget.name)
XCTAssertEqual(buildAction.targets, [targetReference])
let testAction = try XCTUnwrap(result.testAction)
let testTargetReference = TargetReference(projectPath: project.path, name: testTarget.name)
let testableTarget = TestableTarget(target: testTargetReference)
XCTAssertEqual(testAction.targets, [testableTarget])
}
// MARK: - Build Action Tests
func test_schemeBuildAction_whenSingleProject() throws {
@ -662,39 +600,6 @@ final class SchemesGeneratorTests: XCTestCase {
XCTAssertEqual(result.revealArchiveInOrganizer, true)
}
func test_schemeGenerationModes_default() throws {
// Given
let app = Target.test(name: "App", product: .app)
let framework = Target.test(name: "Framework", product: .framework)
let unitTests = Target.test(name: "AppTests", product: .unitTests)
let uiTests = Target.test(name: "AppUITests", product: .uiTests)
let project = Project.test(targets: [app, framework, unitTests, uiTests])
let graph = Graph.create(
project: project,
dependencies: [
(target: app, dependencies: [framework]),
(target: framework, dependencies: []),
(target: unitTests, dependencies: [app]),
(target: uiTests, dependencies: [app]),
]
)
// When
let result = try subject.generateProjectSchemes(project: project,
generatedProject: generatedProject(targets: project.targets),
graph: graph)
// Then
let schemes = result.map(\.xcScheme.name)
XCTAssertEqual(schemes, [
"App",
"Framework",
"AppTests",
"AppUITests",
])
}
func test_schemeGenerationModes_customOnly() throws {
// Given
let app = Target.test(name: "App", product: .app)
@ -702,7 +607,7 @@ final class SchemesGeneratorTests: XCTestCase {
let unitTests = Target.test(name: "AppTests", product: .unitTests)
let uiTests = Target.test(name: "AppUITests", product: .uiTests)
let scheme = Scheme.test()
let project = Project.test(targets: [app, framework, unitTests, uiTests], schemes: [scheme], autogenerateSchemes: false)
let project = Project.test(targets: [app, framework, unitTests, uiTests], schemes: [scheme])
let graph = Graph.create(
project: project,

View File

@ -0,0 +1,96 @@
import Foundation
import TSCBasic
import TuistCore
import XCTest
@testable import TuistGenerator
@testable import TuistSupport
@testable import TuistSupportTesting
final class AutogeneratedSchemesProjectMapperTests: TuistUnitTestCase {
var subject: AutogeneratedSchemesProjectMapper!
override func setUp() {
super.setUp()
subject = AutogeneratedSchemesProjectMapper()
}
override func tearDown() {
super.tearDown()
subject = nil
}
func test_map() throws {
// Given
let targetA = Target.test(name: "A")
let targetB = Target.test(name: "B",
product: .unitTests,
dependencies: [.target(name: "A")])
let project = Project.test(targets: [targetA, targetB])
// When
let (got, sideEffects) = try subject.map(project: project)
// Then
XCTAssertEmpty(sideEffects)
XCTAssertEqual(got.schemes.count, 2)
// Then: A
let aScheme = got.schemes.first!
XCTAssertEqual(aScheme.name, targetA.name)
let aBuildAction = try XCTUnwrap(aScheme.buildAction)
XCTAssertEqual(aBuildAction.targets.count, 1)
let aBuildTargetReference = try XCTUnwrap(aBuildAction.targets.first)
XCTAssertEqual(aBuildTargetReference.projectPath, project.path)
XCTAssertEqual(aBuildTargetReference.name, targetA.name)
// Then: A tests
// Since B is a tests bundle with a dependency on A,
// there should be a test action in A scheme to run the tests in B
let aTestAction = try XCTUnwrap(aScheme.testAction)
XCTAssertEqual(aTestAction.targets.count, 1)
let aTestTargetReference = try XCTUnwrap(aTestAction.targets.first?.target)
XCTAssertEqual(aTestTargetReference.projectPath, project.path)
XCTAssertEqual(aTestTargetReference.name, targetB.name)
// Then: B
let bScheme = got.schemes.last!
XCTAssertEqual(bScheme.name, targetB.name)
let bBuildAction = try XCTUnwrap(bScheme.buildAction)
XCTAssertEqual(bBuildAction.targets.count, 1)
let bBuildTargetReference = try XCTUnwrap(bBuildAction.targets.first)
XCTAssertEqual(bBuildTargetReference.projectPath, project.path)
XCTAssertEqual(bBuildTargetReference.name, targetB.name)
let bTestAction = try XCTUnwrap(bScheme.testAction)
XCTAssertEqual(bTestAction.targets.count, 1)
let bTestTargetReference = try XCTUnwrap(bTestAction.targets.first?.target)
XCTAssertEqual(bTestTargetReference.projectPath, project.path)
XCTAssertEqual(bTestTargetReference.name, targetB.name)
}
func test_map_doesnt_override_user_schemes() throws {
// Given
let targetA = Target.test(name: "A")
let aScheme = Scheme.test(name: "A",
shared: true,
buildAction: nil,
testAction: nil,
runAction: nil,
archiveAction: nil,
profileAction: nil,
analyzeAction: nil)
let project = Project.test(targets: [targetA],
schemes: [aScheme])
// When
let (got, sideEffects) = try subject.map(project: project)
// Then
XCTAssertEmpty(sideEffects)
XCTAssertEqual(got.schemes.count, 1)
// Then: A
let gotAScheme = got.schemes.first!
XCTAssertNil(gotAScheme.buildAction)
}
}

View File

@ -345,11 +345,14 @@ final class MultipleConfigurationsIntegrationTests: TuistUnitTestCase {
private func createProject(path: AbsolutePath, settings: Settings, targets: [Target], packages: [Package] = [], schemes: [Scheme]) -> Project {
Project(path: path,
name: "App",
organizationName: nil,
fileName: nil,
settings: settings,
filesGroup: .group(name: "Project"),
targets: targets,
packages: packages,
schemes: schemes)
schemes: schemes,
additionalFiles: [])
}
private func createAppTarget(settings: Settings?) throws -> Target {

View File

@ -159,6 +159,8 @@ final class StableXcodeProjIntegrationTests: TuistTestCase {
private func createProject(path: AbsolutePath, settings: Settings, targets: [Target], packages: [Package] = [], schemes: [Scheme]) -> Project {
Project(path: path,
name: "App",
organizationName: nil,
fileName: nil,
settings: settings,
filesGroup: .group(name: "Project"),
targets: targets,

View File

@ -3,6 +3,7 @@ import TuistCache
import TuistCloud
import TuistCore
import TuistCoreTesting
import TuistGenerator
import TuistSupport
import XCTest

View File

@ -0,0 +1,37 @@
import Foundation
import TuistCache
import TuistCloud
import TuistCoreTesting
import TuistGenerator
import TuistSupport
import XCTest
@testable import TuistCore
@testable import TuistKit
@testable import TuistSupportTesting
final class ProjectMapperProviderTests: TuistUnitTestCase {
var subject: ProjectMapperProvider!
override func setUp() {
super.setUp()
subject = ProjectMapperProvider()
}
override func tearDown() {
subject = nil
super.tearDown()
}
func test_mapper_returns_a_sequential_mapper_with_the_autogenerated_schemes_project_mapper() throws {
// Given
subject = ProjectMapperProvider()
// When
let got = subject.mapper(config: Config.test(cloud: .test(options: [])))
// Then
let sequentialProjectMapper = try XCTUnwrap(got as? SequentialProjectMapper)
XCTAssertEqual(sequentialProjectMapper.mappers.filter { $0 is AutogeneratedSchemesProjectMapper }.count, 1)
}
}

View File

@ -268,30 +268,6 @@ class GeneratorModelLoaderTests: TuistUnitTestCase {
XCTAssertEqual(model.organizationName, "tuist")
}
func test_loadProject_withDisabledAutogeneratedSchemesFromConfig() throws {
// Given
let temporaryPath = try self.temporaryPath()
try createFiles([
"Config.swift",
])
let manifests = [
temporaryPath: ProjectManifest.test(),
]
let configs = [
temporaryPath: ProjectDescription.TuistConfig.test(generationOptions: [.disableAutogeneratedSchemes]),
]
let manifestLoader = createManifestLoader(with: manifests, configs: configs)
let subject = GeneratorModelLoader(manifestLoader: manifestLoader,
manifestLinter: manifestLinter)
// When
let model = try subject.loadProject(at: temporaryPath)
// Then
XCTAssertFalse(model.autogenerateSchemes)
}
func test_loadWorkspace() throws {
// Given
let temporaryPath = try self.temporaryPath()