Quellcodebibliothek Statistik Leitseite products/Sources/formale Sprachen/C/Firefox/toolkit/components/places/tests/sync/   (Browser von der Mozilla Stiftung Version 136.0.1©)  Datei vom 10.2.2025 mit Größe 13 kB image not shown  

Quelle  head_sync.js   Sprache: JAVA

 
/* Any copyright is dedicated to the Public Domain.
 * http://creativecommons.org/publicdomain/zero/1.0/ */


// Import common head.
{
  /* import-globals-from ../head_common.js */
  let commonFile = do_get_file("../head_common.js"false);
  let uri = Services.io.newFileURI(commonFile);
  Services.scriptloader.loadSubScript(uri.spec, this);
}

// Put any other stuff relative to this test folder below.

var { CanonicalJSON } = ChromeUtils.importESModule(
  "resource://gre/modules/CanonicalJSON.sys.mjs"
);
var { Log } = ChromeUtils.importESModule("resource://gre/modules/Log.sys.mjs");

var { PlacesSyncUtils } = ChromeUtils.importESModule(
  "resource://gre/modules/PlacesSyncUtils.sys.mjs"
);
var { SyncedBookmarksMirror } = ChromeUtils.importESModule(
  "resource://gre/modules/SyncedBookmarksMirror.sys.mjs"
);
var { CommonUtils } = ChromeUtils.importESModule(
  "resource://services-common/utils.sys.mjs"
);
var { FileTestUtils } = ChromeUtils.importESModule(
  "resource://testing-common/FileTestUtils.sys.mjs"
);
var {
  HTTP_400,
  HTTP_401,
  HTTP_402,
  HTTP_403,
  HTTP_404,
  HTTP_405,
  HTTP_406,
  HTTP_407,
  HTTP_408,
  HTTP_409,
  HTTP_410,
  HTTP_411,
  HTTP_412,
  HTTP_413,
  HTTP_414,
  HTTP_415,
  HTTP_417,
  HTTP_500,
  HTTP_501,
  HTTP_502,
  HTTP_503,
  HTTP_504,
  HTTP_505,
  HttpError,
  HttpServer,
} = ChromeUtils.importESModule("resource://testing-common/httpd.sys.mjs");

// These titles are defined in Database::CreateBookmarkRoots
const BookmarksMenuTitle = "menu";
const BookmarksToolbarTitle = "toolbar";
const UnfiledBookmarksTitle = "unfiled";
const MobileBookmarksTitle = "mobile";

function run_test() {
  let bufLog = Log.repository.getLogger("Sync.Engine.Bookmarks.Mirror");
  bufLog.level = Log.Level.All;

  let sqliteLog = Log.repository.getLogger("Sqlite");
  sqliteLog.level = Log.Level.Error;

  let formatter = new Log.BasicFormatter();
  let appender = new Log.DumpAppender(formatter);
  appender.level = Log.Level.All;

  for (let log of [bufLog, sqliteLog]) {
    log.addAppender(appender);
  }

  do_get_profile();
  run_next_test();
}

// A test helper to insert local roots directly into Places, since the public
// bookmarks APIs no longer support custom roots.
async function insertLocalRoot({ guid, title }) {
  await PlacesUtils.withConnectionWrapper(
    "insertLocalRoot",
    async function (db) {
      let dateAdded = PlacesUtils.toPRTime(new Date());
      await db.execute(
        `
        INSERT INTO moz_bookmarks(guid, type, parent, position, title,
                                  dateAdded, lastModified)
        VALUES(:guid, :type, (SELECT id FROM moz_bookmarks
                              WHERE guid = :parentGuid),
               (SELECT COUNT(*) FROM moz_bookmarks
                WHERE parent = (SELECT id FROM moz_bookmarks
                                WHERE guid = :parentGuid)),
               :title, :dateAdded, :dateAdded)`,
        {
          guid,
          type: PlacesUtils.bookmarks.TYPE_FOLDER,
          parentGuid: PlacesUtils.bookmarks.rootGuid,
          title,
          dateAdded,
        }
      );
    }
  );
}

// Returns a `CryptoWrapper`-like object that wraps the Sync record cleartext.
// This exists to avoid importing `record.js` from Sync.
function makeRecord(cleartext) {
  return new Proxy(
    { cleartext },
    {
      get(target, property) {
        if (property == "cleartext") {
          return target.cleartext;
        }
        if (property == "cleartextToString") {
          return () => JSON.stringify(target.cleartext);
        }
        return target.cleartext[property];
      },
      set(target, property, value) {
        if (property == "cleartext") {
          target.cleartext = value;
        } else if (property != "cleartextToString") {
          target.cleartext[property] = value;
        }
      },
      has(target, property) {
        return property == "cleartext" || property in target.cleartext;
      },
      deleteProperty() {},
      ownKeys(target) {
        return ["cleartext", ...Reflect.ownKeys(target)];
      },
    }
  );
}

