Add support for language and region to autogenerated schemes (#4983)

* Add language and region to ProjectDescription.WorkspaceGenerationOptions

* Add testLanguage and testRegion to ProjectOptions

* Rename new workspace options properties

* Add runLanguage and runRegion to RunActionOptions

* Add fixtures for test and run locales

* Remove unnecessary WorkspaceGenerationOptions parameters

* Update projects/tuist/fixtures/project_with_test_and_run_locales/Project.swift

Co-authored-by: Kas <kwridan@users.noreply.github.com>

* Add region parameter to RunActionOptions manifest

Co-authored-by: Kas <kwridan@users.noreply.github.com>
Co-authored-by: Daniele Formichelli <df@bendingspoons.com>
Co-authored-by: Marek Fořt <marekfort@me.com>
This commit is contained in:
Jakub Olejník 2023-01-27 13:50:14 +01:00 committed by GitHub
parent 48e1792837
commit ae4be7be2e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
20 changed files with 321 additions and 24 deletions

View File

@ -76,7 +76,11 @@ extension Project.Options {
run: ["App", "Demo"]
),
codeCoverageEnabled: Bool = false,
testingOptions: TestingOptions = []
testingOptions: TestingOptions = [],
testLanguage: SchemeLanguage? = nil,
testRegion: String? = nil,
runLanguage: SchemeLanguage? = nil,
runRegion: String? = nil
)
/// Disable autogenerated schemes

View File

@ -5,6 +5,9 @@ public struct RunActionOptions: Equatable, Codable {
/// Language to use when running the app.
public let language: SchemeLanguage?
/// Region to use when running the app.
public let region: String?
/// The path of the
/// [StoreKit configuration file](https://developer.apple.com/documentation/xcode/setting_up_storekit_testing_in_xcode#3625700).
public let storeKitConfigurationPath: Path?
@ -33,11 +36,13 @@ public struct RunActionOptions: Equatable, Codable {
/// You can disable it to test your app in best perfomance.
init(
language: SchemeLanguage? = nil,
region: String? = nil,
storeKitConfigurationPath: Path? = nil,
simulatedLocation: SimulatedLocation? = nil,
enableGPUFrameCaptureMode: GPUFrameCaptureMode = GPUFrameCaptureMode.default
) {
self.language = language
self.region = region
self.storeKitConfigurationPath = storeKitConfigurationPath
self.simulatedLocation = simulatedLocation
self.enableGPUFrameCaptureMode = enableGPUFrameCaptureMode

View File

@ -18,7 +18,12 @@ extension Workspace {
/// Tuist will not automatically generate any schemes
case disabled
/// Tuist will generate schemes with the associated testing options
case enabled(codeCoverageMode: CodeCoverageMode = .disabled, testingOptions: TestingOptions = [])
case enabled(
codeCoverageMode: CodeCoverageMode = .disabled,
testingOptions: TestingOptions = [],
testLanguage: SchemeLanguage? = nil,
testRegion: String? = nil
)
}
/// Enable or disable automatic generation of schemes by Xcode.

View File

@ -26,11 +26,23 @@ extension ProjectDescription.Project.Options.AutomaticSchemesOptions {
manifest: TuistGraph.Project.Options.AutomaticSchemesOptions
) -> Self {
switch manifest {
case let .enabled(targetSchemesGrouping, codeCoverageEnabled, testingOptions):
case let .enabled(
targetSchemesGrouping,
codeCoverageEnabled,
testingOptions,
testLanguage,
testRegion,
runLanguage,
runRegion
):
return .enabled(
targetSchemesGrouping: .from(manifest: targetSchemesGrouping),
codeCoverageEnabled: codeCoverageEnabled,
testingOptions: .from(manifest: testingOptions)
testingOptions: .from(manifest: testingOptions),
testLanguage: testLanguage.map { .init(identifier: $0) },
testRegion: testRegion,
runLanguage: runLanguage.map { .init(identifier: $0) },
runRegion: runRegion
)
case .disabled:
return .disabled

View File

@ -549,6 +549,7 @@ final class SchemeDescriptorsGenerator: SchemeDescriptorsGenerating {
commandlineArguments: commandlineArguments,
environmentVariables: environments,
language: scheme.runAction?.options.language,
region: scheme.runAction?.options.region,
launchAutomaticallySubstyle: launchActionConstants.launchAutomaticallySubstyle,
storeKitConfigurationFileReference: storeKitConfigurationFileReference
)

View File

@ -145,6 +145,10 @@ public final class AutogeneratedSchemesProjectMapper: ProjectMapping { // swiftl
executable: .init(projectPath: project.path, name: $0.name),
filePath: nil,
arguments: defaultArguments(for: $0),
options: .init(
language: project.options.runLanguage,
region: project.options.runRegion
),
diagnosticsOptions: [.mainThreadChecker, .performanceAntipatternChecker]
)
}
@ -194,7 +198,9 @@ public final class AutogeneratedSchemesProjectMapper: ProjectMapping { // swiftl
expandVariableFromTarget: nil,
preActions: [],
postActions: [],
diagnosticsOptions: [.mainThreadChecker]
diagnosticsOptions: [.mainThreadChecker],
language: project.options.testLanguage,
region: project.options.testRegion
),
runAction: runAction
)

View File

@ -101,7 +101,9 @@ public final class AutogeneratedWorkspaceSchemeWorkspaceMapper: WorkspaceMapping
expandVariableFromTarget: nil,
preActions: [],
postActions: [],
diagnosticsOptions: [.mainThreadChecker, .performanceAntipatternChecker]
diagnosticsOptions: [.mainThreadChecker, .performanceAntipatternChecker],
language: workspace.workspace.generationOptions.autogeneratedWorkspaceSchemes.testLanguage,
region: workspace.workspace.generationOptions.autogeneratedWorkspaceSchemes.testRegion
)
)
}

