Bug 1028869 - Part 2: xpcshell test updated with ping/restore. r=standard8, a=sledru
authorPaul Kerr [:pkerr] <pkerr@mozilla.com>
Tue, 23 Sep 2014 18:47:58 -0700
changeset 242904 b653be6b040a
parent 242903 fc47c7a95f85
child 242905 97b34f0b9946
push id4336
push userpaulrkerr@gmail.com
push date2015-01-19 21:06 +0000
treeherdermozilla-beta@b653be6b040a [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersstandard8, sledru
bugs1028869
milestone36.0
Bug 1028869 - Part 2: xpcshell test updated with ping/restore. r=standard8, a=sledru
browser/components/loop/test/xpcshell/head.js
browser/components/loop/test/xpcshell/test_looppush_initialize.js
browser/components/loop/test/xpcshell/test_loopservice_busy.js
--- a/browser/components/loop/test/xpcshell/head.js
+++ b/browser/components/loop/test/xpcshell/head.js
@@ -110,23 +110,30 @@ let mockPushHandler = {
   }
 };
 
 /**
  * Mock nsIWebSocketChannel for tests. This mocks the WebSocketChannel, and
  * enables us to check parameters and return messages similar to the push
  * server.
  */
-let MockWebSocketChannel = function(options = {}) {
-  this.defaultMsgHandler = options.defaultMsgHandler;
-};
+function MockWebSocketChannel() {};
 
 MockWebSocketChannel.prototype = {
   QueryInterface: XPCOMUtils.generateQI(Ci.nsIWebSocketChannel),
 
+  initRegStatus: 0,
+
+  defaultMsgHandler: function(msg) {
+    // Treat as a ping
+    this.listener.onMessageAvailable(this.context,
+                                     JSON.stringify({}));
+    return;
+  },
+
   /**
    * nsIWebSocketChannel implementations.
    * See nsIWebSocketChannel.idl for API details.
    */
   asyncOpen: function(aURI, aOrigin, aListener, aContext) {
     this.uri = aURI;
     this.origin = aOrigin;
     this.listener = aListener;
@@ -157,16 +164,20 @@ MockWebSocketChannel.prototype = {
                           channelID: this.channelID,
                           pushEndpoint: kEndPointUrl}));
         break;
       default:
         this.defaultMsgHandler && this.defaultMsgHandler(message);
     }
   },
 
