1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package org.apache.commons.io;
18
19 import static org.junit.jupiter.api.Assertions.assertEquals;
20 import static org.junit.jupiter.api.Assertions.assertFalse;
21 import static org.junit.jupiter.api.Assertions.assertNotNull;
22 import static org.junit.jupiter.api.Assertions.assertThrows;
23 import static org.junit.jupiter.api.Assertions.assertTrue;
24 import static org.junit.jupiter.api.Assertions.fail;
25 import static org.junit.jupiter.api.Assumptions.assumeFalse;
26
27 import java.io.File;
28 import java.io.IOException;
29 import java.nio.charset.StandardCharsets;
30 import java.nio.file.Files;
31 import java.nio.file.Path;
32 import java.util.ArrayList;
33 import java.util.Collection;
34 import java.util.Iterator;
35 import java.util.List;
36 import java.util.UUID;
37 import java.util.concurrent.CompletableFuture;
38 import java.util.concurrent.ExecutionException;
39 import java.util.stream.Collectors;
40 import java.util.stream.Stream;
41
42 import org.apache.commons.io.file.PathUtils;
43 import org.apache.commons.io.filefilter.FileFilterUtils;
44 import org.apache.commons.io.filefilter.IOFileFilter;
45 import org.apache.commons.lang3.JavaVersion;
46 import org.apache.commons.lang3.SystemUtils;
47 import org.apache.commons.lang3.function.Consumers;
48 import org.junit.jupiter.api.BeforeEach;
49 import org.junit.jupiter.api.Test;
50 import org.junit.jupiter.api.io.TempDir;
51
52
53
54
55 class FileUtilsListFilesTest {
56
57 @TempDir
58 public File temporaryFolder;
59
60 @BeforeEach
61 public void setUp() throws Exception {
62 File dir = temporaryFolder;
63 File file = new File(dir, "dummy-build.xml");
64 FileUtils.touch(file);
65 file = new File(dir, "README");
66 FileUtils.touch(file);
67
68 dir = new File(dir, "subdir1");
69 dir.mkdirs();
70 file = new File(dir, "dummy-build.xml");
71 FileUtils.touch(file);
72 file = new File(dir, "dummy-readme.txt");
73 FileUtils.touch(file);
74
75 dir = new File(dir, "subsubdir1");
76 dir.mkdirs();
77 file = new File(dir, "dummy-file.txt");
78 FileUtils.touch(file);
79 file = new File(dir, "dummy-index.html");
80 FileUtils.touch(file);
81 file = new File(dir, "dummy-indexhtml");
82 FileUtils.touch(file);
83
84 dir = dir.getParentFile();
85 dir = new File(dir, "CVS");
86 dir.mkdirs();
87 file = new File(dir, "Entries");
88 FileUtils.touch(file);
89 file = new File(dir, "Repository");
90 FileUtils.touch(file);
91 }
92
93 @Test
94 void testIterateFilesByExtension() {
95 final String[] extensions = { "xml", "txt" };
96
97 Iterator<File> files = FileUtils.iterateFiles(temporaryFolder, extensions, false);
98 try {
99 final Collection<String> fileNames = toFileNames(files);
100 assertEquals(1, fileNames.size());
101 assertTrue(fileNames.contains("dummy-build.xml"));
102 assertFalse(fileNames.contains("README"));
103 assertFalse(fileNames.contains("dummy-file.txt"));
104 } finally {
105
106 files.forEachRemaining(Consumers.nop());
107 }
108
109 try {
110 files = FileUtils.iterateFiles(temporaryFolder, extensions, true);
111 final Collection<String> fileNames = toFileNames(files);
112 assertEquals(4, fileNames.size());
113 assertTrue(fileNames.contains("dummy-file.txt"));
114 assertFalse(fileNames.contains("dummy-index.html"));
115 } finally {
116
117 files.forEachRemaining(Consumers.nop());
118 }
119
120 files = FileUtils.iterateFiles(temporaryFolder, null, false);
121 try {
122 final Collection<String> fileNames = toFileNames(files);
123 assertEquals(2, fileNames.size());
124 assertTrue(fileNames.contains("dummy-build.xml"));
125 assertTrue(fileNames.contains("README"));
126 assertFalse(fileNames.contains("dummy-file.txt"));
127 } finally {
128
129 files.forEachRemaining(Consumers.nop());
130 }
131 }
132
133 @Test
134 void testListFiles() {
135 Collection<File> files;
136 Collection<String> fileNames;
137 IOFileFilter fileFilter;
138 IOFileFilter dirFilter;
139
140
141 fileFilter = FileFilterUtils.trueFileFilter();
142 files = FileUtils.listFiles(temporaryFolder, fileFilter, null);
143 fileNames = toFileNames(files);
144 assertTrue(fileNames.contains("dummy-build.xml"), "'dummy-build.xml' is missing");
145 assertFalse(fileNames.contains("dummy-index.html"), "'dummy-index.html' shouldn't be found");
146 assertFalse(fileNames.contains("Entries"), "'Entries' shouldn't be found");
147
148
149 fileFilter = FileFilterUtils.trueFileFilter();
150 dirFilter = FileFilterUtils.notFileFilter(FileFilterUtils.nameFileFilter("CVS"));
151 files = FileUtils.listFiles(temporaryFolder, fileFilter, dirFilter);
152 fileNames = toFileNames(files);
153 assertTrue(fileNames.contains("dummy-build.xml"), "'dummy-build.xml' is missing");
154 assertTrue(fileNames.contains("dummy-index.html"), "'dummy-index.html' is missing");
155 assertFalse(fileNames.contains("Entries"), "'Entries' shouldn't be found");
156
157
158 fileFilter = FileFilterUtils.trueFileFilter();
159 dirFilter = FileFilterUtils.makeCVSAware(null);
160 files = FileUtils.listFiles(temporaryFolder, fileFilter, dirFilter);
161 fileNames = toFileNames(files);
162 assertTrue(fileNames.contains("dummy-build.xml"), "'dummy-build.xml' is missing");
163 assertTrue(fileNames.contains("dummy-index.html"), "'dummy-index.html' is missing");
164 assertFalse(fileNames.contains("Entries"), "'Entries' shouldn't be found");
165
166
167 fileFilter = FileFilterUtils.trueFileFilter();
168 dirFilter = FileFilterUtils.prefixFileFilter("sub");
169 dirFilter = FileFilterUtils.makeCVSAware(dirFilter);
170 files = FileUtils.listFiles(temporaryFolder, fileFilter, dirFilter);
171 fileNames = toFileNames(files);
172 assertTrue(fileNames.contains("dummy-build.xml"), "'dummy-build.xml' is missing");
173 assertTrue(fileNames.contains("dummy-index.html"), "'dummy-index.html' is missing");
174 assertFalse(fileNames.contains("Entries"), "'Entries' shouldn't be found");
175
176 assertThrows(NullPointerException.class, () -> FileUtils.listFiles(temporaryFolder, null, null));
177 }
178
179 @Test
180 void testListFilesByExtension() {
181 final String[] extensions = {"xml", "txt"};
182
183 Collection<File> files = FileUtils.listFiles(temporaryFolder, extensions, false);
184 assertEquals(1, files.size());
185 Collection<String> fileNames = toFileNames(files);
186 assertTrue(fileNames.contains("dummy-build.xml"));
187 assertFalse(fileNames.contains("README"));
188 assertFalse(fileNames.contains("dummy-file.txt"));
189
190 files = FileUtils.listFiles(temporaryFolder, extensions, true);
191 fileNames = toFileNames(files);
192 assertEquals(4, fileNames.size(), fileNames::toString);
193 assertTrue(fileNames.contains("dummy-file.txt"));
194 assertFalse(fileNames.contains("dummy-index.html"));
195
196 files = FileUtils.listFiles(temporaryFolder, null, false);
197 assertEquals(2, files.size(), files::toString);
198 fileNames = toFileNames(files);
199 assertTrue(fileNames.contains("dummy-build.xml"));
200 assertTrue(fileNames.contains("README"));
201 assertFalse(fileNames.contains("dummy-file.txt"));
202
203 final File directory = new File(temporaryFolder, "subdir1/subsubdir1");
204 files = FileUtils.listFiles(directory, new String[] { "html" }, false);
205 fileNames = toFileNames(files);
206 assertFalse(files.isEmpty(), directory::toString);
207 assertTrue(fileNames.contains("dummy-index.html"));
208 assertFalse(fileNames.contains("dummy-indexhtml"));
209 files = FileUtils.listFiles(temporaryFolder, new String[] { "html" }, true);
210 fileNames = toFileNames(files);
211 assertFalse(files.isEmpty(), temporaryFolder::toString);
212 assertTrue(fileNames.contains("dummy-index.html"));
213 assertFalse(fileNames.contains("dummy-indexhtml"));
214 }
215
216 @Test
217 void testListFilesMissing() {
218 assertTrue(FileUtils.listFiles(new File(temporaryFolder, "dir/does/not/exist/at/all"), null, false).isEmpty());
219 }
220
221
222
223
224 @Test
225 void testListFilesWithDeletionThreaded() throws ExecutionException, InterruptedException {
226
227
228 final Path tempPath = PathUtils.getTempDirectory().resolve("IO-856");
229 final File tempDir = tempPath.toFile();
230 if (!tempDir.exists() && !tempDir.mkdirs()) {
231 fail("Could not create file path: " + tempDir.getAbsolutePath());
232 }
233 final int waitTime = 10_000;
234 final int maxFiles = 500;
235 final byte[] bytes = "TEST".getBytes(StandardCharsets.UTF_8);
236 final CompletableFuture<Void> c1 = CompletableFuture.runAsync(() -> {
237 final long endTime = System.currentTimeMillis() + waitTime;
238 int count = 0;
239 while (System.currentTimeMillis() < endTime && count < maxFiles) {
240 final File file = new File(tempDir.getAbsolutePath(), UUID.randomUUID() + ".deletetester");
241 file.deleteOnExit();
242 try {
243 Files.write(file.toPath(), bytes);
244 count++;
245 } catch (final Exception e) {
246 fail("Could not create test file: '" + file.getAbsolutePath() + "': " + e, e);
247 }
248 if (!file.delete()) {
249 fail("Could not delete test file: '" + file.getAbsolutePath() + "'");
250 }
251 }
252
253 });
254 final CompletableFuture<Void> c2 = CompletableFuture.runAsync(() -> {
255 final long endTime = System.currentTimeMillis() + waitTime;
256 int max = 0;
257 try {
258 while (System.currentTimeMillis() < endTime) {
259 final Collection<File> files = FileUtils.listFiles(tempDir, new String[] { ".deletetester" }, false);
260 assertNotNull(files);
261 max = Math.max(max, files.size());
262 }
263 } catch (final Exception e) {
264 System.out.printf("List size max %,d%n", max);
265 fail("IO-856 test failure: " + e, e);
266
267 e.printStackTrace();
268 }
269
270 });
271
272 c1.get();
273 c2.get();
274 }
275
276 @Test
277 void testStreamFilesWithDeletionCollect() throws IOException {
278 final String[] extensions = {"xml", "txt"};
279 final File xFile = new File(temporaryFolder, "x.xml");
280 if (!xFile.createNewFile()) {
281 fail("could not create test file: " + xFile);
282 }
283 final Collection<File> files = FileUtils.listFiles(temporaryFolder, extensions, true);
284 assertEquals(5, files.size());
285 final List<File> list;
286 try (Stream<File> stream = FileUtils.streamFiles(temporaryFolder, true, extensions)) {
287 assertTrue(xFile.delete());
288
289
290
291
292
293
294 assumeFalse(SystemUtils.isJavaVersionAtLeast(JavaVersion.JAVA_24));
295 list = stream.collect(Collectors.toList());
296 assertFalse(list.contains(xFile), list::toString);
297 }
298 assertEquals(4, list.size());
299 }
300
301 @Test
302 void testStreamFilesWithDeletionForEach() throws IOException {
303 final String[] extensions = {"xml", "txt"};
304 final File xFile = new File(temporaryFolder, "x.xml");
305 if (!xFile.createNewFile()) {
306 fail("could not create test file: " + xFile);
307 }
308 final Collection<File> files = FileUtils.listFiles(temporaryFolder, extensions, true);
309 assertEquals(5, files.size());
310 final List<File> list;
311 try (Stream<File> stream = FileUtils.streamFiles(temporaryFolder, true, extensions)) {
312 assertTrue(xFile.delete());
313 list = new ArrayList<>();
314
315
316
317
318
319
320 assumeFalse(SystemUtils.isJavaVersionAtLeast(JavaVersion.JAVA_24));
321 stream.forEach(list::add);
322 assertFalse(list.contains(xFile), list::toString);
323 }
324 assertEquals(4, list.size());
325 }
326
327 @Test
328 void testStreamFilesWithDeletionIterator() throws IOException {
329 final String[] extensions = {"xml", "txt"};
330 final File xFile = new File(temporaryFolder, "x.xml");
331 if (!xFile.createNewFile()) {
332 fail("could not create test file: " + xFile);
333 }
334 final Collection<File> files = FileUtils.listFiles(temporaryFolder, extensions, true);
335 assertEquals(5, files.size());
336 final List<File> list;
337 try (Stream<File> stream = FileUtils.streamFiles(temporaryFolder, true, extensions)) {
338 assertTrue(xFile.delete());
339 list = new ArrayList<>();
340 final Iterator<File> iterator = stream.iterator();
341
342
343
344
345
346
347 assumeFalse(SystemUtils.isJavaVersionAtLeast(JavaVersion.JAVA_24));
348 while (iterator.hasNext()) {
349 list.add(iterator.next());
350 }
351 assertFalse(list.contains(xFile), list::toString);
352 }
353 assertEquals(4, list.size());
354 }
355
356 private Collection<String> toFileNames(final Collection<File> files) {
357 return files.stream().map(File::getName).collect(Collectors.toList());
358 }
359
360
361
362
363
364
365
366 private Collection<String> toFileNames(final Iterator<File> files) {
367 final Collection<String> fileNames = new ArrayList<>();
368
369 files.forEachRemaining(f -> fileNames.add(f.getName()));
370 return fileNames;
371 }
372
373 }