tests: improve helper utilities
diff --git a/core/plugins/org.eclipse.dltk.core/utils/org/eclipse/dltk/utils/ResourceUtil.java b/core/plugins/org.eclipse.dltk.core/utils/org/eclipse/dltk/utils/ResourceUtil.java
index 22bf680..1b762d8 100644
--- a/core/plugins/org.eclipse.dltk.core/utils/org/eclipse/dltk/utils/ResourceUtil.java
+++ b/core/plugins/org.eclipse.dltk.core/utils/org/eclipse/dltk/utils/ResourceUtil.java
@@ -1,5 +1,5 @@
 /*******************************************************************************
- * Copyright (c) 2010 xored software, Inc.
+ * Copyright (c) 2010, 2013 xored software, Inc and others.
  *
  * All rights reserved. This program and the accompanying materials
  * are made available under the terms of the Eclipse Public License v1.0
@@ -8,11 +8,15 @@
  *
  * Contributors:
  *     xored software, Inc. - initial API and Implementation (Alex Panchenko)
+ *     NumberFour AG - createFolder() added (Alex Panchenko)
  *******************************************************************************/
 package org.eclipse.dltk.utils;
 
+import org.eclipse.core.resources.IContainer;
+import org.eclipse.core.resources.IFolder;
 import org.eclipse.core.resources.IProject;
 import org.eclipse.core.resources.IProjectDescription;
+import org.eclipse.core.resources.IResource;
 import org.eclipse.core.runtime.CoreException;
 import org.eclipse.core.runtime.IProgressMonitor;
 import org.eclipse.core.runtime.OperationCanceledException;
@@ -39,4 +43,17 @@
 		}
 	}
 
+	/**
+	 * Creates a folder and all parent folders if not existing.
+	 */
+	public static void createFolder(IFolder folder, IProgressMonitor monitor)
+			throws CoreException {
+		if (!folder.exists()) {
+			final IContainer parent = folder.getParent();
+			if (parent instanceof IFolder) {
+				createFolder((IFolder) parent, null);
+			}
+			folder.create(IResource.FORCE, true, monitor);
+		}
+	}
 }
diff --git a/core/tests/org.eclipse.dltk.core.tests/src/org/eclipse/dltk/core/tests/AbstractProjectSetup.java b/core/tests/org.eclipse.dltk.core.tests/src/org/eclipse/dltk/core/tests/AbstractProjectSetup.java
index a7f92ad..473500d 100644
--- a/core/tests/org.eclipse.dltk.core.tests/src/org/eclipse/dltk/core/tests/AbstractProjectSetup.java
+++ b/core/tests/org.eclipse.dltk.core.tests/src/org/eclipse/dltk/core/tests/AbstractProjectSetup.java
@@ -11,7 +11,12 @@
  *******************************************************************************/
 package org.eclipse.dltk.core.tests;
 
+import java.io.ByteArrayInputStream;
+import java.io.UnsupportedEncodingException;
+
+import org.eclipse.core.resources.IContainer;
 import org.eclipse.core.resources.IFile;
+import org.eclipse.core.resources.IFolder;
 import org.eclipse.core.resources.IMarker;
 import org.eclipse.core.resources.IProject;
 import org.eclipse.core.resources.IResource;
@@ -22,6 +27,7 @@
 import org.eclipse.core.runtime.CoreException;
 import org.eclipse.core.runtime.IPath;
 import org.eclipse.core.runtime.Path;
+import org.eclipse.dltk.annotations.Nullable;
 import org.eclipse.dltk.core.DLTKCore;
 import org.eclipse.dltk.core.IDLTKLanguageToolkit;
 import org.eclipse.dltk.core.IModelElement;
@@ -36,14 +42,20 @@
 import org.eclipse.dltk.core.search.SearchEngine;
 import org.eclipse.dltk.core.search.SearchParticipant;
 import org.eclipse.dltk.core.search.SearchPattern;
+import org.eclipse.dltk.core.tests.ProjectSetup.Option;
 import org.eclipse.dltk.core.tests.model.TestSearchResults;
 import org.eclipse.dltk.internal.core.util.Util;
