1// Copyright 2015 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/bind.h"
6#include "base/callback.h"
7#include "base/logging.h"
8#include "base/message_loop/message_loop.h"
9#include "base/run_loop.h"
10#include "mojo/public/cpp/bindings/binding_set.h"
11#include "mojo/public/cpp/bindings/interface_request.h"
12#include "mojo/public/cpp/bindings/tests/rect_blink.h"
13#include "mojo/public/cpp/bindings/tests/rect_chromium.h"
14#include "mojo/public/cpp/bindings/tests/struct_with_traits_impl.h"
15#include "mojo/public/cpp/bindings/tests/struct_with_traits_impl_traits.h"
16#include "mojo/public/cpp/bindings/tests/variant_test_util.h"
17#include "mojo/public/interfaces/bindings/tests/struct_with_traits.mojom.h"
18#include "mojo/public/interfaces/bindings/tests/test_native_types.mojom-blink.h"
19#include "mojo/public/interfaces/bindings/tests/test_native_types.mojom.h"
20#include "testing/gtest/include/gtest/gtest.h"
21
22namespace mojo {
23namespace test {
24namespace {
25
26template <typename T>
27void DoExpectResult(const T& expected,
28                    const base::Closure& callback,
29                    const T& actual) {
30  EXPECT_EQ(expected.x(), actual.x());
31  EXPECT_EQ(expected.y(), actual.y());
32  EXPECT_EQ(expected.width(), actual.width());
33  EXPECT_EQ(expected.height(), actual.height());
34  callback.Run();
35}
36
37template <typename T>
38base::Callback<void(const T&)> ExpectResult(const T& r,
39                                            const base::Closure& callback) {
40  return base::Bind(&DoExpectResult<T>, r, callback);
41}
42
43template <typename T>
44void DoFail(const std::string& reason, const T&) {
45  EXPECT_TRUE(false) << reason;
46}
47
48template <typename T>
49base::Callback<void(const T&)> Fail(const std::string& reason) {
50  return base::Bind(&DoFail<T>, reason);
51}
52
53template <typename T>
54void ExpectError(InterfacePtr<T> *proxy, const base::Closure& callback) {
55  proxy->set_connection_error_handler(callback);
56}
57
58// This implements the generated Chromium variant of RectService.
59class ChromiumRectServiceImpl : public RectService {
60 public:
61  ChromiumRectServiceImpl() {}
62
63  // mojo::test::RectService:
64  void AddRect(const RectChromium& r) override {
65    if (r.GetArea() > largest_rect_.GetArea())
66      largest_rect_ = r;
67  }
68
69  void GetLargestRect(const GetLargestRectCallback& callback) override {
70    callback.Run(largest_rect_);
71  }
72
73 private:
74  RectChromium largest_rect_;
75};
76
77// This implements the generated Blink variant of RectService.
78class BlinkRectServiceImpl : public blink::RectService {
79 public:
80  BlinkRectServiceImpl() {}
81
82  // mojo::test::blink::RectService:
83  void AddRect(const RectBlink& r) override {
84    if (r.computeArea() > largest_rect_.computeArea()) {
85      largest_rect_.setX(r.x());
86      largest_rect_.setY(r.y());
87      largest_rect_.setWidth(r.width());
88      largest_rect_.setHeight(r.height());
89    }
90  }
91
92  void GetLargestRect(const GetLargestRectCallback& callback) override {
93    callback.Run(largest_rect_);
94  }
95
96 private:
97  RectBlink largest_rect_;
98};
99
100// A test which runs both Chromium and Blink implementations of a RectService.
101class StructTraitsTest : public testing::Test,
102                         public TraitsTestService {
103 public:
104  StructTraitsTest() {}
105
106 protected:
107  void BindToChromiumService(RectServiceRequest request) {
108    chromium_bindings_.AddBinding(&chromium_service_, std::move(request));
109  }
110  void BindToChromiumService(blink::RectServiceRequest request) {
111    chromium_bindings_.AddBinding(
112        &chromium_service_,
113        ConvertInterfaceRequest<RectService>(std::move(request)));
114  }
115
116  void BindToBlinkService(blink::RectServiceRequest request) {
117    blink_bindings_.AddBinding(&blink_service_, std::move(request));
118  }
119  void BindToBlinkService(RectServiceRequest request) {
120    blink_bindings_.AddBinding(
121        &blink_service_,
122        ConvertInterfaceRequest<blink::RectService>(std::move(request)));
123  }
124
125  TraitsTestServicePtr GetTraitsTestProxy() {
126    return traits_test_bindings_.CreateInterfacePtrAndBind(this);
127  }
128
129 private:
130  // TraitsTestService:
131  void EchoStructWithTraits(
132      const StructWithTraitsImpl& s,
133      const EchoStructWithTraitsCallback& callback) override {
134    callback.Run(s);
135  }
136
137  void EchoPassByValueStructWithTraits(
138      PassByValueStructWithTraitsImpl s,
139      const EchoPassByValueStructWithTraitsCallback& callback) override {
140    callback.Run(std::move(s));
141  }
142
143  void EchoEnumWithTraits(EnumWithTraitsImpl e,
144                          const EchoEnumWithTraitsCallback& callback) override {
145    callback.Run(e);
146  }
147
148  void EchoStructWithTraitsForUniquePtrTest(
149      std::unique_ptr<int> e,
150      const EchoStructWithTraitsForUniquePtrTestCallback& callback) override {
151    callback.Run(std::move(e));
152  }
153
154  base::MessageLoop loop_;
155
156  ChromiumRectServiceImpl chromium_service_;
157  BindingSet<RectService> chromium_bindings_;
158
159  BlinkRectServiceImpl blink_service_;
160  BindingSet<blink::RectService> blink_bindings_;
161
162  BindingSet<TraitsTestService> traits_test_bindings_;
163};
164
165}  // namespace
166
167TEST_F(StructTraitsTest, ChromiumProxyToChromiumService) {
168  RectServicePtr chromium_proxy;
169  BindToChromiumService(GetProxy(&chromium_proxy));
170  {
171    base::RunLoop loop;
172    chromium_proxy->AddRect(RectChromium(1, 1, 4, 5));
173    chromium_proxy->AddRect(RectChromium(-1, -1, 2, 2));
174    chromium_proxy->GetLargestRect(
175        ExpectResult(RectChromium(1, 1, 4, 5), loop.QuitClosure()));
176    loop.Run();
177  }
178}
179
180TEST_F(StructTraitsTest, ChromiumToBlinkService) {
181  RectServicePtr chromium_proxy;
182  BindToBlinkService(GetProxy(&chromium_proxy));
183  {
184    base::RunLoop loop;
185    chromium_proxy->AddRect(RectChromium(1, 1, 4, 5));
186    chromium_proxy->AddRect(RectChromium(2, 2, 5, 5));
187    chromium_proxy->GetLargestRect(
188        ExpectResult(RectChromium(2, 2, 5, 5), loop.QuitClosure()));
189    loop.Run();
190  }
191  // The Blink service should drop our connection because RectBlink's
192  // deserializer rejects negative origins.
193  {
194    base::RunLoop loop;
195    ExpectError(&chromium_proxy, loop.QuitClosure());
196    chromium_proxy->AddRect(RectChromium(-1, -1, 2, 2));
197    chromium_proxy->GetLargestRect(
198        Fail<RectChromium>("The pipe should have been closed."));
199    loop.Run();
200  }
201}
202
203TEST_F(StructTraitsTest, BlinkProxyToBlinkService) {
204  blink::RectServicePtr blink_proxy;
205  BindToBlinkService(GetProxy(&blink_proxy));
206  {
207    base::RunLoop loop;
208    blink_proxy->AddRect(RectBlink(1, 1, 4, 5));
209    blink_proxy->AddRect(RectBlink(10, 10, 20, 20));
210    blink_proxy->GetLargestRect(
211        ExpectResult(RectBlink(10, 10, 20, 20), loop.QuitClosure()));
212    loop.Run();
213  }
214}
215
216TEST_F(StructTraitsTest, BlinkProxyToChromiumService) {
217  blink::RectServicePtr blink_proxy;
218  BindToChromiumService(GetProxy(&blink_proxy));
219  {
220    base::RunLoop loop;
221    blink_proxy->AddRect(RectBlink(1, 1, 4, 5));
222    blink_proxy->AddRect(RectBlink(10, 10, 2, 2));
223    blink_proxy->GetLargestRect(
224        ExpectResult(RectBlink(1, 1, 4, 5), loop.QuitClosure()));
225    loop.Run();
226  }
227}
228
229void ExpectStructWithTraits(const StructWithTraitsImpl& expected,
230                            const base::Closure& closure,
231                            const StructWithTraitsImpl& passed) {
232  EXPECT_EQ(expected.get_enum(), passed.get_enum());
233  EXPECT_EQ(expected.get_bool(), passed.get_bool());
234  EXPECT_EQ(expected.get_uint32(), passed.get_uint32());
235  EXPECT_EQ(expected.get_uint64(), passed.get_uint64());
236  EXPECT_EQ(expected.get_string(), passed.get_string());
237  EXPECT_EQ(expected.get_string_array(), passed.get_string_array());
238  EXPECT_EQ(expected.get_struct(), passed.get_struct());
239  EXPECT_EQ(expected.get_struct_array(), passed.get_struct_array());
240  EXPECT_EQ(expected.get_struct_map(), passed.get_struct_map());
241  closure.Run();
242}
243
244TEST_F(StructTraitsTest, EchoStructWithTraits) {
245  StructWithTraitsImpl input;
246  input.set_enum(EnumWithTraitsImpl::CUSTOM_VALUE_1);
247  input.set_bool(true);
248  input.set_uint32(7);
249  input.set_uint64(42);
250  input.set_string("hello world!");
251  input.get_mutable_string_array().assign({"hello", "world!"});
252  input.get_mutable_struct().value = 42;
253  input.get_mutable_struct_array().resize(2);
254  input.get_mutable_struct_array()[0].value = 1;
255  input.get_mutable_struct_array()[1].value = 2;
256  input.get_mutable_struct_map()["hello"] = NestedStructWithTraitsImpl(1024);
257  input.get_mutable_struct_map()["world"] = NestedStructWithTraitsImpl(2048);
258
259  base::RunLoop loop;
260  TraitsTestServicePtr proxy = GetTraitsTestProxy();
261
262  proxy->EchoStructWithTraits(
263      input,
264      base::Bind(&ExpectStructWithTraits, input, loop.QuitClosure()));
265  loop.Run();
266}
267
268TEST_F(StructTraitsTest, CloneStructWithTraitsContainer) {
269  StructWithTraitsContainerPtr container = StructWithTraitsContainer::New();
270  container->f_struct.set_uint32(7);
271  container->f_struct.set_uint64(42);
272  StructWithTraitsContainerPtr cloned_container = container.Clone();
273  EXPECT_EQ(7u, cloned_container->f_struct.get_uint32());
274  EXPECT_EQ(42u, cloned_container->f_struct.get_uint64());
275}
276
277void CaptureMessagePipe(ScopedMessagePipeHandle* storage,
278                        const base::Closure& closure,
279                        PassByValueStructWithTraitsImpl passed) {
280  storage->reset(MessagePipeHandle(
281      passed.get_mutable_handle().release().value()));
282  closure.Run();
283}
284
285TEST_F(StructTraitsTest, EchoPassByValueStructWithTraits) {
286  MessagePipe mp;
287  PassByValueStructWithTraitsImpl input;
288  input.get_mutable_handle().reset(mp.handle0.release());
289
290  base::RunLoop loop;
291  TraitsTestServicePtr proxy = GetTraitsTestProxy();
292
293  ScopedMessagePipeHandle received;
294  proxy->EchoPassByValueStructWithTraits(
295      std::move(input),
296      base::Bind(&CaptureMessagePipe, &received, loop.QuitClosure()));
297  loop.Run();
298
299  ASSERT_TRUE(received.is_valid());
300
301  // Verify that the message pipe handle is correctly passed.
302  const char kHello[] = "hello";
303  const uint32_t kHelloSize = static_cast<uint32_t>(sizeof(kHello));
304  EXPECT_EQ(MOJO_RESULT_OK,
305            WriteMessageRaw(mp.handle1.get(), kHello, kHelloSize, nullptr, 0,
306                            MOJO_WRITE_MESSAGE_FLAG_NONE));
307
308  EXPECT_EQ(MOJO_RESULT_OK, Wait(received.get(), MOJO_HANDLE_SIGNAL_READABLE,
309                                 MOJO_DEADLINE_INDEFINITE, nullptr));
310
311  char buffer[10] = {0};
312  uint32_t buffer_size = static_cast<uint32_t>(sizeof(buffer));
313  EXPECT_EQ(MOJO_RESULT_OK,
314            ReadMessageRaw(received.get(), buffer, &buffer_size, nullptr,
315                           nullptr, MOJO_READ_MESSAGE_FLAG_NONE));
316  EXPECT_EQ(kHelloSize, buffer_size);
317  EXPECT_STREQ(kHello, buffer);
318}
319
320void ExpectEnumWithTraits(EnumWithTraitsImpl expected_value,
321                          const base::Closure& closure,
322                          EnumWithTraitsImpl value) {
323  EXPECT_EQ(expected_value, value);
324  closure.Run();
325}
326
327TEST_F(StructTraitsTest, EchoEnumWithTraits) {
328  base::RunLoop loop;
329  TraitsTestServicePtr proxy = GetTraitsTestProxy();
330
331  proxy->EchoEnumWithTraits(
332      EnumWithTraitsImpl::CUSTOM_VALUE_1,
333      base::Bind(&ExpectEnumWithTraits, EnumWithTraitsImpl::CUSTOM_VALUE_1,
334                 loop.QuitClosure()));
335  loop.Run();
336}
337
338TEST_F(StructTraitsTest, SerializeStructWithTraits) {
339  StructWithTraitsImpl input;
340  input.set_enum(EnumWithTraitsImpl::CUSTOM_VALUE_1);
341  input.set_bool(true);
342  input.set_uint32(7);
343  input.set_uint64(42);
344  input.set_string("hello world!");
345  input.get_mutable_string_array().assign({"hello", "world!"});
346  input.get_mutable_struct().value = 42;
347  input.get_mutable_struct_array().resize(2);
348  input.get_mutable_struct_array()[0].value = 1;
349  input.get_mutable_struct_array()[1].value = 2;
350  input.get_mutable_struct_map()["hello"] = NestedStructWithTraitsImpl(1024);
351  input.get_mutable_struct_map()["world"] = NestedStructWithTraitsImpl(2048);
352
353  mojo::Array<uint8_t> data = StructWithTraits::Serialize(&input);
354  StructWithTraitsImpl output;
355  ASSERT_TRUE(StructWithTraits::Deserialize(std::move(data), &output));
356
357  EXPECT_EQ(input.get_enum(), output.get_enum());
358  EXPECT_EQ(input.get_bool(), output.get_bool());
359  EXPECT_EQ(input.get_uint32(), output.get_uint32());
360  EXPECT_EQ(input.get_uint64(), output.get_uint64());
361  EXPECT_EQ(input.get_string(), output.get_string());
362  EXPECT_EQ(input.get_string_array(), output.get_string_array());
363  EXPECT_EQ(input.get_struct(), output.get_struct());
364  EXPECT_EQ(input.get_struct_array(), output.get_struct_array());
365  EXPECT_EQ(input.get_struct_map(), output.get_struct_map());
366}
367
368void ExpectUniquePtr(int expected,
369                     const base::Closure& closure,
370                     std::unique_ptr<int> value) {
371  EXPECT_EQ(expected, *value);
372  closure.Run();
373}
374
375TEST_F(StructTraitsTest, TypemapUniquePtr) {
376  base::RunLoop loop;
377  TraitsTestServicePtr proxy = GetTraitsTestProxy();
378
379  proxy->EchoStructWithTraitsForUniquePtrTest(
380      base::MakeUnique<int>(12345),
381      base::Bind(&ExpectUniquePtr, 12345, loop.QuitClosure()));
382  loop.Run();
383}
384
385}  // namespace test
386}  // namespace mojo
387