diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 09:22:09 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 09:22:09 +0000 |
commit | 43a97878ce14b72f0981164f87f2e35e14151312 (patch) | |
tree | 620249daf56c0258faa40cbdcf9cfba06de2a846 /toolkit/components/places/tests/migration | |
parent | Initial commit. (diff) | |
download | firefox-43a97878ce14b72f0981164f87f2e35e14151312.tar.xz firefox-43a97878ce14b72f0981164f87f2e35e14151312.zip |
Adding upstream version 110.0.1.upstream/110.0.1upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'toolkit/components/places/tests/migration')
25 files changed, 1435 insertions, 0 deletions
diff --git a/toolkit/components/places/tests/migration/favicons_v41.sqlite b/toolkit/components/places/tests/migration/favicons_v41.sqlite Binary files differnew file mode 100644 index 0000000000..a59d9d286f --- /dev/null +++ b/toolkit/components/places/tests/migration/favicons_v41.sqlite diff --git a/toolkit/components/places/tests/migration/head_migration.js b/toolkit/components/places/tests/migration/head_migration.js new file mode 100644 index 0000000000..9dce9b34b9 --- /dev/null +++ b/toolkit/components/places/tests/migration/head_migration.js @@ -0,0 +1,47 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +// 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. + +const CURRENT_SCHEMA_VERSION = 71; +const FIRST_UPGRADABLE_SCHEMA_VERSION = 43; + +async function assertAnnotationsRemoved(db, expectedAnnos) { + for (let anno of expectedAnnos) { + let rows = await db.execute( + ` + SELECT id FROM moz_anno_attributes + WHERE name = :anno + `, + { anno } + ); + + Assert.equal(rows.length, 0, `${anno} should not exist in the database`); + } +} + +async function assertNoOrphanAnnotations(db) { + let rows = await db.execute(` + SELECT item_id FROM moz_items_annos + WHERE item_id NOT IN (SELECT id from moz_bookmarks) + `); + + Assert.equal(rows.length, 0, `Should have no orphan annotations.`); + + rows = await db.execute(` + SELECT id FROM moz_anno_attributes + WHERE id NOT IN (SELECT id from moz_items_annos) + `); + + Assert.equal(rows.length, 0, `Should have no orphan annotation attributes.`); +} diff --git a/toolkit/components/places/tests/migration/places_outdated.sqlite b/toolkit/components/places/tests/migration/places_outdated.sqlite Binary files differnew file mode 100644 index 0000000000..2852a4cf97 --- /dev/null +++ b/toolkit/components/places/tests/migration/places_outdated.sqlite diff --git a/toolkit/components/places/tests/migration/places_v43.sqlite b/toolkit/components/places/tests/migration/places_v43.sqlite Binary files differnew file mode 100644 index 0000000000..9210f215fa --- /dev/null +++ b/toolkit/components/places/tests/migration/places_v43.sqlite diff --git a/toolkit/components/places/tests/migration/places_v54.sqlite b/toolkit/components/places/tests/migration/places_v54.sqlite Binary files differnew file mode 100644 index 0000000000..a203b28c10 --- /dev/null +++ b/toolkit/components/places/tests/migration/places_v54.sqlite diff --git a/toolkit/components/places/tests/migration/places_v66.sqlite b/toolkit/components/places/tests/migration/places_v66.sqlite Binary files differnew file mode 100644 index 0000000000..9578ee11e6 --- /dev/null +++ b/toolkit/components/places/tests/migration/places_v66.sqlite diff --git a/toolkit/components/places/tests/migration/places_v68.sqlite b/toolkit/components/places/tests/migration/places_v68.sqlite Binary files differnew file mode 100644 index 0000000000..414fa170ec --- /dev/null +++ b/toolkit/components/places/tests/migration/places_v68.sqlite diff --git a/toolkit/components/places/tests/migration/places_v69.sqlite b/toolkit/components/places/tests/migration/places_v69.sqlite Binary files differnew file mode 100644 index 0000000000..bc3053c18e --- /dev/null +++ b/toolkit/components/places/tests/migration/places_v69.sqlite diff --git a/toolkit/components/places/tests/migration/places_v70.sqlite b/toolkit/components/places/tests/migration/places_v70.sqlite Binary files differnew file mode 100644 index 0000000000..907e7f5046 --- /dev/null +++ b/toolkit/components/places/tests/migration/places_v70.sqlite diff --git a/toolkit/components/places/tests/migration/places_v71.sqlite b/toolkit/components/places/tests/migration/places_v71.sqlite Binary files differnew file mode 100644 index 0000000000..8039b1dd6a --- /dev/null +++ b/toolkit/components/places/tests/migration/places_v71.sqlite diff --git a/toolkit/components/places/tests/migration/test_current_from_downgraded.js b/toolkit/components/places/tests/migration/test_current_from_downgraded.js new file mode 100644 index 0000000000..5daec14e2f --- /dev/null +++ b/toolkit/components/places/tests/migration/test_current_from_downgraded.js @@ -0,0 +1,29 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +// This test ensures we can pass twice through migration methods without +// failing, that is what happens in case of a downgrade followed by an upgrade. + +add_task(async function setup() { + let dbFile = PathUtils.join( + do_get_cwd().path, + `places_v${CURRENT_SCHEMA_VERSION}.sqlite` + ); + Assert.ok(await IOUtils.exists(dbFile)); + await setupPlacesDatabase(`places_v${CURRENT_SCHEMA_VERSION}.sqlite`); + // Downgrade the schema version to the first supported one. + let path = PathUtils.join(PathUtils.profileDir, DB_FILENAME); + let db = await Sqlite.openConnection({ path }); + await db.setSchemaVersion(FIRST_UPGRADABLE_SCHEMA_VERSION); + await db.close(); +}); + +add_task(async function database_is_valid() { + Assert.equal( + PlacesUtils.history.databaseStatus, + PlacesUtils.history.DATABASE_STATUS_UPGRADED + ); + + let db = await PlacesUtils.promiseDBConnection(); + Assert.equal(await db.getSchemaVersion(), CURRENT_SCHEMA_VERSION); +}); diff --git a/toolkit/components/places/tests/migration/test_current_from_outdated.js b/toolkit/components/places/tests/migration/test_current_from_outdated.js new file mode 100644 index 0000000000..e7fad5b3a4 --- /dev/null +++ b/toolkit/components/places/tests/migration/test_current_from_outdated.js @@ -0,0 +1,47 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +/** + * This file tests migration from a preliminary schema version 6 that + * lacks frecency column and moz_inputhistory table. + */ + +add_task(async function setup() { + await setupPlacesDatabase("places_outdated.sqlite"); +}); + +add_task(async function corrupt_database_not_exists() { + let corruptPath = PathUtils.join( + PathUtils.profileDir, + "places.sqlite.corrupt" + ); + Assert.ok( + !(await IOUtils.exists(corruptPath)), + "Corrupt file should not exist" + ); +}); + +add_task(async function database_is_valid() { + Assert.equal( + PlacesUtils.history.databaseStatus, + PlacesUtils.history.DATABASE_STATUS_CORRUPT + ); + + let db = await PlacesUtils.promiseDBConnection(); + Assert.equal(await db.getSchemaVersion(), CURRENT_SCHEMA_VERSION); +}); + +add_task(async function check_columns() { + // Check the database has been replaced, these would throw otherwise. + let db = await PlacesUtils.promiseDBConnection(); + await db.execute("SELECT frecency from moz_places"); + await db.execute("SELECT 1 from moz_inputhistory"); +}); + +add_task(async function corrupt_database_exists() { + let corruptPath = PathUtils.join( + PathUtils.profileDir, + "places.sqlite.corrupt" + ); + Assert.ok(await IOUtils.exists(corruptPath), "Corrupt file should exist"); +}); diff --git a/toolkit/components/places/tests/migration/test_current_from_v43.js b/toolkit/components/places/tests/migration/test_current_from_v43.js new file mode 100644 index 0000000000..70a383bb2e --- /dev/null +++ b/toolkit/components/places/tests/migration/test_current_from_v43.js @@ -0,0 +1,253 @@ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +const EXPECTED_REMAINING_ROOTS = [ + ...PlacesUtils.bookmarks.userContentRoots, + PlacesUtils.bookmarks.tagsGuid, +]; + +const EXPECTED_REMOVED_BOOKMARK_GUIDS = [ + // These first ones are the old left-pane folder queries + "SNLmwJH6GtW9", // Root Query + "r0dY_2_y4mlx", // History + "xGGhZK3b6GnW", // Downloads + "EJG6I1nKkQFQ", // Tags + "gSyHo5oNSUJV", // All Bookmarks + // These are simulated add-on injections that we expect to be removed. + "exaddon_____", + "exaddon1____", + "exaddon2____", + "exaddon3____", + "test________", +]; + +const EXPECTED_REMOVED_ANNOTATIONS = [ + "PlacesOrganizer/OrganizerFolder", + "PlacesOrganizer/OrganizerQuery", +]; + +const EXPECTED_REMOVED_PLACES_ENTRIES = ["exaddonh____", "exaddonh3___"]; +const EXPECTED_KEPT_PLACES_ENTRY = "exaddonh2___"; +const EXPECTED_REMOVED_KEYWORDS = ["exaddon", "exaddon2"]; + +async function assertItemIn(db, table, field, expectedItems) { + let rows = await db.execute(`SELECT ${field} from ${table}`); + + Assert.ok( + rows.length >= expectedItems.length, + "Should be at least the number of annotations we expect to be removed." + ); + + let fieldValues = rows.map(row => row.getResultByName(field)); + + for (let item of expectedItems) { + Assert.ok( + fieldValues.includes(item), + `${table} should have ${expectedItems}` + ); + } +} + +add_task(async function setup() { + await setupPlacesDatabase("places_v43.sqlite"); + + // Setup database contents to be migrated. + let path = PathUtils.join(PathUtils.profileDir, DB_FILENAME); + let db = await Sqlite.openConnection({ path }); + + let rows = await db.execute(`SELECT * FROM moz_bookmarks_deleted`); + Assert.equal(rows.length, 0, "Should be nothing in moz_bookmarks_deleted"); + + // Break roots parenting, to test for Bug 1472127. + await db.execute(`INSERT INTO moz_bookmarks (title, parent, position, guid) + VALUES ("test", 1, 0, "test________")`); + await db.execute(`UPDATE moz_bookmarks + SET parent = (SELECT id FROM moz_bookmarks WHERE guid = "test________") + WHERE guid = "menu________"`); + + await assertItemIn( + db, + "moz_anno_attributes", + "name", + EXPECTED_REMOVED_ANNOTATIONS + ); + await assertItemIn( + db, + "moz_bookmarks", + "guid", + EXPECTED_REMOVED_BOOKMARK_GUIDS + ); + await assertItemIn(db, "moz_keywords", "keyword", EXPECTED_REMOVED_KEYWORDS); + await assertItemIn(db, "moz_places", "guid", EXPECTED_REMOVED_PLACES_ENTRIES); + + await db.close(); +}); + +add_task(async function database_is_valid() { + // Accessing the database for the first time triggers migration. + Assert.equal( + PlacesUtils.history.databaseStatus, + PlacesUtils.history.DATABASE_STATUS_UPGRADED + ); + + let db = await PlacesUtils.promiseDBConnection(); + Assert.equal(await db.getSchemaVersion(), CURRENT_SCHEMA_VERSION); +}); + +add_task(async function test_roots_removed() { + let db = await PlacesUtils.promiseDBConnection(); + let rows = await db.execute( + ` + SELECT id FROM moz_bookmarks + WHERE guid = :guid + `, + { guid: PlacesUtils.bookmarks.rootGuid } + ); + Assert.equal(rows.length, 1, "Should have exactly one root row."); + let rootId = rows[0].getResultByName("id"); + + rows = await db.execute( + ` + SELECT guid FROM moz_bookmarks + WHERE parent = :rootId`, + { rootId } + ); + + Assert.equal( + rows.length, + EXPECTED_REMAINING_ROOTS.length, + "Should only have the built-in folder roots." + ); + + for (let row of rows) { + let guid = row.getResultByName("guid"); + Assert.ok( + EXPECTED_REMAINING_ROOTS.includes(guid), + `Should have only the expected guids remaining, unexpected guid: ${guid}` + ); + } + + // Check the reparented menu now. + rows = await db.execute( + ` + SELECT id, parent FROM moz_bookmarks + WHERE guid = :guid + `, + { guid: PlacesUtils.bookmarks.menuGuid } + ); + Assert.equal(rows.length, 1, "Should have found the menu root."); + Assert.equal( + rows[0].getResultByName("parent"), + await PlacesUtils.promiseItemId(PlacesUtils.bookmarks.rootGuid), + "Should have moved the menu back to the Places root." + ); +}); + +add_task(async function test_tombstones_added() { + let db = await PlacesUtils.promiseDBConnection(); + + let rows = await db.execute(` + SELECT guid FROM moz_bookmarks_deleted + `); + + for (let row of rows) { + let guid = row.getResultByName("guid"); + Assert.ok( + EXPECTED_REMOVED_BOOKMARK_GUIDS.includes(guid), + `Should have tombstoned the expected guids, unexpected guid: ${guid}` + ); + } + + Assert.equal( + rows.length, + EXPECTED_REMOVED_BOOKMARK_GUIDS.length, + "Should have removed all the expected bookmarks." + ); +}); + +add_task(async function test_annotations_removed() { + let db = await PlacesUtils.promiseDBConnection(); + + await assertAnnotationsRemoved(db, EXPECTED_REMOVED_ANNOTATIONS); +}); + +add_task(async function test_check_history_entries() { + let db = await PlacesUtils.promiseDBConnection(); + + for (let entry of EXPECTED_REMOVED_PLACES_ENTRIES) { + let rows = await db.execute(` + SELECT id FROM moz_places + WHERE guid = '${entry}'`); + + Assert.equal( + rows.length, + 0, + `Should have removed an orphaned history entry ${EXPECTED_REMOVED_PLACES_ENTRIES}.` + ); + } + + let rows = await db.execute( + ` + SELECT foreign_count FROM moz_places + WHERE guid = :guid + `, + { guid: EXPECTED_KEPT_PLACES_ENTRY } + ); + + Assert.equal( + rows.length, + 1, + `Should have kept visited history entry ${EXPECTED_KEPT_PLACES_ENTRY}` + ); + + let foreignCount = rows[0].getResultByName("foreign_count"); + Assert.equal( + foreignCount, + 0, + `Should have updated the foreign_count for ${EXPECTED_KEPT_PLACES_ENTRY}` + ); +}); + +add_task(async function test_check_keyword_removed() { + let db = await PlacesUtils.promiseDBConnection(); + + for (let keyword of EXPECTED_REMOVED_KEYWORDS) { + let rows = await db.execute( + ` + SELECT keyword FROM moz_keywords + WHERE keyword = :keyword + `, + { keyword } + ); + + Assert.equal( + rows.length, + 0, + `Should have removed the expected keyword: ${keyword}.` + ); + } +}); + +add_task(async function test_no_orphan_annotations() { + let db = await PlacesUtils.promiseDBConnection(); + + await assertNoOrphanAnnotations(db); +}); + +add_task(async function test_no_orphan_keywords() { + let db = await PlacesUtils.promiseDBConnection(); + + let rows = await db.execute(` + SELECT place_id FROM moz_keywords + WHERE place_id NOT IN (SELECT id from moz_places) + `); + + Assert.equal(rows.length, 0, `Should have no orphan keywords.`); +}); + +add_task(async function test_meta_exists() { + let db = await PlacesUtils.promiseDBConnection(); + await db.execute(`SELECT 1 FROM moz_meta`); +}); diff --git a/toolkit/components/places/tests/migration/test_current_from_v45.js b/toolkit/components/places/tests/migration/test_current_from_v45.js new file mode 100644 index 0000000000..af940d75d4 --- /dev/null +++ b/toolkit/components/places/tests/migration/test_current_from_v45.js @@ -0,0 +1,100 @@ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +let gTags = [ + { + folder: 123456, + url: "place:folder=123456&type=7&queryType=1", + title: "tag1", + hash: "268505532566465", + }, + { + folder: 234567, + url: "place:folder=234567&type=7&queryType=1&somethingelse", + title: "tag2", + hash: "268506675127932", + }, + { + folder: 345678, + url: "place:type=7&folder=345678&queryType=1", + title: "tag3", + hash: "268506471927988", + }, + // This will point to an invalid folder id. + { + folder: 456789, + url: "place:type=7&folder=456789&queryType=1", + expectedUrl: + "place:type=7&invalidOldParentId=456789&queryType=1&excludeItems=1", + title: "invalid", + hash: "268505972797836", + }, +]; +gTags.forEach(t => (t.guid = t.title.padEnd(12, "_"))); + +add_task(async function setup() { + await setupPlacesDatabase("places_v43.sqlite"); + + // Setup database contents to be migrated. + let path = PathUtils.join(PathUtils.profileDir, DB_FILENAME); + let db = await Sqlite.openConnection({ path }); + + for (let tag of gTags) { + // We can reuse the same guid, it doesn't matter for this test. + await db.execute( + `INSERT INTO moz_places (url, guid, url_hash) + VALUES (:url, :guid, :hash) + `, + { url: tag.url, guid: tag.guid, hash: tag.hash } + ); + if (tag.title != "invalid") { + await db.execute( + `INSERT INTO moz_bookmarks (id, fk, guid, title) + VALUES (:id, (SELECT id FROM moz_places WHERE guid = :guid), :guid, :title) + `, + { id: tag.folder, guid: tag.guid, title: tag.title } + ); + } + } + + await db.close(); +}); + +add_task(async function database_is_valid() { + // Accessing the database for the first time triggers migration. + Assert.equal( + PlacesUtils.history.databaseStatus, + PlacesUtils.history.DATABASE_STATUS_UPGRADED + ); + + let db = await PlacesUtils.promiseDBConnection(); + Assert.equal(await db.getSchemaVersion(), CURRENT_SCHEMA_VERSION); +}); + +add_task(async function test_queries_converted() { + for (let tag of gTags) { + let url = + tag.title == "invalid" ? tag.expectedUrl : "place:tag=" + tag.title; + let page = await PlacesUtils.history.fetch(tag.guid); + Assert.equal(page.url.href, url); + } +}); + +add_task(async function test_sync_fields() { + let db = await PlacesUtils.promiseDBConnection(); + for (let tag of gTags) { + if (tag.title != "invalid") { + let rows = await db.execute( + ` + SELECT syncChangeCounter + FROM moz_bookmarks + WHERE guid = :guid + `, + { guid: tag.guid } + ); + Assert.equal(rows[0].getResultByIndex(0), 2); + } + } +}); diff --git a/toolkit/components/places/tests/migration/test_current_from_v46.js b/toolkit/components/places/tests/migration/test_current_from_v46.js new file mode 100644 index 0000000000..a613a3027e --- /dev/null +++ b/toolkit/components/places/tests/migration/test_current_from_v46.js @@ -0,0 +1,52 @@ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +let guid = "null".padEnd(12, "_"); + +add_task(async function setup() { + await setupPlacesDatabase("places_v43.sqlite"); + + // Setup database contents to be migrated. + let path = PathUtils.join(PathUtils.profileDir, DB_FILENAME); + let db = await Sqlite.openConnection({ path }); + // We can reuse the same guid, it doesn't matter for this test. + + await db.execute( + `INSERT INTO moz_places (url, guid, url_hash) + VALUES (NULL, :guid, "123456")`, + { guid } + ); + await db.execute( + `INSERT INTO moz_bookmarks (fk, guid) + VALUES ((SELECT id FROM moz_places WHERE guid = :guid), :guid) + `, + { guid } + ); + await db.close(); +}); + +add_task(async function database_is_valid() { + // Accessing the database for the first time triggers migration. + Assert.equal( + PlacesUtils.history.databaseStatus, + PlacesUtils.history.DATABASE_STATUS_UPGRADED + ); + + let db = await PlacesUtils.promiseDBConnection(); + Assert.equal(await db.getSchemaVersion(), CURRENT_SCHEMA_VERSION); + + let page = await PlacesUtils.history.fetch(guid); + Assert.equal(page.url.href, "place:excludeItems=1"); + + let rows = await db.execute( + ` + SELECT syncChangeCounter + FROM moz_bookmarks + WHERE guid = :guid + `, + { guid } + ); + Assert.equal(rows[0].getResultByIndex(0), 2); +}); diff --git a/toolkit/components/places/tests/migration/test_current_from_v47.js b/toolkit/components/places/tests/migration/test_current_from_v47.js new file mode 100644 index 0000000000..b3d5f47211 --- /dev/null +++ b/toolkit/components/places/tests/migration/test_current_from_v47.js @@ -0,0 +1,128 @@ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +add_task(async function setup() { + await setupPlacesDatabase("places_v43.sqlite"); +}); + +// Accessing the database for the first time should trigger migration, and the +// schema version should be updated. +add_task(async function database_is_valid() { + Assert.equal( + PlacesUtils.history.databaseStatus, + PlacesUtils.history.DATABASE_STATUS_UPGRADED + ); + + let db = await PlacesUtils.promiseDBConnection(); + Assert.equal(await db.getSchemaVersion(), CURRENT_SCHEMA_VERSION); + + // Now wait for moz_origins.frecency to be populated before continuing with + // other test tasks. + await TestUtils.waitForCondition( + () => { + return !Services.prefs.getBoolPref( + "places.database.migrateV52OriginFrecencies", + false + ); + }, + "Waiting for v52 origin frecencies to be migrated", + 100, + 3000 + ); +}); + +// moz_origins should be populated. +add_task(async function test_origins() { + let db = await PlacesUtils.promiseDBConnection(); + + // Collect origins. + let rows = await db.execute(` + SELECT id, prefix, host, frecency + FROM moz_origins + ORDER BY id ASC; + `); + Assert.notEqual(rows.length, 0); + let origins = rows.map(r => ({ + id: r.getResultByName("id"), + prefix: r.getResultByName("prefix"), + host: r.getResultByName("host"), + frecency: r.getResultByName("frecency"), + })); + + // Get moz_places. + rows = await db.execute(` + SELECT get_prefix(url) AS prefix, get_host_and_port(url) AS host, + origin_id, frecency + FROM moz_places; + `); + Assert.notEqual(rows.length, 0); + + let seenOriginIDs = []; + let frecenciesByOriginID = {}; + + // Make sure moz_places.origin_id refers to the right origins. + for (let row of rows) { + let originID = row.getResultByName("origin_id"); + let origin = origins.find(o => o.id == originID); + Assert.ok(origin); + Assert.equal(origin.prefix, row.getResultByName("prefix")); + Assert.equal(origin.host, row.getResultByName("host")); + + seenOriginIDs.push(originID); + + let frecency = row.getResultByName("frecency"); + frecenciesByOriginID[originID] = frecenciesByOriginID[originID] || 0; + frecenciesByOriginID[originID] += frecency; + } + + for (let origin of origins) { + // Make sure each origin corresponds to at least one moz_place. + Assert.ok(seenOriginIDs.includes(origin.id)); + + // moz_origins.frecency should be the sum of frecencies of all moz_places + // with the origin. + Assert.equal(origin.frecency, frecenciesByOriginID[origin.id]); + } + + // Make sure moz_hosts was emptied. + rows = await db.execute(` + SELECT * + FROM moz_hosts; + `); + Assert.equal(rows.length, 0); +}); + +// Frecency stats should have been collected. +add_task(async function test_frecency_stats() { + let db = await PlacesUtils.promiseDBConnection(); + + // Collect positive frecencies from moz_origins. + let rows = await db.execute(` + SELECT frecency FROM moz_origins WHERE frecency > 0 + `); + Assert.notEqual(rows.length, 0); + let frecencies = rows.map(r => r.getResultByName("frecency")); + + // Collect stats. + rows = await db.execute(` + SELECT + (SELECT value FROM moz_meta WHERE key = "origin_frecency_count"), + (SELECT value FROM moz_meta WHERE key = "origin_frecency_sum"), + (SELECT value FROM moz_meta WHERE key = "origin_frecency_sum_of_squares") + `); + let count = rows[0].getResultByIndex(0); + let sum = rows[0].getResultByIndex(1); + let squares = rows[0].getResultByIndex(2); + + Assert.equal(count, frecencies.length); + Assert.equal( + sum, + frecencies.reduce((memo, f) => memo + f, 0) + ); + Assert.equal( + squares, + frecencies.reduce((memo, f) => memo + f * f, 0) + ); +}); diff --git a/toolkit/components/places/tests/migration/test_current_from_v48.js b/toolkit/components/places/tests/migration/test_current_from_v48.js new file mode 100644 index 0000000000..f2c7c683ed --- /dev/null +++ b/toolkit/components/places/tests/migration/test_current_from_v48.js @@ -0,0 +1,190 @@ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +const gCreatedParentGuid = "m47___FOLDER"; + +const gTestItems = [ + { + // Folder shortcuts to built-in folders. + guid: "m47_____ROOT", + url: "place:folder=PLACES_ROOT", + targetParentGuid: "rootGuid", + }, + { + guid: "m47_____MENU", + url: "place:folder=BOOKMARKS_MENU", + targetParentGuid: "menuGuid", + }, + { + guid: "m47_____TAGS", + url: "place:folder=TAGS", + targetParentGuid: "tagsGuid", + }, + { + guid: "m47____OTHER", + url: "place:folder=UNFILED_BOOKMARKS", + targetParentGuid: "unfiledGuid", + }, + { + guid: "m47__TOOLBAR", + url: "place:folder=TOOLBAR", + targetParentGuid: "toolbarGuid", + }, + { + guid: "m47___MOBILE", + url: "place:folder=MOBILE_BOOKMARKS", + targetParentGuid: "mobileGuid", + }, + { + // Folder shortcut to using id. + guid: "m47_______ID", + url: "place:folder=%id%", + expectedUrl: "place:parent=%guid%", + }, + { + // Folder shortcut to multiple folders. + guid: "m47____MULTI", + url: "place:folder=TOOLBAR&folder=%id%&sort=1", + expectedUrl: "place:parent=%toolbarGuid%&parent=%guid%&sort=1", + }, + { + // Folder shortcut to non-existent folder. + guid: "m47______NON", + url: "place:folder=454554545", + expectedUrl: "place:invalidOldParentId=454554545&excludeItems=1", + }, +]; + +add_task(async function setup() { + await setupPlacesDatabase("places_v43.sqlite"); + + // Setup database contents to be migrated. + let path = PathUtils.join(PathUtils.profileDir, DB_FILENAME); + let db = await Sqlite.openConnection({ path }); + + let rows = await db.execute( + `SELECT id FROM moz_bookmarks + WHERE guid = :guid`, + { guid: PlacesUtils.bookmarks.unfiledGuid } + ); + + let unfiledId = rows[0].getResultByName("id"); + + // Insert a test folder. + await db.execute( + `INSERT INTO moz_bookmarks (guid, title, parent) + VALUES (:guid, "Folder", :parent)`, + { guid: gCreatedParentGuid, parent: unfiledId } + ); + + rows = await db.execute( + `SELECT id FROM moz_bookmarks + WHERE guid = :guid`, + { guid: gCreatedParentGuid } + ); + + let createdFolderId = rows[0].getResultByName("id"); + + for (let item of gTestItems) { + item.url = item.url.replace("%id%", createdFolderId); + + // We can reuse the same guid, it doesn't matter for this test. + await db.execute( + `INSERT INTO moz_places (url, guid, url_hash) + VALUES (:url, :guid, :hash) + `, + { + url: item.url, + guid: item.guid, + hash: PlacesUtils.history.hashURL(item.url), + } + ); + await db.execute( + `INSERT INTO moz_bookmarks (id, fk, guid, title, parent) + VALUES (:id, (SELECT id FROM moz_places WHERE guid = :guid), + :guid, :title, + (SELECT id FROM moz_bookmarks WHERE guid = :parentGuid)) + `, + { + id: item.folder, + guid: item.guid, + parentGuid: PlacesUtils.bookmarks.unfiledGuid, + title: item.guid, + } + ); + } + + await db.close(); +}); + +add_task(async function database_is_valid() { + // Accessing the database for the first time triggers migration. + Assert.equal( + PlacesUtils.history.databaseStatus, + PlacesUtils.history.DATABASE_STATUS_UPGRADED + ); + + let db = await PlacesUtils.promiseDBConnection(); + Assert.equal(await db.getSchemaVersion(), CURRENT_SCHEMA_VERSION); +}); + +add_task(async function test_correct_folder_queries() { + for (let item of gTestItems) { + let bm = await PlacesUtils.bookmarks.fetch(item.guid); + + if (item.targetParentGuid) { + Assert.equal( + bm.url, + `place:parent=${PlacesUtils.bookmarks[item.targetParentGuid]}`, + `Should have updated the URL for ${item.guid}` + ); + } else { + let expected = item.expectedUrl + .replace("%guid%", gCreatedParentGuid) + .replace("%toolbarGuid%", PlacesUtils.bookmarks.toolbarGuid); + + Assert.equal( + bm.url, + expected, + `Should have updated the URL for ${item.guid}` + ); + } + } +}); + +add_task(async function test_hashes_valid() { + let db = await PlacesUtils.promiseDBConnection(); + // Ensure all the hashes in moz_places are valid. + let rows = await db.execute(`SELECT url, url_hash FROM moz_places`); + + for (let row of rows) { + let url = row.getResultByName("url"); + let url_hash = row.getResultByName("url_hash"); + Assert.equal( + url_hash, + PlacesUtils.history.hashURL(url), + `url hash should be correct for ${url}` + ); + } +}); + +add_task(async function test_sync_counters_updated() { + let db = await PlacesUtils.promiseDBConnection(); + + for (let test of gTestItems) { + let rows = await db.execute( + `SELECT syncChangeCounter FROM moz_bookmarks + WHERE guid = :guid`, + { guid: test.guid } + ); + + Assert.equal(rows.length, 1, `Should only be one record for ${test.guid}`); + Assert.equal( + rows[0].getResultByName("syncChangeCounter"), + 2, + `Should have bumped the syncChangeCounter for ${test.guid}` + ); + } +}); diff --git a/toolkit/components/places/tests/migration/test_current_from_v50.js b/toolkit/components/places/tests/migration/test_current_from_v50.js new file mode 100644 index 0000000000..af181091c0 --- /dev/null +++ b/toolkit/components/places/tests/migration/test_current_from_v50.js @@ -0,0 +1,209 @@ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +const BASE_GUID = "null".padEnd(11, "_"); +const LAST_USED_ANNO = "bookmarkPropertiesDialog/folderLastUsed"; +const LAST_USED_META_DATA = "places/bookmarks/edit/lastusedfolder"; + +let expectedGuids = []; + +async function adjustIndices(db, itemGuid) { + await db.execute( + ` + UPDATE moz_bookmarks SET + position = position - 1 + WHERE parent = (SELECT parent FROM moz_bookmarks + WHERE guid = :itemGuid) AND + position >= (SELECT position FROM moz_bookmarks + WHERE guid = :itemGuid)`, + { itemGuid } + ); +} + +async function fetchChildInfos(db, parentGuid) { + let rows = await db.execute( + ` + SELECT b.guid, b.position, b.syncChangeCounter + FROM moz_bookmarks b + JOIN moz_bookmarks p ON p.id = b.parent + WHERE p.guid = :parentGuid + ORDER BY b.position`, + { parentGuid } + ); + return rows.map(row => ({ + guid: row.getResultByName("guid"), + position: row.getResultByName("position"), + syncChangeCounter: row.getResultByName("syncChangeCounter"), + })); +} + +add_task(async function setup() { + await setupPlacesDatabase("places_v43.sqlite"); + + // Setup database contents to be migrated. + let path = PathUtils.join(PathUtils.profileDir, DB_FILENAME); + let db = await Sqlite.openConnection({ path }); + // We can reuse the same guid, it doesn't matter for this test. + await db.execute( + `INSERT INTO moz_anno_attributes (name) + VALUES (:last_used_anno)`, + { last_used_anno: LAST_USED_ANNO } + ); + + for (let i = 0; i < 3; i++) { + let guid = `${BASE_GUID}${i}`; + await db.execute( + `INSERT INTO moz_bookmarks (guid, type) + VALUES (:guid, :type) + `, + { guid, type: PlacesUtils.bookmarks.TYPE_FOLDER } + ); + await db.execute( + `INSERT INTO moz_items_annos (item_id, anno_attribute_id, content) + VALUES ((SELECT id FROM moz_bookmarks WHERE guid = :guid), + (SELECT id FROM moz_anno_attributes WHERE name = :last_used_anno), + :content)`, + { + guid, + content: new Date(1517318477569) - (3 - i) * 60 * 60 * 1000, + last_used_anno: LAST_USED_ANNO, + } + ); + expectedGuids.unshift(guid); + } + + info("Move menu into unfiled"); + await adjustIndices(db, "menu________"); + await db.execute( + ` + UPDATE moz_bookmarks SET + parent = (SELECT id FROM moz_bookmarks WHERE guid = :newParentGuid), + position = IFNULL((SELECT MAX(position) + 1 FROM moz_bookmarks + WHERE guid = :newParentGuid), 0) + WHERE guid = :itemGuid`, + { newParentGuid: "unfiled_____", itemGuid: "menu________" } + ); + + info("Move toolbar into mobile"); + let mobileChildren = [ + "bookmarkAAAA", + "bookmarkBBBB", + "toolbar_____", + "bookmarkCCCC", + "bookmarkDDDD", + ]; + await adjustIndices(db, "toolbar_____"); + for (let position = 0; position < mobileChildren.length; position++) { + await db.execute( + ` + INSERT INTO moz_bookmarks(guid, parent, position) + VALUES(:guid, (SELECT id FROM moz_bookmarks WHERE guid = 'mobile______'), + :position) + ON CONFLICT(guid) DO UPDATE SET + parent = excluded.parent, + position = excluded.position`, + { guid: mobileChildren[position], position } + ); + } + + info("Reset Sync change counters"); + await db.execute(`UPDATE moz_bookmarks SET syncChangeCounter = 0`); + + await db.close(); +}); + +add_task(async function database_is_valid() { + // Accessing the database for the first time triggers migration. + Assert.equal( + PlacesUtils.history.databaseStatus, + PlacesUtils.history.DATABASE_STATUS_UPGRADED + ); + + let db = await PlacesUtils.promiseDBConnection(); + Assert.equal(await db.getSchemaVersion(), CURRENT_SCHEMA_VERSION); +}); + +add_task(async function test_folders_migrated() { + let metaData = await PlacesUtils.metadata.get(LAST_USED_META_DATA); + + Assert.deepEqual(JSON.parse(metaData), expectedGuids); +}); + +add_task(async function test_annotations_removed() { + let db = await PlacesUtils.promiseDBConnection(); + + await assertAnnotationsRemoved(db, [LAST_USED_ANNO]); +}); + +add_task(async function test_no_orphan_annotations() { + let db = await PlacesUtils.promiseDBConnection(); + + await assertNoOrphanAnnotations(db); +}); + +add_task(async function test_roots_fixed() { + let db = await PlacesUtils.promiseDBConnection(); + + let expectedRootInfos = [ + { + guid: PlacesUtils.bookmarks.tagsGuid, + position: 0, + syncChangeCounter: 0, + }, + { + guid: PlacesUtils.bookmarks.unfiledGuid, + position: 1, + syncChangeCounter: 1, + }, + { + guid: PlacesUtils.bookmarks.mobileGuid, + position: 2, + syncChangeCounter: 1, + }, + { + guid: PlacesUtils.bookmarks.menuGuid, + position: 3, + syncChangeCounter: 1, + }, + { + guid: PlacesUtils.bookmarks.toolbarGuid, + position: 4, + syncChangeCounter: 1, + }, + ]; + Assert.deepEqual( + expectedRootInfos, + await fetchChildInfos(db, PlacesUtils.bookmarks.rootGuid), + "All roots should be reparented to the Places root" + ); + + let expectedMobileInfos = [ + { + guid: "bookmarkAAAA", + position: 0, + syncChangeCounter: 0, + }, + { + guid: "bookmarkBBBB", + position: 1, + syncChangeCounter: 0, + }, + { + guid: "bookmarkCCCC", + position: 2, + syncChangeCounter: 0, + }, + { + guid: "bookmarkDDDD", + position: 3, + syncChangeCounter: 0, + }, + ]; + Assert.deepEqual( + expectedMobileInfos, + await fetchChildInfos(db, PlacesUtils.bookmarks.mobileGuid), + "Should fix misparented root sibling positions" + ); +}); diff --git a/toolkit/components/places/tests/migration/test_current_from_v53.js b/toolkit/components/places/tests/migration/test_current_from_v53.js new file mode 100644 index 0000000000..f872dea5d5 --- /dev/null +++ b/toolkit/components/places/tests/migration/test_current_from_v53.js @@ -0,0 +1,23 @@ +add_task(async function setup() { + // Since this migration doesn't affect places.sqlite, we can reuse v43. + await setupPlacesDatabase("places_v43.sqlite"); + await setupPlacesDatabase("favicons_v41.sqlite", "favicons.sqlite"); +}); + +add_task(async function database_is_valid() { + // Accessing the database for the first time triggers migration. + Assert.equal( + PlacesUtils.history.databaseStatus, + PlacesUtils.history.DATABASE_STATUS_UPGRADED + ); + + let db = await PlacesUtils.promiseDBConnection(); + Assert.equal(await db.getSchemaVersion(), CURRENT_SCHEMA_VERSION); + + let count = ( + await db.execute( + `SELECT count(*) FROM moz_icons_to_pages WHERE expire_ms = 0` + ) + )[0].getResultByIndex(0); + Assert.equal(count, 0, "All the expirations should be set"); +}); diff --git a/toolkit/components/places/tests/migration/test_current_from_v54.js b/toolkit/components/places/tests/migration/test_current_from_v54.js new file mode 100644 index 0000000000..94c8a26474 --- /dev/null +++ b/toolkit/components/places/tests/migration/test_current_from_v54.js @@ -0,0 +1,58 @@ +add_task(async function setup() { + // Since this migration doesn't affect places.sqlite, we can reuse v43. + await setupPlacesDatabase("places_v54.sqlite"); + await setupPlacesDatabase("favicons_v41.sqlite", "favicons.sqlite"); +}); + +add_task(async function database_is_valid() { + // Accessing the database for the first time triggers migration. + Assert.equal( + PlacesUtils.history.databaseStatus, + PlacesUtils.history.DATABASE_STATUS_UPGRADED + ); + + let db = await PlacesUtils.promiseDBConnection(); + Assert.equal(await db.getSchemaVersion(), CURRENT_SCHEMA_VERSION); + + for (let table of [ + "moz_places_metadata", + "moz_places_metadata_search_queries", + ]) { + let count = ( + await db.execute(`SELECT count(*) FROM ${table}`) + )[0].getResultByIndex(0); + Assert.equal(count, 0, `Empty table ${table}`); + } + + for (let table of [ + "moz_places_metadata_snapshots", + "moz_places_metadata_snapshots_extra", + "moz_places_metadata_snapshots_groups", + "moz_places_metadata_groups_to_snapshots", + "moz_session_metadata", + "moz_session_to_places", + ]) { + await Assert.rejects( + db.execute(`SELECT count(*) FROM ${table}`), + /no such table/, + `Table ${table} should not exist` + ); + } +}); + +add_task(async function scrolling_fields_in_database() { + let db = await PlacesUtils.promiseDBConnection(); + await db.execute( + `SELECT scrolling_time,scrolling_distance FROM moz_places_metadata` + ); +}); + +add_task(async function site_name_field_in_database() { + let db = await PlacesUtils.promiseDBConnection(); + await db.execute(`SELECT site_name FROM moz_places`); +}); + +add_task(async function previews_tombstones_in_database() { + let db = await PlacesUtils.promiseDBConnection(); + await db.execute(`SELECT hash FROM moz_previews_tombstones`); +}); diff --git a/toolkit/components/places/tests/migration/test_current_from_v66.js b/toolkit/components/places/tests/migration/test_current_from_v66.js new file mode 100644 index 0000000000..5ea14f3b9d --- /dev/null +++ b/toolkit/components/places/tests/migration/test_current_from_v66.js @@ -0,0 +1,53 @@ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +add_task(async function setup() { + const path = await setupPlacesDatabase("places_v66.sqlite"); + + const db = await Sqlite.openConnection({ path }); + await db.execute(` + INSERT INTO moz_inputhistory (input, use_count, place_id) + VALUES + ('abc', 1, 1), + ('aBc', 0.9, 1), + ('ABC', 5, 1), + ('ABC', 1, 2), + ('DEF', 1, 3) + `); + await db.close(); +}); + +add_task(async function database_is_valid() { + // Accessing the database for the first time triggers migration. + Assert.equal( + PlacesUtils.history.databaseStatus, + PlacesUtils.history.DATABASE_STATUS_UPGRADED + ); + + let db = await PlacesUtils.promiseDBConnection(); + Assert.equal(await db.getSchemaVersion(), CURRENT_SCHEMA_VERSION); +}); + +add_task(async function moz_inputhistory() { + await PlacesUtils.withConnectionWrapper("test_sqlite_migration", async db => { + const rows = await db.execute( + "SELECT * FROM moz_inputhistory ORDER BY place_id" + ); + + Assert.equal(rows.length, 3); + + Assert.equal(rows[0].getResultByName("place_id"), 1); + Assert.equal(rows[0].getResultByName("input"), "abc"); + Assert.equal(rows[0].getResultByName("use_count"), 5); + + Assert.equal(rows[1].getResultByName("place_id"), 2); + Assert.equal(rows[1].getResultByName("input"), "abc"); + Assert.equal(rows[1].getResultByName("use_count"), 1); + + Assert.equal(rows[2].getResultByName("place_id"), 3); + Assert.equal(rows[2].getResultByName("input"), "def"); + Assert.equal(rows[2].getResultByName("use_count"), 1); + }); +}); diff --git a/toolkit/components/places/tests/migration/test_current_from_v68.js b/toolkit/components/places/tests/migration/test_current_from_v68.js new file mode 100644 index 0000000000..689fcbfd40 --- /dev/null +++ b/toolkit/components/places/tests/migration/test_current_from_v68.js @@ -0,0 +1,35 @@ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +add_task(async function setup() { + const path = await setupPlacesDatabase("places_v68.sqlite"); + + const db = await Sqlite.openConnection({ path }); + await db.execute("INSERT INTO moz_historyvisits (from_visit) VALUES (-1)"); + await db.close(); +}); + +add_task(async function database_is_valid() { + // Accessing the database for the first time triggers migration. + Assert.equal( + PlacesUtils.history.databaseStatus, + PlacesUtils.history.DATABASE_STATUS_UPGRADED + ); + + const db = await PlacesUtils.promiseDBConnection(); + Assert.equal(await db.getSchemaVersion(), CURRENT_SCHEMA_VERSION); +}); + +add_task(async function moz_historyvisits() { + await PlacesUtils.withConnectionWrapper("test_sqlite_migration", async db => { + const rows = await db.execute( + "SELECT * FROM moz_historyvisits WHERE from_visit=-1" + ); + + Assert.equal(rows.length, 1); + Assert.equal(rows[0].getResultByName("source"), 0); + Assert.equal(rows[0].getResultByName("triggeringPlaceId"), null); + }); +}); diff --git a/toolkit/components/places/tests/migration/test_current_from_v69.js b/toolkit/components/places/tests/migration/test_current_from_v69.js new file mode 100644 index 0000000000..09c66fb66e --- /dev/null +++ b/toolkit/components/places/tests/migration/test_current_from_v69.js @@ -0,0 +1,84 @@ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +add_task(async function setup() { + const path = await setupPlacesDatabase("places_v69.sqlite"); + + const db = await Sqlite.openConnection({ path }); + await db.execute(` + INSERT INTO moz_places (url, guid, url_hash, origin_id, frecency) + VALUES + ('https://test1.com', '___________1', '123456', 100, 0), + ('https://test2.com', '___________2', '123456', 101, -1), + ('https://test3.com', '___________3', '123456', 102, -1234) + `); + await db.execute(` + INSERT INTO moz_origins (id, prefix, host, frecency) + VALUES + (100, 'https://', 'test1.com', 0), + (101, 'https://', 'test2.com', 0), + (102, 'https://', 'test3.com', 0) + `); + await db.close(); +}); + +add_task(async function database_is_valid() { + // Accessing the database for the first time triggers migration. + Assert.equal( + PlacesUtils.history.databaseStatus, + PlacesUtils.history.DATABASE_STATUS_UPGRADED + ); + + const db = await PlacesUtils.promiseDBConnection(); + Assert.equal(await db.getSchemaVersion(), CURRENT_SCHEMA_VERSION); +}); + +add_task(async function moz_historyvisits() { + await PlacesUtils.withConnectionWrapper("test_sqlite_migration", async db => { + function expectedFrecency(guid) { + switch (guid) { + case "___________1": + return 0; + case "___________2": + return -1; + case "___________3": + return 1234; + default: + throw new Error("Unknown guid"); + } + } + const rows = await db.execute( + "SELECT guid, frecency FROM moz_places WHERE url_hash = '123456'" + ); + for (let row of rows) { + Assert.equal( + row.getResultByName("frecency"), + expectedFrecency(row.getResultByName("guid")), + "Check expected frecency" + ); + } + const origins = new Map( + (await db.execute("SELECT host, frecency FROM moz_origins")).map(r => [ + r.getResultByName("host"), + r.getResultByName("frecency"), + ]) + ); + Assert.equal(origins.get("test1.com"), 0); + Assert.equal(origins.get("test2.com"), 0); + Assert.equal(origins.get("test3.com"), 1234); + + const statSum = ( + await db.execute( + "SELECT value FROM moz_meta WHERE key = 'origin_frecency_sum'" + ) + )[0].getResultByName("value"); + const sum = ( + await db.execute( + "SELECT SUM(frecency) AS sum from moz_origins WHERE frecency > 0" + ) + )[0].getResultByName("sum"); + Assert.equal(sum, statSum, "Check stats were updated"); + }); +}); diff --git a/toolkit/components/places/tests/migration/test_current_from_v70.js b/toolkit/components/places/tests/migration/test_current_from_v70.js new file mode 100644 index 0000000000..f17439070a --- /dev/null +++ b/toolkit/components/places/tests/migration/test_current_from_v70.js @@ -0,0 +1,96 @@ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +add_task(async function setup() { + let path = await setupPlacesDatabase("places_v70.sqlite"); + + let db = await Sqlite.openConnection({ path }); + await db.execute(` + INSERT INTO moz_places (url, guid, url_hash, origin_id, frecency, foreign_count) + VALUES + ('https://test1.com', '___________1', '123456', 100, 0, 2), + ('https://test2.com', '___________2', '123456', 101, -1, 2), + ('https://test3.com', '___________3', '123456', 102, -1234, 1) + `); + await db.execute(` + INSERT INTO moz_origins (id, prefix, host, frecency) + VALUES + (100, 'https://', 'test1.com', 0), + (101, 'https://', 'test2.com', 0), + (102, 'https://', 'test3.com', 0) + `); + await db.execute( + `INSERT INTO moz_session_metadata + (id, guid) + VALUES (0, "0") + ` + ); + + await db.execute( + `INSERT INTO moz_places_metadata_snapshots + (place_id, created_at, first_interaction_at, last_interaction_at) + VALUES ((SELECT id FROM moz_places WHERE guid = :guid), 0, 0, 0) + `, + { guid: "___________1" } + ); + await db.execute( + `INSERT INTO moz_bookmarks + (fk, guid) + VALUES ((SELECT id FROM moz_places WHERE guid = :guid), :guid) + `, + { guid: "___________1" } + ); + + await db.execute( + `INSERT INTO moz_places_metadata_snapshots + (place_id, created_at, first_interaction_at, last_interaction_at) + VALUES ((SELECT id FROM moz_places WHERE guid = :guid), 0, 0, 0) + `, + { guid: "___________2" } + ); + await db.execute( + `INSERT INTO moz_session_to_places + (session_id, place_id) + VALUES (0, (SELECT id FROM moz_places WHERE guid = :guid)) + `, + { guid: "___________2" } + ); + + await db.execute( + `INSERT INTO moz_session_to_places + (session_id, place_id) + VALUES (0, (SELECT id FROM moz_places WHERE guid = :guid)) + `, + { guid: "___________3" } + ); + + await db.close(); +}); + +add_task(async function database_is_valid() { + // Accessing the database for the first time triggers migration. + Assert.equal( + PlacesUtils.history.databaseStatus, + PlacesUtils.history.DATABASE_STATUS_UPGRADED + ); + + const db = await PlacesUtils.promiseDBConnection(); + Assert.equal(await db.getSchemaVersion(), 71); + + let rows = await db.execute("SELECT guid, foreign_count FROM moz_places"); + for (let row of rows) { + let guid = row.getResultByName("guid"); + let count = row.getResultByName("foreign_count"); + if (guid == "___________1") { + Assert.equal(count, 1, "test1 should have the correct foreign_count"); + } + if (guid == "___________2") { + Assert.equal(count, 0, "test2 should have the correct foreign_count"); + } + if (guid == "___________3") { + Assert.equal(count, 0, "test3 should have the correct foreign_count"); + } + } +}); diff --git a/toolkit/components/places/tests/migration/xpcshell.ini b/toolkit/components/places/tests/migration/xpcshell.ini new file mode 100644 index 0000000000..1a2d52a3de --- /dev/null +++ b/toolkit/components/places/tests/migration/xpcshell.ini @@ -0,0 +1,31 @@ +[DEFAULT] +head = head_migration.js +tags = condprof + +support-files = + favicons_v41.sqlite + places_outdated.sqlite + places_v43.sqlite + places_v54.sqlite + places_v66.sqlite + places_v68.sqlite + places_v69.sqlite + places_v70.sqlite + places_v71.sqlite + +[test_current_from_downgraded.js] +[test_current_from_outdated.js] +[test_current_from_v43.js] +[test_current_from_v45.js] +[test_current_from_v46.js] +[test_current_from_v47.js] +[test_current_from_v48.js] +[test_current_from_v50.js] +[test_current_from_v53.js] +skip-if = condprof # Bug 1769154 - not supported +[test_current_from_v54.js] +skip-if = condprof # Bug 1769154 - not supported +[test_current_from_v66.js] +[test_current_from_v68.js] +[test_current_from_v69.js] +[test_current_from_v70.js] |