Chromium Code Reviews| Index: chrome/browser/android/webapps/add_to_homescreen_data_fetcher_unittest.cc |
| diff --git a/chrome/browser/android/webapps/add_to_homescreen_data_fetcher_unittest.cc b/chrome/browser/android/webapps/add_to_homescreen_data_fetcher_unittest.cc |
| new file mode 100644 |
| index 0000000000000000000000000000000000000000..c89758fce24918098e8e65351d8efcd8b5c4f670 |
| --- /dev/null |
| +++ b/chrome/browser/android/webapps/add_to_homescreen_data_fetcher_unittest.cc |
| @@ -0,0 +1,297 @@ |
| +// Copyright 2016 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/android/webapps/add_to_homescreen_data_fetcher.h" |
| + |
| +#include <memory> |
| +#include <string> |
| + |
| +#include "base/callback_forward.h" |
| +#include "base/files/file_path.h" |
| +#include "base/macros.h" |
| +#include "base/memory/ref_counted.h" |
| +#include "base/message_loop/message_loop.h" |
| +#include "base/run_loop.h" |
| +#include "base/strings/nullable_string16.h" |
| +#include "base/strings/utf_string_conversions.h" |
| +#include "base/time/time.h" |
| +#include "chrome/common/web_application_info.h" |
| +#include "chrome/test/base/chrome_render_view_host_test_harness.h" |
| +#include "chrome/test/base/testing_profile.h" |
| +#include "content/browser/service_worker/embedded_worker_test_helper.h" |
| +#include "content/browser/service_worker/service_worker_context_core.h" |
| +#include "content/common/service_worker/service_worker_status_code.h" |
| +#include "content/public/browser/site_instance.h" |
| +#include "content/public/browser/web_contents.h" |
| +#include "content/public/common/manifest.h" |
| +#include "content/test/test_web_contents.h" |
| +#include "net/http/http_status_code.h" |
| +#include "third_party/WebKit/public/platform/WebDisplayMode.h" |
| +#include "ui/gfx/image/image_unittest_util.h" |
| +#include "url/gurl.h" |
| + |
| +namespace { |
| + |
| +const char* kDefaultManifestUrl = "https://www.example.com/manifest.json"; |
| +const char* kDefaultManifestName = "Default Name"; |
| +const char* kDefaultManifestShortName = "Default Short Name"; |
| +const char* kDefaultStartUrl = "https://www.example.com/index.html"; |
| +const blink::WebDisplayMode kDefaultManifestDisplayMode = |
| + blink::WebDisplayModeStandalone; |
| + |
| +// WebContents subclass which mocks out image and manifest fetching. |
| +class MockWebContents : public content::TestWebContents { |
| + public: |
| + explicit MockWebContents(content::BrowserContext* browser_context) |
| + : content::TestWebContents(browser_context) {} |
| + |
| + ~MockWebContents() override { |
| + } |
| + |
| + // Sets the manifest to be returned by GetManifest(). |
| + // |fetch_delay_ms| is the time in milliseconds that the simulated fetch of |
| + // the web manifest should take. |
| + void SetManifest(const GURL& manifest_url, |
| + const content::Manifest& manifest, |
| + int fetch_delay_ms) { |
| + manifest_url_ = manifest_url; |
| + manifest_ = manifest; |
| + manifest_fetch_delay_ms_ = fetch_delay_ms; |
| + } |
| + |
| + int DownloadImage(const GURL& url, |
| + bool is_favicon, |
|
dominickn
2016/09/26 07:36:07
Nit: indentation
|
| + uint32_t max_bitmap_size, |
| + bool bypass_cache, |
| + const ImageDownloadCallback& callback) override { |
| + const int kIconSizePx = 144; |
| + SkBitmap icon = gfx::test::CreateBitmap(kIconSizePx, kIconSizePx); |
| + std::vector<SkBitmap> icons(1u, icon); |
| + std::vector<gfx::Size> pixel_sizes(1u, gfx::Size(kIconSizePx, kIconSizePx)); |
| + base::MessageLoopForUI::current()->PostTask( |
| + FROM_HERE, |
| + base::Bind(callback, 0, net::HTTP_OK, url, icons, pixel_sizes)); |
| + return 0; |
| + } |
| + |
| + void GetManifest(const GetManifestCallback& callback) override { |
| + base::MessageLoopForUI::current()->PostDelayedTask( |
| + FROM_HERE, base::Bind(callback, manifest_url_, manifest_), |
| + base::TimeDelta::FromMilliseconds(manifest_fetch_delay_ms_)); |
| + } |
| + |
| + private: |
| + GURL manifest_url_; |
| + content::Manifest manifest_; |
| + int manifest_fetch_delay_ms_; |
| + |
| + DISALLOW_COPY_AND_ASSIGN(MockWebContents); |
| +}; |
| + |
| +// Tracks which of the AddToHomescreenDataFetcher::Observer callbacks have been |
| +// called. |
| +class ObserverWaiter : public AddToHomescreenDataFetcher::Observer { |
| + public: |
| + ObserverWaiter() {} |
| + ~ObserverWaiter() override {} |
| + |
| + // Waits till the OnDataAvailable() callback is called. |
| + void WaitForDataAvailable() { |
| + if (data_available_) |
| + return; |
| + |
| + base::RunLoop run_loop; |
| + quit_closure_ = run_loop.QuitClosure(); |
| + run_loop.Run(); |
| + } |
| + |
| + void OnDidDetermineWebApkCompatibility(bool is_webapk_compatible) override { |
| + determined_webapk_compatibility = true; |
| + is_webapk_compatible = is_webapk_compatible; |
| + } |
| + |
| + void OnUserTitleAvailable(const base::string16& title) override { |
| + title_available = true; |
| + } |
| + |
| + SkBitmap FinalizeLauncherIconInBackground(const SkBitmap& icon, |
| + const GURL& url, |
| + bool* is_generated) override { |
| + *is_generated = false; |
| + return icon; |
| + } |
| + |
| + void OnDataAvailable(const ShortcutInfo& info, |
| + const SkBitmap& icon) override { |
| + data_available_ = true; |
| + if (!quit_closure_.is_null()) |
| + quit_closure_.Run(); |
| + } |
| + |
| + bool is_webapk_compatible; |
| + bool determined_webapk_compatibility; |
| + bool title_available; |
| + |
| + private: |
| + bool data_available_; |
| + base::Closure quit_closure_; |
| + |
| + DISALLOW_COPY_AND_ASSIGN(ObserverWaiter); |
| +}; |
| + |
| +// Builds non-null base::NullableString16 from a UTF8 string. |
| +base::NullableString16 NullableStringFromUTF8(const std::string& value) { |
| + return base::NullableString16(base::UTF8ToUTF16(value), false); |
| +} |
| + |
| +// Builds WebAPK compatible content::Manifest. |
| +content::Manifest BuildDefaultManifest() { |
| + content::Manifest manifest; |
| + manifest.name = NullableStringFromUTF8(kDefaultManifestName); |
| + manifest.short_name = NullableStringFromUTF8(kDefaultManifestShortName); |
| + manifest.start_url = GURL(kDefaultStartUrl); |
| + manifest.display = kDefaultManifestDisplayMode; |
| + return manifest; |
| +} |
| + |
| +} // anonymous namespace |
| + |
| +// Tests AddToHomescreenDataFetcher. These tests should be browser tests but |
| +// Android does not support browser tests yet (crbug.com/611756). |
| +class AddToHomescreenDataFetcherTest : public ChromeRenderViewHostTestHarness { |
| + public: |
| + AddToHomescreenDataFetcherTest() {} |
| + ~AddToHomescreenDataFetcherTest() override {} |
| + |
| + void SetUp() override { |
| + ChromeRenderViewHostTestHarness::SetUp(); |
| + |
| + ASSERT_TRUE(profile()->CreateHistoryService(false, true)); |
| + profile()->CreateFaviconService(); |
| + |
| + embedded_worker_test_helper_.reset( |
| + new content::EmbeddedWorkerTestHelper(base::FilePath())); |
| + |
| + scoped_refptr<content::SiteInstance> site_instance = |
| + content::SiteInstance::Create(browser_context()); |
| + site_instance->GetProcess()->Init(); |
| + MockWebContents* mock_web_contents = new MockWebContents(browser_context()); |
| + mock_web_contents->Init(content::WebContents::CreateParams( |
| + browser_context(), std::move(site_instance))); |
| + SetContents(mock_web_contents); |
| + } |
| + |
| + scoped_refptr<AddToHomescreenDataFetcher> BuildFetcher( |
| + bool check_webapk_compatible, |
| + AddToHomescreenDataFetcher::Observer* observer) { |
| + return new AddToHomescreenDataFetcher(web_contents(), 1, 1, 1, 1, |
| + check_webapk_compatible, observer); |
| + } |
| + |
| + // Set the manifest to be returned as a result of WebContents::GetManifest(). |
| + void SetManifest(const GURL& manifest_url, |
| + const content::Manifest& manifest, |
| + int fetch_delay_ms) { |
| + MockWebContents* mock_web_contents = |
| + static_cast<MockWebContents*>(web_contents()); |
| + mock_web_contents->SetManifest(manifest_url, manifest, fetch_delay_ms); |
| + } |
| + |
| + // Registers service worker at |url|. Blocks till the service worker is |
| + // registered. |
| + void RegisterServiceWorker(const GURL& url) { |
| + base::RunLoop run_loop; |
| + embedded_worker_test_helper_->context()->RegisterServiceWorker( |
| + url, GURL(url.spec() + "/service_worker.js"), nullptr, |
| + base::Bind(&AddToHomescreenDataFetcherTest::OnServiceWorkerRegistered, |
| + base::Unretained(this), run_loop.QuitClosure())); |
| + } |
| + |
| + private: |
| + // Callback for RegisterServiceWorker() for when service worker registration |
| + // has completed. |
| + void OnServiceWorkerRegistered(const base::Closure& callback, |
| + content::ServiceWorkerStatusCode status, |
| + const std::string& status_message, |
| + int64_t registration_id) { |
| + ASSERT_EQ(content::SERVICE_WORKER_OK, status) |
| + << content::ServiceWorkerStatusToString(status); |
| + callback.Run(); |
| + } |
| + |
| + scoped_refptr<AddToHomescreenDataFetcher> fetcher_; |
| + std::unique_ptr<ObserverWaiter> waiter_; |
| + std::unique_ptr<content::EmbeddedWorkerTestHelper> |
| + embedded_worker_test_helper_; |
| + |
| + DISALLOW_COPY_AND_ASSIGN(AddToHomescreenDataFetcherTest); |
| +}; |
| + |
| +// Checks that AddToHomescreenDataFetcher::Observer::OnUserTitleAvailable() is |
| +// called when the web manifest fetch times out. The add-to-homescreen dialog |
| +// makes dialog's text field editable once OnUserTitleAvailable() is called. |
| +TEST_F(AddToHomescreenDataFetcherTest, ManifestFetchTimesOutNoServiceWorker) { |
| + SetManifest(GURL(kDefaultManifestUrl), BuildDefaultManifest(), 10000); |
| + |
| + ObserverWaiter waiter; |
| + scoped_refptr<AddToHomescreenDataFetcher> fetcher( |
| + BuildFetcher(false, &waiter)); |
| + fetcher->OnDidGetWebApplicationInfo(WebApplicationInfo()); |
| + waiter.WaitForDataAvailable(); |
| + |
| + EXPECT_FALSE(waiter.determined_webapk_compatibility); |
| + EXPECT_TRUE(waiter.title_available); |
| + |
| + fetcher->set_weak_observer(nullptr); |
| +} |
| + |
| +// Class for tests which should be run with AddToHomescreenDataFetcher built |
| +// with both true and false values of |check_webapk_compatible|. |
| +class AddToHomescreenDataFetcherTestCommon |
| + : public AddToHomescreenDataFetcherTest, |
| + public testing::WithParamInterface<bool> { |
| + public: |
| + AddToHomescreenDataFetcherTestCommon() {} |
| + ~AddToHomescreenDataFetcherTestCommon() override {} |
| + |
| + scoped_refptr<AddToHomescreenDataFetcher> BuildFetcher( |
| + AddToHomescreenDataFetcher::Observer* observer) { |
| + return AddToHomescreenDataFetcherTest::BuildFetcher( |
| + check_webapk_compatibility(), observer); |
| + } |
| + |
| + // The value of |check_webapk_compatible| used when building the |
| + // AddToHomescreenDataFetcher. |
| + bool check_webapk_compatibility() { return GetParam(); } |
| + |
| + private: |
| + DISALLOW_COPY_AND_ASSIGN(AddToHomescreenDataFetcherTestCommon); |
| +}; |
| + |
| +// Checks that the AddToHomescreenDataFetcher::Observer callbacks are called |
| +// when a service worker is registered and the manifest fetch times out. |
| +TEST_P(AddToHomescreenDataFetcherTestCommon, ManifestFetchTimesOut) { |
| + RegisterServiceWorker(GURL(kDefaultStartUrl)); |
| + SetManifest(GURL(kDefaultManifestUrl), BuildDefaultManifest(), 10000); |
| + |
| + ObserverWaiter waiter; |
| + scoped_refptr<AddToHomescreenDataFetcher> fetcher(BuildFetcher(&waiter)); |
| + fetcher->OnDidGetWebApplicationInfo(WebApplicationInfo()); |
| + waiter.WaitForDataAvailable(); |
| + |
| + if (check_webapk_compatibility()) { |
| + EXPECT_TRUE(waiter.determined_webapk_compatibility); |
| + EXPECT_FALSE(waiter.is_webapk_compatible); |
| + } else { |
| + EXPECT_FALSE(waiter.determined_webapk_compatibility); |
| + } |
| + // This callback enables the text field in the add-to-homescreen dialog. |
| + EXPECT_TRUE(waiter.title_available); |
| + |
| + fetcher->set_weak_observer(nullptr); |
| +} |
| + |
| +INSTANTIATE_TEST_CASE_P(CheckWebApkCompatibility, |
| + AddToHomescreenDataFetcherTestCommon, |
| + ::testing::Values(false, true)); |