blob: 88b8e7e37171589e6a9666496737d02ee8cc18cd [file] [log] [blame]
// Copyright 2020 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/ambient/test/ambient_ash_test_base.h"
#include <memory>
#include <utility>
#include <vector>
#include "ash/ambient/ambient_access_token_controller.h"
#include "ash/ambient/ambient_constants.h"
#include "ash/ambient/ambient_photo_controller.h"
#include "ash/ambient/ui/ambient_background_image_view.h"
#include "ash/ambient/ui/ambient_container_view.h"
#include "ash/ambient/ui/media_string_view.h"
#include "ash/ambient/ui/photo_view.h"
#include "ash/assistant/ui/assistant_view_ids.h"
#include "ash/public/cpp/ambient/ambient_prefs.h"
#include "ash/public/cpp/ambient/fake_ambient_backend_controller_impl.h"
#include "ash/session/session_controller_impl.h"
#include "ash/shell.h"
#include "base/callback.h"
#include "base/files/file_util.h"
#include "base/memory/ptr_util.h"
#include "base/run_loop.h"
#include "base/sequenced_task_runner.h"
#include "base/threading/scoped_blocking_call.h"
#include "base/threading/sequenced_task_runner_handle.h"
#include "base/time/time.h"
#include "chromeos/constants/chromeos_features.h"
#include "chromeos/dbus/power/fake_power_manager_client.h"
#include "chromeos/dbus/power/power_manager_client.h"
#include "chromeos/dbus/power_manager/idle.pb.h"
#include "ui/gfx/image/image_skia.h"
#include "ui/gfx/image/image_unittest_util.h"
#include "ui/views/controls/label.h"
namespace ash {
namespace {
constexpr float kFastForwardFactor = 1.0001;
} // namespace
class TestAmbientURLLoaderImpl : public AmbientURLLoader {
public:
TestAmbientURLLoaderImpl() = default;
~TestAmbientURLLoaderImpl() override = default;
// AmbientURLLoader:
void Download(
const std::string& url,
network::SimpleURLLoader::BodyAsStringCallback callback) override {
// Reply with a unique string each time to avoid check to skip loading
// duplicate images.
std::unique_ptr<std::string> data = std::make_unique<std::string>(
data_ ? *data_ : base::StringPrintf("test_image_%i", count_));
count_++;
// Pretend to respond asynchronously.
base::SequencedTaskRunnerHandle::Get()->PostDelayedTask(
FROM_HERE, base::BindOnce(std::move(callback), std::move(data)),
base::TimeDelta::FromMilliseconds(1));
}
void DownloadToFile(
const std::string& url,
network::SimpleURLLoader::DownloadToFileCompleteCallback callback,
const base::FilePath& file_path) override {
if (!data_) {
base::SequencedTaskRunnerHandle::Get()->PostTask(
FROM_HERE, base::BindOnce(std::move(callback), base::FilePath()));
return;
}
if (!WriteFile(file_path, *data_)) {
LOG(WARNING) << "error writing file to file_path: " << file_path;
base::SequencedTaskRunnerHandle::Get()->PostTask(
FROM_HERE, base::BindOnce(std::move(callback), base::FilePath()));
return;
}
base::SequencedTaskRunnerHandle::Get()->PostTask(
FROM_HERE, base::BindOnce(std::move(callback), file_path));
}
void SetData(std::unique_ptr<std::string> data) { data_ = std::move(data); }
private:
bool WriteFile(const base::FilePath& file_path, const std::string& data) {
base::ScopedBlockingCall blocking(FROM_HERE, base::BlockingType::MAY_BLOCK);
return base::WriteFile(file_path, data);
}
int count_ = 0;
// If not null, will return this data.
std::unique_ptr<std::string> data_;
};
class TestAmbientImageDecoderImpl : public AmbientImageDecoder {
public:
TestAmbientImageDecoderImpl() = default;
~TestAmbientImageDecoderImpl() override = default;
// AmbientImageDecoder:
void Decode(
const std::vector<uint8_t>& encoded_bytes,
base::OnceCallback<void(const gfx::ImageSkia&)> callback) override {
gfx::ImageSkia image =
image_ ? *image_ : gfx::test::CreateImageSkia(width_, height_);
// Only use once.
image_.reset();
// Pretend to respond asynchronously.
base::SequencedTaskRunnerHandle::Get()->PostTask(
FROM_HERE, base::BindOnce(std::move(callback), image));
}
void SetImageSize(int width, int height) {
width_ = width;
height_ = height;
}
void SetImage(const gfx::ImageSkia& image) { image_ = image; }
private:
// Width and height of test images.
int width_ = 10;
int height_ = 20;
// If set, will replay this image.
base::Optional<gfx::ImageSkia> image_;
};
AmbientAshTestBase::AmbientAshTestBase()
: AshTestBase(base::test::TaskEnvironment::TimeSource::MOCK_TIME) {}
AmbientAshTestBase::~AmbientAshTestBase() = default;
void AmbientAshTestBase::SetUp() {
scoped_feature_list_.InitAndEnableFeatureWithParameters(
chromeos::features::kAmbientModeFeature,
{{"GeoPhotosEnabled", "true"},
{"CapturedOnPixelPhotosEnabled", "false"}});
image_downloader_ = std::make_unique<TestImageDownloader>();
ambient_client_ = std::make_unique<TestAmbientClient>(&wake_lock_provider_);
chromeos::PowerManagerClient::InitializeFake();
AshTestBase::SetUp();
// Need to reset first and then assign the TestPhotoClient because can only
// have one instance of AmbientBackendController.
ambient_controller()->set_backend_controller_for_testing(nullptr);
ambient_controller()->set_backend_controller_for_testing(
std::make_unique<FakeAmbientBackendControllerImpl>());
photo_controller()->set_url_loader_for_testing(
std::make_unique<TestAmbientURLLoaderImpl>());
photo_controller()->set_image_decoder_for_testing(
std::make_unique<TestAmbientImageDecoderImpl>());
token_controller()->SetTokenUsageBufferForTesting(
base::TimeDelta::FromSeconds(30));
SetAmbientModeEnabled(true);
base::RunLoop().RunUntilIdle();
}
void AmbientAshTestBase::TearDown() {
ambient_client_.reset();
image_downloader_.reset();
AshTestBase::TearDown();
}
void AmbientAshTestBase::SetAmbientModeEnabled(bool enabled) {
Shell::Get()->session_controller()->GetPrimaryUserPrefService()->SetBoolean(
ambient::prefs::kAmbientModeEnabled, enabled);
}
void AmbientAshTestBase::ShowAmbientScreen() {
// The widget will be destroyed in |AshTestBase::TearDown()|.
ambient_controller()->ShowUi();
// The UI only shows when images are downloaded to avoid showing blank screen.
FastForwardToNextImage();
// Flush the message loop to finish all async calls.
base::RunLoop().RunUntilIdle();
}
void AmbientAshTestBase::HideAmbientScreen() {
ambient_controller()->ShowHiddenUi();
}
void AmbientAshTestBase::CloseAmbientScreen() {
ambient_controller()->CloseUi();
}
void AmbientAshTestBase::LockScreen() {
GetSessionControllerClient()->LockScreen();
}
void AmbientAshTestBase::UnlockScreen() {
GetSessionControllerClient()->UnlockScreen();
}
bool AmbientAshTestBase::IsLocked() {
return Shell::Get()->session_controller()->IsScreenLocked();
}
void AmbientAshTestBase::SimulateSystemSuspendAndWait(
power_manager::SuspendImminent::Reason reason) {
chromeos::FakePowerManagerClient::Get()->SendSuspendImminent(reason);
base::RunLoop().RunUntilIdle();
}
void AmbientAshTestBase::SimulateSystemResumeAndWait() {
chromeos::FakePowerManagerClient::Get()->SendSuspendDone();
base::RunLoop().RunUntilIdle();
}
void AmbientAshTestBase::SetScreenIdleStateAndWait(bool is_screen_dimmed,
bool is_off) {
power_manager::ScreenIdleState screen_idle_state;
screen_idle_state.set_dimmed(is_screen_dimmed);
screen_idle_state.set_off(is_off);
chromeos::FakePowerManagerClient::Get()->SendScreenIdleStateChanged(
screen_idle_state);
base::RunLoop().RunUntilIdle();
}
void AmbientAshTestBase::SetScreenBrightnessAndWait(double percent) {
power_manager::BacklightBrightnessChange change;
change.set_percent(percent);
chromeos::FakePowerManagerClient::Get()->SendScreenBrightnessChanged(change);
base::RunLoop().RunUntilIdle();
}
views::View* AmbientAshTestBase::GetMediaStringViewTextContainer() {
return GetMediaStringView()->media_text_container_for_testing();
}
views::Label* AmbientAshTestBase::GetMediaStringViewTextLabel() {
return GetMediaStringView()->media_text_label_for_testing();
}
void AmbientAshTestBase::SimulateMediaMetadataChanged(
media_session::MediaMetadata metadata) {
GetMediaStringView()->MediaSessionMetadataChanged(metadata);
}
void AmbientAshTestBase::SimulateMediaPlaybackStateChanged(
media_session::mojom::MediaPlaybackState state) {
// Creates media session info.
media_session::mojom::MediaSessionInfoPtr session_info(
media_session::mojom::MediaSessionInfo::New());
session_info->playback_state = state;
// Simulate media session info changed.
GetMediaStringView()->MediaSessionInfoChanged(std::move(session_info));
}
void AmbientAshTestBase::SetPhotoViewImageSize(int width, int height) {
auto* image_decoder = static_cast<TestAmbientImageDecoderImpl*>(
photo_controller()->get_image_decoder_for_testing());
image_decoder->SetImageSize(width, height);
}
AmbientBackgroundImageView*
AmbientAshTestBase::GetAmbientBackgroundImageView() {
return static_cast<AmbientBackgroundImageView*>(container_view()->GetViewByID(
AssistantViewID::kAmbientBackgroundImageView));
}
MediaStringView* AmbientAshTestBase::GetMediaStringView() {
return static_cast<MediaStringView*>(
container_view()->GetViewByID(AssistantViewID::kAmbientMediaStringView));
}
void AmbientAshTestBase::FastForwardToInactivity() {
task_environment()->FastForwardBy(
kFastForwardFactor * AmbientController::kAutoShowWaitTimeInterval);
}
void AmbientAshTestBase::FastForwardToNextImage() {
task_environment()->FastForwardBy(kFastForwardFactor * kPhotoRefreshInterval);
}
void AmbientAshTestBase::FastForwardTiny() {
// `TestAmbientURLLoaderImpl` has a small delay (1ms) to fake download delay,
// here we fake plenty of time to download the image.
task_environment()->FastForwardBy(base::TimeDelta::FromMilliseconds(10));
}
void AmbientAshTestBase::FastForwardToLockScreen() {
task_environment()->FastForwardBy(kFastForwardFactor * kLockScreenDelay);
}
void AmbientAshTestBase::FastForwardHalfLockScreenDelay() {
task_environment()->FastForwardBy(0.5 * kFastForwardFactor *
kLockScreenDelay);
}
void AmbientAshTestBase::SetPowerStateCharging() {
power_manager::PowerSupplyProperties proto;
proto.set_battery_state(
power_manager::PowerSupplyProperties_BatteryState_CHARGING);
proto.set_external_power(
power_manager::PowerSupplyProperties_ExternalPower_AC);
PowerStatus::Get()->SetProtoForTesting(proto);
ambient_controller()->OnPowerStatusChanged();
}
void AmbientAshTestBase::SetPowerStateDischarging() {
power_manager::PowerSupplyProperties proto;
proto.set_battery_state(
power_manager::PowerSupplyProperties_BatteryState_DISCHARGING);
proto.set_external_power(
power_manager::PowerSupplyProperties_ExternalPower_DISCONNECTED);
PowerStatus::Get()->SetProtoForTesting(proto);
ambient_controller()->OnPowerStatusChanged();
}
void AmbientAshTestBase::SetPowerStateFull() {
power_manager::PowerSupplyProperties proto;
proto.set_battery_state(
power_manager::PowerSupplyProperties_BatteryState_FULL);
proto.set_external_power(
power_manager::PowerSupplyProperties_ExternalPower_AC);
PowerStatus::Get()->SetProtoForTesting(proto);
ambient_controller()->OnPowerStatusChanged();
}
void AmbientAshTestBase::FastForwardToRefreshWeather() {
task_environment()->FastForwardBy(1.2 * kWeatherRefreshInterval);
}
int AmbientAshTestBase::GetNumOfActiveWakeLocks(
device::mojom::WakeLockType type) {
base::RunLoop run_loop;
int result_count = 0;
wake_lock_provider_.GetActiveWakeLocksForTests(
type, base::BindOnce(
[](base::RunLoop* run_loop, int* result_count, int32_t count) {
*result_count = count;
run_loop->Quit();
},
&run_loop, &result_count));
run_loop.Run();
return result_count;
}
void AmbientAshTestBase::IssueAccessToken(const std::string& token,
bool with_error) {
ambient_client_->IssueAccessToken(token, with_error);
}
bool AmbientAshTestBase::IsAccessTokenRequestPending() const {
return ambient_client_->IsAccessTokenRequestPending();
}
base::TimeDelta AmbientAshTestBase::GetRefreshTokenDelay() {
return token_controller()->GetTimeUntilReleaseForTesting();
}
AmbientController* AmbientAshTestBase::ambient_controller() {
return Shell::Get()->ambient_controller();
}
AmbientPhotoController* AmbientAshTestBase::photo_controller() {
return ambient_controller()->ambient_photo_controller();
}
AmbientContainerView* AmbientAshTestBase::container_view() {
return ambient_controller()->get_container_view_for_testing();
}
AmbientAccessTokenController* AmbientAshTestBase::token_controller() {
return ambient_controller()->access_token_controller_for_testing();
}
FakeAmbientBackendControllerImpl* AmbientAshTestBase::backend_controller() {
return static_cast<FakeAmbientBackendControllerImpl*>(
ambient_controller()->ambient_backend_controller());
}
void AmbientAshTestBase::FetchTopics() {
photo_controller()->FetchTopicsForTesting();
}
void AmbientAshTestBase::FetchImage() {
photo_controller()->FetchImageForTesting();
}
void AmbientAshTestBase::FetchBackupImages() {
photo_controller()->FetchBackupImagesForTesting();
}
void AmbientAshTestBase::SetUrlLoaderData(std::unique_ptr<std::string> data) {
auto* url_loader_ = static_cast<TestAmbientURLLoaderImpl*>(
photo_controller()->get_url_loader_for_testing());
url_loader_->SetData(std::move(data));
}
void AmbientAshTestBase::SetImageDecoderImage(const gfx::ImageSkia& image) {
auto* image_decoder = static_cast<TestAmbientImageDecoderImpl*>(
photo_controller()->get_image_decoder_for_testing());
image_decoder->SetImage(image);
}
} // namespace ash