removed unwanted bar now with waveforms, major updates
This commit is contained in:
@@ -1496,29 +1496,34 @@ final class KJamsCloneTests: XCTestCase {
|
||||
let audio = try XCTUnwrap(Bundle(for:Self.self).resourceURL?.appendingPathComponent("Fixtures/Demo.mp3"))
|
||||
let song = try XCTUnwrap(model.library).add(title:"Demo",audio:audio.path,cdg:nil);try model.refresh();model.activeSection = "Rotation";model.selectedSinger = a
|
||||
let host = NSHostingView(rootView:LibraryView(model:model).frame(width:1060,height:760).preferredColorScheme(.dark));host.frame = NSRect(x:0,y:0,width:1060,height:760)
|
||||
let window = NSWindow(contentRect:host.frame,styleMask:[.borderless],backing:.buffered,defer:false);window.appearance = NSAppearance(named:.darkAqua);window.contentView = host
|
||||
final class RotationTestWindow: NSWindow { override var canBecomeKey: Bool { true } }
|
||||
let window = RotationTestWindow(contentRect:host.frame,styleMask:[.borderless],backing:.buffered,defer:false);window.appearance = NSAppearance(named:.darkAqua);window.contentView = host
|
||||
let previous = NSApp.keyWindow;window.setFrameOrigin(NSPoint(x:-10000,y:-10000));window.orderFront(nil);window.makeKey()
|
||||
defer { window.orderOut(nil);window.contentView = nil;previous?.makeKey() }
|
||||
try await Task.sleep(nanoseconds:250_000_000);host.layoutSubtreeIfNeeded()
|
||||
func capture(_ name: String) throws {
|
||||
host.layoutSubtreeIfNeeded();if let rep = host.bitmapImageRepForCachingDisplay(in:host.bounds) { host.cacheDisplay(in:host.bounds,to:rep);try rep.representation(using:.png,properties:[:])!.write(to:URL(fileURLWithPath:"/tmp/kjmix-rotation-test-"+name+".png")) }
|
||||
host.layoutSubtreeIfNeeded()
|
||||
if let rep = host.bitmapImageRepForCachingDisplay(in:host.bounds) {
|
||||
host.cacheDisplay(in:host.bounds,to:rep)
|
||||
if let data = rep.representation(using:.png,properties:[:]) {
|
||||
let attachment = XCTAttachment(data:data,uniformTypeIdentifier:"public.png");attachment.name = "Rotation "+name;attachment.lifetime = .deleteOnSuccess
|
||||
XCTContext.runActivity(named:"Rotation "+name) { $0.add(attachment) }
|
||||
}
|
||||
}
|
||||
}
|
||||
func click(_ x: Double,_ top: Double) async throws {
|
||||
func descendants(_ view: NSView) -> [NSView] { [view]+view.subviews.flatMap{descendants($0)} }
|
||||
let tabs = try XCTUnwrap(descendants(host).compactMap{$0 as? NSTabView}.first)
|
||||
let rect = tabs.contentRect
|
||||
let point = tabs.convert(NSPoint(x:rect.minX+x,y:tabs.isFlipped ? rect.minY+top : rect.maxY-top),to:nil)
|
||||
let point = host.convert(NSPoint(x:x,y:host.isFlipped ? top : host.bounds.height-top),to:nil)
|
||||
for type in [NSEvent.EventType.leftMouseDown,.leftMouseUp] {
|
||||
let event = try XCTUnwrap(NSEvent.mouseEvent(with:type,location:point,modifierFlags:[],timestamp:ProcessInfo.processInfo.systemUptime,windowNumber:window.windowNumber,context:nil,eventNumber:0,clickCount:1,pressure:type == .leftMouseDown ? 1 : 0));NSApp.sendEvent(event)
|
||||
let event = try XCTUnwrap(NSEvent.mouseEvent(with:type,location:point,modifierFlags:[],timestamp:ProcessInfo.processInfo.systemUptime,windowNumber:window.windowNumber,context:nil,eventNumber:0,clickCount:1,pressure:type == .leftMouseDown ? 1 : 0));window.sendEvent(event)
|
||||
}
|
||||
try await Task.sleep(nanoseconds:120_000_000)
|
||||
}
|
||||
try await click(485,502);try capture("songs")
|
||||
try await click(990,565);try capture("added")
|
||||
try await click(492,521);try capture("songs")
|
||||
try await click(960,595);try capture("added")
|
||||
XCTAssertEqual(model.tonightSongIDs[a],[song],"Actual Add to Tonight button must enqueue the song")
|
||||
try await click(518,507);try capture("reordered")
|
||||
try await click(520,526);try capture("reordered")
|
||||
XCTAssertEqual(model.rotationSingers.map(\.id),[b,a],"Visible down arrow must move Alex after Sam")
|
||||
try await click(335,387)
|
||||
try await click(340,405)
|
||||
XCTAssertTrue(model.player.playing,"Actual Start Rotation button must play");XCTAssertTrue(model.rotationPlaybackActive);XCTAssertEqual(model.playbackSinger,a);XCTAssertNil(model.error)
|
||||
}
|
||||
|
||||
@@ -2627,6 +2632,7 @@ extension KJamsCloneTests {
|
||||
XCTAssertTrue(report.finished);XCTAssertNil(report.failure)
|
||||
XCTAssertEqual(report.completed,48);XCTAssertEqual(report.skipped,47)
|
||||
XCTAssertEqual(report.reportLines.count,48);XCTAssertEqual(system.songs.count,1)
|
||||
for _ in 0..<300 where system.automaticWaveforms.isRunning { try await Task.sleep(nanoseconds:20_000_000) }
|
||||
let song = try XCTUnwrap(system.songs.first)
|
||||
XCTAssertNotNil(try WaveformCache.load(audio:URL(fileURLWithPath:song.audio),directory:root.appendingPathComponent("DJ/Waveforms")))
|
||||
}
|
||||
@@ -2859,8 +2865,9 @@ extension KJamsCloneTests {
|
||||
try await Task.sleep(nanoseconds:300_000_000)
|
||||
XCTAssertEqual(player.loopAudioRestartCount,restarts,"Resizing an active loop must not restart the audio node")
|
||||
XCTAssertTrue(player.nativeLoopActive);XCTAssertLessThan(player.displayTime,0.301)
|
||||
let exit = player.displayTime
|
||||
let exit = player.displayTime,restartsOnExit = player.audioScheduleRestartCount
|
||||
player.loopEnabled = false
|
||||
XCTAssertEqual(player.audioScheduleRestartCount,restartsOnExit,"Loop Off must not stop and reschedule the running audio node")
|
||||
XCTAssertFalse(player.nativeLoopActive);XCTAssertTrue(player.playing)
|
||||
XCTAssertEqual(player.time,exit,accuracy:0.03)
|
||||
try await Task.sleep(nanoseconds:180_000_000)
|
||||
@@ -3019,4 +3026,31 @@ extension KJamsCloneTests {
|
||||
XCTAssertTrue(FileManager.default.fileExists(atPath:cache.path))
|
||||
}
|
||||
|
||||
@MainActor func testImportsFinishBeforeQueuedWaveformsAndAllowAnotherImport() async throws {
|
||||
let root = FileManager.default.temporaryDirectory.appendingPathComponent(UUID().uuidString)
|
||||
defer { try? FileManager.default.removeItem(at:root) }
|
||||
let id = UUID(),policy = WaveformWorkload.shared
|
||||
policy.setPlaying(true,id:id);try policy.acquire(cancelled:{false})
|
||||
var held = true
|
||||
defer { if held { policy.release() };policy.setPlaying(false,id:id) }
|
||||
let fixtures = try XCTUnwrap(Bundle(for:Self.self).resourceURL?.appendingPathComponent("Fixtures"))
|
||||
let fixture = fixtures.appendingPathComponent("Demo.mp3"),second = fixtures.appendingPathComponent("TestTrack.mp3")
|
||||
let model = AppModel(storageURL:root.appendingPathComponent("Karaoke")),dj = DJSystem(storageURL:root.appendingPathComponent("DJ"))
|
||||
model.importFiles([fixture]);dj.importFiles([fixture])
|
||||
for _ in 0..<300 where model.isImporting || dj.importing { try await Task.sleep(nanoseconds:10_000_000) }
|
||||
XCTAssertFalse(model.isImporting);XCTAssertFalse(dj.importing)
|
||||
XCTAssertEqual(model.songs.count,1);XCTAssertEqual(dj.songs.count,1)
|
||||
XCTAssertTrue(model.automaticWaveforms.isRunning);XCTAssertTrue(dj.automaticWaveforms.isRunning)
|
||||
let karaokeSong = try XCTUnwrap(model.songs.first)
|
||||
XCTAssertThrowsError(try WaveformCache.load(audio:URL(fileURLWithPath:karaokeSong.audio),directory:model.root.appendingPathComponent("Waveforms")))
|
||||
dj.importFiles([second])
|
||||
for _ in 0..<300 where dj.importing { try await Task.sleep(nanoseconds:10_000_000) }
|
||||
XCTAssertFalse(dj.importing);XCTAssertEqual(dj.songs.count,2,"A background waveform job must not block subsequent imports")
|
||||
policy.release();held = false
|
||||
for _ in 0..<500 where model.automaticWaveforms.isRunning || dj.automaticWaveforms.isRunning { try await Task.sleep(nanoseconds:10_000_000) }
|
||||
XCTAssertFalse(model.automaticWaveforms.isRunning);XCTAssertFalse(dj.automaticWaveforms.isRunning)
|
||||
XCTAssertNotNil(try WaveformCache.load(audio:URL(fileURLWithPath:karaokeSong.audio),directory:model.root.appendingPathComponent("Waveforms")))
|
||||
for song in dj.songs { XCTAssertNotNil(try WaveformCache.load(audio:URL(fileURLWithPath:song.audio),directory:dj.waveformDirectory)) }
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user