diff options
Diffstat (limited to 'chromium/base/power_monitor')
13 files changed, 381 insertions, 18 deletions
diff --git a/chromium/base/power_monitor/power_monitor.cc b/chromium/base/power_monitor/power_monitor.cc index 0a48f30f521..18cfacf17fa 100644 --- a/chromium/base/power_monitor/power_monitor.cc +++ b/chromium/base/power_monitor/power_monitor.cc @@ -7,8 +7,9 @@ #include <atomic> #include <utility> +#include "base/logging.h" #include "base/power_monitor/power_monitor_source.h" -#include "base/trace_event/trace_event.h" +#include "base/trace_event/base_tracing.h" namespace base { @@ -25,12 +26,8 @@ bool PowerMonitor::IsInitialized() { return GetInstance()->source_.get() != nullptr; } -bool PowerMonitor::AddObserver(PowerObserver* obs) { - PowerMonitor* power_monitor = GetInstance(); - if (!IsInitialized()) - return false; - power_monitor->observers_->AddObserver(obs); - return true; +void PowerMonitor::AddObserver(PowerObserver* obs) { + GetInstance()->observers_->AddObserver(obs); } void PowerMonitor::RemoveObserver(PowerObserver* obs) { @@ -47,15 +44,19 @@ bool PowerMonitor::IsOnBatteryPower() { } void PowerMonitor::ShutdownForTesting() { - PowerMonitor::GetInstance()->observers_->AssertEmpty(); GetInstance()->source_ = nullptr; - g_is_process_suspended.store(false); + g_is_process_suspended.store(false, std::memory_order_relaxed); } bool PowerMonitor::IsProcessSuspended() { return g_is_process_suspended.load(std::memory_order_relaxed); } +PowerObserver::DeviceThermalState PowerMonitor::GetCurrentThermalState() { + DCHECK(IsInitialized()); + return GetInstance()->source_->GetCurrentThermalState(); +} + void PowerMonitor::NotifyPowerStateChange(bool battery_in_use) { DCHECK(IsInitialized()); DVLOG(1) << "PowerStateChange: " << (battery_in_use ? "On" : "Off") @@ -82,6 +83,15 @@ void PowerMonitor::NotifyResume() { GetInstance()->observers_->Notify(FROM_HERE, &PowerObserver::OnResume); } +void PowerMonitor::NotifyThermalStateChange( + PowerObserver::DeviceThermalState new_state) { + DCHECK(IsInitialized()); + DVLOG(1) << "ThermalStateChange: " + << PowerMonitorSource::DeviceThermalStateToString(new_state); + GetInstance()->observers_->Notify( + FROM_HERE, &PowerObserver::OnThermalStateChange, new_state); +} + PowerMonitor* PowerMonitor::GetInstance() { static base::NoDestructor<PowerMonitor> power_monitor; return power_monitor.get(); diff --git a/chromium/base/power_monitor/power_monitor.h b/chromium/base/power_monitor/power_monitor.h index fcf5ee482a5..6b80f3d2dd4 100644 --- a/chromium/base/power_monitor/power_monitor.h +++ b/chromium/base/power_monitor/power_monitor.h @@ -40,12 +40,10 @@ class BASE_EXPORT PowerMonitor { // from which it was registered. // Must not be called from within a notification callback. // - // AddObserver() fails and returns false if PowerMonitor::Initialize() has not - // been invoked. Failure should only happen in unit tests, where the - // PowerMonitor is generally not initialized. It is safe to call - // RemoveObserver with a PowerObserver that was not successfully added as an + // It is safe to add observers before the PowerMonitor is initialized. It is + // safe to call RemoveObserver with a PowerObserver that was not added as an // observer. - static bool AddObserver(PowerObserver* observer); + static void AddObserver(PowerObserver* observer); static void RemoveObserver(PowerObserver* observer); // Is the computer currently on battery power. May only be called if the @@ -58,6 +56,10 @@ class BASE_EXPORT PowerMonitor { // what is the real power state. static bool IsProcessSuspended(); + // Read the current DeviceThermalState if known. Can be called on any thread. + // May only be called if the PowerMonitor has been initialized. + static PowerObserver::DeviceThermalState GetCurrentThermalState(); + // Uninitializes the PowerMonitor. Should be called at the end of any unit // test that mocks out the PowerMonitor, to avoid affecting subsequent tests. // There must be no live PowerObservers when invoked. Safe to call even if the @@ -76,6 +78,8 @@ class BASE_EXPORT PowerMonitor { static void NotifyPowerStateChange(bool battery_in_use); static void NotifySuspend(); static void NotifyResume(); + static void NotifyThermalStateChange( + PowerObserver::DeviceThermalState new_state); static PowerMonitor* GetInstance(); diff --git a/chromium/base/power_monitor/power_monitor_device_source.h b/chromium/base/power_monitor/power_monitor_device_source.h index 409cf0902f6..83b057f139a 100644 --- a/chromium/base/power_monitor/power_monitor_device_source.h +++ b/chromium/base/power_monitor/power_monitor_device_source.h @@ -20,6 +20,7 @@ #include "base/mac/scoped_cftyperef.h" #include "base/mac/scoped_ionotificationportref.h" +#include "base/power_monitor/thermal_state_observer_mac.h" #endif #if defined(OS_IOS) @@ -46,6 +47,8 @@ class BASE_EXPORT PowerMonitorDeviceSource : public PowerMonitorSource { #endif private: + friend class PowerMonitorDeviceSourceTest; + #if defined(OS_WIN) // Represents a message-only window for power message handling on Windows. // Only allow PowerMonitor to create it. @@ -85,6 +88,9 @@ class BASE_EXPORT PowerMonitorDeviceSource : public PowerMonitorSource { bool IsOnBatteryPowerImpl() override; #if defined(OS_MACOSX) && !defined(OS_IOS) + // PowerMonitorSource: + PowerObserver::DeviceThermalState GetCurrentThermalState() override; + // Reference to the system IOPMrootDomain port. io_connect_t power_manager_port_ = IO_OBJECT_NULL; @@ -96,6 +102,9 @@ class BASE_EXPORT PowerMonitorDeviceSource : public PowerMonitorSource { // Run loop source to observe power-source-change events. ScopedCFTypeRef<CFRunLoopSourceRef> power_source_run_loop_source_; + + // Observer of thermal state events: critical temperature etc. + std::unique_ptr<ThermalStateObserverMac> thermal_state_observer_; #endif #if defined(OS_IOS) diff --git a/chromium/base/power_monitor/power_monitor_device_source_mac.mm b/chromium/base/power_monitor/power_monitor_device_source_mac.mm index 71c7403e00c..8618be1549c 100644 --- a/chromium/base/power_monitor/power_monitor_device_source_mac.mm +++ b/chromium/base/power_monitor/power_monitor_device_source_mac.mm @@ -51,6 +51,14 @@ bool PowerMonitorDeviceSource::IsOnBatteryPowerImpl() { return true; } +PowerObserver::DeviceThermalState +PowerMonitorDeviceSource::GetCurrentThermalState() { + if (@available(macOS 10.10.3, *)) { + return thermal_state_observer_->GetCurrentThermalState(); + }; + return PowerObserver::DeviceThermalState::kUnknown; +} + namespace { void BatteryEventCallback(void*) { @@ -82,6 +90,11 @@ void PowerMonitorDeviceSource::PlatformInit() { CFRunLoopAddSource(CFRunLoopGetCurrent(), power_source_run_loop_source_, kCFRunLoopDefaultMode); + + if (@available(macOS 10.10.3, *)) { + thermal_state_observer_ = std::make_unique<ThermalStateObserverMac>( + BindRepeating(&PowerMonitorSource::ProcessThermalEvent)); + }; } void PowerMonitorDeviceSource::PlatformDestroy() { diff --git a/chromium/base/power_monitor/power_monitor_device_source_unittest.cc b/chromium/base/power_monitor/power_monitor_device_source_unittest.cc new file mode 100644 index 00000000000..889f7577ade --- /dev/null +++ b/chromium/base/power_monitor/power_monitor_device_source_unittest.cc @@ -0,0 +1,39 @@ +// Copyright 2020 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "base/power_monitor/power_monitor_device_source.h" + +#include "base/logging.h" +#include "base/power_monitor/power_monitor.h" +#include "base/power_monitor/power_monitor_source.h" +#include "build/build_config.h" +#include "testing/gtest/include/gtest/gtest.h" + +using DeviceThermalState = base::PowerObserver::DeviceThermalState; + +namespace base { + +class PowerMonitorDeviceSourceTest : public testing::Test { + public: + PowerMonitorDeviceSourceTest() = default; + ~PowerMonitorDeviceSourceTest() override = default; + + DeviceThermalState GetCurrentThermalState() { + return power_monitor_device_source_.GetCurrentThermalState(); + } + + PowerMonitorDeviceSource power_monitor_device_source_; +}; + +TEST_F(PowerMonitorDeviceSourceTest, GetCurrentThermalState) { + const DeviceThermalState current_state = GetCurrentThermalState(); +#if defined(OS_MACOSX) && !defined(OS_IOS) + // We cannot make assumptions on |current_state|. Print it out to use the var. + DVLOG(1) << PowerMonitorSource::DeviceThermalStateToString(current_state); +#else + EXPECT_EQ(current_state, DeviceThermalState::kUnknown); +#endif +} + +} // namespace base diff --git a/chromium/base/power_monitor/power_monitor_device_source_win.cc b/chromium/base/power_monitor/power_monitor_device_source_win.cc index f7351356307..ae5ded7ea06 100644 --- a/chromium/base/power_monitor/power_monitor_device_source_win.cc +++ b/chromium/base/power_monitor/power_monitor_device_source_win.cc @@ -4,6 +4,7 @@ #include "base/power_monitor/power_monitor_device_source.h" +#include "base/logging.h" #include "base/message_loop/message_loop_current.h" #include "base/power_monitor/power_monitor.h" #include "base/power_monitor/power_monitor_source.h" diff --git a/chromium/base/power_monitor/power_monitor_source.cc b/chromium/base/power_monitor/power_monitor_source.cc index 794eee7b764..dab12ffc2bd 100644 --- a/chromium/base/power_monitor/power_monitor_source.cc +++ b/chromium/base/power_monitor/power_monitor_source.cc @@ -17,6 +17,11 @@ bool PowerMonitorSource::IsOnBatteryPower() { return on_battery_power_; } +PowerObserver::DeviceThermalState PowerMonitorSource::GetCurrentThermalState() { + return PowerObserver::DeviceThermalState::kUnknown; +} + +// static void PowerMonitorSource::ProcessPowerEvent(PowerEvent event_id) { if (!PowerMonitor::IsInitialized()) return; @@ -58,6 +63,14 @@ void PowerMonitorSource::ProcessPowerEvent(PowerEvent event_id) { } } +// static +void PowerMonitorSource::ProcessThermalEvent( + PowerObserver::DeviceThermalState new_thermal_state) { + if (!PowerMonitor::IsInitialized()) + return; + PowerMonitor::NotifyThermalStateChange(new_thermal_state); +} + void PowerMonitorSource::SetInitialOnBatteryPowerState(bool on_battery_power) { // Must only be called before an initialized PowerMonitor exists, otherwise // the caller should have just used a normal @@ -66,4 +79,23 @@ void PowerMonitorSource::SetInitialOnBatteryPowerState(bool on_battery_power) { on_battery_power_ = on_battery_power; } +// static +const char* PowerMonitorSource::DeviceThermalStateToString( + PowerObserver::DeviceThermalState state) { + switch (state) { + case PowerObserver::DeviceThermalState::kUnknown: + return "Unknown"; + case PowerObserver::DeviceThermalState::kNominal: + return "Nominal"; + case PowerObserver::DeviceThermalState::kFair: + return "Fair"; + case PowerObserver::DeviceThermalState::kSerious: + return "Serious"; + case PowerObserver::DeviceThermalState::kCritical: + return "Critical"; + } + NOTREACHED(); + return "Unknown"; +} + } // namespace base diff --git a/chromium/base/power_monitor/power_monitor_source.h b/chromium/base/power_monitor/power_monitor_source.h index 7f59a644026..a80dc59c6de 100644 --- a/chromium/base/power_monitor/power_monitor_source.h +++ b/chromium/base/power_monitor/power_monitor_source.h @@ -8,6 +8,7 @@ #include "base/base_export.h" #include "base/macros.h" #include "base/memory/ref_counted.h" +#include "base/power_monitor/power_observer.h" #include "base/synchronization/lock.h" namespace base { @@ -30,15 +31,24 @@ class BASE_EXPORT PowerMonitorSource { // Is the computer currently on battery power. Can be called on any thread. bool IsOnBatteryPower(); + // Reads the current DeviceThermalState, if available on the platform. + // Otherwise, returns kUnknown. + virtual PowerObserver::DeviceThermalState GetCurrentThermalState(); + + static const char* DeviceThermalStateToString( + PowerObserver::DeviceThermalState state); + protected: friend class PowerMonitorTest; // Friend function that is allowed to access the protected ProcessPowerEvent. friend void ProcessPowerEventHelper(PowerEvent); - // ProcessPowerEvent should only be called from a single thread, most likely + // Process*Event should only be called from a single thread, most likely // the UI thread or, in child processes, the IO thread. static void ProcessPowerEvent(PowerEvent event_id); + static void ProcessThermalEvent( + PowerObserver::DeviceThermalState new_thermal_state); // Platform-specific method to check whether the system is currently // running on battery power. Returns true if running on batteries, diff --git a/chromium/base/power_monitor/power_monitor_unittest.cc b/chromium/base/power_monitor/power_monitor_unittest.cc index 2f7e04485d8..db18fbfa9c7 100644 --- a/chromium/base/power_monitor/power_monitor_unittest.cc +++ b/chromium/base/power_monitor/power_monitor_unittest.cc @@ -13,12 +13,15 @@ namespace base { class PowerMonitorTest : public testing::Test { protected: - PowerMonitorTest() { + PowerMonitorTest() = default; + + void TearDown() override { PowerMonitor::ShutdownForTesting(); } + + void PowerMonitorInitialize() { power_monitor_source_ = new PowerMonitorTestSource(); PowerMonitor::Initialize( std::unique_ptr<PowerMonitorSource>(power_monitor_source_)); } - ~PowerMonitorTest() override { PowerMonitor::ShutdownForTesting(); } PowerMonitorTestSource* source() { return power_monitor_source_; } @@ -34,9 +37,11 @@ class PowerMonitorTest : public testing::Test { TEST_F(PowerMonitorTest, PowerNotifications) { const int kObservers = 5; + PowerMonitorInitialize(); + PowerMonitorTestObserver observers[kObservers]; for (auto& index : observers) - EXPECT_TRUE(PowerMonitor::AddObserver(&index)); + PowerMonitor::AddObserver(&index); // Sending resume when not suspended should have no effect. source()->GenerateResumeEvent(); @@ -82,4 +87,53 @@ TEST_F(PowerMonitorTest, PowerNotifications) { PowerMonitor::RemoveObserver(&index); } +TEST_F(PowerMonitorTest, ThermalThrottling) { + PowerMonitorTestObserver observer; + PowerMonitor::AddObserver(&observer); + + PowerMonitorInitialize(); + + constexpr PowerObserver::DeviceThermalState kThermalStates[] = { + PowerObserver::DeviceThermalState::kUnknown, + PowerObserver::DeviceThermalState::kNominal, + PowerObserver::DeviceThermalState::kFair, + PowerObserver::DeviceThermalState::kSerious, + PowerObserver::DeviceThermalState::kCritical}; + + for (const auto state : kThermalStates) { + source()->GenerateThermalThrottlingEvent(state); + EXPECT_EQ(state, source()->GetCurrentThermalState()); + EXPECT_EQ(observer.last_thermal_state(), state); + } + + PowerMonitor::RemoveObserver(&observer); +} + +TEST_F(PowerMonitorTest, AddObserverBeforeAndAfterInitialization) { + PowerMonitorTestObserver observer1; + PowerMonitorTestObserver observer2; + + // An observer is added before the PowerMonitor initialization. + PowerMonitor::AddObserver(&observer1); + + PowerMonitorInitialize(); + + // An observer is added after the PowerMonitor initialization. + PowerMonitor::AddObserver(&observer2); + + // Simulate suspend/resume notifications. + source()->GenerateSuspendEvent(); + EXPECT_EQ(observer1.suspends(), 1); + EXPECT_EQ(observer2.suspends(), 1); + EXPECT_EQ(observer1.resumes(), 0); + EXPECT_EQ(observer2.resumes(), 0); + + source()->GenerateResumeEvent(); + EXPECT_EQ(observer1.resumes(), 1); + EXPECT_EQ(observer2.resumes(), 1); + + PowerMonitor::RemoveObserver(&observer1); + PowerMonitor::RemoveObserver(&observer2); +} + } // namespace base diff --git a/chromium/base/power_monitor/power_observer.h b/chromium/base/power_monitor/power_observer.h index 658172c0ead..e6e3d89fc6b 100644 --- a/chromium/base/power_monitor/power_observer.h +++ b/chromium/base/power_monitor/power_observer.h @@ -12,6 +12,21 @@ namespace base { class BASE_EXPORT PowerObserver { public: + // Values to indicate the system's thermal states: from kNominal onwards to + // kCritical they represent increasing SoC die temperatures, usually needing + // disruptive actions by the system like e.g. turning on the fans (on systems + // equipped with those) or reducing voltage and frequency (oftentimes + // degrading overall responsiveness). The taxonomy is derived from MacOS (see + // e.g. [1]) but applies to others e.g. Linux/ChromeOS. + // [1] https://developer.apple.com/library/archive/documentation/Performance/Conceptual/power_efficiency_guidelines_osx/RespondToThermalStateChanges.html + enum class DeviceThermalState { + kUnknown, + kNominal, + kFair, + kSerious, + kCritical, + }; + // Notification of a change in power status of the computer, such // as from switching between battery and A/C power. virtual void OnPowerStateChange(bool on_battery_power) {} @@ -22,6 +37,15 @@ class BASE_EXPORT PowerObserver { // Notification that the system is resuming. virtual void OnResume() {} + // Notification of a change in the thermal status of the system, such as + // entering a critical temperature range. Depending on the severity, the SoC + // or the OS might take steps to reduce said temperature e.g., throttling the + // CPU or switching on the fans if available. API clients may react to the new + // state by reducing expensive computing tasks (e.g. video encoding), or + // notifying the user. The same |new_state| might be received repeatedly. + // TODO(crbug.com/1071431): implemented on MacOS, extend to Linux/CrOs. + virtual void OnThermalStateChange(DeviceThermalState new_state) {} + protected: virtual ~PowerObserver() = default; }; diff --git a/chromium/base/power_monitor/thermal_state_observer_mac.h b/chromium/base/power_monitor/thermal_state_observer_mac.h new file mode 100644 index 00000000000..70466a3d8ff --- /dev/null +++ b/chromium/base/power_monitor/thermal_state_observer_mac.h @@ -0,0 +1,40 @@ +// Copyright 2020 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef BASE_POWER_MONITOR_THERMAL_STATE_OBSERVER_MAC_H_ +#define BASE_POWER_MONITOR_THERMAL_STATE_OBSERVER_MAC_H_ + +#include <objc/objc.h> +#include <memory> + +#include "base/base_export.h" +#include "base/callback.h" +#include "base/power_monitor/power_observer.h" + +namespace base { + +// This class is used to listen for the thermal state change notification +// NSProcessInfoThermalStateDidChangeNotification, routing it to +// PowerMonitorSource. +class BASE_EXPORT ThermalStateObserverMac { + public: + using StateUpdateCallback = + base::RepeatingCallback<void(PowerObserver::DeviceThermalState)>; + + explicit ThermalStateObserverMac(StateUpdateCallback state_update_callback); + ~ThermalStateObserverMac(); + + PowerObserver::DeviceThermalState GetCurrentThermalState(); + + private: + FRIEND_TEST_ALL_PREFIXES(ThermalStateObserverMacTest, StateChange); + PowerObserver::DeviceThermalState state_for_testing_ = + PowerObserver::DeviceThermalState::kUnknown; + + id thermal_state_update_observer_; +}; + +} // namespace base + +#endif // BASE_POWER_MONITOR_THERMAL_STATE_OBSERVER_MAC_H_ diff --git a/chromium/base/power_monitor/thermal_state_observer_mac.mm b/chromium/base/power_monitor/thermal_state_observer_mac.mm new file mode 100644 index 00000000000..54b81aaafee --- /dev/null +++ b/chromium/base/power_monitor/thermal_state_observer_mac.mm @@ -0,0 +1,75 @@ +// Copyright 2020 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "base/power_monitor/thermal_state_observer_mac.h" + +#import <Foundation/Foundation.h> + +#include "base/logging.h" +#include "base/power_monitor/power_monitor.h" +#include "base/power_monitor/power_monitor_source.h" + +namespace { + +base::PowerObserver::DeviceThermalState +NSProcessInfoThermalStateToDeviceThermalState( + NSProcessInfoThermalState nsinfo_state) NS_AVAILABLE_MAC(10_10_3) { + switch (nsinfo_state) { + case NSProcessInfoThermalStateNominal: + return base::PowerObserver::DeviceThermalState::kNominal; + case NSProcessInfoThermalStateFair: + return base::PowerObserver::DeviceThermalState::kFair; + case NSProcessInfoThermalStateSerious: + return base::PowerObserver::DeviceThermalState::kSerious; + case NSProcessInfoThermalStateCritical: + return base::PowerObserver::DeviceThermalState::kCritical; + } + NOTREACHED(); + return base::PowerObserver::DeviceThermalState::kUnknown; +} +} + +namespace base { + +ThermalStateObserverMac::ThermalStateObserverMac( + StateUpdateCallback state_update_callback) NS_AVAILABLE_MAC(10_10_3) { + auto on_state_change_block = ^(NSNotification* notification) { + auto state = PowerObserver::DeviceThermalState::kUnknown; + // |thermalState| is basically a scale of power usage and its associated + // thermal dissipation increase, from Nominal upwards, see: + // https://developer.apple.com/library/archive/documentation/Performance/Conceptual/power_efficiency_guidelines_osx/RespondToThermalStateChanges.html + NSProcessInfoThermalState nsinfo_state = + [[NSProcessInfo processInfo] thermalState]; + state = NSProcessInfoThermalStateToDeviceThermalState(nsinfo_state); + if (state_for_testing_ != PowerObserver::DeviceThermalState::kUnknown) + state = state_for_testing_; + DVLOG(1) << __func__ << ": " + << PowerMonitorSource::DeviceThermalStateToString(state); + state_update_callback.Run(state); + }; + + thermal_state_update_observer_ = [[NSNotificationCenter defaultCenter] + addObserverForName:NSProcessInfoThermalStateDidChangeNotification + object:nil + queue:nil + usingBlock:on_state_change_block]; + + // Force a first call to grab the current status. + on_state_change_block(nil); +} + +ThermalStateObserverMac::~ThermalStateObserverMac() { + [[NSNotificationCenter defaultCenter] + removeObserver:thermal_state_update_observer_]; +} + +PowerObserver::DeviceThermalState +ThermalStateObserverMac::GetCurrentThermalState() NS_AVAILABLE_MAC(10_10_3) { + if (state_for_testing_ != PowerObserver::DeviceThermalState::kUnknown) + return state_for_testing_; + NSProcessInfoThermalState nsinfo_state = + [[NSProcessInfo processInfo] thermalState]; + return NSProcessInfoThermalStateToDeviceThermalState(nsinfo_state); +} +} diff --git a/chromium/base/power_monitor/thermal_state_observer_mac_unittest.mm b/chromium/base/power_monitor/thermal_state_observer_mac_unittest.mm new file mode 100644 index 00000000000..3ecaae78e69 --- /dev/null +++ b/chromium/base/power_monitor/thermal_state_observer_mac_unittest.mm @@ -0,0 +1,52 @@ +// Copyright 2020 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "base/power_monitor/thermal_state_observer_mac.h" + +#include <memory> +#include <queue> + +#import <Foundation/Foundation.h> + +#include "base/power_monitor/power_monitor.h" +#include "base/power_monitor/power_monitor_source.h" +#include "testing/gtest/include/gtest/gtest.h" + +using DeviceThermalState = base::PowerObserver::DeviceThermalState; + +namespace base { + +class ThermalStateObserverMacTest : public testing::Test { + public: + ThermalStateObserverMacTest() = default; + ~ThermalStateObserverMacTest() override = default; + + void OnStateChange(DeviceThermalState state) { state_history_.push(state); } + + std::queue<DeviceThermalState> state_history_; + std::unique_ptr<ThermalStateObserverMac> thermal_state_observer_; +}; + +// Verifies that a NSProcessInfoThermalStateDidChangeNotification produces the +// adequate OnStateChange() call. +TEST_F(ThermalStateObserverMacTest, StateChange) NS_AVAILABLE_MAC(10_10_3) { + EXPECT_TRUE(state_history_.empty()); + + // ThermalStateObserverMac sends the current thermal state on construction. + thermal_state_observer_ = + std::make_unique<ThermalStateObserverMac>(BindRepeating( + &ThermalStateObserverMacTest::OnStateChange, Unretained(this))); + EXPECT_EQ(state_history_.size(), 1u); + state_history_.pop(); + + thermal_state_observer_->state_for_testing_ = DeviceThermalState::kCritical; + [NSNotificationCenter.defaultCenter + postNotificationName:NSProcessInfoThermalStateDidChangeNotification + object:nil + userInfo:nil]; + EXPECT_EQ(state_history_.size(), 1u); + EXPECT_EQ(state_history_.front(), DeviceThermalState::kCritical); +} + +} // namespace base |