199 lines
7.6 KiB
Swift
199 lines
7.6 KiB
Swift
import Foundation
|
|
import MacToolsPluginKit
|
|
import XCTest
|
|
@testable import MacTools
|
|
|
|
@MainActor
|
|
final class PluginPackageStoreTests: XCTestCase {
|
|
private var temporaryRoot: URL!
|
|
private var defaults: UserDefaults!
|
|
private let suiteName = "PluginPackageStoreTests"
|
|
|
|
override func setUpWithError() throws {
|
|
temporaryRoot = FileManager.default.temporaryDirectory
|
|
.appendingPathComponent("PluginPackageStoreTests-\(UUID().uuidString)", isDirectory: true)
|
|
defaults = UserDefaults(suiteName: suiteName)!
|
|
defaults.removePersistentDomain(forName: suiteName)
|
|
}
|
|
|
|
override func tearDownWithError() throws {
|
|
if let temporaryRoot {
|
|
try? FileManager.default.removeItem(at: temporaryRoot)
|
|
}
|
|
defaults.removePersistentDomain(forName: suiteName)
|
|
defaults = nil
|
|
temporaryRoot = nil
|
|
}
|
|
|
|
func testInstallCopiesPackageIntoInstalledDirectoryAndEnablesPlugin() throws {
|
|
let sourceURL = try makePackage(id: "com.example.demo")
|
|
let store = makeStore()
|
|
|
|
let record = try store.installPackage(from: sourceURL)
|
|
|
|
XCTAssertEqual(record.id, "com.example.demo")
|
|
XCTAssertTrue(FileManager.default.fileExists(atPath: record.packageURL.path))
|
|
XCTAssertEqual(store.installedRecords().map(\.id), ["com.example.demo"])
|
|
XCTAssertEqual(store.installedRecords().first?.state, .enabled)
|
|
}
|
|
|
|
func testDisableRemovesPluginFromEnabledRecordsButKeepsFiles() throws {
|
|
let sourceURL = try makePackage(id: "com.example.demo")
|
|
let store = makeStore()
|
|
_ = try store.installPackage(from: sourceURL)
|
|
|
|
store.setEnabled(false, for: "com.example.demo")
|
|
|
|
let record = try XCTUnwrap(store.installedRecords().first)
|
|
XCTAssertEqual(record.state, .disabled)
|
|
XCTAssertTrue(FileManager.default.fileExists(atPath: record.packageURL.path))
|
|
XCTAssertTrue(record.requiresRestartToFullyUnload)
|
|
}
|
|
|
|
func testReenableClearsPendingRestartWarning() throws {
|
|
let sourceURL = try makePackage(id: "com.example.demo")
|
|
let store = makeStore()
|
|
_ = try store.installPackage(from: sourceURL)
|
|
|
|
store.setEnabled(false, for: "com.example.demo")
|
|
store.setEnabled(true, for: "com.example.demo")
|
|
|
|
let record = try XCTUnwrap(store.installedRecords().first)
|
|
XCTAssertEqual(record.state, .enabled)
|
|
XCTAssertFalse(record.requiresRestartToFullyUnload)
|
|
}
|
|
|
|
func testInstallRejectsPreviousPluginKitPackage() throws {
|
|
let sourceURL = try makePackage(
|
|
id: "com.example.demo",
|
|
pluginKitVersion: 1
|
|
)
|
|
let store = makeStore()
|
|
|
|
XCTAssertThrowsError(try store.installPackage(from: sourceURL)) { error in
|
|
XCTAssertEqual(error as? PluginPackageManifestError, .unsupportedPluginKitVersion(1))
|
|
}
|
|
}
|
|
|
|
func testExistingPreviousPluginKitPackageIsMarkedIncompatible() throws {
|
|
let sourceURL = try makePackage(
|
|
id: "com.example.demo",
|
|
pluginKitVersion: 1
|
|
)
|
|
let store = makeStore()
|
|
let installedURL = store.installedDirectory
|
|
.appendingPathComponent("com.example.demo", isDirectory: true)
|
|
.appendingPathExtension("mactoolsplugin")
|
|
try FileManager.default.copyItem(at: sourceURL, to: installedURL)
|
|
|
|
let record = try XCTUnwrap(store.installedRecords().first)
|
|
|
|
XCTAssertEqual(
|
|
record.state,
|
|
.incompatible(
|
|
AppL10n.pluginsFormat(
|
|
"plugin.error.store.installedSDKIncompatibleFormat",
|
|
defaultValue: "插件 SDK 版本不兼容,已安装版本为 %d,当前支持版本为 %d。请更新插件。",
|
|
1,
|
|
PluginPackageManifestLoader.supportedPluginKitVersion
|
|
)
|
|
)
|
|
)
|
|
}
|
|
|
|
func testUninstallDeletesPackageAndCanRemoveStorage() throws {
|
|
let sourceURL = try makePackage(id: "com.example.demo")
|
|
let store = makeStore()
|
|
_ = try store.installPackage(from: sourceURL)
|
|
let storage = UserDefaultsPluginStorage(pluginID: "com.example.demo", userDefaults: defaults)
|
|
storage.set("value", forKey: "setting")
|
|
|
|
try store.uninstall(pluginID: "com.example.demo", removeData: true)
|
|
|
|
XCTAssertTrue(store.installedRecords().isEmpty)
|
|
XCTAssertNil(defaults.object(forKey: "plugin.com.example.demo.setting"))
|
|
}
|
|
|
|
func testFailedUpdateRestoresExistingPackage() throws {
|
|
let sourceURL = try makePackage(id: "com.example.demo", version: "1.0.0")
|
|
let invalidUpdateURL = try makePackage(id: "com.example.demo", version: "2.0.0", bundleRelativePath: "Missing.bundle")
|
|
let store = makeStore()
|
|
_ = try store.installPackage(from: sourceURL)
|
|
|
|
do {
|
|
_ = try store.updatePackage(from: invalidUpdateURL)
|
|
XCTFail("Expected update failure")
|
|
} catch {
|
|
// Expected path.
|
|
}
|
|
|
|
let record = try XCTUnwrap(store.installedRecords().first)
|
|
XCTAssertEqual(record.manifest.version, "1.0.0")
|
|
XCTAssertEqual(record.state, .enabled)
|
|
}
|
|
|
|
func testUpdateKeepsDisabledPluginDisabled() throws {
|
|
let sourceURL = try makePackage(id: "com.example.demo", version: "1.0.0")
|
|
let updateURL = try makePackage(id: "com.example.demo", version: "2.0.0")
|
|
let store = makeStore()
|
|
_ = try store.installPackage(from: sourceURL)
|
|
store.setEnabled(false, for: "com.example.demo")
|
|
|
|
_ = try store.updatePackage(from: updateURL)
|
|
|
|
let record = try XCTUnwrap(store.installedRecords().first)
|
|
XCTAssertEqual(record.manifest.version, "2.0.0")
|
|
XCTAssertEqual(record.state, .disabled)
|
|
}
|
|
|
|
func testDefaultRootDirectoryUsesCurrentApplicationSupportScope() {
|
|
let rootDirectory = PluginPackageStore.defaultRootDirectory(fileManager: .default)
|
|
|
|
XCTAssertEqual(rootDirectory.lastPathComponent, "Plugins")
|
|
XCTAssertEqual(
|
|
rootDirectory.deletingLastPathComponent().lastPathComponent,
|
|
AppStorageScope.applicationSupportDirectoryName
|
|
)
|
|
}
|
|
|
|
private func makeStore() -> PluginPackageStore {
|
|
PluginPackageStore(
|
|
rootDirectory: temporaryRoot,
|
|
userDefaults: defaults,
|
|
hostVersion: "1.0.0"
|
|
)
|
|
}
|
|
|
|
private func makePackage(
|
|
id: String,
|
|
version: String = "1.0.0",
|
|
bundleRelativePath: String = "Demo.bundle",
|
|
pluginKitVersion: Int = PluginPackageManifestLoader.supportedPluginKitVersion
|
|
) throws -> URL {
|
|
let packageURL = temporaryRoot
|
|
.appendingPathComponent("Source", isDirectory: true)
|
|
.appendingPathComponent("\(id)-\(version)", isDirectory: true)
|
|
.appendingPathExtension("mactoolsplugin")
|
|
let bundleURL = packageURL.appendingPathComponent(bundleRelativePath, isDirectory: true)
|
|
|
|
if bundleRelativePath == "Demo.bundle" {
|
|
try FileManager.default.createDirectory(at: bundleURL, withIntermediateDirectories: true)
|
|
} else {
|
|
try FileManager.default.createDirectory(at: packageURL, withIntermediateDirectories: true)
|
|
}
|
|
|
|
let manifest = PluginPackageManifest(
|
|
id: id,
|
|
displayName: "Demo",
|
|
version: version,
|
|
minHostVersion: "0.1.0",
|
|
pluginKitVersion: pluginKitVersion,
|
|
bundleRelativePath: bundleRelativePath
|
|
)
|
|
let data = try JSONEncoder().encode(manifest)
|
|
try data.write(to: packageURL.appendingPathComponent("plugin.json"))
|
|
|
|
return packageURL
|
|
}
|
|
}
|