View Javadoc
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.vfs2;
18  
19  import java.io.File;
20  import java.lang.reflect.Field;
21  import java.lang.reflect.Method;
22  import java.lang.reflect.Modifier;
23  import java.time.Instant;
24  import java.util.ArrayList;
25  import java.util.Enumeration;
26  import java.util.List;
27  
28  import org.apache.commons.io.FileUtils;
29  import org.apache.commons.lang3.ArrayUtils;
30  import org.apache.commons.lang3.StringUtils;
31  import org.apache.commons.vfs2.impl.DefaultFileReplicator;
32  import org.apache.commons.vfs2.impl.DefaultFileSystemManager;
33  import org.apache.commons.vfs2.impl.PrivilegedFileReplicator;
34  import org.apache.commons.vfs2.provider.local.DefaultLocalFileProvider;
35  import org.junit.Assert;
36  
37  import junit.extensions.TestSetup;
38  import junit.framework.Protectable;
39  import junit.framework.Test;
40  import junit.framework.TestResult;
41  import junit.framework.TestSuite;
42  
43  /**
44   * The suite of tests for a file system.
45   */
46  public abstract class AbstractTestSuite extends TestSetup {
47  
48      private static final Thread[] EMPTY_THREAD_ARRAY = new Thread[0];
49      public static final String WRITE_TESTS_FOLDER = "write-tests";
50      public static final String READ_TESTS_FOLDER = "read-tests";
51  
52      private final ProviderTestConfig providerConfig;
53      private final String prefix;
54      private TestSuite testSuite;
55  
56      private FileObject baseFolder;
57      private FileObject readFolder;
58      private FileObject writeFolder;
59      private DefaultFileSystemManager manager;
60      private File tempDir;
61  
62      private Thread[] startThreadSnapshot;
63      private Thread[] endThreadSnapshot;
64      private final boolean addEmptyDir;
65  
66      protected AbstractTestSuite(final ProviderTestConfig providerConfig, final String prefix, final boolean nested)
67          throws Exception {
68          this(providerConfig, prefix, nested, false);
69      }
70  
71      protected AbstractTestSuite(final ProviderTestConfig providerConfig, final String prefix, final boolean nested,
72          final boolean addEmptyDir) throws Exception {
73          super(new TestSuite());
74          testSuite = (TestSuite) fTest;
75          this.providerConfig = providerConfig;
76          this.prefix = prefix;
77          this.addEmptyDir = addEmptyDir;
78          addBaseTests();
79          if (!nested) {
80              // Add nested tests
81              // TODO - move nested jar and zip tests here
82              // TODO - enable this again
83              // testSuite.addTest( new ProviderTestSuite( new JunctionProviderConfig( providerConfig ), "junction.", true
84              // ));
85          }
86      }
87  
88      /**
89       * Adds base tests - excludes the nested test cases.
90       */
91      protected void addBaseTests() throws Exception {
92      }
93  
94      /**
95       * Adds the tests from a class to this suite. The supplied class must be a subclass of
96       * {@link AbstractProviderTestCase} and have a public a no-args constructor. This method creates an instance of the
97       * supplied class for each public 'testNnnn' method provided by the class.
98       */
99      public void addTests(final Class<?> testClass) throws Exception {
100         // Verify the class
101         if (!AbstractProviderTestCase.class.isAssignableFrom(testClass)) {
102             throw new Exception("Test class " + testClass.getName() + " is not assignable to "
103                 + AbstractProviderTestCase.class.getName());
104         }
105 
106         // Locate the test methods
107         final Method[] methods = testClass.getMethods();
108         for (final Method method2 : methods) {
109             final Method method = method2;
110             if (!method.getName().startsWith("test") || Modifier.isStatic(method.getModifiers())
111                 || method.getReturnType() != Void.TYPE || method.getParameterTypes().length != 0) {
112                 continue;
113             }
114 
115             // Create instance
116             final AbstractProviderTestCasehe/commons/vfs2/AbstractProviderTestCase.html#AbstractProviderTestCase">AbstractProviderTestCase testCase = (AbstractProviderTestCase) testClass.newInstance();
117             testCase.setMethod(method);
118             testCase.setName(prefix + method.getName());
119             testCase.addEmptyDir(this.addEmptyDir);
120             testSuite.addTest(testCase);
121         }
122     }
123 
124     /**
125      * Asserts that the temp dir is empty or gone.
126      */
127     private void checkTempDir(final String assertMsg) {
128         if (tempDir.exists()) {
129             Assert.assertTrue(assertMsg + " (" + tempDir.getAbsolutePath() + ")",
130                 tempDir.isDirectory() && ArrayUtils.isEmpty(tempDir.list()));
131         }
132     }
133 
134     private Thread[] createThreadSnapshot() {
135         ThreadGroup tg = Thread.currentThread().getThreadGroup();
136         while (tg.getParent() != null) {
137             tg = tg.getParent();
138         }
139 
140         final Thread[] snapshot = new Thread[200];
141         tg.enumerate(snapshot, true);
142 
143         return snapshot;
144     }
145 
146     private Thread[] diffThreadSnapshot(final Thread[] startThreadSnapshot, final Thread[] endThreadSnapshot) {
147         final List<Thread> diff = new ArrayList<>(10);
148 
149         nextEnd: for (final Thread element : endThreadSnapshot) {
150             for (final Thread element2 : startThreadSnapshot) {
151                 if (element2 == element) {
152                     continue nextEnd;
153                 }
154             }
155 
156             diff.add(element);
157         }
158 
159         return diff.toArray(EMPTY_THREAD_ARRAY);
160     }
161 
162     private String dumpThreadSnapshot(final Thread[] threadSnapshot) {
163         if (ArrayUtils.isEmpty(threadSnapshot)) {
164             return StringUtils.EMPTY;
165         }
166         final StringBuffer sb = new StringBuffer(256);
167         sb.append("Threads still running (" + threadSnapshot.length + ") at " + Instant.now() + ", live threads:");
168         sb.append(System.lineSeparator());
169 
170         Field threadTargetField = null;
171         try {
172             threadTargetField = Thread.class.getDeclaredField("target");
173             threadTargetField.setAccessible(true);
174         } catch (final Exception e) {
175             System.err.println("Test suite cannot show you a thread snapshot: " + e);
176         }
177 
178         int liveCount = 0;
179         for (int index = 0; index < threadSnapshot.length; index++) {
180             final Thread thread = threadSnapshot[index];
181             if (thread != null && thread.isAlive()) {
182                 liveCount++;
183                 sb.append("\tThread[");
184                 sb.append(index);
185                 sb.append("] ");
186                 sb.append(" ID ");
187                 sb.append(thread.getId());
188                 sb.append(", ");
189                 // prints [name,priority,group]
190                 sb.append(thread);
191                 sb.append(",\t");
192                 sb.append(thread.getState());
193                 sb.append(",\t");
194                 if (!thread.isDaemon()) {
195                     sb.append("non_");
196                 }
197                 sb.append("daemon");
198 
199                 if (threadTargetField != null) {
200                     sb.append(",\t");
201                     try {
202                         final Object threadTarget = threadTargetField.get(thread);
203                         if (threadTarget != null) {
204                             sb.append(threadTarget.getClass().getCanonicalName());
205                         } else {
206                             sb.append("null");
207                         }
208                     } catch (final IllegalAccessException e) {
209                         sb.append("unknown (");
210                         sb.append(e);
211                         sb.append(")");
212                     }
213                 }
214 
215                 sb.append(System.lineSeparator());
216 //              Stream.of(thread.getStackTrace()).forEach(e -> {
217 //                  sb.append('\t');
218 //                  sb.append(e);
219 //                  sb.append(System.lineSeparator());
220 //              });
221             }
222         }
223         return liveCount == 0 ? StringUtils.EMPTY : sb.toString();
224     }
225 
226     @Override
227     public void run(final TestResult result) {
228         final Protectable p = () -> {
229             setUp();
230             basicRun(result);
231             tearDown();
232             validateThreadSnapshot();
233         };
234         result.runProtected(this, p);
235     }
236 
237     @Override
238     protected void setUp() throws Exception {
239         startThreadSnapshot = createThreadSnapshot();
240 
241         // Locate the temp directory, and clean it up
242         tempDir = AbstractVfsTestCase.getTestDirectory("temp");
243         FileUtils.cleanDirectory(tempDir);
244         checkTempDir("Temp dir not empty before test");
245 
246         // Create the file system manager
247         manager = providerConfig.getDefaultFileSystemManager();
248         manager.setFilesCache(providerConfig.getFilesCache());
249 
250         final DefaultFileReplicator replicator = new DefaultFileReplicator(tempDir);
251         manager.setReplicator(new PrivilegedFileReplicator(replicator));
252         manager.setTemporaryFileStore(replicator);
253 
254         providerConfig.prepare(manager);
255 
256         if (!manager.hasProvider("file")) {
257             manager.addProvider("file", new DefaultLocalFileProvider());
258         }
259 
260         manager.init();
261 
262         // Locate the base folders
263         baseFolder = providerConfig.getBaseTestFolder(manager);
264         readFolder = baseFolder.resolveFile(READ_TESTS_FOLDER);
265         writeFolder = baseFolder.resolveFile(WRITE_TESTS_FOLDER);
266 
267         // Make some assumptions about the read folder
268         Assert.assertTrue("Folder does not exist: " + readFolder, readFolder.exists());
269         Assert.assertNotEquals(readFolder.getName().getPath(), FileName.ROOT_PATH);
270 
271         // Configure the tests
272         final Enumeration<Test> tests = testSuite.tests();
273         if (!tests.hasMoreElements()) {
274             Assert.fail("No tests.");
275         }
276         while (tests.hasMoreElements()) {
277             final Test test = tests.nextElement();
278             if (test instanceof AbstractProviderTestCase) {
279                 final AbstractProviderTestCasens/vfs2/AbstractProviderTestCase.html#AbstractProviderTestCase">AbstractProviderTestCase providerTestCase = (AbstractProviderTestCase) test;
280                 providerTestCase.setConfig(manager, providerConfig, baseFolder, readFolder, writeFolder);
281             }
282         }
283     }
284 
285     @Override
286     protected void tearDown() throws Exception {
287         readFolder.close();
288         writeFolder.close();
289         baseFolder.close();
290 
291         readFolder = null;
292         writeFolder = null;
293         baseFolder = null;
294         testSuite = null;
295 
296         // Suggest to threads (SoftRefFilesCache) to free all files.
297         System.gc();
298         Thread.sleep(1000);
299         System.gc();
300         Thread.sleep(1000);
301         System.gc();
302         Thread.sleep(1000);
303         System.gc();
304         Thread.sleep(1000);
305 
306         manager.freeUnusedResources();
307         manager.close();
308         // Give a chance for any threads to end.
309         Thread.sleep(20);
310 
311         // Make sure temp directory is empty or gone
312         checkTempDir("Temp dir not empty after test");
313         VFS.close();
314     }
315 
316     private void validateThreadSnapshot() {
317         endThreadSnapshot = createThreadSnapshot();
318 
319         final Thread[] diffThreadSnapshot = diffThreadSnapshot(startThreadSnapshot, endThreadSnapshot);
320         if (diffThreadSnapshot.length > 0) {
321             final String message = dumpThreadSnapshot(diffThreadSnapshot);
322             /*
323              * if (providerConfig.checkCleanThreadState()) { // close the manager to do a "not thread safe" release of
324              * all resources // and allow the vm to shutdown manager.close(); fail(message); } else {
325              */
326             System.out.print(message);
327             // }
328         }
329         // System.in.read();
330     }
331 }