Home | History | Annotate | Download | only in browser
      1 // Copyright (c) 2010 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/eintr_wrapper.h"
     12 #include "base/file_util.h"
     13 #include "base/path_service.h"
     14 #include "chrome/common/chrome_constants.h"
     15 #include "chrome/common/chrome_paths.h"
     16 #include "chrome/test/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   ScopedTempDir temp_dir_;
     65   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   EXPECT_FALSE(IsLocked());
     72   EXPECT_TRUE(ps.Create());
     73   EXPECT_TRUE(IsLocked());
     74   ps.Cleanup();
     75   EXPECT_FALSE(IsLocked());
     76 }
     77 
     78 // The destructor should release the lock.
     79 TEST_F(ProcessSingletonMacTest, DestructorReleases) {
     80   EXPECT_FALSE(IsLocked());
     81   {
     82     ProcessSingleton ps(temp_dir_.path());
     83     EXPECT_TRUE(ps.Create());
     84     EXPECT_TRUE(IsLocked());
     85   }
     86   EXPECT_FALSE(IsLocked());
     87 }
     88 
     89 // Multiple singletons should interlock appropriately.
     90 TEST_F(ProcessSingletonMacTest, Interlock) {
     91   ProcessSingleton ps1(temp_dir_.path());
     92   ProcessSingleton ps2(temp_dir_.path());
     93 
     94   // Windows and Linux use a command-line flag to suppress this, but
     95   // it is on a sub-process so the scope is contained.  Rather than
     96   // add additional API to process_singleton.h in an #ifdef, just tell
     97   // the reader what to expect and move on.
     98   LOG(ERROR) << "Expect two failures to obtain the lock.";
     99 
    100   // When |ps1| has the lock, |ps2| cannot get it.
    101   EXPECT_FALSE(IsLocked());
    102   EXPECT_TRUE(ps1.Create());
    103   EXPECT_TRUE(IsLocked());
    104   EXPECT_FALSE(ps2.Create());
    105   ps1.Cleanup();
    106 
    107   // And when |ps2| has the lock, |ps1| cannot get it.
    108   EXPECT_FALSE(IsLocked());
    109   EXPECT_TRUE(ps2.Create());
    110   EXPECT_TRUE(IsLocked());
    111   EXPECT_FALSE(ps1.Create());
    112   ps2.Cleanup();
    113   EXPECT_FALSE(IsLocked());
    114 }
    115 
    116 // Like |Interlock| test, but via |NotifyOtherProcessOrCreate()|.
    117 TEST_F(ProcessSingletonMacTest, NotifyOtherProcessOrCreate) {
    118   ProcessSingleton ps1(temp_dir_.path());
    119   ProcessSingleton ps2(temp_dir_.path());
    120 
    121   // Windows and Linux use a command-line flag to suppress this, but
    122   // it is on a sub-process so the scope is contained.  Rather than
    123   // add additional API to process_singleton.h in an #ifdef, just tell
    124   // the reader what to expect and move on.
    125   LOG(ERROR) << "Expect two failures to obtain the lock.";
    126 
    127   // When |ps1| has the lock, |ps2| cannot get it.
    128   EXPECT_FALSE(IsLocked());
    129   EXPECT_EQ(ProcessSingleton::PROCESS_NONE, ps1.NotifyOtherProcessOrCreate());
    130   EXPECT_TRUE(IsLocked());
    131   EXPECT_EQ(ProcessSingleton::PROFILE_IN_USE, ps2.NotifyOtherProcessOrCreate());
    132   ps1.Cleanup();
    133 
    134   // And when |ps2| has the lock, |ps1| cannot get it.
    135   EXPECT_FALSE(IsLocked());
    136   EXPECT_EQ(ProcessSingleton::PROCESS_NONE, ps2.NotifyOtherProcessOrCreate());
    137   EXPECT_TRUE(IsLocked());
    138   EXPECT_EQ(ProcessSingleton::PROFILE_IN_USE, ps1.NotifyOtherProcessOrCreate());
    139   ps2.Cleanup();
    140   EXPECT_FALSE(IsLocked());
    141 }
    142 
    143 // TODO(shess): Test that the lock is released when the process dies.
    144 // DEATH_TEST?  I don't know.  If the code to communicate between
    145 // browser processes is ever written, this all would need to be tested
    146 // more like the other platforms, in which case it would be easy.
    147 
    148 }  // namespace
    149