adding releng tests
diff --git a/tests/org.eclipse.wtp.releng.tests/.classpath b/tests/org.eclipse.wtp.releng.tests/.classpath
new file mode 100644
index 0000000..7398f97
--- /dev/null
+++ b/tests/org.eclipse.wtp.releng.tests/.classpath
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<classpath>
+	<classpathentry path="src" kind="src"/>
+	<classpathentry path="org.eclipse.jdt.launching.JRE_CONTAINER" kind="con"/>
+	<classpathentry path="org.eclipse.pde.core.requiredPlugins" kind="con"/>
+	<classpathentry path="bin" kind="output"/>
+</classpath>
diff --git a/tests/org.eclipse.wtp.releng.tests/.cvsignore b/tests/org.eclipse.wtp.releng.tests/.cvsignore
new file mode 100644
index 0000000..c91f213
--- /dev/null
+++ b/tests/org.eclipse.wtp.releng.tests/.cvsignore
@@ -0,0 +1,6 @@
+bin
+temp.folder
+org.eclipse.releng.tests_3.0.0.zip
+relengtestssrc.zip
+build.xml
+relengtests.jar
diff --git a/tests/org.eclipse.wtp.releng.tests/.project b/tests/org.eclipse.wtp.releng.tests/.project
new file mode 100644
index 0000000..c8b6d4d
--- /dev/null
+++ b/tests/org.eclipse.wtp.releng.tests/.project
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+	<name>org.eclipse.wtp.releng.tests</name>
+	<comment></comment>
+	<projects>
+	</projects>
+	<buildSpec>
+		<buildCommand>
+			<name>org.eclipse.jdt.core.javabuilder</name>
+			<arguments>
+			</arguments>
+		</buildCommand>
+		<buildCommand>
+			<name>org.eclipse.pde.ManifestBuilder</name>
+			<arguments>
+			</arguments>
+		</buildCommand>
+		<buildCommand>
+			<name>org.eclipse.pde.SchemaBuilder</name>
+			<arguments>
+			</arguments>
+		</buildCommand>
+	</buildSpec>
+	<natures>
+		<nature>org.eclipse.jdt.core.javanature</nature>
+		<nature>org.eclipse.pde.PluginNature</nature>
+	</natures>
+</projectDescription>
diff --git a/tests/org.eclipse.wtp.releng.tests/META-INF/MANIFEST.MF b/tests/org.eclipse.wtp.releng.tests/META-INF/MANIFEST.MF
new file mode 100644
index 0000000..3d9a6a6
--- /dev/null
+++ b/tests/org.eclipse.wtp.releng.tests/META-INF/MANIFEST.MF
@@ -0,0 +1,11 @@
+Manifest-Version: 1.0
+Bundle-ManifestVersion: 2
+Bundle-Name: %Bundle-Name.0
+Bundle-SymbolicName: org.eclipse.wtp.releng.tests
+Bundle-Version: 1.0.0
+Bundle-Vendor: %Bundle-Vendor.0
+Bundle-Localization: plugin
+Require-Bundle: org.eclipse.core.runtime,
+ org.junit,
+ org.eclipse.callisto.tools.versionchecker
+Eclipse-LazyStart: true
diff --git a/tests/org.eclipse.wtp.releng.tests/about.html b/tests/org.eclipse.wtp.releng.tests/about.html
new file mode 100644
index 0000000..4602330
--- /dev/null
+++ b/tests/org.eclipse.wtp.releng.tests/about.html
@@ -0,0 +1,28 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
+    "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1"/>
+<title>About</title>
+</head>
+<body lang="EN-US">
+<h2>About This Content</h2>
+ 
+<p>June 2, 2006</p>	
+<h3>License</h3>
+
+<p>The Eclipse Foundation makes available all content in this plug-in (&quot;Content&quot;).  Unless otherwise 
+indicated below, the Content is provided to you under the terms and conditions of the
+Eclipse Public License Version 1.0 (&quot;EPL&quot;).  A copy of the EPL is available 
+at <a href="http://www.eclipse.org/legal/epl-v10.html">http://www.eclipse.org/legal/epl-v10.html</a>.
+For purposes of the EPL, &quot;Program&quot; will mean the Content.</p>
+
+<p>If you did not receive this Content directly from the Eclipse Foundation, the Content is 
+being redistributed by another party (&quot;Redistributor&quot;) and different terms and conditions may
+apply to your use of any object code in the Content.  Check the Redistributor's license that was 
+provided with the Content.  If no such license exists, contact the Redistributor.  Unless otherwise
+indicated below, the terms and conditions of the EPL still apply to any source code in the Content
+and such source code may be obtained at <a href="http://www.eclipse.org">http://www.eclipse.org</a>.</p>
+
+</body>
+</html>
\ No newline at end of file
diff --git a/tests/org.eclipse.wtp.releng.tests/build.properties b/tests/org.eclipse.wtp.releng.tests/build.properties
new file mode 100644
index 0000000..e3e1926
--- /dev/null
+++ b/tests/org.eclipse.wtp.releng.tests/build.properties
@@ -0,0 +1,14 @@
+###############################################################################
+# Copyright (c) 2000, 2005 IBM Corporation and others.
+# All rights reserved. This program and the accompanying materials
+# are made available under the terms of the Eclipse Public License v1.0
+# which accompanies this distribution, and is available at
+# http://www.eclipse.org/legal/epl-v10.html
+#
+# Contributors:
+#     IBM Corporation - initial API and implementation
+###############################################################################
+bin.includes = *.xml,\
+               META-INF/,\
+               .
+source.. = src/
diff --git a/tests/org.eclipse.wtp.releng.tests/plugin.properties b/tests/org.eclipse.wtp.releng.tests/plugin.properties
new file mode 100644
index 0000000..ed2afa6
--- /dev/null
+++ b/tests/org.eclipse.wtp.releng.tests/plugin.properties
@@ -0,0 +1,3 @@
+# properties file for org.eclipse.wtp.releng.tests
+Bundle-Name.0 = WTP Releng Tests
+Bundle-Vendor.0 = Eclipse.org
\ No newline at end of file
diff --git a/tests/org.eclipse.wtp.releng.tests/src/org/eclipse/wtp/releng/tests/BuildTests.java b/tests/org.eclipse.wtp.releng.tests/src/org/eclipse/wtp/releng/tests/BuildTests.java
new file mode 100644
index 0000000..050b02e
--- /dev/null
+++ b/tests/org.eclipse.wtp.releng.tests/src/org/eclipse/wtp/releng/tests/BuildTests.java
@@ -0,0 +1,340 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2006 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *     IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.wtp.releng.tests;
+
+import java.io.*;
+import java.util.*;
+import java.util.zip.ZipException;
+import java.util.zip.ZipFile;
+import junit.framework.TestCase;
+
+import org.eclipse.callisto.tools.versionchecker.VersionLister;
+import org.eclipse.core.runtime.*;
+
+public class BuildTests extends TestCase {
+		
+
+
+	public class FileSuffixFilter implements FilenameFilter {
+
+		private String suffix;
+		
+		public FileSuffixFilter(String suffix) {
+			this.suffix = suffix;
+		}
+		
+		/**
+		 * @see java.io.FilenameFilter#accept(java.io.File, java.lang.String)
+		 */
+		public boolean accept(File dir, String name) {
+			int lastDot = name.lastIndexOf('.');
+			if (lastDot == -1) {
+				return false;
+			}
+			return name.substring(lastDot).equals(suffix);
+		}
+
+	}
+
+	private static final String[] REQUIRED_FEATURE_FILES = {"epl-v10.html", "feature.properties", "feature.xml", "license.html"};
+	private static final String REQUIRED_FEATURE_SUFFIX = ".jpg";
+
+	private static final String[] REQUIRED_PLUGIN_FILES = {"about.html", "plugin.properties", "plugin.xml"};
+	private static final String REQUIRED_PLUGIN_SUFFIX = ".jar";
+		
+	private static final String[] REQUIRED_FEATURE_PLUGIN_FILES = {"about.html", "about.ini", "about.mappings", "about.properties", "plugin.properties", "plugin.xml"};
+	private static final String REQUIRED_FEATURE_PLUGIN_SUFFIX = ".gif";
+
+	private static final String[] REQUIRED_FRAGMENT_FILES = {"fragment.xml"};
+	private static final String REQUIRED_FRAGMENT_SUFFIX = "";
+	
+	private static final String[] REQUIRED_SWT_FRAGMENT_FILES = {"fragment.properties"};
+	private static final String REQUIRED_SWT_FRAGMENT_SUFFIX = "";
+	
+	private static final String[] REQUIRED_SOURCE_FILES = {"about.html"};
+	private static final String REQUIRED_SOURCE_SUFFIX = ".zip";
+	
+	private static final String[] REQUIRED_BUNDLE_FILES = {"about.html"};
+	private static final String REQUIRED_BUNDLE_MANIFEST = "MANIFEST.MF";
+	private static final String REQUIRED_BUNDLE_SUFFIX = ".jar";
+	
+	private static final String[] SUFFIX_EXEMPT_LIST = {"org.eclipse.swt","org.apache.ant"};
+	private static final int EXPECTED_NUMBER_OF_FEATURES = 100;
+	private static final int EXPECTED_NUMBER_OF_PLUGINS = 380;
+	
+	/**
+	 * Constructor for EmptyDirectoriesTest.
+	 * @param arg0
+	 */
+	public BuildTests(String arg0) {
+		super(arg0);
+	}
+
+
+	public void _testFeatureFiles() {
+		List result = new ArrayList();
+		String installDir = Platform.getInstallLocation().getURL().getPath();
+		File featureDir = new File(installDir, "features");
+		File[] features = featureDir.listFiles();
+		
+		for (int i = 0; i < features.length; i++) {
+			File aFeature = features[i];
+			if (!testDirectory(aFeature, REQUIRED_FEATURE_FILES, REQUIRED_FEATURE_SUFFIX)) {
+				result.add(aFeature.getPath());
+			}
+	
+		}
+		
+		String aString = "";
+		if (result.size() > 0) {
+			
+			Iterator iter = result.iterator();
+			while (iter.hasNext()) {
+				String element = (String) iter.next();
+				aString = aString + element + "; ";
+			}
+		}
+		assertTrue("Feature directory missing required files: " + aString, result.size() == 0);
+	}
+
+
+	public void _testNumberOfFeatures() {
+		
+		String installDir = Platform.getInstallLocation().getURL().getPath();
+		File featureDir = new File(installDir, "features");
+		File[] features = featureDir.listFiles();
+		int nFeatures = features.length;
+		assertEquals("there were more features than expected: ", EXPECTED_NUMBER_OF_FEATURES, nFeatures);
+		//System.out.println("nFeatures: " + features.length);
+		
+	}
+	
+	public void _testPluginFiles() {
+		List result = new ArrayList();
+		String installDir = Platform.getInstallLocation().getURL().getPath();
+		File pluginDir = new File(installDir, "plugins");
+		File[] plugins = pluginDir.listFiles();
+		
+		
+		System.out.println("nPlugins: " + plugins.length);
+		
+		
+		for (int i = 0; i < plugins.length; i++) {
+			File aPlugin = plugins[i];
+			if (aPlugin.getName().indexOf("test") == -1) {
+				if (!_testPluginFile(aPlugin)) {
+					result.add(aPlugin.getPath());
+				}
+			}
+		}
+	
+		String aString = "";
+		if (result.size() > 0) {
+	
+			Iterator iter = result.iterator();
+			while (iter.hasNext()) {
+				String element = (String) iter.next();
+				aString = aString + element + "; ";
+			}
+		}
+		assertTrue("Plugin directory missing required files: " + aString, result.size() == 0);
+	}
+
+
+	public void _testNumberOfPlugins() {
+		String installDir = Platform.getInstallLocation().getURL().getPath();
+		File pluginDir = new File(installDir, "plugins");
+		File[] plugins = pluginDir.listFiles();
+		int nPlugins = plugins.length;
+		assertEquals("there were more plugins than expected: ", EXPECTED_NUMBER_OF_PLUGINS, nPlugins);
+		//System.out.println("nPlugins: " + plugins.length);
+
+		
+	}
+
+	private boolean _testPluginFile(File aPlugin) {
+	
+		// Are we a doc plugin?
+		if (testDirectory(aPlugin, REQUIRED_PLUGIN_FILES, ".zip")) {
+			return true;
+		}
+		
+		// Are we a feature plugin?
+		if (testDirectory(aPlugin, REQUIRED_FEATURE_PLUGIN_FILES, REQUIRED_FEATURE_PLUGIN_SUFFIX)) {
+			return true;
+		}
+	
+		// Are we a regular plugin
+		if (testDirectory(aPlugin, REQUIRED_PLUGIN_FILES, REQUIRED_PLUGIN_SUFFIX)) {
+			return true;
+		}			
+		
+		// Are we a source plugin
+		if (testSourcePlugin(aPlugin)) {
+			return true;
+		}
+		
+		// Are we a fragment
+		if ((testDirectory(aPlugin, REQUIRED_FRAGMENT_FILES, REQUIRED_FRAGMENT_SUFFIX))||(testBundleDirectory(aPlugin, REQUIRED_BUNDLE_FILES, REQUIRED_BUNDLE_MANIFEST, REQUIRED_FRAGMENT_SUFFIX))) {
+			return true;
+		}				
+		
+		// Are we an swt fragment
+		if (testDirectory(aPlugin, REQUIRED_SWT_FRAGMENT_FILES, REQUIRED_SWT_FRAGMENT_SUFFIX)) {
+			return true;
+		}		
+		
+		// Are we a bundle?
+		if (testBundleDirectory(aPlugin, REQUIRED_BUNDLE_FILES, REQUIRED_BUNDLE_MANIFEST, REQUIRED_BUNDLE_SUFFIX)) {
+			return true;
+		}
+		
+		// No then we are bad
+		return false;
+	}
+	
+	private boolean testPluginJar(File aDirectory, String[] requiredFiles){
+			ArrayList list = new ArrayList();
+			try {
+				ZipFile jarredPlugin=new ZipFile(aDirectory);
+				Enumeration _enum=jarredPlugin.entries();
+				while (_enum.hasMoreElements()){
+					list.add(_enum.nextElement().toString());
+				}				
+			} catch (ZipException e) {
+				e.printStackTrace();
+			} catch (IOException e) {
+				e.printStackTrace();
+			}
+			if (!list.containsAll(Arrays.asList(requiredFiles))) {
+				return false;
+			}
+			return true;
+	}
+	private boolean testDirectory(File aDirectory, String[] requiredFiles, String requiredSuffix) {
+		if (aDirectory.getName().endsWith(".jar")){
+			return testPluginJar(aDirectory,requiredFiles);
+		} else {
+			if (!Arrays.asList(aDirectory.list()).containsAll(
+					Arrays.asList(requiredFiles))) {
+				return false;
+			}
+
+			int index = aDirectory.getName().indexOf('_');
+			if (index == -1) {
+				index = aDirectory.getName().length();
+			}
+
+			String plainName = aDirectory.getName().substring(0, index);
+
+			/*
+			if (requiredSuffix.equals("")
+					|| Arrays.asList(SUFFIX_EXEMPT_LIST).contains(plainName)) {
+				return true;
+			} else if (aDirectory
+					.listFiles(new FileSuffixFilter(requiredSuffix)).length == 0) {
+				return false;
+			}
+			*/
+		}
+		return true;
+	}
+	
+
+	private boolean testBundleDirectory(File aDirectory, String[] requiredFiles, String manifestFile, String requiredSuffix) {
+		if (aDirectory.getName().endsWith(".jar")) {
+			return testPluginJar(aDirectory, requiredFiles);
+		} else {
+			if (!Arrays.asList(aDirectory.list()).containsAll(
+					Arrays.asList(requiredFiles))) {
+				return false;
+			}
+
+			int index = aDirectory.getName().indexOf('_');
+			if (index == -1) {
+				index = aDirectory.getName().length();
+			}
+
+			String plainName = aDirectory.getName().substring(0, index);
+
+			File metaDir = new File(aDirectory, "META-INF");
+
+			String[] metaFiles = metaDir.list();
+			if (metaFiles == null) {
+				return (false);
+			} else {
+				for (int i = 0; i < metaFiles.length; i++) {
+					String filename = metaFiles[i];
+					if (filename == manifestFile) {
+						return true;
+					}
+				}
+			}
+
+			if (!metaDir.exists()) {
+				return false;
+			}
+
+			if (requiredSuffix.equals("")
+					|| Arrays.asList(SUFFIX_EXEMPT_LIST).contains(plainName)) {
+				return true;
+			} else if (aDirectory
+					.listFiles(new FileSuffixFilter(requiredSuffix)).length == 0) {
+				return false;
+			}
+		}
+		return true;
+	}
+
+	/**
+	 * Return true if the receiver is a source plugin, false otherwise A
+	 * separate method because this is a little tricky.
+	 * 
+	 * @param aPlugin
+	 * @return boolean
+	 */
+	private boolean testSourcePlugin(File aPlugin) {
+		if (!testDirectory(aPlugin, REQUIRED_PLUGIN_FILES, "")) {
+			return false;
+		}
+		
+		File sourceDir = new File(aPlugin, "src");
+		File[] sourceDirs = sourceDir.listFiles();
+		if (sourceDirs == null) {
+			return false;
+		}
+		
+		for (int i = 0; i < sourceDirs.length; i++) {
+			File aSourceDir = sourceDirs[i];
+			if (!testDirectory(aSourceDir, REQUIRED_SOURCE_FILES, REQUIRED_SOURCE_SUFFIX)) {
+				return false;
+			}
+		}
+		return true;
+	}	
+
+	/**
+	 * Compares the feature and plug-in versions contained in this Eclipse 
+	 * configuration, against a known previous Eclipse configuration.
+	 * @throws Exception 
+	 */
+	public void testVersionCompare() throws Exception {
+		
+		VersionLister versionLister = new VersionLister();
+		//String [] args = new String[] {"-listToConsole"};
+		String [] args = new String[] {"-testToReference", "WTPSDK152PLUSPATCHES"};
+		Object results = versionLister.run(args);
+		assertEquals("versionCompare failed. Return code was ", IPlatformRunnable.EXIT_OK, results);
+		
+	}
+}
+
+
diff --git a/tests/org.eclipse.wtp.releng.tests/src/org/eclipse/wtp/releng/tests/FileTool.java b/tests/org.eclipse.wtp.releng.tests/src/org/eclipse/wtp/releng/tests/FileTool.java
new file mode 100644
index 0000000..1a50634
--- /dev/null
+++ b/tests/org.eclipse.wtp.releng.tests/src/org/eclipse/wtp/releng/tests/FileTool.java
@@ -0,0 +1,558 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2005 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *     IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.wtp.releng.tests;
+
+import java.io.BufferedInputStream;
+import java.io.BufferedOutputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.util.ArrayList;
+import java.util.Enumeration;
+import java.util.List;
+import java.util.StringTokenizer;
+import java.util.zip.ZipEntry;
+import java.util.zip.ZipFile;
+import java.util.zip.ZipOutputStream;
+
+
+/**
+ * A tool for performing operations on files.
+ */
+public class FileTool {
+	/**
+	 * A zip filter which is used to filter out unwanted entries
+	 * while extracting a zip file.
+	 * 
+	 * @see FileTool#unzip(IZipFilter, ZipFile, File, Log)
+	 */
+	public interface IZipFilter {
+		/**
+		 * Returns a boolean indicating whether the entry with the
+		 * specified name should be extracted from the zip file.
+		 * 
+		 * @param fullEntryName the full entry name; includes full
+		 * path segments for nested zip entries
+		 * @param entryName the partial entry name; only includes
+		 * path segments from the currect zip entry
+		 * @param depth a number greater than or equal to zero
+		 * which specifies the depth of the current nested zip
+		 * entry
+		 * @return a boolean indicating whether the entry with the
+		 * specified name should be extracted from the zip file
+		 */
+		public boolean shouldExtract(String fullEntryName, String entryName, int depth);
+		/**
+		 * Returns a boolean indicating whether the entry (which
+		 * is a zip/jar file) with the specified name should be
+		 * extracted from the zip file and then unzipped.
+		 * 
+		 * @param fullEntryName the full entry name; includes full
+		 * path segments for nested zip entries
+		 * @param entryName the partial entry name; only includes
+		 * path segments from the currect zip entry
+		 * @param depth a number greater than or equal to zero
+		 * which specifies the depth of the current nested zip
+		 * entry
+		 * @return a boolean indicating whether the entry (which
+		 * is a zip/jar file) with the specified name should be
+		 * extracted from the zip file and then unzipped
+		 */
+		public boolean shouldUnzip(String fullEntryName, String entryName, int depth);
+	}
+	/**
+	 * A buffer.
+	 */
+	private static byte[] buffer = new byte[8192];
+	/**
+	 * Returns the given file path with its separator
+	 * character changed from the given old separator to the
+	 * given new separator.
+	 * 
+	 * @param path a file path
+	 * @param oldSeparator a path separator character
+	 * @param newSeparator a path separator character
+	 * @return the file path with its separator character
+	 * changed from the given old separator to the given new
+	 * separator
+	 */
+	public static String changeSeparator(String path, char oldSeparator, char newSeparator){
+		return path.replace(oldSeparator, newSeparator);
+	}
+	/**
+	 * Returns a boolean indicating whether the given files
+	 * have the same content.
+	 * 
+	 * @param file1 the first file
+	 * @param file2 the second file
+	 * @return a boolean indicating whether the given files
+	 * have the same content
+	 */
+	public static boolean compare(File file1, File file2) throws IOException {
+		if(file1.length() != file2.length()){
+			return false;
+		}
+		InputStream is1 = null;
+		InputStream is2 = null;
+		try {
+			is1 = new BufferedInputStream(new FileInputStream(file1));
+			is2 = new BufferedInputStream(new FileInputStream(file2));
+			int a = 0;
+			int b = 0;
+			boolean same = true;
+			while(same && a != -1 && b != -1){
+				a = is1.read();
+				b = is2.read();
+				same = a == b;
+			}
+			return same;
+		} finally {
+			if(is2 != null){
+				try {
+					is2.close();
+				} catch(IOException e){
+				}
+			}
+			if(is1 != null){
+				try {
+					is1.close();
+				} catch(IOException e){
+				}
+			}
+		}
+	}
+	/**
+	 * Copies the given source file to the given destination file.
+	 * 
+	 * @param src the given source file
+	 * @param dst the given destination file
+	 */
+	public static void copy(File src, File dst) throws IOException {
+		copy(src.getParentFile(), src, dst);
+	}
+	/**
+	 * Copies the given source file to the given destination file.
+	 * 
+	 * @param root
+	 * @param src the given source file
+	 * @param dst the given destination file
+	 */
+	public static void copy(File root, File src, File dst) throws IOException {
+		if(src.isDirectory()){
+			String[] children = src.list();
+			for(int i = 0; i < children.length; ++i){
+				File child = new File(src, children[i]);
+				copy(root, child, dst);
+			}
+		} else {
+			String rootString = root.toString();
+			String srcString = src.toString();
+			File dstFile = new File(dst, srcString.substring(rootString.length() + 1));
+			transferData(src, dstFile);
+		}
+	}
+	/**
+	 * Delete the given file or directory. Directories are
+	 * deleted recursively. If the file or directory can
+	 * not be deleted, a warning message is written to
+	 * stdout.
+	 * 
+	 * @param file a file or directory
+	 */
+	public static void delete(File file) {
+		if(file.exists()){
+			if(file.isDirectory()){
+				String[] children = file.list();
+				for(int i = 0; i < children.length; ++i) {
+					File child = new File(file, children[i]);
+					delete(child);
+				}
+			}
+			if(!file.delete()){
+				System.out.println("WARNING: could not delete " + file);
+			}
+				
+		}
+	}
+	/**
+	 * Returns a new <code>File</code> from the given path
+	 * name segments.
+	 * 
+	 * @param segments the given path name segments
+	 * @return a new <code>File</code> from the given path
+	 * name segments
+	 */
+	public static File getFile(String[] segments) {
+		File result = new File(segments[0]);
+		for(int i = 1; i < segments.length; ++i){
+			result = new File(result, segments[i]);
+		}
+		return result;
+	}
+	/**
+	 * Returns a list of all files below the given directory
+	 * that end with a string in the given include list and
+	 * do not end with a string in the given exclude list.
+	 * If include is <code>null</code> all files are included
+	 * except those that are explicitly excluded. If exclude
+	 * is <code>null</code> no files are excluded except those
+	 * that are not included.
+	 * 
+	 * @param dir the given directory
+	 * @param include a list of filenames to include
+	 * @param exclude a list of filenames to exclude
+	 * @return a list of all files below the given directory
+	 * that are included and not explicitly excluded
+	 */
+	public static File[] getFiles(File dir, String[] include, String[] exclude) {
+		List list = new ArrayList();
+		String[] children = dir.list();
+		if(children == null){
+			return new File[0];
+		}
+		for(int i = 0; i < children.length; ++i){
+			File child = new File(dir, children[i]);
+			String name = child.getName();
+			if(child.isDirectory()){
+				File[] result = getFiles(child, include, exclude);
+				for(int j = 0; j < result.length; ++j){
+					list.add(result[j]);
+				}
+			} else {
+				boolean includeFile = include == null;
+				if(include != null){
+					for(int j = 0; j < include.length; ++j){
+						if(name.endsWith(include[j])){
+							includeFile = true;
+							break;
+						}
+					}
+				}
+				boolean excludeFile = exclude != null;
+				if(exclude != null){
+					for(int j = 0; j < exclude.length; ++j){
+						if(name.endsWith(exclude[j])){
+							excludeFile = true;
+							break;
+						}
+					}
+				}
+				if(includeFile && !excludeFile){
+					list.add(child);
+				}
+			}
+		}
+		return (File[])list.toArray(new File[0]);
+	}
+	/**
+	 * Breaks the given file into its path name segments
+	 * and returns the result.
+	 * 
+	 * @param file a file or directory
+	 * @return the path name segments of the given file
+	 */
+	public static String[] getSegments(File file) {
+		return getSegments(file.toString(), File.separatorChar);
+	}
+	/**
+	 * Breaks the given string into segments and returns the
+	 * result.
+	 * 
+	 * @param s a string
+	 * @param separator the segment separator
+	 * @return the segments of the given string
+	 */
+	public static String[] getSegments(String s, char separator){
+		List result = new ArrayList();
+		StringTokenizer tokenizer = new StringTokenizer(s, "" + separator);
+		while(tokenizer.hasMoreTokens()){
+			result.add(tokenizer.nextToken());
+		}
+		return (String[])result.toArray(new String[0]);
+	}
+	/**
+	 * Returns a vector of <code>File</code> paths parsed from
+	 * the given paths string.
+	 * 
+	 * @param paths a paths string
+	 * @return a vector of <code>File</code> paths parsed from
+	 * the given paths string
+	 */
+	public static File[] parsePaths(String paths){
+		List result = new ArrayList();
+		StringTokenizer tokenizer = new StringTokenizer(paths, ";");
+		while(tokenizer.hasMoreTokens()){
+			result.add(new File(tokenizer.nextToken()));
+		}
+		return (File[])result.toArray(new File[0]);
+	}
+	/**
+	 * Copies all bytes in the given source file to
+	 * the given destination file.
+	 * 
+	 * @param source the given source file
+	 * @param destination the given destination file
+	 */
+	public static void transferData(File source, File destination) throws IOException {
+		destination.getParentFile().mkdirs();
+		InputStream is = null;
+		OutputStream os = null;
+		try {
+			is = new FileInputStream(source);
+			os = new FileOutputStream(destination);
+			transferData(is, os);
+		} finally {
+			if(os != null){
+				try {
+					os.close();
+				} catch(IOException e){
+				}
+			}
+			if(is != null){
+				try {
+					is.close();
+				} catch(IOException e){
+				}
+			}
+		}
+	}
+	/**
+	 * Copies all bytes in the given source stream to
+	 * the given destination stream. Neither streams
+	 * are closed.
+	 * 
+	 * @param source the given source stream
+	 * @param destination the given destination stream
+	 */
+	public static void transferData(InputStream source, OutputStream destination) throws IOException {
+		int bytesRead = 0;
+		while(bytesRead != -1){
+			bytesRead = source.read(buffer, 0, buffer.length);
+			if(bytesRead != -1){
+				destination.write(buffer, 0, bytesRead);
+			}
+		}
+	}
+	/**
+	 * Unzips the given zip file to the given destination directory
+	 * extracting only those entries the pass through the given
+	 * filter.
+	 * 
+	 * @param filter filters out unwanted zip entries
+	 * @param zipFile the zip file to unzip
+	 * @param dstDir the destination directory
+	 */
+	public static void unzip(IZipFilter filter, ZipFile zipFile, File dstDir) throws IOException {
+		unzip(filter, zipFile, dstDir, dstDir, 0);
+	}
+	
+	private static void unzip(IZipFilter filter, ZipFile zipFile, File rootDstDir, File dstDir, int depth) throws IOException {
+	
+		Enumeration entries = zipFile.entries();
+	
+		try {
+			while(entries.hasMoreElements()){
+				ZipEntry entry = (ZipEntry)entries.nextElement();
+				if(entry.isDirectory()){
+					continue;
+				}
+				String entryName = entry.getName();
+				File file = new File(dstDir, FileTool.changeSeparator(entryName, '/', File.separatorChar));
+				String fullEntryName = FileTool.changeSeparator(file.toString().substring(rootDstDir.toString().length() + 1), File.separatorChar, '/');
+				if(!(filter == null || filter.shouldExtract(fullEntryName, entryName, depth))){
+					continue;
+				}
+				file.getParentFile().mkdirs();
+				InputStream src = null;
+				OutputStream dst = null;
+				try {
+					src = zipFile.getInputStream(entry);
+					dst = new FileOutputStream(file);
+					transferData(src, dst);
+				} finally {
+					if(dst != null){
+						try {
+							dst.close();
+						} catch(IOException e){
+						}
+					}
+					if(src != null){
+						try {
+							src.close();
+						} catch(IOException e){
+						}
+					}
+				}
+				if((entryName.endsWith(".zip") || entryName.endsWith(".jar")) && (filter == null || filter.shouldUnzip(fullEntryName, entryName, depth))) {
+					String fileName = file.getName();
+					String dirName = fileName.substring(0, fileName.length() - 4) + "_" + fileName.substring(fileName.length() - 3);
+					ZipFile innerZipFile = null;
+					try {
+						innerZipFile = new ZipFile(file);
+						File innerDstDir = new File(file.getParentFile(), dirName);
+						unzip(filter, innerZipFile, rootDstDir, innerDstDir, depth + 1);
+						file.delete();
+					} catch (IOException e) {
+						if(innerZipFile != null){
+							try {
+								innerZipFile.close();
+							} catch(IOException e2){
+							}
+						}
+						System.out.println("Could not unzip: " + fileName + ". InnerZip = " + innerZipFile.getName() + ". Lenght: " + innerZipFile.getName().length());
+						e.printStackTrace();
+					}
+				
+				}
+			}
+		} finally {
+			try {
+				zipFile.close();
+			} catch(IOException e){
+			}
+		}
+	}
+	/**
+	 * Unzips the inner zip files in the given destination directory
+	 * extracting only those entries the pass through the given
+	 * filter.
+	 * 
+	 * @param filter filters out unwanted zip entries
+	 * @param dstDir the destination directory
+	 */
+	public static void unzip(IZipFilter filter, File dstDir) throws IOException {
+		unzip(filter, dstDir, dstDir, 0);
+	}
+	
+	private static void unzip(IZipFilter filter, File rootDstDir, File dstDir, int depth) throws IOException {
+	
+		File [] entries = rootDstDir.listFiles();
+	
+		try {
+			for(int i=0;i<entries.length;i++){
+				if(entries[i].isDirectory()){
+					unzip (filter,entries[i],dstDir,depth);
+				}
+				File entry = entries[i];
+
+				String entryName = entry.getName();
+				File file = new File(dstDir, FileTool.changeSeparator(entryName, '/', File.separatorChar));
+				if (entryName.endsWith(".zip") || entryName.endsWith(".jar"))  {
+					String fileName = file.getName();
+					String dirName = fileName.substring(0, fileName.length() - 4) + "_" + fileName.substring(fileName.length() - 3);
+					ZipFile innerZipFile = null;
+					try {
+						innerZipFile = new ZipFile(entry);
+						File innerDstDir = new File(entry.getParentFile(), dirName);
+						unzip(filter, innerZipFile, rootDstDir, innerDstDir, depth + 1);
+						//entry.delete();
+					} catch (IOException e) {
+						if(innerZipFile != null){
+							try {
+								innerZipFile.close();
+							} catch(IOException e2){
+							}
+						}
+						System.out.println("Could not unzip: " + fileName + ". InnerZip = " + innerZipFile.getName() + ". Lenght: " + innerZipFile.getName().length());
+						e.printStackTrace();
+					}
+				
+				}
+			}
+		} catch(IOException e){
+			e.printStackTrace();
+		}	
+	}
+	/**
+	 * Zips the given directory to the given zip file.
+	 * Directories are zipped recursively. Inner zip files are
+	 * created for directories that end with "_zip" or "_jar".
+	 * If verbose is true, progress information is logged.
+	 * 
+	 * @param dir the directory to zip
+	 * @param zipFile the resulting zip file
+	 * @param verbose a boolean indicating whether progress
+	 * information is logged
+	 */
+	public static void zip(File dir, File zipFile) throws IOException {
+		BufferedOutputStream bos = null;
+		ZipOutputStream zos = null;
+		try {
+			bos = new BufferedOutputStream(new FileOutputStream(zipFile));
+			zos = new ZipOutputStream(bos);
+			zip(dir, dir, zos);
+		} finally {
+			if(zos == null){
+				if(bos != null){
+					try {
+						bos.close();
+					} catch(IOException e){
+					}
+				}
+			} else {
+				try {
+					zos.close();
+				} catch(IOException e){
+				}
+			}
+		}
+	}
+	private static void zip(File root, File file, ZipOutputStream zos) throws IOException {
+		if(file.isDirectory()){
+			String name = file.getName();
+			if(name.endsWith("_zip") || name.endsWith("_jar")){
+				String rootString = root.toString();
+				String fileString = file.toString();
+				String zipEntryName = fileString.substring(rootString.length() + 1);
+				int underscoreIndex = zipEntryName.lastIndexOf("_");
+				zipEntryName = zipEntryName.substring(0, underscoreIndex) + "." + zipEntryName.substring(underscoreIndex + 1);
+				ZipEntry zipEntry = new ZipEntry(changeSeparator(zipEntryName, File.separatorChar, '/'));
+				zos.putNextEntry(zipEntry);
+				ZipOutputStream zos2 = new ZipOutputStream(zos);
+				String[] list = file.list();
+				for(int i = 0; i < list.length; ++i){
+					File item = new File(file, list[i]);
+					zip(file, item, zos2);
+				}
+				zos2.finish();
+				zos.closeEntry();
+			} else {
+				String[] list = file.list();
+				for(int i = 0; i < list.length; ++i){
+					File item = new File(file, list[i]);
+					zip(root, item, zos);
+				}
+			}
+		} else {
+			String rootString = root.toString();
+			String fileString = file.toString();
+			String zipEntryName = fileString.substring(rootString.length() + 1);
+			ZipEntry zipEntry = new ZipEntry(changeSeparator(zipEntryName, File.separatorChar, '/'));
+			zos.putNextEntry(zipEntry);
+			FileInputStream fos = null;
+			try {
+				fos = new FileInputStream(file);
+				transferData(fos, zos);
+			} finally {
+				if(fos != null){
+					try {
+						fos.close();
+					} catch(IOException e){
+					}
+				}
+			}
+			zos.closeEntry();
+		}
+	}
+}
diff --git a/tests/org.eclipse.wtp.releng.tests/src/org/eclipse/wtp/releng/tests/TestBuild.java b/tests/org.eclipse.wtp.releng.tests/src/org/eclipse/wtp/releng/tests/TestBuild.java
new file mode 100644
index 0000000..59f70e2
--- /dev/null
+++ b/tests/org.eclipse.wtp.releng.tests/src/org/eclipse/wtp/releng/tests/TestBuild.java
@@ -0,0 +1,15 @@
+package org.eclipse.wtp.releng.tests;
+
+import junit.framework.Test;
+import junit.framework.TestSuite;
+
+public class TestBuild extends TestSuite {
+	public static Test suite() {
+		return new TestBuild();
+	}
+
+	public TestBuild() {
+		super("Build Test Suite");
+		addTest(new TestSuite(BuildTests.class, "Build Tests"));
+	}
+}	
\ No newline at end of file
diff --git a/tests/org.eclipse.wtp.releng.tests/test.xml b/tests/org.eclipse.wtp.releng.tests/test.xml
new file mode 100644
index 0000000..71671d5
--- /dev/null
+++ b/tests/org.eclipse.wtp.releng.tests/test.xml
@@ -0,0 +1,121 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<project
+    name="testsuite"
+    default="run"
+    basedir=".">
+    
+    <!-- The following properties should be passed into this script -->
+    <!-- ${eclipse-home}   -->
+    <!-- ${buildDirectory}  -->
+    <!-- ${buildLabel}        -->
+
+    <!-- should be little need to change what's above  -->
+    
+    <property
+        name="plugin-name"
+        value="org.eclipse.wtp.releng.tests" />
+    <property
+        name="classname"
+        value="org.eclipse.wtp.releng.tests.TestBuild" />
+    <property
+        name="testType"
+        value="core-test" />
+
+    <!-- should be little need to change what's below -->
+
+    <echo message="basedir: ${basedir}" />
+    <echo message="eclipse-home: ${eclipse-home}" />
+    <echo message="buildDirectory: ${buildDirectory}" />
+    <echo message="plugin-name: ${plugin-name}" />
+    <echo message="classname: ${classname}" />
+    <echo message="testType ${testType}" />
+
+
+    <property
+        name="library-file"
+        value="${eclipse-home}/plugins/org.eclipse.test_3.1.0/library.xml" />
+    <property
+        name="workspace"
+        value="${eclipse-home}/junitworkspaces/${plugin-name}" />
+        
+    <!-- This target holds all initialization code that needs to be done for -->
+    <!-- all tests that are to be run. Initialization for individual tests -->
+    <!-- should be done within the body of the suite target. -->
+    <target name="init">
+        <tstamp />
+        <delete>
+            <fileset
+                dir="${eclipse-home}"
+                includes="${plugin-name}.*xml" />
+        </delete>
+        <!-- make directory, in case path doesn't exist yet -->
+        <mkdir dir="${workspace}" />
+        <!--  but delete to make sure fresh contents-->
+        <delete
+            dir="${workspace}"
+            quiet="true" />
+
+    </target>
+
+    <!-- This target defines the tests that need to be run. -->
+    <target name="suite">
+
+        <ant
+            target="${testType}"
+            antfile="${library-file}"
+            dir="${eclipse-home}">
+            <property
+                name="data-dir"
+                value="${workspace}" />
+            <property
+                name="plugin-name"
+                value="${plugin-name}" />
+            <property
+                name="classname"
+                value="${classname}" />
+            <property
+                name="plugin-path"
+                value="${eclipse-home}/plugins/${plugin-name}" />
+            <property
+                name="buildDirectory"
+                value="${buildDirectory}" />
+            <property
+                name="buildLabel"
+                value="${buildLabel}" />
+        </ant>
+
+        <copy
+            failonerror="false"
+            file="${workspace}/.metadata/.log"
+            tofile="${buildDirectory}/${buildLabel}/testResults/consolelogs/${plugin-name}.consolelog.txt" />
+
+    </target>
+
+    <!-- This target holds code to cleanup the testing environment after -->
+    <!-- after all of the tests have been run. You can use this target to -->
+    <!-- delete temporary files that have been created. -->
+    <target name="cleanup">
+        <!-- usually no need to delete workspace until next run, and leaving it allows inspection -->
+        <!-- <delete dir="${workspace}" quiet="true" /> -->
+    </target>
+
+    <!-- This target runs the test suite. Any actions that need to happen -->
+    <!-- after all the tests have been run should go here. -->
+    <target
+        name="run"
+        depends="init,suite,cleanup">
+        <ant
+            target="collect"
+            antfile="${library-file}"
+            dir="${eclipse-home}">
+            <property
+                name="includes"
+                value="${plugin-name}.*xml" />
+            <property
+                name="output-file"
+                value="${plugin-name}.xml" />
+        </ant>
+    </target>
+
+</project>
\ No newline at end of file