| /******************************************************************************* |
| * Copyright (c) 2000, 2017 IBM Corporation and others. |
| * |
| * This program and the accompanying materials |
| * are made available under the terms of the Eclipse Public License 2.0 |
| * which accompanies this distribution, and is available at |
| * https://www.eclipse.org/legal/epl-2.0/ |
| * |
| * SPDX-License-Identifier: EPL-2.0 |
| * |
| * Contributors: |
| * IBM Corporation - initial API and implementation |
| *******************************************************************************/ |
| package org.eclipse.team.tests.ccvs.core.subscriber; |
| |
| import java.lang.reflect.InvocationTargetException; |
| import java.util.ArrayList; |
| import java.util.List; |
| |
| import org.eclipse.compare.structuremergeviewer.IDiffElement; |
| import org.eclipse.core.resources.IProject; |
| import org.eclipse.core.resources.IResource; |
| import org.eclipse.core.runtime.CoreException; |
| import org.eclipse.core.runtime.IProgressMonitor; |
| import org.eclipse.core.runtime.NullProgressMonitor; |
| import org.eclipse.team.core.TeamException; |
| import org.eclipse.team.core.diff.IDiff; |
| import org.eclipse.team.core.diff.provider.Diff; |
| import org.eclipse.team.core.subscribers.Subscriber; |
| import org.eclipse.team.core.synchronize.SyncInfo; |
| import org.eclipse.team.internal.ccvs.core.CVSCompareSubscriber; |
| import org.eclipse.team.internal.ccvs.core.CVSException; |
| import org.eclipse.team.internal.ccvs.core.CVSMergeSubscriber; |
| import org.eclipse.team.internal.ccvs.core.CVSProviderPlugin; |
| import org.eclipse.team.internal.ccvs.core.CVSTag; |
| import org.eclipse.team.internal.ccvs.ui.subscriber.CVSSubscriberOperation; |
| import org.eclipse.team.internal.ccvs.ui.subscriber.ConfirmMergedOperation; |
| import org.eclipse.team.internal.core.mapping.SyncInfoToDiffConverter; |
| import org.eclipse.team.internal.ui.synchronize.SyncInfoModelElement; |
| import org.eclipse.team.tests.ccvs.core.CVSTestSetup; |
| import org.junit.Assert; |
| |
| import junit.framework.AssertionFailedError; |
| |
| /** |
| * This class acts as the source for the sync info used by the subscriber tests. |
| * The purpose is to allow the sync info to be obtained directly from the subscriber |
| * or through the sync set visible in the sync view. |
| */ |
| public class SyncInfoSource { |
| |
| protected static IProgressMonitor DEFAULT_MONITOR = new NullProgressMonitor(); |
| protected List<CVSMergeSubscriber> mergeSubscribers = new ArrayList<>(); |
| protected List<CVSCompareSubscriber> compareSubscribers = new ArrayList<>(); |
| |
| public CVSMergeSubscriber createMergeSubscriber(IProject project, CVSTag root, CVSTag branch) { |
| return createMergeSubscriber(project, root, branch, false /*default*/); |
| } |
| |
| public CVSMergeSubscriber createMergeSubscriber(IProject project, CVSTag root, CVSTag branch, boolean isModelSync) { |
| CVSMergeSubscriber subscriber = new CVSMergeSubscriber(new IResource[] { project }, root, branch, isModelSync); |
| mergeSubscribers.add(subscriber); |
| return subscriber; |
| } |
| |
| public CVSCompareSubscriber createCompareSubscriber(IResource resource, CVSTag tag) { |
| CVSCompareSubscriber subscriber = new CVSCompareSubscriber(new IResource[] { resource }, tag); |
| compareSubscribers.add(subscriber); |
| return subscriber; |
| } |
| |
| public void disposeSubscriber(Subscriber subscriber) { |
| // Nothing to do |
| } |
| |
| /** |
| * @throws TeamException |
| */ |
| public Subscriber createWorkspaceSubscriber() throws TeamException { |
| return CVSProviderPlugin.getPlugin().getCVSWorkspaceSubscriber(); |
| } |
| |
| /** |
| * Return the sync info for the given subscriber for the given resource. |
| */ |
| protected SyncInfo getSyncInfo(Subscriber subscriber, IResource resource) throws TeamException { |
| return subscriber.getSyncInfo(resource); |
| } |
| |
| /** |
| * Return the diff for the given subscriber for the given resource. |
| */ |
| protected IDiff getDiff(Subscriber subscriber, IResource resource) throws CoreException { |
| return subscriber.getDiff(resource); |
| } |
| |
| /** |
| * Refresh the subscriber for the given resource |
| */ |
| public void refresh(Subscriber subscriber, IResource resource) throws TeamException { |
| refresh(subscriber, new IResource[] { resource}); |
| } |
| |
| /** |
| * Refresh the subscriber for the given resources |
| */ |
| public void refresh(Subscriber subscriber, IResource[] resources) throws TeamException { |
| subscriber.refresh(resources, IResource.DEPTH_INFINITE, DEFAULT_MONITOR); |
| } |
| |
| protected void assertProjectRemoved(Subscriber subscriber, IProject project) { |
| IResource[] roots = subscriber.roots(); |
| for (int i = 0; i < roots.length; i++) { |
| IResource resource = roots[i]; |
| if (resource.equals(project)) { |
| throw new AssertionFailedError(); |
| } |
| } |
| } |
| |
| public void tearDown() { |
| for (CVSMergeSubscriber s : mergeSubscribers) { |
| s.cancel(); |
| } |
| } |
| |
| public void assertSyncEquals(String message, Subscriber subscriber, IResource resource, int syncKind) throws CoreException { |
| int conflictTypeMask = 0x0F; // ignore manual and auto merge sync types for now. |
| SyncInfo info = getSyncInfo(subscriber, resource); |
| int kind; |
| int kindOther = syncKind & conflictTypeMask; |
| if (info == null) { |
| kind = SyncInfo.IN_SYNC; |
| } else { |
| kind = info.getKind() & conflictTypeMask; |
| } |
| // Special handling for folders |
| if (kind != kindOther && resource.getType() == IResource.FOLDER) { |
| // The only two states for folders are outgoing addition and in-sync. |
| // Other additions will appear as in-sync |
| if (info.getKind() == SyncInfo.IN_SYNC |
| && (syncKind & SyncInfo.ADDITION) != 0) { |
| return; |
| } |
| } else { |
| // Only test if kinds are equal |
| assertDiffKindEquals(message, subscriber, resource, SyncInfoToDiffConverter.asDiffFlags(syncKind)); |
| } |
| Assert.assertTrue(message + ": improper sync state for " + resource + " expected " + |
| SyncInfo.kindToString(kindOther) + " but was " + |
| SyncInfo.kindToString(kind), kind == kindOther); |
| |
| } |
| |
| protected void assertDiffKindEquals(String message, Subscriber subscriber, IResource resource, int expectedFlags) throws CoreException { |
| int actualFlags = getActualDiffFlags(subscriber, resource); |
| boolean result = compareFlags(resource, actualFlags, expectedFlags); |
| int count = 0; |
| while (!result && count < 40) { |
| // The discrepancy may be due to a timing issue. |
| // Let's wait a few seconds and get the flags again. |
| try { |
| Thread.sleep(100); |
| } catch (InterruptedException e) { |
| // Ignore |
| } |
| actualFlags = getActualDiffFlags(subscriber, resource); |
| result = compareFlags(resource, actualFlags, expectedFlags); |
| if (result) { |
| System.out.println("A timing issue has been detected in the CVS test"); |
| new Exception().printStackTrace(); |
| } else { |
| count++; |
| } |
| } |
| String errorString = message + ": improper diff for " + resource |
| + " expected " |
| + SyncInfoToDiffConverter.diffStatusToString(expectedFlags) |
| + " but was " |
| + SyncInfoToDiffConverter.diffStatusToString(actualFlags); |
| if (CVSTestSetup.FAIL_ON_BAD_DIFF |
| || (expectedFlags != IDiff.NO_CHANGE && actualFlags == IDiff.NO_CHANGE)) { |
| // When running in the suites, we want to avoid intermittent failures. |
| // However, still fail if we expected a change but we get no change since that can |
| // cause work to be lost |
| Assert.assertTrue(errorString, result); |
| } else if (!result) { |
| System.out.println(errorString); |
| new Exception().printStackTrace(); |
| } |
| } |
| |
| private boolean compareFlags(IResource resource, int actualFlags, int expectedFlags) { |
| // Special handling for folders |
| if (actualFlags != expectedFlags && resource.getType() == IResource.FOLDER) { |
| // The only two states for folders are outgoing addition and in-sync. |
| // Other additions will appear as in-sync |
| int expectedKind = expectedFlags & Diff.KIND_MASK; |
| int actualKind = actualFlags & Diff.KIND_MASK; |
| if (actualKind == IDiff.NO_CHANGE |
| && expectedKind == IDiff.ADD) { |
| return true; |
| } |
| } |
| return actualFlags == expectedFlags; |
| } |
| |
| private int getActualDiffFlags(Subscriber subscriber, IResource resource) |
| throws CoreException { |
| IDiff node = getDiff(subscriber, resource); |
| int actualFlags; |
| if (node == null) { |
| actualFlags = IDiff.NO_CHANGE; |
| } else { |
| actualFlags = ((Diff)node).getStatus(); |
| } |
| return actualFlags; |
| } |
| |
| public void mergeResources(Subscriber subscriber, IResource[] resources, boolean allowOverwrite) throws TeamException, InvocationTargetException, InterruptedException { |
| SyncInfo[] infos = createSyncInfos(subscriber, resources); |
| mergeResources(subscriber, infos, allowOverwrite); |
| } |
| |
| private void mergeResources(Subscriber subscriber, SyncInfo[] infos, boolean allowOverwrite) throws InvocationTargetException, InterruptedException { |
| TestMergeUpdateOperation action = new TestMergeUpdateOperation(getElements(infos), allowOverwrite); |
| action.run(DEFAULT_MONITOR); |
| } |
| |
| protected SyncInfo[] createSyncInfos(Subscriber subscriber, IResource[] resources) throws TeamException { |
| SyncInfo[] result = new SyncInfo[resources.length]; |
| for (int i = 0; i < resources.length; i++) { |
| IResource resource = resources[i]; |
| result[i] = getSyncInfo(subscriber, resource); |
| } |
| return result; |
| } |
| |
| public void markAsMerged(Subscriber subscriber, IResource[] resources) throws InvocationTargetException, InterruptedException, TeamException { |
| SyncInfo[] infos = createSyncInfos(subscriber, resources); |
| new ConfirmMergedOperation(null, getElements(infos)).run(DEFAULT_MONITOR); |
| } |
| |
| |
| protected IDiffElement[] getElements(SyncInfo[] infos) { |
| SyncInfoModelElement[] elements = new SyncInfoModelElement[infos.length]; |
| for (int i = 0; i < elements.length; i++) { |
| elements[i] = new SyncInfoModelElement(null, infos[i]); |
| } |
| return elements; |
| } |
| |
| public void updateResources(Subscriber subscriber, IResource[] resources) throws CoreException { |
| runSubscriberOperation(new TestUpdateOperation(getElements(createSyncInfos(subscriber, resources)))); |
| } |
| |
| public void commitResources(Subscriber subscriber, IResource[] resources) throws CoreException { |
| runSubscriberOperation(new TestCommitOperation(getElements(createSyncInfos(subscriber, resources)), false /* override */)); |
| } |
| |
| public void overrideAndUpdateResources(Subscriber subscriber, boolean shouldPrompt, IResource[] resources) throws CoreException { |
| TestOverrideAndUpdateOperation action = new TestOverrideAndUpdateOperation(getElements(createSyncInfos(subscriber, resources))); |
| runSubscriberOperation(action); |
| Assert.assertTrue(shouldPrompt == action.isPrompted()); |
| } |
| |
| public void overrideAndCommitResources(Subscriber subscriber, IResource[] resources) throws CoreException { |
| TestCommitOperation action = new TestCommitOperation(getElements(createSyncInfos(subscriber, resources)), true /* override */); |
| runSubscriberOperation(action); |
| } |
| |
| private void runSubscriberOperation(CVSSubscriberOperation op) throws CoreException { |
| try { |
| op.run(); |
| } catch (InvocationTargetException e) { |
| throw CVSException.wrapException(e); |
| } catch (InterruptedException e) { |
| Assert.fail("Operation was interrupted"); |
| } |
| } |
| |
| } |