| // Copyright 2015 The Vanadium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style |
| // license that can be found in the LICENSE file. |
| |
| /** |
| * @fileoverview Tests for TypeEncoder and TypeDecoder. |
| */ |
| |
| var test = require('prova'); |
| |
| var stringify = require('./../../src/vdl/stringify.js'); |
| var types = require('./../../src/vdl/types.js'); |
| var kind = require('./../../src/vdl/kind.js'); |
| var Promise = require('./../../src/lib/promise'); |
| |
| var TypeEncoder = require('./../../src/vom/type-encoder.js'); |
| var TypeDecoder = require('./../../src/vom/type-decoder.js'); |
| |
| var ByteArrayMessageWriter = require( |
| './../../src/vom/byte-array-message-writer.js'); |
| var RawVomReader = require('./../../src/vom/raw-vom-reader.js'); |
| |
| /** |
| * Type message reader reads type messages from the provided data. |
| * @param {Uint8Array} bytes The input data. |
| * @constructor |
| */ |
| function TypeMessageReader(bytes) { |
| var header = bytes[0]; |
| this.rawReader = new RawVomReader(bytes); |
| // consume the header byte. |
| this.rawReader._readRawBytes(1); |
| if (header !== 0x80) { |
| throw new Error('Improperly formatted bytes. Must start with 0x80'); |
| } |
| } |
| |
| /** |
| * Read the next type message. |
| */ |
| TypeMessageReader.prototype.nextMessage = function(typeDecoder) { |
| var reader = this; |
| return this.rawReader.readInt().then(function(typeId) { |
| if (typeId >= 0) { |
| throw new Error('Value messages not implemented.'); |
| } |
| return reader.rawReader.readUint().then(function(len) { |
| return reader.rawReader._readRawBytes(len); |
| }).then(function(bytes) { |
| return { |
| typeId: -typeId, |
| messageBytes: bytes, |
| }; |
| }); |
| }, function(err) { |
| return null; |
| }); |
| }; |
| |
| test('type encoding encode and decode (optional fields filled)', function(t) { |
| var tests = require('../vdl/type-test-cases.js'); |
| var promises = []; |
| for (var i = 0; i < tests.length; i++) { |
| promises.push(encodeDecodeType(t, tests[i].type)); |
| } |
| Promise.all(promises).then(function() { |
| t.end(); |
| }, t.end); |
| }); |
| |
| test('type encoding encode and decode (optional fields omitted)', |
| function(t) { |
| var tests = [ |
| { |
| test: { |
| kind: kind.OPTIONAL, |
| elem: types.UINT64 |
| }, |
| expected: { |
| name: '', |
| kind: kind.OPTIONAL, |
| elem: types.UINT64 |
| }, |
| }, |
| { |
| test: { |
| kind: kind.LIST, |
| elem: { |
| kind: kind.UINT16, |
| name: 'namedUint16' |
| } |
| }, |
| expected: { |
| kind: kind.LIST, |
| name: '', |
| elem: { |
| kind: kind.UINT16, |
| name: 'namedUint16' |
| } |
| } |
| }, |
| { |
| test: { |
| kind: kind.UNION, |
| name: 'unionName', |
| fields: [ |
| { |
| name: 'A', |
| type: types.INT16 |
| }, |
| { |
| name: 'B', |
| type: { |
| kind: kind.SET, |
| key: types.BOOL |
| } |
| } |
| ] |
| }, |
| expected: { |
| kind: kind.UNION, |
| name: 'unionName', |
| fields: [ |
| { |
| name: 'A', |
| type: types.INT16 |
| }, |
| { |
| name: 'B', |
| type: { |
| name: '', |
| kind: kind.SET, |
| key: types.BOOL |
| } |
| } |
| ] |
| }, |
| }, |
| { |
| test: { |
| kind: kind.MAP, |
| key: { |
| kind: kind.INT16, |
| name: 'namedInt16' |
| }, |
| elem: { |
| kind: kind.INT32, |
| name: 'namedInt32' |
| } |
| }, |
| expected: { |
| kind: kind.MAP, |
| name: '', |
| key: { |
| kind: kind.INT16, |
| name: 'namedInt16' |
| }, |
| elem: { |
| kind: kind.INT32, |
| name: 'namedInt32' |
| } |
| }, |
| } |
| ]; |
| |
| var promises = []; |
| for (var i = 0; i < tests.length; i++) { |
| promises.push(encodeDecodeType(t, tests[i].test, tests[i].expected)); |
| } |
| Promise.all(promises).then(function() { |
| t.end(); |
| }, t.end); |
| }); |
| |
| var UPPER_LOOP_LIMIT = 100; |
| function encodeDecodeType(t, test, expected) { |
| // If the expected result is not given, use the test value instead. |
| expected = expected || test; |
| |
| var writer = new ByteArrayMessageWriter(); |
| var typeEncoder = new TypeEncoder(writer); |
| var id = typeEncoder.encodeType(test); |
| |
| var typeDecoder = new TypeDecoder(); |
| var reader = new TypeMessageReader(writer.getBytes()); |
| var j = 1; |
| return readMessage(); |
| function readMessage() { |
| return reader.nextMessage().then(function(message) { |
| if (message === null) { |
| return typeDecoder.lookupType(id).then(function(resultType) { |
| var resultStr = stringify(resultType); |
| var expectedStr = stringify(expected); |
| return t.equals(resultStr, expectedStr); |
| }); |
| } |
| if (j === UPPER_LOOP_LIMIT) { |
| return t.fail('read too many messages'); |
| } |
| j++; |
| return typeDecoder.defineType(message.typeId, message.messageBytes). |
| then(readMessage); |
| }); |
| } |
| } |
| |
| // This tests a subset of potential type encoding errors. |
| test('type encoding encode errors', function(t) { |
| var badTypes = { |
| 'no type': undefined, |
| 'no kind': {}, |
| 'invalid kind': { |
| kind: 'non-integer' |
| }, |
| 'unknown kind': { |
| kind: -1 |
| }, |
| 'list w/ bad elem': { |
| kind: kind.LIST, |
| name: 'testList', |
| elem: true |
| }, |
| 'array w/ bad len': { |
| kind: kind.ARRAY, |
| name: 'testArray', |
| elem: types.UINT64, |
| len: -1 |
| }, |
| 'set w/ labels': { |
| kind: kind.SET, |
| key: types.ANY, |
| labels: ['labels', 'are', 'for', 'enums', 'only'] |
| }, |
| 'enum w/ non-string labels': { |
| kind: kind.ENUM, |
| labels: ['do not', 'put a number in', 'the enum labels', 3] |
| }, |
| 'union w/o fields': { |
| kind: kind.UNION, |
| fields: [] |
| } |
| }; |
| |
| for (var testName in badTypes) { |
| if (badTypes.hasOwnProperty(testName)) { |
| var test = badTypes[testName]; |
| |
| var writer = new ByteArrayMessageWriter(); |
| var typeEncoder = new TypeEncoder(writer); |
| t.throws( |
| typeEncoder.encodeType.bind(typeEncoder, test), |
| testName |
| ); |
| } |
| } |
| t.end(); |
| }); |