async function storeRecords(buf, records, options) {
  await buf.store(records.map(makeRecord), options);
}

async function storeChangesInMirror(buf, changesToUpload) {
  let cleartexts = [];
  for (let recordId in changesToUpload) {
    changesToUpload[recordId].synced = true;
    cleartexts.push(changesToUpload[recordId].cleartext);
  }
  await storeRecords(buf, cleartexts, { needsMerge: false });
  await PlacesSyncUtils.bookmarks.pushChanges(changesToUpload);
}

function inspectChangeRecords(changeRecords) {
  let results = { updated: [], deleted: [] };
  for (let [id, record] of Object.entries(changeRecords)) {
    (record.tombstone ? results.deleted : results.updated).push(id);
  }
  results.updated.sort();
  results.deleted.sort();
  return results;
}

async function promiseManyDatesAdded(guids) {
  let datesAdded = new Map();
  let db = await PlacesUtils.promiseDBConnection();
  for (let chunk of PlacesUtils.chunkArray(guids, 100)) {
    let rows = await db.executeCached(
      `
      SELECT guid, dateAdded FROM moz_bookmarks
      WHERE guid IN (${new Array(chunk.length).fill("?").join(",")})`,
      chunk
    );
    if (rows.length != chunk.length) {
      throw new TypeError("Can't fetch date added for nonexistent items");
    }
    for (let row of rows) {
      let dateAdded = row.getResultByName("dateAdded") / 1000;
      datesAdded.set(row.getResultByName("guid"), dateAdded);
    }
  }
  return datesAdded;
}

async function fetchLocalTree(rootGuid) {
  function bookmarkNodeToInfo(node) {
    let { guid, index, title, typeCode: type } = node;
    let itemInfo = { guid, index, title, type };
    if (node.annos) {
      let syncableAnnos = node.annos.filter(anno =>
        [PlacesUtils.LMANNO_FEEDURI, PlacesUtils.LMANNO_SITEURI].includes(
          anno.name
        )
      );
      if (syncableAnnos.length) {
        itemInfo.annos = syncableAnnos;
      }
    }
    if (node.uri) {
      itemInfo.url = node.uri;
    }
    if (node.keyword) {
      itemInfo.keyword = node.keyword;
    }
    if (node.children) {
      itemInfo.children = node.children.map(bookmarkNodeToInfo);
    }
    if (node.tags) {
      itemInfo.tags = node.tags.split(",").sort();
    }
    return itemInfo;
  }
  let root = await PlacesUtils.promiseBookmarksTree(rootGuid);
  return bookmarkNodeToInfo(root);
}

async function assertLocalTree(rootGuid, expected, message) {
  let actual = await fetchLocalTree(rootGuid);
  if (!ObjectUtils.deepEqual(actual, expected)) {
    info(
      `Expected structure for ${rootGuid}: ${CanonicalJSON.stringify(expected)}`
    );
    info(
      `Actual structure for ${rootGuid}:   ${CanonicalJSON.stringify(actual)}`
    );
    throw new Assert.constructor.AssertionError({ actual, expected, message });
  }
}

function makeLivemarkServer() {
  let server = new HttpServer();
  server.registerPrefixHandler("/feed/", do_get_file("./livemark.xml"));
  server.start(-1);
  return {
    server,
    get site() {
      let { identity } = server;
      let host = identity.primaryHost.includes(":")
        ? `[${identity.primaryHost}]`
        : identity.primaryHost;
      return `${identity.primaryScheme}://${host}:${identity.primaryPort}`;
    },
    stopServer() {
      return new Promise(resolve => server.stop(resolve));
    },
  };
}

function shuffle(array) {
  let results = [];
  for (let i = 0; i < array.length; ++i) {
    let randomIndex = Math.floor(Math.random() * (i + 1));
    results[i] = results[randomIndex];
    results[randomIndex] = array[i];
  }
  return results;
}

async function fetchAllKeywords(info) {
  let entries = [];
  await PlacesUtils.keywords.fetch(info, entry => entries.push(entry));
  return entries;
}

async function openMirror(name, options = {}) {
  let buf = await SyncedBookmarksMirror.open({
    path: `${name}_buf.sqlite`,
    recordStepTelemetry(...args) {
      if (options.recordStepTelemetry) {
        options.recordStepTelemetry.call(this, ...args);
      }
    },
    recordValidationTelemetry(...args) {
      if (options.recordValidationTelemetry) {
        options.recordValidationTelemetry.call(this, ...args);
      }
    },
  });
  return buf;
}

function BookmarkObserver({ ignoreDates = true, skipTags = false } = {}) {
  this.notifications = [];
  this.ignoreDates = ignoreDates;
  this.skipTags = skipTags;
  this.handlePlacesEvents = this.handlePlacesEvents.bind(this);
}

