gnunet-svn
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[taler-wallet-core] 02/02: more tests, fix event ordering issue


From: gnunet
Subject: [taler-wallet-core] 02/02: more tests, fix event ordering issue
Date: Mon, 22 Feb 2021 14:28:03 +0100

This is an automated email from the git hooks/post-receive script.

dold pushed a commit to branch master
in repository wallet-core.

commit 3eced74a88de43ab9afe542fcce20a8db8e3fe60
Author: Florian Dold <florian@dold.me>
AuthorDate: Mon Feb 22 14:27:54 2021 +0100

    more tests, fix event ordering issue
---
 packages/idb-bridge/src/bridge-idb.ts              | 44 ++++++++++++++-
 .../event-dispatch-active-flag.test.ts             | 57 +++++++++++++++++++
 .../idbobjectstore-add-put-exception-order.test.ts |  1 -
 .../idbtransaction-oncomplete.test.ts              | 49 +++++++++++++++++
 .../idb-bridge/src/idb-wpt-ported/wptsupport.ts    | 64 +++++++++++++++++++++-
 packages/idb-bridge/src/util/queueTask.ts          | 15 ++++-
 6 files changed, 224 insertions(+), 6 deletions(-)

diff --git a/packages/idb-bridge/src/bridge-idb.ts 
b/packages/idb-bridge/src/bridge-idb.ts
index 643a98de..ceba618d 100644
--- a/packages/idb-bridge/src/bridge-idb.ts
+++ b/packages/idb-bridge/src/bridge-idb.ts
@@ -1609,6 +1609,10 @@ export class BridgeIDBObjectStore implements 
IDBObjectStore {
       throw new TypeError();
     }
 
