Home | History | Annotate | Download | only in js
      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 define([
      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