blob: 1bd32b881174162f7433818097f9fc862b848ebd [file] [log] [blame]
<!DOCTYPE html>
<script src="../resources/testharness.js"></script>
<script src="../resources/testharnessreport.js"></script>
<script src="file:///gen/layout_test_data/mojo/public/js/mojo_bindings.js"></script>
<script src="file:///gen/mojo/public/interfaces/bindings/tests/validation_test_interfaces.mojom.js"></script>
<script src="resources/validation_test_input_parser.js"></script>
<script>
'use strict';
var noError = mojo.internal.validationError.NONE;
function checkData(data, expectedData, input) {
assert_equals(data.byteLength, expectedData.byteLength,
'message length (' + data.byteLength + ') doesn\'t match ' +
'expected length: ' + expectedData.byteLength + ' for ' + input);
for (var i = 0; i < data.byteLength; i++) {
assert_equals(data.getUint8(i), expectedData.getUint8(i),
'message data mismatch at byte offset ' + i + 'for' + input);
}
}
test(() => {
var input = '[f]+.3e9 [d]-10.03';
var msg = mojo.test.parseTestMessage(input);
var expectedData = new mojo.internal.Buffer(12);
expectedData.setFloat32(0, +.3e9);
expectedData.setFloat64(4, -10.03);
checkData(msg.buffer, expectedData, input);
}, 'message parser: float items');
test(() => {
var input = '[u1]0x10// hello world !! \n\r \t [u2]65535 \n' +
'[u4]65536 [u8]0xFFFFFFFFFFFFF 0 0Xff';
var msg = mojo.test.parseTestMessage(input);
var expectedData = new mojo.internal.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);
}, 'message parser: unsigned integer items');
test(() => {
var input = '[s8]-0x800 [s1]-128\t[s2]+0 [s4]-40';
var msg = mojo.test.parseTestMessage(input);
var expectedData = new mojo.internal.Buffer(15);
expectedData.setInt64(0, -0x800);
expectedData.setInt8(8, -128);
expectedData.setInt16(9, 0);
expectedData.setInt32(11, -40);
checkData(msg.buffer, expectedData, input);
}, 'message parser: signed integer items');
test(() => {
var input = '[b]00001011 [b]10000000 // hello world\n [b]00000000';
var msg = mojo.test.parseTestMessage(input);
var expectedData = new mojo.internal.Buffer(3);
expectedData.setUint8(0, 11);
expectedData.setUint8(1, 128);
expectedData.setUint8(2, 0);
checkData(msg.buffer, expectedData, input);
}, 'message parser: byte items');
test(() => {
var input = '[dist4]foo 0 [dist8]bar 0 [anchr]foo [anchr]bar';
var msg = mojo.test.parseTestMessage(input);
var expectedData = new mojo.internal.Buffer(14);
expectedData.setUint32(0, 14);
expectedData.setUint8(4, 0);
expectedData.setUint64(5, 9);
expectedData.setUint8(13, 0);
checkData(msg.buffer, expectedData, input);
}, 'message parser: anchors');
test(() => {
var input = '// This message has handles! \n[handles]50 [u8]2';
var msg = mojo.test.parseTestMessage(input);
var expectedData = new mojo.internal.Buffer(8);
expectedData.setUint64(0, 2);
assert_equals(msg.handleCount, 50,
'wrong handle count (' + msg.handleConut + ') for ' + input);
checkData(msg.buffer, expectedData, input);
}, 'message parser: handles');
test(() => {
var msg = mojo.test.parseTestMessage('');
assert_equals(msg.buffer.byteLength, 0, 'expected empty message for ');
}, 'message parser: empty input');
test(() => {
var input = ' \t // hello world \n\r \t// the answer is 42 ';
var msg = mojo.test.parseTestMessage(input);
assert_equals(msg.buffer.byteLength, 0,
'expected empty message for ' + input);
}, 'message parser: blank input');
test(() => {
function parserShouldFail(input) {
assert_throws(new mojo.test.InputError(), function() {
mojo.test.parseTestMessage(input);
});
}
['/ 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);
}, 'message parser: invalid input');
function fetchLite(url) {
return new Promise((resolve, reject) => {
var xhr = new XMLHttpRequest();
xhr.open('GET', url);
xhr.onreadystatechange = () => {
if (xhr.readyState != 4) return;
resolve(xhr.responseText);
};
xhr.send();
});
}
function getMessageTestFiles(prefix) {
let dataDirectory =
'file:///gen/layout_test_data/mojo/public/interfaces/bindings/tests/data/validation';
return fetchLite(dataDirectory + '_index.txt').then((response) => {
assert_not_equals(response, null);
var testFiles = response.split(/\s+/);
assert_greater_than(testFiles.length, 0);
return testFiles.filter(function(s) {
return s.substr(-5) == '.data' && s.indexOf(prefix) == 0;
}).map(function(s) {
return dataDirectory + '/' + s.slice(0, -5);
});
});
}
function readTestMessage(filename) {
return fetchLite(filename + '.data').then((response) => {
assert_not_equals(response, null);
return mojo.test.parseTestMessage(response);
});
}
function readTestExpected(filename) {
return fetchLite(filename + '.expected').then((response) => {
assert_not_equals(response, null);
return response.trim();
});
}
async function checkValidationResult(testFile, err) {
var actualResult = (err === noError) ? 'PASS' : err;
var expectedResult = await readTestExpected(testFile);
assert_equals(actualResult, expectedResult,
'[Test message validation failed: ' + testFile + ' ]');
}
async function testMessageValidation(prefix, filters) {
var testFiles = await getMessageTestFiles(prefix);
assert_greater_than(testFiles.length, 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 = await readTestMessage(testFiles[i]);
var handles = new Array(testMessage.handleCount);
var message = new mojo.internal.Message(testMessage.buffer, handles);
var messageValidator = new mojo.internal.Validator(message);
var err = messageValidator.validateMessageHeader();
for (var j = 0; err === noError && j < filters.length; ++j)
err = filters[j](messageValidator);
await checkValidationResult(testFiles[i], err);
}
}
promise_test(() => {
return testMessageValidation('conformance_', [
mojo.test.ConformanceTestInterface.validateRequest]);
}, 'conformance message validation');
promise_test(() => {
return testMessageValidation('boundscheck_', [
mojo.test.BoundsCheckTestInterface.validateRequest]);
}, 'bounds check message validation');
promise_test(() => {
return testMessageValidation('resp_conformance_', [
mojo.test.ConformanceTestInterface.validateResponse]);
}, 'response conformance message validation');
promise_test(() => {
return testMessageValidation('resp_boundscheck_', [
mojo.test.BoundsCheckTestInterface.validateResponse]);
}, 'response bounds check message validation');
async function testIntegratedMessageValidation(testFilesPattern, endpoint) {
var testFiles = await getMessageTestFiles(testFilesPattern);
assert_greater_than(testFiles.length, 0);
var testMessagePipe = Mojo.createMessagePipe();
assert_equals(testMessagePipe.result, Mojo.RESULT_OK);
endpoint.bind(testMessagePipe.handle1);
if (endpoint instanceof mojo.InterfacePtrController) {
// Make sure the router and connector are initialized.
endpoint.getProxy();
}
var originalReceiver = endpoint.router_.connector_.incomingReceiver_.accept;
var nextMessageCallback = null;
endpoint.router_.connector_.incomingReceiver_.accept = function(message) {
var result = originalReceiver(message);
if (nextMessageCallback) {
setTimeout(() => {
nextMessageCallback();
nextMessageCallback = null;
}, 0);
}
return result;
}
var observer = mojo.internal.ValidationErrorObserverForTesting.getInstance();
for (var i = 0; i < testFiles.length; i++) {
var testMessage = await readTestMessage(testFiles[i]);
var handles = new Array(testMessage.handleCount);
await new Promise((resolve, reject) => {
nextMessageCallback = resolve;
var writeMessageValue = testMessagePipe.handle0.writeMessage(
new Uint8Array(testMessage.buffer.arrayBuffer),
new Array(testMessage.handleCount));
assert_equals(writeMessageValue, Mojo.RESULT_OK);
});
await checkValidationResult(testFiles[i], observer.lastError);
observer.reset();
}
testMessagePipe.handle0.close();
}
promise_test(() => {
return testIntegratedMessageValidation(
'integration_msghdr',
new mojo.Binding(mojo.test.IntegrationTestInterface, {}))
.then(() => {
return testIntegratedMessageValidation(
'integration_msghdr',
new mojo.test.IntegrationTestInterfacePtr().ptr);
});
}, 'integrated message header validation');
promise_test(() => {
return testIntegratedMessageValidation(
'integration_intf_rqst',
new mojo.Binding(mojo.test.IntegrationTestInterface, {}));
}, 'integrated request message validation');
promise_test(() => {
return testIntegratedMessageValidation(
'integration_intf_resp',
new mojo.test.IntegrationTestInterfacePtr().ptr);
}, 'integrated response message validation');
</script>