Add browser test for CustomizationWallpaperDownloader.

Also implements TestURLFetcher::SaveResponseToTemporaryFile(),
and explicitly allows ScopedIO in TestURLFetcher, as it doesn't support
asynchronous IO by design.

Note to tree sheriffs: this CL can break tests using FakeURLFetcherFactory.

BUG=366614
TEST=none

Review URL: https://codereview.chromium.org/253833006

git-svn-id: svn://svn.chromium.org/chrome/trunk/src@269907 0039d316-1c4b-4281-b951-d872f2087c98
diff --git a/PRESUBMIT.py b/PRESUBMIT.py
index e5bbf5b..b3d3d150 100644
--- a/PRESUBMIT.py
+++ b/PRESUBMIT.py
@@ -170,6 +170,7 @@
         r"^content[\\\/]shell[\\\/]browser[\\\/]shell_message_filter\.cc$",
         r"^mojo[\\\/]system[\\\/]raw_shared_buffer_posix\.cc$",
         r"^net[\\\/]disk_cache[\\\/]cache_util\.cc$",
+        r"^net[\\\/]url_request[\\\/]test_url_fetcher_factory\.cc$",
       ),
     ),
     (
diff --git a/chrome/browser/chromeos/customization_document.h b/chrome/browser/chromeos/customization_document.h
index 97e166d8..ad1b1f6c 100644
--- a/chrome/browser/chromeos/customization_document.h
+++ b/chrome/browser/chromeos/customization_document.h
@@ -194,8 +194,16 @@
   static base::FilePath GetCustomizedWallpaperCacheDir();
   static base::FilePath GetCustomizedWallpaperDownloadedFileName();
 
+  CustomizationWallpaperDownloader* wallpaper_downloader_for_testing() {
+    return wallpaper_downloader_.get();
+  }
+
  private:
   friend struct DefaultSingletonTraits<ServicesCustomizationDocument>;
+  FRIEND_TEST_ALL_PREFIXES(CustomizationWallpaperDownloaderBrowserTest,
+                           OEMWallpaperIsPresent);
+  FRIEND_TEST_ALL_PREFIXES(CustomizationWallpaperDownloaderBrowserTest,
+                           OEMWallpaperRetryFetch);
 
   typedef std::vector<base::WeakPtr<ServicesCustomizationExternalLoader> >
       ExternalLoaders;
diff --git a/chrome/browser/chromeos/customization_wallpaper_downloader.cc b/chrome/browser/chromeos/customization_wallpaper_downloader.cc
index d672945a..da7d0ee 100644
--- a/chrome/browser/chromeos/customization_wallpaper_downloader.cc
+++ b/chrome/browser/chromeos/customization_wallpaper_downloader.cc
@@ -72,6 +72,7 @@
       wallpaper_temporary_file_(wallpaper_downloaded_file.value() +
                                 kTemporarySuffix),
       retries_(0),
+      retry_delay_(base::TimeDelta::FromSeconds(kRetrySleepSeconds)),
       on_wallpaper_fetch_completed_(on_wallpaper_fetch_completed),
       weak_factory_(this) {
   DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
@@ -105,14 +106,14 @@
   DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
   ++retries_;
 
-  const double delay_seconds =
-      std::min(kMaxRetrySleepSeconds,
-               static_cast<double>(retries_) * retries_ * kRetrySleepSeconds);
-  const base::TimeDelta delay =
-      base::TimeDelta::FromSeconds(lround(delay_seconds));
+  const double delay_seconds = std::min(
+      kMaxRetrySleepSeconds,
+      static_cast<double>(retries_) * retries_ * retry_delay_.InSecondsF());
+  const base::TimeDelta delay = base::TimeDelta::FromSecondsD(delay_seconds);
 
   VLOG(1) << "Schedule Customized Wallpaper download in " << delay.InSecondsF()
           << " seconds (retry = " << retries_ << ").";
+  retry_current_delay_ = delay;
   request_scheduled_.Start(
       FROM_HERE, delay, this, &CustomizationWallpaperDownloader::StartRequest);
 }
diff --git a/chrome/browser/chromeos/customization_wallpaper_downloader.h b/chrome/browser/chromeos/customization_wallpaper_downloader.h
index 9922ffb..b1d9036 100644
--- a/chrome/browser/chromeos/customization_wallpaper_downloader.h
+++ b/chrome/browser/chromeos/customization_wallpaper_downloader.h
@@ -52,6 +52,15 @@
   // net::URLFetcherDelegate
   virtual void OnURLFetchComplete(const net::URLFetcher* source) OVERRIDE;
 
+  // This is called in tests to modify (lower) retry delay.
+  void set_retry_delay_for_testing(base::TimeDelta value) {
+    retry_delay_ = value;
+  }
+
+  base::TimeDelta retry_current_delay_for_testing() const {
+    return retry_current_delay_;
+  }
+
  private:
   // Start new request.
   void StartRequest();
@@ -89,6 +98,13 @@
   // Number of download retries (first attempt is not counted as retry).
   size_t retries_;
 
+  // Sleep between retry requests (increasing, see Retry() method for details).
+  // Non-constant value for tests.
+  base::TimeDelta retry_delay_;
+
+  // Retry delay of the last attempt. For testing only.
+  base::TimeDelta retry_current_delay_;
+
   // Callback supplied by caller.
   base::Callback<void(bool success, const GURL&)> on_wallpaper_fetch_completed_;
 
diff --git a/chrome/browser/chromeos/customization_wallpaper_downloader_browsertest.cc b/chrome/browser/chromeos/customization_wallpaper_downloader_browsertest.cc
new file mode 100644
index 0000000..7bcb88d
--- /dev/null
+++ b/chrome/browser/chromeos/customization_wallpaper_downloader_browsertest.cc
@@ -0,0 +1,320 @@
+// 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 <vector>
+
+#include "ash/desktop_background/desktop_background_controller.h"
+#include "ash/shell.h"
+#include "base/command_line.h"
+#include "base/files/scoped_temp_dir.h"
+#include "base/run_loop.h"
+#include "base/time/time.h"
+#include "chrome/browser/chromeos/customization_document.h"
+#include "chrome/browser/chromeos/customization_wallpaper_downloader.h"
+#include "chrome/browser/chromeos/login/wallpaper_manager.h"
+#include "chrome/browser/chromeos/login/wallpaper_manager_test_utils.h"
+#include "chrome/browser/google/google_url_tracker.h"
+#include "chrome/test/base/in_process_browser_test.h"
+#include "chrome/test/base/testing_browser_process.h"
+#include "chromeos/chromeos_switches.h"
+#include "net/http/http_response_headers.h"
+#include "net/http/http_status_code.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"
+
+namespace chromeos {
+
+namespace {
+
+const char kOEMWallpaperURL[] = "http://somedomain.com/image.png";
+
+const char kServicesManifest[] =
+    "{"
+    "  \"version\": \"1.0\","
+    "  \"default_wallpaper\": \"http://somedomain.com/image.png\",\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.
+const int kDownloadRetryIntervalMS = 100;
+
+class TestWallpaperObserver : public WallpaperManager::Observer {
+ public:
+  explicit TestWallpaperObserver(WallpaperManager* wallpaper_manager)
+      : finished_(false),
+        wallpaper_manager_(wallpaper_manager) {
+    DCHECK(wallpaper_manager_);
+    wallpaper_manager_->AddObserver(this);
+  }
+
+  virtual ~TestWallpaperObserver() {
+    wallpaper_manager_->RemoveObserver(this);
+  }
+
+  virtual void OnWallpaperAnimationFinished(const std::string&) OVERRIDE {
+    finished_ = true;
+    base::MessageLoop::current()->Quit();
+  }
+
+  void WaitForWallpaperAnimationFinished() {
+    while (!finished_)
+      base::RunLoop().Run();
+  }
+
+ private:
+  bool finished_;
+  WallpaperManager* wallpaper_manager_;
+
+  DISALLOW_COPY_AND_ASSIGN(TestWallpaperObserver);
+};
+
+}  // namespace
+
+// This is helper class for net::FakeURLFetcherFactory.
+class TestWallpaperImageURLFetcherCallback {
+ public:
+  TestWallpaperImageURLFetcherCallback(
+      const GURL& url,
+      const size_t require_retries,
+      const std::vector<unsigned char>& jpeg_data_raw)
+      : url_(url),
+        require_retries_(require_retries),
+        factory_(NULL) {
+    jpeg_data_.resize(jpeg_data_raw.size());
+    std::copy(jpeg_data_raw.begin(), jpeg_data_raw.end(), jpeg_data_.begin());
+  }
+
+  scoped_ptr<net::FakeURLFetcher> CreateURLFetcher(
+      const GURL& url,
+      net::URLFetcherDelegate* delegate,
+      const std::string& response_data,
+      net::HttpStatusCode response_code,
+      net::URLRequestStatus::Status status) {
+    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() > require_retries_) {
+      response_code = net::HTTP_OK;
+      status = net::URLRequestStatus::SUCCESS;
+      factory_->SetFakeResponse(url, response_data, response_code, status);
+    }
+    scoped_ptr<net::FakeURLFetcher> fetcher(new net::FakeURLFetcher(
+        url, delegate, response_data, response_code, status));
+    scoped_refptr<net::HttpResponseHeaders> download_headers =
+        new net::HttpResponseHeaders(std::string());
+    download_headers->AddHeader("Content-Type: image/jpeg");
+    fetcher->set_response_headers(download_headers);
+    return fetcher.Pass();
+  }
+
+  void Initialize(net::FakeURLFetcherFactory* factory) {
+    factory_ = factory;
+    factory_->SetFakeResponse(url_,
+                              jpeg_data_,
+                              net::HTTP_INTERNAL_SERVER_ERROR,
+                              net::URLRequestStatus::FAILED);
+  }
+
+  size_t num_attempts() const { return attempts_.size(); }
+
+ private:
+  const GURL url_;
+  // Respond with OK on required retry attempt.
+  const size_t require_retries_;
+  net::FakeURLFetcherFactory* factory_;
+  std::vector<base::TimeTicks> attempts_;
+  std::string jpeg_data_;
+
+  DISALLOW_COPY_AND_ASSIGN(TestWallpaperImageURLFetcherCallback);
+};
+
+// This implements fake remote source for wallpaper image.
+// JPEG image is created here and served to CustomizationWallpaperDownloader
+// via net::FakeURLFetcher.
+class WallpaperImageFetcherFactory {
+ public:
+  WallpaperImageFetcherFactory(const GURL& url,
+                               int width,
+                               int height,
+                               SkColor color,
+                               const size_t require_retries) {
+    // ASSERT_TRUE() cannot be directly used in constructor.
+    Initialize(url, width, height, color, require_retries);
+  }
+
+  ~WallpaperImageFetcherFactory() {
+    fetcher_factory_.reset();
+    net::URLFetcherImpl::set_factory(fallback_fetcher_factory_.get());
+    fallback_fetcher_factory_.reset();
+  }
+
+  size_t num_attempts() const { return url_callback_->num_attempts(); }
+
+ private:
+  void Initialize(const GURL& url,
+                  int width,
+                  int height,
+                  SkColor color,
+                  const size_t require_retries) {
+    std::vector<unsigned char> oem_wallpaper_;
+    ASSERT_TRUE(wallpaper_manager_test_utils::CreateJPEGImage(
+        width, height, color, &oem_wallpaper_));
+
+    url_callback_.reset(new TestWallpaperImageURLFetcherCallback(
+        url, require_retries, oem_wallpaper_));
+    fallback_fetcher_factory_.reset(new net::TestURLFetcherFactory);
+    net::URLFetcherImpl::set_factory(NULL);
+    fetcher_factory_.reset(new net::FakeURLFetcherFactory(
+        fallback_fetcher_factory_.get(),
+        base::Bind(&TestWallpaperImageURLFetcherCallback::CreateURLFetcher,
+                   base::Unretained(url_callback_.get()))));
+    url_callback_->Initialize(fetcher_factory_.get());
+  }
+
+  scoped_ptr<TestWallpaperImageURLFetcherCallback> url_callback_;
+
+  // Use a test factory as a fallback so we don't have to deal with other
+  // requests.
+  scoped_ptr<net::TestURLFetcherFactory> fallback_fetcher_factory_;
+  scoped_ptr<net::FakeURLFetcherFactory> fetcher_factory_;
+
+  DISALLOW_COPY_AND_ASSIGN(WallpaperImageFetcherFactory);
+};
+
+class CustomizationWallpaperDownloaderBrowserTest
+    : public InProcessBrowserTest {
+ public:
+  CustomizationWallpaperDownloaderBrowserTest()
+      : controller_(NULL),
+        local_state_(NULL) {
+  }
+
+  virtual ~CustomizationWallpaperDownloaderBrowserTest() {}
+
+  virtual void SetUpOnMainThread() OVERRIDE {
+    controller_ = ash::Shell::GetInstance()->desktop_background_controller();
+    local_state_ = g_browser_process->local_state();
+  }
+
+  virtual void SetUpCommandLine(CommandLine* command_line) OVERRIDE {
+    command_line->AppendSwitch(chromeos::switches::kLoginManager);
+    command_line->AppendSwitchASCII(chromeos::switches::kLoginProfile, "user");
+  }
+
+  virtual void CleanUpOnMainThread() OVERRIDE { controller_ = NULL; }
+
+ protected:
+  void CreateCmdlineWallpapers() {
+    cmdline_wallpaper_dir_.reset(new base::ScopedTempDir);
+    ASSERT_TRUE(cmdline_wallpaper_dir_->CreateUniqueTempDir());
+    wallpaper_manager_test_utils::CreateCmdlineWallpapers(
+        *cmdline_wallpaper_dir_, &wallpaper_manager_command_line_);
+  }
+
+  ash::DesktopBackgroundController* controller_;
+  PrefService* local_state_;
+  scoped_ptr<base::CommandLine> wallpaper_manager_command_line_;
+
+  // Directory created by CreateCmdlineWallpapersAndSetFlags() to store default
+  // wallpaper images.
+  scoped_ptr<base::ScopedTempDir> cmdline_wallpaper_dir_;
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(CustomizationWallpaperDownloaderBrowserTest);
+};
+
+IN_PROC_BROWSER_TEST_F(CustomizationWallpaperDownloaderBrowserTest,
+                       OEMWallpaperIsPresent) {
+  CreateCmdlineWallpapers();
+  WallpaperManager::Get()->SetDefaultWallpaperNow(std::string());
+  wallpaper_manager_test_utils::WaitAsyncWallpaperLoadFinished();
+  EXPECT_TRUE(wallpaper_manager_test_utils::ImageIsNearColor(
+      controller_->GetWallpaper(),
+      wallpaper_manager_test_utils::kSmallDefaultWallpaperColor));
+
+  WallpaperImageFetcherFactory url_factory(
+      GURL(kOEMWallpaperURL),
+      wallpaper_manager_test_utils::kWallpaperSize,
+      wallpaper_manager_test_utils::kWallpaperSize,
+      wallpaper_manager_test_utils::kCustomWallpaperColor,
+      0 /* require_retries */);
+
+  TestWallpaperObserver observer(WallpaperManager::Get());
+  chromeos::ServicesCustomizationDocument* customization =
+      chromeos::ServicesCustomizationDocument::GetInstance();
+  EXPECT_TRUE(
+      customization->LoadManifestFromString(std::string(kServicesManifest)));
+
+  observer.WaitForWallpaperAnimationFinished();
+  EXPECT_TRUE(wallpaper_manager_test_utils::ImageIsNearColor(
+      controller_->GetWallpaper(),
+      wallpaper_manager_test_utils::kCustomWallpaperColor));
+  EXPECT_EQ(1U, url_factory.num_attempts());
+}
+
+IN_PROC_BROWSER_TEST_F(CustomizationWallpaperDownloaderBrowserTest,
+                       OEMWallpaperRetryFetch) {
+  CreateCmdlineWallpapers();
+  WallpaperManager::Get()->SetDefaultWallpaperNow(std::string());
+  wallpaper_manager_test_utils::WaitAsyncWallpaperLoadFinished();
+  EXPECT_TRUE(wallpaper_manager_test_utils::ImageIsNearColor(
+      controller_->GetWallpaper(),
+      wallpaper_manager_test_utils::kSmallDefaultWallpaperColor));
+
+  WallpaperImageFetcherFactory url_factory(
+      GURL(kOEMWallpaperURL),
+      wallpaper_manager_test_utils::kWallpaperSize,
+      wallpaper_manager_test_utils::kWallpaperSize,
+      wallpaper_manager_test_utils::kCustomWallpaperColor,
+      1 /* require_retries */);
+
+  TestWallpaperObserver observer(WallpaperManager::Get());
+  chromeos::ServicesCustomizationDocument* customization =
+      chromeos::ServicesCustomizationDocument::GetInstance();
+  EXPECT_TRUE(
+      customization->LoadManifestFromString(std::string(kServicesManifest)));
+
+  observer.WaitForWallpaperAnimationFinished();
+  EXPECT_TRUE(wallpaper_manager_test_utils::ImageIsNearColor(
+      controller_->GetWallpaper(),
+      wallpaper_manager_test_utils::kCustomWallpaperColor));
+
+  EXPECT_EQ(2U, url_factory.num_attempts());
+}
+
+}  // namespace chromeos
diff --git a/chrome/browser/chromeos/login/wallpaper_manager.cc b/chrome/browser/chromeos/login/wallpaper_manager.cc
index 5877141b..948baae 100644
--- a/chrome/browser/chromeos/login/wallpaper_manager.cc
+++ b/chrome/browser/chromeos/login/wallpaper_manager.cc
@@ -430,15 +430,7 @@
   }
 
   // Destroy self.
