chromium/components/viz/service/display_embedder/software_output_device_win_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 "components/viz/service/display_embedder/software_output_device_win.h"

#include "base/memory/unsafe_shared_memory_region.h"
#include "base/test/gmock_callback_support.h"
#include "components/viz/common/resources/resource_sizes.h"
#include "mojo/public/cpp/bindings/receiver.h"
#include "services/viz/privileged/mojom/compositing/layered_window_updater.mojom.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "ui/gfx/frame_data.h"

using base::test::RunOnceClosure;
using testing::_;

namespace viz {
namespace {

constexpr gfx::Size kDefaultSize(100, 100);

class MockLayeredWindowUpdater : public mojom::LayeredWindowUpdater {
 public:
  MockLayeredWindowUpdater() = default;
  ~MockLayeredWindowUpdater() override = default;

  mojo::PendingRemote<mojom::LayeredWindowUpdater> BindNewPipeAndPassRemote() {
    return receiver_.BindNewPipeAndPassRemote();
  }

  void FlushForTesting() { receiver_.FlushForTesting(); }

  // mojom::LayeredWindowUpdater implementation.
  MOCK_METHOD(void,
              OnAllocatedSharedMemory,
              (const gfx::Size& size,
               base::UnsafeSharedMemoryRegion memory_region),
              (override));
  MOCK_METHOD(void, Draw, (DrawCallback), (override));

 private:
  mojo::Receiver<mojom::LayeredWindowUpdater> receiver_{this};
};

}  // namespace

class SoftwareOutputDeviceWinProxyTest : public testing::Test {
 public:
  SoftwareOutputDeviceWinProxyTest()
      : device_(0, updater_.BindNewPipeAndPassRemote()) {}

  void SetUp() override {
    // Check that calling Resize() results in the allocation of shared memory
    // and triggers the OnAllocatedSharedMemory() IPC.
    EXPECT_CALL(updater_, OnAllocatedSharedMemory(kDefaultSize, _))
        .WillOnce(
            testing::WithArg<1>([](base::UnsafeSharedMemoryRegion region) {
              EXPECT_TRUE(region.IsValid());
              size_t required_bytes = ResourceSizes::CheckedSizeInBytes<size_t>(
                  kDefaultSize, SinglePlaneFormat::kRGBA_8888);
              EXPECT_GE(region.GetSize(), required_bytes);
            }));
    device_.Resize(kDefaultSize, 1.0f);
    updater_.FlushForTesting();
    testing::Mock::VerifyAndClearExpectations(&updater_);
  }

 protected:
  MockLayeredWindowUpdater updater_;
  SoftwareOutputDeviceWinProxy device_;
};

TEST_F(SoftwareOutputDeviceWinProxyTest, DrawWithSwap) {
  // Verify that calling BeginPaint() then EndPaint() triggers the Draw() IPC
  // and then run the DrawCallback.
  device_.BeginPaint(gfx::Rect(kDefaultSize));
  EXPECT_CALL(updater_, Draw(_)).WillOnce(RunOnceClosure<0>());
  device_.EndPaint();
  updater_.FlushForTesting();
  testing::Mock::VerifyAndClearExpectations(&updater_);

  // OnSwapBuffers() is called before DrawAck() so the swap buffers callback
  // shouldn't run yet.
  bool called = false;
  device_.OnSwapBuffers(
      base::BindOnce([](bool* val, const gfx::Size& size) { *val = true; },
                     &called),
      gfx::FrameData());
  EXPECT_FALSE(called);

  // Verify that DrawAck() runs the swap buffers callback.
  updater_.FlushForTesting();
  EXPECT_TRUE(called);
}

TEST_F(SoftwareOutputDeviceWinProxyTest, DrawNoSwap) {
  // Verify that calling BeginPaint() then EndPaint() triggers the Draw() IPC
  // and then run the DrawCallback.
  device_.BeginPaint(gfx::Rect(kDefaultSize));
  EXPECT_CALL(updater_, Draw(_)).WillOnce(RunOnceClosure<0>());
  device_.EndPaint();
  updater_.FlushForTesting();
  testing::Mock::VerifyAndClearExpectations(&updater_);

  // DrawAck() will be triggered after this second flush when the IPC response
  // arrives. OnSwapBuffers() was never called which is allowed.
  updater_.FlushForTesting();
}

}  // namespace viz