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