-  DCHECK(manager->loading_.size() > 0);
-
-  for (WallpaperManager::PendingList::iterator i = manager->loading_.begin();
-       i != manager->loading_.end();
-       ++i)
-    if (i->get() == this) {
-      manager->loading_.erase(i);
-      break;
-    }
+  manager->RemovePendingWallpaperFromList(this);
 }
 
 // WallpaperManager, public: ---------------------------------------------------
@@ -1771,6 +1763,22 @@
   return pending_inactive_;
 }
 
+void WallpaperManager::RemovePendingWallpaperFromList(
+    PendingWallpaper* pending) {
+  DCHECK(loading_.size() > 0);
+  for (WallpaperManager::PendingList::iterator i = loading_.begin();
+       i != loading_.end();
+       ++i) {
+    if (i->get() == pending) {
+      loading_.erase(i);
+      break;
+    }
+  }
+
+  if (loading_.empty())
+    FOR_EACH_OBSERVER(Observer, observers_, OnPendingListEmptyForTesting());
+}
+
 void WallpaperManager::SetCustomizedDefaultWallpaper(
     const GURL& wallpaper_url,
     const base::FilePath& downloaded_file,
@@ -1806,6 +1814,10 @@
   }
 }
 
+size_t WallpaperManager::GetPendingListSizeForTesting() const {
+  return loading_.size();
+}
+
 void WallpaperManager::SetDefaultWallpaperPathsFromCommandLine(
     base::CommandLine* command_line) {
   default_small_wallpaper_file_ = command_line->GetSwitchValuePath(
diff --git a/chrome/browser/chromeos/login/wallpaper_manager.h b/chrome/browser/chromeos/login/wallpaper_manager.h
index 89c0566..4e7c03a 100644
--- a/chrome/browser/chromeos/login/wallpaper_manager.h
+++ b/chrome/browser/chromeos/login/wallpaper_manager.h
@@ -121,6 +121,7 @@
     virtual ~Observer() {}
     virtual void OnWallpaperAnimationFinished(const std::string& user_id) = 0;
     virtual void OnUpdateWallpaperForTesting() {}
+    virtual void OnPendingListEmptyForTesting() {}
   };
 
   // This is "wallpaper either scheduled to load, or loading right now".
@@ -340,6 +341,9 @@
                                      const base::FilePath& downloaded_file,
                                      const base::FilePath& resized_directory);
 
