| // Copyright 2022 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/wallpaper/wallpaper_drivefs_delegate_impl.h" |
| |
| #include <memory> |
| #include <vector> |
| |
| #include "ash/shell.h" |
| #include "ash/wallpaper/wallpaper_controller_impl.h" |
| #include "base/barrier_closure.h" |
| #include "base/files/file.h" |
| #include "base/files/file_path.h" |
| #include "base/files/file_util.h" |
| #include "base/files/scoped_temp_dir.h" |
| #include "base/memory/ref_counted_memory.h" |
| #include "base/memory/scoped_refptr.h" |
| #include "base/run_loop.h" |
| #include "base/test/bind.h" |
| #include "base/threading/thread_restrictions.h" |
| #include "base/time/time.h" |
| #include "chrome/browser/ash/drive/drive_integration_service.h" |
| #include "chrome/browser/ash/drive/drive_integration_service_browser_test_base.h" |
| #include "chrome/browser/ash/drive/file_system_util.h" |
| #include "chrome/browser/ash/profiles/profile_helper.h" |
| #include "chrome/browser/profiles/profile.h" |
| #include "chrome/browser/ui/browser.h" |
| #include "chromeos/ash/components/drivefs/fake_drivefs.h" |
| #include "chromeos/ash/components/drivefs/mojom/drivefs.mojom.h" |
| #include "components/account_id/account_id.h" |
| #include "components/user_manager/user.h" |
| #include "content/public/test/browser_test.h" |
| #include "mojo/public/cpp/bindings/remote.h" |
| #include "testing/gmock/include/gmock/gmock.h" |
| #include "testing/gtest/include/gtest/gtest.h" |
| #include "third_party/skia/include/core/SkBitmap.h" |
| #include "third_party/skia/include/core/SkColor.h" |
| #include "ui/gfx/codec/jpeg_codec.h" |
| #include "ui/gfx/image/image_skia.h" |
| #include "ui/gfx/image/image_unittest_util.h" |
| |
| namespace ash { |
| |
| namespace { |
| |
| scoped_refptr<base::RefCountedBytes> EncodeImage(const gfx::ImageSkia& image) { |
| std::optional<std::vector<uint8_t>> data = |
| gfx::JPEGCodec::Encode(*(image.bitmap()), /*quality=*/90); |
| return base::MakeRefCounted<base::RefCountedBytes>(std::move(data).value()); |
| } |
| |
| WallpaperDriveFsDelegate* GetWallpaperDriveFsDelegate() { |
| Shell* shell = Shell::Get(); |
| auto* wallpaper_controller = shell->wallpaper_controller(); |
| return wallpaper_controller->drivefs_delegate_for_testing(); |
| } |
| |
| // Saves a test wallpaper file. If `target` is empty, will default to the |
| // DriveFS wallpaper path. |
| void SaveTestWallpaperFile(const AccountId& account_id, base::FilePath target) { |
| ASSERT_FALSE(target.empty()) << "target FilePath is required to be non-empty"; |
| base::ScopedAllowBlockingForTesting allow_blocking; |
| if (!base::DirectoryExists(target.DirName())) { |
| ASSERT_TRUE(base::CreateDirectory(target.DirName())); |
| } |
| auto data = EncodeImage(gfx::test::CreateImageSkia(/*size=*/16)); |
| ASSERT_TRUE(base::WriteFile(target, base::span(*data))); |
| } |
| |
| } // namespace |
| |
| class WallpaperDriveFsDelegateImplBrowserTest |
| : public drive::DriveIntegrationServiceBrowserTestBase { |
| public: |
| WallpaperDriveFsDelegateImplBrowserTest() = default; |
| |
| WallpaperDriveFsDelegateImplBrowserTest( |
| const WallpaperDriveFsDelegateImplBrowserTest&) = delete; |
| WallpaperDriveFsDelegateImplBrowserTest& operator=( |
| const WallpaperDriveFsDelegateImplBrowserTest&) = delete; |
| |
| ~WallpaperDriveFsDelegateImplBrowserTest() override = default; |
| |
| const AccountId& GetAccountId() const { |
| user_manager::User* user = |
| ProfileHelper::Get()->GetUserByProfile(browser()->profile()); |
| DCHECK(user); |
| return user->GetAccountId(); |
| } |
| |
| bool SaveWallpaperSync(const AccountId& account_id, |
| const base::FilePath& source) { |
| base::RunLoop loop; |
| bool out = false; |
| GetWallpaperDriveFsDelegate()->SaveWallpaper( |
| account_id, source, |
| base::BindLambdaForTesting([&out, &loop](bool success) { |
| out = success; |
| loop.Quit(); |
| })); |
| loop.Run(); |
| return out; |
| } |
| |
| base::Time GetWallpaperModificationTimeSync(const AccountId& account_id) { |
| base::RunLoop loop; |
| base::Time out; |
| GetWallpaperDriveFsDelegate()->GetWallpaperModificationTime( |
| account_id, base::BindLambdaForTesting([&out, &loop](base::Time time) { |
| out = time; |
| loop.Quit(); |
| })); |
| loop.Run(); |
| return out; |
| } |
| |
| std::vector<drivefs::mojom::FileChangePtr> CreateWallpaperFileChange() { |
| std::vector<drivefs::mojom::FileChangePtr> file_changes; |
| drive::DriveIntegrationService* drive_integration_service = |
| drive::util::GetIntegrationServiceByProfile(browser()->profile()); |
| |
| base::FilePath fake_wallpaper_notification_path( |
| &base::FilePath::kSeparators[0]); |
| |
| EXPECT_TRUE( |
| drive_integration_service->GetMountPointPath().AppendRelativePath( |
| GetWallpaperDriveFsDelegate()->GetWallpaperPath(GetAccountId()), |
| &fake_wallpaper_notification_path)); |
| |
| drivefs::mojom::FileChangePtr wallpaper_change = |
| drivefs::mojom::FileChange::New( |
| fake_wallpaper_notification_path, |
| drivefs::mojom::FileChange::Type::kModify); |
| |
| file_changes.push_back(std::move(wallpaper_change)); |
| return file_changes; |
| } |
| }; |
| |
| IN_PROC_BROWSER_TEST_F(WallpaperDriveFsDelegateImplBrowserTest, |
| EmptyBaseTimeIfNoDriveFs) { |
| InitTestFileMountRoot(browser()->profile()); |
| SaveTestWallpaperFile( |
| GetAccountId(), |
| GetWallpaperDriveFsDelegate()->GetWallpaperPath(GetAccountId())); |
| |
| drive::DriveIntegrationService* drive_integration_service = |
| drive::util::GetIntegrationServiceByProfile(browser()->profile()); |
| ASSERT_TRUE(drive_integration_service); |
| drive_integration_service->SetEnabled(false); |
| |
| const base::Time modification_time = |
| GetWallpaperModificationTimeSync(GetAccountId()); |
| EXPECT_EQ(modification_time, base::Time()) |
| << "DriveFS disabled should result in empty time"; |
| } |
| |
| IN_PROC_BROWSER_TEST_F(WallpaperDriveFsDelegateImplBrowserTest, |
| RespondsWithModifiedAtTime) { |
| InitTestFileMountRoot(browser()->profile()); |
| |
| base::ScopedAllowBlockingForTesting allow_blocking; |
| const base::FilePath drivefs_wallpaper_path = |
| GetWallpaperDriveFsDelegate()->GetWallpaperPath(GetAccountId()); |
| ASSERT_FALSE(base::PathExists(drivefs_wallpaper_path)); |
| |
| SaveTestWallpaperFile(GetAccountId(), drivefs_wallpaper_path); |
| |
| base::File::Info file_info; |
| ASSERT_TRUE(base::GetFileInfo(drivefs_wallpaper_path, &file_info)); |
| |
| const base::Time drivefs_modification_time = |
| GetWallpaperModificationTimeSync(GetAccountId()); |
| |
| EXPECT_EQ(drivefs_modification_time, file_info.last_modified) |
| << "modification_time matches file info time"; |
| } |
| |
| IN_PROC_BROWSER_TEST_F(WallpaperDriveFsDelegateImplBrowserTest, SaveWallpaper) { |
| InitTestFileMountRoot(browser()->profile()); |
| |
| base::FilePath drivefs_wallpaper_path = |
| GetWallpaperDriveFsDelegate()->GetWallpaperPath(GetAccountId()); |
| ASSERT_FALSE(drivefs_wallpaper_path.empty()); |
| |
| base::ScopedAllowBlockingForTesting scoped_allow_blocking; |
| // Write a jpg file to a tmp directory. This file will be copied into DriveFS. |
| base::ScopedTempDir scoped_temp_dir; |
| ASSERT_TRUE(scoped_temp_dir.CreateUniqueTempDir()); |
| base::FilePath source_jpg = scoped_temp_dir.GetPath().Append("source.jpg"); |
| SaveTestWallpaperFile(GetAccountId(), source_jpg); |
| |
| // `SaveWallpaper` should succeed while DriveFS is enabled. |
| EXPECT_TRUE(SaveWallpaperSync(GetAccountId(), source_jpg)); |
| // source.jpg was copied to DriveFS wallpaper path. |
| EXPECT_TRUE(base::PathExists(drivefs_wallpaper_path)); |
| } |
| |
| IN_PROC_BROWSER_TEST_F(WallpaperDriveFsDelegateImplBrowserTest, |
| SaveWallpaperDriveFsDisabled) { |
| InitTestFileMountRoot(browser()->profile()); |
| base::ScopedAllowBlockingForTesting scoped_allow_blocking; |
| |
| // Write a jpg file to a tmp directory. This file will be copied into DriveFS. |
| base::ScopedTempDir scoped_temp_dir; |
| ASSERT_TRUE(scoped_temp_dir.CreateUniqueTempDir()); |
| base::FilePath source_jpg = scoped_temp_dir.GetPath().Append("source.jpg"); |
| SaveTestWallpaperFile(GetAccountId(), source_jpg); |
| |
| base::FilePath drivefs_wallpaper_path = |
| GetWallpaperDriveFsDelegate()->GetWallpaperPath(GetAccountId()); |
| ASSERT_FALSE(drivefs_wallpaper_path.empty()); |
| |
| // Call `SaveWallpaper` while DriveFS is disabled. No file should be written. |
| auto* drive_integration_service = |
| drive::util::GetIntegrationServiceByProfile(browser()->profile()); |
| drive_integration_service->SetEnabled(false); |
| EXPECT_FALSE(SaveWallpaperSync(GetAccountId(), source_jpg)); |
| EXPECT_FALSE(base::PathExists(drivefs_wallpaper_path)); |
| } |
| |
| IN_PROC_BROWSER_TEST_F(WallpaperDriveFsDelegateImplBrowserTest, |
| WaitForWallpaperChange) { |
| InitTestFileMountRoot(browser()->profile()); |
| |
| base::RunLoop loop; |
| |
| GetWallpaperDriveFsDelegate()->WaitForWallpaperChange( |
| GetAccountId(), base::BindLambdaForTesting([&loop](bool success) { |
| EXPECT_TRUE(success); |
| loop.Quit(); |
| })); |
| |
| // Send the fake wallpaper file change notification. |
| drivefs::FakeDriveFs* fake_drivefs = |
| GetFakeDriveFsForProfile(browser()->profile()); |
| fake_drivefs->delegate()->OnFilesChanged(CreateWallpaperFileChange()); |
| |
| loop.Run(); |
| } |
| |
| IN_PROC_BROWSER_TEST_F(WallpaperDriveFsDelegateImplBrowserTest, |
| WaitForWallpaperChangeWithDriveFsDisabled) { |
| drive::DriveIntegrationService* drive_integration_service = |
| drive::util::GetIntegrationServiceByProfile(browser()->profile()); |
| drive_integration_service->SetEnabled(false); |
| |
| base::RunLoop loop; |
| |
| // Responds immediately with `success=false` even without receiving any file |
| // change notifications because DriveFS is disabled. |
| GetWallpaperDriveFsDelegate()->WaitForWallpaperChange( |
| GetAccountId(), base::BindLambdaForTesting([&loop](bool success) { |
| EXPECT_FALSE(success); |
| loop.Quit(); |
| })); |
| |
| loop.Run(); |
| } |
| |
| IN_PROC_BROWSER_TEST_F(WallpaperDriveFsDelegateImplBrowserTest, |
| WaitForWallpaperChangeMultipleTimes) { |
| InitTestFileMountRoot(browser()->profile()); |
| |
| base::RunLoop loop; |
| // Make sure that closure is called twice before `loop` quits. |
| base::RepeatingClosure barrier = |
| base::BarrierClosure(/*num_closures=*/2, loop.QuitClosure()); |
| |
| // The first `WaitForWallpaperChange` call should respond with false when |
| // `WaitForWallpaperChange` is called a second time before receiving a file |
| // change notification. |
| GetWallpaperDriveFsDelegate()->WaitForWallpaperChange( |
| GetAccountId(), base::BindLambdaForTesting([&barrier](bool success) { |
| EXPECT_FALSE(success); |
| barrier.Run(); |
| })); |
| |
| // This call to `WaitForWallpaperChange` should succeed upon receiving the |
| // file change notification. |
| GetWallpaperDriveFsDelegate()->WaitForWallpaperChange( |
| GetAccountId(), base::BindLambdaForTesting([&barrier](bool success) { |
| EXPECT_TRUE(success); |
| barrier.Run(); |
| })); |
| |
| // Send the fake wallpaper file change notification. |
| drivefs::FakeDriveFs* fake_drivefs = |
| GetFakeDriveFsForProfile(browser()->profile()); |
| fake_drivefs->delegate()->OnFilesChanged(CreateWallpaperFileChange()); |
| |
| loop.Run(); |
| } |
| |
| } // namespace ash |