diff options
Diffstat (limited to 'tests/org.eclipse.team.tests.cvs.core/src/org/eclipse/team/tests/ccvs/ui/benchmark/BenchmarkUtils.java')
-rw-r--r-- | tests/org.eclipse.team.tests.cvs.core/src/org/eclipse/team/tests/ccvs/ui/benchmark/BenchmarkUtils.java | 552 |
1 files changed, 552 insertions, 0 deletions
diff --git a/tests/org.eclipse.team.tests.cvs.core/src/org/eclipse/team/tests/ccvs/ui/benchmark/BenchmarkUtils.java b/tests/org.eclipse.team.tests.cvs.core/src/org/eclipse/team/tests/ccvs/ui/benchmark/BenchmarkUtils.java new file mode 100644 index 000000000..c044ce9a0 --- /dev/null +++ b/tests/org.eclipse.team.tests.cvs.core/src/org/eclipse/team/tests/ccvs/ui/benchmark/BenchmarkUtils.java @@ -0,0 +1,552 @@ +/******************************************************************************* + * Copyright (c) 2000, 2003 IBM Corporation and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Common Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/cpl-v10.html + * + * Contributors: + * IBM Corporation - initial API and implementation + *******************************************************************************/ +package org.eclipse.team.tests.ccvs.ui.benchmark; + + +import java.io.*; +import java.lang.reflect.InvocationTargetException; +import java.util.*; +import java.util.zip.ZipException; +import java.util.zip.ZipFile; + +import junit.framework.Assert; + +import org.eclipse.compare.structuremergeviewer.*; +import org.eclipse.core.resources.*; +import org.eclipse.core.runtime.*; +import org.eclipse.team.internal.ccvs.core.CVSStatus; +import org.eclipse.ui.wizards.datatransfer.ImportOperation; +import org.eclipse.ui.wizards.datatransfer.ZipFileStructureProvider; + +/** + * Provides helpers for: + * <ul> + * <li>Resource manipulation</li> + * <li>Diff trees</li> + * <li>UI automation</li> + * <li>Parallel development simulation</li> + * </ul> + * + * Note: This class is referenced from the VCM 1.0 performance tests. + */ +public class BenchmarkUtils { + /*** RESOURCE MANIPULATION SUPPORT ***/ + + /** + * Gets a handle for a project of a given name. + * @param name the project name + * @return the project handle + */ + public static IProject getProject(String name) throws CoreException { + return ResourcesPlugin.getWorkspace().getRoot().getProject(name); + } + + /** + * Creates a new project. + * @param name the project name + * @return the project handle + */ + public static IProject createProject(String name) throws CoreException { + IProject project = getProject(name); + if (!project.exists()) project.create(null); + if (!project.isOpen()) project.open(null); + return project; + } + + /** + * Deletes a project. + * @param project the project + */ + public static void deleteProject(IProject project) throws CoreException { + project.delete(false /*force*/, null); + } + + /** + * Deletes a file and prunes empty containing folders. + * @param file the file to delete + */ + public static void deleteFileAndPrune(IFile file) throws CoreException { + file.delete(false /*force*/, null); + IContainer container = file.getParent(); + while (container != null && container instanceof IFolder && + isFolderEmpty((IFolder) container)) { + deleteFolder((IFolder) container); + container = container.getParent(); + } + } + + /** + * Deletes a folder. + */ + public static void deleteFolder(IFolder folder) throws CoreException { + try { + folder.delete(false /*force*/, null); + } catch (CoreException e) { + IStatus status = e.getStatus(); + // ignore errors caused by attempting to delete folders that CVS needs to have around + if (findStatusByCode(status, CVSStatus.FOLDER_NEEDED_FOR_FILE_DELETIONS) == null) { + throw e; + } + } + } + + /** + * Finds an IStatus instance in a multi-status by status code. + */ + public static IStatus findStatusByCode(IStatus status, int code) { + if (status.getCode() == code) return status; + IStatus[] children = status.getChildren(); + for (int i = 0; i < children.length; i++) { + IStatus found = findStatusByCode(children[i], code); + if (found != null) return found; + } + return null; + } + + /** + * Creates a uniquely named project. + * @param prefix a string prepended to the generated name + * @return the new project + */ + public static IProject createUniqueProject(String prefix) throws CoreException { + return createProject(makeUniqueName(null, prefix, null)); + } + + /** + * Creates a uniquely named file in the parent folder or project with random contents. + * @param gen the sequence generator + * @param parent the parent IFolder or IProject for the new file + * @param meanSize the mean size of file to create (in bytes) + * @param variance 69% of files with be within this amount of the mean + * @param probBinary the probability of a new file being binary as a percentage + * @return the new file + */ + public static IFile createUniqueFile(SequenceGenerator gen, IContainer parent, + int meanSize, int variance, int probBinary) throws IOException, CoreException { + int fileSize; + do { + fileSize = (int) Math.abs(gen.nextGaussian() * variance + meanSize); + } while (fileSize > meanSize + variance * 4); // avoid huge files + + ByteArrayOutputStream os = new ByteArrayOutputStream(); + String fileName; + if (gen.nextInt(100) < probBinary) { + fileName = makeUniqueName(gen, "file", "class"); // binary + writeRandomBytes(gen, os, fileSize); + } else { + fileName = makeUniqueName(gen, "file", "txt"); // text + writeRandomText(gen, os, fileSize); + } + IFile file = parent.getFile(new Path(fileName)); + file.create(new ByteArrayInputStream(os.toByteArray()), true, null); + os.close(); + return file; + } + + /** + * Creates a uniquely named folder in the parent folder. + * @param gen the sequence generator + * @param parent the parent IFolder or IProject for the new folder + * @return the new folder + */ + public static IFolder createUniqueFolder(SequenceGenerator gen, IContainer parent) throws CoreException { + IFolder folder = parent.getFolder(new Path(BenchmarkUtils.makeUniqueName(gen, "folder", null))); + folder.create(false /*force*/, true /*local*/, null); + return folder; + } + + /** + * Renames a resource. + * The resource handle becomes invalid. + * @param resource the existing resource + * @param newName the new name for the resource + */ + public static void renameResource(IResource resource, String newName) throws CoreException { + switch (resource.getType()) { + case IResource.PROJECT: { + IProject project = (IProject) resource; + IProjectDescription desc = project.getDescription(); + desc.setName(newName); + project.move(desc, false /*force*/, true /*keepHistory*/, null); + } break; + case IResource.FOLDER: { + try { + resource.move(new Path(newName), false /*force*/, null); + } catch (CoreException e) { + IStatus status = e.getStatus(); + // ignore errors caused by attempting to delete folders that CVS needs to have around + if (findStatusByCode(status, CVSStatus.FOLDER_NEEDED_FOR_FILE_DELETIONS) == null) { + throw e; + } + } + } break; + default: + resource.move(new Path(newName), false /*force*/, null); + break; + } + } + + /** + * Modified a resource. + * @param gen the sequence generator + * @param file the file to modify + */ + public static void modifyFile(SequenceGenerator gen, IFile file) + throws IOException, CoreException { + ByteArrayOutputStream os = new ByteArrayOutputStream(); + try { + InputStream is = file.getContents(true); + try { + byte[] buffer = new byte[8192]; + int rsize; + boolean changed = false; + while ((rsize = is.read(buffer)) != -1) { + double gaussian; + do { + gaussian = gen.nextGaussian() * 0.5; // large changes are less likely than small ones + } while (gaussian > 1.0 || gaussian < -1.0); + int changeSize = (int) (gaussian * rsize); + changed = changed || changeSize != 0; + os.write(buffer, 0, changeSize < 0 ? - changeSize : rsize); // shrink file + writeRandomText(gen, os, changeSize); // enlarge file + } + if (! changed) os.write('!'); // make sure we actually did change the file + file.setContents(new ByteArrayInputStream(os.toByteArray()), false /*force*/, true /*keepHistory*/, null); + } finally { + is.close(); + } + } finally { + os.close(); + } + } + + /** + * Creates a unique name. + * Ensures that a deterministic sequence of names is generated for all files + * and folders within a project, though not across sessions. + * + * @param gen the generator, or null if this name is to be globally unique + * @param prefix a string prepended to the generated name + * @param extension the file extension not including the period, null if none + * @return the new name + */ + public static String makeUniqueName(SequenceGenerator gen, String prefix, String extension) + throws CoreException { + StringBuffer name = new StringBuffer(prefix); + name.append('-'); + if (gen == null) { + name.append(SequenceGenerator.nextGloballyUniqueLong()); + } else { + name.append(gen.nextUniqueInt()); + } + if (extension != null) { + name.append('.'); + name.append(extension); + } + return name.toString(); + } + + /** + * Imports a .zip file into a container's root folder. + * @param container the container + * @param file the path of the .zip file + */ + public static void importZip(IContainer container, File file) + throws IOException, ZipException, InterruptedException, InvocationTargetException { + ZipFile zipFile = new ZipFile(file); + ZipFileStructureProvider provider = new ZipFileStructureProvider(zipFile); + ImportOperation importOperation = new ImportOperation(container.getFullPath(), + provider.getRoot(), provider, null); + importOperation.setOverwriteResources(true); // don't ask + importOperation.run(new NullProgressMonitor()); + Assert.assertTrue(importOperation.getStatus().isOK()); + } + + /** + * Writes random text to an output stream. + * @param gen the sequence generator + */ + public static void writeRandomText(SequenceGenerator gen, OutputStream os, int count) throws IOException { + while (count-- > 0) { + int c = gen.nextInt(99); + os.write((c >= 95) ? '\n' : c + ' '); + } + } + + /** + * Writes random bytes to an output stream. + * @param gen the sequence generator + */ + public static void writeRandomBytes(SequenceGenerator gen, OutputStream os, int count) throws IOException { + while (count-- > 0) { + os.write(gen.nextInt(256)); + } + } + + /** + * Creates a random folder deeply below the root folder. + * @param gen the sequence generator + * @param root the root IFolder or IProject for the operation + * @return the new folder + */ + public static IFolder createRandomDeepFolder(SequenceGenerator gen, IContainer root) throws CoreException { + IContainer container = pickRandomDeepContainer(gen, root); + for (;;) { + IFolder folder = createUniqueFolder(gen, container); + container = folder; + // 12.5% chance of creating a nested folder + if (gen.nextInt(8) != 0) return folder; + } + } + + /** + * Creates several random files deeply below the root folder. + * @param gen the sequence generator + * @param root the root IFolder or IProject for the operation + * @param count the number of files to create + * @param meanSize the mean size of file to create (in bytes) + * @param probBinary the probability of a new file being binary as a percentage + */ + public static void createRandomDeepFiles(SequenceGenerator gen, IContainer root, int count, + int meanSize, int variance, int probBinary) throws IOException, CoreException { + while (count-- > 0) { + createUniqueFile(gen, pickRandomDeepContainer(gen, root), meanSize, variance, probBinary); + } + } + + /** + * Deletes several random files deeply below the root folder. + * @param gen the sequence generator + * @param root the root IFolder or IProject for the operation + * @param count the number of files to delete + */ + public static void deleteRandomDeepFiles(SequenceGenerator gen, IContainer root, int count) throws CoreException { + while (count-- > 0) { + IFile file = pickRandomDeepFile(gen, root); + if (file == null) break; + deleteFileAndPrune(file); + } + } + + /** + * Modifies several random files deeply below the root folder. + * @param gen the sequence generator + * @param root the root IFolder or IProject for the operation + * @param count the number of files to modify + */ + public static void modifyRandomDeepFiles(SequenceGenerator gen, IContainer root, int count) + throws IOException, CoreException { + // perhaps we can add a parameter for the "magnitude" of the change + while (count-- > 0) { + IFile file = pickRandomDeepFile(gen, root); + if (file == null) break; + modifyFile(gen, file); + } + } + + /** + * Touches several random files deeply below the root folder. + * @param gen the sequence generator + * @param root the root IFolder or IProject for the operation + * @param count the number of files to touch + */ + public static void touchRandomDeepFiles(SequenceGenerator gen, IContainer root, int count) throws CoreException { + while (count-- > 0) { + IFile file = pickRandomDeepFile(gen, root); + if (file == null) break; + file.touch(null); + } + } + + /** + * Renames several random files deeply below the root folder. + * @param gen the sequence generator + * @param root the root IFolder or IProject for the operation + * @param count the number of files to touch + */ + public static void renameRandomDeepFiles(SequenceGenerator gen, IContainer root, int count) throws CoreException { + IProject project = root.getProject(); + while (count-- > 0) { + IFile file = pickRandomDeepFile(gen, root); + if (file == null) break; + renameResource(file, makeUniqueName(gen, "file", file.getFileExtension())); + } + } + + /** + * Picks a random file from the parent folder or project. + * @param gen the sequence generator + * @param parent the parent IFolder or IProject for the operation + * @return the file that was chosen, or null if no suitable files + */ + public static IFile pickRandomFile(SequenceGenerator gen, IContainer parent) throws CoreException { + IResource[] members = filterResources(parent.members()); + for (int size = members.length; size != 0; --size) { + int elem = gen.nextInt(size); + if (members[elem] instanceof IFile) return (IFile) members[elem]; + System.arraycopy(members, elem + 1, members, elem, size - elem - 1); + } + return null; + } + + /** + * Picks a random folder from the parent folder or project. + * @param gen the sequence generator + * @param parent the parent IFolder or IProject for the operation + * @return the folder, or null if no suitable folders + */ + public static IFolder pickRandomFolder(SequenceGenerator gen, IContainer parent) throws CoreException { + IResource[] members = filterResources(parent.members()); + for (int size = members.length; size != 0; --size) { + int elem = gen.nextInt(size); + if (members[elem] instanceof IFolder) return (IFolder) members[elem]; + System.arraycopy(members, elem + 1, members, elem, size - elem - 1); + } + return null; + } + + /** + * Picks a random file deeply from the root folder or project. + * @param gen the sequence generator + * @param root the root IFolder or IProject for the operation + * @return the file that was chosen, or null if no suitable files + */ + public static IFile pickRandomDeepFile(SequenceGenerator gen, IContainer root) throws CoreException { + IResource[] members = filterResources(root.members()); + for (int size = members.length; size != 0; --size) { + int elem = gen.nextInt(size); + IResource resource = members[elem]; + if (resource instanceof IFile) return (IFile) resource; + if (resource instanceof IFolder) { + IFile file = pickRandomDeepFile(gen, (IFolder) resource); + if (file != null) return file; + } + System.arraycopy(members, elem + 1, members, elem, size - elem - 1); + } + return null; + } + + /** + * Picks a random folder deeply from the root folder or project. + * May pick the project's root container. + * @param gen the sequence generator + * @param root the root IFolder or IProject for the operation + * @return the container that was chosen, never null + */ + public static IContainer pickRandomDeepContainer(SequenceGenerator gen, IContainer root) throws CoreException { + if (gen.nextInt(6) == 0) { + IResource[] members = filterResources(root.members()); + for (int size = members.length; size != 0; --size) { + int elem = gen.nextInt(size); + IResource resource = members[elem]; + if (resource instanceof IFolder) { + return pickRandomDeepContainer(gen, (IFolder) resource); + } + System.arraycopy(members, elem + 1, members, elem, size - elem - 1); + } + } + Assert.assertTrue(isValidContainer(root)); + return root; + } + + /** + * Returns true if the folder does not contain any real files. + */ + public static boolean isFolderEmpty(IFolder folder) throws CoreException { + IResource[] members = folder.members(); + for (int i = 0; i < members.length; ++i) { + if (isValidFile(members[i]) || isValidFolder(members[i])) return false; + } + return true; + } + + /** + * Returns true iff file is a valid IFile (that should not be ignored). + */ + public static boolean isValidFile(IResource file) throws CoreException { + String name = file.getName(); + return file instanceof IFile + && ! file.isPhantom() + && ! name.equals(".classpath") + && ! name.equals(".project") + && ! name.equals(".vcm_meta"); + } + + /** + * Returns true iff folder is a valid IFolder (that should not be ignored). + */ + public static boolean isValidFolder(IResource folder) throws CoreException { + String name = folder.getName(); + return folder instanceof IFolder + && ! folder.isPhantom() + && ! name.equals("CVS") + && ! name.equals("bin"); + } + + /** + * Returns true iff container is a valid IFolder or IProject (that should not be ignored). + */ + public static boolean isValidContainer(IResource container) throws CoreException { + return container instanceof IProject || isValidFolder(container); + } + + /** + * Returns true iff resource is a valid IFile, IFolder or IProject (that should not be ignored). + */ + public static boolean isValidResource(IResource resource) throws CoreException { + return isValidFile(resource) || isValidContainer(resource); + } + + /** + * Filters and sorts an array of resources to ensure deterministic behaviour across + * sessions. The general idea is to guarantee that given a known sequence of + * pseudo-random numbers, we will always pick the same sequence of files and + * folders each time we repeat the test. + */ + public static IResource[] filterResources(IResource[] resources) throws CoreException { + List list = new ArrayList(resources.length); + for (int i = 0; i < resources.length; ++i) { + if (isValidResource(resources[i])) list.add(resources[i]); + } + if (list.size() != resources.length) { + resources = (IResource[]) list.toArray(new IResource[list.size()]); + } + Arrays.sort(resources, new Comparator() { + public int compare(Object a, Object b) { + return ((IResource) a).getName().compareTo(((IResource) b).getName()); + } + }); + return resources; + } + + /*** DIFF SUPPORT ***/ + + public static boolean isEmpty(IDiffContainer node) { + if (node == null) return true; + if (node.getKind() != 0) return false; + IDiffElement[] children = node.getChildren(); + for (int i = 0; i < children.length; i++) { + if (!isEmpty(children[i])) return false; + } + return true; + } + public static boolean isEmpty(IDiffElement element) { + if (element == null) return true; + if (element.getKind() != 0) return false; + if (element instanceof IDiffContainer) { + IDiffElement[] children = ((DiffNode)element).getChildren(); + for (int i = 0; i < children.length; i++) { + if (!isEmpty(children[i])) return false; + } + } + return true; + } +} |