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// This file tests the C++ Mojo system core wrappers. 6// TODO(vtl): Maybe rename "CoreCppTest" -> "CoreTest" if/when this gets 7// compiled into a different binary from the C API tests. 8 9#include "mojo/public/cpp/system/core.h" 10 11#include <stddef.h> 12#include <stdint.h> 13#include <map> 14#include <utility> 15 16#include "testing/gtest/include/gtest/gtest.h" 17 18namespace mojo { 19namespace { 20 21const MojoHandleSignals kSignalReadableWritable = 22 MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_WRITABLE; 23 24const MojoHandleSignals kSignalAll = MOJO_HANDLE_SIGNAL_READABLE | 25 MOJO_HANDLE_SIGNAL_WRITABLE | 26 MOJO_HANDLE_SIGNAL_PEER_CLOSED; 27 28TEST(CoreCppTest, GetTimeTicksNow) { 29 const MojoTimeTicks start = GetTimeTicksNow(); 30 EXPECT_NE(static_cast<MojoTimeTicks>(0), start) 31 << "GetTimeTicksNow should return nonzero value"; 32} 33 34TEST(CoreCppTest, Basic) { 35 // Basic |Handle| implementation: 36 { 37 EXPECT_EQ(MOJO_HANDLE_INVALID, kInvalidHandleValue); 38 39 Handle h0; 40 EXPECT_EQ(kInvalidHandleValue, h0.value()); 41 EXPECT_EQ(kInvalidHandleValue, *h0.mutable_value()); 42 EXPECT_FALSE(h0.is_valid()); 43 44 Handle h1(static_cast<MojoHandle>(123)); 45 EXPECT_EQ(static_cast<MojoHandle>(123), h1.value()); 46 EXPECT_EQ(static_cast<MojoHandle>(123), *h1.mutable_value()); 47 EXPECT_TRUE(h1.is_valid()); 48 *h1.mutable_value() = static_cast<MojoHandle>(456); 49 EXPECT_EQ(static_cast<MojoHandle>(456), h1.value()); 50 EXPECT_TRUE(h1.is_valid()); 51 52 h1.swap(h0); 53 EXPECT_EQ(static_cast<MojoHandle>(456), h0.value()); 54 EXPECT_TRUE(h0.is_valid()); 55 EXPECT_FALSE(h1.is_valid()); 56 57 h1.set_value(static_cast<MojoHandle>(789)); 58 h0.swap(h1); 59 EXPECT_EQ(static_cast<MojoHandle>(789), h0.value()); 60 EXPECT_TRUE(h0.is_valid()); 61 EXPECT_EQ(static_cast<MojoHandle>(456), h1.value()); 62 EXPECT_TRUE(h1.is_valid()); 63 64 // Make sure copy constructor works. 65 Handle h2(h0); 66 EXPECT_EQ(static_cast<MojoHandle>(789), h2.value()); 67 // And assignment. 68 h2 = h1; 69 EXPECT_EQ(static_cast<MojoHandle>(456), h2.value()); 70 71 // Make sure that we can put |Handle|s into |std::map|s. 72 h0 = Handle(static_cast<MojoHandle>(987)); 73 h1 = Handle(static_cast<MojoHandle>(654)); 74 h2 = Handle(static_cast<MojoHandle>(321)); 75 Handle h3; 76 std::map<Handle, int> handle_to_int; 77 handle_to_int[h0] = 0; 78 handle_to_int[h1] = 1; 79 handle_to_int[h2] = 2; 80 handle_to_int[h3] = 3; 81 82 EXPECT_EQ(4u, handle_to_int.size()); 83 EXPECT_FALSE(handle_to_int.find(h0) == handle_to_int.end()); 84 EXPECT_EQ(0, handle_to_int[h0]); 85 EXPECT_FALSE(handle_to_int.find(h1) == handle_to_int.end()); 86 EXPECT_EQ(1, handle_to_int[h1]); 87 EXPECT_FALSE(handle_to_int.find(h2) == handle_to_int.end()); 88 EXPECT_EQ(2, handle_to_int[h2]); 89 EXPECT_FALSE(handle_to_int.find(h3) == handle_to_int.end()); 90 EXPECT_EQ(3, handle_to_int[h3]); 91 EXPECT_TRUE(handle_to_int.find(Handle(static_cast<MojoHandle>(13579))) == 92 handle_to_int.end()); 93 94 // TODO(vtl): With C++11, support |std::unordered_map|s, etc. (Or figure out 95 // how to support the variations of |hash_map|.) 96 } 97 98 // |Handle|/|ScopedHandle| functions: 99 { 100 ScopedHandle h; 101 102 EXPECT_EQ(kInvalidHandleValue, h.get().value()); 103 104 // This should be a no-op. 105 Close(std::move(h)); 106 107 // It should still be invalid. 108 EXPECT_EQ(kInvalidHandleValue, h.get().value()); 109 110 EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT, 111 Wait(h.get(), ~MOJO_HANDLE_SIGNAL_NONE, 1000000, nullptr)); 112 113 std::vector<Handle> wh; 114 wh.push_back(h.get()); 115 std::vector<MojoHandleSignals> sigs; 116 sigs.push_back(~MOJO_HANDLE_SIGNAL_NONE); 117 WaitManyResult wait_many_result = 118 WaitMany(wh, sigs, MOJO_DEADLINE_INDEFINITE, nullptr); 119 EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT, wait_many_result.result); 120 EXPECT_TRUE(wait_many_result.IsIndexValid()); 121 EXPECT_FALSE(wait_many_result.AreSignalsStatesValid()); 122 123 // Make sure that our specialized template correctly handles |NULL| as well 124 // as |nullptr|. 125 wait_many_result = WaitMany(wh, sigs, MOJO_DEADLINE_INDEFINITE, NULL); 126 EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT, wait_many_result.result); 127 EXPECT_EQ(0u, wait_many_result.index); 128 EXPECT_TRUE(wait_many_result.IsIndexValid()); 129 EXPECT_FALSE(wait_many_result.AreSignalsStatesValid()); 130 } 131 132 // |MakeScopedHandle| (just compilation tests): 133 { 134 EXPECT_FALSE(MakeScopedHandle(Handle()).is_valid()); 135 EXPECT_FALSE(MakeScopedHandle(MessagePipeHandle()).is_valid()); 136 EXPECT_FALSE(MakeScopedHandle(DataPipeProducerHandle()).is_valid()); 137 EXPECT_FALSE(MakeScopedHandle(DataPipeConsumerHandle()).is_valid()); 138 EXPECT_FALSE(MakeScopedHandle(SharedBufferHandle()).is_valid()); 139 } 140 141 // |MessagePipeHandle|/|ScopedMessagePipeHandle| functions: 142 { 143 MessagePipeHandle h_invalid; 144 EXPECT_FALSE(h_invalid.is_valid()); 145 EXPECT_EQ( 146 MOJO_RESULT_INVALID_ARGUMENT, 147 WriteMessageRaw( 148 h_invalid, nullptr, 0, nullptr, 0, MOJO_WRITE_MESSAGE_FLAG_NONE)); 149 char buffer[10] = {0}; 150 EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT, 151 WriteMessageRaw(h_invalid, 152 buffer, 153 sizeof(buffer), 154 nullptr, 155 0, 156 MOJO_WRITE_MESSAGE_FLAG_NONE)); 157 EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT, 158 ReadMessageRaw(h_invalid, 159 nullptr, 160 nullptr, 161 nullptr, 162 nullptr, 163 MOJO_READ_MESSAGE_FLAG_NONE)); 164 uint32_t buffer_size = static_cast<uint32_t>(sizeof(buffer)); 165 EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT, 166 ReadMessageRaw(h_invalid, 167 buffer, 168 &buffer_size, 169 nullptr, 170 nullptr, 171 MOJO_READ_MESSAGE_FLAG_NONE)); 172 173 // Basic tests of waiting and closing. 174 MojoHandle hv0 = kInvalidHandleValue; 175 { 176 ScopedMessagePipeHandle h0; 177 ScopedMessagePipeHandle h1; 178 EXPECT_FALSE(h0.get().is_valid()); 179 EXPECT_FALSE(h1.get().is_valid()); 180 181 CreateMessagePipe(nullptr, &h0, &h1); 182 EXPECT_TRUE(h0.get().is_valid()); 183 EXPECT_TRUE(h1.get().is_valid()); 184 EXPECT_NE(h0.get().value(), h1.get().value()); 185 // Save the handle values, so we can check that things got closed 186 // correctly. 187 hv0 = h0.get().value(); 188 MojoHandle hv1 = h1.get().value(); 189 MojoHandleSignalsState state; 190 191 EXPECT_EQ(MOJO_RESULT_DEADLINE_EXCEEDED, 192 Wait(h0.get(), MOJO_HANDLE_SIGNAL_READABLE, 0, &state)); 193 194 EXPECT_EQ(MOJO_HANDLE_SIGNAL_WRITABLE, state.satisfied_signals); 195 EXPECT_EQ(kSignalAll, state.satisfiable_signals); 196 197 std::vector<Handle> wh; 198 wh.push_back(h0.get()); 199 wh.push_back(h1.get()); 200 std::vector<MojoHandleSignals> sigs; 201 sigs.push_back(MOJO_HANDLE_SIGNAL_READABLE); 202 sigs.push_back(MOJO_HANDLE_SIGNAL_WRITABLE); 203 std::vector<MojoHandleSignalsState> states(sigs.size()); 204 WaitManyResult wait_many_result = WaitMany(wh, sigs, 1000, &states); 205 EXPECT_EQ(MOJO_RESULT_OK, wait_many_result.result); 206 EXPECT_EQ(1u, wait_many_result.index); 207 EXPECT_TRUE(wait_many_result.IsIndexValid()); 208 EXPECT_TRUE(wait_many_result.AreSignalsStatesValid()); 209 EXPECT_EQ(MOJO_HANDLE_SIGNAL_WRITABLE, states[0].satisfied_signals); 210 EXPECT_EQ(kSignalAll, states[0].satisfiable_signals); 211 EXPECT_EQ(MOJO_HANDLE_SIGNAL_WRITABLE, states[1].satisfied_signals); 212 EXPECT_EQ(kSignalAll, states[1].satisfiable_signals); 213 214 // Test closing |h1| explicitly. 215 Close(std::move(h1)); 216 EXPECT_FALSE(h1.get().is_valid()); 217 218 // Make sure |h1| is closed. 219 EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT, 220 Wait(Handle(hv1), ~MOJO_HANDLE_SIGNAL_NONE, 221 MOJO_DEADLINE_INDEFINITE, nullptr)); 222 223 EXPECT_EQ(MOJO_RESULT_FAILED_PRECONDITION, 224 Wait(h0.get(), MOJO_HANDLE_SIGNAL_READABLE, 225 MOJO_DEADLINE_INDEFINITE, &state)); 226 227 EXPECT_EQ(MOJO_HANDLE_SIGNAL_PEER_CLOSED, state.satisfied_signals); 228 EXPECT_EQ(MOJO_HANDLE_SIGNAL_PEER_CLOSED, state.satisfiable_signals); 229 } 230 // |hv0| should have been closed when |h0| went out of scope, so this close 231 // should fail. 232 EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT, MojoClose(hv0)); 233 234 // Actually test writing/reading messages. 235 { 236 ScopedMessagePipeHandle h0; 237 ScopedMessagePipeHandle h1; 238 CreateMessagePipe(nullptr, &h0, &h1); 239 240 const char kHello[] = "hello"; 241 const uint32_t kHelloSize = static_cast<uint32_t>(sizeof(kHello)); 242 EXPECT_EQ(MOJO_RESULT_OK, 243 WriteMessageRaw(h0.get(), 244 kHello, 245 kHelloSize, 246 nullptr, 247 0, 248 MOJO_WRITE_MESSAGE_FLAG_NONE)); 249 250 MojoHandleSignalsState state; 251 EXPECT_EQ(MOJO_RESULT_OK, Wait(h1.get(), MOJO_HANDLE_SIGNAL_READABLE, 252 MOJO_DEADLINE_INDEFINITE, &state)); 253 EXPECT_EQ(kSignalReadableWritable, state.satisfied_signals); 254 EXPECT_EQ(kSignalAll, state.satisfiable_signals); 255 256 char buffer[10] = {0}; 257 uint32_t buffer_size = static_cast<uint32_t>(sizeof(buffer)); 258 EXPECT_EQ(MOJO_RESULT_OK, 259 ReadMessageRaw(h1.get(), 260 buffer, 261 &buffer_size, 262 nullptr, 263 nullptr, 264 MOJO_READ_MESSAGE_FLAG_NONE)); 265 EXPECT_EQ(kHelloSize, buffer_size); 266 EXPECT_STREQ(kHello, buffer); 267 268 // Send a handle over the previously-establish message pipe. Use the 269 // |MessagePipe| wrapper (to test it), which automatically creates a 270 // message pipe. 271 MessagePipe mp; 272 273 // Write a message to |mp.handle0|, before we send |mp.handle1|. 274 const char kWorld[] = "world!"; 275 const uint32_t kWorldSize = static_cast<uint32_t>(sizeof(kWorld)); 276 EXPECT_EQ(MOJO_RESULT_OK, 277 WriteMessageRaw(mp.handle0.get(), 278 kWorld, 279 kWorldSize, 280 nullptr, 281 0, 282 MOJO_WRITE_MESSAGE_FLAG_NONE)); 283 284 // Send |mp.handle1| over |h1| to |h0|. 285 MojoHandle handles[5]; 286 handles[0] = mp.handle1.release().value(); 287 EXPECT_NE(kInvalidHandleValue, handles[0]); 288 EXPECT_FALSE(mp.handle1.get().is_valid()); 289 uint32_t handles_count = 1; 290 EXPECT_EQ(MOJO_RESULT_OK, 291 WriteMessageRaw(h1.get(), 292 kHello, 293 kHelloSize, 294 handles, 295 handles_count, 296 MOJO_WRITE_MESSAGE_FLAG_NONE)); 297 // |handles[0]| should actually be invalid now. 298 EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT, MojoClose(handles[0])); 299 300 // Read "hello" and the sent handle. 301 EXPECT_EQ(MOJO_RESULT_OK, Wait(h0.get(), MOJO_HANDLE_SIGNAL_READABLE, 302 MOJO_DEADLINE_INDEFINITE, &state)); 303 EXPECT_EQ(kSignalReadableWritable, state.satisfied_signals); 304 EXPECT_EQ(kSignalAll, state.satisfiable_signals); 305 306 memset(buffer, 0, sizeof(buffer)); 307 buffer_size = static_cast<uint32_t>(sizeof(buffer)); 308 for (size_t i = 0; i < arraysize(handles); i++) 309 handles[i] = kInvalidHandleValue; 310 handles_count = static_cast<uint32_t>(arraysize(handles)); 311 EXPECT_EQ(MOJO_RESULT_OK, 312 ReadMessageRaw(h0.get(), 313 buffer, 314 &buffer_size, 315 handles, 316 &handles_count, 317 MOJO_READ_MESSAGE_FLAG_NONE)); 318 EXPECT_EQ(kHelloSize, buffer_size); 319 EXPECT_STREQ(kHello, buffer); 320 EXPECT_EQ(1u, handles_count); 321 EXPECT_NE(kInvalidHandleValue, handles[0]); 322 323 // Read from the sent/received handle. 324 mp.handle1.reset(MessagePipeHandle(handles[0])); 325 // Save |handles[0]| to check that it gets properly closed. 326 hv0 = handles[0]; 327 328 EXPECT_EQ(MOJO_RESULT_OK, 329 Wait(mp.handle1.get(), MOJO_HANDLE_SIGNAL_READABLE, 330 MOJO_DEADLINE_INDEFINITE, &state)); 331 EXPECT_EQ(kSignalReadableWritable, state.satisfied_signals); 332 EXPECT_EQ(kSignalAll, state.satisfiable_signals); 333 334 memset(buffer, 0, sizeof(buffer)); 335 buffer_size = static_cast<uint32_t>(sizeof(buffer)); 336 for (size_t i = 0; i < arraysize(handles); i++) 337 handles[i] = kInvalidHandleValue; 338 handles_count = static_cast<uint32_t>(arraysize(handles)); 339 EXPECT_EQ(MOJO_RESULT_OK, 340 ReadMessageRaw(mp.handle1.get(), 341 buffer, 342 &buffer_size, 343 handles, 344 &handles_count, 345 MOJO_READ_MESSAGE_FLAG_NONE)); 346 EXPECT_EQ(kWorldSize, buffer_size); 347 EXPECT_STREQ(kWorld, buffer); 348 EXPECT_EQ(0u, handles_count); 349 } 350 EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT, MojoClose(hv0)); 351 } 352 353 // TODO(vtl): Test |CloseRaw()|. 354 // TODO(vtl): Test |reset()| more thoroughly? 355} 356 357TEST(CoreCppTest, TearDownWithMessagesEnqueued) { 358 // Tear down a message pipe which still has a message enqueued, with the 359 // message also having a valid message pipe handle. 360 { 361 ScopedMessagePipeHandle h0; 362 ScopedMessagePipeHandle h1; 363 CreateMessagePipe(nullptr, &h0, &h1); 364 365 // Send a handle over the previously-establish message pipe. 366 ScopedMessagePipeHandle h2; 367 ScopedMessagePipeHandle h3; 368 if (CreateMessagePipe(nullptr, &h2, &h3) != MOJO_RESULT_OK) 369 CreateMessagePipe(nullptr, &h2, &h3); // Must be old EDK. 370 371 // Write a message to |h2|, before we send |h3|. 372 const char kWorld[] = "world!"; 373 const uint32_t kWorldSize = static_cast<uint32_t>(sizeof(kWorld)); 374 EXPECT_EQ(MOJO_RESULT_OK, 375 WriteMessageRaw(h2.get(), 376 kWorld, 377 kWorldSize, 378 nullptr, 379 0, 380 MOJO_WRITE_MESSAGE_FLAG_NONE)); 381 // And also a message to |h3|. 382 EXPECT_EQ(MOJO_RESULT_OK, 383 WriteMessageRaw(h3.get(), 384 kWorld, 385 kWorldSize, 386 nullptr, 387 0, 388 MOJO_WRITE_MESSAGE_FLAG_NONE)); 389 390 // Send |h3| over |h1| to |h0|. 391 const char kHello[] = "hello"; 392 const uint32_t kHelloSize = static_cast<uint32_t>(sizeof(kHello)); 393 MojoHandle h3_value; 394 h3_value = h3.release().value(); 395 EXPECT_NE(kInvalidHandleValue, h3_value); 396 EXPECT_FALSE(h3.get().is_valid()); 397 EXPECT_EQ(MOJO_RESULT_OK, 398 WriteMessageRaw(h1.get(), 399 kHello, 400 kHelloSize, 401 &h3_value, 402 1, 403 MOJO_WRITE_MESSAGE_FLAG_NONE)); 404 // |h3_value| should actually be invalid now. 405 EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT, MojoClose(h3_value)); 406 407 EXPECT_EQ(MOJO_RESULT_OK, MojoClose(h0.release().value())); 408 EXPECT_EQ(MOJO_RESULT_OK, MojoClose(h1.release().value())); 409 EXPECT_EQ(MOJO_RESULT_OK, MojoClose(h2.release().value())); 410 } 411 412 // Do this in a different order: make the enqueued message pipe handle only 413 // half-alive. 414 { 415 ScopedMessagePipeHandle h0; 416 ScopedMessagePipeHandle h1; 417 CreateMessagePipe(nullptr, &h0, &h1); 418 419 // Send a handle over the previously-establish message pipe. 420 ScopedMessagePipeHandle h2; 421 ScopedMessagePipeHandle h3; 422 if (CreateMessagePipe(nullptr, &h2, &h3) != MOJO_RESULT_OK) 423 CreateMessagePipe(nullptr, &h2, &h3); // Must be old EDK. 424 425 // Write a message to |h2|, before we send |h3|. 426 const char kWorld[] = "world!"; 427 const uint32_t kWorldSize = static_cast<uint32_t>(sizeof(kWorld)); 428 EXPECT_EQ(MOJO_RESULT_OK, 429 WriteMessageRaw(h2.get(), 430 kWorld, 431 kWorldSize, 432 nullptr, 433 0, 434 MOJO_WRITE_MESSAGE_FLAG_NONE)); 435 // And also a message to |h3|. 436 EXPECT_EQ(MOJO_RESULT_OK, 437 WriteMessageRaw(h3.get(), 438 kWorld, 439 kWorldSize, 440 nullptr, 441 0, 442 MOJO_WRITE_MESSAGE_FLAG_NONE)); 443 444 // Send |h3| over |h1| to |h0|. 445 const char kHello[] = "hello"; 446 const uint32_t kHelloSize = static_cast<uint32_t>(sizeof(kHello)); 447 MojoHandle h3_value; 448 h3_value = h3.release().value(); 449 EXPECT_NE(kInvalidHandleValue, h3_value); 450 EXPECT_FALSE(h3.get().is_valid()); 451 EXPECT_EQ(MOJO_RESULT_OK, 452 WriteMessageRaw(h1.get(), 453 kHello, 454 kHelloSize, 455 &h3_value, 456 1, 457 MOJO_WRITE_MESSAGE_FLAG_NONE)); 458 // |h3_value| should actually be invalid now. 459 EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT, MojoClose(h3_value)); 460 461 EXPECT_EQ(MOJO_RESULT_OK, MojoClose(h2.release().value())); 462 EXPECT_EQ(MOJO_RESULT_OK, MojoClose(h0.release().value())); 463 EXPECT_EQ(MOJO_RESULT_OK, MojoClose(h1.release().value())); 464 } 465} 466 467TEST(CoreCppTest, ScopedHandleMoveCtor) { 468 ScopedSharedBufferHandle buffer1 = SharedBufferHandle::Create(1024); 469 EXPECT_TRUE(buffer1.is_valid()); 470 471 ScopedSharedBufferHandle buffer2 = SharedBufferHandle::Create(1024); 472 EXPECT_TRUE(buffer2.is_valid()); 473 474 // If this fails to close buffer1, ScopedHandleBase::CloseIfNecessary() will 475 // assert. 476 buffer1 = std::move(buffer2); 477 478 EXPECT_TRUE(buffer1.is_valid()); 479 EXPECT_FALSE(buffer2.is_valid()); 480} 481 482TEST(CoreCppTest, BasicSharedBuffer) { 483 ScopedSharedBufferHandle h0 = SharedBufferHandle::Create(100); 484 ASSERT_TRUE(h0.is_valid()); 485 486 // Map everything. 487 ScopedSharedBufferMapping mapping = h0->Map(100); 488 ASSERT_TRUE(mapping); 489 static_cast<char*>(mapping.get())[50] = 'x'; 490 491 // Duplicate |h0| to |h1|. 492 ScopedSharedBufferHandle h1 = 493 h0->Clone(SharedBufferHandle::AccessMode::READ_ONLY); 494 ASSERT_TRUE(h1.is_valid()); 495 496 // Close |h0|. 497 h0.reset(); 498 499 // The mapping should still be good. 500 static_cast<char*>(mapping.get())[51] = 'y'; 501 502 // Unmap it. 503 mapping.reset(); 504 505 // Map half of |h1|. 506 mapping = h1->MapAtOffset(50, 50); 507 ASSERT_TRUE(mapping); 508 509 // It should have what we wrote. 510 EXPECT_EQ('x', static_cast<char*>(mapping.get())[0]); 511 EXPECT_EQ('y', static_cast<char*>(mapping.get())[1]); 512 513 // Unmap it. 514 mapping.reset(); 515 h1.reset(); 516 517 // Creating a 1 EB shared buffer should fail without crashing. 518 EXPECT_FALSE(SharedBufferHandle::Create(1ULL << 60).is_valid()); 519} 520 521// TODO(vtl): Write data pipe tests. 522 523} // namespace 524} // namespace mojo 525