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
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ nonisolated struct SampleDatabaseInstaller: Sendable {
func installIfNeeded() throws -> URL {
let installed = installedURL
guard !FileManager.default.fileExists(atPath: installed.path) else { return installed }
try removeSidecars(of: installed)
try copyBundledFile(to: installed)
Self.logger.info("Installed the sample database")
return installed
Expand All @@ -53,19 +54,28 @@ nonisolated struct SampleDatabaseInstaller: Sendable {
@discardableResult
func reset() throws -> URL {
let installed = installedURL
for url in [installed] + Self.sidecarSuffixes.map({ URL(fileURLWithPath: installed.path + $0) }) {
guard FileManager.default.fileExists(atPath: url.path) else { continue }
do {
try FileManager.default.removeItem(at: url)
} catch {
throw SampleDatabaseError.copyFailed(message: error.localizedDescription)
}
}
try removeItemIfPresent(at: installed)
try removeSidecars(of: installed)
try copyBundledFile(to: installed)
Self.logger.info("Reset the sample database")
return installed
}

private func removeSidecars(of database: URL) throws {
for suffix in Self.sidecarSuffixes {
try removeItemIfPresent(at: URL(fileURLWithPath: database.path + suffix))
}
}

private func removeItemIfPresent(at url: URL) throws {
guard FileManager.default.fileExists(atPath: url.path) else { return }
do {
try FileManager.default.removeItem(at: url)
} catch {
throw SampleDatabaseError.copyFailed(message: error.localizedDescription)
}
}

private func copyBundledFile(to destination: URL) throws {
guard let bundledURL else {
Self.logger.error("Chinook.sqlite is not in the app bundle")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ import Testing

@Suite("Sample database installer")
struct SampleDatabaseInstallerTests {
private static let sqliteSidecarSuffixes = ["-journal", "-wal", "-shm"]

private let directory: URL
private let bundled: URL

Expand Down Expand Up @@ -38,14 +40,30 @@ struct SampleDatabaseInstallerTests {
let installer = SampleDatabaseInstaller(bundledURL: bundled, directory: directory)
let installed = try installer.installIfNeeded()
try Data("edited".utf8).write(to: installed)
for suffix in SampleDatabaseInstaller.sidecarSuffixes {
for suffix in Self.sqliteSidecarSuffixes {
try Data("stale".utf8).write(to: URL(fileURLWithPath: installed.path + suffix))
}

try installer.reset()

#expect(try String(contentsOf: installed, encoding: .utf8) == "original")
for suffix in SampleDatabaseInstaller.sidecarSuffixes {
for suffix in Self.sqliteSidecarSuffixes {
#expect(!FileManager.default.fileExists(atPath: installed.path + suffix))
}
}

@Test("Installing over journal files a failed reset left behind removes them first")
func installRemovesOrphanedSidecars() throws {
let installer = SampleDatabaseInstaller(bundledURL: bundled, directory: directory)
try FileManager.default.createDirectory(at: directory, withIntermediateDirectories: true)
for suffix in Self.sqliteSidecarSuffixes {
try Data("stale".utf8).write(to: URL(fileURLWithPath: installer.installedURL.path + suffix))
}

let installed = try installer.installIfNeeded()

#expect(try String(contentsOf: installed, encoding: .utf8) == "original")
for suffix in Self.sqliteSidecarSuffixes {
#expect(!FileManager.default.fileExists(atPath: installed.path + suffix))
}
}
Expand Down
Loading