1// Copyright 2014 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5
6#include <vector>
7
8#include "ash/desktop_background/desktop_background_controller.h"
9#include "ash/shell.h"
10#include "base/command_line.h"
11#include "base/files/scoped_temp_dir.h"
12#include "base/run_loop.h"
13#include "base/time/time.h"
14#include "chrome/browser/chromeos/customization_document.h"
15#include "chrome/browser/chromeos/customization_wallpaper_downloader.h"
16#include "chrome/browser/chromeos/login/users/wallpaper/wallpaper_manager.h"
17#include "chrome/browser/chromeos/login/users/wallpaper/wallpaper_manager_test_utils.h"
18#include "chrome/test/base/in_process_browser_test.h"
19#include "chrome/test/base/testing_browser_process.h"
20#include "chromeos/chromeos_switches.h"
21#include "components/google/core/browser/google_url_tracker.h"
22#include "net/http/http_response_headers.h"
23#include "net/http/http_status_code.h"
24#include "net/url_request/test_url_fetcher_factory.h"
25#include "net/url_request/url_fetcher_impl.h"
26#include "testing/gtest/include/gtest/gtest.h"
27
28namespace chromeos {
29
30namespace {
31
32const char kOEMWallpaperURL[] = "http://somedomain.com/image.png";
33
34const char kServicesManifest[] =
35    "{"
36    "  \"version\": \"1.0\","
37    "  \"default_wallpaper\": \"http://somedomain.com/image.png\",\n"
38    "  \"default_apps\": [\n"
39    "    \"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\",\n"
40    "    \"bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb\"\n"
41    "  ],\n"
42    "  \"localized_content\": {\n"
43    "    \"en-US\": {\n"
44    "      \"default_apps_folder_name\": \"EN-US OEM Name\"\n"
45    "    },\n"
46    "    \"en\": {\n"
47    "      \"default_apps_folder_name\": \"EN OEM Name\"\n"
48    "    },\n"
49    "    \"default\": {\n"
50    "      \"default_apps_folder_name\": \"Default OEM Name\"\n"
51    "    }\n"
52    "  }\n"
53    "}";
54
55// Expected minimal wallpaper download retry interval in milliseconds.
56const int kDownloadRetryIntervalMS = 100;
57
58class TestWallpaperObserver : public WallpaperManager::Observer {
59 public:
60  explicit TestWallpaperObserver(WallpaperManager* wallpaper_manager)
61      : finished_(false),
62        wallpaper_manager_(wallpaper_manager) {
63    DCHECK(wallpaper_manager_);
64    wallpaper_manager_->AddObserver(this);
65  }
66
67  virtual ~TestWallpaperObserver() {
68    wallpaper_manager_->RemoveObserver(this);
69  }
70
71  virtual void OnWallpaperAnimationFinished(const std::string&) OVERRIDE {
72    finished_ = true;
73    base::MessageLoop::current()->Quit();
74  }
75
76  void WaitForWallpaperAnimationFinished() {
77    while (!finished_)
78      base::RunLoop().Run();
79  }
80
81 private:
82  bool finished_;
83  WallpaperManager* wallpaper_manager_;
84
85  DISALLOW_COPY_AND_ASSIGN(TestWallpaperObserver);
86};
87
88}  // namespace
89
90// This is helper class for net::FakeURLFetcherFactory.
91class TestWallpaperImageURLFetcherCallback {
92 public:
93  TestWallpaperImageURLFetcherCallback(
94      const GURL& url,
95      const size_t require_retries,
96      const std::vector<unsigned char>& jpeg_data_raw)
97      : url_(url),
98        require_retries_(require_retries),
99        factory_(NULL) {
100    jpeg_data_.resize(jpeg_data_raw.size());
101    std::copy(jpeg_data_raw.begin(), jpeg_data_raw.end(), jpeg_data_.begin());
102  }
103
104  scoped_ptr<net::FakeURLFetcher> CreateURLFetcher(
105      const GURL& url,
106      net::URLFetcherDelegate* delegate,
107      const std::string& response_data,
108      net::HttpStatusCode response_code,
109      net::URLRequestStatus::Status status) {
110    chromeos::ServicesCustomizationDocument* customization =
111        chromeos::ServicesCustomizationDocument::GetInstance();
112    customization->wallpaper_downloader_for_testing()
113        ->set_retry_delay_for_testing(
114            base::TimeDelta::FromMilliseconds(kDownloadRetryIntervalMS));
115
116    attempts_.push_back(base::TimeTicks::Now());
117    if (attempts_.size() > 1) {
118      const int retry = num_attempts() - 1;
119      const base::TimeDelta current_delay =
120          customization->wallpaper_downloader_for_testing()
121              ->retry_current_delay_for_testing();
122      const double base_interval = base::TimeDelta::FromMilliseconds(
123                                       kDownloadRetryIntervalMS).InSecondsF();
124      EXPECT_GE(current_delay,
125                base::TimeDelta::FromSecondsD(base_interval * retry * retry))
126          << "Retry too fast. Actual interval " << current_delay.InSecondsF()
127          << " seconds, but expected at least " << base_interval
128          << " * (retry=" << retry
129          << " * retry)= " << base_interval * retry * retry << " seconds.";
130    }
131    if (attempts_.size() > require_retries_) {
132      response_code = net::HTTP_OK;
133      status = net::URLRequestStatus::SUCCESS;
134      factory_->SetFakeResponse(url, response_data, response_code, status);
135    }
136    scoped_ptr<net::FakeURLFetcher> fetcher(new net::FakeURLFetcher(
137        url, delegate, response_data, response_code, status));
138    scoped_refptr<net::HttpResponseHeaders> download_headers =
139        new net::HttpResponseHeaders(std::string());
140    download_headers->AddHeader("Content-Type: image/jpeg");
141    fetcher->set_response_headers(download_headers);
142    return fetcher.Pass();
143  }
144
145  void Initialize(net::FakeURLFetcherFactory* factory) {
146    factory_ = factory;
147    factory_->SetFakeResponse(url_,
148                              jpeg_data_,
149                              net::HTTP_INTERNAL_SERVER_ERROR,
150                              net::URLRequestStatus::FAILED);
151  }
152
153  size_t num_attempts() const { return attempts_.size(); }
154
155 private:
156  const GURL url_;
157  // Respond with OK on required retry attempt.
158  const size_t require_retries_;
159  net::FakeURLFetcherFactory* factory_;
160  std::vector<base::TimeTicks> attempts_;
161  std::string jpeg_data_;
162
163  DISALLOW_COPY_AND_ASSIGN(TestWallpaperImageURLFetcherCallback);
164};
165
166// This implements fake remote source for wallpaper image.
167// JPEG image is created here and served to CustomizationWallpaperDownloader
168// via net::FakeURLFetcher.
169class WallpaperImageFetcherFactory {
170 public:
171  WallpaperImageFetcherFactory(const GURL& url,
172                               int width,
173                               int height,
174                               SkColor color,
175                               const size_t require_retries) {
176    // ASSERT_TRUE() cannot be directly used in constructor.
177    Initialize(url, width, height, color, require_retries);
178  }
179
180  ~WallpaperImageFetcherFactory() {
181    fetcher_factory_.reset();
182    net::URLFetcherImpl::set_factory(fallback_fetcher_factory_.get());
183    fallback_fetcher_factory_.reset();
184  }
185
186  size_t num_attempts() const { return url_callback_->num_attempts(); }
187
188 private:
189  void Initialize(const GURL& url,
190                  int width,
191                  int height,
192                  SkColor color,
193                  const size_t require_retries) {
194    std::vector<unsigned char> oem_wallpaper_;
195    ASSERT_TRUE(wallpaper_manager_test_utils::CreateJPEGImage(
196        width, height, color, &oem_wallpaper_));
197
198    url_callback_.reset(new TestWallpaperImageURLFetcherCallback(
199        url, require_retries, oem_wallpaper_));
200    fallback_fetcher_factory_.reset(new net::TestURLFetcherFactory);
201    net::URLFetcherImpl::set_factory(NULL);
202    fetcher_factory_.reset(new net::FakeURLFetcherFactory(
203        fallback_fetcher_factory_.get(),
204        base::Bind(&TestWallpaperImageURLFetcherCallback::CreateURLFetcher,
205                   base::Unretained(url_callback_.get()))));
206    url_callback_->Initialize(fetcher_factory_.get());
207  }
208
209  scoped_ptr<TestWallpaperImageURLFetcherCallback> url_callback_;
210
211  // Use a test factory as a fallback so we don't have to deal with other
212  // requests.
213  scoped_ptr<net::TestURLFetcherFactory> fallback_fetcher_factory_;
214  scoped_ptr<net::FakeURLFetcherFactory> fetcher_factory_;
215
216  DISALLOW_COPY_AND_ASSIGN(WallpaperImageFetcherFactory);
217};
218
219class CustomizationWallpaperDownloaderBrowserTest
220    : public InProcessBrowserTest {
221 public:
222  CustomizationWallpaperDownloaderBrowserTest()
223      : controller_(NULL),
224        local_state_(NULL) {
225  }
226
227  virtual ~CustomizationWallpaperDownloaderBrowserTest() {}
228
229  virtual void SetUpOnMainThread() OVERRIDE {
230    controller_ = ash::Shell::GetInstance()->desktop_background_controller();
231    local_state_ = g_browser_process->local_state();
232  }
233
234  virtual void SetUpCommandLine(CommandLine* command_line) OVERRIDE {
235    command_line->AppendSwitch(chromeos::switches::kLoginManager);
236    command_line->AppendSwitchASCII(chromeos::switches::kLoginProfile, "user");
237  }
238
239  virtual void TearDownOnMainThread() OVERRIDE { controller_ = NULL; }
240
241 protected:
242  void CreateCmdlineWallpapers() {
243    cmdline_wallpaper_dir_.reset(new base::ScopedTempDir);
244    ASSERT_TRUE(cmdline_wallpaper_dir_->CreateUniqueTempDir());
245    wallpaper_manager_test_utils::CreateCmdlineWallpapers(
246        *cmdline_wallpaper_dir_, &wallpaper_manager_command_line_);
247  }
248
249  ash::DesktopBackgroundController* controller_;
250  PrefService* local_state_;
251  scoped_ptr<base::CommandLine> wallpaper_manager_command_line_;
252
253  // Directory created by CreateCmdlineWallpapersAndSetFlags() to store default
254  // wallpaper images.
255  scoped_ptr<base::ScopedTempDir> cmdline_wallpaper_dir_;
256
257 private:
258  DISALLOW_COPY_AND_ASSIGN(CustomizationWallpaperDownloaderBrowserTest);
259};
260
261IN_PROC_BROWSER_TEST_F(CustomizationWallpaperDownloaderBrowserTest,
262                       OEMWallpaperIsPresent) {
263  CreateCmdlineWallpapers();
264  WallpaperManager::Get()->SetDefaultWallpaperNow(std::string());
265  wallpaper_manager_test_utils::WaitAsyncWallpaperLoadFinished();
266  EXPECT_TRUE(wallpaper_manager_test_utils::ImageIsNearColor(
267      controller_->GetWallpaper(),
268      wallpaper_manager_test_utils::kSmallDefaultWallpaperColor));
269
270  WallpaperImageFetcherFactory url_factory(
271      GURL(kOEMWallpaperURL),
272      wallpaper_manager_test_utils::kWallpaperSize,
273      wallpaper_manager_test_utils::kWallpaperSize,
274      wallpaper_manager_test_utils::kCustomWallpaperColor,
275      0 /* require_retries */);
276
277  TestWallpaperObserver observer(WallpaperManager::Get());
278  chromeos::ServicesCustomizationDocument* customization =
279      chromeos::ServicesCustomizationDocument::GetInstance();
280  EXPECT_TRUE(
281      customization->LoadManifestFromString(std::string(kServicesManifest)));
282
283  observer.WaitForWallpaperAnimationFinished();
284  EXPECT_TRUE(wallpaper_manager_test_utils::ImageIsNearColor(
285      controller_->GetWallpaper(),
286      wallpaper_manager_test_utils::kCustomWallpaperColor));
287  EXPECT_EQ(1U, url_factory.num_attempts());
288}
289
290IN_PROC_BROWSER_TEST_F(CustomizationWallpaperDownloaderBrowserTest,
291                       OEMWallpaperRetryFetch) {
292  CreateCmdlineWallpapers();
293  WallpaperManager::Get()->SetDefaultWallpaperNow(std::string());
294  wallpaper_manager_test_utils::WaitAsyncWallpaperLoadFinished();
295  EXPECT_TRUE(wallpaper_manager_test_utils::ImageIsNearColor(
296      controller_->GetWallpaper(),
297      wallpaper_manager_test_utils::kSmallDefaultWallpaperColor));
298
299  WallpaperImageFetcherFactory url_factory(
300      GURL(kOEMWallpaperURL),
301      wallpaper_manager_test_utils::kWallpaperSize,
302      wallpaper_manager_test_utils::kWallpaperSize,
303      wallpaper_manager_test_utils::kCustomWallpaperColor,
304      1 /* require_retries */);
305
306  TestWallpaperObserver observer(WallpaperManager::Get());
307  chromeos::ServicesCustomizationDocument* customization =
308      chromeos::ServicesCustomizationDocument::GetInstance();
309  EXPECT_TRUE(
310      customization->LoadManifestFromString(std::string(kServicesManifest)));
311
312  observer.WaitForWallpaperAnimationFinished();
313  EXPECT_TRUE(wallpaper_manager_test_utils::ImageIsNearColor(
314      controller_->GetWallpaper(),
315      wallpaper_manager_test_utils::kCustomWallpaperColor));
316
317  EXPECT_EQ(2U, url_factory.num_attempts());
318}
319
320}  // namespace chromeos
321