+  close: function(aCode, aReason) {
+    this.stop(aCode);
+  },
+
   notify: function(version) {
     this.listener.onMessageAvailable(this.context,
       JSON.stringify({
         messageType: "notification", updates: [{
           channelID: this.channelID,
           version: version
         }]
     }));
--- a/browser/components/loop/test/xpcshell/test_looppush_initialize.js
+++ b/browser/components/loop/test/xpcshell/test_looppush_initialize.js
@@ -7,75 +7,37 @@
 
   add_test(function test_initalize_offline() {
     Services.io.offline = true;
     do_check_false(MozLoopPushHandler.initialize());
     Services.io.offline = false;
     run_next_test();
   });
 
-  add_test(function test_initalize() {
-    loopServer.registerPathHandler("/push-server-config", (request, response) => {
-      // The PushHandler should retry the request for the push-server-config for
-      // each of these cases without throwing an error.
-      let n = 0;
-      switch (++pushServerRequestCount) {
-      case ++n:
-        // Non-200 response
-        response.setStatusLine(null, 500, "Retry");
-        response.processAsync();
-        response.finish();
-        break;
-      case ++n:
-        // missing parameter
-        response.setStatusLine(null, 200, "OK");
-        response.write(JSON.stringify({pushServerURI: null}));
-        response.processAsync();
-        response.finish();
-        break;
-      case ++n:
-        // json parse error
-        response.setStatusLine(null, 200, "OK");
-        response.processAsync();
-        response.finish();
-        break;
-      case ++n:
-        response.setStatusLine(null, 200, "OK");
-        response.write(JSON.stringify({pushServerURI: kServerPushUrl}));
-        response.processAsync();
-        response.finish();
-
-        run_next_test();
-        break;
-      }
-    });
-
-    do_check_true(MozLoopPushHandler.initialize({mockWebSocket: mockWebSocket}));
-  });
-
   add_test(function test_initalize_missing_chanid() {
-    Assert.throws(() => {MozLoopPushHandler.register(null, dummyCallback, dummyCallback)});
+    Assert.throws(() => MozLoopPushHandler.register(null, dummyCallback, dummyCallback));
     run_next_test();
   });
 
   add_test(function test_initalize_missing_regcallback() {
-    Assert.throws(() => {MozLoopPushHandler.register("chan-1", null, dummyCallback)});
+    Assert.throws(() => MozLoopPushHandler.register("chan-1", null, dummyCallback));
     run_next_test();
   });
 
   add_test(function test_initalize_missing_notifycallback() {
-    Assert.throws(() => {MozLoopPushHandler.register("chan-1", dummyCallback, null)});
+    Assert.throws(() => MozLoopPushHandler.register("chan-1", dummyCallback, null));
     run_next_test();
   });
 
   add_test(function test_initalize_websocket() {
+    do_check_true(MozLoopPushHandler.initialize({mockWebSocket: mockWebSocket}));
     MozLoopPushHandler.register(
       "chan-1",
       function(err, url, id) {
-        Assert.equal(err, null, "Should return null for success");
+        Assert.equal(err, null, "err should be null to indicate success");
         Assert.equal(url, kEndPointUrl, "Should return push server application URL");
         Assert.equal(id, "chan-1", "Should have channel id = chan-1");
         Assert.equal(mockWebSocket.uri.prePath, kServerPushUrl,
                      "Should have the url from preferences");
         Assert.equal(mockWebSocket.origin, kServerPushUrl,
                      "Should have the origin url from preferences");
         Assert.equal(mockWebSocket.protocol, "push-notification",
                      "Should have the protocol set to push-notifications");
@@ -116,42 +78,151 @@
       },
       function(version, id) {
         Assert.equal(version, 16, "Should have version number 16");
         Assert.equal(id, "chan-2", "Should have channel id = chan-2");
         run_next_test();
       });
   });
 
+  // Test that the PushHander will re-connect after the near-end disconnect.
+  // The uaID is cleared to force re-registration of all notification channels.
   add_test(function test_reconnect_websocket() {
     MozLoopPushHandler.uaID = undefined;
-    MozLoopPushHandler.registeredChannels = {}; //Do this to force a new registration callback.
     mockWebSocket.stop();
+    // Previously registered onRegistration callbacks will fire and be checked (see above).
   });
 
+  // Test that the PushHander will re-connect after the far-end disconnect.
+  // The uaID is cleared to force re-regsitration of all notification channels.
   add_test(function test_reopen_websocket() {
     MozLoopPushHandler.uaID = undefined;
     MozLoopPushHandler.registeredChannels = {}; //Do this to force a new registration callback.
     mockWebSocket.serverClose();
+    // Previously registered onRegistration callbacks will fire and be checked (see above).
   });
 
+  // Force a re-registration cycle and have the PushServer return a 500.
+  // A retry should occur and the registration then complete.
   add_test(function test_retry_registration() {
     MozLoopPushHandler.uaID = undefined;
-    MozLoopPushHandler.registeredChannels = {}; //Do this to force a new registration callback.
     mockWebSocket.initRegStatus = 500;
     mockWebSocket.stop();
   });
 
+  add_test(function test_reconnect_no_registration() {
+    let regCnt = 0;
+    MozLoopPushHandler.shutdown();
+    MozLoopPushHandler.initialize({mockWebSocket: mockWebSocket});
+    MozLoopPushHandler.register(
+      "test-chan",
+      function(err, url, id) {
+        Assert.equal(++regCnt, 1, "onRegistered should only be called once");
+        Assert.equal(err, null, "err should be null to indicate success");
+        Assert.equal(url, kEndPointUrl, "Should return push server application URL");
+        Assert.equal(id, "test-chan", "Should have channel id = test-chan");
+        mockWebSocket.stop();
+        setTimeout(run_next_test(), 0);
+      },
+      function(version, id) {
+        return;
+      });
+  });
+
+  add_test(function test_ping_websocket() {
+    let pingReceived = false,
+        socketClosed = false;
+    mockWebSocket.defaultMsgHandler = (msg) => {
+      pingReceived = true;
+      // Do not send a ping response.
+    }
+    mockWebSocket.close = () => {
+      socketClosed = true;
+    }
+
+    MozLoopPushHandler.shutdown();
+    MozLoopPushHandler.initialize({mockWebSocket: mockWebSocket});
+    MozLoopPushHandler.register(
+      "test-chan",
+      function(err, url) {
+        Assert.equal(err, null, "err should be null to indicate success");
+        waitForCondition(() => pingReceived).then(() => {
+          waitForCondition(() => socketClosed).then(() => {
+            run_next_test();
+          }, () => {
+            do_throw("should have closed the websocket");
+          });
+        }, () => {
+          do_throw("should have sent ping");
+        });
+      },
+      function(version) {
+        return;
+      });
+  });
+
+  add_test(function test_retry_pushurl() {
+    MozLoopPushHandler.shutdown();
+    loopServer.registerPathHandler("/push-server-config", (request, response) => {
+      // The PushHandler should retry the request for the push-server-config for
+      // each of these cases without throwing an error.
+      let n = 0;
+      switch (++pushServerRequestCount) {
+      case ++n:
+        // Non-200 response
+        response.setStatusLine(null, 500, "Retry");
+        response.processAsync();
+        response.finish();
+        break;
+      case ++n:
+        // missing parameter
+        response.setStatusLine(null, 200, "OK");
+        response.write(JSON.stringify({pushServerURI: null}));
+        response.processAsync();
+        response.finish();
+        break;
+      case ++n:
+        // json parse error
+        response.setStatusLine(null, 200, "OK");
+        response.processAsync();
+        response.finish();
+        break;
+      case ++n:
+        response.setStatusLine(null, 200, "OK");
+        response.write(JSON.stringify({pushServerURI: kServerPushUrl}));
+        response.processAsync();
+        response.finish();
+
+        run_next_test();
+        break;
+      }
+    });
+
+    do_check_true(MozLoopPushHandler.initialize({mockWebSocket: mockWebSocket}));
+  });
+
   function run_test() {
     setupFakeLoopServer();
 
+    loopServer.registerPathHandler("/push-server-config", (request, response) => {
+      response.setStatusLine(null, 200, "OK");
+      response.write(JSON.stringify({pushServerURI: kServerPushUrl}));
+      response.processAsync();
+      response.finish();
+    });
+
+    Services.prefs.setCharPref("services.push.serverURL", kServerPushUrl);
     Services.prefs.setIntPref("loop.retry_delay.start", 10); // 10 ms
     Services.prefs.setIntPref("loop.retry_delay.limit", 20); // 20 ms
+    Services.prefs.setIntPref("loop.ping.interval", 50); // 50 ms
+    Services.prefs.setIntPref("loop.ping.timeout", 20); // 20 ms
 
     do_register_cleanup(function() {
+      Services.prefs.clearUserPref("services.push.serverULR");
       Services.prefs.clearUserPref("loop.retry_delay.start");
       Services.prefs.clearUserPref("loop.retry_delay.limit");
-      Services.prefs.setCharPref("loop.server", kLoopServerUrl);
+      Services.prefs.clearUserPref("loop.ping.interval");
+      Services.prefs.clearUserPref("loop.ping.timeout");
     });
 
     run_next_test();
   };
 }
