Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Adds codeCoverageTargets to TestAction #619

Merged
merged 5 commits into from Nov 4, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Expand Up @@ -11,6 +11,7 @@ Please, check out guidelines: https://keepachangelog.com/en/1.0.0/
- **Breaking** Allow specifying a deployment target within project manifests https://github.com/tuist/tuist/pull/541 by @mollyIV
- Add support for sticker pack extension & app extension products https://github.com/tuist/tuist/pull/489 by @Rag0n
- Utility to locate the root directory of a project https://github.com/tuist/tuist/pull/622/checks?check_run_id=284958709 by @pepibumur.
- Adds `codeCoverageTargets` to `TestAction` to make XCode gather coverage info only for that targets https://github.com/tuist/tuist/pull/619/checks?check_run_id=282561179 by @abbasmousavi

### Changed

Expand Down
5 changes: 5 additions & 0 deletions Sources/ProjectDescription/Scheme.swift
Expand Up @@ -72,13 +72,15 @@ public struct TestAction: Equatable, Codable {
public let arguments: Arguments?
public let configurationName: String
public let coverage: Bool
public let codeCoverageTargets: [String]
public let preActions: [ExecutionAction]
public let postActions: [ExecutionAction]

public init(targets: [String],
arguments: Arguments? = nil,
configurationName: String,
coverage: Bool = false,
codeCoverageTargets: [String] = [],
preActions: [ExecutionAction] = [],
postActions: [ExecutionAction] = []) {
self.targets = targets
Expand All @@ -87,18 +89,21 @@ public struct TestAction: Equatable, Codable {
self.coverage = coverage
self.preActions = preActions
self.postActions = postActions
self.codeCoverageTargets = codeCoverageTargets
}

public init(targets: [String],
arguments: Arguments? = nil,
config: PresetBuildConfiguration = .debug,
coverage: Bool = false,
codeCoverageTargets: [String] = [],
preActions: [ExecutionAction] = [],
postActions: [ExecutionAction] = []) {
self.init(targets: targets,
arguments: arguments,
configurationName: config.name,
coverage: coverage,
codeCoverageTargets: codeCoverageTargets,
preActions: preActions,
postActions: postActions)
}
Expand Down
31 changes: 31 additions & 0 deletions Sources/TuistGenerator/Generator/SchemesGenerator.swift
Expand Up @@ -145,6 +145,31 @@ final class SchemesGenerator: SchemesGenerating {
macroExpansion: nil,
testables: testables)
}

/// Generates the array of BuildableReference for targets that the
/// coverage report should be generated for them.
///
/// - Parameters:
/// - testAction: test actions.
/// - project: Project manifest.
/// - generatedProject: Generated Xcode project.
/// - Returns: Array of buildable references.
private func testCoverageTargetReferences(testAction: TestAction, project: Project, generatedProject: GeneratedProject) -> [XCScheme.BuildableReference] {

var codeCoverageTargets: [XCScheme.BuildableReference] = []
testAction.codeCoverageTargets.forEach { name in

guard let target = project.targets.first(where: { $0.name == name }) else { return }
guard let pbxTarget = generatedProject.targets[name] else { return }

let reference = self.targetBuildableReference(target: target,
pbxTarget: pbxTarget,
projectName: generatedProject.name)
codeCoverageTargets.append(reference)
}

return codeCoverageTargets
}

/// Generates the scheme test action.
///
Expand Down Expand Up @@ -189,6 +214,10 @@ final class SchemesGenerator: SchemesGenerating {
args = XCScheme.CommandLineArguments(arguments: commandlineArgruments(arguments.launch))
environments = environmentVariables(arguments.environment)
}

let codeCoverageTargets = self.testCoverageTargetReferences(testAction: testAction, project: project, generatedProject: generatedProject)

let onlyGenerateCoverageForSpecifiedTargets = codeCoverageTargets.count > 0 ? true : nil

let shouldUseLaunchSchemeArgsEnv: Bool = args == nil && environments == nil

Expand All @@ -199,6 +228,8 @@ final class SchemesGenerator: SchemesGenerating {
postActions: postActions,
shouldUseLaunchSchemeArgsEnv: shouldUseLaunchSchemeArgsEnv,
codeCoverageEnabled: testAction.coverage,
codeCoverageTargets: codeCoverageTargets,
onlyGenerateCoverageForSpecifiedTargets: onlyGenerateCoverageForSpecifiedTargets,
commandlineArguments: args,
environmentVariables: environments)
}
Expand Down
32 changes: 32 additions & 0 deletions Sources/TuistGenerator/Linter/SchemeLinter.swift
Expand Up @@ -9,6 +9,7 @@ class SchemeLinter: SchemeLinting {
func lint(project: Project) -> [LintingIssue] {
var issues = [LintingIssue]()
issues.append(contentsOf: lintReferencedBuildConfigurations(schemes: project.schemes, settings: project.settings))
issues.append(contentsOf: lintCodeCoverageTargets(schemes: project.schemes, targets: project.targets))
return issues
}
}
Expand All @@ -32,6 +33,15 @@ private extension SchemeLinter {
}
}

if let testAction = scheme.testAction {
if !buildConfigurationNames.contains(testAction.configurationName) {
issues.append(
missingBuildConfigurationIssue(buildConfigurationName: testAction.configurationName,
actionDescription: "the scheme's test action")
)
}
}

