@@ -16,6 +16,7 @@
|
|||||||
1B15F48F6B19E9EA4556ECD5 /* MMStorageTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 78325870C7CD8312AECA2236 /* MMStorageTests.swift */; };
|
1B15F48F6B19E9EA4556ECD5 /* MMStorageTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 78325870C7CD8312AECA2236 /* MMStorageTests.swift */; };
|
||||||
1B3F1C8ABF0ADFAA290FF6F6 /* MMSMCParser.m in Sources */ = {isa = PBXBuildFile; fileRef = 375AA781A2AA0A20B119C7B7 /* MMSMCParser.m */; };
|
1B3F1C8ABF0ADFAA290FF6F6 /* MMSMCParser.m in Sources */ = {isa = PBXBuildFile; fileRef = 375AA781A2AA0A20B119C7B7 /* MMSMCParser.m */; };
|
||||||
22CA7351A745F7837E487051 /* MMPeripheralsTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D1486E1512594482380CB492 /* MMPeripheralsTests.swift */; };
|
22CA7351A745F7837E487051 /* MMPeripheralsTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D1486E1512594482380CB492 /* MMPeripheralsTests.swift */; };
|
||||||
|
2665E9DAE13C13239857B4BB /* MMSnapshotContractTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = EA79F8A259CA3056F9B131A9 /* MMSnapshotContractTests.swift */; };
|
||||||
2C0F7AB93691ED8AA86FBCF0 /* MMProcessDetailInspector.m in Sources */ = {isa = PBXBuildFile; fileRef = 52E641F8A664D456518EDBA4 /* MMProcessDetailInspector.m */; };
|
2C0F7AB93691ED8AA86FBCF0 /* MMProcessDetailInspector.m in Sources */ = {isa = PBXBuildFile; fileRef = 52E641F8A664D456518EDBA4 /* MMProcessDetailInspector.m */; };
|
||||||
2E966D96347D1DDB24E18632 /* AlertModels.swift in Sources */ = {isa = PBXBuildFile; fileRef = B657679DB7D1F39AB4911B7B /* AlertModels.swift */; };
|
2E966D96347D1DDB24E18632 /* AlertModels.swift in Sources */ = {isa = PBXBuildFile; fileRef = B657679DB7D1F39AB4911B7B /* AlertModels.swift */; };
|
||||||
3BA6709B727B1AAC6C4310B5 /* MMProcessDetailsTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7187632FF63B3B0D07FE9194 /* MMProcessDetailsTests.swift */; };
|
3BA6709B727B1AAC6C4310B5 /* MMProcessDetailsTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7187632FF63B3B0D07FE9194 /* MMProcessDetailsTests.swift */; };
|
||||||
@@ -167,6 +168,7 @@
|
|||||||
E4B4E94FB2886C60D5548A2A /* SystemTelemetryStore.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SystemTelemetryStore.swift; sourceTree = "<group>"; };
|
E4B4E94FB2886C60D5548A2A /* SystemTelemetryStore.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SystemTelemetryStore.swift; sourceTree = "<group>"; };
|
||||||
E6D9BB71DFE7384D0F0E6AA3 /* MMKernelTelemetryProvider.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MMKernelTelemetryProvider.h; sourceTree = "<group>"; };
|
E6D9BB71DFE7384D0F0E6AA3 /* MMKernelTelemetryProvider.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MMKernelTelemetryProvider.h; sourceTree = "<group>"; };
|
||||||
E9DAC4D5C09C974EE833DC7C /* MMDiskIOTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MMDiskIOTests.swift; sourceTree = "<group>"; };
|
E9DAC4D5C09C974EE833DC7C /* MMDiskIOTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MMDiskIOTests.swift; sourceTree = "<group>"; };
|
||||||
|
EA79F8A259CA3056F9B131A9 /* MMSnapshotContractTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MMSnapshotContractTests.swift; sourceTree = "<group>"; };
|
||||||
F444F4543F0A7421C8F8E636 /* MMKernelTelemetryProvider.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = MMKernelTelemetryProvider.m; sourceTree = "<group>"; };
|
F444F4543F0A7421C8F8E636 /* MMKernelTelemetryProvider.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = MMKernelTelemetryProvider.m; sourceTree = "<group>"; };
|
||||||
F5EC59DCDA5FABDA409CDBCB /* MacMonitor.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = MacMonitor.app; sourceTree = BUILT_PRODUCTS_DIR; };
|
F5EC59DCDA5FABDA409CDBCB /* MacMonitor.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = MacMonitor.app; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||||
F698B0CC35A89EDEA03F08A5 /* MMDisplayManager.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = MMDisplayManager.m; sourceTree = "<group>"; };
|
F698B0CC35A89EDEA03F08A5 /* MMDisplayManager.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = MMDisplayManager.m; sourceTree = "<group>"; };
|
||||||
@@ -438,6 +440,7 @@
|
|||||||
7187632FF63B3B0D07FE9194 /* MMProcessDetailsTests.swift */,
|
7187632FF63B3B0D07FE9194 /* MMProcessDetailsTests.swift */,
|
||||||
52471E871E5CC1E444318070 /* MMProcessTests.swift */,
|
52471E871E5CC1E444318070 /* MMProcessTests.swift */,
|
||||||
940D608A1AF80F75584E744E /* MMSMCTests.swift */,
|
940D608A1AF80F75584E744E /* MMSMCTests.swift */,
|
||||||
|
EA79F8A259CA3056F9B131A9 /* MMSnapshotContractTests.swift */,
|
||||||
78325870C7CD8312AECA2236 /* MMStorageTests.swift */,
|
78325870C7CD8312AECA2236 /* MMStorageTests.swift */,
|
||||||
);
|
);
|
||||||
path = Tests;
|
path = Tests;
|
||||||
@@ -618,6 +621,7 @@
|
|||||||
3BA6709B727B1AAC6C4310B5 /* MMProcessDetailsTests.swift in Sources */,
|
3BA6709B727B1AAC6C4310B5 /* MMProcessDetailsTests.swift in Sources */,
|
||||||
7C9AE2ADAED52E593B52F3AE /* MMProcessTests.swift in Sources */,
|
7C9AE2ADAED52E593B52F3AE /* MMProcessTests.swift in Sources */,
|
||||||
EC9FA3E4A119277B89E73F8D /* MMSMCTests.swift in Sources */,
|
EC9FA3E4A119277B89E73F8D /* MMSMCTests.swift in Sources */,
|
||||||
|
2665E9DAE13C13239857B4BB /* MMSnapshotContractTests.swift in Sources */,
|
||||||
1B15F48F6B19E9EA4556ECD5 /* MMStorageTests.swift in Sources */,
|
1B15F48F6B19E9EA4556ECD5 /* MMStorageTests.swift in Sources */,
|
||||||
9BCE74C5454F81EF80492E75 /* MacMonitorTests.swift in Sources */,
|
9BCE74C5454F81EF80492E75 /* MacMonitorTests.swift in Sources */,
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -319,18 +319,18 @@ public final class SystemTelemetryStore {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private func decodeSnapshot(_ snapshot: [String: [String: Any]]) {
|
func decodeSnapshot(_ snapshot: [String: [String: Any]]) {
|
||||||
// 1. CPU Load
|
// 1. CPU Load
|
||||||
if let cpuDict = snapshot["com.i3omb.macmonitor.telemetry.cpu"] {
|
if let cpuDict = snapshot["com.i3omb.macmonitor.telemetry.cpuload"] {
|
||||||
var m = CPULoadMetrics()
|
var m = CPULoadMetrics()
|
||||||
m.systemLoad = (cpuDict["systemLoad"] as? NSNumber)?.doubleValue ?? 0
|
m.systemLoad = (cpuDict["systemPercent"] as? NSNumber)?.doubleValue ?? 0
|
||||||
m.userLoad = (cpuDict["userLoad"] as? NSNumber)?.doubleValue ?? 0
|
m.userLoad = (cpuDict["userPercent"] as? NSNumber)?.doubleValue ?? 0
|
||||||
m.idleLoad = (cpuDict["idleLoad"] as? NSNumber)?.doubleValue ?? 100
|
m.idleLoad = (cpuDict["idlePercent"] as? NSNumber)?.doubleValue ?? 100
|
||||||
m.totalLoad = (cpuDict["totalLoad"] as? NSNumber)?.doubleValue ?? 0
|
m.totalLoad = (cpuDict["totalPercent"] as? NSNumber)?.doubleValue ?? 0
|
||||||
if let perCore = cpuDict["perCoreLoad"] as? [NSNumber] {
|
if let cores = cpuDict["cores"] as? [[String: Any]] {
|
||||||
m.perCoreLoad = perCore.map { $0.doubleValue }
|
m.perCoreLoad = cores.map { ($0["totalPercent"] as? NSNumber)?.doubleValue ?? 0 }
|
||||||
}
|
}
|
||||||
m.cpuFrequencyMHz = (cpuDict["cpuFrequencyMHz"] as? NSNumber)?.doubleValue ?? 0
|
m.cpuFrequencyMHz = ((cpuDict["frequencyHz"] as? NSNumber)?.doubleValue ?? 0) / 1e6
|
||||||
m.isThrottled = (cpuDict["isThrottled"] as? NSNumber)?.boolValue ?? false
|
m.isThrottled = (cpuDict["isThrottled"] as? NSNumber)?.boolValue ?? false
|
||||||
self.cpuLoad = m
|
self.cpuLoad = m
|
||||||
}
|
}
|
||||||
@@ -347,7 +347,8 @@ public final class SystemTelemetryStore {
|
|||||||
m.compressedBytes = (memDict["compressedBytes"] as? NSNumber)?.uint64Value ?? 0
|
m.compressedBytes = (memDict["compressedBytes"] as? NSNumber)?.uint64Value ?? 0
|
||||||
m.swapTotalBytes = (memDict["swapTotalBytes"] as? NSNumber)?.uint64Value ?? 0
|
m.swapTotalBytes = (memDict["swapTotalBytes"] as? NSNumber)?.uint64Value ?? 0
|
||||||
m.swapUsedBytes = (memDict["swapUsedBytes"] as? NSNumber)?.uint64Value ?? 0
|
m.swapUsedBytes = (memDict["swapUsedBytes"] as? NSNumber)?.uint64Value ?? 0
|
||||||
m.utilizationPercentage = (memDict["utilizationPercentage"] as? NSNumber)?.doubleValue ?? 0
|
m.utilizationPercentage = m.totalBytes > 0
|
||||||
|
? Double(m.usedBytes) / Double(m.totalBytes) * 100.0 : 0
|
||||||
m.pressureLevel = memDict["memoryPressureStatus"] as? String
|
m.pressureLevel = memDict["memoryPressureStatus"] as? String
|
||||||
?? memDict["pressureLevel"] as? String ?? "Normal"
|
?? memDict["pressureLevel"] as? String ?? "Normal"
|
||||||
self.memory = m
|
self.memory = m
|
||||||
@@ -361,11 +362,11 @@ public final class SystemTelemetryStore {
|
|||||||
return StorageVolumeItem(
|
return StorageVolumeItem(
|
||||||
mountPoint: mount,
|
mountPoint: mount,
|
||||||
volumeName: dict["volumeName"] as? String ?? mount,
|
volumeName: dict["volumeName"] as? String ?? mount,
|
||||||
fileSystem: dict["fileSystem"] as? String ?? "APFS",
|
fileSystem: dict["fsType"] as? String ?? "APFS",
|
||||||
totalBytes: (dict["totalBytes"] as? NSNumber)?.uint64Value ?? 0,
|
totalBytes: (dict["totalBytes"] as? NSNumber)?.uint64Value ?? 0,
|
||||||
freeBytes: (dict["freeBytes"] as? NSNumber)?.uint64Value ?? 0,
|
freeBytes: (dict["freeBytes"] as? NSNumber)?.uint64Value ?? 0,
|
||||||
usedBytes: (dict["usedBytes"] as? NSNumber)?.uint64Value ?? 0,
|
usedBytes: (dict["usedBytes"] as? NSNumber)?.uint64Value ?? 0,
|
||||||
usedPercentage: (dict["usedPercentage"] as? NSNumber)?.doubleValue ?? 0,
|
usedPercentage: (dict["usedPercent"] as? NSNumber)?.doubleValue ?? 0,
|
||||||
isReadOnly: (dict["isReadOnly"] as? NSNumber)?.boolValue ?? false
|
isReadOnly: (dict["isReadOnly"] as? NSNumber)?.boolValue ?? false
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -394,7 +395,7 @@ public final class SystemTelemetryStore {
|
|||||||
if let fanDict = snapshot["com.i3omb.macmonitor.telemetry.fan"],
|
if let fanDict = snapshot["com.i3omb.macmonitor.telemetry.fan"],
|
||||||
let fanArray = fanDict["fans"] as? [[String: Any]] {
|
let fanArray = fanDict["fans"] as? [[String: Any]] {
|
||||||
self.fans = fanArray.compactMap { d in
|
self.fans = fanArray.compactMap { d in
|
||||||
let idx = (d["index"] as? NSNumber)?.intValue ?? 0
|
let idx = (d["fanIndex"] as? NSNumber)?.intValue ?? 0
|
||||||
return FanTelemetryItem(
|
return FanTelemetryItem(
|
||||||
index: idx,
|
index: idx,
|
||||||
name: d["name"] as? String ?? "Fan \(idx + 1)",
|
name: d["name"] as? String ?? "Fan \(idx + 1)",
|
||||||
@@ -402,7 +403,7 @@ public final class SystemTelemetryStore {
|
|||||||
minRPM: (d["minRPM"] as? NSNumber)?.doubleValue ?? 0,
|
minRPM: (d["minRPM"] as? NSNumber)?.doubleValue ?? 0,
|
||||||
maxRPM: (d["maxRPM"] as? NSNumber)?.doubleValue ?? 0,
|
maxRPM: (d["maxRPM"] as? NSNumber)?.doubleValue ?? 0,
|
||||||
targetRPM: (d["targetRPM"] as? NSNumber)?.doubleValue ?? 0,
|
targetRPM: (d["targetRPM"] as? NSNumber)?.doubleValue ?? 0,
|
||||||
utilization: (d["utilization"] as? NSNumber)?.doubleValue ?? 0
|
utilization: (d["utilizationPercent"] as? NSNumber)?.doubleValue ?? 0
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -446,27 +447,27 @@ public final class SystemTelemetryStore {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 8. Kernel Counters
|
// 8. Kernel Counters
|
||||||
if let kDict = snapshot["com.i3omb.macmonitor.telemetry.kernel"] {
|
if let kDict = snapshot["com.i3omb.macmonitor.telemetry.kernel.counters"] {
|
||||||
var m = KernelCountersMetrics()
|
var m = KernelCountersMetrics()
|
||||||
m.contextSwitchesPerSec = (kDict["contextSwitchesPerSec"] as? NSNumber)?.doubleValue ?? 0
|
m.contextSwitchesPerSec = (kDict["contextSwitchesRate"] as? NSNumber)?.doubleValue ?? 0
|
||||||
m.syscallsPerSec = (kDict["syscallsPerSec"] as? NSNumber)?.doubleValue ?? 0
|
m.syscallsPerSec = (kDict["syscallsRate"] as? NSNumber)?.doubleValue ?? 0
|
||||||
m.pageFaultsPerSec = (kDict["pageFaultsPerSec"] as? NSNumber)?.doubleValue ?? 0
|
m.pageFaultsPerSec = (kDict["pageFaultsRate"] as? NSNumber)?.doubleValue ?? 0
|
||||||
m.cowFaultsPerSec = (kDict["cowFaultsPerSec"] as? NSNumber)?.doubleValue ?? 0
|
m.cowFaultsPerSec = (kDict["cowFaultsRate"] as? NSNumber)?.doubleValue ?? 0
|
||||||
m.zeroFillFaultsPerSec = (kDict["zeroFillFaultsPerSec"] as? NSNumber)?.doubleValue ?? 0
|
m.zeroFillFaultsPerSec = (kDict["zeroFillsRate"] as? NSNumber)?.doubleValue ?? 0
|
||||||
m.pageinsPerSec = (kDict["pageinsPerSec"] as? NSNumber)?.doubleValue ?? 0
|
m.pageinsPerSec = (kDict["pageinsRate"] as? NSNumber)?.doubleValue ?? 0
|
||||||
m.pageoutsPerSec = (kDict["pageoutsPerSec"] as? NSNumber)?.doubleValue ?? 0
|
m.pageoutsPerSec = (kDict["pageoutsRate"] as? NSNumber)?.doubleValue ?? 0
|
||||||
m.totalContextSwitches = (kDict["totalContextSwitches"] as? NSNumber)?.uint64Value ?? 0
|
m.totalContextSwitches = (kDict["cumulative_contextSwitches"] as? NSNumber)?.uint64Value ?? 0
|
||||||
m.totalSyscalls = (kDict["totalSyscalls"] as? NSNumber)?.uint64Value ?? 0
|
m.totalSyscalls = (kDict["cumulative_syscalls"] as? NSNumber)?.uint64Value ?? 0
|
||||||
m.totalPageFaults = (kDict["totalPageFaults"] as? NSNumber)?.uint64Value ?? 0
|
m.totalPageFaults = (kDict["cumulative_pageFaults"] as? NSNumber)?.uint64Value ?? 0
|
||||||
self.kernelCounters = m
|
self.kernelCounters = m
|
||||||
}
|
}
|
||||||
|
|
||||||
// 9. System Load Average & Mach Factor
|
// 9. System Load Average & Mach Factor
|
||||||
if let loadDict = snapshot["com.i3omb.macmonitor.telemetry.systemload"] {
|
if let loadDict = snapshot["com.i3omb.macmonitor.telemetry.system.load"] {
|
||||||
var m = SystemLoadMetrics()
|
var m = SystemLoadMetrics()
|
||||||
m.load1Min = (loadDict["load1Min"] as? NSNumber)?.doubleValue ?? 0
|
m.load1Min = (loadDict["load1m"] as? NSNumber)?.doubleValue ?? 0
|
||||||
m.load5Min = (loadDict["load5Min"] as? NSNumber)?.doubleValue ?? 0
|
m.load5Min = (loadDict["load5m"] as? NSNumber)?.doubleValue ?? 0
|
||||||
m.load15Min = (loadDict["load15Min"] as? NSNumber)?.doubleValue ?? 0
|
m.load15Min = (loadDict["load15m"] as? NSNumber)?.doubleValue ?? 0
|
||||||
m.taskCount = (loadDict["taskCount"] as? NSNumber)?.intValue ?? 0
|
m.taskCount = (loadDict["taskCount"] as? NSNumber)?.intValue ?? 0
|
||||||
m.threadCount = (loadDict["threadCount"] as? NSNumber)?.intValue ?? 0
|
m.threadCount = (loadDict["threadCount"] as? NSNumber)?.intValue ?? 0
|
||||||
m.machFactor = (loadDict["machFactor"] as? NSNumber)?.doubleValue ?? 0
|
m.machFactor = (loadDict["machFactor"] as? NSNumber)?.doubleValue ?? 0
|
||||||
@@ -474,18 +475,18 @@ public final class SystemTelemetryStore {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 10. Disk I/O & IOPS
|
// 10. Disk I/O & IOPS
|
||||||
if let diskDict = snapshot["com.i3omb.macmonitor.telemetry.diskio"],
|
if let diskDict = snapshot["com.i3omb.macmonitor.telemetry.storage.io"],
|
||||||
let disks = diskDict["disks"] as? [[String: Any]] {
|
let disks = diskDict["disks"] as? [[String: Any]] {
|
||||||
self.diskIO = disks.compactMap { d in
|
self.diskIO = disks.compactMap { d in
|
||||||
guard let bsd = d["bsdName"] as? String else { return nil }
|
guard let bsd = d["bsdName"] as? String else { return nil }
|
||||||
return DiskIOItem(
|
return DiskIOItem(
|
||||||
bsdName: bsd,
|
bsdName: bsd,
|
||||||
readBps: (d["readBps"] as? NSNumber)?.doubleValue ?? 0,
|
readBps: (d["readBytesPerSec"] as? NSNumber)?.doubleValue ?? 0,
|
||||||
writeBps: (d["writeBps"] as? NSNumber)?.doubleValue ?? 0,
|
writeBps: (d["writeBytesPerSec"] as? NSNumber)?.doubleValue ?? 0,
|
||||||
readIOPS: (d["readIOPS"] as? NSNumber)?.doubleValue ?? 0,
|
readIOPS: (d["readIOPS"] as? NSNumber)?.doubleValue ?? 0,
|
||||||
writeIOPS: (d["writeIOPS"] as? NSNumber)?.doubleValue ?? 0,
|
writeIOPS: (d["writeIOPS"] as? NSNumber)?.doubleValue ?? 0,
|
||||||
totalBytesRead: (d["totalBytesRead"] as? NSNumber)?.uint64Value ?? 0,
|
totalBytesRead: (d["cumulativeReadBytes"] as? NSNumber)?.uint64Value ?? 0,
|
||||||
totalBytesWritten: (d["totalBytesWritten"] as? NSNumber)?.uint64Value ?? 0
|
totalBytesWritten: (d["cumulativeWriteBytes"] as? NSNumber)?.uint64Value ?? 0
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -494,17 +495,17 @@ public final class SystemTelemetryStore {
|
|||||||
if let netDict = snapshot["com.i3omb.macmonitor.telemetry.network.bandwidth"],
|
if let netDict = snapshot["com.i3omb.macmonitor.telemetry.network.bandwidth"],
|
||||||
let ifaces = netDict["interfaces"] as? [[String: Any]] {
|
let ifaces = netDict["interfaces"] as? [[String: Any]] {
|
||||||
self.networkBandwidth = ifaces.compactMap { d in
|
self.networkBandwidth = ifaces.compactMap { d in
|
||||||
guard let name = d["interfaceName"] as? String else { return nil }
|
guard let name = d["name"] as? String else { return nil }
|
||||||
return NetworkBandwidthItem(
|
return NetworkBandwidthItem(
|
||||||
interfaceName: name,
|
interfaceName: name,
|
||||||
ipAddress: d["ipAddress"] as? String ?? "",
|
ipAddress: d["ipv4Address"] as? String ?? "",
|
||||||
isUp: (d["isUp"] as? NSNumber)?.boolValue ?? false,
|
isUp: (d["isUp"] as? NSNumber)?.boolValue ?? false,
|
||||||
downloadBps: (d["downloadBps"] as? NSNumber)?.doubleValue ?? 0,
|
downloadBps: (d["downloadBytesPerSec"] as? NSNumber)?.doubleValue ?? 0,
|
||||||
uploadBps: (d["uploadBps"] as? NSNumber)?.doubleValue ?? 0,
|
uploadBps: (d["uploadBytesPerSec"] as? NSNumber)?.doubleValue ?? 0,
|
||||||
downloadPps: (d["downloadPps"] as? NSNumber)?.doubleValue ?? 0,
|
downloadPps: (d["downloadPacketsPerSec"] as? NSNumber)?.doubleValue ?? 0,
|
||||||
uploadPps: (d["uploadPps"] as? NSNumber)?.doubleValue ?? 0,
|
uploadPps: (d["uploadPacketsPerSec"] as? NSNumber)?.doubleValue ?? 0,
|
||||||
totalBytesIn: (d["totalBytesIn"] as? NSNumber)?.uint64Value ?? 0,
|
totalBytesIn: (d["cumulativeInBytes"] as? NSNumber)?.uint64Value ?? 0,
|
||||||
totalBytesOut: (d["totalBytesOut"] as? NSNumber)?.uint64Value ?? 0
|
totalBytesOut: (d["cumulativeOutBytes"] as? NSNumber)?.uint64Value ?? 0
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -539,8 +540,8 @@ public final class SystemTelemetryStore {
|
|||||||
ppid: (d["ppid"] as? NSNumber)?.int32Value ?? 0,
|
ppid: (d["ppid"] as? NSNumber)?.int32Value ?? 0,
|
||||||
name: d["name"] as? String ?? "Unknown",
|
name: d["name"] as? String ?? "Unknown",
|
||||||
cpuPercent: (d["cpuPercent"] as? NSNumber)?.doubleValue ?? 0,
|
cpuPercent: (d["cpuPercent"] as? NSNumber)?.doubleValue ?? 0,
|
||||||
residentSize: (d["residentSize"] as? NSNumber)?.uint64Value ?? 0,
|
residentSize: (d["residentBytes"] as? NSNumber)?.uint64Value ?? 0,
|
||||||
virtualSize: (d["virtualSize"] as? NSNumber)?.uint64Value ?? 0,
|
virtualSize: (d["virtualBytes"] as? NSNumber)?.uint64Value ?? 0,
|
||||||
threadCount: (d["threadCount"] as? NSNumber)?.int32Value ?? 1,
|
threadCount: (d["threadCount"] as? NSNumber)?.int32Value ?? 1,
|
||||||
uid: (d["uid"] as? NSNumber)?.uint32Value ?? 0,
|
uid: (d["uid"] as? NSNumber)?.uint32Value ?? 0,
|
||||||
username: d["username"] as? String ?? "",
|
username: d["username"] as? String ?? "",
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ NS_ASSUME_NONNULL_BEGIN
|
|||||||
/**
|
/**
|
||||||
* Static calculator method for deterministic unit testing.
|
* Static calculator method for deterministic unit testing.
|
||||||
*/
|
*/
|
||||||
+ (NSDictionary<NSString *, id> *)calculateFanMetricsFromFanList:(NSArray<NSDictionary<NSString *, NSNumber *> *> *)fanList;
|
+ (NSDictionary<NSString *, id> *)calculateFanMetricsFromFanList:(NSArray<NSDictionary<NSString *, id> *> *)fanList;
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|
||||||
|
|||||||
@@ -63,7 +63,7 @@
|
|||||||
return (_probedFanCount >= 0) ? _probedFanCount : 0;
|
return (_probedFanCount >= 0) ? _probedFanCount : 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
+ (NSDictionary<NSString *, id> *)calculateFanMetricsFromFanList:(NSArray<NSDictionary<NSString *, NSNumber *> *> *)fanList {
|
+ (NSDictionary<NSString *, id> *)calculateFanMetricsFromFanList:(NSArray<NSDictionary<NSString *, id> *> *)fanList {
|
||||||
if (!fanList || fanList.count == 0) {
|
if (!fanList || fanList.count == 0) {
|
||||||
return @{
|
return @{
|
||||||
@"fanCount": @(0),
|
@"fanCount": @(0),
|
||||||
@@ -78,7 +78,7 @@
|
|||||||
double peakRPM = 0.0;
|
double peakRPM = 0.0;
|
||||||
|
|
||||||
for (NSUInteger i = 0; i < fanList.count; i++) {
|
for (NSUInteger i = 0; i < fanList.count; i++) {
|
||||||
NSDictionary<NSString *, NSNumber *> *raw = fanList[i];
|
NSDictionary<NSString *, id> *raw = fanList[i];
|
||||||
double current = [raw[@"currentRPM"] doubleValue];
|
double current = [raw[@"currentRPM"] doubleValue];
|
||||||
double min = [raw[@"minRPM"] doubleValue];
|
double min = [raw[@"minRPM"] doubleValue];
|
||||||
double max = [raw[@"maxRPM"] doubleValue];
|
double max = [raw[@"maxRPM"] doubleValue];
|
||||||
@@ -96,6 +96,7 @@
|
|||||||
|
|
||||||
[resultList addObject:@{
|
[resultList addObject:@{
|
||||||
@"fanIndex": @(i),
|
@"fanIndex": @(i),
|
||||||
|
@"name": raw[@"name"] ?: [NSString stringWithFormat:@"Fan %lu", (unsigned long)(i + 1)],
|
||||||
@"currentRPM": @(current),
|
@"currentRPM": @(current),
|
||||||
@"minRPM": @(min),
|
@"minRPM": @(min),
|
||||||
@"maxRPM": @(max),
|
@"maxRPM": @(max),
|
||||||
@@ -120,20 +121,31 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
NSInteger count = [self fanCount];
|
NSInteger count = [self fanCount];
|
||||||
NSMutableArray<NSDictionary<NSString *, NSNumber *> *> *rawList = [NSMutableArray arrayWithCapacity:count];
|
NSMutableArray<NSDictionary<NSString *, id> *> *rawList = [NSMutableArray arrayWithCapacity:count];
|
||||||
|
|
||||||
for (NSInteger i = 0; i < count; i++) {
|
for (NSInteger i = 0; i < count; i++) {
|
||||||
NSString *acKey = [NSString stringWithFormat:@"F%ldAc", (long)i];
|
NSString *acKey = [NSString stringWithFormat:@"F%ldAc", (long)i];
|
||||||
NSString *mnKey = [NSString stringWithFormat:@"F%ldMn", (long)i];
|
NSString *mnKey = [NSString stringWithFormat:@"F%ldMn", (long)i];
|
||||||
NSString *mxKey = [NSString stringWithFormat:@"F%ldMx", (long)i];
|
NSString *mxKey = [NSString stringWithFormat:@"F%ldMx", (long)i];
|
||||||
NSString *tgKey = [NSString stringWithFormat:@"F%ldTg", (long)i];
|
NSString *tgKey = [NSString stringWithFormat:@"F%ldTg", (long)i];
|
||||||
|
NSString *idKey = [NSString stringWithFormat:@"F%ldID", (long)i];
|
||||||
|
|
||||||
NSNumber *ac = [_smcClient readNumericValueForKey:acKey error:nil] ?: @(0.0);
|
NSNumber *ac = [_smcClient readNumericValueForKey:acKey error:nil] ?: @(0.0);
|
||||||
NSNumber *mn = [_smcClient readNumericValueForKey:mnKey error:nil] ?: @(0.0);
|
NSNumber *mn = [_smcClient readNumericValueForKey:mnKey error:nil] ?: @(0.0);
|
||||||
NSNumber *mx = [_smcClient readNumericValueForKey:mxKey error:nil] ?: @(6000.0);
|
NSNumber *mx = [_smcClient readNumericValueForKey:mxKey error:nil] ?: @(6000.0);
|
||||||
NSNumber *tg = [_smcClient readNumericValueForKey:tgKey error:nil] ?: ac;
|
NSNumber *tg = [_smcClient readNumericValueForKey:tgKey error:nil] ?: ac;
|
||||||
|
|
||||||
|
NSString *fanName = [NSString stringWithFormat:@"Fan %ld", (long)(i + 1)];
|
||||||
|
NSData *idData = [_smcClient readBytesForKey:idKey error:nil];
|
||||||
|
if (idData.length > 0) {
|
||||||
|
NSString *raw = [[NSString alloc] initWithData:idData encoding:NSASCIIStringEncoding];
|
||||||
|
NSString *trimmed = [raw stringByTrimmingCharactersInSet:
|
||||||
|
[NSCharacterSet characterSetWithCharactersInString:@"\0 "]];
|
||||||
|
if (trimmed.length > 0) fanName = trimmed;
|
||||||
|
}
|
||||||
|
|
||||||
[rawList addObject:@{
|
[rawList addObject:@{
|
||||||
|
@"name": fanName,
|
||||||
@"currentRPM": ac,
|
@"currentRPM": ac,
|
||||||
@"minRPM": mn,
|
@"minRPM": mn,
|
||||||
@"maxRPM": mx,
|
@"maxRPM": mx,
|
||||||
|
|||||||
@@ -21,7 +21,8 @@ NS_ASSUME_NONNULL_BEGIN
|
|||||||
+ (NSDictionary<NSString *, id> *)calculateBandwidthMetricsWithCurrentSnapshots:(NSDictionary<NSString *, NSDictionary<NSString *, NSNumber *> *> *)current
|
+ (NSDictionary<NSString *, id> *)calculateBandwidthMetricsWithCurrentSnapshots:(NSDictionary<NSString *, NSDictionary<NSString *, NSNumber *> *> *)current
|
||||||
previousSnapshots:(nullable NSDictionary<NSString *, NSDictionary<NSString *, NSNumber *> *> *)previous
|
previousSnapshots:(nullable NSDictionary<NSString *, NSDictionary<NSString *, NSNumber *> *> *)previous
|
||||||
timeDelta:(NSTimeInterval)timeDelta
|
timeDelta:(NSTimeInterval)timeDelta
|
||||||
ipAddresses:(nullable NSDictionary<NSString *, NSString *> *)ipAddresses;
|
ipAddresses:(nullable NSDictionary<NSString *, NSString *> *)ipAddresses
|
||||||
|
interfaceUpFlags:(nullable NSDictionary<NSString *, NSNumber *> *)interfaceUpFlags;
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|
||||||
|
|||||||
@@ -44,7 +44,8 @@
|
|||||||
+ (NSDictionary<NSString *, id> *)calculateBandwidthMetricsWithCurrentSnapshots:(NSDictionary<NSString *, NSDictionary<NSString *, NSNumber *> *> *)current
|
+ (NSDictionary<NSString *, id> *)calculateBandwidthMetricsWithCurrentSnapshots:(NSDictionary<NSString *, NSDictionary<NSString *, NSNumber *> *> *)current
|
||||||
previousSnapshots:(nullable NSDictionary<NSString *, NSDictionary<NSString *, NSNumber *> *> *)previous
|
previousSnapshots:(nullable NSDictionary<NSString *, NSDictionary<NSString *, NSNumber *> *> *)previous
|
||||||
timeDelta:(NSTimeInterval)timeDelta
|
timeDelta:(NSTimeInterval)timeDelta
|
||||||
ipAddresses:(nullable NSDictionary<NSString *, NSString *> *)ipAddresses {
|
ipAddresses:(nullable NSDictionary<NSString *, NSString *> *)ipAddresses
|
||||||
|
interfaceUpFlags:(nullable NSDictionary<NSString *, NSNumber *> *)interfaceUpFlags {
|
||||||
double totalDownBps = 0.0;
|
double totalDownBps = 0.0;
|
||||||
double totalUpBps = 0.0;
|
double totalUpBps = 0.0;
|
||||||
double totalDownPps = 0.0;
|
double totalDownPps = 0.0;
|
||||||
@@ -99,9 +100,11 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
NSString *ip = ipAddresses[name] ?: @"";
|
NSString *ip = ipAddresses[name] ?: @"";
|
||||||
|
BOOL isUp = interfaceUpFlags[name] ? [interfaceUpFlags[name] boolValue] : NO;
|
||||||
|
|
||||||
[interfaces addObject:@{
|
[interfaces addObject:@{
|
||||||
@"name": name,
|
@"name": name,
|
||||||
|
@"isUp": @(isUp),
|
||||||
@"downloadBytesPerSec": @(downBps),
|
@"downloadBytesPerSec": @(downBps),
|
||||||
@"uploadBytesPerSec": @(upBps),
|
@"uploadBytesPerSec": @(upBps),
|
||||||
@"downloadPacketsPerSec": @(downPps),
|
@"downloadPacketsPerSec": @(downPps),
|
||||||
@@ -171,16 +174,18 @@
|
|||||||
if (buf) free(buf);
|
if (buf) free(buf);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 2. Query IPv4 addresses via getifaddrs
|
// 2. Query IPv4 addresses and interface flags via getifaddrs
|
||||||
NSMutableDictionary<NSString *, NSString *> *ipDict = [NSMutableDictionary dictionary];
|
NSMutableDictionary<NSString *, NSString *> *ipDict = [NSMutableDictionary dictionary];
|
||||||
|
NSMutableDictionary<NSString *, NSNumber *> *upDict = [NSMutableDictionary dictionary];
|
||||||
struct ifaddrs *ifap = NULL;
|
struct ifaddrs *ifap = NULL;
|
||||||
if (getifaddrs(&ifap) == 0) {
|
if (getifaddrs(&ifap) == 0) {
|
||||||
for (struct ifaddrs *ifa = ifap; ifa != NULL; ifa = ifa->ifa_next) {
|
for (struct ifaddrs *ifa = ifap; ifa != NULL; ifa = ifa->ifa_next) {
|
||||||
|
NSString *name = [NSString stringWithUTF8String:ifa->ifa_name];
|
||||||
|
upDict[name] = @((ifa->ifa_flags & IFF_UP) != 0);
|
||||||
if (ifa->ifa_addr && ifa->ifa_addr->sa_family == AF_INET) {
|
if (ifa->ifa_addr && ifa->ifa_addr->sa_family == AF_INET) {
|
||||||
char ipBuf[INET_ADDRSTRLEN] = {0};
|
char ipBuf[INET_ADDRSTRLEN] = {0};
|
||||||
struct sockaddr_in *sin = (struct sockaddr_in *)ifa->ifa_addr;
|
struct sockaddr_in *sin = (struct sockaddr_in *)ifa->ifa_addr;
|
||||||
if (inet_ntop(AF_INET, &sin->sin_addr, ipBuf, sizeof(ipBuf))) {
|
if (inet_ntop(AF_INET, &sin->sin_addr, ipBuf, sizeof(ipBuf))) {
|
||||||
NSString *name = [NSString stringWithUTF8String:ifa->ifa_name];
|
|
||||||
ipDict[name] = [NSString stringWithUTF8String:ipBuf];
|
ipDict[name] = [NSString stringWithUTF8String:ipBuf];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -205,7 +210,8 @@
|
|||||||
return [MMNetworkBandwidthProvider calculateBandwidthMetricsWithCurrentSnapshots:currentSnapshots
|
return [MMNetworkBandwidthProvider calculateBandwidthMetricsWithCurrentSnapshots:currentSnapshots
|
||||||
previousSnapshots:prev
|
previousSnapshots:prev
|
||||||
timeDelta:timeDelta
|
timeDelta:timeDelta
|
||||||
ipAddresses:ipDict];
|
ipAddresses:ipDict
|
||||||
|
interfaceUpFlags:upDict];
|
||||||
}
|
}
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
#import "MMProcessTelemetryProvider.h"
|
#import "MMProcessTelemetryProvider.h"
|
||||||
#import <libproc.h>
|
#import <libproc.h>
|
||||||
#import <sys/proc_info.h>
|
#import <sys/proc_info.h>
|
||||||
|
#import <sys/proc.h>
|
||||||
#import <pwd.h>
|
#import <pwd.h>
|
||||||
#import <signal.h>
|
#import <signal.h>
|
||||||
#import <mach/mach_time.h>
|
#import <mach/mach_time.h>
|
||||||
@@ -127,7 +128,9 @@
|
|||||||
@"residentBytes": @(tai.ptinfo.pti_resident_size),
|
@"residentBytes": @(tai.ptinfo.pti_resident_size),
|
||||||
@"virtualBytes": @(tai.ptinfo.pti_virtual_size),
|
@"virtualBytes": @(tai.ptinfo.pti_virtual_size),
|
||||||
@"threadCount": @(tai.ptinfo.pti_threadnum),
|
@"threadCount": @(tai.ptinfo.pti_threadnum),
|
||||||
@"runningThreads": @(tai.ptinfo.pti_numrunning)
|
@"runningThreads": @(tai.ptinfo.pti_numrunning),
|
||||||
|
@"isStopped": @(tai.pbsd.pbi_status == SSTOP),
|
||||||
|
@"isZombie": @(tai.pbsd.pbi_status == SZOMB)
|
||||||
}];
|
}];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -49,7 +49,8 @@ final class MMNetworkBandwidthTests: XCTestCase {
|
|||||||
withCurrentSnapshots: curr,
|
withCurrentSnapshots: curr,
|
||||||
previousSnapshots: prev,
|
previousSnapshots: prev,
|
||||||
timeDelta: timeDelta,
|
timeDelta: timeDelta,
|
||||||
ipAddresses: ipDict
|
ipAddresses: ipDict,
|
||||||
|
interfaceUpFlags: ["en0": true, "lo0": true]
|
||||||
)
|
)
|
||||||
|
|
||||||
let totalDownBps = metrics["totalDownloadBytesPerSec"] as? Double ?? 0
|
let totalDownBps = metrics["totalDownloadBytesPerSec"] as? Double ?? 0
|
||||||
|
|||||||
@@ -0,0 +1,162 @@
|
|||||||
|
import XCTest
|
||||||
|
@testable import MacMonitor
|
||||||
|
|
||||||
|
/// Snapshot contract conformance tests (Issues #30 / #31).
|
||||||
|
/// Feeds synthetic dictionaries keyed exactly as the Objective-C providers emit them
|
||||||
|
/// through `SystemTelemetryStore.decodeSnapshot` and asserts the decoded Swift models
|
||||||
|
/// populate — guarding against provider-identifier and field-name drift.
|
||||||
|
@MainActor
|
||||||
|
final class MMSnapshotContractTests: XCTestCase {
|
||||||
|
|
||||||
|
private var store: SystemTelemetryStore { SystemTelemetryStore.shared }
|
||||||
|
|
||||||
|
// MARK: - Provider identifier coverage (#30)
|
||||||
|
|
||||||
|
func testEmittedProviderIdentifiersAreDecoded() {
|
||||||
|
// The four identifiers that previously never reached the decoder.
|
||||||
|
let snapshot: [String: [String: Any]] = [
|
||||||
|
"com.i3omb.macmonitor.telemetry.cpuload": [
|
||||||
|
"userPercent": 25.0, "systemPercent": 15.0,
|
||||||
|
"idlePercent": 60.0, "totalPercent": 40.0,
|
||||||
|
"cores": [["totalPercent": 10.0], ["totalPercent": 90.0]],
|
||||||
|
"frequencyHz": 2.4e9, "isThrottled": false
|
||||||
|
],
|
||||||
|
"com.i3omb.macmonitor.telemetry.kernel.counters": [
|
||||||
|
"contextSwitchesRate": 1234.5, "syscallsRate": 9876.5,
|
||||||
|
"pageFaultsRate": 42.0, "cowFaultsRate": 3.0,
|
||||||
|
"zeroFillsRate": 7.0, "pageinsRate": 1.0, "pageoutsRate": 0.5,
|
||||||
|
"cumulative_contextSwitches": 999_999 as UInt64,
|
||||||
|
"cumulative_syscalls": 888_888 as UInt64,
|
||||||
|
"cumulative_pageFaults": 777_777 as UInt64
|
||||||
|
],
|
||||||
|
"com.i3omb.macmonitor.telemetry.system.load": [
|
||||||
|
"load1m": 1.5, "load5m": 2.0, "load15m": 2.5,
|
||||||
|
"taskCount": 400, "threadCount": 2000, "machFactor": 0.75
|
||||||
|
],
|
||||||
|
"com.i3omb.macmonitor.telemetry.storage.io": [
|
||||||
|
"disks": [[
|
||||||
|
"bsdName": "disk0",
|
||||||
|
"readBytesPerSec": 1_048_576.0, "writeBytesPerSec": 524_288.0,
|
||||||
|
"readIOPS": 120.0, "writeIOPS": 60.0,
|
||||||
|
"cumulativeReadBytes": 9_999_999 as UInt64,
|
||||||
|
"cumulativeWriteBytes": 8_888_888 as UInt64
|
||||||
|
]]
|
||||||
|
]
|
||||||
|
]
|
||||||
|
|
||||||
|
store.decodeSnapshot(snapshot)
|
||||||
|
|
||||||
|
XCTAssertEqual(store.cpuLoad.userLoad, 25.0, accuracy: 0.001)
|
||||||
|
XCTAssertEqual(store.cpuLoad.systemLoad, 15.0, accuracy: 0.001)
|
||||||
|
XCTAssertEqual(store.cpuLoad.totalLoad, 40.0, accuracy: 0.001)
|
||||||
|
XCTAssertEqual(store.cpuLoad.perCoreLoad, [10.0, 90.0])
|
||||||
|
XCTAssertEqual(store.cpuLoad.cpuFrequencyMHz, 2400.0, accuracy: 0.001)
|
||||||
|
|
||||||
|
XCTAssertEqual(store.kernelCounters.contextSwitchesPerSec, 1234.5, accuracy: 0.001)
|
||||||
|
XCTAssertEqual(store.kernelCounters.syscallsPerSec, 9876.5, accuracy: 0.001)
|
||||||
|
XCTAssertEqual(store.kernelCounters.totalContextSwitches, 999_999)
|
||||||
|
XCTAssertEqual(store.kernelCounters.totalSyscalls, 888_888)
|
||||||
|
XCTAssertEqual(store.kernelCounters.totalPageFaults, 777_777)
|
||||||
|
|
||||||
|
XCTAssertEqual(store.systemLoad.load1Min, 1.5, accuracy: 0.001)
|
||||||
|
XCTAssertEqual(store.systemLoad.load5Min, 2.0, accuracy: 0.001)
|
||||||
|
XCTAssertEqual(store.systemLoad.load15Min, 2.5, accuracy: 0.001)
|
||||||
|
XCTAssertEqual(store.systemLoad.taskCount, 400)
|
||||||
|
XCTAssertEqual(store.systemLoad.threadCount, 2000)
|
||||||
|
|
||||||
|
XCTAssertEqual(store.diskIO.count, 1)
|
||||||
|
XCTAssertEqual(store.diskIO[0].bsdName, "disk0")
|
||||||
|
XCTAssertEqual(store.diskIO[0].readBps, 1_048_576.0, accuracy: 0.001)
|
||||||
|
XCTAssertEqual(store.diskIO[0].totalBytesRead, 9_999_999)
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: - Field-name drift (#31)
|
||||||
|
|
||||||
|
func testMemoryUtilizationComputedFromUsedOverTotal() {
|
||||||
|
store.decodeSnapshot([
|
||||||
|
"com.i3omb.macmonitor.telemetry.memory": [
|
||||||
|
"totalBytes": 16_000_000_000 as UInt64,
|
||||||
|
"usedBytes": 8_000_000_000 as UInt64,
|
||||||
|
"freeBytes": 8_000_000_000 as UInt64,
|
||||||
|
"memoryPressureStatus": "Normal"
|
||||||
|
]
|
||||||
|
])
|
||||||
|
XCTAssertEqual(store.memory.utilizationPercentage, 50.0, accuracy: 0.001)
|
||||||
|
}
|
||||||
|
|
||||||
|
func testStorageVolumeEmitsUsedPercentAndFSType() {
|
||||||
|
store.decodeSnapshot([
|
||||||
|
"com.i3omb.macmonitor.telemetry.storage": [
|
||||||
|
"volumes": [[
|
||||||
|
"volumeName": "Macintosh HD", "mountPoint": "/",
|
||||||
|
"devicePath": "/dev/disk1s1", "fsType": "apfs",
|
||||||
|
"totalBytes": 500_000_000_000 as UInt64,
|
||||||
|
"usedBytes": 250_000_000_000 as UInt64,
|
||||||
|
"freeBytes": 250_000_000_000 as UInt64,
|
||||||
|
"usedPercent": 50.0, "isReadOnly": false
|
||||||
|
]]
|
||||||
|
]
|
||||||
|
])
|
||||||
|
XCTAssertEqual(store.storageVolumes.count, 1)
|
||||||
|
XCTAssertEqual(store.storageVolumes[0].usedPercentage, 50.0, accuracy: 0.001)
|
||||||
|
XCTAssertEqual(store.storageVolumes[0].fileSystem, "apfs")
|
||||||
|
}
|
||||||
|
|
||||||
|
func testNetworkInterfaceEmittedKeys() {
|
||||||
|
store.decodeSnapshot([
|
||||||
|
"com.i3omb.macmonitor.telemetry.network.bandwidth": [
|
||||||
|
"interfaces": [[
|
||||||
|
"name": "en0", "isUp": true, "ipv4Address": "192.168.1.10",
|
||||||
|
"downloadBytesPerSec": 5_000.0, "uploadBytesPerSec": 1_000.0,
|
||||||
|
"downloadPacketsPerSec": 40.0, "uploadPacketsPerSec": 20.0,
|
||||||
|
"cumulativeInBytes": 10_000_000 as UInt64,
|
||||||
|
"cumulativeOutBytes": 5_000_000 as UInt64
|
||||||
|
]]
|
||||||
|
]
|
||||||
|
])
|
||||||
|
XCTAssertEqual(store.networkBandwidth.count, 1)
|
||||||
|
let iface = store.networkBandwidth[0]
|
||||||
|
XCTAssertEqual(iface.interfaceName, "en0")
|
||||||
|
XCTAssertTrue(iface.isUp)
|
||||||
|
XCTAssertEqual(iface.ipAddress, "192.168.1.10")
|
||||||
|
XCTAssertEqual(iface.downloadBps, 5_000.0, accuracy: 0.001)
|
||||||
|
XCTAssertEqual(iface.totalBytesIn, 10_000_000)
|
||||||
|
}
|
||||||
|
|
||||||
|
func testProcessResidentAndVirtualBytesDecode() {
|
||||||
|
store.decodeSnapshot([
|
||||||
|
"com.i3omb.macmonitor.telemetry.process": [
|
||||||
|
"processes": [[
|
||||||
|
"pid": 42, "ppid": 1, "name": "testproc",
|
||||||
|
"cpuPercent": 3.5,
|
||||||
|
"residentBytes": 100_000_000 as UInt64,
|
||||||
|
"virtualBytes": 400_000_000 as UInt64,
|
||||||
|
"threadCount": 5, "uid": 501, "username": "gordon",
|
||||||
|
"isStopped": false, "isZombie": false
|
||||||
|
]]
|
||||||
|
]
|
||||||
|
])
|
||||||
|
XCTAssertEqual(store.processes.count, 1)
|
||||||
|
XCTAssertEqual(store.processes[0].residentSize, 100_000_000)
|
||||||
|
XCTAssertEqual(store.processes[0].virtualSize, 400_000_000)
|
||||||
|
XCTAssertFalse(store.processes[0].isStopped)
|
||||||
|
XCTAssertFalse(store.processes[0].isZombie)
|
||||||
|
}
|
||||||
|
|
||||||
|
func testFanEmittedKeysDecode() {
|
||||||
|
store.decodeSnapshot([
|
||||||
|
"com.i3omb.macmonitor.telemetry.fan": [
|
||||||
|
"fans": [[
|
||||||
|
"fanIndex": 0, "name": "Exhaust",
|
||||||
|
"currentRPM": 2000.0, "minRPM": 1200.0,
|
||||||
|
"maxRPM": 6000.0, "targetRPM": 2000.0,
|
||||||
|
"utilizationPercent": 16.67
|
||||||
|
]]
|
||||||
|
]
|
||||||
|
])
|
||||||
|
XCTAssertEqual(store.fans.count, 1)
|
||||||
|
XCTAssertEqual(store.fans[0].index, 0)
|
||||||
|
XCTAssertEqual(store.fans[0].name, "Exhaust")
|
||||||
|
XCTAssertEqual(store.fans[0].utilization, 16.67, accuracy: 0.01)
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user