1 | /* Test for <file_change_detection.c>. |
2 | Copyright (C) 2020-2024 Free Software Foundation, Inc. |
3 | This file is part of the GNU C Library. |
4 | |
5 | The GNU C Library is free software; you can redistribute it and/or |
6 | modify it under the terms of the GNU Lesser General Public |
7 | License as published by the Free Software Foundation; either |
8 | version 2.1 of the License, or (at your option) any later version. |
9 | |
10 | The GNU C Library is distributed in the hope that it will be useful, |
11 | but WITHOUT ANY WARRANTY; without even the implied warranty of |
12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
13 | Lesser General Public License for more details. |
14 | |
15 | You should have received a copy of the GNU Lesser General Public |
16 | License along with the GNU C Library; if not, see |
17 | <https://www.gnu.org/licenses/>. */ |
18 | |
19 | #include <file_change_detection.h> |
20 | |
21 | #include <array_length.h> |
22 | #include <stdlib.h> |
23 | #include <support/check.h> |
24 | #include <support/support.h> |
25 | #include <support/temp_file.h> |
26 | #include <support/test-driver.h> |
27 | #include <support/xstdio.h> |
28 | #include <support/xunistd.h> |
29 | #include <unistd.h> |
30 | |
31 | static void |
32 | all_same (struct file_change_detection *array, size_t length) |
33 | { |
34 | for (size_t i = 0; i < length; ++i) |
35 | for (size_t j = 0; j < length; ++j) |
36 | { |
37 | if (test_verbose > 0) |
38 | printf (format: "info: comparing %zu and %zu\n" , i, j); |
39 | TEST_VERIFY (__file_is_unchanged (array + i, array + j)); |
40 | } |
41 | } |
42 | |
43 | static void |
44 | all_different (struct file_change_detection *array, size_t length) |
45 | { |
46 | for (size_t i = 0; i < length; ++i) |
47 | for (size_t j = 0; j < length; ++j) |
48 | { |
49 | if (i == j) |
50 | continue; |
51 | if (test_verbose > 0) |
52 | printf (format: "info: comparing %zu and %zu\n" , i, j); |
53 | TEST_VERIFY (!__file_is_unchanged (array + i, array + j)); |
54 | } |
55 | } |
56 | |
57 | static int |
58 | do_test (void) |
59 | { |
60 | /* Use a temporary directory with various paths. */ |
61 | char *tempdir = support_create_temp_directory (base: "tst-file_change_detection-" ); |
62 | |
63 | char *path_dangling = xasprintf (format: "%s/dangling" , tempdir); |
64 | char *path_does_not_exist = xasprintf (format: "%s/does-not-exist" , tempdir); |
65 | char *path_empty1 = xasprintf (format: "%s/empty1" , tempdir); |
66 | char *path_empty2 = xasprintf (format: "%s/empty2" , tempdir); |
67 | char *path_fifo = xasprintf (format: "%s/fifo" , tempdir); |
68 | char *path_file1 = xasprintf (format: "%s/file1" , tempdir); |
69 | char *path_file2 = xasprintf (format: "%s/file2" , tempdir); |
70 | char *path_loop = xasprintf (format: "%s/loop" , tempdir); |
71 | char *path_to_empty1 = xasprintf (format: "%s/to-empty1" , tempdir); |
72 | char *path_to_file1 = xasprintf (format: "%s/to-file1" , tempdir); |
73 | |
74 | add_temp_file (name: path_dangling); |
75 | add_temp_file (name: path_empty1); |
76 | add_temp_file (name: path_empty2); |
77 | add_temp_file (name: path_fifo); |
78 | add_temp_file (name: path_file1); |
79 | add_temp_file (name: path_file2); |
80 | add_temp_file (name: path_loop); |
81 | add_temp_file (name: path_to_empty1); |
82 | add_temp_file (name: path_to_file1); |
83 | |
84 | xsymlink (target: "target-does-not-exist" , linkpath: path_dangling); |
85 | support_write_file_string (path: path_empty1, contents: "" ); |
86 | support_write_file_string (path: path_empty2, contents: "" ); |
87 | TEST_COMPARE (mknod (path_fifo, 0777 | S_IFIFO, 0), 0); |
88 | support_write_file_string (path: path_file1, contents: "line\n" ); |
89 | support_write_file_string (path: path_file2, contents: "line\n" ); |
90 | xsymlink (target: "loop" , linkpath: path_loop); |
91 | xsymlink (target: "empty1" , linkpath: path_to_empty1); |
92 | xsymlink (target: "file1" , linkpath: path_to_file1); |
93 | |
94 | FILE *fp_file1 = xfopen (path: path_file1, mode: "r" ); |
95 | FILE *fp_file2 = xfopen (path: path_file2, mode: "r" ); |
96 | FILE *fp_empty1 = xfopen (path: path_empty1, mode: "r" ); |
97 | FILE *fp_empty2 = xfopen (path: path_empty2, mode: "r" ); |
98 | |
99 | /* Test for the same (empty) files. */ |
100 | { |
101 | struct file_change_detection fcd[10]; |
102 | int i = 0; |
103 | /* Two empty files always have the same contents. */ |
104 | TEST_VERIFY (__file_change_detection_for_path (&fcd[i++], path_empty1)); |
105 | TEST_VERIFY (__file_change_detection_for_path (&fcd[i++], path_empty2)); |
106 | /* So does a missing file (which is treated as empty). */ |
107 | TEST_VERIFY (__file_change_detection_for_path (&fcd[i++], |
108 | path_does_not_exist)); |
109 | /* And a symbolic link loop. */ |
110 | TEST_VERIFY (__file_change_detection_for_path (&fcd[i++], path_loop)); |
111 | /* And a dangling symbolic link. */ |
112 | TEST_VERIFY (__file_change_detection_for_path (&fcd[i++], path_dangling)); |
113 | /* And a directory. */ |
114 | TEST_VERIFY (__file_change_detection_for_path (&fcd[i++], tempdir)); |
115 | /* And a symbolic link to an empty file. */ |
116 | TEST_VERIFY (__file_change_detection_for_path (&fcd[i++], path_to_empty1)); |
117 | /* Likewise for access the file via a FILE *. */ |
118 | TEST_VERIFY (__file_change_detection_for_fp (&fcd[i++], fp_empty1)); |
119 | TEST_VERIFY (__file_change_detection_for_fp (&fcd[i++], fp_empty2)); |
120 | /* And a NULL FILE * (missing file). */ |
121 | TEST_VERIFY (__file_change_detection_for_fp (&fcd[i++], NULL)); |
122 | TEST_COMPARE (i, array_length (fcd)); |
123 | |
124 | all_same (array: fcd, array_length (fcd)); |
125 | } |
126 | |
127 | /* Symbolic links are resolved. */ |
128 | { |
129 | struct file_change_detection fcd[3]; |
130 | int i = 0; |
131 | TEST_VERIFY (__file_change_detection_for_path (&fcd[i++], path_file1)); |
132 | TEST_VERIFY (__file_change_detection_for_path (&fcd[i++], path_to_file1)); |
133 | TEST_VERIFY (__file_change_detection_for_fp (&fcd[i++], fp_file1)); |
134 | TEST_COMPARE (i, array_length (fcd)); |
135 | all_same (array: fcd, array_length (fcd)); |
136 | } |
137 | |
138 | /* Test for different files. */ |
139 | { |
140 | struct file_change_detection fcd[5]; |
141 | int i = 0; |
142 | /* The other files are not empty. */ |
143 | TEST_VERIFY (__file_change_detection_for_path (&fcd[i++], path_empty1)); |
144 | /* These two files have the same contents, but have different file |
145 | identity. */ |
146 | TEST_VERIFY (__file_change_detection_for_path (&fcd[i++], path_file1)); |
147 | TEST_VERIFY (__file_change_detection_for_path (&fcd[i++], path_file2)); |
148 | /* FIFOs are always different, even with themselves. */ |
149 | TEST_VERIFY (__file_change_detection_for_path (&fcd[i++], path_fifo)); |
150 | TEST_VERIFY (__file_change_detection_for_path (&fcd[i++], path_fifo)); |
151 | TEST_COMPARE (i, array_length (fcd)); |
152 | all_different (array: fcd, array_length (fcd)); |
153 | |
154 | /* Replacing the file with its symbolic link does not make a |
155 | difference. */ |
156 | TEST_VERIFY (__file_change_detection_for_path (&fcd[1], path_to_file1)); |
157 | all_different (array: fcd, array_length (fcd)); |
158 | } |
159 | |
160 | /* Wait for a file change. Depending on file system time stamp |
161 | resolution, this subtest blocks for a while. */ |
162 | for (int use_stdio = 0; use_stdio < 2; ++use_stdio) |
163 | { |
164 | struct file_change_detection initial; |
165 | TEST_VERIFY (__file_change_detection_for_path (&initial, path_file1)); |
166 | while (true) |
167 | { |
168 | support_write_file_string (path: path_file1, contents: "line\n" ); |
169 | struct file_change_detection current; |
170 | if (use_stdio) |
171 | TEST_VERIFY (__file_change_detection_for_fp (¤t, fp_file1)); |
172 | else |
173 | TEST_VERIFY (__file_change_detection_for_path |
174 | (¤t, path_file1)); |
175 | if (!__file_is_unchanged (&initial, ¤t)) |
176 | break; |
177 | /* Wait for a bit to reduce system load. */ |
178 | usleep (useconds: 100 * 1000); |
179 | } |
180 | } |
181 | |
182 | fclose (fp_empty1); |
183 | fclose (fp_empty2); |
184 | fclose (fp_file1); |
185 | fclose (fp_file2); |
186 | |
187 | free (ptr: path_dangling); |
188 | free (ptr: path_does_not_exist); |
189 | free (ptr: path_empty1); |
190 | free (ptr: path_empty2); |
191 | free (ptr: path_fifo); |
192 | free (ptr: path_file1); |
193 | free (ptr: path_file2); |
194 | free (ptr: path_loop); |
195 | free (ptr: path_to_empty1); |
196 | free (ptr: path_to_file1); |
197 | |
198 | free (ptr: tempdir); |
199 | |
200 | return 0; |
201 | } |
202 | |
203 | #include <support/test-driver.c> |
204 | |