blob: 9278a6d2bbf910aa189666be67ba58f4d641ed16 [file] [log] [blame]
/*
* 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.test;
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 junit.extensions.TestSetup;
import junit.framework.Protectable;
import junit.framework.Test;
import junit.framework.TestResult;
import junit.framework.TestSuite;
import org.apache.commons.io.FileUtils;
import org.apache.commons.AbstractVfsTestCase;
import org.apache.commons.vfs2.FileName;
import org.apache.commons.vfs2.FileObject;
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;
/**
* The suite of tests for a file system.
*/
public abstract class AbstractTestSuite
extends TestSetup
{
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;
/**
* Adds the tests for a file system to this suite.
*/
public AbstractTestSuite(final ProviderTestConfig providerConfig) throws Exception
{
this(providerConfig, "", false, false);
}
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 = new Protectable() {
@Override
public void protect() throws Exception {
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");
writeFolder = baseFolder.resolveFile("write-tests");
// Make some assumptions about the read folder
assertTrue("Folder does not exist: " + readFolder, readFolder.exists());
assertFalse(readFolder.getName().getPath().equals(FileName.ROOT_PATH));
// Configure the tests
final Enumeration<Test> tests = testSuite.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;
// force the SoftRefFilesChache 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();
// Make sure temp directory is empty or gone
checkTempDir("Temp dir not empty after test");
}
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.println(message);
// }
}
// System.in.read();
}
/**
* Asserts that the temp dir is empty or gone.
*/
private void checkTempDir(final String assertMsg)
{
if (tempDir.exists())
{
assertTrue(assertMsg + " (" + tempDir.getAbsolutePath() + ")", tempDir.isDirectory() && tempDir.list().length == 0);
}
}
private String dumpThreadSnapshot(final Thread[] threadSnapshot)
{
final StringBuffer sb = new StringBuffer(256);
sb.append("created threads still running:\n");
Field threadTargetField = null;
try
{
threadTargetField = Thread.class.getDeclaredField("target");
threadTargetField.setAccessible(true);
}
catch (final NoSuchFieldException e)
{
// ignored
}
for (int iter = 0; iter < threadSnapshot.length; iter++)
{
final Thread thread = threadSnapshot[iter];
if (thread == null || !thread.isAlive())
{
continue;
}
sb.append("#");
sb.append(iter + 1);
sb.append(": ");
sb.append(thread.getThreadGroup() != null ?
thread.getThreadGroup().getName() : "(null)");
sb.append("\t");
sb.append(thread.getName());
sb.append("\t");
sb.append(thread.getState());
sb.append("\t");
if (thread.isDaemon())
{
sb.append("daemon");
}
else
{
sb.append("not_a_daemon");
}
if (threadTargetField != null)
{
sb.append("\t");
try
{
final Object threadTarget = threadTargetField.get(thread);
if (threadTarget != null)
{
sb.append(threadTarget.getClass());
}
else
{
sb.append("null");
}
}
catch (final IllegalAccessException e)
{
sb.append("unknown class");
}
}
sb.append("\n");
}
return sb.toString();
}
private Thread[] diffThreadSnapshot(final Thread[] startThreadSnapshot, final Thread[] endThreadSnapshot)
{
final List<Thread> diff = new ArrayList<Thread>(10);
nextEnd: for (int iterEnd = 0; iterEnd < endThreadSnapshot.length; iterEnd++)
{
for (int iterStart = 0; iterStart < startThreadSnapshot.length; iterStart++)
{
if (startThreadSnapshot[iterStart] == endThreadSnapshot[iterEnd])
{
continue nextEnd;
}
}
diff.add(endThreadSnapshot[iterEnd]);
}
final Thread ret[] = new Thread[diff.size()];
diff.toArray(ret);
return ret;
}
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;
}
}