• 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.util.ArrayList;
33 import java.util.List;
34 
35 import org.apache.commons.io.file.AbstractTempDirTest;
36 import org.apache.commons.io.test.TestUtils;
37 import org.junit.jupiter.api.AfterEach;
38 import org.junit.jupiter.api.BeforeEach;
39 import org.junit.jupiter.api.Test;
40 
41 /**
42  * This is used to test {@link FileCleaningTracker} for correctness.
43  *
44  * @see FileCleaningTracker
45  */
46 public class FileCleaningTrackerTest extends AbstractTempDirTest {
47 
48     private File testFile;
49 
50     private FileCleaningTracker theInstance;
51 
createRandomAccessFile()52     RandomAccessFile createRandomAccessFile() throws FileNotFoundException {
53         return RandomAccessFileMode.READ_WRITE.create(testFile);
54     }
55 
newInstance()56     protected FileCleaningTracker newInstance() {
57         return new FileCleaningTracker();
58     }
59 
pauseForDeleteToComplete(File file)60     private void pauseForDeleteToComplete(File file) {
61         int count = 0;
62         while (file.exists() && count++ < 40) {
63             TestUtils.sleepQuietly(500L);
64             file = new File(file.getPath());
65         }
66     }
67 
68     @BeforeEach
setUp()69     public void setUp() {
70         testFile = new File(tempDirFile, "file-test.txt");
71         theInstance = newInstance();
72     }
73 
showFailures()74     private String showFailures() {
75         if (theInstance.deleteFailures.size() == 1) {
76             return "[Delete Failed: " + theInstance.deleteFailures.get(0) + "]";
77         }
78         return "[Delete Failures: " + theInstance.deleteFailures.size() + "]";
79     }
80 
81     @AfterEach
tearDown()82     public void tearDown() {
83 
84         // reset file cleaner class, so as not to break other tests
85 
86         /**
87          * The following block of code can possibly be removed when the deprecated {@link FileCleaner} is gone. The
88          * question is, whether we want to support reuse of {@link FileCleaningTracker} instances, which we should, IMO,
89          * not.
90          */
91         {
92             if (theInstance != null) {
93                 theInstance.q = new ReferenceQueue<>();
94                 theInstance.trackers.clear();
95                 theInstance.deleteFailures.clear();
96                 theInstance.exitWhenFinished = false;
97                 theInstance.reaper = null;
98             }
99         }
100 
101         theInstance = null;
102     }
103 
104     @Test
testFileCleanerDirectory()105     public void testFileCleanerDirectory() throws Exception {
106         TestUtils.createFile(testFile, 100);
107         assertTrue(testFile.exists());
108         assertTrue(tempDirFile.exists());
109 
110         Object obj = new Object();
111         assertEquals(0, theInstance.getTrackCount());
112         theInstance.track(tempDirFile, obj);
113         assertEquals(1, theInstance.getTrackCount());
114 
115         obj = null;
116 
117         waitUntilTrackCount();
118 
119         assertEquals(0, theInstance.getTrackCount());
120         assertTrue(testFile.exists());  // not deleted, as dir not empty
121         assertTrue(testFile.getParentFile().exists());  // not deleted, as dir not empty
122     }
123 
124     @Test
testFileCleanerDirectory_ForceStrategy()125     public void testFileCleanerDirectory_ForceStrategy() throws Exception {
126         if (!testFile.getParentFile().exists()) {
127             throw new IOException("Cannot create file " + testFile
128                     + " as the parent directory does not exist");
129         }
130         try (BufferedOutputStream output =
131                 new BufferedOutputStream(Files.newOutputStream(testFile.toPath()))) {
132             TestUtils.generateTestData(output, 100);
133         }
134         assertTrue(testFile.exists());
135         assertTrue(tempDirFile.exists());
136 
137         Object obj = new Object();
138         assertEquals(0, theInstance.getTrackCount());
139         theInstance.track(tempDirFile, obj, FileDeleteStrategy.FORCE);
140         assertEquals(1, theInstance.getTrackCount());
141 
142         obj = null;
143 
144         waitUntilTrackCount();
145         pauseForDeleteToComplete(testFile.getParentFile());
146 
147         assertEquals(0, theInstance.getTrackCount());
148         assertFalse(new File(testFile.getPath()).exists(), showFailures());
149         assertFalse(testFile.getParentFile().exists(), showFailures());
150     }
151 
152     @Test
testFileCleanerDirectory_NullStrategy()153     public void testFileCleanerDirectory_NullStrategy() throws Exception {
154         TestUtils.createFile(testFile, 100);
155         assertTrue(testFile.exists());
156         assertTrue(tempDirFile.exists());
157 
158         Object obj = new Object();
159         assertEquals(0, theInstance.getTrackCount());
160         theInstance.track(tempDirFile, obj, null);
161         assertEquals(1, theInstance.getTrackCount());
162 
163         obj = null;
164 
165         waitUntilTrackCount();
166 
167         assertEquals(0, theInstance.getTrackCount());
168         assertTrue(testFile.exists());  // not deleted, as dir not empty
169         assertTrue(testFile.getParentFile().exists());  // not deleted, as dir not empty
170     }
171 
172     @Test
testFileCleanerExitWhenFinished_NoTrackAfter()173     public void testFileCleanerExitWhenFinished_NoTrackAfter() {
174         assertFalse(theInstance.exitWhenFinished);
175         theInstance.exitWhenFinished();
176         assertTrue(theInstance.exitWhenFinished);
177         assertNull(theInstance.reaper);
178 
179         final String path = testFile.getPath();
180         final Object marker = new Object();
181 
182         assertThrows(IllegalStateException.class, () -> theInstance.track(path, marker));
183         assertTrue(theInstance.exitWhenFinished);
184         assertNull(theInstance.reaper);
185     }
186 
187     @Test
testFileCleanerExitWhenFinished1()188     public void testFileCleanerExitWhenFinished1() throws Exception {
189         final String path = testFile.getPath();
190 
191         assertFalse(testFile.exists(), "1-testFile exists: " + testFile);
192         RandomAccessFile r = createRandomAccessFile();
193         assertTrue(testFile.exists(), "2-testFile exists");
194 
195         assertEquals(0, theInstance.getTrackCount(), "3-Track Count");
196         theInstance.track(path, r);
197         assertEquals(1, theInstance.getTrackCount(), "4-Track Count");
198         assertFalse(theInstance.exitWhenFinished, "5-exitWhenFinished");
199         assertTrue(theInstance.reaper.isAlive(), "6-reaper.isAlive");
200 
201         assertFalse(theInstance.exitWhenFinished, "7-exitWhenFinished");
202         theInstance.exitWhenFinished();
203         assertTrue(theInstance.exitWhenFinished, "8-exitWhenFinished");
204         assertTrue(theInstance.reaper.isAlive(), "9-reaper.isAlive");
205 
206         r.close();
207         testFile = null;
208         r = null;
209 
210         waitUntilTrackCount();
211         pauseForDeleteToComplete(new File(path));
212 
213         assertEquals(0, theInstance.getTrackCount(), "10-Track Count");
214         assertFalse(new File(path).exists(), "11-testFile exists " + showFailures());
215         assertTrue(theInstance.exitWhenFinished, "12-exitWhenFinished");
216         assertFalse(theInstance.reaper.isAlive(), "13-reaper.isAlive");
217     }
218 
219     @Test
testFileCleanerExitWhenFinished2()220     public void testFileCleanerExitWhenFinished2() throws Exception {
221         final String path = testFile.getPath();
222 
223         assertFalse(testFile.exists());
224         RandomAccessFile r = createRandomAccessFile();
225         assertTrue(testFile.exists());
226 
227         assertEquals(0, theInstance.getTrackCount());
228         theInstance.track(path, r);
229         assertEquals(1, theInstance.getTrackCount());
230         assertFalse(theInstance.exitWhenFinished);
231         assertTrue(theInstance.reaper.isAlive());
232 
233         r.close();
234         testFile = null;
235         r = null;
236 
237         waitUntilTrackCount();
238         pauseForDeleteToComplete(new File(path));
239 
240         assertEquals(0, theInstance.getTrackCount());
241         assertFalse(new File(path).exists(), showFailures());
242         assertFalse(theInstance.exitWhenFinished);
243         assertTrue(theInstance.reaper.isAlive());
244 
245         assertFalse(theInstance.exitWhenFinished);
246         theInstance.exitWhenFinished();
247         for (int i = 0; i < 20 && theInstance.reaper.isAlive(); i++) {
248             TestUtils.sleep(500L);  // allow reaper thread to die
249         }
250         assertTrue(theInstance.exitWhenFinished);
251         assertFalse(theInstance.reaper.isAlive());
252     }
253 
254     @Test
testFileCleanerExitWhenFinishedFirst()255     public void testFileCleanerExitWhenFinishedFirst() throws Exception {
256         assertFalse(theInstance.exitWhenFinished);
257         theInstance.exitWhenFinished();
258         assertTrue(theInstance.exitWhenFinished);
259         assertNull(theInstance.reaper);
260 
261         waitUntilTrackCount();
262 
263         assertEquals(0, theInstance.getTrackCount());
264         assertTrue(theInstance.exitWhenFinished);
265         assertNull(theInstance.reaper);
266     }
267 
268     @Test
testFileCleanerFile()269     public void testFileCleanerFile() throws Exception {
270         final String path = testFile.getPath();
271 
272         assertFalse(testFile.exists());
273         RandomAccessFile r = createRandomAccessFile();
274         assertTrue(testFile.exists());
275 
276         assertEquals(0, theInstance.getTrackCount());
277         theInstance.track(path, r);
278         assertEquals(1, theInstance.getTrackCount());
279 
280         r.close();
281         testFile = null;
282         r = null;
283 
284         waitUntilTrackCount();
285         pauseForDeleteToComplete(new File(path));
286 
287         assertEquals(0, theInstance.getTrackCount());
288         assertFalse(new File(path).exists(), showFailures());
289     }
290     @Test
testFileCleanerNull()291     public void testFileCleanerNull() {
292         assertThrows(NullPointerException.class, () -> theInstance.track((File) null, new Object()));
293         assertThrows(NullPointerException.class, () -> theInstance.track((File) null, new Object(), FileDeleteStrategy.NORMAL));
294         assertThrows(NullPointerException.class, () -> theInstance.track((String) null, new Object()));
295         assertThrows(NullPointerException.class, () -> theInstance.track((String) null, new Object(), FileDeleteStrategy.NORMAL));
296     }
297 
waitUntilTrackCount()298     private void waitUntilTrackCount() throws Exception {
299         System.gc();
300         TestUtils.sleep(500);
301         int count = 0;
302         while (theInstance.getTrackCount() != 0 && count++ < 5) {
303             List<String> list = new ArrayList<>();
304             try {
305                 long i = 0;
306                 while (theInstance.getTrackCount() != 0) {
307                     list.add(
308                         "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 "
309                             + i++);
310                 }
311             } catch (final Throwable ignored) {
312             }
313             list = null;
314             System.gc();
315             TestUtils.sleep(1000);
316         }
317         if (theInstance.getTrackCount() != 0) {
318             throw new IllegalStateException("Your JVM is not releasing References, try running the test with less memory (-Xmx)");
319         }
320 
321     }
322 }
323