+    if (!this._transaction._active) {
+      throw new TransactionInactiveError();
+    }
+
     if (this._deleted) {
       throw new InvalidStateError(
         "tried to call 'delete' on a deleted object store",
@@ -1918,6 +1922,8 @@ export class BridgeIDBRequest extends FakeEventTarget 
implements IDBRequest {
   onsuccess: EventListener | null = null;
   onerror: EventListener | null = null;
 
+  _debugName: string | undefined;
+
   get error() {
     if (this.readyState === "pending") {
       throw new InvalidStateError();
@@ -1998,6 +2004,25 @@ export class BridgeIDBOpenDBRequest
   }
 }
 
+function waitMacroQueue(): Promise<void> {
+  return new Promise<void>((resolve, reject) => {
+    let immCalled = false;
+    let timeoutCalled = false;
+    setImmediate(() => {
+      immCalled = true;
+      if (immCalled && timeoutCalled) {
+        resolve();
+      }
+    });
+    setTimeout(() => {
+      timeoutCalled = true;
+      if (immCalled && timeoutCalled) {
+        resolve();
+      }
+    }, 0);
+  });
+}
+
 // http://www.w3.org/TR/2015/REC-IndexedDB-20150108/#transaction
 /** @public */
 export class BridgeIDBTransaction
@@ -2182,7 +2207,7 @@ export class BridgeIDBTransaction
   // http://w3c.github.io/IndexedDB/#dom-idbtransaction-objectstore
   public objectStore(name: string): BridgeIDBObjectStore {
     if (!this._active) {
-      throw new InvalidStateError();
+      throw new TransactionInactiveError();
     }
 
     if (!this._db._schema.objectStores[name]) {
@@ -2279,6 +2304,8 @@ export class BridgeIDBTransaction
         }
       }
 
+      await waitMacroQueue();
+
       if (!request._source) {
         // Special requests like indexes that just need to run some code,
         // with error handling already built into operation
@@ -2289,9 +2316,12 @@ export class BridgeIDBTransaction
           BridgeIDBFactory.enableTracing &&
             console.log("TRACE: running operation in transaction");
           const result = await operation();
+          // Wait until setTimeout/setImmediate tasks are run
           BridgeIDBFactory.enableTracing &&
             console.log(
-              "TRACE: operation in transaction finished with success",
+              `TRACE: request (${
+                request._debugName ?? "??"
+              }) in transaction finished with success`,
             );
           request.readyState = "done";
           request.result = result;
@@ -2304,6 +2334,10 @@ export class BridgeIDBTransaction
             cancelable: false,
           });
 
+          queueTask(() => {
+            this._active = false;
+          });
+
           try {
             event.eventPath = [this._db, this];
             request.dispatchEvent(event);
@@ -2372,7 +2406,11 @@ export class BridgeIDBTransaction
       this._committed = true;
       if (!this._error) {
         if (BridgeIDBFactory.enableTracing) {
-          console.log("dispatching 'complete' event on transaction");
+          console.log(
+            `dispatching 'complete' event on transaction (${
+              this._debugName ?? "??"
+            })`,
+          );
         }
         const event = new FakeEvent("complete");
         event.eventPath = [this._db, this];
diff --git 
a/packages/idb-bridge/src/idb-wpt-ported/event-dispatch-active-flag.test.ts 
b/packages/idb-bridge/src/idb-wpt-ported/event-dispatch-active-flag.test.ts
new file mode 100644
index 00000000..f5668c90
--- /dev/null
+++ b/packages/idb-bridge/src/idb-wpt-ported/event-dispatch-active-flag.test.ts
@@ -0,0 +1,57 @@
+import test from "ava";
+import { BridgeIDBRequest } from "..";
+import {
+  createdb,
+  indexeddb_test,
+  is_transaction_active,
+  keep_alive,
+} from "./wptsupport";
+
+test("WPT test abort-in-initial-upgradeneeded.htm", async (t) => {
+  // Transactions are active during success handlers
+  await indexeddb_test(
+    t,
+    (done, db, tx) => {
+      db.createObjectStore("store");
+    },
+    (done, db) => {
+      const tx = db.transaction("store");
+      const release_tx = keep_alive(t, tx, "store");
+
+      t.assert(
+        is_transaction_active(t, tx, "store"),
+        "Transaction should be active after creation",
+      );
+
+      const request = tx.objectStore("store").get(4242);
+      (request as BridgeIDBRequest)._debugName = "req-main"; 
+      request.onerror = () => t.fail("request should succeed");
+      request.onsuccess = () => {
+
+        t.true(
+          is_transaction_active(t, tx, "store"),
+          "Transaction should be active during success handler",
+        );
+
+        let saw_handler_promise = false;
+        Promise.resolve().then(() => {
+          saw_handler_promise = true;
+          t.true(
+            is_transaction_active(t, tx, "store"),
+            "Transaction should be active in handler's microtasks",
+          );
+        });
+
+        setTimeout(() => {
+          t.true(saw_handler_promise);
+          t.false(
+            is_transaction_active(t, tx, "store"),
+            "Transaction should be inactive in next task",
+          );
+          release_tx();
+          done();
+        }, 0);
+      };
+    },
+  );
+});
diff --git 
a/packages/idb-bridge/src/idb-wpt-ported/idbobjectstore-add-put-exception-order.test.ts
 
b/packages/idb-bridge/src/idb-wpt-ported/idbobjectstore-add-put-exception-order.test.ts
index 77c4a939..a3aead9d 100644
--- 
a/packages/idb-bridge/src/idb-wpt-ported/idbobjectstore-add-put-exception-order.test.ts
+++ 
b/packages/idb-bridge/src/idb-wpt-ported/idbobjectstore-add-put-exception-order.test.ts
@@ -40,7 +40,6 @@ async function t2(t: ExecutionContext, method: string): 
Promise<void> {
       const store = db.createObjectStore("s");
     },
     (done, db) => {
-      (db as any)._debugName = method;
       const tx = db.transaction("s", "readonly");
       const store = tx.objectStore("s");
 
diff --git 
a/packages/idb-bridge/src/idb-wpt-ported/idbtransaction-oncomplete.test.ts 
b/packages/idb-bridge/src/idb-wpt-ported/idbtransaction-oncomplete.test.ts
new file mode 100644
index 00000000..8e0b4387
--- /dev/null
+++ b/packages/idb-bridge/src/idb-wpt-ported/idbtransaction-oncomplete.test.ts
@@ -0,0 +1,49 @@
+import test from "ava";
+import { createdb } from "./wptsupport";
+
+// IDBTransaction - complete event
+test("WPT idbtransaction-oncomplete.htm", async (t) => {
+  await new Promise<void>((resolve, reject) => {
+    var db: any;
+    var store: any;
+    let open_rq = createdb(t);
+    let stages: any[] = [];
+
+    open_rq.onupgradeneeded = function (e: any) {
+      stages.push("upgradeneeded");
+
+      db = e.target.result;
+      store = db.createObjectStore("store");
+
+      e.target.transaction.oncomplete = function () {
+        stages.push("complete");
+      };
+    };
+
+    open_rq.onsuccess = function (e) {
+      stages.push("success");
+
+      // Making a totally new transaction to check
+      db
+        .transaction("store")
+        .objectStore("store")
+        .count().onsuccess = function (e: any) {
+        t.deepEqual(stages, ["upgradeneeded", "complete", "success"]);
+        resolve();
+      };
+      // XXX: Make one with real transactions, not only open() versionchange 
one
+
+      /*db.transaction.objectStore('store').openCursor().onsuccess = 
function(e) {
+          stages.push("opencursor1");
+      }
+      store.openCursor().onsuccess = function(e) {
+          stages.push("opencursor2");
+      }
+      e.target.transaction.objectStore('store').openCursor().onsuccess = 
function(e) {
+          stages.push("opencursor3");
+      }
+      */
+    };
+  });
+  t.pass();
+});
diff --git a/packages/idb-bridge/src/idb-wpt-ported/wptsupport.ts 
b/packages/idb-bridge/src/idb-wpt-ported/wptsupport.ts
index 6777dc12..9ec46c76 100644
--- a/packages/idb-bridge/src/idb-wpt-ported/wptsupport.ts
+++ b/packages/idb-bridge/src/idb-wpt-ported/wptsupport.ts
@@ -1,5 +1,5 @@
 import test, { ExecutionContext } from "ava";
-import { BridgeIDBFactory } from "..";
+import { BridgeIDBFactory, BridgeIDBRequest } from "..";
 import {
   IDBDatabase,
   IDBIndex,
@@ -480,3 +480,65 @@ export function indexeddb_test(
     }
   });
 }
+
+/**
+ * Keeps the passed transaction alive indefinitely (by making requests
+ * against the named store). Returns a function that asserts that the
+ * transaction has not already completed and then ends the request loop so that
+ * the transaction may autocommit and complete.
+ */
+export function keep_alive(
+  t: ExecutionContext,
+  tx: IDBTransaction,
+  store_name: string,
+) {
+  let completed = false;
+  tx.addEventListener("complete", () => {
+    completed = true;
+  });
+
+  let keepSpinning = true;
+  let spinCount = 0;
+
+  function spin() {
+    console.log("spinning");
+    if (!keepSpinning) return;
+    const request = tx.objectStore(store_name).get(0);
+    (request as BridgeIDBRequest)._debugName = `req-spin-${spinCount}`;
+    spinCount++;
+    request.onsuccess = spin;
+  }
+  spin();
+
+  return () => {
+    t.log("stopping spin");
+    t.false(completed, "Transaction completed while kept alive");
+    keepSpinning = false;
+  };
+}
+
+// Checks to see if the passed transaction is active (by making
+// requests against the named store).
+export function is_transaction_active(
+  t: ExecutionContext,
+  tx: IDBTransaction,
+  store_name: string,
+) {
+  try {
+    const request = tx.objectStore(store_name).get(0);
+    request.onerror = (e) => {
+      e.preventDefault();
+      e.stopPropagation();
+    };
+    return true;
+  } catch (ex) {
+    console.log(ex.stack);
+    t.deepEqual(
+      ex.name,
+      "TransactionInactiveError",
+      "Active check should either not throw anything, or throw " +
+        "TransactionInactiveError",
+    );
+    return false;
+  }
+}
diff --git a/packages/idb-bridge/src/util/queueTask.ts 
b/packages/idb-bridge/src/util/queueTask.ts
index 53563ffd..297602c6 100644
--- a/packages/idb-bridge/src/util/queueTask.ts
+++ b/packages/idb-bridge/src/util/queueTask.ts
@@ -15,7 +15,20 @@
  */
 
 export function queueTask(fn: () => void) {
-  setImmediate(fn);
+  let called = false;
+  const callFirst = () => {
+    if (called) {
+      return;
+    }
+    called = true;
+    fn();
+  };
+  // We must schedule both of these,
+  // since on node, there is no guarantee
+  // that a setImmediate function that is registered
+  // before a setTimeout function is called first.
+  setImmediate(callFirst);
+  setTimeout(callFirst, 0);
 }
 
 export default queueTask;

-- 
To stop receiving notification emails like this one, please contact
gnunet@gnunet.org.



reply via email to

[Prev in Thread] Current Thread [Next in Thread]