helpers.js 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625
  1. /**
  2. * Any copyright is dedicated to the Public Domain.
  3. * http://creativecommons.org/publicdomain/zero/1.0/
  4. */
  5. var testGenerator = testSteps();
  6. var archiveReaderEnabled = false;
  7. // The test js is shared between xpcshell (which has no SpecialPowers object)
  8. // and content mochitests (where the |Components| object is accessible only as
  9. // SpecialPowers.Components). Expose Components if necessary here to make things
  10. // work everywhere.
  11. //
  12. // Even if the real |Components| doesn't exist, we might shim in a simple JS
  13. // placebo for compat. An easy way to differentiate this from the real thing
  14. // is whether the property is read-only or not.
  15. var c = Object.getOwnPropertyDescriptor(this, 'Components');
  16. if ((!c.value || c.writable) && typeof SpecialPowers === 'object')
  17. Components = SpecialPowers.Components;
  18. function executeSoon(aFun)
  19. {
  20. let comp = SpecialPowers.wrap(Components);
  21. let thread = comp.classes["@mozilla.org/thread-manager;1"]
  22. .getService(comp.interfaces.nsIThreadManager)
  23. .mainThread;
  24. thread.dispatch({
  25. run: function() {
  26. aFun();
  27. }
  28. }, Components.interfaces.nsIThread.DISPATCH_NORMAL);
  29. }
  30. function clearAllDatabases(callback) {
  31. let qms = SpecialPowers.Services.qms;
  32. let principal = SpecialPowers.wrap(document).nodePrincipal;
  33. let request = qms.clearStoragesForPrincipal(principal);
  34. let cb = SpecialPowers.wrapCallback(callback);
  35. request.callback = cb;
  36. }
  37. var testHarnessGenerator = testHarnessSteps();
  38. testHarnessGenerator.next();
  39. function testHarnessSteps() {
  40. function nextTestHarnessStep(val) {
  41. testHarnessGenerator.send(val);
  42. }
  43. let testScriptPath;
  44. let testScriptFilename;
  45. let scripts = document.getElementsByTagName("script");
  46. for (let i = 0; i < scripts.length; i++) {
  47. let src = scripts[i].src;
  48. let match = src.match(/indexedDB\/test\/unit\/(test_[^\/]+\.js)$/);
  49. if (match && match.length == 2) {
  50. testScriptPath = src;
  51. testScriptFilename = match[1];
  52. break;
  53. }
  54. }
  55. yield undefined;
  56. info("Running" +
  57. (testScriptFilename ? " '" + testScriptFilename + "'" : ""));
  58. info("Pushing preferences");
  59. SpecialPowers.pushPrefEnv(
  60. {
  61. "set": [
  62. ["dom.indexedDB.testing", true],
  63. ["dom.indexedDB.experimental", true],
  64. ["dom.archivereader.enabled", true],
  65. ["dom.workers.latestJSVersion", true],
  66. ["javascript.options.wasm", true]
  67. ]
  68. },
  69. nextTestHarnessStep
  70. );
  71. yield undefined;
  72. info("Pushing permissions");
  73. SpecialPowers.pushPermissions(
  74. [
  75. {
  76. type: "indexedDB",
  77. allow: true,
  78. context: document
  79. }
  80. ],
  81. nextTestHarnessStep
  82. );
  83. yield undefined;
  84. info("Clearing old databases");
  85. clearAllDatabases(nextTestHarnessStep);
  86. yield undefined;
  87. if (testScriptFilename && !window.disableWorkerTest) {
  88. info("Running test in a worker");
  89. let workerScriptBlob =
  90. new Blob([ "(" + workerScript.toString() + ")();" ],
  91. { type: "text/javascript;version=1.7" });
  92. let workerScriptURL = URL.createObjectURL(workerScriptBlob);
  93. let worker = new Worker(workerScriptURL);
  94. worker._expectingUncaughtException = false;
  95. worker.onerror = function(event) {
  96. if (worker._expectingUncaughtException) {
  97. ok(true, "Worker had an expected error: " + event.message);
  98. worker._expectingUncaughtException = false;
  99. event.preventDefault();
  100. return;
  101. }
  102. ok(false, "Worker had an error: " + event.message);
  103. worker.terminate();
  104. nextTestHarnessStep();
  105. };
  106. worker.onmessage = function(event) {
  107. let message = event.data;
  108. switch (message.op) {
  109. case "ok":
  110. ok(message.condition, message.name, message.diag);
  111. break;
  112. case "todo":
  113. todo(message.condition, message.name, message.diag);
  114. break;
  115. case "info":
  116. info(message.msg);
  117. break;
  118. case "ready":
  119. worker.postMessage({ op: "load", files: [ testScriptPath ] });
  120. break;
  121. case "loaded":
  122. worker.postMessage({ op: "start", wasmSupported: isWasmSupported() });
  123. break;
  124. case "done":
  125. ok(true, "Worker finished");
  126. nextTestHarnessStep();
  127. break;
  128. case "expectUncaughtException":
  129. worker._expectingUncaughtException = message.expecting;
  130. break;
  131. case "clearAllDatabases":
  132. clearAllDatabases(function(){
  133. worker.postMessage({ op: "clearAllDatabasesDone" });
  134. });
  135. break;
  136. case "getWasmBinary":
  137. worker.postMessage({ op: "getWasmBinaryDone",
  138. wasmBinary: getWasmBinarySync(message.text) });
  139. break;
  140. default:
  141. ok(false,
  142. "Received a bad message from worker: " + JSON.stringify(message));
  143. nextTestHarnessStep();
  144. }
  145. };
  146. URL.revokeObjectURL(workerScriptURL);
  147. yield undefined;
  148. if (worker._expectingUncaughtException) {
  149. ok(false, "expectUncaughtException was called but no uncaught " +
  150. "exception was detected!");
  151. }
  152. worker.terminate();
  153. worker = null;
  154. clearAllDatabases(nextTestHarnessStep);
  155. yield undefined;
  156. } else if (testScriptFilename) {
  157. todo(false,
  158. "Skipping test in a worker because it is explicitly disabled: " +
  159. disableWorkerTest);
  160. } else {
  161. todo(false,
  162. "Skipping test in a worker because it's not structured properly");
  163. }
  164. info("Running test in main thread");
  165. // Now run the test script in the main thread.
  166. testGenerator.next();
  167. yield undefined;
  168. }
  169. if (!window.runTest) {
  170. window.runTest = function()
  171. {
  172. SimpleTest.waitForExplicitFinish();
  173. testHarnessGenerator.next();
  174. }
  175. }
  176. function finishTest()
  177. {
  178. SimpleTest.executeSoon(function() {
  179. testGenerator.close();
  180. testHarnessGenerator.close();
  181. clearAllDatabases(function() { SimpleTest.finish(); });
  182. });
  183. }
  184. function browserRunTest()
  185. {
  186. testGenerator.next();
  187. }
  188. function browserFinishTest()
  189. {
  190. setTimeout(function() { testGenerator.close(); }, 0);
  191. }
  192. function grabEventAndContinueHandler(event)
  193. {
  194. testGenerator.send(event);
  195. }
  196. function continueToNextStep()
  197. {
  198. SimpleTest.executeSoon(function() {
  199. testGenerator.next();
  200. });
  201. }
  202. function continueToNextStepSync()
  203. {
  204. testGenerator.next();
  205. }
  206. function errorHandler(event)
  207. {
  208. ok(false, "indexedDB error, '" + event.target.error.name + "'");
  209. finishTest();
  210. }
  211. // For error callbacks where the argument is not an event object.
  212. function errorCallbackHandler(err)
  213. {
  214. ok(false, "got unexpected error callback: " + err);
  215. finishTest();
  216. }
  217. function expectUncaughtException(expecting)
  218. {
  219. SimpleTest.expectUncaughtException(expecting);
  220. }
  221. function browserErrorHandler(event)
  222. {
  223. browserFinishTest();
  224. throw new Error("indexedDB error (" + event.code + "): " + event.message);
  225. }
  226. function unexpectedSuccessHandler()
  227. {
  228. ok(false, "Got success, but did not expect it!");
  229. finishTest();
  230. }
  231. function expectedErrorHandler(name)
  232. {
  233. return function(event) {
  234. is(event.type, "error", "Got an error event");
  235. is(event.target.error.name, name, "Expected error was thrown.");
  236. event.preventDefault();
  237. grabEventAndContinueHandler(event);
  238. };
  239. }
  240. function ExpectError(name, preventDefault)
  241. {
  242. this._name = name;
  243. this._preventDefault = preventDefault;
  244. }
  245. ExpectError.prototype = {
  246. handleEvent: function(event)
  247. {
  248. is(event.type, "error", "Got an error event");
  249. is(event.target.error.name, this._name, "Expected error was thrown.");
  250. if (this._preventDefault) {
  251. event.preventDefault();
  252. event.stopPropagation();
  253. }
  254. grabEventAndContinueHandler(event);
  255. }
  256. };
  257. function compareKeys(_k1_, _k2_) {
  258. let t = typeof _k1_;
  259. if (t != typeof _k2_)
  260. return false;
  261. if (t !== "object")
  262. return _k1_ === _k2_;
  263. if (_k1_ instanceof Date) {
  264. return (_k2_ instanceof Date) &&
  265. _k1_.getTime() === _k2_.getTime();
  266. }
  267. if (_k1_ instanceof Array) {
  268. if (!(_k2_ instanceof Array) ||
  269. _k1_.length != _k2_.length)
  270. return false;
  271. for (let i = 0; i < _k1_.length; ++i) {
  272. if (!compareKeys(_k1_[i], _k2_[i]))
  273. return false;
  274. }
  275. return true;
  276. }
  277. return false;
  278. }
  279. function removePermission(type, url)
  280. {
  281. if (!url) {
  282. url = window.document;
  283. }
  284. SpecialPowers.removePermission(type, url);
  285. }
  286. function gc()
  287. {
  288. SpecialPowers.forceGC();
  289. SpecialPowers.forceCC();
  290. }
  291. function scheduleGC()
  292. {
  293. SpecialPowers.exactGC(continueToNextStep);
  294. }
  295. function isWasmSupported()
  296. {
  297. let testingFunctions = SpecialPowers.Cu.getJSTestingFunctions();
  298. return testingFunctions.wasmIsSupported();
  299. }
  300. function getWasmBinarySync(text)
  301. {
  302. let testingFunctions = SpecialPowers.Cu.getJSTestingFunctions();
  303. let wasmTextToBinary = SpecialPowers.unwrap(testingFunctions.wasmTextToBinary);
  304. let binary = wasmTextToBinary(text);
  305. return binary;
  306. }
  307. function workerScript() {
  308. "use strict";
  309. self.wasmSupported = false;
  310. self.repr = function(_thing_) {
  311. if (typeof(_thing_) == "undefined") {
  312. return "undefined";
  313. }
  314. let str;
  315. try {
  316. str = _thing_ + "";
  317. } catch (e) {
  318. return "[" + typeof(_thing_) + "]";
  319. }
  320. if (typeof(_thing_) == "function") {
  321. str = str.replace(/^\s+/, "");
  322. let idx = str.indexOf("{");
  323. if (idx != -1) {
  324. str = str.substr(0, idx) + "{...}";
  325. }
  326. }
  327. return str;
  328. };
  329. self.ok = function(_condition_, _name_, _diag_) {
  330. self.postMessage({ op: "ok",
  331. condition: !!_condition_,
  332. name: _name_,
  333. diag: _diag_ });
  334. };
  335. self.is = function(_a_, _b_, _name_) {
  336. let pass = (_a_ == _b_);
  337. let diag = pass ? "" : "got " + repr(_a_) + ", expected " + repr(_b_);
  338. ok(pass, _name_, diag);
  339. };
  340. self.isnot = function(_a_, _b_, _name_) {
  341. let pass = (_a_ != _b_);
  342. let diag = pass ? "" : "didn't expect " + repr(_a_) + ", but got it";
  343. ok(pass, _name_, diag);
  344. };
  345. self.todo = function(_condition_, _name_, _diag_) {
  346. self.postMessage({ op: "todo",
  347. condition: !!_condition_,
  348. name: _name_,
  349. diag: _diag_ });
  350. };
  351. self.info = function(_msg_) {
  352. self.postMessage({ op: "info", msg: _msg_ });
  353. };
  354. self.executeSoon = function(_fun_) {
  355. var channel = new MessageChannel();
  356. channel.port1.postMessage("");
  357. channel.port2.onmessage = function(event) { _fun_(); };
  358. };
  359. self.finishTest = function() {
  360. if (self._expectingUncaughtException) {
  361. self.ok(false, "expectUncaughtException was called but no uncaught "
  362. + "exception was detected!");
  363. }
  364. self.postMessage({ op: "done" });
  365. };
  366. self.grabEventAndContinueHandler = function(_event_) {
  367. testGenerator.send(_event_);
  368. };
  369. self.continueToNextStep = function() {
  370. executeSoon(function() {
  371. testGenerator.next();
  372. });
  373. };
  374. self.continueToNextStepSync = function() {
  375. testGenerator.next();
  376. };
  377. self.errorHandler = function(_event_) {
  378. ok(false, "indexedDB error, '" + _event_.target.error.name + "'");
  379. finishTest();
  380. };
  381. self.unexpectedSuccessHandler = function()
  382. {
  383. ok(false, "Got success, but did not expect it!");
  384. finishTest();
  385. };
  386. self.expectedErrorHandler = function(_name_)
  387. {
  388. return function(_event_) {
  389. is(_event_.type, "error", "Got an error event");
  390. is(_event_.target.error.name, _name_, "Expected error was thrown.");
  391. _event_.preventDefault();
  392. grabEventAndContinueHandler(_event_);
  393. };
  394. };
  395. self.ExpectError = function(_name_, _preventDefault_)
  396. {
  397. this._name = _name_;
  398. this._preventDefault = _preventDefault_;
  399. }
  400. self.ExpectError.prototype = {
  401. handleEvent: function(_event_)
  402. {
  403. is(_event_.type, "error", "Got an error event");
  404. is(_event_.target.error.name, this._name, "Expected error was thrown.");
  405. if (this._preventDefault) {
  406. _event_.preventDefault();
  407. _event_.stopPropagation();
  408. }
  409. grabEventAndContinueHandler(_event_);
  410. }
  411. };
  412. self.compareKeys = function(_k1_, _k2_) {
  413. let t = typeof _k1_;
  414. if (t != typeof _k2_)
  415. return false;
  416. if (t !== "object")
  417. return _k1_ === _k2_;
  418. if (_k1_ instanceof Date) {
  419. return (_k2_ instanceof Date) &&
  420. _k1_.getTime() === _k2_.getTime();
  421. }
  422. if (_k1_ instanceof Array) {
  423. if (!(_k2_ instanceof Array) ||
  424. _k1_.length != _k2_.length)
  425. return false;
  426. for (let i = 0; i < _k1_.length; ++i) {
  427. if (!compareKeys(_k1_[i], _k2_[i]))
  428. return false;
  429. }
  430. return true;
  431. }
  432. return false;
  433. }
  434. self.getRandomBuffer = function(_size_) {
  435. let buffer = new ArrayBuffer(_size_);
  436. is(buffer.byteLength, _size_, "Correct byte length");
  437. let view = new Uint8Array(buffer);
  438. for (let i = 0; i < _size_; i++) {
  439. view[i] = parseInt(Math.random() * 255)
  440. }
  441. return buffer;
  442. };
  443. self._expectingUncaughtException = false;
  444. self.expectUncaughtException = function(_expecting_) {
  445. self._expectingUncaughtException = !!_expecting_;
  446. self.postMessage({ op: "expectUncaughtException", expecting: !!_expecting_ });
  447. };
  448. self._clearAllDatabasesCallback = undefined;
  449. self.clearAllDatabases = function(_callback_) {
  450. self._clearAllDatabasesCallback = _callback_;
  451. self.postMessage({ op: "clearAllDatabases" });
  452. }
  453. self.onerror = function(_message_, _file_, _line_) {
  454. if (self._expectingUncaughtException) {
  455. self._expectingUncaughtException = false;
  456. ok(true, "Worker: expected exception [" + _file_ + ":" + _line_ + "]: '" +
  457. _message_ + "'");
  458. return;
  459. }
  460. ok(false,
  461. "Worker: uncaught exception [" + _file_ + ":" + _line_ + "]: '" +
  462. _message_ + "'");
  463. self.finishTest();
  464. self.close();
  465. return true;
  466. };
  467. self.isWasmSupported = function() {
  468. return self.wasmSupported;
  469. }
  470. self.getWasmBinarySync = function(_text_) {
  471. self.ok(false, "This can't be used on workers");
  472. }
  473. self.getWasmBinary = function(_text_) {
  474. self.postMessage({ op: "getWasmBinary", text: _text_ });
  475. }
  476. self.getWasmModule = function(_binary_) {
  477. let module = new WebAssembly.Module(_binary_);
  478. return module;
  479. }
  480. self.verifyWasmModule = function(_module) {
  481. self.todo(false, "Need a verifyWasmModule implementation on workers");
  482. self.continueToNextStep();
  483. }
  484. self.onmessage = function(_event_) {
  485. let message = _event_.data;
  486. switch (message.op) {
  487. case "load":
  488. info("Worker: loading " + JSON.stringify(message.files));
  489. self.importScripts(message.files);
  490. self.postMessage({ op: "loaded" });
  491. break;
  492. case "start":
  493. self.wasmSupported = message.wasmSupported;
  494. executeSoon(function() {
  495. info("Worker: starting tests");
  496. testGenerator.next();
  497. });
  498. break;
  499. case "clearAllDatabasesDone":
  500. info("Worker: all databases are cleared");
  501. if (self._clearAllDatabasesCallback) {
  502. self._clearAllDatabasesCallback();
  503. }
  504. break;
  505. case "getWasmBinaryDone":
  506. info("Worker: get wasm binary done");
  507. testGenerator.send(message.wasmBinary);
  508. break;
  509. default:
  510. throw new Error("Received a bad message from parent: " +
  511. JSON.stringify(message));
  512. }
  513. };
  514. self.postMessage({ op: "ready" });
  515. }