191 lines
6.9 KiB
Swift
191 lines
6.9 KiB
Swift
import AppKit
|
|
import CodexBarCore
|
|
import Foundation
|
|
import Testing
|
|
@testable import CodexBar
|
|
|
|
/// Regression coverage for battery drain caused by fallback-provider animation.
|
|
/// See GitHub issues #269, #139.
|
|
@MainActor
|
|
@Suite(.serialized)
|
|
struct BatteryDrainDiagnosticTests {
|
|
private func ensureAppKitInitialized() {
|
|
_ = NSApplication.shared
|
|
}
|
|
|
|
private func makeStatusBarForTesting() -> NSStatusBar {
|
|
// Use the real system status bar in tests. Creating standalone NSStatusBar instances
|
|
// has caused AppKit teardown crashes under swiftpm-testing-helper.
|
|
.system
|
|
}
|
|
|
|
@Test
|
|
func `Fallback provider should not animate when all providers are disabled`() {
|
|
self.ensureAppKitInitialized()
|
|
|
|
let settings = SettingsStore(
|
|
configStore: testConfigStore(suiteName: "BatteryDrain-AllDisabled"),
|
|
zaiTokenStore: NoopZaiTokenStore(),
|
|
syntheticTokenStore: NoopSyntheticTokenStore())
|
|
|
|
settings.statusChecksEnabled = false
|
|
settings.refreshFrequency = .manual
|
|
settings.mergeIcons = false
|
|
|
|
let registry = ProviderRegistry.shared
|
|
for provider in UsageProvider.allCases {
|
|
if let meta = registry.metadata[provider] {
|
|
settings.setProviderEnabled(provider: provider, metadata: meta, enabled: false)
|
|
}
|
|
}
|
|
|
|
let fetcher = UsageFetcher()
|
|
let store = UsageStore(
|
|
fetcher: fetcher,
|
|
browserDetection: BrowserDetection(cacheTTL: 0),
|
|
settings: settings)
|
|
|
|
let controller = StatusItemController(
|
|
store: store,
|
|
settings: settings,
|
|
account: fetcher.loadAccountInfo(),
|
|
updater: DisabledUpdaterController(),
|
|
preferencesSelection: PreferencesSelection(),
|
|
statusBar: self.makeStatusBarForTesting())
|
|
defer { controller.releaseStatusItemsForTesting() }
|
|
|
|
#expect(
|
|
controller.needsMenuBarIconAnimation() == false,
|
|
"Should not animate when only fallback provider is visible")
|
|
#expect(
|
|
controller.animationDriver == nil,
|
|
"Animation driver should not start for fallback provider")
|
|
}
|
|
|
|
@Test
|
|
func `Enabled provider with data should not animate`() {
|
|
self.ensureAppKitInitialized()
|
|
|
|
let settings = SettingsStore(
|
|
configStore: testConfigStore(suiteName: "BatteryDrain-HasData"),
|
|
zaiTokenStore: NoopZaiTokenStore(),
|
|
syntheticTokenStore: NoopSyntheticTokenStore())
|
|
|
|
settings.statusChecksEnabled = false
|
|
settings.refreshFrequency = .manual
|
|
settings.mergeIcons = true
|
|
settings.selectedMenuProvider = .codex
|
|
|
|
let registry = ProviderRegistry.shared
|
|
if let meta = registry.metadata[.codex] {
|
|
settings.setProviderEnabled(provider: .codex, metadata: meta, enabled: true)
|
|
}
|
|
|
|
let fetcher = UsageFetcher()
|
|
let store = UsageStore(
|
|
fetcher: fetcher,
|
|
browserDetection: BrowserDetection(cacheTTL: 0),
|
|
settings: settings)
|
|
|
|
let snapshot = UsageSnapshot(
|
|
primary: RateWindow(usedPercent: 50, windowMinutes: nil, resetsAt: nil, resetDescription: nil),
|
|
secondary: RateWindow(usedPercent: 30, windowMinutes: nil, resetsAt: nil, resetDescription: nil),
|
|
updatedAt: Date())
|
|
store._setSnapshotForTesting(snapshot, provider: .codex)
|
|
|
|
let controller = StatusItemController(
|
|
store: store,
|
|
settings: settings,
|
|
account: fetcher.loadAccountInfo(),
|
|
updater: DisabledUpdaterController(),
|
|
preferencesSelection: PreferencesSelection(),
|
|
statusBar: self.makeStatusBarForTesting())
|
|
defer { controller.releaseStatusItemsForTesting() }
|
|
|
|
#expect(
|
|
controller.needsMenuBarIconAnimation() == false,
|
|
"Should not animate when provider has data")
|
|
#expect(
|
|
controller.animationDriver == nil,
|
|
"Animation driver should be nil when data is present")
|
|
}
|
|
|
|
@Test
|
|
func `Enabled provider without data should animate`() {
|
|
self.ensureAppKitInitialized()
|
|
|
|
let settings = SettingsStore(
|
|
configStore: testConfigStore(suiteName: "BatteryDrain-NoData"),
|
|
zaiTokenStore: NoopZaiTokenStore(),
|
|
syntheticTokenStore: NoopSyntheticTokenStore())
|
|
|
|
settings.statusChecksEnabled = false
|
|
settings.refreshFrequency = .manual
|
|
settings.mergeIcons = false
|
|
|
|
let registry = ProviderRegistry.shared
|
|
if let meta = registry.metadata[.codex] {
|
|
settings.setProviderEnabled(provider: .codex, metadata: meta, enabled: true)
|
|
}
|
|
|
|
let fetcher = UsageFetcher()
|
|
let store = UsageStore(
|
|
fetcher: fetcher,
|
|
browserDetection: BrowserDetection(cacheTTL: 0),
|
|
settings: settings)
|
|
|
|
let controller = StatusItemController(
|
|
store: store,
|
|
settings: settings,
|
|
account: fetcher.loadAccountInfo(),
|
|
updater: DisabledUpdaterController(),
|
|
preferencesSelection: PreferencesSelection(),
|
|
statusBar: self.makeStatusBarForTesting())
|
|
defer { controller.releaseStatusItemsForTesting() }
|
|
|
|
#expect(
|
|
controller.needsMenuBarIconAnimation() == true,
|
|
"Should animate when enabled provider has no data")
|
|
}
|
|
|
|
@Test
|
|
func `Enabled provider with error should not animate`() {
|
|
self.ensureAppKitInitialized()
|
|
|
|
let settings = SettingsStore(
|
|
configStore: testConfigStore(suiteName: "BatteryDrain-ErrorStops"),
|
|
zaiTokenStore: NoopZaiTokenStore(),
|
|
syntheticTokenStore: NoopSyntheticTokenStore())
|
|
|
|
settings.statusChecksEnabled = false
|
|
settings.refreshFrequency = .manual
|
|
settings.mergeIcons = false
|
|
|
|
let registry = ProviderRegistry.shared
|
|
if let meta = registry.metadata[.codex] {
|
|
settings.setProviderEnabled(provider: .codex, metadata: meta, enabled: true)
|
|
}
|
|
|
|
let fetcher = UsageFetcher()
|
|
let store = UsageStore(
|
|
fetcher: fetcher,
|
|
browserDetection: BrowserDetection(cacheTTL: 0),
|
|
settings: settings)
|
|
store._setErrorForTesting("simulated Codex RPC timeout", provider: .codex)
|
|
|
|
let controller = StatusItemController(
|
|
store: store,
|
|
settings: settings,
|
|
account: fetcher.loadAccountInfo(),
|
|
updater: DisabledUpdaterController(),
|
|
preferencesSelection: PreferencesSelection(),
|
|
statusBar: self.makeStatusBarForTesting())
|
|
|
|
#expect(store.isStale(provider: .codex) == true)
|
|
#expect(
|
|
controller.needsMenuBarIconAnimation() == false,
|
|
"Should not animate when provider has recorded an error")
|
|
#expect(controller.animationDriver == nil)
|
|
}
|
|
}
|