Home | History | Annotate | Download | only in app_list
      1 // Copyright (c) 2012 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 #include "chrome/browser/ui/app_list/apps_model_builder.h"
      6 
      7 #include <string>
      8 
      9 #include "base/files/file_path.h"
     10 #include "base/memory/scoped_ptr.h"
     11 #include "base/prefs/pref_service.h"
     12 #include "base/run_loop.h"
     13 #include "base/values.h"
     14 #include "chrome/browser/extensions/extension_function_test_utils.h"
     15 #include "chrome/browser/extensions/extension_service_unittest.h"
     16 #include "chrome/browser/extensions/extension_sorting.h"
     17 #include "chrome/common/extensions/extension_constants.h"
     18 #include "chrome/common/extensions/manifest.h"
     19 #include "chrome/common/pref_names.h"
     20 #include "chrome/test/base/testing_profile.h"
     21 #include "testing/gtest/include/gtest/gtest.h"
     22 #include "ui/app_list/app_list_item_model.h"
     23 
     24 namespace {
     25 
     26 const char kHostedAppId[] = "dceacbkfkmllgmjmbhgkpjegnodmildf";
     27 const char kPackagedApp1Id[] = "emfkafnhnpcmabnnkckkchdilgeoekbo";
     28 const char kPackagedApp2Id[] = "jlklkagmeajbjiobondfhiekepofmljl";
     29 
     30 // Get a string of all apps in |model| joined with ','.
     31 std::string GetModelContent(app_list::AppListModel::Apps* model) {
     32   std::string content;
     33   for (size_t i = 0; i < model->item_count(); ++i) {
     34     if (i > 0)
     35       content += ',';
     36     content += model->GetItemAt(i)->title();
     37   }
     38   return content;
     39 }
     40 
     41 scoped_refptr<extensions::Extension> MakeApp(const std::string& name,
     42                                              const std::string& version,
     43                                              const std::string& url,
     44                                              const std::string& id) {
     45   std::string err;
     46   DictionaryValue value;
     47   value.SetString("name", name);
     48   value.SetString("version", version);
     49   value.SetString("app.launch.web_url", url);
     50   scoped_refptr<extensions::Extension> app =
     51       extensions::Extension::Create(
     52           base::FilePath(),
     53           extensions::Manifest::INTERNAL,
     54           value,
     55           extensions::Extension::WAS_INSTALLED_BY_DEFAULT,
     56           id,
     57           &err);
     58   EXPECT_EQ(err, "");
     59   return app;
     60 }
     61 
     62 }  // namespace
     63 
     64 class AppsModelBuilderTest : public ExtensionServiceTestBase {
     65  public:
     66   AppsModelBuilderTest() {}
     67   virtual ~AppsModelBuilderTest() {}
     68 
     69   virtual void SetUp() OVERRIDE {
     70     ExtensionServiceTestBase::SetUp();
     71 
     72     // Load "app_list" extensions test profile.
     73     // The test profile has 4 extensions:
     74     // 1 dummy extension, 2 packaged extension apps and 1 hosted extension app.
     75     base::FilePath source_install_dir = data_dir_
     76         .AppendASCII("app_list")
     77         .AppendASCII("Extensions");
     78     base::FilePath pref_path = source_install_dir
     79         .DirName()
     80         .AppendASCII("Preferences");
     81     InitializeInstalledExtensionService(pref_path, source_install_dir);
     82     service_->Init();
     83 
     84     // There should be 4 extensions in the test profile.
     85     const ExtensionSet* extensions = service_->extensions();
     86     ASSERT_EQ(static_cast<size_t>(4),  extensions->size());
     87   }
     88 };
     89 
     90 TEST_F(AppsModelBuilderTest, Build) {
     91   scoped_ptr<app_list::AppListModel::Apps> model(
     92       new app_list::AppListModel::Apps);
     93   AppsModelBuilder builder(profile_.get(), model.get(), NULL);
     94   builder.Build();
     95 
     96   // The apps list would have 3 extension apps in the profile.
     97   EXPECT_EQ(std::string("Packaged App 1,Packaged App 2,Hosted App"),
     98             GetModelContent(model.get()));
     99 }
    100 
    101 TEST_F(AppsModelBuilderTest, HideWebStore) {
    102   // Install a "web store" app.
    103   scoped_refptr<extensions::Extension> store =
    104       MakeApp("webstore",
    105               "0.0",
    106               "http://google.com",
    107               std::string(extension_misc::kWebStoreAppId));
    108   service_->AddExtension(store.get());
    109 
    110   // Install an "enterprise web store" app.
    111   scoped_refptr<extensions::Extension> enterprise_store =
    112       MakeApp("enterprise_webstore",
    113               "0.0",
    114               "http://google.com",
    115               std::string(extension_misc::kEnterpriseWebStoreAppId));
    116   service_->AddExtension(enterprise_store.get());
    117 
    118   // Web stores should be present in the AppListModel.
    119   app_list::AppListModel::Apps model1;
    120   AppsModelBuilder builder1(profile_.get(), &model1, NULL);
    121   builder1.Build();
    122   std::string content = GetModelContent(&model1);
    123   EXPECT_NE(std::string::npos, content.find("webstore"));
    124   EXPECT_NE(std::string::npos, content.find("enterprise_webstore"));
    125 
    126   // Activate the HideWebStoreIcon policy.
    127   profile_->GetPrefs()->SetBoolean(prefs::kHideWebStoreIcon, true);
    128 
    129   // Web stores should NOT be in the AppListModel.
    130   app_list::AppListModel::Apps model2;
    131   AppsModelBuilder builder2(profile_.get(), &model2, NULL);
    132   builder2.Build();
    133   content = GetModelContent(&model2);
    134   EXPECT_EQ(std::string::npos, content.find("webstore"));
    135   EXPECT_EQ(std::string::npos, content.find("enterprise_webstore"));
    136 }
    137 
    138 TEST_F(AppsModelBuilderTest, DisableAndEnable) {
    139   scoped_ptr<app_list::AppListModel::Apps> model(
    140       new app_list::AppListModel::Apps);
    141   AppsModelBuilder builder(profile_.get(), model.get(), NULL);
    142   builder.Build();
    143 
    144   service_->DisableExtension(kHostedAppId,
    145                              extensions::Extension::DISABLE_NONE);
    146   EXPECT_EQ(std::string("Packaged App 1,Packaged App 2,Hosted App"),
    147             GetModelContent(model.get()));
    148 
    149   service_->EnableExtension(kHostedAppId);
    150   EXPECT_EQ(std::string("Packaged App 1,Packaged App 2,Hosted App"),
    151             GetModelContent(model.get()));
    152 }
    153 
    154 TEST_F(AppsModelBuilderTest, Uninstall) {
    155   scoped_ptr<app_list::AppListModel::Apps> model(
    156       new app_list::AppListModel::Apps);
    157   AppsModelBuilder builder(profile_.get(), model.get(), NULL);
    158   builder.Build();
    159 
    160   service_->UninstallExtension(kPackagedApp2Id, false, NULL);
    161   EXPECT_EQ(std::string("Packaged App 1,Hosted App"),
    162             GetModelContent(model.get()));
    163 
    164   base::RunLoop().RunUntilIdle();
    165 }
    166 
    167 TEST_F(AppsModelBuilderTest, UninstallTerminatedApp) {
    168   scoped_ptr<app_list::AppListModel::Apps> model(
    169       new app_list::AppListModel::Apps);
    170   AppsModelBuilder builder(profile_.get(), model.get(), NULL);
    171   builder.Build();
    172 
    173   const extensions::Extension* app =
    174       service_->GetInstalledExtension(kPackagedApp2Id);
    175   ASSERT_TRUE(app != NULL);
    176 
    177   // Simulate an app termination.
    178   service_->TrackTerminatedExtensionForTest(app);
    179 
    180   service_->UninstallExtension(kPackagedApp2Id, false, NULL);
    181   EXPECT_EQ(std::string("Packaged App 1,Hosted App"),
    182             GetModelContent(model.get()));
    183 
    184   base::RunLoop().RunUntilIdle();
    185 }
    186 
    187 TEST_F(AppsModelBuilderTest, OrdinalPrefsChange) {
    188   scoped_ptr<app_list::AppListModel::Apps> model(
    189       new app_list::AppListModel::Apps);
    190   AppsModelBuilder builder(profile_.get(), model.get(), NULL);
    191   builder.Build();
    192 
    193   ExtensionSorting* sorting = service_->extension_prefs()->extension_sorting();
    194 
    195   syncer::StringOrdinal package_app_page =
    196       sorting->GetPageOrdinal(kPackagedApp1Id);
    197   sorting->SetPageOrdinal(kHostedAppId, package_app_page.CreateBefore());
    198   EXPECT_EQ(std::string("Hosted App,Packaged App 1,Packaged App 2"),
    199             GetModelContent(model.get()));
    200 
    201   syncer::StringOrdinal app1_ordinal =
    202       sorting->GetAppLaunchOrdinal(kPackagedApp1Id);
    203   syncer::StringOrdinal app2_ordinal =
    204       sorting->GetAppLaunchOrdinal(kPackagedApp2Id);
    205   sorting->SetPageOrdinal(kHostedAppId, package_app_page);
    206   sorting->SetAppLaunchOrdinal(kHostedAppId,
    207                                app1_ordinal.CreateBetween(app2_ordinal));
    208   EXPECT_EQ(std::string("Packaged App 1,Hosted App,Packaged App 2"),
    209             GetModelContent(model.get()));
    210 }
    211 
    212 TEST_F(AppsModelBuilderTest, OnExtensionMoved) {
    213   scoped_ptr<app_list::AppListModel::Apps> model(
    214       new app_list::AppListModel::Apps);
    215   AppsModelBuilder builder(profile_.get(), model.get(), NULL);
    216   builder.Build();
    217 
    218   ExtensionSorting* sorting = service_->extension_prefs()->extension_sorting();
    219   sorting->SetPageOrdinal(kHostedAppId,
    220                           sorting->GetPageOrdinal(kPackagedApp1Id));
    221 
    222   service_->OnExtensionMoved(kHostedAppId, kPackagedApp1Id, kPackagedApp2Id);
    223   EXPECT_EQ(std::string("Packaged App 1,Hosted App,Packaged App 2"),
    224             GetModelContent(model.get()));
    225 
    226   service_->OnExtensionMoved(kHostedAppId, kPackagedApp2Id, std::string());
    227   EXPECT_EQ(std::string("Packaged App 1,Packaged App 2,Hosted App"),
    228             GetModelContent(model.get()));
    229 
    230   service_->OnExtensionMoved(kHostedAppId, std::string(), kPackagedApp1Id);
    231   EXPECT_EQ(std::string("Hosted App,Packaged App 1,Packaged App 2"),
    232             GetModelContent(model.get()));
    233 }
    234 
    235 TEST_F(AppsModelBuilderTest, InvalidOrdinal) {
    236   // Creates a no-ordinal case.
    237   ExtensionSorting* sorting = service_->extension_prefs()->extension_sorting();
    238   sorting->ClearOrdinals(kPackagedApp1Id);
    239 
    240   // Creates an corrupted ordinal case.
    241   ExtensionScopedPrefs* scoped_prefs = service_->extension_prefs();
    242   scoped_prefs->UpdateExtensionPref(
    243       kHostedAppId,
    244       "page_ordinal",
    245       base::Value::CreateStringValue("a corrupted ordinal"));
    246 
    247   scoped_ptr<app_list::AppListModel::Apps> model(
    248       new app_list::AppListModel::Apps);
    249   AppsModelBuilder builder(profile_.get(), model.get(), NULL);
    250 
    251   // This should not assert or crash.
    252   builder.Build();
    253 }
    254 
    255 TEST_F(AppsModelBuilderTest, OrdinalConfilicts) {
    256   // Creates conflict ordinals for app1 and app2.
    257   syncer::StringOrdinal conflict_ordinal =
    258       syncer::StringOrdinal::CreateInitialOrdinal();
    259 
    260   ExtensionSorting* sorting = service_->extension_prefs()->extension_sorting();
    261   sorting->SetPageOrdinal(kHostedAppId, conflict_ordinal);
    262   sorting->SetAppLaunchOrdinal(kHostedAppId, conflict_ordinal);
    263 
    264   sorting->SetPageOrdinal(kPackagedApp1Id, conflict_ordinal);
    265   sorting->SetAppLaunchOrdinal(kPackagedApp1Id, conflict_ordinal);
    266 
    267   sorting->SetPageOrdinal(kPackagedApp2Id, conflict_ordinal);
    268   sorting->SetAppLaunchOrdinal(kPackagedApp2Id, conflict_ordinal);
    269 
    270   scoped_ptr<app_list::AppListModel::Apps> model(
    271       new app_list::AppListModel::Apps);
    272   AppsModelBuilder builder(profile_.get(), model.get(), NULL);
    273   builder.Build();
    274 
    275   // By default, conflicted items are sorted by their app ids.
    276   EXPECT_EQ(std::string("Hosted App,Packaged App 1,Packaged App 2"),
    277             GetModelContent(model.get()));
    278 
    279   // Move hosted app between app1 and app2 and it should not crash.
    280   service_->OnExtensionMoved(kHostedAppId, kPackagedApp1Id, kPackagedApp2Id);
    281   EXPECT_EQ(std::string("Packaged App 1,Hosted App,Packaged App 2"),
    282             GetModelContent(model.get()));
    283 }
    284