Bug 985868 - only report prolonged sync errors once per application invocation. r=rnewman
authorMark Hammond <mhammond@skippinet.com.au>
Thu, 17 Apr 2014 11:25:44 +1000
changeset 179399 a80a58607a35e2c7e892a276bb0dff81ab88d082
parent 179398 5b8b5f3b200f9aa635c3fe82fe8541f252eadd14
child 179400 ccc9cd25ebda0454adaf5b870ba446ebbd51597d
push id272
push userpvanderbeken@mozilla.com
push dateMon, 05 May 2014 16:31:18 +0000
reviewersrnewman
bugs985868
milestone31.0a1
Bug 985868 - only report prolonged sync errors once per application invocation. r=rnewman
services/sync/modules/policies.js
services/sync/tests/unit/test_errorhandler.js
--- a/services/sync/modules/policies.js
+++ b/services/sync/modules/policies.js
@@ -497,16 +497,23 @@ this.ErrorHandler = function ErrorHandle
 ErrorHandler.prototype = {
   MINIMUM_ALERT_INTERVAL_MSEC: 604800000,   // One week.
 
   /**
    * Flag that turns on error reporting for all errors, incl. network errors.
    */
   dontIgnoreErrors: false,
 
+  /**
+   * Flag that indicates if we have already reported a prolonged failure.
+   * Once set, we don't report it again, meaning this error is only reported
+   * one per run.
+   */
+  didReportProlongedError: false,
+
   init: function init() {
     Svc.Obs.add("weave:engine:sync:applied", this);
     Svc.Obs.add("weave:engine:sync:error", this);
     Svc.Obs.add("weave:service:login:error", this);
     Svc.Obs.add("weave:service:sync:error", this);
     Svc.Obs.add("weave:service:sync:finish", this);
 
     this.initLogs();
@@ -767,17 +774,23 @@ ErrorHandler.prototype = {
     if (this.dontIgnoreErrors) {
       return true;
     }
 
     let lastSync = Svc.Prefs.get("lastSync");
     if (lastSync && ((Date.now() - Date.parse(lastSync)) >
         Svc.Prefs.get("errorhandler.networkFailureReportTimeout") * 1000)) {
       Status.sync = PROLONGED_SYNC_FAILURE;
-      this._log.trace("shouldReportError: true (prolonged sync failure).");
+      if (this.didReportProlongedError) {
+        this._log.trace("shouldReportError: false (prolonged sync failure, but" +
+                        " we've already reported it).");
+        return false;
+      }
+      this._log.trace("shouldReportError: true (first prolonged sync failure).");
+      this.didReportProlongedError = true;
       return true;
     }
 
     // We got a 401 mid-sync. Wait for the next sync before actually handling
     // an error. This assumes that we'll get a 401 again on a login fetch in
     // order to report the error.
     if (!this.service.clusterURL) {
       this._log.trace("shouldReportError: false (no cluster URL; " +
--- a/services/sync/tests/unit/test_errorhandler.js
+++ b/services/sync/tests/unit/test_errorhandler.js
@@ -145,16 +145,17 @@ function generateAndUploadKeys() {
   serverKeys.encrypt(Service.identity.syncKeyBundle);
   return serverKeys.upload(Service.resource(Service.cryptoKeysURL)).success;
 }
 
 function clean() {
   Service.startOver();
   Status.resetSync();
   Status.resetBackoff();
+  errorHandler.didReportProlongedError = false;
 }
 
 add_identity_test(this, function test_401_logout() {
   let server = sync_httpd_setup();
   yield setUp(server);
 
   // By calling sync, we ensure we're logged in.
   Service.sync();
@@ -292,129 +293,161 @@ add_identity_test(this, function test_sh
   // Test dontIgnoreErrors, network, prolonged, sync error reported
   Status.resetSync();
   setLastSync(PROLONGED_ERROR_DURATION);
   errorHandler.dontIgnoreErrors = true;
   Status.sync = LOGIN_FAILED_NETWORK_ERROR;
   do_check_true(errorHandler.shouldReportError());
 
   // Test non-network, prolonged, login error reported
+  do_check_false(errorHandler.didReportProlongedError);
   Status.resetSync();
   setLastSync(PROLONGED_ERROR_DURATION);
   errorHandler.dontIgnoreErrors = false;
   Status.login = LOGIN_FAILED_NO_PASSWORD;
   do_check_true(errorHandler.shouldReportError());
+  do_check_true(errorHandler.didReportProlongedError);
+
+  // Second time with prolonged error and without resetting
+  // didReportProlongedError, sync error should not be reported.
+  Status.resetSync();
+  setLastSync(PROLONGED_ERROR_DURATION);
+  errorHandler.dontIgnoreErrors = false;
+  Status.login = LOGIN_FAILED_NO_PASSWORD;
+  do_check_false(errorHandler.shouldReportError());
+  do_check_true(errorHandler.didReportProlongedError);
 
   // Test non-network, prolonged, sync error reported
   Status.resetSync();
   setLastSync(PROLONGED_ERROR_DURATION);
   errorHandler.dontIgnoreErrors = false;
+  errorHandler.didReportProlongedError = false;
   Status.sync = CREDENTIALS_CHANGED;
   do_check_true(errorHandler.shouldReportError());
+  do_check_true(errorHandler.didReportProlongedError);
+  errorHandler.didReportProlongedError = false;
 
   // Test network, prolonged, login error reported
   Status.resetSync();
   setLastSync(PROLONGED_ERROR_DURATION);
   errorHandler.dontIgnoreErrors = false;
   Status.login = LOGIN_FAILED_NETWORK_ERROR;
   do_check_true(errorHandler.shouldReportError());
+  do_check_true(errorHandler.didReportProlongedError);
+  errorHandler.didReportProlongedError = false;
 
   // Test network, prolonged, sync error reported
   Status.resetSync();
   setLastSync(PROLONGED_ERROR_DURATION);
   errorHandler.dontIgnoreErrors = false;
   Status.sync = LOGIN_FAILED_NETWORK_ERROR;
   do_check_true(errorHandler.shouldReportError());
+  do_check_true(errorHandler.didReportProlongedError);
+  errorHandler.didReportProlongedError = false;
 
   // Test non-network, non-prolonged, login error reported
   Status.resetSync();
   setLastSync(NON_PROLONGED_ERROR_DURATION);
   errorHandler.dontIgnoreErrors = false;
   Status.login = LOGIN_FAILED_NO_PASSWORD;
   do_check_true(errorHandler.shouldReportError());
+  do_check_false(errorHandler.didReportProlongedError);
 
   // Test non-network, non-prolonged, sync error reported
   Status.resetSync();
   setLastSync(NON_PROLONGED_ERROR_DURATION);
   errorHandler.dontIgnoreErrors = false;
   Status.sync = CREDENTIALS_CHANGED;
   do_check_true(errorHandler.shouldReportError());
+  do_check_false(errorHandler.didReportProlongedError);
 
   // Test network, non-prolonged, login error reported
   Status.resetSync();
   setLastSync(NON_PROLONGED_ERROR_DURATION);
   errorHandler.dontIgnoreErrors = false;
   Status.login = LOGIN_FAILED_NETWORK_ERROR;
   do_check_false(errorHandler.shouldReportError());
+  do_check_false(errorHandler.didReportProlongedError);
 
   // Test network, non-prolonged, sync error reported
   Status.resetSync();
   setLastSync(NON_PROLONGED_ERROR_DURATION);
   errorHandler.dontIgnoreErrors = false;
   Status.sync = LOGIN_FAILED_NETWORK_ERROR;
   do_check_false(errorHandler.shouldReportError());
+  do_check_false(errorHandler.didReportProlongedError);
 
   // Test server maintenance, sync errors are not reported
   Status.resetSync();
   setLastSync(NON_PROLONGED_ERROR_DURATION);
   errorHandler.dontIgnoreErrors = false;
   Status.sync = SERVER_MAINTENANCE;
   do_check_false(errorHandler.shouldReportError());
+  do_check_false(errorHandler.didReportProlongedError);
 
   // Test server maintenance, login errors are not reported
   Status.resetSync();
   setLastSync(NON_PROLONGED_ERROR_DURATION);
   errorHandler.dontIgnoreErrors = false;
   Status.login = SERVER_MAINTENANCE;
   do_check_false(errorHandler.shouldReportError());
+  do_check_false(errorHandler.didReportProlongedError);
 
   // Test prolonged, server maintenance, sync errors are reported
   Status.resetSync();
   setLastSync(PROLONGED_ERROR_DURATION);
   errorHandler.dontIgnoreErrors = false;
   Status.sync = SERVER_MAINTENANCE;
   do_check_true(errorHandler.shouldReportError());
+  do_check_true(errorHandler.didReportProlongedError);
+  errorHandler.didReportProlongedError = false;
 
   // Test prolonged, server maintenance, login errors are reported
   Status.resetSync();
   setLastSync(PROLONGED_ERROR_DURATION);
   errorHandler.dontIgnoreErrors = false;
   Status.login = SERVER_MAINTENANCE;
   do_check_true(errorHandler.shouldReportError());
+  do_check_true(errorHandler.didReportProlongedError);
+  errorHandler.didReportProlongedError = false;
 
   // Test dontIgnoreErrors, server maintenance, sync errors are reported
   Status.resetSync();
   setLastSync(NON_PROLONGED_ERROR_DURATION);
   errorHandler.dontIgnoreErrors = true;
   Status.sync = SERVER_MAINTENANCE;
   do_check_true(errorHandler.shouldReportError());
+  // dontIgnoreErrors means we don't set didReportProlongedError
+  do_check_false(errorHandler.didReportProlongedError);
 
   // Test dontIgnoreErrors, server maintenance, login errors are reported
   Status.resetSync();
   setLastSync(NON_PROLONGED_ERROR_DURATION);
   errorHandler.dontIgnoreErrors = true;
   Status.login = SERVER_MAINTENANCE;
   do_check_true(errorHandler.shouldReportError());
+  do_check_false(errorHandler.didReportProlongedError);
 
   // Test dontIgnoreErrors, prolonged, server maintenance,
   // sync errors are reported
   Status.resetSync();
   setLastSync(PROLONGED_ERROR_DURATION);
   errorHandler.dontIgnoreErrors = true;
   Status.sync = SERVER_MAINTENANCE;
   do_check_true(errorHandler.shouldReportError());
+  do_check_false(errorHandler.didReportProlongedError);
 
   // Test dontIgnoreErrors, prolonged, server maintenance,
   // login errors are reported
   Status.resetSync();
   setLastSync(PROLONGED_ERROR_DURATION);
   errorHandler.dontIgnoreErrors = true;
   Status.login = SERVER_MAINTENANCE;
   do_check_true(errorHandler.shouldReportError());
-
+  do_check_false(errorHandler.didReportProlongedError);
 });
 
 add_identity_test(this, function test_shouldReportError_master_password() {
   _("Test error ignored due to locked master password");
   let server = sync_httpd_setup();
   yield setUp(server);
 
   // Monkey patch Service.verifyLogin to imitate
@@ -620,16 +653,17 @@ add_task(function test_login_prolonged_n
   let server = sync_httpd_setup();
   yield setUp(server);
   Service.identity.basicPassword = null;
 
   let deferred = Promise.defer();
   Svc.Obs.add("weave:ui:login:error", function onSyncError() {
     Svc.Obs.remove("weave:ui:login:error", onSyncError);
     do_check_eq(Status.sync, PROLONGED_SYNC_FAILURE);
+    do_check_true(errorHandler.didReportProlongedError);
 
     clean();
     server.stop(deferred.resolve);
   });
 
   setLastSync(PROLONGED_ERROR_DURATION);
   Service.sync();
   yield deferred.promise;
@@ -646,16 +680,17 @@ add_task(function test_sync_prolonged_no
   do_check_true(Service.isLoggedIn);
 
   generateCredentialsChangedFailure();
 
   let deferred = Promise.defer();
   Svc.Obs.add("weave:ui:sync:error", function onSyncError() {
     Svc.Obs.remove("weave:ui:sync:error", onSyncError);
     do_check_eq(Status.sync, PROLONGED_SYNC_FAILURE);
+    do_check_true(errorHandler.didReportProlongedError);
 
     clean();
     server.stop(deferred.resolve);
   });
 
   setLastSync(PROLONGED_ERROR_DURATION);
   Service.sync();
   yield deferred.promise;
@@ -666,16 +701,17 @@ add_identity_test(this, function test_lo
   yield configureIdentity({username: "johndoe"});
   Service.serverURL  = FAKE_SERVER_URL;
   Service.clusterURL = FAKE_SERVER_URL;
 
   let deferred = Promise.defer();
   Svc.Obs.add("weave:ui:login:error", function onSyncError() {
     Svc.Obs.remove("weave:ui:login:error", onSyncError);
     do_check_eq(Status.sync, PROLONGED_SYNC_FAILURE);
+    do_check_true(errorHandler.didReportProlongedError);
 
     clean();
     deferred.resolve();
   });
 
   setLastSync(PROLONGED_ERROR_DURATION);
   Service.sync();
   yield deferred.promise;
@@ -683,16 +719,17 @@ add_identity_test(this, function test_lo
 
 add_test(function test_sync_prolonged_network_error() {
   // Test prolonged, network errors are reported
   Services.io.offline = true;
 
   Svc.Obs.add("weave:ui:sync:error", function onSyncError() {
     Svc.Obs.remove("weave:ui:sync:error", onSyncError);
     do_check_eq(Status.sync, PROLONGED_SYNC_FAILURE);
+    do_check_true(errorHandler.didReportProlongedError);
 
     Services.io.offline = false;
     clean();
     run_next_test();
   });
 
   setLastSync(PROLONGED_ERROR_DURATION);
   Service.sync();
@@ -703,16 +740,17 @@ add_task(function test_login_non_network
   let server = sync_httpd_setup();
   yield setUp(server);
   Service.identity.basicPassword = null;
 
   let deferred = Promise.defer();
   Svc.Obs.add("weave:ui:login:error", function onSyncError() {
     Svc.Obs.remove("weave:ui:login:error", onSyncError);
     do_check_eq(Status.login, LOGIN_FAILED_NO_PASSWORD);
+    do_check_false(errorHandler.didReportProlongedError);
 
     clean();
     server.stop(deferred.resolve);
   });
 
   setLastSync(NON_PROLONGED_ERROR_DURATION);
   Service.sync();
   yield deferred.promise;
@@ -729,16 +767,17 @@ add_task(function test_sync_non_network_
   do_check_true(Service.isLoggedIn);
 
   generateCredentialsChangedFailure();
 
   let deferred = Promise.defer();
   Svc.Obs.add("weave:ui:sync:error", function onSyncError() {
     Svc.Obs.remove("weave:ui:sync:error", onSyncError);
     do_check_eq(Status.sync, CREDENTIALS_CHANGED);
+    do_check_false(errorHandler.didReportProlongedError);
 
     clean();
     server.stop(deferred.resolve);
   });
 
   setLastSync(NON_PROLONGED_ERROR_DURATION);
   Service.sync();
   yield deferred.promise;
@@ -750,16 +789,17 @@ add_identity_test(this, function test_lo
   Service.clusterURL = FAKE_SERVER_URL;
 
   let deferred = Promise.defer();
   // Test network errors are not reported.
   Svc.Obs.add("weave:ui:clear-error", function onClearError() {
     Svc.Obs.remove("weave:ui:clear-error", onClearError);
 
     do_check_eq(Status.login, LOGIN_FAILED_NETWORK_ERROR);
+    do_check_false(errorHandler.didReportProlongedError);
 
     Services.io.offline = false;
     clean();
     deferred.resolve()
   });
 
   setLastSync(NON_PROLONGED_ERROR_DURATION);
   Service.sync();
@@ -768,16 +808,17 @@ add_identity_test(this, function test_lo
 
 add_test(function test_sync_network_error() {
   // Test network errors are not reported.
   Services.io.offline = true;
 
   Svc.Obs.add("weave:ui:sync:finish", function onUIUpdate() {
     Svc.Obs.remove("weave:ui:sync:finish", onUIUpdate);
     do_check_eq(Status.sync, LOGIN_FAILED_NETWORK_ERROR);
+    do_check_false(errorHandler.didReportProlongedError);
 
     Services.io.offline = false;
     clean();
     run_next_test();
   });
 
   setLastSync(NON_PROLONGED_ERROR_DURATION);
   Service.sync();
@@ -802,16 +843,17 @@ add_identity_test(this, function test_sy
   do_check_eq(Status.service, STATUS_OK);
 
   let deferred = Promise.defer();
   Svc.Obs.add("weave:ui:sync:finish", function onSyncFinish() {
     Svc.Obs.remove("weave:ui:sync:finish", onSyncFinish);
 
     do_check_eq(Status.service, SYNC_FAILED_PARTIAL);
     do_check_eq(Status.sync, SERVER_MAINTENANCE);
+    do_check_false(errorHandler.didReportProlongedError);
 
     Svc.Obs.remove("weave:ui:sync:error", onSyncError);
     clean();
     server.stop(deferred.resolve);
   });
 
   setLastSync(NON_PROLONGED_ERROR_DURATION);
   Service.sync();
@@ -845,16 +887,17 @@ add_identity_test(this, function test_in
   let deferred = Promise.defer();
   Svc.Obs.add("weave:ui:clear-error", function onLoginFinish() {
     Svc.Obs.remove("weave:ui:clear-error", onLoginFinish);
 
     do_check_true(Status.enforceBackoff);
     do_check_eq(backoffInterval, 42);
     do_check_eq(Status.service, LOGIN_FAILED);
     do_check_eq(Status.login, SERVER_MAINTENANCE);
+    do_check_false(errorHandler.didReportProlongedError);
 
     Svc.Obs.remove("weave:ui:login:error", onUIUpdate);
     clean();
     server.stop(deferred.resolve);
   });
 
   setLastSync(NON_PROLONGED_ERROR_DURATION);
   Service.sync();
@@ -887,16 +930,17 @@ add_identity_test(this, function test_me
   let deferred = Promise.defer();
   Svc.Obs.add("weave:ui:clear-error", function onLoginFinish() {
     Svc.Obs.remove("weave:ui:clear-error", onLoginFinish);
 
     do_check_true(Status.enforceBackoff);
     do_check_eq(backoffInterval, 42);
     do_check_eq(Status.service, LOGIN_FAILED);
     do_check_eq(Status.login, SERVER_MAINTENANCE);
+    do_check_false(errorHandler.didReportProlongedError);
 
     Svc.Obs.remove("weave:ui:login:error", onUIUpdate);
     clean();
     server.stop(deferred.resolve);
   });
 
   setLastSync(NON_PROLONGED_ERROR_DURATION);
   Service.sync();
@@ -932,16 +976,17 @@ add_identity_test(this, function test_cr
   let deferred = Promise.defer();
   Svc.Obs.add("weave:ui:clear-error", function onLoginFinish() {
     Svc.Obs.remove("weave:ui:clear-error", onLoginFinish);
 
     do_check_true(Status.enforceBackoff);
     do_check_eq(backoffInterval, 42);
     do_check_eq(Status.service, LOGIN_FAILED);
     do_check_eq(Status.login, SERVER_MAINTENANCE);
+    do_check_false(errorHandler.didReportProlongedError);
 
     Svc.Obs.remove("weave:ui:login:error", onUIUpdate);
     clean();
     server.stop(deferred.resolve);
   });
 
   setLastSync(NON_PROLONGED_ERROR_DURATION);
   Service.sync();
@@ -959,16 +1004,17 @@ add_task(function test_sync_prolonged_se
   engine.exception = {status: 503,
                       headers: {"retry-after": BACKOFF}};
 
   let deferred = Promise.defer();
   Svc.Obs.add("weave:ui:sync:error", function onUIUpdate() {
     Svc.Obs.remove("weave:ui:sync:error", onUIUpdate);
     do_check_eq(Status.service, SYNC_FAILED);
     do_check_eq(Status.sync, PROLONGED_SYNC_FAILURE);
+    do_check_true(errorHandler.didReportProlongedError);
 
     clean();
     server.stop(deferred.resolve);
   });
 
   do_check_eq(Status.service, STATUS_OK);
 
   setLastSync(PROLONGED_ERROR_DURATION);
@@ -993,16 +1039,17 @@ add_identity_test(this, function test_in
 
   let deferred = Promise.defer();
   Svc.Obs.add("weave:ui:login:error", function onUIUpdate() {
     Svc.Obs.remove("weave:ui:login:error", onUIUpdate);
     do_check_true(Status.enforceBackoff);
     do_check_eq(backoffInterval, 42);
     do_check_eq(Status.service, SYNC_FAILED);
     do_check_eq(Status.sync, PROLONGED_SYNC_FAILURE);
+    do_check_true(errorHandler.didReportProlongedError);
 
     clean();
     server.stop(deferred.resolve);
   });
 
   do_check_false(Status.enforceBackoff);
   do_check_eq(Status.service, STATUS_OK);
 
@@ -1028,16 +1075,17 @@ add_identity_test(this, function test_me
 
   let deferred = Promise.defer();
   Svc.Obs.add("weave:ui:login:error", function onUIUpdate() {
     Svc.Obs.remove("weave:ui:login:error", onUIUpdate);
     do_check_true(Status.enforceBackoff);
     do_check_eq(backoffInterval, 42);
     do_check_eq(Status.service, SYNC_FAILED);
     do_check_eq(Status.sync, PROLONGED_SYNC_FAILURE);
+    do_check_true(errorHandler.didReportProlongedError);
 
     clean();
     server.stop(deferred.resolve);
   });
 
   do_check_false(Status.enforceBackoff);
   do_check_eq(Status.service, STATUS_OK);
 
@@ -1065,16 +1113,17 @@ add_identity_test(this, function test_do
 
   let deferred = Promise.defer();
   Svc.Obs.add("weave:ui:login:error", function onUIUpdate() {
     Svc.Obs.remove("weave:ui:login:error", onUIUpdate);
     do_check_true(Status.enforceBackoff);
     do_check_eq(backoffInterval, 42);
     do_check_eq(Status.service, SYNC_FAILED);
     do_check_eq(Status.sync, PROLONGED_SYNC_FAILURE);
+    do_check_true(errorHandler.didReportProlongedError);
 
     clean();
     server.stop(deferred.resolve);
   });
 
   do_check_false(Status.enforceBackoff);
   do_check_eq(Status.service, STATUS_OK);
 
@@ -1100,16 +1149,17 @@ add_identity_test(this, function test_up
 
   let deferred = Promise.defer();
   Svc.Obs.add("weave:ui:login:error", function onUIUpdate() {
     Svc.Obs.remove("weave:ui:login:error", onUIUpdate);
     do_check_true(Status.enforceBackoff);
     do_check_eq(backoffInterval, 42);
     do_check_eq(Status.service, SYNC_FAILED);
     do_check_eq(Status.sync, PROLONGED_SYNC_FAILURE);
+    do_check_true(errorHandler.didReportProlongedError);
 
     clean();
     server.stop(deferred.resolve);
   });
 
   do_check_false(Status.enforceBackoff);
   do_check_eq(Status.service, STATUS_OK);
 
@@ -1136,16 +1186,17 @@ add_identity_test(this, function test_wi
 
   let deferred = Promise.defer();
   Svc.Obs.add("weave:ui:login:error", function onUIUpdate() {
     Svc.Obs.remove("weave:ui:login:error", onUIUpdate);
     do_check_true(Status.enforceBackoff);
     do_check_eq(backoffInterval, 42);
     do_check_eq(Status.service, SYNC_FAILED);
     do_check_eq(Status.sync, PROLONGED_SYNC_FAILURE);
+    do_check_true(errorHandler.didReportProlongedError);
 
     clean();
     server.stop(deferred.resolve);
   });
 
   do_check_false(Status.enforceBackoff);
   do_check_eq(Status.service, STATUS_OK);
 
@@ -1178,16 +1229,17 @@ add_identity_test(this, function test_wi
   let deferred = Promise.defer();
   Svc.Obs.add("weave:ui:sync:error", function onUIUpdate() {
     Svc.Obs.remove("weave:ui:sync:error", onUIUpdate);
     do_check_true(Status.enforceBackoff);
     do_check_eq(backoffInterval, 42);
     do_check_eq(Status.service, SYNC_FAILED);
     do_check_eq(Status.sync, PROLONGED_SYNC_FAILURE);
     do_check_eq(Svc.Prefs.get("firstSync"), "wipeRemote");
+    do_check_true(errorHandler.didReportProlongedError);
 
     clean();
     server.stop(deferred.resolve);
   });
 
   do_check_false(Status.enforceBackoff);
   do_check_eq(Status.service, STATUS_OK);
 
@@ -1209,16 +1261,17 @@ add_task(function test_sync_syncAndRepor
   engine.exception = {status: 503,
                       headers: {"retry-after": BACKOFF}};
 
   let deferred = Promise.defer();
   Svc.Obs.add("weave:ui:sync:error", function onUIUpdate() {
     Svc.Obs.remove("weave:ui:sync:error", onUIUpdate);
     do_check_eq(Status.service, SYNC_FAILED_PARTIAL);
     do_check_eq(Status.sync, SERVER_MAINTENANCE);
+    do_check_false(errorHandler.didReportProlongedError);
 
     clean();
     server.stop(deferred.resolve);
   });
 
   do_check_eq(Status.service, STATUS_OK);
 
   setLastSync(NON_PROLONGED_ERROR_DURATION);
@@ -1244,16 +1297,17 @@ add_identity_test(this, function test_in
 
   let deferred = Promise.defer();
   Svc.Obs.add("weave:ui:login:error", function onUIUpdate() {
     Svc.Obs.remove("weave:ui:login:error", onUIUpdate);
     do_check_true(Status.enforceBackoff);
     do_check_eq(backoffInterval, 42);
     do_check_eq(Status.service, LOGIN_FAILED);
     do_check_eq(Status.login, SERVER_MAINTENANCE);
+    do_check_false(errorHandler.didReportProlongedError);
 
     clean();
     server.stop(deferred.resolve);
   });
 
   do_check_false(Status.enforceBackoff);
   do_check_eq(Status.service, STATUS_OK);
 
@@ -1280,16 +1334,17 @@ add_identity_test(this, function test_me
 
   let deferred = Promise.defer();
   Svc.Obs.add("weave:ui:login:error", function onUIUpdate() {
     Svc.Obs.remove("weave:ui:login:error", onUIUpdate);
     do_check_true(Status.enforceBackoff);
     do_check_eq(backoffInterval, 42);
     do_check_eq(Status.service, LOGIN_FAILED);
     do_check_eq(Status.login, SERVER_MAINTENANCE);
+    do_check_false(errorHandler.didReportProlongedError);
 
     clean();
     server.stop(deferred.resolve);
   });
 
   do_check_false(Status.enforceBackoff);
   do_check_eq(Status.service, STATUS_OK);
 
@@ -1318,16 +1373,17 @@ add_identity_test(this, function test_do
 
   let deferred = Promise.defer();
   Svc.Obs.add("weave:ui:login:error", function onUIUpdate() {
     Svc.Obs.remove("weave:ui:login:error", onUIUpdate);
     do_check_true(Status.enforceBackoff);
     do_check_eq(backoffInterval, 42);
     do_check_eq(Status.service, LOGIN_FAILED);
     do_check_eq(Status.login, SERVER_MAINTENANCE);
+    do_check_false(errorHandler.didReportProlongedError);
 
     clean();
     server.stop(deferred.resolve);
   });
 
   do_check_false(Status.enforceBackoff);
   do_check_eq(Status.service, STATUS_OK);
 
@@ -1354,16 +1410,17 @@ add_identity_test(this, function test_up
 
   let deferred = Promise.defer();
   Svc.Obs.add("weave:ui:login:error", function onUIUpdate() {
     Svc.Obs.remove("weave:ui:login:error", onUIUpdate);
     do_check_true(Status.enforceBackoff);
     do_check_eq(backoffInterval, 42);
     do_check_eq(Status.service, LOGIN_FAILED);
     do_check_eq(Status.login, SERVER_MAINTENANCE);
+    do_check_false(errorHandler.didReportProlongedError);
 
     clean();
     server.stop(deferred.resolve);
   });
 
   do_check_false(Status.enforceBackoff);
   do_check_eq(Status.service, STATUS_OK);
 
@@ -1390,16 +1447,17 @@ add_identity_test(this, function test_wi
 
   let deferred = Promise.defer();
   Svc.Obs.add("weave:ui:login:error", function onUIUpdate() {
     Svc.Obs.remove("weave:ui:login:error", onUIUpdate);
     do_check_true(Status.enforceBackoff);
     do_check_eq(backoffInterval, 42);
     do_check_eq(Status.service, LOGIN_FAILED);
     do_check_eq(Status.login, SERVER_MAINTENANCE);
+    do_check_false(errorHandler.didReportProlongedError);
 
     clean();
     server.stop(deferred.resolve);
   });
 
   do_check_false(Status.enforceBackoff);
   do_check_eq(Status.service, STATUS_OK);
 
@@ -1431,16 +1489,17 @@ add_identity_test(this, function test_wi
   let deferred = Promise.defer();
   Svc.Obs.add("weave:ui:sync:error", function onUIUpdate() {
     Svc.Obs.remove("weave:ui:sync:error", onUIUpdate);
     do_check_true(Status.enforceBackoff);
     do_check_eq(backoffInterval, 42);
     do_check_eq(Status.service, SYNC_FAILED);
     do_check_eq(Status.sync, SERVER_MAINTENANCE);
     do_check_eq(Svc.Prefs.get("firstSync"), "wipeRemote");
+    do_check_false(errorHandler.didReportProlongedError);
 
     clean();
     server.stop(deferred.resolve);
   });
 
   do_check_false(Status.enforceBackoff);
   do_check_eq(Status.service, STATUS_OK);
 
@@ -1462,16 +1521,19 @@ add_task(function test_sync_syncAndRepor
   engine.exception = {status: 503,
                       headers: {"retry-after": BACKOFF}};
 
   let deferred = Promise.defer();
   Svc.Obs.add("weave:ui:sync:error", function onUIUpdate() {
     Svc.Obs.remove("weave:ui:sync:error", onUIUpdate);
     do_check_eq(Status.service, SYNC_FAILED_PARTIAL);
     do_check_eq(Status.sync, SERVER_MAINTENANCE);
+    // syncAndReportErrors means dontIgnoreErrors, which means
+    // didReportProlongedError not touched.
+    do_check_false(errorHandler.didReportProlongedError);
 
     clean();
     server.stop(deferred.resolve);
   });
 
   do_check_eq(Status.service, STATUS_OK);
 
   setLastSync(PROLONGED_ERROR_DURATION);
@@ -1497,16 +1559,19 @@ add_identity_test(this, function test_in
 
   let deferred = Promise.defer();
   Svc.Obs.add("weave:ui:login:error", function onUIUpdate() {
     Svc.Obs.remove("weave:ui:login:error", onUIUpdate);
     do_check_true(Status.enforceBackoff);
     do_check_eq(backoffInterval, 42);
     do_check_eq(Status.service, LOGIN_FAILED);
     do_check_eq(Status.login, SERVER_MAINTENANCE);
+    // syncAndReportErrors means dontIgnoreErrors, which means
+    // didReportProlongedError not touched.
+    do_check_false(errorHandler.didReportProlongedError);
 
     clean();
     server.stop(deferred.resolve);
   });
 
   do_check_false(Status.enforceBackoff);
   do_check_eq(Status.service, STATUS_OK);
 
@@ -1533,16 +1598,19 @@ add_identity_test(this, function test_me
 
   let deferred = Promise.defer();
   Svc.Obs.add("weave:ui:login:error", function onUIUpdate() {
     Svc.Obs.remove("weave:ui:login:error", onUIUpdate);
     do_check_true(Status.enforceBackoff);
     do_check_eq(backoffInterval, 42);
     do_check_eq(Status.service, LOGIN_FAILED);
     do_check_eq(Status.login, SERVER_MAINTENANCE);
+    // syncAndReportErrors means dontIgnoreErrors, which means
+    // didReportProlongedError not touched.
+    do_check_false(errorHandler.didReportProlongedError);
 
     clean();
     server.stop(deferred.resolve);
   });
 
   do_check_false(Status.enforceBackoff);
   do_check_eq(Status.service, STATUS_OK);
 
@@ -1571,16 +1639,19 @@ add_identity_test(this, function test_do
 
   let deferred = Promise.defer();
   Svc.Obs.add("weave:ui:login:error", function onUIUpdate() {
     Svc.Obs.remove("weave:ui:login:error", onUIUpdate);
     do_check_true(Status.enforceBackoff);
     do_check_eq(backoffInterval, 42);
     do_check_eq(Status.service, LOGIN_FAILED);
     do_check_eq(Status.login, SERVER_MAINTENANCE);
+    // syncAndReportErrors means dontIgnoreErrors, which means
+    // didReportProlongedError not touched.
+    do_check_false(errorHandler.didReportProlongedError);
 
     clean();
     server.stop(deferred.resolve);
   });
 
   do_check_false(Status.enforceBackoff);
   do_check_eq(Status.service, STATUS_OK);
 
@@ -1607,16 +1678,19 @@ add_identity_test(this, function test_up
 
   let deferred = Promise.defer();
   Svc.Obs.add("weave:ui:login:error", function onUIUpdate() {
     Svc.Obs.remove("weave:ui:login:error", onUIUpdate);
     do_check_true(Status.enforceBackoff);
     do_check_eq(backoffInterval, 42);
     do_check_eq(Status.service, LOGIN_FAILED);
     do_check_eq(Status.login, SERVER_MAINTENANCE);
+    // syncAndReportErrors means dontIgnoreErrors, which means
+    // didReportProlongedError not touched.
+    do_check_false(errorHandler.didReportProlongedError);
 
     clean();
     server.stop(deferred.resolve);
   });
 
   do_check_false(Status.enforceBackoff);
   do_check_eq(Status.service, STATUS_OK);
 
@@ -1643,16 +1717,19 @@ add_identity_test(this, function test_wi
 
   let deferred = Promise.defer();
   Svc.Obs.add("weave:ui:login:error", function onUIUpdate() {
     Svc.Obs.remove("weave:ui:login:error", onUIUpdate);
     do_check_true(Status.enforceBackoff);
     do_check_eq(backoffInterval, 42);
     do_check_eq(Status.service, LOGIN_FAILED);
     do_check_eq(Status.login, SERVER_MAINTENANCE);
+    // syncAndReportErrors means dontIgnoreErrors, which means
+    // didReportProlongedError not touched.
+    do_check_false(errorHandler.didReportProlongedError);
 
     clean();
     server.stop(deferred.resolve);
   });
 
   do_check_false(Status.enforceBackoff);
   do_check_eq(Status.service, STATUS_OK);