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 <gtest/gtest.h>
6 #include <fcntl.h>
7 #include <stdlib.h>
8 #include <string>
9
10 #include "cras_util.h"
11 #include "cras_file_wait.h"
12
13 extern "C" {
14 // This function is not exported in cras_util.h.
15 void cras_file_wait_mock_race_condition(struct cras_file_wait *file_wait);
16 }
17
18 namespace {
19
20 // Executes "rm -rf <path>".
RmRF(const std::string & path)21 static int RmRF(const std::string& path) {
22 std::string cmd("rm -rf \"");
23 cmd += path + "\"";
24
25 if (path == "/")
26 return -EINVAL;
27
28 int rc = system(cmd.c_str());
29 if (rc < 0)
30 return -errno;
31 return WEXITSTATUS(rc);
32 }
33
34 // Filled-in by the FileWaitCallback.
35 struct FileWaitResult {
36 size_t called;
37 cras_file_wait_event_t event;
38 };
39
40 // Called by the file wait code for an event.
FileWaitCallback(void * context,cras_file_wait_event_t event,const char * filename)41 static void FileWaitCallback(void *context,
42 cras_file_wait_event_t event,
43 const char *filename)
44 {
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(
116 NULL, CRAS_FILE_WAIT_FLAG_NONE,
117 FileWaitCallback, &file_wait_result, &file_wait));
118 // Empty file path.
119 EXPECT_EQ(-EINVAL, cras_file_wait_create(
120 "", CRAS_FILE_WAIT_FLAG_NONE,
121 FileWaitCallback, &file_wait_result, &file_wait));
122 // No callback structure.
123 EXPECT_EQ(-EINVAL, cras_file_wait_create(
124 ".", CRAS_FILE_WAIT_FLAG_NONE,
125 NULL, NULL, &file_wait));
126 // No file wait structure.
127 EXPECT_EQ(-EINVAL, cras_file_wait_create(
128 ".", CRAS_FILE_WAIT_FLAG_NONE,
129 FileWaitCallback, &file_wait_result, NULL));
130 EXPECT_EQ(-EINVAL, cras_file_wait_dispatch(NULL));
131 EXPECT_EQ(-EINVAL, cras_file_wait_get_fd(NULL));
132
133 // Make sure that /tmp exists.
134 file_wait_result.called = 0;
135 EXPECT_EQ(0, cras_file_wait_create(CRAS_UT_TMPDIR, CRAS_FILE_WAIT_FLAG_NONE,
136 FileWaitCallback, &file_wait_result,
137 &file_wait));
138 EXPECT_NE(reinterpret_cast<struct cras_file_wait *>(NULL), file_wait);
139 EXPECT_EQ(file_wait_result.called, 1);
140 ASSERT_EQ(file_wait_result.event, CRAS_FILE_WAIT_EVENT_CREATED);
141 cras_file_wait_destroy(file_wait);
142
143 // Create our temporary dir.
144 ASSERT_EQ(0, RmRF(dir_path));
145 ASSERT_EQ(0, mkdir(dir_path.c_str(), 0700));
146
147 // Start looking for our file '.../does_not_exist'.
148 EXPECT_EQ(0, cras_file_wait_create(file_path.c_str(),
149 CRAS_FILE_WAIT_FLAG_NONE,
150 FileWaitCallback, &file_wait_result,
151 &file_wait));
152 EXPECT_NE(reinterpret_cast<struct cras_file_wait *>(NULL), file_wait);
153 poll_fd.events = POLLIN;
154 poll_fd.fd = cras_file_wait_get_fd(file_wait);
155 EXPECT_NE(0, poll_fd.fd >= 0);
156
157 // Create a sub-directory in the path.
158 file_wait_result.called = 0;
159 EXPECT_EQ(0, mkdir(subdir_path.c_str(), 0700));
160 EXPECT_EQ(1, cras_poll(&poll_fd, 1, &timeout, NULL));
161 EXPECT_EQ(0, cras_file_wait_dispatch(file_wait));
162 EXPECT_EQ(0, file_wait_result.called);
163 // Removing a watch causes generation of an IN_IGNORED event for the previous
164 // watch_id. cras_file_wait_dispatch will ignore this and return 0.
165 EXPECT_EQ(0, cras_file_wait_dispatch(file_wait));
166 EXPECT_EQ(-EAGAIN, cras_file_wait_dispatch(file_wait));
167
168 // Remove the directory that we're watching.
169 EXPECT_EQ(0, RmRF(subdir_path));
170 timeout.tv_sec = 0;
171 timeout.tv_nsec = 100000000;
172 EXPECT_EQ(1, cras_poll(&poll_fd, 1, &timeout, NULL));
173 EXPECT_EQ(0, cras_file_wait_dispatch(file_wait));
174 EXPECT_EQ(0, file_wait_result.called);
175 EXPECT_EQ(-EAGAIN, cras_file_wait_dispatch(file_wait));
176
177 // Create a sub-directory in the path (again).
178 EXPECT_EQ(0, mkdir(subdir_path.c_str(), 0700));
179 timeout.tv_sec = 0;
180 timeout.tv_nsec = 100000000;
181 EXPECT_EQ(1, cras_poll(&poll_fd, 1, &timeout, NULL));
182 EXPECT_EQ(0, cras_file_wait_dispatch(file_wait));
183 EXPECT_EQ(0, file_wait_result.called);
184 // See IN_IGNORED above.
185 EXPECT_EQ(0, cras_file_wait_dispatch(file_wait));
186 EXPECT_EQ(-EAGAIN, cras_file_wait_dispatch(file_wait));
187
188 // Create the file we're looking for.
189 EXPECT_EQ(0, mknod(file_path.c_str(), S_IFREG | 0600, 0));
190 timeout.tv_sec = 0;
191 timeout.tv_nsec = 100000000;
192 EXPECT_EQ(1, cras_poll(&poll_fd, 1, &timeout, NULL));
193 EXPECT_EQ(0, cras_file_wait_dispatch(file_wait));
194 EXPECT_EQ(1, file_wait_result.called);
195 EXPECT_EQ(CRAS_FILE_WAIT_EVENT_CREATED, file_wait_result.event);
196 EXPECT_EQ(-EAGAIN, cras_file_wait_dispatch(file_wait));
197
198 // Remove the file.
199 file_wait_result.called = 0;
200 EXPECT_EQ(0, unlink(file_path.c_str()));
201 timeout.tv_sec = 0;
202 timeout.tv_nsec = 100000000;
203 EXPECT_EQ(1, cras_poll(&poll_fd, 1, &timeout, NULL));
204 EXPECT_EQ(0, cras_file_wait_dispatch(file_wait));
205 EXPECT_EQ(1, file_wait_result.called);
206 EXPECT_EQ(CRAS_FILE_WAIT_EVENT_DELETED, file_wait_result.event);
207 EXPECT_EQ(-EAGAIN, cras_file_wait_dispatch(file_wait));
208
209 // Re-create the file.
210 file_wait_result.called = 0;
211 EXPECT_EQ(0, mknod(file_path.c_str(), S_IFREG | 0600, 0));
212 timeout.tv_sec = 0;
213 timeout.tv_nsec = 100000000;
214 EXPECT_EQ(1, cras_poll(&poll_fd, 1, &timeout, NULL));
215 EXPECT_EQ(0, cras_file_wait_dispatch(file_wait));
216 EXPECT_EQ(1, file_wait_result.called);
217 EXPECT_EQ(CRAS_FILE_WAIT_EVENT_CREATED, file_wait_result.event);
218 EXPECT_EQ(-EAGAIN, cras_file_wait_dispatch(file_wait));
219
220 // Remove the subdir.
221 file_wait_result.called = 0;
222 EXPECT_EQ(0, RmRF(subdir_path));
223 timeout.tv_sec = 0;
224 timeout.tv_nsec = 100000000;
225 EXPECT_EQ(1, cras_poll(&poll_fd, 1, &timeout, NULL));
226 EXPECT_EQ(0, cras_file_wait_dispatch(file_wait));
227 EXPECT_EQ(1, file_wait_result.called);
228 EXPECT_EQ(CRAS_FILE_WAIT_EVENT_DELETED, file_wait_result.event);
229 EXPECT_EQ(-EAGAIN, cras_file_wait_dispatch(file_wait));
230
231 // Create a sub-directory in the path (again), and this time mock a race
232 // condition for creation of the file.
233 file_wait_result.called = 0;
234 EXPECT_EQ(0, mkdir(subdir_path.c_str(), 0700));
235 timeout.tv_sec = 0;
236 timeout.tv_nsec = 100000000;
237 EXPECT_EQ(1, cras_poll(&poll_fd, 1, &timeout, NULL));
238 cras_file_wait_mock_race_condition(file_wait);
239 EXPECT_EQ(0, cras_file_wait_dispatch(file_wait));
240 EXPECT_EQ(1, file_wait_result.called);
241 EXPECT_EQ(CRAS_FILE_WAIT_EVENT_CREATED, file_wait_result.event);
242 EXPECT_EQ(0, cras_file_wait_dispatch(file_wait));
243 EXPECT_EQ(1, file_wait_result.called);
244 EXPECT_EQ(-EAGAIN, cras_file_wait_dispatch(file_wait));
245
246 // Cleanup.
247 cras_file_wait_destroy(file_wait);
248
249 // Treat consecutive '/' as one.
250 file_path = dir_path + "//does_not_exist_too";
251 SimpleFileWait(file_path.c_str());
252
253 // Stash the current directory.
254 current_dir = open(".", O_RDONLY|O_PATH|O_DIRECTORY);
255 ASSERT_NE(0, current_dir >= 0);
256
257 // Search for a file in the current directory.
258 ASSERT_EQ(0, chdir(dir_path.c_str()));
259 SimpleFileWait("does_not_exist_either");
260
261 // Test notification of deletion in the current directory.
262 SimpleFileWait("does_not_exist_either");
263
264 // Search for a file in the current directory (variation).
265 SimpleFileWait("./does_not_exist_either_too");
266
267 // Return to the start directory.
268 EXPECT_EQ(0, fchdir(current_dir));
269
270 // Clean up.
271 EXPECT_EQ(0, RmRF(dir_path));
272 }
273
274 } // namespace
275
main(int argc,char ** argv)276 int main(int argc, char **argv) {
277 ::testing::InitGoogleTest(&argc, argv);
278 return RUN_ALL_TESTS();
279 }
280