mirror of
https://github.com/GLEGram/GLEGram-iOS.git
synced 2026-04-23 19:36:26 +02:00
4647310322
Based on Swiftgram 12.5 (Telegram iOS 12.5). All GLEGram features ported and organized in GLEGram/ folder. Features: Ghost Mode, Saved Deleted Messages, Content Protection Bypass, Font Replacement, Fake Profile, Chat Export, Plugin System, and more. See CHANGELOG_12.5.md for full details.
1391 lines
65 KiB
Swift
1391 lines
65 KiB
Swift
import PathKit
|
|
import ProjectSpec
|
|
import Spectre
|
|
@testable import XcodeGenKit
|
|
import XcodeProj
|
|
import XCTest
|
|
import Yams
|
|
import TestSupport
|
|
|
|
class SourceGeneratorTests: XCTestCase {
|
|
|
|
func testSourceGenerator() throws {
|
|
try skipIfNecessary()
|
|
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 createFile(at relativePath: Path, content: String) throws -> Path {
|
|
let filePath = directoryPath + relativePath
|
|
try filePath.parent().mkpath()
|
|
try filePath.write(content)
|
|
return filePath
|
|
}
|
|
|
|
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
|
|
- C2.0:
|
|
- c.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)
|
|
try pbxProj.expectFile(paths: ["Sources", "A", "C2.0", "c.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.copyFilesBuildPhases.first
|
|
try expect(buildPhase?.dstSubfolderSpec) == .frameworks
|
|
let fileReference = pbxProj.getFileReference(
|
|
paths: ["Sources", "Foo.framework"],
|
|
names: ["Sources", "Foo.framework"]
|
|
)
|
|
let buildFile = try unwrap(pbxProj.buildFiles
|
|
.first(where: { $0.file == fileReference }))
|
|
try expect(buildPhase?.files?.count) == 1
|
|
try expect(buildPhase?.files?.contains(buildFile)) == true
|
|
}
|
|
|
|
$0.it("generates core data models") {
|
|
let directories = """
|
|
Sources:
|
|
model.xcdatamodeld:
|
|
- .xccurrentversion
|
|
- model.xcdatamodel
|
|
- model1.xcdatamodel
|
|
- model2.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()
|
|
let fileReference = try unwrap(pbxProj.fileReferences.first(where: { $0.nameOrPath == "model2.xcdatamodel" }))
|
|
let versionGroup = try unwrap(pbxProj.versionGroups.first)
|
|
try expect(versionGroup.currentVersion) == fileReference
|
|
try expect(versionGroup.children.count) == 3
|
|
try expect(versionGroup.path) == "model.xcdatamodeld"
|
|
try expect(fileReference.path) == "model2.xcdatamodel"
|
|
}
|
|
|
|
$0.it("generates core data mapping models") {
|
|
let directories = """
|
|
Sources:
|
|
model.xcmappingmodel:
|
|
- xcmapping.xml
|
|
"""
|
|
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", "model.xcmappingmodel"], buildPhase: .sources)
|
|
}
|
|
|
|
$0.it("generates variant groups") {
|
|
let directories = """
|
|
Sources:
|
|
Base.lproj:
|
|
- LocalizedStoryboard.storyboard
|
|
en.lproj:
|
|
- LocalizedStoryboard.strings
|
|
"""
|
|
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()
|
|
|
|
func getFileReferences(_ path: String) -> [PBXFileReference] {
|
|
pbxProj.fileReferences.filter { $0.path == path }
|
|
}
|
|
|
|
func getVariableGroups(_ name: String?) -> [PBXVariantGroup] {
|
|
pbxProj.variantGroups.filter { $0.name == name }
|
|
}
|
|
|
|
let resourceName = "LocalizedStoryboard.storyboard"
|
|
let baseResource = "Base.lproj/LocalizedStoryboard.storyboard"
|
|
let localizedResource = "en.lproj/LocalizedStoryboard.strings"
|
|
|
|
let variableGroup = try unwrap(getVariableGroups(resourceName).first)
|
|
|
|
do {
|
|
let refs = getFileReferences(baseResource)
|
|
try expect(refs.count) == 1
|
|
try expect(variableGroup.children.filter { $0 == refs.first }.count) == 1
|
|
}
|
|
|
|
do {
|
|
let refs = getFileReferences(localizedResource)
|
|
try expect(refs.count) == 1
|
|
try expect(variableGroup.children.filter { $0 == refs.first }.count) == 1
|
|
}
|
|
}
|
|
|
|
$0.it("handles localized resources") {
|
|
let directories = """
|
|
App:
|
|
Resources:
|
|
en-CA.lproj:
|
|
- empty.json
|
|
- Localizable.strings
|
|
en-US.lproj:
|
|
- empty.json
|
|
- Localizable.strings
|
|
en.lproj:
|
|
- empty.json
|
|
- Localizable.strings
|
|
fonts:
|
|
SFUI:
|
|
- SFUILight.ttf
|
|
"""
|
|
try createDirectories(directories)
|
|
|
|
let target = Target(name: "Test", type: .application, platform: .iOS, sources: [TargetSource(path: "App/Resources")])
|
|
|
|
let options = SpecOptions(createIntermediateGroups: true)
|
|
let project = Project(basePath: directoryPath, name: "Test", targets: [target], options: options)
|
|
|
|
let outputXcodeProj = try project.generateXcodeProject()
|
|
try outputXcodeProj.write(path: directoryPath)
|
|
|
|
let inputXcodeProj = try XcodeProj(path: directoryPath)
|
|
let pbxProj = inputXcodeProj.pbxproj
|
|
|
|
func getFileReferences(_ path: String) -> [PBXFileReference] {
|
|
pbxProj.fileReferences.filter { $0.path == path }
|
|
}
|
|
|
|
func getVariableGroups(_ name: String?) -> [PBXVariantGroup] {
|
|
pbxProj.variantGroups.filter { $0.name == name }
|
|
}
|
|
|
|
let stringsResourceName = "Localizable.strings"
|
|
let jsonResourceName = "empty.json"
|
|
|
|
let stringsVariableGroup = try unwrap(getVariableGroups(stringsResourceName).first)
|
|
|
|
let jsonVariableGroup = try unwrap(getVariableGroups(jsonResourceName).first)
|
|
|
|
let stringsResource = "en.lproj/Localizable.strings"
|
|
let jsonResource = "en-CA.lproj/empty.json"
|
|
|
|
do {
|
|
let refs = getFileReferences(stringsResource)
|
|
try expect(refs.count) == 1
|
|
try expect(refs.first!.uuid.hasPrefix("TEMP")) == false
|
|
try expect(stringsVariableGroup.children.filter { $0 == refs.first }.count) == 1
|
|
}
|
|
|
|
do {
|
|
let refs = getFileReferences(jsonResource)
|
|
try expect(refs.count) == 1
|
|
try expect(refs.first!.uuid.hasPrefix("TEMP")) == false
|
|
try expect(jsonVariableGroup.children.filter { $0 == refs.first }.count) == 1
|
|
}
|
|
}
|
|
|
|
$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
|
|
- b.alsoIgnored
|
|
- a.ignored
|
|
- a.alsoIgnored
|
|
- 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
|
|
- a.playground:
|
|
- Sources:
|
|
- a.swift
|
|
- Resources
|
|
"""
|
|
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",
|
|
"*.playground",
|
|
"**/*.ignored",
|
|
"A/B/**/*.alsoIgnored",
|
|
]
|
|
|
|
let target = Target(name: "Test", type: .application, platform: .iOS, sources: [TargetSource(path: "Sources", excludes: excludes)])
|
|
|
|
func test(generateEmptyDirectories: Bool) throws {
|
|
let options = SpecOptions(generateEmptyDirectories: generateEmptyDirectories)
|
|
let project = Project(basePath: directoryPath, name: "Test", targets: [target], options: options)
|
|
let pbxProj = try project.generatePbxProj()
|
|
try pbxProj.expectFile(paths: ["Sources", "A", "a.swift"])
|
|
try pbxProj.expectFile(paths: ["Sources", "A", "a.alsoIgnored"])
|
|
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"])
|
|
try pbxProj.expectFileMissing(paths: ["Sources", "a.playground"])
|
|
try pbxProj.expectFileMissing(paths: ["Sources", "A", "a.ignored"])
|
|
try pbxProj.expectFileMissing(paths: ["Sources", "A", "B", "b.ignored"])
|
|
try pbxProj.expectFileMissing(paths: ["Sources", "A", "B", "b.alsoIgnored"])
|
|
}
|
|
|
|
try test(generateEmptyDirectories: false)
|
|
try test(generateEmptyDirectories: true)
|
|
}
|
|
|
|
$0.it("excludes certain ignored files") {
|
|
let directories = """
|
|
Sources:
|
|
A:
|
|
- a.swift
|
|
- .DS_Store
|
|
- a.swift.orig
|
|
"""
|
|
try createDirectories(directories)
|
|
|
|
let target = Target(name: "Test", type: .application, platform: .iOS, sources: [TargetSource(path: "Sources")])
|
|
let project = Project(basePath: directoryPath, name: "Test", targets: [target])
|
|
let pbxProj = try project.generatePbxProj()
|
|
try pbxProj.expectFile(paths: ["Sources", "A", "a.swift"])
|
|
try pbxProj.expectFileMissing(paths: ["Sources", "A", ".DS_Store"])
|
|
try pbxProj.expectFileMissing(paths: ["Sources", "A", "a.swift.orig"])
|
|
}
|
|
|
|
$0.it("generates file sources") {
|
|
let directories = """
|
|
Sources:
|
|
A:
|
|
- a.swift
|
|
- Assets.xcassets
|
|
- B:
|
|
- b.swift
|
|
- c.jpg
|
|
- D2.0:
|
|
- d.swift
|
|
- E.bundle:
|
|
- e.json
|
|
"""
|
|
try createDirectories(directories)
|
|
|
|
let target = Target(name: "Test", type: .application, platform: .iOS, sources: [
|
|
"Sources/A/a.swift",
|
|
"Sources/A/B/b.swift",
|
|
"Sources/A/D2.0/d.swift",
|
|
"Sources/A/Assets.xcassets",
|
|
"Sources/A/E.bundle/e.json",
|
|
"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/D2.0", "d.swift"], names: ["D2.0", "d.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)
|
|
try pbxProj.expectFile(paths: ["Sources/A/E.bundle", "e.json"], names: ["E.bundle", "e.json"], 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
|
|
B:
|
|
- b.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",
|
|
TargetSource(path: "Sources/B", createIntermediateGroups: false),
|
|
])
|
|
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: ["..", "OtherDirectory", "C", "D", "e.swift"], names: [".", "OtherDirectory", "C", "D", "e.swift"], buildPhase: .sources)
|
|
try pbxProj.expectFile(paths: ["Sources/B", "b.swift"], names: ["B", "b.swift"], buildPhase: .sources)
|
|
}
|
|
|
|
$0.it("generates custom groups") {
|
|
|
|
let directories = """
|
|
- Sources:
|
|
- a.swift
|
|
- A:
|
|
- b.swift
|
|
- F:
|
|
- G:
|
|
- h.swift
|
|
- i.swift
|
|
- B:
|
|
- b.swift
|
|
- C:
|
|
- c.swift
|
|
"""
|
|
try createDirectories(directories)
|
|
|
|
let target = Target(name: "Test", type: .application, platform: .iOS, sources: [
|
|
TargetSource(path: "Sources/a.swift", group: "CustomGroup1"),
|
|
TargetSource(path: "Sources/A/b.swift", group: "CustomGroup1"),
|
|
TargetSource(path: "Sources/F/G/h.swift", group: "CustomGroup1"),
|
|
TargetSource(path: "Sources/B", group: "CustomGroup2", createIntermediateGroups: false),
|
|
TargetSource(path: "Sources/F/G/i.swift", group: "Sources/F/G/CustomGroup3"),
|
|
])
|
|
|
|
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: ["CustomGroup1", "Sources/a.swift"], names: ["CustomGroup1", "a.swift"], buildPhase: .sources)
|
|
try pbxProj.expectFile(paths: ["CustomGroup1", "Sources/A/b.swift"], names: ["CustomGroup1", "b.swift"], buildPhase: .sources)
|
|
try pbxProj.expectFile(paths: ["CustomGroup1", "Sources/F/G/h.swift"], names: ["CustomGroup1", "h.swift"], buildPhase: .sources)
|
|
try pbxProj.expectFile(paths: ["Sources", "F", "G", "CustomGroup3", "i.swift"], names: ["Sources", "F", "G", "CustomGroup3", "i.swift"], buildPhase: .sources)
|
|
try pbxProj.expectFile(paths: ["CustomGroup2", "Sources/B", "b.swift"], names: ["CustomGroup2", "B", "b.swift"], buildPhase: .sources)
|
|
try pbxProj.expectFile(paths: ["CustomGroup2", "Sources/B", "C", "c.swift"], names: ["CustomGroup2", "B", "C", "c.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
|
|
- GoogleService-Info.plist
|
|
- file.xcconfig
|
|
- Localizable.xcstrings
|
|
B:
|
|
- file.swift
|
|
- file.xcassets
|
|
- file.h
|
|
- Sample.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
|
|
- file.metal
|
|
- file.mlmodel
|
|
- file.mlpackage
|
|
- file.mlmodelc
|
|
- Info.plist
|
|
- Intent.intentdefinition
|
|
- Configuration.storekit
|
|
- Settings.bundle:
|
|
- en.lproj:
|
|
- Root.strings
|
|
- Root.plist
|
|
- WithPeriod2.0:
|
|
- file.swift
|
|
- Documentation.docc
|
|
"""
|
|
try createDirectories(directories)
|
|
|
|
let target = Target(name: "Test", type: .framework, platform: .iOS, sources: [
|
|
TargetSource(path: "A", buildPhase: .resources),
|
|
TargetSource(path: "B", buildPhase: BuildPhaseSpec.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", "GoogleService-Info.plist"], buildPhase: .resources)
|
|
try pbxProj.expectFile(paths: ["A", "file.xcconfig"], buildPhase: .resources)
|
|
try pbxProj.expectFile(paths: ["A", "Localizable.xcstrings"], buildPhase: .resources)
|
|
|
|
try pbxProj.expectFile(paths: ["B", "file.swift"], buildPhase: BuildPhaseSpec.none)
|
|
try pbxProj.expectFile(paths: ["B", "file.xcassets"], buildPhase: BuildPhaseSpec.none)
|
|
try pbxProj.expectFile(paths: ["B", "file.h"], buildPhase: BuildPhaseSpec.none)
|
|
try pbxProj.expectFile(paths: ["B", "Sample.plist"], buildPhase: BuildPhaseSpec.none)
|
|
try pbxProj.expectFile(paths: ["B", "file.xcconfig"], buildPhase: BuildPhaseSpec.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: BuildPhaseSpec.none)
|
|
try pbxProj.expectFile(paths: ["C", "file.entitlements"], buildPhase: BuildPhaseSpec.none)
|
|
try pbxProj.expectFile(paths: ["C", "file.gpx"], buildPhase: BuildPhaseSpec.none)
|
|
try pbxProj.expectFile(paths: ["C", "file.apns"], buildPhase: BuildPhaseSpec.none)
|
|
try pbxProj.expectFile(paths: ["C", "file.xcconfig"], buildPhase: BuildPhaseSpec.none)
|
|
try pbxProj.expectFile(paths: ["C", "file.xcconfig"], buildPhase: BuildPhaseSpec.none)
|
|
try pbxProj.expectFile(paths: ["C", "file.xcconfig"], buildPhase: BuildPhaseSpec.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: BuildPhaseSpec.none)
|
|
try pbxProj.expectFile(paths: ["C", "file.metal"], buildPhase: .sources)
|
|
try pbxProj.expectFile(paths: ["C", "file.mlmodel"], buildPhase: .sources)
|
|
try pbxProj.expectFile(paths: ["C", "file.mlpackage"], buildPhase: .sources)
|
|
try pbxProj.expectFile(paths: ["C", "file.mlmodelc"], buildPhase: .resources)
|
|
try pbxProj.expectFile(paths: ["C", "Intent.intentdefinition"], buildPhase: .sources)
|
|
try pbxProj.expectFile(paths: ["C", "Configuration.storekit"], buildPhase: .resources)
|
|
try pbxProj.expectFile(paths: ["C", "Settings.bundle"], buildPhase: .resources)
|
|
try pbxProj.expectFileMissing(paths: ["C", "Settings.bundle", "en.lproj"])
|
|
try pbxProj.expectFileMissing(paths: ["C", "Settings.bundle", "en.lproj", "Root.strings"])
|
|
try pbxProj.expectFileMissing(paths: ["C", "Settings.bundle", "Root.plist"])
|
|
try pbxProj.expectFileMissing(paths: ["C", "WithPeriod2.0"])
|
|
try pbxProj.expectFile(paths: ["C", "WithPeriod2.0", "file.swift"], buildPhase: .sources)
|
|
try pbxProj.expectFile(paths: ["C", "Documentation.docc"], buildPhase: .sources)
|
|
}
|
|
|
|
$0.it("only omits the defined Info.plist from resource build phases but not other plists") {
|
|
try createDirectories("""
|
|
A:
|
|
- A-Info.plist
|
|
B:
|
|
- Info.plist
|
|
- GoogleServices-Info.plist
|
|
C:
|
|
- Info.plist
|
|
- Info-Production.plist
|
|
D:
|
|
- Info-Staging.plist
|
|
- Info-Production.plist
|
|
""")
|
|
|
|
// Explicit plist.path value is respected
|
|
let targetA = Target(
|
|
name: "A",
|
|
type: .application,
|
|
platform: .iOS,
|
|
sources: ["A"],
|
|
info: Plist(path: "A/A-Info.plist")
|
|
)
|
|
|
|
// Automatically picks first 'Info.plist' at the top-level
|
|
let targetB = Target(
|
|
name: "B",
|
|
type: .application,
|
|
platform: .iOS,
|
|
sources: ["B"]
|
|
)
|
|
|
|
// Also respects INFOPLIST_FILE, ignores other files named Info.plist
|
|
let targetC = Target(
|
|
name: "C",
|
|
type: .application,
|
|
platform: .iOS,
|
|
settings: Settings(dictionary: [
|
|
"INFOPLIST_FILE": "C/Info-Production.plist"
|
|
]),
|
|
sources: ["C"]
|
|
)
|
|
|
|
// Does not support INFOPLIST_FILE value that requires expanding
|
|
let targetD = Target(
|
|
name: "D",
|
|
type: .application,
|
|
platform: .iOS,
|
|
settings: Settings(dictionary: [
|
|
"ENVIRONMENT": "Production",
|
|
"INFOPLIST_FILE": "D/Info-${ENVIRONMENT}.plist"
|
|
]),
|
|
sources: ["D"]
|
|
)
|
|
|
|
let project = Project(basePath: directoryPath.absolute(), name: "Test", targets: [targetA, targetB, targetC, targetD])
|
|
let pbxProj = try project.generatePbxProj()
|
|
|
|
try pbxProj.expectFile(paths: ["A", "A-Info.plist"], buildPhase: BuildPhaseSpec.none)
|
|
|
|
try pbxProj.expectFile(paths: ["B", "Info.plist"], buildPhase: BuildPhaseSpec.none)
|
|
try pbxProj.expectFile(paths: ["B", "GoogleServices-Info.plist"], buildPhase: .resources)
|
|
|
|
try pbxProj.expectFile(paths: ["C", "Info.plist"], buildPhase: .resources)
|
|
try pbxProj.expectFile(paths: ["C", "Info-Production.plist"], buildPhase: BuildPhaseSpec.none)
|
|
|
|
try pbxProj.expectFile(paths: ["D", "Info-Staging.plist"], buildPhase: .resources)
|
|
try pbxProj.expectFile(paths: ["D", "Info-Production.plist"], buildPhase: .resources)
|
|
}
|
|
|
|
$0.it("sets file type properties") {
|
|
let directories = """
|
|
A:
|
|
- file.resource1
|
|
- file.source1
|
|
- file.abc:
|
|
- file.a
|
|
- file.exclude1
|
|
- file.unphased1
|
|
- ignored.swift
|
|
"""
|
|
try createDirectories(directories)
|
|
|
|
let target = Target(name: "Test", type: .framework, platform: .iOS, sources: [
|
|
TargetSource(path: "A"),
|
|
])
|
|
let project = Project(basePath: directoryPath, name: "Test", targets: [target], options: .init(fileTypes: [
|
|
"abc": FileType(buildPhase: .sources),
|
|
"source1": FileType(buildPhase: .sources, attributes: ["a1", "a2"], resourceTags: ["r1", "r2"], compilerFlags: ["-c1", "-c2"]),
|
|
"resource1": FileType(buildPhase: .resources, attributes: ["a1", "a2"], resourceTags: ["r1", "r2"], compilerFlags: ["-c1", "-c2"]),
|
|
"unphased1": FileType(buildPhase: BuildPhaseSpec.none),
|
|
"swift": FileType(buildPhase: .resources),
|
|
]))
|
|
|
|
let pbxProj = try project.generatePbxProj()
|
|
try pbxProj.expectFile(paths: ["A", "file.abc"], buildPhase: .sources)
|
|
try pbxProj.expectFile(paths: ["A", "file.source1"], buildPhase: .sources)
|
|
try pbxProj.expectFile(paths: ["A", "file.resource1"], buildPhase: .resources)
|
|
try pbxProj.expectFile(paths: ["A", "file.unphased1"], buildPhase: BuildPhaseSpec.none)
|
|
try pbxProj.expectFile(paths: ["A", "ignored.swift"], buildPhase: .resources)
|
|
|
|
do {
|
|
let fileReference = try unwrap(pbxProj.getFileReference(paths: ["A", "file.resource1"], names: ["A", "file.resource1"]))
|
|
let buildFile = try unwrap(pbxProj.buildFiles.first(where: { $0.file === fileReference }))
|
|
let settings = NSDictionary(dictionary: buildFile.settings ?? [:])
|
|
try expect(settings) == [
|
|
"ATTRIBUTES": ["a1", "a2"],
|
|
"ASSET_TAGS": ["r1", "r2"],
|
|
]
|
|
}
|
|
do {
|
|
let fileReference = try unwrap(pbxProj.getFileReference(paths: ["A", "file.source1"], names: ["A", "file.source1"]))
|
|
let buildFile = try unwrap(pbxProj.buildFiles.first(where: { $0.file === fileReference }))
|
|
let settings = NSDictionary(dictionary: buildFile.settings ?? [:])
|
|
try expect(settings) == [
|
|
"ATTRIBUTES": ["a1", "a2"],
|
|
"COMPILER_FLAGS": "-c1 -c2",
|
|
]
|
|
}
|
|
}
|
|
|
|
$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.buildPhases.first(where: { $0.buildPhase == BuildPhase.sources })!
|
|
|
|
try expect(sourcesBuildPhase.files?.count) == 1
|
|
}
|
|
|
|
$0.it("add only carthage dependencies with same platform") {
|
|
let directories = """
|
|
A:
|
|
- file.swift
|
|
"""
|
|
try createDirectories(directories)
|
|
|
|
let watchTarget = Target(name: "Watch", type: .watch2App, platform: .watchOS, sources: ["A"], dependencies: [Dependency(type: .carthage(findFrameworks: false, linkType: .dynamic), reference: "Alamofire_watch")])
|
|
let watchDependency = Dependency(type: .target, reference: "Watch")
|
|
let target = Target(name: "Test", type: .application, platform: .iOS, sources: ["A"], dependencies: [Dependency(type: .carthage(findFrameworks: false, linkType: .dynamic), reference: "Alamofire"), watchDependency])
|
|
let project = Project(basePath: directoryPath, name: "Test", targets: [target, watchTarget])
|
|
|
|
let pbxProj = try project.generatePbxProj()
|
|
let carthagePhase = pbxProj.nativeTargets.first(where: { $0.name == "Test" })?.buildPhases.first(where: { $0 is PBXShellScriptBuildPhase }) as? PBXShellScriptBuildPhase
|
|
try expect(carthagePhase?.inputPaths) == ["$(SRCROOT)/Carthage/Build/iOS/Alamofire.framework"]
|
|
}
|
|
|
|
$0.it("derived directories are sorted last") {
|
|
let directories = """
|
|
A:
|
|
- file.swift
|
|
P:
|
|
- file.swift
|
|
S:
|
|
- file.swift
|
|
"""
|
|
try createDirectories(directories)
|
|
|
|
let target = Target(name: "Test", type: .application, platform: .iOS, sources: ["A", "P", "S"], dependencies: [Dependency(type: .carthage(findFrameworks: false, linkType: .dynamic), reference: "Alamofire")])
|
|
let project = Project(basePath: directoryPath, name: "Test", targets: [target])
|
|
|
|
let pbxProj = try project.generatePbxProj()
|
|
let groups = try pbxProj.getMainGroup().children.map { $0.nameOrPath }
|
|
try expect(groups) == ["A", "P", "S", "Frameworks", "Products"]
|
|
}
|
|
|
|
$0.it("sorts files") {
|
|
let directories = """
|
|
A:
|
|
- A.swift
|
|
Source:
|
|
- file.swift
|
|
Sources:
|
|
- file3.swift
|
|
- file.swift
|
|
- 10file.a
|
|
- 1file.a
|
|
- file2.swift
|
|
- group2:
|
|
- file.swift
|
|
- group:
|
|
- file.swift
|
|
Z:
|
|
- A:
|
|
- file.swift
|
|
B:
|
|
- file.swift
|
|
"""
|
|
try createDirectories(directories)
|
|
|
|
let target = Target(name: "Test", type: .application, platform: .iOS, sources: [
|
|
"Sources",
|
|
TargetSource(path: "Source", name: "S"),
|
|
"A",
|
|
TargetSource(path: "Z/A", name: "B"),
|
|
"B",
|
|
], dependencies: [Dependency(type: .carthage(findFrameworks: false, linkType: .dynamic), reference: "Alamofire")])
|
|
let project = Project(basePath: directoryPath, name: "Test", targets: [target])
|
|
|
|
let pbxProj = try project.generatePbxProj()
|
|
let mainGroup = try pbxProj.getMainGroup()
|
|
let mainGroupNames = mainGroup.children.prefix(5).map { $0.name }
|
|
try expect(mainGroupNames) == [
|
|
nil,
|
|
nil,
|
|
"B",
|
|
"S",
|
|
nil,
|
|
]
|
|
let mainGroupPaths = mainGroup.children.prefix(5).map { $0.path }
|
|
try expect(mainGroupPaths) == [
|
|
"A",
|
|
"B",
|
|
"Z/A",
|
|
"Source",
|
|
"Sources",
|
|
]
|
|
|
|
let group = mainGroup.children.compactMap { $0 as? PBXGroup }.first { $0.path == "Sources" }!
|
|
let names = group.children.map { $0.name }
|
|
try expect(names) == [
|
|
nil,
|
|
nil,
|
|
nil,
|
|
nil,
|
|
nil,
|
|
nil,
|
|
nil,
|
|
]
|
|
let paths = group.children.map { $0.path }
|
|
try expect(paths) == [
|
|
"1file.a",
|
|
"10file.a",
|
|
"file.swift",
|
|
"file2.swift",
|
|
"file3.swift",
|
|
"group",
|
|
"group2",
|
|
]
|
|
}
|
|
|
|
$0.it("adds missing optional files and folders") {
|
|
|
|
let target = Target(name: "Test", type: .application, platform: .iOS, sources: [
|
|
TargetSource(path: "File1.swift", optional: true),
|
|
TargetSource(path: "File2.swift", type: .file, optional: true),
|
|
TargetSource(path: "Group", type: .folder, optional: true),
|
|
])
|
|
let project = Project(basePath: directoryPath, name: "Test", targets: [target])
|
|
let pbxProj = try project.generatePbxProj()
|
|
try pbxProj.expectFile(paths: ["File1.swift"])
|
|
try pbxProj.expectFile(paths: ["File2.swift"])
|
|
}
|
|
|
|
$0.it("allows missing optional groups") {
|
|
|
|
let target = Target(name: "Test", type: .application, platform: .iOS, sources: [
|
|
TargetSource(path: "Group1", optional: true),
|
|
TargetSource(path: "Group2", type: .group, optional: true),
|
|
TargetSource(path: "Group3", type: .group, optional: true),
|
|
])
|
|
let project = Project(basePath: directoryPath, name: "Test", targets: [target])
|
|
_ = try project.generatePbxProj()
|
|
}
|
|
|
|
$0.it("relative path items outside base path are grouped together") {
|
|
let directories = """
|
|
Sources:
|
|
- Inside:
|
|
- a.swift
|
|
- Inside2:
|
|
- b.swift
|
|
"""
|
|
try createDirectories(directories)
|
|
|
|
let outOfSourceFile1 = outOfRootPath + "Outside/a.swift"
|
|
try outOfSourceFile1.parent().mkpath()
|
|
try outOfSourceFile1.write("")
|
|
|
|
let outOfSourceFile2 = outOfRootPath + "Outside/Outside2/b.swift"
|
|
try outOfSourceFile2.parent().mkpath()
|
|
try outOfSourceFile2.write("")
|
|
|
|
let target = Target(name: "Test", type: .application, platform: .iOS, sources: [
|
|
"Sources",
|
|
"../OtherDirectory",
|
|
])
|
|
let project = Project(basePath: directoryPath, name: "Test", targets: [target])
|
|
|
|
let pbxProj = try project.generatePbxProj()
|
|
try pbxProj.expectFile(paths: ["Sources", "Inside", "a.swift"], buildPhase: .sources)
|
|
try pbxProj.expectFile(paths: ["Sources", "Inside", "Inside2", "b.swift"], buildPhase: .sources)
|
|
try pbxProj.expectFile(paths: ["../OtherDirectory", "Outside", "a.swift"], names: ["OtherDirectory", "Outside", "a.swift"], buildPhase: .sources)
|
|
try pbxProj.expectFile(paths: ["../OtherDirectory", "Outside", "Outside2", "b.swift"], names: ["OtherDirectory", "Outside", "Outside2", "b.swift"], buildPhase: .sources)
|
|
}
|
|
|
|
$0.it("correctly adds target source attributes") {
|
|
let directories = """
|
|
A:
|
|
- Intent.intentdefinition
|
|
"""
|
|
try createDirectories(directories)
|
|
|
|
let definition: String = "Intent.intentdefinition"
|
|
|
|
let target = Target(name: "Test", type: .framework, platform: .iOS, sources: [
|
|
TargetSource(path: "A/\(definition)", buildPhase: .sources, attributes: ["no_codegen"]),
|
|
])
|
|
let project = Project(basePath: directoryPath, name: "Test", targets: [target])
|
|
|
|
let pbxProj = try project.generatePbxProj()
|
|
|
|
let fileReference = pbxProj.getFileReference(
|
|
paths: ["A", definition],
|
|
names: ["A", definition]
|
|
)
|
|
let buildFile = try unwrap(pbxProj.buildFiles.first(where: { $0.file == fileReference }))
|
|
|
|
try pbxProj.expectFile(paths: ["A", definition], buildPhase: .sources)
|
|
|
|
if (buildFile.settings! as NSDictionary) != (["ATTRIBUTES": ["no_codegen"]] as NSDictionary) {
|
|
throw failure("File does not contain no_codegen attribute")
|
|
}
|
|
}
|
|
|
|
$0.it("includes only the specified files when includes is present") {
|
|
let directories = """
|
|
Sources:
|
|
- file3.swift
|
|
- file3Tests.swift
|
|
- file2.swift
|
|
- file2Tests.swift
|
|
- group2:
|
|
- file.swift
|
|
- fileTests.swift
|
|
- group:
|
|
- file.swift
|
|
- group3:
|
|
- group4:
|
|
- group5:
|
|
- file.swift
|
|
- file5Tests.swift
|
|
- file6Tests.m
|
|
- file6Tests.h
|
|
"""
|
|
try createDirectories(directories)
|
|
|
|
let includes = [
|
|
"**/*Tests.*",
|
|
]
|
|
|
|
let target = Target(name: "Test", type: .application, platform: .iOS, sources: [TargetSource(path: "Sources", includes: includes)])
|
|
|
|
let options = SpecOptions(createIntermediateGroups: true, generateEmptyDirectories: true)
|
|
let project = Project(basePath: directoryPath, name: "Test", targets: [target], options: options)
|
|
let pbxProj = try project.generatePbxProj()
|
|
|
|
try pbxProj.expectFile(paths: ["Sources", "file2Tests.swift"])
|
|
try pbxProj.expectFile(paths: ["Sources", "file3Tests.swift"])
|
|
try pbxProj.expectFile(paths: ["Sources", "group2", "fileTests.swift"])
|
|
try pbxProj.expectFile(paths: ["Sources", "group3", "group4", "group5", "file5Tests.swift"])
|
|
try pbxProj.expectFile(paths: ["Sources", "group3", "group4", "group5", "file6Tests.h"])
|
|
try pbxProj.expectFile(paths: ["Sources", "group3", "group4", "group5", "file6Tests.m"])
|
|
try pbxProj.expectFileMissing(paths: ["Sources", "file2.swift"])
|
|
try pbxProj.expectFileMissing(paths: ["Sources", "file3.swift"])
|
|
try pbxProj.expectFileMissing(paths: ["Sources", "group2", "file.swift"])
|
|
try pbxProj.expectFileMissing(paths: ["Sources", "group", "file.swift"])
|
|
}
|
|
|
|
$0.it("handles includes with no matches correctly") {
|
|
let directories = """
|
|
Sources:
|
|
- file3.swift
|
|
- file3Tests.swift
|
|
- file2.swift
|
|
- file2Tests.swift
|
|
- group2:
|
|
- file.swift
|
|
- fileTests.swift
|
|
- group:
|
|
- file.swift
|
|
"""
|
|
try createDirectories(directories)
|
|
|
|
let includes = [
|
|
"**/*NonExistent.*",
|
|
]
|
|
|
|
let target = Target(name: "Test", type: .application, platform: .iOS, sources: [TargetSource(path: "Sources", includes: includes)])
|
|
|
|
let project = Project(basePath: directoryPath, name: "Test", targets: [target])
|
|
let pbxProj = try project.generatePbxProj()
|
|
|
|
try pbxProj.expectFileMissing(paths: ["Sources", "file2.swift"])
|
|
try pbxProj.expectFileMissing(paths: ["Sources", "file3.swift"])
|
|
try pbxProj.expectFileMissing(paths: ["Sources", "file2Tests.swift"])
|
|
try pbxProj.expectFileMissing(paths: ["Sources", "file3Tests.swift"])
|
|
try pbxProj.expectFileMissing(paths: ["Sources", "group2", "file.swift"])
|
|
try pbxProj.expectFileMissing(paths: ["Sources", "group2", "fileTests.swift"])
|
|
try pbxProj.expectFileMissing(paths: ["Sources", "group", "file.swift"])
|
|
}
|
|
|
|
$0.it("prioritizes excludes over includes when both are present") {
|
|
let directories = """
|
|
Sources:
|
|
- file3.swift
|
|
- file3Tests.swift
|
|
- file2.swift
|
|
- file2Tests.swift
|
|
- group2:
|
|
- file.swift
|
|
- fileTests.swift
|
|
- group:
|
|
- file.swift
|
|
"""
|
|
try createDirectories(directories)
|
|
|
|
let includes = [
|
|
"**/*Tests.*",
|
|
]
|
|
|
|
let excludes = [
|
|
"group2",
|
|
]
|
|
|
|
let target = Target(name: "Test", type: .application, platform: .iOS, sources: [TargetSource(path: "Sources", excludes: excludes, includes: includes)])
|
|
|
|
let project = Project(basePath: directoryPath, name: "Test", targets: [target])
|
|
let pbxProj = try project.generatePbxProj()
|
|
|
|
try pbxProj.expectFile(paths: ["Sources", "file2Tests.swift"])
|
|
try pbxProj.expectFile(paths: ["Sources", "file3Tests.swift"])
|
|
try pbxProj.expectFileMissing(paths: ["Sources", "group2", "fileTests.swift"])
|
|
try pbxProj.expectFileMissing(paths: ["Sources", "file2.swift"])
|
|
try pbxProj.expectFileMissing(paths: ["Sources", "file3.swift"])
|
|
try pbxProj.expectFileMissing(paths: ["Sources", "group2", "file.swift"])
|
|
try pbxProj.expectFileMissing(paths: ["Sources", "group", "file.swift"])
|
|
}
|
|
|
|
$0.describe("Localized sources") {
|
|
$0.context("With localized sources") {
|
|
$0.it("*.intentdefinition should be added to source phase") {
|
|
let directories = """
|
|
Sources:
|
|
Base.lproj:
|
|
- Intents.intentdefinition
|
|
en.lproj:
|
|
- Intents.strings
|
|
ja.lproj:
|
|
- Intents.strings
|
|
"""
|
|
try createDirectories(directories)
|
|
let directoryPath = Path("TestDirectory")
|
|
|
|
let target = Target(name: "IntentDefinitions",
|
|
type: .application,
|
|
platform: .iOS,
|
|
sources: [TargetSource(path: "Sources")])
|
|
let project = Project(basePath: directoryPath,
|
|
name: "IntendDefinitions",
|
|
targets: [target])
|
|
let pbxProj = try project.generatePbxProj()
|
|
let sourceBuildPhase = try unwrap(pbxProj.buildPhases.first { $0.buildPhase == .sources })
|
|
try expect(sourceBuildPhase.files?.compactMap { $0.file?.nameOrPath }) == ["Intents.intentdefinition"]
|
|
}
|
|
}
|
|
|
|
$0.context("With localized sources with buildPhase") {
|
|
$0.it("*.intentdefinition with buildPhase should be added to resource phase") {
|
|
let directories = """
|
|
Sources:
|
|
Base.lproj:
|
|
- Intents.intentdefinition
|
|
en.lproj:
|
|
- Intents.strings
|
|
ja.lproj:
|
|
- Intents.strings
|
|
"""
|
|
try createDirectories(directories)
|
|
let directoryPath = Path("TestDirectory")
|
|
|
|
let target = Target(name: "IntentDefinitions",
|
|
type: .application,
|
|
platform: .iOS,
|
|
sources: [TargetSource(path: "Sources", buildPhase: .resources)])
|
|
let project = Project(basePath: directoryPath,
|
|
name: "IntendDefinitions",
|
|
targets: [target])
|
|
let pbxProj = try project.generatePbxProj()
|
|
let sourceBuildPhase = try unwrap(pbxProj.buildPhases.first { $0.buildPhase == .sources })
|
|
let resourcesBuildPhase = try unwrap(pbxProj.buildPhases.first { $0.buildPhase == .resources })
|
|
try expect(sourceBuildPhase.files) == []
|
|
try expect(resourcesBuildPhase.files?.compactMap { $0.file?.nameOrPath }) == ["Intents.intentdefinition"]
|
|
}
|
|
}
|
|
|
|
$0.it("generates resource tags") {
|
|
let directories = """
|
|
A:
|
|
- resourceFile.mp4
|
|
- resourceFile2.mp4
|
|
- sourceFile.swift
|
|
"""
|
|
try createDirectories(directories)
|
|
|
|
let target = Target(
|
|
name: "Test",
|
|
type: .application,
|
|
platform: .iOS,
|
|
sources: [
|
|
TargetSource(path: "A/resourceFile.mp4", buildPhase: .resources, resourceTags: ["tag1", "tag2"]),
|
|
TargetSource(path: "A/resourceFile2.mp4", buildPhase: .resources, resourceTags: ["tag2", "tag3"]),
|
|
TargetSource(path: "A/sourceFile.swift", buildPhase: .sources, resourceTags: ["tag1", "tag2"]),
|
|
]
|
|
)
|
|
|
|
let project = Project(basePath: directoryPath,
|
|
name: "Test",
|
|
targets: [target])
|
|
|
|
let pbxProj = try project.generatePbxProj()
|
|
|
|
let resourceFileReference = try unwrap(pbxProj.getFileReference(
|
|
paths: ["A", "resourceFile.mp4"],
|
|
names: ["A", "resourceFile.mp4"]
|
|
))
|
|
|
|
let resourceFileReference2 = try unwrap(pbxProj.getFileReference(
|
|
paths: ["A", "resourceFile2.mp4"],
|
|
names: ["A", "resourceFile2.mp4"]
|
|
))
|
|
|
|
let sourceFileReference = try unwrap(pbxProj.getFileReference(
|
|
paths: ["A", "sourceFile.swift"],
|
|
names: ["A", "sourceFile.swift"]
|
|
))
|
|
|
|
try pbxProj.expectFile(paths: ["A", "resourceFile.mp4"], buildPhase: .resources)
|
|
try pbxProj.expectFile(paths: ["A", "resourceFile2.mp4"], buildPhase: .resources)
|
|
try pbxProj.expectFile(paths: ["A", "sourceFile.swift"], buildPhase: .sources)
|
|
|
|
let resourceBuildFile = try unwrap(pbxProj.buildFiles.first(where: { $0.file == resourceFileReference }))
|
|
let resourceBuildFile2 = try unwrap(pbxProj.buildFiles.first(where: { $0.file == resourceFileReference2 }))
|
|
let sourceBuildFile = try unwrap(pbxProj.buildFiles.first(where: { $0.file == sourceFileReference }))
|
|
|
|
if (resourceBuildFile.settings! as NSDictionary) != (["ASSET_TAGS": ["tag1", "tag2"]] as NSDictionary) {
|
|
throw failure("File does not contain tag1 and tag2 ASSET_TAGS")
|
|
}
|
|
|
|
if (resourceBuildFile2.settings! as NSDictionary) != (["ASSET_TAGS": ["tag2", "tag3"]] as NSDictionary) {
|
|
throw failure("File does not contain tag2 and tag3 ASSET_TAGS")
|
|
}
|
|
|
|
if sourceBuildFile.settings != nil {
|
|
throw failure("File that buildPhase is source contain settings")
|
|
}
|
|
|
|
if !pbxProj.rootObject!.attributes.keys.contains("knownAssetTags") {
|
|
throw failure("PBXProject does not contain knownAssetTags")
|
|
}
|
|
|
|
try expect(pbxProj.rootObject!.attributes["knownAssetTags"] as? [String]) == ["tag1", "tag2", "tag3"]
|
|
}
|
|
|
|
$0.it("Detects all locales present in a String Catalog") {
|
|
/// This is a catalog with gaps:
|
|
/// - String "foo" is translated into English (en) and Spanish (es)
|
|
/// - String "bar" is translated into English (en) and Italian (it)
|
|
///
|
|
/// It is aimed at representing real world scenarios where translators have not finished translating all strings into their respective languages.
|
|
/// The expectation in this kind of cases is that `includedLocales` returns all locales found at least once in the catalog.
|
|
/// In this example, `includedLocales` is expected to be a set only containing "en", "es" and "it".
|
|
let stringCatalogContent = """
|
|
{
|
|
"sourceLanguage" : "en",
|
|
"strings" : {
|
|
"foo" : {
|
|
"comment" : "Sample string in an asset catalog",
|
|
"extractionState" : "manual",
|
|
"localizations" : {
|
|
"en" : {
|
|
"stringUnit" : {
|
|
"state" : "translated",
|
|
"value" : "Foo English"
|
|
}
|
|
},
|
|
"es" : {
|
|
"stringUnit" : {
|
|
"state" : "translated",
|
|
"value" : "Foo Spanish"
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"bar" : {
|
|
"comment" : "Another sample string in an asset catalog",
|
|
"extractionState" : "manual",
|
|
"localizations" : {
|
|
"en" : {
|
|
"stringUnit" : {
|
|
"state" : "translated",
|
|
"value" : "Bar English"
|
|
}
|
|
},
|
|
"it" : {
|
|
"stringUnit" : {
|
|
"state" : "translated",
|
|
"value" : "Bar Italian"
|
|
}
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"version" : "1.0"
|
|
}
|
|
"""
|
|
|
|
let testStringCatalogRelativePath = Path("Localizable.xcstrings")
|
|
let testStringCatalogPath = try createFile(at: testStringCatalogRelativePath, content: stringCatalogContent)
|
|
|
|
guard let stringCatalog = StringCatalog(from: testStringCatalogPath) else {
|
|
throw failure("Failed decoding string catalog from \(testStringCatalogPath)")
|
|
}
|
|
|
|
try expect(stringCatalog.includedLocales.sorted(by: { $0 < $1 })) == ["en", "es", "it"]
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
extension PBXProj {
|
|
|
|
/// expect a file within groups of the paths, using optional different names
|
|
func expectFile(paths: [String], names: [String]? = nil, buildPhase: BuildPhaseSpec? = 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)"
|
|
}
|
|
error += "\n\(self.printGroups())"
|
|
throw failure(error, file: file, line: line)
|
|
}
|
|
|
|
if let buildPhase = buildPhase {
|
|
let buildFile = buildFiles
|
|
.first(where: { $0.file === fileReference })
|
|
let actualBuildPhase = buildFile
|
|
.flatMap { buildFile in buildPhases.first { $0.files?.contains(buildFile) ?? false } }?.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) -> PBXFileReference? {
|
|
guard let mainGroup = projects.first?.mainGroup else { return nil }
|
|
|
|
return getFileReference(group: mainGroup, paths: paths, names: names)
|
|
}
|
|
|
|
private func getFileReference(group: PBXGroup, paths: [String], names: [String]) -> 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: [PBXFileReference] = group.children.compactMap { $0 as? PBXFileReference }
|
|
return fileReferences.first { ($0.path == nil || $0.path == path) && $0.nameOrPath == name }
|
|
} else {
|
|
let groups = group.children.compactMap { $0 as? PBXGroup }
|
|
guard let group = groups.first(where: { ($0.path == nil || $0.path == path) && $0.nameOrPath == name }) else {
|
|
return nil
|
|
}
|
|
return getFileReference(group: group, paths: restOfPath, names: restOfName)
|
|
}
|
|
}
|
|
}
|