+  // Returns queue size.
+  size_t GetPendingListSizeForTesting() const;
+
  private:
   friend class TestApi;
   friend class WallpaperManagerBrowserTest;
@@ -479,6 +483,9 @@
   PendingWallpaper* GetPendingWallpaper(const std::string& user_id,
                                         bool delayed);
 
+  // This is called by PendingWallpaper when load is finished.
+  void RemovePendingWallpaperFromList(PendingWallpaper* pending);
+
   // Calculate delay for next wallpaper load.
   // It is usually average wallpaper load time.
   // If last wallpaper load happened long ago, timeout should be reduced by
diff --git a/chrome/browser/chromeos/login/wallpaper_manager_browsertest.cc b/chrome/browser/chromeos/login/wallpaper_manager_browsertest.cc
index 0a74926..ad86ec4 100644
--- a/chrome/browser/chromeos/login/wallpaper_manager_browsertest.cc
+++ b/chrome/browser/chromeos/login/wallpaper_manager_browsertest.cc
@@ -4,7 +4,6 @@
 
 #include "chrome/browser/chromeos/login/wallpaper_manager.h"
 
-#include "ash/ash_switches.h"
 #include "ash/desktop_background/desktop_background_controller.h"
 #include "ash/desktop_background/desktop_background_controller_observer.h"
 #include "ash/desktop_background/desktop_background_controller_test_api.h"
@@ -27,6 +26,7 @@
 #include "base/values.h"
 #include "chrome/browser/chromeos/login/user.h"
 #include "chrome/browser/chromeos/login/user_manager.h"
+#include "chrome/browser/chromeos/login/wallpaper_manager_test_utils.h"
 #include "chrome/common/chrome_paths.h"
 #include "chrome/common/chrome_switches.h"
 #include "chrome/test/base/in_process_browser_test.h"
@@ -36,7 +36,7 @@
 #include "grit/ash_resources.h"
 #include "ui/aura/env.h"
 #include "ui/base/resource/resource_bundle.h"
-#include "ui/gfx/codec/jpeg_codec.h"
+#include "ui/gfx/image/image_skia.h"
 #include "ui/gfx/point.h"
 #include "ui/gfx/rect.h"
 
@@ -104,30 +104,7 @@
     base::RunLoop().RunUntilIdle();
   }
 
-  void WaitAsyncWallpaperLoadFinished() {
-    base::RunLoop().RunUntilIdle();
-    while (WallpaperManager::Get()->loading_.size()) {
-      base::PlatformThread::Sleep(base::TimeDelta::FromMilliseconds(100));
-      base::RunLoop().RunUntilIdle();
-    }
-  }
-
  protected:
-  // Colors used for different default wallpapers by
-  // WriteWallpapers().
-  static const SkColor kLargeWallpaperColor = SK_ColorRED;
-  static const SkColor kSmallWallpaperColor = SK_ColorGREEN;
-  static const SkColor kLargeGuestWallpaperColor = SK_ColorBLUE;
-  static const SkColor kSmallGuestWallpaperColor = SK_ColorYELLOW;
-
-  // A color that can be passed to CreateImage(). Specifically chosen to not
-  // conflict with any of the default wallpaper colors.
-  static const SkColor kCustomWallpaperColor = SK_ColorMAGENTA;
-
-  // 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.
-  static const int kWallpaperSize = 2;
 
   // Return custom wallpaper path. Create directory if not exist.
   base::FilePath GetCustomWallpaperPath(const char* sub_dir,
@@ -170,146 +147,22 @@
     WallpaperManager::Get()->ClearDisposableWallpaperCache();
   }
 
-  // Creates a test image of size 1x1.
-  gfx::ImageSkia CreateTestImage(int width, int height, SkColor color) {
-    SkBitmap bitmap;
-    bitmap.setConfig(SkBitmap::kARGB_8888_Config, width, height);
-    bitmap.allocPixels();
-    bitmap.eraseColor(color);
-    return gfx::ImageSkia::CreateFrom1xBitmap(bitmap);
-  }
-
-  // Writes a JPEG image of the specified size and color to |path|. Returns
-  // true on success.
-  bool WriteJPEGFile(const base::FilePath& path,
-                     int width,
-                     int height,
-                     SkColor color) {
-    SkBitmap bitmap;
-    bitmap.setConfig(SkBitmap::kARGB_8888_Config, width, height, 0);
-    bitmap.allocPixels();
-    bitmap.eraseColor(color);
-
-    const int kQuality = 80;
-    std::vector<unsigned char> output;
-    if (!gfx::JPEGCodec::Encode(
-            static_cast<const unsigned char*>(bitmap.getPixels()),
-            gfx::JPEGCodec::FORMAT_SkBitmap,
-            width,
-            height,
-            bitmap.rowBytes(),
-            kQuality,
-            &output)) {
-      LOG(ERROR) << "Unable to encode " << width << "x" << height << " bitmap";
-      return false;
-    }
-
-    size_t bytes_written = base::WriteFile(
-        path, reinterpret_cast<const char*>(&output[0]), output.size());
-    if (bytes_written != output.size()) {
-      LOG(ERROR) << "Wrote " << bytes_written << " byte(s) instead of "
-                 << output.size() << " to " << path.value();
-      return false;
-    }
-
-    return true;
-  }
-
   // Initializes default wallpaper paths "*default_*file" and writes JPEG
   // wallpaper images to them.
   // Only needs to be called (once) by tests that want to test loading of
   // default wallpapers.
