Bug 699843 - Asynchronous Favicons API - ReplaceFaviconDataFromDataURL Tests. r=mak
authorFelix Fung <ffung@mozilla.com>
Thu, 15 Dec 2011 16:56:21 -0800
changeset 82736 13390e878a3c8f5ff973351b503ad90ba1404d4a
parent 82735 6c1fd708628bff79c120c7161973732d8e710959
child 82737 6388d336d45163bae529f9bd84bceba6db8a6735
push id4089
push userffung@mozilla.com
push dateFri, 16 Dec 2011 01:07:32 +0000
treeherdermozilla-inbound@13390e878a3c [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmak
bugs699843
milestone11.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 699843 - Asynchronous Favicons API - ReplaceFaviconDataFromDataURL Tests. r=mak
toolkit/components/places/tests/unit/test_doReplaceFaviconDataFromDataURL.js
toolkit/components/places/tests/unit/xpcshell.ini
new file mode 100644
--- /dev/null
+++ b/toolkit/components/places/tests/unit/test_doReplaceFaviconDataFromDataURL.js
@@ -0,0 +1,310 @@
+/* Any copyright is dedicated to the Public Domain.
+ *    http://creativecommons.org/publicdomain/zero/1.0/ */
+
+/*
+ * Tests for mozIAsyncFavicons::replaceFaviconData()
+ */
+
+let iconsvc = PlacesUtils.favicons;
+let histsvc = PlacesUtils.history;
+
+let originalFavicon = {
+  file: do_get_file("favicon-normal16.png"),
+  uri: uri(do_get_file("favicon-normal16.png")),
+  data: readFileData(do_get_file("favicon-normal16.png")),
+  mimetype: "image/png"
+};
+
+let uniqueFaviconId = 0;
+function createFavicon(fileName) {
+  let tempdir = Services.dirsvc.get("TmpD", Ci.nsILocalFile);
+
+  // remove any existing file at the path we're about to copy to
+  let outfile = tempdir.clone();
+  outfile.append(fileName);
+  try { outfile.remove(false); } catch (e) {}
+
+  originalFavicon.file.copyToFollowingLinks(tempdir, fileName);
+
+  let stream = Cc["@mozilla.org/network/file-output-stream;1"]
+    .createInstance(Ci.nsIFileOutputStream);
+  stream.init(outfile, 0x02 | 0x08 | 0x10, 0600, 0);
+
+  // append some data that sniffers/encoders will ignore that will distinguish
+  // the different favicons we'll create
+  uniqueFaviconId++;
+  let uniqueStr = "uid:" + uniqueFaviconId;
+  stream.write(uniqueStr, uniqueStr.length);
+  stream.close();
+
+  do_check_eq(outfile.leafName.substr(0, fileName.length), fileName);
+
+  return {
+    file: outfile,
+    uri: uri(outfile),
+    data: readFileData(outfile),
+    mimetype: "image/png"
+  };
+}
+
+function createDataURLForFavicon(favicon) {
+  return "data:" + favicon.mimetype + ";base64," + toBase64(favicon.data);
+}
+
+// adds a test URI visit to the database
+function addVisit(aURI) {
+  let time = Date.now() * 1000;
+  histsvc.addVisit(aURI,
+                   time,
+                   null, // no referrer
+                   histsvc.TRANSITION_TYPED, // user typed in URL bar
+                   false, // not redirect
+                   0);
+}
+
+function checkAddSucceeded(pageURI, mimetype, data) {
+  let savedFaviconURI = iconsvc.getFaviconForPage(pageURI);
+  let outMimeType = {};
+  let outData = iconsvc.getFaviconData(savedFaviconURI, outMimeType, {});
+
+  // Ensure input and output are identical
+  do_check_eq(mimetype, outMimeType.value);
+  do_check_true(compareArrays(data, outData));
+  do_check_guid_for_uri(pageURI);
+}
+
+function checkCallbackSucceeded(callbackMimetype, callbackData, sourceMimetype, sourceData) {
+  do_check_eq(callbackMimetype, sourceMimetype);
+  do_check_true(compareArrays(callbackData, sourceData));
+}
+
+function run_test() {
+  // check that the favicon loaded correctly
+  do_check_eq(originalFavicon.data.length, 286);
+  run_next_test();
+};
+
+add_test(function test_replaceFaviconDataFromDataURL_validHistoryURI() {
+  do_log_info("test replaceFaviconDataFromDataURL for valid history uri");
+
+  let pageURI = uri("http://test1.bar/");
+  addVisit(pageURI);
+
+  let favicon = createFavicon("favicon1.png");
+  iconsvc.replaceFaviconDataFromDataURL(favicon.uri, createDataURLForFavicon(favicon));
+  iconsvc.setAndFetchFaviconForPage(pageURI, favicon.uri, true,
+    function test_replaceFaviconDataFromDataURL_validHistoryURI_check(aURI, aDataLen, aData, aMimeType) {
+      checkCallbackSucceeded(aMimeType, aData, favicon.mimetype, favicon.data);
+      checkAddSucceeded(pageURI, favicon.mimetype, favicon.data);
+
+      favicon.file.remove(false);
+      waitForClearHistory(run_next_test);
+    });
+});
+
+add_test(function test_replaceFaviconDataFromDataURL_overrideDefaultFavicon() {
+  do_log_info("test replaceFaviconDataFromDataURL to override a later setAndFetchFaviconForPage");
+
+  let pageURI = uri("http://test2.bar/");
+  addVisit(pageURI);
+
+  let firstFavicon = createFavicon("favicon2.png");
+  let secondFavicon = createFavicon("favicon3.png");
+
+  iconsvc.replaceFaviconDataFromDataURL(firstFavicon.uri, createDataURLForFavicon(secondFavicon));
+  iconsvc.setAndFetchFaviconForPage(
+    pageURI, firstFavicon.uri, true,
+    function test_replaceFaviconDataFromDataURL_overrideDefaultFavicon_check(aURI, aDataLen, aData, aMimeType) {
+      checkCallbackSucceeded(aMimeType, aData, secondFavicon.mimetype, secondFavicon.data);
+      checkAddSucceeded(pageURI, secondFavicon.mimetype, secondFavicon.data);
+
+      firstFavicon.file.remove(false);
+      secondFavicon.file.remove(false);
+      waitForClearHistory(run_next_test);
+    });
+});
+
+add_test(function test_replaceFaviconDataFromDataURL_replaceExisting() {
+  do_log_info("test replaceFaviconDataFromDataURL to override a previous setAndFetchFaviconForPage");
+
+  let pageURI = uri("http://test3.bar");
+  addVisit(pageURI);
+
+  let firstFavicon = createFavicon("favicon4.png");
+  let secondFavicon = createFavicon("favicon5.png");
+
+  iconsvc.setAndFetchFaviconForPage(
+    pageURI, firstFavicon.uri, true,
+    function test_replaceFaviconDataFromDataURL_replaceExisting_firstSet_check(aURI, aDataLen, aData, aMimeType) {
+      checkCallbackSucceeded(aMimeType, aData, firstFavicon.mimetype, firstFavicon.data);
+      checkAddSucceeded(pageURI, firstFavicon.mimetype, firstFavicon.data);
+
+      iconsvc.replaceFaviconDataFromDataURL(firstFavicon.uri, createDataURLForFavicon(secondFavicon));
+      waitForAsyncUpdates(function() {
+        checkAddSucceeded(pageURI, secondFavicon.mimetype, secondFavicon.data);
+        firstFavicon.file.remove(false);
+        secondFavicon.file.remove(false);
+        waitForClearHistory(run_next_test);
+      });
+    });
+});
+
+add_test(function test_replaceFaviconDataFromDataURL_unrelatedReplace() {
+  do_log_info("test replaceFaviconDataFromDataURL to not make unrelated changes");
+
+  let pageURI = uri("http://test4.bar/");
+  addVisit(pageURI);
+
+  let favicon = createFavicon("favicon6.png");
+  let unrelatedFavicon = createFavicon("favicon7.png");
+
+  iconsvc.replaceFaviconDataFromDataURL(unrelatedFavicon.uri, createDataURLForFavicon(unrelatedFavicon));
+  iconsvc.setAndFetchFaviconForPage(
+    pageURI, favicon.uri, true,
+    function test_replaceFaviconDataFromDataURL_unrelatedReplace_check(aURI, aDataLen, aData, aMimeType) {
+      checkCallbackSucceeded(aMimeType, aData, favicon.mimetype, favicon.data);
+      checkAddSucceeded(pageURI, favicon.mimetype, favicon.data);
+
+      favicon.file.remove(false);
+      unrelatedFavicon.file.remove(false);
+      waitForClearHistory(run_next_test);
+    });
+});
+
+add_test(function test_replaceFaviconDataFromDataURL_badInputs() {
+  do_log_info("test replaceFaviconDataFromDataURL to throw on bad inputs");
+
+  let favicon = createFavicon("favicon8.png");
+
+  let ex = null;
+  try {
+    iconsvc.replaceFaviconDataFromDataURL(favicon.uri, "");
+  } catch (e) {
+    ex = e;
+  } finally {
+    do_check_true(!!ex);
+  }
+
+  ex = null;
+  try {
+    iconsvc.replaceFaviconDataFromDataURL(null, createDataURLForFavicon(favicon));
+  } catch (e) {
+    ex = e;
+  } finally {
+    do_check_true(!!ex);
+  }
+
+  favicon.file.remove(false);
+  waitForClearHistory(run_next_test);
+});
+
+add_test(function test_replaceFaviconDataFromDataURL_twiceReplace() {
+  do_log_info("test replaceFaviconDataFromDataURL on multiple replacements");
+
+  let pageURI = uri("http://test5.bar/");
+  addVisit(pageURI);
+
+  let firstFavicon = createFavicon("favicon9.png");
+  let secondFavicon = createFavicon("favicon10.png");
+
+  iconsvc.replaceFaviconDataFromDataURL(firstFavicon.uri, createDataURLForFavicon(firstFavicon));
+  iconsvc.replaceFaviconDataFromDataURL(firstFavicon.uri, createDataURLForFavicon(secondFavicon));
+
+  iconsvc.setAndFetchFaviconForPage(
+    pageURI, firstFavicon.uri, true,
+    function test_replaceFaviconDataFromDataURL_twiceReplace_check(aURI, aDataLen, aData, aMimeType) {
+      checkCallbackSucceeded(aMimeType, aData, secondFavicon.mimetype, secondFavicon.data);
+      checkAddSucceeded(pageURI, secondFavicon.mimetype, secondFavicon.data);
+
+      firstFavicon.file.remove(false);
+      secondFavicon.file.remove(false);
+      waitForClearHistory(run_next_test);
+    });
+});
+
+add_test(function test_replaceFaviconDataFromDataURL_afterRegularAssign() {
+  do_log_info("test replaceFaviconDataFromDataURL after replaceFaviconData");
+
+  let pageURI = uri("http://test6.bar/");
+  addVisit(pageURI);
+
+  let firstFavicon = createFavicon("favicon11.png");
+  let secondFavicon = createFavicon("favicon12.png");
+
+  iconsvc.replaceFaviconData(
+    firstFavicon.uri, firstFavicon.data, firstFavicon.data.length,
+    firstFavicon.mimetype);
+  iconsvc.replaceFaviconDataFromDataURL(firstFavicon.uri, createDataURLForFavicon(secondFavicon));
+
+  iconsvc.setAndFetchFaviconForPage(
+    pageURI, firstFavicon.uri, true,
+    function test_replaceFaviconDataFromDataURL_twiceReplace_check(aURI, aDataLen, aData, aMimeType) {
+      checkCallbackSucceeded(aMimeType, aData, secondFavicon.mimetype, secondFavicon.data);
+      checkAddSucceeded(pageURI, secondFavicon.mimetype, secondFavicon.data);
+
+      firstFavicon.file.remove(false);
+      secondFavicon.file.remove(false);
+      waitForClearHistory(run_next_test);
+    });
+});
+
+add_test(function test_replaceFaviconDataFromDataURL_afterRegularAssign() {
+  do_log_info("test replaceFaviconDataFromDataURL before replaceFaviconData");
+
+  let pageURI = uri("http://test7.bar/");
+  addVisit(pageURI);
+
+  let firstFavicon = createFavicon("favicon13.png");
+  let secondFavicon = createFavicon("favicon14.png");
+
+  iconsvc.replaceFaviconDataFromDataURL(firstFavicon.uri, createDataURLForFavicon(firstFavicon));
+  iconsvc.replaceFaviconData(
+    firstFavicon.uri, secondFavicon.data, secondFavicon.data.length,
+    secondFavicon.mimetype);
+
+  iconsvc.setAndFetchFaviconForPage(
+    pageURI, firstFavicon.uri, true,
+    function test_replaceFaviconDataFromDataURL_twiceReplace_check(aURI, aDataLen, aData, aMimeType) {
+      checkCallbackSucceeded(aMimeType, aData, secondFavicon.mimetype, secondFavicon.data);
+      checkAddSucceeded(pageURI, secondFavicon.mimetype, secondFavicon.data);
+
+      firstFavicon.file.remove(false);
+      secondFavicon.file.remove(false);
+      waitForClearHistory(run_next_test);
+    });
+});
+
+/* toBase64 copied from image/test/unit/test_encoder_png.js */
+
+/* Convert data (an array of integers) to a Base64 string. */
+const toBase64Table = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz' +
+    '0123456789+/';
+const base64Pad = '=';
+function toBase64(data) {
+  let result = '';
+  let length = data.length;
+  let i;
+  // Convert every three bytes to 4 ascii characters.
+  for (i = 0; i < (length - 2); i += 3) {
+    result += toBase64Table[data[i] >> 2];
+    result += toBase64Table[((data[i] & 0x03) << 4) + (data[i+1] >> 4)];
+    result += toBase64Table[((data[i+1] & 0x0f) << 2) + (data[i+2] >> 6)];
+    result += toBase64Table[data[i+2] & 0x3f];
+  }
+
+  // Convert the remaining 1 or 2 bytes, pad out to 4 characters.
+  if (length%3) {
+    i = length - (length%3);
+    result += toBase64Table[data[i] >> 2];
+    if ((length%3) == 2) {
+      result += toBase64Table[((data[i] & 0x03) << 4) + (data[i+1] >> 4)];
+      result += toBase64Table[(data[i+1] & 0x0f) << 2];
+      result += base64Pad;
+    } else {
+      result += toBase64Table[(data[i] & 0x03) << 4];
+      result += base64Pad + base64Pad;
+    }
+  }
+
+  return result;
+}
--- a/toolkit/components/places/tests/unit/xpcshell.ini
+++ b/toolkit/components/places/tests/unit/xpcshell.ini
@@ -58,16 +58,17 @@ skip-if = os == "android"
 [test_bookmarks_setNullTitle.js]
 [test_broken_folderShortcut_result.js]
 [test_browserhistory.js]
 [test_bug636917_isLivemark.js]
 [test_childlessTags.js]
 [test_crash_476292.js]
 [test_database_replaceOnStartup.js]
 [test_doReplaceFaviconData.js]
+[test_doReplaceFaviconDataFromDataURL.js]
 [test_doSetAndLoadFaviconForPage.js]
 [test_doSetAndLoadFaviconForPage_failures.js]
 # Bug 676989: test fails consistently on Android
 fail-if = os == "android"
 [test_download_history.js]
 # Bug 676989: test fails consistently on Android
 fail-if = os == "android"
 [test_exclude_livemarks.js]