Bug 959591 - Add a test to verify functionality. r=fabrice
authorDave Hylands <dhylands@mozilla.com>
Wed, 12 Mar 2014 14:15:05 -0700
changeset 173268 edfea182712fcbbda19ccf436e506076b9784f49
parent 173267 cdcc9accb82216d155aa2fa704bde0cbc2afe82b
child 173269 ebc007dafa31a4d05c84db9001f1996ec3871ba6
push id26397
push userkwierso@gmail.com
push dateThu, 13 Mar 2014 02:51:22 +0000
treeherdermozilla-central@c12c92db5588 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersfabrice
bugs959591
milestone30.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 959591 - Add a test to verify functionality. r=fabrice
dom/devicestorage/nsDeviceStorage.cpp
dom/devicestorage/test/mochitest.ini
dom/devicestorage/test/test_overrideDir.html
testing/mochitest/manifest.webapp
--- a/dom/devicestorage/nsDeviceStorage.cpp
+++ b/dom/devicestorage/nsDeviceStorage.cpp
@@ -585,16 +585,126 @@ DeviceStorageFile::Init()
                                              mStorageName,
                                              getter_AddRefs(mFile));
 
   DebugOnly<DeviceStorageTypeChecker*> typeChecker
     = DeviceStorageTypeChecker::CreateOrGet();
   MOZ_ASSERT(typeChecker);
 }
 
