Bug 1395202 - Part 3: Create a testcase for nsICacheInfoChannel::cacheEntryId. r=michal, f=junior
☠☠ backed out by 68de54365bf4 ☠ ☠
authorHo-Pang Hsu <hopang.hsu@gmail.com>
Tue, 10 Oct 2017 04:10:00 -0400
changeset 427930 7598b4ed02f6f7bd14693b62f4ba61b8ae91315a
parent 427929 92e3c67a38722cd58b70da2dbadc877d66ab8db6
child 427931 6db4543e49c7f5fa3e2d1c9ec66c70d1344e12dc
push id97
push userfmarier@mozilla.com
push dateSat, 14 Oct 2017 01:12:59 +0000
reviewersmichal
bugs1395202
milestone58.0a1
Bug 1395202 - Part 3: Create a testcase for nsICacheInfoChannel::cacheEntryId. r=michal, f=junior
netwerk/test/unit/head_channels.js
netwerk/test/unit/test_cache-entry-id.js
netwerk/test/unit/xpcshell.ini
netwerk/test/unit_ipc/test_cache-entry-id_wrap.js
netwerk/test/unit_ipc/xpcshell.ini
--- a/netwerk/test/unit/head_channels.js
+++ b/netwerk/test/unit/head_channels.js
@@ -48,16 +48,17 @@ const SUSPEND_DELAY = 3000;
  * Note that it also requires a valid content length on the channel and
  * is thus not fully generic.
  */
 function ChannelListener(closure, ctx, flags) {
   this._closure = closure;
   this._closurectx = ctx;
   this._flags = flags;
   this._isFromCache = false;
+  this._cacheEntryId = undefined;
 }
 ChannelListener.prototype = {
   _closure: null,
   _closurectx: null,
   _buffer: "",
   _got_onstartrequest: false,
   _got_onstoprequest: false,
   _contentLen: -1,
@@ -74,19 +75,30 @@ ChannelListener.prototype = {
   onStartRequest: function(request, context) {
     try {
       if (this._got_onstartrequest)
         do_throw("Got second onStartRequest event!");
       this._got_onstartrequest = true;
       this._lastEvent = Date.now();
 
       try {
-        this._isFromCache = request.QueryInterface(Ci.nsICachingChannel).isFromCache();
+        this._isFromCache = request.QueryInterface(Ci.nsICacheInfoChannel).isFromCache();
       } catch (e) {}
 
+      var thrown = false;
+      try {
+        this._cacheEntryId = request.QueryInterface(Ci.nsICacheInfoChannel).getCacheEntryId();
+      } catch (e) {
+        thrown = true;
+      }
+      if (this._isFromCache && thrown)
+        do_throw("Should get a CacheEntryId");
+      else if (!this._isFromCache && !thrown)
+        do_throw("Shouldn't get a CacheEntryId");
+
       request.QueryInterface(Components.interfaces.nsIChannel);
       try {
         this._contentLen = request.contentLength;
       }
       catch (ex) {
         if (!(this._flags & (CL_EXPECT_FAILURE | CL_ALLOW_UNKNOWN_CL)))
           do_throw("Could not get contentLength");
       }
@@ -166,17 +178,21 @@ ChannelListener.prototype = {
       if (!(this._flags & (CL_EXPECT_FAILURE | CL_EXPECT_LATE_FAILURE | CL_IGNORE_CL)) &&
           !(this._flags & CL_EXPECT_GZIP) &&
           this._contentLen != -1)
           do_check_eq(this._buffer.length, this._contentLen)
     } catch (ex) {
       do_throw("Error in onStopRequest: " + ex);
     }
     try {
-      this._closure(request, this._buffer, this._closurectx, this._isFromCache);
+      this._closure(request,
+                    this._buffer,
+                    this._closurectx,
+                    this._isFromCache,
+                    this._cacheEntryId);
       this._closurectx = null;
     } catch (ex) {
       do_throw("Error in closure function: " + ex);
     }
   }
 };
 
 var ES_ABORT_REDIRECT = 0x01;
new file mode 100644
--- /dev/null
+++ b/netwerk/test/unit/test_cache-entry-id.js
@@ -0,0 +1,138 @@
+/**
+ * Test for the "CacheEntryId" under several cases.
+ */
+
+Cu.import("resource://testing-common/httpd.js");
+Cu.import("resource://gre/modules/NetUtil.jsm");
+Cu.import("resource://gre/modules/Services.jsm");
+
+XPCOMUtils.defineLazyGetter(this, "URL", function() {
+  return "http://localhost:" + httpServer.identity.primaryPort + "/content";
+});
+
+var httpServer = null;
+
+const responseContent = "response body";
+const responseContent2 = "response body 2";
+const altContent = "!@#$%^&*()";
+const altContentType = "text/binary";
+
+var handlers = [
+  (m, r) => {r.bodyOutputStream.write(responseContent, responseContent.length)},
+  (m, r) => {r.setStatusLine(m.httpVersion, 304, "Not Modified")},
+  (m, r) => {r.setStatusLine(m.httpVersion, 304, "Not Modified")},
+  (m, r) => {r.setStatusLine(m.httpVersion, 304, "Not Modified")},
+  (m, r) => {r.setStatusLine(m.httpVersion, 304, "Not Modified")},
+  (m, r) => {r.bodyOutputStream.write(responseContent2, responseContent2.length)},
+  (m, r) => {r.setStatusLine(m.httpVersion, 304, "Not Modified")},
+];
+
+function contentHandler(metadata, response)
+{
+  response.setHeader("Content-Type", "text/plain");
+  response.setHeader("Cache-Control", "no-cache");
+
+  var handler = handlers.shift();
+  if (handler) {
+    handler(metadata, response);
+    return;
+  }
+
+  do_check_true(false, "Should not reach here.");
+}
+
+function fetch(preferredDataType = null)
+{
+  return new Promise(resolve => {
+    var chan = NetUtil.newChannel({uri: URL, loadUsingSystemPrincipal: true});
+
+    if (preferredDataType) {
+      var cc = chan.QueryInterface(Ci.nsICacheInfoChannel);
+      cc.preferAlternativeDataType(altContentType);
+    }
+
+    chan.asyncOpen2(new ChannelListener((request,
+                                         buffer,
+                                         ctx,
+                                         isFromCache,
+                                         cacheEntryId) => {
+      resolve({request, buffer, isFromCache, cacheEntryId});
+    }, null));
+  });
+}
+
+function check(response, content, preferredDataType, isFromCache, cacheEntryIdChecker)
+{
+  var cc = response.request.QueryInterface(Ci.nsICacheInfoChannel);
+
+  do_check_eq(response.buffer, content);
+  do_check_eq(cc.alternativeDataType, preferredDataType);
+  do_check_eq(response.isFromCache, isFromCache);
+  do_check_true(!cacheEntryIdChecker || cacheEntryIdChecker(response.cacheEntryId));
+
+  return response;
+}
+
+function writeAltData(request)
+{
+  var cc = request.QueryInterface(Ci.nsICacheInfoChannel);
+  var os = cc.openAlternativeOutputStream(altContentType);
+  os.write(altContent, altContent.length);
+  os.close();
+  gc(); // We need to do a GC pass to ensure the cache entry has been freed.
+
+  return new Promise(resolve => {
+    Services.cache2.QueryInterface(Ci.nsICacheTesting)
+            .flush(resolve);
+  });
+}
+
+function run_test()
+{
+  do_get_profile();
+  httpServer = new HttpServer();
+  httpServer.registerPathHandler("/content", contentHandler);
+  httpServer.start(-1);
+  do_test_pending();
+
+  var targetCacheEntryId = null;
+
+  return Promise.resolve()
+    // Setup testing environment: Placing alternative data into HTTP cache.
+    .then(_ => fetch(altContentType))
+    .then(r => check(r, responseContent, "", false,
+                     cacheEntryId => cacheEntryId === undefined))
+    .then(r => writeAltData(r.request))
+
+    // Start testing.
+    .then(_ => fetch(altContentType))
+    .then(r => check(r, altContent, altContentType, true,
+                     cacheEntryId => cacheEntryId !== undefined))
+    .then(r => targetCacheEntryId = r.cacheEntryId)
+
+    .then(_ => fetch())
+    .then(r => check(r, responseContent, "", true,
+                     cacheEntryId => cacheEntryId === targetCacheEntryId))
+
+    .then(_ => fetch(altContentType))
+    .then(r => check(r, altContent, altContentType, true,
+                     cacheEntryId => cacheEntryId === targetCacheEntryId))
+
+    .then(_ => fetch())
+    .then(r => check(r, responseContent, "", true,
+                     cacheEntryId => cacheEntryId === targetCacheEntryId))
+
+    .then(_ => fetch()) // The response is changed here.
+    .then(r => check(r, responseContent2, "", false,
+                     cacheEntryId => cacheEntryId === undefined))
+
+    .then(_ => fetch())
+    .then(r => check(r, responseContent2, "", true,
+                     cacheEntryId => cacheEntryId !== undefined &&
+                                     cacheEntryId !== targetCacheEntryId))
+
+    // Tear down.
+    .catch(e => do_check_true(false, "Unexpected exception: " + e))
+    .then(_ => do_check_eq(handlers.length, 0))
+    .then(_ => httpServer.stop(do_test_finished));
+}
--- a/netwerk/test/unit/xpcshell.ini
+++ b/netwerk/test/unit/xpcshell.ini
@@ -175,16 +175,17 @@ skip-if = bits != 32
 [test_bug1064258.js]
 [test_bug1177909.js]
 [test_bug1218029.js]
 [test_udpsocket.js]
 [test_udpsocket_offline.js]
 [test_doomentry.js]
 [test_cacheflags.js]
 [test_cache_jar.js]
+[test_cache-entry-id.js]
 [test_channel_close.js]
 [test_compareURIs.js]
 [test_compressappend.js]
 [test_content_encoding_gzip.js]
 [test_content_sniffer.js]
 [test_cookie_header.js]
 [test_cookiejars.js]
 [test_cookiejars_safebrowsing.js]
new file mode 100644
--- /dev/null
+++ b/netwerk/test/unit_ipc/test_cache-entry-id_wrap.js
@@ -0,0 +1,3 @@
+function run_test() {
+  run_test_in_child("../unit/test_cache-entry-id.js");
+}
--- a/netwerk/test/unit_ipc/xpcshell.ini
+++ b/netwerk/test/unit_ipc/xpcshell.ini
@@ -1,16 +1,17 @@
 [DEFAULT]
 head = head_channels_clone.js head_cc.js
 skip-if = toolkit == 'android'
 support-files =
   child_channel_id.js
   !/netwerk/test/unit/test_XHR_redirects.js
   !/netwerk/test/unit/test_bug248970_cookie.js
   !/netwerk/test/unit/test_bug528292.js
+  !/netwerk/test/unit/test_cache-entry-id.js
   !/netwerk/test/unit/test_cache_jar.js
   !/netwerk/test/unit/test_cacheflags.js
   !/netwerk/test/unit/test_channel_close.js
   !/netwerk/test/unit/test_cookie_header.js
   !/netwerk/test/unit/test_cookiejars.js
   !/netwerk/test/unit/test_dns_cancel.js
   !/netwerk/test/unit/test_dns_per_interface.js
   !/netwerk/test/unit/test_dns_service.js
@@ -56,16 +57,17 @@ support-files =
   !/netwerk/test/unit/test_alt-data_simple.js
   !/netwerk/test/unit/test_alt-data_stream.js
   !/netwerk/test/unit/test_channel_priority.js
   !/netwerk/test/unit/test_multipart_streamconv.js
 
 [test_bug528292_wrap.js]
 [test_bug248970_cookie_wrap.js]
 [test_cacheflags_wrap.js]
+[test_cache-entry-id_wrap.js]
 [test_cache_jar_wrap.js]
 [test_channel_close_wrap.js]
 [test_cookie_header_wrap.js]
 [test_cookiejars_wrap.js]
 [test_dns_cancel_wrap.js]
 [test_dns_per_interface_wrap.js]
 [test_dns_service_wrap.js]
 [test_duplicate_headers_wrap.js]