BookmarkObserver.prototype = {
  handlePlacesEvents(events) {
    for (let event of events) {
      switch (event.type) {
        case "bookmark-added": {
          if (this.skipTags && event.isTagging) {
            continue;
          }
          let params = {
            itemId: event.id,
            parentId: event.parentId,
            index: event.index,
            type: event.itemType,
            urlHref: event.url,
            title: event.title,
            guid: event.guid,
            parentGuid: event.parentGuid,
            source: event.source,
            tags: event.tags,
            frecency: event.frecency,
            hidden: event.hidden,
            visitCount: event.visitCount,
          };
          if (!this.ignoreDates) {
            params.dateAdded = event.dateAdded;
            params.lastVisitDate = event.lastVisitDate;
          }
          this.notifications.push({ name: "bookmark-added", params });
          break;
        }
        case "bookmark-removed": {
          if (this.skipTags && event.isTagging) {
            continue;
          }
          // Since we are now skipping tags on the listener side we don't
          // prevent unTagging notifications from going out. These events cause empty
          // tags folders to be removed which creates another bookmark-removed notification
          if (
            this.skipTags &&
            event.parentGuid == PlacesUtils.bookmarks.tagsGuid
          ) {
            continue;
          }
          let params = {
            itemId: event.id,
            parentId: event.parentId,
            index: event.index,
            type: event.itemType,
            urlHref: event.url || null,
            title: event.title,
            guid: event.guid,
            parentGuid: event.parentGuid,
            source: event.source,
          };
          this.notifications.push({ name: "bookmark-removed", params });
          break;
        }
        case "bookmark-moved": {
          const params = {
            itemId: event.id,
            type: event.itemType,
            urlHref: event.url,
            source: event.source,
            guid: event.guid,
            newIndex: event.index,
            newParentGuid: event.parentGuid,
            oldIndex: event.oldIndex,
            oldParentGuid: event.oldParentGuid,
            isTagging: event.isTagging,
            title: event.title,
            tags: event.tags,
            frecency: event.frecency,
            hidden: event.hidden,
            visitCount: event.visitCount,
            dateAdded: event.dateAdded,
            lastVisitDate: event.lastVisitDate,
          };
          this.notifications.push({ name: "bookmark-moved", params });
          break;
        }
        case "bookmark-guid-changed": {
          const params = {
            itemId: event.id,
            type: event.itemType,
            urlHref: event.url,
            guid: event.guid,
            parentGuid: event.parentGuid,
            source: event.source,
            isTagging: event.isTagging,
          };
          this.notifications.push({ name: "bookmark-guid-changed", params });
          break;
        }
        case "bookmark-title-changed": {
          const params = {
            itemId: event.id,
            guid: event.guid,
            title: event.title,
            parentGuid: event.parentGuid,
          };
          this.notifications.push({ name: "bookmark-title-changed", params });
          break;
        }
        case "bookmark-url-changed": {
          const params = {
            itemId: event.id,
            type: event.itemType,
            urlHref: event.url,
            guid: event.guid,
            parentGuid: event.parentGuid,
            source: event.source,
            isTagging: event.isTagging,
          };
          this.notifications.push({ name: "bookmark-url-changed", params });
          break;
        }
      }
    }
  },

  check(expectedNotifications) {
    PlacesUtils.observers.removeListener(
      [
        "bookmark-added",
        "bookmark-removed",
        "bookmark-moved",
        "bookmark-guid-changed",
        "bookmark-title-changed",
        "bookmark-url-changed",
      ],
      this.handlePlacesEvents
    );
    if (!ObjectUtils.deepEqual(this.notifications, expectedNotifications)) {
      info(`Expected notifications: ${JSON.stringify(expectedNotifications)}`);
      info(`Actual notifications: ${JSON.stringify(this.notifications)}`);
      throw new Assert.constructor.AssertionError({
        actual: this.notifications,
        expected: expectedNotifications,
      });
    }
  },
};

function expectBookmarkChangeNotifications(options) {
  let observer = new BookmarkObserver(options);
  PlacesUtils.observers.addListener(
    [
      "bookmark-added",
      "bookmark-removed",
      "bookmark-moved",
      "bookmark-guid-changed",
      "bookmark-title-changed",
      "bookmark-url-changed",
    ],
    observer.handlePlacesEvents
  );
  return observer;
}

// Copies a support file to a temporary fixture file, allowing the support
// file to be reused for multiple tests.
async function setupFixtureFile(fixturePath) {
  let fixtureFile = do_get_file(fixturePath);
  let tempFile = FileTestUtils.getTempFile(fixturePath);
  await IOUtils.copy(fixtureFile.path, tempFile.path);
  return tempFile;
}

Messung V0.5
C=92 H=90 G=90

¤ Dauer der Verarbeitung: 0.5 Sekunden  ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

Beweissystem der NASA

Beweissystem Isabelle

NIST Cobol Testsuite

Cephes Mathematical Library

Wiener Entwicklungsmethode

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.