--- a/browser/components/loop/test/xpcshell/test_loopservice_busy.js
+++ b/browser/components/loop/test/xpcshell/test_loopservice_busy.js
@@ -123,17 +123,18 @@ add_task(function* test_busy_1guest_1fxa
 function run_test() {
   setupFakeLoopServer();
 
   // Setup fake login state so we get FxA requests.
   const MozLoopServiceInternal = Cu.import("resource:///modules/loop/MozLoopService.jsm", {}).MozLoopServiceInternal;
   MozLoopServiceInternal.fxAOAuthTokenData = {token_type:"bearer",access_token:"1bad3e44b12f77a88fe09f016f6a37c42e40f974bc7a8b432bb0d2f0e37e1752",scope:"profile"};
   MozLoopServiceInternal.fxAOAuthProfile = {email: "test@example.com", uid: "abcd1234"};
 
-  let mockWebSocket = new MockWebSocketChannel({defaultMsgHandler: msgHandler});
+  let mockWebSocket = new MockWebSocketChannel();
+  mockWebSocket.defaultMsgHandler = msgHandler;
   LoopCallsInternal.mocks.webSocket = mockWebSocket;
 
   Services.io.offline = false;
 
   // For each notification received from the PushServer, MozLoopService will first query
   // for any pending calls on the FxA hawk session and then again using the guest session.
   // A pair of response objects in the callsResponses array will be consumed for each
   // notification. The even calls object is for the FxA session, the odd the Guest session.