blob: 5896b9b1bd3550db2c82b964631a7b238143bc79 [file] [log] [blame]
// Copyright 2023 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef UI_BASE_INTERACTION_POLLING_STATE_OBSERVER_H_
#define UI_BASE_INTERACTION_POLLING_STATE_OBSERVER_H_
#include <optional>
#include <utility>
#include "base/functional/callback_forward.h"
#include "base/time/time.h"
#include "base/timer/timer.h"
#include "ui/base/interaction/element_tracker.h"
#include "ui/base/interaction/interactive_test_definitions.h"
#include "ui/base/interaction/state_observer.h"
namespace ui::test {
// Observer which polls a state that cannot be observed via listeners/callbacks.
// Continues to poll for the lifetime of the test.
//
// Because this is a polling observer, the value is only valid at the moment it
// is observed, and transient values may be missed. For transient values, prefer
// deterministic state observation, custom events, etc.
//
// Designed for use with the `InteractiveTestApi::PollState()` verb.
template <typename T, typename M = internal::MatcherTypeFor<T>>
class PollingStateObserver : public StateObserver<M> {
public:
using PollCallback = base::RepeatingCallback<T()>;
static constexpr base::TimeDelta kDefaultPollingInterval =
base::Milliseconds(100);
// Calls `poll_callback` to get an updated value every `polling_interval`.
// `poll_callback` will also be called on initialization to get an initial
// value.
template <typename C>
explicit PollingStateObserver(
C&& poll_callback,
base::TimeDelta polling_interval = kDefaultPollingInterval)
: poll_callback_(
internal::MaybeBindRepeating(std::forward<C>(poll_callback))),
timer_(FROM_HERE,
polling_interval,
base::BindRepeating(&PollingStateObserver<T>::OnPoll,
base::Unretained(this))) {
timer_.Reset();
}
~PollingStateObserver() override = default;
// StateObserver:
M GetStateObserverInitialState() const final {
return M(poll_callback_.Run());
}
private:
void OnPoll() {
StateObserver<M>::OnStateObserverStateChanged(M(poll_callback_.Run()));
}
// The callback that returns the value when polled.
PollCallback poll_callback_;
// This timer will fire every `polling_interval`
base::RepeatingTimer timer_;
};
// Need out-of-line declaration of static class variables on some platforms.
template <typename T, typename M>
// static
constexpr base::TimeDelta PollingStateObserver<T, M>::kDefaultPollingInterval;
// Observer which polls a specific element that cannot be observed via
// listeners/callbacks. If an element with `identifier` in `context` is present,
// the observed value will be updated to the result of `poll_element_callback`,
// whereas if the element is not present in the context, it will be
// std::nullopt.
//
// If `context` is not specified, then the element will be located in any
// context.
//
// Because this is a polling observer, the value is only valid at the moment it
// is observed, and transient values may be missed. For transient values, prefer
// deterministic state observation, custom events, etc.
//
// Designed for use with the `InteractiveTestApi::PollElement()` verb.
template <typename T,
typename Superclass =
PollingStateObserver<std::optional<T>,
std::optional<internal::MatcherTypeFor<T>>>>
class PollingElementStateObserver : public Superclass {
public:
using PollElementCallback = base::RepeatingCallback<T(const TrackedElement*)>;
// Calls `poll_element_callback` on the element with `identifier` in
// `context` to update the value every `polling_interval`. If a matching
// element is not present, the value is `std::nullopt`. The callback will be
// called on initialization (if the element already exists) to get an initial
// value.
template <typename C>
PollingElementStateObserver(
ElementIdentifier identifier,
std::optional<ElementContext> context,
C&& poll_element_callback,
base::TimeDelta polling_interval = Superclass::kDefaultPollingInterval)
: Superclass(
base::BindRepeating(
[](ElementIdentifier id,
std::optional<ElementContext> ctx,
PollElementCallback cb) {
auto* const el = ctx.has_value()
? ElementTracker::GetElementTracker()
->GetFirstMatchingElement(id, *ctx)
: ElementTracker::GetElementTracker()
->GetElementInAnyContext(id);
return el ? std::make_optional(cb.Run(el)) : std::nullopt;
},
identifier,
context,
internal::MaybeBindRepeating(
std::forward<C>(poll_element_callback))),
polling_interval) {}
~PollingElementStateObserver() override = default;
};
template <typename T>
concept IsPollingStateObserver = requires {
typename T::ValueType;
} && std::derived_from<T, PollingStateObserver<typename T::ValueType>>;
} // namespace ui::test
#endif // UI_BASE_INTERACTION_POLLING_STATE_OBSERVER_H_