blob: 1b8cb7d2bb5863cb4cd7e17ffc1a430930a37f0d [file] [log] [blame]
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2014 - 2016 Apple Inc. and the Swift project authors
// Licensed under Apache License v2.0 with Runtime Library Exception
//
// See http://swift.org/LICENSE.txt for license information
// See http://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
//
#if DEPLOYMENT_RUNTIME_OBJC || os(Linux)
import Foundation
import XCTest
#else
import SwiftFoundation
import SwiftXCTest
#endif
import CoreFoundation
internal func testBundle() -> Bundle {
#if DARWIN_COMPATIBILITY_TESTS
for bundle in Bundle.allBundles {
if let bundleId = bundle.bundleIdentifier, bundleId == "org.swift.DarwinCompatibilityTests", bundle.resourcePath != nil {
return bundle
}
}
fatalError("Cant find test bundle")
#else
return Bundle.main
#endif
}
class BundlePlayground {
enum Layout {
case flat
case fhsInstalled
case fhsFreestanding
static var allApplicable: [Layout] {
let layouts: [Layout] = [ .flat, .fhsInstalled, .fhsFreestanding ]
#if DEPLOYMENT_RUNTIME_OBJC
let supportsFHS = false
#else
let supportsFHS = _CFBundleSupportsFHSBundles()
#endif
if supportsFHS {
return layouts
} else {
return layouts.filter { !$0.isFHS }
}
}
var isFHS: Bool {
return self == .fhsInstalled || self == .fhsFreestanding
}
}
let bundleName: String
let bundleExtension: String
let resourceFilenames: [String]
let resourceSubdirectory: String
let subdirectoryResourcesFilenames: [String]
let auxiliaryExecutableName: String
let layout: Layout
private(set) var bundlePath: String!
private var playgroundPath: String?
init?(bundleName: String,
bundleExtension: String,
resourceFilenames: [String],
resourceSubdirectory: String,
subdirectoryResourcesFilenames: [String],
auxiliaryExecutableName: String,
layout: Layout) {
self.bundleName = bundleName
self.bundleExtension = bundleExtension
self.resourceFilenames = resourceFilenames
self.resourceSubdirectory = resourceSubdirectory
self.subdirectoryResourcesFilenames = subdirectoryResourcesFilenames
self.auxiliaryExecutableName = auxiliaryExecutableName
self.layout = layout
if !_create() {
destroy()
return nil
}
}
private func _create() -> Bool {
// Make sure the directory is uniquely named
let temporaryDirectory = FileManager.default.temporaryDirectory.appendingPathComponent("TestFoundation_Playground_" + UUID().uuidString)
switch (layout) {
case .flat:
do {
try FileManager.default.createDirectory(atPath: temporaryDirectory.path, withIntermediateDirectories: false, attributes: nil)
// Make a flat bundle in the playground
let bundleURL = temporaryDirectory.appendingPathComponent(bundleName).appendingPathExtension(self.bundleExtension)
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 {
return false
}
guard FileManager.default.createFile(atPath: bundleURL.appendingPathComponent(auxiliaryExecutableName).path, contents: nil) else {
return false
}
// Put some resources in the bundle
for resourceName in resourceFilenames {
guard FileManager.default.createFile(atPath: bundleURL.appendingPathComponent(resourceName).path, contents: nil, attributes: nil) else {
return false
}
}
// Add a resource into a subdirectory
let subdirectoryURL = bundleURL.appendingPathComponent(resourceSubdirectory)
try FileManager.default.createDirectory(atPath: subdirectoryURL.path, withIntermediateDirectories: false, attributes: nil)
for resourceName in subdirectoryResourcesFilenames {
guard FileManager.default.createFile(atPath: subdirectoryURL.appendingPathComponent(resourceName).path, contents: nil, attributes: nil) else {
return false
}
}
self.bundlePath = bundleURL.path
} catch _ {
return false
}
case .fhsInstalled:
do {
// Create a FHS /usr/local-style hierarchy:
try FileManager.default.createDirectory(atPath: temporaryDirectory.path, withIntermediateDirectories: false, attributes: nil)
try FileManager.default.createDirectory(atPath: temporaryDirectory.appendingPathComponent("share").path, withIntermediateDirectories: false, attributes: nil)
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 }
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 }
// Make a .resources directory in …/share:
let resourcesDirectory = temporaryDirectory.appendingPathComponent("share").appendingPathComponent("\(bundleName).resources")
try FileManager.default.createDirectory(atPath: resourcesDirectory.path, withIntermediateDirectories: false, attributes: nil)
// Put some resources in the bundle
for resourceName in resourceFilenames {
guard FileManager.default.createFile(atPath: resourcesDirectory.appendingPathComponent(resourceName).path, contents: nil, attributes: nil) else { return false }
}
// Add a resource into a subdirectory
let subdirectoryURL = resourcesDirectory.appendingPathComponent(resourceSubdirectory)
try FileManager.default.createDirectory(atPath: subdirectoryURL.path, withIntermediateDirectories: false, attributes: nil)
for resourceName in subdirectoryResourcesFilenames {
guard FileManager.default.createFile(atPath: subdirectoryURL.appendingPathComponent(resourceName).path, contents: nil, attributes: nil) else { return false }
}
self.bundlePath = resourcesDirectory.path
} catch _ {
return false
}
case .fhsFreestanding:
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 }
// 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 }
// Put some resources in the bundle
for resourceName in resourceFilenames {
guard FileManager.default.createFile(atPath: resourcesDirectory.appendingPathComponent(resourceName).path, contents: nil, attributes: nil) else { return false }
}
// Add a resource into a subdirectory
let subdirectoryURL = resourcesDirectory.appendingPathComponent(resourceSubdirectory)
try FileManager.default.createDirectory(atPath: subdirectoryURL.path, withIntermediateDirectories: false, attributes: nil)
for resourceName in subdirectoryResourcesFilenames {
guard FileManager.default.createFile(atPath: subdirectoryURL.appendingPathComponent(resourceName).path, contents: nil, attributes: nil) else { return false }
}
self.bundlePath = resourcesDirectory.path
} catch _ {
return false
}
}
self.playgroundPath = temporaryDirectory.path
return true
}
func destroy() {
guard let path = self.playgroundPath else { return }
self.playgroundPath = nil
do {
try FileManager.default.removeItem(atPath: path)
} catch _ {
// ¯\_(ツ)_/¯ We did what we could.
}
}
deinit {
assert(playgroundPath == nil, "All playgrounds should have .destroy() invoked on them before they go out of scope.")
}
}
class TestBundle : XCTestCase {
static var allTests: [(String, (TestBundle) -> () throws -> Void)] {
return [
("test_paths", test_paths),
("test_resources", test_resources),
("test_infoPlist", test_infoPlist),
("test_localizations", test_localizations),
("test_URLsForResourcesWithExtension", test_URLsForResourcesWithExtension),
("test_bundleLoad", test_bundleLoad),
("test_bundleLoadWithError", test_bundleLoadWithError),
("test_bundleWithInvalidPath", test_bundleWithInvalidPath),
("test_bundlePreflight", test_bundlePreflight),
("test_bundleFindExecutable", test_bundleFindExecutable),
("test_bundleFindAuxiliaryExecutables", test_bundleFindAuxiliaryExecutables),
]
}
func test_paths() {
let bundle = testBundle()
// bundlePath
XCTAssert(!bundle.bundlePath.isEmpty)
XCTAssertEqual(bundle.bundleURL.path, bundle.bundlePath)
let path = bundle.bundlePath
// etc
#if os(OSX)
XCTAssertEqual("\(path)/Contents/Resources", bundle.resourcePath)
#if DARWIN_COMPATIBILITY_TESTS
XCTAssertEqual("\(path)/Contents/MacOS/DarwinCompatibilityTests", bundle.executablePath)
#else
XCTAssertEqual("\(path)/Contents/MacOS/TestFoundation", bundle.executablePath)
#endif
XCTAssertEqual("\(path)/Contents/Frameworks", bundle.privateFrameworksPath)
XCTAssertEqual("\(path)/Contents/SharedFrameworks", bundle.sharedFrameworksPath)
XCTAssertEqual("\(path)/Contents/SharedSupport", bundle.sharedSupportPath)
#endif
XCTAssertNil(bundle.path(forAuxiliaryExecutable: "no_such_file"))
#if !DARWIN_COMPATIBILITY_TESTS
XCTAssertNil(bundle.appStoreReceiptURL)
#endif
}
func test_resources() {
let bundle = testBundle()
// bad resources
XCTAssertNil(bundle.url(forResource: nil, withExtension: nil, subdirectory: nil))
XCTAssertNil(bundle.url(forResource: "", withExtension: "", subdirectory: nil))
XCTAssertNil(bundle.url(forResource: "no_such_file", withExtension: nil, subdirectory: nil))
// test file
let testPlist = bundle.url(forResource: "Test", withExtension: "plist")
XCTAssertNotNil(testPlist)
XCTAssertEqual("Test.plist", testPlist!.lastPathComponent)
XCTAssert(FileManager.default.fileExists(atPath: testPlist!.path))
// aliases, paths
XCTAssertEqual(testPlist!.path, bundle.url(forResource: "Test", withExtension: "plist", subdirectory: nil)!.path)
XCTAssertEqual(testPlist!.path, bundle.path(forResource: "Test", ofType: "plist"))
XCTAssertEqual(testPlist!.path, bundle.path(forResource: "Test", ofType: "plist", inDirectory: nil))
}
func test_infoPlist() {
let bundle = testBundle()
// bundleIdentifier
#if DARWIN_COMPATIBILITY_TESTS
XCTAssertEqual("org.swift.DarwinCompatibilityTests", bundle.bundleIdentifier)
#else
XCTAssertEqual("org.swift.TestFoundation", bundle.bundleIdentifier)
#endif
// infoDictionary
let info = bundle.infoDictionary
XCTAssertNotNil(info)
#if DARWIN_COMPATIBILITY_TESTS
XCTAssert("DarwinCompatibilityTests" == info!["CFBundleName"] as! String)
XCTAssert("org.swift.DarwinCompatibilityTests" == info!["CFBundleIdentifier"] as! String)
#else
XCTAssert("TestFoundation" == info!["CFBundleName"] as! String)
XCTAssert("org.swift.TestFoundation" == info!["CFBundleIdentifier"] as! String)
#endif
// localizedInfoDictionary
XCTAssertNil(bundle.localizedInfoDictionary) // FIXME: Add a localized Info.plist for testing
}
func test_localizations() {
let bundle = testBundle()
XCTAssertEqual(["en"], bundle.localizations)
XCTAssertEqual(["en"], bundle.preferredLocalizations)
XCTAssertEqual(["en"], Bundle.preferredLocalizations(from: ["en", "pl", "es"]))
}
private let _bundleName = "MyBundle"
private let _bundleExtension = "bundle"
private let _bundleResourceNames = ["hello.world", "goodbye.world", "swift.org"]
private let _subDirectory = "Sources"
private let _main = "main"
private let _type = "swift"
private let _auxiliaryExecutable = "auxiliaryExecutable"
private func _setupPlayground(layout: BundlePlayground.Layout) -> BundlePlayground? {
return BundlePlayground(bundleName: _bundleName,
bundleExtension: _bundleExtension,
resourceFilenames: _bundleResourceNames,
resourceSubdirectory: _subDirectory,
subdirectoryResourcesFilenames: [ "\(_main).\(_type)" ],
auxiliaryExecutableName: _auxiliaryExecutable,
layout: layout)
}
private func _withEachPlaygroundLayout(execute: (BundlePlayground) throws -> Void) rethrows {
for layout in BundlePlayground.Layout.allApplicable {
if let playground = _setupPlayground(layout: layout) {
try execute(playground)
playground.destroy()
}
}
}
private func _cleanupPlayground(_ location: String) {
do {
try FileManager.default.removeItem(atPath: location)
} catch _ {
// Oh well
}
}
func test_URLsForResourcesWithExtension() {
_withEachPlaygroundLayout { (playground) in
let bundle = Bundle(path: playground.bundlePath)!
XCTAssertNotNil(bundle)
let worldResources = bundle.urls(forResourcesWithExtension: "world", subdirectory: nil)
XCTAssertNotNil(worldResources)
XCTAssertEqual(worldResources?.count, 2)
let path = bundle.path(forResource: _main, ofType: _type, inDirectory: _subDirectory)
XCTAssertNotNil(path)
}
}
func test_bundleLoad() {
let bundle = testBundle()
let _ = bundle.load()
XCTAssertTrue(bundle.isLoaded)
}
func test_bundleLoadWithError() {
let bundleValid = testBundle()
// Test valid load using loadAndReturnError
do {
try bundleValid.loadAndReturnError()
}
catch{
XCTFail("should not fail to load")
}
// Executable cannot be located
try! _withEachPlaygroundLayout { (playground) in
let bundle = Bundle(path: playground.bundlePath)
XCTAssertThrowsError(try bundle!.loadAndReturnError())
}
}
func test_bundleWithInvalidPath() {
let bundleInvalid = Bundle(path: NSTemporaryDirectory() + "test.playground")
XCTAssertNil(bundleInvalid)
}
func test_bundlePreflight() {
XCTAssertNoThrow(try testBundle().preflight())
try! _withEachPlaygroundLayout { (playground) in
let bundle = Bundle(path: playground.bundlePath)!
// Must throw as the main executable is a dummy empty file.
XCTAssertThrowsError(try bundle.preflight())
}
}
func test_bundleFindExecutable() {
XCTAssertNotNil(testBundle().executableURL)
_withEachPlaygroundLayout { (playground) in
let bundle = Bundle(path: playground.bundlePath)!
XCTAssertNotNil(bundle.executableURL)
}
}
func test_bundleFindAuxiliaryExecutables() {
_withEachPlaygroundLayout { (playground) in
let bundle = Bundle(path: playground.bundlePath)!
XCTAssertNotNil(bundle.url(forAuxiliaryExecutable: _auxiliaryExecutable))
XCTAssertNil(bundle.url(forAuxiliaryExecutable: "does_not_exist_at_all"))
}
}
}