File: state_observer.h

package info (click to toggle)
chromium 138.0.7204.183-1
  • links: PTS, VCS
  • area: main
  • in suites: trixie
  • size: 6,071,908 kB
  • sloc: cpp: 34,937,088; ansic: 7,176,967; javascript: 4,110,704; python: 1,419,953; asm: 946,768; xml: 739,971; pascal: 187,324; sh: 89,623; perl: 88,663; objc: 79,944; sql: 50,304; cs: 41,786; fortran: 24,137; makefile: 21,806; php: 13,980; tcl: 13,166; yacc: 8,925; ruby: 7,485; awk: 3,720; lisp: 3,096; lex: 1,327; ada: 727; jsp: 228; sed: 36
file content (145 lines) | stat: -rw-r--r-- 4,903 bytes parent folder | download | duplicates (5)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
// 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_