Bug 977786 - Add tests for nsProfileLock. r=ted
authorGian-Carlo Pascutto <gcp@mozilla.com>
Fri, 16 Sep 2016 17:31:14 +0200
changeset 342812 a8b9b1f7675ffbf450858dac44146dd8608024f6
parent 342811 0e42b1a442c466a7e1574e3b92c7bbf7e3b07dc5
child 342813 e59f96abc40be4726e79c0409edd173a9d2b17a0
push id6570
push userraliiev@mozilla.com
push dateMon, 14 Nov 2016 12:26:13 +0000
treeherdermozilla-esr52@f455459b2ae5 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersted
bugs977786
milestone51.0a1
Bug 977786 - Add tests for nsProfileLock. r=ted MozReview-Commit-ID: 6Cr0EwVwVIq
config/system-headers
toolkit/profile/gtest/TestProfileLock.cpp
toolkit/profile/gtest/moz.build
toolkit/profile/moz.build
--- a/config/system-headers
+++ b/config/system-headers
@@ -995,16 +995,17 @@ sys/bitypes.h
 sys/byteorder.h
 syscall.h
 sys/cdefs.h
 sys/cfgodm.h
 sys/elf.h
 sys/endian.h
 sys/epoll.h
 sys/errno.h
+sys/eventfd.h
 sys/fault.h
 sys/fcntl.h
 sys/file.h
 sys/filio.h
 sys/frame.h
 sys/immu.h
 sys/inotify.h
 sys/inttypes.h
new file mode 100644
--- /dev/null
+++ b/toolkit/profile/gtest/TestProfileLock.cpp
@@ -0,0 +1,116 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "gtest/gtest.h"
+
+#include <sys/eventfd.h>
+#include <sched.h>
+
+#include "nsCOMPtr.h"
+#include "nsIFile.h"
+#include "nsProfileLock.h"
+#include "nsString.h"
+
+static void CleanParentLock(const char *tmpdir)
+{
+  // nsProfileLock doesn't clean up all its files
+  char permanent_lockfile[] = "/.parentlock";
+
+  char * parentlock_name;
+  size_t parentlock_name_size = strlen(tmpdir) + strlen(permanent_lockfile) + 1;
+  parentlock_name = (char*)malloc(parentlock_name_size);
+
+  strcpy(parentlock_name, tmpdir);
+  strcat(parentlock_name, permanent_lockfile);
+
+  EXPECT_EQ(0, unlink(parentlock_name));
+  EXPECT_EQ(0, rmdir(tmpdir));
+  free(parentlock_name);
+}
+
+TEST(ProfileLock, BasicLock)
+{
+  char templ[] = "/tmp/profilelocktest.XXXXXX";
+  char * tmpdir = mkdtemp(templ);
+  ASSERT_NE(tmpdir, nullptr);
+
+  // This scope exits so the nsProfileLock destructor
+  // can clean up the files it leaves in tmpdir.
+  {
+    nsProfileLock myLock;
+    nsresult rv;
+    nsCOMPtr<nsIFile> dir(do_CreateInstance(NS_LOCAL_FILE_CONTRACTID, &rv));
+    ASSERT_EQ(NS_SUCCEEDED(rv), true);
+
+    rv = dir->InitWithNativePath(nsCString(tmpdir));
+    ASSERT_EQ(NS_SUCCEEDED(rv), true);
+
+    rv = myLock.Lock(dir, nullptr);
+    EXPECT_EQ(NS_SUCCEEDED(rv), true);
+  }
+
+  CleanParentLock(tmpdir);
+}
+
+TEST(ProfileLock, RetryLock)
+{
+  char templ[] = "/tmp/profilelocktest.XXXXXX";
+  char * tmpdir = mkdtemp(templ);
+  ASSERT_NE(tmpdir, nullptr);
+
+  {
+    nsProfileLock myLock;
+    nsProfileLock myLock2;
+    nsresult rv;
+    nsCOMPtr<nsIFile> dir(do_CreateInstance(NS_LOCAL_FILE_CONTRACTID, &rv));
+    ASSERT_EQ(NS_SUCCEEDED(rv), true);
+
+    rv = dir->InitWithNativePath(nsCString(tmpdir));
+    ASSERT_EQ(NS_SUCCEEDED(rv), true);
+
+    int eventfd_fd = eventfd(0, 0);
+    ASSERT_GE(eventfd_fd, 0);
+
+    // fcntl advisory locks are per process, so it's hard
+    // to avoid using fork here.
+    pid_t childpid = fork();
+
+    if (childpid) {
+      // parent
+
+      // blocking read causing us to lose the race
+      eventfd_t value;
+      EXPECT_EQ(0, eventfd_read(eventfd_fd, &value));
+
+      rv = myLock2.Lock(dir, nullptr);
+      EXPECT_EQ(NS_ERROR_FILE_ACCESS_DENIED, rv);
+
+      // kill the child
+      EXPECT_EQ(0, kill(childpid, SIGTERM));
+
+      // reap zombie (required to collect the lock)
+      int status;
+      EXPECT_EQ(childpid, waitpid(childpid, &status, 0));
+
+      rv = myLock2.Lock(dir, nullptr);
+      EXPECT_EQ(NS_SUCCEEDED(rv), true);
+    } else {
+      // child
+      rv = myLock.Lock(dir, nullptr);
+      ASSERT_EQ(NS_SUCCEEDED(rv), true);
+
+      // unblock parent
+      EXPECT_EQ(0, eventfd_write(eventfd_fd, 1));
+
+      // parent will kill us
+      for (;;)
+        sleep(1);
+    }
+
+    close(eventfd_fd);
+  }
+
+  CleanParentLock(tmpdir);
+}
new file mode 100644
--- /dev/null
+++ b/toolkit/profile/gtest/moz.build
@@ -0,0 +1,16 @@
+# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+LOCAL_INCLUDES += [
+    '..',
+]
+
+if CONFIG['OS_ARCH'] == 'Linux':
+    UNIFIED_SOURCES += [
+        'TestProfileLock.cpp',
+    ]
+
+FINAL_LIBRARY = 'xul-gtest'
--- a/toolkit/profile/moz.build
+++ b/toolkit/profile/moz.build
@@ -1,16 +1,19 @@
 # -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
 # vim: set filetype=python:
 # This Source Code Form is subject to the terms of the Mozilla Public
 # License, v. 2.0. If a copy of the MPL was not distributed with this
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
 MOCHITEST_CHROME_MANIFESTS += ['test/chrome.ini']
 
+if CONFIG['ENABLE_TESTS']:
+    DIRS += ['gtest']
+
 XPIDL_SOURCES += [
     'nsIProfileMigrator.idl',
     'nsIProfileUnlocker.idl',
     'nsIToolkitProfile.idl',
     'nsIToolkitProfileService.idl',
 ]
 
 XPIDL_MODULE = 'toolkitprofile'