diff --git a/CHANGELOG.md b/CHANGELOG.md index 1d0aabe7a..70c698205 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,7 @@ Please, check out guidelines: https://keepachangelog.com/en/1.0.0/ - Synthesize accessors for stringsdict [#1993](https://github.com/tuist/tuist/pull/1993) by [@fortmarek](https://githubl.com/fortmarek) - Add support for `StencilSwiftKit`'s additional filters. [#1994](https://github.com/tuist/tuist/pull/1994) by [@svastven](https://github.com/svastven). - Add `migration list-targets` command to show all targets sorted by number of dependencies [#1732](https://github.com/tuist/tuist/pull/1732) of a given project by [@andreacipriani](https://github.com/andreacipriani). +- Add `enableCodeCoverage` generation option to enable code coverage in automatically generated schemes [#ZZZZ](https://github.com/tuist/tuist/pull/ZZZZ) by [@frijole](https://github.com/frijole).) ### Fixed diff --git a/Sources/ProjectDescription/Config.swift b/Sources/ProjectDescription/Config.swift index 9c25fdc9f..060ac9e88 100644 --- a/Sources/ProjectDescription/Config.swift +++ b/Sources/ProjectDescription/Config.swift @@ -12,6 +12,7 @@ public struct Config: Codable, Equatable { /// - disableAutogeneratedSchemes: When passed, Tuist generates the project only with custom specified schemes, autogenerated default schemes are skipped /// - disableSynthesizedResourceAccessors: When passed, Tuist does not synthesize resource accessors /// - disableShowEnvironmentVarsInScriptPhases: When passed, Tuist disables echoing the ENV in shell script build phases + /// - enableCodeCoverage: When passed, Tuist will enable code coverage for autogenerated default schemes public enum GenerationOptions: Encodable, Decodable, Equatable { case xcodeProjectName(TemplateString) case organizationName(String) @@ -19,6 +20,7 @@ public struct Config: Codable, Equatable { case disableAutogeneratedSchemes case disableSynthesizedResourceAccessors case disableShowEnvironmentVarsInScriptPhases + case enableCodeCoverage } /// Generation options. @@ -55,6 +57,7 @@ extension Config.GenerationOptions { case disableAutogeneratedSchemes case disableSynthesizedResourceAccessors case disableShowEnvironmentVarsInScriptPhases + case enableCodeCoverage } public init(from decoder: Decoder) throws { @@ -90,6 +93,10 @@ extension Config.GenerationOptions { self = .disableShowEnvironmentVarsInScriptPhases return } + if container.allKeys.contains(.enableCodeCoverage), try container.decode(Bool.self, forKey: .enableCodeCoverage) { + self = .enableCodeCoverage + return + } throw DecodingError.dataCorrupted(.init(codingPath: decoder.codingPath, debugDescription: "Unknown enum case")) } @@ -113,6 +120,8 @@ extension Config.GenerationOptions { try container.encode(true, forKey: .disableSynthesizedResourceAccessors) case .disableShowEnvironmentVarsInScriptPhases: try container.encode(true, forKey: .disableShowEnvironmentVarsInScriptPhases) + case .enableCodeCoverage: + try container.encode(true, forKey: .enableCodeCoverage) } } } @@ -136,6 +145,8 @@ public func == (lhs: TuistConfig.GenerationOptions, rhs: TuistConfig.GenerationO return true case (.disableShowEnvironmentVarsInScriptPhases, .disableShowEnvironmentVarsInScriptPhases): return true + case (.enableCodeCoverage, .enableCodeCoverage): + return true default: return false } diff --git a/Sources/TuistCore/Models/Config.swift b/Sources/TuistCore/Models/Config.swift index 3f9b74240..020baaca8 100644 --- a/Sources/TuistCore/Models/Config.swift +++ b/Sources/TuistCore/Models/Config.swift @@ -14,6 +14,7 @@ public struct Config: Equatable, Hashable { case disableAutogeneratedSchemes case disableSynthesizedResourceAccessors case disableShowEnvironmentVarsInScriptPhases + case enableCodeCoverage } /// Generation options. diff --git a/Sources/TuistGenerator/Mappers/AutogeneratedSchemesProjectMapper.swift b/Sources/TuistGenerator/Mappers/AutogeneratedSchemesProjectMapper.swift index 78e277ab0..b466f2209 100644 --- a/Sources/TuistGenerator/Mappers/AutogeneratedSchemesProjectMapper.swift +++ b/Sources/TuistGenerator/Mappers/AutogeneratedSchemesProjectMapper.swift @@ -4,9 +4,13 @@ import TuistCore /// A project mapper that auto-generates schemes for each of the targets of the `Project` /// if the user hasn't already defined schemes for those. public final class AutogeneratedSchemesProjectMapper: ProjectMapping { + private let enableCodeCoverage: Bool + // MARK: - Init - public init() {} + public init(enableCodeCoverage: Bool) { + self.enableCodeCoverage = enableCodeCoverage + } // MARK: - ProjectMapping @@ -17,6 +21,7 @@ public final class AutogeneratedSchemesProjectMapper: ProjectMapping { let autogeneratedSchemes = project.targets.compactMap { (target: Target) -> Scheme? in let scheme = self.createDefaultScheme(target: target, project: project, + codeCoverage: enableCodeCoverage, buildConfiguration: project.defaultDebugBuildConfigurationName) // The user has already defined a scheme with that name. if schemeNames.contains(scheme.name) { return nil } @@ -28,21 +33,23 @@ public final class AutogeneratedSchemesProjectMapper: ProjectMapping { // MARK: - Private - private func createDefaultScheme(target: Target, project: Project, buildConfiguration: String) -> Scheme { + private func createDefaultScheme(target: Target, project: Project, codeCoverage: Bool, buildConfiguration: String) -> Scheme { let targetReference = TargetReference(projectPath: project.path, name: target.name) let buildTargets = buildableTargets(targetReference: targetReference, target: target, project: project) let testTargets = testableTargets(targetReference: targetReference, target: target, project: project) let executable = runnableExecutable(targetReference: targetReference, target: target, project: project) + let codeCoverageTargets = codeCoverage ? [targetReference] : [] + return Scheme(name: target.name, shared: true, buildAction: BuildAction(targets: buildTargets), testAction: TestAction(targets: testTargets, arguments: nil, configurationName: buildConfiguration, - coverage: false, - codeCoverageTargets: [], + coverage: enableCodeCoverage, + codeCoverageTargets: codeCoverageTargets, preActions: [], postActions: [], diagnosticsOptions: Set()), diff --git a/Sources/TuistKit/GraphMappers/ProjectMapperProvider.swift b/Sources/TuistKit/GraphMappers/ProjectMapperProvider.swift index 25b20eb28..f00f0aeb8 100644 --- a/Sources/TuistKit/GraphMappers/ProjectMapperProvider.swift +++ b/Sources/TuistKit/GraphMappers/ProjectMapperProvider.swift @@ -27,7 +27,7 @@ final class ProjectMapperProvider: ProjectMapperProviding { // Auto-generation of schemes if !config.generationOptions.contains(.disableAutogeneratedSchemes) { - mappers.append(AutogeneratedSchemesProjectMapper()) + mappers.append(AutogeneratedSchemesProjectMapper(enableCodeCoverage: config.generationOptions.contains(.enableCodeCoverage))) } // Delete current derived diff --git a/Sources/TuistKit/ProjectEditor/ProjectEditor.swift b/Sources/TuistKit/ProjectEditor/ProjectEditor.swift index 23a84cf23..73b71c95b 100644 --- a/Sources/TuistKit/ProjectEditor/ProjectEditor.swift +++ b/Sources/TuistKit/ProjectEditor/ProjectEditor.swift @@ -71,7 +71,7 @@ final class ProjectEditor: ProjectEditing { templatesDirectoryLocator: TemplatesDirectoryLocating = TemplatesDirectoryLocator(), projectMapper: ProjectMapping = SequentialProjectMapper( mappers: [ - AutogeneratedSchemesProjectMapper(), + AutogeneratedSchemesProjectMapper(enableCodeCoverage: false), ] ), sideEffectDescriptorExecutor: SideEffectDescriptorExecuting = SideEffectDescriptorExecutor() diff --git a/Sources/TuistLoader/Models+ManifestMappers/Config+ManifestMapper.swift b/Sources/TuistLoader/Models+ManifestMappers/Config+ManifestMapper.swift index 6b0a53f32..7b2c4e6af 100644 --- a/Sources/TuistLoader/Models+ManifestMappers/Config+ManifestMapper.swift +++ b/Sources/TuistLoader/Models+ManifestMappers/Config+ManifestMapper.swift @@ -42,6 +42,8 @@ extension TuistCore.Config.GenerationOption { return .disableSynthesizedResourceAccessors case .disableShowEnvironmentVarsInScriptPhases: return .disableShowEnvironmentVarsInScriptPhases + case .enableCodeCoverage: + return .enableCodeCoverage } } } diff --git a/Tests/ProjectDescriptionTests/ConfigTests.swift b/Tests/ProjectDescriptionTests/ConfigTests.swift index eaf0f7bec..f83099524 100644 --- a/Tests/ProjectDescriptionTests/ConfigTests.swift +++ b/Tests/ProjectDescriptionTests/ConfigTests.swift @@ -12,6 +12,7 @@ final class ConfigTests: XCTestCase { .disableAutogeneratedSchemes, .disableSynthesizedResourceAccessors, .disableShowEnvironmentVarsInScriptPhases, + .enableCodeCoverage, ]) XCTAssertCodable(config) diff --git a/Tests/TuistGeneratorTests/ProjectMappers/AutogeneratedSchemesProjectMapperTests.swift b/Tests/TuistGeneratorTests/ProjectMappers/AutogeneratedSchemesProjectMapperTests.swift index 7986d6458..d03c5383f 100644 --- a/Tests/TuistGeneratorTests/ProjectMappers/AutogeneratedSchemesProjectMapperTests.swift +++ b/Tests/TuistGeneratorTests/ProjectMappers/AutogeneratedSchemesProjectMapperTests.swift @@ -12,7 +12,7 @@ final class AutogeneratedSchemesProjectMapperTests: TuistUnitTestCase { override func setUp() { super.setUp() - subject = AutogeneratedSchemesProjectMapper() + subject = AutogeneratedSchemesProjectMapper(enableCodeCoverage: false) } override func tearDown() { @@ -142,13 +142,50 @@ final class AutogeneratedSchemesProjectMapperTests: TuistUnitTestCase { ]) } + func test_map_enables_test_coverage_on_generated_schemes() throws { + // Given + subject = AutogeneratedSchemesProjectMapper(enableCodeCoverage: true) + + let targetA = Target.test( + name: "A", + dependencies: [ + .target(name: "B"), + ] + ) + let targetATests = Target.test( + name: "ATests", + product: .unitTests, + dependencies: [.target(name: "A")] + ) + let projectPath = try temporaryPath() + let project = Project.test( + path: projectPath, + targets: [ + targetA, + targetATests, + ] + ) + + // When + let (got, sideEffects) = try subject.map(project: project) + + // Then + XCTAssertEmpty(sideEffects) + XCTAssertEqual(got.schemes.count, 2) + + // Then: A Tests + let gotAScheme = got.schemes.first! + XCTAssertTrue(gotAScheme.testAction?.coverage != nil) + XCTAssertEqual(gotAScheme.testAction?.codeCoverageTargets.count, 1) + } + // MARK: - Helpers private func testScheme( - target: Target, + target: TuistCore.Target, projectPath: AbsolutePath, testTargetName: String - ) -> Scheme { + ) -> TuistCore.Scheme { Scheme( name: target.name, shared: true, diff --git a/Tests/TuistKitTests/GraphMappers/ProjectMapperProviderTests.swift b/Tests/TuistKitTests/GraphMappers/ProjectMapperProviderTests.swift index b374e5c69..2df7388ff 100644 --- a/Tests/TuistKitTests/GraphMappers/ProjectMapperProviderTests.swift +++ b/Tests/TuistKitTests/GraphMappers/ProjectMapperProviderTests.swift @@ -95,4 +95,22 @@ final class ProjectMapperProviderTests: TuistUnitTestCase { let sequentialProjectMapper = try XCTUnwrap(got as? SequentialProjectMapper) XCTAssertEqual(sequentialProjectMapper.mappers.filter { $0 is TargetProjectMapper }.count, 1) } + + func test_mappers_does_enable_code_coverage() throws { + // Given + subject = ProjectMapperProvider(contentHasher: CacheContentHasher()) + + // When + let got = subject.mapper( + config: Config.test( + generationOptions: [ + .enableCodeCoverage, + ] + ) + ) + + // Then + let sequentialProjectMapper = try XCTUnwrap(got as? SequentialProjectMapper) + XCTAssertEqual(sequentialProjectMapper.mappers.filter { $0 is AutogeneratedSchemesProjectMapper }.count, 1) + } } diff --git a/website/markdown/docs/usage/config.mdx b/website/markdown/docs/usage/config.mdx index a0e84cc3f..a87e11465 100644 --- a/website/markdown/docs/usage/config.mdx +++ b/website/markdown/docs/usage/config.mdx @@ -125,6 +125,11 @@ Generation options allow customizing the generation of Xcode projects. description: 'Do not automatically synthesize resource accessors (assets, localized strings, etc.)', }, + { + case: '.enableCodeCoverage', + description: + 'Enable code coverage for auto generated schemes.', + }, ]} />