Commit 78622bba authored by nancylingwang's avatar nancylingwang Committed by Commit Bot

Add tests to verify the ARC app icon loading with large number apps.

This CL adds 2 test cases to simulate the ARC app icon loading when the
system start with 500 ARC apps.

1. The test case LoadLargeNumberIcons is used to verify adding 500 ARC
apps with good icons, then the system restarts, re-loading 500 ARC
app icons.

2. The test case LoadLargeNumberIconsWithBadIcon is used to verify
adding 500 ARC apps with good icons, and 10 ARC apps with bad icons,
then the system restarts, re-loading 510 ARC app icons.

Start 20 try jobs below, they both run pass.

BUG=1083331

Change-Id: I4d2f46757577e1db79d4aeb3d2aa497820dd0cf4
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2389800
Commit-Queue: Nancy Wang <nancylingwang@chromium.org>
Reviewed-by: default avatarXiyuan Xia <xiyuan@chromium.org>
Reviewed-by: default avatarDominick Ng <dominickn@chromium.org>
Reviewed-by: default avatarLong Cheng <lgcheng@google.com>
Cr-Commit-Position: refs/heads/master@{#805634}
parent 8dc142ad
...@@ -63,6 +63,10 @@ class ArcApps : public KeyedService, ...@@ -63,6 +63,10 @@ class ArcApps : public KeyedService,
~ArcApps() override; ~ArcApps() override;
ArcIconOnceLoader& GetArcIconOnceLoaderForTesting() {
return arc_icon_once_loader_;
}
private: private:
using AppIdToTaskIds = std::map<std::string, std::set<int>>; using AppIdToTaskIds = std::map<std::string, std::set<int>>;
using TaskIdToAppId = std::map<int, std::string>; using TaskIdToAppId = std::map<int, std::string>;
......
...@@ -99,11 +99,9 @@ void ArcIconOnceLoader::SizeSpecificLoader::LoadIcon( ...@@ -99,11 +99,9 @@ void ArcIconOnceLoader::SizeSpecificLoader::LoadIcon(
break; break;
} }
iter = icons_ auto arc_app_icon = host_.arc_app_icon_factory()->CreateArcAppIcon(
.insert(std::make_pair( profile_, app_id, size_in_dip_, this, icon_type);
app_id, std::make_unique<ArcAppIcon>( iter = icons_.insert(std::make_pair(app_id, std::move(arc_app_icon))).first;
profile_, app_id, size_in_dip_, this, icon_type)))
.first;
if (base::FeatureList::IsEnabled(features::kAppServiceAdaptiveIcon)) { if (base::FeatureList::IsEnabled(features::kAppServiceAdaptiveIcon)) {
host_.MaybeStartIconRequest(iter->second.get(), host_.MaybeStartIconRequest(iter->second.get(),
ui::ScaleFactor::NUM_SCALE_FACTORS); ui::ScaleFactor::NUM_SCALE_FACTORS);
...@@ -187,6 +185,7 @@ void ArcIconOnceLoader::SizeSpecificLoader::OnIconFailed(ArcAppIcon* icon) { ...@@ -187,6 +185,7 @@ void ArcIconOnceLoader::SizeSpecificLoader::OnIconFailed(ArcAppIcon* icon) {
ArcIconOnceLoader::ArcIconOnceLoader(Profile* profile) ArcIconOnceLoader::ArcIconOnceLoader(Profile* profile)
: profile_(profile), stop_observing_called_(false) { : profile_(profile), stop_observing_called_(false) {
ArcAppListPrefs::Get(profile)->AddObserver(this); ArcAppListPrefs::Get(profile)->AddObserver(this);
arc_app_icon_factory_ = std::make_unique<arc::ArcAppIconFactory>();
} }
ArcIconOnceLoader::~ArcIconOnceLoader() { ArcIconOnceLoader::~ArcIconOnceLoader() {
...@@ -242,6 +241,11 @@ void ArcIconOnceLoader::OnAppIconUpdated( ...@@ -242,6 +241,11 @@ void ArcIconOnceLoader::OnAppIconUpdated(
} }
} }
void ArcIconOnceLoader::SetArcAppIconFactoryForTesting(
std::unique_ptr<arc::ArcAppIconFactory> arc_app_icon_factory) {
arc_app_icon_factory_ = std::move(arc_app_icon_factory);
}
void ArcIconOnceLoader::MaybeStartIconRequest(ArcAppIcon* arc_app_icon, void ArcIconOnceLoader::MaybeStartIconRequest(ArcAppIcon* arc_app_icon,
ui::ScaleFactor scale_factor) { ui::ScaleFactor scale_factor) {
DCHECK(arc_app_icon); DCHECK(arc_app_icon);
......
...@@ -13,6 +13,7 @@ ...@@ -13,6 +13,7 @@
#include "base/callback_forward.h" #include "base/callback_forward.h"
#include "base/macros.h" #include "base/macros.h"
#include "chrome/browser/ui/app_list/arc/arc_app_icon_descriptor.h" #include "chrome/browser/ui/app_list/arc/arc_app_icon_descriptor.h"
#include "chrome/browser/ui/app_list/arc/arc_app_icon_factory.h"
#include "chrome/browser/ui/app_list/arc/arc_app_list_prefs.h" #include "chrome/browser/ui/app_list/arc/arc_app_list_prefs.h"
#include "components/services/app_service/public/mojom/types.mojom.h" #include "components/services/app_service/public/mojom/types.mojom.h"
...@@ -53,6 +54,13 @@ class ArcIconOnceLoader : public ArcAppListPrefs::Observer { ...@@ -53,6 +54,13 @@ class ArcIconOnceLoader : public ArcAppListPrefs::Observer {
void OnAppIconUpdated(const std::string& app_id, void OnAppIconUpdated(const std::string& app_id,
const ArcAppIconDescriptor& descriptor) override; const ArcAppIconDescriptor& descriptor) override;
void SetArcAppIconFactoryForTesting(
std::unique_ptr<arc::ArcAppIconFactory> arc_app_icon_factory);
arc::ArcAppIconFactory* arc_app_icon_factory() {
return arc_app_icon_factory_.get();
}
private: private:
class SizeSpecificLoader; class SizeSpecificLoader;
...@@ -82,6 +90,8 @@ class ArcIconOnceLoader : public ArcAppListPrefs::Observer { ...@@ -82,6 +90,8 @@ class ArcIconOnceLoader : public ArcAppListPrefs::Observer {
std::map<SizeAndType, std::unique_ptr<SizeSpecificLoader>> std::map<SizeAndType, std::unique_ptr<SizeSpecificLoader>>
size_specific_loaders_; size_specific_loaders_;
std::unique_ptr<arc::ArcAppIconFactory> arc_app_icon_factory_;
// The current icon loading requests. // The current icon loading requests.
std::set<ArcAppIcon*> in_flight_requests_; std::set<ArcAppIcon*> in_flight_requests_;
......
...@@ -1643,6 +1643,8 @@ static_library("ui") { ...@@ -1643,6 +1643,8 @@ static_library("ui") {
"app_list/arc/arc_app_icon.h", "app_list/arc/arc_app_icon.h",
"app_list/arc/arc_app_icon_descriptor.cc", "app_list/arc/arc_app_icon_descriptor.cc",
"app_list/arc/arc_app_icon_descriptor.h", "app_list/arc/arc_app_icon_descriptor.h",
"app_list/arc/arc_app_icon_factory.cc",
"app_list/arc/arc_app_icon_factory.h",
"app_list/arc/arc_app_launcher.cc", "app_list/arc/arc_app_launcher.cc",
"app_list/arc/arc_app_launcher.h", "app_list/arc/arc_app_launcher.h",
"app_list/arc/arc_app_list_prefs.cc", "app_list/arc/arc_app_list_prefs.cc",
......
...@@ -41,8 +41,7 @@ bool disable_safe_decoding_for_testing = false; ...@@ -41,8 +41,7 @@ bool disable_safe_decoding_for_testing = false;
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
// ArcAppIcon::ReadResult // ArcAppIcon::ReadResult
struct ArcAppIcon::ReadResult { ArcAppIcon::ReadResult::ReadResult(bool error,
ReadResult(bool error,
bool request_to_install, bool request_to_install,
ui::ScaleFactor scale_factor, ui::ScaleFactor scale_factor,
bool resize_allowed, bool resize_allowed,
...@@ -52,13 +51,7 @@ struct ArcAppIcon::ReadResult { ...@@ -52,13 +51,7 @@ struct ArcAppIcon::ReadResult {
scale_factor(scale_factor), scale_factor(scale_factor),
resize_allowed(resize_allowed), resize_allowed(resize_allowed),
unsafe_icon_data(std::move(unsafe_icon_data)) {} unsafe_icon_data(std::move(unsafe_icon_data)) {}
ArcAppIcon::ReadResult::~ReadResult() = default;
const bool error;
const bool request_to_install;
const ui::ScaleFactor scale_factor;
const bool resize_allowed;
const std::vector<std::string> unsafe_icon_data;
};
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
// ArcAppIcon::Source // ArcAppIcon::Source
...@@ -287,8 +280,7 @@ ArcAppIcon::ArcAppIcon(content::BrowserContext* context, ...@@ -287,8 +280,7 @@ ArcAppIcon::ArcAppIcon(content::BrowserContext* context,
} }
} }
ArcAppIcon::~ArcAppIcon() { ArcAppIcon::~ArcAppIcon() = default;
}
void ArcAppIcon::LoadSupportedScaleFactors() { void ArcAppIcon::LoadSupportedScaleFactors() {
switch (icon_type_) { switch (icon_type_) {
......
...@@ -65,12 +65,12 @@ class ArcAppIcon { ...@@ -65,12 +65,12 @@ class ArcAppIcon {
int resource_size_in_dip, int resource_size_in_dip,
Observer* observer, Observer* observer,
IconType icon_type = IconType::kUncompressed); IconType icon_type = IconType::kUncompressed);
~ArcAppIcon(); virtual ~ArcAppIcon();
// Starts loading the icon at every supported scale factor. The |observer_| // Starts loading the icon at every supported scale factor. The |observer_|
// will be notified as progress is made. "Supported" is in the same sense as // will be notified as progress is made. "Supported" is in the same sense as
// ui::GetSupportedScaleFactors(). // ui::GetSupportedScaleFactors().
void LoadSupportedScaleFactors(); virtual void LoadSupportedScaleFactors();
// Whether every supported scale factor was successfully loaded. "Supported" // Whether every supported scale factor was successfully loaded. "Supported"
// is in the same sense as ui::GetSupportedScaleFactors(). // is in the same sense as ui::GetSupportedScaleFactors().
...@@ -118,13 +118,21 @@ class ArcAppIcon { ...@@ -118,13 +118,21 @@ class ArcAppIcon {
static void DisableSafeDecodingForTesting(); static void DisableSafeDecodingForTesting();
static bool IsSafeDecodingDisabledForTesting(); static bool IsSafeDecodingDisabledForTesting();
private: protected:
friend class ArcAppIconLoader; struct ReadResult {
friend class apps::ArcIconOnceLoader; ReadResult(bool error,
bool request_to_install,
ui::ScaleFactor scale_factor,
bool resize_allowed,
std::vector<std::string> unsafe_icon_data);
~ReadResult();
class Source; const bool error;
class DecodeRequest; const bool request_to_install;
struct ReadResult; const ui::ScaleFactor scale_factor;
const bool resize_allowed;
const std::vector<std::string> unsafe_icon_data;
};
// Icon loading is performed in several steps. It is initiated by // Icon loading is performed in several steps. It is initiated by
// LoadImageForScaleFactor request that specifies a required scale factor. // LoadImageForScaleFactor request that specifies a required scale factor.
...@@ -141,7 +149,16 @@ class ArcAppIcon { ...@@ -141,7 +149,16 @@ class ArcAppIcon {
// install required resource from ARC side. ArcAppListPrefs notifies UI items // install required resource from ARC side. ArcAppListPrefs notifies UI items
// that new icon is available and corresponding item should invoke // that new icon is available and corresponding item should invoke
// LoadImageForScaleFactor again. // LoadImageForScaleFactor again.
void LoadForScaleFactor(ui::ScaleFactor scale_factor); virtual void LoadForScaleFactor(ui::ScaleFactor scale_factor);
virtual void OnIconRead(std::unique_ptr<ArcAppIcon::ReadResult> read_result);
private:
friend class ArcAppIconLoader;
friend class apps::ArcIconOnceLoader;
class Source;
class DecodeRequest;
void MaybeRequestIcon(ui::ScaleFactor scale_factor); void MaybeRequestIcon(ui::ScaleFactor scale_factor);
static std::unique_ptr<ArcAppIcon::ReadResult> ReadOnBackgroundThread( static std::unique_ptr<ArcAppIcon::ReadResult> ReadOnBackgroundThread(
...@@ -202,7 +219,6 @@ class ArcAppIcon { ...@@ -202,7 +219,6 @@ class ArcAppIcon {
bool resize_allowed, bool resize_allowed,
const base::FilePath& foreground_path, const base::FilePath& foreground_path,
const base::FilePath& background_path); const base::FilePath& background_path);
void OnIconRead(std::unique_ptr<ArcAppIcon::ReadResult> read_result);
void DecodeImage( void DecodeImage(
const std::string& unsafe_icon_data, const std::string& unsafe_icon_data,
const ArcAppIconDescriptor& descriptor, const ArcAppIconDescriptor& descriptor,
......
// 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 "chrome/browser/ui/app_list/arc/arc_app_icon_factory.h"
#include "content/public/browser/browser_context.h"
namespace arc {
ArcAppIconFactory::ArcAppIconFactory() = default;
ArcAppIconFactory::~ArcAppIconFactory() = default;
std::unique_ptr<ArcAppIcon> ArcAppIconFactory::CreateArcAppIcon(
content::BrowserContext* context,
const std::string& app_id,
int size_in_dip,
ArcAppIcon::Observer* observer,
ArcAppIcon::IconType icon_type) {
return std::make_unique<ArcAppIcon>(context, app_id, size_in_dip, observer,
icon_type);
}
} // namespace arc
// 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.
#ifndef CHROME_BROWSER_UI_APP_LIST_ARC_ARC_APP_ICON_FACTORY_H_
#define CHROME_BROWSER_UI_APP_LIST_ARC_ARC_APP_ICON_FACTORY_H_
#include <memory>
#include "chrome/browser/ui/app_list/arc/arc_app_icon.h"
namespace content {
class BrowserContext;
}
namespace arc {
class ArcAppIconFactory {
public:
ArcAppIconFactory();
virtual ~ArcAppIconFactory();
ArcAppIconFactory(const ArcAppIconFactory&) = delete;
ArcAppIconFactory& operator=(const ArcAppIconFactory&) = delete;
virtual std::unique_ptr<ArcAppIcon> CreateArcAppIcon(
content::BrowserContext* context,
const std::string& app_id,
int resource_size_in_dip,
ArcAppIcon::Observer* observer,
ArcAppIcon::IconType icon_type);
};
} // namespace arc
#endif // CHROME_BROWSER_UI_APP_LIST_ARC_ARC_APP_ICON_FACTORY_H_
...@@ -45,6 +45,7 @@ ...@@ -45,6 +45,7 @@
#include "chrome/browser/ui/app_list/app_service/app_service_app_item.h" #include "chrome/browser/ui/app_list/app_service/app_service_app_item.h"
#include "chrome/browser/ui/app_list/app_service/app_service_app_model_builder.h" #include "chrome/browser/ui/app_list/app_service/app_service_app_model_builder.h"
#include "chrome/browser/ui/app_list/arc/arc_app_icon.h" #include "chrome/browser/ui/app_list/arc/arc_app_icon.h"
#include "chrome/browser/ui/app_list/arc/arc_app_icon_factory.h"
#include "chrome/browser/ui/app_list/arc/arc_app_launcher.h" #include "chrome/browser/ui/app_list/arc/arc_app_launcher.h"
#include "chrome/browser/ui/app_list/arc/arc_app_list_prefs.h" #include "chrome/browser/ui/app_list/arc/arc_app_list_prefs.h"
#include "chrome/browser/ui/app_list/arc/arc_app_list_prefs_factory.h" #include "chrome/browser/ui/app_list/arc/arc_app_list_prefs_factory.h"
...@@ -85,6 +86,7 @@ ...@@ -85,6 +86,7 @@
#include "content/public/browser/browser_thread.h" #include "content/public/browser/browser_thread.h"
#include "content/public/test/test_utils.h" #include "content/public/test/test_utils.h"
#include "extensions/browser/extension_system.h" #include "extensions/browser/extension_system.h"
#include "extensions/common/constants.h"
#include "extensions/common/extension.h" #include "extensions/common/extension.h"
#include "extensions/common/manifest_constants.h" #include "extensions/common/manifest_constants.h"
#include "testing/gtest/include/gtest/gtest.h" #include "testing/gtest/include/gtest/gtest.h"
...@@ -101,6 +103,8 @@ constexpr char kFrameworkPackageName[] = "android"; ...@@ -101,6 +103,8 @@ constexpr char kFrameworkPackageName[] = "android";
constexpr int kFrameworkNycVersion = 25; constexpr int kFrameworkNycVersion = 25;
constexpr int kFrameworkPiVersion = 28; constexpr int kFrameworkPiVersion = 28;
constexpr size_t kMaxSimultaneousIconRequests = 250;
class FakeAppIconLoaderDelegate : public AppIconLoaderDelegate { class FakeAppIconLoaderDelegate : public AppIconLoaderDelegate {
public: public:
FakeAppIconLoaderDelegate() = default; FakeAppIconLoaderDelegate() = default;
...@@ -109,15 +113,45 @@ class FakeAppIconLoaderDelegate : public AppIconLoaderDelegate { ...@@ -109,15 +113,45 @@ class FakeAppIconLoaderDelegate : public AppIconLoaderDelegate {
delete; delete;
~FakeAppIconLoaderDelegate() override = default; ~FakeAppIconLoaderDelegate() override = default;
bool ValidateImageIsFullyLoaded(const gfx::ImageSkia& image) {
if (size_in_dip_ != image.width() || size_in_dip_ != image.height())
return false;
const std::vector<ui::ScaleFactor>& scale_factors =
ui::GetSupportedScaleFactors();
for (auto& scale_factor : scale_factors) {
const float scale = ui::GetScaleForScaleFactor(scale_factor);
if (!image.HasRepresentation(scale))
return false;
const gfx::ImageSkiaRep& representation = image.GetRepresentation(scale);
if (representation.is_null() ||
representation.pixel_width() !=
base::ClampCeil(size_in_dip_ * scale) ||
representation.pixel_height() !=
base::ClampCeil(size_in_dip_ * scale))
return false;
}
return true;
}
void OnAppImageUpdated(const std::string& app_id, void OnAppImageUpdated(const std::string& app_id,
const gfx::ImageSkia& image) override { const gfx::ImageSkia& image) override {
app_id_ = app_id; app_id_ = app_id;
image_ = image; image_ = image;
images_[app_id] = image;
++update_image_count_; ++update_image_count_;
if (update_image_count_ == expected_update_image_count_ && if (update_image_count_ == expected_update_image_count_ &&
!icon_updated_callback_.is_null()) { !icon_updated_callback_.is_null()) {
std::move(icon_updated_callback_).Run(); std::move(icon_updated_callback_).Run();
} }
if (icon_loaded_callback_) {
if (ValidateImageIsFullyLoaded(image))
app_ids_.erase(app_id);
if (app_ids_.empty())
std::move(icon_loaded_callback_).Run();
}
} }
void WaitForIconUpdates(size_t expected_updates) { void WaitForIconUpdates(size_t expected_updates) {
...@@ -127,18 +161,111 @@ class FakeAppIconLoaderDelegate : public AppIconLoaderDelegate { ...@@ -127,18 +161,111 @@ class FakeAppIconLoaderDelegate : public AppIconLoaderDelegate {
run_loop.Run(); run_loop.Run();
} }
void WaitForIconLoaded(std::vector<std::string>& app_ids, int size_in_dip) {
base::RunLoop run_loop;
for (const auto& app_id : app_ids)
app_ids_.insert(app_id);
size_in_dip_ = size_in_dip;
icon_loaded_callback_ = run_loop.QuitClosure();
run_loop.Run();
}
size_t update_image_count() const { return update_image_count_; } size_t update_image_count() const { return update_image_count_; }
const std::string& app_id() const { return app_id_; } const std::string& app_id() const { return app_id_; }
const gfx::ImageSkia& image() { return image_; } const gfx::ImageSkia& image() { return image_; }
std::map<std::string, gfx::ImageSkia>& images() { return images_; }
private: private:
size_t update_image_count_ = 0; size_t update_image_count_ = 0;
size_t expected_update_image_count_ = 0; size_t expected_update_image_count_ = 0;
std::string app_id_; std::string app_id_;
gfx::ImageSkia image_; gfx::ImageSkia image_;
std::map<std::string, gfx::ImageSkia> images_;
int size_in_dip_;
std::set<std::string> app_ids_;
base::OnceClosure icon_updated_callback_; base::OnceClosure icon_updated_callback_;
base::OnceClosure icon_loaded_callback_;
};
// FakeArcAppIcon is a sub class of ArcAppIcon, to record all ArcAppIcon load
// icon requests to calculate the max icon loading requests count.
class FakeArcAppIcon : public ArcAppIcon {
public:
FakeArcAppIcon(content::BrowserContext* context,
const std::string& app_id,
int size_in_dip,
ArcAppIcon::Observer* observer,
ArcAppIcon::IconType icon_type,
std::set<ArcAppIcon*>& arc_app_icon_requests,
size_t& max_arc_app_icon_request_count)
: ArcAppIcon(context, app_id, size_in_dip, observer, icon_type),
arc_app_icon_requests_(arc_app_icon_requests),
max_arc_app_icon_request_count_(max_arc_app_icon_request_count) {}
~FakeArcAppIcon() override = default;
FakeArcAppIcon(const FakeArcAppIcon&) = delete;
FakeArcAppIcon& operator=(const FakeArcAppIcon&) = delete;
void LoadSupportedScaleFactors() override {
arc_app_icon_requests_.insert(this);
if (max_arc_app_icon_request_count_ < arc_app_icon_requests_.size())
max_arc_app_icon_request_count_ = arc_app_icon_requests_.size();
ArcAppIcon::LoadSupportedScaleFactors();
}
private:
void LoadForScaleFactor(ui::ScaleFactor scale_factor) override {
arc_app_icon_requests_.insert(this);
if (max_arc_app_icon_request_count_ < arc_app_icon_requests_.size())
max_arc_app_icon_request_count_ = arc_app_icon_requests_.size();
ArcAppIcon::LoadForScaleFactor(scale_factor);
}
void OnIconRead(
std::unique_ptr<ArcAppIcon::ReadResult> read_result) override {
arc_app_icon_requests_.erase(this);
ArcAppIcon::OnIconRead(std::move(read_result));
}
std::set<ArcAppIcon*>& arc_app_icon_requests_;
size_t& max_arc_app_icon_request_count_;
};
// FakeArcAppIconFactory is a sub class of ArcAppIconFactory, to generate
// FakeArcAppIcon.
class FakeArcAppIconFactory : public arc::ArcAppIconFactory {
public:
FakeArcAppIconFactory(std::set<ArcAppIcon*>& arc_app_icon_requests,
size_t& max_arc_app_icon_request_count)
: arc::ArcAppIconFactory(),
arc_app_icon_requests_(arc_app_icon_requests),
max_arc_app_icon_request_count_(max_arc_app_icon_request_count) {}
~FakeArcAppIconFactory() override = default;
FakeArcAppIconFactory(const FakeArcAppIconFactory&) = delete;
FakeArcAppIconFactory& operator=(const FakeArcAppIconFactory&) = delete;
std::unique_ptr<ArcAppIcon> CreateArcAppIcon(
content::BrowserContext* context,
const std::string& app_id,
int size_in_dip,
ArcAppIcon::Observer* observer,
ArcAppIcon::IconType icon_type) override {
return std::make_unique<FakeArcAppIcon>(
context, app_id, size_in_dip, observer, icon_type,
arc_app_icon_requests_, max_arc_app_icon_request_count_);
}
private:
std::set<ArcAppIcon*>& arc_app_icon_requests_;
size_t& max_arc_app_icon_request_count_;
}; };
ArcAppIconDescriptor GetAppListIconDescriptor(ui::ScaleFactor scale_factor) { ArcAppIconDescriptor GetAppListIconDescriptor(ui::ScaleFactor scale_factor) {
...@@ -146,6 +273,11 @@ ArcAppIconDescriptor GetAppListIconDescriptor(ui::ScaleFactor scale_factor) { ...@@ -146,6 +273,11 @@ ArcAppIconDescriptor GetAppListIconDescriptor(ui::ScaleFactor scale_factor) {
ash::AppListConfig::instance().grid_icon_dimension(), scale_factor); ash::AppListConfig::instance().grid_icon_dimension(), scale_factor);
} }
ArcAppIconDescriptor GetAppListIconDescriptor(int dip_size,
ui::ScaleFactor scale_factor) {
return ArcAppIconDescriptor(dip_size, scale_factor);
}
bool IsIconCreated(ArcAppListPrefs* prefs, bool IsIconCreated(ArcAppListPrefs* prefs,
const std::string& app_id, const std::string& app_id,
ui::ScaleFactor scale_factor) { ui::ScaleFactor scale_factor) {
...@@ -155,9 +287,10 @@ bool IsIconCreated(ArcAppListPrefs* prefs, ...@@ -155,9 +287,10 @@ bool IsIconCreated(ArcAppListPrefs* prefs,
void WaitForIconCreation(ArcAppListPrefs* prefs, void WaitForIconCreation(ArcAppListPrefs* prefs,
const std::string& app_id, const std::string& app_id,
int dip_size,
ui::ScaleFactor scale_factor) { ui::ScaleFactor scale_factor) {
const base::FilePath icon_path = const base::FilePath icon_path = prefs->GetIconPath(
prefs->GetIconPath(app_id, GetAppListIconDescriptor(scale_factor)); app_id, GetAppListIconDescriptor(dip_size, scale_factor));
// Process pending tasks. This performs multiple thread hops, so we need // Process pending tasks. This performs multiple thread hops, so we need
// to run it continuously until it is resolved. // to run it continuously until it is resolved.
do { do {
...@@ -524,9 +657,7 @@ class ArcAppModelBuilderTest ...@@ -524,9 +657,7 @@ class ArcAppModelBuilderTest
} }
// Validates that provided image is acceptable as ARC app icon. // Validates that provided image is acceptable as ARC app icon.
void ValidateIcon(const gfx::ImageSkia& image) { void ValidateIcon(const gfx::ImageSkia& image, int icon_dimension) {
const int icon_dimension =
ash::AppListConfig::instance().grid_icon_dimension();
EXPECT_EQ(icon_dimension, image.width()); EXPECT_EQ(icon_dimension, image.width());
EXPECT_EQ(icon_dimension, image.height()); EXPECT_EQ(icon_dimension, image.height());
...@@ -811,14 +942,197 @@ class ArcAppModelIconTest : public ArcAppModelBuilderRecreate, ...@@ -811,14 +942,197 @@ class ArcAppModelIconTest : public ArcAppModelBuilderRecreate,
} }
} }
std::string InstallExtraPackage(int id) {
arc::mojom::AppInfo app;
app.name = base::StringPrintf("Fake App %d", id);
app.package_name = base::StringPrintf("fake.app.%d", id);
app.activity = base::StringPrintf("fake.app.%d.activity", id);
app.sticky = false;
app_instance()->SendAppAdded(app);
app_instance()->SendPackageAdded(arc::mojom::ArcPackageInfo::New(
base::StringPrintf("fake.package.%d", id) /* package_name */,
id /* package_version */, id /* last_backup_android_id */,
0 /* last_backup_time */, false /* sync */));
return ArcAppTest::GetAppId(app);
}
void LoadIconWithIconLoader(const std::string& app_id,
int update_count,
AppServiceAppIconLoader& icon_loader,
FakeAppIconLoaderDelegate& delegate) {
size_t current_update_count = delegate.update_image_count();
icon_loader.FetchImage(app_id);
EXPECT_EQ(current_update_count, delegate.update_image_count());
delegate.WaitForIconUpdates(update_count);
// Validate loaded image.
EXPECT_EQ(update_count + current_update_count,
delegate.update_image_count());
ValidateIcon(delegate.images()[app_id],
extension_misc::EXTENSION_ICON_MEDIUM);
// No more updates are expected.
base::RunLoop().RunUntilIdle();
EXPECT_EQ(update_count + current_update_count,
delegate.update_image_count());
}
void CreateFakeApps(int total_count, std::vector<std::string>& app_ids) {
FakeAppIconLoaderDelegate delegate;
AppServiceAppIconLoader icon_loader(
profile(), extension_misc::EXTENSION_ICON_MEDIUM, &delegate);
const std::vector<ui::ScaleFactor>& scale_factors =
ui::GetSupportedScaleFactors();
int current_count = 0;
while (current_count < total_count) {
// The id should start from 3 to avoid duplicate with the 3 existing fake
// icons.
std::string app_id = InstallExtraPackage(3 + current_count);
app_ids.emplace_back(app_id);
// Wait AppServiceAppItem to finish loading icon.
model_updater()->WaitForIconUpdates(scale_factors.size() + 1);
size_t index;
EXPECT_TRUE(model_updater()->FindItemIndexForTest(app_id, &index));
ChromeAppListItem* item = model_updater()->ItemAtForTest(index);
ASSERT_NE(nullptr, item);
ValidateIcon(item->icon(),
ash::AppListConfig::instance().grid_icon_dimension());
size_t update_count = model_updater()->update_image_count();
// No more updates are expected.
base::RunLoop().RunUntilIdle();
EXPECT_EQ(update_count, model_updater()->update_image_count());
LoadIconWithIconLoader(app_id, scale_factors.size() + 1, icon_loader,
delegate);
// There should be 2 more updates for model_updater(), because fetch
// the icon image for the size extension_misc::EXTENSION_ICON_MEDIUM.
size_t new_update_count = model_updater()->update_image_count();
if (new_update_count < update_count + 2) {
model_updater()->WaitForIconUpdates(update_count + 2 -
new_update_count);
}
current_count++;
}
}
void CreateFakeAppsWithBadIcons(int total_count,
int start_id,
std::vector<std::string>& app_ids) {
app_instance()->set_icon_response_type(
arc::FakeAppInstance::IconResponseType::ICON_RESPONSE_SEND_BAD);
FakeAppIconLoaderDelegate delegate;
AppServiceAppIconLoader icon_loader(
profile(), extension_misc::EXTENSION_ICON_MEDIUM, &delegate);
const std::vector<ui::ScaleFactor>& scale_factors =
ui::GetSupportedScaleFactors();
int current_count = 0;
while (current_count < total_count) {
std::string app_id = InstallExtraPackage(start_id + current_count);
app_ids.emplace_back(app_id);
icon_loader.FetchImage(app_id);
// Wait AppServiceAppItem to generate the bad icon image files.
for (auto& scale_factor : scale_factors) {
// Force the icon to be loaded.
WaitForIconCreation(
ArcAppListPrefs::Get(profile()), app_id,
ash::AppListConfig::instance().grid_icon_dimension(), scale_factor);
}
// Wait AppServiceAppIconLoader to generate the bad icon image files.
for (auto& scale_factor : scale_factors) {
// Force the icon to be loaded.
WaitForIconCreation(ArcAppListPrefs::Get(profile()), app_id,
extension_misc::EXTENSION_ICON_MEDIUM,
scale_factor);
}
current_count++;
}
}
void LoadIconsWithIconLoader(std::vector<std::string>& app_ids,
int update_count) {
FakeAppIconLoaderDelegate delegate;
AppServiceAppIconLoader icon_loader(
profile(), extension_misc::EXTENSION_ICON_MEDIUM, &delegate);
EXPECT_EQ(0UL, delegate.update_image_count());
for (auto& app_id : app_ids) {
icon_loader.FetchImage(app_id);
}
EXPECT_EQ(0UL, delegate.update_image_count());
delegate.WaitForIconUpdates(update_count);
// Validate loaded image.
EXPECT_EQ(update_count, delegate.update_image_count());
for (const auto& app_id : app_ids) {
ValidateIcon(delegate.images()[app_id],
extension_misc::EXTENSION_ICON_MEDIUM);
}
// No more updates are expected.
base::RunLoop().RunUntilIdle();
EXPECT_EQ(update_count, delegate.update_image_count());
}
void RemoveAppsFromIconLoader(std::vector<std::string>& app_ids) {
apps::ArcIconOnceLoader& arc_icon_once_loader =
apps::ArcAppsFactory::GetForProfile(profile())
->GetArcIconOnceLoaderForTesting();
for (const auto& app_id : app_ids) {
arc_icon_once_loader.OnAppRemoved(app_id);
}
// Update the icon key to fetch the new icon and avoid icon catch,
apps_util::IncrementingIconKeyFactory icon_key_factory;
std::vector<apps::mojom::AppPtr> apps;
for (const auto& app_id : app_ids) {
apps::mojom::AppPtr app = apps::mojom::App::New();
app->app_type = apps::mojom::AppType::kArc;
app->app_id = app_id;
app->icon_key = icon_key_factory.MakeIconKey(apps::IconEffects::kNone);
apps.push_back(app.Clone());
}
apps::AppServiceProxyFactory::GetForProfile(profile())
->AppRegistryCache()
.OnApps(std::move(apps));
}
// Set FakeArcAppIconFactory to use FakeArcAppIcon for Arc app icon loading to
// calculate the arc app icon requests number.
void SetFakeArcAppIconFactory() {
apps::ArcAppsFactory::GetForProfile(profile())
->GetArcIconOnceLoaderForTesting()
.SetArcAppIconFactoryForTesting(std::make_unique<FakeArcAppIconFactory>(
arc_app_icon_requests_, max_arc_app_icon_request_count_));
}
arc::mojom::AppInfo test_app() const { return fake_apps()[0]; } arc::mojom::AppInfo test_app() const { return fake_apps()[0]; }
size_t max_arc_app_icon_request_count() {
return max_arc_app_icon_request_count_;
}
private: private:
std::unique_ptr<ui::test::ScopedSetSupportedScaleFactors> std::unique_ptr<ui::test::ScopedSetSupportedScaleFactors>
scoped_supported_scale_factors_; scoped_supported_scale_factors_;
std::unique_ptr<base::RunLoop> run_loop_; std::unique_ptr<base::RunLoop> run_loop_;
base::OnceClosure icon_update_callback_; base::OnceClosure icon_update_callback_;
int icon_updated_count_; int icon_updated_count_;
std::set<ArcAppIcon*> arc_app_icon_requests_;
size_t max_arc_app_icon_request_count_ = 0;
}; };
class ArcDefaultAppTest : public ArcAppModelBuilderRecreate { class ArcDefaultAppTest : public ArcAppModelBuilderRecreate {
...@@ -2205,7 +2519,9 @@ TEST_P(ArcAppModelBuilderTest, IconLoaderWithBadIcon) { ...@@ -2205,7 +2519,9 @@ TEST_P(ArcAppModelBuilderTest, IconLoaderWithBadIcon) {
// Force the icon to be loaded. // Force the icon to be loaded.
app_item->icon().GetRepresentation( app_item->icon().GetRepresentation(
ui::GetScaleForScaleFactor(scale_factor)); ui::GetScaleForScaleFactor(scale_factor));
WaitForIconCreation(prefs, app_id, scale_factor); WaitForIconCreation(prefs, app_id,
ash::AppListConfig::instance().grid_icon_dimension(),
scale_factor);
} }
// After clear request record related to |app_id|, when bad icon is installed, // After clear request record related to |app_id|, when bad icon is installed,
...@@ -2260,13 +2576,83 @@ TEST_P(ArcAppModelBuilderTest, IconLoader) { ...@@ -2260,13 +2576,83 @@ TEST_P(ArcAppModelBuilderTest, IconLoader) {
// Validate loaded image. // Validate loaded image.
EXPECT_EQ(1UL, delegate.update_image_count()); EXPECT_EQ(1UL, delegate.update_image_count());
EXPECT_EQ(app_id, delegate.app_id()); EXPECT_EQ(app_id, delegate.app_id());
ValidateIcon(delegate.image()); ValidateIcon(delegate.image(),
ash::AppListConfig::instance().grid_icon_dimension());
// No more updates are expected. // No more updates are expected.
base::RunLoop().RunUntilIdle(); base::RunLoop().RunUntilIdle();
EXPECT_EQ(1UL, delegate.update_image_count()); EXPECT_EQ(1UL, delegate.update_image_count());
} }
TEST_P(ArcAppModelIconTest, LoadManyIcons) {
if (!base::FeatureList::IsEnabled(features::kAppServiceAdaptiveIcon))
return;
// Remove ARC apps installed as default to avoid test flaky due to update
// those default app icons.
RemoveArcApps(profile(), model_updater());
ArcAppListPrefs* prefs = ArcAppListPrefs::Get(profile_.get());
ASSERT_NE(nullptr, prefs);
SetFakeArcAppIconFactory();
int app_count = 500;
std::vector<std::string> app_ids;
CreateFakeApps(app_count, app_ids);
// Remove ArcAppIcon cache From apps::ArcIconOnceLoader to re-load all icons
// to simulate the system reboot case.
RemoveAppsFromIconLoader(app_ids);
LoadIconsWithIconLoader(app_ids, app_count);
EXPECT_GE(kMaxSimultaneousIconRequests, max_arc_app_icon_request_count());
}
TEST_P(ArcAppModelIconTest, LoadManyIconsWithSomeBadIcons) {
if (!base::FeatureList::IsEnabled(features::kAppServiceAdaptiveIcon))
return;
// Remove ARC apps installed as default to avoid test flaky due to update
// those default app icons.
RemoveArcApps(profile(), model_updater());
ArcAppListPrefs* prefs = ArcAppListPrefs::Get(profile_.get());
ASSERT_NE(nullptr, prefs);
SetFakeArcAppIconFactory();
int app_count = 500;
std::vector<std::string> app_ids;
CreateFakeApps(app_count, app_ids);
int bad_icon_app_count = 10;
// The app_id should start at the end, after 3 fake apps, and |app_count|
// apps.
CreateFakeAppsWithBadIcons(bad_icon_app_count, fake_apps().size() + app_count,
app_ids);
// Remove ArcAppIcon cache From apps::ArcIconOnceLoader to re-load all icons
// to simulate the system reboot case.
RemoveAppsFromIconLoader(app_ids);
app_instance()->set_icon_response_type(
arc::FakeAppInstance::IconResponseType::ICON_RESPONSE_SEND_GOOD);
for (int i = app_count; i < app_ids.size(); i++)
MaybeRemoveIconRequestRecord(app_ids[i]);
FakeAppIconLoaderDelegate delegate;
AppServiceAppIconLoader icon_loader(
profile(), extension_misc::EXTENSION_ICON_MEDIUM, &delegate);
for (auto& app_id : app_ids)
icon_loader.FetchImage(app_id);
delegate.WaitForIconLoaded(app_ids, extension_misc::EXTENSION_ICON_MEDIUM);
EXPECT_GE(kMaxSimultaneousIconRequests, max_arc_app_icon_request_count());
}
TEST_P(ArcAppModelBuilderTest, IconLoaderCompressed) { TEST_P(ArcAppModelBuilderTest, IconLoaderCompressed) {
const arc::mojom::AppInfo& app = fake_apps()[0]; const arc::mojom::AppInfo& app = fake_apps()[0];
const std::string app_id = ArcAppTest::GetAppId(app); const std::string app_id = ArcAppTest::GetAppId(app);
......
...@@ -74,6 +74,8 @@ class FakeAppListModelUpdater : public AppListModelUpdater { ...@@ -74,6 +74,8 @@ class FakeAppListModelUpdater : public AppListModelUpdater {
void WaitForIconUpdates(size_t expected_updates); void WaitForIconUpdates(size_t expected_updates);
size_t update_image_count() const { return update_image_count_; }
private: private:
bool search_engine_is_google_ = false; bool search_engine_is_google_ = false;
std::vector<std::unique_ptr<ChromeAppListItem>> items_; std::vector<std::unique_ptr<ChromeAppListItem>> items_;
......
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