broken changes

This commit is contained in:
Mark Gregory
2026-11-05 16:26:24 +00:00
parent eb98f97ded
commit 7bf251c5e6
215 changed files with 822178 additions and 26 deletions
+75 -4
View File
@@ -1343,7 +1343,7 @@ final class KJamsCloneTests: XCTestCase {
@MainActor func testActualSingerDisclosurePressAndNavigationRetainExpansion() async throws {
let root = FileManager.default.temporaryDirectory.appendingPathComponent(UUID().uuidString);defer { try? FileManager.default.removeItem(at:root) }
let model = AppModel(storageURL:root);_ = try model.createEntity(.venue,name:"Hall");let singer = try model.createEntity(.singer,name:"Alex")
let host = NSHostingView(rootView:LibraryView(model:model,showDJ:false).frame(width:1320,height:900).preferredColorScheme(.dark))
let host = NSHostingView(rootView:LibraryView(model:model).frame(width:1320,height:900).preferredColorScheme(.dark))
host.frame = NSRect(x:0,y:0,width:1320,height:900)
let window = NSWindow(contentRect:host.frame,styleMask:[.borderless],backing:.buffered,defer:false);window.appearance = NSAppearance(named:.darkAqua);window.contentView = host
let previous = NSApp.keyWindow
@@ -1352,7 +1352,7 @@ final class KJamsCloneTests: XCTestCase {
try await Task.sleep(nanoseconds:200_000_000);host.layoutSubtreeIfNeeded();host.displayIfNeeded()
if let image = host.bitmapImageRepForCachingDisplay(in:host.bounds) { host.cacheDisplay(in:host.bounds,to:image) }
func click(_ x: Double,_ top: Double) async throws {
let point = NSPoint(x:x,y:900-top)
let point = NSPoint(x:x,y:900-top-44)
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)
@@ -1488,7 +1488,7 @@ final class KJamsCloneTests: XCTestCase {
let a = try model.createSinger(name:"Alex",venue:venue,songs:[]),b = try model.createSinger(name:"Sam",venue:venue,songs:[])
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,showDJ:false).frame(width:1060,height:760).preferredColorScheme(.dark));host.frame = NSRect(x:0,y:0,width:1060,height:760)
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
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() }
@@ -1498,7 +1498,7 @@ final class KJamsCloneTests: XCTestCase {
}
func click(_ x: Double,_ top: Double) async throws {
for type in [NSEvent.EventType.leftMouseDown,.leftMouseUp] {
let event = try XCTUnwrap(NSEvent.mouseEvent(with:type,location:NSPoint(x:x,y:760-top),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:NSPoint(x:x,y:760-top-44),modifierFlags:[],timestamp:ProcessInfo.processInfo.systemUptime,windowNumber:window.windowNumber,context:nil,eventNumber:0,clickCount:1,pressure:type == .leftMouseDown ? 1 : 0));NSApp.sendEvent(event)
}
try await Task.sleep(nanoseconds:120_000_000)
}
@@ -2069,3 +2069,74 @@ extension KJamsCloneTests {
XCTAssertLessThanOrEqual(DJWaveMarkers.beats(start:0,span:7200,bpm:180,width:500).count,101)
}
}
extension KJamsCloneTests {
private func xmlMapping(_ controls: [[String:Any]],endpoint: String = "XML Test Controller") throws -> DJXMLProfile {
let data = try PropertyListSerialization.data(fromPropertyList:["schemeVersion":1,"uuid":"xml-test","endpointName":endpoint,"controls":controls],format:.xml,options:0)
return try DJXMLProfile.parse(data,filename:endpoint)
}
func testDjayXMLLoadsAllSuppliedProfilesAndReportsUnsupportedActions() throws {
XCTAssertEqual(DJXMLProfile.bundled.count,210)
let dj2go = try XCTUnwrap(DJXMLProfile.bundled.first{$0.name == "Numark DJ2GO"})
XCTAssertTrue(dj2go.bindings.contains{$0.action == "A Play" && $0.status == 0x90 && $0.number == 59})
XCTAssertTrue(dj2go.bindings.contains{$0.action == "B Tempo XML" && $0.flipped && $0.pickup})
XCTAssertTrue(dj2go.issues.contains{$0.contains("pitchBendMinus")})
XCTAssertNotNil(DJXMLProfile.match(" NUMARK DJ2Go ",in:DJXMLProfile.bundled))
XCTAssertNil(DJXMLProfile.match("Numark",in:DJXMLProfile.bundled))
XCTAssertNil(DJXMLProfile.match(dj2go.endpoint,in:[dj2go,dj2go]))
}
func testDjayXMLRejectsMalformedAndConditionalConflictingBindings() throws {
XCTAssertThrowsError(try DJXMLProfile.parse(Data("<xml>invalid</xml>".utf8),filename:"Invalid"))
let profile = try xmlMapping([
["keyPath":"turntable1.playPause","midiMessageType":1,"midiChannel":0,"midiData":1],
["keyPath":"turntable1.bpmSync","midiMessageType":1,"midiChannel":0,"midiData":1,"condition":"shift == 1"],
["keyPath":"turntable2.playPause","midiMessageType":1,"midiChannel":99,"midiData":2],
["keyPath":"turntable3.playPause","midiMessageType":1,"midiChannel":2,"midiData":3],
["keyPath":"turntable1.highEQ","midiMessageType":3,"midiChannel":0,"midiData":4],
])
XCTAssertEqual(profile.bindings.map(\.action),["A High"])
XCTAssertFalse(profile.issues.isEmpty)
}
@MainActor func testDjayXMLRuntimePitchBendInversionRelativePickupAndPersistence() throws {
let suite = "KJMix.XML.\(UUID())",defaults = try XCTUnwrap(UserDefaults(suiteName:suite))
defer { defaults.removePersistentDomain(forName:suite) }
let profile = try xmlMapping([
["keyPath":"turntable1.playPause","midiMessageType":1,"midiChannel":0,"midiData":1],
["keyPath":"turntable1.speed","midiMessageType":6,"midiChannel":1,"midiData":0,"flipped":true,"pickupMode":true],
["keyPath":"musicLibrary.libraryRotary","midiMessageType":3,"midiChannel":0,"midiData":3,"controlType":"rotary-64","flipped":true],
])
let midi = DJMIDI(defaults:defaults,connect:false);midi.addXMLProfiles([profile]);midi.applyXML(profile)
midi.currentValue = { _ in 0.5 }
var actions = [(String,Double,Bool)]();midi.onAction = { actions.append(($0,$1,$2)) }
midi.receive([0x90,1,127,0x90,1,0,0x80,1,0])
XCTAssertEqual(actions.map{$0.0},["A Play"])
midi.receive([0xE1,0,0]);XCTAssertEqual(actions.count,1,"Pickup blocks a distant hardware fader")
midi.receive([0xE1,0,64]);XCTAssertEqual(actions.last?.0,"A Tempo XML");XCTAssertEqual(actions.last!.1,0.5,accuracy:0.001)
midi.receive([0xE1,127,127]);XCTAssertEqual(actions.last!.1,0,accuracy:0.001)
midi.receive([0xB0,3,65]);XCTAssertEqual(actions.last!.0,"Browse");XCTAssertEqual(actions.last!.1,-1);XCTAssertTrue(actions.last!.2)
let restored = DJMIDI(defaults:defaults,connect:false)
XCTAssertEqual(restored.activeXML?.bindings,profile.bindings)
restored.useCustomProfile();XCTAssertNil(restored.activeXML)
}
@MainActor func testXMLControllerAutodetectsReconnectsAndRemembersOptOut() throws {
var client = MIDIClientRef(0),source = MIDIEndpointRef(0)
XCTAssertEqual(MIDIClientCreateWithBlock("KJMix XML detection" as CFString,&client,nil),noErr)
defer { if source != 0 { MIDIEndpointDispose(source) };MIDIClientDispose(client) }
let endpoint = "XML Controller \(UUID())"
XCTAssertEqual(MIDISourceCreate(client,endpoint as CFString,&source),noErr)
let suite = "KJMix.XMLDetect.\(UUID())",defaults = try XCTUnwrap(UserDefaults(suiteName:suite))
defer { defaults.removePersistentDomain(forName:suite) }
let profile = try xmlMapping([["keyPath":"turntable1.playPause","midiMessageType":1,"midiChannel":0,"midiData":1]],endpoint:endpoint)
let midi = DJMIDI(defaults:defaults);midi.addXMLProfiles([profile])
var uid: Int32 = 0;MIDIObjectGetIntegerProperty(source,kMIDIPropertyUniqueID,&uid)
XCTAssertEqual(midi.sourceID,uid);XCTAssertEqual(midi.activeXML?.id,profile.id)
MIDIEndpointDispose(source);source = 0;midi.refreshSources();XCTAssertEqual(midi.sourceID,0)
XCTAssertEqual(defaults.integer(forKey:"DJ.MIDISource.v1"),Int(uid))
XCTAssertEqual(MIDISourceCreate(client,endpoint as CFString,&source),noErr)
XCTAssertEqual(MIDIObjectSetIntegerProperty(source,kMIDIPropertyUniqueID,uid),noErr)
midi.refreshSources();XCTAssertEqual(midi.sourceID,uid)
do { let restored = DJMIDI(defaults:defaults);XCTAssertEqual(restored.sourceID,uid);XCTAssertEqual(restored.activeXML?.id,profile.id) }
midi.select(0);midi.refreshSources();XCTAssertEqual(midi.sourceID,0);XCTAssertFalse(midi.autoDetect)
XCTAssertFalse(DJMIDI(defaults:defaults,connect:false).autoDetect)
}
}