-  void WriteWallpapers() {
+  void CreateCmdlineWallpapers() {
     wallpaper_dir_.reset(new base::ScopedTempDir);
     ASSERT_TRUE(wallpaper_dir_->CreateUniqueTempDir());
-
-    std::vector<std::string> options;
-    options.push_back(std::string("WM_Test_cmdline"));
-    const base::FilePath small_file =
-        wallpaper_dir_->path().Append(FILE_PATH_LITERAL("small.jpg"));
-    options.push_back(std::string("--") +
-                      ash::switches::kAshDefaultWallpaperSmall + "=" +
-                      small_file.value());
-    const base::FilePath large_file =
-        wallpaper_dir_->path().Append(FILE_PATH_LITERAL("large.jpg"));
-    options.push_back(std::string("--") +
-                      ash::switches::kAshDefaultWallpaperLarge + "=" +
-                      large_file.value());
-    const base::FilePath guest_small_file =
-        wallpaper_dir_->path().Append(FILE_PATH_LITERAL("guest_small.jpg"));
-    options.push_back(std::string("--") +
-                      ash::switches::kAshGuestWallpaperSmall + "=" +
-                      guest_small_file.value());
-    const base::FilePath guest_large_file =
-        wallpaper_dir_->path().Append(FILE_PATH_LITERAL("guest_large.jpg"));
-    options.push_back(std::string("--") +
-                      ash::switches::kAshGuestWallpaperLarge + "=" +
-                      guest_large_file.value());
-
-    ASSERT_TRUE(WriteJPEGFile(small_file,
-                              kWallpaperSize,
-                              kWallpaperSize,
-                              kSmallWallpaperColor));
-    ASSERT_TRUE(WriteJPEGFile(large_file,
-                              kWallpaperSize,
-                              kWallpaperSize,
-                              kLargeWallpaperColor));
-    ASSERT_TRUE(WriteJPEGFile(guest_small_file,
-                              kWallpaperSize,
-                              kWallpaperSize,
-                              kSmallGuestWallpaperColor));
-    ASSERT_TRUE(WriteJPEGFile(guest_large_file,
-                              kWallpaperSize,
-                              kWallpaperSize,
-                              kLargeGuestWallpaperColor));
-
-    wallpaper_manager_command_line_.reset(new base::CommandLine(options));
-    WallpaperManager::Get()->SetCommandLineForTesting(
-        wallpaper_manager_command_line_.get());
-  }
-
-  // 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;
-    }
-
-    bitmap->lockPixels();
-    gfx::Point center = gfx::Rect(image.size()).CenterPoint();
-    SkColor image_color = bitmap->getColor(center.x(), center.y());
-    bitmap->unlockPixels();
-
-    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;
+    wallpaper_manager_test_utils::CreateCmdlineWallpapers(
+        *wallpaper_dir_, &wallpaper_manager_command_line_);
   }
 
   DesktopBackgroundController* controller_;
   PrefService* local_state_;
   scoped_ptr<base::CommandLine> wallpaper_manager_command_line_;
 
-  // Directory created by WriteWallpapersAndSetFlags() to store default
+  // Directory created by CreateCmdlineWallpapers () to store default
   // wallpaper images.
   scoped_ptr<base::ScopedTempDir> wallpaper_dir_;
 
@@ -352,7 +205,7 @@
 
   // Set the wallpaper for |kTestUser1|.
   wallpaper_manager->SetUserWallpaperNow(kTestUser1);
-  WaitAsyncWallpaperLoadFinished();
+  wallpaper_manager_test_utils::WaitAsyncWallpaperLoadFinished();
   gfx::ImageSkia wallpaper = controller_->GetWallpaper();
 
   // Display is initialized to 800x600. The small resolution custom wallpaper is
@@ -362,7 +215,7 @@
 
   // Hook up another 800x600 display. This shouldn't trigger a reload.
   UpdateDisplay("800x600,800x600");
-  WaitAsyncWallpaperLoadFinished();
+  wallpaper_manager_test_utils::WaitAsyncWallpaperLoadFinished();
   // The small resolution custom wallpaper is expected.
   EXPECT_EQ(kSmallWallpaperWidth, wallpaper.width());
   EXPECT_EQ(kSmallWallpaperHeight, wallpaper.height());
@@ -372,7 +225,7 @@
   // Hook up a 2000x2000 display. The large resolution custom wallpaper should
   // be loaded.
   UpdateDisplay("800x600,2000x2000");
-  WaitAsyncWallpaperLoadFinished();
+  wallpaper_manager_test_utils::WaitAsyncWallpaperLoadFinished();
   wallpaper = controller_->GetWallpaper();
 
   // The large resolution custom wallpaper is expected.
@@ -384,7 +237,7 @@
   // Hook up the 2000x2000 display again. The large resolution default wallpaper
   // should persist. Test for crbug/165788.
   UpdateDisplay("800x600,2000x2000");
-  WaitAsyncWallpaperLoadFinished();
+  wallpaper_manager_test_utils::WaitAsyncWallpaperLoadFinished();
   wallpaper = controller_->GetWallpaper();
 
   // The large resolution custom wallpaper is expected.
@@ -404,12 +257,12 @@
   // Loads the same wallpaper before the initial one finished. It should be
   // prevented.
   wallpaper_manager->SetUserWallpaperNow(kTestUser1);
-  WaitAsyncWallpaperLoadFinished();
+  wallpaper_manager_test_utils::WaitAsyncWallpaperLoadFinished();
   EXPECT_EQ(1, LoadedWallpapers());
   // Loads the same wallpaper after the initial one finished. It should be
   // prevented.
   wallpaper_manager->SetUserWallpaperNow(kTestUser1);
-  WaitAsyncWallpaperLoadFinished();
+  wallpaper_manager_test_utils::WaitAsyncWallpaperLoadFinished();
   EXPECT_EQ(1, LoadedWallpapers());
   ClearDisposableWallpaperCache();
 
@@ -441,7 +294,7 @@
   WaitAsyncWallpaperLoadStarted();
   EXPECT_EQ(2, LoadedWallpapers());
   wallpaper_manager->SetUserWallpaperNow(kTestUser1);
-  WaitAsyncWallpaperLoadFinished();
+  wallpaper_manager_test_utils::WaitAsyncWallpaperLoadFinished();
   EXPECT_EQ(2, LoadedWallpapers());
 }
 
@@ -472,7 +325,7 @@
 IN_PROC_BROWSER_TEST_P(WallpaperManagerBrowserTest,
                        UseMigratedWallpaperInfo) {
   LogIn(kTestUser1, kTestUser1Hash);
-  WaitAsyncWallpaperLoadFinished();
+  wallpaper_manager_test_utils::WaitAsyncWallpaperLoadFinished();
   // This test should finish normally. If timeout, it is probably because
   // migrated wallpaper is somehow not loaded. Bad things can happen if
   // wallpaper is not loaded at login screen. One example is: crosbug.com/38429.
@@ -493,7 +346,7 @@
 IN_PROC_BROWSER_TEST_P(WallpaperManagerBrowserTest,
                        UsePreMigrationWallpaperInfo) {
   LogIn(kTestUser1, kTestUser1Hash);
-  WaitAsyncWallpaperLoadFinished();
+  wallpaper_manager_test_utils::WaitAsyncWallpaperLoadFinished();
   // This test should finish normally. If timeout, it is probably because chrome
   // can not handle pre migrated user profile (M21 profile or older).
 }
@@ -505,12 +358,12 @@
   UpdateDisplay("800x600");
   // Set initial wallpaper to the default wallpaper.
   WallpaperManager::Get()->SetDefaultWallpaperNow(UserManager::kStubUser);
-  WaitAsyncWallpaperLoadFinished();
+  wallpaper_manager_test_utils::WaitAsyncWallpaperLoadFinished();
 
   // Hook up a 2000x2000 display. The large resolution custom wallpaper should
   // be loaded.
   UpdateDisplay("800x600,2000x2000");
-  WaitAsyncWallpaperLoadFinished();
+  wallpaper_manager_test_utils::WaitAsyncWallpaperLoadFinished();
 }
 
 class WallpaperManagerBrowserTestNoAnimation
