1 /* Test for <file_change_detection.c>.
2    Copyright (C) 2020-2022 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
all_same(struct file_change_detection * array,size_t length)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 ("info: comparing %zu and %zu\n", i, j);
39         TEST_VERIFY (__file_is_unchanged (array + i, array + j));
40       }
41 }
42 
43 static void
all_different(struct file_change_detection * array,size_t length)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 ("info: comparing %zu and %zu\n", i, j);
53         TEST_VERIFY (!__file_is_unchanged (array + i, array + j));
54       }
55 }
56 
57 static int
do_test(void)58 do_test (void)
59 {
60   /* Use a temporary directory with various paths.  */
61   char *tempdir = support_create_temp_directory ("tst-file_change_detection-");
62 
63   char *path_dangling = xasprintf ("%s/dangling", tempdir);
64   char *path_does_not_exist = xasprintf ("%s/does-not-exist", tempdir);
65   char *path_empty1 = xasprintf ("%s/empty1", tempdir);
66   char *path_empty2 = xasprintf ("%s/empty2", tempdir);
67   char *path_fifo = xasprintf ("%s/fifo", tempdir);
68   char *path_file1 = xasprintf ("%s/file1", tempdir);
69   char *path_file2 = xasprintf ("%s/file2", tempdir);
70   char *path_loop = xasprintf ("%s/loop", tempdir);
71   char *path_to_empty1 = xasprintf ("%s/to-empty1", tempdir);
72   char *path_to_file1 = xasprintf ("%s/to-file1", tempdir);
73 
74   add_temp_file (path_dangling);
75   add_temp_file (path_empty1);
76   add_temp_file (path_empty2);
77   add_temp_file (path_fifo);
78   add_temp_file (path_file1);
79   add_temp_file (path_file2);
80   add_temp_file (path_loop);
81   add_temp_file (path_to_empty1);
82   add_temp_file (path_to_file1);
83 
84   xsymlink ("target-does-not-exist", path_dangling);
85   support_write_file_string (path_empty1, "");
86   support_write_file_string (path_empty2, "");
87   TEST_COMPARE (mknod (path_fifo, 0777 | S_IFIFO, 0), 0);
88   support_write_file_string (path_file1, "line\n");
89   support_write_file_string (path_file2, "line\n");
90   xsymlink ("loop", path_loop);
91   xsymlink ("empty1", path_to_empty1);
92   xsymlink ("file1", path_to_file1);
93 
94   FILE *fp_file1 = xfopen (path_file1, "r");
95   FILE *fp_file2 = xfopen (path_file2, "r");
96   FILE *fp_empty1 = xfopen (path_empty1, "r");
97   FILE *fp_empty2 = xfopen (path_empty2, "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 (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 (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 (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 (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_file1, "line\n");
169           struct file_change_detection current;
170           if (use_stdio)
171             TEST_VERIFY (__file_change_detection_for_fp (&current, fp_file1));
172           else
173             TEST_VERIFY (__file_change_detection_for_path
174                          (&current, path_file1));
175           if (!__file_is_unchanged (&initial, &current))
176             break;
177           /* Wait for a bit to reduce system load.  */
178           usleep (100 * 1000);
179         }
180     }
181 
182   fclose (fp_empty1);
183   fclose (fp_empty2);
184   fclose (fp_file1);
185   fclose (fp_file2);
186 
187   free (path_dangling);
188   free (path_does_not_exist);
189   free (path_empty1);
190   free (path_empty2);
191   free (path_fifo);
192   free (path_file1);
193   free (path_file2);
194   free (path_loop);
195   free (path_to_empty1);
196   free (path_to_file1);
197 
198   free (tempdir);
199 
200   return 0;
201 }
202 
203 #include <support/test-driver.c>
204