Bug 977786 - Add tests for nsProfileLock. r=ted
authorGian-Carlo Pascutto <gcp@mozilla.com>
Fri, 16 Sep 2016 17:31:14 +0200
changeset 357801 a8b9b1f7675ffbf450858dac44146dd8608024f6
parent 357800 0e42b1a442c466a7e1574e3b92c7bbf7e3b07dc5
child 357802 e59f96abc40be4726e79c0409edd173a9d2b17a0
push id1324
push usermtabara@mozilla.com
push dateMon, 16 Jan 2017 13:07:44 +0000
treeherdermozilla-release@a01c49833940 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersted
bugs977786
milestone51.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 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'