| // Copyright 2023 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/crosapi/screen_ai_downloader_ash.h" |
| |
| #include "base/task/sequenced_task_runner.h" |
| #include "base/test/task_environment.h" |
| #include "base/test/test_future.h" |
| #include "chrome/browser/screen_ai/screen_ai_downloader_chromeos.h" |
| #include "testing/gtest/include/gtest/gtest.h" |
| |
| namespace crosapi { |
| |
| class FakeScreenAIDownloader : public screen_ai::ScreenAIDownloaderChromeOS { |
| public: |
| void SetLastUsageTime() override {} |
| void DownloadComponentInternal() override { |
| // The passed file path is not used and just indicates that the component |
| // exists. |
| SetComponentFolder(base::FilePath(FILE_PATH_LITERAL("tmp"))); |
| } |
| }; |
| |
| class ScreenAIDownloaderAshTest : public testing::Test { |
| public: |
| ScreenAIDownloaderAshTest() = default; |
| |
| void GetComponentFolder( |
| bool download_if_needed, |
| ScreenAIDownloaderAsh::GetComponentFolderCallback callback) { |
| downloader_remote_->GetComponentFolder( |
| /*download_if_needed=*/true, std::move(callback)); |
| FlushForTesting(); |
| } |
| |
| void FlushForTesting() { downloader_ash_.receivers_.FlushForTesting(); } |
| |
| bool HasPendingCallbacks() { |
| return !downloader_ash_.pending_download_callback_map_.empty(); |
| } |
| |
| bool HasReceivers() { return !downloader_ash_.receivers_.empty(); } |
| |
| protected: |
| base::test::SingleThreadTaskEnvironment task_environment_; |
| FakeScreenAIDownloader install_state_; |
| crosapi::ScreenAIDownloaderAsh downloader_ash_; |
| mojo::Remote<crosapi::mojom::ScreenAIDownloader> downloader_remote_; |
| }; |
| |
| TEST_F(ScreenAIDownloaderAshTest, EnsurePendingCallbackDestruction) { |
| downloader_ash_.Bind(downloader_remote_.BindNewPipeAndPassReceiver()); |
| EXPECT_TRUE(HasReceivers()); |
| |
| // `kDownloading` will create a pending callback. |
| install_state_.SetStateForTesting( |
| screen_ai::ScreenAIInstallState::State::kDownloading); |
| GetComponentFolder( |
| /*download_if_needed=*/true, |
| base::BindOnce([](const std::optional<::base::FilePath>& file_path) { |
| // do nothing |
| })); |
| EXPECT_TRUE(HasPendingCallbacks()); |
| |
| // Destroying the remote will trigger a disconnect handler set in the |
| // receiver, which will clear out any pending callbacks. |
| downloader_remote_.reset(); |
| FlushForTesting(); |
| EXPECT_FALSE(HasPendingCallbacks()); |
| EXPECT_FALSE(HasReceivers()); |
| } |
| |
| class ScreenAIDownloaderAshReplyTest |
| : public ScreenAIDownloaderAshTest, |
| public testing::WithParamInterface<testing::tuple< |
| screen_ai::ScreenAIInstallState::State /*prior_state_*/, |
| bool /*start_observing_before_call_*/, |
| bool /*request_download_if_needed_*/, |
| bool /*fake_download_success_*/>> { |
| public: |
| ScreenAIDownloaderAshReplyTest() { |
| downloader_ash_.Bind(downloader_remote_.BindNewPipeAndPassReceiver()); |
| if (start_observing_before_call_) { |
| base::test::TestFuture<const std::optional<::base::FilePath>&> future; |
| // Calling `GetComponentFolder` for the first time sets the observer. |
| GetComponentFolder( |
| /*download_if_needed=*/true, future.GetCallback()); |
| EXPECT_TRUE(future.Wait()); |
| |
| // Remove downloaded component path and reset state. |
| install_state_.ResetForTesting(); |
| |
| EXPECT_TRUE(downloader_ash_.install_state_observer_.IsObserving()); |
| } |
| |
| if (prior_state_ == screen_ai::ScreenAIInstallState::State::kDownloaded) { |
| // The component is already downloaded. |
| install_state_.DownloadComponentInternal(); |
| } |
| install_state_.SetStateForTesting(prior_state_); |
| } |
| |
| void MockDownloadCompletion() { |
| if (fake_download_success_) { |
| install_state_.DownloadComponentInternal(); |
| } else { |
| install_state_.SetState( |
| screen_ai::ScreenAIInstallState::State::kDownloadFailed); |
| } |
| } |
| |
| protected: |
| // Test params. |
| screen_ai::ScreenAIInstallState::State prior_state_ = std::get<0>(GetParam()); |
| bool start_observing_before_call_ = std::get<1>(GetParam()); |
| bool request_download_if_needed_ = std::get<2>(GetParam()); |
| bool fake_download_success_ = std::get<3>(GetParam()); |
| }; |
| |
| // Tests if the callback function of `GetComponentFolder` always gets called |
| // regardless of the prior state in `ScreenAIInstallState`, |
| // `ScreenAIDownloaderAsh`, and call params. The state is composed of: |
| // - Existing state in `ScreenAIInstallState`. |
| // - If `ScreenAIDownloaderAsh` is observing `ScreenAIInstallState` before the |
| // call. |
| // - Call parameter `download_if_needed`. |
| // - If fake download should result successful. |
| TEST_P(ScreenAIDownloaderAshReplyTest, EnsureReplyInAllStates) { |
| base::test::TestFuture<const std::optional<::base::FilePath>&> future; |
| GetComponentFolder( |
| /*download_if_needed=*/request_download_if_needed_, future.GetCallback()); |
| |
| // A `downloading` state will eventually result in a state change to |
| // `downloaded` or `failed`. |
| if (prior_state_ == screen_ai::ScreenAIInstallState::State::kDownloading) { |
| MockDownloadCompletion(); |
| } |
| |
| EXPECT_TRUE(future.Wait()); |
| } |
| |
| INSTANTIATE_TEST_SUITE_P( |
| All, |
| ScreenAIDownloaderAshReplyTest, |
| testing::Combine( |
| testing::Values(screen_ai::ScreenAIInstallState::State::kNotDownloaded, |
| screen_ai::ScreenAIInstallState::State::kDownloading, |
| screen_ai::ScreenAIInstallState::State::kDownloadFailed, |
| screen_ai::ScreenAIInstallState::State::kDownloaded), |
| testing::Bool(), |
| testing::Bool(), |
| testing::Bool())); |
| |
| } // namespace crosapi |