if let testAction = scheme.testAction {
if !buildConfigurationNames.contains(testAction.configurationName) {
issues.append(
Expand All @@ -48,4 +58,26 @@ private extension SchemeLinter {
let reason = "The build configuration '\(buildConfigurationName)' specified in \(actionDescription) isn't defined in the project."
return LintingIssue(reason: reason, severity: .error)
}

func lintCodeCoverageTargets(schemes: [Scheme], targets: [Target]) -> [LintingIssue] {

let targetNames = targets.map{$0.name}
var issues: [LintingIssue] = []

for scheme in schemes {
for target in scheme.testAction?.codeCoverageTargets ?? [] {
if !targetNames.contains(target) {
issues.append(missingCodeCoverageTargetIssue(missingTargetName: target, schemaName: scheme.name))
}
}
}

return issues
}

func missingCodeCoverageTargetIssue(missingTargetName: String, schemaName: String) -> LintingIssue {
let reason = "The target '\(missingTargetName)' specified in \(schemaName) code coverage targets list isn't defined in the project."
return LintingIssue(reason: reason, severity: .error)
}

}
4 changes: 4 additions & 0 deletions Sources/TuistGenerator/Models/Scheme.swift
Expand Up @@ -114,6 +114,7 @@ public class TestAction: Equatable {
public let arguments: Arguments?
public let configurationName: String
public let coverage: Bool
public let codeCoverageTargets: [String]
public let preActions: [ExecutionAction]
public let postActions: [ExecutionAction]

Expand All @@ -123,6 +124,7 @@ public class TestAction: Equatable {
arguments: Arguments? = nil,
configurationName: String,
coverage: Bool = false,
codeCoverageTargets: [String] = [],
preActions: [ExecutionAction] = [],
postActions: [ExecutionAction] = []) {
self.targets = targets
Expand All @@ -131,6 +133,7 @@ public class TestAction: Equatable {
self.coverage = coverage
self.preActions = preActions
self.postActions = postActions
self.codeCoverageTargets = codeCoverageTargets
}

// MARK: - Equatable
Expand All @@ -140,6 +143,7 @@ public class TestAction: Equatable {
lhs.arguments == rhs.arguments &&
lhs.configurationName == rhs.configurationName &&
lhs.coverage == rhs.coverage &&
lhs.codeCoverageTargets == rhs.codeCoverageTargets &&
lhs.preActions == rhs.preActions &&
lhs.postActions == rhs.postActions
}
Expand Down
2 changes: 2 additions & 0 deletions Sources/TuistKit/Generator/GeneratorModelLoader.swift
Expand Up @@ -634,13 +634,15 @@ extension TuistGenerator.TestAction {
let arguments = manifest.arguments.map { TuistGenerator.Arguments.from(manifest: $0) }
let configurationName = manifest.configurationName
let coverage = manifest.coverage
let codeCoverageTargets = manifest.codeCoverageTargets
let preActions = manifest.preActions.map { TuistGenerator.ExecutionAction.from(manifest: $0) }
let postActions = manifest.postActions.map { TuistGenerator.ExecutionAction.from(manifest: $0) }

return TestAction(targets: targets,
arguments: arguments,
configurationName: configurationName,
coverage: coverage,
codeCoverageTargets: codeCoverageTargets,
preActions: preActions,
postActions: postActions)
}
Expand Down
24 changes: 24 additions & 0 deletions Tests/TuistGeneratorTests/Generator/SchemesGeneratorTests.swift
Expand Up @@ -155,6 +155,30 @@ final class SchemeGeneratorTests: XCTestCase {
XCTAssertEqual(postBuildableReference?.blueprintName, "AppTests")
XCTAssertEqual(postBuildableReference?.buildableIdentifier, "primary")
}

func test_schemeTestAction_with_codeCoverageTargets() {

let target = Target.test(name: "App", product: .app)
let testTarget = Target.test(name: "AppTests", product: .unitTests)

let testAction = TestAction.test(targets: ["AppTests"], coverage: true, codeCoverageTargets: ["App"])
let buildAction = BuildAction.test(targets: ["App"])

let scheme = Scheme.test(name: "AppTests", shared: true, buildAction: buildAction, testAction: testAction)
let project = Project.test(targets: [target,testTarget])

let pbxTarget = PBXNativeTarget(name: "App", productType: .application)
let pbxTestTarget = PBXNativeTarget(name: "AppTests", productType: .unitTestBundle)
let generatedProject = GeneratedProject.test(targets: ["AppTests": pbxTestTarget, "App": pbxTarget])

let got = subject.schemeTestAction(scheme: scheme, project: project, generatedProject: generatedProject)

let codeCoverageTargetsBuildableReference = got?.codeCoverageTargets

XCTAssertEqual(got?.onlyGenerateCoverageForSpecifiedTargets, true)
XCTAssertEqual(codeCoverageTargetsBuildableReference?.count, 1)
XCTAssertEqual(codeCoverageTargetsBuildableReference?.first?.buildableName, "App.app")
}

func test_schemeBuildAction() {
let target = Target.test(name: "App", product: .app)
Expand Down
Expand Up @@ -25,12 +25,14 @@ extension TestAction {
arguments: Arguments? = Arguments.test(),
configurationName: String = BuildConfiguration.debug.name,
coverage: Bool = false,
codeCoverageTargets: [String] = [],
preActions: [ExecutionAction] = [],
postActions: [ExecutionAction] = []) -> TestAction {
return TestAction(targets: targets,
arguments: arguments,
configurationName: configurationName,
coverage: coverage,
codeCoverageTargets: codeCoverageTargets,
preActions: preActions,
postActions: postActions)
}
Expand Down
8 changes: 8 additions & 0 deletions docs/usage/2-projectswift.mdx
Expand Up @@ -797,6 +797,14 @@ Alternatively, when leveraging custom configurations, the configuration name can
optional: true,
default: 'false',
},
{
name: 'codeCoverageTargets',
description:
'A list of targets you want to gather the test coverage data for them, which are defined in the project.',
type: '[String]',
optional: true,
default: '[]',
},
{
name: 'Pre-actions',
description:
Expand Down