@@ -549,7 +402,7 @@
 IN_PROC_BROWSER_TEST_P(WallpaperManagerBrowserTestNoAnimation,
                        UseMigratedWallpaperInfo) {
   LogIn(kTestUser1, kTestUser1Hash);
-  WaitAsyncWallpaperLoadFinished();
+  wallpaper_manager_test_utils::WaitAsyncWallpaperLoadFinished();
   // This test should finish normally. If timeout, it is probably because
   // migrated wallpaper is somehow not loaded. Bad things can happen if
   // wallpaper is not loaded at login screen. One example is: crosbug.com/38429.
@@ -561,7 +414,7 @@
                        PRE_UsePreMigrationWallpaperInfo) {
   // New user log in, a default wallpaper is loaded.
   LogIn(kTestUser1, kTestUser1Hash);
-  WaitAsyncWallpaperLoadFinished();
+  wallpaper_manager_test_utils::WaitAsyncWallpaperLoadFinished();
   // Old wallpaper migration code doesn't exist in codebase anymore. So if
   // user's profile is not migrated, it is the same as no wallpaper info. To
   // simulate this, we remove user's wallpaper info here.
@@ -571,7 +424,7 @@
 IN_PROC_BROWSER_TEST_P(WallpaperManagerBrowserTestNoAnimation,
                        UsePreMigrationWallpaperInfo) {
   LogIn(kTestUser1, kTestUser1Hash);
-  WaitAsyncWallpaperLoadFinished();
+  wallpaper_manager_test_utils::WaitAsyncWallpaperLoadFinished();
   // This test should finish normally. If timeout, it is probably because chrome
   // can not handle pre migrated user profile (M21 profile or older).
 }
@@ -591,7 +444,7 @@
 IN_PROC_BROWSER_TEST_P(WallpaperManagerBrowserTestCrashRestore,
                        PRE_RestoreWallpaper) {
   LogIn(kTestUser1, kTestUser1Hash);
-  WaitAsyncWallpaperLoadFinished();
+  wallpaper_manager_test_utils::WaitAsyncWallpaperLoadFinished();
 }
 
 // Test for crbug.com/270278. It simulates a browser crash and verifies if user
@@ -612,11 +465,7 @@
  protected:
   // Creates a test image of size 1x1.
   gfx::ImageSkia CreateTestImage(SkColor color) {
-    SkBitmap bitmap;
-    bitmap.setConfig(SkBitmap::kARGB_8888_Config, 1, 1);
-    bitmap.allocPixels();
-    bitmap.eraseColor(color);
-    return gfx::ImageSkia::CreateFrom1xBitmap(bitmap);
+    return wallpaper_manager_test_utils::CreateTestImage(1, 1, color);
   }
 };
 
@@ -654,7 +503,7 @@
   };
   wallpaper_manager->SetUserWallpaperInfo(kTestUser1, info, true);
   wallpaper_manager->SetUserWallpaperNow(kTestUser1);
-  WaitAsyncWallpaperLoadFinished();
+  wallpaper_manager_test_utils::WaitAsyncWallpaperLoadFinished();
   scoped_ptr<WallpaperManager::TestApi> test_api;
   test_api.reset(new WallpaperManager::TestApi(wallpaper_manager));
   // Verify SetUserWallpaperNow updates wallpaper cache.
@@ -673,8 +522,12 @@
 // wallpaper cache should not be deleted.
 IN_PROC_BROWSER_TEST_P(WallpaperManagerBrowserTestCacheUpdate,
                        VerifyWallpaperCache) {
-  WaitAsyncWallpaperLoadFinished();
   WallpaperManager* wallpaper_manager = WallpaperManager::Get();
+
+  // Force load initial wallpaper
+  // (simulate DesktopBackgroundController::UpdateDisplay()).
+  wallpaper_manager->UpdateWallpaper(true);
+  wallpaper_manager_test_utils::WaitAsyncWallpaperLoadFinished();
   scoped_ptr<WallpaperManager::TestApi> test_api;
   test_api.reset(new WallpaperManager::TestApi(wallpaper_manager));
   gfx::ImageSkia cached_wallpaper;
@@ -682,7 +535,7 @@
   EXPECT_TRUE(test_api->GetWallpaperFromCache(kTestUser1, &cached_wallpaper));
 
   LogIn(kTestUser2, kTestUser2Hash);
-  WaitAsyncWallpaperLoadFinished();
+  wallpaper_manager_test_utils::WaitAsyncWallpaperLoadFinished();
   // Login another user should not delete logged in user's wallpaper cache.
   // Note active user is still kTestUser1.
   EXPECT_TRUE(test_api->GetWallpaperFromCache(kTestUser1, &cached_wallpaper));
@@ -692,7 +545,7 @@
                                                red_wallpaper,
                                                WALLPAPER_LAYOUT_CENTER,
                                                true);
-  WaitAsyncWallpaperLoadFinished();
+  wallpaper_manager_test_utils::WaitAsyncWallpaperLoadFinished();
   // SetWallpaperFromImageSkia should update wallpaper cache when multi-profile
   // is turned on.
   EXPECT_TRUE(test_api->GetWallpaperFromCache(kTestUser1, &cached_wallpaper));
@@ -706,14 +559,14 @@
                                         User::CUSTOMIZED,
                                         green_wallpaper,
                                         true);
-  WaitAsyncWallpaperLoadFinished();
+  wallpaper_manager_test_utils::WaitAsyncWallpaperLoadFinished();
   // SetCustomWallpaper should also update wallpaper cache when multi-profile is
   // turned on.
   EXPECT_TRUE(test_api->GetWallpaperFromCache(kTestUser1, &cached_wallpaper));
   EXPECT_TRUE(cached_wallpaper.BackedBySameObjectAs(green_wallpaper));
 
   wallpaper_manager->SetDefaultWallpaperNow(kTestUser1);
-  WaitAsyncWallpaperLoadFinished();
+  wallpaper_manager_test_utils::WaitAsyncWallpaperLoadFinished();
   // SetDefaultWallpaper should invalidate the user's wallpaper cache.
   EXPECT_FALSE(test_api->GetWallpaperFromCache(kTestUser1, &cached_wallpaper));
 }
@@ -778,28 +631,35 @@
 
   // Set the wallpaper to ensure that UpdateWallpaper() will be called when the
   // display configuration changes.
-  gfx::ImageSkia image = CreateTestImage(640, 480, kCustomWallpaperColor);
+  gfx::ImageSkia image = wallpaper_manager_test_utils::CreateTestImage(
+      640, 480, wallpaper_manager_test_utils::kCustomWallpaperColor);
   controller_->SetWallpaperImage(image, WALLPAPER_LAYOUT_STRETCH);
 
   // Small wallpaper images should be used for configurations less than or
   // equal to kSmallWallpaperMaxWidth by kSmallWallpaperMaxHeight, even if
   // multiple displays are connected.
   UpdateDisplay("800x600");
-  WaitAsyncWallpaperLoadFinished();
+  // Wait for asynchronous DisplayBackgroundController::UpdateDisplay() call.
+  base::RunLoop().RunUntilIdle();
+  wallpaper_manager_test_utils::WaitAsyncWallpaperLoadFinished();
   WallpaperManager::GetAppropriateResolutionForTesting();
   EXPECT_EQ(WallpaperManager::WALLPAPER_RESOLUTION_SMALL,
             WallpaperManager::Get()->GetAppropriateResolution());
   EXPECT_EQ(0, observer.GetUpdateWallpaperCountAndReset());
 
   UpdateDisplay("800x600,800x600");
-  WaitAsyncWallpaperLoadFinished();
+  // Wait for asynchronous DisplayBackgroundController::UpdateDisplay() call.
+  base::RunLoop().RunUntilIdle();
+  wallpaper_manager_test_utils::WaitAsyncWallpaperLoadFinished();
   WallpaperManager::GetAppropriateResolutionForTesting();
   EXPECT_EQ(WallpaperManager::WALLPAPER_RESOLUTION_SMALL,
             WallpaperManager::Get()->GetAppropriateResolution());
   EXPECT_EQ(0, observer.GetUpdateWallpaperCountAndReset());
 
   UpdateDisplay("1366x800");
-  WaitAsyncWallpaperLoadFinished();
+  // Wait for asynchronous DisplayBackgroundController::UpdateDisplay() call.
+  base::RunLoop().RunUntilIdle();
+  wallpaper_manager_test_utils::WaitAsyncWallpaperLoadFinished();
   WallpaperManager::GetAppropriateResolutionForTesting();
   EXPECT_EQ(WallpaperManager::WALLPAPER_RESOLUTION_SMALL,
             WallpaperManager::Get()->GetAppropriateResolution());
@@ -807,21 +667,27 @@
 
   // At larger sizes, large wallpapers should be used.
   UpdateDisplay("1367x800");
