diff options
Diffstat (limited to 'toolkit/components/places/tests/migration')
34 files changed, 685 insertions, 0 deletions
diff --git a/toolkit/components/places/tests/migration/.eslintrc.js b/toolkit/components/places/tests/migration/.eslintrc.js new file mode 100644 index 000000000..d35787cd2 --- /dev/null +++ b/toolkit/components/places/tests/migration/.eslintrc.js @@ -0,0 +1,7 @@ +"use strict"; + +module.exports = { + "extends": [ + "../../../../../testing/xpcshell/xpcshell.eslintrc.js" + ] +}; 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 000000000..1ebecd4c0 --- /dev/null +++ b/toolkit/components/places/tests/migration/head_migration.js @@ -0,0 +1,46 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict" + +var { classes: Cc, interfaces: Ci, results: Cr, utils: Cu } = Components; + +Cu.import("resource://gre/modules/Services.jsm"); + +// Import common head. +{ + 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 DB_FILENAME = "places.sqlite"; + +/** + * Sets the database to use for the given test. This should be the very first + * thing in the test, otherwise this database will not be used! + * + * @param aFileName + * The filename of the database to use. This database must exist in + * toolkit/components/places/tests/migration! + * @return {Promise} + */ +var setupPlacesDatabase = Task.async(function* (aFileName) { + let currentDir = yield OS.File.getCurrentDirectory(); + + let src = OS.Path.join(currentDir, aFileName); + Assert.ok((yield OS.File.exists(src)), "Database file found"); + + // Ensure that our database doesn't already exist. + let dest = OS.Path.join(OS.Constants.Path.profileDir, DB_FILENAME); + Assert.ok(!(yield OS.File.exists(dest)), "Database file should not exist yet"); + + yield OS.File.copy(src, dest); +}); + +// This works provided all tests in this folder use add_task. +function run_test() { + run_next_test(); +} diff --git a/toolkit/components/places/tests/migration/places_v10.sqlite b/toolkit/components/places/tests/migration/places_v10.sqlite Binary files differnew file mode 100644 index 000000000..80a8ecd6a --- /dev/null +++ b/toolkit/components/places/tests/migration/places_v10.sqlite diff --git a/toolkit/components/places/tests/migration/places_v11.sqlite b/toolkit/components/places/tests/migration/places_v11.sqlite Binary files differnew file mode 100644 index 000000000..bef27d5f5 --- /dev/null +++ b/toolkit/components/places/tests/migration/places_v11.sqlite diff --git a/toolkit/components/places/tests/migration/places_v17.sqlite b/toolkit/components/places/tests/migration/places_v17.sqlite Binary files differnew file mode 100644 index 000000000..5183cde83 --- /dev/null +++ b/toolkit/components/places/tests/migration/places_v17.sqlite diff --git a/toolkit/components/places/tests/migration/places_v19.sqlite b/toolkit/components/places/tests/migration/places_v19.sqlite Binary files differnew file mode 100644 index 000000000..11e2e6247 --- /dev/null +++ b/toolkit/components/places/tests/migration/places_v19.sqlite diff --git a/toolkit/components/places/tests/migration/places_v21.sqlite b/toolkit/components/places/tests/migration/places_v21.sqlite Binary files differnew file mode 100644 index 000000000..f72930826 --- /dev/null +++ b/toolkit/components/places/tests/migration/places_v21.sqlite diff --git a/toolkit/components/places/tests/migration/places_v22.sqlite b/toolkit/components/places/tests/migration/places_v22.sqlite Binary files differnew file mode 100644 index 000000000..30bf840b0 --- /dev/null +++ b/toolkit/components/places/tests/migration/places_v22.sqlite diff --git a/toolkit/components/places/tests/migration/places_v23.sqlite b/toolkit/components/places/tests/migration/places_v23.sqlite Binary files differnew file mode 100644 index 000000000..b519b97d2 --- /dev/null +++ b/toolkit/components/places/tests/migration/places_v23.sqlite diff --git a/toolkit/components/places/tests/migration/places_v24.sqlite b/toolkit/components/places/tests/migration/places_v24.sqlite Binary files differnew file mode 100644 index 000000000..b35f958a6 --- /dev/null +++ b/toolkit/components/places/tests/migration/places_v24.sqlite diff --git a/toolkit/components/places/tests/migration/places_v25.sqlite b/toolkit/components/places/tests/migration/places_v25.sqlite Binary files differnew file mode 100644 index 000000000..2afd1da1f --- /dev/null +++ b/toolkit/components/places/tests/migration/places_v25.sqlite diff --git a/toolkit/components/places/tests/migration/places_v26.sqlite b/toolkit/components/places/tests/migration/places_v26.sqlite Binary files differnew file mode 100644 index 000000000..b4b238179 --- /dev/null +++ b/toolkit/components/places/tests/migration/places_v26.sqlite diff --git a/toolkit/components/places/tests/migration/places_v27.sqlite b/toolkit/components/places/tests/migration/places_v27.sqlite Binary files differnew file mode 100644 index 000000000..57dfb7562 --- /dev/null +++ b/toolkit/components/places/tests/migration/places_v27.sqlite diff --git a/toolkit/components/places/tests/migration/places_v28.sqlite b/toolkit/components/places/tests/migration/places_v28.sqlite Binary files differnew file mode 100644 index 000000000..9a27db324 --- /dev/null +++ b/toolkit/components/places/tests/migration/places_v28.sqlite diff --git a/toolkit/components/places/tests/migration/places_v29.sqlite b/toolkit/components/places/tests/migration/places_v29.sqlite Binary files differnew file mode 100644 index 000000000..f6de0fe8a --- /dev/null +++ b/toolkit/components/places/tests/migration/places_v29.sqlite diff --git a/toolkit/components/places/tests/migration/places_v30.sqlite b/toolkit/components/places/tests/migration/places_v30.sqlite Binary files differnew file mode 100644 index 000000000..9cbabe005 --- /dev/null +++ b/toolkit/components/places/tests/migration/places_v30.sqlite diff --git a/toolkit/components/places/tests/migration/places_v31.sqlite b/toolkit/components/places/tests/migration/places_v31.sqlite Binary files differnew file mode 100644 index 000000000..9d33b9eff --- /dev/null +++ b/toolkit/components/places/tests/migration/places_v31.sqlite diff --git a/toolkit/components/places/tests/migration/places_v32.sqlite b/toolkit/components/places/tests/migration/places_v32.sqlite Binary files differnew file mode 100644 index 000000000..239f6c5fe --- /dev/null +++ b/toolkit/components/places/tests/migration/places_v32.sqlite diff --git a/toolkit/components/places/tests/migration/places_v33.sqlite b/toolkit/components/places/tests/migration/places_v33.sqlite Binary files differnew file mode 100644 index 000000000..6071dc6a6 --- /dev/null +++ b/toolkit/components/places/tests/migration/places_v33.sqlite diff --git a/toolkit/components/places/tests/migration/places_v34.sqlite b/toolkit/components/places/tests/migration/places_v34.sqlite Binary files differnew file mode 100644 index 000000000..474628996 --- /dev/null +++ b/toolkit/components/places/tests/migration/places_v34.sqlite diff --git a/toolkit/components/places/tests/migration/places_v35.sqlite b/toolkit/components/places/tests/migration/places_v35.sqlite Binary files differnew file mode 100644 index 000000000..5e157d778 --- /dev/null +++ b/toolkit/components/places/tests/migration/places_v35.sqlite diff --git a/toolkit/components/places/tests/migration/places_v6.sqlite b/toolkit/components/places/tests/migration/places_v6.sqlite Binary files differnew file mode 100644 index 000000000..2852a4cf9 --- /dev/null +++ b/toolkit/components/places/tests/migration/places_v6.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 000000000..6d36cab14 --- /dev/null +++ b/toolkit/components/places/tests/migration/test_current_from_downgraded.js @@ -0,0 +1,19 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +add_task(function* setup() { + yield setupPlacesDatabase(`places_v${CURRENT_SCHEMA_VERSION}.sqlite`); + // Downgrade the schema version to the first supported one. + let path = OS.Path.join(OS.Constants.Path.profileDir, DB_FILENAME); + let db = yield Sqlite.openConnection({ path: path }); + yield db.setSchemaVersion(FIRST_UPGRADABLE_SCHEMA_VERSION); + yield db.close(); +}); + +add_task(function* database_is_valid() { + Assert.equal(PlacesUtils.history.databaseStatus, + PlacesUtils.history.DATABASE_STATUS_UPGRADED); + + let db = yield PlacesUtils.promiseDBConnection(); + Assert.equal((yield db.getSchemaVersion()), CURRENT_SCHEMA_VERSION); +}); diff --git a/toolkit/components/places/tests/migration/test_current_from_v11.js b/toolkit/components/places/tests/migration/test_current_from_v11.js new file mode 100644 index 000000000..43b8fb1f6 --- /dev/null +++ b/toolkit/components/places/tests/migration/test_current_from_v11.js @@ -0,0 +1,48 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +add_task(function* setup() { + yield setupPlacesDatabase("places_v11.sqlite"); +}); + +add_task(function* database_is_valid() { + Assert.equal(PlacesUtils.history.databaseStatus, + PlacesUtils.history.DATABASE_STATUS_UPGRADED); + + let db = yield PlacesUtils.promiseDBConnection(); + Assert.equal((yield db.getSchemaVersion()), CURRENT_SCHEMA_VERSION); +}); + +add_task(function* test_moz_hosts() { + let db = yield PlacesUtils.promiseDBConnection(); + + // This will throw if the column does not exist. + yield db.execute("SELECT host, frecency, typed, prefix FROM moz_hosts"); + + // moz_hosts is populated asynchronously, so we need to wait. + yield PlacesTestUtils.promiseAsyncUpdates(); + + // check the number of entries in moz_hosts equals the number of + // unique rev_host in moz_places + let rows = yield db.execute( + `SELECT (SELECT COUNT(host) FROM moz_hosts), + (SELECT COUNT(DISTINCT rev_host) + FROM moz_places + WHERE LENGTH(rev_host) > 1) + `); + + Assert.equal(rows.length, 1); + let mozHostsCount = rows[0].getResultByIndex(0); + let mozPlacesCount = rows[0].getResultByIndex(1); + + Assert.ok(mozPlacesCount > 0, "There is some url in the database"); + Assert.equal(mozPlacesCount, mozHostsCount, "moz_hosts has the expected number of entries"); +}); + +add_task(function* test_journal() { + let db = yield PlacesUtils.promiseDBConnection(); + let rows = yield db.execute("PRAGMA journal_mode"); + Assert.equal(rows.length, 1); + // WAL journal mode should be set on this database. + Assert.equal(rows[0].getResultByIndex(0), "wal"); +}); diff --git a/toolkit/components/places/tests/migration/test_current_from_v19.js b/toolkit/components/places/tests/migration/test_current_from_v19.js new file mode 100644 index 000000000..b8d837e68 --- /dev/null +++ b/toolkit/components/places/tests/migration/test_current_from_v19.js @@ -0,0 +1,42 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +const ANNO_LEGACYGUID = "placesInternal/GUID"; + +var getTotalGuidAnnotationsCount = Task.async(function* (db) { + let rows = yield db.execute( + `SELECT count(*) + FROM moz_items_annos a + JOIN moz_anno_attributes b ON a.anno_attribute_id = b.id + WHERE b.name = :attr_name + `, { attr_name: ANNO_LEGACYGUID }); + return rows[0].getResultByIndex(0); +}); + +add_task(function* setup() { + yield setupPlacesDatabase("places_v19.sqlite"); +}); + +add_task(function* initial_state() { + let path = OS.Path.join(OS.Constants.Path.profileDir, DB_FILENAME); + let db = yield Sqlite.openConnection({ path: path }); + + Assert.equal((yield getTotalGuidAnnotationsCount(db)), 1, + "There should be 1 obsolete guid annotation"); + yield db.close(); +}); + +add_task(function* database_is_valid() { + Assert.equal(PlacesUtils.history.databaseStatus, + PlacesUtils.history.DATABASE_STATUS_UPGRADED); + + let db = yield PlacesUtils.promiseDBConnection(); + Assert.equal((yield db.getSchemaVersion()), CURRENT_SCHEMA_VERSION); +}); + +add_task(function* test_bookmark_guid_annotation_removed() +{ + let db = yield PlacesUtils.promiseDBConnection(); + Assert.equal((yield getTotalGuidAnnotationsCount(db)), 0, + "There should be no more obsolete GUID annotations."); +}); diff --git a/toolkit/components/places/tests/migration/test_current_from_v24.js b/toolkit/components/places/tests/migration/test_current_from_v24.js new file mode 100644 index 000000000..0561b4922 --- /dev/null +++ b/toolkit/components/places/tests/migration/test_current_from_v24.js @@ -0,0 +1,36 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +add_task(function* setup() { + yield setupPlacesDatabase("places_v24.sqlite"); +}); + +add_task(function* database_is_valid() { + Assert.equal(PlacesUtils.history.databaseStatus, + PlacesUtils.history.DATABASE_STATUS_UPGRADED); + + let db = yield PlacesUtils.promiseDBConnection(); + Assert.equal((yield db.getSchemaVersion()), CURRENT_SCHEMA_VERSION); +}); + +add_task(function* test_bookmark_guid_annotation_removed() +{ + yield PlacesUtils.bookmarks.eraseEverything(); + + let db = yield PlacesUtils.promiseDBConnection(); + let m = new Map([ + [PlacesUtils.placesRootId, PlacesUtils.bookmarks.rootGuid], + [PlacesUtils.bookmarksMenuFolderId, PlacesUtils.bookmarks.menuGuid], + [PlacesUtils.toolbarFolderId, PlacesUtils.bookmarks.toolbarGuid], + [PlacesUtils.unfiledBookmarksFolderId, PlacesUtils.bookmarks.unfiledGuid], + [PlacesUtils.tagsFolderId, PlacesUtils.bookmarks.tagsGuid], + [PlacesUtils.mobileFolderId, PlacesUtils.bookmarks.mobileGuid], + ]); + + let rows = yield db.execute(`SELECT id, guid FROM moz_bookmarks`); + for (let row of rows) { + let id = row.getResultByName("id"); + let guid = row.getResultByName("guid"); + Assert.equal(m.get(id), guid, "The root folder has the correct GUID"); + } +}); diff --git a/toolkit/components/places/tests/migration/test_current_from_v25.js b/toolkit/components/places/tests/migration/test_current_from_v25.js new file mode 100644 index 000000000..b066975fc --- /dev/null +++ b/toolkit/components/places/tests/migration/test_current_from_v25.js @@ -0,0 +1,30 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +add_task(function* setup() { + yield setupPlacesDatabase("places_v25.sqlite"); +}); + +add_task(function* database_is_valid() { + Assert.equal(PlacesUtils.history.databaseStatus, + PlacesUtils.history.DATABASE_STATUS_UPGRADED); + + let db = yield PlacesUtils.promiseDBConnection(); + Assert.equal((yield db.getSchemaVersion()), CURRENT_SCHEMA_VERSION); +}); + +add_task(function* test_dates_rounded() { + let root = yield PlacesUtils.promiseBookmarksTree(); + function ensureDates(node) { + // When/if promiseBookmarksTree returns these as Date objects, switch this + // test to use getItemDateAdded and getItemLastModified. And when these + // methods are removed, this test can be eliminated altogether. + Assert.strictEqual(typeof(node.dateAdded), "number"); + Assert.strictEqual(typeof(node.lastModified), "number"); + Assert.strictEqual(node.dateAdded % 1000, 0); + Assert.strictEqual(node.lastModified % 1000, 0); + if ("children" in node) + node.children.forEach(ensureDates); + } + ensureDates(root); +}); diff --git a/toolkit/components/places/tests/migration/test_current_from_v26.js b/toolkit/components/places/tests/migration/test_current_from_v26.js new file mode 100644 index 000000000..7ff4bc352 --- /dev/null +++ b/toolkit/components/places/tests/migration/test_current_from_v26.js @@ -0,0 +1,98 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +add_task(function* setup() { + yield setupPlacesDatabase("places_v26.sqlite"); + // Setup database contents to be migrated. + let path = OS.Path.join(OS.Constants.Path.profileDir, DB_FILENAME); + let db = yield Sqlite.openConnection({ path }); + // Add pages. + yield db.execute(`INSERT INTO moz_places (url, guid) + VALUES ("http://test1.com/", "test1_______") + , ("http://test2.com/", "test2_______") + , ("http://test3.com/", "test3_______") + `); + // Add keywords. + yield db.execute(`INSERT INTO moz_keywords (keyword) + VALUES ("kw1") + , ("kw2") + , ("kw3") + , ("kw4") + , ("kw5") + `); + // Add bookmarks. + let now = Date.now() * 1000; + let index = 0; + yield db.execute(`INSERT INTO moz_bookmarks (type, fk, parent, position, dateAdded, lastModified, keyword_id, guid) + VALUES (1, (SELECT id FROM moz_places WHERE guid = 'test1_______'), 3, ${index++}, ${now}, ${now}, + (SELECT id FROM moz_keywords WHERE keyword = 'kw1'), "bookmark1___") + /* same uri, different keyword */ + , (1, (SELECT id FROM moz_places WHERE guid = 'test1_______'), 3, ${index++}, ${now}, ${now}, + (SELECT id FROM moz_keywords WHERE keyword = 'kw2'), "bookmark2___") + /* different uri, same keyword as 1 */ + , (1, (SELECT id FROM moz_places WHERE guid = 'test2_______'), 3, ${index++}, ${now}, ${now}, + (SELECT id FROM moz_keywords WHERE keyword = 'kw1'), "bookmark3___") + /* same uri, same keyword as 1 */ + , (1, (SELECT id FROM moz_places WHERE guid = 'test1_______'), 3, ${index++}, ${now}, ${now}, + (SELECT id FROM moz_keywords WHERE keyword = 'kw1'), "bookmark4___") + /* same uri, same keyword as 2 */ + , (1, (SELECT id FROM moz_places WHERE guid = 'test2_______'), 3, ${index++}, ${now}, ${now}, + (SELECT id FROM moz_keywords WHERE keyword = 'kw2'), "bookmark5___") + /* different uri, same keyword as 1 */ + , (1, (SELECT id FROM moz_places WHERE guid = 'test1_______'), 3, ${index++}, ${now}, ${now}, + (SELECT id FROM moz_keywords WHERE keyword = 'kw3'), "bookmark6___") + , (1, (SELECT id FROM moz_places WHERE guid = 'test3_______'), 3, ${index++}, ${now}, ${now}, + (SELECT id FROM moz_keywords WHERE keyword = 'kw4'), "bookmark7___") + /* same uri and post_data as bookmark7, different keyword */ + , (1, (SELECT id FROM moz_places WHERE guid = 'test3_______'), 3, ${index++}, ${now}, ${now}, + (SELECT id FROM moz_keywords WHERE keyword = 'kw5'), "bookmark8___") + `); + // Add postData. + yield db.execute(`INSERT INTO moz_anno_attributes (name) + VALUES ("bookmarkProperties/POSTData") + , ("someOtherAnno")`); + yield db.execute(`INSERT INTO moz_items_annos(anno_attribute_id, item_id, content) + VALUES ((SELECT id FROM moz_anno_attributes where name = "bookmarkProperties/POSTData"), + (SELECT id FROM moz_bookmarks WHERE guid = "bookmark3___"), "postData1") + , ((SELECT id FROM moz_anno_attributes where name = "bookmarkProperties/POSTData"), + (SELECT id FROM moz_bookmarks WHERE guid = "bookmark5___"), "postData2") + , ((SELECT id FROM moz_anno_attributes where name = "someOtherAnno"), + (SELECT id FROM moz_bookmarks WHERE guid = "bookmark5___"), "zzzzzzzzzz") + , ((SELECT id FROM moz_anno_attributes where name = "bookmarkProperties/POSTData"), + (SELECT id FROM moz_bookmarks WHERE guid = "bookmark7___"), "postData3") + , ((SELECT id FROM moz_anno_attributes where name = "bookmarkProperties/POSTData"), + (SELECT id FROM moz_bookmarks WHERE guid = "bookmark8___"), "postData3") + `); + yield db.close(); +}); + +add_task(function* database_is_valid() { + Assert.equal(PlacesUtils.history.databaseStatus, + PlacesUtils.history.DATABASE_STATUS_UPGRADED); + + let db = yield PlacesUtils.promiseDBConnection(); + Assert.equal((yield db.getSchemaVersion()), CURRENT_SCHEMA_VERSION); +}); + +add_task(function* test_keywords() { + // When 2 urls have the same keyword, if one has postData it will be + // preferred. + let entry1 = yield PlacesUtils.keywords.fetch("kw1"); + Assert.equal(entry1.url.href, "http://test2.com/"); + Assert.equal(entry1.postData, "postData1"); + let entry2 = yield PlacesUtils.keywords.fetch("kw2"); + Assert.equal(entry2.url.href, "http://test2.com/"); + Assert.equal(entry2.postData, "postData2"); + let entry3 = yield PlacesUtils.keywords.fetch("kw3"); + Assert.equal(entry3.url.href, "http://test1.com/"); + Assert.equal(entry3.postData, null); + let entry4 = yield PlacesUtils.keywords.fetch("kw4"); + Assert.equal(entry4, null); + let entry5 = yield PlacesUtils.keywords.fetch("kw5"); + Assert.equal(entry5.url.href, "http://test3.com/"); + Assert.equal(entry5.postData, "postData3"); + + Assert.equal((yield foreign_count("http://test1.com/")), 5); // 4 bookmark2 + 1 keywords + Assert.equal((yield foreign_count("http://test2.com/")), 4); // 2 bookmark2 + 2 keywords + Assert.equal((yield foreign_count("http://test3.com/")), 3); // 2 bookmark2 + 1 keywords +}); diff --git a/toolkit/components/places/tests/migration/test_current_from_v27.js b/toolkit/components/places/tests/migration/test_current_from_v27.js new file mode 100644 index 000000000..1675901eb --- /dev/null +++ b/toolkit/components/places/tests/migration/test_current_from_v27.js @@ -0,0 +1,77 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +add_task(function* setup() { + yield setupPlacesDatabase("places_v27.sqlite"); + // Setup database contents to be migrated. + let path = OS.Path.join(OS.Constants.Path.profileDir, DB_FILENAME); + let db = yield Sqlite.openConnection({ path }); + // Add pages. + yield db.execute(`INSERT INTO moz_places (url, guid) + VALUES ("http://test1.com/", "test1_______") + , ("http://test2.com/", "test2_______") + `); + // Add keywords. + yield db.execute(`INSERT INTO moz_keywords (keyword, place_id, post_data) + VALUES ("kw1", (SELECT id FROM moz_places WHERE guid = "test2_______"), "broken data") + , ("kw2", (SELECT id FROM moz_places WHERE guid = "test2_______"), NULL) + , ("kw3", (SELECT id FROM moz_places WHERE guid = "test1_______"), "zzzzzzzzzz") + `); + // Add bookmarks. + let now = Date.now() * 1000; + let index = 0; + yield db.execute(`INSERT INTO moz_bookmarks (type, fk, parent, position, dateAdded, lastModified, keyword_id, guid) + VALUES (1, (SELECT id FROM moz_places WHERE guid = "test1_______"), 3, ${index++}, ${now}, ${now}, + (SELECT id FROM moz_keywords WHERE keyword = "kw1"), "bookmark1___") + /* same uri, different keyword */ + , (1, (SELECT id FROM moz_places WHERE guid = "test1_______"), 3, ${index++}, ${now}, ${now}, + (SELECT id FROM moz_keywords WHERE keyword = "kw2"), "bookmark2___") + /* different uri, same keyword as 1 */ + , (1, (SELECT id FROM moz_places WHERE guid = "test2_______"), 3, ${index++}, ${now}, ${now}, + (SELECT id FROM moz_keywords WHERE keyword = "kw1"), "bookmark3___") + /* same uri, same keyword as 1 */ + , (1, (SELECT id FROM moz_places WHERE guid = "test1_______"), 3, ${index++}, ${now}, ${now}, + (SELECT id FROM moz_keywords WHERE keyword = "kw1"), "bookmark4___") + /* same uri, same keyword as 2 */ + , (1, (SELECT id FROM moz_places WHERE guid = "test2_______"), 3, ${index++}, ${now}, ${now}, + (SELECT id FROM moz_keywords WHERE keyword = "kw2"), "bookmark5___") + /* different uri, same keyword as 1 */ + , (1, (SELECT id FROM moz_places WHERE guid = "test1_______"), 3, ${index++}, ${now}, ${now}, + (SELECT id FROM moz_keywords WHERE keyword = "kw3"), "bookmark6___") + `); + // Add postData. + yield db.execute(`INSERT INTO moz_anno_attributes (name) + VALUES ("bookmarkProperties/POSTData") + , ("someOtherAnno")`); + yield db.execute(`INSERT INTO moz_items_annos(anno_attribute_id, item_id, content) + VALUES ((SELECT id FROM moz_anno_attributes where name = "bookmarkProperties/POSTData"), + (SELECT id FROM moz_bookmarks WHERE guid = "bookmark3___"), "postData1") + , ((SELECT id FROM moz_anno_attributes where name = "bookmarkProperties/POSTData"), + (SELECT id FROM moz_bookmarks WHERE guid = "bookmark5___"), "postData2") + , ((SELECT id FROM moz_anno_attributes where name = "someOtherAnno"), + (SELECT id FROM moz_bookmarks WHERE guid = "bookmark5___"), "zzzzzzzzzz") + `); + yield db.close(); +}); + +add_task(function* database_is_valid() { + Assert.equal(PlacesUtils.history.databaseStatus, + PlacesUtils.history.DATABASE_STATUS_UPGRADED); + + let db = yield PlacesUtils.promiseDBConnection(); + Assert.equal((yield db.getSchemaVersion()), CURRENT_SCHEMA_VERSION); +}); + +add_task(function* test_keywords() { + // When 2 urls have the same keyword, if one has postData it will be + // preferred. + let entry1 = yield PlacesUtils.keywords.fetch("kw1"); + Assert.equal(entry1.url.href, "http://test2.com/"); + Assert.equal(entry1.postData, "postData1"); + let entry2 = yield PlacesUtils.keywords.fetch("kw2"); + Assert.equal(entry2.url.href, "http://test2.com/"); + Assert.equal(entry2.postData, "postData2"); + let entry3 = yield PlacesUtils.keywords.fetch("kw3"); + Assert.equal(entry3.url.href, "http://test1.com/"); + Assert.equal(entry3.postData, null); +}); diff --git a/toolkit/components/places/tests/migration/test_current_from_v31.js b/toolkit/components/places/tests/migration/test_current_from_v31.js new file mode 100644 index 000000000..6b9131daa --- /dev/null +++ b/toolkit/components/places/tests/migration/test_current_from_v31.js @@ -0,0 +1,46 @@ +// Add pages. +let shorturl = "http://example.com/" + "a".repeat(1981); +let longurl = "http://example.com/" + "a".repeat(1982); +let bmurl = "http://example.com/" + "a".repeat(1983); + +add_task(function* setup() { + yield setupPlacesDatabase("places_v31.sqlite"); + // Setup database contents to be migrated. + let path = OS.Path.join(OS.Constants.Path.profileDir, DB_FILENAME); + let db = yield Sqlite.openConnection({ path }); + + yield db.execute(`INSERT INTO moz_places (url, guid, foreign_count) + VALUES (:shorturl, "test1_______", 0) + , (:longurl, "test2_______", 0) + , (:bmurl, "test3_______", 1) + `, { shorturl, longurl, bmurl }); + // Add visits. + yield db.execute(`INSERT INTO moz_historyvisits (place_id) + VALUES ((SELECT id FROM moz_places WHERE url = :shorturl)) + , ((SELECT id FROM moz_places WHERE url = :longurl)) + `, { shorturl, longurl }); + yield db.close(); +}); + +add_task(function* database_is_valid() { + Assert.equal(PlacesUtils.history.databaseStatus, + PlacesUtils.history.DATABASE_STATUS_UPGRADED); + + let db = yield PlacesUtils.promiseDBConnection(); + Assert.equal((yield db.getSchemaVersion()), CURRENT_SCHEMA_VERSION); +}); + +add_task(function* test_longurls() { + let db = yield PlacesUtils.promiseDBConnection(); + let rows = yield db.execute(`SELECT 1 FROM moz_places where url = :longurl`, + { longurl }); + Assert.equal(rows.length, 0, "Long url should have been removed"); + rows = yield db.execute(`SELECT 1 FROM moz_places where url = :shorturl`, + { shorturl }); + Assert.equal(rows.length, 1, "Short url should have been retained"); + rows = yield db.execute(`SELECT 1 FROM moz_places where url = :bmurl`, + { bmurl }); + Assert.equal(rows.length, 1, "Bookmarked url should have been retained"); + rows = yield db.execute(`SELECT count(*) FROM moz_historyvisits`); + Assert.equal(rows.length, 1, "Orphan visists should have been removed"); +}); diff --git a/toolkit/components/places/tests/migration/test_current_from_v34.js b/toolkit/components/places/tests/migration/test_current_from_v34.js new file mode 100644 index 000000000..115bcec67 --- /dev/null +++ b/toolkit/components/places/tests/migration/test_current_from_v34.js @@ -0,0 +1,141 @@ +Cu.importGlobalProperties(["URL", "crypto"]); + +const { TYPE_BOOKMARK, TYPE_FOLDER } = Ci.nsINavBookmarksService; +const { EXPIRE_NEVER, TYPE_INT32 } = Ci.nsIAnnotationService; + +function makeGuid() { + return ChromeUtils.base64URLEncode(crypto.getRandomValues(new Uint8Array(9)), { + pad: false, + }); +} + +// These queries are more or less copied directly from Bookmarks.jsm, but +// operate on the old, pre-migration DB. We can't use any of the Places SQL +// functions yet, because those are only registered for the main connection. +function* insertItem(db, info) { + let [parentInfo] = yield db.execute(` + SELECT b.id, (SELECT count(*) FROM moz_bookmarks + WHERE parent = b.id) AS childCount + FROM moz_bookmarks b + WHERE b.guid = :parentGuid`, + { parentGuid: info.parentGuid }); + + let guid = makeGuid(); + yield db.execute(` + INSERT INTO moz_bookmarks (fk, type, parent, position, guid) + VALUES ((SELECT id FROM moz_places WHERE url = :url), + :type, :parent, :position, :guid)`, + { url: info.url || "nonexistent", type: info.type, guid, + // Just append items. + position: parentInfo.getResultByName("childCount"), + parent: parentInfo.getResultByName("id") }); + + let id = (yield db.execute(` + SELECT id FROM moz_bookmarks WHERE guid = :guid LIMIT 1`, + { guid }))[0].getResultByName("id"); + + return { id, guid }; +} + +function insertBookmark(db, info) { + return db.executeTransaction(function* () { + if (info.type == TYPE_BOOKMARK) { + // We don't have access to the hash function here, so we omit the + // `url_hash` column. These will be fixed up automatically during + // migration. + let url = new URL(info.url); + let placeGuid = makeGuid(); + yield db.execute(` + INSERT INTO moz_places (url, rev_host, hidden, frecency, guid) + VALUES (:url, :rev_host, 0, -1, :guid)`, + { url: url.href, guid: placeGuid, + rev_host: PlacesUtils.getReversedHost(url) }); + } + return yield* insertItem(db, info); + }); +} + +function* insertAnno(db, itemId, name, value) { + yield db.execute(`INSERT OR IGNORE INTO moz_anno_attributes (name) + VALUES (:name)`, { name }); + yield db.execute(` + INSERT INTO moz_items_annos + (item_id, anno_attribute_id, content, flags, + expiration, type, dateAdded, lastModified) + VALUES (:itemId, + (SELECT id FROM moz_anno_attributes + WHERE name = :name), + 1, 0, :expiration, :type, 0, 0) + `, { itemId, name, expiration: EXPIRE_NEVER, type: TYPE_INT32 }); +} + +function insertMobileFolder(db) { + return db.executeTransaction(function* () { + let item = yield* insertItem(db, { + type: TYPE_FOLDER, + parentGuid: "root________", + }); + yield* insertAnno(db, item.id, "mobile/bookmarksRoot", 1); + return item; + }); +} + +var mobileId, mobileGuid, fxGuid; +var dupeMobileId, dupeMobileGuid, tbGuid; + +add_task(function* setup() { + yield setupPlacesDatabase("places_v34.sqlite"); + // Setup database contents to be migrated. + let path = OS.Path.join(OS.Constants.Path.profileDir, DB_FILENAME); + let db = yield Sqlite.openConnection({ path }); + + do_print("Create mobile folder with bookmarks"); + ({ id: mobileId, guid: mobileGuid } = yield insertMobileFolder(db)); + ({ guid: fxGuid } = yield insertBookmark(db, { + type: TYPE_BOOKMARK, + url: "http://getfirefox.com", + parentGuid: mobileGuid, + })); + + // We should only have one mobile folder, but, in case an old version of Sync + // did the wrong thing and created multiple mobile folders, we should merge + // their contents into the new mobile root. + do_print("Create second mobile folder with different bookmarks"); + ({ id: dupeMobileId, guid: dupeMobileGuid } = yield insertMobileFolder(db)); + ({ guid: tbGuid } = yield insertBookmark(db, { + type: TYPE_BOOKMARK, + url: "http://getthunderbird.com", + parentGuid: dupeMobileGuid, + })); + + yield db.close(); +}); + +add_task(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 = yield PlacesUtils.promiseDBConnection(); + Assert.equal((yield db.getSchemaVersion()), CURRENT_SCHEMA_VERSION); +}); + +add_task(function* test_mobile_root() { + let fxBmk = yield PlacesUtils.bookmarks.fetch(fxGuid); + equal(fxBmk.parentGuid, PlacesUtils.bookmarks.mobileGuid, + "Firefox bookmark should be moved to new mobile root"); + equal(fxBmk.index, 0, "Firefox bookmark should be first child of new root"); + + let tbBmk = yield PlacesUtils.bookmarks.fetch(tbGuid); + equal(tbBmk.parentGuid, PlacesUtils.bookmarks.mobileGuid, + "Thunderbird bookmark should be moved to new mobile root"); + equal(tbBmk.index, 1, + "Thunderbird bookmark should be second child of new root"); + + let mobileRootId = PlacesUtils.promiseItemId( + PlacesUtils.bookmarks.mobileGuid); + let annoItemIds = PlacesUtils.annotations.getItemsWithAnnotation( + PlacesUtils.MOBILE_ROOT_ANNO, {}); + deepEqual(annoItemIds, [mobileRootId], + "Only mobile root should have mobile anno"); +}); diff --git a/toolkit/components/places/tests/migration/test_current_from_v34_no_roots.js b/toolkit/components/places/tests/migration/test_current_from_v34_no_roots.js new file mode 100644 index 000000000..871fe8993 --- /dev/null +++ b/toolkit/components/places/tests/migration/test_current_from_v34_no_roots.js @@ -0,0 +1,21 @@ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +add_task(function* setup() { + yield setupPlacesDatabase("places_v34.sqlite"); + // Setup database contents to be migrated. + let path = OS.Path.join(OS.Constants.Path.profileDir, DB_FILENAME); + let db = yield Sqlite.openConnection({ path }); + // Remove all the roots. + yield db.execute("DELETE FROM moz_bookmarks"); + yield db.close(); +}); + +add_task(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 = yield PlacesUtils.promiseDBConnection(); + Assert.equal((yield db.getSchemaVersion()), CURRENT_SCHEMA_VERSION); +}); diff --git a/toolkit/components/places/tests/migration/test_current_from_v6.js b/toolkit/components/places/tests/migration/test_current_from_v6.js new file mode 100644 index 000000000..a3f9dc229 --- /dev/null +++ b/toolkit/components/places/tests/migration/test_current_from_v6.js @@ -0,0 +1,38 @@ +/* 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(function* setup() { + yield setupPlacesDatabase("places_v6.sqlite"); +}); + +add_task(function* corrupt_database_not_exists() { + let corruptPath = OS.Path.join(OS.Constants.Path.profileDir, + "places.sqlite.corrupt"); + Assert.ok(!(yield OS.File.exists(corruptPath)), "Corrupt file should not exist"); +}); + +add_task(function* database_is_valid() { + Assert.equal(PlacesUtils.history.databaseStatus, + PlacesUtils.history.DATABASE_STATUS_CORRUPT); + + let db = yield PlacesUtils.promiseDBConnection(); + Assert.equal((yield db.getSchemaVersion()), CURRENT_SCHEMA_VERSION); +}); + +add_task(function* check_columns() { + // Check the database has been replaced, these would throw otherwise. + let db = yield PlacesUtils.promiseDBConnection(); + yield db.execute("SELECT frecency from moz_places"); + yield db.execute("SELECT 1 from moz_inputhistory"); +}); + +add_task(function* corrupt_database_exists() { + let corruptPath = OS.Path.join(OS.Constants.Path.profileDir, + "places.sqlite.corrupt"); + Assert.ok((yield OS.File.exists(corruptPath)), "Corrupt file should exist"); +}); diff --git a/toolkit/components/places/tests/migration/xpcshell.ini b/toolkit/components/places/tests/migration/xpcshell.ini new file mode 100644 index 000000000..aae0f75ee --- /dev/null +++ b/toolkit/components/places/tests/migration/xpcshell.ini @@ -0,0 +1,36 @@ +[DEFAULT] +head = head_migration.js +tail = + +support-files = + places_v6.sqlite + places_v10.sqlite + places_v11.sqlite + places_v17.sqlite + places_v19.sqlite + places_v21.sqlite + places_v22.sqlite + places_v23.sqlite + places_v24.sqlite + places_v25.sqlite + places_v26.sqlite + places_v27.sqlite + places_v28.sqlite + places_v30.sqlite + places_v31.sqlite + places_v32.sqlite + places_v33.sqlite + places_v34.sqlite + places_v35.sqlite + +[test_current_from_downgraded.js] +[test_current_from_v6.js] +[test_current_from_v11.js] +[test_current_from_v19.js] +[test_current_from_v24.js] +[test_current_from_v25.js] +[test_current_from_v26.js] +[test_current_from_v27.js] +[test_current_from_v31.js] +[test_current_from_v34.js] +[test_current_from_v34_no_roots.js] |