Source code
Revision control
Copy as Markdown
Other Tools
Test Info:
/* Any copyright is dedicated to the Public Domain.
/**
* Tests PlacesPreviews.sys.mjs
*/
const { PlacesPreviews } = ChromeUtils.importESModule(
"resource://gre/modules/PlacesPreviews.sys.mjs"
);
const { PlacesTestUtils } = ChromeUtils.importESModule(
);
/**
* Counts tombstone entries.
* @returns {integer} number of tombstone entries.
*/
async function countTombstones() {
await PlacesTestUtils.promiseAsyncUpdates();
let db = await PlacesUtils.promiseDBConnection();
return (
await db.execute("SELECT count(*) FROM moz_previews_tombstones")
)[0].getResultByIndex(0);
}
add_task(async function test_thumbnail() {
registerCleanupFunction(async () => {
await PlacesUtils.history.clear();
// Ensure tombstones table has been emptied.
await TestUtils.waitForCondition(async () => {
return (await countTombstones()) == 0;
});
PlacesPreviews.testSetDeletionTimeout(null);
});
// Sanity check initial state.
Assert.equal(await countTombstones(), 0, "There's no tombstone entries");
info("Test preview creation and storage.");
await BrowserTestUtils.withNewTab(TEST_URL1, async browser => {
await retryUpdatePreview(browser.currentURI.spec);
let filePath = PlacesPreviews.getPathForUrl(TEST_URL1);
Assert.ok(await IOUtils.exists(filePath), "The screenshot exists");
Assert.equal(
filePath.substring(filePath.lastIndexOf(".")),
PlacesPreviews.fileExtension,
"Check extension"
);
await testImageFile(filePath);
await testMozPageThumb(TEST_URL1);
});
});
add_task(async function test_page_removal() {
info("Store another preview and test page removal.");
await BrowserTestUtils.withNewTab(TEST_URL2, async browser => {
await retryUpdatePreview(browser.currentURI.spec);
let filePath = PlacesPreviews.getPathForUrl(TEST_URL2);
Assert.ok(await IOUtils.exists(filePath), "The screenshot exists");
});
// Set deletion time to a small value so it runs immediately.
PlacesPreviews.testSetDeletionTimeout(0);
info("Wait for deletion, check one preview is removed, not the other one.");
let promiseDeleted = new Promise(resolve => {
PlacesPreviews.once("places-preview-deleted", (topic, filePath) => {
resolve(filePath);
});
});
await PlacesUtils.history.remove(TEST_URL1);
let deletedFilePath = await promiseDeleted;
Assert.ok(
!(await IOUtils.exists(deletedFilePath)),
"Check deleted file has been removed"
);
info("Check tombstones table has been emptied.");
Assert.equal(await countTombstones(), 0, "There's no tombstone entries");
info("Check the other thumbnail has not been removed.");
let path = PlacesPreviews.getPathForUrl(TEST_URL2);
Assert.ok(await IOUtils.exists(path), "Check non-deleted url is still there");
await testImageFile(path);
await testMozPageThumb(TEST_URL2);
});
add_task(async function async_test_deleteOrphans() {
let path = PlacesPreviews.getPathForUrl(TEST_URL2);
Assert.ok(await IOUtils.exists(path), "Sanity check one preview exists");
// Create a file in the given path that doesn't have an entry in Places.
let fakePath = PathUtils.join(
PlacesPreviews.getPath(),
"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa." + PlacesPreviews.fileExtension
);
// File contents don't matter.
await IOUtils.writeJSON(fakePath, { test: true });
let promiseDeleted = new Promise(resolve => {
PlacesPreviews.once("places-preview-deleted", (topic, filePath) => {
resolve(filePath);
});
});
await PlacesPreviews.deleteOrphans();
let deletedFilePath = await promiseDeleted;
Assert.equal(deletedFilePath, fakePath, "Check orphan has been deleted");
Assert.equal(await countTombstones(), 0, "There's no tombstone entries left");
Assert.ok(
!(await IOUtils.exists(fakePath)),
"Ensure orphan has been deleted"
);
Assert.ok(await IOUtils.exists(path), "Ensure valid preview is still there");
});
async function testImageFile(path) {
info("Load the file and check its content type.");
const buffer = await IOUtils.read(path);
const fourcc = new TextDecoder("utf-8").decode(buffer.slice(8, 12));
Assert.equal(fourcc, "WEBP", "Check the stored preview is webp");
}
async function testMozPageThumb(url) {
info("Check moz-page-thumb protocol: " + PlacesPreviews.getPageThumbURL(url));
let { data, contentType } = await fetchImage(
PlacesPreviews.getPageThumbURL(url)
);
Assert.equal(
contentType,
PlacesPreviews.fileContentType,
"Check the content type"
);
const fourcc = data.slice(8, 12);
Assert.equal(fourcc, "WEBP", "Check the returned preview is webp");
}
function fetchImage(url) {
return new Promise((resolve, reject) => {
NetUtil.asyncFetch(
{
uri: NetUtil.newURI(url),
loadUsingSystemPrincipal: true,
contentPolicyType: Ci.nsIContentPolicy.TYPE_INTERNAL_IMAGE,
},
(input, status, request) => {
if (!Components.isSuccessCode(status)) {
reject(new Error("unable to load image"));
return;
}
try {
let data = NetUtil.readInputStreamToString(input, input.available());
let contentType = request.QueryInterface(Ci.nsIChannel).contentType;
input.close();
resolve({ data, contentType });
} catch (ex) {
reject(ex);
}
}
);
});
}
/**
* Sometimes on macOS fetching the preview fails for timeout/network reasons,
* this retries so the test doesn't intermittently fail over it.
* @param {string} url The url to store a preview for.
* @returns {Promise} resolved once a preview has been captured.
*/
function retryUpdatePreview(url) {
return TestUtils.waitForCondition(() => PlacesPreviews.update(url));
}