| /******************************************************************************* |
| * Copyright (C) 2015, 2018 EclipseSource Munich Gmbh 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: |
| * Philip Langer - initial API and implementation |
| * Christian W. Damus - bug 529253 |
| *******************************************************************************/ |
| package org.eclipse.papyrus.compare.diagram.tests.egit; |
| |
| import static com.google.common.base.Predicates.and; |
| import static com.google.common.collect.Iterables.filter; |
| import static com.google.common.collect.Iterables.transform; |
| import static org.hamcrest.Matchers.hasItem; |
| |
| import java.io.File; |
| import java.io.FileInputStream; |
| import java.io.FileOutputStream; |
| import java.io.IOException; |
| import java.net.URISyntaxException; |
| import java.net.URL; |
| import java.nio.channels.FileChannel; |
| |
| import org.eclipse.core.resources.IProject; |
| import org.eclipse.core.resources.IWorkspaceRoot; |
| import org.eclipse.core.resources.ResourcesPlugin; |
| import org.eclipse.core.resources.mapping.IModelProviderDescriptor; |
| import org.eclipse.core.resources.mapping.ModelProvider; |
| import org.eclipse.core.runtime.FileLocator; |
| import org.eclipse.core.runtime.Path; |
| import org.eclipse.core.runtime.Platform; |
| import org.eclipse.core.runtime.preferences.IEclipsePreferences; |
| import org.eclipse.core.runtime.preferences.InstanceScope; |
| import org.eclipse.egit.core.Activator; |
| import org.eclipse.egit.core.GitCorePreferences; |
| import org.eclipse.emf.common.util.URI; |
| import org.eclipse.emf.compare.ide.ui.internal.EMFCompareIDEUIPlugin; |
| import org.eclipse.emf.compare.ide.ui.internal.logical.EMFModelProvider; |
| import org.eclipse.emf.compare.ide.ui.internal.logical.resolver.CrossReferenceResolutionScope; |
| import org.eclipse.emf.compare.ide.ui.internal.preferences.EMFCompareUIPreferences; |
| import org.eclipse.emf.compare.ide.ui.tests.workspace.TestProject; |
| import org.eclipse.emf.ecore.EObject; |
| import org.eclipse.emf.ecore.resource.Resource; |
| import org.eclipse.emf.ecore.resource.ResourceSet; |
| import org.eclipse.emf.ecore.resource.impl.ResourceSetImpl; |
| import org.eclipse.emf.ecore.util.EcoreUtil; |
| import org.eclipse.jface.preference.IPreferenceStore; |
| import org.eclipse.jgit.lib.Constants; |
| import org.eclipse.jgit.util.FileUtils; |
| import org.eclipse.jgit.util.SystemReader; |
| import org.eclipse.papyrus.compare.diagram.tests.egit.fixture.GitTestRepository; |
| import org.eclipse.papyrus.compare.diagram.tests.egit.fixture.MockSystemReader; |
| import org.eclipse.uml2.uml.Element; |
| import org.eclipse.uml2.uml.NamedElement; |
| import org.eclipse.uml2.uml.Stereotype; |
| import org.hamcrest.Description; |
| import org.hamcrest.FeatureMatcher; |
| import org.hamcrest.Matcher; |
| import org.hamcrest.TypeSafeDiagnosingMatcher; |
| import org.junit.After; |
| import org.junit.AfterClass; |
| import org.junit.Before; |
| import org.junit.BeforeClass; |
| import org.junit.Test; |
| import org.osgi.framework.Bundle; |
| |
| import com.google.common.base.Function; |
| import com.google.common.base.Objects; |
| import com.google.common.base.Preconditions; |
| import com.google.common.base.Predicate; |
| import com.google.common.collect.ImmutableList; |
| import com.google.common.collect.ImmutableList.Builder; |
| |
| /** |
| * Abstract test case to assess the results of merging, rebasing, and cherry-picking of a particular merge |
| * scenario. |
| * <p> |
| * This abstract test case sets up the branches <em>left</em> and <em>right</em> with projects and models of a |
| * given directory specified by subclasses of this test case. Then it performs a merge, rebase, and |
| * cherry-pick in both directions and, for each case, calls the subclass to validate the result. |
| * </p> |
| * |
| * @author Philip Langer <planger@eclipsesource.com> |
| */ |
| @SuppressWarnings({"restriction", "nls" }) |
| public abstract class AbstractGitMergeTestCase { |
| |
| protected static final String DEFAULT_PROJECT = "Project1"; |
| |
| protected static final String TEST_BUNDLE = "org.eclipse.papyrus.compare.diagram.tests.git"; |
| |
| protected static final String MASTER_BRANCH = Constants.R_HEADS + Constants.MASTER; |
| |
| protected static final String BRANCH_LEFT = Constants.R_HEADS + "branch_left"; |
| |
| protected static final String BRANCH_RIGHT = Constants.R_HEADS + "branch_right"; |
| |
| private static final Predicate<File> IS_EXISTING_FILE = new Predicate<File>() { |
| @Override |
| public boolean apply(File input) { |
| return input != null && input.exists() && input.isFile(); |
| } |
| }; |
| |
| protected static String defaultResolutionScope; |
| |
| protected GitTestRepository repository; |
| |
| protected File gitDir; |
| |
| @BeforeClass |
| public static void setUpClass() { |
| // suppress auto-ignoring and auto-sharing to avoid interference |
| final IEclipsePreferences eGitPreferences = InstanceScope.INSTANCE.getNode(Activator.getPluginId()); |
| eGitPreferences.put(GitCorePreferences.core_preferredMergeStrategy, "model recursive"); |
| eGitPreferences.putBoolean(GitCorePreferences.core_autoShareProjects, false); |
| // This is actually the value of |
| // "GitCorePreferences.core_autoIgnoreDerivedResources"... but it was |
| // not in Egit 2.1 |
| eGitPreferences.putBoolean("core_autoIgnoreDerivedResources", false); |
| final IPreferenceStore store = EMFCompareIDEUIPlugin.getDefault().getPreferenceStore(); |
| defaultResolutionScope = store.getString(EMFCompareUIPreferences.RESOLUTION_SCOPE_PREFERENCE); |
| store.setValue(EMFCompareUIPreferences.RESOLUTION_SCOPE_PREFERENCE, getResolutionScope().name()); |
| } |
| |
| @AfterClass |
| public static void tearDownClass() { |
| final IPreferenceStore store = EMFCompareIDEUIPlugin.getDefault().getPreferenceStore(); |
| store.setValue(EMFCompareUIPreferences.RESOLUTION_SCOPE_PREFERENCE, defaultResolutionScope); |
| } |
| |
| @Before |
| public void setUp() throws Exception { |
| Activator.getDefault().getRepositoryCache().clear(); |
| final MockSystemReader mockSystemReader = new MockSystemReader(); |
| SystemReader.setInstance(mockSystemReader); |
| final IWorkspaceRoot workspaceRoot = ResourcesPlugin.getWorkspace().getRoot(); |
| final String gitRepoPath = workspaceRoot.getRawLocation().toFile() + File.separator + "repo"; |
| mockSystemReader.setProperty(Constants.GIT_CEILING_DIRECTORIES_KEY, |
| workspaceRoot.getLocation().toFile().getParentFile().getAbsoluteFile().toString()); |
| gitDir = new File(gitRepoPath, Constants.DOT_GIT); |
| repository = new GitTestRepository(gitDir); |
| repository.ignore(workspaceRoot.getRawLocation().append(".metadata").toFile()); |
| setUpRepository(); |
| } |
| |
| @After |
| public void tearDown() throws Exception { |
| final IModelProviderDescriptor modelProviderDesc = ModelProvider |
| .getModelProviderDescriptor(EMFModelProvider.PROVIDER_ID); |
| final EMFModelProvider emfModelProvider = (EMFModelProvider)modelProviderDesc.getModelProvider(); |
| emfModelProvider.clear(); |
| repository.dispose(); |
| Activator.getDefault().getRepositoryCache().clear(); |
| if (gitDir.exists()) { |
| File gitRoot = gitDir.getParentFile(); |
| if (gitRoot.exists()) { |
| FileUtils.delete(gitRoot, FileUtils.RECURSIVE | FileUtils.RETRY | FileUtils.SKIP_MISSING); |
| } |
| } |
| } |
| |
| protected void setUpRepository() throws Exception { |
| final String testScenarioPath = getTestScenarioPath(); |
| final File testScenarioDir = getTestScenarioFile(testScenarioPath); |
| Preconditions.checkState(testScenarioDir.isDirectory(), "Test scenario path must be a directory."); |
| |
| final File testScenarioDirOrigin = getTestScenarioFile(testScenarioPath + "origin"); |
| final File testScenarioDirLeft = getTestScenarioFile(testScenarioPath + "left"); |
| final File testScenarioDirRight = getTestScenarioFile(testScenarioPath + "right"); |
| Preconditions.checkState(testScenarioDirOrigin.exists() && testScenarioDirOrigin.isDirectory(), |
| "Test scenario directory must contain a directory called \"origin\"."); |
| Preconditions.checkState(testScenarioDirLeft.exists() && testScenarioDirLeft.isDirectory(), |
| "Test scenario directory must contain a directory called \"left\"."); |
| Preconditions.checkState(testScenarioDirRight.exists() && testScenarioDirRight.isDirectory(), |
| "Test scenario directory must contain a directory called \"right\"."); |
| |
| commitContentFrom(testScenarioDirOrigin, "initial-commit"); |
| repository.createBranch(MASTER_BRANCH, BRANCH_LEFT); |
| repository.createBranch(MASTER_BRANCH, BRANCH_RIGHT); |
| repository.checkoutBranch(BRANCH_LEFT); |
| commitContentFrom(testScenarioDirLeft, "left-commit"); |
| repository.checkoutBranch(BRANCH_RIGHT); |
| commitContentFrom(testScenarioDirRight, "right-commit"); |
| } |
| |
| private File getTestScenarioFile(String scenarioPath) throws IOException, URISyntaxException { |
| final Bundle bundle = Platform.getBundle(TEST_BUNDLE); |
| final URI fileUri = getFileUri(bundle.getEntry(scenarioPath)); |
| return toFile(fileUri); |
| } |
| |
| private URI getFileUri(URL bundleUrl) throws IOException { |
| return URI.createFileURI(FileLocator.toFileURL(bundleUrl).getPath()); |
| } |
| |
| private File toFile(URI fileUri) throws URISyntaxException { |
| return new File(fileUri.toFileString()); |
| } |
| |
| private void commitContentFrom(File rootDirectory, String commitMsg) throws Exception { |
| // TODO support multiple projects |
| final File workingDirectory = repository.getRepository().getWorkTree(); |
| final TestProject testProject1 = new TestProject(DEFAULT_PROJECT, workingDirectory.getAbsolutePath()); |
| final IProject iProject = testProject1.getProject(); |
| final File projectDirectory = new File(iProject.getLocation().toOSString()); |
| repository.connect(iProject); |
| copyDirectoryContents(rootDirectory, projectDirectory); |
| repository.addAllAndCommit(commitMsg, true); |
| } |
| |
| private static void copyDirectoryContents(File rootDirectory, final File workingDirectory) |
| throws IOException { |
| String[] list = rootDirectory.list(); |
| if (list != null) { |
| for (String child : list) { |
| copyDirectory(new File(rootDirectory, child), new File(workingDirectory, child)); |
| } |
| } |
| } |
| |
| private static void copyDirectory(File source, File destination) throws IOException { |
| if (source != null && source.isDirectory()) { |
| if (destination != null && !destination.exists()) { |
| destination.mkdir(); |
| } |
| String[] list = source.list(); |
| if (list != null) { |
| for (String child : list) { |
| copyDirectory(new File(source, child), new File(destination, child)); |
| } |
| } |
| } else { |
| copyFile(source, destination); |
| } |
| } |
| |
| private static void copyFile(File source, File dest) throws IOException { |
| FileChannel sourceChannel = null; |
| FileChannel destChannel = null; |
| FileInputStream fileInputStream = new FileInputStream(source); |
| sourceChannel = fileInputStream.getChannel(); |
| FileOutputStream fileOutputStream = new FileOutputStream(dest); |
| destChannel = fileOutputStream.getChannel(); |
| destChannel.transferFrom(sourceChannel, 0, sourceChannel.size()); |
| sourceChannel.close(); |
| destChannel.close(); |
| fileInputStream.close(); |
| fileOutputStream.close(); |
| } |
| |
| private Iterable<File> getAllContainedFiles(File workingDirectory) { |
| final Builder<File> builder = ImmutableList.builder(); |
| File[] listFiles = workingDirectory.listFiles(); |
| if (listFiles != null) { |
| for (File containedFile : listFiles) { |
| if (containedFile.isFile()) { |
| builder.add(containedFile); |
| } else if (containedFile.isDirectory()) { |
| builder.addAll(getAllContainedFiles(containedFile)); |
| } |
| } |
| } |
| return builder.build(); |
| } |
| |
| /** |
| * Tests merging branch <em>left</em> into checked-out branch <em>right</em> and validates the result |
| * based on {@link #validateResult()}. |
| */ |
| @Test |
| public void testMergeLeftIntoRight() throws Exception { |
| repository.checkoutBranch(BRANCH_RIGHT); |
| repository.mergeLogicalWithNewCommit(BRANCH_LEFT); |
| validate(); |
| validateMergeLeftIntoRight(); |
| } |
| |
| /** |
| * Tests merging branch <em>right</em> into checked-out branch <em>left</em> and validates the result |
| * based on {@link #validateResult()}. |
| */ |
| @Test |
| public void testMergeRightIntoLeft() throws Exception { |
| repository.checkoutBranch(BRANCH_LEFT); |
| repository.mergeLogicalWithNewCommit(BRANCH_RIGHT); |
| validate(); |
| validateMergeRightIntoLeft(); |
| } |
| |
| /** |
| * Tests rebasing branch <em>left</em> onto checked-out branch <em>right</em> and validates the result |
| * based on {@link #validateResult()}. |
| */ |
| @Test |
| public void testRebaseLeftOntoRight() throws Exception { |
| repository.checkoutBranch(BRANCH_RIGHT); |
| repository.rebaseLogical(BRANCH_LEFT); |
| validate(); |
| validateRebaseLeftOntoRight(); |
| } |
| |
| /** |
| * Tests rebasing branch <em>right</em> onto checked-out branch <em>left</em> and then validates the |
| * result based on {@link #validateResult()} . |
| */ |
| @Test |
| public void testRebaseRightOntoLeft() throws Exception { |
| repository.checkoutBranch(BRANCH_LEFT); |
| repository.rebaseLogical(BRANCH_RIGHT); |
| validate(); |
| validateRebaseRightOntoLeft(); |
| } |
| |
| /** |
| * Tests cherry-picking branch <em>left</em> onto checked-out branch <em>right</em> and validates the |
| * result based on {@link #validateResult()}. |
| */ |
| @Test |
| public void testCherryPickLeftOntoRight() throws Exception { |
| repository.checkoutBranch(BRANCH_RIGHT); |
| repository.cherryPickLogical(BRANCH_LEFT); |
| validate(); |
| validateCherryPickLeftOntoRight(); |
| } |
| |
| /** |
| * Tests cherry-picking branch <em>right</em> onto checked-out branch <em>left</em> and then validates the |
| * result based on {@link #validateResult()} . |
| */ |
| @Test |
| public void testCherryPickRightOntoLeft() throws Exception { |
| repository.checkoutBranch(BRANCH_LEFT); |
| repository.cherryPickLogical(BRANCH_RIGHT); |
| validate(); |
| validateCherryPickRightOntoLeft(); |
| } |
| |
| protected void validate() throws Exception { |
| validateResult(); |
| validateResources(); |
| } |
| |
| private void validateResources() throws Exception { |
| final ResourceSet resourceSet = new ResourceSetImpl(); |
| final File workingDirectory = repository.getRepository().getWorkTree(); |
| final Iterable<File> filesOfInterest = filter(getAllContainedFiles(workingDirectory), |
| and(IS_EXISTING_FILE, getFileOfInterestFilter())); |
| final Iterable<URI> urisOfInterest = transform(filesOfInterest, toUri()); |
| |
| // On different OS platforms and/or different computer systems, the order in |
| // which the files are gathered from the git working directory is variable. |
| // In case of sub-model units, be sure to resolve all proxies first, so that |
| // the unit linkages are available for test cases that expect to find them |
| for (URI uriOfInterest : urisOfInterest) { |
| resourceSet.getResource(uriOfInterest, true); |
| } |
| EcoreUtil.resolveAll(resourceSet); |
| |
| for (URI uriOfInterest : urisOfInterest) { |
| final Resource resource = resourceSet.getResource(uriOfInterest, false); |
| validateResult(resource); |
| } |
| } |
| |
| private Function<File, URI> toUri() { |
| return new Function<File, URI>() { |
| @Override |
| public URI apply(File input) { |
| return URI.createPlatformResourceURI(repository.getRepoRelativePath(input), true); |
| } |
| }; |
| } |
| |
| private Predicate<File> getFileOfInterestFilter() { |
| return new Predicate<File>() { |
| @Override |
| public boolean apply(File input) { |
| return !input.getAbsolutePath().startsWith(gitDir.getAbsolutePath()) && shouldValidate(input); |
| } |
| }; |
| } |
| |
| protected boolean isConflicting() throws Exception { |
| return repository.status().getConflicting().size() > 0; |
| } |
| |
| protected boolean noConflict() throws Exception { |
| return !isConflicting(); |
| } |
| |
| protected boolean fileExists(String string) { |
| final File workTree = repository.getRepository().getWorkTree(); |
| final File projectDirectory = new File(workTree, DEFAULT_PROJECT); |
| return new File(projectDirectory, string).exists(); |
| } |
| |
| /** |
| * Obtain a matcher for the Git-relative paths of files that are conflicted. |
| * |
| * @return a matcher of Git-relative paths of files that are conflicted |
| */ |
| protected Matcher<String> isConflicted() { |
| return new TypeSafeDiagnosingMatcher<String>() { |
| @Override |
| public void describeTo(Description description) { |
| description.appendText("is conflicted"); |
| } |
| |
| @Override |
| protected boolean matchesSafely(String item, Description failure) { |
| boolean result = false; |
| |
| try { |
| result = repository.status().getConflicting() |
| .contains(new Path(DEFAULT_PROJECT).append(item).toString()); |
| |
| if (!result) { |
| failure.appendText(item).appendText(" is not conflicted"); |
| } |
| } catch (Exception e) { |
| e.printStackTrace(); |
| failure.appendText("could not determine conflict status: " + e.getMessage()); |
| } |
| |
| return result; |
| } |
| }; |
| } |
| |
| /** |
| * Obtain a matcher for the Git-relative paths of files that exist. |
| * |
| * @return a matcher of Git-relative paths of files that exist |
| */ |
| protected Matcher<String> fileExists() { |
| return new TypeSafeDiagnosingMatcher<String>() { |
| @Override |
| public void describeTo(Description description) { |
| description.appendText("file exists"); |
| } |
| |
| @Override |
| protected boolean matchesSafely(String item, Description failure) { |
| boolean result = fileExists(item); |
| |
| if (!result) { |
| failure.appendText(item).appendText(" does not exist"); |
| } |
| |
| return result; |
| } |
| }; |
| } |
| |
| /** |
| * Obtain a matcher for resources that are loaded. |
| * |
| * @return a matcher of resources that are loaded |
| */ |
| protected <T extends Resource> Matcher<T> isLoaded() { |
| return new TypeSafeDiagnosingMatcher<T>() { |
| @Override |
| public void describeTo(Description description) { |
| description.appendText("resource loaded"); |
| } |
| |
| @Override |
| protected boolean matchesSafely(T item, Description failure) { |
| boolean result = item.isLoaded(); |
| |
| if (!result) { |
| failure.appendText(item.getURI().lastSegment()).appendText(" is not loaded"); |
| } |
| |
| return result; |
| } |
| }; |
| } |
| |
| /** |
| * Obtain a matcher for UML elements that have the {@code name}d stereotype applied. |
| * |
| * @param name |
| * the simple name of a stereotype (not a qualified name) |
| * @return the matcher |
| */ |
| protected <T extends Element> Matcher<T> stereotypedAs(String name) { |
| Matcher<Iterable<? super Stereotype>> named = hasItem(named(name)); |
| |
| return new FeatureMatcher<T, Iterable<Stereotype>>(named, String.format("stereotyped as «%s»", name), |
| "appliedStereotypes") { |
| |
| @Override |
| protected Iterable<Stereotype> featureValueOf(T actual) { |
| return actual.getAppliedStereotypes(); |
| } |
| }; |
| } |
| |
| /** |
| * Obtain a matcher for UML elements that have the given {@code name}. |
| * |
| * @param name |
| * the simple name of an element (not a qualified name) |
| * @return the matcher |
| */ |
| protected <T extends NamedElement> Matcher<T> named(final String name) { |
| return new TypeSafeDiagnosingMatcher<T>() { |
| @Override |
| public void describeTo(Description description) { |
| description.appendText("named \"").appendText(name).appendText("\""); |
| } |
| |
| @Override |
| protected boolean matchesSafely(T item, Description failure) { |
| boolean result = Objects.equal(item.getName(), name); |
| |
| if (!result) { |
| failure.appendValue(item).appendText(" is not named \"").appendText(name) |
| .appendText("\""); |
| } |
| |
| return result; |
| } |
| }; |
| } |
| |
| /** |
| * Obtain a matcher for objects that are stored in the given {@code resource}. |
| * |
| * @param resource |
| * the resource containing the elements to match |
| * @return the matcher |
| */ |
| protected <T extends EObject> Matcher<T> storedIn(final Resource resource) { |
| return new TypeSafeDiagnosingMatcher<T>() { |
| @Override |
| public void describeTo(Description description) { |
| description.appendText("stored in ").appendValue(resource.getURI()); |
| } |
| |
| @Override |
| protected boolean matchesSafely(T item, Description failure) { |
| boolean result = item.eResource() == resource; |
| |
| if (!result) { |
| failure.appendValue(item).appendText(" is not in ").appendValue(resource.getURI()); |
| } |
| |
| return result; |
| } |
| }; |
| } |
| |
| /** |
| * Returns the resolution scope to be used for this test case. |
| * <p> |
| * The default value is {@link CrossReferenceResolutionScope#WORKSPACE}. Subclasses may override this |
| * method to provide a different resolution scope. |
| * </p> |
| * |
| * @return the resolution scope to be used for this test case. |
| */ |
| protected static CrossReferenceResolutionScope getResolutionScope() { |
| return CrossReferenceResolutionScope.WORKSPACE; |
| } |
| |
| /** |
| * Returns the path to the data of the test scenario. |
| * |
| * @return the path to the data of the test scenario. |
| */ |
| protected abstract String getTestScenarioPath(); |
| |
| /** |
| * Specifies whether a given {@code file} should be validated in this test. |
| * <p> |
| * Clients may overwrite to include or exclude certain files from being validated with |
| * {@link #validateResult(Resource)}. The default is <code>true</code> for any file. |
| * </p> |
| * |
| * @param file |
| * The input in question. |
| * @return <code>true</code> if the given {@code file} should be validated, <code>false</code> otherwise. |
| */ |
| protected boolean shouldValidate(File file) { |
| return true; |
| } |
| |
| /** |
| * Validates the result after merging, rebasing, or cherry-picking in either direction. |
| * |
| * @throws Exception |
| * if something goes wrong during the validation of the assertions. |
| */ |
| protected abstract void validateResult() throws Exception; |
| |
| /** |
| * Validates contents of a single resource after merging, rebasing, or cherry-picking in either direction. |
| * |
| * @param resource |
| * The resource to validate. |
| * @throws Exception |
| * if something goes wrong during the validation of the assertions. |
| */ |
| protected abstract void validateResult(Resource resource) throws Exception; |
| |
| /** |
| * Validates the result of merging branch <em>left</em> into <em>right</em>. |
| * <p> |
| * This method it intended to be overwritten by sub-classes if the specific tests require specific |
| * validation. |
| * </p> |
| */ |
| protected void validateMergeLeftIntoRight() { |
| // no validation by default, can be overwritten by sub-classes |
| } |
| |
| /** |
| * Validates the result of merging branch <em>right</em> into <em>left</em>. |
| * <p> |
| * This method it intended to be overwritten by sub-classes if the specific tests require specific |
| * validation. |
| * </p> |
| */ |
| protected void validateMergeRightIntoLeft() { |
| // no validation by default, can be overwritten by sub-classes |
| } |
| |
| /** |
| * Validates the result of rebasing branch <em>left</em> onto <em>right</em>. |
| * <p> |
| * This method it intended to be overwritten by sub-classes if the specific tests require specific |
| * validation. |
| * </p> |
| */ |
| protected void validateRebaseLeftOntoRight() { |
| // no validation by default, can be overwritten by sub-classes |
| } |
| |
| /** |
| * Validates the result of rebasing branch <em>right</em> onto <em>left</em>. |
| * <p> |
| * This method it intended to be overwritten by sub-classes if the specific tests require specific |
| * validation. |
| * </p> |
| */ |
| protected void validateRebaseRightOntoLeft() { |
| // no validation by default, can be overwritten by sub-classes |
| } |
| |
| /** |
| * Validates the result of cherry-picking branch <em>left</em> onto <em>right</em>. |
| * <p> |
| * This method it intended to be overwritten by sub-classes if the specific tests require specific |
| * validation. |
| * </p> |
| */ |
| protected void validateCherryPickLeftOntoRight() { |
| // no validation by default, can be overwritten by sub-classes |
| } |
| |
| /** |
| * Validates the result of cherry-picking branch <em>right</em> onto <em>left</em>. |
| * <p> |
| * This method it intended to be overwritten by sub-classes if the specific tests require specific |
| * validation. |
| * </p> |
| */ |
| protected void validateCherryPickRightOntoLeft() { |
| // no validation by default, can be overwritten by sub-classes |
| } |
| } |