chromium/android_webview/browser/gfx/begin_frame_source_webview_unittest.cc

// Copyright 2020 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "android_webview/browser/gfx/begin_frame_source_webview.h"

#include "components/viz/test/begin_frame_source_test.h"

namespace android_webview {
namespace {

class TestBeginFrameSource : public viz::ExternalBeginFrameSource,
                             public viz::ExternalBeginFrameSourceClient {
 public:
  TestBeginFrameSource() : ExternalBeginFrameSource(this) {}
  ~TestBeginFrameSource() override = default;

  void OnNeedsBeginFrames(bool needs_begin_frames) override {
    needs_begin_frames_ = needs_begin_frames;
  }

  bool needs_begin_frames() const { return needs_begin_frames_; }

 private:
  bool needs_begin_frames_ = false;
};

}  // namespace

class BeginFrameSourceWebViewTest : public ::testing::Test {
 public:
  BeginFrameSourceWebViewTest() {
    root_begin_frame_source_.ObserveBeginFrameSource(&test_begin_frame_source_);
  }

  viz::BeginFrameArgs BeginFrameArgsForTesting(uint64_t sequence) {
    return viz::CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE, 1,
                                               sequence);
  }

 protected:
  BeginFrameSourceWebView begin_frame_source_;
  RootBeginFrameSourceWebView root_begin_frame_source_;

  // Test classes used to issue and observe begin frames.
  testing::NiceMock<viz::MockBeginFrameObserver> observer_;
  TestBeginFrameSource test_begin_frame_source_;
};

TEST_F(BeginFrameSourceWebViewTest, RootBeginFrame) {
  EXPECT_FALSE(test_begin_frame_source_.needs_begin_frames());

  // Send BeginFrame without observer.
  auto args1 = BeginFrameArgsForTesting(1);
  test_begin_frame_source_.OnBeginFrame(args1);

  root_begin_frame_source_.AddObserver(&observer_);
  EXPECT_TRUE(test_begin_frame_source_.needs_begin_frames());

  // Send BeginFrame with observer, verify it gets it.
  auto args2 = BeginFrameArgsForTesting(2);
  EXPECT_BEGIN_FRAME_ARGS_USED(observer_, args2);
  test_begin_frame_source_.OnBeginFrame(args2);

  root_begin_frame_source_.RemoveObserver(&observer_);
  EXPECT_FALSE(test_begin_frame_source_.needs_begin_frames());
}

TEST_F(BeginFrameSourceWebViewTest, RootPausedWithObservers) {
  // External BFS is not paused => observer will be unpaused on Add.
  EXPECT_BEGIN_FRAME_SOURCE_PAUSED(observer_, false);
  root_begin_frame_source_.AddObserver(&observer_);

  // External BFS is paused => observer will be paused.
  EXPECT_BEGIN_FRAME_SOURCE_PAUSED(observer_, true);
  test_begin_frame_source_.OnSetBeginFrameSourcePaused(true);

  // External BFS is unpaused => observer will be unpaused.
  EXPECT_BEGIN_FRAME_SOURCE_PAUSED(observer_, false);
  test_begin_frame_source_.OnSetBeginFrameSourcePaused(false);
  root_begin_frame_source_.RemoveObserver(&observer_);
}

TEST_F(BeginFrameSourceWebViewTest, RootPausedWithoutObservers) {
  test_begin_frame_source_.OnSetBeginFrameSourcePaused(true);

  // External BFS is paused => observer will be paused on Add.
  EXPECT_BEGIN_FRAME_SOURCE_PAUSED(observer_, true);
  root_begin_frame_source_.AddObserver(&observer_);

  // External BFS is unpaused => observer will be unpaused.
  EXPECT_BEGIN_FRAME_SOURCE_PAUSED(observer_, false);
  test_begin_frame_source_.OnSetBeginFrameSourcePaused(false);

  root_begin_frame_source_.RemoveObserver(&observer_);

  // External BFS is unpaused => observer will be unpaused on Add.
  EXPECT_BEGIN_FRAME_SOURCE_PAUSED(observer_, false);
  root_begin_frame_source_.AddObserver(&observer_);

  root_begin_frame_source_.RemoveObserver(&observer_);
}

TEST_F(BeginFrameSourceWebViewTest, ChildBeginFrame) {
  begin_frame_source_.SetParentSource(&root_begin_frame_source_);

  EXPECT_FALSE(test_begin_frame_source_.needs_begin_frames());

  // Send BeginFrame without observer.
  auto args1 = BeginFrameArgsForTesting(1);
  test_begin_frame_source_.OnBeginFrame(args1);

  begin_frame_source_.AddObserver(&observer_);
  EXPECT_TRUE(test_begin_frame_source_.needs_begin_frames());

  // Send BeginFrame with observer, verify it gets it.
  auto args2 = BeginFrameArgsForTesting(2);
  EXPECT_BEGIN_FRAME_ARGS_USED(observer_, args2);
  test_begin_frame_source_.OnBeginFrame(args2);

  begin_frame_source_.RemoveObserver(&observer_);
  EXPECT_FALSE(test_begin_frame_source_.needs_begin_frames());
}

