Bug 900954 - Expose addons.json flush to test harness. r=felipe, a=sledru
authorIrving Reid <irving@mozilla.com>
Tue, 29 Apr 2014 10:09:30 -0400
changeset 192218 1c5aa2062579
parent 192217 ebad9b622f2e
child 192219 2919cce749c0
push id3528
push userryanvm@gmail.com
push date2014-05-07 20:48 +0000
treeherdermozilla-beta@2919cce749c0 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersfelipe, sledru
bugs900954
milestone30.0
Bug 900954 - Expose addons.json flush to test harness. r=felipe, a=sledru
toolkit/mozapps/extensions/internal/AddonRepository.jsm
toolkit/mozapps/extensions/test/xpcshell/head_addons.js
toolkit/mozapps/extensions/test/xpcshell/test_AddonRepository_cache.js
--- a/toolkit/mozapps/extensions/internal/AddonRepository.jsm
+++ b/toolkit/mozapps/extensions/internal/AddonRepository.jsm
@@ -45,17 +45,16 @@ const XMLURI_PARSE_ERROR  = "http://www.
 const API_VERSION = "1.5";
 const DEFAULT_CACHE_TYPES = "extension,theme,locale,dictionary";
 
 const KEY_PROFILEDIR        = "ProfD";
 const FILE_DATABASE         = "addons.json";
 const DB_SCHEMA             = 5;
 const DB_MIN_JSON_SCHEMA    = 5;
 const DB_BATCH_TIMEOUT_MS   = 50;
-const DB_DATA_WRITTEN_TOPIC = "addon-repository-data-written"
 
 const BLANK_DB = function() {
   return {
     addons: new Map(),
     schema: DB_SCHEMA
   };
 }
 
