blob: 70aa5f5a471b3310a14075a738dd0569b7231c6d [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/ambient_photo_controller.h"
#include <memory>
#include <utility>
#include "ash/ambient/ambient_constants.h"
#include "ash/ambient/ambient_controller.h"
#include "ash/ambient/model/ambient_backend_model.h"
#include "ash/ambient/test/ambient_ash_test_base.h"
#include "ash/public/cpp/ambient/ambient_backend_controller.h"
#include "ash/public/cpp/ambient/fake_ambient_backend_controller_impl.h"
#include "ash/shell.h"
#include "base/barrier_closure.h"
#include "base/base_paths.h"
#include "base/bind.h"
#include "base/callback.h"
#include "base/files/file_enumerator.h"
#include "base/files/file_path.h"
#include "base/files/file_util.h"
#include "base/hash/sha1.h"
#include "base/path_service.h"
#include "base/run_loop.h"
#include "base/system/sys_info.h"
#include "base/test/bind_test_util.h"
#include "base/timer/timer.h"
#include "ui/gfx/image/image_skia.h"
namespace ash {
using AmbientPhotoControllerTest = AmbientAshTestBase;
// Test that topics are downloaded when starting screen update.
TEST_F(AmbientPhotoControllerTest, ShouldStartToDownloadTopics) {
auto topics = photo_controller()->ambient_backend_model()->topics();
EXPECT_TRUE(topics.empty());
// Start to refresh images.
photo_controller()->StartScreenUpdate();
topics = photo_controller()->ambient_backend_model()->topics();
EXPECT_TRUE(topics.empty());
FastForwardToNextImage();
topics = photo_controller()->ambient_backend_model()->topics();
EXPECT_FALSE(topics.empty());
// Stop to refresh images.
photo_controller()->StopScreenUpdate();
topics = photo_controller()->ambient_backend_model()->topics();
EXPECT_TRUE(topics.empty());
}
// Test that image is downloaded when starting screen update.
TEST_F(AmbientPhotoControllerTest, ShouldStartToDownloadImages) {
auto image = photo_controller()->ambient_backend_model()->GetNextImage();
EXPECT_TRUE(image.IsNull());
// Start to refresh images.
photo_controller()->StartScreenUpdate();
FastForwardToNextImage();
image = photo_controller()->ambient_backend_model()->GetNextImage();
EXPECT_FALSE(image.IsNull());
// Stop to refresh images.
photo_controller()->StopScreenUpdate();
image = photo_controller()->ambient_backend_model()->GetNextImage();
EXPECT_TRUE(image.IsNull());
}
// Tests that photos are updated periodically when starting screen update.
TEST_F(AmbientPhotoControllerTest, ShouldUpdatePhotoPeriodically) {
PhotoWithDetails image1;
PhotoWithDetails image2;
PhotoWithDetails image3;
// Start to refresh images.
photo_controller()->StartScreenUpdate();
FastForwardToNextImage();
image1 = photo_controller()->ambient_backend_model()->GetNextImage();
EXPECT_FALSE(image1.IsNull());
EXPECT_TRUE(image2.IsNull());
FastForwardToNextImage();
image2 = photo_controller()->ambient_backend_model()->GetNextImage();
EXPECT_FALSE(image2.IsNull());
EXPECT_FALSE(image1.photo.BackedBySameObjectAs(image2.photo));
EXPECT_TRUE(image3.IsNull());
FastForwardToNextImage();
image3 = photo_controller()->ambient_backend_model()->GetNextImage();
EXPECT_FALSE(image3.IsNull());
EXPECT_FALSE(image1.photo.BackedBySameObjectAs(image3.photo));
EXPECT_FALSE(image2.photo.BackedBySameObjectAs(image3.photo));
// Stop to refresh images.
photo_controller()->StopScreenUpdate();
}
// Test that image is saved.
TEST_F(AmbientPhotoControllerTest, ShouldSaveImagesOnDisk) {
base::FilePath home_dir;
base::PathService::Get(base::DIR_HOME, &home_dir);
base::FilePath ambient_image_path =
home_dir.Append(FILE_PATH_LITERAL(kAmbientModeDirectoryName));
// Clean up.
base::DeletePathRecursively(ambient_image_path);
// Start to refresh images. It will download a test image and write it in
// |ambient_image_path| in a delayed task.
photo_controller()->StartScreenUpdate();
FastForwardToNextImage();
EXPECT_TRUE(base::PathExists(ambient_image_path));
{
// Count files and directories in root_path. There should only be one file
// that was just created to save image files for this ambient mode session.
base::FileEnumerator files(
ambient_image_path, /*recursive=*/false,
base::FileEnumerator::FILES | base::FileEnumerator::DIRECTORIES);
int count = 0;
for (base::FilePath current = files.Next(); !current.empty();
current = files.Next()) {
EXPECT_FALSE(files.GetInfo().IsDirectory());
count++;
}
// Two image files and two attribution files.
EXPECT_EQ(count, 4);
}
// Clean up.
base::DeletePathRecursively(ambient_image_path);
}
// Test that image is save and will be deleted when stopping ambient mode.
TEST_F(AmbientPhotoControllerTest, ShouldNotDeleteImagesOnDisk) {
base::FilePath home_dir;
base::PathService::Get(base::DIR_HOME, &home_dir);
base::FilePath ambient_image_path =
home_dir.Append(FILE_PATH_LITERAL(kAmbientModeDirectoryName));
// Clean up.
base::DeletePathRecursively(ambient_image_path);
// Start to refresh images. It will download a test image and write it in
// |ambient_image_path| in a delayed task.
photo_controller()->StartScreenUpdate();
FastForwardToNextImage();
EXPECT_TRUE(base::PathExists(ambient_image_path));
auto image = photo_controller()->ambient_backend_model()->GetNextImage();
EXPECT_FALSE(image.IsNull());
// Stop to refresh images.
photo_controller()->StopScreenUpdate();
FastForwardToNextImage();
EXPECT_TRUE(base::PathExists(ambient_image_path));
EXPECT_FALSE(base::IsDirectoryEmpty(ambient_image_path));
image = photo_controller()->ambient_backend_model()->GetNextImage();
EXPECT_TRUE(image.IsNull());
{
// Count files and directories in root_path. There should only be one file
// that was just created to save image files for this ambient mode session.
base::FileEnumerator files(
ambient_image_path, /*recursive=*/false,
base::FileEnumerator::FILES | base::FileEnumerator::DIRECTORIES);
int count = 0;
for (base::FilePath current = files.Next(); !current.empty();
current = files.Next()) {
EXPECT_FALSE(files.GetInfo().IsDirectory());
count++;
}
// Two image files and two attribution files.
EXPECT_EQ(count, 4);
}
// Clean up.
base::DeletePathRecursively(ambient_image_path);
}
// Test that image is read from disk when no more topics.
TEST_F(AmbientPhotoControllerTest, ShouldReadCacheWhenNoMoreTopics) {
base::FilePath home_dir;
base::PathService::Get(base::DIR_HOME, &home_dir);
base::FilePath ambient_image_path =
home_dir.Append(FILE_PATH_LITERAL(kAmbientModeDirectoryName));
// Clean up.
base::DeletePathRecursively(ambient_image_path);
FetchImage();
FastForwardToNextImage();
// Topics is empty. Will read from cache, which is empty.
auto image = photo_controller()->ambient_backend_model()->GetNextImage();
EXPECT_TRUE(image.IsNull());
// Save a file to check if it gets read for display.
auto cached_image = ambient_image_path.Append("0.img");
base::CreateDirectory(ambient_image_path);
base::WriteFile(cached_image, "cached image");
// Reset variables in photo controller.
photo_controller()->StopScreenUpdate();
FetchImage();
FastForwardToNextImage();
image = photo_controller()->ambient_backend_model()->GetNextImage();
EXPECT_FALSE(image.IsNull());
// Clean up.
base::DeletePathRecursively(ambient_image_path);
}
// Test that will try 100 times to read image from disk when no more topics.
TEST_F(AmbientPhotoControllerTest,
ShouldTry100TimesToReadCacheWhenNoMoreTopics) {
base::FilePath home_dir;
base::PathService::Get(base::DIR_HOME, &home_dir);
base::FilePath ambient_image_path =
home_dir.Append(FILE_PATH_LITERAL(kAmbientModeDirectoryName));
// Clean up.
base::DeletePathRecursively(ambient_image_path);
FetchImage();
FastForwardToNextImage();
// Topics is empty. Will read from cache, which is empty.
auto image = photo_controller()->ambient_backend_model()->GetNextImage();
EXPECT_TRUE(image.IsNull());
// The initial file name to be read is 0. Save a file with 99.img to check if
// it gets read for display.
auto cached_image = ambient_image_path.Append("99.img");
base::CreateDirectory(ambient_image_path);
base::WriteFile(cached_image, "cached image");
// Reset variables in photo controller.
photo_controller()->StopScreenUpdate();
FetchImage();
FastForwardToNextImage();
image = photo_controller()->ambient_backend_model()->GetNextImage();
EXPECT_FALSE(image.IsNull());
// Clean up.
base::DeletePathRecursively(ambient_image_path);
}
// Test that image is read from disk when image downloading failed.
TEST_F(AmbientPhotoControllerTest, ShouldReadCacheWhenImageDownloadingFailed) {
base::FilePath home_dir;
base::PathService::Get(base::DIR_HOME, &home_dir);
base::FilePath ambient_image_path =
home_dir.Append(FILE_PATH_LITERAL(kAmbientModeDirectoryName));
// Clean up.
base::DeletePathRecursively(ambient_image_path);
SetUrlLoaderData(std::make_unique<std::string>());
FetchTopics();
// Forward a little bit time. FetchTopics() will succeed. Downloading should
// fail. Will read from cache, which is empty.
task_environment()->FastForwardBy(0.2 * kTopicFetchInterval);
auto image = photo_controller()->ambient_backend_model()->GetNextImage();
EXPECT_TRUE(image.IsNull());
// Save a file to check if it gets read for display.
auto cached_image = ambient_image_path.Append("0.img");
base::CreateDirectory(ambient_image_path);
base::WriteFile(cached_image, "cached image");
// Reset variables in photo controller.
photo_controller()->StopScreenUpdate();
FetchTopics();
// Forward a little bit time. FetchTopics() will succeed. Downloading should
// fail. Will read from cache.
task_environment()->FastForwardBy(0.2 * kTopicFetchInterval);
image = photo_controller()->ambient_backend_model()->GetNextImage();
EXPECT_FALSE(image.IsNull());
// Clean up.
base::DeletePathRecursively(ambient_image_path);
}
// Test that image is read from disk when image decoding failed.
TEST_F(AmbientPhotoControllerTest, ShouldReadCacheWhenImageDecodingFailed) {
base::FilePath home_dir;
base::PathService::Get(base::DIR_HOME, &home_dir);
base::FilePath ambient_image_path =
home_dir.Append(FILE_PATH_LITERAL(kAmbientModeDirectoryName));
// Clean up.
base::DeletePathRecursively(ambient_image_path);
SeteImageDecoderImage(gfx::ImageSkia());
FetchTopics();
// Forward a little bit time. FetchTopics() will succeed.
// Downloading succeed and save the data to disk.
// First decoding should fail. Will read from cache, and then succeed.
task_environment()->FastForwardBy(0.2 * kTopicFetchInterval);
auto image = photo_controller()->ambient_backend_model()->GetNextImage();
EXPECT_FALSE(image.IsNull());
// Clean up.
base::DeletePathRecursively(ambient_image_path);
}
// Test that image will refresh when have more topics.
TEST_F(AmbientPhotoControllerTest, ShouldResumWhenHaveMoreTopics) {
base::FilePath home_dir;
base::PathService::Get(base::DIR_HOME, &home_dir);
base::FilePath ambient_image_path =
home_dir.Append(FILE_PATH_LITERAL(kAmbientModeDirectoryName));
// Clean up.
base::DeletePathRecursively(ambient_image_path);
FetchImage();
FastForwardToNextImage();
// Topics is empty. Will read from cache, which is empty.
auto image = photo_controller()->ambient_backend_model()->GetNextImage();
EXPECT_TRUE(image.IsNull());
FetchTopics();
// Forward a little bit time. FetchTopics() will succeed and refresh image.
task_environment()->FastForwardBy(0.2 * kTopicFetchInterval);
image = photo_controller()->ambient_backend_model()->GetNextImage();
EXPECT_FALSE(image.IsNull());
// Clean up.
base::DeletePathRecursively(ambient_image_path);
}
TEST_F(AmbientPhotoControllerTest, ShouldStartToRefreshWeather) {
auto* model = photo_controller()->ambient_backend_model();
EXPECT_FALSE(model->show_celsius());
EXPECT_TRUE(model->weather_condition_icon().isNull());
WeatherInfo info;
info.show_celsius = true;
info.condition_icon_url = "https://fake-icon-url";
info.temp_f = 70.0f;
backend_controller()->SetWeatherInfo(info);
// Start to refresh weather as screen update starts.
photo_controller()->StartScreenUpdate();
base::RunLoop().RunUntilIdle();
EXPECT_TRUE(model->show_celsius());
EXPECT_FALSE(model->weather_condition_icon().isNull());
EXPECT_GT(info.temp_f, 0);
// Refresh weather again after time passes.
info.show_celsius = false;
info.temp_f = -70.0f;
backend_controller()->SetWeatherInfo(info);
FastForwardToRefreshWeather();
EXPECT_FALSE(model->show_celsius());
EXPECT_LT(info.temp_f, 0);
}
} // namespace ash