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