View File

@ -55,7 +55,11 @@ extension Project.Options {
case enabled(
targetSchemesGrouping: TargetSchemesGrouping,
codeCoverageEnabled: Bool,
testingOptions: TestingOptions
testingOptions: TestingOptions,
testLanguage: String? = nil,
testRegion: String? = nil,
runLanguage: String? = nil,
runRegion: String? = nil
)
/// Disable autogenerated schemes
@ -95,7 +99,7 @@ extension Project.Options {
extension Project.Options {
public var targetSchemesGrouping: AutomaticSchemesOptions.TargetSchemesGrouping? {
switch automaticSchemesOptions {
case let .enabled(targetSchemesGrouping, _, _):
case let .enabled(targetSchemesGrouping, _, _, _, _, _, _):
return targetSchemesGrouping
case .disabled:
return nil
@ -104,7 +108,7 @@ extension Project.Options {
public var codeCoverageEnabled: Bool {
switch automaticSchemesOptions {
case let .enabled(_, codeCoverageEnabled, _):
case let .enabled(_, codeCoverageEnabled, _, _, _, _, _):
return codeCoverageEnabled
case .disabled:
return false
@ -113,10 +117,46 @@ extension Project.Options {
public var testingOptions: TestingOptions {
switch automaticSchemesOptions {
case let .enabled(_, _, testingOptions):
case let .enabled(_, _, testingOptions, _, _, _, _):
return testingOptions
case .disabled:
return []
}
}
public var testLanguage: String? {
switch automaticSchemesOptions {
case let .enabled(_, _, _, language, _, _, _):
return language
case .disabled:
return nil
}
}
public var testRegion: String? {
switch automaticSchemesOptions {
case let .enabled(_, _, _, _, region, _, _):
return region
case .disabled:
return nil
}
}
public var runLanguage: String? {
switch automaticSchemesOptions {
case let .enabled(_, _, _, _, _, language, _):
return language
case .disabled:
return nil
}
}
public var runRegion: String? {
switch automaticSchemesOptions {
case let .enabled(_, _, _, _, _, _, region):
return region
case .disabled:
return nil
}
}
}

View File

@ -6,6 +6,9 @@ public struct RunActionOptions: Equatable, Codable {
/// App Language.
public let language: String?
/// App Region.
public let region: String?
/// The path of the
/// [StoreKit configuration file](https://developer.apple.com/documentation/xcode/setting_up_storekit_testing_in_xcode#3625700)
public let storeKitConfigurationPath: AbsolutePath?
@ -34,11 +37,13 @@ public struct RunActionOptions: Equatable, Codable {
public init(
language: String? = nil,
region: String? = nil,
storeKitConfigurationPath: AbsolutePath? = nil,
simulatedLocation: SimulatedLocation? = nil,
enableGPUFrameCaptureMode: GPUFrameCaptureMode = .autoEnabled
) {
self.language = language
self.region = region
self.storeKitConfigurationPath = storeKitConfigurationPath
self.simulatedLocation = simulatedLocation
self.enableGPUFrameCaptureMode = enableGPUFrameCaptureMode

View File

@ -26,7 +26,12 @@ public struct Workspace: Equatable, Codable {
schemes: [Scheme] = [],
generationOptions: GenerationOptions = .init(
enableAutomaticXcodeSchemes: false,
autogeneratedWorkspaceSchemes: .enabled(codeCoverageMode: .disabled, testingOptions: []),
autogeneratedWorkspaceSchemes: .enabled(
codeCoverageMode: .disabled,
testingOptions: [],
testLanguage: nil,
testRegion: nil
),
lastXcodeUpgradeCheck: nil,
renderMarkdownReadme: false
),
@ -126,7 +131,7 @@ extension Workspace {
extension Workspace {
public var codeCoverageMode: GenerationOptions.AutogeneratedWorkspaceSchemes.CodeCoverageMode {
switch generationOptions.autogeneratedWorkspaceSchemes {
case let .enabled(codeCoverageMode, _):
case let .enabled(codeCoverageMode, _, _, _):
return codeCoverageMode
case .disabled:
return .disabled
@ -135,7 +140,7 @@ extension Workspace {
public var testingOptions: TestingOptions {
switch generationOptions.autogeneratedWorkspaceSchemes {
case let .enabled(_, testingOptions):
case let .enabled(_, testingOptions, _, _):
return testingOptions
case .disabled:
return []

View File

@ -20,11 +20,16 @@ extension Workspace {
/// Tuist will not automatically generate the workspace schemes
case disabled
/// Tuist will generate the workspace schemes
case enabled(codeCoverageMode: CodeCoverageMode = .disabled, testingOptions: TestingOptions = [])
case enabled(
codeCoverageMode: CodeCoverageMode = .disabled,
testingOptions: TestingOptions = [],
testLanguage: String? = nil,
testRegion: String? = nil
)
public var codeCoverageMode: CodeCoverageMode {
switch self {
case let .enabled(codeCoverageMode, _):
case let .enabled(codeCoverageMode, _, _, _):
return codeCoverageMode
case .disabled:
return .disabled
@ -33,12 +38,30 @@ extension Workspace {
public var testingOptions: TestingOptions {
switch self {
case let .enabled(_, testingOptions):
case let .enabled(_, testingOptions, _, _):
return testingOptions
case .disabled:
return []
}
}
public var testLanguage: String? {
switch self {
case let .enabled(_, _, language, _):
return language
case .disabled:
return nil
}
}
public var testRegion: String? {
switch self {
case let .enabled(_, _, _, region):
return region
case .disabled:
return nil
}
}
}
/// Tuist generates a WorkspaceSettings.xcsettings file, setting the related key to the associated value.

View File

@ -30,7 +30,12 @@ extension Workspace {
extension Workspace.GenerationOptions {
public static func test(
enableAutomaticXcodeSchemes: Bool? = false,
autogeneratedWorkspaceSchemes: AutogeneratedWorkspaceSchemes = .enabled(codeCoverageMode: .disabled, testingOptions: []),
autogeneratedWorkspaceSchemes: AutogeneratedWorkspaceSchemes = .enabled(
codeCoverageMode: .disabled,
testingOptions: [],
testLanguage: nil,
testRegion: nil
),
lastXcodeUpgradeCheck: Version? = nil
) -> Self {
.init(

View File

@ -26,11 +26,23 @@ extension TuistGraph.Project.Options.AutomaticSchemesOptions {
manifest: ProjectDescription.Project.Options.AutomaticSchemesOptions
) -> Self {
switch manifest {
case let .enabled(targetSchemesGrouping, codeCoverageEnabled, testingOptions):
case let .enabled(
targetSchemesGrouping,
codeCoverageEnabled,
testingOptions,
testLanguage,
testRegion,
runLanguage,
runRegion
):
return .enabled(
targetSchemesGrouping: .from(manifest: targetSchemesGrouping),
codeCoverageEnabled: codeCoverageEnabled,
testingOptions: .from(manifest: testingOptions)
testingOptions: .from(manifest: testingOptions),
testLanguage: testLanguage?.identifier,
testRegion: testRegion,
runLanguage: runLanguage?.identifier,
runRegion: runRegion
)
case .disabled:
return .disabled

View File

@ -47,6 +47,7 @@ extension TuistGraph.RunActionOptions {
return TuistGraph.RunActionOptions(
language: language,
region: manifest.region,
storeKitConfigurationPath: storeKitConfigurationPath,
simulatedLocation: simulatedLocation,
enableGPUFrameCaptureMode: enableGPUFrameCaptureMode

View File

@ -31,10 +31,12 @@ extension TuistGraph.Workspace.GenerationOptions.AutogeneratedWorkspaceSchemes {
switch manifest {
case .disabled:
return .disabled
case let .enabled(codeCoverageMode, testingOptions):
case let .enabled(codeCoverageMode, testingOptions, testLanguage, testRegion):
return .enabled(
codeCoverageMode: try .from(manifest: codeCoverageMode, generatorPaths: generatorPaths),
testingOptions: .from(manifest: testingOptions)
testingOptions: .from(manifest: testingOptions),
testLanguage: testLanguage?.identifier,
testRegion: testRegion
)
}
}

View File

@ -1072,4 +1072,44 @@ final class AutogeneratedWorkspaceSchemeWorkspaceMapperTests: TuistUnitTestCase
)
XCTAssertFalse(try XCTUnwrap(scheme.testAction?.coverage))
}
func test_map_language_region() throws {
// Given
let subject = AutogeneratedWorkspaceSchemeWorkspaceMapper(forceWorkspaceSchemes: false)
let targetA = Target.test(
name: "A"
)
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,
]
)
let workspace = Workspace.test(
name: "A",
projects: [
project.path,
],
generationOptions: .test(autogeneratedWorkspaceSchemes: .enabled(testLanguage: "cs", testRegion: "CZ"))
)
// When
let (got, _) = try subject.map(
workspace: WorkspaceWithProjects(workspace: workspace, projects: [project])
)
// Then
let scheme = try XCTUnwrap(got.workspace.schemes.first)
XCTAssertEqual(try XCTUnwrap(scheme.testAction?.language), "cs")
XCTAssertEqual(try XCTUnwrap(scheme.testAction?.region), "CZ")
}
}

View File

@ -992,6 +992,71 @@ final class AutogeneratedSchemesProjectMapperTests: TuistUnitTestCase {
XCTAssertEqual(got.schemes, expected)
}
func test_locale() throws {
// Given
let targetA = Target.test(
name: "A",
product: .framework
)
let targetADemo = Target.test(
name: "ADemo",
product: .app
)
let targetATests = Target.test(
name: "ATests",
product: .unitTests,
dependencies: [.target(name: "A")]
)
let projectPath = try temporaryPath()
let project = Project.test(
path: projectPath,
options: .test(
automaticSchemesOptions: .enabled(
targetSchemesGrouping: .byNameSuffix(
build: [],
test: ["Tests"],
run: ["Demo"]
),
codeCoverageEnabled: false,
testingOptions: [],
testLanguage: "en",
testRegion: "US",
runLanguage: "cs",
runRegion: "CZ"
)
),
targets: [
targetA,
targetADemo,
targetATests,
]
)
// When
let (got, sideEffects) = try subject.map(project: project)
// Then
XCTAssertEmpty(sideEffects)
let expected = [
makeScheme(
name: "A",
buildTargetNames: ["A", "ADemo"],
testTargetNames: ["ATests"],
runTargetName: "ADemo",
projectPath: projectPath,
coverage: false,
parallelizable: false,
randomExecution: false,
testLanguage: "en",
testRegion: "US",
runActionOptions: .init(language: "cs", region: "CZ")
),
]
XCTAssertEqual(got.schemes, expected)
}
func test_disabled() throws {
// Given
let targetB = Target.test(name: "B", product: .app)
@ -1324,7 +1389,10 @@ final class AutogeneratedSchemesProjectMapperTests: TuistUnitTestCase {
parallelizable: Bool,
randomExecution: Bool,
runActionArguments: Arguments? = nil,
testActionArguments: Arguments? = nil
testActionArguments: Arguments? = nil,
testLanguage: String? = nil,
testRegion: String? = nil,
runActionOptions: RunActionOptions? = nil
) -> TuistGraph.Scheme {
Scheme(
name: name,
@ -1341,12 +1409,15 @@ final class AutogeneratedSchemesProjectMapperTests: TuistUnitTestCase {
)
},
arguments: testActionArguments,
coverage: coverage
coverage: coverage,
language: testLanguage,
region: testRegion
),
runAction: runTargetName.map {
RunAction.test(
executable: TargetReference(projectPath: projectPath, name: $0),
arguments: runActionArguments
arguments: runActionArguments,
options: runActionOptions ?? .init()
)
}
)

View File

@ -0,0 +1,27 @@
import ProjectDescription
let project = Project(
name: "App",
options: .options(
automaticSchemesOptions: .enabled(
testLanguage: "en",
testRegion: "US",
runLanguage: "en",
runRegion: "US"
)
),
targets: [
.init(
name: "App",
platform: .iOS,
product: .app,
bundleId: "io.tuist.app"
),
.init(
name: "AppTests",
platform: .iOS,
product: .unitTests,
bundleId: "io.tuist.appTests"
),
]
)

View File

@ -0,0 +1,21 @@
import ProjectDescription
let project = Project(
name: "App",
options: .options(
automaticSchemesOptions: .enabled(
testLanguage: "en",
testRegion: "US",
runLanguage: "en",
runRegion: "US"
)
),
targets: [
.init(
name: "App",
platform: .iOS,
product: .app,
bundleId: "io.tuist.app"
),
]
)

View File

@ -0,0 +1,10 @@
import ProjectDescription
let workspace = Workspace(
name: "App",
projects: ["."],
generationOptions: .options(autogeneratedWorkspaceSchemes: .enabled(
testLanguage: "en",
testRegion: "US"
))
)