diff --git a/Fixtures/SynchronizedRootGroups/SynchronizedRootGroups.xcodeproj/project.pbxproj b/Fixtures/SynchronizedRootGroups/SynchronizedRootGroups.xcodeproj/project.pbxproj index a8db250fe..8c0a8d312 100644 --- a/Fixtures/SynchronizedRootGroups/SynchronizedRootGroups.xcodeproj/project.pbxproj +++ b/Fixtures/SynchronizedRootGroups/SynchronizedRootGroups.xcodeproj/project.pbxproj @@ -3,7 +3,7 @@ archiveVersion = 1; classes = { }; - objectVersion = 73; + objectVersion = 77; objects = { /* Begin PBXCopyFilesBuildPhase section */ @@ -23,7 +23,7 @@ /* End PBXFileReference section */ /* Begin PBXFileSystemSynchronizedBuildFileExceptionSet section */ - 6CF05BA32C53F97F00EF267F /* PBXFileSystemSynchronizedBuildFileExceptionSet */ = { + 6CF05BA32C53F97F00EF267F /* Exceptions for "SynchronizedRootGroups" folder in "SynchronizedRootGroups" target */ = { isa = PBXFileSystemSynchronizedBuildFileExceptionSet; membershipExceptions = ( Exception/Exception.swift, @@ -33,7 +33,7 @@ /* End PBXFileSystemSynchronizedBuildFileExceptionSet section */ /* Begin PBXFileSystemSynchronizedGroupBuildPhaseMembershipExceptionSet section */ - F841A9D12D63B00A00059ED6 /* PBXFileSystemSynchronizedGroupBuildPhaseMembershipExceptionSet */ = { + F841A9D12D63B00A00059ED6 /* Exceptions for "SynchronizedRootGroups" folder in "Copy Files" phase from "SynchronizedRootGroups" target */ = { isa = PBXFileSystemSynchronizedGroupBuildPhaseMembershipExceptionSet; attributesByRelativePath = { XPCService.xpc = ( @@ -48,7 +48,15 @@ /* End PBXFileSystemSynchronizedGroupBuildPhaseMembershipExceptionSet section */ /* Begin PBXFileSystemSynchronizedRootGroup section */ - 6CF05B9D2C53F64800EF267F /* SynchronizedRootGroups */ = {isa = PBXFileSystemSynchronizedRootGroup; exceptions = (6CF05BA32C53F97F00EF267F /* PBXFileSystemSynchronizedBuildFileExceptionSet */, F841A9D12D63B00A00059ED6 /* PBXFileSystemSynchronizedGroupBuildPhaseMembershipExceptionSet */, ); explicitFileTypes = {}; explicitFolders = (); path = SynchronizedRootGroups; sourceTree = ""; }; + 6CF05B9D2C53F64800EF267F /* SynchronizedRootGroups */ = { + isa = PBXFileSystemSynchronizedRootGroup; + exceptions = ( + 6CF05BA32C53F97F00EF267F /* Exceptions for "SynchronizedRootGroups" folder in "SynchronizedRootGroups" target */, + F841A9D12D63B00A00059ED6 /* Exceptions for "SynchronizedRootGroups" folder in "Copy Files" phase from "SynchronizedRootGroups" target */, + ); + path = SynchronizedRootGroups; + sourceTree = ""; + }; /* End PBXFileSystemSynchronizedRootGroup section */ /* Begin PBXFrameworksBuildPhase section */ @@ -138,7 +146,7 @@ Base, ); mainGroup = 6CF05B822C53F5F200EF267F; - preferredProjectObjectVersion = 60; + preferredProjectObjectVersion = 77; productRefGroup = 6CF05B8D2C53F5F200EF267F /* Products */; projectDirPath = ""; projectRoot = ""; diff --git a/Fixtures/SynchronizedRootGroups/SynchronizedRootGroups/XPCService.xpc/Info.plist b/Fixtures/SynchronizedRootGroups/SynchronizedRootGroups/XPCService.xpc/Info.plist new file mode 100644 index 000000000..adceb8c9b --- /dev/null +++ b/Fixtures/SynchronizedRootGroups/SynchronizedRootGroups/XPCService.xpc/Info.plist @@ -0,0 +1,19 @@ + + + + + CFBundleIdentifier + com.example.XPCService + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + XPCService + CFBundlePackageType + XPC! + XPCService + + ServiceType + Application + + + diff --git a/Fixtures/iOS/Project.xcodeproj/xcshareddata/xcschemes/iOS.xcscheme b/Fixtures/iOS/Project.xcodeproj/xcshareddata/xcschemes/iOS.xcscheme index 5ec735651..7cc8ee447 100644 --- a/Fixtures/iOS/Project.xcodeproj/xcshareddata/xcschemes/iOS.xcscheme +++ b/Fixtures/iOS/Project.xcodeproj/xcshareddata/xcschemes/iOS.xcscheme @@ -81,12 +81,6 @@ - - - - - - - - - - + + + + + + + + + + + allowLocationSimulation = "YES"> @@ -180,10 +179,6 @@ ReferencedContainer = "container:Project.xcodeproj"> - - + + diff --git a/Fixtures/iOS/Project.xcodeproj/xcuserdata/username1.xcuserdatad/xcschemes/iOS-debug.xcscheme b/Fixtures/iOS/Project.xcodeproj/xcuserdata/username1.xcuserdatad/xcschemes/iOS-debug.xcscheme index 7cb38d44f..04d801fc8 100644 --- a/Fixtures/iOS/Project.xcodeproj/xcuserdata/username1.xcuserdatad/xcschemes/iOS-debug.xcscheme +++ b/Fixtures/iOS/Project.xcodeproj/xcuserdata/username1.xcuserdatad/xcschemes/iOS-debug.xcscheme @@ -27,8 +27,6 @@ selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB" selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB" shouldUseLaunchSchemeArgsEnv = "YES"> - - - - - - + skipped = "NO"> - - PBXTarget? { + guard let projectObjects = try? objects() else { return nil } + let allTargets: [PBXTarget] = Array(projectObjects.nativeTargets.values) + + Array(projectObjects.legacyTargets.values) + + Array(projectObjects.aggregateTargets.values) + return allTargets.first { $0.buildPhaseReferences.map(\.value).contains(reference.value) } + } + + /// Human-readable display name, used in descriptive comment strings. + /// + /// - Returns: display name matching Xcode's comment format. + func displayName() -> String? { + if let phase = self as? PBXCopyFilesBuildPhase { + return phase.name ?? "Copy Files" + } else if let phase = self as? PBXShellScriptBuildPhase { + return phase.name ?? "Run Script" + } + return name() + } + /// Build phase name. /// /// - Returns: build phase name. diff --git a/Sources/XcodeProj/Objects/Files/PBXFileElement.swift b/Sources/XcodeProj/Objects/Files/PBXFileElement.swift index 5f3e80dca..6d623dc50 100644 --- a/Sources/XcodeProj/Objects/Files/PBXFileElement.swift +++ b/Sources/XcodeProj/Objects/Files/PBXFileElement.swift @@ -139,6 +139,16 @@ public class PBXFileElement: PBXContainerItem, PlistSerializable { guard let rhs = object as? PBXFileElement else { return false } return isEqual(to: rhs) } + + // This method is needed to recursively set the parent to all elements. + // This allows us to more quickly find the full path to the elements. + func assignParentToChildren() { + guard let group = self as? PBXGroup else { return } + for child in group.children { + child.parent = self + child.assignParentToChildren() + } + } } // MARK: - Helpers @@ -206,14 +216,4 @@ public extension PBXFileElement { .first(where: { $0.name == "Base" }) else { return nil } return baseReference.path } - - // This method is needed to recursively set the parent to all elements. - // This allows us to more quickly find the full path to the elements. - func assignParentToChildren() { - guard let group = self as? PBXGroup else { return } - for child in group.children { - child.parent = self - child.assignParentToChildren() - } - } } diff --git a/Sources/XcodeProj/Objects/Files/PBXFileSystemSynchronizedBuildFileExceptionSet.swift b/Sources/XcodeProj/Objects/Files/PBXFileSystemSynchronizedBuildFileExceptionSet.swift index e007bb879..d1623ded4 100644 --- a/Sources/XcodeProj/Objects/Files/PBXFileSystemSynchronizedBuildFileExceptionSet.swift +++ b/Sources/XcodeProj/Objects/Files/PBXFileSystemSynchronizedBuildFileExceptionSet.swift @@ -96,6 +96,11 @@ public class PBXFileSystemSynchronizedBuildFileExceptionSet: PBXFileSystemSynchr // MARK: - PlistSerializable + override var plistComment: String { + let folder = synchronizedRootGroup?.fileName() ?? "" + return "Exceptions for \"\(folder)\" folder in \"\(target.name)\" target" + } + func plistKeyAndValue(proj _: PBXProj, reference: String) throws -> (key: CommentedString, value: PlistValue) { var dictionary: [CommentedString: PlistValue] = [:] dictionary["isa"] = .string(CommentedString(PBXFileSystemSynchronizedBuildFileExceptionSet.isa)) @@ -124,6 +129,6 @@ public class PBXFileSystemSynchronizedBuildFileExceptionSet: PBXFileSystemSynchr })) } dictionary["target"] = .string(CommentedString(target.reference.value, comment: target.name)) - return (key: CommentedString(reference, comment: "PBXFileSystemSynchronizedBuildFileExceptionSet"), value: .dictionary(dictionary)) + return (key: CommentedString(reference, comment: plistComment), value: .dictionary(dictionary)) } } diff --git a/Sources/XcodeProj/Objects/Files/PBXFileSystemSynchronizedExceptionSet.swift b/Sources/XcodeProj/Objects/Files/PBXFileSystemSynchronizedExceptionSet.swift index 095bd24e9..7b141471a 100644 --- a/Sources/XcodeProj/Objects/Files/PBXFileSystemSynchronizedExceptionSet.swift +++ b/Sources/XcodeProj/Objects/Files/PBXFileSystemSynchronizedExceptionSet.swift @@ -1,4 +1,10 @@ import Foundation /// Common class for exception sets, such as `PBXFileSystemSynchronizedGroupBuildPhaseMembershipExceptionSet` and `PBXFileSystemSynchronizedBuildFileExceptionSet` -public class PBXFileSystemSynchronizedExceptionSet: PBXObject {} +public class PBXFileSystemSynchronizedExceptionSet: PBXObject { + /// The synchronized root group this exception set belongs to. + public internal(set) weak var synchronizedRootGroup: PBXFileSystemSynchronizedRootGroup? + + /// The comment string used when this object is referenced in a plist. + var plistComment: String { type(of: self).isa } +} diff --git a/Sources/XcodeProj/Objects/Files/PBXFileSystemSynchronizedGroupBuildPhaseMembershipExceptionSet.swift b/Sources/XcodeProj/Objects/Files/PBXFileSystemSynchronizedGroupBuildPhaseMembershipExceptionSet.swift index b2326893e..d18671fcb 100644 --- a/Sources/XcodeProj/Objects/Files/PBXFileSystemSynchronizedGroupBuildPhaseMembershipExceptionSet.swift +++ b/Sources/XcodeProj/Objects/Files/PBXFileSystemSynchronizedGroupBuildPhaseMembershipExceptionSet.swift @@ -66,6 +66,14 @@ public class PBXFileSystemSynchronizedGroupBuildPhaseMembershipExceptionSet: PBX // MARK: - PlistSerializable + override var plistComment: String { + guard let group = synchronizedRootGroup else { return type(of: self).isa } + let folder = group.fileName() ?? "" + let phase = buildPhase.displayName() ?? "" + let target = buildPhase.target()?.name ?? "" + return "Exceptions for \"\(folder)\" folder in \"\(phase)\" phase from \"\(target)\" target" + } + func plistKeyAndValue(proj _: PBXProj, reference: String) throws -> (key: CommentedString, value: PlistValue) { var dictionary: [CommentedString: PlistValue] = [:] dictionary["isa"] = .string(CommentedString(type(of: self).isa)) @@ -78,6 +86,6 @@ public class PBXFileSystemSynchronizedGroupBuildPhaseMembershipExceptionSet: PBX })) } dictionary["buildPhase"] = .string(CommentedString(buildPhase.reference.value, comment: buildPhase.name() ?? "CopyFiles")) - return (key: CommentedString(reference, comment: "PBXFileSystemSynchronizedGroupBuildPhaseMembershipExceptionSet"), value: .dictionary(dictionary)) + return (key: CommentedString(reference, comment: plistComment), value: .dictionary(dictionary)) } } diff --git a/Sources/XcodeProj/Objects/Files/PBXFileSystemSynchronizedRootGroup.swift b/Sources/XcodeProj/Objects/Files/PBXFileSystemSynchronizedRootGroup.swift index 43e090eff..64139c078 100644 --- a/Sources/XcodeProj/Objects/Files/PBXFileSystemSynchronizedRootGroup.swift +++ b/Sources/XcodeProj/Objects/Files/PBXFileSystemSynchronizedRootGroup.swift @@ -83,24 +83,30 @@ public class PBXFileSystemSynchronizedRootGroup: PBXFileElement { // MARK: - PlistSerializable - override var multiline: Bool { (exceptions?.count ?? 0) < 2 } + override func assignParentToChildren() { + super.assignParentToChildren() + exceptions?.forEach { $0.synchronizedRootGroup = self } + } override func plistKeyAndValue(proj: PBXProj, reference: String) throws -> (key: CommentedString, value: PlistValue) { var dictionary: [CommentedString: PlistValue] = try super.plistKeyAndValue(proj: proj, reference: reference).value.dictionary ?? [:] dictionary["isa"] = .string(CommentedString(type(of: self).isa)) if let exceptions, !exceptions.isEmpty { dictionary["exceptions"] = .array(exceptions.map { exception in - .string(CommentedString(exception.reference.value, comment: type(of: exception).isa)) + .string(CommentedString(exception.reference.value, comment: exception.plistComment)) }) } - if let explicitFileTypes { + if let explicitFileTypes, !explicitFileTypes.isEmpty { dictionary["explicitFileTypes"] = .dictionary(Dictionary(uniqueKeysWithValues: explicitFileTypes.map { relativePath, fileType in (CommentedString(relativePath), .string(CommentedString(fileType))) })) } - if let explicitFolders { + if let explicitFolders, !explicitFolders.isEmpty { dictionary["explicitFolders"] = .array(explicitFolders.map { .string(CommentedString($0)) }) } + if name == path { + dictionary["name"] = nil + } return (key: CommentedString(reference, comment: name ?? path), value: .dictionary(dictionary)) diff --git a/Sources/XcodeProj/Project/Xcode.swift b/Sources/XcodeProj/Project/Xcode.swift index 44ab18058..cd0b3e51e 100644 --- a/Sources/XcodeProj/Project/Xcode.swift +++ b/Sources/XcodeProj/Project/Xcode.swift @@ -328,6 +328,7 @@ public enum Xcode { "xcsynspec": "text.plist.xcsynspec", "xctarget": "wrapper.pb-target", "xctest": "wrapper.cfbundle", + "xctestplan": "text", "xctxtmacro": "text.plist.xctxtmacro", "xcworkspace": "wrapper.workspace", "xhtml": "text.xml", diff --git a/Sources/XcodeProj/Scheme/XCScheme+BuildAction.swift b/Sources/XcodeProj/Scheme/XCScheme+BuildAction.swift index 8cc9ed15a..00b3e9d3b 100644 --- a/Sources/XcodeProj/Scheme/XCScheme+BuildAction.swift +++ b/Sources/XcodeProj/Scheme/XCScheme+BuildAction.swift @@ -158,8 +158,8 @@ public extension XCScheme { attributes["buildArchitectures"] = buildArchitecturesXMLString } - if let runPostActionsOnFailure { - attributes["runPostActionsOnFailure"] = runPostActionsOnFailure.xmlString + if runPostActionsOnFailure == true { + attributes["runPostActionsOnFailure"] = "YES" } let element = AEXMLElement(name: "BuildAction", diff --git a/Sources/XcodeProj/Scheme/XCScheme+LaunchAction.swift b/Sources/XcodeProj/Scheme/XCScheme+LaunchAction.swift index f484cc9a6..ae629d323 100644 --- a/Sources/XcodeProj/Scheme/XCScheme+LaunchAction.swift +++ b/Sources/XcodeProj/Scheme/XCScheme+LaunchAction.swift @@ -427,6 +427,14 @@ public extension XCScheme { element.addChild(runnable.xmlElement()) } + if let commandlineArguments, !commandlineArguments.arguments.isEmpty { + element.addChild(commandlineArguments.xmlElement()) + } + + if let environmentVariables { + element.addChild(EnvironmentVariable.xmlElement(from: environmentVariables)) + } + if let locationScenarioReference { element.addChild(locationScenarioReference.xmlElement()) } @@ -436,14 +444,6 @@ public extension XCScheme { macro.addChild(macroExpansion.xmlElement()) } - if let commandlineArguments { - element.addChild(commandlineArguments.xmlElement()) - } - - if let environmentVariables { - element.addChild(EnvironmentVariable.xmlElement(from: environmentVariables)) - } - if let language { element.attributes["language"] = language } diff --git a/Sources/XcodeProj/Scheme/XCScheme+TestAction.swift b/Sources/XcodeProj/Scheme/XCScheme+TestAction.swift index 735e699b1..4d014e86d 100644 --- a/Sources/XcodeProj/Scheme/XCScheme+TestAction.swift +++ b/Sources/XcodeProj/Scheme/XCScheme+TestAction.swift @@ -167,8 +167,8 @@ public extension XCScheme { if codeCoverageEnabled { attributes["codeCoverageEnabled"] = codeCoverageEnabled.xmlString } - if let onlyGenerateCoverageForSpecifiedTargets { - attributes["onlyGenerateCoverageForSpecifiedTargets"] = onlyGenerateCoverageForSpecifiedTargets.xmlString + if onlyGenerateCoverageForSpecifiedTargets == true { + attributes["onlyGenerateCoverageForSpecifiedTargets"] = "YES" } if enableAddressSanitizer { attributes["enableAddressSanitizer"] = enableAddressSanitizer.xmlString @@ -204,24 +204,12 @@ public extension XCScheme { let element = AEXMLElement(name: "TestAction", value: nil, attributes: attributes) super.writeXML(parent: element) - if let testPlans { - let testPlansElement = element.addChild(name: "TestPlans") - for testPlan in testPlans { - testPlansElement.addChild(testPlan.xmlElement()) - } - } - if let macroExpansion { let macro = element.addChild(name: "MacroExpansion") macro.addChild(macroExpansion.xmlElement()) } - let testablesElement = element.addChild(name: "Testables") - for testable in testables { - testablesElement.addChild(testable.xmlElement()) - } - - if let commandlineArguments { + if let commandlineArguments, !commandlineArguments.arguments.isEmpty { element.addChild(commandlineArguments.xmlElement()) } @@ -229,6 +217,20 @@ public extension XCScheme { element.addChild(EnvironmentVariable.xmlElement(from: environmentVariables)) } + if let testPlans { + let testPlansElement = element.addChild(name: "TestPlans") + for testPlan in testPlans { + testPlansElement.addChild(testPlan.xmlElement()) + } + } + + if !testables.isEmpty { + let testablesElement = element.addChild(name: "Testables") + for testable in testables { + testablesElement.addChild(testable.xmlElement()) + } + } + if !additionalOptions.isEmpty { let additionalOptionsElement = element.addChild(AEXMLElement(name: "AdditionalOptions")) for additionalOption in additionalOptions { diff --git a/Sources/XcodeProj/Scheme/XCScheme+TestableReference.swift b/Sources/XcodeProj/Scheme/XCScheme+TestableReference.swift index f6b6a4b42..4d1a7dfa2 100644 --- a/Sources/XcodeProj/Scheme/XCScheme+TestableReference.swift +++ b/Sources/XcodeProj/Scheme/XCScheme+TestableReference.swift @@ -43,7 +43,7 @@ public extension XCScheme { parallelization = .swiftTestingOnly } - useTestSelectionWhitelist = element.attributes["useTestSelectionWhitelist"] == "YES" + useTestSelectionWhitelist = element.attributes["useTestSelectionWhitelist"].map { $0 == "YES" } randomExecutionOrdering = element.attributes["testExecutionOrdering"] == "random" buildableReference = try BuildableReference(element: element["BuildableReference"]) @@ -79,8 +79,8 @@ public extension XCScheme { break // SwiftTesting is inferred by the lack of a value } - if let useTestSelectionWhitelist { - attributes["useTestSelectionWhitelist"] = useTestSelectionWhitelist.xmlString + if useTestSelectionWhitelist == true { + attributes["useTestSelectionWhitelist"] = "YES" } attributes["testExecutionOrdering"] = randomExecutionOrdering ? "random" : nil let element = AEXMLElement(name: "TestableReference", diff --git a/Tests/XcodeProjTests/Objects/Files/PBXFileSystemSynchronizedBuildFileExceptionSetTests.swift b/Tests/XcodeProjTests/Objects/Files/PBXFileSystemSynchronizedBuildFileExceptionSetTests.swift index 0ef9fda58..fedb22e0a 100644 --- a/Tests/XcodeProjTests/Objects/Files/PBXFileSystemSynchronizedBuildFileExceptionSetTests.swift +++ b/Tests/XcodeProjTests/Objects/Files/PBXFileSystemSynchronizedBuildFileExceptionSetTests.swift @@ -75,4 +75,18 @@ final class PBXFileSystemSynchronizedBuildFileExceptionSetTests: XCTestCase { XCTAssertNil(plistValue.dictionary?[CommentedString("platformFiltersByRelativePath")]) } + + func test_plistComment_withSynchronizedRootGroup_returnsDescriptiveComment() { + let group = PBXFileSystemSynchronizedRootGroup(sourceTree: .group, path: "Sources", exceptions: [subject]) + XCTAssertEqual(subject.plistComment, "Exceptions for \"Sources\" folder in \"Test\" target") + withExtendedLifetime(group) {} + } + + func test_plistKeyAndValue_keyComment_matchesPlistComment() throws { + let proj = PBXProj() + let group = PBXFileSystemSynchronizedRootGroup(sourceTree: .group, path: "Sources", exceptions: [subject]) + let (key, _) = try subject.plistKeyAndValue(proj: proj, reference: "ref") + XCTAssertEqual(key.comment, subject.plistComment) + withExtendedLifetime(group) {} + } } diff --git a/Tests/XcodeProjTests/Objects/Files/PBXFileSystemSynchronizedRootGroupTests.swift b/Tests/XcodeProjTests/Objects/Files/PBXFileSystemSynchronizedRootGroupTests.swift index 374156104..aae5a178e 100644 --- a/Tests/XcodeProjTests/Objects/Files/PBXFileSystemSynchronizedRootGroupTests.swift +++ b/Tests/XcodeProjTests/Objects/Files/PBXFileSystemSynchronizedRootGroupTests.swift @@ -48,4 +48,46 @@ final class PBXFileSystemSynchronizedRootGroupTests: XCTestCase { explicitFolders: []) XCTAssertEqual(subject, another) } + + // MARK: - plistKeyAndValue + + func test_plistKeyAndValue_explicitFileTypes_serializedOnlyWhenNonEmpty() throws { + let (_, nonEmpty) = try subject.plistKeyAndValue(proj: PBXProj(), reference: "ref") + XCTAssertNotNil(nonEmpty.dictionary?[CommentedString("explicitFileTypes")]) + + subject.explicitFileTypes = [:] + let (_, empty) = try subject.plistKeyAndValue(proj: PBXProj(), reference: "ref") + XCTAssertNil(empty.dictionary?[CommentedString("explicitFileTypes")]) + } + + func test_plistKeyAndValue_explicitFolders_serializedOnlyWhenNonEmpty() throws { + let (_, empty) = try subject.plistKeyAndValue(proj: PBXProj(), reference: "ref") + XCTAssertNil(empty.dictionary?[CommentedString("explicitFolders")]) + + subject.explicitFolders = ["SubFolder"] + let (_, nonEmpty) = try subject.plistKeyAndValue(proj: PBXProj(), reference: "ref") + XCTAssertNotNil(nonEmpty.dictionary?[CommentedString("explicitFolders")]) + } + + func test_plistKeyAndValue_name_serializedOnlyWhenDifferentFromPath() throws { + let (_, same) = try subject.plistKeyAndValue(proj: PBXProj(), reference: "ref") + XCTAssertNil(same.dictionary?[CommentedString("name")]) + + subject.name = "Display Name" + let (_, different) = try subject.plistKeyAndValue(proj: PBXProj(), reference: "ref") + XCTAssertEqual(different.dictionary?[CommentedString("name")], .string(CommentedString("Display Name"))) + } + + func test_plistKeyAndValue_exceptionReference_usesDescriptiveComment() throws { + let (_, value) = try subject.plistKeyAndValue(proj: PBXProj(), reference: "ref") + let exceptionsArray = try XCTUnwrap(value.dictionary?[CommentedString("exceptions")]?.array) + let entry = try XCTUnwrap(exceptionsArray.first?.string) + XCTAssertEqual(entry.comment, "Exceptions for \"synchronized\" folder in \"Test\" target") + } + + // MARK: - assignParentToChildren + + func test_assignParentToChildren_wiresSynchronizedRootGroup() { + XCTAssertIdentical(exception.synchronizedRootGroup, subject) + } } diff --git a/Tests/XcodeProjTests/Objects/Project/PBXProjEncoderTests.swift b/Tests/XcodeProjTests/Objects/Project/PBXProjEncoderTests.swift index af5233ce0..f2a4980a9 100644 --- a/Tests/XcodeProjTests/Objects/Project/PBXProjEncoderTests.swift +++ b/Tests/XcodeProjTests/Objects/Project/PBXProjEncoderTests.swift @@ -294,7 +294,15 @@ let lines = lines(fromFile: encodeProject(settings: settings)) let beginGroup = lines.findLine("/* Begin PBXFileSystemSynchronizedRootGroup section */") - var line = lines.validate(line: "6CF05B9D2C53F64800EF267F /* SynchronizedRootGroups */ = {isa = PBXFileSystemSynchronizedRootGroup; exceptions = (6CF05BA32C53F97F00EF267F /* PBXFileSystemSynchronizedBuildFileExceptionSet */, F841A9D12D63B00A00059ED6 /* PBXFileSystemSynchronizedGroupBuildPhaseMembershipExceptionSet */, ); explicitFileTypes = {}; explicitFolders = (); path = SynchronizedRootGroups; sourceTree = \"\"; };", after: beginGroup) + var line = lines.validate(line: "6CF05B9D2C53F64800EF267F /* SynchronizedRootGroups */ = {", after: beginGroup) + line = lines.validate(line: "isa = PBXFileSystemSynchronizedRootGroup;", after: line) + line = lines.validate(line: "exceptions = (", after: line) + line = lines.validate(line: "6CF05BA32C53F97F00EF267F /* Exceptions for \"SynchronizedRootGroups\" folder in \"SynchronizedRootGroups\" target */,", after: line) + line = lines.validate(line: "F841A9D12D63B00A00059ED6 /* Exceptions for \"SynchronizedRootGroups\" folder in \"Copy Files\" phase from \"SynchronizedRootGroups\" target */,", after: line) + line = lines.validate(line: ");", after: line) + line = lines.validate(line: "path = SynchronizedRootGroups;", after: line) + line = lines.validate(line: "sourceTree = \"\";", after: line) + line = lines.validate(line: "};", after: line) line = lines.validate(line: "/* End PBXFileSystemSynchronizedRootGroup section */", after: line) } @@ -307,7 +315,7 @@ let lines = lines(fromFile: encodeProject(settings: settings)) let beginGroup = lines.findLine("/* Begin PBXFileSystemSynchronizedBuildFileExceptionSet section */") - var line = lines.validate(line: "6CF05BA32C53F97F00EF267F /* PBXFileSystemSynchronizedBuildFileExceptionSet */ = {", after: beginGroup) + var line = lines.validate(line: "6CF05BA32C53F97F00EF267F /* Exceptions for \"SynchronizedRootGroups\" folder in \"SynchronizedRootGroups\" target */ = {", after: beginGroup) line = lines.validate(line: "isa = PBXFileSystemSynchronizedBuildFileExceptionSet;", after: line) line = lines.validate(line: "membershipExceptions = (", after: line) line = lines.validate(line: "Exception/Exception.swift,", after: line) @@ -326,7 +334,7 @@ let lines = lines(fromFile: encodeProject(settings: settings)) let beginGroup = lines.findLine("/* Begin PBXFileSystemSynchronizedGroupBuildPhaseMembershipExceptionSet section */") - var line = lines.validate(line: "F841A9D12D63B00A00059ED6 /* PBXFileSystemSynchronizedGroupBuildPhaseMembershipExceptionSet */ = {", after: beginGroup) + var line = lines.validate(line: "F841A9D12D63B00A00059ED6 /* Exceptions for \"SynchronizedRootGroups\" folder in \"Copy Files\" phase from \"SynchronizedRootGroups\" target */ = {", after: beginGroup) line = lines.validate(line: "isa = PBXFileSystemSynchronizedGroupBuildPhaseMembershipExceptionSet;", after: line) line = lines.validate(line: "attributesByRelativePath = {", after: line) line = lines.validate(line: "XPCService.xpc = (", after: line) diff --git a/Tests/XcodeProjTests/Scheme/XCSchemeTests.swift b/Tests/XcodeProjTests/Scheme/XCSchemeTests.swift index 3e9aa9f5b..4628d64e9 100644 --- a/Tests/XcodeProjTests/Scheme/XCSchemeTests.swift +++ b/Tests/XcodeProjTests/Scheme/XCSchemeTests.swift @@ -470,7 +470,7 @@ final class XCSchemeIntegrationTests: XCTestCase { XCTAssertEqual(scheme.testAction?.testables.first?.skipped, false) XCTAssertEqual(scheme.testAction?.testables.first?.parallelization, .swiftTestingOnly) XCTAssertEqual(scheme.testAction?.testables.first?.randomExecutionOrdering, false) - XCTAssertEqual(scheme.testAction?.testables.first?.useTestSelectionWhitelist, false) + XCTAssertNil(scheme.testAction?.testables.first?.useTestSelectionWhitelist) XCTAssertEqual(scheme.testAction?.testables.first?.buildableReference.buildableIdentifier, "primary") XCTAssertEqual(scheme.testAction?.testables.first?.buildableReference.blueprintIdentifier, "23766C251EAA3484007A9026") XCTAssertEqual(scheme.testAction?.testables.first?.buildableReference.buildableName, "iOSTests.xctest")