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.assertNull;
22 import static org.junit.jupiter.api.Assertions.assertThrows;
23 import static org.junit.jupiter.api.Assertions.assertTrue;
24
25 import java.io.BufferedOutputStream;
26 import java.io.File;
27 import java.io.FileNotFoundException;
28 import java.io.IOException;
29 import java.io.RandomAccessFile;
30 import java.lang.ref.ReferenceQueue;
31 import java.nio.file.Files;
32 import java.nio.file.Path;
33 import java.nio.file.Paths;
34 import java.util.ArrayList;
35 import java.util.List;
36
37 import org.apache.commons.io.file.AbstractTempDirTest;
38 import org.apache.commons.io.test.TestUtils;
39 import org.junit.jupiter.api.AfterEach;
40 import org.junit.jupiter.api.BeforeEach;
41 import org.junit.jupiter.api.Test;
42
43
44
45
46 class FileCleaningTrackerTest extends AbstractTempDirTest {
47
48 private File testFile;
49 private Path testPath;
50
51 private FileCleaningTracker fileCleaningTracker;
52
53 RandomAccessFile createRandomAccessFile() throws FileNotFoundException {
54 return RandomAccessFileMode.READ_WRITE.create(testFile);
55 }
56
57 private void gcFinalize() {
58 System.gc();
59 System.runFinalization();
60 }
61
62 protected FileCleaningTracker newInstance() {
63 return new FileCleaningTracker();
64 }
65
66 private void pauseForDeleteToComplete(File file) {
67 int count = 0;
68 while (file.exists() && count++ < 40) {
69 TestUtils.sleepQuietly(500L);
70 file = new File(file.getPath());
71 }
72 }
73
74 private void pauseForDeleteToComplete(Path file) {
75 int count = 0;
76 while (Files.exists(file) && count++ < 40) {
77 TestUtils.sleepQuietly(500L);
78 file = Paths.get(file.toAbsolutePath().toString());
79 }
80 }
81
82 @BeforeEach
83 public void setUp() {
84 testFile = new File(tempDirFile, "file-test.txt");
85 testPath = testFile.toPath();
86 fileCleaningTracker = newInstance();
87 }
88
89 private String showFailures() {
90 if (fileCleaningTracker.deleteFailures.size() == 1) {
91 return "[Delete Failed: " + fileCleaningTracker.deleteFailures.get(0) + "]";
92 }
93 return "[Delete Failures: " + fileCleaningTracker.deleteFailures.size() + "]";
94 }
95
96 @AfterEach
97 public void tearDown() {
98
99
100
101
102
103 {
104 if (fileCleaningTracker != null) {
105 if (fileCleaningTracker.reaper != null) {
106 fileCleaningTracker.reaper.interrupt();
107 }
108 fileCleaningTracker.refQueue = new ReferenceQueue<>();
109 fileCleaningTracker.trackers.clear();
110 fileCleaningTracker.deleteFailures.clear();
111 fileCleaningTracker.exitWhenFinished = false;
112 fileCleaningTracker.reaper = null;
113 }
114 }
115 fileCleaningTracker = null;
116 }
117
118 @Test
119 void testFileCleanerDirectory_ForceStrategy_FileSource() throws Exception {
120 if (!testFile.getParentFile().exists()) {
121 throw new IOException("Cannot create file " + testFile + " as the parent directory does not exist");
122 }
123 try (BufferedOutputStream output =
124 new BufferedOutputStream(Files.newOutputStream(testFile.toPath()))) {
125 TestUtils.generateTestData(output, 100);
126 }
127 assertTrue(testFile.exists());
128 assertTrue(tempDirFile.exists());
129
130 Object obj = new Object();
131 assertEquals(0, fileCleaningTracker.getTrackCount());
132 fileCleaningTracker.track(tempDirFile, obj, FileDeleteStrategy.FORCE);
133 assertEquals(1, fileCleaningTracker.getTrackCount());
134
135 obj = null;
136
137 waitUntilTrackCount0();
138 pauseForDeleteToComplete(testFile.getParentFile());
139
140 assertEquals(0, fileCleaningTracker.getTrackCount());
141 assertFalse(new File(testFile.getPath()).exists(), showFailures());
142 assertFalse(testFile.getParentFile().exists(), showFailures());
143 }
144
145 @Test
146 void testFileCleanerDirectory_ForceStrategy_PathSource() throws Exception {
147 if (!Files.exists(testPath.getParent())) {
148 throw new IOException("Cannot create file " + testPath + " as the parent directory does not exist");
149 }
150 try (BufferedOutputStream output =
151 new BufferedOutputStream(Files.newOutputStream(testPath))) {
152 TestUtils.generateTestData(output, 100);
153 }
154 assertTrue(Files.exists(testPath));
155 assertTrue(Files.exists(tempDirPath));
156
157 Object obj = new Object();
158 assertEquals(0, fileCleaningTracker.getTrackCount());
159 fileCleaningTracker.track(tempDirPath, obj, FileDeleteStrategy.FORCE);
160 assertEquals(1, fileCleaningTracker.getTrackCount());
161
162 obj = null;
163
164 waitUntilTrackCount0();
165 pauseForDeleteToComplete(testPath.getParent());
166
167 assertEquals(0, fileCleaningTracker.getTrackCount());
168 assertFalse(Files.exists(testPath), showFailures());
169 assertFalse(Files.exists(testPath.getParent()), showFailures());
170 }
171
172 @Test
173 void testFileCleanerDirectory_NullStrategy() throws Exception {
174 TestUtils.createFile(testFile, 100);
175 assertTrue(testFile.exists());
176 assertTrue(tempDirFile.exists());
177
178 Object obj = new Object();
179 assertEquals(0, fileCleaningTracker.getTrackCount());
180 fileCleaningTracker.track(tempDirFile, obj, null);
181 assertEquals(1, fileCleaningTracker.getTrackCount());
182
183 obj = null;
184
185 waitUntilTrackCount0();
186
187 assertEquals(0, fileCleaningTracker.getTrackCount());
188 assertTrue(testFile.exists());
189 assertTrue(testFile.getParentFile().exists());
190 }
191
192 @Test
193 void testFileCleanerDirectoryFileSource() throws Exception {
194 TestUtils.createFile(testFile, 100);
195 assertTrue(testFile.exists());
196 assertTrue(tempDirFile.exists());
197
198 Object obj = new Object();
199 assertEquals(0, fileCleaningTracker.getTrackCount());
200 fileCleaningTracker.track(tempDirFile, obj);
201 assertEquals(1, fileCleaningTracker.getTrackCount());
202
203 obj = null;
204
205 waitUntilTrackCount0();
206
207 assertEquals(0, fileCleaningTracker.getTrackCount());
208 assertTrue(testFile.exists());
209 assertTrue(testFile.getParentFile().exists());
210 }
211
212 @Test
213 void testFileCleanerDirectoryPathSource() throws Exception {
214 TestUtils.createFile(testPath, 100);
215 assertTrue(Files.exists(testPath));
216 assertTrue(Files.exists(tempDirPath));
217
218 Object obj = new Object();
219 assertEquals(0, fileCleaningTracker.getTrackCount());
220 fileCleaningTracker.track(tempDirPath, obj);
221 assertEquals(1, fileCleaningTracker.getTrackCount());
222
223 obj = null;
224
225 waitUntilTrackCount0();
226
227 assertEquals(0, fileCleaningTracker.getTrackCount());
228 assertTrue(Files.exists(testPath));
229 assertTrue(Files.exists(testPath.getParent()));
230 }
231
232 @Test
233 void testFileCleanerExitWhenFinished_NoTrackAfter() {
234 assertFalse(fileCleaningTracker.exitWhenFinished);
235 fileCleaningTracker.exitWhenFinished();
236 assertTrue(fileCleaningTracker.exitWhenFinished);
237 assertNull(fileCleaningTracker.reaper);
238
239 final String path = testFile.getPath();
240 final Object marker = new Object();
241
242 assertThrows(IllegalStateException.class, () -> fileCleaningTracker.track(path, marker));
243 assertTrue(fileCleaningTracker.exitWhenFinished);
244 assertNull(fileCleaningTracker.reaper);
245 }
246
247 @Test
248 void testFileCleanerExitWhenFinished1() throws Exception {
249 final String path = testFile.getPath();
250
251 assertFalse(testFile.exists(), "1-testFile exists: " + testFile);
252
253
254 RandomAccessFile raf = createRandomAccessFile();
255 assertTrue(testFile.exists(), "2-testFile exists");
256
257 assertEquals(0, fileCleaningTracker.getTrackCount(), "3-Track Count");
258 fileCleaningTracker.track(path, raf);
259 assertEquals(1, fileCleaningTracker.getTrackCount(), "4-Track Count");
260 assertFalse(fileCleaningTracker.exitWhenFinished, "5-exitWhenFinished");
261 assertTrue(fileCleaningTracker.reaper.isAlive(), "6-reaper.isAlive");
262
263 assertFalse(fileCleaningTracker.exitWhenFinished, "7-exitWhenFinished");
264 fileCleaningTracker.exitWhenFinished();
265 assertTrue(fileCleaningTracker.exitWhenFinished, "8-exitWhenFinished");
266 assertTrue(fileCleaningTracker.reaper.isAlive(), "9-reaper.isAlive");
267
268 raf.close();
269 testFile = null;
270 raf = null;
271
272 waitUntilTrackCount0();
273 pauseForDeleteToComplete(new File(path));
274
275 assertEquals(0, fileCleaningTracker.getTrackCount(), "10-Track Count");
276 assertFalse(new File(path).exists(), "11-testFile exists " + showFailures());
277 assertTrue(fileCleaningTracker.exitWhenFinished, "12-exitWhenFinished");
278 assertFalse(fileCleaningTracker.reaper.isAlive(), "13-reaper.isAlive");
279 }
280
281 @Test
282 void testFileCleanerExitWhenFinished2() throws Exception {
283 final String path = testFile.getPath();
284
285 assertFalse(testFile.exists());
286 RandomAccessFile raf = createRandomAccessFile();
287 assertTrue(testFile.exists());
288
289 assertEquals(0, fileCleaningTracker.getTrackCount());
290 fileCleaningTracker.track(path, raf);
291 assertEquals(1, fileCleaningTracker.getTrackCount());
292 assertFalse(fileCleaningTracker.exitWhenFinished);
293 assertTrue(fileCleaningTracker.reaper.isAlive());
294
295 raf.close();
296 testFile = null;
297 raf = null;
298
299 waitUntilTrackCount0();
300 pauseForDeleteToComplete(new File(path));
301
302 assertEquals(0, fileCleaningTracker.getTrackCount());
303 assertFalse(new File(path).exists(), showFailures());
304 assertFalse(fileCleaningTracker.exitWhenFinished);
305 assertTrue(fileCleaningTracker.reaper.isAlive());
306
307 assertFalse(fileCleaningTracker.exitWhenFinished);
308 fileCleaningTracker.exitWhenFinished();
309 for (int i = 0; i < 20 && fileCleaningTracker.reaper.isAlive(); i++) {
310 TestUtils.sleep(500L);
311 }
312 assertTrue(fileCleaningTracker.exitWhenFinished);
313 assertFalse(fileCleaningTracker.reaper.isAlive());
314 assertFalse(Files.exists(Paths.get(path)));
315 }
316
317 @Test
318 void testFileCleanerExitWhenFinishedFirst() throws Exception {
319 assertFalse(fileCleaningTracker.exitWhenFinished);
320 fileCleaningTracker.exitWhenFinished();
321 assertTrue(fileCleaningTracker.exitWhenFinished);
322 assertNull(fileCleaningTracker.reaper);
323
324 waitUntilTrackCount0();
325
326 assertEquals(0, fileCleaningTracker.getTrackCount());
327 assertTrue(fileCleaningTracker.exitWhenFinished);
328 assertNull(fileCleaningTracker.reaper);
329 }
330
331 @Test
332 void testFileCleanerFile() throws Exception {
333 final String path = testFile.getPath();
334
335 assertFalse(testFile.exists());
336 RandomAccessFile raf = createRandomAccessFile();
337 assertTrue(testFile.exists());
338
339 assertEquals(0, fileCleaningTracker.getTrackCount());
340 fileCleaningTracker.track(path, raf);
341 assertEquals(1, fileCleaningTracker.getTrackCount());
342
343 raf.close();
344 testFile = null;
345 raf = null;
346
347 waitUntilTrackCount0();
348 pauseForDeleteToComplete(new File(path));
349
350 assertEquals(0, fileCleaningTracker.getTrackCount());
351 assertFalse(new File(path).exists(), showFailures());
352 }
353
354 @Test
355 void testFileCleanerNull() {
356 assertThrows(NullPointerException.class, () -> fileCleaningTracker.track((File) null, new Object()));
357 assertThrows(NullPointerException.class, () -> fileCleaningTracker.track((File) null, new Object(), FileDeleteStrategy.NORMAL));
358 assertThrows(NullPointerException.class, () -> fileCleaningTracker.track((String) null, new Object()));
359 assertThrows(NullPointerException.class, () -> fileCleaningTracker.track((String) null, new Object(), FileDeleteStrategy.NORMAL));
360 }
361
362
363 private void waitUntilTrackCount0() throws Exception {
364 System.gc();
365 TestUtils.sleep(500);
366 int count = 0;
367 while (fileCleaningTracker.getTrackCount() != 0 && count++ < 5) {
368 List<String> list = new ArrayList<>();
369 try {
370 long i = 0;
371 while (fileCleaningTracker.getTrackCount() != 0) {
372 list.add(
373 "A Big String A Big String A Big String A Big String A Big String A Big String A Big String A Big String A Big String A Big String "
374 + i++);
375 }
376 } catch (final Throwable ignored) {
377 }
378 list = null;
379 gcFinalize();
380 TestUtils.sleep(1000);
381 }
382 if (fileCleaningTracker.getTrackCount() != 0) {
383 throw new IllegalStateException("Your JVM is not releasing References, try running the test with less memory (-Xmx)");
384 }
385 }
386 }