-  WaitAsyncWallpaperLoadFinished();
+  // Wait for asynchronous DisplayBackgroundController::UpdateDisplay() call.
+  base::RunLoop().RunUntilIdle();
+  wallpaper_manager_test_utils::WaitAsyncWallpaperLoadFinished();
   WallpaperManager::GetAppropriateResolutionForTesting();
   EXPECT_EQ(WallpaperManager::WALLPAPER_RESOLUTION_LARGE,
             WallpaperManager::Get()->GetAppropriateResolution());
   EXPECT_EQ(1, observer.GetUpdateWallpaperCountAndReset());
 
   UpdateDisplay("1367x801");
-  WaitAsyncWallpaperLoadFinished();
+  // Wait for asynchronous DisplayBackgroundController::UpdateDisplay() call.
+  base::RunLoop().RunUntilIdle();
+  wallpaper_manager_test_utils::WaitAsyncWallpaperLoadFinished();
   WallpaperManager::GetAppropriateResolutionForTesting();
   EXPECT_EQ(WallpaperManager::WALLPAPER_RESOLUTION_LARGE,
             WallpaperManager::Get()->GetAppropriateResolution());
   EXPECT_EQ(1, observer.GetUpdateWallpaperCountAndReset());
 
   UpdateDisplay("2560x1700");
-  WaitAsyncWallpaperLoadFinished();
+  // Wait for asynchronous DisplayBackgroundController::UpdateDisplay() call.
+  base::RunLoop().RunUntilIdle();
+  wallpaper_manager_test_utils::WaitAsyncWallpaperLoadFinished();
   WallpaperManager::GetAppropriateResolutionForTesting();
   EXPECT_EQ(WallpaperManager::WALLPAPER_RESOLUTION_LARGE,
             WallpaperManager::Get()->GetAppropriateResolution());
@@ -829,29 +695,37 @@
 
   // Rotated smaller screen may use larger image.
   UpdateDisplay("800x600/r");
-  WaitAsyncWallpaperLoadFinished();
+  // Wait for asynchronous DisplayBackgroundController::UpdateDisplay() call.
+  base::RunLoop().RunUntilIdle();
+  wallpaper_manager_test_utils::WaitAsyncWallpaperLoadFinished();
   WallpaperManager::GetAppropriateResolutionForTesting();
   EXPECT_EQ(WallpaperManager::WALLPAPER_RESOLUTION_SMALL,
             WallpaperManager::Get()->GetAppropriateResolution());
   EXPECT_EQ(1, observer.GetUpdateWallpaperCountAndReset());
 
   UpdateDisplay("800x600/r,800x600");
-  WaitAsyncWallpaperLoadFinished();
+  // Wait for asynchronous DisplayBackgroundController::UpdateDisplay() call.
+  base::RunLoop().RunUntilIdle();
+  wallpaper_manager_test_utils::WaitAsyncWallpaperLoadFinished();
   WallpaperManager::GetAppropriateResolutionForTesting();
   EXPECT_EQ(WallpaperManager::WALLPAPER_RESOLUTION_SMALL,
             WallpaperManager::Get()->GetAppropriateResolution());
   EXPECT_EQ(1, observer.GetUpdateWallpaperCountAndReset());
   UpdateDisplay("1366x800/r");
+  // Wait for asynchronous DisplayBackgroundController::UpdateDisplay() call.
+  base::RunLoop().RunUntilIdle();
+  wallpaper_manager_test_utils::WaitAsyncWallpaperLoadFinished();
   WallpaperManager::GetAppropriateResolutionForTesting();
-  WaitAsyncWallpaperLoadFinished();
   EXPECT_EQ(WallpaperManager::WALLPAPER_RESOLUTION_LARGE,
             WallpaperManager::Get()->GetAppropriateResolution());
   EXPECT_EQ(1, observer.GetUpdateWallpaperCountAndReset());
 
   // Max display size didn't chagne.
   UpdateDisplay("900x800/r,400x1366");
+  // Wait for asynchronous DisplayBackgroundController::UpdateDisplay() call.
+  base::RunLoop().RunUntilIdle();
   WallpaperManager::GetAppropriateResolutionForTesting();
-  WaitAsyncWallpaperLoadFinished();
+  wallpaper_manager_test_utils::WaitAsyncWallpaperLoadFinished();
   EXPECT_EQ(0, observer.GetUpdateWallpaperCountAndReset());
 }
 
@@ -863,80 +737,86 @@
   if (!ash::test::AshTestHelper::SupportsMultipleDisplays())
     return;
 
-  WriteWallpapers();
+  CreateCmdlineWallpapers();
 
   // At 800x600, the small wallpaper should be loaded.
   UpdateDisplay("800x600");
   WallpaperManager::Get()->SetDefaultWallpaperNow(std::string());
-  WaitAsyncWallpaperLoadFinished();
-  EXPECT_TRUE(
-      ImageIsNearColor(controller_->GetWallpaper(), kSmallWallpaperColor));
+  wallpaper_manager_test_utils::WaitAsyncWallpaperLoadFinished();
+  EXPECT_TRUE(wallpaper_manager_test_utils::ImageIsNearColor(
+      controller_->GetWallpaper(),
+      wallpaper_manager_test_utils::kSmallDefaultWallpaperColor));
 }
 
 IN_PROC_BROWSER_TEST_P(WallpaperManagerBrowserTest, LargeDefaultWallpaper) {
   if (!ash::test::AshTestHelper::SupportsMultipleDisplays())
     return;
 
-  WriteWallpapers();
+  CreateCmdlineWallpapers();
   UpdateDisplay("1600x1200");
   WallpaperManager::Get()->SetDefaultWallpaperNow(std::string());
-  WaitAsyncWallpaperLoadFinished();
-  EXPECT_TRUE(
-      ImageIsNearColor(controller_->GetWallpaper(), kLargeWallpaperColor));
+  wallpaper_manager_test_utils::WaitAsyncWallpaperLoadFinished();
+  EXPECT_TRUE(wallpaper_manager_test_utils::ImageIsNearColor(
+      controller_->GetWallpaper(),
+      wallpaper_manager_test_utils::kLargeDefaultWallpaperColor));
 }
 
 IN_PROC_BROWSER_TEST_P(WallpaperManagerBrowserTest,
                        LargeDefaultWallpaperWhenRotated) {
   if (!ash::test::AshTestHelper::SupportsMultipleDisplays())
     return;
-  WriteWallpapers();
+  CreateCmdlineWallpapers();
 
   UpdateDisplay("1200x800/r");
   WallpaperManager::Get()->SetDefaultWallpaperNow(std::string());
-  WaitAsyncWallpaperLoadFinished();
-  EXPECT_TRUE(
-      ImageIsNearColor(controller_->GetWallpaper(), kLargeWallpaperColor));
+  wallpaper_manager_test_utils::WaitAsyncWallpaperLoadFinished();
+  EXPECT_TRUE(wallpaper_manager_test_utils::ImageIsNearColor(
+      controller_->GetWallpaper(),
+      wallpaper_manager_test_utils::kLargeDefaultWallpaperColor));
 }
 
 IN_PROC_BROWSER_TEST_P(WallpaperManagerBrowserTest, SmallGuestWallpaper) {
   if (!ash::test::AshTestHelper::SupportsMultipleDisplays())
     return;
-  WriteWallpapers();
+  CreateCmdlineWallpapers();
   UserManager::Get()->UserLoggedIn(
       UserManager::kGuestUserName, UserManager::kGuestUserName, false);
   UpdateDisplay("800x600");
   WallpaperManager::Get()->SetDefaultWallpaperNow(std::string());
-  WaitAsyncWallpaperLoadFinished();
-  EXPECT_TRUE(
-      ImageIsNearColor(controller_->GetWallpaper(), kSmallGuestWallpaperColor));
+  wallpaper_manager_test_utils::WaitAsyncWallpaperLoadFinished();
+  EXPECT_TRUE(wallpaper_manager_test_utils::ImageIsNearColor(
+      controller_->GetWallpaper(),
+      wallpaper_manager_test_utils::kSmallGuestWallpaperColor));
 }
 
 IN_PROC_BROWSER_TEST_P(WallpaperManagerBrowserTest, LargeGuestWallpaper) {
   if (!ash::test::AshTestHelper::SupportsMultipleDisplays())
     return;
 
-  WriteWallpapers();
+  CreateCmdlineWallpapers();
   UserManager::Get()->UserLoggedIn(
       UserManager::kGuestUserName, UserManager::kGuestUserName, false);
   UpdateDisplay("1600x1200");
   WallpaperManager::Get()->SetDefaultWallpaperNow(std::string());
-  WaitAsyncWallpaperLoadFinished();
-  EXPECT_TRUE(
-      ImageIsNearColor(controller_->GetWallpaper(), kLargeGuestWallpaperColor));
+  wallpaper_manager_test_utils::WaitAsyncWallpaperLoadFinished();
+  EXPECT_TRUE(wallpaper_manager_test_utils::ImageIsNearColor(
+      controller_->GetWallpaper(),
+      wallpaper_manager_test_utils::kLargeGuestWallpaperColor));
 }
 
 IN_PROC_BROWSER_TEST_P(WallpaperManagerBrowserTest,
                        SwitchBetweenDefaultAndCustom) {
   // Start loading the default wallpaper.
   UpdateDisplay("640x480");
-  WriteWallpapers();
+  CreateCmdlineWallpapers();
   UserManager::Get()->UserLoggedIn(UserManager::kStubUser, "test_hash", false);
 
   WallpaperManager::Get()->SetDefaultWallpaperNow(std::string());
 
   // Custom wallpaper should be applied immediately, canceling the default
   // wallpaper load task.
-  gfx::ImageSkia image = CreateTestImage(640, 480, kCustomWallpaperColor);
+  gfx::ImageSkia image = wallpaper_manager_test_utils::CreateTestImage(
+      640, 480, wallpaper_manager_test_utils::kCustomWallpaperColor);
   WallpaperManager::Get()->SetCustomWallpaper(UserManager::kStubUser,
                                               "test_hash",
                                               "test-nofile.jpeg",
@@ -944,16 +824,18 @@
                                               User::CUSTOMIZED,
                                               image,
                                               true);
-  WaitAsyncWallpaperLoadFinished();
+  wallpaper_manager_test_utils::WaitAsyncWallpaperLoadFinished();
 
-  EXPECT_TRUE(
-      ImageIsNearColor(controller_->GetWallpaper(), kCustomWallpaperColor));
+  EXPECT_TRUE(wallpaper_manager_test_utils::ImageIsNearColor(
+      controller_->GetWallpaper(),
+      wallpaper_manager_test_utils::kCustomWallpaperColor));
 
   WallpaperManager::Get()->SetDefaultWallpaperNow(std::string());
-  WaitAsyncWallpaperLoadFinished();
+  wallpaper_manager_test_utils::WaitAsyncWallpaperLoadFinished();
 
-  EXPECT_TRUE(
-      ImageIsNearColor(controller_->GetWallpaper(), kSmallWallpaperColor));
+  EXPECT_TRUE(wallpaper_manager_test_utils::ImageIsNearColor(
+      controller_->GetWallpaper(),
+      wallpaper_manager_test_utils::kSmallDefaultWallpaperColor));
 }
 
 }  // namespace chromeos
