Home | History | Annotate | Download | only in tests
      1 // Copyright (c) 2011 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 "ppapi/tests/test_view.h"
      6 
      7 #include <sstream>
      8 
      9 #include "ppapi/c/pp_time.h"
     10 #include "ppapi/c/private/ppb_testing_private.h"
     11 #include "ppapi/cpp/completion_callback.h"
     12 #include "ppapi/tests/testing_instance.h"
     13 
     14 REGISTER_TEST_CASE(View);
     15 
     16 // When waiting for view changed events, wait no longer than this.
     17 #if !defined(THREAD_SANITIZER)
     18 static int kViewChangeTimeoutSec = 5;
     19 #else
     20 // ThreadSanitizer may slow the interaction down significantly.
     21 static int kViewChangeTimeoutSec = 30;
     22 #endif
     23 
     24 TestView::TestView(TestingInstance* instance)
     25     : TestCase(instance),
     26       post_quit_on_view_changed_(false) {
     27 }
     28 
     29 void TestView::DidChangeView(const pp::View& view) {
     30   last_view_ = view;
     31   page_visibility_log_.push_back(view.IsPageVisible());
     32 
     33   if (post_quit_on_view_changed_) {
     34     post_quit_on_view_changed_ = false;
     35     testing_interface_->QuitMessageLoop(instance_->pp_instance());
     36   }
     37 }
     38 
     39 bool TestView::Init() {
     40   return CheckTestingInterface();
     41 }
     42 
     43 void TestView::RunTests(const std::string& filter) {
     44   RUN_TEST(CreatedVisible, filter);
     45   RUN_TEST(CreatedInvisible, filter);
     46   RUN_TEST(PageHideShow, filter);
     47   RUN_TEST(SizeChange, filter);
     48   RUN_TEST(ClipChange, filter);
     49   RUN_TEST(ScrollOffsetChange, filter);
     50 }
     51 
     52 bool TestView::WaitUntilViewChanged() {
     53   // Schedule a callback so this step times out if we don't get a ViewChanged
     54   // in a reasonable amount of time.
     55   pp::CompletionCallbackFactory<TestView> factory(this);
     56   pp::CompletionCallback timeout =
     57       factory.NewCallback(&TestView::QuitMessageLoop);
     58   pp::Module::Get()->core()->CallOnMainThread(
     59       kViewChangeTimeoutSec * 1000, timeout);
     60 
     61   size_t old_page_visibility_change_count = page_visibility_log_.size();
     62 
     63   // Run a nested message loop. It will exit either on ViewChanged or if the
     64   // timeout happens.
     65   post_quit_on_view_changed_ = true;
     66   testing_interface_->RunMessageLoop(instance_->pp_instance());
     67   post_quit_on_view_changed_ = false;
     68 
     69   // We know we got a view changed event if something was appended to the log.
     70   return page_visibility_log_.size() > old_page_visibility_change_count;
     71 }
     72 
     73 void TestView::QuitMessageLoop(int32_t result) {
     74   testing_interface_->QuitMessageLoop(instance_->pp_instance());
     75 }
     76 
     77 std::string TestView::TestCreatedVisible() {
     78   ASSERT_FALSE(page_visibility_log_.empty());
     79   ASSERT_TRUE(page_visibility_log_[0]);
     80   PASS();
     81 }
     82 
     83 std::string TestView::TestCreatedInvisible() {
     84   ASSERT_FALSE(page_visibility_log_.empty());
     85 
     86   if (page_visibility_log_[0]) {
     87     // Add more error message since this test has some extra requirements.
     88     instance_->AppendError("Initial page is set to visible. NOTE: "
     89         "This test must be run in a background tab. "
     90         "Either run in the UI test which does this, or you can middle-click "
     91         "on the test link to run manually.");
     92   }
     93   ASSERT_FALSE(page_visibility_log_[0]);
     94   PASS();
     95 }
     96 
     97 std::string TestView::TestPageHideShow() {
     98   // Initial state should be visible.
     99   ASSERT_FALSE(page_visibility_log_.empty());
    100   ASSERT_TRUE(page_visibility_log_[0]);
    101 
    102   // Now that we're alive, tell the test knows it can change our visibility.
    103   instance_->ReportProgress("TestPageHideShow:Created");
    104 
    105   // Wait until we get a hide event, being careful to handle spurious
    106   // notifications of ViewChanged.
    107   PP_Time begin_time = pp::Module::Get()->core()->GetTime();
    108   while (WaitUntilViewChanged() &&
    109          page_visibility_log_[page_visibility_log_.size() - 1] &&
    110          pp::Module::Get()->core()->GetTime() - begin_time <
    111              kViewChangeTimeoutSec) {
    112   }
    113   if (page_visibility_log_[page_visibility_log_.size() - 1]) {
    114     // Didn't get a view changed event that changed visibility (though there
    115     // may have been some that didn't change visibility).
    116     // Add more error message since this test has some extra requirements.
    117     return "Didn't receive a hide event in timeout. NOTE: "
    118         "This test requires tab visibility to change and won't pass if you "
    119         "just run it in a browser. Normally the UI test should handle "
    120         "this. You can also run manually by waiting 2 secs, creating a new "
    121         "tab, waiting 2 more secs, and closing the new tab.";
    122   }
    123 
    124   // Tell the test so it can show us again.
    125   instance_->ReportProgress("TestPageHideShow:Hidden");
    126 
    127   // Wait until we get a show event.
    128   begin_time = pp::Module::Get()->core()->GetTime();
    129   while (WaitUntilViewChanged() &&
    130          !page_visibility_log_[page_visibility_log_.size() - 1] &&
    131          pp::Module::Get()->core()->GetTime() - begin_time <
    132              kViewChangeTimeoutSec) {
    133   }
    134   ASSERT_TRUE(page_visibility_log_[page_visibility_log_.size() - 1]);
    135 
    136   PASS();
    137 }
    138 
    139 std::string TestView::TestSizeChange() {
    140   pp::Rect original_rect = last_view_.GetRect();
    141 
    142   pp::Rect desired_rect = original_rect;
    143   desired_rect.set_width(original_rect.width() + 10);
    144   desired_rect.set_height(original_rect.height() + 12);
    145 
    146   std::ostringstream script_stream;
    147   script_stream << "var plugin = document.getElementById('plugin');";
    148   script_stream << "plugin.setAttribute('width', "
    149                 << desired_rect.width() << ");";
    150   script_stream << "plugin.setAttribute('height', "
    151                 << desired_rect.height() << ");";
    152 
    153   instance_->EvalScript(script_stream.str());
    154 
    155   PP_Time begin_time = pp::Module::Get()->core()->GetTime();
    156   while (WaitUntilViewChanged() && last_view_.GetRect() != desired_rect &&
    157          pp::Module::Get()->core()->GetTime() - begin_time <
    158              kViewChangeTimeoutSec) {
    159   }
    160   ASSERT_TRUE(last_view_.GetRect() == desired_rect);
    161 
    162   PASS();
    163 }
    164 
    165 std::string TestView::TestClipChange() {
    166   pp::Rect original_rect = last_view_.GetRect();
    167 
    168   // Original clip should be the full frame.
    169   pp::Rect original_clip = last_view_.GetClipRect();
    170   ASSERT_TRUE(original_clip.x() == 0);
    171   ASSERT_TRUE(original_clip.y() == 0);
    172   ASSERT_TRUE(original_clip.width() == original_rect.width());
    173   ASSERT_TRUE(original_clip.height() == original_rect.height());
    174 
    175   int clip_amount = original_rect.height() / 2;
    176 
    177   // It might be nice to set the position to be absolute and set the location,
    178   // but this will cause WebKit to actually tear down the plugin and recreate
    179   // it. So instead we add a big div to cause the document to be scrollable,
    180   // and scroll it down.
    181   std::ostringstream script_stream;
    182   script_stream
    183       << "var big = document.createElement('div');"
    184       << "big.setAttribute('style', 'position:absolute; left:100px; "
    185                                     "top:0px; width:1px; height:5000px;');"
    186       << "document.body.appendChild(big);"
    187       << "window.scrollBy(0, " << original_rect.y() + clip_amount << ");";
    188 
    189   instance_->EvalScript(script_stream.str());
    190 
    191   pp::Rect desired_clip = original_clip;
    192   desired_clip.set_y(clip_amount);
    193   desired_clip.set_height(desired_clip.height() - desired_clip.y());
    194 
    195   PP_Time begin_time = pp::Module::Get()->core()->GetTime();
    196   while (WaitUntilViewChanged() && last_view_.GetClipRect() != desired_clip &&
    197          pp::Module::Get()->core()->GetTime() - begin_time <
    198              kViewChangeTimeoutSec) {
    199   }
    200   ASSERT_TRUE(last_view_.GetClipRect() == desired_clip);
    201   PASS();
    202 }
    203 
    204 std::string TestView::TestScrollOffsetChange() {
    205   instance_->EvalScript("document.body.style.width = '5000px';"
    206                         "document.body.style.height = '5000px';");
    207   instance_->EvalScript("window.scrollTo(5, 1);");
    208 
    209   PP_Time begin_time = pp::Module::Get()->core()->GetTime();
    210   while (WaitUntilViewChanged() &&
    211          last_view_.GetScrollOffset() != pp::Point(5, 1) &&
    212          pp::Module::Get()->core()->GetTime() - begin_time <
    213              kViewChangeTimeoutSec) {
    214   }
    215   ASSERT_EQ(pp::Point(5, 1), last_view_.GetScrollOffset());
    216 
    217   instance_->EvalScript("window.scrollTo(0, 0);");
    218 
    219   begin_time = pp::Module::Get()->core()->GetTime();
    220   while (WaitUntilViewChanged() &&
    221          last_view_.GetScrollOffset() != pp::Point(0, 0) &&
    222          pp::Module::Get()->core()->GetTime() - begin_time <
    223              kViewChangeTimeoutSec) {
    224   }
    225   ASSERT_EQ(pp::Point(0, 0), last_view_.GetScrollOffset());
    226 
    227   PASS();
    228 }
    229