blob: fc6fde65e19b41de8ca315d9c7d2ffffedcc1e48 [file] [log] [blame]
// Copyright 2016 The Tulsi Authors. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
import XCTest
@testable import TulsiGenerator
class PBXObjectsTests: XCTestCase {
enum ExpectedStructure {
case fileReference(String)
case fileReferenceWithName(String, path: String)
case group(String, contents: [ExpectedStructure])
case groupWithName(String, path: String, contents: [ExpectedStructure])
case variantGroup(String, contents: [ExpectedStructure])
}
var project: PBXProject! = nil
override func setUp() {
super.setUp()
project = PBXProject(name: "TestProject")
}
// MARK: - Tests
func testProjectCreateGroupsAndFileReferencesForPaths() {
let paths = [
"root",
"test/file",
"deeply/nested/files/1",
"deeply/nested/files/2",
"/empty/component",
]
let expectedStructure: [ExpectedStructure] = [
.fileReference("root"),
.group(
"test",
contents: [
.fileReference("test/file"),
]),
.group(
"deeply",
contents: [
.group(
"nested",
contents: [
.group(
"files",
contents: [
.fileReference("deeply/nested/files/1"),
.fileReference("deeply/nested/files/2"),
]),
]),
]),
.group(
"/",
contents: [
.group(
"empty",
contents: [
.fileReference("/empty/component"),
]),
]),
]
project.getOrCreateGroupsAndFileReferencesForPaths(paths)
assertProjectStructure(expectedStructure, forGroup: project.mainGroup)
}
func testProjectCreateGroupsAndFileReferencesForNoPathsIsNoOp() {
let mainGroup: PBXGroup = project.mainGroup
XCTAssertEqual(mainGroup.children.count, 0)
project.getOrCreateGroupsAndFileReferencesForPaths([])
XCTAssertEqual(mainGroup.children.count, 0)
}
func testProjectCreateGroupsAndFileReferencesForPathsMultiplePaths() {
let paths1 = [
"1",
"unique/1",
"overlapping/file",
]
let paths2 = [
"2",
"unique/2",
"overlapping/file",
"overlapping/2",
]
let expectedStructure: [ExpectedStructure] = [
.fileReference("1"),
.fileReference("2"),
.group(
"unique",
contents: [
.fileReference("unique/1"),
.fileReference("unique/2"),
]),
.group(
"overlapping",
contents: [
.fileReference("overlapping/file"),
.fileReference("overlapping/2"),
]),
]
project.getOrCreateGroupsAndFileReferencesForPaths(paths1)
project.getOrCreateGroupsAndFileReferencesForPaths(paths2)
assertProjectStructure(expectedStructure, forGroup: project.mainGroup)
}
func testProjectCreateGroupsAndFileReferencesForBundlePath() {
let paths = [
"test",
"bundle.xcassets/test_content",
"subdir/test.app/file_inside",
"subdir/test2.app/dir_inside/file_inside",
]
let expectedStructure: [ExpectedStructure] = [
.fileReference("test"),
.fileReference("bundle.xcassets"),
.group(
"subdir",
contents: [
.fileReference("subdir/test.app"),
.fileReference("subdir/test2.app"),
]),
]
project.getOrCreateGroupsAndFileReferencesForPaths(paths)
assertProjectStructure(expectedStructure, forGroup: project.mainGroup)
}
func testSourceRelativePathGeneration() {
let paths = [
"1",
"test/2",
"deeply/nested/files/3",
"deeply/nested/files/4",
]
let expectedSourceRelativePaths = [
"1": "1",
"test/2": "test/2",
"deeply/nested/files/3": "deeply/nested/files/3",
"deeply/nested/files/4": "deeply/nested/files/4",
]
project.getOrCreateGroupsAndFileReferencesForPaths(paths)
for fileRef in project.mainGroup.allSources {
let sourceRelativePath = fileRef.sourceRootRelativePath
XCTAssertEqual(sourceRelativePath, expectedSourceRelativePaths[fileRef.path!])
}
}
func testPBXReferenceFileExtension() {
let filenameToExt: [String: String?] = [
"test.file": "file",
"test": nil,
"test.something.ext": "ext",
"/someplace/test.something.ext": "ext",
]
for (filename, ext) in filenameToExt {
let f = PBXFileReference(name: "filename", path: filename, sourceTree: .Group, parent: nil)
XCTAssertEqual(f.fileExtension, ext)
let g = PBXGroup(name: "filename", path: filename, sourceTree: .Group, parent: nil)
XCTAssertEqual(g.fileExtension, ext)
}
}
func testPBXReferenceUTI() {
let fileExtensionsToTest = [
"a",
"dylib",
"swift",
"xib",
]
for ext in fileExtensionsToTest {
let filename = "filename.\(ext)"
let f = PBXFileReference(name: filename, path: filename, sourceTree: .Group, parent: nil)
let expectedUTI = FileExtensionToUTI[ext]
XCTAssertEqual(f.uti, expectedUTI, "UTI mismatch for extension '\(ext)'")
let g = PBXGroup(name: filename, path: filename, sourceTree: .Group, parent: nil)
XCTAssertEqual(g.uti, expectedUTI, "UTI mismatch for extension '\(ext)'")
}
let bundleExtensionsToTest = [
"app",
"bundle",
"xcassets",
"xcstickers",
]
for ext in bundleExtensionsToTest {
let filename = "filename.\(ext)"
let f = PBXFileReference(name: filename, path: filename, sourceTree: .Group, parent: nil)
let expectedUTI = DirExtensionToUTI[ext]
XCTAssertEqual(f.uti, expectedUTI, "UTI mismatch for extension '\(ext)'")
let g = PBXGroup(name: filename, path: filename, sourceTree: .Group, parent: nil)
XCTAssertEqual(g.uti, expectedUTI, "UTI mismatch for extension '\(ext)'")
}
}
func testExternalReferencePathMigration() {
let mainGroup = project.mainGroup
let movedDir = "fancyExternalDir"
let paths = [
"dir/file",
"external/project/README.md",
"external/project/src/file.ext",
]
let expectedStructure: [ExpectedStructure] = [
.group(
"dir",
contents: [
.fileReference("dir/file"),
]),
.groupWithName(
"@project", path: movedDir,
contents: [
.fileReference("README.md"),
.group(
"src",
contents: [
.fileReference("src/file.ext"),
]),
]),
]
project.getOrCreateGroupsAndFileReferencesForPaths(paths)
guard let extGroup = mainGroup.childGroupsByName["external"] else {
XCTAssert(false, "Unable to find external group for mainGroup \(mainGroup)")
return
}
for child in extGroup.children {
guard let group = child as? PBXGroup else {
XCTAssert(false, "Expected child of external group \(extGroup) to be a group, not \(child)")
continue
}
let newChild = mainGroup.getOrCreateChildGroupByName(
"@\(child.name)",
path: movedDir,
sourceTree: .Absolute)
newChild.migrateChildrenOfGroup(group)
}
mainGroup.removeChild(extGroup)
assertProjectStructure(expectedStructure, forGroup: project.mainGroup)
}
func testVariantGroupHandling() {
let paths = [
"Base.lproj/Localizable.strings",
"en.lproj/Localizable.strings",
]
let expectedStructure: [ExpectedStructure] = [
.variantGroup(
"Localizable.strings",
contents: [
.fileReferenceWithName("Base", path: "Base.lproj/Localizable.strings"),
.fileReferenceWithName("en", path: "en.lproj/Localizable.strings"),
]),
]
project.getOrCreateGroupsAndFileReferencesForPaths(paths)
assertProjectStructure(expectedStructure, forGroup: project.mainGroup)
}
// MARK: - Helper methods
func assertProjectStructure(
_ expectedStructure: [ExpectedStructure],
forGroup group: PBXGroup,
line: UInt = #line
) {
XCTAssertEqual(
group.children.count,
expectedStructure.count,
"Mismatch in child count for group '\(group.name)'",
line: line)
for element in expectedStructure {
switch element {
case .fileReference(let name):
assertGroup(group, containsSourceTree: .Group, path: name, line: line)
case .fileReferenceWithName(let name, let path):
assertGroup(group, containsSourceTree: .Group, path: path, name: name, line: line)
case .group(let name, let grandChildren):
let childGroup = assertGroup(group, containsGroupWithName: name, line: line)
assertProjectStructure(grandChildren, forGroup: childGroup, line: line)
case .groupWithName(let name, let path, let grandChildren):
let childGroup = assertGroup(group, containsGroupWithName: name, path: path, line: line)
assertProjectStructure(grandChildren, forGroup: childGroup, line: line)
case .variantGroup(let name, let grandChildren):
let childVariantGroup = assertGroup(group, containsVariantGroupWithName: name, line: line)
assertProjectStructure(grandChildren, forGroup: childVariantGroup, line: line)
}
}
}
@discardableResult
func assertGroup(
_ group: PBXGroup,
containsSourceTree sourceTree: SourceTree,
path: String,
name: String? = nil,
line: UInt = #line
) -> PBXFileReference {
let sourceTreePath = SourceTreePath(sourceTree: sourceTree, path: path)
let fileRef = group.fileReferencesBySourceTreePath[sourceTreePath]
XCTAssertNotNil(
fileRef,
"Failed to find expected PBXFileReference '\(path)' in group '\(group.name)",
line: line)
if let name = name {
XCTAssertEqual(name, fileRef!.name)
}
return fileRef!
}
func assertGroup(
_ group: PBXGroup,
containsGroupWithName name: String,
path: String? = nil,
line: UInt = #line
) -> PBXGroup {
let child = group.childGroupsByName[name]
XCTAssertNotNil(
child,
"Failed to find child group '\(name)' in group '\(group.name)'",
line: line)
if let path = path {
XCTAssertNotNil(child!.path, "Expected child \(child!) to have a non-nil path")
XCTAssertEqual(child!.path, path, "Child path \(child!.path!) != expected path \(path)")
} else {
XCTAssertNil(child!.path, "Expected child \(child!) to have a nil path")
}
return child!
}
func assertGroup(
_ group: PBXGroup,
containsVariantGroupWithName name: String,
line: UInt = #line
) -> PBXGroup {
let child = group.childVariantGroupsByName[name]
XCTAssertNotNil(
child,
"Failed to find child variant group '\(name)' in group '\(group.name)'",
line: line)
return child!
}
}