diff --git a/chrome/browser/chromeos/login/wallpaper_manager_test_utils.cc b/chrome/browser/chromeos/login/wallpaper_manager_test_utils.cc
new file mode 100644
index 0000000..7a704ed
--- /dev/null
+++ b/chrome/browser/chromeos/login/wallpaper_manager_test_utils.cc
@@ -0,0 +1,209 @@
+// 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 "chrome/browser/chromeos/login/wallpaper_manager_test_utils.h"
+
+#include "ash/ash_switches.h"
+#include "base/command_line.h"
+#include "base/file_util.h"
+#include "base/files/file_path.h"
+#include "base/files/scoped_temp_dir.h"
+#include "base/logging.h"
+#include "base/run_loop.h"
+#include "base/time/time.h"
+#include "chrome/browser/chromeos/login/wallpaper_manager.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "ui/gfx/codec/jpeg_codec.h"
+#include "ui/gfx/point.h"
+#include "ui/gfx/rect.h"
+
+namespace chromeos {
+
+namespace {
+
+class TestWallpaperObserverPendingListEmpty
+    : public WallpaperManager::Observer {
+ public:
+  explicit TestWallpaperObserverPendingListEmpty(
+      WallpaperManager* wallpaper_manager)
+      : empty_(false), wallpaper_manager_(wallpaper_manager) {
+    DCHECK(wallpaper_manager_);
+    wallpaper_manager_->AddObserver(this);
+  }
+
+  virtual ~TestWallpaperObserverPendingListEmpty() {
+    wallpaper_manager_->RemoveObserver(this);
+  }
+
+  virtual void OnWallpaperAnimationFinished(
+      const std::string& user_id) OVERRIDE {}
+
+  virtual void OnPendingListEmptyForTesting() OVERRIDE {
+    empty_ = true;
+    base::MessageLoop::current()->Quit();
+  }
+
+  void WaitForPendingListEmpty() {
+    if (wallpaper_manager_->GetPendingListSizeForTesting() == 0) {
+      empty_ = true;
+      return;
+    }
+    while (!empty_)
+      base::RunLoop().Run();
+  }
+
+ private:
+  bool empty_;
+  WallpaperManager* wallpaper_manager_;
+
+  DISALLOW_COPY_AND_ASSIGN(TestWallpaperObserverPendingListEmpty);
+};
+
+}  // namespace
+
+namespace wallpaper_manager_test_utils {
+
+const SkColor kLargeDefaultWallpaperColor = SK_ColorRED;
+const SkColor kSmallDefaultWallpaperColor = SK_ColorGREEN;
+const SkColor kLargeGuestWallpaperColor = SK_ColorBLUE;
+const SkColor kSmallGuestWallpaperColor = SK_ColorYELLOW;
+
+const SkColor kCustomWallpaperColor = SK_ColorMAGENTA;
+
+const int kWallpaperSize = 2;
+
+bool CreateJPEGImage(int width,
+                     int height,
+                     SkColor color,
+                     std::vector<unsigned char>* output) {
+  SkBitmap bitmap;
+  bitmap.setConfig(SkBitmap::kARGB_8888_Config, width, height, 0);
+  bitmap.allocPixels();
+  bitmap.eraseColor(color);
+
+  const int kQuality = 80;
+  if (!gfx::JPEGCodec::Encode(
+          static_cast<const unsigned char*>(bitmap.getPixels()),
+          gfx::JPEGCodec::FORMAT_SkBitmap,
+          width,
+          height,
+          bitmap.rowBytes(),
+          kQuality,
+          output)) {
+    LOG(ERROR) << "Unable to encode " << width << "x" << height << " bitmap";
+    return false;
+  }
+  return true;
+}
+
+gfx::ImageSkia CreateTestImage(int width, int height, SkColor color) {
+  SkBitmap bitmap;
+  bitmap.setConfig(SkBitmap::kARGB_8888_Config, width, height);
+  bitmap.allocPixels();
+  bitmap.eraseColor(color);
+  return gfx::ImageSkia::CreateFrom1xBitmap(bitmap);
+}
+
+bool WriteJPEGFile(const base::FilePath& path,
+                   int width,
+                   int height,
+                   SkColor color) {
+  std::vector<unsigned char> output;
+  if (!CreateJPEGImage(width, height, color, &output))
+    return false;
+
+  size_t bytes_written = base::WriteFile(
+      path, reinterpret_cast<const char*>(&output[0]), output.size());
+  if (bytes_written != output.size()) {
+    LOG(ERROR) << "Wrote " << bytes_written << " byte(s) instead of "
+               << output.size() << " to " << path.value();
+    return false;
+  }
+  return true;
+}
+
+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;
+  }
+
+  bitmap->lockPixels();
+  gfx::Point center = gfx::Rect(image.size()).CenterPoint();
+  SkColor image_color = bitmap->getColor(center.x(), center.y());
+  bitmap->unlockPixels();
+
+  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;
+}
+
+void WaitAsyncWallpaperLoadFinished() {
+  TestWallpaperObserverPendingListEmpty observer(WallpaperManager::Get());
+  observer.WaitForPendingListEmpty();
+}
+
+void CreateCmdlineWallpapers(const base::ScopedTempDir& dir,
+                             scoped_ptr<base::CommandLine>* command_line) {
+  std::vector<std::string> options;
+  options.push_back(std::string("WM_Test_cmdline"));
+  const base::FilePath small_file =
+      dir.path().Append(FILE_PATH_LITERAL("small.jpg"));
+  options.push_back(std::string("--") +
+                    ash::switches::kAshDefaultWallpaperSmall + "=" +
+                    small_file.value());
+  const base::FilePath large_file =
+      dir.path().Append(FILE_PATH_LITERAL("large.jpg"));
+  options.push_back(std::string("--") +
+                    ash::switches::kAshDefaultWallpaperLarge + "=" +
+                    large_file.value());
+
+  const base::FilePath guest_small_file =
+      dir.path().Append(FILE_PATH_LITERAL("guest_small.jpg"));
+  options.push_back(std::string("--") + ash::switches::kAshGuestWallpaperSmall +
+                    "=" + guest_small_file.value());
+  const base::FilePath guest_large_file =
+      dir.path().Append(FILE_PATH_LITERAL("guest_large.jpg"));
+  options.push_back(std::string("--") + ash::switches::kAshGuestWallpaperLarge +
+                    "=" + guest_large_file.value());
+
+  ASSERT_TRUE(WriteJPEGFile(
+      small_file, kWallpaperSize, kWallpaperSize, kSmallDefaultWallpaperColor));
+  ASSERT_TRUE(WriteJPEGFile(
+      large_file, kWallpaperSize, kWallpaperSize, kLargeDefaultWallpaperColor));
+
+  ASSERT_TRUE(WriteJPEGFile(guest_small_file,
+                            kWallpaperSize,
+                            kWallpaperSize,
+                            kSmallGuestWallpaperColor));
+  ASSERT_TRUE(WriteJPEGFile(guest_large_file,
+                            kWallpaperSize,
+                            kWallpaperSize,
+                            kLargeGuestWallpaperColor));
+
+  command_line->reset(new base::CommandLine(options));
+  WallpaperManager::Get()->SetCommandLineForTesting(command_line->get());
+}
+
+}  // namespace wallpaper_manager_test_utils
+
+}  // namespace chromeos
diff --git a/chrome/browser/chromeos/login/wallpaper_manager_test_utils.h b/chrome/browser/chromeos/login/wallpaper_manager_test_utils.h
new file mode 100644
index 0000000..3ee84b3
--- /dev/null
+++ b/chrome/browser/chromeos/login/wallpaper_manager_test_utils.h
@@ -0,0 +1,73 @@
+// 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.
+
+#ifndef CHROME_BROWSER_CHROMEOS_LOGIN_WALLPAPER_MANAGER_TEST_UTILS_H_
+#define CHROME_BROWSER_CHROMEOS_LOGIN_WALLPAPER_MANAGER_TEST_UTILS_H_
+
+#include <vector>
+
+#include "ash/ash_constants.h"
+#include "ui/gfx/image/image_skia.h"
+
+namespace base {
+class CommandLine;
+class ScopedTempDir;
+}  // namespace base
+
+namespace chromeos {
+namespace wallpaper_manager_test_utils {
+
+// Colors used for different default wallpapers by CreateCmdlineWallpapers().
+extern const SkColor kLargeDefaultWallpaperColor;
+extern const SkColor kSmallDefaultWallpaperColor;
+extern const SkColor kLargeGuestWallpaperColor;
+extern const SkColor kSmallGuestWallpaperColor;
+
+// A custom color, specifically chosen to not
+// conflict with any of the default wallpaper colors.
+extern const SkColor kCustomWallpaperColor;
+
+// 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.
+extern const int kWallpaperSize;
+
+// Creates compressed JPEG image of solid color.
+// Result bytes are written to |output|.
+// Returns true if gfx::JPEGCodec::Encode() succeeds.
+bool CreateJPEGImage(int width,
+                     int height,
+                     SkColor color,
+                     std::vector<unsigned char>* output);
+
+// Creates a test image of given size.
+gfx::ImageSkia CreateTestImage(int width, int height, SkColor color);
+
+// Writes a JPEG image of the specified size and color to |path|. Returns
+// true on success.
+bool WriteJPEGFile(const base::FilePath& path,
+                   int width,
+                   int height,
+                   SkColor color);
+
+// 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);
+
+// Wait until all wallpaper loading is done, and WallpaperManager comes into
+// a stable state.
+void WaitAsyncWallpaperLoadFinished();
+
+// Initializes default wallpaper paths "*default_*file" and writes JPEG
+// wallpaper images to them.
+// Only needs to be called (once) by tests that want to test loading of
+// default wallpapers.
+void CreateCmdlineWallpapers(const base::ScopedTempDir& dir,
+                             scoped_ptr<base::CommandLine>* command_line);
+
+}  // namespace wallpaper_manager_test_utils
+}  // namespace chromeos
+
+#endif  // CHROME_BROWSER_CHROMEOS_LOGIN_WALLPAPER_MANAGER_TEST_UTILS_H_
diff --git a/chrome/chrome_tests.gypi b/chrome/chrome_tests.gypi
index 0d9e8ac9..249fbd17 100644
--- a/chrome/chrome_tests.gypi
+++ b/chrome/chrome_tests.gypi
@@ -289,6 +289,8 @@
             'browser/chromeos/login/screen_locker_tester.cc',
             'browser/chromeos/login/screen_locker_tester.h',
             'browser/chromeos/login/wallpaper_manager_browsertest.cc',
