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 <stdio.h> 6 7 #include <algorithm> 8 #include <string> 9 #include <vector> 10 11 #include "mojo/public/c/system/macros.h" 12 #include "mojo/public/cpp/bindings/interface_impl.h" 13 #include "mojo/public/cpp/bindings/interface_ptr.h" 14 #include "mojo/public/cpp/bindings/lib/connector.h" 15 #include "mojo/public/cpp/bindings/lib/filter_chain.h" 16 #include "mojo/public/cpp/bindings/lib/message_header_validator.h" 17 #include "mojo/public/cpp/bindings/lib/router.h" 18 #include "mojo/public/cpp/bindings/lib/validation_errors.h" 19 #include "mojo/public/cpp/bindings/message.h" 20 #include "mojo/public/cpp/bindings/tests/validation_test_input_parser.h" 21 #include "mojo/public/cpp/environment/environment.h" 22 #include "mojo/public/cpp/system/core.h" 23 #include "mojo/public/cpp/test_support/test_support.h" 24 #include "mojo/public/cpp/utility/run_loop.h" 25 #include "mojo/public/interfaces/bindings/tests/validation_test_interfaces.mojom.h" 26 #include "testing/gtest/include/gtest/gtest.h" 27 28 namespace mojo { 29 namespace test { 30 namespace { 31 32 template <typename T> 33 void Append(std::vector<uint8_t>* data_vector, T data) { 34 size_t pos = data_vector->size(); 35 data_vector->resize(pos + sizeof(T)); 36 memcpy(&(*data_vector)[pos], &data, sizeof(T)); 37 } 38 39 bool TestInputParser(const std::string& input, 40 bool expected_result, 41 const std::vector<uint8_t>& expected_data, 42 size_t expected_num_handles) { 43 std::vector<uint8_t> data; 44 size_t num_handles; 45 std::string error_message; 46 47 bool result = ParseValidationTestInput(input, &data, &num_handles, 48 &error_message); 49 if (expected_result) { 50 if (result && error_message.empty() && 51 expected_data == data && expected_num_handles == num_handles) { 52 return true; 53 } 54 55 // Compare with an empty string instead of checking |error_message.empty()|, 56 // so that the message will be printed out if the two are not equal. 57 EXPECT_EQ(std::string(), error_message); 58 EXPECT_EQ(expected_data, data); 59 EXPECT_EQ(expected_num_handles, num_handles); 60 return false; 61 } 62 63 EXPECT_FALSE(error_message.empty()); 64 return !result && !error_message.empty(); 65 } 66 67 std::vector<std::string> GetMatchingTests(const std::vector<std::string>& names, 68 const std::string& prefix) { 69 const std::string suffix = ".data"; 70 std::vector<std::string> tests; 71 for (size_t i = 0; i < names.size(); ++i) { 72 if (names[i].size() >= suffix.size() && 73 names[i].substr(0, prefix.size()) == prefix && 74 names[i].substr(names[i].size() - suffix.size()) == suffix) 75 tests.push_back(names[i].substr(0, names[i].size() - suffix.size())); 76 } 77 return tests; 78 } 79 80 bool ReadFile(const std::string& path, std::string* result) { 81 FILE* fp = OpenSourceRootRelativeFile(path.c_str()); 82 if (!fp) { 83 ADD_FAILURE() << "File not found: " << path; 84 return false; 85 } 86 fseek(fp, 0, SEEK_END); 87 size_t size = static_cast<size_t>(ftell(fp)); 88 if (size == 0) { 89 result->clear(); 90 fclose(fp); 91 return true; 92 } 93 fseek(fp, 0, SEEK_SET); 94 result->resize(size); 95 size_t size_read = fread(&result->at(0), 1, size, fp); 96 fclose(fp); 97 return size == size_read; 98 } 99 100 bool ReadAndParseDataFile(const std::string& path, 101 std::vector<uint8_t>* data, 102 size_t* num_handles) { 103 std::string input; 104 if (!ReadFile(path, &input)) 105 return false; 106 107 std::string error_message; 108 if (!ParseValidationTestInput(input, data, num_handles, &error_message)) { 109 ADD_FAILURE() << error_message; 110 return false; 111 } 112 113 return true; 114 } 115 116 bool ReadResultFile(const std::string& path, std::string* result) { 117 if (!ReadFile(path, result)) 118 return false; 119 120 // Result files are new-line delimited text files. Remove any CRs. 121 result->erase(std::remove(result->begin(), result->end(), '\r'), 122 result->end()); 123 124 // Remove trailing LFs. 125 size_t pos = result->find_last_not_of('\n'); 126 if (pos == std::string::npos) 127 result->clear(); 128 else 129 result->resize(pos + 1); 130 131 return true; 132 } 133 134 std::string GetPath(const std::string& root, const std::string& suffix) { 135 return "mojo/public/interfaces/bindings/tests/data/validation/" + 136 root + suffix; 137 } 138 139 // |message| should be a newly created object. 140 bool ReadTestCase(const std::string& test, 141 Message* message, 142 std::string* expected) { 143 std::vector<uint8_t> data; 144 size_t num_handles; 145 if (!ReadAndParseDataFile(GetPath(test, ".data"), &data, &num_handles) || 146 !ReadResultFile(GetPath(test, ".expected"), expected)) { 147 return false; 148 } 149 150 message->AllocUninitializedData(static_cast<uint32_t>(data.size())); 151 if (!data.empty()) 152 memcpy(message->mutable_data(), &data[0], data.size()); 153 message->mutable_handles()->resize(num_handles); 154 155 return true; 156 } 157 158 void RunValidationTests(const std::string& prefix, 159 MessageReceiver* test_message_receiver) { 160 std::vector<std::string> names = 161 EnumerateSourceRootRelativeDirectory(GetPath("", "")); 162 std::vector<std::string> tests = GetMatchingTests(names, prefix); 163 164 for (size_t i = 0; i < tests.size(); ++i) { 165 Message message; 166 std::string expected; 167 ASSERT_TRUE(ReadTestCase(tests[i], &message, &expected)); 168 169 std::string result; 170 mojo::internal::ValidationErrorObserverForTesting observer; 171 bool unused MOJO_ALLOW_UNUSED = test_message_receiver->Accept(&message); 172 if (observer.last_error() == mojo::internal::VALIDATION_ERROR_NONE) 173 result = "PASS"; 174 else 175 result = mojo::internal::ValidationErrorToString(observer.last_error()); 176 177 EXPECT_EQ(expected, result) << "failed test: " << tests[i]; 178 } 179 } 180 181 class DummyMessageReceiver : public MessageReceiver { 182 public: 183 virtual bool Accept(Message* message) MOJO_OVERRIDE { 184 return true; // Any message is OK. 185 } 186 }; 187 188 class ValidationTest : public testing::Test { 189 public: 190 virtual ~ValidationTest() { 191 } 192 193 private: 194 Environment env_; 195 }; 196 197 class ValidationIntegrationTest : public ValidationTest { 198 public: 199 ValidationIntegrationTest() : test_message_receiver_(NULL) { 200 } 201 202 virtual ~ValidationIntegrationTest() { 203 } 204 205 virtual void SetUp() MOJO_OVERRIDE { 206 ScopedMessagePipeHandle tester_endpoint; 207 ASSERT_EQ(MOJO_RESULT_OK, 208 CreateMessagePipe(NULL, &tester_endpoint, &testee_endpoint_)); 209 test_message_receiver_ = 210 new TestMessageReceiver(this, tester_endpoint.Pass()); 211 } 212 213 virtual void TearDown() MOJO_OVERRIDE { 214 delete test_message_receiver_; 215 test_message_receiver_ = NULL; 216 217 // Make sure that the other end receives the OnConnectionError() 218 // notification. 219 PumpMessages(); 220 } 221 222 MessageReceiver* test_message_receiver() { 223 return test_message_receiver_; 224 } 225 226 ScopedMessagePipeHandle testee_endpoint() { 227 return testee_endpoint_.Pass(); 228 } 229 230 private: 231 class TestMessageReceiver : public MessageReceiver { 232 public: 233 TestMessageReceiver(ValidationIntegrationTest* owner, 234 ScopedMessagePipeHandle handle) 235 : owner_(owner), 236 connector_(handle.Pass()) { 237 } 238 virtual ~TestMessageReceiver() { 239 } 240 241 virtual bool Accept(Message* message) MOJO_OVERRIDE { 242 bool rv = connector_.Accept(message); 243 owner_->PumpMessages(); 244 return rv; 245 } 246 247 public: 248 ValidationIntegrationTest* owner_; 249 mojo::internal::Connector connector_; 250 }; 251 252 void PumpMessages() { 253 loop_.RunUntilIdle(); 254 } 255 256 RunLoop loop_; 257 TestMessageReceiver* test_message_receiver_; 258 ScopedMessagePipeHandle testee_endpoint_; 259 }; 260 261 class IntegrationTestInterface1Client : public IntegrationTestInterface1 { 262 public: 263 virtual ~IntegrationTestInterface1Client() { 264 } 265 266 virtual void Method0(BasicStructPtr param0) MOJO_OVERRIDE { 267 } 268 }; 269 270 class IntegrationTestInterface1Impl 271 : public InterfaceImpl<IntegrationTestInterface1> { 272 public: 273 virtual ~IntegrationTestInterface1Impl() { 274 } 275 276 virtual void Method0(BasicStructPtr param0) MOJO_OVERRIDE { 277 } 278 }; 279 280 TEST_F(ValidationTest, InputParser) { 281 { 282 // The parser, as well as Append() defined above, assumes that this code is 283 // running on a little-endian platform. Test whether that is true. 284 uint16_t x = 1; 285 ASSERT_EQ(1, *(reinterpret_cast<char*>(&x))); 286 } 287 { 288 // Test empty input. 289 std::string input; 290 std::vector<uint8_t> expected; 291 292 EXPECT_TRUE(TestInputParser(input, true, expected, 0)); 293 } 294 { 295 // Test input that only consists of comments and whitespaces. 296 std::string input = " \t // hello world \n\r \t// the answer is 42 "; 297 std::vector<uint8_t> expected; 298 299 EXPECT_TRUE(TestInputParser(input, true, expected, 0)); 300 } 301 { 302 std::string input = "[u1]0x10// hello world !! \n\r \t [u2]65535 \n" 303 "[u4]65536 [u8]0xFFFFFFFFFFFFFFFF 0 0Xff"; 304 std::vector<uint8_t> expected; 305 Append(&expected, static_cast<uint8_t>(0x10)); 306 Append(&expected, static_cast<uint16_t>(65535)); 307 Append(&expected, static_cast<uint32_t>(65536)); 308 Append(&expected, static_cast<uint64_t>(0xffffffffffffffff)); 309 Append(&expected, static_cast<uint8_t>(0)); 310 Append(&expected, static_cast<uint8_t>(0xff)); 311 312 EXPECT_TRUE(TestInputParser(input, true, expected, 0)); 313 } 314 { 315 std::string input = "[s8]-0x800 [s1]-128\t[s2]+0 [s4]-40"; 316 std::vector<uint8_t> expected; 317 Append(&expected, -static_cast<int64_t>(0x800)); 318 Append(&expected, static_cast<int8_t>(-128)); 319 Append(&expected, static_cast<int16_t>(0)); 320 Append(&expected, static_cast<int32_t>(-40)); 321 322 EXPECT_TRUE(TestInputParser(input, true, expected, 0)); 323 } 324 { 325 std::string input = "[b]00001011 [b]10000000 // hello world\r [b]00000000"; 326 std::vector<uint8_t> expected; 327 Append(&expected, static_cast<uint8_t>(11)); 328 Append(&expected, static_cast<uint8_t>(128)); 329 Append(&expected, static_cast<uint8_t>(0)); 330 331 EXPECT_TRUE(TestInputParser(input, true, expected, 0)); 332 } 333 { 334 std::string input = "[f]+.3e9 [d]-10.03"; 335 std::vector<uint8_t> expected; 336 Append(&expected, +.3e9f); 337 Append(&expected, -10.03); 338 339 EXPECT_TRUE(TestInputParser(input, true, expected, 0)); 340 } 341 { 342 std::string input = "[dist4]foo 0 [dist8]bar 0 [anchr]foo [anchr]bar"; 343 std::vector<uint8_t> expected; 344 Append(&expected, static_cast<uint32_t>(14)); 345 Append(&expected, static_cast<uint8_t>(0)); 346 Append(&expected, static_cast<uint64_t>(9)); 347 Append(&expected, static_cast<uint8_t>(0)); 348 349 EXPECT_TRUE(TestInputParser(input, true, expected, 0)); 350 } 351 { 352 std::string input = "// This message has handles! \n[handles]50 [u8]2"; 353 std::vector<uint8_t> expected; 354 Append(&expected, static_cast<uint64_t>(2)); 355 356 EXPECT_TRUE(TestInputParser(input, true, expected, 50)); 357 } 358 359 // Test some failure cases. 360 { 361 const char* error_inputs[] = { 362 "/ hello world", 363 "[u1]x", 364 "[u2]-1000", 365 "[u1]0x100", 366 "[s2]-0x8001", 367 "[b]1", 368 "[b]1111111k", 369 "[dist4]unmatched", 370 "[anchr]hello [dist8]hello", 371 "[dist4]a [dist4]a [anchr]a", 372 "[dist4]a [anchr]a [dist4]a [anchr]a", 373 "0 [handles]50", 374 NULL 375 }; 376 377 for (size_t i = 0; error_inputs[i]; ++i) { 378 std::vector<uint8_t> expected; 379 if (!TestInputParser(error_inputs[i], false, expected, 0)) 380 ADD_FAILURE() << "Unexpected test result for: " << error_inputs[i]; 381 } 382 } 383 } 384 385 TEST_F(ValidationTest, Conformance) { 386 DummyMessageReceiver dummy_receiver; 387 mojo::internal::FilterChain validators(&dummy_receiver); 388 validators.Append<mojo::internal::MessageHeaderValidator>(); 389 validators.Append<ConformanceTestInterface::RequestValidator_>(); 390 391 RunValidationTests("conformance_", validators.GetHead()); 392 } 393 394 TEST_F(ValidationTest, NotImplemented) { 395 DummyMessageReceiver dummy_receiver; 396 mojo::internal::FilterChain validators(&dummy_receiver); 397 validators.Append<mojo::internal::MessageHeaderValidator>(); 398 validators.Append<ConformanceTestInterface::RequestValidator_>(); 399 400 RunValidationTests("not_implemented_", validators.GetHead()); 401 } 402 403 TEST_F(ValidationIntegrationTest, InterfacePtr) { 404 // Test that InterfacePtr<X> applies the correct validators and they don't 405 // conflict with each other: 406 // - MessageHeaderValidator 407 // - X::Client::RequestValidator_ 408 // - X::ResponseValidator_ 409 410 IntegrationTestInterface1Client interface1_client; 411 IntegrationTestInterface2Ptr interface2_ptr = 412 MakeProxy<IntegrationTestInterface2>(testee_endpoint().Pass()); 413 interface2_ptr.set_client(&interface1_client); 414 interface2_ptr.internal_state()->router_for_testing()->EnableTestingMode(); 415 416 RunValidationTests("integration_", test_message_receiver()); 417 } 418 419 TEST_F(ValidationIntegrationTest, InterfaceImpl) { 420 // Test that InterfaceImpl<X> applies the correct validators and they don't 421 // conflict with each other: 422 // - MessageHeaderValidator 423 // - X::RequestValidator_ 424 // - X::Client::ResponseValidator_ 425 426 // |interface1_impl| will delete itself when the pipe is closed. 427 IntegrationTestInterface1Impl* interface1_impl = 428 BindToPipe(new IntegrationTestInterface1Impl(), testee_endpoint().Pass()); 429 interface1_impl->internal_state()->router()->EnableTestingMode(); 430 431 RunValidationTests("integration_", test_message_receiver()); 432 } 433 434 } // namespace 435 } // namespace test 436 } // namespace mojo 437