summaryrefslogtreecommitdiff
path: root/chromium/media/capture/video/chromeos/ash/camera_hal_dispatcher_impl_unittest.cc
diff options
context:
space:
mode:
Diffstat (limited to 'chromium/media/capture/video/chromeos/ash/camera_hal_dispatcher_impl_unittest.cc')
-rw-r--r--chromium/media/capture/video/chromeos/ash/camera_hal_dispatcher_impl_unittest.cc423
1 files changed, 423 insertions, 0 deletions
diff --git a/chromium/media/capture/video/chromeos/ash/camera_hal_dispatcher_impl_unittest.cc b/chromium/media/capture/video/chromeos/ash/camera_hal_dispatcher_impl_unittest.cc
new file mode 100644
index 00000000000..933d0e45440
--- /dev/null
+++ b/chromium/media/capture/video/chromeos/ash/camera_hal_dispatcher_impl_unittest.cc
@@ -0,0 +1,423 @@
+// 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 "media/capture/video/chromeos/ash/camera_hal_dispatcher_impl.h"
+
+#include <memory>
+#include <utility>
+
+#include "base/bind.h"
+#include "base/posix/safe_strerror.h"
+#include "base/run_loop.h"
+#include "base/single_thread_task_runner.h"
+#include "base/synchronization/waitable_event.h"
+#include "base/test/task_environment.h"
+#include "media/capture/video/chromeos/mojom/camera_common.mojom.h"
+#include "media/capture/video/chromeos/mojom/cros_camera_service.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 "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using testing::_;
+using testing::InvokeWithoutArgs;
+
+namespace media {
+namespace {
+
+class MockCameraHalServer : public cros::mojom::CameraHalServer {
+ public:
+ MockCameraHalServer() = default;
+
+ ~MockCameraHalServer() = default;
+
+ void CreateChannel(
+ mojo::PendingReceiver<cros::mojom::CameraModule> camera_module_receiver,
+ cros::mojom::CameraClientType camera_client_type) override {
+ DoCreateChannel(std::move(camera_module_receiver), camera_client_type);
+ }
+ MOCK_METHOD2(DoCreateChannel,
+ void(mojo::PendingReceiver<cros::mojom::CameraModule>
+ camera_module_receiver,
+ cros::mojom::CameraClientType camera_client_type));
+
+ MOCK_METHOD1(SetTracingEnabled, void(bool enabled));
+
+ mojo::PendingRemote<cros::mojom::CameraHalServer> GetPendingRemote() {
+ return receiver_.BindNewPipeAndPassRemote();
+ }
+
+ private:
+ mojo::Receiver<cros::mojom::CameraHalServer> receiver_{this};
+ DISALLOW_COPY_AND_ASSIGN(MockCameraHalServer);
+};
+
+class MockCameraHalClient : public cros::mojom::CameraHalClient {
+ public:
+ MockCameraHalClient() = default;
+
+ ~MockCameraHalClient() = default;
+
+ void SetUpChannel(
+ mojo::PendingRemote<cros::mojom::CameraModule> camera_module) override {
+ DoSetUpChannel(std::move(camera_module));
+ }
+ MOCK_METHOD1(
+ DoSetUpChannel,
+ void(mojo::PendingRemote<cros::mojom::CameraModule> camera_module));
+
+ mojo::PendingRemote<cros::mojom::CameraHalClient> GetPendingRemote() {
+ return receiver_.BindNewPipeAndPassRemote();
+ }
+
+ private:
+ mojo::Receiver<cros::mojom::CameraHalClient> receiver_{this};
+ DISALLOW_COPY_AND_ASSIGN(MockCameraHalClient);
+};
+
+class MockCameraActiveClientObserver : public CameraActiveClientObserver {
+ public:
+ void OnActiveClientChange(cros::mojom::CameraClientType type,
+ bool is_active) override {
+ DoOnActiveClientChange(type, is_active);
+ }
+ MOCK_METHOD2(DoOnActiveClientChange,
+ void(cros::mojom::CameraClientType, bool));
+};
+
+} // namespace
+
+class CameraHalDispatcherImplTest : public ::testing::Test {
+ public:
+ CameraHalDispatcherImplTest()
+ : register_client_event_(base::WaitableEvent::ResetPolicy::AUTOMATIC) {}
+
+ ~CameraHalDispatcherImplTest() override = default;
+
+ void SetUp() override {
+ dispatcher_ = new CameraHalDispatcherImpl();
+ EXPECT_TRUE(dispatcher_->StartThreads());
+ }
+
+ void TearDown() override { delete dispatcher_; }
+
+ scoped_refptr<base::SingleThreadTaskRunner> GetProxyTaskRunner() {
+ return dispatcher_->proxy_task_runner_;
+ }
+
+ void DoLoop() {
+ run_loop_.reset(new base::RunLoop());
+ run_loop_->Run();
+ }
+
+ void QuitRunLoop() {
+ if (run_loop_) {
+ run_loop_->Quit();
+ }
+ }
+
+ static void RegisterServer(
+ CameraHalDispatcherImpl* dispatcher,
+ mojo::PendingRemote<cros::mojom::CameraHalServer> server,
+ cros::mojom::CameraHalDispatcher::RegisterServerWithTokenCallback
+ callback) {
+ auto token = base::UnguessableToken::Create();
+ dispatcher->GetTokenManagerForTesting()->AssignServerTokenForTesting(token);
+ dispatcher->RegisterServerWithToken(std::move(server), std::move(token),
+ std::move(callback));
+ }
+
+ static void RegisterClientWithToken(
+ CameraHalDispatcherImpl* dispatcher,
+ mojo::PendingRemote<cros::mojom::CameraHalClient> client,
+ cros::mojom::CameraClientType type,
+ const base::UnguessableToken& token,
+ cros::mojom::CameraHalDispatcher::RegisterClientWithTokenCallback
+ callback) {
+ dispatcher->RegisterClientWithToken(std::move(client), type, token,
+ std::move(callback));
+ }
+
+ void OnRegisteredServer(
+ int32_t result,
+ mojo::PendingRemote<cros::mojom::CameraHalServerCallbacks> callbacks) {
+ if (result != 0) {
+ ADD_FAILURE() << "Failed to register server: "
+ << base::safe_strerror(-result);
+ QuitRunLoop();
+ }
+ }
+
+ void OnRegisteredClient(int32_t result) {
+ last_register_client_result_ = result;
+ if (result != 0) {
+ // If registration fails, CameraHalClient::SetUpChannel() will not be
+ // called, and we need to quit the run loop here.
+ QuitRunLoop();
+ }
+ register_client_event_.Signal();
+ }
+
+ protected:
+ // We can't use std::unique_ptr here because the constructor and destructor of
+ // CameraHalDispatcherImpl are private.
+ CameraHalDispatcherImpl* dispatcher_;
+ base::WaitableEvent register_client_event_;
+ int32_t last_register_client_result_;
+
+ private:
+ base::test::TaskEnvironment task_environment_;
+ std::unique_ptr<base::RunLoop> run_loop_;
+ DISALLOW_COPY_AND_ASSIGN(CameraHalDispatcherImplTest);
+};
+
+// Test that the CameraHalDisptcherImpl correctly re-establishes a Mojo channel
+// for the client when the server crashes.
+TEST_F(CameraHalDispatcherImplTest, ServerConnectionError) {
+ // First verify that a the CameraHalDispatcherImpl establishes a Mojo channel
+ // between the server and the client.
+ auto mock_server = std::make_unique<MockCameraHalServer>();
+ auto mock_client = std::make_unique<MockCameraHalClient>();
+
+ EXPECT_CALL(*mock_server, DoCreateChannel(_, _)).Times(1);
+ EXPECT_CALL(*mock_client, DoSetUpChannel(_))
+ .Times(1)
+ .WillOnce(
+ InvokeWithoutArgs(this, &CameraHalDispatcherImplTest::QuitRunLoop));
+
+ auto server = mock_server->GetPendingRemote();
+ GetProxyTaskRunner()->PostTask(
+ FROM_HERE,
+ base::BindOnce(
+ &CameraHalDispatcherImplTest::RegisterServer,
+ base::Unretained(dispatcher_), std::move(server),
+ base::BindOnce(&CameraHalDispatcherImplTest::OnRegisteredServer,
+ base::Unretained(this))));
+ auto client = mock_client->GetPendingRemote();
+ auto type = cros::mojom::CameraClientType::TESTING;
+ GetProxyTaskRunner()->PostTask(
+ FROM_HERE,
+ base::BindOnce(
+ &CameraHalDispatcherImplTest::RegisterClientWithToken,
+ base::Unretained(dispatcher_), std::move(client), type,
+ dispatcher_->GetTokenForTrustedClient(type),
+ base::BindOnce(&CameraHalDispatcherImplTest::OnRegisteredClient,
+ base::Unretained(this))));
+
+ // Wait until the client gets the established Mojo channel.
+ DoLoop();
+
+ // The client registration callback may be called after
+ // CameraHalClient::SetUpChannel(). Use a waitable event to make sure we have
+ // the result.
+ register_client_event_.Wait();
+ ASSERT_EQ(last_register_client_result_, 0);
+
+ // Re-create a new server to simulate a server crash.
+ mock_server = std::make_unique<MockCameraHalServer>();
+
+ // Make sure we creates a new Mojo channel from the new server to the same
+ // client.
+ EXPECT_CALL(*mock_server, DoCreateChannel(_, _)).Times(1);
+ EXPECT_CALL(*mock_client, DoSetUpChannel(_))
+ .Times(1)
+ .WillOnce(
+ InvokeWithoutArgs(this, &CameraHalDispatcherImplTest::QuitRunLoop));
+
+ server = mock_server->GetPendingRemote();
+ GetProxyTaskRunner()->PostTask(
+ FROM_HERE,
+ base::BindOnce(
+ &CameraHalDispatcherImplTest::RegisterServer,
+ base::Unretained(dispatcher_), std::move(server),
+ base::BindOnce(&CameraHalDispatcherImplTest::OnRegisteredServer,
+ base::Unretained(this))));
+
+ // Wait until the clients gets the newly established Mojo channel.
+ DoLoop();
+}
+
+// Test that the CameraHalDisptcherImpl correctly re-establishes a Mojo channel
+// for the client when the client reconnects after crash.
+TEST_F(CameraHalDispatcherImplTest, ClientConnectionError) {
+ // First verify that a the CameraHalDispatcherImpl establishes a Mojo channel
+ // between the server and the client.
+ auto mock_server = std::make_unique<MockCameraHalServer>();
+ auto mock_client = std::make_unique<MockCameraHalClient>();
+
+ EXPECT_CALL(*mock_server, DoCreateChannel(_, _)).Times(1);
+ EXPECT_CALL(*mock_client, DoSetUpChannel(_))
+ .Times(1)
+ .WillOnce(
+ InvokeWithoutArgs(this, &CameraHalDispatcherImplTest::QuitRunLoop));
+
+ auto server = mock_server->GetPendingRemote();
+ GetProxyTaskRunner()->PostTask(
+ FROM_HERE,
+ base::BindOnce(
+ &CameraHalDispatcherImplTest::RegisterServer,
+ base::Unretained(dispatcher_), std::move(server),
+ base::BindOnce(&CameraHalDispatcherImplTest::OnRegisteredServer,
+ base::Unretained(this))));
+ auto client = mock_client->GetPendingRemote();
+ auto type = cros::mojom::CameraClientType::TESTING;
+ GetProxyTaskRunner()->PostTask(
+ FROM_HERE,
+ base::BindOnce(
+ &CameraHalDispatcherImplTest::RegisterClientWithToken,
+ base::Unretained(dispatcher_), std::move(client), type,
+ dispatcher_->GetTokenForTrustedClient(type),
+ base::BindOnce(&CameraHalDispatcherImplTest::OnRegisteredClient,
+ base::Unretained(this))));
+
+ // Wait until the client gets the established Mojo channel.
+ DoLoop();
+
+ // The client registration callback may be called after
+ // CameraHalClient::SetUpChannel(). Use a waitable event to make sure we have
+ // the result.
+ register_client_event_.Wait();
+ ASSERT_EQ(last_register_client_result_, 0);
+
+ // Re-create a new client to simulate a client crash.
+ mock_client = std::make_unique<MockCameraHalClient>();
+
+ // Make sure we re-create the Mojo channel from the same server to the new
+ // client.
+ EXPECT_CALL(*mock_server, DoCreateChannel(_, _)).Times(1);
+ EXPECT_CALL(*mock_client, DoSetUpChannel(_))
+ .Times(1)
+ .WillOnce(
+ InvokeWithoutArgs(this, &CameraHalDispatcherImplTest::QuitRunLoop));
+
+ client = mock_client->GetPendingRemote();
+ type = cros::mojom::CameraClientType::TESTING;
+ GetProxyTaskRunner()->PostTask(
+ FROM_HERE,
+ base::BindOnce(
+ &CameraHalDispatcherImplTest::RegisterClientWithToken,
+ base::Unretained(dispatcher_), std::move(client), type,
+ dispatcher_->GetTokenForTrustedClient(type),
+ base::BindOnce(&CameraHalDispatcherImplTest::OnRegisteredClient,
+ base::Unretained(this))));
+
+ // Wait until the clients gets the newly established Mojo channel.
+ DoLoop();
+
+ // Make sure the client is still successfully registered.
+ register_client_event_.Wait();
+ ASSERT_EQ(last_register_client_result_, 0);
+}
+
+// Test that trusted camera HAL clients (e.g., Chrome, Android, Testing) can be
+// registered successfully.
+TEST_F(CameraHalDispatcherImplTest, RegisterClientSuccess) {
+ // First verify that a the CameraHalDispatcherImpl establishes a Mojo channel
+ // between the server and the client.
+ auto mock_server = std::make_unique<MockCameraHalServer>();
+
+ auto server = mock_server->GetPendingRemote();
+ GetProxyTaskRunner()->PostTask(
+ FROM_HERE,
+ base::BindOnce(
+ &CameraHalDispatcherImplTest::RegisterServer,
+ base::Unretained(dispatcher_), std::move(server),
+ base::BindOnce(&CameraHalDispatcherImplTest::OnRegisteredServer,
+ base::Unretained(this))));
+
+ for (auto type : TokenManager::kTrustedClientTypes) {
+ auto mock_client = std::make_unique<MockCameraHalClient>();
+ EXPECT_CALL(*mock_server, DoCreateChannel(_, _)).Times(1);
+ EXPECT_CALL(*mock_client, DoSetUpChannel(_))
+ .Times(1)
+ .WillOnce(
+ InvokeWithoutArgs(this, &CameraHalDispatcherImplTest::QuitRunLoop));
+
+ auto client = mock_client->GetPendingRemote();
+ GetProxyTaskRunner()->PostTask(
+ FROM_HERE,
+ base::BindOnce(
+ &CameraHalDispatcherImplTest::RegisterClientWithToken,
+ base::Unretained(dispatcher_), std::move(client), type,
+ dispatcher_->GetTokenForTrustedClient(type),
+ base::BindOnce(&CameraHalDispatcherImplTest::OnRegisteredClient,
+ base::Unretained(this))));
+
+ // Wait until the client gets the established Mojo channel.
+ DoLoop();
+
+ // The client registration callback may be called after
+ // CameraHalClient::SetUpChannel(). Use a waitable event to make sure we
+ // have the result.
+ register_client_event_.Wait();
+ ASSERT_EQ(last_register_client_result_, 0);
+ }
+}
+
+// Test that CameraHalClient registration fails when a wrong (empty) token is
+// provided.
+TEST_F(CameraHalDispatcherImplTest, RegisterClientFail) {
+ // First verify that a the CameraHalDispatcherImpl establishes a Mojo channel
+ // between the server and the client.
+ auto mock_server = std::make_unique<MockCameraHalServer>();
+ auto mock_client = std::make_unique<MockCameraHalClient>();
+
+ auto server = mock_server->GetPendingRemote();
+ GetProxyTaskRunner()->PostTask(
+ FROM_HERE,
+ base::BindOnce(
+ &CameraHalDispatcherImplTest::RegisterServer,
+ base::Unretained(dispatcher_), std::move(server),
+ base::BindOnce(&CameraHalDispatcherImplTest::OnRegisteredServer,
+ base::Unretained(this))));
+
+ // Use an empty token to make sure authentication fails.
+ base::UnguessableToken empty_token;
+ auto client = mock_client->GetPendingRemote();
+ GetProxyTaskRunner()->PostTask(
+ FROM_HERE,
+ base::BindOnce(
+ &CameraHalDispatcherImplTest::RegisterClientWithToken,
+ base::Unretained(dispatcher_), std::move(client),
+ cros::mojom::CameraClientType::TESTING, empty_token,
+ base::BindOnce(&CameraHalDispatcherImplTest::OnRegisteredClient,
+ base::Unretained(this))));
+
+ // We do not need to enter a run loop here because
+ // CameraHalClient::SetUpChannel() isn't expected to called, and we only need
+ // to wait for the callback from CameraHalDispatcher::RegisterClientWithToken.
+ register_client_event_.Wait();
+ ASSERT_EQ(last_register_client_result_, -EPERM);
+}
+
+// Test that CameraHalDispatcherImpl correctly fires CameraActiveClientObserver
+// when a camera device is opened or closed by a client.
+TEST_F(CameraHalDispatcherImplTest, CameraActiveClientObserverTest) {
+ MockCameraActiveClientObserver observer;
+ dispatcher_->AddActiveClientObserver(&observer);
+
+ EXPECT_CALL(observer, DoOnActiveClientChange(
+ cros::mojom::CameraClientType::TESTING, true))
+ .Times(1)
+ .WillOnce(
+ InvokeWithoutArgs(this, &CameraHalDispatcherImplTest::QuitRunLoop));
+ dispatcher_->CameraDeviceActivityChange(
+ /*camera_id=*/0, /*opened=*/true, cros::mojom::CameraClientType::TESTING);
+
+ DoLoop();
+
+ EXPECT_CALL(observer, DoOnActiveClientChange(
+ cros::mojom::CameraClientType::TESTING, false))
+ .Times(1)
+ .WillOnce(
+ InvokeWithoutArgs(this, &CameraHalDispatcherImplTest::QuitRunLoop));
+ dispatcher_->CameraDeviceActivityChange(
+ /*camera_id=*/0, /*opened=*/false,
+ cros::mojom::CameraClientType::TESTING);
+
+ DoLoop();
+}
+
+} // namespace media