Serenity Operating System
1/*
2 * Copyright (c) 2021, sin-ack <sin-ack@protonmail.com>
3 *
4 * SPDX-License-Identifier: BSD-2-Clause
5 */
6
7#include <AK/NumericLimits.h>
8#include <Kernel/API/InodeWatcherEvent.h>
9#include <Kernel/API/InodeWatcherFlags.h>
10#include <LibTest/TestCase.h>
11#include <errno.h>
12#include <fcntl.h>
13#include <stdio.h>
14#include <unistd.h>
15#include <utime.h>
16
17u8 buffer[MAXIMUM_EVENT_SIZE];
18InodeWatcherEvent* event = reinterpret_cast<InodeWatcherEvent*>(buffer);
19
20static int read_event(int fd)
21{
22 int rc = read(fd, &buffer, MAXIMUM_EVENT_SIZE);
23 return rc;
24}
25
26static DeprecatedString get_event_name()
27{
28 if (event->name_length == 0)
29 return DeprecatedString();
30
31 return DeprecatedString { event->name, event->name_length - 1 };
32}
33
34TEST_CASE(inode_watcher_metadata_modified_event)
35{
36 int fd = create_inode_watcher(0);
37 EXPECT_NE(fd, -1);
38
39 int test_fd = creat("/tmp/testfile", 0777);
40 EXPECT_NE(test_fd, -1);
41
42 int wd = inode_watcher_add_watch(fd, "/tmp/testfile", 13, static_cast<unsigned>(InodeWatcherEvent::Type::MetadataModified));
43 EXPECT_NE(wd, -1);
44
45 // "touch" the file
46 int rc = utime("/tmp/testfile", nullptr);
47 EXPECT_NE(rc, -1);
48
49 rc = read_event(fd);
50 EXPECT_EQ(event->watch_descriptor, wd);
51 EXPECT_EQ(event->type, InodeWatcherEvent::Type::MetadataModified);
52
53 close(fd);
54 close(test_fd);
55 unlink("/tmp/testfile");
56}
57
58TEST_CASE(inode_watcher_content_modified_event)
59{
60 int fd = create_inode_watcher(0);
61 EXPECT_NE(fd, -1);
62
63 int test_fd = creat("/tmp/testfile", 0777);
64 EXPECT_NE(test_fd, -1);
65
66 int wd = inode_watcher_add_watch(fd, "/tmp/testfile", 13, static_cast<unsigned>(InodeWatcherEvent::Type::ContentModified));
67 EXPECT_NE(wd, -1);
68
69 int rc = write(test_fd, "test", 4);
70 EXPECT_NE(rc, -1);
71
72 rc = read_event(fd);
73 EXPECT_NE(rc, -1);
74 EXPECT_EQ(event->watch_descriptor, wd);
75 EXPECT_EQ(event->type, InodeWatcherEvent::Type::ContentModified);
76
77 close(fd);
78 close(test_fd);
79 unlink("/tmp/testfile");
80}
81
82TEST_CASE(inode_watcher_deleted_event)
83{
84 int fd = create_inode_watcher(0);
85 EXPECT_NE(fd, -1);
86
87 int test_fd = creat("/tmp/testfile", 0777);
88 EXPECT_NE(test_fd, -1);
89
90 int wd = inode_watcher_add_watch(fd, "/tmp/testfile", 13, static_cast<unsigned>(InodeWatcherEvent::Type::Deleted));
91 EXPECT_NE(wd, -1);
92
93 int rc = unlink("/tmp/testfile");
94 EXPECT_NE(rc, -1);
95
96 rc = read_event(fd);
97 EXPECT_NE(rc, -1);
98 EXPECT_EQ(event->watch_descriptor, wd);
99 EXPECT_EQ(event->type, InodeWatcherEvent::Type::Deleted);
100
101 close(fd);
102 close(test_fd);
103}
104
105TEST_CASE(inode_watcher_child_events)
106{
107 int fd = create_inode_watcher(0);
108 EXPECT_NE(fd, -1);
109
110 int wd = inode_watcher_add_watch(fd, "/tmp/", 5, static_cast<unsigned>(InodeWatcherEvent::Type::ChildCreated | InodeWatcherEvent::Type::ChildDeleted));
111 EXPECT_NE(fd, -1);
112
113 int rc = creat("/tmp/testfile", 0777);
114 EXPECT_NE(rc, -1);
115
116 rc = read_event(fd);
117 EXPECT_NE(rc, -1);
118 EXPECT_EQ(event->watch_descriptor, wd);
119 EXPECT_EQ(event->type, InodeWatcherEvent::Type::ChildCreated);
120 VERIFY(event->name_length > 0);
121 EXPECT_EQ(get_event_name(), "testfile");
122
123 rc = unlink("/tmp/testfile");
124 EXPECT_NE(rc, -1);
125
126 rc = read_event(fd);
127 EXPECT_NE(rc, -1);
128 EXPECT_EQ(event->watch_descriptor, wd);
129 EXPECT_EQ(event->type, InodeWatcherEvent::Type::ChildDeleted);
130 VERIFY(event->name_length > 0);
131 EXPECT_EQ(get_event_name(), "testfile");
132
133 close(fd);
134}
135
136TEST_CASE(inode_watcher_closes_children_on_close)
137{
138 int fd = create_inode_watcher(0);
139 EXPECT_NE(fd, -1);
140
141 int test_fd = creat("/tmp/testfile", 0777);
142 EXPECT_NE(test_fd, -1);
143 int wd = inode_watcher_add_watch(fd, "/tmp/testfile", 13, static_cast<unsigned>(InodeWatcherEvent::Type::MetadataModified));
144 EXPECT_NE(wd, -1);
145
146 int rc = utime("/tmp/testfile", nullptr);
147 EXPECT_NE(rc, -1);
148
149 close(fd);
150
151 rc = read_event(fd);
152 EXPECT_EQ(rc, -1);
153 EXPECT_EQ(errno, EBADF);
154
155 close(test_fd);
156 unlink("/tmp/testfile");
157}
158
159TEST_CASE(inode_watcher_nonblock)
160{
161 int fd = create_inode_watcher(static_cast<unsigned>(InodeWatcherFlags::Nonblock));
162 EXPECT_NE(fd, -1);
163
164 int rc = read_event(fd);
165 EXPECT_EQ(rc, -1);
166 EXPECT_EQ(errno, EAGAIN);
167
168 close(fd);
169}