blob: 87cd53c942aec99f24f7a806882805f43200a4f9 [file] [log] [blame]
// Copyright 2016 The Chromium OS Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include <fcntl.h>
#include <gtest/gtest.h>
#include <stdlib.h>
#include <string>
#include "cras_file_wait.h"
#include "cras_util.h"
extern "C" {
// This function is not exported in cras_util.h.
void cras_file_wait_mock_race_condition(struct cras_file_wait* file_wait);
}
namespace {
// Executes "rm -rf <path>".
static int RmRF(const std::string& path) {
std::string cmd("rm -rf \"");
cmd += path + "\"";
if (path == "/")
return -EINVAL;
int rc = system(cmd.c_str());
if (rc < 0)
return -errno;
return WEXITSTATUS(rc);
}
// Filled-in by the FileWaitCallback.
struct FileWaitResult {
size_t called;
cras_file_wait_event_t event;
};
// Called by the file wait code for an event.
static void FileWaitCallback(void* context,
cras_file_wait_event_t event,
const char* filename) {
FileWaitResult* result = reinterpret_cast<FileWaitResult*>(context);
result->called++;
result->event = event;
}
// Do all of the EXPECTed steps for a simple wait for one file.
static void SimpleFileWait(const char* file_path) {
struct cras_file_wait* file_wait;
FileWaitResult file_wait_result;
struct pollfd poll_fd;
struct timespec timeout = {0, 100000000};
struct stat stat_buf;
int stat_rc;
stat_rc = stat(file_path, &stat_buf);
if (stat_rc < 0)
stat_rc = -errno;
file_wait_result.called = 0;
EXPECT_EQ(0, cras_file_wait_create(file_path, CRAS_FILE_WAIT_FLAG_NONE,
FileWaitCallback, &file_wait_result,
&file_wait));
EXPECT_NE(reinterpret_cast<struct cras_file_wait*>(NULL), file_wait);
if (stat_rc == 0) {
EXPECT_EQ(1, file_wait_result.called);
EXPECT_EQ(CRAS_FILE_WAIT_EVENT_CREATED, file_wait_result.event);
} else {
EXPECT_EQ(0, file_wait_result.called);
}
poll_fd.events = POLLIN;
poll_fd.fd = cras_file_wait_get_fd(file_wait);
file_wait_result.called = 0;
if (stat_rc == 0)
EXPECT_EQ(0, RmRF(file_path));
else
EXPECT_EQ(0, mknod(file_path, S_IFREG | 0600, 0));
EXPECT_EQ(1, cras_poll(&poll_fd, 1, &timeout, NULL));
EXPECT_EQ(0, cras_file_wait_dispatch(file_wait));
EXPECT_EQ(1, file_wait_result.called);
if (stat_rc == 0)
EXPECT_EQ(CRAS_FILE_WAIT_EVENT_DELETED, file_wait_result.event);
else
EXPECT_EQ(CRAS_FILE_WAIT_EVENT_CREATED, file_wait_result.event);
EXPECT_EQ(-EAGAIN, cras_file_wait_dispatch(file_wait));
cras_file_wait_destroy(file_wait);
}
// Test the cras_file_wait functions including multiple path components
// missing and path components deleted and recreated.
TEST(Util, FileWait) {
struct cras_file_wait* file_wait;
FileWaitResult file_wait_result;
pid_t pid = getpid();
struct pollfd poll_fd;
int current_dir;
struct timespec timeout = {0, 100000000};
char pid_buf[32];
std::string tmp_dir(CRAS_UT_TMPDIR);
std::string dir_path;
std::string subdir_path;
std::string file_path;
snprintf(pid_buf, sizeof(pid_buf), "%d", pid);
dir_path = tmp_dir + "/" + pid_buf;
subdir_path = dir_path + "/subdir";
file_path = subdir_path + "/does_not_exist";
// Test arguments.
// Null file path.
EXPECT_EQ(-EINVAL, cras_file_wait_create(NULL, CRAS_FILE_WAIT_FLAG_NONE,
FileWaitCallback, &file_wait_result,
&file_wait));
// Empty file path.
EXPECT_EQ(-EINVAL, cras_file_wait_create("", CRAS_FILE_WAIT_FLAG_NONE,
FileWaitCallback, &file_wait_result,
&file_wait));
// No callback structure.
EXPECT_EQ(-EINVAL, cras_file_wait_create(".", CRAS_FILE_WAIT_FLAG_NONE, NULL,
NULL, &file_wait));
// No file wait structure.
EXPECT_EQ(-EINVAL,
cras_file_wait_create(".", CRAS_FILE_WAIT_FLAG_NONE,
FileWaitCallback, &file_wait_result, NULL));
EXPECT_EQ(-EINVAL, cras_file_wait_dispatch(NULL));
EXPECT_EQ(-EINVAL, cras_file_wait_get_fd(NULL));
// Make sure that /tmp exists.
file_wait_result.called = 0;
EXPECT_EQ(0, cras_file_wait_create(CRAS_UT_TMPDIR, CRAS_FILE_WAIT_FLAG_NONE,
FileWaitCallback, &file_wait_result,
&file_wait));
EXPECT_NE(reinterpret_cast<struct cras_file_wait*>(NULL), file_wait);
EXPECT_EQ(file_wait_result.called, 1);
ASSERT_EQ(file_wait_result.event, CRAS_FILE_WAIT_EVENT_CREATED);
cras_file_wait_destroy(file_wait);
// Create our temporary dir.
ASSERT_EQ(0, RmRF(dir_path));
ASSERT_EQ(0, mkdir(dir_path.c_str(), 0700));
// Start looking for our file '.../does_not_exist'.
EXPECT_EQ(0, cras_file_wait_create(file_path.c_str(),
CRAS_FILE_WAIT_FLAG_NONE, FileWaitCallback,
&file_wait_result, &file_wait));
EXPECT_NE(reinterpret_cast<struct cras_file_wait*>(NULL), file_wait);
poll_fd.events = POLLIN;
poll_fd.fd = cras_file_wait_get_fd(file_wait);
EXPECT_NE(0, poll_fd.fd >= 0);
// Create a sub-directory in the path.
file_wait_result.called = 0;
EXPECT_EQ(0, mkdir(subdir_path.c_str(), 0700));
EXPECT_EQ(1, cras_poll(&poll_fd, 1, &timeout, NULL));
EXPECT_EQ(0, cras_file_wait_dispatch(file_wait));
EXPECT_EQ(0, file_wait_result.called);
// Removing a watch causes generation of an IN_IGNORED event for the previous
// watch_id. cras_file_wait_dispatch will ignore this and return 0.
EXPECT_EQ(0, cras_file_wait_dispatch(file_wait));
EXPECT_EQ(-EAGAIN, cras_file_wait_dispatch(file_wait));
// Remove the directory that we're watching.
EXPECT_EQ(0, RmRF(subdir_path));
timeout.tv_sec = 0;
timeout.tv_nsec = 100000000;
EXPECT_EQ(1, cras_poll(&poll_fd, 1, &timeout, NULL));
EXPECT_EQ(0, cras_file_wait_dispatch(file_wait));
EXPECT_EQ(0, file_wait_result.called);
EXPECT_EQ(-EAGAIN, cras_file_wait_dispatch(file_wait));
// Create a sub-directory in the path (again).
EXPECT_EQ(0, mkdir(subdir_path.c_str(), 0700));
timeout.tv_sec = 0;
timeout.tv_nsec = 100000000;
EXPECT_EQ(1, cras_poll(&poll_fd, 1, &timeout, NULL));
EXPECT_EQ(0, cras_file_wait_dispatch(file_wait));
EXPECT_EQ(0, file_wait_result.called);
// See IN_IGNORED above.
EXPECT_EQ(0, cras_file_wait_dispatch(file_wait));
EXPECT_EQ(-EAGAIN, cras_file_wait_dispatch(file_wait));
// Create the file we're looking for.
EXPECT_EQ(0, mknod(file_path.c_str(), S_IFREG | 0600, 0));
timeout.tv_sec = 0;
timeout.tv_nsec = 100000000;
EXPECT_EQ(1, cras_poll(&poll_fd, 1, &timeout, NULL));
EXPECT_EQ(0, cras_file_wait_dispatch(file_wait));
EXPECT_EQ(1, file_wait_result.called);
EXPECT_EQ(CRAS_FILE_WAIT_EVENT_CREATED, file_wait_result.event);
EXPECT_EQ(-EAGAIN, cras_file_wait_dispatch(file_wait));
// Remove the file.
file_wait_result.called = 0;
EXPECT_EQ(0, unlink(file_path.c_str()));
timeout.tv_sec = 0;
timeout.tv_nsec = 100000000;
EXPECT_EQ(1, cras_poll(&poll_fd, 1, &timeout, NULL));
EXPECT_EQ(0, cras_file_wait_dispatch(file_wait));
EXPECT_EQ(1, file_wait_result.called);
EXPECT_EQ(CRAS_FILE_WAIT_EVENT_DELETED, file_wait_result.event);
EXPECT_EQ(-EAGAIN, cras_file_wait_dispatch(file_wait));
// Re-create the file.
file_wait_result.called = 0;
EXPECT_EQ(0, mknod(file_path.c_str(), S_IFREG | 0600, 0));
timeout.tv_sec = 0;
timeout.tv_nsec = 100000000;
EXPECT_EQ(1, cras_poll(&poll_fd, 1, &timeout, NULL));
EXPECT_EQ(0, cras_file_wait_dispatch(file_wait));
EXPECT_EQ(1, file_wait_result.called);
EXPECT_EQ(CRAS_FILE_WAIT_EVENT_CREATED, file_wait_result.event);
EXPECT_EQ(-EAGAIN, cras_file_wait_dispatch(file_wait));
// Remove the subdir.
file_wait_result.called = 0;
EXPECT_EQ(0, RmRF(subdir_path));
timeout.tv_sec = 0;
timeout.tv_nsec = 100000000;
EXPECT_EQ(1, cras_poll(&poll_fd, 1, &timeout, NULL));
EXPECT_EQ(0, cras_file_wait_dispatch(file_wait));
EXPECT_EQ(1, file_wait_result.called);
EXPECT_EQ(CRAS_FILE_WAIT_EVENT_DELETED, file_wait_result.event);
EXPECT_EQ(-EAGAIN, cras_file_wait_dispatch(file_wait));
// Create a sub-directory in the path (again), and this time mock a race
// condition for creation of the file.
file_wait_result.called = 0;
EXPECT_EQ(0, mkdir(subdir_path.c_str(), 0700));
timeout.tv_sec = 0;
timeout.tv_nsec = 100000000;
EXPECT_EQ(1, cras_poll(&poll_fd, 1, &timeout, NULL));
cras_file_wait_mock_race_condition(file_wait);
EXPECT_EQ(0, cras_file_wait_dispatch(file_wait));
EXPECT_EQ(1, file_wait_result.called);
EXPECT_EQ(CRAS_FILE_WAIT_EVENT_CREATED, file_wait_result.event);
EXPECT_EQ(0, cras_file_wait_dispatch(file_wait));
EXPECT_EQ(1, file_wait_result.called);
EXPECT_EQ(-EAGAIN, cras_file_wait_dispatch(file_wait));
// Cleanup.
cras_file_wait_destroy(file_wait);
// Treat consecutive '/' as one.
file_path = dir_path + "//does_not_exist_too";
SimpleFileWait(file_path.c_str());
// Stash the current directory.
current_dir = open(".", O_RDONLY | O_PATH | O_DIRECTORY);
ASSERT_NE(0, current_dir >= 0);
// Search for a file in the current directory.
ASSERT_EQ(0, chdir(dir_path.c_str()));
SimpleFileWait("does_not_exist_either");
// Test notification of deletion in the current directory.
SimpleFileWait("does_not_exist_either");
// Search for a file in the current directory (variation).
SimpleFileWait("./does_not_exist_either_too");
// Return to the start directory.
EXPECT_EQ(0, fchdir(current_dir));
// Clean up.
EXPECT_EQ(0, RmRF(dir_path));
}
} // namespace
int main(int argc, char** argv) {
::testing::InitGoogleTest(&argc, argv);
return RUN_ALL_TESTS();
}