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

changeset 0
6474c204b198
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/toolkit/components/places/tests/queries/head_queries.js	Wed Dec 31 06:09:35 2014 +0100
     1.3 @@ -0,0 +1,382 @@
     1.4 +/* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
     1.5 +/* vim:set ts=2 sw=2 sts=2 et: */
     1.6 +/* This Source Code Form is subject to the terms of the Mozilla Public
     1.7 + * License, v. 2.0. If a copy of the MPL was not distributed with this
     1.8 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     1.9 +
    1.10 +const Ci = Components.interfaces;
    1.11 +const Cc = Components.classes;
    1.12 +const Cr = Components.results;
    1.13 +const Cu = Components.utils;
    1.14 +
    1.15 +Cu.import("resource://gre/modules/Services.jsm");
    1.16 +
    1.17 +// Import common head.
    1.18 +let (commonFile = do_get_file("../head_common.js", false)) {
    1.19 +  let uri = Services.io.newFileURI(commonFile);
    1.20 +  Services.scriptloader.loadSubScript(uri.spec, this);
    1.21 +}
    1.22 +
    1.23 +// Put any other stuff relative to this test folder below.
    1.24 +
    1.25 +
    1.26 +// Some Useful Date constants - PRTime uses microseconds, so convert
    1.27 +const DAY_MICROSEC = 86400000000;
    1.28 +const today = Date.now() * 1000;
    1.29 +const yesterday = today - DAY_MICROSEC;
    1.30 +const lastweek = today - (DAY_MICROSEC * 7);
    1.31 +const daybefore = today - (DAY_MICROSEC * 2);
    1.32 +const tomorrow = today + DAY_MICROSEC;
    1.33 +const old = today - (DAY_MICROSEC * 3);
    1.34 +const futureday = today + (DAY_MICROSEC * 3);
    1.35 +const olderthansixmonths = today - (DAY_MICROSEC * 31 * 7);
    1.36 +
    1.37 +
    1.38 +/**
    1.39 + * Generalized function to pull in an array of objects of data and push it into
    1.40 + * the database.  It does NOT do any checking to see that the input is
    1.41 + * appropriate.  This function is an asynchronous task, it can be called using
    1.42 + * "Task.spawn" or using the "yield" function inside another task.
    1.43 + */
    1.44 +function task_populateDB(aArray)
    1.45 +{
    1.46 +  // Iterate over aArray and execute all the instructions that can be done with
    1.47 +  // asynchronous APIs, excluding those that will be done in batch mode later.
    1.48 +  for ([, data] in Iterator(aArray)) {
    1.49 +    try {
    1.50 +      // make the data object into a query data object in order to create proper
    1.51 +      // default values for anything left unspecified
    1.52 +      var qdata = new queryData(data);
    1.53 +      if (qdata.isVisit) {
    1.54 +        // Then we should add a visit for this node
    1.55 +        yield promiseAddVisits({
    1.56 +          uri: uri(qdata.uri),
    1.57 +          transition: qdata.transType,
    1.58 +          visitDate: qdata.lastVisit,
    1.59 +          referrer: qdata.referrer ? uri(qdata.referrer) : null,
    1.60 +          title: qdata.title
    1.61 +        });
    1.62 +        if (qdata.visitCount && !qdata.isDetails) {
    1.63 +          // Set a fake visit_count, this is not a real count but can be used
    1.64 +          // to test sorting by visit_count.
    1.65 +          let stmt = DBConn().createAsyncStatement(
    1.66 +            "UPDATE moz_places SET visit_count = :vc WHERE url = :url");
    1.67 +          stmt.params.vc = qdata.visitCount;
    1.68 +          stmt.params.url = qdata.uri;
    1.69 +          try {
    1.70 +            stmt.executeAsync();
    1.71 +          }
    1.72 +          catch (ex) {
    1.73 +            print("Error while setting visit_count.");
    1.74 +          }
    1.75 +          finally {
    1.76 +            stmt.finalize();
    1.77 +          }
    1.78 +        }
    1.79 +      }
    1.80 +
    1.81 +      if (qdata.isRedirect) {
    1.82 +        // This must be async to properly enqueue after the updateFrecency call
    1.83 +        // done by the visit addition.
    1.84 +        let stmt = DBConn().createAsyncStatement(
    1.85 +          "UPDATE moz_places SET hidden = 1 WHERE url = :url");
    1.86 +        stmt.params.url = qdata.uri;
    1.87 +        try {
    1.88 +          stmt.executeAsync();
    1.89 +        }
    1.90 +        catch (ex) {
    1.91 +          print("Error while setting hidden.");
    1.92 +        }
    1.93 +        finally {
    1.94 +          stmt.finalize();
    1.95 +        }
    1.96 +      }
    1.97 +
    1.98 +      if (qdata.isDetails) {
    1.99 +        // Then we add extraneous page details for testing
   1.100 +        yield promiseAddVisits({
   1.101 +          uri: uri(qdata.uri),
   1.102 +          visitDate: qdata.lastVisit,
   1.103 +          title: qdata.title
   1.104 +        });
   1.105 +      }
   1.106 +    } catch (ex) {
   1.107 +      // use the data object here in case instantiation of qdata failed
   1.108 +      LOG("Problem with this URI: " + data.uri);
   1.109 +      do_throw("Error creating database: " + ex + "\n");
   1.110 +    }
   1.111 +  }
   1.112 +
   1.113 +  // Now execute the part of the instructions made with synchronous APIs.
   1.114 +  PlacesUtils.history.runInBatchMode({
   1.115 +    runBatched: function (aUserData)
   1.116 +    {
   1.117 +      aArray.forEach(function (data)
   1.118 +      {
   1.119 +        try {
   1.120 +          // make the data object into a query data object in order to create proper
   1.121 +          // default values for anything left unspecified
   1.122 +          var qdata = new queryData(data);
   1.123 +
   1.124 +          if (qdata.markPageAsTyped) {
   1.125 +            PlacesUtils.history.markPageAsTyped(uri(qdata.uri));
   1.126 +          }
   1.127 +
   1.128 +          if (qdata.isPageAnnotation) {
   1.129 +            if (qdata.removeAnnotation)
   1.130 +              PlacesUtils.annotations.removePageAnnotation(uri(qdata.uri),
   1.131 +                                                           qdata.annoName);
   1.132 +            else {
   1.133 +              PlacesUtils.annotations.setPageAnnotation(uri(qdata.uri),
   1.134 +                                                        qdata.annoName,
   1.135 +                                                        qdata.annoVal,
   1.136 +                                                        qdata.annoFlags,
   1.137 +                                                        qdata.annoExpiration);
   1.138 +            }
   1.139 +          }
   1.140 +
   1.141 +          if (qdata.isItemAnnotation) {
   1.142 +            if (qdata.removeAnnotation)
   1.143 +              PlacesUtils.annotations.removeItemAnnotation(qdata.itemId,
   1.144 +                                                           qdata.annoName);
   1.145 +            else {
   1.146 +              PlacesUtils.annotations.setItemAnnotation(qdata.itemId,
   1.147 +                                                        qdata.annoName,
   1.148 +                                                        qdata.annoVal,
   1.149 +                                                        qdata.annoFlags,
   1.150 +                                                        qdata.annoExpiration);
   1.151 +            }
   1.152 +          }
   1.153 +
   1.154 +          if (qdata.isFolder) {
   1.155 +            let folderId = PlacesUtils.bookmarks.createFolder(qdata.parentFolder,
   1.156 +                                                              qdata.title,
   1.157 +                                                              qdata.index);
   1.158 +            if (qdata.readOnly)
   1.159 +              PlacesUtils.bookmarks.setFolderReadonly(folderId, true);
   1.160 +          }
   1.161 +
   1.162 +          if (qdata.isLivemark) {
   1.163 +            PlacesUtils.livemarks.addLivemark({ title: qdata.title
   1.164 +                                              , parentId: qdata.parentFolder
   1.165 +                                              , index: qdata.index
   1.166 +                                              , feedURI: uri(qdata.feedURI)
   1.167 +                                              , siteURI: uri(qdata.uri)
   1.168 +                                              }).then(null, do_throw);
   1.169 +          }
   1.170 +
   1.171 +          if (qdata.isBookmark) {
   1.172 +            let itemId = PlacesUtils.bookmarks.insertBookmark(qdata.parentFolder,
   1.173 +                                                              uri(qdata.uri),
   1.174 +                                                              qdata.index,
   1.175 +                                                              qdata.title);
   1.176 +            if (qdata.keyword)
   1.177 +              PlacesUtils.bookmarks.setKeywordForBookmark(itemId, qdata.keyword);
   1.178 +            if (qdata.dateAdded)
   1.179 +              PlacesUtils.bookmarks.setItemDateAdded(itemId, qdata.dateAdded);
   1.180 +            if (qdata.lastModified)
   1.181 +              PlacesUtils.bookmarks.setItemLastModified(itemId, qdata.lastModified);
   1.182 +          }
   1.183 +
   1.184 +          if (qdata.isTag) {
   1.185 +            PlacesUtils.tagging.tagURI(uri(qdata.uri), qdata.tagArray);
   1.186 +          }
   1.187 +
   1.188 +          if (qdata.isDynContainer) {
   1.189 +            PlacesUtils.bookmarks.createDynamicContainer(qdata.parentFolder,
   1.190 +                                                         qdata.title,
   1.191 +                                                         qdata.contractId,
   1.192 +                                                         qdata.index);
   1.193 +          }
   1.194 +
   1.195 +          if (qdata.isSeparator)
   1.196 +            PlacesUtils.bookmarks.insertSeparator(qdata.parentFolder, qdata.index);
   1.197 +        } catch (ex) {
   1.198 +          // use the data object here in case instantiation of qdata failed
   1.199 +          LOG("Problem with this URI: " + data.uri);
   1.200 +          do_throw("Error creating database: " + ex + "\n");
   1.201 +        }
   1.202 +      }); // End of function and array
   1.203 +    }
   1.204 +  }, null);
   1.205 +}
   1.206 +
   1.207 +
   1.208 +/**
   1.209 + * The Query Data Object - this object encapsulates data for our queries and is
   1.210 + * used to parameterize our calls to the Places APIs to put data into the
   1.211 + * database. It also has some interesting meta functions to determine which APIs
   1.212 + * should be called, and to determine if this object should show up in the
   1.213 + * resulting query.
   1.214 + * Its parameter is an object specifying which attributes you want to set.
   1.215 + * For ex:
   1.216 + * var myobj = new queryData({isVisit: true, uri:"http://mozilla.com", title="foo"});
   1.217 + * Note that it doesn't do any input checking on that object.
   1.218 + */
   1.219 +function queryData(obj) {
   1.220 +  this.isVisit = obj.isVisit ? obj.isVisit : false;
   1.221 +  this.isBookmark = obj.isBookmark ? obj.isBookmark: false;
   1.222 +  this.uri = obj.uri ? obj.uri : "";
   1.223 +  this.lastVisit = obj.lastVisit ? obj.lastVisit : today;
   1.224 +  this.referrer = obj.referrer ? obj.referrer : null;
   1.225 +  this.transType = obj.transType ? obj.transType : Ci.nsINavHistoryService.TRANSITION_TYPED;
   1.226 +  this.isRedirect = obj.isRedirect ? obj.isRedirect : false;
   1.227 +  this.isDetails = obj.isDetails ? obj.isDetails : false;
   1.228 +  this.title = obj.title ? obj.title : "";
   1.229 +  this.markPageAsTyped = obj.markPageAsTyped ? obj.markPageAsTyped : false;
   1.230 +  this.isPageAnnotation = obj.isPageAnnotation ? obj.isPageAnnotation : false;
   1.231 +  this.removeAnnotation= obj.removeAnnotation ? true : false;
   1.232 +  this.annoName = obj.annoName ? obj.annoName : "";
   1.233 +  this.annoVal = obj.annoVal ? obj.annoVal : "";
   1.234 +  this.annoFlags = obj.annoFlags ? obj.annoFlags : 0;
   1.235 +  this.annoExpiration = obj.annoExpiration ? obj.annoExpiration : 0;
   1.236 +  this.isItemAnnotation = obj.isItemAnnotation ? obj.isItemAnnotation : false;
   1.237 +  this.itemId = obj.itemId ? obj.itemId : 0;
   1.238 +  this.annoMimeType = obj.annoMimeType ? obj.annoMimeType : "";
   1.239 +  this.isTag = obj.isTag ? obj.isTag : false;
   1.240 +  this.tagArray = obj.tagArray ? obj.tagArray : null;
   1.241 +  this.isLivemark = obj.isLivemark ? obj.isLivemark : false;
   1.242 +  this.parentFolder = obj.parentFolder ? obj.parentFolder
   1.243 +                                       : PlacesUtils.placesRootId;
   1.244 +  this.feedURI = obj.feedURI ? obj.feedURI : "";
   1.245 +  this.index = obj.index ? obj.index : PlacesUtils.bookmarks.DEFAULT_INDEX;
   1.246 +  this.isFolder = obj.isFolder ? obj.isFolder : false;
   1.247 +  this.contractId = obj.contractId ? obj.contractId : "";
   1.248 +  this.lastModified = obj.lastModified ? obj.lastModified : today;
   1.249 +  this.dateAdded = obj.dateAdded ? obj.dateAdded : today;
   1.250 +  this.keyword = obj.keyword ? obj.keyword : "";
   1.251 +  this.visitCount = obj.visitCount ? obj.visitCount : 0;
   1.252 +  this.readOnly = obj.readOnly ? obj.readOnly : false;
   1.253 +  this.isSeparator = obj.hasOwnProperty("isSeparator") && obj.isSeparator;
   1.254 +
   1.255 +  // And now, the attribute for whether or not this object should appear in the
   1.256 +  // resulting query
   1.257 +  this.isInQuery = obj.isInQuery ? obj.isInQuery : false;
   1.258 +}
   1.259 +
   1.260 +// All attributes are set in the constructor above
   1.261 +queryData.prototype = { }
   1.262 +
   1.263 +
   1.264 +/**
   1.265 + * Helper function to compare an array of query objects with a result set.
   1.266 + * It assumes the array of query objects contains the SAME SORT as the result
   1.267 + * set.  It checks the the uri, title, time, and bookmarkIndex properties of
   1.268 + * the results, where appropriate.
   1.269 + */
   1.270 +function compareArrayToResult(aArray, aRoot) {
   1.271 +  LOG("Comparing Array to Results");
   1.272 +
   1.273 +  var wasOpen = aRoot.containerOpen;
   1.274 +  if (!wasOpen)
   1.275 +    aRoot.containerOpen = true;
   1.276 +
   1.277 +  // check expected number of results against actual
   1.278 +  var expectedResultCount = aArray.filter(function(aEl) { return aEl.isInQuery; }).length;
   1.279 +  if (expectedResultCount != aRoot.childCount) {
   1.280 +    // Debugging code for failures.
   1.281 +    dump_table("moz_places");
   1.282 +    dump_table("moz_historyvisits");
   1.283 +    LOG("Found children:");
   1.284 +    for (let i = 0; i < aRoot.childCount; i++) {
   1.285 +      LOG(aRoot.getChild(i).uri);
   1.286 +    }
   1.287 +    LOG("Expected:");
   1.288 +    for (let i = 0; i < aArray.length; i++) {
   1.289 +      if (aArray[i].isInQuery)
   1.290 +        LOG(aArray[i].uri);
   1.291 +    }
   1.292 +  }
   1.293 +  do_check_eq(expectedResultCount, aRoot.childCount);
   1.294 +
   1.295 +  var inQueryIndex = 0;
   1.296 +  for (var i = 0; i < aArray.length; i++) {
   1.297 +    if (aArray[i].isInQuery) {
   1.298 +      var child = aRoot.getChild(inQueryIndex);
   1.299 +      //LOG("testing testData[" + i + "] vs result[" + inQueryIndex + "]");
   1.300 +      if (!aArray[i].isFolder && !aArray[i].isSeparator) {
   1.301 +        LOG("testing testData[" + aArray[i].uri + "] vs result[" + child.uri + "]");
   1.302 +        if (aArray[i].uri != child.uri) {
   1.303 +          dump_table("moz_places");
   1.304 +          do_throw("Expected " + aArray[i].uri + " found " + child.uri);
   1.305 +        }
   1.306 +      }
   1.307 +      if (!aArray[i].isSeparator && aArray[i].title != child.title)
   1.308 +        do_throw("Expected " + aArray[i].title + " found " + child.title);
   1.309 +      if (aArray[i].hasOwnProperty("lastVisit") &&
   1.310 +          aArray[i].lastVisit != child.time)
   1.311 +        do_throw("Expected " + aArray[i].lastVisit + " found " + child.time);
   1.312 +      if (aArray[i].hasOwnProperty("index") &&
   1.313 +          aArray[i].index != PlacesUtils.bookmarks.DEFAULT_INDEX &&
   1.314 +          aArray[i].index != child.bookmarkIndex)
   1.315 +        do_throw("Expected " + aArray[i].index + " found " + child.bookmarkIndex);
   1.316 +
   1.317 +      inQueryIndex++;
   1.318 +    }
   1.319 +  }
   1.320 +
   1.321 +  if (!wasOpen)
   1.322 +    aRoot.containerOpen = false;
   1.323 +  LOG("Comparing Array to Results passes");
   1.324 +}
   1.325 +
   1.326 +
   1.327 +/**
   1.328 + * Helper function to check to see if one object either is or is not in the
   1.329 + * result set.  It can accept either a queryData object or an array of queryData
   1.330 + * objects.  If it gets an array, it only compares the first object in the array
   1.331 + * to see if it is in the result set.
   1.332 + * Returns: True if item is in query set, and false if item is not in query set
   1.333 + *          If input is an array, returns True if FIRST object in array is in
   1.334 + *          query set.  To compare entire array, use the function above.
   1.335 + */
   1.336 +function isInResult(aQueryData, aRoot) {
   1.337 +  var rv = false;
   1.338 +  var uri;
   1.339 +  var wasOpen = aRoot.containerOpen;
   1.340 +  if (!wasOpen)
   1.341 +    aRoot.containerOpen = true;
   1.342 +
   1.343 +  // If we have an array, pluck out the first item. If an object, pluc out the
   1.344 +  // URI, we just compare URI's here.
   1.345 +  if ("uri" in aQueryData) {
   1.346 +    uri = aQueryData.uri;
   1.347 +  } else {
   1.348 +    uri = aQueryData[0].uri;
   1.349 +  }
   1.350 +
   1.351 +  for (var i=0; i < aRoot.childCount; i++) {
   1.352 +    if (uri == aRoot.getChild(i).uri) {
   1.353 +      rv = true;
   1.354 +      break;
   1.355 +    }
   1.356 +  }
   1.357 +  if (!wasOpen)
   1.358 +    aRoot.containerOpen = false;
   1.359 +  return rv;
   1.360 +}
   1.361 +
   1.362 +
   1.363 +/**
   1.364 + * A nice helper function for debugging things. It LOGs the contents of a
   1.365 + * result set.
   1.366 + */
   1.367 +function displayResultSet(aRoot) {
   1.368 +
   1.369 +  var wasOpen = aRoot.containerOpen;
   1.370 +  if (!wasOpen)
   1.371 +    aRoot.containerOpen = true;
   1.372 +
   1.373 +  if (!aRoot.hasChildren) {
   1.374 +    // Something wrong? Empty result set?
   1.375 +    LOG("Result Set Empty");
   1.376 +    return;
   1.377 +  }
   1.378 +
   1.379 +  for (var i=0; i < aRoot.childCount; ++i) {
   1.380 +    LOG("Result Set URI: " + aRoot.getChild(i).uri + "   Title: " +
   1.381 +        aRoot.getChild(i).title + "   Visit Time: " + aRoot.getChild(i).time);
   1.382 +  }
   1.383 +  if (!wasOpen)
   1.384 +    aRoot.containerOpen = false;
   1.385 +}

mercurial