Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 23 additions & 1 deletion .github/workflows/xcodeproj.yml
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,13 @@ jobs:
- run: mise use swift@6.0.2
- name: Build
run: swift build --configuration release
build-ios:
name: Build (iOS)
runs-on: macos-latest
steps:
- uses: actions/checkout@v6
- name: Build for iOS Simulator
run: xcodebuild build -scheme XcodeProj -destination 'generic/platform=iOS Simulator'
test:
name: Test (macOS / Xcode)
runs-on: macos-latest
Expand All @@ -41,7 +48,6 @@ jobs:
- uses: jdx/mise-action@v2
- name: Run tests
run: mise run test

test-linux:
name: Test (Linux)
runs-on: ubuntu-latest
Expand All @@ -55,6 +61,22 @@ jobs:
git config --global init.defaultBranch main
- name: Test
run: swift test
test-ios:
name: Test (iOS / Xcode)
runs-on: macos-latest
steps:
- uses: actions/checkout@v3
- name: Select iOS Simulator
run: |
simulator_id="$(xcrun simctl list devices available | awk -F '[()]' '/iPhone/ { print $2; exit }')"
if [ -z "$simulator_id" ]; then
echo "No available iPhone simulator found"
xcrun simctl list devices available
exit 1
fi
echo "IOS_SIMULATOR_ID=$simulator_id" >> "$GITHUB_ENV"
- name: Run tests on iOS Simulator
run: xcodebuild test -scheme XcodeProj -destination "platform=iOS Simulator,id=$IOS_SIMULATOR_ID"

lint:
name: Lint
Expand Down
2 changes: 1 addition & 1 deletion Sources/XcodeProj/Extensions/Path+Extras.swift
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import PathKit
// MARK: - Path extras.

