| Index: third_party/WebKit/LayoutTests/mojo/validation.html | 
| diff --git a/third_party/WebKit/LayoutTests/mojo/validation.html b/third_party/WebKit/LayoutTests/mojo/validation.html | 
| new file mode 100644 | 
| index 0000000000000000000000000000000000000000..03fab5f839fada0c81e5eb7a62f732bf6f8ce50c | 
| --- /dev/null | 
| +++ b/third_party/WebKit/LayoutTests/mojo/validation.html | 
| @@ -0,0 +1,303 @@ | 
| +<!DOCTYPE html> | 
| +<script src="../resources/testharness.js"></script> | 
| +<script src="../resources/testharnessreport.js"></script> | 
| +<script src="../resources/mojo-helpers.js"></script> | 
| +<script src="resources/validation_test_input_parser.js"></script> | 
| +<script> | 
| +'use strict'; | 
| + | 
| +setup({ explicit_done: true }); | 
| + | 
| +define([ | 
| +    'mojo/public/js/bindings', | 
| +    'mojo/public/js/buffer', | 
| +    'mojo/public/js/codec', | 
| +    'mojo/public/js/core', | 
| +    'mojo/resources/validation_test_input_parser', | 
| +    'mojo/public/interfaces/bindings/tests/validation_test_interfaces.mojom', | 
| +    'mojo/public/js/validator', | 
| +], function(bindings, | 
| +            buffer, | 
| +            codec, | 
| +            core, | 
| +            parser, | 
| +            testInterface, | 
| +            validator) { | 
| + | 
| +  var noError = validator.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 = parser.parseTestMessage(input); | 
| +    var expectedData = new buffer.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 = 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); | 
| +  }, 'message parser: unsigned integer items'); | 
| + | 
| +  test(() => { | 
| +    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); | 
| +  }, 'message parser: signed integer items'); | 
| + | 
| +  test(() => { | 
| +    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); | 
| +  }, 'message parser: byte items'); | 
| + | 
| +  test(() => { | 
| +    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); | 
| +  }, 'message parser: anchors'); | 
| + | 
| +  test(() => { | 
| +    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); | 
| + | 
| +    assert_equals(msg.handleCount, 50, | 
| +        'wrong handle count (' + msg.handleConut + ') for ' + input); | 
| +    checkData(msg.buffer, expectedData, input); | 
| +  }, 'message parser: handles'); | 
| + | 
| +  test(() => { | 
| +    var msg = parser.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 = parser.parseTestMessage(input); | 
| +    assert_equals(msg.buffer.byteLength, 0, | 
| +        'expected empty message for ' + input); | 
| +  }, 'message parser: blank input'); | 
| + | 
| +  test(() => { | 
| +    function parserShouldFail(input) { | 
| +      assert_throws(new parser.InputError(), function() { | 
| +        parser.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 genDirectory = '/gen/mojo/public/interfaces/bindings/tests/'; | 
| +    return fetchLite(genDirectory + 'validation_files.txt').then((response) => { | 
| +      assert_not_equals(response, null); | 
| +      var testFiles = response.split('\n'); | 
| +      assert_greater_than(testFiles.length, 0); | 
| +      return testFiles.filter(function(s) { | 
| +        return s.substr(-5) == '.data' && s.indexOf(prefix) == 0; | 
| +      }).map(function(s) { | 
| +        return genDirectory + 'data/validation/' + s.slice(0, -5); | 
| +      }); | 
| +    }); | 
| +  } | 
| + | 
| +  function readTestMessage(filename) { | 
| +    return fetchLite(filename + '.data').then((response) => { | 
| +      assert_not_equals(response, null); | 
| +      return parser.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 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); | 
| + | 
| +      await checkValidationResult(testFiles[i], err); | 
| +    } | 
| +  } | 
| + | 
| +  promise_test(() => { | 
| +    return testMessageValidation('conformance_', [ | 
| +        testInterface.ConformanceTestInterface.validateRequest]); | 
| +  }, 'conformance message validation'); | 
| + | 
| +  promise_test(() => { | 
| +    return testMessageValidation('boundscheck_', [ | 
| +        testInterface.BoundsCheckTestInterface.validateRequest]); | 
| +  }, 'bounds check message validation'); | 
| + | 
| +  promise_test(() => { | 
| +    return testMessageValidation('resp_conformance_', [ | 
| +        testInterface.ConformanceTestInterface.validateResponse]); | 
| +  }, 'response conformance message validation'); | 
| + | 
| +  promise_test(() => { | 
| +    return testMessageValidation('resp_boundscheck_', [ | 
| +        testInterface.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 = core.createMessagePipe(); | 
| +    assert_equals(testMessagePipe.result, core.RESULT_OK); | 
| + | 
| +    endpoint.bind(testMessagePipe.handle1); | 
| +    var testingController = endpoint.enableTestingMode(); | 
| + | 
| +    var validationError; | 
| +    testingController.setInvalidIncomingMessageHandler(function(error) { | 
| +      validationError = error; | 
| +    }); | 
| + | 
| +    for (var i = 0; i < testFiles.length; i++) { | 
| +      validationError = noError; | 
| +      var testMessage = await 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); | 
| +      assert_equals(writeMessageValue, core.RESULT_OK); | 
| + | 
| +      testingController.waitForNextMessage(); | 
| +      await checkValidationResult(testFiles[i], validationError); | 
| +    } | 
| + | 
| +    assert_equals(core.close(testMessagePipe.handle0), core.RESULT_OK); | 
| +  } | 
| + | 
| +  promise_test(() => { | 
| +    return testIntegratedMessageValidation( | 
| +        'integration_msghdr', | 
| +        new bindings.Binding(testInterface.IntegrationTestInterface, {})) | 
| +      .then(() => { | 
| +        return testIntegratedMessageValidation( | 
| +            'integration_msghdr', | 
| +            new testInterface.IntegrationTestInterfacePtr().ptr); | 
| +      }); | 
| +  }, 'integrated message header validation'); | 
| + | 
| +  promise_test(() => { | 
| +    return testIntegratedMessageValidation( | 
| +        'integration_intf_rqst', | 
| +        new bindings.Binding(testInterface.IntegrationTestInterface, {})); | 
| +  }, 'integrated request message validation'); | 
| + | 
| +  promise_test(() => { | 
| +    return testIntegratedMessageValidation( | 
| +        'integration_intf_resp', | 
| +        new testInterface.IntegrationTestInterfacePtr().ptr); | 
| +  }, 'integrated response message validation'); | 
| + | 
| +  done(); | 
| +}); | 
| + | 
| +</script> | 
|  |