-
Notifications
You must be signed in to change notification settings - Fork 1.1k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Extended Bundle layout testing #2083
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -36,26 +36,110 @@ internal func xdgTestHelperURL() -> URL { | |
|
||
|
||
class BundlePlayground { | ||
enum ExecutableType: CaseIterable { | ||
case library | ||
case executable | ||
|
||
var pathExtension: String { | ||
switch self { | ||
case .library: | ||
#if os(macOS) || os(iOS) || os(watchOS) || os(tvOS) | ||
return "dylib" | ||
#elseif os(Windows) | ||
return "dll" | ||
#else | ||
return "so" | ||
#endif | ||
case .executable: | ||
#if os(Windows) | ||
return "exe" | ||
#else | ||
return "" | ||
#endif | ||
} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I wonder if we want to have some constants for this in There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yea :\ probably a SPI picking up that information from CF (like: |
||
} | ||
|
||
var flatPathExtension: String { | ||
#if os(Windows) | ||
return self.pathExtension | ||
#else | ||
return "" | ||
#endif | ||
} | ||
|
||
var fhsPrefix: String { | ||
switch self { | ||
case .executable: | ||
return "bin" | ||
case .library: | ||
return "lib" | ||
} | ||
} | ||
|
||
var nonFlatFilePrefix: String { | ||
switch self { | ||
case .executable: | ||
return "" | ||
case .library: | ||
return "lib" | ||
} | ||
} | ||
} | ||
|
||
enum Layout { | ||
case flat | ||
case fhsInstalled | ||
case fhsFreestanding | ||
case flat(ExecutableType) | ||
case fhs(ExecutableType) | ||
case freestanding(ExecutableType) | ||
|
||
static var allApplicable: [Layout] { | ||
let layouts: [Layout] = [ .flat, .fhsInstalled, .fhsFreestanding ] | ||
|
||
#if NS_FOUNDATION_ALLOWS_TESTABLE_IMPORT | ||
if Bundle._supportsFHSStyle { | ||
return layouts | ||
} else { | ||
return layouts.filter { !$0.isFHS } | ||
let layouts: [Layout] = [ | ||
.flat(.library), | ||
.flat(.executable), | ||
.fhs(.library), | ||
.fhs(.executable), | ||
.freestanding(.library), | ||
.freestanding(.executable), | ||
] | ||
|
||
return layouts.filter { $0.isSupported } | ||
} | ||
|
||
var isFreestanding: Bool { | ||
switch self { | ||
case .freestanding(_): | ||
return true | ||
default: | ||
return false | ||
} | ||
#else | ||
return layouts.filter { !$0.isFHS } | ||
#endif | ||
} | ||
|
||
var isFHS: Bool { | ||
return self == .fhsInstalled || self == .fhsFreestanding | ||
switch self { | ||
case .fhs(_): | ||
return true | ||
default: | ||
return false | ||
} | ||
} | ||
|
||
var isFlat: Bool { | ||
switch self { | ||
case .flat(_): | ||
return true | ||
default: | ||
return false | ||
} | ||
} | ||
|
||
var isSupported: Bool { | ||
switch self { | ||
case .flat(_): | ||
return true | ||
case .freestanding(_): | ||
return Bundle._supportsFreestandingBundles | ||
case .fhs(_): | ||
return Bundle._supportsFHSBundles | ||
} | ||
} | ||
} | ||
|
||
|
@@ -68,6 +152,7 @@ class BundlePlayground { | |
let layout: Layout | ||
|
||
private(set) var bundlePath: String! | ||
private(set) var mainExecutableURL: URL! | ||
private var playgroundPath: String? | ||
|
||
init?(bundleName: String, | ||
|
@@ -96,8 +181,8 @@ class BundlePlayground { | |
|
||
let temporaryDirectory = FileManager.default.temporaryDirectory.appendingPathComponent("TestFoundation_Playground_" + UUID().uuidString) | ||
|
||
switch (layout) { | ||
case .flat: | ||
switch layout { | ||
case .flat(let executableType): | ||
do { | ||
try FileManager.default.createDirectory(atPath: temporaryDirectory.path, withIntermediateDirectories: false, attributes: nil) | ||
|
||
|
@@ -106,10 +191,18 @@ class BundlePlayground { | |
try FileManager.default.createDirectory(atPath: bundleURL.path, withIntermediateDirectories: false, attributes: nil) | ||
|
||
// Make a main and an auxiliary executable: | ||
guard FileManager.default.createFile(atPath: bundleURL.appendingPathComponent(bundleName).path, contents: nil) else { | ||
self.mainExecutableURL = bundleURL | ||
.appendingPathComponent(bundleName) | ||
.appendingPathExtension(executableType.flatPathExtension) | ||
|
||
guard FileManager.default.createFile(atPath: mainExecutableURL.path, contents: nil) else { | ||
return false | ||
} | ||
guard FileManager.default.createFile(atPath: bundleURL.appendingPathComponent(auxiliaryExecutableName).path, contents: nil) else { | ||
|
||
let auxiliaryExecutableURL = bundleURL | ||
.appendingPathComponent(auxiliaryExecutableName) | ||
.appendingPathExtension(executableType.flatPathExtension) | ||
guard FileManager.default.createFile(atPath: auxiliaryExecutableURL.path, contents: nil) else { | ||
return false | ||
} | ||
|
||
|
@@ -135,7 +228,7 @@ class BundlePlayground { | |
return false | ||
} | ||
|
||
case .fhsInstalled: | ||
case .fhs(let executableType): | ||
do { | ||
|
||
// Create a FHS /usr/local-style hierarchy: | ||
|
@@ -144,17 +237,18 @@ class BundlePlayground { | |
try FileManager.default.createDirectory(atPath: temporaryDirectory.appendingPathComponent("lib").path, withIntermediateDirectories: false, attributes: nil) | ||
|
||
// Make a main and an auxiliary executable: | ||
#if os(macOS) || os(iOS) || os(watchOS) || os(tvOS) | ||
let pathExtension = "dylib" | ||
#else | ||
let pathExtension = "so" | ||
#endif | ||
|
||
guard FileManager.default.createFile(atPath: temporaryDirectory.appendingPathComponent("lib").appendingPathComponent("lib\(bundleName).\(pathExtension)").path, contents: nil) else { return false } | ||
self.mainExecutableURL = temporaryDirectory | ||
.appendingPathComponent(executableType.fhsPrefix) | ||
.appendingPathComponent(executableType.nonFlatFilePrefix + bundleName) | ||
.appendingPathExtension(executableType.pathExtension) | ||
guard FileManager.default.createFile(atPath: mainExecutableURL.path, contents: nil) else { return false } | ||
|
||
let executablesDirectory = temporaryDirectory.appendingPathComponent("libexec").appendingPathComponent("\(bundleName).executables") | ||
try FileManager.default.createDirectory(atPath: executablesDirectory.path, withIntermediateDirectories: true, attributes: nil) | ||
guard FileManager.default.createFile(atPath: executablesDirectory.appendingPathComponent(auxiliaryExecutableName).path, contents: nil) else { return false } | ||
let auxiliaryExecutableURL = executablesDirectory | ||
.appendingPathComponent(executableType.nonFlatFilePrefix + auxiliaryExecutableName) | ||
.appendingPathExtension(executableType.pathExtension) | ||
guard FileManager.default.createFile(atPath: auxiliaryExecutableURL.path, contents: nil) else { return false } | ||
|
||
// Make a .resources directory in …/share: | ||
let resourcesDirectory = temporaryDirectory.appendingPathComponent("share").appendingPathComponent("\(bundleName).resources") | ||
|
@@ -178,21 +272,27 @@ class BundlePlayground { | |
return false | ||
} | ||
|
||
case .fhsFreestanding: | ||
case .freestanding(let executableType): | ||
do { | ||
let bundleName = URL(string:self.bundleName)!.deletingPathExtension().path | ||
|
||
try FileManager.default.createDirectory(atPath: temporaryDirectory.path, withIntermediateDirectories: false, attributes: nil) | ||
|
||
// Make a main executable: | ||
guard FileManager.default.createFile(atPath: temporaryDirectory.appendingPathComponent(bundleName).path, contents: nil) else { return false } | ||
self.mainExecutableURL = temporaryDirectory | ||
.appendingPathComponent(executableType.nonFlatFilePrefix + bundleName) | ||
.appendingPathExtension(executableType.pathExtension) | ||
guard FileManager.default.createFile(atPath: mainExecutableURL.path, contents: nil) else { return false } | ||
|
||
// Make a .resources directory: | ||
let resourcesDirectory = temporaryDirectory.appendingPathComponent("\(bundleName).resources") | ||
try FileManager.default.createDirectory(atPath: resourcesDirectory.path, withIntermediateDirectories: false, attributes: nil) | ||
|
||
// Make an auxiliary executable: | ||
guard FileManager.default.createFile(atPath: resourcesDirectory.appendingPathComponent(auxiliaryExecutableName).path, contents: nil) else { return false } | ||
let auxiliaryExecutableURL = resourcesDirectory | ||
.appendingPathComponent(executableType.nonFlatFilePrefix + auxiliaryExecutableName) | ||
.appendingPathExtension(executableType.pathExtension) | ||
guard FileManager.default.createFile(atPath: auxiliaryExecutableURL.path, contents: nil) else { return false } | ||
|
||
// Put some resources in the bundle | ||
for resourceName in resourceFilenames { | ||
|
@@ -244,6 +344,7 @@ class TestBundle : XCTestCase { | |
("test_bundlePreflight", test_bundlePreflight), | ||
("test_bundleFindExecutable", test_bundleFindExecutable), | ||
("test_bundleFindAuxiliaryExecutables", test_bundleFindAuxiliaryExecutables), | ||
("test_bundleReverseBundleLookup", test_bundleReverseBundleLookup), | ||
("test_mainBundleExecutableURL", test_mainBundleExecutableURL), | ||
] | ||
} | ||
|
@@ -430,6 +531,26 @@ class TestBundle : XCTestCase { | |
XCTAssertNil(bundle.url(forAuxiliaryExecutable: "does_not_exist_at_all")) | ||
} | ||
} | ||
|
||
func test_bundleReverseBundleLookup() { | ||
_withEachPlaygroundLayout { (playground) in | ||
#if !os(Windows) | ||
if playground.layout.isFreestanding { | ||
// TODO: Freestanding bundles reverse lookup pending to be implemented on non-Windows platforms. | ||
return | ||
} | ||
#endif | ||
|
||
if playground.layout.isFHS { | ||
// TODO: FHS bundles reverse lookup pending to be implemented on all platforms. | ||
return | ||
} | ||
|
||
let bundle = Bundle(_executableURL: playground.mainExecutableURL) | ||
XCTAssertNotNil(bundle) | ||
XCTAssertEqual(bundle?.bundlePath, playground.bundlePath) | ||
} | ||
} | ||
|
||
func test_mainBundleExecutableURL() { | ||
#if !DARWIN_COMPATIBILITY_TESTS // _CFProcessPath() is unavailable on native Foundation | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@pvieito @millenomi Should this method be public? also
_supportsFHSBundles
and_supportsFreestandingBundle
above? If its just for testing shouldn't they beinternal
?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@spevans If we mark it
internal
we would have to guard the tests withNS_FOUNDATION_ALLOWS_TESTABLE_IMPORT
, which means that they would not be executed on the Linux CI (and I would like them to be executed on the Linux CI, as I can only test them locally on macOS).There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I thought
NS_FOUNDATION_ALLOWS_TESTABLE_IMPORT
works for Linux CI because it uses thedebug-foundation
flag. @millenomi Is that not the case?