• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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
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});
350