File: caption_bubble_context_remote_unittest.cc

package info (click to toggle)
chromium 139.0.7258.127-1
  • links: PTS, VCS
  • area: main
  • in suites:
  • size: 6,122,068 kB
  • sloc: cpp: 35,100,771; ansic: 7,163,530; javascript: 4,103,002; python: 1,436,920; asm: 946,517; xml: 746,709; pascal: 187,653; perl: 88,691; sh: 88,436; objc: 79,953; sql: 51,488; cs: 44,583; fortran: 24,137; makefile: 22,147; tcl: 15,277; php: 13,980; yacc: 8,984; ruby: 7,485; awk: 3,720; lisp: 3,096; lex: 1,327; ada: 727; jsp: 228; sed: 36
file content (150 lines) | stat: -rw-r--r-- 5,218 bytes parent folder | download | duplicates (9)
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
146
147
148
149
150
// 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.

#include "components/live_caption/caption_bubble_context_remote.h"

#include <optional>

#include "base/run_loop.h"
#include "base/test/bind.h"
#include "base/test/task_environment.h"
#include "media/mojo/mojom/speech_recognition.mojom.h"
#include "mojo/public/cpp/bindings/pending_receiver.h"
#include "mojo/public/cpp/bindings/pending_remote.h"
#include "mojo/public/cpp/bindings/receiver.h"
#include "mojo/public/cpp/bindings/remote.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "ui/gfx/geometry/rect.h"

namespace captions {
namespace {

using media::mojom::SpeechRecognitionSurface;
using testing::_;
using testing::Test;

// A surface whose methods can have expectations placed on them.
class MockSurface : public SpeechRecognitionSurface {
 public:
  explicit MockSurface(mojo::PendingReceiver<SpeechRecognitionSurface> receiver)
      : receiver_(this, std::move(receiver)) {}
  ~MockSurface() override = default;

  MockSurface(const MockSurface&) = delete;
  MockSurface& operator=(const MockSurface&) = delete;

  // media::mojom::SpeechRecognitionSurface:
  MOCK_METHOD(void, Activate, (), (override));
  MOCK_METHOD(void,
              GetBounds,
              (SpeechRecognitionSurface::GetBoundsCallback),
              (override));

 private:
  mojo::Receiver<SpeechRecognitionSurface> receiver_;
};

class CaptionBubbleContextRemoteTest : public Test {
 public:
  CaptionBubbleContextRemoteTest() = default;
  ~CaptionBubbleContextRemoteTest() override = default;

  CaptionBubbleContextRemoteTest(const CaptionBubbleContextRemoteTest&) =
      delete;
  CaptionBubbleContextRemoteTest& operator=(
      const CaptionBubbleContextRemoteTest&) = delete;

  void SetUp() override {
    mojo::PendingReceiver<SpeechRecognitionSurface> receiver;
    context_.emplace(receiver.InitWithNewPipeAndPassRemote(), "session-id");
    surface_.emplace(std::move(receiver));
  }

 protected:
  // Use optionals to delay initialization while keeping objects on the stack.
  std::optional<CaptionBubbleContextRemote> context_;
  std::optional<MockSurface> surface_;

 private:
  base::test::TaskEnvironment task_environment_;
};

// Test that activate requests are forwarded to the remote process.
TEST_F(CaptionBubbleContextRemoteTest, Activate) {
  EXPECT_CALL(*surface_, Activate()).Times(2);

  // Our expectation that the activate call is forwarded over Mojo should be
  // met.
  context_->Activate();
  context_->Activate();
  base::RunLoop().RunUntilIdle();
}

// Test that bounds requests are forwarded to the remote process.
TEST_F(CaptionBubbleContextRemoteTest, GetBounds) {
  // Note expectations are saturated from last to first.
  const gfx::Rect expected_bounds_1 = gfx::Rect(1, 2, 3, 4);
  const gfx::Rect expected_bounds_2 = gfx::Rect(5, 6, 7, 8);
  EXPECT_CALL(*surface_, GetBounds(_))
      .WillOnce([&](auto cb) { std::move(cb).Run(expected_bounds_2); })
      .RetiresOnSaturation();
  EXPECT_CALL(*surface_, GetBounds(_))
      .WillOnce([&](auto cb) { std::move(cb).Run(expected_bounds_1); })
      .RetiresOnSaturation();

  // First call should correctly fetch first bounds.
  gfx::Rect actual_bounds;
  context_->GetBounds(base::BindLambdaForTesting(
      [&](const gfx::Rect& bounds) { actual_bounds = bounds; }));
  base::RunLoop().RunUntilIdle();
  EXPECT_EQ(expected_bounds_1, actual_bounds);

  // Next call should correctly fetch updated bounds.
  context_->GetBounds(base::BindLambdaForTesting(
      [&](const gfx::Rect& bounds) { actual_bounds = bounds; }));
  base::RunLoop().RunUntilIdle();
  EXPECT_EQ(expected_bounds_2, actual_bounds);
}

// Test that replacing an observer is handled gracefully.
TEST_F(CaptionBubbleContextRemoteTest, DuplicateObservers) {
  bool ended_1 = false;
  bool ended_2 = false;

  // This observer will be replaced before it can execute its callback.
  auto observer_1 = context_->GetCaptionBubbleSessionObserver();
  observer_1->SetEndSessionCallback(base::BindLambdaForTesting(
      [&](const std::string& id) { ended_1 = id == "session-id"; }));

  // Creating a new observer will invalidate the old one.
  auto observer_2 = context_->GetCaptionBubbleSessionObserver();
  observer_2->SetEndSessionCallback(base::BindLambdaForTesting(
      [&](const std::string& id) { ended_2 = id == "session-id"; }));

  // Trigger session end and make sure one callback is called.
  context_->OnSessionEnded();
  base::RunLoop().RunUntilIdle();

  EXPECT_FALSE(ended_1);
  EXPECT_TRUE(ended_2);
}

// If the observer is dead by the time the session is ended, it shouldn't be
// exercised.
TEST_F(CaptionBubbleContextRemoteTest, DeadObserver) {
  auto observer = context_->GetCaptionBubbleSessionObserver();
  observer->SetEndSessionCallback(base::BindLambdaForTesting(
      [&](const std::string& id) { EXPECT_TRUE(false); }));
  observer.reset();

  // Trigger session end.
  context_->OnSessionEnded();
  base::RunLoop().RunUntilIdle();

  // We shouldn't try to call methods on the destructed observer.
}

}  // namespace
}  // namespace captions