Chromium Code Reviews| Index: services/ui/ws/gpu_host_unittest.cc |
| diff --git a/services/ui/ws/gpu_host_unittest.cc b/services/ui/ws/gpu_host_unittest.cc |
| new file mode 100644 |
| index 0000000000000000000000000000000000000000..c6804e77a828d686994e70c3deb5137a2be9dd9d |
| --- /dev/null |
| +++ b/services/ui/ws/gpu_host_unittest.cc |
| @@ -0,0 +1,201 @@ |
| +// Copyright 2017 The Chromium Authors. All rights reserved. |
| +// Use of this source code is governed by a BSD-style license that can be |
| +// found in the LICENSE file. |
| + |
| +#include "services/ui/ws/gpu_host.h" |
| + |
| +#include "base/macros.h" |
| +#include "base/memory/ptr_util.h" |
| +#include "base/memory/ref_counted.h" |
| +#include "base/memory/weak_ptr.h" |
| +#include "base/message_loop/message_loop.h" |
| +#include "base/test/test_simple_task_runner.h" |
| +#include "gpu/config/gpu_info.h" |
| +#include "gpu/ipc/service/gpu_watchdog_thread.h" |
| +#include "services/ui/gpu/gpu_service.h" |
| +#include "services/ui/public/interfaces/gpu.mojom.h" |
| +#include "services/ui/ws/gpu_host_delegate.h" |
| +#include "testing/gtest/include/gtest/gtest.h" |
| + |
| +namespace ui { |
| +namespace ws { |
| +namespace test { |
| +namespace { |
| + |
| +// No-opt implementation of GpuHostDelegate. |
| +class TestGpuHostDelegate : public GpuHostDelegate { |
| + public: |
| + TestGpuHostDelegate() {} |
| + ~TestGpuHostDelegate() override {} |
| + |
| + void OnGpuServiceInitialized() override {} |
| + |
| + private: |
| + DISALLOW_COPY_AND_ASSIGN(TestGpuHostDelegate); |
| +}; |
| + |
| +// Test implementation of GpuService. For testing behaviour of calls made by |
| +// GpuHost::GpuClient |
| +class TestGpuService : public GpuService { |
| + public: |
| + TestGpuService(scoped_refptr<base::TestSimpleTaskRunner> task_runner); |
| + ~TestGpuService() override {} |
| + |
| + bool establish_gpu_called() { return establish_gpu_called_; } |
| + |
| + // Sets callback for providing test implementation of EstablishGpuChannel/ |
| + void SetEstablishGpuChannelTestCallback(base::Closure callback) { |
| + establish_gpu_channel_callback_ = callback; |
| + } |
| + |
| + // GpuService: |
| + void EstablishGpuChannel( |
| + int32_t client_id, |
| + uint64_t client_tracing_id, |
| + bool is_gpu_host, |
| + const EstablishGpuChannelCallback& callback) override; |
| + |
| + private: |
| + base::Closure establish_gpu_channel_callback_; |
| + |
| + bool establish_gpu_called_ = false; |
| + |
| + DISALLOW_COPY_AND_ASSIGN(TestGpuService); |
| +}; |
| + |
| +TestGpuService::TestGpuService( |
| + scoped_refptr<base::TestSimpleTaskRunner> task_runner) |
| + : GpuService(gpu::GPUInfo(), |
| + gpu::GpuWatchdogThread::Create(), |
| + nullptr, |
| + task_runner, |
| + gpu::GpuFeatureInfo()) {} |
| + |
| +void TestGpuService::EstablishGpuChannel( |
| + int32_t client_id, |
| + uint64_t client_tracing_id, |
| + bool is_gpu_host, |
| + const EstablishGpuChannelCallback& callback) { |
| + establish_gpu_called_ = true; |
| + EXPECT_FALSE(establish_gpu_channel_callback_.is_null()); |
| + EXPECT_FALSE(callback.is_null()); |
| + EXPECT_FALSE(callback.IsCancelled()); |
| + establish_gpu_channel_callback_.Run(); |
| + // Calling should not lead to a crash. |
| + callback.Run(mojo::ScopedMessagePipeHandle()); |
| +} |
| + |
| +// While provide to GpuClient as a callback, actually calling into this during |
| +// the test would lead to a use-after-free. This will explicitly crash to detect |
| +// the error on non-asan builds. |
| +void CrashOnCallback(int32_t client_id, |
| + mojo::ScopedMessagePipeHandle channel_handle, |
| + const gpu::GPUInfo& gpu_info) { |
| + CHECK(false); |
| +} |
| + |
| +} // namespace |
| + |
| +class GpuHostTest : public testing::Test { |
| + public: |
| + GpuHostTest(); |
| + ~GpuHostTest() override {} |
| + |
| + GpuHost* gpu_host() { return gpu_host_.get(); } |
| + |
| + // Implementations for corresponding tests. |
| + void TestGpuClientDestructionOrder(); |
| + void TestHostDeletionInvalidatesGpuClientCallback(); |
| + |
| + // Deletes |gpu_host_| and verifies that |client_ref_| has cleaned up. |
| + void DeleteHostAndVerifyClientDeleted(); |
| + |
| + // testing::Test |
| + void SetUp() override; |
| + |
| + private: |
| + base::MessageLoop message_loop_; |
| + scoped_refptr<base::TestSimpleTaskRunner> task_runner_; |
| + |
| + base::WeakPtr<GpuHost::GpuClient> client_ref_; |
| + |
| + TestGpuHostDelegate gpu_host_delegate_; |
| + TestGpuService gpu_service_; |
| + ui::mojom::GpuServicePtr gpu_service_ptr_; |
| + std::unique_ptr<GpuHost> gpu_host_; |
| + |
| + DISALLOW_COPY_AND_ASSIGN(GpuHostTest); |
| +}; |
| + |
| +GpuHostTest::GpuHostTest() |
| + : task_runner_(new base::TestSimpleTaskRunner), gpu_service_(task_runner_) { |
|
sadrul
2017/03/22 16:42:04
Is there a reason to set this task runner? Can you
jonross
2017/03/23 00:38:29
Allows for manually advancing posted tasks in an i
|
| + message_loop_.SetTaskRunner(task_runner_); |
| +} |
| + |
| +void GpuHostTest::TestGpuClientDestructionOrder() { |
| + mojom::GpuRequest request; |
| + GpuHost::GpuClient* client = static_cast<GpuHost::GpuClient*>( |
| + gpu_host_->AddInternal(std::move(request))); |
| + client_ref_ = client->AsWeakPtr(); |
| + // Closing the host should shutdown the GpuClient. |
| + gpu_host_.reset(); |
| + // If |client_ref| is not deleted the subsequent call will reproduce the |
| + // use-after-free of GpuHost's GpuInfo. |
| + if (client_ref_) { |
| + // Passing in a callback which crashes to identify the error on non-asan |
| + // builds. |
| + client_ref_->OnGpuChannelEstablished(base::Bind(&CrashOnCallback), |
| + mojo::ScopedMessagePipeHandle()); |
| + } |
| + EXPECT_EQ(nullptr, client_ref_); |
|
sadrul
2017/03/22 16:41:13
This the expectation, right? i.e. when GpuHost is
jonross
2017/03/22 16:49:43
I had originally used the CrashOnCallback to confi
jonross
2017/03/23 00:38:29
Done.
|
| +} |
| + |
| +void GpuHostTest::TestHostDeletionInvalidatesGpuClientCallback() { |
| + mojom::GpuRequest request; |
| + GpuHost::GpuClient* client = static_cast<GpuHost::GpuClient*>( |
| + gpu_host_->AddInternal(std::move(request))); |
|
sadrul
2017/03/22 16:41:13
Make the return type GpuClient* so you don't need
jonross
2017/03/23 00:38:29
Done.
|
| + client_ref_ = client->AsWeakPtr(); |
| + |
| + // Second stage of test is DeleteHostAndVerifyClientDeleted invoked by |
| + // TestGpuService. |
| + gpu_service_.SetEstablishGpuChannelTestCallback(base::Bind( |
| + &GpuHostTest::DeleteHostAndVerifyClientDeleted, base::Unretained(this))); |
| + client->EstablishGpuChannel(base::Bind(&CrashOnCallback)); |
| + // TestGpuService will be notified of above in a posted task. |
| + task_runner_->RunPendingTasks(); |
| + EXPECT_TRUE(gpu_service_.establish_gpu_called()); |
|
sadrul
2017/03/22 16:41:13
This is still essentially testing that destroying
jonross
2017/03/22 16:49:43
The other test is a simple verification of that.
jonross
2017/03/23 00:38:29
Done.
|
| +} |
| + |
| +void GpuHostTest::DeleteHostAndVerifyClientDeleted() { |
| + EXPECT_NE(nullptr, client_ref_); |
| + gpu_host_.reset(); |
| + task_runner_->RunPendingTasks(); |
| + // Verifies cleanup of the GpuClient, which prevents callbacks from resolving. |
| + EXPECT_EQ(nullptr, client_ref_); |
| +} |
| + |
| +void GpuHostTest::SetUp() { |
| + testing::Test::SetUp(); |
| + gpu_host_ = base::MakeUnique<GpuHost>(&gpu_host_delegate_); |
| + |
| + ui::mojom::GpuServiceRequest request(&gpu_service_ptr_); |
| + gpu_service_.Bind(std::move(request)); |
| + gpu_host_->gpu_service_ = std::move(gpu_service_ptr_); |
| +} |
| + |
| +// Tests to verify, that if a GpuHost is deleted before GpuClient receives a |
| +// callback, that GpuClient is torn down and does not attempt to use GpuInfo |
| +// after deletion. This should not crash on asan-builds. |
| +TEST_F(GpuHostTest, GpuClientDestructionOrder) { |
| + TestGpuClientDestructionOrder(); |
|
sadrul
2017/03/22 16:41:13
Why a separate function for each test?
jonross
2017/03/22 16:49:43
They each are calling several private methods.
I c
jonross
2017/03/23 00:38:29
Actually the reduced test has less requirements of
|
| +} |
| + |
| +// Tests that if GpuHost is deleted that callbacks provided by GpuClient are |
| +// invalidated and do not cause crashes. |
| +TEST_F(GpuHostTest, HostDeletionInvalidatesGpuClientCallback) { |
| + TestHostDeletionInvalidatesGpuClientCallback(); |
| +} |
| + |
| +} // namespace test |
| +} // namespace ws |
| +} // namespace ui |