blobhasher_test.js 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399
  1. // Copyright 2011 The Closure Library Authors. All Rights Reserved.
  2. //
  3. // Licensed under the Apache License, Version 2.0 (the "License");
  4. // you may not use this file except in compliance with the License.
  5. // You may obtain a copy of the License at
  6. //
  7. // http://www.apache.org/licenses/LICENSE-2.0
  8. //
  9. // Unless required by applicable law or agreed to in writing, software
  10. // distributed under the License is distributed on an "AS-IS" BASIS,
  11. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. // See the License for the specific language governing permissions and
  13. // limitations under the License.
  14. goog.provide('goog.crypt.BlobHasherTest');
  15. goog.setTestOnly('goog.crypt.BlobHasherTest');
  16. goog.require('goog.crypt');
  17. goog.require('goog.crypt.BlobHasher');
  18. goog.require('goog.crypt.Md5');
  19. goog.require('goog.events');
  20. goog.require('goog.testing.PropertyReplacer');
  21. goog.require('goog.testing.jsunit');
  22. // A browser-independent mock of goog.fs.sliceBlob. The actual implementation
  23. // calls the underlying slice method differently based on browser version.
  24. // This mock does not support negative opt_end.
  25. var fsSliceBlobMock = function(blob, start, opt_end) {
  26. if (!goog.isNumber(opt_end)) {
  27. opt_end = blob.size;
  28. }
  29. return blob.slice(start, opt_end);
  30. };
  31. // Mock out the Blob using a string.
  32. BlobMock = function(string) {
  33. this.data = string;
  34. this.size = this.data.length;
  35. };
  36. BlobMock.prototype.slice = function(start, end) {
  37. return new BlobMock(this.data.substr(start, end - start));
  38. };
  39. // Mock out the FileReader to have control over the flow.
  40. FileReaderMock = function() {
  41. this.array_ = [];
  42. this.result = null;
  43. this.readyState = this.EMPTY;
  44. this.onload = null;
  45. this.onabort = null;
  46. this.onerror = null;
  47. };
  48. FileReaderMock.prototype.EMPTY = 0;
  49. FileReaderMock.prototype.LOADING = 1;
  50. FileReaderMock.prototype.DONE = 2;
  51. FileReaderMock.prototype.mockLoad = function() {
  52. this.readyState = this.DONE;
  53. this.result = this.array_;
  54. if (this.onload) {
  55. this.onload.call();
  56. }
  57. };
  58. FileReaderMock.prototype.abort = function() {
  59. this.readyState = this.DONE;
  60. if (this.onabort) {
  61. this.onabort.call();
  62. }
  63. };
  64. FileReaderMock.prototype.mockError = function() {
  65. this.readyState = this.DONE;
  66. if (this.onerror) {
  67. this.onerror.call();
  68. }
  69. };
  70. FileReaderMock.prototype.readAsArrayBuffer = function(blobMock) {
  71. this.readyState = this.LOADING;
  72. this.array_ = [];
  73. for (var i = 0; i < blobMock.size; ++i) {
  74. this.array_[i] = blobMock.data.charCodeAt(i);
  75. }
  76. };
  77. FileReaderMock.prototype.isLoading = function() {
  78. return this.readyState == this.LOADING;
  79. };
  80. var stubs = new goog.testing.PropertyReplacer();
  81. function setUp() {
  82. stubs.set(goog.global, 'FileReader', FileReaderMock);
  83. stubs.set(goog.fs, 'sliceBlob', fsSliceBlobMock);
  84. }
  85. function tearDown() {
  86. stubs.reset();
  87. }
  88. /**
  89. * Makes the blobHasher read chunks from the blob and hash it. The number of
  90. * reads shall not exceed a pre-determined number (typically blob size / chunk
  91. * size) for computing hash. This function fails fast (after maxReads is
  92. * reached), assuming that the hasher failed to generate hashes. This prevents
  93. * the test suite from going into infinite loop.
  94. * @param {!goog.crypt.BlobHasher} blobHasher Hasher in action.
  95. * @param {number} maxReads Max number of read attempts.
  96. */
  97. function readFromBlob(blobHasher, maxReads) {
  98. var counter = 0;
  99. while (blobHasher.fileReader_ && blobHasher.fileReader_.isLoading() &&
  100. counter <= maxReads) {
  101. blobHasher.fileReader_.mockLoad();
  102. counter++;
  103. }
  104. assertTrue(counter <= maxReads);
  105. return counter;
  106. }
  107. function testBasicOperations() {
  108. if (!window.Blob) {
  109. return;
  110. }
  111. // Test hashing with one chunk.
  112. var hashFn = new goog.crypt.Md5();
  113. var blobHasher = new goog.crypt.BlobHasher(hashFn);
  114. var blob = new BlobMock('The quick brown fox jumps over the lazy dog');
  115. blobHasher.hash(blob);
  116. readFromBlob(blobHasher, 1);
  117. assertEquals(
  118. '9e107d9d372bb6826bd81d3542a419d6',
  119. goog.crypt.byteArrayToHex(blobHasher.getHash()));
  120. // Test hashing with multiple chunks.
  121. blobHasher = new goog.crypt.BlobHasher(hashFn, 7);
  122. blobHasher.hash(blob);
  123. readFromBlob(blobHasher, Math.ceil(blob.size / 7));
  124. assertEquals(
  125. '9e107d9d372bb6826bd81d3542a419d6',
  126. goog.crypt.byteArrayToHex(blobHasher.getHash()));
  127. // Test hashing with no chunks.
  128. blob = new BlobMock('');
  129. blobHasher.hash(blob);
  130. readFromBlob(blobHasher, 1);
  131. assertEquals(
  132. 'd41d8cd98f00b204e9800998ecf8427e',
  133. goog.crypt.byteArrayToHex(blobHasher.getHash()));
  134. }
  135. function testNormalFlow() {
  136. if (!window.Blob) {
  137. return;
  138. }
  139. // Test the flow with one chunk.
  140. var hashFn = new goog.crypt.Md5();
  141. var blobHasher = new goog.crypt.BlobHasher(hashFn, 13);
  142. var blob = new BlobMock('short');
  143. var startedEvents = 0;
  144. var progressEvents = 0;
  145. var completeEvents = 0;
  146. goog.events.listen(
  147. blobHasher, goog.crypt.BlobHasher.EventType.STARTED,
  148. function() { ++startedEvents; });
  149. goog.events.listen(
  150. blobHasher, goog.crypt.BlobHasher.EventType.PROGRESS,
  151. function() { ++progressEvents; });
  152. goog.events.listen(
  153. blobHasher, goog.crypt.BlobHasher.EventType.COMPLETE,
  154. function() { ++completeEvents; });
  155. blobHasher.hash(blob);
  156. assertEquals(1, startedEvents);
  157. assertEquals(0, progressEvents);
  158. assertEquals(0, completeEvents);
  159. readFromBlob(blobHasher, 1);
  160. assertEquals(1, startedEvents);
  161. assertEquals(1, progressEvents);
  162. assertEquals(1, completeEvents);
  163. // Test the flow with multiple chunks.
  164. blob = new BlobMock('The quick brown fox jumps over the lazy dog');
  165. startedEvents = 0;
  166. progressEvents = 0;
  167. completeEvents = 0;
  168. var progressLoops = 0;
  169. blobHasher.hash(blob);
  170. assertEquals(1, startedEvents);
  171. assertEquals(0, progressEvents);
  172. assertEquals(0, completeEvents);
  173. progressLoops = readFromBlob(blobHasher, Math.ceil(blob.size / 13));
  174. assertEquals(1, startedEvents);
  175. assertEquals(progressLoops, progressEvents);
  176. assertEquals(1, completeEvents);
  177. }
  178. function testAbortsAndErrors() {
  179. if (!window.Blob) {
  180. return;
  181. }
  182. var hashFn = new goog.crypt.Md5();
  183. var blobHasher = new goog.crypt.BlobHasher(hashFn, 13);
  184. var blob = new BlobMock('The quick brown fox jumps over the lazy dog');
  185. var abortEvents = 0;
  186. var errorEvents = 0;
  187. var completeEvents = 0;
  188. goog.events.listen(
  189. blobHasher, goog.crypt.BlobHasher.EventType.ABORT,
  190. function() { ++abortEvents; });
  191. goog.events.listen(
  192. blobHasher, goog.crypt.BlobHasher.EventType.ERROR,
  193. function() { ++errorEvents; });
  194. goog.events.listen(
  195. blobHasher, goog.crypt.BlobHasher.EventType.COMPLETE,
  196. function() { ++completeEvents; });
  197. // Immediate abort.
  198. blobHasher.hash(blob);
  199. assertEquals(0, abortEvents);
  200. assertEquals(0, errorEvents);
  201. assertEquals(0, completeEvents);
  202. blobHasher.abort();
  203. blobHasher.abort();
  204. assertEquals(1, abortEvents);
  205. assertEquals(0, errorEvents);
  206. assertEquals(0, completeEvents);
  207. abortEvents = 0;
  208. // Delayed abort.
  209. blobHasher.hash(blob);
  210. blobHasher.fileReader_.mockLoad();
  211. assertEquals(0, abortEvents);
  212. assertEquals(0, errorEvents);
  213. assertEquals(0, completeEvents);
  214. blobHasher.abort();
  215. blobHasher.abort();
  216. assertEquals(1, abortEvents);
  217. assertEquals(0, errorEvents);
  218. assertEquals(0, completeEvents);
  219. abortEvents = 0;
  220. // Immediate error.
  221. blobHasher.hash(blob);
  222. blobHasher.fileReader_.mockError();
  223. assertEquals(0, abortEvents);
  224. assertEquals(1, errorEvents);
  225. assertEquals(0, completeEvents);
  226. errorEvents = 0;
  227. // Delayed error.
  228. blobHasher.hash(blob);
  229. blobHasher.fileReader_.mockLoad();
  230. blobHasher.fileReader_.mockError();
  231. assertEquals(0, abortEvents);
  232. assertEquals(1, errorEvents);
  233. assertEquals(0, completeEvents);
  234. abortEvents = 0;
  235. }
  236. function testBasicThrottling() {
  237. if (!window.Blob) {
  238. return;
  239. }
  240. var hashFn = new goog.crypt.Md5();
  241. var blobHasher = new goog.crypt.BlobHasher(hashFn, 5);
  242. var blob = new BlobMock('The quick brown fox jumps over the lazy dog');
  243. var throttledEvents = 0;
  244. var completeEvents = 0;
  245. goog.events.listen(
  246. blobHasher, goog.crypt.BlobHasher.EventType.THROTTLED,
  247. function() { ++throttledEvents; });
  248. goog.events.listen(
  249. blobHasher, goog.crypt.BlobHasher.EventType.COMPLETE,
  250. function() { ++completeEvents; });
  251. // Start a throttled hash. No chunks should be processed yet.
  252. blobHasher.setHashingLimit(0);
  253. assertEquals(0, throttledEvents);
  254. blobHasher.hash(blob);
  255. assertEquals(1, throttledEvents);
  256. assertEquals(0, blobHasher.getBytesProcessed());
  257. assertNull(blobHasher.fileReader_);
  258. // One chunk should be processed.
  259. blobHasher.setHashingLimit(4);
  260. assertEquals(1, throttledEvents);
  261. assertEquals(1, readFromBlob(blobHasher, 1));
  262. assertEquals(2, throttledEvents);
  263. assertEquals(4, blobHasher.getBytesProcessed());
  264. // One more chunk should be processed.
  265. blobHasher.setHashingLimit(5);
  266. assertEquals(2, throttledEvents);
  267. assertEquals(1, readFromBlob(blobHasher, 1));
  268. assertEquals(3, throttledEvents);
  269. assertEquals(5, blobHasher.getBytesProcessed());
  270. // Two more chunks should be processed.
  271. blobHasher.setHashingLimit(15);
  272. assertEquals(3, throttledEvents);
  273. assertEquals(2, readFromBlob(blobHasher, 2));
  274. assertEquals(4, throttledEvents);
  275. assertEquals(15, blobHasher.getBytesProcessed());
  276. // The entire blob should be processed.
  277. blobHasher.setHashingLimit(Infinity);
  278. var expectedChunks = Math.ceil(blob.size / 5) - 3;
  279. assertEquals(expectedChunks, readFromBlob(blobHasher, expectedChunks));
  280. assertEquals(4, throttledEvents);
  281. assertEquals(1, completeEvents);
  282. assertEquals(
  283. '9e107d9d372bb6826bd81d3542a419d6',
  284. goog.crypt.byteArrayToHex(blobHasher.getHash()));
  285. }
  286. function testLengthZeroThrottling() {
  287. if (!window.Blob) {
  288. return;
  289. }
  290. var hashFn = new goog.crypt.Md5();
  291. var blobHasher = new goog.crypt.BlobHasher(hashFn);
  292. var throttledEvents = 0;
  293. var completeEvents = 0;
  294. goog.events.listen(
  295. blobHasher, goog.crypt.BlobHasher.EventType.THROTTLED,
  296. function() { ++throttledEvents; });
  297. goog.events.listen(
  298. blobHasher, goog.crypt.BlobHasher.EventType.COMPLETE,
  299. function() { ++completeEvents; });
  300. // Test throttling with length 0 blob.
  301. var blob = new BlobMock('');
  302. blobHasher.setHashingLimit(0);
  303. blobHasher.hash(blob);
  304. assertEquals(0, throttledEvents);
  305. assertEquals(1, completeEvents);
  306. assertEquals(
  307. 'd41d8cd98f00b204e9800998ecf8427e',
  308. goog.crypt.byteArrayToHex(blobHasher.getHash()));
  309. }
  310. function testAbortsAndErrorsWhileThrottling() {
  311. if (!window.Blob) {
  312. return;
  313. }
  314. var hashFn = new goog.crypt.Md5();
  315. var blobHasher = new goog.crypt.BlobHasher(hashFn, 5);
  316. var blob = new BlobMock('The quick brown fox jumps over the lazy dog');
  317. var abortEvents = 0;
  318. var errorEvents = 0;
  319. var throttledEvents = 0;
  320. var completeEvents = 0;
  321. goog.events.listen(
  322. blobHasher, goog.crypt.BlobHasher.EventType.ABORT,
  323. function() { ++abortEvents; });
  324. goog.events.listen(
  325. blobHasher, goog.crypt.BlobHasher.EventType.ERROR,
  326. function() { ++errorEvents; });
  327. goog.events.listen(
  328. blobHasher, goog.crypt.BlobHasher.EventType.THROTTLED,
  329. function() { ++throttledEvents; });
  330. goog.events.listen(
  331. blobHasher, goog.crypt.BlobHasher.EventType.COMPLETE,
  332. function() { ++completeEvents; });
  333. // Test that processing cannot be continued after abort.
  334. blobHasher.setHashingLimit(0);
  335. blobHasher.hash(blob);
  336. assertEquals(1, throttledEvents);
  337. blobHasher.abort();
  338. assertEquals(1, abortEvents);
  339. blobHasher.setHashingLimit(10);
  340. assertNull(blobHasher.fileReader_);
  341. assertEquals(1, throttledEvents);
  342. assertEquals(0, completeEvents);
  343. assertNull(blobHasher.getHash());
  344. // Test that processing cannot be continued after error.
  345. blobHasher.hash(blob);
  346. assertEquals(1, throttledEvents);
  347. blobHasher.fileReader_.mockError();
  348. assertEquals(1, errorEvents);
  349. blobHasher.setHashingLimit(100);
  350. assertNull(blobHasher.fileReader_);
  351. assertEquals(1, throttledEvents);
  352. assertEquals(0, completeEvents);
  353. assertNull(blobHasher.getHash());
  354. }