+            'browser/chromeos/login/wallpaper_manager_test_utils.cc',
+            'browser/chromeos/login/wallpaper_manager_test_utils.h',
             'test/data/chromeos/service_login.html',
           ],
           'sources!': [
@@ -881,6 +883,7 @@
         'browser/chromeos/app_mode/kiosk_app_manager_browsertest.cc',
         'browser/chromeos/app_mode/kiosk_app_update_service_browsertest.cc',
         'browser/chromeos/attestation/attestation_policy_browsertest.cc',
+        'browser/chromeos/customization_wallpaper_downloader_browsertest.cc',
         'browser/chromeos/device/input_service_proxy_browsertest.cc',
         'browser/chromeos/drive/drive_integration_service_browsertest.cc',
         'browser/chromeos/drive/drive_notification_manager_factory_browsertest.cc',
@@ -962,6 +965,8 @@
         'browser/chromeos/login/user_image_manager_test_util.cc',
         'browser/chromeos/login/user_image_manager_test_util.h',
         'browser/chromeos/login/wallpaper_manager_policy_browsertest.cc',
+        'browser/chromeos/login/wallpaper_manager_test_utils.cc',
+        'browser/chromeos/login/wallpaper_manager_test_utils.h',
         'browser/chromeos/login/wizard_controller_browsertest.cc',
         'browser/chromeos/login/wizard_in_process_browser_test.cc',
         'browser/chromeos/login/wizard_in_process_browser_test.h',
diff --git a/net/url_request/test_url_fetcher_factory.cc b/net/url_request/test_url_fetcher_factory.cc
index d6c6611..bd4f5e1 100644
--- a/net/url_request/test_url_fetcher_factory.cc
+++ b/net/url_request/test_url_fetcher_factory.cc
@@ -8,8 +8,10 @@
 
 #include "base/bind.h"
 #include "base/compiler_specific.h"
+#include "base/file_util.h"
 #include "base/memory/weak_ptr.h"
 #include "base/message_loop/message_loop.h"
+#include "base/threading/thread_restrictions.h"
 #include "net/base/host_port_pair.h"
 #include "net/base/io_buffer.h"
 #include "net/base/net_errors.h"
@@ -143,6 +145,12 @@
 void TestURLFetcher::SaveResponseToFileAtPath(
     const base::FilePath& file_path,
     scoped_refptr<base::SequencedTaskRunner> file_task_runner) {
+  SetResponseFilePath(file_path);
+  // Asynchronous IO is not supported, so file_task_runner is ignored.
+  base::ThreadRestrictions::ScopedAllowIO allow_io;
+  const size_t written_bytes = base::WriteFile(
+      file_path, fake_response_string_.c_str(), fake_response_string_.size());
+  DCHECK_EQ(written_bytes, fake_response_string_.size());
 }
 
 void TestURLFetcher::SaveResponseToTemporaryFile(
@@ -151,6 +159,12 @@
 
 void TestURLFetcher::SaveResponseWithWriter(
     scoped_ptr<URLFetcherResponseWriter> response_writer) {
+  // In class URLFetcherCore this method is called by all three:
+  // GetResponseAsString() / SaveResponseToFileAtPath() /
+  // SaveResponseToTemporaryFile(). But here (in TestURLFetcher), this method
+  // is never used by any of these three methods. So, file writing is expected
+  // to be done in SaveResponseToFileAtPath(), and this method supports only
+  // URLFetcherStringWriter (for testing of this method only).
   if (fake_response_destination_ == STRING) {
     response_writer_ = response_writer.Pass();
     int response = response_writer_->Initialize(CompletionCallback());
@@ -164,8 +178,13 @@
     DCHECK_EQ(static_cast<int>(fake_response_string_.size()), response);
     response = response_writer_->Finish(CompletionCallback());
     DCHECK_EQ(OK, response);
-  } else {
+  } else if (fake_response_destination_ == TEMP_FILE) {
+    // SaveResponseToFileAtPath() should be called instead of this method to
+    // save file. Asynchronous file writing using URLFetcherFileWriter is not
+    // supported.
     NOTIMPLEMENTED();
+  } else {
+    NOTREACHED();
   }
 }