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 <stddef.h>
      6 #include <stdint.h>
      7 
      8 #include <string>
      9 #include <utility>
     10 
     11 #include "base/at_exit.h"
     12 #include "base/files/file_path.h"
     13 #include "base/files/file_util.h"
     14 #include "base/macros.h"
     15 #include "base/message_loop/message_loop.h"
     16 #include "base/run_loop.h"
     17 #include "base/strings/utf_string_conversions.h"
     18 #include "base/threading/thread_task_runner_handle.h"
     19 #include "gin/array_buffer.h"
     20 #include "gin/public/isolate_holder.h"
     21 #include "gin/v8_initializer.h"
     22 #include "mojo/common/data_pipe_utils.h"
     23 #include "mojo/edk/js/mojo_runner_delegate.h"
     24 #include "mojo/edk/js/tests/js_to_cpp.mojom.h"
     25 #include "mojo/public/cpp/bindings/binding.h"
     26 #include "mojo/public/cpp/bindings/lib/validation_errors.h"
     27 #include "mojo/public/cpp/system/core.h"
     28 #include "testing/gtest/include/gtest/gtest.h"
     29 
     30 namespace mojo {
     31 namespace edk {
     32 namespace js {
     33 
     34 // Global value updated by some checks to prevent compilers from optimizing
     35 // reads out of existence.
     36 uint32_t g_waste_accumulator = 0;
     37 
     38 namespace {
     39 
     40 // Negative numbers with different values in each byte, the last of
     41 // which can survive promotion to double and back.
     42 const int8_t kExpectedInt8Value = -65;
     43 const int16_t kExpectedInt16Value = -16961;
     44 const int32_t kExpectedInt32Value = -1145258561;
     45 const int64_t kExpectedInt64Value = -77263311946305LL;
     46 
     47 // Positive numbers with different values in each byte, the last of
     48 // which can survive promotion to double and back.
     49 const uint8_t kExpectedUInt8Value = 65;
     50 const uint16_t kExpectedUInt16Value = 16961;
     51 const uint32_t kExpectedUInt32Value = 1145258561;
     52 const uint64_t kExpectedUInt64Value = 77263311946305LL;
     53 
     54 // Double/float values, including special case constants.
     55 const double kExpectedDoubleVal = 3.14159265358979323846;
     56 const double kExpectedDoubleInf = std::numeric_limits<double>::infinity();
     57 const double kExpectedDoubleNan = std::numeric_limits<double>::quiet_NaN();
     58 const float kExpectedFloatVal = static_cast<float>(kExpectedDoubleVal);
     59 const float kExpectedFloatInf = std::numeric_limits<float>::infinity();
     60 const float kExpectedFloatNan = std::numeric_limits<float>::quiet_NaN();
     61 
     62 // NaN has the property that it is not equal to itself.
     63 #define EXPECT_NAN(x) EXPECT_NE(x, x)
     64 
     65 void CheckDataPipe(ScopedDataPipeConsumerHandle data_pipe_handle) {
     66   std::string buffer;
     67   bool result = common::BlockingCopyToString(std::move(data_pipe_handle),
     68                                              &buffer);
     69   EXPECT_TRUE(result);
     70   EXPECT_EQ(64u, buffer.size());
     71   for (int i = 0; i < 64; ++i) {
     72     EXPECT_EQ(i, buffer[i]);
     73   }
     74 }
     75 
     76 void CheckMessagePipe(MessagePipeHandle message_pipe_handle) {
     77   unsigned char buffer[100];
     78   uint32_t buffer_size = static_cast<uint32_t>(sizeof(buffer));
     79   MojoResult result = Wait(
     80       message_pipe_handle, MOJO_HANDLE_SIGNAL_READABLE,
     81       MOJO_DEADLINE_INDEFINITE, nullptr);
     82   EXPECT_EQ(MOJO_RESULT_OK, result);
     83   result = ReadMessageRaw(
     84       message_pipe_handle, buffer, &buffer_size, 0, 0, 0);
     85   EXPECT_EQ(MOJO_RESULT_OK, result);
     86   EXPECT_EQ(64u, buffer_size);
     87   for (int i = 0; i < 64; ++i) {
     88     EXPECT_EQ(255 - i, buffer[i]);
     89   }
     90 }
     91 
     92 js_to_cpp::EchoArgsPtr BuildSampleEchoArgs() {
     93   js_to_cpp::EchoArgsPtr args(js_to_cpp::EchoArgs::New());
     94   args->si64 = kExpectedInt64Value;
     95   args->si32 = kExpectedInt32Value;
     96   args->si16 = kExpectedInt16Value;
     97   args->si8 = kExpectedInt8Value;
     98   args->ui64 = kExpectedUInt64Value;
     99   args->ui32 = kExpectedUInt32Value;
    100   args->ui16 = kExpectedUInt16Value;
    101   args->ui8 = kExpectedUInt8Value;
    102   args->float_val = kExpectedFloatVal;
    103   args->float_inf = kExpectedFloatInf;
    104   args->float_nan = kExpectedFloatNan;
    105   args->double_val = kExpectedDoubleVal;
    106   args->double_inf = kExpectedDoubleInf;
    107   args->double_nan = kExpectedDoubleNan;
    108   args->name.emplace("coming");
    109   args->string_array.emplace(3);
    110   (*args->string_array)[0] = "one";
    111   (*args->string_array)[1] = "two";
    112   (*args->string_array)[2] = "three";
    113   return args;
    114 }
    115 
    116 void CheckSampleEchoArgs(js_to_cpp::EchoArgsPtr arg) {
    117   EXPECT_EQ(kExpectedInt64Value, arg->si64);
    118   EXPECT_EQ(kExpectedInt32Value, arg->si32);
    119   EXPECT_EQ(kExpectedInt16Value, arg->si16);
    120   EXPECT_EQ(kExpectedInt8Value, arg->si8);
    121   EXPECT_EQ(kExpectedUInt64Value, arg->ui64);
    122   EXPECT_EQ(kExpectedUInt32Value, arg->ui32);
    123   EXPECT_EQ(kExpectedUInt16Value, arg->ui16);
    124   EXPECT_EQ(kExpectedUInt8Value, arg->ui8);
    125   EXPECT_EQ(kExpectedFloatVal, arg->float_val);
    126   EXPECT_EQ(kExpectedFloatInf, arg->float_inf);
    127   EXPECT_NAN(arg->float_nan);
    128   EXPECT_EQ(kExpectedDoubleVal, arg->double_val);
    129   EXPECT_EQ(kExpectedDoubleInf, arg->double_inf);
    130   EXPECT_NAN(arg->double_nan);
    131   EXPECT_EQ(std::string("coming"), *arg->name);
    132   EXPECT_EQ(std::string("one"), (*arg->string_array)[0]);
    133   EXPECT_EQ(std::string("two"), (*arg->string_array)[1]);
    134   EXPECT_EQ(std::string("three"), (*arg->string_array)[2]);
    135   CheckDataPipe(std::move(arg->data_handle));
    136   CheckMessagePipe(arg->message_handle.get());
    137 }
    138 
    139 void CheckSampleEchoArgsList(const js_to_cpp::EchoArgsListPtr& list) {
    140   if (list.is_null())
    141     return;
    142   CheckSampleEchoArgs(std::move(list->item));
    143   CheckSampleEchoArgsList(list->next);
    144 }
    145 
    146 // More forgiving checks are needed in the face of potentially corrupt
    147 // messages. The values don't matter so long as all accesses are within
    148 // bounds.
    149 void CheckCorruptedString(const std::string& arg) {
    150   for (size_t i = 0; i < arg.size(); ++i)
    151     g_waste_accumulator += arg[i];
    152 }
    153 
    154 void CheckCorruptedString(const base::Optional<std::string>& arg) {
    155   if (!arg)
    156     return;
    157   CheckCorruptedString(*arg);
    158 }
    159 
    160 void CheckCorruptedStringArray(
    161     const base::Optional<std::vector<std::string>>& string_array) {
    162   if (!string_array)
    163     return;
    164   for (size_t i = 0; i < string_array->size(); ++i)
    165     CheckCorruptedString((*string_array)[i]);
    166 }
    167 
    168 void CheckCorruptedDataPipe(MojoHandle data_pipe_handle) {
    169   unsigned char buffer[100];
    170   uint32_t buffer_size = static_cast<uint32_t>(sizeof(buffer));
    171   MojoResult result = MojoReadData(
    172       data_pipe_handle, buffer, &buffer_size, MOJO_READ_DATA_FLAG_NONE);
    173   if (result != MOJO_RESULT_OK)
    174     return;
    175   for (uint32_t i = 0; i < buffer_size; ++i)
    176     g_waste_accumulator += buffer[i];
    177 }
    178 
    179 void CheckCorruptedMessagePipe(MojoHandle message_pipe_handle) {
    180   unsigned char buffer[100];
    181   uint32_t buffer_size = static_cast<uint32_t>(sizeof(buffer));
    182   MojoResult result = MojoReadMessage(
    183       message_pipe_handle, buffer, &buffer_size, 0, 0, 0);
    184   if (result != MOJO_RESULT_OK)
    185     return;
    186   for (uint32_t i = 0; i < buffer_size; ++i)
    187     g_waste_accumulator += buffer[i];
    188 }
    189 
    190 void CheckCorruptedEchoArgs(const js_to_cpp::EchoArgsPtr& arg) {
    191   if (arg.is_null())
    192     return;
    193   CheckCorruptedString(arg->name);
    194   CheckCorruptedStringArray(arg->string_array);
    195   if (arg->data_handle.is_valid())
    196     CheckCorruptedDataPipe(arg->data_handle.get().value());
    197   if (arg->message_handle.is_valid())
    198     CheckCorruptedMessagePipe(arg->message_handle.get().value());
    199 }
    200 
    201 void CheckCorruptedEchoArgsList(const js_to_cpp::EchoArgsListPtr& list) {
    202   if (list.is_null())
    203     return;
    204   CheckCorruptedEchoArgs(list->item);
    205   CheckCorruptedEchoArgsList(list->next);
    206 }
    207 
    208 // Base Provider implementation class. It's expected that tests subclass and
    209 // override the appropriate Provider functions. When test is done quit the
    210 // run_loop().
    211 class CppSideConnection : public js_to_cpp::CppSide {
    212  public:
    213   CppSideConnection()
    214       : run_loop_(nullptr),
    215         js_side_(nullptr),
    216         mishandled_messages_(0),
    217         binding_(this) {}
    218   ~CppSideConnection() override {}
    219 
    220   void set_run_loop(base::RunLoop* run_loop) { run_loop_ = run_loop; }
    221   base::RunLoop* run_loop() { return run_loop_; }
    222 
    223   void set_js_side(js_to_cpp::JsSide* js_side) { js_side_ = js_side; }
    224   js_to_cpp::JsSide* js_side() { return js_side_; }
    225 
    226   void Bind(InterfaceRequest<js_to_cpp::CppSide> request) {
    227     binding_.Bind(std::move(request));
    228     // Keep the pipe open even after validation errors.
    229     binding_.EnableTestingMode();
    230   }
    231 
    232   // js_to_cpp::CppSide:
    233   void StartTest() override { NOTREACHED(); }
    234 
    235   void TestFinished() override { NOTREACHED(); }
    236 
    237   void PingResponse() override { mishandled_messages_ += 1; }
    238 
    239   void EchoResponse(js_to_cpp::EchoArgsListPtr list) override {
    240     mishandled_messages_ += 1;
    241   }
    242 
    243   void BitFlipResponse(js_to_cpp::EchoArgsListPtr list) override {
    244     mishandled_messages_ += 1;
    245   }
    246 
    247   void BackPointerResponse(js_to_cpp::EchoArgsListPtr list) override {
    248     mishandled_messages_ += 1;
    249   }
    250 
    251  protected:
    252   base::RunLoop* run_loop_;
    253   js_to_cpp::JsSide* js_side_;
    254   int mishandled_messages_;
    255   mojo::Binding<js_to_cpp::CppSide> binding_;
    256 
    257  private:
    258   DISALLOW_COPY_AND_ASSIGN(CppSideConnection);
    259 };
    260 
    261 // Trivial test to verify a message sent from JS is received.
    262 class PingCppSideConnection : public CppSideConnection {
    263  public:
    264   PingCppSideConnection() : got_message_(false) {}
    265   ~PingCppSideConnection() override {}
    266 
    267   // js_to_cpp::CppSide:
    268   void StartTest() override { js_side_->Ping(); }
    269 
    270   void PingResponse() override {
    271     got_message_ = true;
    272     run_loop()->Quit();
    273   }
    274 
    275   bool DidSucceed() {
    276     return got_message_ && !mishandled_messages_;
    277   }
    278 
    279  private:
    280   bool got_message_;
    281   DISALLOW_COPY_AND_ASSIGN(PingCppSideConnection);
    282 };
    283 
    284 // Test that parameters are passed with correct values.
    285 class EchoCppSideConnection : public CppSideConnection {
    286  public:
    287   EchoCppSideConnection() :
    288       message_count_(0),
    289       termination_seen_(false) {
    290   }
    291   ~EchoCppSideConnection() override {}
    292 
    293   // js_to_cpp::CppSide:
    294   void StartTest() override {
    295     js_side_->Echo(kExpectedMessageCount, BuildSampleEchoArgs());
    296   }
    297 
    298   void EchoResponse(js_to_cpp::EchoArgsListPtr list) override {
    299     const js_to_cpp::EchoArgsPtr& special_arg = list->item;
    300     message_count_ += 1;
    301     EXPECT_EQ(-1, special_arg->si64);
    302     EXPECT_EQ(-1, special_arg->si32);
    303     EXPECT_EQ(-1, special_arg->si16);
    304     EXPECT_EQ(-1, special_arg->si8);
    305     EXPECT_EQ(std::string("going"), *special_arg->name);
    306     CheckSampleEchoArgsList(list->next);
    307   }
    308 
    309   void TestFinished() override {
    310     termination_seen_ = true;
    311     run_loop()->Quit();
    312   }
    313 
    314   bool DidSucceed() {
    315     return termination_seen_ &&
    316         !mishandled_messages_ &&
    317         message_count_ == kExpectedMessageCount;
    318   }
    319 
    320  private:
    321   static const int kExpectedMessageCount = 10;
    322   int message_count_;
    323   bool termination_seen_;
    324   DISALLOW_COPY_AND_ASSIGN(EchoCppSideConnection);
    325 };
    326 
    327 // Test that corrupted messages don't wreak havoc.
    328 class BitFlipCppSideConnection : public CppSideConnection {
    329  public:
    330   BitFlipCppSideConnection() : termination_seen_(false) {}
    331   ~BitFlipCppSideConnection() override {}
    332 
    333   // js_to_cpp::CppSide:
    334   void StartTest() override { js_side_->BitFlip(BuildSampleEchoArgs()); }
    335 
    336   void BitFlipResponse(js_to_cpp::EchoArgsListPtr list) override {
    337     CheckCorruptedEchoArgsList(list);
    338   }
    339 
    340   void TestFinished() override {
    341     termination_seen_ = true;
    342     run_loop()->Quit();
    343   }
    344 
    345   bool DidSucceed() {
    346     return termination_seen_;
    347   }
    348 
    349  private:
    350   bool termination_seen_;
    351   DISALLOW_COPY_AND_ASSIGN(BitFlipCppSideConnection);
    352 };
    353 
    354 // Test that severely random messages don't wreak havoc.
    355 class BackPointerCppSideConnection : public CppSideConnection {
    356  public:
    357   BackPointerCppSideConnection() : termination_seen_(false) {}
    358   ~BackPointerCppSideConnection() override {}
    359 
    360   // js_to_cpp::CppSide:
    361   void StartTest() override { js_side_->BackPointer(BuildSampleEchoArgs()); }
    362 
    363   void BackPointerResponse(js_to_cpp::EchoArgsListPtr list) override {
    364     CheckCorruptedEchoArgsList(list);
    365   }
    366 
    367   void TestFinished() override {
    368     termination_seen_ = true;
    369     run_loop()->Quit();
    370   }
    371 
    372   bool DidSucceed() {
    373     return termination_seen_;
    374   }
    375 
    376  private:
    377   bool termination_seen_;
    378   DISALLOW_COPY_AND_ASSIGN(BackPointerCppSideConnection);
    379 };
    380 
    381 }  // namespace
    382 
    383 class JsToCppTest : public testing::Test {
    384  public:
    385   JsToCppTest() {}
    386 
    387   void RunTest(const std::string& test, CppSideConnection* cpp_side) {
    388     cpp_side->set_run_loop(&run_loop_);
    389 
    390     js_to_cpp::JsSidePtr js_side;
    391     auto js_side_proxy = MakeRequest(&js_side);
    392 
    393     cpp_side->set_js_side(js_side.get());
    394     js_to_cpp::CppSidePtr cpp_side_ptr;
    395     cpp_side->Bind(MakeRequest(&cpp_side_ptr));
    396 
    397     js_side->SetCppSide(std::move(cpp_side_ptr));
    398 
    399 #ifdef V8_USE_EXTERNAL_STARTUP_DATA
    400     gin::V8Initializer::LoadV8Snapshot();
    401     gin::V8Initializer::LoadV8Natives();
    402 #endif
    403 
    404     gin::IsolateHolder::Initialize(gin::IsolateHolder::kStrictMode,
    405                                    gin::IsolateHolder::kStableV8Extras,
    406                                    gin::ArrayBufferAllocator::SharedInstance());
    407     gin::IsolateHolder instance(base::ThreadTaskRunnerHandle::Get());
    408     MojoRunnerDelegate delegate;
    409     gin::ShellRunner runner(&delegate, instance.isolate());
    410     delegate.Start(&runner, js_side_proxy.PassMessagePipe().release().value(),
    411                    test);
    412 
    413     run_loop_.Run();
    414   }
    415 
    416  private:
    417   base::ShadowingAtExitManager at_exit_;
    418   base::MessageLoop loop;
    419   base::RunLoop run_loop_;
    420 
    421   DISALLOW_COPY_AND_ASSIGN(JsToCppTest);
    422 };
    423 
    424 TEST_F(JsToCppTest, Ping) {
    425   PingCppSideConnection cpp_side_connection;
    426   RunTest("mojo/edk/js/tests/js_to_cpp_tests", &cpp_side_connection);
    427   EXPECT_TRUE(cpp_side_connection.DidSucceed());
    428 }
    429 
    430 TEST_F(JsToCppTest, Echo) {
    431   EchoCppSideConnection cpp_side_connection;
    432   RunTest("mojo/edk/js/tests/js_to_cpp_tests", &cpp_side_connection);
    433   EXPECT_TRUE(cpp_side_connection.DidSucceed());
    434 }
    435 
    436 TEST_F(JsToCppTest, BitFlip) {
    437   // These tests generate a lot of expected validation errors. Suppress logging.
    438   mojo::internal::ScopedSuppressValidationErrorLoggingForTests log_suppression;
    439 
    440   BitFlipCppSideConnection cpp_side_connection;
    441   RunTest("mojo/edk/js/tests/js_to_cpp_tests", &cpp_side_connection);
    442   EXPECT_TRUE(cpp_side_connection.DidSucceed());
    443 }
    444 
    445 TEST_F(JsToCppTest, BackPointer) {
    446   // These tests generate a lot of expected validation errors. Suppress logging.
    447   mojo::internal::ScopedSuppressValidationErrorLoggingForTests log_suppression;
    448 
    449   BackPointerCppSideConnection cpp_side_connection;
    450   RunTest("mojo/edk/js/tests/js_to_cpp_tests", &cpp_side_connection);
    451   EXPECT_TRUE(cpp_side_connection.DidSucceed());
    452 }
    453 
    454 }  // namespace js
    455 }  // namespace edk
    456 }  // namespace mojo
    457