123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399 |
- // Copyright 2011 The Closure Library Authors. All Rights Reserved.
- //
- // Licensed under the Apache License, Version 2.0 (the "License");
- // you may not use this file except in compliance with the License.
- // You may obtain a copy of the License at
- //
- // http://www.apache.org/licenses/LICENSE-2.0
- //
- // Unless required by applicable law or agreed to in writing, software
- // distributed under the License is distributed on an "AS-IS" BASIS,
- // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- // See the License for the specific language governing permissions and
- // limitations under the License.
- goog.provide('goog.crypt.BlobHasherTest');
- goog.setTestOnly('goog.crypt.BlobHasherTest');
- goog.require('goog.crypt');
- goog.require('goog.crypt.BlobHasher');
- goog.require('goog.crypt.Md5');
- goog.require('goog.events');
- goog.require('goog.testing.PropertyReplacer');
- goog.require('goog.testing.jsunit');
- // A browser-independent mock of goog.fs.sliceBlob. The actual implementation
- // calls the underlying slice method differently based on browser version.
- // This mock does not support negative opt_end.
- var fsSliceBlobMock = function(blob, start, opt_end) {
- if (!goog.isNumber(opt_end)) {
- opt_end = blob.size;
- }
- return blob.slice(start, opt_end);
- };
- // Mock out the Blob using a string.
- BlobMock = function(string) {
- this.data = string;
- this.size = this.data.length;
- };
- BlobMock.prototype.slice = function(start, end) {
- return new BlobMock(this.data.substr(start, end - start));
- };
- // Mock out the FileReader to have control over the flow.
- FileReaderMock = function() {
- this.array_ = [];
- this.result = null;
- this.readyState = this.EMPTY;
- this.onload = null;
- this.onabort = null;
- this.onerror = null;
- };
- FileReaderMock.prototype.EMPTY = 0;
- FileReaderMock.prototype.LOADING = 1;
- FileReaderMock.prototype.DONE = 2;
- FileReaderMock.prototype.mockLoad = function() {
- this.readyState = this.DONE;
- this.result = this.array_;
- if (this.onload) {
- this.onload.call();
- }
- };
- FileReaderMock.prototype.abort = function() {
- this.readyState = this.DONE;
- if (this.onabort) {
- this.onabort.call();
- }
- };
- FileReaderMock.prototype.mockError = function() {
- this.readyState = this.DONE;
- if (this.onerror) {
- this.onerror.call();
- }
- };
- FileReaderMock.prototype.readAsArrayBuffer = function(blobMock) {
- this.readyState = this.LOADING;
- this.array_ = [];
- for (var i = 0; i < blobMock.size; ++i) {
- this.array_[i] = blobMock.data.charCodeAt(i);
- }
- };
- FileReaderMock.prototype.isLoading = function() {
- return this.readyState == this.LOADING;
- };
- var stubs = new goog.testing.PropertyReplacer();
- function setUp() {
- stubs.set(goog.global, 'FileReader', FileReaderMock);
- stubs.set(goog.fs, 'sliceBlob', fsSliceBlobMock);
- }
- function tearDown() {
- stubs.reset();
- }
- /**
- * Makes the blobHasher read chunks from the blob and hash it. The number of
- * reads shall not exceed a pre-determined number (typically blob size / chunk
- * size) for computing hash. This function fails fast (after maxReads is
- * reached), assuming that the hasher failed to generate hashes. This prevents
- * the test suite from going into infinite loop.
- * @param {!goog.crypt.BlobHasher} blobHasher Hasher in action.
- * @param {number} maxReads Max number of read attempts.
- */
- function readFromBlob(blobHasher, maxReads) {
- var counter = 0;
- while (blobHasher.fileReader_ && blobHasher.fileReader_.isLoading() &&
- counter <= maxReads) {
- blobHasher.fileReader_.mockLoad();
- counter++;
- }
- assertTrue(counter <= maxReads);
- return counter;
- }
- function testBasicOperations() {
- if (!window.Blob) {
- return;
- }
- // Test hashing with one chunk.
- var hashFn = new goog.crypt.Md5();
- var blobHasher = new goog.crypt.BlobHasher(hashFn);
- var blob = new BlobMock('The quick brown fox jumps over the lazy dog');
- blobHasher.hash(blob);
- readFromBlob(blobHasher, 1);
- assertEquals(
- '9e107d9d372bb6826bd81d3542a419d6',
- goog.crypt.byteArrayToHex(blobHasher.getHash()));
- // Test hashing with multiple chunks.
- blobHasher = new goog.crypt.BlobHasher(hashFn, 7);
- blobHasher.hash(blob);
- readFromBlob(blobHasher, Math.ceil(blob.size / 7));
- assertEquals(
- '9e107d9d372bb6826bd81d3542a419d6',
- goog.crypt.byteArrayToHex(blobHasher.getHash()));
- // Test hashing with no chunks.
- blob = new BlobMock('');
- blobHasher.hash(blob);
- readFromBlob(blobHasher, 1);
- assertEquals(
- 'd41d8cd98f00b204e9800998ecf8427e',
- goog.crypt.byteArrayToHex(blobHasher.getHash()));
- }
- function testNormalFlow() {
- if (!window.Blob) {
- return;
- }
- // Test the flow with one chunk.
- var hashFn = new goog.crypt.Md5();
- var blobHasher = new goog.crypt.BlobHasher(hashFn, 13);
- var blob = new BlobMock('short');
- var startedEvents = 0;
- var progressEvents = 0;
- var completeEvents = 0;
- goog.events.listen(
- blobHasher, goog.crypt.BlobHasher.EventType.STARTED,
- function() { ++startedEvents; });
- goog.events.listen(
- blobHasher, goog.crypt.BlobHasher.EventType.PROGRESS,
- function() { ++progressEvents; });
- goog.events.listen(
- blobHasher, goog.crypt.BlobHasher.EventType.COMPLETE,
- function() { ++completeEvents; });
- blobHasher.hash(blob);
- assertEquals(1, startedEvents);
- assertEquals(0, progressEvents);
- assertEquals(0, completeEvents);
- readFromBlob(blobHasher, 1);
- assertEquals(1, startedEvents);
- assertEquals(1, progressEvents);
- assertEquals(1, completeEvents);
- // Test the flow with multiple chunks.
- blob = new BlobMock('The quick brown fox jumps over the lazy dog');
- startedEvents = 0;
- progressEvents = 0;
- completeEvents = 0;
- var progressLoops = 0;
- blobHasher.hash(blob);
- assertEquals(1, startedEvents);
- assertEquals(0, progressEvents);
- assertEquals(0, completeEvents);
- progressLoops = readFromBlob(blobHasher, Math.ceil(blob.size / 13));
- assertEquals(1, startedEvents);
- assertEquals(progressLoops, progressEvents);
- assertEquals(1, completeEvents);
- }
- function testAbortsAndErrors() {
- if (!window.Blob) {
- return;
- }
- var hashFn = new goog.crypt.Md5();
- var blobHasher = new goog.crypt.BlobHasher(hashFn, 13);
- var blob = new BlobMock('The quick brown fox jumps over the lazy dog');
- var abortEvents = 0;
- var errorEvents = 0;
- var completeEvents = 0;
- goog.events.listen(
- blobHasher, goog.crypt.BlobHasher.EventType.ABORT,
- function() { ++abortEvents; });
- goog.events.listen(
- blobHasher, goog.crypt.BlobHasher.EventType.ERROR,
- function() { ++errorEvents; });
- goog.events.listen(
- blobHasher, goog.crypt.BlobHasher.EventType.COMPLETE,
- function() { ++completeEvents; });
- // Immediate abort.
- blobHasher.hash(blob);
- assertEquals(0, abortEvents);
- assertEquals(0, errorEvents);
- assertEquals(0, completeEvents);
- blobHasher.abort();
- blobHasher.abort();
- assertEquals(1, abortEvents);
- assertEquals(0, errorEvents);
- assertEquals(0, completeEvents);
- abortEvents = 0;
- // Delayed abort.
- blobHasher.hash(blob);
- blobHasher.fileReader_.mockLoad();
- assertEquals(0, abortEvents);
- assertEquals(0, errorEvents);
- assertEquals(0, completeEvents);
- blobHasher.abort();
- blobHasher.abort();
- assertEquals(1, abortEvents);
- assertEquals(0, errorEvents);
- assertEquals(0, completeEvents);
- abortEvents = 0;
- // Immediate error.
- blobHasher.hash(blob);
- blobHasher.fileReader_.mockError();
- assertEquals(0, abortEvents);
- assertEquals(1, errorEvents);
- assertEquals(0, completeEvents);
- errorEvents = 0;
- // Delayed error.
- blobHasher.hash(blob);
- blobHasher.fileReader_.mockLoad();
- blobHasher.fileReader_.mockError();
- assertEquals(0, abortEvents);
- assertEquals(1, errorEvents);
- assertEquals(0, completeEvents);
- abortEvents = 0;
- }
- function testBasicThrottling() {
- if (!window.Blob) {
- return;
- }
- var hashFn = new goog.crypt.Md5();
- var blobHasher = new goog.crypt.BlobHasher(hashFn, 5);
- var blob = new BlobMock('The quick brown fox jumps over the lazy dog');
- var throttledEvents = 0;
- var completeEvents = 0;
- goog.events.listen(
- blobHasher, goog.crypt.BlobHasher.EventType.THROTTLED,
- function() { ++throttledEvents; });
- goog.events.listen(
- blobHasher, goog.crypt.BlobHasher.EventType.COMPLETE,
- function() { ++completeEvents; });
- // Start a throttled hash. No chunks should be processed yet.
- blobHasher.setHashingLimit(0);
- assertEquals(0, throttledEvents);
- blobHasher.hash(blob);
- assertEquals(1, throttledEvents);
- assertEquals(0, blobHasher.getBytesProcessed());
- assertNull(blobHasher.fileReader_);
- // One chunk should be processed.
- blobHasher.setHashingLimit(4);
- assertEquals(1, throttledEvents);
- assertEquals(1, readFromBlob(blobHasher, 1));
- assertEquals(2, throttledEvents);
- assertEquals(4, blobHasher.getBytesProcessed());
- // One more chunk should be processed.
- blobHasher.setHashingLimit(5);
- assertEquals(2, throttledEvents);
- assertEquals(1, readFromBlob(blobHasher, 1));
- assertEquals(3, throttledEvents);
- assertEquals(5, blobHasher.getBytesProcessed());
- // Two more chunks should be processed.
- blobHasher.setHashingLimit(15);
- assertEquals(3, throttledEvents);
- assertEquals(2, readFromBlob(blobHasher, 2));
- assertEquals(4, throttledEvents);
- assertEquals(15, blobHasher.getBytesProcessed());
- // The entire blob should be processed.
- blobHasher.setHashingLimit(Infinity);
- var expectedChunks = Math.ceil(blob.size / 5) - 3;
- assertEquals(expectedChunks, readFromBlob(blobHasher, expectedChunks));
- assertEquals(4, throttledEvents);
- assertEquals(1, completeEvents);
- assertEquals(
- '9e107d9d372bb6826bd81d3542a419d6',
- goog.crypt.byteArrayToHex(blobHasher.getHash()));
- }
- function testLengthZeroThrottling() {
- if (!window.Blob) {
- return;
- }
- var hashFn = new goog.crypt.Md5();
- var blobHasher = new goog.crypt.BlobHasher(hashFn);
- var throttledEvents = 0;
- var completeEvents = 0;
- goog.events.listen(
- blobHasher, goog.crypt.BlobHasher.EventType.THROTTLED,
- function() { ++throttledEvents; });
- goog.events.listen(
- blobHasher, goog.crypt.BlobHasher.EventType.COMPLETE,
- function() { ++completeEvents; });
- // Test throttling with length 0 blob.
- var blob = new BlobMock('');
- blobHasher.setHashingLimit(0);
- blobHasher.hash(blob);
- assertEquals(0, throttledEvents);
- assertEquals(1, completeEvents);
- assertEquals(
- 'd41d8cd98f00b204e9800998ecf8427e',
- goog.crypt.byteArrayToHex(blobHasher.getHash()));
- }
- function testAbortsAndErrorsWhileThrottling() {
- if (!window.Blob) {
- return;
- }
- var hashFn = new goog.crypt.Md5();
- var blobHasher = new goog.crypt.BlobHasher(hashFn, 5);
- var blob = new BlobMock('The quick brown fox jumps over the lazy dog');
- var abortEvents = 0;
- var errorEvents = 0;
- var throttledEvents = 0;
- var completeEvents = 0;
- goog.events.listen(
- blobHasher, goog.crypt.BlobHasher.EventType.ABORT,
- function() { ++abortEvents; });
- goog.events.listen(
- blobHasher, goog.crypt.BlobHasher.EventType.ERROR,
- function() { ++errorEvents; });
- goog.events.listen(
- blobHasher, goog.crypt.BlobHasher.EventType.THROTTLED,
- function() { ++throttledEvents; });
- goog.events.listen(
- blobHasher, goog.crypt.BlobHasher.EventType.COMPLETE,
- function() { ++completeEvents; });
- // Test that processing cannot be continued after abort.
- blobHasher.setHashingLimit(0);
- blobHasher.hash(blob);
- assertEquals(1, throttledEvents);
- blobHasher.abort();
- assertEquals(1, abortEvents);
- blobHasher.setHashingLimit(10);
- assertNull(blobHasher.fileReader_);
- assertEquals(1, throttledEvents);
- assertEquals(0, completeEvents);
- assertNull(blobHasher.getHash());
- // Test that processing cannot be continued after error.
- blobHasher.hash(blob);
- assertEquals(1, throttledEvents);
- blobHasher.fileReader_.mockError();
- assertEquals(1, errorEvents);
- blobHasher.setHashingLimit(100);
- assertNull(blobHasher.fileReader_);
- assertEquals(1, throttledEvents);
- assertEquals(0, completeEvents);
- assertNull(blobHasher.getHash());
- }
|