#include "services/viz/public/cpp/gpu/gpu.h"
#include <memory>
#include <utility>
#include "base/functional/bind.h"
#include "base/functional/callback_helpers.h"
#include "base/memory/ptr_util.h"
#include "base/message_loop/message_pump_type.h"
#include "base/run_loop.h"
#include "base/synchronization/waitable_event.h"
#include "base/test/task_environment.h"
#include "base/threading/thread.h"
#include "build/build_config.h"
#include "gpu/command_buffer/common/shared_image_capabilities.h"
#include "gpu/config/gpu_feature_info.h"
#include "gpu/config/gpu_info.h"
#include "mojo/public/cpp/bindings/pending_receiver.h"
#include "mojo/public/cpp/bindings/pending_remote.h"
#include "mojo/public/cpp/bindings/receiver_set.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace viz {
namespace {
class TestGpuImpl : public mojom::Gpu {
public:
TestGpuImpl() = default;
TestGpuImpl(const TestGpuImpl&) = delete;
TestGpuImpl& operator=(const TestGpuImpl&) = delete;
~TestGpuImpl() override = default;
void SetRequestWillSucceed(bool request_will_succeed) {
request_will_succeed_ = request_will_succeed;
}
void CloseBindingOnRequest() { close_binding_on_request_ = true; }
void BindReceiver(mojo::PendingReceiver<mojom::Gpu> receiver) {
receivers_.Add(this, std::move(receiver));
}
void EstablishGpuChannel(EstablishGpuChannelCallback callback) override {
if (close_binding_on_request_) {
receivers_.Clear();
return;
}
constexpr int client_id = 1;
mojo::ScopedMessagePipeHandle handle;
if (request_will_succeed_) {
mojo::MessagePipe message_pipe;
handle = std::move(message_pipe.handle0);
gpu_channel_handle_ = std::move(message_pipe.handle1);
}
std::move(callback).Run(client_id, std::move(handle), gpu::GPUInfo(),
gpu::GpuFeatureInfo(),
gpu::SharedImageCapabilities());
}
#if BUILDFLAG(IS_CHROMEOS)
void CreateJpegDecodeAccelerator(
mojo::PendingReceiver<chromeos_camera::mojom::MjpegDecodeAccelerator>
jda_receiver) override {}
#endif
void CreateVideoEncodeAcceleratorProvider(
mojo::PendingReceiver<media::mojom::VideoEncodeAcceleratorProvider>
receiver) override {}
private:
bool request_will_succeed_ = true;
bool close_binding_on_request_ = false;
mojo::ReceiverSet<mojom::Gpu> receivers_;
mojo::ScopedMessagePipeHandle gpu_channel_handle_;
};
}
class GpuTest : public testing::Test {
public:
GpuTest() : io_thread_("GPUIOThread") {
base::Thread::Options thread_options(base::MessagePumpType::IO, 0);
thread_options.thread_type = base::ThreadType::kDefault;
CHECK(io_thread_.StartWithOptions(std::move(thread_options)));
}
GpuTest(const GpuTest&) = delete;
GpuTest& operator=(const GpuTest&) = delete;
~GpuTest() override = default;
Gpu* gpu() { return gpu_.get(); }
TestGpuImpl* gpu_impl() { return gpu_impl_.get(); }
void FlushMainAndIO() {
base::RunLoop run_loop;
io_thread_.task_runner()->PostTask(
FROM_HERE,
base::BindOnce(
[](base::OnceClosure callback) { std::move(callback).Run(); },
run_loop.QuitClosure()));
run_loop.Run();
}
void BlockMainFlushIO() {
base::WaitableEvent event(base::WaitableEvent::ResetPolicy::AUTOMATIC,
base::WaitableEvent::InitialState::NOT_SIGNALED);
io_thread_.task_runner()->PostTask(
FROM_HERE,
base::BindOnce(
[](base::WaitableEvent* event) {
base::RunLoop(base::RunLoop::Type::kNestableTasksAllowed)
.RunUntilIdle();
event->Signal();
},
base::Unretained(&event)));
event.Wait();
}
void DestroyGpu() { gpu_.reset(); }
void SetUp() override {
gpu_impl_ = std::make_unique<TestGpuImpl>();
gpu_ = base::WrapUnique(new Gpu(GetRemote(), io_thread_.task_runner()));
}
void TearDown() override {
FlushMainAndIO();
DestroyGpuImplOnIO();
}
private:
mojo::PendingRemote<mojom::Gpu> GetRemote() {
mojo::PendingRemote<mojom::Gpu> remote;
io_thread_.task_runner()->PostTask(
FROM_HERE, base::BindOnce(&TestGpuImpl::BindReceiver,
base::Unretained(gpu_impl_.get()),
remote.InitWithNewPipeAndPassReceiver()));
return remote;
}
void DestroyGpuImplOnIO() {
base::WaitableEvent event(base::WaitableEvent::ResetPolicy::AUTOMATIC,
base::WaitableEvent::InitialState::NOT_SIGNALED);
io_thread_.task_runner()->PostTask(
FROM_HERE, base::BindOnce(
[](std::unique_ptr<TestGpuImpl> gpu_impl,
base::WaitableEvent* event) {
gpu_impl.reset();
event->Signal();
},
std::move(gpu_impl_), &event));
event.Wait();
}
base::test::TaskEnvironment env_;
base::Thread io_thread_;
std::unique_ptr<Gpu> gpu_;
std::unique_ptr<TestGpuImpl> gpu_impl_;
};
TEST_F(GpuTest, EstablishRequestsQueued) {
int counter = 2;
base::RunLoop run_loop;
auto callback = base::BindRepeating(
[](int* counter, base::OnceClosure callback,
scoped_refptr<gpu::GpuChannelHost> channel) {
EXPECT_TRUE(channel);
--(*counter);
if (*counter == 0)
std::move(callback).Run();
},
&counter, run_loop.QuitClosure());
gpu()->EstablishGpuChannel(callback);
gpu()->EstablishGpuChannel(callback);
EXPECT_EQ(2, counter);
run_loop.Run();
EXPECT_EQ(0, counter);
}
TEST_F(GpuTest, EstablishRequestOnFailureOnPreviousRequest) {
gpu_impl()->SetRequestWillSucceed(false);
base::RunLoop run_loop;
scoped_refptr<gpu::GpuChannelHost> host;
auto callback = base::BindOnce(
[](scoped_refptr<gpu::GpuChannelHost>* out_host,
base::OnceClosure callback, scoped_refptr<gpu::GpuChannelHost> host) {
std::move(callback).Run();
*out_host = std::move(host);
},
&host, run_loop.QuitClosure());
gpu()->EstablishGpuChannel(base::BindOnce(
[](Gpu* gpu, TestGpuImpl* gpu_impl,
gpu::GpuChannelEstablishedCallback callback,
scoped_refptr<gpu::GpuChannelHost> host) {
EXPECT_FALSE(host);
gpu_impl->SetRequestWillSucceed(true);
gpu->EstablishGpuChannel(std::move(callback));
},
gpu(), gpu_impl(), std::move(callback)));
run_loop.Run();
EXPECT_TRUE(host);
}
TEST_F(GpuTest, EstablishRequestResponseSynchronouslyOnSuccess) {
base::RunLoop run_loop;
gpu()->EstablishGpuChannel(base::BindOnce(
[](base::OnceClosure callback, scoped_refptr<gpu::GpuChannelHost> host) {
EXPECT_TRUE(host);
std::move(callback).Run();
},
run_loop.QuitClosure()));
run_loop.Run();
int counter = 0;
auto callback = base::BindOnce(
[](int* counter, scoped_refptr<gpu::GpuChannelHost> host) {
EXPECT_TRUE(host);
++(*counter);
},
&counter);
gpu()->EstablishGpuChannel(std::move(callback));
EXPECT_EQ(1, counter);
}
TEST_F(GpuTest, EstablishRequestAsyncThenSync) {
int counter = 0;
gpu()->EstablishGpuChannel(base::BindOnce(
[](int* counter, scoped_refptr<gpu::GpuChannelHost> host) {
EXPECT_TRUE(host);
++(*counter);
},
base::Unretained(&counter)));
scoped_refptr<gpu::GpuChannelHost> host = gpu()->EstablishGpuChannelSync();
EXPECT_EQ(1, counter);
EXPECT_TRUE(host);
}
TEST_F(GpuTest, SyncConnectionError) {
gpu_impl()->CloseBindingOnRequest();
scoped_refptr<gpu::GpuChannelHost> channel = gpu()->EstablishGpuChannelSync();
EXPECT_FALSE(channel);
channel = gpu()->EstablishGpuChannelSync();
EXPECT_FALSE(channel);
}
TEST_F(GpuTest, AsyncConnectionError) {
gpu_impl()->CloseBindingOnRequest();
int counter = 2;
base::RunLoop run_loop;
auto callback = base::BindRepeating(
[](int* counter, const base::RepeatingClosure& callback,
scoped_refptr<gpu::GpuChannelHost> channel) {
EXPECT_FALSE(channel);
--(*counter);
if (*counter == 0)
callback.Run();
},
&counter, run_loop.QuitClosure());
gpu()->EstablishGpuChannel(callback);
gpu()->EstablishGpuChannel(callback);
EXPECT_EQ(2, counter);
run_loop.Run();
EXPECT_EQ(0, counter);
}
TEST_F(GpuTest, EstablishRequestAsyncThenSyncWithResponse) {
int counter = 0;
gpu()->EstablishGpuChannel(base::BindOnce(
[](int* counter, scoped_refptr<gpu::GpuChannelHost> host) {
EXPECT_TRUE(host);
++(*counter);
},
base::Unretained(&counter)));
BlockMainFlushIO();
EXPECT_EQ(0, counter);
scoped_refptr<gpu::GpuChannelHost> host = gpu()->EstablishGpuChannelSync();
EXPECT_EQ(1, counter);
EXPECT_TRUE(host);
}
TEST_F(GpuTest, DestroyGpuWithPendingRequest) {
int counter = 0;
gpu()->EstablishGpuChannel(base::BindOnce(
[](int* counter, scoped_refptr<gpu::GpuChannelHost> host) {
++(*counter);
},
base::Unretained(&counter)));
DestroyGpu();
FlushMainAndIO();
EXPECT_EQ(0, counter);
}
}