Commit 8c0c5cbf authored by Jeffrey Young's avatar Jeffrey Young Committed by Chromium LUCI CQ

ambient: move file writing to AmbientPhotoCache

Mock out all Ambient mode disk access in unit tests.

BUG=b:165481552,b:169813071
TEST=ash_unittests --gtest_filter="AmbientPhotoControllerTest.*"
TEST=ash_unittests --gtest_filter="AmbientPhotoCacheTest.*"

Cq-Include-Trybots: luci.chrome.try:linux-chromeos-chrome
Change-Id: Ic063e6b7686a74c67fbe7a23e146a9e1640abfd9
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2558590
Commit-Queue: Jeffrey Young <cowmoo@chromium.org>
Reviewed-by: default avatarXiaohui Chen <xiaohuic@chromium.org>
Reviewed-by: default avatarTao Wu <wutao@chromium.org>
Cr-Commit-Position: refs/heads/master@{#838748}
parent a7bf3cb1
...@@ -1969,6 +1969,7 @@ test("ash_unittests") { ...@@ -1969,6 +1969,7 @@ test("ash_unittests") {
"accessibility/touch_exploration_controller_unittest.cc", "accessibility/touch_exploration_controller_unittest.cc",
"accessibility/touch_exploration_manager_unittest.cc", "accessibility/touch_exploration_manager_unittest.cc",
"ambient/ambient_controller_unittest.cc", "ambient/ambient_controller_unittest.cc",
"ambient/ambient_photo_cache_unittest.cc",
"ambient/ambient_photo_controller_unittest.cc", "ambient/ambient_photo_controller_unittest.cc",
"ambient/autotest_ambient_api_unittest.cc", "ambient/autotest_ambient_api_unittest.cc",
"ambient/model/ambient_backend_model_unittest.cc", "ambient/model/ambient_backend_model_unittest.cc",
......
...@@ -509,6 +509,10 @@ void AmbientController::CloseAllWidgets(bool immediately) { ...@@ -509,6 +509,10 @@ void AmbientController::CloseAllWidgets(bool immediately) {
} }
void AmbientController::OnEnabledPrefChanged() { void AmbientController::OnEnabledPrefChanged() {
// TODO(b/176094707) conditionally create/destroy photo_controller and cache
// if Ambient is enabled
ambient_photo_controller_.InitCache();
if (IsAmbientModeEnabled()) { if (IsAmbientModeEnabled()) {
DVLOG(1) << "Ambient mode enabled"; DVLOG(1) << "Ambient mode enabled";
......
...@@ -8,7 +8,10 @@ ...@@ -8,7 +8,10 @@
#include "ash/public/cpp/ambient/ambient_client.h" #include "ash/public/cpp/ambient/ambient_client.h"
#include "base/files/file_util.h" #include "base/files/file_util.h"
#include "base/memory/weak_ptr.h" #include "base/memory/weak_ptr.h"
#include "base/path_service.h"
#include "base/sequenced_task_runner.h" #include "base/sequenced_task_runner.h"
#include "base/strings/string_number_conversions.h"
#include "base/system/sys_info.h"
#include "base/task/task_traits.h" #include "base/task/task_traits.h"
#include "base/task/thread_pool.h" #include "base/task/thread_pool.h"
#include "services/data_decoder/public/cpp/decode_image.h" #include "services/data_decoder/public/cpp/decode_image.h"
...@@ -52,11 +55,82 @@ std::unique_ptr<network::SimpleURLLoader> CreateSimpleURLLoader( ...@@ -52,11 +55,82 @@ std::unique_ptr<network::SimpleURLLoader> CreateSimpleURLLoader(
NO_TRAFFIC_ANNOTATION_YET); NO_TRAFFIC_ANNOTATION_YET);
} }
// Implementation of |AmbientPhotoCache|. bool CreateDirIfNotExists(const base::FilePath& path) {
return base::DirectoryExists(path) || base::CreateDirectory(path);
}
// Writes |data| to |path| if |data| is not nullptr and is not empty. If |data|
// is nullptr or empty, will delete any existing file at |path|.
bool WriteOrDeleteFile(const base::FilePath& path,
const std::string* const data) {
if (!data || data->empty())
return base::DeleteFile(path);
if (!CreateDirIfNotExists(path.DirName())) {
LOG(ERROR) << "Cannot create ambient mode directory.";
return false;
}
if (base::SysInfo::AmountOfFreeDiskSpace(path.DirName()) <
kMaxReservedAvailableDiskSpaceByte) {
LOG(ERROR) << "Not enough disk space left.";
return false;
}
// Create a temp file.
base::FilePath temp_file;
if (!base::CreateTemporaryFileInDir(path.DirName(), &temp_file)) {
LOG(ERROR) << "Cannot create a temporary file.";
return false;
}
// Write to the tmp file.
const int size = data->size();
int written_size = base::WriteFile(temp_file, data->data(), size);
if (written_size != size) {
LOG(ERROR) << "Cannot write the temporary file.";
base::DeleteFile(temp_file);
return false;
}
// Replace the current file with the temp file.
if (!base::ReplaceFile(temp_file, path, /*error=*/nullptr)) {
LOG(ERROR) << "Cannot replace the temporary file.";
base::DeleteFile(temp_file);
return false;
}
return true;
}
base::FilePath GetPhotoPath(int cache_index,
const base::FilePath& root_path,
bool is_related = false) {
std::string file_ext;
// "_r.img" for related files, ".img" otherwise
if (is_related)
file_ext += kRelatedPhotoSuffix;
file_ext += kPhotoFileExt;
return root_path.Append(base::NumberToString(cache_index) + file_ext);
}
base::FilePath GetDetailsPath(int cache_index,
const base::FilePath& root_path) {
return GetPhotoPath(cache_index, root_path)
.RemoveExtension()
.AddExtension(kPhotoDetailsFileExt);
}
// -----------------AmbientPhotoCacheImpl---------------------------------------
class AmbientPhotoCacheImpl : public AmbientPhotoCache { class AmbientPhotoCacheImpl : public AmbientPhotoCache {
public: public:
AmbientPhotoCacheImpl() explicit AmbientPhotoCacheImpl(base::FilePath path)
: task_runner_(base::ThreadPool::CreateSequencedTaskRunner( : root_directory_(path),
task_runner_(base::ThreadPool::CreateSequencedTaskRunner(
{base::MayBlock(), base::TaskPriority::BEST_EFFORT, {base::MayBlock(), base::TaskPriority::BEST_EFFORT,
base::TaskShutdownBehavior::CONTINUE_ON_SHUTDOWN})) {} base::TaskShutdownBehavior::CONTINUE_ON_SHUTDOWN})) {}
~AmbientPhotoCacheImpl() override = default; ~AmbientPhotoCacheImpl() override = default;
...@@ -65,9 +139,12 @@ class AmbientPhotoCacheImpl : public AmbientPhotoCache { ...@@ -65,9 +139,12 @@ class AmbientPhotoCacheImpl : public AmbientPhotoCache {
void DownloadPhoto(const std::string& url, void DownloadPhoto(const std::string& url,
base::OnceCallback<void(std::unique_ptr<std::string>)> base::OnceCallback<void(std::unique_ptr<std::string>)>
callback) override { callback) override {
auto simple_loader = CreateSimpleURLLoader(url); std::unique_ptr<network::SimpleURLLoader> simple_loader =
CreateSimpleURLLoader(url);
scoped_refptr<network::SharedURLLoaderFactory> loader_factory =
AmbientClient::Get()->GetURLLoaderFactory();
auto* loader_ptr = simple_loader.get(); auto* loader_ptr = simple_loader.get();
auto loader_factory = AmbientClient::Get()->GetURLLoaderFactory();
loader_ptr->DownloadToString( loader_ptr->DownloadToString(
loader_factory.get(), loader_factory.get(),
base::BindOnce(&AmbientPhotoCacheImpl::OnUrlDownloaded, base::BindOnce(&AmbientPhotoCacheImpl::OnUrlDownloaded,
...@@ -77,10 +154,120 @@ class AmbientPhotoCacheImpl : public AmbientPhotoCache { ...@@ -77,10 +154,120 @@ class AmbientPhotoCacheImpl : public AmbientPhotoCache {
} }
void DownloadPhotoToFile(const std::string& url, void DownloadPhotoToFile(const std::string& url,
base::OnceCallback<void(base::FilePath)> callback, int cache_index,
const base::FilePath& file_path) override { bool is_related,
auto simple_loader = CreateSimpleURLLoader(url); base::OnceCallback<void(bool)> callback) override {
auto loader_factory = AmbientClient::Get()->GetURLLoaderFactory(); auto file_path = GetPhotoPath(cache_index, root_directory_, is_related);
task_runner_->PostTaskAndReply(
FROM_HERE,
base::BindOnce(
[](const base::FilePath& path) {
if (!CreateDirIfNotExists(path))
LOG(ERROR) << "Cannot create ambient mode directory";
},
root_directory_),
base::BindOnce(&AmbientPhotoCacheImpl::DownloadPhotoToFileInternal,
weak_factory_.GetWeakPtr(), url, std::move(callback),
file_path));
}
void DecodePhoto(
std::unique_ptr<std::string> data,
base::OnceCallback<void(const gfx::ImageSkia&)> callback) override {
std::vector<uint8_t> image_bytes(data->begin(), data->end());
data_decoder::DecodeImageIsolated(
image_bytes, data_decoder::mojom::ImageCodec::DEFAULT,
/*shrink_to_fit=*/true, data_decoder::kDefaultMaxSizeInBytes,
/*desired_image_frame_size=*/gfx::Size(),
base::BindOnce(&ToImageSkia, std::move(callback)));
}
void WriteFiles(int cache_index,
const std::string* const image,
const std::string* const details,
const std::string* const related_image,
base::OnceClosure callback) override {
DCHECK_LT(cache_index, kMaxNumberOfCachedImages);
task_runner_->PostTaskAndReply(
FROM_HERE,
base::BindOnce(
[](int cache_index, const base::FilePath& root_path,
const std::string* const image, const std::string* const details,
const std::string* const related_image) {
bool success = true;
auto image_path = GetPhotoPath(cache_index, root_path);
success = success && WriteOrDeleteFile(image_path, image);
auto details_path = GetDetailsPath(cache_index, root_path);
success = success && WriteOrDeleteFile(details_path, details);
auto related_image_path =
GetPhotoPath(cache_index, root_path, /*is_related=*/true);
success = success &&
WriteOrDeleteFile(related_image_path, related_image);
if (!success) {
LOG(WARNING) << "Error writing files";
base::DeleteFile(image_path);
base::DeleteFile(details_path);
base::DeleteFile(related_image_path);
}
},
cache_index, root_directory_, image, details, related_image),
std::move(callback));
}
void ReadFiles(int cache_index,
base::OnceCallback<void(PhotoCacheEntry)> callback) override {
task_runner_->PostTaskAndReplyWithResult(
FROM_HERE,
base::BindOnce(
[](int cache_index, const base::FilePath& root_path) {
auto image = std::make_unique<std::string>();
auto details = std::make_unique<std::string>();
auto related_image = std::make_unique<std::string>();
auto image_path = GetPhotoPath(cache_index, root_path);
if (!base::ReadFileToString(image_path, image.get()))
image->clear();
auto details_path = GetDetailsPath(cache_index, root_path);
if (!base::ReadFileToString(details_path, details.get()))
details->clear();
auto related_path =
GetPhotoPath(cache_index, root_path, /*is_related=*/true);
if (!base::ReadFileToString(related_path, related_image.get()))
related_image->clear();
return PhotoCacheEntry(std::move(image), std::move(details),
std::move(related_image));
},
cache_index, root_directory_),
std::move(callback));
}
void Clear() override {
task_runner_->PostTask(FROM_HERE,
base::BindOnce(
[](const base::FilePath& file_path) {
base::DeletePathRecursively(file_path);
},
root_directory_));
}
private:
void DownloadPhotoToFileInternal(const std::string& url,
base::OnceCallback<void(bool)> callback,
const base::FilePath& file_path) {
std::unique_ptr<network::SimpleURLLoader> simple_loader =
CreateSimpleURLLoader(url);
scoped_refptr<network::SharedURLLoaderFactory> loader_factory =
AmbientClient::Get()->GetURLLoaderFactory();
auto* loader_ptr = simple_loader.get(); auto* loader_ptr = simple_loader.get();
auto* loader_factory_ptr = loader_factory.get(); auto* loader_factory_ptr = loader_factory.get();
...@@ -100,18 +287,6 @@ class AmbientPhotoCacheImpl : public AmbientPhotoCache { ...@@ -100,18 +287,6 @@ class AmbientPhotoCacheImpl : public AmbientPhotoCache {
temp_path); temp_path);
} }
void DecodePhoto(
std::unique_ptr<std::string> data,
base::OnceCallback<void(const gfx::ImageSkia&)> callback) override {
std::vector<uint8_t> image_bytes(data->begin(), data->end());
data_decoder::DecodeImageIsolated(
image_bytes, data_decoder::mojom::ImageCodec::DEFAULT,
/*shrink_to_fit=*/true, data_decoder::kDefaultMaxSizeInBytes,
/*desired_image_frame_size=*/gfx::Size(),
base::BindOnce(&ToImageSkia, std::move(callback)));
}
private:
void OnUrlDownloaded( void OnUrlDownloaded(
base::OnceCallback<void(std::unique_ptr<std::string>)> callback, base::OnceCallback<void(std::unique_ptr<std::string>)> callback,
std::unique_ptr<network::SimpleURLLoader> simple_loader, std::unique_ptr<network::SimpleURLLoader> simple_loader,
...@@ -129,7 +304,7 @@ class AmbientPhotoCacheImpl : public AmbientPhotoCache { ...@@ -129,7 +304,7 @@ class AmbientPhotoCacheImpl : public AmbientPhotoCache {
} }
void OnUrlDownloadedToFile( void OnUrlDownloadedToFile(
base::OnceCallback<void(base::FilePath)> callback, base::OnceCallback<void(bool)> callback,
std::unique_ptr<network::SimpleURLLoader> simple_loader, std::unique_ptr<network::SimpleURLLoader> simple_loader,
scoped_refptr<network::SharedURLLoaderFactory> loader_factory, scoped_refptr<network::SharedURLLoaderFactory> loader_factory,
const base::FilePath& desired_path, const base::FilePath& desired_path,
...@@ -147,7 +322,7 @@ class AmbientPhotoCacheImpl : public AmbientPhotoCache { ...@@ -147,7 +322,7 @@ class AmbientPhotoCacheImpl : public AmbientPhotoCache {
}, },
temp_path)); temp_path));
} }
std::move(callback).Run(base::FilePath()); std::move(callback).Run(false);
return; return;
} }
...@@ -163,23 +338,48 @@ class AmbientPhotoCacheImpl : public AmbientPhotoCache { ...@@ -163,23 +338,48 @@ class AmbientPhotoCacheImpl : public AmbientPhotoCache {
// Clean up the files. // Clean up the files.
base::DeleteFile(from_path); base::DeleteFile(from_path);
base::DeleteFile(to_path); base::DeleteFile(to_path);
return base::FilePath(); return false;
} }
return to_path; return true;
}, },
desired_path, temp_path), desired_path, temp_path),
std::move(callback)); std::move(callback));
} }
const base::FilePath root_directory_;
scoped_refptr<base::SequencedTaskRunner> task_runner_; scoped_refptr<base::SequencedTaskRunner> task_runner_;
base::WeakPtrFactory<AmbientPhotoCacheImpl> weak_factory_{this}; base::WeakPtrFactory<AmbientPhotoCacheImpl> weak_factory_{this};
}; };
} // namespace } // namespace
// ---------------- PhotoCacheRead --------------------------------------------
PhotoCacheEntry::PhotoCacheEntry() = default;
PhotoCacheEntry::PhotoCacheEntry(std::unique_ptr<std::string> image,
std::unique_ptr<std::string> details,
std::unique_ptr<std::string> related_image)
: image(std::move(image)),
details(std::move(details)),
related_image(std::move(related_image)) {}
PhotoCacheEntry::PhotoCacheEntry(PhotoCacheEntry&&) = default;
PhotoCacheEntry::~PhotoCacheEntry() = default;
void PhotoCacheEntry::reset() {
image.reset();
details.reset();
related_image.reset();
}
// -------------- AmbientPhotoCache --------------------------------------------
// static // static
std::unique_ptr<AmbientPhotoCache> AmbientPhotoCache::Create() { std::unique_ptr<AmbientPhotoCache> AmbientPhotoCache::Create(
return std::make_unique<AmbientPhotoCacheImpl>(); base::FilePath root_path) {
return std::make_unique<AmbientPhotoCacheImpl>(root_path);
} }
} // namespace ash } // namespace ash
...@@ -8,6 +8,7 @@ ...@@ -8,6 +8,7 @@
#include <memory> #include <memory>
#include <string> #include <string>
#include "ash/ash_export.h"
#include "base/callback_forward.h" #include "base/callback_forward.h"
#include "base/files/file_path.h" #include "base/files/file_path.h"
...@@ -17,31 +18,77 @@ class ImageSkia; ...@@ -17,31 +18,77 @@ class ImageSkia;
namespace ash { namespace ash {
// Holds the return value for |AmbientPhotoCache::ReadFiles| to use with
// |task_runner_->PostTaskAndReplyWithResult|.
// Represented on disk by a file for each of |image|, |details|, and
// |related_image|.
struct ASH_EXPORT PhotoCacheEntry {
PhotoCacheEntry();
PhotoCacheEntry(std::unique_ptr<std::string> image,
std::unique_ptr<std::string> details,
std::unique_ptr<std::string> related_image);
PhotoCacheEntry(const PhotoCacheEntry&) = delete;
PhotoCacheEntry& operator=(const PhotoCacheEntry&) = delete;
PhotoCacheEntry(PhotoCacheEntry&&);
~PhotoCacheEntry();
void reset();
std::unique_ptr<std::string> image;
std::unique_ptr<std::string> details;
std::unique_ptr<std::string> related_image;
};
// Interface for downloading and decoding photos for Ambient mode. Mocked for // Interface for downloading and decoding photos for Ambient mode. Mocked for
// testing to isolate from network. // testing to isolate from network and file system.
class AmbientPhotoCache { // Each cache entry is written to disk as three files in the |root_path|, with
// filenames prefixed by |cache_index|.
class ASH_EXPORT AmbientPhotoCache {
public: public:
AmbientPhotoCache() = default; AmbientPhotoCache() = default;
AmbientPhotoCache(const AmbientPhotoCache&) = delete; AmbientPhotoCache(const AmbientPhotoCache&) = delete;
AmbientPhotoCache& operator=(const AmbientPhotoCache&) = delete; AmbientPhotoCache& operator=(const AmbientPhotoCache&) = delete;
virtual ~AmbientPhotoCache() = default; virtual ~AmbientPhotoCache() = default;
static std::unique_ptr<AmbientPhotoCache> Create(); static std::unique_ptr<AmbientPhotoCache> Create(base::FilePath root_path);
virtual void DownloadPhoto( virtual void DownloadPhoto(
const std::string& url, const std::string& url,
base::OnceCallback<void(std::unique_ptr<std::string>)> callback) = 0; base::OnceCallback<void(std::unique_ptr<std::string>)> callback) = 0;
// Saves the photo to |file_path| and calls |callback| when complete. If an // Saves the photo at |url| to |cache_index| and calls |callback| with a
// error occurs, will call |callback| with an empty path. // boolean that indicates success. Setting |is_related| will change the
virtual void DownloadPhotoToFile( // filename to indicate that this is a paired photo.
const std::string& url, virtual void DownloadPhotoToFile(const std::string& url,
base::OnceCallback<void(base::FilePath)> callback, int cache_index,
const base::FilePath& file_path) = 0; bool is_related,
base::OnceCallback<void(bool)> callback) = 0;
virtual void DecodePhoto( virtual void DecodePhoto(
std::unique_ptr<std::string> data, std::unique_ptr<std::string> data,
base::OnceCallback<void(const gfx::ImageSkia&)> callback) = 0; base::OnceCallback<void(const gfx::ImageSkia&)> callback) = 0;
// Write files to disk at |cache_index| and call |callback| when complete.
// |image| and |related_image| are encoded jpg images that must be decoded
// with |DecodePhoto| to display. |details| is human readable text.
virtual void WriteFiles(int cache_index,
const std::string* const image,
const std::string* const details,
const std::string* const related_image,
base::OnceClosure callback) = 0;
// Read the files at |cache_index| and call |callback| with a struct
// containing the contents of each file. If a particular file fails to be
// read, it may be represented as nullptr or empty string.
virtual void ReadFiles(
int cache_index,
base::OnceCallback<void(PhotoCacheEntry)> callback) = 0;
// Erase all stored files from disk.
virtual void Clear() = 0;
}; };
} // namespace ash } // namespace ash
......
// 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_cache.h"
#include "ash/ambient/ambient_constants.h"
#include "base/callback_forward.h"
#include "base/files/file_path.h"
#include "base/files/file_util.h"
#include "base/path_service.h"
#include "base/run_loop.h"
#include "base/test/task_environment.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace ash {
namespace {
base::FilePath GetTestPath() {
base::FilePath path;
EXPECT_TRUE(base::PathService::Get(base::DIR_TEMP, &path));
path = path.Append(FILE_PATH_LITERAL(kAmbientModeDirectoryName));
return path;
}
} // namespace
class AmbientPhotoCacheTest : public testing::Test {
public:
void SetUp() override {
auto test_path = GetTestPath();
base::DeletePathRecursively(test_path);
photo_cache_ = AmbientPhotoCache::Create(test_path);
}
void TearDown() override { base::DeletePathRecursively(GetTestPath()); }
AmbientPhotoCache* photo_cache() { return photo_cache_.get(); }
protected:
base::test::TaskEnvironment task_environment_;
private:
std::unique_ptr<AmbientPhotoCache> photo_cache_;
};
TEST_F(AmbientPhotoCacheTest, ReadsBackWrittenFiles) {
int cache_index = 0;
std::string image("image");
std::string details("details");
std::string related_image("related image");
{
base::RunLoop loop;
photo_cache()->WriteFiles(cache_index, &image, &details, &related_image,
loop.QuitClosure());
loop.Run();
}
{
base::RunLoop loop;
// Read the files back using photo cache.
photo_cache()->ReadFiles(
cache_index,
base::BindOnce(
[](base::OnceClosure done, PhotoCacheEntry cache_read) {
EXPECT_EQ(*cache_read.image, "image");
EXPECT_EQ(*cache_read.details, "details");
EXPECT_EQ(*cache_read.related_image, "related image");
std::move(done).Run();
},
loop.QuitClosure()));
loop.Run();
}
}
TEST_F(AmbientPhotoCacheTest, WritesFileToDisk) {
base::FilePath test_path = GetTestPath();
int cache_index = 5;
std::string image("image 5");
std::string details("details 5");
std::string related_image("related image 5");
// Make sure files are not on disk.
EXPECT_FALSE(base::PathExists(test_path.Append(FILE_PATH_LITERAL("5.img"))));
EXPECT_FALSE(base::PathExists(test_path.Append(FILE_PATH_LITERAL("5.txt"))));
EXPECT_FALSE(
base::PathExists(test_path.Append(FILE_PATH_LITERAL("5_r.img"))));
// Write the data to the cache.
{
base::RunLoop loop;
photo_cache()->WriteFiles(cache_index, &image, &details, &related_image,
loop.QuitClosure());
loop.Run();
}
// Verify that expected files are written to disk.
std::string actual_image;
EXPECT_TRUE(base::ReadFileToString(
test_path.Append(FILE_PATH_LITERAL("5.img")), &actual_image));
EXPECT_EQ(actual_image, image);
std::string actual_details;
EXPECT_TRUE(base::ReadFileToString(
test_path.Append(FILE_PATH_LITERAL("5.txt")), &actual_details));
EXPECT_EQ(actual_details, details);
std::string actual_related_image;
EXPECT_TRUE(base::ReadFileToString(
test_path.Append(FILE_PATH_LITERAL("5_r.img")), &actual_related_image));
EXPECT_EQ(actual_related_image, related_image);
}
TEST_F(AmbientPhotoCacheTest, SetsDataToEmptyStringWhenFilesMissing) {
base::FilePath test_path = GetTestPath();
EXPECT_FALSE(base::DirectoryExists(test_path));
{
base::RunLoop loop;
photo_cache()->ReadFiles(
/*cache_index=*/1,
base::BindOnce(
[](base::OnceClosure done, PhotoCacheEntry cache_read) {
EXPECT_TRUE(cache_read.image->empty());
EXPECT_TRUE(cache_read.details->empty());
EXPECT_TRUE(cache_read.related_image->empty());
std::move(done).Run();
},
loop.QuitClosure()));
}
}
} // namespace ash
...@@ -85,69 +85,6 @@ base::TaskTraits GetTaskTraits() { ...@@ -85,69 +85,6 @@ base::TaskTraits GetTaskTraits() {
base::TaskShutdownBehavior::CONTINUE_ON_SHUTDOWN}; base::TaskShutdownBehavior::CONTINUE_ON_SHUTDOWN};
} }
// Get the root path for ambient mode.
base::FilePath GetRootPath() {
base::FilePath home_dir;
CHECK(base::PathService::Get(base::DIR_HOME, &home_dir));
return home_dir.Append(FILE_PATH_LITERAL(kAmbientModeDirectoryName));
}
base::FilePath GetCachePath() {
return GetRootPath().Append(
FILE_PATH_LITERAL(kAmbientModeCacheDirectoryName));
}
base::FilePath GetBackupCachePath() {
return GetRootPath().Append(
FILE_PATH_LITERAL(kAmbientModeBackupCacheDirectoryName));
}
base::FilePath GetBackupFilePath(size_t index) {
return GetBackupCachePath().Append(base::NumberToString(index) +
kPhotoFileExt);
}
base::FilePath GetRelatedFilePath(const std::string& file_name) {
return GetCachePath().Append(file_name + kRelatedPhotoSuffix + kPhotoFileExt);
}
bool CreateDirIfNotExists(const base::FilePath& path) {
return base::DirectoryExists(path) || base::CreateDirectory(path);
}
void WriteFile(const base::FilePath& path, const std::string& data) {
if (!CreateDirIfNotExists(GetCachePath())) {
LOG(ERROR) << "Cannot create ambient mode directory.";
return;
}
if (base::SysInfo::AmountOfFreeDiskSpace(GetRootPath()) <
kMaxReservedAvailableDiskSpaceByte) {
LOG(WARNING) << "Not enough disk space left.";
return;
}
// Create a temp file.
base::FilePath temp_file;
if (!base::CreateTemporaryFileInDir(path.DirName(), &temp_file)) {
LOG(ERROR) << "Cannot create a temporary file.";
return;
}
// Write to the tmp file.
const int size = data.size();
int written_size = base::WriteFile(temp_file, data.data(), size);
if (written_size != size) {
LOG(ERROR) << "Cannot write the temporary file.";
base::DeleteFile(temp_file);
return;
}
// Replace the current file with the temp file.
if (!base::ReplaceFile(temp_file, path, /*error=*/nullptr))
LOG(ERROR) << "Cannot replace the temporary file.";
}
const std::array<const char*, 2>& GetBackupPhotoUrls() { const std::array<const char*, 2>& GetBackupPhotoUrls() {
return Shell::Get() return Shell::Get()
->ambient_controller() ->ambient_controller()
...@@ -155,12 +92,18 @@ const std::array<const char*, 2>& GetBackupPhotoUrls() { ...@@ -155,12 +92,18 @@ const std::array<const char*, 2>& GetBackupPhotoUrls() {
->GetBackupPhotoUrls(); ->GetBackupPhotoUrls();
} }
// Get the cache root path for ambient mode.
base::FilePath GetCacheRootPath() {
base::FilePath home_dir;
CHECK(base::PathService::Get(base::DIR_HOME, &home_dir));
return home_dir.Append(FILE_PATH_LITERAL(kAmbientModeDirectoryName));
}
} // namespace } // namespace
AmbientPhotoController::AmbientPhotoController() AmbientPhotoController::AmbientPhotoController()
: fetch_topic_retry_backoff_(&kFetchTopicRetryBackoffPolicy), : fetch_topic_retry_backoff_(&kFetchTopicRetryBackoffPolicy),
resume_fetch_image_backoff_(&kResumeFetchImageBackoffPolicy), resume_fetch_image_backoff_(&kResumeFetchImageBackoffPolicy),
photo_cache_(AmbientPhotoCache::Create()),
task_runner_( task_runner_(
base::ThreadPool::CreateSequencedTaskRunner(GetTaskTraits())) { base::ThreadPool::CreateSequencedTaskRunner(GetTaskTraits())) {
ambient_backend_model_observer_.Add(&ambient_backend_model_); ambient_backend_model_observer_.Add(&ambient_backend_model_);
...@@ -179,7 +122,7 @@ void AmbientPhotoController::StartScreenUpdate() { ...@@ -179,7 +122,7 @@ void AmbientPhotoController::StartScreenUpdate() {
// Would use |timer_.FireNow()| but this does not execute if screen is // Would use |timer_.FireNow()| but this does not execute if screen is
// locked. Manually call the expected callback instead. // locked. Manually call the expected callback instead.
backup_photo_refresh_timer_.Stop(); backup_photo_refresh_timer_.Stop();
PrepareFetchBackupImages(); FetchBackupImages();
} }
} }
...@@ -204,7 +147,7 @@ void AmbientPhotoController::ScheduleFetchBackupImages() { ...@@ -204,7 +147,7 @@ void AmbientPhotoController::ScheduleFetchBackupImages() {
FROM_HERE, FROM_HERE,
std::max(kBackupPhotoRefreshDelay, std::max(kBackupPhotoRefreshDelay,
resume_fetch_image_backoff_.GetTimeUntilRelease()), resume_fetch_image_backoff_.GetTimeUntilRelease()),
base::BindOnce(&AmbientPhotoController::PrepareFetchBackupImages, base::BindOnce(&AmbientPhotoController::FetchBackupImages,
weak_factory_.GetWeakPtr())); weak_factory_.GetWeakPtr()));
} }
...@@ -238,12 +181,21 @@ void AmbientPhotoController::FetchWeather() { ...@@ -238,12 +181,21 @@ void AmbientPhotoController::FetchWeather() {
} }
void AmbientPhotoController::ClearCache() { void AmbientPhotoController::ClearCache() {
task_runner_->PostTask(FROM_HERE, DCHECK(photo_cache_);
base::BindOnce( DCHECK(backup_photo_cache_);
[](const base::FilePath& file_path) { photo_cache_->Clear();
base::DeletePathRecursively(file_path); backup_photo_cache_->Clear();
}, }
GetCachePath()));
void AmbientPhotoController::InitCache() {
if (!photo_cache_) {
photo_cache_ = AmbientPhotoCache::Create(GetCacheRootPath().Append(
FILE_PATH_LITERAL(kAmbientModeCacheDirectoryName)));
}
if (!backup_photo_cache_) {
backup_photo_cache_ = AmbientPhotoCache::Create(GetCacheRootPath().Append(
FILE_PATH_LITERAL(kAmbientModeBackupCacheDirectoryName)));
}
} }
void AmbientPhotoController::ScheduleFetchTopics(bool backoff) { void AmbientPhotoController::ScheduleFetchTopics(bool backoff) {
...@@ -265,28 +217,21 @@ void AmbientPhotoController::ScheduleRefreshImage() { ...@@ -265,28 +217,21 @@ void AmbientPhotoController::ScheduleRefreshImage() {
weak_factory_.GetWeakPtr())); weak_factory_.GetWeakPtr()));
} }
void AmbientPhotoController::PrepareFetchBackupImages() {
task_runner_->PostTaskAndReply(
FROM_HERE,
base::BindOnce([]() { CreateDirIfNotExists(GetBackupCachePath()); }),
base::BindOnce(&AmbientPhotoController::FetchBackupImages,
weak_factory_.GetWeakPtr()));
}
void AmbientPhotoController::FetchBackupImages() { void AmbientPhotoController::FetchBackupImages() {
const auto& backup_photo_urls = GetBackupPhotoUrls(); const auto& backup_photo_urls = GetBackupPhotoUrls();
backup_retries_to_read_from_cache_ = backup_photo_urls.size(); backup_retries_to_read_from_cache_ = backup_photo_urls.size();
for (size_t i = 0; i < backup_photo_urls.size(); i++) { for (size_t i = 0; i < backup_photo_urls.size(); i++) {
photo_cache_->DownloadPhotoToFile( backup_photo_cache_->DownloadPhotoToFile(
backup_photo_urls.at(i), backup_photo_urls.at(i),
/*cache_index=*/i,
/*is_related=*/false,
base::BindOnce(&AmbientPhotoController::OnBackupImageFetched, base::BindOnce(&AmbientPhotoController::OnBackupImageFetched,
weak_factory_.GetWeakPtr()), weak_factory_.GetWeakPtr()));
GetBackupFilePath(i));
} }
} }
void AmbientPhotoController::OnBackupImageFetched(base::FilePath file_path) { void AmbientPhotoController::OnBackupImageFetched(bool success) {
if (file_path.empty()) { if (!success) {
// TODO(b/169807068) Change to retry individual failed images. // TODO(b/169807068) Change to retry individual failed images.
resume_fetch_image_backoff_.InformOfRequest(/*succeeded=*/false); resume_fetch_image_backoff_.InformOfRequest(/*succeeded=*/false);
LOG(WARNING) << "Downloading backup image failed."; LOG(WARNING) << "Downloading backup image failed.";
...@@ -299,10 +244,8 @@ void AmbientPhotoController::OnBackupImageFetched(base::FilePath file_path) { ...@@ -299,10 +244,8 @@ void AmbientPhotoController::OnBackupImageFetched(base::FilePath file_path) {
const AmbientModeTopic* AmbientPhotoController::GetNextTopic() { const AmbientModeTopic* AmbientPhotoController::GetNextTopic() {
const auto& topics = ambient_backend_model_.topics(); const auto& topics = ambient_backend_model_.topics();
// If no more topics, will read from cache. // If no more topics, will read from cache.
if (topic_index_ == topics.size()) { if (topic_index_ == topics.size())
DVLOG(3) << "No more topics";
return nullptr; return nullptr;
}
return &topics[topic_index_++]; return &topics[topic_index_++];
} }
...@@ -328,9 +271,7 @@ void AmbientPhotoController::OnScreenUpdateInfoFetched( ...@@ -328,9 +271,7 @@ void AmbientPhotoController::OnScreenUpdateInfoFetched(
} }
void AmbientPhotoController::ResetImageData() { void AmbientPhotoController::ResetImageData() {
image_data_.reset(); cache_entry_.reset();
related_image_data_.reset();
image_details_.reset();
image_ = gfx::ImageSkia(); image_ = gfx::ImageSkia();
related_image_ = gfx::ImageSkia(); related_image_ = gfx::ImageSkia();
...@@ -344,24 +285,21 @@ void AmbientPhotoController::FetchPhotoRawData() { ...@@ -344,24 +285,21 @@ void AmbientPhotoController::FetchPhotoRawData() {
const int num_callbacks = (topic->related_image_url) ? 2 : 1; const int num_callbacks = (topic->related_image_url) ? 2 : 1;
auto on_done = base::BarrierClosure( auto on_done = base::BarrierClosure(
num_callbacks, num_callbacks,
base::BindOnce(&AmbientPhotoController::OnAllPhotoRawDataAvailable, base::BindOnce(&AmbientPhotoController::OnAllPhotoRawDataDownloaded,
weak_factory_.GetWeakPtr(), weak_factory_.GetWeakPtr()));
/*from_downloading=*/true));
photo_cache_->DownloadPhoto( photo_cache_->DownloadPhoto(
topic->url, topic->url,
base::BindOnce(&AmbientPhotoController::OnPhotoRawDataAvailable, base::BindOnce(&AmbientPhotoController::OnPhotoRawDataDownloaded,
weak_factory_.GetWeakPtr(), weak_factory_.GetWeakPtr(),
/*from_downloading=*/true,
/*is_related_image=*/false, on_done, /*is_related_image=*/false, on_done,
std::make_unique<std::string>(topic->details))); std::make_unique<std::string>(topic->details)));
if (topic->related_image_url) { if (topic->related_image_url) {
photo_cache_->DownloadPhoto( photo_cache_->DownloadPhoto(
*(topic->related_image_url), *(topic->related_image_url),
base::BindOnce(&AmbientPhotoController::OnPhotoRawDataAvailable, base::BindOnce(&AmbientPhotoController::OnPhotoRawDataDownloaded,
weak_factory_.GetWeakPtr(), weak_factory_.GetWeakPtr(),
/*from_downloading=*/true,
/*is_related_image=*/true, on_done, /*is_related_image=*/true, on_done,
std::make_unique<std::string>(topic->details))); std::make_unique<std::string>(topic->details)));
} }
...@@ -400,28 +338,14 @@ void AmbientPhotoController::TryReadPhotoRawData() { ...@@ -400,28 +338,14 @@ void AmbientPhotoController::TryReadPhotoRawData() {
} }
--backup_retries_to_read_from_cache_; --backup_retries_to_read_from_cache_;
DVLOG(3) << "Read from backup cache index: "
<< backup_cache_index_for_display_;
// Try to read a backup image. // Try to read a backup image.
auto photo_data = std::make_unique<std::string>(); backup_photo_cache_->ReadFiles(
auto* photo_data_ptr = photo_data.get(); /*cache_index=*/backup_cache_index_for_display_,
auto on_done = base::BindRepeating( base::BindOnce(&AmbientPhotoController::OnAllPhotoRawDataAvailable,
&AmbientPhotoController::OnAllPhotoRawDataAvailable, weak_factory_.GetWeakPtr(), /*from_downloading=*/false));
weak_factory_.GetWeakPtr(), /*from_downloading=*/false);
task_runner_->PostTaskAndReply(
FROM_HERE,
base::BindOnce(
[](size_t index, std::string* data) {
if (!base::ReadFileToString(GetBackupFilePath(index), data)) {
LOG(ERROR) << "Unable to read from backup cache.";
data->clear();
}
},
backup_cache_index_for_display_, photo_data_ptr),
base::BindOnce(&AmbientPhotoController::OnPhotoRawDataAvailable,
weak_factory_.GetWeakPtr(), /*from_downloading=*/false,
/*is_related_image=*/false, std::move(on_done),
/*details=*/std::make_unique<std::string>(),
std::move(photo_data)));
backup_cache_index_for_display_++; backup_cache_index_for_display_++;
if (backup_cache_index_for_display_ == GetBackupPhotoUrls().size()) if (backup_cache_index_for_display_ == GetBackupPhotoUrls().size())
...@@ -430,82 +354,43 @@ void AmbientPhotoController::TryReadPhotoRawData() { ...@@ -430,82 +354,43 @@ void AmbientPhotoController::TryReadPhotoRawData() {
} }
--retries_to_read_from_cache_; --retries_to_read_from_cache_;
std::string file_name = base::NumberToString(cache_index_for_display_); int current_cache_index = cache_index_for_display_;
++cache_index_for_display_; ++cache_index_for_display_;
if (cache_index_for_display_ == kMaxNumberOfCachedImages) if (cache_index_for_display_ == kMaxNumberOfCachedImages)
cache_index_for_display_ = 0; cache_index_for_display_ = 0;
auto photo_data = std::make_unique<std::string>(); DVLOG(3) << "Read from cache index: " << current_cache_index;
auto photo_details = std::make_unique<std::string>(); photo_cache_->ReadFiles(
auto* photo_data_ptr = photo_data.get(); current_cache_index,
auto* photo_details_ptr = photo_details.get();
auto on_done = base::BarrierClosure(
/*num_closures=*/2,
base::BindOnce(&AmbientPhotoController::OnAllPhotoRawDataAvailable, base::BindOnce(&AmbientPhotoController::OnAllPhotoRawDataAvailable,
weak_factory_.GetWeakPtr(), weak_factory_.GetWeakPtr(), /*from_downloading=*/false));
/*from_downloading=*/false)); }
task_runner_->PostTaskAndReply(
FROM_HERE,
base::BindOnce(
[](const std::string& file_name, std::string* photo_data,
std::string* photo_details) {
if (!base::ReadFileToString(
GetCachePath().Append(file_name + kPhotoFileExt),
photo_data)) {
photo_data->clear();
}
if (!base::ReadFileToString(
GetCachePath().Append(file_name + kPhotoDetailsFileExt),
photo_details)) {
photo_details->clear();
}
},
file_name, photo_data_ptr, photo_details_ptr),
base::BindOnce(&AmbientPhotoController::OnPhotoRawDataAvailable,
weak_factory_.GetWeakPtr(), /*from_downloading=*/false,
/*is_related_image=*/false, on_done,
std::move(photo_details), std::move(photo_data)));
auto related_photo_data = std::make_unique<std::string>(); void AmbientPhotoController::OnPhotoRawDataDownloaded(
auto* related_photo_data_ptr = related_photo_data.get();
task_runner_->PostTaskAndReply(
FROM_HERE,
base::BindOnce(
[](const std::string& file_name, std::string* related_photo_data) {
const base::FilePath& file = GetRelatedFilePath(file_name);
if (!base::PathExists(file) ||
!base::ReadFileToString(file, related_photo_data)) {
related_photo_data->clear();
}
},
file_name, related_photo_data_ptr),
base::BindOnce(&AmbientPhotoController::OnPhotoRawDataAvailable,
weak_factory_.GetWeakPtr(), /*from_downloading=*/false,
/*is_related_image=*/true, on_done,
/*details=*/std::make_unique<std::string>(),
std::move(related_photo_data)));
}
void AmbientPhotoController::OnPhotoRawDataAvailable(
bool from_downloading,
bool is_related_image, bool is_related_image,
base::RepeatingClosure on_done, base::RepeatingClosure on_done,
std::unique_ptr<std::string> details, std::unique_ptr<std::string> details,
std::unique_ptr<std::string> data) { std::unique_ptr<std::string> data) {
if (is_related_image) { cache_entry_.details = std::move(details);
related_image_data_ = std::move(data);
} else { if (is_related_image)
image_data_ = std::move(data); cache_entry_.related_image = std::move(data);
image_details_ = std::move(details); else
} cache_entry_.image = std::move(data);
std::move(on_done).Run(); std::move(on_done).Run();
} }
void AmbientPhotoController::OnAllPhotoRawDataAvailable(bool from_downloading) { void AmbientPhotoController::OnAllPhotoRawDataDownloaded() {
if (!image_data_ || image_data_->empty()) { OnAllPhotoRawDataAvailable(/*from_downloading=*/true,
std::move(cache_entry_));
}
void AmbientPhotoController::OnAllPhotoRawDataAvailable(
bool from_downloading,
PhotoCacheEntry cache_entry) {
if (!cache_entry.image || cache_entry.image->empty()) {
if (from_downloading) { if (from_downloading) {
LOG(ERROR) << "Failed to download image"; LOG(ERROR) << "Failed to download image";
resume_fetch_image_backoff_.InformOfRequest(/*succeeded=*/false); resume_fetch_image_backoff_.InformOfRequest(/*succeeded=*/false);
...@@ -514,57 +399,49 @@ void AmbientPhotoController::OnAllPhotoRawDataAvailable(bool from_downloading) { ...@@ -514,57 +399,49 @@ void AmbientPhotoController::OnAllPhotoRawDataAvailable(bool from_downloading) {
TryReadPhotoRawData(); TryReadPhotoRawData();
return; return;
} }
DVLOG_IF(3, from_downloading)
<< "Save photo to cache index: " << cache_index_for_store_; if (from_downloading) {
const std::string file_name = base::NumberToString(cache_index_for_store_); // If the data is fetched from downloading, write to disk.
// If the data is fetched from downloading, write to disk. // Note: WriteFiles could fail. The saved file name may not be continuous.
// Note: WriteFile() could fail. The saved file name may not be continuous. DVLOG(3) << "Save photo to cache index: " << cache_index_for_store_;
if (from_downloading) auto current_cache_index = cache_index_for_store_;
++cache_index_for_store_; ++cache_index_for_store_;
if (cache_index_for_store_ == kMaxNumberOfCachedImages) if (cache_index_for_store_ == kMaxNumberOfCachedImages)
cache_index_for_store_ = 0; cache_index_for_store_ = 0;
auto* image = cache_entry.image.get();
auto* details = cache_entry.details.get();
auto* related_image = cache_entry.related_image.get();
photo_cache_->WriteFiles(
/*cache_index=*/current_cache_index, image, details, related_image,
base::BindOnce(&AmbientPhotoController::OnPhotoRawDataSaved,
weak_factory_.GetWeakPtr(), from_downloading,
std::move(cache_entry)));
} else {
OnPhotoRawDataSaved(from_downloading, std::move(cache_entry));
}
}
void AmbientPhotoController::OnPhotoRawDataSaved(bool from_downloading,
PhotoCacheEntry cache_entry) {
bool has_related =
cache_entry.related_image && !cache_entry.related_image->empty();
const int num_callbacks = has_related ? 2 : 1;
const int num_callbacks = related_image_data_ ? 2 : 1;
auto on_done = base::BarrierClosure( auto on_done = base::BarrierClosure(
num_callbacks, num_callbacks,
base::BindOnce(&AmbientPhotoController::OnAllPhotoDecoded, base::BindOnce(&AmbientPhotoController::OnAllPhotoDecoded,
weak_factory_.GetWeakPtr(), from_downloading, weak_factory_.GetWeakPtr(), from_downloading,
/*hash=*/base::SHA1HashString(*image_data_))); /*hash=*/base::SHA1HashString(*cache_entry.image)));
base::Optional<std::string> related_image_data;
if (related_image_data_)
related_image_data = *related_image_data_;
task_runner_->PostTaskAndReply( DecodePhotoRawData(from_downloading,
FROM_HERE,
base::BindOnce(
[](const std::string& file_name, bool need_to_save,
const std::string& data, const std::string& details,
const base::Optional<std::string>& related_data) {
if (need_to_save) {
WriteFile(GetCachePath().Append(file_name + kPhotoFileExt), data);
WriteFile(GetCachePath().Append(file_name + kPhotoDetailsFileExt),
details);
const base::FilePath& related_data_file =
GetRelatedFilePath(file_name);
if (related_data) {
WriteFile(related_data_file, *related_data);
} else {
if (base::PathExists(related_data_file))
base::DeleteFile(related_data_file);
}
}
},
file_name, from_downloading, *image_data_, *image_details_,
std::move(related_image_data)),
base::BindOnce(&AmbientPhotoController::DecodePhotoRawData,
weak_factory_.GetWeakPtr(), from_downloading,
/*is_related_image=*/false, on_done, /*is_related_image=*/false, on_done,
std::move(image_data_))); std::move(cache_entry.image));
if (related_image_data_) { if (has_related) {
DecodePhotoRawData(from_downloading, /*is_related_image=*/true, on_done, DecodePhotoRawData(from_downloading, /*is_related_image=*/true, on_done,
std::move(related_image_data_)); std::move(cache_entry.related_image));
} }
} }
...@@ -617,7 +494,8 @@ void AmbientPhotoController::OnAllPhotoDecoded(bool from_downloading, ...@@ -617,7 +494,8 @@ void AmbientPhotoController::OnAllPhotoDecoded(bool from_downloading,
PhotoWithDetails detailed_photo; PhotoWithDetails detailed_photo;
detailed_photo.photo = image_; detailed_photo.photo = image_;
detailed_photo.related_photo = related_image_; detailed_photo.related_photo = related_image_;
detailed_photo.details = *image_details_; if (cache_entry_.details)
detailed_photo.details = *cache_entry_.details;
detailed_photo.hash = hash; detailed_photo.hash = hash;
ResetImageData(); ResetImageData();
...@@ -678,7 +556,7 @@ void AmbientPhotoController::FetchImageForTesting() { ...@@ -678,7 +556,7 @@ void AmbientPhotoController::FetchImageForTesting() {
} }
void AmbientPhotoController::FetchBackupImagesForTesting() { void AmbientPhotoController::FetchBackupImagesForTesting() {
PrepareFetchBackupImages(); FetchBackupImages();
} }
} // namespace ash } // namespace ash
...@@ -78,6 +78,8 @@ class ASH_EXPORT AmbientPhotoController : public AmbientBackendModelObserver { ...@@ -78,6 +78,8 @@ class ASH_EXPORT AmbientPhotoController : public AmbientBackendModelObserver {
// Clear cache when Settings changes. // Clear cache when Settings changes.
void ClearCache(); void ClearCache();
void InitCache();
private: private:
friend class AmbientAshTestBase; friend class AmbientAshTestBase;
...@@ -89,13 +91,10 @@ class ASH_EXPORT AmbientPhotoController : public AmbientBackendModelObserver { ...@@ -89,13 +91,10 @@ class ASH_EXPORT AmbientPhotoController : public AmbientBackendModelObserver {
void ScheduleRefreshImage(); void ScheduleRefreshImage();
// Create the backup cache directory and start downloading images.
void PrepareFetchBackupImages();
// Download backup cache images. // Download backup cache images.
void FetchBackupImages(); void FetchBackupImages();
void OnBackupImageFetched(base::FilePath file_path); void OnBackupImageFetched(bool success);
void GetScreenUpdateInfo(); void GetScreenUpdateInfo();
...@@ -114,13 +113,17 @@ class ASH_EXPORT AmbientPhotoController : public AmbientBackendModelObserver { ...@@ -114,13 +113,17 @@ class ASH_EXPORT AmbientPhotoController : public AmbientBackendModelObserver {
// Try to read photo raw data from cache. // Try to read photo raw data from cache.
void TryReadPhotoRawData(); void TryReadPhotoRawData();
void OnPhotoRawDataAvailable(bool from_downloading, void OnPhotoRawDataDownloaded(bool is_related_image,
bool is_related_image, base::RepeatingClosure on_done,
base::RepeatingClosure on_done, std::unique_ptr<std::string> details,
std::unique_ptr<std::string> details, std::unique_ptr<std::string> data);
std::unique_ptr<std::string> data);
void OnAllPhotoRawDataDownloaded();
void OnAllPhotoRawDataAvailable(bool from_downloading); void OnAllPhotoRawDataAvailable(bool from_downloading,
PhotoCacheEntry cache_entry);
void OnPhotoRawDataSaved(bool from_downloading, PhotoCacheEntry cache_entry);
void DecodePhotoRawData(bool from_downloading, void DecodePhotoRawData(bool from_downloading,
bool is_related_image, bool is_related_image,
...@@ -152,6 +155,15 @@ class ASH_EXPORT AmbientPhotoController : public AmbientBackendModelObserver { ...@@ -152,6 +155,15 @@ class ASH_EXPORT AmbientPhotoController : public AmbientBackendModelObserver {
return photo_cache_.get(); return photo_cache_.get();
} }
void set_backup_photo_cache_for_testing(
std::unique_ptr<AmbientPhotoCache> photo_cache) {
backup_photo_cache_ = std::move(photo_cache);
}
AmbientPhotoCache* get_backup_photo_cache_for_testing() {
return backup_photo_cache_.get();
}
void FetchTopicsForTesting(); void FetchTopicsForTesting();
void FetchImageForTesting(); void FetchImageForTesting();
...@@ -206,13 +218,12 @@ class ASH_EXPORT AmbientPhotoController : public AmbientBackendModelObserver { ...@@ -206,13 +218,12 @@ class ASH_EXPORT AmbientPhotoController : public AmbientBackendModelObserver {
ambient_backend_model_observer_{this}; ambient_backend_model_observer_{this};
std::unique_ptr<AmbientPhotoCache> photo_cache_; std::unique_ptr<AmbientPhotoCache> photo_cache_;
std::unique_ptr<AmbientPhotoCache> backup_photo_cache_;
scoped_refptr<base::SequencedTaskRunner> task_runner_; scoped_refptr<base::SequencedTaskRunner> task_runner_;
// Temporary data store when fetching images and details. // Temporary data store when fetching images and details.
std::unique_ptr<std::string> image_data_; PhotoCacheEntry cache_entry_;
std::unique_ptr<std::string> related_image_data_;
std::unique_ptr<std::string> image_details_;
gfx::ImageSkia image_; gfx::ImageSkia image_;
gfx::ImageSkia related_image_; gfx::ImageSkia related_image_;
......
...@@ -26,6 +26,7 @@ ...@@ -26,6 +26,7 @@
#include "base/path_service.h" #include "base/path_service.h"
#include "base/run_loop.h" #include "base/run_loop.h"
#include "base/scoped_observer.h" #include "base/scoped_observer.h"
#include "base/stl_util.h"
#include "base/system/sys_info.h" #include "base/system/sys_info.h"
#include "base/test/bind.h" #include "base/test/bind.h"
#include "base/timer/timer.h" #include "base/timer/timer.h"
...@@ -49,44 +50,35 @@ class MockAmbientBackendModelObserver : public AmbientBackendModelObserver { ...@@ -49,44 +50,35 @@ class MockAmbientBackendModelObserver : public AmbientBackendModelObserver {
class AmbientPhotoControllerTest : public AmbientAshTestBase { class AmbientPhotoControllerTest : public AmbientAshTestBase {
public: public:
// AmbientAshTestBase: std::vector<int> GetSavedCacheIndices(bool backup = false) {
void SetUp() override { std::vector<int> result;
AmbientAshTestBase::SetUp(); const auto& map = backup ? GetBackupCachedFiles() : GetCachedFiles();
CleanupAmbientDir(); for (auto& it : map) {
} result.push_back(it.first);
void TearDown() override {
AmbientAshTestBase::TearDown();
CleanupAmbientDir();
}
void CleanupAmbientDir() { base::DeletePathRecursively(GetRootDir()); }
std::vector<base::FilePath> GetFilePathsInDir(const base::FilePath& dir) {
std::vector<base::FilePath> result;
base::FileEnumerator files(
dir, /*recursive=*/false,
base::FileEnumerator::FILES | base::FileEnumerator::DIRECTORIES);
for (base::FilePath current = files.Next(); !current.empty();
current = files.Next()) {
result.emplace_back(current);
} }
return result; return result;
} }
base::FilePath GetRootDir() { const PhotoCacheEntry* GetCacheEntryAtIndex(int cache_index,
base::FilePath home_dir; bool backup = false) {
base::PathService::Get(base::DIR_HOME, &home_dir); const auto& files = backup ? GetBackupCachedFiles() : GetCachedFiles();
return home_dir.Append(FILE_PATH_LITERAL(kAmbientModeDirectoryName)); auto it = files.find(cache_index);
} if (it == files.end())
return nullptr;
base::FilePath GetCacheDir() { else
return GetRootDir().Append( return &(it->second);
FILE_PATH_LITERAL(kAmbientModeCacheDirectoryName));
} }
base::FilePath GetBackupCacheDir() { void WriteCacheDataBlocking(int cache_index,
return GetRootDir().Append( const std::string* image = nullptr,
FILE_PATH_LITERAL(kAmbientModeBackupCacheDirectoryName)); const std::string* details = nullptr,
const std::string* related_image = nullptr) {
base::RunLoop loop;
photo_cache()->WriteFiles(/*cache_index=*/cache_index, /*image=*/image,
/*details=*/details,
/*related_image=*/related_image,
loop.QuitClosure());
loop.Run();
} }
}; };
...@@ -158,38 +150,27 @@ TEST_F(AmbientPhotoControllerTest, ShouldUpdatePhotoPeriodically) { ...@@ -158,38 +150,27 @@ TEST_F(AmbientPhotoControllerTest, ShouldUpdatePhotoPeriodically) {
// Test that image is saved. // Test that image is saved.
TEST_F(AmbientPhotoControllerTest, ShouldSaveImagesOnDisk) { TEST_F(AmbientPhotoControllerTest, ShouldSaveImagesOnDisk) {
base::FilePath ambient_image_path = GetCacheDir();
// Start to refresh images. It will download two images immediately and write // Start to refresh images. It will download two images immediately and write
// them in |ambient_image_path|. It will also download one more image after // them in |ambient_image_path|. It will also download one more image after
// fast forward. It will also download the related images and not cache them. // fast forward. It will also download the related images and not cache them.
photo_controller()->StartScreenUpdate(); photo_controller()->StartScreenUpdate();
FastForwardToNextImage(); FastForwardToNextImage();
// Count files and directories in ambient_image_path. There should be six // Count number of writes to cache. There should be three cache writes during
// files that were just created to save image files for this ambient mode // this ambient mode session.
// session. auto file_paths = GetSavedCacheIndices();
EXPECT_TRUE(base::PathExists(ambient_image_path)); EXPECT_EQ(file_paths.size(), 3u);
auto file_paths = GetFilePathsInDir(ambient_image_path);
// Three image files, three related image files, and three attribution files.
EXPECT_EQ(file_paths.size(), 9u);
for (auto& path : file_paths) {
// No sub directories.
EXPECT_FALSE(base::DirectoryExists(path));
}
} }
// Test that image is save and will not be deleted when stopping ambient mode. // Test that image is save and will not be deleted when stopping ambient mode.
TEST_F(AmbientPhotoControllerTest, ShouldNotDeleteImagesOnDisk) { TEST_F(AmbientPhotoControllerTest, ShouldNotDeleteImagesOnDisk) {
base::FilePath ambient_image_path = GetCacheDir();
// Start to refresh images. It will download two images immediately and write // Start to refresh images. It will download two images immediately and write
// them in |ambient_image_path|. It will also download one more image after // them in |ambient_image_path|. It will also download one more image after
// fast forward. It will also download the related images and not cache them. // fast forward. It will also download the related images and not cache them.
photo_controller()->StartScreenUpdate(); photo_controller()->StartScreenUpdate();
FastForwardToNextImage(); FastForwardToNextImage();
EXPECT_TRUE(base::PathExists(ambient_image_path)); EXPECT_EQ(GetSavedCacheIndices().size(), 3u);
auto image = photo_controller()->ambient_backend_model()->GetNextImage(); auto image = photo_controller()->ambient_backend_model()->GetNextImage();
EXPECT_FALSE(image.IsNull()); EXPECT_FALSE(image.IsNull());
...@@ -198,29 +179,14 @@ TEST_F(AmbientPhotoControllerTest, ShouldNotDeleteImagesOnDisk) { ...@@ -198,29 +179,14 @@ TEST_F(AmbientPhotoControllerTest, ShouldNotDeleteImagesOnDisk) {
photo_controller()->StopScreenUpdate(); photo_controller()->StopScreenUpdate();
FastForwardToNextImage(); FastForwardToNextImage();
EXPECT_TRUE(base::PathExists(ambient_image_path)); EXPECT_EQ(GetSavedCacheIndices().size(), 3u);
EXPECT_FALSE(base::IsDirectoryEmpty(ambient_image_path));
image = photo_controller()->ambient_backend_model()->GetNextImage(); image = photo_controller()->ambient_backend_model()->GetNextImage();
EXPECT_TRUE(image.IsNull()); EXPECT_TRUE(image.IsNull());
// Count files and directories in ambient_image_path. There should be six
// files that were just created to save image files for the prior ambient mode
// session.
EXPECT_TRUE(base::PathExists(ambient_image_path));
auto file_paths = GetFilePathsInDir(ambient_image_path);
// Three image files, three related image files, and three attribution files.
EXPECT_EQ(file_paths.size(), 9u);
for (auto& path : file_paths) {
// No sub directories.
EXPECT_FALSE(base::DirectoryExists(path));
}
} }
// Test that image is read from disk when no more topics. // Test that image is read from disk when no more topics.
TEST_F(AmbientPhotoControllerTest, ShouldReadCacheWhenNoMoreTopics) { TEST_F(AmbientPhotoControllerTest, ShouldReadCacheWhenNoMoreTopics) {
base::FilePath ambient_image_path = GetCacheDir();
FetchImage(); FetchImage();
FastForwardToNextImage(); FastForwardToNextImage();
// Topics is empty. Will read from cache, which is empty. // Topics is empty. Will read from cache, which is empty.
...@@ -228,9 +194,8 @@ TEST_F(AmbientPhotoControllerTest, ShouldReadCacheWhenNoMoreTopics) { ...@@ -228,9 +194,8 @@ TEST_F(AmbientPhotoControllerTest, ShouldReadCacheWhenNoMoreTopics) {
EXPECT_TRUE(image.IsNull()); EXPECT_TRUE(image.IsNull());
// Save a file to check if it gets read for display. // Save a file to check if it gets read for display.
auto cached_image = ambient_image_path.Append("0.img"); std::string data("cached image");
base::CreateDirectory(ambient_image_path); WriteCacheDataBlocking(/*cache_index=*/0, &data);
base::WriteFile(cached_image, "cached image");
// Reset variables in photo controller. // Reset variables in photo controller.
photo_controller()->StopScreenUpdate(); photo_controller()->StopScreenUpdate();
...@@ -238,27 +203,21 @@ TEST_F(AmbientPhotoControllerTest, ShouldReadCacheWhenNoMoreTopics) { ...@@ -238,27 +203,21 @@ TEST_F(AmbientPhotoControllerTest, ShouldReadCacheWhenNoMoreTopics) {
FastForwardToNextImage(); FastForwardToNextImage();
image = photo_controller()->ambient_backend_model()->GetCurrentImage(); image = photo_controller()->ambient_backend_model()->GetCurrentImage();
EXPECT_FALSE(image.IsNull()); 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 that will try 100 times to read image from disk when no more topics.
TEST_F(AmbientPhotoControllerTest, TEST_F(AmbientPhotoControllerTest,
ShouldTry100TimesToReadCacheWhenNoMoreTopics) { ShouldTry100TimesToReadCacheWhenNoMoreTopics) {
base::FilePath ambient_image_path = GetCacheDir();
FetchImage(); FetchImage();
FastForwardToNextImage(); FastForwardToNextImage();
// Topics is empty. Will read from cache, which is empty. // Topics is empty. Will read from cache, which is empty.
auto image = photo_controller()->ambient_backend_model()->GetCurrentImage(); auto image = photo_controller()->ambient_backend_model()->GetCurrentImage();
EXPECT_TRUE(image.IsNull()); EXPECT_TRUE(image.IsNull());
// The initial file name to be read is 0. Save a file with 99.img to check if // The initial file name to be read is 0. Save a file with 99.img to check
// it gets read for display. // if it gets read for display.
auto cached_image = ambient_image_path.Append("99.img"); std::string data("cached image");
base::CreateDirectory(ambient_image_path); WriteCacheDataBlocking(/*cache_index=*/99, &data);
base::WriteFile(cached_image, "cached image");
// Reset variables in photo controller. // Reset variables in photo controller.
photo_controller()->StopScreenUpdate(); photo_controller()->StopScreenUpdate();
...@@ -270,8 +229,6 @@ TEST_F(AmbientPhotoControllerTest, ...@@ -270,8 +229,6 @@ TEST_F(AmbientPhotoControllerTest,
// Test that image is read from disk when image downloading failed. // Test that image is read from disk when image downloading failed.
TEST_F(AmbientPhotoControllerTest, ShouldReadCacheWhenImageDownloadingFailed) { TEST_F(AmbientPhotoControllerTest, ShouldReadCacheWhenImageDownloadingFailed) {
base::FilePath ambient_image_path = GetCacheDir();
SetDownloadPhotoData(""); SetDownloadPhotoData("");
FetchTopics(); FetchTopics();
// Forward a little bit time. FetchTopics() will succeed. Downloading should // Forward a little bit time. FetchTopics() will succeed. Downloading should
...@@ -281,9 +238,8 @@ TEST_F(AmbientPhotoControllerTest, ShouldReadCacheWhenImageDownloadingFailed) { ...@@ -281,9 +238,8 @@ TEST_F(AmbientPhotoControllerTest, ShouldReadCacheWhenImageDownloadingFailed) {
EXPECT_TRUE(image.IsNull()); EXPECT_TRUE(image.IsNull());
// Save a file to check if it gets read for display. // Save a file to check if it gets read for display.
auto cached_image = ambient_image_path.Append("0.img"); std::string data("cached image");
base::CreateDirectory(ambient_image_path); WriteCacheDataBlocking(/*cache_index=*/0, &data);
base::WriteFile(cached_image, "cached image");
// Reset variables in photo controller. // Reset variables in photo controller.
photo_controller()->StopScreenUpdate(); photo_controller()->StopScreenUpdate();
...@@ -297,8 +253,6 @@ TEST_F(AmbientPhotoControllerTest, ShouldReadCacheWhenImageDownloadingFailed) { ...@@ -297,8 +253,6 @@ TEST_F(AmbientPhotoControllerTest, ShouldReadCacheWhenImageDownloadingFailed) {
// Test that image is read from disk when image decoding failed. // Test that image is read from disk when image decoding failed.
TEST_F(AmbientPhotoControllerTest, ShouldReadCacheWhenImageDecodingFailed) { TEST_F(AmbientPhotoControllerTest, ShouldReadCacheWhenImageDecodingFailed) {
base::FilePath ambient_image_path = GetCacheDir();
SetDecodePhotoImage(gfx::ImageSkia()); SetDecodePhotoImage(gfx::ImageSkia());
FetchTopics(); FetchTopics();
// Forward a little bit time. FetchTopics() will succeed. // Forward a little bit time. FetchTopics() will succeed.
...@@ -311,8 +265,6 @@ TEST_F(AmbientPhotoControllerTest, ShouldReadCacheWhenImageDecodingFailed) { ...@@ -311,8 +265,6 @@ TEST_F(AmbientPhotoControllerTest, ShouldReadCacheWhenImageDecodingFailed) {
// Test that image will refresh when have more topics. // Test that image will refresh when have more topics.
TEST_F(AmbientPhotoControllerTest, ShouldResumWhenHaveMoreTopics) { TEST_F(AmbientPhotoControllerTest, ShouldResumWhenHaveMoreTopics) {
base::FilePath ambient_image_path = GetCacheDir();
FetchImage(); FetchImage();
FastForwardToNextImage(); FastForwardToNextImage();
// Topics is empty. Will read from cache, which is empty. // Topics is empty. Will read from cache, which is empty.
...@@ -327,37 +279,31 @@ TEST_F(AmbientPhotoControllerTest, ShouldResumWhenHaveMoreTopics) { ...@@ -327,37 +279,31 @@ TEST_F(AmbientPhotoControllerTest, ShouldResumWhenHaveMoreTopics) {
} }
TEST_F(AmbientPhotoControllerTest, ShouldDownloadBackupImagesWhenScheduled) { TEST_F(AmbientPhotoControllerTest, ShouldDownloadBackupImagesWhenScheduled) {
base::FilePath backup_image_path = GetBackupCacheDir();
std::string expected_data = "backup data"; std::string expected_data = "backup data";
SetDownloadPhotoData(expected_data); SetBackupDownloadPhotoData(expected_data);
photo_controller()->ScheduleFetchBackupImages(); photo_controller()->ScheduleFetchBackupImages();
EXPECT_TRUE( EXPECT_TRUE(
photo_controller()->backup_photo_refresh_timer_for_testing().IsRunning()); photo_controller()->backup_photo_refresh_timer_for_testing().IsRunning());
// TImer is running but download has not started yet. // Timer is running but download has not started yet.
EXPECT_FALSE(base::DirectoryExists(GetBackupCacheDir())); EXPECT_TRUE(GetSavedCacheIndices(/*backup=*/true).empty());
task_environment()->FastForwardBy(kBackupPhotoRefreshDelay); task_environment()->FastForwardBy(kBackupPhotoRefreshDelay);
// Timer should have stopped. // Timer should have stopped.
EXPECT_FALSE( EXPECT_FALSE(
photo_controller()->backup_photo_refresh_timer_for_testing().IsRunning()); photo_controller()->backup_photo_refresh_timer_for_testing().IsRunning());
// Download has triggered and backup cache directory is created. // Should have been two cache writes to backup data.
EXPECT_TRUE(base::DirectoryExists(backup_image_path)); const auto& backup_data = GetBackupCachedFiles();
EXPECT_EQ(backup_data.size(), 2u);
// Should be two files in backup cache directory. EXPECT_TRUE(base::Contains(backup_data, 0));
auto paths = GetFilePathsInDir(backup_image_path); EXPECT_TRUE(base::Contains(backup_data, 1));
std::sort(paths.begin(), paths.end()); for (const auto& i : backup_data) {
EXPECT_EQ(paths.size(), 2u); EXPECT_EQ(*(i.second.image), expected_data);
EXPECT_EQ(paths[0].BaseName().value(), "0.img"); EXPECT_FALSE(i.second.details);
EXPECT_EQ(paths[1].BaseName().value(), "1.img"); EXPECT_FALSE(i.second.related_image);
for (const auto& path : paths) {
std::string data;
base::ReadFileToString(path, &data);
EXPECT_EQ(data, expected_data);
} }
} }
...@@ -371,11 +317,7 @@ TEST_F(AmbientPhotoControllerTest, ShouldResetTimerWhenBackupImagesFail) { ...@@ -371,11 +317,7 @@ TEST_F(AmbientPhotoControllerTest, ShouldResetTimerWhenBackupImagesFail) {
ClearDownloadPhotoData(); ClearDownloadPhotoData();
task_environment()->FastForwardBy(kBackupPhotoRefreshDelay); task_environment()->FastForwardBy(kBackupPhotoRefreshDelay);
// Directory should have been created, but with no files in it. EXPECT_TRUE(GetBackupCachedFiles().empty());
EXPECT_TRUE(base::DirectoryExists(GetBackupCacheDir()));
auto paths = GetFilePathsInDir(GetBackupCacheDir());
EXPECT_EQ(paths.size(), 0u);
// Timer should have restarted. // Timer should have restarted.
EXPECT_TRUE( EXPECT_TRUE(
...@@ -389,7 +331,7 @@ TEST_F(AmbientPhotoControllerTest, ...@@ -389,7 +331,7 @@ TEST_F(AmbientPhotoControllerTest,
EXPECT_TRUE( EXPECT_TRUE(
photo_controller()->backup_photo_refresh_timer_for_testing().IsRunning()); photo_controller()->backup_photo_refresh_timer_for_testing().IsRunning());
SetDownloadPhotoData("image data"); SetBackupDownloadPhotoData("image data");
photo_controller()->StartScreenUpdate(); photo_controller()->StartScreenUpdate();
...@@ -399,19 +341,16 @@ TEST_F(AmbientPhotoControllerTest, ...@@ -399,19 +341,16 @@ TEST_F(AmbientPhotoControllerTest,
task_environment()->RunUntilIdle(); task_environment()->RunUntilIdle();
// Download has triggered and backup cache directory is created. // Download has triggered and backup cache directory is created. Should be
EXPECT_TRUE(base::DirectoryExists(GetBackupCacheDir())); // two cache writes to backup cache.
const auto& backup_data = GetBackupCachedFiles();
// Should be two files in backup cache directory. EXPECT_EQ(backup_data.size(), 2u);
auto paths = GetFilePathsInDir(GetBackupCacheDir()); EXPECT_TRUE(base::Contains(backup_data, 0));
std::sort(paths.begin(), paths.end()); EXPECT_TRUE(base::Contains(backup_data, 1));
EXPECT_EQ(paths.size(), 2u); for (const auto& i : backup_data) {
EXPECT_EQ(paths[0].BaseName().value(), "0.img"); EXPECT_EQ(*(i.second.image), "image data");
EXPECT_EQ(paths[1].BaseName().value(), "1.img"); EXPECT_FALSE(i.second.details);
for (const auto& path : paths) { EXPECT_FALSE(i.second.related_image);
std::string data;
base::ReadFileToString(path, &data);
EXPECT_EQ(data, "image data");
} }
} }
......
...@@ -4,12 +4,14 @@ ...@@ -4,12 +4,14 @@
#include "ash/ambient/test/ambient_ash_test_base.h" #include "ash/ambient/test/ambient_ash_test_base.h"
#include <map>
#include <memory> #include <memory>
#include <utility> #include <utility>
#include <vector> #include <vector>
#include "ash/ambient/ambient_access_token_controller.h" #include "ash/ambient/ambient_access_token_controller.h"
#include "ash/ambient/ambient_constants.h" #include "ash/ambient/ambient_constants.h"
#include "ash/ambient/ambient_photo_cache.h"
#include "ash/ambient/ambient_photo_controller.h" #include "ash/ambient/ambient_photo_controller.h"
#include "ash/ambient/test/ambient_ash_test_helper.h" #include "ash/ambient/test/ambient_ash_test_helper.h"
#include "ash/ambient/ui/ambient_background_image_view.h" #include "ash/ambient/ui/ambient_background_image_view.h"
...@@ -24,9 +26,7 @@ ...@@ -24,9 +26,7 @@
#include "ash/session/session_controller_impl.h" #include "ash/session/session_controller_impl.h"
#include "ash/shell.h" #include "ash/shell.h"
#include "base/callback.h" #include "base/callback.h"
#include "base/files/file_util.h"
#include "base/memory/ptr_util.h" #include "base/memory/ptr_util.h"
#include "base/notreached.h"
#include "base/run_loop.h" #include "base/run_loop.h"
#include "base/sequenced_task_runner.h" #include "base/sequenced_task_runner.h"
#include "base/threading/scoped_blocking_call.h" #include "base/threading/scoped_blocking_call.h"
...@@ -35,7 +35,6 @@ ...@@ -35,7 +35,6 @@
#include "chromeos/dbus/power/fake_power_manager_client.h" #include "chromeos/dbus/power/fake_power_manager_client.h"
#include "chromeos/dbus/power/power_manager_client.h" #include "chromeos/dbus/power/power_manager_client.h"
#include "chromeos/dbus/power_manager/idle.pb.h" #include "chromeos/dbus/power_manager/idle.pb.h"
#include "ui/display/screen.h"
#include "ui/gfx/image/image_skia.h" #include "ui/gfx/image/image_skia.h"
#include "ui/gfx/image/image_unittest_util.h" #include "ui/gfx/image/image_unittest_util.h"
#include "ui/views/controls/label.h" #include "ui/views/controls/label.h"
...@@ -66,26 +65,30 @@ class TestAmbientPhotoCacheImpl : public AmbientPhotoCache { ...@@ -66,26 +65,30 @@ class TestAmbientPhotoCacheImpl : public AmbientPhotoCache {
FROM_HERE, base::BindOnce(std::move(callback), std::move(data)), FROM_HERE, base::BindOnce(std::move(callback), std::move(data)),
base::TimeDelta::FromMilliseconds(1)); base::TimeDelta::FromMilliseconds(1));
} }
void DownloadPhotoToFile(const std::string& url, void DownloadPhotoToFile(const std::string& url,
base::OnceCallback<void(base::FilePath)> callback, int cache_index,
const base::FilePath& file_path) override { bool is_related,
base::OnceCallback<void(bool)> callback) override {
if (!download_data_) { if (!download_data_) {
base::SequencedTaskRunnerHandle::Get()->PostTask( base::SequencedTaskRunnerHandle::Get()->PostTask(
FROM_HERE, base::BindOnce(std::move(callback), base::FilePath())); FROM_HERE, base::BindOnce(std::move(callback), /*success=*/false));
return; return;
} }
if (!WriteFile(file_path, *download_data_)) { files_.insert(std::pair<int, PhotoCacheEntry>(
LOG(WARNING) << "error writing file to file_path: " << file_path; cache_index,
PhotoCacheEntry(
base::SequencedTaskRunnerHandle::Get()->PostTask( is_related ? nullptr
FROM_HERE, base::BindOnce(std::move(callback), base::FilePath())); : std::make_unique<std::string>(*download_data_),
return; /*details=*/nullptr,
} is_related ? std::make_unique<std::string>(*download_data_)
: nullptr)));
base::SequencedTaskRunnerHandle::Get()->PostTask( base::SequencedTaskRunnerHandle::Get()->PostTask(
FROM_HERE, base::BindOnce(std::move(callback), file_path)); FROM_HERE, base::BindOnce(std::move(callback), /*success=*/true));
} }
void DecodePhoto( void DecodePhoto(
std::unique_ptr<std::string> data, std::unique_ptr<std::string> data,
base::OnceCallback<void(const gfx::ImageSkia&)> callback) override { base::OnceCallback<void(const gfx::ImageSkia&)> callback) override {
...@@ -101,6 +104,47 @@ class TestAmbientPhotoCacheImpl : public AmbientPhotoCache { ...@@ -101,6 +104,47 @@ class TestAmbientPhotoCacheImpl : public AmbientPhotoCache {
FROM_HERE, base::BindOnce(std::move(callback), image)); FROM_HERE, base::BindOnce(std::move(callback), image));
} }
void WriteFiles(int cache_index,
const std::string* const image,
const std::string* const details,
const std::string* const related_image,
base::OnceClosure callback) override {
files_.insert(std::pair<int, PhotoCacheEntry>(
cache_index,
PhotoCacheEntry(
image ? std::make_unique<std::string>(*image) : nullptr,
details ? std::make_unique<std::string>(*details) : nullptr,
related_image ? std::make_unique<std::string>(*related_image)
: nullptr)));
std::move(callback).Run();
}
void ReadFiles(int cache_index,
base::OnceCallback<void(PhotoCacheEntry)> callback) override {
auto it = files_.find(cache_index);
if (it == files_.end()) {
std::move(callback).Run(PhotoCacheEntry());
return;
}
std::move(callback).Run(PhotoCacheEntry(
it->second.image ? std::make_unique<std::string>(*(it->second.image))
: nullptr,
it->second.details
? std::make_unique<std::string>(*(it->second.details))
: nullptr,
it->second.related_image
? std::make_unique<std::string>(*(it->second.related_image))
: nullptr));
}
void Clear() override {
download_count_ = 0;
download_data_.reset();
decoded_size_ = gfx::Size(10, 20);
decoded_image_.reset();
files_.clear();
}
void SetDownloadData(std::unique_ptr<std::string> download_data) { void SetDownloadData(std::unique_ptr<std::string> download_data) {
download_data_ = std::move(download_data); download_data_ = std::move(download_data);
} }
...@@ -112,6 +156,8 @@ class TestAmbientPhotoCacheImpl : public AmbientPhotoCache { ...@@ -112,6 +156,8 @@ class TestAmbientPhotoCacheImpl : public AmbientPhotoCache {
void SetDecodedPhoto(const gfx::ImageSkia& image) { decoded_image_ = image; } void SetDecodedPhoto(const gfx::ImageSkia& image) { decoded_image_ = image; }
const std::map<int, PhotoCacheEntry>& get_files() { return files_; }
private: private:
int download_count_ = 0; int download_count_ = 0;
...@@ -122,6 +168,8 @@ class TestAmbientPhotoCacheImpl : public AmbientPhotoCache { ...@@ -122,6 +168,8 @@ class TestAmbientPhotoCacheImpl : public AmbientPhotoCache {
gfx::Size decoded_size_{10, 20}; gfx::Size decoded_size_{10, 20};
// If set, will replay this image. // If set, will replay this image.
base::Optional<gfx::ImageSkia> decoded_image_; base::Optional<gfx::ImageSkia> decoded_image_;
std::map<int, PhotoCacheEntry> files_;
}; };
AmbientAshTestBase::AmbientAshTestBase() AmbientAshTestBase::AmbientAshTestBase()
...@@ -139,6 +187,8 @@ void AmbientAshTestBase::SetUp() { ...@@ -139,6 +187,8 @@ void AmbientAshTestBase::SetUp() {
std::make_unique<FakeAmbientBackendControllerImpl>()); std::make_unique<FakeAmbientBackendControllerImpl>());
photo_controller()->set_photo_cache_for_testing( photo_controller()->set_photo_cache_for_testing(
std::make_unique<TestAmbientPhotoCacheImpl>()); std::make_unique<TestAmbientPhotoCacheImpl>());
photo_controller()->set_backup_photo_cache_for_testing(
std::make_unique<TestAmbientPhotoCacheImpl>());
token_controller()->SetTokenUsageBufferForTesting( token_controller()->SetTokenUsageBufferForTesting(
base::TimeDelta::FromSeconds(30)); base::TimeDelta::FromSeconds(30));
SetAmbientModeEnabled(true); SetAmbientModeEnabled(true);
...@@ -380,6 +430,21 @@ base::TimeDelta AmbientAshTestBase::GetRefreshTokenDelay() { ...@@ -380,6 +430,21 @@ base::TimeDelta AmbientAshTestBase::GetRefreshTokenDelay() {
return token_controller()->GetTimeUntilReleaseForTesting(); return token_controller()->GetTimeUntilReleaseForTesting();
} }
const std::map<int, PhotoCacheEntry>& AmbientAshTestBase::GetCachedFiles() {
auto* photo_cache = static_cast<TestAmbientPhotoCacheImpl*>(
photo_controller()->get_photo_cache_for_testing());
return photo_cache->get_files();
}
const std::map<int, PhotoCacheEntry>&
AmbientAshTestBase::GetBackupCachedFiles() {
auto* photo_cache = static_cast<TestAmbientPhotoCacheImpl*>(
photo_controller()->get_backup_photo_cache_for_testing());
return photo_cache->get_files();
}
AmbientController* AmbientAshTestBase::ambient_controller() { AmbientController* AmbientAshTestBase::ambient_controller() {
return Shell::Get()->ambient_controller(); return Shell::Get()->ambient_controller();
} }
...@@ -388,6 +453,10 @@ AmbientPhotoController* AmbientAshTestBase::photo_controller() { ...@@ -388,6 +453,10 @@ AmbientPhotoController* AmbientAshTestBase::photo_controller() {
return ambient_controller()->ambient_photo_controller(); return ambient_controller()->ambient_photo_controller();
} }
AmbientPhotoCache* AmbientAshTestBase::photo_cache() {
return photo_controller()->get_photo_cache_for_testing();
}
std::vector<AmbientContainerView*> AmbientAshTestBase::GetContainerViews() { std::vector<AmbientContainerView*> AmbientAshTestBase::GetContainerViews() {
std::vector<AmbientContainerView*> result; std::vector<AmbientContainerView*> result;
for (auto* ctrl : RootWindowController::root_window_controllers()) { for (auto* ctrl : RootWindowController::root_window_controllers()) {
...@@ -449,6 +518,20 @@ void AmbientAshTestBase::ClearDownloadPhotoData() { ...@@ -449,6 +518,20 @@ void AmbientAshTestBase::ClearDownloadPhotoData() {
photo_cache->SetDownloadData(nullptr); photo_cache->SetDownloadData(nullptr);
} }
void AmbientAshTestBase::SetBackupDownloadPhotoData(std::string data) {
auto* backup_cache = static_cast<TestAmbientPhotoCacheImpl*>(
photo_controller()->get_backup_photo_cache_for_testing());
backup_cache->SetDownloadData(std::make_unique<std::string>(std::move(data)));
}
void AmbientAshTestBase::ClearBackupDownloadPhotoData() {
auto* backup_cache = static_cast<TestAmbientPhotoCacheImpl*>(
photo_controller()->get_backup_photo_cache_for_testing());
backup_cache->SetDownloadData(nullptr);
}
void AmbientAshTestBase::SetDecodePhotoImage(const gfx::ImageSkia& image) { void AmbientAshTestBase::SetDecodePhotoImage(const gfx::ImageSkia& image) {
auto* photo_cache = static_cast<TestAmbientPhotoCacheImpl*>( auto* photo_cache = static_cast<TestAmbientPhotoCacheImpl*>(
photo_controller()->get_photo_cache_for_testing()); photo_controller()->get_photo_cache_for_testing());
......
...@@ -137,10 +137,15 @@ class AmbientAshTestBase : public AshTestBase { ...@@ -137,10 +137,15 @@ class AmbientAshTestBase : public AshTestBase {
// Returns the media string view for the default display. // Returns the media string view for the default display.
MediaStringView* GetMediaStringView(); MediaStringView* GetMediaStringView();
const std::map<int, PhotoCacheEntry>& GetCachedFiles();
const std::map<int, PhotoCacheEntry>& GetBackupCachedFiles();
AmbientController* ambient_controller(); AmbientController* ambient_controller();
AmbientPhotoController* photo_controller(); AmbientPhotoController* photo_controller();
AmbientPhotoCache* photo_cache();
// Returns the top-level views which contains all the ambient components. // Returns the top-level views which contains all the ambient components.
std::vector<AmbientContainerView*> GetContainerViews(); std::vector<AmbientContainerView*> GetContainerViews();
// Returns the top level ambient container view for the primary root window. // Returns the top level ambient container view for the primary root window.
...@@ -160,6 +165,10 @@ class AmbientAshTestBase : public AshTestBase { ...@@ -160,6 +165,10 @@ class AmbientAshTestBase : public AshTestBase {
void ClearDownloadPhotoData(); void ClearDownloadPhotoData();
void SetBackupDownloadPhotoData(std::string data);
void ClearBackupDownloadPhotoData();
void SetDecodePhotoImage(const gfx::ImageSkia& image); void SetDecodePhotoImage(const gfx::ImageSkia& image);
private: private:
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment