1// Copyright 2013 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 <stdio.h>
6#include <string.h>
7
8#include <algorithm>
9#include <string>
10
11#include "mojo/public/tests/simple_bindings_support.h"
12#include "mojom/sample_service.h"
13#include "testing/gtest/include/gtest/gtest.h"
14
15namespace mojo {
16
17template <>
18class TypeConverter<sample::Bar, int32_t> {
19 public:
20  static int32_t ConvertTo(const sample::Bar& bar) {
21    return static_cast<int32_t>(bar.alpha()) << 16 |
22           static_cast<int32_t>(bar.beta()) << 8 |
23           static_cast<int32_t>(bar.gamma());
24  }
25};
26
27}  // namespace mojo
28
29namespace sample {
30
31// Set this variable to true to print the binary message in hex.
32bool g_dump_message_as_hex = true;
33
34// Make a sample |Foo|.
35Foo MakeFoo() {
36  mojo::String name("foopy");
37
38  Bar::Builder bar;
39  bar.set_alpha(20);
40  bar.set_beta(40);
41  bar.set_gamma(60);
42  bar.set_type(BAR_VERTICAL);
43
44  mojo::Array<Bar>::Builder extra_bars(3);
45  for (size_t i = 0; i < extra_bars.size(); ++i) {
46    BarType type = i % 2 == 0 ? BAR_VERTICAL : BAR_HORIZONTAL;
47    Bar::Builder bar;
48    uint8_t base = static_cast<uint8_t>(i * 100);
49    bar.set_alpha(base);
50    bar.set_beta(base + 20);
51    bar.set_gamma(base + 40);
52    bar.set_type(type);
53    extra_bars[i] = bar.Finish();
54  }
55
56  mojo::Array<uint8_t>::Builder data(10);
57  for (size_t i = 0; i < data.size(); ++i)
58    data[i] = static_cast<uint8_t>(data.size() - i);
59
60  mojo::ScopedMessagePipeHandle pipe0, pipe1;
61  mojo::CreateMessagePipe(&pipe0, &pipe1);
62
63  Foo::Builder foo;
64  foo.set_name(name);
65  foo.set_x(1);
66  foo.set_y(2);
67  foo.set_a(false);
68  foo.set_b(true);
69  foo.set_c(false);
70  foo.set_bar(bar.Finish());
71  foo.set_extra_bars(extra_bars.Finish());
72  foo.set_data(data.Finish());
73  foo.set_source(pipe1.Pass());
74
75  return foo.Finish();
76}
77
78// Check that the given |Foo| is identical to the one made by |MakeFoo()|.
79void CheckFoo(const Foo& foo) {
80  const std::string kName("foopy");
81  ASSERT_FALSE(foo.name().is_null());
82  EXPECT_EQ(kName.size(), foo.name().size());
83  for (size_t i = 0; i < std::min(kName.size(), foo.name().size()); i++) {
84    // Test both |operator[]| and |at|.
85    EXPECT_EQ(kName[i], foo.name().at(i)) << i;
86    EXPECT_EQ(kName[i], foo.name()[i]) << i;
87  }
88  EXPECT_EQ(kName, foo.name().To<std::string>());
89
90  EXPECT_EQ(1, foo.x());
91  EXPECT_EQ(2, foo.y());
92  EXPECT_FALSE(foo.a());
93  EXPECT_TRUE(foo.b());
94  EXPECT_FALSE(foo.c());
95
96  EXPECT_EQ(20, foo.bar().alpha());
97  EXPECT_EQ(40, foo.bar().beta());
98  EXPECT_EQ(60, foo.bar().gamma());
99  EXPECT_EQ(BAR_VERTICAL, foo.bar().type());
100
101  EXPECT_EQ(3u, foo.extra_bars().size());
102  for (size_t i = 0; i < foo.extra_bars().size(); i++) {
103    uint8_t base = static_cast<uint8_t>(i * 100);
104    BarType type = i % 2 == 0 ? BAR_VERTICAL : BAR_HORIZONTAL;
105    EXPECT_EQ(base, foo.extra_bars()[i].alpha()) << i;
106    EXPECT_EQ(base + 20, foo.extra_bars()[i].beta()) << i;
107    EXPECT_EQ(base + 40, foo.extra_bars()[i].gamma()) << i;
108    EXPECT_EQ(type, foo.extra_bars()[i].type()) << i;
109  }
110
111  EXPECT_EQ(10u, foo.data().size());
112  for (size_t i = 0; i < foo.data().size(); ++i) {
113    EXPECT_EQ(static_cast<uint8_t>(foo.data().size() - i), foo.data()[i]) << i;
114  }
115}
116
117static void PrintSpacer(int depth) {
118  for (int i = 0; i < depth; ++i)
119    printf("   ");
120}
121
122static void Print(int depth, const char* name, bool value) {
123  PrintSpacer(depth);
124  printf("%s: %s\n", name, value ? "true" : "false");
125}
126
127static void Print(int depth, const char* name, int32_t value) {
128  PrintSpacer(depth);
129  printf("%s: %d\n", name, value);
130}
131
132static void Print(int depth, const char* name, uint8_t value) {
133  PrintSpacer(depth);
134  printf("%s: %u\n", name, value);
135}
136
137static void Print(int depth, const char* name, mojo::Handle value) {
138  PrintSpacer(depth);
139  printf("%s: 0x%x\n", name, value.value());
140}
141
142static void Print(int depth, const char* name, const mojo::String& str) {
143  std::string s = str.To<std::string>();
144  PrintSpacer(depth);
145  printf("%s: \"%*s\"\n", name, static_cast<int>(s.size()), s.data());
146}
147
148static void Print(int depth, const char* name, const Bar& bar) {
149  PrintSpacer(depth);
150  printf("%s:\n", name);
151  if (!bar.is_null()) {
152    ++depth;
153    Print(depth, "alpha", bar.alpha());
154    Print(depth, "beta", bar.beta());
155    Print(depth, "gamma", bar.gamma());
156    Print(depth, "packed", bar.To<int32_t>());
157    --depth;
158  }
159}
160
161template <typename T>
162static void Print(int depth, const char* name, const mojo::Array<T>& array) {
163  PrintSpacer(depth);
164  printf("%s:\n", name);
165  if (!array.is_null()) {
166    ++depth;
167    for (size_t i = 0; i < array.size(); ++i) {
168      char buf[32];
169      sprintf(buf, "%lu", static_cast<unsigned long>(i));
170      Print(depth, buf, array.at(i));
171    }
172    --depth;
173  }
174}
175
176static void Print(int depth, const char* name, const Foo& foo) {
177  PrintSpacer(depth);
178  printf("%s:\n", name);
179  if (!foo.is_null()) {
180    ++depth;
181    Print(depth, "name", foo.name());
182    Print(depth, "x", foo.x());
183    Print(depth, "y", foo.y());
184    Print(depth, "a", foo.a());
185    Print(depth, "b", foo.b());
186    Print(depth, "c", foo.c());
187    Print(depth, "bar", foo.bar());
188    Print(depth, "extra_bars", foo.extra_bars());
189    Print(depth, "data", foo.data());
190    --depth;
191  }
192}
193
194static void DumpHex(const uint8_t* bytes, uint32_t num_bytes) {
195  for (uint32_t i = 0; i < num_bytes; ++i) {
196    printf("%02x", bytes[i]);
197
198    if (i % 16 == 15) {
199      printf("\n");
200      continue;
201    }
202
203    if (i % 2 == 1)
204      printf(" ");
205    if (i % 8 == 7)
206      printf(" ");
207  }
208}
209
210class ServiceImpl : public ServiceStub {
211 public:
212  virtual void Frobinate(const Foo& foo, bool baz,
213                         mojo::ScopedMessagePipeHandle port)
214      MOJO_OVERRIDE {
215    // Users code goes here to handle the incoming Frobinate message.
216
217    // We mainly check that we're given the expected arguments.
218    CheckFoo(foo);
219    EXPECT_TRUE(baz);
220
221    // Also dump the Foo structure and all of its members.
222    // TODO(vtl): Make it optional, so that the test spews less?
223    printf("Frobinate:\n");
224    int depth = 1;
225    Print(depth, "foo", foo);
226    Print(depth, "baz", baz);
227    Print(depth, "port", port.get());
228  }
229};
230
231class SimpleMessageReceiver : public mojo::MessageReceiver {
232 public:
233  virtual bool Accept(mojo::Message* message) MOJO_OVERRIDE {
234    // Imagine some IPC happened here.
235
236    if (g_dump_message_as_hex) {
237      DumpHex(reinterpret_cast<const uint8_t*>(message->data),
238              message->data->header.num_bytes);
239    }
240
241    // In the receiving process, an implementation of ServiceStub is known to
242    // the system. It receives the incoming message.
243    ServiceImpl impl;
244
245    ServiceStub* stub = &impl;
246    return stub->Accept(message);
247  }
248};
249
250TEST(BindingsSampleTest, Basic) {
251  mojo::test::SimpleBindingsSupport bindings_support;
252  SimpleMessageReceiver receiver;
253
254  // User has a proxy to a Service somehow.
255  Service* service = new ServiceProxy(&receiver);
256
257  // User constructs a message to send.
258
259  // Notice that it doesn't matter in what order the structs / arrays are
260  // allocated. Here, the various members of Foo are allocated before Foo is
261  // allocated.
262
263  mojo::AllocationScope scope;
264
265  Foo foo = MakeFoo();
266  CheckFoo(foo);
267
268  mojo::ScopedMessagePipeHandle port0, port1;
269  mojo::CreateMessagePipe(&port0, &port1);
270
271  service->Frobinate(foo, true, port0.Pass());
272}
273
274}  // namespace sample
275