importer chnges
This commit is contained in:
@@ -6,6 +6,118 @@ import UniformTypeIdentifiers
|
||||
@testable import KJamsClone
|
||||
|
||||
final class KJamsCloneTests: XCTestCase {
|
||||
@MainActor func testLargeImportReturnsImmediatelyAndReportsCompletion() async throws {
|
||||
let root = FileManager.default.temporaryDirectory.appendingPathComponent(UUID().uuidString)
|
||||
defer { try? FileManager.default.removeItem(at:root) }
|
||||
let inputs = root.appendingPathComponent("Inputs");try FileManager.default.createDirectory(at:inputs,withIntermediateDirectories:true)
|
||||
let fixture = try XCTUnwrap(Bundle(for:Self.self).resourceURL?.appendingPathComponent("Fixtures/Demo.mp3"))
|
||||
var urls = [URL]()
|
||||
for index in 0..<48 { let url = inputs.appendingPathComponent("Track \(index).mp3");try FileManager.default.copyItem(at:fixture,to:url);urls.append(url) }
|
||||
let model = AppModel(storageURL:root.appendingPathComponent("Library"))
|
||||
model.importFiles(urls)
|
||||
XCTAssertTrue(model.isImporting,"Import must return to the UI before processing the files")
|
||||
XCTAssertTrue(model.songs.isEmpty)
|
||||
var heartbeat = false
|
||||
Task { @MainActor in heartbeat = true }
|
||||
for _ in 0..<500 { if !model.isImporting { break };try await Task.sleep(nanoseconds:10_000_000) }
|
||||
XCTAssertTrue(heartbeat);XCTAssertFalse(model.isImporting)
|
||||
XCTAssertEqual(model.importProgress?.completed,48);XCTAssertEqual(model.importProgress?.total,48)
|
||||
XCTAssertEqual(model.importProgress?.finished,true);XCTAssertNil(model.importProgress?.failure)
|
||||
XCTAssertEqual(model.songs.count,1,"Identical audio still deduplicates")
|
||||
model.dismissImport();XCTAssertNil(model.importProgress)
|
||||
}
|
||||
func testImportCancellationReportsSavedPartialWork() throws {
|
||||
let root = FileManager.default.temporaryDirectory.appendingPathComponent(UUID().uuidString)
|
||||
try FileManager.default.createDirectory(at:root,withIntermediateDirectories:true)
|
||||
defer { try? FileManager.default.removeItem(at:root) }
|
||||
let db = try Library(url:root.appendingPathComponent("Library.sqlite"))
|
||||
let fixtures = try XCTUnwrap(Bundle(for:Self.self).resourceURL?.appendingPathComponent("Fixtures"))
|
||||
var cancel = false,latest = ImportProgress()
|
||||
XCTAssertThrowsError(try Importer(media:root.appendingPathComponent("Media")).run([fixtures.appendingPathComponent("Demo.mp3"),fixtures.appendingPathComponent("TestTrack.mp3")],library:db,cancelled:{cancel},progress:{ latest = $0;if $0.completed == 1 { cancel = true } })) { XCTAssertTrue($0 is CancellationError) }
|
||||
XCTAssertEqual(latest.completed,1);XCTAssertEqual(latest.total,2);XCTAssertEqual(try db.songs().count,1)
|
||||
}
|
||||
@MainActor func testImportFailureAndCancellationFinishTheDialog() async throws {
|
||||
let root = FileManager.default.temporaryDirectory.appendingPathComponent(UUID().uuidString)
|
||||
defer { try? FileManager.default.removeItem(at:root) }
|
||||
let model = AppModel(storageURL:root)
|
||||
model.importFiles([root.appendingPathComponent("missing.mp3")])
|
||||
for _ in 0..<200 { if !model.isImporting { break };try await Task.sleep(nanoseconds:10_000_000) }
|
||||
XCTAssertFalse(model.isImporting);XCTAssertEqual(model.importProgress?.finished,true);XCTAssertNotNil(model.importProgress?.failure)
|
||||
model.dismissImport()
|
||||
let fixture = try XCTUnwrap(Bundle(for:Self.self).resourceURL?.appendingPathComponent("Fixtures/Demo.mp3"))
|
||||
model.importFiles([fixture]);model.cancelImport()
|
||||
for _ in 0..<200 { if !model.isImporting { break };try await Task.sleep(nanoseconds:10_000_000) }
|
||||
XCTAssertFalse(model.isImporting);XCTAssertEqual(model.importProgress?.cancelled,true)
|
||||
model.dismissImport();XCTAssertNil(model.importProgress)
|
||||
}
|
||||
func testImportHistoryPersistsAndSkipsUnchangedTracksAndRenamedDuplicates() throws {
|
||||
let root = FileManager.default.temporaryDirectory.appendingPathComponent(UUID().uuidString)
|
||||
try FileManager.default.createDirectory(at:root,withIntermediateDirectories:true)
|
||||
defer { try? FileManager.default.removeItem(at:root) }
|
||||
let fixture = try XCTUnwrap(Bundle(for:Self.self).resourceURL?.appendingPathComponent("Fixtures/Demo.mp3"))
|
||||
let source = root.appendingPathComponent("Original.mp3");try FileManager.default.copyItem(at:fixture,to:source)
|
||||
let database = root.appendingPathComponent("Library.sqlite"),media = root.appendingPathComponent("Media"),importer = Importer(media:media)
|
||||
let db = try Library(url:database)
|
||||
let first = try importer.run([source],library:db)
|
||||
let id = try XCTUnwrap(first.first);try db.rate(id,5)
|
||||
let stored = try XCTUnwrap(db.song(id));let modified = try FileManager.default.attributesOfItem(atPath:stored.audio)[.modificationDate] as? Date
|
||||
let reopened = try Library(url:database);var progress = ImportProgress()
|
||||
XCTAssertEqual(try importer.run([source],library:reopened,progress:{progress = $0}),first)
|
||||
XCTAssertEqual(progress.completed,1);XCTAssertEqual(progress.skipped,1)
|
||||
let renamed = root.appendingPathComponent("Renamed.mp3");try FileManager.default.copyItem(at:source,to:renamed)
|
||||
XCTAssertEqual(try importer.run([renamed],library:reopened,progress:{progress = $0}),first)
|
||||
XCTAssertEqual(progress.skipped,1);XCTAssertEqual(try reopened.songs().count,1)
|
||||
XCTAssertEqual(try reopened.song(id)?.rating,5)
|
||||
XCTAssertEqual(try FileManager.default.attributesOfItem(atPath:stored.audio)[.modificationDate] as? Date,modified)
|
||||
try FileManager.default.removeItem(at:URL(fileURLWithPath:stored.audio))
|
||||
_ = try importer.run([source],library:reopened,progress:{progress = $0})
|
||||
XCTAssertEqual(progress.skipped,0);XCTAssertTrue(FileManager.default.fileExists(atPath:stored.audio))
|
||||
}
|
||||
func testUnchangedArchiveSkipsExtractionAndMissingGraphicsCanBeRestored() throws {
|
||||
let root = FileManager.default.temporaryDirectory.appendingPathComponent(UUID().uuidString)
|
||||
try FileManager.default.createDirectory(at:root,withIntermediateDirectories:true)
|
||||
defer { try? FileManager.default.removeItem(at:root) }
|
||||
let fixture = try XCTUnwrap(Bundle(for:Self.self).resourceURL?.appendingPathComponent("Fixtures/Demo.zip"))
|
||||
let importer = Importer(media:root.appendingPathComponent("Media")),db = try Library(url:root.appendingPathComponent("Library.sqlite"))
|
||||
let ids = try importer.run([fixture],library:db);var updates = [ImportProgress]()
|
||||
XCTAssertEqual(try importer.run([fixture],library:db,progress:{updates.append($0)}),ids)
|
||||
XCTAssertFalse(updates.contains{$0.current.hasPrefix("Extracting ")});XCTAssertEqual(updates.last?.skipped,1)
|
||||
let cdg = try XCTUnwrap(try db.songs().first?.cdg);try FileManager.default.removeItem(atPath:cdg)
|
||||
updates = [];_ = try importer.run([fixture],library:db,progress:{updates.append($0)})
|
||||
XCTAssertTrue(updates.contains{$0.current.hasPrefix("Extracting ")});XCTAssertEqual(updates.last?.skipped,0)
|
||||
XCTAssertTrue(FileManager.default.fileExists(atPath:try XCTUnwrap(try db.songs().first?.cdg)))
|
||||
}
|
||||
func testChangedAudioAndGraphicsAreRechecked() throws {
|
||||
let root = FileManager.default.temporaryDirectory.appendingPathComponent(UUID().uuidString)
|
||||
try FileManager.default.createDirectory(at:root,withIntermediateDirectories:true)
|
||||
defer { try? FileManager.default.removeItem(at:root) }
|
||||
let fixtures = try XCTUnwrap(Bundle(for:Self.self).resourceURL?.appendingPathComponent("Fixtures"))
|
||||
let audio = root.appendingPathComponent("Track.mp3"),cdg = root.appendingPathComponent("Track.cdg")
|
||||
try FileManager.default.copyItem(at:fixtures.appendingPathComponent("Demo.mp3"),to:audio)
|
||||
try FileManager.default.copyItem(at:fixtures.appendingPathComponent("Demo.cdg"),to:cdg)
|
||||
let db = try Library(url:root.appendingPathComponent("Library.sqlite")),importer = Importer(media:root.appendingPathComponent("Media"))
|
||||
let ids = try importer.run([audio],library:db)
|
||||
let originalGraphics = try XCTUnwrap(try db.song(ids[0])?.cdg)
|
||||
let changedGraphics = try Data(contentsOf:fixtures.appendingPathComponent("TestTrack.cdg"));try changedGraphics.write(to:cdg)
|
||||
var progress = ImportProgress()
|
||||
XCTAssertEqual(try importer.run([audio],library:db,progress:{progress = $0}),ids);XCTAssertEqual(progress.skipped,0)
|
||||
let newGraphics = try XCTUnwrap(try db.song(ids[0])?.cdg)
|
||||
XCTAssertNotEqual(originalGraphics,newGraphics);XCTAssertEqual(try Data(contentsOf:URL(fileURLWithPath:newGraphics)),changedGraphics)
|
||||
try Data(contentsOf:fixtures.appendingPathComponent("TestTrack.mp3")).write(to:audio)
|
||||
_ = try importer.run([audio],library:db,progress:{progress = $0});XCTAssertEqual(progress.skipped,0);XCTAssertEqual(try db.songs().count,2)
|
||||
}
|
||||
func testArchiveDeduplicationStillCreatesManagedMediaForReferencedAudio() throws {
|
||||
let root = FileManager.default.temporaryDirectory.appendingPathComponent(UUID().uuidString)
|
||||
try FileManager.default.createDirectory(at:root,withIntermediateDirectories:true)
|
||||
defer { try? FileManager.default.removeItem(at:root) }
|
||||
let fixtures = try XCTUnwrap(Bundle(for:Self.self).resourceURL?.appendingPathComponent("Fixtures"))
|
||||
let db = try Library(url:root.appendingPathComponent("Library.sqlite")),media = root.appendingPathComponent("Media"),importer = Importer(media:media)
|
||||
let ids = try importer.run([fixtures.appendingPathComponent("Demo.mp3")],library:db,copyMedia:false)
|
||||
_ = try importer.run([fixtures.appendingPathComponent("Demo.zip")],library:db,copyMedia:false)
|
||||
XCTAssertEqual(try db.songs().count,1)
|
||||
let song = try XCTUnwrap(try db.song(ids[0]));XCTAssertEqual(URL(fileURLWithPath:song.audio).deletingLastPathComponent().standardizedFileURL,media.standardizedFileURL)
|
||||
XCTAssertTrue(FileManager.default.fileExists(atPath:song.audio));XCTAssertNotNil(song.cdg)
|
||||
}
|
||||
func packet(_ instruction: UInt8, _ data: [UInt8]) -> [UInt8] { [9,instruction,0,0] + Array((data + Array(repeating: 0,count:16)).prefix(16)) + [0,0,0,0] }
|
||||
func testPresetTileXORAndInvalidCoordinates() {
|
||||
var d = CDGDecoder()
|
||||
@@ -219,6 +331,8 @@ final class KJamsCloneTests: XCTestCase {
|
||||
_ = try model.createEntity(.singer,name:"Alex")
|
||||
let fixtures = try XCTUnwrap(Bundle(for:Self.self).resourceURL?.appendingPathComponent("Fixtures"))
|
||||
model.importFiles([fixtures.appendingPathComponent("Demo.zip")])
|
||||
for _ in 0..<200 { if !model.isImporting { break };try await Task.sleep(nanoseconds:10_000_000) }
|
||||
XCTAssertFalse(model.isImporting);XCTAssertNil(model.importProgress?.failure)
|
||||
let song = try XCTUnwrap(model.songs.first)
|
||||
model.enqueue(song.id); model.enqueue(song.id); model.playQueueNext()
|
||||
XCTAssertEqual(model.queue.count,1); XCTAssertEqual(model.history.count,1)
|
||||
|
||||
Reference in New Issue
Block a user