blob: 80d9a3a831daaa245dc2f47d81d03d6c19d7ce89 [file] [log] [blame]
// Copyright 2024 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "ui/views/test/widget_activation_waiter.h"
#include "base/run_loop.h"
#include "ui/views/widget/widget.h"
#include "ui/views/widget/widget_observer.h"
#if BUILDFLAG(IS_MAC)
#include "base/strings/string_number_conversions.h"
#include "base/test/bind.h"
#include "base/test/scoped_run_loop_timeout.h"
#include "base/test/test_timeouts.h"
#endif
namespace views::test {
namespace {
// Use in tests to wait until a Widget's activation change to a particular
// value. To use create and call Wait().
class WidgetActivationWaiter : public WidgetObserver {
public:
WidgetActivationWaiter(Widget* widget, bool active)
: active_(active), widget_(*widget) {
if (active == widget->native_widget_active()) {
observed_ = true;
return;
}
widget_observation_.Observe(widget);
}
WidgetActivationWaiter(const WidgetActivationWaiter&) = delete;
WidgetActivationWaiter& operator=(const WidgetActivationWaiter&) = delete;
~WidgetActivationWaiter() override = default;
// Returns when the active status matches that supplied to the constructor. If
// the active status does not match that of the constructor a RunLoop is used
// until the active status matches, otherwise this returns immediately.
void Wait() {
if (!observed_) {
#if BUILDFLAG(IS_MAC)
// Some tests waiting on widget creation + activation are flaky due to
// timeout. crbug.com/1327590.
const base::test::ScopedRunLoopTimeout increased_run_timeout(
FROM_HERE, TestTimeouts::action_max_timeout(),
base::BindLambdaForTesting([&]() {
return "Requested activation state: " +
base::NumberToString(active_) + ", actual: " +
base::NumberToString(widget_->native_widget_active());
}));
#endif
run_loop_.Run();
}
}
private:
// views::WidgetObserver override:
void OnWidgetActivationChanged(Widget* widget, bool active) override {
if (active_ != active) {
return;
}
observed_ = true;
widget_observation_.Reset();
if (run_loop_.running()) {
run_loop_.Quit();
}
}
bool observed_ = false;
bool active_;
const raw_ref<Widget> widget_;
base::RunLoop run_loop_{base::RunLoop::Type::kNestableTasksAllowed};
base::ScopedObservation<Widget, WidgetObserver> widget_observation_{this};
};
} // namespace
void WaitForWidgetActive(Widget* widget, bool active) {
WidgetActivationWaiter waiter(widget, active);
waiter.Wait();
}
} // namespace views::test