xref: /aosp_15_r20/external/libchrome/base/files/file_locking_unittest.cc (revision 635a864187cb8b6c713ff48b7e790a6b21769273)
1*635a8641SAndroid Build Coastguard Worker // Copyright 2015 The Chromium Authors. All rights reserved.
2*635a8641SAndroid Build Coastguard Worker // Use of this source code is governed by a BSD-style license that can be
3*635a8641SAndroid Build Coastguard Worker // found in the LICENSE file.
4*635a8641SAndroid Build Coastguard Worker 
5*635a8641SAndroid Build Coastguard Worker #include "base/command_line.h"
6*635a8641SAndroid Build Coastguard Worker #include "base/files/file.h"
7*635a8641SAndroid Build Coastguard Worker #include "base/files/file_util.h"
8*635a8641SAndroid Build Coastguard Worker #include "base/files/scoped_temp_dir.h"
9*635a8641SAndroid Build Coastguard Worker #include "base/macros.h"
10*635a8641SAndroid Build Coastguard Worker #include "base/test/multiprocess_test.h"
11*635a8641SAndroid Build Coastguard Worker #include "base/test/test_timeouts.h"
12*635a8641SAndroid Build Coastguard Worker #include "base/threading/platform_thread.h"
13*635a8641SAndroid Build Coastguard Worker #include "base/time/time.h"
14*635a8641SAndroid Build Coastguard Worker #include "build/build_config.h"
15*635a8641SAndroid Build Coastguard Worker #include "testing/gtest/include/gtest/gtest.h"
16*635a8641SAndroid Build Coastguard Worker #include "testing/multiprocess_func_list.h"
17*635a8641SAndroid Build Coastguard Worker 
18*635a8641SAndroid Build Coastguard Worker using base::File;
19*635a8641SAndroid Build Coastguard Worker using base::FilePath;
20*635a8641SAndroid Build Coastguard Worker 
21*635a8641SAndroid Build Coastguard Worker namespace {
22*635a8641SAndroid Build Coastguard Worker 
23*635a8641SAndroid Build Coastguard Worker // Flag for the parent to share a temp dir to the child.
24*635a8641SAndroid Build Coastguard Worker const char kTempDirFlag[] = "temp-dir";
25*635a8641SAndroid Build Coastguard Worker 
26*635a8641SAndroid Build Coastguard Worker // Flags to control how the subprocess unlocks the file.
27*635a8641SAndroid Build Coastguard Worker const char kFileUnlock[] = "file-unlock";
28*635a8641SAndroid Build Coastguard Worker const char kCloseUnlock[] = "close-unlock";
29*635a8641SAndroid Build Coastguard Worker const char kExitUnlock[] = "exit-unlock";
30*635a8641SAndroid Build Coastguard Worker 
31*635a8641SAndroid Build Coastguard Worker // File to lock in temp dir.
32*635a8641SAndroid Build Coastguard Worker const char kLockFile[] = "lockfile";
33*635a8641SAndroid Build Coastguard Worker 
34*635a8641SAndroid Build Coastguard Worker // Constants for various requests and responses, used as |signal_file| parameter
35*635a8641SAndroid Build Coastguard Worker // to signal/wait helpers.
36*635a8641SAndroid Build Coastguard Worker const char kSignalLockFileLocked[] = "locked.signal";
37*635a8641SAndroid Build Coastguard Worker const char kSignalLockFileClose[] = "close.signal";
38*635a8641SAndroid Build Coastguard Worker const char kSignalLockFileClosed[] = "closed.signal";
39*635a8641SAndroid Build Coastguard Worker const char kSignalLockFileUnlock[] = "unlock.signal";
40*635a8641SAndroid Build Coastguard Worker const char kSignalLockFileUnlocked[] = "unlocked.signal";
41*635a8641SAndroid Build Coastguard Worker const char kSignalExit[] = "exit.signal";
42*635a8641SAndroid Build Coastguard Worker 
43*635a8641SAndroid Build Coastguard Worker // Signal an event by creating a file which didn't previously exist.
SignalEvent(const FilePath & signal_dir,const char * signal_file)44*635a8641SAndroid Build Coastguard Worker bool SignalEvent(const FilePath& signal_dir, const char* signal_file) {
45*635a8641SAndroid Build Coastguard Worker   File file(signal_dir.AppendASCII(signal_file),
46*635a8641SAndroid Build Coastguard Worker             File::FLAG_CREATE | File::FLAG_WRITE);
47*635a8641SAndroid Build Coastguard Worker   return file.IsValid();
48*635a8641SAndroid Build Coastguard Worker }
49*635a8641SAndroid Build Coastguard Worker 
50*635a8641SAndroid Build Coastguard Worker // Check whether an event was signaled.
CheckEvent(const FilePath & signal_dir,const char * signal_file)51*635a8641SAndroid Build Coastguard Worker bool CheckEvent(const FilePath& signal_dir, const char* signal_file) {
52*635a8641SAndroid Build Coastguard Worker   File file(signal_dir.AppendASCII(signal_file),
53*635a8641SAndroid Build Coastguard Worker             File::FLAG_OPEN | File::FLAG_READ);
54*635a8641SAndroid Build Coastguard Worker   return file.IsValid();
55*635a8641SAndroid Build Coastguard Worker }
56*635a8641SAndroid Build Coastguard Worker 
57*635a8641SAndroid Build Coastguard Worker // Busy-wait for an event to be signaled, returning false for timeout.
WaitForEventWithTimeout(const FilePath & signal_dir,const char * signal_file,const base::TimeDelta & timeout)58*635a8641SAndroid Build Coastguard Worker bool WaitForEventWithTimeout(const FilePath& signal_dir,
59*635a8641SAndroid Build Coastguard Worker                              const char* signal_file,
60*635a8641SAndroid Build Coastguard Worker                              const base::TimeDelta& timeout) {
61*635a8641SAndroid Build Coastguard Worker   const base::Time finish_by = base::Time::Now() + timeout;
62*635a8641SAndroid Build Coastguard Worker   while (!CheckEvent(signal_dir, signal_file)) {
63*635a8641SAndroid Build Coastguard Worker     if (base::Time::Now() > finish_by)
64*635a8641SAndroid Build Coastguard Worker       return false;
65*635a8641SAndroid Build Coastguard Worker     base::PlatformThread::Sleep(base::TimeDelta::FromMilliseconds(10));
66*635a8641SAndroid Build Coastguard Worker   }
67*635a8641SAndroid Build Coastguard Worker   return true;
68*635a8641SAndroid Build Coastguard Worker }
69*635a8641SAndroid Build Coastguard Worker 
70*635a8641SAndroid Build Coastguard Worker // Wait forever for the event to be signaled (should never return false).
WaitForEvent(const FilePath & signal_dir,const char * signal_file)71*635a8641SAndroid Build Coastguard Worker bool WaitForEvent(const FilePath& signal_dir, const char* signal_file) {
72*635a8641SAndroid Build Coastguard Worker   return WaitForEventWithTimeout(signal_dir, signal_file,
73*635a8641SAndroid Build Coastguard Worker                                  base::TimeDelta::Max());
74*635a8641SAndroid Build Coastguard Worker }
75*635a8641SAndroid Build Coastguard Worker 
76*635a8641SAndroid Build Coastguard Worker // Keep these in sync so StartChild*() can refer to correct test main.
77*635a8641SAndroid Build Coastguard Worker #define ChildMain ChildLockUnlock
78*635a8641SAndroid Build Coastguard Worker #define ChildMainString "ChildLockUnlock"
79*635a8641SAndroid Build Coastguard Worker 
80*635a8641SAndroid Build Coastguard Worker // Subprocess to test getting a file lock then releasing it.  |kTempDirFlag|
81*635a8641SAndroid Build Coastguard Worker // must pass in an existing temporary directory for the lockfile and signal
82*635a8641SAndroid Build Coastguard Worker // files.  One of the following flags must be passed to determine how to unlock
83*635a8641SAndroid Build Coastguard Worker // the lock file:
84*635a8641SAndroid Build Coastguard Worker // - |kFileUnlock| calls Unlock() to unlock.
85*635a8641SAndroid Build Coastguard Worker // - |kCloseUnlock| calls Close() while the lock is held.
86*635a8641SAndroid Build Coastguard Worker // - |kExitUnlock| exits while the lock is held.
MULTIPROCESS_TEST_MAIN(ChildMain)87*635a8641SAndroid Build Coastguard Worker MULTIPROCESS_TEST_MAIN(ChildMain) {
88*635a8641SAndroid Build Coastguard Worker   base::CommandLine* command_line = base::CommandLine::ForCurrentProcess();
89*635a8641SAndroid Build Coastguard Worker   const FilePath temp_path = command_line->GetSwitchValuePath(kTempDirFlag);
90*635a8641SAndroid Build Coastguard Worker   CHECK(base::DirectoryExists(temp_path));
91*635a8641SAndroid Build Coastguard Worker 
92*635a8641SAndroid Build Coastguard Worker   // Immediately lock the file.
93*635a8641SAndroid Build Coastguard Worker   File file(temp_path.AppendASCII(kLockFile),
94*635a8641SAndroid Build Coastguard Worker             File::FLAG_OPEN | File::FLAG_READ | File::FLAG_WRITE);
95*635a8641SAndroid Build Coastguard Worker   CHECK(file.IsValid());
96*635a8641SAndroid Build Coastguard Worker   CHECK_EQ(File::FILE_OK, file.Lock());
97*635a8641SAndroid Build Coastguard Worker   CHECK(SignalEvent(temp_path, kSignalLockFileLocked));
98*635a8641SAndroid Build Coastguard Worker 
99*635a8641SAndroid Build Coastguard Worker   if (command_line->HasSwitch(kFileUnlock)) {
100*635a8641SAndroid Build Coastguard Worker     // Wait for signal to unlock, then unlock the file.
101*635a8641SAndroid Build Coastguard Worker     CHECK(WaitForEvent(temp_path, kSignalLockFileUnlock));
102*635a8641SAndroid Build Coastguard Worker     CHECK_EQ(File::FILE_OK, file.Unlock());
103*635a8641SAndroid Build Coastguard Worker     CHECK(SignalEvent(temp_path, kSignalLockFileUnlocked));
104*635a8641SAndroid Build Coastguard Worker   } else if (command_line->HasSwitch(kCloseUnlock)) {
105*635a8641SAndroid Build Coastguard Worker     // Wait for the signal to close, then close the file.
106*635a8641SAndroid Build Coastguard Worker     CHECK(WaitForEvent(temp_path, kSignalLockFileClose));
107*635a8641SAndroid Build Coastguard Worker     file.Close();
108*635a8641SAndroid Build Coastguard Worker     CHECK(!file.IsValid());
109*635a8641SAndroid Build Coastguard Worker     CHECK(SignalEvent(temp_path, kSignalLockFileClosed));
110*635a8641SAndroid Build Coastguard Worker   } else {
111*635a8641SAndroid Build Coastguard Worker     CHECK(command_line->HasSwitch(kExitUnlock));
112*635a8641SAndroid Build Coastguard Worker   }
113*635a8641SAndroid Build Coastguard Worker 
114*635a8641SAndroid Build Coastguard Worker   // Wait for signal to exit, so that unlock or close can be distinguished from
115*635a8641SAndroid Build Coastguard Worker   // exit.
116*635a8641SAndroid Build Coastguard Worker   CHECK(WaitForEvent(temp_path, kSignalExit));
117*635a8641SAndroid Build Coastguard Worker   return 0;
118*635a8641SAndroid Build Coastguard Worker }
119*635a8641SAndroid Build Coastguard Worker 
120*635a8641SAndroid Build Coastguard Worker }  // namespace
121*635a8641SAndroid Build Coastguard Worker 
122*635a8641SAndroid Build Coastguard Worker class FileLockingTest : public testing::Test {
123*635a8641SAndroid Build Coastguard Worker  public:
124*635a8641SAndroid Build Coastguard Worker   FileLockingTest() = default;
125*635a8641SAndroid Build Coastguard Worker 
126*635a8641SAndroid Build Coastguard Worker  protected:
SetUp()127*635a8641SAndroid Build Coastguard Worker   void SetUp() override {
128*635a8641SAndroid Build Coastguard Worker     testing::Test::SetUp();
129*635a8641SAndroid Build Coastguard Worker 
130*635a8641SAndroid Build Coastguard Worker     // Setup the temp dir and the lock file.
131*635a8641SAndroid Build Coastguard Worker     ASSERT_TRUE(temp_dir_.CreateUniqueTempDir());
132*635a8641SAndroid Build Coastguard Worker     lock_file_.Initialize(
133*635a8641SAndroid Build Coastguard Worker         temp_dir_.GetPath().AppendASCII(kLockFile),
134*635a8641SAndroid Build Coastguard Worker         File::FLAG_CREATE | File::FLAG_READ | File::FLAG_WRITE);
135*635a8641SAndroid Build Coastguard Worker     ASSERT_TRUE(lock_file_.IsValid());
136*635a8641SAndroid Build Coastguard Worker   }
137*635a8641SAndroid Build Coastguard Worker 
SignalEvent(const char * signal_file)138*635a8641SAndroid Build Coastguard Worker   bool SignalEvent(const char* signal_file) {
139*635a8641SAndroid Build Coastguard Worker     return ::SignalEvent(temp_dir_.GetPath(), signal_file);
140*635a8641SAndroid Build Coastguard Worker   }
141*635a8641SAndroid Build Coastguard Worker 
WaitForEventOrTimeout(const char * signal_file)142*635a8641SAndroid Build Coastguard Worker   bool WaitForEventOrTimeout(const char* signal_file) {
143*635a8641SAndroid Build Coastguard Worker     return ::WaitForEventWithTimeout(temp_dir_.GetPath(), signal_file,
144*635a8641SAndroid Build Coastguard Worker                                      TestTimeouts::action_timeout());
145*635a8641SAndroid Build Coastguard Worker   }
146*635a8641SAndroid Build Coastguard Worker 
147*635a8641SAndroid Build Coastguard Worker   // Start a child process set to use the specified unlock action, and wait for
148*635a8641SAndroid Build Coastguard Worker   // it to lock the file.
StartChildAndSignalLock(const char * unlock_action)149*635a8641SAndroid Build Coastguard Worker   void StartChildAndSignalLock(const char* unlock_action) {
150*635a8641SAndroid Build Coastguard Worker     // Create a temporary dir and spin up a ChildLockExit subprocess against it.
151*635a8641SAndroid Build Coastguard Worker     const FilePath temp_path = temp_dir_.GetPath();
152*635a8641SAndroid Build Coastguard Worker     base::CommandLine child_command_line(
153*635a8641SAndroid Build Coastguard Worker         base::GetMultiProcessTestChildBaseCommandLine());
154*635a8641SAndroid Build Coastguard Worker     child_command_line.AppendSwitchPath(kTempDirFlag, temp_path);
155*635a8641SAndroid Build Coastguard Worker     child_command_line.AppendSwitch(unlock_action);
156*635a8641SAndroid Build Coastguard Worker     lock_child_ = base::SpawnMultiProcessTestChild(
157*635a8641SAndroid Build Coastguard Worker         ChildMainString, child_command_line, base::LaunchOptions());
158*635a8641SAndroid Build Coastguard Worker     ASSERT_TRUE(lock_child_.IsValid());
159*635a8641SAndroid Build Coastguard Worker 
160*635a8641SAndroid Build Coastguard Worker     // Wait for the child to lock the file.
161*635a8641SAndroid Build Coastguard Worker     ASSERT_TRUE(WaitForEventOrTimeout(kSignalLockFileLocked));
162*635a8641SAndroid Build Coastguard Worker   }
163*635a8641SAndroid Build Coastguard Worker 
164*635a8641SAndroid Build Coastguard Worker   // Signal the child to exit cleanly.
ExitChildCleanly()165*635a8641SAndroid Build Coastguard Worker   void ExitChildCleanly() {
166*635a8641SAndroid Build Coastguard Worker     ASSERT_TRUE(SignalEvent(kSignalExit));
167*635a8641SAndroid Build Coastguard Worker     int rv = -1;
168*635a8641SAndroid Build Coastguard Worker     ASSERT_TRUE(WaitForMultiprocessTestChildExit(
169*635a8641SAndroid Build Coastguard Worker         lock_child_, TestTimeouts::action_timeout(), &rv));
170*635a8641SAndroid Build Coastguard Worker     ASSERT_EQ(0, rv);
171*635a8641SAndroid Build Coastguard Worker   }
172*635a8641SAndroid Build Coastguard Worker 
173*635a8641SAndroid Build Coastguard Worker   base::ScopedTempDir temp_dir_;
174*635a8641SAndroid Build Coastguard Worker   base::File lock_file_;
175*635a8641SAndroid Build Coastguard Worker   base::Process lock_child_;
176*635a8641SAndroid Build Coastguard Worker 
177*635a8641SAndroid Build Coastguard Worker  private:
178*635a8641SAndroid Build Coastguard Worker   DISALLOW_COPY_AND_ASSIGN(FileLockingTest);
179*635a8641SAndroid Build Coastguard Worker };
180*635a8641SAndroid Build Coastguard Worker 
181*635a8641SAndroid Build Coastguard Worker // Test that locks are released by Unlock().
TEST_F(FileLockingTest,LockAndUnlock)182*635a8641SAndroid Build Coastguard Worker TEST_F(FileLockingTest, LockAndUnlock) {
183*635a8641SAndroid Build Coastguard Worker   StartChildAndSignalLock(kFileUnlock);
184*635a8641SAndroid Build Coastguard Worker 
185*635a8641SAndroid Build Coastguard Worker   ASSERT_NE(File::FILE_OK, lock_file_.Lock());
186*635a8641SAndroid Build Coastguard Worker   ASSERT_TRUE(SignalEvent(kSignalLockFileUnlock));
187*635a8641SAndroid Build Coastguard Worker   ASSERT_TRUE(WaitForEventOrTimeout(kSignalLockFileUnlocked));
188*635a8641SAndroid Build Coastguard Worker   ASSERT_EQ(File::FILE_OK, lock_file_.Lock());
189*635a8641SAndroid Build Coastguard Worker   ASSERT_EQ(File::FILE_OK, lock_file_.Unlock());
190*635a8641SAndroid Build Coastguard Worker 
191*635a8641SAndroid Build Coastguard Worker   ExitChildCleanly();
192*635a8641SAndroid Build Coastguard Worker }
193*635a8641SAndroid Build Coastguard Worker 
194*635a8641SAndroid Build Coastguard Worker // Test that locks are released on Close().
TEST_F(FileLockingTest,UnlockOnClose)195*635a8641SAndroid Build Coastguard Worker TEST_F(FileLockingTest, UnlockOnClose) {
196*635a8641SAndroid Build Coastguard Worker   StartChildAndSignalLock(kCloseUnlock);
197*635a8641SAndroid Build Coastguard Worker 
198*635a8641SAndroid Build Coastguard Worker   ASSERT_NE(File::FILE_OK, lock_file_.Lock());
199*635a8641SAndroid Build Coastguard Worker   ASSERT_TRUE(SignalEvent(kSignalLockFileClose));
200*635a8641SAndroid Build Coastguard Worker   ASSERT_TRUE(WaitForEventOrTimeout(kSignalLockFileClosed));
201*635a8641SAndroid Build Coastguard Worker   ASSERT_EQ(File::FILE_OK, lock_file_.Lock());
202*635a8641SAndroid Build Coastguard Worker   ASSERT_EQ(File::FILE_OK, lock_file_.Unlock());
203*635a8641SAndroid Build Coastguard Worker 
204*635a8641SAndroid Build Coastguard Worker   ExitChildCleanly();
205*635a8641SAndroid Build Coastguard Worker }
206*635a8641SAndroid Build Coastguard Worker 
207*635a8641SAndroid Build Coastguard Worker // Test that locks are released on exit.
TEST_F(FileLockingTest,UnlockOnExit)208*635a8641SAndroid Build Coastguard Worker TEST_F(FileLockingTest, UnlockOnExit) {
209*635a8641SAndroid Build Coastguard Worker   StartChildAndSignalLock(kExitUnlock);
210*635a8641SAndroid Build Coastguard Worker 
211*635a8641SAndroid Build Coastguard Worker   ASSERT_NE(File::FILE_OK, lock_file_.Lock());
212*635a8641SAndroid Build Coastguard Worker   ExitChildCleanly();
213*635a8641SAndroid Build Coastguard Worker   ASSERT_EQ(File::FILE_OK, lock_file_.Lock());
214*635a8641SAndroid Build Coastguard Worker   ASSERT_EQ(File::FILE_OK, lock_file_.Unlock());
215*635a8641SAndroid Build Coastguard Worker }
216*635a8641SAndroid Build Coastguard Worker 
217*635a8641SAndroid Build Coastguard Worker // Test that killing the process releases the lock.  This should cover crashing.
218*635a8641SAndroid Build Coastguard Worker // Flaky on Android (http://crbug.com/747518)
219*635a8641SAndroid Build Coastguard Worker #if defined(OS_ANDROID)
220*635a8641SAndroid Build Coastguard Worker #define MAYBE_UnlockOnTerminate DISABLED_UnlockOnTerminate
221*635a8641SAndroid Build Coastguard Worker #else
222*635a8641SAndroid Build Coastguard Worker #define MAYBE_UnlockOnTerminate UnlockOnTerminate
223*635a8641SAndroid Build Coastguard Worker #endif
TEST_F(FileLockingTest,MAYBE_UnlockOnTerminate)224*635a8641SAndroid Build Coastguard Worker TEST_F(FileLockingTest, MAYBE_UnlockOnTerminate) {
225*635a8641SAndroid Build Coastguard Worker   // The child will wait for an exit which never arrives.
226*635a8641SAndroid Build Coastguard Worker   StartChildAndSignalLock(kExitUnlock);
227*635a8641SAndroid Build Coastguard Worker 
228*635a8641SAndroid Build Coastguard Worker   ASSERT_NE(File::FILE_OK, lock_file_.Lock());
229*635a8641SAndroid Build Coastguard Worker   ASSERT_TRUE(TerminateMultiProcessTestChild(lock_child_, 0, true));
230*635a8641SAndroid Build Coastguard Worker   ASSERT_EQ(File::FILE_OK, lock_file_.Lock());
231*635a8641SAndroid Build Coastguard Worker   ASSERT_EQ(File::FILE_OK, lock_file_.Unlock());
232*635a8641SAndroid Build Coastguard Worker }
233