blob: 01705232a2530c613af0927c97d4d5605fa832ef [file] [log] [blame]
/*
* Copyright 2000-2014 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.intellij.vcs.log.data;
import com.intellij.openapi.components.ServiceManager;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.util.Consumer;
import com.intellij.util.Function;
import com.intellij.util.concurrency.FutureResult;
import com.intellij.util.containers.ContainerUtil;
import com.intellij.util.containers.HashMap;
import com.intellij.vcs.log.*;
import com.intellij.vcs.log.graph.GraphCommit;
import com.intellij.vcs.log.impl.*;
import org.jetbrains.annotations.NotNull;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import static com.intellij.vcs.log.TimedCommitParser.log;
public class VcsLogRefresherTest extends VcsLogPlatformTest {
private static final int RECENT_COMMITS_COUNT = 2;
public static final Consumer<Exception> FAILING_EXCEPTION_HANDLER = new Consumer<Exception>() {
@Override
public void consume(@NotNull Exception e) {
throw new AssertionError(e);
}
};
private TestVcsLogProvider myLogProvider;
private VcsLogDataHolder myDataHolder;
private Map<Hash, VcsCommitMetadata> myTopDetailsCache;
private Map<VirtualFile, VcsLogProvider> myLogProviders;
private List<String> myCommits;
@Override
public void setUp() throws Exception {
super.setUp();
myLogProvider = new TestVcsLogProvider(myProjectRoot);
myLogProviders = Collections.<VirtualFile, VcsLogProvider>singletonMap(myProjectRoot, myLogProvider);
myTopDetailsCache = new HashMap<Hash, VcsCommitMetadata>();
myCommits = Arrays.asList("3|-a2|-a1", "2|-a1|-a", "1|-a|-");
myLogProvider.appendHistory(log(myCommits));
myLogProvider.addRef(createBranchRef("master", "a2"));
}
public void test_initialize_shows_short_history() throws InterruptedException, ExecutionException, TimeoutException {
VcsLogRefresher loader = createLoader(new DataWaiter());
DataPack result = loader.readFirstBlock();
assertNotNull(result);
assertDataPack(log(myCommits.subList(0, 2)), result.getPermanentGraph().getAllCommits());
}
public void test_first_refresh_reports_full_history() throws InterruptedException, ExecutionException, TimeoutException {
DataWaiter dataWaiter = new DataWaiter();
VcsLogRefresher loader = createLoader(dataWaiter);
loader.readFirstBlock();
DataPack result = dataWaiter.get(1, TimeUnit.SECONDS);
assertNotNull(result);
assertDataPack(log(myCommits), result.getPermanentGraph().getAllCommits());
}
public void test_first_refresh_waits_for_full_log() throws InterruptedException, ExecutionException, TimeoutException {
DataWaiter dataWaiter = new DataWaiter();
VcsLogRefresher loader = createLoader(dataWaiter);
myLogProvider.blockFullLog();
loader.readFirstBlock();
assertTimeout(dataWaiter, "Refresh waiter should have failed on the timeout");
myLogProvider.unblockFullLog();
DataPack result = dataWaiter.get(1, TimeUnit.SECONDS);
assertNotNull(result);
assertDataPack(log(myCommits), result.getPermanentGraph().getAllCommits());
}
public void test_refresh_captures_new_commits() throws InterruptedException, ExecutionException, TimeoutException {
DataWaiter dataWaiter = new DataWaiter();
VcsLogRefresher loader = createLoader(dataWaiter);
initAndWaitForFirstRefresh(dataWaiter, loader);
String newCommit = "4|-a3|-a2";
myLogProvider.appendHistory(log(newCommit));
loader.refresh(Collections.singletonList(myProjectRoot));
DataPack result = dataWaiter.get(1, TimeUnit.SECONDS);
assertNotNull(result);
List<String> allCommits = ContainerUtil.newArrayList();
allCommits.add(newCommit);
allCommits.addAll(myCommits);
assertDataPack(log(allCommits), result.getPermanentGraph().getAllCommits());
}
public void test_single_refresh_causes_single_data_read() throws InterruptedException, ExecutionException, TimeoutException {
DataWaiter dataWaiter = new DataWaiter();
VcsLogRefresher loader = createLoader(dataWaiter);
initAndWaitForFirstRefresh(dataWaiter, loader);
myLogProvider.resetReadFirstBlockCounter();
loader.refresh(Collections.singletonList(myProjectRoot));
dataWaiter.get(1, TimeUnit.SECONDS);
assertEquals("Unexpected first block read count", 1, myLogProvider.getReadFirstBlockCounter());
}
public void test_reinitialize_cancels_refresh() throws InterruptedException, ExecutionException, TimeoutException {
DataWaiter dataWaiter = new DataWaiter();
VcsLogRefresher loader = createLoader(dataWaiter);
loader.readFirstBlock();
myLogProvider.blockRefresh();
loader.refresh(Collections.singletonList(myProjectRoot));
loader.readFirstBlock();
myLogProvider.unblockRefresh();
dataWaiter.get(1, TimeUnit.SECONDS);
assertFalse("Only one refresh should have happened", dataWaiter.failed());
}
public void test_two_immediately_consecutive_refreshes_causes_only_one_data_pack_update() throws InterruptedException,
ExecutionException, TimeoutException {
DataWaiter dataWaiter = new DataWaiter();
VcsLogRefresher loader = createLoader(dataWaiter);
myLogProvider.blockRefresh(); // make sure the first refresh won't complete before the second is queued.
loader.readFirstBlock();
loader.refresh(Collections.singletonList(myProjectRoot));
myLogProvider.unblockRefresh();
dataWaiter.get(1, TimeUnit.SECONDS);
dataWaiter.reset();
assertTimeout(dataWaiter, "Second refresh shouldn't cause the data pack update"); // it may also fail in beforehand in set().
assertFalse("DataPack FutureResult failed with an exception", dataWaiter.failed());
}
private static void initAndWaitForFirstRefresh(DataWaiter dataWaiter, VcsLogRefresher loader)
throws InterruptedException, ExecutionException, TimeoutException {
loader.readFirstBlock();
dataWaiter.get(1, TimeUnit.SECONDS); // wait for the first refresh to complete
dataWaiter.reset();
}
private static void assertTimeout(@NotNull DataWaiter dataWaiter, @NotNull String message)
throws InterruptedException, ExecutionException {
boolean timeout = false;
try {
dataWaiter.get(1, TimeUnit.SECONDS);
}
catch (TimeoutException e) {
timeout = true;
}
assertTrue(message, timeout);
}
private VcsLogRefresherImpl createLoader(DataWaiter dataWaiter) {
myDataHolder = new VcsLogDataHolder(myProject, myProject, myLogProviders,
ServiceManager.getService(myProject, VcsLogSettings.class), dataWaiter);
return new VcsLogRefresherImpl(myProject, myDataHolder.getHashMap(), myLogProviders, myDataHolder.getUserRegistry(), myTopDetailsCache,
dataWaiter, FAILING_EXCEPTION_HANDLER, RECENT_COMMITS_COUNT);
}
private void assertDataPack(@NotNull List<TimedVcsCommit> expectedLog, @NotNull List<GraphCommit<Integer>> actualLog) {
List<TimedVcsCommit> convertedActualLog = convert(actualLog);
assertOrderedEquals(convertedActualLog, expectedLog);
}
@NotNull
private List<TimedVcsCommit> convert(@NotNull List<GraphCommit<Integer>> actualLog) {
return ContainerUtil.map(actualLog, new Function<GraphCommit<Integer>, TimedVcsCommit>() {
@NotNull
@Override
public TimedVcsCommit fun(@NotNull GraphCommit<Integer> commit) {
Function<Integer, Hash> convertor = new Function<Integer, Hash>() {
@NotNull
@Override
public Hash fun(Integer integer) {
return myDataHolder.getHash(integer);
}
};
return new TimedVcsCommitImpl(convertor.fun(commit.getId()), ContainerUtil.map(commit.getParents(), convertor),
commit.getTimestamp());
}
});
}
@NotNull
private VcsRefImpl createBranchRef(@NotNull String name, @NotNull String commit) {
return new VcsRefImpl(HashImpl.build(commit), name, TestVcsLogProvider.BRANCH_TYPE, myProjectRoot);
}
private static class DataWaiter extends FutureResult<DataPack> implements Consumer<DataPack> {
private IllegalStateException myException;
@Override
public void consume(DataPack t) {
try {
set(t);
}
catch (IllegalStateException e) {
myException = e;
throw e;
}
}
public boolean failed() {
return myException != null;
}
}
}