org.tigris.subversion.javahl.tests.SVNTests Maven / Gradle / Ivy
The newest version!
/**
* @copyright
* ====================================================================
* Copyright (c) 2003-2005 CollabNet. All rights reserved.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
* are also available at http://subversion.tigris.org/license-1.html.
* If newer versions of this license are posted there, you may use a
* newer version instead, at your option.
*
* This software consists of voluntary contributions made by many
* individuals. For exact contribution history, see the revision
* history and logs, available at http://subversion.tigris.org/.
* ====================================================================
* @endcopyright
*/
package org.tigris.subversion.javahl.tests;
import junit.framework.TestCase;
import junit.framework.TestSuite;
import org.tigris.subversion.javahl.*;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
/**
* common base class for the javahl binding tests
*/
public class SVNTests extends TestCase
{
/**
* our admin object, mostly used for creating,dumping and loading
* repositories
*/
protected SVNAdmin admin;
/**
* the subversion client, what we want to test.
*/
protected SVNClientInterface client;
/**
* the root directory. All other files and directories will created in
* here
*/
protected File rootDir;
/**
* the base name of the test. Together with the testCounter this will make
* up the directory name of the test.
*/
protected String testBaseName;
/**
* this counter will be incremented for every test in one suite (test class)
*/
protected static int testCounter;
/**
* the file in which the sample repository has been dumped.
*/
protected File greekDump;
/**
* the directory of the sample repository.
*/
protected File greekRepos;
/**
* the initial working copy of the sample repository.
*/
protected WC greekWC;
/**
* the directory "svn-test-work/local_tmp" in the rootDir. This
* will be used for the sample repository and its dumpfile and for
* the config directory
*/
protected File localTmp;
/**
* the directory "repositories" in the rootDir. All test repositories will
* be created here.
*/
protected File repositories;
/**
* the directory "working_copies" in the rootDir. All test working copies
* will be created here.
*/
protected File workingCopies;
/**
* the directory "config" in the localTmp. It will be used as the
* configuration directory for all the tests.
*/
protected File conf;
/**
* standard log message. Used for all commits.
*/
protected String logMessage = "Log Message";
/**
* the map of all items expected to be received by the callback for the
* log message. After each commit, this will be cleared
*/
protected Map expectedCommitItems;
/**
* common root directory for all tests. Can be set by the command line or
* by the system property "test.rootdir". If not set, the current directory
* of this process is used
*/
protected static String rootDirectoryName;
/**
* common root URL for all tests. Can be set by the command line or by the
* system property "test.rooturl". If not set, the file url of the
* rootDirectoryName is used.
*/
protected static String rootUrl;
/**
* retrieve the root directory and the root url from the command line
* arguments
* @param args command line arguments
*/
protected static void processArgs(String[] args)
{
if (args == null)
return;
for (int i = 0; i < args.length; i++)
{
String arg = args[i];
if ("-d".equals(arg))
{
if (i + 1 < args.length)
{
rootDirectoryName = args[++i];
}
}
if ("-u".equals(arg))
{
if (i + 1 < args.length)
{
rootUrl = args[++i];
}
}
}
}
/**
* Main method, will call all tests of all test classes
* @param args command line arguments
*/
public static void main(String[] args)
{
processArgs(args);
junit.textui.TestRunner.run(suite());
}
/**
* build a test suite with all test of all test classes known
* @return complete test suite
*/
public static TestSuite suite()
{
TestSuite suite = new TestSuite();
suite.addTestSuite(BasicTests.class);
suite.addTestSuite(SVNAdminTests.class);
return suite;
}
/**
* Initialize one test object
*/
protected SVNTests()
{
// if not already set, get a usefull value for rootDir
if(rootDirectoryName == null)
rootDirectoryName = System.getProperty("test.rootdir");
if(rootDirectoryName == null)
rootDirectoryName = System.getProperty("user.dir");
rootDir = new File(rootDirectoryName);
// if not alread set, get a usefull value for root url
if(rootUrl == null)
rootUrl = System.getProperty("test.rooturl");
if(rootUrl == null)
{
// if no root url, set build a file url
rootUrl = rootDir.toURI().toString();
// java may have a different view about the number of '/' to follow
// "file:" than subversion. We convert to the subversion view.
if(rootUrl.startsWith("file:///"))
; // this is the form subversion needs
else if(rootUrl.startsWith("file://"))
rootUrl = rootUrl.replaceFirst("file://", "file:///");
else if(rootUrl.startsWith("file:/"))
rootUrl = rootUrl.replaceFirst("file:/", "file:///");
}
}
/**
* Standard initialization of one test
* @throws Exception
*/
protected void setUp() throws Exception
{
super.setUp();
// create a clean directory for the config files and the sample
// repository
//
// ### The path is now "svn-test-work/local_tmp", however, I'm
// ### not sure how to update this code for that.
localTmp = new File(rootDir, "local_tmp");
if(localTmp.exists())
removeDirectoryWithContent(localTmp);
localTmp.mkdir();
conf = new File(localTmp, "config");
conf.mkdir();
// create and configure the needed subversion objects
admin = new SVNAdmin();
client = new SVNClientSynchronized();
client.notification2(new MyNotifier());
client.commitMessageHandler(new MyCommitMessage());
client.username("jrandom");
client.password("rayjandom");
client.setConfigDirectory(conf.getAbsolutePath());
expectedCommitItems = new HashMap();
// build and dump the sample repository
File greekFiles = buildGreekFiles();
greekRepos = new File(localTmp, "repos");
greekDump = new File(localTmp, "greek_dump");
admin.create(greekRepos.getAbsolutePath(), true,false, null,
SVNAdmin.BDB);
addExpectedCommitItem(greekFiles.getAbsolutePath(), null, null,
NodeKind.none, CommitItemStateFlags.Add);
client.doImport(greekFiles.getAbsolutePath(), makeReposUrl(greekRepos),
null, true );
admin.dump(greekRepos.getAbsolutePath(), new FileOutputer(greekDump),
new IgnoreOutputer(), null, null, false);
// create the directory for the repositories and the working copies
//
// ### The paths are now "svn-test-work/repositories" and
// ### "svn-test-work/repositories". However, I'm not sure
// ### how to update this code for that.
repositories = new File(rootDir, "repositories");
repositories.mkdirs();
workingCopies = new File(rootDir, "working_copies");
workingCopies.mkdirs();
}
/**
* build a sample directory with test files to be used as import for
* the sample repository. Create also the master working copy test set.
* @return the sample repository
* @throws IOException
*/
private File buildGreekFiles() throws IOException
{
File greekFiles = new File(localTmp, "greek_files");
greekFiles.mkdir();
greekWC = new WC();
greekWC.addItem("",null);
greekWC.addItem("iota", "This is the file 'iota'.");
greekWC.addItem("A", null);
greekWC.addItem("A/mu", "This is the file 'mu'.");
greekWC.addItem("A/B", null);
greekWC.addItem("A/B/lambda", "This is the file 'lambda'.");
greekWC.addItem("A/B/E", null);
greekWC.addItem("A/B/E/alpha", "This is the file 'alpha'.");
greekWC.addItem("A/B/E/beta", "This is the file 'beta'.");
greekWC.addItem("A/B/F", null);
greekWC.addItem("A/C", null);
greekWC.addItem("A/D", null);
greekWC.addItem("A/D/gamma", "This is the file 'gamma'.");
greekWC.addItem("A/D/H", null);
greekWC.addItem("A/D/H/chi", "This is the file 'chi'.");
greekWC.addItem("A/D/H/psi", "This is the file 'psi'.");
greekWC.addItem("A/D/H/omega", "This is the file 'omega'.");
greekWC.addItem("A/D/G", null);
greekWC.addItem("A/D/G/pi", "This is the file 'pi'.");
greekWC.addItem("A/D/G/rho", "This is the file 'rho'.");
greekWC.addItem("A/D/G/tau", "This is the file 'tau'.");
greekWC.materialize(greekFiles);
return greekFiles;
}
/**
* Remove a directory with all files and directories it may contain.
* @param localTmp
*/
protected void removeDirectoryWithContent(File localTmp)
{
if(localTmp.isDirectory())
{
File[] content = localTmp.listFiles();
for(int i = 0; i < content.length; i++)
removeDirectoryWithContent(content[i]);
}
localTmp.delete();
}
/**
* cleanup after one test
* @throws Exception
*/
protected void tearDown() throws Exception
{
// take care of our subversion objects.
admin.dispose();
client.dispose();
// remove the temporary directory
removeDirectoryWithContent(localTmp);
super.tearDown();
}
/**
* Create the url for the repository to be used for the tests.
* @param file the directory of the repository
* @return the URL for the repository
*/
protected String makeReposUrl(File file)
{
// split the common part of the root directory
String path = file.getAbsolutePath().
substring(rootDirectoryName.length()+1);
// append to the root url
return rootUrl + path.replace(File.separatorChar, '/');
}
/**
* add another commit item expected during the callback for the log message.
* @param workingCopyPath the path of the of the working
* @param baseUrl the url for the repository
* @param itemPath the path of the item relative the working copy
* @param nodeKind expected node kind (dir or file or none)
* @param stateFlags expected commit state flags
* (see CommitItemStateFlags)
*/
protected void addExpectedCommitItem(String workingCopyPath, String baseUrl,
String itemPath, int nodeKind,
int stateFlags)
{
//determine the full working copy path and the full url of the item.
String path = null;
if(workingCopyPath != null)
if(itemPath != null)
path = workingCopyPath.replace(File.separatorChar, '/') +
'/' + itemPath;
else
path = workingCopyPath.replace(File.separatorChar, '/');
String url = null;
if(baseUrl != null)
if(itemPath != null)
url = baseUrl + '/' + itemPath;
else
url = baseUrl;
// the key of the item is either the url or the path (if no url)
String key;
if(url != null)
key = url;
else
key = path;
expectedCommitItems.put(key, new MyCommitItem(path, nodeKind,
stateFlags, url));
}
/**
* Intended to be called as part of test method execution
* (post-{@link #setUp()}). Calls fail()
if the
* directory name cannot be determined.
*
* @return The name of the working copy administrative directory.
* @since 1.3
*/
protected String getAdminDirectoryName() {
String admDirName = null;
if (this.client != null) {
admDirName = client.getAdminDirectoryName();
}
if (admDirName == null) {
fail("Unable to determine the WC admin directory name");
}
return admDirName;
}
/**
* internal class which implements the OutputInterface to write the data
* to a file.
*/
public class FileOutputer implements OutputInterface
{
/**
* the output file stream
*/
FileOutputStream myStream;
/**
* create new object
* @param outputName the file to write the data to
* @throws IOException
*/
public FileOutputer(File outputName) throws IOException
{
myStream = new FileOutputStream(outputName);
}
/**
* write the bytes in data to java
* @param data the data to be writtem
* @throws IOException throw in case of problems.
*/
public int write(byte[] data) throws IOException
{
myStream.write(data);
return data.length;
}
/**
* close the output
* @throws IOException throw in case of problems.
*/
public void close() throws IOException
{
myStream.close();
}
}
/**
* internal class implements the OutputInterface, but ignores the data
*/
public class IgnoreOutputer implements OutputInterface
{
/**
* write the bytes in data to java
* @param data the data to be writtem
* @throws IOException throw in case of problems.
*/
public int write(byte[] data) throws IOException
{
return data.length;
}
/**
* close the output
* @throws IOException throw in case of problems.
*/
public void close() throws IOException
{
}
}
/**
* internal class which implements the InputInterface to read the data
* from a file.
*/
public class FileInputer implements InputInterface
{
/**
* input file stream
*/
FileInputStream myStream;
/**
* create a new object
* @param inputName the file from which the data is read
* @throws IOException
*/
public FileInputer(File inputName) throws IOException
{
myStream = new FileInputStream(inputName);
}
/**
* read the number of data.length bytes from input.
* @param data array to store the read bytes.
* @throws IOException throw in case of problems.
*/
public int read(byte[] data) throws IOException
{
return myStream.read(data);
}
/**
* close the input
* @throws IOException throw in case of problems.
*/
public void close() throws IOException
{
myStream.close();
}
}
/**
* this internal class represent the repository and the working copy for
* one test.
*/
protected class OneTest
{
/**
* the file name of repository (used by SVNAdmin)
*/
protected File repository;
/**
* the file name of the working copy directory
*/
protected File workingCopy;
/**
* the url of the repository (used by SVNClient)
*/
protected String url;
/**
* the expected layout of the working copy after the next subversion
* command
*/
protected WC wc;
/**
* build a new test setup with a new repository, a new working and a
* new expected working layout
* @throws Exception
*/
protected OneTest() throws Exception
{
String testName = testBaseName + ++testCounter;
wc = greekWC.copy();
repository = createStartRepository(testName);
url = makeReposUrl(repository);
workingCopy = createStartWorkingCopy(repository, testName);
}
/**
* Copy the working copy and the expected working copy layout for tests
* which need multiple working copy
* @param append append to the working copy name of the original
* @return second test object.
* @throws Exception
*/
protected OneTest copy(String append) throws Exception
{
return new OneTest(this, append);
}
/**
* constructor for create a copy
* @param orig original test
* @param append append this to the directory name of the original
* test
* @throws Exception
*/
private OneTest(OneTest orig, String append) throws Exception
{
String testName = testBaseName + testCounter +append;
repository = orig.getRepository();
url = orig.getUrl();
wc = orig.wc.copy();
workingCopy = createStartWorkingCopy(repository, testName);
}
/**
* Return the directory of the repository
* @return the repository directory name
*/
public File getRepository()
{
return repository;
}
/**
* Return the name of the directory of the repository
* @return the name of repository directory
*/
public String getRepositoryPath()
{
return repository.getAbsolutePath();
}
/**
* Return the working copy directory
* @return the working copy directory
*/
public File getWorkingCopy()
{
return workingCopy;
}
/**
* Return the working copy directory name
* @return the name of the working copy directory
*/
public String getWCPath()
{
return workingCopy.getAbsolutePath();
}
/**
* Returns the url of repository
* @return the url
*/
public String getUrl()
{
return url;
}
/**
* Returns the expected working copy content
* @return the expected working copy content
*/
public WC getWc()
{
return wc;
}
/**
* Create the repository for the beginning of the test
* @param testName the name of the test
* @return the repository directory
* @throws Exception
*/
protected File createStartRepository(String testName) throws Exception
{
// build a clean repository directory
File repos = new File(repositories, testName);
removeDirectoryWithContent(repos);
// create and load the repository from the default repository dump
admin.create(repos.getAbsolutePath(), true, false,
conf.getAbsolutePath(), SVNAdmin.BDB);
admin.load(repos.getAbsolutePath(), new FileInputer(greekDump),
new IgnoreOutputer(), false, false, null);
return repos;
}
/**
* Create the working copy for the beginning of the test
* @param repos the repository directory
* @param testName the name of the test
* @return the directory of the working copy
* @throws Exception
*/
protected File createStartWorkingCopy(File repos, String testName)
throws Exception
{
// build a clean working directory
String uri = makeReposUrl(repos);
workingCopy = new File(workingCopies, testName);
removeDirectoryWithContent(workingCopy);
// checkout the repository
client.checkout(uri, workingCopy.getAbsolutePath(), null, true);
// sanity check the working with its expected status
checkStatus();
return workingCopy;
}
/**
* Check if the working copy has the expected status
* @throws Exception
*/
public void checkStatus() throws Exception
{
Status[] states = client.status(workingCopy.getAbsolutePath(), true,
false, true, true);
wc.check(states, workingCopy.getAbsolutePath());
}
}
/**
* internal class to receive the request for the log messages to check if
* the expected commit items are presented
*/
class MyCommitMessage implements CommitMessage
{
/**
* Retrieve a commit message from the user based on the items to be commited
* @param elementsToBeCommited Array of elements to be commited
* @return the log message of the commit.
*/
public String getLogMessage(CommitItem[] elementsToBeCommited)
{
// check all received CommitItems are expected as received
for (int i = 0; i < elementsToBeCommited.length; i++)
{
CommitItem commitItem = elementsToBeCommited[i];
// since imports do not provide a url, the key is either url or
// path
String key;
if(commitItem.getUrl() != null)
key = commitItem.getUrl();
else
key = commitItem.getPath();
MyCommitItem myItem = (MyCommitItem) expectedCommitItems.get(
key);
// check commit item is expected and has the expected data
assertNotNull("commit item for "+key+ " not expected", myItem);
myItem.test(commitItem, key);
}
// all remaining expected commit items are missing
for (Iterator iterator = expectedCommitItems.keySet().iterator();
iterator.hasNext();)
{
String str = (String) iterator.next();
fail("commit item for "+str+" not found");
}
return logMessage;
}
}
/**
* internal class to describe an expected commit item
*/
class MyCommitItem
{
/**
* the path of the item
*/
String myPath;
/**
* the kind of node (file, directory or none, see NodeKind)
*/
int myNodeKind;
/**
* the reason why this item is commited (see CommitItemStateFlag)
*/
int myStateFlags;
/**
* the url of the item
*/
String myUrl;
/**
* build one expected commit item
* @param path the expected path
* @param nodeKind the expected node kind
* @param stateFlags the expected state flags
* @param url the expected url
*/
private MyCommitItem(String path, int nodeKind, int stateFlags,
String url)
{
myPath = path;
myNodeKind = nodeKind;
myStateFlags = stateFlags;
myUrl = url;
}
/**
* Check if the commit item has the expected data
* @param ci the commit item to check
* @param key the key of the item
*/
private void test(CommitItem ci, String key)
{
assertEquals("commit item path", ci.getPath(), myPath);
assertEquals("commit item node kind", ci.getNodeKind(), myNodeKind);
assertEquals("commit item state flags", ci.getStateFlags(),
myStateFlags);
assertEquals("commit item url", ci.getUrl(), myUrl);
// after the test, remove the item from the expected map
expectedCommitItems.remove(key);
}
}
class MyNotifier implements Notify2
{
/**
* Handler for Subversion notifications.
*
* Override this function to allow Subversion to send notifications
*
* @param info everything to know about this event
*/
public void onNotify(NotifyInformation info)
{
}
}
}