Skip to content

Commit

Permalink
Fix error thrown on adding framework reference
Browse files Browse the repository at this point in the history
- Replace check `isFile` with `exists` for a filePath in `addFile`
  method defined in PBXProjObjects+Helpers.swift
- Add checks for supported files based on PBXFileReference's
  `fileTypeHash`
- Add some more test cases

Misc:
- Add XCTAssertThrowsSpecificError assert to check if specific error was
  thrown
- Move fixture files to Files directory
  • Loading branch information
Alexey Fayzullov committed Feb 12, 2018
1 parent abedbdc commit e7a718e
Show file tree
Hide file tree
Showing 8 changed files with 123 additions and 27 deletions.
File renamed without changes.
Empty file.
Empty file added Fixtures/Files/newfile.swift
Empty file.
Empty file added Fixtures/Files/unsupported.xyz
Empty file.
21 changes: 14 additions & 7 deletions Sources/xcproj/PBXProjObjects+Helpers.swift
Original file line number Diff line number Diff line change
Expand Up @@ -99,10 +99,14 @@ public extension PBXProj.Objects {
sourceTree: PBXSourceTree = .group,
sourceRoot: Path) throws -> ObjectReference<PBXFileReference> {

guard filePath.isFile else {
throw XCodeProjEditingError.notAFile(path: filePath)
guard filePath.exists else {
throw XCodeProjEditingError.fileNotExists(path: filePath)
}

guard let fileType = PBXFileReference.fileType(path: filePath) else {
throw XCodeProjEditingError.unsupportedFileType(path: filePath)
}

guard let groupReference = groups.first(where: { $0.value == toGroup })?.key else {
throw XCodeProjEditingError.groupNotFound(group: toGroup)
}
Expand Down Expand Up @@ -133,8 +137,8 @@ public extension PBXProj.Objects {
let fileReference = PBXFileReference(
sourceTree: sourceTree,
name: filePath.lastComponent,
explicitFileType: PBXFileReference.fileType(path: filePath),
lastKnownFileType: PBXFileReference.fileType(path: filePath),
explicitFileType: fileType,
lastKnownFileType: fileType,
path: path?.string
)
let reference = generateReference(fileReference, filePath.string)
Expand Down Expand Up @@ -200,13 +204,16 @@ public struct GroupAddingOptions: OptionSet {
}

public enum XCodeProjEditingError: Error, CustomStringConvertible {
case notAFile(path: Path)
case unsupportedFileType(path: Path)
case fileNotExists(path: Path)
case groupNotFound(group: PBXGroup)

public var description: String {
switch self {
case .notAFile(let path):
return "\(path) is not a file path"
case .unsupportedFileType(let path):
return "\(path) is not supported."
case .fileNotExists(let path):
return "\(path) doesn't exist."
case .groupNotFound(let group):
return "Group not found in project: \(group)"
}
Expand Down
4 changes: 4 additions & 0 deletions Tests/xcprojTests/Fixtures.swift
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,10 @@ import Foundation
import PathKit
import xcproj

func fixtureFilesPath() -> Path {
return fixturesPath() + "Files"
}

func fixturesPath() -> Path {
return Path(#file).parent().parent().parent() + Path("Fixtures")
}
Expand Down
13 changes: 13 additions & 0 deletions Tests/xcprojTests/XCTestCase+Assertions.swift
Original file line number Diff line number Diff line change
Expand Up @@ -9,4 +9,17 @@ extension XCTestCase {
}
return unwrappedObj
}

typealias EquatableError = Error & Equatable
func XCTAssertThrowsSpecificError<T, E: EquatableError>(_ expression: @autoclosure () throws -> T, _ error: E, _ message: @autoclosure () -> String = "", file: StaticString = #file, line: UInt = #line) {
XCTAssertThrowsError(expression, message, file: file, line: line) { actualError in
let message = "Expected \(error) got \(actualError)"

guard let actualCastedError = actualError as? E else {
XCTFail(message, file: file, line: line)
return
}
XCTAssertEqual(actualCastedError, error, message, file: file, line: line)
}
}
}
112 changes: 92 additions & 20 deletions Tests/xcprojTests/XcodeProjIntegrationSpec.swift
Original file line number Diff line number Diff line change
Expand Up @@ -138,41 +138,93 @@ final class XcodeProjIntegrationSpec: XCTestCase {
XCTAssertEqual(project.pbxproj.objects.groups[newGroups[1].reference], newGroups[1].object)
}

func test_add_new_file() throws {
func test_add_new_source_file() throws {
let proj = projectiOS()!.pbxproj
let filePath = fixturesPath() + "newfile.swift"
let filePath = fixtureFilesPath() + "newfile.swift"
let iOSGroup = proj.objects.group(named: "iOS", inGroup: proj.rootGroup)!
let file = try proj.objects.addFile(at: filePath, toGroup: iOSGroup.object, sourceRoot: fixturesPath() + "iOS")

let expectedFile = PBXFileReference(sourceTree: .group,
name: "newfile.swift",
explicitFileType: "sourcecode.swift",
lastKnownFileType: "sourcecode.swift",
path: "../../Files/newfile.swift")

XCTAssertEqual(proj.objects.fileReferences[file.reference], file.object)
XCTAssertEqual(file.object.name, "newfile.swift")
XCTAssertEqual(file.object.sourceTree, PBXSourceTree.group)
XCTAssertEqual(file.object.path, "../../newfile.swift")
XCTAssertEqual(file.object, expectedFile)
XCTAssertNotNil(iOSGroup.object.children.index(of: file.reference))

let existingFile = try proj.objects.addFile(at: filePath, toGroup: proj.rootGroup, sourceRoot: fixturesPath() + "iOS")

XCTAssertTrue(file == existingFile)
}

func test_add_not_a_file() throws {
func test_add_new_dynamic_framework() throws {
let proj = projectiOS()!.pbxproj
do {
_ = try proj.objects.addFile(at: fixturesPath() + "iOS/iOS", toGroup: proj.rootGroup, sourceRoot: fixturesPath() + "iOS")
XCTFail("Adding not file path should throw error")
} catch {}

do {
_ = try proj.objects.addFile(at: fixturesPath() + "iOS/iOS/newfile.swift", toGroup: proj.rootGroup, sourceRoot: fixturesPath() + "iOS")
XCTFail("Adding not existing file should throw error")
} catch {}
let filePath = fixtureFilesPath() + "dummy.framework"

let iOSGroup = proj.objects.group(named: "iOS", inGroup: proj.rootGroup)!
let file = try proj.objects.addFile(at: filePath,
toGroup: iOSGroup.object,
sourceRoot: fixtureiOSSourcePath())

let expectedFile = PBXFileReference(sourceTree: .group,
name: "dummy.framework",
explicitFileType: "wrapper.framework",
lastKnownFileType: "wrapper.framework",
path: "../../Files/dummy.framework")


XCTAssertEqual(proj.objects.fileReferences[file.reference], file.object)
XCTAssertEqual(file.object, expectedFile)
XCTAssertNotNil(iOSGroup.object.children.index(of: file.reference))
}

func test_add_existing_file_returns_existing_object() throws {
let proj = projectiOS()!.pbxproj
let filePath = fixtureFilesPath() + "newfile.swift"
let iOSGroup = proj.objects.group(named: "iOS", inGroup: proj.rootGroup)!.object

let file = try proj.objects.addFile(at: filePath, toGroup: iOSGroup, sourceRoot: fixtureiOSSourcePath())
let existingFile = try proj.objects.addFile(at: filePath, toGroup: proj.rootGroup, sourceRoot: fixtureiOSSourcePath())
XCTAssertTrue(file == existingFile)
}

func test_add_unsupported_file_throws() {
let proj = projectiOS()!.pbxproj
let filePath = fixtureFilesPath() + "unsupported.xyz"
XCTAssertThrowsSpecificError(
try proj.objects.addFile(at: filePath, toGroup: proj.rootGroup, sourceRoot: fixtureiOSSourcePath()),
XCodeProjEditingError.unsupportedFileType(path: filePath),
"Adding file reference to unsupported file type should throw an error"
)
}

func test_add_directory_throws() {
let proj = projectiOS()!.pbxproj
let filePath = fixtureFilesPath() + "directory"
XCTAssertThrowsSpecificError(
try proj.objects.addFile(at: filePath, toGroup: proj.rootGroup, sourceRoot: fixtureiOSSourcePath()),
XCodeProjEditingError.unsupportedFileType(path: filePath),
"Adding a directory as a file reference should throw an error"
)
}

func test_add_nonexisting_file_throws() {
let proj = projectiOS()!.pbxproj
let filePath = fixtureFilesPath() + "nonexisting.swift"
XCTAssertThrowsSpecificError(
try proj.objects.addFile(at: filePath, toGroup: proj.rootGroup, sourceRoot: fixtureiOSSourcePath()),
XCodeProjEditingError.fileNotExists(path: filePath),
"Adding a reference to non existing file should throw an error"
)
}

func test_add_new_build_file() throws {
let proj = projectiOS()!.pbxproj
let target = proj.objects.targets(named: "iOS").first!
let sourcesBuildPhase = proj.objects.sourcesBuildPhase(target: target.object)!
let filePath = fixturesPath() + "newfile.swift"
let filePath = fixtureFilesPath() + "newfile.swift"
let file = try proj.objects.addFile(at: filePath, toGroup: proj.rootGroup, sourceRoot: fixturesPath() + "iOS")

let buildFile = proj.objects.addBuildFile(toTarget: target.object, reference: file.reference)!
Expand All @@ -193,19 +245,19 @@ final class XcodeProjIntegrationSpec: XCTestCase {
let rootGroupPath = proj.objects.fullPath(fileElement: proj.rootGroup, reference: proj.rootProject!.mainGroup, sourceRoot: sourceRoot)
XCTAssertEqual(rootGroupPath, sourceRoot)

let filePath = fixturesPath() + "newfile.swift"
let filePath = fixtureFilesPath() + "newfile.swift"
var file = try proj.objects.addFile(at: filePath, toGroup: iOSGroup, sourceTree: .group, sourceRoot: sourceRoot)
var fullFilePath = proj.objects.fullPath(fileElement: file.object, reference: file.reference, sourceRoot: sourceRoot)

XCTAssertEqual(file.object.path, "../../newfile.swift")
XCTAssertEqual(file.object.path, "../../Files/newfile.swift")
XCTAssertEqual(fullFilePath, filePath)

proj = projectiOS()!.pbxproj
iOSGroup = proj.objects.group(named: "iOS", inGroup: proj.rootGroup)!.object
file = try proj.objects.addFile(at: filePath, toGroup: iOSGroup, sourceTree: .sourceRoot, sourceRoot: sourceRoot)
fullFilePath = proj.objects.fullPath(fileElement: file.object, reference: file.reference, sourceRoot: sourceRoot)

XCTAssertEqual(file.object.path, "../newfile.swift")
XCTAssertEqual(file.object.path, "../Files/newfile.swift")
XCTAssertEqual(fullFilePath, filePath)

proj = projectiOS()!.pbxproj
Expand Down Expand Up @@ -255,6 +307,10 @@ final class XcodeProjIntegrationSpec: XCTestCase {
return fixturesPath() + "iOS/Project.xcodeproj"
}

private func fixtureiOSSourcePath() -> Path {
return fixturesPath() + "iOS"
}

private func projectiOS() -> XcodeProj? {
return try? XcodeProj(path: fixtureiOSProjectPath())
}
Expand All @@ -263,3 +319,19 @@ final class XcodeProjIntegrationSpec: XCTestCase {
return try XcodeProj(path: fixtureWithoutWorkspaceProjectPath())
}
}

// This could be code generated (e.g. using sourcery)
extension XCodeProjEditingError: Equatable {
static public func == (lhs: XCodeProjEditingError, rhs: XCodeProjEditingError) -> Bool {
switch (lhs, rhs) {
case (.unsupportedFileType(let path1), .unsupportedFileType(let path2)):
return path1 == path2
case (.fileNotExists(let path1), .fileNotExists(let path2)):
return path1 == path2
case (.groupNotFound(let group1), .groupNotFound(let group2)):
return group1 == group2
default:
return false
}
}
}

0 comments on commit e7a718e

Please sign in to comment.