Home | History | Annotate | Download | only in notifications
      1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style license that can be
      3 // found in the LICENSE file.
      4 
      5 #include "chrome/browser/notifications/desktop_notifications_unittest.h"
      6 
      7 #include "base/prefs/testing_pref_service.h"
      8 #include "base/strings/string_util.h"
      9 #include "base/strings/utf_string_conversions.h"
     10 #include "chrome/browser/notifications/balloon_notification_ui_manager.h"
     11 #include "chrome/browser/notifications/fake_balloon_view.h"
     12 #include "chrome/browser/prefs/browser_prefs.h"
     13 #include "chrome/common/pref_names.h"
     14 #include "chrome/test/base/testing_browser_process.h"
     15 #include "chrome/test/base/testing_profile.h"
     16 #include "chrome/test/base/testing_profile_manager.h"
     17 #include "content/public/common/show_desktop_notification_params.h"
     18 #include "ui/base/ime/input_method_initializer.h"
     19 #include "ui/message_center/message_center.h"
     20 
     21 #if defined(USE_ASH)
     22 #include "ash/shell.h"
     23 #include "ash/test/test_shell_delegate.h"
     24 #include "ui/aura/env.h"
     25 #include "ui/aura/root_window.h"
     26 #include "ui/compositor/scoped_animation_duration_scale_mode.h"
     27 #include "ui/compositor/test/context_factories_for_test.h"
     28 #endif
     29 
     30 
     31 using content::BrowserThread;
     32 
     33 // static
     34 const int MockBalloonCollection::kMockBalloonSpace = 5;
     35 
     36 // static
     37 std::string DesktopNotificationsTest::log_output_;
     38 
     39 MockBalloonCollection::MockBalloonCollection() {}
     40 
     41 MockBalloonCollection::~MockBalloonCollection() {}
     42 
     43 void MockBalloonCollection::Add(const Notification& notification,
     44                                 Profile* profile) {
     45   // Swap in a logging proxy for the purpose of logging calls that
     46   // would be made into javascript, then pass this down to the
     47   // balloon collection.
     48   Notification test_notification(
     49       notification.origin_url(),
     50       notification.content_url(),
     51       notification.display_source(),
     52       notification.replace_id(),
     53       new LoggingNotificationProxy(notification.notification_id()));
     54   BalloonCollectionImpl::Add(test_notification, profile);
     55 }
     56 
     57 bool MockBalloonCollection::HasSpace() const {
     58   return count() < kMockBalloonSpace;
     59 }
     60 
     61 Balloon* MockBalloonCollection::MakeBalloon(const Notification& notification,
     62                                             Profile* profile) {
     63   // Start with a normal balloon but mock out the view.
     64   Balloon* balloon = BalloonCollectionImpl::MakeBalloon(notification, profile);
     65   balloon->set_view(new FakeBalloonView(balloon));
     66   balloons_.push_back(balloon);
     67   return balloon;
     68 }
     69 
     70 void MockBalloonCollection::OnBalloonClosed(Balloon* source) {
     71   std::deque<Balloon*>::iterator it;
     72   for (it = balloons_.begin(); it != balloons_.end(); ++it) {
     73     if (*it == source) {
     74       balloons_.erase(it);
     75       BalloonCollectionImpl::OnBalloonClosed(source);
     76       break;
     77     }
     78   }
     79 }
     80 
     81 const BalloonCollection::Balloons& MockBalloonCollection::GetActiveBalloons() {
     82   return balloons_;
     83 }
     84 
     85 int MockBalloonCollection::UppermostVerticalPosition() {
     86   int min = 0;
     87   std::deque<Balloon*>::iterator iter;
     88   for (iter = balloons_.begin(); iter != balloons_.end(); ++iter) {
     89     int pos = (*iter)->GetPosition().y();
     90     if (iter == balloons_.begin() || pos < min)
     91       min = pos;
     92   }
     93   return min;
     94 }
     95 
     96 DesktopNotificationsTest::DesktopNotificationsTest()
     97     : ui_thread_(BrowserThread::UI, &message_loop_),
     98       balloon_collection_(NULL) {
     99 }
    100 
    101 DesktopNotificationsTest::~DesktopNotificationsTest() {
    102 }
    103 
    104 void DesktopNotificationsTest::SetUp() {
    105   ui::InitializeInputMethodForTesting();
    106 #if defined(USE_ASH)
    107   ui::ScopedAnimationDurationScaleMode normal_duration_mode(
    108       ui::ScopedAnimationDurationScaleMode::ZERO_DURATION);
    109   // The message center is notmally initialized on |g_browser_process| which
    110   // is not created for these tests.
    111   message_center::MessageCenter::Initialize();
    112   // The ContextFactory must exist before any Compositors are created.
    113   bool allow_test_contexts = true;
    114   ui::InitializeContextFactoryForTests(allow_test_contexts);
    115   // MockBalloonCollection retrieves information about the screen on creation.
    116   // So it is necessary to make sure the desktop gets created first.
    117   ash::Shell::CreateInstance(new ash::test::TestShellDelegate);
    118 #endif
    119 
    120   chrome::RegisterLocalState(local_state_.registry());
    121   profile_.reset(new TestingProfile());
    122   ui_manager_.reset(new BalloonNotificationUIManager(&local_state_));
    123   balloon_collection_ = new MockBalloonCollection();
    124   ui_manager_->SetBalloonCollection(balloon_collection_);
    125   service_.reset(new DesktopNotificationService(profile(), ui_manager_.get()));
    126   log_output_.clear();
    127 }
    128 
    129 void DesktopNotificationsTest::TearDown() {
    130   service_.reset(NULL);
    131   ui_manager_.reset(NULL);
    132   profile_.reset(NULL);
    133 #if defined(USE_ASH)
    134   ash::Shell::DeleteInstance();
    135   // The message center is notmally shutdown on |g_browser_process| which
    136   // is not created for these tests.
    137   message_center::MessageCenter::Shutdown();
    138   aura::Env::DeleteInstance();
    139   ui::TerminateContextFactoryForTests();
    140 #endif
    141   ui::ShutdownInputMethodForTesting();
    142 }
    143 
    144 content::ShowDesktopNotificationHostMsgParams
    145 DesktopNotificationsTest::StandardTestNotification() {
    146   content::ShowDesktopNotificationHostMsgParams params;
    147   params.notification_id = 0;
    148   params.origin = GURL("http://www.google.com");
    149   params.icon_url = GURL("/icon.png");
    150   params.title = ASCIIToUTF16("Title");
    151   params.body = ASCIIToUTF16("Text");
    152   params.direction = blink::WebTextDirectionDefault;
    153   return params;
    154 }
    155 
    156 TEST_F(DesktopNotificationsTest, TestShow) {
    157   content::ShowDesktopNotificationHostMsgParams params =
    158       StandardTestNotification();
    159   params.notification_id = 1;
    160 
    161   EXPECT_TRUE(service_->ShowDesktopNotification(
    162       params, 0, 0, DesktopNotificationService::PageNotification));
    163   base::MessageLoopForUI::current()->RunUntilIdle();
    164   EXPECT_EQ(1, balloon_collection_->count());
    165 
    166   content::ShowDesktopNotificationHostMsgParams params2 =
    167       StandardTestNotification();
    168   params2.notification_id = 2;
    169   params2.origin = GURL("http://www.google.com");
    170   params2.body = ASCIIToUTF16("Text");
    171 
    172   EXPECT_TRUE(service_->ShowDesktopNotification(
    173       params2, 0, 0, DesktopNotificationService::PageNotification));
    174   base::MessageLoopForUI::current()->RunUntilIdle();
    175   EXPECT_EQ(2, balloon_collection_->count());
    176 
    177   EXPECT_EQ("notification displayed\n"
    178             "notification displayed\n",
    179             log_output_);
    180 }
    181 
    182 TEST_F(DesktopNotificationsTest, TestClose) {
    183   content::ShowDesktopNotificationHostMsgParams params =
    184       StandardTestNotification();
    185   params.notification_id = 1;
    186 
    187   // Request a notification; should open a balloon.
    188   EXPECT_TRUE(service_->ShowDesktopNotification(
    189       params, 0, 0, DesktopNotificationService::PageNotification));
    190   base::MessageLoopForUI::current()->RunUntilIdle();
    191   EXPECT_EQ(1, balloon_collection_->count());
    192 
    193   // Close all the open balloons.
    194   while (balloon_collection_->count() > 0) {
    195     (*(balloon_collection_->GetActiveBalloons().begin()))->OnClose(true);
    196   }
    197 
    198   EXPECT_EQ("notification displayed\n"
    199             "notification closed by user\n",
    200             log_output_);
    201 }
    202 
    203 TEST_F(DesktopNotificationsTest, TestCancel) {
    204   int process_id = 0;
    205   int route_id = 0;
    206   int notification_id = 1;
    207 
    208   content::ShowDesktopNotificationHostMsgParams params =
    209       StandardTestNotification();
    210   params.notification_id = notification_id;
    211 
    212   // Request a notification; should open a balloon.
    213   EXPECT_TRUE(service_->ShowDesktopNotification(
    214       params, process_id, route_id,
    215       DesktopNotificationService::PageNotification));
    216   base::MessageLoopForUI::current()->RunUntilIdle();
    217   EXPECT_EQ(1, balloon_collection_->count());
    218 
    219   // Cancel the same notification
    220   service_->CancelDesktopNotification(process_id,
    221                                       route_id,
    222                                       notification_id);
    223   base::MessageLoopForUI::current()->RunUntilIdle();
    224   // Verify that the balloon collection is now empty.
    225   EXPECT_EQ(0, balloon_collection_->count());
    226 
    227   EXPECT_EQ("notification displayed\n"
    228             "notification closed by script\n",
    229             log_output_);
    230 }
    231 
    232 #if defined(OS_WIN) || defined(TOOLKIT_VIEWS)
    233 TEST_F(DesktopNotificationsTest, TestPositioning) {
    234   content::ShowDesktopNotificationHostMsgParams params =
    235       StandardTestNotification();
    236   std::string expected_log;
    237   // Create some toasts.  After each but the first, make sure there
    238   // is a minimum separation between the toasts.
    239   int last_top = 0;
    240   for (int id = 0; id <= 3; ++id) {
    241     params.notification_id = id;
    242     EXPECT_TRUE(service_->ShowDesktopNotification(
    243         params, 0, 0, DesktopNotificationService::PageNotification));
    244     expected_log.append("notification displayed\n");
    245     int top = balloon_collection_->UppermostVerticalPosition();
    246     if (id > 0)
    247       EXPECT_LE(top, last_top - balloon_collection_->MinHeight());
    248     last_top = top;
    249   }
    250 
    251   EXPECT_EQ(expected_log, log_output_);
    252 }
    253 
    254 TEST_F(DesktopNotificationsTest, TestVariableSize) {
    255   content::ShowDesktopNotificationHostMsgParams params;
    256   params.origin = GURL("http://long.google.com");
    257   params.icon_url = GURL("/icon.png");
    258   params.title = ASCIIToUTF16("Really Really Really Really Really Really "
    259                               "Really Really Really Really Really Really "
    260                               "Really Really Really Really Really Really "
    261                               "Really Long Title"),
    262   params.body = ASCIIToUTF16("Text");
    263   params.notification_id = 0;
    264 
    265   std::string expected_log;
    266   // Create some toasts.  After each but the first, make sure there
    267   // is a minimum separation between the toasts.
    268   EXPECT_TRUE(service_->ShowDesktopNotification(
    269       params, 0, 0, DesktopNotificationService::PageNotification));
    270   expected_log.append("notification displayed\n");
    271 
    272   params.origin = GURL("http://short.google.com");
    273   params.title = ASCIIToUTF16("Short title");
    274   params.notification_id = 1;
    275   EXPECT_TRUE(service_->ShowDesktopNotification(
    276       params, 0, 0, DesktopNotificationService::PageNotification));
    277   expected_log.append("notification displayed\n");
    278 
    279   std::deque<Balloon*>& balloons = balloon_collection_->balloons();
    280   std::deque<Balloon*>::iterator iter;
    281   for (iter = balloons.begin(); iter != balloons.end(); ++iter) {
    282     if ((*iter)->notification().origin_url().host() == "long.google.com") {
    283       EXPECT_GE((*iter)->GetViewSize().height(),
    284                 balloon_collection_->MinHeight());
    285       EXPECT_LE((*iter)->GetViewSize().height(),
    286                 balloon_collection_->MaxHeight());
    287     } else {
    288       EXPECT_EQ((*iter)->GetViewSize().height(),
    289                 balloon_collection_->MinHeight());
    290     }
    291   }
    292   EXPECT_EQ(expected_log, log_output_);
    293 }
    294 #endif
    295 
    296 TEST_F(DesktopNotificationsTest, TestCancelByProfile) {
    297   int process_id = 0;
    298   int route_id = 0;
    299 
    300   TestingBrowserProcess* browser_process =
    301       TestingBrowserProcess::GetGlobal();
    302   TestingProfileManager profile_manager(browser_process);
    303   ASSERT_TRUE(profile_manager.SetUp());
    304 
    305   TestingProfile* second_profile =
    306       profile_manager.CreateTestingProfile("SecondTestingProfile");
    307 
    308   scoped_ptr<DesktopNotificationService> second_service(
    309       new DesktopNotificationService(second_profile, ui_manager_.get()));
    310 
    311   // Request lots of identical notifications.
    312   content::ShowDesktopNotificationHostMsgParams params =
    313       StandardTestNotification();
    314   params.notification_id = 1;
    315   // Notice that the first one is the only one that doesn't use
    316   // the second profile.
    317   EXPECT_TRUE(service_->ShowDesktopNotification(
    318       params, process_id, route_id,
    319       DesktopNotificationService::PageNotification));
    320 
    321   // |kLotsOfToasts| must be large enough to trigger a resize of the underlying
    322   // std::deque while we're clearing it.
    323   const int kLotsOfToasts = 20;
    324   for (int id = 2; id <= kLotsOfToasts; ++id) {
    325     params.notification_id = id;
    326     EXPECT_TRUE(second_service->ShowDesktopNotification(
    327         params, process_id, route_id,
    328         DesktopNotificationService::PageNotification));
    329   }
    330   base::MessageLoopForUI::current()->RunUntilIdle();
    331 
    332   ui_manager_->CancelAllByProfile(second_profile);
    333 
    334   // Verify that the balloon collection only contains the single
    335   // notification from the first profile.
    336   EXPECT_EQ(1, balloon_collection_->count());
    337 }
    338 
    339 TEST_F(DesktopNotificationsTest, TestCancelBySourceOrigin) {
    340   int process_id = 0;
    341   int route_id = 0;
    342 
    343   // Request lots of identical notifications.
    344   content::ShowDesktopNotificationHostMsgParams params =
    345       StandardTestNotification();
    346 
    347   // After the first, all the notifications are from attacker.com.
    348   content::ShowDesktopNotificationHostMsgParams odd_params =
    349       StandardTestNotification();
    350   odd_params.origin = GURL("attacker.com");
    351 
    352   // Show the only non-attacker.com notification.
    353   params.notification_id = 1;
    354   EXPECT_TRUE(service_->ShowDesktopNotification(
    355       params, process_id, route_id,
    356       DesktopNotificationService::PageNotification));
    357 
    358   // |kLotsOfToasts| must be large enough to trigger a resize of the underlying
    359   // std::deque while we're clearing it.
    360   const int kLotsOfToasts = 20;
    361   for (int id = 2; id <= kLotsOfToasts; ++id) {
    362     odd_params.notification_id = id;
    363     EXPECT_TRUE(service_->ShowDesktopNotification(
    364         odd_params, process_id, route_id,
    365         DesktopNotificationService::PageNotification));
    366   }
    367   base::MessageLoopForUI::current()->RunUntilIdle();
    368 
    369   ui_manager_->CancelAllBySourceOrigin(odd_params.origin);
    370 
    371   // Verify that the balloon collection only contains the single
    372   // notification which is not from the canceled origin.
    373   EXPECT_EQ(1, balloon_collection_->count());
    374 }
    375 
    376 TEST_F(DesktopNotificationsTest, TestQueueing) {
    377   int process_id = 0;
    378   int route_id = 0;
    379 
    380   // Request lots of identical notifications.
    381   content::ShowDesktopNotificationHostMsgParams params =
    382       StandardTestNotification();
    383   const int kLotsOfToasts = 20;
    384   for (int id = 1; id <= kLotsOfToasts; ++id) {
    385     params.notification_id = id;
    386     EXPECT_TRUE(service_->ShowDesktopNotification(
    387         params, process_id, route_id,
    388         DesktopNotificationService::PageNotification));
    389   }
    390   base::MessageLoopForUI::current()->RunUntilIdle();
    391 
    392   // Build up an expected log of what should be happening.
    393   std::string expected_log;
    394   for (int i = 0; i < balloon_collection_->max_balloon_count(); ++i) {
    395     expected_log.append("notification displayed\n");
    396   }
    397 
    398   // The max number that our balloon collection can hold should be
    399   // shown.
    400   EXPECT_EQ(balloon_collection_->max_balloon_count(),
    401             balloon_collection_->count());
    402   EXPECT_EQ(expected_log, log_output_);
    403 
    404   // Cancel the notifications from the start; the balloon space should
    405   // remain full.
    406   {
    407     int id;
    408     for (id = 1;
    409          id <= kLotsOfToasts - balloon_collection_->max_balloon_count();
    410          ++id) {
    411       service_->CancelDesktopNotification(process_id, route_id, id);
    412       base::MessageLoopForUI::current()->RunUntilIdle();
    413       expected_log.append("notification closed by script\n");
    414       expected_log.append("notification displayed\n");
    415       EXPECT_EQ(balloon_collection_->max_balloon_count(),
    416                 balloon_collection_->count());
    417       EXPECT_EQ(expected_log, log_output_);
    418     }
    419 
    420     // Now cancel the rest.  It should empty the balloon space.
    421     for (; id <= kLotsOfToasts; ++id) {
    422       service_->CancelDesktopNotification(process_id, route_id, id);
    423       expected_log.append("notification closed by script\n");
    424       base::MessageLoopForUI::current()->RunUntilIdle();
    425       EXPECT_EQ(expected_log, log_output_);
    426     }
    427   }
    428 
    429   // Verify that the balloon collection is now empty.
    430   EXPECT_EQ(0, balloon_collection_->count());
    431 }
    432 
    433 TEST_F(DesktopNotificationsTest, TestEarlyDestruction) {
    434   // Create some toasts and then prematurely delete the notification service,
    435   // just to make sure nothing crashes/leaks.
    436   content::ShowDesktopNotificationHostMsgParams params =
    437       StandardTestNotification();
    438   for (int id = 0; id <= 3; ++id) {
    439     params.notification_id = id;
    440     EXPECT_TRUE(service_->ShowDesktopNotification(
    441         params, 0, 0, DesktopNotificationService::PageNotification));
    442   }
    443   service_.reset(NULL);
    444 }
    445 
    446 TEST_F(DesktopNotificationsTest, TestUserInputEscaping) {
    447   // Create a test script with some HTML; assert that it doesn't get into the
    448   // data:// URL that's produced for the balloon.
    449   content::ShowDesktopNotificationHostMsgParams params =
    450       StandardTestNotification();
    451   params.title = ASCIIToUTF16("<script>window.alert('uh oh');</script>");
    452   params.body = ASCIIToUTF16("<i>this text is in italics</i>");
    453   params.notification_id = 1;
    454   EXPECT_TRUE(service_->ShowDesktopNotification(
    455       params, 0, 0, DesktopNotificationService::PageNotification));
    456 
    457   base::MessageLoopForUI::current()->RunUntilIdle();
    458   EXPECT_EQ(1, balloon_collection_->count());
    459   Balloon* balloon = (*balloon_collection_->balloons().begin());
    460   GURL data_url = balloon->notification().content_url();
    461   EXPECT_EQ(std::string::npos, data_url.spec().find("<script>"));
    462   EXPECT_EQ(std::string::npos, data_url.spec().find("<i>"));
    463   // URL-encoded versions of tags should also not be found.
    464   EXPECT_EQ(std::string::npos, data_url.spec().find("%3cscript%3e"));
    465   EXPECT_EQ(std::string::npos, data_url.spec().find("%3ci%3e"));
    466 }
    467 
    468 TEST_F(DesktopNotificationsTest, TestBoundingBox) {
    469   // Create some notifications.
    470   content::ShowDesktopNotificationHostMsgParams params =
    471       StandardTestNotification();
    472   for (int id = 0; id <= 3; ++id) {
    473     params.notification_id = id;
    474     EXPECT_TRUE(service_->ShowDesktopNotification(
    475         params, 0, 0, DesktopNotificationService::PageNotification));
    476   }
    477 
    478   gfx::Rect box = balloon_collection_->GetBalloonsBoundingBox();
    479 
    480   // Try this for all positions.
    481   BalloonCollection::PositionPreference pref;
    482   for (pref = BalloonCollection::UPPER_RIGHT;
    483        pref <= BalloonCollection::LOWER_LEFT;
    484        pref = static_cast<BalloonCollection::PositionPreference>(pref + 1)) {
    485     // Make sure each balloon's 4 corners are inside the box.
    486     std::deque<Balloon*>& balloons = balloon_collection_->balloons();
    487     std::deque<Balloon*>::iterator iter;
    488     for (iter = balloons.begin(); iter != balloons.end(); ++iter) {
    489       int min_x = (*iter)->GetPosition().x();
    490       int max_x = min_x + (*iter)->GetViewSize().width() - 1;
    491       int min_y = (*iter)->GetPosition().y();
    492       int max_y = min_y + (*iter)->GetViewSize().height() - 1;
    493 
    494       EXPECT_TRUE(box.Contains(gfx::Point(min_x, min_y)));
    495       EXPECT_TRUE(box.Contains(gfx::Point(min_x, max_y)));
    496       EXPECT_TRUE(box.Contains(gfx::Point(max_x, min_y)));
    497       EXPECT_TRUE(box.Contains(gfx::Point(max_x, max_y)));
    498     }
    499   }
    500 }
    501 
    502 TEST_F(DesktopNotificationsTest, TestPositionPreference) {
    503   // Set position preference to lower right.
    504   local_state_.SetInteger(prefs::kDesktopNotificationPosition,
    505                           BalloonCollection::LOWER_RIGHT);
    506 
    507   // Create some notifications.
    508   content::ShowDesktopNotificationHostMsgParams params =
    509       StandardTestNotification();
    510   for (int id = 0; id <= 3; ++id) {
    511     params.notification_id = id;
    512     EXPECT_TRUE(service_->ShowDesktopNotification(
    513         params, 0, 0, DesktopNotificationService::PageNotification));
    514   }
    515 
    516   std::deque<Balloon*>& balloons = balloon_collection_->balloons();
    517   std::deque<Balloon*>::iterator iter;
    518 
    519   // Check that they decrease in y-position (for MAC, with reversed
    520   // coordinates, they should increase).
    521   int last_y = -1;
    522   int last_x = -1;
    523 
    524   for (iter = balloons.begin(); iter != balloons.end(); ++iter) {
    525     int current_x = (*iter)->GetPosition().x();
    526     int current_y = (*iter)->GetPosition().y();
    527     if (last_x > 0)
    528       EXPECT_EQ(last_x, current_x);
    529 
    530     if (last_y > 0) {
    531 #if defined(OS_MACOSX)
    532       EXPECT_GT(current_y, last_y);
    533 #else
    534       EXPECT_LT(current_y, last_y);
    535 #endif
    536     }
    537 
    538     last_x = current_x;
    539     last_y = current_y;
    540   }
    541 
    542   // Now change the position to upper right.  This should cause an immediate
    543   // repositioning, and we check for the reverse ordering.
    544   local_state_.SetInteger(prefs::kDesktopNotificationPosition,
    545                           BalloonCollection::UPPER_RIGHT);
    546   last_x = -1;
    547   last_y = -1;
    548 
    549   for (iter = balloons.begin(); iter != balloons.end(); ++iter) {
    550     int current_x = (*iter)->GetPosition().x();
    551     int current_y = (*iter)->GetPosition().y();
    552 
    553     if (last_x > 0)
    554       EXPECT_EQ(last_x, current_x);
    555 
    556     if (last_y > 0) {
    557 #if defined(OS_MACOSX)
    558       EXPECT_LT(current_y, last_y);
    559 #else
    560       EXPECT_GT(current_y, last_y);
    561 #endif
    562     }
    563 
    564     last_x = current_x;
    565     last_y = current_y;
    566   }
    567 
    568   // Now change the position to upper left.  Confirm that the X value for the
    569   // balloons gets smaller.
    570   local_state_.SetInteger(prefs::kDesktopNotificationPosition,
    571                           BalloonCollection::UPPER_LEFT);
    572 
    573   int current_x = (*balloons.begin())->GetPosition().x();
    574   EXPECT_LT(current_x, last_x);
    575 }
    576