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

Wed, 31 Dec 2014 06:09:35 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:09:35 +0100
changeset 0
6474c204b198
permissions
-rw-r--r--

Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.

     1 /* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
     2 /* vim:set ts=2 sw=2 sts=2 et: */
     3 /* This Source Code Form is subject to the terms of the Mozilla Public
     4  * License, v. 2.0. If a copy of the MPL was not distributed with this
     5  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     7 let tests = [
     8   {
     9     desc: "nsNavHistoryFolderResultNode: Basic test, asynchronously open and " +
    10           "close container with a single child",
    12     loading: function (node, newState, oldState) {
    13       this.checkStateChanged("loading", 1);
    14       this.checkArgs("loading", node, oldState, node.STATE_CLOSED);
    15     },
    17     opened: function (node, newState, oldState) {
    18       this.checkStateChanged("opened", 1);
    19       this.checkState("loading", 1);
    20       this.checkArgs("opened", node, oldState, node.STATE_LOADING);
    22       print("Checking node children");
    23       compareArrayToResult(this.data, node);
    25       print("Closing container");
    26       node.containerOpen = false;
    27     },
    29     closed: function (node, newState, oldState) {
    30       this.checkStateChanged("closed", 1);
    31       this.checkState("opened", 1);
    32       this.checkArgs("closed", node, oldState, node.STATE_OPENED);
    33       this.success();
    34     }
    35   },
    37   {
    38     desc: "nsNavHistoryFolderResultNode: After async open and no changes, " +
    39           "second open should be synchronous",
    41     loading: function (node, newState, oldState) {
    42       this.checkStateChanged("loading", 1);
    43       this.checkState("closed", 0);
    44       this.checkArgs("loading", node, oldState, node.STATE_CLOSED);
    45     },
    47     opened: function (node, newState, oldState) {
    48       let cnt = this.checkStateChanged("opened", 1, 2);
    49       let expectOldState = cnt === 1 ? node.STATE_LOADING : node.STATE_CLOSED;
    50       this.checkArgs("opened", node, oldState, expectOldState);
    52       print("Checking node children");
    53       compareArrayToResult(this.data, node);
    55       print("Closing container");
    56       node.containerOpen = false;
    57     },
    59     closed: function (node, newState, oldState) {
    60       let cnt = this.checkStateChanged("closed", 1, 2);
    61       this.checkArgs("closed", node, oldState, node.STATE_OPENED);
    63       switch (cnt) {
    64       case 1:
    65         node.containerOpen = true;
    66         break;
    67       case 2:
    68         this.success();
    69         break;
    70       }
    71     }
    72   },
    74   {
    75     desc: "nsNavHistoryFolderResultNode: After closing container in " +
    76           "loading(), opened() should not be called",
    78     loading: function (node, newState, oldState) {
    79       this.checkStateChanged("loading", 1);
    80       this.checkArgs("loading", node, oldState, node.STATE_CLOSED);
    81       print("Closing container");
    82       node.containerOpen = false;
    83     },
    85     opened: function (node, newState, oldState) {
    86       do_throw("opened should not be called");
    87     },
    89     closed: function (node, newState, oldState) {
    90       this.checkStateChanged("closed", 1);
    91       this.checkState("loading", 1);
    92       this.checkArgs("closed", node, oldState, node.STATE_LOADING);
    93       this.success();
    94     }
    95   }
    96 ];
    99 /**
   100  * Instances of this class become the prototypes of the test objects above.
   101  * Each test can therefore use the methods of this class, or they can override
   102  * them if they want.  To run a test, call setup() and then run().
   103  */
   104 function Test() {
   105   // This maps a state name to the number of times it's been observed.
   106   this.stateCounts = {};
   107   // Promise object resolved when the next test can be run.
   108   this.deferNextTest = Promise.defer();
   109 }
   111 Test.prototype = {
   112   /**
   113    * Call this when an observer observes a container state change to sanity
   114    * check the arguments.
   115    *
   116    * @param aNewState
   117    *        The name of the new state.  Used only for printing out helpful info.
   118    * @param aNode
   119    *        The node argument passed to containerStateChanged.
   120    * @param aOldState
   121    *        The old state argument passed to containerStateChanged.
   122    * @param aExpectOldState
   123    *        The expected old state.
   124    */
   125   checkArgs: function (aNewState, aNode, aOldState, aExpectOldState) {
   126     print("Node passed on " + aNewState + " should be result.root");
   127     do_check_eq(this.result.root, aNode);
   128     print("Old state passed on " + aNewState + " should be " + aExpectOldState);
   130     // aOldState comes from xpconnect and will therefore be defined.  It may be
   131     // zero, though, so use strict equality just to make sure aExpectOldState is
   132     // also defined.
   133     do_check_true(aOldState === aExpectOldState);
   134   },
   136   /**
   137    * Call this when an observer observes a container state change.  It registers
   138    * the state change and ensures that it has been observed the given number
   139    * of times.  See checkState for parameter explanations.
   140    *
   141    * @return The number of times aState has been observed, including the new
   142    *         observation.
   143    */
   144   checkStateChanged: function (aState, aExpectedMin, aExpectedMax) {
   145     print(aState + " state change observed");
   146     if (!this.stateCounts.hasOwnProperty(aState))
   147       this.stateCounts[aState] = 0;
   148     this.stateCounts[aState]++;
   149     return this.checkState(aState, aExpectedMin, aExpectedMax);
   150   },
   152   /**
   153    * Ensures that the state has been observed the given number of times.
   154    *
   155    * @param  aState
   156    *         The name of the state.
   157    * @param  aExpectedMin
   158    *         The state must have been observed at least this number of times.
   159    * @param  aExpectedMax
   160    *         The state must have been observed at most this number of times.
   161    *         This parameter is optional.  If undefined, it's set to
   162    *         aExpectedMin.
   163    * @return The number of times aState has been observed, including the new
   164    *         observation.
   165    */
   166   checkState: function (aState, aExpectedMin, aExpectedMax) {
   167     let cnt = this.stateCounts[aState] || 0;
   168     if (aExpectedMax === undefined)
   169       aExpectedMax = aExpectedMin;
   170     if (aExpectedMin === aExpectedMax) {
   171       print(aState + " should be observed only " + aExpectedMin +
   172             " times (actual = " + cnt + ")");
   173     }
   174     else {
   175       print(aState + " should be observed at least " + aExpectedMin +
   176             " times and at most " + aExpectedMax + " times (actual = " +
   177             cnt + ")");
   178     }
   179     do_check_true(cnt >= aExpectedMin && cnt <= aExpectedMax);
   180     return cnt;
   181   },
   183   /**
   184    * Asynchronously opens the root of the test's result.
   185    */
   186   openContainer: function () {
   187     // Set up the result observer.  It delegates to this object's callbacks and
   188     // wraps them in a try-catch so that errors don't get eaten.
   189     this.observer = let (self = this) {
   190       containerStateChanged: function (container, oldState, newState) {
   191         print("New state passed to containerStateChanged() should equal the " +
   192               "container's current state");
   193         do_check_eq(newState, container.state);
   195         try {
   196           switch (newState) {
   197           case Ci.nsINavHistoryContainerResultNode.STATE_LOADING:
   198             self.loading(container, newState, oldState);
   199             break;
   200           case Ci.nsINavHistoryContainerResultNode.STATE_OPENED:
   201             self.opened(container, newState, oldState);
   202             break;
   203           case Ci.nsINavHistoryContainerResultNode.STATE_CLOSED:
   204             self.closed(container, newState, oldState);
   205             break;
   206           default:
   207             do_throw("Unexpected new state! " + newState);
   208           }
   209         }
   210         catch (err) {
   211           do_throw(err);
   212         }
   213       },
   214     };
   215     this.result.addObserver(this.observer, false);
   217     print("Opening container");
   218     this.result.root.containerOpen = true;
   219   },
   221   /**
   222    * Starts the test and returns a promise resolved when the test completes.
   223    */
   224   run: function () {
   225     this.openContainer();
   226     return this.deferNextTest.promise;
   227   },
   229   /**
   230    * This must be called before run().  It adds a bookmark and sets up the
   231    * test's result.  Override if need be.
   232    */
   233   setup: function () {
   234     // Populate the database with different types of bookmark items.
   235     this.data = DataHelper.makeDataArray([
   236       { type: "bookmark" },
   237       { type: "separator" },
   238       { type: "folder" },
   239       { type: "bookmark", uri: "place:terms=foo" }
   240     ]);
   241     yield task_populateDB(this.data);
   243     // Make a query.
   244     this.query = PlacesUtils.history.getNewQuery();
   245     this.query.setFolders([DataHelper.defaults.bookmark.parent], 1);
   246     this.opts = PlacesUtils.history.getNewQueryOptions();
   247     this.opts.asyncEnabled = true;
   248     this.result = PlacesUtils.history.executeQuery(this.query, this.opts);
   249   },
   251   /**
   252    * Call this when the test has succeeded.  It cleans up resources and starts
   253    * the next test.
   254    */
   255   success: function () {
   256     this.result.removeObserver(this.observer);
   258     // Resolve the promise object that indicates that the next test can be run.
   259     this.deferNextTest.resolve();
   260   }
   261 };
   263 /**
   264  * This makes it a little bit easier to use the functions of head_queries.js.
   265  */
   266 let DataHelper = {
   267   defaults: {
   268     bookmark: {
   269       parent: PlacesUtils.bookmarks.unfiledBookmarksFolder,
   270       uri: "http://example.com/",
   271       title: "test bookmark"
   272     },
   274     folder: {
   275       parent: PlacesUtils.bookmarks.unfiledBookmarksFolder,
   276       title: "test folder"
   277     },
   279     separator: {
   280       parent: PlacesUtils.bookmarks.unfiledBookmarksFolder
   281     }
   282   },
   284   /**
   285    * Converts an array of simple bookmark item descriptions to the more verbose
   286    * format required by task_populateDB() in head_queries.js.
   287    *
   288    * @param  aData
   289    *         An array of objects, each of which describes a bookmark item.
   290    * @return An array of objects suitable for passing to populateDB().
   291    */
   292   makeDataArray: function DH_makeDataArray(aData) {
   293     return let (self = this) aData.map(function (dat) {
   294       let type = dat.type;
   295       dat = self._makeDataWithDefaults(dat, self.defaults[type]);
   296       switch (type) {
   297       case "bookmark":
   298         return {
   299           isBookmark: true,
   300           uri: dat.uri,
   301           parentFolder: dat.parent,
   302           index: PlacesUtils.bookmarks.DEFAULT_INDEX,
   303           title: dat.title,
   304           isInQuery: true
   305         };
   306       case "separator":
   307         return {
   308           isSeparator: true,
   309           parentFolder: dat.parent,
   310           index: PlacesUtils.bookmarks.DEFAULT_INDEX,
   311           isInQuery: true
   312         };
   313       case "folder":
   314         return {
   315           isFolder: true,
   316           readOnly: false,
   317           parentFolder: dat.parent,
   318           index: PlacesUtils.bookmarks.DEFAULT_INDEX,
   319           title: dat.title,
   320           isInQuery: true
   321         };
   322       default:
   323         do_throw("Unknown data type when populating DB: " + type);
   324       }
   325     });
   326   },
   328   /**
   329    * Returns a copy of aData, except that any properties that are undefined but
   330    * defined in aDefaults are set to the corresponding values in aDefaults.
   331    *
   332    * @param  aData
   333    *         An object describing a bookmark item.
   334    * @param  aDefaults
   335    *         An object describing the default bookmark item.
   336    * @return A copy of aData with defaults values set.
   337    */
   338   _makeDataWithDefaults: function DH__makeDataWithDefaults(aData, aDefaults) {
   339     let dat = {};
   340     for (let [prop, val] in Iterator(aDefaults)) {
   341       dat[prop] = aData.hasOwnProperty(prop) ? aData[prop] : val;
   342     }
   343     return dat;
   344   }
   345 };
   347 function run_test()
   348 {
   349   run_next_test();
   350 }
   352 add_task(function test_async()
   353 {
   354   for (let [, test] in Iterator(tests)) {
   355     remove_all_bookmarks();
   357     test.__proto__ = new Test();
   358     yield test.setup();
   360     print("------ Running test: " + test.desc);
   361     yield test.run();
   362   }
   364   remove_all_bookmarks();
   365   print("All tests done, exiting");
   366 });

mercurial