| /* |
| * Licensed to the Apache Software Foundation (ASF) under one or more |
| * contributor license agreements. See the NOTICE file distributed with |
| * this work for additional information regarding copyright ownership. |
| * The ASF licenses this file to You under the Apache License, Version 2.0 |
| * (the "License"); you may not use this file except in compliance with |
| * the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| package org.apache.commons.vfs2; |
| |
| import java.io.File; |
| import java.lang.reflect.Field; |
| import java.lang.reflect.Method; |
| import java.lang.reflect.Modifier; |
| import java.util.ArrayList; |
| import java.util.Enumeration; |
| import java.util.List; |
| |
| import org.apache.commons.AbstractVfsTestCase; |
| import org.apache.commons.io.FileUtils; |
| import org.apache.commons.lang3.ArrayUtils; |
| import org.apache.commons.lang3.StringUtils; |
| import org.apache.commons.vfs2.impl.DefaultFileReplicator; |
| import org.apache.commons.vfs2.impl.DefaultFileSystemManager; |
| import org.apache.commons.vfs2.impl.PrivilegedFileReplicator; |
| import org.apache.commons.vfs2.provider.local.DefaultLocalFileProvider; |
| import org.junit.Assert; |
| |
| import junit.extensions.TestSetup; |
| import junit.framework.Protectable; |
| import junit.framework.Test; |
| import junit.framework.TestResult; |
| import junit.framework.TestSuite; |
| |
| /** |
| * The suite of tests for a file system. |
| */ |
| public abstract class AbstractTestSuite extends TestSetup { |
| |
| private static final Thread[] EMPTY_THREAD_ARRAY = new Thread[0]; |
| public static final String WRITE_TESTS_FOLDER = "write-tests"; |
| public static final String READ_TESTS_FOLDER = "read-tests"; |
| |
| private final ProviderTestConfig providerConfig; |
| private final String prefix; |
| private TestSuite testSuite; |
| |
| private FileObject baseFolder; |
| private FileObject readFolder; |
| private FileObject writeFolder; |
| private DefaultFileSystemManager manager; |
| private File tempDir; |
| |
| private Thread[] startThreadSnapshot; |
| private Thread[] endThreadSnapshot; |
| private final boolean addEmptyDir; |
| |
| protected AbstractTestSuite(final ProviderTestConfig providerConfig, final String prefix, final boolean nested) |
| throws Exception { |
| this(providerConfig, prefix, nested, false); |
| } |
| |
| protected AbstractTestSuite(final ProviderTestConfig providerConfig, final String prefix, final boolean nested, |
| final boolean addEmptyDir) throws Exception { |
| super(new TestSuite()); |
| testSuite = (TestSuite) fTest; |
| this.providerConfig = providerConfig; |
| this.prefix = prefix; |
| this.addEmptyDir = addEmptyDir; |
| addBaseTests(); |
| if (!nested) { |
| // Add nested tests |
| // TODO - move nested jar and zip tests here |
| // TODO - enable this again |
| // testSuite.addTest( new ProviderTestSuite( new JunctionProviderConfig( providerConfig ), "junction.", true |
| // )); |
| } |
| } |
| |
| /** |
| * Adds base tests - excludes the nested test cases. |
| */ |
| protected void addBaseTests() throws Exception { |
| } |
| |
| /** |
| * Adds the tests from a class to this suite. The supplied class must be a subclass of |
| * {@link AbstractProviderTestCase} and have a public a no-args constructor. This method creates an instance of the |
| * supplied class for each public 'testNnnn' method provided by the class. |
| */ |
| public void addTests(final Class<?> testClass) throws Exception { |
| // Verify the class |
| if (!AbstractProviderTestCase.class.isAssignableFrom(testClass)) { |
| throw new Exception("Test class " + testClass.getName() + " is not assignable to " |
| + AbstractProviderTestCase.class.getName()); |
| } |
| |
| // Locate the test methods |
| final Method[] methods = testClass.getMethods(); |
| for (final Method method2 : methods) { |
| final Method method = method2; |
| if (!method.getName().startsWith("test") || Modifier.isStatic(method.getModifiers()) |
| || method.getReturnType() != Void.TYPE || method.getParameterTypes().length != 0) { |
| continue; |
| } |
| |
| // Create instance |
| final AbstractProviderTestCase testCase = (AbstractProviderTestCase) testClass.newInstance(); |
| testCase.setMethod(method); |
| testCase.setName(prefix + method.getName()); |
| testCase.addEmptyDir(this.addEmptyDir); |
| testSuite.addTest(testCase); |
| } |
| } |
| |
| @Override |
| public void run(final TestResult result) { |
| final Protectable p = () -> { |
| setUp(); |
| basicRun(result); |
| tearDown(); |
| validateThreadSnapshot(); |
| }; |
| result.runProtected(this, p); |
| } |
| |
| @Override |
| protected void setUp() throws Exception { |
| startThreadSnapshot = createThreadSnapshot(); |
| |
| // Locate the temp directory, and clean it up |
| tempDir = AbstractVfsTestCase.getTestDirectory("temp"); |
| FileUtils.cleanDirectory(tempDir); |
| checkTempDir("Temp dir not empty before test"); |
| |
| // Create the file system manager |
| manager = providerConfig.getDefaultFileSystemManager(); |
| manager.setFilesCache(providerConfig.getFilesCache()); |
| |
| final DefaultFileReplicator replicator = new DefaultFileReplicator(tempDir); |
| manager.setReplicator(new PrivilegedFileReplicator(replicator)); |
| manager.setTemporaryFileStore(replicator); |
| |
| providerConfig.prepare(manager); |
| |
| if (!manager.hasProvider("file")) { |
| manager.addProvider("file", new DefaultLocalFileProvider()); |
| } |
| |
| manager.init(); |
| |
| // Locate the base folders |
| baseFolder = providerConfig.getBaseTestFolder(manager); |
| readFolder = baseFolder.resolveFile(READ_TESTS_FOLDER); |
| writeFolder = baseFolder.resolveFile(WRITE_TESTS_FOLDER); |
| |
| // Make some assumptions about the read folder |
| Assert.assertTrue("Folder does not exist: " + readFolder, readFolder.exists()); |
| Assert.assertNotEquals(readFolder.getName().getPath(), FileName.ROOT_PATH); |
| |
| // Configure the tests |
| final Enumeration<Test> tests = testSuite.tests(); |
| if (!tests.hasMoreElements()) { |
| Assert.fail("No tests."); |
| } |
| while (tests.hasMoreElements()) { |
| final Test test = tests.nextElement(); |
| if (test instanceof AbstractProviderTestCase) { |
| final AbstractProviderTestCase providerTestCase = (AbstractProviderTestCase) test; |
| providerTestCase.setConfig(manager, providerConfig, baseFolder, readFolder, writeFolder); |
| } |
| } |
| } |
| |
| @Override |
| protected void tearDown() throws Exception { |
| readFolder.close(); |
| writeFolder.close(); |
| baseFolder.close(); |
| |
| readFolder = null; |
| writeFolder = null; |
| baseFolder = null; |
| testSuite = null; |
| |
| // Suggest to threads (SoftRefFilesCache) to free all files. |
| System.gc(); |
| Thread.sleep(1000); |
| System.gc(); |
| Thread.sleep(1000); |
| System.gc(); |
| Thread.sleep(1000); |
| System.gc(); |
| Thread.sleep(1000); |
| |
| manager.freeUnusedResources(); |
| manager.close(); |
| // Give a chance for any threads to end. |
| Thread.sleep(20); |
| |
| // Make sure temp directory is empty or gone |
| checkTempDir("Temp dir not empty after test"); |
| VFS.close(); |
| } |
| |
| private void validateThreadSnapshot() { |
| endThreadSnapshot = createThreadSnapshot(); |
| |
| final Thread[] diffThreadSnapshot = diffThreadSnapshot(startThreadSnapshot, endThreadSnapshot); |
| if (diffThreadSnapshot.length > 0) { |
| final String message = dumpThreadSnapshot(diffThreadSnapshot); |
| /* |
| * if (providerConfig.checkCleanThreadState()) { // close the manager to do a "not thread safe" release of |
| * all resources // and allow the vm to shutdown manager.close(); fail(message); } else { |
| */ |
| System.out.print(message); |
| // } |
| } |
| // System.in.read(); |
| } |
| |
| /** |
| * Asserts that the temp dir is empty or gone. |
| */ |
| private void checkTempDir(final String assertMsg) { |
| if (tempDir.exists()) { |
| Assert.assertTrue(assertMsg + " (" + tempDir.getAbsolutePath() + ")", |
| tempDir.isDirectory() && ArrayUtils.isEmpty(tempDir.list())); |
| } |
| } |
| |
| private String dumpThreadSnapshot(final Thread[] threadSnapshot) { |
| if (ArrayUtils.isEmpty(threadSnapshot)) { |
| return StringUtils.EMPTY; |
| } |
| final StringBuffer sb = new StringBuffer(256); |
| sb.append("Threads still running (" + threadSnapshot.length + "): "); |
| sb.append(System.lineSeparator()); |
| |
| Field threadTargetField = null; |
| try { |
| threadTargetField = Thread.class.getDeclaredField("target"); |
| threadTargetField.setAccessible(true); |
| } catch (final Exception e) { |
| System.err.println("Test suite cannot show you a thread snapshot: " + e); |
| } |
| |
| int liveCount = 0; |
| for (int index = 0; index < threadSnapshot.length; index++) { |
| final Thread thread = threadSnapshot[index]; |
| if (thread != null && thread.isAlive()) { |
| liveCount++; |
| sb.append("\tThread["); |
| sb.append(index); |
| sb.append("] "); |
| sb.append(" ID "); |
| sb.append(thread.getId()); |
| sb.append(", "); |
| // prints [name,priority,group] |
| sb.append(thread); |
| sb.append(",\t"); |
| sb.append(thread.getState()); |
| sb.append(",\t"); |
| if (!thread.isDaemon()) { |
| sb.append("non_"); |
| } |
| sb.append("daemon"); |
| |
| if (threadTargetField != null) { |
| sb.append(",\t"); |
| try { |
| final Object threadTarget = threadTargetField.get(thread); |
| if (threadTarget != null) { |
| sb.append(threadTarget.getClass().getCanonicalName()); |
| } else { |
| sb.append("null"); |
| } |
| } catch (final IllegalAccessException e) { |
| sb.append("unknown ("); |
| sb.append(e); |
| sb.append(")"); |
| } |
| } |
| |
| sb.append(System.lineSeparator()); |
| // Stream.of(thread.getStackTrace()).forEach(e -> { |
| // sb.append('\t'); |
| // sb.append(e); |
| // sb.append(System.lineSeparator()); |
| // }); |
| } |
| } |
| return liveCount == 0 ? StringUtils.EMPTY : sb.toString(); |
| } |
| |
| private Thread[] diffThreadSnapshot(final Thread[] startThreadSnapshot, final Thread[] endThreadSnapshot) { |
| final List<Thread> diff = new ArrayList<>(10); |
| |
| nextEnd: for (final Thread element : endThreadSnapshot) { |
| for (final Thread element2 : startThreadSnapshot) { |
| if (element2 == element) { |
| continue nextEnd; |
| } |
| } |
| |
| diff.add(element); |
| } |
| |
| return diff.toArray(EMPTY_THREAD_ARRAY); |
| } |
| |
| private Thread[] createThreadSnapshot() { |
| ThreadGroup tg = Thread.currentThread().getThreadGroup(); |
| while (tg.getParent() != null) { |
| tg = tg.getParent(); |
| } |
| |
| final Thread snapshot[] = new Thread[200]; |
| tg.enumerate(snapshot, true); |
| |
| return snapshot; |
| } |
| } |