func systemGlob(_ pattern: UnsafePointer<CChar>!, _ flags: Int32, _ errfunc: (@convention(c) (UnsafePointer<CChar>?, Int32) -> Int32)!, _ vector_ptr: UnsafeMutablePointer<glob_t>!) -> Int32 {
#if os(macOS)
#if os(macOS) || os(iOS)
return Darwin.glob(pattern, flags, errfunc, vector_ptr)
#else
return Glibc.glob(pattern, flags, errfunc, vector_ptr)
Expand Down
2 changes: 1 addition & 1 deletion Sources/XcodeProj/Extensions/String+md5.swift
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ extension String {
return self
}
#if canImport(CryptoKit)
if #available(OSX 10.15, *) {
if #available(OSX 10.15, iOS 13.0, *) {
return Insecure.MD5.hash(data: data)
.withUnsafeBytes { Array($0) }.hexString
} else {
Expand Down
34 changes: 19 additions & 15 deletions Tests/XcodeProjTests/Extensions/XCTestCase+Shell.swift
Original file line number Diff line number Diff line change
Expand Up @@ -3,23 +3,27 @@ import Foundation
/// Returns the output of running `executable` with `args`. Throws an error if the process exits indicating failure.
@discardableResult
func checkedOutput(_ executable: String, _ args: [String]) throws -> String? {
let process = Process()
let output = Pipe()
#if os(iOS)
return nil
#else
let process = Process()
let output = Pipe()

if executable.contains("/") {
process.launchPath = executable
} else {
process.launchPath = try checkedOutput("/usr/bin/which", [executable])?.trimmingCharacters(in: .newlines)
}
if executable.contains("/") {
process.launchPath = executable
} else {
process.launchPath = try checkedOutput("/usr/bin/which", [executable])?.trimmingCharacters(in: .newlines)
}

process.arguments = args
process.standardOutput = output
process.launch()
process.waitUntilExit()
process.arguments = args
process.standardOutput = output
process.launch()
process.waitUntilExit()

guard process.terminationStatus == 0 else {
throw NSError(domain: NSPOSIXErrorDomain, code: Int(process.terminationStatus))
}
guard process.terminationStatus == 0 else {
throw NSError(domain: NSPOSIXErrorDomain, code: Int(process.terminationStatus))
}

return String(data: output.fileHandleForReading.readDataToEndOfFile(), encoding: .utf8)
return String(data: output.fileHandleForReading.readDataToEndOfFile(), encoding: .utf8)
#endif
}
60 changes: 32 additions & 28 deletions Tests/XcodeProjTests/Scheme/XCSchemeManagementTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -45,38 +45,42 @@ final class XCSchemeManagementTests: XCTestCase {
}

func test_write_produces_no_diff() throws {
let tmpDir = try Path.uniqueTemporary()
defer {
try? tmpDir.delete()
}
#if os(iOS)
throw XCTSkip("'Process' API is unavailable on iOS platform")
#else
let tmpDir = try Path.uniqueTemporary()
defer {
try? tmpDir.delete()
}

try tmpDir.chdir {
// Write
let plistPath = tmpDir + "xcschememanagement.plist"
let subject = XCSchemeManagement(
schemeUserState: [
.init(name: "Test 0.xcscheme", shared: true, orderHint: 0, isShown: true),
.init(name: "Test 1.xcscheme", shared: true, orderHint: 1, isShown: true),
.init(name: "Test 2.xcscheme", shared: true, orderHint: 2, isShown: false),
.init(name: "Test 3.xcscheme", shared: true, orderHint: 3, isShown: true),
],
suppressBuildableAutocreation: [
"E525238B16245A900012E2BA": .init(primary: true),
]
)
try subject.write(path: plistPath, override: true)
try tmpDir.chdir {
// Write
let plistPath = tmpDir + "xcschememanagement.plist"
let subject = XCSchemeManagement(
schemeUserState: [
.init(name: "Test 0.xcscheme", shared: true, orderHint: 0, isShown: true),
.init(name: "Test 1.xcscheme", shared: true, orderHint: 1, isShown: true),
.init(name: "Test 2.xcscheme", shared: true, orderHint: 2, isShown: false),
.init(name: "Test 3.xcscheme", shared: true, orderHint: 3, isShown: true),
],
suppressBuildableAutocreation: [
"E525238B16245A900012E2BA": .init(primary: true),
]
)
try subject.write(path: plistPath, override: true)

// Create a commit
try checkedOutput("git", ["init"])
try checkedOutput("git", ["add", "."])
try checkedOutput("git", ["commit", "-m", "test"])
// Create a commit
try checkedOutput("git", ["init"])
try checkedOutput("git", ["add", "."])
try checkedOutput("git", ["commit", "-m", "test"])

// Write again
try subject.write(path: plistPath, override: true)
// Write again
try subject.write(path: plistPath, override: true)

let got = try checkedOutput("git", ["status"])
XCTAssertTrue(got?.contains("nothing to commit") ?? false)
}
let got = try checkedOutput("git", ["status"])
XCTAssertTrue(got?.contains("nothing to commit") ?? false)
}
#endif
}

private var xcschememanagementPath: Path {
Expand Down
44 changes: 24 additions & 20 deletions Tests/XcodeProjTests/Tests/testWrite.swift
Original file line number Diff line number Diff line change
Expand Up @@ -45,28 +45,32 @@ func testReadWriteProducesNoDiff(file _: StaticString = #file,
from path: Path,
initModel: (Path) throws -> some Writable) throws
{
let tmpDir = try Path.uniqueTemporary()
defer {
try? tmpDir.delete()
}
#if os(iOS)
throw XCTSkip("'Process' API is unavailable on iOS platform")
#else
let tmpDir = try Path.uniqueTemporary()
defer {
try? tmpDir.delete()
}

let fileName = path.lastComponent
let tmpPath = tmpDir + fileName
try path.copy(tmpPath)
let fileName = path.lastComponent
let tmpPath = tmpDir + fileName
try path.copy(tmpPath)

try tmpDir.chdir {
// Create a commit
try checkedOutput("git", ["init"])
try checkedOutput("git", ["add", "."])
try checkedOutput("git", [
"-c", "user.email=test@example.com", "-c", "user.name=Test User",
"commit", "-m", "test",
])
try tmpDir.chdir {
// Create a commit
try checkedOutput("git", ["init"])
try checkedOutput("git", ["add", "."])
try checkedOutput("git", [
"-c", "user.email=test@example.com", "-c", "user.name=Test User",
"commit", "-m", "test",
])

let object = try initModel(tmpPath)
try object.write(path: tmpPath, override: true)
let object = try initModel(tmpPath)
try object.write(path: tmpPath, override: true)

let diff = try XCTUnwrap(checkedOutput("git", ["diff"]))
XCTAssertEqual(diff, "")
}
let diff = try XCTUnwrap(checkedOutput("git", ["diff"]))
XCTAssertEqual(diff, "")
}
#endif
}