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