ValueGraph BuildGraphInspector (#2527)

* Convert build service to use value graph.

* Migrate doc service to use value graph.

* Build migrate BuildGraphInspector.

* Edit CHANGELOG.

* Remove TargetNodeGraphMapper.
This commit is contained in:
Marek Fořt 2021-02-17 17:40:51 +01:00 committed by GitHub
parent 9420cf7569
commit 5c0d9efe01
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
14 changed files with 209 additions and 358 deletions

View File

@ -10,6 +10,7 @@ Please, check out guidelines: https://keepachangelog.com/en/1.0.0/
### Changed
- Migrate `BuildGraphInspector` to `ValueGraph` [#2527](https://github.com/tuist/tuist/pull/2527) by [@fortmarek](https://github.com/fortmarek/)
- Replace `ExpressibleByStringLiteral` with `ExpressibleByStringInterpolation` for `ProjectDescription` objects by [@DimaMishchenko](https://github.com/DimaMishchenko)
## 1.34.0 - Shipit

View File

@ -19,39 +19,31 @@ public protocol BuildGraphInspecting {
/// From the list of buildable targets of the given scheme, it returns the first one.
/// - Parameters:
/// - scheme: Scheme in which to look up the target.
/// - graph: Dependency graph.
func buildableTarget(scheme: Scheme, graph: Graph) -> (Project, Target)?
/// - graphTraverser: GraphTraversing traverser.
func buildableTarget(scheme: Scheme, graphTraverser: GraphTraversing) -> (Project, Target)?
/// From the list of testable targets of the given scheme, it returns the first one.
/// - Parameters:
/// - scheme: Scheme in which to look up the target.
/// - graph: Dependency graph.
func testableTarget(scheme: Scheme, graph: Graph) -> TargetNode?
func testableTarget(scheme: Scheme, graphTraverser: GraphTraversing) -> ValueGraphTarget?
/// Given a graph, it returns a list of buildable schemes.
/// - Parameter graph: Dependency graph.
func buildableSchemes(graph: Graph) -> [Scheme]
/// Given a graphTraverser, it returns a list of buildable schemes.
func buildableSchemes(graphTraverser: GraphTraversing) -> [Scheme]
/// Given a graph, it returns a list of buildable schemes that are part of the entry node
/// - Parameters:
/// - graph: Dependency graph
func buildableEntrySchemes(graph: Graph) -> [Scheme]
/// Given a graphTraverser, it returns a list of buildable schemes that are part of the entry node
func buildableEntrySchemes(graphTraverser: GraphTraversing) -> [Scheme]
/// Given a graph, it returns a list of test schemes (those that include only one test target).
/// - Parameter graph: Dependency graph.
func testSchemes(graph: Graph) -> [Scheme]
/// Given a graphTraverser, it returns a list of test schemes (those that include only one test target).
func testSchemes(graphTraverser: GraphTraversing) -> [Scheme]
/// Given a graph, it returns a list of testable schemes.
/// - Parameter graph: Dependency graph.
func testableSchemes(graph: Graph) -> [Scheme]
/// Given a graphTraverser, it returns a list of testable schemes.
func testableSchemes(graphTraverser: GraphTraversing) -> [Scheme]
/// Schemes generated by `AutogeneratedProjectSchemeWorkspaceMapper`
/// - Parameters:
/// - graph: Dependency graph
func projectSchemes(graph: Graph) -> [Scheme]
func projectSchemes(graphTraverser: GraphTraversing) -> [Scheme]
}
public class BuildGraphInspector: BuildGraphInspecting {
public final class BuildGraphInspector: BuildGraphInspecting {
public init() {}
public func buildArguments(project: Project, target: Target, configuration: String?, skipSigning: Bool) -> [XcodeBuildArgument] {
@ -84,7 +76,7 @@ public class BuildGraphInspector: BuildGraphInspecting {
return arguments
}
public func buildableTarget(scheme: Scheme, graph: Graph) -> (Project, Target)? {
public func buildableTarget(scheme: Scheme, graphTraverser: GraphTraversing) -> (Project, Target)? {
guard
scheme.buildAction?.targets.isEmpty == false,
let buildTarget = scheme.buildAction?.targets.first
@ -92,50 +84,52 @@ public class BuildGraphInspector: BuildGraphInspecting {
return nil
}
return graph.target(path: buildTarget.projectPath, name: buildTarget.name).map { ($0.project, $0.target) }
return graphTraverser.target(
path: buildTarget.projectPath,
name: buildTarget.name
)
.map { ($0.project, $0.target) }
}
public func testableTarget(scheme: Scheme, graph: Graph) -> TargetNode? {
public func testableTarget(scheme: Scheme, graphTraverser: GraphTraversing) -> ValueGraphTarget? {
guard let testTarget = scheme.testAction?.targets.first else { return nil }
return graph.target(path: testTarget.target.projectPath, name: testTarget.target.name)
return graphTraverser.target(path: testTarget.target.projectPath, name: testTarget.target.name)
}
public func buildableSchemes(graph: Graph) -> [Scheme] {
graph.schemes
public func buildableSchemes(graphTraverser: GraphTraversing) -> [Scheme] {
graphTraverser.schemes()
.filter { $0.buildAction?.targets.isEmpty == false }
.sorted(by: { $0.name < $1.name })
}
public func buildableEntrySchemes(graph: Graph) -> [Scheme] {
let projects = Set(graph.entryNodes.compactMap { ($0 as? TargetNode)?.project })
public func buildableEntrySchemes(graphTraverser: GraphTraversing) -> [Scheme] {
let projects = Set(graphTraverser.rootTargets().map(\.project))
return projects
.flatMap(\.schemes)
.filter { $0.buildAction?.targets.isEmpty == false }
.sorted(by: { $0.name < $1.name })
}
public func testableSchemes(graph: Graph) -> [Scheme] {
graph.schemes
public func testableSchemes(graphTraverser: GraphTraversing) -> [Scheme] {
graphTraverser.schemes()
.filter { $0.testAction?.targets.isEmpty == false }
.sorted(by: { $0.name < $1.name })
}
public func testSchemes(graph: Graph) -> [Scheme] {
graph.targets.values.flatMap { target -> [Scheme] in
target
.filter { $0.target.product == .unitTests || $0.target.product == .uiTests }
.flatMap { target -> [Scheme] in
target.project.schemes
.filter { $0.targetDependencies().map(\.name) == [target.name] }
}
}
.filter { $0.testAction?.targets.isEmpty == false }
.sorted(by: { $0.name < $1.name })
public func testSchemes(graphTraverser: GraphTraversing) -> [Scheme] {
graphTraverser.allTargets()
.filter { $0.target.product == .unitTests || $0.target.product == .uiTests }
.flatMap { target -> [Scheme] in
target.project.schemes
.filter { $0.targetDependencies().map(\.name) == [target.target.name] }
}
.filter { $0.testAction?.targets.isEmpty == false }
.sorted(by: { $0.name < $1.name })
}
public func projectSchemes(graph: Graph) -> [Scheme] {
graph.workspace.schemes
.filter { $0.name.contains("\(graph.workspace.name)-Project") }
public func projectSchemes(graphTraverser: GraphTraversing) -> [Scheme] {
graphTraverser.workspace.schemes
.filter { $0.name.contains("\(graphTraverser.workspace.name)-Project") }
.sorted(by: { $0.name < $1.name })
}

View File

@ -13,27 +13,27 @@ public final class MockBuildGraphInspector: BuildGraphInspecting {
workspacePathStub?(directory) ?? directory
}
public var buildableTargetStub: ((Scheme, Graph) -> (Project, Target)?)?
public func buildableTarget(scheme: Scheme, graph: Graph) -> (Project, Target)? {
public var buildableTargetStub: ((Scheme, GraphTraversing) -> (Project, Target)?)?
public func buildableTarget(scheme: Scheme, graphTraverser: GraphTraversing) -> (Project, Target)? {
if let buildableTargetStub = buildableTargetStub {
return buildableTargetStub(scheme, graph)
return buildableTargetStub(scheme, graphTraverser)
} else {
return (Project.test(), Target.test())
}
}
public var buildableSchemesStub: ((Graph) -> [Scheme])?
public func buildableSchemes(graph: Graph) -> [Scheme] {
public var buildableSchemesStub: ((GraphTraversing) -> [Scheme])?
public func buildableSchemes(graphTraverser: GraphTraversing) -> [Scheme] {
if let buildableSchemesStub = buildableSchemesStub {
return buildableSchemesStub(graph)
return buildableSchemesStub(graphTraverser)
} else {
return []
}
}
public var buildableEntrySchemesStub: ((Graph) -> [Scheme])?
public func buildableEntrySchemes(graph: Graph) -> [Scheme] {
buildableEntrySchemesStub?(graph) ?? []
public var buildableEntrySchemesStub: ((GraphTraversing) -> [Scheme])?
public func buildableEntrySchemes(graphTraverser: GraphTraversing) -> [Scheme] {
buildableEntrySchemesStub?(graphTraverser) ?? []
}
public var buildArgumentsStub: ((Project, Target, String?, Bool) -> [XcodeBuildArgument])?
@ -45,31 +45,31 @@ public final class MockBuildGraphInspector: BuildGraphInspecting {
}
}
public var testableTargetStub: ((Scheme, Graph) -> TargetNode?)?
public func testableTarget(scheme: Scheme, graph: Graph) -> TargetNode? {
public var testableTargetStub: ((Scheme, GraphTraversing) -> ValueGraphTarget?)?
public func testableTarget(scheme: Scheme, graphTraverser: GraphTraversing) -> ValueGraphTarget? {
if let testableTargetStub = testableTargetStub {
return testableTargetStub(scheme, graph)
return testableTargetStub(scheme, graphTraverser)
} else {
return TargetNode.test()
return ValueGraphTarget.test()
}
}
public var testableSchemesStub: ((Graph) -> [Scheme])?
public func testableSchemes(graph: Graph) -> [Scheme] {
public var testableSchemesStub: ((GraphTraversing) -> [Scheme])?
public func testableSchemes(graphTraverser: GraphTraversing) -> [Scheme] {
if let testableSchemesStub = testableSchemesStub {
return testableSchemesStub(graph)
return testableSchemesStub(graphTraverser)
} else {
return []
}
}
public var testSchemesStub: ((Graph) -> [Scheme])?
public func testSchemes(graph: Graph) -> [Scheme] {
testSchemesStub?(graph) ?? []
public var testSchemesStub: ((GraphTraversing) -> [Scheme])?
public func testSchemes(graphTraverser: GraphTraversing) -> [Scheme] {
testSchemesStub?(graphTraverser) ?? []
}
public var projectSchemesStub: ((Graph) -> [Scheme])?
public func projectSchemes(graph: Graph) -> [Scheme] {
projectSchemesStub?(graph) ?? []
public var projectSchemesStub: ((GraphTraversing) -> [Scheme])?
public func projectSchemes(graphTraverser: GraphTraversing) -> [Scheme] {
projectSchemesStub?(graphTraverser) ?? []
}
}

View File

@ -1,98 +0,0 @@
import Foundation
import TSCBasic
/// Target Node Graph Mapper
///
/// A `GraphMapper` that allows transforming `TargetNode`s without modifying
/// the original nodes. Additionally, any projects associated with orphaned nodes after
/// any transformations will no longer be part of the resulting graph.
///
/// - Note: When mapping, the `transform` block receives a copy of the original `TargetNode`
public class TargetNodeGraphMapper: GraphMapping {
public let mapTargetNode: (TargetNode) -> TargetNode
public init(transform: @escaping (TargetNode) -> TargetNode) {
mapTargetNode = transform
}
// MARK: - GraphMapping
public func map(graph: Graph) -> (Graph, [SideEffectDescriptor]) {
var mappedCache = [GraphNodeMapKey: GraphNode]()
let cache = GraphLoaderCache()
let updatedNodes = graph.entryNodes.map {
map(node: $0, mappedCache: &mappedCache, cache: cache)
}
return (
Graph(
name: graph.name,
entryPath: graph.entryPath,
cache: cache,
entryNodes: updatedNodes,
workspace: graph.workspace
),
[]
)
}
// MARK: - Private
private func map(node: GraphNode,
mappedCache: inout [GraphNodeMapKey: GraphNode],
cache: GraphLoaderCache) -> GraphNode
{
if let cached = mappedCache[node.mapperCacheKey] {
return cached
}
switch node {
case let packageProductNode as PackageProductNode:
cache.add(package: packageProductNode)
return packageProductNode
case let precompiledNode as PrecompiledNode:
cache.add(precompiledNode: precompiledNode)
return precompiledNode
case let cocoapodsNode as CocoaPodsNode:
cache.add(cocoapods: cocoapodsNode)
return cocoapodsNode
case let targetNode as TargetNode:
let updated = map(targetNode: targetNode,
mappedCache: &mappedCache,
cache: cache)
cache.add(targetNode: updated)
cache.add(project: updated.project)
return updated
default:
fatalError("Unhandled graph node type")
}
}
private func map(targetNode: TargetNode,
mappedCache: inout [GraphNodeMapKey: GraphNode],
cache: GraphLoaderCache) -> TargetNode
{
var updated = TargetNode(project: targetNode.project,
target: targetNode.target,
dependencies: targetNode.dependencies)
updated = mapTargetNode(updated)
mappedCache[updated.mapperCacheKey] = updated
updated.dependencies = updated.dependencies.map {
map(node: $0, mappedCache: &mappedCache, cache: cache)
}
return updated
}
}
private struct GraphNodeMapKey: Hashable {
var name: String
var path: AbsolutePath
}
private extension GraphNode {
var mapperCacheKey: GraphNodeMapKey {
.init(name: name, path: path)
}
}

View File

@ -30,6 +30,9 @@ public protocol GraphTraversing {
/// Returns all the apps from the graph.
func apps() -> Set<ValueGraphTarget>
/// - Returns: All the schemes of the graph
func schemes() -> [Scheme]
/// Returns the targets from the project that lives in the directory from which the graph has been loaded.
func rootTargets() -> Set<ValueGraphTarget>
@ -150,6 +153,12 @@ public protocol GraphTraversing {
/// the groups.
/// - Parameter path: Path to the directory where the project is defined.
func allProjectDependencies(path: AbsolutePath) throws -> Set<GraphDependencyReference>
/// Returns true if the given target depends on XCTest.
/// - Parameters:
/// - path: Path to the project tha defines the target.
/// - name: Target name.
func dependsOnXCTest(path: AbsolutePath, name: String) -> Bool
}
public extension GraphTraversing {

View File

@ -49,6 +49,10 @@ public class ValueGraphTraverser: GraphTraversing {
})
}
public func schemes() -> [Scheme] {
projects.values.flatMap(\.schemes) + graph.workspace.schemes
}
public func cocoapodsPaths() -> Set<AbsolutePath> {
dependencies.reduce(into: Set<AbsolutePath>()) { acc, next in
let fromDependency = next.key
@ -396,6 +400,11 @@ public class ValueGraphTraverser: GraphTraversing {
return references
}
public func dependsOnXCTest(path: AbsolutePath, name: String) -> Bool {
directTargetDependencies(path: path, name: name)
.first(where: { $0.target.name == "XCTest" || $0.target.product.testsBundle }) != nil
}
// MARK: - Internal
/// The method collects the dependencies that are selected by the provided test closure.

View File

@ -403,4 +403,23 @@ final class MockGraphTraverser: GraphTraversing {
}
return stubbedAllProjectDependenciesResult
}
var invokedDependsOnXCTest = false
var invokedDependsOnXCTestCount = 0
var invokedDependsOnXCTestParameters: (path: AbsolutePath, name: String)?
var invokedDependsOnXCTestParametersList = [(path: AbsolutePath, name: String)]()
var stubbedDependsOnXCTestResult: Bool! = false
func dependsOnXCTest(path: AbsolutePath, name: String) -> Bool {
invokedDependsOnXCTest = true
invokedDependsOnXCTestCount += 1
invokedDependsOnXCTestParameters = (path, name)
invokedDependsOnXCTestParametersList.append((path, name))
return stubbedDependsOnXCTestResult
}
var schemesStub: (() -> [Scheme])?
func schemes() -> [Scheme] {
schemesStub?() ?? []
}
}

View File

@ -58,14 +58,15 @@ final class BuildService {
configuration: String?,
path: AbsolutePath
) throws {
let graph: Graph
let graph: ValueGraph
if try (generate || buildGraphInspector.workspacePath(directory: path) == nil) {
graph = try generator.generateWithGraph(path: path, projectOnly: false).1
graph = ValueGraph(graph: try generator.generateWithGraph(path: path, projectOnly: false).1)
} else {
graph = try generator.load(path: path)
graph = ValueGraph(graph: try generator.load(path: path))
}
let graphTraverser = ValueGraphTraverser(graph: graph)
let buildableSchemes = buildGraphInspector.buildableSchemes(graph: graph)
let buildableSchemes = buildGraphInspector.buildableSchemes(graphTraverser: graphTraverser)
logger.log(level: .debug, "Found the following buildable schemes: \(buildableSchemes.map(\.name).joined(separator: ", "))")
@ -73,13 +74,13 @@ final class BuildService {
guard let scheme = buildableSchemes.first(where: { $0.name == schemeName }) else {
throw BuildServiceError.schemeNotFound(scheme: schemeName, existing: buildableSchemes.map(\.name))
}
try buildScheme(scheme: scheme, graph: graph, path: path, clean: clean, configuration: configuration)
try buildScheme(scheme: scheme, graphTraverser: graphTraverser, path: path, clean: clean, configuration: configuration)
} else {
var cleaned: Bool = false
// Run only buildable entry schemes when specific schemes has not been passed
let buildableEntrySchemes = buildGraphInspector.buildableEntrySchemes(graph: graph)
let buildableEntrySchemes = buildGraphInspector.buildableEntrySchemes(graphTraverser: graphTraverser)
try buildableEntrySchemes.forEach {
try buildScheme(scheme: $0, graph: graph, path: path, clean: !cleaned && clean, configuration: configuration)
try buildScheme(scheme: $0, graphTraverser: graphTraverser, path: path, clean: !cleaned && clean, configuration: configuration)
cleaned = true
}
}
@ -89,9 +90,9 @@ final class BuildService {
// MARK: - private
private func buildScheme(scheme: Scheme, graph: Graph, path: AbsolutePath, clean: Bool, configuration: String?) throws {
private func buildScheme(scheme: Scheme, graphTraverser: GraphTraversing, path: AbsolutePath, clean: Bool, configuration: String?) throws {
logger.log(level: .notice, "Building scheme \(scheme.name)", metadata: .section)
guard let (project, target) = buildGraphInspector.buildableTarget(scheme: scheme, graph: graph) else {
guard let (project, target) = buildGraphInspector.buildableTarget(scheme: scheme, graphTraverser: graphTraverser) else {
throw BuildServiceError.schemeWithoutBuildableTargets(scheme: scheme.name)
}
let workspacePath = try buildGraphInspector.workspacePath(directory: path)!

View File

@ -5,6 +5,7 @@ import TSCBasic
import TuistCache
import TuistCore
import TuistDoc
import TuistGraph
import TuistSupport
// MARK: - DocServiceError
@ -76,11 +77,11 @@ final class DocService {
}
func run(project path: AbsolutePath, target targetName: String) throws {
let graph = try generator.load(path: path)
let graph = ValueGraph(graph: try generator.load(path: path))
let graphTraverser = ValueGraphTraverser(graph: graph)
let targets = graph.targets
.flatMap(\.value)
.filter { !$0.dependsOnXCTest }
let targets = graphTraverser.allTargets()
.filter { !graphTraverser.dependsOnXCTest(path: $0.path, name: $0.target.name) }
.map(\.target)
guard let target = targets.first(where: { $0.name == targetName }) else {

View File

@ -83,13 +83,16 @@ final class TestService {
testsCacheDirectory: testsCacheTemporaryDirectory.path
)
logger.notice("Generating project for testing", metadata: .section)
let graph: Graph = try generator.generateWithGraph(
path: path,
projectOnly: false
).1
let graph = ValueGraph(
graph: try generator.generateWithGraph(
path: path,
projectOnly: false
).1
)
let graphTraverser = ValueGraphTraverser(graph: graph)
let version = osVersion?.version()
let testableSchemes = buildGraphInspector.testableSchemes(graph: graph) + buildGraphInspector.projectSchemes(graph: graph)
let testableSchemes = buildGraphInspector.testableSchemes(graphTraverser: graphTraverser) + buildGraphInspector.projectSchemes(graphTraverser: graphTraverser)
logger.log(
level: .debug,
"Found the following testable schemes: \(Set(testableSchemes.map(\.name)).joined(separator: ", "))"
@ -115,8 +118,7 @@ final class TestService {
try testSchemes.forEach { testScheme in
try self.testScheme(
scheme: testScheme,
graph: graph,
path: path,
graphTraverser: graphTraverser,
clean: clean,
configuration: configuration,
version: version,
@ -124,7 +126,7 @@ final class TestService {
)
}
} else {
let testSchemes: [Scheme] = buildGraphInspector.projectSchemes(graph: graph)
let testSchemes: [Scheme] = buildGraphInspector.projectSchemes(graphTraverser: graphTraverser)
.filter {
$0.testAction.map { !$0.targets.isEmpty } ?? false
}
@ -137,8 +139,7 @@ final class TestService {
try testSchemes.forEach {
try testScheme(
scheme: $0,
graph: graph,
path: path,
graphTraverser: graphTraverser,
clean: clean,
configuration: configuration,
version: version,
@ -172,28 +173,27 @@ final class TestService {
private func testScheme(
scheme: Scheme,
graph: Graph,
path _: AbsolutePath,
graphTraverser: GraphTraversing,
clean: Bool,
configuration: String?,
version: Version?,
deviceName: String?
) throws {
logger.log(level: .notice, "Testing scheme \(scheme.name)", metadata: .section)
guard let buildableTarget = buildGraphInspector.testableTarget(scheme: scheme, graph: graph) else {
guard let buildableTarget = buildGraphInspector.testableTarget(scheme: scheme, graphTraverser: graphTraverser) else {
throw TestServiceError.schemeWithoutTestableTargets(scheme: scheme.name)
}
let destination = try findDestination(
target: buildableTarget.target,
scheme: scheme,
graph: graph,
graphTraverser: graphTraverser,
version: version,
deviceName: deviceName
)
_ = try xcodebuildController.test(
.workspace(graph.workspace.xcWorkspacePath),
.workspace(graphTraverser.workspace.xcWorkspacePath),
scheme: scheme.name,
clean: clean,
destination: destination,
@ -213,7 +213,7 @@ final class TestService {
private func findDestination(
target: Target,
scheme: Scheme,
graph: Graph,
graphTraverser: GraphTraversing,
version: Version?,
deviceName: String?
) throws -> XcodeBuildDestination {
@ -224,12 +224,13 @@ final class TestService {
minVersion = deploymentTarget.version.version()
} else {
minVersion = scheme.targetDependencies()
.compactMap { graph.findTargetNode(path: $0.projectPath, name: $0.name) }
.flatMap {
$0.targetDependencies
.compactMap { $0.target.deploymentTarget?.version }
graphTraverser
.directTargetDependencies(path: $0.projectPath, name: $0.name)
.map(\.target)
.map(\.deploymentTarget)
.compactMap { $0?.version.version() }
}
.compactMap { $0.version() }
.sorted()
.first
}

View File

@ -138,12 +138,14 @@ final class BuildGraphInspectorTests: TuistUnitTestCase {
let scheme = Scheme.test(buildAction: .test(targets: [.init(projectPath: projectPath, name: "Core")]))
let target = Target.test(name: "Core")
let project = Project.test(path: projectPath)
let targetNode = TargetNode.test(project: project,
target: target)
let graph = Graph.test(targets: [projectPath: [targetNode]])
let graph = ValueGraph.test(
projects: [projectPath: project],
targets: [projectPath: [target.name: target]]
)
let graphTraverser = ValueGraphTraverser(graph: graph)
// When
let got = subject.buildableTarget(scheme: scheme, graph: graph)
let got = subject.buildableTarget(scheme: scheme, graphTraverser: graphTraverser)
// Then
XCTAssertEqual(got?.0, project)
@ -169,16 +171,17 @@ final class BuildGraphInspectorTests: TuistUnitTestCase {
)
)
let workspace = Workspace.test(schemes: [workspaceScheme])
let graph = Graph.test(
let graph = ValueGraph.test(
workspace: workspace,
projects: [
coreProject,
kitProject,
coreProject.path: coreProject,
kitProject.path: kitProject,
]
)
let graphTraverser = ValueGraphTraverser(graph: graph)
// When
let got = subject.buildableSchemes(graph: graph)
let got = subject.buildableSchemes(graphTraverser: graphTraverser)
// Then
XCTAssertEqual(
@ -225,47 +228,57 @@ final class BuildGraphInspectorTests: TuistUnitTestCase {
path: coreProjectPath,
schemes: [coreScheme, coreTestsScheme]
)
let coreTargetNode = TargetNode.test(
project: coreProject,
target: coreTarget
let coreValueGraphTarget = ValueGraphTarget.test(
target: coreTarget,
project: coreProject
)
let coreTestsTarget = Target.test(
name: "CoreTests",
product: .unitTests,
dependencies: [.target(name: "Core")]
)
let coreTestsTargetNode = TargetNode.test(
project: coreProject,
target: coreTestsTarget
let coreTestsValueGraphTarget = ValueGraphTarget.test(
target: coreTestsTarget,
project: coreProject
)
let kitTarget = Target.test(name: "Kit", dependencies: [.target(name: "Core")])
let kitProject = Project.test(
path: projectPath,
schemes: [kitScheme, kitTestsScheme]
)
let kitTargetNode = TargetNode.test(
project: kitProject,
target: kitTarget
let kitValueGraphTarget = ValueGraphTarget.test(
target: kitTarget,
project: kitProject
)
let kitTestsTarget = Target.test(
name: "KitTests",
product: .unitTests,
dependencies: [.target(name: "Kit")]
)
let kitTestsTargetNode = TargetNode.test(
project: kitProject,
target: kitTestsTarget
let kitTestsValueGraphTarget = ValueGraphTarget.test(
target: kitTestsTarget,
project: kitProject
)
let graph = Graph.test(
entryNodes: [kitTargetNode],
let graph = ValueGraph.test(
projects: [
kitProject.path: kitProject,
coreProject.path: coreProject,
],
targets: [
projectPath: [kitTargetNode, kitTestsTargetNode],
coreProjectPath: [coreTargetNode, coreTestsTargetNode],
projectPath: [
kitValueGraphTarget.target.name: kitValueGraphTarget.target,
kitTestsValueGraphTarget.target.name: kitTestsValueGraphTarget.target,
],
coreProjectPath: [
coreValueGraphTarget.target.name: coreValueGraphTarget.target,
coreTestsValueGraphTarget.target.name: coreTestsValueGraphTarget.target,
],
]
)
let graphTraverser = ValueGraphTraverser(graph: graph)
// When
let got = subject.testSchemes(graph: graph)
let got = subject.testSchemes(graphTraverser: graphTraverser)
// Then
XCTAssertEqual(
@ -299,19 +312,24 @@ final class BuildGraphInspectorTests: TuistUnitTestCase {
path: coreProjectPath,
schemes: [coreScheme, coreTestsScheme]
)
let coreTargetNode = TargetNode.test(
project: coreProject,
target: coreTarget
let coreValueGraphTarget = ValueGraphTarget.test(
target: coreTarget,
project: coreProject
)
let graph = Graph.test(
entryNodes: [coreTargetNode],
let graph = ValueGraph.test(
projects: [
coreProject,
coreProject.path: coreProject,
],
targets: [
coreProject.path: [
coreValueGraphTarget.target.name: coreValueGraphTarget.target,
],
]
)
let graphTraverser = ValueGraphTraverser(graph: graph)
// When
let got = subject.testableSchemes(graph: graph)
let got = subject.testableSchemes(graphTraverser: graphTraverser)
// Then
XCTAssertEqual(
@ -331,21 +349,24 @@ final class BuildGraphInspectorTests: TuistUnitTestCase {
let schemeA = Scheme.test(buildAction: .test(targets: [.init(projectPath: projectAPath, name: "A")]))
let projectA = Project.test(path: projectAPath, schemes: [schemeA])
let targetA = Target.test(name: "A")
let targetANode = TargetNode.test(project: projectA,
target: targetA)
let projectBPath = path.appending(component: "ProjectB.xcodeproj")
let schemeB = Scheme.test(buildAction: .test(targets: [.init(projectPath: projectBPath, name: "B")]))
let projectB = Project.test(path: projectBPath, schemes: [schemeB])
let targetB = Target.test(name: "B")
let targetBNode = TargetNode.test(project: projectB,
target: targetB)
let graph = Graph.test(entryNodes: [targetANode],
targets: [projectAPath: [targetANode], projectBPath: [targetBNode]])
let graph = ValueGraph.test(
workspace: Workspace.test(projects: [projectA.path]),
projects: [
projectA.path: projectA,
projectB.path: projectB,
],
targets: [projectAPath: [targetA.name: targetA], projectBPath: [targetB.name: targetB]]
)
let graphTraverser = ValueGraphTraverser(graph: graph)
// When
let got = subject.buildableEntrySchemes(graph: graph)
let got = subject.buildableEntrySchemes(graphTraverser: graphTraverser)
// Then
XCTAssertEqual(got, [schemeA])
@ -396,7 +417,7 @@ final class BuildGraphInspectorTests: TuistUnitTestCase {
func test_projectSchemes_when_multiple_platforms() {
// Given
let graph: Graph = .test(
let graph: ValueGraph = .test(
workspace: .test(
name: "WorkspaceName",
schemes: [
@ -406,9 +427,10 @@ final class BuildGraphInspectorTests: TuistUnitTestCase {
]
)
)
let graphTraverser = ValueGraphTraverser(graph: graph)
// When
let got = subject.projectSchemes(graph: graph)
let got = subject.projectSchemes(graphTraverser: graphTraverser)
// Then
XCTAssertEqual(
@ -422,7 +444,7 @@ final class BuildGraphInspectorTests: TuistUnitTestCase {
func test_projectSchemes_when_single_platform() {
// Given
let graph: Graph = .test(
let graph: ValueGraph = .test(
workspace: .test(
name: "WorkspaceName",
schemes: [
@ -431,9 +453,10 @@ final class BuildGraphInspectorTests: TuistUnitTestCase {
]
)
)
let graphTraverser = ValueGraphTraverser(graph: graph)
// When
let got = subject.projectSchemes(graph: graph)
let got = subject.projectSchemes(graphTraverser: graphTraverser)
// Then
XCTAssertEqual(

View File

@ -1,114 +0,0 @@
import TuistCore
import TuistGraph
import XCTest
@testable import TuistCoreTesting
class TargetNodeGraphMapperTests: XCTestCase {
func test_map() {
// Given
let subject = TargetNodeGraphMapper { targetNode in
TargetNode(project: targetNode.project, target: targetNode.target, dependencies: [])
}
let targetA = Target.test(name: "TargetA")
let targetB = Target.test(name: "TargetB")
let targetC = Target.test(name: "TargetC")
let project = Project.test(targets: [targetA, targetB, targetC])
let graph = Graph.create(project: project,
dependencies: [
(target: targetA, dependencies: [targetB]),
(target: targetB, dependencies: [targetC]),
(target: targetC, dependencies: []),
])
// When
let (results, _) = subject.map(graph: graph)
// Then
XCTAssertEqual(results.targets(at: project.path).count, 3)
XCTAssertEqual(results.target(path: project.path, name: "TargetA")?.dependencies.map(\.name), [])
XCTAssertEqual(results.target(path: project.path, name: "TargetB")?.dependencies.map(\.name), [])
XCTAssertEqual(results.target(path: project.path, name: "TargetC")?.dependencies.map(\.name), [])
}
func test_map_doesNotUpdateOriginal() {
// Given
let subject = TargetNodeGraphMapper { targetNode in
TargetNode(project: targetNode.project, target: targetNode.target, dependencies: [])
}
let targetA = Target.test(name: "TargetA")
let targetB = Target.test(name: "TargetB")
let targetC = Target.test(name: "TargetC")
let project = Project.test(targets: [targetA, targetB, targetC])
let graph = Graph.create(project: project,
dependencies: [
(target: targetA, dependencies: [targetB]),
(target: targetB, dependencies: [targetC]),
(target: targetC, dependencies: []),
])
// When
_ = subject.map(graph: graph)
// Then
XCTAssertEqual(graph.targets(at: project.path).count, 3)
XCTAssertEqual(graph.target(path: project.path, name: "TargetA")?.dependencies.map(\.name), ["TargetB"])
XCTAssertEqual(graph.target(path: project.path, name: "TargetB")?.dependencies.map(\.name), ["TargetC"])
XCTAssertEqual(graph.target(path: project.path, name: "TargetC")?.dependencies.map(\.name), [])
}
func test_map_removesOrphanedNodes() {
// Given
let subject = TargetNodeGraphMapper { targetNode in
TargetNode(project: targetNode.project, target: targetNode.target, dependencies: [])
}
let targetA = Target.test(name: "TargetA")
let targetB = Target.test(name: "TargetB")
let targetC = Target.test(name: "TargetC")
let projectA = Project.test(path: "/test/ProjctA", targets: [targetA])
let projectB = Project.test(path: "/test/ProjctB", targets: [targetB, targetC])
let graph = Graph.create(projects: [projectA, projectB],
entryNodes: [targetA],
dependencies: [
(project: projectA, target: targetA, dependencies: [targetB]),
(project: projectB, target: targetB, dependencies: [targetC]),
(project: projectB, target: targetC, dependencies: []),
])
// When
let (results, _) = subject.map(graph: graph)
// Then
XCTAssertEqual(results.targets.flatMap(\.value).count, 1)
XCTAssertEqual(results.projects.count, 1)
}
func test_map_postMapOrphanedNodesdoNotUpdateOriginal() {
// Given
let subject = TargetNodeGraphMapper { targetNode in
TargetNode(project: targetNode.project, target: targetNode.target, dependencies: [])
}
let targetA = Target.test(name: "TargetA")
let targetB = Target.test(name: "TargetB")
let targetC = Target.test(name: "TargetC")
let projectA = Project.test(path: "/test/ProjctA", targets: [targetA])
let projectB = Project.test(path: "/test/ProjctB", targets: [targetB, targetC])
let graph = Graph.create(projects: [projectA, projectB],
entryNodes: [targetA],
dependencies: [
(project: projectA, target: targetA, dependencies: [targetB]),
(project: projectB, target: targetB, dependencies: [targetC]),
(project: projectB, target: targetC, dependencies: []),
])
// When
_ = subject.map(graph: graph)
// Then
XCTAssertEqual(graph.targets.flatMap(\.value).count, 3)
XCTAssertEqual(graph.projects.count, 2)
}
}

View File

@ -114,10 +114,15 @@ final class TuistDocServiceTests: TuistUnitTestCase {
}
private func mockGraph(targetName: String, atPath path: AbsolutePath) {
let project = Project.test()
let project = Project.test(
path: path
)
let target = Target.test(name: targetName)
let targetNode = TargetNode(project: project, target: target, dependencies: [])
let graph = Graph.test(targets: [path: [targetNode]])
let graph = Graph.test(
projects: [project],
targets: [path: [targetNode]]
)
generator.loadStub = { _ in
graph

View File

@ -85,7 +85,7 @@ final class TestServiceTests: TuistUnitTestCase {
]
}
buildGraphInspector.testableTargetStub = { scheme, _ in
TargetNode.test(
ValueGraphTarget.test(
target: Target.test(
name: scheme.name
)