1/** 2 * @fileoverview Tests for reader.js. 3 */ 4goog.module('protobuf.binary.ReaderTest'); 5 6goog.setTestOnly(); 7 8// Note to the reader: 9// Since the reader behavior changes with the checking level some of the 10// tests in this file have to know which checking level is enable to make 11// correct assertions. 12const BufferDecoder = goog.require('protobuf.binary.BufferDecoder'); 13const ByteString = goog.require('protobuf.ByteString'); 14const reader = goog.require('protobuf.binary.reader'); 15const {CHECK_CRITICAL_STATE} = goog.require('protobuf.internal.checks'); 16const {createBufferDecoder} = goog.require('protobuf.binary.bufferDecoderHelper'); 17const {encode} = goog.require('protobuf.binary.textencoding'); 18const {getBoolPairs} = goog.require('protobuf.binary.boolTestPairs'); 19const {getDoublePairs} = goog.require('protobuf.binary.doubleTestPairs'); 20const {getFixed32Pairs} = goog.require('protobuf.binary.fixed32TestPairs'); 21const {getFloatPairs} = goog.require('protobuf.binary.floatTestPairs'); 22const {getInt32Pairs} = goog.require('protobuf.binary.int32TestPairs'); 23const {getInt64Pairs} = goog.require('protobuf.binary.int64TestPairs'); 24const {getPackedBoolPairs} = goog.require('protobuf.binary.packedBoolTestPairs'); 25const {getPackedDoublePairs} = goog.require('protobuf.binary.packedDoubleTestPairs'); 26const {getPackedFixed32Pairs} = goog.require('protobuf.binary.packedFixed32TestPairs'); 27const {getPackedFloatPairs} = goog.require('protobuf.binary.packedFloatTestPairs'); 28const {getPackedInt32Pairs} = goog.require('protobuf.binary.packedInt32TestPairs'); 29const {getPackedInt64Pairs} = goog.require('protobuf.binary.packedInt64TestPairs'); 30const {getPackedSfixed32Pairs} = goog.require('protobuf.binary.packedSfixed32TestPairs'); 31const {getPackedSfixed64Pairs} = goog.require('protobuf.binary.packedSfixed64TestPairs'); 32const {getPackedSint32Pairs} = goog.require('protobuf.binary.packedSint32TestPairs'); 33const {getPackedSint64Pairs} = goog.require('protobuf.binary.packedSint64TestPairs'); 34const {getPackedUint32Pairs} = goog.require('protobuf.binary.packedUint32TestPairs'); 35const {getSfixed32Pairs} = goog.require('protobuf.binary.sfixed32TestPairs'); 36const {getSfixed64Pairs} = goog.require('protobuf.binary.sfixed64TestPairs'); 37const {getSint32Pairs} = goog.require('protobuf.binary.sint32TestPairs'); 38const {getSint64Pairs} = goog.require('protobuf.binary.sint64TestPairs'); 39const {getUint32Pairs} = goog.require('protobuf.binary.uint32TestPairs'); 40 41/****************************************************************************** 42 * Optional FUNCTIONS 43 ******************************************************************************/ 44 45describe('Read bool does', () => { 46 for (const pair of getBoolPairs()) { 47 it(`decode ${pair.name}`, () => { 48 if (pair.error && CHECK_CRITICAL_STATE) { 49 expect(() => reader.readBool(pair.bufferDecoder, 0)).toThrow(); 50 } else { 51 const d = reader.readBool( 52 pair.bufferDecoder, pair.bufferDecoder.startIndex()); 53 expect(d).toEqual(pair.boolValue); 54 } 55 }); 56 } 57}); 58 59describe('readBytes does', () => { 60 it('throw exception if data is too short', () => { 61 const bufferDecoder = createBufferDecoder(); 62 expect(() => reader.readBytes(bufferDecoder, 0)).toThrow(); 63 }); 64 65 it('read bytes by index', () => { 66 const bufferDecoder = createBufferDecoder(3, 1, 2, 3); 67 const byteString = reader.readBytes(bufferDecoder, 0); 68 expect(ByteString.fromArrayBuffer(new Uint8Array([1, 2, 3]).buffer)) 69 .toEqual(byteString); 70 }); 71}); 72 73describe('readDouble does', () => { 74 it('throw exception if data is too short', () => { 75 const bufferDecoder = createBufferDecoder(); 76 expect(() => reader.readDouble(bufferDecoder, 0)).toThrow(); 77 }); 78 79 for (const pair of getDoublePairs()) { 80 it(`decode ${pair.name}`, () => { 81 const d = reader.readDouble(pair.bufferDecoder, 0); 82 expect(d).toEqual(pair.doubleValue); 83 }); 84 } 85}); 86 87describe('readFixed32 does', () => { 88 it('throw exception if data is too short', () => { 89 const bufferDecoder = createBufferDecoder(); 90 expect(() => reader.readFixed32(bufferDecoder, 0)).toThrow(); 91 }); 92 93 for (const pair of getFixed32Pairs()) { 94 it(`decode ${pair.name}`, () => { 95 const d = reader.readFixed32(pair.bufferDecoder, 0); 96 expect(d).toEqual(pair.intValue); 97 }); 98 } 99}); 100 101describe('readFloat does', () => { 102 it('throw exception if data is too short', () => { 103 const bufferDecoder = createBufferDecoder(); 104 expect(() => reader.readFloat(bufferDecoder, 0)).toThrow(); 105 }); 106 107 for (const pair of getFloatPairs()) { 108 it(`decode ${pair.name}`, () => { 109 const d = reader.readFloat(pair.bufferDecoder, 0); 110 expect(d).toEqual(Math.fround(pair.floatValue)); 111 }); 112 } 113}); 114 115describe('readInt32 does', () => { 116 it('throw exception if data is too short', () => { 117 const bufferDecoder = createBufferDecoder(0x80); 118 expect(() => reader.readInt32(bufferDecoder, 0)).toThrow(); 119 }); 120 121 for (const pair of getInt32Pairs()) { 122 it(`decode ${pair.name}`, () => { 123 if (pair.error && CHECK_CRITICAL_STATE) { 124 expect(() => reader.readInt32(pair.bufferDecoder, 0)).toThrow(); 125 } else { 126 const d = reader.readInt32(pair.bufferDecoder, 0); 127 expect(d).toEqual(pair.intValue); 128 } 129 }); 130 } 131}); 132 133describe('readSfixed32 does', () => { 134 it('throw exception if data is too short', () => { 135 const bufferDecoder = createBufferDecoder(0x80); 136 expect(() => reader.readSfixed32(bufferDecoder, 0)).toThrow(); 137 }); 138 139 for (const pair of getSfixed32Pairs()) { 140 it(`decode ${pair.name}`, () => { 141 const d = reader.readSfixed32(pair.bufferDecoder, 0); 142 expect(d).toEqual(pair.intValue); 143 }); 144 } 145}); 146 147describe('readSfixed64 does', () => { 148 it('throw exception if data is too short', () => { 149 const bufferDecoder = createBufferDecoder(0x80); 150 expect(() => reader.readSfixed64(bufferDecoder, 0)).toThrow(); 151 }); 152 153 for (const pair of getSfixed64Pairs()) { 154 it(`decode ${pair.name}`, () => { 155 const d = reader.readSfixed64(pair.bufferDecoder, 0); 156 expect(d).toEqual(pair.longValue); 157 }); 158 } 159}); 160 161describe('readSint32 does', () => { 162 it('throw exception if data is too short', () => { 163 const bufferDecoder = createBufferDecoder(0x80); 164 expect(() => reader.readSint32(bufferDecoder, 0)).toThrow(); 165 }); 166 167 for (const pair of getSint32Pairs()) { 168 it(`decode ${pair.name}`, () => { 169 if (pair.error && CHECK_CRITICAL_STATE) { 170 expect(() => reader.readSint32(pair.bufferDecoder, 0)).toThrow(); 171 } else { 172 const d = reader.readSint32(pair.bufferDecoder, 0); 173 expect(d).toEqual(pair.intValue); 174 } 175 }); 176 } 177}); 178 179describe('readInt64 does', () => { 180 it('throw exception if data is too short', () => { 181 const bufferDecoder = createBufferDecoder(0x80); 182 expect(() => reader.readInt64(bufferDecoder, 0)).toThrow(); 183 }); 184 185 for (const pair of getInt64Pairs()) { 186 it(`decode ${pair.name}`, () => { 187 if (pair.error && CHECK_CRITICAL_STATE) { 188 expect(() => reader.readInt64(pair.bufferDecoder, 0)).toThrow(); 189 } else { 190 const d = reader.readInt64(pair.bufferDecoder, 0); 191 expect(d).toEqual(pair.longValue); 192 } 193 }); 194 } 195}); 196 197describe('readSint64 does', () => { 198 it('throw exception if data is too short', () => { 199 const bufferDecoder = createBufferDecoder(0x80); 200 expect(() => reader.readSint64(bufferDecoder, 0)).toThrow(); 201 }); 202 203 for (const pair of getSint64Pairs()) { 204 it(`decode ${pair.name}`, () => { 205 if (pair.error && CHECK_CRITICAL_STATE) { 206 expect(() => reader.readSint64(pair.bufferDecoder, 0)).toThrow(); 207 } else { 208 const d = reader.readSint64(pair.bufferDecoder, 0); 209 expect(d).toEqual(pair.longValue); 210 } 211 }); 212 } 213}); 214 215describe('readUint32 does', () => { 216 it('throw exception if data is too short', () => { 217 const bufferDecoder = createBufferDecoder(0x80); 218 expect(() => reader.readUint32(bufferDecoder, 0)).toThrow(); 219 }); 220 221 for (const pair of getUint32Pairs()) { 222 if (!pair.skip_reader) { 223 it(`decode ${pair.name}`, () => { 224 if (pair.error && CHECK_CRITICAL_STATE) { 225 expect(() => reader.readUint32(pair.bufferDecoder, 0)).toThrow(); 226 } else { 227 const d = reader.readUint32(pair.bufferDecoder, 0); 228 expect(d).toEqual(pair.intValue); 229 } 230 }); 231 } 232 } 233}); 234 235/** 236 * 237 * @param {string} s 238 * @return {!Uint8Array} 239 */ 240function encodeString(s) { 241 if (typeof TextEncoder !== 'undefined') { 242 const textEncoder = new TextEncoder('utf-8'); 243 return textEncoder.encode(s); 244 } else { 245 return encode(s); 246 } 247} 248 249/** @param {string} s */ 250function expectEncodedStringToMatch(s) { 251 const array = encodeString(s); 252 const length = array.length; 253 if (length > 127) { 254 throw new Error('Test only works for strings shorter than 128'); 255 } 256 const encodedArray = new Uint8Array(length + 1); 257 encodedArray[0] = length; 258 encodedArray.set(array, 1); 259 const bufferDecoder = BufferDecoder.fromArrayBuffer(encodedArray.buffer); 260 expect(reader.readString(bufferDecoder, 0)).toEqual(s); 261} 262 263describe('readString does', () => { 264 it('return empty string for zero length string', () => { 265 const s = reader.readString(createBufferDecoder(0x00), 0); 266 expect(s).toEqual(''); 267 }); 268 269 it('decode random strings', () => { 270 // 1 byte strings 271 expectEncodedStringToMatch('hello'); 272 expectEncodedStringToMatch('HELLO1!'); 273 274 // 2 byte String 275 expectEncodedStringToMatch('©'); 276 277 // 3 byte string 278 expectEncodedStringToMatch('❄'); 279 280 // 4 byte string 281 expectEncodedStringToMatch(''); 282 }); 283 284 it('decode 1 byte strings', () => { 285 for (let i = 0; i < 0x80; i++) { 286 const s = String.fromCharCode(i); 287 expectEncodedStringToMatch(s); 288 } 289 }); 290 291 it('decode 2 byte strings', () => { 292 for (let i = 0xC0; i < 0x7FF; i++) { 293 const s = String.fromCharCode(i); 294 expectEncodedStringToMatch(s); 295 } 296 }); 297 298 it('decode 3 byte strings', () => { 299 for (let i = 0x7FF; i < 0x8FFF; i++) { 300 const s = String.fromCharCode(i); 301 expectEncodedStringToMatch(s); 302 } 303 }); 304 305 it('throw exception on invalid bytes', () => { 306 // This test will only succeed with the native TextDecoder since 307 // our polyfill does not do any validation. IE10 and IE11 don't support 308 // TextDecoder. 309 // TODO: Remove this check once we no longer need to support IE 310 if (typeof TextDecoder !== 'undefined') { 311 expect( 312 () => reader.readString( 313 createBufferDecoder(0x01, /* invalid utf data point*/ 0xFF), 0)) 314 .toThrow(); 315 } 316 }); 317 318 it('throw exception if data is too short', () => { 319 const array = createBufferDecoder(0x02, '?'.charCodeAt(0)); 320 expect(() => reader.readString(array, 0)).toThrow(); 321 }); 322}); 323 324/****************************************************************************** 325 * REPEATED FUNCTIONS 326 ******************************************************************************/ 327 328describe('readPackedBool does', () => { 329 for (const pair of getPackedBoolPairs()) { 330 it(`decode ${pair.name}`, () => { 331 const d = reader.readPackedBool(pair.bufferDecoder, 0); 332 expect(d).toEqual(pair.boolValues); 333 }); 334 } 335}); 336 337describe('readPackedDouble does', () => { 338 for (const pair of getPackedDoublePairs()) { 339 it(`decode ${pair.name}`, () => { 340 const d = reader.readPackedDouble(pair.bufferDecoder, 0); 341 expect(d).toEqual(pair.doubleValues); 342 }); 343 } 344}); 345 346describe('readPackedFixed32 does', () => { 347 for (const pair of getPackedFixed32Pairs()) { 348 it(`decode ${pair.name}`, () => { 349 const d = reader.readPackedFixed32(pair.bufferDecoder, 0); 350 expect(d).toEqual(pair.fixed32Values); 351 }); 352 } 353}); 354 355describe('readPackedFloat does', () => { 356 for (const pair of getPackedFloatPairs()) { 357 it(`decode ${pair.name}`, () => { 358 const d = reader.readPackedFloat(pair.bufferDecoder, 0); 359 expect(d).toEqual(pair.floatValues); 360 }); 361 } 362}); 363 364describe('readPackedInt32 does', () => { 365 for (const pair of getPackedInt32Pairs()) { 366 it(`decode ${pair.name}`, () => { 367 const d = reader.readPackedInt32(pair.bufferDecoder, 0); 368 expect(d).toEqual(pair.int32Values); 369 }); 370 } 371}); 372 373describe('readPackedInt64 does', () => { 374 for (const pair of getPackedInt64Pairs()) { 375 it(`decode ${pair.name}`, () => { 376 const d = reader.readPackedInt64(pair.bufferDecoder, 0); 377 expect(d).toEqual(pair.int64Values); 378 }); 379 } 380}); 381 382describe('readPackedSfixed32 does', () => { 383 for (const pair of getPackedSfixed32Pairs()) { 384 it(`decode ${pair.name}`, () => { 385 const d = reader.readPackedSfixed32(pair.bufferDecoder, 0); 386 expect(d).toEqual(pair.sfixed32Values); 387 }); 388 } 389}); 390 391describe('readPackedSfixed64 does', () => { 392 for (const pair of getPackedSfixed64Pairs()) { 393 it(`decode ${pair.name}`, () => { 394 const d = reader.readPackedSfixed64(pair.bufferDecoder, 0); 395 expect(d).toEqual(pair.sfixed64Values); 396 }); 397 } 398}); 399 400describe('readPackedSint32 does', () => { 401 for (const pair of getPackedSint32Pairs()) { 402 it(`decode ${pair.name}`, () => { 403 const d = reader.readPackedSint32(pair.bufferDecoder, 0); 404 expect(d).toEqual(pair.sint32Values); 405 }); 406 } 407}); 408 409describe('readPackedSint64 does', () => { 410 for (const pair of getPackedSint64Pairs()) { 411 it(`decode ${pair.name}`, () => { 412 const d = reader.readPackedSint64(pair.bufferDecoder, 0); 413 expect(d).toEqual(pair.sint64Values); 414 }); 415 } 416}); 417 418describe('readPackedUint32 does', () => { 419 for (const pair of getPackedUint32Pairs()) { 420 it(`decode ${pair.name}`, () => { 421 const d = reader.readPackedUint32(pair.bufferDecoder, 0); 422 expect(d).toEqual(pair.uint32Values); 423 }); 424 } 425}); 426