/******************************************************************************* * Copyright (c) 2000, 2011 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.team.tests.ccvs.core; import java.io.BufferedInputStream; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.io.OutputStream; import java.io.PrintStream; import java.io.Reader; import java.lang.reflect.InvocationTargetException; import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; import junit.framework.AssertionFailedError; import junit.framework.Test; import junit.framework.TestSuite; import org.eclipse.core.resources.IContainer; import org.eclipse.core.resources.IFile; import org.eclipse.core.resources.IProject; import org.eclipse.core.resources.IResource; import org.eclipse.core.resources.IResourceVisitor; import org.eclipse.core.resources.IWorkspaceRoot; import org.eclipse.core.resources.IWorkspaceRunnable; import org.eclipse.core.resources.ResourceAttributes; import org.eclipse.core.resources.mapping.ResourceMapping; import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.IPath; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.core.runtime.IStatus; import org.eclipse.core.runtime.NullProgressMonitor; import org.eclipse.core.runtime.Path; import org.eclipse.core.runtime.jobs.Job; import org.eclipse.core.tests.resources.ResourceTest; import org.eclipse.swt.widgets.Display; import org.eclipse.team.core.RepositoryProvider; import org.eclipse.team.core.TeamException; import org.eclipse.team.internal.ccvs.core.CVSException; import org.eclipse.team.internal.ccvs.core.CVSStatus; import org.eclipse.team.internal.ccvs.core.CVSTag; import org.eclipse.team.internal.ccvs.core.CVSTeamProvider; import org.eclipse.team.internal.ccvs.core.ICVSFile; import org.eclipse.team.internal.ccvs.core.ICVSFolder; import org.eclipse.team.internal.ccvs.core.ICVSRemoteFile; import org.eclipse.team.internal.ccvs.core.ICVSRemoteResource; import org.eclipse.team.internal.ccvs.core.ICVSRepositoryLocation; import org.eclipse.team.internal.ccvs.core.ICVSResource; import org.eclipse.team.internal.ccvs.core.Policy; import org.eclipse.team.internal.ccvs.core.client.Command; import org.eclipse.team.internal.ccvs.core.client.Command.LocalOption; import org.eclipse.team.internal.ccvs.core.client.Import; import org.eclipse.team.internal.ccvs.core.client.Session; import org.eclipse.team.internal.ccvs.core.client.Update; import org.eclipse.team.internal.ccvs.core.connection.CVSCommunicationException; import org.eclipse.team.internal.ccvs.core.connection.CVSRepositoryLocation; import org.eclipse.team.internal.ccvs.core.connection.CVSServerException; import org.eclipse.team.internal.ccvs.core.resources.CVSWorkspaceRoot; import org.eclipse.team.internal.ccvs.core.resources.RemoteFile; import org.eclipse.team.internal.ccvs.core.resources.RemoteFolder; import org.eclipse.team.internal.ccvs.core.resources.RemoteFolderTree; import org.eclipse.team.internal.ccvs.core.syncinfo.FolderSyncInfo; import org.eclipse.team.internal.ccvs.core.syncinfo.ResourceSyncInfo; import org.eclipse.team.internal.ccvs.core.util.SyncFileChangeListener; import org.eclipse.team.internal.ccvs.ui.mappings.ModelReplaceOperation; import org.eclipse.team.internal.ccvs.ui.mappings.ModelUpdateOperation; import org.eclipse.team.internal.ccvs.ui.operations.AddOperation; import org.eclipse.team.internal.ccvs.ui.operations.BranchOperation; import org.eclipse.team.internal.ccvs.ui.operations.CVSOperation; import org.eclipse.team.internal.ccvs.ui.operations.CheckoutSingleProjectOperation; import org.eclipse.team.internal.ccvs.ui.operations.CommitOperation; import org.eclipse.team.internal.ccvs.ui.operations.ITagOperation; import org.eclipse.team.internal.ccvs.ui.operations.ReplaceOperation; import org.eclipse.team.internal.ccvs.ui.operations.RepositoryProviderOperation; import org.eclipse.team.internal.ccvs.ui.operations.ShareProjectOperation; import org.eclipse.team.internal.ccvs.ui.operations.TagInRepositoryOperation; import org.eclipse.team.internal.ccvs.ui.operations.TagOperation; import org.eclipse.team.internal.ccvs.ui.operations.UpdateOperation; import org.eclipse.team.internal.ccvs.ui.operations.WorkspaceResourceMapper; import org.eclipse.team.internal.core.subscribers.SubscriberSyncInfoCollector; import org.eclipse.team.ui.TeamOperation; import org.eclipse.ui.IWorkbenchPart; import org.eclipse.ui.internal.decorators.DecoratorManager; public class EclipseTest extends ResourceTest { private static final int LOCK_WAIT_TIME = 1000; private static final String CVS_TEST_LOCK_FILE = ".lock"; private static final String CVS_TEST_LOCK_PROJECT = "cvsTestLock"; protected static IProgressMonitor DEFAULT_MONITOR = new NullProgressMonitor(); protected static final int RANDOM_CONTENT_SIZE = 3876; protected static String eol = System.getProperty("line.separator"); private static boolean modelSync = true; private static final long LOCK_EXPIRATION_THRESHOLD = 1000 * 60 * 10; // 10 minutes private static final int MAX_LOCK_ATTEMPTS = 60 * 30; // 30 minutes private String lockId; public static Test suite(Class c) { String testName = System.getProperty("eclipse.cvs.testName"); if (testName == null) { TestSuite suite = new TestSuite(c); return new CVSTestSetup(suite); } else { try { return new CVSTestSetup((Test)c.getConstructor(new Class[] { String.class }).newInstance(new Object[] {testName})); } catch (Exception e) { fail(e.getMessage()); // Above will throw so below is never actually reached return null; } } } public static boolean isModelSyncEnabled() { return modelSync; } public static void setModelSync(boolean modelSync) { EclipseTest.modelSync = modelSync; } public EclipseTest() { super(); if (eol == null) eol = "\n"; } public EclipseTest(String name) { super(name); if (eol == null) eol = "\n"; } public ICVSRemoteResource getRemoteTree(IResource resource, CVSTag tag, IProgressMonitor progress) throws TeamException { return CVSWorkspaceRoot.getRemoteTree(resource, tag, false /* cache file contents hint */, IResource.DEPTH_INFINITE, progress); } /* * Get the resources for the given resource names */ public IResource[] getResources(IContainer container, String[] hierarchy) { IResource[] resources = new IResource[hierarchy.length]; for (int i=0;i MAX_LOCK_ATTEMPTS) { write(new CVSStatus(IStatus.ERROR, "Could not obtain the CVS server lock. The test will continue but any performance timings may be affected", new Exception()), 0); return; } } } } private boolean hasExpired(IFile lockFile) { long timestamp = lockFile.getLocalTimeStamp(); return System.currentTimeMillis() - timestamp > LOCK_EXPIRATION_THRESHOLD; } private void overwriteLock(IFile lockFile) throws CoreException { lockFile.setContents(getLockContents(), true, true, null); commitResources(new IResource[] { lockFile }, IResource.DEPTH_ZERO); } private void writeLock(IFile lockFile) throws CoreException { lockFile.create(getLockContents(), false, null); addResources(new IResource[] { lockFile }); commitResources(new IResource[] { lockFile }, IResource.DEPTH_ZERO); } private InputStream getLockContents() { lockId = Long.toString(System.currentTimeMillis()); return new ByteArrayInputStream(lockId.getBytes()); } private void createTestLockProject(IProgressMonitor monitor) throws TeamException { CVSRepositoryLocation repository = getRepository(); RemoteFolderTree root = new RemoteFolderTree(null, repository, Path.EMPTY.toString(), null); RemoteFolderTree child = new RemoteFolderTree(root, CVS_TEST_LOCK_PROJECT, repository, new Path(null, root.getRepositoryRelativePath()).append(CVS_TEST_LOCK_PROJECT).toString(), null); root.setChildren(new ICVSRemoteResource[] { child }); Session s = new Session(repository, root); s.open(monitor, true /* open for modification */); try { IStatus status = Command.ADD.execute(s, Command.NO_GLOBAL_OPTIONS, Command.NO_LOCAL_OPTIONS, new String[] { CVS_TEST_LOCK_PROJECT }, null, monitor); // If we get a warning, the operation most likely failed so check that the status is OK if (status.getCode() == CVSStatus.SERVER_ERROR || ! status.isOK()) { throw new CVSServerException(status); } } finally { s.close(); } } private void releaseCVSServerLock() { if (lockId != null) { try { IProject project = getWorkspace().getRoot().getProject(CVS_TEST_LOCK_PROJECT); // Update the project and verify we still have the lock IFile file = project.getFile(CVS_TEST_LOCK_FILE); String id = getFileContents(file); if (id.equals(lockId)) { // We have the lock so let's free it (but first check if someone preempted us) ICVSFile cvsFile = CVSWorkspaceRoot.getCVSFileFor(file); byte[] bytes = cvsFile.getSyncBytes(); if (bytes != null) { String revision = ResourceSyncInfo.getRevision(bytes); updateResources(new IResource[] { file }, true); bytes = cvsFile.getSyncBytes(); if (bytes == null || !ResourceSyncInfo.getRevision(bytes).equals(revision)) { write(new CVSStatus(IStatus.ERROR, "The CVS server lock expired while this test was running. Any performance timings may be affected", new Exception()), 0); return; } } // Delete the lock file and commit deleteResources(project, new String[] { CVS_TEST_LOCK_FILE }, true); } } catch (CoreException e) { write(e.getStatus(), 0); } catch (IOException e) { write(new CVSStatus(IStatus.ERROR, "An error occurred while reading the lock file", e), 0); } } } protected void write(IStatus status, int indent) { PrintStream output = System.out; indent(output, indent); output.println("Severity: " + status.getSeverity()); indent(output, indent); output.println("Plugin ID: " + status.getPlugin()); indent(output, indent); output.println("Code: " + status.getCode()); indent(output, indent); output.println("Message: " + status.getMessage()); Throwable t = status.getException(); if (t != null) { t.printStackTrace(output); if (t instanceof CoreException) { write(((CoreException)t).getStatus(), indent + 1); } } if (status.isMultiStatus()) { IStatus[] children = status.getChildren(); for (int i = 0; i < children.length; i++) write(children[i], indent + 1); } } protected static void indent(OutputStream output, int indent) { for (int i = 0; i < indent; i++) try { output.write(" ".getBytes()); } catch (IOException e) { // ignore } } /* (non-Javadoc) * @see junit.framework.TestCase#runBare() */ public void runBare() throws Throwable { try { super.runBare(); } catch (CVSException e) { // If a communication exception occurred // perhaps it is a server problem // Try again, just in case it is if (containsCommunicationException(e)) { super.runBare(); } else { throw e; } } } private boolean containsCommunicationException(CVSException e) { if (e instanceof CVSCommunicationException) return true; IStatus status = e.getStatus(); if (status.getException() instanceof CVSCommunicationException) return true; if (status.isMultiStatus()) { IStatus[] children = status.getChildren(); for (int i = 0; i < children.length; i++) { IStatus child = children[i]; if (child.getException() instanceof CVSCommunicationException) return true; } } return false; } /* (non-Javadoc) * @see org.eclipse.core.tests.harness.EclipseWorkspaceTest#ensureDoesNotExistInWorkspace(org.eclipse.core.resources.IResource) */ public void ensureDoesNotExistInWorkspace(IResource resource) { // Overridden to change how the workspace is deleted on teardown if (resource.getType() == IResource.ROOT) { // Delete each project individually Job[] allJobs = Job.getJobManager().find(null /* all families */); IProject[] projects = ((IWorkspaceRoot)resource).getProjects(); try { ensureDoesNotExistInWorkspace(projects); } catch (AssertionFailedError e) { // The delete failed. Write the active jobs to stdout System.out.println(e.getMessage()); System.out.println("Jobs active at time of deletion failure: "); //$NON-NLS-1$ if (allJobs.length == 0) { System.out.println("None"); //$NON-NLS-1$ } for (int i = 0; i < allJobs.length; i++) { Job job = allJobs[i]; System.out.println(job.getName()); } if (CVSTestSetup.FAIL_IF_EXCEPTION_LOGGED) { throw e; } } } else { ensureNotReadOnly(resource); super.ensureDoesNotExistInWorkspace(resource); } } private void ensureNotReadOnly(IResource resource) { if (resource.exists()) { try { resource.accept(new IResourceVisitor() { public boolean visit(IResource resource) throws CoreException { ResourceAttributes attrs = resource.getResourceAttributes(); if (resource.exists() && attrs.isReadOnly()) { attrs.setReadOnly(false); resource.setResourceAttributes(attrs); } return true; } }); } catch (CoreException e) { fail("#ensureNotReadOnly " + resource.getFullPath(), e); } } } /** * Delete each project from the workspace and return a status that * contains any failures */ public void ensureDoesNotExistInWorkspace(final IProject[] projects) { final Map failures = new HashMap(); IWorkspaceRunnable body = new IWorkspaceRunnable() { public void run(IProgressMonitor monitor) { for (int i = 0; i < projects.length; i++) { try { if (projects[i].exists()) { try { projects[i].delete(true, null); } catch (CoreException e) { // Ignore the exception and try again after making // sure the project doesn't contain any read-only resources ensureNotReadOnly(projects[i]); if (projects[i].exists()) { projects[i].refreshLocal(IResource.DEPTH_INFINITE, null); projects[i].delete(true, null); } } } } catch (CoreException e) { write(new CVSStatus(IStatus.ERROR, "Could not delete project " + projects[i].getName(), e), 0); failures.put(projects[i], e); } } } }; try { getWorkspace().run(body, null); } catch (CoreException e) { fail("#ensureDoesNotExistInWorkspace(IResource[])", e); } if (!failures.isEmpty()) { StringBuffer text = new StringBuffer(); text.append("Could not delete all projects: "); for (Iterator iter = failures.keySet().iterator(); iter.hasNext();) { IProject project = (IProject) iter.next(); text.append(project.getName()); } fail(text.toString()); } } protected void assertStatusContainsCode(IStatus status, int code) { if (status.isMultiStatus()) { IStatus[] children = status.getChildren(); for (int i = 0; i < children.length; i++) { IStatus child = children[i]; if (child.getCode() == code) return; } fail("Expected status code was not present"); } else { assertEquals("Status code is not what is expected", status.getCode(), code); } } /* (non-Javadoc) * @see junit.framework.TestCase#runTest() */ protected void runTest() throws Throwable { if (!CVSTestSetup.RECORD_PROTOCOL_TRAFFIC) { super.runTest(); return; } ByteArrayOutputStream os = new ByteArrayOutputStream(); Policy.recorder = new PrintStream(os); try { try { // Override the runTest method in order to print the entire trace of a // test that failed due to a CoreException including nested exceptions super.runTest(); } catch (CoreException e) { e.printStackTrace(); write(e.getStatus(), 0); throw e; } } catch (Throwable e) { // Transfer the recorded debug info to stdout Policy.recorder.close(); System.out.println(new String(os.toByteArray())); throw e; } finally { Policy.recorder.close(); Policy.recorder = null; } } protected void cleanup() throws CoreException { ensureDoesNotExistInWorkspace(getWorkspace().getRoot()); getWorkspace().save(true, null); //don't leak builder jobs, since they may affect subsequent tests waitForBuild(); } }