304 lines
14 KiB
Swift
304 lines
14 KiB
Swift
import XCTest
|
|
import PuterHelperProtocol
|
|
@testable import puter
|
|
|
|
final class ModelTests: XCTestCase {
|
|
func testNativeProcessSamplerPerformanceBudget() {
|
|
let result = ProcessSamplerBenchmark.run(iterations: 6)
|
|
XCTAssertGreaterThan(result.processCount, 20, "Native telemetry should return a credible process inventory")
|
|
XCTAssertLessThan(
|
|
result.averageSeconds,
|
|
0.5,
|
|
"The native hot path should remain comfortably below the foreground sampling interval"
|
|
)
|
|
let lightweight = ProcessSamplerBenchmark.run(iterations: 12, includeProcessInventory: false)
|
|
print(
|
|
String(
|
|
format: "Sampler benchmark: full %.4f s, lightweight %.4f s per iteration",
|
|
result.averageSeconds,
|
|
lightweight.averageSeconds
|
|
)
|
|
)
|
|
XCTAssertEqual(lightweight.processCount, 0)
|
|
XCTAssertLessThan(
|
|
lightweight.averageSeconds,
|
|
result.averageSeconds,
|
|
"Lightweight system telemetry must remain cheaper than enumerating every process"
|
|
)
|
|
}
|
|
|
|
func testOnlyProcessConsumerPagesUseLiveInventory() {
|
|
XCTAssertTrue(TaskSection.processes.usesLiveProcessInventory)
|
|
XCTAssertTrue(TaskSection.performance.usesLiveProcessInventory)
|
|
XCTAssertTrue(TaskSection.energy.usesLiveProcessInventory)
|
|
XCTAssertTrue(TaskSection.diagnostics.usesLiveProcessInventory)
|
|
XCTAssertFalse(TaskSection.hardware.usesLiveProcessInventory)
|
|
XCTAssertFalse(TaskSection.startup.usesLiveProcessInventory)
|
|
XCTAssertFalse(TaskSection.services.usesLiveProcessInventory)
|
|
XCTAssertFalse(TaskSection.settings.usesLiveProcessInventory)
|
|
}
|
|
|
|
func testProcessHeaderCyclesDescendingAscendingThenGrouped() {
|
|
let descending = ProcessSortCycleState.next(
|
|
currentField: .name, ascending: true, grouped: true, clicked: .cpu
|
|
)
|
|
XCTAssertEqual(descending, .init(field: .cpu, ascending: false, grouped: false))
|
|
let ascending = ProcessSortCycleState.next(
|
|
currentField: descending.field, ascending: descending.ascending,
|
|
grouped: descending.grouped, clicked: .cpu
|
|
)
|
|
XCTAssertEqual(ascending, .init(field: .cpu, ascending: true, grouped: false))
|
|
let grouped = ProcessSortCycleState.next(
|
|
currentField: ascending.field, ascending: ascending.ascending,
|
|
grouped: ascending.grouped, clicked: .cpu
|
|
)
|
|
XCTAssertEqual(grouped, .init(field: .cpu, ascending: true, grouped: true))
|
|
XCTAssertEqual(descending.accessibilityValue(for: .cpu), "Sorted descending")
|
|
XCTAssertEqual(ascending.accessibilityValue(for: .cpu), "Sorted ascending")
|
|
XCTAssertEqual(grouped.accessibilityValue(for: .cpu), "Not sorted; grouped by category")
|
|
XCTAssertEqual(descending.accessibilityValue(for: .memory), "Not sorted; grouped by category")
|
|
}
|
|
|
|
func testPrivilegedHelperRejectsUnsafeFanTargets() {
|
|
XCTAssertEqual(PuterFanTargetValidator.validate([0: 2_000, 1: 4_500])?.count, 2)
|
|
XCTAssertNil(PuterFanTargetValidator.validate([16: 2_000]))
|
|
XCTAssertNil(PuterFanTargetValidator.validate([0: 20_001]))
|
|
XCTAssertNil(PuterFanTargetValidator.validate([:] as [NSNumber: NSNumber]))
|
|
}
|
|
|
|
func testBackgroundTaskRegistryParsesAndDeduplicatesLoginApps() {
|
|
let fixture = """
|
|
#1:
|
|
Name: Example App
|
|
Developer Name: Example Corp
|
|
Type: app (0x2)
|
|
Disposition: [disabled, allowed, notified] (0xa)
|
|
URL: file:///Applications/Example%20App.app/
|
|
Bundle Identifier: com.example.app
|
|
|
|
#2:
|
|
Name: Example App
|
|
Developer Name: Example Corp
|
|
Type: app (0x2)
|
|
Disposition: [enabled, allowed, notified] (0xb)
|
|
URL: file:///Applications/Example%20App.app/
|
|
Bundle Identifier: com.example.app
|
|
|
|
#3:
|
|
Name: Background Daemon
|
|
Type: daemon (0x10)
|
|
Disposition: [enabled, allowed, notified] (0xb)
|
|
URL: file:///Library/LaunchDaemons/com.example.daemon.plist
|
|
"""
|
|
let items = BackgroundTaskRegistryParser.parse(fixture)
|
|
XCTAssertEqual(items.count, 1)
|
|
XCTAssertEqual(items[0].name, "Example App")
|
|
XCTAssertEqual(items[0].developer, "Example Corp")
|
|
XCTAssertEqual(items[0].bundleIdentifier, "com.example.app")
|
|
XCTAssertEqual(items[0].path, "/Applications/Example App.app")
|
|
XCTAssertTrue(items[0].enabled)
|
|
}
|
|
|
|
func testOnlySleepRelatedAssertionsAreBlockers() {
|
|
let blocker = SleepAssertionSnapshot(
|
|
id: "1", pid: 42, processName: "Video", type: "NoIdleSleepAssertion", reason: "Playback", duration: "00:01:00"
|
|
)
|
|
let activity = SleepAssertionSnapshot(
|
|
id: "2", pid: 43, processName: "WindowServer", type: "UserIsActive", reason: "Input", duration: "00:00:01"
|
|
)
|
|
XCTAssertTrue(blocker.preventsSleep)
|
|
XCTAssertFalse(activity.preventsSleep)
|
|
}
|
|
|
|
func testResourceAlertsRequireSustainedSamplesAndCooldown() {
|
|
let now = Date(timeIntervalSince1970: 1_000)
|
|
XCTAssertFalse(ResourceAlertPolicy.shouldFire(
|
|
value: 95, threshold: 90, consecutiveSamples: 2, requiredSamples: 3,
|
|
lastFired: nil, now: now, cooldown: 300
|
|
))
|
|
XCTAssertTrue(ResourceAlertPolicy.shouldFire(
|
|
value: 95, threshold: 90, consecutiveSamples: 3, requiredSamples: 3,
|
|
lastFired: nil, now: now, cooldown: 300
|
|
))
|
|
XCTAssertFalse(ResourceAlertPolicy.shouldFire(
|
|
value: 95, threshold: 90, consecutiveSamples: 4, requiredSamples: 3,
|
|
lastFired: now.addingTimeInterval(-299), now: now, cooldown: 300
|
|
))
|
|
XCTAssertTrue(ResourceAlertPolicy.shouldFire(
|
|
value: 95, threshold: 90, consecutiveSamples: 4, requiredSamples: 3,
|
|
lastFired: now.addingTimeInterval(-300), now: now, cooldown: 300
|
|
))
|
|
}
|
|
|
|
func testTelemetryRecordingSampleRoundTripsThroughJSON() throws {
|
|
let sample = TelemetryRecordingSample(
|
|
timestamp: Date(timeIntervalSince1970: 123), cpuPercent: 12.5,
|
|
memoryUsedBytes: 1_000, memoryTotalBytes: 2_000, memoryPressure: "Normal",
|
|
diskReadBytesPerSecond: 10, diskWriteBytesPerSecond: 20, gpuPercent: 30,
|
|
networkReceiveBytesPerSecond: 40, networkSendBytesPerSecond: 50,
|
|
systemPowerWatts: 6.5, thermalState: "Nominal", processCount: 100
|
|
)
|
|
XCTAssertEqual(try JSONDecoder().decode(TelemetryRecordingSample.self, from: JSONEncoder().encode(sample)), sample)
|
|
}
|
|
|
|
func testDiagnosticCaptureRoundTripsThroughJSON() throws {
|
|
let capture = DiagnosticCapture(
|
|
id: UUID(), name: "Baseline", createdAt: Date(timeIntervalSince1970: 100),
|
|
system: DiagnosticSystemMetrics(
|
|
cpuPercent: 10, memoryUsedBytes: 100, memoryTotalBytes: 200, memoryPressure: "Normal",
|
|
compressedBytes: 5, swapUsedBytes: 0, diskReadBytesPerSecond: 1,
|
|
diskWriteBytesPerSecond: 2, diskActivePercent: 3, gpuPercent: 4,
|
|
networkReceiveBytesPerSecond: 5, networkSendBytesPerSecond: 6,
|
|
systemPowerWatts: 7, thermalState: "Nominal", processCount: 8,
|
|
threadCount: 9, uptimeSeconds: 10
|
|
),
|
|
processes: [], disks: []
|
|
)
|
|
XCTAssertEqual(try JSONDecoder().decode(DiagnosticCapture.self, from: JSONEncoder().encode(capture)), capture)
|
|
}
|
|
|
|
func testSystemExecutableHasAValidCodeSignature() {
|
|
let security = ProcessSecurityInspector.inspect(executablePath: "/bin/ls")
|
|
XCTAssertEqual(security.signatureStatus, "Valid")
|
|
XCTAssertNotEqual(security.signingIdentifier, "Not reported")
|
|
}
|
|
|
|
func testPhysicalDiskRemainingLifeIsDerivedFromPercentageUsed() {
|
|
let disk = PhysicalDiskSnapshot(
|
|
id: "disk0", name: "SSD", protocolName: "NVMe", capacity: 1_000,
|
|
isInternal: true, isRemovable: false, isSolidState: true, smartStatus: "Verified",
|
|
trimEnabled: true, temperatureCelsius: 39, percentageUsed: 5, availableSparePercent: 100,
|
|
powerOnHours: 10, powerCycles: 2, unsafeShutdowns: 0, mediaErrors: 0,
|
|
bytesRead: 100, bytesWritten: 200
|
|
)
|
|
XCTAssertEqual(disk.remainingLifePercent, 95)
|
|
}
|
|
|
|
func testDiskActivityExcludesVirtualDiskImages() {
|
|
XCTAssertTrue(DiskMediaClassifier.isPhysicalMediaName("APPLE SSD AP1024Z Media"))
|
|
XCTAssertTrue(DiskMediaClassifier.isPhysicalMediaName("External USB SSD Media"))
|
|
XCTAssertFalse(DiskMediaClassifier.isPhysicalMediaName("Apple Disk Image Media"))
|
|
XCTAssertFalse(DiskMediaClassifier.isPhysicalMediaName("RAM Disk Media"))
|
|
XCTAssertFalse(DiskMediaClassifier.isPhysicalMediaName(""))
|
|
}
|
|
|
|
func testPhysicalDiskScannerFindsAttachedStorage() {
|
|
let disks = PhysicalDiskScanner.capture()
|
|
XCTAssertFalse(disks.isEmpty)
|
|
XCTAssertTrue(disks.allSatisfy { !$0.id.isEmpty && $0.capacity > 0 })
|
|
XCTAssertTrue(disks.compactMap(\.percentageUsed).allSatisfy { $0 == $0.rounded() && (0...100).contains($0) })
|
|
XCTAssertTrue(disks.compactMap(\.availableSparePercent).allSatisfy { $0 == $0.rounded() && (0...100).contains($0) })
|
|
}
|
|
|
|
func testAppleSystemServicePublisherAndDomainIdentity() {
|
|
let system = ServiceRecord(label: "com.apple.accessoryd", pid: 7750, lastExitStatus: 0, domain: .system)
|
|
let user = ServiceRecord(label: "com.apple.accessoryd", pid: 731, lastExitStatus: 0, domain: .user)
|
|
|
|
XCTAssertEqual(system.publisher, "Apple")
|
|
XCTAssertEqual(system.domain.launchctlPrefix, "system")
|
|
XCTAssertFalse(system.isControllable)
|
|
XCTAssertTrue(user.isControllable)
|
|
XCTAssertNotEqual(system.id, user.id)
|
|
}
|
|
|
|
func testThirdPartyApplicationServicePublisher() {
|
|
let service = ServiceRecord(
|
|
label: "application.com.adobe.AdobeResourceSynchronizer.210006977.210006983",
|
|
pid: 938,
|
|
lastExitStatus: 0,
|
|
domain: .user
|
|
)
|
|
XCTAssertEqual(service.publisher, "Adobe")
|
|
XCTAssertEqual(service.displayName, "AdobeResourceSynchronizer")
|
|
}
|
|
|
|
func testProcessDisplayNameFallback() {
|
|
let process = ProcessRecord(
|
|
pid: 42,
|
|
parentPID: 1,
|
|
name: " ",
|
|
executablePath: "/usr/bin/example",
|
|
user: "tester",
|
|
cpu: 0,
|
|
memoryPercent: 0,
|
|
residentBytes: 0,
|
|
state: "S",
|
|
elapsed: "00:01",
|
|
cpuTime: 0,
|
|
threadCount: 1,
|
|
openFileCount: 0,
|
|
architecture: "ARM64",
|
|
priority: 31,
|
|
nice: 0
|
|
)
|
|
XCTAssertEqual(process.displayName, "Process 42")
|
|
}
|
|
|
|
func testAllNavigationSectionsHaveIcons() {
|
|
XCTAssertEqual(Set(TaskSection.allCases.map(\.rawValue)).count, TaskSection.allCases.count)
|
|
XCTAssertTrue(TaskSection.allCases.allSatisfy { !$0.icon.isEmpty })
|
|
}
|
|
|
|
func testBatteryHealthUsesFullChargeAndDesignCapacity() {
|
|
let battery = BatterySnapshot(fullChargeCapacityMAh: 5_761, designCapacityMAh: 6_249)
|
|
XCTAssertEqual(battery.healthPercent, 92.19, accuracy: 0.01)
|
|
}
|
|
|
|
func testBatteryPowerRejectsCorruptTelemetry() {
|
|
let normal = BatterySnapshot(voltageVolts: 12.1, currentAmps: -2.5)
|
|
let corrupt = BatterySnapshot(voltageVolts: 12.1, currentAmps: Double(UInt64.max))
|
|
|
|
XCTAssertEqual(normal.batteryPowerWatts, 30.25, accuracy: 0.001)
|
|
XCTAssertEqual(corrupt.batteryPowerWatts, 0)
|
|
}
|
|
|
|
func testLiveSMCPowerSensorsOverrideCalculatedBatteryPower() {
|
|
let parsed = SMCPowerSensorParser.parse("""
|
|
[PDTR] 42.33818435668945
|
|
[PPBR] 23.479623794555664
|
|
[PSTR] 66.0309829711914
|
|
""")
|
|
XCTAssertEqual(parsed.systemWatts, 66.0309829711914, accuracy: 0.0001)
|
|
XCTAssertEqual(parsed.adapterWatts, 42.33818435668945, accuracy: 0.0001)
|
|
XCTAssertEqual(parsed.batteryWatts, 23.479623794555664, accuracy: 0.0001)
|
|
let battery = BatterySnapshot(
|
|
voltageVolts: 12, currentAmps: 1,
|
|
sensorBatteryPowerWatts: parsed.batteryWatts
|
|
)
|
|
XCTAssertEqual(battery.batteryPowerWatts, parsed.batteryWatts, accuracy: 0.0001)
|
|
}
|
|
|
|
func testUSBPDProfileAndNegotiatedPower() {
|
|
let profile = PowerProfile(voltageVolts: 20, currentAmps: 4.29)
|
|
let adapter = PowerAdapterSnapshot(
|
|
ratedWatts: 86,
|
|
negotiatedVoltage: profile.voltageVolts,
|
|
negotiatedCurrent: profile.currentAmps,
|
|
profiles: [profile]
|
|
)
|
|
XCTAssertEqual(profile.watts, 85.8, accuracy: 0.001)
|
|
XCTAssertEqual(adapter.negotiatedWatts, 85.8, accuracy: 0.001)
|
|
}
|
|
|
|
func testMemoryBandwidthCatalogUsesSpecificChipBeforeFamilyFallback() {
|
|
XCTAssertEqual(
|
|
MemoryBandwidthCatalog.description(for: "Apple M4 Pro", cpuCoreCount: 14),
|
|
"273 GB/s bandwidth (Apple specification)"
|
|
)
|
|
XCTAssertEqual(
|
|
MemoryBandwidthCatalog.description(for: "Apple M4 Max", cpuCoreCount: 14),
|
|
"410 GB/s bandwidth (Apple specification)"
|
|
)
|
|
XCTAssertEqual(
|
|
MemoryBandwidthCatalog.description(for: "Apple M4 Max", cpuCoreCount: 16),
|
|
"546 GB/s bandwidth (Apple specification)"
|
|
)
|
|
XCTAssertEqual(
|
|
MemoryBandwidthCatalog.description(for: "Apple M5 Pro", cpuCoreCount: 18),
|
|
"Up to 307 GB/s bandwidth (Apple specification)"
|
|
)
|
|
XCTAssertEqual(MemoryBandwidthCatalog.description(for: "Intel Core i9", cpuCoreCount: 8), "Clock not published by macOS")
|
|
XCTAssertEqual(MemoryBandwidthCatalog.specifications.count, 20)
|
|
}
|
|
}
|