Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(46)

Side by Side Diff: chrome/browser/history/expire_history_backend_unittest.cc

Issue 6693021: fav icon -> favicon. Pass 5: fav_icon -> favicon (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Created 9 years, 9 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
« no previous file with comments | « chrome/browser/custom_home_pages_table_model.cc ('k') | chrome/browser/history/history.cc » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 // Copyright (c) 2010 The Chromium Authors. All rights reserved. 1 // Copyright (c) 2010 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include <string> 5 #include <string>
6 #include <utility> 6 #include <utility>
7 7
8 #include "base/basictypes.h" 8 #include "base/basictypes.h"
9 #include "base/compiler_specific.h" 9 #include "base/compiler_specific.h"
10 #include "base/file_path.h" 10 #include "base/file_path.h"
(...skipping 183 matching lines...) Expand 10 before | Expand all | Expand 10 after
194 194
195 // Four times for each visit. 195 // Four times for each visit.
196 visit_times[3] = Time::Now(); 196 visit_times[3] = Time::Now();
197 visit_times[2] = visit_times[3] - TimeDelta::FromDays(1); 197 visit_times[2] = visit_times[3] - TimeDelta::FromDays(1);
198 visit_times[1] = visit_times[3] - TimeDelta::FromDays(2); 198 visit_times[1] = visit_times[3] - TimeDelta::FromDays(2);
199 visit_times[0] = visit_times[3] - TimeDelta::FromDays(3); 199 visit_times[0] = visit_times[3] - TimeDelta::FromDays(3);
200 200
201 // Two favicons. The first two URLs will share the same one, while the last 201 // Two favicons. The first two URLs will share the same one, while the last
202 // one will have a unique favicon. 202 // one will have a unique favicon.
203 FavIconID favicon1 = thumb_db_->AddFavIcon(GURL("http://favicon/url1"), 203 FavIconID favicon1 = thumb_db_->AddFavIcon(GURL("http://favicon/url1"),
204 FAV_ICON); 204 FAVICON);
205 FavIconID favicon2 = thumb_db_->AddFavIcon(GURL("http://favicon/url2"), 205 FavIconID favicon2 = thumb_db_->AddFavIcon(GURL("http://favicon/url2"),
206 FAV_ICON); 206 FAVICON);
207 207
208 // Three URLs. 208 // Three URLs.
209 URLRow url_row1(GURL("http://www.google.com/1")); 209 URLRow url_row1(GURL("http://www.google.com/1"));
210 url_row1.set_last_visit(visit_times[0]); 210 url_row1.set_last_visit(visit_times[0]);
211 url_row1.set_visit_count(1); 211 url_row1.set_visit_count(1);
212 url_ids[0] = main_db_->AddURL(url_row1); 212 url_ids[0] = main_db_->AddURL(url_row1);
213 thumb_db_->AddIconMapping(url_row1.url(), favicon1); 213 thumb_db_->AddIconMapping(url_row1.url(), favicon1);
214 214
215 URLRow url_row2(GURL("http://www.google.com/2")); 215 URLRow url_row2(GURL("http://www.google.com/2"));
216 url_row2.set_last_visit(visit_times[2]); 216 url_row2.set_last_visit(visit_times[2]);
(...skipping 189 matching lines...) Expand 10 before | Expand all | Expand 10 after
406 found_typed_changed_notification = true; 406 found_typed_changed_notification = true;
407 } 407 }
408 } 408 }
409 EXPECT_TRUE(found_delete_notification); 409 EXPECT_TRUE(found_delete_notification);
410 EXPECT_EQ(row.typed_count() > 0, found_typed_changed_notification); 410 EXPECT_EQ(row.typed_count() > 0, found_typed_changed_notification);
411 } 411 }
412 412
413 TEST_F(ExpireHistoryTest, DeleteFaviconsIfPossible) { 413 TEST_F(ExpireHistoryTest, DeleteFaviconsIfPossible) {
414 // Add a favicon record. 414 // Add a favicon record.
415 const GURL favicon_url("http://www.google.com/favicon.ico"); 415 const GURL favicon_url("http://www.google.com/favicon.ico");
416 FavIconID icon_id = thumb_db_->AddFavIcon(favicon_url, FAV_ICON); 416 FavIconID icon_id = thumb_db_->AddFavIcon(favicon_url, FAVICON);
417 EXPECT_TRUE(icon_id); 417 EXPECT_TRUE(icon_id);
418 EXPECT_TRUE(HasFavIcon(icon_id)); 418 EXPECT_TRUE(HasFavIcon(icon_id));
419 419
420 // The favicon should be deletable with no users. 420 // The favicon should be deletable with no users.
421 std::set<FavIconID> favicon_set; 421 std::set<FavIconID> favicon_set;
422 favicon_set.insert(icon_id); 422 favicon_set.insert(icon_id);
423 expirer_.DeleteFaviconsIfPossible(favicon_set); 423 expirer_.DeleteFaviconsIfPossible(favicon_set);
424 EXPECT_FALSE(HasFavIcon(icon_id)); 424 EXPECT_FALSE(HasFavIcon(icon_id));
425 425
426 // Add back the favicon. 426 // Add back the favicon.
(...skipping 26 matching lines...) Expand all
453 // should also get deleted. 453 // should also get deleted.
454 // Fails near end of month. http://crbug.com/43586 454 // Fails near end of month. http://crbug.com/43586
455 TEST_F(ExpireHistoryTest, FLAKY_DeleteURLAndFavicon) { 455 TEST_F(ExpireHistoryTest, FLAKY_DeleteURLAndFavicon) {
456 URLID url_ids[3]; 456 URLID url_ids[3];
457 Time visit_times[4]; 457 Time visit_times[4];
458 AddExampleData(url_ids, visit_times); 458 AddExampleData(url_ids, visit_times);
459 459
460 // Verify things are the way we expect with a URL row, favicon, thumbnail. 460 // Verify things are the way we expect with a URL row, favicon, thumbnail.
461 URLRow last_row; 461 URLRow last_row;
462 ASSERT_TRUE(main_db_->GetURLRow(url_ids[2], &last_row)); 462 ASSERT_TRUE(main_db_->GetURLRow(url_ids[2], &last_row));
463 FavIconID fav_icon_id = GetFavicon(last_row.url(), FAV_ICON); 463 FavIconID favicon_id = GetFavicon(last_row.url(), FAVICON);
464 EXPECT_TRUE(HasFavIcon(fav_icon_id)); 464 EXPECT_TRUE(HasFavIcon(favicon_id));
465 // TODO(sky): fix this, see comment in HasThumbnail. 465 // TODO(sky): fix this, see comment in HasThumbnail.
466 // EXPECT_TRUE(HasThumbnail(url_ids[2])); 466 // EXPECT_TRUE(HasThumbnail(url_ids[2]));
467 467
468 VisitVector visits; 468 VisitVector visits;
469 main_db_->GetVisitsForURL(url_ids[2], &visits); 469 main_db_->GetVisitsForURL(url_ids[2], &visits);
470 ASSERT_EQ(1U, visits.size()); 470 ASSERT_EQ(1U, visits.size());
471 EXPECT_EQ(1, CountTextMatchesForURL(last_row.url())); 471 EXPECT_EQ(1, CountTextMatchesForURL(last_row.url()));
472 472
473 // In this test we also make sure that any pending entries in the text 473 // In this test we also make sure that any pending entries in the text
474 // database manager are removed. 474 // database manager are removed.
(...skipping 28 matching lines...) Expand all
503 text_db_.get()); 503 text_db_.get());
504 504
505 // Run the text database expirer. This will flush any pending entries so we 505 // Run the text database expirer. This will flush any pending entries so we
506 // can check that nothing was committed. We use a time far in the future so 506 // can check that nothing was committed. We use a time far in the future so
507 // that anything added recently will get flushed. 507 // that anything added recently will get flushed.
508 TimeTicks expiration_time = TimeTicks::Now() + TimeDelta::FromDays(1); 508 TimeTicks expiration_time = TimeTicks::Now() + TimeDelta::FromDays(1);
509 text_db_->FlushOldChangesForTime(expiration_time); 509 text_db_->FlushOldChangesForTime(expiration_time);
510 510
511 // All the normal data + the favicon should be gone. 511 // All the normal data + the favicon should be gone.
512 EnsureURLInfoGone(last_row); 512 EnsureURLInfoGone(last_row);
513 EXPECT_FALSE(GetFavicon(last_row.url(), FAV_ICON)); 513 EXPECT_FALSE(GetFavicon(last_row.url(), FAVICON));
514 EXPECT_FALSE(HasFavIcon(fav_icon_id)); 514 EXPECT_FALSE(HasFavIcon(favicon_id));
515 } 515 }
516 516
517 // Deletes a URL with a favicon that other URLs reference, so that the favicon 517 // Deletes a URL with a favicon that other URLs reference, so that the favicon
518 // should not get deleted. This also tests deleting more than one visit. 518 // should not get deleted. This also tests deleting more than one visit.
519 TEST_F(ExpireHistoryTest, DeleteURLWithoutFavicon) { 519 TEST_F(ExpireHistoryTest, DeleteURLWithoutFavicon) {
520 URLID url_ids[3]; 520 URLID url_ids[3];
521 Time visit_times[4]; 521 Time visit_times[4];
522 AddExampleData(url_ids, visit_times); 522 AddExampleData(url_ids, visit_times);
523 523
524 // Verify things are the way we expect with a URL row, favicon, thumbnail. 524 // Verify things are the way we expect with a URL row, favicon, thumbnail.
525 URLRow last_row; 525 URLRow last_row;
526 ASSERT_TRUE(main_db_->GetURLRow(url_ids[1], &last_row)); 526 ASSERT_TRUE(main_db_->GetURLRow(url_ids[1], &last_row));
527 FavIconID fav_icon_id = GetFavicon(last_row.url(), FAV_ICON); 527 FavIconID favicon_id = GetFavicon(last_row.url(), FAVICON);
528 EXPECT_TRUE(HasFavIcon(fav_icon_id)); 528 EXPECT_TRUE(HasFavIcon(favicon_id));
529 // TODO(sky): fix this, see comment in HasThumbnail. 529 // TODO(sky): fix this, see comment in HasThumbnail.
530 // EXPECT_TRUE(HasThumbnail(url_ids[1])); 530 // EXPECT_TRUE(HasThumbnail(url_ids[1]));
531 531
532 VisitVector visits; 532 VisitVector visits;
533 main_db_->GetVisitsForURL(url_ids[1], &visits); 533 main_db_->GetVisitsForURL(url_ids[1], &visits);
534 EXPECT_EQ(2U, visits.size()); 534 EXPECT_EQ(2U, visits.size());
535 EXPECT_EQ(1, CountTextMatchesForURL(last_row.url())); 535 EXPECT_EQ(1, CountTextMatchesForURL(last_row.url()));
536 536
537 // Delete the URL and its dependencies. 537 // Delete the URL and its dependencies.
538 expirer_.DeleteURL(last_row.url()); 538 expirer_.DeleteURL(last_row.url());
539 539
540 // All the normal data + the favicon should be gone. 540 // All the normal data + the favicon should be gone.
541 EnsureURLInfoGone(last_row); 541 EnsureURLInfoGone(last_row);
542 EXPECT_TRUE(HasFavIcon(fav_icon_id)); 542 EXPECT_TRUE(HasFavIcon(favicon_id));
543 } 543 }
544 544
545 // DeleteURL should not delete starred urls. 545 // DeleteURL should not delete starred urls.
546 TEST_F(ExpireHistoryTest, DontDeleteStarredURL) { 546 TEST_F(ExpireHistoryTest, DontDeleteStarredURL) {
547 URLID url_ids[3]; 547 URLID url_ids[3];
548 Time visit_times[4]; 548 Time visit_times[4];
549 AddExampleData(url_ids, visit_times); 549 AddExampleData(url_ids, visit_times);
550 550
551 URLRow url_row; 551 URLRow url_row;
552 ASSERT_TRUE(main_db_->GetURLRow(url_ids[2], &url_row)); 552 ASSERT_TRUE(main_db_->GetURLRow(url_ids[2], &url_row));
553 553
554 // Star the last URL. 554 // Star the last URL.
555 StarURL(url_row.url()); 555 StarURL(url_row.url());
556 556
557 // Attempt to delete the url. 557 // Attempt to delete the url.
558 expirer_.DeleteURL(url_row.url()); 558 expirer_.DeleteURL(url_row.url());
559 559
560 // Because the url is starred, it shouldn't be deleted. 560 // Because the url is starred, it shouldn't be deleted.
561 GURL url = url_row.url(); 561 GURL url = url_row.url();
562 ASSERT_TRUE(main_db_->GetRowForURL(url, &url_row)); 562 ASSERT_TRUE(main_db_->GetRowForURL(url, &url_row));
563 563
564 // And the favicon should exist. 564 // And the favicon should exist.
565 FavIconID fav_icon_id = GetFavicon(url_row.url(), FAV_ICON); 565 FavIconID favicon_id = GetFavicon(url_row.url(), FAVICON);
566 EXPECT_TRUE(HasFavIcon(fav_icon_id)); 566 EXPECT_TRUE(HasFavIcon(favicon_id));
567 567
568 // But there should be no fts. 568 // But there should be no fts.
569 ASSERT_EQ(0, CountTextMatchesForURL(url_row.url())); 569 ASSERT_EQ(0, CountTextMatchesForURL(url_row.url()));
570 570
571 // And no visits. 571 // And no visits.
572 VisitVector visits; 572 VisitVector visits;
573 main_db_->GetVisitsForURL(url_row.id(), &visits); 573 main_db_->GetVisitsForURL(url_row.id(), &visits);
574 ASSERT_EQ(0U, visits.size()); 574 ASSERT_EQ(0U, visits.size());
575 575
576 // Should still have the thumbnail. 576 // Should still have the thumbnail.
(...skipping 48 matching lines...) Expand 10 before | Expand all | Expand 10 after
625 URLRow temp_row; 625 URLRow temp_row;
626 ASSERT_TRUE(main_db_->GetURLRow(url_ids[1], &temp_row)); 626 ASSERT_TRUE(main_db_->GetURLRow(url_ids[1], &temp_row));
627 EXPECT_TRUE(visit_times[2] == url_row1.last_visit()); // Previous value. 627 EXPECT_TRUE(visit_times[2] == url_row1.last_visit()); // Previous value.
628 EXPECT_TRUE(visit_times[1] == temp_row.last_visit()); // New value. 628 EXPECT_TRUE(visit_times[1] == temp_row.last_visit()); // New value.
629 EXPECT_EQ(2, url_row1.visit_count()); 629 EXPECT_EQ(2, url_row1.visit_count());
630 EXPECT_EQ(1, temp_row.visit_count()); 630 EXPECT_EQ(1, temp_row.visit_count());
631 EXPECT_EQ(1, url_row1.typed_count()); 631 EXPECT_EQ(1, url_row1.typed_count());
632 EXPECT_EQ(0, temp_row.typed_count()); 632 EXPECT_EQ(0, temp_row.typed_count());
633 633
634 // Verify that the middle URL's favicon and thumbnail is still there. 634 // Verify that the middle URL's favicon and thumbnail is still there.
635 FavIconID fav_icon_id = GetFavicon(url_row1.url(), FAV_ICON); 635 FavIconID favicon_id = GetFavicon(url_row1.url(), FAVICON);
636 EXPECT_TRUE(HasFavIcon(fav_icon_id)); 636 EXPECT_TRUE(HasFavIcon(favicon_id));
637 // TODO(sky): fix this, see comment in HasThumbnail. 637 // TODO(sky): fix this, see comment in HasThumbnail.
638 // EXPECT_TRUE(HasThumbnail(url_row1.id())); 638 // EXPECT_TRUE(HasThumbnail(url_row1.id()));
639 639
640 // Verify that the last URL was deleted. 640 // Verify that the last URL was deleted.
641 FavIconID fav_icon_id2 = GetFavicon(url_row2.url(), FAV_ICON); 641 FavIconID favicon_id2 = GetFavicon(url_row2.url(), FAVICON);
642 EnsureURLInfoGone(url_row2); 642 EnsureURLInfoGone(url_row2);
643 EXPECT_FALSE(HasFavIcon(fav_icon_id2)); 643 EXPECT_FALSE(HasFavIcon(favicon_id2));
644 } 644 }
645 645
646 // Expires only a specific URLs more recent than a given time, with no starred 646 // Expires only a specific URLs more recent than a given time, with no starred
647 // items. Our time threshold is such that the URL should be updated (we delete 647 // items. Our time threshold is such that the URL should be updated (we delete
648 // one of the two visits). 648 // one of the two visits).
649 TEST_F(ExpireHistoryTest, FlushRecentURLsUnstarredRestricted) { 649 TEST_F(ExpireHistoryTest, FlushRecentURLsUnstarredRestricted) {
650 URLID url_ids[3]; 650 URLID url_ids[3];
651 Time visit_times[4]; 651 Time visit_times[4];
652 AddExampleData(url_ids, visit_times); 652 AddExampleData(url_ids, visit_times);
653 653
(...skipping 30 matching lines...) Expand all
684 URLRow temp_row; 684 URLRow temp_row;
685 ASSERT_TRUE(main_db_->GetURLRow(url_ids[1], &temp_row)); 685 ASSERT_TRUE(main_db_->GetURLRow(url_ids[1], &temp_row));
686 EXPECT_TRUE(visit_times[2] == url_row1.last_visit()); // Previous value. 686 EXPECT_TRUE(visit_times[2] == url_row1.last_visit()); // Previous value.
687 EXPECT_TRUE(visit_times[1] == temp_row.last_visit()); // New value. 687 EXPECT_TRUE(visit_times[1] == temp_row.last_visit()); // New value.
688 EXPECT_EQ(2, url_row1.visit_count()); 688 EXPECT_EQ(2, url_row1.visit_count());
689 EXPECT_EQ(1, temp_row.visit_count()); 689 EXPECT_EQ(1, temp_row.visit_count());
690 EXPECT_EQ(1, url_row1.typed_count()); 690 EXPECT_EQ(1, url_row1.typed_count());
691 EXPECT_EQ(0, temp_row.typed_count()); 691 EXPECT_EQ(0, temp_row.typed_count());
692 692
693 // Verify that the middle URL's favicon and thumbnail is still there. 693 // Verify that the middle URL's favicon and thumbnail is still there.
694 FavIconID fav_icon_id = GetFavicon(url_row1.url(), FAV_ICON); 694 FavIconID favicon_id = GetFavicon(url_row1.url(), FAVICON);
695 EXPECT_TRUE(HasFavIcon(fav_icon_id)); 695 EXPECT_TRUE(HasFavIcon(favicon_id));
696 // TODO(sky): fix this, see comment in HasThumbnail. 696 // TODO(sky): fix this, see comment in HasThumbnail.
697 // EXPECT_TRUE(HasThumbnail(url_row1.id())); 697 // EXPECT_TRUE(HasThumbnail(url_row1.id()));
698 698
699 // Verify that the last URL was not touched. 699 // Verify that the last URL was not touched.
700 EXPECT_TRUE(main_db_->GetURLRow(url_ids[2], &temp_row)); 700 EXPECT_TRUE(main_db_->GetURLRow(url_ids[2], &temp_row));
701 EXPECT_TRUE(HasFavIcon(fav_icon_id)); 701 EXPECT_TRUE(HasFavIcon(favicon_id));
702 // TODO(sky): fix this, see comment in HasThumbnail. 702 // TODO(sky): fix this, see comment in HasThumbnail.
703 // EXPECT_TRUE(HasThumbnail(url_row2.id())); 703 // EXPECT_TRUE(HasThumbnail(url_row2.id()));
704 } 704 }
705 705
706 // Expire a starred URL, it shouldn't get deleted 706 // Expire a starred URL, it shouldn't get deleted
707 TEST_F(ExpireHistoryTest, FlushRecentURLsStarred) { 707 TEST_F(ExpireHistoryTest, FlushRecentURLsStarred) {
708 URLID url_ids[3]; 708 URLID url_ids[3];
709 Time visit_times[4]; 709 Time visit_times[4];
710 AddExampleData(url_ids, visit_times); 710 AddExampleData(url_ids, visit_times);
711 711
(...skipping 21 matching lines...) Expand all
733 // Visit/typed count should not be updated for bookmarks. 733 // Visit/typed count should not be updated for bookmarks.
734 EXPECT_EQ(0, new_url_row1.typed_count()); 734 EXPECT_EQ(0, new_url_row1.typed_count());
735 EXPECT_EQ(1, new_url_row1.visit_count()); 735 EXPECT_EQ(1, new_url_row1.visit_count());
736 EXPECT_EQ(0, new_url_row2.typed_count()); 736 EXPECT_EQ(0, new_url_row2.typed_count());
737 EXPECT_EQ(0, new_url_row2.visit_count()); 737 EXPECT_EQ(0, new_url_row2.visit_count());
738 738
739 // Thumbnails and favicons should still exist. Note that we keep thumbnails 739 // Thumbnails and favicons should still exist. Note that we keep thumbnails
740 // that may have been updated since the time threshold. Since the URL still 740 // that may have been updated since the time threshold. Since the URL still
741 // exists in history, this should not be a privacy problem, we only update 741 // exists in history, this should not be a privacy problem, we only update
742 // the visit counts in this case for consistency anyway. 742 // the visit counts in this case for consistency anyway.
743 FavIconID fav_icon_id = GetFavicon(url_row1.url(), FAV_ICON); 743 FavIconID favicon_id = GetFavicon(url_row1.url(), FAVICON);
744 EXPECT_TRUE(HasFavIcon(fav_icon_id)); 744 EXPECT_TRUE(HasFavIcon(favicon_id));
745 // TODO(sky): fix this, see comment in HasThumbnail. 745 // TODO(sky): fix this, see comment in HasThumbnail.
746 // EXPECT_TRUE(HasThumbnail(new_url_row1.id())); 746 // EXPECT_TRUE(HasThumbnail(new_url_row1.id()));
747 fav_icon_id = GetFavicon(url_row1.url(), FAV_ICON); 747 favicon_id = GetFavicon(url_row1.url(), FAVICON);
748 EXPECT_TRUE(HasFavIcon(fav_icon_id)); 748 EXPECT_TRUE(HasFavIcon(favicon_id));
749 // TODO(sky): fix this, see comment in HasThumbnail. 749 // TODO(sky): fix this, see comment in HasThumbnail.
750 // EXPECT_TRUE(HasThumbnail(new_url_row2.id())); 750 // EXPECT_TRUE(HasThumbnail(new_url_row2.id()));
751 } 751 }
752 752
753 TEST_F(ExpireHistoryTest, ArchiveHistoryBeforeUnstarred) { 753 TEST_F(ExpireHistoryTest, ArchiveHistoryBeforeUnstarred) {
754 URLID url_ids[3]; 754 URLID url_ids[3];
755 Time visit_times[4]; 755 Time visit_times[4];
756 AddExampleData(url_ids, visit_times); 756 AddExampleData(url_ids, visit_times);
757 757
758 URLRow url_row1, url_row2; 758 URLRow url_row1, url_row2;
(...skipping 169 matching lines...) Expand 10 before | Expand all | Expand 10 after
928 main_db_->GetVisitsForURL(url_id, &archived_visits); 928 main_db_->GetVisitsForURL(url_id, &archived_visits);
929 EXPECT_EQ(0U, archived_visits.size()); 929 EXPECT_EQ(0U, archived_visits.size());
930 } 930 }
931 931
932 // TODO(brettw) add some visits with no URL to make sure everything is updated 932 // TODO(brettw) add some visits with no URL to make sure everything is updated
933 // properly. Have the visits also refer to nonexistent FTS rows. 933 // properly. Have the visits also refer to nonexistent FTS rows.
934 // 934 //
935 // Maybe also refer to invalid favicons. 935 // Maybe also refer to invalid favicons.
936 936
937 } // namespace history 937 } // namespace history
OLDNEW
« no previous file with comments | « chrome/browser/custom_home_pages_table_model.cc ('k') | chrome/browser/history/history.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698