• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Licensed to the Apache Software Foundation (ASF) under one or more
3  * contributor license agreements.  See the NOTICE file distributed with
4  * this work for additional information regarding copyright ownership.
5  * The ASF licenses this file to You under the Apache License, Version 2.0
6  * (the "License"); you may not use this file except in compliance with
7  * the License.  You may obtain a copy of the License at
8  *
9  *      http://www.apache.org/licenses/LICENSE-2.0
10  *
11  * Unless required by applicable law or agreed to in writing, software
12  * distributed under the License is distributed on an "AS IS" BASIS,
13  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14  * See the License for the specific language governing permissions and
15  * limitations under the License.
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  * Tests {@link FileCleaningTracker}.
45  */
46 public class FileCleaningTrackerTest extends AbstractTempDirTest {
47 
48     private File testFile;
49     private Path testPath;
50 
51     private FileCleaningTracker theInstance;
52 
createRandomAccessFile()53     RandomAccessFile createRandomAccessFile() throws FileNotFoundException {
54         return RandomAccessFileMode.READ_WRITE.create(testFile);
55     }
56 
newInstance()57     protected FileCleaningTracker newInstance() {
58         return new FileCleaningTracker();
59     }
60 
pauseForDeleteToComplete(File file)61     private void pauseForDeleteToComplete(File file) {
62         int count = 0;
63         while (file.exists() && count++ < 40) {
64             TestUtils.sleepQuietly(500L);
65             file = new File(file.getPath());
66         }
67     }
68 
pauseForDeleteToComplete(Path file)69     private void pauseForDeleteToComplete(Path file) {
70         int count = 0;
71         while (Files.exists(file) && count++ < 40) {
72             TestUtils.sleepQuietly(500L);
73             file = Paths.get(file.toAbsolutePath().toString());
74         }
75     }
76 
77     @BeforeEach
setUp()78     public void setUp() {
79         testFile = new File(tempDirFile, "file-test.txt");
80         testPath = testFile.toPath();
81         theInstance = newInstance();
82     }
83 
showFailures()84     private String showFailures() {
85         if (theInstance.deleteFailures.size() == 1) {
86             return "[Delete Failed: " + theInstance.deleteFailures.get(0) + "]";
87         }
88         return "[Delete Failures: " + theInstance.deleteFailures.size() + "]";
89     }
90 
91     @AfterEach
tearDown()92     public void tearDown() {
93 
94         // reset file cleaner class, so as not to break other tests
95 
96         /**
97          * The following block of code can possibly be removed when the deprecated {@link FileCleaner} is gone. The
98          * question is, whether we want to support reuse of {@link FileCleaningTracker} instances, which we should, IMO,
99          * not.
100          */
101         {
102             if (theInstance != null) {
103                 theInstance.q = new ReferenceQueue<>();
104                 theInstance.trackers.clear();
105                 theInstance.deleteFailures.clear();
106                 theInstance.exitWhenFinished = false;
107                 theInstance.reaper = null;
108             }
109         }
110 
111         theInstance = null;
112     }
113 
114     @Test
testFileCleanerDirectory_ForceStrategy_FileSource()115     public void testFileCleanerDirectory_ForceStrategy_FileSource() throws Exception {
116         if (!testFile.getParentFile().exists()) {
117             throw new IOException("Cannot create file " + testFile
118                     + " as the parent directory does not exist");
119         }
120         try (BufferedOutputStream output =
121                 new BufferedOutputStream(Files.newOutputStream(testFile.toPath()))) {
122             TestUtils.generateTestData(output, 100);
123         }
124         assertTrue(testFile.exists());
125         assertTrue(tempDirFile.exists());
126 
127         Object obj = new Object();
128         assertEquals(0, theInstance.getTrackCount());
129         theInstance.track(tempDirFile, obj, FileDeleteStrategy.FORCE);
130         assertEquals(1, theInstance.getTrackCount());
131 
132         obj = null;
133 
134         waitUntilTrackCount();
135         pauseForDeleteToComplete(testFile.getParentFile());
136 
137         assertEquals(0, theInstance.getTrackCount());
138         assertFalse(new File(testFile.getPath()).exists(), showFailures());
139         assertFalse(testFile.getParentFile().exists(), showFailures());
140     }
141 
142     @Test
testFileCleanerDirectory_ForceStrategy_PathSource()143     public void testFileCleanerDirectory_ForceStrategy_PathSource() throws Exception {
144         if (!Files.exists(testPath.getParent())) {
145             throw new IOException("Cannot create file " + testPath
146                     + " as the parent directory does not exist");
147         }
148         try (BufferedOutputStream output =
149                 new BufferedOutputStream(Files.newOutputStream(testPath))) {
150             TestUtils.generateTestData(output, 100);
151         }
152         assertTrue(Files.exists(testPath));
153         assertTrue(Files.exists(tempDirPath));
154 
155         Object obj = new Object();
156         assertEquals(0, theInstance.getTrackCount());
157         theInstance.track(tempDirPath, obj, FileDeleteStrategy.FORCE);
158         assertEquals(1, theInstance.getTrackCount());
159 
160         obj = null;
161 
162         waitUntilTrackCount();
163         pauseForDeleteToComplete(testPath.getParent());
164 
165         assertEquals(0, theInstance.getTrackCount());
166         assertFalse(Files.exists(testPath), showFailures());
167         assertFalse(Files.exists(testPath.getParent()), showFailures());
168     }
169 
170     @Test
testFileCleanerDirectory_NullStrategy()171     public void testFileCleanerDirectory_NullStrategy() throws Exception {
172         TestUtils.createFile(testFile, 100);
173         assertTrue(testFile.exists());
174         assertTrue(tempDirFile.exists());
175 
176         Object obj = new Object();
177         assertEquals(0, theInstance.getTrackCount());
178         theInstance.track(tempDirFile, obj, null);
179         assertEquals(1, theInstance.getTrackCount());
180 
181         obj = null;
182 
183         waitUntilTrackCount();
184 
185         assertEquals(0, theInstance.getTrackCount());
186         assertTrue(testFile.exists());  // not deleted, as dir not empty
187         assertTrue(testFile.getParentFile().exists());  // not deleted, as dir not empty
188     }
189 
190     @Test
testFileCleanerDirectoryFileSource()191     public void testFileCleanerDirectoryFileSource() throws Exception {
192         TestUtils.createFile(testFile, 100);
193         assertTrue(testFile.exists());
194         assertTrue(tempDirFile.exists());
195 
196         Object obj = new Object();
197         assertEquals(0, theInstance.getTrackCount());
198         theInstance.track(tempDirFile, obj);
199         assertEquals(1, theInstance.getTrackCount());
200 
201         obj = null;
202 
203         waitUntilTrackCount();
204 
205         assertEquals(0, theInstance.getTrackCount());
206         assertTrue(testFile.exists());  // not deleted, as dir not empty
207         assertTrue(testFile.getParentFile().exists());  // not deleted, as dir not empty
208     }
209 
210     @Test
testFileCleanerDirectoryPathSource()211     public void testFileCleanerDirectoryPathSource() throws Exception {
212         TestUtils.createFile(testPath, 100);
213         assertTrue(Files.exists(testPath));
214         assertTrue(Files.exists(tempDirPath));
215 
216         Object obj = new Object();
217         assertEquals(0, theInstance.getTrackCount());
218         theInstance.track(tempDirPath, obj);
219         assertEquals(1, theInstance.getTrackCount());
220 
221         obj = null;
222 
223         waitUntilTrackCount();
224 
225         assertEquals(0, theInstance.getTrackCount());
226         assertTrue(Files.exists(testPath));  // not deleted, as dir not empty
227         assertTrue(Files.exists(testPath.getParent()));  // not deleted, as dir not empty
228     }
229 
230     @Test
testFileCleanerExitWhenFinished_NoTrackAfter()231     public void testFileCleanerExitWhenFinished_NoTrackAfter() {
232         assertFalse(theInstance.exitWhenFinished);
233         theInstance.exitWhenFinished();
234         assertTrue(theInstance.exitWhenFinished);
235         assertNull(theInstance.reaper);
236 
237         final String path = testFile.getPath();
238         final Object marker = new Object();
239 
240         assertThrows(IllegalStateException.class, () -> theInstance.track(path, marker));
241         assertTrue(theInstance.exitWhenFinished);
242         assertNull(theInstance.reaper);
243     }
244 
245     @Test
testFileCleanerExitWhenFinished1()246     public void testFileCleanerExitWhenFinished1() throws Exception {
247         final String path = testFile.getPath();
248 
249         assertFalse(testFile.exists(), "1-testFile exists: " + testFile);
250 
251         // Do NOT used a try-with-resources statement here or the test will fail.
252         RandomAccessFile raf = createRandomAccessFile();
253         assertTrue(testFile.exists(), "2-testFile exists");
254 
255         assertEquals(0, theInstance.getTrackCount(), "3-Track Count");
256         theInstance.track(path, raf);
257         assertEquals(1, theInstance.getTrackCount(), "4-Track Count");
258         assertFalse(theInstance.exitWhenFinished, "5-exitWhenFinished");
259         assertTrue(theInstance.reaper.isAlive(), "6-reaper.isAlive");
260 
261         assertFalse(theInstance.exitWhenFinished, "7-exitWhenFinished");
262         theInstance.exitWhenFinished();
263         assertTrue(theInstance.exitWhenFinished, "8-exitWhenFinished");
264         assertTrue(theInstance.reaper.isAlive(), "9-reaper.isAlive");
265 
266         raf.close();
267         testFile = null;
268         raf = null;
269 
270         waitUntilTrackCount();
271         pauseForDeleteToComplete(new File(path));
272 
273         assertEquals(0, theInstance.getTrackCount(), "10-Track Count");
274         assertFalse(new File(path).exists(), "11-testFile exists " + showFailures());
275         assertTrue(theInstance.exitWhenFinished, "12-exitWhenFinished");
276         assertFalse(theInstance.reaper.isAlive(), "13-reaper.isAlive");
277     }
278 
279     @Test
testFileCleanerExitWhenFinished2()280     public void testFileCleanerExitWhenFinished2() throws Exception {
281         final String path = testFile.getPath();
282 
283         assertFalse(testFile.exists());
284         RandomAccessFile r = createRandomAccessFile();
285         assertTrue(testFile.exists());
286 
287         assertEquals(0, theInstance.getTrackCount());
288         theInstance.track(path, r);
289         assertEquals(1, theInstance.getTrackCount());
290         assertFalse(theInstance.exitWhenFinished);
291         assertTrue(theInstance.reaper.isAlive());
292 
293         r.close();
294         testFile = null;
295         r = null;
296 
297         waitUntilTrackCount();
298         pauseForDeleteToComplete(new File(path));
299 
300         assertEquals(0, theInstance.getTrackCount());
301         assertFalse(new File(path).exists(), showFailures());
302         assertFalse(theInstance.exitWhenFinished);
303         assertTrue(theInstance.reaper.isAlive());
304 
305         assertFalse(theInstance.exitWhenFinished);
306         theInstance.exitWhenFinished();
307         for (int i = 0; i < 20 && theInstance.reaper.isAlive(); i++) {
308             TestUtils.sleep(500L);  // allow reaper thread to die
309         }
310         assertTrue(theInstance.exitWhenFinished);
311         assertFalse(theInstance.reaper.isAlive());
312     }
313 
314     @Test
testFileCleanerExitWhenFinishedFirst()315     public void testFileCleanerExitWhenFinishedFirst() throws Exception {
316         assertFalse(theInstance.exitWhenFinished);
317         theInstance.exitWhenFinished();
318         assertTrue(theInstance.exitWhenFinished);
319         assertNull(theInstance.reaper);
320 
321         waitUntilTrackCount();
322 
323         assertEquals(0, theInstance.getTrackCount());
324         assertTrue(theInstance.exitWhenFinished);
325         assertNull(theInstance.reaper);
326     }
327 
328     @Test
testFileCleanerFile()329     public void testFileCleanerFile() throws Exception {
330         final String path = testFile.getPath();
331 
332         assertFalse(testFile.exists());
333         RandomAccessFile r = createRandomAccessFile();
334         assertTrue(testFile.exists());
335 
336         assertEquals(0, theInstance.getTrackCount());
337         theInstance.track(path, r);
338         assertEquals(1, theInstance.getTrackCount());
339 
340         r.close();
341         testFile = null;
342         r = null;
343 
344         waitUntilTrackCount();
345         pauseForDeleteToComplete(new File(path));
346 
347         assertEquals(0, theInstance.getTrackCount());
348         assertFalse(new File(path).exists(), showFailures());
349     }
350     @Test
testFileCleanerNull()351     public void testFileCleanerNull() {
352         assertThrows(NullPointerException.class, () -> theInstance.track((File) null, new Object()));
353         assertThrows(NullPointerException.class, () -> theInstance.track((File) null, new Object(), FileDeleteStrategy.NORMAL));
354         assertThrows(NullPointerException.class, () -> theInstance.track((String) null, new Object()));
355         assertThrows(NullPointerException.class, () -> theInstance.track((String) null, new Object(), FileDeleteStrategy.NORMAL));
356     }
357 
waitUntilTrackCount()358     private void waitUntilTrackCount() throws Exception {
359         System.gc();
360         TestUtils.sleep(500);
361         int count = 0;
362         while (theInstance.getTrackCount() != 0 && count++ < 5) {
363             List<String> list = new ArrayList<>();
364             try {
365                 long i = 0;
366                 while (theInstance.getTrackCount() != 0) {
367                     list.add(
368                         "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 "
369                             + i++);
370                 }
371             } catch (final Throwable ignored) {
372             }
373             list = null;
374             System.gc();
375             TestUtils.sleep(1000);
376         }
377         if (theInstance.getTrackCount() != 0) {
378             throw new IllegalStateException("Your JVM is not releasing References, try running the test with less memory (-Xmx)");
379         }
380 
381     }
382 }
383