gecko/toolkit/components/places/tests/queries/head_queries.js

420 lines
17 KiB
JavaScript
Raw Normal View History

/* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim:set ts=2 sw=2 sts=2 et: */
/* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is Places Test Code.
*
* The Initial Developer of the Original Code is Mozilla Foundation
* Portions created by the Initial Developer are Copyright (C) 2008
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Clint Talbert <ctalbert@mozilla.com>
*
* Alternatively, the contents of this file may be used under the terms of
* either the GNU General Public License Version 2 or later (the "GPL"), or
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above. If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL, and not to allow others to
* use your version of this file under the terms of the MPL, indicate your
* decision by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL or the LGPL. If you do not delete
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the MPL, the GPL or the LGPL.
*
* ***** END LICENSE BLOCK ***** */
const Ci = Components.interfaces;
const Cc = Components.classes;
const Cr = Components.results;
const Cu = Components.utils;
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.
// Some Useful Date constants - PRTime uses microseconds, so convert
const DAY_MICROSEC = 86400000000;
const today = Date.now() * 1000;
const yesterday = today - DAY_MICROSEC;
const lastweek = today - (DAY_MICROSEC * 7);
const daybefore = today - (DAY_MICROSEC * 2);
const tomorrow = today + DAY_MICROSEC;
const old = today - (DAY_MICROSEC * 3);
const futureday = today + (DAY_MICROSEC * 3);
/**
* Generalized function to pull in an array of objects of data and push it into
* the database. It does NOT do any checking to see that the input is
* appropriate.
*/
function populateDB(aArray) {
aArray.forEach(function(data) {
dump_table("moz_bookmarks");
try {
// make the data object into a query data object in order to create proper
// default values for anything left unspecified
var qdata = new queryData(data);
if (qdata.isVisit) {
// Then we should add a visit for this node
var referrer = qdata.referrer ? uri(qdata.referrer) : null;
var visitId = PlacesUtils.history.addVisit(uri(qdata.uri), qdata.lastVisit,
referrer, qdata.transType,
qdata.isRedirect, qdata.sessionID);
do_check_true(visitId > 0);
if (qdata.title && !qdata.isDetails) {
// Set the page title synchronously, otherwise setPageTitle is LAZY.
let stmt = DBConn().createStatement(
"UPDATE moz_places_view SET title = :title WHERE url = :url"
);
stmt.params.title = qdata.title;
stmt.params.url = qdata.uri;
try {
stmt.execute();
// Force a notification so results are updated.
PlacesUtils.history.runInBatchMode({runBatched: function(){}}, null);
}
finally {
stmt.finalize();
}
}
if (qdata.visitCount && !qdata.isDetails) {
// Set a fake visit_count, this is not a real count but can be used
// to test sorting by visit_count.
let stmt = DBConn().createStatement(
"UPDATE moz_places_view SET visit_count = :vc WHERE url = :url");
stmt.params.vc = qdata.visitCount;
stmt.params.url = qdata.uri;
try {
stmt.execute();
// Force a notification so results are updated.
PlacesUtils.history.runInBatchMode({runBatched: function(){}}, null);
}
finally {
stmt.finalize();
}
}
}
if (qdata.isDetails) {
// Then we add extraneous page details for testing
PlacesUtils.history.addPageWithDetails(uri(qdata.uri),
qdata.title, qdata.lastVisit);
}
if (qdata.markPageAsTyped){
PlacesUtils.bhistory.markPageAsTyped(uri(qdata.uri));
}
if (qdata.hidePage){
PlacesUtils.bhistory.hidePage(uri(qdata.uri));
}
if (qdata.isPageAnnotation) {
if (qdata.removeAnnotation)
PlacesUtils.annotations.removePageAnnotation(uri(qdata.uri),
qdata.annoName);
else {
PlacesUtils.annotations.setPageAnnotation(uri(qdata.uri),
qdata.annoName,
qdata.annoVal,
qdata.annoFlags,
qdata.annoExpiration);
}
}
if (qdata.isItemAnnotation) {
if (qdata.removeAnnotation)
PlacesUtils.annotations.removeItemAnnotation(qdata.itemId,
qdata.annoName);
else {
PlacesUtils.annotations.setItemAnnotation(qdata.itemId,
qdata.annoName,
qdata.annoVal,
qdata.annoFlags,
qdata.annoExpiration);
}
}
if (qdata.isPageBinaryAnnotation) {
if (qdata.removeAnnotation)
PlacesUtils.annotations.removePageAnnotation(uri(qdata.uri),
qdata.annoName);
else {
PlacesUtils.annotations.setPageAnnotationBinary(uri(qdata.uri),
qdata.annoName,
qdata.binarydata,
qdata.binaryDataLength,
qdata.annoMimeType,
qdata.annoFlags,
qdata.annoExpiration);
}
}
if (qdata.isItemBinaryAnnotation) {
if (qdata.removeAnnotation)
PlacesUtils.annotations.removeItemAnnotation(qdata.itemId,
qdata.annoName);
else {
PlacesUtils.annotations.setItemAnnotationBinary(qdata.itemId,
qdata.annoName,
qdata.binaryData,
qdata.binaryDataLength,
qdata.annoMimeType,
qdata.annoFlags,
qdata.annoExpiration);
}
}
if (qdata.isFavicon) {
// Not planning on doing deep testing of favIcon service so these two
// calls should be sufficient to get favicons into the database
try {
PlacesUtils.favicons.setFaviconData(uri(qdata.faviconURI),
qdata.favicon,
qdata.faviconLen,
qdata.faviconMimeType,
qdata.faviconExpiration);
} catch (ex) {}
PlacesUtils.favicons.setFaviconUrlForPage(uri(qdata.uri),
uri(qdata.faviconURI));
}
if (qdata.isFolder) {
let folderId = PlacesUtils.bookmarks.createFolder(qdata.parentFolder,
qdata.title,
qdata.index);
if (qdata.readOnly)
PlacesUtils.bookmarks.setFolderReadonly(folderId, true);
}
if (qdata.isLivemark) {
PlacesUtils.livemarks.createLivemark(qdata.parentFolder,
qdata.title,
uri(qdata.uri),
uri(qdata.feedURI),
qdata.index);
}
if (qdata.isBookmark) {
let itemId = PlacesUtils.bookmarks.insertBookmark(qdata.parentFolder,
uri(qdata.uri),
qdata.index,
qdata.title);
if (qdata.keyword)
PlacesUtils.bookmarks.setKeywordForBookmark(itemId, qdata.keyword);
if (qdata.dateAdded)
PlacesUtils.bookmarks.setItemDateAdded(itemId, qdata.dateAdded);
if (qdata.lastModified)
PlacesUtils.bookmarks.setItemLastModified(itemId, qdata.lastModified);
}
if (qdata.isTag) {
PlacesUtils.tagging.tagURI(uri(qdata.uri), qdata.tagArray);
}
if (qdata.isDynContainer) {
PlacesUtils.bookmarks.createDynamicContainer(qdata.parentFolder,
qdata.title,
qdata.contractId,
qdata.index);
}
} catch (ex) {
// use the data object here in case instantiation of qdata failed
LOG("Problem with this URI: " + data.uri);
do_throw("Error creating database: " + ex + "\n");
}
}); // End of function and array
}
/**
* The Query Data Object - this object encapsulates data for our queries and is
* used to parameterize our calls to the Places APIs to put data into the
* database. It also has some interesting meta functions to determine which APIs
* should be called, and to determine if this object should show up in the
* resulting query.
* Its parameter is an object specifying which attributes you want to set.
* For ex:
* var myobj = new queryData({isVisit: true, uri:"http://mozilla.com", title="foo"});
* Note that it doesn't do any input checking on that object.
*/
function queryData(obj) {
this.isVisit = obj.isVisit ? obj.isVisit : false;
this.isBookmark = obj.isBookmark ? obj.isBookmark: false;
this.uri = obj.uri ? obj.uri : "";
this.lastVisit = obj.lastVisit ? obj.lastVisit : today;
this.referrer = obj.referrer ? obj.referrer : null;
this.transType = obj.transType ? obj.transType : Ci.nsINavHistoryService.TRANSITION_TYPED;
this.isRedirect = obj.isRedirect ? obj.isRedirect : false;
this.sessionID = obj.sessionID ? obj.sessionID : 0;
this.isDetails = obj.isDetails ? obj.isDetails : false;
this.title = obj.title ? obj.title : "";
this.markPageAsTyped = obj.markPageAsTyped ? obj.markPageAsTyped : false;
this.hidePage = obj.hidePage ? obj.hidePage : false;
this.isPageAnnotation = obj.isPageAnnotation ? obj.isPageAnnotation : false;
this.removeAnnotation= obj.removeAnnotation ? true : false;
this.annoName = obj.annoName ? obj.annoName : "";
this.annoVal = obj.annoVal ? obj.annoVal : "";
this.annoFlags = obj.annoFlags ? obj.annoFlags : 0;
this.annoExpiration = obj.annoExpiration ? obj.annoExpiration : 0;
this.isItemAnnotation = obj.isItemAnnotation ? obj.isItemAnnotation : false;
this.itemId = obj.itemId ? obj.itemId : 0;
this.isPageBinaryAnnotation = obj.isPageBinaryAnnotation ?
obj.isPageBinaryAnnotation : false;
this.isItemBinaryAnnotation = obj.isItemBinaryAnnotation ?
obj.isItemBinaryAnnotation : false;
this.binaryData = obj.binaryData ? obj.binaryData : null;
this.binaryDataLength = obj.binaryDataLength ? obj.binaryDataLength : 0;
this.annoMimeType = obj.annoMimeType ? obj.annoMimeType : "";
this.isTag = obj.isTag ? obj.isTag : false;
this.tagArray = obj.tagArray ? obj.tagArray : null;
this.isFavicon = obj.isFavicon ? obj.isFavicon : false;
this.faviconURI = obj.faviconURI ? obj.faviconURI : "";
this.faviconLen = obj.faviconLen ? obj.faviconLen : 0;
this.faviconMimeType = obj.faviconMimeType ? obj.faviconMimeType : "";
this.faviconExpiration = obj.faviconExpiration ?
obj.faviconExpiration : futureday;
this.isLivemark = obj.isLivemark ? obj.isLivemark : false;
this.parentFolder = obj.parentFolder ? obj.parentFolder
: PlacesUtils.placesRootId;
this.feedURI = obj.feedURI ? obj.feedURI : "";
this.index = obj.index ? obj.index : PlacesUtils.bookmarks.DEFAULT_INDEX;
this.isFolder = obj.isFolder ? obj.isFolder : false;
this.contractId = obj.contractId ? obj.contractId : "";
this.lastModified = obj.lastModified ? obj.lastModified : today;
this.dateAdded = obj.dateAdded ? obj.dateAdded : today;
this.keyword = obj.keyword ? obj.keyword : "";
this.visitCount = obj.visitCount ? obj.visitCount : 0;
this.readOnly = obj.readOnly ? obj.readOnly : false;
// And now, the attribute for whether or not this object should appear in the
// resulting query
this.isInQuery = obj.isInQuery ? obj.isInQuery : false;
}
// All attributes are set in the constructor above
queryData.prototype = { }
/**
* Helper function to compare an array of query objects with a result set.
* It assumes the array of query objects contains the SAME SORT as the result
* set. It checks the the uri, title, time, and bookmarkIndex properties of
* the results, where appropriate.
*/
function compareArrayToResult(aArray, aRoot) {
LOG("Comparing Array to Results");
var wasOpen = aRoot.containerOpen;
if (!wasOpen)
aRoot.containerOpen = true;
// check expected number of results against actual
var expectedResultCount = aArray.filter(function(aEl) { return aEl.isInQuery; }).length;
do_check_eq(expectedResultCount, aRoot.childCount);
var inQueryIndex = 0;
for (var i = 0; i < aArray.length; i++) {
if (aArray[i].isInQuery) {
var child = aRoot.getChild(inQueryIndex);
LOG("testing testData[" + i + "] vs result[" + inQueryIndex + "]");
if(!aArray[i].isFolder) {
LOG("testing testData[" + aArray[i].uri + "] vs result[" + child.uri + "]");
if (aArray[i].uri != child.uri)
do_throw("Expected " + aArray[i].uri + " found " + child.uri);
}
if (aArray[i].title != child.title)
do_throw("Expected " + aArray[i].title + " found " + child.title);
if (aArray[i].hasOwnProperty("lastVisit") &&
aArray[i].lastVisit != child.time)
do_throw("Expected " + aArray[i].lastVisit + " found " + child.time);
if (aArray[i].hasOwnProperty("index") &&
aArray[i].index != PlacesUtils.bookmarks.DEFAULT_INDEX &&
aArray[i].index != child.bookmarkIndex)
do_throw("Expected " + aArray[i].index + " found " + child.bookmarkIndex);
inQueryIndex++;
}
}
if (!wasOpen)
aRoot.containerOpen = false;
LOG("Comparing Array to Results passes");
}
/**
* Helper function to check to see if one object either is or is not in the
* result set. It can accept either a queryData object or an array of queryData
* objects. If it gets an array, it only compares the first object in the array
* to see if it is in the result set.
* Returns: True if item is in query set, and false if item is not in query set
* If input is an array, returns True if FIRST object in array is in
* query set. To compare entire array, use the function above.
*/
function isInResult(aQueryData, aRoot) {
var rv = false;
var uri;
if (!aRoot.containerOpen)
aRoot.containerOpen = true;
// If we have an array, pluck out the first item. If an object, pluc out the
// URI, we just compare URI's here.
if ("uri" in aQueryData) {
uri = aQueryData.uri;
} else {
uri = aQueryData[0].uri;
}
for (var i=0; i < aRoot.childCount; i++) {
if (uri == aRoot.getChild(i).uri) {
rv = true;
break;
}
}
return rv;
}
/**
* A nice helper function for debugging things. It LOGs the contents of a
* result set.
*/
function displayResultSet(aRoot) {
if (!aRoot.containerOpen)
aRoot.containerOpen = true;
if (!aRoot.hasChildren) {
// Something wrong? Empty result set?
LOG("Result Set Empty");
return;
}
for (var i=0; i < aRoot.childCount; ++i) {
LOG("Result Set URI: " + aRoot.getChild(i).uri + " Title: " +
aRoot.getChild(i).title + " Visit Time: " + aRoot.getChild(i).time);
}
}