15821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Copyright (c) 2012 The Chromium Authors. All rights reserved.
25821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Use of this source code is governed by a BSD-style license that can be
35821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// found in the LICENSE file.
45821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
55821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "chrome/browser/ui/app_list/apps_model_builder.h"
65821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
75821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include <string>
85821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
92a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "base/files/file_path.h"
105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "base/memory/scoped_ptr.h"
112a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "base/prefs/pref_service.h"
124311e82a78ceafbe0585f51d4c8a86df9f21aa0dBen Murdoch#include "base/run_loop.h"
132a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "base/values.h"
142a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "chrome/browser/extensions/extension_function_test_utils.h"
155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "chrome/browser/extensions/extension_service_unittest.h"
165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "chrome/browser/extensions/extension_sorting.h"
172a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "chrome/common/extensions/extension_constants.h"
182a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "chrome/common/extensions/manifest.h"
192a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "chrome/common/pref_names.h"
205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "chrome/test/base/testing_profile.h"
215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "testing/gtest/include/gtest/gtest.h"
225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "ui/app_list/app_list_item_model.h"
235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)namespace {
255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)const char kHostedAppId[] = "dceacbkfkmllgmjmbhgkpjegnodmildf";
275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)const char kPackagedApp1Id[] = "emfkafnhnpcmabnnkckkchdilgeoekbo";
285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)const char kPackagedApp2Id[] = "jlklkagmeajbjiobondfhiekepofmljl";
295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Get a string of all apps in |model| joined with ','.
315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)std::string GetModelContent(app_list::AppListModel::Apps* model) {
325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  std::string content;
335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  for (size_t i = 0; i < model->item_count(); ++i) {
345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (i > 0)
355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      content += ',';
365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    content += model->GetItemAt(i)->title();
375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return content;
395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
412a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)scoped_refptr<extensions::Extension> MakeApp(const std::string& name,
422a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                                             const std::string& version,
432a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                                             const std::string& url,
442a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                                             const std::string& id) {
452a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  std::string err;
462a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  DictionaryValue value;
472a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  value.SetString("name", name);
482a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  value.SetString("version", version);
492a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  value.SetString("app.launch.web_url", url);
502a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  scoped_refptr<extensions::Extension> app =
512a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      extensions::Extension::Create(
522a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)          base::FilePath(),
532a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)          extensions::Manifest::INTERNAL,
542a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)          value,
552a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)          extensions::Extension::WAS_INSTALLED_BY_DEFAULT,
562a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)          id,
572a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)          &err);
582a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  EXPECT_EQ(err, "");
592a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  return app;
602a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
612a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}  // namespace
635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)class AppsModelBuilderTest : public ExtensionServiceTestBase {
655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) public:
665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  AppsModelBuilderTest() {}
675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  virtual ~AppsModelBuilderTest() {}
685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  virtual void SetUp() OVERRIDE {
70c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    ExtensionServiceTestBase::SetUp();
71c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // Load "app_list" extensions test profile.
735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // The test profile has 4 extensions:
745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // 1 dummy extension, 2 packaged extension apps and 1 hosted extension app.
752a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    base::FilePath source_install_dir = data_dir_
765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        .AppendASCII("app_list")
775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        .AppendASCII("Extensions");
782a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    base::FilePath pref_path = source_install_dir
795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        .DirName()
805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        .AppendASCII("Preferences");
815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    InitializeInstalledExtensionService(pref_path, source_install_dir);
825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    service_->Init();
835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // There should be 4 extensions in the test profile.
855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    const ExtensionSet* extensions = service_->extensions();
865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    ASSERT_EQ(static_cast<size_t>(4),  extensions->size());
875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)};
895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)TEST_F(AppsModelBuilderTest, Build) {
915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  scoped_ptr<app_list::AppListModel::Apps> model(
925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      new app_list::AppListModel::Apps);
935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  AppsModelBuilder builder(profile_.get(), model.get(), NULL);
945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  builder.Build();
955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // The apps list would have 3 extension apps in the profile.
975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  EXPECT_EQ(std::string("Packaged App 1,Packaged App 2,Hosted App"),
985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)            GetModelContent(model.get()));
995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
1005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1012a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)TEST_F(AppsModelBuilderTest, HideWebStore) {
1022a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // Install a "web store" app.
1032a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  scoped_refptr<extensions::Extension> store =
1042a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      MakeApp("webstore",
1052a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)              "0.0",
1062a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)              "http://google.com",
1072a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)              std::string(extension_misc::kWebStoreAppId));
1087d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)  service_->AddExtension(store.get());
1092a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
1102a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // Install an "enterprise web store" app.
1112a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  scoped_refptr<extensions::Extension> enterprise_store =
1122a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      MakeApp("enterprise_webstore",
1132a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)              "0.0",
1142a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)              "http://google.com",
1152a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)              std::string(extension_misc::kEnterpriseWebStoreAppId));
1167d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)  service_->AddExtension(enterprise_store.get());
1172a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
1182a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // Web stores should be present in the AppListModel.
1192a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  app_list::AppListModel::Apps model1;
1202a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  AppsModelBuilder builder1(profile_.get(), &model1, NULL);
1212a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  builder1.Build();
1222a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  std::string content = GetModelContent(&model1);
1232a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  EXPECT_NE(std::string::npos, content.find("webstore"));
1242a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  EXPECT_NE(std::string::npos, content.find("enterprise_webstore"));
1252a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
1262a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // Activate the HideWebStoreIcon policy.
1272a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  profile_->GetPrefs()->SetBoolean(prefs::kHideWebStoreIcon, true);
1282a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
1292a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // Web stores should NOT be in the AppListModel.
1302a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  app_list::AppListModel::Apps model2;
1312a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  AppsModelBuilder builder2(profile_.get(), &model2, NULL);
1322a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  builder2.Build();
1332a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  content = GetModelContent(&model2);
1342a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  EXPECT_EQ(std::string::npos, content.find("webstore"));
1352a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  EXPECT_EQ(std::string::npos, content.find("enterprise_webstore"));
1362a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
1372a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
1385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)TEST_F(AppsModelBuilderTest, DisableAndEnable) {
1395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  scoped_ptr<app_list::AppListModel::Apps> model(
1405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      new app_list::AppListModel::Apps);
1415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  AppsModelBuilder builder(profile_.get(), model.get(), NULL);
1425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  builder.Build();
1435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  service_->DisableExtension(kHostedAppId,
1455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                             extensions::Extension::DISABLE_NONE);
1462a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  EXPECT_EQ(std::string("Packaged App 1,Packaged App 2,Hosted App"),
1475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)            GetModelContent(model.get()));
1485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  service_->EnableExtension(kHostedAppId);
1505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  EXPECT_EQ(std::string("Packaged App 1,Packaged App 2,Hosted App"),
1515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)            GetModelContent(model.get()));
1525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
1535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1542a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)TEST_F(AppsModelBuilderTest, Uninstall) {
1552a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  scoped_ptr<app_list::AppListModel::Apps> model(
1562a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      new app_list::AppListModel::Apps);
1572a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  AppsModelBuilder builder(profile_.get(), model.get(), NULL);
1582a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  builder.Build();
1592a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
1602a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  service_->UninstallExtension(kPackagedApp2Id, false, NULL);
1612a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  EXPECT_EQ(std::string("Packaged App 1,Hosted App"),
1627dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch            GetModelContent(model.get()));
1637dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch
1644311e82a78ceafbe0585f51d4c8a86df9f21aa0dBen Murdoch  base::RunLoop().RunUntilIdle();
1657dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch}
1667dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch
1677dbb3d5cf0c15f500944d211057644d6a2f37371Ben MurdochTEST_F(AppsModelBuilderTest, UninstallTerminatedApp) {
1687dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch  scoped_ptr<app_list::AppListModel::Apps> model(
1697dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch      new app_list::AppListModel::Apps);
1707dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch  AppsModelBuilder builder(profile_.get(), model.get(), NULL);
1717dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch  builder.Build();
1727dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch
1737dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch  const extensions::Extension* app =
1747dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch      service_->GetInstalledExtension(kPackagedApp2Id);
1757dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch  ASSERT_TRUE(app != NULL);
1767dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch
1777dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch  // Simulate an app termination.
1787dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch  service_->TrackTerminatedExtensionForTest(app);
1797dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch
1807dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch  service_->UninstallExtension(kPackagedApp2Id, false, NULL);
1817dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch  EXPECT_EQ(std::string("Packaged App 1,Hosted App"),
1822a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)            GetModelContent(model.get()));
1832a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
1844311e82a78ceafbe0585f51d4c8a86df9f21aa0dBen Murdoch  base::RunLoop().RunUntilIdle();
1852a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
1862a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
1875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)TEST_F(AppsModelBuilderTest, OrdinalPrefsChange) {
1885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  scoped_ptr<app_list::AppListModel::Apps> model(
1895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      new app_list::AppListModel::Apps);
1905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  AppsModelBuilder builder(profile_.get(), model.get(), NULL);
1915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  builder.Build();
1925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  ExtensionSorting* sorting = service_->extension_prefs()->extension_sorting();
1945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  syncer::StringOrdinal package_app_page =
1965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      sorting->GetPageOrdinal(kPackagedApp1Id);
1975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  sorting->SetPageOrdinal(kHostedAppId, package_app_page.CreateBefore());
1985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  EXPECT_EQ(std::string("Hosted App,Packaged App 1,Packaged App 2"),
1995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)            GetModelContent(model.get()));
2005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  syncer::StringOrdinal app1_ordinal =
2025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      sorting->GetAppLaunchOrdinal(kPackagedApp1Id);
2035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  syncer::StringOrdinal app2_ordinal =
2045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      sorting->GetAppLaunchOrdinal(kPackagedApp2Id);
2055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  sorting->SetPageOrdinal(kHostedAppId, package_app_page);
2065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  sorting->SetAppLaunchOrdinal(kHostedAppId,
2075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                               app1_ordinal.CreateBetween(app2_ordinal));
2085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  EXPECT_EQ(std::string("Packaged App 1,Hosted App,Packaged App 2"),
2095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)            GetModelContent(model.get()));
2105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
2115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)TEST_F(AppsModelBuilderTest, OnExtensionMoved) {
2135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  scoped_ptr<app_list::AppListModel::Apps> model(
2145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      new app_list::AppListModel::Apps);
2155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  AppsModelBuilder builder(profile_.get(), model.get(), NULL);
2165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  builder.Build();
2175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  ExtensionSorting* sorting = service_->extension_prefs()->extension_sorting();
2195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  sorting->SetPageOrdinal(kHostedAppId,
2205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                          sorting->GetPageOrdinal(kPackagedApp1Id));
2215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  service_->OnExtensionMoved(kHostedAppId, kPackagedApp1Id, kPackagedApp2Id);
2235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  EXPECT_EQ(std::string("Packaged App 1,Hosted App,Packaged App 2"),
2245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)            GetModelContent(model.get()));
2255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  service_->OnExtensionMoved(kHostedAppId, kPackagedApp2Id, std::string());
2275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  EXPECT_EQ(std::string("Packaged App 1,Packaged App 2,Hosted App"),
2285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)            GetModelContent(model.get()));
2295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  service_->OnExtensionMoved(kHostedAppId, std::string(), kPackagedApp1Id);
2315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  EXPECT_EQ(std::string("Hosted App,Packaged App 1,Packaged App 2"),
2325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)            GetModelContent(model.get()));
2335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
2342a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
2352a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)TEST_F(AppsModelBuilderTest, InvalidOrdinal) {
2362a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // Creates a no-ordinal case.
2372a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  ExtensionSorting* sorting = service_->extension_prefs()->extension_sorting();
2382a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  sorting->ClearOrdinals(kPackagedApp1Id);
2392a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
2402a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // Creates an corrupted ordinal case.
2412a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  ExtensionScopedPrefs* scoped_prefs = service_->extension_prefs();
2422a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  scoped_prefs->UpdateExtensionPref(
2432a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      kHostedAppId,
2442a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      "page_ordinal",
2452a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      base::Value::CreateStringValue("a corrupted ordinal"));
2462a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
2472a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  scoped_ptr<app_list::AppListModel::Apps> model(
2482a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      new app_list::AppListModel::Apps);
2492a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  AppsModelBuilder builder(profile_.get(), model.get(), NULL);
2502a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
2512a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // This should not assert or crash.
2522a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  builder.Build();
2532a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
2542a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
2552a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)TEST_F(AppsModelBuilderTest, OrdinalConfilicts) {
2562a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // Creates conflict ordinals for app1 and app2.
2572a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  syncer::StringOrdinal conflict_ordinal =
2582a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      syncer::StringOrdinal::CreateInitialOrdinal();
2592a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
2602a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  ExtensionSorting* sorting = service_->extension_prefs()->extension_sorting();
2612a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  sorting->SetPageOrdinal(kHostedAppId, conflict_ordinal);
2622a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  sorting->SetAppLaunchOrdinal(kHostedAppId, conflict_ordinal);
2632a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
2642a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  sorting->SetPageOrdinal(kPackagedApp1Id, conflict_ordinal);
2652a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  sorting->SetAppLaunchOrdinal(kPackagedApp1Id, conflict_ordinal);
2662a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
2672a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  sorting->SetPageOrdinal(kPackagedApp2Id, conflict_ordinal);
2682a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  sorting->SetAppLaunchOrdinal(kPackagedApp2Id, conflict_ordinal);
2692a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
2702a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  scoped_ptr<app_list::AppListModel::Apps> model(
2712a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      new app_list::AppListModel::Apps);
2722a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  AppsModelBuilder builder(profile_.get(), model.get(), NULL);
2732a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  builder.Build();
2742a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
2752a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // By default, conflicted items are sorted by their app ids.
2762a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  EXPECT_EQ(std::string("Hosted App,Packaged App 1,Packaged App 2"),
2772a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)            GetModelContent(model.get()));
2782a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
2792a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // Move hosted app between app1 and app2 and it should not crash.
2802a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  service_->OnExtensionMoved(kHostedAppId, kPackagedApp1Id, kPackagedApp2Id);
2812a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  EXPECT_EQ(std::string("Packaged App 1,Hosted App,Packaged App 2"),
2822a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)            GetModelContent(model.get()));
2832a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
284