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