• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2016 The Chromium OS 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 <fcntl.h>
6 #include <gtest/gtest.h>
7 #include <stdlib.h>
8 
9 #include <string>
10 
11 #include "cras_file_wait.h"
12 #include "cras_util.h"
13 
14 extern "C" {
15 // This function is not exported in cras_util.h.
16 void cras_file_wait_mock_race_condition(struct cras_file_wait* file_wait);
17 }
18 
19 namespace {
20 
21 // Executes "rm -rf <path>".
RmRF(const std::string & path)22 static int RmRF(const std::string& path) {
23   std::string cmd("rm -rf \"");
24   cmd += path + "\"";
25 
26   if (path == "/")
27     return -EINVAL;
28 
29   int rc = system(cmd.c_str());
30   if (rc < 0)
31     return -errno;
32   return WEXITSTATUS(rc);
33 }
34 
35 // Filled-in by the FileWaitCallback.
36 struct FileWaitResult {
37   size_t called;
38   cras_file_wait_event_t event;
39 };
40 
41 // Called by the file wait code for an event.
FileWaitCallback(void * context,cras_file_wait_event_t event,const char * filename)42 static void FileWaitCallback(void* context,
43                              cras_file_wait_event_t event,
44                              const char* filename) {
45   FileWaitResult* result = reinterpret_cast<FileWaitResult*>(context);
46   result->called++;
47   result->event = event;
48 }
49 
50 // Do all of the EXPECTed steps for a simple wait for one file.
SimpleFileWait(const char * file_path)51 static void SimpleFileWait(const char* file_path) {
52   struct cras_file_wait* file_wait;
53   FileWaitResult file_wait_result;
54   struct pollfd poll_fd;
55   struct timespec timeout = {0, 100000000};
56   struct stat stat_buf;
57   int stat_rc;
58 
59   stat_rc = stat(file_path, &stat_buf);
60   if (stat_rc < 0)
61     stat_rc = -errno;
62 
63   file_wait_result.called = 0;
64   EXPECT_EQ(0, cras_file_wait_create(file_path, CRAS_FILE_WAIT_FLAG_NONE,
65                                      FileWaitCallback, &file_wait_result,
66                                      &file_wait));
67   EXPECT_NE(reinterpret_cast<struct cras_file_wait*>(NULL), file_wait);
68   if (stat_rc == 0) {
69     EXPECT_EQ(1, file_wait_result.called);
70     EXPECT_EQ(CRAS_FILE_WAIT_EVENT_CREATED, file_wait_result.event);
71   } else {
72     EXPECT_EQ(0, file_wait_result.called);
73   }
74   poll_fd.events = POLLIN;
75   poll_fd.fd = cras_file_wait_get_fd(file_wait);
76 
77   file_wait_result.called = 0;
78   if (stat_rc == 0)
79     EXPECT_EQ(0, RmRF(file_path));
80   else
81     EXPECT_EQ(0, mknod(file_path, S_IFREG | 0600, 0));
82   EXPECT_EQ(1, cras_poll(&poll_fd, 1, &timeout, NULL));
83   EXPECT_EQ(0, cras_file_wait_dispatch(file_wait));
84   EXPECT_EQ(1, file_wait_result.called);
85   if (stat_rc == 0)
86     EXPECT_EQ(CRAS_FILE_WAIT_EVENT_DELETED, file_wait_result.event);
87   else
88     EXPECT_EQ(CRAS_FILE_WAIT_EVENT_CREATED, file_wait_result.event);
89   EXPECT_EQ(-EAGAIN, cras_file_wait_dispatch(file_wait));
90   cras_file_wait_destroy(file_wait);
91 }
92 
93 // Test the cras_file_wait functions including multiple path components
94 // missing and path components deleted and recreated.
TEST(Util,FileWait)95 TEST(Util, FileWait) {
96   struct cras_file_wait* file_wait;
97   FileWaitResult file_wait_result;
98   pid_t pid = getpid();
99   struct pollfd poll_fd;
100   int current_dir;
101   struct timespec timeout = {0, 100000000};
102   char pid_buf[32];
103   std::string tmp_dir(CRAS_UT_TMPDIR);
104   std::string dir_path;
105   std::string subdir_path;
106   std::string file_path;
107 
108   snprintf(pid_buf, sizeof(pid_buf), "%d", pid);
109   dir_path = tmp_dir + "/" + pid_buf;
110   subdir_path = dir_path + "/subdir";
111   file_path = subdir_path + "/does_not_exist";
112 
113   // Test arguments.
114   // Null file path.
115   EXPECT_EQ(-EINVAL, cras_file_wait_create(NULL, CRAS_FILE_WAIT_FLAG_NONE,
116                                            FileWaitCallback, &file_wait_result,
117                                            &file_wait));
118   // Empty file path.
119   EXPECT_EQ(-EINVAL, cras_file_wait_create("", CRAS_FILE_WAIT_FLAG_NONE,
120                                            FileWaitCallback, &file_wait_result,
121                                            &file_wait));
122   // No callback structure.
123   EXPECT_EQ(-EINVAL, cras_file_wait_create(".", CRAS_FILE_WAIT_FLAG_NONE, NULL,
124                                            NULL, &file_wait));
125   // No file wait structure.
126   EXPECT_EQ(-EINVAL,
127             cras_file_wait_create(".", CRAS_FILE_WAIT_FLAG_NONE,
128                                   FileWaitCallback, &file_wait_result, NULL));
129   EXPECT_EQ(-EINVAL, cras_file_wait_dispatch(NULL));
130   EXPECT_EQ(-EINVAL, cras_file_wait_get_fd(NULL));
131 
132   // Make sure that /tmp exists.
133   file_wait_result.called = 0;
134   EXPECT_EQ(0, cras_file_wait_create(CRAS_UT_TMPDIR, CRAS_FILE_WAIT_FLAG_NONE,
135                                      FileWaitCallback, &file_wait_result,
136                                      &file_wait));
137   EXPECT_NE(reinterpret_cast<struct cras_file_wait*>(NULL), file_wait);
138   EXPECT_EQ(file_wait_result.called, 1);
139   ASSERT_EQ(file_wait_result.event, CRAS_FILE_WAIT_EVENT_CREATED);
140   cras_file_wait_destroy(file_wait);
141 
142   // Create our temporary dir.
143   ASSERT_EQ(0, RmRF(dir_path));
144   ASSERT_EQ(0, mkdir(dir_path.c_str(), 0700));
145 
146   // Start looking for our file '.../does_not_exist'.
147   EXPECT_EQ(0, cras_file_wait_create(file_path.c_str(),
148                                      CRAS_FILE_WAIT_FLAG_NONE, FileWaitCallback,
149                                      &file_wait_result, &file_wait));
150   EXPECT_NE(reinterpret_cast<struct cras_file_wait*>(NULL), file_wait);
151   poll_fd.events = POLLIN;
152   poll_fd.fd = cras_file_wait_get_fd(file_wait);
153   EXPECT_NE(0, poll_fd.fd >= 0);
154 
155   // Create a sub-directory in the path.
156   file_wait_result.called = 0;
157   EXPECT_EQ(0, mkdir(subdir_path.c_str(), 0700));
158   EXPECT_EQ(1, cras_poll(&poll_fd, 1, &timeout, NULL));
159   EXPECT_EQ(0, cras_file_wait_dispatch(file_wait));
160   EXPECT_EQ(0, file_wait_result.called);
161   // Removing a watch causes generation of an IN_IGNORED event for the previous
162   // watch_id. cras_file_wait_dispatch will ignore this and return 0.
163   EXPECT_EQ(0, cras_file_wait_dispatch(file_wait));
164   EXPECT_EQ(-EAGAIN, cras_file_wait_dispatch(file_wait));
165 
166   // Remove the directory that we're watching.
167   EXPECT_EQ(0, RmRF(subdir_path));
168   timeout.tv_sec = 0;
169   timeout.tv_nsec = 100000000;
170   EXPECT_EQ(1, cras_poll(&poll_fd, 1, &timeout, NULL));
171   EXPECT_EQ(0, cras_file_wait_dispatch(file_wait));
172   EXPECT_EQ(0, file_wait_result.called);
173   EXPECT_EQ(-EAGAIN, cras_file_wait_dispatch(file_wait));
174 
175   // Create a sub-directory in the path (again).
176   EXPECT_EQ(0, mkdir(subdir_path.c_str(), 0700));
177   timeout.tv_sec = 0;
178   timeout.tv_nsec = 100000000;
179   EXPECT_EQ(1, cras_poll(&poll_fd, 1, &timeout, NULL));
180   EXPECT_EQ(0, cras_file_wait_dispatch(file_wait));
181   EXPECT_EQ(0, file_wait_result.called);
182   // See IN_IGNORED above.
183   EXPECT_EQ(0, cras_file_wait_dispatch(file_wait));
184   EXPECT_EQ(-EAGAIN, cras_file_wait_dispatch(file_wait));
185 
186   // Create the file we're looking for.
187   EXPECT_EQ(0, mknod(file_path.c_str(), S_IFREG | 0600, 0));
188   timeout.tv_sec = 0;
189   timeout.tv_nsec = 100000000;
190   EXPECT_EQ(1, cras_poll(&poll_fd, 1, &timeout, NULL));
191   EXPECT_EQ(0, cras_file_wait_dispatch(file_wait));
192   EXPECT_EQ(1, file_wait_result.called);
193   EXPECT_EQ(CRAS_FILE_WAIT_EVENT_CREATED, file_wait_result.event);
194   EXPECT_EQ(-EAGAIN, cras_file_wait_dispatch(file_wait));
195 
196   // Remove the file.
197   file_wait_result.called = 0;
198   EXPECT_EQ(0, unlink(file_path.c_str()));
199   timeout.tv_sec = 0;
200   timeout.tv_nsec = 100000000;
201   EXPECT_EQ(1, cras_poll(&poll_fd, 1, &timeout, NULL));
202   EXPECT_EQ(0, cras_file_wait_dispatch(file_wait));
203   EXPECT_EQ(1, file_wait_result.called);
204   EXPECT_EQ(CRAS_FILE_WAIT_EVENT_DELETED, file_wait_result.event);
205   EXPECT_EQ(-EAGAIN, cras_file_wait_dispatch(file_wait));
206 
207   // Re-create the file.
208   file_wait_result.called = 0;
209   EXPECT_EQ(0, mknod(file_path.c_str(), S_IFREG | 0600, 0));
210   timeout.tv_sec = 0;
211   timeout.tv_nsec = 100000000;
212   EXPECT_EQ(1, cras_poll(&poll_fd, 1, &timeout, NULL));
213   EXPECT_EQ(0, cras_file_wait_dispatch(file_wait));
214   EXPECT_EQ(1, file_wait_result.called);
215   EXPECT_EQ(CRAS_FILE_WAIT_EVENT_CREATED, file_wait_result.event);
216   EXPECT_EQ(-EAGAIN, cras_file_wait_dispatch(file_wait));
217 
218   // Remove the subdir.
219   file_wait_result.called = 0;
220   EXPECT_EQ(0, RmRF(subdir_path));
221   timeout.tv_sec = 0;
222   timeout.tv_nsec = 100000000;
223   EXPECT_EQ(1, cras_poll(&poll_fd, 1, &timeout, NULL));
224   EXPECT_EQ(0, cras_file_wait_dispatch(file_wait));
225   EXPECT_EQ(1, file_wait_result.called);
226   EXPECT_EQ(CRAS_FILE_WAIT_EVENT_DELETED, file_wait_result.event);
227   EXPECT_EQ(-EAGAIN, cras_file_wait_dispatch(file_wait));
228 
229   // Create a sub-directory in the path (again), and this time mock a race
230   // condition for creation of the file.
231   file_wait_result.called = 0;
232   EXPECT_EQ(0, mkdir(subdir_path.c_str(), 0700));
233   timeout.tv_sec = 0;
234   timeout.tv_nsec = 100000000;
235   EXPECT_EQ(1, cras_poll(&poll_fd, 1, &timeout, NULL));
236   cras_file_wait_mock_race_condition(file_wait);
237   EXPECT_EQ(0, cras_file_wait_dispatch(file_wait));
238   EXPECT_EQ(1, file_wait_result.called);
239   EXPECT_EQ(CRAS_FILE_WAIT_EVENT_CREATED, file_wait_result.event);
240   EXPECT_EQ(0, cras_file_wait_dispatch(file_wait));
241   EXPECT_EQ(1, file_wait_result.called);
242   EXPECT_EQ(-EAGAIN, cras_file_wait_dispatch(file_wait));
243 
244   // Cleanup.
245   cras_file_wait_destroy(file_wait);
246 
247   // Treat consecutive '/' as one.
248   file_path = dir_path + "//does_not_exist_too";
249   SimpleFileWait(file_path.c_str());
250 
251   // Stash the current directory.
252   current_dir = open(".", O_RDONLY | O_PATH | O_DIRECTORY);
253   ASSERT_NE(0, current_dir >= 0);
254 
255   // Search for a file in the current directory.
256   ASSERT_EQ(0, chdir(dir_path.c_str()));
257   SimpleFileWait("does_not_exist_either");
258 
259   // Test notification of deletion in the current directory.
260   SimpleFileWait("does_not_exist_either");
261 
262   // Search for a file in the current directory (variation).
263   SimpleFileWait("./does_not_exist_either_too");
264 
265   // Return to the start directory.
266   EXPECT_EQ(0, fchdir(current_dir));
267 
268   // Clean up.
269   EXPECT_EQ(0, RmRF(dir_path));
270 }
271 
272 }  //  namespace
273 
main(int argc,char ** argv)274 int main(int argc, char** argv) {
275   ::testing::InitGoogleTest(&argc, argv);
276   return RUN_ALL_TESTS();
277 }
278