Bug 1395202 - Part 3: Create a testcase for nsICacheInfoChannel::cacheEntryId. r=michal, f=junior
authorHo-Pang Hsu <hopang.hsu@gmail.com>
Tue, 10 Oct 2017 04:10:00 -0400
changeset 386161 4896e4d4e3f52ba3ebb39448f0c5ef17ae8fd9ab
parent 386160 ed4c6ec74ab5ff38f7f6a35892ed98f9b1b9938a
child 386162 73a75509eb9240b978fce88eefbac73c99407838
push id32676
push userarchaeopteryx@coole-files.de
push dateFri, 13 Oct 2017 21:38:18 +0000
treeherdermozilla-central@a31334a65a1c [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmichal
bugs1395202
milestone58.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
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]