1// Copyright 2013 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/extensions/chrome_app_sorting.h"
6
7#include <map>
8
9#include "chrome/browser/extensions/./extension_prefs_unittest.h"
10#include "components/crx_file/id_util.h"
11#include "extensions/common/constants.h"
12#include "extensions/common/manifest_constants.h"
13#include "sync/api/string_ordinal.h"
14#include "testing/gtest/include/gtest/gtest.h"
15
16namespace extensions {
17
18namespace keys = manifest_keys;
19
20class ChromeAppSortingTest : public ExtensionPrefsTest {
21 protected:
22  ChromeAppSorting* app_sorting() {
23    return static_cast<ChromeAppSorting*>(prefs()->app_sorting());
24  }
25};
26
27class ChromeAppSortingAppLocation : public ChromeAppSortingTest {
28 public:
29  virtual void Initialize() OVERRIDE {
30    extension_ = prefs_.AddExtension("not_an_app");
31    // Non-apps should not have any app launch ordinal or page ordinal.
32    prefs()->OnExtensionInstalled(extension_.get(),
33                                  Extension::ENABLED,
34                                  syncer::StringOrdinal(),
35                                  std::string());
36  }
37
38  virtual void Verify() OVERRIDE {
39    EXPECT_FALSE(
40        app_sorting()->GetAppLaunchOrdinal(extension_->id()).IsValid());
41    EXPECT_FALSE(
42        app_sorting()->GetPageOrdinal(extension_->id()).IsValid());
43  }
44
45 private:
46  scoped_refptr<Extension> extension_;
47};
48TEST_F(ChromeAppSortingAppLocation, ChromeAppSortingAppLocation) {}
49
50class ChromeAppSortingAppLaunchOrdinal : public ChromeAppSortingTest {
51 public:
52  virtual void Initialize() OVERRIDE {
53    // No extensions yet.
54    syncer::StringOrdinal page = syncer::StringOrdinal::CreateInitialOrdinal();
55    EXPECT_TRUE(syncer::StringOrdinal::CreateInitialOrdinal().Equals(
56        app_sorting()->CreateNextAppLaunchOrdinal(page)));
57
58    extension_ = prefs_.AddApp("on_extension_installed");
59    EXPECT_FALSE(prefs()->IsExtensionDisabled(extension_->id()));
60    prefs()->OnExtensionInstalled(extension_.get(),
61                                  Extension::ENABLED,
62                                  syncer::StringOrdinal(),
63                                  std::string());
64  }
65
66  virtual void Verify() OVERRIDE {
67    syncer::StringOrdinal launch_ordinal =
68        app_sorting()->GetAppLaunchOrdinal(extension_->id());
69    syncer::StringOrdinal page_ordinal =
70        syncer::StringOrdinal::CreateInitialOrdinal();
71
72    // Extension should have been assigned a valid StringOrdinal.
73    EXPECT_TRUE(launch_ordinal.IsValid());
74    EXPECT_TRUE(launch_ordinal.LessThan(
75        app_sorting()->CreateNextAppLaunchOrdinal(page_ordinal)));
76    // Set a new launch ordinal of and verify it comes after.
77    app_sorting()->SetAppLaunchOrdinal(
78        extension_->id(),
79        app_sorting()->CreateNextAppLaunchOrdinal(page_ordinal));
80    syncer::StringOrdinal new_launch_ordinal =
81        app_sorting()->GetAppLaunchOrdinal(extension_->id());
82    EXPECT_TRUE(launch_ordinal.LessThan(new_launch_ordinal));
83
84    // This extension doesn't exist, so it should return an invalid
85    // StringOrdinal.
86    syncer::StringOrdinal invalid_app_launch_ordinal =
87        app_sorting()->GetAppLaunchOrdinal("foo");
88    EXPECT_FALSE(invalid_app_launch_ordinal.IsValid());
89    EXPECT_EQ(-1, app_sorting()->PageStringOrdinalAsInteger(
90        invalid_app_launch_ordinal));
91
92    // The second page doesn't have any apps so its next launch ordinal should
93    // be the first launch ordinal.
94    syncer::StringOrdinal next_page = page_ordinal.CreateAfter();
95    syncer::StringOrdinal next_page_app_launch_ordinal =
96        app_sorting()->CreateNextAppLaunchOrdinal(next_page);
97    EXPECT_TRUE(next_page_app_launch_ordinal.Equals(
98        app_sorting()->CreateFirstAppLaunchOrdinal(next_page)));
99  }
100
101 private:
102  scoped_refptr<Extension> extension_;
103};
104TEST_F(ChromeAppSortingAppLaunchOrdinal, ChromeAppSortingAppLaunchOrdinal) {}
105
106class ChromeAppSortingPageOrdinal : public ChromeAppSortingTest {
107 public:
108  virtual void Initialize() OVERRIDE {
109    extension_ = prefs_.AddApp("page_ordinal");
110    // Install with a page preference.
111    first_page_ = syncer::StringOrdinal::CreateInitialOrdinal();
112    prefs()->OnExtensionInstalled(extension_.get(),
113                                  Extension::ENABLED,
114                                  first_page_,
115                                  std::string());
116    EXPECT_TRUE(first_page_.Equals(
117        app_sorting()->GetPageOrdinal(extension_->id())));
118    EXPECT_EQ(0, app_sorting()->PageStringOrdinalAsInteger(first_page_));
119
120    scoped_refptr<Extension> extension2 = prefs_.AddApp("page_ordinal_2");
121    // Install without any page preference.
122    prefs()->OnExtensionInstalled(extension2.get(),
123                                  Extension::ENABLED,
124                                  syncer::StringOrdinal(),
125                                  std::string());
126    EXPECT_TRUE(first_page_.Equals(
127        app_sorting()->GetPageOrdinal(extension2->id())));
128  }
129  virtual void Verify() OVERRIDE {
130    // Set the page ordinal.
131    syncer::StringOrdinal new_page = first_page_.CreateAfter();
132    app_sorting()->SetPageOrdinal(extension_->id(), new_page);
133    // Verify the page ordinal.
134    EXPECT_TRUE(
135        new_page.Equals(app_sorting()->GetPageOrdinal(extension_->id())));
136    EXPECT_EQ(1, app_sorting()->PageStringOrdinalAsInteger(new_page));
137
138    // This extension doesn't exist, so it should return an invalid
139    // StringOrdinal.
140    EXPECT_FALSE(app_sorting()->GetPageOrdinal("foo").IsValid());
141  }
142
143 private:
144  syncer::StringOrdinal first_page_;
145  scoped_refptr<Extension> extension_;
146};
147TEST_F(ChromeAppSortingPageOrdinal, ChromeAppSortingPageOrdinal) {}
148
149// Ensure that ChromeAppSorting is able to properly initialize off a set
150// of old page and app launch indices and properly convert them.
151class ChromeAppSortingInitialize : public PrefsPrepopulatedTestBase {
152 public:
153  ChromeAppSortingInitialize() {}
154  virtual ~ChromeAppSortingInitialize() {}
155
156  virtual void Initialize() OVERRIDE {
157    // A preference determining the order of which the apps appear on the NTP.
158    const char kPrefAppLaunchIndexDeprecated[] = "app_launcher_index";
159    // A preference determining the page on which an app appears in the NTP.
160    const char kPrefPageIndexDeprecated[] = "page_index";
161
162    // Setup the deprecated preferences.
163    ExtensionScopedPrefs* scoped_prefs =
164        static_cast<ExtensionScopedPrefs*>(prefs());
165    scoped_prefs->UpdateExtensionPref(extension1()->id(),
166                                      kPrefAppLaunchIndexDeprecated,
167                                      new base::FundamentalValue(0));
168    scoped_prefs->UpdateExtensionPref(extension1()->id(),
169                                      kPrefPageIndexDeprecated,
170                                      new base::FundamentalValue(0));
171
172    scoped_prefs->UpdateExtensionPref(extension2()->id(),
173                                      kPrefAppLaunchIndexDeprecated,
174                                      new base::FundamentalValue(1));
175    scoped_prefs->UpdateExtensionPref(extension2()->id(),
176                                      kPrefPageIndexDeprecated,
177                                      new base::FundamentalValue(0));
178
179    scoped_prefs->UpdateExtensionPref(extension3()->id(),
180                                      kPrefAppLaunchIndexDeprecated,
181                                      new base::FundamentalValue(0));
182    scoped_prefs->UpdateExtensionPref(extension3()->id(),
183                                      kPrefPageIndexDeprecated,
184                                      new base::FundamentalValue(1));
185
186    // We insert the ids in reserve order so that we have to deal with the
187    // element on the 2nd page before the 1st page is seen.
188    ExtensionIdList ids;
189    ids.push_back(extension3()->id());
190    ids.push_back(extension2()->id());
191    ids.push_back(extension1()->id());
192
193    prefs()->app_sorting()->Initialize(ids);
194  }
195  virtual void Verify() OVERRIDE {
196    syncer::StringOrdinal first_ordinal =
197        syncer::StringOrdinal::CreateInitialOrdinal();
198    AppSorting* app_sorting = prefs()->app_sorting();
199
200    EXPECT_TRUE(first_ordinal.Equals(
201        app_sorting->GetAppLaunchOrdinal(extension1()->id())));
202    EXPECT_TRUE(first_ordinal.LessThan(
203        app_sorting->GetAppLaunchOrdinal(extension2()->id())));
204    EXPECT_TRUE(first_ordinal.Equals(
205        app_sorting->GetAppLaunchOrdinal(extension3()->id())));
206
207    EXPECT_TRUE(first_ordinal.Equals(
208        app_sorting->GetPageOrdinal(extension1()->id())));
209    EXPECT_TRUE(first_ordinal.Equals(
210        app_sorting->GetPageOrdinal(extension2()->id())));
211    EXPECT_TRUE(first_ordinal.LessThan(
212        app_sorting->GetPageOrdinal(extension3()->id())));
213  }
214};
215TEST_F(ChromeAppSortingInitialize, ChromeAppSortingInitialize) {}
216
217// Make sure that initialization still works when no extensions are present
218// (i.e. make sure that the web store icon is still loaded into the map).
219class ChromeAppSortingInitializeWithNoApps : public PrefsPrepopulatedTestBase {
220 public:
221  ChromeAppSortingInitializeWithNoApps() {}
222  virtual ~ChromeAppSortingInitializeWithNoApps() {}
223
224  virtual void Initialize() OVERRIDE {
225    AppSorting* app_sorting = prefs()->app_sorting();
226
227    // Make sure that the web store has valid ordinals.
228    syncer::StringOrdinal initial_ordinal =
229        syncer::StringOrdinal::CreateInitialOrdinal();
230    app_sorting->SetPageOrdinal(extensions::kWebStoreAppId,
231                                initial_ordinal);
232    app_sorting->SetAppLaunchOrdinal(extensions::kWebStoreAppId,
233                                     initial_ordinal);
234
235    ExtensionIdList ids;
236    app_sorting->Initialize(ids);
237  }
238  virtual void Verify() OVERRIDE {
239    ChromeAppSorting* app_sorting =
240        static_cast<ChromeAppSorting*>(prefs()->app_sorting());
241
242    syncer::StringOrdinal page =
243        app_sorting->GetPageOrdinal(extensions::kWebStoreAppId);
244    EXPECT_TRUE(page.IsValid());
245
246    ChromeAppSorting::PageOrdinalMap::iterator page_it =
247        app_sorting->ntp_ordinal_map_.find(page);
248    EXPECT_TRUE(page_it != app_sorting->ntp_ordinal_map_.end());
249
250    syncer::StringOrdinal app_launch =
251        app_sorting->GetPageOrdinal(extensions::kWebStoreAppId);
252    EXPECT_TRUE(app_launch.IsValid());
253
254    ChromeAppSorting::AppLaunchOrdinalMap::iterator app_launch_it =
255        page_it->second.find(app_launch);
256    EXPECT_TRUE(app_launch_it != page_it->second.end());
257  }
258};
259TEST_F(ChromeAppSortingInitializeWithNoApps,
260       ChromeAppSortingInitializeWithNoApps) {}
261
262// Tests the application index to ordinal migration code for values that
263// shouldn't be converted. This should be removed when the migrate code
264// is taken out.
265// http://crbug.com/107376
266class ChromeAppSortingMigrateAppIndexInvalid
267    : public PrefsPrepopulatedTestBase {
268 public:
269  ChromeAppSortingMigrateAppIndexInvalid() {}
270  virtual ~ChromeAppSortingMigrateAppIndexInvalid() {}
271
272  virtual void Initialize() OVERRIDE {
273    // A preference determining the order of which the apps appear on the NTP.
274    const char kPrefAppLaunchIndexDeprecated[] = "app_launcher_index";
275    // A preference determining the page on which an app appears in the NTP.
276    const char kPrefPageIndexDeprecated[] = "page_index";
277
278    // Setup the deprecated preference.
279    ExtensionScopedPrefs* scoped_prefs =
280        static_cast<ExtensionScopedPrefs*>(prefs());
281    scoped_prefs->UpdateExtensionPref(extension1()->id(),
282                                      kPrefAppLaunchIndexDeprecated,
283                                      new base::FundamentalValue(0));
284    scoped_prefs->UpdateExtensionPref(extension1()->id(),
285                                      kPrefPageIndexDeprecated,
286                                      new base::FundamentalValue(-1));
287
288    ExtensionIdList ids;
289    ids.push_back(extension1()->id());
290
291    prefs()->app_sorting()->Initialize(ids);
292  }
293  virtual void Verify() OVERRIDE {
294    // Make sure that the invalid page_index wasn't converted over.
295    EXPECT_FALSE(prefs()->app_sorting()->GetAppLaunchOrdinal(
296        extension1()->id()).IsValid());
297  }
298};
299TEST_F(ChromeAppSortingMigrateAppIndexInvalid,
300       ChromeAppSortingMigrateAppIndexInvalid) {}
301
302class ChromeAppSortingFixNTPCollisionsAllCollide
303    : public PrefsPrepopulatedTestBase {
304 public:
305  ChromeAppSortingFixNTPCollisionsAllCollide() {}
306  virtual ~ChromeAppSortingFixNTPCollisionsAllCollide() {}
307
308  virtual void Initialize() OVERRIDE {
309    repeated_ordinal_ = syncer::StringOrdinal::CreateInitialOrdinal();
310
311    AppSorting* app_sorting = prefs()->app_sorting();
312
313    app_sorting->SetAppLaunchOrdinal(extension1()->id(),
314                                     repeated_ordinal_);
315    app_sorting->SetPageOrdinal(extension1()->id(), repeated_ordinal_);
316
317    app_sorting->SetAppLaunchOrdinal(extension2()->id(), repeated_ordinal_);
318    app_sorting->SetPageOrdinal(extension2()->id(), repeated_ordinal_);
319
320    app_sorting->SetAppLaunchOrdinal(extension3()->id(), repeated_ordinal_);
321    app_sorting->SetPageOrdinal(extension3()->id(), repeated_ordinal_);
322
323    app_sorting->FixNTPOrdinalCollisions();
324  }
325  virtual void Verify() OVERRIDE {
326    AppSorting* app_sorting = prefs()->app_sorting();
327    syncer::StringOrdinal extension1_app_launch =
328        app_sorting->GetAppLaunchOrdinal(extension1()->id());
329    syncer::StringOrdinal extension2_app_launch =
330        app_sorting->GetAppLaunchOrdinal(extension2()->id());
331    syncer::StringOrdinal extension3_app_launch =
332        app_sorting->GetAppLaunchOrdinal(extension3()->id());
333
334    // The overlapping extensions should have be adjusted so that they are
335    // sorted by their id.
336    EXPECT_EQ(extension1()->id() < extension2()->id(),
337              extension1_app_launch.LessThan(extension2_app_launch));
338    EXPECT_EQ(extension1()->id() < extension3()->id(),
339              extension1_app_launch.LessThan(extension3_app_launch));
340    EXPECT_EQ(extension2()->id() < extension3()->id(),
341              extension2_app_launch.LessThan(extension3_app_launch));
342
343    // The page ordinal should be unchanged.
344    EXPECT_TRUE(app_sorting->GetPageOrdinal(extension1()->id()).Equals(
345        repeated_ordinal_));
346    EXPECT_TRUE(app_sorting->GetPageOrdinal(extension2()->id()).Equals(
347        repeated_ordinal_));
348    EXPECT_TRUE(app_sorting->GetPageOrdinal(extension3()->id()).Equals(
349        repeated_ordinal_));
350  }
351
352 private:
353  syncer::StringOrdinal repeated_ordinal_;
354};
355TEST_F(ChromeAppSortingFixNTPCollisionsAllCollide,
356       ChromeAppSortingFixNTPCollisionsAllCollide) {}
357
358class ChromeAppSortingFixNTPCollisionsSomeCollideAtStart
359    : public PrefsPrepopulatedTestBase {
360 public:
361  ChromeAppSortingFixNTPCollisionsSomeCollideAtStart() {}
362  virtual ~ChromeAppSortingFixNTPCollisionsSomeCollideAtStart() {}
363
364  virtual void Initialize() OVERRIDE {
365    first_ordinal_ = syncer::StringOrdinal::CreateInitialOrdinal();
366    syncer::StringOrdinal second_ordinal = first_ordinal_.CreateAfter();
367
368    AppSorting* app_sorting = prefs()->app_sorting();
369
370    // Have the first two extension in the same position, with a third
371    // (non-colliding) extension after.
372
373    app_sorting->SetAppLaunchOrdinal(extension1()->id(), first_ordinal_);
374    app_sorting->SetPageOrdinal(extension1()->id(), first_ordinal_);
375
376    app_sorting->SetAppLaunchOrdinal(extension2()->id(), first_ordinal_);
377    app_sorting->SetPageOrdinal(extension2()->id(), first_ordinal_);
378
379    app_sorting->SetAppLaunchOrdinal(extension3()->id(), second_ordinal);
380    app_sorting->SetPageOrdinal(extension3()->id(), first_ordinal_);
381
382    app_sorting->FixNTPOrdinalCollisions();
383  }
384  virtual void Verify() OVERRIDE {
385    AppSorting* app_sorting = prefs()->app_sorting();
386    syncer::StringOrdinal extension1_app_launch =
387        app_sorting->GetAppLaunchOrdinal(extension1()->id());
388    syncer::StringOrdinal extension2_app_launch =
389        app_sorting->GetAppLaunchOrdinal(extension2()->id());
390    syncer::StringOrdinal extension3_app_launch =
391        app_sorting->GetAppLaunchOrdinal(extension3()->id());
392
393    // The overlapping extensions should have be adjusted so that they are
394    // sorted by their id, but they both should be before ext3, which wasn't
395    // overlapping.
396    EXPECT_EQ(extension1()->id() < extension2()->id(),
397              extension1_app_launch.LessThan(extension2_app_launch));
398    EXPECT_TRUE(extension1_app_launch.LessThan(extension3_app_launch));
399    EXPECT_TRUE(extension2_app_launch.LessThan(extension3_app_launch));
400
401    // The page ordinal should be unchanged.
402    EXPECT_TRUE(app_sorting->GetPageOrdinal(extension1()->id()).Equals(
403        first_ordinal_));
404    EXPECT_TRUE(app_sorting->GetPageOrdinal(extension2()->id()).Equals(
405        first_ordinal_));
406    EXPECT_TRUE(app_sorting->GetPageOrdinal(extension3()->id()).Equals(
407        first_ordinal_));
408  }
409
410 private:
411  syncer::StringOrdinal first_ordinal_;
412};
413TEST_F(ChromeAppSortingFixNTPCollisionsSomeCollideAtStart,
414       ChromeAppSortingFixNTPCollisionsSomeCollideAtStart) {}
415
416class ChromeAppSortingFixNTPCollisionsSomeCollideAtEnd
417    : public PrefsPrepopulatedTestBase {
418 public:
419  ChromeAppSortingFixNTPCollisionsSomeCollideAtEnd() {}
420  virtual ~ChromeAppSortingFixNTPCollisionsSomeCollideAtEnd() {}
421
422  virtual void Initialize() OVERRIDE {
423    first_ordinal_ = syncer::StringOrdinal::CreateInitialOrdinal();
424    syncer::StringOrdinal second_ordinal = first_ordinal_.CreateAfter();
425
426    AppSorting* app_sorting = prefs()->app_sorting();
427
428    // Have the first extension in a non-colliding position, followed by two
429    // two extension in the same position.
430
431    app_sorting->SetAppLaunchOrdinal(extension1()->id(), first_ordinal_);
432    app_sorting->SetPageOrdinal(extension1()->id(), first_ordinal_);
433
434    app_sorting->SetAppLaunchOrdinal(extension2()->id(), second_ordinal);
435    app_sorting->SetPageOrdinal(extension2()->id(), first_ordinal_);
436
437    app_sorting->SetAppLaunchOrdinal(extension3()->id(), second_ordinal);
438    app_sorting->SetPageOrdinal(extension3()->id(), first_ordinal_);
439
440    app_sorting->FixNTPOrdinalCollisions();
441  }
442  virtual void Verify() OVERRIDE {
443    AppSorting* app_sorting = prefs()->app_sorting();
444    syncer::StringOrdinal extension1_app_launch =
445        app_sorting->GetAppLaunchOrdinal(extension1()->id());
446    syncer::StringOrdinal extension2_app_launch =
447        app_sorting->GetAppLaunchOrdinal(extension2()->id());
448    syncer::StringOrdinal extension3_app_launch =
449        app_sorting->GetAppLaunchOrdinal(extension3()->id());
450
451    // The overlapping extensions should have be adjusted so that they are
452    // sorted by their id, but they both should be after ext1, which wasn't
453    // overlapping.
454    EXPECT_TRUE(extension1_app_launch.LessThan(extension2_app_launch));
455    EXPECT_TRUE(extension1_app_launch.LessThan(extension3_app_launch));
456    EXPECT_EQ(extension2()->id() < extension3()->id(),
457              extension2_app_launch.LessThan(extension3_app_launch));
458
459    // The page ordinal should be unchanged.
460    EXPECT_TRUE(app_sorting->GetPageOrdinal(extension1()->id()).Equals(
461        first_ordinal_));
462    EXPECT_TRUE(app_sorting->GetPageOrdinal(extension2()->id()).Equals(
463        first_ordinal_));
464    EXPECT_TRUE(app_sorting->GetPageOrdinal(extension3()->id()).Equals(
465        first_ordinal_));
466  }
467
468 private:
469  syncer::StringOrdinal first_ordinal_;
470};
471TEST_F(ChromeAppSortingFixNTPCollisionsSomeCollideAtEnd,
472       ChromeAppSortingFixNTPCollisionsSomeCollideAtEnd) {}
473
474class ChromeAppSortingFixNTPCollisionsTwoCollisions
475    : public PrefsPrepopulatedTestBase {
476 public:
477  ChromeAppSortingFixNTPCollisionsTwoCollisions() {}
478  virtual ~ChromeAppSortingFixNTPCollisionsTwoCollisions() {}
479
480  virtual void Initialize() OVERRIDE {
481    first_ordinal_ = syncer::StringOrdinal::CreateInitialOrdinal();
482    syncer::StringOrdinal second_ordinal = first_ordinal_.CreateAfter();
483
484    AppSorting* app_sorting = prefs()->app_sorting();
485
486    // Have two extensions colliding, followed by two more colliding extensions.
487    app_sorting->SetAppLaunchOrdinal(extension1()->id(), first_ordinal_);
488    app_sorting->SetPageOrdinal(extension1()->id(), first_ordinal_);
489
490    app_sorting->SetAppLaunchOrdinal(extension2()->id(), first_ordinal_);
491    app_sorting->SetPageOrdinal(extension2()->id(), first_ordinal_);
492
493    app_sorting->SetAppLaunchOrdinal(extension3()->id(), second_ordinal);
494    app_sorting->SetPageOrdinal(extension3()->id(), first_ordinal_);
495
496    app_sorting->SetAppLaunchOrdinal(extension4()->id(), second_ordinal);
497    app_sorting->SetPageOrdinal(extension4()->id(), first_ordinal_);
498
499    app_sorting->FixNTPOrdinalCollisions();
500  }
501  virtual void Verify() OVERRIDE {
502    AppSorting* app_sorting = prefs()->app_sorting();
503    syncer::StringOrdinal extension1_app_launch =
504        app_sorting->GetAppLaunchOrdinal(extension1()->id());
505    syncer::StringOrdinal extension2_app_launch =
506        app_sorting->GetAppLaunchOrdinal(extension2()->id());
507    syncer::StringOrdinal extension3_app_launch =
508        app_sorting->GetAppLaunchOrdinal(extension3()->id());
509    syncer::StringOrdinal extension4_app_launch =
510        app_sorting->GetAppLaunchOrdinal(extension4()->id());
511
512    // The overlapping extensions should have be adjusted so that they are
513    // sorted by their id, with |ext1| and |ext2| appearing before |ext3| and
514    // |ext4|.
515    EXPECT_TRUE(extension1_app_launch.LessThan(extension3_app_launch));
516    EXPECT_TRUE(extension1_app_launch.LessThan(extension4_app_launch));
517    EXPECT_TRUE(extension2_app_launch.LessThan(extension3_app_launch));
518    EXPECT_TRUE(extension2_app_launch.LessThan(extension4_app_launch));
519
520    EXPECT_EQ(extension1()->id() < extension2()->id(),
521              extension1_app_launch.LessThan(extension2_app_launch));
522    EXPECT_EQ(extension3()->id() < extension4()->id(),
523              extension3_app_launch.LessThan(extension4_app_launch));
524
525    // The page ordinal should be unchanged.
526    EXPECT_TRUE(app_sorting->GetPageOrdinal(extension1()->id()).Equals(
527        first_ordinal_));
528    EXPECT_TRUE(app_sorting->GetPageOrdinal(extension2()->id()).Equals(
529        first_ordinal_));
530    EXPECT_TRUE(app_sorting->GetPageOrdinal(extension3()->id()).Equals(
531        first_ordinal_));
532    EXPECT_TRUE(app_sorting->GetPageOrdinal(extension4()->id()).Equals(
533        first_ordinal_));
534  }
535
536 private:
537  syncer::StringOrdinal first_ordinal_;
538};
539TEST_F(ChromeAppSortingFixNTPCollisionsTwoCollisions,
540       ChromeAppSortingFixNTPCollisionsTwoCollisions) {}
541
542class ChromeAppSortingEnsureValidOrdinals
543    : public PrefsPrepopulatedTestBase {
544 public :
545  ChromeAppSortingEnsureValidOrdinals() {}
546  virtual ~ChromeAppSortingEnsureValidOrdinals() {}
547
548  virtual void Initialize() OVERRIDE {}
549  virtual void Verify() OVERRIDE {
550    AppSorting* app_sorting = prefs()->app_sorting();
551
552    // Give ext1 invalid ordinals and then check that EnsureValidOrdinals fixes
553    // them.
554    app_sorting->SetAppLaunchOrdinal(extension1()->id(),
555                                     syncer::StringOrdinal());
556    app_sorting->SetPageOrdinal(extension1()->id(), syncer::StringOrdinal());
557
558    app_sorting->EnsureValidOrdinals(extension1()->id(),
559                                     syncer::StringOrdinal());
560
561    EXPECT_TRUE(app_sorting->GetAppLaunchOrdinal(extension1()->id()).IsValid());
562    EXPECT_TRUE(app_sorting->GetPageOrdinal(extension1()->id()).IsValid());
563  }
564};
565TEST_F(ChromeAppSortingEnsureValidOrdinals,
566       ChromeAppSortingEnsureValidOrdinals) {}
567
568class ChromeAppSortingPageOrdinalMapping : public PrefsPrepopulatedTestBase {
569 public:
570  ChromeAppSortingPageOrdinalMapping() {}
571  virtual ~ChromeAppSortingPageOrdinalMapping() {}
572
573  virtual void Initialize() OVERRIDE {}
574  virtual void Verify() OVERRIDE {
575    std::string ext_1 = "ext_1";
576    std::string ext_2 = "ext_2";
577
578    ChromeAppSorting* app_sorting =
579        static_cast<ChromeAppSorting*>(prefs()->app_sorting());
580    syncer::StringOrdinal first_ordinal =
581        syncer::StringOrdinal::CreateInitialOrdinal();
582
583    // Ensure attempting to removing a mapping with an invalid page doesn't
584    // modify the map.
585    EXPECT_TRUE(app_sorting->ntp_ordinal_map_.empty());
586    app_sorting->RemoveOrdinalMapping(
587        ext_1, first_ordinal, first_ordinal);
588    EXPECT_TRUE(app_sorting->ntp_ordinal_map_.empty());
589
590    // Add new mappings.
591    app_sorting->AddOrdinalMapping(ext_1, first_ordinal, first_ordinal);
592    app_sorting->AddOrdinalMapping(ext_2, first_ordinal, first_ordinal);
593
594    EXPECT_EQ(1U, app_sorting->ntp_ordinal_map_.size());
595    EXPECT_EQ(2U, app_sorting->ntp_ordinal_map_[first_ordinal].size());
596
597    ChromeAppSorting::AppLaunchOrdinalMap::iterator it =
598        app_sorting->ntp_ordinal_map_[first_ordinal].find(first_ordinal);
599    EXPECT_EQ(ext_1, it->second);
600    ++it;
601    EXPECT_EQ(ext_2, it->second);
602
603    app_sorting->RemoveOrdinalMapping(ext_1, first_ordinal, first_ordinal);
604    EXPECT_EQ(1U, app_sorting->ntp_ordinal_map_.size());
605    EXPECT_EQ(1U, app_sorting->ntp_ordinal_map_[first_ordinal].size());
606
607    it = app_sorting->ntp_ordinal_map_[first_ordinal].find(first_ordinal);
608    EXPECT_EQ(ext_2, it->second);
609
610    // Ensure that attempting to remove an extension with a valid page and app
611    // launch ordinals, but a unused id has no effect.
612    app_sorting->RemoveOrdinalMapping(
613        "invalid_ext", first_ordinal, first_ordinal);
614    EXPECT_EQ(1U, app_sorting->ntp_ordinal_map_.size());
615    EXPECT_EQ(1U, app_sorting->ntp_ordinal_map_[first_ordinal].size());
616
617    it = app_sorting->ntp_ordinal_map_[first_ordinal].find(first_ordinal);
618    EXPECT_EQ(ext_2, it->second);
619  }
620};
621TEST_F(ChromeAppSortingPageOrdinalMapping,
622       ChromeAppSortingPageOrdinalMapping) {}
623
624class ChromeAppSortingPreinstalledAppsBase : public PrefsPrepopulatedTestBase {
625 public:
626  ChromeAppSortingPreinstalledAppsBase() {
627    base::DictionaryValue simple_dict;
628    simple_dict.SetString(keys::kVersion, "1.0.0.0");
629    simple_dict.SetString(keys::kName, "unused");
630    simple_dict.SetString(keys::kApp, "true");
631    simple_dict.SetString(keys::kLaunchLocalPath, "fake.html");
632
633    std::string error;
634    app1_scoped_ = Extension::Create(
635        prefs_.temp_dir().AppendASCII("app1_"), Manifest::EXTERNAL_PREF,
636        simple_dict, Extension::NO_FLAGS, &error);
637    prefs()->OnExtensionInstalled(app1_scoped_.get(),
638                                  Extension::ENABLED,
639                                  syncer::StringOrdinal(),
640                                  std::string());
641
642    app2_scoped_ = Extension::Create(
643        prefs_.temp_dir().AppendASCII("app2_"), Manifest::EXTERNAL_PREF,
644        simple_dict, Extension::NO_FLAGS, &error);
645    prefs()->OnExtensionInstalled(app2_scoped_.get(),
646                                  Extension::ENABLED,
647                                  syncer::StringOrdinal(),
648                                  std::string());
649
650    app1_ = app1_scoped_.get();
651    app2_ = app2_scoped_.get();
652  }
653  virtual ~ChromeAppSortingPreinstalledAppsBase() {}
654
655 protected:
656  // Weak references, for convenience.
657  Extension* app1_;
658  Extension* app2_;
659
660 private:
661  scoped_refptr<Extension> app1_scoped_;
662  scoped_refptr<Extension> app2_scoped_;
663};
664
665class ChromeAppSortingGetMinOrMaxAppLaunchOrdinalsOnPage
666    : public ChromeAppSortingPreinstalledAppsBase {
667 public:
668  ChromeAppSortingGetMinOrMaxAppLaunchOrdinalsOnPage() {}
669  virtual ~ChromeAppSortingGetMinOrMaxAppLaunchOrdinalsOnPage() {}
670
671  virtual void Initialize() OVERRIDE {}
672  virtual void Verify() OVERRIDE {
673    syncer::StringOrdinal page = syncer::StringOrdinal::CreateInitialOrdinal();
674    ChromeAppSorting* app_sorting =
675        static_cast<ChromeAppSorting*>(prefs()->app_sorting());
676
677    syncer::StringOrdinal min =
678        app_sorting->GetMinOrMaxAppLaunchOrdinalsOnPage(
679            page,
680            ChromeAppSorting::MIN_ORDINAL);
681    syncer::StringOrdinal max =
682        app_sorting->GetMinOrMaxAppLaunchOrdinalsOnPage(
683            page,
684            ChromeAppSorting::MAX_ORDINAL);
685    EXPECT_TRUE(min.IsValid());
686    EXPECT_TRUE(max.IsValid());
687    EXPECT_TRUE(min.LessThan(max));
688
689    // Ensure that the min and max values aren't set for empty pages.
690    min = syncer::StringOrdinal();
691    max = syncer::StringOrdinal();
692    syncer::StringOrdinal empty_page = page.CreateAfter();
693    EXPECT_FALSE(min.IsValid());
694    EXPECT_FALSE(max.IsValid());
695    min = app_sorting->GetMinOrMaxAppLaunchOrdinalsOnPage(
696        empty_page,
697        ChromeAppSorting::MIN_ORDINAL);
698    max = app_sorting->GetMinOrMaxAppLaunchOrdinalsOnPage(
699        empty_page,
700        ChromeAppSorting::MAX_ORDINAL);
701    EXPECT_FALSE(min.IsValid());
702    EXPECT_FALSE(max.IsValid());
703  }
704};
705TEST_F(ChromeAppSortingGetMinOrMaxAppLaunchOrdinalsOnPage,
706       ChromeAppSortingGetMinOrMaxAppLaunchOrdinalsOnPage) {}
707
708// Make sure that empty pages aren't removed from the integer to ordinal
709// mapping. See http://crbug.com/109802 for details.
710class ChromeAppSortingKeepEmptyStringOrdinalPages
711    : public ChromeAppSortingPreinstalledAppsBase {
712 public:
713  ChromeAppSortingKeepEmptyStringOrdinalPages() {}
714  virtual ~ChromeAppSortingKeepEmptyStringOrdinalPages() {}
715
716  virtual void Initialize() OVERRIDE {
717    AppSorting* app_sorting = prefs()->app_sorting();
718
719    syncer::StringOrdinal first_page =
720        syncer::StringOrdinal::CreateInitialOrdinal();
721    app_sorting->SetPageOrdinal(app1_->id(), first_page);
722    EXPECT_EQ(0, app_sorting->PageStringOrdinalAsInteger(first_page));
723
724    last_page_ = first_page.CreateAfter();
725    app_sorting->SetPageOrdinal(app2_->id(), last_page_);
726    EXPECT_EQ(1, app_sorting->PageStringOrdinalAsInteger(last_page_));
727
728    // Move the second app to create an empty page.
729    app_sorting->SetPageOrdinal(app2_->id(), first_page);
730    EXPECT_EQ(0, app_sorting->PageStringOrdinalAsInteger(first_page));
731  }
732  virtual void Verify() OVERRIDE {
733    AppSorting* app_sorting = prefs()->app_sorting();
734
735    // Move the second app to a new empty page at the end, skipping over
736    // the current empty page.
737    last_page_ = last_page_.CreateAfter();
738    app_sorting->SetPageOrdinal(app2_->id(), last_page_);
739    EXPECT_EQ(2, app_sorting->PageStringOrdinalAsInteger(last_page_));
740    EXPECT_TRUE(last_page_.Equals(app_sorting->PageIntegerAsStringOrdinal(2)));
741  }
742
743 private:
744  syncer::StringOrdinal last_page_;
745};
746TEST_F(ChromeAppSortingKeepEmptyStringOrdinalPages,
747       ChromeAppSortingKeepEmptyStringOrdinalPages) {}
748
749class ChromeAppSortingMakesFillerOrdinals
750    : public ChromeAppSortingPreinstalledAppsBase {
751 public:
752  ChromeAppSortingMakesFillerOrdinals() {}
753  virtual ~ChromeAppSortingMakesFillerOrdinals() {}
754
755  virtual void Initialize() OVERRIDE {
756    AppSorting* app_sorting = prefs()->app_sorting();
757
758    syncer::StringOrdinal first_page =
759        syncer::StringOrdinal::CreateInitialOrdinal();
760    app_sorting->SetPageOrdinal(app1_->id(), first_page);
761    EXPECT_EQ(0, app_sorting->PageStringOrdinalAsInteger(first_page));
762  }
763  virtual void Verify() OVERRIDE {
764    AppSorting* app_sorting = prefs()->app_sorting();
765
766    // Because the UI can add an unlimited number of empty pages without an app
767    // on them, this test simulates dropping of an app on the 1st and 4th empty
768    // pages (3rd and 6th pages by index) to ensure we don't crash and that
769    // filler ordinals are created as needed. See: http://crbug.com/122214
770    syncer::StringOrdinal page_three =
771        app_sorting->PageIntegerAsStringOrdinal(2);
772    app_sorting->SetPageOrdinal(app1_->id(), page_three);
773    EXPECT_EQ(2, app_sorting->PageStringOrdinalAsInteger(page_three));
774
775    syncer::StringOrdinal page_six = app_sorting->PageIntegerAsStringOrdinal(5);
776    app_sorting->SetPageOrdinal(app1_->id(), page_six);
777    EXPECT_EQ(5, app_sorting->PageStringOrdinalAsInteger(page_six));
778  }
779};
780TEST_F(ChromeAppSortingMakesFillerOrdinals,
781       ChromeAppSortingMakesFillerOrdinals) {}
782
783class ChromeAppSortingDefaultOrdinalsBase : public ChromeAppSortingTest {
784 public:
785  ChromeAppSortingDefaultOrdinalsBase() {}
786  virtual ~ChromeAppSortingDefaultOrdinalsBase() {}
787
788  virtual void Initialize() OVERRIDE {
789    app_ = CreateApp("app");
790
791    InitDefaultOrdinals();
792    ChromeAppSorting* app_sorting =
793        static_cast<ChromeAppSorting*>(prefs()->app_sorting());
794    ChromeAppSorting::AppOrdinalsMap& sorting_defaults =
795        app_sorting->default_ordinals_;
796    sorting_defaults[app_->id()].page_ordinal = default_page_ordinal_;
797    sorting_defaults[app_->id()].app_launch_ordinal =
798        default_app_launch_ordinal_;
799
800    SetupUserOrdinals();
801    InstallApps();
802  }
803
804 protected:
805  scoped_refptr<Extension> CreateApp(const std::string& name) {
806    base::DictionaryValue simple_dict;
807    simple_dict.SetString(keys::kVersion, "1.0.0.0");
808    simple_dict.SetString(keys::kName, name);
809    simple_dict.SetString(keys::kApp, "true");
810    simple_dict.SetString(keys::kLaunchLocalPath, "fake.html");
811
812    std::string errors;
813    scoped_refptr<Extension> app = Extension::Create(
814        prefs_.temp_dir().AppendASCII(name), Manifest::EXTERNAL_PREF,
815        simple_dict, Extension::NO_FLAGS, &errors);
816    EXPECT_TRUE(app.get()) << errors;
817    EXPECT_TRUE(crx_file::id_util::IdIsValid(app->id()));
818    return app;
819  }
820
821  void InitDefaultOrdinals() {
822    default_page_ordinal_ =
823        syncer::StringOrdinal::CreateInitialOrdinal().CreateAfter();
824    default_app_launch_ordinal_ =
825        syncer::StringOrdinal::CreateInitialOrdinal().CreateBefore();
826  }
827
828  virtual void SetupUserOrdinals() {}
829
830  virtual void InstallApps() {
831    prefs()->OnExtensionInstalled(app_.get(),
832                                  Extension::ENABLED,
833                                  syncer::StringOrdinal(),
834                                  std::string());
835  }
836
837  scoped_refptr<Extension> app_;
838  syncer::StringOrdinal default_page_ordinal_;
839  syncer::StringOrdinal default_app_launch_ordinal_;
840};
841
842// Tests that the app gets its default ordinals.
843class ChromeAppSortingDefaultOrdinals
844    : public ChromeAppSortingDefaultOrdinalsBase {
845 public:
846  ChromeAppSortingDefaultOrdinals() {}
847  virtual ~ChromeAppSortingDefaultOrdinals() {}
848
849  virtual void Verify() OVERRIDE {
850    AppSorting* app_sorting = prefs()->app_sorting();
851    EXPECT_TRUE(app_sorting->GetPageOrdinal(app_->id()).Equals(
852        default_page_ordinal_));
853    EXPECT_TRUE(app_sorting->GetAppLaunchOrdinal(app_->id()).Equals(
854        default_app_launch_ordinal_));
855  }
856};
857TEST_F(ChromeAppSortingDefaultOrdinals,
858       ChromeAppSortingDefaultOrdinals) {}
859
860// Tests that the default page ordinal is overridden by install page ordinal.
861class ChromeAppSortingDefaultOrdinalOverriddenByInstallPage
862    : public ChromeAppSortingDefaultOrdinalsBase {
863 public:
864  ChromeAppSortingDefaultOrdinalOverriddenByInstallPage() {}
865  virtual ~ChromeAppSortingDefaultOrdinalOverriddenByInstallPage() {}
866
867  virtual void Verify() OVERRIDE {
868    AppSorting* app_sorting = prefs()->app_sorting();
869
870    EXPECT_FALSE(app_sorting->GetPageOrdinal(app_->id()).Equals(
871        default_page_ordinal_));
872    EXPECT_TRUE(app_sorting->GetPageOrdinal(app_->id()).Equals(install_page_));
873  }
874
875 protected:
876  virtual void InstallApps() OVERRIDE {
877    install_page_ = default_page_ordinal_.CreateAfter();
878    prefs()->OnExtensionInstalled(app_.get(),
879                                  Extension::ENABLED,
880                                  install_page_,
881                                  std::string());
882  }
883
884 private:
885  syncer::StringOrdinal install_page_;
886};
887TEST_F(ChromeAppSortingDefaultOrdinalOverriddenByInstallPage,
888       ChromeAppSortingDefaultOrdinalOverriddenByInstallPage) {}
889
890// Tests that the default ordinals are overridden by user values.
891class ChromeAppSortingDefaultOrdinalOverriddenByUserValue
892    : public ChromeAppSortingDefaultOrdinalsBase {
893 public:
894  ChromeAppSortingDefaultOrdinalOverriddenByUserValue() {}
895  virtual ~ChromeAppSortingDefaultOrdinalOverriddenByUserValue() {}
896
897  virtual void Verify() OVERRIDE {
898    AppSorting* app_sorting = prefs()->app_sorting();
899
900    EXPECT_TRUE(app_sorting->GetPageOrdinal(app_->id()).Equals(
901        user_page_ordinal_));
902    EXPECT_TRUE(app_sorting->GetAppLaunchOrdinal(app_->id()).Equals(
903        user_app_launch_ordinal_));
904  }
905
906 protected:
907  virtual void SetupUserOrdinals() OVERRIDE {
908    user_page_ordinal_ = default_page_ordinal_.CreateAfter();
909    user_app_launch_ordinal_ = default_app_launch_ordinal_.CreateBefore();
910
911    AppSorting* app_sorting = prefs()->app_sorting();
912    app_sorting->SetPageOrdinal(app_->id(), user_page_ordinal_);
913    app_sorting->SetAppLaunchOrdinal(app_->id(), user_app_launch_ordinal_);
914  }
915
916 private:
917  syncer::StringOrdinal user_page_ordinal_;
918  syncer::StringOrdinal user_app_launch_ordinal_;
919};
920TEST_F(ChromeAppSortingDefaultOrdinalOverriddenByUserValue,
921       ChromeAppSortingDefaultOrdinalOverriddenByUserValue) {}
922
923// Tests that the default app launch ordinal is changed to avoid collision.
924class ChromeAppSortingDefaultOrdinalNoCollision
925    : public ChromeAppSortingDefaultOrdinalsBase {
926 public:
927  ChromeAppSortingDefaultOrdinalNoCollision() {}
928  virtual ~ChromeAppSortingDefaultOrdinalNoCollision() {}
929
930  virtual void Verify() OVERRIDE {
931    AppSorting* app_sorting = prefs()->app_sorting();
932
933    // Use the default page.
934    EXPECT_TRUE(app_sorting->GetPageOrdinal(app_->id()).Equals(
935        default_page_ordinal_));
936    // Not using the default app launch ordinal because of the collision.
937    EXPECT_FALSE(app_sorting->GetAppLaunchOrdinal(app_->id()).Equals(
938        default_app_launch_ordinal_));
939  }
940
941 protected:
942  virtual void SetupUserOrdinals() OVERRIDE {
943    other_app_ = prefs_.AddApp("other_app");
944    // Creates a collision.
945    AppSorting* app_sorting = prefs()->app_sorting();
946    app_sorting->SetPageOrdinal(other_app_->id(), default_page_ordinal_);
947    app_sorting->SetAppLaunchOrdinal(other_app_->id(),
948                                     default_app_launch_ordinal_);
949
950    yet_another_app_ = prefs_.AddApp("yet_aother_app");
951    app_sorting->SetPageOrdinal(yet_another_app_->id(), default_page_ordinal_);
952    app_sorting->SetAppLaunchOrdinal(yet_another_app_->id(),
953                                     default_app_launch_ordinal_);
954  }
955
956 private:
957  scoped_refptr<Extension> other_app_;
958  scoped_refptr<Extension> yet_another_app_;
959};
960TEST_F(ChromeAppSortingDefaultOrdinalNoCollision,
961       ChromeAppSortingDefaultOrdinalNoCollision) {}
962
963// Tests that SetExtensionVisible() correctly hides and unhides extensions.
964class ChromeAppSortingSetExtensionVisible : public ChromeAppSortingTest {
965 public:
966  ChromeAppSortingSetExtensionVisible() {}
967  virtual ~ChromeAppSortingSetExtensionVisible() {}
968
969  virtual void Initialize() OVERRIDE {
970    first_app_ = prefs_.AddApp("first_app");
971    second_app_ = prefs_.AddApp("second_app");
972  }
973
974  virtual void Verify() OVERRIDE {
975    ChromeAppSorting* sorting = app_sorting();
976    syncer::StringOrdinal page1 = sorting->GetPageOrdinal(first_app_->id());
977    syncer::StringOrdinal page2 = sorting->GetPageOrdinal(second_app_->id());
978    EXPECT_TRUE(sorting->GetAppLaunchOrdinal(first_app_->id()).IsValid());
979    EXPECT_TRUE(sorting->GetAppLaunchOrdinal(second_app_->id()).IsValid());
980    EXPECT_TRUE(page1.IsValid());
981    EXPECT_TRUE(page2.IsValid());
982    EXPECT_TRUE(page1.Equals(page2));
983
984    sorting->SetExtensionVisible(first_app_->id(), false);
985    EXPECT_EQ(
986        1U, sorting->CountItemsVisibleOnNtp(sorting->ntp_ordinal_map_[page1]));
987
988    sorting->SetExtensionVisible(first_app_->id(), true);
989    EXPECT_EQ(
990        2U, sorting->CountItemsVisibleOnNtp(sorting->ntp_ordinal_map_[page1]));
991  }
992
993 private:
994  scoped_refptr<Extension> first_app_;
995  scoped_refptr<Extension> second_app_;
996};
997TEST_F(ChromeAppSortingSetExtensionVisible,
998       ChromeAppSortingSetExtensionVisible) {
999}
1000
1001}  // namespace extensions
1002