gnunet-svn
[Top][All Lists]
Advanced

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

[taler-wallet-core] branch master updated: idb: make more tests pass, i


From: gnunet
Subject: [taler-wallet-core] branch master updated: idb: make more tests pass, implement Cursor.advance()
Date: Wed, 17 Feb 2021 17:39:34 +0100

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

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

The following commit(s) were added to refs/heads/master by this push:
     new 4b4640db idb: make more tests pass, implement Cursor.advance()
4b4640db is described below

commit 4b4640dbcb2ddc9703a21dd6702a4e264a7331a1
Author: Florian Dold <florian@dold.me>
AuthorDate: Wed Feb 17 17:38:47 2021 +0100

    idb: make more tests pass, implement Cursor.advance()
---
 packages/idb-bridge/src/MemoryBackend.ts           | 67 ++++++++-------
 packages/idb-bridge/src/bridge-idb.ts              | 95 +++++++++++++++++++++-
 .../src/idb-wpt-ported/idbfactory-open.test.ts     | 12 +--
 3 files changed, 135 insertions(+), 39 deletions(-)

diff --git a/packages/idb-bridge/src/MemoryBackend.ts 
b/packages/idb-bridge/src/MemoryBackend.ts
index 4fdcf257..0051005e 100644
--- a/packages/idb-bridge/src/MemoryBackend.ts
+++ b/packages/idb-bridge/src/MemoryBackend.ts
@@ -46,11 +46,10 @@ type Key = IDBValidKey;
 type Value = unknown;
 
 enum TransactionLevel {
-  Disconnected = 0,
-  Connected = 1,
-  Read = 2,
-  Write = 3,
-  VersionChange = 4,
+  None = 0,
+  Read = 1,
+  Write = 2,
+  VersionChange = 3,
 }
 
 interface ObjectStore {
@@ -83,12 +82,18 @@ interface Database {
 
   txLevel: TransactionLevel;
 
+  txOwnerConnectionCookie?: string;
+  txOwnerTransactionCookie?: string;
+
   /**
    * Object stores that the transaction is allowed to access.
    */
   txRestrictObjectStores: string[] | undefined;
 
-  connectionCookie: string | undefined;
+  /**
+   * Connection cookies of current connections.
+   */
+  connectionCookies: string[];
 }
 
 /** @public */
@@ -245,7 +250,7 @@ export class MemoryBackend implements Backend {
   private disconnectCond: AsyncCondition = new AsyncCondition();
 
   /**
-   * Conditation that is triggered whenever a transaction finishes.
+   * Condition that is triggered whenever a transaction finishes.
    */
   private transactionDoneCond: AsyncCondition = new AsyncCondition();
 
@@ -327,8 +332,8 @@ export class MemoryBackend implements Backend {
         deleted: false,
         committedObjectStores: objectStores,
         committedSchema: structuredClone(schema),
-        connectionCookie: undefined,
-        txLevel: TransactionLevel.Disconnected,
+        connectionCookies: [],
+        txLevel: TransactionLevel.None,
         txRestrictObjectStores: undefined,
       };
       this.databases[dbName] = db;
@@ -425,9 +430,9 @@ export class MemoryBackend implements Backend {
     if (myDb.txLevel < TransactionLevel.VersionChange) {
       throw new InvalidStateError();
     }
-    if (myDb.connectionCookie !== tx.transactionCookie) {
-      throw new InvalidAccessError();
-    }
+    // if (myDb.connectionCookie !== tx.transactionCookie) {
+    //   throw new InvalidAccessError();
+    // }
     myDb.deleted = true;
   }
 
@@ -449,20 +454,18 @@ export class MemoryBackend implements Backend {
         committedSchema: schema,
         deleted: false,
         committedObjectStores: {},
-        txLevel: TransactionLevel.Disconnected,
-        connectionCookie: undefined,
+        txLevel: TransactionLevel.None,
+        connectionCookies: [],
         txRestrictObjectStores: undefined,
       };
       this.databases[name] = database;
     }
 
-    while (database.txLevel !== TransactionLevel.Disconnected) {
-      await this.disconnectCond.wait();
+    if (database.connectionCookies.includes(connectionCookie)) {
+      throw Error("already connected");
     }
 
-    database.txLevel = TransactionLevel.Connected;
-    database.txRestrictObjectStores = undefined;
-    database.connectionCookie = connectionCookie;
+    database.connectionCookies.push(connectionCookie);
 
     const myConn: Connection = {
       dbName: name,
@@ -494,7 +497,7 @@ export class MemoryBackend implements Backend {
       throw Error("db not found");
     }
 
-    while (myDb.txLevel !== TransactionLevel.Connected) {
+    while (myDb.txLevel !== TransactionLevel.None) {
       if (this.enableTracing) {
         console.log(`TRACING: beginTransaction -- waiting for others to 
close`);
       }
@@ -533,11 +536,13 @@ export class MemoryBackend implements Backend {
       throw Error("db not found");
     }
 
-    while (myDb.txLevel !== TransactionLevel.Connected) {
+    while (myDb.txLevel !== TransactionLevel.None) {
       await this.transactionDoneCond.wait();
     }
 
     myDb.txLevel = TransactionLevel.VersionChange;
+    myDb.txOwnerConnectionCookie = conn.connectionCookie;
+    myDb.txOwnerTransactionCookie = transactionCookie;
     myDb.txRestrictObjectStores = undefined;
 
     this.connectionsByTransaction[transactionCookie] = myConn;
@@ -557,11 +562,13 @@ export class MemoryBackend implements Backend {
     }
     if (!myConn.deleted) {
       const myDb = this.databases[myConn.dbName];
-      if (myDb.txLevel != TransactionLevel.Connected) {
-        throw Error("invalid state");
-      }
-      myDb.txLevel = TransactionLevel.Disconnected;
-      myDb.txRestrictObjectStores = undefined;
+      // if (myDb.connectionCookies.includes(conn.connectionCookie)) {
+      //   throw Error("invalid state");
+      // }
+      // FIXME: what if we're still in a transaction?
+      myDb.connectionCookies = myDb.connectionCookies.filter(
+        (x) => x != conn.connectionCookie,
+      );
     }
     delete this.connections[conn.connectionCookie];
     this.disconnectCond.trigger();
@@ -1390,7 +1397,7 @@ export class MemoryBackend implements Backend {
       throw Error("db not found");
     }
     if (db.txLevel < TransactionLevel.Write) {
-      throw Error("only allowed while running a transaction");
+      throw Error("store operation only allowed while running a transaction");
     }
     if (
       db.txRestrictObjectStores &&
@@ -1588,9 +1595,9 @@ export class MemoryBackend implements Backend {
       throw Error("db not found");
     }
     if (db.txLevel < TransactionLevel.Read) {
-      throw Error("only allowed while running a transaction");
+      throw Error("rollback is only allowed while running a transaction");
     }
-    db.txLevel = TransactionLevel.Connected;
+    db.txLevel = TransactionLevel.None;
     db.txRestrictObjectStores = undefined;
     myConn.modifiedSchema = structuredClone(db.committedSchema);
     myConn.objectStoreMap = this.makeObjectStoreMap(db);
@@ -1633,7 +1640,7 @@ export class MemoryBackend implements Backend {
     }
 
     db.committedSchema = structuredClone(myConn.modifiedSchema);
-    db.txLevel = TransactionLevel.Connected;
+    db.txLevel = TransactionLevel.None;
     db.txRestrictObjectStores = undefined;
 
     db.committedObjectStores = {};
diff --git a/packages/idb-bridge/src/bridge-idb.ts 
b/packages/idb-bridge/src/bridge-idb.ts
index e23c78d4..6ca6633a 100644
--- a/packages/idb-bridge/src/bridge-idb.ts
+++ b/packages/idb-bridge/src/bridge-idb.ts
@@ -312,7 +312,43 @@ export class BridgeIDBCursor implements IDBCursor {
    * 
http://www.w3.org/TR/2015/REC-IndexedDB-20150108/#widl-IDBCursor-advance-void-unsigned-long-count
    */
   public advance(count: number) {
-    throw Error("not implemented");
+    const transaction = this._effectiveObjectStore._transaction;
+
+    if (!transaction._active) {
+      throw new TransactionInactiveError();
+    }
+
+    if (this._effectiveObjectStore._deleted) {
+      throw new InvalidStateError();
+    }
+    if (
+      !(this.source instanceof BridgeIDBObjectStore) &&
+      this.source._deleted
+    ) {
+      throw new InvalidStateError();
+    }
+
+    if (!this._gotValue) {
+      throw new InvalidStateError();
+    }
+
+    if (this._request) {
+      this._request.readyState = "pending";
+    }
+
+    const operation = async () => {
+      for (let i = 0; i < count; i++) {
+        await this._iterate();
+      }
+    };
+
+    transaction._execRequestAsync({
+      operation,
+      request: this._request,
+      source: this.source,
+    });
+
+    this._gotValue = false;
   }
 
   /**
@@ -760,8 +796,23 @@ export class BridgeIDBFactory {
     queueTask(async () => {
       let dbconn: DatabaseConnection;
       try {
+        if (BridgeIDBFactory.enableTracing) {
+          console.log(
+            "TRACE: connecting to database",
+          );
+        }
         dbconn = await this.backend.connectDatabase(name);
+        if (BridgeIDBFactory.enableTracing) {
+          console.log(
+            "TRACE: connected!",
+          );
+        }
       } catch (err) {
+        if (BridgeIDBFactory.enableTracing) {
+          console.log(
+            "TRACE: caught exception while trying to connect with backend",
+          );
+        }
         request._finishWithError(err);
         return;
       }
@@ -796,11 +847,24 @@ export class BridgeIDBFactory {
           cancelable: false,
         });
         event2.eventPath = [];
+        if (BridgeIDBFactory.enableTracing) {
+          console.log(
+            "open() requested same version, dispatching 'success' event on 
transaction",
+          );
+        }
         request.dispatchEvent(event2);
       } else if (existingVersion < requestedVersion) {
         // 
http://www.w3.org/TR/2015/REC-IndexedDB-20150108/#dfn-steps-for-running-a-versionchange-transaction
 
         for (const otherConn of this.connections) {
+          if (otherConn._closePending) {
+            continue;
+          }
+          if (BridgeIDBFactory.enableTracing) {
+            console.log(
+              "dispatching 'versionchange' event to other connection",
+            );
+          }
           const event = new BridgeIDBVersionChangeEvent("versionchange", {
             newVersion: version,
             oldVersion: existingVersion,
@@ -809,6 +873,11 @@ export class BridgeIDBFactory {
         }
 
         if (this._anyOpen()) {
+          if (BridgeIDBFactory.enableTracing) {
+            console.log(
+              "other connections are still open, dispatching 'blocked' event 
to other connection",
+            );
+          }
           const event = new BridgeIDBVersionChangeEvent("blocked", {
             newVersion: version,
             oldVersion: existingVersion,
@@ -835,6 +904,10 @@ export class BridgeIDBFactory {
 
         db._upgradeTransaction = transaction;
 
+        if (BridgeIDBFactory.enableTracing) {
+          console.log("dispatching upgradeneeded event");
+        }
+
         const event = new BridgeIDBVersionChangeEvent("upgradeneeded", {
           newVersion: version,
           oldVersion: existingVersion,
@@ -866,6 +939,10 @@ export class BridgeIDBFactory {
           event2.eventPath = [];
           request.dispatchEvent(event2);
         } else {
+          if (BridgeIDBFactory.enableTracing) {
+            console.log("dispatching 'success' event for opening db");
+          }
+
           const event2 = new FakeEvent("success", {
             bubbles: false,
             cancelable: false,
@@ -1801,10 +1878,10 @@ export class BridgeIDBRequest extends FakeEventTarget 
implements IDBRequest {
   _result: any = null;
   _error: Error | null | undefined = null;
   get source(): IDBObjectStore | IDBIndex | IDBCursor {
-    if (this._source) {
-      return this._source;
+    if (!this._source) {
+      throw Error("internal invariant failed: source is null");
     }
-    throw Error("source is null");
+    return this._source;
   }
   _source:
     | BridgeIDBCursor
@@ -1875,6 +1952,16 @@ export class BridgeIDBOpenDBRequest
   public onupgradeneeded: EventListener | null = null;
   public onblocked: EventListener | null = null;
 
+  get source(): IDBObjectStore | IDBIndex | IDBCursor {
+    // This is a type safety violation, but it is required by the
+    // IndexedDB standard.
+    // On the one hand, IDBOpenDBRequest implements IDBRequest.
+    // But that's technically impossible, as the "source" of the
+    // IDBOpenDB request may be null, while the one from IDBRequest
+    // may not be null.
+    return this._source as any;
+  }
+
   constructor() {
     super();
     // https://www.w3.org/TR/IndexedDB/#open-requests
diff --git a/packages/idb-bridge/src/idb-wpt-ported/idbfactory-open.test.ts 
b/packages/idb-bridge/src/idb-wpt-ported/idbfactory-open.test.ts
index 4ba7caa6..0d1f24c4 100644
--- a/packages/idb-bridge/src/idb-wpt-ported/idbfactory-open.test.ts
+++ b/packages/idb-bridge/src/idb-wpt-ported/idbfactory-open.test.ts
@@ -20,7 +20,7 @@ test("WPT idbfactory-open.htm", async (t) => {
 // IDBFactory.open() - database 'name' and 'version' are correctly set
 test("WPT idbfactory-open2.htm", async (t) => {
   await new Promise<void>((resolve, reject) => {
-    var database_name = __filename + "-database_name";
+    var database_name = t.title + "-database_name";
     var open_rq = createdb(t, database_name, 13);
 
     open_rq.onupgradeneeded = function (e) {};
@@ -28,7 +28,7 @@ test("WPT idbfactory-open2.htm", async (t) => {
       var db = e.target.result;
       t.deepEqual(db.name, database_name, "db.name");
       t.deepEqual(db.version, 13, "db.version");
-      resolve;
+      resolve();
     };
   });
   t.pass();
@@ -63,7 +63,7 @@ test("WPT idbfactory-open3.htm", async (t) => {
 test("WPT idbfactory-open4.htm", async (t) => {
   const indexedDB = idbFactory;
   await new Promise<void>((resolve, reject) => {
-    var open_rq = createdb(t, __filename + "-database_name");
+    var open_rq = createdb(t, t.title + "-database_name");
 
     open_rq.onupgradeneeded = function (e: any) {
       t.deepEqual(e.target.result.version, 1, "db.version");
@@ -80,7 +80,7 @@ test("WPT idbfactory-open4.htm", async (t) => {
 test("WPT idbfactory-open5.htm", async (t) => {
   const indexedDB = idbFactory;
   await new Promise<void>((resolve, reject) => {
-    var open_rq = createdb(t, __filename + "-database_name");
+    var open_rq = createdb(t, t.title + "-database_name");
 
     open_rq.onupgradeneeded = function () {};
     open_rq.onsuccess = function (e: any) {
@@ -100,7 +100,6 @@ test("WPT idbfactory-open6.htm", async (t) => {
   const indexedDB = idbFactory;
   await new Promise<void>((resolve, reject) => {
     var open_rq = createdb(t, undefined, 13);
-    var did_upgrade = false;
     var open_rq2: any;
 
     open_rq.onupgradeneeded = function () {};
@@ -115,8 +114,10 @@ test("WPT idbfactory-open6.htm", async (t) => {
     };
 
     function open_previous_db(e: any) {
+      t.log("opening previous DB");
       var open_rq3 = indexedDB.open(e.target.result.name, 13);
       open_rq3.onerror = function (e: any) {
+        t.log("got open error");
         t.deepEqual(e.target.error.name, "VersionError", 
"e.target.error.name");
         open_rq2.result.close();
         resolve();
@@ -506,6 +507,7 @@ test("WPT idbfactory-open12.htm", async (t) => {
      * Second test
      */
     db.onversionchange = function () {
+      t.log("onversionchange called");
       db.close();
     };
 

-- 
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]