Luis Hector Chavez | 21a249e | 2017-07-26 17:38:05 +0000 | [diff] [blame] | 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 | }); |