/******************************************************************************* * Copyright (c) 2015, 2017 Obeo 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: * Michael Borkowski - conversion of inner classes to mockito mocks * Martin Fleck - bug 512677 *******************************************************************************/ package org.eclipse.emf.compare.ide.ui.tests.unit; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertSame; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; import static org.mockito.Matchers.any; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import java.io.File; import java.io.InputStream; import java.util.Collections; import org.eclipse.core.resources.IFile; import org.eclipse.core.resources.IProject; import org.eclipse.core.resources.IResource; import org.eclipse.core.resources.IStorage; import org.eclipse.core.resources.ResourcesPlugin; import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.IPath; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.emf.common.util.URI; import org.eclipse.emf.compare.ide.ui.internal.logical.resolver.RevisionedURIConverter; import org.eclipse.emf.compare.ide.ui.logical.IStorageProvider; import org.eclipse.emf.compare.ide.ui.logical.IStorageProviderAccessor; import org.eclipse.emf.compare.ide.ui.logical.IStorageProviderAccessor.DiffSide; import org.eclipse.emf.compare.ide.utils.ResourceUtil; import org.eclipse.emf.ecore.EClass; import org.eclipse.emf.ecore.EPackage; import org.eclipse.emf.ecore.resource.Resource; import org.eclipse.emf.ecore.resource.ResourceSet; import org.eclipse.emf.ecore.resource.URIConverter; import org.eclipse.emf.ecore.resource.impl.ExtensibleURIConverterImpl; import org.eclipse.emf.ecore.resource.impl.ResourceSetImpl; import org.eclipse.jgit.api.Status; import org.junit.Test; import org.mockito.invocation.InvocationOnMock; import org.mockito.stubbing.Answer; @SuppressWarnings("unchecked") public class RevisionedURIConverterTest extends AbstractURITest { /** * using the "straight" repo from {@link #setupStraightRepo()}, we expect that the base will be that * initial commit, the remote side will be the branch, and the source side will be master. */ @Test public void testStorageAccessorContents_straight() throws Exception { setupStraightRepo(); IStorageProviderAccessor accessor = createAccessorForComparison(MASTER, BRANCH, false); URI file1URI = ResourceUtil.createURIFor(iFile1); // origin is the "initial-commit" state assertStateInitial(file1URI, accessor, DiffSide.ORIGIN); // source is the "MASTER" state. C2 no longer exists assertStateMaster(file1URI, accessor, DiffSide.SOURCE); // remote is the "BRANCH" state. C2 is now a super-type of C1 assertStateBranch(file1URI, accessor, DiffSide.REMOTE); } /** * If we compare "master" with "branch2", we expect the common ancestor to be "commit-master-1". */ @Test public void testStorageAccessorContents_multipleCommits() throws Exception { String branch1Name = BRANCH + "1"; String branch2Name = BRANCH + "2"; setupMultipleCommitsRepo(branch1Name, branch2Name); IStorageProviderAccessor accessor = createAccessorForComparison(MASTER, branch2Name, false); URI file1URI = ResourceUtil.createURIFor(iFile1); // origin is the "commit-master-1" state, which we initialized to be the // same as our usual "initial" state assertStateInitial(file1URI, accessor, DiffSide.ORIGIN); // source is the "MASTER" state. C2 no longer exists assertStateMaster(file1URI, accessor, DiffSide.SOURCE); // remote is the "BRANCH" state. C2 is now a super-type of C1 assertStateBranch(file1URI, accessor, DiffSide.REMOTE); } /** * This will create three files with the expected initial, master and branch content, then use a custom * content accessor that will redirect {@link #file1}'s URI to these. */ @Test public void testRedirectingAccessor() throws Exception { /* * setup has created a dummy file file1. we're gonna create the three sides in separate folders, and * expect the storage accessor to do the redirection. */ final IProject iProject = project.getProject(); final ResourceSet resourceSet = new ResourceSetImpl(); File originFile = project.getOrCreateFile(iProject, DiffSide.ORIGIN.toString() + "/file1.ecore"); File sourceFile = project.getOrCreateFile(iProject, DiffSide.SOURCE.toString() + "/file1.ecore"); File remoteFile = project.getOrCreateFile(iProject, DiffSide.REMOTE.toString() + "/file1.ecore"); IFile originIFile = project.getIFile(iProject, originFile); IFile sourceIFile = project.getIFile(iProject, sourceFile); IFile remoteIFile = project.getIFile(iProject, remoteFile); Resource originResource = connectResource(originIFile, resourceSet); Resource sourceResource = connectResource(sourceIFile, resourceSet); Resource remoteResource = connectResource(remoteIFile, resourceSet); final EPackage originRoot = createPackage(null, "P1"); createClass(originRoot, "C1"); createClass(originRoot, "C2"); originResource.getContents().add(originRoot); save(originResource); final EPackage sourceRoot = createPackage(null, "P1"); createClass(sourceRoot, "C1_renamed"); sourceResource.getContents().add(sourceRoot); save(sourceResource); final EPackage remoteRoot = createPackage(null, "P1"); final EClass class1 = createClass(remoteRoot, "C1"); final EClass class2 = createClass(remoteRoot, "C2"); createClass(remoteRoot, "C3"); class1.getESuperTypes().add(class2); remoteResource.getContents().add(remoteRoot); save(remoteResource); final Status status = repository.status(); assertFalse(status.hasUncommittedChanges()); IStorageProviderAccessor accessor = mock(IStorageProviderAccessor.class); when(accessor.getStorageProvider(any(IResource.class), any(DiffSide.class))) .then(getPathRedirectingStorageProvider()); URI file1URI = ResourceUtil.createURIFor(iFile1); // origin is the "initial-commit" state assertStateInitial(file1URI, accessor, DiffSide.ORIGIN); // source is the "MASTER" state. C2 no longer exists assertStateMaster(file1URI, accessor, DiffSide.SOURCE); // remote is the "BRANCH" state. C2 is now a super-type of C1 assertStateBranch(file1URI, accessor, DiffSide.REMOTE); } /** * We'll be using a storage provider accessor that returns <code>null</code> IStorageProviders. We expect * the uri converter not to throw exceptions, and not to fall back to the existing file pointed by the * URI. */ @Test public void testNullAccessor() throws Exception { IStorageProviderAccessor accessor = mock(IStorageProviderAccessor.class); URIConverter delegate = new ExtensibleURIConverterImpl(); URI fileURI = ResourceUtil.createURIFor(iFile1); RevisionedURIConverter converter = new RevisionedURIConverter(delegate, accessor, DiffSide.ORIGIN); InputStream stream = converter.createInputStream(fileURI, Collections.emptyMap()); assertNull(stream); converter = new RevisionedURIConverter(delegate, accessor, DiffSide.REMOTE); stream = converter.createInputStream(fileURI, Collections.emptyMap()); assertNull(stream); converter = new RevisionedURIConverter(delegate, accessor, DiffSide.SOURCE); stream = converter.createInputStream(fileURI, Collections.emptyMap()); assertNull(stream); } /** * We'll be using a storage provide accessor that throws exception. We expect that exception to slip * through and fail the test. */ @Test public void testExceptionAccessor() throws Exception { IStorageProviderAccessor accessor = mock(IStorageProviderAccessor.class); when(accessor.getStorageProvider(any(IResource.class), any(DiffSide.class))) .thenThrow(UnsupportedOperationException.class); URIConverter delegate = new ExtensibleURIConverterImpl(); URI fileURI = ResourceUtil.createURIFor(iFile1); RevisionedURIConverter converter = new RevisionedURIConverter(delegate, accessor, DiffSide.ORIGIN); try { converter.createInputStream(fileURI, Collections.emptyMap()); fail(); } catch (UnsupportedOperationException e) { // expected this one to slip through } try { converter = new RevisionedURIConverter(delegate, accessor, DiffSide.REMOTE); converter.createInputStream(fileURI, Collections.emptyMap()); fail(); } catch (UnsupportedOperationException e) { // expected this one to slip through } try { converter = new RevisionedURIConverter(delegate, accessor, DiffSide.SOURCE); converter.createInputStream(fileURI, Collections.emptyMap()); fail(); } catch (UnsupportedOperationException e) { // expected this one to slip through } } /** * Tests that the resource existences are cached correctly. * * @throws CoreException * mocking errors */ @Test public void testExistsCache() throws CoreException { // setup mocks final IStorageProvider fileStorageProvider = mock(IStorageProvider.class); when(fileStorageProvider.getStorage(any(IProgressMonitor.class))).thenReturn(iFile1); final IStorageProvider nullStorageProvider = mock(IStorageProvider.class); when(nullStorageProvider.getStorage(any(IProgressMonitor.class))).thenReturn(null); IStorageProviderAccessor accessor = mock(IStorageProviderAccessor.class); when(accessor.getStorageProvider(any(IResource.class), any(DiffSide.class))) .then(new Answer<IStorageProvider>() { public IStorageProvider answer(InvocationOnMock invocation) throws Throwable { return iFile1.equals(invocation.getArguments()[0]) ? fileStorageProvider : nullStorageProvider; } }); // verify mock setup assertSame(nullStorageProvider, accessor.getStorageProvider(null, DiffSide.ORIGIN)); assertSame(fileStorageProvider, accessor.getStorageProvider(iFile1, DiffSide.ORIGIN)); URI fileURI = ResourceUtil.createURIFor(iFile1); URI nullURI = URI.createURI("noProject/notExisting.file"); //$NON-NLS-1$ URIConverter delegate = new ExtensibleURIConverterImpl(); RevisionedURIConverter converter = new RevisionedURIConverter(delegate, accessor, DiffSide.ORIGIN); // check existence assertTrue(converter.exists(fileURI, null)); assertFalse(converter.exists(nullURI, null)); // storage providers where only called once verify(fileStorageProvider, times(1)).getStorage(any(IProgressMonitor.class)); verify(nullStorageProvider, times(1)).getStorage(any(IProgressMonitor.class)); // check existence multiple times assertTrue(converter.exists(fileURI, null)); assertFalse(converter.exists(nullURI, null)); assertTrue(converter.exists(fileURI, null)); assertFalse(converter.exists(nullURI, null)); assertTrue(converter.exists(fileURI, null)); assertFalse(converter.exists(nullURI, null)); // storage providers where only called once, cache was used verify(fileStorageProvider, times(1)).getStorage(any(IProgressMonitor.class)); verify(nullStorageProvider, times(1)).getStorage(any(IProgressMonitor.class)); } private Answer<IStorageProvider> getPathRedirectingStorageProvider() { return new Answer<IStorageProvider>() { public IStorageProvider answer(InvocationOnMock invocation) throws Throwable { IResource resource = (IResource)invocation.getArguments()[0]; DiffSide side = (DiffSide)invocation.getArguments()[1]; assertTrue(resource instanceof IFile && resource.exists()); IPath originalPath = resource.getFullPath(); String fileName = originalPath.lastSegment(); final IPath redirectedPath = originalPath.removeLastSegments(1).append(side.toString()) .append(fileName); return new IStorageProvider() { public IStorage getStorage(IProgressMonitor monitor) throws CoreException { return ResourcesPlugin.getWorkspace().getRoot().getFile(redirectedPath); } }; } }; } }