js_to_cpp_unittest.cc revision 0de6073388f4e2780db8536178b129cd8f6ab386
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/file_util.h"
6#include "base/files/file_path.h"
7#include "base/message_loop/message_loop.h"
8#include "base/run_loop.h"
9#include "base/strings/utf_string_conversions.h"
10#include "gin/public/isolate_holder.h"
11#include "mojo/apps/js/mojo_runner_delegate.h"
12#include "mojo/apps/js/test/js_to_cpp.mojom.h"
13#include "mojo/common/common_type_converters.h"
14#include "mojo/common/test/test_utils.h"
15#include "mojo/public/cpp/bindings/allocation_scope.h"
16#include "mojo/public/cpp/environment/environment.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
21namespace mojo {
22namespace js {
23namespace {
24
25// Negative numbers with different values in each byte, the last of
26// which can survive promotion to double and back.
27const int8  kExpectedInt8Value = -65;
28const int16 kExpectedInt16Value = -16961;
29const int32 kExpectedInt32Value = -1145258561;
30const int64 kExpectedInt64Value = -77263311946305LL;
31
32// Positive numbers with different values in each byte, the last of
33// which can survive promotion to double and back.
34const uint8  kExpectedUInt8Value = 65;
35const uint16 kExpectedUInt16Value = 16961;
36const uint32 kExpectedUInt32Value = 1145258561;
37const uint64 kExpectedUInt64Value = 77263311946305LL;
38
39// Double/float values, including special case constants.
40const double kExpectedDoubleVal = 3.14159265358979323846;
41const double kExpectedDoubleInf = std::numeric_limits<double>::infinity();
42const double kExpectedDoubleNan = std::numeric_limits<double>::quiet_NaN();
43const float kExpectedFloatVal = static_cast<float>(kExpectedDoubleVal);
44const float kExpectedFloatInf = std::numeric_limits<float>::infinity();
45const float kExpectedFloatNan = std::numeric_limits<float>::quiet_NaN();
46
47// NaN has the property that it is not equal to itself.
48#define EXPECT_NAN(x) EXPECT_NE(x, x)
49
50bool IsRunningOnIsolatedBot() {
51  // TODO(yzshen): Remove this check once isolated tests are supported on the
52  // Chromium waterfall. (http://crbug.com/351214)
53  const base::FilePath test_file_path(
54      test::GetFilePathForJSResource(
55          "mojo/public/interfaces/bindings/tests/sample_interfaces.mojom"));
56  if (!base::PathExists(test_file_path)) {
57    LOG(WARNING) << "Mojom binding files don't exist. Skipping the test.";
58    return true;
59  }
60  return false;
61}
62
63// NOTE: Callers will need to have established an AllocationScope, or you're
64// gonna have a bad time.
65js_to_cpp::EchoArgs BuildSampleEchoArgs() {
66    js_to_cpp::EchoArgs::Builder builder;
67    builder.set_si64(kExpectedInt64Value);
68    builder.set_si32(kExpectedInt32Value);
69    builder.set_si16(kExpectedInt16Value);
70    builder.set_si8(kExpectedInt8Value);
71    builder.set_ui64(kExpectedUInt64Value);
72    builder.set_ui32(kExpectedUInt32Value);
73    builder.set_ui16(kExpectedUInt16Value);
74    builder.set_ui8(kExpectedUInt8Value);
75    builder.set_float_val(kExpectedFloatVal);
76    builder.set_float_inf(kExpectedFloatInf);
77    builder.set_float_nan(kExpectedFloatNan);
78    builder.set_double_val(kExpectedDoubleVal);
79    builder.set_double_inf(kExpectedDoubleInf);
80    builder.set_double_nan(kExpectedDoubleNan);
81    builder.set_name("coming");
82    mojo::Array<mojo::String>::Builder string_array(3);
83    string_array[0] = "one";
84    string_array[1] = "two";
85    string_array[2] = "three";
86    builder.set_string_array(string_array.Finish());
87    return builder.Finish();
88}
89
90void CheckSampleEchoArgs(const js_to_cpp::EchoArgs& arg) {
91    EXPECT_EQ(kExpectedInt64Value, arg.si64());
92    EXPECT_EQ(kExpectedInt32Value, arg.si32());
93    EXPECT_EQ(kExpectedInt16Value, arg.si16());
94    EXPECT_EQ(kExpectedInt8Value, arg.si8());
95    EXPECT_EQ(kExpectedUInt64Value, arg.ui64());
96    EXPECT_EQ(kExpectedUInt32Value, arg.ui32());
97    EXPECT_EQ(kExpectedUInt16Value, arg.ui16());
98    EXPECT_EQ(kExpectedUInt8Value, arg.ui8());
99    EXPECT_EQ(kExpectedFloatVal, arg.float_val());
100    EXPECT_EQ(kExpectedFloatInf, arg.float_inf());
101    EXPECT_NAN(arg.float_nan());
102    EXPECT_EQ(kExpectedDoubleVal, arg.double_val());
103    EXPECT_EQ(kExpectedDoubleInf, arg.double_inf());
104    EXPECT_NAN(arg.double_nan());
105    EXPECT_EQ(std::string("coming"), arg.name().To<std::string>());
106    EXPECT_EQ(std::string("one"), arg.string_array()[0].To<std::string>());
107    EXPECT_EQ(std::string("two"), arg.string_array()[1].To<std::string>());
108    EXPECT_EQ(std::string("three"), arg.string_array()[2].To<std::string>());
109}
110
111// Base Provider implementation class. It's expected that tests subclass and
112// override the appropriate Provider functions. When test is done quit the
113// run_loop().
114class CppSideConnection : public js_to_cpp::CppSide {
115 public:
116  CppSideConnection() : run_loop_(NULL), js_side_(NULL) {
117  }
118  virtual ~CppSideConnection() {}
119
120  void set_run_loop(base::RunLoop* run_loop) { run_loop_ = run_loop; }
121  base::RunLoop* run_loop() { return run_loop_; }
122
123  void set_js_side(js_to_cpp::JsSide* js_side) { js_side_ = js_side; }
124  js_to_cpp::JsSide* js_side() { return js_side_; }
125
126  // js_to_cpp::CppSide:
127  virtual void StartTest() OVERRIDE {
128    NOTREACHED();
129  }
130
131  virtual void TestFinished() OVERRIDE {
132    NOTREACHED();
133  }
134
135  virtual void PingResponse() OVERRIDE {
136    NOTREACHED();
137  }
138
139  virtual void EchoResponse(const js_to_cpp::EchoArgs& arg1,
140                            const js_to_cpp::EchoArgs& arg2) OVERRIDE {
141    NOTREACHED();
142  }
143
144  virtual void BitFlipResponse(const js_to_cpp::EchoArgs& arg1) OVERRIDE {
145    NOTREACHED();
146  }
147
148 protected:
149  base::RunLoop* run_loop_;
150  js_to_cpp::JsSide* js_side_;
151
152 private:
153  Environment environment;
154  DISALLOW_COPY_AND_ASSIGN(CppSideConnection);
155};
156
157// Trivial test to verify a message sent from JS is received.
158class PingCppSideConnection : public CppSideConnection {
159 public:
160  explicit PingCppSideConnection() : got_message_(false) {}
161  virtual ~PingCppSideConnection() {}
162
163  // js_to_cpp::CppSide:
164  virtual void StartTest() OVERRIDE {
165    js_side_->Ping();
166  }
167
168  virtual void PingResponse() OVERRIDE {
169    got_message_ = true;
170    run_loop()->Quit();
171  }
172
173  bool DidSucceed() {
174    return got_message_;
175  }
176
177 private:
178  bool got_message_;
179  DISALLOW_COPY_AND_ASSIGN(PingCppSideConnection);
180};
181
182// Test that parameters are passed with correct values.
183class EchoCppSideConnection : public CppSideConnection {
184 public:
185  explicit EchoCppSideConnection() :
186      message_count_(0),
187      termination_seen_(false) {
188  }
189  virtual ~EchoCppSideConnection() {}
190
191  // js_to_cpp::CppSide:
192  virtual void StartTest() OVERRIDE {
193    AllocationScope scope;
194    js_side_->Echo(kExpectedMessageCount, BuildSampleEchoArgs());
195  }
196
197  virtual void EchoResponse(const js_to_cpp::EchoArgs& arg1,
198                            const js_to_cpp::EchoArgs& arg2) OVERRIDE {
199    message_count_ += 1;
200    CheckSampleEchoArgs(arg1);
201    EXPECT_EQ(-1, arg2.si64());
202    EXPECT_EQ(-1, arg2.si32());
203    EXPECT_EQ(-1, arg2.si16());
204    EXPECT_EQ(-1, arg2.si8());
205    EXPECT_EQ(std::string("going"), arg2.name().To<std::string>());
206  }
207
208  virtual void TestFinished() OVERRIDE {
209    termination_seen_ = true;
210    run_loop()->Quit();
211  }
212
213  bool DidSucceed() {
214    return termination_seen_ && message_count_ == kExpectedMessageCount;
215  }
216
217 private:
218  static const int kExpectedMessageCount = 100;
219  int message_count_;
220  bool termination_seen_;
221  DISALLOW_COPY_AND_ASSIGN(EchoCppSideConnection);
222};
223
224// Test that corrupted messages don't wreak havoc.
225class BitFlipCppSideConnection : public CppSideConnection {
226 public:
227  explicit BitFlipCppSideConnection() : termination_seen_(false) {}
228  virtual ~BitFlipCppSideConnection() {}
229
230  // js_to_cpp::CppSide:
231  virtual void StartTest() OVERRIDE {
232    AllocationScope scope;
233    js_side_->BitFlip(BuildSampleEchoArgs());
234  }
235
236  virtual void BitFlipResponse(const js_to_cpp::EchoArgs& arg1) OVERRIDE {
237    // TODO(tsepez): How to check, may be corrupt in various ways.
238  }
239
240  virtual void TestFinished() OVERRIDE {
241    termination_seen_ = true;
242    run_loop()->Quit();
243  }
244
245  bool DidSucceed() {
246    return termination_seen_;
247  }
248
249 private:
250  bool termination_seen_;
251  DISALLOW_COPY_AND_ASSIGN(BitFlipCppSideConnection);
252};
253
254}  // namespace
255
256class JsToCppTest : public testing::Test {
257 public:
258  JsToCppTest() {}
259
260  void RunTest(const std::string& test, CppSideConnection* cpp_side) {
261    cpp_side->set_run_loop(&run_loop_);
262
263    MessagePipe pipe;
264    js_to_cpp::JsSidePtr js_side =
265        MakeProxy<js_to_cpp::JsSide>(pipe.handle0.Pass());
266    js_side->SetClient(cpp_side);
267
268    js_side.internal_state()->router()->
269        set_enforce_errors_from_incoming_receiver(false);
270
271    cpp_side->set_js_side(js_side.get());
272
273    gin::IsolateHolder instance(gin::IsolateHolder::kStrictMode);
274    apps::MojoRunnerDelegate delegate;
275    gin::ShellRunner runner(&delegate, instance.isolate());
276    delegate.Start(&runner, pipe.handle1.release().value(), test);
277
278    run_loop_.Run();
279  }
280
281 private:
282  base::MessageLoop loop;
283  base::RunLoop run_loop_;
284
285  DISALLOW_COPY_AND_ASSIGN(JsToCppTest);
286};
287
288TEST_F(JsToCppTest, Ping) {
289  if (IsRunningOnIsolatedBot())
290    return;
291
292  PingCppSideConnection cpp_side_connection;
293  RunTest("mojo/apps/js/test/js_to_cpp_unittest", &cpp_side_connection);
294  EXPECT_TRUE(cpp_side_connection.DidSucceed());
295}
296
297TEST_F(JsToCppTest, Echo) {
298  if (IsRunningOnIsolatedBot())
299    return;
300
301  EchoCppSideConnection cpp_side_connection;
302  RunTest("mojo/apps/js/test/js_to_cpp_unittest", &cpp_side_connection);
303  EXPECT_TRUE(cpp_side_connection.DidSucceed());
304}
305
306// TODO(tsepez): Disabled due to http://crbug.com/366797.
307TEST_F(JsToCppTest, DISABLED_BitFlip) {
308  if (IsRunningOnIsolatedBot())
309    return;
310
311  BitFlipCppSideConnection cpp_side_connection;
312  RunTest("mojo/apps/js/test/js_to_cpp_unittest", &cpp_side_connection);
313  EXPECT_TRUE(cpp_side_connection.DidSucceed());
314}
315
316}  // namespace js
317}  // namespace mojo
318