mirror of
https://github.com/yonaskolb/XcodeGen.git
synced 2026-03-18 20:02:25 +00:00
integrate XCTest with Spectre for nicer tests in Xcode
This commit is contained in:
+5
-5
@@ -42,8 +42,8 @@
|
||||
"repositoryURL": "https://github.com/onevcat/Rainbow.git",
|
||||
"state": {
|
||||
"branch": null,
|
||||
"revision": "f69961599ad524251d677fbec9e4bac57385d6fc",
|
||||
"version": "3.1.1"
|
||||
"revision": "797a68d0a642609424b08f11eb56974a54d5f6e2",
|
||||
"version": "3.1.4"
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -57,11 +57,11 @@
|
||||
},
|
||||
{
|
||||
"package": "Spectre",
|
||||
"repositoryURL": "https://github.com/kylef/Spectre.git",
|
||||
"repositoryURL": "https://github.com/yonaskolb/Spectre.git",
|
||||
"state": {
|
||||
"branch": null,
|
||||
"revision": "e34d5687e1e9d865e3527dd58bc2f7464ef6d936",
|
||||
"version": "0.8.0"
|
||||
"revision": "2b520ec417fc515c70e7093ae3ee321c3237e215",
|
||||
"version": "0.8.1"
|
||||
}
|
||||
},
|
||||
{
|
||||
|
||||
+1
-1
@@ -14,7 +14,7 @@ let package = Package(
|
||||
.package(url: "https://github.com/kylef/Commander.git", from: "0.8.0"),
|
||||
.package(url: "https://github.com/jpsim/Yams.git", from: "0.3.6"),
|
||||
.package(url: "https://github.com/yonaskolb/JSONUtilities.git", from: "3.3.0"),
|
||||
.package(url: "https://github.com/kylef/Spectre.git", from: "0.8.0"),
|
||||
.package(url: "https://github.com/yonaskolb/Spectre.git", from: "0.8.1"),
|
||||
.package(url: "https://github.com/onevcat/Rainbow.git", from: "3.0.0"),
|
||||
.package(url: "https://github.com/xcodeswift/xcproj.git", from: "4.1.0")
|
||||
],
|
||||
|
||||
@@ -1,9 +0,0 @@
|
||||
@testable import XcodeGenKitTests
|
||||
import XCTest
|
||||
|
||||
XCTMain([
|
||||
testCase(GeneratorTests.allTests),
|
||||
testCase(SpecLoadingTests.allTests),
|
||||
testCase(FixtureTests.allTests),
|
||||
testCase(ProjectSpecTests.allTests),
|
||||
])
|
||||
@@ -1,81 +0,0 @@
|
||||
import PathKit
|
||||
import ProjectSpec
|
||||
import Spectre
|
||||
import XcodeGenKit
|
||||
import xcproj
|
||||
|
||||
let fixturePath = Path(#file).parent().parent() + "Fixtures"
|
||||
|
||||
func generateXcodeProject(specPath: Path, projectPath: Path) throws -> XcodeProj {
|
||||
let project = try Project(path: specPath)
|
||||
let generator = ProjectGenerator(project: project)
|
||||
let xcodeProject = try generator.generateXcodeProject()
|
||||
let oldProject = try XcodeProj(path: projectPath)
|
||||
let pbxProjPath = projectPath + XcodeProj.pbxprojPath(projectPath)
|
||||
let oldProjectString: String = try pbxProjPath.read()
|
||||
try xcodeProject.write(path: projectPath, override: true)
|
||||
let newProjectString: String = try pbxProjPath.read()
|
||||
|
||||
let newProject = try XcodeProj(path: projectPath)
|
||||
let stringDiff = newProjectString != oldProjectString
|
||||
if newProject != oldProject || stringDiff {
|
||||
var message = "\(projectPath.string) has changed. If change is legitimate commit the change and run test again"
|
||||
if stringDiff {
|
||||
message += ":\n\n\(pbxProjPath):\n\(prettyFirstDifferenceBetweenStrings(oldProjectString, newProjectString))"
|
||||
}
|
||||
throw failure(message)
|
||||
}
|
||||
|
||||
return newProject
|
||||
}
|
||||
|
||||
func fixtureTests() {
|
||||
|
||||
describe("Test Project") {
|
||||
var xcodeProject: XcodeProj?
|
||||
|
||||
$0.it("generates") {
|
||||
xcodeProject = try generateXcodeProject(specPath: fixturePath + "TestProject/project.yml", projectPath: fixturePath + "TestProject/Project.xcodeproj")
|
||||
}
|
||||
|
||||
$0.it("generates variant group") {
|
||||
guard let xcodeProject = xcodeProject else { return }
|
||||
|
||||
func getFileReferences(_ path: String) -> [ObjectReference<PBXFileReference>] {
|
||||
return xcodeProject.pbxproj.objects.fileReferences.objectReferences.filter { $0.object.path == path }
|
||||
}
|
||||
|
||||
func getVariableGroups(_ name: String?) -> [PBXVariantGroup] {
|
||||
return xcodeProject.pbxproj.objects.variantGroups.referenceValues.filter { $0.name == name }
|
||||
}
|
||||
|
||||
let resourceName = "LocalizedStoryboard.storyboard"
|
||||
let baseResource = "Base.lproj/LocalizedStoryboard.storyboard"
|
||||
let localizedResource = "en.lproj/LocalizedStoryboard.strings"
|
||||
|
||||
guard let variableGroup = getVariableGroups(resourceName).first else { throw failure("Couldn't find the variable group") }
|
||||
|
||||
do {
|
||||
let refs = getFileReferences(baseResource)
|
||||
try expect(refs.count) == 1
|
||||
try expect(variableGroup.children.filter { $0 == refs.first?.reference }.count) == 1
|
||||
}
|
||||
|
||||
do {
|
||||
let refs = getFileReferences(localizedResource)
|
||||
try expect(refs.count) == 1
|
||||
try expect(variableGroup.children.filter { $0 == refs.first?.reference }.count) == 1
|
||||
}
|
||||
}
|
||||
|
||||
$0.it("generates scheme execution actions") {
|
||||
guard let xcodeProject = xcodeProject else { return }
|
||||
|
||||
let frameworkScheme = xcodeProject.sharedData?.schemes.first { $0.name == "Framework" }
|
||||
try expect(frameworkScheme?.buildAction?.preActions.first?.scriptText) == "echo Starting Framework Build"
|
||||
try expect(frameworkScheme?.buildAction?.preActions.first?.title) == "Run Script"
|
||||
try expect(frameworkScheme?.buildAction?.preActions.first?.environmentBuildable?.blueprintName) == "Framework_iOS"
|
||||
try expect(frameworkScheme?.buildAction?.preActions.first?.environmentBuildable?.buildableName) == "Framework.framework"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
import PathKit
|
||||
import ProjectSpec
|
||||
import Spectre
|
||||
import XcodeGenKit
|
||||
import xcproj
|
||||
import XCTest
|
||||
import Yams
|
||||
|
||||
extension Project {
|
||||
|
||||
func generateXcodeProject(file: String = #file, line: Int = #line) throws -> XcodeProj {
|
||||
return try doThrowing(file: file, line: line) {
|
||||
let generator = ProjectGenerator(project: self)
|
||||
return try generator.generateXcodeProject()
|
||||
}
|
||||
}
|
||||
|
||||
func generatePbxProj(file: String = #file, line: Int = #line) throws -> PBXProj {
|
||||
return try doThrowing(file: file, line: line) {
|
||||
let xcodeProject = try generateXcodeProject().pbxproj
|
||||
try xcodeProject.validate()
|
||||
return xcodeProject
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extension PBXProj {
|
||||
|
||||
// validates that a PBXProj is correct
|
||||
// TODO: Use xclint?
|
||||
func validate() throws {
|
||||
let mainGroup = try getMainGroup()
|
||||
|
||||
func validateGroup(_ group: PBXGroup) throws {
|
||||
let hasDuplicatedChildren = group.children.count != Set(group.children).count
|
||||
if hasDuplicatedChildren {
|
||||
throw failure("Group \"\(group.nameOrPath)\" has duplicated children:\n - \(group.children.sorted().joined(separator: "\n - "))")
|
||||
}
|
||||
for child in group.children {
|
||||
if let group = objects.groups.getReference(child) {
|
||||
try validateGroup(group)
|
||||
}
|
||||
}
|
||||
}
|
||||
try validateGroup(mainGroup)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,83 @@
|
||||
import PathKit
|
||||
import ProjectSpec
|
||||
import Spectre
|
||||
import XcodeGenKit
|
||||
import xcproj
|
||||
import XCTest
|
||||
|
||||
|
||||
class ProjectFixtureTests: XCTestCase {
|
||||
|
||||
func testProjectFixture() {
|
||||
describe {
|
||||
var xcodeProject: XcodeProj?
|
||||
|
||||
$0.it("generates") {
|
||||
xcodeProject = try generateXcodeProject(specPath: fixturePath + "TestProject/project.yml", projectPath: fixturePath + "TestProject/Project.xcodeproj")
|
||||
}
|
||||
|
||||
$0.it("generates variant group") {
|
||||
guard let xcodeProject = xcodeProject else { return }
|
||||
|
||||
func getFileReferences(_ path: String) -> [ObjectReference<PBXFileReference>] {
|
||||
return xcodeProject.pbxproj.objects.fileReferences.objectReferences.filter { $0.object.path == path }
|
||||
}
|
||||
|
||||
func getVariableGroups(_ name: String?) -> [PBXVariantGroup] {
|
||||
return xcodeProject.pbxproj.objects.variantGroups.referenceValues.filter { $0.name == name }
|
||||
}
|
||||
|
||||
let resourceName = "LocalizedStoryboard.storyboard"
|
||||
let baseResource = "Base.lproj/LocalizedStoryboard.storyboard"
|
||||
let localizedResource = "en.lproj/LocalizedStoryboard.strings"
|
||||
|
||||
guard let variableGroup = getVariableGroups(resourceName).first else { throw failure("Couldn't find the variable group") }
|
||||
|
||||
do {
|
||||
let refs = getFileReferences(baseResource)
|
||||
try expect(refs.count) == 1
|
||||
try expect(variableGroup.children.filter { $0 == refs.first?.reference }.count) == 1
|
||||
}
|
||||
|
||||
do {
|
||||
let refs = getFileReferences(localizedResource)
|
||||
try expect(refs.count) == 1
|
||||
try expect(variableGroup.children.filter { $0 == refs.first?.reference }.count) == 1
|
||||
}
|
||||
}
|
||||
|
||||
$0.it("generates scheme execution actions") {
|
||||
guard let xcodeProject = xcodeProject else { return }
|
||||
|
||||
let frameworkScheme = xcodeProject.sharedData?.schemes.first { $0.name == "Framework" }
|
||||
try expect(frameworkScheme?.buildAction?.preActions.first?.scriptText) == "echo Starting Framework Build"
|
||||
try expect(frameworkScheme?.buildAction?.preActions.first?.title) == "Run Script"
|
||||
try expect(frameworkScheme?.buildAction?.preActions.first?.environmentBuildable?.blueprintName) == "Framework_iOS"
|
||||
try expect(frameworkScheme?.buildAction?.preActions.first?.environmentBuildable?.buildableName) == "Framework.framework"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fileprivate func generateXcodeProject(specPath: Path, projectPath: Path, file: String = #file, line: Int = #line) throws -> XcodeProj {
|
||||
let project = try Project(path: specPath)
|
||||
let generator = ProjectGenerator(project: project)
|
||||
let xcodeProject = try generator.generateXcodeProject()
|
||||
let oldProject = try XcodeProj(path: projectPath)
|
||||
let pbxProjPath = projectPath + XcodeProj.pbxprojPath(projectPath)
|
||||
let oldProjectString: String = try pbxProjPath.read()
|
||||
try xcodeProject.write(path: projectPath, override: true)
|
||||
let newProjectString: String = try pbxProjPath.read()
|
||||
|
||||
let newProject = try XcodeProj(path: projectPath)
|
||||
let stringDiff = newProjectString != oldProjectString
|
||||
if newProject != oldProject || stringDiff {
|
||||
var message = "\(projectPath.string) has changed. If change is legitimate commit the change and run test again"
|
||||
if stringDiff {
|
||||
message += ":\n\n\(pbxProjPath):\n\(prettyFirstDifferenceBetweenStrings(oldProjectString, newProjectString))"
|
||||
}
|
||||
throw failure(message, file: file, line: line)
|
||||
}
|
||||
|
||||
return newProject
|
||||
}
|
||||
@@ -3,54 +3,44 @@ import ProjectSpec
|
||||
import Spectre
|
||||
import XcodeGenKit
|
||||
import xcproj
|
||||
import XCTest
|
||||
import Yams
|
||||
|
||||
func projectGeneratorTests() {
|
||||
fileprivate let app = Target(
|
||||
name: "MyApp",
|
||||
type: .application,
|
||||
platform: .iOS,
|
||||
settings: Settings(buildSettings: ["SETTING_1": "VALUE"]),
|
||||
dependencies: [Dependency(type: .target, reference: "MyFramework")]
|
||||
)
|
||||
|
||||
func getXcodeProject(_ project: Project) throws -> XcodeProj {
|
||||
let generator = ProjectGenerator(project: project)
|
||||
return try generator.generateXcodeProject()
|
||||
}
|
||||
fileprivate let framework = Target(
|
||||
name: "MyFramework",
|
||||
type: .framework,
|
||||
platform: .iOS,
|
||||
settings: Settings(buildSettings: ["SETTING_2": "VALUE"])
|
||||
)
|
||||
|
||||
func getPbxProj(_ project: Project) throws -> PBXProj {
|
||||
let xcodeProject = try getXcodeProject(project).pbxproj
|
||||
try xcodeProject.validate()
|
||||
return xcodeProject
|
||||
}
|
||||
fileprivate let uiTest = Target(
|
||||
name: "MyAppUITests",
|
||||
type: .uiTestBundle,
|
||||
platform: .iOS,
|
||||
settings: Settings(buildSettings: ["SETTING_3": "VALUE"]),
|
||||
dependencies: [Dependency(type: .target, reference: "MyApp")]
|
||||
)
|
||||
|
||||
describe("Project Generator") {
|
||||
fileprivate let targets = [app, framework, uiTest]
|
||||
|
||||
let application = Target(
|
||||
name: "MyApp",
|
||||
type: .application,
|
||||
platform: .iOS,
|
||||
settings: Settings(buildSettings: ["SETTING_1": "VALUE"]),
|
||||
dependencies: [Dependency(type: .target, reference: "MyFramework")]
|
||||
)
|
||||
class ProjectGeneratorTests: XCTestCase {
|
||||
|
||||
let framework = Target(
|
||||
name: "MyFramework",
|
||||
type: .framework,
|
||||
platform: .iOS,
|
||||
settings: Settings(buildSettings: ["SETTING_2": "VALUE"])
|
||||
)
|
||||
func testOptions() throws {
|
||||
|
||||
let uiTest = Target(
|
||||
name: "MyAppUITests",
|
||||
type: .uiTestBundle,
|
||||
platform: .iOS,
|
||||
settings: Settings(buildSettings: ["SETTING_3": "VALUE"]),
|
||||
dependencies: [Dependency(type: .target, reference: "MyApp")]
|
||||
)
|
||||
|
||||
let targets = [application, framework, uiTest]
|
||||
|
||||
$0.describe("Options") {
|
||||
describe {
|
||||
|
||||
$0.it("generates bundle id") {
|
||||
let options = SpecOptions(bundleIdPrefix: "com.test")
|
||||
let project = Project(basePath: "", name: "test", targets: [framework], options: options)
|
||||
let pbxProj = try getPbxProj(project)
|
||||
let pbxProj = try project.generatePbxProj()
|
||||
guard let target = pbxProj.objects.nativeTargets.first?.value,
|
||||
let buildConfigList = target.buildConfigurationList,
|
||||
let buildConfigs = pbxProj.objects.configurationLists.getReference(buildConfigList),
|
||||
@@ -64,7 +54,7 @@ func projectGeneratorTests() {
|
||||
$0.it("clears setting presets") {
|
||||
let options = SpecOptions(settingPresets: .none)
|
||||
let project = Project(basePath: "", name: "test", targets: [framework], options: options)
|
||||
let pbxProj = try getPbxProj(project)
|
||||
let pbxProj = try project.generatePbxProj()
|
||||
let allSettings = pbxProj.objects.buildConfigurations.referenceValues.reduce([:]) { $0.merged($1.buildSettings) }.keys.sorted()
|
||||
try expect(allSettings) == ["SETTING_2"]
|
||||
}
|
||||
@@ -72,7 +62,7 @@ func projectGeneratorTests() {
|
||||
$0.it("generates development language") {
|
||||
let options = SpecOptions(developmentLanguage: "de")
|
||||
let project = Project(basePath: "", name: "test", options: options)
|
||||
let pbxProj = try getPbxProj(project)
|
||||
let pbxProj = try project.generatePbxProj()
|
||||
guard let pbxProject = pbxProj.objects.projects.first?.value else {
|
||||
throw failure("Could't find PBXProject")
|
||||
}
|
||||
@@ -100,7 +90,7 @@ func projectGeneratorTests() {
|
||||
$0.it("uses the default configuration name") {
|
||||
let options = SpecOptions(defaultConfig: "Bconfig")
|
||||
let project = Project(basePath: "", name: "test", configs: [Config(name: "Aconfig"), Config(name: "Bconfig")], targets: [framework], options: options)
|
||||
let pbxProject = try getPbxProj(project)
|
||||
let pbxProject = try project.generatePbxProj()
|
||||
|
||||
guard let projectConfigListReference = pbxProject.objects.projects.values.first?.buildConfigurationList,
|
||||
let defaultConfigurationName = pbxProject.objects.configurationLists[projectConfigListReference]?.defaultConfigurationName
|
||||
@@ -111,12 +101,14 @@ func projectGeneratorTests() {
|
||||
try expect(defaultConfigurationName) == "Bconfig"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$0.describe("Config") {
|
||||
func testConfigGenerator() {
|
||||
describe {
|
||||
|
||||
$0.it("generates config defaults") {
|
||||
let project = Project(basePath: "", name: "test")
|
||||
let pbxProj = try getPbxProj(project)
|
||||
let pbxProj = try project.generatePbxProj()
|
||||
let configs = pbxProj.objects.buildConfigurations.referenceValues
|
||||
try expect(configs.count) == 2
|
||||
try expect(configs).contains(name: "Debug")
|
||||
@@ -129,7 +121,7 @@ func projectGeneratorTests() {
|
||||
name: "test",
|
||||
configs: [Config(name: "config1"), Config(name: "config2")]
|
||||
)
|
||||
let pbxProj = try getPbxProj(project)
|
||||
let pbxProj = try project.generatePbxProj()
|
||||
let configs = pbxProj.objects.buildConfigurations.referenceValues
|
||||
try expect(configs.count) == 2
|
||||
try expect(configs).contains(name: "config1")
|
||||
@@ -142,7 +134,7 @@ func projectGeneratorTests() {
|
||||
name: "test",
|
||||
configs: [Config(name: "config")]
|
||||
)
|
||||
let pbxProj = try getPbxProj(project)
|
||||
let pbxProj = try project.generatePbxProj()
|
||||
guard let config = pbxProj.objects.buildConfigurations.first?.value else {
|
||||
throw failure("configuration not found")
|
||||
}
|
||||
@@ -193,35 +185,37 @@ func projectGeneratorTests() {
|
||||
try expect(buildSettings["SETTING2"] as? String) == "VALUE2"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$0.describe("Targets") {
|
||||
func testTargets() {
|
||||
describe {
|
||||
|
||||
let project = Project(basePath: "", name: "test", targets: targets)
|
||||
|
||||
$0.it("generates targets") {
|
||||
let pbxProject = try getPbxProj(project)
|
||||
let pbxProject = try project.generatePbxProj()
|
||||
let nativeTargets = pbxProject.objects.nativeTargets.referenceValues
|
||||
try expect(nativeTargets.count) == 3
|
||||
try expect(nativeTargets.contains { $0.name == application.name }).beTrue()
|
||||
try expect(nativeTargets.contains { $0.name == app.name }).beTrue()
|
||||
try expect(nativeTargets.contains { $0.name == framework.name }).beTrue()
|
||||
try expect(nativeTargets.contains { $0.name == uiTest.name }).beTrue()
|
||||
}
|
||||
|
||||
$0.it("generates target attributes") {
|
||||
var appTargetWithAttributes = application
|
||||
var appTargetWithAttributes = app
|
||||
appTargetWithAttributes.settings.buildSettings["DEVELOPMENT_TEAM"] = "123"
|
||||
appTargetWithAttributes.attributes = ["ProvisioningStyle": "Automatic"]
|
||||
|
||||
var testTargetWithAttributes = uiTest
|
||||
testTargetWithAttributes.settings.buildSettings["CODE_SIGN_STYLE"] = "Manual"
|
||||
var project = Project(basePath: "", name: "test", targets: [appTargetWithAttributes, framework, testTargetWithAttributes])
|
||||
let pbxProject = try getPbxProj(project)
|
||||
let project = Project(basePath: "", name: "test", targets: [appTargetWithAttributes, framework, testTargetWithAttributes])
|
||||
let pbxProject = try project.generatePbxProj()
|
||||
|
||||
guard let targetAttributes = pbxProject.objects.projects.referenceValues.first?.attributes["TargetAttributes"] as? [String: [String: Any]] else {
|
||||
throw failure("Couldn't find Project TargetAttributes")
|
||||
}
|
||||
|
||||
guard let appTarget = pbxProject.objects.targets(named: application.name).first else {
|
||||
guard let appTarget = pbxProject.objects.targets(named: app.name).first else {
|
||||
throw failure("Couldn't find App Target")
|
||||
}
|
||||
|
||||
@@ -239,7 +233,7 @@ func projectGeneratorTests() {
|
||||
let target = Target(name: "Target", type: .application, platform: .watchOS, deploymentTarget: "2.0")
|
||||
let project = Project(basePath: "", name: "", targets: [target], options: .init(deploymentTarget: DeploymentTarget(iOS: "10.0", watchOS: "3.0")))
|
||||
|
||||
let pbxProject = try getPbxProj(project)
|
||||
let pbxProject = try project.generatePbxProj()
|
||||
|
||||
guard let projectConfigListReference = pbxProject.objects.projects.values.first?.buildConfigurationList,
|
||||
let projectConfigReference = pbxProject.objects.configurationLists[projectConfigListReference]?.buildConfigurations.first,
|
||||
@@ -265,20 +259,20 @@ func projectGeneratorTests() {
|
||||
}
|
||||
|
||||
$0.it("generates dependencies") {
|
||||
let pbxProject = try getPbxProj(project)
|
||||
let pbxProject = try project.generatePbxProj()
|
||||
|
||||
let nativeTargets = pbxProject.objects.nativeTargets.objectReferences
|
||||
let dependencies = pbxProject.objects.targetDependencies.objectReferences
|
||||
try expect(dependencies.count) == 2
|
||||
try expect(dependencies[0].object.target) == nativeTargets.first { $0.object.name == framework.name }!.reference
|
||||
try expect(dependencies[1].object.target) == nativeTargets.first { $0.object.name == application.name }!.reference
|
||||
try expect(dependencies[1].object.target) == nativeTargets.first { $0.object.name == app.name }!.reference
|
||||
}
|
||||
|
||||
$0.it("generates run scripts") {
|
||||
var scriptSpec = project
|
||||
scriptSpec.targets[0].prebuildScripts = [BuildScript(script: .script("script1"))]
|
||||
scriptSpec.targets[0].postbuildScripts = [BuildScript(script: .script("script2"))]
|
||||
let pbxProject = try getPbxProj(scriptSpec)
|
||||
let pbxProject = try scriptSpec.generatePbxProj()
|
||||
|
||||
guard let nativeTarget = pbxProject.objects.nativeTargets.referenceValues
|
||||
.first(where: { $0.buildPhases.count >= 2 }) else {
|
||||
@@ -316,15 +310,17 @@ func projectGeneratorTests() {
|
||||
targets: [target1, target2]
|
||||
)
|
||||
|
||||
_ = try getPbxProj(project)
|
||||
_ = try project.generatePbxProj()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$0.describe("Schemes") {
|
||||
func testSchemes() {
|
||||
describe {
|
||||
|
||||
let buildTarget = Scheme.BuildTarget(target: application.name)
|
||||
let buildTarget = Scheme.BuildTarget(target: app.name)
|
||||
$0.it("generates scheme") {
|
||||
let preAction = Scheme.ExecutionAction(name: "Script", script: "echo Starting", settingsTarget: application.name)
|
||||
let preAction = Scheme.ExecutionAction(name: "Script", script: "echo Starting", settingsTarget: app.name)
|
||||
let scheme = Scheme(
|
||||
name: "MyScheme",
|
||||
build: Scheme.Build(targets: [buildTarget], preActions: [preAction])
|
||||
@@ -332,12 +328,12 @@ func projectGeneratorTests() {
|
||||
let project = Project(
|
||||
basePath: "",
|
||||
name: "test",
|
||||
targets: [application, framework],
|
||||
targets: [app, framework],
|
||||
schemes: [scheme]
|
||||
)
|
||||
let xcodeProject = try getXcodeProject(project)
|
||||
let xcodeProject = try project.generateXcodeProject()
|
||||
guard let target = xcodeProject.pbxproj.objects.nativeTargets.objectReferences
|
||||
.first(where: { $0.object.name == application.name }) else {
|
||||
.first(where: { $0.object.name == app.name }) else {
|
||||
throw failure("Target not found")
|
||||
}
|
||||
guard let xcscheme = xcodeProject.sharedData?.schemes.first else {
|
||||
@@ -391,20 +387,17 @@ func projectGeneratorTests() {
|
||||
let project = Project(
|
||||
basePath: "",
|
||||
name: "test",
|
||||
targets: [application, framework],
|
||||
targets: [app, framework],
|
||||
schemes: [scheme]
|
||||
)
|
||||
let xcodeProject = try getXcodeProject(project)
|
||||
|
||||
guard let target = xcodeProject.pbxproj.objects.nativeTargets.objectReferences
|
||||
.first(where: { $0.object.name == application.name }) else {
|
||||
throw failure("Target not found")
|
||||
}
|
||||
let xcodeProject = try project.generateXcodeProject()
|
||||
|
||||
guard let xcscheme = xcodeProject.sharedData?.schemes.first else {
|
||||
throw failure("Scheme not found")
|
||||
}
|
||||
|
||||
try expect(xcodeProject.pbxproj.objects.nativeTargets.objectReferences
|
||||
.contains(where: { $0.object.name == app.name })).beTrue()
|
||||
try expect(xcscheme.launchAction?.environmentVariables) == runVariables
|
||||
try expect(xcscheme.testAction?.environmentVariables).to.beNil()
|
||||
try expect(xcscheme.profileAction?.environmentVariables).to.beNil()
|
||||
@@ -412,7 +405,7 @@ func projectGeneratorTests() {
|
||||
|
||||
$0.it("generates target schemes from config variant") {
|
||||
let configVariants = ["Test", "Production"]
|
||||
var target = application
|
||||
var target = app
|
||||
target.scheme = TargetScheme(configVariants: configVariants)
|
||||
let configs: [Config] = [
|
||||
Config(name: "Test Debug", type: .debug),
|
||||
@@ -422,12 +415,12 @@ func projectGeneratorTests() {
|
||||
]
|
||||
|
||||
let project = Project(basePath: "", name: "test", configs: configs, targets: [target, framework])
|
||||
let xcodeProject = try getXcodeProject(project)
|
||||
let xcodeProject = try project.generateXcodeProject()
|
||||
|
||||
try expect(xcodeProject.sharedData?.schemes.count) == 2
|
||||
|
||||
guard let nativeTarget = xcodeProject.pbxproj.objects.nativeTargets.objectReferences
|
||||
.first(where: { $0.object.name == application.name }) else {
|
||||
.first(where: { $0.object.name == app.name }) else {
|
||||
throw failure("Target not found")
|
||||
}
|
||||
guard let xcscheme = xcodeProject.sharedData?.schemes
|
||||
@@ -448,11 +441,11 @@ func projectGeneratorTests() {
|
||||
|
||||
$0.it("generates environment variables for target schemes") {
|
||||
let variables: [XCScheme.EnvironmentVariable] = [XCScheme.EnvironmentVariable(variable: "env", value: "var", enabled: false)]
|
||||
var target = application
|
||||
var target = app
|
||||
target.scheme = TargetScheme(environmentVariables: variables)
|
||||
|
||||
let project = Project(basePath: "", name: "test", targets: [target, framework])
|
||||
let xcodeProject = try getXcodeProject(project)
|
||||
let xcodeProject = try project.generateXcodeProject()
|
||||
|
||||
try expect(xcodeProject.sharedData?.schemes.count) == 1
|
||||
|
||||
@@ -466,14 +459,14 @@ func projectGeneratorTests() {
|
||||
}
|
||||
|
||||
$0.it("generates pre and post actions for target schemes") {
|
||||
var target = application
|
||||
var target = app
|
||||
target.scheme = TargetScheme(
|
||||
preActions: [.init(name: "Run", script: "do")],
|
||||
postActions: [.init(name: "Run2", script: "post", settingsTarget: "MyApp")]
|
||||
)
|
||||
|
||||
let project = Project(basePath: "", name: "test", targets: [target, framework])
|
||||
let xcodeProject = try getXcodeProject(project)
|
||||
let xcodeProject = try project.generateXcodeProject()
|
||||
|
||||
try expect(xcodeProject.sharedData?.schemes.count) == 1
|
||||
|
||||
@@ -491,553 +484,6 @@ func projectGeneratorTests() {
|
||||
try expect(xcscheme.testAction?.postActions.first?.environmentBuildable?.blueprintName) == "MyApp"
|
||||
}
|
||||
}
|
||||
|
||||
$0.describe("Sources") {
|
||||
|
||||
let directoryPath = Path("TestDirectory")
|
||||
let outOfRootPath = Path("OtherDirectory")
|
||||
|
||||
func createDirectories(_ directories: String) throws {
|
||||
|
||||
let yaml = try Yams.load(yaml: directories)!
|
||||
|
||||
func getFiles(_ file: Any, path: Path) -> [Path] {
|
||||
if let array = file as? [Any] {
|
||||
return array.flatMap { getFiles($0, path: path) }
|
||||
} else if let string = file as? String {
|
||||
return [path + string]
|
||||
} else if let dictionary = file as? [String: Any] {
|
||||
var array: [Path] = []
|
||||
for (key, value) in dictionary {
|
||||
array += getFiles(value, path: path + key)
|
||||
}
|
||||
return array
|
||||
} else {
|
||||
return []
|
||||
}
|
||||
}
|
||||
|
||||
let files = getFiles(yaml, path: directoryPath).filter { $0.extension != nil }
|
||||
for file in files {
|
||||
try file.parent().mkpath()
|
||||
try file.write("")
|
||||
}
|
||||
}
|
||||
|
||||
func removeDirectories() {
|
||||
try? directoryPath.delete()
|
||||
try? outOfRootPath.delete()
|
||||
}
|
||||
|
||||
$0.before {
|
||||
removeDirectories()
|
||||
}
|
||||
|
||||
$0.after {
|
||||
removeDirectories()
|
||||
}
|
||||
|
||||
$0.it("generates source groups") {
|
||||
let directories = """
|
||||
Sources:
|
||||
A:
|
||||
- a.swift
|
||||
- B:
|
||||
- b.swift
|
||||
"""
|
||||
try createDirectories(directories)
|
||||
|
||||
let target = Target(name: "Test", type: .application, platform: .iOS, sources: ["Sources"])
|
||||
let project = Project(basePath: directoryPath, name: "Test", targets: [target])
|
||||
|
||||
let pbxProj = try getPbxProj(project)
|
||||
try pbxProj.expectFile(paths: ["Sources", "A", "a.swift"], buildPhase: .sources)
|
||||
try pbxProj.expectFile(paths: ["Sources", "A", "B", "b.swift"], buildPhase: .sources)
|
||||
}
|
||||
|
||||
$0.it("supports frameworks in sources") {
|
||||
let directories = """
|
||||
Sources:
|
||||
- Foo.framework
|
||||
- Bar.swift
|
||||
"""
|
||||
|
||||
try createDirectories(directories)
|
||||
|
||||
let target = Target(name: "Test", type: .application, platform: .iOS, sources: ["Sources"])
|
||||
let project = Project(basePath: directoryPath, name: "Test", targets: [target])
|
||||
let pbxProj = try getPbxProj(project)
|
||||
try pbxProj.expectFile(paths: ["Sources", "Bar.swift"], buildPhase: .sources)
|
||||
let buildPhase = pbxProj.objects.copyFilesBuildPhases.referenceValues.first
|
||||
try expect(buildPhase?.dstSubfolderSpec) == .frameworks
|
||||
let fileReference = pbxProj.getFileReference(paths: ["Sources", "Foo.framework"],
|
||||
names: ["Sources", "Foo.framework"])?.reference ?? ""
|
||||
let buildFile = pbxProj.objects.buildFiles.objectReferences
|
||||
.first(where: { $0.object.fileRef == fileReference })?.reference ?? ""
|
||||
try expect(buildPhase?.files.count) == 1
|
||||
try expect(buildPhase?.files.contains(buildFile)) == true
|
||||
}
|
||||
|
||||
$0.it("generates core data models") {
|
||||
let directories = """
|
||||
Sources:
|
||||
model.xcdatamodeld:
|
||||
- model.xcdatamodel
|
||||
"""
|
||||
try createDirectories(directories)
|
||||
|
||||
let target = Target(name: "Test", type: .application, platform: .iOS, sources: ["Sources"])
|
||||
let project = Project(basePath: directoryPath, name: "Test", targets: [target])
|
||||
|
||||
let pbxProj = try getPbxProj(project)
|
||||
guard let fileReference = pbxProj.objects.fileReferences.first(where: { $0.value.nameOrPath == "model.xcdatamodel" }) else {
|
||||
throw failure("Couldn't find model file reference")
|
||||
}
|
||||
guard let versionGroup = pbxProj.objects.versionGroups.values.first else {
|
||||
throw failure("Couldn't find version group")
|
||||
}
|
||||
try expect(versionGroup.currentVersion) == fileReference.key
|
||||
try expect(versionGroup.children) == [fileReference.key]
|
||||
try expect(versionGroup.path) == "model.xcdatamodeld"
|
||||
try expect(fileReference.value.path) == "model.xcdatamodel"
|
||||
}
|
||||
|
||||
$0.it("handles duplicate names") {
|
||||
let directories = """
|
||||
Sources:
|
||||
- a.swift
|
||||
- a:
|
||||
- a.swift
|
||||
- a:
|
||||
- a.swift
|
||||
|
||||
"""
|
||||
try createDirectories(directories)
|
||||
|
||||
let target = Target(name: "Test", type: .application, platform: .iOS, sources: ["Sources"])
|
||||
let project = Project(
|
||||
basePath: directoryPath,
|
||||
name: "Test",
|
||||
targets: [target],
|
||||
fileGroups: ["Sources"]
|
||||
)
|
||||
|
||||
let pbxProj = try getPbxProj(project)
|
||||
try pbxProj.expectFile(paths: ["Sources", "a.swift"], buildPhase: .sources)
|
||||
try pbxProj.expectFile(paths: ["Sources", "a", "a.swift"], buildPhase: .sources)
|
||||
try pbxProj.expectFile(paths: ["Sources", "a", "a", "a.swift"], buildPhase: .sources)
|
||||
}
|
||||
|
||||
$0.it("renames sources") {
|
||||
let directories = """
|
||||
Sources:
|
||||
- a.swift
|
||||
OtherSource:
|
||||
- b.swift
|
||||
"""
|
||||
try createDirectories(directories)
|
||||
|
||||
let target = Target(name: "Test", type: .application, platform: .iOS, sources: [
|
||||
TargetSource(path: "Sources", name: "NewSource"),
|
||||
TargetSource(path: "OtherSource/b.swift", name: "c.swift"),
|
||||
])
|
||||
let project = Project(basePath: directoryPath, name: "Test", targets: [target])
|
||||
|
||||
let pbxProj = try getPbxProj(project)
|
||||
try pbxProj.expectFile(paths: ["Sources", "a.swift"], names: ["NewSource", "a.swift"], buildPhase: .sources)
|
||||
try pbxProj.expectFile(paths: ["OtherSource", "b.swift"], names: ["OtherSource", "c.swift"], buildPhase: .sources)
|
||||
}
|
||||
|
||||
$0.it("excludes sources") {
|
||||
let directories = """
|
||||
Sources:
|
||||
- A:
|
||||
- a.swift
|
||||
- B:
|
||||
- b.swift
|
||||
- b.ignored
|
||||
- a.ignored
|
||||
- B:
|
||||
- b.swift
|
||||
- D:
|
||||
- d.h
|
||||
- d.m
|
||||
- E:
|
||||
- e.jpg
|
||||
- e.h
|
||||
- e.m
|
||||
- F:
|
||||
- f.swift
|
||||
- G:
|
||||
- H:
|
||||
- h.swift
|
||||
- types:
|
||||
- a.swift
|
||||
- a.m
|
||||
- a.h
|
||||
- a.x
|
||||
- numbers:
|
||||
- file1.a
|
||||
- file2.a
|
||||
- file3.a
|
||||
- file4.a
|
||||
- partial:
|
||||
- file_part
|
||||
- ignore.file
|
||||
- a.ignored
|
||||
- project.xcodeproj:
|
||||
- project.pbxproj
|
||||
"""
|
||||
try createDirectories(directories)
|
||||
|
||||
let excludes = [
|
||||
"B",
|
||||
"d.m",
|
||||
"E/F/*.swift",
|
||||
"G/H/",
|
||||
"types/*.[hx]",
|
||||
"numbers/file[2-3].a",
|
||||
"partial/*_part",
|
||||
"ignore.file",
|
||||
"*.ignored",
|
||||
"*.xcodeproj",
|
||||
// not supported
|
||||
// "**/*.ignored",
|
||||
]
|
||||
|
||||
let target = Target(name: "Test", type: .application, platform: .iOS, sources: [TargetSource(path: "Sources", excludes: excludes)])
|
||||
let project = Project(basePath: directoryPath, name: "Test", targets: [target])
|
||||
|
||||
let pbxProj = try getPbxProj(project)
|
||||
try pbxProj.expectFile(paths: ["Sources", "A", "a.swift"])
|
||||
try pbxProj.expectFile(paths: ["Sources", "D", "d.h"])
|
||||
try pbxProj.expectFile(paths: ["Sources", "D", "d.m"])
|
||||
try pbxProj.expectFile(paths: ["Sources", "E", "e.jpg"])
|
||||
try pbxProj.expectFile(paths: ["Sources", "E", "e.m"])
|
||||
try pbxProj.expectFile(paths: ["Sources", "E", "e.h"])
|
||||
try pbxProj.expectFile(paths: ["Sources", "types", "a.swift"])
|
||||
try pbxProj.expectFile(paths: ["Sources", "numbers", "file1.a"])
|
||||
try pbxProj.expectFile(paths: ["Sources", "numbers", "file4.a"])
|
||||
try pbxProj.expectFileMissing(paths: ["Sources", "B", "b.swift"])
|
||||
try pbxProj.expectFileMissing(paths: ["Sources", "E", "F", "f.swift"])
|
||||
try pbxProj.expectFileMissing(paths: ["Sources", "G", "H", "h.swift"])
|
||||
try pbxProj.expectFileMissing(paths: ["Sources", "types", "a.h"])
|
||||
try pbxProj.expectFileMissing(paths: ["Sources", "types", "a.x"])
|
||||
try pbxProj.expectFileMissing(paths: ["Sources", "numbers", "file2.a"])
|
||||
try pbxProj.expectFileMissing(paths: ["Sources", "numbers", "file3.a"])
|
||||
try pbxProj.expectFileMissing(paths: ["Sources", "partial", "file_part"])
|
||||
try pbxProj.expectFileMissing(paths: ["Sources", "a.ignored"])
|
||||
try pbxProj.expectFileMissing(paths: ["Sources", "ignore.file"])
|
||||
try pbxProj.expectFileMissing(paths: ["Sources", "project.xcodeproj"])
|
||||
}
|
||||
|
||||
$0.it("generates file sources") {
|
||||
let directories = """
|
||||
Sources:
|
||||
A:
|
||||
- a.swift
|
||||
- Assets.xcassets
|
||||
- B:
|
||||
- b.swift
|
||||
- c.jpg
|
||||
"""
|
||||
try createDirectories(directories)
|
||||
|
||||
let target = Target(name: "Test", type: .application, platform: .iOS, sources: [
|
||||
"Sources/A/a.swift",
|
||||
"Sources/A/B/b.swift",
|
||||
"Sources/A/Assets.xcassets",
|
||||
"Sources/A/B/c.jpg",
|
||||
])
|
||||
let project = Project(basePath: directoryPath, name: "Test", targets: [target])
|
||||
|
||||
let pbxProj = try getPbxProj(project)
|
||||
try pbxProj.expectFile(paths: ["Sources/A", "a.swift"], names: ["A", "a.swift"], buildPhase: .sources)
|
||||
try pbxProj.expectFile(paths: ["Sources/A/B", "b.swift"], names: ["B", "b.swift"], buildPhase: .sources)
|
||||
try pbxProj.expectFile(paths: ["Sources/A/B", "c.jpg"], names: ["B", "c.jpg"], buildPhase: .resources)
|
||||
try pbxProj.expectFile(paths: ["Sources/A", "Assets.xcassets"], names: ["A", "Assets.xcassets"], buildPhase: .resources)
|
||||
}
|
||||
|
||||
$0.it("generates shared sources") {
|
||||
let directories = """
|
||||
Sources:
|
||||
A:
|
||||
- a.swift
|
||||
- B:
|
||||
- b.swift
|
||||
- c.jpg
|
||||
"""
|
||||
try createDirectories(directories)
|
||||
|
||||
let target1 = Target(name: "Test1", type: .framework, platform: .iOS, sources: ["Sources"])
|
||||
let target2 = Target(name: "Test2", type: .framework, platform: .tvOS, sources: ["Sources"])
|
||||
let project = Project(basePath: directoryPath, name: "Test", targets: [target1, target2])
|
||||
|
||||
_ = try getPbxProj(project)
|
||||
// TODO: check there are build files for both targets
|
||||
}
|
||||
|
||||
$0.it("generates intermediate groups") {
|
||||
|
||||
let directories = """
|
||||
Sources:
|
||||
A:
|
||||
- b.swift
|
||||
F:
|
||||
- G:
|
||||
- h.swift
|
||||
"""
|
||||
try createDirectories(directories)
|
||||
let outOfSourceFile = outOfRootPath + "C/D/e.swift"
|
||||
try outOfSourceFile.parent().mkpath()
|
||||
try outOfSourceFile.write("")
|
||||
|
||||
let target = Target(name: "Test", type: .application, platform: .iOS, sources: [
|
||||
"Sources/A/b.swift",
|
||||
"Sources/F/G/h.swift",
|
||||
"../OtherDirectory/C/D/e.swift",
|
||||
])
|
||||
let options = SpecOptions(createIntermediateGroups: true)
|
||||
let project = Project(basePath: directoryPath, name: "Test", targets: [target], options: options)
|
||||
|
||||
let pbxProj = try getPbxProj(project)
|
||||
try pbxProj.expectFile(paths: ["Sources", "A", "b.swift"], buildPhase: .sources)
|
||||
try pbxProj.expectFile(paths: ["Sources", "F", "G", "h.swift"], buildPhase: .sources)
|
||||
try pbxProj.expectFile(paths: [(outOfRootPath + "C/D").string, "e.swift"], names: ["D", "e.swift"], buildPhase: .sources)
|
||||
}
|
||||
|
||||
$0.it("generates folder references") {
|
||||
let directories = """
|
||||
Sources:
|
||||
A:
|
||||
- a.resource
|
||||
- b.resource
|
||||
"""
|
||||
try createDirectories(directories)
|
||||
|
||||
let target = Target(name: "Test", type: .application, platform: .iOS, sources: [
|
||||
TargetSource(path: "Sources/A", type: .folder),
|
||||
])
|
||||
let project = Project(basePath: directoryPath, name: "Test", targets: [target])
|
||||
|
||||
let pbxProj = try getPbxProj(project)
|
||||
try pbxProj.expectFile(paths: ["Sources/A"], names: ["A"], buildPhase: .resources)
|
||||
try pbxProj.expectFileMissing(paths: ["Sources", "A", "a.swift"])
|
||||
}
|
||||
|
||||
$0.it("adds files to correct build phase") {
|
||||
let directories = """
|
||||
A:
|
||||
- file.swift
|
||||
- file.xcassets
|
||||
- file.h
|
||||
- Info.plist
|
||||
- file.xcconfig
|
||||
B:
|
||||
- file.swift
|
||||
- file.xcassets
|
||||
- file.h
|
||||
- Info.plist
|
||||
- file.xcconfig
|
||||
C:
|
||||
- file.swift
|
||||
- file.m
|
||||
- file.mm
|
||||
- file.cpp
|
||||
- file.c
|
||||
- file.S
|
||||
- file.h
|
||||
- file.hh
|
||||
- file.hpp
|
||||
- file.ipp
|
||||
- file.tpp
|
||||
- file.hxx
|
||||
- file.def
|
||||
- file.xcconfig
|
||||
- file.entitlements
|
||||
- file.gpx
|
||||
- file.apns
|
||||
- file.123
|
||||
- file.xcassets
|
||||
- Info.plist
|
||||
"""
|
||||
try createDirectories(directories)
|
||||
|
||||
let target = Target(name: "Test", type: .framework, platform: .iOS, sources: [
|
||||
TargetSource(path: "A", buildPhase: .resources),
|
||||
TargetSource(path: "B", buildPhase: .none),
|
||||
TargetSource(path: "C", buildPhase: nil),
|
||||
])
|
||||
let project = Project(basePath: directoryPath, name: "Test", targets: [target])
|
||||
|
||||
let pbxProj = try getPbxProj(project)
|
||||
try pbxProj.expectFile(paths: ["A", "file.swift"], buildPhase: .resources)
|
||||
try pbxProj.expectFile(paths: ["A", "file.xcassets"], buildPhase: .resources)
|
||||
try pbxProj.expectFile(paths: ["A", "file.h"], buildPhase: .resources)
|
||||
try pbxProj.expectFile(paths: ["A", "Info.plist"], buildPhase: .none)
|
||||
try pbxProj.expectFile(paths: ["A", "file.xcconfig"], buildPhase: .resources)
|
||||
|
||||
try pbxProj.expectFile(paths: ["B", "file.swift"], buildPhase: .none)
|
||||
try pbxProj.expectFile(paths: ["B", "file.xcassets"], buildPhase: .none)
|
||||
try pbxProj.expectFile(paths: ["B", "file.h"], buildPhase: .none)
|
||||
try pbxProj.expectFile(paths: ["B", "Info.plist"], buildPhase: .none)
|
||||
try pbxProj.expectFile(paths: ["B", "file.xcconfig"], buildPhase: .none)
|
||||
|
||||
try pbxProj.expectFile(paths: ["C", "file.swift"], buildPhase: .sources)
|
||||
try pbxProj.expectFile(paths: ["C", "file.m"], buildPhase: .sources)
|
||||
try pbxProj.expectFile(paths: ["C", "file.mm"], buildPhase: .sources)
|
||||
try pbxProj.expectFile(paths: ["C", "file.cpp"], buildPhase: .sources)
|
||||
try pbxProj.expectFile(paths: ["C", "file.c"], buildPhase: .sources)
|
||||
try pbxProj.expectFile(paths: ["C", "file.S"], buildPhase: .sources)
|
||||
try pbxProj.expectFile(paths: ["C", "file.h"], buildPhase: .headers)
|
||||
try pbxProj.expectFile(paths: ["C", "file.hh"], buildPhase: .headers)
|
||||
try pbxProj.expectFile(paths: ["C", "file.hpp"], buildPhase: .headers)
|
||||
try pbxProj.expectFile(paths: ["C", "file.ipp"], buildPhase: .headers)
|
||||
try pbxProj.expectFile(paths: ["C", "file.tpp"], buildPhase: .headers)
|
||||
try pbxProj.expectFile(paths: ["C", "file.hxx"], buildPhase: .headers)
|
||||
try pbxProj.expectFile(paths: ["C", "file.def"], buildPhase: .headers)
|
||||
try pbxProj.expectFile(paths: ["C", "file.xcconfig"], buildPhase: .none)
|
||||
try pbxProj.expectFile(paths: ["C", "file.entitlements"], buildPhase: .none)
|
||||
try pbxProj.expectFile(paths: ["C", "file.gpx"], buildPhase: .none)
|
||||
try pbxProj.expectFile(paths: ["C", "file.apns"], buildPhase: .none)
|
||||
try pbxProj.expectFile(paths: ["C", "file.xcconfig"], buildPhase: .none)
|
||||
try pbxProj.expectFile(paths: ["C", "file.xcconfig"], buildPhase: .none)
|
||||
try pbxProj.expectFile(paths: ["C", "file.xcconfig"], buildPhase: .none)
|
||||
try pbxProj.expectFile(paths: ["C", "file.xcassets"], buildPhase: .resources)
|
||||
try pbxProj.expectFile(paths: ["C", "file.123"], buildPhase: .resources)
|
||||
try pbxProj.expectFile(paths: ["C", "Info.plist"], buildPhase: .none)
|
||||
}
|
||||
|
||||
$0.it("duplicate TargetSource is included once in sources build phase") {
|
||||
let directories = """
|
||||
Sources:
|
||||
A:
|
||||
- a.swift
|
||||
"""
|
||||
try createDirectories(directories)
|
||||
|
||||
let target = Target(name: "Test", type: .application, platform: .iOS, sources: [
|
||||
"Sources/A/a.swift",
|
||||
"Sources/A/a.swift",
|
||||
])
|
||||
let project = Project(basePath: directoryPath, name: "Test", targets: [target])
|
||||
|
||||
let pbxProj = try getPbxProj(project)
|
||||
try pbxProj.expectFile(paths: ["Sources/A", "a.swift"], names: ["A", "a.swift"], buildPhase: .sources)
|
||||
|
||||
let sourcesBuildPhase = pbxProj.objects.buildPhases
|
||||
.first(where: { $0.1.buildPhase == BuildPhase.sources })!
|
||||
.value
|
||||
|
||||
try expect(sourcesBuildPhase.files.count) == 1
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extension PBXProj {
|
||||
|
||||
// validates that a PBXProj is correct
|
||||
// TODO: Use xclint?
|
||||
func validate() throws {
|
||||
let mainGroup = try getMainGroup()
|
||||
|
||||
func validateGroup(_ group: PBXGroup) throws {
|
||||
let hasDuplicatedChildren = group.children.count != Set(group.children).count
|
||||
if hasDuplicatedChildren {
|
||||
throw failure("Group \"\(group.nameOrPath)\" has duplicated children:\n - \(group.children.sorted().joined(separator: "\n - "))")
|
||||
}
|
||||
for child in group.children {
|
||||
if let group = objects.groups.getReference(child) {
|
||||
try validateGroup(group)
|
||||
}
|
||||
}
|
||||
}
|
||||
try validateGroup(mainGroup)
|
||||
}
|
||||
}
|
||||
|
||||
extension PBXProj {
|
||||
|
||||
/// expect a file within groups of the paths, using optional different names
|
||||
func expectFile(paths: [String], names: [String]? = nil, buildPhase: TargetSource.BuildPhase? = nil) throws {
|
||||
guard let fileReference = getFileReference(paths: paths, names: names ?? paths) else {
|
||||
var error = "Could not find file at path \(paths.joined(separator: "/").quoted)"
|
||||
if let names = names, names != paths {
|
||||
error += " and name \(names.joined(separator: "/").quoted)"
|
||||
}
|
||||
throw failure(error)
|
||||
}
|
||||
|
||||
if let buildPhase = buildPhase {
|
||||
let buildFile = objects.buildFiles.objectReferences
|
||||
.first(where: { $0.object.fileRef == fileReference.reference })
|
||||
let actualBuildPhase = buildFile
|
||||
.flatMap { buildFile in objects.buildPhases.referenceValues.first { $0.files.contains(buildFile.reference) } }?.buildPhase
|
||||
|
||||
var error: String?
|
||||
if let buildPhase = buildPhase.buildPhase {
|
||||
if actualBuildPhase != buildPhase {
|
||||
if let actualBuildPhase = actualBuildPhase {
|
||||
error = "is in the \(actualBuildPhase.rawValue) build phase instead of the expected \(buildPhase.rawValue.quoted)"
|
||||
} else {
|
||||
error = "isn't in a build phase when it's expected to be in \(buildPhase.rawValue.quoted)"
|
||||
}
|
||||
}
|
||||
} else if let actualBuildPhase = actualBuildPhase {
|
||||
error = "is in the \(actualBuildPhase.rawValue.quoted) build phase when it's expected to not be in any"
|
||||
}
|
||||
if let error = error {
|
||||
throw failure("File \(paths.joined(separator: "/").quoted) \(error)")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// expect a missing file within groups of the paths, using optional different names
|
||||
func expectFileMissing(paths: [String], names: [String]? = nil) throws {
|
||||
let names = names ?? paths
|
||||
if getFileReference(paths: paths, names: names) != nil {
|
||||
throw failure("Found unexpected file at path \(paths.joined(separator: "/").quoted) and name \(paths.joined(separator: "/").quoted)")
|
||||
}
|
||||
}
|
||||
|
||||
func getFileReference(paths: [String], names: [String]) -> ObjectReference<PBXFileReference>? {
|
||||
guard let project = objects.projects.first?.value else { return nil }
|
||||
guard let mainGroup = objects.groups.getReference(project.mainGroup) else { return nil }
|
||||
|
||||
return getFileReference(group: mainGroup, paths: paths, names: names)
|
||||
}
|
||||
|
||||
func getMainGroup() throws -> PBXGroup {
|
||||
guard let project = objects.projects.first?.value else {
|
||||
throw failure("Couldn't find project")
|
||||
}
|
||||
guard let mainGroup = objects.groups.getReference(project.mainGroup) else {
|
||||
throw failure("Couldn't find main group")
|
||||
}
|
||||
return mainGroup
|
||||
}
|
||||
|
||||
private func getFileReference(group: PBXGroup, paths: [String], names: [String]) -> ObjectReference<PBXFileReference>? {
|
||||
|
||||
guard !paths.isEmpty else { return nil }
|
||||
let path = paths.first!
|
||||
let name = names.first!
|
||||
let restOfPath = Array(paths.dropFirst())
|
||||
let restOfName = Array(names.dropFirst())
|
||||
if restOfPath.isEmpty {
|
||||
let fileReferences: [ObjectReference<PBXFileReference>] = group.children.compactMap { reference in
|
||||
if let fileReference = self.objects.fileReferences.getReference(reference) {
|
||||
return ObjectReference(reference: reference, object: fileReference)
|
||||
} else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
return fileReferences.first { $0.object.path == path && $0.object.nameOrPath == name }
|
||||
} else {
|
||||
let groups = group.children.compactMap { self.objects.groups.getReference($0) }
|
||||
guard let group = groups.first(where: { $0.path == path && $0.nameOrPath == name }) else { return nil }
|
||||
return getFileReference(group: group, paths: restOfPath, names: restOfName)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -3,46 +3,31 @@ import ProjectSpec
|
||||
import Spectre
|
||||
import XcodeGenKit
|
||||
import xcproj
|
||||
import XCTest
|
||||
|
||||
func projectSpecTests() {
|
||||
class ProjectSpecTests: XCTestCase {
|
||||
|
||||
describe("ProjectSpec") {
|
||||
func testTargetType() {
|
||||
describe {
|
||||
|
||||
let framework = Target(
|
||||
name: "MyFramework",
|
||||
type: .framework,
|
||||
platform: .iOS,
|
||||
settings: Settings(buildSettings: ["SETTING_2": "VALUE"])
|
||||
)
|
||||
let staticLibrary = Target(
|
||||
name: "MyStaticLibrary",
|
||||
type: .staticLibrary,
|
||||
platform: .iOS,
|
||||
settings: Settings(buildSettings: ["SETTING_2": "VALUE"])
|
||||
)
|
||||
let dynamicLibrary = Target(
|
||||
name: "MyDynamicLibrary",
|
||||
type: .dynamicLibrary,
|
||||
platform: .iOS,
|
||||
settings: Settings(buildSettings: ["SETTING_2": "VALUE"])
|
||||
)
|
||||
|
||||
func expectValidationError(_ project: Project, _ expectedError: SpecValidationError.ValidationError) throws {
|
||||
do {
|
||||
try project.validate()
|
||||
} catch let error as SpecValidationError {
|
||||
if !error.errors
|
||||
.contains(where: { $0.description == expectedError.description }) {
|
||||
throw failure("Supposed to fail with:\n\(expectedError)\nbut got:\n\(error.errors.map { $0.description }.joined(separator: "\n"))")
|
||||
}
|
||||
return
|
||||
} catch {
|
||||
throw failure("Supposed to fail with \"\(expectedError)\"")
|
||||
}
|
||||
throw failure("Supposed to fail with \"\(expectedError)\"")
|
||||
}
|
||||
|
||||
$0.describe("Types") {
|
||||
let framework = Target(
|
||||
name: "MyFramework",
|
||||
type: .framework,
|
||||
platform: .iOS,
|
||||
settings: Settings(buildSettings: ["SETTING_2": "VALUE"])
|
||||
)
|
||||
let staticLibrary = Target(
|
||||
name: "MyStaticLibrary",
|
||||
type: .staticLibrary,
|
||||
platform: .iOS,
|
||||
settings: Settings(buildSettings: ["SETTING_2": "VALUE"])
|
||||
)
|
||||
let dynamicLibrary = Target(
|
||||
name: "MyDynamicLibrary",
|
||||
type: .dynamicLibrary,
|
||||
platform: .iOS,
|
||||
settings: Settings(buildSettings: ["SETTING_2": "VALUE"])
|
||||
)
|
||||
$0.it("is a framework when it has the right extension") {
|
||||
try expect(framework.type.isFramework).to.beTrue()
|
||||
}
|
||||
@@ -52,8 +37,10 @@ func projectSpecTests() {
|
||||
try expect(dynamicLibrary.type.isLibrary).to.beTrue()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$0.describe("Deployment Target") {
|
||||
func testDeploymentTarget() {
|
||||
describe {
|
||||
|
||||
$0.it("has correct build setting") {
|
||||
try expect(Platform.iOS.deploymentTargetSetting) == "IPHONEOS_DEPLOYMENT_TARGET"
|
||||
@@ -77,8 +64,10 @@ func projectSpecTests() {
|
||||
try expect(Version(2.1).deploymentTarget) == "2.1"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$0.describe("Validation") {
|
||||
func testValidation() {
|
||||
describe {
|
||||
|
||||
let baseProject = Project(basePath: "", name: "", configs: [Config(name: "invalid")])
|
||||
let invalidSettings = Settings(
|
||||
@@ -176,3 +165,18 @@ func projectSpecTests() {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fileprivate func expectValidationError(_ project: Project, _ expectedError: SpecValidationError.ValidationError, file: String = #file, line: Int = #line) throws {
|
||||
do {
|
||||
try project.validate()
|
||||
} catch let error as SpecValidationError {
|
||||
if !error.errors
|
||||
.contains(where: { $0.description == expectedError.description }) {
|
||||
throw failure("Supposed to fail with:\n\(expectedError)\nbut got:\n\(error.errors.map { $0.description }.joined(separator: "\n"))", file: file, line: line)
|
||||
}
|
||||
return
|
||||
} catch {
|
||||
throw failure("Supposed to fail with \"\(expectedError)\"", file: file, line: line)
|
||||
}
|
||||
throw failure("Supposed to fail with \"\(expectedError)\"", file: file, line: line)
|
||||
}
|
||||
|
||||
@@ -0,0 +1,541 @@
|
||||
import PathKit
|
||||
import ProjectSpec
|
||||
import Spectre
|
||||
import XcodeGenKit
|
||||
import xcproj
|
||||
import XCTest
|
||||
import Yams
|
||||
|
||||
class SourceGeneratorTests: XCTestCase {
|
||||
|
||||
func testSourceGenerator() {
|
||||
describe {
|
||||
|
||||
let directoryPath = Path("TestDirectory")
|
||||
let outOfRootPath = Path("OtherDirectory")
|
||||
|
||||
func createDirectories(_ directories: String) throws {
|
||||
|
||||
let yaml = try Yams.load(yaml: directories)!
|
||||
|
||||
func getFiles(_ file: Any, path: Path) -> [Path] {
|
||||
if let array = file as? [Any] {
|
||||
return array.flatMap { getFiles($0, path: path) }
|
||||
} else if let string = file as? String {
|
||||
return [path + string]
|
||||
} else if let dictionary = file as? [String: Any] {
|
||||
var array: [Path] = []
|
||||
for (key, value) in dictionary {
|
||||
array += getFiles(value, path: path + key)
|
||||
}
|
||||
return array
|
||||
} else {
|
||||
return []
|
||||
}
|
||||
}
|
||||
|
||||
let files = getFiles(yaml, path: directoryPath).filter { $0.extension != nil }
|
||||
for file in files {
|
||||
try file.parent().mkpath()
|
||||
try file.write("")
|
||||
}
|
||||
}
|
||||
|
||||
func removeDirectories() {
|
||||
try? directoryPath.delete()
|
||||
try? outOfRootPath.delete()
|
||||
}
|
||||
|
||||
$0.before {
|
||||
removeDirectories()
|
||||
}
|
||||
|
||||
$0.after {
|
||||
removeDirectories()
|
||||
}
|
||||
|
||||
$0.it("generates source groups") {
|
||||
let directories = """
|
||||
Sources:
|
||||
A:
|
||||
- a.swift
|
||||
- B:
|
||||
- b.swift
|
||||
"""
|
||||
try createDirectories(directories)
|
||||
|
||||
let target = Target(name: "Test", type: .application, platform: .iOS, sources: ["Sources"])
|
||||
let project = Project(basePath: directoryPath, name: "Test", targets: [target])
|
||||
|
||||
let pbxProj = try project.generatePbxProj()
|
||||
try pbxProj.expectFile(paths: ["Sources", "A", "a.swift"], buildPhase: .sources)
|
||||
try pbxProj.expectFile(paths: ["Sources", "A", "B", "b.swift"], buildPhase: .sources)
|
||||
}
|
||||
|
||||
$0.it("supports frameworks in sources") {
|
||||
let directories = """
|
||||
Sources:
|
||||
- Foo.framework
|
||||
- Bar.swift
|
||||
"""
|
||||
|
||||
try createDirectories(directories)
|
||||
|
||||
let target = Target(name: "Test", type: .application, platform: .iOS, sources: ["Sources"])
|
||||
let project = Project(basePath: directoryPath, name: "Test", targets: [target])
|
||||
let pbxProj = try project.generatePbxProj()
|
||||
try pbxProj.expectFile(paths: ["Sources", "Bar.swift"], buildPhase: .sources)
|
||||
let buildPhase = pbxProj.objects.copyFilesBuildPhases.referenceValues.first
|
||||
try expect(buildPhase?.dstSubfolderSpec) == .frameworks
|
||||
let fileReference = pbxProj.getFileReference(
|
||||
paths: ["Sources", "Foo.framework"],
|
||||
names: ["Sources", "Foo.framework"]
|
||||
)?.reference ?? ""
|
||||
let buildFile = pbxProj.objects.buildFiles.objectReferences
|
||||
.first(where: { $0.object.fileRef == fileReference })?.reference ?? ""
|
||||
try expect(buildPhase?.files.count) == 1
|
||||
try expect(buildPhase?.files.contains(buildFile)) == true
|
||||
}
|
||||
|
||||
$0.it("generates core data models") {
|
||||
let directories = """
|
||||
Sources:
|
||||
model.xcdatamodeld:
|
||||
- model.xcdatamodel
|
||||
"""
|
||||
try createDirectories(directories)
|
||||
|
||||
let target = Target(name: "Test", type: .application, platform: .iOS, sources: ["Sources"])
|
||||
let project = Project(basePath: directoryPath, name: "Test", targets: [target])
|
||||
|
||||
let pbxProj = try project.generatePbxProj()
|
||||
guard let fileReference = pbxProj.objects.fileReferences.first(where: { $0.value.nameOrPath == "model.xcdatamodel" }) else {
|
||||
throw failure("Couldn't find model file reference")
|
||||
}
|
||||
guard let versionGroup = pbxProj.objects.versionGroups.values.first else {
|
||||
throw failure("Couldn't find version group")
|
||||
}
|
||||
try expect(versionGroup.currentVersion) == fileReference.key
|
||||
try expect(versionGroup.children) == [fileReference.key]
|
||||
try expect(versionGroup.path) == "model.xcdatamodeld"
|
||||
try expect(fileReference.value.path) == "model.xcdatamodel"
|
||||
}
|
||||
|
||||
|
||||
$0.it("handles duplicate names") {
|
||||
let directories = """
|
||||
Sources:
|
||||
- a.swift
|
||||
- a:
|
||||
- a.swift
|
||||
- a:
|
||||
- a.swift
|
||||
|
||||
"""
|
||||
try createDirectories(directories)
|
||||
|
||||
let target = Target(name: "Test", type: .application, platform: .iOS, sources: ["Sources"])
|
||||
let project = Project(
|
||||
basePath: directoryPath,
|
||||
name: "Test",
|
||||
targets: [target],
|
||||
fileGroups: ["Sources"]
|
||||
)
|
||||
|
||||
let pbxProj = try project.generatePbxProj()
|
||||
try pbxProj.expectFile(paths: ["Sources", "a.swift"], buildPhase: .sources)
|
||||
try pbxProj.expectFile(paths: ["Sources", "a", "a.swift"], buildPhase: .sources)
|
||||
try pbxProj.expectFile(paths: ["Sources", "a", "a", "a.swift"], buildPhase: .sources)
|
||||
}
|
||||
|
||||
$0.it("renames sources") {
|
||||
let directories = """
|
||||
Sources:
|
||||
- a.swift
|
||||
OtherSource:
|
||||
- b.swift
|
||||
"""
|
||||
try createDirectories(directories)
|
||||
|
||||
let target = Target(name: "Test", type: .application, platform: .iOS, sources: [
|
||||
TargetSource(path: "Sources", name: "NewSource"),
|
||||
TargetSource(path: "OtherSource/b.swift", name: "c.swift"),
|
||||
])
|
||||
let project = Project(basePath: directoryPath, name: "Test", targets: [target])
|
||||
|
||||
let pbxProj = try project.generatePbxProj()
|
||||
try pbxProj.expectFile(paths: ["Sources", "a.swift"], names: ["NewSource", "a.swift"], buildPhase: .sources)
|
||||
try pbxProj.expectFile(paths: ["OtherSource", "b.swift"], names: ["OtherSource", "c.swift"], buildPhase: .sources)
|
||||
}
|
||||
|
||||
$0.it("excludes sources") {
|
||||
let directories = """
|
||||
Sources:
|
||||
- A:
|
||||
- a.swift
|
||||
- B:
|
||||
- b.swift
|
||||
- b.ignored
|
||||
- a.ignored
|
||||
- B:
|
||||
- b.swift
|
||||
- D:
|
||||
- d.h
|
||||
- d.m
|
||||
- E:
|
||||
- e.jpg
|
||||
- e.h
|
||||
- e.m
|
||||
- F:
|
||||
- f.swift
|
||||
- G:
|
||||
- H:
|
||||
- h.swift
|
||||
- types:
|
||||
- a.swift
|
||||
- a.m
|
||||
- a.h
|
||||
- a.x
|
||||
- numbers:
|
||||
- file1.a
|
||||
- file2.a
|
||||
- file3.a
|
||||
- file4.a
|
||||
- partial:
|
||||
- file_part
|
||||
- ignore.file
|
||||
- a.ignored
|
||||
- project.xcodeproj:
|
||||
- project.pbxproj
|
||||
"""
|
||||
try createDirectories(directories)
|
||||
|
||||
let excludes = [
|
||||
"B",
|
||||
"d.m",
|
||||
"E/F/*.swift",
|
||||
"G/H/",
|
||||
"types/*.[hx]",
|
||||
"numbers/file[2-3].a",
|
||||
"partial/*_part",
|
||||
"ignore.file",
|
||||
"*.ignored",
|
||||
"*.xcodeproj",
|
||||
// not supported
|
||||
// "**/*.ignored",
|
||||
]
|
||||
|
||||
let target = Target(name: "Test", type: .application, platform: .iOS, sources: [TargetSource(path: "Sources", excludes: excludes)])
|
||||
let project = Project(basePath: directoryPath, name: "Test", targets: [target])
|
||||
|
||||
let pbxProj = try project.generatePbxProj()
|
||||
try pbxProj.expectFile(paths: ["Sources", "A", "a.swift"])
|
||||
try pbxProj.expectFile(paths: ["Sources", "D", "d.h"])
|
||||
try pbxProj.expectFile(paths: ["Sources", "D", "d.m"])
|
||||
try pbxProj.expectFile(paths: ["Sources", "E", "e.jpg"])
|
||||
try pbxProj.expectFile(paths: ["Sources", "E", "e.m"])
|
||||
try pbxProj.expectFile(paths: ["Sources", "E", "e.h"])
|
||||
try pbxProj.expectFile(paths: ["Sources", "types", "a.swift"])
|
||||
try pbxProj.expectFile(paths: ["Sources", "numbers", "file1.a"])
|
||||
try pbxProj.expectFile(paths: ["Sources", "numbers", "file4.a"])
|
||||
try pbxProj.expectFileMissing(paths: ["Sources", "B", "b.swift"])
|
||||
try pbxProj.expectFileMissing(paths: ["Sources", "E", "F", "f.swift"])
|
||||
try pbxProj.expectFileMissing(paths: ["Sources", "G", "H", "h.swift"])
|
||||
try pbxProj.expectFileMissing(paths: ["Sources", "types", "a.h"])
|
||||
try pbxProj.expectFileMissing(paths: ["Sources", "types", "a.x"])
|
||||
try pbxProj.expectFileMissing(paths: ["Sources", "numbers", "file2.a"])
|
||||
try pbxProj.expectFileMissing(paths: ["Sources", "numbers", "file3.a"])
|
||||
try pbxProj.expectFileMissing(paths: ["Sources", "partial", "file_part"])
|
||||
try pbxProj.expectFileMissing(paths: ["Sources", "a.ignored"])
|
||||
try pbxProj.expectFileMissing(paths: ["Sources", "ignore.file"])
|
||||
try pbxProj.expectFileMissing(paths: ["Sources", "project.xcodeproj"])
|
||||
}
|
||||
|
||||
$0.it("generates file sources") {
|
||||
let directories = """
|
||||
Sources:
|
||||
A:
|
||||
- a.swift
|
||||
- Assets.xcassets
|
||||
- B:
|
||||
- b.swift
|
||||
- c.jpg
|
||||
"""
|
||||
try createDirectories(directories)
|
||||
|
||||
let target = Target(name: "Test", type: .application, platform: .iOS, sources: [
|
||||
"Sources/A/a.swift",
|
||||
"Sources/A/B/b.swift",
|
||||
"Sources/A/Assets.xcassets",
|
||||
"Sources/A/B/c.jpg",
|
||||
])
|
||||
let project = Project(basePath: directoryPath, name: "Test", targets: [target])
|
||||
|
||||
let pbxProj = try project.generatePbxProj()
|
||||
try pbxProj.expectFile(paths: ["Sources/A", "a.swift"], names: ["A", "a.swift"], buildPhase: .sources)
|
||||
try pbxProj.expectFile(paths: ["Sources/A/B", "b.swift"], names: ["B", "b.swift"], buildPhase: .sources)
|
||||
try pbxProj.expectFile(paths: ["Sources/A/B", "c.jpg"], names: ["B", "c.jpg"], buildPhase: .resources)
|
||||
try pbxProj.expectFile(paths: ["Sources/A", "Assets.xcassets"], names: ["A", "Assets.xcassets"], buildPhase: .resources)
|
||||
}
|
||||
|
||||
$0.it("generates shared sources") {
|
||||
let directories = """
|
||||
Sources:
|
||||
A:
|
||||
- a.swift
|
||||
- B:
|
||||
- b.swift
|
||||
- c.jpg
|
||||
"""
|
||||
try createDirectories(directories)
|
||||
|
||||
let target1 = Target(name: "Test1", type: .framework, platform: .iOS, sources: ["Sources"])
|
||||
let target2 = Target(name: "Test2", type: .framework, platform: .tvOS, sources: ["Sources"])
|
||||
let project = Project(basePath: directoryPath, name: "Test", targets: [target1, target2])
|
||||
|
||||
_ = try project.generatePbxProj()
|
||||
// TODO: check there are build files for both targets
|
||||
}
|
||||
|
||||
$0.it("generates intermediate groups") {
|
||||
|
||||
let directories = """
|
||||
Sources:
|
||||
A:
|
||||
- b.swift
|
||||
F:
|
||||
- G:
|
||||
- h.swift
|
||||
"""
|
||||
try createDirectories(directories)
|
||||
let outOfSourceFile = outOfRootPath + "C/D/e.swift"
|
||||
try outOfSourceFile.parent().mkpath()
|
||||
try outOfSourceFile.write("")
|
||||
|
||||
let target = Target(name: "Test", type: .application, platform: .iOS, sources: [
|
||||
"Sources/A/b.swift",
|
||||
"Sources/F/G/h.swift",
|
||||
"../OtherDirectory/C/D/e.swift",
|
||||
])
|
||||
let options = SpecOptions(createIntermediateGroups: true)
|
||||
let project = Project(basePath: directoryPath, name: "Test", targets: [target], options: options)
|
||||
|
||||
let pbxProj = try project.generatePbxProj()
|
||||
try pbxProj.expectFile(paths: ["Sources", "A", "b.swift"], buildPhase: .sources)
|
||||
try pbxProj.expectFile(paths: ["Sources", "F", "G", "h.swift"], buildPhase: .sources)
|
||||
try pbxProj.expectFile(paths: [(outOfRootPath + "C/D").string, "e.swift"], names: ["D", "e.swift"], buildPhase: .sources)
|
||||
}
|
||||
|
||||
$0.it("generates folder references") {
|
||||
let directories = """
|
||||
Sources:
|
||||
A:
|
||||
- a.resource
|
||||
- b.resource
|
||||
"""
|
||||
try createDirectories(directories)
|
||||
|
||||
let target = Target(name: "Test", type: .application, platform: .iOS, sources: [
|
||||
TargetSource(path: "Sources/A", type: .folder),
|
||||
])
|
||||
let project = Project(basePath: directoryPath, name: "Test", targets: [target])
|
||||
|
||||
let pbxProj = try project.generatePbxProj()
|
||||
try pbxProj.expectFile(paths: ["Sources/A"], names: ["A"], buildPhase: .resources)
|
||||
try pbxProj.expectFileMissing(paths: ["Sources", "A", "a.swift"])
|
||||
}
|
||||
|
||||
$0.it("adds files to correct build phase") {
|
||||
let directories = """
|
||||
A:
|
||||
- file.swift
|
||||
- file.xcassets
|
||||
- file.h
|
||||
- Info.plist
|
||||
- file.xcconfig
|
||||
B:
|
||||
- file.swift
|
||||
- file.xcassets
|
||||
- file.h
|
||||
- Info.plist
|
||||
- file.xcconfig
|
||||
C:
|
||||
- file.swift
|
||||
- file.m
|
||||
- file.mm
|
||||
- file.cpp
|
||||
- file.c
|
||||
- file.S
|
||||
- file.h
|
||||
- file.hh
|
||||
- file.hpp
|
||||
- file.ipp
|
||||
- file.tpp
|
||||
- file.hxx
|
||||
- file.def
|
||||
- file.xcconfig
|
||||
- file.entitlements
|
||||
- file.gpx
|
||||
- file.apns
|
||||
- file.123
|
||||
- file.xcassets
|
||||
- Info.plist
|
||||
"""
|
||||
try createDirectories(directories)
|
||||
|
||||
let target = Target(name: "Test", type: .framework, platform: .iOS, sources: [
|
||||
TargetSource(path: "A", buildPhase: .resources),
|
||||
TargetSource(path: "B", buildPhase: .none),
|
||||
TargetSource(path: "C", buildPhase: nil),
|
||||
])
|
||||
let project = Project(basePath: directoryPath, name: "Test", targets: [target])
|
||||
|
||||
let pbxProj = try project.generatePbxProj()
|
||||
try pbxProj.expectFile(paths: ["A", "file.swift"], buildPhase: .resources)
|
||||
try pbxProj.expectFile(paths: ["A", "file.xcassets"], buildPhase: .resources)
|
||||
try pbxProj.expectFile(paths: ["A", "file.h"], buildPhase: .resources)
|
||||
try pbxProj.expectFile(paths: ["A", "Info.plist"], buildPhase: .none)
|
||||
try pbxProj.expectFile(paths: ["A", "file.xcconfig"], buildPhase: .resources)
|
||||
|
||||
try pbxProj.expectFile(paths: ["B", "file.swift"], buildPhase: .none)
|
||||
try pbxProj.expectFile(paths: ["B", "file.xcassets"], buildPhase: .none)
|
||||
try pbxProj.expectFile(paths: ["B", "file.h"], buildPhase: .none)
|
||||
try pbxProj.expectFile(paths: ["B", "Info.plist"], buildPhase: .none)
|
||||
try pbxProj.expectFile(paths: ["B", "file.xcconfig"], buildPhase: .none)
|
||||
|
||||
try pbxProj.expectFile(paths: ["C", "file.swift"], buildPhase: .sources)
|
||||
try pbxProj.expectFile(paths: ["C", "file.m"], buildPhase: .sources)
|
||||
try pbxProj.expectFile(paths: ["C", "file.mm"], buildPhase: .sources)
|
||||
try pbxProj.expectFile(paths: ["C", "file.cpp"], buildPhase: .sources)
|
||||
try pbxProj.expectFile(paths: ["C", "file.c"], buildPhase: .sources)
|
||||
try pbxProj.expectFile(paths: ["C", "file.S"], buildPhase: .sources)
|
||||
try pbxProj.expectFile(paths: ["C", "file.h"], buildPhase: .headers)
|
||||
try pbxProj.expectFile(paths: ["C", "file.hh"], buildPhase: .headers)
|
||||
try pbxProj.expectFile(paths: ["C", "file.hpp"], buildPhase: .headers)
|
||||
try pbxProj.expectFile(paths: ["C", "file.ipp"], buildPhase: .headers)
|
||||
try pbxProj.expectFile(paths: ["C", "file.tpp"], buildPhase: .headers)
|
||||
try pbxProj.expectFile(paths: ["C", "file.hxx"], buildPhase: .headers)
|
||||
try pbxProj.expectFile(paths: ["C", "file.def"], buildPhase: .headers)
|
||||
try pbxProj.expectFile(paths: ["C", "file.xcconfig"], buildPhase: .none)
|
||||
try pbxProj.expectFile(paths: ["C", "file.entitlements"], buildPhase: .none)
|
||||
try pbxProj.expectFile(paths: ["C", "file.gpx"], buildPhase: .none)
|
||||
try pbxProj.expectFile(paths: ["C", "file.apns"], buildPhase: .none)
|
||||
try pbxProj.expectFile(paths: ["C", "file.xcconfig"], buildPhase: .none)
|
||||
try pbxProj.expectFile(paths: ["C", "file.xcconfig"], buildPhase: .none)
|
||||
try pbxProj.expectFile(paths: ["C", "file.xcconfig"], buildPhase: .none)
|
||||
try pbxProj.expectFile(paths: ["C", "file.xcassets"], buildPhase: .resources)
|
||||
try pbxProj.expectFile(paths: ["C", "file.123"], buildPhase: .resources)
|
||||
try pbxProj.expectFile(paths: ["C", "Info.plist"], buildPhase: .none)
|
||||
}
|
||||
|
||||
$0.it("duplicate TargetSource is included once in sources build phase") {
|
||||
let directories = """
|
||||
Sources:
|
||||
A:
|
||||
- a.swift
|
||||
"""
|
||||
try createDirectories(directories)
|
||||
|
||||
let target = Target(name: "Test", type: .application, platform: .iOS, sources: [
|
||||
"Sources/A/a.swift",
|
||||
"Sources/A/a.swift",
|
||||
])
|
||||
let project = Project(basePath: directoryPath, name: "Test", targets: [target])
|
||||
|
||||
let pbxProj = try project.generatePbxProj()
|
||||
try pbxProj.expectFile(paths: ["Sources/A", "a.swift"], names: ["A", "a.swift"], buildPhase: .sources)
|
||||
|
||||
let sourcesBuildPhase = pbxProj.objects.buildPhases
|
||||
.first(where: { $0.1.buildPhase == BuildPhase.sources })!
|
||||
.value
|
||||
|
||||
try expect(sourcesBuildPhase.files.count) == 1
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extension PBXProj {
|
||||
|
||||
/// expect a file within groups of the paths, using optional different names
|
||||
func expectFile(paths: [String], names: [String]? = nil, buildPhase: TargetSource.BuildPhase? = nil, file: String = #file, line: Int = #line) throws {
|
||||
guard let fileReference = getFileReference(paths: paths, names: names ?? paths) else {
|
||||
var error = "Could not find file at path \(paths.joined(separator: "/").quoted)"
|
||||
if let names = names, names != paths {
|
||||
error += " and name \(names.joined(separator: "/").quoted)"
|
||||
}
|
||||
throw failure(error, file: file, line: line)
|
||||
}
|
||||
|
||||
if let buildPhase = buildPhase {
|
||||
let buildFile = objects.buildFiles.objectReferences
|
||||
.first(where: { $0.object.fileRef == fileReference.reference })
|
||||
let actualBuildPhase = buildFile
|
||||
.flatMap { buildFile in objects.buildPhases.referenceValues.first { $0.files.contains(buildFile.reference) } }?.buildPhase
|
||||
|
||||
var error: String?
|
||||
if let buildPhase = buildPhase.buildPhase {
|
||||
if actualBuildPhase != buildPhase {
|
||||
if let actualBuildPhase = actualBuildPhase {
|
||||
error = "is in the \(actualBuildPhase.rawValue) build phase instead of the expected \(buildPhase.rawValue.quoted)"
|
||||
} else {
|
||||
error = "isn't in a build phase when it's expected to be in \(buildPhase.rawValue.quoted)"
|
||||
}
|
||||
}
|
||||
} else if let actualBuildPhase = actualBuildPhase {
|
||||
error = "is in the \(actualBuildPhase.rawValue.quoted) build phase when it's expected to not be in any"
|
||||
}
|
||||
if let error = error {
|
||||
throw failure("File \(paths.joined(separator: "/").quoted) \(error)", file: file, line: line)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// expect a missing file within groups of the paths, using optional different names
|
||||
func expectFileMissing(paths: [String], names: [String]? = nil, file: String = #file, line: Int = #line) throws {
|
||||
let names = names ?? paths
|
||||
if getFileReference(paths: paths, names: names) != nil {
|
||||
throw failure("Found unexpected file at path \(paths.joined(separator: "/").quoted) and name \(paths.joined(separator: "/").quoted)", file: file, line: line)
|
||||
}
|
||||
}
|
||||
|
||||
func getFileReference(paths: [String], names: [String], file: String = #file, line: Int = #line) -> ObjectReference<PBXFileReference>? {
|
||||
guard let project = objects.projects.first?.value else { return nil }
|
||||
guard let mainGroup = objects.groups.getReference(project.mainGroup) else { return nil }
|
||||
|
||||
return getFileReference(group: mainGroup, paths: paths, names: names)
|
||||
}
|
||||
|
||||
func getMainGroup(function: String = #function, file: String = #file, line: Int = #line) throws -> PBXGroup {
|
||||
guard let project = objects.projects.first?.value else {
|
||||
throw failure("Couldn't find project", file: file, line: line)
|
||||
}
|
||||
guard let mainGroup = objects.groups.getReference(project.mainGroup) else {
|
||||
throw failure("Couldn't find main group", file: file, line: line)
|
||||
}
|
||||
return mainGroup
|
||||
}
|
||||
|
||||
private func getFileReference(group: PBXGroup, paths: [String], names: [String]) -> ObjectReference<PBXFileReference>? {
|
||||
|
||||
guard !paths.isEmpty else { return nil }
|
||||
let path = paths.first!
|
||||
let name = names.first!
|
||||
let restOfPath = Array(paths.dropFirst())
|
||||
let restOfName = Array(names.dropFirst())
|
||||
if restOfPath.isEmpty {
|
||||
let fileReferences: [ObjectReference<PBXFileReference>] = group.children.compactMap { reference in
|
||||
if let fileReference = self.objects.fileReferences.getReference(reference) {
|
||||
return ObjectReference(reference: reference, object: fileReference)
|
||||
} else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
return fileReferences.first { $0.object.path == path && $0.object.nameOrPath == name }
|
||||
} else {
|
||||
let groups = group.children.compactMap { self.objects.groups.getReference($0) }
|
||||
guard let group = groups.first(where: { $0.path == path && $0.nameOrPath == name }) else { return nil }
|
||||
return getFileReference(group: group, paths: restOfPath, names: restOfName)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -4,417 +4,428 @@ import ProjectSpec
|
||||
import Spectre
|
||||
import XcodeGenKit
|
||||
import xcproj
|
||||
import XCTest
|
||||
|
||||
func projectLoadingTests() {
|
||||
class SpecLoadingTests: XCTestCase {
|
||||
|
||||
@discardableResult
|
||||
func getProjectSpec(_ project: [String: Any]) throws -> Project {
|
||||
var projectDictionary: [String: Any] = ["name": "test"]
|
||||
for (key, value) in project {
|
||||
projectDictionary[key] = value
|
||||
}
|
||||
return try Project(basePath: "", jsonDictionary: projectDictionary)
|
||||
}
|
||||
func testSpecLoader() {
|
||||
describe {
|
||||
$0.it("merges includes") {
|
||||
let path = fixturePath + "include_test.yml"
|
||||
let project = try Project(path: path)
|
||||
|
||||
func expectSpecError(_ project: [String: Any], _ expectedError: SpecParsingError) throws {
|
||||
try expectError(expectedError) {
|
||||
try getProjectSpec(project)
|
||||
}
|
||||
}
|
||||
try expect(project.name) == "NewName"
|
||||
try expect(project.settingGroups) == [
|
||||
"test": Settings(dictionary: ["MY_SETTING1": "NEW VALUE", "MY_SETTING2": "VALUE2", "MY_SETTING3": "VALUE3"]),
|
||||
"new": Settings(dictionary: ["MY_SETTING": "VALUE"]),
|
||||
"toReplace": Settings(dictionary: ["MY_SETTING2": "VALUE2"]),
|
||||
]
|
||||
try expect(project.targets) == [
|
||||
Target(name: "IncludedTargetNew", type: .application, platform: .tvOS, sources: ["NewSource"]),
|
||||
Target(name: "NewTarget", type: .application, platform: .iOS),
|
||||
]
|
||||
}
|
||||
|
||||
func expectTargetError(_ target: [String: Any], _ expectedError: SpecParsingError) throws {
|
||||
try expectError(expectedError) {
|
||||
_ = try Target(name: "test", jsonDictionary: target)
|
||||
}
|
||||
}
|
||||
|
||||
let validTarget: [String: Any] = ["type": "application", "platform": "iOS"]
|
||||
let invalid = "invalid"
|
||||
|
||||
describe("Spec Loader") {
|
||||
$0.it("merges includes") {
|
||||
let path = fixturePath + "include_test.yml"
|
||||
let project = try Project(path: path)
|
||||
|
||||
try expect(project.name) == "NewName"
|
||||
try expect(project.settingGroups) == [
|
||||
"test": Settings(dictionary: ["MY_SETTING1": "NEW VALUE", "MY_SETTING2": "VALUE2", "MY_SETTING3": "VALUE3"]),
|
||||
"new": Settings(dictionary: ["MY_SETTING": "VALUE"]),
|
||||
"toReplace": Settings(dictionary: ["MY_SETTING2": "VALUE2"]),
|
||||
]
|
||||
try expect(project.targets) == [
|
||||
Target(name: "IncludedTargetNew", type: .application, platform: .tvOS, sources: ["NewSource"]),
|
||||
Target(name: "NewTarget", type: .application, platform: .iOS),
|
||||
]
|
||||
}
|
||||
|
||||
$0.it("parses yaml types") {
|
||||
let path = fixturePath + "yaml.yml"
|
||||
let dictionary = try loadYamlDictionary(path: path)
|
||||
let expectedDictionary: [String: Any] = [
|
||||
"true": true,
|
||||
"false": false,
|
||||
"yes": true,
|
||||
"no": false,
|
||||
"yesQuote": "YES",
|
||||
"noQuote": "NO",
|
||||
"int": 1,
|
||||
"intQuote": "1",
|
||||
"float": 3.2,
|
||||
"floatQuote": "10.10",
|
||||
"string": "hello",
|
||||
"stringQuote": "hello",
|
||||
"space": " ",
|
||||
"empty": "",
|
||||
"emptyQuote": "",
|
||||
"emptyDictionary": [String: Any](),
|
||||
"arrayLiteral": [1, 2],
|
||||
"arrayList": [1, 2],
|
||||
]
|
||||
for (key, expectedValue) in expectedDictionary {
|
||||
guard let parsedValue = dictionary[key] else {
|
||||
throw failure("\(key) does not exist")
|
||||
$0.it("parses yaml types") {
|
||||
let path = fixturePath + "yaml.yml"
|
||||
let dictionary = try loadYamlDictionary(path: path)
|
||||
let expectedDictionary: [String: Any] = [
|
||||
"true": true,
|
||||
"false": false,
|
||||
"yes": true,
|
||||
"no": false,
|
||||
"yesQuote": "YES",
|
||||
"noQuote": "NO",
|
||||
"int": 1,
|
||||
"intQuote": "1",
|
||||
"float": 3.2,
|
||||
"floatQuote": "10.10",
|
||||
"string": "hello",
|
||||
"stringQuote": "hello",
|
||||
"space": " ",
|
||||
"empty": "",
|
||||
"emptyQuote": "",
|
||||
"emptyDictionary": [String: Any](),
|
||||
"arrayLiteral": [1, 2],
|
||||
"arrayList": [1, 2],
|
||||
]
|
||||
for (key, expectedValue) in expectedDictionary {
|
||||
guard let parsedValue = dictionary[key] else {
|
||||
throw failure("\(key) does not exist")
|
||||
}
|
||||
if String(describing: expectedValue) != String(describing: parsedValue) {
|
||||
throw failure("\(key): \(parsedValue) does not equal \(expectedValue)")
|
||||
}
|
||||
}
|
||||
if String(describing: expectedValue) != String(describing: parsedValue) {
|
||||
throw failure("\(key): \(parsedValue) does not equal \(expectedValue)")
|
||||
if !(dictionary as NSDictionary).isEqual(expectedDictionary) {
|
||||
throw failure("parsed yaml types don't match:\n\nParsed:\n\t\(dictionary.map { "\($0.key): \($0.value)" }.joined(separator: "\n\t"))\nExpected:\n\t\(expectedDictionary.map { "\($0.key): \($0.value)" }.joined(separator: "\n\t"))")
|
||||
}
|
||||
}
|
||||
if !(dictionary as NSDictionary).isEqual(expectedDictionary) {
|
||||
throw failure("parsed yaml types don't match:\n\nParsed:\n\t\(dictionary.map { "\($0.key): \($0.value)" }.joined(separator: "\n\t"))\nExpected:\n\t\(expectedDictionary.map { "\($0.key): \($0.value)" }.joined(separator: "\n\t"))")
|
||||
}
|
||||
}
|
||||
|
||||
func testSpecLoaderLoadingJSON() {
|
||||
describe {
|
||||
$0.it("merges includes") {
|
||||
let path = fixturePath + "include_test.json"
|
||||
let project = try Project(path: path)
|
||||
|
||||
try expect(project.name) == "NewName"
|
||||
try expect(project.settingGroups) == [
|
||||
"test": Settings(dictionary: ["MY_SETTING1": "NEW VALUE", "MY_SETTING2": "VALUE2", "MY_SETTING3": "VALUE3"]),
|
||||
"new": Settings(dictionary: ["MY_SETTING": "VALUE"]),
|
||||
"toReplace": Settings(dictionary: ["MY_SETTING2": "VALUE2"]),
|
||||
]
|
||||
try expect(project.targets) == [
|
||||
Target(name: "IncludedTargetNew", type: .application, platform: .tvOS, sources: ["NewSource"]),
|
||||
Target(name: "NewTarget", type: .application, platform: .iOS),
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
describe("Spec Loader JSON") {
|
||||
$0.it("merges includes") {
|
||||
let path = fixturePath + "include_test.json"
|
||||
let project = try Project(path: path)
|
||||
func testProjectSpecParser() {
|
||||
let validTarget: [String: Any] = ["type": "application", "platform": "iOS"]
|
||||
let invalid = "invalid"
|
||||
|
||||
try expect(project.name) == "NewName"
|
||||
try expect(project.settingGroups) == [
|
||||
"test": Settings(dictionary: ["MY_SETTING1": "NEW VALUE", "MY_SETTING2": "VALUE2", "MY_SETTING3": "VALUE3"]),
|
||||
"new": Settings(dictionary: ["MY_SETTING": "VALUE"]),
|
||||
"toReplace": Settings(dictionary: ["MY_SETTING2": "VALUE2"]),
|
||||
]
|
||||
try expect(project.targets) == [
|
||||
Target(name: "IncludedTargetNew", type: .application, platform: .tvOS, sources: ["NewSource"]),
|
||||
Target(name: "NewTarget", type: .application, platform: .iOS),
|
||||
]
|
||||
}
|
||||
}
|
||||
describe {
|
||||
|
||||
describe("Project Spec Parser") {
|
||||
$0.it("fails with incorrect platform") {
|
||||
var target = validTarget
|
||||
target["platform"] = invalid
|
||||
try expectTargetError(target, .unknownTargetPlatform(invalid))
|
||||
}
|
||||
|
||||
$0.it("fails with incorrect platform") {
|
||||
var target = validTarget
|
||||
target["platform"] = invalid
|
||||
try expectTargetError(target, .unknownTargetPlatform(invalid))
|
||||
}
|
||||
$0.it("fails with incorrect product type") {
|
||||
var target = validTarget
|
||||
target["type"] = invalid
|
||||
try expectTargetError(target, .unknownTargetType(invalid))
|
||||
}
|
||||
|
||||
$0.it("fails with incorrect product type") {
|
||||
var target = validTarget
|
||||
target["type"] = invalid
|
||||
try expectTargetError(target, .unknownTargetType(invalid))
|
||||
}
|
||||
$0.it("fails with invalid dependency") {
|
||||
var target = validTarget
|
||||
target["dependencies"] = [[invalid: "name"]]
|
||||
try expectTargetError(target, .invalidDependency([invalid: "name"]))
|
||||
}
|
||||
|
||||
$0.it("fails with invalid dependency") {
|
||||
var target = validTarget
|
||||
target["dependencies"] = [[invalid: "name"]]
|
||||
try expectTargetError(target, .invalidDependency([invalid: "name"]))
|
||||
}
|
||||
$0.it("parses sources") {
|
||||
var targetDictionary1 = validTarget
|
||||
targetDictionary1["sources"] = [
|
||||
"sourceString",
|
||||
["path": "sourceObject"],
|
||||
["path": "sourceWithFlagsArray", "compilerFlags": ["-Werror"]],
|
||||
["path": "sourceWithFlagsString", "compilerFlags": "-Werror -Wextra"],
|
||||
["path": "sourceWithExcludes", "excludes": ["Foo.swift"]],
|
||||
["path": "sourceWithFileType", "type": "file"],
|
||||
["path": "sourceWithGroupType", "type": "group"],
|
||||
["path": "sourceWithFolderType", "type": "folder"],
|
||||
]
|
||||
var targetDictionary2 = validTarget
|
||||
targetDictionary2["sources"] = "source3"
|
||||
|
||||
$0.it("parses sources") {
|
||||
var targetDictionary1 = validTarget
|
||||
targetDictionary1["sources"] = [
|
||||
"sourceString",
|
||||
["path": "sourceObject"],
|
||||
["path": "sourceWithFlagsArray", "compilerFlags": ["-Werror"]],
|
||||
["path": "sourceWithFlagsString", "compilerFlags": "-Werror -Wextra"],
|
||||
["path": "sourceWithExcludes", "excludes": ["Foo.swift"]],
|
||||
["path": "sourceWithFileType", "type": "file"],
|
||||
["path": "sourceWithGroupType", "type": "group"],
|
||||
["path": "sourceWithFolderType", "type": "folder"],
|
||||
]
|
||||
var targetDictionary2 = validTarget
|
||||
targetDictionary2["sources"] = "source3"
|
||||
let target1 = try Target(name: "test", jsonDictionary: targetDictionary1)
|
||||
let target2 = try Target(name: "test", jsonDictionary: targetDictionary2)
|
||||
|
||||
let target1 = try Target(name: "test", jsonDictionary: targetDictionary1)
|
||||
let target2 = try Target(name: "test", jsonDictionary: targetDictionary2)
|
||||
let target1SourcesExpect = [
|
||||
TargetSource(path: "sourceString"),
|
||||
TargetSource(path: "sourceObject"),
|
||||
TargetSource(path: "sourceWithFlagsArray", compilerFlags: ["-Werror"]),
|
||||
TargetSource(path: "sourceWithFlagsString", compilerFlags: ["-Werror", "-Wextra"]),
|
||||
TargetSource(path: "sourceWithExcludes", excludes: ["Foo.swift"]),
|
||||
TargetSource(path: "sourceWithFileType", type: .file),
|
||||
TargetSource(path: "sourceWithGroupType", type: .group),
|
||||
TargetSource(path: "sourceWithFolderType", type: .folder),
|
||||
]
|
||||
|
||||
let target1SourcesExpect = [
|
||||
TargetSource(path: "sourceString"),
|
||||
TargetSource(path: "sourceObject"),
|
||||
TargetSource(path: "sourceWithFlagsArray", compilerFlags: ["-Werror"]),
|
||||
TargetSource(path: "sourceWithFlagsString", compilerFlags: ["-Werror", "-Wextra"]),
|
||||
TargetSource(path: "sourceWithExcludes", excludes: ["Foo.swift"]),
|
||||
TargetSource(path: "sourceWithFileType", type: .file),
|
||||
TargetSource(path: "sourceWithGroupType", type: .group),
|
||||
TargetSource(path: "sourceWithFolderType", type: .folder),
|
||||
]
|
||||
try expect(target1.sources) == target1SourcesExpect
|
||||
try expect(target2.sources) == ["source3"]
|
||||
}
|
||||
|
||||
try expect(target1.sources) == target1SourcesExpect
|
||||
try expect(target2.sources) == ["source3"]
|
||||
}
|
||||
$0.it("parses target dependencies") {
|
||||
var targetDictionary = validTarget
|
||||
targetDictionary["dependencies"] = [
|
||||
["target": "name", "embed": false],
|
||||
["carthage": "name"],
|
||||
["framework": "path"],
|
||||
]
|
||||
let target = try Target(name: "test", jsonDictionary: targetDictionary)
|
||||
try expect(target.dependencies.count) == 3
|
||||
try expect(target.dependencies[0]) == Dependency(type: .target, reference: "name", embed: false)
|
||||
try expect(target.dependencies[1]) == Dependency(type: .carthage, reference: "name")
|
||||
try expect(target.dependencies[2]) == Dependency(type: .framework, reference: "path")
|
||||
}
|
||||
|
||||
$0.it("parses target dependencies") {
|
||||
var targetDictionary = validTarget
|
||||
targetDictionary["dependencies"] = [
|
||||
["target": "name", "embed": false],
|
||||
["carthage": "name"],
|
||||
["framework": "path"],
|
||||
]
|
||||
let target = try Target(name: "test", jsonDictionary: targetDictionary)
|
||||
try expect(target.dependencies.count) == 3
|
||||
try expect(target.dependencies[0]) == Dependency(type: .target, reference: "name", embed: false)
|
||||
try expect(target.dependencies[1]) == Dependency(type: .carthage, reference: "name")
|
||||
try expect(target.dependencies[2]) == Dependency(type: .framework, reference: "path")
|
||||
}
|
||||
$0.it("parses cross platform targets") {
|
||||
let targetDictionary: [String: Any] = [
|
||||
"platform": ["iOS", "tvOS"],
|
||||
"type": "framework",
|
||||
"sources": ["Framework", "Framework $platform"],
|
||||
"settings": ["SETTING": "value_$platform"],
|
||||
]
|
||||
|
||||
$0.it("parses cross platform targets") {
|
||||
let targetDictionary: [String: Any] = [
|
||||
"platform": ["iOS", "tvOS"],
|
||||
"type": "framework",
|
||||
"sources": ["Framework", "Framework $platform"],
|
||||
"settings": ["SETTING": "value_$platform"],
|
||||
]
|
||||
let project = try getProjectSpec(["targets": ["Framework": targetDictionary]])
|
||||
var target_iOS = Target(name: "Framework_iOS", type: .framework, platform: .iOS)
|
||||
var target_tvOS = Target(name: "Framework_tvOS", type: .framework, platform: .tvOS)
|
||||
|
||||
let project = try getProjectSpec(["targets": ["Framework": targetDictionary]])
|
||||
var target_iOS = Target(name: "Framework_iOS", type: .framework, platform: .iOS)
|
||||
var target_tvOS = Target(name: "Framework_tvOS", type: .framework, platform: .tvOS)
|
||||
target_iOS.sources = ["Framework", "Framework iOS"]
|
||||
target_tvOS.sources = ["Framework", "Framework tvOS"]
|
||||
target_iOS.settings = ["PRODUCT_NAME": "Framework", "SETTING": "value_iOS"]
|
||||
target_tvOS.settings = ["PRODUCT_NAME": "Framework", "SETTING": "value_tvOS"]
|
||||
|
||||
target_iOS.sources = ["Framework", "Framework iOS"]
|
||||
target_tvOS.sources = ["Framework", "Framework tvOS"]
|
||||
target_iOS.settings = ["PRODUCT_NAME": "Framework", "SETTING": "value_iOS"]
|
||||
target_tvOS.settings = ["PRODUCT_NAME": "Framework", "SETTING": "value_tvOS"]
|
||||
try expect(project.targets.count) == 2
|
||||
try expect(project.targets) == [target_iOS, target_tvOS]
|
||||
}
|
||||
|
||||
try expect(project.targets.count) == 2
|
||||
try expect(project.targets) == [target_iOS, target_tvOS]
|
||||
}
|
||||
|
||||
$0.it("parses target schemes") {
|
||||
var targetDictionary = validTarget
|
||||
targetDictionary["scheme"] = [
|
||||
"testTargets": ["t1", "t2"],
|
||||
"configVariants": ["dev", "app-store"],
|
||||
"commandLineArguments": [
|
||||
"ENV1": true,
|
||||
],
|
||||
"gatherCoverageData": true,
|
||||
"environmentVariables": [
|
||||
"TEST_VAR": "TEST_VAL",
|
||||
],
|
||||
"preActions": [
|
||||
[
|
||||
"script": "dothing",
|
||||
"name": "Do Thing",
|
||||
"settingsTarget": "test",
|
||||
$0.it("parses target schemes") {
|
||||
var targetDictionary = validTarget
|
||||
targetDictionary["scheme"] = [
|
||||
"testTargets": ["t1", "t2"],
|
||||
"configVariants": ["dev", "app-store"],
|
||||
"commandLineArguments": [
|
||||
"ENV1": true,
|
||||
],
|
||||
],
|
||||
"postActions": [
|
||||
[
|
||||
"script": "hello",
|
||||
],
|
||||
],
|
||||
]
|
||||
|
||||
let target = try Target(name: "test", jsonDictionary: targetDictionary)
|
||||
|
||||
let scheme = TargetScheme(
|
||||
testTargets: ["t1", "t2"],
|
||||
configVariants: ["dev", "app-store"],
|
||||
gatherCoverageData: true,
|
||||
commandLineArguments: ["ENV1": true],
|
||||
environmentVariables: [XCScheme.EnvironmentVariable(variable: "TEST_VAR", value: "TEST_VAL", enabled: true)],
|
||||
preActions: [.init(name: "Do Thing", script: "dothing", settingsTarget: "test")],
|
||||
postActions: [.init(name: "Run Script", script: "hello")]
|
||||
)
|
||||
|
||||
try expect(target.scheme) == scheme
|
||||
}
|
||||
|
||||
$0.it("parses schemes") {
|
||||
let schemeDictionary: [String: Any] = [
|
||||
"build": [
|
||||
"parallelizeBuild": false,
|
||||
"buildImplicitDependencies": false,
|
||||
"targets": [
|
||||
"Target1": "all",
|
||||
"Target2": "testing",
|
||||
"Target3": "none",
|
||||
"Target4": ["testing": true],
|
||||
"Target5": ["testing": false],
|
||||
"Target6": ["test", "analyze"],
|
||||
"gatherCoverageData": true,
|
||||
"environmentVariables": [
|
||||
"TEST_VAR": "TEST_VAL",
|
||||
],
|
||||
"preActions": [
|
||||
[
|
||||
"script": "echo Before Build",
|
||||
"name": "Before Build",
|
||||
"settingsTarget": "Target1",
|
||||
"script": "dothing",
|
||||
"name": "Do Thing",
|
||||
"settingsTarget": "test",
|
||||
],
|
||||
],
|
||||
],
|
||||
]
|
||||
let scheme = try Scheme(name: "Scheme", jsonDictionary: schemeDictionary)
|
||||
let expectedTargets: [Scheme.BuildTarget] = [
|
||||
Scheme.BuildTarget(target: "Target1", buildTypes: BuildType.all),
|
||||
Scheme.BuildTarget(target: "Target2", buildTypes: [.testing, .analyzing]),
|
||||
Scheme.BuildTarget(target: "Target3", buildTypes: []),
|
||||
Scheme.BuildTarget(target: "Target4", buildTypes: [.testing]),
|
||||
Scheme.BuildTarget(target: "Target5", buildTypes: []),
|
||||
Scheme.BuildTarget(target: "Target6", buildTypes: [.testing, .analyzing]),
|
||||
]
|
||||
try expect(scheme.name) == "Scheme"
|
||||
try expect(scheme.build.targets) == expectedTargets
|
||||
try expect(scheme.build.preActions.first?.script) == "echo Before Build"
|
||||
try expect(scheme.build.preActions.first?.name) == "Before Build"
|
||||
try expect(scheme.build.preActions.first?.settingsTarget) == "Target1"
|
||||
|
||||
try expect(scheme.build.parallelizeBuild) == false
|
||||
try expect(scheme.build.buildImplicitDependencies) == false
|
||||
}
|
||||
|
||||
$0.it("parses schemes variables") {
|
||||
let schemeDictionary: [String: Any] = [
|
||||
"build": [
|
||||
"targets": ["Target1": "all"],
|
||||
],
|
||||
"run": [
|
||||
"environmentVariables": [
|
||||
["variable": "BOOL_TRUE", "value": true],
|
||||
["variable": "BOOL_YES", "value": "YES"],
|
||||
["variable": "ENVIRONMENT", "value": "VARIABLE"],
|
||||
["variable": "OTHER_ENV_VAR", "value": "VAL", "isEnabled": false],
|
||||
"postActions": [
|
||||
[
|
||||
"script": "hello",
|
||||
],
|
||||
],
|
||||
],
|
||||
"test": [
|
||||
"environmentVariables": [
|
||||
"BOOL_TRUE": true,
|
||||
"BOOL_YES": "YES",
|
||||
"TEST": "VARIABLE",
|
||||
],
|
||||
],
|
||||
"profile": [
|
||||
"config": "Release",
|
||||
],
|
||||
]
|
||||
|
||||
let scheme = try Scheme(name: "Scheme", jsonDictionary: schemeDictionary)
|
||||
|
||||
let expectedRunVariables = [
|
||||
XCScheme.EnvironmentVariable(variable: "BOOL_TRUE", value: "YES", enabled: true),
|
||||
XCScheme.EnvironmentVariable(variable: "BOOL_YES", value: "YES", enabled: true),
|
||||
XCScheme.EnvironmentVariable(variable: "ENVIRONMENT", value: "VARIABLE", enabled: true),
|
||||
XCScheme.EnvironmentVariable(variable: "OTHER_ENV_VAR", value: "VAL", enabled: false),
|
||||
]
|
||||
|
||||
let expectedTestVariables = [
|
||||
XCScheme.EnvironmentVariable(variable: "BOOL_TRUE", value: "YES", enabled: true),
|
||||
XCScheme.EnvironmentVariable(variable: "BOOL_YES", value: "YES", enabled: true),
|
||||
XCScheme.EnvironmentVariable(variable: "TEST", value: "VARIABLE", enabled: true),
|
||||
]
|
||||
|
||||
try expect(scheme.run?.environmentVariables) == expectedRunVariables
|
||||
try expect(scheme.test?.environmentVariables) == expectedTestVariables
|
||||
try expect(scheme.profile?.config) == "Release"
|
||||
try expect(scheme.profile?.environmentVariables.isEmpty) == true
|
||||
}
|
||||
|
||||
$0.it("parses settings") {
|
||||
let project = try Project(path: fixturePath + "settings_test.yml")
|
||||
let buildSettings: BuildSettings = ["SETTING": "value"]
|
||||
let configSettings: [String: Settings] = ["config1": Settings(buildSettings: ["SETTING1": "value"])]
|
||||
let groups = ["preset1"]
|
||||
|
||||
let preset1 = Settings(buildSettings: buildSettings, configSettings: [:], groups: [])
|
||||
let preset2 = Settings(buildSettings: [:], configSettings: configSettings, groups: [])
|
||||
let preset3 = Settings(buildSettings: buildSettings, configSettings: configSettings, groups: [])
|
||||
let preset4 = Settings(buildSettings: buildSettings, configSettings: [:], groups: [])
|
||||
let preset5 = Settings(buildSettings: buildSettings, configSettings: [:], groups: groups)
|
||||
let preset6 = Settings(buildSettings: buildSettings, configSettings: configSettings, groups: groups)
|
||||
let preset7 = Settings(buildSettings: buildSettings, configSettings: ["config1": Settings(buildSettings: buildSettings, groups: groups)])
|
||||
let preset8 = Settings(buildSettings: [:], configSettings: ["config1": Settings(configSettings: configSettings)])
|
||||
|
||||
try expect(project.settingGroups.count) == 8
|
||||
try expect(project.settingGroups["preset1"]) == preset1
|
||||
try expect(project.settingGroups["preset2"]) == preset2
|
||||
try expect(project.settingGroups["preset3"]) == preset3
|
||||
try expect(project.settingGroups["preset4"]) == preset4
|
||||
try expect(project.settingGroups["preset5"]) == preset5
|
||||
try expect(project.settingGroups["preset6"]) == preset6
|
||||
try expect(project.settingGroups["preset7"]) == preset7
|
||||
try expect(project.settingGroups["preset8"]) == preset8
|
||||
}
|
||||
|
||||
$0.it("parses run scripts") {
|
||||
var target = validTarget
|
||||
let scripts: [[String: Any]] = [
|
||||
["path": "script.sh"],
|
||||
["script": "shell script\ndo thing", "name": "myscript", "inputFiles": ["file", "file2"], "outputFiles": ["file", "file2"], "shell": "bin/customshell", "runOnlyWhenInstalling": true],
|
||||
]
|
||||
target["prebuildScripts"] = scripts
|
||||
target["postbuildScripts"] = scripts
|
||||
|
||||
let expectedScripts = [
|
||||
BuildScript(script: .path("script.sh")),
|
||||
BuildScript(script: .script("shell script\ndo thing"), name: "myscript", inputFiles: ["file", "file2"], outputFiles: ["file", "file2"], shell: "bin/customshell", runOnlyWhenInstalling: true),
|
||||
]
|
||||
|
||||
let parsedTarget = try Target(name: "test", jsonDictionary: target)
|
||||
try expect(parsedTarget.prebuildScripts) == expectedScripts
|
||||
try expect(parsedTarget.postbuildScripts) == expectedScripts
|
||||
}
|
||||
|
||||
$0.it("parses build rules") {
|
||||
var target = validTarget
|
||||
let buildRules: [[String: Any]] = [
|
||||
[
|
||||
"name": "My Rule",
|
||||
"script": "my script",
|
||||
"filePattern": "*.swift",
|
||||
"outputFiles": ["file1", "file2"],
|
||||
"outputFilesCompilerFlags": ["-a","-b"]
|
||||
],
|
||||
[
|
||||
"compilerSpec": "apple.tool",
|
||||
"fileType": "sourcecode.swift",
|
||||
]
|
||||
]
|
||||
target["buildRules"] = buildRules
|
||||
|
||||
let expectedBuildRules = [
|
||||
BuildRule(fileType: .pattern("*.swift"), action: .script("my script"), name: "My Rule", outputFiles: ["file1", "file2"], outputFilesCompilerFlags: ["-a","-b"]),
|
||||
BuildRule(fileType: .type("sourcecode.swift"), action: .compilerSpec("apple.tool")),
|
||||
]
|
||||
|
||||
let parsedTarget = try Target(name: "test", jsonDictionary: target)
|
||||
try expect(parsedTarget.buildRules) == expectedBuildRules
|
||||
}
|
||||
let target = try Target(name: "test", jsonDictionary: targetDictionary)
|
||||
|
||||
$0.it("parses options") {
|
||||
let options = SpecOptions(
|
||||
carthageBuildPath: "../Carthage/Build",
|
||||
carthageExecutablePath: "../bin/carthage",
|
||||
createIntermediateGroups: true,
|
||||
bundleIdPrefix: "com.test",
|
||||
developmentLanguage: "ja",
|
||||
deploymentTarget: DeploymentTarget(
|
||||
iOS: "11.1",
|
||||
tvOS: "10.0",
|
||||
watchOS: "3.0",
|
||||
macOS: "10.12.1"
|
||||
let scheme = TargetScheme(
|
||||
testTargets: ["t1", "t2"],
|
||||
configVariants: ["dev", "app-store"],
|
||||
gatherCoverageData: true,
|
||||
commandLineArguments: ["ENV1": true],
|
||||
environmentVariables: [XCScheme.EnvironmentVariable(variable: "TEST_VAR", value: "TEST_VAL", enabled: true)],
|
||||
preActions: [.init(name: "Do Thing", script: "dothing", settingsTarget: "test")],
|
||||
postActions: [.init(name: "Run Script", script: "hello")]
|
||||
)
|
||||
)
|
||||
let expected = Project(basePath: "", name: "test", options: options)
|
||||
let dictionary: [String: Any] = ["options": [
|
||||
"carthageBuildPath": "../Carthage/Build",
|
||||
"carthageExecutablePath": "../bin/carthage",
|
||||
"bundleIdPrefix": "com.test",
|
||||
"createIntermediateGroups": true,
|
||||
"developmentLanguage": "ja",
|
||||
"deploymentTarget": ["iOS": 11.1, "tvOS": 10.0, "watchOS": "3", "macOS": "10.12.1"],
|
||||
]]
|
||||
let parsedSpec = try getProjectSpec(dictionary)
|
||||
try expect(parsedSpec) == expected
|
||||
|
||||
try expect(target.scheme) == scheme
|
||||
}
|
||||
|
||||
$0.it("parses schemes") {
|
||||
let schemeDictionary: [String: Any] = [
|
||||
"build": [
|
||||
"parallelizeBuild": false,
|
||||
"buildImplicitDependencies": false,
|
||||
"targets": [
|
||||
"Target1": "all",
|
||||
"Target2": "testing",
|
||||
"Target3": "none",
|
||||
"Target4": ["testing": true],
|
||||
"Target5": ["testing": false],
|
||||
"Target6": ["test", "analyze"],
|
||||
],
|
||||
"preActions": [
|
||||
[
|
||||
"script": "echo Before Build",
|
||||
"name": "Before Build",
|
||||
"settingsTarget": "Target1",
|
||||
],
|
||||
],
|
||||
],
|
||||
]
|
||||
let scheme = try Scheme(name: "Scheme", jsonDictionary: schemeDictionary)
|
||||
let expectedTargets: [Scheme.BuildTarget] = [
|
||||
Scheme.BuildTarget(target: "Target1", buildTypes: BuildType.all),
|
||||
Scheme.BuildTarget(target: "Target2", buildTypes: [.testing, .analyzing]),
|
||||
Scheme.BuildTarget(target: "Target3", buildTypes: []),
|
||||
Scheme.BuildTarget(target: "Target4", buildTypes: [.testing]),
|
||||
Scheme.BuildTarget(target: "Target5", buildTypes: []),
|
||||
Scheme.BuildTarget(target: "Target6", buildTypes: [.testing, .analyzing]),
|
||||
]
|
||||
try expect(scheme.name) == "Scheme"
|
||||
try expect(scheme.build.targets) == expectedTargets
|
||||
try expect(scheme.build.preActions.first?.script) == "echo Before Build"
|
||||
try expect(scheme.build.preActions.first?.name) == "Before Build"
|
||||
try expect(scheme.build.preActions.first?.settingsTarget) == "Target1"
|
||||
|
||||
try expect(scheme.build.parallelizeBuild) == false
|
||||
try expect(scheme.build.buildImplicitDependencies) == false
|
||||
}
|
||||
|
||||
$0.it("parses schemes variables") {
|
||||
let schemeDictionary: [String: Any] = [
|
||||
"build": [
|
||||
"targets": ["Target1": "all"],
|
||||
],
|
||||
"run": [
|
||||
"environmentVariables": [
|
||||
["variable": "BOOL_TRUE", "value": true],
|
||||
["variable": "BOOL_YES", "value": "YES"],
|
||||
["variable": "ENVIRONMENT", "value": "VARIABLE"],
|
||||
["variable": "OTHER_ENV_VAR", "value": "VAL", "isEnabled": false],
|
||||
],
|
||||
],
|
||||
"test": [
|
||||
"environmentVariables": [
|
||||
"BOOL_TRUE": true,
|
||||
"BOOL_YES": "YES",
|
||||
"TEST": "VARIABLE",
|
||||
],
|
||||
],
|
||||
"profile": [
|
||||
"config": "Release",
|
||||
],
|
||||
]
|
||||
|
||||
let scheme = try Scheme(name: "Scheme", jsonDictionary: schemeDictionary)
|
||||
|
||||
let expectedRunVariables = [
|
||||
XCScheme.EnvironmentVariable(variable: "BOOL_TRUE", value: "YES", enabled: true),
|
||||
XCScheme.EnvironmentVariable(variable: "BOOL_YES", value: "YES", enabled: true),
|
||||
XCScheme.EnvironmentVariable(variable: "ENVIRONMENT", value: "VARIABLE", enabled: true),
|
||||
XCScheme.EnvironmentVariable(variable: "OTHER_ENV_VAR", value: "VAL", enabled: false),
|
||||
]
|
||||
|
||||
let expectedTestVariables = [
|
||||
XCScheme.EnvironmentVariable(variable: "BOOL_TRUE", value: "YES", enabled: true),
|
||||
XCScheme.EnvironmentVariable(variable: "BOOL_YES", value: "YES", enabled: true),
|
||||
XCScheme.EnvironmentVariable(variable: "TEST", value: "VARIABLE", enabled: true),
|
||||
]
|
||||
|
||||
try expect(scheme.run?.environmentVariables) == expectedRunVariables
|
||||
try expect(scheme.test?.environmentVariables) == expectedTestVariables
|
||||
try expect(scheme.profile?.config) == "Release"
|
||||
try expect(scheme.profile?.environmentVariables.isEmpty) == true
|
||||
}
|
||||
|
||||
$0.it("parses settings") {
|
||||
let project = try Project(path: fixturePath + "settings_test.yml")
|
||||
let buildSettings: BuildSettings = ["SETTING": "value"]
|
||||
let configSettings: [String: Settings] = ["config1": Settings(buildSettings: ["SETTING1": "value"])]
|
||||
let groups = ["preset1"]
|
||||
|
||||
let preset1 = Settings(buildSettings: buildSettings, configSettings: [:], groups: [])
|
||||
let preset2 = Settings(buildSettings: [:], configSettings: configSettings, groups: [])
|
||||
let preset3 = Settings(buildSettings: buildSettings, configSettings: configSettings, groups: [])
|
||||
let preset4 = Settings(buildSettings: buildSettings, configSettings: [:], groups: [])
|
||||
let preset5 = Settings(buildSettings: buildSettings, configSettings: [:], groups: groups)
|
||||
let preset6 = Settings(buildSettings: buildSettings, configSettings: configSettings, groups: groups)
|
||||
let preset7 = Settings(buildSettings: buildSettings, configSettings: ["config1": Settings(buildSettings: buildSettings, groups: groups)])
|
||||
let preset8 = Settings(buildSettings: [:], configSettings: ["config1": Settings(configSettings: configSettings)])
|
||||
|
||||
try expect(project.settingGroups.count) == 8
|
||||
try expect(project.settingGroups["preset1"]) == preset1
|
||||
try expect(project.settingGroups["preset2"]) == preset2
|
||||
try expect(project.settingGroups["preset3"]) == preset3
|
||||
try expect(project.settingGroups["preset4"]) == preset4
|
||||
try expect(project.settingGroups["preset5"]) == preset5
|
||||
try expect(project.settingGroups["preset6"]) == preset6
|
||||
try expect(project.settingGroups["preset7"]) == preset7
|
||||
try expect(project.settingGroups["preset8"]) == preset8
|
||||
}
|
||||
|
||||
$0.it("parses run scripts") {
|
||||
var target = validTarget
|
||||
let scripts: [[String: Any]] = [
|
||||
["path": "script.sh"],
|
||||
["script": "shell script\ndo thing", "name": "myscript", "inputFiles": ["file", "file2"], "outputFiles": ["file", "file2"], "shell": "bin/customshell", "runOnlyWhenInstalling": true],
|
||||
]
|
||||
target["prebuildScripts"] = scripts
|
||||
target["postbuildScripts"] = scripts
|
||||
|
||||
let expectedScripts = [
|
||||
BuildScript(script: .path("script.sh")),
|
||||
BuildScript(script: .script("shell script\ndo thing"), name: "myscript", inputFiles: ["file", "file2"], outputFiles: ["file", "file2"], shell: "bin/customshell", runOnlyWhenInstalling: true),
|
||||
]
|
||||
|
||||
let parsedTarget = try Target(name: "test", jsonDictionary: target)
|
||||
try expect(parsedTarget.prebuildScripts) == expectedScripts
|
||||
try expect(parsedTarget.postbuildScripts) == expectedScripts
|
||||
}
|
||||
|
||||
$0.it("parses build rules") {
|
||||
var target = validTarget
|
||||
let buildRules: [[String: Any]] = [
|
||||
[
|
||||
"name": "My Rule",
|
||||
"script": "my script",
|
||||
"filePattern": "*.swift",
|
||||
"outputFiles": ["file1", "file2"],
|
||||
"outputFilesCompilerFlags": ["-a", "-b"],
|
||||
],
|
||||
[
|
||||
"compilerSpec": "apple.tool",
|
||||
"fileType": "sourcecode.swift",
|
||||
],
|
||||
]
|
||||
target["buildRules"] = buildRules
|
||||
|
||||
let expectedBuildRules = [
|
||||
BuildRule(fileType: .pattern("*.swift"), action: .script("my script"), name: "My Rule", outputFiles: ["file1", "file2"], outputFilesCompilerFlags: ["-a", "-b"]),
|
||||
BuildRule(fileType: .type("sourcecode.swift"), action: .compilerSpec("apple.tool")),
|
||||
]
|
||||
|
||||
let parsedTarget = try Target(name: "test", jsonDictionary: target)
|
||||
try expect(parsedTarget.buildRules) == expectedBuildRules
|
||||
}
|
||||
|
||||
$0.it("parses options") {
|
||||
let options = SpecOptions(
|
||||
carthageBuildPath: "../Carthage/Build",
|
||||
carthageExecutablePath: "../bin/carthage",
|
||||
createIntermediateGroups: true,
|
||||
bundleIdPrefix: "com.test",
|
||||
developmentLanguage: "ja",
|
||||
deploymentTarget: DeploymentTarget(
|
||||
iOS: "11.1",
|
||||
tvOS: "10.0",
|
||||
watchOS: "3.0",
|
||||
macOS: "10.12.1"
|
||||
)
|
||||
)
|
||||
let expected = Project(basePath: "", name: "test", options: options)
|
||||
let dictionary: [String: Any] = ["options": [
|
||||
"carthageBuildPath": "../Carthage/Build",
|
||||
"carthageExecutablePath": "../bin/carthage",
|
||||
"bundleIdPrefix": "com.test",
|
||||
"createIntermediateGroups": true,
|
||||
"developmentLanguage": "ja",
|
||||
"deploymentTarget": ["iOS": 11.1, "tvOS": 10.0, "watchOS": "3", "macOS": "10.12.1"],
|
||||
]]
|
||||
let parsedSpec = try getProjectSpec(dictionary)
|
||||
try expect(parsedSpec) == expected
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@discardableResult
|
||||
fileprivate func getProjectSpec(_ project: [String: Any], file: String = #file, line: Int = #line) throws -> Project {
|
||||
var projectDictionary: [String: Any] = ["name": "test"]
|
||||
for (key, value) in project {
|
||||
projectDictionary[key] = value
|
||||
}
|
||||
do {
|
||||
return try Project(basePath: "", jsonDictionary: projectDictionary)
|
||||
} catch {
|
||||
throw failure("\(error)", file: file, line: line)
|
||||
}
|
||||
}
|
||||
|
||||
fileprivate func expectSpecError(_ project: [String: Any], _ expectedError: SpecParsingError, file: String = #file, line: Int = #line) throws {
|
||||
try expectError(expectedError, file: file, line: line) {
|
||||
try getProjectSpec(project)
|
||||
}
|
||||
}
|
||||
|
||||
fileprivate func expectTargetError(_ target: [String: Any], _ expectedError: SpecParsingError, file: String = #file, line: Int = #line) throws {
|
||||
try expectError(expectedError, file: file, line: line) {
|
||||
_ = try Target(name: "test", jsonDictionary: target)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,17 +2,28 @@ import Foundation
|
||||
import ProjectSpec
|
||||
import Spectre
|
||||
import xcproj
|
||||
import PathKit
|
||||
|
||||
func expectError<T: Error>(_ expectedError: T, _ closure: () throws -> Void) throws where T: CustomStringConvertible {
|
||||
let fixturePath = Path(#file).parent().parent() + "Fixtures"
|
||||
|
||||
func doThrowing<T>(file: String = #file, line: Int = #line, _ closure: () throws -> T) throws -> T {
|
||||
do {
|
||||
return try closure()
|
||||
} catch {
|
||||
throw failure(String(describing: error), file: file, line: line)
|
||||
}
|
||||
}
|
||||
|
||||
func expectError<T: Error>(_ expectedError: T, function: String = #function, file: String = #file, line: Int = #line, _ closure: () throws -> Void) throws where T: CustomStringConvertible {
|
||||
do {
|
||||
try closure()
|
||||
} catch let error as T {
|
||||
try expect(error.description) == expectedError.description
|
||||
try expect(error.description, file: file, line: line, function: function) == expectedError.description
|
||||
return
|
||||
} catch {
|
||||
throw failure("Supposed to fail with \"\(expectedError)\"")
|
||||
throw failure("Supposed to fail with \"\(expectedError)\"", function: function, file: file, line: line)
|
||||
}
|
||||
throw failure("Supposed to fail with \"\(expectedError)\"")
|
||||
throw failure("Supposed to fail with \"\(expectedError)\"", function: function, file: file, line: line)
|
||||
}
|
||||
|
||||
struct ExpectationFailure: FailureType {
|
||||
@@ -32,7 +43,7 @@ struct ExpectationFailure: FailureType {
|
||||
|
||||
open class ArrayExpectation<T>: ExpectationType {
|
||||
public typealias ValueType = Array<T>
|
||||
open let expression: () throws -> ValueType?
|
||||
public let expression: () throws -> ValueType?
|
||||
|
||||
let file: String
|
||||
let line: Int
|
||||
|
||||
@@ -1,12 +0,0 @@
|
||||
import Foundation
|
||||
import XCTest
|
||||
|
||||
class XCodeGenKitTests: XCTestCase {
|
||||
|
||||
func testXcodeGenKit() {
|
||||
projectGeneratorTests()
|
||||
projectLoadingTests()
|
||||
fixtureTests()
|
||||
projectSpecTests()
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user