[go: nahoru, domu]

blob: 05b56027c39ed07dec69c170851e527b9b198d30 [file] [log] [blame]
// Copyright 2018 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 "ash/components/drivefs/drivefs_host.h"
#include <set>
#include <type_traits>
#include <utility>
#include "ash/components/drivefs/drivefs_host_observer.h"
#include "ash/components/drivefs/fake_drivefs.h"
#include "ash/components/drivefs/mojom/drivefs.mojom-test-utils.h"
#include "ash/components/drivefs/mojom/drivefs.mojom.h"
#include "base/bind.h"
#include "base/check.h"
#include "base/notreached.h"
#include "base/run_loop.h"
#include "base/scoped_observation.h"
#include "base/strings/strcat.h"
#include "base/strings/string_split.h"
#include "base/test/bind.h"
#include "base/test/gmock_callback_support.h"
#include "base/test/simple_test_clock.h"
#include "base/test/task_environment.h"
#include "base/timer/mock_timer.h"
#include "chromeos/components/mojo_bootstrap/pending_connection_manager.h"
#include "chromeos/disks/mock_disk_mount_manager.h"
#include "components/drive/drive_notification_manager.h"
#include "components/drive/drive_notification_observer.h"
#include "components/invalidation/impl/fake_invalidation_service.h"
#include "components/signin/public/identity_manager/identity_manager.h"
#include "components/signin/public/identity_manager/identity_test_environment.h"
#include "mojo/public/cpp/bindings/clone_traits.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 "mojo/public/cpp/bindings/receiver_set.h"
#include "mojo/public/cpp/bindings/remote.h"
#include "services/network/public/cpp/shared_url_loader_factory.h"
#include "services/network/test/test_network_connection_tracker.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace drivefs {
namespace {
using base::test::RunOnceClosure;
using testing::_;
using MountFailure = DriveFsHost::MountObserver::MountFailure;
constexpr base::TimeDelta kTokenLifetime = base::TimeDelta::FromHours(1);
class MockDriveFs : public mojom::DriveFsInterceptorForTesting,
public mojom::SearchQuery {
public:
MockDriveFs() = default;
DriveFs* GetForwardingInterface() override {
NOTREACHED();
return nullptr;
}
void FetchChangeLog(std::vector<mojom::FetchChangeLogOptionsPtr> options) {
std::vector<std::pair<int64_t, std::string>> unwrapped_options;
for (auto& entry : options) {
unwrapped_options.push_back(
std::make_pair(entry->change_id, entry->team_drive_id));
}
FetchChangeLogImpl(unwrapped_options);
}
MOCK_METHOD1(
FetchChangeLogImpl,
void(const std::vector<std::pair<int64_t, std::string>>& options));
MOCK_METHOD0(FetchAllChangeLogs, void());
MOCK_CONST_METHOD1(OnStartSearchQuery, void(const mojom::QueryParameters&));
void StartSearchQuery(mojo::PendingReceiver<mojom::SearchQuery> receiver,
mojom::QueryParametersPtr query_params) override {
search_receiver_.reset();
OnStartSearchQuery(*query_params);
search_receiver_.Bind(std::move(receiver));
}
MOCK_METHOD1(OnGetNextPage,
drive::FileError(
absl::optional<std::vector<mojom::QueryItemPtr>>* items));
void GetNextPage(GetNextPageCallback callback) override {
absl::optional<std::vector<mojom::QueryItemPtr>> items;
auto error = OnGetNextPage(&items);
std::move(callback).Run(error, std::move(items));
}
private:
mojo::Receiver<mojom::SearchQuery> search_receiver_{this};
};
class TestingDriveFsHostDelegate : public DriveFsHost::Delegate,
public DriveFsHost::MountObserver {
public:
TestingDriveFsHostDelegate(signin::IdentityManager* identity_manager,
const AccountId& account_id)
: identity_manager_(identity_manager),
account_id_(account_id),
drive_notification_manager_(&invalidation_service_) {}
TestingDriveFsHostDelegate(const TestingDriveFsHostDelegate&) = delete;
TestingDriveFsHostDelegate& operator=(const TestingDriveFsHostDelegate&) =
delete;
~TestingDriveFsHostDelegate() override {
drive_notification_manager_.Shutdown();
}
void set_pending_bootstrap(
mojo::PendingRemote<mojom::DriveFsBootstrap> pending_bootstrap) {
pending_bootstrap_ = std::move(pending_bootstrap);
}
void set_verbose_logging_enabled(bool enabled) {
verbose_logging_enabled_ = enabled;
}
drivefs::mojom::ExtensionConnectionParams& get_last_extension_params() {
return *extension_params_;
}
// DriveFsHost::MountObserver:
MOCK_METHOD1(OnMounted, void(const base::FilePath&));
MOCK_METHOD2(OnMountFailed,
void(MountFailure, absl::optional<base::TimeDelta>));
MOCK_METHOD1(OnUnmounted, void(absl::optional<base::TimeDelta>));
drive::DriveNotificationManager& GetDriveNotificationManager() override {
return drive_notification_manager_;
}
private:
// DriveFsHost::Delegate:
scoped_refptr<network::SharedURLLoaderFactory> GetURLLoaderFactory()
override {
return nullptr;
}
signin::IdentityManager* GetIdentityManager() override {
return identity_manager_;
}
const AccountId& GetAccountId() override { return account_id_; }
std::string GetObfuscatedAccountId() override {
return "salt-" + account_id_.GetAccountIdKey();
}
bool IsMetricsCollectionEnabled() override { return false; }
std::unique_ptr<DriveFsBootstrapListener> CreateMojoListener() override {
DCHECK(pending_bootstrap_);
return std::make_unique<FakeDriveFsBootstrapListener>(
std::move(pending_bootstrap_));
}
std::string GetLostAndFoundDirectoryName() override {
return "recovered files";
}
base::FilePath GetMyFilesPath() override {
return base::FilePath("/MyFiles");
}
bool IsVerboseLoggingEnabled() override { return verbose_logging_enabled_; }
drivefs::mojom::DriveFsDelegate::ExtensionConnectionStatus ConnectToExtension(
drivefs::mojom::ExtensionConnectionParamsPtr params,
mojo::PendingReceiver<drivefs::mojom::NativeMessagingPort> port,
mojo::PendingRemote<drivefs::mojom::NativeMessagingHost> host) override {
extension_params_ = std::move(params);
return drivefs::mojom::DriveFsDelegate::ExtensionConnectionStatus::
kExtensionNotFound;
}
signin::IdentityManager* const identity_manager_;
const AccountId account_id_;
mojo::PendingRemote<mojom::DriveFsBootstrap> pending_bootstrap_;
bool verbose_logging_enabled_ = false;
invalidation::FakeInvalidationService invalidation_service_;
drive::DriveNotificationManager drive_notification_manager_;
drivefs::mojom::ExtensionConnectionParamsPtr extension_params_;
};
class MockDriveFsHostObserver : public DriveFsHostObserver {
public:
MOCK_METHOD0(OnUnmounted, void());
MOCK_METHOD1(OnSyncingStatusUpdate, void(const mojom::SyncingStatus& status));
MOCK_METHOD1(OnFilesChanged,
void(const std::vector<mojom::FileChange>& changes));
MOCK_METHOD1(OnError, void(const mojom::DriveError& error));
};
class DriveFsHostTest : public ::testing::Test, public mojom::DriveFsBootstrap {
public:
DriveFsHostTest()
: network_connection_tracker_(
network::TestNetworkConnectionTracker::CreateInstance()) {
clock_.SetNow(base::Time::Now());
}
protected:
void SetUp() override {
testing::Test::SetUp();
profile_path_ = base::FilePath(FILE_PATH_LITERAL("/path/to/profile"));
account_id_ = AccountId::FromUserEmailGaiaId("test@example.com", "ID");
disk_manager_ = std::make_unique<chromeos::disks::MockDiskMountManager>();
identity_test_env_.MakePrimaryAccountAvailable(
"test@example.com", signin::ConsentLevel::kSignin);
host_delegate_ = std::make_unique<TestingDriveFsHostDelegate>(
identity_test_env_.identity_manager(), account_id_);
auto timer = std::make_unique<base::MockOneShotTimer>();
timer_ = timer.get();
host_ = std::make_unique<DriveFsHost>(
profile_path_, host_delegate_.get(), host_delegate_.get(),
network_connection_tracker_.get(), &clock_, disk_manager_.get(),
std::move(timer));
}
void TearDown() override {
host_.reset();
disk_manager_.reset();
}
void DispatchMountEvent(
chromeos::disks::DiskMountManager::MountEvent event,
chromeos::MountError error_code,
const chromeos::disks::DiskMountManager::MountPointInfo& mount_info) {
disk_manager_->NotifyMountEvent(event, error_code, mount_info);
}
std::string StartMount() {
std::string source;
EXPECT_CALL(
*disk_manager_,
MountPath(
testing::StartsWith("drivefs://"), "", "drivefs-salt-g-ID",
testing::AllOf(testing::Contains(
"datadir=/path/to/profile/GCache/v2/salt-g-ID"),
testing::Contains("myfiles=/MyFiles")),
_, chromeos::MOUNT_ACCESS_MODE_READ_WRITE))
.WillOnce(testing::SaveArg<0>(&source));
host_delegate_->set_pending_bootstrap(
bootstrap_receiver_.BindNewPipeAndPassRemote());
pending_delegate_receiver_ = delegate_.BindNewPipeAndPassReceiver();
EXPECT_TRUE(host_->Mount());
testing::Mock::VerifyAndClear(&disk_manager_);
return source.substr(strlen("drivefs://"));
}
void DispatchMountSuccessEvent(const std::string& token) {
DispatchMountEvent(chromeos::disks::DiskMountManager::MOUNTING,
chromeos::MOUNT_ERROR_NONE,
{base::StrCat({"drivefs://", token}),
"/media/drivefsroot/salt-g-ID",
chromeos::MOUNT_TYPE_NETWORK_STORAGE,
{}});
}
void SendOnMounted() { delegate_->OnMounted(); }
void SendOnUnmounted(absl::optional<base::TimeDelta> delay) {
delegate_->OnUnmounted(std::move(delay));
}
void SendMountFailed(absl::optional<base::TimeDelta> delay) {
delegate_->OnMountFailed(std::move(delay));
}
void EstablishConnection() {
token_ = StartMount();
DispatchMountSuccessEvent(token_);
ASSERT_TRUE(mojo_bootstrap::PendingConnectionManager::Get().OpenIpcChannel(
token_, {}));
{
base::RunLoop run_loop;
bootstrap_receiver_.set_disconnect_handler(run_loop.QuitClosure());
run_loop.Run();
}
}
void DoMount() {
EstablishConnection();
base::RunLoop run_loop;
base::OnceClosure quit_closure = run_loop.QuitClosure();
EXPECT_CALL(*host_delegate_,
OnMounted(base::FilePath("/media/drivefsroot/salt-g-ID")))
.WillOnce(RunOnceClosure(std::move(quit_closure)));
// Eventually we must attempt unmount.
EXPECT_CALL(*disk_manager_, UnmountPath("/media/drivefsroot/salt-g-ID", _));
SendOnMounted();
run_loop.Run();
ASSERT_TRUE(host_->IsMounted());
}
void DoUnmount() {
EXPECT_CALL(*host_delegate_, OnUnmounted(_)).Times(0);
host_->Unmount();
receiver_.reset();
bootstrap_receiver_.reset();
delegate_.reset();
base::RunLoop().RunUntilIdle();
testing::Mock::VerifyAndClearExpectations(disk_manager_.get());
testing::Mock::VerifyAndClearExpectations(host_delegate_.get());
}
void Init(mojom::DriveFsConfigurationPtr config,
mojo::PendingReceiver<mojom::DriveFs> drive_fs_receiver,
mojo::PendingRemote<mojom::DriveFsDelegate> delegate) override {
EXPECT_EQ("test@example.com", config->user_email);
EXPECT_EQ("recovered files",
config->lost_and_found_directory_name.value_or("<None>"));
verbose_logging_enabled_ = config->enable_verbose_logging;
init_access_token_ = std::move(config->access_token);
receiver_.Bind(std::move(drive_fs_receiver));
mojo::FusePipes(std::move(pending_delegate_receiver_), std::move(delegate));
}
base::FilePath profile_path_;
base::test::TaskEnvironment task_environment_;
AccountId account_id_;
std::unique_ptr<chromeos::disks::MockDiskMountManager> disk_manager_;
std::unique_ptr<network::TestNetworkConnectionTracker>
network_connection_tracker_;
base::SimpleTestClock clock_;
signin::IdentityTestEnvironment identity_test_env_;
std::unique_ptr<TestingDriveFsHostDelegate> host_delegate_;
std::unique_ptr<DriveFsHost> host_;
base::MockOneShotTimer* timer_;
absl::optional<bool> verbose_logging_enabled_;
mojo::Receiver<mojom::DriveFsBootstrap> bootstrap_receiver_{this};
MockDriveFs mock_drivefs_;
mojo::Receiver<mojom::DriveFs> receiver_{&mock_drivefs_};
mojo::Remote<mojom::DriveFsDelegate> delegate_;
mojo::PendingReceiver<mojom::DriveFsDelegate> pending_delegate_receiver_;
std::string token_;
absl::optional<std::string> init_access_token_;
private:
DISALLOW_COPY_AND_ASSIGN(DriveFsHostTest);
};
TEST_F(DriveFsHostTest, Basic) {
MockDriveFsHostObserver observer;
base::ScopedObservation<DriveFsHost, DriveFsHostObserver> observation_scoper(
&observer);
observation_scoper.Observe(host_.get());
EXPECT_FALSE(host_->IsMounted());
EXPECT_EQ(base::FilePath("/path/to/profile/GCache/v2/salt-g-ID"),
host_->GetDataPath());
ASSERT_NO_FATAL_FAILURE(DoMount());
EXPECT_FALSE(init_access_token_);
ASSERT_TRUE(verbose_logging_enabled_);
EXPECT_FALSE(verbose_logging_enabled_.value());
EXPECT_EQ(base::FilePath("/media/drivefsroot/salt-g-ID"),
host_->GetMountPath());
EXPECT_CALL(observer, OnUnmounted());
EXPECT_CALL(*host_delegate_, OnUnmounted(_)).Times(0);
base::RunLoop run_loop;
delegate_.set_disconnect_handler(run_loop.QuitClosure());
host_->Unmount();
run_loop.Run();
}
TEST_F(DriveFsHostTest, EnableVerboseLogging) {
ASSERT_FALSE(host_->IsMounted());
host_delegate_->set_verbose_logging_enabled(true);
ASSERT_NO_FATAL_FAILURE(DoMount());
ASSERT_TRUE(verbose_logging_enabled_);
EXPECT_TRUE(verbose_logging_enabled_.value());
}
TEST_F(DriveFsHostTest, GetMountPathWhileUnmounted) {
EXPECT_EQ(base::FilePath("/media/fuse/drivefs-salt-g-ID"),
host_->GetMountPath());
}
TEST_F(DriveFsHostTest, OnMountFailedFromMojo) {
ASSERT_FALSE(host_->IsMounted());
ASSERT_NO_FATAL_FAILURE(EstablishConnection());
base::RunLoop run_loop;
base::OnceClosure quit_closure = run_loop.QuitClosure();
EXPECT_CALL(*host_delegate_, OnMountFailed(MountFailure::kUnknown, _))
.WillOnce(RunOnceClosure(std::move(quit_closure)));
SendMountFailed({});
run_loop.Run();
ASSERT_FALSE(host_->IsMounted());
}
TEST_F(DriveFsHostTest, OnMountFailedFromDbus) {
ASSERT_FALSE(host_->IsMounted());
EXPECT_CALL(*disk_manager_, UnmountPath(_, _)).Times(0);
auto token = StartMount();
base::RunLoop run_loop;
base::OnceClosure quit_closure = run_loop.QuitClosure();
EXPECT_CALL(*host_delegate_, OnMountFailed(MountFailure::kInvocation, _))
.WillOnce(RunOnceClosure(std::move(quit_closure)));
DispatchMountEvent(chromeos::disks::DiskMountManager::MOUNTING,
chromeos::MOUNT_ERROR_INVALID_MOUNT_OPTIONS,
{base::StrCat({"drivefs://", token}),
"/media/drivefsroot/salt-g-ID",
chromeos::MOUNT_TYPE_NETWORK_STORAGE,
{}});
run_loop.Run();
ASSERT_FALSE(host_->IsMounted());
EXPECT_FALSE(mojo_bootstrap::PendingConnectionManager::Get().OpenIpcChannel(
token, {}));
}
TEST_F(DriveFsHostTest, DestroyBeforeMojoConnection) {
auto token = StartMount();
DispatchMountSuccessEvent(token);
base::RunLoop run_loop;
EXPECT_CALL(*disk_manager_, UnmountPath("/media/drivefsroot/salt-g-ID", _))
.WillOnce(base::test::RunClosure(run_loop.QuitClosure()));
host_.reset();
EXPECT_FALSE(mojo_bootstrap::PendingConnectionManager::Get().OpenIpcChannel(
token, {}));
run_loop.Run();
}
TEST_F(DriveFsHostTest, MountWhileAlreadyMounted) {
DoMount();
EXPECT_FALSE(host_->Mount());
}
TEST_F(DriveFsHostTest, UnsupportedAccountTypes) {
EXPECT_CALL(*disk_manager_, MountPath(_, _, _, _, _, _)).Times(0);
const AccountId unsupported_accounts[] = {
AccountId::FromGaiaId("ID"),
AccountId::FromUserEmail("test2@example.com"),
AccountId::AdFromObjGuid("ID"),
};
for (auto& account : unsupported_accounts) {
host_delegate_ = std::make_unique<TestingDriveFsHostDelegate>(
identity_test_env_.identity_manager(), account);
host_ = std::make_unique<DriveFsHost>(
profile_path_, host_delegate_.get(), host_delegate_.get(),
network_connection_tracker_.get(), &clock_, disk_manager_.get(),
std::make_unique<base::MockOneShotTimer>());
EXPECT_FALSE(host_->Mount());
EXPECT_FALSE(host_->IsMounted());
}
}
TEST_F(DriveFsHostTest, GetAccessToken_UnmountDuringMojoRequest) {
ASSERT_NO_FATAL_FAILURE(DoMount());
base::RunLoop run_loop;
delegate_.set_disconnect_handler(run_loop.QuitClosure());
delegate_->GetAccessToken(
"client ID", "app ID", {"scope1", "scope2"},
base::BindLambdaForTesting([](mojom::AccessTokenStatus status,
const std::string& token) { FAIL(); }));
host_->Unmount();
run_loop.Run();
EXPECT_FALSE(host_->IsMounted());
EXPECT_FALSE(identity_test_env_.IsAccessTokenRequestPending());
}
ACTION_P(CloneStruct, output) {
*output = arg0.Clone();
}
TEST_F(DriveFsHostTest, OnSyncingStatusUpdate_ForwardToObservers) {
ASSERT_NO_FATAL_FAILURE(DoMount());
MockDriveFsHostObserver observer;
base::ScopedObservation<DriveFsHost, DriveFsHostObserver> observation_scoper(
&observer);
observation_scoper.Observe(host_.get());
auto status = mojom::SyncingStatus::New();
status->item_events.emplace_back(base::in_place, 12, 34, "filename.txt",
mojom::ItemEvent::State::kInProgress, 123,
456, mojom::ItemEventReason::kPin);
mojom::SyncingStatusPtr observed_status;
EXPECT_CALL(observer, OnSyncingStatusUpdate(_))
.WillOnce(CloneStruct(&observed_status));
delegate_->OnSyncingStatusUpdate(status.Clone());
delegate_.FlushForTesting();
testing::Mock::VerifyAndClear(&observer);
EXPECT_EQ(status, observed_status);
}
ACTION_P(CloneVectorOfStructs, output) {
for (auto& s : arg0) {
output->emplace_back(s.Clone());
}
}
TEST_F(DriveFsHostTest, OnFilesChanged_ForwardToObservers) {
ASSERT_NO_FATAL_FAILURE(DoMount());
MockDriveFsHostObserver observer;
base::ScopedObservation<DriveFsHost, DriveFsHostObserver> observation_scoper(
&observer);
observation_scoper.Observe(host_.get());
std::vector<mojom::FileChangePtr> changes;
changes.emplace_back(base::in_place, base::FilePath("/create"),
mojom::FileChange::Type::kCreate);
changes.emplace_back(base::in_place, base::FilePath("/delete"),
mojom::FileChange::Type::kDelete);
changes.emplace_back(base::in_place, base::FilePath("/modify"),
mojom::FileChange::Type::kModify);
std::vector<mojom::FileChangePtr> observed_changes;
EXPECT_CALL(observer, OnFilesChanged(_))
.WillOnce(CloneVectorOfStructs(&observed_changes));
delegate_->OnFilesChanged(mojo::Clone(changes));
delegate_.FlushForTesting();
testing::Mock::VerifyAndClear(&observer);
EXPECT_EQ(changes, observed_changes);
}
TEST_F(DriveFsHostTest, OnError_ForwardToObservers) {
ASSERT_NO_FATAL_FAILURE(DoMount());
MockDriveFsHostObserver observer;
base::ScopedObservation<DriveFsHost, DriveFsHostObserver> observation_scoper(
&observer);
observation_scoper.Observe(host_.get());
auto error = mojom::DriveError::New(
mojom::DriveError::Type::kCantUploadStorageFull, base::FilePath("/foo"));
mojom::DriveErrorPtr observed_error;
EXPECT_CALL(observer, OnError(_)).WillOnce(CloneStruct(&observed_error));
delegate_->OnError(error.Clone());
delegate_.FlushForTesting();
testing::Mock::VerifyAndClear(&observer);
EXPECT_EQ(error, observed_error);
}
TEST_F(DriveFsHostTest, OnError_IgnoreUnknownErrorTypes) {
ASSERT_NO_FATAL_FAILURE(DoMount());
MockDriveFsHostObserver observer;
base::ScopedObservation<DriveFsHost, DriveFsHostObserver> observation_scoper(
&observer);
observation_scoper.Observe(host_.get());
EXPECT_CALL(observer, OnError(_)).Times(0);
delegate_->OnError(mojom::DriveError::New(
static_cast<mojom::DriveError::Type>(
static_cast<std::underlying_type_t<mojom::DriveError::Type>>(
mojom::DriveError::Type::kMaxValue) +
1),
base::FilePath("/foo")));
delegate_.FlushForTesting();
}
TEST_F(DriveFsHostTest, DisplayConfirmDialog_ForwardToHandler) {
ASSERT_NO_FATAL_FAILURE(DoMount());
auto reason = mojom::DialogReason::New(
mojom::DialogReason::Type::kEnableDocsOffline, base::FilePath());
mojom::DialogReasonPtr observed_reason;
host_->set_dialog_handler(base::BindLambdaForTesting(
[&](const mojom::DialogReason& reason,
base::OnceCallback<void(mojom::DialogResult)> callback) {
observed_reason = reason.Clone();
std::move(callback).Run(mojom::DialogResult::kAccept);
}));
bool called = false;
delegate_->DisplayConfirmDialog(
reason.Clone(),
base::BindLambdaForTesting([&](mojom::DialogResult result) {
EXPECT_EQ(mojom::DialogResult::kAccept, result);
called = true;
}));
delegate_.FlushForTesting();
EXPECT_EQ(reason, observed_reason);
EXPECT_TRUE(called);
}
TEST_F(DriveFsHostTest, DisplayConfirmDialogImpl_IgnoreIfNoHandler) {
ASSERT_NO_FATAL_FAILURE(DoMount());
bool called = false;
delegate_->DisplayConfirmDialog(
mojom::DialogReason::New(mojom::DialogReason::Type::kEnableDocsOffline,
base::FilePath()),
base::BindLambdaForTesting([&](mojom::DialogResult result) {
EXPECT_EQ(mojom::DialogResult::kNotDisplayed, result);
called = true;
}));
delegate_.FlushForTesting();
EXPECT_TRUE(called);
}
TEST_F(DriveFsHostTest, DisplayConfirmDialogImpl_IgnoreUnknownReasonTypes) {
ASSERT_NO_FATAL_FAILURE(DoMount());
host_->set_dialog_handler(base::BindRepeating(
[](const mojom::DialogReason&,
base::OnceCallback<void(mojom::DialogResult)>) { NOTREACHED(); }));
bool called = false;
delegate_->DisplayConfirmDialog(
mojom::DialogReason::New(
static_cast<mojom::DialogReason::Type>(
static_cast<std::underlying_type_t<mojom::DialogReason::Type>>(
mojom::DialogReason::Type::kMaxValue) +
1),
base::FilePath()),
base::BindLambdaForTesting([&](mojom::DialogResult result) {
EXPECT_EQ(mojom::DialogResult::kNotDisplayed, result);
called = true;
}));
delegate_.FlushForTesting();
EXPECT_TRUE(called);
}
TEST_F(DriveFsHostTest, TeamDriveTracking) {
ASSERT_NO_FATAL_FAILURE(DoMount());
delegate_->OnTeamDrivesListReady({"a", "b"});
delegate_.FlushForTesting();
EXPECT_EQ(
(std::set<std::string>{"a", "b"}),
host_delegate_->GetDriveNotificationManager().team_drive_ids_for_test());
delegate_->OnTeamDriveChanged(
"c", mojom::DriveFsDelegate::CreateOrDelete::kCreated);
delegate_.FlushForTesting();
EXPECT_EQ(
(std::set<std::string>{"a", "b", "c"}),
host_delegate_->GetDriveNotificationManager().team_drive_ids_for_test());
delegate_->OnTeamDriveChanged(
"b", mojom::DriveFsDelegate::CreateOrDelete::kDeleted);
delegate_.FlushForTesting();
EXPECT_EQ(
(std::set<std::string>{"a", "c"}),
host_delegate_->GetDriveNotificationManager().team_drive_ids_for_test());
}
TEST_F(DriveFsHostTest, TeamDriveTrackingIgnoreChanges) {
ASSERT_NO_FATAL_FAILURE(DoMount());
EXPECT_EQ(
std::set<std::string>(),
host_delegate_->GetDriveNotificationManager().team_drive_ids_for_test());
delegate_->OnTeamDriveChanged(
"a", mojom::DriveFsDelegate::CreateOrDelete::kCreated);
delegate_.FlushForTesting();
EXPECT_EQ(
std::set<std::string>(),
host_delegate_->GetDriveNotificationManager().team_drive_ids_for_test());
delegate_->OnTeamDriveChanged(
"b", mojom::DriveFsDelegate::CreateOrDelete::kDeleted);
delegate_.FlushForTesting();
EXPECT_EQ(
std::set<std::string>(),
host_delegate_->GetDriveNotificationManager().team_drive_ids_for_test());
}
TEST_F(DriveFsHostTest, Invalidation) {
ASSERT_NO_FATAL_FAILURE(DoMount());
delegate_->OnTeamDrivesListReady({"a", "b"});
delegate_.FlushForTesting();
EXPECT_CALL(mock_drivefs_,
FetchChangeLogImpl(std::vector<std::pair<int64_t, std::string>>{
{123, ""}, {456, "a"}}));
for (auto& observer :
host_delegate_->GetDriveNotificationManager().observers_for_test()) {
observer.OnNotificationReceived({{"", 123}, {"a", 456}});
}
receiver_.FlushForTesting();
}
TEST_F(DriveFsHostTest, InvalidateAll) {
ASSERT_NO_FATAL_FAILURE(DoMount());
delegate_->OnTeamDrivesListReady({"a", "b"});
delegate_.FlushForTesting();
EXPECT_CALL(mock_drivefs_, FetchAllChangeLogs());
for (auto& observer :
host_delegate_->GetDriveNotificationManager().observers_for_test()) {
observer.OnNotificationTimerFired();
}
receiver_.FlushForTesting();
}
TEST_F(DriveFsHostTest, RemoveDriveNotificationObserver) {
ASSERT_NO_FATAL_FAILURE(DoMount());
delegate_->OnTeamDrivesListReady({"a", "b"});
delegate_.FlushForTesting();
EXPECT_TRUE(!host_delegate_->GetDriveNotificationManager()
.observers_for_test()
.empty());
host_.reset();
EXPECT_FALSE(!host_delegate_->GetDriveNotificationManager()
.observers_for_test()
.empty());
}
TEST_F(DriveFsHostTest, Remount_CachedOnceOnly) {
ASSERT_NO_FATAL_FAILURE(DoMount());
// Request an access token.
delegate_->GetAccessToken(
"client ID", "app ID", {"scope1", "scope2"},
base::BindLambdaForTesting(
[&](mojom::AccessTokenStatus status, const std::string& token) {
EXPECT_EQ(mojom::AccessTokenStatus::kSuccess, status);
EXPECT_EQ("auth token", token);
}));
delegate_.FlushForTesting();
EXPECT_TRUE(identity_test_env_.IsAccessTokenRequestPending());
// Fulfill the request.
identity_test_env_.WaitForAccessTokenRequestIfNecessaryAndRespondWithToken(
"auth token", clock_.Now() + kTokenLifetime);
EXPECT_FALSE(identity_test_env_.IsAccessTokenRequestPending());
absl::optional<base::TimeDelta> delay = base::TimeDelta::FromSeconds(5);
EXPECT_CALL(*host_delegate_, OnUnmounted(delay));
SendOnUnmounted(delay);
base::RunLoop().RunUntilIdle();
ASSERT_NO_FATAL_FAILURE(DoUnmount());
// Second mount attempt should reuse already available token.
ASSERT_NO_FATAL_FAILURE(DoMount());
EXPECT_FALSE(identity_test_env_.IsAccessTokenRequestPending());
EXPECT_EQ("auth token", init_access_token_.value_or(""));
// But if it asks for token again it goes to identity manager.
delegate_->GetAccessToken(
"client ID", "app ID", {"scope1", "scope2"},
base::BindLambdaForTesting(
[&](mojom::AccessTokenStatus status, const std::string& token) {
EXPECT_EQ(mojom::AccessTokenStatus::kSuccess, status);
EXPECT_EQ("auth token 2", token);
}));
delegate_.FlushForTesting();
EXPECT_TRUE(identity_test_env_.IsAccessTokenRequestPending());
// Fulfill the request with a different token.
identity_test_env_.WaitForAccessTokenRequestIfNecessaryAndRespondWithToken(
"auth token 2", clock_.Now() + kTokenLifetime);
EXPECT_FALSE(identity_test_env_.IsAccessTokenRequestPending());
}
TEST_F(DriveFsHostTest, Remount_RequestInflight) {
ASSERT_NO_FATAL_FAILURE(DoMount());
delegate_->GetAccessToken(
"client ID", "app ID", {"scope1", "scope2"},
base::BindLambdaForTesting([&](mojom::AccessTokenStatus status,
const std::string& token) { FAIL(); }));
absl::optional<base::TimeDelta> delay = base::TimeDelta::FromSeconds(5);
EXPECT_CALL(*host_delegate_, OnUnmounted(delay));
SendOnUnmounted(delay);
base::RunLoop().RunUntilIdle();
ASSERT_NO_FATAL_FAILURE(DoUnmount());
EXPECT_TRUE(identity_test_env_.IsAccessTokenRequestPending());
// Now the response is ready.
identity_test_env_.WaitForAccessTokenRequestIfNecessaryAndRespondWithToken(
"auth token", clock_.Now() + kTokenLifetime);
// Second mount will reuse previous token.
ASSERT_NO_FATAL_FAILURE(DoMount());
EXPECT_FALSE(identity_test_env_.IsAccessTokenRequestPending());
EXPECT_EQ("auth token", init_access_token_.value_or(""));
}
TEST_F(DriveFsHostTest, Remount_RequestInflightCompleteAfterMount) {
ASSERT_NO_FATAL_FAILURE(DoMount());
delegate_->GetAccessToken(
"client ID", "app ID", {"scope1", "scope2"},
base::BindLambdaForTesting([&](mojom::AccessTokenStatus status,
const std::string& token) { FAIL(); }));
absl::optional<base::TimeDelta> delay = base::TimeDelta::FromSeconds(5);
EXPECT_CALL(*host_delegate_, OnUnmounted(delay));
SendOnUnmounted(delay);
base::RunLoop().RunUntilIdle();
ASSERT_NO_FATAL_FAILURE(DoUnmount());
EXPECT_TRUE(identity_test_env_.IsAccessTokenRequestPending());
// Second mount will reuse previous token.
ASSERT_NO_FATAL_FAILURE(DoMount());
EXPECT_FALSE(init_access_token_);
EXPECT_TRUE(identity_test_env_.IsAccessTokenRequestPending());
// Now the response is ready.
identity_test_env_.WaitForAccessTokenRequestIfNecessaryAndRespondWithToken(
"auth token", clock_.Now() + kTokenLifetime);
EXPECT_FALSE(identity_test_env_.IsAccessTokenRequestPending());
// A new request will reuse the cached token.
delegate_->GetAccessToken(
"client ID", "app ID", {"scope1", "scope2"},
base::BindLambdaForTesting(
[&](mojom::AccessTokenStatus status, const std::string& token) {
EXPECT_EQ(mojom::AccessTokenStatus::kSuccess, status);
EXPECT_EQ("auth token", token);
}));
delegate_.FlushForTesting();
EXPECT_FALSE(identity_test_env_.IsAccessTokenRequestPending());
}
TEST_F(DriveFsHostTest, ConnectToExtension) {
ASSERT_NO_FATAL_FAILURE(DoMount());
mojo::Remote<drivefs::mojom::NativeMessagingPort> remote;
mojo::PendingRemote<drivefs::mojom::NativeMessagingHost> host_remote;
auto receiver = host_remote.InitWithNewPipeAndPassReceiver();
base::RunLoop run_loop;
delegate_->ConnectToExtension(
drivefs::mojom::ExtensionConnectionParams::New("foo"),
remote.BindNewPipeAndPassReceiver(), std::move(host_remote),
base::BindLambdaForTesting(
[&](drivefs::mojom::DriveFsDelegate::ExtensionConnectionStatus
status) {
EXPECT_EQ(drivefs::mojom::DriveFsDelegate::
ExtensionConnectionStatus::kExtensionNotFound,
status);
run_loop.Quit();
}));
run_loop.Run();
EXPECT_EQ("foo", host_delegate_->get_last_extension_params().extension_id);
}
} // namespace
} // namespace drivefs