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 <stddef.h>
6 #include <stdint.h>
7 #include <stdio.h>
8 #include <algorithm>
9 #include <string>
10 #include <utility>
11 #include <vector>
12
13 #include "base/message_loop/message_loop.h"
14 #include "base/run_loop.h"
15 #include "base/threading/thread_task_runner_handle.h"
16 #include "mojo/public/c/system/macros.h"
17 #include "mojo/public/cpp/bindings/binding.h"
18 #include "mojo/public/cpp/bindings/connector.h"
19 #include "mojo/public/cpp/bindings/interface_ptr.h"
20 #include "mojo/public/cpp/bindings/lib/filter_chain.h"
21 #include "mojo/public/cpp/bindings/lib/router.h"
22 #include "mojo/public/cpp/bindings/lib/validation_errors.h"
23 #include "mojo/public/cpp/bindings/message.h"
24 #include "mojo/public/cpp/bindings/message_header_validator.h"
25 #include "mojo/public/cpp/bindings/tests/validation_test_input_parser.h"
26 #include "mojo/public/cpp/system/core.h"
27 #include "mojo/public/cpp/test_support/test_support.h"
28 #include "mojo/public/interfaces/bindings/tests/validation_test_associated_interfaces.mojom.h"
29 #include "mojo/public/interfaces/bindings/tests/validation_test_interfaces.mojom.h"
30 #include "testing/gtest/include/gtest/gtest.h"
31
32 namespace mojo {
33 namespace test {
34 namespace {
35
36 template <typename T>
Append(std::vector<uint8_t> * data_vector,T data)37 void Append(std::vector<uint8_t>* data_vector, T data) {
38 size_t pos = data_vector->size();
39 data_vector->resize(pos + sizeof(T));
40 memcpy(&(*data_vector)[pos], &data, sizeof(T));
41 }
42
TestInputParser(const std::string & input,bool expected_result,const std::vector<uint8_t> & expected_data,size_t expected_num_handles)43 bool TestInputParser(const std::string& input,
44 bool expected_result,
45 const std::vector<uint8_t>& expected_data,
46 size_t expected_num_handles) {
47 std::vector<uint8_t> data;
48 size_t num_handles;
49 std::string error_message;
50
51 bool result =
52 ParseValidationTestInput(input, &data, &num_handles, &error_message);
53 if (expected_result) {
54 if (result && error_message.empty() && expected_data == data &&
55 expected_num_handles == num_handles) {
56 return true;
57 }
58
59 // Compare with an empty string instead of checking |error_message.empty()|,
60 // so that the message will be printed out if the two are not equal.
61 EXPECT_EQ(std::string(), error_message);
62 EXPECT_EQ(expected_data, data);
63 EXPECT_EQ(expected_num_handles, num_handles);
64 return false;
65 }
66
67 EXPECT_FALSE(error_message.empty());
68 return !result && !error_message.empty();
69 }
70
GetMatchingTests(const std::vector<std::string> & names,const std::string & prefix)71 std::vector<std::string> GetMatchingTests(const std::vector<std::string>& names,
72 const std::string& prefix) {
73 const std::string suffix = ".data";
74 std::vector<std::string> tests;
75 for (size_t i = 0; i < names.size(); ++i) {
76 if (names[i].size() >= suffix.size() &&
77 names[i].substr(0, prefix.size()) == prefix &&
78 names[i].substr(names[i].size() - suffix.size()) == suffix)
79 tests.push_back(names[i].substr(0, names[i].size() - suffix.size()));
80 }
81 return tests;
82 }
83
ReadFile(const std::string & path,std::string * result)84 bool ReadFile(const std::string& path, std::string* result) {
85 FILE* fp = OpenSourceRootRelativeFile(path.c_str());
86 if (!fp) {
87 ADD_FAILURE() << "File not found: " << path;
88 return false;
89 }
90 fseek(fp, 0, SEEK_END);
91 size_t size = static_cast<size_t>(ftell(fp));
92 if (size == 0) {
93 result->clear();
94 fclose(fp);
95 return true;
96 }
97 fseek(fp, 0, SEEK_SET);
98 result->resize(size);
99 size_t size_read = fread(&result->at(0), 1, size, fp);
100 fclose(fp);
101 return size == size_read;
102 }
103
ReadAndParseDataFile(const std::string & path,std::vector<uint8_t> * data,size_t * num_handles)104 bool ReadAndParseDataFile(const std::string& path,
105 std::vector<uint8_t>* data,
106 size_t* num_handles) {
107 std::string input;
108 if (!ReadFile(path, &input))
109 return false;
110
111 std::string error_message;
112 if (!ParseValidationTestInput(input, data, num_handles, &error_message)) {
113 ADD_FAILURE() << error_message;
114 return false;
115 }
116
117 return true;
118 }
119
ReadResultFile(const std::string & path,std::string * result)120 bool ReadResultFile(const std::string& path, std::string* result) {
121 if (!ReadFile(path, result))
122 return false;
123
124 // Result files are new-line delimited text files. Remove any CRs.
125 result->erase(std::remove(result->begin(), result->end(), '\r'),
126 result->end());
127
128 // Remove trailing LFs.
129 size_t pos = result->find_last_not_of('\n');
130 if (pos == std::string::npos)
131 result->clear();
132 else
133 result->resize(pos + 1);
134
135 return true;
136 }
137
GetPath(const std::string & root,const std::string & suffix)138 std::string GetPath(const std::string& root, const std::string& suffix) {
139 return "mojo/public/interfaces/bindings/tests/data/validation/" + root +
140 suffix;
141 }
142
143 // |message| should be a newly created object.
ReadTestCase(const std::string & test,Message * message,std::string * expected)144 bool ReadTestCase(const std::string& test,
145 Message* message,
146 std::string* expected) {
147 std::vector<uint8_t> data;
148 size_t num_handles;
149 if (!ReadAndParseDataFile(GetPath(test, ".data"), &data, &num_handles) ||
150 !ReadResultFile(GetPath(test, ".expected"), expected)) {
151 return false;
152 }
153
154 message->Initialize(static_cast<uint32_t>(data.size()),
155 false /* zero_initialized */);
156 if (!data.empty())
157 memcpy(message->mutable_data(), &data[0], data.size());
158 message->mutable_handles()->resize(num_handles);
159
160 return true;
161 }
162
RunValidationTests(const std::string & prefix,MessageReceiver * test_message_receiver)163 void RunValidationTests(const std::string& prefix,
164 MessageReceiver* test_message_receiver) {
165 std::vector<std::string> names =
166 EnumerateSourceRootRelativeDirectory(GetPath("", ""));
167 std::vector<std::string> tests = GetMatchingTests(names, prefix);
168 ASSERT_FALSE(tests.empty());
169
170 for (size_t i = 0; i < tests.size(); ++i) {
171 Message message;
172 std::string expected;
173 ASSERT_TRUE(ReadTestCase(tests[i], &message, &expected));
174
175 std::string result;
176 base::RunLoop run_loop;
177 mojo::internal::ValidationErrorObserverForTesting observer(
178 run_loop.QuitClosure());
179 ignore_result(test_message_receiver->Accept(&message));
180 if (expected != "PASS") // Observer only gets called on errors.
181 run_loop.Run();
182 if (observer.last_error() == mojo::internal::VALIDATION_ERROR_NONE)
183 result = "PASS";
184 else
185 result = mojo::internal::ValidationErrorToString(observer.last_error());
186
187 EXPECT_EQ(expected, result) << "failed test: " << tests[i];
188 }
189 }
190
191 class DummyMessageReceiver : public MessageReceiver {
192 public:
Accept(Message * message)193 bool Accept(Message* message) override {
194 return true; // Any message is OK.
195 }
196 };
197
198 class ValidationTest : public testing::Test {
199 public:
ValidationTest()200 ValidationTest() {}
201
202 protected:
203 base::MessageLoop loop_;
204 };
205
206 class ValidationIntegrationTest : public ValidationTest {
207 public:
ValidationIntegrationTest()208 ValidationIntegrationTest() : test_message_receiver_(nullptr) {}
209
~ValidationIntegrationTest()210 ~ValidationIntegrationTest() override {}
211
SetUp()212 void SetUp() override {
213 ScopedMessagePipeHandle tester_endpoint;
214 ASSERT_EQ(MOJO_RESULT_OK,
215 CreateMessagePipe(nullptr, &tester_endpoint, &testee_endpoint_));
216 test_message_receiver_ =
217 new TestMessageReceiver(this, std::move(tester_endpoint));
218 }
219
TearDown()220 void TearDown() override {
221 delete test_message_receiver_;
222 test_message_receiver_ = nullptr;
223
224 // Make sure that the other end receives the OnConnectionError()
225 // notification.
226 PumpMessages();
227 }
228
test_message_receiver()229 MessageReceiver* test_message_receiver() { return test_message_receiver_; }
230
testee_endpoint()231 ScopedMessagePipeHandle testee_endpoint() {
232 return std::move(testee_endpoint_);
233 }
234
235 private:
236 class TestMessageReceiver : public MessageReceiver {
237 public:
TestMessageReceiver(ValidationIntegrationTest * owner,ScopedMessagePipeHandle handle)238 TestMessageReceiver(ValidationIntegrationTest* owner,
239 ScopedMessagePipeHandle handle)
240 : owner_(owner),
241 connector_(std::move(handle),
242 mojo::Connector::SINGLE_THREADED_SEND,
243 base::ThreadTaskRunnerHandle::Get()) {
244 connector_.set_enforce_errors_from_incoming_receiver(false);
245 }
~TestMessageReceiver()246 ~TestMessageReceiver() override {}
247
Accept(Message * message)248 bool Accept(Message* message) override {
249 return connector_.Accept(message);
250 }
251
252 public:
253 ValidationIntegrationTest* owner_;
254 mojo::Connector connector_;
255 };
256
PumpMessages()257 void PumpMessages() { base::RunLoop().RunUntilIdle(); }
258
259 TestMessageReceiver* test_message_receiver_;
260 ScopedMessagePipeHandle testee_endpoint_;
261 };
262
263 class IntegrationTestInterfaceImpl : public IntegrationTestInterface {
264 public:
~IntegrationTestInterfaceImpl()265 ~IntegrationTestInterfaceImpl() override {}
266
Method0(BasicStructPtr param0,const Method0Callback & callback)267 void Method0(BasicStructPtr param0,
268 const Method0Callback& callback) override {
269 callback.Run(Array<uint8_t>::New(0u));
270 }
271 };
272
TEST_F(ValidationTest,InputParser)273 TEST_F(ValidationTest, InputParser) {
274 {
275 // The parser, as well as Append() defined above, assumes that this code is
276 // running on a little-endian platform. Test whether that is true.
277 uint16_t x = 1;
278 ASSERT_EQ(1, *(reinterpret_cast<char*>(&x)));
279 }
280 {
281 // Test empty input.
282 std::string input;
283 std::vector<uint8_t> expected;
284
285 EXPECT_TRUE(TestInputParser(input, true, expected, 0));
286 }
287 {
288 // Test input that only consists of comments and whitespaces.
289 std::string input = " \t // hello world \n\r \t// the answer is 42 ";
290 std::vector<uint8_t> expected;
291
292 EXPECT_TRUE(TestInputParser(input, true, expected, 0));
293 }
294 {
295 std::string input =
296 "[u1]0x10// hello world !! \n\r \t [u2]65535 \n"
297 "[u4]65536 [u8]0xFFFFFFFFFFFFFFFF 0 0Xff";
298 std::vector<uint8_t> expected;
299 Append(&expected, static_cast<uint8_t>(0x10));
300 Append(&expected, static_cast<uint16_t>(65535));
301 Append(&expected, static_cast<uint32_t>(65536));
302 Append(&expected, static_cast<uint64_t>(0xffffffffffffffff));
303 Append(&expected, static_cast<uint8_t>(0));
304 Append(&expected, static_cast<uint8_t>(0xff));
305
306 EXPECT_TRUE(TestInputParser(input, true, expected, 0));
307 }
308 {
309 std::string input = "[s8]-0x800 [s1]-128\t[s2]+0 [s4]-40";
310 std::vector<uint8_t> expected;
311 Append(&expected, -static_cast<int64_t>(0x800));
312 Append(&expected, static_cast<int8_t>(-128));
313 Append(&expected, static_cast<int16_t>(0));
314 Append(&expected, static_cast<int32_t>(-40));
315
316 EXPECT_TRUE(TestInputParser(input, true, expected, 0));
317 }
318 {
319 std::string input = "[b]00001011 [b]10000000 // hello world\r [b]00000000";
320 std::vector<uint8_t> expected;
321 Append(&expected, static_cast<uint8_t>(11));
322 Append(&expected, static_cast<uint8_t>(128));
323 Append(&expected, static_cast<uint8_t>(0));
324
325 EXPECT_TRUE(TestInputParser(input, true, expected, 0));
326 }
327 {
328 std::string input = "[f]+.3e9 [d]-10.03";
329 std::vector<uint8_t> expected;
330 Append(&expected, +.3e9f);
331 Append(&expected, -10.03);
332
333 EXPECT_TRUE(TestInputParser(input, true, expected, 0));
334 }
335 {
336 std::string input = "[dist4]foo 0 [dist8]bar 0 [anchr]foo [anchr]bar";
337 std::vector<uint8_t> expected;
338 Append(&expected, static_cast<uint32_t>(14));
339 Append(&expected, static_cast<uint8_t>(0));
340 Append(&expected, static_cast<uint64_t>(9));
341 Append(&expected, static_cast<uint8_t>(0));
342
343 EXPECT_TRUE(TestInputParser(input, true, expected, 0));
344 }
345 {
346 std::string input = "// This message has handles! \n[handles]50 [u8]2";
347 std::vector<uint8_t> expected;
348 Append(&expected, static_cast<uint64_t>(2));
349
350 EXPECT_TRUE(TestInputParser(input, true, expected, 50));
351 }
352
353 // Test some failure cases.
354 {
355 const char* error_inputs[] = {"/ hello world",
356 "[u1]x",
357 "[u2]-1000",
358 "[u1]0x100",
359 "[s2]-0x8001",
360 "[b]1",
361 "[b]1111111k",
362 "[dist4]unmatched",
363 "[anchr]hello [dist8]hello",
364 "[dist4]a [dist4]a [anchr]a",
365 "[dist4]a [anchr]a [dist4]a [anchr]a",
366 "0 [handles]50",
367 nullptr};
368
369 for (size_t i = 0; error_inputs[i]; ++i) {
370 std::vector<uint8_t> expected;
371 if (!TestInputParser(error_inputs[i], false, expected, 0))
372 ADD_FAILURE() << "Unexpected test result for: " << error_inputs[i];
373 }
374 }
375 }
376
TEST_F(ValidationTest,Conformance)377 TEST_F(ValidationTest, Conformance) {
378 DummyMessageReceiver dummy_receiver;
379 mojo::internal::FilterChain validators(&dummy_receiver);
380 validators.Append<mojo::MessageHeaderValidator>();
381 validators.Append<ConformanceTestInterface::RequestValidator_>();
382
383 RunValidationTests("conformance_", validators.GetHead());
384 }
385
TEST_F(ValidationTest,AssociatedConformace)386 TEST_F(ValidationTest, AssociatedConformace) {
387 DummyMessageReceiver dummy_receiver;
388 mojo::internal::FilterChain validators(&dummy_receiver);
389 validators.Append<mojo::MessageHeaderValidator>();
390 validators.Append<AssociatedConformanceTestInterface::RequestValidator_>();
391
392 RunValidationTests("associated_conformance_", validators.GetHead());
393 }
394
395 // This test is similar to Conformance test but its goal is specifically
396 // do bounds-check testing of message validation. For example we test the
397 // detection of off-by-one errors in method ordinals.
TEST_F(ValidationTest,BoundsCheck)398 TEST_F(ValidationTest, BoundsCheck) {
399 DummyMessageReceiver dummy_receiver;
400 mojo::internal::FilterChain validators(&dummy_receiver);
401 validators.Append<mojo::MessageHeaderValidator>();
402 validators.Append<BoundsCheckTestInterface::RequestValidator_>();
403
404 RunValidationTests("boundscheck_", validators.GetHead());
405 }
406
407 // This test is similar to the Conformance test but for responses.
TEST_F(ValidationTest,ResponseConformance)408 TEST_F(ValidationTest, ResponseConformance) {
409 DummyMessageReceiver dummy_receiver;
410 mojo::internal::FilterChain validators(&dummy_receiver);
411 validators.Append<mojo::MessageHeaderValidator>();
412 validators.Append<ConformanceTestInterface::ResponseValidator_>();
413
414 RunValidationTests("resp_conformance_", validators.GetHead());
415 }
416
417 // This test is similar to the BoundsCheck test but for responses.
TEST_F(ValidationTest,ResponseBoundsCheck)418 TEST_F(ValidationTest, ResponseBoundsCheck) {
419 DummyMessageReceiver dummy_receiver;
420 mojo::internal::FilterChain validators(&dummy_receiver);
421 validators.Append<mojo::MessageHeaderValidator>();
422 validators.Append<BoundsCheckTestInterface::ResponseValidator_>();
423
424 RunValidationTests("resp_boundscheck_", validators.GetHead());
425 }
426
427 // Test that InterfacePtr<X> applies the correct validators and they don't
428 // conflict with each other:
429 // - MessageHeaderValidator
430 // - X::ResponseValidator_
TEST_F(ValidationIntegrationTest,InterfacePtr)431 TEST_F(ValidationIntegrationTest, InterfacePtr) {
432 IntegrationTestInterfacePtr interface_ptr = MakeProxy(
433 InterfacePtrInfo<IntegrationTestInterface>(testee_endpoint(), 0u));
434 interface_ptr.internal_state()->EnableTestingMode();
435
436 RunValidationTests("integration_intf_resp", test_message_receiver());
437 RunValidationTests("integration_msghdr", test_message_receiver());
438 }
439
440 // Test that Binding<X> applies the correct validators and they don't
441 // conflict with each other:
442 // - MessageHeaderValidator
443 // - X::RequestValidator_
TEST_F(ValidationIntegrationTest,Binding)444 TEST_F(ValidationIntegrationTest, Binding) {
445 IntegrationTestInterfaceImpl interface_impl;
446 Binding<IntegrationTestInterface> binding(
447 &interface_impl,
448 MakeRequest<IntegrationTestInterface>(testee_endpoint()));
449 binding.EnableTestingMode();
450
451 RunValidationTests("integration_intf_rqst", test_message_receiver());
452 RunValidationTests("integration_msghdr", test_message_receiver());
453 }
454
455 // Test pointer validation (specifically, that the encoded offset is 32-bit)
TEST_F(ValidationTest,ValidateEncodedPointer)456 TEST_F(ValidationTest, ValidateEncodedPointer) {
457 uint64_t offset;
458
459 offset = 0ULL;
460 EXPECT_TRUE(mojo::internal::ValidateEncodedPointer(&offset));
461
462 offset = 1ULL;
463 EXPECT_TRUE(mojo::internal::ValidateEncodedPointer(&offset));
464
465 // offset must be <= 32-bit.
466 offset = std::numeric_limits<uint32_t>::max() + 1ULL;
467 EXPECT_FALSE(mojo::internal::ValidateEncodedPointer(&offset));
468 }
469
470 // Tests the IsKnownEnumValue() function generated for BasicEnum.
TEST(EnumValueValidationTest,BasicEnum)471 TEST(EnumValueValidationTest, BasicEnum) {
472 // BasicEnum can have -3,0,1,10 as possible integral values.
473 EXPECT_FALSE(IsKnownEnumValue(static_cast<BasicEnum>(-4)));
474 EXPECT_TRUE(IsKnownEnumValue(static_cast<BasicEnum>(-3)));
475 EXPECT_FALSE(IsKnownEnumValue(static_cast<BasicEnum>(-2)));
476 EXPECT_FALSE(IsKnownEnumValue(static_cast<BasicEnum>(-1)));
477 EXPECT_TRUE(IsKnownEnumValue(static_cast<BasicEnum>(0)));
478 EXPECT_TRUE(IsKnownEnumValue(static_cast<BasicEnum>(1)));
479 EXPECT_FALSE(IsKnownEnumValue(static_cast<BasicEnum>(2)));
480 EXPECT_FALSE(IsKnownEnumValue(static_cast<BasicEnum>(9)));
481 // In the mojom, we represent this value as hex (0xa).
482 EXPECT_TRUE(IsKnownEnumValue(static_cast<BasicEnum>(10)));
483 EXPECT_FALSE(IsKnownEnumValue(static_cast<BasicEnum>(11)));
484 }
485
486 // Tests the IsKnownEnumValue() method generated for StructWithEnum.
TEST(EnumValueValidationTest,EnumWithin)487 TEST(EnumValueValidationTest, EnumWithin) {
488 // StructWithEnum::EnumWithin can have [0,4] as possible integral values.
489 EXPECT_FALSE(IsKnownEnumValue(static_cast<StructWithEnum::EnumWithin>(-1)));
490 EXPECT_TRUE(IsKnownEnumValue(static_cast<StructWithEnum::EnumWithin>(0)));
491 EXPECT_TRUE(IsKnownEnumValue(static_cast<StructWithEnum::EnumWithin>(1)));
492 EXPECT_TRUE(IsKnownEnumValue(static_cast<StructWithEnum::EnumWithin>(2)));
493 EXPECT_TRUE(IsKnownEnumValue(static_cast<StructWithEnum::EnumWithin>(3)));
494 EXPECT_FALSE(IsKnownEnumValue(static_cast<StructWithEnum::EnumWithin>(4)));
495 }
496
497 } // namespace
498 } // namespace test
499 } // namespace mojo
500