/******************************************************************************* * Copyright (C) 2015 Andrey Loskutov <loskutov@gmx.de> 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 *******************************************************************************/ package org.eclipse.egit.core.internal.indexdiff; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; import java.util.Arrays; import java.util.Collection; import java.util.Collections; import org.eclipse.core.resources.IResource; import org.eclipse.core.resources.IWorkspaceRunnable; import org.eclipse.core.resources.ResourcesPlugin; import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.egit.core.Activator; import org.eclipse.egit.core.JobFamilies; import org.eclipse.egit.core.test.GitTestCase; import org.eclipse.egit.core.test.TestRepository; import org.eclipse.egit.core.test.TestUtils; import org.eclipse.jgit.lib.Repository; import org.junit.After; import org.junit.Before; import org.junit.Test; public class IndexDiffCacheEntryTest extends GitTestCase { // trigger reload if more than one file is changed private static final int MAX_FILES_FOR_UPDATE = 1; private static final int MAX_WAIT_TIME = 10 * 1000; private TestRepository testRepository; private Repository repository; private IndexDiffCacheEntry2 entry; @Test public void basicTest() throws Exception { prepareCacheEntry(); entry.refresh(); waitForJobs(MAX_WAIT_TIME, JobFamilies.INDEX_DIFF_CACHE_UPDATE); // on refresh, full reload is triggered assertTrue(entry.reloadScheduled); assertFalse(entry.updateScheduled); cleanEntryFlags(); entry.refreshFiles(Arrays.asList("a")); waitForJobs(MAX_WAIT_TIME, JobFamilies.INDEX_DIFF_CACHE_UPDATE); // one single file: no reload assertFalse(entry.reloadScheduled); assertTrue(entry.updateScheduled); cleanEntryFlags(); entry.refreshFiles(Arrays.asList("a", "b")); waitForJobs(MAX_WAIT_TIME, JobFamilies.INDEX_DIFF_CACHE_UPDATE); // two files: update is triggered, but decides to run full reload assertTrue(entry.reloadScheduled); assertTrue(entry.updateScheduled); cleanEntryFlags(); entry.getUpdateJob().addChanges(Arrays.asList("a", "b"), Collections.<IResource> emptyList()); waitForJobs(MAX_WAIT_TIME, JobFamilies.INDEX_DIFF_CACHE_UPDATE); // two files: update is not triggered (we call through the job directly) // but this calls full reload assertTrue(entry.reloadScheduled); assertFalse(entry.updateScheduled); cleanEntryFlags(); } @Test public void testProjectDeletion() throws Exception { prepareCacheEntry(); testRepository.connect(project.project); waitForJobs(MAX_WAIT_TIME, JobFamilies.INDEX_DIFF_CACHE_UPDATE); // Should have something from the project String projectName = project.project.getName(); assertTrue(containsItemsStartingWith( entry.getIndexDiff().getUntracked(), projectName + '/')); project.project.delete(true, null); waitForJobs(MAX_WAIT_TIME, JobFamilies.INDEX_DIFF_CACHE_UPDATE); assertFalse(containsItemsStartingWith( entry.getIndexDiff().getUntracked(), projectName + '/')); } @Test public void testReloadAndUpdate() throws Exception { prepareCacheEntry(); testRepository.connect(project.project); waitForJobs(MAX_WAIT_TIME, JobFamilies.INDEX_DIFF_CACHE_UPDATE); // on a simple connect, nothing should be called assertFalse(entry.reloadScheduled); assertFalse(entry.updateScheduled); cleanEntryFlags(); // adds .project and .classpath files: more than limit of 1, // so update redirects to reload testRepository.addToIndex(project.project); waitForJobs(MAX_WAIT_TIME, JobFamilies.INDEX_DIFF_CACHE_UPDATE); assertTrue(entry.reloadScheduled); assertTrue(entry.updateScheduled); cleanEntryFlags(); testRepository.createInitialCommit("first commit\n"); waitForJobs(MAX_WAIT_TIME, JobFamilies.INDEX_DIFF_CACHE_UPDATE); // RefsChangedEvent causes always full update assertTrue(entry.reloadScheduled); // single "dummy" file from commit assertTrue(entry.updateScheduled); cleanEntryFlags(); ResourcesPlugin.getWorkspace().run(new IWorkspaceRunnable() { @Override public void run(IProgressMonitor monitor) throws CoreException { try { project.createFile("bla", "bla\n".getBytes("UTF-8")); project.createFile("blup", "blup\n".getBytes("UTF-8")); } catch (Exception e) { throw new CoreException(Activator.error("Failure", e)); } } }, null); waitForJobs(MAX_WAIT_TIME, JobFamilies.INDEX_DIFF_CACHE_UPDATE); // adds 2 files: more than limit of 1, // so update redirects to reload assertTrue(entry.reloadScheduled); assertTrue(entry.updateScheduled); cleanEntryFlags(); ResourcesPlugin.getWorkspace().run(new IWorkspaceRunnable() { @Override public void run(IProgressMonitor monitor) throws CoreException { try { project.createFile("blip", "blip\n".getBytes("UTF-8")); } catch (Exception e) { throw new CoreException(Activator.error("Failure", e)); } } }, null); waitForJobs(MAX_WAIT_TIME, JobFamilies.INDEX_DIFF_CACHE_UPDATE); // adds 1 file: less than limit of 1, so no reload assertFalse(entry.reloadScheduled); assertTrue(entry.updateScheduled); cleanEntryFlags(); ResourcesPlugin.getWorkspace().run(new IWorkspaceRunnable() { @Override public void run(IProgressMonitor monitor) throws CoreException { try { project.createFile(".gitignore", "\n".getBytes("UTF-8")); } catch (Exception e) { throw new CoreException(Activator.error("Failure", e)); } } }, null); waitForJobs(MAX_WAIT_TIME, JobFamilies.INDEX_DIFF_CACHE_UPDATE); // adds .gitignore file: always full reload assertTrue(entry.reloadScheduled); assertFalse(entry.updateScheduled); cleanEntryFlags(); } /** * Waits at least 50 milliseconds until no jobs of given family are running * * @param maxWaitTime * @param family * @throws InterruptedException */ private void waitForJobs(long maxWaitTime, Object family) throws InterruptedException { TestUtils.waitForJobs(maxWaitTime, family); } private void cleanEntryFlags() { entry.reloadScheduled = false; entry.updateScheduled = false; } private IndexDiffCacheEntry2 prepareCacheEntry() throws Exception { entry = new IndexDiffCacheEntry2(repository); waitForJobs(MAX_WAIT_TIME, JobFamilies.INDEX_DIFF_CACHE_UPDATE); // on creation, full reload is triggered assertTrue(entry.reloadScheduled); assertFalse(entry.updateScheduled); cleanEntryFlags(); return entry; } private boolean containsItemsStartingWith(Collection<String> values, String prefix) { for (String item : values) { if (item.startsWith(prefix)) { return true; } } return false; } @Override @Before public void setUp() throws Exception { super.setUp(); testRepository = new TestRepository(gitDir); repository = testRepository.getRepository(); } @Override @After public void tearDown() throws Exception { entry.dispose(); testRepository.dispose(); repository = null; super.tearDown(); } private static class IndexDiffCacheEntry2 extends IndexDiffCacheEntry { boolean reloadScheduled; boolean updateScheduled; public IndexDiffCacheEntry2(Repository repository) { super(repository, null); } @Override protected void scheduleReloadJob(String trigger) { reloadScheduled = true; super.scheduleReloadJob(trigger); } @Override protected void scheduleUpdateJob(Collection<String> filesToUpdate, Collection<IResource> resourcesToUpdate) { updateScheduled = true; super.scheduleUpdateJob(filesToUpdate, resourcesToUpdate); } @Override protected boolean shouldReload(Collection<String> filesToUpdate) { return filesToUpdate.size() > MAX_FILES_FOR_UPDATE; } @Override public IndexDiffUpdateJob getUpdateJob() { return super.getUpdateJob(); } } }