+import org.eclipse.dltk.utils.ResourceUtil;
 import org.eclipse.osgi.util.NLS;
 import org.junit.Assert;
 import org.junit.rules.ExternalResource;
 
 public abstract class AbstractProjectSetup extends ExternalResource {
 
+	protected boolean isVerbose() {
+		return false;
+	}
+
 	public abstract IProject get();
 
 	public abstract String getProjectName();
@@ -56,6 +68,13 @@
 	}
 
 	/**
+	 * Returns the specified folder from this project.
+	 */
+	public IFolder getFolder(String name) {
+		return get().getFolder(name);
+	}
+
+	/**
 	 * Returns the specified file from this project.
 	 */
 	public IFile getFile(String name) {
@@ -426,7 +445,77 @@
 	 * Performs the incremental build in this project.
 	 */
 	public void build() throws CoreException {
+		final long start = System.currentTimeMillis();
 		get().build(IncrementalProjectBuilder.INCREMENTAL_BUILD, null);
+		if (isVerbose()) {
+			System.out.println((System.currentTimeMillis() - start)
+					+ " ms for incremental build of " + getProjectName()
+					+ " project");
+		}
+	}
+
+	/**
+	 * Writes the specified content to the specfied content, creating the file
+	 * and all the parent folders if the don't exist.
+	 * 
+	 * @param fileName
+	 * @param contents
+	 * @return
+	 * @throws UnsupportedEncodingException
+	 * @throws CoreException
+	 */
+	public IFile writeFile(String fileName, String contents)
+			throws CoreException {
+		final IFile file = getFile(fileName);
+		final byte[] bytes;
+		try {
+			bytes = contents.getBytes(file.getCharset());
+		} catch (UnsupportedEncodingException e) {
+			throw new IllegalStateException(e);
+		}
+		final ByteArrayInputStream input = new ByteArrayInputStream(bytes);
+		if (file.exists()) {
+			file.setContents(input, IResource.NONE, null);
+		} else {
+			final IContainer parent = file.getParent();
+			if (parent instanceof IFolder) {
+				ResourceUtil.createFolder((IFolder) parent, null);
+			}
+			file.create(input, IResource.NONE, null);
+		}
+		return file;
+	}
+
+	/**
+	 * Deletes all the members in the specified folder. If some members were
+	 * deleted and option == {@link Option#BUILD} then the build operation is
+	 * also executed.
+	 * 
+	 * @param folderName
+	 *            folder name
+	 * @param option
+	 *            {@link Option#BUILD} or <code>null</code>
+	 * @throws CoreException
+	 */
+	public void deleteFolderMembers(String folderName, @Nullable Option option)
+			throws CoreException {
+		if (option != null) {
+			if (option != Option.BUILD) {
+				throw new IllegalArgumentException();
+			}
+		}
+		final IFolder folder = getFolder(folderName);
+		if (!folder.exists()) {
+			return;
+		}
+		int count = 0;
+		for (IResource member : folder.members()) {
+			member.delete(true, null);
+			++count;
+		}
+		if (count != 0 && option == Option.BUILD) {
+			build();
+		}
 	}
 
 }
diff --git a/core/tests/org.eclipse.dltk.core.tests/src/org/eclipse/dltk/core/tests/ProjectSetup.java b/core/tests/org.eclipse.dltk.core.tests/src/org/eclipse/dltk/core/tests/ProjectSetup.java
index bd31106..c0a428c 100644
--- a/core/tests/org.eclipse.dltk.core.tests/src/org/eclipse/dltk/core/tests/ProjectSetup.java
+++ b/core/tests/org.eclipse.dltk.core.tests/src/org/eclipse/dltk/core/tests/ProjectSetup.java
@@ -41,7 +41,7 @@
 public class ProjectSetup extends AbstractProjectSetup {
 
 	public static enum Option {
-		BUILD, INDEXER_DISABLED, WAIT_INDEXES_READY
+		BUILD, INDEXER_DISABLED, WAIT_INDEXES_READY, VERBOSE
 	}
 
 	/**
@@ -91,8 +91,9 @@
 		}
 	}
 
+	@Override
 	protected boolean isVerbose() {
-		return false;
+		return options.contains(Option.VERBOSE);
 	}
 
 	@Override
@@ -108,7 +109,7 @@
 			buildProject();
 			if (isVerbose()) {
 				System.out.println((System.currentTimeMillis() - start)
-						+ " ms to build " + projectName + " project");
+						+ " ms for full build of " + projectName + " project");
 			}
 		}
 		if (options.contains(Option.WAIT_INDEXES_READY)) {
diff --git a/core/tests/org.eclipse.dltk.core.tests/src/org/eclipse/dltk/core/tests/model/AbstractModelTests.java b/core/tests/org.eclipse.dltk.core.tests/src/org/eclipse/dltk/core/tests/model/AbstractModelTests.java
index de86b06..386823c 100644
--- a/core/tests/org.eclipse.dltk.core.tests/src/org/eclipse/dltk/core/tests/model/AbstractModelTests.java
+++ b/core/tests/org.eclipse.dltk.core.tests/src/org/eclipse/dltk/core/tests/model/AbstractModelTests.java
@@ -213,6 +213,9 @@
 			if (sourceChild.isDirectory()) {
 				copyDirectory(sourceChild, targetChild);
 			} else {
+				if (".emptydir".equals(name)) {
+					continue;
+				}
 				copy(sourceChild, targetChild);
 			}
 		}