| Index: chrome/browser/ui/app_list/arc/arc_app_unittest.cc | 
| diff --git a/chrome/browser/ui/app_list/arc/arc_app_unittest.cc b/chrome/browser/ui/app_list/arc/arc_app_unittest.cc | 
| new file mode 100644 | 
| index 0000000000000000000000000000000000000000..078c17d9480f529da414881730e02d0af3a6316c | 
| --- /dev/null | 
| +++ b/chrome/browser/ui/app_list/arc/arc_app_unittest.cc | 
| @@ -0,0 +1,383 @@ | 
| +// Copyright 2015 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 <algorithm> | 
| +#include <map> | 
| +#include <string> | 
| +#include <vector> | 
| + | 
| +#include "base/files/file_path.h" | 
| +#include "base/files/file_util.h" | 
| +#include "base/run_loop.h" | 
| +#include "base/task_runner_util.h" | 
| +#include "chrome/browser/ui/app_list/app_list_test_util.h" | 
| +#include "chrome/browser/ui/app_list/arc/arc_app_icon.h" | 
| +#include "chrome/browser/ui/app_list/arc/arc_app_item.h" | 
| +#include "chrome/browser/ui/app_list/arc/arc_app_list_prefs.h" | 
| +#include "chrome/browser/ui/app_list/arc/arc_app_model_builder.h" | 
| +#include "chrome/browser/ui/app_list/test/test_app_list_controller_delegate.h" | 
| +#include "chrome/test/base/testing_profile.h" | 
| +#include "components/arc/arc_bridge_service.h" | 
| +#include "components/arc/test/fake_arc_bridge_service.h" | 
| +#include "content/public/browser/browser_thread.h" | 
| +#include "testing/gtest/include/gtest/gtest.h" | 
| +#include "ui/app_list/app_list_model.h" | 
| +#include "ui/gfx/image/image_skia.h" | 
| + | 
| +namespace { | 
| + | 
| +const struct FakeArcBridgeService::TestAppInfo kFakeApps[] = { | 
| +  {"Arc app 1", "fake.app.1", "fake.app.1.Activity"}, | 
| +  {"Arc app 2", "fake.app.2", "fake.app.2.Activity"}, | 
| +  {"Arc app 3", "fake.app.3", "fake.app.3.Activity"}, | 
| +}; | 
| + | 
| +const size_t kFakeAppCount =  arraysize(kFakeApps); | 
| + | 
| +}  // namespace | 
| + | 
| +class ArcAppModelBuilderTest : public AppListTestBase { | 
| + public: | 
| +  ArcAppModelBuilderTest() {} | 
| +  ~ArcAppModelBuilderTest() override { | 
| +    // Release profile file in order to keep right sequence. | 
| +    profile_.reset(); | 
| +  } | 
| + | 
| +  void SetUp() override { | 
| +    AppListTestBase::SetUp(); | 
| + | 
| +    // Make sure we have enough data for test. | 
| +    static_assert(kFakeAppCount >= 2, "Not enough data for test"); | 
| + | 
| +    bridge_service_.reset(new FakeArcBridgeService()); | 
| + | 
| +    // Check initial conditions. | 
| +    EXPECT_EQ(bridge_service_.get(), arc::ArcBridgeService::Get()); | 
| +    EXPECT_EQ(false, arc::ArcBridgeService::Get()->available()); | 
| +    EXPECT_EQ(arc::ArcBridgeService::State::STOPPED, | 
| +              arc::ArcBridgeService::Get()->state()); | 
| + | 
| +    CreateBuilder(); | 
| + | 
| +    // At this point we should have ArcAppListPrefs as observer of service. | 
| +    EXPECT_EQ( | 
| +        true, | 
| +        bridge_service_->HasObserver(ArcAppListPrefs::Get(profile_.get()))); | 
| +  } | 
| + | 
| +  void TearDown() override { ResetBuilder(); } | 
| + | 
| + protected: | 
| +  // Creates a new builder, destroying any existing one. | 
| +  void CreateBuilder() { | 
| +    ResetBuilder();  // Destroy any existing builder in the correct order. | 
| + | 
| +    model_.reset(new app_list::AppListModel); | 
| +    controller_.reset(new test::TestAppListControllerDelegate); | 
| +    builder_.reset(new ArcAppModelBuilder(controller_.get())); | 
| +    builder_->InitializeWithProfile(profile_.get(), model_.get()); | 
| +  } | 
| + | 
| +  void ResetBuilder() { | 
| +    builder_.reset(); | 
| +    controller_.reset(); | 
| +    model_.reset(); | 
| +  } | 
| + | 
| +  size_t GetArcItemCount() const { | 
| +    size_t arc_count = 0; | 
| +    const size_t count = model_->top_level_item_list()->item_count(); | 
| +    for (size_t i = 0; i < count; ++i) { | 
| +      app_list::AppListItem* item = model_->top_level_item_list()->item_at(i); | 
| +      if (item->GetItemType() == ArcAppItem::kItemType) { | 
| +        ++arc_count; | 
| +      } | 
| +    } | 
| +    return arc_count; | 
| +  } | 
| + | 
| +  ArcAppItem* GetArcItem(size_t index) const { | 
| +    size_t arc_count = 0; | 
| +    const size_t count = model_->top_level_item_list()->item_count(); | 
| +    ArcAppItem* arc_item = nullptr; | 
| +    for (size_t i = 0; i < count; ++i) { | 
| +      app_list::AppListItem* item = model_->top_level_item_list()->item_at(i); | 
| +      if (item->GetItemType() == ArcAppItem::kItemType) { | 
| +        if (arc_count++ == index) { | 
| +          arc_item = reinterpret_cast<ArcAppItem*>(item); | 
| +          break; | 
| +        } | 
| +      } | 
| +    } | 
| +    EXPECT_NE(nullptr, arc_item); | 
| +    return arc_item; | 
| +  } | 
| + | 
| +  ArcAppItem* FindArcItem(const std::string& id) const { | 
| +    const size_t count = GetArcItemCount(); | 
| +    for (size_t i = 0; i < count; ++i) { | 
| +      ArcAppItem* item = GetArcItem(i); | 
| +      if (item && item->id() == id) { | 
| +        return item; | 
| +      } | 
| +    } | 
| +    return nullptr; | 
| +  } | 
| + | 
| +  // Validate that prefs and model have right content. | 
| +  void ValidateHaveApps(size_t app_count, | 
| +                        const FakeArcBridgeService::TestAppInfo* apps) { | 
| +    ArcAppListPrefs* prefs = ArcAppListPrefs::Get(profile_.get()); | 
| +    const std::vector<std::string> ids = prefs->GetAppIds(); | 
| +    ASSERT_EQ(app_count, ids.size()); | 
| +    ASSERT_EQ(app_count, GetArcItemCount()); | 
| +    // In principle, order of items is not defined. | 
| +    for (size_t i = 0; i < app_count; ++i) { | 
| +      const std::string id = apps[i].id(); | 
| +      EXPECT_NE(std::find(ids.begin(), ids.end(), id), ids.end()); | 
| +      scoped_ptr<ArcAppListPrefs::AppInfo> app_info = prefs->GetApp(ids[i]); | 
| +      ASSERT_NE(nullptr, app_info.get()); | 
| +      EXPECT_EQ(apps[i].name, app_info->name); | 
| +      EXPECT_EQ(apps[i].package, app_info->package); | 
| +      EXPECT_EQ(apps[i].activity, app_info->activity); | 
| + | 
| +      const ArcAppItem* app_item = FindArcItem(ids[i]); | 
| +      ASSERT_NE(nullptr, app_item); | 
| +      EXPECT_EQ(apps[i].name, app_item->GetDisplayName()); | 
| +    } | 
| +  } | 
| + | 
| +  // Validate that requested apps have required ready state and other apps have | 
| +  // opposite state. | 
| +  void ValidateAppReadyState(size_t app_count, | 
| +                             const FakeArcBridgeService::TestAppInfo* apps, | 
| +                             bool ready) { | 
| +    ArcAppListPrefs* prefs = ArcAppListPrefs::Get(profile_.get()); | 
| +    ASSERT_NE(nullptr, prefs); | 
| + | 
| +    std::vector<std::string> ids = prefs->GetAppIds(); | 
| +    EXPECT_EQ(ids.size(), GetArcItemCount()); | 
| + | 
| +    // Process requested apps. | 
| +    for (size_t i = 0; i < app_count; ++i) { | 
| +      const std::string id = apps[i].id(); | 
| +      std::vector<std::string>::iterator it_id = std::find(ids.begin(), | 
| +                                                           ids.end(), | 
| +                                                           id); | 
| +      ASSERT_NE(it_id, ids.end()); | 
| +      ids.erase(it_id); | 
| + | 
| +      scoped_ptr<ArcAppListPrefs::AppInfo> app_info = prefs->GetApp(id); | 
| +      ASSERT_NE(nullptr, app_info.get()); | 
| +      EXPECT_EQ(ready, app_info->ready); | 
| +      const ArcAppItem* app_item = FindArcItem(id); | 
| +      ASSERT_NE(nullptr, app_item); | 
| +      EXPECT_EQ(ready, app_item->ready()); | 
| +    } | 
| + | 
| +    // Process the rest of the apps. | 
| +    for (size_t i = 0; i < ids.size(); ++i) { | 
| +      const std::string id = ids[i]; | 
| +      scoped_ptr<ArcAppListPrefs::AppInfo> app_info = prefs->GetApp(id); | 
| +      ASSERT_NE(nullptr, app_info.get()); | 
| +      EXPECT_NE(ready, app_info->ready); | 
| +      const ArcAppItem* app_item = FindArcItem(id); | 
| +      ASSERT_NE(nullptr, app_item); | 
| +      EXPECT_NE(ready, app_item->ready()); | 
| +    } | 
| +  } | 
| + | 
| +  scoped_ptr<app_list::AppListModel> model_; | 
| +  scoped_ptr<test::TestAppListControllerDelegate> controller_; | 
| +  scoped_ptr<ArcAppModelBuilder> builder_; | 
| +  scoped_ptr<FakeArcBridgeService> bridge_service_; | 
| + | 
| + private: | 
| +  DISALLOW_COPY_AND_ASSIGN(ArcAppModelBuilderTest); | 
| +}; | 
| + | 
| +TEST_F(ArcAppModelBuilderTest, RefreshAllOnReady) { | 
| +  EXPECT_EQ(0, bridge_service_->refresh_apps_count()); | 
| +  bridge_service_->SetReady(); | 
| +  EXPECT_EQ(1, bridge_service_->refresh_apps_count()); | 
| +} | 
| + | 
| +TEST_F(ArcAppModelBuilderTest, RefreshAllFillsContent) { | 
| +  ValidateHaveApps(0, nullptr); | 
| +  bridge_service_->SetReady(); | 
| +  bridge_service_->SendRefreshApps(kFakeAppCount, kFakeApps); | 
| +  ValidateHaveApps(kFakeAppCount, kFakeApps); | 
| +} | 
| + | 
| +TEST_F(ArcAppModelBuilderTest, MultipleRefreshAll) { | 
| +  ValidateHaveApps(0, nullptr); | 
| +  bridge_service_->SetReady(); | 
| +  // Send info about all fake apps except last. | 
| +  bridge_service_->SendRefreshApps(kFakeAppCount - 1, kFakeApps); | 
| +  // At this point all apps (except last) should exist and be ready. | 
| +  ValidateHaveApps(kFakeAppCount - 1, kFakeApps); | 
| +  ValidateAppReadyState(kFakeAppCount - 1, kFakeApps, true); | 
| + | 
| +  // Send info about all fake apps except first. | 
| +  bridge_service_->SendRefreshApps(kFakeAppCount - 1, kFakeApps + 1); | 
| +  // At this point all apps should exist but first one should be non-ready. | 
| +  ValidateHaveApps(kFakeAppCount, kFakeApps); | 
| +  ValidateAppReadyState(kFakeAppCount - 1, kFakeApps + 1, true); | 
| + | 
| +  // Send info about all fake apps. | 
| +  bridge_service_->SendRefreshApps(kFakeAppCount, kFakeApps); | 
| +  // At this point all apps should exist and be ready. | 
| +  ValidateHaveApps(kFakeAppCount, kFakeApps); | 
| +  ValidateAppReadyState(kFakeAppCount, kFakeApps, true); | 
| + | 
| +  // Send info no app available. | 
| +  bridge_service_->SendRefreshApps(0, nullptr); | 
| +  // At this point all apps should exist and be non-ready. | 
| +  ValidateHaveApps(kFakeAppCount, kFakeApps); | 
| +  ValidateAppReadyState(kFakeAppCount, kFakeApps, false); | 
| +} | 
| + | 
| +TEST_F(ArcAppModelBuilderTest, StopServiceDisablesApps) { | 
| +  ArcAppListPrefs* prefs = ArcAppListPrefs::Get(profile_.get()); | 
| +  ASSERT_NE(nullptr, prefs); | 
| + | 
| +  bridge_service_->SetReady(); | 
| +  EXPECT_EQ(static_cast<size_t>(0), GetArcItemCount()); | 
| +  EXPECT_EQ(static_cast<size_t>(0), prefs->GetAppIds().size()); | 
| + | 
| +  bridge_service_->SendRefreshApps(kFakeAppCount, kFakeApps); | 
| +  std::vector<std::string> ids = prefs->GetAppIds(); | 
| +  EXPECT_EQ(kFakeAppCount, ids.size()); | 
| +  ValidateAppReadyState(kFakeAppCount, kFakeApps, true); | 
| + | 
| +  // Stopping service does not delete items. It makes them non-ready. | 
| +  bridge_service_->SetStopped(); | 
| +  // Ids should be the same. | 
| +  EXPECT_EQ(ids, prefs->GetAppIds()); | 
| +  ValidateAppReadyState(kFakeAppCount, kFakeApps, false); | 
| +} | 
| + | 
| +TEST_F(ArcAppModelBuilderTest, LaunchApps) { | 
| +  bridge_service_->SetReady(); | 
| +  bridge_service_->SendRefreshApps(kFakeAppCount, kFakeApps); | 
| + | 
| +  // Simulate item activate. | 
| +  ArcAppItem* item_first = FindArcItem(kFakeApps[0].id()); | 
| +  ArcAppItem* item_last = FindArcItem(kFakeApps[kFakeAppCount - 1].id()); | 
| +  ASSERT_NE(nullptr, item_first); | 
| +  ASSERT_NE(nullptr, item_last); | 
| +  item_first->Activate(0); | 
| +  item_last->Activate(0); | 
| +  item_first->Activate(0); | 
| + | 
| +  const std::vector<std::string> launch_requests = | 
| +      bridge_service_->launch_requests(); | 
| +  EXPECT_EQ(static_cast<size_t>(3), launch_requests.size()); | 
| +  EXPECT_EQ(kFakeApps[0].id(), launch_requests[0]); | 
| +  EXPECT_EQ(kFakeApps[kFakeAppCount - 1].id(), launch_requests[1]); | 
| +  EXPECT_EQ(kFakeApps[0].id(), launch_requests[2]); | 
| +} | 
| + | 
| +TEST_F(ArcAppModelBuilderTest, RequestIcons) { | 
| +  // Make sure we are on UI thread. | 
| +  ASSERT_EQ(true, | 
| +            content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); | 
| + | 
| +  bridge_service_->SetReady(); | 
| +  bridge_service_->SendRefreshApps(kFakeAppCount, kFakeApps); | 
| + | 
| +  // Validate that no icon exists at the beginning and request icon for | 
| +  // each supported scale factor. This will start asynchronous loading. | 
| +  uint32_t expected_mask = 0; | 
| +  const std::vector<ui::ScaleFactor>& scale_factors = | 
| +      ui::GetSupportedScaleFactors(); | 
| +  for (size_t i = 0; i < scale_factors.size(); ++i) { | 
| +    const ui::ScaleFactor scale_factor = scale_factors[i]; | 
| +    expected_mask |= 1 <<  scale_factor; | 
| +    for (size_t j = 0; j < kFakeAppCount; ++j) { | 
| +      ArcAppItem* app_item = FindArcItem(kFakeApps[j].id()); | 
| +      ASSERT_NE(nullptr, app_item); | 
| +      const float scale = ui::GetScaleForScaleFactor(scale_factor); | 
| +      app_item->icon().GetRepresentation(scale); | 
| +    } | 
| +  } | 
| + | 
| +  // Process pending tasks. | 
| +  content::BrowserThread::GetBlockingPool()->FlushForTesting(); | 
| +  base::RunLoop().RunUntilIdle(); | 
| + | 
| +  // At this moment we should receive all requests for icon loading. | 
| +  const std::vector<FakeArcBridgeService::IconRequest>& icon_requests = | 
| +      bridge_service_->icon_requests(); | 
| +  EXPECT_EQ(scale_factors.size() * kFakeAppCount, icon_requests.size()); | 
| +  std::map<std::string, uint32_t> app_masks; | 
| +  for (size_t i = 0; i < icon_requests.size(); ++i) { | 
| +    const FakeArcBridgeService::IconRequest icon_request = icon_requests[i]; | 
| +    const std::string id = ArcAppListPrefs::GetAppId(icon_request.package, | 
| +                                                     icon_request.activity); | 
| +    // Make sure no double requests. | 
| +    EXPECT_NE(app_masks[id], app_masks[id] | (1 << icon_request.scale_factor)); | 
| +    app_masks[id] |= (1 << icon_request.scale_factor); | 
| +  } | 
| + | 
| +  // Validate that we have a request for each icon for each supported scale | 
| +  // factor. | 
| +  EXPECT_EQ(kFakeAppCount, app_masks.size()); | 
| +  for (size_t i = 0; i < kFakeAppCount; ++i) { | 
| +    const std::string id = kFakeApps[i].id(); | 
| +    ASSERT_NE(app_masks.find(id), app_masks.end()); | 
| +    EXPECT_EQ(app_masks[id], expected_mask); | 
| +  } | 
| +} | 
| + | 
| +TEST_F(ArcAppModelBuilderTest, InstallIcon) { | 
| +  // Make sure we are on UI thread. | 
| +  ASSERT_EQ(true, | 
| +            content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); | 
| + | 
| +  bridge_service_->SetReady(); | 
| +  bridge_service_->SendRefreshApps(1, kFakeApps); | 
| + | 
| +  ArcAppListPrefs* prefs = ArcAppListPrefs::Get(profile_.get()); | 
| +  ASSERT_NE(nullptr, prefs); | 
| + | 
| +  const ui::ScaleFactor scale_factor = ui::GetSupportedScaleFactors()[0]; | 
| +  const float scale = ui::GetScaleForScaleFactor(scale_factor); | 
| +  const base::FilePath icon_path = prefs->GetIconPath(kFakeApps[0].id(), | 
| +                                                      scale_factor); | 
| +  EXPECT_EQ(false, base::PathExists(icon_path)); | 
| + | 
| +  const ArcAppItem* app_item = FindArcItem(kFakeApps[0].id()); | 
| +  EXPECT_NE(nullptr, app_item); | 
| +  // This initiates async loading. | 
| +  app_item->icon().GetRepresentation(scale); | 
| + | 
| +  // Process pending tasks. | 
| +  content::BrowserThread::GetBlockingPool()->FlushForTesting(); | 
| +  base::RunLoop().RunUntilIdle(); | 
| + | 
| +  // Validating decoded content does not fit well for unit tests. | 
| +  ArcAppIcon::DisableDecodingForTesting(); | 
| + | 
| +  // Now send generated icon for the app. | 
| +  std::vector<unsigned char> png_data; | 
| +  EXPECT_EQ(true, bridge_service_->GenerateAndSendIcon(kFakeApps[0], | 
| +                                                       scale_factor, | 
| +                                                       &png_data)); | 
| + | 
| +  // Process pending tasks. | 
| +  content::BrowserThread::GetBlockingPool()->FlushForTesting(); | 
| +  base::RunLoop().RunUntilIdle(); | 
| + | 
| +  // Validate that icons are installed, have right content and icon is | 
| +  // refreshed for ARC app item. | 
| +  EXPECT_EQ(true, base::PathExists(icon_path)); | 
| + | 
| +  std::string icon_data; | 
| +  // Read the file from disk and compare with reference data. | 
| +  EXPECT_EQ(true, base::ReadFileToString(icon_path, &icon_data)); | 
| +  ASSERT_EQ(icon_data.size(), png_data.size()); | 
| +  EXPECT_EQ(0, memcmp(&icon_data[0], &png_data[0], png_data.size())); | 
| +} | 
|  |