blob: 19c4370406f1b0262a5cb268fc7dff235fbe0647 [file] [log] [blame]
// Copyright 2014 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 <stddef.h>
#include <vector>
#include "ash/public/interfaces/wallpaper.mojom.h"
#include "base/bind.h"
#include "base/command_line.h"
#include "base/macros.h"
#include "base/run_loop.h"
#include "base/strings/stringprintf.h"
#include "base/test/bind_test_util.h"
#include "base/time/time.h"
#include "chrome/browser/chromeos/customization/customization_document.h"
#include "chrome/browser/chromeos/customization/customization_wallpaper_downloader.h"
#include "chrome/browser/ui/ash/wallpaper_controller_client.h"
#include "chrome/test/base/in_process_browser_test.h"
#include "chrome/test/base/testing_browser_process.h"
#include "chromeos/constants/chromeos_switches.h"
#include "mojo/public/cpp/bindings/associated_binding.h"
#include "net/http/http_response_headers.h"
#include "net/http/http_status_code.h"
#include "net/test/embedded_test_server/http_response.h"
#include "net/url_request/test_url_fetcher_factory.h"
#include "net/url_request/url_fetcher_impl.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "ui/gfx/codec/jpeg_codec.h"
namespace chromeos {
namespace {
constexpr char kOEMWallpaperRelativeURL[] = "/image.png";
constexpr char kServicesManifest[] =
"{"
" \"version\": \"1.0\","
" \"default_wallpaper\": \"\%s\",\n"
" \"default_apps\": [\n"
" \"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\",\n"
" \"bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb\"\n"
" ],\n"
" \"localized_content\": {\n"
" \"en-US\": {\n"
" \"default_apps_folder_name\": \"EN-US OEM Name\"\n"
" },\n"
" \"en\": {\n"
" \"default_apps_folder_name\": \"EN OEM Name\"\n"
" },\n"
" \"default\": {\n"
" \"default_apps_folder_name\": \"Default OEM Name\"\n"
" }\n"
" }\n"
"}";
// Expected minimal wallpaper download retry interval in milliseconds.
constexpr int kDownloadRetryIntervalMS = 100;
// Dimension used for width and height of default wallpaper images. A small
// value is used to minimize the amount of time spent compressing and writing
// images.
constexpr int kWallpaperSize = 2;
constexpr SkColor kCustomizedDefaultWallpaperColor = SK_ColorDKGRAY;
std::string ManifestForURL(const std::string& url) {
return base::StringPrintf(kServicesManifest, url.c_str());
}
// Returns true if the color at the center of |image| is close to
// |expected_color|. (The center is used so small wallpaper images can be
// used.)
bool ImageIsNearColor(gfx::ImageSkia image, SkColor expected_color) {
if (image.size().IsEmpty()) {
LOG(ERROR) << "Image is empty";
return false;
}
const SkBitmap* bitmap = image.bitmap();
if (!bitmap) {
LOG(ERROR) << "Unable to get bitmap from image";
return false;
}
gfx::Point center = gfx::Rect(image.size()).CenterPoint();
SkColor image_color = bitmap->getColor(center.x(), center.y());
const int kDiff = 3;
if (std::abs(static_cast<int>(SkColorGetA(image_color)) -
static_cast<int>(SkColorGetA(expected_color))) > kDiff ||
std::abs(static_cast<int>(SkColorGetR(image_color)) -
static_cast<int>(SkColorGetR(expected_color))) > kDiff ||
std::abs(static_cast<int>(SkColorGetG(image_color)) -
static_cast<int>(SkColorGetG(expected_color))) > kDiff ||
std::abs(static_cast<int>(SkColorGetB(image_color)) -
static_cast<int>(SkColorGetB(expected_color))) > kDiff) {
LOG(ERROR) << "Expected color near 0x" << std::hex << expected_color
<< " but got 0x" << image_color;
return false;
}
return true;
}
// Creates compressed JPEG image of solid color. Result bytes are written to
// |output|. Returns true on success.
bool CreateJPEGImage(int width,
int height,
SkColor color,
std::vector<unsigned char>* output) {
SkBitmap bitmap;
bitmap.allocN32Pixels(width, height);
bitmap.eraseColor(color);
if (!gfx::JPEGCodec::Encode(bitmap, 80 /*quality=*/, output)) {
LOG(ERROR) << "Unable to encode " << width << "x" << height << " bitmap";
return false;
}
return true;
}
class TestWallpaperObserver : public ash::mojom::WallpaperObserver {
public:
TestWallpaperObserver() : finished_(false), observer_binding_(this) {
ash::mojom::WallpaperObserverAssociatedPtrInfo ptr_info;
observer_binding_.Bind(mojo::MakeRequest(&ptr_info));
WallpaperControllerClient::Get()->AddObserver(std::move(ptr_info));
}
~TestWallpaperObserver() override = default;
// ash::mojom::WallpaperObserver:
void OnWallpaperChanged(uint32_t image_id) override {
finished_ = true;
base::RunLoop::QuitCurrentWhenIdleDeprecated();
}
void OnWallpaperColorsChanged(
const std::vector<SkColor>& prominent_colors) override {}
void OnWallpaperBlurChanged(bool blurred) override {}
// Wait until the wallpaper update is completed.
void WaitForWallpaperChanged() {
while (!finished_)
base::RunLoop().Run();
}
void Reset() { finished_ = false; }
private:
bool finished_;
// The binding this instance uses to implement ash::mojom::WallpaperObserver.
mojo::AssociatedBinding<ash::mojom::WallpaperObserver> observer_binding_;
DISALLOW_COPY_AND_ASSIGN(TestWallpaperObserver);
};
} // namespace
class CustomizationWallpaperDownloaderBrowserTest
: public InProcessBrowserTest {
public:
CustomizationWallpaperDownloaderBrowserTest() {}
~CustomizationWallpaperDownloaderBrowserTest() override {}
// InProcessBrowserTest overrides:
void SetUpOnMainThread() override {
InProcessBrowserTest::SetUpOnMainThread();
std::vector<unsigned char> oem_wallpaper;
ASSERT_TRUE(CreateJPEGImage(kWallpaperSize, kWallpaperSize,
kCustomizedDefaultWallpaperColor,
&oem_wallpaper));
jpeg_data_.resize(oem_wallpaper.size());
std::copy(oem_wallpaper.begin(), oem_wallpaper.end(), jpeg_data_.begin());
// Set up the test server.
embedded_test_server()->RegisterRequestHandler(base::BindRepeating(
&CustomizationWallpaperDownloaderBrowserTest::HandleRequest,
base::Unretained(this)));
ASSERT_TRUE(embedded_test_server()->Start());
}
void SetUpCommandLine(base::CommandLine* command_line) override {
command_line->AppendSwitch(chromeos::switches::kLoginManager);
command_line->AppendSwitchASCII(chromeos::switches::kLoginProfile, "user");
}
void SetRequiredRetries(size_t retries) { required_retries_ = retries; }
size_t num_attempts() const { return attempts_.size(); }
private:
std::unique_ptr<net::test_server::HttpResponse> HandleRequest(
const net::test_server::HttpRequest& request) {
chromeos::ServicesCustomizationDocument* customization =
chromeos::ServicesCustomizationDocument::GetInstance();
customization->wallpaper_downloader_for_testing()
->set_retry_delay_for_testing(
base::TimeDelta::FromMilliseconds(kDownloadRetryIntervalMS));
attempts_.push_back(base::TimeTicks::Now());
if (attempts_.size() > 1) {
const int retry = num_attempts() - 1;
const base::TimeDelta current_delay =
customization->wallpaper_downloader_for_testing()
->retry_current_delay_for_testing();
const double base_interval = base::TimeDelta::FromMilliseconds(
kDownloadRetryIntervalMS).InSecondsF();
EXPECT_GE(current_delay,
base::TimeDelta::FromSecondsD(base_interval * retry * retry))
<< "Retry too fast. Actual interval " << current_delay.InSecondsF()
<< " seconds, but expected at least " << base_interval
<< " * (retry=" << retry
<< " * retry)= " << base_interval * retry * retry << " seconds.";
}
if (attempts_.size() > required_retries_) {
std::unique_ptr<net::test_server::BasicHttpResponse> response =
std::make_unique<net::test_server::BasicHttpResponse>();
response->set_content_type("image/jpeg");
response->set_code(net::HTTP_OK);
response->set_content(jpeg_data_);
return std::move(response);
}
return nullptr;
}
// Sample Wallpaper content.
std::string jpeg_data_;
// Number of loads performed.
std::vector<base::TimeTicks> attempts_;
// Number of retries required.
size_t required_retries_ = 0;
DISALLOW_COPY_AND_ASSIGN(CustomizationWallpaperDownloaderBrowserTest);
};
IN_PROC_BROWSER_TEST_F(CustomizationWallpaperDownloaderBrowserTest,
OEMWallpaperIsPresent) {
TestWallpaperObserver observer;
// Show a built-in default wallpaper first.
WallpaperControllerClient::Get()->ShowSigninWallpaper();
observer.WaitForWallpaperChanged();
observer.Reset();
// Set the number of required retries.
SetRequiredRetries(0);
// Start fetching the customized default wallpaper.
GURL url = embedded_test_server()->GetURL(kOEMWallpaperRelativeURL);
chromeos::ServicesCustomizationDocument* customization =
chromeos::ServicesCustomizationDocument::GetInstance();
EXPECT_TRUE(
customization->LoadManifestFromString(ManifestForURL(url.spec())));
observer.WaitForWallpaperChanged();
observer.Reset();
// Verify the customized default wallpaper has replaced the built-in default
// wallpaper.
base::RunLoop run_loop;
WallpaperControllerClient::Get()->GetWallpaperImage(
base::BindLambdaForTesting([&run_loop](const gfx::ImageSkia& image) {
run_loop.Quit();
EXPECT_TRUE(ImageIsNearColor(image, kCustomizedDefaultWallpaperColor));
}));
run_loop.Run();
EXPECT_EQ(1U, num_attempts());
}
IN_PROC_BROWSER_TEST_F(CustomizationWallpaperDownloaderBrowserTest,
OEMWallpaperRetryFetch) {
TestWallpaperObserver observer;
// Show a built-in default wallpaper.
WallpaperControllerClient::Get()->ShowSigninWallpaper();
observer.WaitForWallpaperChanged();
observer.Reset();
// Set the number of required retries.
SetRequiredRetries(1);
// Start fetching the customized default wallpaper.
GURL url = embedded_test_server()->GetURL(kOEMWallpaperRelativeURL);
chromeos::ServicesCustomizationDocument* customization =
chromeos::ServicesCustomizationDocument::GetInstance();
EXPECT_TRUE(
customization->LoadManifestFromString(ManifestForURL(url.spec())));
observer.WaitForWallpaperChanged();
observer.Reset();
// Verify the customized default wallpaper has replaced the built-in default
// wallpaper.
base::RunLoop run_loop;
WallpaperControllerClient::Get()->GetWallpaperImage(
base::BindLambdaForTesting([&run_loop](const gfx::ImageSkia& image) {
run_loop.Quit();
EXPECT_TRUE(ImageIsNearColor(image, kCustomizedDefaultWallpaperColor));
}));
run_loop.Run();
EXPECT_EQ(2U, num_attempts());
}
} // namespace chromeos