diff options
Diffstat (limited to 'mojo/public/js/tests/validation_unittest.js')
-rw-r--r-- | mojo/public/js/tests/validation_unittest.js | 334 |
1 files changed, 334 insertions, 0 deletions
diff --git a/mojo/public/js/tests/validation_unittest.js b/mojo/public/js/tests/validation_unittest.js new file mode 100644 index 0000000000..2a07315436 --- /dev/null +++ b/mojo/public/js/tests/validation_unittest.js @@ -0,0 +1,334 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +define([ + "console", + "file", + "gin/test/expect", + "mojo/public/interfaces/bindings/tests/validation_test_interfaces.mojom", + "mojo/public/js/bindings", + "mojo/public/js/buffer", + "mojo/public/js/codec", + "mojo/public/js/core", + "mojo/public/js/tests/validation_test_input_parser", + "mojo/public/js/validator", +], function(console, + file, + expect, + testInterface, + bindings, + buffer, + codec, + core, + parser, + validator) { + + var noError = validator.validationError.NONE; + + function checkTestMessageParser() { + function TestMessageParserFailure(message, input) { + this.message = message; + this.input = input; + } + + TestMessageParserFailure.prototype.toString = function() { + return 'Error: ' + this.message + ' for "' + this.input + '"'; + }; + + function checkData(data, expectedData, input) { + if (data.byteLength != expectedData.byteLength) { + var s = "message length (" + data.byteLength + ") doesn't match " + + "expected length: " + expectedData.byteLength; + throw new TestMessageParserFailure(s, input); + } + + for (var i = 0; i < data.byteLength; i++) { + if (data.getUint8(i) != expectedData.getUint8(i)) { + var s = 'message data mismatch at byte offset ' + i; + throw new TestMessageParserFailure(s, input); + } + } + } + + function testFloatItems() { + var input = '[f]+.3e9 [d]-10.03'; + var msg = parser.parseTestMessage(input); + var expectedData = new buffer.Buffer(12); + expectedData.setFloat32(0, +.3e9); + expectedData.setFloat64(4, -10.03); + checkData(msg.buffer, expectedData, input); + } + + function testUnsignedIntegerItems() { + var input = '[u1]0x10// hello world !! \n\r \t [u2]65535 \n' + + '[u4]65536 [u8]0xFFFFFFFFFFFFF 0 0Xff'; + var msg = parser.parseTestMessage(input); + var expectedData = new buffer.Buffer(17); + expectedData.setUint8(0, 0x10); + expectedData.setUint16(1, 65535); + expectedData.setUint32(3, 65536); + expectedData.setUint64(7, 0xFFFFFFFFFFFFF); + expectedData.setUint8(15, 0); + expectedData.setUint8(16, 0xff); + checkData(msg.buffer, expectedData, input); + } + + function testSignedIntegerItems() { + var input = '[s8]-0x800 [s1]-128\t[s2]+0 [s4]-40'; + var msg = parser.parseTestMessage(input); + var expectedData = new buffer.Buffer(15); + expectedData.setInt64(0, -0x800); + expectedData.setInt8(8, -128); + expectedData.setInt16(9, 0); + expectedData.setInt32(11, -40); + checkData(msg.buffer, expectedData, input); + } + + function testByteItems() { + var input = '[b]00001011 [b]10000000 // hello world\n [b]00000000'; + var msg = parser.parseTestMessage(input); + var expectedData = new buffer.Buffer(3); + expectedData.setUint8(0, 11); + expectedData.setUint8(1, 128); + expectedData.setUint8(2, 0); + checkData(msg.buffer, expectedData, input); + } + + function testAnchors() { + var input = '[dist4]foo 0 [dist8]bar 0 [anchr]foo [anchr]bar'; + var msg = parser.parseTestMessage(input); + var expectedData = new buffer.Buffer(14); + expectedData.setUint32(0, 14); + expectedData.setUint8(4, 0); + expectedData.setUint64(5, 9); + expectedData.setUint8(13, 0); + checkData(msg.buffer, expectedData, input); + } + + function testHandles() { + var input = '// This message has handles! \n[handles]50 [u8]2'; + var msg = parser.parseTestMessage(input); + var expectedData = new buffer.Buffer(8); + expectedData.setUint64(0, 2); + + if (msg.handleCount != 50) { + var s = 'wrong handle count (' + msg.handleCount + ')'; + throw new TestMessageParserFailure(s, input); + } + checkData(msg.buffer, expectedData, input); + } + + function testEmptyInput() { + var msg = parser.parseTestMessage(''); + if (msg.buffer.byteLength != 0) + throw new TestMessageParserFailure('expected empty message', ''); + } + + function testBlankInput() { + var input = ' \t // hello world \n\r \t// the answer is 42 '; + var msg = parser.parseTestMessage(input); + if (msg.buffer.byteLength != 0) + throw new TestMessageParserFailure('expected empty message', input); + } + + function testInvalidInput() { + function parserShouldFail(input) { + try { + parser.parseTestMessage(input); + } catch (e) { + if (e instanceof parser.InputError) + return; + throw new TestMessageParserFailure( + 'unexpected exception ' + e.toString(), input); + } + throw new TestMessageParserFailure("didn't detect invalid input", file); + } + + ['/ hello world', + '[u1]x', + '[u2]-1000', + '[u1]0x100', + '[s2]-0x8001', + '[b]1', + '[b]1111111k', + '[dist4]unmatched', + '[anchr]hello [dist8]hello', + '[dist4]a [dist4]a [anchr]a', + // '[dist4]a [anchr]a [dist4]a [anchr]a', + '0 [handles]50' + ].forEach(parserShouldFail); + } + + try { + testFloatItems(); + testUnsignedIntegerItems(); + testSignedIntegerItems(); + testByteItems(); + testInvalidInput(); + testEmptyInput(); + testBlankInput(); + testHandles(); + testAnchors(); + } catch (e) { + return e.toString(); + } + return null; + } + + function getMessageTestFiles(prefix) { + var sourceRoot = file.getSourceRootDirectory(); + expect(sourceRoot).not.toBeNull(); + + var testDir = sourceRoot + + "/mojo/public/interfaces/bindings/tests/data/validation/"; + var testFiles = file.getFilesInDirectory(testDir); + expect(testFiles).not.toBeNull(); + expect(testFiles.length).toBeGreaterThan(0); + + // The matching ".data" pathnames with the extension removed. + return testFiles.filter(function(s) { + return s.substr(-5) == ".data" && s.indexOf(prefix) == 0; + }).map(function(s) { + return testDir + s.slice(0, -5); + }); + } + + function readTestMessage(filename) { + var contents = file.readFileToString(filename + ".data"); + expect(contents).not.toBeNull(); + return parser.parseTestMessage(contents); + } + + function readTestExpected(filename) { + var contents = file.readFileToString(filename + ".expected"); + expect(contents).not.toBeNull(); + return contents.trim(); + } + + function checkValidationResult(testFile, err) { + var actualResult = (err === noError) ? "PASS" : err; + var expectedResult = readTestExpected(testFile); + if (actualResult != expectedResult) + console.log("[Test message validation failed: " + testFile + " ]"); + expect(actualResult).toEqual(expectedResult); + } + + function testMessageValidation(prefix, filters) { + var testFiles = getMessageTestFiles(prefix); + expect(testFiles.length).toBeGreaterThan(0); + + for (var i = 0; i < testFiles.length; i++) { + // TODO(hansmuller) Temporarily skipping array pointer overflow tests + // because JS numbers are limited to 53 bits. + // TODO(rudominer): Temporarily skipping 'no-such-method', + // 'invalid_request_flags', and 'invalid_response_flags' until additional + // logic in *RequestValidator and *ResponseValidator is ported from + // cpp to js. + // TODO(crbug/640298): Implement max recursion depth for JS. + // TODO(crbug/628104): Support struct map keys for JS. + if (testFiles[i].indexOf("overflow") != -1 || + testFiles[i].indexOf("conformance_mthd19") != -1 || + testFiles[i].indexOf("conformance_mthd20") != -1 || + testFiles[i].indexOf("no_such_method") != -1 || + testFiles[i].indexOf("invalid_request_flags") != -1 || + testFiles[i].indexOf("invalid_response_flags") != -1) { + console.log("[Skipping " + testFiles[i] + "]"); + continue; + } + + var testMessage = readTestMessage(testFiles[i]); + var handles = new Array(testMessage.handleCount); + var message = new codec.Message(testMessage.buffer, handles); + var messageValidator = new validator.Validator(message); + + var err = messageValidator.validateMessageHeader(); + for (var j = 0; err === noError && j < filters.length; ++j) + err = filters[j](messageValidator); + + checkValidationResult(testFiles[i], err); + } + } + + function testConformanceMessageValidation() { + testMessageValidation("conformance_", [ + testInterface.ConformanceTestInterface.validateRequest]); + } + + function testBoundsCheckMessageValidation() { + testMessageValidation("boundscheck_", [ + testInterface.BoundsCheckTestInterface.validateRequest]); + } + + function testResponseConformanceMessageValidation() { + testMessageValidation("resp_conformance_", [ + testInterface.ConformanceTestInterface.validateResponse]); + } + + function testResponseBoundsCheckMessageValidation() { + testMessageValidation("resp_boundscheck_", [ + testInterface.BoundsCheckTestInterface.validateResponse]); + } + + function testIntegratedMessageValidation(testFilesPattern, endpoint) { + var testFiles = getMessageTestFiles(testFilesPattern); + expect(testFiles.length).toBeGreaterThan(0); + + var testMessagePipe = core.createMessagePipe(); + expect(testMessagePipe.result).toBe(core.RESULT_OK); + + endpoint.bind(testMessagePipe.handle1); + var observer = validator.ValidationErrorObserverForTesting.getInstance(); + + for (var i = 0; i < testFiles.length; i++) { + var testMessage = readTestMessage(testFiles[i]); + var handles = new Array(testMessage.handleCount); + + var writeMessageValue = core.writeMessage( + testMessagePipe.handle0, + new Uint8Array(testMessage.buffer.arrayBuffer), + new Array(testMessage.handleCount), + core.WRITE_MESSAGE_FLAG_NONE); + expect(writeMessageValue).toBe(core.RESULT_OK); + + endpoint.waitForNextMessageForTesting(); + checkValidationResult(testFiles[i], observer.lastError); + observer.reset(); + } + + expect(core.close(testMessagePipe.handle0)).toBe(core.RESULT_OK); + } + + function testIntegratedMessageHeaderValidation() { + testIntegratedMessageValidation( + "integration_msghdr", + new bindings.Binding(testInterface.IntegrationTestInterface, {})); + testIntegratedMessageValidation( + "integration_msghdr", + new testInterface.IntegrationTestInterfacePtr().ptr); + } + + function testIntegratedRequestMessageValidation() { + testIntegratedMessageValidation( + "integration_intf_rqst", + new bindings.Binding(testInterface.IntegrationTestInterface, {})); + } + + function testIntegratedResponseMessageValidation() { + testIntegratedMessageValidation( + "integration_intf_resp", + new testInterface.IntegrationTestInterfacePtr().ptr); + } + + expect(checkTestMessageParser()).toBeNull(); + testConformanceMessageValidation(); + testBoundsCheckMessageValidation(); + testResponseConformanceMessageValidation(); + testResponseBoundsCheckMessageValidation(); + testIntegratedMessageHeaderValidation(); + testIntegratedResponseMessageValidation(); + testIntegratedRequestMessageValidation(); + validator.clearTestingMode(); + + this.result = "PASS"; +}); |