blob: 817d42c5715f79ff9252689ee35d3ac54a7041e8 [file] [log] [blame]
Luis Hector Chavez21a249e2017-07-26 17:38:05 +00001// 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
5define([
6 "console",
7 "file",
8 "gin/test/expect",
9 "mojo/public/interfaces/bindings/tests/validation_test_interfaces.mojom",
10 "mojo/public/js/buffer",
11 "mojo/public/js/codec",
12 "mojo/public/js/connection",
13 "mojo/public/js/connector",
14 "mojo/public/js/core",
15 "mojo/public/js/test/validation_test_input_parser",
16 "mojo/public/js/router",
17 "mojo/public/js/validator",
18], function(console,
19 file,
20 expect,
21 testInterface,
22 buffer,
23 codec,
24 connection,
25 connector,
26 core,
27 parser,
28 router,
29 validator) {
30
31 var noError = validator.validationError.NONE;
32
33 function checkTestMessageParser() {
34 function TestMessageParserFailure(message, input) {
35 this.message = message;
36 this.input = input;
37 }
38
39 TestMessageParserFailure.prototype.toString = function() {
40 return 'Error: ' + this.message + ' for "' + this.input + '"';
41 }
42
43 function checkData(data, expectedData, input) {
44 if (data.byteLength != expectedData.byteLength) {
45 var s = "message length (" + data.byteLength + ") doesn't match " +
46 "expected length: " + expectedData.byteLength;
47 throw new TestMessageParserFailure(s, input);
48 }
49
50 for (var i = 0; i < data.byteLength; i++) {
51 if (data.getUint8(i) != expectedData.getUint8(i)) {
52 var s = 'message data mismatch at byte offset ' + i;
53 throw new TestMessageParserFailure(s, input);
54 }
55 }
56 }
57
58 function testFloatItems() {
59 var input = '[f]+.3e9 [d]-10.03';
60 var msg = parser.parseTestMessage(input);
61 var expectedData = new buffer.Buffer(12);
62 expectedData.setFloat32(0, +.3e9);
63 expectedData.setFloat64(4, -10.03);
64 checkData(msg.buffer, expectedData, input);
65 }
66
67 function testUnsignedIntegerItems() {
68 var input = '[u1]0x10// hello world !! \n\r \t [u2]65535 \n' +
69 '[u4]65536 [u8]0xFFFFFFFFFFFFF 0 0Xff';
70 var msg = parser.parseTestMessage(input);
71 var expectedData = new buffer.Buffer(17);
72 expectedData.setUint8(0, 0x10);
73 expectedData.setUint16(1, 65535);
74 expectedData.setUint32(3, 65536);
75 expectedData.setUint64(7, 0xFFFFFFFFFFFFF);
76 expectedData.setUint8(15, 0);
77 expectedData.setUint8(16, 0xff);
78 checkData(msg.buffer, expectedData, input);
79 }
80
81 function testSignedIntegerItems() {
82 var input = '[s8]-0x800 [s1]-128\t[s2]+0 [s4]-40';
83 var msg = parser.parseTestMessage(input);
84 var expectedData = new buffer.Buffer(15);
85 expectedData.setInt64(0, -0x800);
86 expectedData.setInt8(8, -128);
87 expectedData.setInt16(9, 0);
88 expectedData.setInt32(11, -40);
89 checkData(msg.buffer, expectedData, input);
90 }
91
92 function testByteItems() {
93 var input = '[b]00001011 [b]10000000 // hello world\n [b]00000000';
94 var msg = parser.parseTestMessage(input);
95 var expectedData = new buffer.Buffer(3);
96 expectedData.setUint8(0, 11);
97 expectedData.setUint8(1, 128);
98 expectedData.setUint8(2, 0);
99 checkData(msg.buffer, expectedData, input);
100 }
101
102 function testAnchors() {
103 var input = '[dist4]foo 0 [dist8]bar 0 [anchr]foo [anchr]bar';
104 var msg = parser.parseTestMessage(input);
105 var expectedData = new buffer.Buffer(14);
106 expectedData.setUint32(0, 14);
107 expectedData.setUint8(4, 0);
108 expectedData.setUint64(5, 9);
109 expectedData.setUint8(13, 0);
110 checkData(msg.buffer, expectedData, input);
111 }
112
113 function testHandles() {
114 var input = '// This message has handles! \n[handles]50 [u8]2';
115 var msg = parser.parseTestMessage(input);
116 var expectedData = new buffer.Buffer(8);
117 expectedData.setUint64(0, 2);
118
119 if (msg.handleCount != 50) {
120 var s = 'wrong handle count (' + msg.handleCount + ')';
121 throw new TestMessageParserFailure(s, input);
122 }
123 checkData(msg.buffer, expectedData, input);
124 }
125
126 function testEmptyInput() {
127 var msg = parser.parseTestMessage('');
128 if (msg.buffer.byteLength != 0)
129 throw new TestMessageParserFailure('expected empty message', '');
130 }
131
132 function testBlankInput() {
133 var input = ' \t // hello world \n\r \t// the answer is 42 ';
134 var msg = parser.parseTestMessage(input);
135 if (msg.buffer.byteLength != 0)
136 throw new TestMessageParserFailure('expected empty message', input);
137 }
138
139 function testInvalidInput() {
140 function parserShouldFail(input) {
141 try {
142 parser.parseTestMessage(input);
143 } catch (e) {
144 if (e instanceof parser.InputError)
145 return;
146 throw new TestMessageParserFailure(
147 'unexpected exception ' + e.toString(), input);
148 }
149 throw new TestMessageParserFailure("didn't detect invalid input", file);
150 }
151
152 ['/ hello world',
153 '[u1]x',
154 '[u2]-1000',
155 '[u1]0x100',
156 '[s2]-0x8001',
157 '[b]1',
158 '[b]1111111k',
159 '[dist4]unmatched',
160 '[anchr]hello [dist8]hello',
161 '[dist4]a [dist4]a [anchr]a',
162 // '[dist4]a [anchr]a [dist4]a [anchr]a',
163 '0 [handles]50'
164 ].forEach(parserShouldFail);
165 }
166
167 try {
168 testFloatItems();
169 testUnsignedIntegerItems();
170 testSignedIntegerItems();
171 testByteItems();
172 testInvalidInput();
173 testEmptyInput();
174 testBlankInput();
175 testHandles();
176 testAnchors();
177 } catch (e) {
178 return e.toString();
179 }
180 return null;
181 }
182
183 function getMessageTestFiles(prefix) {
184 var sourceRoot = file.getSourceRootDirectory();
185 expect(sourceRoot).not.toBeNull();
186
187 var testDir = sourceRoot +
188 "/mojo/public/interfaces/bindings/tests/data/validation/";
189 var testFiles = file.getFilesInDirectory(testDir);
190 expect(testFiles).not.toBeNull();
191 expect(testFiles.length).toBeGreaterThan(0);
192
193 // The matching ".data" pathnames with the extension removed.
194 return testFiles.filter(function(s) {
195 return s.substr(-5) == ".data" && s.indexOf(prefix) == 0;
196 }).map(function(s) {
197 return testDir + s.slice(0, -5);
198 });
199 }
200
201 function readTestMessage(filename) {
202 var contents = file.readFileToString(filename + ".data");
203 expect(contents).not.toBeNull();
204 return parser.parseTestMessage(contents);
205 }
206
207 function readTestExpected(filename) {
208 var contents = file.readFileToString(filename + ".expected");
209 expect(contents).not.toBeNull();
210 return contents.trim();
211 }
212
213 function checkValidationResult(testFile, err) {
214 var actualResult = (err === noError) ? "PASS" : err;
215 var expectedResult = readTestExpected(testFile);
216 if (actualResult != expectedResult)
217 console.log("[Test message validation failed: " + testFile + " ]");
218 expect(actualResult).toEqual(expectedResult);
219 }
220
221 function testMessageValidation(prefix, filters) {
222 var testFiles = getMessageTestFiles(prefix);
223 expect(testFiles.length).toBeGreaterThan(0);
224
225 for (var i = 0; i < testFiles.length; i++) {
226 // TODO(hansmuller) Temporarily skipping array pointer overflow tests
227 // because JS numbers are limited to 53 bits.
228 // TODO(yzshen) Skipping struct versioning tests (tests with "mthd11"
229 // in the name) because the feature is not supported in JS yet.
230 // TODO(yzshen) Skipping enum validation tests (tests with "enum" in the
231 // name) because the feature is not supported in JS yet. crbug.com/581390
232 // TODO(rudominer): Temporarily skipping 'no-such-method',
233 // 'invalid_request_flags', and 'invalid_response_flags' until additional
234 // logic in *RequestValidator and *ResponseValidator is ported from
235 // cpp to js.
236 if (testFiles[i].indexOf("overflow") != -1 ||
237 testFiles[i].indexOf("mthd11") != -1 ||
238 testFiles[i].indexOf("enum") != -1 ||
239 testFiles[i].indexOf("no_such_method") != -1 ||
240 testFiles[i].indexOf("invalid_request_flags") != -1 ||
241 testFiles[i].indexOf("invalid_response_flags") != -1) {
242 console.log("[Skipping " + testFiles[i] + "]");
243 continue;
244 }
245
246 var testMessage = readTestMessage(testFiles[i]);
247 var handles = new Array(testMessage.handleCount);
248 var message = new codec.Message(testMessage.buffer, handles);
249 var messageValidator = new validator.Validator(message);
250
251 var err = messageValidator.validateMessageHeader();
252 for (var j = 0; err === noError && j < filters.length; ++j)
253 err = filters[j](messageValidator);
254
255 checkValidationResult(testFiles[i], err);
256 }
257 }
258
259 function testConformanceMessageValidation() {
260 testMessageValidation("conformance_", [
261 testInterface.ConformanceTestInterface.validateRequest]);
262 }
263
264 function testBoundsCheckMessageValidation() {
265 testMessageValidation("boundscheck_", [
266 testInterface.BoundsCheckTestInterface.validateRequest]);
267 }
268
269 function testResponseConformanceMessageValidation() {
270 testMessageValidation("resp_conformance_", [
271 testInterface.ConformanceTestInterface.validateResponse]);
272 }
273
274 function testResponseBoundsCheckMessageValidation() {
275 testMessageValidation("resp_boundscheck_", [
276 testInterface.BoundsCheckTestInterface.validateResponse]);
277 }
278
279 function testIntegratedMessageValidation(testFilesPattern,
280 localFactory,
281 remoteFactory) {
282 var testFiles = getMessageTestFiles(testFilesPattern);
283 expect(testFiles.length).toBeGreaterThan(0);
284
285 var testMessagePipe = core.createMessagePipe();
286 expect(testMessagePipe.result).toBe(core.RESULT_OK);
287 var testConnection = new connection.TestConnection(
288 testMessagePipe.handle1, localFactory, remoteFactory);
289
290 for (var i = 0; i < testFiles.length; i++) {
291 var testMessage = readTestMessage(testFiles[i]);
292 var handles = new Array(testMessage.handleCount);
293
294 var writeMessageValue = core.writeMessage(
295 testMessagePipe.handle0,
296 new Uint8Array(testMessage.buffer.arrayBuffer),
297 new Array(testMessage.handleCount),
298 core.WRITE_MESSAGE_FLAG_NONE);
299 expect(writeMessageValue).toBe(core.RESULT_OK);
300
301 var validationError = noError;
302 testConnection.router_.validationErrorHandler = function(err) {
303 validationError = err;
304 }
305
306 testConnection.router_.connector_.waitForNextMessage();
307 checkValidationResult(testFiles[i], validationError);
308 }
309
310 testConnection.close();
311 expect(core.close(testMessagePipe.handle0)).toBe(core.RESULT_OK);
312 }
313
314 function testIntegratedMessageHeaderValidation() {
315 testIntegratedMessageValidation(
316 "integration_msghdr",
317 testInterface.IntegrationTestInterface.stubClass,
318 undefined);
319 testIntegratedMessageValidation(
320 "integration_msghdr",
321 undefined,
322 testInterface.IntegrationTestInterface.proxyClass);
323 }
324
325 function testIntegratedRequestMessageValidation() {
326 testIntegratedMessageValidation(
327 "integration_intf_rqst",
328 testInterface.IntegrationTestInterface.stubClass,
329 undefined);
330 }
331
332 function testIntegratedResponseMessageValidation() {
333 testIntegratedMessageValidation(
334 "integration_intf_resp",
335 undefined,
336 testInterface.IntegrationTestInterface.proxyClass);
337 }
338
339 expect(checkTestMessageParser()).toBeNull();
340 testConformanceMessageValidation();
341 testBoundsCheckMessageValidation();
342 testResponseConformanceMessageValidation();
343 testResponseBoundsCheckMessageValidation();
344 testIntegratedMessageHeaderValidation();
345 testIntegratedResponseMessageValidation();
346 testIntegratedRequestMessageValidation();
347
348 this.result = "PASS";
349});