blob: 75ded8fad289b0ad022ad25db330b37c4ed57e0b [file] [log] [blame]
// Copyright 2024 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chrome/browser/ash/dbus/arc_crosh_service_provider.h"
#include <memory>
#include <string>
#include <utility>
#include "ash/test/ash_test_base.h"
#include "base/functional/bind.h"
#include "base/test/scoped_command_line.h"
#include "chrome/browser/ash/arc/session/arc_session_manager.h"
#include "chrome/browser/ash/arc/test/test_arc_session_manager.h"
#include "chrome/browser/ash/login/users/fake_chrome_user_manager.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/test/base/testing_browser_process.h"
#include "chrome/test/base/testing_profile_manager.h"
#include "chromeos/ash/components/dbus/concierge/concierge_client.h"
#include "chromeos/ash/components/dbus/services/service_provider_test_helper.h"
#include "chromeos/ash/components/dbus/upstart/upstart_client.h"
#include "chromeos/ash/experiences/arc/mojom/crosh.mojom.h"
#include "chromeos/ash/experiences/arc/session/arc_bridge_service.h"
#include "chromeos/ash/experiences/arc/session/arc_service_manager.h"
#include "chromeos/ash/experiences/arc/test/connection_holder_util.h"
#include "chromeos/ash/experiences/arc/test/fake_arc_session.h"
#include "components/account_id/account_id.h"
#include "components/session_manager/core/fake_session_manager_delegate.h"
#include "components/session_manager/core/session_manager.h"
#include "components/user_manager/user.h"
#include "components/user_manager/user_manager.h"
#include "content/public/browser/browser_thread.h"
#include "content/public/test/browser_task_environment.h"
#include "dbus/message.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "third_party/cros_system_api/dbus/service_constants.h"
namespace ash {
namespace {
// Arrows controlling the result of Exec() from Mojo.
class MockArcShellExecutionInstance
: public arc::mojom::ArcShellExecutionInstance {
public:
void Exec(arc::mojom::ArcShellExecutionRequestPtr request,
ExecCallback callback) override {
EXPECT_TRUE(!request.is_null());
recieved_exec_request_ = std::move(request);
EXPECT_TRUE(!callback.is_null());
EXPECT_TRUE(!exec_result_.is_null());
std::move(callback).Run(std::move(exec_result_));
}
arc::mojom::ArcShellExecutionRequestPtr RecievedExecRequest() {
return std::move(recieved_exec_request_);
}
void SetNextExecResult(arc::mojom::ArcShellExecutionResultPtr result) {
exec_result_ = std::move(result);
}
private:
arc::mojom::ArcShellExecutionResultPtr exec_result_;
arc::mojom::ArcShellExecutionRequestPtr recieved_exec_request_;
};
constexpr char kPrimaryUserProfileName[] = "primary@gmail.com";
constexpr char KSecondaryUserProfileName[] = "secondary@gmail.com";
class ArcCroshServiceProviderTest : public testing::Test {
public:
ArcCroshServiceProviderTest() = default;
ArcCroshServiceProviderTest(const ArcCroshServiceProviderTest&) = delete;
ArcCroshServiceProviderTest& operator=(const ArcCroshServiceProviderTest&) =
delete;
~ArcCroshServiceProviderTest() override = default;
void SetUp() override {
ash::UpstartClient::InitializeFake();
ash::ConciergeClient::InitializeFake(/*fake_cicerone_client=*/nullptr);
base::CommandLine* command_line =
scoped_command_line.GetProcessCommandLine();
command_line->InitFromArgv(
{"", "--arc-availability=officially-supported", "--enable-arcvm"});
service_provider_ = std::make_unique<ArcCroshServiceProvider>();
fake_user_manager_.Reset(std::make_unique<ash::FakeChromeUserManager>());
arc_service_manager_ = std::make_unique<arc::ArcServiceManager>();
// Make the session manager skip creating UI.
arc::ArcSessionManager::SetUiEnabledForTesting(/*enabled=*/false);
arc_session_manager_ = arc::CreateTestArcSessionManager(
std::make_unique<arc::ArcSessionRunner>(
base::BindRepeating(arc::FakeArcSession::Create)));
// Log in as a primary profile to enable ARCVM.
profile_manager_ = std::make_unique<TestingProfileManager>(
TestingBrowserProcess::GetGlobal());
ASSERT_TRUE(profile_manager_->SetUp());
const AccountId primary_user_account_id =
AccountId::FromUserEmail(kPrimaryUserProfileName);
user_manager::User* primary_user =
fake_user_manager_->AddUser(primary_user_account_id);
primary_username_hash_ = primary_user->username_hash();
Profile* primary_user_profile =
profile_manager_->CreateTestingProfile(kPrimaryUserProfileName);
const AccountId secondary_user_account_id =
AccountId::FromUserEmail(KSecondaryUserProfileName);
user_manager::User* secondary_user =
fake_user_manager_->AddUser(secondary_user_account_id);
secondary_username_hash_ = secondary_user->username_hash();
profile_manager_->CreateTestingProfile(KSecondaryUserProfileName);
fake_user_manager_->LoginUser(primary_user_account_id);
arc_session_manager_->SetProfile(primary_user_profile);
arc_session_manager_->Initialize();
arc_session_manager_->RequestEnable();
arc_session_manager_->StartArcForTesting();
arc_bridge_service()->arc_shell_execution()->SetInstance(
&mock_arc_shell_execution_instance_);
arc::WaitForInstanceReady(arc_bridge_service()->arc_shell_execution());
test_helper_.SetUp(arc::crosh::kArcCroshServiceName,
dbus::ObjectPath(arc::crosh::kArcCroshServicePath),
arc::crosh::kArcCroshInterfaceName,
arc::crosh::kArcCroshRequest, service_provider_.get());
}
void TearDown() override {
test_helper_.TearDown();
arc_bridge_service()->arc_shell_execution()->CloseInstance(
&mock_arc_shell_execution_instance_);
arc_session_manager_->Shutdown();
profile_manager_->DeleteTestingProfile(kPrimaryUserProfileName);
profile_manager_->DeleteTestingProfile(KSecondaryUserProfileName);
arc_session_manager_.reset();
arc_service_manager_.reset();
service_provider_.reset();
ash::ConciergeClient::Shutdown();
ash::UpstartClient::Shutdown();
}
protected:
std::unique_ptr<dbus::Response> CallExec(
arc::ArcShellExecutionRequest message) {
dbus::MethodCall method_call{arc::crosh::kArcCroshServiceName,
arc::crosh::kArcCroshRequest};
dbus::MessageWriter writer(&method_call);
writer.AppendProtoAsArrayOfBytes(message);
std::unique_ptr<dbus::Response> response =
test_helper_.CallMethod(&method_call);
return response;
}
content::BrowserTaskEnvironment& task_environment() {
return task_environment_;
}
MockArcShellExecutionInstance& mock_arc_shell_execution_instance() {
return mock_arc_shell_execution_instance_;
}
arc::ArcBridgeService* arc_bridge_service() {
return arc_service_manager_->arc_bridge_service();
}
const std::string& primary_username_hash() const {
return primary_username_hash_;
}
const std::string& secondary_username_hash() const {
return secondary_username_hash_;
}
private:
content::BrowserTaskEnvironment task_environment_;
ServiceProviderTestHelper test_helper_;
std::unique_ptr<ArcCroshServiceProvider> service_provider_;
MockArcShellExecutionInstance mock_arc_shell_execution_instance_;
std::unique_ptr<arc::ArcServiceManager> arc_service_manager_;
std::unique_ptr<arc::ArcSessionManager> arc_session_manager_;
user_manager::TypedScopedUserManager<ash::FakeChromeUserManager>
fake_user_manager_;
std::unique_ptr<TestingProfileManager> profile_manager_;
session_manager::SessionManager session_manager_{
std::make_unique<session_manager::FakeSessionManagerDelegate>()};
std::string primary_username_hash_;
std::string secondary_username_hash_;
base::test::ScopedCommandLine scoped_command_line;
};
TEST_F(ArcCroshServiceProviderTest, Success) {
arc::mojom::ArcShellExecutionResultPtr mock_result =
arc::mojom::ArcShellExecutionResult::NewStdout("top command result");
mock_arc_shell_execution_instance().SetNextExecResult(std::move(mock_result));
arc::ArcShellExecutionRequest request;
request.set_command(arc::ArcShellExecutionRequest_ArcShellCommand::
ArcShellExecutionRequest_ArcShellCommand_TOP);
request.set_user_id(primary_username_hash());
std::unique_ptr<dbus::Response> response = CallExec(request);
arc::mojom::ArcShellExecutionRequestPtr sent_mojo_request =
mock_arc_shell_execution_instance().RecievedExecRequest();
EXPECT_EQ(sent_mojo_request->command, arc::mojom::ArcShellCommand::kTop);
arc::ArcShellExecutionResult result;
dbus::MessageReader reader(response.get());
ASSERT_TRUE(reader.PopArrayOfBytesAsProto(&result));
EXPECT_FALSE(result.has_error());
ASSERT_TRUE(result.has_stdout());
EXPECT_EQ("top command result", result.stdout());
}
TEST_F(ArcCroshServiceProviderTest, EmptyCommand) {
arc::ArcShellExecutionRequest request;
request.set_user_id(primary_username_hash());
std::unique_ptr<dbus::Response> response = CallExec(request);
EXPECT_EQ(DBUS_ERROR_INVALID_ARGS, response->GetErrorName());
std::string error_message;
dbus::MessageReader reader(response.get());
ASSERT_TRUE(reader.PopString(&error_message));
EXPECT_EQ("command should not be empty", error_message);
}
TEST_F(ArcCroshServiceProviderTest, RequestWithoutUserId) {
arc::ArcShellExecutionRequest request;
request.set_command(arc::ArcShellExecutionRequest_ArcShellCommand::
ArcShellExecutionRequest_ArcShellCommand_TOP);
std::unique_ptr<dbus::Response> response = CallExec(request);
EXPECT_EQ(DBUS_ERROR_INVALID_ARGS, response->GetErrorName());
std::string error_message;
dbus::MessageReader reader(response.get());
ASSERT_TRUE(reader.PopString(&error_message));
EXPECT_EQ("user_id should not be empty", error_message);
}
TEST_F(ArcCroshServiceProviderTest, RequestFromSecondaryUser) {
arc::ArcShellExecutionRequest request;
request.set_command(arc::ArcShellExecutionRequest_ArcShellCommand::
ArcShellExecutionRequest_ArcShellCommand_TOP);
request.set_user_id(secondary_username_hash());
std::unique_ptr<dbus::Response> response = CallExec(request);
EXPECT_EQ(DBUS_ERROR_ACCESS_DENIED, response->GetErrorName());
std::string error_message;
dbus::MessageReader reader(response.get());
ASSERT_TRUE(reader.PopString(&error_message));
EXPECT_EQ("Request from not primary user is prohibited", error_message);
}
} // namespace
} // namespace ash