Home | History | Annotate | Download | only in tests
      1 // Copyright 2014 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_message_handler.h"
      6 
      7 #include <string.h>
      8 #include <algorithm>
      9 #include <map>
     10 #include <sstream>
     11 
     12 #include "ppapi/c/pp_var.h"
     13 #include "ppapi/c/ppb_file_io.h"
     14 #include "ppapi/c/ppb_messaging.h"
     15 #include "ppapi/c/ppp_message_handler.h"
     16 #include "ppapi/cpp/completion_callback.h"
     17 #include "ppapi/cpp/file_io.h"
     18 #include "ppapi/cpp/file_ref.h"
     19 #include "ppapi/cpp/file_system.h"
     20 #include "ppapi/cpp/instance.h"
     21 #include "ppapi/cpp/message_handler.h"
     22 #include "ppapi/cpp/module_impl.h"
     23 #include "ppapi/cpp/network_list.h"
     24 #include "ppapi/cpp/network_monitor.h"
     25 #include "ppapi/cpp/var.h"
     26 #include "ppapi/cpp/var_array.h"
     27 #include "ppapi/cpp/var_array_buffer.h"
     28 #include "ppapi/cpp/var_dictionary.h"
     29 #include "ppapi/tests/pp_thread.h"
     30 #include "ppapi/tests/test_utils.h"
     31 #include "ppapi/tests/testing_instance.h"
     32 
     33 // Windows defines 'PostMessage', so we have to undef it.
     34 #ifdef PostMessage
     35 #undef PostMessage
     36 #endif
     37 
     38 REGISTER_TEST_CASE(MessageHandler);
     39 
     40 namespace {
     41 
     42 // Created and destroyed on the main thread. All public methods should be called
     43 // on the main thread. Most data members are only accessed on the main thread.
     44 // (Though it handles messages on the background thread).
     45 class EchoingMessageHandler : public pp::MessageHandler {
     46  public:
     47   explicit EchoingMessageHandler(TestingInstance* instance,
     48                                  const pp::MessageLoop& loop)
     49       : testing_instance_(instance),
     50         message_handler_loop_(loop),
     51         is_registered_(false),
     52         test_finished_event_(instance->pp_instance()),
     53         destroy_event_(instance->pp_instance()) {
     54     AssertOnMainThread();
     55   }
     56   void Register() {
     57     AssertOnMainThread();
     58     assert(!is_registered_);
     59     int32_t result =
     60         testing_instance_->RegisterMessageHandler(this, message_handler_loop_);
     61     if (result == PP_OK) {
     62       is_registered_ = true;
     63     } else {
     64       std::ostringstream stream;
     65       stream << "Failed to register message handler; got error " << result;
     66       AddError(stream.str());
     67       test_finished_event_.Signal();
     68     }
     69     // Note, at this point, we can't safely read or write errors_ until we wait
     70     // on destroy_event_.
     71   }
     72   void Unregister() {
     73     AssertOnMainThread();
     74     assert(is_registered_);
     75     testing_instance_->UnregisterMessageHandler();
     76     is_registered_ = false;
     77   }
     78   void WaitForTestFinishedMessage() {
     79     test_finished_event_.Wait();
     80     test_finished_event_.Reset();
     81   }
     82   // Wait for Destroy() to be called on the MessageHandler thread. When it's
     83   // done, return any errors that occurred during the time the MessageHandler
     84   // was getting messages.
     85   std::string WaitForDestroy() {
     86     AssertOnMainThread();
     87     // If we haven't called Unregister, we'll be waiting forever.
     88     assert(!is_registered_);
     89     destroy_event_.Wait();
     90     destroy_event_.Reset();
     91     // Now that we know Destroy() has been called, we know errors_ isn't being
     92     // written on the MessageHandler thread anymore. So we can safely read it
     93     // here on the main thread (since destroy_event_ gave us a memory barrier).
     94     std::string temp_errors;
     95     errors_.swap(temp_errors);
     96     return temp_errors;
     97   }
     98  private:
     99   static void AssertOnMainThread() {
    100     assert(pp::MessageLoop::GetForMainThread() ==
    101            pp::MessageLoop::GetCurrent());
    102   }
    103   void AddError(const std::string& error) {
    104     if (!error.empty()) {
    105       if (!errors_.empty())
    106         errors_ += "<p>";
    107       errors_ += error;
    108     }
    109   }
    110   virtual void HandleMessage(pp::InstanceHandle instance, const pp::Var& var) {
    111     if (pp::MessageLoop::GetCurrent() != message_handler_loop_)
    112       AddError("HandleMessage was called on the wrong thread!");
    113     if (instance.pp_instance() != testing_instance_->pp_instance())
    114       AddError("HandleMessage was passed the wrong instance!");
    115     if (var.is_string() && var.AsString() == "FINISHED_TEST")
    116       test_finished_event_.Signal();
    117     else
    118       testing_instance_->PostMessage(var);
    119   }
    120 
    121   virtual pp::Var HandleBlockingMessage(pp::InstanceHandle instance,
    122                                         const pp::Var& var) {
    123     if (pp::MessageLoop::GetCurrent() != message_handler_loop_)
    124       AddError("HandleBlockingMessage was called on the wrong thread!");
    125     if (instance.pp_instance() != testing_instance_->pp_instance())
    126       AddError("HandleBlockingMessage was passed the wrong instance!");
    127 
    128     // Attempt a blocking operation; make sure it's disallowed.
    129     pp::NetworkMonitor monitor(instance);
    130     PP_Resource out_param = 0;
    131     pp::CompletionCallbackWithOutput<pp::NetworkList> blocking_callback(
    132         &out_param);
    133     int32_t error = monitor.UpdateNetworkList(blocking_callback);
    134     if (error != PP_ERROR_WOULD_BLOCK_THREAD) {
    135       AddError("HandleBlockingMessage was allowed to do a blocking call!");
    136       pp::Module::Get()->core()->ReleaseResource(out_param);
    137     }
    138 
    139     return var;
    140   }
    141 
    142   virtual void WasUnregistered(pp::InstanceHandle instance) {
    143     if (pp::MessageLoop::GetCurrent() != message_handler_loop_)
    144       AddError("Destroy was called on the wrong thread!");
    145     if (instance.pp_instance() != testing_instance_->pp_instance())
    146       AddError("Destroy was passed the wrong instance!");
    147     destroy_event_.Signal();
    148   }
    149 
    150   // These data members are initialized on the main thread, but don't change for
    151   // the life of the object, so are safe to access on the background thread,
    152   // because there will be a memory barrier before the the MessageHandler calls
    153   // are invoked.
    154   TestingInstance* const testing_instance_;
    155   const pp::MessageLoop message_handler_loop_;
    156   const pp::MessageLoop main_loop_;
    157 
    158   // is_registered_ is only read/written on the main thread.
    159   bool is_registered_;
    160 
    161   // errors_ is written on the MessageHandler thread. When Destroy() is
    162   // called, we stop writing to errors_ and signal destroy_event_. This causes
    163   // a memory barrier, so it's safe to read errors_ after that.
    164   std::string errors_;
    165   NestedEvent test_finished_event_;
    166   NestedEvent destroy_event_;
    167 
    168   // Undefined & private to disallow copy and assign.
    169   EchoingMessageHandler(const EchoingMessageHandler&);
    170   EchoingMessageHandler& operator=(const EchoingMessageHandler&);
    171 };
    172 
    173 void FakeHandleMessage(PP_Instance instance,
    174                        void* user_data,
    175                        const PP_Var* message_data) {}
    176 void FakeHandleBlockingMessage(PP_Instance instance,
    177                                void* user_data,
    178                                const PP_Var* message_data,
    179                                PP_Var* result) {}
    180 void FakeDestroy(PP_Instance instance, void* user_data) {}
    181 
    182 }  // namespace
    183 
    184 TestMessageHandler::TestMessageHandler(TestingInstance* instance)
    185     : TestCase(instance),
    186       ppb_messaging_if_(NULL),
    187       handler_thread_(instance),
    188       message_received_(instance->pp_instance()) {
    189 }
    190 
    191 TestMessageHandler::~TestMessageHandler() {
    192   handler_thread_.Join();
    193 }
    194 
    195 bool TestMessageHandler::Init() {
    196   ppb_messaging_if_ = static_cast<const PPB_Messaging_1_2*>(
    197       pp::Module::Get()->GetBrowserInterface(PPB_MESSAGING_INTERFACE_1_2));
    198   return ppb_messaging_if_ &&
    199          CheckTestingInterface() &&
    200          handler_thread_.Start();
    201 }
    202 
    203 void TestMessageHandler::RunTests(const std::string& filter) {
    204   RUN_TEST(RegisterErrorConditions, filter);
    205   RUN_TEST(PostMessageAndAwaitResponse, filter);
    206   RUN_TEST(Exceptions, filter);
    207 }
    208 
    209 void TestMessageHandler::HandleMessage(const pp::Var& message_data) {
    210   if (instance()->current_test_name() == "Exceptions") {
    211     // For TestPostMessageAndAwaitResponse(), all messages should go to the
    212     // background thread message handler.
    213     assert(false);
    214   } else {
    215     if (message_data.is_string()) {
    216       last_message_ = message_data.AsString();
    217     } else {
    218       last_message_ = "message_data was not a string!";
    219     }
    220     message_received_.Signal();
    221   }
    222 }
    223 
    224 std::string TestMessageHandler::TestRegisterErrorConditions() {
    225   {
    226     // Test registering with the main thread as the message loop.
    227     PPP_MessageHandler_0_2 fake_ppp_message_handler = {
    228       &FakeHandleMessage, &FakeHandleBlockingMessage, &FakeDestroy
    229     };
    230     pp::MessageLoop main_loop = pp::MessageLoop::GetForMainThread();
    231     int32_t result = ppb_messaging_if_->RegisterMessageHandler(
    232         instance()->pp_instance(),
    233         reinterpret_cast<void*>(0xdeadbeef),
    234         &fake_ppp_message_handler,
    235         main_loop.pp_resource());
    236     ASSERT_EQ(PP_ERROR_WRONG_THREAD, result);
    237   }
    238   {
    239     // Test registering with incomplete PPP_Messaging interface.
    240     PPP_MessageHandler_0_2 bad_ppp_ifs[] = {
    241         { NULL, &FakeHandleBlockingMessage, &FakeDestroy },
    242         { &FakeHandleMessage, NULL, &FakeDestroy },
    243         { &FakeHandleMessage, &FakeHandleBlockingMessage, NULL }};
    244     for (size_t i = 0; i < sizeof(bad_ppp_ifs)/sizeof(bad_ppp_ifs[0]); ++i) {
    245       int32_t result = ppb_messaging_if_->RegisterMessageHandler(
    246           instance()->pp_instance(),
    247           reinterpret_cast<void*>(0xdeadbeef),
    248           &bad_ppp_ifs[i],
    249           handler_thread_.message_loop().pp_resource());
    250       ASSERT_EQ(PP_ERROR_BADARGUMENT, result);
    251     }
    252   }
    253   PASS();
    254 }
    255 
    256 std::string TestMessageHandler::TestPostMessageAndAwaitResponse() {
    257   EchoingMessageHandler handler(instance(),
    258                                 handler_thread_.message_loop());
    259   // Test doing a sync call before the handler is registered.
    260   handler.Register();
    261   std::string js_code("var plugin = document.getElementById('plugin');\n");
    262   js_code += "var result = undefined;\n";
    263   const char* const values_to_test[] = {
    264       "5",
    265       "undefined",
    266       "1.5",
    267       "'hello'",
    268       "{'key': 'value', 'array_key': [1, 2, 3, 4, 5]}",
    269       NULL
    270   };
    271   for (size_t i = 0; values_to_test[i]; ++i) {
    272     js_code += "result = plugin.postMessageAndAwaitResponse(";
    273     js_code +=     values_to_test[i];
    274     js_code += ");\n";
    275     js_code += "if (!deepCompare(result, ";
    276     js_code +=     values_to_test[i];
    277     js_code += "))\n";
    278     js_code += "  InternalError(\" Failed postMessageAndAwaitResponse for: ";
    279     js_code +=      values_to_test[i];
    280     js_code +=    " result: \" + result);\n";
    281   }
    282   instance_->EvalScript(js_code);
    283   instance_->EvalScript("plugin.postMessage('FINISHED_TEST');\n");
    284   handler.WaitForTestFinishedMessage();
    285   handler.Unregister();
    286   ASSERT_SUBTEST_SUCCESS(handler.WaitForDestroy());
    287 
    288   PASS();
    289 }
    290 
    291 std::string TestMessageHandler::TestExceptions() {
    292   EchoingMessageHandler handler(instance(),
    293                                 handler_thread_.message_loop());
    294   {
    295     // First, try sending a blocking message when there is no handler
    296     // registered. It should throw an exception.
    297     std::string js_code(
    298         "var plugin = document.getElementById('plugin');\n"
    299         "var caught_exception = false;\n"
    300         "try {\n"
    301         "  plugin.postMessageAndAwaitResponse('Hello!');\n"
    302         "} catch (err) {\n"
    303         "  caught_exception = true;\n"
    304         "}\n"
    305         "plugin.postMessage(caught_exception ? 'SUCCESS' : 'FAIL');\n");
    306     instance_->EvalScript(js_code);
    307     message_received_.Wait();
    308     ASSERT_EQ("SUCCESS", last_message_);
    309   }
    310   handler.Register();
    311   {
    312     // Now that a handler is registered, try requesting and sending a
    313     // FileSystem. It should throw an exception. The file system is opened
    314     // asynchronously. What *should* happen is that it opens successfully, then
    315     // we try to send it via postMessageAndAwaitResponse, which fails with an
    316     // exception. The test could fail either because the filesystem doesn't
    317     // open or because postMessageAndAwaitResponse doesn't throw an exception.
    318     std::string js_code(
    319         "var plugin = document.getElementById('plugin');\n"
    320         "function gotFileSystem(fs) {\n"
    321         "  var caught_exception = false;\n"
    322         "  try {\n"
    323         "    plugin.postMessageAndAwaitResponse(fs);\n"
    324         "  } catch (err) {\n"
    325         "    caught_exception = true;\n"
    326         "  }\n"
    327         "  plugin.postMessage(caught_exception ? 'SUCCESS' : 'FAIL');\n"
    328         "}\n"
    329         "function fileSystemError() {\n"
    330         "  plugin.postMessage('Failed to open filesystem');\n"
    331         "}\n"
    332         "window.webkitRequestFileSystem(\n"
    333         "    window.Temporary, 1024, gotFileSystem, fileSystemError)\n");
    334     instance_->EvalScript(js_code);
    335     message_received_.Wait();
    336     ASSERT_EQ("SUCCESS", last_message_);
    337   }
    338   handler.Unregister();
    339   ASSERT_SUBTEST_SUCCESS(handler.WaitForDestroy());
    340 
    341   PASS();
    342 }
    343 
    344