@@ -1425,16 +1424,17 @@ this.AddonRepository = {
     this._request.overrideMimeType("text/xml");
     if (aTimeout) {
       this._request.timeout = aTimeout;
     }
 
     this._request.addEventListener("error", aEvent => this._reportFailure(), false);
     this._request.addEventListener("timeout", aEvent => this._reportFailure(), false);
     this._request.addEventListener("load", aEvent => {
+      logger.debug("Got metadata search load event");
       let request = aEvent.target;
       let responseXML = request.responseXML;
 
       if (!responseXML || responseXML.documentElement.namespaceURI == XMLURI_PARSE_ERROR ||
           (request.status != 200 && request.status != 0)) {
         this._reportFailure();
         return;
       }
@@ -1513,18 +1513,21 @@ this.AddonRepository = {
       if (Services.vc.compare(override.minVersion, aAddonVersion) <= 0 &&
           Services.vc.compare(aAddonVersion, override.maxVersion) <= 0 &&
           Services.vc.compare(override.appMinVersion, appVersion) <= 0 &&
           Services.vc.compare(appVersion, override.appMaxVersion) <= 0) {
         return override;
       }
     }
     return null;
+  },
+
+  flush: function() {
+    return AddonDatabase.flush();
   }
-
 };
 
 var AddonDatabase = {
   // true if the database connection has been opened
   initialized: false,
   // false if there was an unrecoverable error openning the database
   databaseOk: true,
 
@@ -1675,23 +1678,24 @@ var AddonDatabase = {
    * first if initialized
    *
    * @param  aCallback
    *         An optional callback to call once complete
    */
   delete: function AD_delete(aCallback) {
     this.DB = BLANK_DB();
 
-    this.Writer.flush()
+    this._deleting = this.Writer.flush()
       .then(null, () => {})
       // shutdown(true) never rejects
       .then(() => this.shutdown(true))
       .then(() => OS.File.remove(this.jsonFile.path, {}))
       .then(null, error => logger.error("Unable to delete Addon Repository file " +
                                  this.jsonFile.path, error))
+      .then(() => this._deleting = null)
       .then(aCallback);
   },
 
   toJSON: function AD_toJSON() {
     let json = {
       schema: this.DB.schema,
       addons: []
     }
@@ -1713,16 +1717,29 @@ var AddonDatabase = {
       this.jsonFile.path,
       () => { return JSON.stringify(this); },
       DB_BATCH_TIMEOUT_MS
     );
     return this.Writer;
   },
 
   /**
+   * Flush any pending I/O on the addons.json file
+   * @return: Promise{null}
+   *          Resolves when the pending I/O (writing out or deleting
+   *          addons.json) completes
+   */
+  flush: function() {
+    if (this._deleting) {
+      return this._deleting;
+    }
+    return this.Writer.flush();
+  },
+
+  /**
    * Asynchronously retrieve all add-ons from the database, and pass it
    * to the specified callback
    *
    * @param  aCallback
    *         The callback to pass the add-ons back to
    */
   retrieveStoredData: function AD_retrieveStoredData(aCallback) {
     if (!this.initialized)
@@ -1914,18 +1931,18 @@ var AddonDatabase = {
    * Write the in-memory DB to disk, after waiting for
    * the DB_BATCH_TIMEOUT_MS timeout.
    *
    * @return Promise A promise that resolves after the
    *                 write to disk has completed.
    */
   _saveDBToDisk: function() {
     return this.Writer.saveChanges().then(
-      function() Services.obs.notifyObservers(null, DB_DATA_WRITTEN_TOPIC, null),
-      logger.error);
+      null,
+      e => logger.error("SaveDBToDisk failed", e));
   },
 
   /**
    * Make a developer object from a vanilla
    * JS object from the JSON database
    *
    * @param  aObj
    *         The JS object to use
--- a/toolkit/mozapps/extensions/test/xpcshell/head_addons.js
+++ b/toolkit/mozapps/extensions/test/xpcshell/head_addons.js
@@ -394,69 +394,95 @@ function startupManager(aAppChanged) {
 
   gInternalManager.observe(null, "addons-startup", null);
 
   // Load the add-ons list as it was after extension registration
   loadAddonsList();
 }
 
 /**
+ * Helper to spin the event loop until a promise resolves or rejects
+ */
+function loopUntilPromise(aPromise) {
+  let done = false;
+  aPromise.then(
+    () => done = true,
+    err => {
+      do_report_unexpected_exception(err);
+      done = true;
+    });
+
+  let thr = Services.tm.mainThread;
+
+  while (!done) {
+    thr.processNextEvent(true);
+  }
+}
+
+/**
  * Restarts the add-on manager as if the host application was restarted.
  *
  * @param  aNewVersion
  *         An optional new version to use for the application. Passing this
  *         will change nsIXULAppInfo.version and make the startup appear as if
  *         the application version has changed.
  */
 function restartManager(aNewVersion) {
-  shutdownManager();
-  if (aNewVersion) {
-    gAppInfo.version = aNewVersion;
-    startupManager(true);
-  }
-  else {
-    startupManager(false);
-  }
+  loopUntilPromise(promiseRestartManager(aNewVersion));
+}
+
+function promiseRestartManager(aNewVersion) {
+  return promiseShutdownManager()
+    .then(null, err => do_report_unexpected_exception(err))
+    .then(() => {
+      if (aNewVersion) {
+        gAppInfo.version = aNewVersion;
+        startupManager(true);
+      }
+      else {
+        startupManager(false);
+      }
+    });
 }
 
 function shutdownManager() {
-  if (!gInternalManager)
-    return;
+  loopUntilPromise(promiseShutdownManager());
+}
 
-  let shutdownDone = false;
-
-  Services.obs.notifyObservers(null, "quit-application-granted", null);
-  MockAsyncShutdown.hook().then(
-    () => shutdownDone = true,
-    err => shutdownDone = true);
-
-  let thr = Services.tm.mainThread;
-
-  // Wait until we observe the shutdown notifications
-  while (!shutdownDone) {
-    thr.processNextEvent(true);
+function promiseShutdownManager() {
+  if (!gInternalManager) {
+    return Promise.resolve(false);
   }
 
-  gInternalManager = null;
+  let hookErr = null;
+  Services.obs.notifyObservers(null, "quit-application-granted", null);
+  return MockAsyncShutdown.hook()
+    .then(null, err => hookErr = err)
+    .then( () => {
+      gInternalManager = null;
 
-  // Load the add-ons list as it was after application shutdown
-  loadAddonsList();
+      // Load the add-ons list as it was after application shutdown
+      loadAddonsList();
 
-  // Clear any crash report annotations
-  gAppInfo.annotations = {};
+      // Clear any crash report annotations
+      gAppInfo.annotations = {};
 
-  // Force the XPIProvider provider to reload to better
-  // simulate real-world usage.
-  let XPIscope = Components.utils.import("resource://gre/modules/addons/XPIProvider.jsm");
-  // This would be cleaner if I could get it as the rejection reason from
-  // the AddonManagerInternal.shutdown() promise
-  gXPISaveError = XPIscope.XPIProvider._shutdownError;
-  do_print("gXPISaveError set to: " + gXPISaveError);
-  AddonManagerPrivate.unregisterProvider(XPIscope.XPIProvider);
-  Components.utils.unload("resource://gre/modules/addons/XPIProvider.jsm");
+      // Force the XPIProvider provider to reload to better
+      // simulate real-world usage.
+      let XPIscope = Components.utils.import("resource://gre/modules/addons/XPIProvider.jsm");
+      // This would be cleaner if I could get it as the rejection reason from
+      // the AddonManagerInternal.shutdown() promise
+      gXPISaveError = XPIscope.XPIProvider._shutdownError;
+      do_print("gXPISaveError set to: " + gXPISaveError);
+      AddonManagerPrivate.unregisterProvider(XPIscope.XPIProvider);
+      Components.utils.unload("resource://gre/modules/addons/XPIProvider.jsm");
+      if (hookErr) {
+        throw hookErr;
+      }
+    });
 }
 
 function loadAddonsList() {
   function readDirectories(aSection) {
     var dirs = [];
     var keys = parser.getKeys(aSection);
     while (keys.hasMore()) {
       let descriptor = parser.getString(aSection, keys.getNext());
--- a/toolkit/mozapps/extensions/test/xpcshell/test_AddonRepository_cache.js
+++ b/toolkit/mozapps/extensions/test/xpcshell/test_AddonRepository_cache.js
@@ -400,41 +400,35 @@ const WITH_EXTENSION_CACHE = [{
   screenshots:            [{ get url () { return get_subfile_uri(ADDON_IDS[2], "preview.png"); } }],
   sourceURI:              NetUtil.newURI(ADDON_FILES[2]).spec
 }];
 
 
 /*
  * Trigger an AddonManager background update check
  *
- * @param  aCallback
- *         Callback to call once the background update is complete
+ * @return Promise{null}
+ *         Resolves when the background update notification is received
  */
-function trigger_background_update(aCallback) {
-  Services.obs.addObserver({
-    observe: function(aSubject, aTopic, aData) {
-      Services.obs.removeObserver(this, "addons-background-update-complete");
-      do_execute_soon(aCallback);
-    }
-  }, "addons-background-update-complete", false);
+function trigger_background_update() {
+  return new Promise((resolve, reject) => {
+    Services.obs.addObserver({
+      observe: function(aSubject, aTopic, aData) {
+        do_print("Observed " + aTopic);
+        Services.obs.removeObserver(this, "addons-background-update-complete");
+        resolve();
+      }
+    }, "addons-background-update-complete", false);
 
-  gInternalManager.notify(null);
+    gInternalManager.notify(null);
+  });
 }
 
-/*
- * Check whether or not the add-ons database exists
- *
- * @param  aExpectedExists
- *         Whether or not the database is expected to exist
- */
-function check_database_exists(aExpectedExists) {
-  let file = gProfD.clone();
-  file.append(FILE_DATABASE);
-  do_check_eq(file.exists(), aExpectedExists);
-}
+let gDBFile = gProfD.clone();
+gDBFile.append(FILE_DATABASE);
 
 /*
  * Check the actual add-on results against the expected add-on results
  *
  * @param  aActualAddons
  *         The array of actual add-ons to check
  * @param  aExpectedAddons
  *         The array of expected add-ons to check against
@@ -466,307 +460,276 @@ function check_results(aActualAddons, aE
  *
  * @param  aExpectedToFind
  *         An array of booleans representing which REPOSITORY_ADDONS are
  *         expected to be found in the cache
  * @param  aExpectedImmediately
  *         A boolean representing if results from the cache are expected
  *         immediately. Results are not immediate if the cache has not been
  *         initialized yet.
- * @param  aCallback
- *         A callback to call once the checks are complete
+ * @return Promise{null}
+ *         Resolves once the checks are complete
  */
-function check_cache(aExpectedToFind, aExpectedImmediately, aCallback) {
+function check_cache(aExpectedToFind, aExpectedImmediately) {
   do_check_eq(aExpectedToFind.length, REPOSITORY_ADDONS.length);
 
-  let pendingAddons = REPOSITORY_ADDONS.length;
-  let immediatelyFound = true;
+  let lookups = [];
 
-  for (let i = 0; i < REPOSITORY_ADDONS.length; i++) {
-    let expected = aExpectedToFind[i] ? REPOSITORY_ADDONS[i] : null;
-    AddonRepository.getCachedAddonByID(REPOSITORY_ADDONS[i].id, function(aAddon) {
-      do_check_eq(immediatelyFound, aExpectedImmediately);
-
-      if (expected == null)
-        do_check_eq(aAddon, null);
-      else
-        check_results([aAddon], [expected], true);
-
-      if (--pendingAddons == 0)
-        do_execute_soon(aCallback);
-    });
+  for (let i = 0 ; i < REPOSITORY_ADDONS.length ; i++) {
+    lookups.push(new Promise((resolve, reject) => {
+      let immediatelyFound = true;
+      let expected = aExpectedToFind[i] ? REPOSITORY_ADDONS[i] : null;
+      // can't Promise-wrap this because we're also testing whether the callback is
+      // sync or async
+      AddonRepository.getCachedAddonByID(REPOSITORY_ADDONS[i].id, function(aAddon) {
+        do_check_eq(immediatelyFound, aExpectedImmediately);
+        if (expected == null)
+          do_check_eq(aAddon, null);
+        else
+          check_results([aAddon], [expected], true);
+        resolve();
+      });
+      immediatelyFound = false;
+    }));
   }
-
-  immediatelyFound = false;
+  return Promise.all(lookups);
 }
 
 /*
- * Check an initialized cache by checking the cache, then restarting the
+ * Task to check an initialized cache by checking the cache, then restarting the
  * manager, and checking the cache. This checks that the cache is consistent
  * across manager restarts.
  *
  * @param  aExpectedToFind
  *         An array of booleans representing which REPOSITORY_ADDONS are
  *         expected to be found in the cache
- * @param  aCallback
- *         A callback to call once the checks are complete
  */
-function check_initialized_cache(aExpectedToFind, aCallback) {
-  check_cache(aExpectedToFind, true, function restart_initialized_cache() {
-    restartManager();
+function* check_initialized_cache(aExpectedToFind) {
+  yield check_cache(aExpectedToFind, true);
+  yield promiseRestartManager();
 
-    // If cache is disabled, then expect results immediately
-    let cacheEnabled = Services.prefs.getBoolPref(PREF_GETADDONS_CACHE_ENABLED);
-    check_cache(aExpectedToFind, !cacheEnabled, aCallback);
-  });
-}
-
-// Waits for the data to be written from the in-memory DB to the addons.json
-// file that is done asynchronously through OS.File
-function waitForFlushedData(aCallback) {
-  Services.obs.addObserver({
-    observe: function(aSubject, aTopic, aData) {
-      Services.obs.removeObserver(this, "addon-repository-data-written");
-      aCallback(aData == "true");
-    }
-  }, "addon-repository-data-written", false);
+  // If cache is disabled, then expect results immediately
+  let cacheEnabled = Services.prefs.getBoolPref(PREF_GETADDONS_CACHE_ENABLED);
+  yield check_cache(aExpectedToFind, !cacheEnabled);
 }
 
 function run_test() {
+  run_next_test();
+}
+
+add_task(function* setup() {
   // Setup for test
-  do_test_pending("test_AddonRepository_cache");
   createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "1", "1.9");
 
   startupManager();
 
   // Install XPI add-ons
-  installAllFiles(ADDON_FILES, function first_installs() {
-    restartManager();
+  yield promiseInstallAllFiles(ADDON_FILES);
+  yield promiseRestartManager();
 
-    gServer = new HttpServer();
-    gServer.registerDirectory("/data/", do_get_file("data"));
-    gServer.start(PORT);
-
-    do_execute_soon(run_test_1);
-  });
-}
-
-function end_test() {
-  gServer.stop(function() {do_test_finished("test_AddonRepository_cache");});
-}
+  gServer = new HttpServer();
+  gServer.registerDirectory("/data/", do_get_file("data"));
+  gServer.start(PORT);
+});
 
 // Tests AddonRepository.cacheEnabled
-function run_test_1() {
+add_task(function* run_test_1() {
   Services.prefs.setBoolPref(PREF_GETADDONS_CACHE_ENABLED, false);
   do_check_false(AddonRepository.cacheEnabled);
   Services.prefs.setBoolPref(PREF_GETADDONS_CACHE_ENABLED, true);
   do_check_true(AddonRepository.cacheEnabled);
-
-  do_execute_soon(run_test_2);
-}
+});
 
 // Tests that the cache and database begin as empty
-function run_test_2() {
-  check_database_exists(false);
-  check_cache([false, false, false], false, function(){});
-  waitForFlushedData(run_test_3);
-}
+add_task(function* run_test_2() {
+  do_check_false(gDBFile.exists());
+  yield check_cache([false, false, false], false);
+  yield AddonRepository.flush();
+});
 
 // Tests repopulateCache when the search fails
-function run_test_3() {
-  check_database_exists(true);
+add_task(function* run_test_3() {
+  do_check_true(gDBFile.exists());
   Services.prefs.setBoolPref(PREF_GETADDONS_CACHE_ENABLED, true);
   Services.prefs.setCharPref(PREF_GETADDONS_BYIDS, GETADDONS_FAILED);
 
-  AddonRepository.repopulateCache(ADDON_IDS, function test_3_repopulated() {
-    check_initialized_cache([false, false, false], run_test_4);
-  });
-}
+  yield new Promise((resolve, reject) =>
+    AddonRepository.repopulateCache(ADDON_IDS, resolve));
+  yield check_initialized_cache([false, false, false]);
+});
 
 // Tests repopulateCache when search returns no results
-function run_test_4() {
+add_task(function* run_test_4() {
   Services.prefs.setCharPref(PREF_GETADDONS_BYIDS, GETADDONS_EMPTY);
 
-  AddonRepository.repopulateCache(ADDON_IDS, function() {
-    check_initialized_cache([false, false, false], run_test_5);
-  });
-}
+  yield new Promise((resolve, reject) =>
+    AddonRepository.repopulateCache(ADDON_IDS, resolve));
+  yield check_initialized_cache([false, false, false]);
+});
 
 // Tests repopulateCache when search returns results
-function run_test_5() {
+add_task(function* run_test_5() {
   Services.prefs.setCharPref(PREF_GETADDONS_BYIDS, GETADDONS_RESULTS);
 
-  AddonRepository.repopulateCache(ADDON_IDS, function() {
-    check_initialized_cache([true, true, true], run_test_5_1);
-  });
-}
+  yield new Promise((resolve, reject) =>
+    AddonRepository.repopulateCache(ADDON_IDS, resolve));
+  yield check_initialized_cache([true, true, true]);
+});
 
 // Tests repopulateCache when caching is disabled for a single add-on
-function run_test_5_1() {
+add_task(function* run_test_5_1() {
   Services.prefs.setBoolPref(PREF_ADDON0_CACHE_ENABLED, false);
 
-  AddonRepository.repopulateCache(ADDON_IDS, function() {
-    // Reset pref for next test
-    Services.prefs.setBoolPref(PREF_ADDON0_CACHE_ENABLED, true);
-    check_initialized_cache([false, true, true], run_test_6);
-  });
-}
+  yield new Promise((resolve, reject) =>
+    AddonRepository.repopulateCache(ADDON_IDS, resolve));
+
+  // Reset pref for next test
+  Services.prefs.setBoolPref(PREF_ADDON0_CACHE_ENABLED, true);
+
+  yield check_initialized_cache([false, true, true]);
+});
 
 // Tests repopulateCache when caching is disabled
-function run_test_6() {
-  check_database_exists(true);
+add_task(function* run_test_6() {
+  do_check_true(gDBFile.exists());
   Services.prefs.setBoolPref(PREF_GETADDONS_CACHE_ENABLED, false);
 
-  AddonRepository.repopulateCache(ADDON_IDS, function() {
-    // Database should have been deleted
-    check_database_exists(false);
+  yield new Promise((resolve, reject) =>
+    AddonRepository.repopulateCache(ADDON_IDS, resolve));
+  // Database should have been deleted
+  do_check_false(gDBFile.exists());
 
-    Services.prefs.setBoolPref(PREF_GETADDONS_CACHE_ENABLED, true);
-    check_cache([false, false, false], false, function() {});
-
-    waitForFlushedData(run_test_7);
-  });
-}
+  Services.prefs.setBoolPref(PREF_GETADDONS_CACHE_ENABLED, true);
+  yield check_cache([false, false, false], false);
+  yield AddonRepository.flush();
+});
 
 // Tests cacheAddons when the search fails
-function run_test_7() {
-  check_database_exists(true);
+add_task(function* run_test_7() {
+  do_check_true(gDBFile.exists());
   Services.prefs.setCharPref(PREF_GETADDONS_BYIDS, GETADDONS_FAILED);
 
-  AddonRepository.cacheAddons(ADDON_IDS, function() {
-    check_initialized_cache([false, false, false], run_test_8);
-  });
-}
+  yield new Promise((resolve, reject) =>
+    AddonRepository.cacheAddons(ADDON_IDS, resolve));
+  yield check_initialized_cache([false, false, false]);
+});
 
 // Tests cacheAddons when the search returns no results
-function run_test_8() {
+add_task(function* run_test_8() {
   Services.prefs.setCharPref(PREF_GETADDONS_BYIDS, GETADDONS_EMPTY);
 
-  AddonRepository.cacheAddons(ADDON_IDS, function() {
-    check_initialized_cache([false, false, false], run_test_9);
-  });
-}
+  yield new Promise((resolve, reject) =>
+    AddonRepository.cacheAddons(ADDON_IDS, resolve));
+  yield check_initialized_cache([false, false, false]);
+});
 
 // Tests cacheAddons for a single add-on when search returns results
-function run_test_9() {
+add_task(function* run_test_9() {
   Services.prefs.setCharPref(PREF_GETADDONS_BYIDS, GETADDONS_RESULTS);
 
-  AddonRepository.cacheAddons([ADDON_IDS[0]], function() {
-    check_initialized_cache([true, false, false], run_test_9_1);
-  });
-}
+  yield new Promise((resolve, reject) =>
+    AddonRepository.cacheAddons([ADDON_IDS[0]], resolve));
+  yield check_initialized_cache([true, false, false]);
+});
 
 // Tests cacheAddons when caching is disabled for a single add-on
-function run_test_9_1() {
+add_task(function* run_test_9_1() {
   Services.prefs.setBoolPref(PREF_ADDON1_CACHE_ENABLED, false);
 
-  AddonRepository.cacheAddons(ADDON_IDS, function() {
-    // Reset pref for next test
-    Services.prefs.setBoolPref(PREF_ADDON1_CACHE_ENABLED, true);
-    check_initialized_cache([true, false, true], run_test_10);
-  });
-}
+  yield new Promise((resolve, reject) =>
+    AddonRepository.cacheAddons(ADDON_IDS, resolve));
+
+  // Reset pref for next test
+  Services.prefs.setBoolPref(PREF_ADDON1_CACHE_ENABLED, true);
+
+  yield check_initialized_cache([true, false, true]);
+});
 
 // Tests cacheAddons for multiple add-ons, some already in the cache,
-function run_test_10() {
-  AddonRepository.cacheAddons(ADDON_IDS, function() {
-    check_initialized_cache([true, true, true], run_test_11);
-  });
-}
+add_task(function* run_test_10() {
+  yield new Promise((resolve, reject) =>
+    AddonRepository.cacheAddons(ADDON_IDS, resolve));
+  yield check_initialized_cache([true, true, true]);
+});
 
 // Tests cacheAddons when caching is disabled
-function run_test_11() {
-  check_database_exists(true);
+add_task(function* run_test_11() {
+  do_check_true(gDBFile.exists());
   Services.prefs.setBoolPref(PREF_GETADDONS_CACHE_ENABLED, false);
 
-  AddonRepository.cacheAddons(ADDON_IDS, function() {
-    // Database deleted for repopulateCache, not cacheAddons
-    check_database_exists(true);
+  yield new Promise((resolve, reject) =>
+    AddonRepository.cacheAddons(ADDON_IDS, resolve));
+  do_check_true(gDBFile.exists());
 
-    Services.prefs.setBoolPref(PREF_GETADDONS_CACHE_ENABLED, true);
-    check_initialized_cache([true, true, true], run_test_12);
-  });
-}
+  Services.prefs.setBoolPref(PREF_GETADDONS_CACHE_ENABLED, true);
+  yield check_initialized_cache([true, true, true]);
+});
 
 // Tests that XPI add-ons do not use any of the repository properties if
 // caching is disabled, even if there are repository properties available
-function run_test_12() {
-  check_database_exists(true);
+add_task(function* run_test_12() {
+  do_check_true(gDBFile.exists());
   Services.prefs.setBoolPref(PREF_GETADDONS_CACHE_ENABLED, false);
   Services.prefs.setCharPref(PREF_GETADDONS_BYIDS, GETADDONS_RESULTS);
 
-  AddonManager.getAddonsByIDs(ADDON_IDS, function test_12_check(aAddons) {
-    check_results(aAddons, WITHOUT_CACHE);
-    do_execute_soon(run_test_13);
-  });
-}
+  let aAddons = yield promiseAddonsByIDs(ADDON_IDS);
+  check_results(aAddons, WITHOUT_CACHE);
+});
 
 // Tests that a background update with caching disabled deletes the add-ons
 // database, and that XPI add-ons still do not use any of repository properties
-function run_test_13() {
-  check_database_exists(true);
+add_task(function* run_test_13() {
+  do_check_true(gDBFile.exists());
   Services.prefs.setCharPref(PREF_GETADDONS_BYIDS_PERFORMANCE, GETADDONS_EMPTY);
 
-  trigger_background_update(function() {
-    // Database should have been deleted
-    check_database_exists(false);
+  yield trigger_background_update();
+  // Database should have been deleted
+  do_check_false(gDBFile.exists());
 
-    AddonManager.getAddonsByIDs(ADDON_IDS, function(aAddons) {
-      check_results(aAddons, WITHOUT_CACHE);
-      do_execute_soon(run_test_14);
-    });
-  });
-}
+  let aAddons = yield promiseAddonsByIDs(ADDON_IDS);
+  check_results(aAddons, WITHOUT_CACHE);
+});
 
 // Tests that the XPI add-ons have the correct properties if caching is
 // enabled but has no information
-function run_test_14() {
+add_task(function* run_test_14() {
   Services.prefs.setBoolPref(PREF_GETADDONS_CACHE_ENABLED, true);
 
-  waitForFlushedData(function() {
-    check_database_exists(true);
+  yield trigger_background_update();
+  yield AddonRepository.flush();
+  do_check_true(gDBFile.exists());
 
-    AddonManager.getAddonsByIDs(ADDON_IDS, function(aAddons) {
-      check_results(aAddons, WITHOUT_CACHE);
-      do_execute_soon(run_test_15);
-    });
-  });
-
-  trigger_background_update();
-}
+  let aAddons = yield promiseAddonsByIDs(ADDON_IDS);
+  check_results(aAddons, WITHOUT_CACHE);
+});
 
 // Tests that the XPI add-ons correctly use the repository properties when
 // caching is enabled and the repository information is available
-function run_test_15() {
+add_task(function* run_test_15() {
   Services.prefs.setCharPref(PREF_GETADDONS_BYIDS_PERFORMANCE, GETADDONS_RESULTS);
 
-  trigger_background_update(function() {
-    AddonManager.getAddonsByIDs(ADDON_IDS, function(aAddons) {
-      check_results(aAddons, WITH_CACHE);
-      do_execute_soon(run_test_16);
-    });
-  });
-}
+  yield trigger_background_update();
+  let aAddons = yield promiseAddonsByIDs(ADDON_IDS);
+  check_results(aAddons, WITH_CACHE);
+});
 
 // Tests that restarting the manager does not change the checked properties
 // on the XPI add-ons (repository properties still exist and are still properly
 // used)
-function run_test_16() {
-  restartManager();
+add_task(function* run_test_16() {
+  yield promiseRestartManager();
 
-  AddonManager.getAddonsByIDs(ADDON_IDS, function(aAddons) {
-    check_results(aAddons, WITH_CACHE);
-    do_execute_soon(run_test_17);
-  });
-}
+  let aAddons = yield promiseAddonsByIDs(ADDON_IDS);
+  check_results(aAddons, WITH_CACHE);
+});
 
 // Tests that setting a list of types to cache works
-function run_test_17() {
+add_task(function* run_test_17() {
   Services.prefs.setCharPref(PREF_GETADDONS_CACHE_TYPES, "foo,bar,extension,baz");
 
-  trigger_background_update(function() {
-    AddonManager.getAddonsByIDs(ADDON_IDS, function(aAddons) {
-      check_results(aAddons, WITH_EXTENSION_CACHE);
-      end_test();
-    });
-  });
-}
+  yield trigger_background_update();
+  let aAddons = yield promiseAddonsByIDs(ADDON_IDS);
+  check_results(aAddons, WITH_EXTENSION_CACHE);
+});
 
+add_task(function* end_test() {
+  yield new Promise((resolve, reject) => gServer.stop(resolve));
+});