TEST_F(BeginFrameSourceWebViewTest, ChildPausedWithObservers) {
  begin_frame_source_.SetParentSource(&root_begin_frame_source_);

  // External BFS is not paused => observer will be not paused on Add.
  EXPECT_BEGIN_FRAME_SOURCE_PAUSED(observer_, false);
  begin_frame_source_.AddObserver(&observer_);

  // External BFS paused => observer will be paused.
  EXPECT_BEGIN_FRAME_SOURCE_PAUSED(observer_, true);
  test_begin_frame_source_.OnSetBeginFrameSourcePaused(true);

  // External BFS unpaused => observer will be unpaused.
  EXPECT_BEGIN_FRAME_SOURCE_PAUSED(observer_, false);
  test_begin_frame_source_.OnSetBeginFrameSourcePaused(false);
  begin_frame_source_.RemoveObserver(&observer_);
}

TEST_F(BeginFrameSourceWebViewTest, ChildPausedWithoutObservers) {
  begin_frame_source_.SetParentSource(&root_begin_frame_source_);
  test_begin_frame_source_.OnSetBeginFrameSourcePaused(true);

  // External BFS is paused => observer will be paused on Add.
  EXPECT_BEGIN_FRAME_SOURCE_PAUSED(observer_, true);
  begin_frame_source_.AddObserver(&observer_);

  // External BFS unpaused => observer will be unpaused.
  EXPECT_BEGIN_FRAME_SOURCE_PAUSED(observer_, false);
  test_begin_frame_source_.OnSetBeginFrameSourcePaused(false);

  begin_frame_source_.RemoveObserver(&observer_);

  // External BFS is unpaused => observer will be unpaused on Add.
  EXPECT_BEGIN_FRAME_SOURCE_PAUSED(observer_, false);
  begin_frame_source_.AddObserver(&observer_);

  begin_frame_source_.RemoveObserver(&observer_);
}

TEST_F(BeginFrameSourceWebViewTest, ChildPausedNoParent) {
  // BFS doesn't have parent so it is paused => observer will be paused on Add.
  EXPECT_BEGIN_FRAME_SOURCE_PAUSED(observer_, true);
  begin_frame_source_.AddObserver(&observer_);

  // As there is no parent we are always paused, so we don't expect the call.
  EXPECT_CALL(observer_, OnBeginFrameSourcePausedChanged(testing::_)).Times(0);
  test_begin_frame_source_.OnSetBeginFrameSourcePaused(true);

  // As there is no parent we are always paused, so we don't expect the call.
  EXPECT_CALL(observer_, OnBeginFrameSourcePausedChanged(testing::_)).Times(0);
  test_begin_frame_source_.OnSetBeginFrameSourcePaused(false);

  begin_frame_source_.RemoveObserver(&observer_);
}

TEST_F(BeginFrameSourceWebViewTest, ChildPauseChangeOnSetParent) {
  // BFS doesn't have parent so it is paused => observer will be paused on Add.
  EXPECT_BEGIN_FRAME_SOURCE_PAUSED(observer_, true);
  begin_frame_source_.AddObserver(&observer_);

  // We set parent and it's unpaused => observer will be unpaused.
  EXPECT_BEGIN_FRAME_SOURCE_PAUSED(observer_, false);
  begin_frame_source_.SetParentSource(&root_begin_frame_source_);

  // We remove parent so BFS becomes paused => observer will be paused.
  EXPECT_BEGIN_FRAME_SOURCE_PAUSED(observer_, true);
  begin_frame_source_.SetParentSource(nullptr);

  test_begin_frame_source_.OnSetBeginFrameSourcePaused(true);

  // We set parent and it's paused => observer will be paused.
  // As it's already paused we don't expect the call.
  EXPECT_CALL(observer_, OnBeginFrameSourcePausedChanged(testing::_)).Times(0);
  begin_frame_source_.SetParentSource(&root_begin_frame_source_);

  begin_frame_source_.RemoveObserver(&observer_);
}

TEST_F(BeginFrameSourceWebViewTest, Reentrancy) {
  begin_frame_source_.SetParentSource(&root_begin_frame_source_);
  begin_frame_source_.AddObserver(&observer_);

  // Re-Add observer inside OnBeginFrame so it will trigger missed BeginFrame
  EXPECT_CALL(observer_, OnBeginFrame(testing::_))
      .WillRepeatedly(testing::Invoke([&](const viz::BeginFrameArgs& args) {
        if (args.type == viz::BeginFrameArgs::MISSED)
          return;
        begin_frame_source_.RemoveObserver(&observer_);
        begin_frame_source_.AddObserver(&observer_);
      }));

  test_begin_frame_source_.OnBeginFrame(BeginFrameArgsForTesting(1));

  begin_frame_source_.RemoveObserver(&observer_);
}

}  // namespace android_webview