910e62b5创建于 1月15日历史提交
// 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_STATE_OBSERVER_H_
#define UI_BASE_INTERACTION_STATE_OBSERVER_H_

#include <algorithm>
#include <concepts>
#include <ostream>
#include <utility>

#include "base/functional/callback.h"
#include "base/memory/raw_ptr.h"
#include "base/scoped_observation.h"
#include "ui/base/interaction/element_identifier.h"
#include "ui/base/interaction/interactive_test_definitions.h"
#include "ui/base/interaction/typed_identifier.h"

namespace ui::test {

// Base class for all state change observers. Observes some state of the system,
// which we can then wait on.
//
// Used with `InteractiveTestApi::ObserveState()` and `WaitForState()`.
//
// Value type `T` must be default-constructible and copy-assignable.
template <typename T>
  requires ::ui::test::internal::IsValidMatcherType<T>
class StateObserver {
 public:
  using StateChangedCallback = base::RepeatingCallback<void(T)>;
  using ValueType = T;

  StateObserver() = default;
  virtual ~StateObserver() { OnStateObserverStateChanged(T()); }

  // Copy and assignment are not allowed.
  StateObserver(const StateObserver&) = delete;
  void operator=(const StateObserver&) = delete;

  // Returns the initial state. Override to provide your own logic; returns
  // `T()` by default.
  virtual T GetStateObserverInitialState() const { return T(); }

  // Used by the owning test to set the state change callback. Do not call
  // directly. The caller should ensure the the new callback is issued with the
  // current state.
  void SetStateObserverStateChangedCallback(StateChangedCallback callback) {
    CHECK(!state_changed_callback_);
    state_changed_callback_ = std::move(callback);
  }

 protected:
  // Call to update the state.
  void OnStateObserverStateChanged(T state) {
    if (state_changed_callback_) {
      state_changed_callback_.Run(state);
    }
  }

 private:
  StateChangedCallback state_changed_callback_;
};

template <typename T>
concept IsStateObserver = requires {
  typename T::ValueType;
} && std::derived_from<T, StateObserver<typename T::ValueType>>;

// State observer that uses a `ScopedObservation<T, Source, Observer>` to watch
// for state changes using an observer pattern.
//
// You will still need to override the specific observer methods to detect:
//  - The actual state change
//     - call `OnStateObserverStateChanged()`
//  - The "source destroyed" message (optional)
//     - call `OnObservationStateObserverSourceDestroyed()`
//
// If the initial state may vary, you can also override
// `GetStateObserverInitialState()`. Use `source()` to extract the relevant
// information.
//
// An example can be found in chrome/test/interaction/README.md.
template <typename T, typename Source, typename Observer>
class ObservationStateObserver : public StateObserver<T>, public Observer {
 public:
  explicit ObservationStateObserver(Source* source_object)
      : source_(source_object) {
    observation_.Observe(source_object);
  }

  ~ObservationStateObserver() override = default;

  Source* source() const { return source_; }

 protected:
  // Call to indicate that the `source` object is going away. If the object will
  // never go away during the scope of this object, or there is no callback to
  // detect destruction, you can ignore this method.
  //
  // Resets the state value to `T()`.
  void OnObservationStateObserverSourceDestroyed() {
    StateObserver<T>::OnStateObserverStateChanged(T());
    observation_.Reset();
    source_ = nullptr;
  }

 private:
  raw_ptr<Source> source_;
  base::ScopedObservation<Source, Observer> observation_{this};
};

template <typename T>
using StateIdentifier = TypedIdentifier<T>;

}  // namespace ui::test

// The following macros create a state identifier value for use in tests.
//
// The associated type of observer should be specified along with the unique
// identifier name:
// ```
// DECLARE_STATE_IDENTIFIER_VALUE(MyObserverType, kMyState);
// ```
//
// `DECLARE_STATE_IDENTIFIER_VALUE()` and `DEFINE_STATE_IDENTIFIER_VALUE()` are
// for use in .h and .cc files, respectively, as with declaring
// `ElementIdentifier`s.
//
// To declare a `StateIdentifier` local to a .cc file or method body, use
// DEFINE_LOCAL_STATE_IDENTIFIER_VALUE() instead. This will create a file- and
// line-mangled name that will not suffer name collisions with other
// identifiers.

#define DECLARE_STATE_IDENTIFIER_VALUE(ObserverType, Name) \
  DECLARE_TYPED_IDENTIFIER_VALUE(ObserverType, Name)

#define DEFINE_STATE_IDENTIFIER_VALUE(ObserverType, Name) \
  DEFINE_TYPED_IDENTIFIER_VALUE(ObserverType, Name)

#define DEFINE_LOCAL_STATE_IDENTIFIER_VALUE(ObserverType, Name) \
  DEFINE_MACRO_TYPED_IDENTIFIER_VALUE(__FILE__, __LINE__, ObserverType, Name)

#endif  // UI_BASE_INTERACTION_STATE_OBSERVER_H_