drivefs_host_unittest.cc 32 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888
  1. // Copyright 2018 The Chromium Authors. All rights reserved.
  2. // Use of this source code is governed by a BSD-style license that can be
  3. // found in the LICENSE file.
  4. #include "ash/components/drivefs/drivefs_host.h"
  5. #include <set>
  6. #include <type_traits>
  7. #include <utility>
  8. #include "ash/components/disks/disk_mount_manager.h"
  9. #include "ash/components/disks/mock_disk_mount_manager.h"
  10. #include "ash/components/drivefs/drivefs_host_observer.h"
  11. #include "ash/components/drivefs/fake_drivefs.h"
  12. #include "ash/components/drivefs/mojom/drivefs.mojom-test-utils.h"
  13. #include "ash/components/drivefs/mojom/drivefs.mojom.h"
  14. #include "base/bind.h"
  15. #include "base/check.h"
  16. #include "base/notreached.h"
  17. #include "base/run_loop.h"
  18. #include "base/scoped_observation.h"
  19. #include "base/strings/strcat.h"
  20. #include "base/strings/string_split.h"
  21. #include "base/test/bind.h"
  22. #include "base/test/gmock_callback_support.h"
  23. #include "base/test/gmock_move_support.h"
  24. #include "base/test/simple_test_clock.h"
  25. #include "base/test/task_environment.h"
  26. #include "base/timer/mock_timer.h"
  27. #include "chromeos/components/mojo_bootstrap/pending_connection_manager.h"
  28. #include "components/drive/drive_notification_manager.h"
  29. #include "components/drive/drive_notification_observer.h"
  30. #include "components/invalidation/impl/fake_invalidation_service.h"
  31. #include "components/signin/public/identity_manager/identity_manager.h"
  32. #include "components/signin/public/identity_manager/identity_test_environment.h"
  33. #include "mojo/public/cpp/bindings/clone_traits.h"
  34. #include "mojo/public/cpp/bindings/pending_receiver.h"
  35. #include "mojo/public/cpp/bindings/pending_remote.h"
  36. #include "mojo/public/cpp/bindings/receiver.h"
  37. #include "mojo/public/cpp/bindings/receiver_set.h"
  38. #include "mojo/public/cpp/bindings/remote.h"
  39. #include "services/network/public/cpp/shared_url_loader_factory.h"
  40. #include "services/network/test/test_network_connection_tracker.h"
  41. #include "testing/gtest/include/gtest/gtest.h"
  42. #include "third_party/abseil-cpp/absl/utility/utility.h"
  43. namespace drivefs {
  44. namespace {
  45. using base::test::RunOnceClosure;
  46. using testing::_;
  47. using MountFailure = DriveFsHost::MountObserver::MountFailure;
  48. using ChangeLogOptionPair = std::pair<int64_t, std::string>;
  49. constexpr base::TimeDelta kTokenLifetime = base::Hours(1);
  50. class MockDriveFs : public mojom::DriveFsInterceptorForTesting,
  51. public mojom::SearchQuery {
  52. public:
  53. MockDriveFs() = default;
  54. DriveFs* GetForwardingInterface() override {
  55. NOTREACHED();
  56. return nullptr;
  57. }
  58. void FetchChangeLog(
  59. std::vector<mojom::FetchChangeLogOptionsPtr> options) override {
  60. std::vector<ChangeLogOptionPair> unwrapped_options;
  61. for (auto& entry : options) {
  62. unwrapped_options.push_back(
  63. std::make_pair(entry->change_id, entry->team_drive_id));
  64. }
  65. FetchChangeLogImpl(unwrapped_options);
  66. }
  67. MOCK_METHOD(void,
  68. FetchChangeLogImpl,
  69. (const std::vector<ChangeLogOptionPair>&));
  70. MOCK_METHOD(void, FetchAllChangeLogs, ());
  71. MOCK_METHOD(void,
  72. OnStartSearchQuery,
  73. (const mojom::QueryParameters&),
  74. (const));
  75. void StartSearchQuery(mojo::PendingReceiver<mojom::SearchQuery> receiver,
  76. mojom::QueryParametersPtr query_params) override {
  77. search_receiver_.reset();
  78. OnStartSearchQuery(*query_params);
  79. search_receiver_.Bind(std::move(receiver));
  80. }
  81. MOCK_METHOD(drive::FileError,
  82. OnGetNextPage,
  83. (absl::optional<std::vector<mojom::QueryItemPtr>> * items));
  84. void GetNextPage(GetNextPageCallback callback) override {
  85. absl::optional<std::vector<mojom::QueryItemPtr>> items;
  86. auto error = OnGetNextPage(&items);
  87. std::move(callback).Run(error, std::move(items));
  88. }
  89. private:
  90. mojo::Receiver<mojom::SearchQuery> search_receiver_{this};
  91. };
  92. class TestingDriveFsHostDelegate : public DriveFsHost::Delegate,
  93. public DriveFsHost::MountObserver {
  94. public:
  95. TestingDriveFsHostDelegate(signin::IdentityManager* identity_manager,
  96. const AccountId& account_id)
  97. : identity_manager_(identity_manager),
  98. account_id_(account_id),
  99. drive_notification_manager_(&invalidation_service_) {}
  100. TestingDriveFsHostDelegate(const TestingDriveFsHostDelegate&) = delete;
  101. TestingDriveFsHostDelegate& operator=(const TestingDriveFsHostDelegate&) =
  102. delete;
  103. ~TestingDriveFsHostDelegate() override {
  104. drive_notification_manager_.Shutdown();
  105. }
  106. void set_pending_bootstrap(
  107. mojo::PendingRemote<mojom::DriveFsBootstrap> pending_bootstrap) {
  108. pending_bootstrap_ = std::move(pending_bootstrap);
  109. }
  110. void set_verbose_logging_enabled(bool enabled) {
  111. verbose_logging_enabled_ = enabled;
  112. }
  113. drivefs::mojom::ExtensionConnectionParams& get_last_extension_params() {
  114. return *extension_params_;
  115. }
  116. // DriveFsHost::MountObserver:
  117. MOCK_METHOD(void, OnMounted, (const base::FilePath&));
  118. MOCK_METHOD(void,
  119. OnMountFailed,
  120. (MountFailure, absl::optional<base::TimeDelta>));
  121. MOCK_METHOD(void, OnUnmounted, (absl::optional<base::TimeDelta>));
  122. drive::DriveNotificationManager& GetDriveNotificationManager() override {
  123. return drive_notification_manager_;
  124. }
  125. private:
  126. // DriveFsHost::Delegate:
  127. scoped_refptr<network::SharedURLLoaderFactory> GetURLLoaderFactory()
  128. override {
  129. return nullptr;
  130. }
  131. signin::IdentityManager* GetIdentityManager() override {
  132. return identity_manager_;
  133. }
  134. const AccountId& GetAccountId() override { return account_id_; }
  135. std::string GetObfuscatedAccountId() override {
  136. return "salt-" + account_id_.GetAccountIdKey();
  137. }
  138. bool IsMetricsCollectionEnabled() override { return false; }
  139. std::unique_ptr<DriveFsBootstrapListener> CreateMojoListener() override {
  140. DCHECK(pending_bootstrap_);
  141. return std::make_unique<FakeDriveFsBootstrapListener>(
  142. std::move(pending_bootstrap_));
  143. }
  144. std::string GetLostAndFoundDirectoryName() override {
  145. return "recovered files";
  146. }
  147. base::FilePath GetMyFilesPath() override {
  148. return base::FilePath("/MyFiles");
  149. }
  150. bool IsVerboseLoggingEnabled() override { return verbose_logging_enabled_; }
  151. drivefs::mojom::DriveFsDelegate::ExtensionConnectionStatus ConnectToExtension(
  152. drivefs::mojom::ExtensionConnectionParamsPtr params,
  153. mojo::PendingReceiver<drivefs::mojom::NativeMessagingPort> port,
  154. mojo::PendingRemote<drivefs::mojom::NativeMessagingHost> host) override {
  155. extension_params_ = std::move(params);
  156. return drivefs::mojom::DriveFsDelegate::ExtensionConnectionStatus::
  157. kExtensionNotFound;
  158. }
  159. const std::string GetMachineRootID() override { return ""; }
  160. void PersistMachineRootID(const std::string& id) override {}
  161. signin::IdentityManager* const identity_manager_;
  162. const AccountId account_id_;
  163. mojo::PendingRemote<mojom::DriveFsBootstrap> pending_bootstrap_;
  164. bool verbose_logging_enabled_ = false;
  165. invalidation::FakeInvalidationService invalidation_service_;
  166. drive::DriveNotificationManager drive_notification_manager_;
  167. drivefs::mojom::ExtensionConnectionParamsPtr extension_params_;
  168. };
  169. class MockDriveFsHostObserver : public DriveFsHostObserver {
  170. public:
  171. MOCK_METHOD(void, OnUnmounted, ());
  172. MOCK_METHOD(void,
  173. OnSyncingStatusUpdate,
  174. (const mojom::SyncingStatus& status));
  175. MOCK_METHOD(void,
  176. OnMirrorSyncingStatusUpdate,
  177. (const mojom::SyncingStatus& status));
  178. MOCK_METHOD(void,
  179. OnFilesChanged,
  180. (const std::vector<mojom::FileChange>& changes));
  181. MOCK_METHOD(void, OnError, (const mojom::DriveError& error));
  182. };
  183. class DriveFsHostTest : public ::testing::Test, public mojom::DriveFsBootstrap {
  184. public:
  185. DriveFsHostTest()
  186. : network_connection_tracker_(
  187. network::TestNetworkConnectionTracker::CreateInstance()) {
  188. clock_.SetNow(base::Time::Now());
  189. }
  190. DriveFsHostTest(const DriveFsHostTest&) = delete;
  191. DriveFsHostTest& operator=(const DriveFsHostTest&) = delete;
  192. protected:
  193. void SetUp() override {
  194. testing::Test::SetUp();
  195. profile_path_ = base::FilePath(FILE_PATH_LITERAL("/path/to/profile"));
  196. account_id_ = AccountId::FromUserEmailGaiaId("test@example.com", "ID");
  197. disk_manager_ = std::make_unique<ash::disks::MockDiskMountManager>();
  198. identity_test_env_.MakePrimaryAccountAvailable(
  199. "test@example.com", signin::ConsentLevel::kSignin);
  200. host_delegate_ = std::make_unique<TestingDriveFsHostDelegate>(
  201. identity_test_env_.identity_manager(), account_id_);
  202. auto timer = std::make_unique<base::MockOneShotTimer>();
  203. timer_ = timer.get();
  204. host_ = std::make_unique<DriveFsHost>(
  205. profile_path_, host_delegate_.get(), host_delegate_.get(),
  206. network_connection_tracker_.get(), &clock_, disk_manager_.get(),
  207. std::move(timer));
  208. }
  209. void TearDown() override {
  210. host_.reset();
  211. disk_manager_.reset();
  212. }
  213. std::string StartMount() {
  214. std::string source;
  215. EXPECT_CALL(
  216. *disk_manager_,
  217. MountPath(
  218. testing::StartsWith("drivefs://"), "", "drivefs-salt-g-ID",
  219. testing::AllOf(testing::Contains(
  220. "datadir=/path/to/profile/GCache/v2/salt-g-ID"),
  221. testing::Contains("myfiles=/MyFiles")),
  222. _, ash::MountAccessMode::kReadWrite, _))
  223. .WillOnce(testing::DoAll(testing::SaveArg<0>(&source),
  224. MoveArg<6>(&mount_callback_)));
  225. host_delegate_->set_pending_bootstrap(
  226. bootstrap_receiver_.BindNewPipeAndPassRemote());
  227. pending_delegate_receiver_ = delegate_.BindNewPipeAndPassReceiver();
  228. EXPECT_TRUE(host_->Mount());
  229. testing::Mock::VerifyAndClear(&disk_manager_);
  230. return source.substr(strlen("drivefs://"));
  231. }
  232. void CallMountCallbackSuccess(const std::string& token) {
  233. std::move(mount_callback_)
  234. .Run(ash::MountError::kNone, {base::StrCat({"drivefs://", token}),
  235. "/media/drivefsroot/salt-g-ID",
  236. ash::MountType::kNetworkStorage,
  237. {}});
  238. }
  239. void SendOnMounted() { delegate_->OnMounted(); }
  240. void SendOnUnmounted(absl::optional<base::TimeDelta> delay) {
  241. delegate_->OnUnmounted(std::move(delay));
  242. }
  243. void SendMountFailed(absl::optional<base::TimeDelta> delay) {
  244. delegate_->OnMountFailed(std::move(delay));
  245. }
  246. void EstablishConnection() {
  247. token_ = StartMount();
  248. CallMountCallbackSuccess(token_);
  249. ASSERT_TRUE(mojo_bootstrap::PendingConnectionManager::Get().OpenIpcChannel(
  250. token_, {}));
  251. {
  252. base::RunLoop run_loop;
  253. bootstrap_receiver_.set_disconnect_handler(run_loop.QuitClosure());
  254. run_loop.Run();
  255. }
  256. }
  257. void DoMount() {
  258. EstablishConnection();
  259. base::RunLoop run_loop;
  260. base::OnceClosure quit_closure = run_loop.QuitClosure();
  261. EXPECT_CALL(*host_delegate_,
  262. OnMounted(base::FilePath("/media/drivefsroot/salt-g-ID")))
  263. .WillOnce(RunOnceClosure(std::move(quit_closure)));
  264. // Eventually we must attempt unmount.
  265. EXPECT_CALL(*disk_manager_, UnmountPath("/media/drivefsroot/salt-g-ID", _));
  266. SendOnMounted();
  267. run_loop.Run();
  268. ASSERT_TRUE(host_->IsMounted());
  269. }
  270. void DoUnmount() {
  271. EXPECT_CALL(*host_delegate_, OnUnmounted(_)).Times(0);
  272. host_->Unmount();
  273. receiver_.reset();
  274. bootstrap_receiver_.reset();
  275. delegate_.reset();
  276. base::RunLoop().RunUntilIdle();
  277. testing::Mock::VerifyAndClearExpectations(disk_manager_.get());
  278. testing::Mock::VerifyAndClearExpectations(host_delegate_.get());
  279. }
  280. void Init(mojom::DriveFsConfigurationPtr config,
  281. mojo::PendingReceiver<mojom::DriveFs> drive_fs_receiver,
  282. mojo::PendingRemote<mojom::DriveFsDelegate> delegate) override {
  283. EXPECT_EQ("test@example.com", config->user_email);
  284. EXPECT_EQ("recovered files",
  285. config->lost_and_found_directory_name.value_or("<None>"));
  286. verbose_logging_enabled_ = config->enable_verbose_logging;
  287. init_access_token_ = std::move(config->access_token);
  288. receiver_.Bind(std::move(drive_fs_receiver));
  289. mojo::FusePipes(std::move(pending_delegate_receiver_), std::move(delegate));
  290. }
  291. base::FilePath profile_path_;
  292. base::test::TaskEnvironment task_environment_;
  293. AccountId account_id_;
  294. std::unique_ptr<ash::disks::MockDiskMountManager> disk_manager_;
  295. ash::disks::DiskMountManager::MountPathCallback mount_callback_;
  296. std::unique_ptr<network::TestNetworkConnectionTracker>
  297. network_connection_tracker_;
  298. base::SimpleTestClock clock_;
  299. signin::IdentityTestEnvironment identity_test_env_;
  300. std::unique_ptr<TestingDriveFsHostDelegate> host_delegate_;
  301. std::unique_ptr<DriveFsHost> host_;
  302. base::MockOneShotTimer* timer_;
  303. absl::optional<bool> verbose_logging_enabled_;
  304. mojo::Receiver<mojom::DriveFsBootstrap> bootstrap_receiver_{this};
  305. MockDriveFs mock_drivefs_;
  306. mojo::Receiver<mojom::DriveFs> receiver_{&mock_drivefs_};
  307. mojo::Remote<mojom::DriveFsDelegate> delegate_;
  308. mojo::PendingReceiver<mojom::DriveFsDelegate> pending_delegate_receiver_;
  309. std::string token_;
  310. absl::optional<std::string> init_access_token_;
  311. };
  312. TEST_F(DriveFsHostTest, Basic) {
  313. MockDriveFsHostObserver observer;
  314. base::ScopedObservation<DriveFsHost, DriveFsHostObserver> observation_scoper(
  315. &observer);
  316. observation_scoper.Observe(host_.get());
  317. EXPECT_FALSE(host_->IsMounted());
  318. EXPECT_EQ(base::FilePath("/path/to/profile/GCache/v2/salt-g-ID"),
  319. host_->GetDataPath());
  320. ASSERT_NO_FATAL_FAILURE(DoMount());
  321. EXPECT_FALSE(init_access_token_);
  322. ASSERT_TRUE(verbose_logging_enabled_);
  323. EXPECT_FALSE(verbose_logging_enabled_.value());
  324. EXPECT_EQ(base::FilePath("/media/drivefsroot/salt-g-ID"),
  325. host_->GetMountPath());
  326. EXPECT_CALL(observer, OnUnmounted());
  327. EXPECT_CALL(*host_delegate_, OnUnmounted(_)).Times(0);
  328. base::RunLoop run_loop;
  329. delegate_.set_disconnect_handler(run_loop.QuitClosure());
  330. host_->Unmount();
  331. run_loop.Run();
  332. }
  333. TEST_F(DriveFsHostTest, EnableVerboseLogging) {
  334. ASSERT_FALSE(host_->IsMounted());
  335. host_delegate_->set_verbose_logging_enabled(true);
  336. ASSERT_NO_FATAL_FAILURE(DoMount());
  337. ASSERT_TRUE(verbose_logging_enabled_);
  338. EXPECT_TRUE(verbose_logging_enabled_.value());
  339. }
  340. TEST_F(DriveFsHostTest, GetMountPathWhileUnmounted) {
  341. EXPECT_EQ(base::FilePath("/media/fuse/drivefs-salt-g-ID"),
  342. host_->GetMountPath());
  343. }
  344. TEST_F(DriveFsHostTest, OnMountFailedFromMojo) {
  345. ASSERT_FALSE(host_->IsMounted());
  346. ASSERT_NO_FATAL_FAILURE(EstablishConnection());
  347. base::RunLoop run_loop;
  348. base::OnceClosure quit_closure = run_loop.QuitClosure();
  349. EXPECT_CALL(*host_delegate_, OnMountFailed(MountFailure::kUnknown, _))
  350. .WillOnce(RunOnceClosure(std::move(quit_closure)));
  351. SendMountFailed({});
  352. run_loop.Run();
  353. ASSERT_FALSE(host_->IsMounted());
  354. }
  355. TEST_F(DriveFsHostTest, OnMountFailedFromDbus) {
  356. ASSERT_FALSE(host_->IsMounted());
  357. EXPECT_CALL(*disk_manager_, UnmountPath(_, _)).Times(0);
  358. auto token = StartMount();
  359. base::RunLoop run_loop;
  360. base::OnceClosure quit_closure = run_loop.QuitClosure();
  361. EXPECT_CALL(*host_delegate_, OnMountFailed(MountFailure::kInvocation, _))
  362. .WillOnce(RunOnceClosure(std::move(quit_closure)));
  363. std::move(mount_callback_)
  364. .Run(ash::MountError::kInvalidMountOptions,
  365. {base::StrCat({"drivefs://", token}),
  366. "/media/drivefsroot/salt-g-ID",
  367. ash::MountType::kNetworkStorage,
  368. {}});
  369. run_loop.Run();
  370. ASSERT_FALSE(host_->IsMounted());
  371. EXPECT_FALSE(mojo_bootstrap::PendingConnectionManager::Get().OpenIpcChannel(
  372. token, {}));
  373. }
  374. TEST_F(DriveFsHostTest, DestroyBeforeMojoConnection) {
  375. auto token = StartMount();
  376. CallMountCallbackSuccess(token);
  377. base::RunLoop run_loop;
  378. EXPECT_CALL(*disk_manager_, UnmountPath("/media/drivefsroot/salt-g-ID", _))
  379. .WillOnce(base::test::RunClosure(run_loop.QuitClosure()));
  380. host_.reset();
  381. EXPECT_FALSE(mojo_bootstrap::PendingConnectionManager::Get().OpenIpcChannel(
  382. token, {}));
  383. run_loop.Run();
  384. }
  385. TEST_F(DriveFsHostTest, MountWhileAlreadyMounted) {
  386. DoMount();
  387. EXPECT_FALSE(host_->Mount());
  388. }
  389. TEST_F(DriveFsHostTest, UnsupportedAccountTypes) {
  390. EXPECT_CALL(*disk_manager_, MountPath(_, _, _, _, _, _, _)).Times(0);
  391. const AccountId unsupported_accounts[] = {
  392. AccountId::FromGaiaId("ID"),
  393. AccountId::FromUserEmail("test2@example.com"),
  394. };
  395. for (auto& account : unsupported_accounts) {
  396. host_delegate_ = std::make_unique<TestingDriveFsHostDelegate>(
  397. identity_test_env_.identity_manager(), account);
  398. host_ = std::make_unique<DriveFsHost>(
  399. profile_path_, host_delegate_.get(), host_delegate_.get(),
  400. network_connection_tracker_.get(), &clock_, disk_manager_.get(),
  401. std::make_unique<base::MockOneShotTimer>());
  402. EXPECT_FALSE(host_->Mount());
  403. EXPECT_FALSE(host_->IsMounted());
  404. }
  405. }
  406. TEST_F(DriveFsHostTest, GetAccessToken_UnmountDuringMojoRequest) {
  407. ASSERT_NO_FATAL_FAILURE(DoMount());
  408. base::RunLoop run_loop;
  409. delegate_.set_disconnect_handler(run_loop.QuitClosure());
  410. delegate_->GetAccessToken(
  411. "client ID", "app ID", {"scope1", "scope2"},
  412. base::BindLambdaForTesting([](mojom::AccessTokenStatus status,
  413. const std::string& token) { FAIL(); }));
  414. host_->Unmount();
  415. run_loop.Run();
  416. EXPECT_FALSE(host_->IsMounted());
  417. EXPECT_FALSE(identity_test_env_.IsAccessTokenRequestPending());
  418. }
  419. ACTION_P(CloneStruct, output) {
  420. *output = arg0.Clone();
  421. }
  422. TEST_F(DriveFsHostTest, OnSyncingStatusUpdate_ForwardToObservers) {
  423. ASSERT_NO_FATAL_FAILURE(DoMount());
  424. MockDriveFsHostObserver observer;
  425. base::ScopedObservation<DriveFsHost, DriveFsHostObserver> observation_scoper(
  426. &observer);
  427. observation_scoper.Observe(host_.get());
  428. auto status = mojom::SyncingStatus::New();
  429. status->item_events.emplace_back(absl::in_place, 12, 34, "filename.txt",
  430. mojom::ItemEvent::State::kInProgress, 123,
  431. 456, mojom::ItemEventReason::kPin);
  432. mojom::SyncingStatusPtr observed_status;
  433. EXPECT_CALL(observer, OnSyncingStatusUpdate(_))
  434. .WillOnce(CloneStruct(&observed_status));
  435. delegate_->OnSyncingStatusUpdate(status.Clone());
  436. delegate_.FlushForTesting();
  437. testing::Mock::VerifyAndClear(&observer);
  438. EXPECT_EQ(status, observed_status);
  439. }
  440. ACTION_P(CloneVectorOfStructs, output) {
  441. for (auto& s : arg0) {
  442. output->emplace_back(s.Clone());
  443. }
  444. }
  445. TEST_F(DriveFsHostTest, OnFilesChanged_ForwardToObservers) {
  446. ASSERT_NO_FATAL_FAILURE(DoMount());
  447. MockDriveFsHostObserver observer;
  448. base::ScopedObservation<DriveFsHost, DriveFsHostObserver> observation_scoper(
  449. &observer);
  450. observation_scoper.Observe(host_.get());
  451. std::vector<mojom::FileChangePtr> changes;
  452. changes.emplace_back(absl::in_place, base::FilePath("/create"),
  453. mojom::FileChange::Type::kCreate);
  454. changes.emplace_back(absl::in_place, base::FilePath("/delete"),
  455. mojom::FileChange::Type::kDelete);
  456. changes.emplace_back(absl::in_place, base::FilePath("/modify"),
  457. mojom::FileChange::Type::kModify);
  458. std::vector<mojom::FileChangePtr> observed_changes;
  459. EXPECT_CALL(observer, OnFilesChanged(_))
  460. .WillOnce(CloneVectorOfStructs(&observed_changes));
  461. delegate_->OnFilesChanged(mojo::Clone(changes));
  462. delegate_.FlushForTesting();
  463. testing::Mock::VerifyAndClear(&observer);
  464. EXPECT_EQ(changes, observed_changes);
  465. }
  466. TEST_F(DriveFsHostTest, OnError_ForwardToObservers) {
  467. ASSERT_NO_FATAL_FAILURE(DoMount());
  468. MockDriveFsHostObserver observer;
  469. base::ScopedObservation<DriveFsHost, DriveFsHostObserver> observation_scoper(
  470. &observer);
  471. observation_scoper.Observe(host_.get());
  472. auto error = mojom::DriveError::New(
  473. mojom::DriveError::Type::kCantUploadStorageFull, base::FilePath("/foo"));
  474. mojom::DriveErrorPtr observed_error;
  475. EXPECT_CALL(observer, OnError(_)).WillOnce(CloneStruct(&observed_error));
  476. delegate_->OnError(error.Clone());
  477. delegate_.FlushForTesting();
  478. testing::Mock::VerifyAndClear(&observer);
  479. EXPECT_EQ(error, observed_error);
  480. }
  481. TEST_F(DriveFsHostTest, OnError_IgnoreUnknownErrorTypes) {
  482. ASSERT_NO_FATAL_FAILURE(DoMount());
  483. MockDriveFsHostObserver observer;
  484. base::ScopedObservation<DriveFsHost, DriveFsHostObserver> observation_scoper(
  485. &observer);
  486. observation_scoper.Observe(host_.get());
  487. EXPECT_CALL(observer, OnError(_)).Times(0);
  488. delegate_->OnError(mojom::DriveError::New(
  489. static_cast<mojom::DriveError::Type>(
  490. static_cast<std::underlying_type_t<mojom::DriveError::Type>>(
  491. mojom::DriveError::Type::kMaxValue) +
  492. 1),
  493. base::FilePath("/foo")));
  494. delegate_.FlushForTesting();
  495. }
  496. TEST_F(DriveFsHostTest, DisplayConfirmDialog_ForwardToHandler) {
  497. ASSERT_NO_FATAL_FAILURE(DoMount());
  498. auto reason = mojom::DialogReason::New(
  499. mojom::DialogReason::Type::kEnableDocsOffline, base::FilePath());
  500. mojom::DialogReasonPtr observed_reason;
  501. host_->set_dialog_handler(base::BindLambdaForTesting(
  502. [&](const mojom::DialogReason& reason,
  503. base::OnceCallback<void(mojom::DialogResult)> callback) {
  504. observed_reason = reason.Clone();
  505. std::move(callback).Run(mojom::DialogResult::kAccept);
  506. }));
  507. bool called = false;
  508. delegate_->DisplayConfirmDialog(
  509. reason.Clone(),
  510. base::BindLambdaForTesting([&](mojom::DialogResult result) {
  511. EXPECT_EQ(mojom::DialogResult::kAccept, result);
  512. called = true;
  513. }));
  514. delegate_.FlushForTesting();
  515. EXPECT_EQ(reason, observed_reason);
  516. EXPECT_TRUE(called);
  517. }
  518. TEST_F(DriveFsHostTest, DisplayConfirmDialogImpl_IgnoreIfNoHandler) {
  519. ASSERT_NO_FATAL_FAILURE(DoMount());
  520. bool called = false;
  521. delegate_->DisplayConfirmDialog(
  522. mojom::DialogReason::New(mojom::DialogReason::Type::kEnableDocsOffline,
  523. base::FilePath()),
  524. base::BindLambdaForTesting([&](mojom::DialogResult result) {
  525. EXPECT_EQ(mojom::DialogResult::kNotDisplayed, result);
  526. called = true;
  527. }));
  528. delegate_.FlushForTesting();
  529. EXPECT_TRUE(called);
  530. }
  531. TEST_F(DriveFsHostTest, DisplayConfirmDialogImpl_IgnoreUnknownReasonTypes) {
  532. ASSERT_NO_FATAL_FAILURE(DoMount());
  533. host_->set_dialog_handler(base::BindRepeating(
  534. [](const mojom::DialogReason&,
  535. base::OnceCallback<void(mojom::DialogResult)>) { NOTREACHED(); }));
  536. bool called = false;
  537. delegate_->DisplayConfirmDialog(
  538. mojom::DialogReason::New(
  539. static_cast<mojom::DialogReason::Type>(
  540. static_cast<std::underlying_type_t<mojom::DialogReason::Type>>(
  541. mojom::DialogReason::Type::kMaxValue) +
  542. 1),
  543. base::FilePath()),
  544. base::BindLambdaForTesting([&](mojom::DialogResult result) {
  545. EXPECT_EQ(mojom::DialogResult::kNotDisplayed, result);
  546. called = true;
  547. }));
  548. delegate_.FlushForTesting();
  549. EXPECT_TRUE(called);
  550. }
  551. TEST_F(DriveFsHostTest, TeamDriveTracking) {
  552. ASSERT_NO_FATAL_FAILURE(DoMount());
  553. delegate_->OnTeamDrivesListReady({"a", "b"});
  554. delegate_.FlushForTesting();
  555. EXPECT_EQ(
  556. (std::set<std::string>{"a", "b"}),
  557. host_delegate_->GetDriveNotificationManager().team_drive_ids_for_test());
  558. delegate_->OnTeamDriveChanged(
  559. "c", mojom::DriveFsDelegate::CreateOrDelete::kCreated);
  560. delegate_.FlushForTesting();
  561. EXPECT_EQ(
  562. (std::set<std::string>{"a", "b", "c"}),
  563. host_delegate_->GetDriveNotificationManager().team_drive_ids_for_test());
  564. delegate_->OnTeamDriveChanged(
  565. "b", mojom::DriveFsDelegate::CreateOrDelete::kDeleted);
  566. delegate_.FlushForTesting();
  567. EXPECT_EQ(
  568. (std::set<std::string>{"a", "c"}),
  569. host_delegate_->GetDriveNotificationManager().team_drive_ids_for_test());
  570. }
  571. TEST_F(DriveFsHostTest, TeamDriveTrackingIgnoreChanges) {
  572. ASSERT_NO_FATAL_FAILURE(DoMount());
  573. EXPECT_EQ(
  574. std::set<std::string>(),
  575. host_delegate_->GetDriveNotificationManager().team_drive_ids_for_test());
  576. delegate_->OnTeamDriveChanged(
  577. "a", mojom::DriveFsDelegate::CreateOrDelete::kCreated);
  578. delegate_.FlushForTesting();
  579. EXPECT_EQ(
  580. std::set<std::string>(),
  581. host_delegate_->GetDriveNotificationManager().team_drive_ids_for_test());
  582. delegate_->OnTeamDriveChanged(
  583. "b", mojom::DriveFsDelegate::CreateOrDelete::kDeleted);
  584. delegate_.FlushForTesting();
  585. EXPECT_EQ(
  586. std::set<std::string>(),
  587. host_delegate_->GetDriveNotificationManager().team_drive_ids_for_test());
  588. }
  589. TEST_F(DriveFsHostTest, Invalidation) {
  590. ASSERT_NO_FATAL_FAILURE(DoMount());
  591. delegate_->OnTeamDrivesListReady({"a", "b"});
  592. delegate_.FlushForTesting();
  593. EXPECT_CALL(mock_drivefs_,
  594. FetchChangeLogImpl(
  595. std::vector<ChangeLogOptionPair>{{123, ""}, {456, "a"}}));
  596. for (auto& observer :
  597. host_delegate_->GetDriveNotificationManager().observers_for_test()) {
  598. observer.OnNotificationReceived({{"", 123}, {"a", 456}});
  599. }
  600. receiver_.FlushForTesting();
  601. }
  602. TEST_F(DriveFsHostTest, InvalidateAll) {
  603. ASSERT_NO_FATAL_FAILURE(DoMount());
  604. delegate_->OnTeamDrivesListReady({"a", "b"});
  605. delegate_.FlushForTesting();
  606. EXPECT_CALL(mock_drivefs_, FetchAllChangeLogs());
  607. for (auto& observer :
  608. host_delegate_->GetDriveNotificationManager().observers_for_test()) {
  609. observer.OnNotificationTimerFired();
  610. }
  611. receiver_.FlushForTesting();
  612. }
  613. TEST_F(DriveFsHostTest, RemoveDriveNotificationObserver) {
  614. ASSERT_NO_FATAL_FAILURE(DoMount());
  615. delegate_->OnTeamDrivesListReady({"a", "b"});
  616. delegate_.FlushForTesting();
  617. EXPECT_TRUE(!host_delegate_->GetDriveNotificationManager()
  618. .observers_for_test()
  619. .empty());
  620. host_.reset();
  621. EXPECT_FALSE(!host_delegate_->GetDriveNotificationManager()
  622. .observers_for_test()
  623. .empty());
  624. }
  625. TEST_F(DriveFsHostTest, Remount_CachedOnceOnly) {
  626. ASSERT_NO_FATAL_FAILURE(DoMount());
  627. // Request an access token.
  628. delegate_->GetAccessToken(
  629. "client ID", "app ID", {"scope1", "scope2"},
  630. base::BindLambdaForTesting(
  631. [&](mojom::AccessTokenStatus status, const std::string& token) {
  632. EXPECT_EQ(mojom::AccessTokenStatus::kSuccess, status);
  633. EXPECT_EQ("auth token", token);
  634. }));
  635. delegate_.FlushForTesting();
  636. EXPECT_TRUE(identity_test_env_.IsAccessTokenRequestPending());
  637. // Fulfill the request.
  638. identity_test_env_.WaitForAccessTokenRequestIfNecessaryAndRespondWithToken(
  639. "auth token", clock_.Now() + kTokenLifetime);
  640. EXPECT_FALSE(identity_test_env_.IsAccessTokenRequestPending());
  641. absl::optional<base::TimeDelta> delay = base::Seconds(5);
  642. EXPECT_CALL(*host_delegate_, OnUnmounted(delay));
  643. SendOnUnmounted(delay);
  644. base::RunLoop().RunUntilIdle();
  645. ASSERT_NO_FATAL_FAILURE(DoUnmount());
  646. // Second mount attempt should reuse already available token.
  647. ASSERT_NO_FATAL_FAILURE(DoMount());
  648. EXPECT_FALSE(identity_test_env_.IsAccessTokenRequestPending());
  649. EXPECT_EQ("auth token", init_access_token_.value_or(""));
  650. // But if it asks for token again it goes to identity manager.
  651. delegate_->GetAccessToken(
  652. "client ID", "app ID", {"scope1", "scope2"},
  653. base::BindLambdaForTesting(
  654. [&](mojom::AccessTokenStatus status, const std::string& token) {
  655. EXPECT_EQ(mojom::AccessTokenStatus::kSuccess, status);
  656. EXPECT_EQ("auth token 2", token);
  657. }));
  658. delegate_.FlushForTesting();
  659. EXPECT_TRUE(identity_test_env_.IsAccessTokenRequestPending());
  660. // Fulfill the request with a different token.
  661. identity_test_env_.WaitForAccessTokenRequestIfNecessaryAndRespondWithToken(
  662. "auth token 2", clock_.Now() + kTokenLifetime);
  663. EXPECT_FALSE(identity_test_env_.IsAccessTokenRequestPending());
  664. }
  665. TEST_F(DriveFsHostTest, Remount_RequestInflight) {
  666. ASSERT_NO_FATAL_FAILURE(DoMount());
  667. delegate_->GetAccessToken(
  668. "client ID", "app ID", {"scope1", "scope2"},
  669. base::BindLambdaForTesting([&](mojom::AccessTokenStatus status,
  670. const std::string& token) { FAIL(); }));
  671. absl::optional<base::TimeDelta> delay = base::Seconds(5);
  672. EXPECT_CALL(*host_delegate_, OnUnmounted(delay));
  673. SendOnUnmounted(delay);
  674. base::RunLoop().RunUntilIdle();
  675. ASSERT_NO_FATAL_FAILURE(DoUnmount());
  676. EXPECT_TRUE(identity_test_env_.IsAccessTokenRequestPending());
  677. // Now the response is ready.
  678. identity_test_env_.WaitForAccessTokenRequestIfNecessaryAndRespondWithToken(
  679. "auth token", clock_.Now() + kTokenLifetime);
  680. // Second mount will reuse previous token.
  681. ASSERT_NO_FATAL_FAILURE(DoMount());
  682. EXPECT_FALSE(identity_test_env_.IsAccessTokenRequestPending());
  683. EXPECT_EQ("auth token", init_access_token_.value_or(""));
  684. }
  685. TEST_F(DriveFsHostTest, Remount_RequestInflightCompleteAfterMount) {
  686. ASSERT_NO_FATAL_FAILURE(DoMount());
  687. delegate_->GetAccessToken(
  688. "client ID", "app ID", {"scope1", "scope2"},
  689. base::BindLambdaForTesting([&](mojom::AccessTokenStatus status,
  690. const std::string& token) { FAIL(); }));
  691. absl::optional<base::TimeDelta> delay = base::Seconds(5);
  692. EXPECT_CALL(*host_delegate_, OnUnmounted(delay));
  693. SendOnUnmounted(delay);
  694. base::RunLoop().RunUntilIdle();
  695. ASSERT_NO_FATAL_FAILURE(DoUnmount());
  696. EXPECT_TRUE(identity_test_env_.IsAccessTokenRequestPending());
  697. // Second mount will reuse previous token.
  698. ASSERT_NO_FATAL_FAILURE(DoMount());
  699. EXPECT_FALSE(init_access_token_);
  700. EXPECT_TRUE(identity_test_env_.IsAccessTokenRequestPending());
  701. // Now the response is ready.
  702. identity_test_env_.WaitForAccessTokenRequestIfNecessaryAndRespondWithToken(
  703. "auth token", clock_.Now() + kTokenLifetime);
  704. EXPECT_FALSE(identity_test_env_.IsAccessTokenRequestPending());
  705. // A new request will reuse the cached token.
  706. delegate_->GetAccessToken(
  707. "client ID", "app ID", {"scope1", "scope2"},
  708. base::BindLambdaForTesting(
  709. [&](mojom::AccessTokenStatus status, const std::string& token) {
  710. EXPECT_EQ(mojom::AccessTokenStatus::kSuccess, status);
  711. EXPECT_EQ("auth token", token);
  712. }));
  713. delegate_.FlushForTesting();
  714. EXPECT_FALSE(identity_test_env_.IsAccessTokenRequestPending());
  715. }
  716. TEST_F(DriveFsHostTest, ConnectToExtension) {
  717. ASSERT_NO_FATAL_FAILURE(DoMount());
  718. mojo::Remote<drivefs::mojom::NativeMessagingPort> remote;
  719. mojo::PendingRemote<drivefs::mojom::NativeMessagingHost> host_remote;
  720. auto receiver = host_remote.InitWithNewPipeAndPassReceiver();
  721. base::RunLoop run_loop;
  722. delegate_->ConnectToExtension(
  723. drivefs::mojom::ExtensionConnectionParams::New("foo"),
  724. remote.BindNewPipeAndPassReceiver(), std::move(host_remote),
  725. base::BindLambdaForTesting(
  726. [&](drivefs::mojom::DriveFsDelegate::ExtensionConnectionStatus
  727. status) {
  728. EXPECT_EQ(drivefs::mojom::DriveFsDelegate::
  729. ExtensionConnectionStatus::kExtensionNotFound,
  730. status);
  731. run_loop.Quit();
  732. }));
  733. run_loop.Run();
  734. EXPECT_EQ("foo", host_delegate_->get_last_extension_params().extension_id);
  735. }
  736. TEST_F(DriveFsHostTest, OnMirrorSyncingStatusUpdate_ForwardToObservers) {
  737. ASSERT_NO_FATAL_FAILURE(DoMount());
  738. MockDriveFsHostObserver observer;
  739. base::ScopedObservation<DriveFsHost, DriveFsHostObserver> observation_scoper(
  740. &observer);
  741. observation_scoper.Observe(host_.get());
  742. auto status = mojom::SyncingStatus::New();
  743. status->item_events.emplace_back(absl::in_place, 12, 34, "filename.txt",
  744. mojom::ItemEvent::State::kInProgress, 123,
  745. 456, mojom::ItemEventReason::kPin);
  746. mojom::SyncingStatusPtr observed_status;
  747. EXPECT_CALL(observer, OnMirrorSyncingStatusUpdate(_))
  748. .WillOnce(CloneStruct(&observed_status));
  749. delegate_->OnMirrorSyncingStatusUpdate(status.Clone());
  750. delegate_.FlushForTesting();
  751. testing::Mock::VerifyAndClear(&observer);
  752. EXPECT_EQ(status, observed_status);
  753. }
  754. } // namespace
  755. } // namespace drivefs