- SessionController wraps RoyalVNCKit.VNCConnection via nonisolated delegate adapter that bridges callbacks to @MainActor; Keychain-resolved passwords; reconnect with jittered exponential backoff; NWPathMonitor adaptive-quality hook; framebuffer rendered to CALayer.contents from didUpdateFramebuffer. - Touch + trackpad input modes with floating soft cursor overlay; hardware keyboard via pressesBegan/Ended → X11 keysyms; UIPointerInteraction with hidden cursor for indirect pointers; pinch-to-zoom; Apple Pencil as direct touch; two-finger pan / indirect scroll wheel events. - Bidirectional clipboard sync (per-connection opt-in); multi-monitor screen picker with input remapping; screenshot capture → share sheet; on-disconnect reconnect/close prompt; view-only and curtain-mode persisted. - iPad multi-window via WindowGroup(for: UUID.self) + context-menu open; CloudKit-backed ModelContainer with local fallback; PrivacyInfo.xcprivacy. 10 VNCCore tests + 4 VNCUI tests pass; iPhone and iPad simulator builds clean. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
56 lines
2.3 KiB
Swift
56 lines
2.3 KiB
Swift
import Testing
|
|
@testable import VNCUI
|
|
import CoreGraphics
|
|
|
|
@Suite struct InputMapperTests {
|
|
@Test func centerOfViewMapsToCenterOfFramebuffer() {
|
|
let mapper = InputMapper()
|
|
let fb = CGSize(width: 1920, height: 1080)
|
|
let view = CGSize(width: 192, height: 108)
|
|
let normalized = mapper.normalize(viewPoint: CGPoint(x: 96, y: 54),
|
|
in: view,
|
|
framebufferSize: fb)
|
|
#expect(normalized != nil)
|
|
#expect(abs((normalized?.x ?? 0) - 0.5) < 0.001)
|
|
#expect(abs((normalized?.y ?? 0) - 0.5) < 0.001)
|
|
}
|
|
|
|
@Test func aspectFitLetterboxesTallerView() {
|
|
let mapper = InputMapper()
|
|
let fb = CGSize(width: 1600, height: 900) // 16:9
|
|
let view = CGSize(width: 800, height: 800) // 1:1
|
|
let displayed = mapper.displayedRect(for: fb, in: view)
|
|
#expect(displayed != nil)
|
|
#expect(abs((displayed?.width ?? 0) - 800) < 0.001)
|
|
#expect(abs((displayed?.height ?? 0) - 450) < 0.001)
|
|
#expect(abs((displayed?.origin.y ?? 0) - 175) < 0.001)
|
|
}
|
|
|
|
@Test func aspectFitPillarboxesWiderView() {
|
|
let mapper = InputMapper()
|
|
let fb = CGSize(width: 800, height: 800)
|
|
let view = CGSize(width: 1600, height: 900)
|
|
let displayed = mapper.displayedRect(for: fb, in: view)
|
|
#expect(displayed != nil)
|
|
#expect(abs((displayed?.height ?? 0) - 900) < 0.001)
|
|
#expect(abs((displayed?.width ?? 0) - 900) < 0.001)
|
|
#expect(abs((displayed?.origin.x ?? 0) - 350) < 0.001)
|
|
}
|
|
|
|
@Test func roundTripNormalizationIsStable() {
|
|
let mapper = InputMapper()
|
|
let fb = CGSize(width: 1920, height: 1080)
|
|
let view = CGSize(width: 800, height: 600)
|
|
let target = CGPoint(x: 0.25, y: 0.75)
|
|
let viewPoint = mapper.viewPoint(forNormalized: target,
|
|
in: view,
|
|
framebufferSize: fb)
|
|
let normalized = mapper.normalize(viewPoint: viewPoint ?? .zero,
|
|
in: view,
|
|
framebufferSize: fb)
|
|
#expect(normalized != nil)
|
|
#expect(abs((normalized?.x ?? 0) - target.x) < 0.001)
|
|
#expect(abs((normalized?.y ?? 0) - target.y) < 0.001)
|
|
}
|
|
}
|