+// The OverrideRootDir is needed to facilitate testing of the
+// device.storage.overrideRootDir preference. The preference is normally
+// only read once during initialization, but since the test environment has
+// no convenient way to restart, we use a pref watcher instead.
+class OverrideRootDir MOZ_FINAL : public nsIObserver
+{
+public:
+  NS_DECL_ISUPPORTS
+  NS_DECL_NSIOBSERVER
+
+  static OverrideRootDir* GetSingleton();
+  ~OverrideRootDir();
+  void Init();
+private:
+  static mozilla::StaticRefPtr<OverrideRootDir> sSingleton;
+};
+
+NS_IMPL_ISUPPORTS1(OverrideRootDir, nsIObserver)
+
+mozilla::StaticRefPtr<OverrideRootDir>
+  OverrideRootDir::sSingleton;
+
+OverrideRootDir*
+OverrideRootDir::GetSingleton()
+{
+  if (sSingleton) {
+    return sSingleton;
+  }
+  // Preference changes are automatically forwarded from parent to child
+  // in ContentParent::Observe, so we'll see the change in both the parent
+  // and the child process.
+
+  sSingleton = new OverrideRootDir();
+  Preferences::AddStrongObserver(sSingleton, "device.storage.overrideRootDir");
+  ClearOnShutdown(&sSingleton);
+
+  return sSingleton;
+}
+
+OverrideRootDir::~OverrideRootDir()
+{
+  Preferences::RemoveObserver(this, "device.storage.overrideRootDir");
+}
+
+NS_IMETHODIMP
+OverrideRootDir::Observe(nsISupports *aSubject,
+                              const char *aTopic,
+                              const char16_t *aData)
+{
+  MOZ_ASSERT(NS_IsMainThread());
+
+  if (sSingleton) {
+    sSingleton->Init();
+  }
+  return NS_OK;
+}
+
+void
+OverrideRootDir::Init()
+{
+  MOZ_ASSERT(NS_IsMainThread());
+
+  if (!sDirs) {
+    return;
+  }
+
+  if (mozilla::Preferences::GetBool("device.storage.testing", false)) {
+    nsCOMPtr<nsIProperties> dirService
+      = do_GetService(NS_DIRECTORY_SERVICE_CONTRACTID);
+    MOZ_ASSERT(dirService);
+    dirService->Get(NS_OS_TEMP_DIR, NS_GET_IID(nsIFile),
+                    getter_AddRefs(sDirs->overrideRootDir));
+    if (sDirs->overrideRootDir) {
+      sDirs->overrideRootDir->AppendRelativeNativePath(
+        NS_LITERAL_CSTRING("device-storage-testing"));
+    }
+  } else {
+    // For users running on desktop, it's convenient to be able to override
+    // all of the directories to point to a single tree, much like what happens
+    // on a real device.
+    const nsAdoptingString& overrideRootDir =
+      mozilla::Preferences::GetString("device.storage.overrideRootDir");
+    if (overrideRootDir && overrideRootDir.Length() > 0) {
+      NS_NewLocalFile(overrideRootDir, false,
+                      getter_AddRefs(sDirs->overrideRootDir));
+    } else {
+      sDirs->overrideRootDir = nullptr;
+    }
+  }
+
+  if (sDirs->overrideRootDir) {
+    if (XRE_GetProcessType() == GeckoProcessType_Default) {
+      // Only the parent process can create directories. In testing, because
+      // the preference is updated after startup, its entirely possible that
+      // the preference updated notification will be received by a child
+      // prior to the parent.
+      nsresult rv
+        = sDirs->overrideRootDir->Create(nsIFile::DIRECTORY_TYPE, 0777);
+      nsString path;
+      sDirs->overrideRootDir->GetPath(path);
+      if (NS_FAILED(rv) && rv != NS_ERROR_FILE_ALREADY_EXISTS) {
+        nsPrintfCString msg("DeviceStorage: Unable to create directory '%s'",
+                            NS_LossyConvertUTF16toASCII(path).get());
+        NS_WARNING(msg.get());
+      }
+    }
+    sDirs->overrideRootDir->Normalize();
+  }
+}
+
 // Directories which don't depend on a volume should be calculated once
 // here. Directories which depend on the root directory of a volume
 // should be calculated in DeviceStorageFile::GetRootDirectoryForType.
 static void
 InitDirs()
 {
   if (sDirs) {
     return;
@@ -672,47 +782,17 @@ InitDirs()
     // gAppData from toolkit/xre/nsAppRunner.cpp is not initialized.
 #ifdef MOZ_WIDGET_GONK
     NS_NewLocalFile(NS_LITERAL_STRING("/data/b2g/mozilla/Crash Reports"),
                                       false,
                                       getter_AddRefs(sDirs->crashes));
 #endif
   }
 
-  if (mozilla::Preferences::GetBool("device.storage.testing", false)) {
-    dirService->Get(NS_OS_TEMP_DIR, NS_GET_IID(nsIFile),
-                    getter_AddRefs(sDirs->overrideRootDir));
-    if (sDirs->overrideRootDir) {
-      sDirs->overrideRootDir->AppendRelativeNativePath(
-        NS_LITERAL_CSTRING("device-storage-testing"));
-    }
-  } else {
-    // For users running on desktop, it's convenient to be able to override
-    // all of the directories to point to a single tree, much like what happens
-    // on a real device.
-    const nsAdoptingString& overrideRootDir =
-      mozilla::Preferences::GetString("device.storage.overrideRootDir");
-    if (overrideRootDir) {
-      NS_NewLocalFile(overrideRootDir, false,
-                      getter_AddRefs(sDirs->overrideRootDir));
-    }
-  }
-
-  if (sDirs->overrideRootDir) {
-    nsresult rv
-      = sDirs->overrideRootDir->Create(nsIFile::DIRECTORY_TYPE, 0777);
-    if (NS_FAILED(rv) && rv != NS_ERROR_FILE_ALREADY_EXISTS) {
-      nsString path;
-      sDirs->overrideRootDir->GetPath(path);
-      nsPrintfCString msg("DeviceStorage: Unable to create directory '%s'",
-                          NS_LossyConvertUTF16toASCII(path).get());
-      NS_WARNING(msg.get());
-    }
-    sDirs->overrideRootDir->Normalize();
-  }
+  OverrideRootDir::GetSingleton()->Init();
 }
 
 void
 DeviceStorageFile::GetFullPath(nsAString &aFullPath)
 {
   aFullPath.Truncate();
   if (!mStorageName.EqualsLiteral("")) {
     aFullPath.AppendLiteral("/");
--- a/dom/devicestorage/test/mochitest.ini
+++ b/dom/devicestorage/test/mochitest.ini
@@ -3,16 +3,17 @@ skip-if = toolkit == 'android' #bug 7817
 support-files = devicestorage_common.js
 
 [test_823965.html]
 # [test_add.html]
 # man, our mime database sucks hard.  followup bug # 788273
 [test_addCorrectType.html]
 [test_available.html]
 [test_basic.html]
+[test_overrideDir.html]
 # [test_diskSpace.html]
 # Possible race between the time we write a file, and the
 # time it takes to be reflected by statfs().  Bug # 791287
 [test_dotdot.html]
 [test_enumerate.html]
 [test_enumerateMultipleContinue.html]
 [test_enumerateOptions.html]
 [test_freeSpace.html]
new file mode 100644
--- /dev/null
+++ b/dom/devicestorage/test/test_overrideDir.html
@@ -0,0 +1,163 @@
+<!--
+  Any copyright is dedicated to the Public Domain.
+  http://creativecommons.org/publicdomain/zero/1.0/
+-->
+<!DOCTYPE HTML>
+<html> <!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=717103
+-->
+<head>
+  <title>Test for the device storage API </title>
+  <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <script type="text/javascript" src="devicestorage_common.js"></script>
+
+<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=717103">Mozilla Bug 717103</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+  
+</div>
+<pre id="test">
+<script class="testbody" type="text/javascript">
+
+devicestorage_setup();
+
+var gFileName = "devicestorage/" + randomFilename(12) + "/hi.png";
+var gData = "My name is Doug Turner.  My IRC nick is DougT.  I like Maple cookies."
+var gDataBlob = new Blob([gData], {type: 'image/png'});
+var gFileReader = new FileReader();
+
+function getAfterDeleteSuccess(e) {
+  ok(false, "file was deleted not successfully");
+  devicestorage_cleanup();
+}
+
+function getAfterDeleteError(e) {
+  ok(true, "file was deleted successfully");
+  devicestorage_cleanup();
+}
+
+function deleteSuccess(e) {
+
+  ok(e.target.result == gFileName, "File name should match");
+  dump(e.target.result + "\n")
+
+  // File was deleted using the sdcard stoage area. It should be gone
+  // from the pictures as well.
+  var storage = navigator.getDeviceStorage("pictures");
+  request = storage.get(e.target.result);
+  request.onsuccess = getAfterDeleteSuccess;
+  request.onerror = getAfterDeleteError;
+}
+
+function deleteError(e) {
+  ok(false, "deleteError was called : " + e.target.error.name);
+  devicestorage_cleanup();
+}
+
+function getSuccess(e) {
+  // We wrote the file out using pictures type. Since we've over-ridden the
+  // root directory, we should be able to read it back using the sdcard
+  // storage area.
+  var storage = navigator.getDeviceStorage("sdcard");
+  ok(navigator.getDeviceStorage, "Should have getDeviceStorage");
+
+  ok(e.target.result.name == gFileName, "File name should match");
+  ok(e.target.result.size > 0, "File size be greater than zero");
+  ok(e.target.result.type, "File should have a mime type");
+  ok(e.target.result.lastModifiedDate, "File should have a last modified date");
+
+  var name = e.target.result.name;
+
+  gFileReader.readAsArrayBuffer(gDataBlob);
+  gFileReader.onload = function(e) {
+    readerCallback(e);
+
+    request = storage.delete(name)
+    request.onsuccess = deleteSuccess;
+    request.onerror = deleteError;
+  }
+}
+
+function readerCallback(e) {
+
+  ab = e.target.result;
+
+  is(ab.byteLength, gData.length, "wrong arraybuffer byteLength");
+  var u8v = new Uint8Array(ab);
+  is(String.fromCharCode.apply(String, u8v), gData, "wrong values");
+}
+
+function getError(e) {
+  ok(false, "getError was called : " + e.target.error.name);
+  devicestorage_cleanup();
+}
+
+function addSuccess(e) {
+
+  var filename = e.target.result;
+  if (filename[0] == "/") {
+    // We got /storageName/prefix/filename
+    // Remove the storageName (this shows up on FirefoxOS)
+    filename = filename.substring(1); // Remove leading slash
+    var slashIndex = filename.indexOf("/");
+    if (slashIndex >= 0) {
+      filename = filename.substring(slashIndex + 1); // Remove storageName
+    }
+  }
+  ok(filename == gFileName, "File name should match");
+
+  // Update gFileName to be the fully qualified name so that
+  // further checks will pass.
+  gFileName = e.target.result;
+
+  var storage = navigator.getDeviceStorage("pictures");
+  request = storage.get(gFileName);
+  request.onsuccess = getSuccess;
+  request.onerror = getError;
+
+  ok(true, "addSuccess was called");
+}
+
+function addError(e) {
+  ok(false, "addError was called : " + e.target.error.name);
+  devicestorage_cleanup();
+}
+
+function startTest() {
+  ok(navigator.getDeviceStorage, "Should have getDeviceStorage");
+
+  var storage = navigator.getDeviceStorage("pictures");
+  ok(storage, "Should have gotten a storage");
+
+  request = storage.addNamed(gDataBlob, gFileName);
+  ok(request, "Should have a non-null request");
+
+  request.onsuccess = addSuccess;
+  request.onerror = addError;
+}
+
+try {
+  const Cc = SpecialPowers.Cc;
+  const Ci = SpecialPowers.Ci;
+  var directoryService = Cc["@mozilla.org/file/directory_service;1"].getService(Ci.nsIProperties);
+  var f = directoryService.get("TmpD", Ci.nsIFile);
+  f.appendRelativePath("device-storage-sdcard");
+  try {
+    // The remove will fail if the directory doesn't exist, which is fine.
+    f.remove(true);
+  } catch (e) {}
+  SpecialPowers.pushPrefEnv({'set': [["device.storage.overrideRootDir", f.path],
+                                     ["device.storage.testing", false]]},
+    function() {
+      startTest();
+    });
+} catch(e) {}
+
+</script>
+</pre>
+</body>
+</html>
+
--- a/testing/mochitest/manifest.webapp
+++ b/testing/mochitest/manifest.webapp
@@ -11,16 +11,17 @@
     "browser":{},
     "power":{},
     "webapps-manage":{},
     "mobileconnection":{},
     "bluetooth":{},
     "telephony":{},
     "voicemail":{},
     "device-storage:pictures":{ "access": "readwrite" },
+    "device-storage:sdcard":{ "access": "readwrite" },
     "settings":{ "access": "readwrite" },
     "storage":{},
     "camera":{},
     "geolocation":{},
     "wifi-manage":{},
     "desktop-notification":{},
     "idle":{},
     "network-events":{},