Home | History | Annotate | Download | only in browser
      1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style license that can be
      3 // found in the LICENSE file.
      4 
      5 #include <errno.h>
      6 #include <fcntl.h>
      7 #include <sys/file.h>
      8 
      9 #include "chrome/browser/process_singleton.h"
     10 
     11 #include "base/file_util.h"
     12 #include "base/path_service.h"
     13 #include "base/posix/eintr_wrapper.h"
     14 #include "chrome/common/chrome_constants.h"
     15 #include "chrome/common/chrome_paths.h"
     16 #include "chrome/test/base/testing_profile.h"
     17 #include "testing/platform_test.h"
     18 
     19 namespace {
     20 
     21 class ProcessSingletonMacTest : public PlatformTest {
     22  public:
     23   virtual void SetUp() {
     24     PlatformTest::SetUp();
     25 
     26     // Put the lock in a temporary directory.  Doesn't need to be a
     27     // full profile to test this code.
     28     ASSERT_TRUE(temp_dir_.CreateUniqueTempDir());
     29     lock_path_ = temp_dir_.path().Append(chrome::kSingletonLockFilename);
     30   }
     31 
     32   virtual void TearDown() {
     33     PlatformTest::TearDown();
     34 
     35     // Verify that the lock was released.
     36     EXPECT_FALSE(IsLocked());
     37   }
     38 
     39   // Return |true| if the file exists and is locked.  Forces a failure
     40   // in the containing test in case of error condition.
     41   bool IsLocked() {
     42     int fd = HANDLE_EINTR(open(lock_path_.value().c_str(), O_RDONLY));
     43     if (fd == -1) {
     44       EXPECT_EQ(ENOENT, errno) << "Unexpected error opening lockfile.";
     45       return false;
     46     }
     47 
     48     file_util::ScopedFD auto_close(&fd);
     49 
     50     int rc = HANDLE_EINTR(flock(fd, LOCK_EX|LOCK_NB));
     51 
     52     // Got the lock, so it wasn't already locked.  Close releases.
     53     if (rc != -1)
     54       return false;
     55 
     56     // Someone else has the lock.
     57     if (errno == EWOULDBLOCK)
     58       return true;
     59 
     60     EXPECT_EQ(EWOULDBLOCK, errno) << "Unexpected error acquiring lock.";
     61     return false;
     62   }
     63 
     64   base::ScopedTempDir temp_dir_;
     65   base::FilePath lock_path_;
     66 };
     67 
     68 // Test that the base case doesn't blow up.
     69 TEST_F(ProcessSingletonMacTest, Basic) {
     70   ProcessSingleton ps(temp_dir_.path(),
     71                       ProcessSingleton::NotificationCallback());
     72   EXPECT_FALSE(IsLocked());
     73   EXPECT_TRUE(ps.Create());
     74   EXPECT_TRUE(IsLocked());
     75   ps.Cleanup();
     76   EXPECT_FALSE(IsLocked());
     77 }
     78 
     79 // The destructor should release the lock.
     80 TEST_F(ProcessSingletonMacTest, DestructorReleases) {
     81   EXPECT_FALSE(IsLocked());
     82   {
     83     ProcessSingleton ps(temp_dir_.path(),
     84                         ProcessSingleton::NotificationCallback());
     85     EXPECT_TRUE(ps.Create());
     86     EXPECT_TRUE(IsLocked());
     87   }
     88   EXPECT_FALSE(IsLocked());
     89 }
     90 
     91 // Multiple singletons should interlock appropriately.
     92 TEST_F(ProcessSingletonMacTest, Interlock) {
     93   ProcessSingleton ps1(temp_dir_.path(),
     94                        ProcessSingleton::NotificationCallback());
     95   ProcessSingleton ps2(temp_dir_.path(),
     96                        ProcessSingleton::NotificationCallback());
     97 
     98   // Windows and Linux use a command-line flag to suppress this, but
     99   // it is on a sub-process so the scope is contained.  Rather than
    100   // add additional API to process_singleton.h in an #ifdef, just tell
    101   // the reader what to expect and move on.
    102   LOG(ERROR) << "Expect two failures to obtain the lock.";
    103 
    104   // When |ps1| has the lock, |ps2| cannot get it.
    105   EXPECT_FALSE(IsLocked());
    106   EXPECT_TRUE(ps1.Create());
    107   EXPECT_TRUE(IsLocked());
    108   EXPECT_FALSE(ps2.Create());
    109   ps1.Cleanup();
    110 
    111   // And when |ps2| has the lock, |ps1| cannot get it.
    112   EXPECT_FALSE(IsLocked());
    113   EXPECT_TRUE(ps2.Create());
    114   EXPECT_TRUE(IsLocked());
    115   EXPECT_FALSE(ps1.Create());
    116   ps2.Cleanup();
    117   EXPECT_FALSE(IsLocked());
    118 }
    119 
    120 // Like |Interlock| test, but via |NotifyOtherProcessOrCreate()|.
    121 TEST_F(ProcessSingletonMacTest, NotifyOtherProcessOrCreate) {
    122   ProcessSingleton ps1(temp_dir_.path(),
    123                        ProcessSingleton::NotificationCallback());
    124   ProcessSingleton ps2(temp_dir_.path(),
    125                        ProcessSingleton::NotificationCallback());
    126 
    127   // Windows and Linux use a command-line flag to suppress this, but
    128   // it is on a sub-process so the scope is contained.  Rather than
    129   // add additional API to process_singleton.h in an #ifdef, just tell
    130   // the reader what to expect and move on.
    131   LOG(ERROR) << "Expect two failures to obtain the lock.";
    132 
    133   // When |ps1| has the lock, |ps2| cannot get it.
    134   EXPECT_FALSE(IsLocked());
    135   EXPECT_EQ(
    136       ProcessSingleton::PROCESS_NONE,
    137       ps1.NotifyOtherProcessOrCreate());
    138   EXPECT_TRUE(IsLocked());
    139   EXPECT_EQ(
    140       ProcessSingleton::PROFILE_IN_USE,
    141       ps2.NotifyOtherProcessOrCreate());
    142   ps1.Cleanup();
    143 
    144   // And when |ps2| has the lock, |ps1| cannot get it.
    145   EXPECT_FALSE(IsLocked());
    146   EXPECT_EQ(
    147       ProcessSingleton::PROCESS_NONE,
    148       ps2.NotifyOtherProcessOrCreate());
    149   EXPECT_TRUE(IsLocked());
    150   EXPECT_EQ(
    151       ProcessSingleton::PROFILE_IN_USE,
    152       ps1.NotifyOtherProcessOrCreate());
    153   ps2.Cleanup();
    154   EXPECT_FALSE(IsLocked());
    155 }
    156 
    157 // TODO(shess): Test that the lock is released when the process dies.
    158 // DEATH_TEST?  I don't know.  If the code to communicate between
    159 // browser processes is ever written, this all would need to be tested
    160 // more like the other platforms, in which case it would be easy.
    161 
    162 }  // namespace
    163