summaryrefslogtreecommitdiff
path: root/mojo/public/js/tests/validation_unittest.js
diff options
context:
space:
mode:
Diffstat (limited to 'mojo/public/js/tests/validation_unittest.js')
-rw-r--r--mojo/public/js/tests/validation_unittest.js334
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";
+});